@xyagent/cli 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +860 -0
- package/bin/agentlink +8585 -0
- package/bin/agentlink-agent +7 -0
- package/bin/agentlink-mcp-stdio +173 -0
- package/docs/LOGGING.md +159 -0
- package/package.json +38 -0
- package/src/core.mjs +320 -0
- package/src/tunnel.mjs +291 -0
- package/src/tunnel_proxy.mjs +913 -0
- package/src/tunnel_service.mjs +699 -0
- package/src/tunnel_state.mjs +301 -0
- package/src/unified_producer.mjs +259 -0
- package/src-ext/README.md +75 -0
- package/src-ext/bin.mjs +128 -0
- package/src-ext/commands/agent.mjs +66 -0
- package/src-ext/commands/help.mjs +31 -0
- package/src-ext/commands/pair.mjs +455 -0
- package/src-ext/commands/reload.mjs +78 -0
- package/src-ext/commands/service.mjs +64 -0
- package/src-ext/core/accountResolver.mjs +137 -0
- package/src-ext/core/activeRuns.mjs +76 -0
- package/src-ext/core/agentlinkToolsBootstrap.mjs +75 -0
- package/src-ext/core/agentlinkToolsRegistry.mjs +323 -0
- package/src-ext/core/agentlinkToolsServer.mjs +109 -0
- package/src-ext/core/approvalGateway.mjs +132 -0
- package/src-ext/core/attachAuditLog.mjs +79 -0
- package/src-ext/core/attachDenylist.mjs +128 -0
- package/src-ext/core/autoTunnelDetector.mjs +98 -0
- package/src-ext/core/autoTunnelGateway.mjs +393 -0
- package/src-ext/core/autoTunnelWiring.mjs +184 -0
- package/src-ext/core/boundedMap.mjs +98 -0
- package/src-ext/core/bridgeAttachAutoScanner.mjs +237 -0
- package/src-ext/core/bridgeHooks.mjs +168 -0
- package/src-ext/core/bridgeSelfUninstall.mjs +133 -0
- package/src-ext/core/claudeTrust.mjs +74 -0
- package/src-ext/core/codexTrust.mjs +77 -0
- package/src-ext/core/cosUploadClient.mjs +326 -0
- package/src-ext/core/createWorkspace.mjs +170 -0
- package/src-ext/core/daemonRestart.mjs +49 -0
- package/src-ext/core/defaultWorkspace.mjs +65 -0
- package/src-ext/core/ensureGitRepo.mjs +57 -0
- package/src-ext/core/hermesProfile.mjs +75 -0
- package/src-ext/core/interruptGateway.mjs +119 -0
- package/src-ext/core/interruptRunListener.mjs +106 -0
- package/src-ext/core/mcpRuntimeFanout.mjs +236 -0
- package/src-ext/core/mcpStdioClient.mjs +316 -0
- package/src-ext/core/openclawGatewayClient.mjs +230 -0
- package/src-ext/core/pairCodeClient.mjs +331 -0
- package/src-ext/core/pairInventory.mjs +59 -0
- package/src-ext/core/pathExpand.mjs +34 -0
- package/src-ext/core/preprocessAttachments.mjs +113 -0
- package/src-ext/core/relayWorker.mjs +284 -0
- package/src-ext/core/relayWorkerHttp.mjs +58 -0
- package/src-ext/core/scanWorkspaces.mjs +842 -0
- package/src-ext/core/sessionSlot.mjs +70 -0
- package/src-ext/core/stateMachine.mjs +85 -0
- package/src-ext/core/toolsServerLifecycle.mjs +169 -0
- package/src-ext/core/unifiedDispatchHandler.mjs +439 -0
- package/src-ext/core/usageReporter.mjs +207 -0
- package/src-ext/openclaw-plugin/envelope-builder.cjs +619 -0
- package/src-ext/openclaw-plugin/index.cjs +1919 -0
- package/src-ext/openclaw-plugin/openclaw.plugin.json +20 -0
- package/src-ext/openclaw-plugin/package.json +12 -0
- package/src-ext/openclaw-plugin/relay-defaults.json +3 -0
- package/src-ext/openclaw-plugin/todoTranslatorUtils.cjs +101 -0
- package/src-ext/runtime/_shared/cliExec.mjs +138 -0
- package/src-ext/runtime/_shared/cosTextRefs.mjs +120 -0
- package/src-ext/runtime/_shared/fetchAndBase64Encode.mjs +52 -0
- package/src-ext/runtime/_shared/relayObjectToBlock.mjs +307 -0
- package/src-ext/runtime/_shared/sessionPicker.mjs +418 -0
- package/src-ext/runtime/_shared/slashCommandRouter.mjs +1346 -0
- package/src-ext/runtime/_shared/todoTranslatorUtils.mjs +94 -0
- package/src-ext/runtime/claude/askUserQuestionTranslator.mjs +241 -0
- package/src-ext/runtime/claude/customCommandsCollector.mjs +132 -0
- package/src-ext/runtime/claude/handleRequest.mjs +1418 -0
- package/src-ext/runtime/claude/index.mjs +160 -0
- package/src-ext/runtime/claude/internalCommandNames.mjs +58 -0
- package/src-ext/runtime/claude/launcher.mjs +313 -0
- package/src-ext/runtime/claude/mcpConfigAdapter.mjs +174 -0
- package/src-ext/runtime/claude/mcpConfigWriter.mjs +57 -0
- package/src-ext/runtime/claude/modelContextWindow.mjs +30 -0
- package/src-ext/runtime/claude/modelScanner.mjs +257 -0
- package/src-ext/runtime/claude/permissionInterruptBlock.mjs +86 -0
- package/src-ext/runtime/claude/permissionPromptServer.mjs +170 -0
- package/src-ext/runtime/claude/permissionPromptServerWorker.mjs +214 -0
- package/src-ext/runtime/claude/preflight.mjs +62 -0
- package/src-ext/runtime/claude/stdoutParser.mjs +365 -0
- package/src-ext/runtime/claude/todoTranslator.mjs +66 -0
- package/src-ext/runtime/claude/usageProbe.mjs +576 -0
- package/src-ext/runtime/claude/usageProbeStrip.mjs +14 -0
- package/src-ext/runtime/codex/codexCustomCommandsCollector.mjs +87 -0
- package/src-ext/runtime/codex/elicitationHandler.mjs +91 -0
- package/src-ext/runtime/codex/eventMapper.mjs +154 -0
- package/src-ext/runtime/codex/handleRequest.mjs +1152 -0
- package/src-ext/runtime/codex/index.mjs +142 -0
- package/src-ext/runtime/codex/mcpConfigAdapter.mjs +155 -0
- package/src-ext/runtime/codex/modelScanner.mjs +305 -0
- package/src-ext/runtime/codex/planNotificationHandler.mjs +61 -0
- package/src-ext/runtime/codex/preflight.mjs +67 -0
- package/src-ext/runtime/hermes/commandBackend.mjs +117 -0
- package/src-ext/runtime/hermes/envSetup.mjs +229 -0
- package/src-ext/runtime/hermes/gatewayManager.mjs +625 -0
- package/src-ext/runtime/hermes/handleRequest.mjs +1257 -0
- package/src-ext/runtime/hermes/httpBackend.mjs +219 -0
- package/src-ext/runtime/hermes/index.mjs +196 -0
- package/src-ext/runtime/hermes/mcpConfigAdapter.mjs +189 -0
- package/src-ext/runtime/hermes/preflight.mjs +359 -0
- package/src-ext/runtime/hermes/responsesBackend.mjs +276 -0
- package/src-ext/runtime/hermes/sessionStore.mjs +78 -0
- package/src-ext/runtime/hermes/todoTranslator.mjs +90 -0
- package/src-ext/runtime/hermes/workspaceRpc.mjs +124 -0
- package/src-ext/runtime/openclaw/buildOpenclawDaemonInput.mjs +143 -0
- package/src-ext/runtime/openclaw/mcpConfigAdapter.mjs +91 -0
- package/src-ext/runtime/openclaw/openclawConfig.mjs +145 -0
- package/src-ext/runtime/picoclaw/constants.mjs +39 -0
- package/src-ext/runtime/picoclaw/handleRequest.mjs +289 -0
- package/src-ext/runtime/picoclaw/index.mjs +314 -0
- package/src-ext/runtime/picoclaw/pairFlow.mjs +224 -0
- package/src-ext/runtime/picoclaw/state.mjs +78 -0
- package/src-ext/runtime/picoclaw/todoTranslator.mjs +67 -0
- package/src-ext/runtime/picoclaw/translator.mjs +272 -0
- package/src-ext/runtime/picoclaw/wsClient.mjs +129 -0
- package/src-ext/service/serviceManager.mjs +521 -0
- package/src-shared/README.md +44 -0
- package/src-shared/chat_push_normalizer.mjs +113 -0
- package/src-shared/correlation.mjs +25 -0
- package/src-shared/envelope_builder.mjs +612 -0
- package/src-shared/logger.mjs +167 -0
- package/src-shared/relay-defaults.json +3 -0
- package/src-shared/relay_revoke.mjs +46 -0
- package/src-shared/vendor/README.md +60 -0
- package/src-shared/vendor/smol-toml/LICENSE +24 -0
- package/src-shared/vendor/smol-toml/README.md +236 -0
- package/src-shared/vendor/smol-toml/VENDORED.md +37 -0
- package/src-shared/vendor/smol-toml/dist/date.d.ts +41 -0
- package/src-shared/vendor/smol-toml/dist/date.js +127 -0
- package/src-shared/vendor/smol-toml/dist/error.d.ts +38 -0
- package/src-shared/vendor/smol-toml/dist/error.js +63 -0
- package/src-shared/vendor/smol-toml/dist/extract.d.ts +30 -0
- package/src-shared/vendor/smol-toml/dist/extract.js +109 -0
- package/src-shared/vendor/smol-toml/dist/index.cjs +900 -0
- package/src-shared/vendor/smol-toml/dist/index.d.ts +43 -0
- package/src-shared/vendor/smol-toml/dist/index.js +33 -0
- package/src-shared/vendor/smol-toml/dist/parse.d.ts +37 -0
- package/src-shared/vendor/smol-toml/dist/parse.js +148 -0
- package/src-shared/vendor/smol-toml/dist/primitive.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/primitive.js +178 -0
- package/src-shared/vendor/smol-toml/dist/stringify.d.ts +31 -0
- package/src-shared/vendor/smol-toml/dist/stringify.js +163 -0
- package/src-shared/vendor/smol-toml/dist/struct.d.ts +32 -0
- package/src-shared/vendor/smol-toml/dist/struct.js +194 -0
- package/src-shared/vendor/smol-toml/dist/util.d.ts +42 -0
- package/src-shared/vendor/smol-toml/dist/util.js +100 -0
- package/src-shared/vendor/smol-toml/package.json +54 -0
- package/src-shared/vendor/yaml/LICENSE +13 -0
- package/src-shared/vendor/yaml/README.md +155 -0
- package/src-shared/vendor/yaml/VENDORED.md +29 -0
- package/src-shared/vendor/yaml/bin.mjs +11 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-collection.js +88 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-doc.js +42 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-node.js +92 -0
- package/src-shared/vendor/yaml/browser/dist/compose/compose-scalar.js +80 -0
- package/src-shared/vendor/yaml/browser/dist/compose/composer.js +217 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-map.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-seq.js +47 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-end.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-collection.js +203 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-scalar.js +223 -0
- package/src-shared/vendor/yaml/browser/dist/compose/resolve-props.js +148 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-contains-newline.js +34 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-empty-scalar-position.js +27 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/compose/util-map-includes.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/doc/Document.js +334 -0
- package/src-shared/vendor/yaml/browser/dist/doc/anchors.js +72 -0
- package/src-shared/vendor/yaml/browser/dist/doc/applyReviver.js +55 -0
- package/src-shared/vendor/yaml/browser/dist/doc/createNode.js +89 -0
- package/src-shared/vendor/yaml/browser/dist/doc/directives.js +176 -0
- package/src-shared/vendor/yaml/browser/dist/errors.js +57 -0
- package/src-shared/vendor/yaml/browser/dist/index.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/log.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Alias.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Collection.js +147 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Node.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Pair.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/Scalar.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLMap.js +144 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/addPairToJSMap.js +104 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/identity.js +36 -0
- package/src-shared/vendor/yaml/browser/dist/nodes/toJS.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-scalar.js +214 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-stringify.js +61 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst-visit.js +97 -0
- package/src-shared/vendor/yaml/browser/dist/parse/cst.js +98 -0
- package/src-shared/vendor/yaml/browser/dist/parse/lexer.js +717 -0
- package/src-shared/vendor/yaml/browser/dist/parse/line-counter.js +39 -0
- package/src-shared/vendor/yaml/browser/dist/parse/parser.js +954 -0
- package/src-shared/vendor/yaml/browser/dist/public-api.js +99 -0
- package/src-shared/vendor/yaml/browser/dist/schema/Schema.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/map.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/null.js +15 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/seq.js +17 -0
- package/src-shared/vendor/yaml/browser/dist/schema/common/string.js +14 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/bool.js +19 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/float.js +43 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/int.js +38 -0
- package/src-shared/vendor/yaml/browser/dist/schema/core/schema.js +23 -0
- package/src-shared/vendor/yaml/browser/dist/schema/json/schema.js +62 -0
- package/src-shared/vendor/yaml/browser/dist/schema/tags.js +83 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/binary.js +66 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/schema.js +37 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
- package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringify.js +124 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyCollection.js +143 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyComment.js +20 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyNumber.js +24 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyPair.js +150 -0
- package/src-shared/vendor/yaml/browser/dist/stringify/stringifyString.js +328 -0
- package/src-shared/vendor/yaml/browser/dist/util.js +11 -0
- package/src-shared/vendor/yaml/browser/dist/visit.js +233 -0
- package/src-shared/vendor/yaml/browser/index.js +5 -0
- package/src-shared/vendor/yaml/browser/package.json +3 -0
- package/src-shared/vendor/yaml/dist/cli.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/cli.mjs +199 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/compose-collection.js +90 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/compose-doc.js +44 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/compose/compose-node.js +95 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/compose/compose-scalar.js +82 -0
- package/src-shared/vendor/yaml/dist/compose/composer.d.ts +62 -0
- package/src-shared/vendor/yaml/dist/compose/composer.js +221 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.js +200 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-end.js +39 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.js +205 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/compose/resolve-props.js +150 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-contains-newline.js +36 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.js +29 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.js +17 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/compose/util-map-includes.js +19 -0
- package/src-shared/vendor/yaml/dist/doc/Document.d.ts +141 -0
- package/src-shared/vendor/yaml/dist/doc/Document.js +336 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.d.ts +24 -0
- package/src-shared/vendor/yaml/dist/doc/anchors.js +77 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/doc/applyReviver.js +57 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/doc/createNode.js +91 -0
- package/src-shared/vendor/yaml/dist/doc/directives.d.ts +49 -0
- package/src-shared/vendor/yaml/dist/doc/directives.js +178 -0
- package/src-shared/vendor/yaml/dist/errors.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/errors.js +62 -0
- package/src-shared/vendor/yaml/dist/index.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/index.js +50 -0
- package/src-shared/vendor/yaml/dist/log.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/log.js +17 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/nodes/Alias.js +103 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.d.ts +73 -0
- package/src-shared/vendor/yaml/dist/nodes/Collection.js +151 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.d.ts +47 -0
- package/src-shared/vendor/yaml/dist/nodes/Node.js +40 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/nodes/Pair.js +39 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.d.ts +42 -0
- package/src-shared/vendor/yaml/dist/nodes/Scalar.js +27 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.d.ts +53 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLMap.js +147 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
- package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.js +115 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.js +106 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.d.ts +23 -0
- package/src-shared/vendor/yaml/dist/nodes/identity.js +53 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.d.ts +27 -0
- package/src-shared/vendor/yaml/dist/nodes/toJS.js +39 -0
- package/src-shared/vendor/yaml/dist/options.d.ts +338 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.d.ts +64 -0
- package/src-shared/vendor/yaml/dist/parse/cst-scalar.js +218 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.d.ts +8 -0
- package/src-shared/vendor/yaml/dist/parse/cst-stringify.js +63 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.d.ts +39 -0
- package/src-shared/vendor/yaml/dist/parse/cst-visit.js +99 -0
- package/src-shared/vendor/yaml/dist/parse/cst.d.ts +108 -0
- package/src-shared/vendor/yaml/dist/parse/cst.js +112 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.d.ts +87 -0
- package/src-shared/vendor/yaml/dist/parse/lexer.js +719 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.d.ts +22 -0
- package/src-shared/vendor/yaml/dist/parse/line-counter.js +41 -0
- package/src-shared/vendor/yaml/dist/parse/parser.d.ts +84 -0
- package/src-shared/vendor/yaml/dist/parse/parser.js +958 -0
- package/src-shared/vendor/yaml/dist/public-api.d.ts +43 -0
- package/src-shared/vendor/yaml/dist/public-api.js +104 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.d.ts +18 -0
- package/src-shared/vendor/yaml/dist/schema/Schema.js +40 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/map.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/common/null.js +17 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/seq.js +19 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/common/string.js +16 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/bool.js +21 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/float.js +47 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/core/int.js +42 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/core/schema.js +25 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/json/schema.js +64 -0
- package/src-shared/vendor/yaml/dist/schema/json-schema.d.ts +69 -0
- package/src-shared/vendor/yaml/dist/schema/tags.d.ts +40 -0
- package/src-shared/vendor/yaml/dist/schema/tags.js +86 -0
- package/src-shared/vendor/yaml/dist/schema/types.d.ts +90 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.js +68 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.js +29 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.js +50 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.js +76 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.js +77 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.js +96 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
- package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
- package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.js +151 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.d.ts +21 -0
- package/src-shared/vendor/yaml/dist/stringify/stringify.js +127 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.js +145 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.d.ts +10 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyComment.js +24 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.js +87 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.js +26 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.d.ts +3 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyPair.js +152 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.d.ts +9 -0
- package/src-shared/vendor/yaml/dist/stringify/stringifyString.js +330 -0
- package/src-shared/vendor/yaml/dist/test-events.d.ts +4 -0
- package/src-shared/vendor/yaml/dist/test-events.js +134 -0
- package/src-shared/vendor/yaml/dist/util.d.ts +12 -0
- package/src-shared/vendor/yaml/dist/util.js +28 -0
- package/src-shared/vendor/yaml/dist/visit.d.ts +102 -0
- package/src-shared/vendor/yaml/dist/visit.js +236 -0
- package/src-shared/vendor/yaml/package.json +96 -0
- package/src-shared/vendor/yaml/util.js +2 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// attachDenylist.mjs — agentlink_attach_file 工具的隐私防线
|
|
2
|
+
//
|
|
3
|
+
// 在 CLI 读文件之前先匹配候选 path,命中即拒绝(agent 拿 reason="denied",
|
|
4
|
+
// 不发任何字节到 relay / COS)。**故意不接受用户配置弱化**——denylist 永远
|
|
5
|
+
// 优先,是不可越权的硬底。原因:即便 agent 是用户授权的,AI 也可能错把
|
|
6
|
+
// `~/.ssh/id_rsa` 之类的敏感文件 attach 出去;这条防线必须在用户级别之上。
|
|
7
|
+
//
|
|
8
|
+
// spec ref: openspec/changes/host-file-cos-attachment/specs/host-file-attach-cos/spec.md
|
|
9
|
+
// Requirement: denylist enforcement
|
|
10
|
+
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
|
|
13
|
+
// ── 模式定义(不可配置)─────────────────────────────────────────────
|
|
14
|
+
// 顺序:先看 basename 完全匹配,再看 basename 通配(.env.*),再看 segment 包含。
|
|
15
|
+
// 大小写:basename / segment 都做 lowercase 比对(macOS 默认大小写不敏感文件
|
|
16
|
+
// 系统,但 path 字符串本身保留大小写,所以这里显式 lower)。
|
|
17
|
+
|
|
18
|
+
// 完全匹配的 basename(不带扩展名通配)
|
|
19
|
+
const EXACT_BASENAMES = new Set([
|
|
20
|
+
".env",
|
|
21
|
+
".netrc",
|
|
22
|
+
".npmrc",
|
|
23
|
+
".pypirc",
|
|
24
|
+
"id_rsa",
|
|
25
|
+
"id_ed25519",
|
|
26
|
+
"id_ecdsa",
|
|
27
|
+
"id_dsa",
|
|
28
|
+
"credentials",
|
|
29
|
+
"authorized_keys",
|
|
30
|
+
"known_hosts",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// 前缀匹配的 basename:basename.startsWith(prefix + ".") 或 basename === prefix
|
|
34
|
+
const PREFIX_BASENAMES = [
|
|
35
|
+
".env", // .env.production / .env.local / .env.test ...
|
|
36
|
+
"id_rsa", // id_rsa.pub / id_rsa.bak ...
|
|
37
|
+
"id_ed25519",
|
|
38
|
+
"id_ecdsa",
|
|
39
|
+
"id_dsa",
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// 后缀(扩展名)匹配
|
|
43
|
+
const EXT_DENY = new Set([
|
|
44
|
+
".pem",
|
|
45
|
+
".key",
|
|
46
|
+
".pfx",
|
|
47
|
+
".p12",
|
|
48
|
+
".keystore",
|
|
49
|
+
".jks",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
// 任一 segment 命中即拒。segment 用 `/` 切;标准化后路径在 macOS 上是 absolute。
|
|
53
|
+
const SEGMENT_DENY = new Set([
|
|
54
|
+
".ssh",
|
|
55
|
+
".gnupg",
|
|
56
|
+
".aws",
|
|
57
|
+
".azure",
|
|
58
|
+
".kube",
|
|
59
|
+
".git", // 防 .git/config 之类暴露 remote URL / token
|
|
60
|
+
".gitconfig", // 不是目录但常被误当成目录,保险起见
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 检查 absolutePath 是否命中 denylist。
|
|
65
|
+
*
|
|
66
|
+
* 调用方约定:absolutePath 必须已经过 `fs.realpath` 解析 + `path.resolve`
|
|
67
|
+
* 规范化(消除 `..` traversal)。本函数不做 normalization,只做匹配——
|
|
68
|
+
* 把 normalization 留给 caller 是为了避免在这一层重复 IO。
|
|
69
|
+
*
|
|
70
|
+
* @param {string} absolutePath 已规范化的绝对路径
|
|
71
|
+
* @returns {{ denied: boolean, pattern?: string }}
|
|
72
|
+
* - denied=true 时 pattern 是命中的具体模式名(forensic / audit log 用)
|
|
73
|
+
* - denied=false 时 pattern 不出现
|
|
74
|
+
*/
|
|
75
|
+
export function checkDenylist(absolutePath) {
|
|
76
|
+
if (typeof absolutePath !== "string" || absolutePath === "") {
|
|
77
|
+
return { denied: true, pattern: "<empty-path>" };
|
|
78
|
+
}
|
|
79
|
+
// 防御性:caller 应该传 absolute 已 normalize 的;这里再保底一次。
|
|
80
|
+
const normalized = path.posix.normalize(absolutePath.split(path.sep).join("/"));
|
|
81
|
+
const lower = normalized.toLowerCase();
|
|
82
|
+
|
|
83
|
+
// 1) segment 命中
|
|
84
|
+
const segments = lower.split("/").filter((s) => s !== "");
|
|
85
|
+
for (const seg of segments) {
|
|
86
|
+
if (SEGMENT_DENY.has(seg)) {
|
|
87
|
+
return { denied: true, pattern: seg };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 2) basename 检查(exact + prefix + ext)
|
|
92
|
+
const basename = segments.length > 0 ? segments[segments.length - 1] : "";
|
|
93
|
+
if (basename === "") {
|
|
94
|
+
return { denied: false };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (EXACT_BASENAMES.has(basename)) {
|
|
98
|
+
return { denied: true, pattern: basename };
|
|
99
|
+
}
|
|
100
|
+
for (const prefix of PREFIX_BASENAMES) {
|
|
101
|
+
if (basename === prefix || basename.startsWith(prefix + ".")) {
|
|
102
|
+
return { denied: true, pattern: prefix + ".*" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 扩展名:basename 最后一个 `.` 之后
|
|
106
|
+
const dotIdx = basename.lastIndexOf(".");
|
|
107
|
+
if (dotIdx > 0 && dotIdx < basename.length - 1) {
|
|
108
|
+
const ext = basename.substring(dotIdx); // 含点
|
|
109
|
+
if (EXT_DENY.has(ext)) {
|
|
110
|
+
return { denied: true, pattern: "*" + ext };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { denied: false };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 暴露当前模式集合供测试 / 诊断(不修改)。
|
|
119
|
+
* @returns {{ exact: string[], prefix: string[], ext: string[], segment: string[] }}
|
|
120
|
+
*/
|
|
121
|
+
export function listDenylistPatterns() {
|
|
122
|
+
return {
|
|
123
|
+
exact: Array.from(EXACT_BASENAMES),
|
|
124
|
+
prefix: PREFIX_BASENAMES.slice(),
|
|
125
|
+
ext: Array.from(EXT_DENY),
|
|
126
|
+
segment: Array.from(SEGMENT_DENY),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// autoTunnelDetector — 从 AI 文本中扫描出 loopback / LAN URL,提取 host + port。
|
|
2
|
+
//
|
|
3
|
+
// 检测范围(spec bridge-auto-tunnel Decision 4 + Requirement "loopback 等价类"):
|
|
4
|
+
// loopback:127.0.0.1 / localhost / [::1],端口可省(http→80, https→443)
|
|
5
|
+
// LAN:10.0.0.0/8 / 172.16.0.0/12 / 192.168.0.0/16 / 0.0.0.0
|
|
6
|
+
//
|
|
7
|
+
// 设计要点:
|
|
8
|
+
// - 用单一锚定正则,**不**走 URL parser(避免 URL parser 的归一化把 path 改写)
|
|
9
|
+
// - URL 尾部(path / query / fragment)以空白或常见闭合字符() ] > " ' `)为边界,
|
|
10
|
+
// 避免在 markdown `[label](url)` 里把 `)` 吞进 URL
|
|
11
|
+
// - 返回值按文本中出现顺序,对 fullUrl 去重
|
|
12
|
+
// - port 范围 [1, 65535],其它(含 "0"、"65536+")丢弃
|
|
13
|
+
//
|
|
14
|
+
// 不在本模块做的事:tunnel 启动、文本替换、bridge_rewrites 元数据 —— 全由 gateway 协调。
|
|
15
|
+
|
|
16
|
+
const LOOPBACK_HOST = "(?:127\\.0\\.0\\.1|localhost|\\[::1\\])";
|
|
17
|
+
const LAN_HOST =
|
|
18
|
+
"(?:0\\.0\\.0\\.0" +
|
|
19
|
+
"|10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}" +
|
|
20
|
+
"|172\\.(?:1[6-9]|2\\d|3[01])\\.\\d{1,3}\\.\\d{1,3}" +
|
|
21
|
+
"|192\\.168\\.\\d{1,3}\\.\\d{1,3})";
|
|
22
|
+
|
|
23
|
+
// URL 尾部:可省略;若有,以 / ? # 起,止于空白或闭合标点。
|
|
24
|
+
// 不包含 ) ] > " ' ` 避免吞 markdown / HTML / 引号边界。
|
|
25
|
+
const URL_TAIL = "(?:[/?#][^\\s)\\]>\"'`]*)?";
|
|
26
|
+
|
|
27
|
+
function buildRegex(hostFragment) {
|
|
28
|
+
// 锚定:URL 前必须有非字母数字(或字符串开头)
|
|
29
|
+
// 锚定:URL 后由 URL_TAIL 自然终止
|
|
30
|
+
return new RegExp(
|
|
31
|
+
`(?<![A-Za-z0-9])(https?)://(${hostFragment})(?::(\\d{1,5}))?(${URL_TAIL})`,
|
|
32
|
+
"gi",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const LOOPBACK_RE = buildRegex(LOOPBACK_HOST);
|
|
37
|
+
const LAN_RE = buildRegex(LAN_HOST);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {{
|
|
41
|
+
* fullUrl: string, // 原文里出现的完整 URL 字符串(含 path/query/fragment 若有)
|
|
42
|
+
* scheme: string, // "http" | "https"(小写)
|
|
43
|
+
* host: string, // "127.0.0.1" / "localhost" / "[::1]" / "192.168.1.10" 等
|
|
44
|
+
* port: number, // 解析后的端口(含默认 80/443)
|
|
45
|
+
* suffix: string, // path + query + fragment(开头含 / ? 或 # 或为空串)
|
|
46
|
+
* }} DetectedUrl
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
function defaultPort(scheme) {
|
|
50
|
+
return scheme.toLowerCase() === "https" ? 443 : 80;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isValidPort(p) {
|
|
54
|
+
return Number.isInteger(p) && p >= 1 && p <= 65535;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function runRegex(regex, text) {
|
|
58
|
+
if (typeof text !== "string" || text.length === 0) return [];
|
|
59
|
+
const out = [];
|
|
60
|
+
const seen = new Set(); // dedup by fullUrl
|
|
61
|
+
regex.lastIndex = 0;
|
|
62
|
+
let m;
|
|
63
|
+
while ((m = regex.exec(text)) !== null) {
|
|
64
|
+
const [fullUrl, scheme, host, portRaw, suffix] = m;
|
|
65
|
+
const port = portRaw ? Number(portRaw) : defaultPort(scheme);
|
|
66
|
+
if (!isValidPort(port)) continue;
|
|
67
|
+
if (seen.has(fullUrl)) continue;
|
|
68
|
+
seen.add(fullUrl);
|
|
69
|
+
out.push({
|
|
70
|
+
fullUrl,
|
|
71
|
+
scheme: scheme.toLowerCase(),
|
|
72
|
+
host: host, // 保留原大小写('localhost' vs 'LocalHost'),替换时按原文匹配
|
|
73
|
+
port,
|
|
74
|
+
suffix: suffix || "",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} text
|
|
82
|
+
* @returns {DetectedUrl[]}
|
|
83
|
+
*/
|
|
84
|
+
export function detectLoopbackUrls(text) {
|
|
85
|
+
return runRegex(LOOPBACK_RE, text);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} text
|
|
90
|
+
* @returns {DetectedUrl[]}
|
|
91
|
+
*/
|
|
92
|
+
export function detectLanUrls(text) {
|
|
93
|
+
return runRegex(LAN_RE, text);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 测试 / debug 用:原始正则字符串
|
|
97
|
+
export const _LOOPBACK_REGEX_SRC = LOOPBACK_RE.source;
|
|
98
|
+
export const _LAN_REGEX_SRC = LAN_RE.source;
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
// autoTunnelGateway — bridge-auto-tunnel 的协调层。
|
|
2
|
+
//
|
|
3
|
+
// 职责:
|
|
4
|
+
// - 出向(handleOutgoing):扫描 content_block.completed text envelope,
|
|
5
|
+
// 命中 loopback URL → 调 tunnel API → 等待公网 URL(≤ tunnelTimeoutMs)→
|
|
6
|
+
// 原地替换 block.text、写入 extensions.bridge_rewrites、首次发说明 block;
|
|
7
|
+
// 命中 LAN URL → 仅追加说明 block,不启动 tunnel;
|
|
8
|
+
// >maxUrlsPerEnvelope 命中 → 前 N 个处理,余下追加 overflow 提示 block。
|
|
9
|
+
// - 入向(handleIncoming):识别 `/tunnel ls` / `/tunnel off <port>` 严格前缀,
|
|
10
|
+
// 本地执行后 reply 一条 bridge-origin text block,runtime 不感知。
|
|
11
|
+
//
|
|
12
|
+
// 失败模型:handleOutgoing / handleIncoming 抛错由 applyOutgoingHooks /
|
|
13
|
+
// applyIncomingHooks 包裹兜底(spec cli-unified-bridge delta),主链路安全。
|
|
14
|
+
//
|
|
15
|
+
// Schema 约束:envelope.schema.json 顶层 additionalProperties:false,允许的
|
|
16
|
+
// 自定义字段是 `extensions`(不是 `metadata`)。审计数据放 extensions 下,
|
|
17
|
+
// 否则 relay 会以 schema validation 拒收整个 envelope(HTTP 207),App 永远
|
|
18
|
+
// 看不到改写后的文本。
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
detectLoopbackUrls,
|
|
22
|
+
detectLanUrls,
|
|
23
|
+
} from "./autoTunnelDetector.mjs";
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// ULID-ish blk_ id(与 envelope_builder.newBlockId 同一字母表 / 长度,但这里独立
|
|
27
|
+
// 实现以避免循环依赖;正确性同等,纯随机 128bit 编码 26 Crockford base32 字符)。
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
30
|
+
function newBlockId() {
|
|
31
|
+
const bytes = new Uint8Array(16);
|
|
32
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
33
|
+
crypto.getRandomValues(bytes);
|
|
34
|
+
} else {
|
|
35
|
+
for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
36
|
+
}
|
|
37
|
+
let n = 0n;
|
|
38
|
+
for (const b of bytes) n = (n << 8n) | BigInt(b);
|
|
39
|
+
const chars = [];
|
|
40
|
+
for (let i = 0; i < 26; i++) {
|
|
41
|
+
chars.unshift(CROCKFORD[Number(n & 31n)]);
|
|
42
|
+
n >>= 5n;
|
|
43
|
+
}
|
|
44
|
+
return "blk_" + chars.join("");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// helpers
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
function noopLogger() {
|
|
51
|
+
return { info() {}, warn() {}, error() {}, child() { return this; } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildBridgeTextEnvelope(runId, text) {
|
|
55
|
+
return {
|
|
56
|
+
type: "content_block.completed",
|
|
57
|
+
run_id: runId,
|
|
58
|
+
payload: {
|
|
59
|
+
block: {
|
|
60
|
+
id: newBlockId(),
|
|
61
|
+
type: "text",
|
|
62
|
+
format: "markdown",
|
|
63
|
+
text,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
// 注意:envelope.schema.json 顶层 additionalProperties:false,自定义字段
|
|
67
|
+
// 必须放 extensions(不是 metadata)。
|
|
68
|
+
extensions: { origin: "bridge" },
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function withTimeout(p, ms, label) {
|
|
73
|
+
if (!ms || ms <= 0) return p;
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const t = setTimeout(() => {
|
|
76
|
+
reject(new Error(label || "timeout"));
|
|
77
|
+
}, ms);
|
|
78
|
+
t.unref?.();
|
|
79
|
+
Promise.resolve(p).then(
|
|
80
|
+
(v) => { clearTimeout(t); resolve(v); },
|
|
81
|
+
(e) => { clearTimeout(t); reject(e); },
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// strict prefix matchers for incoming /tunnel commands
|
|
87
|
+
const CMD_LS_RE = /^\/tunnel\s+ls$/;
|
|
88
|
+
const CMD_OFF_RE = /^\/tunnel\s+off\s+(\d{1,5})$/;
|
|
89
|
+
|
|
90
|
+
function extractFirstTextBlockText(input) {
|
|
91
|
+
if (typeof input === "string") return input;
|
|
92
|
+
if (Array.isArray(input)) {
|
|
93
|
+
const first = input[0];
|
|
94
|
+
if (first && first.type === "text" && typeof first.text === "string") return first.text;
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (input && Array.isArray(input.blocks)) {
|
|
98
|
+
const first = input.blocks[0];
|
|
99
|
+
if (first && first.type === "text" && typeof first.text === "string") return first.text;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Factory
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {{
|
|
110
|
+
* startTunnel?: (opts:{port:number}) => Promise<{ok:boolean, publicUrl?:string, reason?:string}>,
|
|
111
|
+
* queryTunnels?: () => Promise<{ tunnels: Array<{port:number, publicUrl?:string, status?:string}> }>,
|
|
112
|
+
* stopTunnel?: (opts:{port:number}) => Promise<{ok:boolean, reason?:string}>,
|
|
113
|
+
* logger?: any,
|
|
114
|
+
* maxUrlsPerEnvelope?: number,
|
|
115
|
+
* tunnelTimeoutMs?: number,
|
|
116
|
+
* }} [opts]
|
|
117
|
+
*
|
|
118
|
+
* 注入点 startTunnel / queryTunnels / stopTunnel:production 由调用方
|
|
119
|
+
* 在 bin/agentlink(或 src-ext/bin.mjs)注入实际实现(包装
|
|
120
|
+
* src/tunnel.mjs.runTunnelCommand 与 src/tunnel_state.mjs.readTunnelState);
|
|
121
|
+
* 测试中注入 stub。
|
|
122
|
+
*/
|
|
123
|
+
export function createAutoTunnelGateway(opts = {}) {
|
|
124
|
+
const logger = opts.logger ?? noopLogger();
|
|
125
|
+
const maxUrlsPerEnvelope = Number.isInteger(opts.maxUrlsPerEnvelope) && opts.maxUrlsPerEnvelope > 0
|
|
126
|
+
? opts.maxUrlsPerEnvelope : 3;
|
|
127
|
+
const tunnelTimeoutMs = Number.isInteger(opts.tunnelTimeoutMs) && opts.tunnelTimeoutMs > 0
|
|
128
|
+
? opts.tunnelTimeoutMs : 3000;
|
|
129
|
+
// state map 容量上限:长期运行 daemon 经过海量 thread 后避免内存单调增长。
|
|
130
|
+
// 触达即驱逐插入顺序最旧的 entry(Map / Set 保留插入顺序)。重启清空可接受。
|
|
131
|
+
const maxTrackedThreads = Number.isInteger(opts.maxTrackedThreads) && opts.maxTrackedThreads > 0
|
|
132
|
+
? opts.maxTrackedThreads : 10_000;
|
|
133
|
+
|
|
134
|
+
const startTunnel = opts.startTunnel ?? (async () => ({ ok: false, reason: "no_starter_injected" }));
|
|
135
|
+
const queryTunnels = opts.queryTunnels ?? (async () => ({ tunnels: [] }));
|
|
136
|
+
const stopTunnel = opts.stopTunnel ?? (async () => ({ ok: false, reason: "no_stopper_injected" }));
|
|
137
|
+
|
|
138
|
+
// state
|
|
139
|
+
const tunnelStarted = new Map(); // thread_id → Map<fullUrl, publicUrl>
|
|
140
|
+
const firstNoticeThreads = new Set(); // thread_id 已发过 first-notice
|
|
141
|
+
const lanNoticed = new Map(); // thread_id → Set<lanFullUrl>
|
|
142
|
+
|
|
143
|
+
// 触达容量上限就驱逐插入顺序最旧的一个 entry。
|
|
144
|
+
function evictIfFull(collection) {
|
|
145
|
+
if (collection.size > maxTrackedThreads) {
|
|
146
|
+
const oldest = collection.keys().next().value;
|
|
147
|
+
collection.delete(oldest);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// handleOutgoing
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
async function handleOutgoing(envelopes, ctx = {}) {
|
|
155
|
+
if (!Array.isArray(envelopes) || envelopes.length === 0) return envelopes;
|
|
156
|
+
const out = [];
|
|
157
|
+
for (const env of envelopes) {
|
|
158
|
+
let processed;
|
|
159
|
+
try {
|
|
160
|
+
processed = await processOne(env, ctx);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
logger.warn?.("auto_tunnel.process_err", "processOne threw, passing through original envelope", {
|
|
163
|
+
err: String(err?.message || err),
|
|
164
|
+
run_id: env?.run_id,
|
|
165
|
+
});
|
|
166
|
+
processed = [env];
|
|
167
|
+
}
|
|
168
|
+
out.push(...processed);
|
|
169
|
+
}
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function processOne(env, ctx) {
|
|
174
|
+
// 只对 content_block.completed + text 起反应
|
|
175
|
+
if (!env || env.type !== "content_block.completed") return [env];
|
|
176
|
+
const block = env.payload?.block;
|
|
177
|
+
if (!block || block.type !== "text") return [env];
|
|
178
|
+
const text = block.text;
|
|
179
|
+
if (typeof text !== "string" || text.length === 0) return [env];
|
|
180
|
+
|
|
181
|
+
const thread_id = ctx?.thread_id ?? null;
|
|
182
|
+
const run_id = env.run_id || "";
|
|
183
|
+
|
|
184
|
+
const loopback = detectLoopbackUrls(text);
|
|
185
|
+
const lan = detectLanUrls(text);
|
|
186
|
+
if (loopback.length === 0 && lan.length === 0) return [env];
|
|
187
|
+
|
|
188
|
+
const processable = loopback.slice(0, maxUrlsPerEnvelope);
|
|
189
|
+
const overflow = loopback.slice(maxUrlsPerEnvelope);
|
|
190
|
+
|
|
191
|
+
// 解析每个 URL → publicUrl。
|
|
192
|
+
//
|
|
193
|
+
// 关键:先按 port 去重 —— 同 envelope 内 N 个 URL 共享同一 port(如
|
|
194
|
+
// `http://127.0.0.1:8765` 与 `http://127.0.0.1:8765/api` 共享 8765)时,
|
|
195
|
+
// 必须只调一次 startTunnel,不能并行起 N 个 runTunnelCommand 对同 port
|
|
196
|
+
// 互相竞争(race condition:后写的 state 文件会用新 slug 顶掉先写的,
|
|
197
|
+
// 导致先写的 publicUrl 失效)。
|
|
198
|
+
const cache = thread_id ? tunnelStarted.get(thread_id) : null;
|
|
199
|
+
|
|
200
|
+
// 按 port 聚合:port → { urls, cachedPublicUrl? }
|
|
201
|
+
const byPort = new Map();
|
|
202
|
+
for (const u of processable) {
|
|
203
|
+
if (!byPort.has(u.port)) {
|
|
204
|
+
// 取该 port 任一已缓存的 fullUrl 对应的 publicUrl(同 port 跨 path
|
|
205
|
+
// 共享 publicUrl,因为 tunnel 是 per-port 的)
|
|
206
|
+
let cachedPublicUrl = null;
|
|
207
|
+
if (cache) {
|
|
208
|
+
for (const cachedFullUrl of cache.keys()) {
|
|
209
|
+
// 简化:detector 保证 fullUrl 形如 scheme://host:port[suffix],
|
|
210
|
+
// 通过把 cached fullUrl 的 port 也匹配出来即可;这里偷懒,直接看
|
|
211
|
+
// u.fullUrl 在 cache 中是否命中(同 fullUrl 命中是最常见情况)
|
|
212
|
+
void cachedFullUrl;
|
|
213
|
+
}
|
|
214
|
+
cachedPublicUrl = cache.get(u.fullUrl) ?? null;
|
|
215
|
+
}
|
|
216
|
+
byPort.set(u.port, { urls: [u], cachedPublicUrl });
|
|
217
|
+
} else {
|
|
218
|
+
byPort.get(u.port).urls.push(u);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 每个 port 只启动一次 tunnel
|
|
223
|
+
const portResults = new Map(); // port → { ok, publicUrl?, reason?, fromCache }
|
|
224
|
+
await Promise.all(
|
|
225
|
+
[...byPort.entries()].map(async ([port, { cachedPublicUrl }]) => {
|
|
226
|
+
if (cachedPublicUrl) {
|
|
227
|
+
portResults.set(port, { ok: true, publicUrl: cachedPublicUrl, fromCache: true });
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
const r = await withTimeout(
|
|
232
|
+
startTunnel({ port }),
|
|
233
|
+
tunnelTimeoutMs,
|
|
234
|
+
"tunnel_start_timeout",
|
|
235
|
+
);
|
|
236
|
+
if (r && r.ok && r.publicUrl) {
|
|
237
|
+
portResults.set(port, { ok: true, publicUrl: r.publicUrl, fromCache: false });
|
|
238
|
+
} else {
|
|
239
|
+
portResults.set(port, { ok: false, reason: r?.reason || "no_public_url" });
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
portResults.set(port, { ok: false, reason: String(err?.message || err) });
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// 把 port 级别结果展开回每个 URL(同 port 下的 N 个 URL 共享同一结果)
|
|
248
|
+
const results = processable.map((u) => {
|
|
249
|
+
const pr = portResults.get(u.port);
|
|
250
|
+
if (pr.ok) {
|
|
251
|
+
if (thread_id) {
|
|
252
|
+
const m = tunnelStarted.get(thread_id) ?? new Map();
|
|
253
|
+
m.set(u.fullUrl, pr.publicUrl);
|
|
254
|
+
tunnelStarted.set(thread_id, m);
|
|
255
|
+
evictIfFull(tunnelStarted);
|
|
256
|
+
}
|
|
257
|
+
return { url: u, ok: true, publicUrl: pr.publicUrl, fromCache: pr.fromCache };
|
|
258
|
+
}
|
|
259
|
+
return { url: u, ok: false, reason: pr.reason };
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// 文本替换 + rewrites 收集
|
|
263
|
+
//
|
|
264
|
+
// 两道防护:
|
|
265
|
+
// 1. 长度降序:避免文本里两个**都合法**的 URL 互为前缀(如 base URL
|
|
266
|
+
// `http://127.0.0.1:8080` 与带 path `http://127.0.0.1:8080/api`
|
|
267
|
+
// 同 port、不同 fullUrl 共存)时,先替短的会把长的前缀污染。
|
|
268
|
+
// 2. regex + (?![0-9]) lookahead:避免 detector **丢弃**的超界 port
|
|
269
|
+
// URL(如 `http://127.0.0.1:80801`,port>65535 不在 results 里)
|
|
270
|
+
// 被合法 fullUrl `http://127.0.0.1:8080` 的字面替换污染。简单
|
|
271
|
+
// `split.join` 不感知 URL 边界;用 regex 保证 fullUrl 末尾后必须
|
|
272
|
+
// 不是数字(即不是另一个更长 port 的前缀)。
|
|
273
|
+
const successResults = results
|
|
274
|
+
.filter((r) => r.ok)
|
|
275
|
+
.sort((a, b) => b.url.fullUrl.length - a.url.fullUrl.length);
|
|
276
|
+
const failures = results.filter((r) => !r.ok);
|
|
277
|
+
|
|
278
|
+
let newText = text;
|
|
279
|
+
const rewrites = [];
|
|
280
|
+
for (const r of successResults) {
|
|
281
|
+
const target = r.publicUrl + r.url.suffix;
|
|
282
|
+
const escaped = r.url.fullUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
283
|
+
// markdown link: 显示文字保留模型给出的原 localhost 地址,点击跳穿透 URL。
|
|
284
|
+
const replacement = `[${r.url.fullUrl}](${target})`;
|
|
285
|
+
|
|
286
|
+
// 替换分两步:
|
|
287
|
+
//
|
|
288
|
+
// 1. backtick-wrapped (inline code) 形态: `URL` → `[localhost](public)`
|
|
289
|
+
// LLM 常把 URL 写成 inline code(反引号包围),如果只替换中间 URL,
|
|
290
|
+
// 外层反引号还在 → markdown 优先识别为 inline code,内部 link 失效,
|
|
291
|
+
// 用户看到字面 `[http://...](http://...)` 文本。所以连反引号一起吃掉。
|
|
292
|
+
//
|
|
293
|
+
// 2. 裸 URL 形态: URL → [localhost](public)
|
|
294
|
+
//
|
|
295
|
+
// 在 markdown link 内部位置(`[label](url)` 的 label 或 url 部分)的
|
|
296
|
+
// URL 不替换 —— 否则会出现 `[[url](url)](url)` 嵌套破坏。
|
|
297
|
+
//
|
|
298
|
+
// (?![0-9]) 防 fullUrl 是另一个更长 port URL 的前缀(如 8080 ⊂ 80801)。
|
|
299
|
+
|
|
300
|
+
// step 1: `URL` 形态
|
|
301
|
+
const backtickRe = new RegExp(`\`${escaped}(?![0-9])\``, "g");
|
|
302
|
+
newText = newText.replace(backtickRe, replacement);
|
|
303
|
+
|
|
304
|
+
// step 2: 裸 URL —— lookbehind/lookahead 排除:
|
|
305
|
+
// - 已替换的 markdown link 内的 `[url]` 和 `(url)` 位置
|
|
306
|
+
// - 残留 inline code 标记(虽然 step 1 已经吃掉成对反引号,但保险起见)
|
|
307
|
+
const plainRe = new RegExp(
|
|
308
|
+
`(?<![\\[\`])(?<!\\]\\()${escaped}(?![0-9])(?![\\]\\)\`])`,
|
|
309
|
+
"g",
|
|
310
|
+
);
|
|
311
|
+
newText = newText.replace(plainRe, replacement);
|
|
312
|
+
|
|
313
|
+
rewrites.push({ from: r.url.fullUrl, to: target });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 改造后的原 envelope
|
|
317
|
+
// 审计数据(bridge_rewrites)挂在 extensions 下,不是 metadata —— envelope.
|
|
318
|
+
// schema.json 顶层 additionalProperties:false,relay 会拒掉带 metadata 的
|
|
319
|
+
// envelope(HTTP 207),整段改写就丢了。
|
|
320
|
+
const existingRewrites = env.extensions?.bridge_rewrites ?? [];
|
|
321
|
+
const modified = (rewrites.length === 0 && existingRewrites.length === 0)
|
|
322
|
+
? env
|
|
323
|
+
: {
|
|
324
|
+
...env,
|
|
325
|
+
payload: { ...env.payload, block: { ...block, text: newText } },
|
|
326
|
+
extensions: {
|
|
327
|
+
...(env.extensions ?? {}),
|
|
328
|
+
...(rewrites.length > 0 ? {
|
|
329
|
+
bridge_rewrites: [...existingRewrites, ...rewrites],
|
|
330
|
+
} : {}),
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// 静默模式:不追加任何说明 block。
|
|
335
|
+
// - 能替换的 URL → 替换为 markdown link(走上面已实现的 newText 改写)
|
|
336
|
+
// - 无法替换的 URL(tunnel 启动失败 / LAN 非 loopback / 超过 maxUrls) →
|
|
337
|
+
// 保留原文,不显示任何提示原因。
|
|
338
|
+
// 失败 / LAN / overflow 的状态不再发 notice block —— 用户明确要求"不能
|
|
339
|
+
// 转化的地址,就原样显示,不改变消息结果,不显示原因"。
|
|
340
|
+
void failures; void overflow; void lan; void firstNoticeThreads; void lanNoticed; void run_id;
|
|
341
|
+
return [modified];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
// handleIncoming
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
async function handleIncoming(payload) {
|
|
348
|
+
const input = payload?.input;
|
|
349
|
+
const run_id = payload?.run_id || "";
|
|
350
|
+
const text = extractFirstTextBlockText(input);
|
|
351
|
+
if (typeof text !== "string") return { intercept: false };
|
|
352
|
+
const trimmed = text.trim();
|
|
353
|
+
|
|
354
|
+
if (CMD_LS_RE.test(trimmed)) {
|
|
355
|
+
const result = await queryTunnels().catch((err) =>
|
|
356
|
+
({ tunnels: [], _err: String(err?.message || err) }));
|
|
357
|
+
const tunnels = Array.isArray(result?.tunnels) ? result.tunnels : [];
|
|
358
|
+
const body = tunnels.length === 0
|
|
359
|
+
? "📭 当前没有活动 tunnel。"
|
|
360
|
+
: "🔍 活动 tunnel:\n\n" + tunnels.map((t) =>
|
|
361
|
+
`- port \`${t.port}\` → ${t.publicUrl || "(无公网 URL)"}${t.status ? ` _(${t.status})_` : ""}`,
|
|
362
|
+
).join("\n");
|
|
363
|
+
return { intercept: true, replies: [buildBridgeTextEnvelope(run_id, body)] };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const mOff = CMD_OFF_RE.exec(trimmed);
|
|
367
|
+
if (mOff) {
|
|
368
|
+
const port = Number(mOff[1]);
|
|
369
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
370
|
+
return {
|
|
371
|
+
intercept: true,
|
|
372
|
+
replies: [buildBridgeTextEnvelope(run_id, `⚠️ 端口 \`${mOff[1]}\` 不在 [1, 65535] 区间。`)],
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const r = await stopTunnel({ port }).catch((err) =>
|
|
376
|
+
({ ok: false, reason: String(err?.message || err) }));
|
|
377
|
+
const body = r?.ok
|
|
378
|
+
? `✅ 已关闭 port \`${port}\` 的 tunnel。`
|
|
379
|
+
: `⚠️ 关闭 port \`${port}\` tunnel 失败:${r?.reason || "unknown"}`;
|
|
380
|
+
return { intercept: true, replies: [buildBridgeTextEnvelope(run_id, body)] };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return { intercept: false };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function _resetState() {
|
|
387
|
+
tunnelStarted.clear();
|
|
388
|
+
firstNoticeThreads.clear();
|
|
389
|
+
lanNoticed.clear();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return { handleOutgoing, handleIncoming, _resetState };
|
|
393
|
+
}
|