@proagentstore/cli 0.2.9 → 0.2.11
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 +76 -38
- package/package.json +1 -1
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 as
|
|
10
|
+
import { createRequire as createRequire3 } from "module";
|
|
11
11
|
import { Command as Command8 } from "commander";
|
|
12
12
|
|
|
13
13
|
// src/commands/check.ts
|
|
@@ -620,7 +620,15 @@ import { arch, homedir as homedir2, hostname, platform } from "os";
|
|
|
620
620
|
import { resolve as resolve4 } from "path";
|
|
621
621
|
import { createServer as createServer2 } from "net";
|
|
622
622
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
623
|
+
import { createRequire } from "module";
|
|
623
624
|
import { Command as Command6 } from "commander";
|
|
625
|
+
var CLI_VERSION = (() => {
|
|
626
|
+
try {
|
|
627
|
+
return createRequire(import.meta.url)("../package.json").version;
|
|
628
|
+
} catch {
|
|
629
|
+
return "";
|
|
630
|
+
}
|
|
631
|
+
})();
|
|
624
632
|
async function findFreePort2(start) {
|
|
625
633
|
for (let p = start; p < start + 25; p++) {
|
|
626
634
|
const free = await new Promise((res) => {
|
|
@@ -906,52 +914,61 @@ function responseErrorMessage(data, text, statusText) {
|
|
|
906
914
|
function collectCapability(value, previous = []) {
|
|
907
915
|
return [...previous, value];
|
|
908
916
|
}
|
|
909
|
-
async function connectViaRelay(instanceIds, localUrl, runnerToken, opts) {
|
|
917
|
+
async function connectViaRelay(instanceIds, localUrl, runnerToken, opts, force = false) {
|
|
910
918
|
const apiBase = pagsApiBase(opts.apiBase).replace(/^http/, "ws");
|
|
911
919
|
const pagsToken = clean(opts.pagsToken) || clean(process.env.PAGS_TOKEN) || clean(loadSession()?.token);
|
|
912
920
|
if (!pagsToken) throw new Error("PAGS token required for WebSocket relay");
|
|
913
921
|
const capabilities = await requestRunner("GET", "/capabilities", { url: localUrl, token: runnerToken, instanceId: instanceIds[0] });
|
|
914
922
|
const caps = Array.isArray(capabilities.capabilities) ? capabilities.capabilities.filter((item) => typeof item === "string") : [];
|
|
915
923
|
for (const id of instanceIds) {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
+
try {
|
|
925
|
+
await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime`, opts, {
|
|
926
|
+
endpointUrl: localUrl,
|
|
927
|
+
token: runnerToken,
|
|
928
|
+
placement: "local",
|
|
929
|
+
capabilities: caps,
|
|
930
|
+
runnerVersion: CLI_VERSION,
|
|
931
|
+
runnerNode: hostname(),
|
|
932
|
+
force
|
|
933
|
+
});
|
|
934
|
+
} catch (e) {
|
|
935
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
936
|
+
if (msg.includes("409") && msg.includes("Another machine")) {
|
|
937
|
+
writeError(msg);
|
|
938
|
+
writeLine("");
|
|
939
|
+
writeLine(" To take over, run: pags up --tunnel ws --force");
|
|
940
|
+
writeLine("");
|
|
941
|
+
throw new Error("Another machine is already connected");
|
|
942
|
+
}
|
|
943
|
+
writeError(`register ${id.slice(0, 8)}\u2026 failed: ${msg}`);
|
|
944
|
+
}
|
|
924
945
|
}
|
|
925
946
|
for (const id of instanceIds) {
|
|
926
|
-
openRelaySocket(id, apiBase, pagsToken, localUrl, runnerToken);
|
|
947
|
+
openRelaySocket(id, apiBase, pagsToken, localUrl, runnerToken, force);
|
|
927
948
|
}
|
|
928
949
|
writeLine("Runtime registered with PAGS \u2713");
|
|
929
950
|
writeLine("");
|
|
930
951
|
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");
|
|
931
|
-
writeLine(
|
|
952
|
+
writeLine(` \u2705 CONNECTED \u2014 WebSocket relay \xB7 ${hostname()}`);
|
|
932
953
|
writeLine(` Agents: ${instanceIds.length} instance${instanceIds.length === 1 ? "" : "s"}`);
|
|
933
954
|
writeLine(" No cloudflared needed. Ctrl+C to disconnect.");
|
|
934
955
|
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");
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
while (!stopped) {
|
|
938
|
-
await new Promise((r) => setTimeout(r, 3e4));
|
|
939
|
-
if (stopped) break;
|
|
956
|
+
const heartbeat = () => {
|
|
957
|
+
const timer = setTimeout(async () => {
|
|
940
958
|
for (const id of instanceIds) {
|
|
941
959
|
await requestPags("POST", `/v1/instances/${apiPathSegment(id)}/runtime/heartbeat`, opts, {}).catch(() => void 0);
|
|
942
960
|
}
|
|
943
|
-
|
|
961
|
+
heartbeat();
|
|
962
|
+
}, 3e4);
|
|
963
|
+
timer.unref();
|
|
944
964
|
};
|
|
945
|
-
|
|
946
|
-
process.once("beforeExit", () => {
|
|
947
|
-
stopped = true;
|
|
948
|
-
});
|
|
965
|
+
heartbeat();
|
|
949
966
|
}
|
|
950
|
-
function openRelaySocket(instanceId, wsBase, pagsToken, localUrl, runnerToken) {
|
|
967
|
+
function openRelaySocket(instanceId, wsBase, pagsToken, localUrl, runnerToken, force = false) {
|
|
951
968
|
let backoffMs = 1e3;
|
|
952
969
|
let reconnecting = false;
|
|
953
970
|
const connect = () => {
|
|
954
|
-
const url = `${wsBase}/v1/relay/${encodeURIComponent(instanceId)}/connect?token=${encodeURIComponent(pagsToken)}`;
|
|
971
|
+
const url = `${wsBase}/v1/relay/${encodeURIComponent(instanceId)}/connect?token=${encodeURIComponent(pagsToken)}${force ? "&force=1" : ""}`;
|
|
955
972
|
const ws = new WebSocket(url);
|
|
956
973
|
ws.onopen = () => {
|
|
957
974
|
backoffMs = 1e3;
|
|
@@ -959,6 +976,13 @@ function openRelaySocket(instanceId, wsBase, pagsToken, localUrl, runnerToken) {
|
|
|
959
976
|
};
|
|
960
977
|
ws.onmessage = async (event) => {
|
|
961
978
|
const text = typeof event.data === "string" ? event.data : String(event.data);
|
|
979
|
+
if (text === "ping") {
|
|
980
|
+
try {
|
|
981
|
+
ws.send("pong");
|
|
982
|
+
} catch {
|
|
983
|
+
}
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
962
986
|
let cmd;
|
|
963
987
|
try {
|
|
964
988
|
cmd = JSON.parse(text);
|
|
@@ -978,16 +1002,29 @@ function openRelaySocket(instanceId, wsBase, pagsToken, localUrl, runnerToken) {
|
|
|
978
1002
|
headers,
|
|
979
1003
|
body: hasBody ? JSON.stringify(cmd.body) : void 0
|
|
980
1004
|
});
|
|
981
|
-
const
|
|
982
|
-
|
|
1005
|
+
const text2 = await res.text().catch(() => "");
|
|
1006
|
+
let result;
|
|
1007
|
+
try {
|
|
1008
|
+
result = text2 ? JSON.parse(text2) : {};
|
|
1009
|
+
} catch {
|
|
1010
|
+
result = { raw: text2.slice(0, 500) };
|
|
1011
|
+
}
|
|
1012
|
+
try {
|
|
1013
|
+
ws.send(JSON.stringify({ id: cmd.id, status: res.status, result }));
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
983
1016
|
} catch (err) {
|
|
984
|
-
|
|
1017
|
+
try {
|
|
1018
|
+
ws.send(JSON.stringify({ id: cmd.id, status: 500, error: err instanceof Error ? err.message : String(err) }));
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
985
1021
|
}
|
|
986
1022
|
};
|
|
987
|
-
ws.onclose = () => {
|
|
1023
|
+
ws.onclose = (ev) => {
|
|
988
1024
|
if (reconnecting) return;
|
|
989
1025
|
reconnecting = true;
|
|
990
|
-
|
|
1026
|
+
const reason = ev.code === 4401 || ev.code === 1008 ? " (token expired \u2014 run `pags login` then `pags up`)" : "";
|
|
1027
|
+
writeLine(`Relay disconnected: ${instanceId.slice(0, 8)}\u2026${reason} \u2014 reconnecting in ${Math.round(backoffMs / 1e3)}s`);
|
|
991
1028
|
setTimeout(() => {
|
|
992
1029
|
reconnecting = false;
|
|
993
1030
|
connect();
|
|
@@ -1006,7 +1043,7 @@ function createRunnerCommand() {
|
|
|
1006
1043
|
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) => {
|
|
1007
1044
|
await startRunnerForeground(opts);
|
|
1008
1045
|
});
|
|
1009
|
-
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").option("--tunnel <mode>", "Tunnel mode: 'ws' (WebSocket relay), 'named' (
|
|
1046
|
+
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").option("--tunnel <mode>", "Tunnel mode: 'ws' (WebSocket relay, default), 'named' (stable hostname), or 'quick' (cloudflared)", "ws").option("--force", "Take over from another connected machine").action(async (instanceIds, opts) => {
|
|
1010
1047
|
const runnerToken = clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN) || `pags_runner_${randomUUID()}`;
|
|
1011
1048
|
const host = clean(opts.host) || "127.0.0.1";
|
|
1012
1049
|
const port = clean(opts.port) || String(await findFreePort2(49171));
|
|
@@ -1046,7 +1083,7 @@ function createRunnerCommand() {
|
|
|
1046
1083
|
await waitForLocalRunner({ url: localUrl, token: runnerToken, instanceId: primary });
|
|
1047
1084
|
writeLine(`Local FAGS runtime healthy at ${localUrl}`);
|
|
1048
1085
|
if (opts.tunnel === "ws") {
|
|
1049
|
-
await connectViaRelay(instanceIds, localUrl, runnerToken, opts);
|
|
1086
|
+
await connectViaRelay(instanceIds, localUrl, runnerToken, opts, Boolean(opts.force));
|
|
1050
1087
|
await new Promise((resolvePromise) => {
|
|
1051
1088
|
runner.on("exit", () => resolvePromise());
|
|
1052
1089
|
});
|
|
@@ -1347,7 +1384,7 @@ function createRunnerCommand() {
|
|
|
1347
1384
|
}
|
|
1348
1385
|
|
|
1349
1386
|
// src/commands/up.ts
|
|
1350
|
-
import { createRequire } from "module";
|
|
1387
|
+
import { createRequire as createRequire2 } from "module";
|
|
1351
1388
|
import { Command as Command7 } from "commander";
|
|
1352
1389
|
|
|
1353
1390
|
// src/tui.ts
|
|
@@ -1454,7 +1491,7 @@ async function waitForKey(keys) {
|
|
|
1454
1491
|
|
|
1455
1492
|
// src/commands/up.ts
|
|
1456
1493
|
var API_BASE2 = "https://api.proagentstore.online";
|
|
1457
|
-
var
|
|
1494
|
+
var CLI_VERSION2 = createRequire2(import.meta.url)("../package.json").version;
|
|
1458
1495
|
async function stopRunnerProcesses() {
|
|
1459
1496
|
if (process.platform === "win32") return false;
|
|
1460
1497
|
const { execSync } = await import("child_process");
|
|
@@ -1474,7 +1511,7 @@ async function stopRunnerProcesses() {
|
|
|
1474
1511
|
}
|
|
1475
1512
|
return stopped;
|
|
1476
1513
|
}
|
|
1477
|
-
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").option("--tunnel <mode>", "Tunnel mode: 'ws' (WebSocket relay,
|
|
1514
|
+
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").option("--tunnel <mode>", "Tunnel mode: 'ws' (WebSocket relay, default), 'named' (stable hostname), or 'quick' (cloudflared)", "ws").option("--force", "Take over from another connected machine").action(async (opts) => {
|
|
1478
1515
|
const session = requireSession();
|
|
1479
1516
|
const state = {
|
|
1480
1517
|
user: session.user.login,
|
|
@@ -1486,10 +1523,10 @@ var upCommand = new Command7("up").description("Start the browser runner for all
|
|
|
1486
1523
|
registration: "pending",
|
|
1487
1524
|
lastEvent: "Fetching instances...",
|
|
1488
1525
|
taskCount: 0,
|
|
1489
|
-
version:
|
|
1526
|
+
version: CLI_VERSION2
|
|
1490
1527
|
};
|
|
1491
1528
|
clearScreen();
|
|
1492
|
-
printLogo(
|
|
1529
|
+
printLogo(CLI_VERSION2);
|
|
1493
1530
|
printStep("Signed in as " + session.user.login, "ok");
|
|
1494
1531
|
printStep("Fetching instances...", "wait");
|
|
1495
1532
|
const res = await fetch(`${API_BASE2}/v1/instances/my/instances`, {
|
|
@@ -1523,6 +1560,7 @@ var upCommand = new Command7("up").description("Start the browser runner for all
|
|
|
1523
1560
|
if (opts.headless) args.push("--headless");
|
|
1524
1561
|
if (opts.tunnel === "named") args.push("--tunnel", "named");
|
|
1525
1562
|
if (opts.tunnel === "ws") args.push("--tunnel", "ws");
|
|
1563
|
+
if (opts.force) args.push("--force");
|
|
1526
1564
|
const child = spawn3(process.execPath, args, {
|
|
1527
1565
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1528
1566
|
env: { ...process.env, PAGS_TOKEN: session.token }
|
|
@@ -1646,7 +1684,7 @@ var upCommand = new Command7("up").description("Start the browser runner for all
|
|
|
1646
1684
|
});
|
|
1647
1685
|
var downCommand = new Command7("down").description("Stop the browser runner and disconnect (cleans up any stuck processes)").action(async () => {
|
|
1648
1686
|
clearScreen();
|
|
1649
|
-
printLogo(
|
|
1687
|
+
printLogo(CLI_VERSION2);
|
|
1650
1688
|
if (process.platform === "win32") {
|
|
1651
1689
|
writeLine(" On Windows: switch to the 'pags up' window and press Ctrl+C to disconnect.");
|
|
1652
1690
|
writeLine("");
|
|
@@ -1664,7 +1702,7 @@ var downCommand = new Command7("down").description("Stop the browser runner and
|
|
|
1664
1702
|
});
|
|
1665
1703
|
|
|
1666
1704
|
// src/index.ts
|
|
1667
|
-
var require2 =
|
|
1705
|
+
var require2 = createRequire3(import.meta.url);
|
|
1668
1706
|
var { version } = require2("../package.json");
|
|
1669
1707
|
var program = new Command8();
|
|
1670
1708
|
program.name("pags").description(
|