@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
package/README.md ADDED
@@ -0,0 +1,860 @@
1
+ # @xyagent/cli
2
+
3
+ 把本地 AI runtime(OpenClaw 或 Hermes)接入 Agentlink relay 服务的独立 CLI。
4
+
5
+ 手机端 App 通过 relay 跟本地 runtime 对话,CLI 负责:
6
+
7
+ - 配对:把手机 App 账号和本地 runtime 绑定到同一个 relay gateway
8
+ - 桥接:长时间运行一个中继 worker,把 relay 队列里的请求拉下来、喂给本地 runtime、再把事件发回 relay
9
+ - 守护:用系统服务(launchd / systemd / Task Scheduler)让桥接在后台常驻
10
+
11
+ **深入文档**:
12
+
13
+ - [docs/PAIR_FLOW.md](docs/PAIR_FLOW.md) — `agentlink pair` 完整流程:每一步做了什么、改了哪些文件、`--runtime all` 模式遇到 runtime 缺失怎么处理、对 OpenClaw 配置改动的安全性分析、故障排查 checklist
14
+ - [docs/EXTERNAL_AGENTS.md](docs/EXTERNAL_AGENTS.md) — claude / codex 等外部 agent 接入指南
15
+ - [docs/LOGGING.md](docs/LOGGING.md) — 日志格式与排查
16
+ - [docs/PLATFORM.md](docs/PLATFORM.md) — 跨平台细节(launchd / systemd / Windows)
17
+ - [agentlink-server/TOOL_EVENTS.md](../agentlink-server/TOOL_EVENTS.md) — 工具调用链事件 schema(OpenClaw plugin / Hermes / Codex / Claude → server `session.tool` 字段映射 + 真实样例)
18
+
19
+ ---
20
+
21
+ ## 1. 整体架构
22
+
23
+ ```
24
+ ┌────────────────┐ HTTPS / SSE ┌───────────────────────┐
25
+ │ Mobile App │◀─────────────────▶│ Relay Server (Go) │
26
+ │ (nexus_flutter)│ │ go-relay-test.xyagent.com │
27
+ └────────────────┘ │ │
28
+ │ MySQL + Redis │
29
+ └───────────┬───────────┘
30
+
31
+ long-poll + POST events
32
+
33
+ ┌───────────▼───────────┐
34
+ │ agentlink CLI (Node) │
35
+ │ bin/agentlink │
36
+ │ │
37
+ │ ┌─────────────────┐ │
38
+ │ │ Bridge worker │ │
39
+ │ └────────┬────────┘ │
40
+ └───────────┼───────────┘
41
+
42
+ ┌───────────────┬────────────────┼────────────────┬──────────────┐
43
+ │ │ │ │ │
44
+ (runtime=openclaw) (runtime=hermes) (runtime=claude) (runtime=codex) │
45
+ │ │ │ │ │
46
+ HTTP /v1/responses agentlink- agentlink- agentlink- (其他 runtime
47
+ │ agent hermes agent claude agent codex 走相同 shim)
48
+ ▼ ▼ ▼ ▼ ▼
49
+ ┌────────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
50
+ │ OpenClaw GW │ │ Hermes JS │ │ Claude CLI │ │ Codex MCP │
51
+ │ (外部进程) │ │ HTTP/cmd │ │ stream-json│ │ stdio │
52
+ │ 127.0.0.1:<p> │ │ backend │ │ │ │ │
53
+ └────────────────┘ └────────────┘ └────────────┘ └────────────┘
54
+ ```
55
+
56
+ **核心架构(重构后)**:4 种 runtime 走**完全统一的 long-poll 模式**,CLI 端不做协议翻译。
57
+
58
+ ```
59
+ 启动 (任意 runtime bridge)
60
+
61
+ 1. POST /v1/pair-codes/<code>/consume
62
+ body { client_user_id, runtime }
63
+ resp { gw_id, bridge_token, runtime }
64
+
65
+ 2. 持久化 ~/.agentlink/ext-<runtime>-last.json
66
+
67
+ 3. long-poll loop(永不退出):
68
+ ┌─ GET /v1/gateways/<gw>/requests/next?waitSeconds=25
69
+ │ Bearer <bridge_token>
70
+ │ ↓ 拿到 user request
71
+
72
+ ├─ 调本地 runtime(LLM HTTP / Claude CLI / Codex MCP / OpenClaw HTTP)
73
+
74
+ ├─ 流式 POST /v1/gateways/<gw>/requests/<rid>/events
75
+ │ body 是该 runtime 的 native 事件(stream-json / OpenAI SSE chunk / codex/event 等)
76
+ │ ↑↑↑ 服务端 internal/adapter/<runtime>/ 翻译为统一信封
77
+
78
+ └─ 心跳 POST /v1/gateways/<gw>/meta(每 10s)
79
+ ```
80
+
81
+ **关键特征(重构后)**:
82
+
83
+ - CLI bridge **不知道**统一 Agent SSE 协议(`/v1/agents/threads/*` 端点)的存在
84
+ - CLI bridge **直接发** runtime 自己的原生事件,服务端 `adapter/<runtime>/` 做 native → unified envelope 翻译
85
+ - 之前 CLI 端的 `src-ext/core/unified_relay_shim.mjs` 翻译层 + `src-shared/agent-client/` HTTP 封装**已整体删除**
86
+ - 4 个 bridge(OpenClaw / Hermes / Claude / Codex)共用同一套 `relayWorker` long-poll 主循环
87
+
88
+ 三个关键角色:
89
+
90
+ | 角色 | 位置 | 职责 |
91
+ | ---------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
92
+ | **Relay Server** | `agentlink-server/`(Go) | 握住 App ↔ runtime 之间的所有流量;`internal/adapter/<runtime>/` 把 native 事件翻成统一信封 |
93
+ | **CLI** | `bin/agentlink` + `bin/agentlink-agent`(Node ≥22)| 配对 + 桥接;4 种 runtime 共用 `relayWorker` long-poll 主循环;不做协议翻译 |
94
+ | **Runtime** | OpenClaw gateway / Hermes(JS) / Claude CLI / Codex CLI | 实际执行 AI turn 的进程或本地 LLM 调用 |
95
+
96
+ ### 4 种 runtime bridge 对比
97
+
98
+ | Runtime | 启动命令 | 入口文件 | native 协议(POST 给服务端的事件格式) | 服务端 adapter |
99
+ | -------- | ------------------------- | --------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------- |
100
+ | OpenClaw | `agentlink bridge` | `src/tunnel.mjs` + `src/tunnel_proxy.mjs` + `src/tunnel_state.mjs` | OpenClaw `/v1/responses` SSE chunk | `internal/adapter/openclaw` |
101
+ | Hermes | `agentlink-agent hermes` | `src-ext/runtime/hermes/index.mjs` | OpenAI-compatible SSE / NDJSON | `internal/adapter/hermes`(共享 openclaw 逻辑) |
102
+ | Claude | `agentlink-agent claude` | `src-ext/runtime/claude/index.mjs` | Claude CLI stream-json | `internal/adapter/claude` |
103
+ | Codex | `agentlink-agent codex` | `src-ext/runtime/codex/index.mjs` | Codex MCP `codex/event` notification | `internal/adapter/codex` |
104
+
105
+ 4 个 bridge 入口文件大小都 ≤ 800 行硬限,最大约 700 行。
106
+
107
+ ---
108
+
109
+ ## 2. 代码结构
110
+
111
+ ```
112
+ agentlink-cli/
113
+ ├── bin/
114
+ │ ├── agentlink # 用户入口(setup / pair / bridge / service / tunnel)
115
+ │ ├── agentlink-agent # bridge 子进程入口(claude / codex / hermes)
116
+ │ └── agentlink-mcp-stdio # codex 反向 MCP stdio bridge
117
+ ├── src/ # OpenClaw bridge + tunnel + 共享工具
118
+ │ ├── core.mjs # URL / code 归一化 / 路径解析等
119
+ │ ├── tunnel.mjs # OpenClaw bridge 主循环(277 行)
120
+ │ ├── tunnel_proxy.mjs # OpenClaw HTTP / SSE 转发(476 行)
121
+ │ ├── tunnel_state.mjs # 文件锁 / 状态机(301 行)
122
+ │ └── tunnel_service.mjs # OpenClaw 桥接的 service install/uninstall
123
+ ├── src-shared/ # 纯基础设施(无业务依赖)
124
+ │ ├── logger.mjs # 结构化 JSON 日志
125
+ │ ├── correlation.mjs # cid 生成 / 解析
126
+ │ └── relay-defaults.json # 默认 relay URL
127
+ ├── src-ext/ # External Agents(Claude / Codex / Hermes)
128
+ │ ├── bin.mjs # agentlink-agent 入口分发
129
+ │ ├── commands/ # pair / service / agent / reload / help 子命令
130
+ │ ├── core/
131
+ │ │ ├── pairCodeClient.mjs # POST /v1/pair-codes/<code>/consume + 状态持久化
132
+ │ │ ├── relayWorker.mjs # 4 个 bridge 共用的 long-poll 主循环
133
+ │ │ ├── relayWorkerHttp.mjs # bridge_token Bearer header / fetchJson 工具
134
+ │ │ ├── sessionSlot.mjs # 单 session 槽状态机
135
+ │ │ ├── stateMachine.mjs # 通用 FSM
136
+ │ │ ├── approvalGateway.mjs # HITL 工具审批
137
+ │ │ ├── mcpStdioClient.mjs # MCP stdio 客户端(codex 用)
138
+ │ │ ├── agentlinkToolsRegistry.mjs / agentlinkToolsServer.mjs # 反向工具注入
139
+ │ │ ├── accountResolver.mjs # accountId/clientUserId 解析
140
+ │ │ └── usageReporter.mjs # token 用量上报
141
+ │ ├── runtime/
142
+ │ │ ├── claude/index.mjs # Claude bridge(spawn `claude` CLI + stream-json,223 行)
143
+ │ │ ├── codex/index.mjs # Codex bridge(spawn `codex` MCP stdio,240 行)
144
+ │ │ └── hermes/index.mjs # Hermes bridge(OpenAI-compatible HTTP / subprocess,256 行)
145
+ │ ├── service/
146
+ │ │ └── serviceManager.mjs # launchd / systemd / Task Scheduler 统一接入
147
+ │ └── openclaw-plugin/ # agentlink-hooks OpenClaw 插件源码(详见 docs/PLUGIN_ACTIVE_RUNS.md)
148
+ └── test/ # node --test 测试套件(72 个 test)
149
+ ```
150
+
151
+ **Hermes 已 100% JS 化** —— 旧 `runtime/hermes_bridge/` Python 目录已删除。`bin/agentlink` 中所有 Python 探测 / spawn 逻辑也已清除。
152
+
153
+ **已删除的代码(重构清理)**:
154
+
155
+ - `src-ext/core/unified_relay_shim.mjs` —— 整个文件删除。之前 CLI 在这里把 legacy `{kind: ...}` 事件翻译成统一信封 + POST 到 `/v1/agents/threads/<tid>/events`,现在改由服务端 adapter 翻译
156
+ - `src-shared/agent-client/` —— 整个目录删除。CLI bridge 不再调统一协议端点,因此不需要 `/v1/agents/*` HTTP + SSE 客户端
157
+ - 各 runtime 子目录里的 unified 翻译辅助文件(如 `usageReporter` 的 envelope 包装等)
158
+
159
+ `bin/agentlink` 本身只 spawn `agentlink-agent` 子进程;`openclaw` CLI 是外部可执行文件,通过 `PATH` / `OPENCLAW_HOME` 定位。
160
+
161
+ ---
162
+
163
+ ## 3. 4 种 runtime 模式
164
+
165
+ CLI 通过 `runtimeKind` 分流。**所有 runtime 在线协议层完全一致** —— 4 个 bridge 共用 `src-ext/core/relayWorker.mjs` long-poll 主循环,通过相同的 3 个端点跟 relay 通讯:
166
+
167
+ - `POST /v1/pair-codes/<code>/consume`(启动配对)
168
+ - `GET /v1/gateways/<gw>/requests/next?waitSeconds=25`(长轮询拉用户消息)
169
+ - `POST /v1/gateways/<gw>/requests/<rid>/events`(流式回 native 事件)
170
+ - `POST /v1/gateways/<gw>/meta`(10s 心跳)
171
+
172
+ 唯一的差异是 bridge 内部对接的本地 runtime 进程,以及推回 relay 的 native 事件格式。**CLI 不做协议翻译**,服务端 `internal/adapter/<runtime>/` 把 native 事件统一翻译为 unified envelope 落库 `agent_events` 表。
173
+
174
+ ### 3.1 OpenClaw 模式(默认)
175
+
176
+ ```
177
+ App ──► Relay ──► CLI bridge (Node) ──► HTTP POST /v1/responses ──► OpenClaw Gateway
178
+
179
+ events ◄──────────────── SSE / JSON ┘
180
+ (publish back to relay)
181
+ ```
182
+
183
+ - 入口:`agentlink bridge`(`src/tunnel.mjs` 主循环 + `tunnel_proxy.mjs` HTTP / SSE 转发 + `tunnel_state.mjs` 状态机)
184
+ - OpenClaw 是独立的外部进程(`openclaw gateway run`),监听 `127.0.0.1:<port>`
185
+ - CLI 把 relay long-poll 拉下来的请求转成 `POST /v1/responses`,再把流式响应原样切片发回 relay
186
+ - 网关鉴权:CLI 从 `~/.openclaw/config.json` 读 `gateway.auth.token`
187
+ - 服务端 `internal/adapter/openclaw` 把 OpenClaw 的 SSE chunk 翻译为统一信封事件
188
+
189
+ ### 3.2 Hermes 模式(pure JS,已替代旧 Python bridge)
190
+
191
+ ```
192
+ App ──► Relay ──► agentlink-agent hermes ──► OpenAI-compatible HTTP
193
+ long-poll (Node 22+ stdlib fetch) (or external command)
194
+ ```
195
+
196
+ - 入口:`agentlink-agent hermes`(`src-ext/runtime/hermes/index.mjs`,256 行)
197
+ - 走 `relayWorker` long-poll:拿到 user request → 调本地 LLM → 流式回 OpenAI-compatible SSE chunk 给 relay
198
+ - 后端 2 选 1:
199
+ - **HTTP**:`HERMES_BRIDGE_API_BASE_URL` + `HERMES_BRIDGE_API_KEY`(OpenAI / DeepSeek / 自建 vLLM 等)
200
+ - **subprocess**:`HERMES_BRIDGE_COMMAND="<path-to-binary>"`(newline-delimited JSON 协议)
201
+ - 本地 transcript 存在 `~/.agentlink/hermes/sessions/<sessionId>.json`
202
+ - 零 Python 依赖
203
+
204
+ ### 3.3 Claude 模式
205
+
206
+ ```
207
+ App ──► Relay ──► agentlink-agent claude ──► spawn `claude` CLI (stream-json)
208
+ ```
209
+
210
+ - 入口:`agentlink-agent claude`(`src-ext/runtime/claude/index.mjs`,223 行)
211
+ - spawn 外部 `claude` CLI(Anthropic 官方),从 stdout 读 stream-json,**直接**作为事件 POST 到 `/v1/gateways/<gw>/requests/<rid>/events`
212
+ - 服务端 `internal/adapter/claude` 解析 stream-json,把 `assistant_message_delta` / `tool_use_block` / `parent_tool_use_id` 等翻译成统一信封事件(`content_block.delta` / `tool_use` / subagent run_id 等)
213
+ - 工具审批走 `approvalGateway`:PreToolUse hook → 推 interrupt block → 等 App 端 resume
214
+
215
+ ### 3.4 Codex 模式
216
+
217
+ ```
218
+ App ──► Relay ──► agentlink-agent codex ──► spawn `codex` MCP stdio server
219
+ ```
220
+
221
+ - 入口:`agentlink-agent codex`(`src-ext/runtime/codex/index.mjs`,240 行)
222
+ - 通过 MCP stdio 协议跟 `codex` CLI 通信,反向注入 `agentlink-mcp-stdio` 作为工具服务器
223
+ - `codex/event` notification 直接序列化为事件 POST 给 relay
224
+ - 服务端 `internal/adapter/codex` 把 `codex/event` 翻译为统一信封
225
+
226
+ 4 种 runtime 使用**独立**的 relay 凭证和独立的 session 模型(见 `AppRuntimeKind` 枚举),配对流程里由 App 端决定写哪一组。
227
+
228
+ ---
229
+
230
+ ## 4. Relay 协议
231
+
232
+ CLI 跟 relay 之间只走 HTTP(长轮询 + POST),没有 WebSocket。关键端点:
233
+
234
+ ### 4.1 配对阶段
235
+
236
+ | 端点 | 方向 | 用途 |
237
+ | ------------------------------------------ | ----------- | -------------------------------------------------------------------- |
238
+ | `POST /v1/pair-sessions` | App → Relay | App 创建 4 位 pair code(含 `accountId` / `runtimeKind`) |
239
+ | `GET /v1/pair-sessions/{code}` | CLI → Relay | (openclaw 模式)查配对 session 是否存在、读出 App 设定的 runtimeKind |
240
+ | **`POST /v1/pair-codes/{code}/consume`** | CLI → Relay | **统一入口**:消费 4 位码,body `{client_user_id, runtime}` → 颁发 `{gw_id, bridge_token, runtime}` |
241
+
242
+ 兼容性:服务端仍保留 `POST /api/ext/pair-codes/<code>/consume?runtime=<rt>`(legacy 老 CLI 用),新 CLI 默认调 `/v1/pair-codes/...`,404 / 410 时回退老端点(升级期间双端兼容)。
243
+
244
+ ### 4.2 桥接阶段
245
+
246
+ | 端点 | 方向 | 用途 |
247
+ | ----------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------- |
248
+ | `GET /v1/gateways/{gwId}/requests/next?waitSeconds=N` | CLI/bridge → Relay | long-poll 下一个 request(204 表示超时) |
249
+ | `POST /v1/gateways/{gwId}/requests/{reqId}/events` | CLI/bridge → Relay | 推 native 事件(`start` / `chunk` / `end` / `error` / `response`),服务端 adapter 翻译为 unified envelope |
250
+ | `POST /v1/gateways/{gwId}/chat-events` | CLI → Relay | OpenClaw 模式下发送结构化聊天事件(供 App 做 structured history) |
251
+ | `POST /v1/gateways/{gwId}/meta` | CLI/bridge → Relay | presence 心跳(`online` / `connected` / `state`),默认 10s 一次 |
252
+ | `GET/DELETE /v1/gateways/{gwId}/binding` | CLI → Relay | 查询 / 解绑当前 gateway |
253
+ | `POST /v1/gateways/{gwId}/reset` | CLI → Relay | 硬重置(`agentlink reset`) |
254
+
255
+ **鉴权**:桥接阶段所有端点用 `Authorization: Bearer <bridge_token>`(pair-codes consume 颁发,单向 producer-only)。
256
+
257
+ > **bridge_token vs client_token**
258
+ >
259
+ > CLI 持有的是 `bridge_token`(producer 凭证):能 long-poll 拉 user request、能 POST events 推回。
260
+ > App 侧持有的是 `client_token`(consumer 凭证):能 GET `/v1/agents/threads/<tid>/stream` 订阅、能查询 thread 状态。
261
+ > **两个 token 互不可换用**,混用返回 403。
262
+
263
+ ### 4.3 事件形状
264
+
265
+ 桥接 worker 发回 relay 的事件统一是:
266
+
267
+ ```json
268
+ {
269
+ "event": "start|chunk|end|error|response",
270
+ "sessionKey": "agent:<id>:relay-gw:<gwId>::<sessionKey>",
271
+ "runId": "...",
272
+ "statusCode": 200,
273
+ "contentType": "text/event-stream; charset=utf-8",
274
+ "data": "...",
275
+ "createdAtMs": 1713312000000
276
+ }
277
+ ```
278
+
279
+ `sessionKey` 格式前后端不一致:App 侧带装饰前缀(`agent:` / `relay-gw:`),relay 事件来的是裸 key。两边比较必须归一化(Go 端 `normalizeSessionKeyForMatch`,Flutter 端 `_sessionKeyMatchesForPreview`)。
280
+
281
+ ---
282
+
283
+ ## 5. 典型流程
284
+
285
+ ### 5.1 配对(`agentlink pair <code>`,统一 long-poll 模式)
286
+
287
+ 4 种 runtime 走完全一致的配对流程(OpenClaw 多一步本地 daemon 重启,详见 [docs/PAIR_FLOW.md](docs/PAIR_FLOW.md)):
288
+
289
+ ```
290
+ App Relay CLI
291
+ │ │ │
292
+ │ 1. POST /v1/pair-sessions │ │
293
+ │ {runtime, accountId, │ │
294
+ │ clientUserId} │ │
295
+ ├────────────────────────────▶│ │
296
+ │ │ │
297
+ │ 2. App 屏幕显示 4 位 code │ │
298
+ │ │ │
299
+ │ │ 3. 用户在 CLI 输入 code │
300
+ │ │ │
301
+ │ │ 4. POST /v1/pair-codes/ │
302
+ │ │ <code>/consume │
303
+ │ │ {client_user_id, │
304
+ │ │ runtime} │
305
+ │ │◀────────────────────────────┤
306
+ │ │ │
307
+ │ │ 5. {gw_id, bridge_token, │
308
+ │ │ runtime} │
309
+ │ ├────────────────────────────▶│
310
+ │ │ │
311
+ │ │ 6. 持久化到 │
312
+ │ │ ~/.agentlink/ │
313
+ │ │ ext-<runtime>-last.json│
314
+ │ │ │
315
+ │ │ 7. 进入 long-poll loop: │
316
+ │ │ GET .../requests/next │
317
+ │ │◀────────────────────────────┤
318
+ │ │ │
319
+ │ 8. App 端订阅 thread │ │
320
+ │ GET /v1/agents/threads/ │ │
321
+ │ <tid>/stream │ │
322
+ ├────────────────────────────▶│ │
323
+ ```
324
+
325
+ 配对完成判定 = `/pair-codes/<code>/consume` 返回 `gw_id` + `bridge_token`。CLI 立刻进入 long-poll 主循环,不需要再等 App 端"确认"。
326
+
327
+ 持久化位置(按 runtime 分文件,互不干扰):
328
+
329
+ ```
330
+ ~/.agentlink/ext-openclaw-last.json
331
+ ~/.agentlink/ext-hermes-last.json
332
+ ~/.agentlink/ext-claude-last.json
333
+ ~/.agentlink/ext-codex-last.json
334
+ ```
335
+
336
+ 文件内容:`{ gw_id, runtime, relay_url, bridge_token, saved_at }`。`session_id` 字段保留作老 CLI 兼容(值与 `gw_id` 相同)。
337
+
338
+ ### 5.2 桥接(`agentlink bridge`,OpenClaw 模式)
339
+
340
+ ```
341
+ ┌──────────┐ ┌──────────┐ ┌────────────┐
342
+ │ CLI │ long-poll 90s │ Relay │ │ OpenClaw │
343
+ │ bridge │───────────────────▶│ │ │ Gateway │
344
+ │ │◀───────────────────│ │ │ /v1/resp.. │
345
+ │ │ request │ │ │ │
346
+ │ │ │ │ │ │
347
+ │ │─── POST /v1/resp ─────────────────────────▶ │ │
348
+ │ │◀── SSE stream ────────────────────────────── │ │
349
+ │ │ │ │ │ │
350
+ │ │── POST events ────▶│ │ │ │
351
+ │ │ (start/chunk/ │ │ │ │
352
+ │ │ end) │ │ │ │
353
+ │ │ │ │ │ │
354
+ │ │── POST meta ──────▶│ │ │ │
355
+ │ │ (heartbeat 10s) │ │ │ │
356
+ └──────────┘ └──────────┘ └────────────┘
357
+ ```
358
+
359
+ - Bridge 使用 `acquireBridgeLock` 做文件锁,防止一台机器上两个 bridge 抢同一个 gateway
360
+ - SIGINT/SIGTERM 时发一次 `state: "stopped"` presence,让 App 立刻看到离线(而不是等 25s TTL)
361
+
362
+ ### 5.3 桥接(Hermes / Claude / Codex 模式 — 统一 long-poll)
363
+
364
+ 3 个 External Agent runtime 共用同一套主循环,只是本地 backend 不同:
365
+
366
+ ```
367
+ agentlink-agent <hermes|claude|codex>
368
+
369
+ ├── relayWorker.run(handler):
370
+ │ loop:
371
+ │ req = GET /v1/gateways/<gw>/requests/next?waitSeconds=25
372
+ │ if 204 → continue // long-poll 超时
373
+ │ handler(req, ctx) →
374
+
375
+ ├── handler 实现(按 runtime 分流):
376
+ │ hermes: fetch(POST OpenAI /chat/completions, stream:true)
377
+ │ ↓ 每个 SSE chunk: ctx.publishEvent({kind:"chunk", data:...})
378
+ │ claude: spawn `claude` CLI(stream-json mode)
379
+ │ ↓ 每个 stream-json 事件: ctx.publishEvent({kind:"chunk", data:...})
380
+ │ codex: MCP stdio 双向通讯 + 反向工具注入
381
+ │ ↓ 每个 codex/event: ctx.publishEvent({kind:"chunk", data:...})
382
+
383
+ ├── ctx.publishEvent → POST /v1/gateways/<gw>/requests/<rid>/events
384
+ │ body 是 native 事件,**不在 CLI 翻译**
385
+
386
+ └── 心跳 POST /v1/gateways/<gw>/meta(每 10s)
387
+ ```
388
+
389
+ 服务端 `internal/adapter/<runtime>/` 接到 native 事件后翻译为统一信封写入 `agent_events` 表。
390
+
391
+ Hermes 会话历史持久化在 `~/.agentlink/hermes/sessions/<sessionId>.json`。无 Python,无 venv,无 pip install。
392
+
393
+ ---
394
+
395
+ ## 6. 安装 & 快速开始
396
+
397
+ ```bash
398
+ npm i -g @xyagent/cli
399
+ ```
400
+
401
+ OpenClaw 模式(默认):
402
+
403
+ ```bash
404
+ # 1) 准备 OpenClaw 配置 + relay 凭证
405
+ agentlink setup \
406
+ --relay http://<relay-host>:8090 \
407
+ --public-base http://<gateway-public-host>:<port>
408
+
409
+ # 2) 启动本地 OpenClaw gateway
410
+ openclaw gateway run --bind loopback --port <port> --force
411
+
412
+ # 3) 用 App 里生成的 code 完成配对
413
+ agentlink pair 123456
414
+
415
+ # 4) 把桥接装成后台服务
416
+ agentlink service install
417
+ ```
418
+
419
+ Hermes 模式(pure JS,无 Python 依赖):
420
+
421
+ ```bash
422
+ # 配置后端:HTTP(OpenAI-compatible)
423
+ export HERMES_BRIDGE_API_BASE_URL=https://api.openai.com/v1
424
+ export HERMES_BRIDGE_API_KEY=sk-xxx
425
+ export HERMES_BRIDGE_MODEL=gpt-4o-mini
426
+
427
+ # 或者用 subprocess 后端(外部 binary,NDJSON 协议)
428
+ # export HERMES_BRIDGE_COMMAND=/path/to/my-llm-script
429
+
430
+ # 配对(CLI 自动检测后端 env,未配置会报错)
431
+ agentlink pair 123456 --runtime hermes
432
+
433
+ # 前台跑(调试)
434
+ agentlink bridge --runtime hermes
435
+ ```
436
+
437
+ Claude / Codex External Agents:
438
+
439
+ ```bash
440
+ # Claude Code / Codex CLI 需要先在本机安装并登录
441
+
442
+ # 配对后会自动安装后台服务
443
+ agentlink pair 1234 -r claude
444
+ agentlink pair 1234 -r codex
445
+
446
+ # 前台调试运行(当前终端内运行,Ctrl+C 可停止)
447
+ agentlink bridge -r claude
448
+ agentlink bridge -r codex
449
+ ```
450
+
451
+ 重装 App 后重新配对(典型场景):
452
+
453
+ ```bash
454
+ # 如果之前是 hermes 模式,配对报 "这台主机已经绑定过设备"
455
+ agentlink reset --runtime hermes # 只清 hermes 凭证,不动 openclaw
456
+ agentlink pair <new-code> --runtime hermes
457
+
458
+ # openclaw 模式不传 --runtime 即可
459
+ agentlink reset
460
+ agentlink pair <new-code>
461
+ ```
462
+
463
+ ---
464
+
465
+ ## 7. 命令参考
466
+
467
+ ```bash
468
+ agentlink setup [--relay URL] [--public-base URL] # 写配置 + 装权限
469
+ agentlink pair <code> [--runtime openclaw|hermes|claude|codex] # 配对;claude/codex 默认会自动装后台服务
470
+ agentlink bridge [--runtime openclaw|hermes|claude|codex] # 前台运行桥接;claude/codex 用 Ctrl+C 停止
471
+ agentlink service install|stop|uninstall|restart|status [--runtime ...] # 后台服务;claude/codex 中 stop 是 uninstall 别名
472
+ agentlink uninstall --runtime claude|codex # claude/codex 完整卸载:停服务 + 清本地配对状态
473
+ agentlink reset [--runtime openclaw|hermes] [--gateway GWID] # 硬重置绑定
474
+ agentlink pair-url [--account ID] [--code CODE] # 生成手动配对 URL
475
+ agentlink tunnel on <port> [--relay URL] # 暴露本地 HTTP 端口
476
+ agentlink tunnel off <port> [--relay URL] # 关闭端口转发
477
+ agentlink tunnel ls # 查看 tunnel 状态
478
+ agentlink status [--runtime openclaw|hermes] # 查当前 runtime / 服务 / 绑定
479
+ ```
480
+
481
+ ### Tunnel 快速测试
482
+
483
+ ```bash
484
+ # 启一个本地测试服务(任何 HTTP 服务器都行;这里用 Node 内置)
485
+ mkdir -p /tmp/agentlink-tunnel-demo
486
+ printf 'hello tunnel\n' >/tmp/agentlink-tunnel-demo/index.html
487
+ node -e 'import("node:http").then(({createServer})=>{import("node:fs").then(({readFileSync})=>{createServer((_,res)=>res.end(readFileSync("/tmp/agentlink-tunnel-demo/index.html"))).listen(8080,"127.0.0.1");console.log("listening on 127.0.0.1:8080");})})'
488
+ ```
489
+
490
+ 另一个终端:
491
+
492
+ ```bash
493
+ agentlink tunnel on 8080
494
+ ```
495
+
496
+ 示例输出:
497
+
498
+ ```text
499
+ Tunnel started
500
+ Local: http://127.0.0.1:8080
501
+ Public: https://go-relay-test.xyagent.com/t/p8080-03ce6ff5
502
+ Logs: ~/.agentlink/logs/tunnel-8080.log
503
+ Mode: systemd-user
504
+ ```
505
+
506
+ 说明:
507
+
508
+ - tunnel agent 会每 10 秒向 relay 发 heartbeat
509
+ - relay 超过约 35 秒没收到 heartbeat,会把 tunnel 视为 offline,并对公网请求直接返回 `503 tunnel offline`
510
+ - `agentlink tunnel on <port>` 会直接打印公网访问地址,示例里的 `Public: https://...` 就是可分享的访问 URL
511
+ - 如果不传 `--relay`,默认使用 CLI 当前内置或环境变量指定的 relay 地址
512
+ - 后台守护优先走当前平台的服务管理:
513
+ - Linux: `systemd --user`
514
+ - macOS: `launchd`
515
+ - Windows: `Task Scheduler`
516
+ - 如果当前环境没有可用的服务管理器,会自动回退到 detached 进程,并在 `agentlink tunnel on` 输出里提示
517
+
518
+ ### Tunnel 技术实现
519
+
520
+ 这套 tunnel 是标准的反向内网穿透,不要求目标机器有公网 IP,也不要求目标机器开放入站端口。
521
+
522
+ 核心思路:
523
+
524
+ - 外部用户访问的是 relay server 暴露出来的公网 URL,例如 `https://go-relay-test.xyagent.com/t/<slug>`
525
+ - 内网机器上的 `agentlink tunnel agent` 主动连接 relay,通过长轮询拉取待处理请求
526
+ - agent 在本机访问 `127.0.0.1:<port>`,再把响应回传给 relay
527
+ - relay 把响应返回给浏览器或调用方
528
+
529
+ 涉及的 3 个角色:
530
+
531
+ - `Client / Browser`
532
+ - 访问公网 tunnel 地址
533
+ - `Relay Server`
534
+ - 负责创建 tunnel、分配 `slug`、接收公网请求、转发给 agent、返回响应
535
+ - `Tunnel Agent`
536
+ - 运行在用户机器上,负责 heartbeat、拉取请求、访问本地服务、发布响应
537
+
538
+ 关键接口:
539
+
540
+ - `POST /v1/tunnels`
541
+ - 创建 tunnel,返回 `id`、`agentToken`、`publicUrl`
542
+ - `GET /v1/tunnels/:id/requests/next`
543
+ - agent 长轮询拉取待处理请求
544
+ - `POST /v1/tunnels/:id/heartbeat`
545
+ - agent 上报存活状态
546
+ - `POST /v1/tunnels/:id/responses/:reqId/publish`
547
+ - agent 发布本地服务响应
548
+ - `GET /t/:slug/*path`
549
+ - 公网入口,外部流量从这里进入 tunnel
550
+
551
+ 工作流程:
552
+
553
+ ```mermaid
554
+ sequenceDiagram
555
+ participant U as "User / Browser"
556
+ participant R as "Agentlink Relay Server"
557
+ participant A as "Agentlink Tunnel Agent"
558
+ participant L as "Local Service (127.0.0.1:8080)"
559
+
560
+ A->>R: "POST /v1/tunnels"
561
+ R-->>A: "id + agentToken + publicUrl"
562
+ A->>R: "POST /v1/tunnels/:id/heartbeat"
563
+ A->>R: "GET /v1/tunnels/:id/requests/next"
564
+
565
+ U->>R: "GET /t/:slug/api/echo?x=1"
566
+ R-->>A: "返回待处理 request"
567
+ A->>L: "GET http://127.0.0.1:8080/api/echo?x=1"
568
+ L-->>A: "200 + headers + body"
569
+ A->>R: "POST /v1/tunnels/:id/responses/:reqId/publish"
570
+ R-->>U: "200 + headers + body"
571
+ ```
572
+
573
+ 在线状态与恢复机制:
574
+
575
+ - agent 每 `10s` 发送一次 heartbeat
576
+ - server 超过约 `35s` 没收到 heartbeat,会把 tunnel 判定为 `offline`
577
+ - `offline` 状态下,公网请求会直接返回 `503 tunnel offline`
578
+ - 后台运行优先使用系统服务托管:
579
+ - Linux: `systemd --user`,root 环境额外支持 `systemd system service`
580
+ - macOS: `launchd`
581
+ - Windows: `Task Scheduler`
582
+ - 如果 agent 进程异常退出,服务管理器会自动重启;如果没有服务管理器,则退回 detached 进程模式
583
+
584
+ 实现边界:
585
+
586
+ - 当前 tunnel 第一版只支持 HTTP 请求/响应转发
587
+ - 支持 path、query、JSON/body、常见响应头透传
588
+ - 暂不包含 WebSocket、任意 TCP、UDP 转发
589
+ - 公网入口当前使用路径路由 `/t/<slug>`,没有使用独立子域名
590
+
591
+ 复杂路由跳转的适配说明:
592
+
593
+ - 大多数复杂 Web 系统都可以正常工作,包括:
594
+ - SPA 前端路由,例如 `react-router`、`vue-router`
595
+ - 后端页面路由,例如 `/admin/users/list`
596
+ - 带 query 的跳转,例如 `/orders?page=2&status=paid`
597
+ - 常见的 `301/302/307/308` 重定向
598
+ - 这类场景通常没问题,因为 tunnel 会保留:
599
+ - 请求 path
600
+ - 原始 query string
601
+ - 常规请求头与响应头
602
+ - HTTP 状态码
603
+ - `x-forwarded-host`
604
+ - 真正容易出问题的通常不是“路由复杂”,而是“应用把绝对地址写死”:
605
+ - 例如后端返回 `Location: http://127.0.0.1:8080/login`
606
+ - 或前端代码里写死 `http://内网IP:端口/...`
607
+ - 或登录回调地址、Cookie domain、绝对资源地址强依赖固定 host
608
+ - 推荐应用侧尽量使用相对路径,或者正确读取反向代理后的 host/proto 信息生成跳转地址
609
+ - 如果系统强依赖 WebSocket、HMR dev server、长连接 upgrade,当前版本需要单独验证,不能默认保证完全可用
610
+
611
+ 固定的 `curl` 用例:
612
+
613
+ ```bash
614
+ # 1) GET 根路径
615
+ curl -i https://go-relay-test.xyagent.com/t/p8080-03ce6ff5
616
+
617
+ # 2) GET 带 path + query
618
+ curl -i 'https://go-relay-test.xyagent.com/t/p8080-03ce6ff5/api/echo?name=agentlink&mode=query'
619
+
620
+ # 3) POST JSON 到带 path + query 的入口
621
+ curl -i \
622
+ -X POST \
623
+ 'https://go-relay-test.xyagent.com/t/p8080-03ce6ff5/api/echo?source=curl&lang=zh' \
624
+ -H 'content-type: application/json' \
625
+ -d '{"hello":"world","from":"agentlink"}'
626
+ ```
627
+
628
+ 看 agent 日志:
629
+
630
+ ```bash
631
+ tail -f ~/.agentlink/logs/tunnel-8080.log
632
+ ```
633
+
634
+ 关闭:
635
+
636
+ ```bash
637
+ agentlink tunnel off 8080
638
+ ```
639
+
640
+ `agentlink reset` 的行为:
641
+
642
+ | 命令 | 作用对象 |
643
+ | -------------------------------- | --------------------------------------------------- |
644
+ | `agentlink reset` | 重置 openclaw 凭证(并额外清空 hermes 凭证,兜底) |
645
+ | `agentlink reset --runtime hermes`| **只**清 hermes 凭证,openclaw 绑定不动 |
646
+
647
+ reset 做的事:
648
+
649
+ - 调 relay `DELETE /v1/gateways/{id}/binding` 清服务端绑定(配对码 / 会话 / 联系人)
650
+ - 卸载目标 runtime 的后台服务(launchd / systemd / Task Scheduler)
651
+ - 停掉目标 runtime 正在跑的桥接进程
652
+ - 清 `~/.agentlink/config.json` 本地凭证:
653
+ - openclaw → 重新生成 `relayGatewayId` + 轮换 token,并连带清空 hermes 凭证
654
+ - hermes → 只删 `hermesRelayGatewayId` / `hermesRelayClientToken` / `hermesRelayGatewayToken`
655
+
656
+ reset 完成后即可对同一 runtime 发起新的 `agentlink pair`。
657
+
658
+ code 接受 `1234`、`123456` 或 `XXXX-XXXX`(参考 `normalizeProvidedCode`)。
659
+
660
+ ---
661
+
662
+ ## 8. 服务管理
663
+
664
+ | 平台 | 机制 | 单元文件 |
665
+ | ------------------ | ----------------- | ----------------------------------------------------------- |
666
+ | macOS | launchd | `~/Library/LaunchAgents/com.agentlink.bridge[-hermes].plist` |
667
+ | Linux (systemd) | systemd user unit | `~/.config/systemd/user/agentlink-bridge[-hermes].service` |
668
+ | Linux (无 systemd) | — | 只能手动 `agentlink bridge`,service install 会报友好错误 |
669
+ | Windows | Task Scheduler | task name `Agentlink Bridge` |
670
+
671
+ OpenClaw 和 Hermes 两种 runtime 的服务名**不同**(参考 `serviceNames(runtimeKind)`),可以同时装一台机上跑两种:
672
+
673
+ ```bash
674
+ agentlink service status # 默认看 openclaw
675
+ agentlink service status --runtime hermes # 看 hermes 服务
676
+ agentlink service install # 装 openclaw bridge(默认)
677
+ agentlink service install --runtime hermes # 装 hermes bridge
678
+ agentlink service uninstall [--runtime hermes] # 停 + 删
679
+ agentlink service restart [--runtime hermes] # 重启
680
+ ```
681
+
682
+ 日志:
683
+
684
+ ```bash
685
+ # OpenClaw 桥接日志
686
+ tail -f ~/.agentlink/logs/bridge.stdout.log
687
+ tail -f ~/.agentlink/logs/bridge.stderr.log
688
+
689
+ # Hermes 桥接日志
690
+ tail -f ~/.agentlink/logs/bridge-hermes.stdout.log
691
+ tail -f ~/.agentlink/logs/bridge-hermes.stderr.log
692
+ ```
693
+
694
+ ### Claude / Codex(External Agents)
695
+
696
+ Claude / Codex 走的是独立的 `agentlink-agent` service 管理实现,命令入口仍然是顶层 `agentlink`:
697
+
698
+ | 平台 | 机制 | 单元文件 |
699
+ | ------------------ | ----------------- | ----------------------------------------------------------- |
700
+ | macOS | launchd | `~/Library/LaunchAgents/com.agentlink.agent.<runtime>.plist` |
701
+ | Linux (systemd) | systemd user unit | `~/.config/systemd/user/agentlink-agent-<runtime>.service` |
702
+ | Linux (无 systemd) | — | 只能手动 `agentlink bridge -r <runtime>` |
703
+ | Windows | Task Scheduler | `Agentlink\\Agent-<runtime>` |
704
+
705
+ 常用命令:
706
+
707
+ ```bash
708
+ # 配对后自动装后台服务
709
+ agentlink pair 1234 -r claude
710
+ agentlink pair 1234 -r codex
711
+
712
+ # 查看后台状态
713
+ agentlink service status -r claude
714
+ agentlink service status -r codex
715
+
716
+ # 重装后台服务
717
+ agentlink service restart -r claude
718
+ agentlink service restart -r codex
719
+
720
+ # 只停掉后台服务,但保留本地配对状态
721
+ agentlink service stop -r claude
722
+ agentlink service stop -r codex
723
+
724
+ # `service uninstall` 对 claude/codex 与 `service stop` 等价
725
+ agentlink service uninstall -r claude
726
+ agentlink service uninstall -r codex
727
+
728
+ # 完整卸载:停服务并清掉本地配对状态(~/.agentlink/ext-<runtime>-last.json)
729
+ agentlink uninstall -r claude
730
+ agentlink uninstall -r codex
731
+
732
+ # 前台调试运行;停止方式就是 Ctrl+C
733
+ agentlink bridge -r claude
734
+ agentlink bridge -r codex
735
+ ```
736
+
737
+ 说明:
738
+
739
+ - `agentlink pair <code> -r claude|codex` 成功后,会自动执行后台服务安装。
740
+ - 对 `claude/codex` 来说,`agentlink service stop -r <runtime>` 和 `agentlink service uninstall -r <runtime>` 在当前实现里效果相同,都会卸载对应 service,但不会删除本地配对状态。
741
+ - 如果你想彻底解绑本机上的 Claude / Codex,请使用 `agentlink uninstall -r <runtime>`,它会在停服务之外再清掉本地状态文件。
742
+
743
+ ---
744
+
745
+ ## 9. 状态与持久化
746
+
747
+ | 路径 | 内容 |
748
+ | -------------------------------------------- | ------------------------------------------------------------- |
749
+ | `~/.openclaw/openclaw.json` | OpenClaw gateway 配置(模型、端口、profile、relay 配置片段) |
750
+ | `~/.agentlink/config.json` | CLI 级别状态(`runtimeKind`、4 套 relay 凭证) |
751
+ | `~/.agentlink/hermes/sessions/<sid>.json` | Hermes JS bridge 的会话历史持久化 |
752
+ | `~/.agentlink/bridge-launch.sh` | OpenClaw 桥接的 launchd/systemd 启动脚本 |
753
+ | `~/.agentlink/bridge-launch-hermes.sh` | Hermes 桥接的 launchd/systemd 启动脚本 |
754
+ | `~/.agentlink/logs/` | 桥接 stdout / stderr 日志 |
755
+ | `~/.openclaw/agentlink/runtime/bridge-*.lock` | 桥接文件锁(按 `relayUrl + gatewayId + gatewayBase` 取 hash) |
756
+
757
+ `~/.agentlink/config.json` 里两套凭证并存:
758
+
759
+ - **OpenClaw**:`relayGatewayId` / `relayClientToken` / `relayGatewayToken`
760
+ - **Hermes**:`hermesRelayGatewayId` / `hermesRelayClientToken` / `hermesRelayGatewayToken`
761
+
762
+ `runtimeKind` 字段记录最近一次配对的 runtime。敏感字段(`gateway.auth.token` / `channels.*.appSecret`)在 debug 输出里会被脱敏成 `***`。
763
+
764
+ ---
765
+
766
+ ## 10. 环境变量
767
+
768
+ | 变量 | 作用 |
769
+ | ----------------------------------------------------------------------- | ---------------------------------------------------------------- |
770
+ | `OPENCLAW_HOME` | 覆盖 OpenClaw 主目录(默认 `~/.openclaw`) |
771
+ | `AGENTLINK_RELAY_URL` | 覆盖 relay 基址(默认 `https://go-relay-test.xyagent.com`) |
772
+ | `AGENTLINK_RELAY_TOKEN` | Bearer token(配对/绑定端点用) |
773
+ | `OPENCLAW_AGENTLINK_GATEWAY_BASE_URL` | 覆盖本地 gateway 基址 |
774
+ | `OPENCLAW_GATEWAY_PORT` / `OPENCLAW_GATEWAY_HOST` | 显式指定 host/port |
775
+ | `AGENTLINK_RELAY_GATEWAY_ID` / `_CLIENT_TOKEN` / `_GATEWAY_TOKEN` | 覆盖 openclaw 的 relay 凭证 |
776
+ | `AGENTLINK_HERMES_RELAY_GATEWAY_ID` / `_CLIENT_TOKEN` / `_GATEWAY_TOKEN` | 覆盖 hermes 的 relay 凭证 |
777
+ | **`HERMES_BRIDGE_API_BASE_URL`** | **Hermes JS bridge HTTP 后端:OpenAI-compatible base URL** |
778
+ | **`HERMES_BRIDGE_API_KEY`** | **Hermes HTTP 后端 Bearer token** |
779
+ | **`HERMES_BRIDGE_MODEL`** | **Hermes 默认模型名(`gpt-4o-mini` 等)** |
780
+ | **`HERMES_BRIDGE_COMMAND`** | **Hermes 子进程后端:外部 binary 路径(NDJSON 协议)** |
781
+
782
+ ---
783
+
784
+ ## 11. Hermes JS bridge 多 provider 与流式
785
+
786
+ Hermes JS bridge 通过 OpenAI-compatible HTTP 协议跑,任何兼容
787
+ `POST /v1/chat/completions { stream: true }` 的服务都能直接接:OpenAI、Azure
788
+ OpenAI、DeepSeek、自建 vLLM、LiteLLM、Ollama 兼容层。要切 provider 就改
789
+ `HERMES_BRIDGE_API_BASE_URL`。
790
+
791
+ 流式默认按 OpenAI SSE 帧解析(`data:` 前缀 + `[DONE]` 终止符),逐 chunk 透传
792
+ 为 unified `content_block.delta`,最终一帧合并为 `content_block.completed`。
793
+
794
+ 不需要外部多 provider 配置文件 — 后端切换通过 env 完成:
795
+
796
+ ```bash
797
+ # OpenAI 官方
798
+ export HERMES_BRIDGE_API_BASE_URL=https://api.openai.com/v1
799
+ export HERMES_BRIDGE_MODEL=gpt-4o-mini
800
+
801
+ # DeepSeek
802
+ export HERMES_BRIDGE_API_BASE_URL=https://api.deepseek.com/v1
803
+ export HERMES_BRIDGE_MODEL=deepseek-chat
804
+
805
+ # 本地 vLLM / Ollama 兼容层
806
+ export HERMES_BRIDGE_API_BASE_URL=http://127.0.0.1:8000/v1
807
+ export HERMES_BRIDGE_MODEL=qwen2.5
808
+ ```
809
+
810
+ 需要走非 OpenAI 协议(如 Anthropic 原生 `/messages`)?用 subprocess 后端:写
811
+ 一个外部 binary 实现 `stdin JSON → stdout NDJSON`,然后
812
+ `export HERMES_BRIDGE_COMMAND=/path/to/my-binary`。bridge 会 spawn 它跑每个
813
+ turn。
814
+
815
+ 模型选择用 `<provider>/<model-id>`(例 `minimax/MiniMax-M2.7`)。桥接先匹配配置的 provider,没匹配到才回退到默认 Hermes API server。
816
+
817
+ ---
818
+
819
+ ## 12. 已知陷阱
820
+
821
+ 1. **relay_client_token 可能在多设备共享** — 删除绑定前必须走 `canUseClientTokenDeleteFallback` 检查,避免误删别人的 binding
822
+ 2. **Session key 格式前后不一致** — App 侧装饰(`agent:xxx:relay-gw:GW-A::...`),relay 事件是裸 key。Flutter 用 `_sessionKeyMatchesForPreview` 宽松匹配
823
+ 3. **Windows 的 PowerShell 编码** — 服务注册时通过 Base64 传命令行,避免 CJK 路径在 cmd.exe 下被 mojibake
824
+ 4. **Hermes / OpenClaw 凭证是两套** — `AppRuntimeKind` 决定写哪一组,不能互相借用;`agentlink reset` 不传 `--runtime` 默认只重置 openclaw
825
+ 5. **Hermes cron 任务与配对无关** — `~/.hermes/cron/jobs.json` 是本地文件。一次性任务(`30m` / `2h` / 具体时间戳)执行后会**自动删除**,重新配对看不到是正常现象
826
+
827
+ ---
828
+
829
+ ## 13. 测试
830
+
831
+ 需要 Node.js ≥ 22。
832
+
833
+ ```bash
834
+ npm test
835
+ node --test test/cli.test.mjs
836
+ ```
837
+
838
+ 73 个测试(13 个测试文件),覆盖:
839
+
840
+ - CLI basics / 平台路径 / OpenClaw home 解析 / XML & systemd 转义 / PID 检测 / 文件锁 / PowerShell Base64 / 服务状态 / Node 版本保护
841
+ - `pairCodeClient` —— `/v1/pair-codes/<code>/consume` 协议契约 + legacy 端点回退
842
+ - `relayWorker` —— long-poll 拉取 + publishEvent + 心跳 + 退避重试
843
+ - `claudeParser` / `codexEventMapper` —— native 事件解析(CLI 端只解析不翻译)
844
+ - `approvalGateway` / `elicitationHandler` —— HITL 工具审批
845
+ - `mcpStdioClient` / `mcpStdioBridge` / `agentlinkTools` —— Codex 反向 MCP 工具注入
846
+ - `usageReporter` —— token 用量上报
847
+
848
+ > 之前 161 测试中有近一半是 `unified_relay_shim` 的 native → unified envelope 翻译契约测试。重构把翻译层挪到服务端 adapter 后,CLI 端只剩 73 测试覆盖 long-poll + pair-code consume + native 事件解析这些核心路径。
849
+
850
+ CI:
851
+
852
+ - GitLab CI (`.gitlab-ci.yml`):Node 22 on Alpine
853
+ - GitHub Actions (`.github/workflows/test.yml`):Ubuntu / macOS / Windows × Node 22
854
+
855
+ | 平台 | 状态 |
856
+ | ----------------------------------- | ---------------------------- |
857
+ | macOS (launchd) | 完全支持 |
858
+ | Linux + systemd | 完全支持 |
859
+ | Linux 无 systemd(Alpine / OpenRC) | 只支持手动 `agentlink bridge` |
860
+ | Windows (Task Scheduler) | 支持,CI 覆盖 |