mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-09 19:49:58 +00:00
fix: drop apt nodejs/npm, install Node 22 directly via n (#1746)
apt-get install nodejs npm pulls in hundreds of node-* packages (libhwasan, node-jsonify, node-eslint-utils, etc.) adding 60-90s to cloud-init. We immediately replace it with Node 22 via n anyway. Fix: bootstrap n directly from curl and install Node 22 in one step. No apt nodejs/npm needed. Before: apt install nodejs npm → npm install -g n → n 22 (slow) After: curl n | bash -s install 22 (fast, no apt bloat) Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0a8e6a30ad
commit
57d4ee7eeb
9 changed files with 29 additions and 31 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.6.12",
|
||||
"version": "0.6.13",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const DASHBOARD_URL = "https://lightsail.aws.amazon.com/";
|
||||
|
||||
|
|
@ -461,10 +461,10 @@ function getCloudInitUserdata(tier: CloudInitTier = "full"): string {
|
|||
"apt-get update -y",
|
||||
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
|
||||
];
|
||||
if (needsNodeUpgrade(tier)) {
|
||||
if (needsNode(tier)) {
|
||||
lines.push(
|
||||
"# Upgrade Node.js to v22 LTS",
|
||||
"npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx",
|
||||
"# Install Node.js 22 via n",
|
||||
`su - ubuntu -c '${NODE_INSTALL_CMD}'`,
|
||||
"# Install Claude Code",
|
||||
"su - ubuntu -c 'curl -fsSL https://claude.ai/install.sh | bash'",
|
||||
"# Configure npm global prefix",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const DAYTONA_API_BASE = "https://app.daytona.io/api";
|
||||
const DAYTONA_DASHBOARD_URL = "https://app.daytona.io/";
|
||||
|
|
@ -484,13 +484,8 @@ export async function waitForCloudInit(tier: CloudInitTier = "full"): Promise<vo
|
|||
`apt-get update -y`,
|
||||
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
|
||||
];
|
||||
if (needsNodeUpgrade(tier)) {
|
||||
parts.push(
|
||||
`npm install -g n && n 22`,
|
||||
`ln -sf /usr/local/bin/node /usr/bin/node`,
|
||||
`ln -sf /usr/local/bin/npm /usr/bin/npm`,
|
||||
`ln -sf /usr/local/bin/npx /usr/bin/npx`,
|
||||
);
|
||||
if (needsNode(tier)) {
|
||||
parts.push(NODE_INSTALL_CMD);
|
||||
}
|
||||
if (needsBun(tier)) {
|
||||
parts.push(`curl -fsSL https://bun.sh/install | bash`);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const DO_API_BASE = "https://api.digitalocean.com/v2";
|
||||
const DO_DASHBOARD_URL = "https://cloud.digitalocean.com/droplets";
|
||||
|
|
@ -624,8 +624,8 @@ function getCloudInitUserdata(tier: CloudInitTier = "full"): string {
|
|||
"apt-get update -y",
|
||||
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
|
||||
];
|
||||
if (needsNodeUpgrade(tier)) {
|
||||
lines.push("npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx || true");
|
||||
if (needsNode(tier)) {
|
||||
lines.push(`${NODE_INSTALL_CMD} || true`);
|
||||
}
|
||||
if (needsBun(tier)) {
|
||||
lines.push('if ! command -v bun >/dev/null 2>&1; then curl -fsSL https://bun.sh/install | bash; fi');
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const FLY_API_BASE = "https://api.machines.dev/v1";
|
||||
const FLY_DASHBOARD_URL = "https://fly.io/dashboard";
|
||||
|
|
@ -874,9 +874,9 @@ export async function waitForCloudInit(tier: CloudInitTier = "full"): Promise<vo
|
|||
`echo "==> Installing base packages..."`,
|
||||
`export DEBIAN_FRONTEND=noninteractive`,
|
||||
`apt-get update -y && apt-get install -y --no-install-recommends ${packages.join(" ")} || true`,
|
||||
...(needsNodeUpgrade(tier) ? [
|
||||
`echo "==> Upgrading Node.js to v22 LTS..."`,
|
||||
`npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx || true`,
|
||||
...(needsNode(tier) ? [
|
||||
`echo "==> Installing Node.js 22..."`,
|
||||
`${NODE_INSTALL_CMD} || true`,
|
||||
] : []),
|
||||
...(needsBun(tier) ? [
|
||||
`echo "==> Checking bun..."`,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const DASHBOARD_URL = "https://console.cloud.google.com/compute/instances";
|
||||
|
||||
|
|
@ -435,10 +435,10 @@ function getStartupScript(username: string, tier: CloudInitTier = "full"): strin
|
|||
"apt-get update -y",
|
||||
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
|
||||
];
|
||||
if (needsNodeUpgrade(tier)) {
|
||||
if (needsNode(tier)) {
|
||||
lines.push(
|
||||
"# Upgrade Node.js to v22 LTS",
|
||||
"npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx",
|
||||
"# Install Node.js 22 via n",
|
||||
`su - "${username}" -c '${NODE_INSTALL_CMD}'`,
|
||||
`# Install Claude Code as the login user`,
|
||||
`su - "${username}" -c 'curl -fsSL https://claude.ai/install.sh | bash' || true`,
|
||||
"# Configure npm global prefix",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
toKebabCase,
|
||||
} from "../shared/ui";
|
||||
import type { CloudInitTier } from "../shared/agents";
|
||||
import { getPackagesForTier, needsNodeUpgrade, needsBun } from "../shared/cloud-init";
|
||||
import { getPackagesForTier, needsNode, needsBun, NODE_INSTALL_CMD } from "../shared/cloud-init";
|
||||
|
||||
const HETZNER_API_BASE = "https://api.hetzner.cloud/v1";
|
||||
const HETZNER_DASHBOARD_URL = "https://console.hetzner.cloud/";
|
||||
|
|
@ -295,8 +295,8 @@ function getCloudInitUserdata(tier: CloudInitTier = "full"): string {
|
|||
"apt-get update -y",
|
||||
`apt-get install -y --no-install-recommends ${packages.join(" ")}`,
|
||||
];
|
||||
if (needsNodeUpgrade(tier)) {
|
||||
lines.push("npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx || true");
|
||||
if (needsNode(tier)) {
|
||||
lines.push(`${NODE_INSTALL_CMD} || true`);
|
||||
}
|
||||
if (needsBun(tier)) {
|
||||
lines.push('curl -fsSL https://bun.sh/install | bash || true');
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export async function installClaudeCode(runner: CloudRunner): Promise<void> {
|
|||
`curl -fsSL https://claude.ai/install.sh | bash || true`,
|
||||
`export PATH="${claudePath}:$PATH"`,
|
||||
`if command -v claude >/dev/null 2>&1; then ${finalize}; exit 0; fi`,
|
||||
`if ! command -v node >/dev/null 2>&1; then apt-get update -y && apt-get install -y --no-install-recommends nodejs npm && npm install -g n && n 22 && ln -sf /usr/local/bin/node /usr/bin/node && ln -sf /usr/local/bin/npm /usr/bin/npm && ln -sf /usr/local/bin/npx /usr/bin/npx || true; fi`,
|
||||
`if ! command -v node >/dev/null 2>&1; then curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s install 22 || true; fi`,
|
||||
`echo "==> Installing Claude Code (method 2/2: npm)..."`,
|
||||
`npm install -g @anthropic-ai/claude-code || true`,
|
||||
`export PATH="${claudePath}:$PATH"`,
|
||||
|
|
|
|||
|
|
@ -7,13 +7,16 @@ const MINIMAL = ["curl", "unzip", "git", "ca-certificates"];
|
|||
export function getPackagesForTier(tier: CloudInitTier = "full"): string[] {
|
||||
switch (tier) {
|
||||
case "minimal": return [...MINIMAL];
|
||||
case "node": return [...MINIMAL, "zsh", "nodejs", "npm", "build-essential"];
|
||||
case "node": return [...MINIMAL, "zsh", "build-essential"];
|
||||
case "bun": return [...MINIMAL, "zsh"];
|
||||
case "full": return [...MINIMAL, "zsh", "nodejs", "npm", "build-essential"];
|
||||
case "full": return [...MINIMAL, "zsh", "build-essential"];
|
||||
}
|
||||
}
|
||||
|
||||
export function needsNodeUpgrade(tier: CloudInitTier = "full"): boolean {
|
||||
/** Node 22 install via `n` bootstrapped directly from curl (no apt nodejs/npm). */
|
||||
export const NODE_INSTALL_CMD = 'curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s install 22';
|
||||
|
||||
export function needsNode(tier: CloudInitTier = "full"): boolean {
|
||||
return tier === "node" || tier === "full";
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue