@axiom-lattice/gateway 8.3.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
- package/dist/{a2a-BHBLACJV.mjs → a2a-TDZ3TT6H.mjs} +3 -3
- package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-INVCUSAW.mjs} +39 -2
- package/dist/a2a-standard-INVCUSAW.mjs.map +1 -0
- package/dist/chunk-A7YNAU5L.mjs +377 -0
- package/dist/chunk-A7YNAU5L.mjs.map +1 -0
- package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
- package/dist/chunk-BOPNXTMO.mjs.map +1 -0
- package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
- package/dist/chunk-JGQURU44.mjs.map +1 -0
- package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
- package/dist/chunk-LCM4OQTI.mjs.map +1 -0
- package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
- package/dist/chunk-MFWTGNQR.mjs.map +1 -0
- package/dist/{chunk-HYYLYIMH.mjs → chunk-OBODBHIS.mjs} +2 -2
- package/dist/chunk-T2HIWZGP.mjs +580 -0
- package/dist/chunk-T2HIWZGP.mjs.map +1 -0
- package/dist/chunk-Y7OVVPVU.mjs +13 -0
- package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
- package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
- package/dist/chunk-YR32MWEJ.mjs.map +1 -0
- package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
- package/dist/embed/{embed-page-AgentDetailSideApp-7YTMN643.js → embed-page-AgentDetailSideApp-TQEJK6ZF.js} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
- package/dist/embed/{embed-page-AgentGovernanceWorkbench-YWJ4ZLEV.js → embed-page-AgentGovernanceWorkbench-II2MRGCG.js} +1 -1
- package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-7OANGQXY.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-N2DXVMEK.js → embed-page-MessageGraph-KJPQWTHE.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
- package/dist/embed/{embed-page-ReactChart-3QDZX2WG.js → embed-page-ReactChart-2CAIM4JD.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
- package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-243MXRXC.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-MYIRJWFM.js → embed-page-TasksSideApp-OA7CULHS.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
- package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-Z4424X3J.js} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-3O6VJUZR.js → embed-page-TopologyRuntimeView-THTCZUS7.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
- package/dist/embed/{embed-page-WorkflowAutomationView-YHOS4WQR.js → embed-page-WorkflowAutomationView-YDIDG35E.js} +1 -1
- package/dist/embed/{embed-page-chunk-4XKCMM5C.js → embed-page-chunk-7ULTMRM2.js} +1 -1
- package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-FZX4A32D.js} +5 -5
- package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-JMP7EQEF.js} +1 -1
- package/dist/embed/{embed-page-chunk-IELKCYQX.js → embed-page-chunk-L2EGVVJS.js} +1 -1
- package/dist/embed/{embed-page-chunk-WGEXK773.js → embed-page-chunk-LDQGH44H.js} +1 -1
- package/dist/embed/{embed-page-chunk-JTL73EBV.js → embed-page-chunk-OMN6G52V.js} +1 -1
- package/dist/embed/{embed-page-chunk-PROZXFWS.js → embed-page-chunk-RKQJBKN2.js} +1 -1
- package/dist/embed/{embed-page-chunk-YMVKQQXJ.js → embed-page-chunk-VYS3TAK4.js} +1 -1
- package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-W5OP2CZG.js} +2 -2
- package/dist/embed/embed-page-chunk-Z6KWW64I.js +6837 -0
- package/dist/embed/{embed-page-en-E35GE7TA.js → embed-page-en-D4M3DPFT.js} +1 -1
- package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
- package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
- package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
- package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
- package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
- package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
- package/dist/embed/{embed-page-zh-ZLK3GLM3.js → embed-page-zh-NEXLMDHF.js} +1 -1
- package/dist/embed/embed-page.css +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/embed/mcp-app-page.js +61 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +2551 -1476
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +116 -690
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
- package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
- package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
- package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
- package/dist/open-27ZPLBIW.mjs.map +1 -0
- package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
- package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
- package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-BPLYUIX6.mjs} +4 -4
- package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
- package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
- package/dist/open-session-CLZT3HVG.mjs.map +1 -0
- package/dist/openLattice-D5YTSZBY.mjs +28 -0
- package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
- package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
- package/dist/resources-YD5DOPGD.mjs.map +1 -0
- package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
- package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
- package/package.json +10 -8
- package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
- package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
- package/dist/chunk-GL7RV5L3.mjs.map +0 -1
- package/dist/chunk-GPHOYR3X.mjs.map +0 -1
- package/dist/chunk-GXZFO3VU.mjs +0 -42
- package/dist/chunk-GXZFO3VU.mjs.map +0 -1
- package/dist/chunk-HVIKCUOF.mjs.map +0 -1
- package/dist/chunk-L6MBEEII.mjs.map +0 -1
- package/dist/embed/embed-page-chunk-IBRMWDHE.js +0 -6835
- package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
- package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
- package/dist/open-7ED27I5B.mjs.map +0 -1
- package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
- package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
- package/dist/openLattice-Q6M5S6CY.mjs +0 -18
- package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
- package/dist/resources-DAU5ZIPB.mjs.map +0 -1
- package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
- /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
- /package/dist/{a2a-BHBLACJV.mjs.map → a2a-TDZ3TT6H.mjs.map} +0 -0
- /package/dist/{chunk-HYYLYIMH.mjs.map → chunk-OBODBHIS.mjs.map} +0 -0
- /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
- /package/dist/{open-exposure-I3DN2X4E.mjs.map → open-exposure-BPLYUIX6.mjs.map} +0 -0
- /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
- /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
- /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
|
@@ -1,21 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isConnectionBacked
|
|
3
|
+
} from "./chunk-Y7OVVPVU.mjs";
|
|
1
4
|
import {
|
|
2
5
|
openLattice
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MFWTGNQR.mjs";
|
|
4
7
|
import "./chunk-NFEGQTCC.mjs";
|
|
5
8
|
|
|
6
9
|
// src/routes/open-catalog.ts
|
|
7
10
|
import { getStoreLattice } from "@axiom-lattice/core";
|
|
8
11
|
|
|
9
12
|
// src/services/open/openCatalogService.ts
|
|
10
|
-
import { getSandboxPluginCatalog } from "@axiom-lattice/core";
|
|
13
|
+
import { ConnectionRegistry, getSandboxPluginCatalog } from "@axiom-lattice/core";
|
|
11
14
|
async function defaultListTenantPlugins(tenantId) {
|
|
12
15
|
return (await getSandboxPluginCatalog().ensureLoaded(tenantId)).plugins;
|
|
13
16
|
}
|
|
17
|
+
async function defaultListConnections(tenantId, type) {
|
|
18
|
+
try {
|
|
19
|
+
const entries = await ConnectionRegistry.list(type, tenantId);
|
|
20
|
+
return entries.map((entry) => ({
|
|
21
|
+
key: entry.key,
|
|
22
|
+
name: entry.name,
|
|
23
|
+
...entry.description ? { description: entry.description } : {}
|
|
24
|
+
}));
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
14
29
|
var OpenCatalogService = class {
|
|
15
|
-
constructor(assistantStore, lattice = openLattice, listTenantPlugins = defaultListTenantPlugins) {
|
|
30
|
+
constructor(assistantStore, lattice = openLattice, listTenantPlugins = defaultListTenantPlugins, listConnections = defaultListConnections) {
|
|
16
31
|
this.assistantStore = assistantStore;
|
|
17
32
|
this.lattice = lattice;
|
|
18
33
|
this.listTenantPlugins = listTenantPlugins;
|
|
34
|
+
this.listConnections = listConnections;
|
|
35
|
+
}
|
|
36
|
+
async connectionItems(tenantId, type) {
|
|
37
|
+
try {
|
|
38
|
+
return await this.listConnections(tenantId, type);
|
|
39
|
+
} catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
19
42
|
}
|
|
20
43
|
async buildForTenant(tenantId) {
|
|
21
44
|
const domains = this.lattice.describeCatalog();
|
|
@@ -34,9 +57,13 @@ var OpenCatalogService = class {
|
|
|
34
57
|
items = [];
|
|
35
58
|
}
|
|
36
59
|
enriched.push({ ...domain, items });
|
|
37
|
-
|
|
38
|
-
enriched.push(domain);
|
|
60
|
+
continue;
|
|
39
61
|
}
|
|
62
|
+
const connections = domain.connectionBacked ? await this.connectionItems(tenantId, domain.domain) : void 0;
|
|
63
|
+
enriched.push({
|
|
64
|
+
...domain,
|
|
65
|
+
...connections === void 0 ? {} : { connections }
|
|
66
|
+
});
|
|
40
67
|
}
|
|
41
68
|
const knownDomains = new Set(domains.map((d) => d.domain));
|
|
42
69
|
let plugins = [];
|
|
@@ -55,13 +82,17 @@ var OpenCatalogService = class {
|
|
|
55
82
|
label: t.name,
|
|
56
83
|
description: t.description
|
|
57
84
|
}));
|
|
85
|
+
const connectionBacked = isConnectionBacked(plugin);
|
|
86
|
+
const connections = connectionBacked ? await this.connectionItems(tenantId, type) : void 0;
|
|
58
87
|
enriched.push({
|
|
59
88
|
domain: type,
|
|
60
89
|
label: plugin.meta.name,
|
|
61
90
|
source: "plugin",
|
|
62
91
|
scopeKind: "tenant",
|
|
63
92
|
itemKind: "action",
|
|
64
|
-
items
|
|
93
|
+
items,
|
|
94
|
+
connectionBacked,
|
|
95
|
+
...connections === void 0 ? {} : { connections }
|
|
65
96
|
});
|
|
66
97
|
}
|
|
67
98
|
return { domains: enriched };
|
|
@@ -87,4 +118,4 @@ function registerOpenCatalogRoutes(app) {
|
|
|
87
118
|
export {
|
|
88
119
|
registerOpenCatalogRoutes
|
|
89
120
|
};
|
|
90
|
-
//# sourceMappingURL=open-catalog-
|
|
121
|
+
//# sourceMappingURL=open-catalog-NVGP6SQP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes/open-catalog.ts","../src/services/open/openCatalogService.ts"],"sourcesContent":["/**\n * Open capability catalog endpoint (console-session guarded): returns the\n * grantable capabilities for the caller's tenant, driving the key picker.\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport { getStoreLattice } from \"@axiom-lattice/core\";\nimport type { AssistantStore } from \"@axiom-lattice/protocols\";\nimport { OpenCatalogService } from \"../services/open/openCatalogService\";\n\ninterface RequestWithUser extends FastifyRequest {\n user?: { id: string; tenantId?: string };\n}\n\nfunction tenantIdOf(request: FastifyRequest): string | undefined {\n return (request as RequestWithUser).user?.tenantId;\n}\n\nexport function registerOpenCatalogRoutes(app: FastifyInstance): void {\n app.get(\"/api/open/catalog\", async (request: FastifyRequest, reply: FastifyReply) => {\n const tenantId = tenantIdOf(request);\n if (!tenantId) {\n return reply.status(401).send({ success: false, error: \"Unauthorized\" });\n }\n const assistantStore = getStoreLattice(\"default\", \"assistant\")\n .store as AssistantStore;\n const service = new OpenCatalogService(assistantStore);\n const catalog = await service.buildForTenant(tenantId);\n return reply.status(200).send({ success: true, data: catalog });\n });\n}\n","/**\n * OpenCatalogService — assembles the tenant-scoped grantable capability\n * catalog for the key-management picker: registry domains (built-in + plugin)\n * plus per-tenant agent instances and tenant sandbox plugin domains.\n */\n\nimport { ConnectionRegistry, getSandboxPluginCatalog } from \"@axiom-lattice/core\";\nimport type {\n AssistantStore,\n OpenCatalog,\n OpenCatalogConnection,\n OpenCatalogDomain,\n OpenCatalogItem,\n Plugin,\n} from \"@axiom-lattice/protocols\";\nimport { OpenLattice, openLattice } from \"./openLattice\";\nimport { isConnectionBacked } from \"./pluginConnectionMetadata\";\n\n/** Reads the tenant's sandbox plugins; injectable for tests. */\nexport type TenantPluginLister = (tenantId: string) => Promise<Plugin[]>;\n\n/**\n * Default tenant plugin source: the process-wide sandbox plugin catalog.\n * A missing/unconfigured sandbox degrades to an empty list.\n */\nasync function defaultListTenantPlugins(tenantId: string): Promise<Plugin[]> {\n return (await getSandboxPluginCatalog().ensureLoaded(tenantId)).plugins;\n}\n\n/** Lists a tenant's connections for one plugin type; injectable for tests. */\nexport type TenantConnectionLister = (\n tenantId: string,\n type: string,\n) => Promise<OpenCatalogConnection[]>;\n\n/**\n * Default connection source: the tenant Connection Store. A missing or failing\n * store degrades to an empty list; config/secrets are never copied.\n */\nasync function defaultListConnections(\n tenantId: string,\n type: string,\n): Promise<OpenCatalogConnection[]> {\n try {\n const entries = await ConnectionRegistry.list(type, tenantId);\n return entries.map((entry) => ({\n key: entry.key,\n name: entry.name,\n ...(entry.description ? { description: entry.description } : {}),\n }));\n } catch {\n return [];\n }\n}\n\nexport class OpenCatalogService {\n constructor(\n private readonly assistantStore: AssistantStore,\n private readonly lattice: OpenLattice = openLattice,\n private readonly listTenantPlugins: TenantPluginLister = defaultListTenantPlugins,\n private readonly listConnections: TenantConnectionLister = defaultListConnections,\n ) {}\n\n private async connectionItems(\n tenantId: string,\n type: string,\n ): Promise<OpenCatalogConnection[]> {\n try {\n return await this.listConnections(tenantId, type);\n } catch {\n return [];\n }\n }\n\n async buildForTenant(tenantId: string): Promise<OpenCatalog> {\n const domains = this.lattice.describeCatalog();\n const enriched: OpenCatalogDomain[] = [];\n for (const domain of domains) {\n if (domain.domain === \"agent\" && domain.itemKind === \"instance\") {\n let items: OpenCatalogDomain[\"items\"] = [];\n try {\n const assistants = await this.assistantStore.getAllAssistants(tenantId);\n items = assistants.map((a) => ({\n id: a.id,\n toolName: `invoke_${a.id}`,\n label: a.name || a.id,\n }));\n } catch {\n items = [];\n }\n enriched.push({ ...domain, items });\n continue;\n }\n const connections = domain.connectionBacked\n ? await this.connectionItems(tenantId, domain.domain)\n : undefined;\n enriched.push({\n ...domain,\n ...(connections === undefined ? {} : { connections }),\n });\n }\n\n // Tenant sandbox plugins live in per-tenant volumes and are not part of the\n // OpenLattice registry, so without this the console picker could never grant\n // their domains. Global domains win on collision (consistent with\n // `mergeOpenCatalog`).\n const knownDomains = new Set(domains.map((d) => d.domain));\n let plugins: Plugin[] = [];\n try {\n plugins = await this.listTenantPlugins(tenantId);\n } catch {\n plugins = [];\n }\n for (const plugin of plugins) {\n const type = plugin.meta.type;\n if (knownDomains.has(type)) continue;\n knownDomains.add(type);\n const items: OpenCatalogItem[] = (plugin.meta.tools ?? [])\n .filter((t) => t.expose)\n .map((t) => ({\n id: t.name,\n toolName: `${type}_${t.name}`,\n label: t.name,\n description: t.description,\n }));\n const connectionBacked = isConnectionBacked(plugin);\n const connections = connectionBacked\n ? await this.connectionItems(tenantId, type)\n : undefined;\n enriched.push({\n domain: type,\n label: plugin.meta.name,\n source: \"plugin\",\n scopeKind: \"tenant\",\n itemKind: \"action\",\n items,\n connectionBacked,\n ...(connections === undefined ? {} : { connections }),\n });\n }\n return { domains: enriched };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
buildTenantOpenPlugins,
|
|
6
6
|
mergeOpenCatalog
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JGQURU44.mjs";
|
|
8
|
+
import "./chunk-XUWD4VKY.mjs";
|
|
8
9
|
import {
|
|
9
10
|
openLattice
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-XUWD4VKY.mjs";
|
|
11
|
+
} from "./chunk-MFWTGNQR.mjs";
|
|
12
12
|
import "./chunk-NFEGQTCC.mjs";
|
|
13
13
|
|
|
14
14
|
// src/routes/open-exposure.ts
|
|
@@ -106,4 +106,4 @@ function registerOpenExposureRoutes(app, deps = {}) {
|
|
|
106
106
|
export {
|
|
107
107
|
registerOpenExposureRoutes
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=open-exposure-
|
|
109
|
+
//# sourceMappingURL=open-exposure-BPLYUIX6.mjs.map
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
OpenAuditService,
|
|
3
3
|
OpenCredentialService
|
|
4
4
|
} from "./chunk-JUA3VJE7.mjs";
|
|
5
|
-
import {
|
|
6
|
-
openLattice
|
|
7
|
-
} from "./chunk-HVIKCUOF.mjs";
|
|
8
5
|
import {
|
|
9
6
|
resolveContextSandbox
|
|
10
7
|
} from "./chunk-PAKR5N7U.mjs";
|
|
11
8
|
import "./chunk-EXPERMZR.mjs";
|
|
9
|
+
import {
|
|
10
|
+
openLattice
|
|
11
|
+
} from "./chunk-MFWTGNQR.mjs";
|
|
12
12
|
import "./chunk-NFEGQTCC.mjs";
|
|
13
13
|
|
|
14
14
|
// src/routes/open-files.ts
|
|
@@ -143,4 +143,4 @@ function registerOpenFilesRoutes(app, deps = {}) {
|
|
|
143
143
|
export {
|
|
144
144
|
registerOpenFilesRoutes
|
|
145
145
|
};
|
|
146
|
-
//# sourceMappingURL=open-files-
|
|
146
|
+
//# sourceMappingURL=open-files-JPB2NBL6.mjs.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
openLattice
|
|
3
|
-
} from "./chunk-HVIKCUOF.mjs";
|
|
4
1
|
import {
|
|
5
2
|
BROWSER_GRANTS
|
|
6
3
|
} from "./chunk-7CG57LEF.mjs";
|
|
@@ -8,6 +5,9 @@ import {
|
|
|
8
5
|
init_openToken,
|
|
9
6
|
signOpenToken
|
|
10
7
|
} from "./chunk-EXPERMZR.mjs";
|
|
8
|
+
import {
|
|
9
|
+
openLattice
|
|
10
|
+
} from "./chunk-MFWTGNQR.mjs";
|
|
11
11
|
import "./chunk-NFEGQTCC.mjs";
|
|
12
12
|
|
|
13
13
|
// src/routes/open-session.ts
|
|
@@ -24,7 +24,7 @@ function registerOpenSessionRoutes(app) {
|
|
|
24
24
|
}
|
|
25
25
|
const store = getStoreLattice("default", "sharedResource").store;
|
|
26
26
|
const record = await store.findByToken(shareToken);
|
|
27
|
-
if (!record || record.revoked) {
|
|
27
|
+
if (!record || record.kind !== "file" || record.revoked) {
|
|
28
28
|
return reply.status(404).send({ error: "SHARE_NOT_FOUND" });
|
|
29
29
|
}
|
|
30
30
|
if (record.expiresAt && new Date(record.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
@@ -50,4 +50,4 @@ function registerOpenSessionRoutes(app) {
|
|
|
50
50
|
export {
|
|
51
51
|
registerOpenSessionRoutes
|
|
52
52
|
};
|
|
53
|
-
//# sourceMappingURL=open-session-
|
|
53
|
+
//# sourceMappingURL=open-session-CLZT3HVG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes/open-session.ts"],"sourcesContent":["/**\n * Open session exchange: downgrades a share token into a short-lived browser\n * token scoped to the platform browser whitelist. The share link is the key;\n * grants come from the whitelist (V1: kb read-only), not the caller.\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport { getStoreLattice } from \"@axiom-lattice/core\";\nimport type { SharedResourceStore } from \"@axiom-lattice/protocols\";\nimport { signOpenToken } from \"../services/open/openToken\";\nimport { BROWSER_GRANTS } from \"../services/open/previewToken\";\nimport type { OpenToolDescriptor } from \"../services/open/openLattice\";\nimport { openLattice } from \"../services/open/openLattice\";\n\nexport function registerOpenSessionRoutes(app: FastifyInstance): void {\n app.post(\n \"/open/session\",\n async (request: FastifyRequest, reply: FastifyReply) => {\n const body = (request.body ?? {}) as { shareToken?: string };\n const shareToken = body.shareToken;\n if (typeof shareToken !== \"string\" || shareToken.length === 0) {\n return reply.status(400).send({ error: \"SHARE_TOKEN_REQUIRED\" });\n }\n\n const store = getStoreLattice(\"default\", \"sharedResource\")\n .store as SharedResourceStore;\n const record = await store.findByToken(shareToken);\n if (!record || record.kind !== \"file\" || record.revoked) {\n return reply.status(404).send({ error: \"SHARE_NOT_FOUND\" });\n }\n if (record.expiresAt && new Date(record.expiresAt) < new Date()) {\n return reply.status(410).send({ error: \"SHARE_EXPIRED\" });\n }\n\n const grants = BROWSER_GRANTS;\n const browserToken = signOpenToken({\n kind: \"browser_token\",\n tenantId: record.tenantId,\n projectId: record.projectId,\n shareId: record.token,\n grants,\n });\n\n const actions: OpenToolDescriptor[] = await openLattice.catalogFor(grants, { tenantId: record.tenantId });\n\n return reply.status(200).send({\n browserToken,\n expiresAt: Date.now() + 15 * 60 * 1000,\n actions: actions.map((a) => a.toolName),\n });\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;AASA;AAFA,SAAS,uBAAuB;AAOzB,SAAS,0BAA0B,KAA4B;AACpE,MAAI;AAAA,IACF;AAAA,IACA,OAAO,SAAyB,UAAwB;AACtD,YAAM,OAAQ,QAAQ,QAAQ,CAAC;AAC/B,YAAM,aAAa,KAAK;AACxB,UAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAAG;AAC7D,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,uBAAuB,CAAC;AAAA,MACjE;AAEA,YAAM,QAAQ,gBAAgB,WAAW,gBAAgB,EACtD;AACH,YAAM,SAAS,MAAM,MAAM,YAAY,UAAU;AACjD,UAAI,CAAC,UAAU,OAAO,SAAS,UAAU,OAAO,SAAS;AACvD,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,MAC5D;AACA,UAAI,OAAO,aAAa,IAAI,KAAK,OAAO,SAAS,IAAI,oBAAI,KAAK,GAAG;AAC/D,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAAA,MAC1D;AAEA,YAAM,SAAS;AACf,YAAM,eAAe,cAAc;AAAA,QACjC,MAAM;AAAA,QACN,UAAU,OAAO;AAAA,QACjB,WAAW,OAAO;AAAA,QAClB,SAAS,OAAO;AAAA,QAChB;AAAA,MACF,CAAC;AAED,YAAM,UAAgC,MAAM,YAAY,WAAW,QAAQ,EAAE,UAAU,OAAO,SAAS,CAAC;AAExG,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK;AAAA,QAC5B;AAAA,QACA,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK;AAAA,QAClC,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ;AAAA,MACxC,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActionNotFoundError,
|
|
3
|
+
ConnectionNotAllowedError,
|
|
4
|
+
GrantDeniedError,
|
|
5
|
+
OpenLattice,
|
|
6
|
+
ValidationError,
|
|
7
|
+
findCoveringGrant,
|
|
8
|
+
grantCovers,
|
|
9
|
+
normalizeConnectionKeys,
|
|
10
|
+
normalizeGrantConnectionKeys,
|
|
11
|
+
openLattice,
|
|
12
|
+
resolveCoveringConnectionKeys
|
|
13
|
+
} from "./chunk-MFWTGNQR.mjs";
|
|
14
|
+
import "./chunk-NFEGQTCC.mjs";
|
|
15
|
+
export {
|
|
16
|
+
ActionNotFoundError,
|
|
17
|
+
ConnectionNotAllowedError,
|
|
18
|
+
GrantDeniedError,
|
|
19
|
+
OpenLattice,
|
|
20
|
+
ValidationError,
|
|
21
|
+
findCoveringGrant,
|
|
22
|
+
grantCovers,
|
|
23
|
+
normalizeConnectionKeys,
|
|
24
|
+
normalizeGrantConnectionKeys,
|
|
25
|
+
openLattice,
|
|
26
|
+
resolveCoveringConnectionKeys
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=openLattice-D5YTSZBY.mjs.map
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectExposedPluginUiResources,
|
|
3
3
|
registerExposedPluginTools
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-BOPNXTMO.mjs";
|
|
5
|
+
import "./chunk-Y7OVVPVU.mjs";
|
|
6
|
+
import "./chunk-MFWTGNQR.mjs";
|
|
6
7
|
import "./chunk-NFEGQTCC.mjs";
|
|
7
8
|
export {
|
|
8
9
|
collectExposedPluginUiResources,
|
|
9
10
|
registerExposedPluginTools
|
|
10
11
|
};
|
|
11
|
-
//# sourceMappingURL=registerExposedPlugins-
|
|
12
|
+
//# sourceMappingURL=registerExposedPlugins-2JGXSMAL.mjs.map
|
|
@@ -15,6 +15,9 @@ import bcrypt from "bcryptjs";
|
|
|
15
15
|
import {
|
|
16
16
|
generateToken,
|
|
17
17
|
createSharePayload,
|
|
18
|
+
createMcpAppSharePayload,
|
|
19
|
+
SharePayloadError,
|
|
20
|
+
verifyMcpUiScope,
|
|
18
21
|
buildNamedVolumeName
|
|
19
22
|
} from "@axiom-lattice/core";
|
|
20
23
|
|
|
@@ -41,9 +44,12 @@ var ResourceController = class {
|
|
|
41
44
|
async createShare(request, reply) {
|
|
42
45
|
const userId = request.user ? request.user.id ?? "unknown" : "unknown";
|
|
43
46
|
const tenantId = request.headers["x-tenant-id"] || "default";
|
|
44
|
-
const workspaceId = request.headers["x-workspace-id"];
|
|
45
|
-
const projectId = request.headers["x-project-id"];
|
|
47
|
+
const workspaceId = request.headers["x-workspace-id"] || "";
|
|
48
|
+
const projectId = request.headers["x-project-id"] || "";
|
|
46
49
|
const body = request.body;
|
|
50
|
+
if (body.kind === "mcpApp") {
|
|
51
|
+
return this.createMcpAppShare(request, reply, body, tenantId, userId, workspaceId, projectId);
|
|
52
|
+
}
|
|
47
53
|
if (!workspaceId || !projectId) {
|
|
48
54
|
return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
|
|
49
55
|
}
|
|
@@ -66,6 +72,53 @@ var ResourceController = class {
|
|
|
66
72
|
}
|
|
67
73
|
return reply.status(201).send({ token, url: `/s/${token}` });
|
|
68
74
|
}
|
|
75
|
+
async createMcpAppShare(request, reply, body, tenantId, userId, workspaceId, projectId) {
|
|
76
|
+
if (body.visibility !== "public" && body.visibility !== "password") {
|
|
77
|
+
return reply.status(400).send({ error: "INTERNAL_NOT_SUPPORTED" });
|
|
78
|
+
}
|
|
79
|
+
if (body.app.kind === "plugin") {
|
|
80
|
+
const scope = body.app.scope ? verifyMcpUiScope(body.app.scope) : null;
|
|
81
|
+
if (!scope) return reply.status(400).send({ error: "SCOPE_REQUIRED" });
|
|
82
|
+
if (scope.tid !== tenantId || scope.plugin !== body.app.pluginType || scope.resource !== body.app.resource) {
|
|
83
|
+
return reply.status(403).send({ error: "SCOPE_MISMATCH" });
|
|
84
|
+
}
|
|
85
|
+
} else if (!body.app.serverKey || !body.app.resourceUri) {
|
|
86
|
+
return reply.status(400).send({ error: "INVALID_REQUEST" });
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const payload = createMcpAppSharePayload({
|
|
90
|
+
tenantId,
|
|
91
|
+
createdBy: userId,
|
|
92
|
+
workspaceId,
|
|
93
|
+
projectId,
|
|
94
|
+
app: body.app,
|
|
95
|
+
title: body.title,
|
|
96
|
+
visibility: body.visibility,
|
|
97
|
+
password: body.password,
|
|
98
|
+
expiresAt: body.expiresAt ? new Date(body.expiresAt) : void 0,
|
|
99
|
+
maxAccess: body.maxAccess
|
|
100
|
+
});
|
|
101
|
+
const record = await this.deps.store.create(payload);
|
|
102
|
+
request.log.info({
|
|
103
|
+
event: "share.created",
|
|
104
|
+
tenantId,
|
|
105
|
+
userId,
|
|
106
|
+
kind: "mcpApp",
|
|
107
|
+
visibility: body.visibility,
|
|
108
|
+
appKind: body.app.kind,
|
|
109
|
+
tool: body.app.toolName,
|
|
110
|
+
tokenPrefix: record.token.slice(0, 8)
|
|
111
|
+
}, "share created");
|
|
112
|
+
return reply.status(201).send({ token: record.token, url: `/s/${record.token}` });
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error instanceof SharePayloadError) {
|
|
115
|
+
const status = error.code === "PAYLOAD_TOO_LARGE" ? 413 : 400;
|
|
116
|
+
return reply.status(status).send({ error: error.code });
|
|
117
|
+
}
|
|
118
|
+
request.log.error(error);
|
|
119
|
+
return reply.status(500).send({ error: "SHARE_CREATE_FAILED" });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
69
122
|
async listShares(request, reply) {
|
|
70
123
|
const userId = request.user ? request.user.id ?? "unknown" : "unknown";
|
|
71
124
|
const tenantId = request.headers["x-tenant-id"] || "default";
|
|
@@ -80,6 +133,9 @@ var ResourceController = class {
|
|
|
80
133
|
if (!record || record.createdBy !== userId) {
|
|
81
134
|
return reply.status(404).send({ error: "Share not found" });
|
|
82
135
|
}
|
|
136
|
+
if (record.kind === "mcpApp" && patch.visibility === "internal") {
|
|
137
|
+
return reply.status(400).send({ error: "INTERNAL_NOT_SUPPORTED" });
|
|
138
|
+
}
|
|
83
139
|
await this.deps.store.update(token, patch);
|
|
84
140
|
if (patch.revoked) {
|
|
85
141
|
this.deps.cache.invalidate(token);
|
|
@@ -112,6 +168,7 @@ var ResourceController = class {
|
|
|
112
168
|
if (record2.expiresAt && new Date(record2.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
113
169
|
return reply.status(410).send("Expired");
|
|
114
170
|
}
|
|
171
|
+
if (record2.kind === "mcpApp") return reply.status(404).send("Not found");
|
|
115
172
|
if (!this._isInternalRequest(request)) {
|
|
116
173
|
if (record2.visibility === "internal") {
|
|
117
174
|
return reply.status(401).send({ error: "Internal access only" });
|
|
@@ -135,6 +192,9 @@ var ResourceController = class {
|
|
|
135
192
|
this.deps.logger.info("[share] token expired", { token, expiresAt: record.expiresAt });
|
|
136
193
|
return reply.status(410).send("Expired");
|
|
137
194
|
}
|
|
195
|
+
if (record.kind === "mcpApp") {
|
|
196
|
+
return this._serveMcpAppShell(record, reply);
|
|
197
|
+
}
|
|
138
198
|
this.deps.logger.info("[share] record found", {
|
|
139
199
|
token,
|
|
140
200
|
volume: record.address.volume,
|
|
@@ -297,6 +357,26 @@ var ResourceController = class {
|
|
|
297
357
|
"Access-Control-Allow-Origin": "*"
|
|
298
358
|
});
|
|
299
359
|
}
|
|
360
|
+
async _serveMcpAppShell(_record, reply) {
|
|
361
|
+
const html = [
|
|
362
|
+
"<!doctype html>",
|
|
363
|
+
'<html lang="en">',
|
|
364
|
+
" <head>",
|
|
365
|
+
' <meta charset="utf-8" />',
|
|
366
|
+
' <meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
367
|
+
" <title>Shared app</title>",
|
|
368
|
+
" </head>",
|
|
369
|
+
" <body>",
|
|
370
|
+
' <div id="axiom-mcp-app-root">Loading\u2026</div>',
|
|
371
|
+
' <script type="module" src="/embed/mcp-app-page.js"></script>',
|
|
372
|
+
" </body>",
|
|
373
|
+
"</html>"
|
|
374
|
+
].join("\n");
|
|
375
|
+
return reply.header("cache-control", "no-store").header("referrer-policy", "no-referrer").header("x-content-type-options", "nosniff").header(
|
|
376
|
+
"content-security-policy",
|
|
377
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self'; frame-ancestors 'none'; base-uri 'none'"
|
|
378
|
+
).type("text/html; charset=utf-8").send(html);
|
|
379
|
+
}
|
|
300
380
|
async _serveFile(address, token, subPath, sandboxConfig, reply, rangeHeader) {
|
|
301
381
|
const resourcePath = subPath ? this._resolveSafeSubPath(address.resourcePath, subPath) : address.resourcePath;
|
|
302
382
|
return this._resolveAndServe(
|
|
@@ -399,4 +479,4 @@ var ResourceController = class {
|
|
|
399
479
|
export {
|
|
400
480
|
ResourceController
|
|
401
481
|
};
|
|
402
|
-
//# sourceMappingURL=resources-
|
|
482
|
+
//# sourceMappingURL=resources-YD5DOPGD.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/controllers/resources.ts","../src/services/open/sandboxGrants.ts"],"sourcesContent":["import { FastifyRequest, FastifyReply } from \"fastify\";\nimport type {\n CreateShareRequest,\n FileShareRecord,\n McpAppShareRecord,\n ResourceAddress,\n ShareRecord,\n SharedResourceStore,\n} from \"@axiom-lattice/protocols\";\nimport bcrypt from \"bcryptjs\";\nimport {\n generateToken,\n createSharePayload,\n createMcpAppSharePayload,\n SharePayloadError,\n verifyMcpUiScope,\n TokenCache,\n SandboxLatticeManager,\n buildNamedVolumeName,\n} from \"@axiom-lattice/core\";\nimport { getContentTypeFromFilename } from \"../utils/mime\";\nimport { sendBufferWithRange } from \"../utils/http-range\";\nimport { sanitizeShareOpenGrants } from \"../services/open/sandboxGrants\";\n\ninterface ResourceControllerDeps {\n store: SharedResourceStore;\n sandboxManager: SandboxLatticeManager;\n cache: TokenCache;\n logger: { info: (msg: string, obj?: object) => void; warn: (msg: string, obj?: object) => void; error: (msg: string, obj?: object) => void };\n}\n\nexport class ResourceController {\n constructor(private deps: ResourceControllerDeps) {}\n\n async createShare(request: FastifyRequest, reply: FastifyReply) {\n const userId = (request as any).user\n ? (request as any).user.id ?? \"unknown\"\n : \"unknown\";\n const tenantId = (request.headers[\"x-tenant-id\"] as string) || \"default\";\n const workspaceId = (request.headers[\"x-workspace-id\"] as string) || \"\";\n const projectId = (request.headers[\"x-project-id\"] as string) || \"\";\n const body = request.body as CreateShareRequest;\n\n if (body.kind === \"mcpApp\") {\n return this.createMcpAppShare(request, reply, body, tenantId, userId, workspaceId, projectId);\n }\n\n if (!workspaceId || !projectId) {\n return reply.status(400).send({ error: \"x-workspace-id and x-project-id headers required\" });\n }\n\n const payload = createSharePayload(\n tenantId,\n workspaceId,\n projectId,\n userId as string,\n body,\n );\n const token = generateToken();\n\n try {\n await this.deps.store.create({ ...payload, token });\n this.deps.logger.info(\n \"[share] created\",\n { token, originalPath: body.resourcePath, normalizedPath: payload.address.resourcePath, volume: payload.address.volume, userId },\n );\n } catch {\n return reply.status(500).send({ error: \"Failed to create share\" });\n }\n\n return reply.status(201).send({ token, url: `/s/${token}` });\n }\n\n private async createMcpAppShare(\n request: FastifyRequest,\n reply: FastifyReply,\n body: Extract<CreateShareRequest, { kind: \"mcpApp\" }>,\n tenantId: string,\n userId: string,\n workspaceId: string,\n projectId: string,\n ) {\n if (body.visibility !== \"public\" && body.visibility !== \"password\") {\n return reply.status(400).send({ error: \"INTERNAL_NOT_SUPPORTED\" });\n }\n if (body.app.kind === \"plugin\") {\n const scope = body.app.scope ? verifyMcpUiScope(body.app.scope) : null;\n if (!scope) return reply.status(400).send({ error: \"SCOPE_REQUIRED\" });\n if (\n scope.tid !== tenantId ||\n scope.plugin !== body.app.pluginType ||\n scope.resource !== body.app.resource\n ) {\n return reply.status(403).send({ error: \"SCOPE_MISMATCH\" });\n }\n } else if (!body.app.serverKey || !body.app.resourceUri) {\n return reply.status(400).send({ error: \"INVALID_REQUEST\" });\n }\n\n try {\n const payload = createMcpAppSharePayload({\n tenantId,\n createdBy: userId,\n workspaceId,\n projectId,\n app: body.app,\n title: body.title,\n visibility: body.visibility,\n password: body.password,\n expiresAt: body.expiresAt ? new Date(body.expiresAt) : undefined,\n maxAccess: body.maxAccess,\n });\n const record = await this.deps.store.create(payload);\n request.log.info({\n event: \"share.created\",\n tenantId,\n userId,\n kind: \"mcpApp\",\n visibility: body.visibility,\n appKind: body.app.kind,\n tool: body.app.toolName,\n tokenPrefix: record.token.slice(0, 8),\n }, \"share created\");\n return reply.status(201).send({ token: record.token, url: `/s/${record.token}` });\n } catch (error) {\n if (error instanceof SharePayloadError) {\n const status = error.code === \"PAYLOAD_TOO_LARGE\" ? 413 : 400;\n return reply.status(status).send({ error: error.code });\n }\n request.log.error(error);\n return reply.status(500).send({ error: \"SHARE_CREATE_FAILED\" });\n }\n }\n\n async listShares(request: FastifyRequest, reply: FastifyReply) {\n const userId = (request as any).user\n ? (request as any).user.id ?? \"unknown\"\n : \"unknown\";\n const tenantId = (request.headers[\"x-tenant-id\"] as string) || \"default\";\n const shares = await this.deps.store.listByUser(tenantId, userId as string);\n return reply.send(shares.filter((r: any) => (r as any).visibility !== \"internal\"));\n }\n\n async updateShare(request: FastifyRequest, reply: FastifyReply) {\n const { token } = request.params as unknown as { token: string };\n const userId = (request as any).user\n ? (request as any).user.id ?? \"unknown\"\n : \"unknown\";\n const patch = request.body as any;\n\n const record = await this.deps.store.findByToken(token);\n if (!record || record.createdBy !== userId) {\n return reply.status(404).send({ error: \"Share not found\" });\n }\n if (record.kind === \"mcpApp\" && patch.visibility === \"internal\") {\n return reply.status(400).send({ error: \"INTERNAL_NOT_SUPPORTED\" });\n }\n\n await this.deps.store.update(token, patch);\n\n if (patch.revoked) {\n this.deps.cache.invalidate(token);\n }\n\n return reply.send({ ok: true });\n }\n\n async revokeShare(request: FastifyRequest, reply: FastifyReply) {\n const { token } = request.params as unknown as { token: string };\n const userId = (request as any).user\n ? (request as any).user.id ?? \"unknown\"\n : \"unknown\";\n\n const record = await this.deps.store.findByToken(token);\n if (!record || record.createdBy !== userId) {\n return reply.status(404).send({ error: \"Share not found\" });\n }\n\n await this.deps.store.update(token, { revoked: true });\n this.deps.cache.invalidate(token);\n\n return reply.send({ ok: true });\n }\n\n async resolveResource(request: FastifyRequest, reply: FastifyReply) {\n const params = request.params as unknown as { token: string; \"*\"?: string };\n const { token } = params;\n const subPath = params[\"*\"] ?? \"\";\n\n this.deps.logger.info(\"[share] resolve start\", { token, subPath });\n\n const isApi = subPath && /(^|\\/)api\\//.test(subPath) && (subPath.endsWith(\".js\") || subPath.endsWith(\".py\"));\n\n // Only API paths accept non-GET methods\n if (request.method !== \"GET\" && !isApi) {\n return reply.status(405).send({ error: \"Method not allowed\" });\n }\n\n // API runtime — proxy to sandbox. Detected by /api/ anywhere in subPath.\n if (isApi) {\n const record = await this.deps.store.findByToken(token);\n if (!record || record.revoked) return reply.status(404).send(\"Not found\");\n if (record.expiresAt && new Date(record.expiresAt) < new Date()) {\n return reply.status(410).send(\"Expired\");\n }\n if (record.kind === \"mcpApp\") return reply.status(404).send(\"Not found\");\n // Auth check for API calls (same as file access)\n if (!this._isInternalRequest(request)) {\n if (record.visibility === \"internal\") {\n return reply.status(401).send({ error: \"Internal access only\" });\n }\n if (record.visibility === \"password\") {\n if (!this._isUnlocked(request, token)) {\n return reply.status(401).send({ error: \"Password required\" });\n }\n }\n }\n const ext = subPath.endsWith(\".py\") ? \"py\" : \"js\";\n return this._execApi(record, subPath, ext, request, reply);\n }\n\n // 1. Always validate against the share store before serving. A cached\n // token may have been revoked (or had its password/visibility changed) on\n // another replica, so a process-local cache hit must not bypass this check.\n this.deps.logger.info(\"[share] querying DB\", { token });\n\n // 2. Single DB lookup\n const record = await this.deps.store.findByToken(token);\n if (!record || record.revoked) {\n this.deps.logger.warn(\"[share] token not found or revoked\", { token, found: !!record, revoked: record?.revoked });\n return reply.status(404).send(\"Not found\");\n }\n if (record.expiresAt && new Date(record.expiresAt) < new Date()) {\n this.deps.logger.info(\"[share] token expired\", { token, expiresAt: record.expiresAt });\n return reply.status(410).send(\"Expired\");\n }\n if (record.kind === \"mcpApp\") {\n return this._serveMcpAppShell(record, reply);\n }\n\n this.deps.logger.info(\"[share] record found\", {\n token,\n volume: record.address.volume,\n resourcePath: record.address.resourcePath,\n visibility: record.visibility,\n workspaceId: record.workspaceId,\n projectId: record.projectId,\n });\n\n // 3. Auth\n const isInternal = this._isInternalRequest(request);\n if (!isInternal) {\n // Internal shares: must be logged in\n if (record.visibility === \"internal\") {\n return reply.status(401).send({ error: \"Internal access only\" });\n }\n if (record.visibility === \"password\") {\n const unlocked = this._isUnlocked(request, token);\n if (!unlocked) {\n this.deps.logger.info(\"[share] password protected, returning password page\", { token });\n return reply.type(\"text/html\").send(this._passwordPage(token));\n }\n this.deps.logger.info(\"[share] password unlocked\", { token });\n }\n }\n\n // 4. Access count\n if (record.maxAccess !== null) {\n const ok = await this.deps.store.atomicIncrementAccess(token);\n if (!ok) return reply.status(410).send(\"Access limit reached\");\n } else {\n this.deps.store.incrementAccess(token).catch(() => {});\n }\n\n // 5. Serve (no process-local cache: the store lookup above is authoritative)\n const sandboxConfig = { tenantId: record.tenantId, workspaceId: record.workspaceId, projectId: record.projectId, assistantId: record.assistantId };\n\n return this._serveFile(record.address, token, subPath, sandboxConfig, reply, request.headers.range);\n }\n\n /**\n * Thin redirect: /f/{uuidHex3}/{path} → /s/{internal-token}/{path}\n * Decodes 3 UUIDs (no dashes, 96 hex chars), ensures internal share, 302 redirects.\n */\n async resolveDirectFile(request: FastifyRequest, reply: FastifyReply) {\n const params = request.params as unknown as { tid: string; wid: string; pid: string; \"*\"?: string };\n const tenantId = params.tid;\n const workspaceId = params.wid;\n const projectId = params.pid;\n const subPath = (params[\"*\"] ?? \"\");\n\n const isApi = subPath && /(^|\\/)api\\//.test(subPath) && (subPath.endsWith(\".js\") || subPath.endsWith(\".py\"));\n\n // Only API paths accept non-GET methods\n if (request.method !== \"GET\" && !isApi) {\n return reply.status(405).send({ error: \"Method not allowed\" });\n }\n\n // API runtime\n if (isApi) {\n const ext = subPath.endsWith(\".py\") ? \"py\" : \"js\";\n return this._execApiCore({\n tenantId, workspaceId, projectId, assistantId: \"\",\n resourcePath: subPath,\n }, subPath, ext, request, reply);\n }\n\n const volume = buildNamedVolumeName(\"p\", \"project\", tenantId, workspaceId, projectId);\n return this._resolveAndServe(volume, subPath, reply, `/f/${tenantId}/${workspaceId}/${projectId}`,\n { tenantId, workspaceId, projectId, assistantId: null },\n request.headers.range,\n );\n }\n\n async unlockShare(request: FastifyRequest, reply: FastifyReply) {\n const { token } = request.params as unknown as { token: string };\n const { password } = request.body as unknown as { password: string };\n\n const record = await this.deps.store.findByToken(token);\n if (!record || !record.passwordHash) {\n return reply.status(404).send({ error: \"Invalid request\" });\n }\n\n const valid = await bcrypt.compare(password, record.passwordHash!);\n if (!valid) {\n return reply.status(401).send({ error: \"Incorrect password\" });\n }\n\n const cookieOpts = [\n `share_unlock_${token}=1`,\n \"Max-Age=86400\",\n `Path=/s/${token}`,\n \"SameSite=Lax\",\n \"HttpOnly\",\n ];\n if (request.protocol === \"https\") {\n cookieOpts.push(\"Secure\");\n }\n reply.header(\"Set-Cookie\", cookieOpts.join(\"; \"));\n return reply.send({ ok: true });\n }\n\n private _isInternalRequest(request: FastifyRequest): boolean {\n const user = (request as any).user;\n if (user && user.id) return true;\n // Fallback: same-origin browser request (iframe/img embed — no Auth header)\n const origin = request.headers.origin || request.headers.referer;\n if (origin) {\n try {\n const { hostname } = new URL(origin);\n if (hostname === request.hostname) return true;\n } catch { /* malformed */ }\n }\n return false;\n }\n\n private _isUnlocked(request: FastifyRequest, token: string): boolean {\n const cookie = request.headers.cookie ?? \"\";\n return cookie.includes(`share_unlock_${token}=1`);\n }\n\n /** Returns true if the path has a file extension indicating it's a file, not a directory */\n private _isFilePath(path: string): boolean {\n return /\\.[a-zA-Z0-9]+$/.test(path);\n }\n\n /**\n * Injects a `<base href>` tag into an HTML document when it does not already\n * declare one. Injects right after `<head>` when present, otherwise prefixes\n * the document.\n */\n private _injectBaseTag(html: string, baseHref: string): string {\n if (/<base\\b/i.test(html)) return html;\n const baseTag = `<base href=\"${baseHref}\">`;\n return html.includes(\"<head>\") ? html.replace(\"<head>\", `<head>${baseTag}`) : baseTag + html;\n }\n\n /**\n * Shared file resolution: directory → index.html, volume FS → sandbox, base tag, response.\n */\n private async _resolveAndServe(\n volume: string,\n resourcePath: string,\n reply: FastifyReply,\n baseTagPrefix: string,\n sandboxConfig?: { tenantId: string; workspaceId: string; projectId: string; assistantId: string | null },\n rangeHeader?: string,\n ) {\n const cleanPath = resourcePath.replace(/\\/+$/, \"\");\n const entryPath = !cleanPath\n ? \"index.html\"\n : this._isFilePath(cleanPath)\n ? cleanPath\n : `${cleanPath}/index.html`;\n\n const address: ResourceAddress = { volume, resourcePath: \"\" };\n\n let buf: Buffer;\n try {\n // Volume FS first\n const provider = this.deps.sandboxManager.getDefaultProvider();\n buf = await provider.getResourceResolver().resolve({ ...address, resourcePath: entryPath });\n } catch {\n // Sandbox fallback\n if (!sandboxConfig) return reply.status(404).send(\"File not found\");\n try {\n const sandbox = await this.deps.sandboxManager.getSandboxFromConfig({\n assistant_id: sandboxConfig.assistantId ?? \"\",\n thread_id: \"\",\n tenantId: sandboxConfig.tenantId,\n workspaceId: sandboxConfig.workspaceId,\n projectId: sandboxConfig.projectId,\n });\n buf = await sandbox.file.downloadFile({ file: `/project/${entryPath}` });\n } catch {\n return reply.status(404).send(\"File not found\");\n }\n }\n\n const filename = entryPath.split(\"/\").pop() || \"download\";\n const contentType = getContentTypeFromFilename(filename);\n\n // Inject base tag for HTML\n if (/\\.(html|htm)$/i.test(filename)) {\n const baseDir = cleanPath\n ? (this._isFilePath(cleanPath) ? cleanPath.replace(/[^/]+$/, \"\") : cleanPath)\n : \"\";\n const baseHref = `${baseTagPrefix}${baseDir ? \"/\" + baseDir : \"\"}/`;\n buf = Buffer.from(\n this._injectBaseTag(buf.toString(\"utf-8\"), baseHref),\n \"utf-8\"\n );\n }\n\n return sendBufferWithRange(reply, buf, contentType, rangeHeader, {\n \"Content-Disposition\": `inline; filename*=UTF-8''${encodeURIComponent(filename)}`,\n \"Access-Control-Allow-Origin\": \"*\",\n });\n }\n\n private async _serveMcpAppShell(\n _record: McpAppShareRecord,\n reply: FastifyReply,\n ) {\n const html = [\n \"<!doctype html>\",\n '<html lang=\"en\">',\n \" <head>\",\n ' <meta charset=\"utf-8\" />',\n ' <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n \" <title>Shared app</title>\",\n \" </head>\",\n \" <body>\",\n ' <div id=\"axiom-mcp-app-root\">Loading…</div>',\n ' <script type=\"module\" src=\"/embed/mcp-app-page.js\"></script>',\n \" </body>\",\n \"</html>\",\n ].join(\"\\n\");\n return reply\n .header(\"cache-control\", \"no-store\")\n .header(\"referrer-policy\", \"no-referrer\")\n .header(\"x-content-type-options\", \"nosniff\")\n .header(\n \"content-security-policy\",\n \"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; frame-src 'self'; frame-ancestors 'none'; base-uri 'none'\",\n )\n .type(\"text/html; charset=utf-8\")\n .send(html);\n }\n\n private async _serveFile(\n address: ResourceAddress,\n token: string,\n subPath: string,\n sandboxConfig: { tenantId: string; workspaceId: string; projectId: string; assistantId: string | null },\n reply: FastifyReply,\n rangeHeader: string | undefined,\n ) {\n const resourcePath = subPath\n ? this._resolveSafeSubPath(address.resourcePath, subPath)\n : address.resourcePath;\n return this._resolveAndServe(\n address.volume,\n resourcePath,\n reply,\n `/s/${token}`,\n sandboxConfig,\n rangeHeader,\n );\n }\n\n private async _execApi(\n record: FileShareRecord,\n apiSubPath: string,\n ext: \"js\" | \"py\",\n request: FastifyRequest,\n reply: FastifyReply,\n ) {\n const basePath = this._isFilePath(record.address.resourcePath)\n ? \"\"\n : `${record.address.resourcePath}/`;\n return this._execApiCore({\n tenantId: record.tenantId,\n workspaceId: record.workspaceId,\n projectId: record.projectId,\n assistantId: record.assistantId,\n resourcePath: basePath + apiSubPath,\n openGrants: record.openGrants,\n }, apiSubPath, ext, request, reply);\n }\n\n private async _execApiCore(\n config: {\n tenantId: string;\n workspaceId: string;\n projectId: string;\n assistantId?: string | null;\n resourcePath: string;\n openGrants?: import(\"@axiom-lattice/protocols\").OpenGrant[];\n },\n apiSubPath: string,\n ext: \"js\" | \"py\",\n request: FastifyRequest,\n reply: FastifyReply,\n ) {\n let sandbox: any;\n try {\n sandbox = await this.deps.sandboxManager.getSandboxFromConfig({\n assistant_id: config.assistantId ?? \"\",\n thread_id: \"\",\n tenantId: config.tenantId,\n workspaceId: config.workspaceId,\n projectId: config.projectId,\n });\n } catch (err) {\n this.deps.logger.error(\"[share] sandbox start failed for API\", { error: (err as Error).message });\n return reply.status(502).send({ error: \"Sandbox unavailable\" });\n }\n\n const scriptPath = `/project/${config.resourcePath}`;\n const cmd = ext === \"py\" ? `python3 ${scriptPath}` : `node ${scriptPath}`;\n\n // Pass query params and body via environment\n const queryStr = request.url.includes(\"?\") ? (request.url as string).split(\"?\")[1] : \"\";\n const queryObj: Record<string, string> = {};\n if (queryStr) {\n for (const [k, v] of new URLSearchParams(queryStr)) {\n queryObj[k] = v;\n }\n }\n\n const bodyStr = (request.body as string) ?? \"\";\n\n const envVars = [\n `export API_QUERY='${JSON.stringify(queryObj).replace(/'/g, \"'\\\\''\")}'`,\n `export API_BODY='${bodyStr.replace(/'/g, \"'\\\\''\")}'`,\n `export API_METHOD='${request.method}'`,\n `export API_GATEWAY_URL='${request.protocol}://${request.headers[\"x-forwarded-host\"] ?? request.headers.host ?? request.hostname}'`,\n `export API_TENANT_ID='${config.tenantId}'`,\n `export API_ASSISTANT_ID='${config.assistantId || \"\"}'`,\n `export OPEN_API_TOKEN='${this._mintSandboxToken(sandbox.name, config)}'`,\n ].join(\" && \");\n\n const fullCmd = `${envVars} && ${cmd}`;\n\n this.deps.logger.info(\"[api] exec\", { scriptPath, cmd: cmd.substring(0, 80) });\n\n try {\n const result = await sandbox.shell.execCommand({ command: fullCmd });\n // Parse stdout as JSON\n let data: unknown;\n try {\n data = JSON.parse(result.output?.trim() || \"{}\");\n } catch {\n data = { output: result.output?.trim() || \"\" };\n }\n return reply.status(200).type(\"application/json\").send(data);\n } catch (err) {\n this.deps.logger.warn(\"[api] exec failed\", { scriptPath, error: (err as Error).message });\n return reply.status(500).send({ error: (err as Error).message });\n }\n }\n\n private _resolveSafeSubPath(entryFile: string, subPath: string): string {\n if (!subPath) return entryFile;\n if (subPath.includes(\"..\")) throw new Error(\"Path traversal denied\");\n if (subPath.startsWith(entryFile + \"/\") || subPath === entryFile) return subPath;\n const entryDir = this._isFilePath(entryFile)\n ? entryFile.replace(/[^/]+$/, \"\")\n : entryFile + \"/\";\n return (entryDir + subPath).replace(/\\/+/g, \"/\");\n }\n\n private _passwordPage(token: string): string {\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Password Protected</title><style>body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f5f5f5}.card{background:#fff;padding:32px;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.1);width:100%;max-width:360px;text-align:center}h2{margin:0 0 8px;font-size:20px}p{margin:0 0 20px;color:#666;font-size:14px}input{width:100%;padding:10px 12px;border:1px solid #d9d9d9;border-radius:8px;font-size:14px;box-sizing:border-box;margin-bottom:12px}button{width:100%;padding:10px;background:#6366f1;color:#fff;border:none;border-radius:8px;font-size:14px;cursor:pointer}.error{color:#ef4444;font-size:13px;margin-top:8px;display:none}</style></head><body><div class=\"card\"><h2>Password Protected</h2><p>This share requires a password to access.</p><form id=\"f\"><input type=\"password\" id=\"p\" placeholder=\"Enter password\" required><button type=\"submit\">Unlock</button><div class=\"error\" id=\"e\">Incorrect password</div></form></div><script>document.getElementById('f').onsubmit=async(e)=>{e.preventDefault();const r=await fetch('/s/${token}/unlock',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({password:document.getElementById('p').value})});if(r.ok)location.reload();else document.getElementById('e').style.display='block'}</script></body></html>`;\n }\n\n private _mintSandboxToken(\n sandboxName: string,\n config: {\n tenantId: string;\n workspaceId: string;\n projectId: string;\n openGrants?: import(\"@axiom-lattice/protocols\").OpenGrant[];\n },\n ): string {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { signOpenToken } = require(\"../services/open/openToken\") as {\n signOpenToken: (p: unknown) => string;\n };\n return signOpenToken({\n kind: \"sandbox_token\",\n tenantId: config.tenantId,\n workspaceId: config.workspaceId,\n projectId: config.projectId,\n sandboxName,\n grants: [\n {\n domain: \"sandbox\",\n items: [\"files_read\", \"files_list\", \"files_write\", \"shell_exec\"],\n },\n ...sanitizeShareOpenGrants(config.openGrants),\n ],\n });\n }\n}\n","/**\n * Sandbox-token grant sanitation.\n *\n * Sandbox tokens always carry the self-binding sandbox grant (resolved from\n * the credential's tenant/workspace/project). Shares may append extra\n * Open-door grants, but only within the V1 whitelist: read-only kb access.\n * Anything else is dropped server-side (the share creator cannot widen scope).\n */\n\nimport type { OpenGrant } from \"@axiom-lattice/protocols\";\n\n/** V1 allowlist: domain → permitted (read-only) actions. */\nconst ALLOWED: Record<string, string[]> = {\n kb: [\"search\", \"entries_read\"],\n};\n\nexport function sanitizeShareOpenGrants(grants: OpenGrant[] | undefined): OpenGrant[] {\n const out: OpenGrant[] = [];\n for (const grant of grants ?? []) {\n const allowed = ALLOWED[grant.domain];\n if (!allowed) continue;\n const items =\n grant.items && grant.items.length > 0\n ? grant.items.filter((item) => allowed.includes(item))\n : allowed;\n if (items.length > 0) out.push({ domain: grant.domain, items });\n }\n return out;\n}\n"],"mappings":"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","names":["record"]}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createDefaultAgentReflectionDeps,
|
|
3
|
-
createDefaultPluginReflectionDeps,
|
|
4
3
|
mergeGrantsByDomain,
|
|
5
|
-
resolveAgentOpenGrants
|
|
6
|
-
|
|
7
|
-
} from "./chunk-GL7RV5L3.mjs";
|
|
4
|
+
resolveAgentOpenGrants
|
|
5
|
+
} from "./chunk-YR32MWEJ.mjs";
|
|
8
6
|
import {
|
|
9
7
|
init_openToken,
|
|
10
8
|
signOpenToken
|
|
11
9
|
} from "./chunk-EXPERMZR.mjs";
|
|
10
|
+
import "./chunk-MFWTGNQR.mjs";
|
|
12
11
|
import "./chunk-NFEGQTCC.mjs";
|
|
13
12
|
|
|
14
13
|
// src/services/open/sandboxOpenCredentialProvider.ts
|
|
15
14
|
init_openToken();
|
|
16
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
registerSandboxOpenCredentialProvider,
|
|
17
|
+
registerSandboxOpenGatewayUrlProvider
|
|
18
|
+
} from "@axiom-lattice/core";
|
|
17
19
|
var DEFAULT_SANDBOX_OPEN_GRANTS = [
|
|
18
20
|
{ domain: "sandbox", items: ["files_read", "files_list", "files_write", "shell_exec"] },
|
|
19
21
|
{ domain: "kb", items: ["search", "entries_read"] }
|
|
@@ -21,20 +23,20 @@ var DEFAULT_SANDBOX_OPEN_GRANTS = [
|
|
|
21
23
|
var MAX_SANDBOX_OPEN_DEPTH = 5;
|
|
22
24
|
function defaultReflection() {
|
|
23
25
|
return {
|
|
24
|
-
agent: (tenantId, assistantId) => resolveAgentOpenGrants(tenantId, assistantId, createDefaultAgentReflectionDeps(tenantId))
|
|
25
|
-
plugin: (tenantId) => resolvePluginOpenGrants(tenantId, createDefaultPluginReflectionDeps())
|
|
26
|
+
agent: (tenantId, assistantId) => resolveAgentOpenGrants(tenantId, assistantId, createDefaultAgentReflectionDeps(tenantId))
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
function createSandboxOpenCredentialProvider(options) {
|
|
29
30
|
const reflection = options.reflection ?? defaultReflection();
|
|
30
31
|
return async (context) => {
|
|
31
32
|
if (!options.gatewayUrl) return void 0;
|
|
33
|
+
if (!context.assistantId) return void 0;
|
|
32
34
|
const depth = (context.depth ?? 0) + 1;
|
|
33
35
|
if (depth > MAX_SANDBOX_OPEN_DEPTH) {
|
|
34
36
|
options.onError?.(new Error("sandbox open credential depth exceeded"));
|
|
35
37
|
return void 0;
|
|
36
38
|
}
|
|
37
|
-
const reflected =
|
|
39
|
+
const reflected = await reflection.agent(context.tenantId, context.assistantId);
|
|
38
40
|
const grants = mergeGrantsByDomain([...DEFAULT_SANDBOX_OPEN_GRANTS, ...reflected]);
|
|
39
41
|
const token = signOpenToken({
|
|
40
42
|
kind: "sandbox_token",
|
|
@@ -49,13 +51,16 @@ function createSandboxOpenCredentialProvider(options) {
|
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
function registerGatewaySandboxOpenCredentialProvider(onError) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
onError
|
|
56
|
-
}),
|
|
54
|
+
const gatewayUrl = process.env.PUBLIC_GATEWAY_URL?.trim() || void 0;
|
|
55
|
+
const unregisterCredential = registerSandboxOpenCredentialProvider(
|
|
56
|
+
createSandboxOpenCredentialProvider({ gatewayUrl, onError }),
|
|
57
57
|
{ onError }
|
|
58
58
|
);
|
|
59
|
+
const unregisterGatewayUrl = registerSandboxOpenGatewayUrlProvider(() => gatewayUrl);
|
|
60
|
+
return () => {
|
|
61
|
+
unregisterGatewayUrl();
|
|
62
|
+
unregisterCredential();
|
|
63
|
+
};
|
|
59
64
|
}
|
|
60
65
|
export {
|
|
61
66
|
DEFAULT_SANDBOX_OPEN_GRANTS,
|
|
@@ -63,4 +68,4 @@ export {
|
|
|
63
68
|
createSandboxOpenCredentialProvider,
|
|
64
69
|
registerGatewaySandboxOpenCredentialProvider
|
|
65
70
|
};
|
|
66
|
-
//# sourceMappingURL=sandboxOpenCredentialProvider-
|
|
71
|
+
//# sourceMappingURL=sandboxOpenCredentialProvider-RTYOYRCT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/open/sandboxOpenCredentialProvider.ts"],"sourcesContent":["/**\n * Gateway composition-root implementation of the sandbox open-credential seam.\n *\n * Mints a short-lived, self-bound `sandbox_token` for agent `code_eval` so its\n * sandbox code can call the Open MCP endpoint. Plugin handlers do not use this\n * seam: they read the platform API key from their connection config directly and\n * only need the gateway URL, which is exposed through a separate URL seam.\n */\n\nimport type { OpenGrant } from \"@axiom-lattice/protocols\";\nimport type { SandboxOpenCredentialProvider } from \"@axiom-lattice/core\";\nimport {\n registerSandboxOpenCredentialProvider,\n registerSandboxOpenGatewayUrlProvider,\n} from \"@axiom-lattice/core\";\nimport { signOpenToken } from \"./openToken\";\nimport {\n createDefaultAgentReflectionDeps,\n mergeGrantsByDomain,\n resolveAgentOpenGrants,\n} from \"./sandboxOpenReflection\";\n\n/** Conservative default grants for sandbox-initiated Open calls (B0). */\nexport const DEFAULT_SANDBOX_OPEN_GRANTS: OpenGrant[] = [\n { domain: \"sandbox\", items: [\"files_read\", \"files_list\", \"files_write\", \"shell_exec\"] },\n { domain: \"kb\", items: [\"search\", \"entries_read\"] },\n];\n\n/** Maximum nested sandbox Open orchestration depth before minting is refused. */\nexport const MAX_SANDBOX_OPEN_DEPTH = 5;\n\n/** Reflection seam: how the provider derives caller grants for an agent. */\nexport interface SandboxOpenReflection {\n /** Reflect an agent caller's own Open capabilities. */\n agent: (tenantId: string, assistantId: string) => Promise<OpenGrant[]>;\n}\n\n/** Options for {@link createSandboxOpenCredentialProvider}. */\nexport interface SandboxOpenCredentialProviderFactoryOptions {\n /** Public base URL of this gateway (`PUBLIC_GATEWAY_URL`). Absent ⇒ inject nothing. */\n gatewayUrl?: string;\n /** Best-effort hook for structured warnings (e.g. depth cap reached). */\n onError?: (error: unknown) => void;\n /** Test seam replacing the live agent reflection resolver. */\n reflection?: SandboxOpenReflection;\n}\n\n/** Default reflection wired to the live tenant registries. */\nfunction defaultReflection(): SandboxOpenReflection {\n return {\n agent: (tenantId, assistantId) =>\n resolveAgentOpenGrants(tenantId, assistantId, createDefaultAgentReflectionDeps(tenantId)),\n };\n}\n\n/**\n * Build the provider.\n *\n * Only agent callers are served: the grants come from the caller agent's own\n * configured middleware. Plugin handlers never use this provider — they carry\n * the platform API key in their connection config.\n *\n * Each mint increments the orchestration `depth`; past\n * {@link MAX_SANDBOX_OPEN_DEPTH} the provider refuses to mint, breaking cycles.\n *\n * @param options - Gateway URL, error hook, and optional reflection seam\n * @returns A {@link SandboxOpenCredentialProvider}\n */\nexport function createSandboxOpenCredentialProvider(\n options: SandboxOpenCredentialProviderFactoryOptions,\n): SandboxOpenCredentialProvider {\n const reflection = options.reflection ?? defaultReflection();\n return async (context) => {\n if (!options.gatewayUrl) return undefined;\n if (!context.assistantId) return undefined;\n const depth = (context.depth ?? 0) + 1;\n if (depth > MAX_SANDBOX_OPEN_DEPTH) {\n options.onError?.(new Error(\"sandbox open credential depth exceeded\"));\n return undefined;\n }\n const reflected = await reflection.agent(context.tenantId, context.assistantId);\n const grants = mergeGrantsByDomain([...DEFAULT_SANDBOX_OPEN_GRANTS, ...reflected]);\n const token = signOpenToken({\n kind: \"sandbox_token\",\n tenantId: context.tenantId,\n workspaceId: context.workspaceId ?? \"\",\n projectId: context.projectId ?? \"\",\n sandboxName: context.sandboxName,\n grants,\n depth,\n });\n return { gatewayUrl: options.gatewayUrl, token };\n };\n}\n\n/**\n * Register the gateway credential provider and the plugin gateway-URL seam.\n *\n * @param onError - Optional hook for structured warnings when resolution fails\n * @returns An unregister function (for tests/shutdown)\n */\nexport function registerGatewaySandboxOpenCredentialProvider(\n onError?: (error: unknown) => void,\n): () => void {\n const gatewayUrl = process.env.PUBLIC_GATEWAY_URL?.trim() || undefined;\n const unregisterCredential = registerSandboxOpenCredentialProvider(\n createSandboxOpenCredentialProvider({ gatewayUrl, onError }),\n { onError },\n );\n const unregisterGatewayUrl = registerSandboxOpenGatewayUrlProvider(() => gatewayUrl);\n return () => {\n unregisterGatewayUrl();\n unregisterCredential();\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAeA;AAJA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AASA,IAAM,8BAA2C;AAAA,EACtD,EAAE,QAAQ,WAAW,OAAO,CAAC,cAAc,cAAc,eAAe,YAAY,EAAE;AAAA,EACtF,EAAE,QAAQ,MAAM,OAAO,CAAC,UAAU,cAAc,EAAE;AACpD;AAGO,IAAM,yBAAyB;AAmBtC,SAAS,oBAA2C;AAClD,SAAO;AAAA,IACL,OAAO,CAAC,UAAU,gBAChB,uBAAuB,UAAU,aAAa,iCAAiC,QAAQ,CAAC;AAAA,EAC5F;AACF;AAeO,SAAS,oCACd,SAC+B;AAC/B,QAAM,aAAa,QAAQ,cAAc,kBAAkB;AAC3D,SAAO,OAAO,YAAY;AACxB,QAAI,CAAC,QAAQ,WAAY,QAAO;AAChC,QAAI,CAAC,QAAQ,YAAa,QAAO;AACjC,UAAM,SAAS,QAAQ,SAAS,KAAK;AACrC,QAAI,QAAQ,wBAAwB;AAClC,cAAQ,UAAU,IAAI,MAAM,wCAAwC,CAAC;AACrE,aAAO;AAAA,IACT;AACA,UAAM,YAAY,MAAM,WAAW,MAAM,QAAQ,UAAU,QAAQ,WAAW;AAC9E,UAAM,SAAS,oBAAoB,CAAC,GAAG,6BAA6B,GAAG,SAAS,CAAC;AACjF,UAAM,QAAQ,cAAc;AAAA,MAC1B,MAAM;AAAA,MACN,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ,eAAe;AAAA,MACpC,WAAW,QAAQ,aAAa;AAAA,MAChC,aAAa,QAAQ;AAAA,MACrB;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,EAAE,YAAY,QAAQ,YAAY,MAAM;AAAA,EACjD;AACF;AAQO,SAAS,6CACd,SACY;AACZ,QAAM,aAAa,QAAQ,IAAI,oBAAoB,KAAK,KAAK;AAC7D,QAAM,uBAAuB;AAAA,IAC3B,oCAAoC,EAAE,YAAY,QAAQ,CAAC;AAAA,IAC3D,EAAE,QAAQ;AAAA,EACZ;AACA,QAAM,uBAAuB,sCAAsC,MAAM,UAAU;AACnF,SAAO,MAAM;AACX,yBAAqB;AACrB,yBAAqB;AAAA,EACvB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/gateway",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
"uuid": "^9.0.1",
|
|
56
56
|
"web-push": "^3.6.7",
|
|
57
57
|
"zod": "3.25.76",
|
|
58
|
-
"@axiom-lattice/cli-a2a": "0.2.
|
|
59
|
-
"@axiom-lattice/core": "10.0
|
|
60
|
-
"@axiom-lattice/pg-stores": "4.0
|
|
61
|
-
"@axiom-lattice/protocols": "6.
|
|
62
|
-
"@axiom-lattice/queue-redis": "1.1.
|
|
58
|
+
"@axiom-lattice/cli-a2a": "0.2.28",
|
|
59
|
+
"@axiom-lattice/core": "10.1.0",
|
|
60
|
+
"@axiom-lattice/pg-stores": "4.1.0",
|
|
61
|
+
"@axiom-lattice/protocols": "6.3.0",
|
|
62
|
+
"@axiom-lattice/queue-redis": "1.1.28"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/bcrypt": "^6.0.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"typescript": "^5.8.2",
|
|
85
85
|
"ws": "^8.19.0",
|
|
86
86
|
"@axiom-lattice/open-mcp-client": "0.2.0",
|
|
87
|
-
"@axiom-lattice/react-sdk": "8.
|
|
87
|
+
"@axiom-lattice/react-sdk": "8.4.0"
|
|
88
88
|
},
|
|
89
89
|
"scripts": {
|
|
90
90
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean --sourcemap && pnpm copy:embed-assets",
|
|
@@ -101,6 +101,8 @@
|
|
|
101
101
|
"test:e2e:open-mcp-sandbox-token": "tsx scripts/open-mcp-sandbox-token-e2e.ts",
|
|
102
102
|
"test:e2e:sandbox-open-credential": "tsx scripts/sandbox-open-credential-e2e.ts",
|
|
103
103
|
"test:e2e:sandbox-open-orchestration": "tsx scripts/sandbox-open-orchestration-e2e.ts",
|
|
104
|
-
"test:e2e:sandbox-plugin-loop": "tsx scripts/sandbox-plugin-loop-e2e.ts"
|
|
104
|
+
"test:e2e:sandbox-plugin-loop": "tsx scripts/sandbox-plugin-loop-e2e.ts",
|
|
105
|
+
"test:e2e:sandbox-plugin-ptc": "tsx scripts/sandbox-plugin-ptc-e2e.ts",
|
|
106
|
+
"test:e2e:mcp-app-share": "tsx scripts/mcp-app-share-e2e.ts"
|
|
105
107
|
}
|
|
106
108
|
}
|