@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,184 @@
|
|
|
1
|
+
// autoTunnelWiring — 把 autoTunnelGateway 接到 bridge 启动入口的生产级 adapter。
|
|
2
|
+
//
|
|
3
|
+
// 职责:
|
|
4
|
+
// - 把 src/tunnel.mjs.runTunnelCommand 适配成 gateway 所需的 startTunnel /
|
|
5
|
+
// queryTunnels / stopTunnel 接口(gateway 不直接依赖 runTunnelCommand,
|
|
6
|
+
// 保留测试时换桩的能力)。
|
|
7
|
+
// - 按 process.env.AGENTLINK_AUTO_TUNNEL(默认开;设为 "0" / "false" / "off"
|
|
8
|
+
// 则禁用)选择是否注册到 bridgeHooks 全局 registry。
|
|
9
|
+
// - 多次调用幂等:用模块级 flag 防止重复注册。
|
|
10
|
+
//
|
|
11
|
+
// 调用方:每个 runtime 入口(src-ext/runtime/{claude,codex,hermes,picoclaw}/index.mjs)
|
|
12
|
+
// 在 bridge 主循环启动前调一次 installAutoTunnel({ relayUrl, log })。
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
|
|
18
|
+
import { runTunnelCommand } from "../../src/tunnel.mjs";
|
|
19
|
+
|
|
20
|
+
// runTunnelCommand 在 `tunnel on` 时会把 cliPath 写进 launchd / systemd
|
|
21
|
+
// launch 脚本(即 `node <cliPath> tunnel agent ...`)。bridge 进程 argv[1]
|
|
22
|
+
// 通常是 bin/agentlink-agent —— 不能 spawn agent subcommand。我们需要指向
|
|
23
|
+
// 同包内的 bin/agentlink(主 CLI 入口,有 tunnel agent 派发)。
|
|
24
|
+
const PKG_BIN_AGENTLINK = (() => {
|
|
25
|
+
// 本文件在 src-ext/core/autoTunnelWiring.mjs;bin/agentlink 在 npm_cli/bin/agentlink
|
|
26
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
return path.resolve(here, "..", "..", "bin", "agentlink");
|
|
28
|
+
})();
|
|
29
|
+
import {
|
|
30
|
+
tunnelStatePath,
|
|
31
|
+
tunnelStateDir,
|
|
32
|
+
readTunnelState,
|
|
33
|
+
ensureTunnelStateDir,
|
|
34
|
+
} from "../../src/tunnel_state.mjs";
|
|
35
|
+
import { getTunnelRuntimeStatus } from "../../src/tunnel_service.mjs";
|
|
36
|
+
|
|
37
|
+
import { registerOutgoingHook, registerIncomingHook } from "./bridgeHooks.mjs";
|
|
38
|
+
import { createAutoTunnelGateway } from "./autoTunnelGateway.mjs";
|
|
39
|
+
|
|
40
|
+
// 两个独立 flag:installed 表示 hook 已注册;disabledByEnv 表示 env 显式禁用。
|
|
41
|
+
// 解耦后 AGENTLINK_AUTO_TUNNEL=0 + 二次调用 installAutoTunnel 能返回正确语义。
|
|
42
|
+
let installed = false;
|
|
43
|
+
let disabledByEnv = false;
|
|
44
|
+
|
|
45
|
+
function envDisabled() {
|
|
46
|
+
const v = String(process.env.AGENTLINK_AUTO_TUNNEL ?? "").trim().toLowerCase();
|
|
47
|
+
return v === "0" || v === "false" || v === "off" || v === "no";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 把 runTunnelCommand({on}) → { ok, publicUrl, reason }
|
|
52
|
+
* - 调命令;失败 → ok:false
|
|
53
|
+
* - 成功后从 ~/.agentlink/tunnels/<port>.json 读 publicUrl
|
|
54
|
+
* - 缺 publicUrl → ok:false
|
|
55
|
+
*
|
|
56
|
+
* fail callback 在 runTunnelCommand 内部出错时被同步 throw,catch 块统一捕获。
|
|
57
|
+
*/
|
|
58
|
+
function makeStartTunnel({ relayUrl, log: _log }) {
|
|
59
|
+
return async ({ port }) => {
|
|
60
|
+
try {
|
|
61
|
+
await runTunnelCommand({
|
|
62
|
+
action: "on",
|
|
63
|
+
port: String(port),
|
|
64
|
+
relay: relayUrl,
|
|
65
|
+
defaultRelayUrl: relayUrl,
|
|
66
|
+
cliPath: PKG_BIN_AGENTLINK,
|
|
67
|
+
json: true,
|
|
68
|
+
fail: (msg) => { throw new Error(msg); },
|
|
69
|
+
});
|
|
70
|
+
const state = readTunnelState(tunnelStatePath(port));
|
|
71
|
+
if (state?.publicUrl) {
|
|
72
|
+
return { ok: true, publicUrl: state.publicUrl };
|
|
73
|
+
}
|
|
74
|
+
return { ok: false, reason: "no_public_url_in_state_file" };
|
|
75
|
+
} catch (err) {
|
|
76
|
+
return { ok: false, reason: String(err?.message || err) };
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* runTunnelCommand({ls}) prints to stdout but doesn't return data. 我们直接
|
|
83
|
+
* 扫 tunnelStateDir 拿到全部 port 的 state 文件,并用 getTunnelRuntimeStatus
|
|
84
|
+
* 判断 alive。这与 `agentlink tunnel ls` 看到的同源。
|
|
85
|
+
*/
|
|
86
|
+
function makeQueryTunnels({ log }) {
|
|
87
|
+
return async () => {
|
|
88
|
+
try {
|
|
89
|
+
ensureTunnelStateDir();
|
|
90
|
+
const dir = tunnelStateDir();
|
|
91
|
+
let files;
|
|
92
|
+
try {
|
|
93
|
+
files = fs.readdirSync(dir);
|
|
94
|
+
} catch {
|
|
95
|
+
files = [];
|
|
96
|
+
}
|
|
97
|
+
const tunnels = [];
|
|
98
|
+
for (const f of files) {
|
|
99
|
+
if (!f.endsWith(".json")) continue;
|
|
100
|
+
const state = readTunnelState(path.join(dir, f));
|
|
101
|
+
if (!state || !state.port) continue;
|
|
102
|
+
const runtimeStatus = getTunnelRuntimeStatus({
|
|
103
|
+
localPort: state.port,
|
|
104
|
+
runtime: state.runtime,
|
|
105
|
+
pid: state.pid,
|
|
106
|
+
});
|
|
107
|
+
if (!runtimeStatus?.running) continue;
|
|
108
|
+
tunnels.push({
|
|
109
|
+
port: state.port,
|
|
110
|
+
publicUrl: state.publicUrl || null,
|
|
111
|
+
status: runtimeStatus.manager || "running",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return { tunnels };
|
|
115
|
+
} catch (err) {
|
|
116
|
+
log?.warn?.("auto_tunnel.query_err", "queryTunnels failed", {
|
|
117
|
+
err: String(err?.message || err),
|
|
118
|
+
});
|
|
119
|
+
return { tunnels: [] };
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function makeStopTunnel({ relayUrl }) {
|
|
125
|
+
return async ({ port }) => {
|
|
126
|
+
try {
|
|
127
|
+
await runTunnelCommand({
|
|
128
|
+
action: "off",
|
|
129
|
+
port: String(port),
|
|
130
|
+
relay: relayUrl,
|
|
131
|
+
defaultRelayUrl: relayUrl,
|
|
132
|
+
cliPath: PKG_BIN_AGENTLINK,
|
|
133
|
+
json: true,
|
|
134
|
+
fail: (msg) => { throw new Error(msg); },
|
|
135
|
+
});
|
|
136
|
+
return { ok: true };
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return { ok: false, reason: String(err?.message || err) };
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 接到 bridge:注册 outgoing + incoming hook。
|
|
145
|
+
* 幂等:重复调用直接返回 noop。
|
|
146
|
+
*
|
|
147
|
+
* @param {{ relayUrl: string, log: any }} opts
|
|
148
|
+
*/
|
|
149
|
+
export function installAutoTunnel({ relayUrl, log }) {
|
|
150
|
+
if (disabledByEnv) return { enabled: false, reason: "env_disabled" };
|
|
151
|
+
if (installed) return { enabled: true, alreadyInstalled: true };
|
|
152
|
+
if (envDisabled()) {
|
|
153
|
+
log?.info?.("auto_tunnel.disabled",
|
|
154
|
+
"auto-tunnel gateway disabled by AGENTLINK_AUTO_TUNNEL env");
|
|
155
|
+
disabledByEnv = true; // 用独立 flag,避免后续重复打印 disabled
|
|
156
|
+
return { enabled: false, reason: "env_disabled" };
|
|
157
|
+
}
|
|
158
|
+
if (!relayUrl) {
|
|
159
|
+
log?.warn?.("auto_tunnel.no_relay",
|
|
160
|
+
"auto-tunnel gateway not installed: relayUrl missing");
|
|
161
|
+
return { enabled: false, reason: "no_relay_url" };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const gateway = createAutoTunnelGateway({
|
|
165
|
+
startTunnel: makeStartTunnel({ relayUrl, log }),
|
|
166
|
+
queryTunnels: makeQueryTunnels({ log }),
|
|
167
|
+
stopTunnel: makeStopTunnel({ relayUrl }),
|
|
168
|
+
logger: log,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
registerOutgoingHook((envelopes, ctx) => gateway.handleOutgoing(envelopes, ctx));
|
|
172
|
+
registerIncomingHook((payload) => gateway.handleIncoming(payload));
|
|
173
|
+
|
|
174
|
+
installed = true;
|
|
175
|
+
log?.info?.("auto_tunnel.enabled",
|
|
176
|
+
"auto-tunnel gateway enabled (loopback URL auto-tunnel + /tunnel ls/off)");
|
|
177
|
+
return { enabled: true };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 测试桩:让单测可以重置 installed + disabledByEnv 状态
|
|
181
|
+
export function _resetInstalledForTest() {
|
|
182
|
+
installed = false;
|
|
183
|
+
disabledByEnv = false;
|
|
184
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boundedMap.mjs — 带容量上限 + TTL 的 Map(audit 修复:进程级缓存无界增长)。
|
|
3
|
+
*
|
|
4
|
+
* bridge 是 launchd 常驻进程,threadSessionMap / dispatchedRunIds 这类
|
|
5
|
+
* 进程级缓存只增不减,跑数周必然内存膨胀。本容器提供 Map 兼容子集:
|
|
6
|
+
* - maxSize:超限时淘汰最久未访问的条目(get/set 均刷新热度,LRU)
|
|
7
|
+
* - ttlMs:条目自最后一次访问起超时后惰性过期(get/has 时检查)
|
|
8
|
+
*
|
|
9
|
+
* 实现依赖 JS Map 的插入序遍历:刷新热度 = delete + 重插到尾部;淘汰 = 删头部。
|
|
10
|
+
* 无定时器(不阻进程退出),过期清理在读写路径上惰性进行。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export class BoundedMap {
|
|
14
|
+
/**
|
|
15
|
+
* @param {{ maxSize?: number, ttlMs?: number }} [opts]
|
|
16
|
+
*/
|
|
17
|
+
constructor({ maxSize = 10_000, ttlMs = 0 } = {}) {
|
|
18
|
+
if (!(maxSize > 0)) throw new Error("BoundedMap: maxSize must be > 0");
|
|
19
|
+
this._max = maxSize;
|
|
20
|
+
this._ttl = ttlMs > 0 ? ttlMs : 0;
|
|
21
|
+
/** @type {Map<any, { v: any, at: number }>} */
|
|
22
|
+
this._m = new Map();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
_expired(entry, now) {
|
|
26
|
+
return this._ttl > 0 && now - entry.at > this._ttl;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get(key) {
|
|
30
|
+
const e = this._m.get(key);
|
|
31
|
+
if (!e) return undefined;
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
if (this._expired(e, now)) {
|
|
34
|
+
this._m.delete(key);
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
// LRU touch:重插到尾部并刷新时间戳
|
|
38
|
+
this._m.delete(key);
|
|
39
|
+
e.at = now;
|
|
40
|
+
this._m.set(key, e);
|
|
41
|
+
return e.v;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
has(key) {
|
|
45
|
+
return this.get(key) !== undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
set(key, value) {
|
|
49
|
+
if (this._m.has(key)) this._m.delete(key);
|
|
50
|
+
this._m.set(key, { v: value, at: Date.now() });
|
|
51
|
+
if (this._m.size > this._max) {
|
|
52
|
+
// 超限:先惰性清一轮过期项,仍超限则删最久未访问(Map 头部)
|
|
53
|
+
if (this._ttl > 0) {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
for (const [k, e] of this._m) {
|
|
56
|
+
if (this._m.size <= this._max) break;
|
|
57
|
+
if (this._expired(e, now)) this._m.delete(k);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
while (this._m.size > this._max) {
|
|
61
|
+
this._m.delete(this._m.keys().next().value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
delete(key) {
|
|
68
|
+
return this._m.delete(key);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get size() {
|
|
72
|
+
return this._m.size;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
clear() {
|
|
76
|
+
this._m.clear();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Set 形态的便捷封装(dispatchedRunIds 用)。 */
|
|
81
|
+
export class BoundedSet {
|
|
82
|
+
constructor(opts) {
|
|
83
|
+
this._m = new BoundedMap(opts);
|
|
84
|
+
}
|
|
85
|
+
add(key) {
|
|
86
|
+
this._m.set(key, true);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
has(key) {
|
|
90
|
+
return this._m.has(key);
|
|
91
|
+
}
|
|
92
|
+
delete(key) {
|
|
93
|
+
return this._m.delete(key);
|
|
94
|
+
}
|
|
95
|
+
get size() {
|
|
96
|
+
return this._m.size;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// bridgeAttachAutoScanner.mjs — 流式 assistant 文本里自动抓"本机文件路径" → 调
|
|
2
|
+
// agentlink_attach_file 自动上传 COS。
|
|
3
|
+
//
|
|
4
|
+
// 设计要点(来自 user 2026-05 review):
|
|
5
|
+
// - **不阻塞流式输出**:所有 scan / attach 都是 fire-and-forget。runtime
|
|
6
|
+
// publishEvent(chunk) 不被等待。
|
|
7
|
+
// - **按句子/行积累**:streaming runtime(codex/hermes)的 delta 是单 token 级,
|
|
8
|
+
// 路径会被切碎。accumulator buffer 到换行(`\n`) 或缓冲长度阈值再 scan。
|
|
9
|
+
// - **不限数量**:user 明确说不限 attach 数(per session / per turn 都不限)。
|
|
10
|
+
// - **必须 fs.access + isFile** 才 attach。零假阳性。
|
|
11
|
+
// - **去重**:Map<absPath, mtimeMs>。同 path 同 mtime 不重传;mtime 变了重传(自然
|
|
12
|
+
// 处理 agent rewrite workspace 文件场景)。
|
|
13
|
+
// - **denylist 复用**:dispatch("agentlink_attach_file") 内部已走 denylist + audit + COS PUT。
|
|
14
|
+
//
|
|
15
|
+
// 不在这里处理:
|
|
16
|
+
// - OpenClaw plugin path: plugin 是 CJS, 跑在 daemon 进程, 用 HTTP 调 /tools/call,
|
|
17
|
+
// 直接在 openclaw-plugin/index.cjs 里 inline path 提取 + fetch, 不 import 本模块。
|
|
18
|
+
//
|
|
19
|
+
// 调用方式(在 bridge runtime 里):
|
|
20
|
+
// const scanner = toolsLifecycle.scanner; // bootstrapAgentlinkTools 返回的
|
|
21
|
+
// const acc = scanner.createTurnAccumulator({ requestId: ctx.requestId });
|
|
22
|
+
// // 每收到 assistant delta:
|
|
23
|
+
// acc.append(deltaText);
|
|
24
|
+
// // turn 完成:
|
|
25
|
+
// acc.flush();
|
|
26
|
+
|
|
27
|
+
import os from "node:os";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import fs from "node:fs/promises";
|
|
30
|
+
|
|
31
|
+
// 路径正则:先用宽松匹配抓"以 `/` 或 `~/` 开头的连续 path 字符序列",
|
|
32
|
+
// 然后在 JS 里通过 basename 必须含 `.` 来过滤掉纯目录(如 /Users/jiayi)。
|
|
33
|
+
// lookbehind `(?<!...)` 排除"路径紧贴前一个 path-segment 字符"的场景(最典型:
|
|
34
|
+
// 相对路径 `../foo.md` 里不该把其中的 `/foo.md` 当 abs path)。
|
|
35
|
+
const PATH_REGEX =
|
|
36
|
+
/(?<![A-Za-z0-9_.+\-/~])(?:file:\/\/)?(?:~|\/)(?:\/?[A-Za-z0-9_.+\-]+)+/g;
|
|
37
|
+
|
|
38
|
+
// 累积 buffer 超这个长度也强制 flush,防长段无换行卡住(罕见但要兜底)。
|
|
39
|
+
const FORCE_FLUSH_BYTES = 4096;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {object} opts
|
|
43
|
+
* @param {{ list: Array, dispatch: (name: string, args: object) => Promise<any> }} opts.registry
|
|
44
|
+
* @param {object} opts.log
|
|
45
|
+
* @returns {{
|
|
46
|
+
* scanText: (text: string, ctxLabel?: string) => Promise<void>,
|
|
47
|
+
* createTurnAccumulator: (ctx?: { requestId?: string }) => {
|
|
48
|
+
* append: (delta: string) => void,
|
|
49
|
+
* flush: () => void,
|
|
50
|
+
* },
|
|
51
|
+
* }}
|
|
52
|
+
*/
|
|
53
|
+
export function createPathAttachScanner({ registry, log }) {
|
|
54
|
+
if (!registry || typeof registry.dispatch !== "function") {
|
|
55
|
+
throw new Error("createPathAttachScanner: registry.dispatch required");
|
|
56
|
+
}
|
|
57
|
+
if (!log) throw new Error("createPathAttachScanner: log required");
|
|
58
|
+
|
|
59
|
+
const sLog = log.child({ comp: "scanner", side: "attach-auto" });
|
|
60
|
+
|
|
61
|
+
// 已 attach 过的 path → mtimeMs。mtime 变了重新 attach(自然支持文件被覆写)。
|
|
62
|
+
const attachedByPath = new Map();
|
|
63
|
+
|
|
64
|
+
async function scanText(text, ctxLabel) {
|
|
65
|
+
if (!text || typeof text !== "string") return;
|
|
66
|
+
const raws = extractPathCandidates(text);
|
|
67
|
+
if (raws.size === 0) return;
|
|
68
|
+
sLog.info("scanner.matched", "regex hits", {
|
|
69
|
+
ctx: ctxLabel || null,
|
|
70
|
+
count: raws.size,
|
|
71
|
+
sample: Array.from(raws).slice(0, 3),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 串行处理(fs.stat / dispatch 都是异步),但整个 scanText 是 fire-and-forget,
|
|
75
|
+
// 不会阻塞 caller 的 publishEvent。
|
|
76
|
+
for (const absPath of raws) {
|
|
77
|
+
try {
|
|
78
|
+
const st = await fs.stat(absPath);
|
|
79
|
+
if (!st.isFile()) {
|
|
80
|
+
sLog.debug("scanner.skip.not_file", "skipping non-regular file", {
|
|
81
|
+
path: absPath,
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const prevMtime = attachedByPath.get(absPath);
|
|
86
|
+
if (prevMtime === st.mtimeMs) {
|
|
87
|
+
sLog.debug("scanner.skip.unchanged", "already attached, mtime unchanged", {
|
|
88
|
+
path: absPath,
|
|
89
|
+
mtimeMs: st.mtimeMs,
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// 提前 record,避免同一 turn 内并发多次 hit 同 path 重复 PUT。dispatch 失败
|
|
94
|
+
// 时 rollback。
|
|
95
|
+
attachedByPath.set(absPath, st.mtimeMs);
|
|
96
|
+
sLog.info("scanner.attach.start", "auto-attaching", {
|
|
97
|
+
path: absPath,
|
|
98
|
+
size: st.size,
|
|
99
|
+
mtimeMs: st.mtimeMs,
|
|
100
|
+
ctx: ctxLabel || null,
|
|
101
|
+
});
|
|
102
|
+
let result;
|
|
103
|
+
try {
|
|
104
|
+
result = await registry.dispatch("agentlink_attach_file", {
|
|
105
|
+
path: absPath,
|
|
106
|
+
reason: "auto-scan",
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
attachedByPath.delete(absPath);
|
|
110
|
+
sLog.warn("scanner.attach.threw", "dispatch threw", {
|
|
111
|
+
path: absPath,
|
|
112
|
+
err: err?.message || String(err),
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (result?.isError) {
|
|
117
|
+
attachedByPath.delete(absPath);
|
|
118
|
+
const summary = result?.content?.[0]?.text?.slice(0, 200);
|
|
119
|
+
sLog.warn("scanner.attach.failed", "tool returned isError", {
|
|
120
|
+
path: absPath,
|
|
121
|
+
content: summary,
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
const summary = result?.content?.[0]?.text?.slice(0, 200);
|
|
125
|
+
sLog.info("scanner.attach.ok", "auto-attached", {
|
|
126
|
+
path: absPath,
|
|
127
|
+
content: summary,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (err && err.code === "ENOENT") {
|
|
132
|
+
sLog.debug("scanner.skip.not_found", "path does not exist", {
|
|
133
|
+
path: absPath,
|
|
134
|
+
});
|
|
135
|
+
} else if (err && err.code === "EACCES") {
|
|
136
|
+
sLog.debug("scanner.skip.no_access", "no read permission", {
|
|
137
|
+
path: absPath,
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
sLog.warn("scanner.stat.threw", "unexpected stat error", {
|
|
141
|
+
path: absPath,
|
|
142
|
+
err: err?.message || String(err),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function createTurnAccumulator(ctx) {
|
|
150
|
+
const requestId = ctx?.requestId || null;
|
|
151
|
+
const ctxLabel = requestId ? `req=${requestId.slice(0, 8)}` : "(no-req)";
|
|
152
|
+
let buf = "";
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
append(delta) {
|
|
156
|
+
if (typeof delta !== "string" || delta.length === 0) return;
|
|
157
|
+
buf += delta;
|
|
158
|
+
// 按换行切:完整行扔给 scan,trailing 留下次。
|
|
159
|
+
const lastNl = buf.lastIndexOf("\n");
|
|
160
|
+
if (lastNl >= 0) {
|
|
161
|
+
const completed = buf.slice(0, lastNl);
|
|
162
|
+
buf = buf.slice(lastNl + 1);
|
|
163
|
+
if (completed.length > 0) {
|
|
164
|
+
void scanText(completed, ctxLabel).catch((err) => {
|
|
165
|
+
sLog.warn("scanner.append.threw", "scanText threw", {
|
|
166
|
+
ctx: ctxLabel,
|
|
167
|
+
err: err?.message || String(err),
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// 没换行:缓冲过长时强制 flush 一段。保留尾部 256B carry-over 避免切碎
|
|
174
|
+
// 路径(256B 远大于任何合理本机路径长度)。
|
|
175
|
+
if (buf.length >= FORCE_FLUSH_BYTES) {
|
|
176
|
+
const carry = buf.slice(-256);
|
|
177
|
+
const completed = buf.slice(0, buf.length - 256);
|
|
178
|
+
buf = carry;
|
|
179
|
+
void scanText(completed, ctxLabel).catch((err) => {
|
|
180
|
+
sLog.warn("scanner.append.threw", "scanText threw", {
|
|
181
|
+
ctx: ctxLabel,
|
|
182
|
+
err: err?.message || String(err),
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
flush() {
|
|
188
|
+
if (buf.length === 0) return;
|
|
189
|
+
const tail = buf;
|
|
190
|
+
buf = "";
|
|
191
|
+
void scanText(tail, ctxLabel).catch((err) => {
|
|
192
|
+
sLog.warn("scanner.flush.threw", "scanText threw", {
|
|
193
|
+
ctx: ctxLabel,
|
|
194
|
+
err: err?.message || String(err),
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { scanText, createTurnAccumulator };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 把一段文本里出现的本机路径候选项提取出来,规范化为绝对路径 Set。
|
|
206
|
+
* 不做 I/O — 不验证存在性。caller 负责后续 fs.stat 校验。
|
|
207
|
+
*
|
|
208
|
+
* Exported for testing.
|
|
209
|
+
*/
|
|
210
|
+
export function extractPathCandidates(text) {
|
|
211
|
+
const out = new Set();
|
|
212
|
+
if (!text || typeof text !== "string") return out;
|
|
213
|
+
PATH_REGEX.lastIndex = 0;
|
|
214
|
+
let m;
|
|
215
|
+
while ((m = PATH_REGEX.exec(text)) !== null) {
|
|
216
|
+
let raw = m[0];
|
|
217
|
+
if (raw.startsWith("file://")) raw = raw.slice(7);
|
|
218
|
+
// 砍掉尾巴上的非路径标点 + trailing 斜杠(regex `\/?` 可能拖一个 `/`)
|
|
219
|
+
while (raw.length > 1 && /[.,;:!?)\]}"'<>\/]$/.test(raw)) {
|
|
220
|
+
raw = raw.slice(0, -1);
|
|
221
|
+
}
|
|
222
|
+
if (!raw || raw === "~") continue;
|
|
223
|
+
// basename 必须含 `.`(过滤纯目录如 `/Users/jiayi`,但允许 dotfile 如 `.bashrc`)
|
|
224
|
+
const lastSlash = raw.lastIndexOf("/");
|
|
225
|
+
const basename = lastSlash >= 0 ? raw.slice(lastSlash + 1) : raw;
|
|
226
|
+
if (!basename.includes(".") || basename === "." || basename === "..") continue;
|
|
227
|
+
// ~/ 展开
|
|
228
|
+
if (raw.startsWith("~/")) {
|
|
229
|
+
raw = path.join(os.homedir(), raw.slice(2));
|
|
230
|
+
}
|
|
231
|
+
if (!raw.startsWith("/")) continue;
|
|
232
|
+
try {
|
|
233
|
+
out.add(path.resolve(raw));
|
|
234
|
+
} catch { /* swallow */ }
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// bridgeHooks — 注册 + 调用 outgoing/incoming hook 的最小框架。
|
|
2
|
+
//
|
|
3
|
+
// outgoing:拦截 envelope 上行(用于 bridge-auto-tunnel 把 loopback URL 替换为公网 URL、
|
|
4
|
+
// 追加首次提示 / 失败提示 / LAN 提示等 bridge-origin envelope)。
|
|
5
|
+
// incoming:拦截 dispatch 入口(用于识别 /tunnel ls / /tunnel off <port> 等 chat-side 命令)。
|
|
6
|
+
//
|
|
7
|
+
// 失败语义(spec cli-unified-bridge delta):
|
|
8
|
+
// - outgoing hook 抛错 / 超过整体 3000 ms → 用"hook 链上修改前一步的 envelopes"上行,不丢消息
|
|
9
|
+
// - incoming hook 抛错 → 兜底走 pass-through(intercept: false),不丢用户消息
|
|
10
|
+
//
|
|
11
|
+
// 进程内单例 registry。Bridge 重启清空。
|
|
12
|
+
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
14
|
+
|
|
15
|
+
const state = {
|
|
16
|
+
outgoing: /** @type {Array<(envelopes:any[]) => Promise<any[]>|any[]>} */ ([]),
|
|
17
|
+
incoming: /** @type {Array<(input:{thread_id:string, run_id:string, input:any}) => Promise<{intercept:boolean, replies?:any[]}>|{intercept:boolean, replies?:any[]}>} */ ([]),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function noopLogger() {
|
|
21
|
+
return { info() {}, warn() {}, error() {}, child() { return noopLogger(); } };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getLogger(opts) {
|
|
25
|
+
if (opts && opts.logger) return opts.logger;
|
|
26
|
+
return noopLogger();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function registerOutgoingHook(fn) {
|
|
30
|
+
if (typeof fn !== "function") throw new Error("registerOutgoingHook: fn must be function");
|
|
31
|
+
state.outgoing.push(fn);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function registerIncomingHook(fn) {
|
|
35
|
+
if (typeof fn !== "function") throw new Error("registerIncomingHook: fn must be function");
|
|
36
|
+
state.incoming.push(fn);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Test seam: reset registry (NOT exported in production paths, but tests need it).
|
|
40
|
+
export function _resetHooksForTest() {
|
|
41
|
+
state.outgoing.length = 0;
|
|
42
|
+
state.incoming.length = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function _outgoingHookCountForTest() {
|
|
46
|
+
return state.outgoing.length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Apply outgoing hooks in sequence (chain). Each hook receives the current
|
|
51
|
+
* envelopes array (possibly mutated by prior hooks) and may return a new array
|
|
52
|
+
* (or a Promise of one).
|
|
53
|
+
*
|
|
54
|
+
* Failure modes:
|
|
55
|
+
* - Any single hook throws / rejects → log warn, drop that hook's contribution,
|
|
56
|
+
* keep the array produced by the previous link.
|
|
57
|
+
* - Total elapsed time > timeoutMs → log warn, return the array as it stood
|
|
58
|
+
* when timeout hit (or original if first hook hadn't returned).
|
|
59
|
+
*
|
|
60
|
+
* Always returns an array. NEVER throws.
|
|
61
|
+
*
|
|
62
|
+
* @param {any[]} envelopes
|
|
63
|
+
* @param {{ logger?: any, timeoutMs?: number }} [opts]
|
|
64
|
+
* @returns {Promise<any[]>}
|
|
65
|
+
*/
|
|
66
|
+
export async function applyOutgoingHooks(envelopes, opts = {}) {
|
|
67
|
+
const logger = getLogger(opts);
|
|
68
|
+
const timeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
69
|
+
|
|
70
|
+
if (!Array.isArray(envelopes)) return envelopes;
|
|
71
|
+
if (state.outgoing.length === 0) return envelopes;
|
|
72
|
+
|
|
73
|
+
// 单次 timeout 覆盖整个 hook 链(含所有 hook 累计耗时)。
|
|
74
|
+
let timedOut = false;
|
|
75
|
+
let timerHandle = null;
|
|
76
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
77
|
+
timerHandle = setTimeout(() => {
|
|
78
|
+
timedOut = true;
|
|
79
|
+
resolve("__timeout__");
|
|
80
|
+
}, timeoutMs);
|
|
81
|
+
timerHandle.unref?.();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// ctx 透传给每个 hook 作为第二参数。hook 不感知 ctx 时可忽略;新 hook(如
|
|
85
|
+
// autoTunnelGateway.handleOutgoing)通过 ctx.thread_id 拿到当前 thread 上下文。
|
|
86
|
+
const hookCtx = { thread_id: opts.thread_id ?? null, logger };
|
|
87
|
+
|
|
88
|
+
let current = envelopes;
|
|
89
|
+
const chainPromise = (async () => {
|
|
90
|
+
for (const hook of state.outgoing) {
|
|
91
|
+
if (timedOut) break;
|
|
92
|
+
const before = current;
|
|
93
|
+
try {
|
|
94
|
+
const result = await hook(before, hookCtx);
|
|
95
|
+
if (Array.isArray(result)) {
|
|
96
|
+
current = result;
|
|
97
|
+
} else {
|
|
98
|
+
// hook 返回非数组视为契约违规,但不致命:保留 before,落 warn。
|
|
99
|
+
logger.warn?.("hooks.outgoing.bad_return", "outgoing hook returned non-array, ignoring", {
|
|
100
|
+
type: typeof result,
|
|
101
|
+
});
|
|
102
|
+
current = before;
|
|
103
|
+
}
|
|
104
|
+
} catch (err) {
|
|
105
|
+
logger.warn?.("hooks.outgoing.err", "outgoing hook threw, falling back to prior envelopes", {
|
|
106
|
+
err: err?.message || String(err),
|
|
107
|
+
});
|
|
108
|
+
current = before;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return current;
|
|
112
|
+
})();
|
|
113
|
+
// 关键:给 chainPromise 挂一个 no-op handler,避免它在 timeout 分支里"无人 await"
|
|
114
|
+
// 时被 node:test / Node 当成 dangling Promise 触发 "still pending"。
|
|
115
|
+
chainPromise.then(() => {}, () => {});
|
|
116
|
+
|
|
117
|
+
const winner = await Promise.race([chainPromise, timeoutPromise]);
|
|
118
|
+
if (timerHandle) clearTimeout(timerHandle); // race 落地立刻清理 timer
|
|
119
|
+
if (winner === "__timeout__") {
|
|
120
|
+
logger.warn?.("hooks.outgoing.timeout", "outgoing hook chain exceeded timeout, using last known envelopes", {
|
|
121
|
+
timeout_ms: timeoutMs,
|
|
122
|
+
});
|
|
123
|
+
return current; // last 已知 array(可能是原 envelopes 或中间某一步结果)
|
|
124
|
+
}
|
|
125
|
+
return Array.isArray(winner) ? winner : envelopes;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Apply incoming hooks in sequence. First hook to return `{intercept: true, replies}`
|
|
130
|
+
* wins; subsequent hooks are NOT consulted.
|
|
131
|
+
*
|
|
132
|
+
* Failure modes:
|
|
133
|
+
* - A hook throws → log warn, try next hook.
|
|
134
|
+
* - All hooks throw or return `{intercept: false}` → return `{intercept: false}` (pass-through).
|
|
135
|
+
*
|
|
136
|
+
* Always returns a synchronous shape. NEVER throws.
|
|
137
|
+
*
|
|
138
|
+
* @param {{ thread_id?: string, run_id?: string, input: any }} payload
|
|
139
|
+
* @param {{ logger?: any }} [opts]
|
|
140
|
+
* @returns {Promise<{intercept: false} | {intercept: true, replies: any[]}>}
|
|
141
|
+
*/
|
|
142
|
+
export async function applyIncomingHooks(payload, opts = {}) {
|
|
143
|
+
const logger = getLogger(opts);
|
|
144
|
+
|
|
145
|
+
for (const hook of state.incoming) {
|
|
146
|
+
let result;
|
|
147
|
+
try {
|
|
148
|
+
result = await hook(payload);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
logger.warn?.("hooks.incoming.err", "incoming hook threw, trying next", {
|
|
151
|
+
err: err?.message || String(err),
|
|
152
|
+
run_id: payload?.run_id,
|
|
153
|
+
});
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (result && result.intercept === true) {
|
|
157
|
+
if (!Array.isArray(result.replies)) {
|
|
158
|
+
logger.warn?.("hooks.incoming.bad_shape", "incoming hook returned intercept without replies array, falling through", {
|
|
159
|
+
run_id: payload?.run_id,
|
|
160
|
+
});
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
return { intercept: true, replies: result.replies };
|
|
164
|
+
}
|
|
165
|
+
// intercept: false 或形状不对 → 试下一个
|
|
166
|
+
}
|
|
167
|
+
return { intercept: false };
|
|
168
|
+
}
|