@shadowob/cloud 1.1.6-dev.311

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 (334) hide show
  1. package/README.md +509 -0
  2. package/dist/agent-browser-CERTMCDL.js +117 -0
  3. package/dist/agent-browser-CIRZRIY4.js +118 -0
  4. package/dist/agent-pack-LF3O5TR4.js +1236 -0
  5. package/dist/agent-pack-RQT27V7R.js +1235 -0
  6. package/dist/airtable-BG2Q75G2.js +82 -0
  7. package/dist/airtable-JCQXFM5D.js +83 -0
  8. package/dist/alipay-MZX2XCDB.js +52 -0
  9. package/dist/alipay-TZQI34RB.js +51 -0
  10. package/dist/amap-5RQB3VGC.js +45 -0
  11. package/dist/amap-KPCLZYYL.js +44 -0
  12. package/dist/atlassian-LGOEWYC7.js +54 -0
  13. package/dist/atlassian-TVS2A4IU.js +55 -0
  14. package/dist/baidu-appbuilder-6UMESXHW.js +41 -0
  15. package/dist/baidu-appbuilder-QRRL3ETM.js +42 -0
  16. package/dist/baidu-maps-HEPMVP5D.js +44 -0
  17. package/dist/baidu-maps-HXC4FBVP.js +45 -0
  18. package/dist/baidu-netdisk-G5Q6B5NH.js +45 -0
  19. package/dist/baidu-netdisk-RS2K5W2M.js +44 -0
  20. package/dist/baidu-smartprogram-EWTK5WKK.js +41 -0
  21. package/dist/baidu-smartprogram-JHD3XWF6.js +40 -0
  22. package/dist/browserbase-IUIYVYI7.js +67 -0
  23. package/dist/browserbase-JFO2PCIA.js +68 -0
  24. package/dist/canva-3YOFL7JS.js +62 -0
  25. package/dist/canva-FMYN65SM.js +61 -0
  26. package/dist/chunk-6P2K6QZR.js +529 -0
  27. package/dist/chunk-7VMRQ7MG.js +90 -0
  28. package/dist/chunk-AD3JTIU3.js +17 -0
  29. package/dist/chunk-BF6CV2Y4.js +64 -0
  30. package/dist/chunk-CTNUKOQE.js +439 -0
  31. package/dist/chunk-EEFMJYKB.js +97 -0
  32. package/dist/chunk-EJKFQ35I.js +739 -0
  33. package/dist/chunk-HUICDC56.js +62 -0
  34. package/dist/chunk-JUPAE5IA.js +527 -0
  35. package/dist/chunk-JY2HTT7Q.js +437 -0
  36. package/dist/chunk-KEPTCLUO.js +121 -0
  37. package/dist/chunk-KKK5H7YX.js +3622 -0
  38. package/dist/chunk-POSVEKIY.js +210 -0
  39. package/dist/chunk-QET4LT4J.js +5769 -0
  40. package/dist/chunk-QV4XWO3P.js +30 -0
  41. package/dist/chunk-R52J3PH2.js +120 -0
  42. package/dist/chunk-R5U7XKVJ.js +16 -0
  43. package/dist/chunk-RECNVWMT.js +212 -0
  44. package/dist/chunk-RTPBU5HF.js +92 -0
  45. package/dist/chunk-SUZ2ATT6.js +5774 -0
  46. package/dist/chunk-SVMXSIMG.js +98 -0
  47. package/dist/chunk-TV3CBM7R.js +28 -0
  48. package/dist/chunk-V2LU736V.js +3495 -0
  49. package/dist/chunk-ZUYL3W53.js +741 -0
  50. package/dist/claude-plugin-577TAQVS.js +1463 -0
  51. package/dist/claude-plugin-L3MXJJ6J.js +1464 -0
  52. package/dist/cli.js +7021 -0
  53. package/dist/cloudflare-HBBABPK6.js +114 -0
  54. package/dist/cloudflare-RDFPKMM5.js +115 -0
  55. package/dist/cnb-FLP3QX46.js +44 -0
  56. package/dist/cnb-YAVVEYFB.js +45 -0
  57. package/dist/console/index.html +12 -0
  58. package/dist/console/logo.png +0 -0
  59. package/dist/console/static/css/5079.f9e0918d.css +1 -0
  60. package/dist/console/static/css/index.7f91f806.css +1 -0
  61. package/dist/console/static/font/codicon.5b7d6fac.ttf +0 -0
  62. package/dist/console/static/js/5079.72a51ca2.js +699 -0
  63. package/dist/console/static/js/5079.72a51ca2.js.LICENSE.txt +35 -0
  64. package/dist/console/static/js/7426.f8d483ea.js +1 -0
  65. package/dist/console/static/js/async/1008.4df521b7.js +1 -0
  66. package/dist/console/static/js/async/102.1d473ec7.js +1 -0
  67. package/dist/console/static/js/async/1134.3f9fd9e7.js +1 -0
  68. package/dist/console/static/js/async/1318.4b8e48e7.js +1 -0
  69. package/dist/console/static/js/async/1360.5606da88.js +7 -0
  70. package/dist/console/static/js/async/1546.045f484f.js +1 -0
  71. package/dist/console/static/js/async/1562.187de2a8.js +1 -0
  72. package/dist/console/static/js/async/168.456d4813.js +1 -0
  73. package/dist/console/static/js/async/1750.e6dc2664.js +1 -0
  74. package/dist/console/static/js/async/1994.3fc86066.js +1 -0
  75. package/dist/console/static/js/async/2348.613ae3d9.js +1 -0
  76. package/dist/console/static/js/async/2390.1b890b9d.js +1 -0
  77. package/dist/console/static/js/async/2414.9d040212.js +1 -0
  78. package/dist/console/static/js/async/2454.4c1784ab.js +1 -0
  79. package/dist/console/static/js/async/2498.f5f92030.js +1 -0
  80. package/dist/console/static/js/async/2924.b823cd1a.js +1 -0
  81. package/dist/console/static/js/async/3062.63fddea6.js +1 -0
  82. package/dist/console/static/js/async/3078.dd712008.js +1 -0
  83. package/dist/console/static/js/async/3198.1f307065.js +1 -0
  84. package/dist/console/static/js/async/3246.3d5a899f.js +1 -0
  85. package/dist/console/static/js/async/3286.871676eb.js +1 -0
  86. package/dist/console/static/js/async/342.10bf3b90.js +1 -0
  87. package/dist/console/static/js/async/3446.9681a4d7.js +1 -0
  88. package/dist/console/static/js/async/3698.ccfaabec.js +1 -0
  89. package/dist/console/static/js/async/3790.2a1106a6.js +1 -0
  90. package/dist/console/static/js/async/4231.b29784d4.js +1 -0
  91. package/dist/console/static/js/async/4551.515bd41d.js +1 -0
  92. package/dist/console/static/js/async/4596.40f6e71b.js +1 -0
  93. package/dist/console/static/js/async/4600.4aaebe6d.js +1 -0
  94. package/dist/console/static/js/async/4718.1aae022f.js +1 -0
  95. package/dist/console/static/js/async/4846.a347c020.js +1 -0
  96. package/dist/console/static/js/async/4860.83dadf89.js +1 -0
  97. package/dist/console/static/js/async/500.fcfa37cb.js +1 -0
  98. package/dist/console/static/js/async/5096.b360203d.js +1 -0
  99. package/dist/console/static/js/async/5222.043274fe.js +1 -0
  100. package/dist/console/static/js/async/5362.f498001c.js +1 -0
  101. package/dist/console/static/js/async/54.c94f0755.js +1 -0
  102. package/dist/console/static/js/async/5478.50dd9ef0.js +2 -0
  103. package/dist/console/static/js/async/5478.50dd9ef0.js.LICENSE.txt +3 -0
  104. package/dist/console/static/js/async/5507.a6a1f793.js +1 -0
  105. package/dist/console/static/js/async/5638.bc6b102d.js +1 -0
  106. package/dist/console/static/js/async/5722.e0029049.js +1 -0
  107. package/dist/console/static/js/async/5942.74635c6b.js +1 -0
  108. package/dist/console/static/js/async/5994.1c5629c1.js +1 -0
  109. package/dist/console/static/js/async/6054.6bddf720.js +1 -0
  110. package/dist/console/static/js/async/6118.45e754e5.js +1 -0
  111. package/dist/console/static/js/async/6127.adcbcbb6.js +1 -0
  112. package/dist/console/static/js/async/614.3f434c20.js +1 -0
  113. package/dist/console/static/js/async/6234.ba3b002d.js +1 -0
  114. package/dist/console/static/js/async/6310.6546a9ba.js +1 -0
  115. package/dist/console/static/js/async/6378.9f805419.js +1 -0
  116. package/dist/console/static/js/async/6380.e4433c49.js +1 -0
  117. package/dist/console/static/js/async/6418.f23bcfda.js +1 -0
  118. package/dist/console/static/js/async/6428.77c86114.js +1 -0
  119. package/dist/console/static/js/async/6443.83318a6c.js +1 -0
  120. package/dist/console/static/js/async/6508.2b445d62.js +3 -0
  121. package/dist/console/static/js/async/6542.e82a26c8.js +1 -0
  122. package/dist/console/static/js/async/6544.62111ecb.js +1 -0
  123. package/dist/console/static/js/async/6612.a0c9fcf4.js +1 -0
  124. package/dist/console/static/js/async/6740.695aebf0.js +1 -0
  125. package/dist/console/static/js/async/6822.dbbb32bc.js +1 -0
  126. package/dist/console/static/js/async/6824.ad3540ab.js +1 -0
  127. package/dist/console/static/js/async/6930.585dab94.js +1 -0
  128. package/dist/console/static/js/async/6982.c81b95e6.js +1 -0
  129. package/dist/console/static/js/async/7046.ab2378c1.js +1 -0
  130. package/dist/console/static/js/async/7110.a603277f.js +1 -0
  131. package/dist/console/static/js/async/7142.4a21366f.js +1 -0
  132. package/dist/console/static/js/async/7348.15cc6148.js +1373 -0
  133. package/dist/console/static/js/async/7348.15cc6148.js.LICENSE.txt +14 -0
  134. package/dist/console/static/js/async/7374.b1ac5c44.js +1 -0
  135. package/dist/console/static/js/async/742.847f17ca.js +1 -0
  136. package/dist/console/static/js/async/7446.743954d8.js +1 -0
  137. package/dist/console/static/js/async/7673.59bbbaac.js +1 -0
  138. package/dist/console/static/js/async/7684.c5760c8c.js +1 -0
  139. package/dist/console/static/js/async/7714.c30d0f94.js +1 -0
  140. package/dist/console/static/js/async/8118.36d5a3bf.js +298 -0
  141. package/dist/console/static/js/async/8145.4bcf043a.js +1 -0
  142. package/dist/console/static/js/async/8246.408de938.js +1 -0
  143. package/dist/console/static/js/async/8390.bdae1f7d.js +1 -0
  144. package/dist/console/static/js/async/8422.fd94dbe1.js +1 -0
  145. package/dist/console/static/js/async/8434.94a0e2ae.js +1 -0
  146. package/dist/console/static/js/async/8518.3158de13.js +1 -0
  147. package/dist/console/static/js/async/8564.fc2eb841.js +1 -0
  148. package/dist/console/static/js/async/8678.73af4c9b.js +1 -0
  149. package/dist/console/static/js/async/8694.79747168.js +1 -0
  150. package/dist/console/static/js/async/8756.1de37b83.js +1 -0
  151. package/dist/console/static/js/async/8804.7fe6bdf9.js +3 -0
  152. package/dist/console/static/js/async/8883.e717ee94.js +1 -0
  153. package/dist/console/static/js/async/8886.fe6e876c.js +1 -0
  154. package/dist/console/static/js/async/9030.fc1ae402.js +1 -0
  155. package/dist/console/static/js/async/9094.5598d084.js +1 -0
  156. package/dist/console/static/js/async/9218.ee7b84b7.js +1 -0
  157. package/dist/console/static/js/async/94.9b80bc35.js +1 -0
  158. package/dist/console/static/js/async/9526.92aba34c.js +1 -0
  159. package/dist/console/static/js/async/9762.f83bc4f3.js +1 -0
  160. package/dist/console/static/js/async/984.e11c113a.js +1 -0
  161. package/dist/console/static/js/async/9846.246653cd.js +1 -0
  162. package/dist/console/static/js/index.4487e1ff.js +1 -0
  163. package/dist/console/static/js/lib-react.15d7ca9a.js +2 -0
  164. package/dist/console/static/js/lib-react.15d7ca9a.js.LICENSE.txt +49 -0
  165. package/dist/coze-C6PMDPBI.js +49 -0
  166. package/dist/coze-E6VGRNLV.js +48 -0
  167. package/dist/dashboard.command-J7XOZNXU.js +8 -0
  168. package/dist/dashboard.command-RV2NHDKW.js +7 -0
  169. package/dist/dingtalk-JNRNRN7E.js +77 -0
  170. package/dist/dingtalk-WZGGIAHJ.js +76 -0
  171. package/dist/douyin-miniprogram-AIJPPIZH.js +41 -0
  172. package/dist/douyin-miniprogram-HCYZ5NBW.js +42 -0
  173. package/dist/figma-2YYNSCDX.js +103 -0
  174. package/dist/figma-RYOBMENP.js +102 -0
  175. package/dist/firebase-2IJDDBXX.js +112 -0
  176. package/dist/firebase-OYSY6HPT.js +111 -0
  177. package/dist/firecrawl-2T3SBUW7.js +66 -0
  178. package/dist/firecrawl-IYYXLAZM.js +65 -0
  179. package/dist/flyai-7FJ4TRAG.js +81 -0
  180. package/dist/flyai-QS5Q6FJR.js +82 -0
  181. package/dist/gitagent-MWI75OIX.js +725 -0
  182. package/dist/gitagent-YBMWY7NZ.js +726 -0
  183. package/dist/gitee-3N7OFOM7.js +53 -0
  184. package/dist/gitee-KVNK6KLZ.js +54 -0
  185. package/dist/github-LUEC2LID.js +143 -0
  186. package/dist/github-XRO5Z3GC.js +142 -0
  187. package/dist/google-ads-A3QAJI4D.js +88 -0
  188. package/dist/google-ads-VPKWTX67.js +89 -0
  189. package/dist/google-analytics-C4UR5ZR2.js +50 -0
  190. package/dist/google-analytics-XDYZA2B7.js +49 -0
  191. package/dist/google-workspace-LL3EWVHH.js +320 -0
  192. package/dist/google-workspace-YX35SHHX.js +321 -0
  193. package/dist/huawei-xiaoyi-6BSMGJHR.js +40 -0
  194. package/dist/huawei-xiaoyi-KPWLTSHB.js +41 -0
  195. package/dist/hubspot-DIUHGEDI.js +45 -0
  196. package/dist/hubspot-FTIEMNZO.js +44 -0
  197. package/dist/huggingface-MJCOXA7E.js +116 -0
  198. package/dist/huggingface-UUXK2RHK.js +117 -0
  199. package/dist/index.d.ts +3013 -0
  200. package/dist/index.js +15649 -0
  201. package/dist/inference-ai-image-generation-CMI6R5T3.js +106 -0
  202. package/dist/inference-ai-image-generation-PXV6IG4U.js +107 -0
  203. package/dist/inference-sh-7AZOLEFI.js +94 -0
  204. package/dist/inference-sh-ABQOD3YF.js +95 -0
  205. package/dist/init.command-6E24K4H3.js +9 -0
  206. package/dist/init.command-O4HG4HKR.js +10 -0
  207. package/dist/klaviyo-6K5YEFNH.js +45 -0
  208. package/dist/klaviyo-LDPBWBSS.js +44 -0
  209. package/dist/kuaidi100-HGFM5VK2.js +42 -0
  210. package/dist/kuaidi100-UHPFCVXP.js +41 -0
  211. package/dist/lark-6LNA3LUQ.js +103 -0
  212. package/dist/lark-URVBZNS4.js +102 -0
  213. package/dist/linear-7QFSFPOD.js +57 -0
  214. package/dist/linear-T4ORUP7N.js +56 -0
  215. package/dist/lovart-PDUXRUHJ.js +99 -0
  216. package/dist/lovart-QO3SK55T.js +100 -0
  217. package/dist/meta-ads-SCNFI45S.js +42 -0
  218. package/dist/meta-ads-V6XPZWX3.js +41 -0
  219. package/dist/miclaw-5CNTW7VV.js +36 -0
  220. package/dist/miclaw-TPPPS2WN.js +35 -0
  221. package/dist/model-provider-AVSFJSZP.js +393 -0
  222. package/dist/model-provider-KFB76XV5.js +392 -0
  223. package/dist/notion-FZK76MN2.js +69 -0
  224. package/dist/notion-WFA7KGZZ.js +70 -0
  225. package/dist/oceanengine-3JZUS3PP.js +43 -0
  226. package/dist/oceanengine-5BRIJVJE.js +42 -0
  227. package/dist/opencli-PFXHGCS2.js +81 -0
  228. package/dist/opencli-VIGRJTGH.js +80 -0
  229. package/dist/paypal-33UADIPR.js +54 -0
  230. package/dist/paypal-Z5JYHIWD.js +55 -0
  231. package/dist/playwright-MG5WHK47.js +58 -0
  232. package/dist/playwright-SQAQ3DZG.js +59 -0
  233. package/dist/plugins-HZBWK3WQ.js +120 -0
  234. package/dist/plugins-I4GD5SZX.js +121 -0
  235. package/dist/posthog-MU5MAJOQ.js +79 -0
  236. package/dist/posthog-RJRRKDWB.js +80 -0
  237. package/dist/salesforce-34FVIJTG.js +82 -0
  238. package/dist/salesforce-3QZ6OFVO.js +83 -0
  239. package/dist/sentry-MCIRMACU.js +111 -0
  240. package/dist/sentry-PIWW46VA.js +110 -0
  241. package/dist/seo-suite-4SQ3I67Q.js +54 -0
  242. package/dist/seo-suite-WJXMA3S4.js +55 -0
  243. package/dist/serve.command-5FMIPQRY.js +10 -0
  244. package/dist/serve.command-DNE6GPMK.js +9 -0
  245. package/dist/shadowob-JELOWHWX.js +1068 -0
  246. package/dist/shadowob-PRSMI5MW.js +1069 -0
  247. package/dist/sherlock-2PKY2E2Y.js +66 -0
  248. package/dist/sherlock-C5ZWPPVT.js +67 -0
  249. package/dist/shopify-GL3NFVGE.js +94 -0
  250. package/dist/shopify-R4G3UXM6.js +93 -0
  251. package/dist/skill-discovery-7INAUP4D.js +77 -0
  252. package/dist/skill-discovery-YPXXV622.js +78 -0
  253. package/dist/state-7MCZBTR5.js +17 -0
  254. package/dist/state-FGOFLFBE.js +18 -0
  255. package/dist/stripe-C22RR4ZS.js +83 -0
  256. package/dist/stripe-LJNPQ3CQ.js +82 -0
  257. package/dist/supabase-IRNQ54FJ.js +98 -0
  258. package/dist/supabase-N4ONFJNQ.js +97 -0
  259. package/dist/taobao-aipaas-LRR4GMO3.js +45 -0
  260. package/dist/taobao-aipaas-RVKORSF4.js +46 -0
  261. package/dist/tapd-3JPVJ7XH.js +46 -0
  262. package/dist/tapd-TMQRSMFG.js +47 -0
  263. package/dist/tencent-ads-IGD33LO7.js +42 -0
  264. package/dist/tencent-ads-UHC6OPBV.js +43 -0
  265. package/dist/tencent-docs-C3A4J3CJ.js +47 -0
  266. package/dist/tencent-docs-O2SC4FHL.js +48 -0
  267. package/dist/tencent-maps-HMMWMNF4.js +37 -0
  268. package/dist/tencent-maps-OQOKHVW2.js +36 -0
  269. package/dist/vercel-KOXDDTHX.js +73 -0
  270. package/dist/vercel-OLNVDWMG.js +74 -0
  271. package/dist/webflow-FULU5Q2I.js +114 -0
  272. package/dist/webflow-OMJKZM54.js +115 -0
  273. package/dist/wechat-miniprogram-skyline-KYCDMQNW.js +74 -0
  274. package/dist/wechat-miniprogram-skyline-VR4FVIQL.js +75 -0
  275. package/dist/wechat-pay-BCMAJ6UW.js +50 -0
  276. package/dist/wechat-pay-YQQKXVUI.js +51 -0
  277. package/dist/wonda-NGWIORYN.js +81 -0
  278. package/dist/wonda-RBABXFNM.js +82 -0
  279. package/dist/wordpress-woocommerce-RDIUTHYT.js +57 -0
  280. package/dist/wordpress-woocommerce-RNA5HB3N.js +58 -0
  281. package/dist/wps-DAEFQHDE.js +47 -0
  282. package/dist/wps-LUWHMZQQ.js +48 -0
  283. package/dist/yuque-HCHTJWNI.js +72 -0
  284. package/dist/yuque-KRH5O74J.js +71 -0
  285. package/images/RUNNERS.md +270 -0
  286. package/images/cc-connect-runner/entrypoint.mjs +311 -0
  287. package/images/claude-runner/Dockerfile +88 -0
  288. package/images/claude-runner/RUNNER.md +222 -0
  289. package/images/claude-runner/entrypoint.mjs +2 -0
  290. package/images/codex-runner/Dockerfile +87 -0
  291. package/images/codex-runner/RUNNER.md +226 -0
  292. package/images/codex-runner/entrypoint.mjs +2 -0
  293. package/images/gemini-runner/Dockerfile +87 -0
  294. package/images/gemini-runner/RUNNER.md +218 -0
  295. package/images/gemini-runner/entrypoint.mjs +2 -0
  296. package/images/hermes-runner/Dockerfile +74 -0
  297. package/images/hermes-runner/RUNNER.md +243 -0
  298. package/images/hermes-runner/entrypoint.mjs +283 -0
  299. package/images/openclaw-runner/Dockerfile +212 -0
  300. package/images/openclaw-runner/RUNNER.md +170 -0
  301. package/images/openclaw-runner/entrypoint.mjs +1113 -0
  302. package/images/openclaw-runner/warm-runtime-deps.mjs +95 -0
  303. package/images/opencode-runner/Dockerfile +87 -0
  304. package/images/opencode-runner/RUNNER.md +202 -0
  305. package/images/opencode-runner/entrypoint.mjs +2 -0
  306. package/package.json +121 -0
  307. package/templates/agent-marketplace-buddy.template.json +131 -0
  308. package/templates/ai-werewolf.template.json +92 -0
  309. package/templates/bmad-method-buddy.template.json +123 -0
  310. package/templates/brain-fix.template.json +92 -0
  311. package/templates/claude-ads-buddy.template.json +123 -0
  312. package/templates/claude-financial-services-buddy.template.json +111 -0
  313. package/templates/claude-seo-buddy.template.json +123 -0
  314. package/templates/code-arena.template.json +92 -0
  315. package/templates/daily-brief.template.json +92 -0
  316. package/templates/e-wife.template.json +92 -0
  317. package/templates/everything-claude-code-buddy.template.json +125 -0
  318. package/templates/financial-freedom.template.json +92 -0
  319. package/templates/gitstory.template.json +92 -0
  320. package/templates/google-workspace-buddy.template.json +88 -0
  321. package/templates/gsd-buddy.template.json +119 -0
  322. package/templates/gstack-buddy.template.json +143 -0
  323. package/templates/gstack.template.json +92 -0
  324. package/templates/little-match-girl.template.json +114 -0
  325. package/templates/lovart-buddy.template.json +110 -0
  326. package/templates/marketingskills-buddy.template.json +102 -0
  327. package/templates/retire-buddy.template.json +92 -0
  328. package/templates/scientific-skills-buddy.template.json +119 -0
  329. package/templates/seomachine-buddy.template.json +113 -0
  330. package/templates/shadow-server-app-demo.template.json +105 -0
  331. package/templates/slavingia-skills-buddy.template.json +102 -0
  332. package/templates/superclaude-buddy.template.json +146 -0
  333. package/templates/superpowers-buddy.template.json +108 -0
  334. package/templates/world-pulse.template.json +92 -0
