akanjs 3.0.0-alpha.7 → 3.0.0-alpha.71
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/CHANGELOG.md +103 -0
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/base/baseEnv.ts +7 -4
- package/base/primitiveRegistry.ts +59 -12
- package/base/symbols.ts +4 -0
- package/client/capacitor.ts +34 -0
- package/client/clientRuntime.ts +5 -0
- package/client/csrTypes.ts +4 -3
- package/client/index.ts +1 -0
- package/client/locale.ts +2 -2
- package/client/router.ts +7 -7
- package/client/sharedContext.ts +31 -0
- package/client/translator.ts +2 -2
- package/client/types.ts +3 -3
- package/common/Logger.ts +280 -67
- package/common/clientAddress.ts +40 -0
- package/common/deepObjectify.ts +24 -9
- package/common/index.ts +42 -1
- package/common/logContext.ts +27 -0
- package/common/mcpExposure.ts +114 -0
- package/common/pathGetLoose.ts +31 -0
- package/common/pathSet.ts +17 -5
- package/common/plainFieldsOf.ts +13 -0
- package/common/routeConvention.ts +22 -12
- package/common/websocketBinaryFrame.ts +46 -0
- package/constant/crystalize.ts +94 -28
- package/constant/dateSlot.ts +62 -0
- package/constant/deserialize.ts +3 -2
- package/constant/fieldInfo.ts +43 -3
- package/constant/fieldQueryMeta.ts +35 -0
- package/constant/getDefault.ts +16 -11
- package/constant/hydrationPlan.ts +72 -0
- package/constant/immerify.ts +4 -6
- package/constant/index.ts +5 -0
- package/constant/labelOf.ts +26 -0
- package/constant/mask.ts +67 -0
- package/constant/purify.ts +17 -11
- package/constant/textFieldPaths.ts +13 -7
- package/constant/types.ts +19 -13
- package/constant/via.ts +24 -17
- 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 +81 -0
- package/dictionary/dictInfo.ts +11 -2
- package/dictionary/dictionary.ts +9 -3
- package/dictionary/index.ts +1 -0
- package/dictionary/trans.ts +2 -2
- package/document/documentQuery.ts +1 -0
- package/document/filterMeta.ts +77 -12
- package/document/index.ts +1 -0
- package/document/noDocumentError.ts +12 -0
- package/document/types.ts +0 -1
- package/fetch/agentTurn.ts +19 -0
- package/fetch/client/fetchClient.ts +137 -37
- package/fetch/client/httpClient.ts +95 -20
- package/fetch/client/wsClient.ts +27 -12
- package/fetch/fetchType/appliedReturn.type.ts +65 -44
- package/fetch/fetchType/endpointFetch.type.ts +17 -8
- package/fetch/fetchType/sliceFetch.type.ts +27 -50
- package/fetch/index.ts +1 -0
- package/index.ts +80 -6
- package/local/apps/akan/akan-local.db +0 -0
- package/local/apps/akan/akan-local.db-shm +0 -0
- package/local/apps/akan/akan-local.db-wal +0 -0
- package/local/apps/akan/akan-local_solid.db +0 -0
- package/local/apps/akan/akan-local_solid.db-shm +0 -0
- package/local/apps/akan/akan-local_solid.db-wal +0 -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 +27 -18
- package/server/akanApp.ts +278 -138
- package/server/akanAppHeaders.ts +16 -2
- package/server/akanOption.ts +63 -8
- package/server/akanServer.ts +387 -32
- package/server/animatedImage.ts +83 -0
- package/server/assetEncoding.ts +4 -24
- package/server/binaryPubsub.ts +55 -0
- package/server/console.ts +110 -101
- package/server/consoleEvaluator.ts +51 -0
- package/server/consolePasteFilter.ts +91 -0
- package/server/consoleSession.ts +242 -0
- package/server/contentEncoding.ts +83 -0
- package/server/devtools/signalSerializer.ts +2 -2
- package/server/devtools/types.ts +4 -4
- package/server/di/diLifecycle.ts +123 -13
- package/server/di/predefinedAdaptor.ts +6 -0
- package/server/di/utils.ts +40 -0
- package/server/imageOptimizer.ts +154 -57
- package/server/lifecycle/runtimeDir.ts +14 -0
- package/server/lifecycle/shutdownManager.ts +1 -1
- package/server/logging/childOutputReader.ts +137 -0
- package/server/logging/hubFileSink.ts +39 -0
- package/server/logging/logControlSocket.ts +173 -0
- package/server/logging/logForwarder.ts +131 -0
- package/server/logging/logHub.ts +0 -0
- package/server/logging/logQuery.ts +130 -0
- package/server/logging/logStdoutWriter.ts +39 -0
- package/server/logging/logStreamRoute.ts +87 -0
- package/server/logging/logTailClient.ts +161 -0
- package/server/mcp/McpAuth.ts +177 -0
- package/server/mcp/McpDispatcher.ts +289 -0
- package/server/mcp/McpEventStream.ts +24 -0
- package/server/mcp/McpExecutionContext.ts +105 -0
- package/server/mcp/McpRouter.ts +621 -0
- package/server/mcp/index.ts +5 -0
- package/server/processMetricsCollector.ts +1 -1
- package/server/resolver/database.resolver.ts +5 -4
- package/server/resolver/signal.resolver.ts +208 -86
- package/server/routeTreeBuilder.ts +10 -8
- package/server/routing/apiRouter.ts +44 -14
- package/server/routing/appWsData.ts +24 -2
- package/server/routing/eventStream.ts +93 -0
- package/server/routing/soloAppRoutes.ts +50 -0
- package/server/rscWorker.tsx +36 -22
- package/server/rscWorkerHost.ts +59 -8
- package/server/semaphore.ts +22 -0
- package/server/subRouteIndexDocument.tsx +61 -0
- package/server/systemPageDocument.tsx +3 -3
- package/server/systemPages.tsx +49 -16
- package/server/types.tsx +28 -0
- package/server/vendor/akanjs-fetch.ts +1 -0
- package/server/webRouter.ts +96 -39
- package/service/adapt.ts +4 -4
- package/service/agent.service.ts +81 -0
- package/service/base.service.ts +2 -1
- package/service/index.ts +1 -0
- package/service/injectInfo.ts +8 -3
- package/service/ipcTypes.ts +6 -0
- package/service/predefinedAdaptor/compress.adaptor.ts +5 -1
- package/service/predefinedAdaptor/database.adaptor.ts +68 -37
- package/service/predefinedAdaptor/deepseekLlm.ts +271 -0
- package/service/predefinedAdaptor/index.ts +3 -0
- package/service/predefinedAdaptor/insightQuery.ts +183 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +115 -0
- package/service/predefinedAdaptor/logging.adaptor.ts +1 -1
- package/service/predefinedAdaptor/queue.adaptor.ts +5 -2
- package/service/predefinedAdaptor/role.adaptor.ts +2 -0
- package/service/predefinedAdaptor/schedule.adaptor.ts +12 -5
- package/service/predefinedAdaptor/solidPubsub.adaptor.ts +10 -6
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +6 -2
- package/service/predefinedAdaptor/solidSqlite.ts +11 -9
- package/service/predefinedAdaptor/websocket.adaptor.ts +6 -1
- package/service/serve.ts +16 -9
- package/service/toolNames.ts +119 -0
- package/signal/agent/AgentCatalogue.ts +165 -0
- package/signal/agent/index.ts +1 -0
- package/signal/agent.signal.ts +38 -0
- package/signal/agentTurn.ts +8 -0
- package/signal/agentTurnStream.ts +60 -0
- package/signal/base.signal.ts +2 -1
- package/signal/endpointInfo.ts +38 -2
- package/signal/guard.ts +28 -1
- package/signal/guards.ts +49 -1
- package/signal/index.ts +6 -0
- package/signal/intercept.ts +4 -4
- package/signal/internalArg.ts +21 -4
- package/signal/mcp/McpDocument.ts +326 -0
- package/signal/mcp/McpProgress.ts +106 -0
- package/signal/mcp/McpUriTemplate.ts +80 -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 +3 -3
- package/signal/openapi/openapi.ts +36 -173
- package/signal/schema/JsonSchemaBuilder.ts +221 -0
- package/signal/schema/index.ts +1 -0
- package/signal/serializer/fetch.serializer.ts +51 -0
- package/signal/signalContext.ts +324 -76
- package/signal/slice.ts +62 -27
- package/signal/trace.ts +268 -9
- package/signal/types.ts +55 -4
- package/store/action.ts +34 -41
- package/store/actionTag.ts +33 -0
- package/store/agent/AgentBridge.ts +87 -0
- package/store/agent/AgentContext.ts +71 -0
- package/store/agent/AgentPrompts.ts +111 -0
- package/store/agent/ScreenReader.ts +349 -0
- package/store/agent/ScreenSettle.ts +45 -0
- package/store/agent/ScreenTarget.ts +135 -0
- package/store/agent/StoreCatalogue.ts +116 -0
- package/store/agent/StoreSurfaceSource.ts +225 -0
- package/store/agent/index.ts +13 -0
- package/store/agent/storeSurface.ts +28 -0
- package/store/agent/types.ts +16 -0
- package/store/agentic/AgentValue.ts +119 -0
- package/store/agentic/StExposeBuilder.ts +47 -0
- package/store/agentic/StExposeDraft.ts +24 -0
- package/store/agentic/StStateBuilder.ts +85 -0
- package/store/agentic/StStateDraft.ts +23 -0
- package/store/agentic/StToolBuilder.ts +279 -0
- package/store/agentic/StToolDraft.ts +22 -0
- package/store/agentic/attachAgentic.ts +33 -0
- package/store/agentic/formFields.ts +194 -0
- package/store/agentic/index.ts +12 -0
- package/store/agentic/useFieldTool.ts +183 -0
- package/store/agentic/useFormTools.ts +77 -0
- package/store/agentic/useRelationFieldTool.ts +113 -0
- package/store/baseSt.ts +1 -1
- package/store/databaseStateNames.ts +31 -0
- package/store/formSetterNames.ts +35 -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 +9 -0
- package/store/storeInstance.ts +185 -20
- package/store/storeRegistry.ts +39 -1
- package/store/types.ts +5 -0
- package/store/withSelector.ts +5 -4
- package/test/index.ts +1 -1
- package/test/registerDom.ts +4 -0
- package/test/sampleOf.ts +2 -0
- package/test/signalTestRuntime.ts +5 -5
- package/test/testServer.ts +14 -11
- package/types/base/baseEnv.d.ts +1 -1
- package/types/base/primitiveRegistry.d.ts +20 -8
- package/types/base/symbols.d.ts +4 -0
- package/types/client/capacitor.d.ts +39 -0
- package/types/client/clientRuntime.d.ts +7 -0
- package/types/client/index.d.ts +1 -0
- package/types/client/sharedContext.d.ts +21 -0
- package/types/client/types.d.ts +5 -3
- package/types/common/Logger.d.ts +88 -6
- package/types/common/clientAddress.d.ts +19 -0
- package/types/common/deepObjectify.d.ts +4 -2
- package/types/common/index.d.ts +8 -2
- package/types/common/logContext.d.ts +17 -0
- package/types/common/mcpExposure.d.ts +64 -0
- package/types/common/pathGetLoose.d.ts +8 -0
- package/types/common/plainFieldsOf.d.ts +6 -0
- package/types/common/routeConvention.d.ts +6 -0
- package/types/common/websocketBinaryFrame.d.ts +17 -0
- package/types/constant/constantRegistry.d.ts +2 -10
- package/types/constant/crystalize.d.ts +5 -0
- package/types/constant/dateSlot.d.ts +14 -0
- package/types/constant/fieldInfo.d.ts +20 -3
- package/types/constant/fieldQueryMeta.d.ts +17 -0
- package/types/constant/getDefault.d.ts +8 -0
- package/types/constant/hydrationPlan.d.ts +24 -0
- package/types/constant/index.d.ts +5 -0
- package/types/constant/labelOf.d.ts +7 -0
- package/types/constant/mask.d.ts +41 -0
- package/types/constant/purify.d.ts +2 -2
- package/types/constant/types.d.ts +5 -5
- 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 +7 -1
- package/types/dictionary/dictionary.d.ts +11 -9
- package/types/dictionary/index.d.ts +1 -0
- package/types/document/filterMeta.d.ts +24 -4
- package/types/document/index.d.ts +1 -0
- package/types/document/noDocumentError.d.ts +12 -0
- package/types/document/types.d.ts +0 -1
- package/types/fetch/agentTurn.d.ts +475 -0
- package/types/fetch/client/fetchClient.d.ts +12 -0
- package/types/fetch/client/httpClient.d.ts +2 -1
- package/types/fetch/fetchType/appliedReturn.type.d.ts +37 -18
- package/types/fetch/fetchType/endpointFetch.type.d.ts +8 -5
- package/types/fetch/fetchType/sliceFetch.type.d.ts +11 -15
- package/types/fetch/index.d.ts +1 -0
- package/types/index.d.ts +74 -10
- package/types/server/akanApp.d.ts +12 -0
- package/types/server/akanAppHeaders.d.ts +7 -1
- package/types/server/akanOption.d.ts +36 -6
- package/types/server/akanServer.d.ts +67 -9
- package/types/server/animatedImage.d.ts +7 -0
- package/types/server/assetEncoding.d.ts +1 -1
- package/types/server/binaryPubsub.d.ts +18 -0
- package/types/server/console.d.ts +11 -1
- package/types/server/consoleEvaluator.d.ts +2 -0
- package/types/server/consolePasteFilter.d.ts +11 -0
- package/types/server/consoleSession.d.ts +22 -0
- package/types/server/contentEncoding.d.ts +28 -0
- package/types/server/devtools/types.d.ts +4 -4
- package/types/server/di/diLifecycle.d.ts +10 -2
- package/types/server/di/predefinedAdaptor.d.ts +4 -1
- package/types/server/di/utils.d.ts +16 -0
- package/types/server/lifecycle/runtimeDir.d.ts +5 -0
- package/types/server/logging/childOutputReader.d.ts +40 -0
- package/types/server/logging/hubFileSink.d.ts +18 -0
- package/types/server/logging/logControlSocket.d.ts +60 -0
- package/types/server/logging/logForwarder.d.ts +37 -0
- package/types/server/logging/logHub.d.ts +70 -0
- package/types/server/logging/logQuery.d.ts +31 -0
- package/types/server/logging/logStdoutWriter.d.ts +35 -0
- package/types/server/logging/logStreamRoute.d.ts +20 -0
- package/types/server/logging/logTailClient.d.ts +42 -0
- package/types/server/mcp/McpAuth.d.ts +62 -0
- package/types/server/mcp/McpDispatcher.d.ts +61 -0
- package/types/server/mcp/McpEventStream.d.ts +17 -0
- package/types/server/mcp/McpExecutionContext.d.ts +44 -0
- package/types/server/mcp/McpRouter.d.ts +65 -0
- package/types/server/mcp/index.d.ts +5 -0
- package/types/server/mcp.d.ts +1 -0
- package/types/server/resolver/signal.resolver.d.ts +7 -4
- package/types/server/routing/apiRouter.d.ts +2 -1
- package/types/server/routing/appWsData.d.ts +13 -1
- package/types/server/routing/eventStream.d.ts +23 -0
- package/types/server/routing/soloAppRoutes.d.ts +17 -0
- package/types/server/rscWorkerHost.d.ts +4 -1
- package/types/server/semaphore.d.ts +5 -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/types.d.ts +16 -0
- package/types/server/vendor/akanjs-fetch.d.ts +1 -0
- package/types/server/webRouter.d.ts +15 -4
- package/types/service/adapt.d.ts +2 -2
- package/types/service/agent.service.d.ts +32 -0
- package/types/service/base.service.d.ts +4 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/injectInfo.d.ts +2 -2
- package/types/service/ipcTypes.d.ts +12 -0
- package/types/service/predefinedAdaptor/compress.adaptor.d.ts +1 -1
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +9 -3
- package/types/service/predefinedAdaptor/deepseekLlm.d.ts +76 -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 +105 -0
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +3 -2
- package/types/service/predefinedAdaptor/role.adaptor.d.ts +2 -0
- package/types/service/predefinedAdaptor/schedule.adaptor.d.ts +10 -6
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +6 -2
- package/types/service/predefinedAdaptor/solidSqlite.d.ts +2 -2
- package/types/service/serve.d.ts +3 -3
- package/types/service/toolNames.d.ts +35 -0
- 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 +24 -0
- package/types/signal/agentTurn.d.ts +9 -0
- package/types/signal/agentTurnStream.d.ts +24 -0
- package/types/signal/base.signal.d.ts +1 -1
- package/types/signal/endpointInfo.d.ts +21 -3
- package/types/signal/guard.d.ts +12 -0
- package/types/signal/guards.d.ts +23 -1
- package/types/signal/index.d.ts +6 -0
- package/types/signal/intercept.d.ts +2 -2
- package/types/signal/internalArg.d.ts +22 -6
- package/types/signal/mcp/McpDocument.d.ts +84 -0
- package/types/signal/mcp/McpProgress.d.ts +40 -0
- package/types/signal/mcp/McpUriTemplate.d.ts +25 -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/middleware.d.ts +7 -7
- package/types/signal/openapi/openapi.d.ts +3 -3
- package/types/signal/schema/JsonSchemaBuilder.d.ts +51 -0
- package/types/signal/schema/index.d.ts +1 -0
- package/types/signal/schema.d.ts +1 -0
- package/types/signal/signalContext.d.ts +67 -8
- package/types/signal/slice.d.ts +42 -15
- package/types/signal/trace.d.ts +52 -2
- package/types/signal/types.d.ts +53 -4
- package/types/store/action.d.ts +7 -5
- package/types/store/actionTag.d.ts +21 -0
- package/types/store/agent/AgentBridge.d.ts +37 -0
- package/types/store/agent/AgentContext.d.ts +16 -0
- package/types/store/agent/AgentPrompts.d.ts +31 -0
- package/types/store/agent/ScreenReader.d.ts +29 -0
- package/types/store/agent/ScreenSettle.d.ts +22 -0
- package/types/store/agent/ScreenTarget.d.ts +34 -0
- package/types/store/agent/StoreCatalogue.d.ts +26 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +30 -0
- package/types/store/agent/index.d.ts +11 -0
- package/types/store/agent/storeSurface.d.ts +14 -0
- package/types/store/agent/types.d.ts +15 -0
- package/types/store/agent.d.ts +1 -0
- package/types/store/agentic/AgentValue.d.ts +43 -0
- package/types/store/agentic/StExposeBuilder.d.ts +21 -0
- package/types/store/agentic/StExposeDraft.d.ts +13 -0
- package/types/store/agentic/StStateBuilder.d.ts +21 -0
- package/types/store/agentic/StStateDraft.d.ts +12 -0
- package/types/store/agentic/StToolBuilder.d.ts +56 -0
- package/types/store/agentic/StToolDraft.d.ts +13 -0
- package/types/store/agentic/attachAgentic.d.ts +25 -0
- package/types/store/agentic/formFields.d.ts +53 -0
- package/types/store/agentic/index.d.ts +12 -0
- package/types/store/agentic/useFieldTool.d.ts +22 -0
- package/types/store/agentic/useFormTools.d.ts +19 -0
- package/types/store/agentic/useRelationFieldTool.d.ts +27 -0
- package/types/store/agentic.d.ts +1 -0
- package/types/store/baseSt.d.ts +35 -39
- package/types/store/databaseStateNames.d.ts +25 -0
- package/types/store/formSetterNames.d.ts +30 -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 +5 -1
- package/types/store/storeInstance.d.ts +33 -1
- package/types/store/storeRegistry.d.ts +5 -0
- package/types/store/types.d.ts +4 -0
- package/types/store/withSelector.d.ts +5 -4
- package/types/test/index.d.ts +1 -1
- package/types/test/registerDom.d.ts +1 -0
- package/types/test/signalTestRuntime.d.ts +2 -3
- package/types/test/testServer.d.ts +6 -4
- package/types/ui/Agent/Approval.d.ts +8 -0
- package/types/ui/Agent/Attach.d.ts +16 -0
- package/types/ui/Agent/Bubble.d.ts +26 -0
- package/types/ui/Agent/Chat.d.ts +91 -0
- package/types/ui/Agent/ChatCommands.d.ts +34 -0
- package/types/ui/Agent/Composer.d.ts +23 -0
- package/types/ui/Agent/Context.d.ts +13 -0
- package/types/ui/Agent/Dock.d.ts +17 -0
- package/types/ui/Agent/Guide.d.ts +9 -0
- package/types/ui/Agent/History.d.ts +26 -0
- package/types/ui/Agent/Launcher.d.ts +16 -0
- package/types/ui/Agent/Markdown.d.ts +19 -0
- package/types/ui/Agent/Menu.d.ts +18 -0
- package/types/ui/Agent/Mic.d.ts +8 -0
- package/types/ui/Agent/Question.d.ts +14 -0
- package/types/ui/Agent/Section.d.ts +10 -0
- package/types/ui/Agent/Skip.d.ts +22 -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 +48 -0
- package/types/ui/Agent/agentSessionOf.d.ts +27 -0
- package/types/ui/Agent/attachment.d.ts +34 -0
- package/types/ui/Agent/fetchRunner.d.ts +12 -0
- package/types/ui/Agent/index.d.ts +19 -0
- package/types/ui/Agent/index_.d.ts +1 -0
- package/types/ui/Agent/markdownBlocks.d.ts +35 -0
- package/types/ui/Agent/markdownSpans.d.ts +2 -0
- package/types/ui/Agent/markdownTable.d.ts +16 -0
- package/types/ui/Agent/sessionHistory.d.ts +14 -0
- package/types/ui/Agent/sessionView.d.ts +15 -0
- package/types/ui/Agent/tokenCount.d.ts +6 -0
- package/types/ui/Agent/useChatAttachments.d.ts +24 -0
- package/types/ui/Agent/useChatVoice.d.ts +30 -0
- package/types/ui/Agent/useDraftRecall.d.ts +14 -0
- package/types/ui/Agent/useKeyboardInset.d.ts +8 -0
- package/types/ui/Agent/useSlashMenu.d.ts +24 -0
- package/types/ui/Agent/voice.d.ts +71 -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/Constant/Graph.d.ts +10 -0
- package/types/ui/Constant/index.d.ts +2 -1
- package/types/ui/Constant/schemaGraph.d.ts +39 -0
- package/types/ui/Data/Dashboard.d.ts +16 -3
- package/types/ui/Data/Insight.d.ts +1 -1
- package/types/ui/Data/ListContainer.d.ts +15 -7
- package/types/ui/Data/QueryMaker.d.ts +45 -3
- package/types/ui/Data/RefPicker.d.ts +16 -0
- package/types/ui/Data/dataExport.d.ts +5 -0
- package/types/ui/Data/dataText.d.ts +6 -0
- package/types/ui/Data/index.d.ts +2 -0
- package/types/ui/Data/index_.d.ts +1 -0
- package/types/ui/DatePicker.d.ts +15 -11
- package/types/ui/Dialog/LegacyModal.d.ts +13 -0
- package/types/ui/Dialog/Provider.d.ts +3 -1
- package/types/ui/Dialog/context.d.ts +9 -1
- package/types/ui/Dialog/index.d.ts +1 -0
- package/types/ui/DraggableList.d.ts +1 -1
- package/types/ui/Dropdown.d.ts +5 -1
- package/types/ui/Field.d.ts +13 -12
- package/types/ui/Layout/BottomTab.d.ts +3 -2
- package/types/ui/LegacyModal.d.ts +24 -0
- package/types/ui/Loading/ProgressBar.d.ts +4 -0
- package/types/ui/Loading/Spin.d.ts +2 -1
- package/types/ui/Model/AdminPanel.d.ts +15 -3
- package/types/ui/Model/EditModal.d.ts +1 -1
- package/types/ui/Model/New.d.ts +3 -1
- package/types/ui/Model/NewWrapper.d.ts +2 -0
- package/types/ui/Model/NewWrapper_Client.d.ts +2 -1
- package/types/ui/Pagination.d.ts +2 -2
- package/types/ui/Radio.d.ts +1 -1
- package/types/ui/RecentTime.d.ts +18 -1
- package/types/ui/Reference/Collapse.d.ts +11 -0
- package/types/ui/Reference/Panel.d.ts +29 -0
- package/types/ui/Reference/Segmented.d.ts +17 -0
- package/types/ui/Reference/Summary.d.ts +13 -0
- package/types/ui/Reference/Toolbar.d.ts +21 -0
- package/types/ui/Reference/dictText.d.ts +6 -0
- package/types/ui/Reference/index.d.ts +7 -0
- package/types/ui/Reference/style.d.ts +20 -0
- package/types/ui/Reference.d.ts +1 -0
- package/types/ui/ScreenNavigator.d.ts +3 -1
- package/types/ui/Signal/Arg.d.ts +7 -3
- package/types/ui/Signal/Doc.d.ts +4 -6
- package/types/ui/Signal/Object.d.ts +2 -1
- package/types/ui/Signal/RestApi.d.ts +2 -1
- package/types/ui/Signal/WebSocket.d.ts +2 -1
- package/types/ui/Signal/endpointEntries.d.ts +20 -0
- package/types/ui/Signal/style.d.ts +9 -8
- package/types/ui/System/Client.d.ts +1 -2
- package/types/ui/System/Common.d.ts +3 -4
- package/types/ui/System/Messages.d.ts +1 -1
- package/types/ui/Tab/Provider.d.ts +3 -1
- package/types/ui/Tab/context.d.ts +4 -2
- package/types/ui/ToggleSelect.d.ts +1 -0
- package/types/ui/UiOverride/context.d.ts +18 -1
- package/types/ui/agentAttrs.d.ts +14 -0
- package/types/ui/index.d.ts +22 -1
- package/types/ui/overlayLayer.d.ts +13 -0
- package/types/ui/overlayPosition.d.ts +28 -0
- package/types/ui/recipe/badgeRecipe.d.ts +2 -2
- package/types/ui/recipe/buttonRecipe.d.ts +2 -2
- package/types/vendor/use-agentic/AgentAbort.d.ts +17 -0
- package/types/vendor/use-agentic/AgentProgress.d.ts +27 -0
- package/types/vendor/use-agentic/AgentProvider.d.ts +17 -0
- package/types/vendor/use-agentic/AgentScope.d.ts +9 -0
- package/types/vendor/use-agentic/AgentSession.d.ts +163 -0
- package/types/vendor/use-agentic/Agentic.d.ts +21 -0
- package/types/vendor/use-agentic/AgenticSurface.d.ts +58 -0
- package/types/vendor/use-agentic/Compaction.d.ts +48 -0
- package/types/vendor/use-agentic/ToolOutput.d.ts +21 -0
- package/types/vendor/use-agentic/Transcript.d.ts +18 -0
- package/types/vendor/use-agentic/httpRunner.d.ts +15 -0
- package/types/vendor/use-agentic/index.d.ts +19 -0
- package/types/vendor/use-agentic/sharedContext.d.ts +15 -0
- package/types/vendor/use-agentic/surfaceContext.d.ts +5 -0
- package/types/vendor/use-agentic/types.d.ts +182 -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 +12 -0
- package/types/vendor/use-agentic/useAgentState.d.ts +16 -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 +4 -0
- package/types/webkit/useBodyScrollLock.d.ts +2 -0
- package/types/webkit/usePageTool.d.ts +19 -0
- package/types/webkit/useScreenScope.d.ts +25 -0
- package/ui/Agent/Approval.tsx +32 -0
- package/ui/Agent/Attach.tsx +77 -0
- package/ui/Agent/Bubble.tsx +192 -0
- package/ui/Agent/Chat.tsx +525 -0
- package/ui/Agent/ChatCommands.ts +141 -0
- package/ui/Agent/Composer.tsx +94 -0
- package/ui/Agent/Context.tsx +49 -0
- package/ui/Agent/Dock.tsx +77 -0
- package/ui/Agent/Guide.tsx +16 -0
- package/ui/Agent/History.tsx +49 -0
- package/ui/Agent/Launcher.tsx +60 -0
- package/ui/Agent/Markdown.tsx +142 -0
- package/ui/Agent/Menu.tsx +55 -0
- package/ui/Agent/Mic.tsx +27 -0
- package/ui/Agent/Question.tsx +63 -0
- package/ui/Agent/Section.tsx +24 -0
- package/ui/Agent/Skip.tsx +30 -0
- package/ui/Agent/StateKey.tsx +46 -0
- package/ui/Agent/Tool.tsx +59 -0
- package/ui/Agent/Transcript.tsx +33 -0
- package/ui/Agent/Zone.tsx +116 -0
- package/ui/Agent/agentSessionOf.ts +60 -0
- package/ui/Agent/attachment.ts +73 -0
- package/ui/Agent/fetchRunner.ts +44 -0
- package/ui/Agent/index.ts +27 -0
- package/ui/Agent/index_.tsx +4 -0
- package/ui/Agent/markdownBlocks.ts +137 -0
- package/ui/Agent/markdownSpans.tsx +49 -0
- package/ui/Agent/markdownTable.ts +54 -0
- package/ui/Agent/sessionHistory.ts +62 -0
- package/ui/Agent/sessionView.ts +38 -0
- package/ui/Agent/tokenCount.ts +10 -0
- package/ui/Agent/useChatAttachments.ts +84 -0
- package/ui/Agent/useChatVoice.ts +129 -0
- package/ui/Agent/useDraftRecall.ts +42 -0
- package/ui/Agent/useKeyboardInset.ts +26 -0
- package/ui/Agent/useSlashMenu.ts +58 -0
- package/ui/Agent/voice.ts +177 -0
- package/ui/Badge.tsx +1 -1
- package/ui/BottomSheet.tsx +32 -36
- package/ui/Button.tsx +3 -1
- package/ui/Clipboard.tsx +3 -6
- package/ui/Constant/Doc.tsx +265 -254
- package/ui/Constant/Graph.tsx +151 -0
- package/ui/Constant/index.ts +3 -2
- package/ui/Constant/schemaGraph.ts +232 -0
- package/ui/Data/CardList.tsx +14 -3
- package/ui/Data/Dashboard.tsx +70 -52
- package/ui/Data/Insight.tsx +18 -12
- package/ui/Data/Item.tsx +42 -56
- package/ui/Data/ListContainer.tsx +246 -132
- package/ui/Data/Pagination.tsx +10 -3
- package/ui/Data/QueryMaker.tsx +160 -11
- package/ui/Data/RefPicker.tsx +202 -0
- package/ui/Data/TableList.tsx +22 -13
- package/ui/Data/dataExport.ts +54 -0
- package/ui/Data/dataText.ts +22 -0
- package/ui/Data/index.ts +13 -1
- package/ui/Data/index_.tsx +1 -0
- package/ui/DatePicker.tsx +110 -128
- package/ui/Dialog/Close.tsx +3 -7
- package/ui/Dialog/LegacyModal.tsx +241 -0
- package/ui/Dialog/Modal.tsx +62 -195
- package/ui/Dialog/Provider.tsx +47 -3
- package/ui/Dialog/Trigger.tsx +3 -7
- package/ui/Dialog/context.ts +14 -2
- package/ui/Dialog/index.tsx +2 -0
- package/ui/DragAction.tsx +3 -2
- package/ui/DraggableList.tsx +9 -4
- package/ui/Dropdown.tsx +89 -27
- package/ui/Empty.tsx +6 -4
- package/ui/Field.tsx +128 -58
- package/ui/Input.tsx +27 -5
- package/ui/KeyboardAvoiding.tsx +2 -2
- package/ui/Layout/BottomTab.tsx +32 -37
- package/ui/Layout/Sider.tsx +37 -14
- package/ui/LegacyModal.tsx +53 -0
- package/ui/Link/CsrLink.tsx +1 -1
- package/ui/Load/Pagination.tsx +15 -8
- package/ui/Load/Units.tsx +30 -7
- package/ui/Load/View.tsx +10 -3
- package/ui/Loading/Area.tsx +7 -6
- package/ui/Loading/Button.tsx +7 -9
- package/ui/Loading/Input.tsx +7 -9
- package/ui/Loading/ProgressBar.tsx +16 -7
- package/ui/Loading/Skeleton.tsx +8 -13
- package/ui/Loading/Spin.tsx +20 -14
- package/ui/Menu.tsx +1 -1
- package/ui/Model/AdminPanel.tsx +52 -12
- package/ui/Model/EditModal.tsx +28 -6
- package/ui/Model/EditWrapper.tsx +16 -4
- package/ui/Model/New.tsx +4 -0
- package/ui/Model/NewWrapper.tsx +2 -0
- package/ui/Model/NewWrapper_Client.tsx +20 -6
- package/ui/Model/Remove.tsx +18 -6
- package/ui/Model/RemoveWrapper.tsx +12 -2
- package/ui/Model/SureToRemove.tsx +18 -6
- package/ui/Model/ViewEditModal.tsx +64 -15
- package/ui/Model/ViewModal.tsx +25 -11
- package/ui/Model/ViewWrapper.tsx +15 -4
- package/ui/Model/index_.tsx +1 -1
- package/ui/Pagination.tsx +51 -66
- package/ui/Popconfirm.tsx +88 -78
- package/ui/Radio.tsx +53 -24
- package/ui/RecentTime.tsx +83 -35
- package/ui/Reference/Collapse.tsx +26 -0
- package/ui/Reference/Panel.tsx +79 -0
- package/ui/Reference/Segmented.tsx +41 -0
- package/ui/Reference/Summary.tsx +25 -0
- package/ui/Reference/Toolbar.tsx +44 -0
- package/ui/Reference/dictText.ts +9 -0
- package/ui/Reference/index.ts +7 -0
- package/ui/Reference/style.ts +45 -0
- package/ui/ScreenNavigator.tsx +17 -2
- package/ui/Select.tsx +96 -57
- package/ui/Signal/Arg.tsx +83 -68
- package/ui/Signal/Doc.tsx +163 -129
- package/ui/Signal/Listener.tsx +40 -29
- package/ui/Signal/Message.tsx +87 -135
- package/ui/Signal/Object.tsx +79 -57
- package/ui/Signal/PubSub.tsx +72 -113
- package/ui/Signal/Request.tsx +2 -22
- package/ui/Signal/Response.tsx +19 -45
- package/ui/Signal/RestApi.tsx +170 -200
- package/ui/Signal/WebSocket.tsx +29 -26
- package/ui/Signal/endpointEntries.ts +59 -0
- package/ui/Signal/makeExample.ts +10 -3
- package/ui/Signal/style.ts +34 -29
- package/ui/Switch.tsx +4 -0
- package/ui/System/CSR.tsx +1 -1
- package/ui/System/Client.tsx +31 -15
- package/ui/System/Common.tsx +3 -4
- package/ui/System/DevModeToggle.tsx +1 -1
- package/ui/System/Gtag.tsx +2 -2
- package/ui/System/Messages.tsx +52 -31
- package/ui/System/Reconnect.tsx +22 -56
- package/ui/System/SelectLanguage.tsx +24 -15
- package/ui/System/ThemeToggle.tsx +25 -13
- package/ui/Tab/Menu.tsx +24 -12
- package/ui/Tab/Menus.tsx +6 -3
- package/ui/Tab/Provider.tsx +30 -3
- package/ui/Tab/context.ts +8 -4
- package/ui/Table.tsx +66 -60
- package/ui/ToggleSelect.tsx +8 -7
- package/ui/Tooltip.tsx +15 -1
- package/ui/UiOverride/context.ts +21 -2
- package/ui/Unauthorized.tsx +6 -4
- package/ui/agentAttrs.ts +19 -0
- package/ui/index.ts +49 -1
- package/ui/overlayLayer.ts +12 -2
- package/ui/overlayPosition.ts +104 -0
- package/ui/recipe/buttonRecipe.ts +2 -2
- package/ui/styles.css +31 -0
- package/vendor/use-agentic/AgentAbort.ts +29 -0
- package/vendor/use-agentic/AgentProgress.ts +40 -0
- package/vendor/use-agentic/AgentProvider.tsx +39 -0
- package/vendor/use-agentic/AgentScope.tsx +20 -0
- package/vendor/use-agentic/AgentSession.ts +750 -0
- package/vendor/use-agentic/Agentic.tsx +44 -0
- package/vendor/use-agentic/AgenticSurface.ts +333 -0
- package/vendor/use-agentic/Compaction.ts +120 -0
- package/vendor/use-agentic/ToolOutput.ts +60 -0
- package/vendor/use-agentic/Transcript.ts +50 -0
- package/vendor/use-agentic/WIRE.md +86 -0
- package/vendor/use-agentic/httpRunner.ts +131 -0
- package/vendor/use-agentic/index.ts +20 -0
- package/vendor/use-agentic/sharedContext.ts +25 -0
- package/vendor/use-agentic/surfaceContext.ts +10 -0
- package/vendor/use-agentic/types.ts +192 -0
- package/vendor/use-agentic/useAgent.ts +18 -0
- package/vendor/use-agentic/useAgentGuide.ts +16 -0
- package/vendor/use-agentic/useAgentResource.ts +36 -0
- package/vendor/use-agentic/useAgentState.ts +64 -0
- package/vendor/use-agentic/useAgentTool.ts +55 -0
- package/webkit/bootCsr.tsx +10 -8
- package/webkit/index.ts +4 -0
- package/webkit/lazy.tsx +5 -1
- package/webkit/useBodyScrollLock.tsx +29 -0
- package/webkit/useFrameRuntime.ts +1 -1
- package/webkit/usePageTool.tsx +37 -0
- package/webkit/useScreenScope.tsx +60 -0
- package/types/ui/Constant/Mermaid.d.ts +0 -8
- package/types/ui/Signal/Collapse.d.ts +0 -12
- package/ui/Constant/Mermaid.tsx +0 -149
- package/ui/Signal/Collapse.tsx +0 -29
package/server/akanApp.ts
CHANGED
|
@@ -2,15 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
import { mkdir, readdir, rm } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { Logger } from "akanjs/common";
|
|
5
|
+
import { Logger, logSeverity } from "akanjs/common";
|
|
6
6
|
import type { AkanChildRole, AkanChildStatus, AkanIpcMessage, AkanMetricsReport, AkanUpstream } from "akanjs/service";
|
|
7
|
-
import { isTraceEnabled } from "
|
|
7
|
+
import { isTraceEnabled } from "../signal/trace";
|
|
8
8
|
import { makeAkanChildProxyHeaders } from "./akanAppHeaders";
|
|
9
9
|
import type { BuilderCsrReq, BuilderCsrRes, BuilderMessage, BuilderReq, BuilderRes } from "./artifact";
|
|
10
10
|
import { resolveEncodedSidecar } from "./assetEncoding";
|
|
11
|
+
import { compressResponse } from "./contentEncoding";
|
|
11
12
|
import { isPortInUseError } from "./lifecycle/portInUse";
|
|
13
|
+
import { resolveRuntimeDir } from "./lifecycle/runtimeDir";
|
|
14
|
+
import { ChildOutputReader } from "./logging/childOutputReader";
|
|
15
|
+
import { HubFileSink } from "./logging/hubFileSink";
|
|
16
|
+
import { LogControlSocket } from "./logging/logControlSocket";
|
|
17
|
+
import { LogHub } from "./logging/logHub";
|
|
18
|
+
import { LogStreamRoute } from "./logging/logStreamRoute";
|
|
12
19
|
import { RotatingLogWriter } from "./logging/rotatingLogWriter";
|
|
13
20
|
import { ProcessMetricsCollector } from "./processMetricsCollector";
|
|
21
|
+
import { getWebConfigFromEnv } from "./types";
|
|
14
22
|
|
|
15
23
|
interface ChildState {
|
|
16
24
|
idx: number;
|
|
@@ -35,9 +43,13 @@ interface ChildState {
|
|
|
35
43
|
lastErrorMessage?: string;
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
/** The shape `#startSolo` needs off `server.ts`; the module itself is loaded by path, so it has no type here. */
|
|
47
|
+
interface SoloServer {
|
|
48
|
+
start: (options: { listen: boolean }) => Promise<unknown>;
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
interface GatewayWsData {
|
|
39
52
|
childIdx: number;
|
|
40
|
-
socketId: string;
|
|
41
53
|
upstream: WebSocket;
|
|
42
54
|
}
|
|
43
55
|
|
|
@@ -46,6 +58,19 @@ type GatewayUpstream = {
|
|
|
46
58
|
ws?: Extract<AkanUpstream, { type: "tcp" }>;
|
|
47
59
|
};
|
|
48
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Received-only close codes cannot be sent in a close frame. The gateway deliberately normalizes
|
|
63
|
+
* every unsendable code, including semantically distinct 1005 and 1006 events, to 1001 in both
|
|
64
|
+
* relay directions. In particular, Bun's global client `WebSocket.close()` throws an
|
|
65
|
+
* InvalidAccessError for these codes at the client-to-upstream relay; normalization at the
|
|
66
|
+
* upstream-to-client `Bun.ServerWebSocket.close()` relay is defensive and keeps behavior symmetric.
|
|
67
|
+
*/
|
|
68
|
+
const relayableCloseCode = (code: number): number => {
|
|
69
|
+
if (code >= 3000 && code <= 4999) return code;
|
|
70
|
+
if (code >= 1000 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006) return code;
|
|
71
|
+
return 1001;
|
|
72
|
+
};
|
|
73
|
+
|
|
49
74
|
/** Options for the Akan gateway that launches child server replicas and listens for traffic. */
|
|
50
75
|
export interface AkanAppOptions {
|
|
51
76
|
replica?: number | string;
|
|
@@ -54,6 +79,18 @@ export interface AkanAppOptions {
|
|
|
54
79
|
port?: number;
|
|
55
80
|
wsBasePort?: number;
|
|
56
81
|
openapi?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Boot only these modules and the ones they reach, in every child. Omitted or empty mounts every enabled
|
|
84
|
+
* module. Handed down as `AKAN_MODULES`, since each replica builds its own container.
|
|
85
|
+
*/
|
|
86
|
+
modules?: string[];
|
|
87
|
+
/**
|
|
88
|
+
* Run the one replica in this process instead of spawning it — see `#startSolo`. Defaults on for a single
|
|
89
|
+
* traffic replica the environment configured, and off whenever `replica` is passed here, because code that
|
|
90
|
+
* states a topology is asking for the gateway that serves it. `AKAN_SOLO=false` turns it off; like
|
|
91
|
+
* `AKAN_SSR`, the env can only narrow, so it cannot force a gateway's replicas into one process.
|
|
92
|
+
*/
|
|
93
|
+
solo?: boolean;
|
|
57
94
|
}
|
|
58
95
|
|
|
59
96
|
interface AkanReplicaConfig {
|
|
@@ -76,6 +113,9 @@ export class AkanApp {
|
|
|
76
113
|
/** Hosted by `akan start`: crash loops should yield to the dev host, which restarts on file edits. */
|
|
77
114
|
readonly #devHosted = process.env.AKAN_COMMAND_TYPE === "start";
|
|
78
115
|
readonly #healthTimeoutMs = AkanApp.#parseHealthTimeoutMs();
|
|
116
|
+
readonly #upstreamWaitMs = AkanApp.#parseUpstreamWaitMs();
|
|
117
|
+
/** Child stderr is bundler/runtime noise the gateway cannot act on; it still reaches the rotating log file. */
|
|
118
|
+
readonly #printChildStderr = process.env.AKAN_CHILD_STDERR === "1";
|
|
79
119
|
readonly #serverPath: string;
|
|
80
120
|
readonly #artifactDir: string;
|
|
81
121
|
readonly #replica: AkanReplicaConfig;
|
|
@@ -84,6 +124,10 @@ export class AkanApp {
|
|
|
84
124
|
readonly #port: number;
|
|
85
125
|
readonly #wsBasePort: number;
|
|
86
126
|
readonly #openapi?: boolean;
|
|
127
|
+
/** The gateway hands `/_akan/client|styles|fonts` straight off disk, so it needs the same answer its children do. */
|
|
128
|
+
readonly #web = getWebConfigFromEnv();
|
|
129
|
+
readonly #modules: string[];
|
|
130
|
+
readonly #solo: boolean;
|
|
87
131
|
readonly #children = new Map<number, ChildState>();
|
|
88
132
|
readonly #roomChildren = new Map<string, Set<number>>();
|
|
89
133
|
readonly #childRooms = new Map<number, Set<string>>();
|
|
@@ -98,9 +142,10 @@ export class AkanApp {
|
|
|
98
142
|
#metricsTimer: Timer | null = null;
|
|
99
143
|
#logWriter: RotatingLogWriter | null = null;
|
|
100
144
|
#removeLogSink: (() => void) | null = null;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
#logHub: LogHub | null = null;
|
|
146
|
+
#logControl: LogControlSocket | null = null;
|
|
147
|
+
#hubFileSink: HubFileSink | null = null;
|
|
148
|
+
#logStream: LogStreamRoute | null = null;
|
|
104
149
|
static readonly #ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
105
150
|
#gatewayMetrics: AkanMetricsReport = {};
|
|
106
151
|
#proxyHopCount = 0;
|
|
@@ -115,16 +160,30 @@ export class AkanApp {
|
|
|
115
160
|
this.#serverPath = AkanApp.#resolveServerPath(resolvedOptions.serverPath ?? serverPath);
|
|
116
161
|
this.#artifactDir = path.resolve(path.dirname(this.#serverPath), ".akan", "artifact");
|
|
117
162
|
this.#replica = AkanApp.#parseReplicaConfig(resolvedOptions.replica);
|
|
118
|
-
this.#runtimeDir =
|
|
119
|
-
resolvedOptions.runtimeDir ??
|
|
120
|
-
process.env.AKAN_RUNTIME_DIR ??
|
|
121
|
-
(process.env.NODE_ENV === "production"
|
|
122
|
-
? path.resolve(process.cwd(), "runtime")
|
|
123
|
-
: path.resolve(process.cwd(), "local", "apps", process.env.AKAN_PUBLIC_APP_NAME ?? "unknown", "runtime")),
|
|
124
|
-
);
|
|
163
|
+
this.#runtimeDir = resolveRuntimeDir(resolvedOptions.runtimeDir);
|
|
125
164
|
this.#port = Number(resolvedOptions.port ?? process.env.PORT ?? 8282);
|
|
126
165
|
this.#wsBasePort = Number(resolvedOptions.wsBasePort ?? process.env.AKAN_WS_BASE_PORT ?? this.#port + 10_000);
|
|
127
166
|
this.#openapi = resolvedOptions.openapi;
|
|
167
|
+
this.#modules = resolvedOptions.modules ?? [];
|
|
168
|
+
this.#solo = AkanApp.#resolveSolo(resolvedOptions, this.#replica);
|
|
169
|
+
this.#logHub = LogHub.attach();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One replica that serves traffic means there is nothing to balance and nothing to fan pubsub out to, so the
|
|
174
|
+
* gateway is a second Bun runtime and a proxy hop in front of the only server there is. Measured on
|
|
175
|
+
* `apps/akan`: 28MB of RSS and half the requests per second.
|
|
176
|
+
*
|
|
177
|
+
* A batch-only replica is excluded because it never listens — keeping the gateway is what leaves something
|
|
178
|
+
* bound to answer `/_akan/app/health`. `akan start` is excluded because the gateway is also the dev host's
|
|
179
|
+
* builder relay, its crash page, and what holds the port across a child restart.
|
|
180
|
+
*/
|
|
181
|
+
static #resolveSolo(options: AkanAppOptions, replica: AkanReplicaConfig) {
|
|
182
|
+
if (options.solo !== undefined) return options.solo;
|
|
183
|
+
if (process.env.AKAN_SOLO === "false" || process.env.AKAN_SOLO === "0") return false;
|
|
184
|
+
if (process.env.AKAN_COMMAND_TYPE === "start") return false;
|
|
185
|
+
if (options.replica !== undefined) return false;
|
|
186
|
+
return replica.total === 1 && replica.batch === 0;
|
|
128
187
|
}
|
|
129
188
|
|
|
130
189
|
static #resolveServerPath(serverPath: string) {
|
|
@@ -157,10 +216,16 @@ export class AkanApp {
|
|
|
157
216
|
return Math.max(min, parsed);
|
|
158
217
|
}
|
|
159
218
|
|
|
219
|
+
/**
|
|
220
|
+
* The command that started this gateway outranks an inherited `NODE_ENV`: the command is a statement about
|
|
221
|
+
* this run, while the env var can arrive from a shell export, a CI image, or the workspace `.env` Bun loads
|
|
222
|
+
* on its own. Handing a dev child `NODE_ENV=production` makes it serve from the production route cache and
|
|
223
|
+
* throw on every route, since only `akan build` writes the routes manifest that cache expects.
|
|
224
|
+
*/
|
|
160
225
|
static #defaultChildNodeEnv() {
|
|
161
|
-
if (process.env.NODE_ENV) return process.env.NODE_ENV;
|
|
162
226
|
if (process.env.AKAN_COMMAND_TYPE === "start") return "development";
|
|
163
227
|
if (process.env.AKAN_COMMAND_TYPE === "build") return "production";
|
|
228
|
+
if (process.env.NODE_ENV) return process.env.NODE_ENV;
|
|
164
229
|
return Bun.main.endsWith(".js") ? "production" : "development";
|
|
165
230
|
}
|
|
166
231
|
|
|
@@ -174,6 +239,17 @@ export class AkanApp {
|
|
|
174
239
|
return process.env.AKAN_COMMAND_TYPE === "start" ? 15_000 : 5_000;
|
|
175
240
|
}
|
|
176
241
|
|
|
242
|
+
/**
|
|
243
|
+
* How long a request waits for a replica that is booting or restarting before the gateway answers
|
|
244
|
+
* 503. Dev pays first-touch transpiles on the boot path, so it gets the wider budget; `0` restores
|
|
245
|
+
* the old fail-immediately behavior.
|
|
246
|
+
*/
|
|
247
|
+
static #parseUpstreamWaitMs() {
|
|
248
|
+
const configured = Number(process.env.AKAN_UPSTREAM_WAIT_MS);
|
|
249
|
+
if (Number.isFinite(configured) && configured >= 0) return configured;
|
|
250
|
+
return process.env.AKAN_COMMAND_TYPE === "start" ? 15_000 : 5_000;
|
|
251
|
+
}
|
|
252
|
+
|
|
177
253
|
/**
|
|
178
254
|
* Must exceed the child's own shutdown timeout (see `AkanServer.#defaultShutdownTimeoutMs`) so
|
|
179
255
|
* children always get to exit on their own before this gateway stops waiting.
|
|
@@ -185,7 +261,10 @@ export class AkanApp {
|
|
|
185
261
|
}
|
|
186
262
|
|
|
187
263
|
async start() {
|
|
264
|
+
if (this.#solo) return await this.#startSolo();
|
|
265
|
+
Logger.role = "gateway";
|
|
188
266
|
await this.#prepareRuntimeDir();
|
|
267
|
+
await this.#startLogHub();
|
|
189
268
|
this.#startFileLogging();
|
|
190
269
|
for (let idx = 0; idx < this.#replica.total; idx++) this.#spawn(idx);
|
|
191
270
|
try {
|
|
@@ -212,6 +291,25 @@ export class AkanApp {
|
|
|
212
291
|
});
|
|
213
292
|
}
|
|
214
293
|
|
|
294
|
+
async #startSolo() {
|
|
295
|
+
const role = this.#getRole(0);
|
|
296
|
+
|
|
297
|
+
Object.assign(process.env, {
|
|
298
|
+
NODE_ENV: AkanApp.#defaultChildNodeEnv(),
|
|
299
|
+
AKAN_REPLICA: this.#replica.value,
|
|
300
|
+
AKAN_REPLICA_IDX: "0",
|
|
301
|
+
AKAN_APP_DIR: path.dirname(this.#serverPath),
|
|
302
|
+
SERVER_MODE: role,
|
|
303
|
+
...(this.#openapi === undefined ? {} : { AKAN_OPENAPI: this.#openapi ? "true" : "false" }),
|
|
304
|
+
...(this.#modules.length ? { AKAN_MODULES: this.#modules.join(",") } : {}),
|
|
305
|
+
});
|
|
306
|
+
this.logger.info(`Starting ${role} replica in this process (solo); set AKAN_SOLO=false for the gateway`);
|
|
307
|
+
const mod = (await import(this.#serverPath)) as { server?: SoloServer; app?: SoloServer };
|
|
308
|
+
const server = mod.server ?? mod.app;
|
|
309
|
+
if (!server?.start) throw new Error("server.ts must export server or app with start()");
|
|
310
|
+
await server.start({ listen: true });
|
|
311
|
+
}
|
|
312
|
+
|
|
215
313
|
async stop(signal = "SIGTERM") {
|
|
216
314
|
if (this.#stopping) return;
|
|
217
315
|
this.#stopping = true;
|
|
@@ -245,6 +343,7 @@ export class AkanApp {
|
|
|
245
343
|
for (const child of stragglers) child.proc.kill("SIGKILL");
|
|
246
344
|
await Promise.all(stragglers.map((child) => child.proc.exited.catch(() => undefined)));
|
|
247
345
|
this.#children.clear();
|
|
346
|
+
await this.#stopLogHub();
|
|
248
347
|
await this.#stopFileLogging();
|
|
249
348
|
this.#resolveStopped?.();
|
|
250
349
|
this.#resolveStopped = null;
|
|
@@ -289,6 +388,7 @@ export class AkanApp {
|
|
|
289
388
|
AKAN_CHILD_SOCKET: upstream.http.socketPath,
|
|
290
389
|
AKAN_CHILD_WS_PORT: upstream.ws ? String(upstream.ws.port) : "",
|
|
291
390
|
...(this.#openapi === undefined ? {} : { AKAN_OPENAPI: this.#openapi ? "true" : "false" }),
|
|
391
|
+
...(this.#modules.length ? { AKAN_MODULES: this.#modules.join(",") } : {}),
|
|
292
392
|
},
|
|
293
393
|
ipc: (message) => this.#handleMessage(idx, message as AkanIpcMessage, proc),
|
|
294
394
|
stdout: "pipe",
|
|
@@ -312,8 +412,12 @@ export class AkanApp {
|
|
|
312
412
|
lastRestartReason: previous?.lastRestartReason,
|
|
313
413
|
});
|
|
314
414
|
this.#invalidateFederationChildCache();
|
|
315
|
-
|
|
316
|
-
|
|
415
|
+
const output = new ChildOutputReader({
|
|
416
|
+
onLine: (line) => this.#writeChildLine(idx, role, proc.pid, "stdout", line),
|
|
417
|
+
onBlock: (lines) => this.#writeChildStderrBlock(idx, role, proc.pid, lines),
|
|
418
|
+
});
|
|
419
|
+
void output.pipe(proc.stdout, "stdout");
|
|
420
|
+
void output.pipe(proc.stderr, "stderr");
|
|
317
421
|
proc.exited.then((code) => this.#handleChildExit(idx, proc, code));
|
|
318
422
|
}
|
|
319
423
|
|
|
@@ -463,7 +567,7 @@ export class AkanApp {
|
|
|
463
567
|
const entries = await readdir(this.#runtimeDir).catch(() => []);
|
|
464
568
|
await Promise.all(
|
|
465
569
|
entries
|
|
466
|
-
.filter((name) => /^akan-child
|
|
570
|
+
.filter((name) => /^akan-(child-.*|control)\.sock$/.test(name))
|
|
467
571
|
.map((name) => rm(path.join(this.#runtimeDir, name), { force: true }).catch(() => undefined)),
|
|
468
572
|
);
|
|
469
573
|
}
|
|
@@ -479,6 +583,13 @@ export class AkanApp {
|
|
|
479
583
|
#startFileLogging() {
|
|
480
584
|
this.#logWriter = RotatingLogWriter.fromRuntimeDir(this.#runtimeDir);
|
|
481
585
|
if (!this.#logWriter) return;
|
|
586
|
+
if (this.#logHub && Logger.isNdjson) {
|
|
587
|
+
this.#hubFileSink = new HubFileSink(this.#logHub, this.#logWriter, {
|
|
588
|
+
minSev: logSeverity[Logger.fileLevel],
|
|
589
|
+
json: Logger.format === "ndjson-only",
|
|
590
|
+
});
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
482
593
|
this.#removeLogSink = Logger.addSink((entry) => {
|
|
483
594
|
this.#logWriter?.write("gateway", entry.plainMessage);
|
|
484
595
|
});
|
|
@@ -487,11 +598,38 @@ export class AkanApp {
|
|
|
487
598
|
async #stopFileLogging() {
|
|
488
599
|
this.#removeLogSink?.();
|
|
489
600
|
this.#removeLogSink = null;
|
|
601
|
+
this.#hubFileSink?.close();
|
|
602
|
+
this.#hubFileSink = null;
|
|
490
603
|
const writer = this.#logWriter;
|
|
491
604
|
this.#logWriter = null;
|
|
492
605
|
await writer?.close();
|
|
493
606
|
}
|
|
494
607
|
|
|
608
|
+
/** The journal every child forwards into, and the socket `akan logs` reads it from. Additive to the text file. */
|
|
609
|
+
async #startLogHub() {
|
|
610
|
+
const hub = LogHub.attach();
|
|
611
|
+
this.#logHub = hub;
|
|
612
|
+
hub.onFloorChange((minSev) => this.#fanoutToAll({ type: "log.level", minSev }));
|
|
613
|
+
this.#logStream = LogStreamRoute.fromEnv(() => this.#logHub);
|
|
614
|
+
const control = new LogControlSocket(hub, this.#runtimeDir);
|
|
615
|
+
try {
|
|
616
|
+
await control.start();
|
|
617
|
+
this.#logControl = control;
|
|
618
|
+
} catch (error) {
|
|
619
|
+
this.logger.warn(
|
|
620
|
+
`Log control socket unavailable at ${control.path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async #stopLogHub() {
|
|
626
|
+
await this.#logControl?.stop();
|
|
627
|
+
this.#logControl = null;
|
|
628
|
+
this.#logStream = null;
|
|
629
|
+
this.#logHub?.close();
|
|
630
|
+
this.#logHub = null;
|
|
631
|
+
}
|
|
632
|
+
|
|
495
633
|
#listen() {
|
|
496
634
|
this.#server = Bun.serve({
|
|
497
635
|
idleTimeout: 0,
|
|
@@ -515,11 +653,12 @@ export class AkanApp {
|
|
|
515
653
|
const url = new URL(req.url);
|
|
516
654
|
if (url.pathname === "/_akan/app/health") return Response.json(this.#getHealthStatus());
|
|
517
655
|
if (url.pathname === "/_akan/app/metrics") return Response.json(this.#getMetricsStatus());
|
|
656
|
+
if (url.pathname === LogStreamRoute.path && this.#logStream) return this.#logStream.handle(req);
|
|
518
657
|
if (url.pathname === "/_akan/bench/ping") return new Response("ok");
|
|
519
658
|
if (this.#isWebSocketPath(url.pathname)) return this.#upgradeWebSocket(req, server);
|
|
520
659
|
const assetResponse = await this.#serveImmutableArtifact(req, url);
|
|
521
660
|
if (assetResponse) return assetResponse;
|
|
522
|
-
return await this.#proxyHttp(req);
|
|
661
|
+
return await this.#proxyHttp(req, server);
|
|
523
662
|
}
|
|
524
663
|
|
|
525
664
|
#isWebSocketPath(pathname: string) {
|
|
@@ -527,6 +666,7 @@ export class AkanApp {
|
|
|
527
666
|
}
|
|
528
667
|
|
|
529
668
|
async #serveImmutableArtifact(req: Request, url: URL): Promise<Response | null> {
|
|
669
|
+
if (!this.#web.ssr) return null;
|
|
530
670
|
const clientPrefix = "/_akan/client/";
|
|
531
671
|
if (url.pathname.startsWith(clientPrefix)) {
|
|
532
672
|
const filePath = this.#safeResolve(
|
|
@@ -564,10 +704,10 @@ export class AkanApp {
|
|
|
564
704
|
if (!child || !upstream) return new Response("No websocket upstream is ready", { status: 503 });
|
|
565
705
|
const url = new URL(req.url);
|
|
566
706
|
const upstreamWs = new WebSocket(`ws://${upstream.host}:${upstream.port}${url.pathname}${url.search}`, {
|
|
567
|
-
headers: this.#makeProxyHeaders(req, child.idx),
|
|
707
|
+
headers: this.#makeProxyHeaders(req, child.idx, server),
|
|
568
708
|
} as unknown as string[]);
|
|
569
|
-
|
|
570
|
-
const upgraded = server.upgrade(req, { data: { childIdx: child.idx,
|
|
709
|
+
|
|
710
|
+
const upgraded = server.upgrade(req, { data: { childIdx: child.idx, upstream: upstreamWs } });
|
|
571
711
|
if (!upgraded) {
|
|
572
712
|
upstreamWs.close();
|
|
573
713
|
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
@@ -586,7 +726,9 @@ export class AkanApp {
|
|
|
586
726
|
const result = ws.send(event.data as string | ArrayBuffer);
|
|
587
727
|
if (result === 0) upstream.close();
|
|
588
728
|
});
|
|
589
|
-
upstream.addEventListener("close", (event) =>
|
|
729
|
+
upstream.addEventListener("close", (event) => {
|
|
730
|
+
ws.close(relayableCloseCode(event.code), event.reason);
|
|
731
|
+
});
|
|
590
732
|
upstream.addEventListener("error", () => ws.close(1011, "upstream websocket error"));
|
|
591
733
|
Object.assign(ws.data, { pending });
|
|
592
734
|
}
|
|
@@ -607,7 +749,7 @@ export class AkanApp {
|
|
|
607
749
|
}
|
|
608
750
|
|
|
609
751
|
#handleWsClose(ws: Bun.ServerWebSocket<GatewayWsData>, code: number, reason: string) {
|
|
610
|
-
ws.data.upstream.close(code, reason);
|
|
752
|
+
ws.data.upstream.close(relayableCloseCode(code), reason);
|
|
611
753
|
const child = this.#children.get(ws.data.childIdx);
|
|
612
754
|
if (child) child.metrics.activeWebSockets = Math.max(0, (child.metrics.activeWebSockets ?? 1) - 1);
|
|
613
755
|
}
|
|
@@ -661,14 +803,12 @@ export class AkanApp {
|
|
|
661
803
|
};
|
|
662
804
|
}
|
|
663
805
|
|
|
664
|
-
async #proxyHttp(req: Request): Promise<Response> {
|
|
665
|
-
const child = this.#
|
|
666
|
-
if (!child?.upstream || child.upstream.type !== "unix")
|
|
667
|
-
return this.#respondWithCrashPage(req) ?? new Response("No healthy federation child is ready", { status: 503 });
|
|
668
|
-
}
|
|
806
|
+
async #proxyHttp(req: Request, server: Bun.Server<GatewayWsData>): Promise<Response> {
|
|
807
|
+
const child = await this.#pickReadyFederationChild(req);
|
|
808
|
+
if (!child?.upstream || child.upstream.type !== "unix") return this.#respondWithUnavailable(req);
|
|
669
809
|
const url = new URL(req.url);
|
|
670
810
|
const upstreamUrl = `http://akan-child${url.pathname}${url.search}`;
|
|
671
|
-
const headers = this.#makeProxyHeaders(req, child.idx);
|
|
811
|
+
const headers = this.#makeProxyHeaders(req, child.idx, server);
|
|
672
812
|
child.metrics.activeRequests = (child.metrics.activeRequests ?? 0) + 1;
|
|
673
813
|
child.metrics.totalRequests = (child.metrics.totalRequests ?? 0) + 1;
|
|
674
814
|
const traced = isTraceEnabled();
|
|
@@ -682,14 +822,14 @@ export class AkanApp {
|
|
|
682
822
|
signal: req.signal,
|
|
683
823
|
redirect: "manual",
|
|
684
824
|
});
|
|
685
|
-
return this.#proxyResponse(upstreamRes);
|
|
825
|
+
return await this.#proxyResponse(req, upstreamRes);
|
|
686
826
|
} catch (error) {
|
|
687
827
|
if (AkanApp.#isUpstreamOpenFailure(error)) {
|
|
688
828
|
this.logger.error(
|
|
689
829
|
`Child ${child.idx}/${child.role} upstream is unreachable (${child.upstream.socketPath}); restarting`,
|
|
690
830
|
);
|
|
691
831
|
this.#scheduleChildRestart(child, child.proc, "upstream-open-failed");
|
|
692
|
-
return
|
|
832
|
+
return AkanApp.#unavailableResponse(req, "Federation child upstream is unreachable; restarting");
|
|
693
833
|
}
|
|
694
834
|
throw error;
|
|
695
835
|
} finally {
|
|
@@ -705,26 +845,82 @@ export class AkanApp {
|
|
|
705
845
|
}
|
|
706
846
|
|
|
707
847
|
/**
|
|
708
|
-
*
|
|
709
|
-
*
|
|
848
|
+
* A replica that is booting or restarting is normally back within a second or two, and a browser
|
|
849
|
+
* handed a 503 stays on that dead page until somebody reloads by hand — so a request waits for the
|
|
850
|
+
* upstream instead of failing at it. A dev crash loop is terminal, so it is answered immediately.
|
|
710
851
|
*/
|
|
711
|
-
#
|
|
852
|
+
async #pickReadyFederationChild(req: Request): Promise<ChildState | null> {
|
|
853
|
+
const ready = this.#pickFederationChild();
|
|
854
|
+
if (ready?.upstream) return ready;
|
|
855
|
+
const deadline = performance.now() + this.#upstreamWaitMs;
|
|
856
|
+
while (performance.now() < deadline) {
|
|
857
|
+
if (this.#stopping || req.signal.aborted || this.#getCrashLoopDetail()) return null;
|
|
858
|
+
await Bun.sleep(50);
|
|
859
|
+
const child = this.#pickFederationChild();
|
|
860
|
+
if (child?.upstream) return child;
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/** Dev-only terminal state: every traffic replica gave up booting. The detail is the boot error. */
|
|
866
|
+
#getCrashLoopDetail(): string | null {
|
|
712
867
|
if (!this.#devHosted) return null;
|
|
713
868
|
const trafficChildren = [...this.#children.values()].filter((child) => child.role !== "batch");
|
|
714
869
|
if (trafficChildren.length === 0) return null;
|
|
715
870
|
if (!trafficChildren.every((child) => child.status === "crashed")) return null;
|
|
716
|
-
|
|
871
|
+
return (
|
|
717
872
|
trafficChildren.map((child) => child.lastErrorMessage ?? child.lastRestartReason).find(Boolean) ??
|
|
718
|
-
"unknown boot error"
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
873
|
+
"unknown boot error"
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Both states answer 503, so an ingress or CDN reads them exactly as before; the HTML body is what
|
|
879
|
+
* a browser navigation needs, since nothing on a bare-text 503 can bring the page back on its own.
|
|
880
|
+
*/
|
|
881
|
+
#respondWithUnavailable(req: Request): Response {
|
|
882
|
+
const crashDetail = this.#getCrashLoopDetail();
|
|
883
|
+
const page = crashDetail
|
|
884
|
+
? {
|
|
885
|
+
heading: "Backend failed to start",
|
|
886
|
+
detail: crashDetail,
|
|
887
|
+
text: `Backend failed to start after ${AkanApp.#devMaxChildBootFailures} boot attempts: ${crashDetail}`,
|
|
888
|
+
note: `The dev server stopped retrying after ${AkanApp.#devMaxChildBootFailures} failed boots. Fix the error and save — this page reloads automatically.`,
|
|
889
|
+
}
|
|
890
|
+
: {
|
|
891
|
+
heading: "Backend is starting",
|
|
892
|
+
detail: "No healthy federation child is ready",
|
|
893
|
+
text: "No healthy federation child is ready",
|
|
894
|
+
note: "A replica is booting or restarting — this page reloads itself as soon as it answers.",
|
|
895
|
+
};
|
|
896
|
+
if (req.headers.get("accept")?.includes("text/html")) {
|
|
897
|
+
return new Response(AkanApp.#statusPageHtml(page), {
|
|
898
|
+
status: 503,
|
|
899
|
+
headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
|
|
900
|
+
});
|
|
722
901
|
}
|
|
723
|
-
|
|
902
|
+
return AkanApp.#unavailableResponse(req, page.text);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* An API caller asks for JSON, so it is answered with the payload `HttpClient` restores into an `Err` —
|
|
907
|
+
* a bare-text 503 reaches the browser as a JSON parse error naming the page body instead of the outage.
|
|
908
|
+
*/
|
|
909
|
+
static #unavailableResponse(req: Request, detail: string): Response {
|
|
910
|
+
const headers = { "cache-control": "no-store" };
|
|
911
|
+
if (!req.headers.get("accept")?.includes("application/json")) return new Response(detail, { status: 503, headers });
|
|
912
|
+
return Response.json(
|
|
913
|
+
{ error: "base.error.serverUnavailable", statusCode: 503, data: { status: 503 }, details: detail },
|
|
914
|
+
{ status: 503, headers },
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
static #statusPageHtml({ heading, detail, note }: { heading: string; detail: string; note: string }) {
|
|
919
|
+
return `<!doctype html>
|
|
724
920
|
<html>
|
|
725
921
|
<head>
|
|
726
922
|
<meta charset="utf-8" />
|
|
727
|
-
<title
|
|
923
|
+
<title>${AkanApp.#escapeHtml(heading)}</title>
|
|
728
924
|
<style>
|
|
729
925
|
body { margin: 0; padding: 48px 24px; background: #111827; color: #e5e7eb; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
730
926
|
main { max-width: 720px; margin: 0 auto; }
|
|
@@ -735,9 +931,9 @@ export class AkanApp {
|
|
|
735
931
|
</head>
|
|
736
932
|
<body>
|
|
737
933
|
<main>
|
|
738
|
-
<h1
|
|
934
|
+
<h1>${AkanApp.#escapeHtml(heading)}</h1>
|
|
739
935
|
<pre>${AkanApp.#escapeHtml(detail)}</pre>
|
|
740
|
-
<p
|
|
936
|
+
<p>${AkanApp.#escapeHtml(note)}</p>
|
|
741
937
|
</main>
|
|
742
938
|
<script>
|
|
743
939
|
const poll = async () => {
|
|
@@ -751,10 +947,6 @@ export class AkanApp {
|
|
|
751
947
|
</script>
|
|
752
948
|
</body>
|
|
753
949
|
</html>`;
|
|
754
|
-
return new Response(html, {
|
|
755
|
-
status: 503,
|
|
756
|
-
headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
|
|
757
|
-
});
|
|
758
950
|
}
|
|
759
951
|
|
|
760
952
|
static #escapeHtml(text: string) {
|
|
@@ -778,17 +970,23 @@ export class AkanApp {
|
|
|
778
970
|
this.#proxyHopMaxMs = Math.max(this.#proxyHopMaxMs, durationMs);
|
|
779
971
|
}
|
|
780
972
|
|
|
781
|
-
#proxyResponse(upstreamRes: Response): Response {
|
|
973
|
+
async #proxyResponse(req: Request, upstreamRes: Response): Promise<Response> {
|
|
782
974
|
const headers = new Headers(upstreamRes.headers);
|
|
783
975
|
|
|
784
976
|
headers.delete("content-encoding");
|
|
785
977
|
headers.delete("content-length");
|
|
786
978
|
this.#rewriteInternalLocation(headers);
|
|
787
|
-
|
|
979
|
+
const proxied = new Response(upstreamRes.body, {
|
|
788
980
|
status: upstreamRes.status,
|
|
789
981
|
statusText: upstreamRes.statusText,
|
|
790
982
|
headers,
|
|
791
983
|
});
|
|
984
|
+
|
|
985
|
+
return AkanApp.#isProxiedJson(headers) ? await compressResponse(req, proxied) : proxied;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
static #isProxiedJson(headers: Headers): boolean {
|
|
989
|
+
return (headers.get("content-type") ?? "").split(";")[0]?.trim().toLowerCase() === "application/json";
|
|
792
990
|
}
|
|
793
991
|
|
|
794
992
|
#rewriteInternalLocation(headers: Headers) {
|
|
@@ -837,8 +1035,8 @@ export class AkanApp {
|
|
|
837
1035
|
return resolved;
|
|
838
1036
|
}
|
|
839
1037
|
|
|
840
|
-
#makeProxyHeaders(req: Request, childIdx: number) {
|
|
841
|
-
return makeAkanChildProxyHeaders(req, childIdx);
|
|
1038
|
+
#makeProxyHeaders(req: Request, childIdx: number, server?: Bun.Server<GatewayWsData>) {
|
|
1039
|
+
return makeAkanChildProxyHeaders(req, childIdx, server?.requestIP(req));
|
|
842
1040
|
}
|
|
843
1041
|
|
|
844
1042
|
#invalidateFederationChildCache() {
|
|
@@ -902,6 +1100,10 @@ export class AkanApp {
|
|
|
902
1100
|
case "metrics.report":
|
|
903
1101
|
this.#updateMetrics(idx, message.metrics);
|
|
904
1102
|
return;
|
|
1103
|
+
case "log.records":
|
|
1104
|
+
this.#logHub?.ingestMany(message.records);
|
|
1105
|
+
if (message.dropped) this.logger.warn(`Child ${idx} dropped ${message.dropped} log records (ipc backpressure)`);
|
|
1106
|
+
return;
|
|
905
1107
|
case "health.pong":
|
|
906
1108
|
this.#markHealthy(idx);
|
|
907
1109
|
return;
|
|
@@ -948,6 +1150,9 @@ export class AkanApp {
|
|
|
948
1150
|
child.healthPath = message.healthPath;
|
|
949
1151
|
child.lastPongAtMono = performance.now();
|
|
950
1152
|
child.restartAttempts = 0;
|
|
1153
|
+
|
|
1154
|
+
if (this.#logHub && this.#logHub.floor !== null)
|
|
1155
|
+
this.#sendToChild(child, { type: "log.level", minSev: this.#logHub.floor });
|
|
951
1156
|
child.restartPending = false;
|
|
952
1157
|
child.lastErrorMessage = undefined;
|
|
953
1158
|
this.#invalidateFederationChildCache();
|
|
@@ -1140,6 +1345,10 @@ export class AkanApp {
|
|
|
1140
1345
|
}
|
|
1141
1346
|
}
|
|
1142
1347
|
|
|
1348
|
+
#fanoutToAll(message: AkanIpcMessage) {
|
|
1349
|
+
for (const child of this.#children.values()) this.#sendToChild(child, message);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1143
1352
|
#fanoutToBatch(message: AkanIpcMessage) {
|
|
1144
1353
|
for (const child of this.#children.values()) {
|
|
1145
1354
|
if (child.role === "batch" || child.role === "all") {
|
|
@@ -1165,99 +1374,30 @@ export class AkanApp {
|
|
|
1165
1374
|
}
|
|
1166
1375
|
}
|
|
1167
1376
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
type: "stdout" | "stderr",
|
|
1173
|
-
) {
|
|
1174
|
-
if (!stream) return;
|
|
1175
|
-
const bufferKey = `${idx}:${type}`;
|
|
1176
|
-
const reader = stream.getReader();
|
|
1177
|
-
const decoder = new TextDecoder();
|
|
1178
|
-
try {
|
|
1179
|
-
for (;;) {
|
|
1180
|
-
const { done, value } = await reader.read();
|
|
1181
|
-
if (done) break;
|
|
1182
|
-
const text = decoder.decode(value, { stream: true });
|
|
1183
|
-
this.#writeChildOutput(idx, role, type, bufferKey, text);
|
|
1184
|
-
}
|
|
1185
|
-
const remaining = decoder.decode();
|
|
1186
|
-
if (remaining) this.#writeChildOutput(idx, role, type, bufferKey, remaining);
|
|
1187
|
-
} finally {
|
|
1188
|
-
this.#flushChildOutput(idx, role, type, bufferKey);
|
|
1189
|
-
if (type === "stderr") this.#flushChildStderrBlock(idx, role, AkanApp.#childStderrBlockKey(idx, role));
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
#writeChildOutput(idx: number, role: AkanChildRole, type: "stdout" | "stderr", bufferKey: string, text: string) {
|
|
1194
|
-
let buffered = `${this.#childOutputBuffers.get(bufferKey) ?? ""}${text}`;
|
|
1195
|
-
for (;;) {
|
|
1196
|
-
const newlineIdx = buffered.indexOf("\n");
|
|
1197
|
-
if (newlineIdx === -1) break;
|
|
1198
|
-
const line = buffered.slice(0, newlineIdx + 1);
|
|
1199
|
-
buffered = buffered.slice(newlineIdx + 1);
|
|
1200
|
-
this.#writeChildOutputLine(idx, role, type, line);
|
|
1377
|
+
#writeChildLine(idx: number, role: AkanChildRole, pid: number | null, type: "stdout" | "stderr", line: string) {
|
|
1378
|
+
if (Logger.isNdjson) {
|
|
1379
|
+
this.#logHub?.ingest(ChildOutputReader.toRecord({ type, text: line, name: "child", role, replicaIdx: idx, pid }));
|
|
1380
|
+
return;
|
|
1201
1381
|
}
|
|
1202
|
-
if (buffered) this.#childOutputBuffers.set(bufferKey, buffered);
|
|
1203
|
-
else this.#childOutputBuffers.delete(bufferKey);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
#flushChildOutput(idx: number, role: AkanChildRole, type: "stdout" | "stderr", bufferKey: string) {
|
|
1207
|
-
const buffered = this.#childOutputBuffers.get(bufferKey);
|
|
1208
|
-
if (!buffered) return;
|
|
1209
|
-
this.#childOutputBuffers.delete(bufferKey);
|
|
1210
|
-
this.#writeChildOutputLine(idx, role, type, `${buffered}\n`);
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
#writeChildOutputLine(idx: number, role: AkanChildRole, type: "stdout" | "stderr", line: string) {
|
|
1214
|
-
if (type === "stderr" && this.#bufferChildStderrLine(idx, role, line)) return;
|
|
1215
|
-
this.#writeChildOutputLineRaw(idx, role, type, line);
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
#writeChildOutputLineRaw(idx: number, role: AkanChildRole, type: "stdout" | "stderr", line: string) {
|
|
1219
1382
|
const prefixedLine = `[child:${idx} ${role}] [${type}] ${line}`;
|
|
1220
|
-
process[type].write(prefixedLine);
|
|
1383
|
+
if (type === "stdout" || this.#printChildStderr) process[type].write(prefixedLine);
|
|
1221
1384
|
this.#logWriter?.write(`${idx}-${role}`, AkanApp.#stripAnsi(prefixedLine));
|
|
1222
1385
|
}
|
|
1223
1386
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
const existingTimer = this.#childStderrBlockTimers.get(key);
|
|
1231
|
-
if (existingTimer) clearTimeout(existingTimer);
|
|
1232
|
-
|
|
1233
|
-
if (line.trim() === "" || block.length >= 64) {
|
|
1234
|
-
this.#flushChildStderrBlock(idx, role, key);
|
|
1235
|
-
return true;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
this.#childStderrBlockTimers.set(
|
|
1239
|
-
key,
|
|
1240
|
-
setTimeout(() => this.#flushChildStderrBlock(idx, role, key), 50),
|
|
1241
|
-
);
|
|
1242
|
-
return true;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
#flushChildStderrBlock(idx: number, role: AkanChildRole, key: string) {
|
|
1246
|
-
const timer = this.#childStderrBlockTimers.get(key);
|
|
1247
|
-
if (timer) clearTimeout(timer);
|
|
1248
|
-
this.#childStderrBlockTimers.delete(key);
|
|
1249
|
-
|
|
1250
|
-
const block = this.#childStderrBlockBuffers.get(key);
|
|
1251
|
-
if (!block?.length) return;
|
|
1252
|
-
this.#childStderrBlockBuffers.delete(key);
|
|
1253
|
-
|
|
1254
|
-
const text = block.join("");
|
|
1387
|
+
/**
|
|
1388
|
+
* In ndjson mode a stack trace is one record, and `AKAN_CHILD_STDERR` does not apply: the line that killed a
|
|
1389
|
+
* child is the one the collector must not miss.
|
|
1390
|
+
*/
|
|
1391
|
+
#writeChildStderrBlock(idx: number, role: AkanChildRole, pid: number | null, lines: string[]) {
|
|
1392
|
+
const text = lines.join("");
|
|
1255
1393
|
if (AkanApp.#isBenignRsdwConnectionClosedBlock(text)) return;
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1394
|
+
if (Logger.isNdjson) {
|
|
1395
|
+
this.#logHub?.ingest(
|
|
1396
|
+
ChildOutputReader.toRecord({ type: "stderr", text, name: "child", role, replicaIdx: idx, pid }),
|
|
1397
|
+
);
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
for (const line of lines) this.#writeChildLine(idx, role, pid, "stderr", line);
|
|
1261
1401
|
}
|
|
1262
1402
|
|
|
1263
1403
|
static #isBenignRsdwConnectionClosedBlock(text: string): boolean {
|