@proagentstore/cli 0.1.19 → 0.1.21

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.
@@ -57,3 +57,12 @@ const shutdown = async () => {
57
57
  };
58
58
  process.on("SIGINT", () => void shutdown());
59
59
  process.on("SIGTERM", () => void shutdown());
60
+ // Self-exit if our parent (the `runner connect` CLI) dies — otherwise we'd orphan
61
+ // and keep holding the port, making the NEXT `pags up` fail with EADDRINUSE / a 401
62
+ // against our stale token. When the parent dies we're reparented (ppid changes to
63
+ // launchd/init), so a changed ppid means it's gone.
64
+ const parentPid = process.ppid;
65
+ setInterval(() => {
66
+ if (process.ppid !== parentPid)
67
+ void shutdown();
68
+ }, 3000).unref();
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/index.ts
10
- import { createRequire } from "module";
10
+ import { createRequire as createRequire2 } from "module";
11
11
  import { Command as Command8 } from "commander";
12
12
 
13
13
  // src/commands/check.ts
@@ -618,8 +618,21 @@ import { chmod, mkdir, writeFile } from "fs/promises";
618
618
  import { gunzipSync } from "zlib";
619
619
  import { arch, homedir as homedir2, platform } from "os";
620
620
  import { resolve as resolve4 } from "path";
621
+ import { createServer as createServer2 } from "net";
621
622
  import { fileURLToPath as fileURLToPath2 } from "url";
622
623
  import { Command as Command6 } from "commander";
624
+ async function findFreePort2(start) {
625
+ for (let p = start; p < start + 25; p++) {
626
+ const free = await new Promise((res) => {
627
+ const s = createServer2();
628
+ s.once("error", () => res(false));
629
+ s.once("listening", () => s.close(() => res(true)));
630
+ s.listen(p, "127.0.0.1");
631
+ });
632
+ if (free) return p;
633
+ }
634
+ return start;
635
+ }
623
636
  var runnerCommand = createRunnerCommand();
624
637
  function runnerBaseUrl(url) {
625
638
  return (clean(url) || clean(process.env.PAGS_RUNNER_URL) || "http://127.0.0.1:49171").replace(/\/$/, "");
@@ -761,7 +774,11 @@ async function resolveCloudflaredCommand(requested) {
761
774
  return downloadCloudflared(cloudflaredAssetForPlatform());
762
775
  }
763
776
  function parseCloudflaredTunnelUrl(text) {
764
- return text.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i)?.[0] || null;
777
+ const matches = text.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/gi) || [];
778
+ for (const m of matches) {
779
+ if (!/^https:\/\/api\.trycloudflare\.com/i.test(m)) return m;
780
+ }
781
+ return null;
765
782
  }
