chainlesschain 0.165.1 → 0.165.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -2
- package/package.json +4 -1
- package/src/assets/web-panel/assets/{AIOps-CYxFDrcm.js → AIOps-lS1R0Cza.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CPBtdiU2.js → ActionButton-BmlgBPsj.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Dayq7Dp7.js → Analytics-CNKczv1S.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-6U7bPb2s.js → AppLayout-BuZGJARQ.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-D2Iqg_3D.js → Artifacts-Dm73Sl-O.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CdoOB7WV.js → Audit-BGMW7A5e.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DrnAVB8w.js → BackgroundAgents-BQ6M54JR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CesMi5cy.js → Backup-DBzGP0bR.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DaX9ptax.js → BaseInput-D1mLCoOV.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-OrwOPxDS.js → Chat-BUfcN7p8.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CvPZFRxM.js → ChatBubbleRenderer-CY5m8ZHg.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-DiHbcysC.js → Checkbox-Du0Yg5Pi.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CbgCkaBJ.js → Codegen-B2RXziHW.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Du5hgrh4.js → Col-DWhekOrR.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DsPOBW-p.js → Community-HQhrkon4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BwHIFkNC.js → Compact-izHNb4uz.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BvrDQSgd.js → Compliance-BDwLy41-.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-gwAE6arx.js → Cowork-DFJhLa7K.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DW-0l4uF.js → Cron-DYY9RG7-.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-j-NsLesB.js → Crosschain-rr7tJTtv.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CibVEBOI.js → DID-D2sfHRLi.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-W4z9vSfe.js → Dashboard-q_eGdhha.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DAcrkKxG.js → Dropdown-Cufrh2Bz.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-6GXah-89.js → EmailListRenderer-B-kdv10f.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-TKDCq8Ku.js → FamilyGuardDashboard-D21XjgfG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BXBLlcNm.js → Federation-CAwRTLZH.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BoJJBkG3.js → FormItemContext-vx3daNDF.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-okjsl46v.js → GenericCardRenderer-CRWY2wU4.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Rvs7i6nn.js → Git-Q6SwauyD.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CHSUxXXS.js → Governance-BJ1q4IQZ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-V3BlfxsB.js → Inference-CSWjROgD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C_NlD4aR.js → KnowledgeGraph-fzuQsWuY.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Nn4LNxhA.js → Logs-DVF8Mnns.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BLif4QSg.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-DEmjFcg-.js → Marketplace-BQP7LOA4.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DlopXipI.js → McpTools-BNlJCmNc.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-BG3ynX60.js → Memory-DLLg5WQ4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DeRzWOfT.js → MobileBridge-CR2skNAF.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Tyd3o2A0.js → MobileProjects-BxVavObl.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BoPbOv1C.js → Mtc-B17pgqMI.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-un-rTHp7.js → MtcAudit-4UmV5cIL.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-Ds0oveEB.js → Multisig-DoMEXEcU.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-D09JI-yY.js → NLProgramming-h9DMXFX4.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DTRhiKJn.js → Notes-Cihn-xX8.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BAnnNCim.js → NotificationSettings-BE6ZGa3R.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Yr-1fSYi.js → OrderTableRenderer-CnwojoSI.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-S9EAolpj.js → Organization-B2vpwmjb.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CqcLKqJ8.js → Overflow-C4z28R0-.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Dw0F9EcU.js → P2P-BVlFkwhW.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BDRwUevN.js → PdhVaultBrowser-BXbEdL5B.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-D3_c3nKB.js → Permissions-C28xs4lp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CxdMV98x.js → PersonalDataHub-Dgl_rBjM.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-CgjNQFLI.js → Pipeline-3QUIhrKI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BHghIIht.js → Privacy-8Gh33OZ5.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-De6pqaKM.js → ProjectInit-BNmg4tZi.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Bg4ZkNYQ.js → ProjectSettings-vgMfeCf9.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BhtAVilc.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CUnHbNf-.js → Providers-CXoOa8eX.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DA2f3Wts.js → QrScannerModal-BhfTF0oS.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-C1FeHDqp.js → QuickAsk-C746HEAj.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-B5guaUMN.js → Recommend-BSej8Eo0.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-PDADSds3.js → RemoteSession-f3gmnlG3.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-eTlRhgvK.js → Reputation-CyvybpkN.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D7AqSGY0.js → Row-DwV2PeYn.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-JiuLm4Ja.js → RssFeed-11GrzjZy.js} +3 -3
- package/src/assets/web-panel/assets/{Search-CwruahW3.js → Search-BjKXkrAY.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CdhqaR0b.js → Security-eWiFb9Yk.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CU0ZIlcT.js → Services-CdCsMAtt.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CtduPfAb.js → Skeleton-4Q2UTKQ5.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CROXm6AY.js → Skills-v4rtYrRj.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DiZiABFD.js → Sla-CRhsFvd_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DLjKsDVu.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-DBX6J1Oe.js → SyncSettings-D16zonl7.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BLyRJd3-.js → Tasks-Xxozz0i_.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BKa5qzmk.js → Templates-CXJpPtCT.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DnD3a4M6.js → Tenant-XRV0L1ZF.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DEitQeTb.js → Terminal-CYuO1i_w.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CjHggTDq.js → TimelineRenderer-BXqitWsu.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CsGT7sfl.js → Tokens-BvPrDHIy.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-yr4rFDrW.js → Trigger-DhCrSTf5.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CLvu1Sjp.js → Trust-DmsuZD6K.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B0m7yUP8.js → UkeySign-D_yrts5C.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C0VvP4lc.js → VideoEditing-B01ihPrm.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-kllBAG1M.js → Wallet-DpUjKa-N.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-C54UQNLx.js → WebAuthn-CL07yON6.js} +3 -3
- package/src/assets/web-panel/assets/{WorkflowEditor-B7y8qSvU.js → WorkflowEditor-CLMflnRr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DRzTh8CJ.js → chat-DtUqe8h_.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BSMwSnCx.js → colors-DPKrgVfy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BTg602H5.js → compact-item-BTt2GtOm.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B6EGsdvp.js → createContext-D_VeUc1J.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Bj5nUW2v.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DDKqJ8RD.js → hasIn-1kndiylj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUEU48o1.js → index-2eyAXE3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-rjzk9z1Q.js → index-4bna2_SR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmAubjDi.js → index-8nz_fmlj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Co7oM1qr.js → index-B9KvcS7R.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMuGd2--.js → index-BAXfRKfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVTAl5N2.js → index-BHpBMWCh.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpTHIRfZ.js → index-BaZAbw9A.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWGax2QW.js → index-BepLZwgB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiW0sDkz.js → index-Bhudc_Ez.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGNklbP6.js → index-BlDNVgBt.js} +1 -1
- package/src/assets/web-panel/assets/{index-u2YVld9W.js → index-By5n8up9.js} +1 -1
- package/src/assets/web-panel/assets/{index-pqfux8vZ.js → index-CHG-KcS4.js} +1 -1
- package/src/assets/web-panel/assets/index-CPcM-eqo.js +1 -0
- package/src/assets/web-panel/assets/{index-BRqt4fO-.js → index-CX81qSM4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsR7Y2NG.js → index-CauoBQkH.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_JzIXUq.js → index-CcO04eDn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQlHFN_g.js → index-CtF7EN49.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwPUZbMI.js → index-CzjkKUmR.js} +1 -1
- package/src/assets/web-panel/assets/{index-th2ihDc0.js → index-D95OFht7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHfLYwcW.js → index-DFIZ_1Sf.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1GFfuqN.js → index-DG2MZQ3f.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1naNj4U.js → index-DJ8vXGOa.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLdrHhML.js → index-Dbbymxpj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBT3YeQ9.js → index-DdkplL9R.js} +1 -1
- package/src/assets/web-panel/assets/{index-BICn3Qh1.js → index-DfGl8CqG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DqFXGzzC.js → index-DfKBRdSS.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bx044qDj.js → index-DsJCDUJG.js} +1 -1
- package/src/assets/web-panel/assets/{index-XFe-1twF.js → index-Dwqmtk65.js} +1 -1
- package/src/assets/web-panel/assets/{index-C66NpWy5.js → index-DxdTdBix.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmAInUkX.js → index-FzdwcSy2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf23wVMs.js → index-GsoUQ2M7.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5CsBShd.js → index-brCInrzq.js} +1 -1
- package/src/assets/web-panel/assets/{index-323Hhxxt.js → index-dVc-CXY4.js} +1 -1
- package/src/assets/web-panel/assets/{index-p2HYn89m.js → index-fk2Rjacw.js} +3 -3
- package/src/assets/web-panel/assets/{index-Dfv2lKu3.js → index-l1BETrDb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNxg61YP.js → index-m31Ka8hZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDTvsX8t.js → index-ocf06EVQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dc77VR3H.js → index-sDoMHTNA.js} +1 -1
- package/src/assets/web-panel/assets/index-xeSyVoRe.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-C3Djh9fa.js → initDefaultProps-CvyYJd7z.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BzqFmbSe.js → motion-DYZkhUdz.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJo_FFaW.js → move-slnJRVT1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-_tlTiBvk.js → mtc-parser-DVc0CKmy.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CB2ZyxUg.js → omit-BCXAtHCq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DZwIFD4u.js → pickAttrs-DBsemz_K.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DxZY0dgk.js → placementArrow-BRtkJ-nv.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CzzGt7GL.js → responsiveObserve-Cebq7EZn.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BaIK52yz.js → slide-DT7qvId2.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-3-FHu9_M.js → statusUtils-pQiITLkG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BkRT68fK.js → styleChecker-c-19BVKQ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DHYwKS-s.js → useFlexGapSupport-DsS5-0bE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DVVUhV8p.js → useFs-CxGiMe5n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CkrFKAzQ.js → usePersonalDataHub-D9btVID5.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BuBLFz0c.js → vnode-CUYFsFEN.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-kpm6LtXU.js → zoom-CXmn_35j.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/cowork.js +280 -5
- package/src/commands/plugin.js +819 -64
- package/src/commands/session-location.js +318 -7
- package/src/constants.js +2 -2
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/action-protocol.js +1 -1
- package/src/harness/jsonl-session-store.js +638 -2
- package/src/harness/prompt-compressor.js +4 -0
- package/src/lib/background-agent-keeper-client.js +29 -0
- package/src/lib/background-agent-keeper-protocol.js +4 -0
- package/src/lib/background-agent-supervisor.js +59 -29
- package/src/lib/cowork-task-runner.js +70 -0
- package/src/lib/cowork-workflow.js +42 -9
- package/src/lib/doctor-checkup.js +34 -10
- package/src/lib/dynamic-workflow-draft.js +535 -0
- package/src/lib/dynamic-workflow-facade.js +11 -11
- package/src/lib/dynamic-workflow-runtime.js +2974 -0
- package/src/lib/execution-location-contract.js +111 -7
- package/src/lib/execution-location-runtime.js +3 -1
- package/src/lib/execution-location-target.js +910 -0
- package/src/lib/llm-providers.js +1 -0
- package/src/lib/mcp-call-ledger-store.js +35 -0
- package/src/lib/mcp-call-ledger.js +30 -0
- package/src/lib/plugin-runtime/__tests__/remote-source.test.js +34 -16
- package/src/lib/plugin-runtime/install.js +4956 -657
- package/src/lib/plugin-runtime/manifest.js +3 -1
- package/src/lib/plugin-runtime/marketplace-artifact-readback.js +671 -31
- package/src/lib/plugin-runtime/marketplace-catalog.js +68 -2
- package/src/lib/plugin-runtime/marketplace-impact.js +208 -15
- package/src/lib/plugin-runtime/marketplace-network.js +317 -0
- package/src/lib/plugin-runtime/marketplace-pac-worker.js +15 -0
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +25 -2
- package/src/lib/plugin-runtime/marketplace-source-cache.js +359 -0
- package/src/lib/plugin-runtime/policy.js +21 -6
- package/src/lib/plugin-runtime/publisher-trust.js +332 -0
- package/src/lib/plugin-runtime/remote-source.js +323 -27
- package/src/lib/plugin-runtime/scopes.js +317 -41
- package/src/lib/plugin-runtime/transaction-journal.js +806 -0
- package/src/lib/public-ide-capability-manifest.js +1 -1
- package/src/lib/remote-membership-coordinator.js +8 -6
- package/src/lib/sub-agent-context.js +268 -2
- package/src/lib/task-model-selector.js +1 -1
- package/src/lib/with-file-lock.js +95 -5
- package/src/runtime/agent-core.js +595 -41
- package/src/workers/background-agent-keeper.js +40 -10
- package/src/assets/web-panel/assets/MarkdownRenderer-SVAgNnkt.js +0 -1
- package/src/assets/web-panel/assets/Projects-CP8vPvzs.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-B5pLcXUw.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +0 -1
- package/src/assets/web-panel/assets/index-C3QX5tOl.js +0 -1
- package/src/assets/web-panel/assets/index-xYybvEic.js +0 -1
|
@@ -28,6 +28,7 @@ import os from "os";
|
|
|
28
28
|
import path from "path";
|
|
29
29
|
import crypto from "crypto";
|
|
30
30
|
import { getElectronUserDataDir } from "../paths.js";
|
|
31
|
+
import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
|
|
31
32
|
|
|
32
33
|
export const _deps = {
|
|
33
34
|
// Node 22+ global fetch; injectable for tests.
|
|
@@ -36,9 +37,21 @@ export const _deps = {
|
|
|
36
37
|
writeFileSync: fs.writeFileSync,
|
|
37
38
|
existsSync: fs.existsSync,
|
|
38
39
|
mkdirSync: fs.mkdirSync,
|
|
40
|
+
lstatSync: fs.lstatSync,
|
|
41
|
+
readdirSync: fs.readdirSync,
|
|
42
|
+
openSync: fs.openSync,
|
|
43
|
+
fstatSync: fs.fstatSync,
|
|
44
|
+
readSync: fs.readSync,
|
|
45
|
+
closeSync: fs.closeSync,
|
|
46
|
+
fsyncSync: fs.fsyncSync,
|
|
47
|
+
linkSync: fs.linkSync,
|
|
48
|
+
unlinkSync: fs.unlinkSync,
|
|
39
49
|
};
|
|
40
50
|
|
|
41
51
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
52
|
+
export const MAX_REGISTRY_DOCUMENT_BYTES = 4 * 1024 * 1024;
|
|
53
|
+
export const MAX_REGISTRY_CACHE_CANDIDATES = 64;
|
|
54
|
+
const SHA256_RE = /^[a-f0-9]{64}$/;
|
|
42
55
|
|
|
43
56
|
/** True when `raw` looks like a remote registry/manifest URL (http(s) + .json). */
|
|
44
57
|
export function isRemoteSource(raw) {
|
|
@@ -79,8 +92,8 @@ export function assertRegistryUrlSafe(url, { allowInsecure = false } = {}) {
|
|
|
79
92
|
);
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
/**
|
|
83
|
-
export function
|
|
95
|
+
/** Directory for immutable registry documents, addressed by the complete URL. */
|
|
96
|
+
export function registryCacheDirectory(url, cacheDir) {
|
|
84
97
|
const dir =
|
|
85
98
|
cacheDir || path.join(getElectronUserDataDir(), "plugin-registry-cache");
|
|
86
99
|
const hash = crypto
|
|
@@ -88,7 +101,16 @@ export function registryCachePath(url, cacheDir) {
|
|
|
88
101
|
.update(String(url))
|
|
89
102
|
.digest("hex")
|
|
90
103
|
.slice(0, 32);
|
|
91
|
-
return path.join(dir,
|
|
104
|
+
return path.join(dir, hash);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Path for one immutable registry document bound to its SHA-256 digest. */
|
|
108
|
+
export function registryCachePath(url, cacheDir, documentSha256) {
|
|
109
|
+
const digest = normalizeRegistrySha256(
|
|
110
|
+
documentSha256,
|
|
111
|
+
"registry document SHA-256",
|
|
112
|
+
);
|
|
113
|
+
return path.join(registryCacheDirectory(url, cacheDir), `${digest}.json`);
|
|
92
114
|
}
|
|
93
115
|
|
|
94
116
|
/**
|
|
@@ -112,12 +134,13 @@ export function resolveRegistryToken(url, { token, config } = {}) {
|
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
/**
|
|
115
|
-
* GET a registry/manifest URL as JSON, with optional bearer auth. On
|
|
116
|
-
* body is written to
|
|
117
|
-
*
|
|
118
|
-
* only when
|
|
137
|
+
* GET a registry/manifest URL as bounded JSON, with optional bearer auth. On
|
|
138
|
+
* success the body is written to an immutable content-addressed cache. Explicit
|
|
139
|
+
* offline mode never attempts network access. An unpinned cache read is allowed
|
|
140
|
+
* only when exactly one valid historical document exists for the URL.
|
|
119
141
|
*
|
|
120
|
-
* @returns {Promise<{ registry: object, fromCache: boolean
|
|
142
|
+
* @returns {Promise<{ registry: object, fromCache: boolean,
|
|
143
|
+
* documentSha256: string }>}
|
|
121
144
|
*/
|
|
122
145
|
export async function fetchRegistry(url, opts = {}) {
|
|
123
146
|
const {
|
|
@@ -125,19 +148,47 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
125
148
|
cacheDir,
|
|
126
149
|
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
127
150
|
allowCache = true,
|
|
151
|
+
offline = false,
|
|
128
152
|
} = opts;
|
|
129
153
|
assertRegistryUrlSafe(url, { allowInsecure: opts.allowInsecure });
|
|
130
|
-
const
|
|
154
|
+
const expectedSha256 = normalizeRegistrySha256(
|
|
155
|
+
opts.expectedSha256,
|
|
156
|
+
"expected registry document SHA-256",
|
|
157
|
+
{ optional: true },
|
|
158
|
+
);
|
|
131
159
|
const headers = { Accept: "application/json" };
|
|
132
160
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
133
161
|
|
|
162
|
+
if (offline === true) {
|
|
163
|
+
if (!allowCache) {
|
|
164
|
+
throw new Error("offline registry mode requires immutable cache access");
|
|
165
|
+
}
|
|
166
|
+
const cached = readCachedRegistry({
|
|
167
|
+
url,
|
|
168
|
+
cacheDir,
|
|
169
|
+
expectedSha256,
|
|
170
|
+
});
|
|
171
|
+
if (cached) return cached;
|
|
172
|
+
throw new Error(
|
|
173
|
+
`registry ${url} is unavailable in the verified immutable cache` +
|
|
174
|
+
(expectedSha256 ? ` at SHA-256 ${expectedSha256}` : ""),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const networkTransport = createMarketplaceNetworkTransport({
|
|
179
|
+
proxyUrl: opts.proxyUrl,
|
|
180
|
+
pacFile: opts.pacFile,
|
|
181
|
+
caFile: opts.caFile,
|
|
182
|
+
});
|
|
183
|
+
const fetchImpl = networkTransport?.fetch || _deps.fetch;
|
|
134
184
|
let networkErr = null;
|
|
185
|
+
let cacheFallbackAllowed = false;
|
|
135
186
|
try {
|
|
136
187
|
const controller = new AbortController();
|
|
137
188
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
138
189
|
let res;
|
|
139
190
|
try {
|
|
140
|
-
res = await
|
|
191
|
+
res = await fetchImpl(url, {
|
|
141
192
|
headers,
|
|
142
193
|
redirect: "follow",
|
|
143
194
|
signal: controller.signal,
|
|
@@ -146,43 +197,282 @@ export async function fetchRegistry(url, opts = {}) {
|
|
|
146
197
|
clearTimeout(timer);
|
|
147
198
|
}
|
|
148
199
|
if (!res.ok) {
|
|
149
|
-
|
|
200
|
+
const error = new Error(
|
|
150
201
|
`registry fetch failed: HTTP ${res.status} ${res.statusText || ""}`.trim(),
|
|
151
202
|
);
|
|
203
|
+
error.cacheFallbackAllowed = Number(res.status) >= 500;
|
|
204
|
+
throw error;
|
|
152
205
|
}
|
|
153
|
-
const
|
|
206
|
+
const bytes = await readBoundedRegistryResponse(res);
|
|
207
|
+
const documentSha256 = sha256(bytes);
|
|
208
|
+
if (expectedSha256 && documentSha256 !== expectedSha256) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`registry document SHA-256 mismatch: expected ${expectedSha256}, got ${documentSha256}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
const text = decodeRegistryUtf8(bytes);
|
|
154
214
|
const registry = validateRegistry(JSON.parse(text), url);
|
|
155
|
-
if (allowCache)
|
|
156
|
-
return {
|
|
215
|
+
if (allowCache) writeImmutableCache(url, cacheDir, bytes, documentSha256);
|
|
216
|
+
return {
|
|
217
|
+
registry,
|
|
218
|
+
fromCache: false,
|
|
219
|
+
documentSha256,
|
|
220
|
+
...(networkTransport?.authority
|
|
221
|
+
? { networkAuthority: networkTransport.authority }
|
|
222
|
+
: {}),
|
|
223
|
+
};
|
|
157
224
|
} catch (err) {
|
|
158
225
|
networkErr = err;
|
|
226
|
+
cacheFallbackAllowed =
|
|
227
|
+
err?.cacheFallbackAllowed === true ||
|
|
228
|
+
err?.name === "AbortError" ||
|
|
229
|
+
err instanceof TypeError;
|
|
230
|
+
} finally {
|
|
231
|
+
await networkTransport?.close();
|
|
159
232
|
}
|
|
160
233
|
|
|
161
|
-
//
|
|
162
|
-
|
|
234
|
+
// Only transport failures and server-side outages may fall back. Authentication,
|
|
235
|
+
// authorization, malformed content, and digest mismatches remain authoritative.
|
|
236
|
+
if (allowCache && cacheFallbackAllowed) {
|
|
163
237
|
try {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
238
|
+
const cached = readCachedRegistry({
|
|
239
|
+
url,
|
|
240
|
+
cacheDir,
|
|
241
|
+
expectedSha256,
|
|
242
|
+
});
|
|
243
|
+
if (cached) return cached;
|
|
244
|
+
} catch (cacheError) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`could not fetch registry ${url}: ${networkErr.message}; verified immutable cache rejected: ${cacheError.message}`,
|
|
247
|
+
);
|
|
169
248
|
}
|
|
170
249
|
}
|
|
171
250
|
throw new Error(
|
|
172
251
|
`could not fetch registry ${url}: ${networkErr ? networkErr.message : "unknown error"}` +
|
|
173
|
-
(allowCache
|
|
252
|
+
(allowCache && cacheFallbackAllowed
|
|
253
|
+
? " (no verified immutable cache available)"
|
|
254
|
+
: ""),
|
|
174
255
|
);
|
|
175
256
|
}
|
|
176
257
|
|
|
177
|
-
function
|
|
258
|
+
async function readBoundedRegistryResponse(response) {
|
|
259
|
+
const contentLength = Number(response?.headers?.get?.("content-length"));
|
|
260
|
+
if (
|
|
261
|
+
Number.isFinite(contentLength) &&
|
|
262
|
+
contentLength > MAX_REGISTRY_DOCUMENT_BYTES
|
|
263
|
+
) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`registry document exceeds ${MAX_REGISTRY_DOCUMENT_BYTES} bytes`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
if (response?.body?.getReader) {
|
|
269
|
+
const reader = response.body.getReader();
|
|
270
|
+
const chunks = [];
|
|
271
|
+
let total = 0;
|
|
272
|
+
try {
|
|
273
|
+
while (true) {
|
|
274
|
+
const { done, value } = await reader.read();
|
|
275
|
+
if (done) break;
|
|
276
|
+
const chunk = Buffer.from(value);
|
|
277
|
+
total += chunk.length;
|
|
278
|
+
if (total > MAX_REGISTRY_DOCUMENT_BYTES) {
|
|
279
|
+
await reader.cancel();
|
|
280
|
+
throw new Error(
|
|
281
|
+
`registry document exceeds ${MAX_REGISTRY_DOCUMENT_BYTES} bytes`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
chunks.push(chunk);
|
|
285
|
+
}
|
|
286
|
+
return Buffer.concat(chunks, total);
|
|
287
|
+
} finally {
|
|
288
|
+
reader.releaseLock?.();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const text = await response.text();
|
|
292
|
+
const bytes = Buffer.from(text, "utf8");
|
|
293
|
+
if (bytes.length > MAX_REGISTRY_DOCUMENT_BYTES) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`registry document exceeds ${MAX_REGISTRY_DOCUMENT_BYTES} bytes`,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return bytes;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function writeImmutableCache(url, cacheDir, bytes, digest) {
|
|
302
|
+
const cachePath = registryCachePath(url, cacheDir, digest);
|
|
303
|
+
const directory = path.dirname(cachePath);
|
|
304
|
+
let tempPath = null;
|
|
305
|
+
let descriptor = null;
|
|
178
306
|
try {
|
|
179
|
-
_deps.mkdirSync(
|
|
180
|
-
|
|
307
|
+
_deps.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
308
|
+
assertCacheDirectorySafe(directory);
|
|
309
|
+
tempPath = path.join(
|
|
310
|
+
directory,
|
|
311
|
+
`.tmp-${process.pid}-${crypto.randomBytes(12).toString("hex")}`,
|
|
312
|
+
);
|
|
313
|
+
descriptor = _deps.openSync(tempPath, "wx", 0o600);
|
|
314
|
+
_deps.writeFileSync(descriptor, bytes);
|
|
315
|
+
_deps.fsyncSync(descriptor);
|
|
316
|
+
_deps.closeSync(descriptor);
|
|
317
|
+
descriptor = null;
|
|
318
|
+
try {
|
|
319
|
+
_deps.linkSync(tempPath, cachePath);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error?.code !== "EEXIST") throw error;
|
|
322
|
+
const existing = readBoundedRegularFile(cachePath);
|
|
323
|
+
if (sha256(existing) !== digest || !existing.equals(bytes)) throw error;
|
|
324
|
+
}
|
|
181
325
|
} catch {
|
|
182
|
-
/* caching
|
|
326
|
+
/* caching remains best-effort; verification never trusts a partial write */
|
|
327
|
+
} finally {
|
|
328
|
+
if (descriptor != null) {
|
|
329
|
+
try {
|
|
330
|
+
_deps.closeSync(descriptor);
|
|
331
|
+
} catch {
|
|
332
|
+
/* best-effort cleanup */
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (tempPath) {
|
|
336
|
+
try {
|
|
337
|
+
_deps.unlinkSync(tempPath);
|
|
338
|
+
} catch {
|
|
339
|
+
/* hidden temporary files are never cache candidates */
|
|
340
|
+
}
|
|
341
|
+
}
|
|
183
342
|
}
|
|
184
343
|
}
|
|
185
344
|
|
|
345
|
+
function readCachedRegistry({ url, cacheDir, expectedSha256 }) {
|
|
346
|
+
const directory = registryCacheDirectory(url, cacheDir);
|
|
347
|
+
if (!_deps.existsSync(directory)) return null;
|
|
348
|
+
assertCacheDirectorySafe(directory);
|
|
349
|
+
const names = expectedSha256
|
|
350
|
+
? [`${expectedSha256}.json`]
|
|
351
|
+
: _deps
|
|
352
|
+
.readdirSync(directory, { withFileTypes: true })
|
|
353
|
+
.filter(
|
|
354
|
+
(entry) => entry.isFile() && /^[a-f0-9]{64}\.json$/.test(entry.name),
|
|
355
|
+
)
|
|
356
|
+
.map((entry) => entry.name)
|
|
357
|
+
.sort();
|
|
358
|
+
if (names.length > MAX_REGISTRY_CACHE_CANDIDATES) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`registry immutable cache has more than ${MAX_REGISTRY_CACHE_CANDIDATES} candidates`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
const matches = [];
|
|
364
|
+
for (const name of names) {
|
|
365
|
+
const digest = name.slice(0, 64);
|
|
366
|
+
const cachePath = path.join(directory, name);
|
|
367
|
+
if (!_deps.existsSync(cachePath)) continue;
|
|
368
|
+
try {
|
|
369
|
+
const bytes = readBoundedRegularFile(cachePath);
|
|
370
|
+
if (sha256(bytes) !== digest) {
|
|
371
|
+
throw new Error("registry immutable cache digest mismatch");
|
|
372
|
+
}
|
|
373
|
+
const registry = validateRegistry(
|
|
374
|
+
JSON.parse(decodeRegistryUtf8(bytes)),
|
|
375
|
+
url,
|
|
376
|
+
);
|
|
377
|
+
matches.push({
|
|
378
|
+
registry,
|
|
379
|
+
fromCache: true,
|
|
380
|
+
documentSha256: digest,
|
|
381
|
+
});
|
|
382
|
+
} catch (error) {
|
|
383
|
+
if (expectedSha256) throw error;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (matches.length > 1) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
"registry immutable cache is ambiguous; pin the expected registry document SHA-256",
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return matches[0] || null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function assertCacheDirectorySafe(directory) {
|
|
395
|
+
const stat = _deps.lstatSync(directory);
|
|
396
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
397
|
+
throw new Error("registry immutable cache path is not a directory");
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function readBoundedRegularFile(file) {
|
|
402
|
+
let descriptor = null;
|
|
403
|
+
try {
|
|
404
|
+
const linkStat = _deps.lstatSync(file);
|
|
405
|
+
if (
|
|
406
|
+
!linkStat.isFile() ||
|
|
407
|
+
linkStat.isSymbolicLink() ||
|
|
408
|
+
linkStat.nlink !== 1 ||
|
|
409
|
+
linkStat.size <= 0 ||
|
|
410
|
+
linkStat.size > MAX_REGISTRY_DOCUMENT_BYTES
|
|
411
|
+
) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
"registry immutable cache entry is not a bounded single-link regular file",
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
descriptor = _deps.openSync(
|
|
417
|
+
file,
|
|
418
|
+
fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0),
|
|
419
|
+
);
|
|
420
|
+
const fileStat = _deps.fstatSync(descriptor);
|
|
421
|
+
if (
|
|
422
|
+
!fileStat.isFile() ||
|
|
423
|
+
fileStat.nlink !== 1 ||
|
|
424
|
+
fileStat.size <= 0 ||
|
|
425
|
+
fileStat.size > MAX_REGISTRY_DOCUMENT_BYTES
|
|
426
|
+
) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
"registry immutable cache entry changed during inspection",
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const chunks = [];
|
|
432
|
+
let total = 0;
|
|
433
|
+
while (total <= MAX_REGISTRY_DOCUMENT_BYTES) {
|
|
434
|
+
const chunk = Buffer.allocUnsafe(
|
|
435
|
+
Math.min(64 * 1024, MAX_REGISTRY_DOCUMENT_BYTES + 1 - total),
|
|
436
|
+
);
|
|
437
|
+
const count = _deps.readSync(descriptor, chunk, 0, chunk.length, null);
|
|
438
|
+
if (count === 0) break;
|
|
439
|
+
chunks.push(chunk.subarray(0, count));
|
|
440
|
+
total += count;
|
|
441
|
+
}
|
|
442
|
+
if (total > MAX_REGISTRY_DOCUMENT_BYTES) {
|
|
443
|
+
throw new Error(
|
|
444
|
+
`registry immutable cache entry exceeds ${MAX_REGISTRY_DOCUMENT_BYTES} bytes`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return Buffer.concat(chunks, total);
|
|
448
|
+
} finally {
|
|
449
|
+
if (descriptor != null) _deps.closeSync(descriptor);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function decodeRegistryUtf8(bytes) {
|
|
454
|
+
try {
|
|
455
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
456
|
+
} catch {
|
|
457
|
+
throw new Error("registry document is not valid UTF-8");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function normalizeRegistrySha256(value, label, { optional = false } = {}) {
|
|
462
|
+
if ((value == null || value === "") && optional) return null;
|
|
463
|
+
const digest = String(value || "")
|
|
464
|
+
.trim()
|
|
465
|
+
.toLowerCase();
|
|
466
|
+
if (!SHA256_RE.test(digest)) {
|
|
467
|
+
throw new Error(`${label} must be 64 lowercase hexadecimal characters`);
|
|
468
|
+
}
|
|
469
|
+
return digest;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function sha256(value) {
|
|
473
|
+
return crypto.createHash("sha256").update(value).digest("hex");
|
|
474
|
+
}
|
|
475
|
+
|
|
186
476
|
/** Validate + normalize a fetched document into a registry object. Throws on garbage. */
|
|
187
477
|
export function validateRegistry(doc, url = "") {
|
|
188
478
|
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
|
|
@@ -254,7 +544,11 @@ export function resolvePluginEntry(registry, name) {
|
|
|
254
544
|
*/
|
|
255
545
|
export async function resolveRemoteSource(url, opts = {}) {
|
|
256
546
|
const token = resolveRegistryToken(url, opts);
|
|
257
|
-
const { registry, fromCache
|
|
547
|
+
const { registry, fromCache, documentSha256, networkAuthority } =
|
|
548
|
+
await fetchRegistry(url, {
|
|
549
|
+
...opts,
|
|
550
|
+
token,
|
|
551
|
+
});
|
|
258
552
|
const entry = resolvePluginEntry(registry, opts.name);
|
|
259
553
|
// Carry a `#ref` on the source string so installFromSource pins the checkout,
|
|
260
554
|
// matching its existing `owner/repo#ref` / `url#ref` convention.
|
|
@@ -265,6 +559,8 @@ export async function resolveRemoteSource(url, opts = {}) {
|
|
|
265
559
|
sha256: entry.sha256 || null,
|
|
266
560
|
entry,
|
|
267
561
|
fromCache,
|
|
562
|
+
documentSha256,
|
|
563
|
+
...(networkAuthority ? { networkAuthority } : {}),
|
|
268
564
|
};
|
|
269
565
|
}
|
|
270
566
|
|