@proagentstore/cli 0.1.28 → 0.1.29

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 +30 -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,17 @@ 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
+ await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime`, opts, {
976
+ endpointUrl: url,
977
+ token: runnerToken,
978
+ placement: "local",
979
+ capabilities: caps,
980
+ runnerVersion: clean(opts.runnerVersion) || ""
981
+ });
982
+ }
980
983
  };
981
984
  const first = await openTunnel();
982
985
  tunnel = first.proc;
@@ -987,17 +990,16 @@ function createRunnerCommand() {
987
990
  writeLine("Runtime registered with PAGS \u2713");
988
991
  writeLine("");
989
992
  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)}...`);
993
+ writeLine(" \u2705 CONNECTED \u2014 runner is live");
994
+ writeLine(` Agents: ${instanceIds.length} instance${instanceIds.length === 1 ? "" : "s"} served by this runner`);
992
995
  writeLine(` Tunnel: ${tunnelUrl}`);
993
996
  writeLine(" Auto-reconnect is on \u2014 keep this terminal open. Ctrl+C to disconnect.");
994
997
  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
998
  let stopped = false;
996
- const heartbeatPath = `/v1/instances/${apiPathSegment(instanceId)}/runtime/heartbeat`;
997
999
  const probeTunnel = async (url) => {
998
1000
  try {
999
1001
  const res = await fetch(`${url.replace(/\/$/, "")}/health`, {
1000
- headers: { Authorization: `Bearer ${runnerToken}`, "X-PAGS-Instance-Id": instanceId },
1002
+ headers: { Authorization: `Bearer ${runnerToken}`, "X-PAGS-Instance-Id": primary },
1001
1003
  signal: AbortSignal.timeout(8e3)
1002
1004
  });
1003
1005
  return res.ok;
@@ -1005,12 +1007,20 @@ function createRunnerCommand() {
1005
1007
  return false;
1006
1008
  }
1007
1009
  };
1010
+ const heartbeatAll = async () => {
1011
+ let allOk = true;
1012
+ for (const id of instanceIds) {
1013
+ const ok = await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime/heartbeat`, opts, {}).then(() => true).catch(() => false);
1014
+ if (!ok) allOk = false;
1015
+ }
1016
+ return allOk;
1017
+ };
1008
1018
  void (async () => {
1009
1019
  while (!stopped) {
1010
1020
  await new Promise((r) => setTimeout(r, 3e4));
1011
1021
  if (stopped) break;
1012
1022
  if (await probeTunnel(tunnelUrl)) {
1013
- const beat = await requestPags("POST", heartbeatPath, opts, {}).then(() => true).catch(() => false);
1023
+ const beat = await heartbeatAll();
1014
1024
  if (!beat) {
1015
1025
  await registerRuntime(tunnelUrl).then(() => writeLine("\u2705 Re-registered with PAGS")).catch(() => void 0);
1016
1026
  }
@@ -1317,13 +1327,12 @@ var upCommand = new Command7("up").description("Start the browser runner for all
1317
1327
  for (const inst of state.instances) {
1318
1328
  writeLine(` ${inst.name} (${inst.id.slice(0, 8)}...)`);
1319
1329
  }
1320
- const target = instances[0];
1321
- state.activeInstance = target.name || target.slug || target.id.slice(0, 8);
1322
- printStep(`Connecting: ${state.activeInstance}`, "wait");
1330
+ state.activeInstance = instances.length === 1 ? instances[0].name || instances[0].slug || instances[0].id.slice(0, 8) : `${instances.length} agents`;
1331
+ printStep(`Connecting ${state.activeInstance}\u2026`, "wait");
1323
1332
  await stopRunnerProcesses();
1324
1333
  const { spawn: spawn3 } = await import("child_process");
1325
1334
  const cliPath = process.argv[1];
1326
- const args = [cliPath, "runner", "connect", target.id];
1335
+ const args = [cliPath, "runner", "connect", ...instances.map((i) => i.id)];
1327
1336
  if (opts.headless) args.push("--headless");
1328
1337
  const child = spawn3(process.execPath, args, {
1329
1338
  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.29",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",