chainlesschain 0.162.188 → 0.162.189
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/assets/{AIOps-Brr1LVcE.js → AIOps-DbZft8w0.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DQU74DKG.js → ActionButton-DoOZpwfN.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CYp9178y.js → Analytics-B0J0KLTW.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DW4SCYsW.js → AppLayout-D9eIR0qq.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CYlD_ed1.js → Artifacts-BnZXEVeo.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CWl1rZOi.js → Audit-X1ce4Bjn.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOR-LPsa.js → BackgroundAgents-BvWR-y5v.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DLpG7j1X.js → Backup-BublSGEd.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B35K9wdr.js → BaseInput-CR_77Hso.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CwUJb9gB.js → Chat-DixW52tq.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-1v6aMS1m.js → ChatBubbleRenderer-DDHDpBBA.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-pi3-tPNW.js → Checkbox-CCv1C87K.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-FyNF35eL.js → Codegen-B03Cc3dd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-tGt_JDEr.js → Col-CA06-6pU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CC7LonQX.js → Community-Bj881J52.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BGXrHHjE.js → Compact-UZLTPoaL.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DSW0TBZ8.js → Compliance-kGwzRb_0.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Bhk7rbvE.js → Cowork-KundoYM8.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DxyiQ2NZ.js → Cron-BYZyoqDe.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B2ZkxHok.js → Crosschain-BhrrppBX.js} +1 -1
- package/src/assets/web-panel/assets/{DID-yOJV99eJ.js → DID-CdC6EguZ.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CKsttz7D.js → Dashboard-CcQx3J4y.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CVFldXSL.js → Dropdown-BpijtEzQ.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-Bufv638J.js → EmailListRenderer-DfZjeZTe.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-4ncjoIjL.js → FamilyGuardDashboard-BFYSf69C.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DsknEDoX.js → Federation-Okdi3pJm.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cqe5jWXA.js → FormItemContext-mH-HebKW.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-NAJaFKxc.js → GenericCardRenderer-xivV69x7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CszKQpnl.js → Git-1rh_YBwg.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BXGodXi6.js → Governance-eAXkNxdY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CkoYuG2c.js → Inference-CS6dMVTN.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DFlEr_T0.js → KnowledgeGraph-Bq_TnkvD.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CVaENddA.js → Logs-KchHtiHt.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-TabqHW9B.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-CDlLT0fw.js → Marketplace-CsmGcX5X.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DdwOyszN.js → McpTools-BaPFnnyz.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DKQnkKuG.js → Memory-2m-sUUf7.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DGseqlzJ.js → MobileBridge-D5nVZBRu.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Bo8MLZvT.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-O61Vwyli.js → Mtc-3GDvamGa.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-Dw2SyasS.js → MtcAudit-D2zGuZCH.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-hSu_l8kf.js → Multisig-BBVQ4t6t.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CX1Epurr.js → NLProgramming-BIOCyOGp.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-R9xX8o8_.js → Notes-DVwBGpzc.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-jtSNLCuK.js → NotificationSettings-BDnHfIfX.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BU2Yqq5H.js → OrderTableRenderer-C7Ek20ag.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Pu2WO-U3.js → Organization-D3SRKaa7.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BjnqUyFp.js → Overflow-C2B8ahyn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-jjkvz44c.js → P2P-2iwQzG65.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-69ca_WeR.js → PdhVaultBrowser-CoPiWqMN.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-D4yDpivd.js → Permissions-yV5XNXo5.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-0qsL6iH1.js → PersonalDataHub-uCDJhMUF.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Cx7Ybrk2.js → Pipeline-hN7ucMOA.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-PbfR_M_V.js → Privacy-CgU0ptN4.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CyV6Gsub.js → ProjectInit-CfYPiT6j.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7KZ2Wdk.js → ProjectSettings-B0A2C4nw.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DQDjO-yK.js → Projects-BE5C0ajn.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DyFVBBMX.js → Providers-CstQPf8W.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CzB1BC9_.js → QrScannerModal-Co3z_ynk.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bk636mC1.js → QuickAsk-DMTS6pQ6.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DoWxHIT3.js → Recommend-ETw4odUa.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-B0rkE09d.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-BucoReGG.js → Reputation-BTVqQgKq.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzmOdJU7.js → Row-CBKKwwtO.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-C1tK7KGU.js → RssFeed-BHUQ438I.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CSnInOqk.js → Search-B4NqeUsF.js} +1 -1
- package/src/assets/web-panel/assets/{Security-dlejr15l.js → Security-BRjoYG5_.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BioPZuNh.js → Services-CxQzu3p3.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DWMibwpC.js → Skeleton-BKzwwmWO.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BAAnx1B2.js → Skills-DrsFcgth.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-jWNp4hNO.js → Sla-D21tmXCH.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-y5kqKha-.js → SpeechSettings-CjyjLfp8.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-B9GNsc3e.js → SyncSettings-Cu45-tnx.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BDBYKRen.js → Tasks-oMcILYOj.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DRiE5eaN.js → Templates-Bh-Zcr5R.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BGlAvExF.js → Tenant-68ns63aI.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B8uzrbNY.js → Terminal-BLJkFykE.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Df48iveY.js → TimelineRenderer-xcrJLq-O.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CcmHndN6.js → Tokens-C6PpFJtf.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPkWxmQ2.js → Trigger-BaKA7tdM.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DTb5a5AV.js → Trust-BzhA57Jc.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CBmg5vch.js → UkeySign-4_U3JkIR.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B0mZrz4O.js → VideoEditing-162AANxX.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Bxs7_mpd.js → Wallet-B1viuql_.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-C8x3cMxU.js → WebAuthn-ZVWazhCy.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-DPrNznrP.js → WorkflowEditor-tzdyUk-1.js} +1 -1
- package/src/assets/web-panel/assets/{chat-D3bYWu7S.js → chat-DmrHNuwq.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DY-PwMu4.js → colors-DAJ_M-PK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-LDk10Kw6.js → compact-item-uIzmYwLs.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C-2MZnFq.js → createContext-D_ZmuLwb.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DIrncrV1.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BQIBjcnB.js → hasIn-CDt_shTL.js} +1 -1
- package/src/assets/web-panel/assets/{index-8JC9BJa1.js → index-BAvSmJXW.js} +1 -1
- package/src/assets/web-panel/assets/{index-KFZO_lcx.js → index-BDF6EQBs.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiTvBAr-.js → index-BHvlm0In.js} +1 -1
- package/src/assets/web-panel/assets/{index-COG61sRv.js → index-BRa4kJ_S.js} +1 -1
- package/src/assets/web-panel/assets/{index-lmm9p-jm.js → index-BTCXf1t-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTIZpPtG.js → index-Bbxjfstq.js} +1 -1
- package/src/assets/web-panel/assets/{index-_HRO_cIu.js → index-Bx4d6c1k.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRI3ngNg.js → index-ByzGvpR9.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3GNvExG.js → index-C16fYKFD.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3Oq-DLL.js → index-C5WcdJH2.js} +3 -3
- package/src/assets/web-panel/assets/{index-L3smcoQX.js → index-CG0lY4h3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dh-tkpKQ.js → index-CKyd472S.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQVb2dWp.js → index-CaelyMgF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DY2FIrmz.js → index-CbD_WW_6.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYyLJPlF.js → index-CcZ9xJI9.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7FC8cP2.js → index-CeUpKjvi.js} +1 -1
- package/src/assets/web-panel/assets/{index-BN-xdSWZ.js → index-Cvak6Oe2.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSvo_bp0.js → index-CzXqvMuP.js} +1 -1
- package/src/assets/web-panel/assets/index-D-Swwqld.js +1 -0
- package/src/assets/web-panel/assets/{index-BdXnYMCZ.js → index-D4n1lGTB.js} +1 -1
- package/src/assets/web-panel/assets/{index-L6qixyLc.js → index-D9OE8B3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-VCh5zNJo.js → index-DBFmcaCQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlRYqt3S.js → index-DdUMcwRB.js} +1 -1
- package/src/assets/web-panel/assets/{index-k7C_oDCO.js → index-DeGBoLQA.js} +1 -1
- package/src/assets/web-panel/assets/{index-7xKu7Fns.js → index-DfSSy9uy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CY4KnaO9.js → index-DqDSF2RW.js} +1 -1
- package/src/assets/web-panel/assets/{index-HasPt4-h.js → index-DvFhhe6r.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaslYch6.js → index-DwfvPe-j.js} +1 -1
- package/src/assets/web-panel/assets/index-FMZAMKy8.js +1 -0
- package/src/assets/web-panel/assets/{index-B5pgtZnY.js → index-FyplHuNt.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cz4b6Z_7.js → index-GcKOHD_c.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI4KgcYC.js → index-OzTxFIJP.js} +1 -1
- package/src/assets/web-panel/assets/{index-BX2b9Orc.js → index-QsgfSZrV.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbR6ROOa.js → index-Z8P4BexE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrnRO9yE.js → index-clSX4PEc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dd2Km97U.js → index-ifxmu8SY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhRt9cYJ.js → index-rS3gl_68.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4K1weKc.js → index-rXdwUeE3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cx6D_dx2.js → index-saf3VNeP.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-zQPSHf2C.js → initDefaultProps-pmfaD-aK.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CKCStGVk.js → motion-CaQ2CpYF.js} +1 -1
- package/src/assets/web-panel/assets/{move-BufVDGYF.js → move-CkKTqNbI.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DcaFxy5D.js → mtc-parser-Dd4xsgRI.js} +1 -1
- package/src/assets/web-panel/assets/{omit-EScpkGdo.js → omit-BFuA5YEs.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-72hG_qfT.js → pickAttrs-DDfxOAJc.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CMFWiH20.js → placementArrow-Be595JCp.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DKQHS41p.js → responsiveObserve-7byljUM3.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QVAFHTIQ.js → slide-CTWfOTaY.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DIz38e2G.js → statusUtils-CNZOuIxM.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-zK5cyLby.js → styleChecker-DwOcBV78.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DhZngk62.js → useFlexGapSupport-B6MNwAO9.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BvV2cvkg.js → useFs-CzyQjH9r.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CRmxb9l3.js → usePersonalDataHub-Dxh3KiVA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CgQIbobP.js → vnode-DVfZiS5N.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-D4XrrO0R.js → zoom-BYme7fbY.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +22 -0
- package/src/commands/checkpoint-managed.js +1059 -0
- package/src/commands/checkpoint.js +3 -0
- package/src/commands/team-distributed.js +3324 -0
- package/src/commands/team.js +1130 -74
- package/src/data/changelog.json +13 -0
- package/src/lib/agent-core.js +1 -0
- package/src/lib/agent-team/task-lease.js +271 -3
- package/src/lib/agent-team/team-adjudication.js +1501 -0
- package/src/lib/agent-team/team-budget.js +4 -1
- package/src/lib/agent-team/team-control-store.js +582 -0
- package/src/lib/agent-team/team-distributed-queue.js +5246 -0
- package/src/lib/agent-team/team-process-checkpoint.js +211 -0
- package/src/lib/agent-team/team-runner.js +319 -22
- package/src/lib/agent-team/team-worktree.js +1403 -95
- package/src/lib/managed-checkpoint-render.js +60 -0
- package/src/lib/managed-tool-checkpoint.js +194 -0
- package/src/lib/process-execution-broker/index.js +438 -5
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +28 -1
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +28 -4
- package/src/lib/process-execution-broker/platform-sandbox.js +92 -70
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +284 -19
- package/src/lib/process-execution-broker/workspace-transaction.js +3556 -0
- package/src/lib/secure-file-identity.js +433 -0
- package/src/lib/turn-binding.js +160 -2
- package/src/lib/with-file-lock.js +140 -23
- package/src/repl/agent-repl.js +19 -1
- package/src/runtime/agent-core.js +366 -122
- package/src/runtime/headless-runner.js +12 -0
- package/src/runtime/headless-stream.js +3 -0
- package/src/runtime/policies/agent-policy.js +14 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-7iWGwZ-r.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-COBbEOlj.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-oOVIv9wq.js +0 -5
- package/src/assets/web-panel/assets/devWarning-B4dXIJqD.js +0 -1
- package/src/assets/web-panel/assets/index-C-C7DaG6.js +0 -1
- package/src/assets/web-panel/assets/index-DkfWzWtd.js +0 -1
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
import { consumeIssuedPluginSandboxExecutionContract } from "../plugin-runtime/bin.js";
|
|
48
48
|
import runtimeProvenanceLedger from "../runtime-provenance-ledger.js";
|
|
49
49
|
import { credentialAgent } from "./credential-agent.js";
|
|
50
|
+
import { WorkspaceTransactionManager } from "./workspace-transaction.js";
|
|
50
51
|
|
|
51
52
|
const SUPPORTED_SANDBOX_BOUNDARIES = new Set(Object.values(SANDBOX_BOUNDARIES));
|
|
52
53
|
const SUPPORTED_SANDBOX_PROFILES = new Set([
|
|
@@ -537,6 +538,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
537
538
|
};
|
|
538
539
|
this._ptyAdapter = createNativePtyAdapter();
|
|
539
540
|
this._hooksEventSink = null;
|
|
541
|
+
this._workspaceTransactionManagers = new Map();
|
|
540
542
|
|
|
541
543
|
this._ensureLogDir();
|
|
542
544
|
this._loadPermissions();
|
|
@@ -868,7 +870,8 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
868
870
|
requiredBoundaries.some(
|
|
869
871
|
(boundary) =>
|
|
870
872
|
boundary !== SANDBOX_BOUNDARIES.FILESYSTEM &&
|
|
871
|
-
boundary !== SANDBOX_BOUNDARIES.NETWORK
|
|
873
|
+
boundary !== SANDBOX_BOUNDARIES.NETWORK &&
|
|
874
|
+
boundary !== SANDBOX_BOUNDARIES.PROCESS_TREE,
|
|
872
875
|
)
|
|
873
876
|
) {
|
|
874
877
|
return null;
|
|
@@ -986,6 +989,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
986
989
|
delete options.sandboxExecutionContract;
|
|
987
990
|
}
|
|
988
991
|
|
|
992
|
+
_stripWorkspaceTransactionOptions(options) {
|
|
993
|
+
delete options.workspaceTransactionId;
|
|
994
|
+
delete options.workspaceTransactionStateDir;
|
|
995
|
+
delete options.workspaceTransactionCapture;
|
|
996
|
+
}
|
|
997
|
+
|
|
989
998
|
_sandboxUnavailablePlan(command, args, options, reason, sandboxPolicy = {}) {
|
|
990
999
|
return {
|
|
991
1000
|
contractVersion: 1,
|
|
@@ -1124,6 +1133,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1124
1133
|
let genericWorkspaceProbe = false;
|
|
1125
1134
|
let sanitizedFilesystemPolicy = null;
|
|
1126
1135
|
let sanitizedNetworkPolicy = null;
|
|
1136
|
+
let sanitizedProcessTreePolicy = null;
|
|
1127
1137
|
let sanitizedPtyPolicy = null;
|
|
1128
1138
|
if (plan.runtimeProbe !== null && plan.runtimeProbe !== undefined) {
|
|
1129
1139
|
if (
|
|
@@ -1515,6 +1525,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1515
1525
|
"outsideMarkerHidden",
|
|
1516
1526
|
"networkNamespace",
|
|
1517
1527
|
"networkNamespaceChanged",
|
|
1528
|
+
"pidNamespace",
|
|
1529
|
+
"pidNamespaceChanged",
|
|
1530
|
+
"processTreeCloseProbe",
|
|
1531
|
+
"bubblewrapPid1Reaper",
|
|
1532
|
+
"dieWithParent",
|
|
1533
|
+
"closeImpliesProcessTreeClosed",
|
|
1518
1534
|
"socketCreationDenied",
|
|
1519
1535
|
"descriptorMounts",
|
|
1520
1536
|
"mountTopologyAtomic",
|
|
@@ -1564,6 +1580,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1564
1580
|
if (genericWorkspaceKind) {
|
|
1565
1581
|
const filesystemPolicy = plan.filesystemPolicy;
|
|
1566
1582
|
const networkPolicy = plan.networkPolicy;
|
|
1583
|
+
const processTreePolicy = plan.processTreePolicy;
|
|
1567
1584
|
const ptyPolicy = plan.ptyPolicy ?? null;
|
|
1568
1585
|
const ptyPlan =
|
|
1569
1586
|
ptyPolicy !== null &&
|
|
@@ -1607,6 +1624,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1607
1624
|
/^[a-f0-9]{64}$/.test(plan.runtimeProbe.contractDigest || "") &&
|
|
1608
1625
|
guarantees.includes(SANDBOX_BOUNDARIES.FILESYSTEM) &&
|
|
1609
1626
|
guarantees.includes(SANDBOX_BOUNDARIES.NETWORK) &&
|
|
1627
|
+
guarantees.includes(SANDBOX_BOUNDARIES.PROCESS_TREE) &&
|
|
1610
1628
|
plan.runtimeProbe.attempted === true &&
|
|
1611
1629
|
plan.runtimeProbe.runnable === true &&
|
|
1612
1630
|
plan.runtimeProbe.reason === null &&
|
|
@@ -1633,6 +1651,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1633
1651
|
"outsideMarkerHidden",
|
|
1634
1652
|
"networkNamespace",
|
|
1635
1653
|
"networkNamespaceChanged",
|
|
1654
|
+
"pidNamespace",
|
|
1655
|
+
"pidNamespaceChanged",
|
|
1656
|
+
"processTreeCloseProbe",
|
|
1657
|
+
"bubblewrapPid1Reaper",
|
|
1658
|
+
"dieWithParent",
|
|
1659
|
+
"closeImpliesProcessTreeClosed",
|
|
1636
1660
|
"socketCreationDenied",
|
|
1637
1661
|
"descriptorMounts",
|
|
1638
1662
|
].every((field) => plan.runtimeProbe[field] === true) &&
|
|
@@ -1670,14 +1694,24 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1670
1694
|
networkPolicy?.namespace === "new" &&
|
|
1671
1695
|
networkPolicy?.namespaceIdentityChanged === true &&
|
|
1672
1696
|
networkPolicy?.seccomp === "deny-network-creation" &&
|
|
1697
|
+
processTreePolicy?.namespace === "new" &&
|
|
1698
|
+
processTreePolicy?.namespaceIdentityChanged === true &&
|
|
1699
|
+
processTreePolicy?.init === "bubblewrap-pid1-reaper" &&
|
|
1700
|
+
processTreePolicy?.parentDeathSignal === "SIGKILL" &&
|
|
1701
|
+
processTreePolicy?.asPid1 === false &&
|
|
1702
|
+
processTreePolicy?.closeFence === "pid-namespace-empty-or-killed" &&
|
|
1673
1703
|
plan.command.startsWith("/proc/self/fd/") &&
|
|
1674
1704
|
plan.options?.cwd === "/" &&
|
|
1675
1705
|
plan.options?.shell === false &&
|
|
1706
|
+
plan.options?.detached === false &&
|
|
1676
1707
|
Array.isArray(plan.options?.stdio) &&
|
|
1677
1708
|
plan.args.includes("--bind-fd") &&
|
|
1678
1709
|
plan.args.includes("--ro-bind-fd") &&
|
|
1679
1710
|
plan.args.includes("--remount-ro") &&
|
|
1680
1711
|
plan.args.includes("--unshare-net") &&
|
|
1712
|
+
plan.args.includes("--unshare-pid") &&
|
|
1713
|
+
plan.args.includes("--die-with-parent") &&
|
|
1714
|
+
!plan.args.includes("--as-pid-1") &&
|
|
1681
1715
|
plan.args.includes("--seccomp") &&
|
|
1682
1716
|
!plan.args.includes("--ro-bind") &&
|
|
1683
1717
|
ptyPlanValid;
|
|
@@ -1715,6 +1749,14 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1715
1749
|
namespaceIdentityChanged: networkPolicy.namespaceIdentityChanged,
|
|
1716
1750
|
seccomp: networkPolicy.seccomp,
|
|
1717
1751
|
});
|
|
1752
|
+
sanitizedProcessTreePolicy = Object.freeze({
|
|
1753
|
+
namespace: processTreePolicy.namespace,
|
|
1754
|
+
namespaceIdentityChanged: processTreePolicy.namespaceIdentityChanged,
|
|
1755
|
+
init: processTreePolicy.init,
|
|
1756
|
+
parentDeathSignal: processTreePolicy.parentDeathSignal,
|
|
1757
|
+
asPid1: processTreePolicy.asPid1,
|
|
1758
|
+
closeFence: processTreePolicy.closeFence,
|
|
1759
|
+
});
|
|
1718
1760
|
sanitizedPtyPolicy = ptyPlan
|
|
1719
1761
|
? Object.freeze({
|
|
1720
1762
|
mode: ptyPolicy.mode,
|
|
@@ -1835,6 +1877,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1835
1877
|
? {
|
|
1836
1878
|
filesystemPolicy: sanitizedFilesystemPolicy,
|
|
1837
1879
|
networkPolicy: sanitizedNetworkPolicy,
|
|
1880
|
+
processTreePolicy: sanitizedProcessTreePolicy,
|
|
1838
1881
|
ptyPolicy: sanitizedPtyPolicy,
|
|
1839
1882
|
}
|
|
1840
1883
|
: {}),
|
|
@@ -2038,6 +2081,17 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2038
2081
|
seccomp: plan.networkPolicy.seccomp,
|
|
2039
2082
|
}
|
|
2040
2083
|
: null;
|
|
2084
|
+
auditEntry.sandboxProcessTreePolicy = genericPolicy
|
|
2085
|
+
? {
|
|
2086
|
+
namespace: plan.processTreePolicy.namespace,
|
|
2087
|
+
namespaceIdentityChanged:
|
|
2088
|
+
plan.processTreePolicy.namespaceIdentityChanged,
|
|
2089
|
+
init: plan.processTreePolicy.init,
|
|
2090
|
+
parentDeathSignal: plan.processTreePolicy.parentDeathSignal,
|
|
2091
|
+
asPid1: plan.processTreePolicy.asPid1,
|
|
2092
|
+
closeFence: plan.processTreePolicy.closeFence,
|
|
2093
|
+
}
|
|
2094
|
+
: null;
|
|
2041
2095
|
auditEntry.sandboxPtyPolicy =
|
|
2042
2096
|
genericPolicy && plan.ptyPolicy
|
|
2043
2097
|
? {
|
|
@@ -2115,6 +2169,34 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2115
2169
|
return run;
|
|
2116
2170
|
}
|
|
2117
2171
|
|
|
2172
|
+
_createWorkspaceProcessCloseFence(auditEntry, proc) {
|
|
2173
|
+
if (
|
|
2174
|
+
!Array.isArray(auditEntry.workspaceTransactionIds) ||
|
|
2175
|
+
auditEntry.workspaceTransactionIds.length === 0
|
|
2176
|
+
) {
|
|
2177
|
+
return null;
|
|
2178
|
+
}
|
|
2179
|
+
return new Promise((resolve) => {
|
|
2180
|
+
if (typeof proc?.once === "function") {
|
|
2181
|
+
proc.once("close", (exitCode, signal) => {
|
|
2182
|
+
resolve({ observed: true, exitCode, signal });
|
|
2183
|
+
});
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
if (typeof proc?.onExit === "function") {
|
|
2187
|
+
proc.onExit((event = {}) => {
|
|
2188
|
+
resolve({
|
|
2189
|
+
observed: true,
|
|
2190
|
+
exitCode: event.exitCode ?? null,
|
|
2191
|
+
signal: event.signal ?? null,
|
|
2192
|
+
});
|
|
2193
|
+
});
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
resolve({ observed: false, exitCode: null, signal: null });
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2118
2200
|
_runPostSpawnSandbox(proc, plan, auditEntry) {
|
|
2119
2201
|
if (!plan.applied || !plan.postSpawn.required) {
|
|
2120
2202
|
this._applySandboxAudit(auditEntry, plan, plan.applied);
|
|
@@ -2142,10 +2224,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2142
2224
|
} catch {
|
|
2143
2225
|
// The sandbox failure remains fatal even if the child already exited.
|
|
2144
2226
|
}
|
|
2145
|
-
|
|
2227
|
+
const failure = this._sandboxError(
|
|
2146
2228
|
"post_spawn_failed",
|
|
2147
2229
|
`Post-spawn sandbox setup failed: ${error.message}`,
|
|
2148
2230
|
);
|
|
2231
|
+
failure.processTerminationRequested = true;
|
|
2232
|
+
throw failure;
|
|
2149
2233
|
}
|
|
2150
2234
|
process.emitWarning(
|
|
2151
2235
|
`Post-spawn sandbox setup failed (continuing without): ${error.message}`,
|
|
@@ -2163,10 +2247,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2163
2247
|
} catch {
|
|
2164
2248
|
// The sandbox failure remains fatal even if the child already exited.
|
|
2165
2249
|
}
|
|
2166
|
-
|
|
2250
|
+
const failure = this._sandboxError(
|
|
2167
2251
|
"async_post_spawn_contract_violation",
|
|
2168
2252
|
"Strict sandbox adapter returned an asynchronous post-spawn result",
|
|
2169
2253
|
);
|
|
2254
|
+
failure.processTerminationRequested = true;
|
|
2255
|
+
throw failure;
|
|
2170
2256
|
}
|
|
2171
2257
|
auditEntry.sandboxed = false;
|
|
2172
2258
|
auditEntry.sandboxState = "pending";
|
|
@@ -2349,6 +2435,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2349
2435
|
policyDigest: auditEntry.sandboxPolicyDigest || null,
|
|
2350
2436
|
filesystemPolicy: auditEntry.sandboxFilesystemPolicy || null,
|
|
2351
2437
|
networkPolicy: auditEntry.sandboxNetworkPolicy || null,
|
|
2438
|
+
processTreePolicy: auditEntry.sandboxProcessTreePolicy || null,
|
|
2352
2439
|
},
|
|
2353
2440
|
traceId:
|
|
2354
2441
|
traceCtx?.traceId || auditEntry.traceId || `trace-${Date.now()}`,
|
|
@@ -2380,11 +2467,235 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2380
2467
|
this._hooksEventSink = hooks;
|
|
2381
2468
|
}
|
|
2382
2469
|
|
|
2470
|
+
_workspaceTransactionManager(stateDir, _lockDir) {
|
|
2471
|
+
const stateKey =
|
|
2472
|
+
typeof stateDir === "string" && stateDir.trim()
|
|
2473
|
+
? path.resolve(stateDir)
|
|
2474
|
+
: "<default>";
|
|
2475
|
+
// Lock authority is intentionally not caller-selectable. Keep accepting
|
|
2476
|
+
// legacy lockDir-bearing option objects at the Broker boundary, but route
|
|
2477
|
+
// every one of them to the same canonical manager.
|
|
2478
|
+
const key = `${stateKey}\0<default>`;
|
|
2479
|
+
let manager = this._workspaceTransactionManagers.get(key);
|
|
2480
|
+
if (!manager) {
|
|
2481
|
+
manager = new WorkspaceTransactionManager({
|
|
2482
|
+
...(stateKey === "<default>" ? {} : { stateDir: stateKey }),
|
|
2483
|
+
});
|
|
2484
|
+
this._workspaceTransactionManagers.set(key, manager);
|
|
2485
|
+
}
|
|
2486
|
+
return manager;
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
_workspaceTransactionMembershipForCwd(cwd) {
|
|
2490
|
+
const memberships = [];
|
|
2491
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2492
|
+
const membership = manager.activeWorkspaceMembershipForCwd(cwd);
|
|
2493
|
+
if (!membership) continue;
|
|
2494
|
+
const key =
|
|
2495
|
+
process.platform === "win32"
|
|
2496
|
+
? path.resolve(membership.workspaceRoot).toLowerCase()
|
|
2497
|
+
: path.resolve(membership.workspaceRoot);
|
|
2498
|
+
if (!memberships.some((entry) => entry.key === key)) {
|
|
2499
|
+
memberships.push({ key, ...membership });
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
if (memberships.length > 1) {
|
|
2503
|
+
const error = new Error(
|
|
2504
|
+
"multiple workspace transactions claim the same process cwd",
|
|
2505
|
+
);
|
|
2506
|
+
error.code = "WORKSPACE_TRANSACTION_OVERLAPPING_WORKSPACE";
|
|
2507
|
+
error.workspaceRoots = memberships.map((entry) => entry.workspaceRoot);
|
|
2508
|
+
throw error;
|
|
2509
|
+
}
|
|
2510
|
+
return memberships[0] || null;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
_workspaceTransactionRequiredBoundaries(cwd, membership = undefined) {
|
|
2514
|
+
const resolved =
|
|
2515
|
+
membership === undefined
|
|
2516
|
+
? this._workspaceTransactionMembershipForCwd(cwd)
|
|
2517
|
+
: membership;
|
|
2518
|
+
return resolved ? [SANDBOX_BOUNDARIES.PROCESS_TREE] : [];
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
_workspaceTransactionRootForCwd(cwd, membership = undefined) {
|
|
2522
|
+
const resolved =
|
|
2523
|
+
membership === undefined
|
|
2524
|
+
? this._workspaceTransactionMembershipForCwd(cwd)
|
|
2525
|
+
: membership;
|
|
2526
|
+
return resolved?.workspaceRoot || null;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
_withWorkspaceTransactionBoundaries(
|
|
2530
|
+
options,
|
|
2531
|
+
cwd,
|
|
2532
|
+
{ command, args = [], sync = false, pty = false } = {},
|
|
2533
|
+
) {
|
|
2534
|
+
const membership = this._workspaceTransactionMembershipForCwd(cwd);
|
|
2535
|
+
const required = this._workspaceTransactionRequiredBoundaries(
|
|
2536
|
+
cwd,
|
|
2537
|
+
membership,
|
|
2538
|
+
);
|
|
2539
|
+
if (required.length === 0) return options;
|
|
2540
|
+
if (
|
|
2541
|
+
options.requiredBoundaries !== undefined &&
|
|
2542
|
+
!Array.isArray(options.requiredBoundaries)
|
|
2543
|
+
) {
|
|
2544
|
+
// Preserve the invalid value so normal policy validation rejects it.
|
|
2545
|
+
return options;
|
|
2546
|
+
}
|
|
2547
|
+
const nextOptions = {
|
|
2548
|
+
...options,
|
|
2549
|
+
...(membership?.cwd ? { cwd: membership.cwd } : {}),
|
|
2550
|
+
requiredBoundaries: [
|
|
2551
|
+
...new Set([...(options.requiredBoundaries || []), ...required]),
|
|
2552
|
+
],
|
|
2553
|
+
};
|
|
2554
|
+
const workspaceRoot = this._workspaceTransactionRootForCwd(cwd, membership);
|
|
2555
|
+
// Transaction preflight always rejects detached writers. Do not let a
|
|
2556
|
+
// platform contract fail first and hide that stable, typed denial.
|
|
2557
|
+
if (
|
|
2558
|
+
workspaceRoot &&
|
|
2559
|
+
nextOptions.detached !== true &&
|
|
2560
|
+
nextOptions.sandboxExecutionContract === undefined
|
|
2561
|
+
) {
|
|
2562
|
+
const contract = this.issueLinuxWorkspaceSandboxExecutionContract(
|
|
2563
|
+
command,
|
|
2564
|
+
args,
|
|
2565
|
+
nextOptions,
|
|
2566
|
+
workspaceRoot,
|
|
2567
|
+
{ sync, pty },
|
|
2568
|
+
);
|
|
2569
|
+
if (contract) nextOptions.sandboxExecutionContract = contract;
|
|
2570
|
+
}
|
|
2571
|
+
return nextOptions;
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
_requiresExplicitLinuxWorkspaceShell(cwd) {
|
|
2575
|
+
return (
|
|
2576
|
+
process.platform === "linux" &&
|
|
2577
|
+
this._workspaceTransactionRequiredBoundaries(cwd).length > 0
|
|
2578
|
+
);
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
_explicitLinuxShellInvocation(command, options = {}) {
|
|
2582
|
+
return {
|
|
2583
|
+
command:
|
|
2584
|
+
typeof options.shell === "string" && options.shell.trim()
|
|
2585
|
+
? options.shell
|
|
2586
|
+
: "/bin/sh",
|
|
2587
|
+
args: ["-c", String(command)],
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
/**
|
|
2592
|
+
* Start a fail-closed, content-addressed workspace checkpoint transaction.
|
|
2593
|
+
*
|
|
2594
|
+
* Active transactions automatically bind every Broker spawn whose canonical
|
|
2595
|
+
* cwd is inside their workspace. The transaction itself owns accept/rollback
|
|
2596
|
+
* and durable evidence APIs.
|
|
2597
|
+
*/
|
|
2598
|
+
beginWorkspaceTransaction(options = {}) {
|
|
2599
|
+
return this._workspaceTransactionManager(
|
|
2600
|
+
options.stateDir,
|
|
2601
|
+
options.lockDir,
|
|
2602
|
+
).begin(options);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
recoverWorkspaceTransactions(options = {}) {
|
|
2606
|
+
return this._workspaceTransactionManager(
|
|
2607
|
+
options.stateDir,
|
|
2608
|
+
options.lockDir,
|
|
2609
|
+
).recoverPending(options);
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
inspectWorkspaceTransaction(id, options = {}) {
|
|
2613
|
+
return this._workspaceTransactionManager(
|
|
2614
|
+
options.stateDir,
|
|
2615
|
+
options.lockDir,
|
|
2616
|
+
).inspect(id);
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
listWorkspaceTransactions(options = {}) {
|
|
2620
|
+
return this._workspaceTransactionManager(
|
|
2621
|
+
options.stateDir,
|
|
2622
|
+
options.lockDir,
|
|
2623
|
+
).list(options);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
restoreWorkspaceTransaction(id, options = {}) {
|
|
2627
|
+
return this._workspaceTransactionManager(
|
|
2628
|
+
options.stateDir,
|
|
2629
|
+
options.lockDir,
|
|
2630
|
+
).restore(id, options);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
undoWorkspaceTransactionRestore(id, options = {}) {
|
|
2634
|
+
return this._workspaceTransactionManager(
|
|
2635
|
+
options.stateDir,
|
|
2636
|
+
options.lockDir,
|
|
2637
|
+
).undoRestore(id, options);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
getWorkspaceTransaction(id) {
|
|
2641
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2642
|
+
const transaction = manager.get(id);
|
|
2643
|
+
if (transaction) return transaction;
|
|
2644
|
+
}
|
|
2645
|
+
return null;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
_prepareWorkspaceTransactionAudit(auditEntry) {
|
|
2649
|
+
const transactionIds = [];
|
|
2650
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2651
|
+
transactionIds.push(...manager.prepareSpawn(auditEntry));
|
|
2652
|
+
}
|
|
2653
|
+
auditEntry.workspaceTransactionIds = [...new Set(transactionIds)].sort();
|
|
2654
|
+
return auditEntry.workspaceTransactionIds;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
_preflightWorkspaceTransactionAudit(auditEntry) {
|
|
2658
|
+
const transactionIds = [];
|
|
2659
|
+
try {
|
|
2660
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2661
|
+
transactionIds.push(...manager.preflightSpawn(auditEntry));
|
|
2662
|
+
}
|
|
2663
|
+
} catch (error) {
|
|
2664
|
+
error.auditEntry = auditEntry;
|
|
2665
|
+
throw error;
|
|
2666
|
+
}
|
|
2667
|
+
return [...new Set(transactionIds)].sort();
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
_bindWorkspaceTransactionProcess(auditEntry, proc) {
|
|
2671
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2672
|
+
manager.bindProcess(auditEntry, proc);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
_updateWorkspaceTransactionProcessGuarantees(auditEntry) {
|
|
2677
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2678
|
+
manager.updateProcessGuarantees(auditEntry);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
_settleWorkspaceTransactionSpawn(auditEntry, outcome = {}) {
|
|
2683
|
+
for (const manager of this._workspaceTransactionManagers.values()) {
|
|
2684
|
+
manager.settleSpawn(auditEntry, outcome);
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2383
2688
|
spawn(command, args, options = {}) {
|
|
2384
2689
|
const executionId = crypto.randomUUID();
|
|
2385
2690
|
const startTime = Date.now();
|
|
2386
2691
|
const origin = options.origin || "unknown";
|
|
2387
|
-
const
|
|
2692
|
+
const requestedCwd = options.cwd || process.cwd();
|
|
2693
|
+
options = this._withWorkspaceTransactionBoundaries(options, requestedCwd, {
|
|
2694
|
+
command,
|
|
2695
|
+
args,
|
|
2696
|
+
sync: false,
|
|
2697
|
+
});
|
|
2698
|
+
const cwd = options.cwd || requestedCwd;
|
|
2388
2699
|
const scope = options.scope || "default";
|
|
2389
2700
|
const policy = options.policy || this._checkPermission(origin, command);
|
|
2390
2701
|
const isDangerous = this._isDangerousCommand(
|
|
@@ -2408,6 +2719,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2408
2719
|
policy,
|
|
2409
2720
|
isDangerous,
|
|
2410
2721
|
shell: !!options.shell,
|
|
2722
|
+
detached: options.detached === true,
|
|
2411
2723
|
pid: null,
|
|
2412
2724
|
exitCode: null,
|
|
2413
2725
|
endTime: null,
|
|
@@ -2463,9 +2775,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2463
2775
|
}
|
|
2464
2776
|
|
|
2465
2777
|
// 传播traceparent环境变量
|
|
2778
|
+
this._preflightWorkspaceTransactionAudit(auditEntry);
|
|
2779
|
+
|
|
2466
2780
|
const spawnOpts = this._sanitizeOptions(options);
|
|
2467
2781
|
this._stripSandboxControlOptions(spawnOpts);
|
|
2468
2782
|
this._stripPluginControlOptions(spawnOpts);
|
|
2783
|
+
this._stripWorkspaceTransactionOptions(spawnOpts);
|
|
2469
2784
|
if (traceCtx) {
|
|
2470
2785
|
spawnOpts.env = { ...(spawnOpts.env || process.env) };
|
|
2471
2786
|
spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
|
|
@@ -2527,17 +2842,45 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2527
2842
|
sandboxPlan,
|
|
2528
2843
|
sandboxPlan.applied && !sandboxPlan.postSpawn.required,
|
|
2529
2844
|
);
|
|
2845
|
+
try {
|
|
2846
|
+
this._prepareWorkspaceTransactionAudit(auditEntry);
|
|
2847
|
+
} catch (transactionError) {
|
|
2848
|
+
sandboxPlan.cleanup?.();
|
|
2849
|
+
transactionError.auditEntry = auditEntry;
|
|
2850
|
+
throw transactionError;
|
|
2851
|
+
}
|
|
2530
2852
|
|
|
2531
2853
|
// Use native spawn from _native (set by patch-child-process.js)
|
|
2532
2854
|
const nativeSpawnFn = this._native?.spawn || nativeSpawn;
|
|
2533
2855
|
let proc;
|
|
2534
2856
|
try {
|
|
2535
2857
|
proc = nativeSpawnFn(command, args, optsForSpawn);
|
|
2858
|
+
this._bindWorkspaceTransactionProcess(auditEntry, proc);
|
|
2536
2859
|
} catch (spawnError) {
|
|
2860
|
+
if (proc && typeof proc.kill === "function") {
|
|
2861
|
+
try {
|
|
2862
|
+
proc.kill("SIGKILL");
|
|
2863
|
+
} catch {
|
|
2864
|
+
// The transaction remains unsettled if shutdown cannot be proven.
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
if (!proc) {
|
|
2868
|
+
try {
|
|
2869
|
+
this._settleWorkspaceTransactionSpawn(auditEntry, {
|
|
2870
|
+
error: spawnError.message,
|
|
2871
|
+
});
|
|
2872
|
+
} catch {
|
|
2873
|
+
// An unsettled durable execution keeps the transaction fail-closed.
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2537
2876
|
sandboxPlan.cleanup?.();
|
|
2538
2877
|
throw spawnError;
|
|
2539
2878
|
}
|
|
2540
2879
|
auditEntry.pid = proc.pid;
|
|
2880
|
+
const workspaceProcessClosed = this._createWorkspaceProcessCloseFence(
|
|
2881
|
+
auditEntry,
|
|
2882
|
+
proc,
|
|
2883
|
+
);
|
|
2541
2884
|
const cleanupSandbox = this._scheduleSandboxCleanup(
|
|
2542
2885
|
proc,
|
|
2543
2886
|
sandboxPlan.cleanup,
|
|
@@ -2558,6 +2901,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2558
2901
|
}
|
|
2559
2902
|
try {
|
|
2560
2903
|
this._runPostSpawnSandbox(proc, sandboxPlan, auditEntry);
|
|
2904
|
+
this._updateWorkspaceTransactionProcessGuarantees(auditEntry);
|
|
2561
2905
|
auditEntry.pid = proc.pid;
|
|
2562
2906
|
if (Number.isSafeInteger(proc.sandboxWrapperPid)) {
|
|
2563
2907
|
auditEntry.sandboxWrapperPid = proc.sandboxWrapperPid;
|
|
@@ -2566,9 +2910,19 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2566
2910
|
auditEntry.sandboxTargetPid = proc.sandboxTargetPid;
|
|
2567
2911
|
}
|
|
2568
2912
|
} catch (postSpawnError) {
|
|
2913
|
+
if (postSpawnError.processTerminationRequested !== true) {
|
|
2914
|
+
try {
|
|
2915
|
+
proc.kill?.("SIGKILL");
|
|
2916
|
+
} catch {
|
|
2917
|
+
// The close fence remains pending and recovery stays fail-closed.
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2569
2920
|
cleanupSandbox();
|
|
2570
2921
|
this._recordSandboxDenial(auditEntry, postSpawnError, startTime);
|
|
2571
2922
|
postSpawnError.auditEntry = auditEntry;
|
|
2923
|
+
postSpawnError.spawnedProcess = proc;
|
|
2924
|
+
postSpawnError.workspaceProcessClosed = workspaceProcessClosed;
|
|
2925
|
+
postSpawnError.workspaceTerminationRequested = true;
|
|
2572
2926
|
throw postSpawnError;
|
|
2573
2927
|
}
|
|
2574
2928
|
|
|
@@ -2624,7 +2978,13 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2624
2978
|
const executionId = crypto.randomUUID();
|
|
2625
2979
|
const startTime = Date.now();
|
|
2626
2980
|
const origin = options.origin || "unknown";
|
|
2627
|
-
const
|
|
2981
|
+
const requestedCwd = options.cwd || process.cwd();
|
|
2982
|
+
options = this._withWorkspaceTransactionBoundaries(options, requestedCwd, {
|
|
2983
|
+
command,
|
|
2984
|
+
args,
|
|
2985
|
+
sync: true,
|
|
2986
|
+
});
|
|
2987
|
+
const cwd = options.cwd || requestedCwd;
|
|
2628
2988
|
const scope = options.scope || "default";
|
|
2629
2989
|
const policy = options.policy || this._checkPermission(origin, command);
|
|
2630
2990
|
const isDangerous = this._isDangerousCommand(
|
|
@@ -2648,6 +3008,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2648
3008
|
policy,
|
|
2649
3009
|
isDangerous,
|
|
2650
3010
|
shell: !!options.shell,
|
|
3011
|
+
detached: options.detached === true,
|
|
2651
3012
|
sync: true,
|
|
2652
3013
|
pluginId: options.pluginId || null,
|
|
2653
3014
|
pluginVersion: options.pluginVersion || null,
|
|
@@ -2693,9 +3054,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2693
3054
|
throw err;
|
|
2694
3055
|
}
|
|
2695
3056
|
|
|
3057
|
+
this._preflightWorkspaceTransactionAudit(auditEntry);
|
|
3058
|
+
|
|
2696
3059
|
const spawnOpts = this._sanitizeOptions(options);
|
|
2697
3060
|
this._stripSandboxControlOptions(spawnOpts);
|
|
2698
3061
|
this._stripPluginControlOptions(spawnOpts);
|
|
3062
|
+
this._stripWorkspaceTransactionOptions(spawnOpts);
|
|
2699
3063
|
if (traceCtx) {
|
|
2700
3064
|
spawnOpts.env = { ...(spawnOpts.env || process.env) };
|
|
2701
3065
|
spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
|
|
@@ -2754,10 +3118,22 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2754
3118
|
args = [...sandboxPlan.args];
|
|
2755
3119
|
const optsForSync = { ...sandboxPlan.options };
|
|
2756
3120
|
this._applySandboxAudit(auditEntry, sandboxPlan, sandboxPlan.applied);
|
|
3121
|
+
try {
|
|
3122
|
+
this._prepareWorkspaceTransactionAudit(auditEntry);
|
|
3123
|
+
} catch (transactionError) {
|
|
3124
|
+
sandboxPlan.cleanup?.();
|
|
3125
|
+
transactionError.auditEntry = auditEntry;
|
|
3126
|
+
throw transactionError;
|
|
3127
|
+
}
|
|
2757
3128
|
|
|
2758
3129
|
const nativeSpawnSyncFn = this._native?.spawnSync || nativeSpawnSync;
|
|
2759
3130
|
try {
|
|
2760
3131
|
const result = nativeSpawnSyncFn(command, args, optsForSync);
|
|
3132
|
+
this._settleWorkspaceTransactionSpawn(auditEntry, {
|
|
3133
|
+
exitCode: result.status,
|
|
3134
|
+
signal: result.signal,
|
|
3135
|
+
error: result.error?.message || null,
|
|
3136
|
+
});
|
|
2761
3137
|
if (sandboxPlan.applied) this._stats.sandboxed++;
|
|
2762
3138
|
auditEntry.exitCode = result.status;
|
|
2763
3139
|
auditEntry.endTime = Date.now();
|
|
@@ -2766,6 +3142,14 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2766
3142
|
this._writeRplEntry(auditEntry, "completed");
|
|
2767
3143
|
return result;
|
|
2768
3144
|
} catch (err) {
|
|
3145
|
+
try {
|
|
3146
|
+
this._settleWorkspaceTransactionSpawn(auditEntry, {
|
|
3147
|
+
error: err.message,
|
|
3148
|
+
});
|
|
3149
|
+
} catch {
|
|
3150
|
+
// Preserve the primary spawn failure; transaction settlement remains
|
|
3151
|
+
// fail-closed and blocks accept/rollback.
|
|
3152
|
+
}
|
|
2769
3153
|
auditEntry.error = err.message;
|
|
2770
3154
|
auditEntry.endTime = Date.now();
|
|
2771
3155
|
auditEntry.durationMs = auditEntry.endTime - startTime;
|
|
@@ -2788,6 +3172,11 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2788
3172
|
if (!ptyModule || typeof ptyModule.spawn !== "function") {
|
|
2789
3173
|
throw new TypeError("pty_module_spawn_unavailable");
|
|
2790
3174
|
}
|
|
3175
|
+
options = this._withWorkspaceTransactionBoundaries(
|
|
3176
|
+
options,
|
|
3177
|
+
options.cwd || process.cwd(),
|
|
3178
|
+
{ command, args, sync: false, pty: true },
|
|
3179
|
+
);
|
|
2791
3180
|
const executionId = crypto.randomUUID();
|
|
2792
3181
|
const startTime = Date.now();
|
|
2793
3182
|
const origin = options.origin || "terminal:pty";
|
|
@@ -2806,6 +3195,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2806
3195
|
policy,
|
|
2807
3196
|
operation: "pty.spawn",
|
|
2808
3197
|
pty: true,
|
|
3198
|
+
detached: options.detached === true,
|
|
2809
3199
|
sandboxed: false,
|
|
2810
3200
|
sandboxReason: "native_pty_host_boundary",
|
|
2811
3201
|
sandboxRequired: [],
|
|
@@ -2850,6 +3240,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2850
3240
|
const spawnOptions = { ...options, args: [...auditEntry.args] };
|
|
2851
3241
|
this._stripSandboxControlOptions(spawnOptions);
|
|
2852
3242
|
this._stripPluginControlOptions(spawnOptions);
|
|
3243
|
+
this._stripWorkspaceTransactionOptions(spawnOptions);
|
|
2853
3244
|
try {
|
|
2854
3245
|
const terminalName =
|
|
2855
3246
|
typeof options.name === "string" && options.name
|
|
@@ -2880,12 +3271,23 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2880
3271
|
}
|
|
2881
3272
|
const filteredArgs = spawnOptions.args || [];
|
|
2882
3273
|
delete spawnOptions.args;
|
|
3274
|
+
this._prepareWorkspaceTransactionAudit(auditEntry);
|
|
2883
3275
|
|
|
2884
3276
|
if (sandboxPolicy.requiredBoundaries.length === 0) {
|
|
2885
3277
|
delete spawnOptions.origin;
|
|
2886
3278
|
delete spawnOptions.policy;
|
|
2887
3279
|
delete spawnOptions.scope;
|
|
2888
3280
|
const proc = ptyModule.spawn(command, filteredArgs, spawnOptions);
|
|
3281
|
+
try {
|
|
3282
|
+
this._bindWorkspaceTransactionProcess(auditEntry, proc);
|
|
3283
|
+
} catch (transactionError) {
|
|
3284
|
+
try {
|
|
3285
|
+
proc.kill?.();
|
|
3286
|
+
} catch {
|
|
3287
|
+
// The durable execution remains unsettled and blocks recovery.
|
|
3288
|
+
}
|
|
3289
|
+
throw transactionError;
|
|
3290
|
+
}
|
|
2889
3291
|
auditEntry.pid = proc?.pid ?? null;
|
|
2890
3292
|
auditEntry.endTime = Date.now();
|
|
2891
3293
|
auditEntry.durationMs = auditEntry.endTime - startTime;
|
|
@@ -2966,6 +3368,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
2966
3368
|
],
|
|
2967
3369
|
});
|
|
2968
3370
|
proc = wrapSandboxedPty(terminal, child, command, this._ptyAdapter);
|
|
3371
|
+
this._bindWorkspaceTransactionProcess(auditEntry, proc);
|
|
2969
3372
|
} catch (ptyError) {
|
|
2970
3373
|
if (child && typeof child.kill === "function") {
|
|
2971
3374
|
try {
|
|
@@ -3025,6 +3428,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
3025
3428
|
}
|
|
3026
3429
|
sandboxPlan.cleanup?.();
|
|
3027
3430
|
this._applySandboxAudit(auditEntry, sandboxPlan, true);
|
|
3431
|
+
this._updateWorkspaceTransactionProcessGuarantees(auditEntry);
|
|
3028
3432
|
this._stats.sandboxed++;
|
|
3029
3433
|
|
|
3030
3434
|
child.once("exit", (code, signal) => {
|
|
@@ -3064,6 +3468,14 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
3064
3468
|
exec(command, options, callback) {
|
|
3065
3469
|
const opts = typeof options === "function" ? {} : options;
|
|
3066
3470
|
const cb = typeof options === "function" ? options : callback;
|
|
3471
|
+
if (this._requiresExplicitLinuxWorkspaceShell(opts.cwd || process.cwd())) {
|
|
3472
|
+
const invocation = this._explicitLinuxShellInvocation(command, opts);
|
|
3473
|
+
return this.spawn(invocation.command, invocation.args, {
|
|
3474
|
+
...opts,
|
|
3475
|
+
shell: false,
|
|
3476
|
+
origin: opts.origin || "shell:exec",
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3067
3479
|
return this.spawn(command, [], {
|
|
3068
3480
|
...opts,
|
|
3069
3481
|
shell: true,
|
|
@@ -3072,6 +3484,27 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
3072
3484
|
}
|
|
3073
3485
|
|
|
3074
3486
|
execSync(command, options = {}) {
|
|
3487
|
+
if (
|
|
3488
|
+
this._requiresExplicitLinuxWorkspaceShell(options.cwd || process.cwd())
|
|
3489
|
+
) {
|
|
3490
|
+
const invocation = this._explicitLinuxShellInvocation(command, options);
|
|
3491
|
+
const result = this.spawnSync(invocation.command, invocation.args, {
|
|
3492
|
+
...options,
|
|
3493
|
+
shell: false,
|
|
3494
|
+
origin: options.origin || "shell:execSync",
|
|
3495
|
+
});
|
|
3496
|
+
if (result?.error) throw result.error;
|
|
3497
|
+
if (result?.status != null && result.status !== 0) {
|
|
3498
|
+
const error = new Error(
|
|
3499
|
+
`Command failed (exit ${result.status}): ${command}`,
|
|
3500
|
+
);
|
|
3501
|
+
error.status = result.status;
|
|
3502
|
+
error.stdout = result.stdout;
|
|
3503
|
+
error.stderr = result.stderr;
|
|
3504
|
+
throw error;
|
|
3505
|
+
}
|
|
3506
|
+
return result?.stdout ?? "";
|
|
3507
|
+
}
|
|
3075
3508
|
const spawnOpts = {
|
|
3076
3509
|
...options,
|
|
3077
3510
|
shell: true,
|