766
783
  function buildRuntimeRegistrationBody(opts, capabilities = []) {
767
784
  return {
@@ -898,7 +915,7 @@ function createRunnerCommand() {
898
915
  command.command("connect <instanceId>").description("Start the local FAGS runtime, open a Cloudflare quick tunnel, and register it with PAGS").option("--host <host>", "Host to bind", "127.0.0.1").option("--port <port>", "Port to bind", "49171").option("--data-dir <path>", "Runner data directory").option("--token <token>", "Runner bearer token. Defaults to PAGS_RUNNER_TOKEN or a generated token").option("--headless", "Run Playwright headless").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").option("--cloudflared <path>", "cloudflared executable").option("--runner-version <version>", "Runner version").option("--skip-probe", "Skip PAGS FAGS-runtime probe after registration").action(async (instanceId, opts) => {
899
916
  const runnerToken = clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN) || `pags_runner_${randomUUID()}`;
900
917
  const host = clean(opts.host) || "127.0.0.1";
901
- const port = clean(opts.port) || "49171";
918
+ const port = clean(opts.port) || String(await findFreePort2(49171));
902
919
  const localUrl = `http://${host}:${port}`;
903
920
  const runnerOpts = {
904
921
  ...opts,
@@ -964,7 +981,9 @@ function createRunnerCommand() {
964
981
  const first = await openTunnel();
965
982
  tunnel = first.proc;
966
983
  let tunnelUrl = first.url;
967
- await registerRuntime(tunnelUrl);
984
+ await registerRuntime(tunnelUrl).catch((error) => {
985
+ writeError(`registration will retry (${error instanceof Error ? error.message : String(error)})`);
986
+ });
968
987
  writeLine("Runtime registered with PAGS \u2713");
969
988
  writeLine("");
970
989
  writeLine("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
@@ -991,7 +1010,10 @@ function createRunnerCommand() {
991
1010
  await new Promise((r) => setTimeout(r, 3e4));
992
1011
  if (stopped) break;
993
1012
  if (await probeTunnel(tunnelUrl)) {
994
- await requestPags("POST", heartbeatPath, opts, {}).catch(() => void 0);
1013
+ const beat = await requestPags("POST", heartbeatPath, opts, {}).then(() => true).catch(() => false);
1014
+ if (!beat) {
1015
+ await registerRuntime(tunnelUrl).then(() => writeLine("\u2705 Re-registered with PAGS")).catch(() => void 0);
1016
+ }
995
1017
  continue;
996
1018
  }
997
1019
  writeLine("\u26A0 Tunnel unreachable \u2014 reconnecting\u2026");
@@ -1129,6 +1151,7 @@ function createRunnerCommand() {
1129
1151
  }
1130
1152
 
1131
1153
  // src/commands/up.ts
1154
+ import { createRequire } from "module";
1132
1155
  import { Command as Command7 } from "commander";
1133
1156
 
1134
1157
  // src/tui.ts
@@ -1150,43 +1173,56 @@ var LOGO = [
1150
1173
  function clearScreen() {
1151
1174
  console.clear();
1152
1175
  }
1153
- function printLogo() {
1176
+ function printLogo(version2) {
1154
1177
  console.log("");
1155
1178
  for (const line of LOGO) {
1156
1179
  console.log(pad + c(line));
1157
1180
  }
1181
+ console.log(pad + d(" Browser runner") + (version2 ? d(` \xB7 v${version2}`) : ""));
1158
1182
  console.log("");
1159
1183
  }
1184
+ function describe(kind, s) {
1185
+ const ok = s === "online" || s === "registered";
1186
+ const busy = s === "starting" || s === "pending";
1187
+ const icon = ok ? chalk.green("\u2713") : busy ? chalk.yellow("\u2026") : chalk.red("\u2717");
1188
+ const map = {
1189
+ runner: { label: "Browser", ok: "running on your computer", busy: "starting up\u2026", bad: "stopped \u2014 press r to retry" },
1190
+ tunnel: { label: "Secure link", ok: "connected to ProAgentStore", busy: "opening\u2026", bad: "offline" },
1191
+ pags: { label: "ProAgentStore", ok: "connected \u2014 ready for jobs", busy: "registering\u2026", bad: "not registered (retries automatically)" }
1192
+ };
1193
+ const m = map[kind];
1194
+ return { icon, label: m.label, note: ok ? m.ok : busy ? m.busy : m.bad };
1195
+ }
1160
1196
  function printStatus(state) {
1161
1197
  clearScreen();
1162
- printLogo();
1163
- const statusIcon = (s) => {
1164
- if (s === "online" || s === "registered") return chalk.green("\u25CF");
1165
- if (s === "starting" || s === "pending") return chalk.yellow("\u25C9");
1166
- if (s === "error" || s === "failed") return chalk.red("\u25CF");
1167
- return d("\u25CB");
1168
- };
1169
- const statusText = (s) => {
1170
- if (s === "online" || s === "registered") return chalk.green(s);
1171
- if (s === "starting" || s === "pending") return chalk.yellow(s);
1172
- if (s === "error" || s === "failed") return chalk.red(s);
1173
- return d(s);
1198
+ printLogo(state.version);
1199
+ const connected = state.runner === "online" && state.tunnel === "online" && state.registration === "registered";
1200
+ console.log(pad + d("Signed in as ") + w(state.user) + d(" \xB7 agent: ") + w(state.activeInstance));
1201
+ console.log("");
1202
+ const row = (kind, s) => {
1203
+ const { icon, label, note } = describe(kind, s);
1204
+ console.log(pad + icon + " " + w(label.padEnd(13)) + d(note));
1174
1205
  };
1175
- console.log(pad + d("User ") + w(state.user));
1176
- console.log(pad + d("Instance ") + w(state.activeInstance));
1177
- console.log(pad + d("Runner ") + statusIcon(state.runner) + " " + statusText(state.runner));
1178
- console.log(pad + d("Tunnel ") + statusIcon(state.tunnel) + " " + statusText(state.tunnel));
1179
- console.log(pad + d("PAGS ") + statusIcon(state.registration) + " " + statusText(state.registration));
1180
- if (state.tunnelUrl) {
1181
- console.log(pad + d("URL ") + c(state.tunnelUrl));
1182
- }
1206
+ row("runner", state.runner);
1207
+ row("tunnel", state.tunnel);
1208
+ row("pags", state.registration);
1183
1209
  console.log("");
1184
- if (state.lastEvent) {
1185
- console.log(pad + d("Last: ") + d(state.lastEvent));
1210
+ if (connected) {
1211
+ console.log(pad + chalk.green("\u2713 You're all set!") + d(" Your agent can now act on the web."));
1212
+ console.log("");
1213
+ console.log(pad + w("What to do next:"));
1214
+ console.log(pad + d(" 1. Open ") + c("https://proagentstore.online/console"));
1215
+ console.log(pad + d(" 2. Pick your agent and paste a job link to apply"));
1216
+ console.log(pad + d(" 3. Keep ") + w("this window open") + d(" while your agent works"));
1217
+ console.log("");
1218
+ console.log(pad + d("The website can take a few seconds to show \u201Conline\u201D \u2014 that's normal."));
1219
+ } else {
1220
+ console.log(pad + d("Setting things up\u2026 this takes a few seconds. Keep this window open."));
1221
+ if (state.lastEvent) console.log(pad + d("Status: ") + d(state.lastEvent));
1186
1222
  }
1187
1223
  console.log("");
1188
1224
  console.log(
1189
- pad + c("r") + d(" Refresh ") + c("l") + d(" Logs ") + c("q") + d(" Quit")
1225
+ pad + c("r") + d(" Reconnect ") + c("l") + d(" View logs ") + c("q") + d(" Quit (disconnect)")
1190
1226
  );
1191
1227
  console.log("");
1192
1228
  }
@@ -1221,6 +1257,7 @@ async function waitForKey(keys) {
1221
1257
 
1222
1258
  // src/commands/up.ts
1223
1259
  var API_BASE2 = "https://api.proagentstore.online";
1260
+ var CLI_VERSION = createRequire(import.meta.url)("../package.json").version;
1224
1261
  var upCommand = new Command7("up").description("Start the browser runner for all your agent instances").option("--headless", "Run browser in headless mode").option("--instance <id>", "Connect to a specific instance only").action(async (opts) => {
1225
1262
  const session = requireSession();
1226
1263
  const state = {
@@ -1232,10 +1269,11 @@ var upCommand = new Command7("up").description("Start the browser runner for all
1232
1269
  tunnelUrl: "",
1233
1270
  registration: "pending",
1234
1271
  lastEvent: "Fetching instances...",
1235
- taskCount: 0
1272
+ taskCount: 0,
1273
+ version: CLI_VERSION
1236
1274
  };
1237
1275
  clearScreen();
1238
- printLogo();
1276
+ printLogo(CLI_VERSION);
1239
1277
  printStep("Signed in as " + session.user.login, "ok");
1240
1278
  printStep("Fetching instances...", "wait");
1241
1279
  const res = await fetch(`${API_BASE2}/v1/instances/my/instances`, {
@@ -1280,7 +1318,7 @@ var upCommand = new Command7("up").description("Start the browser runner for all
1280
1318
  logs.push(trimmed);
1281
1319
  if (logs.length > 200) logs.shift();
1282
1320
  const tunnelMatch = trimmed.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i);
1283
- if (tunnelMatch) {
1321
+ if (tunnelMatch && !/^https:\/\/api\.trycloudflare\.com/i.test(tunnelMatch[0])) {
1284
1322
  state.tunnel = "online";
1285
1323
  state.tunnelUrl = tunnelMatch[0];
1286
1324
  state.lastEvent = "Tunnel created";
@@ -1373,9 +1411,41 @@ var upCommand = new Command7("up").description("Start the browser runner for all
1373
1411
  }
1374
1412
  }
1375
1413
  });
1414
+ var downCommand = new Command7("down").description("Stop the browser runner and disconnect (cleans up any stuck processes)").action(async () => {
1415
+ clearScreen();
1416
+ printLogo(CLI_VERSION);
1417
+ if (process.platform === "win32") {
1418
+ writeLine(" On Windows: switch to the 'pags up' window and press Ctrl+C to disconnect.");
1419
+ writeLine("");
1420
+ return;
1421
+ }
1422
+ const { execSync } = await import("child_process");
1423
+ const patterns = [
1424
+ "dist/browser-runner/index.js",
1425
+ "browser-runner/src/index",
1426
+ "runner connect",
1427
+ "tunnel --url http://127.0.0.1"
1428
+ ];
1429
+ let stopped = false;
1430
+ for (const p of patterns) {
1431
+ try {
1432
+ execSync(`pkill -f ${JSON.stringify(p)}`, { stdio: "ignore" });
1433
+ stopped = true;
1434
+ } catch {
1435
+ }
1436
+ }
1437
+ if (stopped) {
1438
+ writeLine(" \u2713 Runner stopped \u2014 you're disconnected.");
1439
+ writeLine("");
1440
+ writeLine(" Your agent won't act on the web until you run 'pags up' again.");
1441
+ } else {
1442
+ writeLine(" No runner was running \u2014 nothing to stop.");
1443
+ }
1444
+ writeLine("");
1445
+ });
1376
1446
 
1377
1447
  // src/index.ts
1378
- var require2 = createRequire(import.meta.url);
1448
+ var require2 = createRequire2(import.meta.url);
1379
1449
  var { version } = require2("../package.json");
1380
1450
  var program = new Command8();
1381
1451
  program.name("pags").description(
@@ -1385,6 +1455,7 @@ program.addCommand(loginCommand);
1385
1455
  program.addCommand(logoutCommand);
1386
1456
  program.addCommand(whoamiCommand);
1387
1457
  program.addCommand(upCommand);
1458
+ program.addCommand(downCommand);
1388
1459
  program.addCommand(initCommand);
1389
1460
  program.addCommand(checkCommand);
1390
1461
  program.addCommand(publishCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",