chainlesschain 0.162.182 → 0.162.184
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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DzZ5_xoj.js → AIOps-DzAl5fGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DeLXddIg.js → ActionButton-DAHtMGeC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-rvDCmet1.js → Analytics-BHQaexv5.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Yt6QVDnp.js → AppLayout-Phz_pD0n.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BS112K5L.js → Artifacts-DHt47kCA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-0wJuQrNu.js → Audit--Y_8FULl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-B90PMgbn.js → BackgroundAgents-N1zchH7D.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-TkgMglQQ.js → Backup-B_BoyN30.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CQVS9p1l.js → BaseInput-Gl4uQHcT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-YTjQHoC7.js → Chat-Cg-eIyAZ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-5bwaOabq.js → Checkbox-D0MGR2wA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFmNQ_Xe.js → Codegen-BdObJEFn.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BT_u4Dzp.js → Col--ghxXT3z.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CJP52u5b.js → Community-BUFeXOqz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BpzhJE9i.js → Compact-DhQO2_Mr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Cg7rfslp.js → Compliance-3HoTqKYd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-JisxL6Yy.js → Cowork-B3zmO4VW.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Bu7Ug_MC.js → Cron-CJDrZOcc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CvuqoLdj.js → Crosschain-SMl15obd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C2u9G8Pu.js → DID-CgTrTWrl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BFt5Yv3d.js → Dashboard-dXFKaKol.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DIaT9Wxq.js → Dropdown-qQHEhCvI.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D_A601Y4.js → EmailListRenderer-C_ej0MWH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--IyQ8I8w.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BihWHxuY.js → Federation-Cio2y49h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BB8dOXc_.js → FormItemContext-ClJz1cYp.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DQYhj1HH.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B-2QZxGf.js → Git-CevLwp5u.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CusSb-nG.js → Governance-CW9YyQUY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BAjmvPPL.js → Inference-CQqeAyER.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DoNYWKYq.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B8fKWD-n.js → Logs-DIuHri5d.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BlJm1J7E.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CyOscbSL.js → Marketplace-CY1VhFGx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Ex6Om5da.js → McpTools-BBAbi1vt.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CDcD8l8r.js → Memory-BWYGHxJA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xfBqoFDg.js → MobileBridge-CPIFkW7A.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BwObMMA2.js → MobileProjects-DQyWxCho.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CIWd36OV.js → Mtc-Bo3GnQbx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CPX0ouuA.js → MtcAudit-C8-c4rSW.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BDJsIUuW.js → Multisig-ChqMTtoq.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DEkbuD3N.js → NLProgramming-C48TyhvI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DJpHRhH1.js → Notes-zogUFWxp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C1UZ-Qd5.js → NotificationSettings-v-K9XpSB.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Bbhj0fac.js → Organization-Boi1dQfR.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-1H04x3kx.js → Overflow-DM5YWwgA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CDpPe3et.js → P2P-C57LKpKK.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-MUX0Bpvw.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-A07-0Ci1.js → Permissions-D6fB9YSp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DhhDWxAZ.js → PersonalDataHub-Bp7EefEo.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CyfbJfe-.js → Pipeline-BVln3hZf.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CNFps3ED.js → Privacy-Bf8P3nF6.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit--T-u3Dr5.js → ProjectInit-DM6wA2E2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJmrK2fC.js → ProjectSettings-CGbc5Iho.js} +2 -2
- package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DLzzbOZd.js → Providers-B2kJcxS6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CexOomKS.js → QrScannerModal-Cxu7dXYy.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-QklGi19_.js → QuickAsk-DTlrTPms.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bb9kwSw8.js → Recommend-Cm3X-cCj.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DZuVNNN5.js → RemoteSession-DNb2kjvt.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Dr5cLP0N.js → Reputation-CZUFFjc1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-oCQHYv5t.js → Row-DAPgloxA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-IFDEykIR.js → RssFeed-dsx9S793.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CspVIxDc.js → Search-DnnC8kz-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bm66o_DH.js → Security-VuZknRMK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DvanNqXl.js → Services-B3XNX8Vx.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CHjEe9_O.js → Skeleton-Dl1nVtCj.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +1 -0
- package/src/assets/web-panel/assets/{Sla-DxZm1491.js → Sla-HzU56n1z.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DWxEEclC.js → SpeechSettings-CRle7Jl2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-FIvOFw5p.js → SyncSettings-DWadz7Sl.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CaNjzV_t.js → Templates-BBAgvrlM.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DjlP6TmH.js → Tenant-BsbbvGN8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-DoZ8y7y8.js → TimelineRenderer-mTyjNE6r.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-390cg2TC.js → Tokens-Bz97oBgs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CLrtCj5_.js → Trigger-BTRKC140.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DpESfSPn.js → Trust-B4A_KuyX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CN6279O0.js → UkeySign-DES9TmLU.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DPtAUlSe.js → VideoEditing-ClXs7Qxp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-nKOMV_1n.js → Wallet-IzZqFz-7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-HuXlSOc4.js → WebAuthn-BtOP04g_.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CUfFjARi.js → WorkflowEditor-_0cqnopY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-6Eq0PcZJ.js → chat-ax7193uU.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BP_Sf6Iy.js → colors-Bu4WGJah.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BufMKIh5.js → compact-item-BX1dlTW0.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BBMXxWSW.js → createContext-CktA_Dj7.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-x877iKKS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-fUK_tRO2.js → hasIn-D5wd817l.js} +1 -1
- package/src/assets/web-panel/assets/{index-t8N1tSeX.js → index-5PYYkr8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLpIs_P_.js → index-A4uBPQCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVsqvI4M.js → index-BAtBNfkr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkg4ZePE.js → index-BLLsUNDc.js} +1 -1
- package/src/assets/web-panel/assets/{index-cpEli44X.js → index-BQtoRyTO.js} +1 -1
- package/src/assets/web-panel/assets/{index-wKTLbKyc.js → index-BRfXoRuh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Etk0Te.js → index-Bad2CYeH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwSf5pnf.js → index-BcGdL3sG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3FuwBewR.js → index-C3HmzCLt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHy6yNiF.js → index-C8DkWyNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgiECHu2.js → index-C8tLR2TG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C44atv4k.js → index-CP8ELJBc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW_G-MXw.js → index-CUForzsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-pum7HYBA.js → index-CYRE0NJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWKoBU8R.js → index-CeVZaJSC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4_MuGkd.js → index-CfzxR61U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dyngbd29.js → index-Ci3y1ZAv.js} +19 -19
- package/src/assets/web-panel/assets/{index-fzaMtSJ8.js → index-CjEJjl5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp8vhyZy.js → index-Cv7bVWJ5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPdUNk1i.js → index-D-o4Rwjk.js} +1 -1
- package/src/assets/web-panel/assets/index-D7pfPouB.js +1 -0
- package/src/assets/web-panel/assets/{index-CeASi4fr.js → index-D9op8poE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DASluSqY.js → index-DF1TysIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocdRcbd.js → index-DYo9S5OT.js} +1 -1
- package/src/assets/web-panel/assets/{index-2A5ph8b2.js → index-D_cycOET.js} +1 -1
- package/src/assets/web-panel/assets/{index-wV6iGdpY.js → index-Db3CpWTr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMLHv389.js → index-DkD1K-fF.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9WYSn4m.js → index-DmPJ6T6F.js} +1 -1
- package/src/assets/web-panel/assets/{index-uCbEN7cY.js → index-DmVUeWUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmkMXwnR.js → index-Dzdr0zDf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZ6Yt17L.js → index-PBLzwYnu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2DdTP2.js → index-bFCaM7Eq.js} +1 -1
- package/src/assets/web-panel/assets/index-e4i-VLBd.js +1 -0
- package/src/assets/web-panel/assets/{index-GwdRUZ-x.js → index-h3KJZr95.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2vS66eI.js → index-iHcPtQ2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-6GmBa4Kg.js → index-jzlzm4VK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9vU25ij.js → index-o14NNnxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGjmILDm.js → index-tYPSfGbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-LvCBfTLo.js → index-ytp2H5Zx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-V5Fo95FM.js → initDefaultProps-D2Iut83B.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BYWU0hUT.js → motion-BuaJVdK9.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cr1M-J2h.js → move-CaEuM92y.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BysQKi2N.js → mtc-parser-wuKNBwys.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D1zHU8GC.js → omit-7a3eklaq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CUArxtfi.js → pickAttrs-Dn4z7KvJ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChoYEys0.js → placementArrow-DVn_qT4-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQRs3Mo7.js → responsiveObserve-Dodz1OMQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B8zbsCkg.js → slide-CxoGskbp.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C2U8Gs6h.js → statusUtils-DBh_epQH.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dd8MgL_V.js → styleChecker-RXQ4gG5P.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hknqY5NH.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CLTAmcfw.js → useFs-CI_ySNxJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-ByegHbiB.js → usePersonalDataHub-C6973rZP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DqlAy5DP.js → vnode-CalynwKr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DiXiztQn.js → zoom-CM1cd-Q0.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/agent.js +9 -0
- package/src/commands/background-session.js +60 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +318 -43
- package/src/commands/session.js +44 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/harness/jsonl-session-store.js +59 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-runner.js +19 -1
- package/src/lib/agent-team/team-worktree.js +3 -0
- package/src/lib/async-hook-supervisor.cjs +82 -10
- package/src/lib/async-hook-supervisor.js +50 -4
- package/src/lib/background-agent-supervisor.js +49 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/hook-runner.cjs +98 -17
- package/src/lib/hook-runner.js +77 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +188 -77
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +18 -0
- package/src/lib/lsp/lsp-client.js +12 -1
- package/src/lib/plugin-monitor-supervisor.js +53 -16
- package/src/lib/plugin-runtime/bin.js +1 -1
- package/src/lib/plugin-runtime/install.js +465 -54
- package/src/lib/plugin-runtime/mcp.js +21 -2
- package/src/lib/plugin-runtime/monitors.js +19 -3
- package/src/lib/plugin-runtime/sandbox-policy.js +99 -0
- package/src/lib/plugin-runtime/scopes.js +13 -2
- package/src/lib/plugin-usage-attribution.js +44 -0
- package/src/lib/process-execution-broker/index.js +705 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +1831 -157
- package/src/lib/session-usage.js +278 -1
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +38 -9
- package/src/runtime/agent-core.js +32 -4
- package/src/runtime/headless-runner.js +46 -1
- package/src/runtime/headless-stream.js +54 -3
- package/src/runtime/mcp-config.js +15 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-GlJtqLjT.js +0 -1
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +0 -1
- package/src/assets/web-panel/assets/Skills-Cb8jWjMX.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +0 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +0 -3
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +0 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +0 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +0 -1
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable, secret-free governance records for short-lived `cc team` / `cc
|
|
3
|
+
* batch` worktree runs.
|
|
4
|
+
*
|
|
5
|
+
* These are deliberately separate from background-agent state: a team/batch
|
|
6
|
+
* task is not independently attachable or stoppable, and pretending otherwise
|
|
7
|
+
* could make `cc daemon stop` kill the shared coordinator process. The daemon
|
|
8
|
+
* JSON view may project these records read-only for IDE task rows.
|
|
9
|
+
*
|
|
10
|
+
* Prompts, argv, tool arguments, model output, credentials, and side-effect
|
|
11
|
+
* metadata never enter this store.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
readdirSync,
|
|
19
|
+
renameSync,
|
|
20
|
+
writeFileSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
24
|
+
import { getHomeDir } from "./paths.js";
|
|
25
|
+
|
|
26
|
+
export const COLLABORATION_RUN_VERSION = 1;
|
|
27
|
+
const RUN_KINDS = new Set(["team", "batch"]);
|
|
28
|
+
const PERMISSION_MODES = new Set([
|
|
29
|
+
"manual",
|
|
30
|
+
"auto",
|
|
31
|
+
"dontAsk",
|
|
32
|
+
"default",
|
|
33
|
+
"plan",
|
|
34
|
+
"acceptEdits",
|
|
35
|
+
"bypassPermissions",
|
|
36
|
+
]);
|
|
37
|
+
const TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "cancelled"]);
|
|
38
|
+
const UNIT_STATUSES = new Set([
|
|
39
|
+
"pending",
|
|
40
|
+
"running",
|
|
41
|
+
"completed",
|
|
42
|
+
"failed",
|
|
43
|
+
"test-failed",
|
|
44
|
+
"no-changes",
|
|
45
|
+
"cancelled",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
export const _deps = {
|
|
49
|
+
now: () => Date.now(),
|
|
50
|
+
randomHex: () => randomBytes(3).toString("hex"),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function boundedString(value, max) {
|
|
54
|
+
if (value === null || value === undefined) return null;
|
|
55
|
+
const clean = String(value)
|
|
56
|
+
.replace(/\p{Cc}/gu, "")
|
|
57
|
+
.trim();
|
|
58
|
+
return clean ? clean.slice(0, max) : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function positiveNumber(value) {
|
|
62
|
+
const number = Number(value);
|
|
63
|
+
return Number.isFinite(number) && number > 0 ? number : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function nonNegativeInteger(value) {
|
|
67
|
+
const number = Number(value);
|
|
68
|
+
return Number.isFinite(number) && number >= 0 ? Math.floor(number) : 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function safeKind(kind) {
|
|
72
|
+
const value = String(kind || "");
|
|
73
|
+
if (!RUN_KINDS.has(value)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Unsupported collaboration run kind: ${value || "(empty)"}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function safeRunId(id) {
|
|
82
|
+
const value = String(id || "");
|
|
83
|
+
if (value.length > 160 || !/^(team|batch)-[a-zA-Z0-9._-]+$/.test(value)) {
|
|
84
|
+
throw new Error(`Invalid collaboration run id: ${value || "(empty)"}`);
|
|
85
|
+
}
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function collaborationRunsDir() {
|
|
90
|
+
const dir =
|
|
91
|
+
process.env.CC_COLLABORATION_RUNS_DIR ||
|
|
92
|
+
join(getHomeDir(), "collaboration-runs");
|
|
93
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
94
|
+
return dir;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createCollaborationRunId(kind) {
|
|
98
|
+
const normalizedKind = safeKind(kind);
|
|
99
|
+
return `${normalizedKind}-${Math.floor(_deps.now())}-${_deps.randomHex()}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function createCollaborationSessionId(runId, key) {
|
|
103
|
+
const safeId = safeRunId(runId);
|
|
104
|
+
const digest = createHash("sha256")
|
|
105
|
+
.update(String(key || "unit"))
|
|
106
|
+
.digest("hex")
|
|
107
|
+
.slice(0, 12);
|
|
108
|
+
return `session-${safeId}-${digest}`.slice(0, 256);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function collaborationRunPath(id) {
|
|
112
|
+
return join(collaborationRunsDir(), `${safeRunId(id)}.json`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeBudget(resourceBudget) {
|
|
116
|
+
const source =
|
|
117
|
+
resourceBudget && typeof resourceBudget === "object" ? resourceBudget : {};
|
|
118
|
+
return {
|
|
119
|
+
maxTurns: positiveNumber(source.maxTurns),
|
|
120
|
+
maxCostUsd: positiveNumber(source.maxCostUsd),
|
|
121
|
+
maxTasks: positiveNumber(source.maxTasks),
|
|
122
|
+
maxTokens: positiveNumber(source.maxTokens),
|
|
123
|
+
maxWallMs: positiveNumber(source.maxWallMs),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function summarizeSideEffects(source) {
|
|
128
|
+
const ops =
|
|
129
|
+
source && typeof source.list === "function"
|
|
130
|
+
? source.list()
|
|
131
|
+
: Array.isArray(source?.ops)
|
|
132
|
+
? source.ops
|
|
133
|
+
: null;
|
|
134
|
+
if (ops) {
|
|
135
|
+
const count = (state) => ops.filter((op) => op?.state === state).length;
|
|
136
|
+
return {
|
|
137
|
+
total: ops.length,
|
|
138
|
+
unsettled: ops.filter(
|
|
139
|
+
(op) => op?.state === "prepared" || op?.state === "started",
|
|
140
|
+
).length,
|
|
141
|
+
unknown: count("unknown"),
|
|
142
|
+
committed: count("committed"),
|
|
143
|
+
failed: count("failed"),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const value = source && typeof source === "object" ? source : {};
|
|
147
|
+
return {
|
|
148
|
+
total: nonNegativeInteger(value.total),
|
|
149
|
+
unsettled: nonNegativeInteger(value.unsettled),
|
|
150
|
+
unknown: nonNegativeInteger(value.unknown),
|
|
151
|
+
committed: nonNegativeInteger(value.committed),
|
|
152
|
+
failed: nonNegativeInteger(value.failed),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function normalizeUnit(unit, run) {
|
|
157
|
+
const key = boundedString(unit?.key, 256);
|
|
158
|
+
if (!key) throw new Error("Collaboration unit key is required");
|
|
159
|
+
const status = UNIT_STATUSES.has(unit?.status) ? unit.status : "pending";
|
|
160
|
+
const sessionId = boundedString(unit?.sessionId, 256);
|
|
161
|
+
return {
|
|
162
|
+
key,
|
|
163
|
+
owner: `${run.kind}:${run.id}:${key}`.slice(0, 512),
|
|
164
|
+
sessionId,
|
|
165
|
+
branch: boundedString(unit?.branch, 512),
|
|
166
|
+
worktreePath: boundedString(unit?.worktreePath, 4096),
|
|
167
|
+
status,
|
|
168
|
+
startedAt: positiveNumber(unit?.startedAt),
|
|
169
|
+
endedAt: positiveNumber(unit?.endedAt),
|
|
170
|
+
sideEffects: summarizeSideEffects(unit?.sideEffects),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeRun(input) {
|
|
175
|
+
const kind = safeKind(input?.kind);
|
|
176
|
+
const id = safeRunId(input?.id);
|
|
177
|
+
if (!id.startsWith(`${kind}-`)) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Collaboration run id "${id}" does not match kind "${kind}"`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const permissionMode = PERMISSION_MODES.has(input?.permissionMode)
|
|
183
|
+
? input.permissionMode
|
|
184
|
+
: "default";
|
|
185
|
+
const status = new Set(["running", ...TERMINAL_RUN_STATUSES]).has(
|
|
186
|
+
input?.status,
|
|
187
|
+
)
|
|
188
|
+
? input.status
|
|
189
|
+
: "running";
|
|
190
|
+
const startedAt = positiveNumber(input?.startedAt) || _deps.now();
|
|
191
|
+
const run = {
|
|
192
|
+
version: COLLABORATION_RUN_VERSION,
|
|
193
|
+
id,
|
|
194
|
+
kind,
|
|
195
|
+
owner: `${kind}:${id}`,
|
|
196
|
+
status,
|
|
197
|
+
repoRoot: boundedString(input?.repoRoot, 4096),
|
|
198
|
+
permissionMode,
|
|
199
|
+
resourceBudget: normalizeBudget(input?.resourceBudget),
|
|
200
|
+
startedAt,
|
|
201
|
+
updatedAt: positiveNumber(input?.updatedAt) || startedAt,
|
|
202
|
+
endedAt: TERMINAL_RUN_STATUSES.has(status)
|
|
203
|
+
? positiveNumber(input?.endedAt) || startedAt
|
|
204
|
+
: null,
|
|
205
|
+
units: [],
|
|
206
|
+
};
|
|
207
|
+
const seen = new Set();
|
|
208
|
+
for (const unit of Array.isArray(input?.units)
|
|
209
|
+
? input.units.slice(0, 1000)
|
|
210
|
+
: []) {
|
|
211
|
+
const normalized = normalizeUnit(unit, run);
|
|
212
|
+
if (seen.has(normalized.key)) {
|
|
213
|
+
throw new Error(`Duplicate collaboration unit key: ${normalized.key}`);
|
|
214
|
+
}
|
|
215
|
+
seen.add(normalized.key);
|
|
216
|
+
run.units.push(normalized);
|
|
217
|
+
}
|
|
218
|
+
return run;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function writeCollaborationRun(input) {
|
|
222
|
+
const run = normalizeRun(input);
|
|
223
|
+
const target = collaborationRunPath(run.id);
|
|
224
|
+
const temp = `${target}.${process.pid}.tmp`;
|
|
225
|
+
writeFileSync(temp, JSON.stringify(run, null, 2), { mode: 0o600 });
|
|
226
|
+
renameSync(temp, target);
|
|
227
|
+
return run;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function createCollaborationRun(input) {
|
|
231
|
+
const kind = safeKind(input?.kind);
|
|
232
|
+
const id = input?.id ? safeRunId(input.id) : createCollaborationRunId(kind);
|
|
233
|
+
if (existsSync(collaborationRunPath(id))) {
|
|
234
|
+
throw new Error(`Collaboration run already exists: ${id}`);
|
|
235
|
+
}
|
|
236
|
+
return writeCollaborationRun({
|
|
237
|
+
...input,
|
|
238
|
+
id,
|
|
239
|
+
kind,
|
|
240
|
+
status: "running",
|
|
241
|
+
startedAt: input?.startedAt || _deps.now(),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function readCollaborationRun(id) {
|
|
246
|
+
const target = collaborationRunPath(id);
|
|
247
|
+
if (!existsSync(target)) return null;
|
|
248
|
+
try {
|
|
249
|
+
return normalizeRun(JSON.parse(readFileSync(target, "utf8")));
|
|
250
|
+
} catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function updateCollaborationRun(id, updater) {
|
|
256
|
+
const current = readCollaborationRun(id);
|
|
257
|
+
if (!current) throw new Error(`Collaboration run not found: ${id}`);
|
|
258
|
+
const draft = JSON.parse(JSON.stringify(current));
|
|
259
|
+
const changed =
|
|
260
|
+
typeof updater === "function"
|
|
261
|
+
? updater(draft) || draft
|
|
262
|
+
: { ...draft, ...updater };
|
|
263
|
+
return writeCollaborationRun({
|
|
264
|
+
...changed,
|
|
265
|
+
id: current.id,
|
|
266
|
+
kind: current.kind,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function updateCollaborationUnit(runId, key, patch = {}) {
|
|
271
|
+
const targetKey = boundedString(key, 256);
|
|
272
|
+
return updateCollaborationRun(runId, (run) => {
|
|
273
|
+
const index = run.units.findIndex((unit) => unit.key === targetKey);
|
|
274
|
+
if (index < 0) {
|
|
275
|
+
throw new Error(`Collaboration unit not found: ${targetKey}`);
|
|
276
|
+
}
|
|
277
|
+
const current = run.units[index];
|
|
278
|
+
const status = UNIT_STATUSES.has(patch.status)
|
|
279
|
+
? patch.status
|
|
280
|
+
: current.status;
|
|
281
|
+
run.units[index] = {
|
|
282
|
+
...current,
|
|
283
|
+
...(patch.branch !== undefined
|
|
284
|
+
? { branch: boundedString(patch.branch, 512) }
|
|
285
|
+
: {}),
|
|
286
|
+
...(patch.worktreePath !== undefined
|
|
287
|
+
? { worktreePath: boundedString(patch.worktreePath, 4096) }
|
|
288
|
+
: {}),
|
|
289
|
+
status,
|
|
290
|
+
startedAt:
|
|
291
|
+
patch.startedAt !== undefined
|
|
292
|
+
? positiveNumber(patch.startedAt)
|
|
293
|
+
: current.startedAt,
|
|
294
|
+
endedAt:
|
|
295
|
+
patch.endedAt !== undefined
|
|
296
|
+
? positiveNumber(patch.endedAt)
|
|
297
|
+
: current.endedAt,
|
|
298
|
+
sideEffects:
|
|
299
|
+
patch.sideEffects !== undefined
|
|
300
|
+
? summarizeSideEffects(patch.sideEffects)
|
|
301
|
+
: current.sideEffects,
|
|
302
|
+
};
|
|
303
|
+
run.updatedAt = _deps.now();
|
|
304
|
+
return run;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function finalizeCollaborationRun(id, status = "completed") {
|
|
309
|
+
if (!TERMINAL_RUN_STATUSES.has(status)) {
|
|
310
|
+
throw new Error(`Invalid terminal collaboration status: ${status}`);
|
|
311
|
+
}
|
|
312
|
+
return updateCollaborationRun(id, (run) => {
|
|
313
|
+
run.status = status;
|
|
314
|
+
run.updatedAt = _deps.now();
|
|
315
|
+
run.endedAt = run.updatedAt;
|
|
316
|
+
return run;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function listCollaborationRuns({ limit = 200 } = {}) {
|
|
321
|
+
const capped = Math.max(1, Math.min(1000, Number(limit) || 200));
|
|
322
|
+
return readdirSync(collaborationRunsDir())
|
|
323
|
+
.filter((name) => /^(team|batch)-[a-zA-Z0-9._-]+\.json$/.test(name))
|
|
324
|
+
.map((name) => readCollaborationRun(name.slice(0, -5)))
|
|
325
|
+
.filter(Boolean)
|
|
326
|
+
.sort((a, b) => b.startedAt - a.startedAt)
|
|
327
|
+
.slice(0, capped);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Project run units into the bounded shape consumed by IDE worktree task rows.
|
|
332
|
+
* The caller can publish this as `managedTasks`; no control operation consumes
|
|
333
|
+
* these records.
|
|
334
|
+
*/
|
|
335
|
+
export function projectCollaborationTasks(runs) {
|
|
336
|
+
const rows = [];
|
|
337
|
+
for (const run of Array.isArray(runs) ? runs.slice(0, 200) : []) {
|
|
338
|
+
for (const unit of Array.isArray(run?.units)
|
|
339
|
+
? run.units.slice(0, 1000)
|
|
340
|
+
: []) {
|
|
341
|
+
if (!unit.branch && !unit.worktreePath) continue;
|
|
342
|
+
rows.push({
|
|
343
|
+
managedTaskId: `${run.id}:${unit.key}`.slice(0, 512),
|
|
344
|
+
runId: run.id,
|
|
345
|
+
runKind: run.kind,
|
|
346
|
+
branch: unit.branch,
|
|
347
|
+
worktreePath: unit.worktreePath,
|
|
348
|
+
status: unit.status,
|
|
349
|
+
governance: {
|
|
350
|
+
version: 1,
|
|
351
|
+
owner: unit.owner,
|
|
352
|
+
sessionId: unit.sessionId,
|
|
353
|
+
permissionMode: run.permissionMode,
|
|
354
|
+
resourceBudget: run.resourceBudget,
|
|
355
|
+
},
|
|
356
|
+
sideEffects: summarizeSideEffects(unit.sideEffects),
|
|
357
|
+
});
|
|
358
|
+
if (rows.length >= 1000) return rows;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return rows;
|
|
362
|
+
}
|
package/src/lib/hook-runner.cjs
CHANGED
|
@@ -26,9 +26,14 @@ const {
|
|
|
26
26
|
loadShellConfig,
|
|
27
27
|
} = require("./shell-selector.cjs");
|
|
28
28
|
const { mergeHookDecisions } = require("./hook-event-bus.cjs");
|
|
29
|
+
const { buildManagedHookEnvironment } = require("./hook-environment.cjs");
|
|
29
30
|
const {
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
buildExplicitHookShellInvocation,
|
|
32
|
+
issueTrustedHookSandboxContract,
|
|
33
|
+
requireTrustedHookRoot,
|
|
34
|
+
requiresExplicitHookShell,
|
|
35
|
+
sandboxBoundaryError,
|
|
36
|
+
} = require("./hook-shell-command.cjs");
|
|
32
37
|
|
|
33
38
|
const _deps = { runSync: null, run: null };
|
|
34
39
|
Object.defineProperties(_deps, {
|
|
@@ -125,17 +130,13 @@ function hookSpawnFailure(error) {
|
|
|
125
130
|
const sandboxDenied =
|
|
126
131
|
error?.code === "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
127
132
|
return {
|
|
128
|
-
decision: sandboxDenied
|
|
129
|
-
? HOOK_DECISIONS.BLOCK
|
|
130
|
-
: HOOK_DECISIONS.CONTINUE,
|
|
133
|
+
decision: sandboxDenied ? HOOK_DECISIONS.BLOCK : HOOK_DECISIONS.CONTINUE,
|
|
131
134
|
reason: sandboxDenied
|
|
132
135
|
? `hook sandbox denied: ${error.message}`
|
|
133
136
|
: `hook spawn failed: ${error.message}`,
|
|
134
137
|
exitCode: null,
|
|
135
138
|
error: error.message,
|
|
136
|
-
...(sandboxDenied
|
|
137
|
-
? { sandboxDenied: true }
|
|
138
|
-
: { nonBlockingError: true }),
|
|
139
|
+
...(sandboxDenied ? { sandboxDenied: true } : { nonBlockingError: true }),
|
|
139
140
|
};
|
|
140
141
|
}
|
|
141
142
|
|
|
@@ -321,12 +322,17 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
321
322
|
let res;
|
|
322
323
|
try {
|
|
323
324
|
const sandboxPolicy = resolveHookSandboxPolicy({}, opts);
|
|
325
|
+
const explicitSandboxShell = requiresExplicitHookShell(sandboxPolicy);
|
|
326
|
+
const trustedRoot = explicitSandboxShell
|
|
327
|
+
? requireTrustedHookRoot(cwd)
|
|
328
|
+
: null;
|
|
329
|
+
const workingDirectory = trustedRoot || cwd || process.cwd();
|
|
324
330
|
const common = {
|
|
325
331
|
input: JSON.stringify({
|
|
326
332
|
...input,
|
|
327
333
|
schema_version: input.schema_version ?? HOOK_PAYLOAD_SCHEMA_VERSION,
|
|
328
334
|
}),
|
|
329
|
-
cwd:
|
|
335
|
+
cwd: workingDirectory,
|
|
330
336
|
encoding: "utf-8",
|
|
331
337
|
timeout,
|
|
332
338
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -346,6 +352,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
346
352
|
pluginId: opts.pluginId || null,
|
|
347
353
|
pluginVersion: opts.pluginVersion || null,
|
|
348
354
|
pluginSource: opts.pluginSource || null,
|
|
355
|
+
...(explicitSandboxShell || usePowershell ? { shell: false } : {}),
|
|
349
356
|
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
350
357
|
};
|
|
351
358
|
const brokerRunSync =
|
|
@@ -355,13 +362,42 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
355
362
|
? opts.broker["spawnSync"].bind(opts.broker)
|
|
356
363
|
: null;
|
|
357
364
|
if (brokerRunSync) {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
365
|
+
const powershellOptions = {
|
|
366
|
+
executionPolicy: loadShellConfig({ cwd: common.cwd }).executionPolicy,
|
|
367
|
+
};
|
|
368
|
+
const invocation = explicitSandboxShell
|
|
369
|
+
? buildExplicitHookShellInvocation(command, {
|
|
370
|
+
shellKind,
|
|
371
|
+
buildPowershellArgv,
|
|
372
|
+
powershellOptions,
|
|
362
373
|
})
|
|
363
|
-
:
|
|
364
|
-
|
|
374
|
+
: usePowershell
|
|
375
|
+
? buildPowershellArgv(command, shellKind, powershellOptions)
|
|
376
|
+
: { file: command, argv: [] };
|
|
377
|
+
let brokerOptions = processOptions;
|
|
378
|
+
if (explicitSandboxShell) {
|
|
379
|
+
const issuer = opts.broker?.issueLinuxWorkspaceSandboxExecutionContract;
|
|
380
|
+
const sandboxExecutionContract = issueTrustedHookSandboxContract({
|
|
381
|
+
issuer,
|
|
382
|
+
receiver: opts.broker,
|
|
383
|
+
file: invocation.file,
|
|
384
|
+
args: invocation.argv,
|
|
385
|
+
options: processOptions,
|
|
386
|
+
trustedRoot,
|
|
387
|
+
sync: true,
|
|
388
|
+
label: "trusted plugin hook sandbox contract issuance failed",
|
|
389
|
+
});
|
|
390
|
+
if (process.platform === "linux" && !sandboxExecutionContract) {
|
|
391
|
+
throw sandboxBoundaryError(
|
|
392
|
+
"trusted Linux hook sandbox contract issuer is unavailable",
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
brokerOptions = {
|
|
396
|
+
...processOptions,
|
|
397
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
res = brokerRunSync(invocation.file, invocation.argv, brokerOptions);
|
|
365
401
|
} else if (usePowershell) {
|
|
366
402
|
// Per-hook `shell: powershell|pwsh` (settings hook entry): explicit argv
|
|
367
403
|
// instead of the default shell; the enterprise ExecutionPolicy override
|
|
@@ -374,6 +410,12 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
374
410
|
throw new Error("hook process runner unavailable");
|
|
375
411
|
}
|
|
376
412
|
res = _deps.runSync(inv.file, inv.argv, processOptions);
|
|
413
|
+
} else if (explicitSandboxShell) {
|
|
414
|
+
if (typeof _deps.runSync !== "function") {
|
|
415
|
+
throw new Error("hook process runner unavailable");
|
|
416
|
+
}
|
|
417
|
+
const invocation = buildExplicitHookShellInvocation(command);
|
|
418
|
+
res = _deps.runSync(invocation.file, invocation.argv, processOptions);
|
|
377
419
|
} else {
|
|
378
420
|
if (typeof _deps.runSync !== "function") {
|
|
379
421
|
throw new Error("hook process runner unavailable");
|
|
@@ -467,7 +509,6 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
467
509
|
: 60000;
|
|
468
510
|
const shellKind = normalizeShellKind(opts.shell);
|
|
469
511
|
const usePowershell = shellKind === "powershell" || shellKind === "pwsh";
|
|
470
|
-
const wd = cwd || process.cwd();
|
|
471
512
|
const inputJson = JSON.stringify({
|
|
472
513
|
...input,
|
|
473
514
|
schema_version: input.schema_version ?? HOOK_PAYLOAD_SCHEMA_VERSION,
|
|
@@ -485,6 +526,14 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
485
526
|
} catch (error) {
|
|
486
527
|
return hookSpawnFailure(error);
|
|
487
528
|
}
|
|
529
|
+
const explicitSandboxShell = requiresExplicitHookShell(sandboxPolicy);
|
|
530
|
+
let trustedRoot;
|
|
531
|
+
try {
|
|
532
|
+
trustedRoot = explicitSandboxShell ? requireTrustedHookRoot(cwd) : null;
|
|
533
|
+
} catch (error) {
|
|
534
|
+
return hookSpawnFailure(error);
|
|
535
|
+
}
|
|
536
|
+
const wd = trustedRoot || cwd || process.cwd();
|
|
488
537
|
const processOptions = {
|
|
489
538
|
cwd: wd,
|
|
490
539
|
env: spawnEnv,
|
|
@@ -494,6 +543,7 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
494
543
|
pluginId: opts.pluginId || null,
|
|
495
544
|
pluginVersion: opts.pluginVersion || null,
|
|
496
545
|
pluginSource: opts.pluginSource || null,
|
|
546
|
+
...(explicitSandboxShell || usePowershell ? { shell: false } : {}),
|
|
497
547
|
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
498
548
|
};
|
|
499
549
|
|
|
@@ -509,7 +559,38 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
509
559
|
if (typeof run !== "function") {
|
|
510
560
|
throw new Error("hook process runner unavailable");
|
|
511
561
|
}
|
|
512
|
-
if (
|
|
562
|
+
if (explicitSandboxShell) {
|
|
563
|
+
const invocation = buildExplicitHookShellInvocation(command, {
|
|
564
|
+
shellKind,
|
|
565
|
+
buildPowershellArgv,
|
|
566
|
+
powershellOptions: {
|
|
567
|
+
executionPolicy: loadShellConfig({ cwd: wd }).executionPolicy,
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
let brokerOptions = processOptions;
|
|
571
|
+
if (brokerRun) {
|
|
572
|
+
const issuer = opts.broker?.issueLinuxWorkspaceSandboxExecutionContract;
|
|
573
|
+
const sandboxExecutionContract = issueTrustedHookSandboxContract({
|
|
574
|
+
issuer,
|
|
575
|
+
receiver: opts.broker,
|
|
576
|
+
file: invocation.file,
|
|
577
|
+
args: invocation.argv,
|
|
578
|
+
options: processOptions,
|
|
579
|
+
trustedRoot,
|
|
580
|
+
label: "trusted plugin hook sandbox contract issuance failed",
|
|
581
|
+
});
|
|
582
|
+
if (process.platform === "linux" && !sandboxExecutionContract) {
|
|
583
|
+
throw sandboxBoundaryError(
|
|
584
|
+
"trusted Linux hook sandbox contract issuer is unavailable",
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
brokerOptions = {
|
|
588
|
+
...processOptions,
|
|
589
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
child = run(invocation.file, invocation.argv, brokerOptions);
|
|
593
|
+
} else if (usePowershell) {
|
|
513
594
|
const { executionPolicy } = loadShellConfig({ cwd: wd });
|
|
514
595
|
const inv = buildPowershellArgv(command, shellKind, { executionPolicy });
|
|
515
596
|
child = run(inv.file, inv.argv, processOptions);
|
package/src/lib/hook-runner.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import runner from "./hook-runner.cjs";
|
|
2
2
|
import executionBroker from "./process-execution-broker/index.js";
|
|
3
|
+
import hookShellCommand from "./hook-shell-command.cjs";
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
issueTrustedHookSandboxContract,
|
|
7
|
+
requireTrustedHookRoot,
|
|
8
|
+
requiresExplicitHookShell,
|
|
9
|
+
sandboxBoundaryError,
|
|
10
|
+
} = hookShellCommand;
|
|
3
11
|
|
|
4
12
|
const brokerRunner = executionBroker.spawn.bind(executionBroker);
|
|
5
13
|
const brokerSyncRunner = executionBroker.spawnSync.bind(executionBroker);
|
|
@@ -17,21 +25,84 @@ function normalizeInvocation(argsOrOptions, maybeOptions) {
|
|
|
17
25
|
|
|
18
26
|
function runHookProcess(file, argsOrOptions, maybeOptions) {
|
|
19
27
|
const { args, options } = normalizeInvocation(argsOrOptions, maybeOptions);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
const callerOptions = { ...options };
|
|
29
|
+
delete callerOptions.sandboxExecutionContract;
|
|
30
|
+
const processOptions = {
|
|
31
|
+
...callerOptions,
|
|
32
|
+
origin: callerOptions.origin || "hook",
|
|
23
33
|
policy: "allow",
|
|
24
34
|
scope: "hook",
|
|
35
|
+
};
|
|
36
|
+
const requiresContract = requiresExplicitHookShell(
|
|
37
|
+
processOptions.sandboxPolicy,
|
|
38
|
+
);
|
|
39
|
+
const trustedRoot = requiresContract
|
|
40
|
+
? requireTrustedHookRoot(processOptions.cwd)
|
|
41
|
+
: null;
|
|
42
|
+
const sandboxExecutionContract = requiresContract
|
|
43
|
+
? issueTrustedHookSandboxContract({
|
|
44
|
+
issuer: executionBroker.issueLinuxWorkspaceSandboxExecutionContract,
|
|
45
|
+
receiver: executionBroker,
|
|
46
|
+
file,
|
|
47
|
+
args,
|
|
48
|
+
options: processOptions,
|
|
49
|
+
trustedRoot,
|
|
50
|
+
})
|
|
51
|
+
: null;
|
|
52
|
+
if (
|
|
53
|
+
process.platform === "linux" &&
|
|
54
|
+
requiresContract &&
|
|
55
|
+
!sandboxExecutionContract
|
|
56
|
+
) {
|
|
57
|
+
throw sandboxBoundaryError(
|
|
58
|
+
"trusted Linux hook sandbox contract could not be issued",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return _processDeps.run(file, args, {
|
|
62
|
+
...processOptions,
|
|
63
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
25
64
|
});
|
|
26
65
|
}
|
|
27
66
|
|
|
28
67
|
function runHookProcessSync(file, argsOrOptions, maybeOptions) {
|
|
29
68
|
const { args, options } = normalizeInvocation(argsOrOptions, maybeOptions);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
69
|
+
const callerOptions = { ...options };
|
|
70
|
+
delete callerOptions.sandboxExecutionContract;
|
|
71
|
+
const processOptions = {
|
|
72
|
+
...callerOptions,
|
|
73
|
+
origin: callerOptions.origin || "hook",
|
|
33
74
|
policy: "allow",
|
|
34
75
|
scope: "hook",
|
|
76
|
+
};
|
|
77
|
+
const requiresContract = requiresExplicitHookShell(
|
|
78
|
+
processOptions.sandboxPolicy,
|
|
79
|
+
);
|
|
80
|
+
const trustedRoot = requiresContract
|
|
81
|
+
? requireTrustedHookRoot(processOptions.cwd)
|
|
82
|
+
: null;
|
|
83
|
+
const sandboxExecutionContract = requiresContract
|
|
84
|
+
? issueTrustedHookSandboxContract({
|
|
85
|
+
issuer: executionBroker.issueLinuxWorkspaceSandboxExecutionContract,
|
|
86
|
+
receiver: executionBroker,
|
|
87
|
+
file,
|
|
88
|
+
args,
|
|
89
|
+
options: processOptions,
|
|
90
|
+
trustedRoot,
|
|
91
|
+
sync: true,
|
|
92
|
+
})
|
|
93
|
+
: null;
|
|
94
|
+
if (
|
|
95
|
+
process.platform === "linux" &&
|
|
96
|
+
requiresContract &&
|
|
97
|
+
!sandboxExecutionContract
|
|
98
|
+
) {
|
|
99
|
+
throw sandboxBoundaryError(
|
|
100
|
+
"trusted Linux hook sandbox contract could not be issued",
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return _processDeps.runSync(file, args, {
|
|
104
|
+
...processOptions,
|
|
105
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
35
106
|
});
|
|
36
107
|
}
|
|
37
108
|
|