@synap-core/cli 1.8.0 → 1.11.0

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 (372) hide show
  1. package/dist/commands/agent-chat.d.ts +47 -0
  2. package/dist/commands/agent-chat.js +250 -0
  3. package/dist/commands/agent-chat.js.map +1 -0
  4. package/dist/commands/agent-run.js +3 -4
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.js +85 -46
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/automation.js +2 -1
  9. package/dist/commands/automation.js.map +1 -1
  10. package/dist/commands/bridge-setup.d.ts +66 -0
  11. package/dist/commands/bridge-setup.js +1112 -0
  12. package/dist/commands/bridge-setup.js.map +1 -0
  13. package/dist/commands/cap-connections.d.ts +28 -0
  14. package/dist/commands/cap-connections.js +224 -0
  15. package/dist/commands/cap-connections.js.map +1 -0
  16. package/dist/commands/capability.d.ts +89 -0
  17. package/dist/commands/capability.js +1594 -0
  18. package/dist/commands/capability.js.map +1 -0
  19. package/dist/commands/centrality.d.ts +20 -0
  20. package/dist/commands/centrality.js +95 -0
  21. package/dist/commands/centrality.js.map +1 -0
  22. package/dist/commands/connect.d.ts +2 -0
  23. package/dist/commands/connect.js +16 -35
  24. package/dist/commands/connect.js.map +1 -1
  25. package/dist/commands/context.js +6 -7
  26. package/dist/commands/context.js.map +1 -1
  27. package/dist/commands/data-extra.d.ts +1 -0
  28. package/dist/commands/data-extra.js +41 -14
  29. package/dist/commands/data-extra.js.map +1 -1
  30. package/dist/commands/data.d.ts +4 -4
  31. package/dist/commands/data.js +254 -187
  32. package/dist/commands/data.js.map +1 -1
  33. package/dist/commands/diagnose.d.ts +20 -0
  34. package/dist/commands/diagnose.js +186 -0
  35. package/dist/commands/diagnose.js.map +1 -0
  36. package/dist/commands/digest.d.ts +17 -0
  37. package/dist/commands/digest.js +122 -0
  38. package/dist/commands/digest.js.map +1 -0
  39. package/dist/commands/discover.d.ts +2 -0
  40. package/dist/commands/discover.js +25 -8
  41. package/dist/commands/discover.js.map +1 -1
  42. package/dist/commands/doc.d.ts +14 -0
  43. package/dist/commands/doc.js +61 -4
  44. package/dist/commands/doc.js.map +1 -1
  45. package/dist/commands/doctor.d.ts +21 -0
  46. package/dist/commands/doctor.js +189 -0
  47. package/dist/commands/doctor.js.map +1 -0
  48. package/dist/commands/events.js +5 -2
  49. package/dist/commands/events.js.map +1 -1
  50. package/dist/commands/explain.d.ts +1 -1
  51. package/dist/commands/explain.js +18 -15
  52. package/dist/commands/explain.js.map +1 -1
  53. package/dist/commands/facet.d.ts +29 -0
  54. package/dist/commands/facet.js +113 -0
  55. package/dist/commands/facet.js.map +1 -0
  56. package/dist/commands/graph.js +7 -4
  57. package/dist/commands/graph.js.map +1 -1
  58. package/dist/commands/import.d.ts +38 -0
  59. package/dist/commands/import.js +471 -0
  60. package/dist/commands/import.js.map +1 -0
  61. package/dist/commands/init.js +35 -16
  62. package/dist/commands/init.js.map +1 -1
  63. package/dist/commands/keys.js +3 -4
  64. package/dist/commands/keys.js.map +1 -1
  65. package/dist/commands/knowledge.d.ts +8 -0
  66. package/dist/commands/knowledge.js +308 -33
  67. package/dist/commands/knowledge.js.map +1 -1
  68. package/dist/commands/launch.d.ts +50 -0
  69. package/dist/commands/launch.js +790 -0
  70. package/dist/commands/launch.js.map +1 -0
  71. package/dist/commands/lens.d.ts +45 -0
  72. package/dist/commands/lens.js +386 -0
  73. package/dist/commands/lens.js.map +1 -0
  74. package/dist/commands/market-authoring.d.ts +45 -0
  75. package/dist/commands/market-authoring.js +225 -0
  76. package/dist/commands/market-authoring.js.map +1 -0
  77. package/dist/commands/market.d.ts +224 -0
  78. package/dist/commands/market.js +1643 -0
  79. package/dist/commands/market.js.map +1 -0
  80. package/dist/commands/mcp.d.ts +64 -0
  81. package/dist/commands/mcp.js +265 -0
  82. package/dist/commands/mcp.js.map +1 -0
  83. package/dist/commands/observe.js +14 -11
  84. package/dist/commands/observe.js.map +1 -1
  85. package/dist/commands/open.d.ts +21 -4
  86. package/dist/commands/open.js +45 -4
  87. package/dist/commands/open.js.map +1 -1
  88. package/dist/commands/pod.d.ts +100 -0
  89. package/dist/commands/pod.js +359 -0
  90. package/dist/commands/pod.js.map +1 -0
  91. package/dist/commands/pods.d.ts +15 -0
  92. package/dist/commands/pods.js +233 -17
  93. package/dist/commands/pods.js.map +1 -1
  94. package/dist/commands/proposals-actions.js +43 -3
  95. package/dist/commands/proposals-actions.js.map +1 -1
  96. package/dist/commands/purge.d.ts +23 -0
  97. package/dist/commands/purge.js +148 -0
  98. package/dist/commands/purge.js.map +1 -0
  99. package/dist/commands/raycast.d.ts +15 -0
  100. package/dist/commands/raycast.js +153 -0
  101. package/dist/commands/raycast.js.map +1 -0
  102. package/dist/commands/sessions.js +47 -22
  103. package/dist/commands/sessions.js.map +1 -1
  104. package/dist/commands/skill-manage.d.ts +21 -0
  105. package/dist/commands/skill-manage.js +195 -0
  106. package/dist/commands/skill-manage.js.map +1 -0
  107. package/dist/commands/skills-commands.js +2 -1
  108. package/dist/commands/skills-commands.js.map +1 -1
  109. package/dist/commands/skills.js +7 -4
  110. package/dist/commands/skills.js.map +1 -1
  111. package/dist/commands/status.d.ts +3 -1
  112. package/dist/commands/status.js +166 -14
  113. package/dist/commands/status.js.map +1 -1
  114. package/dist/commands/statusline.d.ts +24 -0
  115. package/dist/commands/statusline.js +450 -0
  116. package/dist/commands/statusline.js.map +1 -0
  117. package/dist/commands/subscribe.js +2 -2
  118. package/dist/commands/subscribe.js.map +1 -1
  119. package/dist/commands/template.d.ts +42 -0
  120. package/dist/commands/template.js +191 -0
  121. package/dist/commands/template.js.map +1 -0
  122. package/dist/commands/tools.d.ts +30 -0
  123. package/dist/commands/tools.js +315 -0
  124. package/dist/commands/tools.js.map +1 -0
  125. package/dist/commands/upload.d.ts +30 -0
  126. package/dist/commands/upload.js +155 -0
  127. package/dist/commands/upload.js.map +1 -0
  128. package/dist/commands/versions.d.ts +27 -0
  129. package/dist/commands/versions.js +277 -0
  130. package/dist/commands/versions.js.map +1 -0
  131. package/dist/commands/view.js +3 -2
  132. package/dist/commands/view.js.map +1 -1
  133. package/dist/commands/whoami.d.ts +21 -0
  134. package/dist/commands/whoami.js +129 -0
  135. package/dist/commands/whoami.js.map +1 -0
  136. package/dist/index.js +894 -60
  137. package/dist/index.js.map +1 -1
  138. package/dist/lib/approval-poll.d.ts +42 -0
  139. package/dist/lib/approval-poll.js +52 -0
  140. package/dist/lib/approval-poll.js.map +1 -0
  141. package/dist/lib/auth.d.ts +29 -6
  142. package/dist/lib/auth.js +105 -94
  143. package/dist/lib/auth.js.map +1 -1
  144. package/dist/lib/bundle-version.d.ts +11 -0
  145. package/dist/lib/bundle-version.js +33 -0
  146. package/dist/lib/bundle-version.js.map +1 -0
  147. package/dist/lib/capture-lane.d.ts +30 -12
  148. package/dist/lib/capture-lane.js +73 -14
  149. package/dist/lib/capture-lane.js.map +1 -1
  150. package/dist/lib/capture-structure.d.ts +123 -0
  151. package/dist/lib/capture-structure.js +57 -0
  152. package/dist/lib/capture-structure.js.map +1 -0
  153. package/dist/lib/cp-packages.d.ts +177 -0
  154. package/dist/lib/cp-packages.js +389 -0
  155. package/dist/lib/cp-packages.js.map +1 -0
  156. package/dist/lib/describe-lens.d.ts +46 -0
  157. package/dist/lib/describe-lens.js +33 -0
  158. package/dist/lib/describe-lens.js.map +1 -0
  159. package/dist/lib/hub-client.d.ts +91 -1
  160. package/dist/lib/hub-client.js +565 -22
  161. package/dist/lib/hub-client.js.map +1 -1
  162. package/dist/lib/id.d.ts +23 -0
  163. package/dist/lib/id.js +35 -0
  164. package/dist/lib/id.js.map +1 -0
  165. package/dist/lib/installed.d.ts +51 -0
  166. package/dist/lib/installed.js +70 -0
  167. package/dist/lib/installed.js.map +1 -0
  168. package/dist/lib/next-steps.d.ts +61 -0
  169. package/dist/lib/next-steps.js +146 -0
  170. package/dist/lib/next-steps.js.map +1 -0
  171. package/dist/lib/pod.d.ts +144 -22
  172. package/dist/lib/pod.js +302 -64
  173. package/dist/lib/pod.js.map +1 -1
  174. package/dist/lib/project-ref.d.ts +95 -0
  175. package/dist/lib/project-ref.js +144 -0
  176. package/dist/lib/project-ref.js.map +1 -0
  177. package/dist/lib/project.d.ts +35 -0
  178. package/dist/lib/project.js +39 -0
  179. package/dist/lib/project.js.map +1 -0
  180. package/dist/lib/session-lens.d.ts +33 -0
  181. package/dist/lib/session-lens.js +76 -0
  182. package/dist/lib/session-lens.js.map +1 -0
  183. package/dist/lib/skill-import.d.ts +40 -0
  184. package/dist/lib/skill-import.js +203 -0
  185. package/dist/lib/skill-import.js.map +1 -0
  186. package/dist/lib/skills-installer.d.ts +17 -6
  187. package/dist/lib/skills-installer.js +133 -19
  188. package/dist/lib/skills-installer.js.map +1 -1
  189. package/dist/lib/suite.d.ts +28 -0
  190. package/dist/lib/suite.js +29 -0
  191. package/dist/lib/suite.js.map +1 -0
  192. package/dist/lib/superwhisper-import.d.ts +40 -0
  193. package/dist/lib/superwhisper-import.js +389 -0
  194. package/dist/lib/superwhisper-import.js.map +1 -0
  195. package/dist/lib/targets.d.ts +49 -1
  196. package/dist/lib/targets.js +420 -288
  197. package/dist/lib/targets.js.map +1 -1
  198. package/dist/lib/template-file.d.ts +74 -0
  199. package/dist/lib/template-file.js +140 -0
  200. package/dist/lib/template-file.js.map +1 -0
  201. package/dist/lib/unwrapList.d.ts +1 -0
  202. package/dist/lib/unwrapList.js +20 -0
  203. package/dist/lib/unwrapList.js.map +1 -0
  204. package/dist/utils/logger.d.ts +2 -0
  205. package/dist/utils/logger.js +10 -2
  206. package/dist/utils/logger.js.map +1 -1
  207. package/node_modules/@synap/hub-rest-client/dist/index.cjs +414 -34
  208. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +762 -22
  209. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +762 -22
  210. package/node_modules/@synap/hub-rest-client/dist/index.js +414 -34
  211. package/node_modules/@synap/hub-rest-client/package.json +1 -1
  212. package/node_modules/@synap/hub-rest-client/src/client.ts +525 -37
  213. package/node_modules/@synap/hub-rest-client/src/index.ts +42 -0
  214. package/node_modules/@synap/hub-rest-client/src/setup.ts +57 -1
  215. package/node_modules/@synap/hub-rest-client/src/types.ts +697 -37
  216. package/package.json +11 -5
  217. package/skills/agent-os/SKILL.md +218 -0
  218. package/skills/manifest.json +5 -0
  219. package/skills/onboard/SKILL.md +193 -0
  220. package/skills/synap/SKILL.md +1284 -58
  221. package/skills/synap/_order.txt +37 -0
  222. package/skills/synap/_skill.yaml +24 -0
  223. package/skills/synap/authentication.md +10 -0
  224. package/skills/synap/automations.md +264 -0
  225. package/skills/synap/capabilities.md +107 -0
  226. package/skills/synap/capture.md +50 -142
  227. package/skills/synap/cli-operations.md +107 -0
  228. package/skills/synap/common-mistakes.md +13 -0
  229. package/skills/synap/content-os.md +83 -0
  230. package/skills/synap/creative-loop.md +43 -0
  231. package/skills/synap/crm.md +117 -0
  232. package/skills/synap/diagnostics.md +35 -0
  233. package/skills/synap/digesting-a-channel.md +63 -0
  234. package/skills/synap/document-embeds.md +123 -0
  235. package/skills/synap/escalation-ladder.md +71 -0
  236. package/skills/synap/focus-sessions.md +47 -0
  237. package/skills/synap/governance-rules.md +64 -0
  238. package/skills/synap/governance.md +6 -6
  239. package/skills/synap/graph-gardening.md +21 -0
  240. package/skills/synap/inline-patterns.md +38 -0
  241. package/skills/synap/lenses.md +38 -0
  242. package/skills/synap/linking-principle.md +39 -0
  243. package/skills/synap/linking.md +1 -0
  244. package/skills/synap/mental-model.md +45 -0
  245. package/skills/synap/more.md +7 -0
  246. package/skills/synap/multi-entity-capture.md +12 -0
  247. package/skills/synap/next-action.md +37 -0
  248. package/skills/synap/north-star.md +17 -0
  249. package/skills/synap/operating-mode-rules.md +45 -0
  250. package/skills/synap/operating-mode.md +9 -0
  251. package/skills/synap/quick-reference.md +67 -0
  252. package/skills/synap/reading.md +43 -0
  253. package/skills/synap/recap.md +21 -0
  254. package/skills/synap/reflexes.md +46 -0
  255. package/skills/synap/response-style.md +21 -0
  256. package/skills/synap/scope.md +13 -0
  257. package/skills/synap/showing.md +17 -0
  258. package/skills/synap/user-model.md +25 -0
  259. package/skills/synap/viewframe-cells.md +290 -0
  260. package/skills/synap/work-flow.md +113 -0
  261. package/skills/synap/worked-examples.md +74 -0
  262. package/skills/synap/workspace-design.md +45 -0
  263. package/skills/synap/workspace-edges.md +49 -0
  264. package/skills/synap/writes.md +213 -0
  265. package/skills/synap-app/SKILL.md +319 -0
  266. package/skills/synap-app/_order.txt +5 -0
  267. package/skills/synap-app/_skill.yaml +20 -0
  268. package/skills/synap-app/creating-the-base-app.md +64 -0
  269. package/skills/synap-app/governance.md +42 -0
  270. package/skills/synap-app/install-commands.md +55 -0
  271. package/skills/synap-app/overview.md +50 -0
  272. package/skills/synap-app/using-the-sdk.md +73 -0
  273. package/skills/synap-market/SKILL.md +335 -0
  274. package/skills/synap-market/_order.txt +5 -0
  275. package/skills/synap-market/_skill.yaml +24 -0
  276. package/skills/synap-market/author-and-publish.md +75 -0
  277. package/skills/synap-market/discover-and-fetch.md +59 -0
  278. package/skills/synap-market/governance-and-catalog.md +56 -0
  279. package/skills/synap-market/install.md +55 -0
  280. package/skills/synap-market/overview.md +51 -0
  281. package/skills/synap-schema/SKILL.md +97 -6
  282. package/skills/synap-schema/_order.txt +13 -0
  283. package/skills/synap-schema/_skill.yaml +22 -0
  284. package/skills/synap-schema/before-you-touch.md +14 -0
  285. package/skills/synap-schema/common-mistakes.md +9 -0
  286. package/skills/synap-schema/creating-profiles.md +17 -0
  287. package/skills/synap-schema/discover-profiles.md +24 -0
  288. package/skills/synap-schema/entity-scope.md +8 -0
  289. package/skills/synap-schema/extend-vs-create.md +31 -0
  290. package/skills/synap-schema/more.md +6 -0
  291. package/skills/synap-schema/overlay-properties.md +22 -0
  292. package/skills/synap-schema/profiles.md +3 -0
  293. package/skills/synap-schema/property-defs.md +29 -0
  294. package/skills/synap-schema/read-before-write.md +50 -0
  295. package/skills/synap-schema/relations.md +18 -0
  296. package/skills/synap-schema/worked-example.md +30 -0
  297. package/skills/synap-ui/SKILL.md +26 -2
  298. package/skills/synap-ui/_order.txt +13 -0
  299. package/skills/synap-ui/_skill.yaml +23 -0
  300. package/skills/synap-ui/ai-companion.md +27 -0
  301. package/skills/synap-ui/arranging-bento.md +8 -0
  302. package/skills/synap-ui/before-you-build.md +20 -0
  303. package/skills/synap-ui/bento-layouts.md +35 -0
  304. package/skills/synap-ui/common-mistakes.md +10 -0
  305. package/skills/synap-ui/creating-views.md +26 -0
  306. package/skills/synap-ui/four-builds.md +24 -0
  307. package/skills/synap-ui/more.md +7 -0
  308. package/skills/synap-ui/proposing-vs-extending.md +11 -0
  309. package/skills/synap-ui/view-list.md +24 -0
  310. package/skills/synap-ui/views.md +3 -0
  311. package/skills/synap-ui/worked-example.md +18 -0
  312. package/skills/synap-ui/workspaces.md +47 -0
  313. package/templates/automations/bookmark-enrichment.automation.json +44 -0
  314. package/templates/automations/mail-feed.automation.json +86 -0
  315. package/templates/automations/url-bookmark-capture.automation.json +70 -0
  316. package/templates/skills/README.md +21 -0
  317. package/templates/skills/authoring-automations/SKILL.md +95 -0
  318. package/templates/skills/authoring-automations/reference/node-matrix.md +49 -0
  319. package/templates/skills/authoring-capabilities/SKILL.md +93 -0
  320. package/templates/skills/authoring-capabilities/reference/template-anatomy.md +70 -0
  321. package/templates/skills/authoring-playbooks/SKILL.md +102 -0
  322. package/templates/skills/authoring-playbooks/reference/stages-contract.md +35 -0
  323. package/templates/skills/authoring-playbooks/reference/worked-example.md +64 -0
  324. package/templates/skills/bookmark-enrichment/SKILL.md +40 -0
  325. package/templates/skills/coordinating-agents/SKILL.md +32 -0
  326. package/templates/skills/coordinating-branches/SKILL.md +27 -0
  327. package/templates/skills/mapping-the-server/SKILL.md +54 -0
  328. package/templates/skills/reading-client-progress/SKILL.md +65 -0
  329. package/templates/skills/routing-to-channels/SKILL.md +54 -0
  330. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  331. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  332. package/templates/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  333. package/templates/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  334. package/templates/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  335. package/templates/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  336. package/templates/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  337. package/templates/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  338. package/templates/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  339. package/templates/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  340. package/templates/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  341. package/templates/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  342. package/templates/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  343. package/templates/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  344. package/templates/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  345. package/templates/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  346. package/templates/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  347. package/templates/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  348. package/templates/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  349. package/templates/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  350. package/templates/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  351. package/templates/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  352. package/templates/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  353. package/templates/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  354. package/templates/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  355. package/templates/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  356. package/templates/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  357. package/templates/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  358. package/dist/commands/connectors.d.ts +0 -31
  359. package/dist/commands/connectors.js +0 -276
  360. package/dist/commands/connectors.js.map +0 -1
  361. package/dist/commands/finish.d.ts +0 -25
  362. package/dist/commands/finish.js +0 -274
  363. package/dist/commands/finish.js.map +0 -1
  364. package/dist/commands/openclaw.d.ts +0 -40
  365. package/dist/commands/openclaw.js +0 -1148
  366. package/dist/commands/openclaw.js.map +0 -1
  367. package/dist/commands/security-audit.d.ts +0 -12
  368. package/dist/commands/security-audit.js +0 -100
  369. package/dist/commands/security-audit.js.map +0 -1
  370. package/dist/lib/hardening.d.ts +0 -18
  371. package/dist/lib/hardening.js +0 -214
  372. package/dist/lib/hardening.js.map +0 -1
