@axiom-lattice/gateway 8.2.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.
Files changed (111) hide show
  1. package/dist/{A2AAuthService-7UGQ7GMW.mjs → A2AAuthService-UKN53ZGP.mjs} +2 -2
  2. package/dist/{a2a-BHBLACJV.mjs → a2a-TDZ3TT6H.mjs} +3 -3
  3. package/dist/{a2a-standard-VL5I7B4W.mjs → a2a-standard-INVCUSAW.mjs} +39 -2
  4. package/dist/a2a-standard-INVCUSAW.mjs.map +1 -0
  5. package/dist/chunk-A7YNAU5L.mjs +377 -0
  6. package/dist/chunk-A7YNAU5L.mjs.map +1 -0
  7. package/dist/{chunk-3XIIQ6EK.mjs → chunk-BOPNXTMO.mjs} +66 -5
  8. package/dist/chunk-BOPNXTMO.mjs.map +1 -0
  9. package/dist/{chunk-L6MBEEII.mjs → chunk-JGQURU44.mjs} +24 -6
  10. package/dist/chunk-JGQURU44.mjs.map +1 -0
  11. package/dist/{chunk-GPHOYR3X.mjs → chunk-LCM4OQTI.mjs} +30 -1
  12. package/dist/chunk-LCM4OQTI.mjs.map +1 -0
  13. package/dist/{chunk-HVIKCUOF.mjs → chunk-MFWTGNQR.mjs} +74 -5
  14. package/dist/chunk-MFWTGNQR.mjs.map +1 -0
  15. package/dist/{chunk-HYYLYIMH.mjs → chunk-OBODBHIS.mjs} +2 -2
  16. package/dist/chunk-T2HIWZGP.mjs +580 -0
  17. package/dist/chunk-T2HIWZGP.mjs.map +1 -0
  18. package/dist/chunk-Y7OVVPVU.mjs +13 -0
  19. package/dist/chunk-Y7OVVPVU.mjs.map +1 -0
  20. package/dist/{chunk-GL7RV5L3.mjs → chunk-YR32MWEJ.mjs} +36 -73
  21. package/dist/chunk-YR32MWEJ.mjs.map +1 -0
  22. package/dist/embed/{embed-page-AgentDetailSideApp-O6G4NJ7I.css → embed-page-AgentDetailSideApp-JMJOOC4V.css} +1 -1
  23. package/dist/embed/{embed-page-AgentDetailSideApp-WDDE7BEH.js → embed-page-AgentDetailSideApp-TQEJK6ZF.js} +1 -1
  24. package/dist/embed/{embed-page-AgentGovernanceWorkbench-SFZNVMUH.css → embed-page-AgentGovernanceWorkbench-3T3ZFJI5.css} +1 -1
  25. package/dist/embed/{embed-page-AgentGovernanceWorkbench-7YPU3QB2.js → embed-page-AgentGovernanceWorkbench-II2MRGCG.js} +1 -1
  26. package/dist/embed/{embed-page-LangfuseDashboard-BEOJDMWF.js → embed-page-LangfuseDashboard-7OANGQXY.js} +1 -1
  27. package/dist/embed/embed-page-MessageGraph-KJPQWTHE.js +6 -0
  28. package/dist/embed/{embed-page-TopologyRuntimeView-CWOXLVFW.css → embed-page-MessageGraph-UDC6GQA5.css} +1 -1
  29. package/dist/embed/{embed-page-ReactChart-D7RZQB2H.js → embed-page-ReactChart-2CAIM4JD.js} +1 -1
  30. package/dist/embed/{embed-page-ReactChart-MXGF35OQ.css → embed-page-ReactChart-URBKYQYE.css} +1 -1
  31. package/dist/embed/{embed-page-SkillFlow-3X24ILKU.js → embed-page-SkillFlow-243MXRXC.js} +1 -1
  32. package/dist/embed/{embed-page-TasksSideApp-KD4S22SL.js → embed-page-TasksSideApp-OA7CULHS.js} +1 -1
  33. package/dist/embed/{embed-page-TasksSideApp-HV6DKQ26.css → embed-page-TasksSideApp-TTCP4ENP.css} +1 -1
  34. package/dist/embed/{embed-page-ThreadMaintenancePanel-V327PV5R.js → embed-page-ThreadMaintenancePanel-Z4424X3J.js} +1 -1
  35. package/dist/embed/{embed-page-MessageGraph-UT2TCPSO.css → embed-page-TopologyRuntimeView-S4AMB4RI.css} +1 -1
  36. package/dist/embed/{embed-page-TopologyRuntimeView-Z7MY6U5T.js → embed-page-TopologyRuntimeView-THTCZUS7.js} +1 -1
  37. package/dist/embed/{embed-page-WorkflowAutomationView-YQFB3CTV.css → embed-page-WorkflowAutomationView-FWVCQ3YM.css} +1 -1
  38. package/dist/embed/{embed-page-WorkflowAutomationView-4MY4HG7R.js → embed-page-WorkflowAutomationView-YDIDG35E.js} +1 -1
  39. package/dist/embed/{embed-page-chunk-4XKCMM5C.js → embed-page-chunk-7ULTMRM2.js} +1 -1
  40. package/dist/embed/{embed-page-chunk-GSUSX54K.js → embed-page-chunk-FE6KYRCO.js} +1 -1
  41. package/dist/embed/{embed-page-chunk-BSRASK7V.js → embed-page-chunk-FZX4A32D.js} +5 -5
  42. package/dist/embed/{embed-page-chunk-PMOGSY6W.js → embed-page-chunk-JMP7EQEF.js} +1 -1
  43. package/dist/embed/{embed-page-chunk-EM6VGUZ2.js → embed-page-chunk-L2EGVVJS.js} +1 -1
  44. package/dist/embed/{embed-page-chunk-HZZ5HNTF.js → embed-page-chunk-LDQGH44H.js} +2 -2
  45. package/dist/embed/{embed-page-chunk-BZKAPY7M.js → embed-page-chunk-OMN6G52V.js} +1 -1
  46. package/dist/embed/{embed-page-chunk-G5MXFDNZ.js → embed-page-chunk-RKQJBKN2.js} +1 -1
  47. package/dist/embed/{embed-page-chunk-G5PDHDBZ.js → embed-page-chunk-VYS3TAK4.js} +1 -1
  48. package/dist/embed/{embed-page-chunk-ASEIOK7X.js → embed-page-chunk-W5OP2CZG.js} +2 -2
  49. package/dist/embed/embed-page-chunk-Z6KWW64I.js +6837 -0
  50. package/dist/embed/{embed-page-en-E35GE7TA.js → embed-page-en-D4M3DPFT.js} +1 -1
  51. package/dist/embed/{embed-page-en-XJYWUJEL.js → embed-page-en-DNDGCPMA.js} +1 -1
  52. package/dist/embed/embed-page-en-H5NOZEKE.js +1 -0
  53. package/dist/embed/{embed-page-en-HDSVQ4O6.js → embed-page-en-QJRX2NQE.js} +1 -1
  54. package/dist/embed/{embed-page-zh-6EBUEFKP.js → embed-page-zh-2H4EB3F3.js} +1 -1
  55. package/dist/embed/embed-page-zh-4C2K3RH2.js +1 -0
  56. package/dist/embed/{embed-page-zh-IR652A6J.js → embed-page-zh-HTEHFCSL.js} +1 -1
  57. package/dist/embed/{embed-page-zh-ZLK3GLM3.js → embed-page-zh-NEXLMDHF.js} +1 -1
  58. package/dist/embed/embed-page.css +1 -1
  59. package/dist/embed/embed-page.js +3 -3
  60. package/dist/embed/mcp-app-page.js +61 -0
  61. package/dist/index.d.mts +29 -0
  62. package/dist/index.d.ts +29 -0
  63. package/dist/index.js +2609 -1491
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +174 -705
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/mcp-app-shares-SHPBJQXS.mjs +300 -0
  68. package/dist/mcp-app-shares-SHPBJQXS.mjs.map +1 -0
  69. package/dist/mcp-apps-QVUK6UBA.mjs +26 -0
  70. package/dist/{open-7ED27I5B.mjs → open-27ZPLBIW.mjs} +101 -18
  71. package/dist/open-27ZPLBIW.mjs.map +1 -0
  72. package/dist/{open-catalog-SZQ435AY.mjs → open-catalog-NVGP6SQP.mjs} +38 -7
  73. package/dist/open-catalog-NVGP6SQP.mjs.map +1 -0
  74. package/dist/{open-exposure-I3DN2X4E.mjs → open-exposure-BPLYUIX6.mjs} +4 -4
  75. package/dist/{open-files-V3R2BFTV.mjs → open-files-JPB2NBL6.mjs} +4 -4
  76. package/dist/{open-session-2Y7XJJW2.mjs → open-session-CLZT3HVG.mjs} +5 -5
  77. package/dist/open-session-CLZT3HVG.mjs.map +1 -0
  78. package/dist/openLattice-D5YTSZBY.mjs +28 -0
  79. package/dist/{registerExposedPlugins-6GCOE64C.mjs → registerExposedPlugins-2JGXSMAL.mjs} +4 -3
  80. package/dist/{resources-DAU5ZIPB.mjs → resources-YD5DOPGD.mjs} +83 -3
  81. package/dist/resources-YD5DOPGD.mjs.map +1 -0
  82. package/dist/{sandboxOpenCredentialProvider-AT227D4N.mjs → sandboxOpenCredentialProvider-RTYOYRCT.mjs} +19 -14
  83. package/dist/sandboxOpenCredentialProvider-RTYOYRCT.mjs.map +1 -0
  84. package/package.json +10 -8
  85. package/dist/a2a-standard-VL5I7B4W.mjs.map +0 -1
  86. package/dist/chunk-3XIIQ6EK.mjs.map +0 -1
  87. package/dist/chunk-GL7RV5L3.mjs.map +0 -1
  88. package/dist/chunk-GPHOYR3X.mjs.map +0 -1
  89. package/dist/chunk-GXZFO3VU.mjs +0 -42
  90. package/dist/chunk-GXZFO3VU.mjs.map +0 -1
  91. package/dist/chunk-HVIKCUOF.mjs.map +0 -1
  92. package/dist/chunk-L6MBEEII.mjs.map +0 -1
  93. package/dist/embed/embed-page-MessageGraph-LNNMCH6I.js +0 -6
  94. package/dist/embed/embed-page-chunk-CFFDP526.js +0 -6835
  95. package/dist/embed/embed-page-en-GLT2HEZZ.js +0 -1
  96. package/dist/embed/embed-page-zh-XMEJPDMF.js +0 -1
  97. package/dist/open-7ED27I5B.mjs.map +0 -1
  98. package/dist/open-catalog-SZQ435AY.mjs.map +0 -1
  99. package/dist/open-session-2Y7XJJW2.mjs.map +0 -1
  100. package/dist/openLattice-Q6M5S6CY.mjs +0 -18
  101. package/dist/openRateLimiter-XEOL3A5G.mjs +0 -8
  102. package/dist/resources-DAU5ZIPB.mjs.map +0 -1
  103. package/dist/sandboxOpenCredentialProvider-AT227D4N.mjs.map +0 -1
  104. /package/dist/{A2AAuthService-7UGQ7GMW.mjs.map → A2AAuthService-UKN53ZGP.mjs.map} +0 -0
  105. /package/dist/{a2a-BHBLACJV.mjs.map → a2a-TDZ3TT6H.mjs.map} +0 -0
  106. /package/dist/{chunk-HYYLYIMH.mjs.map → chunk-OBODBHIS.mjs.map} +0 -0
  107. /package/dist/{openLattice-Q6M5S6CY.mjs.map → mcp-apps-QVUK6UBA.mjs.map} +0 -0
  108. /package/dist/{open-exposure-I3DN2X4E.mjs.map → open-exposure-BPLYUIX6.mjs.map} +0 -0
  109. /package/dist/{open-files-V3R2BFTV.mjs.map → open-files-JPB2NBL6.mjs.map} +0 -0
  110. /package/dist/{openRateLimiter-XEOL3A5G.mjs.map → openLattice-D5YTSZBY.mjs.map} +0 -0
  111. /package/dist/{registerExposedPlugins-6GCOE64C.mjs.map → registerExposedPlugins-2JGXSMAL.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- import"./embed-page-chunk-QBTAJ2YI.js";var e={"mcpapp.fallbackTitle":"MCP App","mcpapp.openInSideApp":"Open in side app","mcpapp.unavailable":"MCP App unavailable","mcpapp.unavailableDescription":"MCP Apps require the gateway sandbox to be served from a different origin than the console.","mcpapp.failedToLoad":"MCP App failed to load: {{error}}","mcpapp.resourceLoadFailed":"Failed to load MCP app resource","mcpapp.toolResultAlt":"tool result","mcpapp.test.badge":"Plugin test","mcpapp.test.input":"Test input","mcpapp.test.result":"Result","mcpapp.test.data":"Data","mcpapp.test.ui":"UI"},t={messages:e};export{t as default,e as messages};
@@ -1 +0,0 @@
1
- import"./embed-page-chunk-QBTAJ2YI.js";var e={"mcpapp.fallbackTitle":"MCP App","mcpapp.openInSideApp":"\u5728\u4FA7\u680F\u4E2D\u6253\u5F00","mcpapp.unavailable":"MCP App \u4E0D\u53EF\u7528","mcpapp.unavailableDescription":"MCP App \u8981\u6C42\u7F51\u5173\u6C99\u7BB1\u4E0E\u63A7\u5236\u53F0\u7531\u4E0D\u540C\u6E90\uFF08origin\uFF09\u63D0\u4F9B\u670D\u52A1\u3002","mcpapp.failedToLoad":"MCP App \u52A0\u8F7D\u5931\u8D25\uFF1A{{error}}","mcpapp.resourceLoadFailed":"\u52A0\u8F7D MCP App \u8D44\u6E90\u5931\u8D25","mcpapp.toolResultAlt":"\u5DE5\u5177\u6267\u884C\u7ED3\u679C","mcpapp.test.badge":"\u63D2\u4EF6\u6D4B\u8BD5","mcpapp.test.input":"\u6D4B\u8BD5\u8F93\u5165","mcpapp.test.result":"\u6D4B\u8BD5\u7ED3\u679C","mcpapp.test.data":"Data","mcpapp.test.ui":"UI"},t={messages:e};export{t as default,e as messages};
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/routes/open.ts","../src/services/open/openMcpServer.ts"],"sourcesContent":["/**\n * Open API surface routes: GET/POST/DELETE /open/mcp.\n *\n * The route is a thin shell — credential resolution (OpenAuthService),\n * catalog filtering and invocation (OpenLattice), and stateless MCP server\n * construction live in services/open/*. The transport writes the response\n * itself, so the reply is hijacked (same precedent as the Project Room SSE\n * route).\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport { getReplicaId, getStoreLattice } from \"@axiom-lattice/core\";\nimport { REPLICA_HEADER, type A2AApiKeyStore, type OpenAuditStore, type OpenGrant, type ProjectStore } from \"@axiom-lattice/protocols\";\nimport { OpenCredentialService } from \"../services/open/openCredentialService\";\nimport { OpenAuditService } from \"../services/open/openAuditService\";\nimport { OpenRateLimiter } from \"../services/open/openRateLimiter\";\nimport type { OpenCredential } from \"../services/open/openCredentialService\";\nimport {\n openLattice,\n ActionNotFoundError,\n GrantDeniedError,\n ValidationError,\n} from \"../services/open/openLattice\";\nimport { buildSessionServer } from \"../services/open/openMcpServer\";\nimport { collectExposedPluginUiResources } from \"../services/open/registerExposedPlugins\";\nimport {\n buildTenantOpenPlugins,\n mergeOpenCatalog,\n type TenantOpenPlugins,\n} from \"../services/open/tenantOpenPlugins\";\n\nexport interface OpenRouteDeps {\n openAuth?: OpenCredentialService;\n grantsSource?: (ctx: OpenCredential) => OpenGrant[];\n audit?: OpenAuditService;\n rateLimiter?: OpenRateLimiter;\n /**\n * Test seam: build the credential's tenant sandbox plugin bundle. Defaults to\n * {@link buildTenantOpenPlugins} with the global catalog's tool names reserved\n * so a tenant tool can never shadow a global/built-in one.\n */\n tenantPlugins?: (\n tenantId: string,\n grants: OpenGrant[],\n reservedToolNames: ReadonlySet<string>,\n ) => Promise<TenantOpenPlugins>;\n}\n\nfunction extractBearer(request: FastifyRequest): string | undefined {\n const header = request.headers.authorization;\n if (!header?.startsWith(\"Bearer \")) return undefined;\n return header.slice(\"Bearer \".length).trim() || undefined;\n}\n\n/**\n * Degraded tenant plugin bundle used when the sandbox catalog cannot be read.\n *\n * A catalog read failure must not 500 the session: the MCP server still builds\n * and serves the global tools. `invoke` is unreachable through normal routing\n * (`hasTool` is always false, so calls route to OpenLattice) but returns an\n * error result if it is ever reached.\n */\nfunction emptyTenantPlugins(): TenantOpenPlugins {\n return {\n tools: [],\n uiResources: new Map(),\n hasTool: () => false,\n degraded: true,\n invoke: async (toolName) => ({\n content: [\n { type: \"text\", text: `Tenant plugin catalog unavailable for ${toolName}` },\n ],\n isError: true,\n }),\n };\n}\n\n/** JSON-RPC error codes used on the Open surface. */\nconst RPC_ERROR = {\n GRANT_DENIED: -32001,\n ACTION_NOT_FOUND: -32002,\n VALIDATION_FAILED: -32003,\n EXECUTION_ERROR: -32004,\n RATE_LIMITED: -32005,\n} as const;\n\nexport function registerOpenRoutes(\n app: FastifyInstance,\n deps: OpenRouteDeps = {},\n): void {\n const openAuth =\n deps.openAuth ??\n new OpenCredentialService(\n getStoreLattice(\"default\", \"a2aApiKey\").store as A2AApiKeyStore,\n getStoreLattice(\"default\", \"project\").store as ProjectStore,\n );\n const grantsSource =\n deps.grantsSource ?? ((ctx: OpenCredential) => ctx.grants);\n const audit =\n deps.audit ??\n (() => {\n try {\n const store = getStoreLattice(\"default\", \"openAudit\").store as OpenAuditStore;\n return new OpenAuditService(store);\n } catch {\n return undefined;\n }\n })();\n\n const rateLimiter = deps.rateLimiter ?? new OpenRateLimiter();\n\n const markReplica = (reply: FastifyReply): void => {\n const replica = getReplicaId();\n reply.header(REPLICA_HEADER, replica);\n reply.raw.setHeader(REPLICA_HEADER, replica);\n };\n\n const buildServerFor = async (\n context: OpenCredential,\n log: FastifyRequest[\"log\"],\n ) => {\n const grants = grantsSource(context);\n const catalog = await openLattice.catalogFor(grants, { tenantId: context.tenantId });\n // Tenant sandbox plugins are volume-resident and outside the OpenLattice\n // registry; merge their grant-filtered tools/UI alongside the globals.\n // Global/built-in tool names are reserved so a tenant plugin of the same\n // type (kb/sandbox/agent) can neither crash `registerTool` with a duplicate\n // nor capture the invoke routing for a global tool.\n const globalToolNames = new Set(catalog.map((t) => t.toolName));\n const buildTenant =\n deps.tenantPlugins ??\n ((tenantId, tenantGrants, reservedToolNames) =>\n buildTenantOpenPlugins(tenantId, tenantGrants, { reservedToolNames }));\n let tenant: TenantOpenPlugins;\n try {\n tenant = await buildTenant(context.tenantId, grants, globalToolNames);\n if (tenant.degraded) {\n // The catalog's common failure mode is a diagnostic, not a throw\n // (`ensureLoaded` returns error-level diagnostics). Surface it and\n // serve global tools only.\n log.warn(\n { tenantId: context.tenantId },\n \"open mcp: tenant plugin catalog degraded; serving global tools only\",\n );\n }\n } catch (error) {\n // Sandbox catalogs are volume-resident and can be transiently\n // unavailable; degrade to globals-only rather than failing the whole\n // session so external hosts can still connect. `err` keeps the stack.\n log.warn(\n { tenantId: context.tenantId, err: error },\n \"open mcp: tenant plugin catalog unavailable; serving global tools only\",\n );\n tenant = emptyTenantPlugins();\n }\n const mergedCatalog = mergeOpenCatalog(catalog, tenant.tools);\n const uiResources = new Map([\n ...collectExposedPluginUiResources(grants),\n ...tenant.uiResources,\n ]);\n const built = await buildSessionServer({\n credential: { credentialId: context.credentialId, kind: context.kind },\n grants,\n catalog: mergedCatalog,\n uiResources,\n invoke: async (toolName, args) => {\n const startedAt = Date.now();\n const sep = toolName.indexOf(\"_\");\n const auditDomain = sep > 0 ? toolName.slice(0, sep) : toolName;\n const auditAction = sep > 0 ? toolName.slice(sep + 1) : \"\";\n\n const decision = rateLimiter.check(context.credentialId, auditDomain);\n if (!decision.allowed) {\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: \"error\",\n errorCode: \"RATE_LIMITED\",\n durationMs: Date.now() - startedAt,\n });\n throw Object.assign(\n new Error(\n `RATE_LIMITED: retry after ${decision.retryAfterMs ?? 1000}ms`,\n ),\n { rpcCode: RPC_ERROR.RATE_LIMITED },\n );\n }\n\n try {\n const isTenantRoute = tenant.hasTool(toolName) && !globalToolNames.has(toolName);\n const runConfig: Record<string, unknown> = {\n sandboxName: context.sandboxName,\n credentialId: context.credentialId,\n depth: context.depth,\n };\n const result = isTenantRoute\n ? await tenant.invoke(toolName, args, {\n tenantId: context.tenantId,\n projectId: context.projectId,\n workspaceId: context.workspaceId,\n runConfig,\n })\n : await openLattice.invoke(\n grants,\n {\n tenantId: context.tenantId,\n projectId: context.projectId,\n workspaceId: context.workspaceId,\n runConfig,\n },\n toolName,\n args,\n );\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: result.isError ? \"error\" : \"ok\",\n ...(result.isError ? { errorCode: \"EXECUTION_ERROR\" } : {}),\n durationMs: Date.now() - startedAt,\n });\n return result;\n } catch (err) {\n const code =\n err instanceof GrantDeniedError\n ? RPC_ERROR.GRANT_DENIED\n : err instanceof ActionNotFoundError\n ? RPC_ERROR.ACTION_NOT_FOUND\n : err instanceof ValidationError\n ? RPC_ERROR.VALIDATION_FAILED\n : RPC_ERROR.EXECUTION_ERROR;\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: \"error\",\n errorCode:\n err instanceof GrantDeniedError\n ? \"GRANT_DENIED\"\n : err instanceof ActionNotFoundError\n ? \"ACTION_NOT_FOUND\"\n : err instanceof ValidationError\n ? \"VALIDATION_FAILED\"\n : \"EXECUTION_ERROR\",\n durationMs: Date.now() - startedAt,\n });\n throw Object.assign(\n new Error(err instanceof Error ? err.message : String(err)),\n { rpcCode: code },\n );\n }\n },\n });\n return built;\n };\n\n const handleOpenMcpPost = async (\n request: FastifyRequest,\n reply: FastifyReply,\n ) => {\n markReplica(reply);\n const context = await openAuth.resolve(extractBearer(request));\n if (!context) {\n return reply.status(401).send({ error: \"AUTH_FAILED\" });\n }\n\n const built = await buildServerFor(context, request.log);\n await built.server.connect(built.transport);\n reply.hijack();\n const close = (): void => {\n void built.transport.close().catch(() => undefined);\n void built.server.close().catch(() => undefined);\n };\n reply.raw.once(\"close\", close);\n await built.transport.handleRequest(request.raw, reply.raw, request.body);\n };\n\n const methodNotAllowed = async (\n _request: FastifyRequest,\n reply: FastifyReply,\n ) => {\n reply.header(\"Allow\", \"POST\");\n return reply.status(405).send({ error: \"METHOD_NOT_ALLOWED\" });\n };\n\n app.post(\"/open/mcp\", handleOpenMcpPost);\n // Canonical MCP surface aliases the unified Open MCP server.\n app.post(\"/mcp\", handleOpenMcpPost);\n app.post(\"/mcp/\", handleOpenMcpPost);\n app.get(\"/mcp\", methodNotAllowed);\n app.get(\"/mcp/\", methodNotAllowed);\n\n // Stateless Streamable HTTP has no server→client GET stream or session\n // DELETE. A 404 is not a recognized \"not supported\" signal: the official\n // client transport tolerates 405 (`Allow` set) and continues.\n app.get(\"/open/mcp\", methodNotAllowed);\n app.get(\"/open/mcp/\", methodNotAllowed);\n app.delete(\"/open/mcp\", methodNotAllowed);\n app.delete(\"/mcp\", methodNotAllowed);\n app.delete(\"/mcp/\", methodNotAllowed);\n\n}\n","/**\n * Builds a stateless MCP server instance whose tool list is the\n * credential-filtered OpenLattice catalog. A fresh instance is built per HTTP\n * request so no server/session state is shared between replicas.\n *\n * The transport uses `sessionIdGenerator: undefined`: it never issues\n * `Mcp-Session-Id`, and every request is self-contained. Credential-scoped\n * tool lists therefore stay per-request without a session registry.\n *\n * Every tool executor routes through the OpenLattice invoke kernel, so\n * authorization is re-checked on each invocation (default-deny, live grants).\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport type { McpUiCsp, OpenGrant } from \"@axiom-lattice/protocols\";\nimport type { OpenToolDescriptor } from \"./openLattice\";\n\nexport const OPEN_MCP_SERVER_INFO = {\n name: \"axiom-open\",\n version: \"0.1.0\",\n} as const;\n\n/**\n * A `ui://` HTML resource served by the session. `read` is invoked per\n * `resources/read`, so the caller can fetch plugin UI lazily.\n */\nexport interface OpenUiResource {\n /** MIME for the resource; defaults to \"text/html;profile=mcp-app\". */\n mimeType?: string;\n csp?: McpUiCsp;\n read: () => Promise<{ html: string; csp?: McpUiCsp }>;\n}\n\nexport interface BuiltSession {\n server: McpServer;\n transport: StreamableHTTPServerTransport;\n tools: OpenToolDescriptor[];\n}\n\n/**\n * Builds (but does not connect) a stateless MCP server. The caller MUST\n * connect it to a transport (`await built.server.connect(built.transport)`)\n * before handling a request — otherwise the transport has no onmessage\n * handler and the request is swallowed. Deferring connect is what lets tests\n * attach the server to an `InMemoryTransport`.\n */\nexport async function buildSessionServer(options: {\n credential: { credentialId: string; kind: string };\n grants: OpenGrant[];\n catalog: OpenToolDescriptor[];\n invoke: (\n toolName: string,\n args: unknown,\n ) => Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n }>;\n uiResources?: Map<string, OpenUiResource>;\n}): Promise<BuiltSession> {\n const { catalog, invoke, uiResources } = options;\n const server = new McpServer(OPEN_MCP_SERVER_INFO, {\n capabilities: {\n // The SDK has no `extensions` field; MCP App hosts discover the UI\n // extension through `experimental`.\n experimental: { \"io.modelcontextprotocol/ui\": {} },\n },\n });\n for (const tool of catalog) {\n server.registerTool(\n tool.toolName,\n {\n description: tool.description,\n inputSchema: tool.schema,\n annotations: {\n readOnlyHint: tool.annotations.readOnlyHint,\n destructiveHint: tool.annotations.destructiveHint,\n },\n ...(tool.ui ? { _meta: { ui: { resourceUri: tool.ui.resourceUri } } } : {}),\n },\n async (args: unknown) => {\n const result = await invoke(tool.toolName, args);\n return { content: result.content, isError: result.isError };\n },\n );\n }\n for (const [uri, res] of uiResources ?? []) {\n const mimeType = res.mimeType ?? \"text/html;profile=mcp-app\";\n server.registerResource(\n `mcp-app:${uri}`,\n uri,\n {\n mimeType,\n ...(res.csp ? { _meta: { ui: { csp: res.csp } } } : {}),\n },\n async (u) => {\n const { html, csp } = await res.read();\n return {\n contents: [\n {\n uri: u.href,\n mimeType,\n text: html,\n ...(csp ? { _meta: { ui: { csp } } } : {}),\n },\n ],\n };\n },\n );\n }\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n return { server, transport, tools: catalog };\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 { getSandboxPluginCatalog } from \"@axiom-lattice/core\";\nimport type {\n AssistantStore,\n OpenCatalog,\n OpenCatalogDomain,\n OpenCatalogItem,\n Plugin,\n} from \"@axiom-lattice/protocols\";\nimport { OpenLattice, openLattice } from \"./openLattice\";\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\nexport class OpenCatalogService {\n constructor(\n private readonly assistantStore: AssistantStore,\n private readonly lattice: OpenLattice = openLattice,\n private readonly listTenantPlugins: TenantPluginLister = defaultListTenantPlugins,\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 } else {\n enriched.push(domain);\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 enriched.push({\n domain: type,\n label: plugin.meta.name,\n source: \"plugin\",\n scopeKind: \"tenant\",\n itemKind: \"action\",\n items,\n });\n }\n return { domains: enriched };\n }\n}\n"],"mappings":";;;;;;AAMA,SAAS,uBAAuB;;;ACAhC,SAAS,+BAA+B;AAiBxC,eAAe,yBAAyB,UAAqC;AAC3E,UAAQ,MAAM,wBAAwB,EAAE,aAAa,QAAQ,GAAG;AAClE;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAC9B,YACmB,gBACA,UAAuB,aACvB,oBAAwC,0BACzD;AAHiB;AACA;AACA;AAAA,EAChB;AAAA,EAEH,MAAM,eAAe,UAAwC;AAC3D,UAAM,UAAU,KAAK,QAAQ,gBAAgB;AAC7C,UAAM,WAAgC,CAAC;AACvC,eAAW,UAAU,SAAS;AAC5B,UAAI,OAAO,WAAW,WAAW,OAAO,aAAa,YAAY;AAC/D,YAAI,QAAoC,CAAC;AACzC,YAAI;AACF,gBAAM,aAAa,MAAM,KAAK,eAAe,iBAAiB,QAAQ;AACtE,kBAAQ,WAAW,IAAI,CAAC,OAAO;AAAA,YAC7B,IAAI,EAAE;AAAA,YACN,UAAU,UAAU,EAAE,EAAE;AAAA,YACxB,OAAO,EAAE,QAAQ,EAAE;AAAA,UACrB,EAAE;AAAA,QACJ,QAAQ;AACN,kBAAQ,CAAC;AAAA,QACX;AACA,iBAAS,KAAK,EAAE,GAAG,QAAQ,MAAM,CAAC;AAAA,MACpC,OAAO;AACL,iBAAS,KAAK,MAAM;AAAA,MACtB;AAAA,IACF;AAMA,UAAM,eAAe,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACzD,QAAI,UAAoB,CAAC;AACzB,QAAI;AACF,gBAAU,MAAM,KAAK,kBAAkB,QAAQ;AAAA,IACjD,QAAQ;AACN,gBAAU,CAAC;AAAA,IACb;AACA,eAAW,UAAU,SAAS;AAC5B,YAAM,OAAO,OAAO,KAAK;AACzB,UAAI,aAAa,IAAI,IAAI,EAAG;AAC5B,mBAAa,IAAI,IAAI;AACrB,YAAM,SAA4B,OAAO,KAAK,SAAS,CAAC,GACrD,OAAO,CAAC,MAAM,EAAE,MAAM,EACtB,IAAI,CAAC,OAAO;AAAA,QACX,IAAI,EAAE;AAAA,QACN,UAAU,GAAG,IAAI,IAAI,EAAE,IAAI;AAAA,QAC3B,OAAO,EAAE;AAAA,QACT,aAAa,EAAE;AAAA,MACjB,EAAE;AACJ,eAAS,KAAK;AAAA,QACZ,QAAQ;AAAA,QACR,OAAO,OAAO,KAAK;AAAA,QACnB,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AACA,WAAO,EAAE,SAAS,SAAS;AAAA,EAC7B;AACF;;;AD5EA,SAAS,WAAW,SAA6C;AAC/D,SAAQ,QAA4B,MAAM;AAC5C;AAEO,SAAS,0BAA0B,KAA4B;AACpE,MAAI,IAAI,qBAAqB,OAAO,SAAyB,UAAwB;AACnF,UAAM,WAAW,WAAW,OAAO;AACnC,QAAI,CAAC,UAAU;AACb,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,eAAe,CAAC;AAAA,IACzE;AACA,UAAM,iBAAiB,gBAAgB,WAAW,WAAW,EAC1D;AACH,UAAM,UAAU,IAAI,mBAAmB,cAAc;AACrD,UAAM,UAAU,MAAM,QAAQ,eAAe,QAAQ;AACrD,WAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,EAChE,CAAC;AACH;","names":[]}
@@ -1 +0,0 @@
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.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;AAC7B,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":[]}
@@ -1,18 +0,0 @@
1
- import {
2
- ActionNotFoundError,
3
- GrantDeniedError,
4
- OpenLattice,
5
- ValidationError,
6
- grantCovers,
7
- openLattice
8
- } from "./chunk-HVIKCUOF.mjs";
9
- import "./chunk-NFEGQTCC.mjs";
10
- export {
11
- ActionNotFoundError,
12
- GrantDeniedError,
13
- OpenLattice,
14
- ValidationError,
15
- grantCovers,
16
- openLattice
17
- };
18
- //# sourceMappingURL=openLattice-Q6M5S6CY.mjs.map
@@ -1,8 +0,0 @@
1
- import {
2
- OpenRateLimiter
3
- } from "./chunk-GXZFO3VU.mjs";
4
- import "./chunk-NFEGQTCC.mjs";
5
- export {
6
- OpenRateLimiter
7
- };
8
- //# sourceMappingURL=openRateLimiter-XEOL3A5G.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/controllers/resources.ts","../src/services/open/sandboxGrants.ts"],"sourcesContent":["import { FastifyRequest, FastifyReply } from \"fastify\";\nimport type { ResourceAddress, ShareRecord, SharedResourceStore } from \"@axiom-lattice/protocols\";\nimport bcrypt from \"bcryptjs\";\nimport {\n generateToken,\n createSharePayload,\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 any;\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 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\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 // 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\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 _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: ShareRecord,\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 +0,0 @@
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 sandbox code so it can call\n * the Open MCP endpoint. Core stays credential-agnostic; only the gateway knows\n * the signing secret and the public URL.\n */\n\nimport type { OpenGrant } from \"@axiom-lattice/protocols\";\nimport type { SandboxOpenCredentialProvider } from \"@axiom-lattice/core\";\nimport { registerSandboxOpenCredentialProvider } from \"@axiom-lattice/core\";\nimport { signOpenToken } from \"./openToken\";\nimport {\n createDefaultAgentReflectionDeps,\n createDefaultPluginReflectionDeps,\n mergeGrantsByDomain,\n resolveAgentOpenGrants,\n resolvePluginOpenGrants,\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 a mint. */\nexport interface SandboxOpenReflection {\n /** Reflect an agent caller's own Open capabilities. */\n agent: (tenantId: string, assistantId: string) => Promise<OpenGrant[]>;\n /** Reflect the tenant's grantable Open catalog for a plugin handler caller. */\n plugin: (tenantId: 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 reflection resolvers. */\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 plugin: (tenantId) =>\n resolvePluginOpenGrants(tenantId, createDefaultPluginReflectionDeps()),\n };\n}\n\n/**\n * Build the provider.\n *\n * Grants are the union of the conservative B0 defaults with grants reflected\n * from the caller's identity (`assistantId` ⇒ agent reflection, otherwise the\n * tenant Open catalog). 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 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 = context.assistantId\n ? await reflection.agent(context.tenantId, context.assistantId)\n : await reflection.plugin(context.tenantId);\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 provider from `PUBLIC_GATEWAY_URL`.\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 return registerSandboxOpenCredentialProvider(\n createSandboxOpenCredentialProvider({\n gatewayUrl: process.env.PUBLIC_GATEWAY_URL?.trim() || undefined,\n onError,\n }),\n { onError },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAWA;AADA,SAAS,6CAA6C;AAW/C,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;AAqBtC,SAAS,oBAA2C;AAClD,SAAO;AAAA,IACL,OAAO,CAAC,UAAU,gBAChB,uBAAuB,UAAU,aAAa,iCAAiC,QAAQ,CAAC;AAAA,IAC1F,QAAQ,CAAC,aACP,wBAAwB,UAAU,kCAAkC,CAAC;AAAA,EACzE;AACF;AAaO,SAAS,oCACd,SAC+B;AAC/B,QAAM,aAAa,QAAQ,cAAc,kBAAkB;AAC3D,SAAO,OAAO,YAAY;AACxB,QAAI,CAAC,QAAQ,WAAY,QAAO;AAChC,UAAM,SAAS,QAAQ,SAAS,KAAK;AACrC,QAAI,QAAQ,wBAAwB;AAClC,cAAQ,UAAU,IAAI,MAAM,wCAAwC,CAAC;AACrE,aAAO;AAAA,IACT;AACA,UAAM,YAAY,QAAQ,cACtB,MAAM,WAAW,MAAM,QAAQ,UAAU,QAAQ,WAAW,IAC5D,MAAM,WAAW,OAAO,QAAQ,QAAQ;AAC5C,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,SAAO;AAAA,IACL,oCAAoC;AAAA,MAClC,YAAY,QAAQ,IAAI,oBAAoB,KAAK,KAAK;AAAA,MACtD;AAAA,IACF,CAAC;AAAA,IACD,EAAE,QAAQ;AAAA,EACZ;AACF;","names":[]}