chainlesschain 0.166.85 → 0.166.86
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 +8 -0
- package/package.json +3 -2
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cgo7OFpi.js → AIOps-DZwiXAIh.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-lUMnE2Ln.js → ActionButton-aNzXoIJP.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CsvKzTot.js → Analytics-ETQk4ZXt.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-C2z1h1Pv.js → AppLayout-D5oEbeEC.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-C5Q0ZhNb.js → Artifacts-ja48FAne.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DXnfPKsI.js → Audit-DlN0YnDt.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DjbGcLm3.js → BackgroundAgents-CzvyNsRg.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9bVrKvXE.js → Backup-DXgEC2MQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-bx5ri7BD.js → BaseInput-D9XqejLx.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CCFmeUMY.js → Chat-BTwuYUlN.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-0anIC2Wn.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-ei7MQXe9.js → Checkbox-CLuaQbDt.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CXgBJ9iu.js → Codegen-KwQUZs8A.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DMkMsizS.js → Col-BCmROkzc.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CvpB_UFR.js → Community-CXlUEHR4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-gay5hbFX.js → Compact-Dzx-lWI2.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-dG8JQyRL.js → Compliance-CfXW82FF.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BA3xfaTW.js → Cowork-DIISTRm2.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-D1_HsxyY.js → Cron-nyJ-KcFE.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DZqoqoy5.js → Crosschain-Dtv1E4cV.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Cwnr8hCO.js → DID-BCTpektS.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CxevypiI.js → Dashboard-C1Q_m0So.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Bs7-yWgr.js → Dropdown-BrQ5_PPL.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CcEs2Puo.js → EmailListRenderer-DjoNlONP.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-C9ipBzAQ.js → EvolutionSettings-DaGaE6bX.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dt6--mGn.js → FamilyGuardDashboard-nJDWBECt.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CD0JN5FF.js → Federation-DhMUY_wG.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-POypz3Xv.js → FormItemContext-CbONJPuc.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Dv9f-0F5.js → GenericCardRenderer-CDdX4dX-.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BbTh5Atv.js → Git-DXbDVJdR.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-C71dgoir.js → Governance-DbAfAvs1.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DwSHxbJB.js → Inference-Dy-fGea2.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cx1oQYQZ.js → KnowledgeGraph-DAxRpGUF.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BAJWe00o.js → Logs-5L1UCR8X.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-CD8a7lHT.js → MarkdownRenderer-u0FPafvT.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-fwuY7P7Y.js → Marketplace-DUk_McDG.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Di4693uc.js → McpTools-B6PNsZ_p.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-Cz3L-GUB.js → Memory-BUUs8j5F.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BuXKIkSb.js → MobileBridge-pXRsgyuO.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-D7v-2Hwm.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-LnACMFd0.js → Mtc-Deaj4faB.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BZy7yE0t.js → MtcAudit-wUV0VUH1.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BFG5iVrE.js → Multisig-C67IIkts.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DgXi04vJ.js → NLProgramming-BCA_CWrW.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BAj0Kgg4.js → Notes-D9eu14hQ.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-AixrsHw5.js → NotificationSettings-Cnd-J4LM.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dwqk3FlG.js → OrderTableRenderer-C4pHUuyZ.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-UNCBC3ux.js → Organization-4YExAbnp.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Drh8oAFT.js → Overflow-BF-wFuZz.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-bc2q0xEB.js → P2P-CdiajqCT.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-BGwKxcDM.js → PdhVaultBrowser-BkjfS6Th.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DOnUxUzu.js → Permissions-DrZ4D78_.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B3qPZkrr.js → PersonalDataHub-lDc6OXjz.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-B6hfa4-i.js → Pipeline-DqDS9ON1.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-Bun3c35F.js → Privacy-BMekZRZu.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CCObdzk9.js → ProjectInit-LYS7NFvJ.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C6lKrSXY.js → ProjectSettings-CcJNHk9A.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-DYcta7MZ.js → Projects-DrfAHgdF.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BtykxKCe.js → Providers-aqsly9SR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-D9CDyroY.js → QrScannerModal-ZWvR3zqg.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-CJauCxXx.js → QuickAsk-BT5CgsvW.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BbiYZofR.js → Recommend-Cz8Dz2RN.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BymCrgFW.js → RemoteSession-BlxPVbtQ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BWrGpMpF.js → Reputation-B3VGBE-1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-9pOYIhiW.js → Row-Bwv7vSEA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D3bQJ4qB.js → RssFeed-DHI085r_.js} +2 -2
- package/src/assets/web-panel/assets/{Search-BpKOOFE0.js → Search-DBler5T0.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DC6C0NSz.js → Security-BZdw43NW.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Lkf1046J.js → Services-Cslk5lUj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-eCQ_SsDb.js → Skeleton-Bur-C-25.js} +1 -1
- package/src/assets/web-panel/assets/Skills-v0zgLy0X.js +1 -0
- package/src/assets/web-panel/assets/{Sla-BQbKy8XJ.js → Sla-B1OWgQ51.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DHsRx-XA.js → SpeechSettings-CaBcEaV4.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CC281X_J.js → SyncSettings-CBbG8L6J.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-C7EjXrnN.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Cr2vC0tg.js → Templates-VBBOZ1H5.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DeYO5pIG.js → Tenant-CtQTj7IF.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBNgbfPr.js → Terminal-DXVTQjnl.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-MoxgaF3x.js → TimelineRenderer-BRIno0aN.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CT2QfAN7.js → Tokens-uiiAfkYN.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DPUnomxs.js → Trigger-CPApN8o6.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DnufuaTx.js → Trust-T2jKVwCE.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CJ6iOdPs.js → UkeySign-C2U_gbzP.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-xBWkH8bL.js → VideoEditing-BCdlr3Kj.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-WYV-4hMR.js → Wallet-CWnjoSc1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DJ5Mhd9L.js → WebAuthn-CqSbYqHq.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-B__UdOjN.js → WorkflowEditor-Dr_I3jcr.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CLlmBVhp.js → chat-Cg21FKdE.js} +1 -1
- package/src/assets/web-panel/assets/{colors-B8zoPQYm.js → colors-CK85tY9e.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CvJ-dUsH.js → compact-item-m-XYPC7W.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-LAtMm8SC.js → createContext-BmiEeNvs.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CkMEODFC.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CLYvg42H.js → hasIn-CCw4KAoc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B02qgCao.js → index-6jXf-Lln.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6zlMzZz.js → index-8KzhlvJ9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCzyJ1xS.js → index-B7xJkhtJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Di9vKira.js → index-B8IC9ZeZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfOOACfd.js → index-BGUYgc96.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDm9m4vC.js → index-BGb1j2z9.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX0aHEHL.js → index-BHsiSoGY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUVrYDNV.js → index-BIOTRzaF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUcE6rIa.js → index-BStyjmLG.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNQUdPam.js → index-BUYyhYP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CxJ4NPVq.js → index-BcfVHmFK.js} +1 -1
- package/src/assets/web-panel/assets/{index-4rr5jFOL.js → index-BiVPFsat.js} +1 -1
- package/src/assets/web-panel/assets/index-BjE5qaKn.js +1 -0
- package/src/assets/web-panel/assets/{index-C3uD5H7u.js → index-Br0ZNdu1.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5v7cEtm.js → index-Br9t_I1X.js} +1 -1
- package/src/assets/web-panel/assets/{index-DX99e8Xy.js → index-BtvVupUL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGrQ2uEl.js → index-Bv90WEWa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpda0SAz.js → index-BvjC3t35.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cv7vwX3z.js → index-ByVvAXdl.js} +1 -1
- package/src/assets/web-panel/assets/{index-D0Z13xcZ.js → index-CBcgIWOe.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYW4Z3y-.js → index-CN2xbLGz.js} +1 -1
- package/src/assets/web-panel/assets/{index-f1ptYBMr.js → index-CYEzWoyZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_dq-6_3.js → index-Cf5arJie.js} +1 -1
- package/src/assets/web-panel/assets/index-CsFH-2Zk.js +1 -0
- package/src/assets/web-panel/assets/{index-C2KFnUsv.js → index-D1sU5-XA.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_RPWP_5.js → index-D7gS_TJl.js} +1 -1
- package/src/assets/web-panel/assets/{index-iJ6s24bA.js → index-DAlGVix-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bu7-I2L4.js → index-DI9GJFcn.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6PETyel.js → index-DIlGxS2W.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVDcRQuX.js → index-DMbrl43d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkcQJvsz.js → index-DmSgfXuz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHq2Of6z.js → index-DpIy2HzE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM2HqHdT.js → index-DxRhnsEC.js} +1 -1
- package/src/assets/web-panel/assets/{index-GR3omApz.js → index-IskAKXhx.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1hHp9_M.js → index-J81uvUMN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpeV-6y4.js → index-OIbfsi-o.js} +1 -1
- package/src/assets/web-panel/assets/{index-oX216HRH.js → index-lhoexBDH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnLXLaEF.js → index-sWUmvT8H.js} +1 -1
- package/src/assets/web-panel/assets/{index-09YzRb6Z.js → index-yYmjkf38.js} +3 -3
- package/src/assets/web-panel/assets/{initDefaultProps-QD-koeaw.js → initDefaultProps-Cc1XOmNe.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CTItLW4-.js → motion-BehsX88u.js} +1 -1
- package/src/assets/web-panel/assets/{move-CGsdTOdN.js → move-BPx2YsqY.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C1kW_CNU.js → mtc-parser-BSFJgFQA.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B4PilaYy.js → omit-DBN5KUeo.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DBGUE12e.js → pickAttrs-BDK1yZ-9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DZxCLjuo.js → placementArrow-C4rMmKNJ.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CZjQiocN.js → responsiveObserve-BoeoOnxX.js} +1 -1
- package/src/assets/web-panel/assets/{slide-Btz5hh2_.js → slide-BKlr1bl9.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CfuxuLIt.js → statusUtils-Ky1apWPI.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DdO0Obo_.js → styleChecker-DxxR7uXk.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CxftJ-Zs.js → useFlexGapSupport-DmsUqVe9.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-pVJChh6K.js → useFs-CwkG7sOg.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-wv7Q0uNE.js → usePersonalDataHub-CddgeEfF.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DVOVfAqC.js → vnode-C_-H8i4Y.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-B5tebZuT.js → zoom-aAsHUMNE.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/data/changelog.json +13 -0
- package/src/lib/context-memory-kernel/durable-memory-port.js +5 -2
- package/src/lib/context-memory-kernel/index.js +1 -0
- package/src/lib/context-memory-kernel/memory-service.js +43 -16
- package/src/lib/context-memory-kernel/runtime.js +5 -3
- package/src/lib/context-memory-kernel/segmented-memory-port.js +687 -0
- package/src/lib/packer/pack-update-applier.js +13 -8
- package/src/lib/permission-authority.js +146 -10
- package/src/lib/sandbox-egress-authority-monitor.js +62 -5
- package/src/lib/scoped-permission-store.js +104 -1
- package/src/lib/settings-authority-domain.cjs +37 -2
- package/src/lib/settings-loader.cjs +67 -34
- package/src/lib/settings-permission-authority.cjs +373 -0
- package/src/runtime/agent-core.js +79 -383
- package/src/runtime/headless-runner.js +8 -3
- package/src/runtime/headless-stream.js +10 -3
- package/src/runtime/permission-authority-host.js +137 -0
- package/src/runtime/provider-stream-state.js +379 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-ClqsIfTT.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-Bk-lK4Um.js +0 -1
- package/src/assets/web-panel/assets/Skills-BvDSQ3-d.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CHHSWHom.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DgZ4fdGI.js +0 -1
- package/src/assets/web-panel/assets/index-jLEdASOV.js +0 -1
- package/src/assets/web-panel/assets/index-jesGo4rl.js +0 -1
|
@@ -146,9 +146,11 @@ function accrete(target, sources, arr, file, kind) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/** The ordered list of candidate settings files for a cwd. */
|
|
149
|
-
function settingsPaths(cwd, explicitFile) {
|
|
149
|
+
function settingsPaths(cwd, explicitFile, userSettingsFile = null) {
|
|
150
150
|
const home = _deps.homedir();
|
|
151
|
-
const list = [
|
|
151
|
+
const list = [
|
|
152
|
+
userSettingsFile || path.join(home, ".claude", "settings.json"),
|
|
153
|
+
];
|
|
152
154
|
// When run from a subdirectory, the project-root `.claude` sits BELOW cwd's
|
|
153
155
|
// own (closest wins) but ABOVE the user layer — so its rules apply yet a
|
|
154
156
|
// cwd-local settings file still overrides them. Null when cwd IS the root.
|
|
@@ -171,7 +173,7 @@ function settingsPaths(cwd, explicitFile) {
|
|
|
171
173
|
function inspectSettingsSources(opts = {}) {
|
|
172
174
|
const cwd = path.resolve(opts.cwd || process.cwd());
|
|
173
175
|
const candidates = [
|
|
174
|
-
...settingsPaths(cwd, opts.settingsFile),
|
|
176
|
+
...settingsPaths(cwd, opts.settingsFile, opts.userSettingsFile),
|
|
175
177
|
managedSettingsPath(opts),
|
|
176
178
|
];
|
|
177
179
|
const observation = Object.freeze({
|
|
@@ -390,7 +392,38 @@ function scopeFile(cwd, scope) {
|
|
|
390
392
|
* @returns {{ file:string, added:boolean }} added=false → already present.
|
|
391
393
|
* @throws if the target file exists but is malformed JSON (refuse to clobber).
|
|
392
394
|
*/
|
|
393
|
-
function
|
|
395
|
+
function appendPermissionRule(data, file, kind, rule) {
|
|
396
|
+
if (
|
|
397
|
+
Object.hasOwn(data, "permissions") &&
|
|
398
|
+
(!data.permissions ||
|
|
399
|
+
typeof data.permissions !== "object" ||
|
|
400
|
+
Array.isArray(data.permissions))
|
|
401
|
+
)
|
|
402
|
+
throw new TypeError(`refusing to overwrite malformed permissions: ${file}`);
|
|
403
|
+
data.permissions ??= {};
|
|
404
|
+
for (const decision of KINDS) {
|
|
405
|
+
if (
|
|
406
|
+
Object.hasOwn(data.permissions, decision) &&
|
|
407
|
+
(!Array.isArray(data.permissions[decision]) ||
|
|
408
|
+
data.permissions[decision].some((entry) => typeof entry !== "string"))
|
|
409
|
+
)
|
|
410
|
+
throw new TypeError(
|
|
411
|
+
`refusing to overwrite malformed permission rules: ${file}`,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
data.permissions[kind] ??= [];
|
|
415
|
+
if (data.permissions[kind].includes(rule)) return false;
|
|
416
|
+
data.permissions[kind].push(rule);
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function addRule({
|
|
421
|
+
cwd = process.cwd(),
|
|
422
|
+
kind,
|
|
423
|
+
rule,
|
|
424
|
+
scope = "project",
|
|
425
|
+
settingsAuthority = null,
|
|
426
|
+
} = {}) {
|
|
394
427
|
const entryCwd = process.cwd();
|
|
395
428
|
const absoluteCwd = path.resolve(entryCwd, cwd);
|
|
396
429
|
// Check before lock acquisition: notification callbacks may reenter here.
|
|
@@ -404,11 +437,38 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
|
|
|
404
437
|
if (!["project", "local", "user"].includes(scope)) {
|
|
405
438
|
throw new TypeError("scope must be project | local | user");
|
|
406
439
|
}
|
|
407
|
-
|
|
408
|
-
|
|
440
|
+
let file = path.resolve(entryCwd, scopeFile(absoluteCwd, scope));
|
|
441
|
+
if (settingsAuthority !== null) {
|
|
442
|
+
const authority = require("./settings-permission-authority.cjs");
|
|
443
|
+
const bound =
|
|
444
|
+
authority.settingsPermissionAuthorityOptions(settingsAuthority);
|
|
445
|
+
if (absoluteCwd !== bound.cwd)
|
|
446
|
+
throw new TypeError("settings authority cwd mismatch");
|
|
447
|
+
if (scope === "user") file = bound.userSettingsFile;
|
|
448
|
+
} else _deps.fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
409
449
|
let began = false;
|
|
410
450
|
let committed = false;
|
|
411
451
|
try {
|
|
452
|
+
if (settingsAuthority !== null) {
|
|
453
|
+
const authority = require("./settings-permission-authority.cjs");
|
|
454
|
+
const result = authority.mutateSettingsPermissionSource(
|
|
455
|
+
settingsAuthority,
|
|
456
|
+
{
|
|
457
|
+
logicalPath: file,
|
|
458
|
+
prepare: (data) => appendPermissionRule(data, file, kind, rule),
|
|
459
|
+
revokeLocal() {
|
|
460
|
+
beginSettingsMutation();
|
|
461
|
+
began = true;
|
|
462
|
+
},
|
|
463
|
+
replace(physicalPath, data) {
|
|
464
|
+
_deps.writeSecurityStore(physicalPath, "settings", data);
|
|
465
|
+
committed = true;
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
);
|
|
469
|
+
if (began) finishSettingsMutation("ready");
|
|
470
|
+
return { file, added: result.changed };
|
|
471
|
+
}
|
|
412
472
|
const result = _deps.withFileLock(
|
|
413
473
|
file,
|
|
414
474
|
() => {
|
|
@@ -423,35 +483,8 @@ function addRule({ cwd = process.cwd(), kind, rule, scope = "project" } = {}) {
|
|
|
423
483
|
);
|
|
424
484
|
}
|
|
425
485
|
const data = _deps.readSecurityStore(file, "settings");
|
|
426
|
-
if (
|
|
427
|
-
Object.hasOwn(data, "permissions") &&
|
|
428
|
-
(!data.permissions ||
|
|
429
|
-
typeof data.permissions !== "object" ||
|
|
430
|
-
Array.isArray(data.permissions))
|
|
431
|
-
) {
|
|
432
|
-
throw new TypeError(
|
|
433
|
-
`refusing to overwrite malformed permissions: ${file}`,
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
data.permissions ??= {};
|
|
437
|
-
// Refuse to hide a damaged rule list, including a different decision.
|
|
438
|
-
for (const decision of KINDS) {
|
|
439
|
-
if (
|
|
440
|
-
Object.hasOwn(data.permissions, decision) &&
|
|
441
|
-
(!Array.isArray(data.permissions[decision]) ||
|
|
442
|
-
data.permissions[decision].some(
|
|
443
|
-
(entry) => typeof entry !== "string",
|
|
444
|
-
))
|
|
445
|
-
) {
|
|
446
|
-
throw new TypeError(
|
|
447
|
-
`refusing to overwrite malformed permission rules: ${file}`,
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
data.permissions[kind] ??= [];
|
|
452
|
-
if (data.permissions[kind].includes(rule))
|
|
486
|
+
if (!appendPermissionRule(data, file, kind, rule))
|
|
453
487
|
return { file, added: false };
|
|
454
|
-
data.permissions[kind].push(rule);
|
|
455
488
|
beginSettingsMutation();
|
|
456
489
|
began = true;
|
|
457
490
|
_deps.writeSecurityStore(file, "settings", data);
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Trusted-host, cooperative Linux binding. Only explicit provisioning creates
|
|
4
|
+
// an authority; a reader/writer never registers or repairs a missing domain.
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { types } = require("node:util");
|
|
7
|
+
const { createHash } = require("node:crypto");
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const { observeSettingsSources } = require("./settings-source-observation.cjs");
|
|
10
|
+
const domain = require("./settings-authority-domain.cjs");
|
|
11
|
+
const records = require("./settings-authority-record.cjs");
|
|
12
|
+
const bindings = new WeakMap();
|
|
13
|
+
const SCHEMA = "chainlesschain.settings-permission-launch/v1";
|
|
14
|
+
const loader = () => require("./settings-loader.cjs");
|
|
15
|
+
|
|
16
|
+
function fail(code = "CC_SETTINGS_PERMISSION_BINDING_INVALID") {
|
|
17
|
+
throw Object.assign(
|
|
18
|
+
new Error("Persistent settings permission binding is unavailable"),
|
|
19
|
+
{ code },
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function dataCopy(input, depth = 0) {
|
|
24
|
+
if (input === null || ["string", "boolean", "number"].includes(typeof input))
|
|
25
|
+
return input;
|
|
26
|
+
if (!input || typeof input !== "object" || types.isProxy(input) || depth > 12)
|
|
27
|
+
fail();
|
|
28
|
+
const array = Array.isArray(input);
|
|
29
|
+
if (
|
|
30
|
+
!array &&
|
|
31
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(input))
|
|
32
|
+
)
|
|
33
|
+
fail();
|
|
34
|
+
const output = array ? [] : {};
|
|
35
|
+
const keys = Reflect.ownKeys(input);
|
|
36
|
+
if (keys.length > 1024) fail();
|
|
37
|
+
for (const key of keys) {
|
|
38
|
+
if (array && key === "length") continue;
|
|
39
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
40
|
+
if (
|
|
41
|
+
typeof key !== "string" ||
|
|
42
|
+
!descriptor ||
|
|
43
|
+
!Object.hasOwn(descriptor, "value") ||
|
|
44
|
+
key === "__proto__" ||
|
|
45
|
+
(array && !/^(0|[1-9][0-9]*)$/.test(key))
|
|
46
|
+
)
|
|
47
|
+
fail();
|
|
48
|
+
output[key] = dataCopy(descriptor.value, depth + 1);
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
array &&
|
|
52
|
+
(output.length !== input.length ||
|
|
53
|
+
Object.keys(output).length !== input.length)
|
|
54
|
+
)
|
|
55
|
+
fail();
|
|
56
|
+
return Object.freeze(output);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function exactKeys(value, keys) {
|
|
60
|
+
if (
|
|
61
|
+
!value ||
|
|
62
|
+
Array.isArray(value) ||
|
|
63
|
+
Object.keys(value).sort().join(",") !== [...keys].sort().join(",")
|
|
64
|
+
)
|
|
65
|
+
fail();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function absolute(value) {
|
|
69
|
+
if (
|
|
70
|
+
typeof value !== "string" ||
|
|
71
|
+
!value ||
|
|
72
|
+
!path.isAbsolute(value) ||
|
|
73
|
+
path.resolve(value) !== value
|
|
74
|
+
)
|
|
75
|
+
fail();
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function captureContexts(input, captured = false) {
|
|
80
|
+
const contexts = dataCopy(input);
|
|
81
|
+
if (!Array.isArray(contexts) || !contexts.length || contexts.length > 64)
|
|
82
|
+
fail();
|
|
83
|
+
const ids = new Set();
|
|
84
|
+
return Object.freeze(
|
|
85
|
+
contexts.map((context) => {
|
|
86
|
+
const fields = [
|
|
87
|
+
"contextId",
|
|
88
|
+
"cwd",
|
|
89
|
+
"settingsFile",
|
|
90
|
+
"managedSettingsFile",
|
|
91
|
+
"userSettingsFile",
|
|
92
|
+
"scopedFile",
|
|
93
|
+
];
|
|
94
|
+
if (captured) exactKeys(context, fields);
|
|
95
|
+
else if (Object.keys(context).some((key) => !fields.includes(key)))
|
|
96
|
+
fail();
|
|
97
|
+
if (
|
|
98
|
+
typeof context.contextId !== "string" ||
|
|
99
|
+
!/^[a-zA-Z0-9._:-]{1,128}$/.test(context.contextId) ||
|
|
100
|
+
ids.has(context.contextId)
|
|
101
|
+
)
|
|
102
|
+
fail();
|
|
103
|
+
ids.add(context.contextId);
|
|
104
|
+
const cwd = captured
|
|
105
|
+
? absolute(context.cwd)
|
|
106
|
+
: path.resolve(context.cwd || process.cwd());
|
|
107
|
+
const settingsFile =
|
|
108
|
+
context.settingsFile === null || context.settingsFile === undefined
|
|
109
|
+
? null
|
|
110
|
+
: captured
|
|
111
|
+
? absolute(context.settingsFile)
|
|
112
|
+
: path.resolve(cwd, context.settingsFile);
|
|
113
|
+
const managedSettingsFile = captured
|
|
114
|
+
? absolute(context.managedSettingsFile)
|
|
115
|
+
: loader().managedSettingsPath(context);
|
|
116
|
+
const userSettingsFile = captured
|
|
117
|
+
? absolute(context.userSettingsFile)
|
|
118
|
+
: context.userSettingsFile
|
|
119
|
+
? path.resolve(context.userSettingsFile)
|
|
120
|
+
: loader().scopeFile(cwd, "user");
|
|
121
|
+
const scopedFile = captured
|
|
122
|
+
? absolute(context.scopedFile)
|
|
123
|
+
: context.scopedFile
|
|
124
|
+
? path.resolve(context.scopedFile)
|
|
125
|
+
: require("./scoped-permission-store.js").defaultScopedPermissionStatePath(
|
|
126
|
+
cwd,
|
|
127
|
+
);
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
contextId: context.contextId,
|
|
130
|
+
cwd,
|
|
131
|
+
settingsFile,
|
|
132
|
+
managedSettingsFile: path.resolve(managedSettingsFile),
|
|
133
|
+
userSettingsFile: path.resolve(userSettingsFile),
|
|
134
|
+
scopedFile,
|
|
135
|
+
});
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function observe(contexts) {
|
|
141
|
+
const observations = new Map();
|
|
142
|
+
const manifests = contexts.map((context) => {
|
|
143
|
+
const observation = loader().inspectSettingsSources(context);
|
|
144
|
+
const scopedObservation = observeSettingsSources([context.scopedFile])[0];
|
|
145
|
+
observations.set(context.contextId, { observation, scopedObservation });
|
|
146
|
+
return records.createManifest({
|
|
147
|
+
contextId: context.contextId,
|
|
148
|
+
discovery: [
|
|
149
|
+
context.cwd,
|
|
150
|
+
context.userSettingsFile,
|
|
151
|
+
context.managedSettingsFile,
|
|
152
|
+
...(context.settingsFile ? [context.settingsFile] : []),
|
|
153
|
+
context.scopedFile,
|
|
154
|
+
],
|
|
155
|
+
sources: [...observation.sources, scopedObservation],
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
return { observations, manifests };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function observer(contexts, save = () => {}) {
|
|
162
|
+
return (expected) => {
|
|
163
|
+
if (
|
|
164
|
+
expected.length !== contexts.length ||
|
|
165
|
+
expected.some(
|
|
166
|
+
(context, index) => context.contextId !== contexts[index].contextId,
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
fail("CC_SETTINGS_AUTHORITY_BINDING_CHANGED");
|
|
170
|
+
// Recompute the complete discovery route, never reuse ledger source paths.
|
|
171
|
+
const current = observe(contexts);
|
|
172
|
+
save(current.observations);
|
|
173
|
+
return current.manifests;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Explicit administrator/host provisioning. Parent directories must exist. */
|
|
178
|
+
function initializeSettingsPermissionAuthority({
|
|
179
|
+
directory,
|
|
180
|
+
forbiddenRoots,
|
|
181
|
+
contexts,
|
|
182
|
+
}) {
|
|
183
|
+
const captured = captureContexts(contexts);
|
|
184
|
+
const initial = observe(captured);
|
|
185
|
+
const handle = domain.initializeSettingsAuthorityDomain({
|
|
186
|
+
directory,
|
|
187
|
+
forbiddenRoots: [
|
|
188
|
+
...new Set([
|
|
189
|
+
...forbiddenRoots,
|
|
190
|
+
...captured.map((context) => context.cwd),
|
|
191
|
+
]),
|
|
192
|
+
],
|
|
193
|
+
contexts: initial.manifests,
|
|
194
|
+
observeContexts: observer(captured),
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
return dataCopy({
|
|
198
|
+
schema: SCHEMA,
|
|
199
|
+
domain: domain.exportSettingsAuthorityDomain(handle),
|
|
200
|
+
contexts: captured,
|
|
201
|
+
});
|
|
202
|
+
} finally {
|
|
203
|
+
domain.closeSettingsAuthorityDomain(handle);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Reopen a fixed launch descriptor in a process or Worker; never bootstrap. */
|
|
208
|
+
function openSettingsPermissionAuthority({ launch, contextId }) {
|
|
209
|
+
launch = dataCopy(launch);
|
|
210
|
+
exactKeys(launch, ["schema", "domain", "contexts"]);
|
|
211
|
+
if (launch.schema !== SCHEMA) fail();
|
|
212
|
+
const contexts = captureContexts(launch.contexts, true);
|
|
213
|
+
const selected = contexts.find((context) => context.contextId === contextId);
|
|
214
|
+
if (!selected) fail("CC_SETTINGS_AUTHORITY_CONTEXT_UNREGISTERED");
|
|
215
|
+
const handle = domain.reopenSettingsAuthorityDomain({
|
|
216
|
+
descriptor: launch.domain,
|
|
217
|
+
});
|
|
218
|
+
const binding = Object.freeze({});
|
|
219
|
+
const state = { handle, launch, contexts, selected, closed: false };
|
|
220
|
+
bindings.set(binding, state);
|
|
221
|
+
try {
|
|
222
|
+
readSettingsPermissionAuthority(binding);
|
|
223
|
+
return binding;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
closeSettingsPermissionAuthority(binding);
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function stateFor(binding) {
|
|
231
|
+
const state = bindings.get(binding);
|
|
232
|
+
if (!state || state.closed) fail();
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function settingsPermissionAuthorityOptions(binding) {
|
|
237
|
+
return stateFor(binding).selected;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function exportSettingsPermissionAuthority(binding) {
|
|
241
|
+
const state = stateFor(binding);
|
|
242
|
+
domain.exportSettingsAuthorityDomain(state.handle); // Verify namespace first.
|
|
243
|
+
return state.launch;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function readSettingsPermissionAuthority(binding, { forUpdate = false } = {}) {
|
|
247
|
+
const state = stateFor(binding);
|
|
248
|
+
let observations;
|
|
249
|
+
const read = forUpdate
|
|
250
|
+
? domain.readSettingsAuthorityForUpdate
|
|
251
|
+
: domain.readSettingsAuthority;
|
|
252
|
+
const result = read(state.handle, {
|
|
253
|
+
observeContexts: observer(state.contexts, (value) => {
|
|
254
|
+
observations = value;
|
|
255
|
+
}),
|
|
256
|
+
localRevision: () => localRevision(state),
|
|
257
|
+
});
|
|
258
|
+
const observed = observations.get(state.selected.contextId);
|
|
259
|
+
return Object.freeze({
|
|
260
|
+
snapshot: result.snapshot,
|
|
261
|
+
observation: observed.observation,
|
|
262
|
+
scopedObservation: observed.scopedObservation,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function localRevision(state) {
|
|
267
|
+
const settings = loader().getSettingsPermissionRevision();
|
|
268
|
+
const scoped =
|
|
269
|
+
require("./scoped-permission-store.js").getScopedPermissionRevision();
|
|
270
|
+
if (
|
|
271
|
+
state.localRevision?.settings !== settings ||
|
|
272
|
+
state.localRevision?.scoped !== scoped
|
|
273
|
+
)
|
|
274
|
+
state.localRevision = Object.freeze({ settings, scoped });
|
|
275
|
+
return state.localRevision;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Keep the observation's minting and projection within one loader instance.
|
|
279
|
+
// Hosts with an ESM transform loader can also load CJS through native require;
|
|
280
|
+
// the native observation must still pass its original private WeakSet brand.
|
|
281
|
+
function projectSettingsPermissionObservation(observation, options) {
|
|
282
|
+
return loader().projectSettingsObservation(observation, options);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function assertSettingsPermissionWritableRoots(binding, roots) {
|
|
286
|
+
const state = stateFor(binding);
|
|
287
|
+
const directory = domain.exportSettingsAuthorityDomain(
|
|
288
|
+
state.handle,
|
|
289
|
+
).directory;
|
|
290
|
+
for (const root of roots) {
|
|
291
|
+
const canonical = fs.realpathSync(absolute(root));
|
|
292
|
+
const overlaps = [
|
|
293
|
+
path.relative(canonical, directory),
|
|
294
|
+
path.relative(directory, canonical),
|
|
295
|
+
].some(
|
|
296
|
+
(relative) =>
|
|
297
|
+
relative === "" ||
|
|
298
|
+
(!relative.startsWith(".." + path.sep) &&
|
|
299
|
+
relative !== ".." &&
|
|
300
|
+
!path.isAbsolute(relative)),
|
|
301
|
+
);
|
|
302
|
+
if (overlaps) fail("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Used only by the official synchronous settings writer. Callbacks remain a
|
|
307
|
+
// trusted host contract; no callback or arbitrary source enters launch data.
|
|
308
|
+
function mutateSettingsPermissionSource(
|
|
309
|
+
binding,
|
|
310
|
+
{ logicalPath, prepare, revokeLocal, replace },
|
|
311
|
+
) {
|
|
312
|
+
const state = stateFor(binding);
|
|
313
|
+
logicalPath = absolute(logicalPath);
|
|
314
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
315
|
+
const current = readSettingsPermissionAuthority(binding, {
|
|
316
|
+
forUpdate: true,
|
|
317
|
+
});
|
|
318
|
+
const source = [
|
|
319
|
+
...current.observation.sources,
|
|
320
|
+
current.scopedObservation,
|
|
321
|
+
].find((item) => item.logicalPath === logicalPath);
|
|
322
|
+
if (!source) fail("CC_SETTINGS_AUTHORITY_SOURCE_UNREGISTERED");
|
|
323
|
+
const data = JSON.parse(JSON.stringify(source.settings || {}));
|
|
324
|
+
if (prepare(data) === false)
|
|
325
|
+
return { changed: false, snapshot: current.snapshot };
|
|
326
|
+
const bytes = Buffer.from(`${JSON.stringify(data, null, 2)}\n`);
|
|
327
|
+
if (bytes.length > 1024 * 1024) fail("CC_SETTINGS_SOURCE_TOO_LARGE");
|
|
328
|
+
try {
|
|
329
|
+
return domain.transitionSettingsAuthority(state.handle, {
|
|
330
|
+
intent: {
|
|
331
|
+
kind: "write",
|
|
332
|
+
physicalPath: source.physicalPath,
|
|
333
|
+
after: {
|
|
334
|
+
exists: true,
|
|
335
|
+
byteLength: bytes.length,
|
|
336
|
+
digest: createHash("sha256").update(bytes).digest("hex"),
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
expectedSnapshot: current.snapshot,
|
|
340
|
+
localRevision: () => localRevision(state),
|
|
341
|
+
observeContexts: observer(state.contexts),
|
|
342
|
+
revokeLocal,
|
|
343
|
+
replace: () => replace(source.physicalPath, data),
|
|
344
|
+
});
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (
|
|
347
|
+
error.cause?.code !== "CC_SETTINGS_AUTHORITY_CONFLICT" ||
|
|
348
|
+
error.commitState !== "not-committed" ||
|
|
349
|
+
error.authorityState !== "unchanged"
|
|
350
|
+
)
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
fail("CC_SETTINGS_AUTHORITY_CONFLICT");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function closeSettingsPermissionAuthority(binding) {
|
|
358
|
+
const state = stateFor(binding);
|
|
359
|
+
domain.closeSettingsAuthorityDomain(state.handle);
|
|
360
|
+
state.closed = true;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module.exports = {
|
|
364
|
+
initializeSettingsPermissionAuthority,
|
|
365
|
+
openSettingsPermissionAuthority,
|
|
366
|
+
exportSettingsPermissionAuthority,
|
|
367
|
+
closeSettingsPermissionAuthority,
|
|
368
|
+
settingsPermissionAuthorityOptions,
|
|
369
|
+
readSettingsPermissionAuthority,
|
|
370
|
+
projectSettingsPermissionObservation,
|
|
371
|
+
mutateSettingsPermissionSource,
|
|
372
|
+
assertSettingsPermissionWritableRoots,
|
|
373
|
+
};
|