@proagentstore/cli 0.4.63 → 0.4.65

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.
Files changed (2) hide show
  1. package/dist/index.js +77 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1097,7 +1097,7 @@ var publishCommand = new Command6("publish").description("Publish an agent to Pr
1097
1097
  });
1098
1098
 
1099
1099
  // src/commands/runner/command.ts
1100
- import { spawn as spawn3 } from "child_process";
1100
+ import { spawn as spawn4 } from "child_process";
1101
1101
  import { randomUUID as randomUUID2 } from "crypto";
1102
1102
  import { Command as Command7 } from "commander";
1103
1103
 
@@ -1205,25 +1205,53 @@ async function waitForLocalRunner(opts, timeoutMs = 15e3) {
1205
1205
  import { hostname as hostname3 } from "os";
1206
1206
 
1207
1207
  // src/commands/runner/self-update.ts
1208
- import { execFile } from "child_process";
1208
+ import { execFile, spawn as spawn3 } from "child_process";
1209
1209
  import { promisify } from "util";
1210
1210
  var run = promisify(execFile);
1211
1211
  var RUNNER_UPDATE_PATH = "/pags/runner/update";
1212
1212
  var RUNNER_RESTART_EXIT_CODE = 75;
1213
1213
  var SUPERVISED_ENV = "PAGS_UP_SUPERVISED";
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;
1241
+ }
1214
1242
  function planRunnerUpdate(f) {
1215
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." };
1216
1244
  if (!f.latest) return { action: "refused", current: f.current, reason: `npm could not be asked for the latest ${CLI_PACKAGE} from this machine.` };
1217
1245
  if (!olderThan(f.current, f.latest)) return { action: "up-to-date", current: f.current };
1218
- if (!f.supervised) {
1246
+ if (!f.restarter) {
1219
1247
  return {
1220
1248
  action: "refused",
1221
1249
  current: f.current,
1222
- 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}\`.)`
1223
1251
  };
1224
1252
  }
1225
1253
  if (f.busy.length > 0) return { action: "wait", current: f.current, latest: f.latest, waitingFor: f.busy };
1226
- return { action: "update", current: f.current, latest: f.latest };
1254
+ return { action: "update", current: f.current, latest: f.latest, restarter: f.restarter };
1227
1255
  }