@@ -1,1148 +0,0 @@
1
- /**
2
- * synap openclaw
3
- *
4
- * Everything you need to access, configure, and connect OpenClaw
5
- * after installation. Subcommands:
6
- *
7
- * synap openclaw — overview: gateway status, AI key, next steps
8
- * synap openclaw connect — show MCP client configs for Claude Desktop, Cursor, etc.
9
- * synap openclaw configure — set AI provider key interactively
10
- * synap openclaw logs — tail container logs
11
- * synap openclaw restart — restart the container
12
- */
13
- import chalk from "chalk";
14
- import ora from "ora";
15
- import { execSync } from "child_process";
16
- import prompts from "prompts";
17
- import crypto from "node:crypto";
18
- import fs from "node:fs";
19
- import path from "node:path";
20
- import { log, banner } from "../utils/logger.js";
21
- import { detectOpenClaw } from "../lib/openclaw.js";
22
- import { findSynapDeployDir, getLocalPodConfig } from "../lib/pod.js";
23
- import { getStoredToken } from "../lib/auth.js";
24
- // ─── Overview ────────────────────────────────────────────────────────────────
25
- export async function openclawOverview() {
26
- banner();
27
- const oc = detectOpenClaw();
28
- if (!oc.found) {
29
- log.warn("OpenClaw is not running.");
30
- log.blank();
31
- log.dim("Start it: docker compose --profile openclaw up -d openclaw");
32
- log.dim("Or run: synap init");
33
- return;
34
- }
35
- // ── Gateway status ──────────────────────────────────────────────────────
36
- log.heading("Gateway");
37
- const gatewayPort = oc.gatewayPort ?? 18789;
38
- if (oc.gatewayRunning) {
39
- log.success(`Running on port ${gatewayPort}`);
40
- log.info(`MCP endpoint: ${chalk.cyan(`http://localhost:${gatewayPort}/mcp`)}`);
41
- }
42
- else {
43
- log.warn(`Port ${gatewayPort} not responding — OpenClaw may still be starting`);
44
- log.dim("Give it a minute, then: synap openclaw");
45
- }
46
- if (oc.runtime === "docker") {
47
- log.dim(`Container: ${oc.containerName ?? "openclaw"}`);
48
- }
49
- if (oc.version)
50
- log.dim(`Version: ${oc.version}`);
51
- // ── AI provider ─────────────────────────────────────────────────────────
52
- log.heading("AI Provider");
53
- const aiConfig = readOpenClawAiConfig(oc);
54
- const hasAnyKey = !!(aiConfig.anthropicKey || aiConfig.openaiKey || aiConfig.geminiKey);
55
- const apiKeyStatus = { configured: hasAnyKey };
56
- if (hasAnyKey) {
57
- if (aiConfig.anthropicKey)
58
- log.success(`Anthropic: ${maskKey(aiConfig.anthropicKey)}`);
59
- if (aiConfig.openaiKey)
60
- log.success(`OpenAI: ${maskKey(aiConfig.openaiKey)}`);
61
- if (aiConfig.geminiKey)
62
- log.success(`Google: ${maskKey(aiConfig.geminiKey)}`);
63
- if (aiConfig.primaryModel)
64
- log.dim(`Model: ${aiConfig.primaryModel}`);
65
- }
66
- else {
67
- log.warn("No AI API key configured — OpenClaw cannot process requests");
68
- log.blank();
69
- if (oc.runtime === "docker") {
70
- log.dim("Set your key: synap openclaw configure");
71
- }
72
- else {
73
- log.dim("Set your key: openclaw configure (or: synap openclaw configure)");
74
- }
75
- log.blank();
76
- log.dim("Supported: Anthropic (Claude), OpenAI, Google, or Synap IS");
77
- }
78
- // ── Skill ───────────────────────────────────────────────────────────────
79
- log.heading("Synap Skill");
80
- const skillInstalled = checkSkillInstalled(oc);
81
- if (skillInstalled) {
82
- log.success("synap skill installed");
83
- }
84
- else {
85
- log.warn("Synap skill not installed");
86
- const containerName = oc.containerName ?? "openclaw";
87
- log.dim(oc.runtime === "docker"
88
- ? `Install: docker exec ${containerName} openclaw skills install synap`
89
- : "Install: openclaw skills install synap");
90
- }
91
- // ── Dashboard ────────────────────────────────────────────────────────────
92
- log.heading("Dashboard");
93
- const publicUrl = getOpenClawPublicUrl();
94
- if (publicUrl) {
95
- log.success(`Public: ${chalk.cyan(publicUrl)}`);
96
- log.dim(`Local: http://localhost:${oc.gatewayPort ?? 18789}`);
97
- log.dim("Open: synap openclaw dashboard");
98
- }
99
- else if (oc.runtime === "docker") {
100
- log.info(`Local: ${chalk.cyan(`http://localhost:${oc.gatewayPort ?? 18789}`)}`);
101
- log.dim("Expose via domain: synap openclaw setup-domain");
102
- log.dim("SSH tunnel (remote): synap openclaw dashboard");
103
- }
104
- else {
105
- log.info(`Web UI: ${chalk.cyan(`http://localhost:${oc.gatewayPort ?? 18789}`)}`);
106
- log.dim("Open: openclaw dashboard — or: synap openclaw dashboard");
107
- }
108
- // ── How to connect ───────────────────────────────────────────────────────
109
- log.heading("AI Client (MCP)");
110
- log.info("Connect Claude Desktop, Cursor, or Windsurf:");
111
- log.blank();
112
- log.dim(" Claude Desktop synap openclaw connect --client claude");
113
- log.dim(" Cursor synap openclaw connect --client cursor");
114
- log.dim(" Windsurf synap openclaw connect --client windsurf");
115
- log.blank();
116
- // ── Next steps ───────────────────────────────────────────────────────────
117
- log.heading("Next Steps");
118
- if (!apiKeyStatus.configured) {
119
- log.info("1. Set your AI key: synap openclaw configure");
120
- log.info("2. Connect a client: synap openclaw connect");
121
- }
122
- else if (!skillInstalled) {
123
- const containerName = oc.containerName ?? "openclaw";
124
- log.info(oc.runtime === "docker"
125
- ? `1. Install skill: docker exec ${containerName} openclaw skills install synap`
126
- : "1. Install skill: openclaw skills install synap");
127
- log.info("2. Connect a client: synap openclaw connect");
128
- }
129
- else if (!oc.gatewayRunning) {
130
- log.info("1. Wait for gateway (it may still be starting)");
131
- log.info("2. Connect a client: synap openclaw connect");
132
- }
133
- else {
134
- log.success("Everything looks good.");
135
- log.info("Connect an AI client: synap openclaw connect");
136
- }
137
- log.blank();
138
- }
139
- // ─── Connect: show MCP client configs ────────────────────────────────────────
140
- export async function openclawConnect(opts) {
141
- const oc = detectOpenClaw();
142
- const gatewayPort = oc.gatewayPort ?? 18789;
143
- const isDocker = oc.runtime === "docker";
144
- // OpenClaw MCP is stdio-based — clients run `openclaw mcp serve` as a local process
145
- // which connects to the gateway over WebSocket. The gateway token authenticates
146
- // the connection — we fetch it from the container so the config is ready to paste.
147
- const token = readGatewayToken(oc) ?? undefined;
148
- if (!token) {
149
- log.warn("Could not read gateway token from OpenClaw.");
150
- log.dim("The MCP configs below will require you to add --token manually.");
151
- log.dim("Run: synap openclaw token");
152
- log.blank();
153
- }
154
- const client = opts.client?.toLowerCase();
155
- if (!client || client === "claude") {
156
- printMcpConfig("Claude Desktop", gatewayPort, isDocker, "claude", token);
157
- }
158
- if (!client || client === "cursor") {
159
- printMcpConfig("Cursor", gatewayPort, isDocker, "cursor", token);
160
- }
161
- if (!client || client === "windsurf") {
162
- printMcpConfig("Windsurf", gatewayPort, isDocker, "windsurf", token);
163
- }
164
- if (client && !["claude", "cursor", "windsurf"].includes(client)) {
165
- log.warn(`Unknown client "${client}". Showing generic config.`);
166
- printMcpConfig("MCP Client", gatewayPort, isDocker, "generic", token);
167
- }
168
- if (isDocker) {
169
- log.blank();
170
- log.info("Remote server? Tunnel the gateway port first:");
171
- log.dim(` ssh -N -L ${gatewayPort}:localhost:${gatewayPort} user@your-server`);
172
- log.dim(" Then use the configs above (they point to localhost)");
173
- log.blank();
174
- log.dim("openclaw must be installed locally on the client machine:");
175
- log.dim(" npm i -g openclaw");
176
- }
177
- }
178
- function printMcpConfig(label, gatewayPort, isRemote, client, token) {
179
- log.heading(label);
180
- // MCP config: stdio command that connects to the local (or tunneled) gateway
181
- const args = ["mcp", "serve"];
182
- if (isRemote) {
183
- args.push("--url", `ws://localhost:${gatewayPort}`);
184
- }
185
- if (token) {
186
- args.push("--token", token);
187
- }
188
- const config = JSON.stringify({ mcpServers: { openclaw: { command: "openclaw", args } } }, null, 2);
189
- const paths = {
190
- claude: "macOS: ~/Library/Application Support/Claude/claude_desktop_config.json",
191
- cursor: "~/.cursor/mcp.json",
192
- windsurf: "~/.windsurf/mcp.json",
193
- generic: "<your MCP client config file>",
194
- };
195
- log.dim(`Config file: ${paths[client]}`);
196
- log.blank();
197
- console.log(chalk.cyan(config));
198
- log.blank();
199
- if (client === "claude") {
200
- log.dim("After saving: quit and reopen Claude Desktop");
201
- log.dim('Ask: "What tools do you have?" — OpenClaw tools should appear');
202
- }
203
- if (client === "cursor") {
204
- log.dim("After saving: Cmd+Shift+P → MCP: Restart Servers");
205
- }
206
- log.blank();
207
- }
208
- // ─── Dashboard ───────────────────────────────────────────────────────────────
209
- export function openclawDashboard() {
210
- const oc = detectOpenClaw();
211
- const port = oc.gatewayPort ?? 18789;
212
- const localUrl = `http://localhost:${port}`;
213
- // Check if a public domain is configured via Caddy
214
- const publicUrl = getOpenClawPublicUrl();
215
- if (publicUrl) {
216
- log.info(`Dashboard: ${chalk.cyan(publicUrl)}`);
217
- log.blank();
218
- try {
219
- const open = process.platform === "darwin" ? "open" : "xdg-open";
220
- execSync(`${open} ${publicUrl}`, { stdio: "ignore" });
221
- }
222
- catch {
223
- log.dim("Open the URL above in your browser.");
224
- }
225
- return;
226
- }
227
- if (oc.runtime === "docker") {
228
- const isRemoteServer = !process.env.DISPLAY && process.platform === "linux";
229
- if (isRemoteServer) {
230
- log.info(`Dashboard: ${chalk.cyan(localUrl)}`);
231
- log.blank();
232
- log.info("You're on a remote server — tunnel the port to your laptop:");
233
- log.blank();
234
- console.log(chalk.cyan(` ssh -N -L ${port}:localhost:${port} user@$(hostname -I | awk '{print $1}')`));
235
- log.blank();
236
- log.dim(`Then open ${chalk.cyan(localUrl)} in your browser.`);
237
- log.blank();
238
- log.info("Or expose it permanently via your domain:");
239
- log.dim(" synap openclaw setup-domain");
240
- }
241
- else {
242
- log.info(`Opening ${chalk.cyan(localUrl)} ...`);
243
- try {
244
- const open = process.platform === "darwin" ? "open" : "xdg-open";
245
- execSync(`${open} ${localUrl}`, { stdio: "ignore" });
246
- }
247
- catch {
248
- log.dim(`Open manually: ${localUrl}`);
249
- }
250
- }
251
- }
252
- else {
253
- log.info("Opening dashboard...");
254
- try {
255
- execSync("openclaw dashboard", { stdio: "inherit", timeout: 5000 });
256
- }
257
- catch {
258
- try {
259
- const open = process.platform === "darwin" ? "open" : "xdg-open";
260
- execSync(`${open} ${localUrl}`, { stdio: "ignore" });
261
- }
262
- catch {
263
- log.info(`Dashboard: ${chalk.cyan(localUrl)}`);
264
- }
265
- }
266
- }
267
- }
268
- export async function openclawSetupDomain() {
269
- banner();
270
- log.heading("Expose OpenClaw Dashboard");
271
- log.blank();
272
- const deployDir = findSynapDeployDir();
273
- if (!deployDir) {
274
- log.error("Couldn't find the Synap deploy directory.");
275
- log.dim("Run this command on the server where your pod is deployed.");
276
- return;
277
- }
278
- const envDomain = readEnvVar(deployDir, "DOMAIN");
279
- if (!envDomain) {
280
- log.error("DOMAIN is not set in .env — can't determine pod domain.");
281
- return;
282
- }
283
- // Detect pod type: managed (*.synap.live) vs self-hosted (custom domain)
284
- const isManaged = envDomain.endsWith(".synap.live") || envDomain === "synap.live";
285
- const localConfig = getLocalPodConfig();
286
- const creds = getStoredToken();
287
- log.info(`Pod domain: ${chalk.cyan(envDomain)}`);
288
- log.dim(`Type: ${isManaged ? "managed (synap.live)" : "self-hosted"}`);
289
- log.blank();
290
- let publicDomain;
291
- let authMode;
292
- let basicAuthPassword;
293
- if (isManaged) {
294
- // ── Managed pod flow: CP creates DNS + CP OAuth ───────────────────────
295
- if (!creds) {
296
- log.error("Not logged in to Synap.");
297
- log.dim("Run: synap login (or: synap login --token <token>)");
298
- return;
299
- }
300
- if (!localConfig?.podId) {
301
- log.error("Pod ID not found in local config.");
302
- log.dim("Run: synap init (to set up the pod connection)");
303
- return;
304
- }
305
- const { confirm } = await prompts({
306
- type: "confirm",
307
- name: "confirm",
308
- message: `Create DNS record openclaw.${envDomain}? (CP will provision it)`,
309
- initial: true,
310
- });
311
- if (!confirm)
312
- return;
313
- const spinner = ora("Asking Control Plane to create DNS record...").start();
314
- try {
315
- const result = await requestDashboardDomainFromCp(creds.token, localConfig.podId);
316
- if (result.domain.trim().toLowerCase() === envDomain.trim().toLowerCase()) {
317
- throw new Error(`Refusing unsafe dashboard domain "${result.domain}": use a dedicated subdomain (e.g. openclaw.${envDomain}).`);
318
- }
319
- publicDomain = result.domain;
320
- authMode = result.authMode;
321
- spinner.succeed(`Domain created: ${chalk.cyan(publicDomain)}`);
322
- }
323
- catch (err) {
324
- spinner.fail(err instanceof Error ? err.message : String(err));
325
- log.dim("If this is a custom-domain or self-hosted pod, run again — it will fall back to manual mode.");
326
- return;
327
- }
328
- }
329
- else {
330
- // ── Self-hosted flow: user sets DNS, we use basic auth ────────────────
331
- log.info("Self-hosted pod — you'll need to add a DNS record yourself.");
332
- log.blank();
333
- const { subdomain } = await prompts({
334
- type: "text",
335
- name: "subdomain",
336
- message: "Public subdomain for OpenClaw:",
337
- initial: `openclaw.${envDomain}`,
338
- });
339
- if (!subdomain)
340
- return;
341
- if (subdomain.trim().toLowerCase() === envDomain.trim().toLowerCase()) {
342
- log.error("OPENCLAW_DOMAIN cannot match DOMAIN. This causes redirect loops.");
343
- log.dim(`Use a dedicated host, for example: openclaw.${envDomain}`);
344
- return;
345
- }
346
- publicDomain = subdomain;
347
- authMode = "basic";
348
- log.blank();
349
- log.info("Add this DNS A record:");
350
- log.dim(` Type: A`);
351
- log.dim(` Name: ${publicDomain}`);
352
- log.dim(` Value: <this server's public IP>`);
353
- log.blank();
354
- const { dnsReady } = await prompts({
355
- type: "confirm",
356
- name: "dnsReady",
357
- message: "DNS record added?",
358
- initial: true,
359
- });
360
- if (!dnsReady) {
361
- log.dim("Run this command again once DNS is set up.");
362
- return;
363
- }
364
- // Generate a strong random password for basic auth
365
- basicAuthPassword = generatePassword(32);
366
- }
367
- // ── Write the Caddy auth snippet ──────────────────────────────────────────
368
- const snippetPath = path.join(deployDir, "openclaw_auth.snippet");
369
- if (authMode === "cp-oauth") {
370
- fs.writeFileSync(snippetPath, generateCpOAuthSnippet(), { mode: 0o644 });
371
- log.success("Wrote CP OAuth auth snippet");
372
- }
373
- else {
374
- const hash = await bcryptHashViaCaddy(basicAuthPassword);
375
- fs.writeFileSync(snippetPath, generateBasicAuthSnippet("openclaw", hash), { mode: 0o644 });
376
- log.success("Wrote basic auth snippet");
377
- }
378
- // ── Update .env ───────────────────────────────────────────────────────────
379
- const envFile = path.join(deployDir, ".env");
380
- writeEnvVar(envFile, "OPENCLAW_DOMAIN", publicDomain);
381
- log.success(`Set OPENCLAW_DOMAIN=${publicDomain} in .env`);
382
- // ── Restart Caddy ────────────────────────────────────────────────────────
383
- log.blank();
384
- log.info("Restarting Caddy to apply changes...");
385
- try {
386
- execSync("docker compose restart caddy", {
387
- cwd: deployDir,
388
- stdio: "pipe",
389
- timeout: 30000,
390
- });
391
- log.success("Caddy restarted");
392
- }
393
- catch {
394
- log.warn("Caddy restart failed — run manually:");
395
- log.dim(` cd ${deployDir} && docker compose restart caddy`);
396
- }
397
- // ── Summary ──────────────────────────────────────────────────────────────
398
- log.blank();
399
- console.log(chalk.green("═══════════════════════════════════════════"));
400
- console.log(chalk.green.bold(" OpenClaw Dashboard Ready"));
401
- console.log(chalk.green("═══════════════════════════════════════════"));
402
- log.blank();
403
- log.info(`URL: ${chalk.cyan(`https://${publicDomain}`)}`);
404
- log.blank();
405
- if (authMode === "cp-oauth") {
406
- log.info("Auth: Synap session (you're already logged in to synap.live)");
407
- log.dim("Open the URL — if you're not signed in, you'll be redirected to login.");
408
- }
409
- else {
410
- log.info("Auth: basic auth");
411
- log.dim(` Username: openclaw`);
412
- log.dim(` Password: ${chalk.cyan(basicAuthPassword)}`);
413
- log.blank();
414
- log.warn("Save this password — it won't be shown again.");
415
- }
416
- log.blank();
417
- log.dim("TLS: Caddy will provision a Let's Encrypt certificate on first visit.");
418
- log.dim("This takes ~30s the first time.");
419
- log.blank();
420
- }
421
- export async function openclawConfigure(opts = {}) {
422
- const oc = detectOpenClaw();
423
- if (!oc.found) {
424
- log.error("OpenClaw is not running.");
425
- return;
426
- }
427
- // Verify openclaw binary is actually callable inside the container before
428
- // we start writing config. Protects against "container up but openclaw not ready".
429
- if (oc.runtime === "docker" && !verifyOpenclawCliReady(oc.containerName ?? "openclaw")) {
430
- log.error("OpenClaw container is running, but the openclaw CLI isn't responding.");
431
- log.dim("It may still be initializing. Wait 30s and try again, or run: synap openclaw doctor");
432
- return;
433
- }
434
- // ── Show current config ─────────────────────────────────────────────────
435
- if (opts.show) {
436
- log.heading("OpenClaw AI Config");
437
- const current = readOpenClawAiConfig(oc);
438
- if (current.anthropicKey)
439
- log.success(`Anthropic: ${maskKey(current.anthropicKey)}`);
440
- if (current.openaiKey)
441
- log.success(`OpenAI: ${maskKey(current.openaiKey)}`);
442
- if (current.geminiKey)
443
- log.success(`Google: ${maskKey(current.geminiKey)}`);
444
- if (current.primaryModel)
445
- log.info(`Model: ${current.primaryModel}`);
446
- if (!current.anthropicKey && !current.openaiKey && !current.geminiKey) {
447
- log.warn("No AI provider key configured");
448
- }
449
- return;
450
- }
451
- // ── Interactive (delegate to OpenClaw's own wizard) ──────────────────────
452
- if (opts.interactive) {
453
- handoffToOpenClawWizard(oc);
454
- return;
455
- }
456
- banner();
457
- log.heading("Configure AI Provider");
458
- // ── Scripted path (--provider + --key) ───────────────────────────────────
459
- let provider = opts.provider;
460
- let apiKey = opts.key;
461
- let model = opts.model;
462
- if (!provider) {
463
- const pick = await prompts({
464
- type: "select",
465
- name: "provider",
466
- message: "Which AI provider?",
467
- choices: [
468
- { title: "Anthropic (Claude)", description: "recommended", value: "anthropic" },
469
- { title: "OpenAI (GPT-4o)", value: "openai" },
470
- { title: "Google (Gemini)", value: "google" },
471
- { title: "Run OpenClaw's own wizard", description: "interactive", value: "wizard" },
472
- ],
473
- });
474
- if (!pick.provider)
475
- return;
476
- // Inline handoff — don't recurse, we already have `oc` in scope
477
- if (pick.provider === "wizard") {
478
- handoffToOpenClawWizard(oc);
479
- return;
480
- }
481
- provider = pick.provider;
482
- }
483
- const envKey = provider === "anthropic"
484
- ? "ANTHROPIC_API_KEY"
485
- : provider === "openai"
486
- ? "OPENAI_API_KEY"
487
- : "GEMINI_API_KEY";
488
- const modelDefault = provider === "anthropic"
489
- ? "anthropic/claude-sonnet-4-6"
490
- : provider === "openai"
491
- ? "openai/gpt-4o"
492
- : "google/gemini-2.0-flash";
493
- if (!apiKey) {
494
- const res = await prompts({ type: "password", name: "apiKey", message: `${envKey}:` });
495
- if (!res.apiKey)
496
- return;
497
- apiKey = res.apiKey;
498
- }
499
- if (!model) {
500
- const res = await prompts({
501
- type: "text",
502
- name: "model",
503
- message: "Model:",
504
- initial: modelDefault,
505
- });
506
- if (!res.model)
507
- return;
508
- model = res.model;
509
- }
510
- // Final narrowing — both are guaranteed strings by this point
511
- const finalKey = apiKey;
512
- const finalModel = model;
513
- // ── Write via OpenClaw's own config system ──────────────────────────────
514
- const containerName = oc.containerName ?? "openclaw";
515
- // Step 1: API key — MUST succeed
516
- const keySpinner = ora(`Setting env.${envKey}...`).start();
517
- const keyResult = runOpenClawConfigSet(containerName, `env.${envKey}`, finalKey);
518
- if (!keyResult.ok) {
519
- keySpinner.fail(`Failed to set env.${envKey}`);
520
- log.dim(keyResult.stderr || keyResult.error || "(no output)");
521
- log.dim("Diagnose: synap openclaw doctor");
522
- return;
523
- }
524
- keySpinner.succeed(`Set env.${envKey}`);
525
- // Step 2: Model — non-fatal (schema path may differ across OpenClaw versions)
526
- const modelSpinner = ora(`Setting default model to ${finalModel}...`).start();
527
- const modelPaths = [
528
- "agents.defaults.model.primary",
529
- "models.default",
530
- "agent.model",
531
- ];
532
- let modelSet = false;
533
- let modelError = "";
534
- for (const modelPath of modelPaths) {
535
- const r = runOpenClawConfigSet(containerName, modelPath, finalModel);
536
- if (r.ok) {
537
- modelSpinner.succeed(`Set ${modelPath} = ${finalModel}`);
538
- modelSet = true;
539
- break;
540
- }
541
- modelError = r.stderr || r.error || modelError;
542
- }
543
- if (!modelSet) {
544
- modelSpinner.warn(`Couldn't set model automatically — set it manually`);
545
- log.dim(`Tried: ${modelPaths.join(", ")}`);
546
- if (modelError)
547
- log.dim(`Last error: ${modelError.split("\n")[0]}`);
548
- log.dim(`Run: docker exec -it ${containerName} openclaw configure`);
549
- }
550
- // ── Restart to apply ─────────────────────────────────────────────────────
551
- log.info("Restarting OpenClaw to apply...");
552
- try {
553
- execSync(`docker restart ${containerName}`, { stdio: "pipe", timeout: 30000 });
554
- log.success("Restarted — give it ~30s to come back up");
555
- log.dim("Check: synap openclaw");
556
- }
557
- catch {
558
- log.warn("Restart failed — run manually: docker restart openclaw");
559
- }
560
- log.blank();
561
- }
562
- // ─── Connections: unified view of what's connected ─────────────────────────
563
- export function openclawConnections() {
564
- banner();
565
- const oc = detectOpenClaw();
566
- if (!oc.found) {
567
- log.error("OpenClaw is not running.");
568
- return;
569
- }
570
- const publicUrl = getOpenClawPublicUrl();
571
- const gatewayPort = oc.gatewayPort ?? 18789;
572
- const localUrl = `http://localhost:${gatewayPort}`;
573
- const dashboardUrl = publicUrl ?? localUrl;
574
- // ── AI Providers ─────────────────────────────────────────────────────────
575
- log.heading("AI Providers");
576
- const ai = readOpenClawAiConfig(oc);
577
- const synapProvider = readSynapProviderConfig(oc);
578
- const hasAny = ai.anthropicKey || ai.openaiKey || ai.geminiKey || synapProvider;
579
- if (!hasAny) {
580
- log.warn("No provider configured");
581
- log.dim("Run: synap openclaw configure");
582
- }
583
- else {
584
- if (ai.anthropicKey)
585
- log.success(`Anthropic ${maskKey(ai.anthropicKey)}`);
586
- if (ai.openaiKey)
587
- log.success(`OpenAI ${maskKey(ai.openaiKey)}`);
588
- if (ai.geminiKey)
589
- log.success(`Google Gemini ${maskKey(ai.geminiKey)}`);
590
- if (synapProvider?.baseUrl) {
591
- log.success(`Synap IS ${synapProvider.baseUrl}`);
592
- log.dim(" Models: synap/auto, synap/balanced, synap/advanced");
593
- }
594
- if (ai.primaryModel)
595
- log.dim(`Primary model: ${ai.primaryModel}`);
596
- }
597
- // ── Skills ───────────────────────────────────────────────────────────────
598
- log.heading("Skills");
599
- const skills = readOpenClawSkills(oc);
600
- if (skills.length > 0) {
601
- for (const skill of skills) {
602
- if (skill.toLowerCase().includes("synap")) {
603
- log.success(`${skill} ${chalk.dim("(Synap knowledge graph + relay)")}`);
604
- }
605
- else {
606
- log.info(skill);
607
- }
608
- }
609
- }
610
- else {
611
- log.dim("None detected");
612
- }
613
- log.dim(`Browse more: ${dashboardUrl}/#skills`);
614
- // ── Channels ─────────────────────────────────────────────────────────────
615
- log.heading("Channels");
616
- const channels = readOpenClawChannels(oc);
617
- const channelTypes = [
618
- "telegram",
619
- "discord",
620
- "whatsapp",
621
- "slack",
622
- "signal",
623
- "imessage",
624
- "matrix",
625
- ];
626
- for (const type of channelTypes) {
627
- const connected = channels.some((c) => c.toLowerCase().includes(type));
628
- const label = type.charAt(0).toUpperCase() + type.slice(1);
629
- if (connected) {
630
- log.success(`${label.padEnd(12)}connected`);
631
- }
632
- else {
633
- log.dim(`○ ${label.padEnd(12)}not connected`);
634
- }
635
- }
636
- log.blank();
637
- log.dim(`Connect channels: ${dashboardUrl}/#channels`);
638
- log.dim("Or: synap openclaw open channels");
639
- // ── MCP Clients ──────────────────────────────────────────────────────────
640
- log.heading("MCP Clients");
641
- log.info("Connect Claude Desktop, Cursor, Windsurf to OpenClaw:");
642
- log.dim(" synap openclaw connect --client claude");
643
- log.dim(" synap openclaw connect --client cursor");
644
- log.dim(" synap openclaw connect --client windsurf");
645
- // ── Dashboard ────────────────────────────────────────────────────────────
646
- log.heading("Dashboard");
647
- log.info(chalk.cyan(dashboardUrl));
648
- if (!publicUrl) {
649
- log.dim("Not publicly exposed — run: synap openclaw setup-domain");
650
- }
651
- log.blank();
652
- }
653
- // ─── Open: deep-link to dashboard sections ───────────────────────────────────
654
- const DASHBOARD_SECTIONS = ["channels", "skills", "config", "chat", "sessions", "logs"];
655
- export function openclawOpen(section) {
656
- const oc = detectOpenClaw();
657
- if (!oc.found) {
658
- log.error("OpenClaw is not running.");
659
- return;
660
- }
661
- const publicUrl = getOpenClawPublicUrl();
662
- const gatewayPort = oc.gatewayPort ?? 18789;
663
- const baseUrl = publicUrl ?? `http://localhost:${gatewayPort}`;
664
- let url = baseUrl;
665
- if (section) {
666
- const s = section.toLowerCase();
667
- if (DASHBOARD_SECTIONS.includes(s)) {
668
- url = `${baseUrl}/#${s}`;
669
- }
670
- else {
671
- log.warn(`Unknown section "${section}". Known sections:`);
672
- log.dim(` ${DASHBOARD_SECTIONS.join(", ")}`);
673
- log.info(`Opening main dashboard instead: ${chalk.cyan(baseUrl)}`);
674
- url = baseUrl;
675
- }
676
- }
677
- log.info(`Opening ${chalk.cyan(url)}`);
678
- try {
679
- const open = process.platform === "darwin" ? "open" : "xdg-open";
680
- execSync(`${open} ${JSON.stringify(url)}`, { stdio: "ignore" });
681
- }
682
- catch {
683
- log.dim("Could not open browser automatically — open the URL above manually.");
684
- }
685
- }
686
- // ─── Configure helpers ───────────────────────────────────────────────────────
687
- function verifyOpenclawCliReady(containerName) {
688
- try {
689
- execSync(`docker exec ${containerName} openclaw --version`, {
690
- stdio: "pipe",
691
- timeout: 8000,
692
- });
693
- return true;
694
- }
695
- catch {
696
- return false;
697
- }
698
- }
699
- function handoffToOpenClawWizard(oc) {
700
- if (oc.runtime !== "docker") {
701
- log.error("Interactive wizard only works for Docker runtime.");
702
- log.dim("For local install, run: openclaw configure");
703
- return;
704
- }
705
- const containerName = oc.containerName ?? "openclaw";
706
- log.heading("Handing off to OpenClaw");
707
- log.dim(`Running: docker exec -it ${containerName} openclaw configure`);
708
- log.blank();
709
- try {
710
- // stdio: inherit passes our TTY through to docker exec.
711
- // If we're not on a TTY (e.g. piped input), docker exec -it will fail — catch it.
712
- execSync(`docker exec -it ${containerName} openclaw configure`, { stdio: "inherit" });
713
- }
714
- catch (err) {
715
- const msg = err instanceof Error ? err.message : String(err);
716
- log.error(`openclaw configure failed: ${msg}`);
717
- log.dim(`Run directly: docker exec -it ${containerName} openclaw configure`);
718
- }
719
- }
720
- function runOpenClawConfigSet(containerName, key, value) {
721
- try {
722
- // Use shell quoting via JSON.stringify — handles quotes + special chars safely.
723
- // stdio: pipe so we can capture stderr on failure.
724
- execSync(`docker exec ${containerName} openclaw config set ${key} ${JSON.stringify(value)}`, { stdio: "pipe", timeout: 15000, encoding: "utf-8" });
725
- return { ok: true };
726
- }
727
- catch (err) {
728
- const e = err;
729
- const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf-8");
730
- return {
731
- ok: false,
732
- stderr: stderr?.trim(),
733
- error: e.message,
734
- };
735
- }
736
- }
737
- // ─── Logs: tail container output ─────────────────────────────────────────────
738
- export function openclawLogs(opts) {
739
- const oc = detectOpenClaw();
740
- if (oc.runtime !== "docker") {
741
- log.warn("Log tailing only available for Docker runtime.");
742
- log.dim("OpenClaw logs: check your process manager or journald");
743
- return;
744
- }
745
- const containerName = oc.containerName ?? "openclaw";
746
- const lines = opts.lines ?? 50;
747
- const flag = opts.follow ? "-f" : `--tail ${lines}`;
748
- try {
749
- log.dim(`docker logs ${containerName} ${flag}`);
750
- log.blank();
751
- execSync(`docker logs ${containerName} ${flag}`, { stdio: "inherit", timeout: opts.follow ? 0 : 30000 });
752
- }
753
- catch {
754
- log.error(`Could not get logs for container "${containerName}"`);
755
- log.dim(`Try: docker logs ${containerName} --tail 50`);
756
- }
757
- }
758
- // ─── Token: print the gateway token ──────────────────────────────────────────
759
- export function openclawToken(opts) {
760
- const oc = detectOpenClaw();
761
- if (!oc.found) {
762
- log.error("OpenClaw is not running.");
763
- return;
764
- }
765
- const token = readGatewayToken(oc);
766
- if (!token) {
767
- log.error("Could not read gateway token from OpenClaw.");
768
- log.dim("Try: synap openclaw doctor");
769
- return;
770
- }
771
- if (opts.for) {
772
- // Print a pre-filled MCP client config with the token embedded
773
- const client = opts.for.toLowerCase();
774
- const gatewayPort = oc.gatewayPort ?? 18789;
775
- const config = {
776
- mcpServers: {
777
- openclaw: {
778
- command: "openclaw",
779
- args: [
780
- "mcp",
781
- "serve",
782
- "--url",
783
- `ws://localhost:${gatewayPort}`,
784
- "--token",
785
- token,
786
- ],
787
- },
788
- },
789
- };
790
- const paths = {
791
- claude: "~/Library/Application Support/Claude/claude_desktop_config.json",
792
- cursor: "~/.cursor/mcp.json",
793
- windsurf: "~/.windsurf/mcp.json",
794
- };
795
- log.heading(client.charAt(0).toUpperCase() + client.slice(1));
796
- if (paths[client])
797
- log.dim(`Config file: ${paths[client]}`);
798
- log.blank();
799
- console.log(chalk.cyan(JSON.stringify(config, null, 2)));
800
- log.blank();
801
- return;
802
- }
803
- if (opts.copy) {
804
- try {
805
- const pbcopy = process.platform === "darwin"
806
- ? "pbcopy"
807
- : process.platform === "linux"
808
- ? "xclip -selection clipboard"
809
- : null;
810
- if (pbcopy) {
811
- execSync(`echo -n ${JSON.stringify(token)} | ${pbcopy}`, { stdio: "pipe" });
812
- log.success("Token copied to clipboard");
813
- return;
814
- }
815
- }
816
- catch {
817
- // fall through to print
818
- }
819
- }
820
- // Plain print
821
- console.log(token);
822
- }
823
- function readGatewayToken(oc) {
824
- if (!oc.found)
825
- return null;
826
- if (oc.runtime === "docker") {
827
- const containerName = oc.containerName ?? "openclaw";
828
- // Try OpenClaw's own config first — works even if token file path changes
829
- try {
830
- const raw = execSync(`docker exec ${containerName} openclaw config get gateway.token 2>/dev/null`, { encoding: "utf-8", timeout: 5000 }).trim();
831
- if (raw && raw !== "undefined" && raw !== "null") {
832
- return raw.replace(/^["']|["']$/g, "");
833
- }
834
- }
835
- catch {
836
- // fall through
837
- }
838
- // Fallback: read the token file directly
839
- try {
840
- const raw = execSync(`docker exec ${containerName} cat /root/.openclaw/gateway.token 2>/dev/null`, { encoding: "utf-8", timeout: 5000 }).trim();
841
- return raw || null;
842
- }
843
- catch {
844
- return null;
845
- }
846
- }
847
- // Local install — read from host filesystem
848
- try {
849
- const tokenPath = `${process.env.HOME}/.openclaw/gateway.token`;
850
- if (fs.existsSync(tokenPath)) {
851
- return fs.readFileSync(tokenPath, "utf-8").trim();
852
- }
853
- }
854
- catch {
855
- // ignore
856
- }
857
- return null;
858
- }
859
- // ─── Doctor: run OpenClaw's own diagnostic ───────────────────────────────────
860
- export function openclawDoctor(opts) {
861
- const oc = detectOpenClaw();
862
- if (!oc.found) {
863
- log.error("OpenClaw is not running.");
864
- return;
865
- }
866
- const fixFlag = opts.fix ? " --fix" : "";
867
- if (oc.runtime === "docker") {
868
- const containerName = oc.containerName ?? "openclaw";
869
- log.dim(`Running: docker exec ${containerName} openclaw doctor${fixFlag}`);
870
- log.blank();
871
- try {
872
- execSync(`docker exec ${containerName} openclaw doctor${fixFlag}`, {
873
- stdio: "inherit",
874
- timeout: 60000,
875
- });
876
- }
877
- catch {
878
- log.warn("openclaw doctor reported issues or failed");
879
- }
880
- }
881
- else {
882
- try {
883
- execSync(`openclaw doctor${fixFlag}`, { stdio: "inherit", timeout: 60000 });
884
- }
885
- catch {
886
- log.warn("openclaw doctor reported issues or failed");
887
- }
888
- }
889
- }
890
- // ─── Restart ─────────────────────────────────────────────────────────────────
891
- export async function openclawRestart() {
892
- const oc = detectOpenClaw();
893
- if (oc.runtime !== "docker") {
894
- log.warn("Restart only available for Docker runtime.");
895
- return;
896
- }
897
- const containerName = oc.containerName ?? "openclaw";
898
- const { confirm } = await prompts({
899
- type: "confirm",
900
- name: "confirm",
901
- message: `Restart ${containerName}?`,
902
- initial: true,
903
- });
904
- if (!confirm)
905
- return;
906
- try {
907
- execSync(`docker restart ${containerName}`, { stdio: "pipe", timeout: 30000 });
908
- log.success(`${containerName} restarted — give it ~30s to come back up`);
909
- log.dim("Check: synap openclaw");
910
- }
911
- catch {
912
- log.error("Restart failed");
913
- log.dim(`Try: docker restart ${containerName}`);
914
- }
915
- }
916
- // ─── Helpers ─────────────────────────────────────────────────────────────────
917
- function getOpenClawPublicUrl() {
918
- const deployDir = findSynapDeployDir();
919
- if (!deployDir)
920
- return null;
921
- try {
922
- const content = fs.readFileSync(`${deployDir}/.env`, "utf-8");
923
- const match = content.match(/^OPENCLAW_DOMAIN=(.+)$/m);
924
- const domain = match?.[1]?.trim();
925
- if (!domain || domain === "disabled.invalid" || domain === "")
926
- return null;
927
- return `https://${domain}`;
928
- }
929
- catch {
930
- return null;
931
- }
932
- }
933
- function checkSkillInstalled(oc) {
934
- if (!oc.found)
935
- return false;
936
- if (oc.runtime === "docker") {
937
- try {
938
- const containerName = oc.containerName ?? "openclaw";
939
- const out = execSync(`docker exec ${containerName} openclaw skills list 2>/dev/null`, { encoding: "utf-8", timeout: 10000 });
940
- return /synap/i.test(out);
941
- }
942
- catch {
943
- return false;
944
- }
945
- }
946
- // Local install — check skills directory
947
- return false;
948
- }
949
- function writeEnvVar(envFile, key, value) {
950
- let content = "";
951
- try {
952
- content = fs.existsSync(envFile) ? fs.readFileSync(envFile, "utf-8") : "";
953
- }
954
- catch {
955
- // start fresh
956
- }
957
- const regex = new RegExp(`^${key}=.*`, "m");
958
- const line = `${key}=${value}`;
959
- content = regex.test(content)
960
- ? content.replace(regex, line)
961
- : content.endsWith("\n") || content === ""
962
- ? content + line + "\n"
963
- : content + "\n" + line + "\n";
964
- fs.writeFileSync(envFile, content, { mode: 0o600 });
965
- }
966
- // ─── Domain setup helpers ────────────────────────────────────────────────────
967
- function readEnvVar(deployDir, key) {
968
- try {
969
- const content = fs.readFileSync(path.join(deployDir, ".env"), "utf-8");
970
- const match = content.match(new RegExp(`^${key}=(.+)$`, "m"));
971
- return match?.[1]?.trim() ?? null;
972
- }
973
- catch {
974
- return null;
975
- }
976
- }
977
- function generatePassword(length) {
978
- // URL-safe random password
979
- return crypto
980
- .randomBytes(length)
981
- .toString("base64url")
982
- .slice(0, length);
983
- }
984
- async function bcryptHashViaCaddy(plaintext) {
985
- // Caddy ships `caddy hash-password` which outputs a bcrypt hash.
986
- // Run it via the running caddy container so we don't need a bcrypt dep in Node.
987
- try {
988
- const hash = execSync(`docker exec -i caddy caddy hash-password --plaintext ${JSON.stringify(plaintext)}`, { encoding: "utf-8", timeout: 10000 }).trim();
989
- return hash;
990
- }
991
- catch (err) {
992
- // Fallback: try without container (if caddy is in PATH)
993
- try {
994
- return execSync(`caddy hash-password --plaintext ${JSON.stringify(plaintext)}`, { encoding: "utf-8", timeout: 10000 }).trim();
995
- }
996
- catch {
997
- throw new Error(`Could not hash password via caddy: ${err instanceof Error ? err.message : String(err)}`);
998
- }
999
- }
1000
- }
1001
- function generateCpOAuthSnippet() {
1002
- return `# OpenClaw dashboard auth — CP OAuth (generated by synap openclaw setup-domain)
1003
- #
1004
- # Validates the Better Auth session cookie against the Synap Control Plane.
1005
- # Because the CP cookie is set with Domain=.synap.live (crossSubDomainCookies),
1006
- # the browser sends it automatically to this subdomain.
1007
- #
1008
- # 200 → authenticated, pass through to openclaw
1009
- # 401 → redirect to synap.live login
1010
-
1011
- forward_auth https://api.synap.live {
1012
- uri /api/auth/me
1013
- copy_headers X-Authenticated-User
1014
-
1015
- @unauthorized status 401
1016
- handle_response @unauthorized {
1017
- redir https://synap.live/login?redirect=https://{host}{uri} temporary
1018
- }
1019
- }
1020
- `;
1021
- }
1022
- function generateBasicAuthSnippet(username, bcryptHash) {
1023
- return `# OpenClaw dashboard auth — basic auth (generated by synap openclaw setup-domain)
1024
- #
1025
- # Single credential protects the dashboard. The password hash below was
1026
- # generated with \`caddy hash-password\`. To rotate, run:
1027
- # synap openclaw setup-domain
1028
-
1029
- basicauth {
1030
- ${username} ${bcryptHash}
1031
- }
1032
- `;
1033
- }
1034
- function readOpenClawAiConfig(oc) {
1035
- if (!oc.found || oc.runtime !== "docker")
1036
- return {};
1037
- const containerName = oc.containerName ?? "openclaw";
1038
- const read = (key) => {
1039
- try {
1040
- const out = execSync(`docker exec ${containerName} openclaw config get ${key} 2>/dev/null`, { encoding: "utf-8", timeout: 5000 }).trim();
1041
- if (!out || out === "undefined" || out === "null")
1042
- return undefined;
1043
- return out.replace(/^["']|["']$/g, "");
1044
- }
1045
- catch {
1046
- return undefined;
1047
- }
1048
- };
1049
- return {
1050
- anthropicKey: read("env.ANTHROPIC_API_KEY"),
1051
- openaiKey: read("env.OPENAI_API_KEY"),
1052
- geminiKey: read("env.GEMINI_API_KEY"),
1053
- primaryModel: read("agents.defaults.model.primary"),
1054
- };
1055
- }
1056
- function maskKey(key) {
1057
- if (key.length <= 8)
1058
- return "•".repeat(key.length);
1059
- return `${key.slice(0, 4)}...${key.slice(-4)}`;
1060
- }
1061
- function readSynapProviderConfig(oc) {
1062
- if (!oc.found || oc.runtime !== "docker")
1063
- return null;
1064
- const containerName = oc.containerName ?? "openclaw";
1065
- try {
1066
- const out = execSync(`docker exec ${containerName} openclaw config get models.providers.synap.baseUrl 2>/dev/null`, { encoding: "utf-8", timeout: 5000 }).trim();
1067
- if (!out || out === "undefined" || out === "null")
1068
- return null;
1069
- return { baseUrl: out.replace(/^["']|["']$/g, "") };
1070
- }
1071
- catch {
1072
- return null;
1073
- }
1074
- }
1075
- function readOpenClawSkills(oc) {
1076
- if (!oc.found)
1077
- return [];
1078
- if (oc.runtime === "docker") {
1079
- const containerName = oc.containerName ?? "openclaw";
1080
- try {
1081
- const out = execSync(`docker exec ${containerName} openclaw skills list 2>/dev/null`, { encoding: "utf-8", timeout: 10000 });
1082
- // OpenClaw's `skills list` output format varies; extract slug-like tokens.
1083
- const names = new Set();
1084
- for (const line of out.split("\n")) {
1085
- const trimmed = line.trim();
1086
- if (!trimmed || trimmed.startsWith("#") || /^(name|slug|version|installed)/i.test(trimmed)) {
1087
- continue;
1088
- }
1089
- // Pick the first slug-looking token on the line
1090
- const match = trimmed.match(/^([a-z0-9][\w-]+)\b/i);
1091
- if (match && match[1].length >= 3) {
1092
- names.add(match[1]);
1093
- }
1094
- }
1095
- return Array.from(names);
1096
- }
1097
- catch {
1098
- return [];
1099
- }
1100
- }
1101
- return [];
1102
- }
1103
- function readOpenClawChannels(oc) {
1104
- if (!oc.found)
1105
- return [];
1106
- if (oc.runtime === "docker") {
1107
- const containerName = oc.containerName ?? "openclaw";
1108
- try {
1109
- const out = execSync(`docker exec ${containerName} openclaw channels list 2>/dev/null`, { encoding: "utf-8", timeout: 10000 });
1110
- const names = new Set();
1111
- for (const line of out.split("\n")) {
1112
- const trimmed = line.trim();
1113
- if (!trimmed)
1114
- continue;
1115
- // Extract any line that mentions a known channel type
1116
- for (const type of ["telegram", "discord", "whatsapp", "slack", "signal", "imessage", "matrix"]) {
1117
- if (new RegExp(`\\b${type}\\b`, "i").test(trimmed)) {
1118
- names.add(type);
1119
- }
1120
- }
1121
- }
1122
- return Array.from(names);
1123
- }
1124
- catch {
1125
- return [];
1126
- }
1127
- }
1128
- return [];
1129
- }
1130
- async function requestDashboardDomainFromCp(cpToken, podId) {
1131
- const cpUrl = process.env.SYNAP_CP_URL ?? "https://api.synap.live";
1132
- const res = await fetch(`${cpUrl}/openclaw/expose-dashboard`, {
1133
- method: "POST",
1134
- headers: {
1135
- "Content-Type": "application/json",
1136
- Authorization: `Bearer ${cpToken}`,
1137
- },
1138
- body: JSON.stringify({ podId }),
1139
- signal: AbortSignal.timeout(20000),
1140
- });
1141
- if (!res.ok) {
1142
- const body = (await res.json().catch(() => null));
1143
- throw new Error(`CP request failed (HTTP ${res.status}): ${body?.error ?? "unknown error"}`);
1144
- }
1145
- const data = (await res.json());
1146
- return { domain: data.domain, authMode: data.authMode };
1147
- }
1148
- //# sourceMappingURL=openclaw.js.map