@truefoundry/trueforge 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_frontend/assets/{ConnectorSettings-EYOQ72OS-DdSqeM08.js → ConnectorSettings-ODC3LVHN-CAU9MgGN.js} +1 -1
- package/dist/_frontend/assets/ConnectorSettings-ODC3LVHN-CAU9MgGN.js.br +0 -0
- package/dist/_frontend/assets/ConnectorSettings-ODC3LVHN-CAU9MgGN.js.gz +0 -0
- package/dist/_frontend/assets/{DockLayout-XIOJZ2YM-htVfvSLo.js → DockLayout-2RWDSUKE-eKoTN8R3.js} +1 -1
- package/dist/_frontend/assets/{DrawerLayout-CNKNNVI7-CQ7wZgB6.js → DrawerLayout-CZLJYMIN-DWPAdLZz.js} +2 -2
- package/dist/_frontend/assets/DrawerLayout-CZLJYMIN-DWPAdLZz.js.br +0 -0
- package/dist/_frontend/assets/DrawerLayout-CZLJYMIN-DWPAdLZz.js.gz +0 -0
- package/dist/_frontend/assets/{ModelSettings-WBDPLI6O-CYfqXYXo.js → ModelSettings-CKQ5CVAC-UBz_ch6T.js} +1 -1
- package/dist/_frontend/assets/ModelSettings-CKQ5CVAC-UBz_ch6T.js.br +0 -0
- package/dist/_frontend/assets/ModelSettings-CKQ5CVAC-UBz_ch6T.js.gz +0 -0
- package/dist/_frontend/assets/{SandboxSettings-4GHBXULN-BPT3vPku.js → SandboxSettings-HSCPYUOC-Csl-nC9h.js} +1 -1
- package/dist/_frontend/assets/SandboxSettings-HSCPYUOC-Csl-nC9h.js.br +0 -0
- package/dist/_frontend/assets/SandboxSettings-HSCPYUOC-Csl-nC9h.js.gz +0 -0
- package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-Ce3OWfa1.js +2 -0
- package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-Ce3OWfa1.js.br +0 -0
- package/dist/_frontend/assets/SettingsBuilder-MYWRFXRI-Ce3OWfa1.js.gz +0 -0
- package/dist/_frontend/assets/{ShellRouteSync-T4WLY57Q-BKbLAZEv.js → ShellRouteSync-T4WLY57Q-B5lyCu4o.js} +1 -1
- package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-B5lyCu4o.js.br +0 -0
- package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-B5lyCu4o.js.gz +0 -0
- package/dist/_frontend/assets/{SidebarLayout-JGLTENB3-BiZz45he.js → SidebarLayout-WVY54TGZ-CIx5jQIC.js} +2 -2
- package/dist/_frontend/assets/SidebarLayout-WVY54TGZ-CIx5jQIC.js.br +0 -0
- package/dist/_frontend/assets/SidebarLayout-WVY54TGZ-CIx5jQIC.js.gz +0 -0
- package/dist/_frontend/assets/{SkillSettings-UG6USX4N-DhQTOueg.js → SkillSettings-ECH6SHZ7-CKWEzdO_.js} +1 -1
- package/dist/_frontend/assets/SkillSettings-ECH6SHZ7-CKWEzdO_.js.br +0 -0
- package/dist/_frontend/assets/SkillSettings-ECH6SHZ7-CKWEzdO_.js.gz +0 -0
- package/dist/_frontend/assets/{TrueForgeUIWithRouter-CTC5XRSY-BnWelV-R.js → TrueForgeUIWithRouter-RKZ2E7PV-CoVbEgto.js} +1 -1
- package/dist/_frontend/assets/{WidgetLayout-6P6OUUTG-B1G5uZ4_.js → WidgetLayout-NWWB2SXI-cUmY1p2v.js} +1 -1
- package/dist/_frontend/assets/WidgetLayout-NWWB2SXI-cUmY1p2v.js.br +0 -0
- package/dist/_frontend/assets/WidgetLayout-NWWB2SXI-cUmY1p2v.js.gz +0 -0
- package/dist/_frontend/assets/{chunk-62JRHF6Z-yJ2twjOf.js → chunk-62JRHF6Z-BftTGWZl.js} +1 -1
- package/dist/_frontend/assets/chunk-62JRHF6Z-BftTGWZl.js.br +0 -0
- package/dist/_frontend/assets/chunk-62JRHF6Z-BftTGWZl.js.gz +0 -0
- package/dist/_frontend/assets/{chunk-27MKI3HF-CVw53aeu.js → chunk-OZTNBS6V-CLmmFX7d.js} +1 -1
- package/dist/_frontend/assets/chunk-OZTNBS6V-CLmmFX7d.js.br +0 -0
- package/dist/_frontend/assets/chunk-OZTNBS6V-CLmmFX7d.js.gz +0 -0
- package/dist/_frontend/assets/{chunk-H75C5ETA-CX-fQN0e.js → chunk-TCAVLWYL-DVhtkE3q.js} +2 -2
- package/dist/_frontend/assets/chunk-TCAVLWYL-DVhtkE3q.js.br +0 -0
- package/dist/_frontend/assets/chunk-TCAVLWYL-DVhtkE3q.js.gz +0 -0
- package/dist/_frontend/assets/{cssMode-CEpMXY8Z.js → cssMode-CvKwYh_L.js} +1 -1
- package/dist/_frontend/assets/cssMode-CvKwYh_L.js.br +0 -0
- package/dist/_frontend/assets/cssMode-CvKwYh_L.js.gz +0 -0
- package/dist/_frontend/assets/{freemarker2-cQSgojlC.js → freemarker2-Bb2yOpT3.js} +1 -1
- package/dist/_frontend/assets/freemarker2-Bb2yOpT3.js.br +0 -0
- package/dist/_frontend/assets/freemarker2-Bb2yOpT3.js.gz +0 -0
- package/dist/_frontend/assets/{handlebars-BQ-clQ7_.js → handlebars-FL3wsxe0.js} +1 -1
- package/dist/_frontend/assets/handlebars-FL3wsxe0.js.br +0 -0
- package/dist/_frontend/assets/handlebars-FL3wsxe0.js.gz +0 -0
- package/dist/_frontend/assets/{html-ChfWgJt-.js → html-Cb3v_ZRD.js} +1 -1
- package/dist/_frontend/assets/html-Cb3v_ZRD.js.br +0 -0
- package/dist/_frontend/assets/html-Cb3v_ZRD.js.gz +0 -0
- package/dist/_frontend/assets/{htmlMode-D1PmpoAx.js → htmlMode-JQDvK0Qf.js} +1 -1
- package/dist/_frontend/assets/htmlMode-JQDvK0Qf.js.br +0 -0
- package/dist/_frontend/assets/htmlMode-JQDvK0Qf.js.gz +0 -0
- package/dist/_frontend/assets/{index-BRS0kvLQ.js → index-DJ1uWGdt.js} +122 -122
- package/dist/_frontend/assets/index-DJ1uWGdt.js.br +0 -0
- package/dist/_frontend/assets/index-DJ1uWGdt.js.gz +0 -0
- package/dist/_frontend/assets/{index-aEojVaVa.js → index-DRjgykCE.js} +3 -3
- package/dist/_frontend/assets/index-DRjgykCE.js.br +0 -0
- package/dist/_frontend/assets/index-DRjgykCE.js.gz +0 -0
- package/dist/_frontend/assets/{javascript-DFHGvkjD.js → javascript-2WsgEpYV.js} +1 -1
- package/dist/_frontend/assets/{jsonMode-DWo0prLv.js → jsonMode-DnbseMlM.js} +1 -1
- package/dist/_frontend/assets/jsonMode-DnbseMlM.js.br +0 -0
- package/dist/_frontend/assets/jsonMode-DnbseMlM.js.gz +0 -0
- package/dist/_frontend/assets/{liquid-Bs2__P9v.js → liquid-DeE37I1e.js} +1 -1
- package/dist/_frontend/assets/liquid-DeE37I1e.js.br +0 -0
- package/dist/_frontend/assets/liquid-DeE37I1e.js.gz +0 -0
- package/dist/_frontend/assets/{lspLanguageFeatures-zZ0stYf4.js → lspLanguageFeatures-Bty8j9di.js} +1 -1
- package/dist/_frontend/assets/lspLanguageFeatures-Bty8j9di.js.br +0 -0
- package/dist/_frontend/assets/lspLanguageFeatures-Bty8j9di.js.gz +0 -0
- package/dist/_frontend/assets/{mdx-C7c8MDI_.js → mdx-DfKPZX1Z.js} +1 -1
- package/dist/_frontend/assets/mdx-DfKPZX1Z.js.br +0 -0
- package/dist/_frontend/assets/mdx-DfKPZX1Z.js.gz +0 -0
- package/dist/_frontend/assets/{python-CLO39XAV.js → python-2i3Kiv75.js} +1 -1
- package/dist/_frontend/assets/python-2i3Kiv75.js.br +0 -0
- package/dist/_frontend/assets/python-2i3Kiv75.js.gz +0 -0
- package/dist/_frontend/assets/{razor-efddsYhk.js → razor-B0N4vZnc.js} +1 -1
- package/dist/_frontend/assets/razor-B0N4vZnc.js.br +0 -0
- package/dist/_frontend/assets/razor-B0N4vZnc.js.gz +0 -0
- package/dist/_frontend/assets/{tsMode-xfchPgTX.js → tsMode-D1Y8xh-o.js} +1 -1
- package/dist/_frontend/assets/tsMode-D1Y8xh-o.js.br +0 -0
- package/dist/_frontend/assets/tsMode-D1Y8xh-o.js.gz +0 -0
- package/dist/_frontend/assets/{typescript-BFzaCQP8.js → typescript-2nhip0PT.js} +1 -1
- package/dist/_frontend/assets/typescript-2nhip0PT.js.br +0 -0
- package/dist/_frontend/assets/typescript-2nhip0PT.js.gz +0 -0
- package/dist/_frontend/assets/{workers-DKwMBzKS.js → workers-DSmTvA39.js} +1 -1
- package/dist/_frontend/assets/workers-DSmTvA39.js.br +0 -0
- package/dist/_frontend/assets/workers-DSmTvA39.js.gz +0 -0
- package/dist/_frontend/assets/{xml-Cx6gHwq3.js → xml-CA4M6Pks.js} +1 -1
- package/dist/_frontend/assets/xml-CA4M6Pks.js.br +0 -0
- package/dist/_frontend/assets/xml-CA4M6Pks.js.gz +0 -0
- package/dist/_frontend/assets/{yaml-DyrfMWlf.js → yaml-D5KNk7Ji.js} +1 -1
- package/dist/_frontend/assets/yaml-D5KNk7Ji.js.br +0 -0
- package/dist/_frontend/assets/yaml-D5KNk7Ji.js.gz +0 -0
- package/dist/_frontend/index.html +1 -1
- package/dist/_frontend/index.html.br +0 -0
- package/dist/_frontend/index.html.gz +0 -0
- package/dist/main.js +2152 -468
- package/dist/main.js.map +1 -1
- package/package.json +10 -4
- package/dist/_frontend/assets/ConnectorSettings-EYOQ72OS-DdSqeM08.js.br +0 -0
- package/dist/_frontend/assets/ConnectorSettings-EYOQ72OS-DdSqeM08.js.gz +0 -0
- package/dist/_frontend/assets/DrawerLayout-CNKNNVI7-CQ7wZgB6.js.br +0 -0
- package/dist/_frontend/assets/DrawerLayout-CNKNNVI7-CQ7wZgB6.js.gz +0 -0
- package/dist/_frontend/assets/ModelSettings-WBDPLI6O-CYfqXYXo.js.br +0 -0
- package/dist/_frontend/assets/ModelSettings-WBDPLI6O-CYfqXYXo.js.gz +0 -0
- package/dist/_frontend/assets/SandboxSettings-4GHBXULN-BPT3vPku.js.br +0 -0
- package/dist/_frontend/assets/SandboxSettings-4GHBXULN-BPT3vPku.js.gz +0 -0
- package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js +0 -2
- package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js.br +0 -0
- package/dist/_frontend/assets/SettingsBuilder-TD42LCIU-COZb69Wb.js.gz +0 -0
- package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BKbLAZEv.js.br +0 -0
- package/dist/_frontend/assets/ShellRouteSync-T4WLY57Q-BKbLAZEv.js.gz +0 -0
- package/dist/_frontend/assets/SidebarLayout-JGLTENB3-BiZz45he.js.br +0 -0
- package/dist/_frontend/assets/SidebarLayout-JGLTENB3-BiZz45he.js.gz +0 -0
- package/dist/_frontend/assets/SkillSettings-UG6USX4N-DhQTOueg.js.br +0 -0
- package/dist/_frontend/assets/SkillSettings-UG6USX4N-DhQTOueg.js.gz +0 -0
- package/dist/_frontend/assets/WidgetLayout-6P6OUUTG-B1G5uZ4_.js.br +0 -0
- package/dist/_frontend/assets/WidgetLayout-6P6OUUTG-B1G5uZ4_.js.gz +0 -0
- package/dist/_frontend/assets/chunk-27MKI3HF-CVw53aeu.js.br +0 -0
- package/dist/_frontend/assets/chunk-27MKI3HF-CVw53aeu.js.gz +0 -0
- package/dist/_frontend/assets/chunk-62JRHF6Z-yJ2twjOf.js.br +0 -0
- package/dist/_frontend/assets/chunk-62JRHF6Z-yJ2twjOf.js.gz +0 -0
- package/dist/_frontend/assets/chunk-H75C5ETA-CX-fQN0e.js.br +0 -0
- package/dist/_frontend/assets/chunk-H75C5ETA-CX-fQN0e.js.gz +0 -0
- package/dist/_frontend/assets/cssMode-CEpMXY8Z.js.br +0 -0
- package/dist/_frontend/assets/cssMode-CEpMXY8Z.js.gz +0 -0
- package/dist/_frontend/assets/freemarker2-cQSgojlC.js.br +0 -0
- package/dist/_frontend/assets/freemarker2-cQSgojlC.js.gz +0 -0
- package/dist/_frontend/assets/handlebars-BQ-clQ7_.js.br +0 -0
- package/dist/_frontend/assets/handlebars-BQ-clQ7_.js.gz +0 -0
- package/dist/_frontend/assets/html-ChfWgJt-.js.br +0 -0
- package/dist/_frontend/assets/html-ChfWgJt-.js.gz +0 -0
- package/dist/_frontend/assets/htmlMode-D1PmpoAx.js.br +0 -0
- package/dist/_frontend/assets/htmlMode-D1PmpoAx.js.gz +0 -0
- package/dist/_frontend/assets/index-BRS0kvLQ.js.br +0 -0
- package/dist/_frontend/assets/index-BRS0kvLQ.js.gz +0 -0
- package/dist/_frontend/assets/index-aEojVaVa.js.br +0 -0
- package/dist/_frontend/assets/index-aEojVaVa.js.gz +0 -0
- package/dist/_frontend/assets/jsonMode-DWo0prLv.js.br +0 -0
- package/dist/_frontend/assets/jsonMode-DWo0prLv.js.gz +0 -0
- package/dist/_frontend/assets/liquid-Bs2__P9v.js.br +0 -0
- package/dist/_frontend/assets/liquid-Bs2__P9v.js.gz +0 -0
- package/dist/_frontend/assets/lspLanguageFeatures-zZ0stYf4.js.br +0 -0
- package/dist/_frontend/assets/lspLanguageFeatures-zZ0stYf4.js.gz +0 -0
- package/dist/_frontend/assets/mdx-C7c8MDI_.js.br +0 -0
- package/dist/_frontend/assets/mdx-C7c8MDI_.js.gz +0 -0
- package/dist/_frontend/assets/python-CLO39XAV.js.br +0 -0
- package/dist/_frontend/assets/python-CLO39XAV.js.gz +0 -0
- package/dist/_frontend/assets/razor-efddsYhk.js.br +0 -0
- package/dist/_frontend/assets/razor-efddsYhk.js.gz +0 -0
- package/dist/_frontend/assets/tsMode-xfchPgTX.js.br +0 -0
- package/dist/_frontend/assets/tsMode-xfchPgTX.js.gz +0 -0
- package/dist/_frontend/assets/typescript-BFzaCQP8.js.br +0 -0
- package/dist/_frontend/assets/typescript-BFzaCQP8.js.gz +0 -0
- package/dist/_frontend/assets/workers-DKwMBzKS.js.br +0 -0
- package/dist/_frontend/assets/workers-DKwMBzKS.js.gz +0 -0
- package/dist/_frontend/assets/xml-Cx6gHwq3.js.br +0 -0
- package/dist/_frontend/assets/xml-Cx6gHwq3.js.gz +0 -0
- package/dist/_frontend/assets/yaml-DyrfMWlf.js.br +0 -0
- package/dist/_frontend/assets/yaml-DyrfMWlf.js.gz +0 -0
package/dist/main.js
CHANGED
|
@@ -8,28 +8,6 @@ var __export = (target, all) => {
|
|
|
8
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
// src/db/agentStore.ts
|
|
12
|
-
import { AgentSpecSchema } from "@truefoundry/trueforge-core/agent-session";
|
|
13
|
-
function parseStoredAgentSpec(manifest) {
|
|
14
|
-
return AgentSpecSchema.parse(manifest);
|
|
15
|
-
}
|
|
16
|
-
var AgentNameConflictError;
|
|
17
|
-
var init_agentStore = __esm({
|
|
18
|
-
"src/db/agentStore.ts"() {
|
|
19
|
-
"use strict";
|
|
20
|
-
AgentNameConflictError = class extends Error {
|
|
21
|
-
tenant_id;
|
|
22
|
-
agent_name;
|
|
23
|
-
constructor({ tenant_id, name }, options) {
|
|
24
|
-
super(`Agent name already exists: ${name}`, options);
|
|
25
|
-
this.name = "AgentNameConflictError";
|
|
26
|
-
this.tenant_id = tenant_id;
|
|
27
|
-
this.agent_name = name;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
11
|
// src/config.ts
|
|
34
12
|
var config_exports = {};
|
|
35
13
|
__export(config_exports, {
|
|
@@ -38,7 +16,8 @@ __export(config_exports, {
|
|
|
38
16
|
isOidcConfigured: () => isOidcConfigured,
|
|
39
17
|
parseOidcScopes: () => parseOidcScopes
|
|
40
18
|
});
|
|
41
|
-
import { existsSync } from "fs";
|
|
19
|
+
import { existsSync as existsSync2 } from "fs";
|
|
20
|
+
import os from "os";
|
|
42
21
|
import path from "path";
|
|
43
22
|
import { fileURLToPath } from "url";
|
|
44
23
|
import envPaths from "env-paths";
|
|
@@ -102,7 +81,7 @@ function parseBoolean(options) {
|
|
|
102
81
|
}
|
|
103
82
|
function resolveDefaultFrontendDir() {
|
|
104
83
|
const packaged = path.join(PACKAGE_ROOT, "dist", "_frontend");
|
|
105
|
-
if (
|
|
84
|
+
if (existsSync2(path.join(packaged, "index.html"))) {
|
|
106
85
|
return packaged;
|
|
107
86
|
}
|
|
108
87
|
return path.join(PACKAGE_ROOT, "..", "frontend", "dist");
|
|
@@ -129,6 +108,12 @@ function resolveSqlitePath() {
|
|
|
129
108
|
const paths = envPaths(ENV_PATHS_APP_NAME, { suffix: "" });
|
|
130
109
|
return path.join(paths.data, "db", "db.sqlite");
|
|
131
110
|
}
|
|
111
|
+
function resolveLocalSandboxRootParent() {
|
|
112
|
+
return path.join(envPaths(ENV_PATHS_APP_NAME, { suffix: "" }).data, "sandboxes");
|
|
113
|
+
}
|
|
114
|
+
function resolveCodeModeSocketParent() {
|
|
115
|
+
return path.join(os.tmpdir(), "tf_cms");
|
|
116
|
+
}
|
|
132
117
|
function resolveRedisUrl() {
|
|
133
118
|
const raw = getEnv("REDIS_URL", { defaultValue: DEFAULT_REDIS_URL }) ?? DEFAULT_REDIS_URL;
|
|
134
119
|
if (raw.trim() === "") {
|
|
@@ -305,7 +290,9 @@ var init_config = __esm({
|
|
|
305
290
|
configuration = standalone ? {
|
|
306
291
|
...shared,
|
|
307
292
|
STANDALONE: true,
|
|
308
|
-
SQLITE_PATH: resolveSqlitePath()
|
|
293
|
+
SQLITE_PATH: resolveSqlitePath(),
|
|
294
|
+
LOCAL_SANDBOX_ROOT_PARENT: resolveLocalSandboxRootParent(),
|
|
295
|
+
CODE_MODE_SOCKET_PARENT: resolveCodeModeSocketParent()
|
|
309
296
|
} : {
|
|
310
297
|
...shared,
|
|
311
298
|
STANDALONE: false,
|
|
@@ -329,7 +316,1609 @@ var init_config = __esm({
|
|
|
329
316
|
REDIS_URL: resolveRedisUrl(),
|
|
330
317
|
OIDC: resolveOIDCConfig()
|
|
331
318
|
};
|
|
332
|
-
config_default = configuration;
|
|
319
|
+
config_default = configuration;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// src/db/agentStore.ts
|
|
324
|
+
import { AgentSpecSchema } from "@truefoundry/trueforge-core/agent-session";
|
|
325
|
+
function parseStoredAgentSpec(manifest) {
|
|
326
|
+
return AgentSpecSchema.parse(manifest);
|
|
327
|
+
}
|
|
328
|
+
var AgentNameConflictError;
|
|
329
|
+
var init_agentStore = __esm({
|
|
330
|
+
"src/db/agentStore.ts"() {
|
|
331
|
+
"use strict";
|
|
332
|
+
AgentNameConflictError = class extends Error {
|
|
333
|
+
tenant_id;
|
|
334
|
+
agent_name;
|
|
335
|
+
constructor({ tenant_id, name }, options) {
|
|
336
|
+
super(`Agent name already exists: ${name}`, options);
|
|
337
|
+
this.name = "AgentNameConflictError";
|
|
338
|
+
this.tenant_id = tenant_id;
|
|
339
|
+
this.agent_name = name;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// src/sandbox/local/sandboxScripts.gen.ts
|
|
346
|
+
var sandboxScripts;
|
|
347
|
+
var init_sandboxScripts_gen = __esm({
|
|
348
|
+
"src/sandbox/local/sandboxScripts.gen.ts"() {
|
|
349
|
+
"use strict";
|
|
350
|
+
sandboxScripts = {
|
|
351
|
+
mcpClientLocal: `#!/usr/bin/env python3
|
|
352
|
+
"""Code Mode UDS client \u2014 same public surface as product mcp_client.py, stdlib only.
|
|
353
|
+
|
|
354
|
+
Authoritative reference (do not diverge on API/CLI/policy semantics):
|
|
355
|
+
packages/trueforge-core/src/core/sandbox/scripts/mcp_client.py
|
|
356
|
+
|
|
357
|
+
Inlined into TypeScript via scripts/generate-local-sandbox-scripts.mjs
|
|
358
|
+
(src/sandbox/local/sandboxScripts.gen.ts), same pattern as core sandboxScripts.
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
from __future__ import annotations
|
|
362
|
+
|
|
363
|
+
import argparse
|
|
364
|
+
import asyncio
|
|
365
|
+
import base64
|
|
366
|
+
import json
|
|
367
|
+
import logging
|
|
368
|
+
import os
|
|
369
|
+
import socket
|
|
370
|
+
import sys
|
|
371
|
+
import time
|
|
372
|
+
from pathlib import Path
|
|
373
|
+
from typing import Any, TypedDict
|
|
374
|
+
|
|
375
|
+
logger = logging.getLogger(__name__)
|
|
376
|
+
|
|
377
|
+
MAX_MESSAGE_BYTES = 64 * 1024 * 1024
|
|
378
|
+
_TOOLS_CACHE_TTL_SECONDS = 600
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class _ServerConfig(TypedDict):
|
|
382
|
+
allowed_tools: list[str]
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
_inflight_list_tools: dict[str, asyncio.Task[list[dict[str, Any]]]] = {}
|
|
386
|
+
|
|
387
|
+
_raw_servers = os.environ.get("TFY_MCP_SERVERS")
|
|
388
|
+
_servers_map: dict[str, _ServerConfig] = (
|
|
389
|
+
json.loads(base64.b64decode(_raw_servers).decode()) if _raw_servers else {}
|
|
390
|
+
)
|
|
391
|
+
_enable_agent_approvals = os.environ.get("TFY_ENABLE_AGENT_APPROVALS", "true").lower() == "true"
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _sock_path() -> str:
|
|
395
|
+
path = os.environ.get("TFY_MCP_SOCK")
|
|
396
|
+
if not path:
|
|
397
|
+
raise RuntimeError("TFY_MCP_SOCK is not set")
|
|
398
|
+
return path
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def _request_timeout() -> float:
|
|
402
|
+
return float(os.environ.get("TFY_CM_REQUEST_TIMEOUT_SECONDS", "60"))
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _check_tool_allowed(server: str, tool_name: str) -> None:
|
|
406
|
+
server_config = _servers_map.get(server)
|
|
407
|
+
if server_config is None:
|
|
408
|
+
raise RuntimeError(f"Access denied: MCP server '{server}' is not available for this agent") from None
|
|
409
|
+
server_tools = server_config.get("allowed_tools") or []
|
|
410
|
+
if len(server_tools) > 0 and tool_name not in server_tools:
|
|
411
|
+
raise RuntimeError(f"Access denied: tool '{tool_name}' is not enabled on server '{server}'") from None
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _cache_path(server: str) -> Path:
|
|
415
|
+
return Path(__file__).parent / f"{server}.tools.json"
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _read_tools_cache(server: str) -> list[dict[str, Any]] | None:
|
|
419
|
+
p = _cache_path(server)
|
|
420
|
+
if not p.exists():
|
|
421
|
+
return None
|
|
422
|
+
try:
|
|
423
|
+
raw = json.loads(p.read_text(encoding="utf-8"))
|
|
424
|
+
if not isinstance(raw, dict):
|
|
425
|
+
raise ValueError("cache root must be object")
|
|
426
|
+
fetched_at = raw.get("fetched_at")
|
|
427
|
+
tools = raw.get("tools")
|
|
428
|
+
if not isinstance(fetched_at, (int, float)) or not isinstance(tools, list):
|
|
429
|
+
raise ValueError("cache shape invalid")
|
|
430
|
+
if time.time() - float(fetched_at) > _TOOLS_CACHE_TTL_SECONDS:
|
|
431
|
+
p.unlink(missing_ok=True)
|
|
432
|
+
return None
|
|
433
|
+
return [t for t in tools if isinstance(t, dict)]
|
|
434
|
+
except Exception:
|
|
435
|
+
logger.exception("_read_tools_cache")
|
|
436
|
+
p.unlink(missing_ok=True)
|
|
437
|
+
return None
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _write_tools_cache(server: str, tools: list[dict[str, Any]]) -> None:
|
|
441
|
+
try:
|
|
442
|
+
payload = {"fetched_at": time.time(), "tools": tools}
|
|
443
|
+
_cache_path(server).write_text(json.dumps(payload), encoding="utf-8")
|
|
444
|
+
except Exception:
|
|
445
|
+
logger.exception("_write_tools_cache")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _read_message(sock: socket.socket) -> Any:
|
|
449
|
+
body = b""
|
|
450
|
+
while True:
|
|
451
|
+
chunk = sock.recv(65536)
|
|
452
|
+
if not chunk:
|
|
453
|
+
break
|
|
454
|
+
body += chunk
|
|
455
|
+
if len(body) > MAX_MESSAGE_BYTES:
|
|
456
|
+
raise RuntimeError(f"message exceeds max {MAX_MESSAGE_BYTES} bytes")
|
|
457
|
+
return json.loads(body.decode("utf-8"))
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _write_message(sock: socket.socket, value: Any) -> None:
|
|
461
|
+
sock.sendall(json.dumps(value).encode("utf-8"))
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def _uds_request_sync(payload: dict[str, Any]) -> Any:
|
|
465
|
+
"""Connect \u2192 JSON request \u2192 write-close \u2192 JSON reply (no request_id)."""
|
|
466
|
+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
467
|
+
try:
|
|
468
|
+
sock.settimeout(_request_timeout())
|
|
469
|
+
sock.connect(_sock_path())
|
|
470
|
+
_write_message(sock, payload)
|
|
471
|
+
sock.shutdown(socket.SHUT_WR)
|
|
472
|
+
reply = _read_message(sock)
|
|
473
|
+
finally:
|
|
474
|
+
sock.close()
|
|
475
|
+
if not isinstance(reply, dict):
|
|
476
|
+
raise RuntimeError(f"Code Mode reply is not an object: {reply!r}")
|
|
477
|
+
ok = reply.get("ok")
|
|
478
|
+
if ok is not True:
|
|
479
|
+
source = reply.get("source", "internal")
|
|
480
|
+
error = reply.get("error", "unknown error")
|
|
481
|
+
if source == "caller":
|
|
482
|
+
raise RuntimeError(f"Invalid MCP request: {error}")
|
|
483
|
+
if source == "transport":
|
|
484
|
+
raise RuntimeError(f"Code Mode transport error: {error}")
|
|
485
|
+
raise RuntimeError(f"Internal MCP error: {error}")
|
|
486
|
+
return reply.get("result")
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
async def _uds_request(payload: dict[str, Any]) -> Any:
|
|
490
|
+
return await asyncio.to_thread(_uds_request_sync, payload)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
async def _fetch_tools(server: str) -> list[dict[str, Any]]:
|
|
494
|
+
result = await _uds_request({"op": "list_tools", "server": server})
|
|
495
|
+
if not isinstance(result, dict):
|
|
496
|
+
raise RuntimeError(f"list_tools '{server}' returned unexpected shape: {result!r}") from None
|
|
497
|
+
tools = result.get("tools", [])
|
|
498
|
+
if not isinstance(tools, list):
|
|
499
|
+
raise RuntimeError(f"list_tools '{server}' tools is not a list: {tools!r}") from None
|
|
500
|
+
return [t for t in tools if isinstance(t, dict)]
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
async def _fetch_and_cache_tools(server: str) -> list[dict[str, Any]]:
|
|
504
|
+
tools = await _fetch_tools(server)
|
|
505
|
+
_write_tools_cache(server, tools)
|
|
506
|
+
return tools
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
async def _get_tools(server: str) -> list[dict[str, Any]]:
|
|
510
|
+
cached = _read_tools_cache(server)
|
|
511
|
+
if cached is not None:
|
|
512
|
+
return cached
|
|
513
|
+
task = _inflight_list_tools.get(server)
|
|
514
|
+
if task is None:
|
|
515
|
+
task = asyncio.create_task(_fetch_and_cache_tools(server))
|
|
516
|
+
_inflight_list_tools[server] = task
|
|
517
|
+
task.add_done_callback(lambda _t: _inflight_list_tools.pop(server, None))
|
|
518
|
+
return await task
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
async def _get_tool(server: str, tool_name: str) -> dict[str, Any] | None:
|
|
522
|
+
for t in await _get_tools(server):
|
|
523
|
+
if t.get("name") == tool_name:
|
|
524
|
+
return t
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def _is_destructive(tool: dict[str, Any]) -> bool:
|
|
529
|
+
annotations = tool.get("annotations")
|
|
530
|
+
if annotations is None:
|
|
531
|
+
return False
|
|
532
|
+
if not isinstance(annotations, dict):
|
|
533
|
+
return False
|
|
534
|
+
destructive = annotations.get("destructiveHint")
|
|
535
|
+
read_only = annotations.get("readOnlyHint")
|
|
536
|
+
return bool(destructive) or (not read_only and read_only is not None)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
async def _ensure_non_destructive(server: str, tool_name: str) -> None:
|
|
540
|
+
tool = await _get_tool(server, tool_name)
|
|
541
|
+
if tool is None:
|
|
542
|
+
raise RuntimeError(f"Tool '{tool_name}' not found on MCP server '{server}'") from None
|
|
543
|
+
if _is_destructive(tool):
|
|
544
|
+
raise RuntimeError(
|
|
545
|
+
f"Tool '{tool_name}' on MCP server '{server}' is destructive and cannot be called in Code Mode; "
|
|
546
|
+
f"call it directly so it can go through the user approval flow"
|
|
547
|
+
) from None
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _project_call_tool_result(server: str, tool: str, result: Any) -> Any:
|
|
551
|
+
"""Project an MCP-wire CallToolResult-shaped object into the user-facing Python value."""
|
|
552
|
+
if not isinstance(result, dict):
|
|
553
|
+
raise RuntimeError(f"call_tool reply for '{server}/{tool}' is malformed: expected object") from None
|
|
554
|
+
|
|
555
|
+
if result.get("isError"):
|
|
556
|
+
content = result.get("content")
|
|
557
|
+
text_parts: list[str] = []
|
|
558
|
+
if isinstance(content, list):
|
|
559
|
+
for c in content:
|
|
560
|
+
if isinstance(c, dict) and c.get("type") == "text":
|
|
561
|
+
text = c.get("text")
|
|
562
|
+
if isinstance(text, str) and text:
|
|
563
|
+
text_parts.append(text)
|
|
564
|
+
msg = "; ".join(text_parts) if text_parts else "tool returned an error"
|
|
565
|
+
raise RuntimeError(f"MCP tool error (server={server}, tool={tool}): {msg}") from None
|
|
566
|
+
|
|
567
|
+
if result.get("structuredContent") is not None:
|
|
568
|
+
return result["structuredContent"]
|
|
569
|
+
|
|
570
|
+
content = result.get("content")
|
|
571
|
+
if isinstance(content, list) and len(content) == 1:
|
|
572
|
+
first = content[0]
|
|
573
|
+
if isinstance(first, dict) and first.get("type") == "text":
|
|
574
|
+
text = first.get("text")
|
|
575
|
+
if isinstance(text, str) and text:
|
|
576
|
+
try:
|
|
577
|
+
return json.loads(text)
|
|
578
|
+
except Exception:
|
|
579
|
+
pass
|
|
580
|
+
if isinstance(content, list) and content:
|
|
581
|
+
return content
|
|
582
|
+
return None
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
async def call_tool(server: str, tool: str, body: dict[str, Any]) -> Any:
|
|
586
|
+
_check_tool_allowed(server, tool)
|
|
587
|
+
if _enable_agent_approvals:
|
|
588
|
+
await _ensure_non_destructive(server, tool)
|
|
589
|
+
|
|
590
|
+
raw = await _uds_request(
|
|
591
|
+
{
|
|
592
|
+
"op": "call_tool",
|
|
593
|
+
"server": server,
|
|
594
|
+
"tool": tool,
|
|
595
|
+
"arguments": body,
|
|
596
|
+
},
|
|
597
|
+
)
|
|
598
|
+
return _project_call_tool_result(server, tool, raw)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
_USAGE = "mcp_client_local.py call-tool <server> <tool> <args-json>"
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def _build_arg_parser() -> argparse.ArgumentParser:
|
|
605
|
+
parser = argparse.ArgumentParser(prog="mcp_client_local.py", usage=_USAGE)
|
|
606
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
607
|
+
|
|
608
|
+
call_tool_p = sub.add_parser("call-tool", help="Invoke an MCP tool")
|
|
609
|
+
call_tool_p.add_argument("server")
|
|
610
|
+
call_tool_p.add_argument("tool")
|
|
611
|
+
call_tool_p.add_argument("args_json", metavar="args-json", type=json.loads)
|
|
612
|
+
|
|
613
|
+
return parser
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
async def _main() -> None:
|
|
617
|
+
args = _build_arg_parser().parse_args()
|
|
618
|
+
try:
|
|
619
|
+
if args.cmd == "call-tool":
|
|
620
|
+
result = await call_tool(args.server, args.tool, args.args_json)
|
|
621
|
+
print(json.dumps(result, default=str))
|
|
622
|
+
except RuntimeError as e:
|
|
623
|
+
sys.exit(str(e))
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
if __name__ == "__main__":
|
|
627
|
+
asyncio.run(_main())
|
|
628
|
+
`
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// src/sandbox/local/schemas/jsonMessage.ts
|
|
634
|
+
import { z as z5 } from "zod";
|
|
635
|
+
var JsonMessageValueSchema;
|
|
636
|
+
var init_jsonMessage = __esm({
|
|
637
|
+
"src/sandbox/local/schemas/jsonMessage.ts"() {
|
|
638
|
+
"use strict";
|
|
639
|
+
JsonMessageValueSchema = z5.json();
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// src/sandbox/local/core/frame.ts
|
|
644
|
+
function encodeJsonMessage(value) {
|
|
645
|
+
return Buffer.from(JSON.stringify(value), "utf8");
|
|
646
|
+
}
|
|
647
|
+
var MAX_MESSAGE_BYTES, JsonMessageReader;
|
|
648
|
+
var init_frame = __esm({
|
|
649
|
+
"src/sandbox/local/core/frame.ts"() {
|
|
650
|
+
"use strict";
|
|
651
|
+
init_jsonMessage();
|
|
652
|
+
MAX_MESSAGE_BYTES = 64 * 1024 * 1024;
|
|
653
|
+
JsonMessageReader = class {
|
|
654
|
+
#buffer = Buffer.alloc(0);
|
|
655
|
+
#maxBytes;
|
|
656
|
+
constructor(options = {}) {
|
|
657
|
+
this.#maxBytes = options.maxBytes ?? MAX_MESSAGE_BYTES;
|
|
658
|
+
}
|
|
659
|
+
push(chunk) {
|
|
660
|
+
if (this.#buffer.length + chunk.length > this.#maxBytes) {
|
|
661
|
+
throw new Error(`message exceeds max ${String(this.#maxBytes)} bytes`);
|
|
662
|
+
}
|
|
663
|
+
this.#buffer = Buffer.concat([this.#buffer, chunk]);
|
|
664
|
+
}
|
|
665
|
+
finish() {
|
|
666
|
+
try {
|
|
667
|
+
return JsonMessageValueSchema.parse(JSON.parse(this.#buffer.toString("utf8")));
|
|
668
|
+
} catch (error) {
|
|
669
|
+
throw new Error("invalid JSON message", { cause: error });
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// src/sandbox/local/core/CodeModeUdsTransport.ts
|
|
677
|
+
import { CodeModeRequestSchema, validateNoPathTraversal } from "@truefoundry/trueforge-core/core";
|
|
678
|
+
import { chmodSync, existsSync as existsSync3, realpathSync, statSync } from "fs";
|
|
679
|
+
import { chmod, mkdir as mkdir2, rm as rm2, symlink, unlink, writeFile } from "fs/promises";
|
|
680
|
+
import { createServer } from "net";
|
|
681
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
682
|
+
import { ulid } from "ulid";
|
|
683
|
+
function assertCodeModeSocketParentPath(path7) {
|
|
684
|
+
if (!isAbsolute(path7)) {
|
|
685
|
+
throw new Error("codeModeSocketParentPath must be an absolute path");
|
|
686
|
+
}
|
|
687
|
+
validateNoPathTraversal(path7);
|
|
688
|
+
const resolved = resolve(path7);
|
|
689
|
+
if (!existsSync3(resolved) || !statSync(resolved).isDirectory()) {
|
|
690
|
+
throw new Error("codeModeSocketParentPath must be an existing directory");
|
|
691
|
+
}
|
|
692
|
+
const real = realpathSync(resolved);
|
|
693
|
+
const bytes = Buffer.byteLength(real);
|
|
694
|
+
if (bytes > MAX_CODE_MODE_SOCKET_PARENT_BYTES) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`codeModeSocketParentPath (${real}) must be at most ${String(MAX_CODE_MODE_SOCKET_PARENT_BYTES)} bytes (got ${String(bytes)})`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
chmodSync(real, CODE_MODE_SOCKET_PARENT_MODE);
|
|
700
|
+
const mode = statSync(real).mode & 511;
|
|
701
|
+
if (mode !== CODE_MODE_SOCKET_PARENT_MODE) {
|
|
702
|
+
throw new Error(`codeModeSocketParentPath must be mode 0700 after chmod (got 0o${mode.toString(8)})`);
|
|
703
|
+
}
|
|
704
|
+
return real;
|
|
705
|
+
}
|
|
706
|
+
async function probeCodeModeUnixSocket(parentPath) {
|
|
707
|
+
const realParent = assertCodeModeSocketParentPath(parentPath);
|
|
708
|
+
const sockPath = join(realParent, ulid().toLowerCase());
|
|
709
|
+
await unlink(sockPath).catch(() => void 0);
|
|
710
|
+
const server = createServer();
|
|
711
|
+
try {
|
|
712
|
+
await new Promise((resolveListen, reject) => {
|
|
713
|
+
server.once("error", reject);
|
|
714
|
+
server.listen(sockPath, () => {
|
|
715
|
+
server.off("error", reject);
|
|
716
|
+
resolveListen();
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
} catch (error) {
|
|
720
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
721
|
+
throw new Error(`listen ${sockPath} (${String(Buffer.byteLength(sockPath))} bytes): ${message}`, { cause: error });
|
|
722
|
+
} finally {
|
|
723
|
+
await new Promise((resolveClose) => {
|
|
724
|
+
server.close(() => {
|
|
725
|
+
resolveClose();
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
await unlink(sockPath).catch(() => void 0);
|
|
729
|
+
}
|
|
730
|
+
return { sockPath };
|
|
731
|
+
}
|
|
732
|
+
var MAX_CODE_MODE_SOCKET_PARENT_BYTES, CODE_MODE_SOCKET_PARENT_MODE, CODE_MODE_SOCKET_MODE, CodeModeUdsTransport;
|
|
733
|
+
var init_CodeModeUdsTransport = __esm({
|
|
734
|
+
"src/sandbox/local/core/CodeModeUdsTransport.ts"() {
|
|
735
|
+
"use strict";
|
|
736
|
+
init_sandboxScripts_gen();
|
|
737
|
+
init_frame();
|
|
738
|
+
MAX_CODE_MODE_SOCKET_PARENT_BYTES = 65;
|
|
739
|
+
CODE_MODE_SOCKET_PARENT_MODE = 448;
|
|
740
|
+
CODE_MODE_SOCKET_MODE = 384;
|
|
741
|
+
CodeModeUdsTransport = class {
|
|
742
|
+
codeModeSocketParentPath;
|
|
743
|
+
maxMessageBytes;
|
|
744
|
+
onProtocolError;
|
|
745
|
+
clientRemotePath;
|
|
746
|
+
sessionPromise;
|
|
747
|
+
server;
|
|
748
|
+
sockPath;
|
|
749
|
+
dispatcher;
|
|
750
|
+
cachedEnv;
|
|
751
|
+
constructor(options) {
|
|
752
|
+
this.codeModeSocketParentPath = assertCodeModeSocketParentPath(options.codeModeSocketParentPath);
|
|
753
|
+
this.maxMessageBytes = options.maxMessageBytes ?? MAX_MESSAGE_BYTES;
|
|
754
|
+
this.onProtocolError = options.onProtocolError;
|
|
755
|
+
this.clientRemotePath = options.clientRemotePath;
|
|
756
|
+
}
|
|
757
|
+
getClientInstall(params) {
|
|
758
|
+
return {
|
|
759
|
+
content: sandboxScripts.mcpClientLocal,
|
|
760
|
+
remotePath: this.clientRemotePath(params.sandboxId)
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
start(params) {
|
|
764
|
+
this.dispatcher = params.codeModeDispatcher;
|
|
765
|
+
this.sessionPromise ??= this.listenSession(params).catch((e) => {
|
|
766
|
+
this.sessionPromise = void 0;
|
|
767
|
+
this.cachedEnv = void 0;
|
|
768
|
+
throw e;
|
|
769
|
+
});
|
|
770
|
+
return this.sessionPromise;
|
|
771
|
+
}
|
|
772
|
+
async stop() {
|
|
773
|
+
const pending = this.sessionPromise;
|
|
774
|
+
this.sessionPromise = void 0;
|
|
775
|
+
this.cachedEnv = void 0;
|
|
776
|
+
if (pending !== void 0) {
|
|
777
|
+
try {
|
|
778
|
+
await pending;
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const server = this.server;
|
|
783
|
+
const sockPath = this.sockPath;
|
|
784
|
+
this.server = void 0;
|
|
785
|
+
this.sockPath = void 0;
|
|
786
|
+
if (server !== void 0) {
|
|
787
|
+
await new Promise((resolveClose) => {
|
|
788
|
+
server.close(() => {
|
|
789
|
+
resolveClose();
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
if (sockPath !== void 0) {
|
|
794
|
+
await unlink(sockPath).catch(() => void 0);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
async listenSession(params) {
|
|
798
|
+
if (this.server !== void 0 && this.cachedEnv !== void 0) {
|
|
799
|
+
return { env: this.cachedEnv };
|
|
800
|
+
}
|
|
801
|
+
assertCodeModeSocketParentPath(this.codeModeSocketParentPath);
|
|
802
|
+
const sockPath = join(this.codeModeSocketParentPath, ulid().toLowerCase());
|
|
803
|
+
await unlink(sockPath).catch(() => void 0);
|
|
804
|
+
const server = createServer({ allowHalfOpen: true });
|
|
805
|
+
try {
|
|
806
|
+
await new Promise((resolveListen, reject) => {
|
|
807
|
+
server.once("error", reject);
|
|
808
|
+
server.listen(sockPath, () => {
|
|
809
|
+
server.off("error", reject);
|
|
810
|
+
resolveListen();
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
await chmod(sockPath, CODE_MODE_SOCKET_MODE);
|
|
814
|
+
} catch (error) {
|
|
815
|
+
server.close();
|
|
816
|
+
await unlink(sockPath).catch(() => void 0);
|
|
817
|
+
throw error;
|
|
818
|
+
}
|
|
819
|
+
this.server = server;
|
|
820
|
+
this.sockPath = sockPath;
|
|
821
|
+
server.on("connection", (socket) => {
|
|
822
|
+
this.handleConnection(socket);
|
|
823
|
+
});
|
|
824
|
+
const env = {
|
|
825
|
+
TFY_MCP_SOCK: sockPath,
|
|
826
|
+
TFY_CM_REQUEST_TIMEOUT_SECONDS: String(params.requestTimeoutSeconds)
|
|
827
|
+
};
|
|
828
|
+
this.cachedEnv = env;
|
|
829
|
+
return { env };
|
|
830
|
+
}
|
|
831
|
+
handleConnection(socket) {
|
|
832
|
+
const reader = new JsonMessageReader({ maxBytes: this.maxMessageBytes });
|
|
833
|
+
let settled = false;
|
|
834
|
+
socket.on("error", () => void 0);
|
|
835
|
+
const fail = (message) => {
|
|
836
|
+
if (settled) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
settled = true;
|
|
840
|
+
this.onProtocolError?.(message);
|
|
841
|
+
socket.destroy();
|
|
842
|
+
};
|
|
843
|
+
socket.on("data", (chunk) => {
|
|
844
|
+
try {
|
|
845
|
+
reader.push(chunk);
|
|
846
|
+
} catch (error) {
|
|
847
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
socket.on("end", () => {
|
|
851
|
+
if (settled) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
settled = true;
|
|
855
|
+
void this.dispatchConnection(socket, reader).catch((error) => {
|
|
856
|
+
this.onProtocolError?.(error instanceof Error ? error.message : String(error));
|
|
857
|
+
socket.destroy();
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
async dispatchConnection(socket, reader) {
|
|
862
|
+
let request;
|
|
863
|
+
try {
|
|
864
|
+
const parsed = CodeModeRequestSchema.safeParse(reader.finish());
|
|
865
|
+
if (!parsed.success) {
|
|
866
|
+
const reply2 = {
|
|
867
|
+
ok: false,
|
|
868
|
+
error: "Malformed Code Mode request",
|
|
869
|
+
source: "caller"
|
|
870
|
+
};
|
|
871
|
+
socket.write(encodeJsonMessage(reply2));
|
|
872
|
+
socket.end();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
request = parsed.data;
|
|
876
|
+
} catch (error) {
|
|
877
|
+
this.onProtocolError?.(error instanceof Error ? error.message : String(error));
|
|
878
|
+
socket.destroy();
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const dispatcher = this.dispatcher;
|
|
882
|
+
if (dispatcher === void 0) {
|
|
883
|
+
const reply2 = {
|
|
884
|
+
ok: false,
|
|
885
|
+
error: "Code Mode dispatcher is not configured",
|
|
886
|
+
source: "internal"
|
|
887
|
+
};
|
|
888
|
+
socket.write(encodeJsonMessage(reply2));
|
|
889
|
+
socket.end();
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
const reply = await dispatcher.dispatch({ request, traceCarrier: {} });
|
|
893
|
+
try {
|
|
894
|
+
socket.write(encodeJsonMessage(reply));
|
|
895
|
+
} finally {
|
|
896
|
+
socket.end();
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// src/sandbox/local/core/hostRun.ts
|
|
904
|
+
import { getDefaultWritePaths, SandboxManager } from "@anthropic-ai/sandbox-runtime";
|
|
905
|
+
import { execFile, spawn } from "child_process";
|
|
906
|
+
import { randomUUID } from "crypto";
|
|
907
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
908
|
+
import { mkdir as mkdir3, rm as rm3 } from "fs/promises";
|
|
909
|
+
import { createRequire } from "module";
|
|
910
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2 } from "path";
|
|
911
|
+
import { promisify } from "util";
|
|
912
|
+
function codeModeSocketParentAllow() {
|
|
913
|
+
return codeModeSocketParentPath === void 0 ? [] : [codeModeSocketParentPath];
|
|
914
|
+
}
|
|
915
|
+
function requireActivePlatform() {
|
|
916
|
+
if (activePlatform === void 0) {
|
|
917
|
+
throw new Error("SRT platform is not set; call initSrt({ platform }) first");
|
|
918
|
+
}
|
|
919
|
+
return activePlatform;
|
|
920
|
+
}
|
|
921
|
+
function commandPath(platform) {
|
|
922
|
+
return COMMAND_PATH_BY_PLATFORM[platform];
|
|
923
|
+
}
|
|
924
|
+
function sandboxVenvPath(sandboxRootPath) {
|
|
925
|
+
return join2(sandboxRootPath, SANDBOX_VENV_DIR);
|
|
926
|
+
}
|
|
927
|
+
function allowedNetworkDomains() {
|
|
928
|
+
return [...LOCAL_SANDBOX_ALLOWED_DOMAINS];
|
|
929
|
+
}
|
|
930
|
+
function darwinSandboxNetworkShellPreamble() {
|
|
931
|
+
return [
|
|
932
|
+
'if [ -n "${HTTP_PROXY:-}" ]; then export HTTP_PROXY="${HTTP_PROXY//localhost/127.0.0.1}"; fi',
|
|
933
|
+
'if [ -n "${HTTPS_PROXY:-}" ]; then export HTTPS_PROXY="${HTTPS_PROXY//localhost/127.0.0.1}"; fi',
|
|
934
|
+
'if [ -n "${http_proxy:-}" ]; then export http_proxy="${http_proxy//localhost/127.0.0.1}"; fi',
|
|
935
|
+
'if [ -n "${https_proxy:-}" ]; then export https_proxy="${https_proxy//localhost/127.0.0.1}"; fi',
|
|
936
|
+
'if [ -n "${ALL_PROXY:-}" ]; then export ALL_PROXY="${ALL_PROXY//localhost/127.0.0.1}"; fi',
|
|
937
|
+
'if [ -n "${all_proxy:-}" ]; then export all_proxy="${all_proxy//localhost/127.0.0.1}"; fi',
|
|
938
|
+
'if [ -n "${GRPC_PROXY:-}" ]; then export GRPC_PROXY="${GRPC_PROXY//localhost/127.0.0.1}"; fi',
|
|
939
|
+
'if [ -n "${grpc_proxy:-}" ]; then export grpc_proxy="${grpc_proxy//localhost/127.0.0.1}"; fi'
|
|
940
|
+
].join("; ");
|
|
941
|
+
}
|
|
942
|
+
function wrapSandboxCommand(params) {
|
|
943
|
+
if (params.platform !== "darwin") {
|
|
944
|
+
return params.command;
|
|
945
|
+
}
|
|
946
|
+
return `${darwinSandboxNetworkShellPreamble()}; ${params.command}`;
|
|
947
|
+
}
|
|
948
|
+
function srtHostBinaryNames(platform) {
|
|
949
|
+
return SRT_HOST_BINARIES_BY_PLATFORM[platform];
|
|
950
|
+
}
|
|
951
|
+
async function checkSrtHostDependencies() {
|
|
952
|
+
const result = await SandboxManager.checkDependenciesAsync();
|
|
953
|
+
return { errors: [...result.errors], warnings: [...result.warnings] };
|
|
954
|
+
}
|
|
955
|
+
async function resolveCommandOnHost(params) {
|
|
956
|
+
if (!/^[A-Za-z0-9._+-]+$/.test(params.name)) {
|
|
957
|
+
throw new Error(`invalid command name for resolveCommandOnHost: ${params.name}`);
|
|
958
|
+
}
|
|
959
|
+
const pathEnv = params.pathEnv !== void 0 && params.pathEnv.length > 0 ? params.pathEnv : commandPath(params.platform);
|
|
960
|
+
try {
|
|
961
|
+
const { stdout } = await execFileAsync("/bin/sh", ["-c", `command -v -- ${params.name}`], {
|
|
962
|
+
env: { PATH: pathEnv },
|
|
963
|
+
encoding: "utf8"
|
|
964
|
+
});
|
|
965
|
+
const resolved = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
|
|
966
|
+
if (resolved === void 0 || resolved.length === 0 || !isAbsolute2(resolved)) {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
return resolved;
|
|
970
|
+
} catch {
|
|
971
|
+
return void 0;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
async function resolvePythonExecutableOnHost(params) {
|
|
975
|
+
if (!isAbsolute2(params.commandPath)) {
|
|
976
|
+
return void 0;
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
const { stdout } = await execFileAsync(params.commandPath, ["-c", "import sys; print(sys.executable)"], {
|
|
980
|
+
encoding: "utf8",
|
|
981
|
+
timeout: 5e3
|
|
982
|
+
});
|
|
983
|
+
const executable = stdout.trim().split(/\r?\n/).filter(Boolean).at(-1);
|
|
984
|
+
if (executable === void 0 || executable.length === 0 || !isAbsolute2(executable)) {
|
|
985
|
+
return void 0;
|
|
986
|
+
}
|
|
987
|
+
return realpathSync2(executable);
|
|
988
|
+
} catch {
|
|
989
|
+
return void 0;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function denySharedDefaultWritePaths() {
|
|
993
|
+
return getDefaultWritePaths().filter((path7) => !path7.startsWith("/dev/"));
|
|
994
|
+
}
|
|
995
|
+
function platformAllowRead(platform) {
|
|
996
|
+
return [...ALLOW_READ_BY_PLATFORM[platform]];
|
|
997
|
+
}
|
|
998
|
+
function filesystemPolicy(params) {
|
|
999
|
+
return {
|
|
1000
|
+
allowWrite: [params.sandboxRootPath],
|
|
1001
|
+
denyWrite: denySharedDefaultWritePaths(),
|
|
1002
|
+
denyRead: ["/"],
|
|
1003
|
+
allowRead: [params.sandboxRootPath, ...codeModeSocketParentAllow(), ...platformAllowRead(params.platform)]
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function commandEnv(params) {
|
|
1007
|
+
const tmp = join2(params.sandboxRootPath, ".tmp");
|
|
1008
|
+
const home = join2(params.sandboxRootPath, ".home");
|
|
1009
|
+
const venvDir = sandboxVenvPath(params.sandboxRootPath);
|
|
1010
|
+
const locked = {
|
|
1011
|
+
HOME: home,
|
|
1012
|
+
TMPDIR: tmp,
|
|
1013
|
+
TMP: tmp,
|
|
1014
|
+
TEMP: tmp,
|
|
1015
|
+
VIRTUAL_ENV: venvDir,
|
|
1016
|
+
// Cwd-relative layout dirs (not host-absolute). Absolute paths under
|
|
1017
|
+
// `Application Support` break PATH lookup (`mcp-client: command not found`).
|
|
1018
|
+
PATH: `${join2(SANDBOX_VENV_DIR, "bin")}:mcp-client/bin:${commandPath(params.platform)}`
|
|
1019
|
+
};
|
|
1020
|
+
return {
|
|
1021
|
+
...params.extra,
|
|
1022
|
+
...locked
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
function sessionFilesystem(platform) {
|
|
1026
|
+
const allowWrite = [];
|
|
1027
|
+
return {
|
|
1028
|
+
allowWrite,
|
|
1029
|
+
denyWrite: denySharedDefaultWritePaths(),
|
|
1030
|
+
denyRead: ["/"],
|
|
1031
|
+
allowRead: [...platformAllowRead(platform), ...codeModeSocketParentAllow()]
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
function sessionNetwork(params) {
|
|
1035
|
+
const allowedDomains = allowedNetworkDomains();
|
|
1036
|
+
const deniedDomains = [];
|
|
1037
|
+
if (params.platform === "linux") {
|
|
1038
|
+
return {
|
|
1039
|
+
allowedDomains,
|
|
1040
|
+
deniedDomains,
|
|
1041
|
+
allowAllUnixSockets: true
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
allowedDomains,
|
|
1046
|
+
deniedDomains,
|
|
1047
|
+
allowAllUnixSockets: false,
|
|
1048
|
+
allowUnixSockets: params.unixSockets ?? []
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function darwinUnixSocketPaths() {
|
|
1052
|
+
return [...darwinUnixSocketSandboxRoots, ...codeModeSocketParentAllow()];
|
|
1053
|
+
}
|
|
1054
|
+
function syncDarwinUnixSockets() {
|
|
1055
|
+
if (SandboxManager.getConfig() === void 0) {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const platform = requireActivePlatform();
|
|
1059
|
+
if (platform !== "darwin") {
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
SandboxManager.updateConfig(buildSessionConfig(platform));
|
|
1063
|
+
}
|
|
1064
|
+
function buildSessionConfig(platform) {
|
|
1065
|
+
return {
|
|
1066
|
+
network: sessionNetwork({ platform, unixSockets: darwinUnixSocketPaths() }),
|
|
1067
|
+
filesystem: sessionFilesystem(platform)
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
async function createSandbox(sandboxRootPath) {
|
|
1071
|
+
await mkdir3(sandboxRootPath, { recursive: true, mode: 448 });
|
|
1072
|
+
await mkdir3(join2(sandboxRootPath, ".tmp"), { recursive: true, mode: 448 });
|
|
1073
|
+
await mkdir3(join2(sandboxRootPath, ".home"), { recursive: true, mode: 448 });
|
|
1074
|
+
const realRoot = realpathSync2(sandboxRootPath);
|
|
1075
|
+
darwinUnixSocketSandboxRoots.add(realRoot);
|
|
1076
|
+
syncDarwinUnixSockets();
|
|
1077
|
+
return realRoot;
|
|
1078
|
+
}
|
|
1079
|
+
async function removeSandbox(sandboxRootPath) {
|
|
1080
|
+
darwinUnixSocketSandboxRoots.delete(sandboxRootPath);
|
|
1081
|
+
syncDarwinUnixSockets();
|
|
1082
|
+
await rm3(sandboxRootPath, { recursive: true, force: true });
|
|
1083
|
+
}
|
|
1084
|
+
async function initSrt(params) {
|
|
1085
|
+
activePlatform = params.platform;
|
|
1086
|
+
codeModeSocketParentPath = params.codeModeSocketParentPath === void 0 ? void 0 : realpathSync2(params.codeModeSocketParentPath);
|
|
1087
|
+
await SandboxManager.initialize(buildSessionConfig(params.platform));
|
|
1088
|
+
}
|
|
1089
|
+
async function resetSrt() {
|
|
1090
|
+
codeModeSocketParentPath = void 0;
|
|
1091
|
+
activePlatform = void 0;
|
|
1092
|
+
await SandboxManager.reset();
|
|
1093
|
+
}
|
|
1094
|
+
function isSrtInitialized() {
|
|
1095
|
+
return activePlatform !== void 0 && SandboxManager.getConfig() !== void 0;
|
|
1096
|
+
}
|
|
1097
|
+
function killExecTree(child) {
|
|
1098
|
+
if (!child) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const pid = child.pid;
|
|
1102
|
+
if (pid !== void 0 && process.platform !== "win32") {
|
|
1103
|
+
try {
|
|
1104
|
+
process.kill(-pid, "SIGKILL");
|
|
1105
|
+
return;
|
|
1106
|
+
} catch {
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (!child.killed) {
|
|
1110
|
+
child.kill("SIGKILL");
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
async function runSupervisorSession(params) {
|
|
1114
|
+
const {
|
|
1115
|
+
sandboxRootPath,
|
|
1116
|
+
command: rawCommand,
|
|
1117
|
+
shell,
|
|
1118
|
+
platform,
|
|
1119
|
+
cwd = sandboxRootPath,
|
|
1120
|
+
env,
|
|
1121
|
+
stdin,
|
|
1122
|
+
onChildSpawn,
|
|
1123
|
+
timeoutMs
|
|
1124
|
+
} = params;
|
|
1125
|
+
const command = wrapSandboxCommand({ platform, command: rawCommand });
|
|
1126
|
+
const wrap = await SandboxManager.wrapWithSandboxArgv(
|
|
1127
|
+
command,
|
|
1128
|
+
shell,
|
|
1129
|
+
{
|
|
1130
|
+
filesystem: filesystemPolicy({ sandboxRootPath, platform }),
|
|
1131
|
+
network: {
|
|
1132
|
+
allowedDomains: allowedNetworkDomains(),
|
|
1133
|
+
deniedDomains: []
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
void 0,
|
|
1137
|
+
sandboxRootPath,
|
|
1138
|
+
{ commandId: randomUUID(), commandText: command }
|
|
1139
|
+
);
|
|
1140
|
+
const [argv0, ...argvRest] = wrap.argv;
|
|
1141
|
+
if (argv0 === void 0) {
|
|
1142
|
+
throw new Error("wrapWithSandboxArgv returned empty argv");
|
|
1143
|
+
}
|
|
1144
|
+
const childEnv = {
|
|
1145
|
+
...commandEnv({ sandboxRootPath, platform, ...env === void 0 ? {} : { extra: env } })
|
|
1146
|
+
};
|
|
1147
|
+
const child = spawn(argv0, argvRest, {
|
|
1148
|
+
cwd,
|
|
1149
|
+
env: childEnv,
|
|
1150
|
+
shell: false,
|
|
1151
|
+
// Detached process groups break stdin forwarding for upload (`cat` via pipe) under Jest.
|
|
1152
|
+
detached: stdin === void 0 && process.platform !== "win32",
|
|
1153
|
+
stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
|
|
1154
|
+
});
|
|
1155
|
+
if (child.pid !== void 0) {
|
|
1156
|
+
onChildSpawn?.(child.pid);
|
|
1157
|
+
}
|
|
1158
|
+
if (stdin !== void 0) {
|
|
1159
|
+
const stdinStream = child.stdin;
|
|
1160
|
+
if (stdinStream === null) {
|
|
1161
|
+
killExecTree(child);
|
|
1162
|
+
SandboxManager.cleanupAfterCommand();
|
|
1163
|
+
throw new Error("stdin unavailable for sandboxed command");
|
|
1164
|
+
}
|
|
1165
|
+
stdinStream.on("error", () => void 0);
|
|
1166
|
+
await new Promise((resolve3, reject) => {
|
|
1167
|
+
stdinStream.end(stdin, (error) => {
|
|
1168
|
+
if (error) {
|
|
1169
|
+
reject(error);
|
|
1170
|
+
} else {
|
|
1171
|
+
resolve3();
|
|
1172
|
+
}
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
let stdoutText = "";
|
|
1177
|
+
let stderrText = "";
|
|
1178
|
+
let bufferedOutput = 0;
|
|
1179
|
+
let protocolError;
|
|
1180
|
+
let timedOut = false;
|
|
1181
|
+
let closed = false;
|
|
1182
|
+
const ignoreStreamError = (stream) => {
|
|
1183
|
+
stream?.on("error", () => void 0);
|
|
1184
|
+
};
|
|
1185
|
+
const appendOutput = (stream, chunk) => {
|
|
1186
|
+
bufferedOutput += chunk.length;
|
|
1187
|
+
if (bufferedOutput > MAX_OUTPUT_BYTES) {
|
|
1188
|
+
protocolError = `buffered output exceeded ${String(MAX_OUTPUT_BYTES)} bytes`;
|
|
1189
|
+
killExecTree(child);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
const text = chunk.toString("utf8");
|
|
1193
|
+
if (stream === "stdout") {
|
|
1194
|
+
stdoutText += text;
|
|
1195
|
+
} else {
|
|
1196
|
+
stderrText += text;
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
ignoreStreamError(child.stdout);
|
|
1200
|
+
ignoreStreamError(child.stderr);
|
|
1201
|
+
child.stdout?.on("data", (chunk) => {
|
|
1202
|
+
appendOutput("stdout", chunk);
|
|
1203
|
+
});
|
|
1204
|
+
child.stderr?.on("data", (chunk) => {
|
|
1205
|
+
appendOutput("stderr", chunk);
|
|
1206
|
+
});
|
|
1207
|
+
return await new Promise((resolve3, reject) => {
|
|
1208
|
+
const timer = setTimeout(() => {
|
|
1209
|
+
timedOut = true;
|
|
1210
|
+
killExecTree(child);
|
|
1211
|
+
}, timeoutMs);
|
|
1212
|
+
child.on("error", (error) => {
|
|
1213
|
+
if (closed) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
closed = true;
|
|
1217
|
+
clearTimeout(timer);
|
|
1218
|
+
SandboxManager.cleanupAfterCommand();
|
|
1219
|
+
reject(error);
|
|
1220
|
+
});
|
|
1221
|
+
child.on("close", (code) => {
|
|
1222
|
+
if (closed) {
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
closed = true;
|
|
1226
|
+
clearTimeout(timer);
|
|
1227
|
+
SandboxManager.cleanupAfterCommand();
|
|
1228
|
+
resolve3({
|
|
1229
|
+
stdoutText,
|
|
1230
|
+
stderrText,
|
|
1231
|
+
exitCode: typeof code === "number" ? code : timedOut ? 1 : 0,
|
|
1232
|
+
protocolError,
|
|
1233
|
+
timedOut,
|
|
1234
|
+
childPid: child.pid
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
var execFileAsync, SRT_VENDOR, MAX_OUTPUT_BYTES, activePlatform, codeModeSocketParentPath, COMMAND_PATH_BY_PLATFORM, SANDBOX_VENV_DIR, LOCAL_SANDBOX_ALLOWED_DOMAINS, SRT_HOST_BINARIES_BY_PLATFORM, ALLOW_READ_BY_PLATFORM, darwinUnixSocketSandboxRoots;
|
|
1240
|
+
var init_hostRun = __esm({
|
|
1241
|
+
"src/sandbox/local/core/hostRun.ts"() {
|
|
1242
|
+
"use strict";
|
|
1243
|
+
execFileAsync = promisify(execFile);
|
|
1244
|
+
SRT_VENDOR = join2(
|
|
1245
|
+
dirname2(createRequire(import.meta.url).resolve("@anthropic-ai/sandbox-runtime/package.json")),
|
|
1246
|
+
"vendor"
|
|
1247
|
+
);
|
|
1248
|
+
MAX_OUTPUT_BYTES = 14 * 1024 * 1024;
|
|
1249
|
+
COMMAND_PATH_BY_PLATFORM = {
|
|
1250
|
+
// On macOS, prefer Homebrew ahead of `/usr/bin` shims (those need Xcode select
|
|
1251
|
+
// paths that we intentionally do not allow-read).
|
|
1252
|
+
darwin: "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin",
|
|
1253
|
+
linux: "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
1254
|
+
};
|
|
1255
|
+
SANDBOX_VENV_DIR = ".venv";
|
|
1256
|
+
LOCAL_SANDBOX_ALLOWED_DOMAINS = [
|
|
1257
|
+
"pypi.org",
|
|
1258
|
+
"*.pypi.org",
|
|
1259
|
+
"pythonhosted.org",
|
|
1260
|
+
"files.pythonhosted.org",
|
|
1261
|
+
"*.pythonhosted.org",
|
|
1262
|
+
"github.com",
|
|
1263
|
+
"api.github.com",
|
|
1264
|
+
"codeload.github.com",
|
|
1265
|
+
"*.github.com",
|
|
1266
|
+
"githubusercontent.com",
|
|
1267
|
+
"raw.githubusercontent.com",
|
|
1268
|
+
"objects.githubusercontent.com",
|
|
1269
|
+
"*.githubusercontent.com"
|
|
1270
|
+
];
|
|
1271
|
+
SRT_HOST_BINARIES_BY_PLATFORM = {
|
|
1272
|
+
linux: ["bwrap", "socat", "rg"],
|
|
1273
|
+
darwin: []
|
|
1274
|
+
};
|
|
1275
|
+
ALLOW_READ_BY_PLATFORM = {
|
|
1276
|
+
darwin: [
|
|
1277
|
+
"/opt/homebrew/bin",
|
|
1278
|
+
"/usr/local",
|
|
1279
|
+
"/usr/bin",
|
|
1280
|
+
"/bin",
|
|
1281
|
+
"/usr/sbin",
|
|
1282
|
+
"/sbin",
|
|
1283
|
+
"/usr/lib",
|
|
1284
|
+
"/System/Library",
|
|
1285
|
+
"/Library",
|
|
1286
|
+
"/private/var/db/dyld",
|
|
1287
|
+
"/private/var/select",
|
|
1288
|
+
"/opt/homebrew",
|
|
1289
|
+
"/dev",
|
|
1290
|
+
// Darwin `/etc` is a symlink to `/private/etc`. SRT refuses that realpath as
|
|
1291
|
+
// "outside boundary" and keeps `(subpath "/etc")`. Seatbelt still checks the
|
|
1292
|
+
// resolved path, so Apple LibreSSL's fopen(`/private/etc/ssl/openssl.cnf`)
|
|
1293
|
+
// needs `/private/etc` listed too. Do not use a `[/]etc/**` glob: SRT treats
|
|
1294
|
+
// that as cwd-relative (`path.isAbsolute('[/]…')` is false).
|
|
1295
|
+
"/etc",
|
|
1296
|
+
"/private/etc"
|
|
1297
|
+
],
|
|
1298
|
+
linux: [
|
|
1299
|
+
"/usr/bin",
|
|
1300
|
+
"/bin",
|
|
1301
|
+
"/usr/sbin",
|
|
1302
|
+
"/sbin",
|
|
1303
|
+
"/lib",
|
|
1304
|
+
"/lib64",
|
|
1305
|
+
"/usr/lib",
|
|
1306
|
+
"/usr/lib64",
|
|
1307
|
+
"/usr/local",
|
|
1308
|
+
"/etc",
|
|
1309
|
+
"/dev",
|
|
1310
|
+
"/proc",
|
|
1311
|
+
"/sys",
|
|
1312
|
+
SRT_VENDOR
|
|
1313
|
+
]
|
|
1314
|
+
};
|
|
1315
|
+
darwinUnixSocketSandboxRoots = /* @__PURE__ */ new Set();
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
// src/sandbox/local/schemas/xferFileInfo.ts
|
|
1320
|
+
import { z as z6 } from "zod";
|
|
1321
|
+
var XferFileInfoSchema;
|
|
1322
|
+
var init_xferFileInfo = __esm({
|
|
1323
|
+
"src/sandbox/local/schemas/xferFileInfo.ts"() {
|
|
1324
|
+
"use strict";
|
|
1325
|
+
XferFileInfoSchema = z6.object({
|
|
1326
|
+
size: z6.number(),
|
|
1327
|
+
isDir: z6.boolean()
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
// src/sandbox/local/provider/LocalSandboxProvider.ts
|
|
1333
|
+
var LocalSandboxProvider_exports = {};
|
|
1334
|
+
__export(LocalSandboxProvider_exports, {
|
|
1335
|
+
LocalSandboxProvider: () => LocalSandboxProvider,
|
|
1336
|
+
formatLocalSandboxSupportReason: () => formatLocalSandboxSupportReason,
|
|
1337
|
+
localSandboxUploadCommand: () => localSandboxUploadCommand
|
|
1338
|
+
});
|
|
1339
|
+
import {
|
|
1340
|
+
absolutizeRelativeExecEnv,
|
|
1341
|
+
SandboxFileNotFoundError,
|
|
1342
|
+
SandboxFileTooLargeError,
|
|
1343
|
+
SandboxNotAvailableError,
|
|
1344
|
+
SandboxPathIsDirectoryError,
|
|
1345
|
+
shellEscape,
|
|
1346
|
+
validateNoPathTraversal as validateNoPathTraversal2
|
|
1347
|
+
} from "@truefoundry/trueforge-core/core";
|
|
1348
|
+
import { execFile as execFile2 } from "child_process";
|
|
1349
|
+
import { existsSync as existsSync4, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
|
|
1350
|
+
import { mkdir as mkdir4, mkdtemp, rm as rm4 } from "fs/promises";
|
|
1351
|
+
import { tmpdir } from "os";
|
|
1352
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve2, sep } from "path";
|
|
1353
|
+
import { promisify as promisify2 } from "util";
|
|
1354
|
+
import { ulid as ulid2 } from "ulid";
|
|
1355
|
+
function formatLocalSandboxSupportReason(params) {
|
|
1356
|
+
const details = params.attempts.map(formatLocalSandboxSupportAttempt).join("; ");
|
|
1357
|
+
return details.length === 0 ? params.summary : `${params.summary}: ${details}`;
|
|
1358
|
+
}
|
|
1359
|
+
function formatLocalSandboxSupportAttempt(attempt) {
|
|
1360
|
+
if (attempt.resolved === void 0) {
|
|
1361
|
+
return attempt.kind === "host" ? `${attempt.name}: not on PATH` : `${attempt.name}: not on sandbox PATH`;
|
|
1362
|
+
}
|
|
1363
|
+
const parts = [`${attempt.name}: resolved=${attempt.resolved}`];
|
|
1364
|
+
if (attempt.executable !== void 0 && attempt.executable !== attempt.resolved) {
|
|
1365
|
+
parts.push(`executable=${attempt.executable}`);
|
|
1366
|
+
}
|
|
1367
|
+
if (attempt.protocolError !== void 0) {
|
|
1368
|
+
parts.push(`protocolError=${attempt.protocolError}`);
|
|
1369
|
+
}
|
|
1370
|
+
if (attempt.exitCode !== void 0) {
|
|
1371
|
+
parts.push(`exit=${String(attempt.exitCode)}`);
|
|
1372
|
+
}
|
|
1373
|
+
if (attempt.timedOut === true) {
|
|
1374
|
+
parts.push("timedOut");
|
|
1375
|
+
}
|
|
1376
|
+
if (attempt.stderr !== void 0 && attempt.stderr.length > 0) {
|
|
1377
|
+
parts.push(`stderr=${JSON.stringify(attempt.stderr)}`);
|
|
1378
|
+
}
|
|
1379
|
+
if (attempt.stdout !== void 0 && attempt.stdout.length > 0) {
|
|
1380
|
+
parts.push(`stdout=${JSON.stringify(attempt.stdout)}`);
|
|
1381
|
+
}
|
|
1382
|
+
return parts.join(" ");
|
|
1383
|
+
}
|
|
1384
|
+
function probeAttemptFromSession(params) {
|
|
1385
|
+
return {
|
|
1386
|
+
kind: params.kind,
|
|
1387
|
+
name: params.name,
|
|
1388
|
+
resolved: params.resolved,
|
|
1389
|
+
...params.executable === void 0 ? {} : { executable: params.executable },
|
|
1390
|
+
exitCode: params.session.exitCode,
|
|
1391
|
+
stdout: params.session.stdoutText,
|
|
1392
|
+
stderr: params.session.stderrText,
|
|
1393
|
+
...params.session.protocolError === void 0 ? {} : { protocolError: params.session.protocolError },
|
|
1394
|
+
timedOut: params.session.timedOut
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
function unsupported(params) {
|
|
1398
|
+
return {
|
|
1399
|
+
supported: false,
|
|
1400
|
+
reason: params.reason,
|
|
1401
|
+
...params.platform === void 0 ? {} : { platform: params.platform },
|
|
1402
|
+
...params.attempts === void 0 ? {} : { attempts: params.attempts }
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
function toSandboxRelativePath(params) {
|
|
1406
|
+
const rel = relative(params.sandboxRootPath, params.absolutePath);
|
|
1407
|
+
return rel === "" ? "." : rel;
|
|
1408
|
+
}
|
|
1409
|
+
function isStrictDescendant(params) {
|
|
1410
|
+
const rel = relative(params.ancestor, params.descendant);
|
|
1411
|
+
return rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute3(rel);
|
|
1412
|
+
}
|
|
1413
|
+
function execFileErrorDetail(error) {
|
|
1414
|
+
if (!(error instanceof Error)) {
|
|
1415
|
+
return String(error);
|
|
1416
|
+
}
|
|
1417
|
+
const parts = [error.message];
|
|
1418
|
+
if ("stderr" in error && typeof error.stderr === "string" && error.stderr.length > 0) {
|
|
1419
|
+
parts.push(error.stderr);
|
|
1420
|
+
}
|
|
1421
|
+
if ("stdout" in error && typeof error.stdout === "string" && error.stdout.length > 0) {
|
|
1422
|
+
parts.push(error.stdout);
|
|
1423
|
+
}
|
|
1424
|
+
return parts.join(" ");
|
|
1425
|
+
}
|
|
1426
|
+
function sessionFailDetail(session) {
|
|
1427
|
+
return [session.protocolError, session.stderrText, session.stdoutText].filter((part) => part !== void 0 && part.length > 0).join(" ");
|
|
1428
|
+
}
|
|
1429
|
+
function localSandboxUploadCommand(remotePath) {
|
|
1430
|
+
const parent = dirname3(remotePath);
|
|
1431
|
+
return [
|
|
1432
|
+
`mkdir -p ${shellEscape(parent)}`,
|
|
1433
|
+
`{ chmod u+w ${shellEscape(remotePath)} 2>/dev/null || true; }`,
|
|
1434
|
+
`/bin/cat > ${shellEscape(remotePath)}`
|
|
1435
|
+
].join(" && ");
|
|
1436
|
+
}
|
|
1437
|
+
var execFileAsync2, DEFAULT_EXEC_TIMEOUT_SECONDS, DEFAULT_FILE_MAX_BYTES, SUPPORT_PROBE_TIMEOUT_MS, VENV_CREATE_TIMEOUT_MS, VENV_PIP_TIMEOUT_MS, VENV_PYDANTIC_PIN, SHELL_CANDIDATES, PYTHON_CANDIDATES, LocalSandboxProvider;
|
|
1438
|
+
var init_LocalSandboxProvider = __esm({
|
|
1439
|
+
"src/sandbox/local/provider/LocalSandboxProvider.ts"() {
|
|
1440
|
+
"use strict";
|
|
1441
|
+
init_CodeModeUdsTransport();
|
|
1442
|
+
init_hostRun();
|
|
1443
|
+
init_xferFileInfo();
|
|
1444
|
+
execFileAsync2 = promisify2(execFile2);
|
|
1445
|
+
DEFAULT_EXEC_TIMEOUT_SECONDS = 60;
|
|
1446
|
+
DEFAULT_FILE_MAX_BYTES = 10 * 1024 * 1024;
|
|
1447
|
+
SUPPORT_PROBE_TIMEOUT_MS = 5e3;
|
|
1448
|
+
VENV_CREATE_TIMEOUT_MS = 6e4;
|
|
1449
|
+
VENV_PIP_TIMEOUT_MS = 12e4;
|
|
1450
|
+
VENV_PYDANTIC_PIN = "pydantic>=2.0.0,<3.0.0";
|
|
1451
|
+
SHELL_CANDIDATES = ["bash", "sh"];
|
|
1452
|
+
PYTHON_CANDIDATES = ["python3", "python"];
|
|
1453
|
+
LocalSandboxProvider = class _LocalSandboxProvider {
|
|
1454
|
+
type = "local";
|
|
1455
|
+
sandboxRootPathParent;
|
|
1456
|
+
codeModeSocketParentPath;
|
|
1457
|
+
support;
|
|
1458
|
+
fileMaxBytesForDownload;
|
|
1459
|
+
defaultExecTimeoutSeconds;
|
|
1460
|
+
srtInitialized = false;
|
|
1461
|
+
logger;
|
|
1462
|
+
/** Local SRT has no image build step — always ready. */
|
|
1463
|
+
static readyBuild = {
|
|
1464
|
+
status: "ready",
|
|
1465
|
+
reason: null,
|
|
1466
|
+
metadata: null
|
|
1467
|
+
};
|
|
1468
|
+
/**
|
|
1469
|
+
* Probe whether this host can run LocalSandboxProvider
|
|
1470
|
+
* (OS + Code Mode UDS listen + SRT host binaries + in-sandbox shell + Python 3).
|
|
1471
|
+
* On success, returns platform/shell/python to pass into the constructor as `support`.
|
|
1472
|
+
*/
|
|
1473
|
+
static async isSupported(params) {
|
|
1474
|
+
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
1475
|
+
return unsupported({
|
|
1476
|
+
reason: `LocalSandboxProvider supports macOS and Linux only (got ${process.platform})`
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
const platform = process.platform;
|
|
1480
|
+
let createdSocketParent;
|
|
1481
|
+
const socketParentPath = params?.codeModeSocketParentPath;
|
|
1482
|
+
try {
|
|
1483
|
+
const parentPath = socketParentPath ?? await mkdtemp(join3("/tmp", "tfc-"));
|
|
1484
|
+
if (socketParentPath === void 0) {
|
|
1485
|
+
createdSocketParent = parentPath;
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
await probeCodeModeUnixSocket(parentPath);
|
|
1489
|
+
} catch (error) {
|
|
1490
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1491
|
+
return unsupported({
|
|
1492
|
+
platform,
|
|
1493
|
+
attempts: [{ kind: "socket", name: "uds", resolved: parentPath, protocolError: message }],
|
|
1494
|
+
reason: `Code Mode UDS listen failed: ${message}`
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
} finally {
|
|
1498
|
+
if (createdSocketParent !== void 0) {
|
|
1499
|
+
await rm4(createdSocketParent, { recursive: true, force: true });
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
const hostPath = process.env["PATH"];
|
|
1503
|
+
const hostAttempts = [];
|
|
1504
|
+
for (const name of srtHostBinaryNames(platform)) {
|
|
1505
|
+
const resolved = await resolveCommandOnHost({
|
|
1506
|
+
platform,
|
|
1507
|
+
name,
|
|
1508
|
+
...hostPath === void 0 || hostPath.length === 0 ? {} : { pathEnv: hostPath }
|
|
1509
|
+
});
|
|
1510
|
+
hostAttempts.push({ kind: "host", name, resolved });
|
|
1511
|
+
}
|
|
1512
|
+
if (hostAttempts.some((attempt) => attempt.resolved === void 0)) {
|
|
1513
|
+
const required = srtHostBinaryNames(platform).join(", ");
|
|
1514
|
+
return unsupported({
|
|
1515
|
+
platform,
|
|
1516
|
+
attempts: hostAttempts,
|
|
1517
|
+
reason: formatLocalSandboxSupportReason({
|
|
1518
|
+
summary: `SRT host dependencies missing (${platform}: ${required})`,
|
|
1519
|
+
attempts: hostAttempts
|
|
1520
|
+
})
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
const srtDeps = await checkSrtHostDependencies();
|
|
1524
|
+
if (srtDeps.errors.length > 0) {
|
|
1525
|
+
return unsupported({
|
|
1526
|
+
platform,
|
|
1527
|
+
attempts: hostAttempts.length === 0 ? void 0 : hostAttempts,
|
|
1528
|
+
reason: `SRT host dependencies failed: ${srtDeps.errors.join("; ")}`
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
const alreadyInitialized = isSrtInitialized();
|
|
1532
|
+
let probeRoot;
|
|
1533
|
+
const attempts = [...hostAttempts];
|
|
1534
|
+
const pythonAttempts = [];
|
|
1535
|
+
try {
|
|
1536
|
+
if (!alreadyInitialized) {
|
|
1537
|
+
await initSrt({
|
|
1538
|
+
platform,
|
|
1539
|
+
...socketParentPath === void 0 ? {} : { codeModeSocketParentPath: socketParentPath }
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
probeRoot = await createSandbox(await mkdtemp(join3(tmpdir(), "tfy-local-sandbox-support-")));
|
|
1543
|
+
let shell;
|
|
1544
|
+
for (const name of SHELL_CANDIDATES) {
|
|
1545
|
+
const resolved = await resolveCommandOnHost({ platform, name });
|
|
1546
|
+
if (resolved === void 0) {
|
|
1547
|
+
attempts.push({ kind: "shell", name, resolved: void 0 });
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
const probe = await runSupervisorSession({
|
|
1551
|
+
sandboxRootPath: probeRoot,
|
|
1552
|
+
platform,
|
|
1553
|
+
shell: resolved,
|
|
1554
|
+
command: "echo shell-ok",
|
|
1555
|
+
timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
|
|
1556
|
+
});
|
|
1557
|
+
const attempt = probeAttemptFromSession({ kind: "shell", name, resolved, session: probe });
|
|
1558
|
+
if (probe.protocolError === void 0 && probe.exitCode === 0 && probe.stdoutText.includes("shell-ok")) {
|
|
1559
|
+
shell = resolved;
|
|
1560
|
+
break;
|
|
1561
|
+
}
|
|
1562
|
+
attempts.push(attempt);
|
|
1563
|
+
}
|
|
1564
|
+
if (shell === void 0) {
|
|
1565
|
+
return unsupported({
|
|
1566
|
+
platform,
|
|
1567
|
+
attempts,
|
|
1568
|
+
reason: formatLocalSandboxSupportReason({
|
|
1569
|
+
summary: "No usable shell in sandbox (bash or sh via command -v)",
|
|
1570
|
+
attempts
|
|
1571
|
+
})
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
let python;
|
|
1575
|
+
for (const name of PYTHON_CANDIDATES) {
|
|
1576
|
+
const resolved = await resolveCommandOnHost({ platform, name });
|
|
1577
|
+
if (resolved === void 0) {
|
|
1578
|
+
pythonAttempts.push({ kind: "python", name, resolved: void 0 });
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const executable = await resolvePythonExecutableOnHost({ commandPath: resolved }) ?? resolved;
|
|
1582
|
+
const probe = await runSupervisorSession({
|
|
1583
|
+
sandboxRootPath: probeRoot,
|
|
1584
|
+
platform,
|
|
1585
|
+
shell,
|
|
1586
|
+
command: `${shellEscape(executable)} -c ${shellEscape(
|
|
1587
|
+
"import sys; raise SystemExit(0 if sys.version_info[0] == 3 else 1)"
|
|
1588
|
+
)}`,
|
|
1589
|
+
timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
|
|
1590
|
+
});
|
|
1591
|
+
const attempt = probeAttemptFromSession({
|
|
1592
|
+
kind: "python",
|
|
1593
|
+
name,
|
|
1594
|
+
resolved,
|
|
1595
|
+
executable,
|
|
1596
|
+
session: probe
|
|
1597
|
+
});
|
|
1598
|
+
if (probe.protocolError === void 0 && probe.exitCode === 0) {
|
|
1599
|
+
python = executable;
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
pythonAttempts.push(attempt);
|
|
1603
|
+
}
|
|
1604
|
+
if (python === void 0) {
|
|
1605
|
+
return unsupported({
|
|
1606
|
+
platform,
|
|
1607
|
+
attempts: pythonAttempts,
|
|
1608
|
+
reason: formatLocalSandboxSupportReason({
|
|
1609
|
+
summary: "No usable Python 3 interpreter in sandbox (python3 or python via command -v)",
|
|
1610
|
+
attempts: pythonAttempts
|
|
1611
|
+
})
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
return { supported: true, platform, shell, python };
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1617
|
+
const seen = [...attempts, ...pythonAttempts];
|
|
1618
|
+
return unsupported({
|
|
1619
|
+
platform,
|
|
1620
|
+
attempts: seen.length === 0 ? void 0 : seen,
|
|
1621
|
+
reason: seen.length === 0 ? message : `${message}: ${formatLocalSandboxSupportReason({ summary: "probe aborted", attempts: seen })}`
|
|
1622
|
+
});
|
|
1623
|
+
} finally {
|
|
1624
|
+
if (probeRoot !== void 0) {
|
|
1625
|
+
await removeSandbox(probeRoot);
|
|
1626
|
+
}
|
|
1627
|
+
if (!alreadyInitialized) {
|
|
1628
|
+
await resetSrt();
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
constructor(options) {
|
|
1633
|
+
if (!options.support.supported) {
|
|
1634
|
+
throw new Error(`LocalSandboxProvider is not supported: ${options.support.reason}`);
|
|
1635
|
+
}
|
|
1636
|
+
if (!isAbsolute3(options.sandboxRootPathParent)) {
|
|
1637
|
+
throw new Error("sandboxRootPathParent must be an absolute path");
|
|
1638
|
+
}
|
|
1639
|
+
validateNoPathTraversal2(options.sandboxRootPathParent);
|
|
1640
|
+
const resolvedParent = resolve2(options.sandboxRootPathParent);
|
|
1641
|
+
this.sandboxRootPathParent = existsSync4(resolvedParent) && statSync2(resolvedParent).isDirectory() ? realpathSync3(resolvedParent) : resolvedParent;
|
|
1642
|
+
this.codeModeSocketParentPath = assertCodeModeSocketParentPath(options.codeModeSocketParentPath);
|
|
1643
|
+
this.support = options.support;
|
|
1644
|
+
this.fileMaxBytesForDownload = options.fileMaxBytesForDownload ?? DEFAULT_FILE_MAX_BYTES;
|
|
1645
|
+
this.defaultExecTimeoutSeconds = options.defaultExecTimeoutSeconds ?? DEFAULT_EXEC_TIMEOUT_SECONDS;
|
|
1646
|
+
this.logger = options.logger.child({ module: "LocalSandboxProvider" });
|
|
1647
|
+
}
|
|
1648
|
+
pythonC(code, relPath) {
|
|
1649
|
+
return `${this.support.python} -c ${shellEscape(code)} ${shellEscape(relPath)}`;
|
|
1650
|
+
}
|
|
1651
|
+
statCommand(relPath) {
|
|
1652
|
+
const code = [
|
|
1653
|
+
"import json, os, sys",
|
|
1654
|
+
"p = sys.argv[1]",
|
|
1655
|
+
"st = os.stat(p)",
|
|
1656
|
+
'print(json.dumps({"size": st.st_size, "isDir": os.path.isdir(p)}))'
|
|
1657
|
+
].join("\n");
|
|
1658
|
+
return this.pythonC(code, relPath);
|
|
1659
|
+
}
|
|
1660
|
+
base64EncodeCommand(relPath) {
|
|
1661
|
+
const code = [
|
|
1662
|
+
"import base64, sys",
|
|
1663
|
+
"p = sys.argv[1]",
|
|
1664
|
+
'sys.stdout.write(base64.b64encode(open(p, "rb").read()).decode("ascii"))'
|
|
1665
|
+
].join("\n");
|
|
1666
|
+
return this.pythonC(code, relPath);
|
|
1667
|
+
}
|
|
1668
|
+
buildImage() {
|
|
1669
|
+
return Promise.resolve(_LocalSandboxProvider.readyBuild);
|
|
1670
|
+
}
|
|
1671
|
+
getImageBuildStatus() {
|
|
1672
|
+
return Promise.resolve(_LocalSandboxProvider.readyBuild);
|
|
1673
|
+
}
|
|
1674
|
+
async ensureSrt() {
|
|
1675
|
+
if (this.srtInitialized) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
await initSrt({
|
|
1679
|
+
platform: this.support.platform,
|
|
1680
|
+
codeModeSocketParentPath: this.codeModeSocketParentPath
|
|
1681
|
+
});
|
|
1682
|
+
this.srtInitialized = true;
|
|
1683
|
+
}
|
|
1684
|
+
/** Missing, non-directory, or not a child of this provider's parent → recreate path in Sandbox. */
|
|
1685
|
+
ensureSandboxRoot(sandboxRootPath) {
|
|
1686
|
+
if (!isAbsolute3(sandboxRootPath)) {
|
|
1687
|
+
throw new SandboxNotAvailableError(sandboxRootPath);
|
|
1688
|
+
}
|
|
1689
|
+
const resolved = resolve2(sandboxRootPath);
|
|
1690
|
+
if (!existsSync4(resolved) || !statSync2(resolved).isDirectory()) {
|
|
1691
|
+
throw new SandboxNotAvailableError(sandboxRootPath);
|
|
1692
|
+
}
|
|
1693
|
+
const parentReal = existsSync4(this.sandboxRootPathParent) ? realpathSync3(this.sandboxRootPathParent) : this.sandboxRootPathParent;
|
|
1694
|
+
const realRoot = realpathSync3(resolved);
|
|
1695
|
+
if (!isStrictDescendant({ ancestor: parentReal, descendant: realRoot })) {
|
|
1696
|
+
throw new SandboxNotAvailableError(sandboxRootPath);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Host `python -m venv` (stdlib only), then sandboxed pip for pydantic.
|
|
1701
|
+
* Idempotent: skip create when `.venv/bin/python` exists; skip pip when import works.
|
|
1702
|
+
*/
|
|
1703
|
+
async ensureVenv(sandboxRootPath) {
|
|
1704
|
+
const venvDir = join3(sandboxRootPath, SANDBOX_VENV_DIR);
|
|
1705
|
+
const venvPython = join3(venvDir, "bin", "python");
|
|
1706
|
+
if (!existsSync4(venvPython)) {
|
|
1707
|
+
try {
|
|
1708
|
+
await execFileAsync2(this.support.python, ["-m", "venv", venvDir], { timeout: VENV_CREATE_TIMEOUT_MS });
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
throw new Error(`Failed to create sandbox ${SANDBOX_VENV_DIR}: ${execFileErrorDetail(error)}`, {
|
|
1711
|
+
cause: error
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
const relPython = join3(SANDBOX_VENV_DIR, "bin", "python");
|
|
1716
|
+
const relPip = join3(SANDBOX_VENV_DIR, "bin", "pip");
|
|
1717
|
+
const check = await runSupervisorSession({
|
|
1718
|
+
sandboxRootPath,
|
|
1719
|
+
platform: this.support.platform,
|
|
1720
|
+
shell: this.support.shell,
|
|
1721
|
+
command: `${shellEscape(relPython)} -c ${shellEscape("import pydantic")}`,
|
|
1722
|
+
timeoutMs: SUPPORT_PROBE_TIMEOUT_MS
|
|
1723
|
+
});
|
|
1724
|
+
if (check.protocolError !== void 0) {
|
|
1725
|
+
throw new Error(`Sandbox venv pydantic check failed: ${check.protocolError}`);
|
|
1726
|
+
}
|
|
1727
|
+
if (check.exitCode === 0) {
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
const install = await runSupervisorSession({
|
|
1731
|
+
sandboxRootPath,
|
|
1732
|
+
platform: this.support.platform,
|
|
1733
|
+
shell: this.support.shell,
|
|
1734
|
+
command: `${shellEscape(relPip)} install --trusted-host pypi.org --trusted-host files.pythonhosted.org ${shellEscape(VENV_PYDANTIC_PIN)}`,
|
|
1735
|
+
timeoutMs: VENV_PIP_TIMEOUT_MS
|
|
1736
|
+
});
|
|
1737
|
+
if (install.protocolError !== void 0 || install.exitCode !== 0 || install.timedOut) {
|
|
1738
|
+
throw new Error(
|
|
1739
|
+
`Failed to pip install ${VENV_PYDANTIC_PIN} into sandbox ${SANDBOX_VENV_DIR}: ${sessionFailDetail(install)}`
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
resolveInSandboxRoot(sandboxRootPath, userPath) {
|
|
1744
|
+
validateNoPathTraversal2(userPath);
|
|
1745
|
+
const resolved = userPath.startsWith("/") ? resolve2(userPath) : resolve2(sandboxRootPath, userPath);
|
|
1746
|
+
const root = resolve2(sandboxRootPath);
|
|
1747
|
+
if (resolved !== root && !resolved.startsWith(root + sep)) {
|
|
1748
|
+
throw new SandboxFileNotFoundError(userPath);
|
|
1749
|
+
}
|
|
1750
|
+
return resolved;
|
|
1751
|
+
}
|
|
1752
|
+
async runSandboxCommand(params) {
|
|
1753
|
+
const session = await runSupervisorSession({
|
|
1754
|
+
sandboxRootPath: params.sandboxRootPath,
|
|
1755
|
+
platform: this.support.platform,
|
|
1756
|
+
shell: this.support.shell,
|
|
1757
|
+
command: params.command,
|
|
1758
|
+
...params.stdin === void 0 ? {} : { stdin: params.stdin },
|
|
1759
|
+
timeoutMs: this.defaultExecTimeoutSeconds * 1e3
|
|
1760
|
+
});
|
|
1761
|
+
if (session.protocolError !== void 0) {
|
|
1762
|
+
throw new Error(session.protocolError);
|
|
1763
|
+
}
|
|
1764
|
+
return {
|
|
1765
|
+
exitCode: session.exitCode,
|
|
1766
|
+
stdoutText: session.stdoutText,
|
|
1767
|
+
stderrText: session.stderrText
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
async getFileInfo(params) {
|
|
1771
|
+
const result = await this.runSandboxCommand({
|
|
1772
|
+
sandboxRootPath: params.sandboxRootPath,
|
|
1773
|
+
command: this.statCommand(params.relPath)
|
|
1774
|
+
});
|
|
1775
|
+
if (result.exitCode !== 0) {
|
|
1776
|
+
throw new SandboxFileNotFoundError(params.userPath);
|
|
1777
|
+
}
|
|
1778
|
+
return XferFileInfoSchema.parse(JSON.parse(result.stdoutText.trim()));
|
|
1779
|
+
}
|
|
1780
|
+
async createSandbox() {
|
|
1781
|
+
await this.ensureSrt();
|
|
1782
|
+
const sandboxId = await createSandbox(join3(this.sandboxRootPathParent, ulid2().toLowerCase()));
|
|
1783
|
+
this.logger.info("LocalSandboxProvider created sandbox", {
|
|
1784
|
+
sandboxId,
|
|
1785
|
+
shell: this.support.shell,
|
|
1786
|
+
python: this.support.python
|
|
1787
|
+
});
|
|
1788
|
+
await mkdir4(join3(sandboxId, this.getToolResultDumpDir()), { recursive: true, mode: 448 });
|
|
1789
|
+
await mkdir4(join3(sandboxId, this.getFileUploadsDir()), { recursive: true, mode: 448 });
|
|
1790
|
+
await mkdir4(join3(sandboxId, this.getSkillsDir()), { recursive: true, mode: 448 });
|
|
1791
|
+
await this.ensureVenv(sandboxId);
|
|
1792
|
+
return { sandboxId };
|
|
1793
|
+
}
|
|
1794
|
+
async exec(params) {
|
|
1795
|
+
this.ensureSandboxRoot(params.sandboxId);
|
|
1796
|
+
try {
|
|
1797
|
+
await this.ensureSrt();
|
|
1798
|
+
await this.ensureVenv(params.sandboxId);
|
|
1799
|
+
const sandboxRootPath = params.sandboxId;
|
|
1800
|
+
const cwd = params.cwd === void 0 || params.cwd === "" ? sandboxRootPath : this.resolveInSandboxRoot(sandboxRootPath, params.cwd);
|
|
1801
|
+
const timeoutSeconds = params.timeoutSeconds ?? this.defaultExecTimeoutSeconds;
|
|
1802
|
+
const env = params.env === void 0 ? void 0 : absolutizeRelativeExecEnv({ root: sandboxRootPath, env: params.env });
|
|
1803
|
+
const session = await runSupervisorSession({
|
|
1804
|
+
sandboxRootPath,
|
|
1805
|
+
platform: this.support.platform,
|
|
1806
|
+
shell: this.support.shell,
|
|
1807
|
+
command: params.command,
|
|
1808
|
+
cwd,
|
|
1809
|
+
...env === void 0 ? {} : { env },
|
|
1810
|
+
timeoutMs: timeoutSeconds * 1e3
|
|
1811
|
+
});
|
|
1812
|
+
if (session.protocolError !== void 0) {
|
|
1813
|
+
return { success: false, error: session.protocolError };
|
|
1814
|
+
}
|
|
1815
|
+
const result = session.stdoutText + (session.stderrText ? session.stderrText : "");
|
|
1816
|
+
return {
|
|
1817
|
+
success: true,
|
|
1818
|
+
response: { exitCode: session.exitCode, result }
|
|
1819
|
+
};
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
if (error instanceof SandboxNotAvailableError) {
|
|
1822
|
+
throw error;
|
|
1823
|
+
}
|
|
1824
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1825
|
+
return { success: false, error: message };
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
getAdditionalInstructions() {
|
|
1829
|
+
return [
|
|
1830
|
+
"SANDBOX RULES:",
|
|
1831
|
+
`- Platform: ${this.support.platform}.`,
|
|
1832
|
+
`- Commands run under the sandbox shell: ${this.support.shell}.`,
|
|
1833
|
+
"- A Python virtualenv lives at .venv. `python` and `pip` on PATH are that environment; packages you `pip install` persist in this sandbox.",
|
|
1834
|
+
"- uploads, skills, and tool-results live in the sandbox working directory.",
|
|
1835
|
+
"- ALL file creation and writes MUST stay within the sandbox working directory.",
|
|
1836
|
+
"- The Agent must NOT write outside the working directory (including host home and /tmp)."
|
|
1837
|
+
].join("\n");
|
|
1838
|
+
}
|
|
1839
|
+
// Cwd-relative: SRT cwd is the sandbox root. Init mkdir/ln and the agent prompt must not use
|
|
1840
|
+
// host-absolute paths — those contain spaces under macOS Application Support and lose quoting.
|
|
1841
|
+
// uploads, skills, tool-results, git_downloader.py, .git-credentials, .venv
|
|
1842
|
+
// mcp-client/mcp_client.py
|
|
1843
|
+
getToolResultDumpDir() {
|
|
1844
|
+
return "tool-results";
|
|
1845
|
+
}
|
|
1846
|
+
getGitCredentialsPath() {
|
|
1847
|
+
return ".git-credentials";
|
|
1848
|
+
}
|
|
1849
|
+
getFileUploadsDir() {
|
|
1850
|
+
return "uploads";
|
|
1851
|
+
}
|
|
1852
|
+
getSkillsDir() {
|
|
1853
|
+
return "skills";
|
|
1854
|
+
}
|
|
1855
|
+
getGitDownloaderPath() {
|
|
1856
|
+
return "git_downloader.py";
|
|
1857
|
+
}
|
|
1858
|
+
async downloadFile(params) {
|
|
1859
|
+
this.ensureSandboxRoot(params.sandboxId);
|
|
1860
|
+
await this.ensureSrt();
|
|
1861
|
+
await this.ensureVenv(params.sandboxId);
|
|
1862
|
+
const sandboxRootPath = params.sandboxId;
|
|
1863
|
+
const absolutePath = this.resolveInSandboxRoot(sandboxRootPath, params.path);
|
|
1864
|
+
const relPath = toSandboxRelativePath({ sandboxRootPath, absolutePath });
|
|
1865
|
+
const info = await this.getFileInfo({ sandboxRootPath, relPath, userPath: params.path });
|
|
1866
|
+
if (info.isDir) {
|
|
1867
|
+
throw new SandboxPathIsDirectoryError(params.path);
|
|
1868
|
+
}
|
|
1869
|
+
if (info.size > this.fileMaxBytesForDownload) {
|
|
1870
|
+
throw new SandboxFileTooLargeError(params.path, info.size, this.fileMaxBytesForDownload);
|
|
1871
|
+
}
|
|
1872
|
+
const result = await this.runSandboxCommand({
|
|
1873
|
+
sandboxRootPath,
|
|
1874
|
+
command: this.base64EncodeCommand(relPath)
|
|
1875
|
+
});
|
|
1876
|
+
if (result.exitCode !== 0) {
|
|
1877
|
+
throw new SandboxFileNotFoundError(params.path);
|
|
1878
|
+
}
|
|
1879
|
+
const buf = Buffer.from(result.stdoutText.trim(), "base64");
|
|
1880
|
+
if (buf.length > this.fileMaxBytesForDownload) {
|
|
1881
|
+
throw new SandboxFileTooLargeError(params.path, buf.length, this.fileMaxBytesForDownload);
|
|
1882
|
+
}
|
|
1883
|
+
return buf;
|
|
1884
|
+
}
|
|
1885
|
+
/** Payload on stdin so large uploads stay off argv. */
|
|
1886
|
+
async uploadFile(params) {
|
|
1887
|
+
this.ensureSandboxRoot(params.sandboxId);
|
|
1888
|
+
await this.ensureSrt();
|
|
1889
|
+
await this.ensureVenv(params.sandboxId);
|
|
1890
|
+
if (params.content.length > this.fileMaxBytesForDownload) {
|
|
1891
|
+
throw new SandboxFileTooLargeError(params.remotePath, params.content.length, this.fileMaxBytesForDownload);
|
|
1892
|
+
}
|
|
1893
|
+
const sandboxRootPath = params.sandboxId;
|
|
1894
|
+
const absolutePath = this.resolveInSandboxRoot(sandboxRootPath, params.remotePath);
|
|
1895
|
+
const remotePath = toSandboxRelativePath({ sandboxRootPath, absolutePath });
|
|
1896
|
+
const result = await this.runSandboxCommand({
|
|
1897
|
+
sandboxRootPath,
|
|
1898
|
+
command: localSandboxUploadCommand(remotePath),
|
|
1899
|
+
stdin: params.content
|
|
1900
|
+
});
|
|
1901
|
+
if (result.exitCode !== 0) {
|
|
1902
|
+
const detail = [result.stderrText, result.stdoutText].filter((part) => part.length > 0).join(" ");
|
|
1903
|
+
throw new Error(
|
|
1904
|
+
detail.length > 0 ? `upload ${params.remotePath} failed (exit ${String(result.exitCode)}): ${detail}` : `upload ${params.remotePath} failed (exit ${String(result.exitCode)})`
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
createCodeModeTransport() {
|
|
1909
|
+
return new CodeModeUdsTransport({
|
|
1910
|
+
codeModeSocketParentPath: this.codeModeSocketParentPath,
|
|
1911
|
+
clientRemotePath: () => join3("mcp-client", "mcp_client.py")
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
/** Reset process-scoped SRT for this provider. */
|
|
1915
|
+
async dispose() {
|
|
1916
|
+
if (this.srtInitialized) {
|
|
1917
|
+
await resetSrt();
|
|
1918
|
+
this.srtInitialized = false;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
333
1922
|
}
|
|
334
1923
|
});
|
|
335
1924
|
|
|
@@ -740,10 +2329,10 @@ async function assembleTurnRecord(db, args) {
|
|
|
740
2329
|
}
|
|
741
2330
|
const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(
|
|
742
2331
|
"turn_thread_context as ttc",
|
|
743
|
-
(
|
|
2332
|
+
(join5) => join5.on("ttc.session_id", "=", args.session_id).on("ttc.turn_id", "=", args.turn_id).onRef("ttc.thread_id", "=", "tt.thread_id")
|
|
744
2333
|
).leftJoin(
|
|
745
2334
|
"thread_context_log as l",
|
|
746
|
-
(
|
|
2335
|
+
(join5) => join5.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "ttc.append_id")
|
|
747
2336
|
).select([
|
|
748
2337
|
"tt.thread_id",
|
|
749
2338
|
jsonText(sql2.ref("tt.checkpoint")).as("checkpoint"),
|
|
@@ -840,10 +2429,10 @@ async function createTurn(db, input) {
|
|
|
840
2429
|
if (prevTurnId != null) {
|
|
841
2430
|
const prevRows = await trx.selectFrom("turn as t").leftJoin(
|
|
842
2431
|
"turn_thread as tt",
|
|
843
|
-
(
|
|
2432
|
+
(join5) => join5.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
|
|
844
2433
|
).leftJoin(
|
|
845
2434
|
db.selectFrom("turn_thread_context").select(["thread_id", "turn_id", sql2`MAX(pos)`.as("max_pos")]).where("session_id", "=", input.session_id).where("turn_id", "=", prevTurnId).groupBy(["thread_id", "turn_id"]).as("tc_agg"),
|
|
846
|
-
(
|
|
2435
|
+
(join5) => join5.onRef("tc_agg.thread_id", "=", "tt.thread_id").onRef("tc_agg.turn_id", "=", "tt.turn_id")
|
|
847
2436
|
).select([
|
|
848
2437
|
jsonText(sql2.ref("t.checkpoint")).as("turn_checkpoint"),
|
|
849
2438
|
jsonText(sql2.ref("t.state")).as("turn_state"),
|
|
@@ -1212,7 +2801,7 @@ async function listTurnEvents(db, input) {
|
|
|
1212
2801
|
const eventOrder = input.order === "desc" ? "desc" : "asc";
|
|
1213
2802
|
const rows = await db.selectFrom("turn as t").leftJoin(
|
|
1214
2803
|
(eb) => eb.selectFrom("session_event").select(["session_id", "turn_id", "event_id", jsonText(sql4.ref("event")).as("event")]).where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).orderBy("event_id", eventOrder).limit(limit + 1).offset(offset).as("e"),
|
|
1215
|
-
(
|
|
2804
|
+
(join5) => join5.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
|
|
1216
2805
|
).select(["t.turn_id", "e.event"]).where("t.session_id", "=", input.session_id).where("t.turn_id", "=", input.turn_id).orderBy("e.event_id", eventOrder).execute();
|
|
1217
2806
|
if (rows.length === 0) {
|
|
1218
2807
|
throw new TurnNotFoundError3(input.turn_id);
|
|
@@ -1272,7 +2861,7 @@ async function listSessionEvents(db, input) {
|
|
|
1272
2861
|
const chainJson = JSON.stringify(chainIds);
|
|
1273
2862
|
const rows = await db.selectFrom(sql4`json_each(${chainJson})`.as("c")).innerJoin(
|
|
1274
2863
|
"session_event as e",
|
|
1275
|
-
(
|
|
2864
|
+
(join5) => join5.on("e.session_id", "=", input.session_id).on(sql4`e.turn_id = c.value`)
|
|
1276
2865
|
).select([sql4`c.value`.as("turn_id"), jsonText(sql4.ref("e.event")).as("event")]).orderBy(sql4`c.key`, "desc").orderBy("e.event_id", "desc").limit(limit + 1).offset(cursor.offset).execute();
|
|
1277
2866
|
return paginateSessionEventRows(rows, limit, cursor);
|
|
1278
2867
|
}
|
|
@@ -1958,7 +3547,7 @@ var SqliteMcpServerStore_exports = {};
|
|
|
1958
3547
|
__export(SqliteMcpServerStore_exports, {
|
|
1959
3548
|
SqliteMcpServerStore: () => SqliteMcpServerStore
|
|
1960
3549
|
});
|
|
1961
|
-
import { ulid as
|
|
3550
|
+
import { ulid as ulid5 } from "ulid";
|
|
1962
3551
|
function recordColumns2(eb) {
|
|
1963
3552
|
return [
|
|
1964
3553
|
"id",
|
|
@@ -2008,7 +3597,7 @@ var init_SqliteMcpServerStore = __esm({
|
|
|
2008
3597
|
const timestamp = nowIso();
|
|
2009
3598
|
try {
|
|
2010
3599
|
return await db.insertInto("mcp_server").values({
|
|
2011
|
-
id:
|
|
3600
|
+
id: ulid5(),
|
|
2012
3601
|
tenant_id: input.tenant_id,
|
|
2013
3602
|
name: input.name,
|
|
2014
3603
|
manifest: jsonbBind(input.manifest),
|
|
@@ -2028,7 +3617,7 @@ var init_SqliteMcpServerStore = __esm({
|
|
|
2028
3617
|
const db = transaction ?? this.#db;
|
|
2029
3618
|
const timestamp = nowIso();
|
|
2030
3619
|
return await db.insertInto("mcp_server").values({
|
|
2031
|
-
id:
|
|
3620
|
+
id: ulid5(),
|
|
2032
3621
|
tenant_id: input.tenant_id,
|
|
2033
3622
|
name: input.name,
|
|
2034
3623
|
manifest: jsonbBind(input.manifest),
|
|
@@ -2358,7 +3947,7 @@ var SqliteAgentStore_exports = {};
|
|
|
2358
3947
|
__export(SqliteAgentStore_exports, {
|
|
2359
3948
|
SqliteAgentStore: () => SqliteAgentStore
|
|
2360
3949
|
});
|
|
2361
|
-
import { ulid as
|
|
3950
|
+
import { ulid as ulid6 } from "ulid";
|
|
2362
3951
|
function recordColumns5(eb) {
|
|
2363
3952
|
return [
|
|
2364
3953
|
"id",
|
|
@@ -2405,7 +3994,7 @@ var init_SqliteAgentStore = __esm({
|
|
|
2405
3994
|
const timestamp = nowIso();
|
|
2406
3995
|
try {
|
|
2407
3996
|
const row = await db.insertInto("agent").values({
|
|
2408
|
-
id:
|
|
3997
|
+
id: ulid6().toLowerCase(),
|
|
2409
3998
|
tenant_id: input.tenant_id,
|
|
2410
3999
|
name: input.name,
|
|
2411
4000
|
manifest: jsonbBind(input.manifest),
|
|
@@ -2669,9 +4258,9 @@ async function assembleTurnRecord2(db, args) {
|
|
|
2669
4258
|
if (!turn) {
|
|
2670
4259
|
return void 0;
|
|
2671
4260
|
}
|
|
2672
|
-
const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(lateralUnnestBigintArrayWithOrdinality(sql11`tt.context_ids`, "c"), (
|
|
4261
|
+
const contextRows = await db.selectFrom("turn_thread as tt").leftJoin(lateralUnnestBigintArrayWithOrdinality(sql11`tt.context_ids`, "c"), (join5) => join5.onTrue()).leftJoin(
|
|
2673
4262
|
"thread_context_log as l",
|
|
2674
|
-
(
|
|
4263
|
+
(join5) => join5.on("l.session_id", "=", args.session_id).onRef("l.thread_id", "=", "tt.thread_id").onRef("l.append_id", "=", "c.append_id")
|
|
2675
4264
|
).select(["tt.thread_id", "tt.checkpoint", "tt.agent_info", "tt.current_context_usage", "l.body", "c.pos"]).where("tt.session_id", "=", args.session_id).where("tt.turn_id", "=", args.turn_id).orderBy("tt.thread_id").orderBy("c.pos").execute();
|
|
2676
4265
|
const capabilityRows = await db.selectFrom("thread_capability_state").select(["thread_id", sql11`jsonb_object_agg(key, state)`.as("capability_state")]).where("session_id", "=", args.session_id).where("turn_id", "=", args.turn_id).groupBy("thread_id").execute();
|
|
2677
4266
|
const capabilityByThread = /* @__PURE__ */ new Map();
|
|
@@ -2754,7 +4343,7 @@ async function createTurn2(db, input) {
|
|
|
2754
4343
|
if (prevTurnId != null) {
|
|
2755
4344
|
const prevRows = await trx.selectFrom("turn as t").leftJoin(
|
|
2756
4345
|
"turn_thread as tt",
|
|
2757
|
-
(
|
|
4346
|
+
(join5) => join5.onRef("tt.session_id", "=", "t.session_id").onRef("tt.turn_id", "=", "t.turn_id")
|
|
2758
4347
|
).select([
|
|
2759
4348
|
"t.checkpoint as turn_checkpoint",
|
|
2760
4349
|
"t.state as turn_state",
|
|
@@ -3076,7 +4665,7 @@ async function listTurnEvents2(db, input) {
|
|
|
3076
4665
|
const eventOrder = input.order === "desc" ? "desc" : "asc";
|
|
3077
4666
|
const rows = await db.selectFrom("turn as t").leftJoin(
|
|
3078
4667
|
(eb) => eb.selectFrom("session_event").select(["session_id", "turn_id", "event_id", "event"]).where("session_id", "=", input.session_id).where("turn_id", "=", input.turn_id).orderBy("event_id", eventOrder).limit(limit + 1).offset(offset).as("e"),
|
|
3079
|
-
(
|
|
4668
|
+
(join5) => join5.onRef("e.session_id", "=", "t.session_id").onRef("e.turn_id", "=", "t.turn_id")
|
|
3080
4669
|
).select(["t.turn_id", "e.event"]).where("t.session_id", "=", input.session_id).where("t.turn_id", "=", input.turn_id).orderBy("e.event_id", eventOrder).execute();
|
|
3081
4670
|
if (rows.length === 0) {
|
|
3082
4671
|
throw new TurnNotFoundError5(input.turn_id);
|
|
@@ -3111,7 +4700,7 @@ async function listSessionEvents2(db, input) {
|
|
|
3111
4700
|
const chainIds = await resolveAncestorChain2(db, input.session_id, anchor);
|
|
3112
4701
|
const rows = await db.selectFrom(unnestWithOrdinality(chainIds, "c")).innerJoin(
|
|
3113
4702
|
"session_event as e",
|
|
3114
|
-
(
|
|
4703
|
+
(join5) => join5.on("e.session_id", "=", input.session_id).onRef("e.turn_id", "=", "c.turn_id")
|
|
3115
4704
|
).select(["e.turn_id as turn_id", "e.event as event"]).orderBy("c.pos", "desc").orderBy("e.event_id", "desc").limit(limit + 1).offset(cursor.offset).execute();
|
|
3116
4705
|
return paginateSessionEventRows2(rows, limit, cursor);
|
|
3117
4706
|
}
|
|
@@ -3751,7 +5340,7 @@ var PostgresMcpServerStore_exports = {};
|
|
|
3751
5340
|
__export(PostgresMcpServerStore_exports, {
|
|
3752
5341
|
PostgresMcpServerStore: () => PostgresMcpServerStore
|
|
3753
5342
|
});
|
|
3754
|
-
import { ulid as
|
|
5343
|
+
import { ulid as ulid7 } from "ulid";
|
|
3755
5344
|
function toRecord3(row) {
|
|
3756
5345
|
return {
|
|
3757
5346
|
id: row.id,
|
|
@@ -3799,7 +5388,7 @@ var init_PostgresMcpServerStore = __esm({
|
|
|
3799
5388
|
const db = transaction ?? this.#db;
|
|
3800
5389
|
try {
|
|
3801
5390
|
const row = await db.insertInto("mcp_server").values({
|
|
3802
|
-
id:
|
|
5391
|
+
id: ulid7(),
|
|
3803
5392
|
tenant_id: input.tenant_id,
|
|
3804
5393
|
name: input.name,
|
|
3805
5394
|
manifest: json(input.manifest),
|
|
@@ -3819,7 +5408,7 @@ var init_PostgresMcpServerStore = __esm({
|
|
|
3819
5408
|
async upsertServer(input, transaction) {
|
|
3820
5409
|
const db = transaction ?? this.#db;
|
|
3821
5410
|
const row = await db.insertInto("mcp_server").values({
|
|
3822
|
-
id:
|
|
5411
|
+
id: ulid7(),
|
|
3823
5412
|
tenant_id: input.tenant_id,
|
|
3824
5413
|
name: input.name,
|
|
3825
5414
|
manifest: json(input.manifest),
|
|
@@ -4142,7 +5731,7 @@ var PostgresAgentStore_exports = {};
|
|
|
4142
5731
|
__export(PostgresAgentStore_exports, {
|
|
4143
5732
|
PostgresAgentStore: () => PostgresAgentStore
|
|
4144
5733
|
});
|
|
4145
|
-
import { ulid as
|
|
5734
|
+
import { ulid as ulid8 } from "ulid";
|
|
4146
5735
|
function toRecord6(row) {
|
|
4147
5736
|
return {
|
|
4148
5737
|
id: row.id,
|
|
@@ -4185,7 +5774,7 @@ var init_PostgresAgentStore = __esm({
|
|
|
4185
5774
|
const db = transaction ?? this.#db;
|
|
4186
5775
|
try {
|
|
4187
5776
|
const row = await db.insertInto("agent").values({
|
|
4188
|
-
id:
|
|
5777
|
+
id: ulid8().toLowerCase(),
|
|
4189
5778
|
tenant_id: input.tenant_id,
|
|
4190
5779
|
name: input.name,
|
|
4191
5780
|
manifest: json(input.manifest),
|
|
@@ -4218,8 +5807,41 @@ var init_PostgresAgentStore = __esm({
|
|
|
4218
5807
|
|
|
4219
5808
|
// src/main.ts
|
|
4220
5809
|
import { extractErrorLogFields as extractErrorLogFields9 } from "@truefoundry/trueforge-core/core";
|
|
4221
|
-
import { mkdir } from "fs/promises";
|
|
5810
|
+
import { mkdir as mkdir5 } from "fs/promises";
|
|
4222
5811
|
import path6 from "path";
|
|
5812
|
+
|
|
5813
|
+
// src/sandbox/localLifecycle.ts
|
|
5814
|
+
import { existsSync } from "fs";
|
|
5815
|
+
import { mkdir, rm } from "fs/promises";
|
|
5816
|
+
var SOCKET_PARENT_MODE = 448;
|
|
5817
|
+
async function prepareCodeModeSocketParent(params) {
|
|
5818
|
+
if (existsSync(params.path)) {
|
|
5819
|
+
params.logger.warn("Removing leftover Code Mode socket parent", { path: params.path });
|
|
5820
|
+
await rm(params.path, { recursive: true, force: true });
|
|
5821
|
+
}
|
|
5822
|
+
await mkdir(params.path, { recursive: true, mode: SOCKET_PARENT_MODE });
|
|
5823
|
+
}
|
|
5824
|
+
async function removeCodeModeSocketParent(socketParentPath) {
|
|
5825
|
+
await rm(socketParentPath, { recursive: true, force: true });
|
|
5826
|
+
}
|
|
5827
|
+
async function ensureLocalSandboxRootParent(sandboxRootPathParent) {
|
|
5828
|
+
await mkdir(sandboxRootPathParent, { recursive: true, mode: SOCKET_PARENT_MODE });
|
|
5829
|
+
}
|
|
5830
|
+
|
|
5831
|
+
// src/sandbox/localRuntime.ts
|
|
5832
|
+
init_config();
|
|
5833
|
+
var cachedSupport;
|
|
5834
|
+
function setCachedLocalSandboxSupport(support) {
|
|
5835
|
+
cachedSupport = support;
|
|
5836
|
+
}
|
|
5837
|
+
function getCachedLocalSandboxSupport() {
|
|
5838
|
+
return cachedSupport;
|
|
5839
|
+
}
|
|
5840
|
+
function isLocalSandboxFallbackEnabled() {
|
|
5841
|
+
return config_default.STANDALONE && cachedSupport?.supported === true;
|
|
5842
|
+
}
|
|
5843
|
+
|
|
5844
|
+
// src/main.ts
|
|
4223
5845
|
import { serve } from "@hono/node-server";
|
|
4224
5846
|
import {
|
|
4225
5847
|
CancellationReason as CancellationReason4,
|
|
@@ -4229,7 +5851,7 @@ import { RequestReplyExecutor, RequestReplyRouter } from "@truefoundry/trueforge
|
|
|
4229
5851
|
|
|
4230
5852
|
// src/app.ts
|
|
4231
5853
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
4232
|
-
import { OpenAPIHono as OpenAPIHono14, z as
|
|
5854
|
+
import { OpenAPIHono as OpenAPIHono14, z as z26 } from "@hono/zod-openapi";
|
|
4233
5855
|
import { extractErrorLogFields as extractErrorLogFields7 } from "@truefoundry/trueforge-core/core";
|
|
4234
5856
|
import { bodyLimit } from "hono/body-limit";
|
|
4235
5857
|
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
@@ -4251,6 +5873,8 @@ var NameSchema = z.string().min(2).max(64).regex(
|
|
|
4251
5873
|
/^[a-z](?:[a-z0-9._-]{0,62}[a-z0-9])$/,
|
|
4252
5874
|
'must be 2\u201364 lowercase chars: start with a letter, end with alphanumeric, optionally separated by ".", "_" or "-"'
|
|
4253
5875
|
).openapi("ResourceName");
|
|
5876
|
+
var PAGE_LIMIT = 25;
|
|
5877
|
+
var EVENTS_PAGE_LIMIT = 100;
|
|
4254
5878
|
function uniqueNames(entries, ctx) {
|
|
4255
5879
|
const seen = /* @__PURE__ */ new Set();
|
|
4256
5880
|
for (const entry of entries) {
|
|
@@ -4281,9 +5905,9 @@ var CreateAgentRequestSchema = z2.object({
|
|
|
4281
5905
|
name: NameSchema,
|
|
4282
5906
|
manifest: AgentSpecSchema2
|
|
4283
5907
|
}).strict().openapi("CreateAgentRequest");
|
|
4284
|
-
var
|
|
5908
|
+
var UpdateAgentRequestSchema = z2.object({
|
|
4285
5909
|
manifest: AgentSpecSchema2
|
|
4286
|
-
}).strict().openapi("
|
|
5910
|
+
}).strict().openapi("UpdateAgentRequest");
|
|
4287
5911
|
var AgentSchema = z2.object({
|
|
4288
5912
|
id: z2.string().min(1).describe("Immutable server-generated agent identifier."),
|
|
4289
5913
|
name: NameSchema,
|
|
@@ -4304,15 +5928,26 @@ var RequestErrorResponseSchema = z3.object({
|
|
|
4304
5928
|
})
|
|
4305
5929
|
}).openapi("RequestErrorResponse");
|
|
4306
5930
|
|
|
5931
|
+
// src/routes/openapiTags.ts
|
|
5932
|
+
var OPENAPI_DOCUMENT_TAGS = [
|
|
5933
|
+
{ name: "Auth" /* AUTH */ },
|
|
5934
|
+
{ name: "Capabilities" /* CAPABILITIES */ },
|
|
5935
|
+
{ name: "Models" /* MODELS */ },
|
|
5936
|
+
{ name: "MCP Servers" /* MCP_SERVERS */ },
|
|
5937
|
+
{ name: "Skills" /* SKILLS */ },
|
|
5938
|
+
{ name: "Sandboxes" /* SANDBOXES */ },
|
|
5939
|
+
{ name: "Agents" /* AGENTS */ },
|
|
5940
|
+
{ name: "Agent Sessions" /* AGENT_SESSIONS */ }
|
|
5941
|
+
];
|
|
5942
|
+
|
|
4307
5943
|
// src/routes/agentRoutes.ts
|
|
4308
|
-
var AGENTS_TAG = "Agents";
|
|
4309
5944
|
var AgentIdParamsSchema = z4.object({
|
|
4310
5945
|
agent_id: z4.string().min(1).max(64).describe("Immutable agent identifier.")
|
|
4311
5946
|
});
|
|
4312
5947
|
var listAgentsRoute = createRoute({
|
|
4313
5948
|
method: "get",
|
|
4314
5949
|
path: "/",
|
|
4315
|
-
tags: [
|
|
5950
|
+
tags: ["Agents" /* AGENTS */],
|
|
4316
5951
|
summary: "List agents",
|
|
4317
5952
|
description: "All configured agents for the tenant.",
|
|
4318
5953
|
"x-fern-sdk-group-name": ["agents"],
|
|
@@ -4331,7 +5966,7 @@ var listAgentsRoute = createRoute({
|
|
|
4331
5966
|
var createAgentRoute = createRoute({
|
|
4332
5967
|
method: "post",
|
|
4333
5968
|
path: "/",
|
|
4334
|
-
tags: [
|
|
5969
|
+
tags: ["Agents" /* AGENTS */],
|
|
4335
5970
|
summary: "Create an agent",
|
|
4336
5971
|
description: "Creates an agent and allocates an immutable id. Fails if `name` is already taken. Name cannot be changed later.",
|
|
4337
5972
|
"x-fern-sdk-group-name": ["agents"],
|
|
@@ -4364,7 +5999,7 @@ var createAgentRoute = createRoute({
|
|
|
4364
5999
|
var getAgentRoute = createRoute({
|
|
4365
6000
|
method: "get",
|
|
4366
6001
|
path: "/{agent_id}",
|
|
4367
|
-
tags: [
|
|
6002
|
+
tags: ["Agents" /* AGENTS */],
|
|
4368
6003
|
summary: "Get an agent",
|
|
4369
6004
|
description: "Fetch a configured agent by immutable id.",
|
|
4370
6005
|
"x-fern-sdk-group-name": ["agents"],
|
|
@@ -4386,7 +6021,7 @@ var getAgentRoute = createRoute({
|
|
|
4386
6021
|
var deleteAgentRoute = createRoute({
|
|
4387
6022
|
method: "delete",
|
|
4388
6023
|
path: "/{agent_id}",
|
|
4389
|
-
tags: [
|
|
6024
|
+
tags: ["Agents" /* AGENTS */],
|
|
4390
6025
|
summary: "Delete an agent",
|
|
4391
6026
|
description: "Delete a configured agent by immutable id. Idempotent if already gone.",
|
|
4392
6027
|
"x-fern-sdk-group-name": ["agents"],
|
|
@@ -4408,7 +6043,7 @@ var deleteAgentRoute = createRoute({
|
|
|
4408
6043
|
var putAgentRoute = createRoute({
|
|
4409
6044
|
method: "put",
|
|
4410
6045
|
path: "/{agent_id}",
|
|
4411
|
-
tags: [
|
|
6046
|
+
tags: ["Agents" /* AGENTS */],
|
|
4412
6047
|
summary: "Update an agent",
|
|
4413
6048
|
description: "Replaces the manifest for an existing agent keyed by immutable `agent_id`.",
|
|
4414
6049
|
"x-fern-sdk-group-name": ["agents"],
|
|
@@ -4416,7 +6051,7 @@ var putAgentRoute = createRoute({
|
|
|
4416
6051
|
request: {
|
|
4417
6052
|
params: AgentIdParamsSchema,
|
|
4418
6053
|
body: {
|
|
4419
|
-
content: { "application/json": { schema:
|
|
6054
|
+
content: { "application/json": { schema: UpdateAgentRequestSchema } },
|
|
4420
6055
|
required: true
|
|
4421
6056
|
}
|
|
4422
6057
|
},
|
|
@@ -4447,6 +6082,7 @@ import {
|
|
|
4447
6082
|
SkillMounter
|
|
4448
6083
|
} from "@truefoundry/trueforge-core/core";
|
|
4449
6084
|
import { HTTPException } from "hono/http-exception";
|
|
6085
|
+
import { join as join4 } from "path";
|
|
4450
6086
|
|
|
4451
6087
|
// src/mcp/auth/mcpDcr.ts
|
|
4452
6088
|
import {
|
|
@@ -4748,46 +6384,49 @@ async function completeMcpAuthorization(params) {
|
|
|
4748
6384
|
});
|
|
4749
6385
|
}
|
|
4750
6386
|
|
|
6387
|
+
// src/runtime/sessionResources.ts
|
|
6388
|
+
init_LocalSandboxProvider();
|
|
6389
|
+
|
|
4751
6390
|
// src/sandbox/providerUtils.ts
|
|
4752
6391
|
init_config();
|
|
4753
6392
|
import { Daytona, DaytonaError } from "@daytona/sdk";
|
|
4754
6393
|
import { DaytonaSandboxProvider, SANDBOX_IMAGE_URI } from "@truefoundry/trueforge-core/core";
|
|
4755
6394
|
|
|
4756
6395
|
// src/schemas/sandboxProvider.ts
|
|
4757
|
-
import { z as
|
|
4758
|
-
var DaytonaSandboxProviderAuthSchema =
|
|
4759
|
-
api_key:
|
|
6396
|
+
import { z as z7 } from "@hono/zod-openapi";
|
|
6397
|
+
var DaytonaSandboxProviderAuthSchema = z7.object({
|
|
6398
|
+
api_key: z7.string().min(1).describe(
|
|
4760
6399
|
"Daytona API key. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored key."
|
|
4761
6400
|
)
|
|
4762
6401
|
}).strict().describe("Daytona authentication credentials.").openapi("DaytonaSandboxProviderAuth");
|
|
4763
|
-
var DaytonaSandboxProviderSchema =
|
|
4764
|
-
type:
|
|
6402
|
+
var DaytonaSandboxProviderSchema = z7.object({
|
|
6403
|
+
type: z7.literal("daytona").describe("Daytona sandbox provider."),
|
|
4765
6404
|
auth: DaytonaSandboxProviderAuthSchema,
|
|
4766
|
-
exec_timeout_ms:
|
|
4767
|
-
auto_stop_interval_in_minutes:
|
|
4768
|
-
auto_archive_interval_in_minutes:
|
|
4769
|
-
auto_delete_interval_in_minutes:
|
|
6405
|
+
exec_timeout_ms: z7.number().int().positive().describe("Default sandbox command exec timeout in milliseconds."),
|
|
6406
|
+
auto_stop_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes of idle time before Daytona auto-stops the sandbox (0 disables)."),
|
|
6407
|
+
auto_archive_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes before Daytona auto-archives the sandbox (0 disables)."),
|
|
6408
|
+
auto_delete_interval_in_minutes: z7.number().int().nonnegative().describe("Minutes before Daytona auto-deletes the sandbox (0 disables).")
|
|
4770
6409
|
}).strict();
|
|
4771
6410
|
var SandboxProviderManifestSchema = DaytonaSandboxProviderSchema.openapi("SandboxProviderManifest");
|
|
4772
|
-
var SandboxBuildStatusSchema =
|
|
4773
|
-
var SandboxBuildMetadataSchema =
|
|
4774
|
-
var SandboxStatusSchema =
|
|
6411
|
+
var SandboxBuildStatusSchema = z7.enum(["pending", "ready", "failed"]).describe("Current build status.").openapi("SandboxBuildStatus");
|
|
6412
|
+
var SandboxBuildMetadataSchema = z7.record(z7.string(), z7.string()).describe("Provider-specific build metadata (opaque string map).");
|
|
6413
|
+
var SandboxStatusSchema = z7.object({
|
|
4775
6414
|
status: SandboxBuildStatusSchema,
|
|
4776
|
-
status_reason:
|
|
6415
|
+
status_reason: z7.string().nullable().describe("Human-readable detail for the current status; null when ready."),
|
|
4777
6416
|
build_metadata: SandboxBuildMetadataSchema.nullable().describe(
|
|
4778
6417
|
"Provider-specific build metadata; null when the provider has none."
|
|
4779
6418
|
)
|
|
4780
6419
|
}).strict();
|
|
4781
|
-
var
|
|
6420
|
+
var ConfiguredSandboxProviderSchema = z7.object({
|
|
4782
6421
|
manifest: SandboxProviderManifestSchema,
|
|
4783
6422
|
status: SandboxBuildStatusSchema,
|
|
4784
|
-
status_reason:
|
|
4785
|
-
}).strict().openapi("
|
|
4786
|
-
var
|
|
6423
|
+
status_reason: z7.string().nullable().describe("Human-readable detail for the current status; null when ready.")
|
|
6424
|
+
}).strict().openapi("ConfiguredSandboxProvider");
|
|
6425
|
+
var UpdateSandboxProviderRequestSchema = z7.object({
|
|
4787
6426
|
manifest: SandboxProviderManifestSchema
|
|
4788
|
-
}).strict().openapi("
|
|
4789
|
-
var GetSandboxProviderResponseSchema =
|
|
4790
|
-
data:
|
|
6427
|
+
}).strict().openapi("UpdateSandboxProviderRequest");
|
|
6428
|
+
var GetSandboxProviderResponseSchema = z7.object({
|
|
6429
|
+
data: ConfiguredSandboxProviderSchema
|
|
4791
6430
|
}).openapi("GetSandboxProviderResponse");
|
|
4792
6431
|
function toDaytonaSandboxProviderInput(manifest) {
|
|
4793
6432
|
return {
|
|
@@ -4868,57 +6507,57 @@ async function checkSnapshotStatus({
|
|
|
4868
6507
|
}
|
|
4869
6508
|
|
|
4870
6509
|
// src/schemas/mcpServer.ts
|
|
4871
|
-
import { z as
|
|
4872
|
-
var McpServerTypeSchema =
|
|
4873
|
-
var McpServerHeaderAuthSchema =
|
|
4874
|
-
type:
|
|
4875
|
-
headers:
|
|
6510
|
+
import { z as z8 } from "@hono/zod-openapi";
|
|
6511
|
+
var McpServerTypeSchema = z8.enum(["remote"]).openapi("MCPServerType");
|
|
6512
|
+
var McpServerHeaderAuthSchema = z8.object({
|
|
6513
|
+
type: z8.literal("header").describe("Authenticate with static HTTP headers."),
|
|
6514
|
+
headers: z8.record(z8.string().min(1), z8.string().min(1)).refine((headers) => Object.keys(headers).length > 0, {
|
|
4876
6515
|
message: "must include at least one header"
|
|
4877
6516
|
}).describe(
|
|
4878
6517
|
"Request headers for this MCP server. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored secret for that header name."
|
|
4879
6518
|
)
|
|
4880
|
-
}).strict().openapi("
|
|
4881
|
-
var McpServerDcrAuthSchema =
|
|
4882
|
-
type:
|
|
4883
|
-
}).strict().openapi("
|
|
4884
|
-
var McpServerManifestAuthSchema =
|
|
4885
|
-
var McpServerDescriptionSchema =
|
|
4886
|
-
var McpServerManifestObjectSchema =
|
|
6519
|
+
}).strict().openapi("MCPServerHeaderAuth");
|
|
6520
|
+
var McpServerDcrAuthSchema = z8.object({
|
|
6521
|
+
type: z8.literal("dcr").describe("Authenticate via OAuth Dynamic Client Registration.")
|
|
6522
|
+
}).strict().openapi("MCPServerDcrAuth");
|
|
6523
|
+
var McpServerManifestAuthSchema = z8.discriminatedUnion("type", [McpServerHeaderAuthSchema, McpServerDcrAuthSchema]).describe("Optional auth settings. Omit when the server needs no credentials.").openapi("MCPServerManifestAuth");
|
|
6524
|
+
var McpServerDescriptionSchema = z8.string().trim().min(1).describe("Concise summary of what this MCP server provides.");
|
|
6525
|
+
var McpServerManifestObjectSchema = z8.object({
|
|
4887
6526
|
type: McpServerTypeSchema,
|
|
4888
6527
|
name: NameSchema,
|
|
4889
|
-
url:
|
|
6528
|
+
url: z8.url().describe("URL of the remote MCP server."),
|
|
4890
6529
|
description: McpServerDescriptionSchema,
|
|
4891
6530
|
auth: McpServerManifestAuthSchema.optional()
|
|
4892
6531
|
}).strict();
|
|
4893
|
-
var McpServerManifestSchema = McpServerManifestObjectSchema.openapi("
|
|
4894
|
-
var McpAuthStatusSchema =
|
|
4895
|
-
status:
|
|
4896
|
-
authorization_url:
|
|
4897
|
-
}).strict().describe("Current auth state.").openapi("
|
|
4898
|
-
var ConfiguredMcpServerSchema =
|
|
6532
|
+
var McpServerManifestSchema = McpServerManifestObjectSchema.openapi("MCPServerManifest");
|
|
6533
|
+
var McpAuthStatusSchema = z8.object({
|
|
6534
|
+
status: z8.enum(["authenticated", "auth_required", "not_required"]).describe("Current auth state for this MCP server."),
|
|
6535
|
+
authorization_url: z8.url().optional().describe("When auth is required, this contains the URL to redirect the user to for authorization.")
|
|
6536
|
+
}).strict().describe("Current auth state.").openapi("MCPAuthStatus");
|
|
6537
|
+
var ConfiguredMcpServerSchema = z8.object({
|
|
4899
6538
|
name: NameSchema,
|
|
4900
6539
|
manifest: McpServerManifestSchema,
|
|
4901
6540
|
auth_status: McpAuthStatusSchema
|
|
4902
|
-
}).strict().openapi("
|
|
4903
|
-
var CreateMcpServerRequestSchema =
|
|
6541
|
+
}).strict().openapi("ConfiguredMCPServer");
|
|
6542
|
+
var CreateMcpServerRequestSchema = z8.object({
|
|
4904
6543
|
manifest: McpServerManifestSchema
|
|
4905
|
-
}).strict().openapi("
|
|
4906
|
-
var
|
|
6544
|
+
}).strict().openapi("CreateMCPServerRequest");
|
|
6545
|
+
var UpdateMcpServerRequestSchema = z8.object({
|
|
4907
6546
|
manifest: McpServerManifestSchema
|
|
4908
|
-
}).strict().openapi("
|
|
4909
|
-
var GetMcpServerResponseSchema =
|
|
4910
|
-
var ListMcpServersResponseSchema =
|
|
4911
|
-
var McpServerAuthPublicSchema =
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
]).describe("Auth mechanism when configured (no secrets). Omit when the server needs no credentials.").openapi("
|
|
4915
|
-
var
|
|
6547
|
+
}).strict().openapi("UpdateMCPServerRequest");
|
|
6548
|
+
var GetMcpServerResponseSchema = z8.object({ data: ConfiguredMcpServerSchema }).openapi("GetMCPServerResponse");
|
|
6549
|
+
var ListMcpServersResponseSchema = z8.object({ data: z8.array(ConfiguredMcpServerSchema) }).openapi("ListMCPServersResponse");
|
|
6550
|
+
var McpServerAuthPublicSchema = z8.discriminatedUnion("type", [
|
|
6551
|
+
z8.object({ type: z8.literal("dcr") }).strict(),
|
|
6552
|
+
z8.object({ type: z8.literal("header") }).strict()
|
|
6553
|
+
]).describe("Auth mechanism when configured (no secrets). Omit when the server needs no credentials.").openapi("MCPServerAuthPublic");
|
|
6554
|
+
var AvailableMcpServerSchema = z8.object({
|
|
4916
6555
|
name: NameSchema,
|
|
4917
|
-
url:
|
|
6556
|
+
url: z8.url().describe("URL of the remote MCP server."),
|
|
4918
6557
|
auth: McpServerAuthPublicSchema.optional(),
|
|
4919
6558
|
auth_status: McpAuthStatusSchema
|
|
4920
|
-
}).strict().openapi("
|
|
4921
|
-
var ListAvailableMcpServersResponseSchema =
|
|
6559
|
+
}).strict().openapi("AvailableMCPServer");
|
|
6560
|
+
var ListAvailableMcpServersResponseSchema = z8.object({ data: z8.array(AvailableMcpServerSchema) }).openapi("ListAvailableMCPServersResponse");
|
|
4922
6561
|
function resolveConfiguredMcpRequestHeaders(manifest) {
|
|
4923
6562
|
if (manifest.auth?.type === "header") {
|
|
4924
6563
|
return { ...manifest.auth.headers };
|
|
@@ -5066,20 +6705,40 @@ async function resolveGitSkills({
|
|
|
5066
6705
|
}
|
|
5067
6706
|
return resolved;
|
|
5068
6707
|
}
|
|
6708
|
+
function localSandboxSessionSegment(sessionId) {
|
|
6709
|
+
if (sessionId === void 0 || sessionId.length === 0 || sessionId.includes("/") || sessionId.includes("..")) {
|
|
6710
|
+
return "_";
|
|
6711
|
+
}
|
|
6712
|
+
return sessionId;
|
|
6713
|
+
}
|
|
5069
6714
|
async function resolveSandboxProvider({
|
|
5070
6715
|
tenant_id,
|
|
5071
6716
|
store,
|
|
5072
|
-
logger
|
|
6717
|
+
logger,
|
|
6718
|
+
sessionId
|
|
5073
6719
|
}) {
|
|
5074
6720
|
const record = await store.getSandboxProvider(tenant_id);
|
|
5075
|
-
if (record
|
|
6721
|
+
if (record !== void 0) {
|
|
6722
|
+
return toDaytonaSandboxProvider({
|
|
6723
|
+
manifest: record.manifest,
|
|
6724
|
+
tenant_id,
|
|
6725
|
+
logger,
|
|
6726
|
+
build_metadata: record.build_metadata
|
|
6727
|
+
});
|
|
6728
|
+
}
|
|
6729
|
+
if (!config_default.STANDALONE) {
|
|
5076
6730
|
return void 0;
|
|
5077
6731
|
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
6732
|
+
const support = getCachedLocalSandboxSupport();
|
|
6733
|
+
if (support?.supported !== true) {
|
|
6734
|
+
return void 0;
|
|
6735
|
+
}
|
|
6736
|
+
return new LocalSandboxProvider({
|
|
6737
|
+
sandboxRootPathParent: join4(config_default.LOCAL_SANDBOX_ROOT_PARENT, localSandboxSessionSegment(sessionId)),
|
|
6738
|
+
codeModeSocketParentPath: config_default.CODE_MODE_SOCKET_PARENT,
|
|
6739
|
+
support,
|
|
6740
|
+
fileMaxBytesForDownload: config_default.SANDBOX_FILE_MAX_BYTES_FOR_DOWNLOAD,
|
|
6741
|
+
logger
|
|
5083
6742
|
});
|
|
5084
6743
|
}
|
|
5085
6744
|
function buildTurnSandbox(input) {
|
|
@@ -5091,7 +6750,6 @@ function buildTurnSandbox(input) {
|
|
|
5091
6750
|
blockDestructiveToolsInCodeMode: true,
|
|
5092
6751
|
mcpRequestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
|
|
5093
6752
|
mcpConnectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
|
|
5094
|
-
tenantName: input.tenantName,
|
|
5095
6753
|
...skillMounter ? { skillMounter } : {},
|
|
5096
6754
|
tracing: input.tracing,
|
|
5097
6755
|
logger: input.logger
|
|
@@ -5160,7 +6818,7 @@ async function validateAgentSpec({
|
|
|
5160
6818
|
const hasSkills = requestedSkills.length > 0;
|
|
5161
6819
|
if (wantsSandbox || hasSkills) {
|
|
5162
6820
|
const record = await sandboxProviderStore.getSandboxProvider(tenant_id);
|
|
5163
|
-
if (record === void 0) {
|
|
6821
|
+
if (record === void 0 && !isLocalSandboxFallbackEnabled()) {
|
|
5164
6822
|
throw new HTTPException(422, {
|
|
5165
6823
|
message: hasSkills ? "skills require a sandbox provider \u2014 configure via PUT /settings/sandbox-providers" : "sandbox is enabled but no sandbox provider is configured \u2014 PUT /settings/sandbox-providers"
|
|
5166
6824
|
});
|
|
@@ -5183,14 +6841,14 @@ import {
|
|
|
5183
6841
|
redisRequest,
|
|
5184
6842
|
RequestTimeoutError
|
|
5185
6843
|
} from "@truefoundry/trueforge-core/request-reply";
|
|
5186
|
-
import { ulid as
|
|
5187
|
-
import { z as
|
|
6844
|
+
import { ulid as ulid4 } from "ulid";
|
|
6845
|
+
import { z as z13 } from "zod";
|
|
5188
6846
|
|
|
5189
6847
|
// src/routes/sessionRoutes.ts
|
|
5190
|
-
import { createRoute as createRoute2, z as
|
|
6848
|
+
import { createRoute as createRoute2, z as z12 } from "@hono/zod-openapi";
|
|
5191
6849
|
|
|
5192
6850
|
// src/schemas/events.ts
|
|
5193
|
-
import { z as
|
|
6851
|
+
import { z as z9 } from "@hono/zod-openapi";
|
|
5194
6852
|
import {
|
|
5195
6853
|
EventType,
|
|
5196
6854
|
SessionEventItemSchema,
|
|
@@ -5210,7 +6868,7 @@ import {
|
|
|
5210
6868
|
ToolResponseEventSchema,
|
|
5211
6869
|
ToolResponseRequiredEventSchema
|
|
5212
6870
|
} from "@truefoundry/trueforge-core/core";
|
|
5213
|
-
var TurnStreamingEventSchema =
|
|
6871
|
+
var TurnStreamingEventSchema = z9.discriminatedUnion("type", [
|
|
5214
6872
|
ModelMessageEventSchema,
|
|
5215
6873
|
ModelMessageDeltaEventSchema,
|
|
5216
6874
|
ToolResponseEventSchema,
|
|
@@ -5224,105 +6882,94 @@ var TurnStreamingEventSchema = z7.discriminatedUnion("type", [
|
|
|
5224
6882
|
TurnCreatedEventSchema,
|
|
5225
6883
|
TurnDoneEventSchema
|
|
5226
6884
|
]).openapi("TurnStreamingEvent");
|
|
5227
|
-
var
|
|
5228
|
-
|
|
5229
|
-
page_token: z7.string().optional().describe(
|
|
6885
|
+
var ListSessionEventsRequestQuerySchema = z9.object({
|
|
6886
|
+
page_token: z9.string().optional().describe(
|
|
5230
6887
|
"Pagination cursor from `pagination.next_page_token`. It retains the branch anchor turn and returns older events toward the session start."
|
|
5231
6888
|
),
|
|
5232
|
-
last_turn_id:
|
|
6889
|
+
last_turn_id: z9.string().optional().describe(
|
|
5233
6890
|
"Newest turn in the listing window (initial load only; ignored when `page_token` is set). Lists that turn and its ancestors, newest events first. Omit to use the session last turn."
|
|
5234
6891
|
),
|
|
5235
|
-
limit:
|
|
5236
|
-
`Max events per response. Default ${String(SESSION_EVENTS_LIMIT)}, max ${String(SESSION_EVENTS_LIMIT)}.`
|
|
5237
|
-
)
|
|
6892
|
+
limit: z9.coerce.number().int().min(1).max(EVENTS_PAGE_LIMIT).optional().default(EVENTS_PAGE_LIMIT).describe(`Page size. Defaults to ${String(EVENTS_PAGE_LIMIT)}, max ${String(EVENTS_PAGE_LIMIT)}.`)
|
|
5238
6893
|
}).openapi("ListSessionEventsRequestQuery");
|
|
5239
|
-
var ListSessionEventsResponseSchema =
|
|
5240
|
-
data:
|
|
6894
|
+
var ListSessionEventsResponseSchema = z9.object({
|
|
6895
|
+
data: z9.array(SessionEventItemSchema),
|
|
5241
6896
|
pagination: TokenPaginationSchema
|
|
5242
6897
|
}).openapi("ListSessionEventsResponse");
|
|
5243
6898
|
|
|
5244
6899
|
// src/schemas/session.ts
|
|
5245
|
-
import { z as
|
|
6900
|
+
import { z as z10 } from "@hono/zod-openapi";
|
|
5246
6901
|
import { AgentSpecSchema as AgentSpecSchema3, SessionSchema, TokenPaginationSchema as TokenPaginationSchema2 } from "@truefoundry/trueforge-core/agent-session";
|
|
5247
|
-
var SessionAgentNameRefSchema =
|
|
5248
|
-
var SessionAgentSpecBodySchema =
|
|
5249
|
-
var CreateSessionAgentSchema =
|
|
6902
|
+
var SessionAgentNameRefSchema = z10.object({ name: NameSchema }).strict().openapi("SessionAgentNameRef");
|
|
6903
|
+
var SessionAgentSpecBodySchema = z10.object({ spec: AgentSpecSchema3 }).strict().openapi("SessionAgentSpecBody");
|
|
6904
|
+
var CreateSessionAgentSchema = z10.union([SessionAgentNameRefSchema, SessionAgentSpecBodySchema]).openapi("CreateSessionAgent");
|
|
5250
6905
|
function isSessionAgentNameRef(agent) {
|
|
5251
6906
|
return SessionAgentNameRefSchema.safeParse(agent).success;
|
|
5252
6907
|
}
|
|
5253
|
-
var CreateSessionRequestSchema =
|
|
6908
|
+
var CreateSessionRequestSchema = z10.object({
|
|
5254
6909
|
agent: CreateSessionAgentSchema
|
|
5255
6910
|
}).strict().openapi("CreateSessionRequest");
|
|
5256
|
-
var UpdateSessionRequestSchema =
|
|
6911
|
+
var UpdateSessionRequestSchema = z10.object({
|
|
5257
6912
|
agent: SessionAgentSpecBodySchema.optional()
|
|
5258
6913
|
}).strict().openapi("UpdateSessionRequest");
|
|
5259
|
-
var
|
|
5260
|
-
var
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
order: z8.enum(["asc", "desc"]).optional().default("desc").describe('Sort sessions by `updated_at`. Defaults to "desc".').openapi("ListSessionsOrder"),
|
|
5265
|
-
page_token: z8.string().min(1).optional().describe("Opaque keyset cursor from a previous response `next_page_token`."),
|
|
6914
|
+
var IsoTimestampQueryParam = z10.iso.datetime({ offset: true }).openapi({ type: "string", format: "date-time" }).transform((s) => new Date(s));
|
|
6915
|
+
var ListSessionsRequestQuerySchema = z10.object({
|
|
6916
|
+
limit: z10.coerce.number().int().min(1).max(PAGE_LIMIT).optional().default(PAGE_LIMIT).describe(`Page size. Defaults to ${String(PAGE_LIMIT)}, max ${String(PAGE_LIMIT)}.`),
|
|
6917
|
+
order: z10.enum(["asc", "desc"]).optional().default("desc").describe('Sort sessions by `updated_at`. Defaults to "desc".').openapi("ListSessionsOrder"),
|
|
6918
|
+
page_token: z10.string().min(1).optional().describe("Opaque keyset cursor from a previous response `next_page_token`."),
|
|
5266
6919
|
start_timestamp: IsoTimestampQueryParam.optional().describe(
|
|
5267
6920
|
"Inclusive lower bound on `created_at` (ISO-8601 / RFC 3339)."
|
|
5268
6921
|
),
|
|
5269
6922
|
end_timestamp: IsoTimestampQueryParam.optional().describe(
|
|
5270
6923
|
"Inclusive upper bound on `created_at` (ISO-8601 / RFC 3339)."
|
|
5271
6924
|
),
|
|
5272
|
-
agent_id:
|
|
6925
|
+
agent_id: z10.string().min(1).optional().describe("When set, only sessions bound to this agent id are returned.")
|
|
5273
6926
|
}).openapi("ListSessionsRequestQuery");
|
|
5274
|
-
var GetSessionResponseSchema =
|
|
6927
|
+
var GetSessionResponseSchema = z10.object({
|
|
5275
6928
|
data: SessionSchema
|
|
5276
6929
|
}).openapi("GetSessionResponse");
|
|
5277
|
-
var ListSessionsResponseSchema =
|
|
5278
|
-
data:
|
|
6930
|
+
var ListSessionsResponseSchema = z10.object({
|
|
6931
|
+
data: z10.array(SessionSchema),
|
|
5279
6932
|
pagination: TokenPaginationSchema2
|
|
5280
6933
|
}).openapi("ListSessionsResponse");
|
|
5281
6934
|
|
|
5282
6935
|
// src/schemas/turn.ts
|
|
5283
|
-
import { z as
|
|
6936
|
+
import { z as z11 } from "@hono/zod-openapi";
|
|
5284
6937
|
import { SessionEventSchema, TokenPaginationSchema as TokenPaginationSchema3, TurnSchema } from "@truefoundry/trueforge-core/agent-session";
|
|
5285
6938
|
import { CreateTurnRequestSchema, TurnSchema as TurnSchema2 } from "@truefoundry/trueforge-core/agent-session";
|
|
5286
|
-
var SubscribeTurnQuerySchema =
|
|
6939
|
+
var SubscribeTurnQuerySchema = z11.object({
|
|
5287
6940
|
// Query strings need coerce; map null/'' to undefined first so Number(null)→0
|
|
5288
6941
|
// cannot silently become a resume cursor (and so OpenAPI does not advertise null).
|
|
5289
|
-
after_sequence_number:
|
|
6942
|
+
after_sequence_number: z11.preprocess(
|
|
5290
6943
|
(val) => val === null || val === "" ? void 0 : val,
|
|
5291
|
-
|
|
6944
|
+
z11.coerce.number().int().nonnegative().optional().describe(
|
|
5292
6945
|
"Exclusive resume cursor: replay only events with a sequence number greater than this value. Omit to start from the beginning of the live buffer."
|
|
5293
6946
|
)
|
|
5294
6947
|
)
|
|
5295
6948
|
}).openapi("SubscribeTurnQuery");
|
|
5296
|
-
var CancelSessionRequestSchema =
|
|
5297
|
-
var CancelSessionResponseSchema =
|
|
5298
|
-
var
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
limit: z9.coerce.number().int().min(1).max(TURNS_MAX_LIMIT).optional().default(DEFAULT_TURNS_LIMIT).describe(`Page size. Defaults to ${String(DEFAULT_TURNS_LIMIT)}, max ${String(TURNS_MAX_LIMIT)}.`),
|
|
5302
|
-
page_token: z9.string().optional().describe("Opaque token from a previous response `next_page_token`.")
|
|
6949
|
+
var CancelSessionRequestSchema = z11.object({}).describe("Empty request body. Cancel identifies the session via the path only.").openapi("CancelSessionRequest");
|
|
6950
|
+
var CancelSessionResponseSchema = z11.object({}).describe("Empty success body. HTTP 200 means the cancel request was accepted (or nothing was running).").openapi("CancelSessionResponse");
|
|
6951
|
+
var ListTurnsRequestQuerySchema = z11.object({
|
|
6952
|
+
limit: z11.coerce.number().int().min(1).max(PAGE_LIMIT).optional().default(PAGE_LIMIT).describe(`Page size. Defaults to ${String(PAGE_LIMIT)}, max ${String(PAGE_LIMIT)}.`),
|
|
6953
|
+
page_token: z11.string().optional().describe("Opaque token from a previous response `next_page_token`.")
|
|
5303
6954
|
}).openapi("ListTurnsRequestQuery");
|
|
5304
|
-
var GetTurnResponseSchema =
|
|
6955
|
+
var GetTurnResponseSchema = z11.object({
|
|
5305
6956
|
data: TurnSchema
|
|
5306
6957
|
}).openapi("GetTurnResponse");
|
|
5307
|
-
var ListTurnsResponseSchema =
|
|
5308
|
-
data:
|
|
6958
|
+
var ListTurnsResponseSchema = z11.object({
|
|
6959
|
+
data: z11.array(TurnSchema),
|
|
5309
6960
|
pagination: TokenPaginationSchema3
|
|
5310
6961
|
}).openapi("ListTurnsResponse");
|
|
5311
|
-
var
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
`Max events per response. Default ${String(DEFAULT_TURN_EVENTS_LIMIT)}, max ${String(TURN_EVENTS_MAX_LIMIT)}.`
|
|
5316
|
-
),
|
|
5317
|
-
page_token: z9.string().optional().describe("Opaque token from a previous response `next_page_token`."),
|
|
5318
|
-
order: z9.enum(["asc", "desc"]).optional().default("asc").describe('Sort events by insertion order. Defaults to "asc".').openapi("ListTurnEventsOrder")
|
|
6962
|
+
var ListTurnEventsRequestQuerySchema = z11.object({
|
|
6963
|
+
limit: z11.coerce.number().int().min(1).max(EVENTS_PAGE_LIMIT).optional().default(EVENTS_PAGE_LIMIT).describe(`Page size. Defaults to ${String(EVENTS_PAGE_LIMIT)}, max ${String(EVENTS_PAGE_LIMIT)}.`),
|
|
6964
|
+
page_token: z11.string().optional().describe("Opaque token from a previous response `next_page_token`."),
|
|
6965
|
+
order: z11.enum(["asc", "desc"]).optional().default("asc").describe('Sort events by insertion order. Defaults to "asc".').openapi("ListTurnEventsOrder")
|
|
5319
6966
|
}).openapi("ListTurnEventsRequestQuery");
|
|
5320
|
-
var ListTurnEventsResponseSchema =
|
|
5321
|
-
data:
|
|
6967
|
+
var ListTurnEventsResponseSchema = z11.object({
|
|
6968
|
+
data: z11.array(SessionEventSchema),
|
|
5322
6969
|
pagination: TokenPaginationSchema3
|
|
5323
6970
|
}).openapi("ListTurnEventsResponse");
|
|
5324
|
-
var DownloadSandboxFileRequestQuerySchema =
|
|
5325
|
-
path:
|
|
6971
|
+
var DownloadSandboxFileRequestQuerySchema = z11.object({
|
|
6972
|
+
path: z11.string().min(1).describe(
|
|
5326
6973
|
"Absolute path of the file inside the sandbox, as listed in the assistant's `sandbox_artifacts` block."
|
|
5327
6974
|
)
|
|
5328
6975
|
}).openapi("DownloadSandboxFileRequestQuery");
|
|
@@ -5335,14 +6982,13 @@ var TOKEN_PAGINATION = {
|
|
|
5335
6982
|
};
|
|
5336
6983
|
|
|
5337
6984
|
// src/routes/sessionRoutes.ts
|
|
5338
|
-
var
|
|
5339
|
-
|
|
5340
|
-
session_id: z10.string().min(1).max(64).describe("Session identifier.")
|
|
6985
|
+
var SessionIdParamsSchema = z12.object({
|
|
6986
|
+
session_id: z12.string().min(1).max(64).describe("Session identifier.")
|
|
5341
6987
|
});
|
|
5342
6988
|
var createSessionRoute = createRoute2({
|
|
5343
6989
|
method: "post",
|
|
5344
6990
|
path: "/",
|
|
5345
|
-
tags: [
|
|
6991
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5346
6992
|
summary: "Create a session",
|
|
5347
6993
|
description: 'Create a session with `agent` as either `{ name }` (named registry binding) or `{ spec: AgentSpec }` (inline). Named sessions snapshot the agent name at create and resolve the live agent on each turn. Responses use `{ type: "reference", name, id }` or `{ type: "inline", spec }`.',
|
|
5348
6994
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5375,7 +7021,7 @@ var createSessionRoute = createRoute2({
|
|
|
5375
7021
|
var getSessionRoute = createRoute2({
|
|
5376
7022
|
method: "get",
|
|
5377
7023
|
path: "/{session_id}",
|
|
5378
|
-
tags: [
|
|
7024
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5379
7025
|
summary: "Get a session",
|
|
5380
7026
|
description: "Fetch a session by ID. Only the session creator (`created_by`) may fetch it.",
|
|
5381
7027
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5401,7 +7047,7 @@ var getSessionRoute = createRoute2({
|
|
|
5401
7047
|
var deleteSessionRoute = createRoute2({
|
|
5402
7048
|
method: "delete",
|
|
5403
7049
|
path: "/{session_id}",
|
|
5404
|
-
tags: [
|
|
7050
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5405
7051
|
summary: "Delete a session",
|
|
5406
7052
|
description: "Delete a session and all related turns, events, and internal state. Only the session creator (`created_by`) may delete it. Idempotent if already gone.",
|
|
5407
7053
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5422,7 +7068,7 @@ var deleteSessionRoute = createRoute2({
|
|
|
5422
7068
|
var updateSessionRoute = createRoute2({
|
|
5423
7069
|
method: "patch",
|
|
5424
7070
|
path: "/{session_id}",
|
|
5425
|
-
tags: [
|
|
7071
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5426
7072
|
summary: "Update a session",
|
|
5427
7073
|
description: "Update a session by replacing `agent` with `{ spec: AgentSpec }`. Named (reference) sessions reject agent updates. An empty body is a valid no-op that refreshes `updated_at`. Only the session creator (`created_by`) may update it.",
|
|
5428
7074
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5460,7 +7106,7 @@ var updateSessionRoute = createRoute2({
|
|
|
5460
7106
|
var listSessionsRoute = createRoute2({
|
|
5461
7107
|
method: "get",
|
|
5462
7108
|
path: "/",
|
|
5463
|
-
tags: [
|
|
7109
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5464
7110
|
summary: "List sessions",
|
|
5465
7111
|
description: "List the caller's sessions (newest first by default), token-paginated. Results are scoped to the authenticated identity via the session store's `created_by` filter (not a client query param). Optional `agent_id` filters to sessions bound to that named agent. Pass `page_token` to fetch the next page, keeping the other query params constant.",
|
|
5466
7112
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5483,7 +7129,7 @@ var listSessionsRoute = createRoute2({
|
|
|
5483
7129
|
var cancelSessionRoute = createRoute2({
|
|
5484
7130
|
method: "post",
|
|
5485
7131
|
path: "/{session_id}/cancel",
|
|
5486
|
-
tags: [
|
|
7132
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5487
7133
|
summary: "Cancel a running turn in a session",
|
|
5488
7134
|
description: "Cancel the running last turn for a session. Only the session creator (`created_by`) may cancel.",
|
|
5489
7135
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5517,7 +7163,7 @@ var cancelSessionRoute = createRoute2({
|
|
|
5517
7163
|
var listSessionEventsRoute = createRoute2({
|
|
5518
7164
|
method: "get",
|
|
5519
7165
|
path: "/{session_id}/events",
|
|
5520
|
-
tags: [
|
|
7166
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
5521
7167
|
summary: "List session events",
|
|
5522
7168
|
description: "List session events as `{ turn_id, event }` across the active turn branch (newest first), including persisted events from a running tip. Each turn contributes turn.created, content events (model.message, tool.call, \u2026), and turn.done when terminal; streaming deltas are not included. Use `page_token` to paginate backward toward older events while retaining the original branch anchor. Only the session creator (`created_by`) may list events.",
|
|
5523
7169
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -5549,9 +7195,9 @@ var listSessionEventsRoute = createRoute2({
|
|
|
5549
7195
|
|
|
5550
7196
|
// src/runtime/peeringIds.ts
|
|
5551
7197
|
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
5552
|
-
import { ulid } from "ulid";
|
|
7198
|
+
import { ulid as ulid3 } from "ulid";
|
|
5553
7199
|
function mintPeeredTurnId(executorId) {
|
|
5554
|
-
return `${
|
|
7200
|
+
return `${ulid3().toLowerCase()}.${executorId}`;
|
|
5555
7201
|
}
|
|
5556
7202
|
function executorFromTurnId(turnId) {
|
|
5557
7203
|
const parts = turnId.split(".");
|
|
@@ -5564,10 +7210,10 @@ function executorFromTurnId(turnId) {
|
|
|
5564
7210
|
// src/apis/sessions.ts
|
|
5565
7211
|
var TENANT_ID = "default";
|
|
5566
7212
|
var SESSIONS_CANCEL_PATH = "sessions/cancel";
|
|
5567
|
-
var CancelPeerBodySchema =
|
|
5568
|
-
session_id:
|
|
5569
|
-
turn_id:
|
|
5570
|
-
reason:
|
|
7213
|
+
var CancelPeerBodySchema = z13.object({
|
|
7214
|
+
session_id: z13.string(),
|
|
7215
|
+
turn_id: z13.string(),
|
|
7216
|
+
reason: z13.enum(CancellationReason)
|
|
5571
7217
|
});
|
|
5572
7218
|
function toWireSession(record) {
|
|
5573
7219
|
return {
|
|
@@ -5668,7 +7314,7 @@ function checkSessionAccess({ userRef, createdBy }) {
|
|
|
5668
7314
|
function createSessionsRouter(deps) {
|
|
5669
7315
|
const createSessionHandler = async (c) => {
|
|
5670
7316
|
const body = c.req.valid("json");
|
|
5671
|
-
const sessionId =
|
|
7317
|
+
const sessionId = ulid4().toLowerCase();
|
|
5672
7318
|
if (isSessionAgentNameRef(body.agent)) {
|
|
5673
7319
|
const agent = await deps.agentStore.getAgent({ tenant_id: TENANT_ID, name: body.agent.name });
|
|
5674
7320
|
if (agent === void 0) {
|
|
@@ -5929,13 +7575,13 @@ import { extractErrorLogFields as extractErrorLogFields2 } from "@truefoundry/tr
|
|
|
5929
7575
|
// src/auth/cookies.ts
|
|
5930
7576
|
init_config();
|
|
5931
7577
|
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
|
5932
|
-
import { z as
|
|
7578
|
+
import { z as z14 } from "zod";
|
|
5933
7579
|
var OAUTH_STATE_COOKIE = "oauth_state";
|
|
5934
7580
|
var ID_TOKEN_COOKIE = "id_token";
|
|
5935
|
-
var OAuthStateSchema =
|
|
5936
|
-
state:
|
|
5937
|
-
code_verifier:
|
|
5938
|
-
return_to:
|
|
7581
|
+
var OAuthStateSchema = z14.object({
|
|
7582
|
+
state: z14.string(),
|
|
7583
|
+
code_verifier: z14.string(),
|
|
7584
|
+
return_to: z14.string()
|
|
5939
7585
|
});
|
|
5940
7586
|
function getAuthCookieAttributes() {
|
|
5941
7587
|
return {
|
|
@@ -6232,36 +7878,36 @@ var adminAuthMiddleware = async (c, next) => {
|
|
|
6232
7878
|
import { createRoute as createRoute3 } from "@hono/zod-openapi";
|
|
6233
7879
|
|
|
6234
7880
|
// src/schemas/auth.ts
|
|
6235
|
-
import { z as
|
|
6236
|
-
var AuthLoginQuerySchema =
|
|
6237
|
-
return_to:
|
|
7881
|
+
import { z as z15 } from "@hono/zod-openapi";
|
|
7882
|
+
var AuthLoginQuerySchema = z15.object({
|
|
7883
|
+
return_to: z15.string().optional().describe('Path to return to after login. Must be a same-origin relative path; anything else falls back to "/".')
|
|
6238
7884
|
});
|
|
6239
|
-
var OAuthCallbackQuerySchema =
|
|
6240
|
-
code:
|
|
6241
|
-
state:
|
|
6242
|
-
error:
|
|
6243
|
-
error_description:
|
|
7885
|
+
var OAuthCallbackQuerySchema = z15.object({
|
|
7886
|
+
code: z15.string().min(1).optional().describe("Authorization code, present when the user granted consent."),
|
|
7887
|
+
state: z15.string().min(1).describe("Opaque token; correlates this callback to its pending login. Always present, success or error."),
|
|
7888
|
+
error: z15.string().optional().describe("Error code from the identity provider, present instead of `code` if the user denied consent."),
|
|
7889
|
+
error_description: z15.string().optional().describe("Human-readable error detail from the identity provider when `error` is set.")
|
|
6244
7890
|
});
|
|
6245
|
-
var OAuthCallbackSuccessSchema =
|
|
6246
|
-
success:
|
|
7891
|
+
var OAuthCallbackSuccessSchema = z15.object({
|
|
7892
|
+
success: z15.literal(true).describe("Present when the OAuth callback completed without a return_to.")
|
|
6247
7893
|
});
|
|
6248
|
-
var
|
|
6249
|
-
type:
|
|
7894
|
+
var GetMeResponseSchema = z15.object({
|
|
7895
|
+
type: z15.enum(["default", "oidc-connected"]).describe(
|
|
6250
7896
|
"Session kind: `default` when no valid OIDC session; `oidc-connected` after a successful browser login."
|
|
6251
7897
|
),
|
|
6252
|
-
email:
|
|
6253
|
-
role:
|
|
6254
|
-
}).openapi("
|
|
7898
|
+
email: z15.string().describe('User email from the ID token when connected; `"default"` when anonymous.'),
|
|
7899
|
+
role: z15.string().describe("Caller role.")
|
|
7900
|
+
}).openapi("GetMeResponse");
|
|
6255
7901
|
|
|
6256
7902
|
// src/routes/authRoutes.ts
|
|
6257
|
-
var AUTH_TAG = "Auth";
|
|
6258
7903
|
var authLoginRoute = createRoute3({
|
|
6259
7904
|
method: "get",
|
|
6260
7905
|
path: "/login",
|
|
6261
|
-
tags: [
|
|
7906
|
+
tags: ["Auth" /* AUTH */],
|
|
6262
7907
|
summary: "Start the login flow",
|
|
6263
7908
|
description: "Redirects the browser to the configured identity provider. In local/single-binary mode, redirects straight back into the app \u2014 there is nothing to log into.",
|
|
6264
7909
|
"x-fern-ignore": true,
|
|
7910
|
+
"x-excluded": true,
|
|
6265
7911
|
request: { query: AuthLoginQuerySchema },
|
|
6266
7912
|
responses: {
|
|
6267
7913
|
302: { description: "Redirect to the IdP authorization endpoint." }
|
|
@@ -6270,10 +7916,11 @@ var authLoginRoute = createRoute3({
|
|
|
6270
7916
|
var oAuthCallbackRoute = createRoute3({
|
|
6271
7917
|
method: "get",
|
|
6272
7918
|
path: "/callback",
|
|
6273
|
-
tags: [
|
|
7919
|
+
tags: ["Auth" /* AUTH */],
|
|
6274
7920
|
summary: "Login callback",
|
|
6275
7921
|
description: "Browser-redirect target hit by the identity provider after login, never called directly by SDK consumers. In local/single-binary mode, redirects straight back into the app.",
|
|
6276
7922
|
"x-fern-ignore": true,
|
|
7923
|
+
"x-excluded": true,
|
|
6277
7924
|
request: { query: OAuthCallbackQuerySchema },
|
|
6278
7925
|
responses: {
|
|
6279
7926
|
302: { description: "Redirect back into the app on success, or to /?error=<reason> on failure." }
|
|
@@ -6282,11 +7929,11 @@ var oAuthCallbackRoute = createRoute3({
|
|
|
6282
7929
|
var authLogoutRoute = createRoute3({
|
|
6283
7930
|
method: "post",
|
|
6284
7931
|
path: "/logout",
|
|
6285
|
-
tags: [
|
|
7932
|
+
tags: ["Auth" /* AUTH */],
|
|
6286
7933
|
summary: "Clear the local session",
|
|
6287
7934
|
description: "Ends the local harness session only \u2014 does not hit the IdP end-session endpoint. A no-op in local/single-binary mode, since there is no real session to clear.",
|
|
6288
|
-
"x-fern-
|
|
6289
|
-
"x-
|
|
7935
|
+
"x-fern-ignore": true,
|
|
7936
|
+
"x-excluded": true,
|
|
6290
7937
|
responses: {
|
|
6291
7938
|
204: { description: "Session cookie cleared." }
|
|
6292
7939
|
}
|
|
@@ -6294,14 +7941,14 @@ var authLogoutRoute = createRoute3({
|
|
|
6294
7941
|
var meRoute = createRoute3({
|
|
6295
7942
|
method: "get",
|
|
6296
7943
|
path: "/me",
|
|
6297
|
-
tags: [
|
|
7944
|
+
tags: ["Auth" /* AUTH */],
|
|
6298
7945
|
summary: "Current session",
|
|
6299
7946
|
description: "Returns the authenticated caller identity. When auth is enabled this requires a valid `id_token` cookie or `Authorization: Bearer` ID token (401 otherwise). When auth is disabled, returns the default identity.",
|
|
6300
7947
|
"x-fern-sdk-group-name": ["auth"],
|
|
6301
7948
|
"x-fern-sdk-method-name": "me",
|
|
6302
7949
|
responses: {
|
|
6303
7950
|
200: {
|
|
6304
|
-
content: { "application/json": { schema:
|
|
7951
|
+
content: { "application/json": { schema: GetMeResponseSchema } },
|
|
6305
7952
|
description: "Session type and identity for the current request."
|
|
6306
7953
|
},
|
|
6307
7954
|
401: {
|
|
@@ -6341,6 +7988,9 @@ function createAuthRouter(params) {
|
|
|
6341
7988
|
const pending = readOAuthStateCookie({ context: c, logger: params.logger });
|
|
6342
7989
|
clearAuthCookie({ context: c, name: OAUTH_STATE_COOKIE });
|
|
6343
7990
|
if (pending?.state !== query.state || query.error || !query.code) {
|
|
7991
|
+
if (await resolveAuthUser(c)) {
|
|
7992
|
+
return c.redirect("/", 302);
|
|
7993
|
+
}
|
|
6344
7994
|
const description = query.error_description?.trim();
|
|
6345
7995
|
const reason = query.error && description ? description : "login_failed";
|
|
6346
7996
|
return c.redirect(oauthErrorRedirect(reason), 302);
|
|
@@ -6356,6 +8006,9 @@ function createAuthRouter(params) {
|
|
|
6356
8006
|
return c.redirect(safeReturnTo(pending.return_to), 302);
|
|
6357
8007
|
} catch (error) {
|
|
6358
8008
|
params.logger.error("Failed to exchange authorization code", extractErrorLogFields2(error));
|
|
8009
|
+
if (await resolveAuthUser(c)) {
|
|
8010
|
+
return c.redirect(safeReturnTo(pending.return_to), 302);
|
|
8011
|
+
}
|
|
6359
8012
|
const reason = error instanceof Error ? error.message : "login_failed";
|
|
6360
8013
|
return c.redirect(oauthErrorRedirect(reason), 302);
|
|
6361
8014
|
}
|
|
@@ -6380,25 +8033,29 @@ import { OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
|
|
|
6380
8033
|
import { extractErrorLogFields as extractErrorLogFields3 } from "@truefoundry/trueforge-core/core";
|
|
6381
8034
|
|
|
6382
8035
|
// src/routes/capabilityRoutes.ts
|
|
6383
|
-
import { createRoute as createRoute4, z as
|
|
6384
|
-
var
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
8036
|
+
import { createRoute as createRoute4, z as z16 } from "@hono/zod-openapi";
|
|
8037
|
+
var SandboxCapabilitySchema = z16.object({
|
|
8038
|
+
enabled: z16.boolean().describe("Whether a sandbox provider is configured for this tenant.")
|
|
8039
|
+
}).openapi("SandboxCapability");
|
|
8040
|
+
var SkillCapabilitySchema = z16.object({
|
|
8041
|
+
enabled: z16.boolean().describe("Whether skills are available. False when sandbox is not enabled (skills require a sandbox)."),
|
|
8042
|
+
reason: z16.string().optional().describe("Present when skills are disabled. Explains why.")
|
|
8043
|
+
}).openapi("SkillCapability");
|
|
8044
|
+
var SettingsCapabilitySchema = z16.object({
|
|
8045
|
+
enabled: z16.boolean().describe("Whether the settings UI/API is enabled.")
|
|
8046
|
+
}).openapi("SettingsCapability");
|
|
8047
|
+
var CapabilitiesDataSchema = z16.object({
|
|
8048
|
+
sandbox: SandboxCapabilitySchema,
|
|
8049
|
+
skill: SkillCapabilitySchema,
|
|
8050
|
+
settings: SettingsCapabilitySchema
|
|
8051
|
+
}).openapi("CapabilitiesData");
|
|
8052
|
+
var GetCapabilitiesResponseSchema = z16.object({
|
|
8053
|
+
data: CapabilitiesDataSchema
|
|
6397
8054
|
}).openapi("GetCapabilitiesResponse");
|
|
6398
8055
|
var getCapabilitiesRoute = createRoute4({
|
|
6399
8056
|
method: "get",
|
|
6400
8057
|
path: "/",
|
|
6401
|
-
tags: ["Capabilities"],
|
|
8058
|
+
tags: ["Capabilities" /* CAPABILITIES */],
|
|
6402
8059
|
summary: "Get server capabilities",
|
|
6403
8060
|
"x-fern-sdk-group-name": ["server"],
|
|
6404
8061
|
"x-fern-sdk-method-name": "get_capabilities",
|
|
@@ -6436,7 +8093,7 @@ function createCapabilitiesRouter(deps) {
|
|
|
6436
8093
|
} catch (error) {
|
|
6437
8094
|
deps.logger.warn("Sandbox image status check failed; reporting sandbox disabled", extractErrorLogFields3(error));
|
|
6438
8095
|
}
|
|
6439
|
-
const sandboxEnabled = status === "ready";
|
|
8096
|
+
const sandboxEnabled = status === "ready" || status === void 0 && isLocalSandboxFallbackEnabled();
|
|
6440
8097
|
const settingsEnabled = isAdmin(resolveUserContext(c));
|
|
6441
8098
|
return c.json(
|
|
6442
8099
|
{
|
|
@@ -6460,42 +8117,42 @@ import { SUPPORTED_REASONING_EFFORTS as SUPPORTED_REASONING_EFFORTS2 } from "@tr
|
|
|
6460
8117
|
import { createRoute as createRoute5 } from "@hono/zod-openapi";
|
|
6461
8118
|
|
|
6462
8119
|
// src/schemas/mcpCatalog.ts
|
|
6463
|
-
import { z as
|
|
6464
|
-
var CatalogMcpServerSchema =
|
|
8120
|
+
import { z as z17 } from "@hono/zod-openapi";
|
|
8121
|
+
var CatalogMcpServerSchema = z17.object({
|
|
6465
8122
|
type: McpServerTypeSchema,
|
|
6466
8123
|
name: NameSchema,
|
|
6467
|
-
logo:
|
|
6468
|
-
url:
|
|
8124
|
+
logo: z17.url().optional().describe("URL of the MCP server logo asset."),
|
|
8125
|
+
url: z17.url().describe("URL of the remote MCP server."),
|
|
6469
8126
|
description: McpServerDescriptionSchema,
|
|
6470
8127
|
auth: McpServerManifestAuthSchema.optional()
|
|
6471
|
-
}).strict().openapi("
|
|
6472
|
-
var McpCatalogFileSchema =
|
|
6473
|
-
mcp_servers:
|
|
8128
|
+
}).strict().openapi("CatalogMCPServer");
|
|
8129
|
+
var McpCatalogFileSchema = z17.object({
|
|
8130
|
+
mcp_servers: z17.array(CatalogMcpServerSchema)
|
|
6474
8131
|
}).strict().superRefine((file, ctx) => {
|
|
6475
8132
|
uniqueNames(file.mcp_servers, ctx);
|
|
6476
8133
|
});
|
|
6477
|
-
var GetMcpServerCatalogResponseSchema =
|
|
6478
|
-
data:
|
|
6479
|
-
}).openapi("
|
|
8134
|
+
var GetMcpServerCatalogResponseSchema = z17.object({
|
|
8135
|
+
data: z17.array(CatalogMcpServerSchema)
|
|
8136
|
+
}).openapi("GetMCPServerCatalogResponse");
|
|
6480
8137
|
|
|
6481
8138
|
// src/schemas/modelCatalog.ts
|
|
6482
|
-
import { z as
|
|
8139
|
+
import { z as z19 } from "@hono/zod-openapi";
|
|
6483
8140
|
|
|
6484
8141
|
// src/schemas/modelProvider.ts
|
|
6485
|
-
import { z as
|
|
8142
|
+
import { z as z18 } from "@hono/zod-openapi";
|
|
6486
8143
|
import { SUPPORTED_REASONING_EFFORTS, VERCEL_AI_PROVIDER_NAMES } from "@truefoundry/trueforge-core/core";
|
|
6487
|
-
var ModelProviderTypeSchema =
|
|
6488
|
-
var ReasoningEffortSchema =
|
|
6489
|
-
var ModelPropertiesSchema =
|
|
6490
|
-
context_length:
|
|
6491
|
-
max_output_tokens:
|
|
6492
|
-
reasoning_efforts:
|
|
8144
|
+
var ModelProviderTypeSchema = z18.enum(VERCEL_AI_PROVIDER_NAMES).openapi("ModelProviderType");
|
|
8145
|
+
var ReasoningEffortSchema = z18.enum(SUPPORTED_REASONING_EFFORTS).openapi("ReasoningEffort");
|
|
8146
|
+
var ModelPropertiesSchema = z18.object({
|
|
8147
|
+
context_length: z18.number().int().positive().optional().describe("Maximum context window size in tokens."),
|
|
8148
|
+
max_output_tokens: z18.number().int().positive().optional().describe("Maximum output tokens the model can generate."),
|
|
8149
|
+
reasoning_efforts: z18.array(ReasoningEffortSchema).min(1).optional().describe("Supported reasoning-effort values for this model.")
|
|
6493
8150
|
}).strict().describe("Optional model capability metadata.").openapi("ModelProperties");
|
|
6494
|
-
var
|
|
6495
|
-
model_id:
|
|
8151
|
+
var ConfiguredModelSchema = z18.object({
|
|
8152
|
+
model_id: z18.string().min(1).describe("Upstream, provider-specific identifier sent to the provider API."),
|
|
6496
8153
|
name: NameSchema,
|
|
6497
8154
|
properties: ModelPropertiesSchema
|
|
6498
|
-
}).strict().openapi("
|
|
8155
|
+
}).strict().openapi("ConfiguredModel");
|
|
6499
8156
|
function refineUniqueModels(models, ctx) {
|
|
6500
8157
|
uniqueNames(models, ctx);
|
|
6501
8158
|
const seenModelIds = /* @__PURE__ */ new Set();
|
|
@@ -6510,22 +8167,22 @@ function refineUniqueModels(models, ctx) {
|
|
|
6510
8167
|
seenModelIds.add(model.model_id);
|
|
6511
8168
|
}
|
|
6512
8169
|
}
|
|
6513
|
-
var ModelProviderAuthSchema =
|
|
6514
|
-
api_key:
|
|
8170
|
+
var ModelProviderAuthSchema = z18.object({
|
|
8171
|
+
api_key: z18.string().min(1).describe(
|
|
6515
8172
|
"Provider API key. Responses are redacted; on PUT, a real value sets/rotates and a redacted value keeps the stored key."
|
|
6516
8173
|
)
|
|
6517
8174
|
}).strict().describe("Provider authentication credentials.").openapi("ModelProviderAuth");
|
|
6518
|
-
var ModelProviderManifestBaseSchema =
|
|
8175
|
+
var ModelProviderManifestBaseSchema = z18.object({
|
|
6519
8176
|
auth: ModelProviderAuthSchema,
|
|
6520
|
-
models:
|
|
8177
|
+
models: z18.array(ConfiguredModelSchema).min(1).describe("Models exposed by this provider (at least one).")
|
|
6521
8178
|
}).strict();
|
|
6522
8179
|
function wellKnownProviderSchema({
|
|
6523
8180
|
type,
|
|
6524
8181
|
base_url
|
|
6525
8182
|
}) {
|
|
6526
8183
|
return ModelProviderManifestBaseSchema.extend({
|
|
6527
|
-
type:
|
|
6528
|
-
base_url:
|
|
8184
|
+
type: z18.literal(type),
|
|
8185
|
+
base_url: z18.url().default(base_url).describe("Override of the provider's default API base URL.")
|
|
6529
8186
|
}).strict();
|
|
6530
8187
|
}
|
|
6531
8188
|
var OpenAiModelProviderSchema = wellKnownProviderSchema({
|
|
@@ -6561,15 +8218,15 @@ var AlibabaModelProviderSchema = wellKnownProviderSchema({
|
|
|
6561
8218
|
base_url: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
6562
8219
|
}).openapi("AlibabaModelProvider");
|
|
6563
8220
|
var CustomModelProviderSchema = ModelProviderManifestBaseSchema.extend({
|
|
6564
|
-
type:
|
|
8221
|
+
type: z18.literal("custom"),
|
|
6565
8222
|
name: NameSchema,
|
|
6566
|
-
base_url:
|
|
8223
|
+
base_url: z18.url().describe("Base URL of the provider's API."),
|
|
6567
8224
|
auth: ModelProviderAuthSchema.optional()
|
|
6568
8225
|
}).strict().openapi("CustomModelProvider");
|
|
6569
8226
|
function refineModelProviderManifest(manifest, ctx) {
|
|
6570
8227
|
refineUniqueModels(manifest.models, ctx);
|
|
6571
8228
|
}
|
|
6572
|
-
var ModelProviderBodySchema =
|
|
8229
|
+
var ModelProviderBodySchema = z18.discriminatedUnion("type", [
|
|
6573
8230
|
OpenAiModelProviderSchema,
|
|
6574
8231
|
AnthropicModelProviderSchema,
|
|
6575
8232
|
GoogleGeminiModelProviderSchema,
|
|
@@ -6584,87 +8241,87 @@ var ModelProviderManifestSchema = ModelProviderBodySchema.openapi("ModelProvider
|
|
|
6584
8241
|
function modelProviderName(provider) {
|
|
6585
8242
|
return provider.type === "custom" ? provider.name : provider.type;
|
|
6586
8243
|
}
|
|
6587
|
-
var
|
|
8244
|
+
var ConfiguredModelProviderSchema = z18.object({
|
|
6588
8245
|
name: NameSchema,
|
|
6589
8246
|
manifest: ModelProviderManifestSchema
|
|
6590
|
-
}).strict().openapi("
|
|
6591
|
-
var CreateModelProviderRequestSchema =
|
|
8247
|
+
}).strict().openapi("ConfiguredModelProvider");
|
|
8248
|
+
var CreateModelProviderRequestSchema = z18.object({
|
|
6592
8249
|
manifest: ModelProviderManifestSchema
|
|
6593
8250
|
}).strict().openapi("CreateModelProviderRequest");
|
|
6594
|
-
var
|
|
8251
|
+
var UpdateModelProviderRequestSchema = z18.object({
|
|
6595
8252
|
manifest: ModelProviderManifestSchema
|
|
6596
|
-
}).strict().openapi("
|
|
6597
|
-
var GetModelProviderResponseSchema =
|
|
6598
|
-
data:
|
|
8253
|
+
}).strict().openapi("UpdateModelProviderRequest");
|
|
8254
|
+
var GetModelProviderResponseSchema = z18.object({
|
|
8255
|
+
data: ConfiguredModelProviderSchema
|
|
6599
8256
|
}).openapi("GetModelProviderResponse");
|
|
6600
|
-
var ListModelProvidersResponseSchema =
|
|
6601
|
-
data:
|
|
8257
|
+
var ListModelProvidersResponseSchema = z18.object({
|
|
8258
|
+
data: z18.array(ConfiguredModelProviderSchema)
|
|
6602
8259
|
}).openapi("ListModelProvidersResponse");
|
|
6603
|
-
var
|
|
6604
|
-
name:
|
|
6605
|
-
}).strict().describe("Owning configured provider.").openapi("
|
|
6606
|
-
var
|
|
6607
|
-
name:
|
|
6608
|
-
model_id:
|
|
6609
|
-
provider:
|
|
8260
|
+
var AvailableModelProviderSchema = z18.object({
|
|
8261
|
+
name: z18.string().min(1).describe("Configured provider resource name; matches the FQN prefix of `name`.")
|
|
8262
|
+
}).strict().describe("Owning configured provider.").openapi("AvailableModelProvider");
|
|
8263
|
+
var AvailableModelSchema = z18.object({
|
|
8264
|
+
name: z18.string().describe('Fully qualified name `provider_name/model_name`, e.g. "openai/gpt-5-6-sol". Unique within a tenant.'),
|
|
8265
|
+
model_id: z18.string().describe("Upstream, provider-specific identifier sent to the provider API."),
|
|
8266
|
+
provider: AvailableModelProviderSchema,
|
|
6610
8267
|
properties: ModelPropertiesSchema
|
|
6611
|
-
}).strict().openapi("
|
|
6612
|
-
var
|
|
6613
|
-
data:
|
|
6614
|
-
}).openapi("
|
|
8268
|
+
}).strict().openapi("AvailableModel");
|
|
8269
|
+
var ListAvailableModelsResponseSchema = z18.object({
|
|
8270
|
+
data: z18.array(AvailableModelSchema)
|
|
8271
|
+
}).openapi("ListAvailableModelsResponse");
|
|
6615
8272
|
|
|
6616
8273
|
// src/schemas/modelCatalog.ts
|
|
6617
8274
|
var CatalogWellKnownModelProviderTypeSchema = ModelProviderTypeSchema.exclude(["custom"]).openapi(
|
|
6618
8275
|
"CatalogWellKnownModelProviderType"
|
|
6619
8276
|
);
|
|
6620
|
-
var
|
|
8277
|
+
var CatalogModelSchema = ConfiguredModelSchema.openapi("CatalogModel");
|
|
8278
|
+
var CatalogWellKnownModelProviderSchema = z19.object({
|
|
6621
8279
|
type: CatalogWellKnownModelProviderTypeSchema,
|
|
6622
|
-
logo:
|
|
6623
|
-
models:
|
|
8280
|
+
logo: z19.url().optional().describe("URL of the provider logo asset"),
|
|
8281
|
+
models: z19.array(CatalogModelSchema).describe("Preset models")
|
|
6624
8282
|
}).strict().openapi("CatalogWellKnownModelProvider");
|
|
6625
|
-
var CatalogCustomModelProviderSchema =
|
|
6626
|
-
type:
|
|
6627
|
-
supported_reasoning_efforts:
|
|
8283
|
+
var CatalogCustomModelProviderSchema = z19.object({
|
|
8284
|
+
type: z19.literal("custom"),
|
|
8285
|
+
supported_reasoning_efforts: z19.array(ReasoningEffortSchema).describe("Supported reasoning-effort values for this provider")
|
|
6628
8286
|
}).strict().openapi("CatalogCustomModelProvider");
|
|
6629
|
-
var CatalogModelProviderSchema =
|
|
6630
|
-
var ModelCatalogFileSchema =
|
|
6631
|
-
providers:
|
|
8287
|
+
var CatalogModelProviderSchema = z19.union([CatalogWellKnownModelProviderSchema, CatalogCustomModelProviderSchema]).openapi("CatalogModelProvider");
|
|
8288
|
+
var ModelCatalogFileSchema = z19.object({
|
|
8289
|
+
providers: z19.array(CatalogWellKnownModelProviderSchema)
|
|
6632
8290
|
}).strict().superRefine((file, ctx) => {
|
|
6633
8291
|
uniqueTypes(file.providers, ctx);
|
|
6634
8292
|
for (const provider of file.providers) {
|
|
6635
8293
|
refineUniqueModels(provider.models, ctx);
|
|
6636
8294
|
}
|
|
6637
8295
|
});
|
|
6638
|
-
var GetModelProviderCatalogResponseSchema =
|
|
6639
|
-
data:
|
|
8296
|
+
var GetModelProviderCatalogResponseSchema = z19.object({
|
|
8297
|
+
data: z19.array(CatalogModelProviderSchema)
|
|
6640
8298
|
}).openapi("GetModelProviderCatalogResponse");
|
|
6641
8299
|
|
|
6642
8300
|
// src/schemas/sandboxCatalog.ts
|
|
6643
|
-
import { z as
|
|
6644
|
-
var
|
|
6645
|
-
var
|
|
6646
|
-
|
|
6647
|
-
providers: z18.array(CatalogSandboxProviderSchema)
|
|
8301
|
+
import { z as z20 } from "@hono/zod-openapi";
|
|
8302
|
+
var CatalogSandboxProviderSchema = DaytonaSandboxProviderSchema.omit({ auth: true }).strict().openapi("CatalogSandboxProvider");
|
|
8303
|
+
var SandboxCatalogFileSchema = z20.object({
|
|
8304
|
+
providers: z20.array(CatalogSandboxProviderSchema)
|
|
6648
8305
|
}).strict();
|
|
6649
|
-
var GetSandboxProviderCatalogResponseSchema =
|
|
6650
|
-
data:
|
|
8306
|
+
var GetSandboxProviderCatalogResponseSchema = z20.object({
|
|
8307
|
+
data: z20.array(CatalogSandboxProviderSchema)
|
|
6651
8308
|
}).openapi("GetSandboxProviderCatalogResponse");
|
|
6652
8309
|
|
|
6653
8310
|
// src/schemas/skillCatalog.ts
|
|
6654
|
-
import { z as
|
|
8311
|
+
import { z as z22 } from "@hono/zod-openapi";
|
|
6655
8312
|
|
|
6656
8313
|
// src/schemas/skill.ts
|
|
6657
|
-
import { z as
|
|
6658
|
-
var SkillTypeSchema =
|
|
8314
|
+
import { z as z21 } from "@hono/zod-openapi";
|
|
8315
|
+
var SkillTypeSchema = z21.enum(["git"]).openapi("SkillType");
|
|
6659
8316
|
var GIT_URL_REGEX = /^https:\/\/(github\.com\/[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+|gitlab\.com\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)(\/|\.git)?$/;
|
|
6660
8317
|
var GIT_REF_REGEX = /^[A-Za-z0-9._\-/]+$/;
|
|
6661
8318
|
var SKILL_PATH_REGEX = /^[A-Za-z0-9._\-/]+$/;
|
|
6662
8319
|
var hasParentTraversal = (value) => value.split("/").includes("..");
|
|
6663
|
-
var SkillGitUrlSchema =
|
|
6664
|
-
var SkillGitPathSchema =
|
|
6665
|
-
var SkillGitRefSchema =
|
|
6666
|
-
var SkillDescriptionSchema =
|
|
6667
|
-
var SkillManifestObjectSchema =
|
|
8320
|
+
var SkillGitUrlSchema = z21.string().trim().min(1).regex(GIT_URL_REGEX, "Must be a GitHub or GitLab HTTPS URL").refine((v) => !hasParentTraversal(v), 'URL must not contain ".." segments').describe("Full HTTPS URL of a GitHub or GitLab repository.");
|
|
8321
|
+
var SkillGitPathSchema = z21.string().trim().min(1).regex(SKILL_PATH_REGEX, 'Path may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Path must not contain ".." segments').refine((v) => !v.split("/").includes("."), 'Path must not contain "." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Path must reference a subdirectory, not only slashes").describe("Path to the skill directory within the repository. Omit to use the repository root.");
|
|
8322
|
+
var SkillGitRefSchema = z21.string().trim().min(1).regex(GIT_REF_REGEX, 'Ref may only contain letters, numbers, ".", "_", "-", and "/"').refine((v) => !hasParentTraversal(v), 'Ref must not contain ".." segments').refine((v) => v.replace(/^\/+|\/+$/g, "").length > 0, "Ref must not consist only of slashes").describe("Git ref \u2014 branch name, tag, or commit SHA.");
|
|
8323
|
+
var SkillDescriptionSchema = z21.string().trim().min(1).describe("Concise guidance for when the agent should use the skill.");
|
|
8324
|
+
var SkillManifestObjectSchema = z21.object({
|
|
6668
8325
|
type: SkillTypeSchema,
|
|
6669
8326
|
name: NameSchema,
|
|
6670
8327
|
url: SkillGitUrlSchema,
|
|
@@ -6673,26 +8330,26 @@ var SkillManifestObjectSchema = z19.object({
|
|
|
6673
8330
|
description: SkillDescriptionSchema
|
|
6674
8331
|
}).strict();
|
|
6675
8332
|
var SkillManifestSchema = SkillManifestObjectSchema.openapi("SkillManifest");
|
|
6676
|
-
var ConfiguredSkillSchema =
|
|
8333
|
+
var ConfiguredSkillSchema = z21.object({
|
|
6677
8334
|
name: NameSchema,
|
|
6678
8335
|
manifest: SkillManifestSchema
|
|
6679
8336
|
}).strict().openapi("ConfiguredSkill");
|
|
6680
|
-
var CreateSkillRequestSchema =
|
|
8337
|
+
var CreateSkillRequestSchema = z21.object({
|
|
6681
8338
|
manifest: SkillManifestSchema
|
|
6682
8339
|
}).strict().openapi("CreateSkillRequest");
|
|
6683
|
-
var
|
|
8340
|
+
var UpdateSkillRequestSchema = z21.object({
|
|
6684
8341
|
manifest: SkillManifestSchema
|
|
6685
|
-
}).strict().openapi("
|
|
6686
|
-
var GetSkillResponseSchema =
|
|
6687
|
-
var ListSkillsResponseSchema =
|
|
6688
|
-
var
|
|
8342
|
+
}).strict().openapi("UpdateSkillRequest");
|
|
8343
|
+
var GetSkillResponseSchema = z21.object({ data: ConfiguredSkillSchema }).openapi("GetSkillResponse");
|
|
8344
|
+
var ListSkillsResponseSchema = z21.object({ data: z21.array(ConfiguredSkillSchema) }).openapi("ListSkillsResponse");
|
|
8345
|
+
var AvailableSkillSchema = z21.object({
|
|
6689
8346
|
name: NameSchema,
|
|
6690
8347
|
description: SkillDescriptionSchema
|
|
6691
|
-
}).strict().openapi("
|
|
6692
|
-
var ListAvailableSkillsResponseSchema =
|
|
8348
|
+
}).strict().openapi("AvailableSkill");
|
|
8349
|
+
var ListAvailableSkillsResponseSchema = z21.object({ data: z21.array(AvailableSkillSchema) }).openapi("ListAvailableSkillsResponse");
|
|
6693
8350
|
|
|
6694
8351
|
// src/schemas/skillCatalog.ts
|
|
6695
|
-
var CatalogSkillSchema =
|
|
8352
|
+
var CatalogSkillSchema = z22.object({
|
|
6696
8353
|
type: SkillTypeSchema,
|
|
6697
8354
|
name: NameSchema,
|
|
6698
8355
|
url: SkillGitUrlSchema,
|
|
@@ -6700,24 +8357,23 @@ var CatalogSkillSchema = z20.object({
|
|
|
6700
8357
|
ref: SkillGitRefSchema,
|
|
6701
8358
|
description: SkillDescriptionSchema
|
|
6702
8359
|
}).strict().openapi("CatalogSkill");
|
|
6703
|
-
var SkillCatalogFileSchema =
|
|
6704
|
-
skills:
|
|
8360
|
+
var SkillCatalogFileSchema = z22.object({
|
|
8361
|
+
skills: z22.array(CatalogSkillSchema)
|
|
6705
8362
|
}).strict().superRefine((file, ctx) => {
|
|
6706
8363
|
uniqueNames(file.skills, ctx);
|
|
6707
8364
|
});
|
|
6708
|
-
var GetSkillCatalogResponseSchema =
|
|
6709
|
-
data:
|
|
8365
|
+
var GetSkillCatalogResponseSchema = z22.object({
|
|
8366
|
+
data: z22.array(CatalogSkillSchema)
|
|
6710
8367
|
}).openapi("GetSkillCatalogResponse");
|
|
6711
8368
|
|
|
6712
8369
|
// src/routes/catalogRoutes.ts
|
|
6713
|
-
var CATALOG_TAG = "Catalog";
|
|
6714
8370
|
var listModelProviderCatalogRoute = createRoute5({
|
|
6715
8371
|
method: "get",
|
|
6716
8372
|
path: "/model-providers",
|
|
6717
|
-
tags: [
|
|
8373
|
+
tags: ["Models" /* MODELS */],
|
|
6718
8374
|
summary: "Get the model catalog",
|
|
6719
8375
|
description: "Shipped model-provider presets (discovery-only). Copy into PUT /settings/model-providers to configure. Includes a `custom` sentinel with `supported_reasoning_efforts`.",
|
|
6720
|
-
"x-fern-sdk-group-name": ["
|
|
8376
|
+
"x-fern-sdk-group-name": ["catalogs", "modelProviders"],
|
|
6721
8377
|
"x-fern-sdk-method-name": "list",
|
|
6722
8378
|
responses: {
|
|
6723
8379
|
200: {
|
|
@@ -6733,10 +8389,10 @@ var listModelProviderCatalogRoute = createRoute5({
|
|
|
6733
8389
|
var listMcpServerCatalogRoute = createRoute5({
|
|
6734
8390
|
method: "get",
|
|
6735
8391
|
path: "/mcp-servers",
|
|
6736
|
-
tags: [
|
|
8392
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6737
8393
|
summary: "Get the MCP catalog",
|
|
6738
8394
|
description: "Shipped MCP server presets (discovery-only). Copy into PUT /settings/mcp-servers to configure.",
|
|
6739
|
-
"x-fern-sdk-group-name": ["
|
|
8395
|
+
"x-fern-sdk-group-name": ["catalogs", "mcpServers"],
|
|
6740
8396
|
"x-fern-sdk-method-name": "list",
|
|
6741
8397
|
responses: {
|
|
6742
8398
|
200: {
|
|
@@ -6752,10 +8408,10 @@ var listMcpServerCatalogRoute = createRoute5({
|
|
|
6752
8408
|
var listSkillCatalogRoute = createRoute5({
|
|
6753
8409
|
method: "get",
|
|
6754
8410
|
path: "/skills",
|
|
6755
|
-
tags: [
|
|
8411
|
+
tags: ["Skills" /* SKILLS */],
|
|
6756
8412
|
summary: "Get the skill catalog",
|
|
6757
8413
|
description: "Shipped skill presets (discovery-only). Copy into PUT /settings/skills to configure.",
|
|
6758
|
-
"x-fern-sdk-group-name": ["
|
|
8414
|
+
"x-fern-sdk-group-name": ["catalogs", "skills"],
|
|
6759
8415
|
"x-fern-sdk-method-name": "list",
|
|
6760
8416
|
responses: {
|
|
6761
8417
|
200: {
|
|
@@ -6771,10 +8427,10 @@ var listSkillCatalogRoute = createRoute5({
|
|
|
6771
8427
|
var listSandboxProviderCatalogRoute = createRoute5({
|
|
6772
8428
|
method: "get",
|
|
6773
8429
|
path: "/sandbox-providers",
|
|
6774
|
-
tags: [
|
|
8430
|
+
tags: ["Sandboxes" /* SANDBOXES */],
|
|
6775
8431
|
summary: "Get the sandbox provider catalog",
|
|
6776
8432
|
description: "Shipped sandbox-provider presets (discovery-only). Copy into PUT /settings/sandbox-providers to configure.",
|
|
6777
|
-
"x-fern-sdk-group-name": ["
|
|
8433
|
+
"x-fern-sdk-group-name": ["catalogs", "sandboxProviders"],
|
|
6778
8434
|
"x-fern-sdk-method-name": "list",
|
|
6779
8435
|
responses: {
|
|
6780
8436
|
200: {
|
|
@@ -6822,15 +8478,15 @@ import { extractErrorLogFields as extractErrorLogFields4, McpConnectionError as
|
|
|
6822
8478
|
|
|
6823
8479
|
// src/routes/mcpOAuthRoutes.ts
|
|
6824
8480
|
import { createRoute as createRoute6 } from "@hono/zod-openapi";
|
|
6825
|
-
var MCP_OAUTH_TAG = "MCP OAuth";
|
|
6826
8481
|
var mcpOAuthCallbackRoute = createRoute6({
|
|
6827
8482
|
method: "get",
|
|
6828
8483
|
path: "/callback",
|
|
6829
|
-
tags: [
|
|
8484
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6830
8485
|
summary: "OAuth callback for MCP authorization",
|
|
6831
8486
|
description: "Browser redirect target for MCP server OAuth. The authorization server redirects here with `code`/`state` (or `error`). Exchanges the code for tokens, then either returns JSON or redirects to the `return_to` path supplied at authorize time. Not called by the SDK \u2014 browsers hit this URL directly.",
|
|
6832
8487
|
// Browser-redirect target hit directly by the authorization server, never called by SDK.
|
|
6833
8488
|
"x-fern-ignore": true,
|
|
8489
|
+
"x-excluded": true,
|
|
6834
8490
|
request: {
|
|
6835
8491
|
query: OAuthCallbackQuerySchema
|
|
6836
8492
|
},
|
|
@@ -6923,12 +8579,11 @@ init_config();
|
|
|
6923
8579
|
init_mcpServerStore();
|
|
6924
8580
|
|
|
6925
8581
|
// src/routes/mcpServerRoutes.ts
|
|
6926
|
-
import { createRoute as createRoute7, z as
|
|
6927
|
-
var MCP_SERVERS_TAG = "MCP Servers";
|
|
8582
|
+
import { createRoute as createRoute7, z as z23 } from "@hono/zod-openapi";
|
|
6928
8583
|
var listAvailableMcpServersRoute = createRoute7({
|
|
6929
8584
|
method: "get",
|
|
6930
8585
|
path: "/",
|
|
6931
|
-
tags: [
|
|
8586
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6932
8587
|
summary: "List MCP servers for chat",
|
|
6933
8588
|
description: "MCP servers as a slim name/url list for the composer. No auth or auth_status.",
|
|
6934
8589
|
"x-fern-sdk-group-name": ["mcpServers"],
|
|
@@ -6947,7 +8602,7 @@ var listAvailableMcpServersRoute = createRoute7({
|
|
|
6947
8602
|
var listMcpServersRoute = createRoute7({
|
|
6948
8603
|
method: "get",
|
|
6949
8604
|
path: "/",
|
|
6950
|
-
tags: [
|
|
8605
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6951
8606
|
summary: "List MCP servers",
|
|
6952
8607
|
description: "All MCP servers with nested auth_status (settings / admin projection). Header auth values are redacted.",
|
|
6953
8608
|
"x-fern-sdk-group-name": ["settings", "mcpServers"],
|
|
@@ -6967,13 +8622,13 @@ var listMcpServersRoute = createRoute7({
|
|
|
6967
8622
|
}
|
|
6968
8623
|
}
|
|
6969
8624
|
});
|
|
6970
|
-
var McpServerNameParamsSchema =
|
|
6971
|
-
name:
|
|
8625
|
+
var McpServerNameParamsSchema = z23.object({
|
|
8626
|
+
name: z23.string().min(1).describe("MCP server name.")
|
|
6972
8627
|
});
|
|
6973
8628
|
var getMcpServerRoute = createRoute7({
|
|
6974
8629
|
method: "get",
|
|
6975
8630
|
path: "/{name}",
|
|
6976
|
-
tags: [
|
|
8631
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6977
8632
|
summary: "Get a single MCP server by name",
|
|
6978
8633
|
description: "A single MCP server by name, with nested auth_status (settings / admin projection). Header auth values are redacted.",
|
|
6979
8634
|
"x-fern-sdk-group-name": ["settings", "mcpServers"],
|
|
@@ -6995,7 +8650,7 @@ var getMcpServerRoute = createRoute7({
|
|
|
6995
8650
|
var createMcpServerRoute = createRoute7({
|
|
6996
8651
|
method: "post",
|
|
6997
8652
|
path: "/",
|
|
6998
|
-
tags: [
|
|
8653
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
6999
8654
|
summary: "Create an MCP server",
|
|
7000
8655
|
description: "Creates an MCP server by `name`. Fails if `name` is already taken. Runs DCR registration when `auth.type` is `dcr`. Header secrets: real value required; redacted with no stored value returns 400.",
|
|
7001
8656
|
"x-fern-sdk-group-name": ["settings", "mcpServers"],
|
|
@@ -7028,14 +8683,14 @@ var createMcpServerRoute = createRoute7({
|
|
|
7028
8683
|
var putMcpServerRoute = createRoute7({
|
|
7029
8684
|
method: "put",
|
|
7030
8685
|
path: "/",
|
|
7031
|
-
tags: [
|
|
8686
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
7032
8687
|
summary: "Create or replace an MCP server",
|
|
7033
8688
|
description: "Create or replace by `name`. Does not start DCR or change oauth client columns. Header secrets: real value sets/rotates; redacted keeps existing (400 if none).",
|
|
7034
8689
|
"x-fern-sdk-group-name": ["settings", "mcpServers"],
|
|
7035
|
-
"x-fern-sdk-method-name": "
|
|
8690
|
+
"x-fern-sdk-method-name": "create_or_update",
|
|
7036
8691
|
request: {
|
|
7037
8692
|
body: {
|
|
7038
|
-
content: { "application/json": { schema:
|
|
8693
|
+
content: { "application/json": { schema: UpdateMcpServerRequestSchema } },
|
|
7039
8694
|
required: true
|
|
7040
8695
|
}
|
|
7041
8696
|
},
|
|
@@ -7054,16 +8709,16 @@ var putMcpServerRoute = createRoute7({
|
|
|
7054
8709
|
}
|
|
7055
8710
|
}
|
|
7056
8711
|
});
|
|
7057
|
-
var ListMcpServerToolsResponseSchema =
|
|
8712
|
+
var ListMcpServerToolsResponseSchema = z23.object({
|
|
7058
8713
|
// TODO: Type tools/list entries to the MCP tool shape (name, description, inputSchema, …) for OpenAPI quality.
|
|
7059
|
-
data:
|
|
7060
|
-
}).openapi("
|
|
8714
|
+
data: z23.array(z23.record(z23.string(), z23.unknown())).describe("MCP `tools/list` entries, passed through verbatim from the MCP server.")
|
|
8715
|
+
}).openapi("ListMCPServerToolsResponse");
|
|
7061
8716
|
var listMcpServerToolsRoute = createRoute7({
|
|
7062
8717
|
method: "get",
|
|
7063
8718
|
path: "/{name}/tools",
|
|
7064
|
-
tags: [
|
|
8719
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
7065
8720
|
summary: "List tools of an MCP server",
|
|
7066
|
-
"x-fern-sdk-group-name": ["
|
|
8721
|
+
"x-fern-sdk-group-name": ["mcpServers"],
|
|
7067
8722
|
"x-fern-sdk-method-name": "list_tools",
|
|
7068
8723
|
description: "All tools exposed by the given MCP server (non-paginated), as returned by the MCP `tools/list` call.",
|
|
7069
8724
|
request: {
|
|
@@ -7076,7 +8731,7 @@ var listMcpServerToolsRoute = createRoute7({
|
|
|
7076
8731
|
},
|
|
7077
8732
|
401: {
|
|
7078
8733
|
content: { "application/json": { schema: RequestErrorResponseSchema } },
|
|
7079
|
-
description: "
|
|
8734
|
+
description: "OIDC is configured and the request has no valid session cookie."
|
|
7080
8735
|
},
|
|
7081
8736
|
404: {
|
|
7082
8737
|
content: { "application/json": { schema: RequestErrorResponseSchema } },
|
|
@@ -7092,15 +8747,15 @@ var listMcpServerToolsRoute = createRoute7({
|
|
|
7092
8747
|
}
|
|
7093
8748
|
}
|
|
7094
8749
|
});
|
|
7095
|
-
var McpAuthorizeQuerySchema =
|
|
7096
|
-
return_to:
|
|
8750
|
+
var McpAuthorizeQuerySchema = z23.object({
|
|
8751
|
+
return_to: z23.string().optional().describe(
|
|
7097
8752
|
"Optional path to return to after OAuth. Must be a same-origin relative path; the OAuth callback redirects here with `isSuccess`/`reason` appended."
|
|
7098
8753
|
)
|
|
7099
8754
|
});
|
|
7100
8755
|
var authorizeMcpServerRoute = createRoute7({
|
|
7101
8756
|
method: "get",
|
|
7102
8757
|
path: "/{name}/authorize",
|
|
7103
|
-
tags: [
|
|
8758
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
7104
8759
|
summary: "Start (or short-circuit) the auth flow for an MCP server",
|
|
7105
8760
|
"x-fern-sdk-group-name": ["mcpServers"],
|
|
7106
8761
|
"x-fern-sdk-method-name": "authorize",
|
|
@@ -7139,7 +8794,7 @@ var authorizeMcpServerRoute = createRoute7({
|
|
|
7139
8794
|
var deleteAuthorizationMcpServerRoute = createRoute7({
|
|
7140
8795
|
method: "delete",
|
|
7141
8796
|
path: "/{name}/authorize",
|
|
7142
|
-
tags: [
|
|
8797
|
+
tags: ["MCP Servers" /* MCP_SERVERS */],
|
|
7143
8798
|
summary: "Disconnect OAuth for an MCP server",
|
|
7144
8799
|
"x-fern-sdk-group-name": ["mcpServers"],
|
|
7145
8800
|
"x-fern-sdk-method-name": "delete_authorization",
|
|
@@ -7246,7 +8901,7 @@ function toConfiguredMcpServer({
|
|
|
7246
8901
|
})
|
|
7247
8902
|
};
|
|
7248
8903
|
}
|
|
7249
|
-
function
|
|
8904
|
+
function toAvailableMcpServer({
|
|
7250
8905
|
record,
|
|
7251
8906
|
token
|
|
7252
8907
|
}) {
|
|
@@ -7402,53 +9057,10 @@ function createSettingsMcpServersRouter(deps) {
|
|
|
7402
9057
|
throw error;
|
|
7403
9058
|
}
|
|
7404
9059
|
};
|
|
7405
|
-
const listToolsHandler = async (c) => {
|
|
7406
|
-
const { name } = c.req.valid("param");
|
|
7407
|
-
const userRef = deps.resolveUserContext(c).userRef;
|
|
7408
|
-
const connection = await getMcpConnection({
|
|
7409
|
-
tenant_id: TENANT_ID,
|
|
7410
|
-
name,
|
|
7411
|
-
store: deps.mcpServerStore,
|
|
7412
|
-
tokenStore: deps.tokenStore,
|
|
7413
|
-
clientName: config_default.MCP_DCR_OAUTH_CLIENT_NAME,
|
|
7414
|
-
userRef
|
|
7415
|
-
});
|
|
7416
|
-
if (connection === void 0) {
|
|
7417
|
-
return c.json({ error: { message: `MCP server not found: ${name}` } }, 404);
|
|
7418
|
-
}
|
|
7419
|
-
const remote = new RemoteMCP({
|
|
7420
|
-
id: name,
|
|
7421
|
-
name,
|
|
7422
|
-
url: connection.url,
|
|
7423
|
-
headers: connection.headers,
|
|
7424
|
-
requestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
|
|
7425
|
-
connectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
|
|
7426
|
-
logger: deps.logger,
|
|
7427
|
-
signal: c.req.raw.signal
|
|
7428
|
-
});
|
|
7429
|
-
try {
|
|
7430
|
-
const response = await remote.listTools();
|
|
7431
|
-
if (isAuthRequired(response)) {
|
|
7432
|
-
return c.json({ error: { message: `MCP server "${name}" requires authentication` } }, 422);
|
|
7433
|
-
}
|
|
7434
|
-
const data = response.result.tools.map((tool) => omitUndefinedEntries({ ...tool }));
|
|
7435
|
-
return c.json({ data }, 200);
|
|
7436
|
-
} catch (error) {
|
|
7437
|
-
if (error instanceof McpConnectionError4) {
|
|
7438
|
-
deps.logger.warn(`MCP tools/list failed for "${name}"`, extractErrorLogFields5(error));
|
|
7439
|
-
if (error.statusCode === 401) {
|
|
7440
|
-
return c.json({ error: { message: error.message } }, 422);
|
|
7441
|
-
}
|
|
7442
|
-
return c.json({ error: { message: error.message } }, 502);
|
|
7443
|
-
}
|
|
7444
|
-
throw error;
|
|
7445
|
-
}
|
|
7446
|
-
};
|
|
7447
9060
|
const router = new OpenAPIHono7();
|
|
7448
9061
|
router.openapi(listMcpServersRoute, listHandler);
|
|
7449
9062
|
router.openapi(createMcpServerRoute, createHandler);
|
|
7450
9063
|
router.openapi(putMcpServerRoute, putHandler);
|
|
7451
|
-
router.openapi(listMcpServerToolsRoute, listToolsHandler);
|
|
7452
9064
|
router.openapi(getMcpServerRoute, getHandler);
|
|
7453
9065
|
return router;
|
|
7454
9066
|
}
|
|
@@ -7498,6 +9110,48 @@ function createMcpServersRouter(deps) {
|
|
|
7498
9110
|
return c.json({ error: { message: "Internal server error" } }, 500);
|
|
7499
9111
|
}
|
|
7500
9112
|
};
|
|
9113
|
+
const listToolsHandler = async (c) => {
|
|
9114
|
+
const { name } = c.req.valid("param");
|
|
9115
|
+
const userRef = deps.resolveUserContext(c).userRef;
|
|
9116
|
+
const connection = await getMcpConnection({
|
|
9117
|
+
tenant_id: TENANT_ID,
|
|
9118
|
+
name,
|
|
9119
|
+
store: deps.mcpServerStore,
|
|
9120
|
+
tokenStore: deps.tokenStore,
|
|
9121
|
+
clientName: config_default.MCP_DCR_OAUTH_CLIENT_NAME,
|
|
9122
|
+
userRef
|
|
9123
|
+
});
|
|
9124
|
+
if (connection === void 0) {
|
|
9125
|
+
return c.json({ error: { message: `MCP server not found: ${name}` } }, 404);
|
|
9126
|
+
}
|
|
9127
|
+
const remote = new RemoteMCP({
|
|
9128
|
+
id: name,
|
|
9129
|
+
name,
|
|
9130
|
+
url: connection.url,
|
|
9131
|
+
headers: connection.headers,
|
|
9132
|
+
requestTimeoutMs: config_default.MCP_REQUEST_TIMEOUT_MS,
|
|
9133
|
+
connectTimeoutMs: config_default.MCP_CONNECT_TIMEOUT_MS,
|
|
9134
|
+
logger: deps.logger,
|
|
9135
|
+
signal: c.req.raw.signal
|
|
9136
|
+
});
|
|
9137
|
+
try {
|
|
9138
|
+
const response = await remote.listTools();
|
|
9139
|
+
if (isAuthRequired(response)) {
|
|
9140
|
+
return c.json({ error: { message: `MCP server "${name}" requires authentication` } }, 422);
|
|
9141
|
+
}
|
|
9142
|
+
const data = response.result.tools.map((tool) => omitUndefinedEntries({ ...tool }));
|
|
9143
|
+
return c.json({ data }, 200);
|
|
9144
|
+
} catch (error) {
|
|
9145
|
+
if (error instanceof McpConnectionError4) {
|
|
9146
|
+
deps.logger.warn(`MCP tools/list failed for "${name}"`, extractErrorLogFields5(error));
|
|
9147
|
+
if (error.statusCode === 401) {
|
|
9148
|
+
return c.json({ error: { message: error.message } }, 422);
|
|
9149
|
+
}
|
|
9150
|
+
return c.json({ error: { message: error.message } }, 502);
|
|
9151
|
+
}
|
|
9152
|
+
throw error;
|
|
9153
|
+
}
|
|
9154
|
+
};
|
|
7501
9155
|
const deleteAuthorizationHandler = async (c) => {
|
|
7502
9156
|
const { name } = c.req.valid("param");
|
|
7503
9157
|
const userRef = deps.resolveUserContext(c).userRef;
|
|
@@ -7518,11 +9172,12 @@ function createMcpServersRouter(deps) {
|
|
|
7518
9172
|
const tokens = await deps.tokenStore.getTokens({ ids: dcrIds, userRef });
|
|
7519
9173
|
return c.json(
|
|
7520
9174
|
{
|
|
7521
|
-
data: records.map((record) =>
|
|
9175
|
+
data: records.map((record) => toAvailableMcpServer({ record, token: tokens.get(record.id) }))
|
|
7522
9176
|
},
|
|
7523
9177
|
200
|
|
7524
9178
|
);
|
|
7525
9179
|
});
|
|
9180
|
+
router.openapi(listMcpServerToolsRoute, listToolsHandler);
|
|
7526
9181
|
router.openapi(authorizeMcpServerRoute, authorizeHandler);
|
|
7527
9182
|
router.openapi(deleteAuthorizationMcpServerRoute, deleteAuthorizationHandler);
|
|
7528
9183
|
return router;
|
|
@@ -7533,18 +9188,18 @@ import { OpenAPIHono as OpenAPIHono8 } from "@hono/zod-openapi";
|
|
|
7533
9188
|
|
|
7534
9189
|
// src/routes/modelRoutes.ts
|
|
7535
9190
|
import { createRoute as createRoute8 } from "@hono/zod-openapi";
|
|
7536
|
-
var
|
|
9191
|
+
var listAvailableModelsRoute = createRoute8({
|
|
7537
9192
|
method: "get",
|
|
7538
9193
|
path: "/",
|
|
7539
|
-
tags: ["Models"],
|
|
7540
|
-
summary: "List models",
|
|
9194
|
+
tags: ["Models" /* MODELS */],
|
|
9195
|
+
summary: "List models for chat",
|
|
7541
9196
|
"x-fern-sdk-group-name": ["models"],
|
|
7542
9197
|
"x-fern-sdk-method-name": "list",
|
|
7543
|
-
description: "
|
|
9198
|
+
description: "Configured models as a slim FQN list for the composer.",
|
|
7544
9199
|
responses: {
|
|
7545
9200
|
200: {
|
|
7546
|
-
content: { "application/json": { schema:
|
|
7547
|
-
description: "All models
|
|
9201
|
+
content: { "application/json": { schema: ListAvailableModelsResponseSchema } },
|
|
9202
|
+
description: "All configured models (chat projection)."
|
|
7548
9203
|
},
|
|
7549
9204
|
401: {
|
|
7550
9205
|
content: { "application/json": { schema: RequestErrorResponseSchema } },
|
|
@@ -7557,7 +9212,7 @@ var listModelsRoute = createRoute8({
|
|
|
7557
9212
|
function createModelsRouter(deps) {
|
|
7558
9213
|
const router = new OpenAPIHono8();
|
|
7559
9214
|
router.openapi(
|
|
7560
|
-
|
|
9215
|
+
listAvailableModelsRoute,
|
|
7561
9216
|
async (c) => c.json({ data: await deps.modelProviderStore.listModels(TENANT_ID) }, 200)
|
|
7562
9217
|
);
|
|
7563
9218
|
return router;
|
|
@@ -7572,11 +9227,10 @@ import { OpenAPIHono as OpenAPIHono9 } from "@hono/zod-openapi";
|
|
|
7572
9227
|
|
|
7573
9228
|
// src/routes/modelProviderRoutes.ts
|
|
7574
9229
|
import { createRoute as createRoute9 } from "@hono/zod-openapi";
|
|
7575
|
-
var MODEL_PROVIDERS_TAG = "Model Providers";
|
|
7576
9230
|
var listModelProvidersRoute = createRoute9({
|
|
7577
9231
|
method: "get",
|
|
7578
9232
|
path: "/",
|
|
7579
|
-
tags: [
|
|
9233
|
+
tags: ["Models" /* MODELS */],
|
|
7580
9234
|
summary: "List configured model providers",
|
|
7581
9235
|
description: "All configured providers with nested manifests.",
|
|
7582
9236
|
"x-fern-sdk-group-name": ["settings", "modelProviders"],
|
|
@@ -7599,7 +9253,7 @@ var listModelProvidersRoute = createRoute9({
|
|
|
7599
9253
|
var createModelProviderRoute = createRoute9({
|
|
7600
9254
|
method: "post",
|
|
7601
9255
|
path: "/",
|
|
7602
|
-
tags: [
|
|
9256
|
+
tags: ["Models" /* MODELS */],
|
|
7603
9257
|
summary: "Create a model provider",
|
|
7604
9258
|
description: "Creates a provider (models included). Fails if `name` is already taken. Well-known types use `type` as `name` (one each); `custom` is named by the caller. `auth.api_key`: real value required; redacted with no stored secret returns 400.",
|
|
7605
9259
|
"x-fern-sdk-group-name": ["settings", "modelProviders"],
|
|
@@ -7628,14 +9282,14 @@ var createModelProviderRoute = createRoute9({
|
|
|
7628
9282
|
var putModelProviderRoute = createRoute9({
|
|
7629
9283
|
method: "put",
|
|
7630
9284
|
path: "/",
|
|
7631
|
-
tags: [
|
|
9285
|
+
tags: ["Models" /* MODELS */],
|
|
7632
9286
|
summary: "Create or replace a model provider",
|
|
7633
9287
|
description: "Create or replace a provider (models included). Well-known types use `type` as `name` (one each); `custom` is named by the caller. `auth.api_key`: real value sets/rotates; redacted keeps existing (400 if none).",
|
|
7634
9288
|
"x-fern-sdk-group-name": ["settings", "modelProviders"],
|
|
7635
|
-
"x-fern-sdk-method-name": "
|
|
9289
|
+
"x-fern-sdk-method-name": "create_or_update",
|
|
7636
9290
|
request: {
|
|
7637
9291
|
body: {
|
|
7638
|
-
content: { "application/json": { schema:
|
|
9292
|
+
content: { "application/json": { schema: UpdateModelProviderRequestSchema } },
|
|
7639
9293
|
required: true
|
|
7640
9294
|
}
|
|
7641
9295
|
},
|
|
@@ -7744,11 +9398,10 @@ import { withTimeout } from "@truefoundry/trueforge-core/core";
|
|
|
7744
9398
|
|
|
7745
9399
|
// src/routes/sandboxProviderRoutes.ts
|
|
7746
9400
|
import { createRoute as createRoute10 } from "@hono/zod-openapi";
|
|
7747
|
-
var SANDBOX_PROVIDERS_TAG = "Sandbox Providers";
|
|
7748
9401
|
var getSandboxProviderRoute = createRoute10({
|
|
7749
9402
|
method: "get",
|
|
7750
9403
|
path: "/",
|
|
7751
|
-
tags: [
|
|
9404
|
+
tags: ["Sandboxes" /* SANDBOXES */],
|
|
7752
9405
|
summary: "Get the configured sandbox provider",
|
|
7753
9406
|
description: "The single configured sandbox provider for this tenant. `auth.api_key` is redacted.",
|
|
7754
9407
|
"x-fern-sdk-group-name": ["settings", "sandboxProviders"],
|
|
@@ -7767,14 +9420,14 @@ var getSandboxProviderRoute = createRoute10({
|
|
|
7767
9420
|
var putSandboxProviderRoute = createRoute10({
|
|
7768
9421
|
method: "put",
|
|
7769
9422
|
path: "/",
|
|
7770
|
-
tags: [
|
|
9423
|
+
tags: ["Sandboxes" /* SANDBOXES */],
|
|
7771
9424
|
summary: "Create or replace the sandbox provider",
|
|
7772
9425
|
description: "Upserts the single sandbox provider for this tenant: creates it or replaces its entire configuration. `auth.api_key`: real value sets/rotates; redacted keeps existing (400 if none).",
|
|
7773
9426
|
"x-fern-sdk-group-name": ["settings", "sandboxProviders"],
|
|
7774
|
-
"x-fern-sdk-method-name": "
|
|
9427
|
+
"x-fern-sdk-method-name": "create_or_update",
|
|
7775
9428
|
request: {
|
|
7776
9429
|
body: {
|
|
7777
|
-
content: { "application/json": { schema:
|
|
9430
|
+
content: { "application/json": { schema: UpdateSandboxProviderRequestSchema } },
|
|
7778
9431
|
required: true
|
|
7779
9432
|
}
|
|
7780
9433
|
},
|
|
@@ -7887,11 +9540,10 @@ import { OpenAPIHono as OpenAPIHono11 } from "@hono/zod-openapi";
|
|
|
7887
9540
|
|
|
7888
9541
|
// src/routes/skillRoutes.ts
|
|
7889
9542
|
import { createRoute as createRoute11 } from "@hono/zod-openapi";
|
|
7890
|
-
var SKILLS_TAG = "Skills";
|
|
7891
9543
|
var listAvailableSkillsRoute = createRoute11({
|
|
7892
9544
|
method: "get",
|
|
7893
9545
|
path: "/",
|
|
7894
|
-
tags: [
|
|
9546
|
+
tags: ["Skills" /* SKILLS */],
|
|
7895
9547
|
summary: "List skills for chat",
|
|
7896
9548
|
description: "Configured skills as a slim name/description list for the composer.",
|
|
7897
9549
|
"x-fern-sdk-group-name": ["skills"],
|
|
@@ -7910,7 +9562,7 @@ var listAvailableSkillsRoute = createRoute11({
|
|
|
7910
9562
|
var listConfiguredSkillsRoute = createRoute11({
|
|
7911
9563
|
method: "get",
|
|
7912
9564
|
path: "/",
|
|
7913
|
-
tags: [
|
|
9565
|
+
tags: ["Skills" /* SKILLS */],
|
|
7914
9566
|
summary: "List configured skills",
|
|
7915
9567
|
description: "All configured skills with nested manifests (settings / admin projection).",
|
|
7916
9568
|
"x-fern-sdk-group-name": ["settings", "skills"],
|
|
@@ -7933,7 +9585,7 @@ var listConfiguredSkillsRoute = createRoute11({
|
|
|
7933
9585
|
var createSkillRoute = createRoute11({
|
|
7934
9586
|
method: "post",
|
|
7935
9587
|
path: "/",
|
|
7936
|
-
tags: [
|
|
9588
|
+
tags: ["Skills" /* SKILLS */],
|
|
7937
9589
|
summary: "Create a skill",
|
|
7938
9590
|
description: "Creates a skill keyed by `name`. Fails if `name` is already taken.",
|
|
7939
9591
|
"x-fern-sdk-group-name": ["settings", "skills"],
|
|
@@ -7962,14 +9614,14 @@ var createSkillRoute = createRoute11({
|
|
|
7962
9614
|
var putSkillRoute = createRoute11({
|
|
7963
9615
|
method: "put",
|
|
7964
9616
|
path: "/",
|
|
7965
|
-
tags: [
|
|
9617
|
+
tags: ["Skills" /* SKILLS */],
|
|
7966
9618
|
summary: "Create or replace a skill",
|
|
7967
9619
|
description: "Full upsert keyed by `name`: creates the skill or replaces its entire manifest.",
|
|
7968
9620
|
"x-fern-sdk-group-name": ["settings", "skills"],
|
|
7969
|
-
"x-fern-sdk-method-name": "
|
|
9621
|
+
"x-fern-sdk-method-name": "create_or_update",
|
|
7970
9622
|
request: {
|
|
7971
9623
|
body: {
|
|
7972
|
-
content: { "application/json": { schema:
|
|
9624
|
+
content: { "application/json": { schema: UpdateSkillRequestSchema } },
|
|
7973
9625
|
required: true
|
|
7974
9626
|
}
|
|
7975
9627
|
},
|
|
@@ -8097,27 +9749,27 @@ import {
|
|
|
8097
9749
|
} from "@truefoundry/trueforge-core/agent-session";
|
|
8098
9750
|
import {
|
|
8099
9751
|
AgentHarnessError,
|
|
9752
|
+
existingSandboxIdForProvider,
|
|
8100
9753
|
extractErrorLogFields as extractErrorLogFields6,
|
|
8101
9754
|
isAgentInputUserMessage,
|
|
8102
9755
|
isFileContentPart,
|
|
8103
9756
|
McpConnectionError as McpConnectionError5,
|
|
9757
|
+
rawSandboxId,
|
|
8104
9758
|
SandboxError as SandboxError2,
|
|
8105
|
-
validateSandboxOwnedByTenant,
|
|
8106
9759
|
VercelAILLM
|
|
8107
9760
|
} from "@truefoundry/trueforge-core/core";
|
|
8108
9761
|
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
8109
9762
|
import { streamSSE } from "hono/streaming";
|
|
8110
9763
|
|
|
8111
9764
|
// src/routes/turnRoutes.ts
|
|
8112
|
-
import { createRoute as createRoute12, z as
|
|
8113
|
-
var SESSIONS_TAG2 = "Sessions";
|
|
9765
|
+
import { createRoute as createRoute12, z as z24 } from "@hono/zod-openapi";
|
|
8114
9766
|
var TurnIdParamsSchema = SessionIdParamsSchema.extend({
|
|
8115
|
-
turn_id:
|
|
9767
|
+
turn_id: z24.string().min(1).describe("Turn identifier.")
|
|
8116
9768
|
});
|
|
8117
9769
|
var listTurnsRoute = createRoute12({
|
|
8118
9770
|
method: "get",
|
|
8119
9771
|
path: "/{session_id}/turns",
|
|
8120
|
-
tags: [
|
|
9772
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8121
9773
|
summary: "List turns in a session",
|
|
8122
9774
|
description: "List turns for a session (newest first by default), token-paginated. Only the session creator (`created_by`) may list turns.",
|
|
8123
9775
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -8149,7 +9801,7 @@ var listTurnsRoute = createRoute12({
|
|
|
8149
9801
|
var getTurnRoute = createRoute12({
|
|
8150
9802
|
method: "get",
|
|
8151
9803
|
path: "/{session_id}/turns/{turn_id}",
|
|
8152
|
-
tags: [
|
|
9804
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8153
9805
|
summary: "Get a turn",
|
|
8154
9806
|
description: "Fetch a single turn by ID. Only the session creator (`created_by`) may fetch it.",
|
|
8155
9807
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -8174,8 +9826,8 @@ var getTurnRoute = createRoute12({
|
|
|
8174
9826
|
});
|
|
8175
9827
|
var downloadSandboxFileRoute = createRoute12({
|
|
8176
9828
|
method: "get",
|
|
8177
|
-
path: "/{session_id}/turns/{turn_id}/download",
|
|
8178
|
-
tags: [
|
|
9829
|
+
path: "/{session_id}/turns/{turn_id}/download-sandbox-file",
|
|
9830
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8179
9831
|
summary: "Download a file from the turn sandbox",
|
|
8180
9832
|
description: "Download a file from the sandbox this turn ran in. Paths come from the assistant's `sandbox_artifacts` block. Only the session creator (`created_by`) may download.",
|
|
8181
9833
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -8186,7 +9838,7 @@ var downloadSandboxFileRoute = createRoute12({
|
|
|
8186
9838
|
},
|
|
8187
9839
|
responses: {
|
|
8188
9840
|
200: {
|
|
8189
|
-
content: { "application/octet-stream": { schema:
|
|
9841
|
+
content: { "application/octet-stream": { schema: z24.string().openapi({ format: "binary" }) } },
|
|
8190
9842
|
description: "File contents."
|
|
8191
9843
|
},
|
|
8192
9844
|
400: {
|
|
@@ -8195,7 +9847,7 @@ var downloadSandboxFileRoute = createRoute12({
|
|
|
8195
9847
|
},
|
|
8196
9848
|
403: {
|
|
8197
9849
|
content: { "application/json": { schema: RequestErrorResponseSchema } },
|
|
8198
|
-
description: "Caller is not the session creator
|
|
9850
|
+
description: "Caller is not the session creator."
|
|
8199
9851
|
},
|
|
8200
9852
|
404: {
|
|
8201
9853
|
content: { "application/json": { schema: RequestErrorResponseSchema } },
|
|
@@ -8222,7 +9874,7 @@ var downloadSandboxFileRoute = createRoute12({
|
|
|
8222
9874
|
var listTurnEventsRoute = createRoute12({
|
|
8223
9875
|
method: "get",
|
|
8224
9876
|
path: "/{session_id}/turns/{turn_id}/events",
|
|
8225
|
-
tags: [
|
|
9877
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8226
9878
|
summary: "List turn events",
|
|
8227
9879
|
description: "Paginated persisted events for a turn (insertion order by default). Only the session creator (`created_by`) may list events.",
|
|
8228
9880
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -8254,7 +9906,7 @@ var listTurnEventsRoute = createRoute12({
|
|
|
8254
9906
|
var createAndExecuteTurnRoute = createRoute12({
|
|
8255
9907
|
method: "post",
|
|
8256
9908
|
path: "/{session_id}/turns",
|
|
8257
|
-
tags: [
|
|
9909
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8258
9910
|
summary: "Create and execute a turn in a session",
|
|
8259
9911
|
description: `Create a turn within a session and execute it.
|
|
8260
9912
|
Only the session creator (\`created_by\`) may create turns.
|
|
@@ -8318,7 +9970,7 @@ Use \`previous_turn_id\` to chain to the session's last turn (defaults to \`auto
|
|
|
8318
9970
|
var subscribeTurnRoute = createRoute12({
|
|
8319
9971
|
method: "get",
|
|
8320
9972
|
path: "/{session_id}/turns/{turn_id}/subscribe",
|
|
8321
|
-
tags: [
|
|
9973
|
+
tags: ["Agent Sessions" /* AGENT_SESSIONS */],
|
|
8322
9974
|
summary: "Subscribe to a running turn",
|
|
8323
9975
|
description: "Subscribe to the live SSE stream for a turn. Only the session creator (`created_by`) may subscribe. Pass `after_sequence_number` to resume after a disconnect (exclusive \u2014 events after this sequence number are replayed).",
|
|
8324
9976
|
"x-fern-sdk-group-name": ["sessions"],
|
|
@@ -8599,7 +10251,7 @@ var StreamGoneError = class extends Error {
|
|
|
8599
10251
|
};
|
|
8600
10252
|
|
|
8601
10253
|
// src/runtime/sandboxFilePath.ts
|
|
8602
|
-
import { SandboxError, validateNoPathTraversal } from "@truefoundry/trueforge-core/core";
|
|
10254
|
+
import { SandboxError, validateNoPathTraversal as validateNoPathTraversal3 } from "@truefoundry/trueforge-core/core";
|
|
8603
10255
|
var SandboxInvalidPathError = class extends SandboxError {
|
|
8604
10256
|
statusCode = 400;
|
|
8605
10257
|
constructor({ path: path7, reason }) {
|
|
@@ -8625,7 +10277,7 @@ function validateSandboxFilePath(path7) {
|
|
|
8625
10277
|
reason: `must not have a segment longer than ${String(MAX_SEGMENT_BYTES)} bytes`
|
|
8626
10278
|
});
|
|
8627
10279
|
}
|
|
8628
|
-
|
|
10280
|
+
validateNoPathTraversal3(path7);
|
|
8629
10281
|
}
|
|
8630
10282
|
|
|
8631
10283
|
// src/apis/turns.ts
|
|
@@ -8662,7 +10314,8 @@ function createTurnResolver(deps) {
|
|
|
8662
10314
|
modelProviderStore,
|
|
8663
10315
|
logger,
|
|
8664
10316
|
signal,
|
|
8665
|
-
userRef
|
|
10317
|
+
userRef,
|
|
10318
|
+
sessionId
|
|
8666
10319
|
} = deps;
|
|
8667
10320
|
return new TurnResourceResolver({
|
|
8668
10321
|
llm: async (name) => {
|
|
@@ -8702,14 +10355,19 @@ function createTurnResolver(deps) {
|
|
|
8702
10355
|
const provider = await resolveSandboxProvider({
|
|
8703
10356
|
tenant_id: TENANT_ID,
|
|
8704
10357
|
store: sandboxProviderStore,
|
|
8705
|
-
logger
|
|
10358
|
+
logger,
|
|
10359
|
+
sessionId
|
|
8706
10360
|
});
|
|
8707
10361
|
if (provider === void 0) {
|
|
8708
10362
|
throw new HTTPException4(422, {
|
|
8709
10363
|
message: "no sandbox provider configured \u2014 PUT /settings/sandbox-providers"
|
|
8710
10364
|
});
|
|
8711
10365
|
}
|
|
8712
|
-
|
|
10366
|
+
const carriedSandboxId = existingSandboxIdForProvider({
|
|
10367
|
+
existingSandboxId,
|
|
10368
|
+
currentProviderType: provider.type
|
|
10369
|
+
});
|
|
10370
|
+
if (carriedSandboxId === void 0 && provider.type !== "local") {
|
|
8713
10371
|
const status = await checkSnapshotStatus({ store: sandboxProviderStore, tenant_id: TENANT_ID, logger });
|
|
8714
10372
|
if (status?.status !== "ready") {
|
|
8715
10373
|
throw new HTTPException4(422, {
|
|
@@ -8727,9 +10385,8 @@ function createTurnResolver(deps) {
|
|
|
8727
10385
|
logger,
|
|
8728
10386
|
gitSkills,
|
|
8729
10387
|
fileDownloadEnabled: spec.config.sandbox.file_downloads,
|
|
8730
|
-
existingSandboxId,
|
|
8731
|
-
tracing
|
|
8732
|
-
tenantName: TENANT_ID
|
|
10388
|
+
existingSandboxId: carriedSandboxId,
|
|
10389
|
+
tracing
|
|
8733
10390
|
});
|
|
8734
10391
|
},
|
|
8735
10392
|
agent: async (agentId) => {
|
|
@@ -8879,13 +10536,13 @@ function createTurnsRouter(deps) {
|
|
|
8879
10536
|
const provider = await resolveSandboxProvider({
|
|
8880
10537
|
tenant_id: TENANT_ID,
|
|
8881
10538
|
store: deps.sandboxProviderStore,
|
|
8882
|
-
logger: deps.logger
|
|
10539
|
+
logger: deps.logger,
|
|
10540
|
+
sessionId
|
|
8883
10541
|
});
|
|
8884
10542
|
if (provider === void 0) {
|
|
8885
10543
|
return c.json({ error: { message: "No sandbox provider configured" } }, 412);
|
|
8886
10544
|
}
|
|
8887
|
-
|
|
8888
|
-
const content = await provider.downloadFile({ sandboxId, path: path7 });
|
|
10545
|
+
const content = await provider.downloadFile({ sandboxId: rawSandboxId(sandboxId), path: path7 });
|
|
8889
10546
|
return c.body(toArrayBuffer(content), 200, {
|
|
8890
10547
|
"Content-Type": "application/octet-stream",
|
|
8891
10548
|
"Content-Length": String(content.byteLength),
|
|
@@ -8954,7 +10611,8 @@ function createTurnsRouter(deps) {
|
|
|
8954
10611
|
modelProviderStore: deps.modelProviderStore,
|
|
8955
10612
|
logger: deps.logger,
|
|
8956
10613
|
signal: abortController.signal,
|
|
8957
|
-
userRef: deps.resolveUserContext(c).userRef
|
|
10614
|
+
userRef: deps.resolveUserContext(c).userRef,
|
|
10615
|
+
sessionId
|
|
8958
10616
|
});
|
|
8959
10617
|
const title = session.record.last_turn_id ? void 0 : deriveSessionTitle(body.input);
|
|
8960
10618
|
let turn;
|
|
@@ -9134,9 +10792,9 @@ function readPackageVersion() {
|
|
|
9134
10792
|
var PACKAGE_VERSION = readPackageVersion();
|
|
9135
10793
|
|
|
9136
10794
|
// src/zodErrorResponse.ts
|
|
9137
|
-
import { z as
|
|
10795
|
+
import { z as z25 } from "@hono/zod-openapi";
|
|
9138
10796
|
function zodErrorResponse(c, error) {
|
|
9139
|
-
return c.json({ error: { message:
|
|
10797
|
+
return c.json({ error: { message: z25.prettifyError(error) } }, 400);
|
|
9140
10798
|
}
|
|
9141
10799
|
var zodValidationHook = (result, c) => {
|
|
9142
10800
|
if (!result.success) {
|
|
@@ -9155,7 +10813,7 @@ function createRequestBodyLimitMiddleware(maxSize) {
|
|
|
9155
10813
|
}
|
|
9156
10814
|
function createAppErrorHandler(params) {
|
|
9157
10815
|
return (error, c) => {
|
|
9158
|
-
if (error instanceof
|
|
10816
|
+
if (error instanceof z26.ZodError) {
|
|
9159
10817
|
return zodErrorResponse(c, error);
|
|
9160
10818
|
}
|
|
9161
10819
|
if (error instanceof HTTPException5) {
|
|
@@ -9177,7 +10835,8 @@ var openApiDocConfig = {
|
|
|
9177
10835
|
title: "TrueForge API",
|
|
9178
10836
|
description: "HTTP API for the TrueForge agent server (`/api/v1`). Interactive docs are served at `/api/v1/docs` (OpenAPI JSON at `/api/v1/openapi.json`).\n\n**Authentication:** Standalone deployments (no OIDC) accept requests without credentials \u2014 middleware stamps a local default user. When OIDC is configured, protected routes require a valid `id_token` cookie or `Authorization: Bearer` ID token. There is no built-in API-key scheme; pass custom headers only if your reverse proxy or IdP layer requires them.\n\nCovers DB-backed sessions, the agent registry, settings catalogs, and model/MCP/skill/sandbox providers.",
|
|
9179
10837
|
version: PACKAGE_VERSION
|
|
9180
|
-
}
|
|
10838
|
+
},
|
|
10839
|
+
tags: OPENAPI_DOCUMENT_TAGS
|
|
9181
10840
|
};
|
|
9182
10841
|
function registerOpenApiBearerAuth(app) {
|
|
9183
10842
|
app.openAPIRegistry.registerComponent("securitySchemes", BEARER_AUTH_SCHEME, {
|
|
@@ -9238,7 +10897,7 @@ function createServerApp(deps) {
|
|
|
9238
10897
|
)
|
|
9239
10898
|
);
|
|
9240
10899
|
app.route(
|
|
9241
|
-
"/api/v1/
|
|
10900
|
+
"/api/v1/catalogs",
|
|
9242
10901
|
withAuth(
|
|
9243
10902
|
createCatalogRouter({
|
|
9244
10903
|
modelCatalog: deps.modelCatalog,
|
|
@@ -9608,7 +11267,7 @@ init_config();
|
|
|
9608
11267
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
9609
11268
|
import { every } from "hono/combine";
|
|
9610
11269
|
import { compress } from "hono/compress";
|
|
9611
|
-
import { existsSync as
|
|
11270
|
+
import { existsSync as existsSync5 } from "fs";
|
|
9612
11271
|
import path3 from "path";
|
|
9613
11272
|
var SERVER_PATH_PREFIXES = ["/api", "/healthz"];
|
|
9614
11273
|
var HASHED_ASSET_PREFIX = "/assets/";
|
|
@@ -9618,7 +11277,7 @@ function isServerPath(pathname) {
|
|
|
9618
11277
|
return SERVER_PATH_PREFIXES.some((prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`));
|
|
9619
11278
|
}
|
|
9620
11279
|
function mountFrontend(app, dir) {
|
|
9621
|
-
if (!
|
|
11280
|
+
if (!existsSync5(path3.join(dir, "index.html"))) {
|
|
9622
11281
|
return false;
|
|
9623
11282
|
}
|
|
9624
11283
|
const serveFile = serveStatic({ root: dir, precompressed: true });
|
|
@@ -9751,9 +11410,9 @@ var ActiveTurnRegistry = class {
|
|
|
9751
11410
|
*/
|
|
9752
11411
|
track(input) {
|
|
9753
11412
|
const key = activeTurnKey(input.sessionId, input.turnId);
|
|
9754
|
-
const { promise: waitUntilCompleted, resolve } = Promise.withResolvers();
|
|
11413
|
+
const { promise: waitUntilCompleted, resolve: resolve3 } = Promise.withResolvers();
|
|
9755
11414
|
const markCompleted = () => {
|
|
9756
|
-
|
|
11415
|
+
resolve3(void 0);
|
|
9757
11416
|
};
|
|
9758
11417
|
const run = {
|
|
9759
11418
|
abortController: input.abortController,
|
|
@@ -9943,7 +11602,7 @@ try {
|
|
|
9943
11602
|
}
|
|
9944
11603
|
async function createStandalonePersistence(options) {
|
|
9945
11604
|
const { sqlitePath, logger } = options;
|
|
9946
|
-
await
|
|
11605
|
+
await mkdir5(path6.dirname(sqlitePath), { recursive: true });
|
|
9947
11606
|
const [{ createSqliteDb: createSqliteDb2 }, { migrateSqliteToLatest: migrateSqliteToLatest2 }, sqliteStores] = await Promise.all([
|
|
9948
11607
|
Promise.resolve().then(() => (init_client(), client_exports)),
|
|
9949
11608
|
Promise.resolve().then(() => (init_migrateSqlite(), migrateSqlite_exports)),
|
|
@@ -10092,6 +11751,26 @@ try {
|
|
|
10092
11751
|
});
|
|
10093
11752
|
if (configuration2.STANDALONE) {
|
|
10094
11753
|
printStandaloneStartupBanner({ version: PACKAGE_VERSION, color: shouldColorize() });
|
|
11754
|
+
await prepareCodeModeSocketParent({ path: configuration2.CODE_MODE_SOCKET_PARENT, logger });
|
|
11755
|
+
await ensureLocalSandboxRootParent(configuration2.LOCAL_SANDBOX_ROOT_PARENT);
|
|
11756
|
+
const { LocalSandboxProvider: LocalSandboxProvider2 } = await Promise.resolve().then(() => (init_LocalSandboxProvider(), LocalSandboxProvider_exports));
|
|
11757
|
+
const support = await LocalSandboxProvider2.isSupported({
|
|
11758
|
+
codeModeSocketParentPath: configuration2.CODE_MODE_SOCKET_PARENT
|
|
11759
|
+
});
|
|
11760
|
+
setCachedLocalSandboxSupport(support);
|
|
11761
|
+
if (support.supported) {
|
|
11762
|
+
logger.info("Local sandbox fallback is available", {
|
|
11763
|
+
platform: support.platform,
|
|
11764
|
+
shell: support.shell,
|
|
11765
|
+
python: support.python
|
|
11766
|
+
});
|
|
11767
|
+
} else {
|
|
11768
|
+
logger.warn("Local sandbox fallback is unavailable", {
|
|
11769
|
+
reason: support.reason,
|
|
11770
|
+
...support.platform === void 0 ? {} : { platform: support.platform },
|
|
11771
|
+
...support.attempts === void 0 ? {} : { attempts: support.attempts }
|
|
11772
|
+
});
|
|
11773
|
+
}
|
|
10095
11774
|
} else {
|
|
10096
11775
|
logger.info("TrueForge starting", { mode: "distributed" });
|
|
10097
11776
|
}
|
|
@@ -10146,9 +11825,9 @@ try {
|
|
|
10146
11825
|
logger.warn(`Drain timed out after ${String(configuration2.GRACEFUL_TIMEOUT_SECONDS)}s, exiting`);
|
|
10147
11826
|
process.exit(1);
|
|
10148
11827
|
}, configuration2.GRACEFUL_TIMEOUT_SECONDS * 1e3).unref();
|
|
10149
|
-
const closed = new Promise((
|
|
11828
|
+
const closed = new Promise((resolve3) => {
|
|
10150
11829
|
server.close(() => {
|
|
10151
|
-
|
|
11830
|
+
resolve3();
|
|
10152
11831
|
});
|
|
10153
11832
|
});
|
|
10154
11833
|
await activeTurns.shutdownAndWait(CancellationReason4.Abandoned);
|
|
@@ -10160,6 +11839,11 @@ try {
|
|
|
10160
11839
|
await redis?.close().catch((error) => {
|
|
10161
11840
|
logger.warn("[Redis] Error closing client during shutdown", extractErrorLogFields9(error));
|
|
10162
11841
|
});
|
|
11842
|
+
if (configuration2.STANDALONE) {
|
|
11843
|
+
await removeCodeModeSocketParent(configuration2.CODE_MODE_SOCKET_PARENT).catch((error) => {
|
|
11844
|
+
logger.warn("Error removing Code Mode socket parent during shutdown", extractErrorLogFields9(error));
|
|
11845
|
+
});
|
|
11846
|
+
}
|
|
10163
11847
|
await destroyDb();
|
|
10164
11848
|
process.exit(0);
|
|
10165
11849
|
};
|