harden installers rendering and dependency hygiene

This commit is contained in:
Advait Paliwal
2026-04-09 10:27:23 -07:00
parent 3148f2e62b
commit 96234425ba
14 changed files with 138 additions and 75 deletions

View File

@@ -131,3 +131,12 @@ Use this file to track chronology, not release notes. Keep entries short, factua
- Failed / learned: The skills-only install issue was not just docs drift; the shipped `SKILL.md` files referenced prompt paths that only made sense after installation, so the repo needed both path normalization and packaging changes. - Failed / learned: The skills-only install issue was not just docs drift; the shipped `SKILL.md` files referenced prompt paths that only made sense after installation, so the repo needed both path normalization and packaging changes.
- Blockers: Remote issue/PR closure and merge actions still depend on the final reviewed branch state being pushed. - Blockers: Remote issue/PR closure and merge actions still depend on the final reviewed branch state being pushed.
- Next: Push the validated fixes, close the duplicate Windows/reporting issues they supersede, reject the promotional ValiChord PR explicitly, and then review whether the remaining docs-only or feature PRs should be merged separately. - Next: Push the validated fixes, close the duplicate Windows/reporting issues they supersede, reject the promotional ValiChord PR explicitly, and then review whether the remaining docs-only or feature PRs should be merged separately.
### 2026-04-09 10:28 PDT — verification-and-security-pass
- Objective: Run a deeper install/security verification pass against the post-cleanup `0.2.17` tree instead of assuming the earlier targeted fixes covered the shipped artifacts.
- Changed: Reworked `extensions/research-tools/header.ts` to use `@mariozechner/pi-tui` width-aware helpers for truncation/wrapping so wide Unicode text does not overflow custom header rows; changed `src/pi/launch.ts` to stop mirroring child crash signals back onto the parent process and instead emit a conventional exit code; added `FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL` overrides to the skills installers for pre-release smoke testing; aligned root and website dependency trees with patched transitive versions using npm `overrides`; fixed `src/pi/web-access.ts` so `search status` respects `FEYNMAN_HOME` semantics instead of hardcoding the current shell home directory; added `tests/pi-launch.test.ts`.
- Verified: Ran `npm test`, `npm run typecheck`, `npm run build`, `cd website && npm run build`, `npm run build:native-bundle`; smoke-tested `scripts/install/install.sh` against a locally served `dist/release/feynman-0.2.17-darwin-arm64.tar.gz`; smoke-tested `scripts/install/install-skills.sh` against a local source archive; confirmed installed `feynman --version`, `feynman --help`, `feynman doctor`, and packaged `feynman search status` work from the installed bundle; `npm audit --omit=dev` is clean in the root app and website after overrides.
- Failed / learned: The first packaged `search status` smoke test still showed the user home path because the native bundle had been built before the `FEYNMAN_HOME` path fix; rebuilding the native bundle resolved that mismatch.
- Blockers: PowerShell runtime was unavailable locally, so Windows installer execution remained code-path validated rather than actually executed.
- Next: Push the second-pass hardening commit, then keep issue `#46` and issue `#47` open until users on the affected Linux/CJK environments confirm whether the launcher/header fixes fully resolve them.

View File