@@ -0,0 +1,270 @@
1
+ # Cloud Runner Research Index
2
+
3
+ Research date: 2026-05-14.
4
+
5
+ This document is the cross-runner index for the `apps/cloud` runtime refactor.
6
+ It separates runner families by their native process and configuration boundary
7
+ so OpenClaw remains a first-class adapter without becoming the implicit contract
8
+ for every other runner.
9
+
10
+ ## Target runner set
11
+
12
+ Phase 1 keeps only these runners:
13
+
14
+ | Runtime | Target process boundary | Shadow bridge | Per-runner notes |
15
+ | --- | --- | --- | --- |
16
+ | `openclaw` | Native OpenClaw gateway | `@shadowob/openclaw-shadowob` plugin | Remains a first-class OpenClaw adapter. |
17
+ | `claude-code` | `cc-connect` fork -> Claude Code CLI | `cc-connect` ShadowOB platform | No OpenClaw gateway or ACPX in this path. |
18
+ | `codex` | `cc-connect` fork -> Codex CLI | `cc-connect` ShadowOB platform | No OpenClaw gateway or ACPX in this path. |
19
+ | `opencode` | `cc-connect` fork -> OpenCode CLI | `cc-connect` ShadowOB platform | No OpenClaw gateway or ACPX in this path. |
20
+ | `gemini` | `cc-connect` fork -> Gemini CLI | `cc-connect` ShadowOB platform | No OpenClaw gateway or ACPX in this path. |
21
+ | `hermes` | Native Hermes gateway | ShadowOB Hermes platform plugin | New runner; not a `cc-connect` target in phase 1. |
22
+
23
+ Everything else should be removed from Cloud's first-phase runtime surface. The
24
+ `cc-connect` fork still contains more agents, but the Cloud build should produce
25
+ a narrowed binary/image that excludes non-phase-1 agents where build tags allow
26
+ it.
27
+
28
+ ## Current state in this repository
29
+
30
+ `apps/cloud/src/runtimes/*` now owns runtime-specific package builders and
31
+ container layout. `apps/cloud/src/infra/runtime-package.ts` only orchestrates
32
+ plugin extension collection, env/secret splitting, and dispatch to the selected
33
+ runtime adapter. The OpenClaw adapter emits `config.json`; Claude Code, Codex,
34
+ OpenCode, and Gemini emit `cc-connect-config.toml` plus native CLI config files
35
+ through `runtime-files.json`; Hermes emits native Hermes files through the same
36
+ runner file materialization contract.
37
+
38
+ The remaining refactor target is to keep moving plugin APIs away from
39
+ OpenClaw-shaped fragments wherever the capability is not OpenClaw-specific. The
40
+ runtime package layer already consumes runner-neutral ShadowOB runtime
41
+ extensions for cc-connect and Hermes.
42
+
43
+ ## Runtime filesystem baseline
44
+
45
+ All phase-1 runner images now run as the non-root `shadow` user with
46
+ `HOME=/home/shadow`. Runtime state and generated home-scoped config must use
47
+ that home:
48
+
49
+ | Runtime family | Home/state baseline | Compatibility |
50
+ | --- | --- | --- |
51
+ | OpenClaw | `/home/shadow/.openclaw` | `/home/openclaw` is a compatibility symlink only. |
52
+ | cc-connect based | `/home/shadow/.cc-connect` plus the native CLI home config, such as `/home/shadow/.codex` or `/home/shadow/.gemini` | Legacy plugin credential paths should migrate to `/home/shadow`; the symlink exists only for old images/configs. |
53
+ | Hermes | `/home/shadow/.hermes` | `/home/openclaw` is not a first-class Hermes path. |
54
+
55
+ ShadowOB assets are installed in two explicit places:
56
+
57
+ - CLI and helper binaries: `shadowob` and `shadowob-connector` on `PATH`.
58
+ - Runner skills: `/workspace/.agents/skills/shadowob/SKILL.md`, plus native
59
+ homes where the CLI supports a skill directory, such as
60
+ `/home/shadow/.codex/skills/shadowob/SKILL.md`,
61
+ `/home/shadow/.gemini/skills/shadowob/SKILL.md`, and
62
+ `/home/shadow/.hermes/skills/shadowob/SKILL.md`.
63
+ - Slash command index: `/etc/shadowob/slash-commands.json` exists for every
64
+ runner. Each runner owns its own command catalog under
65
+ `apps/cloud/src/runtimes/slash-commands/`; common packaging only serializes
66
+ the selected runner's catalog. Runtime/plugin slash command artifacts are
67
+ additive sources. The runner catalog is loaded first, plugin artifacts are
68
+ loaded afterward in runtime-extension order, and duplicate command names use
69
+ first-wins semantics rather than overwriting an earlier command.
70
+
71
+ ## Slash command catalogs
72
+
73
+ Slash commands are runner-specific. Do not put runtime command lists in shared
74
+ package code.
75
+
76
+ | Runtime | Catalog source | Current Cloud injection |
77
+ | --- | --- | --- |
78
+ | OpenClaw | https://docs.openclaw.ai/tools/slash-commands | Injects the official OpenClaw command list with `dispatch=passthrough`, so OpenClaw receives the original `/...` message. |
79
+ | Claude Code | https://code.claude.com/docs/en/commands | Injects non-conflicting Claude Code command names into cc-connect's ShadowOB `slash_commands_path`; cc-connect-owned names such as `/model`, `/status`, `/help`, `/config`, and `/compact` remain cc-connect management commands. |
80
+ | Codex | https://developers.openai.com/codex/cli/slash-commands | Injects non-conflicting Codex command names such as `/permissions`, `/init`, `/review`, `/mcp`, `/agent`, `/apps`, `/plugins`, `/logout`, `/clear`, `/plan`, and `/statusline`; cc-connect owns `/new`, `/model`, `/status`, `/diff`, `/help`, `/stop`, `/ps`, and `/compact`. |
81
+ | OpenCode | https://opencode.ai/docs/tui/ | Injects non-conflicting OpenCode commands such as `/connect`, `/details`, `/editor`, `/export`, `/init`, `/models`, `/redo`, `/share`, `/themes`, `/thinking`, `/undo`, and `/unshare`; cc-connect owns session/control names that overlap. |
82
+ | Gemini CLI | https://github.com/google-gemini/gemini-cli/blob/main/docs/reference/commands.md | Injects non-conflicting Gemini commands such as `/about`, `/agents`, `/auth`, `/bug`, `/chat`, `/hooks`, `/ide`, `/init`, `/mcp`, `/permissions`, `/plan`, `/settings`, `/stats`, `/tools`, and `/vim`; cc-connect owns overlapping management commands. |
83
+ | Hermes | https://github.com/NousResearch/hermes-agent/blob/main/website/docs/reference/slash-commands.md | Injects Hermes messaging commands such as `/model`, `/goal`, `/queue`, `/steer`, `/background`, `/approve`, `/deny`, and `/commands` through the Hermes ShadowOB plugin path. Hermes documents `/cron` as CLI-only, so Cloud should not expose it in Shadow until the Hermes gateway supports it safely. |
84
+
85
+ The cc-connect fork also publishes its own universal bot commands from the
86
+ engine (`/new`, `/list`, `/switch`, `/model`, `/reasoning`, `/mode`, `/cron`,
87
+ `/provider`, `/stop`, `/help`, and related aliases). Native CLI command names
88
+ that collide with those are intentionally excluded from the local per-runner
89
+ catalog to avoid stealing cc-connect control flow.
90
+
91
+ ## Adapter boundary required by the refactor
92
+
93
+ The runtime adapter should emit native runner artifacts instead of mutating one
94
+ OpenClaw config tree for every runtime:
95
+
96
+ | Runtime family | Primary generated config | Native config surfaces to preserve |
97
+ | --- | --- | --- |
98
+ | OpenClaw | OpenClaw config JSON/JSON5/YAML | `agents`, `models`, `skills`, `mcp`, `cron`, `hooks`, `channels`, `plugins`, `gateway`. |
99
+ | cc-connect based | `cc-connect` `config.toml` plus native CLI config files | `[[projects]]`, `[projects.agent]`, provider refs, ShadowOB platform config, and runtime-specific files such as `.claude/settings.json`, `.codex/config.toml`, `opencode.json`, or `.gemini/settings.json`. |
100
+ | Hermes | Hermes `config.yaml`, `.env`, plugin files | `platforms.shadowob`, `plugins.enabled`, providers/models, skills, MCP, cron, hooks, sessions, logs. |
101
+
102
+ The schema should follow that split. `AgentDeployment.configuration.openclaw`
103
+ can remain for the OpenClaw runner, but it should not be the only exit path. Add
104
+ runtime-specific configuration sections for at least `claude`, `codex`,
105
+ `opencode`, `gemini`, `ccConnect`, and `hermes`, then have each adapter decide
106
+ which files and environment variables belong in the runtime package.
107
+
108
+ ## Schema and type anchors
109
+
110
+ Use these anchors in implementation and tests. Do not invent hand-maintained
111
+ schemas when a runtime publishes one.
112
+
113
+ | Runtime | Config format | Schema URL or source | Type anchor |
114
+ | --- | --- | --- | --- |
115
+ | OpenClaw | JSON5/YAML-compatible config object | No fixed public schema URL found; official source is `openclaw config schema` and gateway `config.schema.lookup`. | OpenClaw CLI live schema plus Cloud `OpenClawConfig` types. |
116
+ | Claude Code | JSON settings | `https://json.schemastore.org/claude-code-settings.json` is documented by Claude Code as its official settings schema, with a lag warning. | Settings docs table, `~/.claude.json` global config, `.mcp.json`, `.claude/agents/*.md`. |
117
+ | Codex | TOML | Generated schema source in the official repo: `https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json`; docs source is the Codex config reference. | Codex config reference key/type table plus generated schema; generated TOML must parse through Codex. |
118
+ | OpenCode | JSON/JSONC | `https://opencode.ai/config.json`; TUI config also uses `https://opencode.ai/tui.json`. | OpenCode config schema and docs for permissions, MCP, agents, commands, skills. |
119
+ | Gemini CLI | JSON settings | `https://raw.githubusercontent.com/google-gemini/gemini-cli/main/schemas/settings.schema.json`. | Gemini CLI settings schema plus docs reference. |
120
+ | Hermes | YAML | No fixed public JSON Schema URL found; web dashboard exposes `GET /api/config/schema` from `DEFAULT_CONFIG`. | Hermes config docs and runtime-discovered schema endpoint. |
121
+ | cc-connect | TOML | No public schema URL found; source of truth is Go config structs in the fork. | `../cc-connect/config/config.go`, agent option structs, and `config.example.toml`. |
122
+
123
+ Validation expectation:
124
+
125
+ - JSON/JSONC configs must be validated with the published schema where one
126
+ exists.
127
+ - TOML/YAML configs must be parsed and type-checked through the owning runtime
128
+ or source type, not by ad hoc string assertions.
129
+ - Runtime adapters must snapshot the generated files in tests so schema drift is
130
+ visible in code review.
131
+
132
+ ## Provider and authentication matrix
133
+
134
+ The model/provider adapter must be runner-native. Do not translate every
135
+ provider into OpenClaw `models.providers`, and do not assume all CLIs can use a
136
+ headless subscription login.
137
+
138
+ | Runtime | Headless-safe auth | Subscription/OAuth auth | Custom provider/gateway support | Adapter requirement |
139
+ | --- | --- | --- | --- | --- |
140
+ | OpenClaw | API keys through OpenClaw provider config and env-backed secrets. | Depends on provider/plugin; not the baseline for non-OpenClaw runners. | OpenClaw provider/failover config. | Keep OpenClaw provider config only in the OpenClaw adapter. |
141
+ | Claude Code | `ANTHROPIC_API_KEY`, Bedrock/Vertex/Foundry envs, or gateway token envs. | Claude Pro/Max/Team/Enterprise login is interactive/user-account based and should not be assumed in a fresh container. | `ANTHROPIC_BASE_URL` routes requests; gateway model discovery and `ANTHROPIC_CUSTOM_MODEL_OPTION` handle custom model IDs. | Generate Claude `env`/settings and cc-connect options; do not write OpenClaw model config. |
142
+ | Codex | API key auth with `OPENAI_API_KEY` plus `preferred_auth_method = "apikey"` when needed. Custom providers use `[model_providers.<id>]`, `base_url`, `env_key`, optional auth command, and Responses API `wire_api`. | ChatGPT Plus/Pro/Business/Edu/Enterprise login can work interactively but is not suitable as the only Kubernetes bootstrap path. | Built-in `openai`, local `ollama`/`lmstudio`, and custom provider tables. | Generate `$CODEX_HOME/config.toml` with provider/profile tables and keep `auth.json` out of ConfigMaps. |
143
+ | OpenCode | Provider API keys stored by `/connect` in `~/.local/share/opencode/auth.json`, or `provider.<id>.options.apiKey` only when Cloud intentionally materializes a secret file. | Provider-specific OAuth flows may exist through `/connect`; not a generic headless default. | `provider.<id>` supports custom AI SDK packages, `baseURL`, headers, and model catalogs, including OpenAI-compatible APIs. | Generate `opencode.json` provider blocks and mount credentials separately. |
144
+ | Gemini CLI | `GEMINI_API_KEY` for AI Studio, `GOOGLE_API_KEY` for Vertex API-key mode, or Vertex ADC/service-account material. | Google login is recommended for Pro/Ultra subscriptions but needs a browser/localhost callback, so it is not a default container bootstrap. | Vertex AI project/location and API-key/ADC modes; no generic OpenAI-compatible provider in Gemini CLI settings. | Generate `.gemini/settings.json` plus explicit env requirements; unset conflicting API-key vars for ADC. |
145
+ | Hermes | `.env` keys such as `OPENROUTER_API_KEY`, `AI_GATEWAY_API_KEY`, provider-specific keys, or configured OpenAI-compatible `model.base_url`. | `hermes model` supports Nous Portal OAuth/subscription, Codex ChatGPT OAuth, GitHub Copilot OAuth, and Claude OAuth paths. | Hermes provider routing/fallback, OpenRouter, AI Gateway, and OpenAI-compatible endpoints. | Generate `~/.hermes/config.yaml` and `~/.hermes/.env` natively; keep gateway/platform auth separate from model auth. |
146
+
147
+ ## Security and audit dimensions
148
+
149
+ Every runner adapter must explicitly map these dimensions:
150
+
151
+ | Dimension | Required adapter output |
152
+ | --- | --- |
153
+ | Permission mode | Native allow/ask/deny, approval, sandbox, or yolo/bypass settings. |
154
+ | Tool surface | Explicit enabled/disabled tools, MCP tool allowlists, skill loading policy, and subagent delegation policy. |
155
+ | Filesystem | Workspace root, external directory access, secret path denies, sandbox/bind mount policy, and generated config file permissions. |
156
+ | Network | Web fetch/search controls, MCP remote URLs, provider base URLs, domain allow/deny lists where supported. |
157
+ | Cost controls | Model selection, small/auxiliary model routing, max turns, context/tool-output summarization, cron frequency, and provider allowlist. |
158
+ | Audit and logs | Native runtime logs, cc-connect logs, OpenTelemetry or telemetry toggles, token/event metadata, and log redaction behavior. |
159
+ | Secrets | Secret env separation, file credential materialization, allowlisted env passthrough, and proof that generated logs/config maps do not contain raw tokens. |
160
+
161
+ ## Runner documentation
162
+
163
+ - [OpenClaw runner](./openclaw-runner/RUNNER.md)
164
+ - [Claude Code runner](./claude-runner/RUNNER.md)
165
+ - [Codex runner](./codex-runner/RUNNER.md)
166
+ - [OpenCode runner](./opencode-runner/RUNNER.md)
167
+ - [Gemini runner](./gemini-runner/RUNNER.md)
168
+ - [Hermes runner](./hermes-runner/RUNNER.md)
169
+
170
+ ## cc-connect fork notes
171
+
172
+ The Shadow connector package currently pins the fork in
173
+ `packages/connector/src/cc-connect-fork.ts`:
174
+
175
+ - repository: `buggyblues/cc-connect`
176
+ - package version: `1.3.3-beta.5`
177
+ - pinned ref: `63b5d59`
178
+
179
+ Local fork research used `../cc-connect`. Important implementation facts:
180
+
181
+ - The fork has first-class agents named `claudecode`, `codex`, `gemini`, and
182
+ `opencode`.
183
+ - Project config uses `[[projects]]`, `[projects.agent] type = "..."`, and
184
+ `[projects.agent.options] work_dir = "..."`.
185
+ - Global providers can be restricted by `agent_types`.
186
+ - The daemon uses `data_dir` for session state and `CC_LOG_FILE` for rotating
187
+ daemon logs when run as a daemon.
188
+ - The fork contains other agents (`cursor`, `devin`, `iflow`, `kimi`, `qoder`,
189
+ etc.); Cloud phase 1 should not expose them.
190
+
191
+ ## Shared migration checklist
192
+
193
+ - Runtime adapters now declare a runtime family and the package generator emits
194
+ multiple native config files where needed.
195
+ - `buildOpenClawConfig` is used only by the OpenClaw runtime package path.
196
+ - The cc-connect runner images build the ShadowOB fork and narrow the exposed
197
+ agent set to `claudecode`, `codex`, `opencode`, and `gemini`.
198
+ - Per-runtime native config files are generated instead of translating
199
+ non-OpenClaw runtimes into OpenClaw `agents.defaults`.
200
+ - Logs and session paths stay native; Cloud normalizes only collection labels.
201
+ - `hermes` is now present in the runtime schema, loader, runner package, and
202
+ image directory.
203
+
204
+ ## Adapter unit tests
205
+
206
+ The multi-runner adapter tests cover:
207
+
208
+ - Each adapter emits the expected config files, env vars, secret refs, workspace
209
+ files, and plugin/runtime resources.
210
+ - Non-OpenClaw adapters do not emit `/etc/openclaw/config.json`, OpenClaw ACPX
211
+ config, or OpenClaw plugin fragments.
212
+ - OpenClaw emits only native OpenClaw artifacts and still supports existing
213
+ plugin runtime extensions.
214
+ - Published JSON schemas validate generated OpenCode, Gemini, and Claude
215
+ settings where applicable.
216
+ - TOML/YAML outputs parse successfully and preserve expected scalar/list/table
217
+ types.
218
+ - Permission mappings are fail-closed: deny beats allow, yolo/bypass is disabled
219
+ unless explicitly requested, external directory and network access remain
220
+ restricted by default.
221
+ - Secrets are split into secret data and never appear in config maps, logs, or
222
+ snapshot fixtures.
223
+ - Cost/audit settings are present for model, small/auxiliary model, telemetry,
224
+ context/tool-output budget, cron frequency, and log paths where supported.
225
+
226
+ ## Smoke tests
227
+
228
+ The current smoke tests inspect generated runtime packages, parsed native
229
+ configs, secret separation, runtime file materialization inputs, and workspace
230
+ writes for every runner. Full Docker image build-and-run smoke should still be
231
+ run before publishing new image tags.
232
+
233
+ Target container smoke assertions:
234
+
235
+ Add smoke tests per runner image after implementation:
236
+
237
+ | Runner | Smoke assertions |
238
+ | --- | --- |
239
+ | OpenClaw | Container starts, `openclaw config schema` works, `/etc/openclaw/config.json` is valid, ShadowOB plugin loads, workspace writes behave as configured, logs are redacted. |
240
+ | Claude Code | cc-connect starts with `type = "claudecode"`, Claude CLI is on PATH, `.claude/settings.json` validates against the settings schema, no OpenClaw files exist, workspace write and deny rules are honored. |
241
+ | Codex | cc-connect starts with `type = "codex"`, Codex CLI is on PATH, `$CODEX_HOME/config.toml` parses, `.codex` and `.agents/skills` are written correctly, logs land outside `/var/log/openclaw`. |
242
+ | OpenCode | cc-connect starts with `type = "opencode"`, OpenCode CLI is on PATH, `opencode.json` validates against `https://opencode.ai/config.json`, permission and MCP objects survive startup. |
243
+ | Gemini | cc-connect starts with `type = "gemini"`, Gemini CLI is on PATH, `.gemini/settings.json` validates against the official schema, telemetry/log settings resolve, workspace trust can be supplied headlessly. |
244
+ | Hermes | `hermes gateway` starts, ShadowOB plugin is enabled, `config.yaml` loads, `GET /api/config/schema` or equivalent schema endpoint is reachable when dashboard/API is enabled, logs and cron dirs are created. |
245
+
246
+ Smoke tests should inspect the container filesystem and process logs, not only
247
+ assert that the process exits successfully.
248
+
249
+ ## Primary sources
250
+
251
+ - OpenClaw docs index and gateway configuration:
252
+ https://docs.openclaw.ai/llms.txt,
253
+ https://docs.openclaw.ai/gateway/configuration
254
+ - Claude Code docs: https://code.claude.com/docs/en/settings
255
+ - Claude Code model/env docs:
256
+ https://code.claude.com/docs/en/model-config,
257
+ https://code.claude.com/docs/en/env-vars
258
+ - Codex docs: https://developers.openai.com/codex/config-basic
259
+ - Codex CLI/config reference:
260
+ https://developers.openai.com/codex/cli,
261
+ https://developers.openai.com/codex/config-reference
262
+ - OpenCode docs: https://opencode.ai/docs/config
263
+ - OpenCode providers: https://dev.opencode.ai/docs/providers/
264
+ - Gemini CLI docs:
265
+ https://github.com/google-gemini/gemini-cli/blob/main/docs/reference/configuration.md,
266
+ https://google-gemini.github.io/gemini-cli/docs/get-started/authentication.html
267
+ - Hermes Agent docs:
268
+ https://hermes-agent.nousresearch.com/docs/user-guide/configuration,
269
+ https://hermes-agent.nousresearch.com/docs/integrations/providers
270
+ - cc-connect fork source: https://github.com/buggyblues/cc-connect
@@ -0,0 +1,311 @@
1
+ /**
2
+ * cc-connect runner entrypoint.
3
+ *
4
+ * Native runner flow:
5
+ * 1. Read runtime-files.json from the mounted ConfigMap.
6
+ * 2. Materialize cc-connect, CLI, skills, and workspace files.
7
+ * 3. Resolve ${ENV_VAR} placeholders from Kubernetes Secret/env.
8
+ * 4. Start cc-connect with the generated config.
9
+ */
10
+
11
+ import { spawn, spawnSync } from 'node:child_process'
12
+ import {
13
+ chmodSync,
14
+ cpSync,
15
+ existsSync,
16
+ mkdirSync,
17
+ readdirSync,
18
+ readFileSync,
19
+ statSync,
20
+ writeFileSync,
21
+ } from 'node:fs'
22
+ import { createServer } from 'node:http'
23
+ import { dirname, join, resolve } from 'node:path'
24
+
25
+ const RUNTIME_NAME = process.env.SHADOW_RUNNER_NAME ?? 'cc-connect-runner'
26
+ const CONFIG_MOUNT = process.env.SHADOW_RUNNER_CONFIG_MOUNT ?? '/etc/openclaw'
27
+ const RUNTIME_FILES_PATH = join(CONFIG_MOUNT, 'runtime-files.json')
28
+ const RUNTIME_EXTENSIONS_PATH = join(CONFIG_MOUNT, 'runtime-extensions.json')
29
+ const RUNNER_HOME = process.env.HOME ?? '/home/shadow'
30
+ const CC_CONNECT_CONFIG_PATH =
31
+ process.env.CC_CONNECT_CONFIG_PATH ?? join(RUNNER_HOME, '.cc-connect/config.toml')
32
+ const HEALTH_PORT = Number.parseInt(
33
+ process.env.SHADOW_RUNNER_HEALTH_PORT ??
34
+ process.env.OPENCLAW_GATEWAY_PORT ??
35
+ process.env.PORT ??
36
+ '3100',
37
+ 10,
38
+ )
39
+ const LOG_DIR = process.env.SHADOW_RUNNER_LOG_DIR ?? '/var/log/shadowob'
40
+ const ALLOWED_FILE_ROOTS = [RUNNER_HOME, '/home/openclaw', '/workspace', '/etc/shadowob']
41
+
42
+ let ready = false
43
+ let child = null
44
+
45
+ const KEY_PATTERNS = [
46
+ /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g,
47
+ /\bsk-proj-[A-Za-z0-9_-]{20,}\b/g,
48
+ /\bsk-[A-Za-z0-9]{20,}\b/g,
49
+ /\bgsk_[A-Za-z0-9]{20,}\b/g,
50
+ /\bBearer\s+[A-Za-z0-9._-]{20,}/g,
51
+ /\bshadow-[A-Za-z0-9._-]{20,}/g,
52
+ ]
53
+
54
+ function redact(line) {
55
+ let result = line
56
+ for (const pattern of KEY_PATTERNS) {
57
+ pattern.lastIndex = 0
58
+ result = result.replace(pattern, '[REDACTED]')
59
+ }
60
+ return result
61
+ }
62
+
63
+ function loadJson(path, fallback) {
64
+ if (!existsSync(path)) return fallback
65
+ const raw = readFileSync(path, 'utf-8')
66
+ const parsed = JSON.parse(raw)
67
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return fallback
68
+ return parsed
69
+ }
70
+
71
+ function resolveEnvPlaceholders(value) {
72
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, key) => process.env[key] ?? '')
73
+ }
74
+
75
+ function assertAllowedPath(path) {
76
+ const absolute = resolve(path)
77
+ if (!ALLOWED_FILE_ROOTS.some((root) => absolute === root || absolute.startsWith(`${root}/`))) {
78
+ throw new Error(`Refusing to materialize runtime file outside allowed roots: ${path}`)
79
+ }
80
+ return absolute
81
+ }
82
+
83
+ function modeForPath(path) {
84
+ if (path.endsWith('/.env') || path.endsWith('.toml') || path.endsWith('.json')) return 0o600
85
+ return 0o644
86
+ }
87
+
88
+ function materializeRuntimeFiles() {
89
+ const files = loadJson(RUNTIME_FILES_PATH, {})
90
+ for (const [path, content] of Object.entries(files)) {
91
+ if (typeof content !== 'string') continue
92
+ const absolute = assertAllowedPath(path)
93
+ const mode = modeForPath(absolute)
94
+ mkdirSync(dirname(absolute), { recursive: true })
95
+ writeFileSync(absolute, resolveEnvPlaceholders(content), { encoding: 'utf-8', mode })
96
+ chmodSync(absolute, mode)
97
+ console.log(`[entrypoint] wrote ${absolute}`)
98
+ }
99
+ }
100
+
101
+ function materializeCredentialFiles() {
102
+ const runtimeExtensions = loadJson(RUNTIME_EXTENSIONS_PATH, {})
103
+ const files = Array.isArray(runtimeExtensions.credentialFiles)
104
+ ? runtimeExtensions.credentialFiles
105
+ : []
106
+ for (const file of files) {
107
+ if (!file || typeof file.envKey !== 'string' || typeof file.path !== 'string') continue
108
+ const value = process.env[file.envKey]
109
+ if (!value) continue
110
+ const absolute = assertAllowedPath(file.path)
111
+ const mode =
112
+ typeof file.mode === 'string' && /^[0-7]{3,4}$/.test(file.mode)
113
+ ? Number.parseInt(file.mode, 8)
114
+ : 0o600
115
+ mkdirSync(dirname(absolute), { recursive: true })
116
+ writeFileSync(absolute, value, { encoding: 'utf-8', mode })
117
+ chmodSync(absolute, mode)
118
+ console.log(`[entrypoint] materialized credential file ${absolute}`)
119
+ }
120
+ }
121
+
122
+ function entriesWithMarker(root, marker) {
123
+ if (!existsSync(root)) return []
124
+ if (existsSync(join(root, marker))) return [{ source: root, name: root.split('/').pop() }]
125
+ return readdirSync(root)
126
+ .map((name) => ({ source: join(root, name), name }))
127
+ .filter((entry) => {
128
+ try {
129
+ return statSync(entry.source).isDirectory() && existsSync(join(entry.source, marker))
130
+ } catch {
131
+ return false
132
+ }
133
+ })
134
+ }
135
+
136
+ function subagentEntries(root) {
137
+ if (!existsSync(root)) return []
138
+ return readdirSync(root)
139
+ .map((name) => ({ source: join(root, name), name }))
140
+ .filter((entry) => {
141
+ try {
142
+ const stat = statSync(entry.source)
143
+ if (stat.isFile()) return entry.name.endsWith('.md')
144
+ return stat.isDirectory()
145
+ } catch {
146
+ return false
147
+ }
148
+ })
149
+ }
150
+
151
+ function copyIfMissing(source, destination) {
152
+ if (existsSync(destination)) return false
153
+ mkdirSync(dirname(destination), { recursive: true })
154
+ cpSync(source, destination, { recursive: true })
155
+ return true
156
+ }
157
+
158
+ function isAllowedPluginAssetRoot(path) {
159
+ const absolute = resolve(path)
160
+ return [RUNNER_HOME, '/home/openclaw', '/workspace'].some(
161
+ (root) => absolute === root || absolute.startsWith(`${root}/`),
162
+ )
163
+ }
164
+
165
+ function materializePluginRuntimeAssets() {
166
+ const runtimeExtensions = loadJson(RUNTIME_EXTENSIONS_PATH, {})
167
+ const skillRoots = Array.isArray(runtimeExtensions.skillSources)
168
+ ? runtimeExtensions.skillSources
169
+ .map((source) => (typeof source?.targetPath === 'string' ? source.targetPath : undefined))
170
+ .filter((path) => path && isAllowedPluginAssetRoot(path))
171
+ .filter(Boolean)
172
+ : []
173
+ const subagentRoots = Array.isArray(runtimeExtensions.subagentSources)
174
+ ? runtimeExtensions.subagentSources
175
+ .map((source) => (typeof source?.targetPath === 'string' ? source.targetPath : undefined))
176
+ .filter((path) => path && isAllowedPluginAssetRoot(path))
177
+ .filter(Boolean)
178
+ : []
179
+ const skillDestinations = [
180
+ '/workspace/.agents/skills',
181
+ '/workspace/.claude/skills',
182
+ '/workspace/.opencode/skills',
183
+ join(RUNNER_HOME, '.codex/skills'),
184
+ join(RUNNER_HOME, '.gemini/skills'),
185
+ ]
186
+ const subagentDestinations = [
187
+ '/workspace/.agents/agents',
188
+ '/workspace/.claude/agents',
189
+ '/workspace/.opencode/agents',
190
+ join(RUNNER_HOME, '.codex/agents'),
191
+ join(RUNNER_HOME, '.gemini/agents'),
192
+ ]
193
+
194
+ for (const root of skillRoots) {
195
+ for (const entry of entriesWithMarker(root, 'SKILL.md')) {
196
+ for (const destinationRoot of skillDestinations) {
197
+ if (copyIfMissing(entry.source, join(destinationRoot, entry.name))) {
198
+ console.log(`[entrypoint] mirrored plugin skill ${entry.name} to ${destinationRoot}`)
199
+ }
200
+ }
201
+ }
202
+ }
203
+ for (const root of subagentRoots) {
204
+ for (const entry of subagentEntries(root)) {
205
+ for (const destinationRoot of subagentDestinations) {
206
+ if (copyIfMissing(entry.source, join(destinationRoot, entry.name))) {
207
+ console.log(`[entrypoint] mirrored plugin subagent ${entry.name} to ${destinationRoot}`)
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ function startHealthServer() {
215
+ const server = createServer((req, res) => {
216
+ if (req.url === '/health' || req.url === '/ready') {
217
+ res.writeHead(ready ? 200 : 503, { 'Content-Type': 'application/json' })
218
+ res.end(JSON.stringify({ status: ready ? 'ready' : 'starting', runtime: RUNTIME_NAME }))
219
+ return
220
+ }
221
+ if (req.url === '/live') {
222
+ res.writeHead(200, { 'Content-Type': 'application/json' })
223
+ res.end(JSON.stringify({ status: 'live', runtime: RUNTIME_NAME }))
224
+ return
225
+ }
226
+ res.writeHead(404)
227
+ res.end()
228
+ })
229
+ server.listen(HEALTH_PORT, '0.0.0.0', () => {
230
+ console.log(`[entrypoint] health server listening on :${HEALTH_PORT}`)
231
+ })
232
+ return server
233
+ }
234
+
235
+ function verifyBinary(command, args) {
236
+ const result = spawnSync(command, args, {
237
+ stdio: ['ignore', 'pipe', 'pipe'],
238
+ timeout: 10_000,
239
+ })
240
+ if (result.status !== 0) {
241
+ throw new Error(`${command} ${args.join(' ')} failed: ${result.stderr?.toString() ?? ''}`)
242
+ }
243
+ }
244
+
245
+ function startCcConnect() {
246
+ if (!existsSync(CC_CONNECT_CONFIG_PATH)) {
247
+ throw new Error(`Missing generated cc-connect config: ${CC_CONNECT_CONFIG_PATH}`)
248
+ }
249
+
250
+ mkdirSync(LOG_DIR, { recursive: true })
251
+ const env = {
252
+ ...process.env,
253
+ CC_LOG_FILE: process.env.CC_LOG_FILE ?? join(LOG_DIR, 'cc-connect.log'),
254
+ }
255
+ const proc = spawn('cc-connect', ['--config', CC_CONNECT_CONFIG_PATH], {
256
+ env,
257
+ stdio: ['ignore', 'pipe', 'pipe'],
258
+ cwd: '/workspace',
259
+ })
260
+ child = proc
261
+ proc.stdout.on('data', (chunk) => process.stdout.write(redact(chunk.toString())))
262
+ proc.stderr.on('data', (chunk) => process.stderr.write(redact(chunk.toString())))
263
+ proc.on('exit', (code, signal) => {
264
+ ready = false
265
+ console.error(
266
+ `[entrypoint] cc-connect exited code=${code ?? 'null'} signal=${signal ?? 'null'}`,
267
+ )
268
+ process.exit(code ?? 1)
269
+ })
270
+ setTimeout(() => {
271
+ if (!proc.killed) ready = true
272
+ }, 3000)
273
+ }
274
+
275
+ function setupSignals(server) {
276
+ const shutdown = (signal) => {
277
+ ready = false
278
+ server.close()
279
+ if (child && !child.killed) child.kill(signal)
280
+ setTimeout(() => process.exit(0), 5000).unref()
281
+ }
282
+ process.on('SIGTERM', () => shutdown('SIGTERM'))
283
+ process.on('SIGINT', () => shutdown('SIGINT'))
284
+ }
285
+
286
+ async function main() {
287
+ console.log(`[entrypoint] ${RUNTIME_NAME} starting`)
288
+ mkdirSync('/workspace', { recursive: true })
289
+ mkdirSync('/etc/shadowob', { recursive: true })
290
+ materializeRuntimeFiles()
291
+ materializeCredentialFiles()
292
+ materializePluginRuntimeAssets()
293
+
294
+ if (process.env.SHADOW_RUNNER_VALIDATE_ONLY === '1') {
295
+ verifyBinary('cc-connect', ['--help'])
296
+ verifyBinary('shadowob', ['--help'])
297
+ verifyBinary('shadowob-connector', ['--help'])
298
+ ready = true
299
+ console.log('[entrypoint] validation completed')
300
+ return
301
+ }
302
+
303
+ const server = startHealthServer()
304
+ setupSignals(server)
305
+ startCcConnect()
306
+ }
307
+
308
+ main().catch((err) => {
309
+ console.error('[entrypoint] Fatal:', err)
310
+ process.exit(1)
311
+ })
@@ -0,0 +1,88 @@
1
+ # syntax=docker/dockerfile:1.7
2
+
3
+ # ─── Claude Runner ────────────────────────────────────────────────────────
4
+ # Native cc-connect runner for Claude Code.
5
+ #
6
+ # Build from the repository root:
7
+ # docker build -t ghcr.io/buggyblues/claude-runner:latest \
8
+ # -f apps/cloud/images/claude-runner/Dockerfile .
9
+ # ──────────────────────────────────────────────────────────────────────────
10
+
11
+ FROM golang:1.25-bookworm AS cc-builder
12
+
13
+ ARG CC_CONNECT_REF=63b5d59127b3004bc7002f2d51892b1f2a91ea83
14
+ ARG CC_CONNECT_REPO=https://github.com/buggyblues/cc-connect.git
15
+
16
+ WORKDIR /build
17
+
18
+ RUN apt-get update && \
19
+ apt-get install -y --no-install-recommends ca-certificates git && \
20
+ rm -rf /var/lib/apt/lists/*
21
+
22
+ RUN git clone --depth 1 "${CC_CONNECT_REPO}" /tmp/cc-connect && \
23
+ cd /tmp/cc-connect && \
24
+ git fetch --depth 1 origin "${CC_CONNECT_REF}" && \
25
+ git checkout "${CC_CONNECT_REF}" && \
26
+ CGO_ENABLED=0 go build \
27
+ -tags "no_web no_acp no_cursor no_devin no_iflow no_kimi no_qoder no_feishu no_telegram no_discord no_slack no_dingtalk no_wecom no_weixin no_qq no_qqbot no_line no_weibo" \
28
+ -ldflags "-s -w" \
29
+ -o /build/cc-connect ./cmd/cc-connect
30
+
31
+ FROM node:22-bookworm-slim AS node-deps
32
+
33
+ WORKDIR /build
34
+
35
+ RUN npm init -y && \
36
+ npm install --no-audit --fund=false \
37
+ @anthropic-ai/claude-code@latest \
38
+ @shadowob/cli@latest \
39
+ @shadowob/connector@latest
40
+
41
+ FROM node:22-bookworm-slim AS runner
42
+
43
+ LABEL org.opencontainers.image.source="https://github.com/nicepkg/shadow"
44
+ LABEL org.opencontainers.image.description="Shadow Cloud Claude Runner (cc-connect + Claude Code)"
45
+
46
+ RUN apt-get update && \
47
+ apt-get install -y --no-install-recommends ca-certificates curl git tini && \
48
+ rm -rf /var/lib/apt/lists/*
49
+
50
+ RUN userdel -r node 2>/dev/null || true; \
51
+ groupdel node 2>/dev/null || true; \
52
+ groupadd -g 1000 shadow; \
53
+ useradd -u 1000 -g shadow -m -d /home/shadow -s /usr/sbin/nologin shadow
54
+
55
+ WORKDIR /app
56
+ RUN mkdir -p /home/shadow/.cc-connect /home/shadow/.claude /etc/openclaw /etc/shadowob \
57
+ /var/log/shadowob /workspace /tmp/npm-cache && \
58
+ ln -s /home/shadow /home/openclaw && \
59
+ chown -R shadow:shadow /home/shadow /etc/shadowob /var/log/shadowob \
60
+ /workspace /tmp/npm-cache /app
61
+
62
+ COPY --from=node-deps --chown=shadow:shadow /build/node_modules ./node_modules
63
+ COPY --from=node-deps --chown=shadow:shadow /build/package.json ./package.json
64
+ COPY --from=cc-builder /build/cc-connect /usr/local/bin/cc-connect
65
+
66
+ RUN ln -s /app/node_modules/.bin/claude /usr/local/bin/claude && \
67
+ ln -s /app/node_modules/.bin/shadowob /usr/local/bin/shadowob && \
68
+ ln -s /app/node_modules/.bin/shadowob-connector /usr/local/bin/shadowob-connector
69
+
70
+ COPY --chown=shadow:shadow apps/cloud/images/cc-connect-runner/entrypoint.mjs /app/entrypoint.mjs
71
+
72
+ HEALTHCHECK --interval=15s --timeout=5s --start-period=30s --retries=3 \
73
+ CMD curl -f http://localhost:3100/health || exit 1
74
+
75
+ EXPOSE 3100
76
+
77
+ ENV NODE_ENV=production
78
+ ENV HOME=/home/shadow
79
+ ENV SHADOW_RUNNER_HEALTH_PORT=3100
80
+ ENV OPENCLAW_NO_RESPAWN=1
81
+ ENV SHADOW_RUNNER_NAME=claude-runner
82
+ ENV CLAUDE_CONFIG_DIR=/home/shadow/.claude
83
+ ENV npm_config_cache=/tmp/npm-cache
84
+
85
+ USER shadow
86
+
87
+ ENTRYPOINT ["tini", "--"]
88
+ CMD ["node", "/app/entrypoint.mjs"]