@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,129 @@
1
+ // PicoClaw daemon WebSocket 客户端。
2
+ //
3
+ // PicoClaw daemon 默认监听 127.0.0.1:18790,WS 路径 /pico/ws。
4
+ // 鉴权 3 种方式(picoclaw 自身支持):
5
+ // 1. Authorization: Bearer <token> (服务端 / Node `ws` 包可用)
6
+ // 2. Sec-WebSocket-Protocol "token.<value>" (浏览器 / Node 22 内置 WebSocket 用这条)
7
+ // 3. query 参数 ?token=<value>(picoclaw 必须开启 AllowTokenQuery)
8
+ //
9
+ // 这里走 #2 子协议 — Node 22 内置 WebSocket 不支持自定义 header,但支持
10
+ // 第二参数 protocols。`@xyagent/cli` 之前没有任何 npm 依赖,我们保持零依赖,
11
+ // 直接用 globalThis.WebSocket(Node ≥21 内置)。
12
+ //
13
+ // 通讯使用 PicoMessage:
14
+ // { type, id, session_id, timestamp, payload }
15
+ //
16
+ // CLI 端只需:
17
+ // send(msg) —— 发消息(JSON 序列化)
18
+ // onMessage(fn) —— 收消息回调
19
+ // onClose(fn) —— 断开回调(自动重连由调用方决定)
20
+ // close() —— 主动关闭
21
+
22
+ import {
23
+ PICOCLAW_TOKEN,
24
+ PICOCLAW_WS_URL,
25
+ } from "./constants.mjs";
26
+
27
+ /**
28
+ * 建一个 PicoClaw 连接。
29
+ * @returns {Promise<PicoclawConn>}
30
+ */
31
+ export function connect({ url, token, log }) {
32
+ const target = url || PICOCLAW_WS_URL;
33
+ const bearer = token || PICOCLAW_TOKEN;
34
+ return new Promise((resolve, reject) => {
35
+ const subprotocol = `token.${bearer}`;
36
+ const WS = globalThis.WebSocket;
37
+ if (!WS) {
38
+ reject(new Error("global WebSocket not available; require Node >= 21"));
39
+ return;
40
+ }
41
+ const ws = new WS(target, [subprotocol]);
42
+ let settled = false;
43
+ ws.addEventListener("open", () => {
44
+ if (settled) return;
45
+ settled = true;
46
+ resolve(makeConn(ws, log, target));
47
+ });
48
+ ws.addEventListener("error", (ev) => {
49
+ if (settled) return;
50
+ settled = true;
51
+ const msg = (ev && ev.message) || "ws error";
52
+ reject(new Error(String(msg)));
53
+ });
54
+ ws.addEventListener("close", (ev) => {
55
+ if (settled) return;
56
+ settled = true;
57
+ reject(new Error(`picoclaw ws closed before open: code=${ev?.code}`));
58
+ });
59
+ });
60
+ }
61
+
62
+ function makeConn(ws, log, target) {
63
+ /** @type {Set<(msg:any)=>void>} */
64
+ const messageHandlers = new Set();
65
+ /** @type {Set<(reason:string)=>void>} */
66
+ const closeHandlers = new Set();
67
+
68
+ ws.addEventListener("message", (ev) => {
69
+ let parsed;
70
+ try {
71
+ const data = typeof ev.data === "string" ? ev.data : Buffer.from(ev.data).toString("utf8");
72
+ parsed = JSON.parse(data);
73
+ } catch (err) {
74
+ if (log) log.warn("picoclaw.ws.parse.err", "bad pico message", { err: String(err?.message || err) });
75
+ return;
76
+ }
77
+ for (const h of messageHandlers) {
78
+ try { h(parsed); } catch (err) {
79
+ if (log) log.warn("picoclaw.ws.handler.err", "handler threw", { err: String(err?.message || err) });
80
+ }
81
+ }
82
+ });
83
+ ws.addEventListener("close", (ev) => {
84
+ if (log) log.info("picoclaw.ws.closed", "ws closed", { code: ev?.code, reason: String(ev?.reason || "") });
85
+ for (const h of closeHandlers) {
86
+ try { h(`code=${ev?.code}`); } catch {}
87
+ }
88
+ });
89
+ ws.addEventListener("error", (ev) => {
90
+ if (log) log.warn("picoclaw.ws.err", "ws error", { err: String(ev?.message || "ws error") });
91
+ });
92
+
93
+ if (log) log.info("picoclaw.ws.open", "connected", { url: target });
94
+
95
+ return {
96
+ send(picoMessage) {
97
+ if (ws.readyState !== 1 /* OPEN */) {
98
+ throw new Error(`picoclaw ws not open: state=${ws.readyState}`);
99
+ }
100
+ ws.send(JSON.stringify(picoMessage));
101
+ },
102
+ onMessage(fn) { messageHandlers.add(fn); return () => messageHandlers.delete(fn); },
103
+ onClose(fn) { closeHandlers.add(fn); return () => closeHandlers.delete(fn); },
104
+ close(reason) {
105
+ try { ws.close(1000, reason || "client close"); } catch {}
106
+ },
107
+ get readyState() { return ws.readyState; },
108
+ get url() { return target; },
109
+ };
110
+ }
111
+
112
+ /**
113
+ * 构造 message.send 帧(用户消息进入 PicoClaw)。
114
+ *
115
+ * @param {{sessionId:string, content:string, requestId?:string}} arg
116
+ */
117
+ export function buildMessageSend({ sessionId, content, requestId }) {
118
+ return {
119
+ type: "message.send",
120
+ id: requestId || randomID(),
121
+ session_id: sessionId,
122
+ timestamp: Date.now(),
123
+ payload: { content: String(content || "") },
124
+ };
125
+ }
126
+
127
+ function randomID() {
128
+ return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
129
+ }
@@ -0,0 +1,521 @@
1
+ // 跨平台 service 管理骨架。
2
+ //
3
+ // 目标:用户关终端 / 机器休眠后 bridge 仍自动拉起并保持和 relay 连接。
4
+ //
5
+ // 平台映射(详见 docs/PLATFORM.md):
6
+ // - macOS → launchd `~/Library/LaunchAgents/com.agentlink.agent.<runtime>.plist`
7
+ // - Linux → systemd user unit `~/.config/systemd/user/agentlink-agent-<runtime>.service`
8
+ // - Linux (no systemd) → 打印提示,建议 tmux/nohup
9
+ // - Windows → schtasks `Agentlink\Agent-<runtime>`(ONLOGON + 10s 重启延迟)
10
+ //
11
+ // service 里实际执行:`agentlink-agent <runtime>`(零参数,读本地持久化状态)。
12
+ // 所以调用前必须先跑过 `agentlink pair <code> -r <runtime>` —— ensureState 会校验。
13
+
14
+ import fs from "node:fs";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import process from "node:process";
18
+ import { spawnSync } from "node:child_process";
19
+ import { readPairState } from "../core/pairCodeClient.mjs";
20
+ import { ensureDefaultWorkspace, defaultWorkspacePath } from "../core/defaultWorkspace.mjs";
21
+
22
+ const SERVICE_RUNTIMES = new Set(["claude", "codex"]);
23
+
24
+ /**
25
+ * reload 用的 runtime 清单 —— 比 install 集合更广:也覆盖 openclaw/hermes
26
+ * 的后台 bridge service(这两者的 install 在 bin/agentlink 里维护,label 固定)。
27
+ * reload 只做"重启已存在"的动作,不创建/不删除,所以即使 openclaw/hermes 未装也
28
+ * 只是 skip。
29
+ *
30
+ * 三端标签必须和各 runtime 实际 install 时写入的一致:
31
+ * - claude/codex : 见下方 macInstall / sdInstall / winInstall
32
+ * - openclaw/hermes: 见 bin/agentlink 的 serviceNames() —— label/unit/task 规则
33
+ */
34
+ const ALL_RELOAD_TARGETS = {
35
+ claude: {
36
+ mac: "com.agentlink.agent.claude",
37
+ linux: "agentlink-agent-claude.service",
38
+ win: "Agentlink\\Agent-claude",
39
+ },
40
+ codex: {
41
+ mac: "com.agentlink.agent.codex",
42
+ linux: "agentlink-agent-codex.service",
43
+ win: "Agentlink\\Agent-codex",
44
+ },
45
+ openclaw: {
46
+ mac: "com.agentlink.bridge",
47
+ linux: "agentlink-bridge.service",
48
+ win: "Agentlink Bridge",
49
+ },
50
+ hermes: {
51
+ mac: "com.agentlink.bridge-hermes",
52
+ linux: "agentlink-bridge-hermes.service",
53
+ win: "Agentlink Bridge Hermes",
54
+ },
55
+ };
56
+
57
+ function ensureRuntime(runtime) {
58
+ if (!SERVICE_RUNTIMES.has(runtime)) {
59
+ throw new Error(`service runtime must be one of: ${[...SERVICE_RUNTIMES].join(", ")}`);
60
+ }
61
+ }
62
+
63
+ function ensureReloadRuntime(runtime) {
64
+ if (!ALL_RELOAD_TARGETS[runtime]) {
65
+ throw new Error(`reload runtime must be one of: ${Object.keys(ALL_RELOAD_TARGETS).join(", ")}`);
66
+ }
67
+ }
68
+
69
+ function ensureStateOrFail(runtime) {
70
+ const s = readPairState(runtime);
71
+ if (!s) {
72
+ throw new Error(
73
+ `尚未配对 —— 请先在 App 上创建会话,然后运行:\n` +
74
+ ` agentlink pair <code> -r ${runtime} --relay <url>\n` +
75
+ `配对成功后再 service install。`,
76
+ );
77
+ }
78
+ return s;
79
+ }
80
+
81
+ function agentBinPath() {
82
+ // 取和当前 agentlink 可执行同目录的 agentlink-agent
83
+ const here = path.dirname(new URL(import.meta.url).pathname);
84
+ const guess = path.resolve(here, "../../bin/agentlink-agent");
85
+ if (fs.existsSync(guess)) return guess;
86
+ // 尝试 PATH
87
+ const which = spawnSync(process.platform === "win32" ? "where" : "which", ["agentlink-agent"], {
88
+ encoding: "utf8",
89
+ });
90
+ if (which.status === 0) {
91
+ const first = which.stdout.trim().split(/\r?\n/)[0];
92
+ if (first) return first;
93
+ }
94
+ throw new Error("could not locate agentlink-agent binary");
95
+ }
96
+
97
+ // ===== macOS launchd =====
98
+ function macLabel(runtime) { return `com.agentlink.agent.${runtime}`; }
99
+ function macPlistPath(runtime) {
100
+ return path.join(os.homedir(), "Library", "LaunchAgents", `${macLabel(runtime)}.plist`);
101
+ }
102
+
103
+ function macUid() {
104
+ return typeof process.getuid === "function" ? process.getuid() : 0;
105
+ }
106
+
107
+ /**
108
+ * 现代 launchd 注册序列(Big Sur+ 可靠)。
109
+ *
110
+ * 老路径用 `launchctl load -w` 依赖 plist 的 `RunAtLoad` 拉起进程。在 Big Sur+
111
+ * 上 legacy `load`/`unload` 已废弃,且 `load -w` 对**已加载**的 job 是 no-op——
112
+ * 不会重新触发 `RunAtLoad`,job 停在 "loaded but runs=0" 永远不真正执行。
113
+ * `agentlink pair <code> -r claude/codex` 装完 sleep 3s 查状态因此偶发报
114
+ * "进程未运行起来",反复 pair / 残留旧 plist 更易触发。
115
+ *
116
+ * 健壮序列(与 bin/agentlink 的 installBridgeServiceMac 对齐):
117
+ * bootout —— 现代 unload,先拆掉任何同 label 的旧实例(幂等,未加载也无妨)
118
+ * bootstrap —— 把 job 加载进 GUI domain
119
+ * enable —— 清掉上一次 unload -w / disable 留下的 stale "disabled" override
120
+ * kickstart -k —— 强制真正运行(已在跑则重启),不再靠 RunAtLoad 的运气
121
+ *
122
+ * 纯函数:返回有序的 { cmd, args } 列表,便于单测;实际执行在 macInstall。
123
+ */
124
+ export function buildMacInstallSteps({ uid, label, plistPath }) {
125
+ const domain = `gui/${uid}`;
126
+ const service = `gui/${uid}/${label}`;
127
+ return [
128
+ { cmd: "launchctl", args: ["bootout", domain, plistPath] },
129
+ { cmd: "launchctl", args: ["bootstrap", domain, plistPath] },
130
+ { cmd: "launchctl", args: ["enable", service] },
131
+ { cmd: "launchctl", args: ["kickstart", "-k", service] },
132
+ ];
133
+ }
134
+
135
+ /**
136
+ * 解析 `launchctl print gui/<uid>/<label>` 的关键字段。纯函数,便于单测。
137
+ * 只有 print 能暴露 `runs`,识破 pid 文件看不出的 "loaded but runs=0" 陷阱。
138
+ */
139
+ export function parseLaunchctlPrint(out) {
140
+ const h = { state: null, pid: null, runs: null, lastExitCode: null };
141
+ for (const line of String(out || "").split(/\r?\n/)) {
142
+ const t = line.trim();
143
+ let m;
144
+ if ((m = t.match(/^state\s*=\s*(.+)$/))) h.state = m[1].trim();
145
+ else if ((m = t.match(/^pid\s*=\s*(\d+)$/))) h.pid = Number(m[1]);
146
+ else if ((m = t.match(/^runs\s*=\s*(\d+)$/))) h.runs = Number(m[1]);
147
+ else if ((m = t.match(/^last exit code\s*=\s*(\d+)$/))) h.lastExitCode = Number(m[1]);
148
+ }
149
+ h.running = (h.state || "").toLowerCase() === "running" || (h.pid != null && h.pid > 0);
150
+ return h;
151
+ }
152
+
153
+ async function macInstall(runtime, log) {
154
+ const bin = agentBinPath();
155
+ const node = process.execPath;
156
+ const state = ensureStateOrFail(runtime);
157
+ const label = macLabel(runtime);
158
+ const plistPath = macPlistPath(runtime);
159
+ const logDir = path.join(os.homedir(), ".agentlink", "logs");
160
+ fs.mkdirSync(logDir, { recursive: true });
161
+
162
+ // align-runtime-default-workspace: 仅 claude/codex 注入 WorkingDirectory
163
+ // install 前确保目录存在(否则 launchd 因 WorkingDirectory 不存在拒绝启动)
164
+ let workingDirectory = null;
165
+ if (runtime === "claude" || runtime === "codex") {
166
+ workingDirectory = await ensureDefaultWorkspace(runtime);
167
+ }
168
+ const stdoutLog = path.join(logDir, `service-${runtime}.out.log`);
169
+ const stderrLog = path.join(logDir, `service-${runtime}.err.log`);
170
+
171
+ // 关键:launchd 启动的进程默认 PATH 很短,收不到终端 rc 里的 PATH。
172
+ // agentlink-agent 之后要 spawn `claude` / `codex`,若 PATH 没它们所在目录 → ENOENT → 崩溃。
173
+ // 策略:取用户当前 shell 里的 PATH(安装命令执行时的环境),再并入常见 brew/npm 路径。
174
+ const pathParts = new Set();
175
+ for (const p of (process.env.PATH || "").split(":")) {
176
+ if (p) pathParts.add(p);
177
+ }
178
+ for (const p of [
179
+ "/opt/homebrew/bin", "/opt/homebrew/sbin",
180
+ "/usr/local/bin", "/usr/local/sbin",
181
+ path.join(os.homedir(), ".npm-global/bin"),
182
+ path.join(os.homedir(), ".nvm/versions/node"),
183
+ "/usr/bin", "/bin", "/usr/sbin", "/sbin",
184
+ ]) {
185
+ pathParts.add(p);
186
+ }
187
+ const mergedPath = [...pathParts].filter(Boolean).join(":");
188
+
189
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
190
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTD/PropertyList-1.0.dtd">
191
+ <plist version="1.0">
192
+ <dict>
193
+ <key>Label</key><string>${label}</string>
194
+ <key>ProgramArguments</key>
195
+ <array>
196
+ <string>${node}</string>
197
+ <string>${bin}</string>
198
+ <string>${runtime}</string>
199
+ </array>
200
+ <key>RunAtLoad</key><true/>
201
+ <key>KeepAlive</key><true/>
202
+ <key>ThrottleInterval</key><integer>10</integer>
203
+ <key>EnvironmentVariables</key>
204
+ <dict>
205
+ <key>AGENTLINK_RELAY_URL</key><string>${escapeXml(state.relay_url)}</string>
206
+ <key>AGENTLINK_AGENT_LOG_LEVEL</key><string>info</string>
207
+ <key>PATH</key><string>${escapeXml(mergedPath)}</string>
208
+ <key>HOME</key><string>${escapeXml(os.homedir())}</string>
209
+ </dict>${workingDirectory ? `
210
+ <key>WorkingDirectory</key><string>${escapeXml(workingDirectory)}</string>` : ""}
211
+ <key>StandardOutPath</key><string>${stdoutLog}</string>
212
+ <key>StandardErrorPath</key><string>${stderrLog}</string>
213
+ </dict>
214
+ </plist>
215
+ `;
216
+ fs.mkdirSync(path.dirname(plistPath), { recursive: true });
217
+ fs.writeFileSync(plistPath, plist, "utf8");
218
+ log?.info("service.install", "plist written", { path: plistPath, label });
219
+
220
+ // 现代序列 bootout→bootstrap→enable→kickstart(见 buildMacInstallSteps 注释)。
221
+ const uid = macUid();
222
+ const steps = buildMacInstallSteps({ uid, label, plistPath });
223
+ let bootstrapOk = false;
224
+ let kickstart = null;
225
+ for (const step of steps) {
226
+ const verb = step.args[0];
227
+ const r = spawnSync(step.cmd, step.args, { encoding: "utf8" });
228
+ // bootout 对未加载的 job 必然失败——幂等忽略。
229
+ if (verb === "bootout") continue;
230
+ if (verb === "bootstrap") {
231
+ bootstrapOk = r.status === 0;
232
+ // bootstrap 失败常见于 job 已加载(EALREADY/5);enable+kickstart 仍可救回,
233
+ // 故不在此抛错,留到最后看 kickstart 结果。
234
+ continue;
235
+ }
236
+ if (verb === "enable") continue; // 清 disabled override,失败无妨
237
+ if (verb === "kickstart") kickstart = r;
238
+ }
239
+
240
+ if (!kickstart || kickstart.status !== 0) {
241
+ // 现代序列没能拉起——回退 legacy load -w 兜底(与 bin/agentlink 一致)。
242
+ log?.warn("service.install", "modern launchctl sequence failed, falling back to load -w", {
243
+ bootstrap_ok: bootstrapOk,
244
+ kickstart_err: (kickstart?.stderr || kickstart?.stdout || "").trim(),
245
+ });
246
+ const fallback = spawnSync("launchctl", ["load", "-w", plistPath], { encoding: "utf8" });
247
+ if (fallback.status !== 0) {
248
+ const msg = (kickstart?.stderr || kickstart?.stdout || fallback.stderr || fallback.stdout || "").trim();
249
+ throw new Error(`launchctl bootstrap/kickstart failed: ${msg || "unknown"}`);
250
+ }
251
+ }
252
+ return { backend: "launchd", path: plistPath, label, logs: { out: stdoutLog, err: stderrLog } };
253
+ }
254
+
255
+ function macUninstall(runtime) {
256
+ const plistPath = macPlistPath(runtime);
257
+ const uid = macUid();
258
+ // bootout 是现代 unload;gui 与 user domain 都试一遍兜旧装法。
259
+ spawnSync("launchctl", ["bootout", `gui/${uid}`, plistPath], { encoding: "utf8" });
260
+ spawnSync("launchctl", ["bootout", `user/${uid}`, plistPath], { encoding: "utf8" });
261
+ // 兼容历史用 load -w 装的实例。
262
+ spawnSync("launchctl", ["unload", plistPath], { encoding: "utf8" });
263
+ try { fs.unlinkSync(plistPath); } catch {}
264
+ return { backend: "launchd", path: plistPath };
265
+ }
266
+
267
+ function macStatus(runtime) {
268
+ const label = macLabel(runtime);
269
+ const uid = macUid();
270
+ // launchctl print 能暴露 runs/state,识破 "loaded but runs=0" 陷阱。
271
+ const out = spawnSync("launchctl", ["print", `gui/${uid}/${label}`], { encoding: "utf8" });
272
+ if (out.status !== 0) {
273
+ // 未加载(job 不存在)→ print 返回非 0。
274
+ return { backend: "launchd", running: false };
275
+ }
276
+ const h = parseLaunchctlPrint(out.stdout);
277
+ return {
278
+ backend: "launchd",
279
+ running: h.running,
280
+ pid: h.pid,
281
+ runs: h.runs,
282
+ state: h.state,
283
+ last_exit: h.lastExitCode,
284
+ };
285
+ }
286
+
287
+ // ===== Linux systemd (user) =====
288
+ function sdUnitName(runtime) { return `agentlink-agent-${runtime}.service`; }
289
+ function sdUnitPath(runtime) {
290
+ return path.join(os.homedir(), ".config", "systemd", "user", sdUnitName(runtime));
291
+ }
292
+
293
+ function systemdAvailable() {
294
+ return spawnSync("systemctl", ["--user", "status"], { encoding: "utf8" }).status !== 127;
295
+ }
296
+
297
+ async function sdInstall(runtime, log) {
298
+ if (!systemdAvailable()) {
299
+ throw new Error(
300
+ "systemd user unit 不可用(systemctl 未安装)。" +
301
+ "请用 tmux/nohup 保活,或等未来支持 cron/init.d。",
302
+ );
303
+ }
304
+ const bin = agentBinPath();
305
+ const node = process.execPath;
306
+ const state = ensureStateOrFail(runtime);
307
+ const unitPath = sdUnitPath(runtime);
308
+ const logDir = path.join(os.homedir(), ".agentlink", "logs");
309
+ fs.mkdirSync(logDir, { recursive: true });
310
+
311
+ // align-runtime-default-workspace: 仅 claude/codex 注入 WorkingDirectory
312
+ // install 前确保目录存在(否则 systemd 因 WorkingDirectory 不存在拒绝启动)
313
+ let workingDirectoryLine = "";
314
+ if (runtime === "claude" || runtime === "codex") {
315
+ const wsPath = await ensureDefaultWorkspace(runtime);
316
+ workingDirectoryLine = `WorkingDirectory=${wsPath}`;
317
+ }
318
+
319
+ // 同 macOS:systemd user unit 也要扩 PATH,确保能找到 claude / codex
320
+ const pathEnv = (process.env.PATH || "/usr/local/bin:/usr/bin:/bin") +
321
+ ":/usr/local/bin:/opt/homebrew/bin:" +
322
+ path.join(os.homedir(), ".npm-global/bin");
323
+ const unit = `[Unit]
324
+ Description=Agentlink Agent (${runtime})
325
+ After=network-online.target
326
+
327
+ [Service]
328
+ Type=simple
329
+ Environment=AGENTLINK_RELAY_URL=${state.relay_url}
330
+ Environment=AGENTLINK_AGENT_LOG_LEVEL=info
331
+ Environment=PATH=${pathEnv}
332
+ Environment=HOME=${os.homedir()}${workingDirectoryLine ? `\n${workingDirectoryLine}` : ""}
333
+ ExecStart=${node} ${bin} ${runtime}
334
+ Restart=on-failure
335
+ RestartSec=10s
336
+ StandardOutput=append:${logDir}/service-${runtime}.out.log
337
+ StandardError=append:${logDir}/service-${runtime}.err.log
338
+
339
+ [Install]
340
+ WantedBy=default.target
341
+ `;
342
+ fs.mkdirSync(path.dirname(unitPath), { recursive: true });
343
+ fs.writeFileSync(unitPath, unit, "utf8");
344
+ log?.info("service.install", "unit written", { path: unitPath });
345
+
346
+ const daemon = spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
347
+ if (daemon.status !== 0) throw new Error(`daemon-reload failed: ${daemon.stderr}`);
348
+ const enable = spawnSync("systemctl", ["--user", "enable", "--now", sdUnitName(runtime)], { encoding: "utf8" });
349
+ if (enable.status !== 0) throw new Error(`enable --now failed: ${enable.stderr}`);
350
+
351
+ return { backend: "systemd-user", path: unitPath, logs: { dir: logDir } };
352
+ }
353
+
354
+ function sdUninstall(runtime) {
355
+ spawnSync("systemctl", ["--user", "disable", "--now", sdUnitName(runtime)], { encoding: "utf8" });
356
+ try { fs.unlinkSync(sdUnitPath(runtime)); } catch {}
357
+ spawnSync("systemctl", ["--user", "daemon-reload"], { encoding: "utf8" });
358
+ return { backend: "systemd-user", path: sdUnitPath(runtime) };
359
+ }
360
+
361
+ function sdStatus(runtime) {
362
+ const r = spawnSync("systemctl", ["--user", "is-active", sdUnitName(runtime)], { encoding: "utf8" });
363
+ const running = (r.stdout || "").trim() === "active";
364
+ return { backend: "systemd-user", running };
365
+ }
366
+
367
+ // ===== Windows schtasks =====
368
+ function winTaskName(runtime) { return `Agentlink\\Agent-${runtime}`; }
369
+
370
+ function winInstall(runtime, log) {
371
+ const bin = agentBinPath();
372
+ const node = process.execPath;
373
+ const state = ensureStateOrFail(runtime);
374
+ // ONLOGON + failure restart. 简化起见不加 env —— Windows schtasks 设置 env 要走注册表,麻烦。
375
+ // 把 relay url 作为 CLI 参数传入:
376
+ const args = `"${bin}" ${runtime} --relay ${state.relay_url}`;
377
+ const tr = `"${node}" ${args}`;
378
+ const create = spawnSync("schtasks", [
379
+ "/Create", "/F",
380
+ "/TN", winTaskName(runtime),
381
+ "/TR", tr,
382
+ "/SC", "ONLOGON",
383
+ "/RL", "LIMITED",
384
+ ], { encoding: "utf8" });
385
+ if (create.status !== 0) {
386
+ throw new Error(`schtasks /Create failed: ${create.stderr || create.stdout}`);
387
+ }
388
+ log?.info("service.install", "scheduled task created", { task: winTaskName(runtime) });
389
+ return { backend: "schtasks", task: winTaskName(runtime) };
390
+ }
391
+
392
+ function winUninstall(runtime) {
393
+ const r = spawnSync("schtasks", ["/Delete", "/F", "/TN", winTaskName(runtime)], { encoding: "utf8" });
394
+ return { backend: "schtasks", task: winTaskName(runtime), deleted: r.status === 0 };
395
+ }
396
+
397
+ function winStatus(runtime) {
398
+ const r = spawnSync("schtasks", ["/Query", "/TN", winTaskName(runtime), "/FO", "LIST"], { encoding: "utf8" });
399
+ if (r.status !== 0) return { backend: "schtasks", running: false };
400
+ const m = (r.stdout || "").match(/Status:\s*(\w+)/i);
401
+ return { backend: "schtasks", running: m ? m[1].toLowerCase() === "running" : false, raw: r.stdout.trim() };
402
+ }
403
+
404
+ // ===== 分发 =====
405
+ export async function serviceInstall(runtime, log) {
406
+ ensureRuntime(runtime);
407
+ if (process.platform === "darwin") return macInstall(runtime, log);
408
+ if (process.platform === "linux") return sdInstall(runtime, log);
409
+ if (process.platform === "win32") return winInstall(runtime, log);
410
+ throw new Error(`unsupported platform: ${process.platform}`);
411
+ }
412
+
413
+ export function serviceUninstall(runtime, log) {
414
+ ensureRuntime(runtime);
415
+ if (process.platform === "darwin") return macUninstall(runtime);
416
+ if (process.platform === "linux") return sdUninstall(runtime);
417
+ if (process.platform === "win32") return winUninstall(runtime);
418
+ throw new Error(`unsupported platform: ${process.platform}`);
419
+ }
420
+
421
+ export function serviceStatus(runtime) {
422
+ ensureRuntime(runtime);
423
+ if (process.platform === "darwin") return macStatus(runtime);
424
+ if (process.platform === "linux") return sdStatus(runtime);
425
+ if (process.platform === "win32") return winStatus(runtime);
426
+ throw new Error(`unsupported platform: ${process.platform}`);
427
+ }
428
+
429
+ // ── Reload (restart existing service without re-installing) ─────────────────
430
+ //
431
+ // 语义:只针对"已安装的" service 做进程重启。用于开发者更新代码后希望让
432
+ // 长寿 agent 进程重新加载模块。找不到已安装 service 的返回 "not-installed",
433
+ // 不视为错误。
434
+ //
435
+ // 三端实现:
436
+ // - macOS: launchctl kickstart -k gui/$UID/<label> (先探测 `launchctl print`)
437
+ // - Linux: systemctl --user restart <unit> (先探测 `list-unit-files`)
438
+ // - Windows: schtasks /End + /Run <task> (先探测 `schtasks /Query`)
439
+
440
+ export function serviceReload(runtime) {
441
+ ensureReloadRuntime(runtime);
442
+ if (process.platform === "darwin") return macReload(runtime);
443
+ if (process.platform === "linux") return sdReload(runtime);
444
+ if (process.platform === "win32") return winReload(runtime);
445
+ throw new Error(`unsupported platform: ${process.platform}`);
446
+ }
447
+
448
+ /**
449
+ * 重启所有已知 runtime。返回 { [runtime]: result },每项有 status 字段
450
+ * ("restarted" / "not-installed" / "error")。
451
+ */
452
+ export function serviceReloadAll() {
453
+ const out = {};
454
+ for (const rt of Object.keys(ALL_RELOAD_TARGETS)) {
455
+ try {
456
+ out[rt] = serviceReload(rt);
457
+ } catch (err) {
458
+ out[rt] = { status: "error", error: err?.message || String(err) };
459
+ }
460
+ }
461
+ return out;
462
+ }
463
+
464
+ // macOS reload
465
+ function macReload(runtime) {
466
+ const label = ALL_RELOAD_TARGETS[runtime].mac;
467
+ const uid = typeof process.getuid === "function" ? process.getuid() : 0;
468
+ const ref = `gui/${uid}/${label}`;
469
+ const present = spawnSync("launchctl", ["print", ref], { encoding: "utf8" });
470
+ if (present.status !== 0) {
471
+ return { status: "not-installed", backend: "launchd", label };
472
+ }
473
+ const r = spawnSync("launchctl", ["kickstart", "-k", ref], { encoding: "utf8" });
474
+ if (r.status !== 0) {
475
+ const msg = (r.stderr || r.stdout || "").trim();
476
+ throw new Error(`launchctl kickstart failed: ${msg || "exit " + r.status}`);
477
+ }
478
+ return { status: "restarted", backend: "launchd", label, ref };
479
+ }
480
+
481
+ // Linux systemd user reload
482
+ function sdReload(runtime) {
483
+ const unit = ALL_RELOAD_TARGETS[runtime].linux;
484
+ // list-unit-files 可同时检测 masked/enabled/disabled;exit 0 表示 unit 存在。
485
+ // 另一种探测:`systemctl --user cat <unit>` —— 不存在会返回非 0。
486
+ const probe = spawnSync("systemctl", ["--user", "cat", unit], { encoding: "utf8" });
487
+ if (probe.status !== 0) {
488
+ return { status: "not-installed", backend: "systemd-user", unit };
489
+ }
490
+ const r = spawnSync("systemctl", ["--user", "restart", unit], { encoding: "utf8" });
491
+ if (r.status !== 0) {
492
+ const msg = (r.stderr || r.stdout || "").trim();
493
+ throw new Error(`systemctl --user restart failed: ${msg || "exit " + r.status}`);
494
+ }
495
+ return { status: "restarted", backend: "systemd-user", unit };
496
+ }
497
+
498
+ // Windows schtasks reload
499
+ function winReload(runtime) {
500
+ const task = ALL_RELOAD_TARGETS[runtime].win;
501
+ const probe = spawnSync("schtasks", ["/Query", "/TN", task, "/FO", "LIST"], { encoding: "utf8" });
502
+ if (probe.status !== 0) {
503
+ return { status: "not-installed", backend: "schtasks", task };
504
+ }
505
+ // /End 若任务没在跑会返回非 0,忽略;/Run 真正触发启动
506
+ spawnSync("schtasks", ["/End", "/TN", task], { encoding: "utf8" });
507
+ const r = spawnSync("schtasks", ["/Run", "/TN", task], { encoding: "utf8" });
508
+ if (r.status !== 0) {
509
+ const msg = (r.stderr || r.stdout || "").trim();
510
+ throw new Error(`schtasks /Run failed: ${msg || "exit " + r.status}`);
511
+ }
512
+ return { status: "restarted", backend: "schtasks", task };
513
+ }
514
+
515
+ function escapeXml(s) {
516
+ return String(s)
517
+ .replace(/&/g, "&amp;")
518
+ .replace(/</g, "&lt;")
519
+ .replace(/>/g, "&gt;")
520
+ .replace(/"/g, "&quot;");
521
+ }