@@ -4,6 +4,7 @@ import { execSync } from "node:child_process";
import { resolve as resolvePath } from "node:path"; import { resolve as resolvePath } from "node:path";
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { import {
APP_ROOT, APP_ROOT,
@@ -11,10 +12,8 @@ import {
FEYNMAN_VERSION, FEYNMAN_VERSION,
} from "./shared.js"; } from "./shared.js";
const ANSI_RE = /\x1b\[[0-9;]*m/g;
function visibleLength(text: string): number { function visibleLength(text: string): number {
return text.replace(ANSI_RE, "").length; return visibleWidth(text);
} }
function formatHeaderPath(path: string): string { function formatHeaderPath(path: string): string {
@@ -23,10 +22,8 @@ function formatHeaderPath(path: string): string {
} }
function truncateVisible(text: string, maxVisible: number): string { function truncateVisible(text: string, maxVisible: number): string {
const raw = text.replace(ANSI_RE, ""); if (visibleWidth(text) <= maxVisible) return text;
if (raw.length <= maxVisible) return text; return truncateToWidth(text, maxVisible, maxVisible <= 3 ? "" : "...");
if (maxVisible <= 3) return ".".repeat(maxVisible);
return `${raw.slice(0, maxVisible - 3)}...`;
} }
function wrapWords(text: string, maxW: number): string[] { function wrapWords(text: string, maxW: number): string[] {
@@ -34,12 +31,12 @@ function wrapWords(text: string, maxW: number): string[] {
const lines: string[] = []; const lines: string[] = [];
let cur = ""; let cur = "";
for (let word of words) { for (let word of words) {
if (word.length > maxW) { if (visibleWidth(word) > maxW) {
if (cur) { lines.push(cur); cur = ""; } if (cur) { lines.push(cur); cur = ""; }
word = maxW > 3 ? `${word.slice(0, maxW - 1)}` : word.slice(0, maxW); word = truncateToWidth(word, maxW, maxW > 3 ? "…" : "");
} }
const test = cur ? `${cur} ${word}` : word; const test = cur ? `${cur} ${word}` : word;
if (cur && test.length > maxW) { if (cur && visibleWidth(test) > maxW) {
lines.push(cur); lines.push(cur);
cur = word; cur = word;
} else { } else {
@@ -56,9 +53,10 @@ function padRight(text: string, width: number): string {
} }
function centerText(text: string, width: number): string { function centerText(text: string, width: number): string {
if (text.length >= width) return text.slice(0, width); const textWidth = visibleWidth(text);
const left = Math.floor((width - text.length) / 2); if (textWidth >= width) return truncateToWidth(text, width, "");
const right = width - text.length - left; const left = Math.floor((width - textWidth) / 2);
const right = width - textWidth - left;
return `${" ".repeat(left)}${text}${" ".repeat(right)}`; return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
} }
@@ -287,8 +285,8 @@ export function installFeynmanHeader(
if (activity) { if (activity) {
const maxActivityLen = leftW * 2; const maxActivityLen = leftW * 2;
const trimmed = activity.length > maxActivityLen const trimmed = visibleWidth(activity) > maxActivityLen
? `${activity.slice(0, maxActivityLen - 1)}` ? truncateToWidth(activity, maxActivityLen, "…")
: activity; : activity;
leftLines.push(""); leftLines.push("");
leftLines.push(theme.fg("accent", theme.bold("Last Activity"))); leftLines.push(theme.fg("accent", theme.bold("Last Activity")));

30
package-lock.json generated
View File

@@ -1265,9 +1265,9 @@
} }
}, },
"node_modules/@hono/node-server": { "node_modules/@hono/node-server": {
"version": "1.19.11", "version": "1.19.13",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz",
"integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18.14.1" "node": ">=18.14.1"
@@ -2530,9 +2530,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/basic-ftp": { "node_modules/basic-ftp": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.1.tgz",
"integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", "integrity": "sha512-0yaL8JdxTknKDILitVpfYfV2Ob6yb3udX/hK97M7I3jOeznBNxQPtVvTUtnhUkyHlxFWyr5Lvknmgzoc7jf+1Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
@@ -2578,9 +2578,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "5.0.4", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^4.0.2" "balanced-match": "^4.0.2"
@@ -3623,9 +3623,9 @@
} }
}, },
"node_modules/hono": { "node_modules/hono": {
"version": "4.12.9", "version": "4.12.12",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz",
"integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==", "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=16.9.0" "node": ">=16.9.0"
@@ -4218,9 +4218,9 @@
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "8.3.0", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",

View File

@@ -66,6 +66,28 @@
"@sinclair/typebox": "^0.34.48", "@sinclair/typebox": "^0.34.48",
"dotenv": "^17.3.1" "dotenv": "^17.3.1"
}, },
"overrides": {
"basic-ftp": "5.2.1",
"@modelcontextprotocol/sdk": {
"@hono/node-server": "1.19.13",
"hono": "4.12.12"
},
"express": {
"router": {
"path-to-regexp": "8.4.2"
}
},
"proxy-agent": {
"pac-proxy-agent": {
"get-uri": {
"basic-ftp": "5.2.1"
}
}
},
"minimatch": {
"brace-expansion": "5.0.5"
}
},
"devDependencies": { "devDependencies": {
"@types/node": "^25.5.0", "@types/node": "^25.5.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",

View File

@@ -46,7 +46,7 @@ function Resolve-VersionMetadata {
return [PSCustomObject]@{ return [PSCustomObject]@{
ResolvedVersion = $resolvedVersion ResolvedVersion = $resolvedVersion
GitRef = "v$resolvedVersion" GitRef = "v$resolvedVersion"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip" DownloadUrl = if ($env:FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL) { $env:FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL } else { "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip" }
} }
} }

View File

@@ -146,7 +146,8 @@ archive_metadata="$(resolve_version)"
resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')" resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')"
git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')" git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')"
archive_url="" archive_url="${FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL:-}"
if [ -z "$archive_url" ]; then
case "$git_ref" in case "$git_ref" in
main) main)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz" archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz"
@@ -155,6 +156,7 @@ case "$git_ref" in
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz" archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz"
;; ;;
esac esac
fi
if [ -z "$archive_url" ]; then if [ -z "$archive_url" ]; then
echo "Could not resolve a download URL for ref: $git_ref" >&2 echo "Could not resolve a download URL for ref: $git_ref" >&2

View File

@@ -1,9 +1,15 @@
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import { constants } from "node:os";
import { buildPiArgs, buildPiEnv, type PiRuntimeOptions, resolvePiPaths, toNodeImportSpecifier } from "./runtime.js"; import { buildPiArgs, buildPiEnv, type PiRuntimeOptions, resolvePiPaths, toNodeImportSpecifier } from "./runtime.js";
import { ensureSupportedNodeVersion } from "../system/node-version.js"; import { ensureSupportedNodeVersion } from "../system/node-version.js";
export function exitCodeFromSignal(signal: NodeJS.Signals): number {
const signalNumber = constants.signals[signal];
return typeof signalNumber === "number" ? 128 + signalNumber : 1;
}
export async function launchPiChat(options: PiRuntimeOptions): Promise<void> { export async function launchPiChat(options: PiRuntimeOptions): Promise<void> {
ensureSupportedNodeVersion(); ensureSupportedNodeVersion();
@@ -36,11 +42,9 @@ export async function launchPiChat(options: PiRuntimeOptions): Promise<void> {
child.on("error", reject); child.on("error", reject);
child.on("exit", (code, signal) => { child.on("exit", (code, signal) => {
if (signal) { if (signal) {
try { console.error(`feynman terminated because the Pi child exited with ${signal}.`);
process.kill(process.pid, signal); process.exitCode = exitCodeFromSignal(signal);
} catch { resolvePromise();
process.exitCode = 1;
}
return; return;
} }
process.exitCode = code ?? 0; process.exitCode = code ?? 0;

View File

@@ -1,6 +1,7 @@
import { existsSync, readFileSync } from "node:fs"; import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os"; import { homedir } from "node:os";
import { resolve } from "node:path"; import { resolve } from "node:path";
import { getFeynmanHome } from "../config/paths.js";
export type PiWebSearchProvider = "auto" | "perplexity" | "exa" | "gemini"; export type PiWebSearchProvider = "auto" | "perplexity" | "exa" | "gemini";
@@ -26,8 +27,9 @@ export type PiWebAccessStatus = {
note: string; note: string;
}; };
export function getPiWebSearchConfigPath(home = process.env.HOME ?? homedir()): string { export function getPiWebSearchConfigPath(home?: string): string {
return resolve(home, ".feynman", "web-search.json"); const feynmanHome = home ? resolve(home, ".feynman") : getFeynmanHome();
return resolve(feynmanHome, "web-search.json");
} }
function normalizeProvider(value: unknown): PiWebSearchProvider | undefined { function normalizeProvider(value: unknown): PiWebSearchProvider | undefined {

9
tests/pi-launch.test.ts Normal file
View File

@@ -0,0 +1,9 @@
import test from "node:test";
import assert from "node:assert/strict";
import { exitCodeFromSignal } from "../src/pi/launch.js";
test("exitCodeFromSignal maps POSIX signals to conventional shell exit codes", () => {
assert.equal(exitCodeFromSignal("SIGTERM"), 143);
assert.equal(exitCodeFromSignal("SIGSEGV"), 139);
});

View File

@@ -18,6 +18,10 @@ test("loadPiWebAccessConfig returns empty config when Pi web config is missing",
assert.deepEqual(loadPiWebAccessConfig(configPath), {}); assert.deepEqual(loadPiWebAccessConfig(configPath), {});
}); });
test("getPiWebSearchConfigPath respects FEYNMAN_HOME semantics", () => {
assert.equal(getPiWebSearchConfigPath("/tmp/custom-home"), "/tmp/custom-home/.feynman/web-search.json");
});
test("getPiWebAccessStatus reads Pi web-access config directly", () => { test("getPiWebAccessStatus reads Pi web-access config directly", () => {
const root = mkdtempSync(join(tmpdir(), "feynman-pi-web-")); const root = mkdtempSync(join(tmpdir(), "feynman-pi-web-"));
const configPath = getPiWebSearchConfigPath(root); const configPath = getPiWebSearchConfigPath(root);

View File

@@ -7,9 +7,6 @@
"": { "": {
"name": "website", "name": "website",
"version": "0.0.1", "version": "0.0.1",
"engines": {
"node": ">=20.19.0"
},
"dependencies": { "dependencies": {
"@astrojs/react": "^4.4.2", "@astrojs/react": "^4.4.2",
"@fontsource-variable/ibm-plex-sans": "^5.2.8", "@fontsource-variable/ibm-plex-sans": "^5.2.8",
@@ -39,6 +36,9 @@
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"typescript-eslint": "^8.57.1" "typescript-eslint": "^8.57.1"
},
"engines": {
"node": ">=20.19.0"
} }
}, },
"node_modules/@astrojs/compiler": { "node_modules/@astrojs/compiler": {
@@ -1369,9 +1369,9 @@
} }
}, },
"node_modules/@hono/node-server": { "node_modules/@hono/node-server": {
"version": "1.19.11", "version": "1.19.13",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz",
"integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18.14.1" "node": ">=18.14.1"
@@ -6186,9 +6186,9 @@
} }
}, },
"node_modules/defu": { "node_modules/defu": {
"version": "6.1.4", "version": "6.1.7",
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/depd": { "node_modules/depd": {
@@ -7677,9 +7677,9 @@
} }
}, },
"node_modules/hono": { "node_modules/hono": {
"version": "4.12.9", "version": "4.12.12",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz",
"integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==", "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=16.9.0" "node": ">=16.9.0"
@@ -10994,9 +10994,9 @@
} }
}, },
"node_modules/router/node_modules/path-to-regexp": { "node_modules/router/node_modules/path-to-regexp": {
"version": "8.3.0", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -12367,9 +12367,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.4.1", "version": "6.4.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",

View File

@@ -33,6 +33,17 @@
"tailwindcss": "^4.2.1", "tailwindcss": "^4.2.1",
"tw-animate-css": "^1.4.0" "tw-animate-css": "^1.4.0"
}, },
"overrides": {
"@modelcontextprotocol/sdk": {
"@hono/node-server": "1.19.13",
"hono": "4.12.12"
},
"router": {
"path-to-regexp": "8.4.2"
},
"defu": "6.1.7",
"vite": "6.4.2"
},
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.4", "@eslint/js": "^9.39.4",
"eslint": "^9.39.4", "eslint": "^9.39.4",

View File

@@ -146,7 +146,8 @@ archive_metadata="$(resolve_version)"
resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')" resolved_version="$(printf '%s\n' "$archive_metadata" | sed -n '1p')"
git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')" git_ref="$(printf '%s\n' "$archive_metadata" | sed -n '2p')"
archive_url="" archive_url="${FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL:-}"
if [ -z "$archive_url" ]; then
case "$git_ref" in case "$git_ref" in
main) main)
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz" archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/heads/main.tar.gz"
@@ -155,6 +156,7 @@ case "$git_ref" in
archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz" archive_url="https://github.com/getcompanion-ai/feynman/archive/refs/tags/${git_ref}.tar.gz"
;; ;;
esac esac
fi
if [ -z "$archive_url" ]; then if [ -z "$archive_url" ]; then
echo "Could not resolve a download URL for ref: $git_ref" >&2 echo "Could not resolve a download URL for ref: $git_ref" >&2

View File

@@ -46,7 +46,7 @@ function Resolve-VersionMetadata {
return [PSCustomObject]@{ return [PSCustomObject]@{
ResolvedVersion = $resolvedVersion ResolvedVersion = $resolvedVersion
GitRef = "v$resolvedVersion" GitRef = "v$resolvedVersion"
DownloadUrl = "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip" DownloadUrl = if ($env:FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL) { $env:FEYNMAN_INSTALL_SKILLS_ARCHIVE_URL } else { "https://github.com/getcompanion-ai/feynman/archive/refs/tags/v$resolvedVersion.zip" }
} }
} }