@evilstar9527/tool-bridge 0.1.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/README.md +288 -0
- package/dist/sdk/admin.js +157 -0
- package/dist/sdk/chunks/client-BWz3o-l-.js +36 -0
- package/dist/sdk/chunks/errors-DJj3RaDk.js +54 -0
- package/dist/sdk/host.js +98 -0
- package/dist/sdk/index.js +6 -0
- package/dist/sdk/tb.js +0 -0
- package/dist/sdk/transport.js +14 -0
- package/dist/sdk/tunnel-agent.js +43 -0
- package/dist/sdk/worker.js +2940 -0
- package/dist/types/sdk/admin/index.d.ts +237 -0
- package/dist/types/sdk/client.d.ts +17 -0
- package/dist/types/sdk/host/index.d.ts +65 -0
- package/dist/types/sdk/index.d.ts +5 -0
- package/dist/types/sdk/transport.d.ts +8 -0
- package/dist/types/sdk/tunnel-agent/index.d.ts +29 -0
- package/dist/types/worker/index.d.ts +19 -0
- package/dist/types/worker/tb/adapters/builtin.d.ts +4 -0
- package/dist/types/worker/tb/adapters/directory.d.ts +6 -0
- package/dist/types/worker/tb/adapters/http.d.ts +2 -0
- package/dist/types/worker/tb/adapters/index.d.ts +2 -0
- package/dist/types/worker/tb/adapters/mcp.d.ts +2 -0
- package/dist/types/worker/tb/adapters/mount.d.ts +2 -0
- package/dist/types/worker/tb/adapters/remote.d.ts +2 -0
- package/dist/types/worker/tb/audit.d.ts +56 -0
- package/dist/types/worker/tb/crawl.d.ts +11 -0
- package/dist/types/worker/tb/device.d.ts +80 -0
- package/dist/types/worker/tb/dynamic-servers.d.ts +12 -0
- package/dist/types/worker/tb/entities.d.ts +74 -0
- package/dist/types/worker/tb/errors.d.ts +34 -0
- package/dist/types/worker/tb/help.d.ts +2 -0
- package/dist/types/worker/tb/host-api.d.ts +12 -0
- package/dist/types/worker/tb/materialize.d.ts +4 -0
- package/dist/types/worker/tb/mcp-client.d.ts +17 -0
- package/dist/types/worker/tb/provider-api.d.ts +11 -0
- package/dist/types/worker/tb/registry.d.ts +11 -0
- package/dist/types/worker/tb/remote-client.d.ts +3 -0
- package/dist/types/worker/tb/resolve.d.ts +8 -0
- package/dist/types/worker/tb/storage-r2.d.ts +2 -0
- package/dist/types/worker/tb/tenant.d.ts +28 -0
- package/dist/types/worker/tb/testing/fake-kv.d.ts +20 -0
- package/dist/types/worker/tb/types.d.ts +155 -0
- package/dist/types/worker/tb/util.d.ts +18 -0
- package/dist/types/worker/tb/virtualize.d.ts +7 -0
- package/docs/sdk.md +149 -0
- package/package.json +79 -0
|
@@ -0,0 +1,2940 @@
|
|
|
1
|
+
import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./chunks/errors-DJj3RaDk.js";
|
|
2
|
+
import { createRemoteJWKSet as c, jwtVerify as l } from "jose";
|
|
3
|
+
//#region src/worker/tb/util.ts
|
|
4
|
+
var u = "2025-11-25", d = "tool-bridge", f = "draft", p = 1e6, m = 5e3;
|
|
5
|
+
function h(e, t = {}) {
|
|
6
|
+
let n = new Headers(t.headers);
|
|
7
|
+
return n.set("Content-Type", "application/json; charset=utf-8"), new Response(JSON.stringify(e, null, 2), {
|
|
8
|
+
...t,
|
|
9
|
+
headers: n
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function g(e, t = {}) {
|
|
13
|
+
let n = new Headers(t.headers);
|
|
14
|
+
return n.set("Content-Type", n.get("Content-Type") ?? "text/plain; charset=utf-8"), new Response(e, {
|
|
15
|
+
...t,
|
|
16
|
+
headers: n
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function _(e) {
|
|
20
|
+
return !!e && typeof e == "object" && !Array.isArray(e);
|
|
21
|
+
}
|
|
22
|
+
function v(e, t) {
|
|
23
|
+
let n = e[t];
|
|
24
|
+
return typeof n == "string" && n.length > 0 ? n : void 0;
|
|
25
|
+
}
|
|
26
|
+
function y(e) {
|
|
27
|
+
if (!_(e)) return;
|
|
28
|
+
let t = {};
|
|
29
|
+
for (let [n, r] of Object.entries(e)) typeof r == "string" && (t[n] = r);
|
|
30
|
+
return t;
|
|
31
|
+
}
|
|
32
|
+
function b(e) {
|
|
33
|
+
if (Array.isArray(e)) return e.filter((e) => typeof e == "string");
|
|
34
|
+
}
|
|
35
|
+
function x(e) {
|
|
36
|
+
return e.replace(/\s+/g, " ").trim();
|
|
37
|
+
}
|
|
38
|
+
function S(e) {
|
|
39
|
+
return e instanceof Error ? e.message : String(e);
|
|
40
|
+
}
|
|
41
|
+
async function C(e, t) {
|
|
42
|
+
if (!e.body) return "";
|
|
43
|
+
let n = e.body.getReader(), r = new TextDecoder(), i = 0, a = "";
|
|
44
|
+
for (;;) {
|
|
45
|
+
let { done: e, value: o } = await n.read();
|
|
46
|
+
if (e) return a + r.decode();
|
|
47
|
+
if (i += o.byteLength, i > t) throw Error("Response body exceeded the maximum size.");
|
|
48
|
+
a += r.decode(o, { stream: !0 });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function ee(e) {
|
|
52
|
+
try {
|
|
53
|
+
return await C(e, 8e3);
|
|
54
|
+
} catch {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/worker/tb/registry.ts
|
|
60
|
+
var w = "/htbp";
|
|
61
|
+
function te(e) {
|
|
62
|
+
return ne(e.MCP_SERVERS_JSON || "{}");
|
|
63
|
+
}
|
|
64
|
+
function ne(e) {
|
|
65
|
+
let t = JSON.parse(e || "{}");
|
|
66
|
+
if (_(t) && (t.type === "directory" || Array.isArray(t.children))) {
|
|
67
|
+
let e = ie({
|
|
68
|
+
id: "root",
|
|
69
|
+
...t
|
|
70
|
+
}, "root");
|
|
71
|
+
if (e.kind !== "directory") throw Error("Root tree node must be a directory.");
|
|
72
|
+
return T(e), e;
|
|
73
|
+
}
|
|
74
|
+
let n = {
|
|
75
|
+
kind: "directory",
|
|
76
|
+
id: "root",
|
|
77
|
+
title: "Tool Bridge",
|
|
78
|
+
children: re(t)
|
|
79
|
+
};
|
|
80
|
+
return T(n), n;
|
|
81
|
+
}
|
|
82
|
+
function re(e) {
|
|
83
|
+
let t = [];
|
|
84
|
+
if (Array.isArray(e)) {
|
|
85
|
+
for (let n of e) t.push(oe(n));
|
|
86
|
+
return t;
|
|
87
|
+
}
|
|
88
|
+
if (_(e)) for (let [n, r] of Object.entries(e)) t.push(oe({
|
|
89
|
+
id: n,
|
|
90
|
+
..._(r) ? r : {}
|
|
91
|
+
}));
|
|
92
|
+
return t;
|
|
93
|
+
}
|
|
94
|
+
function ie(e, t) {
|
|
95
|
+
if (!_(e)) throw Error(`Tree node at '${t}' must be an object.`);
|
|
96
|
+
let n = v(e, "type") || (e.children ? "directory" : "mcp");
|
|
97
|
+
switch (n) {
|
|
98
|
+
case "directory": return ae(e, t);
|
|
99
|
+
case "mcp": return oe(e);
|
|
100
|
+
case "http": return ce(e, t);
|
|
101
|
+
case "remote": return ue(e, t);
|
|
102
|
+
case "mount": return de(e, t);
|
|
103
|
+
case "builtin": return fe(e, t);
|
|
104
|
+
default: throw Error(`Unknown tree node type '${n}' at '${t}'.`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function ae(e, t) {
|
|
108
|
+
let n = v(e, "id") || v(e, "name");
|
|
109
|
+
if (!n) throw Error(`Directory node at '${t}' is missing id/name.`);
|
|
110
|
+
let r = (Array.isArray(e.children) ? e.children : []).map((e, r) => ie(e, `${t}/${n}[${r}]`));
|
|
111
|
+
return he(r, `${t}/${n}`), {
|
|
112
|
+
kind: "directory",
|
|
113
|
+
id: n,
|
|
114
|
+
title: v(e, "title") || v(e, "name") || n,
|
|
115
|
+
summary: v(e, "summary") || v(e, "description"),
|
|
116
|
+
children: r
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function oe(e) {
|
|
120
|
+
if (!_(e)) throw Error("MCP server config must be an object.");
|
|
121
|
+
let t = v(e, "id") || v(e, "name"), n = v(e, "endpoint") || v(e, "url") || v(e, "baseUrl");
|
|
122
|
+
if (!t) throw Error("MCP server config is missing id/name.");
|
|
123
|
+
if (!n) throw Error(`MCP server '${t}' is missing endpoint.`);
|
|
124
|
+
return {
|
|
125
|
+
kind: "mcp",
|
|
126
|
+
id: t,
|
|
127
|
+
title: v(e, "name") || t,
|
|
128
|
+
endpoint: n,
|
|
129
|
+
description: v(e, "description"),
|
|
130
|
+
headers: y(e.headers),
|
|
131
|
+
allowedTools: b(e.allowedTools) ?? b(e.allowed_tools),
|
|
132
|
+
namespace: v(e, "namespace"),
|
|
133
|
+
toolOverrides: se(e.toolOverrides ?? e.tool_overrides)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function se(e) {
|
|
137
|
+
if (!_(e)) return;
|
|
138
|
+
let t = {};
|
|
139
|
+
for (let [n, r] of Object.entries(e)) _(r) && (t[n] = {
|
|
140
|
+
hide: r.hide === !0,
|
|
141
|
+
rename: v(r, "rename"),
|
|
142
|
+
description: v(r, "description"),
|
|
143
|
+
effect: me(r.effect),
|
|
144
|
+
scope: v(r, "scope"),
|
|
145
|
+
confirm: r.confirm === !0 ? !0 : void 0
|
|
146
|
+
});
|
|
147
|
+
return Object.keys(t).length > 0 ? t : void 0;
|
|
148
|
+
}
|
|
149
|
+
function ce(e, t) {
|
|
150
|
+
let n = v(e, "id") || v(e, "name");
|
|
151
|
+
if (!n) throw Error(`HTTP node at '${t}' is missing id/name.`);
|
|
152
|
+
let r = (Array.isArray(e.endpoints) ? e.endpoints : []).map((e, r) => le(e, `${t}/${n}[${r}]`));
|
|
153
|
+
if (r.length === 0) throw Error(`HTTP node '${n}' must declare at least one endpoint.`);
|
|
154
|
+
return he(r.map((e) => ({ id: e.name })), `${t}/${n}`), {
|
|
155
|
+
kind: "http",
|
|
156
|
+
id: n,
|
|
157
|
+
title: v(e, "title") || v(e, "name") || n,
|
|
158
|
+
summary: v(e, "summary") || v(e, "description"),
|
|
159
|
+
endpoints: r
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function le(e, t) {
|
|
163
|
+
if (!_(e)) throw Error(`HTTP endpoint at '${t}' must be an object.`);
|
|
164
|
+
let n = v(e, "name"), r = v(e, "method"), i = v(e, "url");
|
|
165
|
+
if (!n) throw Error(`HTTP endpoint at '${t}' is missing name.`);
|
|
166
|
+
if (!r) throw Error(`HTTP endpoint '${n}' is missing method.`);
|
|
167
|
+
if (!i) throw Error(`HTTP endpoint '${n}' is missing url.`);
|
|
168
|
+
return {
|
|
169
|
+
name: n,
|
|
170
|
+
method: r.toUpperCase(),
|
|
171
|
+
url: i,
|
|
172
|
+
description: v(e, "description"),
|
|
173
|
+
inputSchema: e.inputSchema,
|
|
174
|
+
outputSchema: e.outputSchema,
|
|
175
|
+
example: e.example,
|
|
176
|
+
headers: y(e.headers),
|
|
177
|
+
effect: me(e.effect),
|
|
178
|
+
scope: v(e, "scope"),
|
|
179
|
+
confirm: e.confirm === !0 ? !0 : void 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function ue(e, t) {
|
|
183
|
+
let n = v(e, "id") || v(e, "name");
|
|
184
|
+
if (!n) throw Error(`Remote node at '${t}' is missing id/name.`);
|
|
185
|
+
let r = v(e, "helpUrl") || v(e, "help_url") || v(e, "url");
|
|
186
|
+
if (!r) throw Error(`Remote node '${n}' is missing helpUrl.`);
|
|
187
|
+
return {
|
|
188
|
+
kind: "remote",
|
|
189
|
+
id: n,
|
|
190
|
+
title: v(e, "title") || v(e, "name") || n,
|
|
191
|
+
summary: v(e, "summary") || v(e, "description"),
|
|
192
|
+
helpUrl: r,
|
|
193
|
+
headers: y(e.headers)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function de(e, t) {
|
|
197
|
+
let n = v(e, "id") || v(e, "name");
|
|
198
|
+
if (!n) throw Error(`Mount node at '${t}' is missing id/name.`);
|
|
199
|
+
let r = v(e, "bucket") || v(e, "binding");
|
|
200
|
+
if (!r) throw Error(`Mount node '${n}' is missing bucket binding name.`);
|
|
201
|
+
return {
|
|
202
|
+
kind: "mount",
|
|
203
|
+
id: n,
|
|
204
|
+
title: v(e, "title") || v(e, "name") || n,
|
|
205
|
+
summary: v(e, "summary") || v(e, "description"),
|
|
206
|
+
bucket: r,
|
|
207
|
+
prefix: v(e, "prefix"),
|
|
208
|
+
description: v(e, "description")
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function fe(e, t) {
|
|
212
|
+
let n = v(e, "id") || v(e, "name");
|
|
213
|
+
if (!n) throw Error(`Builtin node at '${t}' is missing id/name.`);
|
|
214
|
+
let r = _(e.builtin) ? e.builtin : e, i = (Array.isArray(r.tools) ? r.tools : []).map((e, r) => pe(e, `${t}/${n}[${r}]`));
|
|
215
|
+
if (i.length === 0) throw Error(`Builtin node '${n}' must declare at least one tool.`);
|
|
216
|
+
return he(i.map((e) => ({ id: e.name })), `${t}/${n}`), {
|
|
217
|
+
kind: "builtin",
|
|
218
|
+
id: n,
|
|
219
|
+
title: v(e, "title") || v(e, "name") || n,
|
|
220
|
+
summary: v(e, "summary") || v(e, "description"),
|
|
221
|
+
description: v(e, "description"),
|
|
222
|
+
tools: i
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function pe(e, t) {
|
|
226
|
+
if (!_(e)) throw Error(`Builtin tool at '${t}' must be an object.`);
|
|
227
|
+
let n = v(e, "name"), r = v(e, "handler");
|
|
228
|
+
if (!n) throw Error(`Builtin tool at '${t}' is missing name.`);
|
|
229
|
+
if (!r) throw Error(`Builtin tool '${n}' is missing handler.`);
|
|
230
|
+
return {
|
|
231
|
+
name: n,
|
|
232
|
+
handler: r,
|
|
233
|
+
description: v(e, "description"),
|
|
234
|
+
inputSchema: e.inputSchema,
|
|
235
|
+
outputSchema: e.outputSchema,
|
|
236
|
+
effect: me(e.effect),
|
|
237
|
+
scope: v(e, "scope"),
|
|
238
|
+
confirm: e.confirm === !0 ? !0 : void 0
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function me(e) {
|
|
242
|
+
if (e === "read" || e === "write" || e === "destructive" || e === "external") return e;
|
|
243
|
+
}
|
|
244
|
+
function he(e, t) {
|
|
245
|
+
let n = /* @__PURE__ */ new Set();
|
|
246
|
+
for (let r of e) {
|
|
247
|
+
if (n.has(r.id)) throw Error(`Duplicate sibling id '${r.id}' under '${t}'.`);
|
|
248
|
+
n.add(r.id);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function ge(e, t) {
|
|
252
|
+
return ie(e, t);
|
|
253
|
+
}
|
|
254
|
+
function T(e) {
|
|
255
|
+
if (e.kind === "directory") for (let t of e.children) Object.defineProperty(t, "parent", {
|
|
256
|
+
value: e,
|
|
257
|
+
enumerable: !1,
|
|
258
|
+
writable: !0,
|
|
259
|
+
configurable: !0
|
|
260
|
+
}), T(t);
|
|
261
|
+
}
|
|
262
|
+
function _e(e) {
|
|
263
|
+
let t = [], n = e;
|
|
264
|
+
for (; n && n.parent;) t.unshift(n.id), n = n.parent;
|
|
265
|
+
return t.length === 0 ? w : `${w}/${t.map(encodeURIComponent).join("/")}`;
|
|
266
|
+
}
|
|
267
|
+
function E(e, t) {
|
|
268
|
+
let n = e;
|
|
269
|
+
for (let e = 0; e < t.length; e += 1) {
|
|
270
|
+
let r = t[e];
|
|
271
|
+
if (n.kind !== "directory") return {
|
|
272
|
+
node: n,
|
|
273
|
+
sub: t.slice(e)
|
|
274
|
+
};
|
|
275
|
+
let i = n.children.find((e) => e.id === r);
|
|
276
|
+
if (!i) return;
|
|
277
|
+
n = i;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
node: n,
|
|
281
|
+
sub: []
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
//#endregion
|
|
285
|
+
//#region src/worker/tb/audit.ts
|
|
286
|
+
var ve = 168 * 3600, ye = 0x9184e72a000;
|
|
287
|
+
function be(e) {
|
|
288
|
+
return !!e.TENANTS && e.AUDIT_MODE !== "off";
|
|
289
|
+
}
|
|
290
|
+
function xe(e) {
|
|
291
|
+
let t = e.headers.get("X-TB-Trace-Id");
|
|
292
|
+
return t && /^[A-Za-z0-9._-]{1,128}$/.test(t) ? t : crypto.randomUUID();
|
|
293
|
+
}
|
|
294
|
+
function Se(e) {
|
|
295
|
+
if (e === void 0) return;
|
|
296
|
+
let t = 0;
|
|
297
|
+
try {
|
|
298
|
+
t = JSON.stringify(e)?.length ?? 0;
|
|
299
|
+
} catch {
|
|
300
|
+
t = 0;
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
bytes: t,
|
|
304
|
+
keys: _(e) ? Object.keys(e).slice(0, 32) : void 0
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function Ce(e) {
|
|
308
|
+
return e ?? "_global";
|
|
309
|
+
}
|
|
310
|
+
function we(e, t) {
|
|
311
|
+
let n = E(e, t);
|
|
312
|
+
if (!n) return {};
|
|
313
|
+
let r = n.node, i = {
|
|
314
|
+
tool: n.sub.length > 0 ? n.sub.join("/") : void 0,
|
|
315
|
+
provider: r.tbProviderId
|
|
316
|
+
}, a = n.sub[0];
|
|
317
|
+
if (a) {
|
|
318
|
+
if (r.kind === "http") {
|
|
319
|
+
let e = r.endpoints.find((e) => e.name === a);
|
|
320
|
+
i.effect = e?.effect, i.scope = e?.scope;
|
|
321
|
+
} else if (r.kind === "builtin") {
|
|
322
|
+
let e = r.tools.find((e) => e.name === a);
|
|
323
|
+
i.effect = e?.effect, i.scope = e?.scope;
|
|
324
|
+
} else if (r.kind === "mcp") {
|
|
325
|
+
let e = r.toolOverrides?.[a];
|
|
326
|
+
i.effect = e?.effect, i.scope = e?.scope;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return i;
|
|
330
|
+
}
|
|
331
|
+
async function Te(e) {
|
|
332
|
+
try {
|
|
333
|
+
let t = await e.clone().json();
|
|
334
|
+
return typeof t?.error?.code == "string" ? t.error.code : void 0;
|
|
335
|
+
} catch {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function Ee(e, t) {
|
|
340
|
+
if (!be(e)) return;
|
|
341
|
+
let n = e.TENANTS, r = String(ye - Date.parse(t.ts)).padStart(14, "0"), i = crypto.randomUUID().slice(0, 8), a = Number(e.AUDIT_TTL_SECONDS), o = Number.isFinite(a) && a >= 60 ? a : ve;
|
|
342
|
+
await n.put(`audit:${Ce(t.tenantId)}:${r}:${i}`, JSON.stringify(t), { expirationTtl: o });
|
|
343
|
+
}
|
|
344
|
+
function De(e, t, n) {
|
|
345
|
+
let r = Ee(e, n).catch(() => {});
|
|
346
|
+
return t?.waitUntil ? (t.waitUntil(r), Promise.resolve()) : r;
|
|
347
|
+
}
|
|
348
|
+
async function Oe(e, t, n) {
|
|
349
|
+
let r = new URL(e.url);
|
|
350
|
+
if (r.pathname !== "/api/audit/events") return;
|
|
351
|
+
if (e.method !== "GET") return o(405, "method_not_allowed", "Use GET /api/audit/events.");
|
|
352
|
+
if (!be(t)) return o(501, "not_supported", "Audit requires the TENANTS KV binding.");
|
|
353
|
+
if (!n.isAdmin && !n.tenantId) return o(403, "Forbidden", "Audit query requires an admin or tenant-bound key.");
|
|
354
|
+
let i = Math.min(Math.max(Number(r.searchParams.get("limit")) || 50, 1), 200), a = r.searchParams.get("tenant") ?? void 0, s = n.isAdmin ? a : Ce(n.tenantId), c = s ? `audit:${s}:` : "audit:", l = t.TENANTS, u = [], d;
|
|
355
|
+
for (; u.length < i;) {
|
|
356
|
+
let e = await l.list({
|
|
357
|
+
prefix: c,
|
|
358
|
+
cursor: d,
|
|
359
|
+
limit: Math.min(i - u.length, 100)
|
|
360
|
+
});
|
|
361
|
+
for (let t of e.keys) {
|
|
362
|
+
let e = await l.get(t.name, "json");
|
|
363
|
+
if (e && u.push(e), u.length >= i) break;
|
|
364
|
+
}
|
|
365
|
+
if (e.list_complete) break;
|
|
366
|
+
d = e.cursor;
|
|
367
|
+
}
|
|
368
|
+
return h({
|
|
369
|
+
scope: s ?? "all",
|
|
370
|
+
events: u
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/worker/tb/adapters/builtin.ts
|
|
375
|
+
var ke = {
|
|
376
|
+
kind: "builtin",
|
|
377
|
+
async describe(e, t, n) {
|
|
378
|
+
if (n.length === 0) return {
|
|
379
|
+
htbp: "draft",
|
|
380
|
+
kind: "builtin",
|
|
381
|
+
title: e.title,
|
|
382
|
+
description: e.description,
|
|
383
|
+
cachable: !0,
|
|
384
|
+
resources: e.tools.map(Ae)
|
|
385
|
+
};
|
|
386
|
+
let r = je(e, n[0]);
|
|
387
|
+
return {
|
|
388
|
+
htbp: "draft",
|
|
389
|
+
kind: "builtin",
|
|
390
|
+
title: r.name,
|
|
391
|
+
description: r.description,
|
|
392
|
+
cachable: !0,
|
|
393
|
+
endpoint: {
|
|
394
|
+
method: "POST",
|
|
395
|
+
inputSchema: r.inputSchema ?? { type: "object" },
|
|
396
|
+
outputSchema: r.outputSchema,
|
|
397
|
+
example: {},
|
|
398
|
+
effect: r.effect,
|
|
399
|
+
scope: r.scope,
|
|
400
|
+
confirm: r.confirm
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
async call(e, t, n, r) {
|
|
405
|
+
if (n.length === 0) throw Error(`Builtin node '${e.id}' requires a tool: call POST {path}/{tool}.`);
|
|
406
|
+
let i = je(e, n[0]), a = (t.builtinHandlers ?? {})[i.handler];
|
|
407
|
+
if (!a) throw Error(`Builtin tool '${i.name}' references handler '${i.handler}', which the host did not register.`);
|
|
408
|
+
return a(Me(r), t);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
function Ae(e) {
|
|
412
|
+
return {
|
|
413
|
+
name: e.name,
|
|
414
|
+
path: `./${encodeURIComponent(e.name)}`,
|
|
415
|
+
description: e.description ? x(e.description) : void 0
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function je(e, t) {
|
|
419
|
+
let r = e.tools.find((e) => e.name === t);
|
|
420
|
+
if (!r) throw new n(`Tool '${t}' is not exposed by builtin node '${e.id}'.`);
|
|
421
|
+
return r;
|
|
422
|
+
}
|
|
423
|
+
function Me(e) {
|
|
424
|
+
return e && typeof e == "object" && !Array.isArray(e) && "arguments" in e ? e.arguments ?? {} : e ?? {};
|
|
425
|
+
}
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/worker/tb/materialize.ts
|
|
428
|
+
function Ne(e, t) {
|
|
429
|
+
let n = {};
|
|
430
|
+
for (let [r, i] of Object.entries(t ?? {})) n[r] = Pe(e, i);
|
|
431
|
+
return n;
|
|
432
|
+
}
|
|
433
|
+
function Pe(e, t) {
|
|
434
|
+
return t.startsWith("$env:") ? Fe(e, t.slice(5)) : t.replace(/\$\{([A-Z0-9_]+)\}/g, (t, n) => Fe(e, n));
|
|
435
|
+
}
|
|
436
|
+
function Fe(e, t) {
|
|
437
|
+
let n = e[t];
|
|
438
|
+
if (typeof n != "string" || n.length === 0) throw Error(`Environment variable '${t}' is required for header substitution.`);
|
|
439
|
+
return n;
|
|
440
|
+
}
|
|
441
|
+
function Ie(e, t, n) {
|
|
442
|
+
let r = new URL(t);
|
|
443
|
+
if (r.protocol !== "https:" && e.ALLOW_INSECURE_MCP_HTTP !== "true") throw Error(`${n} must use https://.`);
|
|
444
|
+
return r.toString();
|
|
445
|
+
}
|
|
446
|
+
function Le(e, t) {
|
|
447
|
+
let n = (e.HTBP_REMOTE_ALLOWLIST || "").split(",").map((e) => e.trim().toLowerCase()).filter((e) => e.length > 0);
|
|
448
|
+
if (n.length === 0) return;
|
|
449
|
+
let r = new URL(t).hostname.toLowerCase();
|
|
450
|
+
if (!n.some((e) => r === e || r.endsWith(`.${e}`))) throw Error(`Remote host '${r}' is not in HTBP_REMOTE_ALLOWLIST.`);
|
|
451
|
+
}
|
|
452
|
+
//#endregion
|
|
453
|
+
//#region src/worker/tb/remote-client.ts
|
|
454
|
+
var Re = [
|
|
455
|
+
"directory",
|
|
456
|
+
"mcp",
|
|
457
|
+
"http",
|
|
458
|
+
"remote"
|
|
459
|
+
];
|
|
460
|
+
async function ze(t, n, r) {
|
|
461
|
+
let a = Ie(t, n, `Remote help URL '${n}'`);
|
|
462
|
+
Le(t, a);
|
|
463
|
+
let o = new Headers(Ne(t, r));
|
|
464
|
+
o.set("Accept", "application/json");
|
|
465
|
+
let s = new AbortController(), c = setTimeout(() => s.abort(), m);
|
|
466
|
+
try {
|
|
467
|
+
let e = await fetch(a, {
|
|
468
|
+
method: "GET",
|
|
469
|
+
headers: o,
|
|
470
|
+
signal: s.signal
|
|
471
|
+
});
|
|
472
|
+
if (!e.ok) throw new i(`Remote help '${a}' returned ${e.status}: ${await ee(e)}`, {
|
|
473
|
+
upstream: a,
|
|
474
|
+
status: e.status
|
|
475
|
+
});
|
|
476
|
+
return Be(await C(e, p));
|
|
477
|
+
} catch (t) {
|
|
478
|
+
throw t instanceof e ? t : new i(`Remote help '${a}' failed: ${S(t)}`, { upstream: a });
|
|
479
|
+
} finally {
|
|
480
|
+
clearTimeout(c);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function Be(e) {
|
|
484
|
+
let t = JSON.parse(e);
|
|
485
|
+
if (!_(t)) throw Error("Remote help payload is not a JSON object.");
|
|
486
|
+
return {
|
|
487
|
+
htbp: "draft",
|
|
488
|
+
kind: typeof t.kind == "string" && Re.includes(t.kind) ? t.kind : "directory",
|
|
489
|
+
title: typeof t.title == "string" ? t.title : "Remote TB Server",
|
|
490
|
+
description: typeof t.description == "string" ? t.description : void 0,
|
|
491
|
+
cachable: t.cachable === !0,
|
|
492
|
+
resources: Ve(t.resources),
|
|
493
|
+
endpoint: _(t.endpoint) ? He(t.endpoint) : void 0
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function Ve(e) {
|
|
497
|
+
if (!Array.isArray(e)) return;
|
|
498
|
+
let t = [];
|
|
499
|
+
for (let n of e) _(n) && typeof n.name == "string" && typeof n.path == "string" && t.push({
|
|
500
|
+
name: n.name,
|
|
501
|
+
path: n.path,
|
|
502
|
+
description: typeof n.description == "string" ? n.description : void 0
|
|
503
|
+
});
|
|
504
|
+
return t;
|
|
505
|
+
}
|
|
506
|
+
function He(e) {
|
|
507
|
+
let t = (Array.isArray(e.tools) ? e.tools : void 0)?.filter((e) => _(e) && typeof e.name == "string").map((e) => ({
|
|
508
|
+
name: e.name,
|
|
509
|
+
description: typeof e.description == "string" ? e.description : void 0,
|
|
510
|
+
inputSchema: e.inputSchema,
|
|
511
|
+
outputSchema: e.outputSchema
|
|
512
|
+
}));
|
|
513
|
+
return {
|
|
514
|
+
method: typeof e.method == "string" ? e.method : "POST",
|
|
515
|
+
inputSchema: e.inputSchema,
|
|
516
|
+
outputSchema: e.outputSchema,
|
|
517
|
+
example: e.example,
|
|
518
|
+
tools: t
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
522
|
+
//#region src/worker/tb/adapters/directory.ts
|
|
523
|
+
var Ue = {
|
|
524
|
+
kind: "directory",
|
|
525
|
+
async describe(e, t, r) {
|
|
526
|
+
if (r.length > 0) throw new n(`No child '${r[0]}' under directory '${e.id}'.`);
|
|
527
|
+
let i = await Promise.all(e.children.map((e) => We(e, t)));
|
|
528
|
+
return {
|
|
529
|
+
htbp: "draft",
|
|
530
|
+
kind: "directory",
|
|
531
|
+
title: e.title,
|
|
532
|
+
description: e.summary,
|
|
533
|
+
cachable: !0,
|
|
534
|
+
resources: i
|
|
535
|
+
};
|
|
536
|
+
},
|
|
537
|
+
async call(e) {
|
|
538
|
+
throw Error(`Directory '${e.id}' is not callable; descend to an end-path resource.`);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
async function We(e, t) {
|
|
542
|
+
let n = {
|
|
543
|
+
name: e.title,
|
|
544
|
+
path: `./${encodeURIComponent(e.id)}`,
|
|
545
|
+
description: Ke(e)
|
|
546
|
+
};
|
|
547
|
+
return e.kind === "remote" && await Ge(e, t, n), n;
|
|
548
|
+
}
|
|
549
|
+
async function Ge(e, t, n) {
|
|
550
|
+
try {
|
|
551
|
+
let r = await ze(t.env, e.helpUrl, e.headers);
|
|
552
|
+
r.title && (n.name = r.title), r.description && (n.description = r.description);
|
|
553
|
+
} catch {}
|
|
554
|
+
}
|
|
555
|
+
function Ke(e) {
|
|
556
|
+
return e.kind === "directory" || e.kind === "http" || e.kind === "remote" ? e.summary : e.description;
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/worker/tb/adapters/http.ts
|
|
560
|
+
var qe = {
|
|
561
|
+
kind: "http",
|
|
562
|
+
async describe(e, t, n) {
|
|
563
|
+
if (n.length === 0) return {
|
|
564
|
+
htbp: "draft",
|
|
565
|
+
kind: "http",
|
|
566
|
+
title: e.title,
|
|
567
|
+
description: e.summary,
|
|
568
|
+
cachable: !0,
|
|
569
|
+
resources: e.endpoints.map(Je)
|
|
570
|
+
};
|
|
571
|
+
let r = Ye(e, n[0]);
|
|
572
|
+
return {
|
|
573
|
+
htbp: "draft",
|
|
574
|
+
kind: "http",
|
|
575
|
+
title: r.name,
|
|
576
|
+
description: r.description,
|
|
577
|
+
cachable: !0,
|
|
578
|
+
endpoint: {
|
|
579
|
+
method: r.method,
|
|
580
|
+
inputSchema: r.inputSchema ?? {},
|
|
581
|
+
outputSchema: r.outputSchema,
|
|
582
|
+
example: r.example,
|
|
583
|
+
effect: r.effect,
|
|
584
|
+
scope: r.scope,
|
|
585
|
+
confirm: r.confirm
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
},
|
|
589
|
+
async call(e, t, n, r) {
|
|
590
|
+
if (n.length === 0) throw Error(`HTTP node '${e.id}' requires an endpoint name to call.`);
|
|
591
|
+
let a = Ye(e, n[0]), o = Ie(t.env, a.url, `HTTP endpoint '${a.name}'`);
|
|
592
|
+
Le(t.env, o);
|
|
593
|
+
let s = new Headers(Ne(t.env, a.headers)), c = a.method !== "GET" && a.method !== "HEAD";
|
|
594
|
+
c && !s.has("Content-Type") && s.set("Content-Type", "application/json"), s.set("Accept", s.get("Accept") ?? "application/json"), s.has("User-Agent") || s.set("User-Agent", "tool-bridge");
|
|
595
|
+
let l = new AbortController(), u = setTimeout(() => l.abort(), m);
|
|
596
|
+
try {
|
|
597
|
+
let t;
|
|
598
|
+
try {
|
|
599
|
+
t = await fetch(o, {
|
|
600
|
+
method: a.method,
|
|
601
|
+
headers: s,
|
|
602
|
+
body: c ? JSON.stringify(r ?? {}) : void 0,
|
|
603
|
+
signal: l.signal
|
|
604
|
+
});
|
|
605
|
+
} catch (t) {
|
|
606
|
+
throw new i(`HTTP endpoint '${a.name}' fetch failed: ${S(t)}`, {
|
|
607
|
+
upstream: e.id,
|
|
608
|
+
endpoint: a.name
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (!t.ok) throw new i(`HTTP endpoint '${a.name}' returned ${t.status}: ${await ee(t)}`, {
|
|
612
|
+
upstream: e.id,
|
|
613
|
+
endpoint: a.name,
|
|
614
|
+
status: t.status
|
|
615
|
+
});
|
|
616
|
+
let n = await C(t, p), u = t.headers.get("Content-Type") ?? "";
|
|
617
|
+
try {
|
|
618
|
+
return u.includes("application/json") && n ? JSON.parse(n) : n;
|
|
619
|
+
} catch (t) {
|
|
620
|
+
throw new i(`HTTP endpoint '${a.name}' returned malformed JSON: ${S(t)}`, {
|
|
621
|
+
upstream: e.id,
|
|
622
|
+
endpoint: a.name
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
} finally {
|
|
626
|
+
clearTimeout(u);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
function Je(e) {
|
|
631
|
+
return {
|
|
632
|
+
name: e.name,
|
|
633
|
+
path: `./${encodeURIComponent(e.name)}`,
|
|
634
|
+
description: e.description ? x(e.description) : void 0
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function Ye(e, t) {
|
|
638
|
+
let r = e.endpoints.find((e) => e.name === t);
|
|
639
|
+
if (!r) throw new n(`Endpoint '${t}' not found on HTTP node '${e.id}'.`);
|
|
640
|
+
return r;
|
|
641
|
+
}
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/worker/tb/mcp-client.ts
|
|
644
|
+
function Xe(e, t) {
|
|
645
|
+
return {
|
|
646
|
+
id: t.id,
|
|
647
|
+
endpoint: Ie(e, t.endpoint, `MCP endpoint for '${t.id}'`),
|
|
648
|
+
allowedTools: t.allowedTools,
|
|
649
|
+
resolvedHeaders: Ne(e, t.headers)
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
async function Ze(e) {
|
|
653
|
+
let t = await $e(e, "tools/list", {}), n = (_(t) && Array.isArray(t.tools) ? t.tools : []).map(ct).filter(Boolean);
|
|
654
|
+
if (!e.allowedTools || e.allowedTools.length === 0) return n;
|
|
655
|
+
let r = new Set(e.allowedTools);
|
|
656
|
+
return n.filter((e) => r.has(e.name));
|
|
657
|
+
}
|
|
658
|
+
async function Qe(e, t, r) {
|
|
659
|
+
if (e.allowedTools && e.allowedTools.length > 0 && !e.allowedTools.includes(t)) throw new n(`Tool '${t}' is not allowed for server '${e.id}'.`);
|
|
660
|
+
return $e(e, "tools/call", {
|
|
661
|
+
name: t,
|
|
662
|
+
arguments: _(r) ? r : {}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
async function $e(t, n, r) {
|
|
666
|
+
try {
|
|
667
|
+
let e = await et(t);
|
|
668
|
+
try {
|
|
669
|
+
return await tt(t, e.sessionId, "notifications/initialized", {}), (await nt(t, e.sessionId, n, r)).result;
|
|
670
|
+
} finally {
|
|
671
|
+
e.sessionId && await st(t, e.sessionId).catch(() => {});
|
|
672
|
+
}
|
|
673
|
+
} catch (r) {
|
|
674
|
+
throw r instanceof e ? r : new i(`MCP server '${t.id}': ${S(r)}`, {
|
|
675
|
+
upstream: t.id,
|
|
676
|
+
method: n
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async function et(e) {
|
|
681
|
+
return { sessionId: (await nt(e, void 0, "initialize", {
|
|
682
|
+
protocolVersion: u,
|
|
683
|
+
capabilities: {},
|
|
684
|
+
clientInfo: {
|
|
685
|
+
name: d,
|
|
686
|
+
version: f
|
|
687
|
+
}
|
|
688
|
+
})).sessionId };
|
|
689
|
+
}
|
|
690
|
+
async function tt(e, t, n, r) {
|
|
691
|
+
let i = await fetch(e.endpoint, {
|
|
692
|
+
method: "POST",
|
|
693
|
+
headers: rt(e, t),
|
|
694
|
+
body: JSON.stringify({
|
|
695
|
+
jsonrpc: "2.0",
|
|
696
|
+
method: n,
|
|
697
|
+
params: r
|
|
698
|
+
})
|
|
699
|
+
});
|
|
700
|
+
if (i.status !== 202 && !i.ok) throw Error(`MCP notification '${n}' failed with HTTP ${i.status}.`);
|
|
701
|
+
}
|
|
702
|
+
async function nt(e, t, n, r) {
|
|
703
|
+
let i = crypto.randomUUID(), a = await fetch(e.endpoint, {
|
|
704
|
+
method: "POST",
|
|
705
|
+
headers: rt(e, t),
|
|
706
|
+
body: JSON.stringify({
|
|
707
|
+
jsonrpc: "2.0",
|
|
708
|
+
id: i,
|
|
709
|
+
method: n,
|
|
710
|
+
params: r
|
|
711
|
+
})
|
|
712
|
+
});
|
|
713
|
+
if (!a.ok) throw Error(`MCP request '${n}' failed with HTTP ${a.status}: ${await ee(a)}`);
|
|
714
|
+
let o = await it(a, i);
|
|
715
|
+
if (o.error) throw Error(`MCP error ${o.error.code}: ${o.error.message}`);
|
|
716
|
+
return {
|
|
717
|
+
result: o.result,
|
|
718
|
+
sessionId: a.headers.get("MCP-Session-Id") ?? void 0
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function rt(e, t) {
|
|
722
|
+
let n = new Headers(e.resolvedHeaders);
|
|
723
|
+
return n.set("Accept", "application/json, text/event-stream"), n.set("Content-Type", "application/json"), n.set("MCP-Protocol-Version", u), t && n.set("MCP-Session-Id", t), n;
|
|
724
|
+
}
|
|
725
|
+
async function it(e, t) {
|
|
726
|
+
if ((e.headers.get("Content-Type") ?? "").includes("text/event-stream")) return at(e, t);
|
|
727
|
+
let n = await C(e, p), r = JSON.parse(n);
|
|
728
|
+
if (r.id !== t) throw Error(`Unexpected MCP response id '${String(r.id)}'.`);
|
|
729
|
+
return r;
|
|
730
|
+
}
|
|
731
|
+
async function at(e, t) {
|
|
732
|
+
if (!e.body) throw Error("MCP server returned an empty SSE response.");
|
|
733
|
+
let n = e.body.getReader(), r = new TextDecoder(), i = "", a = [], o = 0;
|
|
734
|
+
for (;;) {
|
|
735
|
+
let { done: e, value: s } = await n.read();
|
|
736
|
+
if (e) break;
|
|
737
|
+
if (o += s.byteLength, o > 4e6) throw Error("MCP SSE response exceeded the maximum size.");
|
|
738
|
+
i += r.decode(s, { stream: !0 });
|
|
739
|
+
let c = i.indexOf("\n");
|
|
740
|
+
for (; c >= 0;) {
|
|
741
|
+
let e = i.slice(0, c);
|
|
742
|
+
i = i.slice(c + 1);
|
|
743
|
+
let n = e.endsWith("\r") ? e.slice(0, -1) : e;
|
|
744
|
+
if (n === "") {
|
|
745
|
+
let e = ot(a);
|
|
746
|
+
if (a = [], e && e.id === t) return e;
|
|
747
|
+
} else n.startsWith("data:") && a.push(n.slice(5).trimStart());
|
|
748
|
+
c = i.indexOf("\n");
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
let s = ot(a);
|
|
752
|
+
if (s && s.id === t) return s;
|
|
753
|
+
throw Error(`MCP SSE stream ended before response '${t}' was received.`);
|
|
754
|
+
}
|
|
755
|
+
function ot(e) {
|
|
756
|
+
if (e.length === 0) return;
|
|
757
|
+
let t = e.join("\n").trim();
|
|
758
|
+
if (t.startsWith("{")) return JSON.parse(t);
|
|
759
|
+
}
|
|
760
|
+
async function st(e, t) {
|
|
761
|
+
await fetch(e.endpoint, {
|
|
762
|
+
method: "DELETE",
|
|
763
|
+
headers: rt(e, t)
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
function ct(e) {
|
|
767
|
+
if (!(!_(e) || typeof e.name != "string")) return {
|
|
768
|
+
name: e.name,
|
|
769
|
+
description: typeof e.description == "string" ? e.description : void 0,
|
|
770
|
+
inputSchema: e.inputSchema,
|
|
771
|
+
outputSchema: e.outputSchema,
|
|
772
|
+
annotations: e.annotations
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/worker/tb/virtualize.ts
|
|
777
|
+
function lt(e, t) {
|
|
778
|
+
let n = e.toolOverrides ?? {}, r = [], i = /* @__PURE__ */ new Map();
|
|
779
|
+
for (let a of t) {
|
|
780
|
+
let t = n[a.name];
|
|
781
|
+
if (t?.hide) continue;
|
|
782
|
+
let o = dt(e.namespace, t?.rename ?? a.name);
|
|
783
|
+
i.set(o, a.name), r.push({
|
|
784
|
+
name: o,
|
|
785
|
+
description: t?.description ?? a.description,
|
|
786
|
+
inputSchema: a.inputSchema,
|
|
787
|
+
outputSchema: a.outputSchema,
|
|
788
|
+
effect: t?.effect ?? a.effect,
|
|
789
|
+
scope: t?.scope ?? a.scope,
|
|
790
|
+
confirm: t?.confirm ?? a.confirm
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
exposed: r,
|
|
795
|
+
reverse: i
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function ut(e, t, r) {
|
|
799
|
+
let { reverse: i } = lt(e, t), a = i.get(r);
|
|
800
|
+
if (!a) throw new n(`Tool '${r}' is not exposed by node '${e.id}'.`);
|
|
801
|
+
return a;
|
|
802
|
+
}
|
|
803
|
+
function dt(e, t) {
|
|
804
|
+
return e ? `${e}__${t}` : t;
|
|
805
|
+
}
|
|
806
|
+
//#endregion
|
|
807
|
+
//#region src/worker/tb/adapters/mcp.ts
|
|
808
|
+
var ft = {
|
|
809
|
+
kind: "mcp",
|
|
810
|
+
async describe(e, t, r) {
|
|
811
|
+
let { exposed: i } = lt(e, (await Ze(Xe(t.env, e))).map(mt));
|
|
812
|
+
if (r.length === 0) return {
|
|
813
|
+
htbp: "draft",
|
|
814
|
+
kind: "mcp",
|
|
815
|
+
title: e.title,
|
|
816
|
+
description: e.description,
|
|
817
|
+
cachable: !0,
|
|
818
|
+
resources: i.map(pt)
|
|
819
|
+
};
|
|
820
|
+
let a = r[0], o = i.find((e) => e.name === a);
|
|
821
|
+
if (!o) throw new n(`Tool '${a}' is not exposed by node '${e.id}'.`);
|
|
822
|
+
return {
|
|
823
|
+
htbp: "draft",
|
|
824
|
+
kind: "mcp",
|
|
825
|
+
title: o.name,
|
|
826
|
+
description: o.description,
|
|
827
|
+
cachable: !0,
|
|
828
|
+
endpoint: {
|
|
829
|
+
method: "POST",
|
|
830
|
+
inputSchema: o.inputSchema ?? { type: "object" },
|
|
831
|
+
outputSchema: o.outputSchema,
|
|
832
|
+
example: {},
|
|
833
|
+
effect: o.effect,
|
|
834
|
+
scope: o.scope,
|
|
835
|
+
confirm: o.confirm
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
},
|
|
839
|
+
async call(e, t, n, r) {
|
|
840
|
+
if (n.length === 0) throw Error(`MCP node '${e.id}' requires a tool: call POST {path}/{tool}.`);
|
|
841
|
+
let i = Xe(t.env, e);
|
|
842
|
+
return Qe(i, ut(e, (await Ze(i)).map(mt), n[0]), ht(r));
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
function pt(e) {
|
|
846
|
+
return {
|
|
847
|
+
name: e.name,
|
|
848
|
+
path: `./${encodeURIComponent(e.name)}`,
|
|
849
|
+
description: e.description
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function mt(e) {
|
|
853
|
+
return {
|
|
854
|
+
name: e.name,
|
|
855
|
+
description: e.description ? x(e.description) : void 0,
|
|
856
|
+
inputSchema: e.inputSchema,
|
|
857
|
+
outputSchema: e.outputSchema
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
function ht(e) {
|
|
861
|
+
return e && typeof e == "object" && !Array.isArray(e) && "arguments" in e ? e.arguments ?? {} : e ?? {};
|
|
862
|
+
}
|
|
863
|
+
//#endregion
|
|
864
|
+
//#region src/worker/tb/storage-r2.ts
|
|
865
|
+
var gt = "/", _t = 1e6;
|
|
866
|
+
function vt(e, t) {
|
|
867
|
+
let n = e[t];
|
|
868
|
+
if (!n || typeof n.list != "function") throw Error(`R2 bucket binding '${t}' is not configured on this Worker.`);
|
|
869
|
+
return {
|
|
870
|
+
async list(e) {
|
|
871
|
+
let t = e && !e.endsWith(gt) ? `${e}${gt}` : e, r = await n.list({
|
|
872
|
+
prefix: t,
|
|
873
|
+
delimiter: gt
|
|
874
|
+
}), i = r.delimitedPrefixes.map((e) => ({
|
|
875
|
+
name: yt(e.replace(/\/$/, ""), t),
|
|
876
|
+
key: e,
|
|
877
|
+
isDir: !0
|
|
878
|
+
})), a = r.objects.filter((e) => e.key !== t).map((e) => ({
|
|
879
|
+
name: yt(e.key, t),
|
|
880
|
+
key: e.key,
|
|
881
|
+
isDir: !1
|
|
882
|
+
}));
|
|
883
|
+
return [...i, ...a];
|
|
884
|
+
},
|
|
885
|
+
async get(e) {
|
|
886
|
+
let t = await n.get(e);
|
|
887
|
+
if (!t) return null;
|
|
888
|
+
if (typeof t.size == "number" && t.size > _t) throw Error(`Object '${e}' exceeds the maximum readable size.`);
|
|
889
|
+
return {
|
|
890
|
+
key: e,
|
|
891
|
+
body: await t.text(),
|
|
892
|
+
contentType: t.httpMetadata?.contentType,
|
|
893
|
+
size: t.size
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function yt(e, t) {
|
|
899
|
+
return t && e.startsWith(t) ? e.slice(t.length) : e;
|
|
900
|
+
}
|
|
901
|
+
//#endregion
|
|
902
|
+
//#region src/worker/tb/adapters/mount.ts
|
|
903
|
+
var bt = {
|
|
904
|
+
kind: "mount",
|
|
905
|
+
async describe(e, t, r) {
|
|
906
|
+
let i = xt(t, e), a = St(e, r), o = await i.get(a);
|
|
907
|
+
if (o) return {
|
|
908
|
+
htbp: "draft",
|
|
909
|
+
kind: "mount",
|
|
910
|
+
title: r[r.length - 1] ?? e.title,
|
|
911
|
+
description: `File ${a} (${o.contentType ?? "application/octet-stream"})`,
|
|
912
|
+
cachable: !0,
|
|
913
|
+
endpoint: {
|
|
914
|
+
method: "GET",
|
|
915
|
+
inputSchema: {},
|
|
916
|
+
outputSchema: { type: "string" },
|
|
917
|
+
example: {}
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
let s = await i.list(a);
|
|
921
|
+
if (s.length === 0 && r.length > 0) throw new n(`No file or folder at '${a}'.`);
|
|
922
|
+
return {
|
|
923
|
+
htbp: "draft",
|
|
924
|
+
kind: "mount",
|
|
925
|
+
title: r.length === 0 ? e.title : r[r.length - 1] ?? e.title,
|
|
926
|
+
description: e.description,
|
|
927
|
+
cachable: !0,
|
|
928
|
+
resources: s.map(wt)
|
|
929
|
+
};
|
|
930
|
+
},
|
|
931
|
+
async call(e, t, r) {
|
|
932
|
+
let i = xt(t, e), a = St(e, r), o = await i.get(a);
|
|
933
|
+
if (!o) throw new n(`File '${a}' not found.`);
|
|
934
|
+
return {
|
|
935
|
+
key: o.key,
|
|
936
|
+
contentType: o.contentType,
|
|
937
|
+
content: o.body
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
function xt(e, t) {
|
|
942
|
+
return vt(e.env, t.bucket);
|
|
943
|
+
}
|
|
944
|
+
function St(e, t) {
|
|
945
|
+
return [e.prefix ? Ct(e.prefix) : "", t.map((e) => decodeURIComponent(e)).join("/")].filter((e) => e.length > 0).join("/");
|
|
946
|
+
}
|
|
947
|
+
function Ct(e) {
|
|
948
|
+
return e.replace(/^\/+|\/+$/g, "");
|
|
949
|
+
}
|
|
950
|
+
function wt(e) {
|
|
951
|
+
return {
|
|
952
|
+
name: e.name,
|
|
953
|
+
path: `./${encodeURIComponent(e.name)}`,
|
|
954
|
+
description: e.isDir ? "folder" : "file"
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
//#endregion
|
|
958
|
+
//#region src/worker/tb/adapters/index.ts
|
|
959
|
+
var Tt = {
|
|
960
|
+
directory: Ue,
|
|
961
|
+
mcp: ft,
|
|
962
|
+
http: qe,
|
|
963
|
+
remote: {
|
|
964
|
+
kind: "remote",
|
|
965
|
+
async describe(e, t) {
|
|
966
|
+
let n = await ze(t.env, e.helpUrl, e.headers);
|
|
967
|
+
return {
|
|
968
|
+
...n,
|
|
969
|
+
kind: "remote",
|
|
970
|
+
title: n.title || e.title
|
|
971
|
+
};
|
|
972
|
+
},
|
|
973
|
+
async call(e) {
|
|
974
|
+
throw Error(`Remote node '${e.id}' is a federation pointer; call the remote TB Server directly at its own endpoint.`);
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
mount: bt,
|
|
978
|
+
builtin: ke
|
|
979
|
+
};
|
|
980
|
+
function Et(e) {
|
|
981
|
+
let t = Tt[e.kind];
|
|
982
|
+
if (!t) throw Error(`No adapter registered for node kind '${e.kind}'.`);
|
|
983
|
+
return t;
|
|
984
|
+
}
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region src/worker/tb/crawl.ts
|
|
987
|
+
var Dt = {
|
|
988
|
+
maxDepth: 8,
|
|
989
|
+
maxNodes: 200
|
|
990
|
+
}, Ot = 8, kt = 200;
|
|
991
|
+
function At(e) {
|
|
992
|
+
return {
|
|
993
|
+
maxDepth: Ut(e?.maxDepth ?? Dt.maxDepth, 1, Ot),
|
|
994
|
+
maxNodes: Ut(e?.maxNodes ?? Dt.maxNodes, 1, kt)
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
async function jt(e, t, n, r, i = Dt) {
|
|
998
|
+
let a = {
|
|
999
|
+
env: e,
|
|
1000
|
+
root: t,
|
|
1001
|
+
authMode: r,
|
|
1002
|
+
opts: i,
|
|
1003
|
+
visited: /* @__PURE__ */ new Set(),
|
|
1004
|
+
count: 0
|
|
1005
|
+
};
|
|
1006
|
+
return n.url ? Nt(a, n.url, 0) : Mt(a, It(n.path ?? ""), 0);
|
|
1007
|
+
}
|
|
1008
|
+
async function Mt(e, t, n) {
|
|
1009
|
+
let r = E(e.root, t), i = t.length === 0 ? w : `${w}/${t.join("/")}`;
|
|
1010
|
+
if (!r) return {
|
|
1011
|
+
kind: "directory",
|
|
1012
|
+
path: i,
|
|
1013
|
+
helpUrl: `${i}/~help`,
|
|
1014
|
+
children: [],
|
|
1015
|
+
error: "Resource not found."
|
|
1016
|
+
};
|
|
1017
|
+
let a = i, o = {
|
|
1018
|
+
env: e.env,
|
|
1019
|
+
authMode: e.authMode,
|
|
1020
|
+
basePath: _e(r.node)
|
|
1021
|
+
};
|
|
1022
|
+
if (r.node.kind === "remote") return Nt(e, r.node.helpUrl, n, a);
|
|
1023
|
+
let s;
|
|
1024
|
+
try {
|
|
1025
|
+
s = await Et(r.node).describe(r.node, o, r.sub);
|
|
1026
|
+
} catch (e) {
|
|
1027
|
+
return {
|
|
1028
|
+
kind: r.node.kind,
|
|
1029
|
+
path: a,
|
|
1030
|
+
helpUrl: `${a}/~help`,
|
|
1031
|
+
children: [],
|
|
1032
|
+
error: Ht(e)
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
return Pt(e, Ft(s, a, `${a}/~help`), s, n, (t) => Mt(e, t, n + 1), a);
|
|
1036
|
+
}
|
|
1037
|
+
async function Nt(e, t, n, r) {
|
|
1038
|
+
let i = Vt(t), a = r ?? t, o;
|
|
1039
|
+
try {
|
|
1040
|
+
o = await ze(e.env, t, void 0);
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
return {
|
|
1043
|
+
kind: "remote",
|
|
1044
|
+
path: a,
|
|
1045
|
+
helpUrl: t,
|
|
1046
|
+
children: [],
|
|
1047
|
+
error: Ht(e)
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
let s = Ft(o, a, t);
|
|
1051
|
+
return s.kind = "remote", Pt(e, s, o, n, (t, r) => Nt(e, r, n + 1), i, t);
|
|
1052
|
+
}
|
|
1053
|
+
async function Pt(e, t, n, r, i, a, o) {
|
|
1054
|
+
if (n.endpoint) return t.endpoint = n.endpoint, t;
|
|
1055
|
+
if (e.visited.has(a) || (e.visited.add(a), r >= e.opts.maxDepth)) return t.truncated = !0, t;
|
|
1056
|
+
for (let r of n.resources ?? []) {
|
|
1057
|
+
if (e.count >= e.opts.maxNodes) {
|
|
1058
|
+
t.truncated = !0;
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
if (e.count += 1, o) {
|
|
1062
|
+
let e = zt(o, r.path);
|
|
1063
|
+
t.children.push(await i([], e));
|
|
1064
|
+
} else {
|
|
1065
|
+
let e = Lt(t.path, r.path);
|
|
1066
|
+
t.children.push(await i(e, ""));
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return t;
|
|
1070
|
+
}
|
|
1071
|
+
function Ft(e, t, n) {
|
|
1072
|
+
return {
|
|
1073
|
+
kind: e.kind,
|
|
1074
|
+
path: t,
|
|
1075
|
+
title: e.title,
|
|
1076
|
+
description: e.description,
|
|
1077
|
+
helpUrl: n,
|
|
1078
|
+
children: []
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function It(e) {
|
|
1082
|
+
return e.replace(/^\/?htbp\/?/, "").split("/").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
1083
|
+
}
|
|
1084
|
+
function Lt(e, t) {
|
|
1085
|
+
let n = It(e);
|
|
1086
|
+
return t.startsWith("../") ? [...n.slice(0, -1), ...Rt(t.slice(3))] : t.startsWith("./") ? [...n, ...Rt(t.slice(2))] : [...n, ...Rt(t)];
|
|
1087
|
+
}
|
|
1088
|
+
function Rt(e) {
|
|
1089
|
+
return e.split("/").filter((e) => e.length > 0);
|
|
1090
|
+
}
|
|
1091
|
+
function zt(e, t) {
|
|
1092
|
+
let n = e.replace(/\/~help$/, "/");
|
|
1093
|
+
return new URL(t.endsWith("/~help") ? t : `${Bt(t)}/~help`, n).toString();
|
|
1094
|
+
}
|
|
1095
|
+
function Bt(e) {
|
|
1096
|
+
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
1097
|
+
}
|
|
1098
|
+
function Vt(e) {
|
|
1099
|
+
try {
|
|
1100
|
+
return new URL(e).toString();
|
|
1101
|
+
} catch {
|
|
1102
|
+
return e;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
function Ht(e) {
|
|
1106
|
+
return e instanceof Error ? e.message : String(e);
|
|
1107
|
+
}
|
|
1108
|
+
function Ut(e, t, n) {
|
|
1109
|
+
return Number.isFinite(e) ? Math.max(t, Math.min(n, Math.floor(e))) : t;
|
|
1110
|
+
}
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region src/worker/tb/entities.ts
|
|
1113
|
+
var Wt = "_global";
|
|
1114
|
+
function D(e) {
|
|
1115
|
+
return e ?? Wt;
|
|
1116
|
+
}
|
|
1117
|
+
function O() {
|
|
1118
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1119
|
+
}
|
|
1120
|
+
function k(e) {
|
|
1121
|
+
if (!e.TENANTS) throw new s("Provider entities require the TENANTS KV binding.");
|
|
1122
|
+
return e.TENANTS;
|
|
1123
|
+
}
|
|
1124
|
+
function Gt(e) {
|
|
1125
|
+
return !!e.TENANTS;
|
|
1126
|
+
}
|
|
1127
|
+
var Kt = [
|
|
1128
|
+
"builtin",
|
|
1129
|
+
"first-party",
|
|
1130
|
+
"verified",
|
|
1131
|
+
"community",
|
|
1132
|
+
"federated"
|
|
1133
|
+
], qt = [
|
|
1134
|
+
"active",
|
|
1135
|
+
"suspended",
|
|
1136
|
+
"retired"
|
|
1137
|
+
], Jt = [
|
|
1138
|
+
"draft",
|
|
1139
|
+
"published",
|
|
1140
|
+
"deprecated",
|
|
1141
|
+
"retired"
|
|
1142
|
+
], Yt = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
1143
|
+
function A(e, t) {
|
|
1144
|
+
if (!e || !Yt.test(e)) throw new s(`${t} must match ${Yt} (lowercase alphanumeric, ".", "_", "-").`);
|
|
1145
|
+
return e;
|
|
1146
|
+
}
|
|
1147
|
+
async function j(e, t) {
|
|
1148
|
+
return await k(e).get(`provider:${t}`, "json");
|
|
1149
|
+
}
|
|
1150
|
+
async function M(e, t) {
|
|
1151
|
+
await k(e).put(`provider:${t.id}`, JSON.stringify(t));
|
|
1152
|
+
}
|
|
1153
|
+
async function Xt(e, t) {
|
|
1154
|
+
await k(e).delete(`provider:${t}`);
|
|
1155
|
+
}
|
|
1156
|
+
async function Zt(e) {
|
|
1157
|
+
return hn(e, "provider:");
|
|
1158
|
+
}
|
|
1159
|
+
function Qt(e, t) {
|
|
1160
|
+
if (!_(e)) throw new s("Provider must be a JSON object.");
|
|
1161
|
+
let n = t?.id ?? A(v(e, "id"), "provider id"), r = v(e, "trustTier") ?? t?.trustTier ?? "community";
|
|
1162
|
+
if (!Kt.includes(r)) throw new s(`trustTier must be one of ${Kt.join(", ")}.`);
|
|
1163
|
+
let i = v(e, "status") ?? t?.status ?? "active";
|
|
1164
|
+
if (!qt.includes(i)) throw new s(`status must be one of ${qt.join(", ")}.`);
|
|
1165
|
+
return {
|
|
1166
|
+
id: n,
|
|
1167
|
+
displayName: v(e, "displayName") ?? t?.displayName ?? n,
|
|
1168
|
+
contact: v(e, "contact") ?? t?.contact,
|
|
1169
|
+
trustTier: r,
|
|
1170
|
+
status: i,
|
|
1171
|
+
createdAt: t?.createdAt ?? O(),
|
|
1172
|
+
updatedAt: O()
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
async function N(e, t, n) {
|
|
1176
|
+
return await k(e).get(`pub:${t}:${n}`, "json");
|
|
1177
|
+
}
|
|
1178
|
+
async function P(e, t) {
|
|
1179
|
+
await k(e).put(`pub:${t.providerId}:${t.pubId}`, JSON.stringify(t));
|
|
1180
|
+
}
|
|
1181
|
+
async function $t(e, t, n) {
|
|
1182
|
+
await k(e).delete(`pub:${t}:${n}`);
|
|
1183
|
+
}
|
|
1184
|
+
async function F(e, t) {
|
|
1185
|
+
return hn(e, `pub:${t}:`);
|
|
1186
|
+
}
|
|
1187
|
+
function en(e, t, n) {
|
|
1188
|
+
if (!_(e)) throw new s("Publication must be a JSON object.");
|
|
1189
|
+
let r = n?.pubId ?? A(v(e, "pubId") ?? v(e, "id"), "pubId"), i = v(e, "status") ?? n?.status ?? "draft";
|
|
1190
|
+
if (!Jt.includes(i)) throw new s(`status must be one of ${Jt.join(", ")}.`);
|
|
1191
|
+
let a = _(e.binding) ? e.binding : n?.binding;
|
|
1192
|
+
if (!a) throw new s("Publication binding is required.");
|
|
1193
|
+
let o = _(e.shaping) ? e.shaping : n?.shaping, c = tn(e.semantics) ?? n?.semantics, l = {
|
|
1194
|
+
providerId: t,
|
|
1195
|
+
pubId: r,
|
|
1196
|
+
version: v(e, "version") ?? n?.version ?? "0.1.0",
|
|
1197
|
+
binding: a,
|
|
1198
|
+
shaping: o,
|
|
1199
|
+
semantics: c,
|
|
1200
|
+
status: i,
|
|
1201
|
+
createdAt: n?.createdAt ?? O(),
|
|
1202
|
+
updatedAt: O()
|
|
1203
|
+
};
|
|
1204
|
+
return ln(l, "binding-check"), l;
|
|
1205
|
+
}
|
|
1206
|
+
function tn(e) {
|
|
1207
|
+
if (!_(e)) return;
|
|
1208
|
+
let t = {};
|
|
1209
|
+
for (let [n, r] of Object.entries(e)) {
|
|
1210
|
+
if (!_(r)) continue;
|
|
1211
|
+
let e = r.effect;
|
|
1212
|
+
t[n] = {
|
|
1213
|
+
effect: e === "read" || e === "write" || e === "destructive" || e === "external" ? e : void 0,
|
|
1214
|
+
scope: v(r, "scope"),
|
|
1215
|
+
confirm: r.confirm === !0 ? !0 : void 0
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
return Object.keys(t).length > 0 ? t : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
async function nn(e, t, n) {
|
|
1221
|
+
return await k(e).get(`placement:${D(t)}:${n}`, "json");
|
|
1222
|
+
}
|
|
1223
|
+
async function rn(e, t) {
|
|
1224
|
+
await k(e).put(`placement:${D(t.tenantId)}:${t.id}`, JSON.stringify(t));
|
|
1225
|
+
}
|
|
1226
|
+
async function an(e, t, n) {
|
|
1227
|
+
await k(e).delete(`placement:${D(t)}:${n}`);
|
|
1228
|
+
}
|
|
1229
|
+
async function on(e, t) {
|
|
1230
|
+
return hn(e, `placement:${D(t)}:`);
|
|
1231
|
+
}
|
|
1232
|
+
function sn(e) {
|
|
1233
|
+
let t = (e ?? "").split("/").map((e) => e.trim()).filter((e) => e.length > 0);
|
|
1234
|
+
if (t.length === 0) throw new s("Placement path is required.");
|
|
1235
|
+
for (let e of t) if (e.startsWith("~") || e === "." || e === ".." || !Yt.test(e)) throw new s(`Placement path segment '${e}' is not allowed.`);
|
|
1236
|
+
return t;
|
|
1237
|
+
}
|
|
1238
|
+
function cn() {
|
|
1239
|
+
return `plc_${[...crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(6))].map((e) => e.toString(16).padStart(2, "0")).join("")}`;
|
|
1240
|
+
}
|
|
1241
|
+
function ln(e, t) {
|
|
1242
|
+
let n = {
|
|
1243
|
+
...un(e.binding),
|
|
1244
|
+
id: t
|
|
1245
|
+
};
|
|
1246
|
+
return e.shaping?.namespace && (n.namespace = e.shaping.namespace), e.shaping?.toolOverrides && (n.toolOverrides = {
|
|
1247
|
+
..._(n.toolOverrides) ? n.toolOverrides : {},
|
|
1248
|
+
...e.shaping.toolOverrides
|
|
1249
|
+
}), dn(n, e.semantics), ge(n, `pub:${e.providerId}:${e.pubId}`);
|
|
1250
|
+
}
|
|
1251
|
+
function un(e) {
|
|
1252
|
+
return JSON.parse(JSON.stringify(e));
|
|
1253
|
+
}
|
|
1254
|
+
function dn(e, t) {
|
|
1255
|
+
if (!t) return;
|
|
1256
|
+
let n = e.type ?? "mcp";
|
|
1257
|
+
if (n === "mcp") {
|
|
1258
|
+
let n = _(e.toolOverrides) ? { ...e.toolOverrides } : {};
|
|
1259
|
+
for (let [e, r] of Object.entries(t)) n[e] = {
|
|
1260
|
+
..._(n[e]) ? n[e] : {},
|
|
1261
|
+
...r
|
|
1262
|
+
};
|
|
1263
|
+
e.toolOverrides = n;
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
let r = n === "http" ? e.endpoints : n === "builtin" ? e.tools : void 0;
|
|
1267
|
+
if (Array.isArray(r)) for (let e of r) _(e) && typeof e.name == "string" && t[e.name] && Object.assign(e, t[e.name]);
|
|
1268
|
+
}
|
|
1269
|
+
async function fn(e, t, n) {
|
|
1270
|
+
let r = {
|
|
1271
|
+
applied: 0,
|
|
1272
|
+
skipped: []
|
|
1273
|
+
};
|
|
1274
|
+
if (!Gt(e)) return r;
|
|
1275
|
+
let i = await on(e, n);
|
|
1276
|
+
if (i.length === 0) return r;
|
|
1277
|
+
let a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
|
|
1278
|
+
for (let n of i) {
|
|
1279
|
+
if (!n.enabled) {
|
|
1280
|
+
r.skipped.push({
|
|
1281
|
+
placementId: n.id,
|
|
1282
|
+
reason: "disabled"
|
|
1283
|
+
});
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
let { providerId: i, pubId: s, version: c } = n.pubRef;
|
|
1287
|
+
a.has(i) || a.set(i, await j(e, i));
|
|
1288
|
+
let l = a.get(i);
|
|
1289
|
+
if (!l || l.status !== "active") {
|
|
1290
|
+
r.skipped.push({
|
|
1291
|
+
placementId: n.id,
|
|
1292
|
+
reason: `provider ${l ? l.status : "missing"}`
|
|
1293
|
+
});
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1296
|
+
let u = `${i}:${s}`;
|
|
1297
|
+
o.has(u) || o.set(u, await N(e, i, s));
|
|
1298
|
+
let d = o.get(u);
|
|
1299
|
+
if (!d || d.status !== "published" && d.status !== "deprecated") {
|
|
1300
|
+
r.skipped.push({
|
|
1301
|
+
placementId: n.id,
|
|
1302
|
+
reason: `publication ${d ? d.status : "missing"}`
|
|
1303
|
+
});
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (c && pn(c) !== pn(d.version)) {
|
|
1307
|
+
r.skipped.push({
|
|
1308
|
+
placementId: n.id,
|
|
1309
|
+
reason: `version pin ${c} != ${d.version}`
|
|
1310
|
+
});
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
let f, p;
|
|
1314
|
+
try {
|
|
1315
|
+
f = sn(n.path), p = ln(d, f[f.length - 1]);
|
|
1316
|
+
} catch (e) {
|
|
1317
|
+
r.skipped.push({
|
|
1318
|
+
placementId: n.id,
|
|
1319
|
+
reason: e instanceof Error ? e.message : "invalid"
|
|
1320
|
+
});
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
Object.defineProperty(p, "tbProviderId", {
|
|
1324
|
+
value: l.id,
|
|
1325
|
+
enumerable: !1,
|
|
1326
|
+
writable: !0,
|
|
1327
|
+
configurable: !0
|
|
1328
|
+
}), mn(t, f.slice(0, -1), p) ? r.applied += 1 : r.skipped.push({
|
|
1329
|
+
placementId: n.id,
|
|
1330
|
+
reason: "path conflict"
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
return r.applied > 0 && T(t), r;
|
|
1334
|
+
}
|
|
1335
|
+
function pn(e) {
|
|
1336
|
+
return e.split(".")[0] ?? e;
|
|
1337
|
+
}
|
|
1338
|
+
function mn(e, t, n) {
|
|
1339
|
+
let r = e;
|
|
1340
|
+
for (let e of t) {
|
|
1341
|
+
let t = r.children.find((t) => t.id === e);
|
|
1342
|
+
if (t || (t = {
|
|
1343
|
+
kind: "directory",
|
|
1344
|
+
id: e,
|
|
1345
|
+
title: e,
|
|
1346
|
+
children: []
|
|
1347
|
+
}, r.children.push(t)), t.kind !== "directory") return !1;
|
|
1348
|
+
r = t;
|
|
1349
|
+
}
|
|
1350
|
+
return r.children.some((e) => e.id === n.id) ? !1 : (r.children.push(n), !0);
|
|
1351
|
+
}
|
|
1352
|
+
async function hn(e, t) {
|
|
1353
|
+
let n = k(e), r = [], i;
|
|
1354
|
+
do {
|
|
1355
|
+
let e = await n.list({
|
|
1356
|
+
prefix: t,
|
|
1357
|
+
cursor: i
|
|
1358
|
+
}), a = await Promise.all(e.keys.map((e) => n.get(e.name, "json")));
|
|
1359
|
+
for (let e of a) e && r.push(e);
|
|
1360
|
+
i = e.list_complete ? void 0 : e.cursor;
|
|
1361
|
+
} while (i);
|
|
1362
|
+
return r;
|
|
1363
|
+
}
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region src/worker/tb/tenant.ts
|
|
1366
|
+
function gn(e) {
|
|
1367
|
+
return !!e.TENANTS && e.TENANT_MODE === "true";
|
|
1368
|
+
}
|
|
1369
|
+
async function _n(e) {
|
|
1370
|
+
let t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
|
|
1371
|
+
return [...new Uint8Array(t)].map((e) => e.toString(16).padStart(2, "0")).join("");
|
|
1372
|
+
}
|
|
1373
|
+
var vn = [
|
|
1374
|
+
"agent",
|
|
1375
|
+
"provider",
|
|
1376
|
+
"host",
|
|
1377
|
+
"admin",
|
|
1378
|
+
"service"
|
|
1379
|
+
];
|
|
1380
|
+
async function yn(e, t) {
|
|
1381
|
+
if (!e.TENANTS) return null;
|
|
1382
|
+
let n = await _n(t), r = await e.TENANTS.get(`apikey:${n}`, "json");
|
|
1383
|
+
if (!_(r)) return null;
|
|
1384
|
+
let i = vn.includes(r.principal) ? r.principal : "agent", a = {
|
|
1385
|
+
principal: i,
|
|
1386
|
+
tenantId: typeof r.tenantId == "string" ? r.tenantId : void 0,
|
|
1387
|
+
providerId: typeof r.providerId == "string" ? r.providerId : void 0,
|
|
1388
|
+
hostId: typeof r.hostId == "string" ? r.hostId : void 0,
|
|
1389
|
+
label: typeof r.label == "string" ? r.label : void 0,
|
|
1390
|
+
createdAt: typeof r.createdAt == "string" ? r.createdAt : void 0,
|
|
1391
|
+
expiresAt: typeof r.expiresAt == "string" ? r.expiresAt : void 0
|
|
1392
|
+
};
|
|
1393
|
+
if (a.expiresAt && Date.parse(a.expiresAt) < Date.now()) return null;
|
|
1394
|
+
if (a.tenantId) {
|
|
1395
|
+
let t = await e.TENANTS.get(`tenant:${a.tenantId}`);
|
|
1396
|
+
if (t) a.root = ne(t);
|
|
1397
|
+
else if (i === "agent") return null;
|
|
1398
|
+
} else if (i === "agent") return null;
|
|
1399
|
+
return a;
|
|
1400
|
+
}
|
|
1401
|
+
function bn(e) {
|
|
1402
|
+
return `${e}_${[...crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16))].map((e) => e.toString(16).padStart(2, "0")).join("")}`;
|
|
1403
|
+
}
|
|
1404
|
+
async function xn(e, t, n) {
|
|
1405
|
+
if (!e.TENANTS) throw Error("API keys require the TENANTS KV binding.");
|
|
1406
|
+
if (n.expiresAt && Number.isNaN(Date.parse(n.expiresAt))) throw new s("expiresAt must be a valid date string.");
|
|
1407
|
+
await e.TENANTS.put(`apikey:${await _n(t)}`, JSON.stringify(n));
|
|
1408
|
+
}
|
|
1409
|
+
async function Sn(e) {
|
|
1410
|
+
if (!e.TENANTS) return [];
|
|
1411
|
+
let t = [], n;
|
|
1412
|
+
do {
|
|
1413
|
+
let r = await e.TENANTS.list({
|
|
1414
|
+
prefix: "apikey:",
|
|
1415
|
+
cursor: n
|
|
1416
|
+
});
|
|
1417
|
+
for (let n of r.keys) {
|
|
1418
|
+
let r = await e.TENANTS.get(n.name, "json");
|
|
1419
|
+
r && t.push({
|
|
1420
|
+
hash: n.name.slice(7),
|
|
1421
|
+
record: r
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
n = r.list_complete ? void 0 : r.cursor;
|
|
1425
|
+
} while (n);
|
|
1426
|
+
return t;
|
|
1427
|
+
}
|
|
1428
|
+
//#endregion
|
|
1429
|
+
//#region src/worker/tb/host-api.ts
|
|
1430
|
+
var Cn = /^[a-z0-9][a-z0-9._-]{0,63}$/, wn = "mnt-", Tn = "mnt_";
|
|
1431
|
+
function I(e) {
|
|
1432
|
+
if (!e.TENANTS) throw new s("Hosts require the TENANTS KV binding.");
|
|
1433
|
+
return e.TENANTS;
|
|
1434
|
+
}
|
|
1435
|
+
async function En(e, t) {
|
|
1436
|
+
return await I(e).get(`host:${t}`, "json");
|
|
1437
|
+
}
|
|
1438
|
+
async function Dn(e, t, r) {
|
|
1439
|
+
let i = new URL(e.url).pathname;
|
|
1440
|
+
if (!i.startsWith("/api/hosts")) return;
|
|
1441
|
+
if (!Gt(t)) return o(501, "not_supported", "Host registration requires the TENANTS KV binding.");
|
|
1442
|
+
if (i === "/api/hosts" && e.method === "POST") return On(r, "Registering hosts"), An(e, t);
|
|
1443
|
+
let s = /^\/api\/hosts\/([^/]+)(?:\/(.*))?$/.exec(i);
|
|
1444
|
+
if (!s) return o(404, "not_found", "API route not found.");
|
|
1445
|
+
let c = decodeURIComponent(s[1] ?? ""), l = s[2] ?? "";
|
|
1446
|
+
if (!r.isAdmin && !(r.principal === "host" && r.hostId === c)) throw new a(`Not allowed to access host '${c}'.`);
|
|
1447
|
+
let u = await En(t, c);
|
|
1448
|
+
if (!u) throw new n(`Host '${c}' not found.`);
|
|
1449
|
+
if (l === "" && e.method === "GET") return h({ host: u });
|
|
1450
|
+
if (l === "keys" && e.method === "POST") {
|
|
1451
|
+
On(r, "Minting host keys");
|
|
1452
|
+
let n = await kn(e), i = bn("tbk"), a = {
|
|
1453
|
+
principal: "host",
|
|
1454
|
+
hostId: c,
|
|
1455
|
+
tenantId: u.tenantId,
|
|
1456
|
+
providerId: u.providerId,
|
|
1457
|
+
label: v(n, "label"),
|
|
1458
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1459
|
+
expiresAt: v(n, "expiresAt")
|
|
1460
|
+
};
|
|
1461
|
+
return await xn(t, i, a), h({
|
|
1462
|
+
key: i,
|
|
1463
|
+
record: a
|
|
1464
|
+
}, { status: 201 });
|
|
1465
|
+
}
|
|
1466
|
+
return l === "mounts:sync" && e.method === "POST" ? Mn(e, t, u) : o(404, "not_found", "API route not found.");
|
|
1467
|
+
}
|
|
1468
|
+
function On(e, t) {
|
|
1469
|
+
if (!e.isAdmin) throw new a(`${t} requires an admin key.`);
|
|
1470
|
+
}
|
|
1471
|
+
async function kn(e) {
|
|
1472
|
+
let t = await e.json().catch(() => void 0);
|
|
1473
|
+
if (!_(t)) throw new s("Request body must be a JSON object.");
|
|
1474
|
+
return t;
|
|
1475
|
+
}
|
|
1476
|
+
async function An(e, t) {
|
|
1477
|
+
let n = await kn(e), r = v(n, "id");
|
|
1478
|
+
if (!r || !Cn.test(r)) throw new s(`Host id must match ${Cn}.`);
|
|
1479
|
+
if (await En(t, r)) return o(409, "conflict", `Host '${r}' already exists.`);
|
|
1480
|
+
let i = v(n, "tenantId") ?? r, a = (/* @__PURE__ */ new Date()).toISOString(), c = {
|
|
1481
|
+
id: r,
|
|
1482
|
+
tenantId: i,
|
|
1483
|
+
providerId: r,
|
|
1484
|
+
confirmDelegated: n.confirmDelegated === !0 ? !0 : void 0,
|
|
1485
|
+
createdAt: a,
|
|
1486
|
+
updatedAt: a
|
|
1487
|
+
};
|
|
1488
|
+
return await j(t, r) || await M(t, {
|
|
1489
|
+
id: r,
|
|
1490
|
+
displayName: v(n, "displayName") ?? r,
|
|
1491
|
+
trustTier: "first-party",
|
|
1492
|
+
status: "active",
|
|
1493
|
+
createdAt: a,
|
|
1494
|
+
updatedAt: a
|
|
1495
|
+
}), await I(t).get(`tenant:${i}`) || await I(t).put(`tenant:${i}`, JSON.stringify({
|
|
1496
|
+
type: "directory",
|
|
1497
|
+
id: "root",
|
|
1498
|
+
title: c.id,
|
|
1499
|
+
children: []
|
|
1500
|
+
})), await I(t).put(`host:${r}`, JSON.stringify(c)), h({ host: c }, { status: 201 });
|
|
1501
|
+
}
|
|
1502
|
+
function jn(e) {
|
|
1503
|
+
return e.replace(/\//g, "-");
|
|
1504
|
+
}
|
|
1505
|
+
async function Mn(e, t, n) {
|
|
1506
|
+
let r = await kn(e);
|
|
1507
|
+
if (!Array.isArray(r.mounts)) throw new s("mounts must be an array of {path, binding}.");
|
|
1508
|
+
let i = r.prune !== !1, a = (/* @__PURE__ */ new Date()).toISOString(), o = [], c = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Set();
|
|
1509
|
+
for (let e of r.mounts) {
|
|
1510
|
+
if (!_(e)) throw new s("Each mount must be an object.");
|
|
1511
|
+
let r = e;
|
|
1512
|
+
if (typeof r.path != "string" || !_(r.binding)) throw new s("Each mount needs a path and a binding object.");
|
|
1513
|
+
let i = jn(r.path), u = `${wn}${i}`;
|
|
1514
|
+
await P(t, en({
|
|
1515
|
+
pubId: u,
|
|
1516
|
+
version: r.version ?? "0.1.0",
|
|
1517
|
+
binding: r.binding,
|
|
1518
|
+
shaping: r.shaping,
|
|
1519
|
+
semantics: r.semantics,
|
|
1520
|
+
status: "published"
|
|
1521
|
+
}, n.providerId)), l.add(u);
|
|
1522
|
+
let d = {
|
|
1523
|
+
id: `${Tn}${i}`,
|
|
1524
|
+
tenantId: n.tenantId,
|
|
1525
|
+
path: r.path,
|
|
1526
|
+
pubRef: {
|
|
1527
|
+
providerId: n.providerId,
|
|
1528
|
+
pubId: u
|
|
1529
|
+
},
|
|
1530
|
+
enabled: !0,
|
|
1531
|
+
createdAt: a,
|
|
1532
|
+
updatedAt: a
|
|
1533
|
+
};
|
|
1534
|
+
await rn(t, d), c.add(d.id), o.push(d);
|
|
1535
|
+
}
|
|
1536
|
+
let u = 0;
|
|
1537
|
+
if (i) {
|
|
1538
|
+
for (let e of await on(t, n.tenantId)) e.id.startsWith(Tn) && !c.has(e.id) && (await an(t, n.tenantId, e.id), u += 1);
|
|
1539
|
+
for (let e of await F(t, n.providerId)) e.pubId.startsWith(wn) && !l.has(e.pubId) && await $t(t, n.providerId, e.pubId);
|
|
1540
|
+
}
|
|
1541
|
+
return await I(t).put(`host:${n.id}`, JSON.stringify({
|
|
1542
|
+
...n,
|
|
1543
|
+
updatedAt: a
|
|
1544
|
+
})), h({
|
|
1545
|
+
ok: !0,
|
|
1546
|
+
applied: o.length,
|
|
1547
|
+
removed: u,
|
|
1548
|
+
placements: o
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
//#endregion
|
|
1552
|
+
//#region src/worker/tb/provider-api.ts
|
|
1553
|
+
var Nn = 1e6;
|
|
1554
|
+
async function L(e) {
|
|
1555
|
+
let t = e.headers.get("Content-Length");
|
|
1556
|
+
if (t && Number(t) > Nn) throw new s("Request body is too large.");
|
|
1557
|
+
let n = await e.json().catch(() => void 0);
|
|
1558
|
+
if (!_(n)) throw new s("Request body must be a JSON object.");
|
|
1559
|
+
return n;
|
|
1560
|
+
}
|
|
1561
|
+
function Pn(e, t) {
|
|
1562
|
+
if (!e.isAdmin && !(e.principal === "provider" && e.providerId === t)) throw new a(`Not allowed to access provider '${t}'.`);
|
|
1563
|
+
}
|
|
1564
|
+
function R(e, t) {
|
|
1565
|
+
if (!e.isAdmin) throw new a(`${t} requires an admin key.`);
|
|
1566
|
+
}
|
|
1567
|
+
async function Fn(e, t, r) {
|
|
1568
|
+
let i = new URL(e.url), s = i.pathname;
|
|
1569
|
+
if (!s.startsWith("/api/providers") && !s.startsWith("/api/placements")) return;
|
|
1570
|
+
if (!Gt(t)) return o(501, "not_supported", "Provider entities require the TENANTS KV binding.");
|
|
1571
|
+
if (s === "/api/placements" && e.method === "GET") {
|
|
1572
|
+
R(r, "Listing placements");
|
|
1573
|
+
let e = i.searchParams.get("tenant"), n = !e || e === "_global" ? null : e;
|
|
1574
|
+
return h({
|
|
1575
|
+
tenantId: D(n),
|
|
1576
|
+
placements: await on(t, n)
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
if (s === "/api/placements" && e.method === "POST") return R(r, "Writing placements"), In(e, t);
|
|
1580
|
+
let c = /^\/api\/placements\/([^/]+)$/.exec(s);
|
|
1581
|
+
if (c && e.method === "DELETE") return R(r, "Deleting placements"), Ln(e, t, decodeURIComponent(c[1] ?? ""), i);
|
|
1582
|
+
if (s === "/api/providers" && e.method === "GET") {
|
|
1583
|
+
if (r.isAdmin) return h({ providers: await Zt(t) });
|
|
1584
|
+
if (r.principal === "provider" && r.providerId) {
|
|
1585
|
+
let e = await j(t, r.providerId);
|
|
1586
|
+
return h({ providers: e ? [e] : [] });
|
|
1587
|
+
}
|
|
1588
|
+
throw new a("Listing providers requires an admin or provider key.");
|
|
1589
|
+
}
|
|
1590
|
+
if (s === "/api/providers" && e.method === "POST") {
|
|
1591
|
+
R(r, "Creating providers");
|
|
1592
|
+
let n = Qt(await L(e));
|
|
1593
|
+
return await j(t, n.id) ? o(409, "conflict", `Provider '${n.id}' already exists.`) : (await M(t, n), h({ provider: n }, { status: 201 }));
|
|
1594
|
+
}
|
|
1595
|
+
let l = /^\/api\/providers\/([^/]+)(?:\/(.*))?$/.exec(s);
|
|
1596
|
+
if (!l) return o(404, "not_found", "API route not found.");
|
|
1597
|
+
let u = decodeURIComponent(l[1] ?? ""), d = l[2] ?? "";
|
|
1598
|
+
Pn(r, u);
|
|
1599
|
+
let f = await j(t, u);
|
|
1600
|
+
if (!f && !(d === "" && e.method === "PUT" && r.isAdmin)) throw new n(`Provider '${u}' not found.`);
|
|
1601
|
+
if (d === "") {
|
|
1602
|
+
if (e.method === "GET") return h({ provider: f });
|
|
1603
|
+
if (e.method === "PUT") {
|
|
1604
|
+
let n = await L(e), i = Qt(r.isAdmin ? n : {
|
|
1605
|
+
displayName: n.displayName,
|
|
1606
|
+
contact: n.contact
|
|
1607
|
+
}, f ?? Qt({ id: u }));
|
|
1608
|
+
return await M(t, i), h({ provider: i });
|
|
1609
|
+
}
|
|
1610
|
+
if (e.method === "DELETE") {
|
|
1611
|
+
R(r, "Deleting providers");
|
|
1612
|
+
let e = await F(t, u);
|
|
1613
|
+
return e.length > 0 ? o(409, "conflict", `Provider '${u}' still has ${e.length} publication(s).`) : (await Xt(t, u), h({ ok: !0 }));
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (d === "keys" && e.method === "POST") {
|
|
1617
|
+
R(r, "Minting provider keys");
|
|
1618
|
+
let n = await L(e), i = bn("tbp"), a = {
|
|
1619
|
+
principal: "provider",
|
|
1620
|
+
providerId: u,
|
|
1621
|
+
label: v(n, "label"),
|
|
1622
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1623
|
+
expiresAt: v(n, "expiresAt")
|
|
1624
|
+
};
|
|
1625
|
+
return await xn(t, i, a), h({
|
|
1626
|
+
key: i,
|
|
1627
|
+
record: a
|
|
1628
|
+
}, { status: 201 });
|
|
1629
|
+
}
|
|
1630
|
+
if (d === "pubs") {
|
|
1631
|
+
if (e.method === "GET") return h({ publications: await F(t, u) });
|
|
1632
|
+
if (e.method === "POST") {
|
|
1633
|
+
let n = en(await L(e), u);
|
|
1634
|
+
return await N(t, u, n.pubId) ? o(409, "conflict", `Publication '${n.pubId}' already exists.`) : (await P(t, n), h({ publication: n }, { status: 201 }));
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
let p = /^pubs\/([^/]+)(\/publish)?$/.exec(d);
|
|
1638
|
+
if (p) {
|
|
1639
|
+
let r = decodeURIComponent(p[1] ?? ""), i = await N(t, u, r);
|
|
1640
|
+
if (p[2] === "/publish" && e.method === "POST") {
|
|
1641
|
+
if (!i) throw new n(`Publication '${r}' not found.`);
|
|
1642
|
+
let e = {
|
|
1643
|
+
...i,
|
|
1644
|
+
status: "published",
|
|
1645
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1646
|
+
};
|
|
1647
|
+
return await P(t, e), h({ publication: e });
|
|
1648
|
+
}
|
|
1649
|
+
if (e.method === "GET") {
|
|
1650
|
+
if (!i) throw new n(`Publication '${r}' not found.`);
|
|
1651
|
+
return h({ publication: i });
|
|
1652
|
+
}
|
|
1653
|
+
if (e.method === "PUT") {
|
|
1654
|
+
let n = en({
|
|
1655
|
+
...await L(e),
|
|
1656
|
+
pubId: r
|
|
1657
|
+
}, u, i ?? void 0);
|
|
1658
|
+
return await P(t, n), h({ publication: n }, { status: i ? 200 : 201 });
|
|
1659
|
+
}
|
|
1660
|
+
if (e.method === "DELETE") return await $t(t, u, r), h({ ok: !0 });
|
|
1661
|
+
}
|
|
1662
|
+
return o(404, "not_found", "API route not found.");
|
|
1663
|
+
}
|
|
1664
|
+
async function In(e, t) {
|
|
1665
|
+
let n = await L(e), r = v(n, "tenantId") ?? null, i = sn(v(n, "path")), a = _(n.pubRef) ? n.pubRef : void 0, o = {
|
|
1666
|
+
providerId: A(a && v(a, "providerId"), "pubRef.providerId"),
|
|
1667
|
+
pubId: A(a && v(a, "pubId"), "pubRef.pubId"),
|
|
1668
|
+
version: a ? v(a, "version") : void 0
|
|
1669
|
+
};
|
|
1670
|
+
if (!await j(t, o.providerId)) throw new s(`pubRef.providerId '${o.providerId}' does not exist.`);
|
|
1671
|
+
if (!await N(t, o.providerId, o.pubId)) throw new s(`pubRef.pubId '${o.pubId}' does not exist under '${o.providerId}'.`);
|
|
1672
|
+
let c = v(n, "id"), l = c ? await nn(t, r, c) : null, u = i.join("/"), d = {
|
|
1673
|
+
id: l?.id ?? c ?? cn(),
|
|
1674
|
+
tenantId: r,
|
|
1675
|
+
path: u,
|
|
1676
|
+
pubRef: o,
|
|
1677
|
+
enabled: n.enabled !== !1,
|
|
1678
|
+
createdAt: l?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1679
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1680
|
+
}, f = n.dryRun === !0 || new URL(e.url).searchParams.get("dryRun") === "true", p = l ? l.path === u ? "update" : "move" : "create", m = l && l.path !== u ? [l.path, u] : [u];
|
|
1681
|
+
return f ? h({
|
|
1682
|
+
dryRun: !0,
|
|
1683
|
+
action: p,
|
|
1684
|
+
placement: d,
|
|
1685
|
+
affected: await Rn(t, r, m)
|
|
1686
|
+
}) : (await rn(t, d), h({
|
|
1687
|
+
placement: d,
|
|
1688
|
+
action: p
|
|
1689
|
+
}, { status: l ? 200 : 201 }));
|
|
1690
|
+
}
|
|
1691
|
+
async function Ln(e, t, r, i) {
|
|
1692
|
+
let a = i.searchParams.get("tenant"), o = !a || a === "_global" ? null : a, s = await nn(t, o, r);
|
|
1693
|
+
if (!s) throw new n(`Placement '${r}' not found in scope '${D(o)}'.`);
|
|
1694
|
+
return i.searchParams.get("dryRun") === "true" ? h({
|
|
1695
|
+
dryRun: !0,
|
|
1696
|
+
action: "delete",
|
|
1697
|
+
placement: s,
|
|
1698
|
+
affected: await Rn(t, o, [s.path])
|
|
1699
|
+
}) : (await an(t, o, r), h({ ok: !0 }));
|
|
1700
|
+
}
|
|
1701
|
+
async function Rn(e, t, n) {
|
|
1702
|
+
return t === null ? {
|
|
1703
|
+
tenantId: D(t),
|
|
1704
|
+
paths: n,
|
|
1705
|
+
grants: [],
|
|
1706
|
+
note: "Global tree: affects every non-tenant caller of /htbp."
|
|
1707
|
+
} : {
|
|
1708
|
+
tenantId: t,
|
|
1709
|
+
paths: n,
|
|
1710
|
+
grants: (await Sn(e)).filter((e) => e.record.tenantId === t).map((e) => ({
|
|
1711
|
+
keyHash: e.hash.slice(0, 12),
|
|
1712
|
+
principal: e.record.principal ?? "agent",
|
|
1713
|
+
label: e.record.label
|
|
1714
|
+
}))
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
//#endregion
|
|
1718
|
+
//#region src/worker/tb/device.ts
|
|
1719
|
+
var zn = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/, Bn = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/, Vn = [
|
|
1720
|
+
"exec.run",
|
|
1721
|
+
"fs.read",
|
|
1722
|
+
"logs.tail"
|
|
1723
|
+
], Hn = 3e4, z = 1e6, Un = /* @__PURE__ */ new Set([
|
|
1724
|
+
"rm",
|
|
1725
|
+
"mkfs",
|
|
1726
|
+
"dd",
|
|
1727
|
+
"shutdown",
|
|
1728
|
+
"reboot",
|
|
1729
|
+
"halt",
|
|
1730
|
+
"sudo",
|
|
1731
|
+
"su"
|
|
1732
|
+
]), Wn = [
|
|
1733
|
+
/\brm\s+-[^\s]*r[^\s]*f\b.*(?:\s\/|\s\*)/,
|
|
1734
|
+
/:\s*\(\s*\)\s*\{/,
|
|
1735
|
+
/>\s*\/dev\/sd[a-z]/,
|
|
1736
|
+
/\bcurl\b.*\|\s*(?:sh|bash)\b/,
|
|
1737
|
+
/\bwget\b.*\|\s*(?:sh|bash)\b/
|
|
1738
|
+
];
|
|
1739
|
+
function B(e) {
|
|
1740
|
+
if (!e.TENANTS) throw new s("Tunnel endpoints require the TENANTS KV binding.");
|
|
1741
|
+
return e.TENANTS;
|
|
1742
|
+
}
|
|
1743
|
+
function Gn(e) {
|
|
1744
|
+
return !!e.TENANTS;
|
|
1745
|
+
}
|
|
1746
|
+
async function V(e, t) {
|
|
1747
|
+
return await B(e).get(`endpoint:${t}`, "json");
|
|
1748
|
+
}
|
|
1749
|
+
async function H(e, t) {
|
|
1750
|
+
await B(e).put(`endpoint:${t.id}`, JSON.stringify(t));
|
|
1751
|
+
}
|
|
1752
|
+
async function Kn(e) {
|
|
1753
|
+
let t = [], n;
|
|
1754
|
+
do {
|
|
1755
|
+
let r = await B(e).list({
|
|
1756
|
+
prefix: "endpoint:",
|
|
1757
|
+
cursor: n
|
|
1758
|
+
});
|
|
1759
|
+
for (let n of r.keys) {
|
|
1760
|
+
let r = await B(e).get(n.name, "json");
|
|
1761
|
+
r && t.push(r);
|
|
1762
|
+
}
|
|
1763
|
+
n = r.list_complete ? void 0 : r.cursor;
|
|
1764
|
+
} while (n);
|
|
1765
|
+
return t;
|
|
1766
|
+
}
|
|
1767
|
+
async function qn(e, t) {
|
|
1768
|
+
return await B(e).get(`command-policy:${t}`, "json");
|
|
1769
|
+
}
|
|
1770
|
+
async function Jn(e, t) {
|
|
1771
|
+
await B(e).put(`command-policy:${t.id}`, JSON.stringify(t));
|
|
1772
|
+
}
|
|
1773
|
+
async function Yn(e) {
|
|
1774
|
+
let t = [], n;
|
|
1775
|
+
do {
|
|
1776
|
+
let r = await B(e).list({
|
|
1777
|
+
prefix: "command-policy:",
|
|
1778
|
+
cursor: n
|
|
1779
|
+
});
|
|
1780
|
+
for (let n of r.keys) {
|
|
1781
|
+
let r = await B(e).get(n.name, "json");
|
|
1782
|
+
r && t.push(r);
|
|
1783
|
+
}
|
|
1784
|
+
n = r.list_complete ? void 0 : r.cursor;
|
|
1785
|
+
} while (n);
|
|
1786
|
+
return t;
|
|
1787
|
+
}
|
|
1788
|
+
async function Xn(e, t, r) {
|
|
1789
|
+
let i = new URL(e.url).pathname;
|
|
1790
|
+
if (!i.startsWith("/api/endpoints") && !i.startsWith("/api/command-policies")) return;
|
|
1791
|
+
if (!Gn(t)) return o(501, "not_supported", "Tunnel endpoints require the TENANTS KV binding.");
|
|
1792
|
+
if (sr(r, "Managing tunnel endpoints"), i === "/api/endpoints") {
|
|
1793
|
+
if (e.method === "GET") return h({ endpoints: await Kn(t) });
|
|
1794
|
+
if (e.method === "POST") {
|
|
1795
|
+
let n = cr(await U(e));
|
|
1796
|
+
return await H(t, n), h({ endpoint: n }, { status: 201 });
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
let a = /^\/api\/endpoints\/([^/]+)$/.exec(i);
|
|
1800
|
+
if (a) {
|
|
1801
|
+
let r = decodeURIComponent(a[1] ?? ""), i = await V(t, r);
|
|
1802
|
+
if (!i) throw new n(`Endpoint '${r}' not found.`);
|
|
1803
|
+
if (e.method === "GET") return h({ endpoint: i });
|
|
1804
|
+
if (e.method === "PUT") {
|
|
1805
|
+
let n = cr(await U(e), i);
|
|
1806
|
+
return await H(t, n), h({ endpoint: n });
|
|
1807
|
+
}
|
|
1808
|
+
if (e.method === "DELETE") {
|
|
1809
|
+
let e = {
|
|
1810
|
+
...i,
|
|
1811
|
+
status: "revoked",
|
|
1812
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1813
|
+
};
|
|
1814
|
+
return await H(t, e), h({ endpoint: e });
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
if (i === "/api/command-policies") {
|
|
1818
|
+
if (e.method === "GET") return h({ policies: await Yn(t) });
|
|
1819
|
+
if (e.method === "POST") {
|
|
1820
|
+
let n = lr(await U(e));
|
|
1821
|
+
return await Jn(t, n), h({ policy: n }, { status: 201 });
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
let s = /^\/api\/command-policies\/([^/]+)$/.exec(i);
|
|
1825
|
+
if (s) {
|
|
1826
|
+
let r = decodeURIComponent(s[1] ?? ""), i = await qn(t, r);
|
|
1827
|
+
if (!i) throw new n(`Command policy '${r}' not found.`);
|
|
1828
|
+
if (e.method === "GET") return h({ policy: i });
|
|
1829
|
+
if (e.method === "PUT") {
|
|
1830
|
+
let n = lr(await U(e), i);
|
|
1831
|
+
return await Jn(t, n), h({ policy: n });
|
|
1832
|
+
}
|
|
1833
|
+
if (e.method === "DELETE") return await B(t).delete(`command-policy:${r}`), h({ ok: !0 });
|
|
1834
|
+
}
|
|
1835
|
+
return o(404, "not_found", "API route not found.");
|
|
1836
|
+
}
|
|
1837
|
+
async function Zn(e, t, n) {
|
|
1838
|
+
let r = new URL(e.url);
|
|
1839
|
+
if (r.pathname.startsWith("/tunnel/")) {
|
|
1840
|
+
if (!Gn(t)) return o(501, "not_supported", "Tunnel endpoints require the TENANTS KV binding.");
|
|
1841
|
+
if (e.method !== "POST") return o(405, "method_not_allowed", "Use POST for tunnel control-plane calls.");
|
|
1842
|
+
if (r.pathname === "/tunnel/connect") {
|
|
1843
|
+
let r = await fr(e, t), i = await n?.connect?.(r), a = (/* @__PURE__ */ new Date()).toISOString(), o = i?.sessionId ?? crypto.randomUUID();
|
|
1844
|
+
return await H(t, {
|
|
1845
|
+
...r,
|
|
1846
|
+
status: "online",
|
|
1847
|
+
sessionId: o,
|
|
1848
|
+
lastSeenAt: a,
|
|
1849
|
+
updatedAt: a
|
|
1850
|
+
}), h({
|
|
1851
|
+
ok: !0,
|
|
1852
|
+
endpointId: r.id,
|
|
1853
|
+
sessionId: o,
|
|
1854
|
+
capabilities: r.capabilities
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
if (r.pathname === "/tunnel/heartbeat") {
|
|
1858
|
+
let r = await pr(t, await U(e));
|
|
1859
|
+
await n?.heartbeat?.(r);
|
|
1860
|
+
let i = (/* @__PURE__ */ new Date()).toISOString();
|
|
1861
|
+
return await H(t, {
|
|
1862
|
+
...r,
|
|
1863
|
+
lastSeenAt: i,
|
|
1864
|
+
updatedAt: i
|
|
1865
|
+
}), h({
|
|
1866
|
+
ok: !0,
|
|
1867
|
+
endpointId: r.id
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
if (r.pathname === "/tunnel/capabilities") {
|
|
1871
|
+
let n = await U(e), r = await pr(t, n), i = ur(n.capabilities, r.capabilities);
|
|
1872
|
+
if (i.some((e) => !r.capabilities.includes(e))) throw new a("Endpoint cannot report capabilities outside its registered maximum.");
|
|
1873
|
+
let o = (/* @__PURE__ */ new Date()).toISOString();
|
|
1874
|
+
return await H(t, {
|
|
1875
|
+
...r,
|
|
1876
|
+
activeCapabilities: i,
|
|
1877
|
+
lastSeenAt: o,
|
|
1878
|
+
updatedAt: o
|
|
1879
|
+
}), h({
|
|
1880
|
+
ok: !0,
|
|
1881
|
+
endpointId: r.id,
|
|
1882
|
+
capabilities: i
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
return o(404, "not_found", "Tunnel route not found.");
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
async function Qn(e) {
|
|
1889
|
+
let { env: t, principal: i, segments: o, isHelp: c, input: l, traceId: u, broker: d } = e;
|
|
1890
|
+
if (o[0] !== "~device") throw new n("Device route not found.");
|
|
1891
|
+
if (o.length === 1) {
|
|
1892
|
+
if (!c) throw new s("Device root is describe-only; select /~device/{id}/{tool}.");
|
|
1893
|
+
return {
|
|
1894
|
+
response: h(await $n(t, i), { headers: { "Cache-Control": "private, max-age=30" } }),
|
|
1895
|
+
audit: {}
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
let f = o[1], p = await V(t, f);
|
|
1899
|
+
if (!p || p.status === "revoked") throw new n(`Endpoint '${f}' not found.`);
|
|
1900
|
+
if (mr(i, p), o.length > 3) throw new n(`Device path '/${o.join("/")}' not found.`);
|
|
1901
|
+
let m = o[2], g = m ?? yr(l), _ = {
|
|
1902
|
+
tool: g,
|
|
1903
|
+
provider: p.providerId,
|
|
1904
|
+
effect: ar(g),
|
|
1905
|
+
scope: or(g)
|
|
1906
|
+
};
|
|
1907
|
+
if (c) return {
|
|
1908
|
+
response: h(m ? tr(p, m) : er(p), { headers: { "Cache-Control": "private, max-age=30" } }),
|
|
1909
|
+
audit: _,
|
|
1910
|
+
tenantId: p.tenantId
|
|
1911
|
+
};
|
|
1912
|
+
if (!g) throw new s("Device calls require a tool path or a body.tool field.");
|
|
1913
|
+
if (g === "shell.run") throw new a("shell.run is not exposed by default.");
|
|
1914
|
+
if (!Vn.includes(g)) throw new n(`Device tool '${g}' is not exposed.`);
|
|
1915
|
+
if (!rr(p).includes(g)) throw new n(`Device endpoint '${p.id}' does not expose '${g}'.`);
|
|
1916
|
+
if (p.status !== "online" || !p.sessionId || !d) throw new r(`Endpoint '${p.id}' is offline.`);
|
|
1917
|
+
let v = await hr(t, p, g, l);
|
|
1918
|
+
p.id;
|
|
1919
|
+
let y = await d.dispatch(p, {
|
|
1920
|
+
endpointId: p.id,
|
|
1921
|
+
sessionId: p.sessionId,
|
|
1922
|
+
tool: g,
|
|
1923
|
+
traceId: u,
|
|
1924
|
+
input: v.input,
|
|
1925
|
+
deadlineMs: v.deadlineMs,
|
|
1926
|
+
maxOutputBytes: v.maxOutputBytes
|
|
1927
|
+
});
|
|
1928
|
+
return {
|
|
1929
|
+
response: h({
|
|
1930
|
+
resource: `/htbp/${o.join("/")}`,
|
|
1931
|
+
result: y
|
|
1932
|
+
}),
|
|
1933
|
+
audit: _,
|
|
1934
|
+
tenantId: p.tenantId
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
async function $n(e, t) {
|
|
1938
|
+
return {
|
|
1939
|
+
htbp: "draft",
|
|
1940
|
+
kind: "directory",
|
|
1941
|
+
title: "Devices",
|
|
1942
|
+
cachable: !1,
|
|
1943
|
+
resources: (await Kn(e)).filter((e) => e.status !== "revoked" && (t.isAdmin || !e.tenantId || e.tenantId === t.tenantId)).map((e) => ({
|
|
1944
|
+
name: e.id,
|
|
1945
|
+
path: `./${encodeURIComponent(e.id)}`,
|
|
1946
|
+
description: e.label
|
|
1947
|
+
}))
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function er(e) {
|
|
1951
|
+
return {
|
|
1952
|
+
htbp: "draft",
|
|
1953
|
+
kind: "builtin",
|
|
1954
|
+
title: e.label ?? e.id,
|
|
1955
|
+
description: `${e.kind} endpoint (${e.status})`,
|
|
1956
|
+
cachable: !1,
|
|
1957
|
+
resources: rr(e).map(nr)
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
function tr(e, t) {
|
|
1961
|
+
if (!rr(e).includes(t)) throw new n(`Device endpoint '${e.id}' does not expose '${t}'.`);
|
|
1962
|
+
return {
|
|
1963
|
+
htbp: "draft",
|
|
1964
|
+
kind: "builtin",
|
|
1965
|
+
title: t,
|
|
1966
|
+
cachable: !1,
|
|
1967
|
+
endpoint: {
|
|
1968
|
+
method: "POST",
|
|
1969
|
+
inputSchema: ir(t),
|
|
1970
|
+
outputSchema: { type: "object" },
|
|
1971
|
+
effect: ar(t),
|
|
1972
|
+
scope: or(t)
|
|
1973
|
+
}
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
function nr(e) {
|
|
1977
|
+
return {
|
|
1978
|
+
name: e,
|
|
1979
|
+
path: `./${encodeURIComponent(e)}`,
|
|
1980
|
+
description: e === "exec.run" ? "Run a structured argv command" : e === "fs.read" ? "Read a file from the endpoint" : "Tail endpoint logs"
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
function rr(e) {
|
|
1984
|
+
return e.activeCapabilities ?? e.capabilities;
|
|
1985
|
+
}
|
|
1986
|
+
function ir(e) {
|
|
1987
|
+
return e === "exec.run" ? {
|
|
1988
|
+
type: "object",
|
|
1989
|
+
required: ["argv"],
|
|
1990
|
+
properties: {
|
|
1991
|
+
argv: {
|
|
1992
|
+
type: "array",
|
|
1993
|
+
items: { type: "string" },
|
|
1994
|
+
minItems: 1
|
|
1995
|
+
},
|
|
1996
|
+
cwd: { type: "string" },
|
|
1997
|
+
timeoutMs: { type: "number" },
|
|
1998
|
+
maxOutputBytes: { type: "number" }
|
|
1999
|
+
}
|
|
2000
|
+
} : e === "fs.read" ? {
|
|
2001
|
+
type: "object",
|
|
2002
|
+
required: ["path"],
|
|
2003
|
+
properties: {
|
|
2004
|
+
path: { type: "string" },
|
|
2005
|
+
maxBytes: { type: "number" }
|
|
2006
|
+
}
|
|
2007
|
+
} : {
|
|
2008
|
+
type: "object",
|
|
2009
|
+
properties: {
|
|
2010
|
+
stream: { type: "string" },
|
|
2011
|
+
lines: { type: "number" }
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function ar(e) {
|
|
2016
|
+
if (e) return e === "exec.run" ? "destructive" : "read";
|
|
2017
|
+
}
|
|
2018
|
+
function or(e) {
|
|
2019
|
+
if (e) return e === "exec.run" ? "device:exec" : e === "fs.read" ? "device:fs.read" : "device:logs.tail";
|
|
2020
|
+
}
|
|
2021
|
+
function sr(e, t) {
|
|
2022
|
+
if (!e.isAdmin) throw new a(`${t} requires an admin key.`);
|
|
2023
|
+
}
|
|
2024
|
+
async function U(e) {
|
|
2025
|
+
let t = await e.json().catch(() => void 0);
|
|
2026
|
+
if (!_(t)) throw new s("Request body must be a JSON object.");
|
|
2027
|
+
return t;
|
|
2028
|
+
}
|
|
2029
|
+
function cr(e, t) {
|
|
2030
|
+
let n = v(e, "id") ?? t?.id;
|
|
2031
|
+
if (!n || !zn.test(n)) throw new s(`Endpoint id must match ${zn}.`);
|
|
2032
|
+
let r = (/* @__PURE__ */ new Date()).toISOString(), i = ur(e.capabilities, t?.capabilities ?? ["exec.run"]), a = dr(v(e, "kind") ?? t?.kind ?? "generic"), o = v(e, "status") ?? t?.status ?? "offline", c = o === "online" || o === "offline" || o === "revoked" ? o : t?.status ?? "offline";
|
|
2033
|
+
return {
|
|
2034
|
+
id: n,
|
|
2035
|
+
tenantId: v(e, "tenantId") ?? t?.tenantId,
|
|
2036
|
+
providerId: v(e, "providerId") ?? t?.providerId,
|
|
2037
|
+
kind: a,
|
|
2038
|
+
label: v(e, "label") ?? t?.label,
|
|
2039
|
+
capabilities: i,
|
|
2040
|
+
activeCapabilities: t?.activeCapabilities,
|
|
2041
|
+
status: c,
|
|
2042
|
+
commandPolicyId: v(e, "commandPolicyId") ?? t?.commandPolicyId,
|
|
2043
|
+
sessionId: t?.sessionId,
|
|
2044
|
+
lastSeenAt: t?.lastSeenAt,
|
|
2045
|
+
createdAt: t?.createdAt ?? r,
|
|
2046
|
+
updatedAt: r
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
function lr(e, t) {
|
|
2050
|
+
let n = v(e, "id") ?? t?.id;
|
|
2051
|
+
if (!n || !Bn.test(n)) throw new s(`Command policy id must match ${Bn}.`);
|
|
2052
|
+
let r = (/* @__PURE__ */ new Date()).toISOString();
|
|
2053
|
+
return {
|
|
2054
|
+
id: n,
|
|
2055
|
+
defaultMode: e.defaultMode === "deny" || e.defaultMode === "allow" ? e.defaultMode : t?.defaultMode ?? "allow",
|
|
2056
|
+
allowCommands: b(e.allowCommands) ?? t?.allowCommands,
|
|
2057
|
+
denyCommands: b(e.denyCommands) ?? t?.denyCommands,
|
|
2058
|
+
denyPatterns: b(e.denyPatterns) ?? t?.denyPatterns,
|
|
2059
|
+
allowShell: typeof e.allowShell == "boolean" ? e.allowShell : t?.allowShell,
|
|
2060
|
+
allowedCwdPrefixes: b(e.allowedCwdPrefixes) ?? t?.allowedCwdPrefixes,
|
|
2061
|
+
maxTimeoutMs: W(e.maxTimeoutMs) ?? t?.maxTimeoutMs,
|
|
2062
|
+
maxOutputBytes: W(e.maxOutputBytes) ?? t?.maxOutputBytes,
|
|
2063
|
+
requireConfirmFor: b(e.requireConfirmFor) ?? t?.requireConfirmFor,
|
|
2064
|
+
envAllowlist: b(e.envAllowlist) ?? t?.envAllowlist,
|
|
2065
|
+
createdAt: t?.createdAt ?? r,
|
|
2066
|
+
updatedAt: r
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function ur(e, t) {
|
|
2070
|
+
let n = (Array.isArray(e) ? e : t).filter((e) => typeof e == "string" && Vn.includes(e));
|
|
2071
|
+
if (n.length === 0) throw new s("Endpoint capabilities must include at least one supported device tool.");
|
|
2072
|
+
return [...new Set(n)];
|
|
2073
|
+
}
|
|
2074
|
+
function dr(e) {
|
|
2075
|
+
if ([
|
|
2076
|
+
"sandbox",
|
|
2077
|
+
"k8s-pod",
|
|
2078
|
+
"pc",
|
|
2079
|
+
"browser-host",
|
|
2080
|
+
"mobile",
|
|
2081
|
+
"generic"
|
|
2082
|
+
].includes(e)) return e;
|
|
2083
|
+
throw new s(`Unsupported endpoint kind '${e}'.`);
|
|
2084
|
+
}
|
|
2085
|
+
function W(e) {
|
|
2086
|
+
return typeof e == "number" && Number.isFinite(e) && e > 0 ? e : void 0;
|
|
2087
|
+
}
|
|
2088
|
+
async function fr(e, t) {
|
|
2089
|
+
let r = v(await U(e), "endpointId") ?? e.headers.get("X-TB-Endpoint-Id") ?? void 0;
|
|
2090
|
+
if (!r) throw new s("endpointId is required.");
|
|
2091
|
+
let i = await V(t, r);
|
|
2092
|
+
if (!i) throw new n(`Endpoint '${r}' not found.`);
|
|
2093
|
+
if (i.status === "revoked") throw new a(`Endpoint '${r}' is revoked.`);
|
|
2094
|
+
return i;
|
|
2095
|
+
}
|
|
2096
|
+
async function pr(e, t) {
|
|
2097
|
+
let r = v(t, "endpointId"), i = v(t, "sessionId");
|
|
2098
|
+
if (!r || !i) throw new s("endpointId and sessionId are required.");
|
|
2099
|
+
let o = await V(e, r);
|
|
2100
|
+
if (!o || o.status === "revoked") throw new n(`Endpoint '${r}' not found.`);
|
|
2101
|
+
if (o.sessionId !== i) throw new a("Endpoint session is invalid.");
|
|
2102
|
+
return o;
|
|
2103
|
+
}
|
|
2104
|
+
function mr(e, t) {
|
|
2105
|
+
if (!e.isAdmin && !(t.tenantId && e.tenantId === t.tenantId)) throw new n(`Endpoint '${t.id}' not found.`);
|
|
2106
|
+
}
|
|
2107
|
+
async function hr(e, t, n, r) {
|
|
2108
|
+
let i = vr(r);
|
|
2109
|
+
if (n === "exec.run") return gr(e, t, i);
|
|
2110
|
+
if (n === "fs.read") {
|
|
2111
|
+
let e = v(i, "path");
|
|
2112
|
+
if (!e) throw new s("fs.read requires path.");
|
|
2113
|
+
let t = Math.min(W(i.maxBytes) ?? z, z);
|
|
2114
|
+
return {
|
|
2115
|
+
input: {
|
|
2116
|
+
path: e,
|
|
2117
|
+
maxBytes: t
|
|
2118
|
+
},
|
|
2119
|
+
deadlineMs: Hn,
|
|
2120
|
+
maxOutputBytes: t
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
let a = Math.min(Math.max(W(i.lines) ?? 100, 1), 1e3);
|
|
2124
|
+
return {
|
|
2125
|
+
input: {
|
|
2126
|
+
stream: v(i, "stream") ?? "default",
|
|
2127
|
+
lines: a
|
|
2128
|
+
},
|
|
2129
|
+
deadlineMs: Hn,
|
|
2130
|
+
maxOutputBytes: z
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
async function gr(e, t, n) {
|
|
2134
|
+
let r = b(n.argv);
|
|
2135
|
+
if (!r || r.length === 0 || r.some((e) => e.length === 0)) throw new s("exec.run requires non-empty argv: string[].");
|
|
2136
|
+
let i = t.commandPolicyId ? await qn(e, t.commandPolicyId) : null;
|
|
2137
|
+
if (t.commandPolicyId && !i) throw new a(`Command policy '${t.commandPolicyId}' not found.`);
|
|
2138
|
+
_r(r, n, i);
|
|
2139
|
+
let o = i?.maxTimeoutMs ?? Hn, c = i?.maxOutputBytes ?? z, l = Math.min(W(n.timeoutMs) ?? Hn, o), u = Math.min(W(n.maxOutputBytes) ?? z, c), d = {
|
|
2140
|
+
argv: r,
|
|
2141
|
+
timeoutMs: l,
|
|
2142
|
+
maxOutputBytes: u
|
|
2143
|
+
}, f = v(n, "cwd");
|
|
2144
|
+
return f && (d.cwd = f), {
|
|
2145
|
+
input: d,
|
|
2146
|
+
deadlineMs: l,
|
|
2147
|
+
maxOutputBytes: u
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
function _r(e, t, n) {
|
|
2151
|
+
let r = e[0], i = e.join(" ");
|
|
2152
|
+
if (Un.has(r)) throw new a(`Command '${r}' is denied by the global policy.`);
|
|
2153
|
+
if (Wn.some((e) => e.test(i))) throw new a("Command is denied by the global policy.");
|
|
2154
|
+
if (n?.denyCommands?.includes(r)) throw new a(`Command '${r}' is denied by policy '${n.id}'.`);
|
|
2155
|
+
if (n?.denyPatterns?.some((e) => new RegExp(e).test(i))) throw new a(`Command is denied by policy '${n.id}'.`);
|
|
2156
|
+
if (n?.allowCommands && !n.allowCommands.includes(r) || n?.defaultMode === "deny" && !n.allowCommands?.includes(r)) throw new a(`Command '${r}' is not allowed by policy '${n.id}'.`);
|
|
2157
|
+
let o = v(t, "cwd");
|
|
2158
|
+
if (n?.allowedCwdPrefixes && n.allowedCwdPrefixes.length > 0) {
|
|
2159
|
+
if (!o) throw new a(`cwd is required by policy '${n.id}'.`);
|
|
2160
|
+
if (!n.allowedCwdPrefixes.some((e) => o.startsWith(e))) throw new a(`cwd '${o}' is outside policy '${n.id}'.`);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
function vr(e) {
|
|
2164
|
+
return _(e) && _(e.arguments) ? e.arguments : _(e) ? e : {};
|
|
2165
|
+
}
|
|
2166
|
+
function yr(e) {
|
|
2167
|
+
if (!_(e)) return;
|
|
2168
|
+
let t = v(e, "tool");
|
|
2169
|
+
return t && Vn.includes(t) ? t : void 0;
|
|
2170
|
+
}
|
|
2171
|
+
//#endregion
|
|
2172
|
+
//#region src/worker/tb/help.ts
|
|
2173
|
+
function br(e, t, n) {
|
|
2174
|
+
let r = [
|
|
2175
|
+
"htbp draft",
|
|
2176
|
+
`resource ${t}`,
|
|
2177
|
+
`title ${x(e.title)}`
|
|
2178
|
+
];
|
|
2179
|
+
if (e.description && r.push(`summary ${x(e.description)}`), r.push("skill ./~skill", `auth ${n}`, ""), e.endpoint) {
|
|
2180
|
+
let { method: i, tools: a } = e.endpoint;
|
|
2181
|
+
if (a && a.length > 0) for (let e of a) r.push(`cmd ${e.name} ${i} ${t}`), r.push(` body application/json {"tool":"${e.name}","arguments":object?}`), r.push(` auth ${n}`), xr(r, e), r.push(" returns 200 application/json"), e.description && r.push(` note ${x(e.description)}`);
|
|
2182
|
+
else r.push(`cmd call ${i} ${t}`), r.push(" body application/json {\"arguments\":object?}"), r.push(` auth ${n}`), xr(r, e.endpoint), r.push(" returns 200 application/json");
|
|
2183
|
+
}
|
|
2184
|
+
for (let n of e.resources ?? []) r.push(`link child ${Sr(t, n.path)}/~help`), n.description && r.push(` note ${x(n.description)}`);
|
|
2185
|
+
return `${r.join("\n").trimEnd()}\n`;
|
|
2186
|
+
}
|
|
2187
|
+
function xr(e, t) {
|
|
2188
|
+
e.push(` effect ${t.effect ?? "external"}`), t.scope && e.push(` scope ${x(t.scope)}`), t.confirm && e.push(" confirm true");
|
|
2189
|
+
}
|
|
2190
|
+
function Sr(e, t) {
|
|
2191
|
+
return t.startsWith("./") ? `${e}/${t.slice(2)}` : t.startsWith("../") ? `${e.split("/").slice(0, -1).join("/")}/${t.slice(3)}` : `${e}/${t}`;
|
|
2192
|
+
}
|
|
2193
|
+
//#endregion
|
|
2194
|
+
//#region src/worker/tb/resolve.ts
|
|
2195
|
+
async function Cr(e, t, r, i, a) {
|
|
2196
|
+
let o = E(t, r);
|
|
2197
|
+
if (!o) throw new n(`No TB resource at '/${r.join("/")}'.`);
|
|
2198
|
+
let s = r.length > 0 ? `${w}/${r.join("/")}` : w, c = {
|
|
2199
|
+
env: e,
|
|
2200
|
+
authMode: i,
|
|
2201
|
+
basePath: _e(o.node),
|
|
2202
|
+
builtinHandlers: a
|
|
2203
|
+
};
|
|
2204
|
+
return {
|
|
2205
|
+
payload: await Et(o.node).describe(o.node, c, o.sub),
|
|
2206
|
+
resourcePath: s
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
async function wr(e, t, n, r, i, a) {
|
|
2210
|
+
let { payload: o, resourcePath: s } = await Cr(e, t, n, r, a);
|
|
2211
|
+
return Er(i) ? g(br(o, s, r === "none" ? "none" : "bearer"), { headers: {
|
|
2212
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
2213
|
+
"Cache-Control": Dr(o, r)
|
|
2214
|
+
} }) : h(o, { headers: { "Cache-Control": Dr(o, r) } });
|
|
2215
|
+
}
|
|
2216
|
+
async function Tr(e, t, r, i, a, o) {
|
|
2217
|
+
let s = E(t, r);
|
|
2218
|
+
if (!s) throw new n(`No TB resource at '/${r.join("/")}'.`);
|
|
2219
|
+
let c = r.length > 0 ? `${w}/${r.join("/")}` : w, l = {
|
|
2220
|
+
env: e,
|
|
2221
|
+
authMode: i,
|
|
2222
|
+
basePath: _e(s.node),
|
|
2223
|
+
builtinHandlers: o
|
|
2224
|
+
};
|
|
2225
|
+
return h({
|
|
2226
|
+
resource: c,
|
|
2227
|
+
result: await Et(s.node).call(s.node, l, s.sub, a)
|
|
2228
|
+
});
|
|
2229
|
+
}
|
|
2230
|
+
function Er(e) {
|
|
2231
|
+
let t = e.toLowerCase();
|
|
2232
|
+
return t.includes("application/json") ? !1 : t.includes("text/plain") || t.includes("text/markdown");
|
|
2233
|
+
}
|
|
2234
|
+
function Dr(e, t) {
|
|
2235
|
+
let n = t === "none" ? "public" : "private";
|
|
2236
|
+
return e.cachable === !1 ? "no-store" : `${n}, max-age=300`;
|
|
2237
|
+
}
|
|
2238
|
+
//#endregion
|
|
2239
|
+
//#region src/worker/tb/dynamic-servers.ts
|
|
2240
|
+
var Or = "dynamic-server:", G = "dynamic";
|
|
2241
|
+
function kr(e) {
|
|
2242
|
+
return !!e.TENANTS;
|
|
2243
|
+
}
|
|
2244
|
+
async function Ar(e) {
|
|
2245
|
+
if (!e.TENANTS) return [];
|
|
2246
|
+
let t = await Pr(e), n = new Set(t.map((e) => e.id)), r = (await Fr(e)).filter((e) => !n.has(e.id));
|
|
2247
|
+
return [...t, ...r];
|
|
2248
|
+
}
|
|
2249
|
+
async function jr(e, t) {
|
|
2250
|
+
if (!e.TENANTS) throw Error("Dynamic servers require the TENANTS KV binding.");
|
|
2251
|
+
let n = (/* @__PURE__ */ new Date()).toISOString();
|
|
2252
|
+
await j(e, "dynamic") || await M(e, {
|
|
2253
|
+
id: G,
|
|
2254
|
+
displayName: "Dynamic Servers (compat)",
|
|
2255
|
+
trustTier: "community",
|
|
2256
|
+
status: "active",
|
|
2257
|
+
createdAt: n,
|
|
2258
|
+
updatedAt: n
|
|
2259
|
+
});
|
|
2260
|
+
let r = await N(e, G, t.id);
|
|
2261
|
+
await P(e, {
|
|
2262
|
+
providerId: G,
|
|
2263
|
+
pubId: t.id,
|
|
2264
|
+
version: r?.version ?? "0.0.0",
|
|
2265
|
+
binding: {
|
|
2266
|
+
type: "mcp",
|
|
2267
|
+
name: t.name,
|
|
2268
|
+
endpoint: t.endpoint,
|
|
2269
|
+
description: t.description
|
|
2270
|
+
},
|
|
2271
|
+
status: "published",
|
|
2272
|
+
createdAt: r?.createdAt ?? n,
|
|
2273
|
+
updatedAt: n
|
|
2274
|
+
}), await rn(e, {
|
|
2275
|
+
id: Nr(t.id),
|
|
2276
|
+
tenantId: null,
|
|
2277
|
+
path: t.id,
|
|
2278
|
+
pubRef: {
|
|
2279
|
+
providerId: G,
|
|
2280
|
+
pubId: t.id
|
|
2281
|
+
},
|
|
2282
|
+
enabled: !0,
|
|
2283
|
+
createdAt: n,
|
|
2284
|
+
updatedAt: n
|
|
2285
|
+
}), await e.TENANTS.delete(`${Or}${t.id}`);
|
|
2286
|
+
}
|
|
2287
|
+
async function Mr(e, t) {
|
|
2288
|
+
if (!e.TENANTS) throw Error("Dynamic servers require the TENANTS KV binding.");
|
|
2289
|
+
await e.TENANTS.delete(`${Or}${t}`), await $t(e, G, t), await an(e, null, Nr(t));
|
|
2290
|
+
}
|
|
2291
|
+
function Nr(e) {
|
|
2292
|
+
return `dyn_${e}`;
|
|
2293
|
+
}
|
|
2294
|
+
async function Pr(e) {
|
|
2295
|
+
let t = await F(e, G), n = [];
|
|
2296
|
+
for (let e of t) {
|
|
2297
|
+
let t = _(e.binding) ? e.binding : {}, r = v(t, "endpoint");
|
|
2298
|
+
r && n.push({
|
|
2299
|
+
id: e.pubId,
|
|
2300
|
+
name: v(t, "name") ?? e.pubId,
|
|
2301
|
+
endpoint: r,
|
|
2302
|
+
description: v(t, "description")
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
return n;
|
|
2306
|
+
}
|
|
2307
|
+
async function Fr(e) {
|
|
2308
|
+
let t = e.TENANTS;
|
|
2309
|
+
if (!t) return [];
|
|
2310
|
+
let n = [], r;
|
|
2311
|
+
do {
|
|
2312
|
+
let e = await t.list({
|
|
2313
|
+
prefix: Or,
|
|
2314
|
+
cursor: r
|
|
2315
|
+
});
|
|
2316
|
+
for (let r of e.keys) {
|
|
2317
|
+
let e = await t.get(r.name);
|
|
2318
|
+
if (e) try {
|
|
2319
|
+
n.push(JSON.parse(e));
|
|
2320
|
+
} catch {}
|
|
2321
|
+
}
|
|
2322
|
+
r = e.list_complete ? void 0 : e.cursor;
|
|
2323
|
+
} while (r);
|
|
2324
|
+
return n;
|
|
2325
|
+
}
|
|
2326
|
+
//#endregion
|
|
2327
|
+
//#region src/worker/index.ts
|
|
2328
|
+
var Ir = "2025-11-25", Lr = "tool-bridge", Rr = "draft", zr = 1e6, Br = 4e6;
|
|
2329
|
+
function K(e, t = {}) {
|
|
2330
|
+
let n = new Headers(t.headers);
|
|
2331
|
+
return n.set("Content-Type", "application/json; charset=utf-8"), new Response(JSON.stringify(e, null, 2), {
|
|
2332
|
+
...t,
|
|
2333
|
+
headers: n
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
function Vr(e, t = {}) {
|
|
2337
|
+
let n = new Headers(t.headers);
|
|
2338
|
+
return n.set("Content-Type", n.get("Content-Type") ?? "text/plain; charset=utf-8"), new Response(e, {
|
|
2339
|
+
...t,
|
|
2340
|
+
headers: n
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
function q(e, t, n, r) {
|
|
2344
|
+
return K({ error: {
|
|
2345
|
+
code: t,
|
|
2346
|
+
message: n,
|
|
2347
|
+
details: r
|
|
2348
|
+
} }, { status: e });
|
|
2349
|
+
}
|
|
2350
|
+
function Hr(e) {
|
|
2351
|
+
return e.OAUTH_ISSUER ? "oauth" : e.AUTH_BEARER_TOKEN ? "bearer" : "none";
|
|
2352
|
+
}
|
|
2353
|
+
function Ur(e) {
|
|
2354
|
+
let t = e.headers.get("Authorization");
|
|
2355
|
+
if (t) return /^Bearer\s+(.+)$/i.exec(t)?.[1];
|
|
2356
|
+
}
|
|
2357
|
+
async function Wr(e) {
|
|
2358
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e)));
|
|
2359
|
+
}
|
|
2360
|
+
async function Gr(e, t) {
|
|
2361
|
+
let n = await Wr(e), r = await Wr(t), i = n.length ^ r.length, a = Math.max(n.length, r.length);
|
|
2362
|
+
for (let e = 0; e < a; e += 1) i |= (n[e] ?? 0) ^ (r[e] ?? 0);
|
|
2363
|
+
return i === 0;
|
|
2364
|
+
}
|
|
2365
|
+
async function Kr(e, t) {
|
|
2366
|
+
let n = Hr(t);
|
|
2367
|
+
if (gn(t)) {
|
|
2368
|
+
let r = Ur(e);
|
|
2369
|
+
if (!r) return q(401, "unauthorized", "Secret Key (bearer token) required.");
|
|
2370
|
+
let i = await yn(t, r);
|
|
2371
|
+
return i ? {
|
|
2372
|
+
mode: n,
|
|
2373
|
+
subject: i.label ?? i.providerId ?? i.hostId ?? i.tenantId,
|
|
2374
|
+
tenantId: i.tenantId,
|
|
2375
|
+
root: i.root,
|
|
2376
|
+
principal: i.principal,
|
|
2377
|
+
providerId: i.providerId,
|
|
2378
|
+
hostId: i.hostId,
|
|
2379
|
+
isAdmin: i.principal === "admin"
|
|
2380
|
+
} : q(401, "unauthorized", "Secret Key is invalid.");
|
|
2381
|
+
}
|
|
2382
|
+
if (n === "none") return {
|
|
2383
|
+
mode: n,
|
|
2384
|
+
isAdmin: !t.TENANTS
|
|
2385
|
+
};
|
|
2386
|
+
let r = Ur(e);
|
|
2387
|
+
if (!r) return q(401, "unauthorized", "Bearer token required.");
|
|
2388
|
+
if (n === "bearer") {
|
|
2389
|
+
let e = t.AUTH_BEARER_TOKEN;
|
|
2390
|
+
return e ? await Gr(r, e) ? {
|
|
2391
|
+
mode: n,
|
|
2392
|
+
subject: "static-bearer",
|
|
2393
|
+
isAdmin: !0
|
|
2394
|
+
} : q(401, "unauthorized", "Bearer token is invalid.") : q(500, "auth_misconfigured", "AUTH_BEARER_TOKEN is required for bearer auth.");
|
|
2395
|
+
}
|
|
2396
|
+
try {
|
|
2397
|
+
let e = t.OAUTH_ISSUER;
|
|
2398
|
+
if (!e) return q(500, "auth_misconfigured", "OAUTH_ISSUER is required for OAuth auth.");
|
|
2399
|
+
let i = t.OAUTH_JWKS_URI || await qr(e), a = await l(r, c(new URL(i)), {
|
|
2400
|
+
issuer: e,
|
|
2401
|
+
audience: t.OAUTH_REQUIRED_AUDIENCE || void 0
|
|
2402
|
+
});
|
|
2403
|
+
return {
|
|
2404
|
+
mode: n,
|
|
2405
|
+
subject: typeof a.payload.sub == "string" ? a.payload.sub : void 0,
|
|
2406
|
+
isAdmin: !0
|
|
2407
|
+
};
|
|
2408
|
+
} catch (e) {
|
|
2409
|
+
return q(401, "unauthorized", "OAuth bearer token is invalid.", Ci(e));
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async function qr(e) {
|
|
2413
|
+
let t = e.endsWith("/") ? e.slice(0, -1) : e, n = await fetch(`${t}/.well-known/openid-configuration`, { headers: { Accept: "application/json" } });
|
|
2414
|
+
if (!n.ok) throw Error(`Unable to load OIDC metadata: HTTP ${n.status}`);
|
|
2415
|
+
let r = await n.json();
|
|
2416
|
+
if (typeof r.jwks_uri != "string" || r.jwks_uri.length === 0) throw Error("OIDC metadata does not include jwks_uri.");
|
|
2417
|
+
return r.jwks_uri;
|
|
2418
|
+
}
|
|
2419
|
+
function Jr(e) {
|
|
2420
|
+
return {
|
|
2421
|
+
mode: Hr(e),
|
|
2422
|
+
oauthIssuer: e.OAUTH_ISSUER || void 0,
|
|
2423
|
+
oauthAudience: e.OAUTH_REQUIRED_AUDIENCE || void 0
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
async function J(e) {
|
|
2427
|
+
let t = e.headers.get("Content-Length");
|
|
2428
|
+
if (t && Number(t) > zr) throw Error("Request body is too large.");
|
|
2429
|
+
return await e.json();
|
|
2430
|
+
}
|
|
2431
|
+
function Yr(e) {
|
|
2432
|
+
let t = e.MCP_SERVERS_JSON || "{}", n = JSON.parse(t), r = [];
|
|
2433
|
+
if (Z(n) && (n.type === "directory" || Array.isArray(n.children))) return Xr(n, r), r;
|
|
2434
|
+
if (Array.isArray(n)) {
|
|
2435
|
+
for (let e of n) r.push(Zr(e));
|
|
2436
|
+
return r;
|
|
2437
|
+
}
|
|
2438
|
+
if (Z(n)) for (let [e, t] of Object.entries(n)) r.push(Zr({
|
|
2439
|
+
id: e,
|
|
2440
|
+
...Z(t) ? t : {}
|
|
2441
|
+
}));
|
|
2442
|
+
return r;
|
|
2443
|
+
}
|
|
2444
|
+
function Xr(e, t) {
|
|
2445
|
+
if (Z(e)) {
|
|
2446
|
+
if (e.type === "mcp") {
|
|
2447
|
+
t.push(Zr(e));
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
if (Array.isArray(e.children)) for (let n of e.children) Xr(n, t);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
function Zr(e) {
|
|
2454
|
+
if (!Z(e)) throw Error("MCP server config must be an object.");
|
|
2455
|
+
let t = Q(e, "id") || Q(e, "name"), n = Q(e, "endpoint") || Q(e, "url") || Q(e, "baseUrl");
|
|
2456
|
+
if (!t) throw Error("MCP server config is missing id/name.");
|
|
2457
|
+
if (!n) throw Error(`MCP server '${t}' is missing endpoint.`);
|
|
2458
|
+
let r = xi(e.headers), i = Si(e.allowedTools) ?? Si(e.allowed_tools);
|
|
2459
|
+
return {
|
|
2460
|
+
id: t,
|
|
2461
|
+
name: Q(e, "name") || t,
|
|
2462
|
+
endpoint: n,
|
|
2463
|
+
description: Q(e, "description"),
|
|
2464
|
+
headers: r,
|
|
2465
|
+
allowedTools: i
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function Qr(e) {
|
|
2469
|
+
if (!e.endpoint) throw Error("Ad-hoc server endpoint is required.");
|
|
2470
|
+
let t = { ...e.headers ?? {} };
|
|
2471
|
+
return e.bearerToken && (t.Authorization = `Bearer ${e.bearerToken}`), {
|
|
2472
|
+
id: e.name || "adhoc",
|
|
2473
|
+
name: e.name || "Ad-hoc MCP",
|
|
2474
|
+
endpoint: e.endpoint,
|
|
2475
|
+
headers: t
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
function $r(e) {
|
|
2479
|
+
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "server";
|
|
2480
|
+
}
|
|
2481
|
+
async function ei(e, t) {
|
|
2482
|
+
let n = Yr(e).find((e) => e.id === t || e.name === t);
|
|
2483
|
+
if (n) return Y(e, n);
|
|
2484
|
+
let r = (await Ar(e)).find((e) => e.id === t || e.name === t);
|
|
2485
|
+
if (r) return Y(e, {
|
|
2486
|
+
id: r.id,
|
|
2487
|
+
name: r.name,
|
|
2488
|
+
endpoint: r.endpoint,
|
|
2489
|
+
description: r.description
|
|
2490
|
+
});
|
|
2491
|
+
throw Error(`Unknown MCP server '${t}'.`);
|
|
2492
|
+
}
|
|
2493
|
+
function Y(e, t) {
|
|
2494
|
+
let n = new URL(t.endpoint);
|
|
2495
|
+
if (n.protocol !== "https:" && e.ALLOW_INSECURE_MCP_HTTP !== "true") throw Error(`MCP endpoint for '${t.id}' must use https://.`);
|
|
2496
|
+
return {
|
|
2497
|
+
...t,
|
|
2498
|
+
endpoint: n.toString(),
|
|
2499
|
+
resolvedHeaders: ti(e, t.headers)
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function ti(e, t) {
|
|
2503
|
+
let n = {};
|
|
2504
|
+
for (let [r, i] of Object.entries(t ?? {})) n[r] = ni(e, i);
|
|
2505
|
+
return n;
|
|
2506
|
+
}
|
|
2507
|
+
function ni(e, t) {
|
|
2508
|
+
return t.startsWith("$env:") ? ri(e, t.slice(5)) : t.replace(/\$\{([A-Z0-9_]+)\}/g, (t, n) => ri(e, n));
|
|
2509
|
+
}
|
|
2510
|
+
function ri(e, t) {
|
|
2511
|
+
let n = e[t];
|
|
2512
|
+
if (typeof n != "string" || n.length === 0) throw Error(`Environment variable '${t}' is required for MCP header substitution.`);
|
|
2513
|
+
return n;
|
|
2514
|
+
}
|
|
2515
|
+
async function X(e) {
|
|
2516
|
+
let t = await ai(e, "tools/list", {}), n = (Z(t) && Array.isArray(t.tools) ? t.tools : []).map(gi).filter(Boolean);
|
|
2517
|
+
if (!e.allowedTools || e.allowedTools.length === 0) return n;
|
|
2518
|
+
let r = new Set(e.allowedTools);
|
|
2519
|
+
return n.filter((e) => r.has(e.name));
|
|
2520
|
+
}
|
|
2521
|
+
async function ii(e, t, n) {
|
|
2522
|
+
if (e.allowedTools && e.allowedTools.length > 0 && !e.allowedTools.includes(t)) throw Error(`Tool '${t}' is not allowed for server '${e.id}'.`);
|
|
2523
|
+
return ai(e, "tools/call", {
|
|
2524
|
+
name: t,
|
|
2525
|
+
arguments: Z(n) ? n : {}
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
async function ai(e, t, n) {
|
|
2529
|
+
let r = await oi(e);
|
|
2530
|
+
try {
|
|
2531
|
+
return await si(e, r.sessionId, "notifications/initialized", {}), (await ci(e, r.sessionId, t, n)).result;
|
|
2532
|
+
} finally {
|
|
2533
|
+
r.sessionId && await hi(e, r.sessionId).catch(() => {});
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
async function oi(e) {
|
|
2537
|
+
return { sessionId: (await ci(e, void 0, "initialize", {
|
|
2538
|
+
protocolVersion: Ir,
|
|
2539
|
+
capabilities: {},
|
|
2540
|
+
clientInfo: {
|
|
2541
|
+
name: Lr,
|
|
2542
|
+
version: Rr
|
|
2543
|
+
}
|
|
2544
|
+
})).sessionId };
|
|
2545
|
+
}
|
|
2546
|
+
async function si(e, t, n, r) {
|
|
2547
|
+
let i = await fetch(e.endpoint, {
|
|
2548
|
+
method: "POST",
|
|
2549
|
+
headers: li(e, t),
|
|
2550
|
+
body: JSON.stringify({
|
|
2551
|
+
jsonrpc: "2.0",
|
|
2552
|
+
method: n,
|
|
2553
|
+
params: r
|
|
2554
|
+
})
|
|
2555
|
+
});
|
|
2556
|
+
if (i.status !== 202 && !i.ok) throw Error(`MCP notification '${n}' failed with HTTP ${i.status}.`);
|
|
2557
|
+
}
|
|
2558
|
+
async function ci(e, t, n, r) {
|
|
2559
|
+
let i = crypto.randomUUID(), a = await fetch(e.endpoint, {
|
|
2560
|
+
method: "POST",
|
|
2561
|
+
headers: li(e, t),
|
|
2562
|
+
body: JSON.stringify({
|
|
2563
|
+
jsonrpc: "2.0",
|
|
2564
|
+
id: i,
|
|
2565
|
+
method: n,
|
|
2566
|
+
params: r
|
|
2567
|
+
})
|
|
2568
|
+
});
|
|
2569
|
+
if (!a.ok) throw Error(`MCP request '${n}' failed with HTTP ${a.status}: ${await mi(a)}`);
|
|
2570
|
+
let o = await ui(a, i);
|
|
2571
|
+
if (o.error) throw Error(`MCP error ${o.error.code}: ${o.error.message}`);
|
|
2572
|
+
return {
|
|
2573
|
+
result: o.result,
|
|
2574
|
+
sessionId: a.headers.get("MCP-Session-Id") ?? void 0
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
function li(e, t) {
|
|
2578
|
+
let n = new Headers(e.resolvedHeaders);
|
|
2579
|
+
return n.set("Accept", "application/json, text/event-stream"), n.set("Content-Type", "application/json"), n.set("MCP-Protocol-Version", Ir), t && n.set("MCP-Session-Id", t), n;
|
|
2580
|
+
}
|
|
2581
|
+
async function ui(e, t) {
|
|
2582
|
+
if ((e.headers.get("Content-Type") ?? "").includes("text/event-stream")) return di(e, t);
|
|
2583
|
+
let n = await pi(e, zr), r = JSON.parse(n);
|
|
2584
|
+
if (r.id !== t) throw Error(`Unexpected MCP response id '${String(r.id)}'.`);
|
|
2585
|
+
return r;
|
|
2586
|
+
}
|
|
2587
|
+
async function di(e, t) {
|
|
2588
|
+
if (!e.body) throw Error("MCP server returned an empty SSE response.");
|
|
2589
|
+
let n = e.body.getReader(), r = new TextDecoder(), i = "", a = [], o = 0;
|
|
2590
|
+
for (;;) {
|
|
2591
|
+
let { done: e, value: s } = await n.read();
|
|
2592
|
+
if (e) break;
|
|
2593
|
+
if (o += s.byteLength, o > Br) throw Error("MCP SSE response exceeded the maximum size.");
|
|
2594
|
+
i += r.decode(s, { stream: !0 });
|
|
2595
|
+
let c = i.indexOf("\n");
|
|
2596
|
+
for (; c >= 0;) {
|
|
2597
|
+
let e = i.slice(0, c);
|
|
2598
|
+
i = i.slice(c + 1);
|
|
2599
|
+
let n = e.endsWith("\r") ? e.slice(0, -1) : e;
|
|
2600
|
+
if (n === "") {
|
|
2601
|
+
let e = fi(a);
|
|
2602
|
+
if (a = [], e && e.id === t) return e;
|
|
2603
|
+
} else n.startsWith("data:") && a.push(n.slice(5).trimStart());
|
|
2604
|
+
c = i.indexOf("\n");
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
let s = fi(a);
|
|
2608
|
+
if (s && s.id === t) return s;
|
|
2609
|
+
throw Error(`MCP SSE stream ended before response '${t}' was received.`);
|
|
2610
|
+
}
|
|
2611
|
+
function fi(e) {
|
|
2612
|
+
if (e.length === 0) return;
|
|
2613
|
+
let t = e.join("\n").trim();
|
|
2614
|
+
if (t.startsWith("{")) return JSON.parse(t);
|
|
2615
|
+
}
|
|
2616
|
+
async function pi(e, t) {
|
|
2617
|
+
if (!e.body) return "";
|
|
2618
|
+
let n = e.body.getReader(), r = new TextDecoder(), i = 0, a = "";
|
|
2619
|
+
for (;;) {
|
|
2620
|
+
let { done: e, value: o } = await n.read();
|
|
2621
|
+
if (e) return a + r.decode();
|
|
2622
|
+
if (i += o.byteLength, i > t) throw Error("Response body exceeded the maximum size.");
|
|
2623
|
+
a += r.decode(o, { stream: !0 });
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
async function mi(e) {
|
|
2627
|
+
try {
|
|
2628
|
+
return await pi(e, 8e3);
|
|
2629
|
+
} catch {
|
|
2630
|
+
return "";
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
async function hi(e, t) {
|
|
2634
|
+
await fetch(e.endpoint, {
|
|
2635
|
+
method: "DELETE",
|
|
2636
|
+
headers: li(e, t)
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
function gi(e) {
|
|
2640
|
+
if (!(!Z(e) || typeof e.name != "string")) return {
|
|
2641
|
+
name: e.name,
|
|
2642
|
+
description: typeof e.description == "string" ? e.description : void 0,
|
|
2643
|
+
inputSchema: e.inputSchema,
|
|
2644
|
+
outputSchema: e.outputSchema,
|
|
2645
|
+
annotations: e.annotations
|
|
2646
|
+
};
|
|
2647
|
+
}
|
|
2648
|
+
function _i(e) {
|
|
2649
|
+
return e === "none" ? "none" : "bearer";
|
|
2650
|
+
}
|
|
2651
|
+
function vi(e, t, n, r) {
|
|
2652
|
+
let i = _i(r), a = [
|
|
2653
|
+
"htbp draft",
|
|
2654
|
+
`resource ${n}`,
|
|
2655
|
+
`title ${e.name}`,
|
|
2656
|
+
e.description ? `summary ${e.description}` : `summary MCP Streamable HTTP bridge for ${e.name}.`,
|
|
2657
|
+
"skill ./~skill",
|
|
2658
|
+
`auth ${i}`,
|
|
2659
|
+
""
|
|
2660
|
+
];
|
|
2661
|
+
for (let e of t) a.push(`cmd ${e.name} POST ${n}/tools/${encodeURIComponent(e.name)}`), a.push(" body application/json {\"arguments\":object?}"), a.push(` auth ${i}`), a.push(" effect external"), a.push(" returns 200 application/json"), e.description && a.push(` note ${bi(e.description)}`), a.push("");
|
|
2662
|
+
return `${a.join("\n").trimEnd()}\n`;
|
|
2663
|
+
}
|
|
2664
|
+
function yi(e, t, n, r) {
|
|
2665
|
+
let i = t.map((e) => `- \`${e.name}\`${e.description ? `:${bi(e.description)}` : ""}`).join("\n"), a = r === "none" ? "" : "Authorization: Bearer <token>\n";
|
|
2666
|
+
return `# ${e.name}
|
|
2667
|
+
|
|
2668
|
+
## When To Use
|
|
2669
|
+
|
|
2670
|
+
当需要通过 HTBP 调用 \`${e.name}\` 这个 MCP server 暴露的 tool 时,先读取 \`${n}/~help\`,再选择具体 tool endpoint。
|
|
2671
|
+
|
|
2672
|
+
## Request Construction
|
|
2673
|
+
|
|
2674
|
+
每个 MCP tool 都映射为一个普通 HTTP POST:
|
|
2675
|
+
|
|
2676
|
+
\`\`\`http
|
|
2677
|
+
POST ${n}/tools/{tool}
|
|
2678
|
+
${a}Content-Type: application/json
|
|
2679
|
+
\`\`\`
|
|
2680
|
+
|
|
2681
|
+
请求体:
|
|
2682
|
+
|
|
2683
|
+
\`\`\`json
|
|
2684
|
+
{
|
|
2685
|
+
"arguments": {}
|
|
2686
|
+
}
|
|
2687
|
+
\`\`\`
|
|
2688
|
+
|
|
2689
|
+
## Available Tools
|
|
2690
|
+
|
|
2691
|
+
${i || "当前没有可见 tool。"}
|
|
2692
|
+
|
|
2693
|
+
## Safety
|
|
2694
|
+
|
|
2695
|
+
这些调用会转发到上游 MCP server。执行 write、delete、external side effect 操作前,应先确认用户意图。
|
|
2696
|
+
`;
|
|
2697
|
+
}
|
|
2698
|
+
function bi(e) {
|
|
2699
|
+
return e.replace(/\s+/g, " ").trim();
|
|
2700
|
+
}
|
|
2701
|
+
function Z(e) {
|
|
2702
|
+
return !!e && typeof e == "object" && !Array.isArray(e);
|
|
2703
|
+
}
|
|
2704
|
+
function Q(e, t) {
|
|
2705
|
+
let n = e[t];
|
|
2706
|
+
return typeof n == "string" && n.length > 0 ? n : void 0;
|
|
2707
|
+
}
|
|
2708
|
+
function xi(e) {
|
|
2709
|
+
if (!Z(e)) return;
|
|
2710
|
+
let t = {};
|
|
2711
|
+
for (let [n, r] of Object.entries(e)) typeof r == "string" && (t[n] = r);
|
|
2712
|
+
return t;
|
|
2713
|
+
}
|
|
2714
|
+
function Si(e) {
|
|
2715
|
+
if (Array.isArray(e)) return e.filter((e) => typeof e == "string");
|
|
2716
|
+
}
|
|
2717
|
+
function Ci(e) {
|
|
2718
|
+
return e instanceof Error ? e.message : String(e);
|
|
2719
|
+
}
|
|
2720
|
+
async function wi(e, t) {
|
|
2721
|
+
let n = new URL(e.url).pathname;
|
|
2722
|
+
if (n === "/api/auth/config" && e.method === "GET") return K(Jr(t));
|
|
2723
|
+
let r = await Kr(e, t);
|
|
2724
|
+
if (r instanceof Response) return r;
|
|
2725
|
+
let i = await Fn(e, t, r);
|
|
2726
|
+
if (i) return i;
|
|
2727
|
+
let a = await Xn(e, t, r);
|
|
2728
|
+
if (a) return a;
|
|
2729
|
+
let o = await Dn(e, t, r);
|
|
2730
|
+
if (o) return o;
|
|
2731
|
+
let s = await Oe(e, t, r);
|
|
2732
|
+
if (s) return s;
|
|
2733
|
+
if (n === "/api/servers" && e.method === "GET") {
|
|
2734
|
+
let e = Yr(t).map(({ headers: e, ...t }) => ({
|
|
2735
|
+
...t,
|
|
2736
|
+
source: "static"
|
|
2737
|
+
})), n = (await Ar(t)).map((e) => ({
|
|
2738
|
+
...e,
|
|
2739
|
+
source: "dynamic"
|
|
2740
|
+
})), i = new Set(e.map((e) => e.id));
|
|
2741
|
+
return K({
|
|
2742
|
+
auth: r,
|
|
2743
|
+
servers: [...e, ...n.filter((e) => !i.has(e.id))],
|
|
2744
|
+
dynamicEnabled: kr(t)
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
if (n === "/api/servers" && e.method === "POST") {
|
|
2748
|
+
if (!kr(t)) return q(501, "not_supported", "Saving servers requires the TENANTS KV binding.");
|
|
2749
|
+
let n = await J(e);
|
|
2750
|
+
if (!n.endpoint) return q(400, "bad_request", "endpoint is required.");
|
|
2751
|
+
let r = Y(t, Qr({
|
|
2752
|
+
name: n.name,
|
|
2753
|
+
endpoint: n.endpoint
|
|
2754
|
+
})), i = $r(n.name || r.id);
|
|
2755
|
+
return await jr(t, {
|
|
2756
|
+
id: i,
|
|
2757
|
+
name: n.name || i,
|
|
2758
|
+
endpoint: r.endpoint,
|
|
2759
|
+
description: n.description
|
|
2760
|
+
}), K({
|
|
2761
|
+
ok: !0,
|
|
2762
|
+
id: i
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
let c = /^\/api\/servers\/([^/]+)$/.exec(n);
|
|
2766
|
+
if (c && e.method === "DELETE") return kr(t) ? (await Mr(t, decodeURIComponent(c[1] ?? "")), K({ ok: !0 })) : q(501, "not_supported", "Deleting servers requires the TENANTS KV binding.");
|
|
2767
|
+
if (n === "/api/bridge/tools" && e.method === "POST") {
|
|
2768
|
+
let n = Y(t, Qr((await J(e)).server ?? {})), r = await X(n);
|
|
2769
|
+
return K({
|
|
2770
|
+
server: $(n),
|
|
2771
|
+
tools: r
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
if (n === "/api/bridge/call" && e.method === "POST") {
|
|
2775
|
+
let n = await J(e);
|
|
2776
|
+
if (!n.tool) return q(400, "bad_request", "Tool name is required.");
|
|
2777
|
+
let r = Y(t, Qr(n.server ?? {})), i = await ii(r, n.tool, n.arguments);
|
|
2778
|
+
return K({
|
|
2779
|
+
server: $(r),
|
|
2780
|
+
tool: n.tool,
|
|
2781
|
+
result: i
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
if (n === "/api/tree" && e.method === "GET") return K({
|
|
2785
|
+
auth: r,
|
|
2786
|
+
tree: await jt(t, await Ti(r, t), { path: "" }, r.mode)
|
|
2787
|
+
});
|
|
2788
|
+
if (n === "/api/crawl" && e.method === "POST") {
|
|
2789
|
+
let n = await J(e), i = At({
|
|
2790
|
+
maxDepth: n.maxDepth,
|
|
2791
|
+
maxNodes: n.maxNodes
|
|
2792
|
+
});
|
|
2793
|
+
return K({
|
|
2794
|
+
auth: r,
|
|
2795
|
+
tree: await jt(t, await Ti(r, t), n.start ?? { path: "" }, r.mode, i)
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
let l = /^\/api\/servers\/([^/]+)(\/.*)?$/.exec(n);
|
|
2799
|
+
if (l) {
|
|
2800
|
+
let n = await ei(t, decodeURIComponent(l[1] ?? ""));
|
|
2801
|
+
return Oi(e, n, l[2] ?? "", `/api/servers/${encodeURIComponent(n.id)}`, r.mode);
|
|
2802
|
+
}
|
|
2803
|
+
return q(404, "not_found", "API route not found.");
|
|
2804
|
+
}
|
|
2805
|
+
async function Ti(e, t) {
|
|
2806
|
+
let n = e.root ?? te(t);
|
|
2807
|
+
return await fn(t, n, e.root && e.tenantId ? e.tenantId : null), n;
|
|
2808
|
+
}
|
|
2809
|
+
async function Ei(e, n, r, i, o) {
|
|
2810
|
+
let s = Date.now(), c = xe(e), l = new URL(e.url), u = l.pathname.replace(/^\/htbp\/?/, "").split("/").filter((e) => e.length > 0), d = u[u.length - 1] === "~help", f = u.length === 0 || d, p = (d ? u.slice(0, -1) : u).map(decodeURIComponent), m = {
|
|
2811
|
+
principal: "anonymous",
|
|
2812
|
+
onBehalfOf: e.headers.get("X-TB-On-Behalf-Of") ?? void 0
|
|
2813
|
+
}, h, g = {}, _, v;
|
|
2814
|
+
try {
|
|
2815
|
+
let t = await Kr(e, n);
|
|
2816
|
+
if (t instanceof Response) v = t;
|
|
2817
|
+
else {
|
|
2818
|
+
if (m.principal = t.principal ?? (t.mode === "none" ? "anonymous" : t.mode === "bearer" ? "static-bearer" : "oauth"), m.subject = t.subject, h = t.tenantId, gn(n) && !t.root && !t.isAdmin) throw new a("This key is not bound to a tenant tree.");
|
|
2819
|
+
if (p[0] === "~device") if (!f && e.method !== "POST") v = q(405, "method_not_allowed", "Use GET {path}/~help or POST {path} to call.");
|
|
2820
|
+
else {
|
|
2821
|
+
let r = e.headers.get("Accept") ?? "", a = f ? void 0 : await J(e);
|
|
2822
|
+
_ = Se(a);
|
|
2823
|
+
let o = await Qn({
|
|
2824
|
+
env: n,
|
|
2825
|
+
principal: t,
|
|
2826
|
+
segments: p,
|
|
2827
|
+
isHelp: f,
|
|
2828
|
+
accept: r,
|
|
2829
|
+
input: a,
|
|
2830
|
+
traceId: c,
|
|
2831
|
+
broker: i
|
|
2832
|
+
});
|
|
2833
|
+
v = o.response, g = o.audit, h = o.tenantId ?? h;
|
|
2834
|
+
}
|
|
2835
|
+
else if (f) {
|
|
2836
|
+
let i = await Ti(t, n);
|
|
2837
|
+
g = we(i, p);
|
|
2838
|
+
let a = e.headers.get("Accept") ?? "";
|
|
2839
|
+
v = await wr(n, i, p, t.mode, a, r);
|
|
2840
|
+
} else if (e.method === "POST") {
|
|
2841
|
+
let i = await Ti(t, n);
|
|
2842
|
+
g = we(i, p);
|
|
2843
|
+
let a = await J(e);
|
|
2844
|
+
_ = Se(a), v = await Tr(n, i, p, t.mode, a, r);
|
|
2845
|
+
} else v = q(405, "method_not_allowed", "Use GET {path}/~help or POST {path} to call.");
|
|
2846
|
+
}
|
|
2847
|
+
} catch (e) {
|
|
2848
|
+
v = t(e);
|
|
2849
|
+
}
|
|
2850
|
+
return v = new Response(v.body, v), v.headers.set("X-TB-Trace-Id", c), await De(n, o, {
|
|
2851
|
+
ts: new Date(s).toISOString(),
|
|
2852
|
+
traceId: c,
|
|
2853
|
+
action: f ? "describe" : "call",
|
|
2854
|
+
actor: m,
|
|
2855
|
+
tenantId: h,
|
|
2856
|
+
path: l.pathname,
|
|
2857
|
+
tool: g.tool,
|
|
2858
|
+
provider: g.provider,
|
|
2859
|
+
effect: g.effect,
|
|
2860
|
+
scope: g.scope,
|
|
2861
|
+
decision: v.status === 401 || v.status === 403 ? "deny" : v.status === 404 ? "not_found" : "allow",
|
|
2862
|
+
result: v.ok ? "ok" : "error",
|
|
2863
|
+
status: v.status,
|
|
2864
|
+
errorCode: v.ok ? void 0 : await Te(v),
|
|
2865
|
+
latencyMs: Date.now() - s,
|
|
2866
|
+
reason: e.headers.get("X-TB-Reason") ?? void 0,
|
|
2867
|
+
input: _
|
|
2868
|
+
}), v;
|
|
2869
|
+
}
|
|
2870
|
+
async function Di(e, t) {
|
|
2871
|
+
let n = await Kr(e, t);
|
|
2872
|
+
if (n instanceof Response) return n;
|
|
2873
|
+
let r = new URL(e.url), i = /^\/mcp\/([^/]+)(\/.*)?$/.exec(r.pathname);
|
|
2874
|
+
if (!i) return q(404, "not_found", "MCP bridge route not found.");
|
|
2875
|
+
let a = await ei(t, decodeURIComponent(i[1] ?? ""));
|
|
2876
|
+
return Oi(e, a, i[2] ?? "", `/mcp/${encodeURIComponent(a.id)}`, n.mode);
|
|
2877
|
+
}
|
|
2878
|
+
async function Oi(e, t, n, r, i) {
|
|
2879
|
+
if (n === "" && e.method === "GET") return K({
|
|
2880
|
+
server: $(t),
|
|
2881
|
+
links: {
|
|
2882
|
+
help: `${r}/~help`,
|
|
2883
|
+
skill: `${r}/~skill`
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2886
|
+
if (n === "/tools" && e.method === "GET") {
|
|
2887
|
+
let e = await X(t);
|
|
2888
|
+
return K({
|
|
2889
|
+
server: $(t),
|
|
2890
|
+
tools: e
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
if (n === "/~help" && e.method === "GET") return Vr(vi(t, await X(t), r, i));
|
|
2894
|
+
if (n === "/~skill" && e.method === "GET") return Vr(yi(t, await X(t), r, i), { headers: { "Content-Type": "text/markdown; charset=utf-8" } });
|
|
2895
|
+
let a = /^\/tools\/([^/]+)(?:\/call)?$/.exec(n);
|
|
2896
|
+
if (a && e.method === "POST") {
|
|
2897
|
+
let n = await J(e), r = decodeURIComponent(a[1] ?? ""), i = await ii(t, r, n.arguments);
|
|
2898
|
+
return K({
|
|
2899
|
+
server: $(t),
|
|
2900
|
+
tool: r,
|
|
2901
|
+
result: i
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
return q(404, "not_found", "Server route not found.");
|
|
2905
|
+
}
|
|
2906
|
+
function $(e) {
|
|
2907
|
+
return {
|
|
2908
|
+
id: e.id,
|
|
2909
|
+
name: e.name,
|
|
2910
|
+
endpoint: e.endpoint,
|
|
2911
|
+
description: e.description,
|
|
2912
|
+
allowedTools: e.allowedTools
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
async function ki(e, n, r = {}, i) {
|
|
2916
|
+
let a = new URL(e.url);
|
|
2917
|
+
try {
|
|
2918
|
+
if (a.pathname.startsWith("/api/")) return await wi(e, n);
|
|
2919
|
+
if (a.pathname.startsWith("/tunnel/")) {
|
|
2920
|
+
let t = await Zn(e, n, r.tunnelBroker);
|
|
2921
|
+
if (t) return t;
|
|
2922
|
+
}
|
|
2923
|
+
if (a.pathname.startsWith("/mcp/")) return await Di(e, n);
|
|
2924
|
+
if (a.pathname === "/htbp" || a.pathname.startsWith("/htbp/")) {
|
|
2925
|
+
let t = await Ei(e, n, r.builtinHandlers, r.tunnelBroker, i), a = new Response(t.body, t);
|
|
2926
|
+
return a.headers.set("Access-Control-Allow-Origin", "*"), a;
|
|
2927
|
+
}
|
|
2928
|
+
return n.ASSETS.fetch(e);
|
|
2929
|
+
} catch (e) {
|
|
2930
|
+
return t(e);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
function Ai(e = {}) {
|
|
2934
|
+
return { fetch(t, n, r) {
|
|
2935
|
+
return ki(t, n, e, r);
|
|
2936
|
+
} };
|
|
2937
|
+
}
|
|
2938
|
+
var ji = Ai();
|
|
2939
|
+
//#endregion
|
|
2940
|
+
export { Ai as createBridge, ji as default };
|