@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.
- package/dist/index.js +30 -21
- 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 <
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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
|
|
991
|
-
writeLine(`
|
|
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":
|
|
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
|
|
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
|
-
|
|
1321
|
-
state.activeInstance
|
|
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",
|
|
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"],
|