Improve Feynman packaging and research prompts
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { FEYNMAN_LOGO_HTML } from "../logo.mjs";
|
||||
@@ -54,6 +54,45 @@ const piMemoryPath = resolve(workspaceRoot, "@samfp", "pi-memory", "src", "index
|
||||
const settingsPath = resolve(appRoot, ".feynman", "settings.json");
|
||||
const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
||||
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
||||
const workspaceArchivePath = resolve(appRoot, ".feynman", "runtime-workspace.tgz");
|
||||
|
||||
function parsePackageName(spec) {
|
||||
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@.+)?$/);
|
||||
return match?.[1] ?? spec;
|
||||
}
|
||||
|
||||
function restorePackagedWorkspace(packageSpecs) {
|
||||
if (!existsSync(workspaceArchivePath)) return false;
|
||||
|
||||
rmSync(workspaceDir, { recursive: true, force: true });
|
||||
mkdirSync(resolve(appRoot, ".feynman"), { recursive: true });
|
||||
|
||||
const result = spawnSync("tar", ["-xzf", workspaceArchivePath, "-C", resolve(appRoot, ".feynman")], {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
timeout: 300000,
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
if (result.stderr?.length) process.stderr.write(result.stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return packageSpecs.every((spec) => existsSync(resolve(workspaceRoot, parsePackageName(spec))));
|
||||
}
|
||||
|
||||
function refreshPackagedWorkspace(packageSpecs) {
|
||||
const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", "--prefix", workspaceDir, ...packageSpecs], {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
timeout: 300000,
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
if (result.stderr?.length) process.stderr.write(result.stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveExecutable(name, fallbackPaths = []) {
|
||||
for (const candidate of fallbackPaths) {
|
||||
@@ -82,7 +121,8 @@ function ensurePackageWorkspace() {
|
||||
: [];
|
||||
|
||||
if (packageSpecs.length === 0) return;
|
||||
if (existsSync(resolve(workspaceRoot, packageSpecs[0]))) return;
|
||||
if (existsSync(resolve(workspaceRoot, parsePackageName(packageSpecs[0])))) return;
|
||||
if (restorePackagedWorkspace(packageSpecs) && refreshPackagedWorkspace(packageSpecs)) return;
|
||||
|
||||
mkdirSync(workspaceDir, { recursive: true });
|
||||
writeFileSync(
|
||||
|
||||
149
scripts/prepare-runtime-workspace.mjs
Normal file
149
scripts/prepare-runtime-workspace.mjs
Normal file
@@ -0,0 +1,149 @@
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const appRoot = resolve(import.meta.dirname, "..");
|
||||
const settingsPath = resolve(appRoot, ".feynman", "settings.json");
|
||||
const feynmanDir = resolve(appRoot, ".feynman");
|
||||
const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
||||
const workspaceNodeModulesDir = resolve(workspaceDir, "node_modules");
|
||||
const manifestPath = resolve(workspaceDir, ".runtime-manifest.json");
|
||||
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
||||
const workspaceArchivePath = resolve(feynmanDir, "runtime-workspace.tgz");
|
||||
|
||||
function readPackageSpecs() {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
||||
if (!Array.isArray(settings.packages)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return settings.packages
|
||||
.filter((value) => typeof value === "string" && value.startsWith("npm:"))
|
||||
.map((value) => value.slice(4));
|
||||
}
|
||||
|
||||
function parsePackageName(spec) {
|
||||
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@.+)?$/);
|
||||
return match?.[1] ?? spec;
|
||||
}
|
||||
|
||||
function arraysMatch(left, right) {
|
||||
return left.length === right.length && left.every((value, index) => value === right[index]);
|
||||
}
|
||||
|
||||
function workspaceIsCurrent(packageSpecs) {
|
||||
if (!existsSync(manifestPath) || !existsSync(workspaceNodeModulesDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
||||
if (!Array.isArray(manifest.packageSpecs) || !arraysMatch(manifest.packageSpecs, packageSpecs)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
manifest.nodeAbi !== process.versions.modules ||
|
||||
manifest.platform !== process.platform ||
|
||||
manifest.arch !== process.arch
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return packageSpecs.every((spec) => existsSync(resolve(workspaceNodeModulesDir, parsePackageName(spec))));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function writeWorkspacePackageJson() {
|
||||
writeFileSync(
|
||||
workspacePackageJsonPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "feynman-runtime",
|
||||
private: true,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
function prepareWorkspace(packageSpecs) {
|
||||
rmSync(workspaceDir, { recursive: true, force: true });
|
||||
mkdirSync(workspaceDir, { recursive: true });
|
||||
writeWorkspacePackageJson();
|
||||
|
||||
if (packageSpecs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = spawnSync(
|
||||
process.env.npm_execpath ? process.execPath : "npm",
|
||||
process.env.npm_execpath
|
||||
? [process.env.npm_execpath, "install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", "--prefix", workspaceDir, ...packageSpecs]
|
||||
: ["install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", "--prefix", workspaceDir, ...packageSpecs],
|
||||
{ stdio: "inherit" },
|
||||
);
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
function writeManifest(packageSpecs) {
|
||||
writeFileSync(
|
||||
manifestPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
packageSpecs,
|
||||
generatedAt: new Date().toISOString(),
|
||||
nodeAbi: process.versions.modules,
|
||||
nodeVersion: process.version,
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
function archiveIsCurrent() {
|
||||
if (!existsSync(workspaceArchivePath) || !existsSync(manifestPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return statSync(workspaceArchivePath).mtimeMs >= statSync(manifestPath).mtimeMs;
|
||||
}
|
||||
|
||||
function createWorkspaceArchive() {
|
||||
rmSync(workspaceArchivePath, { force: true });
|
||||
|
||||
const result = spawnSync("tar", ["-czf", workspaceArchivePath, "-C", feynmanDir, "npm"], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
const packageSpecs = readPackageSpecs();
|
||||
|
||||
if (workspaceIsCurrent(packageSpecs)) {
|
||||
console.log("[feynman] vendored runtime workspace already up to date");
|
||||
if (archiveIsCurrent()) {
|
||||
process.exit(0);
|
||||
}
|
||||
console.log("[feynman] refreshing runtime workspace archive...");
|
||||
createWorkspaceArchive();
|
||||
console.log("[feynman] runtime workspace archive ready");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log("[feynman] preparing vendored runtime workspace...");
|
||||
prepareWorkspace(packageSpecs);
|
||||
writeManifest(packageSpecs);
|
||||
createWorkspaceArchive();
|
||||
console.log("[feynman] vendored runtime workspace ready");
|
||||
Reference in New Issue
Block a user