antpath 0.2.0 → 0.2.1
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/cli.mjs +367 -0
- package/dist/cli.mjs.sha256 +1 -0
- package/package.json +5 -2
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// dist/cli.js
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
|
|
5
|
+
// ../shared/dist/config.js
|
|
6
|
+
var DEFAULT_CAPS = {
|
|
7
|
+
maxRunDurationMs: 5 * 60 * 1e3,
|
|
8
|
+
maxActiveRunsPerWorkspace: 1,
|
|
9
|
+
maxActiveRunsPerUserOrToken: 1,
|
|
10
|
+
pollingBaseIntervalMs: 5e3,
|
|
11
|
+
pollingMaxIntervalMs: 6e4,
|
|
12
|
+
pollingJitterRatio: 0.2,
|
|
13
|
+
providerCreateTokensPerMinute: 20,
|
|
14
|
+
providerDeleteTokensPerMinute: 30,
|
|
15
|
+
providerPollTokensPerMinute: 60,
|
|
16
|
+
providerRetryBackoffMs: 5e3,
|
|
17
|
+
leaseDurationMs: 6e4,
|
|
18
|
+
leaseRenewalThresholdMs: 2e4,
|
|
19
|
+
maxProviderAttempts: 1,
|
|
20
|
+
cleanupRetryCount: 3,
|
|
21
|
+
cleanupRetryBackoffMs: 1e4,
|
|
22
|
+
outputDownloadSafetyCapBytes: 1024 * 1024 * 1024,
|
|
23
|
+
workspaceStorageCapBytes: 5 * 1024 * 1024 * 1024,
|
|
24
|
+
signedUrlTtlSeconds: 300
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ../shared/dist/proxy-protocol.js
|
|
28
|
+
var PROXY_PROTOCOL_VERSION = "1";
|
|
29
|
+
var PROXY_PROTOCOL_HEADER = "x-antpath-proxy-protocol";
|
|
30
|
+
var PROXY_METHOD_HEADER = "x-antpath-method";
|
|
31
|
+
var PROXY_PATH_HEADER = "x-antpath-path";
|
|
32
|
+
var PROXY_QUERY_HEADER = "x-antpath-query";
|
|
33
|
+
var PROXY_HEADERS_HEADER = "x-antpath-headers";
|
|
34
|
+
var PROXY_RESPONSE_MODE_HEADER = "x-antpath-response-mode";
|
|
35
|
+
var PROXY_RESPONSE_MODES = ["status_only", "headers_only", "full"];
|
|
36
|
+
|
|
37
|
+
// ../shared/dist/status.js
|
|
38
|
+
var TERMINAL_RUN_STATUSES = [
|
|
39
|
+
"succeeded",
|
|
40
|
+
"failed",
|
|
41
|
+
"timed_out",
|
|
42
|
+
"cancelled",
|
|
43
|
+
"cleanup_failed",
|
|
44
|
+
"pending_delete",
|
|
45
|
+
"deleted"
|
|
46
|
+
];
|
|
47
|
+
var terminalRunStatuses = new Set(TERMINAL_RUN_STATUSES);
|
|
48
|
+
|
|
49
|
+
// ../shared/dist/submission.js
|
|
50
|
+
var PROXY_ENDPOINT_DEFAULTS = {
|
|
51
|
+
allowHeaders: [],
|
|
52
|
+
responseMode: "headers_only",
|
|
53
|
+
maxRequestBytes: 64 * 1024,
|
|
54
|
+
maxResponseBytes: 1024 * 1024,
|
|
55
|
+
timeoutMs: 1e4,
|
|
56
|
+
perCallBudget: 60,
|
|
57
|
+
responseByteBudget: 1024 * 1024
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// dist/internal.js
|
|
61
|
+
var ANTPATH_INDEX_PATH = "/antpath/index.json";
|
|
62
|
+
var ANTPATH_RUN_TOKEN_PATH = "/antpath/run-token";
|
|
63
|
+
|
|
64
|
+
// dist/run.js
|
|
65
|
+
var SUCCESS = { code: 0 };
|
|
66
|
+
var USAGE_ERR = { code: 2 };
|
|
67
|
+
var RUNTIME_ERR = { code: 1 };
|
|
68
|
+
async function runCli(io2) {
|
|
69
|
+
const args = io2.argv.slice(2);
|
|
70
|
+
try {
|
|
71
|
+
const exit = await dispatch(io2, args);
|
|
72
|
+
io2.exit(exit.code);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const body = { error: "internal_error", message: err.message ?? "unknown error" };
|
|
75
|
+
io2.stderr(JSON.stringify(body) + "\n");
|
|
76
|
+
io2.exit(RUNTIME_ERR.code);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function dispatch(io2, args) {
|
|
80
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
81
|
+
return await printGlobalHelp(io2);
|
|
82
|
+
}
|
|
83
|
+
const sub = args[0];
|
|
84
|
+
const rest = args.slice(1);
|
|
85
|
+
switch (sub) {
|
|
86
|
+
case "proxy":
|
|
87
|
+
return await runProxy(io2, rest);
|
|
88
|
+
default:
|
|
89
|
+
io2.stderr(`unknown subcommand: ${sub}
|
|
90
|
+
`);
|
|
91
|
+
io2.stderr("run `antpath --help` for usage\n");
|
|
92
|
+
return USAGE_ERR;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function printGlobalHelp(io2) {
|
|
96
|
+
const manifest = await tryReadManifest(io2);
|
|
97
|
+
io2.stdout("antpath \u2014 managed proxy CLI for skill scripts\n\n");
|
|
98
|
+
io2.stdout("Usage:\n");
|
|
99
|
+
io2.stdout(" antpath proxy <endpoint-name> [flags]\n");
|
|
100
|
+
io2.stdout(" antpath --help\n\n");
|
|
101
|
+
if (manifest) {
|
|
102
|
+
if (manifest.endpoints.length === 0) {
|
|
103
|
+
io2.stdout("This run declared no proxy endpoints.\n");
|
|
104
|
+
} else {
|
|
105
|
+
io2.stdout("Declared proxy endpoints for this run:\n");
|
|
106
|
+
for (const ep of manifest.endpoints) {
|
|
107
|
+
io2.stdout(` \u2022 ${ep.name} (${ep.allowMethods.join("/")} ${ep.allowPathPrefixes.join(",")}, mode=${ep.responseMode})
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
io2.stdout(`
|
|
112
|
+
Protocol version: ${manifest.protocolVersion}
|
|
113
|
+
`);
|
|
114
|
+
} else {
|
|
115
|
+
io2.stdout("(no manifest mounted \u2014 run `antpath proxy --help` for details)\n");
|
|
116
|
+
}
|
|
117
|
+
return SUCCESS;
|
|
118
|
+
}
|
|
119
|
+
async function printProxyHelp(io2) {
|
|
120
|
+
io2.stdout("antpath proxy \u2014 call an upstream HTTP endpoint via the managed proxy.\n\n");
|
|
121
|
+
io2.stdout("Usage:\n");
|
|
122
|
+
io2.stdout(" antpath proxy <endpoint-name> [flags]\n\n");
|
|
123
|
+
io2.stdout("Flags:\n");
|
|
124
|
+
io2.stdout(" --method <verb> HTTP method (default: GET)\n");
|
|
125
|
+
io2.stdout(" --path <path> Caller-supplied path; must match policy prefixes\n");
|
|
126
|
+
io2.stdout(` --query <json> JSON object of query parameters (e.g. '{"q":"x"}')
|
|
127
|
+
`);
|
|
128
|
+
io2.stdout(" --header K=V Add a caller header (repeatable)\n");
|
|
129
|
+
io2.stdout(" --data <value> Request body. Use '-' for stdin, '@<file>' for file content\n");
|
|
130
|
+
io2.stdout(" --response-mode <mode> status_only | headers_only | full (may only narrow policy)\n");
|
|
131
|
+
io2.stdout(" --help Show this message\n\n");
|
|
132
|
+
const manifest = await tryReadManifest(io2);
|
|
133
|
+
if (manifest && manifest.endpoints.length > 0) {
|
|
134
|
+
io2.stdout("Declared endpoints:\n");
|
|
135
|
+
for (const ep of manifest.endpoints) {
|
|
136
|
+
io2.stdout(` \u2022 ${ep.name}: ${ep.allowMethods.join(",")} ${ep.allowPathPrefixes.join(",")} (mode=${ep.responseMode}, budget=${ep.perCallBudget}/run)
|
|
137
|
+
`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return SUCCESS;
|
|
141
|
+
}
|
|
142
|
+
function parseProxyFlags(rest) {
|
|
143
|
+
let endpointName = null;
|
|
144
|
+
let method = "GET";
|
|
145
|
+
let path = "/";
|
|
146
|
+
let query = null;
|
|
147
|
+
const headers = /* @__PURE__ */ new Map();
|
|
148
|
+
let dataSpec = null;
|
|
149
|
+
let responseMode = null;
|
|
150
|
+
let showHelp = false;
|
|
151
|
+
for (let i = 0; i < rest.length; i++) {
|
|
152
|
+
const arg = rest[i];
|
|
153
|
+
if (arg === "--help" || arg === "-h") {
|
|
154
|
+
showHelp = true;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (arg === "--method") {
|
|
158
|
+
method = expect(rest, ++i, "--method");
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (arg === "--path") {
|
|
162
|
+
path = expect(rest, ++i, "--path");
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (arg === "--query") {
|
|
166
|
+
query = expect(rest, ++i, "--query");
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (arg === "--header") {
|
|
170
|
+
const kv = expect(rest, ++i, "--header");
|
|
171
|
+
const eq = kv.indexOf("=");
|
|
172
|
+
if (eq <= 0)
|
|
173
|
+
return { ok: false, reason: "--header must be in the form KEY=VALUE" };
|
|
174
|
+
headers.set(kv.slice(0, eq).toLowerCase(), kv.slice(eq + 1));
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (arg === "--data") {
|
|
178
|
+
dataSpec = expect(rest, ++i, "--data");
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (arg === "--response-mode") {
|
|
182
|
+
responseMode = expect(rest, ++i, "--response-mode");
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (arg.startsWith("--")) {
|
|
186
|
+
return { ok: false, reason: `unknown flag: ${arg}` };
|
|
187
|
+
}
|
|
188
|
+
if (endpointName === null) {
|
|
189
|
+
endpointName = arg;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
return { ok: false, reason: `unexpected positional argument: ${arg}` };
|
|
193
|
+
}
|
|
194
|
+
return { ok: true, flags: { endpointName, method, path, query, headers, dataSpec, responseMode, showHelp } };
|
|
195
|
+
}
|
|
196
|
+
function expect(arr, idx, flag) {
|
|
197
|
+
const v = arr[idx];
|
|
198
|
+
if (v === void 0) {
|
|
199
|
+
throw new CliUsageError(`${flag} requires a value`);
|
|
200
|
+
}
|
|
201
|
+
return v;
|
|
202
|
+
}
|
|
203
|
+
var CliUsageError = class extends Error {
|
|
204
|
+
};
|
|
205
|
+
async function runProxy(io2, rest) {
|
|
206
|
+
let parsed;
|
|
207
|
+
try {
|
|
208
|
+
parsed = parseProxyFlags(rest);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
if (err instanceof CliUsageError) {
|
|
211
|
+
io2.stderr(`${err.message}
|
|
212
|
+
`);
|
|
213
|
+
return USAGE_ERR;
|
|
214
|
+
}
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
if (!parsed.ok) {
|
|
218
|
+
io2.stderr(`${parsed.reason}
|
|
219
|
+
`);
|
|
220
|
+
return USAGE_ERR;
|
|
221
|
+
}
|
|
222
|
+
const f = parsed.flags;
|
|
223
|
+
if (f.showHelp) {
|
|
224
|
+
return await printProxyHelp(io2);
|
|
225
|
+
}
|
|
226
|
+
if (!f.endpointName) {
|
|
227
|
+
io2.stderr("missing endpoint-name\n");
|
|
228
|
+
io2.stderr("usage: antpath proxy <endpoint-name> [flags]\n");
|
|
229
|
+
return USAGE_ERR;
|
|
230
|
+
}
|
|
231
|
+
if (f.responseMode && !PROXY_RESPONSE_MODES.includes(f.responseMode)) {
|
|
232
|
+
io2.stderr(`--response-mode must be one of: ${PROXY_RESPONSE_MODES.join(", ")}
|
|
233
|
+
`);
|
|
234
|
+
return USAGE_ERR;
|
|
235
|
+
}
|
|
236
|
+
const manifest = await tryReadManifest(io2);
|
|
237
|
+
if (!manifest) {
|
|
238
|
+
emitError(io2, {
|
|
239
|
+
error: "internal_error",
|
|
240
|
+
message: "manifest not mounted; this CLI must run inside an antpath-managed run"
|
|
241
|
+
});
|
|
242
|
+
return RUNTIME_ERR;
|
|
243
|
+
}
|
|
244
|
+
if (!manifest.proxyBaseUrl) {
|
|
245
|
+
emitError(io2, {
|
|
246
|
+
error: "endpoint_not_found",
|
|
247
|
+
message: "this run has no proxy endpoints declared",
|
|
248
|
+
endpointName: f.endpointName
|
|
249
|
+
});
|
|
250
|
+
return RUNTIME_ERR;
|
|
251
|
+
}
|
|
252
|
+
let token;
|
|
253
|
+
try {
|
|
254
|
+
token = (await io2.readFile(ANTPATH_RUN_TOKEN_PATH)).trim();
|
|
255
|
+
} catch {
|
|
256
|
+
emitError(io2, {
|
|
257
|
+
error: "unauthorized",
|
|
258
|
+
message: "run token file missing; this run has no proxy bearer"
|
|
259
|
+
});
|
|
260
|
+
return RUNTIME_ERR;
|
|
261
|
+
}
|
|
262
|
+
if (!token) {
|
|
263
|
+
emitError(io2, { error: "unauthorized", message: "run token is empty" });
|
|
264
|
+
return RUNTIME_ERR;
|
|
265
|
+
}
|
|
266
|
+
let body;
|
|
267
|
+
if (f.dataSpec !== null) {
|
|
268
|
+
try {
|
|
269
|
+
body = await resolveBody(io2, f.dataSpec);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
io2.stderr(`failed to read request body: ${err.message}
|
|
272
|
+
`);
|
|
273
|
+
return RUNTIME_ERR;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const url = `${manifest.proxyBaseUrl.replace(/\/+$/, "")}/${encodeURIComponent(f.endpointName)}`;
|
|
277
|
+
const requestHeaders = new Headers();
|
|
278
|
+
requestHeaders.set("authorization", `Bearer ${token}`);
|
|
279
|
+
requestHeaders.set(PROXY_PROTOCOL_HEADER, PROXY_PROTOCOL_VERSION);
|
|
280
|
+
requestHeaders.set(PROXY_METHOD_HEADER, f.method.toUpperCase());
|
|
281
|
+
requestHeaders.set(PROXY_PATH_HEADER, f.path);
|
|
282
|
+
if (f.query) {
|
|
283
|
+
requestHeaders.set(PROXY_QUERY_HEADER, f.query);
|
|
284
|
+
}
|
|
285
|
+
if (f.headers.size > 0) {
|
|
286
|
+
requestHeaders.set(PROXY_HEADERS_HEADER, JSON.stringify(Object.fromEntries(f.headers)));
|
|
287
|
+
}
|
|
288
|
+
if (f.responseMode) {
|
|
289
|
+
requestHeaders.set(PROXY_RESPONSE_MODE_HEADER, f.responseMode);
|
|
290
|
+
}
|
|
291
|
+
if (body !== void 0) {
|
|
292
|
+
requestHeaders.set("content-length", String(body.byteLength));
|
|
293
|
+
}
|
|
294
|
+
const init = {
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: requestHeaders,
|
|
297
|
+
redirect: "manual"
|
|
298
|
+
};
|
|
299
|
+
if (body !== void 0) {
|
|
300
|
+
init.body = body;
|
|
301
|
+
}
|
|
302
|
+
let response;
|
|
303
|
+
try {
|
|
304
|
+
response = await io2.fetchImpl(url, init);
|
|
305
|
+
} catch (err) {
|
|
306
|
+
emitError(io2, {
|
|
307
|
+
error: "upstream_error",
|
|
308
|
+
message: `proxy request failed: ${err.message}`,
|
|
309
|
+
endpointName: f.endpointName
|
|
310
|
+
});
|
|
311
|
+
return RUNTIME_ERR;
|
|
312
|
+
}
|
|
313
|
+
const text = await response.text();
|
|
314
|
+
let parsedBody;
|
|
315
|
+
try {
|
|
316
|
+
parsedBody = text ? JSON.parse(text) : {};
|
|
317
|
+
} catch {
|
|
318
|
+
emitError(io2, {
|
|
319
|
+
error: "internal_error",
|
|
320
|
+
message: "proxy returned non-JSON response",
|
|
321
|
+
endpointName: f.endpointName
|
|
322
|
+
});
|
|
323
|
+
return RUNTIME_ERR;
|
|
324
|
+
}
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
io2.stderr(JSON.stringify(parsedBody) + "\n");
|
|
327
|
+
return RUNTIME_ERR;
|
|
328
|
+
}
|
|
329
|
+
io2.stdout(JSON.stringify(parsedBody) + "\n");
|
|
330
|
+
return SUCCESS;
|
|
331
|
+
}
|
|
332
|
+
async function resolveBody(io2, spec) {
|
|
333
|
+
if (spec === "-") {
|
|
334
|
+
const data = await io2.readFile("/dev/stdin");
|
|
335
|
+
return new Uint8Array(Buffer.from(data, "utf8"));
|
|
336
|
+
}
|
|
337
|
+
if (spec.startsWith("@")) {
|
|
338
|
+
const path = spec.slice(1);
|
|
339
|
+
if (!path)
|
|
340
|
+
throw new Error("--data @<file> requires a path");
|
|
341
|
+
const data = await io2.readFile(path);
|
|
342
|
+
return new Uint8Array(Buffer.from(data, "utf8"));
|
|
343
|
+
}
|
|
344
|
+
return new Uint8Array(Buffer.from(spec, "utf8"));
|
|
345
|
+
}
|
|
346
|
+
function emitError(io2, body) {
|
|
347
|
+
io2.stderr(JSON.stringify(body) + "\n");
|
|
348
|
+
}
|
|
349
|
+
async function tryReadManifest(io2) {
|
|
350
|
+
try {
|
|
351
|
+
const raw = await io2.readFile(ANTPATH_INDEX_PATH);
|
|
352
|
+
return JSON.parse(raw);
|
|
353
|
+
} catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// dist/cli.js
|
|
359
|
+
var io = {
|
|
360
|
+
readFile: (path) => readFile(path, "utf8"),
|
|
361
|
+
fetchImpl: fetch,
|
|
362
|
+
stdout: (chunk) => process.stdout.write(chunk),
|
|
363
|
+
stderr: (chunk) => process.stderr.write(chunk),
|
|
364
|
+
exit: (code) => process.exit(code),
|
|
365
|
+
argv: process.argv
|
|
366
|
+
};
|
|
367
|
+
await runCli(io);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e4adb3ee5b4053a78a8ddaf9cd653fbaf6a0ddf98432411308964c8fd40909f8 cli.mjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antpath",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "TypeScript SDK for running autonomous Claude Managed Agents sessions.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"import": "./dist/index.js"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"antpath": "./dist/cli.mjs"
|
|
20
|
+
},
|
|
18
21
|
"files": [
|
|
19
22
|
"dist",
|
|
20
23
|
"README.md",
|
|
@@ -30,7 +33,7 @@
|
|
|
30
33
|
"node": ">=20"
|
|
31
34
|
},
|
|
32
35
|
"scripts": {
|
|
33
|
-
"build": "tsc -p tsconfig.build.json",
|
|
36
|
+
"build": "pnpm --filter @antpath/cli run build && tsc -p tsconfig.build.json && node ./scripts/bundle-cli.mjs",
|
|
34
37
|
"lint": "tsc --noEmit",
|
|
35
38
|
"test": "vitest run test/unit",
|
|
36
39
|
"test:unit": "vitest run test/unit",
|