akanjs 3.0.0-alpha.2 → 3.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/base/symbols.ts +4 -0
- package/client/clientRuntime.ts +8 -0
- package/client/cn.ts +7 -0
- package/client/csrTypes.ts +2 -0
- package/client/frameDebug.ts +3 -2
- package/common/Logger.ts +4 -0
- package/common/deepObjectify.ts +21 -7
- package/common/index.ts +14 -0
- package/common/mcpExposure.ts +99 -0
- package/common/pathSet.ts +17 -5
- package/common/routeConvention.ts +30 -0
- package/constant/crystalize.ts +3 -1
- package/constant/fieldInfo.ts +6 -0
- package/constant/getDefault.ts +37 -9
- package/constant/immerify.ts +1 -1
- package/constant/index.ts +2 -0
- package/constant/labelOf.ts +17 -0
- package/constant/mask.ts +60 -0
- package/constant/purify.ts +17 -11
- package/constant/types.ts +14 -12
- package/dictionary/DictionaryLookup.ts +30 -0
- package/dictionary/agent.dictionary.ts +31 -0
- package/dictionary/agentTurn.dictionary.ts +29 -0
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/dictInfo.ts +8 -0
- package/dictionary/dictionary.ts +9 -3
- package/dictionary/index.ts +1 -0
- package/document/database.ts +21 -0
- package/document/filterMeta.ts +7 -0
- package/document/index.ts +1 -0
- package/document/into.ts +7 -1
- package/document/noDocumentError.ts +12 -0
- package/fetch/agentTurn.ts +19 -0
- package/fetch/client/fetchClient.ts +71 -13
- package/fetch/client/wsClient.ts +17 -2
- package/fetch/fetchType/endpointFetch.type.ts +10 -4
- package/fetch/index.ts +1 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-wal +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-wal +0 -0
- package/package.json +11 -9
- package/server/akanApp.ts +30 -34
- package/server/akanOption.ts +65 -7
- package/server/akanServer.ts +172 -7
- package/server/animatedImage.ts +83 -0
- package/server/artifact/routeClientCache.ts +14 -0
- package/server/assetEncoding.ts +47 -0
- package/server/devtools/signalSerializer.ts +2 -2
- package/server/devtools/types.ts +2 -2
- package/server/di/diLifecycle.ts +31 -3
- package/server/di/predefinedAdaptor.ts +6 -0
- package/server/imageOptimizer.ts +33 -46
- package/server/index.ts +1 -2
- package/server/mcp/McpAuth.ts +177 -0
- package/server/mcp/McpDispatcher.ts +245 -0
- package/server/mcp/McpEventStream.ts +76 -0
- package/server/mcp/McpExecutionContext.ts +105 -0
- package/server/mcp/McpRouter.ts +591 -0
- package/server/mcp/index.ts +5 -0
- package/server/resolver/CascadeRunner.ts +1 -1
- package/server/resolver/database.resolver.ts +42 -5
- package/server/resolver/service.resolver.ts +29 -0
- package/server/resolver/signal.resolver.ts +16 -1
- package/server/routeTreeBuilder.ts +2 -44
- package/server/rscWorker.tsx +6 -2
- package/server/subRouteIndexDocument.tsx +61 -0
- package/server/systemPageDocument.tsx +2 -2
- package/server/systemPages.tsx +49 -16
- package/server/vendor/akanjs-fetch.ts +1 -0
- package/server/webRouter.ts +159 -35
- package/service/agent.service.ts +14 -0
- package/service/base.service.ts +2 -1
- package/service/index.ts +1 -0
- package/service/predefinedAdaptor/database.adaptor.ts +78 -24
- package/service/predefinedAdaptor/deepseekLlm.ts +230 -0
- package/service/predefinedAdaptor/index.ts +3 -0
- package/service/predefinedAdaptor/insightQuery.ts +183 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +76 -0
- package/service/predefinedAdaptor/role.adaptor.ts +2 -0
- package/service/predefinedAdaptor/schedule.adaptor.ts +12 -5
- package/service/serviceModule.ts +17 -0
- package/service/types.ts +4 -0
- package/signal/agent/AgentCatalogue.ts +165 -0
- package/signal/agent/index.ts +1 -0
- package/signal/agent.signal.ts +42 -0
- package/signal/agentTurn.ts +8 -0
- package/signal/agentTurnStream.ts +48 -0
- package/signal/base.signal.ts +2 -1
- package/signal/endpointInfo.ts +38 -2
- package/signal/guard.ts +13 -1
- package/signal/guards.ts +52 -1
- package/signal/index.ts +6 -0
- package/signal/mcp/McpDocument.ts +286 -0
- package/signal/mcp/McpProgress.ts +106 -0
- package/signal/mcp/McpUriTemplate.ts +85 -0
- package/signal/mcp/Msg.ts +368 -0
- package/signal/mcp/index.ts +5 -0
- package/signal/mcp/mcpProtocol.ts +120 -0
- package/signal/middleware.ts +7 -3
- package/signal/openapi/openapi.ts +32 -171
- package/signal/schema/JsonSchemaBuilder.ts +211 -0
- package/signal/schema/index.ts +1 -0
- package/signal/signalContext.ts +85 -9
- package/signal/slice.ts +9 -9
- package/signal/types.ts +1 -1
- package/store/action.ts +11 -22
- package/store/actionTag.ts +33 -0
- package/store/agent/AgentBridge.ts +323 -0
- package/store/agent/AgentContext.ts +74 -0
- package/store/agent/AgentPrompts.ts +82 -0
- package/store/agent/AgentVisibility.ts +68 -0
- package/store/agent/ScreenReader.ts +208 -0
- package/store/agent/StoreCatalogue.ts +351 -0
- package/store/agent/StoreSurfaceSource.ts +122 -0
- package/store/agent/index.ts +9 -0
- package/store/agent/storeSurface.ts +28 -0
- package/store/agent/types.ts +50 -0
- package/store/agentic/StToolBuilder.ts +199 -0
- package/store/agentic/attachAgentic.ts +22 -0
- package/store/agentic/index.ts +5 -0
- package/store/agentic/readableValue.ts +17 -0
- package/store/agentic/useStExpose.ts +18 -0
- package/store/agentic/useStState.ts +35 -0
- package/store/baseSt.ts +7 -0
- package/store/databaseStateNames.ts +31 -0
- package/store/formSetterNames.ts +21 -0
- package/store/index.ts +8 -0
- package/store/rootStore.ts +3 -1
- package/store/sliceRole.ts +36 -0
- package/store/state.ts +2 -12
- package/store/store.ts +19 -1
- package/store/storeInstance.ts +165 -20
- package/store/storeRegistry.ts +16 -1
- package/store/types.ts +18 -0
- package/store/withSelector.ts +7 -8
- package/test/registerDom.ts +4 -0
- package/types/base/symbols.d.ts +4 -0
- package/types/client/cn.d.ts +5 -0
- package/types/client/csrTypes.d.ts +2 -0
- package/types/common/Logger.d.ts +2 -0
- package/types/common/deepObjectify.d.ts +4 -2
- package/types/common/index.d.ts +2 -1
- package/types/common/mcpExposure.d.ts +60 -0
- package/types/common/routeConvention.d.ts +8 -0
- package/types/constant/fieldInfo.d.ts +1 -0
- package/types/constant/getDefault.d.ts +5 -0
- package/types/constant/index.d.ts +2 -0
- package/types/constant/labelOf.d.ts +6 -0
- package/types/constant/mask.d.ts +34 -0
- package/types/constant/purify.d.ts +2 -2
- package/types/constant/types.d.ts +2 -2
- package/types/dictionary/DictionaryLookup.d.ts +14 -0
- package/types/dictionary/agent.dictionary.d.ts +1 -0
- package/types/dictionary/agentTurn.dictionary.d.ts +1 -0
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictInfo.d.ts +6 -0
- package/types/dictionary/dictionary.d.ts +11 -9
- package/types/dictionary/index.d.ts +1 -0
- package/types/document/database.d.ts +20 -0
- package/types/document/filterMeta.d.ts +1 -0
- package/types/document/index.d.ts +1 -0
- package/types/document/into.d.ts +6 -1
- package/types/document/noDocumentError.d.ts +12 -0
- package/types/fetch/agentTurn.d.ts +593 -0
- package/types/fetch/client/fetchClient.d.ts +10 -0
- package/types/fetch/fetchType/endpointFetch.type.d.ts +5 -3
- package/types/fetch/index.d.ts +1 -0
- package/types/server/akanOption.d.ts +35 -7
- package/types/server/akanServer.d.ts +44 -1
- package/types/server/animatedImage.d.ts +7 -0
- package/types/server/artifact/routeClientCache.d.ts +6 -0
- package/types/server/assetEncoding.d.ts +7 -0
- package/types/server/devtools/types.d.ts +2 -2
- package/types/server/di/predefinedAdaptor.d.ts +4 -1
- package/types/server/index.d.ts +0 -2
- package/types/server/mcp/McpAuth.d.ts +62 -0
- package/types/server/mcp/McpDispatcher.d.ts +54 -0
- package/types/server/mcp/McpEventStream.d.ts +18 -0
- package/types/server/mcp/McpExecutionContext.d.ts +44 -0
- package/types/server/mcp/McpRouter.d.ts +60 -0
- package/types/server/mcp/index.d.ts +5 -0
- package/types/server/mcp.d.ts +1 -0
- package/types/server/subRouteIndexDocument.d.ts +8 -0
- package/types/server/systemPageDocument.d.ts +1 -0
- package/types/server/systemPages.d.ts +5 -0
- package/types/server/vendor/akanjs-fetch.d.ts +1 -0
- package/types/service/agent.service.d.ts +12 -0
- package/types/service/base.service.d.ts +4 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +24 -2
- package/types/service/predefinedAdaptor/deepseekLlm.d.ts +64 -0
- package/types/service/predefinedAdaptor/index.d.ts +3 -0
- package/types/service/predefinedAdaptor/insightQuery.d.ts +50 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +68 -0
- package/types/service/predefinedAdaptor/role.adaptor.d.ts +2 -0
- package/types/service/predefinedAdaptor/schedule.adaptor.d.ts +10 -6
- package/types/service/types.d.ts +4 -1
- package/types/signal/agent/AgentCatalogue.d.ts +100 -0
- package/types/signal/agent/index.d.ts +1 -0
- package/types/signal/agent.d.ts +1 -0
- package/types/signal/agent.signal.d.ts +29 -0
- package/types/signal/agentTurn.d.ts +9 -0
- package/types/signal/agentTurnStream.d.ts +16 -0
- package/types/signal/base.signal.d.ts +1 -1
- package/types/signal/endpointInfo.d.ts +21 -3
- package/types/signal/guard.d.ts +11 -0
- package/types/signal/guards.d.ts +22 -1
- package/types/signal/index.d.ts +6 -0
- package/types/signal/mcp/McpDocument.d.ts +65 -0
- package/types/signal/mcp/McpProgress.d.ts +40 -0
- package/types/signal/mcp/McpUriTemplate.d.ts +26 -0
- package/types/signal/mcp/Msg.d.ts +143 -0
- package/types/signal/mcp/index.d.ts +5 -0
- package/types/signal/mcp/mcpProtocol.d.ts +107 -0
- package/types/signal/mcp.d.ts +1 -0
- package/types/signal/openapi/openapi.d.ts +3 -3
- package/types/signal/schema/JsonSchemaBuilder.d.ts +47 -0
- package/types/signal/schema/index.d.ts +1 -0
- package/types/signal/schema.d.ts +1 -0
- package/types/signal/signalContext.d.ts +16 -1
- package/types/signal/slice.d.ts +8 -8
- package/types/signal/types.d.ts +1 -1
- package/types/store/actionTag.d.ts +21 -0
- package/types/store/agent/AgentBridge.d.ts +77 -0
- package/types/store/agent/AgentContext.d.ts +16 -0
- package/types/store/agent/AgentPrompts.d.ts +31 -0
- package/types/store/agent/AgentVisibility.d.ts +21 -0
- package/types/store/agent/ScreenReader.d.ts +11 -0
- package/types/store/agent/StoreCatalogue.d.ts +26 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +18 -0
- package/types/store/agent/index.d.ts +9 -0
- package/types/store/agent/storeSurface.d.ts +14 -0
- package/types/store/agent/types.d.ts +49 -0
- package/types/store/agent.d.ts +1 -0
- package/types/store/agentic/StToolBuilder.d.ts +41 -0
- package/types/store/agentic/attachAgentic.d.ts +14 -0
- package/types/store/agentic/index.d.ts +5 -0
- package/types/store/agentic/readableValue.d.ts +3 -0
- package/types/store/agentic/useStExpose.d.ts +8 -0
- package/types/store/agentic/useStState.d.ts +12 -0
- package/types/store/agentic.d.ts +1 -0
- package/types/store/baseSt.d.ts +29 -22
- package/types/store/databaseStateNames.d.ts +25 -0
- package/types/store/formSetterNames.d.ts +16 -0
- package/types/store/index.d.ts +7 -0
- package/types/store/rootStore.d.ts +5 -1
- package/types/store/sliceRole.d.ts +25 -0
- package/types/store/store.d.ts +7 -2
- package/types/store/storeInstance.d.ts +33 -1
- package/types/store/storeRegistry.d.ts +5 -0
- package/types/store/types.d.ts +15 -0
- package/types/store/withSelector.d.ts +7 -8
- package/types/test/registerDom.d.ts +1 -0
- package/types/ui/Agent/Approval.d.ts +7 -0
- package/types/ui/Agent/Bubble.d.ts +13 -0
- package/types/ui/Agent/Chat.d.ts +24 -0
- package/types/ui/Agent/Context.d.ts +9 -0
- package/types/ui/Agent/Dock.d.ts +16 -0
- package/types/ui/Agent/Guide.d.ts +9 -0
- package/types/ui/Agent/Section.d.ts +10 -0
- package/types/ui/Agent/StateKey.d.ts +16 -0
- package/types/ui/Agent/Tool.d.ts +15 -0
- package/types/ui/Agent/Transcript.d.ts +13 -0
- package/types/ui/Agent/Zone.d.ts +23 -0
- package/types/ui/Agent/fetchRunner.d.ts +12 -0
- package/types/ui/Agent/index.d.ts +17 -0
- package/types/ui/Agent/index_.d.ts +1 -0
- package/types/ui/Agent/sessionHistory.d.ts +12 -0
- package/types/ui/Agent.d.ts +1 -0
- package/types/ui/Badge.d.ts +1 -1
- package/types/ui/Button.d.ts +1 -1
- package/types/ui/CsrImage.d.ts +1 -1
- package/types/ui/Dropdown.d.ts +2 -0
- package/types/ui/Image.d.ts +3 -3
- package/types/ui/Layout/BottomInset.d.ts +2 -1
- package/types/ui/Layout/index.d.ts +1 -1
- package/types/ui/Signal/style.d.ts +4 -1
- package/types/ui/UiOverride/context.d.ts +14 -2
- package/types/ui/agentAttrs.d.ts +14 -0
- package/types/ui/index.d.ts +4 -1
- package/types/ui/overlayLayer.d.ts +24 -0
- package/types/ui/recipe/badgeRecipe.d.ts +7 -3
- package/types/ui/recipe/buttonRecipe.d.ts +7 -3
- package/types/ui/recipe/inputRecipe.d.ts +5 -1
- package/types/vendor/use-agentic/AgentProvider.d.ts +18 -0
- package/types/vendor/use-agentic/AgentScope.d.ts +9 -0
- package/types/vendor/use-agentic/AgentSession.d.ts +50 -0
- package/types/vendor/use-agentic/Agentic.d.ts +21 -0
- package/types/vendor/use-agentic/AgenticSurface.d.ts +44 -0
- package/types/vendor/use-agentic/httpRunner.d.ts +15 -0
- package/types/vendor/use-agentic/index.d.ts +13 -0
- package/types/vendor/use-agentic/surfaceContext.d.ts +5 -0
- package/types/vendor/use-agentic/types.d.ts +133 -0
- package/types/vendor/use-agentic/useAgent.d.ts +4 -0
- package/types/vendor/use-agentic/useAgentGuide.d.ts +6 -0
- package/types/vendor/use-agentic/useAgentResource.d.ts +7 -0
- package/types/vendor/use-agentic/useAgentState.d.ts +13 -0
- package/types/vendor/use-agentic/useAgentTool.d.ts +16 -0
- package/types/vendor/use-agentic.d.ts +1 -0
- package/types/webkit/index.d.ts +3 -0
- package/types/webkit/lazy.d.ts +12 -0
- package/types/webkit/useCsrValues.d.ts +3 -3
- package/types/webkit/useEscapeKey.d.ts +5 -0
- package/types/webkit/useFrameRuntime.d.ts +6 -1
- package/types/webkit/useScreenScope.d.ts +18 -0
- package/ui/Agent/Approval.tsx +29 -0
- package/ui/Agent/Bubble.tsx +74 -0
- package/ui/Agent/Chat.tsx +225 -0
- package/ui/Agent/Context.tsx +38 -0
- package/ui/Agent/Dock.tsx +72 -0
- package/ui/Agent/Guide.tsx +16 -0
- package/ui/Agent/Section.tsx +24 -0
- package/ui/Agent/StateKey.tsx +44 -0
- package/ui/Agent/Tool.tsx +66 -0
- package/ui/Agent/Transcript.tsx +33 -0
- package/ui/Agent/Zone.tsx +48 -0
- package/ui/Agent/fetchRunner.ts +30 -0
- package/ui/Agent/index.ts +12 -0
- package/ui/Agent/index_.tsx +4 -0
- package/ui/Agent/sessionHistory.ts +33 -0
- package/ui/Badge.tsx +3 -3
- package/ui/BottomSheet.tsx +5 -0
- package/ui/Button.tsx +6 -2
- package/ui/Constant/Doc.tsx +1 -1
- package/ui/CsrImage.tsx +1 -1
- package/ui/Data/ListContainer.tsx +1 -1
- package/ui/DatePicker.tsx +5 -4
- package/ui/Dialog/Modal.tsx +12 -15
- package/ui/DraggableList.tsx +3 -1
- package/ui/Dropdown.tsx +33 -11
- package/ui/Field.tsx +30 -16
- package/ui/Image.tsx +3 -3
- package/ui/Input.tsx +14 -5
- package/ui/Layout/BottomInset.tsx +6 -1
- package/ui/Load/Units.tsx +11 -2
- package/ui/Load/View.tsx +7 -2
- package/ui/Loading/ProgressBar.tsx +8 -1
- package/ui/Menu.tsx +7 -8
- package/ui/Model/EditModal.tsx +37 -2
- package/ui/Model/SureToRemove.tsx +2 -1
- package/ui/Model/index_.tsx +42 -15
- package/ui/ObjectId.tsx +3 -4
- package/ui/Pagination.tsx +3 -4
- package/ui/Popconfirm.tsx +8 -7
- package/ui/Select.tsx +6 -4
- package/ui/Signal/RestApi.tsx +43 -16
- package/ui/Signal/WebSocket.tsx +1 -1
- package/ui/Signal/style.ts +6 -1
- package/ui/Switch.tsx +2 -0
- package/ui/System/CSR.tsx +6 -2
- package/ui/System/ThemeToggle.tsx +55 -33
- package/ui/ToggleSelect.tsx +4 -3
- package/ui/Tooltip.tsx +2 -1
- package/ui/UiOverride/context.ts +14 -2
- package/ui/agentAttrs.ts +19 -0
- package/ui/index.ts +10 -1
- package/ui/overlayLayer.ts +39 -0
- package/ui/recipe/badgeRecipe.ts +22 -3
- package/ui/recipe/buttonRecipe.ts +51 -2
- package/ui/recipe/factory.ts +2 -2
- package/ui/recipe/inputRecipe.ts +18 -4
- package/vendor/use-agentic/AgentProvider.tsx +39 -0
- package/vendor/use-agentic/AgentScope.tsx +20 -0
- package/vendor/use-agentic/AgentSession.ts +292 -0
- package/vendor/use-agentic/Agentic.tsx +44 -0
- package/vendor/use-agentic/AgenticSurface.ts +294 -0
- package/vendor/use-agentic/WIRE.md +57 -0
- package/vendor/use-agentic/httpRunner.ts +115 -0
- package/vendor/use-agentic/index.ts +14 -0
- package/vendor/use-agentic/surfaceContext.ts +9 -0
- package/vendor/use-agentic/types.ts +144 -0
- package/vendor/use-agentic/useAgent.ts +17 -0
- package/vendor/use-agentic/useAgentGuide.ts +16 -0
- package/vendor/use-agentic/useAgentResource.ts +30 -0
- package/vendor/use-agentic/useAgentState.ts +61 -0
- package/vendor/use-agentic/useAgentTool.ts +55 -0
- package/webkit/bootCsr.tsx +2 -33
- package/webkit/index.ts +3 -0
- package/webkit/lazy.tsx +27 -3
- package/webkit/useCsrValues.ts +121 -4
- package/webkit/useEscapeKey.tsx +42 -0
- package/webkit/useFrameRuntime.ts +65 -32
- package/webkit/useScreenScope.tsx +49 -0
|
@@ -204,7 +204,7 @@ export class SignalSerializer {
|
|
|
204
204
|
return Object.entries(nodes).flatMap(([key, node]) => {
|
|
205
205
|
const info = meta[key];
|
|
206
206
|
if (!info) return [];
|
|
207
|
-
const transport = node.type === "
|
|
207
|
+
const transport = node.type === "pubsub" || node.type === "message" ? "ws" : "http";
|
|
208
208
|
return [
|
|
209
209
|
{
|
|
210
210
|
signal,
|
|
@@ -212,7 +212,7 @@ export class SignalSerializer {
|
|
|
212
212
|
source: typeof source === "function" ? source(key) : source,
|
|
213
213
|
type: node.type,
|
|
214
214
|
transport,
|
|
215
|
-
method: node.type === "
|
|
215
|
+
method: node.type === "mutation" ? "POST" : transport === "http" ? "GET" : null,
|
|
216
216
|
path:
|
|
217
217
|
transport === "ws"
|
|
218
218
|
? SignalSerializer.#joinPath(prefix, websocketPrefix)
|
package/server/devtools/types.ts
CHANGED
|
@@ -134,7 +134,7 @@ export interface ConstantData {
|
|
|
134
134
|
export type { ArgType, SerializedArg, SerializedReturns };
|
|
135
135
|
|
|
136
136
|
export interface EndpointNode {
|
|
137
|
-
type: "query" | "mutation" | "pubsub" | "message";
|
|
137
|
+
type: "query" | "mutation" | "pubsub" | "message" | "prompt";
|
|
138
138
|
args: SerializedArg[];
|
|
139
139
|
returns: SerializedReturns;
|
|
140
140
|
path?: string;
|
|
@@ -192,7 +192,7 @@ export interface RouteRow {
|
|
|
192
192
|
signal: string;
|
|
193
193
|
key: string;
|
|
194
194
|
source: "declared" | "crud" | "slice";
|
|
195
|
-
type: "query" | "mutation" | "message" | "pubsub";
|
|
195
|
+
type: "query" | "mutation" | "message" | "pubsub" | "prompt";
|
|
196
196
|
transport: "http" | "ws";
|
|
197
197
|
method: "GET" | "POST" | null;
|
|
198
198
|
/** Fully prefixed, `:param` placeholders intact — e.g. `/api/user/:userId`. */
|
package/server/di/diLifecycle.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
srv,
|
|
13
13
|
type WebsocketAdaptor,
|
|
14
14
|
} from "akanjs/service";
|
|
15
|
+
import { agent as agentSignal } from "../../signal/agent.signal";
|
|
16
|
+
import { agentTurnConstant, agentTurnDocument } from "../../signal/agentTurn";
|
|
15
17
|
import { Base, BaseEndpoint, BaseInternal } from "../../signal/base.signal";
|
|
16
18
|
import type { Endpoint } from "../../signal/endpoint";
|
|
17
19
|
import type { Internal } from "../../signal/internal";
|
|
@@ -82,14 +84,24 @@ export class DiLifecycle {
|
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
static #envOn(...names: string[]) {
|
|
88
|
+
return !names.some((name) => process.env[name] === "false" || process.env[name] === "0");
|
|
89
|
+
}
|
|
90
|
+
|
|
85
91
|
constructor(env: BaseEnv, serverMode: "federation" | "batch" | "all", ...libs: AkanLib[]) {
|
|
86
92
|
this.#env = env;
|
|
87
|
-
|
|
93
|
+
|
|
94
|
+
this.#predefinedAdaptor = { ...getPredefinedAdaptor(env.databaseMode ?? "single") };
|
|
88
95
|
this.#libs = libs;
|
|
89
96
|
this.#service.set("base", {
|
|
90
97
|
service: srv.base,
|
|
91
98
|
signal: SignalRegistry.registerService("base" as const, BaseInternal, BaseEndpoint, Base),
|
|
92
99
|
});
|
|
100
|
+
|
|
101
|
+
const frameworkAgent: ServiceModule | null = DiLifecycle.#envOn("AKAN_AGENT", "AKAN_PUBLIC_AGENT")
|
|
102
|
+
? { service: srv.agent, signal: agentSignal }
|
|
103
|
+
: null;
|
|
104
|
+
if (frameworkAgent) this.#service.set("agent", frameworkAgent);
|
|
93
105
|
this.#middleware.set(Logging.refName, Logging);
|
|
94
106
|
const defaultOption = createDefaultAkanOption();
|
|
95
107
|
defaultOption.getMiddlewares().forEach((middleware) => {
|
|
@@ -102,6 +114,14 @@ export class DiLifecycle {
|
|
|
102
114
|
lib.option.getMiddlewares().forEach((middleware) => {
|
|
103
115
|
this.#middleware.set(middleware.refName, middleware);
|
|
104
116
|
});
|
|
117
|
+
lib.option.getAdaptorOverrides().forEach(({ role, adaptor }) => {
|
|
118
|
+
const roleKey = Object.entries(this.#predefinedAdaptorRole).find(([, roleCls]) => roleCls === role)?.[0];
|
|
119
|
+
if (!roleKey) {
|
|
120
|
+
this.logger.warn(`applyAdaptor got an unknown role "${role.refName}" — override ignored`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
(this.#predefinedAdaptor as Record<string, AdaptorCls>)[roleKey] = adaptor;
|
|
124
|
+
});
|
|
105
125
|
this.webProxies.push(...lib.option.getWebProxies());
|
|
106
126
|
lib.database.forEach((mod) => {
|
|
107
127
|
databaseCandidates.set(mod.constant.refName, { refName: mod.constant.refName, module: mod });
|
|
@@ -122,6 +142,10 @@ export class DiLifecycle {
|
|
|
122
142
|
if (disabledModules.has(refName)) return;
|
|
123
143
|
this.#service.set(refName, module as ServiceModule);
|
|
124
144
|
});
|
|
145
|
+
if (frameworkAgent && this.#service.get("agent") !== frameworkAgent)
|
|
146
|
+
this.logger.info("agent relay is provided by a lib module — the framework's is skipped");
|
|
147
|
+
if (!this.#scalar.has("agentTurn"))
|
|
148
|
+
this.#scalar.set("agentTurn", { constant: agentTurnConstant, database: agentTurnDocument });
|
|
125
149
|
this.#database.forEach((mod) => {
|
|
126
150
|
const { adaptor, schema } = DatabaseResolver.resolveDatabase(mod.constant, mod.database);
|
|
127
151
|
this.#adaptor.set(adaptor.refName, adaptor);
|
|
@@ -365,7 +389,11 @@ export class DiLifecycle {
|
|
|
365
389
|
}
|
|
366
390
|
|
|
367
391
|
async #initializeUses() {
|
|
368
|
-
|
|
392
|
+
|
|
393
|
+
const uses = Object.assign(
|
|
394
|
+
{ llmOption: Object.assign({}, ...this.#libs.map((lib) => lib.option.getLlm(this.#env))) },
|
|
395
|
+
...this.#libs.map((lib) => lib.option.getUses(this.#env)),
|
|
396
|
+
);
|
|
369
397
|
const entries = Object.entries(uses);
|
|
370
398
|
await runStage(
|
|
371
399
|
"uses",
|
|
@@ -473,7 +501,7 @@ export class DiLifecycle {
|
|
|
473
501
|
);
|
|
474
502
|
}
|
|
475
503
|
|
|
476
|
-
this.#cascade.seal((refName) => this.getService(refName));
|
|
504
|
+
this.#cascade.seal((refName: string) => this.getService(refName));
|
|
477
505
|
}
|
|
478
506
|
|
|
479
507
|
async #initializeInternal() {
|
|
@@ -10,8 +10,11 @@ import {
|
|
|
10
10
|
ConsoleLogger,
|
|
11
11
|
type DatabaseAdaptor,
|
|
12
12
|
DatabaseAdaptorRole,
|
|
13
|
+
DeepseekLlm,
|
|
13
14
|
JsonCompressor,
|
|
14
15
|
LibsqlDatabase,
|
|
16
|
+
type LlmAdaptor,
|
|
17
|
+
LlmAdaptorRole,
|
|
15
18
|
type LoggingAdaptor,
|
|
16
19
|
LoggingAdaptorRole,
|
|
17
20
|
PostgresDatabase,
|
|
@@ -41,6 +44,7 @@ export interface PredefinedAdaptor {
|
|
|
41
44
|
logging: AdaptorCls<LoggingAdaptor>;
|
|
42
45
|
websocket: AdaptorCls<WebsocketAdaptor>;
|
|
43
46
|
compress: AdaptorCls<CompressAdaptor>;
|
|
47
|
+
llm: AdaptorCls<LlmAdaptor>;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export const predefinedAdaptorRole = {
|
|
@@ -52,6 +56,7 @@ export const predefinedAdaptorRole = {
|
|
|
52
56
|
logging: LoggingAdaptorRole,
|
|
53
57
|
websocket: WebsocketAdaptorRole,
|
|
54
58
|
compress: CompressAdaptorRole,
|
|
59
|
+
llm: LlmAdaptorRole,
|
|
55
60
|
} satisfies PredefinedAdaptor;
|
|
56
61
|
|
|
57
62
|
export const predefinedAdaptor = {
|
|
@@ -63,6 +68,7 @@ export const predefinedAdaptor = {
|
|
|
63
68
|
logging: ConsoleLogger,
|
|
64
69
|
websocket: SolidPubSub,
|
|
65
70
|
compress: JsonCompressor,
|
|
71
|
+
llm: DeepseekLlm,
|
|
66
72
|
};
|
|
67
73
|
|
|
68
74
|
export const getPredefinedAdaptor = (mode: DatabaseMode = "single"): PredefinedAdaptor => {
|
package/server/imageOptimizer.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { Logger } from "akanjs/common";
|
|
5
|
+
import { isAnimatedImage } from "./animatedImage";
|
|
4
6
|
import { ImageOptimizerError } from "./imageOptimizerError";
|
|
5
7
|
import {
|
|
6
8
|
type AkanImageConfig,
|
|
@@ -10,18 +12,6 @@ import {
|
|
|
10
12
|
mergeAkanImageConfig,
|
|
11
13
|
} from "./types";
|
|
12
14
|
|
|
13
|
-
type SharpFactory = typeof import("sharp");
|
|
14
|
-
|
|
15
|
-
let sharpLoad: Promise<SharpFactory> | null = null;
|
|
16
|
-
|
|
17
|
-
function loadSharp(): Promise<SharpFactory> {
|
|
18
|
-
sharpLoad ??= import("sharp").then((mod) => {
|
|
19
|
-
const loaded = mod as unknown as { default?: SharpFactory } & SharpFactory;
|
|
20
|
-
return loaded.default ?? loaded;
|
|
21
|
-
});
|
|
22
|
-
return sharpLoad;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
15
|
export interface ImageOptimizerOptions {
|
|
26
16
|
publicDir: string;
|
|
27
17
|
cacheDir: string;
|
|
@@ -59,18 +49,37 @@ export class ImageOptimizer {
|
|
|
59
49
|
static readonly #webp = "image/webp";
|
|
60
50
|
static readonly #gif = "image/gif";
|
|
61
51
|
static readonly #avif = "image/avif";
|
|
52
|
+
static readonly #heic = "image/heic";
|
|
62
53
|
static readonly #bypassTypes = new Set(["image/x-icon", "image/x-icns", "image/bmp", "image/jxl", "image/heic"]);
|
|
63
54
|
|
|
64
55
|
#publicDir: string;
|
|
65
56
|
#cacheDir: string;
|
|
66
57
|
#prodMode: boolean;
|
|
67
58
|
#config: AkanImageConfig;
|
|
59
|
+
#logger = new Logger("ImageOptimizer");
|
|
68
60
|
|
|
69
61
|
constructor({ publicDir, cacheDir, prodMode, config }: ImageOptimizerOptions) {
|
|
70
62
|
this.#publicDir = publicDir;
|
|
71
63
|
this.#cacheDir = cacheDir;
|
|
72
64
|
this.#prodMode = prodMode;
|
|
73
|
-
|
|
65
|
+
const merged = mergeAkanImageConfig(config);
|
|
66
|
+
const formats = merged.formats.filter((format) => ImageOptimizer.#isCodecAvailable(format));
|
|
67
|
+
if (formats.length !== merged.formats.length) {
|
|
68
|
+
const dropped = merged.formats.filter((format) => !formats.includes(format));
|
|
69
|
+
this.#logger.warn(`${dropped.join(", ")} needs an OS codec this platform does not have; serving webp instead`);
|
|
70
|
+
}
|
|
71
|
+
this.#config = { ...merged, formats: formats.length ? formats : ["image/webp"] };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* AVIF, HEIC and TIFF ride the OS codec, which only the `system` backend has — macOS and Windows.
|
|
76
|
+
* A Linux container runs the `bun` backend, where both encode and decode raise
|
|
77
|
+
* `ERR_IMAGE_FORMAT_UNSUPPORTED`, so those types are decided up front rather than thrown at.
|
|
78
|
+
*/
|
|
79
|
+
static #isCodecAvailable(contentType: string): boolean {
|
|
80
|
+
if (contentType !== ImageOptimizer.#avif && contentType !== ImageOptimizer.#heic && contentType !== "image/tiff")
|
|
81
|
+
return true;
|
|
82
|
+
return Bun.Image.backend === "system";
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
async handle(req: Request): Promise<Response> {
|
|
@@ -217,7 +226,8 @@ export class ImageOptimizer {
|
|
|
217
226
|
const shouldBypass =
|
|
218
227
|
inputType === ImageOptimizer.#svg ||
|
|
219
228
|
ImageOptimizer.#bypassTypes.has(inputType) ||
|
|
220
|
-
|
|
229
|
+
!ImageOptimizer.#isCodecAvailable(inputType) ||
|
|
230
|
+
isAnimatedImage(source.buffer, inputType);
|
|
221
231
|
const outputType =
|
|
222
232
|
shouldBypass || !params.outputType || inputType === ImageOptimizer.#webp || inputType === ImageOptimizer.#avif
|
|
223
233
|
? inputType
|
|
@@ -240,7 +250,7 @@ export class ImageOptimizer {
|
|
|
240
250
|
let cacheable = true;
|
|
241
251
|
if (!shouldBypass) {
|
|
242
252
|
try {
|
|
243
|
-
buffer = await ImageOptimizer.#
|
|
253
|
+
buffer = await ImageOptimizer.#optimize(source.buffer, {
|
|
244
254
|
width: params.width,
|
|
245
255
|
quality: params.quality,
|
|
246
256
|
contentType: outputType,
|
|
@@ -281,24 +291,17 @@ export class ImageOptimizer {
|
|
|
281
291
|
return new Response(ImageOptimizer.#toArrayBuffer(image.buffer), { headers });
|
|
282
292
|
}
|
|
283
293
|
|
|
284
|
-
static async #
|
|
294
|
+
static async #optimize(
|
|
285
295
|
buffer: Buffer,
|
|
286
296
|
options: { width: number; quality: number; contentType: string },
|
|
287
297
|
): Promise<Buffer> {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (options.contentType === ImageOptimizer.#avif)
|
|
296
|
-
return await transformer.avif({ quality: options.quality }).toBuffer();
|
|
297
|
-
if (options.contentType === ImageOptimizer.#webp)
|
|
298
|
-
return await transformer.webp({ quality: options.quality }).toBuffer();
|
|
299
|
-
if (options.contentType === ImageOptimizer.#png)
|
|
300
|
-
return await transformer.png({ quality: options.quality }).toBuffer();
|
|
301
|
-
return await transformer.jpeg({ quality: options.quality }).toBuffer();
|
|
298
|
+
const image = new Bun.Image(buffer).resize(options.width, undefined, { withoutEnlargement: true });
|
|
299
|
+
|
|
300
|
+
if (options.contentType === ImageOptimizer.#avif) return await image.avif({ quality: options.quality }).toBuffer();
|
|
301
|
+
if (options.contentType === ImageOptimizer.#webp) return await image.webp({ quality: options.quality }).toBuffer();
|
|
302
|
+
|
|
303
|
+
if (options.contentType === ImageOptimizer.#png) return await image.png().toBuffer();
|
|
304
|
+
return await image.jpeg({ quality: options.quality }).toBuffer();
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
static #getPreferredOutputType(accept: string, formats: AkanImageFormat[]): AkanImageFormat | "" {
|
|
@@ -375,22 +378,6 @@ export class ImageOptimizer {
|
|
|
375
378
|
return Buffer.concat(chunks);
|
|
376
379
|
}
|
|
377
380
|
|
|
378
|
-
static async #isAnimated(buffer: Buffer, contentType: string): Promise<boolean> {
|
|
379
|
-
if (
|
|
380
|
-
contentType !== ImageOptimizer.#gif &&
|
|
381
|
-
contentType !== ImageOptimizer.#webp &&
|
|
382
|
-
contentType !== ImageOptimizer.#png
|
|
383
|
-
)
|
|
384
|
-
return false;
|
|
385
|
-
try {
|
|
386
|
-
const sharp = await loadSharp();
|
|
387
|
-
const metadata = await sharp(buffer, { animated: true }).metadata();
|
|
388
|
-
return Boolean(metadata.pages && metadata.pages > 1);
|
|
389
|
-
} catch {
|
|
390
|
-
return false;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
381
|
static #detectContentType(buffer: Buffer): string | null {
|
|
395
382
|
if (buffer.length < 12) return null;
|
|
396
383
|
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return ImageOptimizer.#jpeg;
|
package/server/index.ts
CHANGED
|
@@ -9,8 +9,7 @@ export * from "./devtools";
|
|
|
9
9
|
export type { ChangeBatch, ChangeKind } from "./hmr/changeBatch";
|
|
10
10
|
export * from "./processMetricsCollector";
|
|
11
11
|
export * from "./proxy";
|
|
12
|
-
|
|
13
|
-
export * from "./routeTreeBuilder";
|
|
12
|
+
|
|
14
13
|
export * from "./sitemap";
|
|
15
14
|
export type { SsrManifest, SsrManifestEntry } from "./ssrTypes";
|
|
16
15
|
export * from "./types";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { HttpRoutes } from "../types";
|
|
2
|
+
|
|
3
|
+
export interface McpAuthOption {
|
|
4
|
+
/** Where a client may obtain a token, published in the metadata document for it to discover. */
|
|
5
|
+
authorizationServers?: string[];
|
|
6
|
+
/** Scopes every call must carry. Declaring any turns `insufficient_scope` enforcement on. */
|
|
7
|
+
scopes?: string[];
|
|
8
|
+
/** Overrides the resource identifier when the public URL differs from what the request reports. */
|
|
9
|
+
resource?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface McpAuthProps extends McpAuthOption {
|
|
13
|
+
/** Path the MCP endpoint is mounted at. Its absolute URL is this server's OAuth resource identifier. */
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface McpAuthFailure {
|
|
18
|
+
status: 401 | 403;
|
|
19
|
+
error: "invalid_token" | "insufficient_scope";
|
|
20
|
+
description: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The OAuth 2.1 protected-resource half of the MCP authorization spec: the RFC 9728 metadata document, the
|
|
25
|
+
* `WWW-Authenticate` challenge that points at it, and the token checks that can be made without an issuer.
|
|
26
|
+
*
|
|
27
|
+
* The authorization server itself is a separate project. What lives here is what a resource server owes a
|
|
28
|
+
* client regardless of who mints the tokens, so wiring one up later needs no change on this side.
|
|
29
|
+
*/
|
|
30
|
+
export class McpAuth {
|
|
31
|
+
static readonly wellKnownPath = "/.well-known/oauth-protected-resource";
|
|
32
|
+
|
|
33
|
+
readonly #props: McpAuthProps;
|
|
34
|
+
|
|
35
|
+
constructor(props: McpAuthProps) {
|
|
36
|
+
this.#props = props;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Both spellings of the metadata path. RFC 9728 inserts the resource's path into the well-known URL, and MCP
|
|
41
|
+
* clients try that form first and the bare one second; serving only one leaves half the clients at a 404.
|
|
42
|
+
*/
|
|
43
|
+
createRoutes(): HttpRoutes {
|
|
44
|
+
const handler = { GET: (req: Request) => this.#metadata(req) };
|
|
45
|
+
return {
|
|
46
|
+
[McpAuth.wellKnownPath]: handler,
|
|
47
|
+
[`${McpAuth.wellKnownPath}${this.#props.path}`]: handler,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
unauthorized(req: Request, failure?: McpAuthFailure) {
|
|
52
|
+
const { status, error, description } = failure ?? {
|
|
53
|
+
status: 401 as const,
|
|
54
|
+
error: "invalid_token" as const,
|
|
55
|
+
description: "Authentication is required to use this MCP server.",
|
|
56
|
+
};
|
|
57
|
+
return Response.json(
|
|
58
|
+
{ error, error_description: description },
|
|
59
|
+
{ status, headers: { "WWW-Authenticate": this.#challenge(req, error, description) } },
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Refuses a bearer token that is already provably unusable, before the signal pipeline sees it.
|
|
65
|
+
*
|
|
66
|
+
* The claims are read **without verifying the signature**, which is sound only because every branch below can
|
|
67
|
+
* deny and none can grant: a forged token still has to pass the app's own middleware afterwards, so the worst
|
|
68
|
+
* a crafted payload achieves is refusing itself. What this buys is the failure mode it removes — an expired or
|
|
69
|
+
* foreign token otherwise degrades to an anonymous caller, and the agent is told a tool does not exist rather
|
|
70
|
+
* than that it needs to authenticate.
|
|
71
|
+
*/
|
|
72
|
+
reject(req: Request): Response | null {
|
|
73
|
+
const claims = McpAuth.#claims(req);
|
|
74
|
+
if (!claims) return null;
|
|
75
|
+
const failure = this.#failureOf(claims, req);
|
|
76
|
+
return failure ? this.unauthorized(req, failure) : null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#failureOf(claims: Record<string, unknown>, req: Request): McpAuthFailure | null {
|
|
80
|
+
const { exp, aud } = claims;
|
|
81
|
+
if (typeof exp === "number" && exp * 1000 <= Date.now())
|
|
82
|
+
return { status: 401, error: "invalid_token", description: "The access token has expired." };
|
|
83
|
+
const audience = (Array.isArray(aud) ? aud : typeof aud === "string" ? [aud] : []).filter(
|
|
84
|
+
(value): value is string => typeof value === "string",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (this.#props.authorizationServers?.length && !audience.length)
|
|
88
|
+
return { status: 401, error: "invalid_token", description: "The access token names no resource." };
|
|
89
|
+
if (audience.length && !audience.includes(this.#resource(req)))
|
|
90
|
+
return { status: 401, error: "invalid_token", description: "The access token was issued for another resource." };
|
|
91
|
+
const missing = this.#missingScopes(claims);
|
|
92
|
+
|
|
93
|
+
if (missing.length)
|
|
94
|
+
return { status: 403, error: "insufficient_scope", description: `Missing required scope: ${missing.join(" ")}.` };
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Enforced only when `scopes` is configured, because this server's own tokens carry no scope claim at all —
|
|
100
|
+
* declaring one is how a deployment says its issuer mints them.
|
|
101
|
+
*/
|
|
102
|
+
#missingScopes(claims: Record<string, unknown>) {
|
|
103
|
+
const required = this.#props.scopes ?? [];
|
|
104
|
+
if (!required.length) return [];
|
|
105
|
+
const raw = claims.scope ?? claims.scp;
|
|
106
|
+
const granted = new Set(Array.isArray(raw) ? raw.map(String) : String(raw ?? "").split(/\s+/));
|
|
107
|
+
return required.filter((scope) => !granted.has(scope));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#metadata(req: Request) {
|
|
111
|
+
const { authorizationServers, scopes } = this.#props;
|
|
112
|
+
return Response.json(
|
|
113
|
+
{
|
|
114
|
+
resource: this.#resource(req),
|
|
115
|
+
...(authorizationServers?.length ? { authorization_servers: authorizationServers } : {}),
|
|
116
|
+
bearer_methods_supported: ["header"],
|
|
117
|
+
...(scopes?.length ? { scopes_supported: scopes } : {}),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
|
|
121
|
+
headers: { "access-control-allow-origin": "*", "cache-control": "public, max-age=3600" },
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#challenge(req: Request, error: string, description: string) {
|
|
127
|
+
const scopes = this.#props.scopes ?? [];
|
|
128
|
+
return [
|
|
129
|
+
"Bearer",
|
|
130
|
+
[
|
|
131
|
+
`resource_metadata="${new URL(McpAuth.wellKnownPath + this.#props.path, McpAuth.origin(req)).href}"`,
|
|
132
|
+
`error="${error}"`,
|
|
133
|
+
`error_description="${description}"`,
|
|
134
|
+
...(scopes.length ? [`scope="${scopes.join(" ")}"`] : []),
|
|
135
|
+
].join(", "),
|
|
136
|
+
].join(" ");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#resource(req: Request) {
|
|
140
|
+
return this.#props.resource ?? new URL(this.#props.path, McpAuth.origin(req)).href;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The public origin, not the one the request arrived on. Behind a proxy `req.url` names the internal host the
|
|
145
|
+
* proxy dialed, and publishing that as the resource identifier is not cosmetic: an authorization server issues
|
|
146
|
+
* `aud` for the URL the *client* used, so every correctly-issued token would then fail the audience check.
|
|
147
|
+
*
|
|
148
|
+
* `McpRouter` compares an `Origin` header against this too — the same proxy that makes `req.url` wrong for the
|
|
149
|
+
* metadata document makes it wrong for a same-origin check, and there the cost is a flat 403.
|
|
150
|
+
*
|
|
151
|
+
* `x-forwarded-host` is a request header, so this is exactly as trustworthy as the edge that overwrites it, and
|
|
152
|
+
* a deployment whose edge merely appends leaves both the same-origin comparison and the audience a token is
|
|
153
|
+
* checked against to the caller. A browser cannot reach it — a custom header is not on a preflight, and the
|
|
154
|
+
* `OPTIONS` is judged on the real host — but a direct caller can. `AKAN_MCP_RESOURCE` pins the identifier for a
|
|
155
|
+
* deployment that cannot make the guarantee.
|
|
156
|
+
*/
|
|
157
|
+
static origin(req: Request) {
|
|
158
|
+
const url = new URL(req.url);
|
|
159
|
+
const forwarded = (header: string) => req.headers.get(header)?.split(",")[0]?.trim();
|
|
160
|
+
const host = forwarded("x-forwarded-host") ?? req.headers.get("host") ?? url.host;
|
|
161
|
+
return `${forwarded("x-forwarded-proto") ?? url.protocol.replace(":", "")}://${host}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static #claims(req: Request): Record<string, unknown> | null {
|
|
165
|
+
const [scheme, token] = (req.headers.get("authorization") ?? "").split(" ");
|
|
166
|
+
if (scheme !== "Bearer" || !token) return null;
|
|
167
|
+
const segments = token.split(".");
|
|
168
|
+
|
|
169
|
+
if (segments.length !== 3) return null;
|
|
170
|
+
try {
|
|
171
|
+
const claims: unknown = JSON.parse(Buffer.from(segments[1], "base64url").toString("utf8"));
|
|
172
|
+
return claims && typeof claims === "object" ? (claims as Record<string, unknown>) : null;
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|