@abide/abide 0.48.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +85 -59
- package/CHANGELOG.md +216 -0
- package/README.md +21 -10
- package/package.json +15 -4
- package/src/abideLsp.ts +5 -6
- package/src/abideResolverPlugin.ts +197 -169
- package/src/build.ts +79 -56
- package/src/buildCli.ts +3 -5
- package/src/buildDisconnected.ts +2 -5
- package/src/bundleApp.ts +2 -3
- package/src/checkAbide.ts +12 -2
- package/src/compile.ts +2 -4
- package/src/devEntry.ts +113 -28
- package/src/discoveryEntry.ts +3 -2
- package/src/lib/bundle/disconnected.abide +69 -73
- package/src/lib/bundle/infoPlist.ts +13 -7
- package/src/lib/bundle/installDownloads.ts +13 -3
- package/src/lib/bundle/installMacMenu.ts +13 -3
- package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
- package/src/lib/bundle/waitForServer.ts +6 -1
- package/src/lib/cli/parseArgvForRpc.ts +36 -9
- package/src/lib/cli/tokenizeArgvFlags.ts +4 -2
- package/src/lib/mcp/createMcpResourceServer.ts +13 -2
- package/src/lib/mcp/mcpTools.ts +15 -6
- package/src/lib/mcp/toolResultFromResponse.ts +40 -12
- package/src/lib/server/error.ts +6 -6
- package/src/lib/server/json.ts +17 -3
- package/src/lib/server/rpc/defineRpc.ts +58 -24
- package/src/lib/server/rpc/parseArgs.ts +113 -14
- package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
- package/src/lib/server/rpc/runWithRpcTimeout.ts +12 -1
- package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
- package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
- package/src/lib/server/rpc/validationError.ts +1 -1
- package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
- package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
- package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
- package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
- package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
- package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
- package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
- package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
- package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
- package/src/lib/server/runtime/createServer.ts +120 -324
- package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
- package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
- package/src/lib/server/runtime/finalizeResponse.ts +11 -1
- package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
- package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
- package/src/lib/server/runtime/pathStore.ts +15 -0
- package/src/lib/server/runtime/registryManifests.ts +1 -1
- package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
- package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
- package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
- package/src/lib/server/runtime/snapshotEntryFromCache.ts +24 -16
- package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
- package/src/lib/server/runtime/streamFromIterator.ts +30 -2
- package/src/lib/server/runtime/textResponse.ts +23 -0
- package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/RequestStore.ts +27 -0
- package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
- package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
- package/src/lib/server/sockets/defineSocket.ts +5 -4
- package/src/lib/server/sse.ts +5 -1
- package/src/lib/shared/ASYNC_CELL.ts +5 -0
- package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
- package/src/lib/shared/HTTP_METHODS.ts +6 -0
- package/src/lib/shared/HttpError.ts +1 -5
- package/src/lib/shared/MCP_PATH.ts +6 -0
- package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
- package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
- package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
- package/src/lib/shared/activeCacheStore.ts +1 -0
- package/src/lib/shared/activePage.ts +1 -0
- package/src/lib/shared/buildArtifact.ts +20 -0
- package/src/lib/shared/buildRpcRequest.ts +6 -2
- package/src/lib/shared/buildSocketOverChannel.ts +4 -3
- package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
- package/src/lib/shared/cache.ts +156 -131
- package/src/lib/shared/canonicalJson.ts +24 -1
- package/src/lib/shared/changeAffectsClient.ts +12 -7
- package/src/lib/shared/createCacheStore.ts +31 -8
- package/src/lib/shared/createChannelLog.ts +24 -0
- package/src/lib/shared/createLifecycleChannel.ts +7 -1
- package/src/lib/shared/createReachable.ts +47 -78
- package/src/lib/shared/createRemoteFunction.ts +52 -30
- package/src/lib/shared/createRpcServerProgram.ts +820 -0
- package/src/lib/shared/decodeRefJson.ts +4 -4
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/decodeWireBody.ts +35 -0
- package/src/lib/shared/detectRpcMethod.ts +8 -1
- package/src/lib/shared/done.ts +8 -2
- package/src/lib/shared/encodeRefJson.ts +4 -4
- package/src/lib/shared/encodeWireBody.ts +18 -0
- package/src/lib/shared/exitOnBuildFailure.ts +4 -3
- package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
- package/src/lib/shared/generateDeclarations.ts +72 -0
- package/src/lib/shared/hasSeedableRequest.ts +25 -0
- package/src/lib/shared/hydrationWindow.ts +53 -0
- package/src/lib/shared/isAsyncCell.ts +11 -0
- package/src/lib/shared/isAsyncIterable.ts +8 -0
- package/src/lib/shared/isSubscribable.ts +3 -3
- package/src/lib/shared/isThenable.ts +9 -0
- package/src/lib/shared/jsonSchemaForType.ts +258 -0
- package/src/lib/shared/lenientDecode.ts +15 -0
- package/src/lib/shared/loadProjectTsConfig.ts +28 -0
- package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
- package/src/lib/shared/matchRoute.ts +4 -14
- package/src/lib/shared/parseEnv.ts +17 -6
- package/src/lib/shared/peek.ts +14 -0
- package/src/lib/shared/pending.ts +9 -6
- package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
- package/src/lib/shared/prepareRpcModule.ts +91 -93
- package/src/lib/shared/prepareSocketModule.ts +3 -2
- package/src/lib/shared/probeRegistries.ts +5 -18
- package/src/lib/shared/reachable.ts +7 -10
- package/src/lib/shared/refresh.ts +12 -2
- package/src/lib/shared/refreshing.ts +8 -2
- package/src/lib/shared/resolvedCellsSlot.ts +13 -0
- package/src/lib/shared/reviveWireField.ts +49 -0
- package/src/lib/shared/reviveWireOutput.ts +36 -0
- package/src/lib/shared/rpcServerForRoot.ts +25 -0
- package/src/lib/shared/scanPages.ts +25 -0
- package/src/lib/shared/serializeEnv.ts +18 -6
- package/src/lib/shared/snapshotShippable.ts +8 -7
- package/src/lib/shared/snippet.ts +11 -6
- package/src/lib/shared/streamedCellsSlot.ts +14 -0
- package/src/lib/shared/subscribableFromResponse.ts +4 -4
- package/src/lib/shared/subscribableProbes.ts +1 -1
- package/src/lib/shared/tailProbeSlot.ts +1 -1
- package/src/lib/shared/types/AsyncComputed.ts +20 -0
- package/src/lib/shared/types/AsyncState.ts +13 -0
- package/src/lib/shared/types/CacheEntry.ts +7 -7
- package/src/lib/shared/types/CacheOptions.ts +23 -37
- package/src/lib/shared/types/CachePolicy.ts +25 -0
- package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
- package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
- package/src/lib/shared/types/HttpMethod.ts +3 -1
- package/src/lib/shared/types/InputCoercion.ts +17 -0
- package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
- package/src/lib/shared/types/OutputWirePlan.ts +17 -0
- package/src/lib/shared/types/PagesScan.ts +7 -0
- package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
- package/src/lib/shared/types/RemoteCallable.ts +8 -7
- package/src/lib/shared/types/RemoteFunction.ts +16 -15
- package/src/lib/shared/types/ResolvedCells.ts +11 -0
- package/src/lib/shared/types/ReturnBody.ts +15 -0
- package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
- package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
- package/src/lib/shared/types/Socket.ts +4 -4
- package/src/lib/shared/types/SsrPayload.ts +5 -1
- package/src/lib/shared/types/StreamPolicy.ts +11 -0
- package/src/lib/shared/types/StreamedCells.ts +19 -0
- package/src/lib/shared/types/StreamedResolution.ts +24 -6
- package/src/lib/shared/types/TailHooks.ts +1 -1
- package/src/lib/shared/types/WireKind.ts +11 -0
- package/src/lib/shared/url.ts +5 -0
- package/src/lib/shared/validationHttpError.ts +33 -0
- package/src/lib/shared/warmSeedKey.ts +16 -0
- package/src/lib/shared/wireJsonReplacer.ts +30 -0
- package/src/lib/shared/writeRpcDts.ts +11 -1
- package/src/lib/shared/writeTestSocketsDts.ts +1 -1
- package/src/lib/test/createTestApp.ts +19 -1
- package/src/lib/ui/README.md +1 -1
- package/src/lib/ui/activePendingCells.ts +14 -0
- package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
- package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -7
- package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
- package/src/lib/ui/compile/analyzeComponent.ts +89 -6
- package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
- package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
- package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
- package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
- package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
- package/src/lib/ui/compile/bindListenEvent.ts +5 -6
- package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
- package/src/lib/ui/compile/catchBinding.ts +7 -5
- package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
- package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
- package/src/lib/ui/compile/compileComponent.ts +19 -3
- package/src/lib/ui/compile/compileModule.ts +27 -58
- package/src/lib/ui/compile/compileSSR.ts +52 -13
- package/src/lib/ui/compile/compileShadow.ts +265 -42
- package/src/lib/ui/compile/composeProps.ts +15 -4
- package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
- package/src/lib/ui/compile/createShadowProgram.ts +35 -6
- package/src/lib/ui/compile/declaredNames.ts +36 -0
- package/src/lib/ui/compile/desugarSignals.ts +419 -37
- package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
- package/src/lib/ui/compile/generateBuild.ts +74 -17
- package/src/lib/ui/compile/generateSSR.ts +286 -75
- package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
- package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
- package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
- package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
- package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
- package/src/lib/ui/compile/isWhitespaceText.ts +10 -2
- package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
- package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
- package/src/lib/ui/compile/lowerContext.ts +10 -0
- package/src/lib/ui/compile/lowerDocAccess.ts +51 -10
- package/src/lib/ui/compile/lowerScript.ts +40 -4
- package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
- package/src/lib/ui/compile/parseTemplate.ts +18 -1090
- package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
- package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
- package/src/lib/ui/compile/renameSignalRefs.ts +26 -40
- package/src/lib/ui/compile/resolveReactiveExport.ts +4 -7
- package/src/lib/ui/compile/scopeCss.ts +27 -4
- package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
- package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
- package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
- package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
- package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
- package/src/lib/ui/compile/templateStartOffset.ts +15 -0
- package/src/lib/ui/compile/tryPlan.ts +4 -3
- package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
- package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
- package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
- package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
- package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
- package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
- package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
- package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
- package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
- package/src/lib/ui/computed.ts +28 -1
- package/src/lib/ui/createScope.ts +35 -68
- package/src/lib/ui/dom/anchoredBranch.ts +142 -0
- package/src/lib/ui/dom/appendText.ts +8 -3
- package/src/lib/ui/dom/awaitBlock.ts +62 -75
- package/src/lib/ui/dom/bindProp.ts +22 -0
- package/src/lib/ui/dom/bindableProp.ts +47 -0
- package/src/lib/ui/dom/cellPending.ts +24 -0
- package/src/lib/ui/dom/discardBoundary.ts +24 -6
- package/src/lib/ui/dom/disposeRange.ts +2 -1
- package/src/lib/ui/dom/each.ts +41 -12
- package/src/lib/ui/dom/eachAsync.ts +39 -3
- package/src/lib/ui/dom/fillBefore.ts +6 -7
- package/src/lib/ui/dom/fillBoundary.ts +2 -3
- package/src/lib/ui/dom/fillRange.ts +4 -4
- package/src/lib/ui/dom/hydrate.ts +6 -12
- package/src/lib/ui/dom/isComment.ts +1 -1
- package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
- package/src/lib/ui/dom/mergeProps.ts +1 -1
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/mountChild.ts +9 -40
- package/src/lib/ui/dom/mountRange.ts +2 -3
- package/src/lib/ui/dom/mountSlot.ts +1 -1
- package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
- package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
- package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
- package/src/lib/ui/dom/on.ts +2 -2
- package/src/lib/ui/dom/readCell.ts +40 -0
- package/src/lib/ui/dom/restProps.ts +2 -2
- package/src/lib/ui/dom/spreadProps.ts +3 -4
- package/src/lib/ui/dom/switchBlock.ts +30 -7
- package/src/lib/ui/dom/tryBlock.ts +233 -70
- package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
- package/src/lib/ui/dom/when.ts +12 -2
- package/src/lib/ui/dom/withScope.ts +9 -2
- package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
- package/src/lib/ui/flight.ts +56 -0
- package/src/lib/ui/html.ts +12 -1
- package/src/lib/ui/isolateCellBarrier.ts +17 -0
- package/src/lib/ui/linked.ts +48 -2
- package/src/lib/ui/props.ts +17 -0
- package/src/lib/ui/remoteProxy.ts +84 -159
- package/src/lib/ui/renderChain.ts +53 -13
- package/src/lib/ui/renderToStream.ts +22 -18
- package/src/lib/ui/resumeSeedScript.ts +1 -1
- package/src/lib/ui/router.ts +86 -53
- package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
- package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
- package/src/lib/ui/runtime/CHILD_PRESENT.ts +2 -2
- package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
- package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
- package/src/lib/ui/runtime/RENDER.ts +10 -7
- package/src/lib/ui/runtime/RESUME.ts +4 -4
- package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
- package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
- package/src/lib/ui/runtime/applyPatchToTree.ts +17 -4
- package/src/lib/ui/runtime/blockId.ts +31 -0
- package/src/lib/ui/runtime/boundaryFor.ts +11 -0
- package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
- package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
- package/src/lib/ui/runtime/createDoc.ts +31 -54
- package/src/lib/ui/runtime/createEffectNode.ts +9 -0
- package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
- package/src/lib/ui/runtime/flushEffects.ts +33 -1
- package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
- package/src/lib/ui/runtime/nextBlockId.ts +8 -7
- package/src/lib/ui/runtime/renderPath.ts +16 -0
- package/src/lib/ui/runtime/scope.ts +11 -0
- package/src/lib/ui/runtime/toTeardown.ts +10 -3
- package/src/lib/ui/runtime/types/Boundary.ts +9 -0
- package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
- package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
- package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
- package/src/lib/ui/runtime/types/UiProps.ts +6 -5
- package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
- package/src/lib/ui/runtime/withPath.ts +16 -0
- package/src/lib/ui/runtime/withPathFrom.ts +20 -0
- package/src/lib/ui/runtime/withoutHydration.ts +22 -0
- package/src/lib/ui/seedStreamedResolution.ts +31 -6
- package/src/lib/ui/settleAsyncCells.ts +24 -0
- package/src/lib/ui/socketProxy.ts +1 -1
- package/src/lib/ui/startClient.ts +16 -31
- package/src/lib/ui/state.ts +9 -3
- package/src/lib/ui/trackedComputed.ts +68 -0
- package/src/lib/ui/types/Scope.ts +8 -24
- package/src/lib/ui/watch.ts +3 -3
- package/src/serverEntry.ts +14 -0
- package/template/src/server/rpc/getHello.ts +15 -13
- package/template/test/app.test.ts +1 -1
- package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
- package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
- package/src/lib/shared/hasReplayableRequest.ts +0 -17
- package/src/lib/shared/hydratingSlot.ts +0 -12
- package/src/lib/shared/outboxProbeSlot.ts +0 -20
- package/src/lib/shared/types/Outbox.ts +0 -9
- package/src/lib/shared/types/OutboxEntry.ts +0 -27
- package/src/lib/shared/types/SmartReadOptions.ts +0 -36
- package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
- package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
- package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
- package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
- package/src/lib/ui/compile/markupTokens.ts +0 -313
- package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
- package/src/lib/ui/dom/applyResolved.ts +0 -87
- package/src/lib/ui/dom/scopeLabel.ts +0 -21
- package/src/lib/ui/dom/text.ts +0 -20
- package/src/lib/ui/history.ts +0 -101
- package/src/lib/ui/installHotBridge.ts +0 -93
- package/src/lib/ui/installInspectorBridge.ts +0 -140
- package/src/lib/ui/outbox.ts +0 -35
- package/src/lib/ui/persist.ts +0 -115
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
- package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
- package/src/lib/ui/runtime/captureModelDoc.ts +0 -28
- package/src/lib/ui/runtime/hotInstances.ts +0 -10
- package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
- package/src/lib/ui/runtime/hotReplace.ts +0 -38
- package/src/lib/ui/runtime/liveScopes.ts +0 -15
- package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
- package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
- package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
- package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
- package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
- package/src/lib/ui/seedResolved.ts +0 -28
- package/src/lib/ui/sync.ts +0 -48
- package/src/lib/ui/types/History.ts +0 -14
- package/src/lib/ui/types/PersistHandle.ts +0 -11
- package/src/lib/ui/types/PersistenceStore.ts +0 -12
- package/src/lib/ui/types/ResolvedFrame.ts +0 -15
- package/src/lib/ui/types/SyncTransport.ts +0 -13
|
@@ -149,72 +149,69 @@ async function saveConfig() {
|
|
|
149
149
|
}
|
|
150
150
|
</script>
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
{#if phase === 'splash'}
|
|
153
153
|
<div class="flex min-h-screen items-center justify-center bg-gray-50 dark:bg-gray-950">
|
|
154
|
-
|
|
154
|
+
{#if logo}
|
|
155
155
|
<img src={logo} alt="" class="h-16 w-16 rounded-xl object-contain opacity-90" />
|
|
156
|
-
|
|
156
|
+
{/if}
|
|
157
157
|
</div>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<form class="flex flex-col gap-3" onsubmit={(event) => { event.preventDefault(); void connect() }}>
|
|
172
|
-
<input
|
|
173
|
-
type="url"
|
|
174
|
-
bind:value={url}
|
|
175
|
-
placeholder={placeholder}
|
|
176
|
-
autocomplete="url"
|
|
177
|
-
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
|
|
178
|
-
<button
|
|
179
|
-
type="submit"
|
|
180
|
-
class="w-full rounded-lg bg-gray-900 px-3 py-2 text-sm font-medium text-white hover:bg-gray-700 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-300">
|
|
181
|
-
Connect
|
|
182
|
-
</button>
|
|
183
|
-
</form>
|
|
184
|
-
|
|
185
|
-
<div class="my-5 flex items-center gap-3 text-xs text-gray-400 dark:text-gray-500">
|
|
186
|
-
<span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
|
|
187
|
-
or
|
|
188
|
-
<span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
|
|
189
|
-
</div>
|
|
158
|
+
{:else}
|
|
159
|
+
<main
|
|
160
|
+
class="flex min-h-screen items-center justify-center bg-gray-50 p-6 text-gray-900 dark:bg-gray-950 dark:text-gray-100">
|
|
161
|
+
<div
|
|
162
|
+
class="w-full max-w-sm rounded-2xl bg-white p-8 shadow-sm ring-1 ring-gray-200 dark:bg-gray-900 dark:ring-gray-800">
|
|
163
|
+
{#if logo}
|
|
164
|
+
<img
|
|
165
|
+
src={logo}
|
|
166
|
+
alt=""
|
|
167
|
+
class="mx-auto mb-5 h-16 w-16 rounded-xl object-contain" />
|
|
168
|
+
{/if}
|
|
169
|
+
<h1 class="mb-6 text-center text-xl font-semibold tracking-tight">{heading}</h1>
|
|
190
170
|
|
|
171
|
+
<form class="flex flex-col gap-3" onsubmit={(event) => { event.preventDefault(); void connect() }}>
|
|
172
|
+
<input
|
|
173
|
+
type="url"
|
|
174
|
+
bind:value={url}
|
|
175
|
+
placeholder={placeholder}
|
|
176
|
+
autocomplete="url"
|
|
177
|
+
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
|
|
191
178
|
<button
|
|
192
|
-
type="
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Start server
|
|
179
|
+
type="submit"
|
|
180
|
+
class="w-full rounded-lg bg-gray-900 px-3 py-2 text-sm font-medium text-white hover:bg-gray-700 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-300">
|
|
181
|
+
Connect
|
|
196
182
|
</button>
|
|
183
|
+
</form>
|
|
197
184
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</template>
|
|
203
|
-
|
|
204
|
-
<p class="mt-8 text-center text-xs text-gray-400 dark:text-gray-500">
|
|
205
|
-
made with
|
|
206
|
-
<a
|
|
207
|
-
href="https://github.com/briancray/abide"
|
|
208
|
-
class="underline hover:text-gray-600 dark:hover:text-gray-300"
|
|
209
|
-
>abide</a
|
|
210
|
-
>
|
|
211
|
-
</p>
|
|
185
|
+
<div class="my-5 flex items-center gap-3 text-xs text-gray-400 dark:text-gray-500">
|
|
186
|
+
<span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
|
|
187
|
+
or
|
|
188
|
+
<span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
|
|
212
189
|
</div>
|
|
213
|
-
</main>
|
|
214
|
-
</template>
|
|
215
|
-
</template>
|
|
216
190
|
|
|
217
|
-
<
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
onclick={() => void start()}
|
|
194
|
+
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm font-medium hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800">
|
|
195
|
+
Start server
|
|
196
|
+
</button>
|
|
197
|
+
|
|
198
|
+
{#if error}
|
|
199
|
+
<p class="mt-4 text-center text-sm text-red-600 dark:text-red-400">{error}</p>
|
|
200
|
+
{/if}
|
|
201
|
+
|
|
202
|
+
<p class="mt-8 text-center text-xs text-gray-400 dark:text-gray-500">
|
|
203
|
+
made with
|
|
204
|
+
<a
|
|
205
|
+
href="https://github.com/briancray/abide"
|
|
206
|
+
class="underline hover:text-gray-600 dark:hover:text-gray-300"
|
|
207
|
+
>abide</a
|
|
208
|
+
>
|
|
209
|
+
</p>
|
|
210
|
+
</div>
|
|
211
|
+
</main>
|
|
212
|
+
{/if}
|
|
213
|
+
|
|
214
|
+
{#if showConfig}
|
|
218
215
|
<div
|
|
219
216
|
class="fixed inset-0 z-10 flex items-center justify-center bg-black/40 p-6 text-gray-900 dark:text-gray-100">
|
|
220
217
|
<div
|
|
@@ -222,29 +219,28 @@ async function saveConfig() {
|
|
|
222
219
|
<h2 class="mb-5 text-lg font-semibold tracking-tight">Set up {heading}</h2>
|
|
223
220
|
|
|
224
221
|
<form class="flex flex-col gap-4" onsubmit={(event) => { event.preventDefault(); void saveConfig() }}>
|
|
225
|
-
|
|
222
|
+
{#for field of configFields by field.key}
|
|
226
223
|
<label class="flex flex-col gap-1 text-sm">
|
|
227
224
|
<span class="font-medium">{field.label}</span>
|
|
228
|
-
|
|
225
|
+
{#if field.inputType === 'checkbox'}
|
|
229
226
|
<input
|
|
230
227
|
type="checkbox"
|
|
231
228
|
onchange={(event) => { configValues[field.key] = event.currentTarget.checked ? 'true' : 'false' }}
|
|
232
229
|
class="mt-1 size-4 self-start rounded border-gray-300 dark:border-gray-700" />
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<template if={field.description}>
|
|
230
|
+
{:else}
|
|
231
|
+
<input
|
|
232
|
+
type={field.inputType}
|
|
233
|
+
value={configValues[field.key]}
|
|
234
|
+
oninput={(event) => { configValues[field.key] = event.currentTarget.value }}
|
|
235
|
+
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
|
|
236
|
+
{/if}
|
|
237
|
+
{#if field.description}
|
|
242
238
|
<span class="text-xs text-gray-400 dark:text-gray-500"
|
|
243
239
|
>{field.description}</span
|
|
244
240
|
>
|
|
245
|
-
|
|
241
|
+
{/if}
|
|
246
242
|
</label>
|
|
247
|
-
|
|
243
|
+
{/for}
|
|
248
244
|
|
|
249
245
|
<div class="mt-1 flex gap-3">
|
|
250
246
|
<button
|
|
@@ -261,9 +257,9 @@ async function saveConfig() {
|
|
|
261
257
|
</div>
|
|
262
258
|
</form>
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
{#if error}
|
|
265
261
|
<p class="mt-4 text-center text-sm text-red-600 dark:text-red-400">{error}</p>
|
|
266
|
-
|
|
262
|
+
{/if}
|
|
267
263
|
</div>
|
|
268
264
|
</div>
|
|
269
|
-
|
|
265
|
+
{/if}
|
|
@@ -15,9 +15,15 @@ export function infoPlist({
|
|
|
15
15
|
version: string
|
|
16
16
|
icon?: string
|
|
17
17
|
}): string {
|
|
18
|
+
/* Interpolated values ride inside plist `<string>` elements, so any `&`/`<`/`>` in a
|
|
19
|
+
project name would otherwise emit malformed XML that codesign/Gatekeeper reject. */
|
|
20
|
+
const escapeXml = (value: string): string =>
|
|
21
|
+
value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>')
|
|
22
|
+
const safeName = escapeXml(name)
|
|
23
|
+
const safeVersion = escapeXml(version)
|
|
18
24
|
const iconEntry = icon
|
|
19
25
|
? ` <key>CFBundleIconFile</key>
|
|
20
|
-
<string>${icon}</string>
|
|
26
|
+
<string>${escapeXml(icon)}</string>
|
|
21
27
|
`
|
|
22
28
|
: ''
|
|
23
29
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -25,17 +31,17 @@ export function infoPlist({
|
|
|
25
31
|
<plist version="1.0">
|
|
26
32
|
<dict>
|
|
27
33
|
<key>CFBundleName</key>
|
|
28
|
-
<string>${
|
|
34
|
+
<string>${safeName}</string>
|
|
29
35
|
<key>CFBundleDisplayName</key>
|
|
30
|
-
<string>${
|
|
36
|
+
<string>${safeName}</string>
|
|
31
37
|
<key>CFBundleExecutable</key>
|
|
32
|
-
<string>${
|
|
38
|
+
<string>${safeName}</string>
|
|
33
39
|
<key>CFBundleIdentifier</key>
|
|
34
|
-
<string>com.abide.${
|
|
40
|
+
<string>com.abide.${safeName}</string>
|
|
35
41
|
<key>CFBundleVersion</key>
|
|
36
|
-
<string>${
|
|
42
|
+
<string>${safeVersion}</string>
|
|
37
43
|
<key>CFBundleShortVersionString</key>
|
|
38
|
-
<string>${
|
|
44
|
+
<string>${safeVersion}</string>
|
|
39
45
|
${iconEntry} <key>CFBundlePackageType</key>
|
|
40
46
|
<string>APPL</string>
|
|
41
47
|
<key>NSHighResolutionCapable</key>
|
|
@@ -16,9 +16,19 @@ export function installDownloads(libPath: string, webviewHandle: Pointer | null)
|
|
|
16
16
|
if (process.platform !== 'darwin') {
|
|
17
17
|
return
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/* abide_install_downloads exists only in abide's own webview lib. A vanilla
|
|
20
|
+
ABIDE_WEBVIEW_LIB on darwin lacks it, so dlopen throws — degrade to a no-op
|
|
21
|
+
(downloads simply unhandled) instead of crashing the launcher, matching the
|
|
22
|
+
optional-symbol FFI binders. */
|
|
23
|
+
let symbols: { abide_install_downloads: (handle: Pointer | null) => void }
|
|
24
|
+
let close: () => void
|
|
25
|
+
try {
|
|
26
|
+
;({ symbols, close } = dlopen(libPath, {
|
|
27
|
+
abide_install_downloads: { args: [FFIType.ptr], returns: FFIType.void },
|
|
28
|
+
}))
|
|
29
|
+
} catch {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
22
32
|
symbols.abide_install_downloads(webviewHandle)
|
|
23
33
|
close()
|
|
24
34
|
}
|
|
@@ -30,9 +30,19 @@ export function installMacMenu(
|
|
|
30
30
|
if (process.platform !== 'darwin') {
|
|
31
31
|
return
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
/* The abide_install_app_menu symbol is compiled only into abide's own webview lib.
|
|
34
|
+
A user who points ABIDE_WEBVIEW_LIB at a vanilla webview.dylib is on darwin but
|
|
35
|
+
lacks the symbol, so dlopen throws — degrade to a no-op (native menu simply absent)
|
|
36
|
+
rather than crashing the launcher, matching bindConnectedFlag/bindRequestNavigate. */
|
|
37
|
+
let symbols: { abide_install_app_menu: (handle: Pointer | null, config: Uint8Array) => void }
|
|
38
|
+
let close: () => void
|
|
39
|
+
try {
|
|
40
|
+
;({ symbols, close } = dlopen(libPath, {
|
|
41
|
+
abide_install_app_menu: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.void },
|
|
42
|
+
}))
|
|
43
|
+
} catch {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
36
46
|
const config = JSON.stringify({ appName, fileMenu, menu })
|
|
37
47
|
symbols.abide_install_app_menu(webviewHandle, new TextEncoder().encode(`${config}\0`))
|
|
38
48
|
close()
|
|
@@ -53,10 +53,19 @@ export async function spawnEmbeddedServer({
|
|
|
53
53
|
env: { ...process.env, PORT: String(port), ABIDE_PARENT_PID: String(process.pid) },
|
|
54
54
|
stdio: ['inherit', 'inherit', 'inherit'],
|
|
55
55
|
})
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
let outcome: number | undefined
|
|
57
|
+
try {
|
|
58
|
+
outcome = await Promise.race([
|
|
59
|
+
waitForServer(url, timeoutMs ? { timeoutMs } : undefined).then(() => undefined),
|
|
60
|
+
child.exited,
|
|
61
|
+
])
|
|
62
|
+
} catch (error) {
|
|
63
|
+
/* Readiness timed out (waitForServer threw) — reap the child we spawned so it doesn't
|
|
64
|
+
orphan and hold its fixed PORT, which would then fail the next spawn's bind. The caller
|
|
65
|
+
only owns the child once we successfully return it. */
|
|
66
|
+
child.kill()
|
|
67
|
+
throw error
|
|
68
|
+
}
|
|
60
69
|
if (outcome !== undefined) {
|
|
61
70
|
throw new Error(`[abide] embedded server exited (code ${outcome}) before binding`)
|
|
62
71
|
}
|
|
@@ -13,7 +13,12 @@ export async function waitForServer(
|
|
|
13
13
|
const deadline = Bun.nanoseconds() + timeoutMs * 1e6
|
|
14
14
|
while (Bun.nanoseconds() < deadline) {
|
|
15
15
|
try {
|
|
16
|
-
|
|
16
|
+
/* Bound each probe by the time left to the deadline: a connection that is accepted but
|
|
17
|
+
stalls its HTTP response leaves a bare `fetch(url)` pending forever (it never rejects),
|
|
18
|
+
which would hang the loop past its own timeout. The abort caps a single hung probe at
|
|
19
|
+
the remaining budget so the deadline is always honored. */
|
|
20
|
+
const remainingMs = Math.max(0, (deadline - Bun.nanoseconds()) / 1e6)
|
|
21
|
+
await fetch(url, { signal: AbortSignal.timeout(remainingMs) })
|
|
17
22
|
return
|
|
18
23
|
} catch {
|
|
19
24
|
await Bun.sleep(intervalMs)
|
|
@@ -74,8 +74,36 @@ export async function parseArgvForRpc(
|
|
|
74
74
|
}
|
|
75
75
|
const name = token.name
|
|
76
76
|
const propType = properties[name]?.type
|
|
77
|
+
/* Coerce one string token to a schema scalar type (or throw). Shared by scalar
|
|
78
|
+
props and array ELEMENTS so `--tags 1 --tags 2` on `z.array(z.number())` yields
|
|
79
|
+
numbers, not strings the server then rejects. */
|
|
80
|
+
const coerceScalar = (raw: string, type: string | undefined): unknown => {
|
|
81
|
+
if (type === 'number' || type === 'integer') {
|
|
82
|
+
// Reject a blank value explicitly — `Number('')` / `Number(' ')` is 0,
|
|
83
|
+
// not NaN, so the NaN guard alone would silently coerce it to zero.
|
|
84
|
+
const n = raw.trim() === '' ? Number.NaN : Number(raw)
|
|
85
|
+
if (Number.isNaN(n)) {
|
|
86
|
+
throw new Error(`--${name} expects a number, got ${raw}`)
|
|
87
|
+
}
|
|
88
|
+
return n
|
|
89
|
+
}
|
|
90
|
+
if (type === 'boolean') {
|
|
91
|
+
const lowered = raw.trim().toLowerCase()
|
|
92
|
+
if (lowered === 'true' || lowered === '1') {
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
if (lowered === 'false' || lowered === '0') {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
throw new Error(`--${name} expects true or false, got ${raw}`)
|
|
99
|
+
}
|
|
100
|
+
return raw
|
|
101
|
+
}
|
|
77
102
|
if (propType === 'boolean') {
|
|
78
|
-
|
|
103
|
+
/* Bare `--flag` / `--no-flag` toggles; inline `--flag=false` honours the RHS
|
|
104
|
+
instead of always resolving to true. */
|
|
105
|
+
args[name] =
|
|
106
|
+
token.value !== undefined ? coerceScalar(token.value, 'boolean') : !token.negated
|
|
79
107
|
continue
|
|
80
108
|
}
|
|
81
109
|
if (token.missingValue || token.value === undefined) {
|
|
@@ -83,18 +111,17 @@ export async function parseArgvForRpc(
|
|
|
83
111
|
}
|
|
84
112
|
const value = token.value
|
|
85
113
|
if (propType === 'number' || propType === 'integer') {
|
|
86
|
-
|
|
87
|
-
// not NaN, so the NaN guard alone would silently coerce it to zero.
|
|
88
|
-
const n = value.trim() === '' ? Number.NaN : Number(value)
|
|
89
|
-
if (Number.isNaN(n)) {
|
|
90
|
-
throw new Error(`--${name} expects a number, got ${value}`)
|
|
91
|
-
}
|
|
92
|
-
args[name] = n
|
|
114
|
+
args[name] = coerceScalar(value, propType)
|
|
93
115
|
continue
|
|
94
116
|
}
|
|
95
117
|
if (propType === 'array') {
|
|
118
|
+
/* Coerce each repeated element per the schema's `items.type` — a string-only
|
|
119
|
+
array arg silently fails server-side Zod validation for numeric/boolean items. */
|
|
120
|
+
const itemType = (properties[name] as { items?: { type?: string } } | undefined)?.items
|
|
121
|
+
?.type
|
|
122
|
+
const element = coerceScalar(value, itemType)
|
|
96
123
|
const existing = args[name]
|
|
97
|
-
args[name] = Array.isArray(existing) ? [...existing,
|
|
124
|
+
args[name] = Array.isArray(existing) ? [...existing, element] : [element]
|
|
98
125
|
continue
|
|
99
126
|
}
|
|
100
127
|
args[name] = value
|
|
@@ -64,9 +64,11 @@ export function* tokenizeArgvFlags(
|
|
|
64
64
|
: undefined
|
|
65
65
|
const negated = negatedName !== undefined && properties[negatedName]?.type === 'boolean'
|
|
66
66
|
const name = negated ? (negatedName as string) : literalName
|
|
67
|
-
// Boolean props and inline `--name=value` consume no following token.
|
|
67
|
+
// Boolean props and inline `--name=value` consume no following token. Pass any
|
|
68
|
+
// inline value through (`--flag=false`) so the parser can honour the RHS instead
|
|
69
|
+
// of the boolean always resolving to true.
|
|
68
70
|
if (properties[name]?.type === 'boolean') {
|
|
69
|
-
yield { name, negated }
|
|
71
|
+
yield { name, negated, value: inlineValue }
|
|
70
72
|
continue
|
|
71
73
|
}
|
|
72
74
|
if (inlineValue !== undefined) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// node:fs existsSync — cheap sync presence check, mirrors createPublicAssetServer
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
+
import { resolve, sep } from 'node:path'
|
|
3
4
|
import { Glob } from 'bun'
|
|
4
5
|
import { mimeForExtension } from '../server/runtime/mimeForExtension.ts'
|
|
5
6
|
import type { Assets } from '../server/runtime/types/Assets.ts'
|
|
@@ -81,8 +82,10 @@ export function createMcpResourceServer({
|
|
|
81
82
|
return undefined
|
|
82
83
|
}
|
|
83
84
|
const relativePath = uri.slice(URI_PREFIX.length)
|
|
84
|
-
// reject `..` traversal
|
|
85
|
-
|
|
85
|
+
// reject `..` traversal on EITHER separator — a backslash-joined `..\` escapes
|
|
86
|
+
// resourcesDir on Windows (its OS layer treats `\` as a separator regardless of
|
|
87
|
+
// how the JS string was built), so a `/`-only split lets it through.
|
|
88
|
+
if (relativePath.split(/[\\/]/).includes('..')) {
|
|
86
89
|
return undefined
|
|
87
90
|
}
|
|
88
91
|
if (mcpResources) {
|
|
@@ -92,6 +95,14 @@ export function createMcpResourceServer({
|
|
|
92
95
|
}
|
|
93
96
|
return contentsFor(relativePath, Bun.gunzipSync(compressed))
|
|
94
97
|
}
|
|
98
|
+
// Belt-and-braces for the on-disk branch: resolve the target and confirm it
|
|
99
|
+
// stays inside resourcesDir, catching any traversal the segment check missed
|
|
100
|
+
// (absolute paths, platform separator quirks).
|
|
101
|
+
const root = resolve(resourcesDir)
|
|
102
|
+
const target = resolve(root, relativePath)
|
|
103
|
+
if (target !== root && !target.startsWith(root + sep)) {
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
95
106
|
const file = Bun.file(`${resourcesDir}/${relativePath}`)
|
|
96
107
|
if (!(await file.exists())) {
|
|
97
108
|
return undefined
|
package/src/lib/mcp/mcpTools.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { dispatchRpcInProcess } from '../server/rpc/dispatchRpcInProcess.ts'
|
|
2
|
+
import {
|
|
3
|
+
resolveInputJsonSchema,
|
|
4
|
+
resolveOutputJsonSchema,
|
|
5
|
+
} from '../server/rpc/resolveRpcJsonSchema.ts'
|
|
2
6
|
import { rpcRegistry } from '../server/rpc/rpcRegistry.ts'
|
|
3
7
|
import type { RpcRegistryEntry } from '../server/rpc/types/RpcRegistryEntry.ts'
|
|
4
8
|
import { socketOperations } from '../server/sockets/socketOperations.ts'
|
|
@@ -37,7 +41,7 @@ RPCs enumerate first, so on a name collision the rpc tool wins.
|
|
|
37
41
|
function mcpToolRefs(): McpToolRef[] {
|
|
38
42
|
const refs: McpToolRef[] = []
|
|
39
43
|
for (const entry of rpcRegistry.values()) {
|
|
40
|
-
if (!entry.clients.mcp) {
|
|
44
|
+
if (!entry.remote.clients.mcp) {
|
|
41
45
|
continue
|
|
42
46
|
}
|
|
43
47
|
refs.push({ kind: 'rpc', name: commandNameForUrl(entry.remote.url), entry })
|
|
@@ -63,7 +67,9 @@ from a write.
|
|
|
63
67
|
*/
|
|
64
68
|
function describeTool(ref: McpToolRef): ToolDescriptor {
|
|
65
69
|
if (ref.kind === 'rpc') {
|
|
66
|
-
|
|
70
|
+
// The declared inputSchema VALIDATOR overrides the build-projected input-type schema
|
|
71
|
+
// (ADR-0030 input side); with neither the tool advertises the opaque fallback, as before.
|
|
72
|
+
const inputSchema = resolveInputJsonSchema(ref.entry) ?? jsonSchemaForSchema(undefined)
|
|
67
73
|
const tool: ToolDescriptor = {
|
|
68
74
|
name: ref.name,
|
|
69
75
|
description:
|
|
@@ -72,8 +78,11 @@ function describeTool(ref: McpToolRef): ToolDescriptor {
|
|
|
72
78
|
inputSchema,
|
|
73
79
|
annotations: annotationsForMethod(ref.entry.remote.method),
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
// The declared outputSchema VALIDATOR overrides the build-projected return-type schema
|
|
82
|
+
// (ADR-0030 D2); with neither the tool advertises no outputSchema, exactly as before.
|
|
83
|
+
const outputSchema = resolveOutputJsonSchema(ref.entry)
|
|
84
|
+
if (outputSchema) {
|
|
85
|
+
tool.outputSchema = outputSchema
|
|
77
86
|
}
|
|
78
87
|
return tool
|
|
79
88
|
}
|
|
@@ -149,8 +158,8 @@ function callSocketTool(
|
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
160
|
try {
|
|
152
|
-
//
|
|
153
|
-
entry.socket.
|
|
161
|
+
// publish() validates the payload against the socket schema and throws on failure.
|
|
162
|
+
entry.socket.publish(args)
|
|
154
163
|
} catch (error) {
|
|
155
164
|
return textResult(messageFromError(error), true)
|
|
156
165
|
}
|
|
@@ -4,6 +4,12 @@ import { messageFromError } from '../shared/messageFromError.ts'
|
|
|
4
4
|
import { responseErrorText } from '../shared/responseErrorText.ts'
|
|
5
5
|
import { streamResponse } from '../shared/streamResponse.ts'
|
|
6
6
|
|
|
7
|
+
/* MCP `tools/call` is request/response, not a streaming transport, so a genuinely live
|
|
8
|
+
stream must be bounded: return what arrived within these limits (with a truncation note)
|
|
9
|
+
rather than buffering forever and never answering. */
|
|
10
|
+
const MAX_STREAM_FRAMES = 1000
|
|
11
|
+
const STREAM_TOOL_TIMEOUT_MS = 30_000
|
|
12
|
+
|
|
7
13
|
// Frames a value as MCP text content — strings verbatim, everything else as JSON.
|
|
8
14
|
// An undefined body (204 / empty) becomes '' — `JSON.stringify(undefined)` is the
|
|
9
15
|
// value `undefined`, not a string, which would make an invalid text content block.
|
|
@@ -38,26 +44,48 @@ export async function toolResultFromResponse(response: Response): Promise<Record
|
|
|
38
44
|
|
|
39
45
|
if (isStreamingResponse(response)) {
|
|
40
46
|
const frames: unknown[] = []
|
|
47
|
+
// Set when WE stop the stream (cap/timeout), so the iterator's resulting end/throw
|
|
48
|
+
// reads as truncation, not a stream error.
|
|
49
|
+
let truncated: string | undefined
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
truncated = `stream truncated after ${STREAM_TOOL_TIMEOUT_MS}ms — MCP tools/call is request/response, not a live stream`
|
|
52
|
+
void response.body?.cancel()
|
|
53
|
+
}, STREAM_TOOL_TIMEOUT_MS)
|
|
41
54
|
try {
|
|
42
55
|
for await (const frame of streamResponse(response)) {
|
|
43
56
|
frames.push(frame)
|
|
57
|
+
if (frames.length >= MAX_STREAM_FRAMES) {
|
|
58
|
+
truncated = `stream truncated at ${MAX_STREAM_FRAMES} frames`
|
|
59
|
+
void response.body?.cancel()
|
|
60
|
+
break
|
|
61
|
+
}
|
|
44
62
|
}
|
|
45
63
|
} catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
/* A real mid-stream error (we didn't cancel) surfaces as isError; a throw caused
|
|
65
|
+
by our own cancel falls through to the truncated result below. */
|
|
66
|
+
if (truncated === undefined) {
|
|
67
|
+
clearTimeout(timer)
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{ type: 'text', text: frames.map(asText).join('\n') },
|
|
71
|
+
{ type: 'text', text: `stream error: ${messageFromError(error)}` },
|
|
72
|
+
],
|
|
73
|
+
structuredContent: { frames },
|
|
74
|
+
isError: true,
|
|
75
|
+
}
|
|
56
76
|
}
|
|
77
|
+
} finally {
|
|
78
|
+
clearTimeout(timer)
|
|
79
|
+
}
|
|
80
|
+
const content: { type: 'text'; text: string }[] = [
|
|
81
|
+
{ type: 'text', text: frames.map(asText).join('\n') },
|
|
82
|
+
]
|
|
83
|
+
if (truncated !== undefined) {
|
|
84
|
+
content.push({ type: 'text', text: truncated })
|
|
57
85
|
}
|
|
58
86
|
return {
|
|
59
|
-
content
|
|
60
|
-
structuredContent: { frames },
|
|
87
|
+
content,
|
|
88
|
+
structuredContent: truncated === undefined ? { frames } : { frames, truncated },
|
|
61
89
|
}
|
|
62
90
|
}
|
|
63
91
|
|
package/src/lib/server/error.ts
CHANGED
|
@@ -93,15 +93,15 @@ expose the error on `rpc.isError(e, 'name')` (`.kind` and typed `.data`). No
|
|
|
93
93
|
(a const can't read its own variable name); `schema` types `.data` and is never
|
|
94
94
|
validated at runtime here.
|
|
95
95
|
*/
|
|
96
|
-
function typed<Name extends string, Schema extends StandardSchemaV1>(
|
|
96
|
+
function typed<Name extends string, Status extends number, Schema extends StandardSchemaV1>(
|
|
97
97
|
name: Name,
|
|
98
|
-
status:
|
|
98
|
+
status: Status,
|
|
99
99
|
schema: Schema,
|
|
100
|
-
): (data: StandardSchemaV1.InferInput<Schema>) => TypedError<Name, { status:
|
|
101
|
-
function typed<Name extends string>(
|
|
100
|
+
): (data: StandardSchemaV1.InferInput<Schema>) => TypedError<Name, { status: Status; data: Schema }>
|
|
101
|
+
function typed<Name extends string, Status extends number>(
|
|
102
102
|
name: Name,
|
|
103
|
-
status:
|
|
104
|
-
): () => TypedError<Name, { status:
|
|
103
|
+
status: Status,
|
|
104
|
+
): () => TypedError<Name, { status: Status; data?: undefined }>
|
|
105
105
|
function typed(name: string, status: number, _schema?: StandardSchemaV1) {
|
|
106
106
|
return (data?: unknown) => typedErrorResponse(name, status, data)
|
|
107
107
|
}
|
package/src/lib/server/json.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { NO_STORE } from '../shared/CACHE_CONTROL_VALUES.ts'
|
|
2
|
+
import { encodeWireBody } from '../shared/encodeWireBody.ts'
|
|
2
3
|
import type { TypedResponse } from './rpc/types/TypedResponse.ts'
|
|
3
4
|
import { withResponseDefaults } from './runtime/withResponseDefaults.ts'
|
|
4
5
|
|
|
6
|
+
/* The Content-Type `Response.json` sets — replicated here because the wire-encode step serializes
|
|
7
|
+
with a replacer, so the body is built by hand rather than through `Response.json`. Seeds the
|
|
8
|
+
default header set so an explicit caller `content-type` still wins. */
|
|
9
|
+
const JSON_CONTENT_TYPE = 'application/json;charset=utf-8'
|
|
10
|
+
|
|
5
11
|
/*
|
|
6
12
|
JSON Response with rpc-friendly defaults — same shape as
|
|
7
13
|
`Response.json(data, init)`, except `Cache-Control: no-store` is set
|
|
@@ -36,8 +42,16 @@ export function json<T>(data: T, init?: ResponseInit): TypedResponse<T> {
|
|
|
36
42
|
withResponseDefaults(init, { 'Cache-Control': NO_STORE }, 204),
|
|
37
43
|
) as TypedResponse<T>
|
|
38
44
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
/* Wire-encode the body (ADR-0029 output path) through the shared honest-JSON encoder: a
|
|
46
|
+
value-directed replacer rewrites a `Set` → array, a `Map` → `[K,V]` entries, and a `bigint`
|
|
47
|
+
→ digit string, so a structured return crosses as honest JSON rather than a lost `{}` or a
|
|
48
|
+
500 (plain `Response.json` throws on a bigint). A `Date` already rides as an ISO string via
|
|
49
|
+
its native `toJSON`. */
|
|
50
|
+
return new Response(
|
|
51
|
+
encodeWireBody(data),
|
|
52
|
+
withResponseDefaults(init, {
|
|
53
|
+
'Cache-Control': NO_STORE,
|
|
54
|
+
'Content-Type': JSON_CONTENT_TYPE,
|
|
55
|
+
}),
|
|
42
56
|
) as TypedResponse<T>
|
|
43
57
|
}
|