chainlesschain 0.162.179 → 0.162.181
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 +6 -3
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BF13YdB-.js → AIOps-cq0dwbnf.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-B9y67AjF.js → ActionButton-CvcgGl3w.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-FcdDHoSC.js → Analytics-hM2-jFqg.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CRm_zwSp.js → AppLayout-B9_Gsd_g.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Cbo7-usf.js → Artifacts-B7bu52Og.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-vMaLpkHC.js → Audit-DThfDgqB.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-FiUY692t.js → BackgroundAgents-DjlFiD4v.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BK80j_GQ.js → Backup-CK9xJKnG.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-ChNMuYR1.js → BaseInput-DEiSU8K8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-928UREpm.js → Chat-CpT96czj.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BKPPNmZI.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-dFW-zDbe.js → Checkbox-C1GfDnO0.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BCcNJUj2.js → Codegen-3DAKgiLx.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DcEkLv70.js → Col-74cOwZU1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DihBRRqg.js → Community-tn9Kkz9j.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DGYxwWET.js → Compact-BcoXH7m4.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-OnYe1vS4.js → Compliance-DIPGUBgi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CmWB54c2.js → Cowork-doD5_91c.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BrANb2uw.js → Cron-C-IDvubz.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-2HgWzSxg.js → Crosschain-CdcCoBdj.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DdDwNI6K.js → DID-DdH5aMMA.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-D6PnE78-.js → Dashboard-DHLA4mm0.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-ClzvSN4O.js → Dropdown-dEeCt7Md.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BUk9B727.js → EmailListRenderer-BMaecFv9.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C8hi3v7i.js → FamilyGuardDashboard-DUaUukTN.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B5SP8xkg.js → Federation-CMBE0_rp.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWzJ8AU5.js → FormItemContext-PG-JDbXq.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-3E3Q3rjG.js → GenericCardRenderer-Bcb0p88m.js} +1 -1
- package/src/assets/web-panel/assets/{Git-z8IdCMId.js → Git-Bgv15myB.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CgqCF9lX.js → Governance-DB8YTArI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-zgS-AuH9.js → Inference-BQMwCWaS.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BdbgmESJ.js → KnowledgeGraph-B1_ZziiB.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BHpknnjy.js → Logs-DovEEnt_.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-D201OL4T.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Di9sAR7j.js → Marketplace-BlCXSpyB.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DtnR9Z_A.js → McpTools-CsCR09Wj.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BaX9uuXD.js → Memory-CBVRLKno.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xelyKWTY.js → MobileBridge-C9OIafgA.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BiFTOGBN.js → MobileProjects-D_zoJ8Xs.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-bV3OzTiO.js → Mtc-vE-HdvLj.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BkwvFTdZ.js → MtcAudit-CPLaekCd.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-_6HQQ-oC.js → Multisig-C_UP9C2o.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BBYhmWQZ.js → NLProgramming-2nODJcU1.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cq-6dQnU.js → Notes-BhE-SJGu.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-ILZ9zkX6.js → NotificationSettings-D21q7BNU.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DgmTBEvk.js → OrderTableRenderer-0b62GYOD.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CkLnWwCk.js → Organization-CEqmw_sb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CWVHZbhf.js → Overflow-MW-gMDO_.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BXbqETkr.js → P2P-CY0hjI_G.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser--0JR-0ex.js → PdhVaultBrowser-BbWh7Man.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Cz9_tfSy.js → Permissions-ChqEA_4Q.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dq7OWYtx.js → PersonalDataHub-B3YrbYfD.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-DHPUNcc-.js → Pipeline-B8WBhhlW.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CEAbrDkS.js → Privacy-Cmb0PXNx.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CJYfclXE.js → ProjectInit-hF6zW3Nz.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-oCTgPCgp.js → ProjectSettings-BFSVfpW0.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DAWkVFkc.js +1 -0
- package/src/assets/web-panel/assets/{Providers-BLTZtzTG.js → Providers-B8yux8Dx.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C0b0TQB3.js → QrScannerModal-N76cQluO.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-tOl6N_gM.js → QuickAsk-ClvZ0KYR.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DPzKr8yr.js → Recommend-B-GfgBpT.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-mlglCdQi.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-Cji1Vl79.js → Reputation-DJIZzQeG.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D-NERWf2.js → Row-COzxu_bb.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DzlgmAlB.js → RssFeed-CoYsJQuE.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cs0SWSa4.js → Search-DC77Mq9S.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DL7EzcUO.js → Security-Loy2EdMO.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CT-PzgTd.js → Services-GUD07Eu0.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-tByJWdJg.js → Skeleton-DWzkptYU.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Dnr_6TTl.js → Skills-CEDn1r-Z.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-4O-yq0LY.js → Sla-ub_k4pXh.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BYBVg1oR.js → SpeechSettings-D7HLX9CA.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CpOYPbE5.js → SyncSettings-DbwOvl6m.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CGVhOVh6.js → Tasks-CBQzoAu5.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BuHFxR4X.js → Templates-BNVeIhud.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DxDxHzgf.js → Tenant-PS9AzhXU.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B9g_nISg.js → Terminal-Cgb4eLB_.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DlrBC_4A.js → TimelineRenderer-CrmXMOkg.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DvBlYZ6i.js → Tokens-DD-VDHw1.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-Dg8G9bhF.js → Trigger-BsoN_n-n.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-COICx4E-.js → Trust-n8LmN4ZV.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4pOBv7T.js → UkeySign-C9_7MImA.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Qt4NMA8Q.js → VideoEditing-DyBteNgn.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-X1EkQc2Z.js → Wallet-BS5ixeXI.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CyeEFBE5.js → WebAuthn-spc4UImU.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-nwzbMiVQ.js → WorkflowEditor-D7iVvaBO.js} +1 -1
- package/src/assets/web-panel/assets/chat-6CmBkHrL.js +1 -0
- package/src/assets/web-panel/assets/{colors-5Isz5lhJ.js → colors-BGoCLkxI.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DsFQS3Oy.js → compact-item-fYZBetMO.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-OcEXp2uz.js → createContext-BeM-1UVj.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BzPwHnw8.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CZvC4p09.js → hasIn-CE7ZuEnV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF1sab0Q.js → index--lap6cf7.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7yeL3rf.js → index-ATcSr_2O.js} +1 -1
- package/src/assets/web-panel/assets/{index-VkFXA9N-.js → index-B2U-ObMq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DSmGGg5h.js → index-B3CDgvNW.js} +1 -1
- package/src/assets/web-panel/assets/{index-jrNIrpq4.js → index-B4wiTEOM.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8ibl7bc.js → index-B8BsdNV_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DOLozRk8.js → index-BEVm0lCS.js} +10 -10
- package/src/assets/web-panel/assets/{index-BPgc_bdR.js → index-BNZDILcO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTZZzlMO.js → index-BU_2qiDk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Vubi6Tf5.js → index-BV551878.js} +1 -1
- package/src/assets/web-panel/assets/{index-nXrnztXp.js → index-BfPL-m46.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN8lPVpQ.js → index-BowH1eLQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgE2BZl2.js → index-BsMfoaeo.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cd5b2JHB.js → index-BunaQCa0.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQlGw0nQ.js → index-C0v6YKKO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Do6FJK32.js → index-C4SJVjs4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqvo1f9T.js → index-C9Y3k0HI.js} +1 -1
- package/src/assets/web-panel/assets/{index-LUdgPZg9.js → index-CIpVrf5P.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpD2O7_p.js → index-CRLjEP1H.js} +1 -1
- package/src/assets/web-panel/assets/{index-r_9NG6P7.js → index-CRnUE8Cb.js} +1 -1
- package/src/assets/web-panel/assets/{index-FIgMAfBh.js → index-Cip8emOQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-RsQ1lFDV.js → index-CpIGIB85.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGJupV0S.js → index-D1r9tT3J.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpglwPWl.js → index-DT3zPSVX.js} +1 -1
- package/src/assets/web-panel/assets/index-DacrAdc-.js +1 -0
- package/src/assets/web-panel/assets/{index-CG3B2wCF.js → index-De0quFDw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxA2PDK1.js → index-DoXk5KJ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaGKOVvQ.js → index-DsvfrESp.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7CtJYXq.js → index-Dw1PyYPm.js} +1 -1
- package/src/assets/web-panel/assets/index-EHzNJozo.js +1 -0
- package/src/assets/web-panel/assets/{index-Bu9NXvIa.js → index-MtOuq2i6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsZkTaa3.js → index-Puq6Luf1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPE4zHXF.js → index-QIpnpZgz.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bsb-iylv.js → index-RMZ8a7Yt.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMtGRQqw.js → index-WUrJ6S1j.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3lWvhk4.js → index-YjcumOZ4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmVbCGx_.js → index-lJBjB1QX.js} +1 -1
- package/src/assets/web-panel/assets/{index-A5SHQW_M.js → index-ukg99D1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFJgt6qM.js → index-zb7rVOhV.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cdgep9YT.js → initDefaultProps-CYHMs1ag.js} +1 -1
- package/src/assets/web-panel/assets/{motion-7yn1ACEm.js → motion-DDB2Ffk5.js} +1 -1
- package/src/assets/web-panel/assets/{move-D8o133Xc.js → move-Bg3cD-dX.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CClHIG9V.js → mtc-parser-B5nedjTS.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CLnrPE2d.js → omit-BHyuUILu.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-1CTWoZWl.js → pickAttrs-C9YQnEN9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-xNNtS4Sp.js → placementArrow-DBUFYUe7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-ypzAofs7.js → responsiveObserve-nBs2Obnk.js} +1 -1
- package/src/assets/web-panel/assets/{slide-QsrY9UwD.js → slide-DkCEaXNU.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DSdThzOB.js → statusUtils-aqE1Wi6U.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B_kKM8Ob.js → styleChecker-CN5bhfHB.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CcdTBNV9.js → useFlexGapSupport-B_oX_kvJ.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BuAH2XFU.js → useFs-rCuXHeme.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-B3zWiG4A.js → usePersonalDataHub-DZR9Abyd.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-B45Rc-CQ.js → vnode-ConCYQlX.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DaYZDU5f.js → zoom-CUdDxG5M.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +4 -1
- package/src/commands/background-session.js +2 -1
- package/src/commands/context.js +69 -2
- package/src/commands/hub.js +30 -24
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/background-agent-protocol.js +38 -0
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/remote-session-protocol.js +65 -6
- package/src/gateways/ws/ws-server.js +8 -1
- package/src/harness/feature-flags.js +15 -8
- package/src/harness/mcp-client.js +618 -43
- package/src/lazy-dispatch.js +30 -1
- package/src/lib/agent-authority.js +22 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -0
- package/src/lib/agent-team/team-runner.js +22 -0
- package/src/lib/artifact-store.js +35 -0
- package/src/lib/async-hook-supervisor.cjs +13 -2
- package/src/lib/audit-mtc.js +3 -2
- package/src/lib/background-agent-supervisor.js +104 -3
- package/src/lib/background-interaction-journal.js +407 -0
- package/src/lib/background-interaction-resolver.js +23 -34
- package/src/lib/background-session-transport.js +12 -2
- package/src/lib/capability-manifest.js +41 -3
- package/src/lib/capability-negotiation.js +1 -1
- package/src/lib/channels/channel-manager.js +33 -0
- package/src/lib/channels/telegram-channel.js +23 -5
- package/src/lib/channels/webhook-channel.js +38 -15
- package/src/lib/chrome-connector.js +169 -23
- package/src/lib/config-manager.js +23 -9
- package/src/lib/context-breakdown.js +31 -0
- package/src/lib/cowork-cron.js +274 -32
- package/src/lib/cross-chain-mtc.js +3 -2
- package/src/lib/durable-security-store.js +124 -0
- package/src/lib/event-runtime-host.js +67 -6
- package/src/lib/event-runtime-store.js +163 -0
- package/src/lib/goal-store.js +32 -12
- package/src/lib/headless-manifest.js +2 -1
- package/src/lib/hook-environment.cjs +58 -0
- package/src/lib/hook-manager.js +12 -5
- package/src/lib/hook-runner.cjs +122 -28
- package/src/lib/hook-runner.js +1 -0
- package/src/lib/hooks-v2-producers.js +36 -0
- package/src/lib/hooks-v2-runtime.js +329 -52
- package/src/lib/interaction-adapter.js +60 -19
- package/src/lib/interaction-binding.js +64 -0
- package/src/lib/json-schema-output.js +439 -14
- package/src/lib/json-schema-validate.js +514 -883
- package/src/lib/lan-pairing-tokens.js +115 -57
- package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
- package/src/lib/lsp/lsp-client.js +4 -0
- package/src/lib/lsp/lsp-manager.js +3 -0
- package/src/lib/lsp/lsp-server-registry.js +18 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +35 -21
- package/src/lib/packer/pkg-config-generator.js +1 -0
- package/src/lib/permission-decision.js +99 -0
- package/src/lib/plugin-monitor-supervisor.js +3 -0
- package/src/lib/plugin-runtime/capabilities.js +50 -0
- package/src/lib/plugin-runtime/capability-consent.js +42 -35
- package/src/lib/plugin-runtime/hooks.js +36 -9
- package/src/lib/plugin-runtime/install.js +23 -7
- package/src/lib/plugin-runtime/lsp.js +4 -0
- package/src/lib/plugin-runtime/manifest.js +50 -2
- package/src/lib/plugin-runtime/mcp.js +30 -5
- package/src/lib/plugin-runtime/monitors.js +11 -0
- package/src/lib/plugin-runtime/plugin-options.js +94 -38
- package/src/lib/plugin-runtime/policy.js +29 -2
- package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
- package/src/lib/plugin-runtime/scopes.js +7 -3
- package/src/lib/plugin-runtime/trust.js +36 -31
- package/src/lib/process-execution-broker/credential-agent.js +114 -16
- package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
- package/src/lib/process-execution-broker/credential-transport.js +378 -0
- package/src/lib/process-execution-broker/index.js +408 -19
- package/src/lib/process-execution-broker/platform-sandbox.js +561 -21
- package/src/lib/process-execution-broker/windows-sandbox.ps1 +999 -0
- package/src/lib/project-mcp-trust.js +21 -26
- package/src/lib/protocol-replay.js +19 -12
- package/src/lib/settings-hooks.cjs +13 -0
- package/src/lib/skill-loader.js +83 -10
- package/src/lib/sync-credentials.js +5 -4
- package/src/lib/turn-binding.js +8 -6
- package/src/repl/agent-repl.js +113 -28
- package/src/repl/repl-turn-binding.js +5 -2
- package/src/runtime/agent-core.js +331 -26
- package/src/runtime/coding-agent-contract-shared.cjs +3 -3
- package/src/runtime/headless-runner.js +101 -4
- package/src/runtime/headless-stream.js +209 -23
- package/src/runtime/mcp-config.js +29 -17
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/workers/background-agent-worker.js +128 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DvySUzxJ.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-DkxCweb1.js +0 -1
- package/src/assets/web-panel/assets/Projects-ClomCrN0.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-pRgaWsUK.js +0 -4
- package/src/assets/web-panel/assets/chat-Bvl-ojg2.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CSVgo71Z.js +0 -1
- package/src/assets/web-panel/assets/index-DMIWh9fu.js +0 -1
- package/src/assets/web-panel/assets/index-ssTvV1l6.js +0 -1
|
@@ -33,6 +33,7 @@ import path from "node:path";
|
|
|
33
33
|
import os from "node:os";
|
|
34
34
|
import crypto from "node:crypto";
|
|
35
35
|
import { getHomeDir } from "./paths.js";
|
|
36
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
36
37
|
|
|
37
38
|
/** Token lifetime — mirrors device-pairing-handler.js pairingTimeout (5 min). */
|
|
38
39
|
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
@@ -108,7 +109,10 @@ export function buildToken({ did, deviceInfo } = {}) {
|
|
|
108
109
|
* @param {string} [filePath]
|
|
109
110
|
* @returns {{ schema: string, tokens: Array }}
|
|
110
111
|
*/
|
|
111
|
-
export function readTokens(
|
|
112
|
+
export function readTokens(
|
|
113
|
+
filePath = defaultTokenStorePath(),
|
|
114
|
+
{ failIfUnavailable = false } = {},
|
|
115
|
+
) {
|
|
112
116
|
if (!fs.existsSync(filePath)) {
|
|
113
117
|
return { schema: "cc-pairing-tokens/v1", tokens: [] };
|
|
114
118
|
}
|
|
@@ -121,9 +125,28 @@ export function readTokens(filePath = defaultTokenStorePath()) {
|
|
|
121
125
|
tokens: parsed.tokens,
|
|
122
126
|
};
|
|
123
127
|
}
|
|
128
|
+
if (failIfUnavailable) {
|
|
129
|
+
const error = new Error(
|
|
130
|
+
`Pairing token store must contain a tokens array: ${filePath}`,
|
|
131
|
+
);
|
|
132
|
+
error.code = "PAIRING_TOKEN_STORE_CORRUPT";
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
124
135
|
return { schema: "cc-pairing-tokens/v1", tokens: [] };
|
|
125
|
-
} catch (
|
|
126
|
-
|
|
136
|
+
} catch (cause) {
|
|
137
|
+
if (
|
|
138
|
+
failIfUnavailable ||
|
|
139
|
+
cause?.code === "PAIRING_TOKEN_STORE_CORRUPT"
|
|
140
|
+
) {
|
|
141
|
+
if (cause?.code === "PAIRING_TOKEN_STORE_CORRUPT") throw cause;
|
|
142
|
+
const error = new Error(
|
|
143
|
+
`Could not read pairing token store: ${filePath}`,
|
|
144
|
+
{ cause },
|
|
145
|
+
);
|
|
146
|
+
error.code = "PAIRING_TOKEN_STORE_UNAVAILABLE";
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
// Advisory listing keeps its legacy empty fallback.
|
|
127
150
|
return { schema: "cc-pairing-tokens/v1", tokens: [] };
|
|
128
151
|
}
|
|
129
152
|
}
|
|
@@ -134,19 +157,46 @@ export function readTokens(filePath = defaultTokenStorePath()) {
|
|
|
134
157
|
* @param {{ schema?: string, tokens: Array }} state
|
|
135
158
|
* @param {string} [filePath]
|
|
136
159
|
*/
|
|
137
|
-
|
|
160
|
+
function _prepareTokenStore(filePath) {
|
|
138
161
|
const dir = path.dirname(filePath);
|
|
139
162
|
if (!fs.existsSync(dir)) {
|
|
140
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
163
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
141
164
|
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function _writeTokensUnlocked(state, filePath) {
|
|
168
|
+
_prepareTokenStore(filePath);
|
|
142
169
|
const toWrite = {
|
|
143
170
|
schema: state.schema || "cc-pairing-tokens/v1",
|
|
144
171
|
tokens: state.tokens || [],
|
|
145
172
|
};
|
|
146
173
|
// Write to temp + rename for atomicity (avoid partial reads).
|
|
147
|
-
const tmp = filePath
|
|
148
|
-
|
|
149
|
-
|
|
174
|
+
const tmp = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
175
|
+
try {
|
|
176
|
+
fs.writeFileSync(tmp, JSON.stringify(toWrite, null, 2), {
|
|
177
|
+
encoding: "utf-8",
|
|
178
|
+
mode: 0o600,
|
|
179
|
+
});
|
|
180
|
+
fs.renameSync(tmp, filePath);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
try {
|
|
183
|
+
fs.rmSync(tmp, { force: true });
|
|
184
|
+
} catch {
|
|
185
|
+
// Preserve the original persistence error.
|
|
186
|
+
}
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function _withTokenStoreLock(filePath, body) {
|
|
192
|
+
_prepareTokenStore(filePath);
|
|
193
|
+
return withFileLock(filePath, body, { failIfUnavailable: true });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function writeTokens(state, filePath = defaultTokenStorePath()) {
|
|
197
|
+
return _withTokenStoreLock(filePath, () =>
|
|
198
|
+
_writeTokensUnlocked(state, filePath),
|
|
199
|
+
);
|
|
150
200
|
}
|
|
151
201
|
|
|
152
202
|
/**
|
|
@@ -160,17 +210,19 @@ export function writeTokens(state, filePath = defaultTokenStorePath()) {
|
|
|
160
210
|
*/
|
|
161
211
|
export function addToken(input, filePath = defaultTokenStorePath()) {
|
|
162
212
|
const token = buildToken(input);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
213
|
+
return _withTokenStoreLock(filePath, () => {
|
|
214
|
+
const state = readTokens(filePath, { failIfUnavailable: true });
|
|
215
|
+
// Invalidate prior pending tokens for the same DID.
|
|
216
|
+
state.tokens = state.tokens.map((t) => {
|
|
217
|
+
if (t.qrData?.did === input.did && t.status === STATUS.PENDING) {
|
|
218
|
+
return { ...t, status: STATUS.REVOKED, revokedAtMs: Date.now() };
|
|
219
|
+
}
|
|
220
|
+
return t;
|
|
221
|
+
});
|
|
222
|
+
state.tokens.push(token);
|
|
223
|
+
_writeTokensUnlocked(state, filePath);
|
|
224
|
+
return token;
|
|
170
225
|
});
|
|
171
|
-
state.tokens.push(token);
|
|
172
|
-
writeTokens(state, filePath);
|
|
173
|
-
return token;
|
|
174
226
|
}
|
|
175
227
|
|
|
176
228
|
/**
|
|
@@ -178,18 +230,20 @@ export function addToken(input, filePath = defaultTokenStorePath()) {
|
|
|
178
230
|
* before `list` to keep status accurate.
|
|
179
231
|
*/
|
|
180
232
|
export function sweepExpired(filePath = defaultTokenStorePath()) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
233
|
+
return _withTokenStoreLock(filePath, () => {
|
|
234
|
+
const state = readTokens(filePath, { failIfUnavailable: true });
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
let touched = 0;
|
|
237
|
+
state.tokens = state.tokens.map((t) => {
|
|
238
|
+
if (t.status === STATUS.PENDING && t.expiresAtMs <= now) {
|
|
239
|
+
touched += 1;
|
|
240
|
+
return { ...t, status: STATUS.EXPIRED, expiredAtMs: now };
|
|
241
|
+
}
|
|
242
|
+
return t;
|
|
243
|
+
});
|
|
244
|
+
if (touched > 0) _writeTokensUnlocked(state, filePath);
|
|
245
|
+
return touched;
|
|
190
246
|
});
|
|
191
|
-
if (touched > 0) writeTokens(state, filePath);
|
|
192
|
-
return touched;
|
|
193
247
|
}
|
|
194
248
|
|
|
195
249
|
/**
|
|
@@ -224,20 +278,22 @@ export function findToken(code, filePath = defaultTokenStorePath()) {
|
|
|
224
278
|
* not found or already non-pending.
|
|
225
279
|
*/
|
|
226
280
|
export function revokeToken(code, filePath = defaultTokenStorePath()) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
281
|
+
return _withTokenStoreLock(filePath, () => {
|
|
282
|
+
const state = readTokens(filePath, { failIfUnavailable: true });
|
|
283
|
+
const i = state.tokens.findIndex((t) => t.code === code);
|
|
284
|
+
if (i < 0) return { revoked: false, reason: "not_found" };
|
|
285
|
+
const t = state.tokens[i];
|
|
286
|
+
if (t.status !== STATUS.PENDING) {
|
|
287
|
+
return { revoked: false, reason: `not_pending(${t.status})` };
|
|
288
|
+
}
|
|
289
|
+
state.tokens[i] = {
|
|
290
|
+
...t,
|
|
291
|
+
status: STATUS.REVOKED,
|
|
292
|
+
revokedAtMs: Date.now(),
|
|
293
|
+
};
|
|
294
|
+
_writeTokensUnlocked(state, filePath);
|
|
295
|
+
return { revoked: true, token: state.tokens[i] };
|
|
296
|
+
});
|
|
241
297
|
}
|
|
242
298
|
|
|
243
299
|
/**
|
|
@@ -245,20 +301,22 @@ export function revokeToken(code, filePath = defaultTokenStorePath()) {
|
|
|
245
301
|
* this after the mobile successfully connects.
|
|
246
302
|
*/
|
|
247
303
|
export function markConsumed(code, filePath = defaultTokenStorePath()) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
304
|
+
return _withTokenStoreLock(filePath, () => {
|
|
305
|
+
const state = readTokens(filePath, { failIfUnavailable: true });
|
|
306
|
+
const i = state.tokens.findIndex((t) => t.code === code);
|
|
307
|
+
if (i < 0) return { consumed: false, reason: "not_found" };
|
|
308
|
+
const t = state.tokens[i];
|
|
309
|
+
if (t.status !== STATUS.PENDING) {
|
|
310
|
+
return { consumed: false, reason: `not_pending(${t.status})` };
|
|
311
|
+
}
|
|
312
|
+
state.tokens[i] = {
|
|
313
|
+
...t,
|
|
314
|
+
status: STATUS.CONSUMED,
|
|
315
|
+
consumedAtMs: Date.now(),
|
|
316
|
+
};
|
|
317
|
+
_writeTokensUnlocked(state, filePath);
|
|
318
|
+
return { consumed: true, token: state.tokens[i] };
|
|
319
|
+
});
|
|
262
320
|
}
|
|
263
321
|
|
|
264
322
|
export { STATUS, QR_TYPE, DEFAULT_TTL_MS };
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
buildSpawnCommand,
|
|
16
16
|
} from "../lsp-client.js";
|
|
17
17
|
import { encodeMessage, MessageBuffer } from "../jsonrpc-stream.js";
|
|
18
|
+
import { executionBroker } from "../../process-execution-broker/index.js";
|
|
18
19
|
|
|
19
20
|
/** A fake language server process. Auto-replies to `initialize`; test controls the rest. */
|
|
20
21
|
class FakeChild extends EventEmitter {
|
|
@@ -94,6 +95,37 @@ describe("LSPClient handshake", () => {
|
|
|
94
95
|
shell: false,
|
|
95
96
|
}),
|
|
96
97
|
);
|
|
98
|
+
expect(_deps.spawn.mock.calls[0][2]).not.toHaveProperty("sandboxPolicy");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("passes plugin sandbox requirements to ProcessExecutionBroker", async () => {
|
|
102
|
+
const child = new FakeChild();
|
|
103
|
+
const brokerSpawn = vi
|
|
104
|
+
.spyOn(executionBroker, "spawn")
|
|
105
|
+
.mockReturnValue(child);
|
|
106
|
+
const client = makeClient(child, {
|
|
107
|
+
origin: "plugin:lsp",
|
|
108
|
+
pluginId: "strict-lsp",
|
|
109
|
+
pluginVersion: "1.0.0",
|
|
110
|
+
pluginSource: "/plugins/strict-lsp/.lsp.json",
|
|
111
|
+
sandboxPolicy: {
|
|
112
|
+
requiredBoundaries: ["filesystem", "network"],
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await client.start();
|
|
117
|
+
|
|
118
|
+
expect(brokerSpawn).toHaveBeenCalledWith(
|
|
119
|
+
"fake-lsp",
|
|
120
|
+
["--stdio"],
|
|
121
|
+
expect.objectContaining({
|
|
122
|
+
origin: "plugin:lsp",
|
|
123
|
+
sandboxPolicy: {
|
|
124
|
+
requiredBoundaries: ["filesystem", "network"],
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
expect(_deps.spawn).not.toHaveBeenCalled();
|
|
97
129
|
});
|
|
98
130
|
|
|
99
131
|
it("kills the spawned server when the initialize handshake times out (no orphan)", async () => {
|
|
@@ -19,6 +19,7 @@ import { _deps as regDeps } from "../lsp-server-registry.js";
|
|
|
19
19
|
class FakeClient extends EventEmitter {
|
|
20
20
|
constructor(opts) {
|
|
21
21
|
super();
|
|
22
|
+
this.opts = opts;
|
|
22
23
|
this.rootPath = opts.rootPath;
|
|
23
24
|
this.running = true;
|
|
24
25
|
this.notifications = [];
|
|
@@ -102,6 +103,30 @@ describe("position helpers", () => {
|
|
|
102
103
|
});
|
|
103
104
|
|
|
104
105
|
describe("ensureFor", () => {
|
|
106
|
+
it("passes resolved plugin sandbox requirements into LSPClient", async () => {
|
|
107
|
+
const mgr = new LSPManager();
|
|
108
|
+
|
|
109
|
+
await mgr._startServer(
|
|
110
|
+
"/proj::strict-lsp",
|
|
111
|
+
{
|
|
112
|
+
id: "strict-lsp",
|
|
113
|
+
command: "strict-lsp",
|
|
114
|
+
args: ["--stdio"],
|
|
115
|
+
origin: "plugin:lsp",
|
|
116
|
+
pluginId: "strict-plugin",
|
|
117
|
+
sandboxPolicy: {
|
|
118
|
+
requiredBoundaries: ["filesystem", "network"],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
"/proj",
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(lastClient.opts.sandboxPolicy).toEqual({
|
|
125
|
+
requiredBoundaries: ["filesystem", "network"],
|
|
126
|
+
});
|
|
127
|
+
await mgr.disposeAll();
|
|
128
|
+
});
|
|
129
|
+
|
|
105
130
|
it("starts a server and sends didOpen the first time", async () => {
|
|
106
131
|
const mgr = new LSPManager();
|
|
107
132
|
const ready = await mgr.ensureFor("/proj/src/a.ts");
|
|
@@ -47,6 +47,7 @@ export class LSPClient extends EventEmitter {
|
|
|
47
47
|
this.pluginId = opts.pluginId || null;
|
|
48
48
|
this.pluginVersion = opts.pluginVersion || null;
|
|
49
49
|
this.pluginSource = opts.pluginSource || null;
|
|
50
|
+
this.sandboxPolicy = opts.sandboxPolicy || null;
|
|
50
51
|
|
|
51
52
|
this._child = null;
|
|
52
53
|
this._buffer = new MessageBuffer();
|
|
@@ -97,6 +98,9 @@ export class LSPClient extends EventEmitter {
|
|
|
97
98
|
pluginId: this.pluginId,
|
|
98
99
|
pluginVersion: this.pluginVersion,
|
|
99
100
|
pluginSource: this.pluginSource,
|
|
101
|
+
...(this.sandboxPolicy
|
|
102
|
+
? { sandboxPolicy: this.sandboxPolicy }
|
|
103
|
+
: {}),
|
|
100
104
|
}
|
|
101
105
|
: {}),
|
|
102
106
|
});
|
|
@@ -243,6 +243,9 @@ export class LSPManager {
|
|
|
243
243
|
pluginId: resolved.pluginId,
|
|
244
244
|
pluginVersion: resolved.pluginVersion,
|
|
245
245
|
pluginSource: resolved.pluginSource,
|
|
246
|
+
...(resolved.sandboxPolicy
|
|
247
|
+
? { sandboxPolicy: resolved.sandboxPolicy }
|
|
248
|
+
: {}),
|
|
246
249
|
});
|
|
247
250
|
// Collect pushed diagnostics keyed by document, tracking publish count/time
|
|
248
251
|
// so callers can wait for the project to load (first publish) and settle
|
|
@@ -96,6 +96,15 @@ export function registerLanguageServer(def) {
|
|
|
96
96
|
pluginId: def.pluginId || null,
|
|
97
97
|
pluginVersion: def.pluginVersion || null,
|
|
98
98
|
pluginSource: def.pluginSource || null,
|
|
99
|
+
...(def.sandboxPolicy
|
|
100
|
+
? {
|
|
101
|
+
sandboxPolicy: {
|
|
102
|
+
requiredBoundaries: [
|
|
103
|
+
...(def.sandboxPolicy.requiredBoundaries || []),
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
99
108
|
};
|
|
100
109
|
const existing = pluginServers.get(def.languageId) || [];
|
|
101
110
|
// De-dupe by server id so re-registering the same server (e.g. a reload)
|
|
@@ -154,6 +163,15 @@ export function resolveServer(languageId, projectRoot) {
|
|
|
154
163
|
pluginId: def.pluginId,
|
|
155
164
|
pluginVersion: def.pluginVersion,
|
|
156
165
|
pluginSource: def.pluginSource,
|
|
166
|
+
...(def.sandboxPolicy
|
|
167
|
+
? {
|
|
168
|
+
sandboxPolicy: {
|
|
169
|
+
requiredBoundaries: [
|
|
170
|
+
...(def.sandboxPolicy.requiredBoundaries || []),
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
157
175
|
}
|
|
158
176
|
: {}),
|
|
159
177
|
};
|
package/src/lib/mcp-client.js
CHANGED
package/src/lib/mcp-oauth.js
CHANGED
|
@@ -49,6 +49,7 @@ export const _deps = {
|
|
|
49
49
|
warn: (msg) => {
|
|
50
50
|
if (!process.env.VITEST) process.stderr.write(msg);
|
|
51
51
|
},
|
|
52
|
+
withStoreLock,
|
|
52
53
|
};
|
|
53
54
|
|
|
54
55
|
/** Discovery/token requests retry ONCE after a transient error (CC 2.1.191). */
|
|
@@ -500,29 +501,36 @@ function _sleepSyncMs(ms) {
|
|
|
500
501
|
* lost update. An O_EXCL lockfile makes the whole RMW mutually exclusive across
|
|
501
502
|
* processes, so each writer re-reads the latest store inside the lock.
|
|
502
503
|
*
|
|
503
|
-
* Robustness: a stale lock (holder crashed) older than STALE_MS is stolen
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
* directly — single-process correctness is unchanged, only cross-process mutual
|
|
508
|
-
* exclusion is lost. Lock timing uses the wall clock (not the `now` test seam).
|
|
504
|
+
* Robustness: a stale lock (holder crashed) older than STALE_MS is stolen.
|
|
505
|
+
* Credential metadata is critical, so missing lock primitives, unexpected lock
|
|
506
|
+
* errors, and bounded contention all fail closed instead of running `fn`
|
|
507
|
+
* without exclusion. Lock timing uses the wall clock (not the `now` test seam).
|
|
509
508
|
*/
|
|
510
|
-
function withStoreLock(file, fn) {
|
|
509
|
+
export function withStoreLock(file, fn) {
|
|
511
510
|
const fs = _deps.fs;
|
|
512
511
|
if (
|
|
513
512
|
typeof fs.openSync !== "function" ||
|
|
514
513
|
typeof fs.closeSync !== "function" ||
|
|
515
514
|
typeof fs.unlinkSync !== "function"
|
|
516
515
|
) {
|
|
517
|
-
|
|
516
|
+
const error = new Error(
|
|
517
|
+
`MCP OAuth token store lock primitives unavailable: ${file}`,
|
|
518
|
+
);
|
|
519
|
+
error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
|
|
520
|
+
throw error;
|
|
518
521
|
}
|
|
519
522
|
const STALE_MS = 10_000;
|
|
520
523
|
const MAX_WAIT_MS = 5_000;
|
|
521
524
|
const lockPath = `${file}.lock`;
|
|
522
525
|
try {
|
|
523
526
|
fs.mkdirSync(pathDefault.dirname(file), { recursive: true, mode: 0o700 });
|
|
524
|
-
} catch {
|
|
525
|
-
|
|
527
|
+
} catch (cause) {
|
|
528
|
+
const error = new Error(
|
|
529
|
+
`Could not prepare MCP OAuth token store lock: ${file}`,
|
|
530
|
+
{ cause },
|
|
531
|
+
);
|
|
532
|
+
error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
|
|
533
|
+
throw error;
|
|
526
534
|
}
|
|
527
535
|
const start = Date.now();
|
|
528
536
|
let fd = null;
|
|
@@ -532,9 +540,12 @@ function withStoreLock(file, fn) {
|
|
|
532
540
|
break;
|
|
533
541
|
} catch (err) {
|
|
534
542
|
if (!err || err.code !== "EEXIST") {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
543
|
+
const error = new Error(
|
|
544
|
+
`Could not acquire MCP OAuth token store lock: ${file}`,
|
|
545
|
+
{ cause: err },
|
|
546
|
+
);
|
|
547
|
+
error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
|
|
548
|
+
throw error;
|
|
538
549
|
}
|
|
539
550
|
let stale = false;
|
|
540
551
|
try {
|
|
@@ -552,7 +563,14 @@ function withStoreLock(file, fn) {
|
|
|
552
563
|
}
|
|
553
564
|
continue;
|
|
554
565
|
}
|
|
555
|
-
if (Date.now() - start > MAX_WAIT_MS)
|
|
566
|
+
if (Date.now() - start > MAX_WAIT_MS) {
|
|
567
|
+
const error = new Error(
|
|
568
|
+
`Timed out acquiring MCP OAuth token store lock: ${file}`,
|
|
569
|
+
{ cause: err },
|
|
570
|
+
);
|
|
571
|
+
error.code = "MCP_TOKEN_STORE_LOCK_UNAVAILABLE";
|
|
572
|
+
throw error;
|
|
573
|
+
}
|
|
556
574
|
_sleepSyncMs(50);
|
|
557
575
|
}
|
|
558
576
|
}
|
|
@@ -576,7 +594,7 @@ function withStoreLock(file, fn) {
|
|
|
576
594
|
|
|
577
595
|
export function saveStoredToken(serverUrl, record) {
|
|
578
596
|
const file = tokenStorePath();
|
|
579
|
-
return withStoreLock(file, () => {
|
|
597
|
+
return _deps.withStoreLock(file, () => {
|
|
580
598
|
const r = _readStore(file); // re-read inside the lock (latest state)
|
|
581
599
|
if (!r.ok) {
|
|
582
600
|
// The existing store is unreadable. Writing a fresh single-entry store
|
|
@@ -594,16 +612,12 @@ export function saveStoredToken(serverUrl, record) {
|
|
|
594
612
|
|
|
595
613
|
export function deleteStoredToken(serverUrl) {
|
|
596
614
|
const file = tokenStorePath();
|
|
597
|
-
return withStoreLock(file, () => {
|
|
615
|
+
return _deps.withStoreLock(file, () => {
|
|
598
616
|
const store = loadTokenStore(); // re-read inside the lock (latest state)
|
|
599
617
|
const key = serverKey(serverUrl);
|
|
600
618
|
if (!(key in store)) return false;
|
|
601
619
|
delete store[key];
|
|
602
|
-
|
|
603
|
-
_writeStoreSecure(file, store);
|
|
604
|
-
} catch {
|
|
605
|
-
/* best-effort */
|
|
606
|
-
}
|
|
620
|
+
_writeStoreSecure(file, store);
|
|
607
621
|
return true;
|
|
608
622
|
});
|
|
609
623
|
}
|
|
@@ -385,6 +385,7 @@ export function generatePkgConfig(ctx) {
|
|
|
385
385
|
const assets = [
|
|
386
386
|
`${posixify(distDir)}/**/*`,
|
|
387
387
|
`${posixify(templatesDir)}/**/*`,
|
|
388
|
+
`${posixify(cliRoot)}/src/lib/process-execution-broker/*.ps1`,
|
|
388
389
|
];
|
|
389
390
|
if (prebuildsDir) assets.push(`${posixify(prebuildsDir)}/**/*`);
|
|
390
391
|
// Project mode: bundle the collected .chainlesschain/ snapshot as an asset.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the additive, protocol-safe PermissionDecision attached to a gated
|
|
3
|
+
* tool result. The runtime remains the authority; IDEs and SDKs only render
|
|
4
|
+
* this explanation and must never re-evaluate it.
|
|
5
|
+
*
|
|
6
|
+
* The source result can contain user-controlled rules/reasons. Keep the wire
|
|
7
|
+
* object bounded and run every text field through the export-grade secret
|
|
8
|
+
* redactor before it reaches transcripts, ledgers, or IDE logs.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { redactSecrets } from "./secret-scan.js";
|
|
12
|
+
|
|
13
|
+
export const PERMISSION_DECISION_VERSION = 1;
|
|
14
|
+
|
|
15
|
+
const MAX_TEXT = 512;
|
|
16
|
+
const MAX_CHAIN = 8;
|
|
17
|
+
|
|
18
|
+
function boundedText(value, max = MAX_TEXT) {
|
|
19
|
+
if (value == null) return null;
|
|
20
|
+
let text;
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
text = value;
|
|
23
|
+
} else {
|
|
24
|
+
try {
|
|
25
|
+
text = JSON.stringify(value);
|
|
26
|
+
} catch {
|
|
27
|
+
text = String(value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (typeof text !== "string") text = String(value);
|
|
31
|
+
text = redactSecrets(text);
|
|
32
|
+
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeChain(value) {
|
|
36
|
+
if (!Array.isArray(value)) return [];
|
|
37
|
+
return value.slice(0, MAX_CHAIN).map((entry) => {
|
|
38
|
+
const row = entry && typeof entry === "object" ? entry : {};
|
|
39
|
+
return {
|
|
40
|
+
layer: boundedText(row.layer, 80),
|
|
41
|
+
outcome: boundedText(row.outcome, 80),
|
|
42
|
+
via: boundedText(row.via || row.source, 120),
|
|
43
|
+
rule: boundedText(row.rule, 256),
|
|
44
|
+
reason: boundedText(row.reason),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {{
|
|
51
|
+
* toolUseId?: string,
|
|
52
|
+
* tool?: string,
|
|
53
|
+
* result?: object,
|
|
54
|
+
* }} input
|
|
55
|
+
* @returns {object|null}
|
|
56
|
+
*/
|
|
57
|
+
export function buildPermissionDecision(input = {}) {
|
|
58
|
+
const result =
|
|
59
|
+
input.result && typeof input.result === "object" ? input.result : {};
|
|
60
|
+
const policy =
|
|
61
|
+
result.policy && typeof result.policy === "object" ? result.policy : {};
|
|
62
|
+
const approval =
|
|
63
|
+
result.approval && typeof result.approval === "object"
|
|
64
|
+
? result.approval
|
|
65
|
+
: {};
|
|
66
|
+
const chain = normalizeChain(result.permissionChain);
|
|
67
|
+
const terminal = chain.length > 0 ? chain[chain.length - 1] : {};
|
|
68
|
+
|
|
69
|
+
const decision = boundedText(
|
|
70
|
+
policy.decision || approval.decision || terminal.outcome,
|
|
71
|
+
32,
|
|
72
|
+
);
|
|
73
|
+
if (!decision && chain.length === 0) return null;
|
|
74
|
+
|
|
75
|
+
const via = boundedText(
|
|
76
|
+
policy.via || approval.via || terminal.via || terminal.layer,
|
|
77
|
+
120,
|
|
78
|
+
);
|
|
79
|
+
const rule = boundedText(policy.rule || approval.rule || terminal.rule, 256);
|
|
80
|
+
const reason = boundedText(
|
|
81
|
+
policy.reason ||
|
|
82
|
+
approval.reason ||
|
|
83
|
+
terminal.reason ||
|
|
84
|
+
result.shellCommandPolicy?.reason,
|
|
85
|
+
);
|
|
86
|
+
const toolUseId = boundedText(input.toolUseId, 160);
|
|
87
|
+
const gate = via || decision || "policy";
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
version: PERMISSION_DECISION_VERSION,
|
|
91
|
+
id: toolUseId ? `${toolUseId}:perm:${gate}` : null,
|
|
92
|
+
tool: boundedText(input.tool, 120),
|
|
93
|
+
decision,
|
|
94
|
+
via,
|
|
95
|
+
rule,
|
|
96
|
+
reason,
|
|
97
|
+
chain,
|
|
98
|
+
};
|
|
99
|
+
}
|