@proagentstore/cli 0.1.28 → 0.1.30

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 +34 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -912,17 +912,17 @@ function createRunnerCommand() {
912
912
  command.command("start").description("Start the local FAGS browser runtime in the foreground").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>", "Require this bearer token").option("--instance-id <id>", "Bind runner requests to a PAGS instance id").option("--headless", "Run Playwright headless").action(async (opts) => {
913
913
  await startRunnerForeground(opts);
914
914
  });
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) => {
915
+ command.command("connect <instanceIds...>").description("Start ONE local runtime, open a Cloudflare quick tunnel, and register it for every given PAGS instance").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 (instanceIds, opts) => {
916
916
  const runnerToken = clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN) || `pags_runner_${randomUUID()}`;
917
917
  const host = clean(opts.host) || "127.0.0.1";
918
918
  const port = clean(opts.port) || String(await findFreePort2(49171));
919
919
  const localUrl = `http://${host}:${port}`;
920
+ const primary = instanceIds[0];
920
921
  const runnerOpts = {
921
922
  ...opts,
922
923
  host,
923
924
  port,
924
- token: runnerToken,
925
- instanceId
925
+ token: runnerToken
926
926
  };
927
927
  const spec = runnerSpawnSpec(runnerOpts);
928
928
  const runner = spawn2(spec.command, spec.args, {
@@ -949,7 +949,7 @@ function createRunnerCommand() {
949
949
  process.exit(0);
950
950
  });
951
951
  try {
952
- await waitForLocalRunner({ url: localUrl, token: runnerToken, instanceId });
952
+ await waitForLocalRunner({ url: localUrl, token: runnerToken, instanceId: primary });
953
953
  writeLine(`Local FAGS runtime healthy at ${localUrl}`);
954
954
  const cloudflaredPath = await resolveCloudflaredCommand(opts.cloudflared);
955
955
  const openTunnel = () => new Promise((resolveTunnel, reject) => {
@@ -969,14 +969,21 @@ function createRunnerCommand() {
969
969
  setTimeout(() => reject(new Error("timed out waiting for cloudflared tunnel URL")), 45e3).unref();
970
970
  });
971
971
  const registerRuntime = async (url) => {
972
- const capabilities = await requestRunner("GET", "/capabilities", { url: localUrl, token: runnerToken, instanceId });
973
- await requestPags("POST", `/v1/instances/${apiPathSegment(instanceId)}/runtime`, opts, {
974
- endpointUrl: url,
975
- token: runnerToken,
976
- placement: "local",
977
- capabilities: Array.isArray(capabilities.capabilities) ? capabilities.capabilities.filter((item) => typeof item === "string") : [],
978
- runnerVersion: clean(opts.runnerVersion) || ""
979
- });
972
+ const capabilities = await requestRunner("GET", "/capabilities", { url: localUrl, token: runnerToken, instanceId: primary });
973
+ const caps = Array.isArray(capabilities.capabilities) ? capabilities.capabilities.filter((item) => typeof item === "string") : [];
974
+ for (const id of instanceIds) {
975
+ try {
976
+ await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime`, opts, {
977
+ endpointUrl: url,
978
+ token: runnerToken,
979
+ placement: "local",
980
+ capabilities: caps,
981
+ runnerVersion: clean(opts.runnerVersion) || ""
982
+ });
983
+ } catch (error) {
984
+ writeError(`register ${id.slice(0, 8)}\u2026 failed (${error instanceof Error ? error.message : String(error)}); will retry`);
985
+ }
986
+ }
980
987
  };
981
988
  const first = await openTunnel();
982
989
  tunnel = first.proc;
@@ -987,17 +994,16 @@ function createRunnerCommand() {
987
994
  writeLine("Runtime registered with PAGS \u2713");
988
995
  writeLine("");
989
996
  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");
990
- writeLine(" \u2705 CONNECTED \u2014 Browser runner is live");
991
- writeLine(` Instance: ${instanceId.slice(0, 8)}...`);
997
+ writeLine(" \u2705 CONNECTED \u2014 runner is live");
998
+ writeLine(` Agents: ${instanceIds.length} instance${instanceIds.length === 1 ? "" : "s"} served by this runner`);
992
999
  writeLine(` Tunnel: ${tunnelUrl}`);
993
1000
  writeLine(" Auto-reconnect is on \u2014 keep this terminal open. Ctrl+C to disconnect.");
994
1001
  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");
995
1002
  let stopped = false;
996
- const heartbeatPath = `/v1/instances/${apiPathSegment(instanceId)}/runtime/heartbeat`;
997
1003
  const probeTunnel = async (url) => {
998
1004
  try {
999
1005
  const res = await fetch(`${url.replace(/\/$/, "")}/health`, {
1000
- headers: { Authorization: `Bearer ${runnerToken}`, "X-PAGS-Instance-Id": instanceId },
1006
+ headers: { Authorization: `Bearer ${runnerToken}`, "X-PAGS-Instance-Id": primary },
1001
1007
  signal: AbortSignal.timeout(8e3)
1002
1008
  });
1003
1009
  return res.ok;
@@ -1005,12 +1011,20 @@ function createRunnerCommand() {
1005
1011
  return false;
1006
1012
  }
1007
1013
  };
1014
+ const heartbeatAll = async () => {
1015
+ let allOk = true;
1016
+ for (const id of instanceIds) {
1017
+ const ok = await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime/heartbeat`, opts, {}).then(() => true).catch(() => false);
1018
+ if (!ok) allOk = false;
1019
+ }
1020
+ return allOk;
1021
+ };
1008
1022
  void (async () => {
1009
1023
  while (!stopped) {
1010
1024
  await new Promise((r) => setTimeout(r, 3e4));
1011
1025
  if (stopped) break;
1012
1026
  if (await probeTunnel(tunnelUrl)) {
1013
- const beat = await requestPags("POST", heartbeatPath, opts, {}).then(() => true).catch(() => false);
1027
+ const beat = await heartbeatAll();
1014
1028
  if (!beat) {
1015
1029
  await registerRuntime(tunnelUrl).then(() => writeLine("\u2705 Re-registered with PAGS")).catch(() => void 0);
1016
1030
  }
@@ -1317,13 +1331,12 @@ var upCommand = new Command7("up").description("Start the browser runner for all
1317
1331
  for (const inst of state.instances) {
1318
1332
  writeLine(` ${inst.name} (${inst.id.slice(0, 8)}...)`);
1319
1333
  }
1320
- const target = instances[0];
1321
- state.activeInstance = target.name || target.slug || target.id.slice(0, 8);
1322
- printStep(`Connecting: ${state.activeInstance}`, "wait");
1334
+ state.activeInstance = instances.length === 1 ? instances[0].name || instances[0].slug || instances[0].id.slice(0, 8) : `${instances.length} agents`;
1335
+ printStep(`Connecting ${state.activeInstance}\u2026`, "wait");
1323
1336
  await stopRunnerProcesses();
1324
1337
  const { spawn: spawn3 } = await import("child_process");
1325
1338
  const cliPath = process.argv[1];
1326
- const args = [cliPath, "runner", "connect", target.id];
1339
+ const args = [cliPath, "runner", "connect", ...instances.map((i) => i.id)];
1327
1340
  if (opts.headless) args.push("--headless");
1328
1341
  const child = spawn3(process.execPath, args, {
1329
1342
  stdio: ["ignore", "pipe", "pipe"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",