antpath 0.10.14 → 0.11.0
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/LICENSE +201 -0
- package/README.md +16 -8
- package/dist/_shared/blueprint.d.ts +93 -108
- package/dist/_shared/blueprint.js +144 -78
- package/dist/_shared/cleanup-policy.d.ts +2 -2
- package/dist/_shared/cleanup-policy.js +2 -5
- package/dist/_shared/http.d.ts +2 -2
- package/dist/_shared/index.d.ts +7 -1
- package/dist/_shared/index.js +6 -1
- package/dist/_shared/mcp-proxy-url.d.ts +55 -0
- package/dist/_shared/mcp-proxy-url.js +65 -0
- package/dist/_shared/operations.d.ts +55 -8
- package/dist/_shared/operations.js +163 -20
- package/dist/_shared/provider-proxy-url.d.ts +64 -0
- package/dist/_shared/provider-proxy-url.js +73 -0
- package/dist/_shared/proxy-validation.d.ts +1 -1
- package/dist/_shared/proxy-validation.js +2 -2
- package/dist/_shared/run-unit.d.ts +23 -36
- package/dist/_shared/run-unit.js +30 -46
- package/dist/_shared/runner-event.d.ts +120 -0
- package/dist/_shared/runner-event.js +193 -0
- package/dist/_shared/runner-job.d.ts +159 -0
- package/dist/_shared/runner-job.js +54 -0
- package/dist/_shared/runtime-manifest.d.ts +191 -0
- package/dist/_shared/runtime-manifest.js +221 -0
- package/dist/_shared/runtime-types.d.ts +7 -16
- package/dist/_shared/sse.d.ts +74 -0
- package/dist/_shared/sse.js +0 -0
- package/dist/_shared/stable.d.ts +15 -10
- package/dist/_shared/stable.js +15 -10
- package/dist/_shared/submission.d.ts +199 -73
- package/dist/_shared/submission.js +409 -210
- package/dist/_shared/telemetry.d.ts +2 -2
- package/dist/_shared/telemetry.js +2 -2
- package/dist/_shared/template/index.d.ts +0 -1
- package/dist/_shared/template/index.js +0 -1
- package/dist/agents-md.d.ts +25 -67
- package/dist/agents-md.js +35 -121
- package/dist/agents-md.js.map +1 -1
- package/dist/asset-upload.d.ts +34 -0
- package/dist/asset-upload.js +34 -0
- package/dist/asset-upload.js.map +1 -1
- package/dist/blueprint.d.ts +3 -3
- package/dist/bundle.d.ts +2 -2
- package/dist/bundle.js +1 -1
- package/dist/cli.mjs +559 -105
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +53 -22
- package/dist/client.js +196 -130
- package/dist/client.js.map +1 -1
- package/dist/file.d.ts +28 -94
- package/dist/file.js +35 -175
- package/dist/file.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +10 -2
- package/dist/mcp-server.js +17 -2
- package/dist/mcp-server.js.map +1 -1
- package/dist/skill.d.ts +44 -214
- package/dist/skill.js +50 -284
- package/dist/skill.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/cleanup.md +1 -1
- package/docs/credentials.md +2 -2
- package/docs/events.md +8 -8
- package/docs/outputs.md +2 -0
- package/docs/quickstart.md +18 -2
- package/docs/skills.md +1 -3
- package/docs/templates.md +6 -5
- package/package.json +2 -1
- package/dist/_shared/secrets.d.ts +0 -7
- package/dist/_shared/secrets.js +0 -20
- package/dist/_shared/template/mapper.d.ts +0 -11
- package/dist/_shared/template/mapper.js +0 -70
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { iterateSse } from "./sse.js";
|
|
1
2
|
/**
|
|
2
3
|
* The single source of truth for SDK<->BFF transport. The SDK class
|
|
3
4
|
* AND the CLI subcommands both call these functions; neither
|
|
@@ -11,12 +12,6 @@
|
|
|
11
12
|
* `references/development-principles.md` (Agent-first surface design,
|
|
12
13
|
* Concrete rule 3).
|
|
13
14
|
*/
|
|
14
|
-
export async function submitRun(http, request) {
|
|
15
|
-
return http.request("/api/runs", {
|
|
16
|
-
method: "POST",
|
|
17
|
-
body: JSON.stringify(request)
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
15
|
export async function getRun(http, runId) {
|
|
21
16
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
22
17
|
return hasRun(result) ? result.run : result;
|
|
@@ -26,7 +21,7 @@ export async function getRun(http, runId) {
|
|
|
26
21
|
* parsed submission inputs, attempts, indexed events (with
|
|
27
22
|
* pagination cursor for large runs), raw-event Storage manifest,
|
|
28
23
|
* outputs, capture failures, proxy-call audit, pinned skills,
|
|
29
|
-
* provider skills,
|
|
24
|
+
* provider skills, inline skills.
|
|
30
25
|
*
|
|
31
26
|
* Backed by the same `GET /api/runs/:runId` endpoint that
|
|
32
27
|
* `getRun` calls; this variant just narrows the return type to
|
|
@@ -40,6 +35,154 @@ export async function listRunEvents(http, runId) {
|
|
|
40
35
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/events`);
|
|
41
36
|
return result.events;
|
|
42
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Open the `GET /api/runs/:runId/events/stream` SSE endpoint and yield
|
|
40
|
+
* each `event: run_event` frame as a parsed `RunEvent`. The iterator
|
|
41
|
+
* completes when the server emits `event: terminal`, when the caller's
|
|
42
|
+
* AbortSignal fires, or when the underlying stream closes.
|
|
43
|
+
*
|
|
44
|
+
* The caller controls reconnection — this function deliberately does
|
|
45
|
+
* NOT auto-reconnect. The SDK wraps it with a backoff loop and falls
|
|
46
|
+
* back to polling when SSE is unavailable; the CLI tails events with
|
|
47
|
+
* the same reconnect contract.
|
|
48
|
+
*
|
|
49
|
+
* Cursor semantics: the dashboard sends each event frame with `id: <cursor>`
|
|
50
|
+
* which natively maps to `Last-Event-ID` on reconnect, AND embeds the
|
|
51
|
+
* cursor inside the JSON payload as `.cursor` so non-EventSource
|
|
52
|
+
* consumers can read it. We surface both.
|
|
53
|
+
*/
|
|
54
|
+
export async function* streamRunEventsSse(http, runId, options = {}) {
|
|
55
|
+
const headers = {
|
|
56
|
+
accept: "text/event-stream"
|
|
57
|
+
};
|
|
58
|
+
if (options.cursor) {
|
|
59
|
+
// Both the header and the query-string carry the cursor — the
|
|
60
|
+
// header wins server-side, but some intermediaries strip
|
|
61
|
+
// `Last-Event-ID`, so we belt-and-braces it.
|
|
62
|
+
headers["last-event-id"] = options.cursor;
|
|
63
|
+
}
|
|
64
|
+
const query = {};
|
|
65
|
+
if (options.cursor)
|
|
66
|
+
query.cursor = options.cursor;
|
|
67
|
+
let outcome = {
|
|
68
|
+
kind: "disconnected",
|
|
69
|
+
nextCursor: options.cursor
|
|
70
|
+
};
|
|
71
|
+
let download;
|
|
72
|
+
try {
|
|
73
|
+
const init = { method: "GET", headers };
|
|
74
|
+
if (options.signal)
|
|
75
|
+
init.signal = options.signal;
|
|
76
|
+
download = await http.download(`/api/runs/${encodeURIComponent(runId)}/events/stream`, init, query);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
return {
|
|
80
|
+
kind: options.signal?.aborted ? "aborted" : "disconnected",
|
|
81
|
+
nextCursor: options.cursor,
|
|
82
|
+
error: err instanceof Error ? err.message : String(err)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const body = download.response.body;
|
|
86
|
+
if (!body) {
|
|
87
|
+
return {
|
|
88
|
+
kind: "disconnected",
|
|
89
|
+
nextCursor: options.cursor,
|
|
90
|
+
error: "SSE response body was empty"
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
let cursor = options.cursor;
|
|
94
|
+
try {
|
|
95
|
+
for await (const frame of iterateSse(body, options.signal)) {
|
|
96
|
+
options.onFrame?.(frame);
|
|
97
|
+
if (frame.id)
|
|
98
|
+
cursor = frame.id;
|
|
99
|
+
switch (frame.event) {
|
|
100
|
+
case "run_event": {
|
|
101
|
+
const event = parseRunEventFrame(frame.data);
|
|
102
|
+
if (event) {
|
|
103
|
+
// The dashboard duplicates the cursor inside the data
|
|
104
|
+
// payload — prefer it if present, since it survives proxies
|
|
105
|
+
// that strip the `id:` line.
|
|
106
|
+
const payloadCursor = readStringField(event, "cursor");
|
|
107
|
+
if (payloadCursor)
|
|
108
|
+
cursor = payloadCursor;
|
|
109
|
+
yield event;
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "terminal": {
|
|
114
|
+
const status = readStringField(parseJsonObject(frame.data) ?? {}, "status");
|
|
115
|
+
outcome = {
|
|
116
|
+
kind: "terminal",
|
|
117
|
+
nextCursor: cursor,
|
|
118
|
+
...(status ? { terminalStatus: status } : {})
|
|
119
|
+
};
|
|
120
|
+
return outcome;
|
|
121
|
+
}
|
|
122
|
+
case "gone": {
|
|
123
|
+
outcome = {
|
|
124
|
+
kind: "disconnected",
|
|
125
|
+
nextCursor: cursor,
|
|
126
|
+
error: "run no longer exists"
|
|
127
|
+
};
|
|
128
|
+
return outcome;
|
|
129
|
+
}
|
|
130
|
+
case "error": {
|
|
131
|
+
const message = readStringField(parseJsonObject(frame.data) ?? {}, "message") ?? "SSE stream reported an error";
|
|
132
|
+
outcome = {
|
|
133
|
+
kind: "disconnected",
|
|
134
|
+
nextCursor: cursor,
|
|
135
|
+
error: message
|
|
136
|
+
};
|
|
137
|
+
return outcome;
|
|
138
|
+
}
|
|
139
|
+
// `ready` and `ping` are no-ops — they keep proxies alive and
|
|
140
|
+
// surface the initial cursor; consumers see them via `onFrame`.
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
return {
|
|
146
|
+
kind: options.signal?.aborted ? "aborted" : "disconnected",
|
|
147
|
+
nextCursor: cursor,
|
|
148
|
+
error: err instanceof Error ? err.message : String(err)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Stream ended without a terminal frame (server closed unexpectedly).
|
|
152
|
+
return {
|
|
153
|
+
kind: options.signal?.aborted ? "aborted" : "disconnected",
|
|
154
|
+
nextCursor: cursor
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parseRunEventFrame(data) {
|
|
158
|
+
const parsed = parseJsonObject(data);
|
|
159
|
+
if (!parsed)
|
|
160
|
+
return null;
|
|
161
|
+
// Run events always carry id + type; reject malformed frames.
|
|
162
|
+
const id = readStringField(parsed, "id");
|
|
163
|
+
const type = readStringField(parsed, "type");
|
|
164
|
+
if (!id || !type)
|
|
165
|
+
return null;
|
|
166
|
+
return parsed;
|
|
167
|
+
}
|
|
168
|
+
function parseJsonObject(data) {
|
|
169
|
+
if (data.length === 0)
|
|
170
|
+
return null;
|
|
171
|
+
try {
|
|
172
|
+
const value = JSON.parse(data);
|
|
173
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function readStringField(obj, field) {
|
|
183
|
+
const value = obj[field];
|
|
184
|
+
return typeof value === "string" ? value : undefined;
|
|
185
|
+
}
|
|
43
186
|
export async function listOutputs(http, runId) {
|
|
44
187
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/outputs`);
|
|
45
188
|
return result.outputs;
|
|
@@ -73,22 +216,22 @@ export async function downloadRunArchive(http, runId) {
|
|
|
73
216
|
return response;
|
|
74
217
|
}
|
|
75
218
|
// ===========================================================================
|
|
76
|
-
//
|
|
219
|
+
// Run submission operations (Skill / McpServer / Blueprint composition)
|
|
77
220
|
// ===========================================================================
|
|
78
|
-
export async function
|
|
221
|
+
export async function submitRun(http, request) {
|
|
79
222
|
return http.request("/api/runs", {
|
|
80
223
|
method: "POST",
|
|
81
224
|
body: JSON.stringify(request)
|
|
82
225
|
});
|
|
83
226
|
}
|
|
84
227
|
/**
|
|
85
|
-
* Multipart variant of `
|
|
228
|
+
* Multipart variant of `submitRun` for runs that carry transient
|
|
86
229
|
* (per-run) skill bundles and/or transient AgentsMd content.
|
|
87
230
|
*
|
|
88
231
|
* The JSON submission travels as the `submission` part; each
|
|
89
|
-
* `
|
|
232
|
+
* `InlineSkillRef.slot` in `request.submission.skills` MUST be
|
|
90
233
|
* mirrored by exactly one `skill:<slot>` part with the bundle bytes.
|
|
91
|
-
* Each `
|
|
234
|
+
* Each `InlineAgentsMdRef.slot` in `request.submission.agentsMd`
|
|
92
235
|
* MUST be mirrored by exactly one `agentsmd:<slot>` part with the
|
|
93
236
|
* markdown text.
|
|
94
237
|
*
|
|
@@ -97,12 +240,12 @@ export async function submitRunFlat(http, request) {
|
|
|
97
240
|
*
|
|
98
241
|
* At least one of `bundles` or `agentsMdParts` must be non-empty.
|
|
99
242
|
*/
|
|
100
|
-
export async function
|
|
243
|
+
export async function submitRunMultipart(http, request, bundles, agentsMdParts, fileParts) {
|
|
101
244
|
const hasBundles = Array.isArray(bundles) && bundles.length > 0;
|
|
102
245
|
const hasAgentsMd = Array.isArray(agentsMdParts) && agentsMdParts.length > 0;
|
|
103
246
|
const hasFiles = Array.isArray(fileParts) && fileParts.length > 0;
|
|
104
247
|
if (!hasBundles && !hasAgentsMd && !hasFiles) {
|
|
105
|
-
throw new Error("
|
|
248
|
+
throw new Error("submitRunMultipart: bundles, agentsMdParts, or fileParts must be non-empty");
|
|
106
249
|
}
|
|
107
250
|
const form = new FormData();
|
|
108
251
|
// Submission rides as a typed JSON Blob so the BFF reads
|
|
@@ -112,10 +255,10 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
|
|
|
112
255
|
const seen = new Set();
|
|
113
256
|
for (const bundle of bundles) {
|
|
114
257
|
if (typeof bundle.slot !== "string" || !bundle.slot) {
|
|
115
|
-
throw new Error("
|
|
258
|
+
throw new Error("submitRunMultipart: each bundle must have a non-empty slot id");
|
|
116
259
|
}
|
|
117
260
|
if (seen.has(bundle.slot)) {
|
|
118
|
-
throw new Error(`
|
|
261
|
+
throw new Error(`submitRunMultipart: duplicate inline skill slot "${bundle.slot}"`);
|
|
119
262
|
}
|
|
120
263
|
seen.add(bundle.slot);
|
|
121
264
|
const blob = toBlob(bundle.bytes, "application/zip");
|
|
@@ -123,11 +266,11 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
|
|
|
123
266
|
}
|
|
124
267
|
for (const part of agentsMdParts ?? []) {
|
|
125
268
|
if (typeof part.slot !== "string" || !part.slot) {
|
|
126
|
-
throw new Error("
|
|
269
|
+
throw new Error("submitRunMultipart: each agentsMd part must have a non-empty slot id");
|
|
127
270
|
}
|
|
128
271
|
const partKey = `agentsmd:${part.slot}`;
|
|
129
272
|
if (seen.has(partKey)) {
|
|
130
|
-
throw new Error(`
|
|
273
|
+
throw new Error(`submitRunMultipart: duplicate agentsMd slot "${part.slot}"`);
|
|
131
274
|
}
|
|
132
275
|
seen.add(partKey);
|
|
133
276
|
const blob = new Blob([part.content], { type: "text/plain" });
|
|
@@ -135,11 +278,11 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
|
|
|
135
278
|
}
|
|
136
279
|
for (const part of fileParts ?? []) {
|
|
137
280
|
if (typeof part.slot !== "string" || !part.slot) {
|
|
138
|
-
throw new Error("
|
|
281
|
+
throw new Error("submitRunMultipart: each file part must have a non-empty slot id");
|
|
139
282
|
}
|
|
140
283
|
const partKey = `file:${part.slot}`;
|
|
141
284
|
if (seen.has(partKey)) {
|
|
142
|
-
throw new Error(`
|
|
285
|
+
throw new Error(`submitRunMultipart: duplicate file slot "${part.slot}"`);
|
|
143
286
|
}
|
|
144
287
|
seen.add(partKey);
|
|
145
288
|
const blob = toBlob(part.bytes, "application/zip");
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run Antpath provider proxy URL + bearer minting types.
|
|
3
|
+
*
|
|
4
|
+
* The provider proxy is the second of Antpath's two
|
|
5
|
+
* runtime-independent proxies (the other is the MCP proxy in
|
|
6
|
+
* `./mcp-proxy-url.ts`). It sits between the self-hosted runner and
|
|
7
|
+
* the customer's provider API — today that's DeepSeek's
|
|
8
|
+
* Anthropic-compatible endpoint, but the route is namespaced so
|
|
9
|
+
* additional providers slot in without a wire-format change.
|
|
10
|
+
*
|
|
11
|
+
* The runner container sees only `ANTHROPIC_BASE_URL=<proxy-url>` and
|
|
12
|
+
* `ANTHROPIC_AUTH_TOKEN=<scoped-bearer>`; the real provider key + base
|
|
13
|
+
* URL stay in the run Vault bundle. See
|
|
14
|
+
* `references/deepseek-runtime-mcp-plan.md` (Provider proxy +
|
|
15
|
+
* Security requirements).
|
|
16
|
+
*/
|
|
17
|
+
import type { RunProvider } from "./submission.js";
|
|
18
|
+
/**
|
|
19
|
+
* Platform-reserved key under which the per-run provider proxy bearer
|
|
20
|
+
* is folded into the Vault bundle. Distinct from
|
|
21
|
+
* `__antpath_proxy_token` (customer-declared HTTP proxy) and
|
|
22
|
+
* `__antpath_mcp_proxy_token` (MCP proxy) so the three capabilities
|
|
23
|
+
* are revoked independently. The shared `parseInlineSecrets` rejects
|
|
24
|
+
* any inbound submission whose `secrets` carries a key with the
|
|
25
|
+
* `__antpath_` prefix; only the BFF mutates this after the parser.
|
|
26
|
+
*/
|
|
27
|
+
export declare const PROVIDER_PROXY_BEARER_BUNDLE_KEY = "__antpath_provider_proxy_token";
|
|
28
|
+
/**
|
|
29
|
+
* Output of the per-run provider proxy bearer mint. Same primitive
|
|
30
|
+
* (high-entropy random + peppered SHA-256) as the customer-proxy +
|
|
31
|
+
* MCP-proxy mints; persisted in dedicated columns / Vault keys so
|
|
32
|
+
* the three capabilities don't share a fate.
|
|
33
|
+
*/
|
|
34
|
+
export interface MintedProviderProxyBearer {
|
|
35
|
+
readonly plaintext: string;
|
|
36
|
+
readonly hash: string;
|
|
37
|
+
readonly expiresAt: Date;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* DeepSeek's documented Anthropic-compatibility endpoint. Used by the
|
|
41
|
+
* provider proxy as the default upstream when the customer does not
|
|
42
|
+
* supply `secrets.deepseek.baseUrl`. Subject to DeepSeek's own
|
|
43
|
+
* upstream changes — see `references/deepseek-runtime-mcp-plan.md`
|
|
44
|
+
* (Source facts to re-check).
|
|
45
|
+
*/
|
|
46
|
+
export declare const DEEPSEEK_ANTHROPIC_COMPAT_DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
47
|
+
/**
|
|
48
|
+
* Canonical URL path of the per-run provider proxy endpoint. The BFF
|
|
49
|
+
* route lives at `/api/runs/[runId]/provider-proxy/[providerName]`;
|
|
50
|
+
* the runner / runtime receives the absolute URL from
|
|
51
|
+
* {@link buildProviderProxyUrl}.
|
|
52
|
+
*/
|
|
53
|
+
export declare function providerProxyPath(runId: string, provider: RunProvider): string;
|
|
54
|
+
/**
|
|
55
|
+
* Build the absolute per-run, per-provider proxy URL. `baseUrl` is the
|
|
56
|
+
* configured Antpath API plane root (e.g. `https://api.antpath.ai`);
|
|
57
|
+
* the function strips any trailing slash and appends the canonical
|
|
58
|
+
* provider proxy path.
|
|
59
|
+
*/
|
|
60
|
+
export declare function buildProviderProxyUrl(args: {
|
|
61
|
+
readonly baseUrl: string;
|
|
62
|
+
readonly runId: string;
|
|
63
|
+
readonly provider: RunProvider;
|
|
64
|
+
}): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run Antpath provider proxy URL + bearer minting types.
|
|
3
|
+
*
|
|
4
|
+
* The provider proxy is the second of Antpath's two
|
|
5
|
+
* runtime-independent proxies (the other is the MCP proxy in
|
|
6
|
+
* `./mcp-proxy-url.ts`). It sits between the self-hosted runner and
|
|
7
|
+
* the customer's provider API — today that's DeepSeek's
|
|
8
|
+
* Anthropic-compatible endpoint, but the route is namespaced so
|
|
9
|
+
* additional providers slot in without a wire-format change.
|
|
10
|
+
*
|
|
11
|
+
* The runner container sees only `ANTHROPIC_BASE_URL=<proxy-url>` and
|
|
12
|
+
* `ANTHROPIC_AUTH_TOKEN=<scoped-bearer>`; the real provider key + base
|
|
13
|
+
* URL stay in the run Vault bundle. See
|
|
14
|
+
* `references/deepseek-runtime-mcp-plan.md` (Provider proxy +
|
|
15
|
+
* Security requirements).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Platform-reserved key under which the per-run provider proxy bearer
|
|
19
|
+
* is folded into the Vault bundle. Distinct from
|
|
20
|
+
* `__antpath_proxy_token` (customer-declared HTTP proxy) and
|
|
21
|
+
* `__antpath_mcp_proxy_token` (MCP proxy) so the three capabilities
|
|
22
|
+
* are revoked independently. The shared `parseInlineSecrets` rejects
|
|
23
|
+
* any inbound submission whose `secrets` carries a key with the
|
|
24
|
+
* `__antpath_` prefix; only the BFF mutates this after the parser.
|
|
25
|
+
*/
|
|
26
|
+
export const PROVIDER_PROXY_BEARER_BUNDLE_KEY = "__antpath_provider_proxy_token";
|
|
27
|
+
/**
|
|
28
|
+
* DeepSeek's documented Anthropic-compatibility endpoint. Used by the
|
|
29
|
+
* provider proxy as the default upstream when the customer does not
|
|
30
|
+
* supply `secrets.deepseek.baseUrl`. Subject to DeepSeek's own
|
|
31
|
+
* upstream changes — see `references/deepseek-runtime-mcp-plan.md`
|
|
32
|
+
* (Source facts to re-check).
|
|
33
|
+
*/
|
|
34
|
+
export const DEEPSEEK_ANTHROPIC_COMPAT_DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic";
|
|
35
|
+
/**
|
|
36
|
+
* Canonical URL path of the per-run provider proxy endpoint. The BFF
|
|
37
|
+
* route lives at `/api/runs/[runId]/provider-proxy/[providerName]`;
|
|
38
|
+
* the runner / runtime receives the absolute URL from
|
|
39
|
+
* {@link buildProviderProxyUrl}.
|
|
40
|
+
*/
|
|
41
|
+
export function providerProxyPath(runId, provider) {
|
|
42
|
+
if (!runId || typeof runId !== "string") {
|
|
43
|
+
throw new Error("providerProxyPath: runId must be a non-empty string");
|
|
44
|
+
}
|
|
45
|
+
if (provider !== "anthropic" && provider !== "deepseek") {
|
|
46
|
+
throw new Error(`providerProxyPath: unsupported provider ${JSON.stringify(provider)}`);
|
|
47
|
+
}
|
|
48
|
+
return `/api/runs/${encodeURIComponent(runId)}/provider-proxy/${provider}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build the absolute per-run, per-provider proxy URL. `baseUrl` is the
|
|
52
|
+
* configured Antpath API plane root (e.g. `https://api.antpath.ai`);
|
|
53
|
+
* the function strips any trailing slash and appends the canonical
|
|
54
|
+
* provider proxy path.
|
|
55
|
+
*/
|
|
56
|
+
export function buildProviderProxyUrl(args) {
|
|
57
|
+
if (!args.baseUrl || typeof args.baseUrl !== "string") {
|
|
58
|
+
throw new Error("buildProviderProxyUrl: baseUrl must be a non-empty string");
|
|
59
|
+
}
|
|
60
|
+
let parsedBase;
|
|
61
|
+
try {
|
|
62
|
+
parsedBase = new URL(args.baseUrl);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error(`buildProviderProxyUrl: baseUrl is not a valid URL: ${args.baseUrl}`);
|
|
66
|
+
}
|
|
67
|
+
if (parsedBase.protocol !== "https:" && parsedBase.protocol !== "http:") {
|
|
68
|
+
throw new Error(`buildProviderProxyUrl: baseUrl protocol must be http or https (got ${parsedBase.protocol})`);
|
|
69
|
+
}
|
|
70
|
+
const trimmedBase = `${parsedBase.origin}${parsedBase.pathname.replace(/\/+$/, "")}`;
|
|
71
|
+
return `${trimmedBase}${providerProxyPath(args.runId, args.provider)}`;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=provider-proxy-url.js.map
|
|
@@ -14,6 +14,6 @@ export declare function validateProxyAuth(endpoints: readonly PlatformProxyEndpo
|
|
|
14
14
|
* caller is hand-rolling networking.
|
|
15
15
|
*/
|
|
16
16
|
export declare function buildPlatformAllowedHosts(input: {
|
|
17
|
-
readonly
|
|
17
|
+
readonly baseUrl: string;
|
|
18
18
|
readonly extraHosts?: readonly string[];
|
|
19
19
|
}): readonly string[];
|
|
@@ -37,10 +37,10 @@ export function validateProxyAuth(endpoints, auth) {
|
|
|
37
37
|
export function buildPlatformAllowedHosts(input) {
|
|
38
38
|
const result = [];
|
|
39
39
|
try {
|
|
40
|
-
result.push(new URL(input.
|
|
40
|
+
result.push(new URL(input.baseUrl).host);
|
|
41
41
|
}
|
|
42
42
|
catch {
|
|
43
|
-
throw new Error("buildPlatformAllowedHosts:
|
|
43
|
+
throw new Error("buildPlatformAllowedHosts: baseUrl must be an absolute URL");
|
|
44
44
|
}
|
|
45
45
|
for (const host of input.extraHosts ?? []) {
|
|
46
46
|
if (!result.includes(host))
|
|
@@ -19,36 +19,16 @@
|
|
|
19
19
|
* `rawEventPages` (manifest only; bytes downloaded out-of-band so the
|
|
20
20
|
* detail response stays bounded). The archive zip carries the bytes.
|
|
21
21
|
*/
|
|
22
|
-
import type { JsonValue, PlatformCleanupPolicy,
|
|
22
|
+
import type { JsonValue, PlatformCleanupPolicy, PlatformSubmission, PlatformProxyEndpoint } from "./submission.js";
|
|
23
23
|
/**
|
|
24
|
-
* Parsed view of `runs.template_snapshot` jsonb.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* - `kind: "flat"` (current): {kind:"flat", submission, cleanup?}
|
|
28
|
-
* written by `insertRunWithSkillSnapshots` for all new runs.
|
|
29
|
-
* - legacy template-shaped: {template, executionPayloadSecretId,
|
|
30
|
-
* variables?, cleanup?} written by `insertRun` before the flat
|
|
31
|
-
* pivot.
|
|
32
|
-
*
|
|
33
|
-
* The parser tolerates both and returns a discriminated union so
|
|
34
|
-
* consumers branch mechanically. New work targets `kind: "flat"`.
|
|
35
|
-
*
|
|
36
|
-
* Note on legacy redaction: historic template-shaped snapshots have
|
|
37
|
-
* `system` and `messages` replaced with `"[redacted]"` by
|
|
38
|
-
* `redactTemplateForMetadata`. That redaction predates the raw-event /
|
|
39
|
-
* raw-input policy update and we do not retroactively un-redact data we
|
|
40
|
-
* never stored. Flat-shape snapshots are verbatim.
|
|
24
|
+
* Parsed view of `runs.template_snapshot` jsonb. Stored shape is
|
|
25
|
+
* `{kind:"submission", submission, cleanup?}` written by
|
|
26
|
+
* `insertRunWithSkillSnapshots` for all runs.
|
|
41
27
|
*/
|
|
42
|
-
export type RunUnitSubmission = RunUnitFlatSubmission
|
|
28
|
+
export type RunUnitSubmission = RunUnitFlatSubmission;
|
|
43
29
|
export interface RunUnitFlatSubmission {
|
|
44
|
-
readonly kind: "
|
|
45
|
-
readonly submission:
|
|
46
|
-
readonly cleanup?: PlatformCleanupPolicy;
|
|
47
|
-
}
|
|
48
|
-
export interface RunUnitTemplateSubmission {
|
|
49
|
-
readonly kind: "template";
|
|
50
|
-
readonly template: PlatformTemplateSubmission;
|
|
51
|
-
readonly variables?: Record<string, JsonValue>;
|
|
30
|
+
readonly kind: "submission";
|
|
31
|
+
readonly submission: PlatformSubmission;
|
|
52
32
|
readonly cleanup?: PlatformCleanupPolicy;
|
|
53
33
|
}
|
|
54
34
|
export interface RunUnitAttempt {
|
|
@@ -157,7 +137,7 @@ export interface RunUnitProviderSkill {
|
|
|
157
137
|
readonly skillId: string;
|
|
158
138
|
readonly version?: string;
|
|
159
139
|
}
|
|
160
|
-
export interface
|
|
140
|
+
export interface RunUnitInlineSkill {
|
|
161
141
|
readonly id: string;
|
|
162
142
|
readonly slotId: string;
|
|
163
143
|
readonly skillName: string;
|
|
@@ -190,17 +170,24 @@ export interface RunUnit {
|
|
|
190
170
|
readonly proxyCalls: RunUnitProxyCallPage;
|
|
191
171
|
readonly skillSnapshots: readonly RunUnitSkillSnapshot[];
|
|
192
172
|
readonly providerSkills: readonly RunUnitProviderSkill[];
|
|
193
|
-
readonly
|
|
173
|
+
readonly inlineSkills: readonly RunUnitInlineSkill[];
|
|
174
|
+
/**
|
|
175
|
+
* Per-run, per-provider runtime manifest — derived from the validated
|
|
176
|
+
* submission + the chosen provider (`buildRuntimeManifest`). Tells
|
|
177
|
+
* SDK consumers where antpath placed things in-container and what
|
|
178
|
+
* env vars the agent will see. Undefined on responses from BFFs
|
|
179
|
+
* that predate Phase 2 of the runtime-environment rollout.
|
|
180
|
+
*/
|
|
181
|
+
readonly runtimeManifest?: import("./runtime-manifest.js").RuntimeManifest;
|
|
194
182
|
}
|
|
195
183
|
/**
|
|
196
|
-
* Parse a `runs.template_snapshot` jsonb payload into the typed
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* worker-side enrichment.
|
|
184
|
+
* Parse a `runs.template_snapshot` jsonb payload into the typed flat
|
|
185
|
+
* submission. Never throws on minor unknown keys so we can
|
|
186
|
+
* forward-compat with worker-side enrichment.
|
|
200
187
|
*
|
|
201
188
|
* Returns a typed shape even for malformed snapshots — the worst case
|
|
202
|
-
* is `{kind: "
|
|
203
|
-
* — because the dashboard must still render *something* for a
|
|
204
|
-
* historical row rather than 500ing the whole detail page.
|
|
189
|
+
* is `{kind: "submission", submission: {model: "", ...}}` with empty
|
|
190
|
+
* defaults — because the dashboard must still render *something* for a
|
|
191
|
+
* buggy historical row rather than 500ing the whole detail page.
|
|
205
192
|
*/
|
|
206
193
|
export declare function parseRunUnitSubmission(input: unknown): RunUnitSubmission;
|
package/dist/_shared/run-unit.js
CHANGED
|
@@ -24,29 +24,25 @@ import { parseMcpServerRef, parseSkillRef } from "./blueprint.js";
|
|
|
24
24
|
// Submission parser
|
|
25
25
|
// ---------------------------------------------------------------------------
|
|
26
26
|
/**
|
|
27
|
-
* Parse a `runs.template_snapshot` jsonb payload into the typed
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* worker-side enrichment.
|
|
27
|
+
* Parse a `runs.template_snapshot` jsonb payload into the typed flat
|
|
28
|
+
* submission. Never throws on minor unknown keys so we can
|
|
29
|
+
* forward-compat with worker-side enrichment.
|
|
31
30
|
*
|
|
32
31
|
* Returns a typed shape even for malformed snapshots — the worst case
|
|
33
|
-
* is `{kind: "
|
|
34
|
-
* — because the dashboard must still render *something* for a
|
|
35
|
-
* historical row rather than 500ing the whole detail page.
|
|
32
|
+
* is `{kind: "submission", submission: {model: "", ...}}` with empty
|
|
33
|
+
* defaults — because the dashboard must still render *something* for a
|
|
34
|
+
* buggy historical row rather than 500ing the whole detail page.
|
|
36
35
|
*/
|
|
37
36
|
export function parseRunUnitSubmission(input) {
|
|
38
37
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
39
38
|
return fallbackFlat();
|
|
40
39
|
}
|
|
41
40
|
const value = input;
|
|
42
|
-
if (value.kind === "
|
|
41
|
+
if (value.kind === "submission") {
|
|
43
42
|
return parseFlatProjection(value);
|
|
44
43
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
// Snapshot exists but matches neither shape — surface as an empty
|
|
49
|
-
// flat submission so consumers can still render lifecycle bits.
|
|
44
|
+
// Snapshot exists but does not match the flat shape — surface as an
|
|
45
|
+
// empty flat submission so consumers can still render lifecycle bits.
|
|
50
46
|
return fallbackFlat();
|
|
51
47
|
}
|
|
52
48
|
function parseFlatProjection(value) {
|
|
@@ -69,36 +65,14 @@ function parseFlatProjection(value) {
|
|
|
69
65
|
: {})
|
|
70
66
|
};
|
|
71
67
|
return {
|
|
72
|
-
kind: "
|
|
68
|
+
kind: "submission",
|
|
73
69
|
submission,
|
|
74
70
|
...(cleanup ? { cleanup } : {})
|
|
75
71
|
};
|
|
76
72
|
}
|
|
77
|
-
function parseTemplateProjection(value) {
|
|
78
|
-
const templateRaw = value.template;
|
|
79
|
-
const template = {
|
|
80
|
-
name: typeof templateRaw.name === "string" ? templateRaw.name : "",
|
|
81
|
-
model: typeof templateRaw.model === "string" ? templateRaw.model : "",
|
|
82
|
-
templateHash: typeof templateRaw.templateHash === "string" ? templateRaw.templateHash : "",
|
|
83
|
-
messages: toStringArray(templateRaw.messages),
|
|
84
|
-
...(typeof templateRaw.system === "string" ? { system: templateRaw.system } : {}),
|
|
85
|
-
...(isJsonRecord(templateRaw.metadata) ? { metadata: templateRaw.metadata } : {}),
|
|
86
|
-
...(parseEnvironment(templateRaw.environment)
|
|
87
|
-
? { environment: parseEnvironment(templateRaw.environment) }
|
|
88
|
-
: {})
|
|
89
|
-
};
|
|
90
|
-
const variables = isJsonRecord(value.variables) ? value.variables : undefined;
|
|
91
|
-
const cleanup = parseCleanup(value.cleanup);
|
|
92
|
-
return {
|
|
93
|
-
kind: "template",
|
|
94
|
-
template,
|
|
95
|
-
...(variables ? { variables } : {}),
|
|
96
|
-
...(cleanup ? { cleanup } : {})
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
73
|
function fallbackFlat() {
|
|
100
74
|
return {
|
|
101
|
-
kind: "
|
|
75
|
+
kind: "submission",
|
|
102
76
|
submission: {
|
|
103
77
|
model: "",
|
|
104
78
|
prompt: [],
|
|
@@ -140,7 +114,7 @@ function toSkillRefArray(value) {
|
|
|
140
114
|
const out = [];
|
|
141
115
|
for (let i = 0; i < value.length; i++) {
|
|
142
116
|
try {
|
|
143
|
-
out.push(parseSkillRef(value[i], `submission.skills[${i}]
|
|
117
|
+
out.push(parseSkillRef(value[i], `submission.skills[${i}]`));
|
|
144
118
|
}
|
|
145
119
|
catch {
|
|
146
120
|
// Skip malformed entries rather than failing the whole detail
|
|
@@ -195,7 +169,22 @@ function parseEnvironment(value) {
|
|
|
195
169
|
env.packages = pkgs;
|
|
196
170
|
}
|
|
197
171
|
}
|
|
198
|
-
|
|
172
|
+
// Lenient pass-through for envVars stored in already-validated
|
|
173
|
+
// snapshots. The strict parser in submission.ts enforces shape /
|
|
174
|
+
// size / reserved-prefix rules at submission time; here we just
|
|
175
|
+
// accept whatever shape was persisted.
|
|
176
|
+
if (isRecord(value.envVars)) {
|
|
177
|
+
const out = {};
|
|
178
|
+
for (const [k, v] of Object.entries(value.envVars)) {
|
|
179
|
+
if (typeof v === "string") {
|
|
180
|
+
out[k] = v;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (Object.keys(out).length > 0) {
|
|
184
|
+
env.envVars = out;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return env.networking || env.packages || env.envVars
|
|
199
188
|
? env
|
|
200
189
|
: undefined;
|
|
201
190
|
}
|
|
@@ -204,14 +193,9 @@ function parseCleanup(value) {
|
|
|
204
193
|
return undefined;
|
|
205
194
|
}
|
|
206
195
|
const session = value.session;
|
|
207
|
-
const claudeSession = value.claudeSession;
|
|
208
|
-
const out = {};
|
|
209
196
|
if (session === "retain" || session === "delete") {
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
if (claudeSession === "retain" || claudeSession === "delete") {
|
|
213
|
-
out.claudeSession = claudeSession;
|
|
197
|
+
return { session };
|
|
214
198
|
}
|
|
215
|
-
return
|
|
199
|
+
return undefined;
|
|
216
200
|
}
|
|
217
201
|
//# sourceMappingURL=run-unit.js.map
|