Fix release installers and package manager fallbacks
This commit is contained in:
19
.github/workflows/publish.yml
vendored
19
.github/workflows/publish.yml
vendored
@@ -39,20 +39,29 @@ jobs:
|
||||
|
||||
publish-npm:
|
||||
needs: version-check
|
||||
if: needs.version-check.outputs.should_publish == 'true'
|
||||
if: needs.version-check.outputs.should_build_release == 'true'
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Skip npm publish
|
||||
if: needs.version-check.outputs.should_publish != 'true'
|
||||
run: echo "Skipping npm publish; version ${{ needs.version-check.outputs.version }} is already on npm."
|
||||
- uses: actions/checkout@v6
|
||||
if: needs.version-check.outputs.should_publish == 'true'
|
||||
- uses: actions/setup-node@v5
|
||||
if: needs.version-check.outputs.should_publish == 'true'
|
||||
with:
|
||||
node-version: 24.14.0
|
||||
registry-url: https://registry.npmjs.org
|
||||
- run: npm ci --ignore-scripts
|
||||
- run: npm run build
|
||||
- run: npm test
|
||||
- if: needs.version-check.outputs.should_publish == 'true'
|
||||
run: npm ci --ignore-scripts
|
||||
- if: needs.version-check.outputs.should_publish == 'true'
|
||||
run: npm run build
|
||||
- if: needs.version-check.outputs.should_publish == 'true'
|
||||
run: npm test
|
||||
- run: npm publish --access public
|
||||
if: needs.version-check.outputs.should_publish == 'true'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@@ -109,7 +118,7 @@ jobs:
|
||||
- version-check
|
||||
- publish-npm
|
||||
- build-native-bundles
|
||||
if: always() && needs.version-check.outputs.should_build_release == 'true' && needs.build-native-bundles.result == 'success' && (needs.publish-npm.result == 'success' || needs.publish-npm.result == 'skipped')
|
||||
if: needs.version-check.outputs.should_build_release == 'true' && needs.build-native-bundles.result == 'success' && needs.publish-npm.result == 'success'
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -26,6 +26,12 @@ irm https://feynman.is/install.ps1 | iex
|
||||
```bash
|
||||
# npm fallback
|
||||
npm install -g @companion-ai/feynman
|
||||
|
||||
# pnpm fallback
|
||||
pnpm add -g @companion-ai/feynman
|
||||
|
||||
# bun fallback
|
||||
bun add -g @companion-ai/feynman
|
||||
```
|
||||
|
||||
Then run `feynman setup` to configure your model and get started.
|
||||
|
||||
@@ -45,7 +45,23 @@ New-Item -ItemType Directory -Path $tmpDir | Out-Null
|
||||
try {
|
||||
$archivePath = Join-Path $tmpDir $archiveName
|
||||
Write-Host "==> Downloading $archiveName"
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
|
||||
try {
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
|
||||
} catch {
|
||||
throw @"
|
||||
Failed to download $archiveName from:
|
||||
$downloadUrl
|
||||
|
||||
The win32-$archSuffix bundle is missing from the GitHub release.
|
||||
This usually means the release exists, but not all platform bundles were uploaded.
|
||||
|
||||
Workarounds:
|
||||
- try again after the release finishes publishing
|
||||
- install via npm instead: npm install -g @companion-ai/feynman
|
||||
- install via pnpm instead: pnpm add -g @companion-ai/feynman
|
||||
- install via bun instead: bun add -g @companion-ai/feynman
|
||||
"@
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $installRoot -Force | Out-Null
|
||||
if (Test-Path $bundleDir) {
|
||||
|
||||
@@ -222,7 +222,22 @@ trap cleanup EXIT INT TERM
|
||||
|
||||
archive_path="$tmp_dir/$archive_name"
|
||||
step "Downloading ${archive_name}"
|
||||
download_file "$download_url" "$archive_path"
|
||||
if ! download_file "$download_url" "$archive_path"; then
|
||||
cat >&2 <<EOF
|
||||
Failed to download ${archive_name} from:
|
||||
${download_url}
|
||||
|
||||
The ${asset_target} bundle is missing from the GitHub release.
|
||||
This usually means the release exists, but not all platform bundles were uploaded.
|
||||
|
||||
Workarounds:
|
||||
- try again after the release finishes publishing
|
||||
- install via npm instead: npm install -g @companion-ai/feynman
|
||||
- install via pnpm instead: pnpm add -g @companion-ai/feynman
|
||||
- install via bun instead: bun add -g @companion-ai/feynman
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_APP_DIR"
|
||||
rm -rf "$INSTALL_APP_DIR/$bundle_name"
|
||||
|
||||
@@ -56,6 +56,61 @@ const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
||||
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
||||
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];
|
||||
case "pnpm":
|
||||
return ["add", "--prefer-offline", "--reporter", "silent", ...packageSpecs];
|
||||
case "bun":
|
||||
return ["add", "--silent", ...packageSpecs];
|
||||
default:
|
||||
throw new Error(`Unsupported package manager: ${packageManager}`);
|
||||
}
|
||||
}
|
||||
|
||||
let cachedPackageManager = undefined;
|
||||
|
||||
function resolvePackageManager() {
|
||||
if (cachedPackageManager !== undefined) return cachedPackageManager;
|
||||
|
||||
const requested = process.env.FEYNMAN_PACKAGE_MANAGER?.trim();
|
||||
const candidates = requested ? [requested] : ["npm", "pnpm", "bun"];
|
||||
for (const candidate of candidates) {
|
||||
if (resolveExecutable(candidate)) {
|
||||
cachedPackageManager = candidate;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
cachedPackageManager = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
function installWorkspacePackages(packageSpecs) {
|
||||
const packageManager = resolvePackageManager();
|
||||
if (!packageManager) {
|
||||
process.stderr.write(
|
||||
"[feynman] no supported package manager found; install npm, pnpm, or bun, or set FEYNMAN_PACKAGE_MANAGER.\n",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const result = spawnSync(packageManager, createInstallCommand(packageManager, packageSpecs), {
|
||||
cwd: workspaceDir,
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
timeout: 300000,
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
if (result.stderr?.length) process.stderr.write(result.stderr);
|
||||
process.stderr.write(`[feynman] ${packageManager} failed while setting up bundled packages.\n`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function parsePackageName(spec) {
|
||||
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@.+)?$/);
|
||||
return match?.[1] ?? spec;
|
||||
@@ -81,17 +136,7 @@ function restorePackagedWorkspace(packageSpecs) {
|
||||
}
|
||||
|
||||
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;
|
||||
return installWorkspacePackages(packageSpecs);
|
||||
}
|
||||
|
||||
function resolveExecutable(name, fallbackPaths = []) {
|
||||
@@ -139,17 +184,13 @@ function ensurePackageWorkspace() {
|
||||
process.stderr.write(`\r${frames[frame++ % frames.length]} setting up feynman... ${elapsed}s`);
|
||||
}, 80);
|
||||
|
||||
const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", "--prefix", workspaceDir, ...packageSpecs], {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
timeout: 300000,
|
||||
});
|
||||
const result = installWorkspacePackages(packageSpecs);
|
||||
|
||||
clearInterval(spinner);
|
||||
const elapsed = Math.round((Date.now() - start) / 1000);
|
||||
|
||||
if (result.status !== 0) {
|
||||
if (!result) {
|
||||
process.stderr.write(`\r✗ setup failed (${elapsed}s)\n`);
|
||||
if (result.stderr?.length) process.stderr.write(result.stderr);
|
||||
} else {
|
||||
process.stderr.write(`\r✓ feynman ready (${elapsed}s)\n`);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,22 @@ trap cleanup EXIT INT TERM
|
||||
|
||||
archive_path="$tmp_dir/$archive_name"
|
||||
step "Downloading ${archive_name}"
|
||||
download_file "$download_url" "$archive_path"
|
||||
if ! download_file "$download_url" "$archive_path"; then
|
||||
cat >&2 <<EOF
|
||||
Failed to download ${archive_name} from:
|
||||
${download_url}
|
||||
|
||||
The ${asset_target} bundle is missing from the GitHub release.
|
||||
This usually means the release exists, but not all platform bundles were uploaded.
|
||||
|
||||
Workarounds:
|
||||
- try again after the release finishes publishing
|
||||
- install via npm instead: npm install -g @companion-ai/feynman
|
||||
- install via pnpm instead: pnpm add -g @companion-ai/feynman
|
||||
- install via bun instead: bun add -g @companion-ai/feynman
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_APP_DIR"
|
||||
rm -rf "$INSTALL_APP_DIR/$bundle_name"
|
||||
|
||||
@@ -45,7 +45,23 @@ New-Item -ItemType Directory -Path $tmpDir | Out-Null
|
||||
try {
|
||||
$archivePath = Join-Path $tmpDir $archiveName
|
||||
Write-Host "==> Downloading $archiveName"
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
|
||||
try {
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath
|
||||
} catch {
|
||||
throw @"
|
||||
Failed to download $archiveName from:
|
||||
$downloadUrl
|
||||
|
||||
The win32-$archSuffix bundle is missing from the GitHub release.
|
||||
This usually means the release exists, but not all platform bundles were uploaded.
|
||||
|
||||
Workarounds:
|
||||
- try again after the release finishes publishing
|
||||
- install via npm instead: npm install -g @companion-ai/feynman
|
||||
- install via pnpm instead: pnpm add -g @companion-ai/feynman
|
||||
- install via bun instead: bun add -g @companion-ai/feynman
|
||||
"@
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $installRoot -Force | Out-Null
|
||||
if (Test-Path $bundleDir) {
|
||||
|
||||
@@ -15,8 +15,7 @@ Feynman stores all configuration and state under `~/.feynman/`. This directory i
|
||||
├── web-search.json # Web search routing config
|
||||
├── auth/ # OAuth tokens and API keys
|
||||
├── sessions/ # Persisted conversation history
|
||||
├── packages/ # Installed optional packages
|
||||
└── bin/ # Binary (when installed via the native installer)
|
||||
└── packages/ # Installed optional packages
|
||||
```
|
||||
|
||||
The `settings.json` file is the primary configuration file. It is created by `feynman setup` and can be edited manually. A typical configuration looks like:
|
||||
|
||||
@@ -5,7 +5,7 @@ section: Getting Started
|
||||
order: 1
|
||||
---
|
||||
|
||||
Feynman ships as a standalone binary for macOS and Linux, and as an npm package for all platforms including Windows. The recommended approach is the one-line installer, which downloads a prebuilt native binary with zero dependencies.
|
||||
Feynman ships as a standalone runtime bundle for macOS, Linux, and Windows, and as an npm package for environments where Node.js is already installed. The recommended approach is the one-line installer, which downloads a prebuilt native bundle with zero external runtime dependencies.
|
||||
|
||||
## One-line installer (recommended)
|
||||
|
||||
@@ -15,7 +15,7 @@ On **macOS or Linux**, open a terminal and run:
|
||||
curl -fsSL https://feynman.is/install | bash
|
||||
```
|
||||
|
||||
The installer detects your OS and architecture automatically. On macOS it supports both Intel and Apple Silicon. On Linux it supports x64 and arm64. The binary is installed to `~/.feynman/bin` and added to your `PATH`.
|
||||
The installer detects your OS and architecture automatically. On macOS it supports both Intel and Apple Silicon. On Linux it supports x64 and arm64. The launcher is installed to `~/.local/bin`, the bundled runtime is unpacked into `~/.local/share/feynman`, and your `PATH` is updated when needed.
|
||||
|
||||
On **Windows**, open PowerShell as Administrator and run:
|
||||
|
||||
@@ -23,23 +23,35 @@ On **Windows**, open PowerShell as Administrator and run:
|
||||
irm https://feynman.is/install.ps1 | iex
|
||||
```
|
||||
|
||||
This installs the native Windows binary and adds Feynman to your user `PATH`. You can re-run either installer at any time to update to the latest version.
|
||||
This installs the Windows runtime bundle under `%LOCALAPPDATA%\Programs\feynman`, adds its launcher to your user `PATH`, and lets you re-run the installer at any time to update.
|
||||
|
||||
## npm / npx
|
||||
|
||||
If you already have Node.js 18+ installed, you can install Feynman globally via npm:
|
||||
If you already have Node.js 20.18.1+ installed, you can install Feynman globally via npm:
|
||||
|
||||
```bash
|
||||
npm install -g @companion-ai/feynman
|
||||
```
|
||||
|
||||
`pnpm` and `bun` are supported as well:
|
||||
|
||||
```bash
|
||||
pnpm add -g @companion-ai/feynman
|
||||
bun add -g @companion-ai/feynman
|
||||
```
|
||||
|
||||
Or run it directly without installing:
|
||||
|
||||
```bash
|
||||
npx @companion-ai/feynman
|
||||
```
|
||||
|
||||
The npm distribution bundles the same core runtime as the native installer but depends on Node.js being present on your system. The native installer is preferred because it ships a self-contained binary with faster startup.
|
||||
```bash
|
||||
pnpm dlx @companion-ai/feynman
|
||||
bunx @companion-ai/feynman
|
||||
```
|
||||
|
||||
The npm distribution ships the same core application but depends on Node.js being present on your system. The standalone installer is preferred because it bundles its own Node runtime and works without a separate Node installation.
|
||||
|
||||
## Post-install setup
|
||||
|
||||
|
||||
Reference in New Issue
Block a user