@proagentstore/cli 0.4.150 → 0.4.151

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 +135 -90
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2395,6 +2395,101 @@ function releaseTrial(version2, cell, log, env = process.env, root = payloadRoot
2395
2395
  };
2396
2396
  }
2397
2397
 
2398
+ // src/commands/up-restart.ts
2399
+ import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "child_process";
2400
+ var UP_LAUNCHER_ENV = "PAGS_UP_LAUNCHER";
2401
+ var UP_LAUNCHER_PID_ENV = "PAGS_UP_LAUNCHER_PID";
2402
+ var RUNNER_DIED_EXIT_CODE = 76;
2403
+ function upRestartMode(env, parent = { pid: process.ppid, alive: pidAlive }) {
2404
+ if (env[SERVICE_ENV] === "1") return "service";
2405
+ if (env[RESTART_COMMAND_ENV]?.trim()) return "command";
2406
+ const launcher = Number(env[UP_LAUNCHER_PID_ENV]);
2407
+ if (env[UP_LAUNCHER_ENV] === "1" && launcher === parent.pid && parent.alive(launcher)) return "launcher-child";
2408
+ return "launcher";
2409
+ }
2410
+ function runnerExitAction(code, stopping) {
2411
+ if (code === RUNNER_RESTART_EXIT_CODE) return "restart";
2412
+ if (stopping) return "ignore";
2413
+ return code === 0 ? "stop" : "died";
2414
+ }
2415
+ function restartOwner(env = process.env, alive = pidAlive) {
2416
+ if (env[SERVICE_ENV] === "1") return "service";
2417
+ return env[UP_LAUNCHER_ENV] === "1" && alive(Number(env[UP_LAUNCHER_PID_ENV])) ? "launcher" : "none";
2418
+ }
2419
+ function relaunchDelayMs(failures) {
2420
+ return Math.min(5 * 6e4, 1e4 * 2 ** Math.max(0, failures - 1));
2421
+ }
2422
+ var RAN_MS = 6e4;
2423
+ var RESET_MS = 10 * 6e4;
2424
+ function processRestartDeps() {
2425
+ return {
2426
+ env: process.env,
2427
+ execPath: process.execPath,
2428
+ execArgv: process.execArgv,
2429
+ script: process.argv[1],
2430
+ spawn: nodeSpawn,
2431
+ spawnSync: nodeSpawnSync,
2432
+ exit: (code) => process.exit(code),
2433
+ log: () => {
2434
+ },
2435
+ sleep: (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms),
2436
+ step: (line) => logUpdateStep(line),
2437
+ parent: { pid: process.ppid, alive: pidAlive },
2438
+ pid: process.pid,
2439
+ now: Date.now
2440
+ };
2441
+ }
2442
+ function restartPagsUp(args, extraEnv, deps, opts = {}) {
2443
+ const env = { ...deps.env, ...extraEnv };
2444
+ const argv = [...deps.execArgv, deps.script, ...args];
2445
+ const exitCode = opts.exitCode ?? RUNNER_RESTART_EXIT_CODE;
2446
+ const mode = upRestartMode(env, deps.parent);
2447
+ deps.step(`${opts.initial ? "start" : "restart"}: pags ${args.join(" ")} \u2014 ${opts.initial ? "as" : "leaving via"} ${mode}${exitCode === RUNNER_DIED_EXIT_CODE ? " (the runner died)" : ""}`);
2448
+ if (mode === "service") {
2449
+ deps.exit(exitCode);
2450
+ return;
2451
+ }
2452
+ if (mode === "command") {
2453
+ leaveForRestart(mode, { spawn: deps.spawn, exit: deps.exit });
2454
+ return;
2455
+ }
2456
+ if (mode === "launcher-child") {
2457
+ deps.exit(exitCode);
2458
+ return;
2459
+ }
2460
+ const childEnv = { ...env, [UP_LAUNCHER_ENV]: "1", [UP_LAUNCHER_PID_ENV]: String(deps.pid) };
2461
+ let failures = 0;
2462
+ for (let first = true; ; first = false) {
2463
+ const unattended = !first || env.PAGS_NO_PROMPT === "1";
2464
+ deps.step(`launcher: starting pags up${unattended ? " (unattended)" : ""}`);
2465
+ const startedAt = deps.now();
2466
+ const r = deps.spawnSync(deps.execPath, argv, { stdio: "inherit", env: first ? childEnv : { ...childEnv, PAGS_NO_PROMPT: "1" } });
2467
+ const ran = deps.now() - startedAt;
2468
+ const how = r.error ? `could not start (${r.error.message})` : r.signal ? `was killed by ${r.signal}` : `exited ${r.status}`;
2469
+ deps.step(`launcher: pags up ${how} after ${Math.round(ran / 1e3)}s`);
2470
+ if (ran >= RESET_MS) failures = 0;
2471
+ if (r.status === RUNNER_RESTART_EXIT_CODE) {
2472
+ failures = 0;
2473
+ continue;
2474
+ }
2475
+ if (!r.error && (r.status === 0 || r.signal === "SIGINT")) {
2476
+ deps.exit(0);
2477
+ return;
2478
+ }
2479
+ if (unattended || r.status === RUNNER_DIED_EXIT_CODE || ran >= RAN_MS) {
2480
+ failures++;
2481
+ const wait = relaunchDelayMs(failures);
2482
+ deps.log(`pags up ${how} \u2014 starting it again in ${Math.round(wait / 1e3)}s (attempt ${failures + 1}).`);
2483
+ deps.step(`launcher: retrying in ${Math.round(wait / 1e3)}s (failure ${failures})`);
2484
+ deps.sleep(wait);
2485
+ continue;
2486
+ }
2487
+ if (!opts.initial) deps.log(`Restart failed${typeof r.status === "number" ? ` (exit ${r.status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
2488
+ deps.exit(typeof r.status === "number" && r.status !== 0 ? r.status : 1);
2489
+ return;
2490
+ }
2491
+ }
2492
+
2398
2493
  // src/commands/runner/relay.ts
2399
2494
  var MEMBERSHIP_SYNC_PATH = "/pags/membership/sync";
2400
2495
  var CLI_CONTROL_PATHS = /* @__PURE__ */ new Set([MEMBERSHIP_SYNC_PATH, RUNNER_UPDATE_PATH, RUNNER_RESTART_PATH]);
@@ -2690,7 +2785,9 @@ async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force =
2690
2785
  // #896: who is heartbeating. Set by `runner connect` from the lock it took.
2691
2786
  ...process.env.PAGS_RUNNER_RSID ? { rsid: process.env.PAGS_RUNNER_RSID } : {},
2692
2787
  pid: process.pid,
2693
- ...process.env.PAGS_RUNNER_LAUNCH ? { launch: process.env.PAGS_RUNNER_LAUNCH } : {}
2788
+ ...process.env.PAGS_RUNNER_LAUNCH ? { launch: process.env.PAGS_RUNNER_LAUNCH } : {},
2789
+ supervisor: restartOwner()
2790
+ // #1154: what starts it again if it dies
2694
2791
  },
2695
2792
  relayRttMs,
2696
2793
  sessions: sessions ?? void 0
@@ -3257,6 +3354,8 @@ function createRunnerCommand() {
3257
3354
  process.exit(1);
3258
3355
  }
3259
3356
  lockHeld = { path: taken.path, rsid: taken.lock.rsid, nonce: taken.lock.nonce, launch: taken.lock.launch };
3357
+ process.env.PAGS_RUNNER_RSID = taken.lock.rsid;
3358
+ process.env.PAGS_RUNNER_LAUNCH = taken.lock.launch;
3260
3359
  writeLine(`STATUS runner-session=${taken.lock.rsid}`);
3261
3360
  }
3262
3361
  const releaseHeldLock = () => {
@@ -3453,7 +3552,7 @@ import { readFileSync as readFileSync11, statSync as statSync2, watchFile } from
3453
3552
  import { Command as Command9 } from "commander";
3454
3553
 
3455
3554
  // src/background.ts
3456
- import { spawn as nodeSpawn } from "child_process";
3555
+ import { spawn as nodeSpawn2 } from "child_process";
3457
3556
  import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync10, renameSync as renameSync4, statSync, writeFileSync as writeFileSync6 } from "fs";
3458
3557
  import { dirname as dirname5, join as join11 } from "path";
3459
3558
  var BACKGROUND_ENV = "PAGS_BACKGROUND";
@@ -3492,7 +3591,7 @@ function backgroundRestartCommand(execPath, script, upArgs, platform = process.p
3492
3591
  const argv = [execPath, script, ...upArgs, "--background"].map((a) => shellQuote(a, platform)).join(" ");
3493
3592
  return platform === "win32" ? `set ${BACKGROUND_RESTART_ENV}=1&& ${argv}` : `${BACKGROUND_RESTART_ENV}=1 ${argv}`;
3494
3593
  }
3495
- function spawnDetached(command, args, env, logPath, spawn5 = nodeSpawn) {
3594
+ function spawnDetached(command, args, env, logPath, spawn5 = nodeSpawn2) {
3496
3595
  mkdirSync8(dirname5(logPath), { recursive: true });
3497
3596
  rotateLog(logPath);
3498
3597
  const fd = openSync3(logPath, "a", 384);
@@ -3709,75 +3808,6 @@ async function waitForKey(keys, onInterrupt) {
3709
3808
  });
3710
3809
  }
3711
3810
 
3712
- // src/commands/up-restart.ts
3713
- import { spawn as nodeSpawn2, spawnSync as nodeSpawnSync } from "child_process";
3714
- var UP_LAUNCHER_ENV = "PAGS_UP_LAUNCHER";
3715
- function upRestartMode(env) {
3716
- if (env[SERVICE_ENV] === "1") return "service";
3717
- if (env[RESTART_COMMAND_ENV]?.trim()) return "command";
3718
- if (env[UP_LAUNCHER_ENV] === "1") return "launcher-child";
3719
- return "launcher";
3720
- }
3721
- function relaunchDelayMs(failures) {
3722
- return Math.min(5 * 6e4, 1e4 * 2 ** Math.max(0, failures - 1));
3723
- }
3724
- function processRestartDeps() {
3725
- return {
3726
- env: process.env,
3727
- execPath: process.execPath,
3728
- execArgv: process.execArgv,
3729
- script: process.argv[1],
3730
- spawn: nodeSpawn2,
3731
- spawnSync: nodeSpawnSync,
3732
- exit: (code) => process.exit(code),
3733
- log: () => {
3734
- },
3735
- sleep: (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms),
3736
- step: (line) => logUpdateStep(line)
3737
- };
3738
- }
3739
- function restartPagsUp(args, extraEnv, deps) {
3740
- const env = { ...deps.env, ...extraEnv };
3741
- const argv = [...deps.execArgv, deps.script, ...args];
3742
- const mode = upRestartMode(env);
3743
- deps.step(`restart: pags ${args.join(" ")} \u2014 leaving via ${mode}`);
3744
- if (mode === "service" || mode === "command") {
3745
- leaveForRestart(mode, { spawn: deps.spawn, exit: deps.exit });
3746
- return;
3747
- }
3748
- if (mode === "launcher-child") {
3749
- deps.exit(RUNNER_RESTART_EXIT_CODE);
3750
- return;
3751
- }
3752
- let failures = 0;
3753
- for (let first = true; ; first = false) {
3754
- const unattended = !first || env.PAGS_NO_PROMPT === "1";
3755
- deps.step(`launcher: starting pags up${unattended ? " (unattended)" : ""}`);
3756
- const r = deps.spawnSync(deps.execPath, argv, { stdio: "inherit", env: { ...env, [UP_LAUNCHER_ENV]: "1", ...first ? {} : { PAGS_NO_PROMPT: "1" } } });
3757
- const how = r.error ? `could not start (${r.error.message})` : r.signal ? `was killed by ${r.signal}` : `exited ${r.status}`;
3758
- deps.step(`launcher: pags up ${how}`);
3759
- if (r.status === RUNNER_RESTART_EXIT_CODE) {
3760
- failures = 0;
3761
- continue;
3762
- }
3763
- if (!r.error && r.status === 0) {
3764
- deps.exit(0);
3765
- return;
3766
- }
3767
- if (unattended) {
3768
- failures++;
3769
- const wait = relaunchDelayMs(failures);
3770
- deps.log(`pags up ${how} \u2014 starting it again in ${Math.round(wait / 1e3)}s (attempt ${failures + 1}).`);
3771
- deps.step(`launcher: retrying in ${Math.round(wait / 1e3)}s (failure ${failures})`);
3772
- deps.sleep(wait);
3773
- continue;
3774
- }
3775
- deps.log(`Restart failed${typeof r.status === "number" ? ` (exit ${r.status})` : ""} \u2014 the runner is NOT running. Run 'pags up' again.`);
3776
- deps.exit(typeof r.status === "number" && r.status !== 0 ? r.status : 1);
3777
- return;
3778
- }
3779
- }
3780
-
3781
3811
  // src/runner-replace.ts
3782
3812
  var REPLACE_EXIT_TIMEOUT_MS = 3e4;
3783
3813
  function describeWork(work) {
@@ -3987,6 +4017,15 @@ start();
3987
4017
  var xml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3988
4018
  var unitWord = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3989
4019
  var ps = (s) => `'${s.replace(/'/g, "''")}'`;
4020
+ function versionManagedNode(nodePath) {
4021
+ if (/[\\/]n[\\/]versions[\\/]/.test(nodePath)) return "n";
4022
+ const m = /[\\/]\.?(nvm|fnm|volta|asdf|nodenv)[\\/]/i.exec(nodePath);
4023
+ return m ? m[1].toLowerCase() : null;
4024
+ }
4025
+ var pinnedNodeNote = (ctx) => {
4026
+ const manager = versionManagedNode(ctx.nodePath);
4027
+ return manager ? [`It runs Node ${ctx.nodePath}, installed by ${manager}. Removing or switching away from that Node version stops the service; after changing Node, run \`pags up --install-service\` again.`] : [];
4028
+ };
3990
4029
  function servicePlan(ctx) {
3991
4030
  const supervisor = supervisorPath(ctx);
3992
4031
  const log = serviceLogPath(ctx);
@@ -4010,6 +4049,7 @@ function servicePlan(ctx) {
4010
4049
  <key>KeepAlive</key><true/>
4011
4050
  <key>ThrottleInterval</key><integer>30</integer>
4012
4051
  <key>ProcessType</key><string>Background</string>
4052
+ <key>WorkingDirectory</key><string>${xml(ctx.home)}</string>
4013
4053
  <key>StandardOutPath</key><string>${xml(log)}</string>
4014
4054
  <key>StandardErrorPath</key><string>${xml(log)}</string>
4015
4055
  </dict>
@@ -4025,6 +4065,7 @@ function servicePlan(ctx) {
4025
4065
  { file: "launchctl", args: ["kickstart", "-k", `${domain}/${SERVICE_LABEL}`], why: "start it now" }
4026
4066
  ],
4027
4067
  notes: [
4068
+ ...pinnedNodeNote(ctx),
4028
4069
  `LaunchAgent ${plist} \u2014 runs while this user is logged in. On a machine that must survive a reboot unattended, turn on automatic login for this user.`,
4029
4070
  `Remove it with: pags up --uninstall-service`
4030
4071
  ]
@@ -4039,8 +4080,11 @@ function servicePlan(ctx) {
4039
4080
  $ErrorActionPreference = 'Stop'
4040
4081
  $action = New-ScheduledTaskAction -Execute ${ps(ctx.nodePath)} -Argument ${ps(`"${supervisor}"`)}
4041
4082
  $trigger = New-ScheduledTaskTrigger -AtStartup
4083
+ # Task Scheduler restarts a task that failed to START; a supervisor killed later just ends. Re-trigger it every five
4084
+ # minutes, forever \u2014 MultipleInstances IgnoreNew makes a trigger that finds it running do nothing (#1154).
4085
+ $trigger.Repetition = (New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)).Repetition
4042
4086
  $principal = New-ScheduledTaskPrincipal -UserId ${ps(principal)} -LogonType S4U -RunLevel Limited
4043
- $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
4087
+ $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew
4044
4088
  Unregister-ScheduledTask -TaskName ${ps(WINDOWS_TASK)} -Confirm:$false -ErrorAction SilentlyContinue
4045
4089
  Register-ScheduledTask -TaskName ${ps(WINDOWS_TASK)} -Action $action -Trigger $trigger -Principal $principal -Settings $settings | Out-Null
4046
4090
  Start-ScheduledTask -TaskName ${ps(WINDOWS_TASK)}
@@ -4051,6 +4095,7 @@ Start-ScheduledTask -TaskName ${ps(WINDOWS_TASK)}
4051
4095
  files,
4052
4096
  commands: [{ file: "powershell.exe", args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", task], why: "register and start the Scheduled Task" }],
4053
4097
  notes: [
4098
+ ...pinnedNodeNote(ctx),
4054
4099
  `Scheduled Task "${WINDOWS_TASK}" \u2014 starts at boot as ${principal || "this user"}, with nobody logged in. Registering it needs an elevated PowerShell: if it was refused, run ${task} from one.`,
4055
4100
  `Remove it with: pags up --uninstall-service`
4056
4101
  ]
@@ -4085,7 +4130,7 @@ WantedBy=default.target
4085
4130
  { file: "systemctl", args: ["--user", "enable", "--now", SYSTEMD_UNIT], why: "enable and start it" },
4086
4131
  { file: "loginctl", args: ["enable-linger", ...ctx.user ? [ctx.user] : []], optional: true, why: "keep it running with nobody logged in" }
4087
4132
  ],
4088
- notes: [`systemd user unit ${unit}. Logs: ${log} (and journalctl --user -u ${SYSTEMD_UNIT}).`, `Remove it with: pags up --uninstall-service`]
4133
+ notes: [...pinnedNodeNote(ctx), `systemd user unit ${unit}. Logs: ${log} (and journalctl --user -u ${SYSTEMD_UNIT}).`, `Remove it with: pags up --uninstall-service`]
4089
4134
  };
4090
4135
  }
4091
4136
  function serviceRemovalPlan(ctx) {
@@ -4149,6 +4194,10 @@ var upCommand = new Command10("up").description("Start the browser runner for al
4149
4194
  for (const line of uninstallService(serviceContext()).lines) writeLine(line);
4150
4195
  return;
4151
4196
  }
4197
+ if (!opts.background && !opts.installService && upRestartMode(process.env) === "launcher") {
4198
+ restartPagsUp(process.argv.slice(2), {}, { ...processRestartDeps(), log: (line) => writeLine(` ${line}`) }, { initial: true });
4199
+ return;
4200
+ }
4152
4201
  const session = requireSession();
4153
4202
  if (opts.installService) {
4154
4203
  const result = installService(serviceContext());
@@ -4355,33 +4404,29 @@ var upCommand = new Command10("up").description("Start the browser runner for al
4355
4404
  void deliverCrashReports({ apiBase: API_BASE3, token: session.token, account: session.user.login, paths: crashes }).catch(() => void 0);
4356
4405
  };
4357
4406
  child.on("exit", (code, signal) => {
4358
- if (code === RUNNER_RESTART_EXIT_CODE) {
4407
+ const action = runnerExitAction(code, stopping);
4408
+ if (action === "restart") {
4359
4409
  logUpdateStep("restart: runner connect asked for a restart (exit 75)");
4360
4410
  restartUp("Runner updated remotely \u2014 restarting pags up on the new version\u2026", { PAGS_NO_PROMPT: "1" });
4361
4411
  return;
4362
4412
  }
4363
4413
  childDead = true;
4364
4414
  logUpdateStep(`runner connect ${signal ? `was killed by ${signal}` : `exited ${code}`}${stopping ? " (stopping)" : ""}`);
4365
- if (code !== 0 && !stopping) recordConnectExit(code, signal ?? null, logs.slice(-20));
4366
- if (code === null) {
4367
- state.runner = "error";
4368
- state.lastEvent = "Runner stopped by another process (signal) \u2014 it is not connected any more";
4369
- printStatus(state);
4370
- return;
4371
- }
4372
- if (code !== 0) {
4373
- state.runner = "error";
4374
- state.lastEvent = `Runner exited (code ${code})`;
4375
- const recent = logs.slice(-5);
4376
- if (recent.length) state.lastEvent += ": " + recent[recent.length - 1].slice(0, 60);
4377
- printStatus(state);
4415
+ if (action === "ignore") return;
4416
+ if (action === "stop") {
4417
+ clearScreen();
4418
+ writeLine(" Runner stopped (pags down, or replaced by another pags up).");
4419
+ process.exit(0);
4378
4420
  }
4421
+ recordConnectExit(code, signal ?? null, logs.slice(-20));
4422
+ const recent = logs.slice(-5).at(-1);
4423
+ restartUp(`Runner ${signal ? `was killed by ${signal}` : `exited (code ${code})`}${recent ? `: ${recent.slice(0, 60)}` : ""} \u2014 starting it again\u2026`, { PAGS_NO_PROMPT: "1" }, RUNNER_DIED_EXIT_CODE);
4379
4424
  });
4380
- function restartUp(why, env = {}) {
4425
+ function restartUp(why, env = {}, exitCode) {
4381
4426
  writeLine(` ${why}`);
4382
4427
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
4383
4428
  process.stdin.pause();
4384
- restartPagsUp(restartUpArgs(opts), env, { ...processRestartDeps(), log: (line) => writeLine(` ${line}`) });
4429
+ restartPagsUp(restartUpArgs(opts), env, { ...processRestartDeps(), log: (line) => writeLine(` ${line}`) }, { exitCode });
4385
4430
  process.exit(1);
4386
4431
  }
4387
4432
  const shutdown = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.150",
3
+ "version": "0.4.151",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",