@proagentstore/cli 0.4.62 → 0.4.64

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 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ BOOTSTRAP_ENV,
4
+ NO_SELF_UPDATE_ENV,
5
+ cachedPayloads,
6
+ installPayload,
7
+ latestPublishedVersion,
8
+ newestPayload,
9
+ olderThan,
10
+ payloadAt
11
+ } from "./chunk-JOWYSZOL.js";
12
+
13
+ // src/bootstrap.ts
14
+ import { existsSync } from "fs";
15
+ import { dirname, join } from "path";
16
+ import { fileURLToPath } from "url";
17
+
18
+ // src/bootstrap/stub.ts
19
+ import { pathToFileURL } from "url";
20
+ function checksForUpdate(args) {
21
+ return args.find((a) => !a.startsWith("-")) === "up";
22
+ }
23
+ async function runStub(deps) {
24
+ const log = deps.log ?? ((line) => process.stderr.write(`${line}
25
+ `));
26
+ const local = () => newestPayload([...deps.bundled ? [deps.bundled] : [], ...(deps.cached ?? cachedPayloads)()]);
27
+ let chosen = local();
28
+ if (checksForUpdate(deps.args) && !deps.fromSource && process.env[NO_SELF_UPDATE_ENV] !== "1") {
29
+ const latest = await (deps.latest ?? (() => latestPublishedVersion(15e3)))();
30
+ if (latest && (!chosen || olderThan(chosen.version, latest))) {
31
+ log(`pags: updating ${chosen?.version ?? "(none)"} \u2192 ${latest}\u2026`);
32
+ try {
33
+ chosen = await (deps.install ?? ((v) => installPayload(v)))(latest);
34
+ log(`pags: now on ${chosen.version}`);
35
+ } catch (e) {
36
+ log(`pags: could not fetch ${latest} (${e instanceof Error ? e.message : String(e)}) \u2014 starting ${chosen?.version ?? "nothing"} instead`);
37
+ }
38
+ }
39
+ }
40
+ if (!chosen) throw new Error("pags: no CLI to run \u2014 reinstall with `npm i -g @proagentstore/cli`");
41
+ process.env[BOOTSTRAP_ENV] = deps.self;
42
+ await (deps.load ?? ((entry) => import(pathToFileURL(entry).href)))(chosen.entry);
43
+ return chosen;
44
+ }
45
+
46
+ // src/bootstrap.ts
47
+ var self = fileURLToPath(import.meta.url);
48
+ var packageDir = join(dirname(self), "..");
49
+ try {
50
+ await runStub({
51
+ bundled: payloadAt(packageDir),
52
+ args: process.argv.slice(2),
53
+ self,
54
+ fromSource: existsSync(join(packageDir, "src", "bootstrap.ts"))
55
+ });
56
+ } catch (e) {
57
+ process.stderr.write(`${e instanceof Error ? e.message : String(e)}
58
+ `);
59
+ process.exit(1);
60
+ }
@@ -0,0 +1,97 @@
1
+ // src/bootstrap/payload.ts
2
+ import { execFile } from "child_process";
3
+ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync } from "fs";
4
+ import { homedir } from "os";
5
+ import { join } from "path";
6
+ import { promisify } from "util";
7
+ var run = promisify(execFile);
8
+ var CLI_PACKAGE = "@proagentstore/cli";
9
+ var BOOTSTRAP_ENV = "PAGS_BOOTSTRAP";
10
+ var NO_SELF_UPDATE_ENV = "PAGS_NO_SELF_UPDATE";
11
+ var KEEP = 2;
12
+ function payloadRoot() {
13
+ return process.env.PAGS_CLI_CACHE || join(homedir(), ".config", "proagentstore", "cli");
14
+ }
15
+ function olderThan(a, b) {
16
+ const parse = (v) => /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim())?.slice(1).map(Number);
17
+ const x = parse(a);
18
+ const y = parse(b);
19
+ if (!x || !y) return false;
20
+ for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] < y[i];
21
+ return false;
22
+ }
23
+ function payloadAt(dir) {
24
+ try {
25
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
26
+ if (pkg.name !== CLI_PACKAGE || !pkg.version) return null;
27
+ const entry = join(dir, pkg.pagsPayload || "dist/index.js");
28
+ return existsSync(entry) ? { version: pkg.version, entry } : null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ var installedAt = (prefix) => join(prefix, "node_modules", ...CLI_PACKAGE.split("/"));
34
+ var packageDir = (root, version) => installedAt(join(root, version));
35
+ function cachedPayloads(root = payloadRoot()) {
36
+ let names;
37
+ try {
38
+ names = readdirSync(root);
39
+ } catch {
40
+ return [];
41
+ }
42
+ return names.flatMap((v) => v.startsWith(".") ? [] : payloadAt(packageDir(root, v)) ?? []);
43
+ }
44
+ function newestPayload(payloads) {
45
+ return payloads.reduce((best, p) => !best || olderThan(best.version, p.version) ? p : best, null);
46
+ }
47
+ async function latestPublishedVersion(timeoutMs = 3e4) {
48
+ try {
49
+ const { stdout } = await run("npm", ["view", CLI_PACKAGE, "version"], { timeout: timeoutMs });
50
+ return stdout.trim() || null;
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ async function npmInstallInto(dir, version) {
56
+ try {
57
+ await run("npm", ["install", "--prefix", dir, "--omit=dev", "--no-audit", "--no-fund", "--no-save", `${CLI_PACKAGE}@${version}`], { timeout: 5 * 6e4 });
58
+ } catch (e) {
59
+ const stderr = String(e.stderr ?? "").trim();
60
+ throw new Error((stderr || (e instanceof Error ? e.message : String(e))).slice(-400));
61
+ }
62
+ }
63
+ async function installPayload(version, root = payloadRoot(), install = npmInstallInto) {
64
+ const done = payloadAt(packageDir(root, version));
65
+ if (done) return done;
66
+ mkdirSync(root, { recursive: true });
67
+ const scratch = mkdtempSync(join(root, `.install-${version}-`));
68
+ try {
69
+ await install(scratch, version);
70
+ if (payloadAt(installedAt(scratch))?.version !== version) throw new Error(`npm installed no ${CLI_PACKAGE}@${version}`);
71
+ try {
72
+ renameSync(scratch, join(root, version));
73
+ } catch (e) {
74
+ if (!payloadAt(packageDir(root, version))) throw e;
75
+ }
76
+ } finally {
77
+ rmSync(scratch, { recursive: true, force: true });
78
+ }
79
+ const payload = payloadAt(packageDir(root, version));
80
+ if (!payload) throw new Error(`${CLI_PACKAGE}@${version} is not usable after install`);
81
+ for (const old of cachedPayloads(root).sort((a, b) => olderThan(a.version, b.version) ? 1 : -1).slice(KEEP)) {
82
+ rmSync(join(root, old.version), { recursive: true, force: true });
83
+ }
84
+ return payload;
85
+ }
86
+
87
+ export {
88
+ CLI_PACKAGE,
89
+ BOOTSTRAP_ENV,
90
+ NO_SELF_UPDATE_ENV,
91
+ olderThan,
92
+ payloadAt,
93
+ cachedPayloads,
94
+ newestPayload,
95
+ latestPublishedVersion,
96
+ installPayload
97
+ };
package/dist/index.js CHANGED
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ BOOTSTRAP_ENV,
4
+ CLI_PACKAGE,
5
+ installPayload,
6
+ latestPublishedVersion,
7
+ olderThan
8
+ } from "./chunk-JOWYSZOL.js";
2
9
 
3
10
  // src/index.ts
4
11
  import { createRequire as createRequire3 } from "module";
@@ -1090,7 +1097,7 @@ var publishCommand = new Command6("publish").description("Publish an agent to Pr
1090
1097
  });
1091
1098
 
1092
1099
  // src/commands/runner/command.ts
1093
- import { spawn as spawn3 } from "child_process";
1100
+ import { spawn as spawn4 } from "child_process";
1094
1101
  import { randomUUID as randomUUID2 } from "crypto";
1095
1102
  import { Command as Command7 } from "commander";
1096
1103
 
@@ -1198,44 +1205,59 @@ async function waitForLocalRunner(opts, timeoutMs = 15e3) {
1198
1205
  import { hostname as hostname3 } from "os";
1199
1206
 
1200
1207
  // src/commands/runner/self-update.ts
1201
- import { execFile } from "child_process";
1208
+ import { execFile, spawn as spawn3 } from "child_process";
1202
1209
  import { promisify } from "util";
1203
1210
  var run = promisify(execFile);
1204
1211
  var RUNNER_UPDATE_PATH = "/pags/runner/update";
1205
1212
  var RUNNER_RESTART_EXIT_CODE = 75;
1206
1213
  var SUPERVISED_ENV = "PAGS_UP_SUPERVISED";
1207
- var CLI_PACKAGE = "@proagentstore/cli";
1208
- function olderThan(a, b) {
1209
- const parse = (v) => /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim())?.slice(1).map(Number);
1210
- const x = parse(a);
1211
- const y = parse(b);
1212
- if (!x || !y) return false;
1213
- for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] < y[i];
1214
- return false;
1214
+ var SUPERVISOR_RESTARTS = "2";
1215
+ var SERVICE_ENV = "PAGS_SERVICE";
1216
+ var RESTART_COMMAND_ENV = "PAGS_RESTART_COMMAND";
1217
+ function restarterFrom(env) {
1218
+ if (env[SUPERVISED_ENV] === SUPERVISOR_RESTARTS) return "pags-up";
1219
+ if (env[SUPERVISED_ENV] === "1") return "pags-up-child-only";
1220
+ if (env[SERVICE_ENV] === "1") return "service";
1221
+ if (env[RESTART_COMMAND_ENV]?.trim()) return "command";
1222
+ return null;
1223
+ }
1224
+ function supervisorNote(restarter) {
1225
+ return restarter === "pags-up-child-only" ? "The `pags up` on this machine predates supervisor restarts (#860): the runner comes back on the new release, but the `pags up` window keeps running its own older code until it is restarted there once." : null;
1226
+ }
1227
+ function leaveForRestart(restarter, deps = { spawn: spawn3, exit: (code) => process.exit(code) }) {
1228
+ if (restarter === "command") {
1229
+ deps.spawn(process.env[RESTART_COMMAND_ENV], { shell: true, detached: true, stdio: "ignore", env: process.env }).unref();
1230
+ deps.exit(0);
1231
+ return;
1232
+ }
1233
+ deps.exit(RUNNER_RESTART_EXIT_CODE);
1234
+ }
1235
+ function restartUpArgs(opts) {
1236
+ const args = ["up"];
1237
+ if (opts.headless) args.push("--headless");
1238
+ if (opts.force) args.push("--force");
1239
+ if (opts.instance) args.push("--instance", opts.instance);
1240
+ return args;
1215
1241
  }
1216
1242
  function planRunnerUpdate(f) {
1217
1243
  if (f.fromSource) return { action: "refused", current: f.current, reason: "This runner runs from a source checkout \u2014 update it with `git pull` there, not npm." };
1218
1244
  if (!f.latest) return { action: "refused", current: f.current, reason: `npm could not be asked for the latest ${CLI_PACKAGE} from this machine.` };
1219
1245
  if (!olderThan(f.current, f.latest)) return { action: "up-to-date", current: f.current };
1220
- if (!f.supervised) {
1246
+ if (!f.restarter) {
1221
1247
  return {
1222
1248
  action: "refused",
1223
1249
  current: f.current,
1224
- reason: `This runner was not started by a \`pags up\` that can restart it (\`pags runner connect\` directly, or a \`pags up\` older than the respawn). Update once at the machine: \`npm i -g ${CLI_PACKAGE}\` and restart \`pags up\` \u2014 later updates can then be done remotely.`
1250
+ reason: `Nothing would start this runner again after an update: it was not started by \`pags up\`, and neither ${SERVICE_ENV}=1 (a launchd/systemd unit that restarts it on a non-zero exit) nor ${RESTART_COMMAND_ENV} (a command that starts it again) is set. Run it under \`pags up\`, or set one of the two where it is started \u2014 later updates can then be done remotely. (A \`pags up\` older than 0.4.62 cannot restart it either: update once at the machine with \`npm i -g ${CLI_PACKAGE}\`.)`
1225
1251
  };
1226
1252
  }
1227
1253
  if (f.busy.length > 0) return { action: "wait", current: f.current, latest: f.latest, waitingFor: f.busy };
1228
- return { action: "update", current: f.current, latest: f.latest };
1229
- }
1230
- async function latestPublishedVersion() {
1231
- try {
1232
- const { stdout } = await run("npm", ["view", CLI_PACKAGE, "version"], { timeout: 3e4 });
1233
- return stdout.trim() || null;
1234
- } catch {
1235
- return null;
1236
- }
1254
+ return { action: "update", current: f.current, latest: f.latest, restarter: f.restarter };
1237
1255
  }
1238
1256
  async function installVersion(version2) {
1257
+ if (process.env[BOOTSTRAP_ENV]) {
1258
+ await installPayload(version2);
1259
+ return;
1260
+ }
1239
1261
  try {
1240
1262
  await run("npm", ["i", "-g", `${CLI_PACKAGE}@${version2}`], { timeout: 5 * 6e4 });
1241
1263
  } catch (e) {
@@ -1386,7 +1408,7 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1386
1408
  current: CLI_VERSION,
1387
1409
  latest: await latestPublishedVersion(),
1388
1410
  fromSource: runsFromSource(),
1389
- supervised: process.env[SUPERVISED_ENV] === "1",
1411
+ restarter: restarterFrom(process.env),
1390
1412
  busy: (sessions.sessions ?? []).filter((s) => s.alive && s.runState && s.runState !== "idle").map((s) => s.sessionId)
1391
1413
  };
1392
1414
  };
@@ -1396,7 +1418,7 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1396
1418
  writeLine(`Installed ${plan.latest} \u2014 restarting; every agent re-attaches on the way back up.`);
1397
1419
  setTimeout(() => {
1398
1420
  for (const id of [...attached.keys()]) detach(id);
1399
- process.exit(RUNNER_RESTART_EXIT_CODE);
1421
+ leaveForRestart(plan.restarter);
1400
1422
  }, 500).unref();
1401
1423
  };
1402
1424
  let updateWaiting = false;
@@ -1427,7 +1449,8 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1427
1449
  } catch (e) {
1428
1450
  return { status: 500, result: { error: `npm could not install ${plan.latest}: ${e instanceof Error ? e.message : String(e)}` } };
1429
1451
  }
1430
- return { status: 200, result: { action: "restarting", current: plan.current, latest: plan.latest } };
1452
+ const supervisor = supervisorNote(plan.restarter);
1453
+ return { status: 200, result: { action: "restarting", current: plan.current, latest: plan.latest, restartedBy: plan.restarter, ...supervisor ? { supervisor } : {} } };
1431
1454
  };
