Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,51 @@ jobs:
--base-url "$PRIME_AGENT_DOWNLOAD_BASE_URL" \
--out-dir packages/coding-agent/release/beta

- name: Smoke test installer with npm 12
run: |
npm install --global npm@12.0.2
npm --version | grep -Eq '^12\.'

SMOKE_VERSION=0.0.0-installer-smoke
SMOKE_BASE_URL=http://127.0.0.1:18188
SMOKE_OUT=packages/coding-agent/release/npm12-smoke
SMOKE_ROOT=$(mktemp -d)
export SMOKE_BASE_URL SMOKE_ROOT

npm run release:pack -- \
--channel stable \
--version "$SMOKE_VERSION" \
--base-url "$SMOKE_BASE_URL" \
--out-dir "$SMOKE_OUT"

mkdir -p "$SMOKE_ROOT/releases/v$SMOKE_VERSION"
cp "$SMOKE_OUT/artifacts/"* "$SMOKE_ROOT/releases/v$SMOKE_VERSION/"
cp "$SMOKE_OUT/artifacts/stable" "$SMOKE_ROOT/stable"

node - <<'NODE'
const fs = require("node:fs");
const installer = fs.readFileSync("install.sh", "utf8");
fs.writeFileSync(
"/tmp/prime-agent-npm12-install.sh",
installer
.replaceAll("__PRIME_AGENT_DOWNLOAD_BASE_URL__", process.env.SMOKE_BASE_URL)
.replaceAll("__PRIME_AGENT_DEFAULT_RELEASE_CHANNEL__", "stable"),
);
NODE

python3 -m http.server 18188 --bind 127.0.0.1 --directory "$SMOKE_ROOT" >/tmp/prime-agent-npm12-http.log 2>&1 &
server_pid=$!
trap 'kill "$server_pid" 2>/dev/null || true; rm -rf "$SMOKE_ROOT" "$SMOKE_OUT"' EXIT

curl -fsS --retry 5 --retry-connrefused "$SMOKE_BASE_URL/stable"
export NPM_CONFIG_IGNORE_SCRIPTS=true
export NPM_CONFIG_PREFIX="$SMOKE_ROOT/npm-prefix"
PATH="$NPM_CONFIG_PREFIX/bin:$PATH" \
PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL=0 \
PRIME_AGENT_INSTALLER_PLAIN=1 \
sh /tmp/prime-agent-npm12-install.sh "$SMOKE_VERSION"
test -x "$NPM_CONFIG_PREFIX/bin/prime-agent"

- name: Upload production artifacts
if: env.PUBLISH_PRODUCTION == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
Expand Down
25 changes: 23 additions & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,27 @@ confirm_kernel_runtime_setup() {
fi
}

prime_agent_npm_requires_remote_policy() {
npm_version=$(npm --version 2>/dev/null) || return 1
npm_major=${npm_version%%.*}
case "$npm_major" in
""|*[!0-9]*) return 1 ;;
esac
[ "$npm_major" -ge 12 ]
}

prime_agent_npm_install() {
tarball_path="$1"
shift
if prime_agent_npm_requires_remote_policy; then
# npm 12 blocks the release package's R2 dependencies by default.
# Scope the override to this verified install.
env "$@" npm install -g --no-fund --no-audit --loglevel=error --progress=false --allow-remote=all "$tarball_path"
else
env "$@" npm install -g --no-fund --no-audit --loglevel=error --progress=false "$tarball_path"
fi
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
}

