@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,1069 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
log
|
|
4
|
+
} from "./chunk-BF6CV2Y4.js";
|
|
5
|
+
import {
|
|
6
|
+
defineChannelPlugin
|
|
7
|
+
} from "./chunk-CTNUKOQE.js";
|
|
8
|
+
import "./chunk-AD3JTIU3.js";
|
|
9
|
+
|
|
10
|
+
// src/plugins/shadowob/manifest.json
|
|
11
|
+
var manifest_default = {
|
|
12
|
+
id: "shadowob",
|
|
13
|
+
name: "Shadow",
|
|
14
|
+
description: "Connect agents to Shadow chat platform with servers, channels, and buddy avatars",
|
|
15
|
+
version: "1.0.0",
|
|
16
|
+
category: "communication",
|
|
17
|
+
icon: "message-circle",
|
|
18
|
+
website: "https://shadow.ob",
|
|
19
|
+
auth: {
|
|
20
|
+
type: "token",
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
key: "SHADOW_SERVER_URL",
|
|
24
|
+
label: "Shadow Server URL",
|
|
25
|
+
description: "Shadow platform server base URL",
|
|
26
|
+
required: true,
|
|
27
|
+
sensitive: false,
|
|
28
|
+
placeholder: "https://shadow.example.com"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "SHADOWOB_API_TOKEN",
|
|
32
|
+
label: "Shadow API Token",
|
|
33
|
+
description: "Your Shadow platform API token for provisioning",
|
|
34
|
+
required: false,
|
|
35
|
+
sensitive: true
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
config: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
servers: {
|
|
43
|
+
type: "array",
|
|
44
|
+
description: "Shadow servers to provision"
|
|
45
|
+
},
|
|
46
|
+
buddies: {
|
|
47
|
+
type: "array",
|
|
48
|
+
description: "Buddy agents to create"
|
|
49
|
+
},
|
|
50
|
+
bindings: {
|
|
51
|
+
type: "array",
|
|
52
|
+
description: "Binding rules connecting buddies to agents and channels"
|
|
53
|
+
},
|
|
54
|
+
playLaunch: {
|
|
55
|
+
type: "object",
|
|
56
|
+
description: "Landing behavior for play launch flows, including default channel and greeting"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
capabilities: ["channel"],
|
|
61
|
+
tags: ["communication", "chat", "shadow", "built-in"],
|
|
62
|
+
popularity: 100
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/plugins/shadowob/provisioning.ts
|
|
66
|
+
import { ShadowClient } from "@shadowob/sdk";
|
|
67
|
+
var log2 = log;
|
|
68
|
+
async function provisionShadowResources(config, options) {
|
|
69
|
+
log2 = options.logger ?? log;
|
|
70
|
+
const client = new ShadowClient(options.serverUrl, options.userToken);
|
|
71
|
+
const result = {
|
|
72
|
+
servers: /* @__PURE__ */ new Map(),
|
|
73
|
+
channels: /* @__PURE__ */ new Map(),
|
|
74
|
+
buddies: /* @__PURE__ */ new Map(),
|
|
75
|
+
listings: /* @__PURE__ */ new Map(),
|
|
76
|
+
serverApps: /* @__PURE__ */ new Map(),
|
|
77
|
+
commerce: /* @__PURE__ */ new Map()
|
|
78
|
+
};
|
|
79
|
+
const shadowobEntry = config.use?.find((u) => u.plugin === "shadowob");
|
|
80
|
+
const plugin = shadowobEntry?.options;
|
|
81
|
+
if (!plugin) {
|
|
82
|
+
log2.dim("No shadowob plugin in use array, skipping provisioning");
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
if (options.dryRun) {
|
|
86
|
+
log2.info("Dry run \u2014 would provision:");
|
|
87
|
+
if (plugin.servers?.length) {
|
|
88
|
+
log2.dim(` ${plugin.servers.length} server(s)`);
|
|
89
|
+
for (const s of plugin.servers) {
|
|
90
|
+
log2.dim(` - ${s.name} (${s.channels?.length ?? 0} channels)`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (plugin.buddies?.length) {
|
|
94
|
+
log2.dim(` ${plugin.buddies.length} buddy/buddies`);
|
|
95
|
+
}
|
|
96
|
+
if (plugin.bindings?.length) {
|
|
97
|
+
log2.dim(` ${plugin.bindings.length} binding(s)`);
|
|
98
|
+
}
|
|
99
|
+
if (plugin.serverApps?.length) {
|
|
100
|
+
log2.dim(` ${plugin.serverApps.length} server app(s)`);
|
|
101
|
+
}
|
|
102
|
+
if (plugin.listings?.length) {
|
|
103
|
+
log2.dim(` ${plugin.listings.length} rental listing(s)`);
|
|
104
|
+
for (const l of plugin.listings) {
|
|
105
|
+
log2.dim(` - "${l.title}" for buddy "${l.buddyId}" @ ${l.pricePerHour}/hr`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
const state = options.force ? null : options.existingState ?? null;
|
|
111
|
+
if (state) {
|
|
112
|
+
detectOrphans(state, plugin);
|
|
113
|
+
}
|
|
114
|
+
if (plugin.servers?.length) {
|
|
115
|
+
for (const serverDef of plugin.servers) {
|
|
116
|
+
const serverId = await provisionServer(client, serverDef, state);
|
|
117
|
+
result.servers.set(serverDef.id, serverId);
|
|
118
|
+
if (serverDef.channels?.length) {
|
|
119
|
+
for (const channelDef of serverDef.channels) {
|
|
120
|
+
const channelId = await provisionChannel(client, serverId, channelDef, state);
|
|
121
|
+
result.channels.set(channelDef.id, channelId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const buddyAllowedServerIds = buildBuddyAllowedServerIds(plugin, result);
|
|
127
|
+
if (plugin.buddies?.length) {
|
|
128
|
+
for (const buddyDef of plugin.buddies) {
|
|
129
|
+
const buddyInfo = await provisionBuddy(
|
|
130
|
+
client,
|
|
131
|
+
buddyDef,
|
|
132
|
+
state,
|
|
133
|
+
buddyAllowedServerIds.get(buddyDef.id) ?? []
|
|
134
|
+
);
|
|
135
|
+
result.buddies.set(buddyDef.id, buddyInfo);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (plugin.bindings?.length) {
|
|
139
|
+
for (const binding of plugin.bindings) {
|
|
140
|
+
await processBinding(client, binding, result);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (plugin.serverApps?.length) {
|
|
144
|
+
for (const appDef of plugin.serverApps) {
|
|
145
|
+
const installed = await provisionServerApp(client, appDef, result, state);
|
|
146
|
+
if (installed) result.serverApps.set(appDef.id, installed);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (plugin.listings?.length) {
|
|
150
|
+
for (const listingDef of plugin.listings) {
|
|
151
|
+
const listingId = await provisionListing(client, listingDef, result, state);
|
|
152
|
+
if (listingId) {
|
|
153
|
+
result.listings.set(listingDef.buddyId, listingId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (plugin.commerce?.paidFiles?.length) {
|
|
158
|
+
for (const paidFileDef of plugin.commerce.paidFiles) {
|
|
159
|
+
const commerceIds = await provisionPaidFileCommerce(client, paidFileDef, result, state);
|
|
160
|
+
if (commerceIds) result.commerce.set(paidFileDef.id, commerceIds);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
function detectOrphans(state, plugin) {
|
|
166
|
+
if (!plugin) return;
|
|
167
|
+
const configServerIds = new Set(plugin.servers?.map((s) => s.id) ?? []);
|
|
168
|
+
const configChannelIds = new Set(
|
|
169
|
+
plugin.servers?.flatMap((s) => s.channels?.map((c) => c.id) ?? []) ?? []
|
|
170
|
+
);
|
|
171
|
+
const configBuddyIds = new Set(plugin.buddies?.map((b) => b.id) ?? []);
|
|
172
|
+
const configServerAppIds = new Set(plugin.serverApps?.map((app) => app.id) ?? []);
|
|
173
|
+
for (const id of Object.keys(state.servers ?? {})) {
|
|
174
|
+
if (!configServerIds.has(id)) {
|
|
175
|
+
log2.warn(` Orphaned server in state: "${id}" (not in current config)`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (const id of Object.keys(state.channels ?? {})) {
|
|
179
|
+
if (!configChannelIds.has(id)) {
|
|
180
|
+
log2.warn(` Orphaned channel in state: "${id}" (not in current config)`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (const id of Object.keys(state.buddies ?? {})) {
|
|
184
|
+
if (!configBuddyIds.has(id)) {
|
|
185
|
+
log2.warn(` Orphaned buddy in state: "${id}" (not in current config)`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const id of Object.keys(state.serverApps ?? {})) {
|
|
189
|
+
if (!configServerAppIds.has(id)) {
|
|
190
|
+
log2.warn(` Orphaned server app in state: "${id}" (not in current config)`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const configListingBuddyIds = new Set(plugin.listings?.map((l) => l.buddyId) ?? []);
|
|
194
|
+
for (const id of Object.keys(state.listings ?? {})) {
|
|
195
|
+
if (!configListingBuddyIds.has(id)) {
|
|
196
|
+
log2.warn(
|
|
197
|
+
` Orphaned rental listing in state for buddy "${id}" (no listing config found \u2014 listing may still be active on marketplace)`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const configCommerceIds = new Set(plugin.commerce?.paidFiles?.map((item) => item.id) ?? []);
|
|
202
|
+
for (const id of Object.keys(state.commerce ?? {})) {
|
|
203
|
+
if (!configCommerceIds.has(id)) {
|
|
204
|
+
log2.warn(` Orphaned commerce seed in state: "${id}" (not in current config)`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function provisionServerApp(client, appDef, result, state) {
|
|
209
|
+
const serverId = result.servers.get(appDef.serverId) ?? appDef.serverId;
|
|
210
|
+
const existing = state?.serverApps?.[appDef.id];
|
|
211
|
+
if (existing) {
|
|
212
|
+
log2.dim(` Server App "${appDef.id}" found in state (${existing.appKey}); refreshing install`);
|
|
213
|
+
} else {
|
|
214
|
+
log2.step(`Provisioning server App: ${appDef.id}`);
|
|
215
|
+
}
|
|
216
|
+
if (!appDef.manifestUrl && !appDef.manifest) {
|
|
217
|
+
log2.warn(` Server App "${appDef.id}" skipped: manifestUrl or manifest is required`);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const installed = await client.installServerApp(serverId, {
|
|
221
|
+
manifestUrl: appDef.manifestUrl,
|
|
222
|
+
manifest: appDef.manifest
|
|
223
|
+
});
|
|
224
|
+
log2.success(` Installed server App "${installed.appKey}" on server "${appDef.serverId}"`);
|
|
225
|
+
for (const grant of appDef.grants ?? []) {
|
|
226
|
+
const buddy = result.buddies.get(grant.buddyId);
|
|
227
|
+
if (!buddy) {
|
|
228
|
+
log2.warn(` Server App "${appDef.id}" grant skipped: buddy "${grant.buddyId}" not found`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
await client.grantServerAppToBuddy(serverId, installed.appKey, {
|
|
232
|
+
buddyAgentId: buddy.agentId,
|
|
233
|
+
permissions: grant.permissions ?? ["*"],
|
|
234
|
+
resourceRules: grant.resourceRules,
|
|
235
|
+
approvalMode: grant.approvalMode ?? "none"
|
|
236
|
+
});
|
|
237
|
+
log2.success(` Granted server App "${installed.appKey}" to buddy "${grant.buddyId}"`);
|
|
238
|
+
}
|
|
239
|
+
return { serverAppId: installed.id, appKey: installed.appKey, serverId };
|
|
240
|
+
}
|
|
241
|
+
async function provisionPaidFileCommerce(client, paidFileDef, result, state) {
|
|
242
|
+
const existing = state?.commerce?.[paidFileDef.id];
|
|
243
|
+
if (existing) {
|
|
244
|
+
log2.dim(` Commerce paid file "${paidFileDef.id}" found in state (${existing.offerId})`);
|
|
245
|
+
return existing;
|
|
246
|
+
}
|
|
247
|
+
const serverId = result.servers.get(paidFileDef.serverId) ?? paidFileDef.serverId;
|
|
248
|
+
const sellerBuddyId = paidFileDef.sellerBuddyId ?? paidFileDef.shop.buddyId;
|
|
249
|
+
const sellerBuddy = sellerBuddyId ? result.buddies.get(sellerBuddyId) : void 0;
|
|
250
|
+
let shopId;
|
|
251
|
+
if (paidFileDef.shop.kind === "buddy") {
|
|
252
|
+
if (!sellerBuddy) {
|
|
253
|
+
log2.warn(` Commerce paid file "${paidFileDef.id}" skipped: seller buddy not provisioned`);
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
const shop = await client.getManagedUserShop(sellerBuddy.userId);
|
|
257
|
+
shopId = shop.id;
|
|
258
|
+
} else {
|
|
259
|
+
const shopServerId = paidFileDef.shop.serverId ? result.servers.get(paidFileDef.shop.serverId) ?? paidFileDef.shop.serverId : serverId;
|
|
260
|
+
const shop = await client.getShop(shopServerId);
|
|
261
|
+
shopId = shop.id;
|
|
262
|
+
}
|
|
263
|
+
if (!shopId) throw new Error(`Commerce shop could not be resolved for ${paidFileDef.id}`);
|
|
264
|
+
log2.step(`Provisioning commerce paid file: ${paidFileDef.name}`);
|
|
265
|
+
const mime = paidFileDef.mime ?? "text/html; charset=utf-8";
|
|
266
|
+
const media = await client.uploadMedia(
|
|
267
|
+
new Blob([paidFileDef.html], { type: mime }),
|
|
268
|
+
paidFileDef.fileName,
|
|
269
|
+
mime
|
|
270
|
+
);
|
|
271
|
+
const file = await client.createWorkspaceFile(serverId, {
|
|
272
|
+
name: paidFileDef.fileName,
|
|
273
|
+
mime,
|
|
274
|
+
sizeBytes: media.size,
|
|
275
|
+
contentRef: media.url,
|
|
276
|
+
previewUrl: null,
|
|
277
|
+
metadata: {
|
|
278
|
+
paywall: true,
|
|
279
|
+
paidFile: true,
|
|
280
|
+
commerceSeedId: paidFileDef.id
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
const fileId = typeof file.id === "string" ? file.id : null;
|
|
284
|
+
if (!fileId) throw new Error(`Workspace file create returned no id for ${paidFileDef.id}`);
|
|
285
|
+
const product = await client.createShopProduct(shopId, {
|
|
286
|
+
name: paidFileDef.name,
|
|
287
|
+
slug: paidFileDef.slug,
|
|
288
|
+
type: "entitlement",
|
|
289
|
+
status: "active",
|
|
290
|
+
summary: paidFileDef.summary,
|
|
291
|
+
description: paidFileDef.description,
|
|
292
|
+
basePrice: paidFileDef.price,
|
|
293
|
+
billingMode: paidFileDef.durationSeconds ? "fixed_duration" : "one_time",
|
|
294
|
+
entitlementConfig: {
|
|
295
|
+
resourceType: "workspace_file",
|
|
296
|
+
resourceId: fileId,
|
|
297
|
+
capability: "view",
|
|
298
|
+
durationSeconds: paidFileDef.durationSeconds ?? null,
|
|
299
|
+
privilegeDescription: paidFileDef.summary
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
const productId = typeof product.id === "string" ? product.id : null;
|
|
303
|
+
if (!productId) throw new Error(`Product create returned no id for ${paidFileDef.id}`);
|
|
304
|
+
const offers = await client.listCommerceOffers(shopId, { limit: 50 });
|
|
305
|
+
const offer = offers.offers.find((item) => item.productId === productId) ?? await client.createCommerceOffer(shopId, {
|
|
306
|
+
productId,
|
|
307
|
+
allowedSurfaces: paidFileDef.offerSurfaces ?? ["dm", "channel"],
|
|
308
|
+
sellerBuddyUserId: sellerBuddy?.userId,
|
|
309
|
+
status: "active",
|
|
310
|
+
metadata: { commerceSeedId: paidFileDef.id }
|
|
311
|
+
});
|
|
312
|
+
const offerId = typeof offer.id === "string" ? offer.id : null;
|
|
313
|
+
if (!offerId) throw new Error(`Offer create returned no id for ${paidFileDef.id}`);
|
|
314
|
+
const deliverable = await client.createCommerceDeliverable(shopId, offerId, {
|
|
315
|
+
kind: "paid_file",
|
|
316
|
+
resourceType: "workspace_file",
|
|
317
|
+
resourceId: fileId,
|
|
318
|
+
senderBuddyUserId: sellerBuddy?.userId,
|
|
319
|
+
metadata: {
|
|
320
|
+
commerceSeedId: paidFileDef.id,
|
|
321
|
+
summary: paidFileDef.summary,
|
|
322
|
+
message: paidFileDef.fulfillmentMessage ?? "\u706B\u67F4\u5DF2\u7ECF\u70B9\u4EAE\u3002\u6253\u5F00\u8FD9\u4EFD\u4ED8\u8D39\u6587\u4EF6\uFF0C\u770B\u770B\u706B\u5149\u91CC\u7684\u5C0F\u5C0F\u52A8\u753B\u3002"
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
const deliverableId = typeof deliverable.id === "string" ? deliverable.id : null;
|
|
326
|
+
if (!deliverableId) throw new Error(`Deliverable create returned no id for ${paidFileDef.id}`);
|
|
327
|
+
log2.success(` Created paid-file offer "${paidFileDef.name}" (${offerId})`);
|
|
328
|
+
return { shopId, productId, offerId, fileId, deliverableId };
|
|
329
|
+
}
|
|
330
|
+
async function provisionServer(client, serverDef, state) {
|
|
331
|
+
const accessibleServers = await listAccessibleServers(client);
|
|
332
|
+
const accessibleServerIds = new Set(accessibleServers.map((server2) => server2.id));
|
|
333
|
+
const existingId = state?.servers?.[serverDef.id];
|
|
334
|
+
if (existingId) {
|
|
335
|
+
try {
|
|
336
|
+
const existing = await client.getServer(existingId);
|
|
337
|
+
if (existing && accessibleServerIds.has(existing.id)) {
|
|
338
|
+
log2.dim(` Server "${serverDef.name}" found in state (${existingId})`);
|
|
339
|
+
return existingId;
|
|
340
|
+
}
|
|
341
|
+
if (existing) {
|
|
342
|
+
log2.dim(
|
|
343
|
+
` Server "${serverDef.name}" in state (${existingId}) is not visible to this token, creating a user-owned server...`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
} catch {
|
|
347
|
+
log2.dim(` Server "${serverDef.name}" in state but not found on server, recreating...`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
log2.step(`Provisioning server: ${serverDef.name}`);
|
|
351
|
+
if (serverDef.slug) {
|
|
352
|
+
const existing = accessibleServers.find((server2) => server2.slug === serverDef.slug);
|
|
353
|
+
if (existing) {
|
|
354
|
+
log2.dim(` Server "${serverDef.name}" already exists (${existing.id})`);
|
|
355
|
+
return existing.id;
|
|
356
|
+
}
|
|
357
|
+
log2.dim(
|
|
358
|
+
` No accessible server found for slug "${serverDef.slug}"; creating a user-owned server`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
const server = await client.createServer({
|
|
362
|
+
name: serverDef.name,
|
|
363
|
+
slug: serverDef.slug,
|
|
364
|
+
description: serverDef.description,
|
|
365
|
+
isPublic: serverDef.isPublic
|
|
366
|
+
});
|
|
367
|
+
log2.success(` Created server: ${server.name} (${server.id})`);
|
|
368
|
+
return server.id;
|
|
369
|
+
}
|
|
370
|
+
async function provisionChannel(client, serverId, channelDef, state) {
|
|
371
|
+
const existingId = state?.channels?.[channelDef.id];
|
|
372
|
+
if (existingId) {
|
|
373
|
+
try {
|
|
374
|
+
const existing = await client.getChannel(existingId);
|
|
375
|
+
if (existing.serverId === serverId) {
|
|
376
|
+
log2.dim(` Channel "${channelDef.title}" found in state (${existingId})`);
|
|
377
|
+
return existingId;
|
|
378
|
+
}
|
|
379
|
+
log2.dim(` Channel "${channelDef.title}" in state belongs to another server, recreating...`);
|
|
380
|
+
} catch {
|
|
381
|
+
log2.dim(` Channel "${channelDef.title}" in state but not found, recreating...`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
log2.step(` Provisioning channel: ${channelDef.title}`);
|
|
385
|
+
try {
|
|
386
|
+
const channels = await client.getServerChannels(serverId);
|
|
387
|
+
const expectedName = normalizeChannelName(channelDef.title);
|
|
388
|
+
const expectedId = normalizeChannelName(channelDef.id);
|
|
389
|
+
const existing = channels.find((c) => {
|
|
390
|
+
const channelName = normalizeChannelName(c.name);
|
|
391
|
+
return channelName === expectedName || channelName === expectedId;
|
|
392
|
+
});
|
|
393
|
+
if (existing) {
|
|
394
|
+
log2.dim(` Channel "${channelDef.title}" already exists (${existing.id})`);
|
|
395
|
+
return existing.id;
|
|
396
|
+
}
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
const createChannel = client.createChannel.bind(client);
|
|
400
|
+
const channel = await createChannel(serverId, {
|
|
401
|
+
name: channelDef.title,
|
|
402
|
+
type: channelDef.type,
|
|
403
|
+
description: channelDef.description,
|
|
404
|
+
isPrivate: channelDef.isPrivate
|
|
405
|
+
});
|
|
406
|
+
log2.success(` Created channel: ${channelDef.title} (${channel.id})`);
|
|
407
|
+
return channel.id;
|
|
408
|
+
}
|
|
409
|
+
async function provisionBuddy(client, buddyDef, state, allowedServerIds) {
|
|
410
|
+
const existingBuddy = state?.buddies?.[buddyDef.id];
|
|
411
|
+
if (existingBuddy?.agentId) {
|
|
412
|
+
log2.dim(` Buddy "${buddyDef.name}" found in state (agent: ${existingBuddy.agentId})`);
|
|
413
|
+
try {
|
|
414
|
+
await ensureBuddyServerAccess(client, existingBuddy.agentId, allowedServerIds);
|
|
415
|
+
const tokenResult = await client.generateAgentToken(existingBuddy.agentId);
|
|
416
|
+
return {
|
|
417
|
+
agentId: existingBuddy.agentId,
|
|
418
|
+
token: tokenResult.token,
|
|
419
|
+
userId: existingBuddy.userId
|
|
420
|
+
};
|
|
421
|
+
} catch (err) {
|
|
422
|
+
log2.dim(
|
|
423
|
+
` Buddy "${buddyDef.name}" in state could not mint a fresh token, recreating or looking up existing buddy: ${formatErrorMessage(
|
|
424
|
+
err
|
|
425
|
+
)}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
log2.step(`Provisioning buddy: ${buddyDef.name}`);
|
|
430
|
+
const username = buddyDef.id.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/, "").slice(0, 30);
|
|
431
|
+
let agentId;
|
|
432
|
+
let token;
|
|
433
|
+
let userId;
|
|
434
|
+
let agents = [];
|
|
435
|
+
try {
|
|
436
|
+
agents = await client.listAgents() ?? [];
|
|
437
|
+
} catch {
|
|
438
|
+
agents = [];
|
|
439
|
+
}
|
|
440
|
+
const existing = agents.find((agent) => {
|
|
441
|
+
const botUser = agent.botUser;
|
|
442
|
+
const config = agent.config;
|
|
443
|
+
const shadowob = config?.shadowob;
|
|
444
|
+
const shadowobBuddyId = shadowob && typeof shadowob === "object" && !Array.isArray(shadowob) ? shadowob.buddyId : null;
|
|
445
|
+
return shadowobBuddyId === buddyDef.id || botUser?.username === username || agent.name === buddyDef.name && botUser?.displayName === buddyDef.name;
|
|
446
|
+
});
|
|
447
|
+
if (existing) {
|
|
448
|
+
agentId = existing.id;
|
|
449
|
+
await ensureBuddyServerAccess(
|
|
450
|
+
client,
|
|
451
|
+
agentId,
|
|
452
|
+
allowedServerIds,
|
|
453
|
+
existing.config
|
|
454
|
+
);
|
|
455
|
+
const tokenResult = await client.generateAgentToken(agentId);
|
|
456
|
+
token = tokenResult.token;
|
|
457
|
+
userId = existing.userId ?? existing.botUser?.id ?? "";
|
|
458
|
+
log2.dim(` Reusing buddy: ${buddyDef.name} (agent: ${agentId})`);
|
|
459
|
+
return { agentId, token, userId };
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
const agent = await client.createAgent({
|
|
463
|
+
name: buddyDef.name,
|
|
464
|
+
username,
|
|
465
|
+
displayName: buddyDef.name,
|
|
466
|
+
avatarUrl: buddyDef.avatarUrl,
|
|
467
|
+
buddyMode: "private",
|
|
468
|
+
allowedServerIds,
|
|
469
|
+
config: { shadowob: { buddyId: buddyDef.id } }
|
|
470
|
+
});
|
|
471
|
+
agentId = agent.id;
|
|
472
|
+
userId = agent.userId;
|
|
473
|
+
const newTokenResult = await client.generateAgentToken(agentId);
|
|
474
|
+
token = newTokenResult.token;
|
|
475
|
+
log2.success(` Created buddy: ${buddyDef.name} (agent: ${agentId})`);
|
|
476
|
+
} catch (err) {
|
|
477
|
+
const msg = err.message ?? "";
|
|
478
|
+
if (/already|conflict|duplicate|unique/i.test(msg)) {
|
|
479
|
+
log2.dim(` Buddy "${buddyDef.name}" already exists, looking up...`);
|
|
480
|
+
const fallbackAgents = await client.listAgents();
|
|
481
|
+
const fallback = fallbackAgents.find((a) => a.name === buddyDef.name);
|
|
482
|
+
if (!fallback) throw new Error(`Cannot find existing buddy "${buddyDef.name}": ${msg}`);
|
|
483
|
+
agentId = fallback.id;
|
|
484
|
+
await ensureBuddyServerAccess(
|
|
485
|
+
client,
|
|
486
|
+
agentId,
|
|
487
|
+
allowedServerIds,
|
|
488
|
+
fallback.config
|
|
489
|
+
);
|
|
490
|
+
const tokenResult = await client.generateAgentToken(agentId);
|
|
491
|
+
token = tokenResult.token;
|
|
492
|
+
userId = fallback.userId ?? "";
|
|
493
|
+
log2.dim(` Found existing buddy: ${buddyDef.name} (agent: ${agentId})`);
|
|
494
|
+
} else {
|
|
495
|
+
throw err;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return { agentId, token, userId };
|
|
499
|
+
}
|
|
500
|
+
async function processBinding(client, binding, result) {
|
|
501
|
+
const buddyInfo = result.buddies.get(binding.targetId);
|
|
502
|
+
if (!buddyInfo) {
|
|
503
|
+
log2.warn(` Binding target "${binding.targetId}" not found in provisioned buddies`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
for (const serverConfigId of binding.servers) {
|
|
507
|
+
const serverId = result.servers.get(serverConfigId);
|
|
508
|
+
if (!serverId) {
|
|
509
|
+
log2.warn(` Server "${serverConfigId}" not found in provisioned servers`);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const result2 = await client.addAgentsToServer(serverId, [buddyInfo.agentId]);
|
|
514
|
+
const failed = Array.isArray(result2?.failed) ? result2.failed : [];
|
|
515
|
+
const blockingFailures = failed.filter(
|
|
516
|
+
(item) => !/already (a )?server member/i.test(String(item?.error ?? ""))
|
|
517
|
+
);
|
|
518
|
+
if (blockingFailures.length > 0) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
blockingFailures.map((item) => item?.error).filter(Boolean).join("; ")
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
log2.success(` Added buddy "${binding.targetId}" to server "${serverConfigId}"`);
|
|
524
|
+
} catch (err) {
|
|
525
|
+
throw new Error(
|
|
526
|
+
`Could not add buddy "${binding.targetId}" to server "${serverConfigId}": ${formatErrorMessage(
|
|
527
|
+
err
|
|
528
|
+
)}`
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
for (const channelConfigId of binding.channels) {
|
|
533
|
+
const channelId = result.channels.get(channelConfigId);
|
|
534
|
+
if (!channelId) {
|
|
535
|
+
log2.warn(` Channel "${channelConfigId}" not found in provisioned channels`);
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
await client.addChannelMember(channelId, buddyInfo.userId);
|
|
540
|
+
log2.success(` Added buddy "${binding.targetId}" to channel "${channelConfigId}"`);
|
|
541
|
+
} catch (err) {
|
|
542
|
+
log2.dim(
|
|
543
|
+
` Buddy already in channel "${channelConfigId}" (or error: ${formatErrorMessage(err)})`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (binding.replyPolicy) {
|
|
548
|
+
const policy = binding.replyPolicy;
|
|
549
|
+
const mentionOnly = policy.mode === "mentionOnly";
|
|
550
|
+
const reply = policy.mode !== "disabled";
|
|
551
|
+
const policyConfig = {};
|
|
552
|
+
if (policy.mode === "custom" && policy.custom) {
|
|
553
|
+
Object.assign(policyConfig, policy.custom);
|
|
554
|
+
}
|
|
555
|
+
for (const serverConfigId of binding.servers) {
|
|
556
|
+
const serverId = result.servers.get(serverConfigId);
|
|
557
|
+
if (!serverId) continue;
|
|
558
|
+
try {
|
|
559
|
+
await client.upsertPolicy(buddyInfo.agentId, serverId, {
|
|
560
|
+
channelId: null,
|
|
561
|
+
mentionOnly,
|
|
562
|
+
reply,
|
|
563
|
+
config: policyConfig
|
|
564
|
+
});
|
|
565
|
+
log2.success(
|
|
566
|
+
` Applied replyPolicy "${policy.mode}" to buddy "${binding.targetId}" in server "${serverConfigId}"`
|
|
567
|
+
);
|
|
568
|
+
} catch {
|
|
569
|
+
log2.dim(
|
|
570
|
+
` Could not apply replyPolicy to buddy "${binding.targetId}" in server "${serverConfigId}"`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
for (const channelConfigId of binding.channels) {
|
|
574
|
+
const channelId = result.channels.get(channelConfigId);
|
|
575
|
+
if (!channelId) continue;
|
|
576
|
+
try {
|
|
577
|
+
await client.upsertPolicy(buddyInfo.agentId, serverId, {
|
|
578
|
+
channelId,
|
|
579
|
+
mentionOnly,
|
|
580
|
+
reply,
|
|
581
|
+
config: policyConfig
|
|
582
|
+
});
|
|
583
|
+
} catch {
|
|
584
|
+
log2.dim(
|
|
585
|
+
` Could not apply per-channel replyPolicy for "${channelConfigId}" \u2014 server policy applies`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function buildBuddyAllowedServerIds(plugin, result) {
|
|
593
|
+
const allowed = /* @__PURE__ */ new Map();
|
|
594
|
+
for (const binding of plugin.bindings ?? []) {
|
|
595
|
+
if (binding.targetType !== "buddy") continue;
|
|
596
|
+
const servers = allowed.get(binding.targetId) ?? /* @__PURE__ */ new Set();
|
|
597
|
+
for (const serverConfigId of binding.servers) {
|
|
598
|
+
const serverId = result.servers.get(serverConfigId) ?? serverConfigId;
|
|
599
|
+
if (serverId) servers.add(serverId);
|
|
600
|
+
}
|
|
601
|
+
allowed.set(binding.targetId, servers);
|
|
602
|
+
}
|
|
603
|
+
return new Map([...allowed].map(([buddyId, serverIds]) => [buddyId, [...serverIds]]));
|
|
604
|
+
}
|
|
605
|
+
function configuredAllowedServerIds(config) {
|
|
606
|
+
const raw = config?.allowedServerIds ?? config?.serverWhitelist;
|
|
607
|
+
if (!Array.isArray(raw)) return [];
|
|
608
|
+
return raw.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
609
|
+
}
|
|
610
|
+
async function ensureBuddyServerAccess(client, agentId, allowedServerIds, currentConfig) {
|
|
611
|
+
if (allowedServerIds.length === 0) return;
|
|
612
|
+
const merged = Array.from(
|
|
613
|
+
/* @__PURE__ */ new Set([...configuredAllowedServerIds(currentConfig), ...allowedServerIds])
|
|
614
|
+
);
|
|
615
|
+
await client.updateAgent(agentId, {
|
|
616
|
+
buddyMode: "private",
|
|
617
|
+
allowedServerIds: merged
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
async function listAccessibleServers(client) {
|
|
621
|
+
try {
|
|
622
|
+
const response = await client.listServers();
|
|
623
|
+
if (!Array.isArray(response)) return [];
|
|
624
|
+
return response.map((entry) => {
|
|
625
|
+
const server = entry && typeof entry === "object" && "server" in entry ? entry.server : entry;
|
|
626
|
+
if (!server || typeof server !== "object") return null;
|
|
627
|
+
const candidate = server;
|
|
628
|
+
return typeof candidate.id === "string" ? candidate : null;
|
|
629
|
+
}).filter((server) => server !== null);
|
|
630
|
+
} catch (err) {
|
|
631
|
+
log2.dim(` Could not list accessible servers before provisioning: ${formatErrorMessage(err)}`);
|
|
632
|
+
return [];
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function normalizeChannelName(value) {
|
|
636
|
+
return (value ?? "").toLowerCase().trim().replace(/^#/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
637
|
+
}
|
|
638
|
+
function formatErrorMessage(err) {
|
|
639
|
+
return err instanceof Error ? err.message : String(err);
|
|
640
|
+
}
|
|
641
|
+
async function provisionListing(client, listingDef, result, state) {
|
|
642
|
+
const buddyInfo = result.buddies.get(listingDef.buddyId);
|
|
643
|
+
if (!buddyInfo) {
|
|
644
|
+
log2.warn(` Listing references unknown buddy "${listingDef.buddyId}" \u2014 skipping`);
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
const existingListingId = state?.listings?.[listingDef.buddyId];
|
|
648
|
+
if (existingListingId) {
|
|
649
|
+
try {
|
|
650
|
+
await client.updateListing(existingListingId, {
|
|
651
|
+
title: listingDef.title,
|
|
652
|
+
description: listingDef.description,
|
|
653
|
+
pricePerHour: listingDef.pricePerHour,
|
|
654
|
+
tags: listingDef.tags
|
|
655
|
+
});
|
|
656
|
+
log2.success(
|
|
657
|
+
` Updated rental listing for buddy "${listingDef.buddyId}" (${existingListingId})`
|
|
658
|
+
);
|
|
659
|
+
return existingListingId;
|
|
660
|
+
} catch {
|
|
661
|
+
log2.dim(
|
|
662
|
+
` Listing ${existingListingId} not found on server \u2014 will re-create for buddy "${listingDef.buddyId}"`
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const created = await client.createListing({
|
|
668
|
+
agentId: buddyInfo.agentId,
|
|
669
|
+
title: listingDef.title,
|
|
670
|
+
description: listingDef.description,
|
|
671
|
+
pricePerHour: listingDef.pricePerHour,
|
|
672
|
+
tags: listingDef.tags
|
|
673
|
+
});
|
|
674
|
+
log2.success(
|
|
675
|
+
` Created rental listing "${listingDef.title}" for buddy "${listingDef.buddyId}" (${created.id})`
|
|
676
|
+
);
|
|
677
|
+
if (listingDef.active === false) {
|
|
678
|
+
try {
|
|
679
|
+
await client.toggleListing(created.id);
|
|
680
|
+
} catch {
|
|
681
|
+
log2.dim(` Could not deactivate listing ${created.id}`);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return created.id;
|
|
685
|
+
} catch (err) {
|
|
686
|
+
log2.warn(
|
|
687
|
+
` Failed to create rental listing for buddy "${listingDef.buddyId}": ${err instanceof Error ? err.message : String(err)}`
|
|
688
|
+
);
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/plugins/shadowob/index.ts
|
|
694
|
+
var SHADOWOB_OPENCLAW_EXTENSION_ID = "shadowob";
|
|
695
|
+
var SHADOWOB_OPENCLAW_PLUGIN_ID = "openclaw-shadowob";
|
|
696
|
+
var SHADOWOB_OPENCLAW_EXTENSION_PATH = `/app/extensions/${SHADOWOB_OPENCLAW_EXTENSION_ID}`;
|
|
697
|
+
var SHADOWOB_CLI_SKILL_INTRO = 'Shadow context: use the mounted shadowob-cli skill and `shadowob` CLI when you need current channel/DM history, pins, members, server/channel/workspace state, server App resources, or to send/manage Shadow content. For installed server Apps, use the CLI path only: run `shadowob app discover --server "$SHADOWOB_SERVER_ID" --json`, then `shadowob app call <app-key> <command> --server "$SHADOWOB_SERVER_ID" --json-input \'<raw-command-input-json>\' --json`. Do not use curl, fetch, raw HTTP routes, or the JavaScript SDK for server App commands. Keep reads narrow and prefer `--json`.';
|
|
698
|
+
function shadowEnvKey(prefix, id) {
|
|
699
|
+
return `${prefix}_${id.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
|
|
700
|
+
}
|
|
701
|
+
function shadowEnvRef(key) {
|
|
702
|
+
return `\${env:${key}}`;
|
|
703
|
+
}
|
|
704
|
+
function shadowobChannelCapabilities() {
|
|
705
|
+
return {
|
|
706
|
+
inlineButtons: "all",
|
|
707
|
+
interactive: true,
|
|
708
|
+
forms: true
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function shadowobChannelCapabilitiesSchema() {
|
|
712
|
+
return {
|
|
713
|
+
type: "object",
|
|
714
|
+
additionalProperties: true,
|
|
715
|
+
properties: {
|
|
716
|
+
inlineButtons: {
|
|
717
|
+
anyOf: [
|
|
718
|
+
{ type: "string", enum: ["off", "dm", "group", "all", "allowlist"] },
|
|
719
|
+
{ type: "boolean" }
|
|
720
|
+
]
|
|
721
|
+
},
|
|
722
|
+
interactive: { type: "boolean" },
|
|
723
|
+
forms: { type: "boolean" }
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function shadowobOpenClawPluginConfig() {
|
|
728
|
+
return {
|
|
729
|
+
plugins: {
|
|
730
|
+
enabled: true,
|
|
731
|
+
load: { paths: [SHADOWOB_OPENCLAW_EXTENSION_PATH] },
|
|
732
|
+
entries: { [SHADOWOB_OPENCLAW_PLUGIN_ID]: { enabled: true } }
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function shadowobRuntimeTokenEnvKey(buddyId) {
|
|
737
|
+
return `SHADOW_TOKEN_${buddyId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
|
|
738
|
+
}
|
|
739
|
+
function shadowobChannelConfigMetadata() {
|
|
740
|
+
return {
|
|
741
|
+
label: "ShadowOwnBuddy",
|
|
742
|
+
description: "Shadow server channel integration \u2014 chat with AI agents in Shadow channels",
|
|
743
|
+
schema: {
|
|
744
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
745
|
+
type: "object",
|
|
746
|
+
additionalProperties: true,
|
|
747
|
+
properties: {
|
|
748
|
+
name: { type: "string" },
|
|
749
|
+
enabled: { type: "boolean" },
|
|
750
|
+
token: { type: "string" },
|
|
751
|
+
serverUrl: { type: "string" },
|
|
752
|
+
buddyId: { type: "string" },
|
|
753
|
+
buddyName: { type: "string" },
|
|
754
|
+
buddyDescription: { type: "string" },
|
|
755
|
+
replyToMode: { type: "string", enum: ["first", "all", "off"] },
|
|
756
|
+
capabilities: shadowobChannelCapabilitiesSchema(),
|
|
757
|
+
accountAgentMap: {
|
|
758
|
+
type: "object",
|
|
759
|
+
additionalProperties: { type: "string" }
|
|
760
|
+
},
|
|
761
|
+
accounts: {
|
|
762
|
+
type: "object",
|
|
763
|
+
additionalProperties: {
|
|
764
|
+
type: "object",
|
|
765
|
+
additionalProperties: true,
|
|
766
|
+
properties: {
|
|
767
|
+
enabled: { type: "boolean" },
|
|
768
|
+
token: { type: "string" },
|
|
769
|
+
serverUrl: { type: "string" },
|
|
770
|
+
buddyId: { type: "string" },
|
|
771
|
+
buddyName: { type: "string" },
|
|
772
|
+
buddyDescription: { type: "string" },
|
|
773
|
+
agentId: { type: "string" },
|
|
774
|
+
capabilities: shadowobChannelCapabilitiesSchema(),
|
|
775
|
+
commerceOffers: {
|
|
776
|
+
type: "array",
|
|
777
|
+
items: {
|
|
778
|
+
type: "object",
|
|
779
|
+
additionalProperties: true,
|
|
780
|
+
properties: {
|
|
781
|
+
seedId: { type: "string" },
|
|
782
|
+
name: { type: "string" },
|
|
783
|
+
summary: { type: "string" },
|
|
784
|
+
offerId: { type: "string" },
|
|
785
|
+
productId: { type: "string" },
|
|
786
|
+
fileId: { type: "string" },
|
|
787
|
+
deliverableId: { type: "string" }
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
serverApps: {
|
|
792
|
+
type: "array",
|
|
793
|
+
items: {
|
|
794
|
+
type: "object",
|
|
795
|
+
additionalProperties: true,
|
|
796
|
+
properties: {
|
|
797
|
+
id: { type: "string" },
|
|
798
|
+
serverConfigId: { type: "string" },
|
|
799
|
+
manifestUrl: { type: "string" },
|
|
800
|
+
serverId: { type: "string" },
|
|
801
|
+
serverAppId: { type: "string" },
|
|
802
|
+
appKey: { type: "string" },
|
|
803
|
+
permissions: {
|
|
804
|
+
type: "array",
|
|
805
|
+
items: { type: "string" }
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
},
|
|
815
|
+
uiHints: {
|
|
816
|
+
token: {
|
|
817
|
+
label: "Agent Token",
|
|
818
|
+
sensitive: true,
|
|
819
|
+
placeholder: "Paste the JWT token generated in Shadow -> Agents"
|
|
820
|
+
},
|
|
821
|
+
serverUrl: {
|
|
822
|
+
label: "Server URL",
|
|
823
|
+
placeholder: "https://shadowob.com"
|
|
824
|
+
},
|
|
825
|
+
enabled: {
|
|
826
|
+
label: "Enabled"
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function buildShadowConfig(context) {
|
|
832
|
+
const agentConfig = context.agentConfig;
|
|
833
|
+
const shadowConfig = agentConfig;
|
|
834
|
+
const bindings = shadowConfig.bindings?.filter((b) => b.agentId === context.agent.id) ?? [];
|
|
835
|
+
const pluginConfig = shadowobOpenClawPluginConfig();
|
|
836
|
+
if (bindings.length === 0) {
|
|
837
|
+
return { ...pluginConfig, channels: { shadowob: { enabled: false } } };
|
|
838
|
+
}
|
|
839
|
+
const accounts = {};
|
|
840
|
+
const configBindings = [];
|
|
841
|
+
for (const binding of bindings) {
|
|
842
|
+
const buddy = shadowConfig.buddies?.find((b) => b.id === binding.targetId);
|
|
843
|
+
if (!buddy) continue;
|
|
844
|
+
const account = {
|
|
845
|
+
token: `\${env:SHADOW_TOKEN_${binding.targetId.toUpperCase().replace(/-/g, "_")}}`,
|
|
846
|
+
serverUrl: "${env:SHADOW_SERVER_URL}",
|
|
847
|
+
enabled: true,
|
|
848
|
+
buddyName: buddy.name,
|
|
849
|
+
...buddy.description ? { buddyDescription: buddy.description } : {},
|
|
850
|
+
...buddy.id ? { buddyId: buddy.id } : {},
|
|
851
|
+
capabilities: shadowobChannelCapabilities()
|
|
852
|
+
};
|
|
853
|
+
const commerceOffers = shadowConfig.commerce?.paidFiles?.filter((item) => {
|
|
854
|
+
const sellerBuddyId = item.sellerBuddyId ?? item.shop?.buddyId;
|
|
855
|
+
return sellerBuddyId === binding.targetId;
|
|
856
|
+
}).map((item) => ({
|
|
857
|
+
seedId: item.id,
|
|
858
|
+
name: item.name,
|
|
859
|
+
...item.summary ? { summary: item.summary } : {},
|
|
860
|
+
serverConfigId: item.serverId,
|
|
861
|
+
offerId: shadowEnvRef(shadowEnvKey("SHADOW_COMMERCE_OFFER", item.id)),
|
|
862
|
+
productId: shadowEnvRef(shadowEnvKey("SHADOW_COMMERCE_PRODUCT", item.id)),
|
|
863
|
+
fileId: shadowEnvRef(shadowEnvKey("SHADOW_COMMERCE_FILE", item.id)),
|
|
864
|
+
deliverableId: shadowEnvRef(shadowEnvKey("SHADOW_COMMERCE_DELIVERABLE", item.id))
|
|
865
|
+
})).filter((item) => item.offerId);
|
|
866
|
+
if (commerceOffers?.length) {
|
|
867
|
+
account.commerceOffers = commerceOffers;
|
|
868
|
+
}
|
|
869
|
+
const serverApps = shadowConfig.serverApps?.flatMap(
|
|
870
|
+
(app) => (app.grants ?? []).filter((grant) => grant.buddyId === binding.targetId).map((grant) => ({
|
|
871
|
+
id: app.id,
|
|
872
|
+
serverConfigId: app.serverId,
|
|
873
|
+
...app.manifestUrl ? { manifestUrl: app.manifestUrl } : {},
|
|
874
|
+
serverId: shadowEnvRef(shadowEnvKey("SHADOW_SERVER_APP_SERVER", app.id)),
|
|
875
|
+
serverAppId: shadowEnvRef(shadowEnvKey("SHADOW_SERVER_APP_ID", app.id)),
|
|
876
|
+
appKey: shadowEnvRef(shadowEnvKey("SHADOW_SERVER_APP_KEY", app.id)),
|
|
877
|
+
permissions: grant.permissions ?? ["*"]
|
|
878
|
+
}))
|
|
879
|
+
).filter((item) => item.serverAppId);
|
|
880
|
+
if (serverApps?.length) {
|
|
881
|
+
account.serverApps = serverApps;
|
|
882
|
+
}
|
|
883
|
+
if (binding.replyPolicy) {
|
|
884
|
+
const policy = binding.replyPolicy;
|
|
885
|
+
account.replyPolicy = {
|
|
886
|
+
mode: policy.mode,
|
|
887
|
+
...policy.custom ? { config: policy.custom } : {}
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
accounts[binding.targetId] = account;
|
|
891
|
+
configBindings.push({
|
|
892
|
+
agentId: context.agent.id,
|
|
893
|
+
type: "route",
|
|
894
|
+
match: { channel: "shadowob", accountId: binding.targetId }
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
...pluginConfig,
|
|
899
|
+
channels: {
|
|
900
|
+
shadowob: {
|
|
901
|
+
enabled: true,
|
|
902
|
+
capabilities: shadowobChannelCapabilities(),
|
|
903
|
+
accounts
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
bindings: configBindings
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
var shadowob_default = defineChannelPlugin(manifest_default, buildShadowConfig, (api) => {
|
|
910
|
+
api.onBuildPrompt(() => SHADOWOB_CLI_SKILL_INTRO);
|
|
911
|
+
api.onBuildRuntime((context) => {
|
|
912
|
+
const shadowConfig = context.agentConfig;
|
|
913
|
+
const bindings = shadowConfig.bindings?.filter((b) => b.agentId === context.agent.id) ?? [];
|
|
914
|
+
const accounts = bindings.map((binding) => {
|
|
915
|
+
const buddy = shadowConfig.buddies?.find((b) => b.id === binding.targetId);
|
|
916
|
+
if (!buddy) return void 0;
|
|
917
|
+
return {
|
|
918
|
+
buddyId: buddy.id,
|
|
919
|
+
buddyName: buddy.name,
|
|
920
|
+
...buddy.description ? { buddyDescription: buddy.description } : {},
|
|
921
|
+
tokenEnvKey: shadowobRuntimeTokenEnvKey(binding.targetId),
|
|
922
|
+
serverApps: shadowConfig.serverApps?.flatMap(
|
|
923
|
+
(app) => (app.grants ?? []).filter((grant) => grant.buddyId === binding.targetId).map((grant) => ({
|
|
924
|
+
id: app.id,
|
|
925
|
+
serverConfigId: app.serverId,
|
|
926
|
+
...app.manifestUrl ? { manifestUrl: app.manifestUrl } : {},
|
|
927
|
+
appKeyEnvKey: shadowEnvKey("SHADOW_SERVER_APP_KEY", app.id),
|
|
928
|
+
serverIdEnvKey: shadowEnvKey("SHADOW_SERVER_APP_SERVER", app.id),
|
|
929
|
+
permissions: grant.permissions ?? ["*"]
|
|
930
|
+
}))
|
|
931
|
+
).filter((item) => item.appKeyEnvKey),
|
|
932
|
+
...binding.replyPolicy ? { replyPolicy: binding.replyPolicy } : {}
|
|
933
|
+
};
|
|
934
|
+
}).filter((account) => Boolean(account));
|
|
935
|
+
return {
|
|
936
|
+
openclaw: {
|
|
937
|
+
manifestPatches: [
|
|
938
|
+
{
|
|
939
|
+
extensionId: SHADOWOB_OPENCLAW_EXTENSION_ID,
|
|
940
|
+
channelEnvVars: {
|
|
941
|
+
shadowob: ["SHADOW_SERVER_URL", "SHADOW_AGENT_TOKEN"]
|
|
942
|
+
},
|
|
943
|
+
channelConfigs: {
|
|
944
|
+
shadowob: shadowobChannelConfigMetadata()
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
]
|
|
948
|
+
},
|
|
949
|
+
shadowob: {
|
|
950
|
+
enabled: accounts.length > 0,
|
|
951
|
+
serverUrlEnvKey: "SHADOW_SERVER_URL",
|
|
952
|
+
accounts,
|
|
953
|
+
defaultAccountEnvKey: accounts[0]?.tokenEnvKey,
|
|
954
|
+
capabilities: shadowobChannelCapabilities()
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
});
|
|
958
|
+
api.onValidate((context) => {
|
|
959
|
+
const errors = [];
|
|
960
|
+
if (!context.secrets.SHADOW_SERVER_URL) {
|
|
961
|
+
errors.push({
|
|
962
|
+
path: "secrets.SHADOW_SERVER_URL",
|
|
963
|
+
message: "Shadow server URL is required for shadowob channel",
|
|
964
|
+
severity: "error"
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
const shadowConfig = context.agentConfig;
|
|
968
|
+
const buddyIds = new Set((shadowConfig.buddies ?? []).map((b) => b.id));
|
|
969
|
+
const serverIds = new Set((shadowConfig.servers ?? []).map((s) => s.id));
|
|
970
|
+
for (const binding of shadowConfig.bindings ?? []) {
|
|
971
|
+
if (!buddyIds.has(binding.targetId)) {
|
|
972
|
+
errors.push({
|
|
973
|
+
path: `bindings.${binding.targetId}`,
|
|
974
|
+
message: `Binding references non-existent buddy "${binding.targetId}"`,
|
|
975
|
+
severity: "error"
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
for (const app of shadowConfig.serverApps ?? []) {
|
|
980
|
+
if (!serverIds.has(app.serverId)) {
|
|
981
|
+
errors.push({
|
|
982
|
+
path: `serverApps.${app.id}.serverId`,
|
|
983
|
+
message: `Server App "${app.id}" references non-existent server "${app.serverId}"`,
|
|
984
|
+
severity: "error"
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
if (!app.manifestUrl && !app.manifest) {
|
|
988
|
+
errors.push({
|
|
989
|
+
path: `serverApps.${app.id}`,
|
|
990
|
+
message: `Server App "${app.id}" must provide a manifestUrl or manifest`,
|
|
991
|
+
severity: "error"
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
for (const grant of app.grants ?? []) {
|
|
995
|
+
if (!buddyIds.has(grant.buddyId)) {
|
|
996
|
+
errors.push({
|
|
997
|
+
path: `serverApps.${app.id}.grants.${grant.buddyId}`,
|
|
998
|
+
message: `Server App "${app.id}" grants non-existent buddy "${grant.buddyId}"`,
|
|
999
|
+
severity: "error"
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
return {
|
|
1005
|
+
valid: errors.filter((e) => e.severity === "error").length === 0,
|
|
1006
|
+
errors
|
|
1007
|
+
};
|
|
1008
|
+
});
|
|
1009
|
+
api.onProvision(async (context) => {
|
|
1010
|
+
const serverUrl = context.secrets.SHADOW_SERVER_URL;
|
|
1011
|
+
const provisionUrl = context.secrets.SHADOW_PROVISION_URL ?? process.env.SHADOW_PROVISION_URL ?? serverUrl;
|
|
1012
|
+
const userToken = context.secrets.SHADOW_USER_TOKEN;
|
|
1013
|
+
context.logger.dim(
|
|
1014
|
+
` shadowob: provisionUrl=${provisionUrl} tokenPresent=${Boolean(userToken)} tokenLen=${userToken?.length ?? 0}`
|
|
1015
|
+
);
|
|
1016
|
+
if (!serverUrl || !userToken) {
|
|
1017
|
+
context.logger.dim(
|
|
1018
|
+
" shadowob provision skipped: SHADOW_SERVER_URL / SHADOW_USER_TOKEN not set"
|
|
1019
|
+
);
|
|
1020
|
+
return { state: {} };
|
|
1021
|
+
}
|
|
1022
|
+
const result = await provisionShadowResources(context.config, {
|
|
1023
|
+
serverUrl: provisionUrl ?? serverUrl,
|
|
1024
|
+
userToken,
|
|
1025
|
+
dryRun: context.dryRun,
|
|
1026
|
+
existingState: context.previousState,
|
|
1027
|
+
logger: context.logger
|
|
1028
|
+
});
|
|
1029
|
+
const secrets = {
|
|
1030
|
+
SHADOW_SERVER_URL: serverUrl
|
|
1031
|
+
};
|
|
1032
|
+
for (const [buddyId, { token }] of result.buddies) {
|
|
1033
|
+
const key = shadowobRuntimeTokenEnvKey(buddyId);
|
|
1034
|
+
secrets[key] = token;
|
|
1035
|
+
}
|
|
1036
|
+
for (const [seedId, ids] of result.commerce) {
|
|
1037
|
+
secrets[shadowEnvKey("SHADOW_COMMERCE_SHOP", seedId)] = ids.shopId;
|
|
1038
|
+
secrets[shadowEnvKey("SHADOW_COMMERCE_PRODUCT", seedId)] = ids.productId;
|
|
1039
|
+
secrets[shadowEnvKey("SHADOW_COMMERCE_OFFER", seedId)] = ids.offerId;
|
|
1040
|
+
secrets[shadowEnvKey("SHADOW_COMMERCE_FILE", seedId)] = ids.fileId;
|
|
1041
|
+
secrets[shadowEnvKey("SHADOW_COMMERCE_DELIVERABLE", seedId)] = ids.deliverableId;
|
|
1042
|
+
}
|
|
1043
|
+
for (const [appId, ids] of result.serverApps) {
|
|
1044
|
+
secrets[shadowEnvKey("SHADOW_SERVER_APP_SERVER", appId)] = ids.serverId;
|
|
1045
|
+
secrets[shadowEnvKey("SHADOW_SERVER_APP_ID", appId)] = ids.serverAppId;
|
|
1046
|
+
secrets[shadowEnvKey("SHADOW_SERVER_APP_KEY", appId)] = ids.appKey;
|
|
1047
|
+
}
|
|
1048
|
+
return {
|
|
1049
|
+
state: {
|
|
1050
|
+
shadowServerUrl: serverUrl,
|
|
1051
|
+
servers: Object.fromEntries(result.servers),
|
|
1052
|
+
channels: Object.fromEntries(result.channels),
|
|
1053
|
+
buddies: Object.fromEntries(
|
|
1054
|
+
[...result.buddies.entries()].map(([k, v]) => [
|
|
1055
|
+
k,
|
|
1056
|
+
{ agentId: v.agentId, userId: v.userId }
|
|
1057
|
+
])
|
|
1058
|
+
),
|
|
1059
|
+
...result.listings.size > 0 ? { listings: Object.fromEntries(result.listings) } : {},
|
|
1060
|
+
...result.commerce.size > 0 ? { commerce: Object.fromEntries(result.commerce) } : {},
|
|
1061
|
+
...result.serverApps.size > 0 ? { serverApps: Object.fromEntries(result.serverApps) } : {}
|
|
1062
|
+
},
|
|
1063
|
+
secrets
|
|
1064
|
+
};
|
|
1065
|
+
});
|
|
1066
|
+
});
|
|
1067
|
+
export {
|
|
1068
|
+
shadowob_default as default
|
|
1069
|
+
};
|