@aion0/bastion 0.1.7

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 (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -0
  3. package/README.zh.md +468 -0
  4. package/config/default.yaml +73 -0
  5. package/dist/cli/commands/config.d.ts +3 -0
  6. package/dist/cli/commands/config.d.ts.map +1 -0
  7. package/dist/cli/commands/config.js +31 -0
  8. package/dist/cli/commands/config.js.map +1 -0
  9. package/dist/cli/commands/env.d.ts +3 -0
  10. package/dist/cli/commands/env.d.ts.map +1 -0
  11. package/dist/cli/commands/env.js +83 -0
  12. package/dist/cli/commands/env.js.map +1 -0
  13. package/dist/cli/commands/health.d.ts +3 -0
  14. package/dist/cli/commands/health.d.ts.map +1 -0
  15. package/dist/cli/commands/health.js +45 -0
  16. package/dist/cli/commands/health.js.map +1 -0
  17. package/dist/cli/commands/openclaw.d.ts +3 -0
  18. package/dist/cli/commands/openclaw.d.ts.map +1 -0
  19. package/dist/cli/commands/openclaw.js +1062 -0
  20. package/dist/cli/commands/openclaw.js.map +1 -0
  21. package/dist/cli/commands/proxy.d.ts +8 -0
  22. package/dist/cli/commands/proxy.d.ts.map +1 -0
  23. package/dist/cli/commands/proxy.js +433 -0
  24. package/dist/cli/commands/proxy.js.map +1 -0
  25. package/dist/cli/commands/start.d.ts +3 -0
  26. package/dist/cli/commands/start.d.ts.map +1 -0
  27. package/dist/cli/commands/start.js +62 -0
  28. package/dist/cli/commands/start.js.map +1 -0
  29. package/dist/cli/commands/stats.d.ts +3 -0
  30. package/dist/cli/commands/stats.d.ts.map +1 -0
  31. package/dist/cli/commands/stats.js +32 -0
  32. package/dist/cli/commands/stats.js.map +1 -0
  33. package/dist/cli/commands/stop.d.ts +3 -0
  34. package/dist/cli/commands/stop.d.ts.map +1 -0
  35. package/dist/cli/commands/stop.js +28 -0
  36. package/dist/cli/commands/stop.js.map +1 -0
  37. package/dist/cli/commands/token.d.ts +3 -0
  38. package/dist/cli/commands/token.d.ts.map +1 -0
  39. package/dist/cli/commands/token.js +32 -0
  40. package/dist/cli/commands/token.js.map +1 -0
  41. package/dist/cli/commands/trust-ca.d.ts +3 -0
  42. package/dist/cli/commands/trust-ca.d.ts.map +1 -0
  43. package/dist/cli/commands/trust-ca.js +44 -0
  44. package/dist/cli/commands/trust-ca.js.map +1 -0
  45. package/dist/cli/commands/wrap.d.ts +3 -0
  46. package/dist/cli/commands/wrap.d.ts.map +1 -0
  47. package/dist/cli/commands/wrap.js +70 -0
  48. package/dist/cli/commands/wrap.js.map +1 -0
  49. package/dist/cli/daemon.d.ts +11 -0
  50. package/dist/cli/daemon.d.ts.map +1 -0
  51. package/dist/cli/daemon.js +82 -0
  52. package/dist/cli/daemon.js.map +1 -0
  53. package/dist/cli/index.d.ts +3 -0
  54. package/dist/cli/index.d.ts.map +1 -0
  55. package/dist/cli/index.js +35 -0
  56. package/dist/cli/index.js.map +1 -0
  57. package/dist/config/index.d.ts +3 -0
  58. package/dist/config/index.d.ts.map +1 -0
  59. package/dist/config/index.js +60 -0
  60. package/dist/config/index.js.map +1 -0
  61. package/dist/config/manager.d.ts +12 -0
  62. package/dist/config/manager.d.ts.map +1 -0
  63. package/dist/config/manager.js +73 -0
  64. package/dist/config/manager.js.map +1 -0
  65. package/dist/config/paths.d.ts +10 -0
  66. package/dist/config/paths.d.ts.map +1 -0
  67. package/dist/config/paths.js +16 -0
  68. package/dist/config/paths.js.map +1 -0
  69. package/dist/config/schema.d.ts +85 -0
  70. package/dist/config/schema.d.ts.map +1 -0
  71. package/dist/config/schema.js +3 -0
  72. package/dist/config/schema.js.map +1 -0
  73. package/dist/dashboard/api-routes.d.ts +6 -0
  74. package/dist/dashboard/api-routes.d.ts.map +1 -0
  75. package/dist/dashboard/api-routes.js +671 -0
  76. package/dist/dashboard/api-routes.js.map +1 -0
  77. package/dist/dashboard/api.d.ts +4 -0
  78. package/dist/dashboard/api.d.ts.map +1 -0
  79. package/dist/dashboard/api.js +25 -0
  80. package/dist/dashboard/api.js.map +1 -0
  81. package/dist/dashboard/page.d.ts +3 -0
  82. package/dist/dashboard/page.d.ts.map +1 -0
  83. package/dist/dashboard/page.js +1622 -0
  84. package/dist/dashboard/page.js.map +1 -0
  85. package/dist/dlp/actions.d.ts +13 -0
  86. package/dist/dlp/actions.d.ts.map +1 -0
  87. package/dist/dlp/actions.js +3 -0
  88. package/dist/dlp/actions.js.map +1 -0
  89. package/dist/dlp/ai-validator.d.ts +28 -0
  90. package/dist/dlp/ai-validator.d.ts.map +1 -0
  91. package/dist/dlp/ai-validator.js +214 -0
  92. package/dist/dlp/ai-validator.js.map +1 -0
  93. package/dist/dlp/engine.d.ts +34 -0
  94. package/dist/dlp/engine.d.ts.map +1 -0
  95. package/dist/dlp/engine.js +342 -0
  96. package/dist/dlp/engine.js.map +1 -0
  97. package/dist/dlp/entropy.d.ts +22 -0
  98. package/dist/dlp/entropy.d.ts.map +1 -0
  99. package/dist/dlp/entropy.js +43 -0
  100. package/dist/dlp/entropy.js.map +1 -0
  101. package/dist/dlp/message-cache.d.ts +45 -0
  102. package/dist/dlp/message-cache.d.ts.map +1 -0
  103. package/dist/dlp/message-cache.js +251 -0
  104. package/dist/dlp/message-cache.js.map +1 -0
  105. package/dist/dlp/patterns/context-aware.d.ts +4 -0
  106. package/dist/dlp/patterns/context-aware.d.ts.map +1 -0
  107. package/dist/dlp/patterns/context-aware.js +45 -0
  108. package/dist/dlp/patterns/context-aware.js.map +1 -0
  109. package/dist/dlp/patterns/high-confidence.d.ts +4 -0
  110. package/dist/dlp/patterns/high-confidence.d.ts.map +1 -0
  111. package/dist/dlp/patterns/high-confidence.js +140 -0
  112. package/dist/dlp/patterns/high-confidence.js.map +1 -0
  113. package/dist/dlp/patterns/prompt-injection.d.ts +4 -0
  114. package/dist/dlp/patterns/prompt-injection.d.ts.map +1 -0
  115. package/dist/dlp/patterns/prompt-injection.js +244 -0
  116. package/dist/dlp/patterns/prompt-injection.js.map +1 -0
  117. package/dist/dlp/patterns/validated.d.ts +4 -0
  118. package/dist/dlp/patterns/validated.d.ts.map +1 -0
  119. package/dist/dlp/patterns/validated.js +21 -0
  120. package/dist/dlp/patterns/validated.js.map +1 -0
  121. package/dist/dlp/remote-sync.d.ts +47 -0
  122. package/dist/dlp/remote-sync.d.ts.map +1 -0
  123. package/dist/dlp/remote-sync.js +252 -0
  124. package/dist/dlp/remote-sync.js.map +1 -0
  125. package/dist/dlp/semantics.d.ts +27 -0
  126. package/dist/dlp/semantics.d.ts.map +1 -0
  127. package/dist/dlp/semantics.js +93 -0
  128. package/dist/dlp/semantics.js.map +1 -0
  129. package/dist/dlp/structure.d.ts +25 -0
  130. package/dist/dlp/structure.d.ts.map +1 -0
  131. package/dist/dlp/structure.js +86 -0
  132. package/dist/dlp/structure.js.map +1 -0
  133. package/dist/dlp/validators.d.ts +6 -0
  134. package/dist/dlp/validators.d.ts.map +1 -0
  135. package/dist/dlp/validators.js +46 -0
  136. package/dist/dlp/validators.js.map +1 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +200 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/license/verify.d.ts +18 -0
  142. package/dist/license/verify.d.ts.map +1 -0
  143. package/dist/license/verify.js +71 -0
  144. package/dist/license/verify.js.map +1 -0
  145. package/dist/metrics/collector.d.ts +11 -0
  146. package/dist/metrics/collector.d.ts.map +1 -0
  147. package/dist/metrics/collector.js +17 -0
  148. package/dist/metrics/collector.js.map +1 -0
  149. package/dist/metrics/dashboard.d.ts +6 -0
  150. package/dist/metrics/dashboard.d.ts.map +1 -0
  151. package/dist/metrics/dashboard.js +66 -0
  152. package/dist/metrics/dashboard.js.map +1 -0
  153. package/dist/metrics/pricing.d.ts +10 -0
  154. package/dist/metrics/pricing.d.ts.map +1 -0
  155. package/dist/metrics/pricing.js +62 -0
  156. package/dist/metrics/pricing.js.map +1 -0
  157. package/dist/optimizer/cache.d.ts +14 -0
  158. package/dist/optimizer/cache.d.ts.map +1 -0
  159. package/dist/optimizer/cache.js +58 -0
  160. package/dist/optimizer/cache.js.map +1 -0
  161. package/dist/optimizer/estimator.d.ts +6 -0
  162. package/dist/optimizer/estimator.d.ts.map +1 -0
  163. package/dist/optimizer/estimator.js +12 -0
  164. package/dist/optimizer/estimator.js.map +1 -0
  165. package/dist/optimizer/reorder.d.ts +9 -0
  166. package/dist/optimizer/reorder.d.ts.map +1 -0
  167. package/dist/optimizer/reorder.js +27 -0
  168. package/dist/optimizer/reorder.js.map +1 -0
  169. package/dist/optimizer/trimmer.d.ts +9 -0
  170. package/dist/optimizer/trimmer.d.ts.map +1 -0
  171. package/dist/optimizer/trimmer.js +47 -0
  172. package/dist/optimizer/trimmer.js.map +1 -0
  173. package/dist/plugin-api/index.d.ts +3 -0
  174. package/dist/plugin-api/index.d.ts.map +1 -0
  175. package/dist/plugin-api/index.js +6 -0
  176. package/dist/plugin-api/index.js.map +1 -0
  177. package/dist/plugin-api/types.d.ts +77 -0
  178. package/dist/plugin-api/types.d.ts.map +1 -0
  179. package/dist/plugin-api/types.js +6 -0
  180. package/dist/plugin-api/types.js.map +1 -0
  181. package/dist/plugins/adapter.d.ts +12 -0
  182. package/dist/plugins/adapter.d.ts.map +1 -0
  183. package/dist/plugins/adapter.js +116 -0
  184. package/dist/plugins/adapter.js.map +1 -0
  185. package/dist/plugins/builtin/audit-logger.d.ts +9 -0
  186. package/dist/plugins/builtin/audit-logger.d.ts.map +1 -0
  187. package/dist/plugins/builtin/audit-logger.js +53 -0
  188. package/dist/plugins/builtin/audit-logger.js.map +1 -0
  189. package/dist/plugins/builtin/dlp-scanner.d.ts +19 -0
  190. package/dist/plugins/builtin/dlp-scanner.d.ts.map +1 -0
  191. package/dist/plugins/builtin/dlp-scanner.js +284 -0
  192. package/dist/plugins/builtin/dlp-scanner.js.map +1 -0
  193. package/dist/plugins/builtin/metrics-collector.d.ts +4 -0
  194. package/dist/plugins/builtin/metrics-collector.d.ts.map +1 -0
  195. package/dist/plugins/builtin/metrics-collector.js +111 -0
  196. package/dist/plugins/builtin/metrics-collector.js.map +1 -0
  197. package/dist/plugins/builtin/token-optimizer.d.ts +10 -0
  198. package/dist/plugins/builtin/token-optimizer.d.ts.map +1 -0
  199. package/dist/plugins/builtin/token-optimizer.js +120 -0
  200. package/dist/plugins/builtin/token-optimizer.js.map +1 -0
  201. package/dist/plugins/builtin/tool-guard.d.ts +20 -0
  202. package/dist/plugins/builtin/tool-guard.d.ts.map +1 -0
  203. package/dist/plugins/builtin/tool-guard.js +259 -0
  204. package/dist/plugins/builtin/tool-guard.js.map +1 -0
  205. package/dist/plugins/context.d.ts +8 -0
  206. package/dist/plugins/context.d.ts.map +1 -0
  207. package/dist/plugins/context.js +33 -0
  208. package/dist/plugins/context.js.map +1 -0
  209. package/dist/plugins/event-bus.d.ts +9 -0
  210. package/dist/plugins/event-bus.d.ts.map +1 -0
  211. package/dist/plugins/event-bus.js +25 -0
  212. package/dist/plugins/event-bus.js.map +1 -0
  213. package/dist/plugins/index.d.ts +18 -0
  214. package/dist/plugins/index.d.ts.map +1 -0
  215. package/dist/plugins/index.js +148 -0
  216. package/dist/plugins/index.js.map +1 -0
  217. package/dist/plugins/loader.d.ts +14 -0
  218. package/dist/plugins/loader.d.ts.map +1 -0
  219. package/dist/plugins/loader.js +98 -0
  220. package/dist/plugins/loader.js.map +1 -0
  221. package/dist/plugins/types.d.ts +91 -0
  222. package/dist/plugins/types.d.ts.map +1 -0
  223. package/dist/plugins/types.js +3 -0
  224. package/dist/plugins/types.js.map +1 -0
  225. package/dist/proxy/certs.d.ts +10 -0
  226. package/dist/proxy/certs.d.ts.map +1 -0
  227. package/dist/proxy/certs.js +110 -0
  228. package/dist/proxy/certs.js.map +1 -0
  229. package/dist/proxy/connect.d.ts +11 -0
  230. package/dist/proxy/connect.d.ts.map +1 -0
  231. package/dist/proxy/connect.js +298 -0
  232. package/dist/proxy/connect.js.map +1 -0
  233. package/dist/proxy/forwarder.d.ts +14 -0
  234. package/dist/proxy/forwarder.d.ts.map +1 -0
  235. package/dist/proxy/forwarder.js +342 -0
  236. package/dist/proxy/forwarder.js.map +1 -0
  237. package/dist/proxy/passthrough.d.ts +4 -0
  238. package/dist/proxy/passthrough.d.ts.map +1 -0
  239. package/dist/proxy/passthrough.js +68 -0
  240. package/dist/proxy/passthrough.js.map +1 -0
  241. package/dist/proxy/providers/anthropic.d.ts +4 -0
  242. package/dist/proxy/providers/anthropic.d.ts.map +1 -0
  243. package/dist/proxy/providers/anthropic.js +46 -0
  244. package/dist/proxy/providers/anthropic.js.map +1 -0
  245. package/dist/proxy/providers/classify.d.ts +14 -0
  246. package/dist/proxy/providers/classify.d.ts.map +1 -0
  247. package/dist/proxy/providers/classify.js +37 -0
  248. package/dist/proxy/providers/classify.js.map +1 -0
  249. package/dist/proxy/providers/claude-web.d.ts +8 -0
  250. package/dist/proxy/providers/claude-web.d.ts.map +1 -0
  251. package/dist/proxy/providers/claude-web.js +50 -0
  252. package/dist/proxy/providers/claude-web.js.map +1 -0
  253. package/dist/proxy/providers/gemini.d.ts +4 -0
  254. package/dist/proxy/providers/gemini.d.ts.map +1 -0
  255. package/dist/proxy/providers/gemini.js +38 -0
  256. package/dist/proxy/providers/gemini.js.map +1 -0
  257. package/dist/proxy/providers/index.d.ts +27 -0
  258. package/dist/proxy/providers/index.d.ts.map +1 -0
  259. package/dist/proxy/providers/index.js +32 -0
  260. package/dist/proxy/providers/index.js.map +1 -0
  261. package/dist/proxy/providers/messaging.d.ts +2 -0
  262. package/dist/proxy/providers/messaging.d.ts.map +1 -0
  263. package/dist/proxy/providers/messaging.js +53 -0
  264. package/dist/proxy/providers/messaging.js.map +1 -0
  265. package/dist/proxy/providers/openai.d.ts +4 -0
  266. package/dist/proxy/providers/openai.d.ts.map +1 -0
  267. package/dist/proxy/providers/openai.js +38 -0
  268. package/dist/proxy/providers/openai.js.map +1 -0
  269. package/dist/proxy/providers/telegram.d.ts +8 -0
  270. package/dist/proxy/providers/telegram.d.ts.map +1 -0
  271. package/dist/proxy/providers/telegram.js +35 -0
  272. package/dist/proxy/providers/telegram.js.map +1 -0
  273. package/dist/proxy/router.d.ts +12 -0
  274. package/dist/proxy/router.d.ts.map +1 -0
  275. package/dist/proxy/router.js +26 -0
  276. package/dist/proxy/router.js.map +1 -0
  277. package/dist/proxy/safety.d.ts +13 -0
  278. package/dist/proxy/safety.d.ts.map +1 -0
  279. package/dist/proxy/safety.js +58 -0
  280. package/dist/proxy/safety.js.map +1 -0
  281. package/dist/proxy/server.d.ts +8 -0
  282. package/dist/proxy/server.d.ts.map +1 -0
  283. package/dist/proxy/server.js +126 -0
  284. package/dist/proxy/server.js.map +1 -0
  285. package/dist/proxy/streaming.d.ts +21 -0
  286. package/dist/proxy/streaming.d.ts.map +1 -0
  287. package/dist/proxy/streaming.js +70 -0
  288. package/dist/proxy/streaming.js.map +1 -0
  289. package/dist/storage/database.d.ts +6 -0
  290. package/dist/storage/database.d.ts.map +1 -0
  291. package/dist/storage/database.js +44 -0
  292. package/dist/storage/database.js.map +1 -0
  293. package/dist/storage/encryption.d.ts +11 -0
  294. package/dist/storage/encryption.d.ts.map +1 -0
  295. package/dist/storage/encryption.js +47 -0
  296. package/dist/storage/encryption.js.map +1 -0
  297. package/dist/storage/migrations.d.ts +3 -0
  298. package/dist/storage/migrations.d.ts.map +1 -0
  299. package/dist/storage/migrations.js +265 -0
  300. package/dist/storage/migrations.js.map +1 -0
  301. package/dist/storage/repositories/audit-log.d.ts +115 -0
  302. package/dist/storage/repositories/audit-log.d.ts.map +1 -0
  303. package/dist/storage/repositories/audit-log.js +586 -0
  304. package/dist/storage/repositories/audit-log.js.map +1 -0
  305. package/dist/storage/repositories/cache.d.ts +26 -0
  306. package/dist/storage/repositories/cache.d.ts.map +1 -0
  307. package/dist/storage/repositories/cache.js +44 -0
  308. package/dist/storage/repositories/cache.js.map +1 -0
  309. package/dist/storage/repositories/dlp-config-history.d.ts +17 -0
  310. package/dist/storage/repositories/dlp-config-history.d.ts.map +1 -0
  311. package/dist/storage/repositories/dlp-config-history.js +30 -0
  312. package/dist/storage/repositories/dlp-config-history.js.map +1 -0
  313. package/dist/storage/repositories/dlp-events.d.ts +35 -0
  314. package/dist/storage/repositories/dlp-events.d.ts.map +1 -0
  315. package/dist/storage/repositories/dlp-events.js +57 -0
  316. package/dist/storage/repositories/dlp-events.js.map +1 -0
  317. package/dist/storage/repositories/dlp-patterns.d.ts +70 -0
  318. package/dist/storage/repositories/dlp-patterns.d.ts.map +1 -0
  319. package/dist/storage/repositories/dlp-patterns.js +187 -0
  320. package/dist/storage/repositories/dlp-patterns.js.map +1 -0
  321. package/dist/storage/repositories/optimizer-events.d.ts +28 -0
  322. package/dist/storage/repositories/optimizer-events.d.ts.map +1 -0
  323. package/dist/storage/repositories/optimizer-events.js +49 -0
  324. package/dist/storage/repositories/optimizer-events.js.map +1 -0
  325. package/dist/storage/repositories/plugin-events.d.ts +34 -0
  326. package/dist/storage/repositories/plugin-events.d.ts.map +1 -0
  327. package/dist/storage/repositories/plugin-events.js +64 -0
  328. package/dist/storage/repositories/plugin-events.js.map +1 -0
  329. package/dist/storage/repositories/requests.d.ts +68 -0
  330. package/dist/storage/repositories/requests.d.ts.map +1 -0
  331. package/dist/storage/repositories/requests.js +113 -0
  332. package/dist/storage/repositories/requests.js.map +1 -0
  333. package/dist/storage/repositories/sessions.d.ts +23 -0
  334. package/dist/storage/repositories/sessions.d.ts.map +1 -0
  335. package/dist/storage/repositories/sessions.js +42 -0
  336. package/dist/storage/repositories/sessions.js.map +1 -0
  337. package/dist/storage/repositories/tool-calls.d.ts +49 -0
  338. package/dist/storage/repositories/tool-calls.d.ts.map +1 -0
  339. package/dist/storage/repositories/tool-calls.js +61 -0
  340. package/dist/storage/repositories/tool-calls.js.map +1 -0
  341. package/dist/storage/repositories/tool-guard-rules.d.ts +50 -0
  342. package/dist/storage/repositories/tool-guard-rules.d.ts.map +1 -0
  343. package/dist/storage/repositories/tool-guard-rules.js +120 -0
  344. package/dist/storage/repositories/tool-guard-rules.js.map +1 -0
  345. package/dist/tool-guard/alert.d.ts +30 -0
  346. package/dist/tool-guard/alert.d.ts.map +1 -0
  347. package/dist/tool-guard/alert.js +113 -0
  348. package/dist/tool-guard/alert.js.map +1 -0
  349. package/dist/tool-guard/extractor.d.ts +10 -0
  350. package/dist/tool-guard/extractor.d.ts.map +1 -0
  351. package/dist/tool-guard/extractor.js +309 -0
  352. package/dist/tool-guard/extractor.js.map +1 -0
  353. package/dist/tool-guard/rules.d.ts +18 -0
  354. package/dist/tool-guard/rules.d.ts.map +1 -0
  355. package/dist/tool-guard/rules.js +255 -0
  356. package/dist/tool-guard/rules.js.map +1 -0
  357. package/dist/tool-guard/streaming-guard.d.ts +57 -0
  358. package/dist/tool-guard/streaming-guard.d.ts.map +1 -0
  359. package/dist/tool-guard/streaming-guard.js +389 -0
  360. package/dist/tool-guard/streaming-guard.js.map +1 -0
  361. package/dist/utils/hash.d.ts +2 -0
  362. package/dist/utils/hash.d.ts.map +1 -0
  363. package/dist/utils/hash.js +8 -0
  364. package/dist/utils/hash.js.map +1 -0
  365. package/dist/utils/logger.d.ts +11 -0
  366. package/dist/utils/logger.d.ts.map +1 -0
  367. package/dist/utils/logger.js +54 -0
  368. package/dist/utils/logger.js.map +1 -0
  369. package/dist/utils/timeout.d.ts +5 -0
  370. package/dist/utils/timeout.d.ts.map +1 -0
  371. package/dist/utils/timeout.js +26 -0
  372. package/dist/utils/timeout.js.map +1 -0
  373. package/dist/version.d.ts +5 -0
  374. package/dist/version.d.ts.map +1 -0
  375. package/dist/version.js +23 -0
  376. package/dist/version.js.map +1 -0
  377. package/package.json +67 -0
@@ -0,0 +1,1062 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerOpenclawCommand = registerOpenclawCommand;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const node_os_1 = require("node:os");
9
+ const IS_WIN = (0, node_os_1.platform)() === 'win32';
10
+ const index_js_1 = require("../../config/index.js");
11
+ const certs_js_1 = require("../../proxy/certs.js");
12
+ const daemon_js_1 = require("../daemon.js");
13
+ const paths_js_1 = require("../../config/paths.js");
14
+ const OPENCLAW_DIR = (0, node_path_1.join)(paths_js_1.paths.bastionDir, 'openclaw');
15
+ const DEFAULT_PORT = 18789;
16
+ const DEFAULT_IMAGE = 'openclaw:local';
17
+ /** Brew shim for Docker — maps `brew install` to `apt-get install` so OpenClaw skills install correctly */
18
+ const BREW_SHIM_CONTENT = `#!/bin/sh
19
+ # brew shim for Debian/Ubuntu Docker containers
20
+ # Maps common brew commands to apt-get equivalents
21
+
22
+ # Package name mapping (brew name -> apt name)
23
+ map_pkg() {
24
+ case "$1" in
25
+ ripgrep) echo "ripgrep" ;;
26
+ fd) echo "fd-find" ;;
27
+ bat) echo "bat" ;;
28
+ fzf) echo "fzf" ;;
29
+ jq) echo "jq" ;;
30
+ yq) echo "yq" ;;
31
+ tree) echo "tree" ;;
32
+ wget) echo "wget" ;;
33
+ curl) echo "curl" ;;
34
+ git) echo "git" ;;
35
+ gh) echo "gh" ;;
36
+ sqlite) echo "sqlite3" ;;
37
+ python3|python) echo "python3" ;;
38
+ *) echo "$1" ;;
39
+ esac
40
+ }
41
+
42
+ case "$1" in
43
+ install)
44
+ shift
45
+ PKGS=""
46
+ for pkg in "$@"; do
47
+ case "$pkg" in -*) continue ;; esac
48
+ PKGS="$PKGS $(map_pkg "$pkg")"
49
+ done
50
+ if [ -n "$PKGS" ]; then
51
+ apt-get update -qq 2>/dev/null
52
+ apt-get install -y -qq $PKGS
53
+ fi
54
+ ;;
55
+ uninstall|remove)
56
+ shift
57
+ PKGS=""
58
+ for pkg in "$@"; do
59
+ case "$pkg" in -*) continue ;; esac
60
+ PKGS="$PKGS $(map_pkg "$pkg")"
61
+ done
62
+ if [ -n "$PKGS" ]; then
63
+ apt-get remove -y -qq $PKGS
64
+ fi
65
+ ;;
66
+ list)
67
+ dpkg -l 2>/dev/null | tail -n +6 | awk '{print $2}'
68
+ ;;
69
+ --version|-v)
70
+ echo "brew-shim 1.0 (apt-get wrapper for Docker)"
71
+ ;;
72
+ *)
73
+ echo "brew-shim: unsupported command '$1' (only install/uninstall/list supported)" >&2
74
+ exit 1
75
+ ;;
76
+ esac
77
+ `;
78
+ /** Proxy bootstrap script content — mounted into containers / local via NODE_OPTIONS="--import ..." */
79
+ const PROXY_BOOTSTRAP_CONTENT = `// proxy-bootstrap.mjs — forces all Node.js HTTP/HTTPS through the proxy
80
+ // Uses createRequire for writable CJS refs (ESM namespaces are read-only). No undici dependency.
81
+ // Set BASTION_PROXY_DEBUG=1 for diagnostic output.
82
+ import { createRequire } from 'node:module';
83
+ const require = createRequire(import.meta.url);
84
+ const http = require('node:http');
85
+ const https = require('node:https');
86
+ const tls = require('node:tls');
87
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || process.env.https_proxy || process.env.http_proxy;
88
+ const D = !!process.env.BASTION_PROXY_DEBUG, L = (...a) => D && console.error('[proxy-bootstrap]', ...a);
89
+ if (proxyUrl) {
90
+ L('proxy:', proxyUrl);
91
+ const noProxyList = (process.env.NO_PROXY || process.env.no_proxy || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
92
+ L('no_proxy:', noProxyList.join(', '));
93
+ const shouldBypass = (h) => { h = (h || '').toLowerCase(); return noProxyList.some(np => h === np || h.endsWith('.' + np)); };
94
+ const proxy = new URL(proxyUrl), pH = proxy.hostname, pP = parseInt(proxy.port, 10) || 80;
95
+ try {
96
+ const _cc = https.Agent.prototype.createConnection;
97
+ class T extends https.Agent {
98
+ createConnection(o, cb) {
99
+ const h = o.hostname || o.host || o.servername, p = o.port || 443;
100
+ if (!h || shouldBypass(h)) return _cc.call(this, o, cb);
101
+ L('tunnel:', h + ':' + p);
102
+ const r = http.request({ hostname: pH, port: pP, method: 'CONNECT', path: h+':'+p, headers: { Host: h+':'+p } });
103
+ r.on('connect', (res, sock) => { if (res.statusCode !== 200) { sock.destroy(); cb?.(new Error('CONNECT '+h+':'+p+' failed: '+res.statusCode)); return; } cb?.(null, tls.connect({ socket: sock, servername: h })); });
104
+ r.on('error', e => cb?.(e)); r.end();
105
+ }
106
+ }
107
+ https.globalAgent = new T({ keepAlive: true });
108
+ L('https.globalAgent patched');
109
+ } catch (e) { L('WARN: https.globalAgent patch failed:', e.message); }
110
+ const _origFetch = globalThis.fetch;
111
+ if (typeof _origFetch === 'function') {
112
+ globalThis.fetch = async function(input, init) {
113
+ let url;
114
+ try { if (typeof input === 'string') url = new URL(input); else if (input instanceof URL) url = new URL(input.href); else if (input instanceof Request) url = new URL(input.url); } catch {}
115
+ if (!url || url.protocol !== 'https:' || shouldBypass(url.hostname)) return _origFetch.call(globalThis, input, init);
116
+ L('fetch proxy:', url.hostname + url.pathname);
117
+ // Use Request to normalize headers and body (handles string, URLSearchParams, Blob, FormData, ArrayBuffer, ReadableStream, etc.)
118
+ const normalized = new Request(input, init);
119
+ const method = normalized.method;
120
+ const hdrs = {}; for (const [k, v] of normalized.headers) hdrs[k] = v;
121
+ const bodyBuf = normalized.body ? Buffer.from(await normalized.arrayBuffer()) : null;
122
+ return new Promise((resolve, reject) => {
123
+ const req = https.request({ hostname: url.hostname, port: url.port || 443, path: url.pathname + url.search, method, headers: hdrs }, (res) => {
124
+ const stream = new ReadableStream({ start(ctrl) { res.on('data', c => ctrl.enqueue(new Uint8Array(c))); res.on('end', () => ctrl.close()); res.on('error', e => ctrl.error(e)); } });
125
+ const rh = new Headers(); for (const [k, v] of Object.entries(res.headers)) { if (v == null) continue; if (Array.isArray(v)) v.forEach(x => rh.append(k, x)); else rh.set(k, v); }
126
+ resolve(new Response(stream, { status: res.statusCode, statusText: res.statusMessage, headers: rh }));
127
+ });
128
+ if (init?.signal) { if (init.signal.aborted) { req.destroy(); reject(new DOMException('The operation was aborted.', 'AbortError')); return; } init.signal.addEventListener('abort', () => { req.destroy(); reject(new DOMException('The operation was aborted.', 'AbortError')); }); }
129
+ req.on('error', reject);
130
+ if (bodyBuf) { req.end(bodyBuf); } else { req.end(); }
131
+ });
132
+ };
133
+ L('fetch wrapped');
134
+ }
135
+ } else { L('no proxy URL found, skipping'); }
136
+ `;
137
+ // ── shared helpers ───────────────────────────────────────────────────────────
138
+ function generateToken() {
139
+ return (0, node_crypto_1.randomBytes)(32).toString('hex');
140
+ }
141
+ function checkBastion() {
142
+ const status = (0, daemon_js_1.getDaemonStatus)();
143
+ if (!status.running) {
144
+ console.error('Bastion is not running. Start it first: bastion start');
145
+ process.exit(1);
146
+ }
147
+ }
148
+ function checkDocker() {
149
+ try {
150
+ (0, node_child_process_1.execSync)('docker info', { stdio: 'pipe' });
151
+ }
152
+ catch {
153
+ console.error('Docker is not available. Please install and start Docker.');
154
+ process.exit(1);
155
+ }
156
+ }
157
+ function checkCACert() {
158
+ const caPath = (0, certs_js_1.getCACertPath)();
159
+ if (!(0, node_fs_1.existsSync)(caPath)) {
160
+ console.error('Bastion CA cert not found. Run bastion start first to generate it.');
161
+ process.exit(1);
162
+ }
163
+ return caPath;
164
+ }
165
+ function getBastionPort() {
166
+ const config = (0, index_js_1.loadConfig)();
167
+ return config.server.port;
168
+ }
169
+ function getBastionHost() {
170
+ const config = (0, index_js_1.loadConfig)();
171
+ return config.server.host;
172
+ }
173
+ function sleep(ms) {
174
+ return new Promise((resolve) => setTimeout(resolve, ms));
175
+ }
176
+ // ── docker helpers ───────────────────────────────────────────────────────────
177
+ function instanceDir(name) {
178
+ return (0, node_path_1.join)(OPENCLAW_DIR, 'docker', name);
179
+ }
180
+ function envVal(name, key) {
181
+ const envFile = (0, node_path_1.join)(instanceDir(name), '.env');
182
+ if (!(0, node_fs_1.existsSync)(envFile))
183
+ return '';
184
+ const content = (0, node_fs_1.readFileSync)(envFile, 'utf-8');
185
+ const match = content.match(new RegExp(`^${key}=(.*)$`, 'm'));
186
+ return match?.[1] ?? '';
187
+ }
188
+ function writeEnvFile(dir, vars) {
189
+ const content = Object.entries(vars)
190
+ .map(([k, v]) => `${k}=${v}`)
191
+ .join('\n') + '\n';
192
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, '.env'), content, 'utf-8');
193
+ }
194
+ function generateComposeFile(image) {
195
+ return `services:
196
+ openclaw-gateway:
197
+ image: \${OPENCLAW_IMAGE:-${image}}
198
+ environment:
199
+ HOME: /home/node
200
+ TERM: xterm-256color
201
+ OPENCLAW_GATEWAY_TOKEN: \${OPENCLAW_GATEWAY_TOKEN}
202
+ CLAUDE_AI_SESSION_KEY: \${CLAUDE_AI_SESSION_KEY}
203
+ CLAUDE_WEB_SESSION_KEY: \${CLAUDE_WEB_SESSION_KEY}
204
+ CLAUDE_WEB_COOKIE: \${CLAUDE_WEB_COOKIE}
205
+ volumes:
206
+ - \${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
207
+ - \${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
208
+ ports:
209
+ - "\${OPENCLAW_GATEWAY_PORT:-18789}:18789"
210
+ - "\${OPENCLAW_BRIDGE_PORT:-18790}:18790"
211
+ init: true
212
+ restart: unless-stopped
213
+ command:
214
+ [
215
+ "node",
216
+ "dist/index.js",
217
+ "gateway",
218
+ "--bind",
219
+ "\${OPENCLAW_GATEWAY_BIND:-lan}",
220
+ "--port",
221
+ "18789",
222
+ ]
223
+
224
+ openclaw-cli:
225
+ image: \${OPENCLAW_IMAGE:-${image}}
226
+ environment:
227
+ HOME: /home/node
228
+ TERM: xterm-256color
229
+ OPENCLAW_GATEWAY_TOKEN: \${OPENCLAW_GATEWAY_TOKEN}
230
+ BROWSER: echo
231
+ CLAUDE_AI_SESSION_KEY: \${CLAUDE_AI_SESSION_KEY}
232
+ CLAUDE_WEB_SESSION_KEY: \${CLAUDE_WEB_SESSION_KEY}
233
+ CLAUDE_WEB_COOKIE: \${CLAUDE_WEB_COOKIE}
234
+ volumes:
235
+ - \${OPENCLAW_CONFIG_DIR}:/home/node/.openclaw
236
+ - \${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace
237
+ stdin_open: true
238
+ tty: true
239
+ init: true
240
+ entrypoint: ["node", "dist/index.js"]
241
+ `;
242
+ }
243
+ /** Bastion proxy overlay — merged via docker compose -f ... -f ... */
244
+ function generateBastionOverride(caPath) {
245
+ return `services:
246
+ openclaw-gateway:
247
+ user: root
248
+ environment:
249
+ HOME: /home/node
250
+ HTTPS_PROXY: "http://openclaw-gw@host.docker.internal:\${BASTION_PORT:-8420}"
251
+ NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/bastion-ca.crt"
252
+ NO_PROXY: "localhost,127.0.0.1,host.docker.internal,auth.openai.com"
253
+ NODE_OPTIONS: "--import /opt/bastion/proxy-bootstrap.mjs"
254
+ volumes:
255
+ - ${caPath}:/etc/ssl/certs/bastion-ca.crt:ro
256
+ - ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro
257
+ - ./brew:/usr/local/bin/brew:ro
258
+
259
+ openclaw-cli:
260
+ user: root
261
+ environment:
262
+ HOME: /home/node
263
+ HTTPS_PROXY: "http://openclaw-cli@host.docker.internal:\${BASTION_PORT:-8420}"
264
+ NODE_EXTRA_CA_CERTS: "/etc/ssl/certs/bastion-ca.crt"
265
+ NO_PROXY: "localhost,127.0.0.1,host.docker.internal,auth.openai.com"
266
+ NODE_OPTIONS: "--import /opt/bastion/proxy-bootstrap.mjs"
267
+ volumes:
268
+ - ${caPath}:/etc/ssl/certs/bastion-ca.crt:ro
269
+ - ./proxy-bootstrap.mjs:/opt/bastion/proxy-bootstrap.mjs:ro
270
+ - ./brew:/usr/local/bin/brew:ro
271
+ `;
272
+ }
273
+ /** Build the list of compose file args for an instance. */
274
+ function composeFileArgs(name) {
275
+ const dir = instanceDir(name);
276
+ const args = ['-f', (0, node_path_1.join)(dir, 'docker-compose.yml')];
277
+ const overrideFile = (0, node_path_1.join)(dir, 'docker-compose.bastion.yml');
278
+ if ((0, node_fs_1.existsSync)(overrideFile)) {
279
+ args.push('-f', overrideFile);
280
+ }
281
+ return args;
282
+ }
283
+ /** Run docker compose scoped to an instance, inheriting stdio. Returns exit code. */
284
+ function dc(name, args) {
285
+ const dir = instanceDir(name);
286
+ const envFile = (0, node_path_1.join)(dir, '.env');
287
+ return new Promise((resolve) => {
288
+ const child = (0, node_child_process_1.spawn)('docker', [
289
+ 'compose',
290
+ ...composeFileArgs(name),
291
+ '--env-file', envFile,
292
+ '-p', `openclaw-${name}`,
293
+ ...args,
294
+ ], { stdio: 'inherit' });
295
+ child.on('close', (code) => resolve(code ?? 1));
296
+ child.on('error', (err) => {
297
+ console.error(`docker compose error: ${err.message}`);
298
+ resolve(1);
299
+ });
300
+ });
301
+ }
302
+ /** Run docker compose and capture stdout. */
303
+ function dcOutput(name, args) {
304
+ const dir = instanceDir(name);
305
+ const envFile = (0, node_path_1.join)(dir, '.env');
306
+ try {
307
+ return (0, node_child_process_1.execSync)([
308
+ 'docker', 'compose',
309
+ ...composeFileArgs(name),
310
+ '--env-file', envFile,
311
+ '-p', `openclaw-${name}`,
312
+ ...args,
313
+ ].map(a => `"${a}"`).join(' '), { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
314
+ }
315
+ catch {
316
+ return '';
317
+ }
318
+ }
319
+ /** Sync .env token with the token in openclaw.json (onboard may change it) */
320
+ function syncToken(name) {
321
+ const configDir = envVal(name, 'OPENCLAW_CONFIG_DIR');
322
+ const configFile = (0, node_path_1.join)(configDir, 'openclaw.json');
323
+ if (!(0, node_fs_1.existsSync)(configFile))
324
+ return;
325
+ try {
326
+ const cfg = JSON.parse((0, node_fs_1.readFileSync)(configFile, 'utf-8'));
327
+ const configToken = cfg?.gateway?.auth?.token;
328
+ if (!configToken)
329
+ return;
330
+ const envToken = envVal(name, 'OPENCLAW_GATEWAY_TOKEN');
331
+ if (configToken !== envToken) {
332
+ const envFile = (0, node_path_1.join)(instanceDir(name), '.env');
333
+ let content = (0, node_fs_1.readFileSync)(envFile, 'utf-8');
334
+ content = content.replace(/^OPENCLAW_GATEWAY_TOKEN=.*$/m, `OPENCLAW_GATEWAY_TOKEN=${configToken}`);
335
+ (0, node_fs_1.writeFileSync)(envFile, content, 'utf-8');
336
+ console.log(' (synced .env token with onboard config)');
337
+ }
338
+ }
339
+ catch {
340
+ // best-effort
341
+ }
342
+ }
343
+ /** Ensure gateway config is correct for Docker networking */
344
+ function fixBind(name) {
345
+ const configDir = envVal(name, 'OPENCLAW_CONFIG_DIR');
346
+ const configFile = (0, node_path_1.join)(configDir, 'openclaw.json');
347
+ if (!(0, node_fs_1.existsSync)(configFile))
348
+ return;
349
+ try {
350
+ const cfg = JSON.parse((0, node_fs_1.readFileSync)(configFile, 'utf-8'));
351
+ let changed = false;
352
+ if (!cfg.gateway)
353
+ cfg.gateway = {};
354
+ // bind=lan required for Docker container networking
355
+ if (cfg.gateway.bind !== 'lan') {
356
+ cfg.gateway.bind = 'lan';
357
+ changed = true;
358
+ }
359
+ // non-loopback bind requires controlUi origin config
360
+ if (!cfg.gateway.controlUi)
361
+ cfg.gateway.controlUi = {};
362
+ if (!cfg.gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback) {
363
+ cfg.gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback = true;
364
+ changed = true;
365
+ }
366
+ if (changed) {
367
+ (0, node_fs_1.writeFileSync)(configFile, JSON.stringify(cfg, null, 2), 'utf-8');
368
+ console.log(' (fixed gateway config for Docker networking)');
369
+ }
370
+ }
371
+ catch {
372
+ // best-effort
373
+ }
374
+ }
375
+ /** Approve all pending device pairing requests */
376
+ function approveDevices(name) {
377
+ const configDir = envVal(name, 'OPENCLAW_CONFIG_DIR');
378
+ const pendingPath = (0, node_path_1.join)(configDir, 'devices', 'pending.json');
379
+ const pairedPath = (0, node_path_1.join)(configDir, 'devices', 'paired.json');
380
+ if (!(0, node_fs_1.existsSync)(pendingPath))
381
+ return;
382
+ try {
383
+ const pending = JSON.parse((0, node_fs_1.readFileSync)(pendingPath, 'utf-8'));
384
+ if (!pending || Object.keys(pending).length === 0)
385
+ return;
386
+ let paired = {};
387
+ if ((0, node_fs_1.existsSync)(pairedPath)) {
388
+ paired = JSON.parse((0, node_fs_1.readFileSync)(pairedPath, 'utf-8'));
389
+ }
390
+ let count = 0;
391
+ for (const [, dev] of Object.entries(pending)) {
392
+ const deviceId = dev.deviceId;
393
+ paired[deviceId] = {
394
+ deviceId: dev.deviceId,
395
+ publicKey: dev.publicKey,
396
+ platform: dev.platform,
397
+ clientId: dev.clientId,
398
+ clientMode: dev.clientMode ?? 'webchat',
399
+ role: dev.role ?? 'operator',
400
+ roles: dev.roles ?? ['operator'],
401
+ scopes: dev.scopes ?? [],
402
+ pairedAt: Date.now(),
403
+ };
404
+ count++;
405
+ }
406
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(configDir, 'devices'), { recursive: true });
407
+ (0, node_fs_1.writeFileSync)(pairedPath, JSON.stringify(paired, null, 2), 'utf-8');
408
+ (0, node_fs_1.writeFileSync)(pendingPath, JSON.stringify({}, null, 2), 'utf-8');
409
+ console.log(` (auto-approved ${count} pending device(s))`);
410
+ }
411
+ catch {
412
+ // best-effort
413
+ }
414
+ }
415
+ // ── local helpers ────────────────────────────────────────────────────────────
416
+ const LOCAL_DIR = (0, node_path_1.join)(OPENCLAW_DIR, 'local');
417
+ const LOCAL_PID_FILE = (0, node_path_1.join)(LOCAL_DIR, 'openclaw.pid');
418
+ function localPidFile(name) {
419
+ return (0, node_path_1.join)(LOCAL_DIR, `${name}.pid`);
420
+ }
421
+ function localMetaFile(name) {
422
+ return (0, node_path_1.join)(LOCAL_DIR, `${name}.json`);
423
+ }
424
+ function isProcessRunning(pid) {
425
+ try {
426
+ process.kill(pid, 0);
427
+ return true;
428
+ }
429
+ catch {
430
+ return false;
431
+ }
432
+ }
433
+ function findOpenclawBin() {
434
+ try {
435
+ const cmd = IS_WIN ? 'where openclaw' : 'which openclaw';
436
+ return (0, node_child_process_1.execSync)(cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim().split('\n')[0];
437
+ }
438
+ catch {
439
+ // not in PATH
440
+ }
441
+ // Common install locations
442
+ const candidates = IS_WIN
443
+ ? [(0, node_path_1.join)((0, node_os_1.homedir)(), '.openclaw', 'bin', 'openclaw.cmd')]
444
+ : [
445
+ (0, node_path_1.join)((0, node_os_1.homedir)(), '.openclaw', 'bin', 'openclaw'),
446
+ '/usr/local/bin/openclaw',
447
+ ];
448
+ return candidates.find((p) => (0, node_fs_1.existsSync)(p)) ?? null;
449
+ }
450
+ // ── register ─────────────────────────────────────────────────────────────────
451
+ function registerOpenclawCommand(program) {
452
+ const openclaw = program
453
+ .command('openclaw')
454
+ .description('Manage OpenClaw instances routed through Bastion');
455
+ // ════════════════════════════════════════════════════════════════════════════
456
+ // bastion openclaw docker ...
457
+ // ════════════════════════════════════════════════════════════════════════════
458
+ const docker = openclaw
459
+ .command('docker')
460
+ .description('Manage OpenClaw via Docker Compose');
461
+ // bastion openclaw docker attach <container>
462
+ docker
463
+ .command('attach')
464
+ .description('Inject Bastion proxy into an already-running Docker container')
465
+ .argument('<container>', 'Docker container name or ID')
466
+ .option('--restart', 'Stop, commit, and re-run the container with proxy env vars baked in')
467
+ .action(async (container, options) => {
468
+ checkBastion();
469
+ checkDocker();
470
+ const caPath = checkCACert();
471
+ const bastionPort = getBastionPort();
472
+ // Verify container exists and is running
473
+ try {
474
+ const fmt = IS_WIN ? '"{{.State.Status}}"' : "'{{.State.Status}}'";
475
+ const state = (0, node_child_process_1.execSync)(`docker inspect --format ${fmt} "${container}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
476
+ if (state !== 'running') {
477
+ console.error(`Container '${container}' is not running (state: ${state})`);
478
+ process.exit(1);
479
+ }
480
+ }
481
+ catch {
482
+ console.error(`Container '${container}' not found`);
483
+ process.exit(1);
484
+ }
485
+ // Copy CA cert into container
486
+ console.log(`Copying CA cert into ${container}...`);
487
+ (0, node_child_process_1.execSync)(`docker cp "${caPath}" "${container}:/etc/ssl/certs/bastion-ca.crt"`, { stdio: 'inherit' });
488
+ const envVars = {
489
+ HTTPS_PROXY: `http://host.docker.internal:${bastionPort}`,
490
+ NODE_EXTRA_CA_CERTS: '/etc/ssl/certs/bastion-ca.crt',
491
+ NO_PROXY: 'localhost,127.0.0.1,host.docker.internal',
492
+ };
493
+ if (options.restart) {
494
+ console.log('Restarting container with proxy env vars...');
495
+ const imgFmt = IS_WIN ? '"{{.Config.Image}}"' : "'{{.Config.Image}}'";
496
+ const imageName = (0, node_child_process_1.execSync)(`docker inspect --format ${imgFmt} "${container}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
497
+ const tmpImage = `bastion-attach-${container}-${Date.now()}`;
498
+ (0, node_child_process_1.execSync)(`docker commit "${container}" "${tmpImage}"`, { stdio: 'inherit' });
499
+ (0, node_child_process_1.execSync)(`docker stop "${container}"`, { stdio: 'inherit' });
500
+ (0, node_child_process_1.execSync)(`docker rm "${container}"`, { stdio: 'inherit' });
501
+ const envFlags = Object.entries(envVars)
502
+ .map(([k, v]) => `-e ${k}="${v}"`)
503
+ .join(' ');
504
+ const volumeFlag = `-v "${caPath}:/etc/ssl/certs/bastion-ca.crt:ro"`;
505
+ (0, node_child_process_1.execSync)(`docker run -d --name "${container}" ${envFlags} ${volumeFlag} "${tmpImage}"`, { stdio: 'inherit' });
506
+ console.log(`Container '${container}' restarted with Bastion proxy.`);
507
+ console.log(`(Original image: ${imageName}, committed as: ${tmpImage})`);
508
+ }
509
+ else {
510
+ console.log('');
511
+ console.log('CA cert copied. Add these env vars to your docker-compose.yml or docker run:');
512
+ console.log('');
513
+ for (const [k, v] of Object.entries(envVars)) {
514
+ console.log(` ${k}=${v}`);
515
+ }
516
+ console.log('');
517
+ console.log('And mount the CA cert volume:');
518
+ console.log(` -v ${caPath}:/etc/ssl/certs/bastion-ca.crt:ro`);
519
+ console.log('');
520
+ }
521
+ });
522
+ // bastion openclaw docker up <name>
523
+ docker
524
+ .command('up')
525
+ .description('Create (if needed) and start an OpenClaw Docker instance')
526
+ .argument('<name>', 'Instance name')
527
+ .option('--port <port>', 'Gateway port', String(DEFAULT_PORT))
528
+ .option('--image <image>', 'Docker image', DEFAULT_IMAGE)
529
+ .option('--config-dir <path>', 'OpenClaw config directory (contains devices/, openclaw.json)')
530
+ .option('--workspace <path>', 'OpenClaw workspace directory')
531
+ .action(async (name, options) => {
532
+ checkBastion();
533
+ checkDocker();
534
+ const caPath = checkCACert();
535
+ const bastionPort = getBastionPort();
536
+ const port = parseInt(options.port, 10);
537
+ const bridgePort = port + 1;
538
+ const dir = instanceDir(name);
539
+ // If instance already exists, update overlay + bootstrap and start
540
+ if ((0, node_fs_1.existsSync)(dir)) {
541
+ console.log(`Instance '${name}' exists, starting...`);
542
+ // Always update proxy bootstrap + brew shim + Bastion override (idempotent)
543
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'proxy-bootstrap.mjs'), PROXY_BOOTSTRAP_CONTENT, 'utf-8');
544
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'brew'), BREW_SHIM_CONTENT, { mode: 0o755 });
545
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.bastion.yml'), generateBastionOverride(caPath), 'utf-8');
546
+ // Migrate old-style compose that had Bastion proxy baked in
547
+ const existingCompose = (0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, 'docker-compose.yml'), 'utf-8');
548
+ if (existingCompose.includes('NODE_EXTRA_CA_CERTS')) {
549
+ const composeContent = generateComposeFile(options.image);
550
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.yml'), composeContent, 'utf-8');
551
+ console.log(' (migrated docker-compose.yml — Bastion proxy moved to docker-compose.bastion.yml)');
552
+ }
553
+ syncToken(name);
554
+ fixBind(name);
555
+ const code = await dc(name, ['up', '-d', '--force-recreate', 'openclaw-gateway']);
556
+ if (code !== 0)
557
+ process.exit(code);
558
+ await sleep(3000);
559
+ approveDevices(name);
560
+ const finalPort = envVal(name, 'OPENCLAW_GATEWAY_PORT') || String(port);
561
+ const finalToken = envVal(name, 'OPENCLAW_GATEWAY_TOKEN');
562
+ console.log('');
563
+ console.log(`Dashboard: http://127.0.0.1:${finalPort}/?token=${finalToken}`);
564
+ return;
565
+ }
566
+ // Create new instance
567
+ const configDir = options.configDir ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `.openclaw-${name}`);
568
+ const workspaceDir = options.workspace ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `openclaw-${name}`, 'workspace');
569
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
570
+ (0, node_fs_1.mkdirSync)(configDir, { recursive: true });
571
+ (0, node_fs_1.mkdirSync)(workspaceDir, { recursive: true });
572
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(configDir, 'devices'), { recursive: true });
573
+ const token = generateToken();
574
+ writeEnvFile(dir, {
575
+ OPENCLAW_IMAGE: options.image,
576
+ OPENCLAW_CONFIG_DIR: configDir,
577
+ OPENCLAW_WORKSPACE_DIR: workspaceDir,
578
+ OPENCLAW_GATEWAY_TOKEN: token,
579
+ OPENCLAW_GATEWAY_PORT: String(port),
580
+ OPENCLAW_BRIDGE_PORT: String(bridgePort),
581
+ OPENCLAW_GATEWAY_BIND: 'lan',
582
+ BASTION_PORT: String(bastionPort),
583
+ CLAUDE_AI_SESSION_KEY: '',
584
+ CLAUDE_WEB_SESSION_KEY: '',
585
+ CLAUDE_WEB_COOKIE: '',
586
+ });
587
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.yml'), generateComposeFile(options.image), 'utf-8');
588
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'docker-compose.bastion.yml'), generateBastionOverride(caPath), 'utf-8');
589
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'proxy-bootstrap.mjs'), PROXY_BOOTSTRAP_CONTENT, 'utf-8');
590
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, 'brew'), BREW_SHIM_CONTENT, { mode: 0o755 });
591
+ console.log(`==> Instance '${name}' created (gateway: ${port}, bridge: ${bridgePort})`);
592
+ console.log('==> Initializing gateway config...');
593
+ let code = await dc(name, ['run', '--rm', 'openclaw-cli', 'config', 'set', 'gateway.mode', 'local']);
594
+ if (code !== 0) {
595
+ console.error('Failed to initialize gateway config');
596
+ process.exit(code);
597
+ }
598
+ // Patch config before first start (controlUi origin, bind=lan)
599
+ fixBind(name);
600
+ console.log('==> Starting gateway...');
601
+ code = await dc(name, ['up', '-d', 'openclaw-gateway']);
602
+ if (code !== 0)
603
+ process.exit(code);
604
+ await sleep(3000);
605
+ console.log('');
606
+ console.log('==> Running interactive onboarding...');
607
+ console.log(` When prompted for gateway token, use: ${token}`);
608
+ console.log('');
609
+ code = await dc(name, ['exec', 'openclaw-gateway', 'node', 'dist/index.js', 'onboard', '--no-install-daemon']);
610
+ if (code !== 0) {
611
+ console.error('Onboarding failed');
612
+ process.exit(code);
613
+ }
614
+ console.log('');
615
+ console.log('==> Applying post-onboard fixes...');
616
+ syncToken(name);
617
+ fixBind(name);
618
+ console.log('==> Restarting gateway...');
619
+ await dc(name, ['restart', 'openclaw-gateway']);
620
+ await sleep(3000);
621
+ approveDevices(name);
622
+ await dc(name, ['restart', 'openclaw-gateway']);
623
+ await sleep(2000);
624
+ const finalToken = envVal(name, 'OPENCLAW_GATEWAY_TOKEN');
625
+ console.log('');
626
+ console.log(`==> Instance '${name}' is ready!`);
627
+ console.log('');
628
+ console.log(` Dashboard: http://127.0.0.1:${port}/?token=${finalToken}`);
629
+ console.log('');
630
+ console.log(' Open the URL above in your browser.');
631
+ console.log(' If prompted to pair, refresh the page — devices are auto-approved.');
632
+ console.log('');
633
+ });
634
+ // bastion openclaw docker run
635
+ docker
636
+ .command('run')
637
+ .description('Start OpenClaw via an existing docker-compose.yml with Bastion proxy')
638
+ .option('--compose <path>', 'Path to docker-compose.yml')
639
+ .option('--env-file <path>', 'Path to .env file for docker compose')
640
+ .option('-p, --project <name>', 'Docker compose project name', 'openclaw')
641
+ .action((options) => {
642
+ checkBastion();
643
+ checkDocker();
644
+ checkCACert();
645
+ const bastionPort = getBastionPort();
646
+ let composeFile = options.compose;
647
+ if (!composeFile) {
648
+ const candidates = [
649
+ (0, node_path_1.join)(process.cwd(), 'integration', 'openclaw-docker', 'docker-compose.yml'),
650
+ (0, node_path_1.join)(process.cwd(), 'docker-compose.yml'),
651
+ ];
652
+ composeFile = candidates.find((f) => (0, node_fs_1.existsSync)(f));
653
+ if (!composeFile) {
654
+ console.error('No docker-compose.yml found. Use --compose <path> to specify one.');
655
+ process.exit(1);
656
+ }
657
+ }
658
+ if (!(0, node_fs_1.existsSync)(composeFile)) {
659
+ console.error(`Compose file not found: ${composeFile}`);
660
+ process.exit(1);
661
+ }
662
+ console.log(`Using compose file: ${composeFile}`);
663
+ console.log(`Bastion proxy port: ${bastionPort}`);
664
+ const args = [
665
+ 'compose',
666
+ '-f', composeFile,
667
+ ...(options.envFile ? ['--env-file', options.envFile] : []),
668
+ '-p', options.project,
669
+ 'up', '-d',
670
+ ];
671
+ const env = { ...process.env, BASTION_PORT: String(bastionPort) };
672
+ const child = (0, node_child_process_1.spawn)('docker', args, { stdio: 'inherit', env });
673
+ child.on('close', (code) => {
674
+ if (code === 0) {
675
+ console.log('');
676
+ console.log('OpenClaw started with Bastion proxy.');
677
+ }
678
+ process.exitCode = code ?? 0;
679
+ });
680
+ child.on('error', (err) => {
681
+ console.error(`Failed to start: ${err.message}`);
682
+ process.exitCode = 1;
683
+ });
684
+ });
685
+ // bastion openclaw docker stop <name>
686
+ docker
687
+ .command('stop')
688
+ .description('Stop a Docker OpenClaw instance')
689
+ .argument('<name>', 'Instance name')
690
+ .action(async (name) => {
691
+ const dir = instanceDir(name);
692
+ if (!(0, node_fs_1.existsSync)(dir)) {
693
+ console.error(`Instance '${name}' does not exist.`);
694
+ process.exit(1);
695
+ }
696
+ console.log(`Stopping '${name}'...`);
697
+ const code = await dc(name, ['down']);
698
+ if (code !== 0)
699
+ process.exit(code);
700
+ console.log('Stopped.');
701
+ });
702
+ // bastion openclaw docker exec <name> [-- args...]
703
+ docker
704
+ .command('exec')
705
+ .description('Run a command inside the OpenClaw gateway container')
706
+ .argument('<name>', 'Instance name')
707
+ .argument('[args...]', 'Command args (passed to openclaw CLI)')
708
+ .passThroughOptions()
709
+ .allowUnknownOption()
710
+ .action(async (name, args) => {
711
+ const dir = instanceDir(name);
712
+ if (!(0, node_fs_1.existsSync)(dir)) {
713
+ console.error(`Instance '${name}' does not exist.`);
714
+ process.exit(1);
715
+ }
716
+ const code = await dc(name, ['exec', 'openclaw-gateway', 'node', 'dist/index.js', ...args]);
717
+ if (code !== 0)
718
+ process.exit(code);
719
+ });
720
+ // bastion openclaw docker destroy <name>
721
+ docker
722
+ .command('destroy')
723
+ .description('Stop and remove a Docker OpenClaw instance (data dirs preserved)')
724
+ .argument('<name>', 'Instance name')
725
+ .action(async (name) => {
726
+ const dir = instanceDir(name);
727
+ if (!(0, node_fs_1.existsSync)(dir)) {
728
+ console.error(`Instance '${name}' does not exist.`);
729
+ process.exit(1);
730
+ }
731
+ // Read config/workspace paths before deleting
732
+ const configDir = envVal(name, 'OPENCLAW_CONFIG_DIR');
733
+ const workspaceDir = envVal(name, 'OPENCLAW_WORKSPACE_DIR');
734
+ // docker compose down -v
735
+ console.log(`Destroying instance '${name}'...`);
736
+ await dc(name, ['down', '-v']);
737
+ // Remove instance dir
738
+ const { rmSync } = require('node:fs');
739
+ rmSync(dir, { recursive: true, force: true });
740
+ console.log(`Instance '${name}' removed.`);
741
+ console.log('');
742
+ console.log('Data directories preserved (delete manually if needed):');
743
+ if (configDir)
744
+ console.log(` ${configDir}`);
745
+ if (workspaceDir)
746
+ console.log(` ${workspaceDir}`);
747
+ console.log('');
748
+ console.log(`To fully clean up: rm -rf ${configDir} ${workspaceDir}`);
749
+ });
750
+ // bastion openclaw docker status
751
+ docker
752
+ .command('status')
753
+ .description('List all Docker OpenClaw instances')
754
+ .action(() => {
755
+ const dockerDir = (0, node_path_1.join)(OPENCLAW_DIR, 'docker');
756
+ if (!(0, node_fs_1.existsSync)(dockerDir)) {
757
+ console.log('(no docker instances found)');
758
+ return;
759
+ }
760
+ const entries = (0, node_fs_1.readdirSync)(dockerDir, { withFileTypes: true })
761
+ .filter((d) => d.isDirectory());
762
+ if (entries.length === 0) {
763
+ console.log('(no docker instances found)');
764
+ return;
765
+ }
766
+ const header = { name: 'INSTANCE', status: 'STATUS', port: 'GATEWAY', bridge: 'BRIDGE', dashboard: 'DASHBOARD' };
767
+ const rows = [header];
768
+ for (const entry of entries) {
769
+ const name = entry.name;
770
+ const port = envVal(name, 'OPENCLAW_GATEWAY_PORT') || '-';
771
+ const bridge = envVal(name, 'OPENCLAW_BRIDGE_PORT') || '-';
772
+ const token = envVal(name, 'OPENCLAW_GATEWAY_TOKEN') || '';
773
+ let state = 'stopped';
774
+ try {
775
+ state = dcOutput(name, ['ps', '--format', '{{.State}}', 'openclaw-gateway']) || 'stopped';
776
+ }
777
+ catch {
778
+ // keep stopped
779
+ }
780
+ const dashboard = port !== '-' && token
781
+ ? `http://127.0.0.1:${port}/?token=${token}`
782
+ : '-';
783
+ rows.push({ name, status: state, port, bridge, dashboard });
784
+ }
785
+ const colWidths = {
786
+ name: Math.max(...rows.map((r) => r.name.length)) + 2,
787
+ status: Math.max(...rows.map((r) => r.status.length)) + 2,
788
+ port: Math.max(...rows.map((r) => r.port.length)) + 2,
789
+ bridge: Math.max(...rows.map((r) => r.bridge.length)) + 2,
790
+ };
791
+ for (const row of rows) {
792
+ console.log(row.name.padEnd(colWidths.name) +
793
+ row.status.padEnd(colWidths.status) +
794
+ row.port.padEnd(colWidths.port) +
795
+ row.bridge.padEnd(colWidths.bridge) +
796
+ row.dashboard);
797
+ }
798
+ });
799
+ // bastion openclaw docker logs <name>
800
+ docker
801
+ .command('logs')
802
+ .description('Show logs for a Docker OpenClaw instance')
803
+ .argument('<name>', 'Instance name')
804
+ .option('-f, --follow', 'Follow log output')
805
+ .action(async (name, options) => {
806
+ const dir = instanceDir(name);
807
+ if (!(0, node_fs_1.existsSync)(dir)) {
808
+ console.error(`Instance '${name}' does not exist.`);
809
+ process.exit(1);
810
+ }
811
+ const args = ['logs'];
812
+ if (options.follow)
813
+ args.push('-f');
814
+ args.push('openclaw-gateway');
815
+ const code = await dc(name, args);
816
+ if (code !== 0)
817
+ process.exit(code);
818
+ });
819
+ // ════════════════════════════════════════════════════════════════════════════
820
+ // bastion openclaw local ...
821
+ // ════════════════════════════════════════════════════════════════════════════
822
+ const local = openclaw
823
+ .command('local')
824
+ .description('Manage OpenClaw running as a local process');
825
+ // bastion openclaw local start <name>
826
+ local
827
+ .command('start')
828
+ .description('Start OpenClaw locally with Bastion proxy')
829
+ .argument('<name>', 'Instance name')
830
+ .option('--port <port>', 'Gateway port', String(DEFAULT_PORT))
831
+ .option('--bin <path>', 'Path to openclaw binary')
832
+ .option('--config-dir <path>', 'OpenClaw config directory')
833
+ .option('--workspace <path>', 'OpenClaw workspace directory')
834
+ .option('--foreground', 'Run in foreground (default: daemon)')
835
+ .action((name, options) => {
836
+ checkBastion();
837
+ const caPath = checkCACert();
838
+ const bastionPort = getBastionPort();
839
+ const bastionHost = getBastionHost();
840
+ const port = parseInt(options.port, 10);
841
+ // Find openclaw binary
842
+ const bin = options.bin ?? findOpenclawBin();
843
+ if (!bin || !(0, node_fs_1.existsSync)(bin)) {
844
+ console.error('OpenClaw binary not found. Install openclaw or use --bin <path>.\n' +
845
+ 'Or use "bastion openclaw docker" for Docker mode.');
846
+ process.exit(1);
847
+ }
848
+ // Check if already running
849
+ const pidFile = localPidFile(name);
850
+ if ((0, node_fs_1.existsSync)(pidFile)) {
851
+ const oldPid = parseInt((0, node_fs_1.readFileSync)(pidFile, 'utf-8').trim(), 10);
852
+ if (!isNaN(oldPid) && isProcessRunning(oldPid)) {
853
+ console.error(`Instance '${name}' is already running (PID ${oldPid}). Stop it first.`);
854
+ process.exit(1);
855
+ }
856
+ (0, node_fs_1.unlinkSync)(pidFile);
857
+ }
858
+ const configDir = options.configDir ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `.openclaw-${name}`);
859
+ const workspaceDir = options.workspace ?? (0, node_path_1.join)((0, node_os_1.homedir)(), `openclaw-${name}`, 'workspace');
860
+ (0, node_fs_1.mkdirSync)(LOCAL_DIR, { recursive: true });
861
+ (0, node_fs_1.mkdirSync)(configDir, { recursive: true });
862
+ (0, node_fs_1.mkdirSync)(workspaceDir, { recursive: true });
863
+ // Ensure proxy bootstrap script exists
864
+ const bootstrapPath = (0, node_path_1.join)(paths_js_1.paths.bastionDir, 'proxy-bootstrap.mjs');
865
+ (0, node_fs_1.writeFileSync)(bootstrapPath, PROXY_BOOTSTRAP_CONTENT, 'utf-8');
866
+ // Build env with Bastion proxy
867
+ const env = {
868
+ ...process.env,
869
+ HTTPS_PROXY: `http://openclaw-local-${name}@${bastionHost}:${bastionPort}`,
870
+ NODE_EXTRA_CA_CERTS: caPath,
871
+ NO_PROXY: `${bastionHost},localhost,127.0.0.1`,
872
+ NODE_OPTIONS: `--import ${bootstrapPath}`,
873
+ HOME: (0, node_os_1.homedir)(),
874
+ };
875
+ const args = ['gateway', '--port', String(port), '--bind', 'localhost'];
876
+ // Save metadata
877
+ (0, node_fs_1.writeFileSync)(localMetaFile(name), JSON.stringify({
878
+ port,
879
+ configDir,
880
+ workspaceDir,
881
+ bin,
882
+ startedAt: new Date().toISOString(),
883
+ }, null, 2), 'utf-8');
884
+ if (options.foreground) {
885
+ console.log(`Starting OpenClaw '${name}' on port ${port} (foreground)...`);
886
+ console.log(` Binary: ${bin}`);
887
+ console.log(` Config: ${configDir}`);
888
+ console.log(` Workspace: ${workspaceDir}`);
889
+ console.log(` Proxy: ${bastionHost}:${bastionPort}`);
890
+ console.log('');
891
+ const child = (0, node_child_process_1.spawn)(bin, args, {
892
+ stdio: 'inherit',
893
+ env,
894
+ cwd: workspaceDir,
895
+ ...(IS_WIN ? { shell: true } : {}),
896
+ });
897
+ // Write PID
898
+ if (child.pid) {
899
+ (0, node_fs_1.writeFileSync)(pidFile, String(child.pid), 'utf-8');
900
+ }
901
+ child.on('close', (code) => {
902
+ if ((0, node_fs_1.existsSync)(pidFile))
903
+ (0, node_fs_1.unlinkSync)(pidFile);
904
+ process.exitCode = code ?? 0;
905
+ });
906
+ child.on('error', (err) => {
907
+ console.error(`Failed to start: ${err.message}`);
908
+ if ((0, node_fs_1.existsSync)(pidFile))
909
+ (0, node_fs_1.unlinkSync)(pidFile);
910
+ process.exitCode = 1;
911
+ });
912
+ }
913
+ else {
914
+ console.log(`Starting OpenClaw '${name}' on port ${port} (daemon)...`);
915
+ const logFile = (0, node_path_1.join)(LOCAL_DIR, `${name}.log`);
916
+ const { openSync } = require('node:fs');
917
+ const logFd = openSync(logFile, 'a');
918
+ const child = (0, node_child_process_1.spawn)(bin, args, {
919
+ detached: true,
920
+ stdio: ['ignore', logFd, logFd],
921
+ env,
922
+ cwd: workspaceDir,
923
+ ...(IS_WIN ? { shell: true, windowsHide: true } : {}),
924
+ });
925
+ child.unref();
926
+ if (child.pid) {
927
+ (0, node_fs_1.writeFileSync)(pidFile, String(child.pid), 'utf-8');
928
+ console.log(` PID: ${child.pid}`);
929
+ }
930
+ console.log(` Binary: ${bin}`);
931
+ console.log(` Port: ${port}`);
932
+ console.log(` Config: ${configDir}`);
933
+ console.log(` Workspace: ${workspaceDir}`);
934
+ console.log(` Proxy: ${bastionHost}:${bastionPort}`);
935
+ console.log(` Log: ${logFile}`);
936
+ console.log('');
937
+ console.log(`Dashboard: http://127.0.0.1:${port}/`);
938
+ }
939
+ });
940
+ // bastion openclaw local stop <name>
941
+ local
942
+ .command('stop')
943
+ .description('Stop a locally running OpenClaw instance')
944
+ .argument('<name>', 'Instance name')
945
+ .action((name) => {
946
+ const pidFile = localPidFile(name);
947
+ if (!(0, node_fs_1.existsSync)(pidFile)) {
948
+ console.error(`Instance '${name}' is not running (no PID file).`);
949
+ process.exit(1);
950
+ }
951
+ const pid = parseInt((0, node_fs_1.readFileSync)(pidFile, 'utf-8').trim(), 10);
952
+ if (isNaN(pid)) {
953
+ console.error('Invalid PID file.');
954
+ (0, node_fs_1.unlinkSync)(pidFile);
955
+ process.exit(1);
956
+ }
957
+ if (!isProcessRunning(pid)) {
958
+ console.log(`Instance '${name}' is not running (stale PID ${pid}).`);
959
+ (0, node_fs_1.unlinkSync)(pidFile);
960
+ return;
961
+ }
962
+ try {
963
+ process.kill(pid, 'SIGTERM');
964
+ console.log(`Stopped '${name}' (PID ${pid}).`);
965
+ }
966
+ catch (err) {
967
+ console.error(`Failed to stop PID ${pid}: ${err.message}`);
968
+ }
969
+ (0, node_fs_1.unlinkSync)(pidFile);
970
+ });
971
+ // bastion openclaw local status
972
+ local
973
+ .command('status')
974
+ .description('List all local OpenClaw instances')
975
+ .action(() => {
976
+ if (!(0, node_fs_1.existsSync)(LOCAL_DIR)) {
977
+ console.log('(no local instances found)');
978
+ return;
979
+ }
980
+ const metaFiles = (0, node_fs_1.readdirSync)(LOCAL_DIR).filter((f) => f.endsWith('.json'));
981
+ if (metaFiles.length === 0) {
982
+ console.log('(no local instances found)');
983
+ return;
984
+ }
985
+ const header = { name: 'INSTANCE', status: 'STATUS', port: 'PORT', pid: 'PID', dashboard: 'DASHBOARD' };
986
+ const rows = [header];
987
+ for (const file of metaFiles) {
988
+ const name = file.replace(/\.json$/, '');
989
+ const pidFile = localPidFile(name);
990
+ let port = '-';
991
+ let pidStr = '-';
992
+ let state = 'stopped';
993
+ try {
994
+ const meta = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(LOCAL_DIR, file), 'utf-8'));
995
+ port = String(meta.port ?? '-');
996
+ }
997
+ catch {
998
+ // ignore
999
+ }
1000
+ if ((0, node_fs_1.existsSync)(pidFile)) {
1001
+ const pid = parseInt((0, node_fs_1.readFileSync)(pidFile, 'utf-8').trim(), 10);
1002
+ if (!isNaN(pid) && isProcessRunning(pid)) {
1003
+ state = 'running';
1004
+ pidStr = String(pid);
1005
+ }
1006
+ else {
1007
+ // Stale PID
1008
+ if ((0, node_fs_1.existsSync)(pidFile))
1009
+ (0, node_fs_1.unlinkSync)(pidFile);
1010
+ }
1011
+ }
1012
+ const dashboard = port !== '-'
1013
+ ? `http://127.0.0.1:${port}/`
1014
+ : '-';
1015
+ rows.push({ name, status: state, port, pid: pidStr, dashboard });
1016
+ }
1017
+ const colWidths = {
1018
+ name: Math.max(...rows.map((r) => r.name.length)) + 2,
1019
+ status: Math.max(...rows.map((r) => r.status.length)) + 2,
1020
+ port: Math.max(...rows.map((r) => r.port.length)) + 2,
1021
+ pid: Math.max(...rows.map((r) => r.pid.length)) + 2,
1022
+ };
1023
+ for (const row of rows) {
1024
+ console.log(row.name.padEnd(colWidths.name) +
1025
+ row.status.padEnd(colWidths.status) +
1026
+ row.port.padEnd(colWidths.port) +
1027
+ row.pid.padEnd(colWidths.pid) +
1028
+ row.dashboard);
1029
+ }
1030
+ });
1031
+ // bastion openclaw local logs <name>
1032
+ local
1033
+ .command('logs')
1034
+ .description('Show logs for a local OpenClaw instance')
1035
+ .argument('<name>', 'Instance name')
1036
+ .option('-f, --follow', 'Follow log output')
1037
+ .action((name, options) => {
1038
+ const logFile = (0, node_path_1.join)(LOCAL_DIR, `${name}.log`);
1039
+ if (!(0, node_fs_1.existsSync)(logFile)) {
1040
+ console.error(`No log file found for '${name}'.`);
1041
+ process.exit(1);
1042
+ }
1043
+ let child;
1044
+ if (IS_WIN) {
1045
+ // PowerShell Get-Content as replacement for tail
1046
+ const psArgs = options.follow
1047
+ ? ['-Command', `Get-Content -Path "${logFile}" -Tail 100 -Wait`]
1048
+ : ['-Command', `Get-Content -Path "${logFile}" -Tail 100`];
1049
+ child = (0, node_child_process_1.spawn)('powershell', psArgs, { stdio: 'inherit' });
1050
+ }
1051
+ else {
1052
+ const tailArgs = options.follow
1053
+ ? ['-f', logFile]
1054
+ : ['-100', logFile];
1055
+ child = (0, node_child_process_1.spawn)('tail', tailArgs, { stdio: 'inherit' });
1056
+ }
1057
+ child.on('close', (code) => {
1058
+ process.exitCode = code ?? 0;
1059
+ });
1060
+ });
1061
+ }
1062
+ //# sourceMappingURL=openclaw.js.map