@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.
- package/README.md +509 -0
- package/dist/agent-browser-CERTMCDL.js +117 -0
- package/dist/agent-browser-CIRZRIY4.js +118 -0
- package/dist/agent-pack-LF3O5TR4.js +1236 -0
- package/dist/agent-pack-RQT27V7R.js +1235 -0
- package/dist/airtable-BG2Q75G2.js +82 -0
- package/dist/airtable-JCQXFM5D.js +83 -0
- package/dist/alipay-MZX2XCDB.js +52 -0
- package/dist/alipay-TZQI34RB.js +51 -0
- package/dist/amap-5RQB3VGC.js +45 -0
- package/dist/amap-KPCLZYYL.js +44 -0
- package/dist/atlassian-LGOEWYC7.js +54 -0
- package/dist/atlassian-TVS2A4IU.js +55 -0
- package/dist/baidu-appbuilder-6UMESXHW.js +41 -0
- package/dist/baidu-appbuilder-QRRL3ETM.js +42 -0
- package/dist/baidu-maps-HEPMVP5D.js +44 -0
- package/dist/baidu-maps-HXC4FBVP.js +45 -0
- package/dist/baidu-netdisk-G5Q6B5NH.js +45 -0
- package/dist/baidu-netdisk-RS2K5W2M.js +44 -0
- package/dist/baidu-smartprogram-EWTK5WKK.js +41 -0
- package/dist/baidu-smartprogram-JHD3XWF6.js +40 -0
- package/dist/browserbase-IUIYVYI7.js +67 -0
- package/dist/browserbase-JFO2PCIA.js +68 -0
- package/dist/canva-3YOFL7JS.js +62 -0
- package/dist/canva-FMYN65SM.js +61 -0
- package/dist/chunk-6P2K6QZR.js +529 -0
- package/dist/chunk-7VMRQ7MG.js +90 -0
- package/dist/chunk-AD3JTIU3.js +17 -0
- package/dist/chunk-BF6CV2Y4.js +64 -0
- package/dist/chunk-CTNUKOQE.js +439 -0
- package/dist/chunk-EEFMJYKB.js +97 -0
- package/dist/chunk-EJKFQ35I.js +739 -0
- package/dist/chunk-HUICDC56.js +62 -0
- package/dist/chunk-JUPAE5IA.js +527 -0
- package/dist/chunk-JY2HTT7Q.js +437 -0
- package/dist/chunk-KEPTCLUO.js +121 -0
- package/dist/chunk-KKK5H7YX.js +3622 -0
- package/dist/chunk-POSVEKIY.js +210 -0
- package/dist/chunk-QET4LT4J.js +5769 -0
- package/dist/chunk-QV4XWO3P.js +30 -0
- package/dist/chunk-R52J3PH2.js +120 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RECNVWMT.js +212 -0
- package/dist/chunk-RTPBU5HF.js +92 -0
- package/dist/chunk-SUZ2ATT6.js +5774 -0
- package/dist/chunk-SVMXSIMG.js +98 -0
- package/dist/chunk-TV3CBM7R.js +28 -0
- package/dist/chunk-V2LU736V.js +3495 -0
- package/dist/chunk-ZUYL3W53.js +741 -0
- package/dist/claude-plugin-577TAQVS.js +1463 -0
- package/dist/claude-plugin-L3MXJJ6J.js +1464 -0
- package/dist/cli.js +7021 -0
- package/dist/cloudflare-HBBABPK6.js +114 -0
- package/dist/cloudflare-RDFPKMM5.js +115 -0
- package/dist/cnb-FLP3QX46.js +44 -0
- package/dist/cnb-YAVVEYFB.js +45 -0
- package/dist/console/index.html +12 -0
- package/dist/console/logo.png +0 -0
- package/dist/console/static/css/5079.f9e0918d.css +1 -0
- package/dist/console/static/css/index.7f91f806.css +1 -0
- package/dist/console/static/font/codicon.5b7d6fac.ttf +0 -0
- package/dist/console/static/js/5079.72a51ca2.js +699 -0
- package/dist/console/static/js/5079.72a51ca2.js.LICENSE.txt +35 -0
- package/dist/console/static/js/7426.f8d483ea.js +1 -0
- package/dist/console/static/js/async/1008.4df521b7.js +1 -0
- package/dist/console/static/js/async/102.1d473ec7.js +1 -0
- package/dist/console/static/js/async/1134.3f9fd9e7.js +1 -0
- package/dist/console/static/js/async/1318.4b8e48e7.js +1 -0
- package/dist/console/static/js/async/1360.5606da88.js +7 -0
- package/dist/console/static/js/async/1546.045f484f.js +1 -0
- package/dist/console/static/js/async/1562.187de2a8.js +1 -0
- package/dist/console/static/js/async/168.456d4813.js +1 -0
- package/dist/console/static/js/async/1750.e6dc2664.js +1 -0
- package/dist/console/static/js/async/1994.3fc86066.js +1 -0
- package/dist/console/static/js/async/2348.613ae3d9.js +1 -0
- package/dist/console/static/js/async/2390.1b890b9d.js +1 -0
- package/dist/console/static/js/async/2414.9d040212.js +1 -0
- package/dist/console/static/js/async/2454.4c1784ab.js +1 -0
- package/dist/console/static/js/async/2498.f5f92030.js +1 -0
- package/dist/console/static/js/async/2924.b823cd1a.js +1 -0
- package/dist/console/static/js/async/3062.63fddea6.js +1 -0
- package/dist/console/static/js/async/3078.dd712008.js +1 -0
- package/dist/console/static/js/async/3198.1f307065.js +1 -0
- package/dist/console/static/js/async/3246.3d5a899f.js +1 -0
- package/dist/console/static/js/async/3286.871676eb.js +1 -0
- package/dist/console/static/js/async/342.10bf3b90.js +1 -0
- package/dist/console/static/js/async/3446.9681a4d7.js +1 -0
- package/dist/console/static/js/async/3698.ccfaabec.js +1 -0
- package/dist/console/static/js/async/3790.2a1106a6.js +1 -0
- package/dist/console/static/js/async/4231.b29784d4.js +1 -0
- package/dist/console/static/js/async/4551.515bd41d.js +1 -0
- package/dist/console/static/js/async/4596.40f6e71b.js +1 -0
- package/dist/console/static/js/async/4600.4aaebe6d.js +1 -0
- package/dist/console/static/js/async/4718.1aae022f.js +1 -0
- package/dist/console/static/js/async/4846.a347c020.js +1 -0
- package/dist/console/static/js/async/4860.83dadf89.js +1 -0
- package/dist/console/static/js/async/500.fcfa37cb.js +1 -0
- package/dist/console/static/js/async/5096.b360203d.js +1 -0
- package/dist/console/static/js/async/5222.043274fe.js +1 -0
- package/dist/console/static/js/async/5362.f498001c.js +1 -0
- package/dist/console/static/js/async/54.c94f0755.js +1 -0
- package/dist/console/static/js/async/5478.50dd9ef0.js +2 -0
- package/dist/console/static/js/async/5478.50dd9ef0.js.LICENSE.txt +3 -0
- package/dist/console/static/js/async/5507.a6a1f793.js +1 -0
- package/dist/console/static/js/async/5638.bc6b102d.js +1 -0
- package/dist/console/static/js/async/5722.e0029049.js +1 -0
- package/dist/console/static/js/async/5942.74635c6b.js +1 -0
- package/dist/console/static/js/async/5994.1c5629c1.js +1 -0
- package/dist/console/static/js/async/6054.6bddf720.js +1 -0
- package/dist/console/static/js/async/6118.45e754e5.js +1 -0
- package/dist/console/static/js/async/6127.adcbcbb6.js +1 -0
- package/dist/console/static/js/async/614.3f434c20.js +1 -0
- package/dist/console/static/js/async/6234.ba3b002d.js +1 -0
- package/dist/console/static/js/async/6310.6546a9ba.js +1 -0
- package/dist/console/static/js/async/6378.9f805419.js +1 -0
- package/dist/console/static/js/async/6380.e4433c49.js +1 -0
- package/dist/console/static/js/async/6418.f23bcfda.js +1 -0
- package/dist/console/static/js/async/6428.77c86114.js +1 -0
- package/dist/console/static/js/async/6443.83318a6c.js +1 -0
- package/dist/console/static/js/async/6508.2b445d62.js +3 -0
- package/dist/console/static/js/async/6542.e82a26c8.js +1 -0
- package/dist/console/static/js/async/6544.62111ecb.js +1 -0
- package/dist/console/static/js/async/6612.a0c9fcf4.js +1 -0
- package/dist/console/static/js/async/6740.695aebf0.js +1 -0
- package/dist/console/static/js/async/6822.dbbb32bc.js +1 -0
- package/dist/console/static/js/async/6824.ad3540ab.js +1 -0
- package/dist/console/static/js/async/6930.585dab94.js +1 -0
- package/dist/console/static/js/async/6982.c81b95e6.js +1 -0
- package/dist/console/static/js/async/7046.ab2378c1.js +1 -0
- package/dist/console/static/js/async/7110.a603277f.js +1 -0
- package/dist/console/static/js/async/7142.4a21366f.js +1 -0
- package/dist/console/static/js/async/7348.15cc6148.js +1373 -0
- package/dist/console/static/js/async/7348.15cc6148.js.LICENSE.txt +14 -0
- package/dist/console/static/js/async/7374.b1ac5c44.js +1 -0
- package/dist/console/static/js/async/742.847f17ca.js +1 -0
- package/dist/console/static/js/async/7446.743954d8.js +1 -0
- package/dist/console/static/js/async/7673.59bbbaac.js +1 -0
- package/dist/console/static/js/async/7684.c5760c8c.js +1 -0
- package/dist/console/static/js/async/7714.c30d0f94.js +1 -0
- package/dist/console/static/js/async/8118.36d5a3bf.js +298 -0
- package/dist/console/static/js/async/8145.4bcf043a.js +1 -0
- package/dist/console/static/js/async/8246.408de938.js +1 -0
- package/dist/console/static/js/async/8390.bdae1f7d.js +1 -0
- package/dist/console/static/js/async/8422.fd94dbe1.js +1 -0
- package/dist/console/static/js/async/8434.94a0e2ae.js +1 -0
- package/dist/console/static/js/async/8518.3158de13.js +1 -0
- package/dist/console/static/js/async/8564.fc2eb841.js +1 -0
- package/dist/console/static/js/async/8678.73af4c9b.js +1 -0
- package/dist/console/static/js/async/8694.79747168.js +1 -0
- package/dist/console/static/js/async/8756.1de37b83.js +1 -0
- package/dist/console/static/js/async/8804.7fe6bdf9.js +3 -0
- package/dist/console/static/js/async/8883.e717ee94.js +1 -0
- package/dist/console/static/js/async/8886.fe6e876c.js +1 -0
- package/dist/console/static/js/async/9030.fc1ae402.js +1 -0
- package/dist/console/static/js/async/9094.5598d084.js +1 -0
- package/dist/console/static/js/async/9218.ee7b84b7.js +1 -0
- package/dist/console/static/js/async/94.9b80bc35.js +1 -0
- package/dist/console/static/js/async/9526.92aba34c.js +1 -0
- package/dist/console/static/js/async/9762.f83bc4f3.js +1 -0
- package/dist/console/static/js/async/984.e11c113a.js +1 -0
- package/dist/console/static/js/async/9846.246653cd.js +1 -0
- package/dist/console/static/js/index.4487e1ff.js +1 -0
- package/dist/console/static/js/lib-react.15d7ca9a.js +2 -0
- package/dist/console/static/js/lib-react.15d7ca9a.js.LICENSE.txt +49 -0
- package/dist/coze-C6PMDPBI.js +49 -0
- package/dist/coze-E6VGRNLV.js +48 -0
- package/dist/dashboard.command-J7XOZNXU.js +8 -0
- package/dist/dashboard.command-RV2NHDKW.js +7 -0
- package/dist/dingtalk-JNRNRN7E.js +77 -0
- package/dist/dingtalk-WZGGIAHJ.js +76 -0
- package/dist/douyin-miniprogram-AIJPPIZH.js +41 -0
- package/dist/douyin-miniprogram-HCYZ5NBW.js +42 -0
- package/dist/figma-2YYNSCDX.js +103 -0
- package/dist/figma-RYOBMENP.js +102 -0
- package/dist/firebase-2IJDDBXX.js +112 -0
- package/dist/firebase-OYSY6HPT.js +111 -0
- package/dist/firecrawl-2T3SBUW7.js +66 -0
- package/dist/firecrawl-IYYXLAZM.js +65 -0
- package/dist/flyai-7FJ4TRAG.js +81 -0
- package/dist/flyai-QS5Q6FJR.js +82 -0
- package/dist/gitagent-MWI75OIX.js +725 -0
- package/dist/gitagent-YBMWY7NZ.js +726 -0
- package/dist/gitee-3N7OFOM7.js +53 -0
- package/dist/gitee-KVNK6KLZ.js +54 -0
- package/dist/github-LUEC2LID.js +143 -0
- package/dist/github-XRO5Z3GC.js +142 -0
- package/dist/google-ads-A3QAJI4D.js +88 -0
- package/dist/google-ads-VPKWTX67.js +89 -0
- package/dist/google-analytics-C4UR5ZR2.js +50 -0
- package/dist/google-analytics-XDYZA2B7.js +49 -0
- package/dist/google-workspace-LL3EWVHH.js +320 -0
- package/dist/google-workspace-YX35SHHX.js +321 -0
- package/dist/huawei-xiaoyi-6BSMGJHR.js +40 -0
- package/dist/huawei-xiaoyi-KPWLTSHB.js +41 -0
- package/dist/hubspot-DIUHGEDI.js +45 -0
- package/dist/hubspot-FTIEMNZO.js +44 -0
- package/dist/huggingface-MJCOXA7E.js +116 -0
- package/dist/huggingface-UUXK2RHK.js +117 -0
- package/dist/index.d.ts +3013 -0
- package/dist/index.js +15649 -0
- package/dist/inference-ai-image-generation-CMI6R5T3.js +106 -0
- package/dist/inference-ai-image-generation-PXV6IG4U.js +107 -0
- package/dist/inference-sh-7AZOLEFI.js +94 -0
- package/dist/inference-sh-ABQOD3YF.js +95 -0
- package/dist/init.command-6E24K4H3.js +9 -0
- package/dist/init.command-O4HG4HKR.js +10 -0
- package/dist/klaviyo-6K5YEFNH.js +45 -0
- package/dist/klaviyo-LDPBWBSS.js +44 -0
- package/dist/kuaidi100-HGFM5VK2.js +42 -0
- package/dist/kuaidi100-UHPFCVXP.js +41 -0
- package/dist/lark-6LNA3LUQ.js +103 -0
- package/dist/lark-URVBZNS4.js +102 -0
- package/dist/linear-7QFSFPOD.js +57 -0
- package/dist/linear-T4ORUP7N.js +56 -0
- package/dist/lovart-PDUXRUHJ.js +99 -0
- package/dist/lovart-QO3SK55T.js +100 -0
- package/dist/meta-ads-SCNFI45S.js +42 -0
- package/dist/meta-ads-V6XPZWX3.js +41 -0
- package/dist/miclaw-5CNTW7VV.js +36 -0
- package/dist/miclaw-TPPPS2WN.js +35 -0
- package/dist/model-provider-AVSFJSZP.js +393 -0
- package/dist/model-provider-KFB76XV5.js +392 -0
- package/dist/notion-FZK76MN2.js +69 -0
- package/dist/notion-WFA7KGZZ.js +70 -0
- package/dist/oceanengine-3JZUS3PP.js +43 -0
- package/dist/oceanengine-5BRIJVJE.js +42 -0
- package/dist/opencli-PFXHGCS2.js +81 -0
- package/dist/opencli-VIGRJTGH.js +80 -0
- package/dist/paypal-33UADIPR.js +54 -0
- package/dist/paypal-Z5JYHIWD.js +55 -0
- package/dist/playwright-MG5WHK47.js +58 -0
- package/dist/playwright-SQAQ3DZG.js +59 -0
- package/dist/plugins-HZBWK3WQ.js +120 -0
- package/dist/plugins-I4GD5SZX.js +121 -0
- package/dist/posthog-MU5MAJOQ.js +79 -0
- package/dist/posthog-RJRRKDWB.js +80 -0
- package/dist/salesforce-34FVIJTG.js +82 -0
- package/dist/salesforce-3QZ6OFVO.js +83 -0
- package/dist/sentry-MCIRMACU.js +111 -0
- package/dist/sentry-PIWW46VA.js +110 -0
- package/dist/seo-suite-4SQ3I67Q.js +54 -0
- package/dist/seo-suite-WJXMA3S4.js +55 -0
- package/dist/serve.command-5FMIPQRY.js +10 -0
- package/dist/serve.command-DNE6GPMK.js +9 -0
- package/dist/shadowob-JELOWHWX.js +1068 -0
- package/dist/shadowob-PRSMI5MW.js +1069 -0
- package/dist/sherlock-2PKY2E2Y.js +66 -0
- package/dist/sherlock-C5ZWPPVT.js +67 -0
- package/dist/shopify-GL3NFVGE.js +94 -0
- package/dist/shopify-R4G3UXM6.js +93 -0
- package/dist/skill-discovery-7INAUP4D.js +77 -0
- package/dist/skill-discovery-YPXXV622.js +78 -0
- package/dist/state-7MCZBTR5.js +17 -0
- package/dist/state-FGOFLFBE.js +18 -0
- package/dist/stripe-C22RR4ZS.js +83 -0
- package/dist/stripe-LJNPQ3CQ.js +82 -0
- package/dist/supabase-IRNQ54FJ.js +98 -0
- package/dist/supabase-N4ONFJNQ.js +97 -0
- package/dist/taobao-aipaas-LRR4GMO3.js +45 -0
- package/dist/taobao-aipaas-RVKORSF4.js +46 -0
- package/dist/tapd-3JPVJ7XH.js +46 -0
- package/dist/tapd-TMQRSMFG.js +47 -0
- package/dist/tencent-ads-IGD33LO7.js +42 -0
- package/dist/tencent-ads-UHC6OPBV.js +43 -0
- package/dist/tencent-docs-C3A4J3CJ.js +47 -0
- package/dist/tencent-docs-O2SC4FHL.js +48 -0
- package/dist/tencent-maps-HMMWMNF4.js +37 -0
- package/dist/tencent-maps-OQOKHVW2.js +36 -0
- package/dist/vercel-KOXDDTHX.js +73 -0
- package/dist/vercel-OLNVDWMG.js +74 -0
- package/dist/webflow-FULU5Q2I.js +114 -0
- package/dist/webflow-OMJKZM54.js +115 -0
- package/dist/wechat-miniprogram-skyline-KYCDMQNW.js +74 -0
- package/dist/wechat-miniprogram-skyline-VR4FVIQL.js +75 -0
- package/dist/wechat-pay-BCMAJ6UW.js +50 -0
- package/dist/wechat-pay-YQQKXVUI.js +51 -0
- package/dist/wonda-NGWIORYN.js +81 -0
- package/dist/wonda-RBABXFNM.js +82 -0
- package/dist/wordpress-woocommerce-RDIUTHYT.js +57 -0
- package/dist/wordpress-woocommerce-RNA5HB3N.js +58 -0
- package/dist/wps-DAEFQHDE.js +47 -0
- package/dist/wps-LUWHMZQQ.js +48 -0
- package/dist/yuque-HCHTJWNI.js +72 -0
- package/dist/yuque-KRH5O74J.js +71 -0
- package/images/RUNNERS.md +270 -0
- package/images/cc-connect-runner/entrypoint.mjs +311 -0
- package/images/claude-runner/Dockerfile +88 -0
- package/images/claude-runner/RUNNER.md +222 -0
- package/images/claude-runner/entrypoint.mjs +2 -0
- package/images/codex-runner/Dockerfile +87 -0
- package/images/codex-runner/RUNNER.md +226 -0
- package/images/codex-runner/entrypoint.mjs +2 -0
- package/images/gemini-runner/Dockerfile +87 -0
- package/images/gemini-runner/RUNNER.md +218 -0
- package/images/gemini-runner/entrypoint.mjs +2 -0
- package/images/hermes-runner/Dockerfile +74 -0
- package/images/hermes-runner/RUNNER.md +243 -0
- package/images/hermes-runner/entrypoint.mjs +283 -0
- package/images/openclaw-runner/Dockerfile +212 -0
- package/images/openclaw-runner/RUNNER.md +170 -0
- package/images/openclaw-runner/entrypoint.mjs +1113 -0
- package/images/openclaw-runner/warm-runtime-deps.mjs +95 -0
- package/images/opencode-runner/Dockerfile +87 -0
- package/images/opencode-runner/RUNNER.md +202 -0
- package/images/opencode-runner/entrypoint.mjs +2 -0
- package/package.json +121 -0
- package/templates/agent-marketplace-buddy.template.json +131 -0
- package/templates/ai-werewolf.template.json +92 -0
- package/templates/bmad-method-buddy.template.json +123 -0
- package/templates/brain-fix.template.json +92 -0
- package/templates/claude-ads-buddy.template.json +123 -0
- package/templates/claude-financial-services-buddy.template.json +111 -0
- package/templates/claude-seo-buddy.template.json +123 -0
- package/templates/code-arena.template.json +92 -0
- package/templates/daily-brief.template.json +92 -0
- package/templates/e-wife.template.json +92 -0
- package/templates/everything-claude-code-buddy.template.json +125 -0
- package/templates/financial-freedom.template.json +92 -0
- package/templates/gitstory.template.json +92 -0
- package/templates/google-workspace-buddy.template.json +88 -0
- package/templates/gsd-buddy.template.json +119 -0
- package/templates/gstack-buddy.template.json +143 -0
- package/templates/gstack.template.json +92 -0
- package/templates/little-match-girl.template.json +114 -0
- package/templates/lovart-buddy.template.json +110 -0
- package/templates/marketingskills-buddy.template.json +102 -0
- package/templates/retire-buddy.template.json +92 -0
- package/templates/scientific-skills-buddy.template.json +119 -0
- package/templates/seomachine-buddy.template.json +113 -0
- package/templates/shadow-server-app-demo.template.json +105 -0
- package/templates/slavingia-skills-buddy.template.json +102 -0
- package/templates/superclaude-buddy.template.json +146 -0
- package/templates/superpowers-buddy.template.json +108 -0
- 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"]
|