@saadnvd1/agent-os 0.3.678 → 0.3.684

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
11
+ import { tempDir } from "@/lib/test-temp";
12
+
13
+ const LIB = join(import.meta.dirname, "deps-swap.sh");
14
+
15
+ let dir: string;
16
+ const read = (p: string) => readFileSync(join(dir, p), "utf8");
17
+
18
+ // Runs the library's functions in `dir` with a fake install and native check.
19
+ function run(script: string, env: Record<string, string> = {}) {
20
+ return spawnSync("bash", ["-c", `source "${LIB}"; ${script}`], {
21
+ cwd: dir,
22
+ encoding: "utf8",
23
+ env: { ...process.env, ...env },
24
+ });
25
+ }
26
+
27
+ const INSTALL = "mkdir -p node_modules && echo new > node_modules/marker";
28
+
29
+ beforeEach(() => {
30
+ dir = tempDir("deps-swap-");
31
+ for (const f of ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"])
32
+ writeFileSync(join(dir, f), `{}\n`);
33
+ mkdirSync(join(dir, "node_modules"));
34
+ writeFileSync(join(dir, "node_modules/marker"), "old\n");
35
+ });
36
+ afterEach(() => rmSync(dir, { recursive: true, force: true }));
37
+
38
+ describe("install_beside", () => {
39
+ it("installs beside, checks, and swaps in; the old tree is kept for a rollback", () => {
40
+ const r = run("install_beside && swap_deps", {
41
+ AGENTOS_INSTALL_CMD: INSTALL,
42
+ AGENTOS_NATIVE_CHECK: "true",
43
+ });
44
+ expect(r.status).toBe(0);
45
+ expect(read("node_modules/marker")).toBe("new\n");
46
+ expect(read(".deps/prev/marker")).toBe("old\n");
47
+ expect(existsSync(join(dir, ".deps/stage"))).toBe(false);
48
+ });
49
+
50
+ it("leaves the live node_modules alone when the native check fails", () => {
51
+ const r = run("install_beside", {
52
+ AGENTOS_INSTALL_CMD: INSTALL,
53
+ AGENTOS_NATIVE_CHECK: "exit 1",
54
+ });
55
+ expect(r.status).toBe(1);
56
+ expect(r.stderr).toContain("Native modules do not load");
57
+ expect(read("node_modules/marker")).toBe("old\n");
58
+ expect(existsSync(join(dir, ".deps/stage"))).toBe(false);
59
+ });
60
+
61
+ it("leaves the live node_modules alone when the install fails", () => {
62
+ const r = run("install_beside", {
63
+ AGENTOS_INSTALL_CMD: "exit 3",
64
+ AGENTOS_NATIVE_CHECK: "true",
65
+ });
66
+ expect(r.status).toBe(1);
67
+ expect(read("node_modules/marker")).toBe("old\n");
68
+ });
69
+
70
+ it("skips the install when nothing it depends on changed", () => {
71
+ const first = run("install_beside && swap_deps", {
72
+ AGENTOS_INSTALL_CMD: INSTALL,
73
+ AGENTOS_NATIVE_CHECK: "true",
74
+ });
75
+ expect(first.status).toBe(0);
76
+ const again = run("install_beside", {
77
+ AGENTOS_INSTALL_CMD: "exit 9",
78
+ AGENTOS_NATIVE_CHECK: "exit 9",
79
+ });
80
+ expect(again.status).toBe(10);
81
+ // A rollback has nothing to restore: the last build used these same deps.
82
+ expect(existsSync(join(dir, ".deps/prev"))).toBe(false);
83
+ });
84
+
85
+ it("installs again when the lockfile changes", () => {
86
+ run("install_beside && swap_deps", {
87
+ AGENTOS_INSTALL_CMD: INSTALL,
88
+ AGENTOS_NATIVE_CHECK: "true",
89
+ });
90
+ writeFileSync(join(dir, "pnpm-lock.yaml"), "changed\n");
91
+ const r = run("install_beside", {
92
+ AGENTOS_INSTALL_CMD: INSTALL,
93
+ AGENTOS_NATIVE_CHECK: "true",
94
+ });
95
+ expect(r.status).toBe(0);
96
+ });
97
+ });
98
+
99
+ describe("restore_deps", () => {
100
+ it("puts the previous node_modules back after a swap", () => {
101
+ run("install_beside && swap_deps && restore_deps", {
102
+ AGENTOS_INSTALL_CMD: INSTALL,
103
+ AGENTOS_NATIVE_CHECK: "true",
104
+ });
105
+ expect(read("node_modules/marker")).toBe("old\n");
106
+ expect(existsSync(join(dir, ".deps/prev"))).toBe(false);
107
+ });
108
+
109
+ it("does nothing without a previous tree", () => {
110
+ rmSync(join(dir, ".deps"), { recursive: true, force: true });
111
+ const r = run("restore_deps");
112
+ expect(r.status).toBe(0);
113
+ expect(read("node_modules/marker")).toBe("old\n");
114
+ });
115
+ });
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Make `pnpm` runnable: the pinned version comes from package.json's
3
+ # packageManager field through corepack (bundled with Node 22; Node 25+ needs
4
+ # `npm i -g corepack`). Sourced by scripts/install-deps and scripts/redeploy.
5
+
6
+ use_pnpm() {
7
+ export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
8
+ if ! command -v pnpm >/dev/null 2>&1 && command -v corepack >/dev/null 2>&1; then
9
+ corepack enable pnpm >/dev/null 2>&1 || true
10
+ fi
11
+ if ! command -v pnpm >/dev/null 2>&1; then
12
+ echo "pnpm is not available. Use Node 22 (it bundles corepack: corepack enable pnpm) or npm i -g corepack." >&2
13
+ return 1
14
+ fi
15
+ }
16
+
17
+ # Share -> Download -> PDF prints with Playwright's headless shell, at the
18
+ # revision the locked playwright package names. A failed download only warns:
19
+ # a deploy must not hang on a CDN, and the PDF option hides itself without a
20
+ # browser. Run after the install, from the repository root.
21
+ install_pdf_browser() {
22
+ if ! pnpm exec playwright install chromium-headless-shell; then
23
+ echo "Warning: could not download the PDF browser; share PDF is unavailable until 'pnpm exec playwright install chromium-headless-shell' succeeds." >&2
24
+ return 0
25
+ fi
26
+ if [ "$(uname -s)" = Linux ]; then
27
+ local bin missing
28
+ bin=$(node -e 'const p=require("playwright").chromium.executablePath();const fs=require("fs"),path=require("path");const root=path.dirname(path.dirname(path.dirname(p)));const d=fs.readdirSync(root).filter(x=>x.startsWith("chromium_headless_shell-")).sort().pop();if(d){const b=fs.readdirSync(path.join(root,d)).find(x=>x.startsWith("chrome-headless-shell-"));if(b)console.log(path.join(root,d,b,"chrome-headless-shell"))}' 2>/dev/null || true)
29
+ if [ -n "$bin" ] && command -v ldd >/dev/null 2>&1; then
30
+ missing=$(ldd "$bin" 2>/dev/null | grep -c 'not found' || true)
31
+ if [ "${missing:-0}" -gt 0 ]; then
32
+ echo "Warning: the PDF browser is missing $missing system libraries; run: sudo pnpm exec playwright install-deps chromium-headless-shell" >&2
33
+ fi
34
+ fi
35
+ fi
36
+ }
@@ -47,7 +47,7 @@ check_node() {
47
47
  if command -v node &> /dev/null; then
48
48
  local version
49
49
  version=$(node -v | sed 's/v//' | cut -d. -f1)
50
- if [[ "$version" -ge 20 ]]; then
50
+ if [[ "$version" -ge 22 ]]; then
51
51
  local node_path=$(command -v node)
52
52
  log_success "Found Node.js v$(node -v | sed 's/v//') at $node_path"
53
53
  return 0
@@ -60,7 +60,7 @@ check_node() {
60
60
  if command -v node &> /dev/null; then
61
61
  local version
62
62
  version=$(node -v | sed 's/v//' | cut -d. -f1)
63
- if [[ "$version" -ge 20 ]]; then
63
+ if [[ "$version" -ge 22 ]]; then
64
64
  local node_path=$(command -v node)
65
65
  local manager=""
66
66
 
@@ -202,10 +202,10 @@ install_node() {
202
202
  if command -v node &> /dev/null; then
203
203
  local version
204
204
  version=$(node -v | sed 's/v//' | cut -d. -f1)
205
- if [[ "$version" -ge 20 ]]; then
205
+ if [[ "$version" -ge 22 ]]; then
206
206
  return 0
207
207
  fi
208
- log_warn "Node.js $version found, but 20+ required"
208
+ log_warn "Node.js $version found, but 22+ required"
209
209
  fi
210
210
 
211
211
  log_info "Installing Node.js..."
@@ -224,9 +224,9 @@ install_node() {
224
224
  export PATH="$HOME/.local/share/fnm:$PATH"
225
225
  eval "$(fnm env --use-on-cd)"
226
226
 
227
- # Install and use Node.js 20
228
- fnm install 20
229
- fnm use 20
227
+ # Install and use Node.js 22
228
+ fnm install 22
229
+ fnm use 22
230
230
 
231
231
  # Add fnm to shell profile
232
232
  local shell_profile=""
@@ -261,7 +261,7 @@ install_node() {
261
261
  sudo yum install -y nodejs
262
262
  ;;
263
263
  *)
264
- log_error "Please install Node.js 20+ manually: https://nodejs.org"
264
+ log_error "Please install Node.js 22+ manually: https://nodejs.org"
265
265
  exit 1
266
266
  ;;
267
267
  esac
@@ -499,7 +499,7 @@ check_and_install_prerequisites() {
499
499
 
500
500
  # What this distro has no installer for here is still missing.
501
501
  local still=()
502
- check_node > /dev/null || still+=("node 20+")
502
+ check_node > /dev/null || still+=("node 22+")
503
503
  check_git > /dev/null || still+=("git")
504
504
  check_tmux > /dev/null || still+=("tmux")
505
505
  if [[ ${#still[@]} -gt 0 ]]; then
package/scripts/publish CHANGED
@@ -14,10 +14,11 @@ trap 'rm -f "$npmrc"' EXIT
14
14
  chmod 600 "$npmrc"
15
15
  printf '//registry.npmjs.org/:_authToken=%s\n' "$token" > "$npmrc"
16
16
 
17
- echo "Publishing $name@$version as $(npx -y npm@latest whoami --userconfig "$npmrc")"
17
+ # npm is pinned (at least a week old), not @latest: this runs with the publish token.
18
+ echo "Publishing $name@$version as $(npx -y npm@11.21.0 whoami --userconfig "$npmrc")"
18
19
  # A 409 "previously staged" means npm already took this version (an earlier
19
20
  # run gave up waiting): that's not a failure, just keep waiting for it.
20
- if ! out=$(NODE_ENV=development npx -y npm@latest publish --access public \
21
+ if ! out=$(NODE_ENV=development npx -y npm@11.21.0 publish --access public \
21
22
  --userconfig "$npmrc" --ignore-scripts --loglevel warn 2>&1); then
22
23
  echo "$out"
23
24
  echo "$out" | grep -qiE "previously staged|cannot publish over" || exit 1
package/scripts/redeploy CHANGED
@@ -8,6 +8,10 @@ set -euo pipefail
8
8
  cd "$(dirname "$0")/.."
9
9
  # shellcheck source=lib/build-swap.sh
10
10
  source scripts/lib/build-swap.sh
11
+ # shellcheck source=lib/pnpm.sh
12
+ source scripts/lib/pnpm.sh
13
+ # shellcheck source=lib/deps-swap.sh
14
+ source scripts/lib/deps-swap.sh
11
15
 
12
16
  if [ -s "$HOME/.nvm/nvm.sh" ]; then
13
17
  # shellcheck disable=SC1091
@@ -15,23 +19,36 @@ if [ -s "$HOME/.nvm/nvm.sh" ]; then
15
19
  nvm use >/dev/null
16
20
  fi
17
21
 
22
+ # The pull can replace this script, so what follows runs from a fresh start.
23
+ if [ "${1:-}" != "--rollback" ] && [ "${1:-}" != "--pulled" ]; then
24
+ echo "::phase pulling"
25
+ git pull --ff-only
26
+ exec bash "$0" --pulled
27
+ fi
28
+
18
29
  acquire_build_lock
19
30
 
20
31
  if [ "${1:-}" = "--rollback" ]; then
21
32
  rollback_build
33
+ # The build and the dependencies it ran on go back together.
34
+ restore_deps
22
35
  else
23
- echo "::phase pulling"
24
- git pull --ff-only
25
36
  echo "::phase installing"
37
+ use_pnpm
26
38
  # The build needs dev dependencies even when the shell runs with
27
39
  # NODE_ENV=production (as sessions started by AgentOS do).
28
- npm install --include=dev --no-audit --no-fund
29
- # Another npm version (on another machine) rewrites the lockfile's flags; a
30
- # deploy never changes it, and a dirty lockfile would block the next pull.
31
- git checkout -- package-lock.json 2>/dev/null || true
32
- node scripts/ensure-native.mjs
40
+ unset NODE_ENV npm_config_production npm_config_omit
41
+ # Installed beside the live node_modules and checked (SQLite opens, a pty
42
+ # spawns) before they replace it: a broken install fails the deploy here,
43
+ # and the running server keeps the build and dependencies it has.
44
+ swapped=0
45
+ install_beside && rc=0 || rc=$?
46
+ [ "$rc" = 0 ] && { swap_deps; swapped=1; }
47
+ [ "$rc" = 0 ] || [ "$rc" = 10 ] || exit 1
48
+ install_pdf_browser
33
49
  echo "::phase building"
34
- build_beside
50
+ # A build that fails on the new dependencies puts the old ones back.
51
+ build_beside || { [ "$swapped" = 1 ] && restore_deps; exit 1; }
35
52
  swap_build
36
53
  fi
37
54
 
package/scripts/setup.sh CHANGED
@@ -8,13 +8,13 @@ echo ""
8
8
  # Check Node.js
9
9
  if ! command -v node &> /dev/null; then
10
10
  echo "Error: Node.js is not installed"
11
- echo "Install Node.js 20+ from https://nodejs.org"
11
+ echo "Install Node.js 22+ from https://nodejs.org"
12
12
  exit 1
13
13
  fi
14
14
 
15
15
  NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
16
- if [ "$NODE_VERSION" -lt 20 ]; then
17
- echo "Error: Node.js 20+ required (found v$NODE_VERSION)"
16
+ if [ "$NODE_VERSION" -lt 22 ]; then
17
+ echo "Error: Node.js 22+ required (found v$NODE_VERSION)"
18
18
  exit 1
19
19
  fi
20
20
  echo "Node.js: $(node -v)"
@@ -60,7 +60,7 @@ fi
60
60
  # Install dependencies
61
61
  echo ""
62
62
  echo "Installing dependencies..."
63
- npm install
63
+ scripts/install-deps
64
64
 
65
65
  echo ""
66
66
  echo "Setup complete!"
package/tsconfig.json CHANGED
@@ -33,6 +33,7 @@
33
33
  "node_modules",
34
34
  ".next-build",
35
35
  ".next-prev",
36
+ ".deps",
36
37
  "public/sw.js",
37
38
  "app/sw.ts"
38
39
  ]
@@ -1,23 +0,0 @@
1
- // Native modules are compiled for one Node ABI. After a Node upgrade they fail
2
- // at the first database call, so check before starting and rebuild if needed.
3
- import { createRequire } from "module";
4
- import { execSync } from "child_process";
5
-
6
- const require = createRequire(import.meta.url);
7
- const modules = ["better-sqlite3", "node-pty"];
8
-
9
- function loads(name) {
10
- try {
11
- if (name === "better-sqlite3") require(name)(":memory:").close();
12
- else require(name);
13
- return true;
14
- } catch (err) {
15
- return !String(err?.message).includes("NODE_MODULE_VERSION");
16
- }
17
- }
18
-
19
- const stale = modules.filter((m) => !loads(m));
20
- if (stale.length) {
21
- console.log(`Rebuilding ${stale.join(", ")} for Node ${process.version}...`);
22
- execSync(`npm rebuild ${stale.join(" ")}`, { stdio: "inherit" });
23
- }