install_prime_agent_package() {
tarball_path="$1"
if [ "$prime_agent_bootstrap_kernel_on_install" = 1 ]; then
Expand All @@ -1602,7 +1623,7 @@ Finalizing npm install."
"Installing Prime Agent" \
"Installing Prime Agent" \
"$npm_install_details" \
env PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL=1 PRIME_AGENT_INSTALL_UV=1 npm install -g --no-fund --no-audit --loglevel=error --progress=false "$tarball_path"
prime_agent_npm_install "$tarball_path" PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL=1 PRIME_AGENT_INSTALL_UV=1
else
npm_install_details="Preparing global install.
Linking command binaries.
Expand All @@ -1613,7 +1634,7 @@ Finalizing npm install."
"Installing Prime Agent" \
"Installing Prime Agent" \
"$npm_install_details" \
env PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1 npm install -g --no-fund --no-audit --loglevel=error --progress=false "$tarball_path"
prime_agent_npm_install "$tarball_path" PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL=1
fi
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dev": "concurrently --names \"ai,agent,coding-agent,tui\" --prefix-colors \"cyan,yellow,red,magenta\" \"cd packages/ai && npm run dev\" \"cd packages/agent && npm run dev\" \"cd packages/coding-agent && npm run dev\" \"cd packages/tui && npm run dev\"",
"dev:tsc": "cd packages/ai && npm run dev:tsc",
"check": "biome check --write --error-on-warnings . && tsgo --noEmit && npm run check:installer && npm run check:browser-smoke",
"check:installer": "node scripts/check-installer-render.mjs",
"check:installer": "node scripts/check-installer-render.mjs && node scripts/check-installer-install.mjs",
"check:browser-smoke": "node scripts/check-browser-smoke.mjs",
"profile:tui": "node scripts/profile-coding-agent-node.mjs --mode tui",
"profile:rpc": "node scripts/profile-coding-agent-node.mjs --mode rpc",
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/.changes/npm-12-stable-installer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fixed the stable installer failing under npm 12 when resolving verified release dependencies. ([Discussion #1988](https://github.com/PrimeIntellect-ai/prime-agent/discussions/1988))
117 changes: 117 additions & 0 deletions scripts/check-installer-install.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { spawnSync } from "node:child_process";
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { delimiter, join } from "node:path";

const installerSource = readFileSync("install.sh", "utf-8");
const mainCall = '\nmain "$@"';
const mainCallIndex = installerSource.lastIndexOf(mainCall);

if (mainCallIndex === -1) {
console.error('Installer install check failed: could not find final main "$@" call.');
process.exit(1);
}

const tempDir = mkdtempSync(join(tmpdir(), "prime-agent-installer-install-"));
const binDir = join(tempDir, "bin");
const harnessPath = join(tempDir, "harness.sh");
const npmPath = join(binDir, "npm");
const tarballPath = join(tempDir, "verified release package.tgz");

const harnessSource = `${installerSource.slice(0, mainCallIndex)}

prime_agent_screen_enabled=0
prime_agent_bootstrap_kernel_on_install="$1"
install_prime_agent_package "$2"
`;

const npmSource = `#!/bin/sh
set -eu

if [ "\${1:-}" = "--version" ]; then
printf '%s\\n' "$FAKE_NPM_VERSION"
exit 0
fi

if [ "\${1:-}" != install ]; then
printf 'unexpected npm command: %s\\n' "\${1:-}" >&2
exit 2
fi

remote_policy=
for arg in "$@"; do
case "$arg" in
--allow-remote=*) remote_policy=\${arg#*=} ;;
esac
done

npm_major=\${FAKE_NPM_VERSION%%.*}
if [ "$npm_major" -ge 12 ]; then
if [ "$remote_policy" != all ]; then
printf 'npm error code EALLOWREMOTE\\n' >&2
exit 1
fi
elif [ -n "$remote_policy" ]; then
printf 'npm error Unknown option: allow-remote\\n' >&2
exit 1
fi

if [ "\${PRIME_AGENT_BOOTSTRAP_TOOLS_ON_INSTALL:-}" != 1 ]; then
printf 'missing tool bootstrap environment\\n' >&2
exit 2
fi
if [ "$EXPECT_KERNEL_BOOTSTRAP" = 1 ]; then
if [ "\${PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL:-}" != 1 ] || [ "\${PRIME_AGENT_INSTALL_UV:-}" != 1 ]; then
printf 'missing kernel bootstrap environment\\n' >&2
exit 2
fi
elif [ -n "\${PRIME_AGENT_BOOTSTRAP_KERNEL_ON_INSTALL:-}" ] || [ -n "\${PRIME_AGENT_INSTALL_UV:-}" ]; then
printf 'unexpected kernel bootstrap environment\\n' >&2
exit 2
fi

printf '%s\\n' "$remote_policy" > "$FAKE_NPM_RESULT"
`;

try {
mkdirSync(binDir);
writeFileSync(harnessPath, harnessSource, "utf-8");
writeFileSync(npmPath, npmSource, "utf-8");
writeFileSync(tarballPath, "verified fixture", "utf-8");
chmodSync(npmPath, 0o755);

for (const npmVersion of ["10.9.8", "11.12.1", "12.0.2"]) {
for (const bootstrapKernel of [false, true]) {
const resultPath = join(tempDir, `npm-${npmVersion}-${bootstrapKernel ? "kernel" : "tools"}.txt`);
const result = spawnSync("sh", [harnessPath, bootstrapKernel ? "1" : "0", tarballPath], {
encoding: "utf-8",
env: {
...process.env,
EXPECT_KERNEL_BOOTSTRAP: bootstrapKernel ? "1" : "0",
FAKE_NPM_RESULT: resultPath,
FAKE_NPM_VERSION: npmVersion,
PATH: `${binDir}${delimiter}${process.env.PATH ?? ""}`,
},
});
if (result.status !== 0) {
console.error(
`Installer install check failed for npm ${npmVersion} (${bootstrapKernel ? "kernel" : "tools"}):\n${result.stderr}${result.stdout}`,
);
process.exit(1);
}

const remotePolicy = readFileSync(resultPath, "utf-8").trim();
const expectedPolicy = npmVersion.startsWith("12.") ? "all" : "";
if (remotePolicy !== expectedPolicy) {
console.error(
`Installer install check failed for npm ${npmVersion}: expected remote policy ${JSON.stringify(expectedPolicy)}, got ${JSON.stringify(remotePolicy)}.`,
);
process.exit(1);
}
}
}
} finally {
rmSync(tempDir, { recursive: true, force: true });
}

console.log("Installer install check passed.");
Loading