@proagentstore/cli 0.1.8 → 0.1.9
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/browser-runner/runner.js +253 -38
- package/dist/browser-runner/test-job-server-auth-cli.js +44 -0
- package/dist/browser-runner/test-job-server-auth.js +450 -0
- package/dist/chunk-LNY7MNUC.js +423 -0
- package/dist/index.js +204 -397
- package/dist/runner-CM72EPKQ.js +34 -0
- package/package.json +1 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/commands/runner.ts
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
import { existsSync } from "fs";
|
|
12
|
+
import { resolve } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { Command } from "commander";
|
|
15
|
+
|
|
16
|
+
// src/output.ts
|
|
17
|
+
function writeLine(message = "") {
|
|
18
|
+
process.stdout.write(`${message}
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
function writeError(message = "") {
|
|
22
|
+
process.stderr.write(`${message}
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/commands/runner.ts
|
|
27
|
+
var runnerCommand = createRunnerCommand();
|
|
28
|
+
function runnerBaseUrl(url) {
|
|
29
|
+
return (clean(url) || clean(process.env.PAGS_RUNNER_URL) || "http://127.0.0.1:49171").replace(/\/$/, "");
|
|
30
|
+
}
|
|
31
|
+
function pagsApiBase(url) {
|
|
32
|
+
return (clean(url) || clean(process.env.PAGS_API_BASE) || "https://api.proagentstore.online").replace(/\/$/, "");
|
|
33
|
+
}
|
|
34
|
+
function pagsHeaders(token) {
|
|
35
|
+
const resolved = clean(token) || clean(process.env.PAGS_TOKEN);
|
|
36
|
+
return resolved ? { Authorization: `Bearer ${resolved}` } : {};
|
|
37
|
+
}
|
|
38
|
+
function runnerHeaders(token) {
|
|
39
|
+
const resolved = clean(token) || clean(process.env.PAGS_RUNNER_TOKEN);
|
|
40
|
+
return resolved ? { Authorization: `Bearer ${resolved}` } : {};
|
|
41
|
+
}
|
|
42
|
+
function runnerRequestHeaders(opts) {
|
|
43
|
+
const resolved = clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN);
|
|
44
|
+
const headers = resolved ? { Authorization: `Bearer ${resolved}` } : {};
|
|
45
|
+
const instanceId = clean(opts.instanceId) || clean(process.env.PAGS_INSTANCE_ID);
|
|
46
|
+
if (instanceId) headers["X-PAGS-Instance-Id"] = instanceId;
|
|
47
|
+
return headers;
|
|
48
|
+
}
|
|
49
|
+
function apiPathSegment(value) {
|
|
50
|
+
return encodeURIComponent(value);
|
|
51
|
+
}
|
|
52
|
+
function buildRunnerArgs(opts) {
|
|
53
|
+
const args = [];
|
|
54
|
+
if (clean(opts.host)) args.push("--host", clean(opts.host));
|
|
55
|
+
if (clean(opts.port)) args.push("--port", clean(opts.port));
|
|
56
|
+
if (clean(opts.dataDir)) args.push("--data-dir", clean(opts.dataDir));
|
|
57
|
+
if (clean(opts.token)) args.push("--token", clean(opts.token));
|
|
58
|
+
if (clean(opts.instanceId)) args.push("--instance-id", clean(opts.instanceId));
|
|
59
|
+
if (opts.headless) args.push("--headless");
|
|
60
|
+
return args;
|
|
61
|
+
}
|
|
62
|
+
function buildCloudflaredArgs(localUrl) {
|
|
63
|
+
return ["tunnel", "--url", localUrl];
|
|
64
|
+
}
|
|
65
|
+
function parseCloudflaredTunnelUrl(text) {
|
|
66
|
+
return text.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i)?.[0] || null;
|
|
67
|
+
}
|
|
68
|
+
function buildRuntimeRegistrationBody(opts, capabilities = []) {
|
|
69
|
+
return {
|
|
70
|
+
endpointUrl: clean(opts.endpointUrl) || opts.endpointUrl,
|
|
71
|
+
token: clean(opts.runnerToken) || clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN),
|
|
72
|
+
placement: opts.placement === "managed" ? "managed" : "local",
|
|
73
|
+
capabilities,
|
|
74
|
+
runnerVersion: clean(opts.runnerVersion) || ""
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function clean(value) {
|
|
78
|
+
const trimmed = value?.trim();
|
|
79
|
+
return trimmed || void 0;
|
|
80
|
+
}
|
|
81
|
+
function findWorkspaceRoot() {
|
|
82
|
+
let dir = process.cwd();
|
|
83
|
+
for (let i = 0; i < 8; i++) {
|
|
84
|
+
if (existsSync(resolve(dir, "pnpm-workspace.yaml"))) return dir;
|
|
85
|
+
const parent = resolve(dir, "..");
|
|
86
|
+
if (parent === dir) break;
|
|
87
|
+
dir = parent;
|
|
88
|
+
}
|
|
89
|
+
return process.cwd();
|
|
90
|
+
}
|
|
91
|
+
function bundledRunnerPath() {
|
|
92
|
+
return fileURLToPath(new URL("./browser-runner/index.js", import.meta.url));
|
|
93
|
+
}
|
|
94
|
+
function runnerSpawnSpec(opts) {
|
|
95
|
+
const root = findWorkspaceRoot();
|
|
96
|
+
const localPackage = resolve(root, "packages", "browser-runner", "src", "index.ts");
|
|
97
|
+
const bundledPackage = bundledRunnerPath();
|
|
98
|
+
const runnerArgs = buildRunnerArgs(opts);
|
|
99
|
+
let cwd = root;
|
|
100
|
+
let command = "pags-browser-runner";
|
|
101
|
+
let args = runnerArgs;
|
|
102
|
+
if (existsSync(localPackage)) {
|
|
103
|
+
command = "pnpm";
|
|
104
|
+
args = ["--filter", "@proagentstore/browser-runner", "dev", "--", ...runnerArgs];
|
|
105
|
+
} else if (existsSync(bundledPackage)) {
|
|
106
|
+
cwd = process.cwd();
|
|
107
|
+
command = process.execPath;
|
|
108
|
+
args = [bundledPackage, ...runnerArgs];
|
|
109
|
+
}
|
|
110
|
+
return { command, args, cwd };
|
|
111
|
+
}
|
|
112
|
+
function startRunnerForeground(opts) {
|
|
113
|
+
const spec = runnerSpawnSpec(opts);
|
|
114
|
+
return new Promise((resolvePromise, reject) => {
|
|
115
|
+
const child = spawn(spec.command, spec.args, {
|
|
116
|
+
cwd: spec.cwd,
|
|
117
|
+
stdio: "inherit",
|
|
118
|
+
shell: process.platform === "win32"
|
|
119
|
+
});
|
|
120
|
+
child.on("error", reject);
|
|
121
|
+
child.on("exit", (code) => {
|
|
122
|
+
if (code && code !== 0) reject(new Error(`runner exited with code ${code}`));
|
|
123
|
+
else resolvePromise();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async function waitForLocalRunner(opts, timeoutMs = 15e3) {
|
|
128
|
+
const started = Date.now();
|
|
129
|
+
let lastError;
|
|
130
|
+
while (Date.now() - started < timeoutMs) {
|
|
131
|
+
try {
|
|
132
|
+
await requestRunner("GET", "/health", opts);
|
|
133
|
+
return;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
lastError = error;
|
|
136
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 500));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`runner did not become healthy: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
140
|
+
}
|
|
141
|
+
async function requestRunner(method, path, opts, body) {
|
|
142
|
+
const headers = {
|
|
143
|
+
...runnerRequestHeaders(opts)
|
|
144
|
+
};
|
|
145
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
146
|
+
const res = await fetch(`${runnerBaseUrl(opts.url)}${path}`, {
|
|
147
|
+
method,
|
|
148
|
+
headers,
|
|
149
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
150
|
+
});
|
|
151
|
+
const { text, data } = await readResponse(res);
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
const message = responseErrorMessage(data, text, res.statusText);
|
|
154
|
+
throw new Error(`${res.status} ${message}`);
|
|
155
|
+
}
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
async function requestPags(method, path, opts, body) {
|
|
159
|
+
const headers = {
|
|
160
|
+
...pagsHeaders(opts.pagsToken)
|
|
161
|
+
};
|
|
162
|
+
if (!headers.Authorization) {
|
|
163
|
+
throw new Error("PAGS token required. Set PAGS_TOKEN or pass --pags-token.");
|
|
164
|
+
}
|
|
165
|
+
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
166
|
+
const res = await fetch(`${pagsApiBase(opts.apiBase)}${path}`, {
|
|
167
|
+
method,
|
|
168
|
+
headers,
|
|
169
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
170
|
+
});
|
|
171
|
+
const { text, data } = await readResponse(res);
|
|
172
|
+
if (!res.ok) {
|
|
173
|
+
const message = responseErrorMessage(data, text, res.statusText);
|
|
174
|
+
throw new Error(`${res.status} ${message}`);
|
|
175
|
+
}
|
|
176
|
+
return data;
|
|
177
|
+
}
|
|
178
|
+
async function readResponse(res) {
|
|
179
|
+
const text = await res.text();
|
|
180
|
+
if (!text) return { text, data: {} };
|
|
181
|
+
try {
|
|
182
|
+
return { text, data: JSON.parse(text) };
|
|
183
|
+
} catch {
|
|
184
|
+
return { text, data: {} };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function responseErrorMessage(data, text, statusText) {
|
|
188
|
+
return typeof data.error === "string" ? data.error : text || statusText;
|
|
189
|
+
}
|
|
190
|
+
function collectCapability(value, previous = []) {
|
|
191
|
+
return [...previous, value];
|
|
192
|
+
}
|
|
193
|
+
function createRunnerCommand() {
|
|
194
|
+
const command = new Command("runner").description(
|
|
195
|
+
"Manage the local FAGS browser runtime for ProAgentStore agents"
|
|
196
|
+
);
|
|
197
|
+
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) => {
|
|
198
|
+
await startRunnerForeground(opts);
|
|
199
|
+
});
|
|
200
|
+
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", "cloudflared").option("--runner-version <version>", "Runner version").option("--skip-probe", "Skip PAGS FAGS-runtime probe after registration").action(async (instanceId, opts) => {
|
|
201
|
+
const runnerToken = clean(opts.token) || clean(process.env.PAGS_RUNNER_TOKEN) || `pags_runner_${randomUUID()}`;
|
|
202
|
+
const host = clean(opts.host) || "127.0.0.1";
|
|
203
|
+
const port = clean(opts.port) || "49171";
|
|
204
|
+
const localUrl = `http://${host}:${port}`;
|
|
205
|
+
const runnerOpts = {
|
|
206
|
+
...opts,
|
|
207
|
+
host,
|
|
208
|
+
port,
|
|
209
|
+
token: runnerToken,
|
|
210
|
+
instanceId
|
|
211
|
+
};
|
|
212
|
+
const spec = runnerSpawnSpec(runnerOpts);
|
|
213
|
+
const runner = spawn(spec.command, spec.args, {
|
|
214
|
+
cwd: spec.cwd,
|
|
215
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
216
|
+
shell: process.platform === "win32"
|
|
217
|
+
});
|
|
218
|
+
runner.stdout?.on("data", (data) => process.stdout.write(data));
|
|
219
|
+
runner.stderr?.on("data", (data) => process.stderr.write(data));
|
|
220
|
+
runner.on("exit", (code) => {
|
|
221
|
+
if (code && code !== 0) writeError(`runner exited with code ${code}`);
|
|
222
|
+
});
|
|
223
|
+
let tunnel = null;
|
|
224
|
+
const shutdown = () => {
|
|
225
|
+
if (tunnel && !tunnel.killed) tunnel.kill("SIGTERM");
|
|
226
|
+
if (!runner.killed) runner.kill("SIGTERM");
|
|
227
|
+
};
|
|
228
|
+
process.once("SIGINT", () => {
|
|
229
|
+
shutdown();
|
|
230
|
+
process.exit(0);
|
|
231
|
+
});
|
|
232
|
+
process.once("SIGTERM", () => {
|
|
233
|
+
shutdown();
|
|
234
|
+
process.exit(0);
|
|
235
|
+
});
|
|
236
|
+
try {
|
|
237
|
+
await waitForLocalRunner({ url: localUrl, token: runnerToken, instanceId });
|
|
238
|
+
writeLine(`Local FAGS runtime healthy at ${localUrl}`);
|
|
239
|
+
const tunnelUrl = await new Promise((resolveTunnel, reject) => {
|
|
240
|
+
let output = "";
|
|
241
|
+
const cloudflared = clean(opts.cloudflared) || "cloudflared";
|
|
242
|
+
tunnel = spawn(cloudflared, buildCloudflaredArgs(localUrl), {
|
|
243
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
244
|
+
shell: process.platform === "win32"
|
|
245
|
+
});
|
|
246
|
+
const onData = (data) => {
|
|
247
|
+
const text = data.toString("utf-8");
|
|
248
|
+
output += text;
|
|
249
|
+
process.stderr.write(text);
|
|
250
|
+
const parsed = parseCloudflaredTunnelUrl(output);
|
|
251
|
+
if (parsed) resolveTunnel(parsed);
|
|
252
|
+
};
|
|
253
|
+
tunnel.stdout?.on("data", onData);
|
|
254
|
+
tunnel.stderr?.on("data", onData);
|
|
255
|
+
tunnel.on("error", reject);
|
|
256
|
+
tunnel.on("exit", (code) => {
|
|
257
|
+
if (code && code !== 0) reject(new Error(`cloudflared exited with code ${code}`));
|
|
258
|
+
});
|
|
259
|
+
setTimeout(() => reject(new Error("timed out waiting for cloudflared tunnel URL")), 45e3).unref();
|
|
260
|
+
});
|
|
261
|
+
writeLine(`Tunnel ready: ${tunnelUrl}`);
|
|
262
|
+
await waitForLocalRunner({ url: tunnelUrl, token: runnerToken, instanceId }, 3e4);
|
|
263
|
+
writeLine("Tunnel health check passed");
|
|
264
|
+
const capabilities = await requestRunner("GET", "/capabilities", {
|
|
265
|
+
url: tunnelUrl,
|
|
266
|
+
token: runnerToken,
|
|
267
|
+
instanceId
|
|
268
|
+
});
|
|
269
|
+
const result = await requestPags("POST", `/v1/instances/${apiPathSegment(instanceId)}/runtime`, opts, {
|
|
270
|
+
endpointUrl: tunnelUrl,
|
|
271
|
+
token: runnerToken,
|
|
272
|
+
placement: "local",
|
|
273
|
+
capabilities: Array.isArray(capabilities.capabilities) ? capabilities.capabilities.filter((item) => typeof item === "string") : [],
|
|
274
|
+
runnerVersion: clean(opts.runnerVersion) || ""
|
|
275
|
+
});
|
|
276
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
277
|
+
if (!opts.skipProbe) {
|
|
278
|
+
const status = await requestPags(
|
|
279
|
+
"GET",
|
|
280
|
+
`/v1/instances/${apiPathSegment(instanceId)}/runtime/status`,
|
|
281
|
+
opts
|
|
282
|
+
);
|
|
283
|
+
writeLine(JSON.stringify(status, null, 2));
|
|
284
|
+
}
|
|
285
|
+
writeLine("Connected. Keep this process running while the agent uses your FAGS browser runtime.");
|
|
286
|
+
await new Promise((resolvePromise) => {
|
|
287
|
+
runner.on("exit", () => resolvePromise());
|
|
288
|
+
tunnel?.on("exit", () => resolvePromise());
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
shutdown();
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
command.command("status").description("Check local FAGS runtime health and capabilities").option("--url <url>", "Runner URL").option("--token <token>", "Runner bearer token").option("--instance-id <id>", "PAGS instance id header").action(async (opts) => {
|
|
296
|
+
const health = await requestRunner("GET", "/health", opts);
|
|
297
|
+
const capabilities = await requestRunner("GET", "/capabilities", opts);
|
|
298
|
+
writeLine(JSON.stringify({ health, capabilities }, null, 2));
|
|
299
|
+
});
|
|
300
|
+
command.command("task").description("Create a local FAGS runtime task").requiredOption("--type <type>", "Task type, e.g. echo or browser.open").option("--url <url>", "Runner URL").option("--token <token>", "Runner bearer token").option("--instance-id <id>", "PAGS instance id header").option("--input <json>", "Task input JSON").option("--job-url <url>", "Shortcut input for browser.open").option("--approve", "Create task in needs_approval state").option("--approval-prompt <text>", "Approval prompt").action(
|
|
301
|
+
async (opts) => {
|
|
302
|
+
let input = {};
|
|
303
|
+
if (opts.input) {
|
|
304
|
+
try {
|
|
305
|
+
input = JSON.parse(opts.input);
|
|
306
|
+
} catch {
|
|
307
|
+
writeError("--input must be valid JSON");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (opts.jobUrl) input.url = opts.jobUrl;
|
|
312
|
+
const task = await requestRunner("POST", "/tasks", opts, {
|
|
313
|
+
type: opts.type,
|
|
314
|
+
input,
|
|
315
|
+
requiresApproval: Boolean(opts.approve),
|
|
316
|
+
approvalPrompt: opts.approvalPrompt
|
|
317
|
+
});
|
|
318
|
+
writeLine(JSON.stringify(task, null, 2));
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
command.command("register <instanceId>").description("Register a local or managed FAGS runtime endpoint with a PAGS instance").requiredOption("--endpoint-url <url>", "FAGS runtime endpoint URL to store in PAGS").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").option("--runner-token <token>", "FAGS runtime bearer token to store in PAGS").option("--url <url>", "Local FAGS runtime URL to probe for capabilities").option("--token <token>", "Local FAGS runtime bearer token for capability probe").option("--instance-id <id>", "PAGS instance id header for capability probe").option("--placement <placement>", "Runtime placement: local or managed", "local").option("--runner-version <version>", "Runner version").option("--capability <name>", "Runtime capability; repeatable", collectCapability, []).option("--probe", "Read capabilities from the FAGS runtime before registering").action(async (instanceId, opts) => {
|
|
322
|
+
let capabilities = opts.capability || [];
|
|
323
|
+
if (opts.probe) {
|
|
324
|
+
const data = await requestRunner("GET", "/capabilities", {
|
|
325
|
+
url: opts.url || opts.endpointUrl,
|
|
326
|
+
token: opts.token || opts.runnerToken,
|
|
327
|
+
instanceId: opts.instanceId || instanceId
|
|
328
|
+
});
|
|
329
|
+
capabilities = Array.isArray(data.capabilities) ? data.capabilities.filter((item) => typeof item === "string") : capabilities;
|
|
330
|
+
}
|
|
331
|
+
const body = buildRuntimeRegistrationBody(opts, capabilities);
|
|
332
|
+
const result = await requestPags("POST", `/v1/instances/${apiPathSegment(instanceId)}/runtime`, opts, body);
|
|
333
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
334
|
+
});
|
|
335
|
+
command.command("runtime <instanceId>").description("Read the PAGS runtime registration for an instance").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").option("--probe", "Ask PAGS to probe /health and /capabilities on the FAGS runtime").action(async (instanceId, opts) => {
|
|
336
|
+
const path = opts.probe ? `/v1/instances/${apiPathSegment(instanceId)}/runtime/status` : `/v1/instances/${apiPathSegment(instanceId)}/runtime`;
|
|
337
|
+
const result = await requestPags("GET", path, opts);
|
|
338
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
339
|
+
});
|
|
340
|
+
command.command("unregister <instanceId>").description("Remove the PAGS runtime registration for an instance").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").action(async (instanceId, opts) => {
|
|
341
|
+
const result = await requestPags("DELETE", `/v1/instances/${apiPathSegment(instanceId)}/runtime`, opts);
|
|
342
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
343
|
+
});
|
|
344
|
+
command.command("run <instanceId>").description("Create a task through PAGS on an instance's registered FAGS runtime").requiredOption("--type <type>", "Task type, e.g. echo or browser.open").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").option("--input <json>", "Task input JSON").option("--job-url <url>", "Shortcut input for browser.open").option("--approve", "Create task in needs_approval state").option("--approval-prompt <text>", "Approval prompt").action(
|
|
345
|
+
async (instanceId, opts) => {
|
|
346
|
+
let input = {};
|
|
347
|
+
if (opts.input) {
|
|
348
|
+
try {
|
|
349
|
+
input = JSON.parse(opts.input);
|
|
350
|
+
} catch {
|
|
351
|
+
writeError("--input must be valid JSON");
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (opts.jobUrl) input.url = opts.jobUrl;
|
|
356
|
+
const result = await requestPags("POST", `/v1/instances/${apiPathSegment(instanceId)}/tasks`, opts, {
|
|
357
|
+
type: opts.type,
|
|
358
|
+
input,
|
|
359
|
+
requiresApproval: Boolean(opts.approve),
|
|
360
|
+
approvalPrompt: opts.approvalPrompt
|
|
361
|
+
});
|
|
362
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
command.command("approve-task <instanceId> <taskId>").description("Approve a registered FAGS runtime task through PAGS").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").action(async (instanceId, taskId, opts) => {
|
|
366
|
+
const result = await requestPags(
|
|
367
|
+
"POST",
|
|
368
|
+
`/v1/instances/${apiPathSegment(instanceId)}/tasks/${apiPathSegment(taskId)}/approve`,
|
|
369
|
+
opts
|
|
370
|
+
);
|
|
371
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
372
|
+
});
|
|
373
|
+
command.command("cancel-task <instanceId> <taskId>").description("Cancel a registered FAGS runtime task through PAGS").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").action(async (instanceId, taskId, opts) => {
|
|
374
|
+
const result = await requestPags(
|
|
375
|
+
"POST",
|
|
376
|
+
`/v1/instances/${apiPathSegment(instanceId)}/tasks/${apiPathSegment(taskId)}/cancel`,
|
|
377
|
+
opts
|
|
378
|
+
);
|
|
379
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
380
|
+
});
|
|
381
|
+
command.command("task-events <instanceId>").description("Read registered FAGS runtime task events through PAGS").option("--api-base <url>", "PAGS API base URL").option("--pags-token <token>", "PAGS session token. Defaults to PAGS_TOKEN").option("--limit <n>", "Number of events", "50").action(async (instanceId, opts) => {
|
|
382
|
+
const result = await requestPags(
|
|
383
|
+
"GET",
|
|
384
|
+
`/v1/instances/${apiPathSegment(instanceId)}/task-events?limit=${Number(opts.limit) || 50}`,
|
|
385
|
+
opts
|
|
386
|
+
);
|
|
387
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
388
|
+
});
|
|
389
|
+
command.command("approve <taskId>").description("Approve a task waiting on human approval").option("--url <url>", "Runner URL").option("--token <token>", "Runner bearer token").option("--instance-id <id>", "PAGS instance id header").action(async (taskId, opts) => {
|
|
390
|
+
const task = await requestRunner("POST", `/tasks/${apiPathSegment(taskId)}/approve`, opts);
|
|
391
|
+
writeLine(JSON.stringify(task, null, 2));
|
|
392
|
+
});
|
|
393
|
+
command.command("cancel <taskId>").description("Cancel a local FAGS runtime task").option("--url <url>", "Runner URL").option("--token <token>", "Runner bearer token").option("--instance-id <id>", "PAGS instance id header").action(async (taskId, opts) => {
|
|
394
|
+
const task = await requestRunner("POST", `/tasks/${apiPathSegment(taskId)}/cancel`, opts);
|
|
395
|
+
writeLine(JSON.stringify(task, null, 2));
|
|
396
|
+
});
|
|
397
|
+
command.command("events").description("List recent FAGS runtime events").option("--url <url>", "Runner URL").option("--token <token>", "Runner bearer token").option("--instance-id <id>", "PAGS instance id header").option("--limit <n>", "Number of events", "50").action(async (opts) => {
|
|
398
|
+
const events = await requestRunner("GET", `/events?limit=${Number(opts.limit) || 50}`, opts);
|
|
399
|
+
writeLine(JSON.stringify(events, null, 2));
|
|
400
|
+
});
|
|
401
|
+
return command;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export {
|
|
405
|
+
__require,
|
|
406
|
+
writeLine,
|
|
407
|
+
writeError,
|
|
408
|
+
runnerCommand,
|
|
409
|
+
runnerBaseUrl,
|
|
410
|
+
pagsApiBase,
|
|
411
|
+
pagsHeaders,
|
|
412
|
+
runnerHeaders,
|
|
413
|
+
runnerRequestHeaders,
|
|
414
|
+
apiPathSegment,
|
|
415
|
+
buildRunnerArgs,
|
|
416
|
+
buildCloudflaredArgs,
|
|
417
|
+
parseCloudflaredTunnelUrl,
|
|
418
|
+
buildRuntimeRegistrationBody,
|
|
419
|
+
bundledRunnerPath,
|
|
420
|
+
requestRunner,
|
|
421
|
+
requestPags,
|
|
422
|
+
createRunnerCommand
|
|
423
|
+
};
|