akanjs 3.1.0-rc.2 → 3.1.0-rc.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/base/baseEnv.ts +5 -2
- package/client/device.ts +1 -1
- package/client/frameConfig.ts +2 -2
- package/client/href.ts +35 -0
- package/client/index.ts +1 -0
- package/client/makePageProto.tsx +3 -2
- package/client/router.ts +6 -4
- package/client/rscNavigation.ts +1 -1
- package/client/transitionTypes.ts +0 -4
- package/client/translator.ts +16 -0
- package/client/types.ts +3 -2
- package/client/viewportMeta.ts +23 -8
- package/client/webManifest.ts +5 -3
- package/common/TrustedProxy.ts +14 -0
- package/common/codeAgentWire.ts +8 -1
- package/common/index.ts +1 -0
- package/common/interpolateTranslation.ts +17 -3
- package/common/isEmail.ts +1 -1
- package/common/localeConfig.ts +10 -4
- package/common/markdownSpans.ts +1 -1
- package/common/restClient.ts +3 -1
- package/common/routeConvention.ts +4 -4
- package/common/trimSlashes.ts +11 -0
- package/common/types.ts +1 -1
- package/constant/fieldInfo.ts +2 -2
- package/constant/fieldPlan.ts +80 -0
- package/constant/index.ts +3 -0
- package/constant/inputParser.ts +148 -0
- package/constant/invalidInputError.ts +13 -0
- package/constant/serialize.ts +36 -20
- package/constant/types.ts +1 -1
- package/constant/via.ts +2 -0
- package/dictionary/base.dictionary.ts +4 -0
- package/dictionary/trans.ts +8 -0
- package/document/database.ts +3 -6
- package/document/documentSchema.ts +1 -1
- package/document/noDocumentError.ts +6 -1
- package/fetch/client/fetchClient.ts +2 -1
- package/fetch/client/sliceInitHandle.ts +2 -3
- package/fetch/fetchType/appliedReturn.type.ts +3 -1
- package/fetch/types.ts +4 -2
- package/index.ts +2 -0
- package/package.json +31 -21
- package/server/akanApp.ts +15 -3
- package/server/akanServer.ts +34 -14
- package/server/artifact/routeClientCache.ts +4 -5
- package/server/artifact/routeSeedIndexStore.ts +3 -20
- package/server/artifact/routeSeedMatcher.ts +70 -0
- package/server/console.ts +1 -1
- package/server/di/diLifecycle.ts +16 -7
- package/server/imageOptimizer.ts +1 -1
- package/server/index.ts +1 -0
- package/server/machine/machineAccount.ts +28 -3
- package/server/machine/machineToken.ts +27 -3
- package/server/mcp/McpDispatcher.ts +2 -3
- package/server/mcp/McpExecutionContext.ts +9 -3
- package/server/migration/docPath.ts +121 -0
- package/server/migration/index.ts +15 -0
- package/server/migration/migrationChangeLog.ts +86 -0
- package/server/migration/migrationChanges.ts +70 -0
- package/server/migration/migrationCommand.ts +179 -0
- package/server/migration/migrationContext.ts +53 -0
- package/server/migration/migrationDefinition.ts +87 -0
- package/server/migration/migrationError.ts +45 -0
- package/server/migration/migrationLedger.ts +172 -0
- package/server/migration/migrationLock.ts +103 -0
- package/server/migration/migrationParams.ts +122 -0
- package/server/migration/migrationPulse.ts +39 -0
- package/server/migration/migrationRunner.ts +525 -0
- package/server/migration/migrationScope.ts +93 -0
- package/server/migration/migrationTable.ts +258 -0
- package/server/migration/migrations.ts +57 -0
- package/server/ops/opsCommand.ts +30 -1
- package/server/resolver/CascadeRunner.ts +15 -2
- package/server/resolver/signal.resolver.ts +35 -55
- package/server/routeState.ts +0 -6
- package/server/rscClient.tsx +22 -37
- package/server/rscNavigationState.ts +3 -5
- package/server/rscScrollRestoration.ts +116 -0
- package/server/rscWorker.tsx +12 -19
- package/server/sitemap.ts +4 -4
- package/server/tunnel/TunnelAgent.ts +9 -1
- package/server/webRouter.ts +17 -2
- package/service/agent.service.ts +21 -1
- package/service/endpointCacheKeys.ts +35 -0
- package/service/index.ts +1 -0
- package/service/injectInfo.ts +2 -2
- package/service/predefinedAdaptor/anthropicLlm.ts +37 -17
- package/service/predefinedAdaptor/database.adaptor.ts +15 -0
- package/service/predefinedAdaptor/documentTransfer.ts +50 -0
- package/service/predefinedAdaptor/endpointCacheControl.adaptor.ts +19 -0
- package/service/predefinedAdaptor/index.ts +1 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +7 -0
- package/service/predefinedAdaptor/openaiDialect.ts +53 -17
- package/service/predefinedAdaptor/openaiLlm.ts +1 -1
- package/service/predefinedAdaptor/sql/MigrationTables.ts +90 -0
- package/service/predefinedAdaptor/sql/RawDocumentTable.ts +200 -0
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +6 -2
- package/service/predefinedAdaptor/sql/SqliteInsightSession.ts +4 -1
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +0 -1
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +0 -1
- package/service/predefinedAdaptor/sql/types.ts +3 -1
- package/service/predefinedAdaptor/storage.adaptor.ts +19 -0
- package/signal/endpointCache.ts +23 -7
- package/signal/endpointErrorContract.ts +37 -0
- package/signal/endpointIdempotency.ts +5 -3
- package/signal/endpointInfo.ts +4 -1
- package/signal/endpointRateLimit.ts +6 -2
- package/signal/exception.ts +4 -1
- package/signal/index.ts +1 -0
- package/signal/mcp/McpDocument.ts +16 -1
- package/signal/mcp/McpUriTemplate.ts +1 -1
- package/signal/middlewareChain.ts +105 -0
- package/signal/openapi/openapi.ts +46 -2
- package/signal/serializer/fetch.serializer.ts +1 -0
- package/signal/signalContext.ts +110 -82
- package/signal/slice.ts +2 -2
- package/signal/trace.ts +8 -2
- package/signal/types.ts +13 -0
- package/store/action.ts +102 -42
- package/store/agent/ScreenImage.ts +166 -0
- package/store/agent/ScreenReader.ts +2 -1
- package/store/agent/StoreSurfaceSource.ts +41 -2
- package/store/agent/index.ts +2 -1
- package/store/databaseStateNames.ts +1 -0
- package/store/formUploadOf.ts +41 -0
- package/store/index.ts +1 -0
- package/store/liveInsert.ts +2 -2
- package/store/state.ts +9 -1
- package/store/stateBuilder.ts +8 -2
- package/store/storeInstance.ts +12 -3
- package/store/withSelector.ts +2 -2
- package/types/client/href.d.ts +11 -0
- package/types/client/index.d.ts +1 -0
- package/types/client/rscNavigation.d.ts +1 -1
- package/types/client/transitionTypes.d.ts +0 -2
- package/types/client/translator.d.ts +2 -0
- package/types/common/codeAgentWire.d.ts +7 -0
- package/types/common/index.d.ts +1 -0
- package/types/common/trimSlashes.d.ts +2 -0
- package/types/common/types.d.ts +1 -1
- package/types/constant/fieldInfo.d.ts +2 -2
- package/types/constant/fieldPlan.d.ts +33 -0
- package/types/constant/index.d.ts +3 -0
- package/types/constant/inputParser.d.ts +11 -0
- package/types/constant/invalidInputError.d.ts +8 -0
- package/types/constant/types.d.ts +1 -1
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +3 -3
- package/types/dictionary/trans.d.ts +3 -0
- package/types/document/database.d.ts +2 -3
- package/types/document/documentSchema.d.ts +1 -1
- package/types/document/noDocumentError.d.ts +6 -1
- package/types/fetch/fetchType/appliedReturn.type.d.ts +3 -1
- package/types/fetch/types.d.ts +2 -2
- package/types/index.d.ts +5 -0
- package/types/server/akanServer.d.ts +1 -1
- package/types/server/artifact/routeSeedMatcher.d.ts +8 -0
- package/types/server/di/diLifecycle.d.ts +1 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/machineAccount.d.ts +1 -0
- package/types/server/machine/machineToken.d.ts +9 -0
- package/types/server/migration/docPath.d.ts +20 -0
- package/types/server/migration/index.d.ts +15 -0
- package/types/server/migration/migrationChangeLog.d.ts +27 -0
- package/types/server/migration/migrationChanges.d.ts +27 -0
- package/types/server/migration/migrationCommand.d.ts +33 -0
- package/types/server/migration/migrationContext.d.ts +28 -0
- package/types/server/migration/migrationDefinition.d.ts +42 -0
- package/types/server/migration/migrationError.d.ts +15 -0
- package/types/server/migration/migrationLedger.d.ts +51 -0
- package/types/server/migration/migrationLock.d.ts +34 -0
- package/types/server/migration/migrationParams.d.ts +41 -0
- package/types/server/migration/migrationPulse.d.ts +15 -0
- package/types/server/migration/migrationRunner.d.ts +93 -0
- package/types/server/migration/migrationScope.d.ts +42 -0
- package/types/server/migration/migrationTable.d.ts +52 -0
- package/types/server/migration/migrations.d.ts +12 -0
- package/types/server/migration.d.ts +1 -0
- package/types/server/ops/opsCommand.d.ts +10 -1
- package/types/server/routeState.d.ts +1 -4
- package/types/server/rscNavigationState.d.ts +2 -3
- package/types/server/rscScrollRestoration.d.ts +14 -0
- package/types/server/sitemap.d.ts +1 -1
- package/types/service/agent.service.d.ts +2 -0
- package/types/service/endpointCacheKeys.d.ts +8 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/injectInfo.d.ts +2 -2
- package/types/service/predefinedAdaptor/anthropicLlm.d.ts +9 -4
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +8 -1
- package/types/service/predefinedAdaptor/documentTransfer.d.ts +1 -0
- package/types/service/predefinedAdaptor/endpointCacheControl.adaptor.d.ts +9 -0
- package/types/service/predefinedAdaptor/index.d.ts +1 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +7 -0
- package/types/service/predefinedAdaptor/openaiDialect.d.ts +20 -5
- package/types/service/predefinedAdaptor/sql/MigrationTables.d.ts +17 -0
- package/types/service/predefinedAdaptor/sql/RawDocumentTable.d.ts +38 -0
- package/types/service/predefinedAdaptor/sql/types.d.ts +1 -0
- package/types/service/predefinedAdaptor/storage.adaptor.d.ts +8 -0
- package/types/signal/endpointCache.d.ts +2 -0
- package/types/signal/endpointErrorContract.d.ts +10 -0
- package/types/signal/endpointIdempotency.d.ts +1 -0
- package/types/signal/endpointRateLimit.d.ts +1 -1
- package/types/signal/exception.d.ts +4 -1
- package/types/signal/index.d.ts +1 -0
- package/types/signal/middlewareChain.d.ts +11 -0
- package/types/signal/signalContext.d.ts +2 -6
- package/types/signal/slice.d.ts +1 -1
- package/types/signal/trace.d.ts +1 -1
- package/types/signal/types.d.ts +12 -0
- package/types/store/action.d.ts +2 -0
- package/types/store/agent/ScreenImage.d.ts +27 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +1 -1
- package/types/store/agent/index.d.ts +2 -1
- package/types/store/databaseStateNames.d.ts +1 -0
- package/types/store/formUploadOf.d.ts +9 -0
- package/types/store/index.d.ts +1 -0
- package/types/store/state.d.ts +7 -1
- package/types/store/stateBuilder.d.ts +2 -1
- package/types/store/storeInstance.d.ts +2 -2
- package/types/store/withSelector.d.ts +1 -1
- package/types/ui/Agent/Composer.d.ts +2 -0
- package/types/ui/Agent/Menu.d.ts +2 -0
- package/types/ui/CsrImage.d.ts +2 -0
- package/types/ui/Data/CardList.d.ts +1 -1
- package/types/ui/Data/Dashboard.d.ts +1 -1
- package/types/ui/Data/Insight.d.ts +1 -1
- package/types/ui/Data/Item.d.ts +2 -2
- package/types/ui/Data/ListContainer.d.ts +1 -1
- package/types/ui/Data/Pagination.d.ts +1 -1
- package/types/ui/Data/TableList.d.ts +1 -1
- package/types/ui/Field/Relation.d.ts +13 -10
- package/types/ui/Field/index.d.ts +4 -4
- package/types/ui/Field/useRelationOptions.d.ts +3 -1
- package/types/ui/Field/useRelationWindow.d.ts +14 -0
- package/types/ui/Image.d.ts +3 -1
- package/types/ui/Load/View.d.ts +1 -1
- package/types/ui/Pagination.d.ts +1 -1
- package/types/ui/Signal/Arg.d.ts +1 -1
- package/types/ui/System/CSR.d.ts +1 -1
- package/types/ui/System/Client.d.ts +1 -1
- package/types/ui/System/SSR.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/core/src/plugin.d.ts +3 -3
- package/types/vendor/@akanjs/native/packages/desktop/src/page-veto.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/auth-session/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/auth-session/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/autostart/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/autostart/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/badge/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/biometric/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/camera/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/clipboard/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/clipboard/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/clipboard/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/contacts/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/device/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/device/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/device/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dialog/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dock/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/dock/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/file-picker/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/file-picker/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/filesystem/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/filesystem/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/haptics/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/haptics/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/http/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/http/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/keep-awake/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/opener/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/opener/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/opener/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/desktop.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/preferences/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/secure-storage/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/secure-storage/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/share/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/share/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/single-instance/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/splash-screen/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/splash-screen/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/sqlite/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/toast/src/index.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/toast/src/web.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/window-state/src/desktop.d.ts +2 -2
- package/types/vendor/@akanjs/native/plugins/window-state/src/index.d.ts +1 -1
- package/types/vendor/use-agentic/Compaction.d.ts +3 -1
- package/types/vendor/use-agentic/Reference.d.ts +9 -1
- package/types/vendor/use-agentic/ToolOutput.d.ts +6 -1
- package/types/vendor/use-agentic/ToolReturn.d.ts +18 -0
- package/types/vendor/use-agentic/Transcript.d.ts +7 -0
- package/types/vendor/use-agentic/index.d.ts +1 -0
- package/types/vendor/use-agentic/types.d.ts +6 -0
- package/types/webkit/useDebounce.d.ts +1 -1
- package/ui/Agent/Chat.tsx +1 -0
- package/ui/Agent/Composer.tsx +2 -0
- package/ui/Agent/Menu.tsx +5 -1
- package/ui/Agent/agentSessionOf.ts +15 -8
- package/ui/Agent/mentionDraft.ts +2 -3
- package/ui/Agent/useReferenceMenu.ts +1 -0
- package/ui/Agent/useSlashMenu.ts +1 -0
- package/ui/Clipboard.tsx +1 -0
- package/ui/CsrImage.tsx +3 -1
- package/ui/Data/CardList.tsx +1 -1
- package/ui/Data/Dashboard.tsx +1 -1
- package/ui/Data/Insight.tsx +1 -1
- package/ui/Data/Item.tsx +5 -2
- package/ui/Data/ListContainer.tsx +2 -2
- package/ui/Data/Pagination.tsx +1 -1
- package/ui/Data/TableList.tsx +1 -1
- package/ui/DraggableList.tsx +1 -0
- package/ui/Field/Collection.tsx +4 -0
- package/ui/Field/Relation.tsx +33 -14
- package/ui/Field/useRelationOptions.ts +33 -5
- package/ui/Field/useRelationWindow.ts +50 -0
- package/ui/Image.tsx +4 -4
- package/ui/Input.tsx +1 -0
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Link/Anchor.tsx +7 -5
- package/ui/Load/Units.tsx +17 -7
- package/ui/Load/View.tsx +2 -2
- package/ui/Model/Edit.tsx +1 -1
- package/ui/Model/EditModal.tsx +11 -5
- package/ui/Model/New.tsx +1 -1
- package/ui/Model/SureToRemove.tsx +1 -0
- package/ui/Model/ViewEditModal.tsx +17 -5
- package/ui/Model/ViewModal.tsx +2 -2
- package/ui/ObjectId.tsx +5 -1
- package/ui/Pagination.tsx +1 -1
- package/ui/Signal/Arg.tsx +1 -3
- package/ui/Signal/Doc.tsx +5 -1
- package/ui/System/CSR.tsx +1 -1
- package/ui/System/Client.tsx +1 -1
- package/ui/System/Reconnect.tsx +1 -1
- package/ui/System/SSR.tsx +3 -36
- package/ui/ToggleSelect.tsx +2 -0
- package/ui/styles.css +10 -103
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeBridge.swift +3 -2
- package/vendor/@akanjs/native/packages/cli/src/lib/addons.ts +4 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/apk.ts +2 -2
- package/vendor/@akanjs/native/packages/cli/src/lib/codegen.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/csp.ts +4 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/googleservices.ts +7 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/hmr.ts +8 -5
- package/vendor/@akanjs/native/packages/cli/src/lib/html.ts +18 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/manifestmerge.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/plist.ts +2 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/toolchains.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +10 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/xml.ts +4 -3
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +3 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +3 -3
- package/vendor/@akanjs/native/packages/core/src/live.ts +1 -0
- package/vendor/@akanjs/native/packages/core/src/plugin.ts +5 -3
- package/vendor/@akanjs/native/packages/desktop/src/host.ts +13 -5
- package/vendor/@akanjs/native/packages/desktop/src/lifecycle.ts +1 -0
- package/vendor/@akanjs/native/packages/desktop/src/page-veto.ts +1 -1
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +4 -1
- package/vendor/@akanjs/native/plugins/camera/src/web.ts +3 -3
- package/vendor/@akanjs/native/plugins/file-picker/src/index.ts +4 -1
- package/vendor/@akanjs/native/plugins/local-notifications/src/web.ts +1 -1
- package/vendor/@akanjs/native/plugins/secure-storage/src/desktop.ts +1 -1
- package/vendor/@akanjs/native/plugins/share/src/options.ts +1 -1
- package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +3 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +4 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +2 -2
- package/vendor/use-agentic/Compaction.ts +38 -7
- package/vendor/use-agentic/Reference.ts +16 -9
- package/vendor/use-agentic/ToolOutput.ts +25 -3
- package/vendor/use-agentic/ToolReturn.ts +32 -0
- package/vendor/use-agentic/ToolRunner.ts +15 -13
- package/vendor/use-agentic/Transcript.ts +38 -2
- package/vendor/use-agentic/WIRE.md +20 -0
- package/vendor/use-agentic/index.ts +1 -0
- package/vendor/use-agentic/types.ts +6 -0
- package/webkit/McpAppAgent.ts +12 -2
- package/webkit/RenderLayer.tsx +23 -4
- package/webkit/useDebounce.ts +23 -14
- package/client/viewTransition.ts +0 -10
- package/types/client/viewTransition.d.ts +0 -3
package/server/akanServer.ts
CHANGED
|
@@ -168,6 +168,8 @@ export class AkanServer {
|
|
|
168
168
|
shutdownTimeoutMs = AkanServer.#defaultShutdownTimeoutMs();
|
|
169
169
|
|
|
170
170
|
#di: DiLifecycle;
|
|
171
|
+
#openApiDocument: ReturnType<typeof createOpenApiDocument> | null = null;
|
|
172
|
+
readonly #initTimings = new Map<string, number>();
|
|
171
173
|
#localPublish: LocalPublish | null = null;
|
|
172
174
|
readonly #binaryPubsub = new BinaryPubsub();
|
|
173
175
|
#metricsTimer: Timer | null = null;
|
|
@@ -324,7 +326,16 @@ export class AkanServer {
|
|
|
324
326
|
};
|
|
325
327
|
}
|
|
326
328
|
|
|
327
|
-
async init(
|
|
329
|
+
async init(option: { routes?: boolean; web?: AkanWebOption } = {}) {
|
|
330
|
+
const start = performance.now();
|
|
331
|
+
this.#initTimings.clear();
|
|
332
|
+
await this.#init(option);
|
|
333
|
+
const stages = [...this.#di.initTimings, ...this.#initTimings].map(([stage, ms]) => `${stage} ${ms.toFixed(1)}`);
|
|
334
|
+
this.logger.debug(`Initialized in ${(performance.now() - start).toFixed(1)}ms (${stages.join(" · ")})`);
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async #init({ routes: initRoutes = true, web }: { routes?: boolean; web?: AkanWebOption }) {
|
|
328
339
|
if (this.status !== "stopped") throw new Error("AkanServer is not able to init. It is already running.");
|
|
329
340
|
this.status = "initializing";
|
|
330
341
|
this.#assertPrefixClearsBasePaths();
|
|
@@ -340,7 +351,7 @@ export class AkanServer {
|
|
|
340
351
|
routes,
|
|
341
352
|
routeOptions,
|
|
342
353
|
wsRoutes,
|
|
343
|
-
builtinRoutes: this.#
|
|
354
|
+
builtinRoutes: this.#timedBuiltinRoutes(null),
|
|
344
355
|
renderEnvRoutes: {},
|
|
345
356
|
hmrHub: null,
|
|
346
357
|
webRouter: null,
|
|
@@ -354,6 +365,7 @@ export class AkanServer {
|
|
|
354
365
|
this.logger.debug("web off: serving api only (AKAN_SSR=false, or a build with `web: false`)");
|
|
355
366
|
return noWeb();
|
|
356
367
|
}
|
|
368
|
+
const webStart = performance.now();
|
|
357
369
|
const { WebRouter } = await import("./webRouter");
|
|
358
370
|
const webRouter = await WebRouter.create({
|
|
359
371
|
web: requestedWeb,
|
|
@@ -368,12 +380,13 @@ export class AkanServer {
|
|
|
368
380
|
this.web = webRouter.web;
|
|
369
381
|
this.logger.verbose(`web on: ssr=${this.web.ssr} csr=${this.web.csr}`);
|
|
370
382
|
const { renderEnvRoutes, hmrHub } = await webRouter.initializeRoute();
|
|
383
|
+
this.#initTimings.set("web", performance.now() - webStart);
|
|
371
384
|
const webProxyRunner = WebProxyRunner.create(this.#di.webProxies);
|
|
372
385
|
this.#prepared = {
|
|
373
386
|
routes,
|
|
374
387
|
routeOptions,
|
|
375
388
|
wsRoutes,
|
|
376
|
-
builtinRoutes: this.#
|
|
389
|
+
builtinRoutes: this.#timedBuiltinRoutes(webRouter),
|
|
377
390
|
renderEnvRoutes,
|
|
378
391
|
hmrHub,
|
|
379
392
|
webRouter,
|
|
@@ -666,21 +679,18 @@ export class AkanServer {
|
|
|
666
679
|
}
|
|
667
680
|
}
|
|
668
681
|
|
|
682
|
+
#timedBuiltinRoutes(webRouter: WebRouter | null): HttpRoutes {
|
|
683
|
+
const start = performance.now();
|
|
684
|
+
const builtinRoutes = this.#createBuiltinRoutes(webRouter);
|
|
685
|
+
this.#initTimings.set("builtinRoutes", performance.now() - start);
|
|
686
|
+
return builtinRoutes;
|
|
687
|
+
}
|
|
688
|
+
|
|
669
689
|
#createBuiltinRoutes(webRouter: WebRouter | null): HttpRoutes {
|
|
670
690
|
const { appName } = getEnv();
|
|
671
691
|
const openapiRoutes: HttpRoutes = this.openapi
|
|
672
692
|
? {
|
|
673
|
-
"/openapi.json": {
|
|
674
|
-
GET: () =>
|
|
675
|
-
Response.json(
|
|
676
|
-
createOpenApiDocument(FetchSerializer.serializeRegistry(this.#di.live).signal, {
|
|
677
|
-
title: `${appName} API`,
|
|
678
|
-
version: "0.0.0",
|
|
679
|
-
servers: this.#getOpenApiServers(),
|
|
680
|
-
resolveDescription: AkanServer.#createDescriptionResolver(),
|
|
681
|
-
}),
|
|
682
|
-
),
|
|
683
|
-
},
|
|
693
|
+
"/openapi.json": { GET: () => Response.json(this.#getOpenApiDocument(appName)) },
|
|
684
694
|
}
|
|
685
695
|
: {};
|
|
686
696
|
|
|
@@ -802,6 +812,16 @@ export class AkanServer {
|
|
|
802
812
|
this.#logForwarder = null;
|
|
803
813
|
}
|
|
804
814
|
|
|
815
|
+
#getOpenApiDocument(appName: string) {
|
|
816
|
+
this.#openApiDocument ??= createOpenApiDocument(FetchSerializer.serializeRegistry(this.#di.live).signal, {
|
|
817
|
+
title: `${appName} API`,
|
|
818
|
+
version: "0.0.0",
|
|
819
|
+
servers: this.#getOpenApiServers(),
|
|
820
|
+
resolveDescription: AkanServer.#createDescriptionResolver(),
|
|
821
|
+
});
|
|
822
|
+
return this.#openApiDocument;
|
|
823
|
+
}
|
|
824
|
+
|
|
805
825
|
static #createDescriptionResolver() {
|
|
806
826
|
const lookup = new DictionaryLookup();
|
|
807
827
|
return (key: string) => lookup.text(key);
|
|
@@ -105,13 +105,12 @@ export class RouteClientCache {
|
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
107
|
const generation = this.merged.generation;
|
|
108
|
-
const
|
|
109
|
-
this.#building.set(routeId,
|
|
108
|
+
const pending: PendingBuild = { generation, promise: this.#runBuild(routeId, seeds, generation) };
|
|
109
|
+
this.#building.set(routeId, pending);
|
|
110
110
|
try {
|
|
111
|
-
await promise;
|
|
111
|
+
await pending.promise;
|
|
112
112
|
} finally {
|
|
113
|
-
|
|
114
|
-
if (current?.promise === promise) this.#building.delete(routeId);
|
|
113
|
+
if (this.#building.get(routeId) === pending) this.#building.delete(routeId);
|
|
115
114
|
this.#prune();
|
|
116
115
|
}
|
|
117
116
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import { matchRoutePrefix } from "../../common/routeConvention";
|
|
2
|
+
import { RouteSeedMatcher } from "./routeSeedMatcher";
|
|
4
3
|
|
|
5
4
|
export interface RouteSeedEntry {
|
|
6
5
|
routeId: string;
|
|
@@ -60,27 +59,11 @@ export class RouteSeedIndexStore {
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
static match(pathname: string, entries: RouteSeedEntry[]): MatchedRoute | null {
|
|
63
|
-
|
|
64
|
-
const params = matchRoutePattern(entry.pattern ?? entry.routeId, pathname);
|
|
65
|
-
if (params) return { entry, params };
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
62
|
+
return RouteSeedMatcher.of(entries).match(pathname);
|
|
68
63
|
}
|
|
69
64
|
|
|
70
65
|
static matchPrefix(pathname: string, entries: RouteSeedEntry[]): MatchedRoute | null {
|
|
71
|
-
|
|
72
|
-
.map((entry) => ({
|
|
73
|
-
entry,
|
|
74
|
-
params: matchRoutePrefix(entry.pattern ?? entry.routeId, pathname),
|
|
75
|
-
}))
|
|
76
|
-
.filter((entry): entry is { entry: RouteSeedEntry; params: Record<string, string> } => Boolean(entry.params))
|
|
77
|
-
.sort((a, b) => {
|
|
78
|
-
const lengthDelta =
|
|
79
|
-
b.entry.pattern.split("/").filter(Boolean).length - a.entry.pattern.split("/").filter(Boolean).length;
|
|
80
|
-
if (lengthDelta !== 0) return lengthDelta;
|
|
81
|
-
return a.entry.pattern < b.entry.pattern ? -1 : a.entry.pattern > b.entry.pattern ? 1 : 0;
|
|
82
|
-
});
|
|
83
|
-
return candidates[0] ?? null;
|
|
66
|
+
return RouteSeedMatcher.of(entries).matchPrefix(pathname);
|
|
84
67
|
}
|
|
85
68
|
|
|
86
69
|
static #normalizeArtifactPath(artifactPath: string, artifactDir: string): string {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { MatchedRoute, RouteSeedEntry } from "./routeSeedIndexStore";
|
|
2
|
+
|
|
3
|
+
interface CompiledEntry {
|
|
4
|
+
entry: RouteSeedEntry;
|
|
5
|
+
parts: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class RouteSeedMatcher {
|
|
9
|
+
static readonly #byEntries = new WeakMap<RouteSeedEntry[], RouteSeedMatcher>();
|
|
10
|
+
|
|
11
|
+
static of(entries: RouteSeedEntry[]): RouteSeedMatcher {
|
|
12
|
+
const cached = RouteSeedMatcher.#byEntries.get(entries);
|
|
13
|
+
if (cached && cached.#size === entries.length) return cached;
|
|
14
|
+
const matcher = new RouteSeedMatcher(entries);
|
|
15
|
+
RouteSeedMatcher.#byEntries.set(entries, matcher);
|
|
16
|
+
return matcher;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
readonly #size: number;
|
|
20
|
+
readonly #inOrder: CompiledEntry[];
|
|
21
|
+
|
|
22
|
+
readonly #bySpecificity: CompiledEntry[];
|
|
23
|
+
|
|
24
|
+
constructor(entries: RouteSeedEntry[]) {
|
|
25
|
+
this.#size = entries.length;
|
|
26
|
+
this.#inOrder = entries.map((entry) => ({ entry, parts: RouteSeedMatcher.#split(entry.pattern ?? entry.routeId) }));
|
|
27
|
+
this.#bySpecificity = [...this.#inOrder].sort((a, b) => {
|
|
28
|
+
const lengthDelta = b.parts.length - a.parts.length;
|
|
29
|
+
if (lengthDelta !== 0) return lengthDelta;
|
|
30
|
+
const aPattern = a.entry.pattern ?? a.entry.routeId;
|
|
31
|
+
const bPattern = b.entry.pattern ?? b.entry.routeId;
|
|
32
|
+
return aPattern < bPattern ? -1 : aPattern > bPattern ? 1 : 0;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static #split(text: string) {
|
|
37
|
+
return text.split("/").filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static #paramsOf(parts: string[], pathParts: string[]): Record<string, string> | null {
|
|
41
|
+
const params: Record<string, string> = {};
|
|
42
|
+
for (let idx = 0; idx < parts.length; idx += 1) {
|
|
43
|
+
const part = parts[idx] as string;
|
|
44
|
+
const value = pathParts[idx] as string;
|
|
45
|
+
if (part.startsWith(":")) params[part.slice(1)] = decodeURIComponent(value);
|
|
46
|
+
else if (part !== value) return null;
|
|
47
|
+
}
|
|
48
|
+
return params;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
match(pathname: string): MatchedRoute | null {
|
|
52
|
+
const pathParts = RouteSeedMatcher.#split(pathname);
|
|
53
|
+
for (const { entry, parts } of this.#inOrder) {
|
|
54
|
+
if (parts.length !== pathParts.length) continue;
|
|
55
|
+
const params = RouteSeedMatcher.#paramsOf(parts, pathParts);
|
|
56
|
+
if (params) return { entry, params };
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
matchPrefix(pathname: string): MatchedRoute | null {
|
|
62
|
+
const pathParts = RouteSeedMatcher.#split(pathname);
|
|
63
|
+
for (const { entry, parts } of this.#bySpecificity) {
|
|
64
|
+
if (parts.length > pathParts.length) continue;
|
|
65
|
+
const params = RouteSeedMatcher.#paramsOf(parts, pathParts);
|
|
66
|
+
if (params) return { entry, params };
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/server/console.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface AkanConsoleOptions {
|
|
|
21
21
|
/** `level=warn grep="payment failed" endpoint=mutation:*` → a query object the socket parses. */
|
|
22
22
|
export const parseAkanConsoleQuery = (args: string): Record<string, string> => {
|
|
23
23
|
const query: Record<string, string> = {};
|
|
24
|
-
for (const match of args.matchAll(/([A-Za-z]+)=(?:"([^"]*)"|'([^']*)'|(\S+))/g)) {
|
|
24
|
+
for (const match of args.matchAll(/(?<![A-Za-z])([A-Za-z]+)=(?:"([^"]*)"|'([^']*)'|(\S+))/g)) {
|
|
25
25
|
const [, key, doubleQuoted, singleQuoted, bare] = match;
|
|
26
26
|
if (key) query[key] = doubleQuoted ?? singleQuoted ?? bare ?? "";
|
|
27
27
|
}
|
package/server/di/diLifecycle.ts
CHANGED
|
@@ -68,6 +68,7 @@ export class DiLifecycle {
|
|
|
68
68
|
readonly #middleware = new Map<string, MiddlewareCls>();
|
|
69
69
|
readonly webProxies: WebProxyRegistration[] = [];
|
|
70
70
|
readonly disabledModules = new Map<string, string>();
|
|
71
|
+
readonly initTimings = new Map<string, number>();
|
|
71
72
|
readonly #predefinedAdaptor;
|
|
72
73
|
readonly #cascade = new CascadeRunner();
|
|
73
74
|
|
|
@@ -401,21 +402,22 @@ export class DiLifecycle {
|
|
|
401
402
|
}
|
|
402
403
|
|
|
403
404
|
async #initializeAll(): Promise<SignalRoutes> {
|
|
404
|
-
|
|
405
|
-
await this.#
|
|
406
|
-
await this.#
|
|
407
|
-
await this.#
|
|
408
|
-
await this.#
|
|
405
|
+
this.initTimings.clear();
|
|
406
|
+
await this.#initStage("uses", () => this.#initializeUses());
|
|
407
|
+
await this.#initStage("adaptors", () => this.#initializeAdaptor());
|
|
408
|
+
await this.#initStage("serverSignals", () => this.#initializeServerSignal());
|
|
409
|
+
await this.#initStage("services", () => this.#initializeService());
|
|
410
|
+
await this.#initStage("internals", () => this.#initializeInternal());
|
|
409
411
|
const {
|
|
410
412
|
routes: sliceRoutes,
|
|
411
413
|
wsRoutes: sliceWsRoutes,
|
|
412
414
|
routeOptions: sliceRouteOptions,
|
|
413
|
-
} = await this.#initializeSlice();
|
|
415
|
+
} = await this.#initStage("slices", () => this.#initializeSlice());
|
|
414
416
|
const {
|
|
415
417
|
routes: endpointRoutes,
|
|
416
418
|
wsRoutes: endpointWsRoutes,
|
|
417
419
|
routeOptions: endpointRouteOptions,
|
|
418
|
-
} = await this.#initializeEndpoint();
|
|
420
|
+
} = await this.#initStage("endpoints", () => this.#initializeEndpoint());
|
|
419
421
|
const routes: SignalRoutes["routes"] = {};
|
|
420
422
|
SignalResolver.mergeHttpRoutes(routes, sliceRoutes);
|
|
421
423
|
SignalResolver.mergeHttpRoutes(routes, endpointRoutes);
|
|
@@ -442,6 +444,13 @@ export class DiLifecycle {
|
|
|
442
444
|
await this.#timed("Destroying uses...", "Destroy uses", () => this.destroyUses());
|
|
443
445
|
}
|
|
444
446
|
|
|
447
|
+
async #initStage<T>(stage: string, run: () => Promise<T>): Promise<T> {
|
|
448
|
+
const start = performance.now();
|
|
449
|
+
const result = await run();
|
|
450
|
+
this.initTimings.set(stage, performance.now() - start);
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
|
|
445
454
|
async #timed(startMessage: string, doneLabel: string, run: () => Promise<unknown>) {
|
|
446
455
|
const now = Date.now();
|
|
447
456
|
this.logger.verbose(startMessage);
|
package/server/imageOptimizer.ts
CHANGED
|
@@ -294,7 +294,7 @@ export class ImageOptimizer {
|
|
|
294
294
|
|
|
295
295
|
async #getOptimizedImage(source: ImageSource, params: ParsedImageRequest): Promise<OptimizedImage> {
|
|
296
296
|
const inputType = ImageOptimizer.#detectContentType(source.buffer) || source.contentType;
|
|
297
|
-
if (!inputType
|
|
297
|
+
if (!inputType.startsWith("image/") || inputType.includes(",")) {
|
|
298
298
|
throw new ImageOptimizerError(400, "The requested resource is not a valid image");
|
|
299
299
|
}
|
|
300
300
|
if (inputType === ImageOptimizer.#svg && !this.#config.dangerouslyAllowSVG) {
|
package/server/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./decorators";
|
|
|
8
8
|
export * from "./devtools";
|
|
9
9
|
export type { ChangeBatch, ChangeKind } from "./hmr/wsHub";
|
|
10
10
|
export * from "./machine";
|
|
11
|
+
export * from "./migration";
|
|
11
12
|
export * from "./nativeFile";
|
|
12
13
|
export * from "./oauth";
|
|
13
14
|
export * from "./processMetricsCollector";
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { getEnv } from "akanjs/base";
|
|
2
|
-
import type { Middleware, SignalContext } from "akanjs/signal";
|
|
3
|
-
import { MachineToken } from "./machineToken";
|
|
2
|
+
import type { MachineIdentity, Middleware, SignalContext } from "akanjs/signal";
|
|
3
|
+
import { type MachineProof, MachineToken } from "./machineToken";
|
|
4
|
+
|
|
5
|
+
interface VerifiedMachine {
|
|
6
|
+
authorization: string | null;
|
|
7
|
+
epoch: number;
|
|
8
|
+
proof: MachineProof | null;
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
interface AccountHolder {
|
|
6
12
|
headers?: Headers;
|
|
@@ -10,12 +16,18 @@ interface AccountHolder {
|
|
|
10
16
|
export class MachineAccount implements Middleware {
|
|
11
17
|
static readonly refName = "MachineAccount";
|
|
12
18
|
|
|
19
|
+
static readonly #verifiedBySocket = new WeakMap<object, VerifiedMachine>();
|
|
20
|
+
|
|
13
21
|
use() {
|
|
14
22
|
return async (context: SignalContext, next: () => Promise<unknown>) => {
|
|
15
23
|
const holder = (
|
|
16
24
|
context.transport === "http" ? context.getHttpContext().req : context.getWebSocketContext().ws.data
|
|
17
25
|
) as AccountHolder;
|
|
18
|
-
const
|
|
26
|
+
const authorization = holder.headers?.get("authorization") ?? null;
|
|
27
|
+
const machine =
|
|
28
|
+
context.transport === "websocket"
|
|
29
|
+
? MachineAccount.#verifiedOnSocket(holder, authorization)
|
|
30
|
+
: MachineToken.fromAuthorization(authorization);
|
|
19
31
|
const { machine: _unverified, ...account } = holder.account ?? {};
|
|
20
32
|
if (machine) {
|
|
21
33
|
const { appName, environment } = getEnv();
|
|
@@ -24,4 +36,17 @@ export class MachineAccount implements Middleware {
|
|
|
24
36
|
return await next();
|
|
25
37
|
};
|
|
26
38
|
}
|
|
39
|
+
|
|
40
|
+
static #verifiedOnSocket(socket: object, authorization: string | null): MachineIdentity | null {
|
|
41
|
+
const epoch = MachineToken.keyEpoch();
|
|
42
|
+
const verified = MachineAccount.#verifiedBySocket.get(socket);
|
|
43
|
+
const reusable =
|
|
44
|
+
verified?.authorization === authorization &&
|
|
45
|
+
verified.epoch === epoch &&
|
|
46
|
+
(verified.proof?.expiresAt === undefined || verified.proof.expiresAt > Date.now() / 1000);
|
|
47
|
+
if (verified && reusable) return verified.proof?.identity ?? null;
|
|
48
|
+
const proof = MachineToken.proofOf(authorization);
|
|
49
|
+
MachineAccount.#verifiedBySocket.set(socket, { authorization, epoch, proof });
|
|
50
|
+
return proof?.identity ?? null;
|
|
51
|
+
}
|
|
27
52
|
}
|
|
@@ -12,7 +12,15 @@ export interface MachineTokenIssueOptions {
|
|
|
12
12
|
audience?: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export type MachineTokenVerdict =
|
|
15
|
+
export type MachineTokenVerdict =
|
|
16
|
+
| { ok: true; identity: MachineIdentity; expiresAt?: number }
|
|
17
|
+
| { ok: false; reason: string };
|
|
18
|
+
|
|
19
|
+
/** What a bearer machine token proves, and the second it lapses at when it was issued with an expiry. */
|
|
20
|
+
export interface MachineProof {
|
|
21
|
+
identity: MachineIdentity;
|
|
22
|
+
expiresAt?: number;
|
|
23
|
+
}
|
|
16
24
|
|
|
17
25
|
interface KeyPair {
|
|
18
26
|
privateKey: KeyObject | null;
|
|
@@ -23,6 +31,7 @@ export class MachineToken {
|
|
|
23
31
|
static readonly type = "akan-machine+jwt";
|
|
24
32
|
static readonly #logger = new Logger("MachineToken");
|
|
25
33
|
static #keys: { source: string; pair: KeyPair } | null = null;
|
|
34
|
+
static #keyEpoch = 0;
|
|
26
35
|
static #warnedKeyless = false;
|
|
27
36
|
|
|
28
37
|
static audience() {
|
|
@@ -43,9 +52,16 @@ export class MachineToken {
|
|
|
43
52
|
: null;
|
|
44
53
|
const pair = { privateKey, publicKey };
|
|
45
54
|
MachineToken.#keys = { source, pair };
|
|
55
|
+
MachineToken.#keyEpoch += 1;
|
|
46
56
|
return pair;
|
|
47
57
|
}
|
|
48
58
|
|
|
59
|
+
/** Moves whenever the keys read from the env change, so a verdict kept from before is known to be stale. */
|
|
60
|
+
static keyEpoch() {
|
|
61
|
+
MachineToken.#keyPair();
|
|
62
|
+
return MachineToken.#keyEpoch;
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
static canIssue() {
|
|
50
66
|
return !!MachineToken.#keyPair().privateKey;
|
|
51
67
|
}
|
|
@@ -86,15 +102,23 @@ export class MachineToken {
|
|
|
86
102
|
if (exp !== undefined && (typeof exp !== "number" || exp <= nowSec)) return { ok: false, reason: "expired" };
|
|
87
103
|
const identity = { kind, id: sub, version: ver } as MachineIdentity;
|
|
88
104
|
if (!MachineToken.#isIdentity(identity)) return { ok: false, reason: "sub, kind and ver are required" };
|
|
89
|
-
return {
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
identity: { kind: identity.kind, id: identity.id, version: identity.version },
|
|
108
|
+
...(exp !== undefined ? { expiresAt: exp as number } : {}),
|
|
109
|
+
};
|
|
90
110
|
}
|
|
91
111
|
|
|
92
112
|
/** The identity a bearer machine token proves, or `null`: absent, another kind of token, or one that fails. */
|
|
93
113
|
static fromAuthorization(authorization: string | null | undefined): MachineIdentity | null {
|
|
114
|
+
return MachineToken.proofOf(authorization)?.identity ?? null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static proofOf(authorization: string | null | undefined): MachineProof | null {
|
|
94
118
|
const token = bearerTokenOf(authorization);
|
|
95
119
|
if (!token || !MachineToken.isMachineToken(token)) return null;
|
|
96
120
|
const verdict = MachineToken.verify(token);
|
|
97
|
-
if (verdict.ok) return verdict.identity;
|
|
121
|
+
if (verdict.ok) return verdict.expiresAt === undefined ? { identity: verdict.identity } : verdict;
|
|
98
122
|
if (!MachineToken.canVerify() && !MachineToken.#warnedKeyless) {
|
|
99
123
|
MachineToken.#warnedKeyless = true;
|
|
100
124
|
MachineToken.#logger.error(
|
|
@@ -72,7 +72,7 @@ export class McpDispatcher {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
static #restorable(error: unknown, status: number | undefined) {
|
|
75
|
-
if (!status || status >= 500 || status === 401 || status === 403
|
|
75
|
+
if (!status || status >= 500 || status === 401 || status === 403) return null;
|
|
76
76
|
return isExceptionLike(error) ? error.toJSON() : null;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -169,8 +169,7 @@ export class McpDispatcher {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
static #statusOf(error: unknown): number | undefined {
|
|
172
|
-
if (isExceptionLike(error) || error instanceof McpArgumentError
|
|
173
|
-
return error.statusCode;
|
|
172
|
+
if (isExceptionLike(error) || error instanceof McpArgumentError) return error.statusCode;
|
|
174
173
|
return undefined;
|
|
175
174
|
}
|
|
176
175
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Cls, PrimitiveRegistry, type PrimitiveScalar } from "akanjs/base";
|
|
2
2
|
import { isMcpDescribableArg } from "akanjs/common";
|
|
3
|
-
import {
|
|
3
|
+
import { InputParser, InvalidInputError, serialize } from "akanjs/constant";
|
|
4
4
|
import type { EndpointInfo } from "../../signal/endpointInfo";
|
|
5
5
|
import { HttpExecutionContext } from "../../signal/signalContext";
|
|
6
6
|
|
|
@@ -28,13 +28,15 @@ export class McpExecutionContext extends HttpExecutionContext {
|
|
|
28
28
|
|
|
29
29
|
override parseArg(arg: McpArg, value: unknown): unknown {
|
|
30
30
|
try {
|
|
31
|
-
return
|
|
31
|
+
return InputParser.parse(arg.argRef, arg.arrDepth, value, {
|
|
32
32
|
key: arg.name,
|
|
33
33
|
nullable: arg.option?.nullable,
|
|
34
34
|
enum: arg.enum,
|
|
35
35
|
});
|
|
36
|
-
} catch {
|
|
36
|
+
} catch (error) {
|
|
37
37
|
|
|
38
|
+
if (error instanceof InvalidInputError && error.path !== arg.name)
|
|
39
|
+
throw new McpArgumentError(McpExecutionContext.#fieldMessage(error));
|
|
38
40
|
throw new McpArgumentError(McpExecutionContext.#argumentMessage(arg, value));
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -58,6 +60,10 @@ export class McpExecutionContext extends HttpExecutionContext {
|
|
|
58
60
|
return isMcpDescribableArg({ refName });
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
static #fieldMessage({ path, expected }: InvalidInputError) {
|
|
64
|
+
return expected ? `Invalid field "${path}": expected ${expected}.` : `Missing required field "${path}".`;
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
static #argumentMessage(arg: McpArg, value: unknown) {
|
|
62
68
|
if (value === null) return `Missing required argument "${arg.name}".`;
|
|
63
69
|
if (arg.enum) return `Invalid argument "${arg.name}": expected one of ${arg.enum.values.join(", ")}.`;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { sanitizeJson } from "akanjs/document";
|
|
2
|
+
import { stableJson } from "akanjs/service";
|
|
3
|
+
|
|
4
|
+
/** What a path holds: `[value]`, or `[]` when it holds nothing — a stored `null` is a value. */
|
|
5
|
+
export type DocSlot = [unknown] | [];
|
|
6
|
+
|
|
7
|
+
/** Dot paths over a parsed `_doc`; a numeric segment indexes an array. */
|
|
8
|
+
export class DocPath {
|
|
9
|
+
static readonly #unsafe = new Set(["__proto__", "constructor", "prototype"]);
|
|
10
|
+
|
|
11
|
+
static readonly #index = /^(0|[1-9]\d*)$/;
|
|
12
|
+
|
|
13
|
+
static segments(path: string) {
|
|
14
|
+
const segments = path.split(".");
|
|
15
|
+
if (segments.some((segment) => !segment || DocPath.#unsafe.has(segment)))
|
|
16
|
+
throw new Error(`Invalid document path "${path}"`);
|
|
17
|
+
return segments;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static read(doc: Record<string, unknown>, path: string): DocSlot {
|
|
21
|
+
let node: unknown = doc;
|
|
22
|
+
for (const segment of DocPath.segments(path)) {
|
|
23
|
+
if (!node || typeof node !== "object" || !DocPath.#holds(node, segment)) return [];
|
|
24
|
+
node = (node as Record<string, unknown>)[segment];
|
|
25
|
+
}
|
|
26
|
+
return [node];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Whether every container above `path` is still there, so that writing it would change nothing else. */
|
|
30
|
+
static hasParent(doc: Record<string, unknown>, path: string) {
|
|
31
|
+
const segments = DocPath.segments(path);
|
|
32
|
+
let node: unknown = doc;
|
|
33
|
+
for (const segment of segments.slice(0, -1)) {
|
|
34
|
+
if (!node || typeof node !== "object" || !DocPath.#holds(node, segment)) return false;
|
|
35
|
+
node = (node as Record<string, unknown>)[segment];
|
|
36
|
+
}
|
|
37
|
+
return !!node && typeof node === "object";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The paths a write of `paths` changes as whole values: each path, or the nearest one above it that the write
|
|
42
|
+
* creates, replaces a `null` at, or appends to as an array, so the logged slot holds everything to put back.
|
|
43
|
+
*/
|
|
44
|
+
static anchors(doc: Record<string, unknown>, paths: string[]) {
|
|
45
|
+
const anchors = [...new Set(paths.map((path) => DocPath.#anchor(doc, path)))];
|
|
46
|
+
return anchors.filter((anchor) => !anchors.some((other) => anchor.startsWith(`${other}.`)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static #anchor(doc: Record<string, unknown>, path: string) {
|
|
50
|
+
const segments = DocPath.segments(path);
|
|
51
|
+
let node: unknown = doc;
|
|
52
|
+
for (const [idx, segment] of segments.entries()) {
|
|
53
|
+
if (Array.isArray(node) && !DocPath.#holds(node, segment)) return segments.slice(0, idx).join(".");
|
|
54
|
+
if (idx === segments.length - 1) break;
|
|
55
|
+
const next = DocPath.#holds(node as object, segment) ? (node as Record<string, unknown>)[segment] : undefined;
|
|
56
|
+
if (!next || typeof next !== "object") return segments.slice(0, idx + 1).join(".");
|
|
57
|
+
node = next;
|
|
58
|
+
}
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static #holds(node: object, segment: string) {
|
|
63
|
+
if (!Array.isArray(node)) return Object.hasOwn(node, segment);
|
|
64
|
+
return DocPath.#index.test(segment) && Number(segment) < node.length;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static write(doc: Record<string, unknown>, path: string, value: unknown) {
|
|
68
|
+
const segments = DocPath.segments(path);
|
|
69
|
+
let node: Record<string, unknown> | unknown[] = doc;
|
|
70
|
+
for (const segment of segments.slice(0, -1)) {
|
|
71
|
+
const next = DocPath.#holds(node, segment) ? (node as Record<string, unknown>)[segment] : undefined;
|
|
72
|
+
if (next && typeof next === "object") {
|
|
73
|
+
node = next as Record<string, unknown>;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (next !== undefined && next !== null)
|
|
77
|
+
throw new Error(`Cannot write "${path}": "${segment}" holds a ${typeof next}, not an object`);
|
|
78
|
+
const created: Record<string, unknown> = {};
|
|
79
|
+
DocPath.#assign(node, segment, created, path);
|
|
80
|
+
node = created;
|
|
81
|
+
}
|
|
82
|
+
DocPath.#assign(node, segments[segments.length - 1] ?? "", sanitizeJson(value) ?? null, path);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static remove(doc: Record<string, unknown>, path: string) {
|
|
86
|
+
const segments = DocPath.segments(path);
|
|
87
|
+
let node: unknown = doc;
|
|
88
|
+
for (const segment of segments.slice(0, -1)) {
|
|
89
|
+
if (!node || typeof node !== "object" || !DocPath.#holds(node, segment)) return;
|
|
90
|
+
node = (node as Record<string, unknown>)[segment];
|
|
91
|
+
}
|
|
92
|
+
if (!node || typeof node !== "object") return;
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(node)) throw new Error(`Cannot unset "${path}" inside an array; set the whole array instead`);
|
|
95
|
+
delete (node as Record<string, unknown>)[segments[segments.length - 1] ?? ""];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static same(a: DocSlot, b: DocSlot) {
|
|
99
|
+
if (a.length !== b.length) return false;
|
|
100
|
+
return !a.length || stableJson(a[0]) === stableJson(b[0]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Two paths of one write where one sits inside the other would log a value the other already changed. */
|
|
104
|
+
static assertDisjoint(paths: string[]) {
|
|
105
|
+
for (const path of paths) {
|
|
106
|
+
const inner = paths.find((other) => other.startsWith(`${path}.`));
|
|
107
|
+
if (inner) throw new Error(`"${path}" and "${inner}" overlap; write them in separate calls`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static #assign(node: Record<string, unknown> | unknown[], segment: string, value: unknown, path: string) {
|
|
112
|
+
if (!Array.isArray(node)) {
|
|
113
|
+
node[segment] = value;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const idx = DocPath.#index.test(segment) ? Number(segment) : -1;
|
|
117
|
+
if (idx < 0 || idx > node.length)
|
|
118
|
+
throw new Error(`Cannot write "${path}": "${segment}" is not an index within the array`);
|
|
119
|
+
node[idx] = value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from "./docPath";
|
|
2
|
+
export * from "./migrationChangeLog";
|
|
3
|
+
export * from "./migrationChanges";
|
|
4
|
+
export * from "./migrationCommand";
|
|
5
|
+
export * from "./migrationContext";
|
|
6
|
+
export * from "./migrationDefinition";
|
|
7
|
+
export * from "./migrationError";
|
|
8
|
+
export * from "./migrationLedger";
|
|
9
|
+
export * from "./migrationLock";
|
|
10
|
+
export * from "./migrationParams";
|
|
11
|
+
export * from "./migrationPulse";
|
|
12
|
+
export * from "./migrationRunner";
|
|
13
|
+
export * from "./migrationScope";
|
|
14
|
+
export * from "./migrations";
|
|
15
|
+
export * from "./migrationTable";
|