@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.
Files changed (385) hide show
  1. package/README.md +860 -0
  2. package/bin/agentlink +8585 -0
  3. package/bin/agentlink-agent +7 -0
  4. package/bin/agentlink-mcp-stdio +173 -0
  5. package/docs/LOGGING.md +159 -0
  6. package/package.json +38 -0
  7. package/src/core.mjs +320 -0
  8. package/src/tunnel.mjs +291 -0
  9. package/src/tunnel_proxy.mjs +913 -0
  10. package/src/tunnel_service.mjs +699 -0
  11. package/src/tunnel_state.mjs +301 -0
  12. package/src/unified_producer.mjs +259 -0
  13. package/src-ext/README.md +75 -0
  14. package/src-ext/bin.mjs +128 -0
  15. package/src-ext/commands/agent.mjs +66 -0
  16. package/src-ext/commands/help.mjs +31 -0
  17. package/src-ext/commands/pair.mjs +455 -0
  18. package/src-ext/commands/reload.mjs +78 -0
  19. package/src-ext/commands/service.mjs +64 -0
  20. package/src-ext/core/accountResolver.mjs +137 -0
  21. package/src-ext/core/activeRuns.mjs +76 -0
  22. package/src-ext/core/agentlinkToolsBootstrap.mjs +75 -0
  23. package/src-ext/core/agentlinkToolsRegistry.mjs +323 -0
  24. package/src-ext/core/agentlinkToolsServer.mjs +109 -0
  25. package/src-ext/core/approvalGateway.mjs +132 -0
  26. package/src-ext/core/attachAuditLog.mjs +79 -0
  27. package/src-ext/core/attachDenylist.mjs +128 -0
  28. package/src-ext/core/autoTunnelDetector.mjs +98 -0
  29. package/src-ext/core/autoTunnelGateway.mjs +393 -0
  30. package/src-ext/core/autoTunnelWiring.mjs +184 -0
  31. package/src-ext/core/boundedMap.mjs +98 -0
  32. package/src-ext/core/bridgeAttachAutoScanner.mjs +237 -0
  33. package/src-ext/core/bridgeHooks.mjs +168 -0
  34. package/src-ext/core/bridgeSelfUninstall.mjs +133 -0
  35. package/src-ext/core/claudeTrust.mjs +74 -0
  36. package/src-ext/core/codexTrust.mjs +77 -0
  37. package/src-ext/core/cosUploadClient.mjs +326 -0
  38. package/src-ext/core/createWorkspace.mjs +170 -0
  39. package/src-ext/core/daemonRestart.mjs +49 -0
  40. package/src-ext/core/defaultWorkspace.mjs +65 -0
  41. package/src-ext/core/ensureGitRepo.mjs +57 -0
  42. package/src-ext/core/hermesProfile.mjs +75 -0
  43. package/src-ext/core/interruptGateway.mjs +119 -0
  44. package/src-ext/core/interruptRunListener.mjs +106 -0
  45. package/src-ext/core/mcpRuntimeFanout.mjs +236 -0
  46. package/src-ext/core/mcpStdioClient.mjs +316 -0
  47. package/src-ext/core/openclawGatewayClient.mjs +230 -0
  48. package/src-ext/core/pairCodeClient.mjs +331 -0
  49. package/src-ext/core/pairInventory.mjs +59 -0
  50. package/src-ext/core/pathExpand.mjs +34 -0
  51. package/src-ext/core/preprocessAttachments.mjs +113 -0
  52. package/src-ext/core/relayWorker.mjs +284 -0
  53. package/src-ext/core/relayWorkerHttp.mjs +58 -0
  54. package/src-ext/core/scanWorkspaces.mjs +842 -0
  55. package/src-ext/core/sessionSlot.mjs +70 -0
  56. package/src-ext/core/stateMachine.mjs +85 -0
  57. package/src-ext/core/toolsServerLifecycle.mjs +169 -0
  58. package/src-ext/core/unifiedDispatchHandler.mjs +439 -0
  59. package/src-ext/core/usageReporter.mjs +207 -0
  60. package/src-ext/openclaw-plugin/envelope-builder.cjs +619 -0
  61. package/src-ext/openclaw-plugin/index.cjs +1919 -0
  62. package/src-ext/openclaw-plugin/openclaw.plugin.json +20 -0
  63. package/src-ext/openclaw-plugin/package.json +12 -0
  64. package/src-ext/openclaw-plugin/relay-defaults.json +3 -0
  65. package/src-ext/openclaw-plugin/todoTranslatorUtils.cjs +101 -0
  66. package/src-ext/runtime/_shared/cliExec.mjs +138 -0
  67. package/src-ext/runtime/_shared/cosTextRefs.mjs +120 -0
  68. package/src-ext/runtime/_shared/fetchAndBase64Encode.mjs +52 -0
  69. package/src-ext/runtime/_shared/relayObjectToBlock.mjs +307 -0
  70. package/src-ext/runtime/_shared/sessionPicker.mjs +418 -0
  71. package/src-ext/runtime/_shared/slashCommandRouter.mjs +1346 -0
  72. package/src-ext/runtime/_shared/todoTranslatorUtils.mjs +94 -0
  73. package/src-ext/runtime/claude/askUserQuestionTranslator.mjs +241 -0
  74. package/src-ext/runtime/claude/customCommandsCollector.mjs +132 -0
  75. package/src-ext/runtime/claude/handleRequest.mjs +1418 -0
  76. package/src-ext/runtime/claude/index.mjs +160 -0
  77. package/src-ext/runtime/claude/internalCommandNames.mjs +58 -0
  78. package/src-ext/runtime/claude/launcher.mjs +313 -0
  79. package/src-ext/runtime/claude/mcpConfigAdapter.mjs +174 -0
  80. package/src-ext/runtime/claude/mcpConfigWriter.mjs +57 -0
  81. package/src-ext/runtime/claude/modelContextWindow.mjs +30 -0
  82. package/src-ext/runtime/claude/modelScanner.mjs +257 -0
  83. package/src-ext/runtime/claude/permissionInterruptBlock.mjs +86 -0
  84. package/src-ext/runtime/claude/permissionPromptServer.mjs +170 -0
  85. package/src-ext/runtime/claude/permissionPromptServerWorker.mjs +214 -0
  86. package/src-ext/runtime/claude/preflight.mjs +62 -0
  87. package/src-ext/runtime/claude/stdoutParser.mjs +365 -0
  88. package/src-ext/runtime/claude/todoTranslator.mjs +66 -0
  89. package/src-ext/runtime/claude/usageProbe.mjs +576 -0
  90. package/src-ext/runtime/claude/usageProbeStrip.mjs +14 -0
  91. package/src-ext/runtime/codex/codexCustomCommandsCollector.mjs +87 -0
  92. package/src-ext/runtime/codex/elicitationHandler.mjs +91 -0
  93. package/src-ext/runtime/codex/eventMapper.mjs +154 -0
  94. package/src-ext/runtime/codex/handleRequest.mjs +1152 -0
  95. package/src-ext/runtime/codex/index.mjs +142 -0
  96. package/src-ext/runtime/codex/mcpConfigAdapter.mjs +155 -0
  97. package/src-ext/runtime/codex/modelScanner.mjs +305 -0
  98. package/src-ext/runtime/codex/planNotificationHandler.mjs +61 -0
  99. package/src-ext/runtime/codex/preflight.mjs +67 -0
  100. package/src-ext/runtime/hermes/commandBackend.mjs +117 -0
  101. package/src-ext/runtime/hermes/envSetup.mjs +229 -0
  102. package/src-ext/runtime/hermes/gatewayManager.mjs +625 -0
  103. package/src-ext/runtime/hermes/handleRequest.mjs +1257 -0
  104. package/src-ext/runtime/hermes/httpBackend.mjs +219 -0
  105. package/src-ext/runtime/hermes/index.mjs +196 -0
  106. package/src-ext/runtime/hermes/mcpConfigAdapter.mjs +189 -0
  107. package/src-ext/runtime/hermes/preflight.mjs +359 -0
  108. package/src-ext/runtime/hermes/responsesBackend.mjs +276 -0
  109. package/src-ext/runtime/hermes/sessionStore.mjs +78 -0
  110. package/src-ext/runtime/hermes/todoTranslator.mjs +90 -0
  111. package/src-ext/runtime/hermes/workspaceRpc.mjs +124 -0
  112. package/src-ext/runtime/openclaw/buildOpenclawDaemonInput.mjs +143 -0
  113. package/src-ext/runtime/openclaw/mcpConfigAdapter.mjs +91 -0
  114. package/src-ext/runtime/openclaw/openclawConfig.mjs +145 -0
  115. package/src-ext/runtime/picoclaw/constants.mjs +39 -0
  116. package/src-ext/runtime/picoclaw/handleRequest.mjs +289 -0
  117. package/src-ext/runtime/picoclaw/index.mjs +314 -0
  118. package/src-ext/runtime/picoclaw/pairFlow.mjs +224 -0
  119. package/src-ext/runtime/picoclaw/state.mjs +78 -0
  120. package/src-ext/runtime/picoclaw/todoTranslator.mjs +67 -0
  121. package/src-ext/runtime/picoclaw/translator.mjs +272 -0
  122. package/src-ext/runtime/picoclaw/wsClient.mjs +129 -0
  123. package/src-ext/service/serviceManager.mjs +521 -0
  124. package/src-shared/README.md +44 -0
  125. package/src-shared/chat_push_normalizer.mjs +113 -0
  126. package/src-shared/correlation.mjs +25 -0
  127. package/src-shared/envelope_builder.mjs +612 -0
  128. package/src-shared/logger.mjs +167 -0
  129. package/src-shared/relay-defaults.json +3 -0
  130. package/src-shared/relay_revoke.mjs +46 -0
  131. package/src-shared/vendor/README.md +60 -0
  132. package/src-shared/vendor/smol-toml/LICENSE +24 -0
  133. package/src-shared/vendor/smol-toml/README.md +236 -0
  134. package/src-shared/vendor/smol-toml/VENDORED.md +37 -0
  135. package/src-shared/vendor/smol-toml/dist/date.d.ts +41 -0
  136. package/src-shared/vendor/smol-toml/dist/date.js +127 -0
  137. package/src-shared/vendor/smol-toml/dist/error.d.ts +38 -0
  138. package/src-shared/vendor/smol-toml/dist/error.js +63 -0
  139. package/src-shared/vendor/smol-toml/dist/extract.d.ts +30 -0
  140. package/src-shared/vendor/smol-toml/dist/extract.js +109 -0
  141. package/src-shared/vendor/smol-toml/dist/index.cjs +900 -0
  142. package/src-shared/vendor/smol-toml/dist/index.d.ts +43 -0
  143. package/src-shared/vendor/smol-toml/dist/index.js +33 -0
  144. package/src-shared/vendor/smol-toml/dist/parse.d.ts +37 -0
  145. package/src-shared/vendor/smol-toml/dist/parse.js +148 -0
  146. package/src-shared/vendor/smol-toml/dist/primitive.d.ts +31 -0
  147. package/src-shared/vendor/smol-toml/dist/primitive.js +178 -0
  148. package/src-shared/vendor/smol-toml/dist/stringify.d.ts +31 -0
  149. package/src-shared/vendor/smol-toml/dist/stringify.js +163 -0
  150. package/src-shared/vendor/smol-toml/dist/struct.d.ts +32 -0
  151. package/src-shared/vendor/smol-toml/dist/struct.js +194 -0
  152. package/src-shared/vendor/smol-toml/dist/util.d.ts +42 -0
  153. package/src-shared/vendor/smol-toml/dist/util.js +100 -0
  154. package/src-shared/vendor/smol-toml/package.json +54 -0
  155. package/src-shared/vendor/yaml/LICENSE +13 -0
  156. package/src-shared/vendor/yaml/README.md +155 -0
  157. package/src-shared/vendor/yaml/VENDORED.md +29 -0
  158. package/src-shared/vendor/yaml/bin.mjs +11 -0
  159. package/src-shared/vendor/yaml/browser/dist/compose/compose-collection.js +88 -0
  160. package/src-shared/vendor/yaml/browser/dist/compose/compose-doc.js +42 -0
  161. package/src-shared/vendor/yaml/browser/dist/compose/compose-node.js +92 -0
  162. package/src-shared/vendor/yaml/browser/dist/compose/compose-scalar.js +80 -0
  163. package/src-shared/vendor/yaml/browser/dist/compose/composer.js +217 -0
  164. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-map.js +113 -0
  165. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-scalar.js +198 -0
  166. package/src-shared/vendor/yaml/browser/dist/compose/resolve-block-seq.js +47 -0
  167. package/src-shared/vendor/yaml/browser/dist/compose/resolve-end.js +37 -0
  168. package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-collection.js +203 -0
  169. package/src-shared/vendor/yaml/browser/dist/compose/resolve-flow-scalar.js +223 -0
  170. package/src-shared/vendor/yaml/browser/dist/compose/resolve-props.js +148 -0
  171. package/src-shared/vendor/yaml/browser/dist/compose/util-contains-newline.js +34 -0
  172. package/src-shared/vendor/yaml/browser/dist/compose/util-empty-scalar-position.js +27 -0
  173. package/src-shared/vendor/yaml/browser/dist/compose/util-flow-indent-check.js +15 -0
  174. package/src-shared/vendor/yaml/browser/dist/compose/util-map-includes.js +17 -0
  175. package/src-shared/vendor/yaml/browser/dist/doc/Document.js +334 -0
  176. package/src-shared/vendor/yaml/browser/dist/doc/anchors.js +72 -0
  177. package/src-shared/vendor/yaml/browser/dist/doc/applyReviver.js +55 -0
  178. package/src-shared/vendor/yaml/browser/dist/doc/createNode.js +89 -0
  179. package/src-shared/vendor/yaml/browser/dist/doc/directives.js +176 -0
  180. package/src-shared/vendor/yaml/browser/dist/errors.js +57 -0
  181. package/src-shared/vendor/yaml/browser/dist/index.js +17 -0
  182. package/src-shared/vendor/yaml/browser/dist/log.js +14 -0
  183. package/src-shared/vendor/yaml/browser/dist/nodes/Alias.js +101 -0
  184. package/src-shared/vendor/yaml/browser/dist/nodes/Collection.js +147 -0
  185. package/src-shared/vendor/yaml/browser/dist/nodes/Node.js +38 -0
  186. package/src-shared/vendor/yaml/browser/dist/nodes/Pair.js +36 -0
  187. package/src-shared/vendor/yaml/browser/dist/nodes/Scalar.js +24 -0
  188. package/src-shared/vendor/yaml/browser/dist/nodes/YAMLMap.js +144 -0
  189. package/src-shared/vendor/yaml/browser/dist/nodes/YAMLSeq.js +113 -0
  190. package/src-shared/vendor/yaml/browser/dist/nodes/addPairToJSMap.js +104 -0
  191. package/src-shared/vendor/yaml/browser/dist/nodes/identity.js +36 -0
  192. package/src-shared/vendor/yaml/browser/dist/nodes/toJS.js +37 -0
  193. package/src-shared/vendor/yaml/browser/dist/parse/cst-scalar.js +214 -0
  194. package/src-shared/vendor/yaml/browser/dist/parse/cst-stringify.js +61 -0
  195. package/src-shared/vendor/yaml/browser/dist/parse/cst-visit.js +97 -0
  196. package/src-shared/vendor/yaml/browser/dist/parse/cst.js +98 -0
  197. package/src-shared/vendor/yaml/browser/dist/parse/lexer.js +717 -0
  198. package/src-shared/vendor/yaml/browser/dist/parse/line-counter.js +39 -0
  199. package/src-shared/vendor/yaml/browser/dist/parse/parser.js +954 -0
  200. package/src-shared/vendor/yaml/browser/dist/public-api.js +99 -0
  201. package/src-shared/vendor/yaml/browser/dist/schema/Schema.js +38 -0
  202. package/src-shared/vendor/yaml/browser/dist/schema/common/map.js +17 -0
  203. package/src-shared/vendor/yaml/browser/dist/schema/common/null.js +15 -0
  204. package/src-shared/vendor/yaml/browser/dist/schema/common/seq.js +17 -0
  205. package/src-shared/vendor/yaml/browser/dist/schema/common/string.js +14 -0
  206. package/src-shared/vendor/yaml/browser/dist/schema/core/bool.js +19 -0
  207. package/src-shared/vendor/yaml/browser/dist/schema/core/float.js +43 -0
  208. package/src-shared/vendor/yaml/browser/dist/schema/core/int.js +38 -0
  209. package/src-shared/vendor/yaml/browser/dist/schema/core/schema.js +23 -0
  210. package/src-shared/vendor/yaml/browser/dist/schema/json/schema.js +62 -0
  211. package/src-shared/vendor/yaml/browser/dist/schema/tags.js +83 -0
  212. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/binary.js +66 -0
  213. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/bool.js +26 -0
  214. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/float.js +46 -0
  215. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/int.js +71 -0
  216. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/omap.js +74 -0
  217. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/pairs.js +78 -0
  218. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/schema.js +37 -0
  219. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/set.js +93 -0
  220. package/src-shared/vendor/yaml/browser/dist/schema/yaml-1.1/timestamp.js +101 -0
  221. package/src-shared/vendor/yaml/browser/dist/stringify/foldFlowLines.js +146 -0
  222. package/src-shared/vendor/yaml/browser/dist/stringify/stringify.js +124 -0
  223. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyCollection.js +143 -0
  224. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyComment.js +20 -0
  225. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyDocument.js +85 -0
  226. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyNumber.js +24 -0
  227. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyPair.js +150 -0
  228. package/src-shared/vendor/yaml/browser/dist/stringify/stringifyString.js +328 -0
  229. package/src-shared/vendor/yaml/browser/dist/util.js +11 -0
  230. package/src-shared/vendor/yaml/browser/dist/visit.js +233 -0
  231. package/src-shared/vendor/yaml/browser/index.js +5 -0
  232. package/src-shared/vendor/yaml/browser/package.json +3 -0
  233. package/src-shared/vendor/yaml/dist/cli.d.ts +8 -0
  234. package/src-shared/vendor/yaml/dist/cli.mjs +199 -0
  235. package/src-shared/vendor/yaml/dist/compose/compose-collection.d.ts +11 -0
  236. package/src-shared/vendor/yaml/dist/compose/compose-collection.js +90 -0
  237. package/src-shared/vendor/yaml/dist/compose/compose-doc.d.ts +7 -0
  238. package/src-shared/vendor/yaml/dist/compose/compose-doc.js +44 -0
  239. package/src-shared/vendor/yaml/dist/compose/compose-node.d.ts +28 -0
  240. package/src-shared/vendor/yaml/dist/compose/compose-node.js +95 -0
  241. package/src-shared/vendor/yaml/dist/compose/compose-scalar.d.ts +5 -0
  242. package/src-shared/vendor/yaml/dist/compose/compose-scalar.js +82 -0
  243. package/src-shared/vendor/yaml/dist/compose/composer.d.ts +62 -0
  244. package/src-shared/vendor/yaml/dist/compose/composer.js +221 -0
  245. package/src-shared/vendor/yaml/dist/compose/resolve-block-map.d.ts +6 -0
  246. package/src-shared/vendor/yaml/dist/compose/resolve-block-map.js +115 -0
  247. package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.d.ts +11 -0
  248. package/src-shared/vendor/yaml/dist/compose/resolve-block-scalar.js +200 -0
  249. package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.d.ts +6 -0
  250. package/src-shared/vendor/yaml/dist/compose/resolve-block-seq.js +49 -0
  251. package/src-shared/vendor/yaml/dist/compose/resolve-end.d.ts +6 -0
  252. package/src-shared/vendor/yaml/dist/compose/resolve-end.js +39 -0
  253. package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.d.ts +7 -0
  254. package/src-shared/vendor/yaml/dist/compose/resolve-flow-collection.js +205 -0
  255. package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.d.ts +10 -0
  256. package/src-shared/vendor/yaml/dist/compose/resolve-flow-scalar.js +225 -0
  257. package/src-shared/vendor/yaml/dist/compose/resolve-props.d.ts +23 -0
  258. package/src-shared/vendor/yaml/dist/compose/resolve-props.js +150 -0
  259. package/src-shared/vendor/yaml/dist/compose/util-contains-newline.d.ts +2 -0
  260. package/src-shared/vendor/yaml/dist/compose/util-contains-newline.js +36 -0
  261. package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.d.ts +2 -0
  262. package/src-shared/vendor/yaml/dist/compose/util-empty-scalar-position.js +29 -0
  263. package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.d.ts +3 -0
  264. package/src-shared/vendor/yaml/dist/compose/util-flow-indent-check.js +17 -0
  265. package/src-shared/vendor/yaml/dist/compose/util-map-includes.d.ts +4 -0
  266. package/src-shared/vendor/yaml/dist/compose/util-map-includes.js +19 -0
  267. package/src-shared/vendor/yaml/dist/doc/Document.d.ts +141 -0
  268. package/src-shared/vendor/yaml/dist/doc/Document.js +336 -0
  269. package/src-shared/vendor/yaml/dist/doc/anchors.d.ts +24 -0
  270. package/src-shared/vendor/yaml/dist/doc/anchors.js +77 -0
  271. package/src-shared/vendor/yaml/dist/doc/applyReviver.d.ts +9 -0
  272. package/src-shared/vendor/yaml/dist/doc/applyReviver.js +57 -0
  273. package/src-shared/vendor/yaml/dist/doc/createNode.d.ts +17 -0
  274. package/src-shared/vendor/yaml/dist/doc/createNode.js +91 -0
  275. package/src-shared/vendor/yaml/dist/doc/directives.d.ts +49 -0
  276. package/src-shared/vendor/yaml/dist/doc/directives.js +178 -0
  277. package/src-shared/vendor/yaml/dist/errors.d.ts +21 -0
  278. package/src-shared/vendor/yaml/dist/errors.js +62 -0
  279. package/src-shared/vendor/yaml/dist/index.d.ts +22 -0
  280. package/src-shared/vendor/yaml/dist/index.js +50 -0
  281. package/src-shared/vendor/yaml/dist/log.d.ts +3 -0
  282. package/src-shared/vendor/yaml/dist/log.js +17 -0
  283. package/src-shared/vendor/yaml/dist/nodes/Alias.d.ts +28 -0
  284. package/src-shared/vendor/yaml/dist/nodes/Alias.js +103 -0
  285. package/src-shared/vendor/yaml/dist/nodes/Collection.d.ts +73 -0
  286. package/src-shared/vendor/yaml/dist/nodes/Collection.js +151 -0
  287. package/src-shared/vendor/yaml/dist/nodes/Node.d.ts +47 -0
  288. package/src-shared/vendor/yaml/dist/nodes/Node.js +40 -0
  289. package/src-shared/vendor/yaml/dist/nodes/Pair.d.ts +21 -0
  290. package/src-shared/vendor/yaml/dist/nodes/Pair.js +39 -0
  291. package/src-shared/vendor/yaml/dist/nodes/Scalar.d.ts +42 -0
  292. package/src-shared/vendor/yaml/dist/nodes/Scalar.js +27 -0
  293. package/src-shared/vendor/yaml/dist/nodes/YAMLMap.d.ts +53 -0
  294. package/src-shared/vendor/yaml/dist/nodes/YAMLMap.js +147 -0
  295. package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.d.ts +60 -0
  296. package/src-shared/vendor/yaml/dist/nodes/YAMLSeq.js +115 -0
  297. package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.d.ts +4 -0
  298. package/src-shared/vendor/yaml/dist/nodes/addPairToJSMap.js +106 -0
  299. package/src-shared/vendor/yaml/dist/nodes/identity.d.ts +23 -0
  300. package/src-shared/vendor/yaml/dist/nodes/identity.js +53 -0
  301. package/src-shared/vendor/yaml/dist/nodes/toJS.d.ts +27 -0
  302. package/src-shared/vendor/yaml/dist/nodes/toJS.js +39 -0
  303. package/src-shared/vendor/yaml/dist/options.d.ts +338 -0
  304. package/src-shared/vendor/yaml/dist/parse/cst-scalar.d.ts +64 -0
  305. package/src-shared/vendor/yaml/dist/parse/cst-scalar.js +218 -0
  306. package/src-shared/vendor/yaml/dist/parse/cst-stringify.d.ts +8 -0
  307. package/src-shared/vendor/yaml/dist/parse/cst-stringify.js +63 -0
  308. package/src-shared/vendor/yaml/dist/parse/cst-visit.d.ts +39 -0
  309. package/src-shared/vendor/yaml/dist/parse/cst-visit.js +99 -0
  310. package/src-shared/vendor/yaml/dist/parse/cst.d.ts +108 -0
  311. package/src-shared/vendor/yaml/dist/parse/cst.js +112 -0
  312. package/src-shared/vendor/yaml/dist/parse/lexer.d.ts +87 -0
  313. package/src-shared/vendor/yaml/dist/parse/lexer.js +719 -0
  314. package/src-shared/vendor/yaml/dist/parse/line-counter.d.ts +22 -0
  315. package/src-shared/vendor/yaml/dist/parse/line-counter.js +41 -0
  316. package/src-shared/vendor/yaml/dist/parse/parser.d.ts +84 -0
  317. package/src-shared/vendor/yaml/dist/parse/parser.js +958 -0
  318. package/src-shared/vendor/yaml/dist/public-api.d.ts +43 -0
  319. package/src-shared/vendor/yaml/dist/public-api.js +104 -0
  320. package/src-shared/vendor/yaml/dist/schema/Schema.d.ts +18 -0
  321. package/src-shared/vendor/yaml/dist/schema/Schema.js +40 -0
  322. package/src-shared/vendor/yaml/dist/schema/common/map.d.ts +2 -0
  323. package/src-shared/vendor/yaml/dist/schema/common/map.js +19 -0
  324. package/src-shared/vendor/yaml/dist/schema/common/null.d.ts +4 -0
  325. package/src-shared/vendor/yaml/dist/schema/common/null.js +17 -0
  326. package/src-shared/vendor/yaml/dist/schema/common/seq.d.ts +2 -0
  327. package/src-shared/vendor/yaml/dist/schema/common/seq.js +19 -0
  328. package/src-shared/vendor/yaml/dist/schema/common/string.d.ts +2 -0
  329. package/src-shared/vendor/yaml/dist/schema/common/string.js +16 -0
  330. package/src-shared/vendor/yaml/dist/schema/core/bool.d.ts +4 -0
  331. package/src-shared/vendor/yaml/dist/schema/core/bool.js +21 -0
  332. package/src-shared/vendor/yaml/dist/schema/core/float.d.ts +4 -0
  333. package/src-shared/vendor/yaml/dist/schema/core/float.js +47 -0
  334. package/src-shared/vendor/yaml/dist/schema/core/int.d.ts +4 -0
  335. package/src-shared/vendor/yaml/dist/schema/core/int.js +42 -0
  336. package/src-shared/vendor/yaml/dist/schema/core/schema.d.ts +1 -0
  337. package/src-shared/vendor/yaml/dist/schema/core/schema.js +25 -0
  338. package/src-shared/vendor/yaml/dist/schema/json/schema.d.ts +2 -0
  339. package/src-shared/vendor/yaml/dist/schema/json/schema.js +64 -0
  340. package/src-shared/vendor/yaml/dist/schema/json-schema.d.ts +69 -0
  341. package/src-shared/vendor/yaml/dist/schema/tags.d.ts +40 -0
  342. package/src-shared/vendor/yaml/dist/schema/tags.js +86 -0
  343. package/src-shared/vendor/yaml/dist/schema/types.d.ts +90 -0
  344. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.d.ts +2 -0
  345. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/binary.js +68 -0
  346. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.d.ts +7 -0
  347. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  348. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.d.ts +4 -0
  349. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/float.js +50 -0
  350. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.d.ts +5 -0
  351. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/int.js +76 -0
  352. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.d.ts +28 -0
  353. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  354. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.d.ts +10 -0
  355. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  356. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -0
  357. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/schema.js +39 -0
  358. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.d.ts +28 -0
  359. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/set.js +96 -0
  360. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.d.ts +6 -0
  361. package/src-shared/vendor/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  362. package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.d.ts +34 -0
  363. package/src-shared/vendor/yaml/dist/stringify/foldFlowLines.js +151 -0
  364. package/src-shared/vendor/yaml/dist/stringify/stringify.d.ts +21 -0
  365. package/src-shared/vendor/yaml/dist/stringify/stringify.js +127 -0
  366. package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.d.ts +17 -0
  367. package/src-shared/vendor/yaml/dist/stringify/stringifyCollection.js +145 -0
  368. package/src-shared/vendor/yaml/dist/stringify/stringifyComment.d.ts +10 -0
  369. package/src-shared/vendor/yaml/dist/stringify/stringifyComment.js +24 -0
  370. package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.d.ts +4 -0
  371. package/src-shared/vendor/yaml/dist/stringify/stringifyDocument.js +87 -0
  372. package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.d.ts +2 -0
  373. package/src-shared/vendor/yaml/dist/stringify/stringifyNumber.js +26 -0
  374. package/src-shared/vendor/yaml/dist/stringify/stringifyPair.d.ts +3 -0
  375. package/src-shared/vendor/yaml/dist/stringify/stringifyPair.js +152 -0
  376. package/src-shared/vendor/yaml/dist/stringify/stringifyString.d.ts +9 -0
  377. package/src-shared/vendor/yaml/dist/stringify/stringifyString.js +330 -0
  378. package/src-shared/vendor/yaml/dist/test-events.d.ts +4 -0
  379. package/src-shared/vendor/yaml/dist/test-events.js +134 -0
  380. package/src-shared/vendor/yaml/dist/util.d.ts +12 -0
  381. package/src-shared/vendor/yaml/dist/util.js +28 -0
  382. package/src-shared/vendor/yaml/dist/visit.d.ts +102 -0
  383. package/src-shared/vendor/yaml/dist/visit.js +236 -0
  384. package/src-shared/vendor/yaml/package.json +96 -0
  385. 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
+ }