1228
1256
  async function installVersion(version2) {
1229
1257
  if (process.env[BOOTSTRAP_ENV]) {
@@ -1270,6 +1298,13 @@ function partitionByPin(instances, thisNode, alsoKnownAs = []) {
1270
1298
  for (const inst of instances) (isEligible(inst, thisNode, alsoKnownAs) ? here : elsewhere).push(inst);
1271
1299
  return { here, elsewhere };
1272
1300
  }
1301
+ function pinnedAway(held, instances, thisNode, alsoKnownAs = []) {
1302
+ const pins = new Map(instances.map((i) => [i.id, i.config?.runnerNode]));
1303
+ return [...held].filter((id) => {
1304
+ const pin = pins.get(id);
1305
+ return !!pin && pin !== thisNode && !alsoKnownAs.includes(pin);
1306
+ });
1307
+ }
1273
1308
  function registrationStatus(attached, registered) {
1274
1309
  const ids = [...attached];
1275
1310
  const have = ids.filter((id) => registered.has(id)).length;
@@ -1283,6 +1318,7 @@ function instanceLabel(inst) {
1283
1318
  function reattachPlan(request, state) {
1284
1319
  const target = typeof request?.attach === "string" && request.attach ? request.attach : null;
1285
1320
  const force = target !== null && request?.force === true;
1321
+ if (!state.watching && !target) return { target, unblock: false, detach: false, force: false, release: true };
1286
1322
  if (!state.watching && !(target && state.scope.includes(target))) {
1287
1323
  return {
1288
1324
  target,
@@ -1380,7 +1416,7 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1380
1416
  current: CLI_VERSION,
1381
1417
  latest: await latestPublishedVersion(),
1382
1418
  fromSource: runsFromSource(),
1383
- supervised: process.env[SUPERVISED_ENV] === "1",
1419
+ restarter: restarterFrom(process.env),
1384
1420
  busy: (sessions.sessions ?? []).filter((s) => s.alive && s.runState && s.runState !== "idle").map((s) => s.sessionId)
1385
1421
  };
1386
1422
  };
@@ -1390,7 +1426,7 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1390
1426
  writeLine(`Installed ${plan.latest} \u2014 restarting; every agent re-attaches on the way back up.`);
1391
1427
  setTimeout(() => {
1392
1428
  for (const id of [...attached.keys()]) detach(id);
1393
- process.exit(RUNNER_RESTART_EXIT_CODE);
1429
+ leaveForRestart(plan.restarter);
1394
1430
  }, 500).unref();
1395
1431
  };
1396
1432
  let updateWaiting = false;
@@ -1421,7 +1457,8 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1421
1457
  } catch (e) {
1422
1458
  return { status: 500, result: { error: `npm could not install ${plan.latest}: ${e instanceof Error ? e.message : String(e)}` } };
1423
1459
  }
1424
- return { status: 200, result: { action: "restarting", current: plan.current, latest: plan.latest } };
1460
+ const supervisor = supervisorNote(plan.restarter);
1461
+ return { status: 200, result: { action: "restarting", current: plan.current, latest: plan.latest, restartedBy: plan.restarter, ...supervisor ? { supervisor } : {} } };
1425
1462
  };
1426
1463
  const answerControl = async (path, body) => {
1427
1464
  if (path === RUNNER_UPDATE_PATH) return answerUpdate(body);
@@ -1430,6 +1467,13 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
1430
1467
  const named = typeof request?.attach === "string" ? request.attach : "";
1431
1468
  const plan = reattachPlan(request, { held: attached.has(named), blocked: blocked.has(named), watching: watchInstances, scope: instanceIds });
1432
1469
  if (plan.refuse) return { status: 409, result: { error: plan.refuse } };
1470
+ if (plan.release) {
1471
+ const res = await requestPags("GET", "/v1/instances/my/instances", { ...opts, pagsToken });
1472
+ const released = pinnedAway(attached.keys(), res.instances ?? [], runnerNode, machine.names);
1473
+ for (const id of released) detach(id, `${id.slice(0, 8)}\u2026 (pinned to another machine now)`);
1474
+ if (released.length && attached.size === 0) writeLine("This `pags up --instance` holds no agent now \u2014 close it, or restart it without --instance.");
1475
+ return { status: 200, result: { attached: [...attached.keys()], released } };
1476
+ }
1433
1477
  if (plan.target) {
1434
1478
  if (plan.unblock) blocked.delete(plan.target);
1435
1479
  if (plan.detach) detach(plan.target);
@@ -1728,7 +1772,7 @@ function createRunnerCommand() {
1728
1772
  const primary = instanceIds[0];
1729
1773
  const runnerOpts = { ...opts, host, port, token: runnerToken };
1730
1774
  const spec = runnerSpawnSpec(runnerOpts);
1731
- const runner = spawn3(spec.command, spec.args, {
1775
+ const runner = spawn4(spec.command, spec.args, {
1732
1776
  cwd: spec.cwd,
1733
1777
  stdio: ["ignore", "pipe", "pipe"],
1734
1778
  shell: process.platform === "win32"
@@ -1879,6 +1923,7 @@ function createRunnerCommand() {
1879
1923
  var runnerCommand = createRunnerCommand();
1880
1924
 
1881
1925
  // src/commands/up.ts
1926
+ import { execFileSync as execFileSync2 } from "child_process";
1882
1927
  import { createRequire as createRequire2 } from "module";
1883
1928
  import { Command as Command8 } from "commander";
1884
1929
  import { hostname as hostname5 } from "os";
@@ -2005,7 +2050,7 @@ var API_BASE3 = "https://api.proagentstore.online";
2005
2050
  var CLI_VERSION2 = createRequire2(import.meta.url)("../package.json").version;
2006
2051
  async function stopRunnerProcesses() {
2007
2052
  if (process.platform === "win32") return false;
2008
- const { execFileSync: execFileSync2 } = await import("child_process");
2053
+ const { execFileSync: execFileSync3 } = await import("child_process");
2009
2054
  const patterns = [
2010
2055
  "dist/browser-runner/index.js",
2011
2056
  "browser-runner/src/index",
@@ -2014,7 +2059,7 @@ async function stopRunnerProcesses() {
2014
2059
  let stopped = false;
2015
2060
  for (const p of patterns) {
2016
2061
  try {
2017
- execFileSync2("pkill", ["-f", p], { stdio: "ignore" });
2062
+ execFileSync3("pkill", ["-f", p], { stdio: "ignore" });
2018
2063
  stopped = true;
2019
2064
  } catch {
2020
2065
  }
@@ -2098,17 +2143,16 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2098
2143
  state.activeInstance = instances.length === 1 ? instances[0].name || instances[0].slug || instances[0].id.slice(0, 8) : `${instances.length} agents`;
2099
2144
  printStep(`Connecting ${state.activeInstance}\u2026`, "wait");
2100
2145
  await stopRunnerProcesses();
2101
- const { spawn: spawn4 } = await import("child_process");
2146
+ const { spawn: spawn5 } = await import("child_process");
2102
2147
  const cliPath = process.argv[1];
2103
2148
  const args = [cliPath, "runner", "connect", ...instances.map((i) => i.id)];
2104
2149
  if (opts.headless) args.push("--headless");
2105
2150
  if (opts.force) args.push("--force");
2106
2151
  if (!opts.instance) args.push("--watch-instances");
2107
- const spawnChild = () => spawn4(process.execPath, args, {
2152
+ const child = spawn5(process.execPath, args, {
2108
2153
  stdio: ["ignore", "pipe", "pipe"],
2109
- env: { ...process.env, PAGS_TOKEN: session.token, [SUPERVISED_ENV]: "1" }
2154
+ env: { ...process.env, PAGS_TOKEN: session.token, [SUPERVISED_ENV]: SUPERVISOR_RESTARTS }
2110
2155
  });
2111
- let child = spawnChild();
2112
2156
  const logs = [];
2113
2157
  const handleOutput = (data2) => {
2114
2158
  const text = data2.toString("utf-8");
@@ -2167,17 +2211,11 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2167
2211
  }
2168
2212
  };
2169
2213
  let childDead = false;
2170
- const wire = () => {
2171
- child.stdout?.on("data", handleOutput);
2172
- child.stderr?.on("data", handleOutput);
2173
- child.on("exit", onChildExit);
2174
- };
2175
- function onChildExit(code) {
2214
+ child.stdout?.on("data", handleOutput);
2215
+ child.stderr?.on("data", handleOutput);
2216
+ child.on("exit", (code) => {
2176
2217
  if (code === RUNNER_RESTART_EXIT_CODE) {
2177
- state.lastEvent = "Runner updated remotely \u2014 restarting on the new version";
2178
- printStatus(state);
2179
- child = spawnChild();
2180
- wire();
2218
+ restartUp("Runner updated remotely \u2014 restarting pags up on the new version\u2026", { PAGS_NO_PROMPT: "1" });
2181
2219
  return;
2182
2220
  }
2183
2221
  childDead = true;
@@ -2188,8 +2226,20 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2188
2226
  if (recent.length) state.lastEvent += ": " + recent[recent.length - 1].slice(0, 60);
2189
2227
  printStatus(state);
2190
2228
  }
2229
+ });
2230
+ function restartUp(why, env = {}) {
2231
+ writeLine(` ${why}`);
2232
+ try {
2233
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
2234
+ process.stdin.pause();
2235
+ execFileSync2(process.execPath, [process.argv[1], ...restartUpArgs(opts)], { stdio: "inherit", env: { ...process.env, ...env } });
2236
+ } catch (e) {
2237
+ const status = e.status;
2238
+ writeLine(` Restart failed${typeof status === "number" ? ` (exit ${status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
2239
+ process.exit(typeof status === "number" && status !== 0 ? status : 1);
2240
+ }
2241
+ process.exit(0);
2191
2242
  }
2192
- wire();
2193
2243
  const shutdown = () => {
2194
2244
  child.kill();
2195
2245
  clearScreen();
@@ -2217,25 +2267,7 @@ var upCommand = new Command8("up").description("Start the browser runner for all
2217
2267
  printStatus(state);
2218
2268
  }
2219
2269
  if (key === "r") {
2220
- if (childDead) {
2221
- writeLine(" Restarting runner...");
2222
- const { execFileSync: execFileSync2 } = await import("child_process");
2223
- const restartArgs = [process.argv[1], "up"];
2224
- if (opts.headless) restartArgs.push("--headless");
2225
- if (opts.force) restartArgs.push("--force");
2226
- if (opts.instance) restartArgs.push("--instance", opts.instance);
2227
- try {
2228
- execFileSync2(process.execPath, restartArgs, {
2229
- stdio: "inherit",
2230
- env: process.env
2231
- });
2232
- } catch (e) {
2233
- const status = e.status;
2234
- writeLine(` Restart failed${typeof status === "number" ? ` (exit ${status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
2235
- process.exit(typeof status === "number" && status !== 0 ? status : 1);
2236
- }
2237
- process.exit(0);
2238
- }
2270
+ if (childDead) restartUp("Restarting runner...");
2239
2271
  printStatus(state);
2240
2272
  }
2241
2273
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.63",
3
+ "version": "0.4.65",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",