1432
1455
  const answerControl = async (path, body) => {
1433
1456
  if (path === RUNNER_UPDATE_PATH) return answerUpdate(body);
@@ -1734,7 +1757,7 @@ function createRunnerCommand() {
1734
1757
  const primary = instanceIds[0];
1735
1758
  const runnerOpts = { ...opts, host, port, token: runnerToken };
1736
1759
  const spec = runnerSpawnSpec(runnerOpts);
1737
- const runner = spawn3(spec.command, spec.args, {
1760
+ const runner = spawn4(spec.command, spec.args, {
1738
1761
  cwd: spec.cwd,
1739
1762
  stdio: ["ignore", "pipe", "pipe"],
1740
1763
  shell: process.platform === "win32"
@@ -1885,6 +1908,7 @@ function createRunnerCommand() {
1885
1908
  var runnerCommand = createRunnerCommand();
1886
1909
 
1887
1910
  // src/commands/up.ts
1911
+ import { execFileSync as execFileSync2 } from "child_process";
1888
1912
  import { createRequire as createRequire2 } from "module";
1889
1913
  import { Command as Command8 } from "commander";
1890
1914
  import { hostname as hostname5 } from "os";
@@ -2011,7 +2035,7 @@ var API_BASE3 = "https://api.proagentstore.online";
2011
2035
  var CLI_VERSION2 = createRequire2(import.meta.url)("../package.json").version;
2012
2036
  async function stopRunnerProcesses() {
2013
2037
  if (process.platform === "win32") return false;
2014
- const { execFileSync: execFileSync2 } = await import("child_process");
2038
+ const { execFileSync: execFileSync3 } = await import("child_process");
2015
2039
  const patterns = [
2016
2040
  "dist/browser-runner/index.js",
2017
2041
  "browser-runner/src/index",
@@ -2020,7 +2044,7 @@ async function stopRunnerProcesses() {
2020
2044
  let stopped = false;
2021
2045
  for (const p of patterns) {
2022
2046
  try {
2023
- execFileSync2("pkill", ["-f", p], { stdio: "ignore" });
2047
+ execFileSync3("pkill", ["-f", p], { stdio: "ignore" });
2024
2048
  stopped = true;
2025
2049
  } catch {
2026
2050
  }
@@ -2104,17 +2128,16 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2104
2128
  state.activeInstance = instances.length === 1 ? instances[0].name || instances[0].slug || instances[0].id.slice(0, 8) : `${instances.length} agents`;
2105
2129
  printStep(`Connecting ${state.activeInstance}\u2026`, "wait");
2106
2130
  await stopRunnerProcesses();
2107
- const { spawn: spawn4 } = await import("child_process");
2131
+ const { spawn: spawn5 } = await import("child_process");
2108
2132
  const cliPath = process.argv[1];
2109
2133
  const args = [cliPath, "runner", "connect", ...instances.map((i) => i.id)];
2110
2134
  if (opts.headless) args.push("--headless");
2111
2135
  if (opts.force) args.push("--force");
2112
2136
  if (!opts.instance) args.push("--watch-instances");
2113
- const spawnChild = () => spawn4(process.execPath, args, {
2137
+ const child = spawn5(process.execPath, args, {
2114
2138
  stdio: ["ignore", "pipe", "pipe"],
2115
- env: { ...process.env, PAGS_TOKEN: session.token, [SUPERVISED_ENV]: "1" }
2139
+ env: { ...process.env, PAGS_TOKEN: session.token, [SUPERVISED_ENV]: SUPERVISOR_RESTARTS }
2116
2140
  });
2117
- let child = spawnChild();
2118
2141
  const logs = [];
2119
2142
  const handleOutput = (data2) => {
2120
2143
  const text = data2.toString("utf-8");
@@ -2173,17 +2196,11 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2173
2196
  }
2174
2197
  };
2175
2198
  let childDead = false;
2176
- const wire = () => {
2177
- child.stdout?.on("data", handleOutput);
2178
- child.stderr?.on("data", handleOutput);
2179
- child.on("exit", onChildExit);
2180
- };
2181
- function onChildExit(code) {
2199
+ child.stdout?.on("data", handleOutput);
2200
+ child.stderr?.on("data", handleOutput);
2201
+ child.on("exit", (code) => {
2182
2202
  if (code === RUNNER_RESTART_EXIT_CODE) {
2183
- state.lastEvent = "Runner updated remotely \u2014 restarting on the new version";
2184
- printStatus(state);
2185
- child = spawnChild();
2186
- wire();
2203
+ restartUp("Runner updated remotely \u2014 restarting pags up on the new version\u2026", { PAGS_NO_PROMPT: "1" });
2187
2204
  return;
2188
2205
  }
2189
2206
  childDead = true;
@@ -2194,8 +2211,20 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2194
2211
  if (recent.length) state.lastEvent += ": " + recent[recent.length - 1].slice(0, 60);
2195
2212
  printStatus(state);
2196
2213
  }
2214
+ });
2215
+ function restartUp(why, env = {}) {
2216
+ writeLine(` ${why}`);
2217
+ try {
2218
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
2219
+ process.stdin.pause();
2220
+ execFileSync2(process.execPath, [process.argv[1], ...restartUpArgs(opts)], { stdio: "inherit", env: { ...process.env, ...env } });
2221
+ } catch (e) {
2222
+ const status = e.status;
2223
+ writeLine(` Restart failed${typeof status === "number" ? ` (exit ${status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
2224
+ process.exit(typeof status === "number" && status !== 0 ? status : 1);
2225
+ }
2226
+ process.exit(0);
2197
2227
  }
2198
- wire();
2199
2228
  const shutdown = () => {
2200
2229
  child.kill();
2201
2230
  clearScreen();
@@ -2223,25 +2252,7 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2223
2252
  printStatus(state);
2224
2253
  }
2225
2254
  if (key === "r") {
2226
- if (childDead) {
2227
- writeLine(" Restarting runner...");
2228
- const { execFileSync: execFileSync2 } = await import("child_process");
2229
- const restartArgs = [process.argv[1], "up"];
2230
- if (opts.headless) restartArgs.push("--headless");
2231
- if (opts.force) restartArgs.push("--force");
2232
- if (opts.instance) restartArgs.push("--instance", opts.instance);
2233
- try {
2234
- execFileSync2(process.execPath, restartArgs, {
2235
- stdio: "inherit",
2236
- env: process.env
2237
- });
2238
- } catch (e) {
2239
- const status = e.status;
2240
- writeLine(` Restart failed${typeof status === "number" ? ` (exit ${status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
2241
- process.exit(typeof status === "number" && status !== 0 ? status : 1);
2242
- }
2243
- process.exit(0);
2244
- }
2255
+ if (childDead) restartUp("Restarting runner...");
2245
2256
  printStatus(state);
2246
2257
  }
2247
2258
  }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.62",
3
+ "version": "0.4.64",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
- "pags": "dist/index.js"
8
+ "pags": "dist/bootstrap.js"
9
9
  },
10
+ "pagsPayload": "dist/index.js",
10
11
  "homepage": "https://github.com/ProAgentStore/platform#readme",
11
12
  "repository": {
12
13
  "type": "git",
@@ -25,7 +26,7 @@
25
26
  "access": "public"
26
27
  },
27
28
  "scripts": {
28
- "build": "pnpm --filter @proagentstore/browser-runner build && tsup src/index.ts --format esm --dts && node scripts/copy-browser-runner.mjs",
29
+ "build": "pnpm --filter @proagentstore/browser-runner build && tsup src/index.ts src/bootstrap.ts --format esm --dts && node scripts/copy-browser-runner.mjs",
29
30
  "dev": "tsx src/index.ts",
30
31
  "typecheck": "tsc --noEmit"
31
32
  },