fix startup packaging and content guardrails

This commit is contained in:
Advait Paliwal
2026-04-09 10:09:05 -07:00
parent 554350cc0e
commit 3148f2e62b
39 changed files with 518 additions and 43 deletions

View File

@@ -275,7 +275,8 @@ function writeLauncher(bundleRoot, target) {
"@echo off",
"setlocal",
'set "ROOT=%~dp0"',
'"%ROOT%node\\node.exe" "%ROOT%app\\bin\\feynman.js" %*',
'if "%ROOT:~-1%"=="\\" set "ROOT=%ROOT:~0,-1%"',
'"%ROOT%\\node\\node.exe" "%ROOT%\\app\\bin\\feynman.js" %*',
"",
].join("\r\n"),
"utf8",

View File

@@ -92,8 +92,9 @@ try {
}
$skillsSource = Join-Path $sourceRoot.FullName "skills"
if (-not (Test-Path $skillsSource)) {
throw "Could not find skills/ in downloaded archive."
$promptsSource = Join-Path $sourceRoot.FullName "prompts"
if (-not (Test-Path $skillsSource) -or -not (Test-Path $promptsSource)) {
throw "Could not find the bundled skills resources in the downloaded archive."
}
$installParent = Split-Path $installDir -Parent
@@ -107,6 +108,10 @@ try {
New-Item -ItemType Directory -Path $installDir -Force | Out-Null
Copy-Item -Path (Join-Path $skillsSource "*") -Destination $installDir -Recurse -Force
New-Item -ItemType Directory -Path (Join-Path $installDir "prompts") -Force | Out-Null
Copy-Item -Path (Join-Path $promptsSource "*") -Destination (Join-Path $installDir "prompts") -Recurse -Force
Copy-Item -Path (Join-Path $sourceRoot.FullName "AGENTS.md") -Destination (Join-Path $installDir "AGENTS.md") -Force
Copy-Item -Path (Join-Path $sourceRoot.FullName "CONTRIBUTING.md") -Destination (Join-Path $installDir "CONTRIBUTING.md") -Force
Write-Host "==> Installed skills to $installDir"
if ($Scope -eq "Repo") {

View File

@@ -181,8 +181,8 @@ step "Extracting skills"
tar -xzf "$archive_path" -C "$extract_dir"
source_root="$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
if [ -z "$source_root" ] || [ ! -d "$source_root/skills" ]; then
echo "Could not find skills/ in downloaded archive." >&2
if [ -z "$source_root" ] || [ ! -d "$source_root/skills" ] || [ ! -d "$source_root/prompts" ]; then
echo "Could not find the bundled skills resources in the downloaded archive." >&2
exit 1
fi
@@ -190,6 +190,10 @@ mkdir -p "$(dirname "$install_dir")"
rm -rf "$install_dir"
mkdir -p "$install_dir"
cp -R "$source_root/skills/." "$install_dir/"
mkdir -p "$install_dir/prompts"
cp -R "$source_root/prompts/." "$install_dir/prompts/"
cp "$source_root/AGENTS.md" "$install_dir/AGENTS.md"
cp "$source_root/CONTRIBUTING.md" "$install_dir/CONTRIBUTING.md"
step "Installed skills to $install_dir"
case "$SCOPE" in

View File

@@ -125,12 +125,18 @@ Workarounds:
New-Item -ItemType Directory -Path $installBinDir -Force | Out-Null
$shimPath = Join-Path $installBinDir "feynman.cmd"
$shimPs1Path = Join-Path $installBinDir "feynman.ps1"
Write-Host "==> Linking feynman into $installBinDir"
@"
@echo off
"$bundleDir\feynman.cmd" %*
CALL "$bundleDir\feynman.cmd" %*
"@ | Set-Content -Path $shimPath -Encoding ASCII
@"
`$BundleDir = "$bundleDir"
& "`$BundleDir\node\node.exe" "`$BundleDir\app\bin\feynman.js" @args
"@ | Set-Content -Path $shimPs1Path -Encoding UTF8
$currentUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
$alreadyOnPath = $false
if ($currentUserPath) {

View File

@@ -0,0 +1,2 @@
export const PI_WEB_ACCESS_PATCH_TARGETS: string[];
export function patchPiWebAccessSource(relativePath: string, source: string): string;

View File

@@ -0,0 +1,32 @@
export const PI_WEB_ACCESS_PATCH_TARGETS = [
"index.ts",
"exa.ts",
"gemini-api.ts",
"gemini-search.ts",
"gemini-web.ts",
"github-extract.ts",
"perplexity.ts",
"video-extract.ts",
"youtube-extract.ts",
];
const LEGACY_CONFIG_EXPR = 'join(homedir(), ".pi", "web-search.json")';
const PATCHED_CONFIG_EXPR =
'process.env.FEYNMAN_WEB_SEARCH_CONFIG ?? process.env.PI_WEB_SEARCH_CONFIG ?? join(homedir(), ".pi", "web-search.json")';
export function patchPiWebAccessSource(relativePath, source) {
let patched = source;
if (patched.includes(PATCHED_CONFIG_EXPR)) {
return patched;
}
patched = patched.split(LEGACY_CONFIG_EXPR).join(PATCHED_CONFIG_EXPR);
if (relativePath === "index.ts" && patched !== source) {
patched = patched.replace('import { join } from "node:path";', 'import { dirname, join } from "node:path";');
patched = patched.replace('const dir = join(homedir(), ".pi");', "const dir = dirname(WEB_SEARCH_CONFIG_PATH);");
}
return patched;
}

View File

@@ -1,14 +1,21 @@
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { homedir } from "node:os";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { FEYNMAN_LOGO_HTML } from "../logo.mjs";
import { patchPiExtensionLoaderSource } from "./lib/pi-extension-loader-patch.mjs";
import { PI_WEB_ACCESS_PATCH_TARGETS, patchPiWebAccessSource } from "./lib/pi-web-access-patch.mjs";
import { PI_SUBAGENTS_PATCH_TARGETS, patchPiSubagentsSource } from "./lib/pi-subagents-patch.mjs";
const here = dirname(fileURLToPath(import.meta.url));
const appRoot = resolve(here, "..");
const feynmanHome = resolve(process.env.FEYNMAN_HOME ?? homedir(), ".feynman");
const feynmanNpmPrefix = resolve(feynmanHome, "npm-global");
process.env.FEYNMAN_NPM_PREFIX = feynmanNpmPrefix;
process.env.NPM_CONFIG_PREFIX = feynmanNpmPrefix;
process.env.npm_config_prefix = feynmanNpmPrefix;
const appRequire = createRequire(resolve(appRoot, "package.json"));
const isGlobalInstall = process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
@@ -57,6 +64,15 @@ const extensionLoaderPath = piPackageRoot ? resolve(piPackageRoot, "dist", "core
const terminalPath = piTuiRoot ? resolve(piTuiRoot, "dist", "terminal.js") : null;
const editorPath = piTuiRoot ? resolve(piTuiRoot, "dist", "components", "editor.js") : null;
const workspaceRoot = resolve(appRoot, ".feynman", "npm", "node_modules");
const workspaceExtensionLoaderPath = resolve(
workspaceRoot,
"@mariozechner",
"pi-coding-agent",
"dist",
"core",
"extensions",
"loader.js",
);
const vendorOverrideRoot = resolve(appRoot, ".feynman", "vendor-overrides");
const piSubagentsRoot = resolve(workspaceRoot, "pi-subagents");
const webAccessPath = resolve(workspaceRoot, "pi-web-access", "index.ts");
@@ -76,7 +92,17 @@ const workspaceArchivePath = resolve(appRoot, ".feynman", "runtime-workspace.tgz
function createInstallCommand(packageManager, packageSpecs) {
switch (packageManager) {
case "npm":
return ["install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", ...packageSpecs];
return [
"install",
"--global=false",
"--location=project",
"--prefer-offline",
"--no-audit",
"--no-fund",
"--loglevel",
"error",
...packageSpecs,
];
case "pnpm":
return ["add", "--prefer-offline", "--reporter", "silent", ...packageSpecs];
case "bun":
@@ -367,11 +393,15 @@ if (interactiveModePath && existsSync(interactiveModePath)) {
}
}
if (extensionLoaderPath && existsSync(extensionLoaderPath)) {
const source = readFileSync(extensionLoaderPath, "utf8");
for (const loaderPath of [extensionLoaderPath, workspaceExtensionLoaderPath].filter(Boolean)) {
if (!existsSync(loaderPath)) {
continue;
}
const source = readFileSync(loaderPath, "utf8");
const patched = patchPiExtensionLoaderSource(source);
if (patched !== source) {
writeFileSync(extensionLoaderPath, patched, "utf8");
writeFileSync(loaderPath, patched, "utf8");
}
}
@@ -560,6 +590,21 @@ if (existsSync(webAccessPath)) {
}
}
const piWebAccessRoot = resolve(workspaceRoot, "pi-web-access");
if (existsSync(piWebAccessRoot)) {
for (const relativePath of PI_WEB_ACCESS_PATCH_TARGETS) {
const entryPath = resolve(piWebAccessRoot, relativePath);
if (!existsSync(entryPath)) continue;
const source = readFileSync(entryPath, "utf8");
const patched = patchPiWebAccessSource(relativePath, source);
if (patched !== source) {
writeFileSync(entryPath, patched, "utf8");
}
}
}
if (existsSync(sessionSearchIndexerPath)) {
const source = readFileSync(sessionSearchIndexerPath, "utf8");
const original = 'const sessionsDir = path.join(os.homedir(), ".pi", "agent", "sessions");';