@remnic/core 9.54.4 → 9.54.6
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/dist/access-admin-ops-surface.d.ts +24 -23
- package/dist/access-admin-ops-surface.js +55 -50
- package/dist/access-authorization-probe.d.ts +25 -22
- package/dist/access-authorization-probe.js +58 -51
- package/dist/access-boundary.d.ts +26 -23
- package/dist/access-boundary.js +59 -52
- package/dist/access-cli.js +115 -107
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +19 -18
- package/dist/access-extraction-force-flush.js +55 -50
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +22 -21
- package/dist/access-http-lifecycle-flush.d.ts +22 -21
- package/dist/access-http-offline-stream.d.ts +19 -18
- package/dist/access-http-query.js +55 -50
- package/dist/access-http.d.ts +55 -36
- package/dist/access-http.js +76 -65
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +55 -50
- package/dist/access-lcm-surface.d.ts +21 -20
- package/dist/access-lcm-surface.js +55 -50
- package/dist/access-mcp.d.ts +22 -21
- package/dist/access-mcp.js +63 -57
- package/dist/access-memory-search-fanout.d.ts +22 -2
- package/dist/access-memory-search-fanout.js +1 -1
- package/dist/{access-namespace-preflight-C7n4tgWC.d.ts → access-namespace-preflight-CCNn_6NE.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +55 -50
- package/dist/access-observe-write-surface.d.ts +22 -21
- package/dist/access-observe-write-surface.js +55 -50
- package/dist/access-offline-manifest.d.ts +10 -9
- package/dist/access-offline-manifest.js +12 -10
- package/dist/access-operations-batch.js +57 -52
- package/dist/access-operations.d.ts +23 -22
- package/dist/access-operations.js +61 -55
- package/dist/access-recall-concurrency.d.ts +21 -20
- package/dist/access-recall-concurrency.js +55 -50
- package/dist/access-recall-response.d.ts +19 -18
- package/dist/access-recall-response.js +55 -50
- package/dist/access-recall-surface.d.ts +19 -18
- package/dist/access-recall-surface.js +55 -50
- package/dist/access-schema.d.ts +2 -2
- package/dist/access-schema.js +5 -4
- package/dist/{access-service-CXvbxLK-.d.ts → access-service-DkSdXK4S.d.ts} +1314 -83
- package/dist/access-service-helpers.d.ts +22 -21
- package/dist/access-service.d.ts +31 -30
- package/dist/access-service.js +55 -50
- package/dist/access-surface-catalog.d.ts +22 -21
- package/dist/access-surface-catalog.js +209 -2
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +14 -1
- package/dist/active-memory-bridge.js +6 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +4 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/admin-console/public/what-helps-me/app.js +1594 -0
- package/dist/admin-console/public/what-helps-me/index.html +339 -0
- package/dist/admin-console/public/what-helps-me/model.js +685 -0
- package/dist/admin-console/public/what-helps-me/what-helps-me.css +1296 -0
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-TDHLZPVP.js → auto-sync-NR3GPCOA.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +33 -12
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +3 -3
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-BOOxl-I2.d.ts → catalog-DGFf8mD9.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +34 -13
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-ZYLC7NFO.js → chunk-26ZXBTYN.js} +4 -4
- package/dist/chunk-26ZXBTYN.js.map +1 -0
- package/dist/{chunk-LLJMAULE.js → chunk-2FKEIPIT.js} +5 -5
- package/dist/{chunk-3CSX3ETE.js → chunk-2LWQSV6A.js} +23 -11
- package/dist/chunk-2LWQSV6A.js.map +1 -0
- package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
- package/dist/chunk-37DZZFFM.js +502 -0
- package/dist/chunk-37DZZFFM.js.map +1 -0
- package/dist/chunk-3HSRWJTC.js +9 -0
- package/dist/chunk-3HSRWJTC.js.map +1 -0
- package/dist/{chunk-XUAMCLY4.js → chunk-3TIZPSJC.js} +17 -13
- package/dist/chunk-3TIZPSJC.js.map +1 -0
- package/dist/{chunk-PDDTI7IC.js → chunk-43SEBY2M.js} +1 -1
- package/dist/{chunk-HWRD23DW.js → chunk-46SZBVDS.js} +3 -3
- package/dist/{chunk-6HPHEFXT.js → chunk-5FD7JMTX.js} +2 -2
- package/dist/{chunk-7BZOBA5J.js → chunk-5NB726GV.js} +70 -16
- package/dist/chunk-5NB726GV.js.map +1 -0
- package/dist/chunk-62OKGIJA.js +39 -0
- package/dist/chunk-62OKGIJA.js.map +1 -0
- package/dist/{chunk-IBKR2TFR.js → chunk-6CYHUWCO.js} +2 -2
- package/dist/{chunk-YN6ASZUG.js → chunk-6QPI2ITJ.js} +5 -5
- package/dist/{chunk-UU3G6GUP.js → chunk-76JJSXK7.js} +1 -1
- package/dist/chunk-76JJSXK7.js.map +1 -0
- package/dist/{chunk-PX2LSHYM.js → chunk-7BD6O7ZV.js} +189 -4
- package/dist/chunk-7BD6O7ZV.js.map +1 -0
- package/dist/chunk-7TBXNYLO.js +346 -0
- package/dist/chunk-7TBXNYLO.js.map +1 -0
- package/dist/{chunk-NH5IR2WM.js → chunk-BRR5EUTF.js} +6 -2
- package/dist/chunk-BRR5EUTF.js.map +1 -0
- package/dist/{chunk-NIS256NW.js → chunk-CIAI24TQ.js} +3 -3
- package/dist/{chunk-T2JHZWH6.js → chunk-D3LBIUWV.js} +11703 -7573
- package/dist/chunk-D3LBIUWV.js.map +1 -0
- package/dist/{chunk-7FGL6CGA.js → chunk-DVYRD4HY.js} +2 -2
- package/dist/{chunk-G7Z6EENO.js → chunk-EKTP7MNS.js} +4 -4
- package/dist/{chunk-S75M5ZRK.js → chunk-F2JRDPON.js} +16 -1
- package/dist/chunk-F2JRDPON.js.map +1 -0
- package/dist/{chunk-RZ4XGKOJ.js → chunk-FFRTNF3L.js} +8 -8
- package/dist/{chunk-VKXWK44Q.js → chunk-FONWBCVY.js} +3 -3
- package/dist/{chunk-DZD576ZU.js → chunk-FPLM2TKD.js} +2 -2
- package/dist/{chunk-ZPJ4QSGP.js → chunk-FTPONF6B.js} +2 -2
- package/dist/{chunk-LJS7KUPV.js → chunk-FWX7W5PM.js} +2 -2
- package/dist/{chunk-UTRO5UJU.js → chunk-G6DFSS6A.js} +5 -1
- package/dist/chunk-G6DFSS6A.js.map +1 -0
- package/dist/{chunk-6LAPLZOS.js → chunk-GJ776BC2.js} +8 -3
- package/dist/chunk-GJ776BC2.js.map +1 -0
- package/dist/{chunk-G4T7L557.js → chunk-GN3WRFJY.js} +6 -6
- package/dist/{chunk-QT2FRYI4.js → chunk-H7NAZ3UB.js} +2 -2
- package/dist/{chunk-APPFNGX6.js → chunk-HBUFQRIM.js} +2 -2
- package/dist/{chunk-UU7TRFNE.js → chunk-HGJ6NJ4E.js} +2 -2
- package/dist/{chunk-WB7JSQCP.js → chunk-HIEWDJ2O.js} +5 -2
- package/dist/{chunk-WB7JSQCP.js.map → chunk-HIEWDJ2O.js.map} +1 -1
- package/dist/{chunk-UES4BHA2.js → chunk-I3MSGFGD.js} +17 -6
- package/dist/chunk-I3MSGFGD.js.map +1 -0
- package/dist/{chunk-C4ZNGVK3.js → chunk-IKP5OYQQ.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-RDJRFDJY.js → chunk-IQ4NVWEF.js} +2 -2
- package/dist/{chunk-CGVHPCP2.js → chunk-JYG4OLWH.js} +2 -2
- package/dist/{chunk-WOPA3SGI.js → chunk-K26NWEOC.js} +8 -8
- package/dist/{chunk-K4QS7LXU.js → chunk-K2UGPTYY.js} +485 -44
- package/dist/chunk-K2UGPTYY.js.map +1 -0
- package/dist/{chunk-L6CQ7WIW.js → chunk-KFBOPMF6.js} +11 -11
- package/dist/chunk-KFBOPMF6.js.map +1 -0
- package/dist/{chunk-G7A5HLUS.js → chunk-KJH6W2I7.js} +2 -2
- package/dist/{chunk-IXEBUYO4.js → chunk-KM55SMLG.js} +11 -2
- package/dist/{chunk-IXEBUYO4.js.map → chunk-KM55SMLG.js.map} +1 -1
- package/dist/{chunk-HOIRB77A.js → chunk-L6Z5OAS4.js} +2 -2
- package/dist/chunk-LC2BDILZ.js +57 -0
- package/dist/chunk-LC2BDILZ.js.map +1 -0
- package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
- package/dist/{chunk-6R4G7N4S.js → chunk-M4YHAODQ.js} +388 -748
- package/dist/chunk-M4YHAODQ.js.map +1 -0
- package/dist/{chunk-3JLPGJ43.js → chunk-MIH3CJPN.js} +5 -5
- package/dist/{chunk-QHFV6VNU.js → chunk-MK5VEWCR.js} +2 -2
- package/dist/chunk-NISZQW72.js +140 -0
- package/dist/chunk-NISZQW72.js.map +1 -0
- package/dist/{chunk-GUKBW4HL.js → chunk-NQMSEB5D.js} +6 -6
- package/dist/{chunk-G2LUMAUF.js → chunk-OCIP6YMD.js} +12 -5
- package/dist/chunk-OCIP6YMD.js.map +1 -0
- package/dist/{chunk-47P3RYZP.js → chunk-PG4QBPVG.js} +21 -11
- package/dist/chunk-PG4QBPVG.js.map +1 -0
- package/dist/{chunk-WRJVB3C2.js → chunk-PO7NXBG2.js} +2 -2
- package/dist/{chunk-QWGE2PUH.js → chunk-PT54WISF.js} +190 -56
- package/dist/chunk-PT54WISF.js.map +1 -0
- package/dist/{chunk-AZM4CCAC.js → chunk-QZLDRQ4I.js} +8 -8
- package/dist/{chunk-THPPCISA.js → chunk-RBII7637.js} +5 -5
- package/dist/{chunk-JDZPNCMA.js → chunk-RUNORG2P.js} +6 -3
- package/dist/chunk-RUNORG2P.js.map +1 -0
- package/dist/{chunk-JV5YUBIZ.js → chunk-RZQ6W2W4.js} +50 -44
- package/dist/chunk-RZQ6W2W4.js.map +1 -0
- package/dist/{chunk-RUMNY5DT.js → chunk-SMNYAMJD.js} +6 -2
- package/dist/chunk-SMNYAMJD.js.map +1 -0
- package/dist/{chunk-MKQBXQKU.js → chunk-TB6CBZ5V.js} +22 -15
- package/dist/chunk-TB6CBZ5V.js.map +1 -0
- package/dist/{chunk-VMMQDEN5.js → chunk-TO7ZTWJW.js} +2 -2
- package/dist/{chunk-K2TDK7GG.js → chunk-TWTUGCTY.js} +2 -2
- package/dist/{chunk-HLYR2TQL.js → chunk-UCTS3DDH.js} +2 -2
- package/dist/{chunk-JYIJNNN3.js → chunk-UG6VXIOK.js} +2 -2
- package/dist/{chunk-WM65LU67.js → chunk-UKYS3NU5.js} +2 -2
- package/dist/{chunk-WDBX6VER.js → chunk-UPLZNJKN.js} +5 -5
- package/dist/{chunk-C23BYTJV.js → chunk-UW5NPZJ2.js} +40 -12
- package/dist/chunk-UW5NPZJ2.js.map +1 -0
- package/dist/{chunk-2GFK22OZ.js → chunk-V6ERBR7U.js} +2 -2
- package/dist/{chunk-CYF6WVE7.js → chunk-W5ZKPI34.js} +139 -158
- package/dist/chunk-W5ZKPI34.js.map +1 -0
- package/dist/chunk-XX4HPFFM.js +370 -0
- package/dist/chunk-XX4HPFFM.js.map +1 -0
- package/dist/{chunk-ZMYDYNI4.js → chunk-XZHE2DEN.js} +4 -4
- package/dist/chunk-YXQEKCZM.js +223 -0
- package/dist/chunk-YXQEKCZM.js.map +1 -0
- package/dist/{chunk-GYPQL6XN.js → chunk-Z4N7SVVF.js} +35 -47
- package/dist/chunk-Z4N7SVVF.js.map +1 -0
- package/dist/{chunk-MAXVH44E.js → chunk-Z54EM5YZ.js} +7 -7
- package/dist/{chunk-YGLY6QFV.js → chunk-ZRZVLB7O.js} +2 -2
- package/dist/{chunk-4IIFL7GC.js → chunk-ZUKIUXHQ.js} +10 -10
- package/dist/{cli-DVUFIa5E.d.ts → cli-Bze8S0Qs.d.ts} +5 -5
- package/dist/cli.d.ts +22 -21
- package/dist/cli.js +90 -80
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +33 -12
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +4 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +33 -12
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +32 -12
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +3 -1
- package/dist/contradiction-scan-JDTBXRGW.js +20 -0
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +35 -14
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +25 -24
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-XzF76C9n.d.ts → dependency-propagation-delivery-CzEfB31P.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +33 -12
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +7 -4
- package/dist/external-wiki-access.d.ts +22 -21
- package/dist/external-wiki-access.js +56 -51
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +22 -21
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +2 -2
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-judge.js +2 -2
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +35 -19
- package/dist/fallback-llm.d.ts +14 -1
- package/dist/fallback-llm.js +1 -1
- package/dist/{first-start-migration-626C5554.js → first-start-migration-CRZCZAJN.js} +11 -3
- package/dist/first-start-migration-CRZCZAJN.js.map +1 -0
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +314 -308
- package/dist/index.js +304 -133
- package/dist/inline-script.d.ts +3 -0
- package/dist/inline-script.js +8 -0
- package/dist/inline-script.js.map +1 -0
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +6 -6
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +2 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +33 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +33 -12
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +34 -13
- package/dist/{maintenance-BJzNIKBE.d.ts → maintenance-C8aRWfhq.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +22 -21
- package/dist/mcp-read-only-tools.js +1 -1
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-mutations.js +2 -2
- package/dist/memory-projection-store.d.ts +6 -2
- package/dist/memory-projection-store.js +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +24 -23
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +8 -8
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +33 -12
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-file-io.d.ts +20 -0
- package/dist/offline-sync-file-io.js +18 -0
- package/dist/offline-sync-file-io.js.map +1 -0
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +5 -5
- package/dist/offline-sync.d.ts +8 -6
- package/dist/offline-sync.js +2 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +26 -25
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +39 -36
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +38 -18
- package/dist/{orchestrator-BiXgBJBQ.d.ts → orchestrator-D85CzDDY.d.ts} +11 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +117 -109
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-sz_dcuLg.d.ts → pipeline-DPcUifCO.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/public-http-D-SyWNj6.d.ts +25 -0
- package/dist/{purge-533FZLS4.js → purge-GB7PZHT2.js} +2 -2
- package/dist/{qmd-eAWObK4M.d.ts → qmd-dFCHp1r0.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/manifest.js +6 -5
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/reconcile/plan.js +3 -2
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +5 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +38 -38
- package/dist/{scope-profiles-BBQsXuyj.d.ts → scope-profiles-DICK7GtN.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +6 -6
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +12 -12
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-HD8RkzVA.d.ts → semantic-consolidation-BP7NAX0N.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +34 -13
- package/dist/semantic-rule-promotion.js +33 -12
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +33 -12
- package/dist/{service-DIrtHHEg.d.ts → service-DT-pJyda.d.ts} +660 -660
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +31 -15
- package/dist/{storage-DqCzXi56.d.ts → storage-qOnaJ1Lw.d.ts} +49 -13
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +33 -12
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +5 -5
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +518 -0
- package/dist/support-passport/index.js +357 -0
- package/dist/support-passport/index.js.map +1 -0
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +2 -2
- package/dist/tier-routing.js +7 -1
- package/dist/{tier-stats-2JCZYEF7.js → tier-stats-EXYSKSVN.js} +8 -2
- package/dist/{tier-stats-2JCZYEF7.js.map → tier-stats-EXYSKSVN.js.map} +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +36 -36
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-d_j4vC6N.d.ts → types-ed-SvS2t.d.ts} +8 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -2
- package/dist/verified-recall.js +33 -12
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-rate-limiter.d.ts +11 -5
- package/dist/write-rate-limiter.js +1 -1
- package/package.json +12 -2
- package/scripts/build-with-heap.mjs +11 -1
- package/src/access-authorization-probe.ts +20 -2
- package/src/access-boundary.ts +32 -0
- package/src/access-http.test.ts +1971 -643
- package/src/access-http.ts +81 -74
- package/src/access-mcp.ts +3 -1
- package/src/access-memory-search-fanout.ts +24 -1
- package/src/access-offline-manifest.ts +5 -0
- package/src/access-operations.ts +2 -0
- package/src/access-service-namespace.test.ts +14 -0
- package/src/access-service-observe-scope.test.ts +2 -0
- package/src/access-service-offline-file-content.test.ts +211 -0
- package/src/access-service.ts +628 -884
- package/src/access-surface-catalog.test.ts +40 -10
- package/src/access-surface-catalog.ts +15 -0
- package/src/active-memory-bridge.test.ts +151 -0
- package/src/active-memory-bridge.ts +95 -21
- package/src/briefing.test.ts +38 -0
- package/src/briefing.ts +3 -3
- package/src/cli.ts +6 -1
- package/src/config.ts +18 -9
- package/src/connectors/codex-materialize-runner.ts +2 -0
- package/src/contradiction/contradiction-scan.ts +2 -0
- package/src/contradiction/contradiction.test.ts +42 -0
- package/src/correction/correction-access-wiring.ts +8 -2
- package/src/correction/correction-storage-integration.test.ts +7 -0
- package/src/explicit-capture.test.ts +24 -0
- package/src/explicit-capture.ts +2 -0
- package/src/fallback-llm.test.ts +54 -0
- package/src/fallback-llm.ts +54 -9
- package/src/index.ts +3 -0
- package/src/inline-script.ts +6 -0
- package/src/local-llm-headers-timeout.test.ts +42 -0
- package/src/local-llm.ts +8 -8
- package/src/maintenance/first-start-migration.ts +2 -1
- package/src/maintenance/memory-governance.test.ts +25 -0
- package/src/maintenance/memory-governance.ts +4 -1
- package/src/maintenance/pattern-reinforcement.test.ts +20 -0
- package/src/maintenance/pattern-reinforcement.ts +2 -1
- package/src/maintenance/rebuild-memory-projection.ts +8 -8
- package/src/mcp-read-only-tools.ts +3 -0
- package/src/memory-projection-store.ts +25 -27
- package/src/offline-sync-file-io.ts +68 -0
- package/src/offline-sync.ts +43 -57
- package/src/orchestration/consolidation-run.ts +5 -4
- package/src/orchestration/contradiction-linking-coordinator.ts +7 -0
- package/src/orchestration/contradiction-localization.integration.test.ts +115 -0
- package/src/orchestration/lifecycle-policy-coordinator.ts +2 -1
- package/src/orchestration/qmd-result-resolver.test.ts +215 -14
- package/src/orchestration/qmd-result-resolver.ts +142 -82
- package/src/orchestration/recall-search-pipeline.test.ts +41 -0
- package/src/orchestration/recall-search-pipeline.ts +12 -0
- package/src/orchestration/semantic-consolidation-coordinator.ts +2 -1
- package/src/orchestration/tier-migration-coordinator.ts +23 -56
- package/src/orchestration/workspace-ops.test.ts +37 -0
- package/src/orchestration/workspace-ops.ts +5 -29
- package/src/orchestrator.ts +7 -8
- package/src/relay/mission-access.test.ts +11 -11
- package/src/relay/mission-ui.test.ts +3 -1
- package/src/storage/supersession-audit.ts +7 -1
- package/src/storage/supersession-side-effects.ts +17 -5
- package/src/storage/tombstone-blocked-capture-sync.ts +31 -0
- package/src/storage-contract/failure-semantics.test.ts +102 -0
- package/src/storage.ts +18 -19
- package/src/support-passport/access-http-base.ts +143 -0
- package/src/support-passport/access-http.ts +172 -0
- package/src/support-passport/access-operations.ts +272 -0
- package/src/support-passport/access-service-base.test.ts +193 -0
- package/src/support-passport/access-service-base.ts +176 -0
- package/src/support-passport/access-surface.test.ts +283 -0
- package/src/support-passport/access-surface.ts +286 -0
- package/src/support-passport/card-projection.test.ts +137 -0
- package/src/support-passport/card-projection.ts +348 -0
- package/src/support-passport/card-replacement.ts +126 -0
- package/src/support-passport/card-service-commit-forwarding.test.ts +160 -0
- package/src/support-passport/card-service.test.ts +2964 -0
- package/src/support-passport/card-service.ts +1184 -0
- package/src/support-passport/card-state.ts +158 -0
- package/src/support-passport/config-model-routing.test.ts +37 -0
- package/src/support-passport/config.test.ts +34 -0
- package/src/support-passport/config.ts +41 -0
- package/src/support-passport/contracts.test.ts +156 -0
- package/src/support-passport/contracts.ts +149 -0
- package/src/support-passport/errors.ts +30 -0
- package/src/support-passport/generated-batch.test.ts +185 -0
- package/src/support-passport/generated-batch.ts +338 -0
- package/src/support-passport/grant-contracts.ts +183 -0
- package/src/support-passport/grant-generated-batch-scope.test.ts +201 -0
- package/src/support-passport/grant-service-cache.test.ts +148 -0
- package/src/support-passport/grant-service-cancellation.test.ts +83 -0
- package/src/support-passport/grant-service-marker-cache.test.ts +62 -0
- package/src/support-passport/grant-service.test.ts +3496 -0
- package/src/support-passport/grant-service.ts +513 -0
- package/src/support-passport/grant-store-commit-hook.test.ts +123 -0
- package/src/support-passport/grant-store-order.test.ts +60 -0
- package/src/support-passport/grant-store-path-safety.test.ts +30 -0
- package/src/support-passport/grant-store-recovery.test.ts +448 -0
- package/src/support-passport/grant-store.ts +906 -0
- package/src/support-passport/index.ts +17 -0
- package/src/support-passport/mcp-tools.test.ts +222 -0
- package/src/support-passport/mcp-tools.ts +187 -0
- package/src/support-passport/model-adapter.test.ts +840 -0
- package/src/support-passport/model-adapter.ts +602 -0
- package/src/support-passport/model-audit-pinned-write.test.ts +59 -0
- package/src/support-passport/model-audit.ts +152 -0
- package/src/support-passport/model-bridge.test.ts +456 -0
- package/src/support-passport/model-bridge.ts +439 -0
- package/src/support-passport/model-contracts.ts +124 -0
- package/src/support-passport/model-service.test.ts +1731 -0
- package/src/support-passport/model-service.ts +399 -0
- package/src/support-passport/offline-sync-guard.ts +230 -0
- package/src/support-passport/owner-http.test.ts +671 -0
- package/src/support-passport/owner-lock.ts +70 -0
- package/src/support-passport/private-file.test.ts +99 -0
- package/src/support-passport/private-file.ts +498 -0
- package/src/support-passport/public-http.test.ts +476 -0
- package/src/support-passport/public-http.ts +601 -0
- package/src/support-passport/ui.test.ts +181 -0
- package/src/tier-routing.ts +28 -17
- package/src/training-export/converter.test.ts +24 -0
- package/src/training-export/converter.ts +2 -0
- package/src/types.ts +8 -1
- package/src/wearables/service.test.ts +13 -0
- package/src/wearables/service.ts +2 -0
- package/src/write-rate-limiter.test.ts +30 -6
- package/src/write-rate-limiter.ts +29 -18
- package/dist/chunk-3CSX3ETE.js.map +0 -1
- package/dist/chunk-47P3RYZP.js.map +0 -1
- package/dist/chunk-6LAPLZOS.js.map +0 -1
- package/dist/chunk-6R4G7N4S.js.map +0 -1
- package/dist/chunk-7BZOBA5J.js.map +0 -1
- package/dist/chunk-C23BYTJV.js.map +0 -1
- package/dist/chunk-CYF6WVE7.js.map +0 -1
- package/dist/chunk-G2LUMAUF.js.map +0 -1
- package/dist/chunk-GYPQL6XN.js.map +0 -1
- package/dist/chunk-JDZPNCMA.js.map +0 -1
- package/dist/chunk-JV5YUBIZ.js.map +0 -1
- package/dist/chunk-K4QS7LXU.js.map +0 -1
- package/dist/chunk-L6CQ7WIW.js.map +0 -1
- package/dist/chunk-MKQBXQKU.js.map +0 -1
- package/dist/chunk-NH5IR2WM.js.map +0 -1
- package/dist/chunk-PX2LSHYM.js.map +0 -1
- package/dist/chunk-QWGE2PUH.js.map +0 -1
- package/dist/chunk-RUMNY5DT.js.map +0 -1
- package/dist/chunk-S75M5ZRK.js.map +0 -1
- package/dist/chunk-T2JHZWH6.js.map +0 -1
- package/dist/chunk-UES4BHA2.js.map +0 -1
- package/dist/chunk-UTRO5UJU.js.map +0 -1
- package/dist/chunk-UU3G6GUP.js.map +0 -1
- package/dist/chunk-XUAMCLY4.js.map +0 -1
- package/dist/chunk-ZYLC7NFO.js.map +0 -1
- package/dist/contradiction-scan-HYLIRMJ2.js +0 -14
- package/dist/first-start-migration-626C5554.js.map +0 -1
- /package/dist/{auto-sync-TDHLZPVP.js.map → auto-sync-NR3GPCOA.js.map} +0 -0
- /package/dist/{chunk-LLJMAULE.js.map → chunk-2FKEIPIT.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-PDDTI7IC.js.map → chunk-43SEBY2M.js.map} +0 -0
- /package/dist/{chunk-HWRD23DW.js.map → chunk-46SZBVDS.js.map} +0 -0
- /package/dist/{chunk-6HPHEFXT.js.map → chunk-5FD7JMTX.js.map} +0 -0
- /package/dist/{chunk-IBKR2TFR.js.map → chunk-6CYHUWCO.js.map} +0 -0
- /package/dist/{chunk-YN6ASZUG.js.map → chunk-6QPI2ITJ.js.map} +0 -0
- /package/dist/{chunk-NIS256NW.js.map → chunk-CIAI24TQ.js.map} +0 -0
- /package/dist/{chunk-7FGL6CGA.js.map → chunk-DVYRD4HY.js.map} +0 -0
- /package/dist/{chunk-G7Z6EENO.js.map → chunk-EKTP7MNS.js.map} +0 -0
- /package/dist/{chunk-RZ4XGKOJ.js.map → chunk-FFRTNF3L.js.map} +0 -0
- /package/dist/{chunk-VKXWK44Q.js.map → chunk-FONWBCVY.js.map} +0 -0
- /package/dist/{chunk-DZD576ZU.js.map → chunk-FPLM2TKD.js.map} +0 -0
- /package/dist/{chunk-ZPJ4QSGP.js.map → chunk-FTPONF6B.js.map} +0 -0
- /package/dist/{chunk-LJS7KUPV.js.map → chunk-FWX7W5PM.js.map} +0 -0
- /package/dist/{chunk-G4T7L557.js.map → chunk-GN3WRFJY.js.map} +0 -0
- /package/dist/{chunk-QT2FRYI4.js.map → chunk-H7NAZ3UB.js.map} +0 -0
- /package/dist/{chunk-APPFNGX6.js.map → chunk-HBUFQRIM.js.map} +0 -0
- /package/dist/{chunk-UU7TRFNE.js.map → chunk-HGJ6NJ4E.js.map} +0 -0
- /package/dist/{chunk-C4ZNGVK3.js.map → chunk-IKP5OYQQ.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-RDJRFDJY.js.map → chunk-IQ4NVWEF.js.map} +0 -0
- /package/dist/{chunk-CGVHPCP2.js.map → chunk-JYG4OLWH.js.map} +0 -0
- /package/dist/{chunk-WOPA3SGI.js.map → chunk-K26NWEOC.js.map} +0 -0
- /package/dist/{chunk-G7A5HLUS.js.map → chunk-KJH6W2I7.js.map} +0 -0
- /package/dist/{chunk-HOIRB77A.js.map → chunk-L6Z5OAS4.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-3JLPGJ43.js.map → chunk-MIH3CJPN.js.map} +0 -0
- /package/dist/{chunk-QHFV6VNU.js.map → chunk-MK5VEWCR.js.map} +0 -0
- /package/dist/{chunk-GUKBW4HL.js.map → chunk-NQMSEB5D.js.map} +0 -0
- /package/dist/{chunk-WRJVB3C2.js.map → chunk-PO7NXBG2.js.map} +0 -0
- /package/dist/{chunk-AZM4CCAC.js.map → chunk-QZLDRQ4I.js.map} +0 -0
- /package/dist/{chunk-THPPCISA.js.map → chunk-RBII7637.js.map} +0 -0
- /package/dist/{chunk-VMMQDEN5.js.map → chunk-TO7ZTWJW.js.map} +0 -0
- /package/dist/{chunk-K2TDK7GG.js.map → chunk-TWTUGCTY.js.map} +0 -0
- /package/dist/{chunk-HLYR2TQL.js.map → chunk-UCTS3DDH.js.map} +0 -0
- /package/dist/{chunk-JYIJNNN3.js.map → chunk-UG6VXIOK.js.map} +0 -0
- /package/dist/{chunk-WM65LU67.js.map → chunk-UKYS3NU5.js.map} +0 -0
- /package/dist/{chunk-WDBX6VER.js.map → chunk-UPLZNJKN.js.map} +0 -0
- /package/dist/{chunk-2GFK22OZ.js.map → chunk-V6ERBR7U.js.map} +0 -0
- /package/dist/{chunk-ZMYDYNI4.js.map → chunk-XZHE2DEN.js.map} +0 -0
- /package/dist/{chunk-MAXVH44E.js.map → chunk-Z54EM5YZ.js.map} +0 -0
- /package/dist/{chunk-YGLY6QFV.js.map → chunk-ZRZVLB7O.js.map} +0 -0
- /package/dist/{chunk-4IIFL7GC.js.map → chunk-ZUKIUXHQ.js.map} +0 -0
- /package/dist/{contradiction-scan-HYLIRMJ2.js.map → contradiction-scan-JDTBXRGW.js.map} +0 -0
- /package/dist/{purge-533FZLS4.js.map → purge-GB7PZHT2.js.map} +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { StorageManager } from "../index.js";
|
|
5
|
+
import {
|
|
6
|
+
type HeldFileLockController,
|
|
7
|
+
serializeMutations,
|
|
8
|
+
withHeldFileLock,
|
|
9
|
+
} from "../utils/serialize-mutations.js";
|
|
10
|
+
import { SupportPassportError } from "./errors.js";
|
|
11
|
+
|
|
12
|
+
const CARD_MUTATION_LOCK_STALE_MS = 60_000;
|
|
13
|
+
const CARD_MUTATION_LOCK_MAX_WAIT_MS = 30_000;
|
|
14
|
+
|
|
15
|
+
export type SupportPassportOwnerLockScope =
|
|
16
|
+
| { namespace: string; principal: string; ownerKey?: never }
|
|
17
|
+
| { namespace: string; ownerKey: string; principal?: never };
|
|
18
|
+
|
|
19
|
+
export function computeSupportPassportOwnerLockKey(namespace: string, principal: string): string {
|
|
20
|
+
return createHash("sha256").update(JSON.stringify([namespace, principal])).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function supportPassportOwnerLockPath(
|
|
24
|
+
storage: StorageManager,
|
|
25
|
+
scope: SupportPassportOwnerLockScope,
|
|
26
|
+
): string {
|
|
27
|
+
const ownerLockKey = scope.ownerKey ?? computeSupportPassportOwnerLockKey(scope.namespace, scope.principal);
|
|
28
|
+
if (!/^[a-f0-9]{64}$/.test(ownerLockKey)) {
|
|
29
|
+
throw new SupportPassportError("card_data_invalid", "The support passport owner scope is invalid.", 500);
|
|
30
|
+
}
|
|
31
|
+
return path.join(storage.dir, "state", `support-passport-cards-${ownerLockKey}.lock`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function withSupportPassportOwnerLock<T>(
|
|
35
|
+
storage: StorageManager,
|
|
36
|
+
scope: SupportPassportOwnerLockScope,
|
|
37
|
+
task: (lock: HeldFileLockController) => Promise<T>,
|
|
38
|
+
): Promise<T> {
|
|
39
|
+
const lockPath = supportPassportOwnerLockPath(storage, scope);
|
|
40
|
+
return await serializeMutations(lockPath, () =>
|
|
41
|
+
withHeldFileLock(
|
|
42
|
+
lockPath,
|
|
43
|
+
{
|
|
44
|
+
staleMs: CARD_MUTATION_LOCK_STALE_MS,
|
|
45
|
+
maxWaitMs: CARD_MUTATION_LOCK_MAX_WAIT_MS,
|
|
46
|
+
},
|
|
47
|
+
async (acquired, lock) => {
|
|
48
|
+
if (!acquired) {
|
|
49
|
+
throw new SupportPassportError(
|
|
50
|
+
"storage_conflict",
|
|
51
|
+
"The support passport is busy. Try again.",
|
|
52
|
+
409,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return await task(lock);
|
|
56
|
+
},
|
|
57
|
+
),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function requireSupportPassportOwnerLock(
|
|
62
|
+
lock: HeldFileLockController,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
if (await lock.refresh()) return;
|
|
65
|
+
throw new SupportPassportError(
|
|
66
|
+
"storage_conflict",
|
|
67
|
+
"The support passport changed during the request.",
|
|
68
|
+
409,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, mkdtemp, open, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
appendPrivateFileNoFollow,
|
|
9
|
+
ensurePrivateDirectoryNoFollow,
|
|
10
|
+
supportsSupportPassportPrivateFiles,
|
|
11
|
+
writePrivateFileAtomicallyNoFollow,
|
|
12
|
+
} from "./private-file.js";
|
|
13
|
+
|
|
14
|
+
test("private support passport files publish their platform gate", () => {
|
|
15
|
+
assert.equal(supportsSupportPassportPrivateFiles("linux"), true);
|
|
16
|
+
assert.equal(supportsSupportPassportPrivateFiles("darwin"), true);
|
|
17
|
+
assert.equal(supportsSupportPassportPrivateFiles("freebsd"), true);
|
|
18
|
+
assert.equal(supportsSupportPassportPrivateFiles("openbsd"), true);
|
|
19
|
+
assert.equal(supportsSupportPassportPrivateFiles("win32"), false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("private append cannot escape its pinned directory", async (context) => {
|
|
23
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-"));
|
|
24
|
+
context.after(async () => await rm(root, { recursive: true, force: true }));
|
|
25
|
+
const privateDirectory = path.join(root, "private");
|
|
26
|
+
const outsidePath = path.join(root, "outside.jsonl");
|
|
27
|
+
await mkdir(privateDirectory);
|
|
28
|
+
|
|
29
|
+
await assert.rejects(appendPrivateFileNoFollow(privateDirectory, outsidePath, "private\n", "private path escaped"));
|
|
30
|
+
await assert.rejects(readFile(outsidePath, "utf8"), { code: "ENOENT" });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("private append fails before mutation when descriptor pinning is unavailable", async (context) => {
|
|
34
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-win32-"));
|
|
35
|
+
context.after(async () => await rm(root, { recursive: true, force: true }));
|
|
36
|
+
const privateDirectory = path.join(root, "private");
|
|
37
|
+
const auditPath = path.join(privateDirectory, "audit.jsonl");
|
|
38
|
+
await assert.rejects(
|
|
39
|
+
ensurePrivateDirectoryNoFollow(root, privateDirectory, "private path escaped", undefined, true, "win32"),
|
|
40
|
+
/private path escaped/
|
|
41
|
+
);
|
|
42
|
+
await assert.rejects(readFile(auditPath, "utf8"), { code: "ENOENT" });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("private append rejects a renamed target after opening it", async (context) => {
|
|
46
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-rename-"));
|
|
47
|
+
context.after(async () => await rm(root, { recursive: true, force: true }));
|
|
48
|
+
const privateDirectory = path.join(root, "private");
|
|
49
|
+
const auditPath = path.join(privateDirectory, "audit.jsonl");
|
|
50
|
+
const movedPath = path.join(privateDirectory, "moved.jsonl");
|
|
51
|
+
await mkdir(privateDirectory);
|
|
52
|
+
await writeFile(auditPath, "before\n", { mode: 0o600 });
|
|
53
|
+
|
|
54
|
+
await assert.rejects(
|
|
55
|
+
appendPrivateFileNoFollow(
|
|
56
|
+
privateDirectory,
|
|
57
|
+
auditPath,
|
|
58
|
+
"after\n",
|
|
59
|
+
"audit target changed",
|
|
60
|
+
privateDirectory,
|
|
61
|
+
process.platform,
|
|
62
|
+
async (filePath, flags, mode) => {
|
|
63
|
+
const handle = await open(filePath, flags, mode);
|
|
64
|
+
await rename(auditPath, movedPath);
|
|
65
|
+
return handle;
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/audit target changed/
|
|
69
|
+
);
|
|
70
|
+
assert.equal(await readFile(movedPath, "utf8"), "before\n");
|
|
71
|
+
await assert.rejects(readFile(auditPath, "utf8"), { code: "ENOENT" });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("private atomic writes reject a final pathname replaced after directory sync", async (context) => {
|
|
75
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-atomic-rename-"));
|
|
76
|
+
context.after(async () => await rm(root, { recursive: true, force: true }));
|
|
77
|
+
const privateDirectory = path.join(root, "private");
|
|
78
|
+
const markerPath = path.join(privateDirectory, "marker.json");
|
|
79
|
+
const movedPath = path.join(privateDirectory, "moved.json");
|
|
80
|
+
await mkdir(privateDirectory);
|
|
81
|
+
|
|
82
|
+
await assert.rejects(
|
|
83
|
+
writePrivateFileAtomicallyNoFollow(
|
|
84
|
+
privateDirectory,
|
|
85
|
+
markerPath,
|
|
86
|
+
"committed\n",
|
|
87
|
+
"marker target changed",
|
|
88
|
+
privateDirectory,
|
|
89
|
+
process.platform,
|
|
90
|
+
async () => {
|
|
91
|
+
await rename(markerPath, movedPath);
|
|
92
|
+
await writeFile(markerPath, "replaced\n", { mode: 0o600 });
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
/marker target changed/
|
|
96
|
+
);
|
|
97
|
+
assert.equal(await readFile(movedPath, "utf8"), "committed\n");
|
|
98
|
+
assert.equal(await readFile(markerPath, "utf8"), "replaced\n");
|
|
99
|
+
});
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { type Stats, constants as fsConstants } from "node:fs";
|
|
3
|
+
import { type FileHandle, lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
const UNSUPPORTED_DIRECTORY_SYNC_ERRORS = new Set(["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP"]);
|
|
7
|
+
const NON_WRITABLE_ANCESTOR_SYNC_ERRORS = new Set(["EROFS", "EPERM", "EACCES"]);
|
|
8
|
+
|
|
9
|
+
type PrivateFileOpener = (filePath: string, flags: number, mode: number) => Promise<FileHandle>;
|
|
10
|
+
|
|
11
|
+
function assertStableDirectory(before: Stats, opened: Stats, after: Stats, errorMessage: string): void {
|
|
12
|
+
if (
|
|
13
|
+
before.isSymbolicLink() ||
|
|
14
|
+
!before.isDirectory() ||
|
|
15
|
+
after.isSymbolicLink() ||
|
|
16
|
+
!after.isDirectory() ||
|
|
17
|
+
before.dev !== opened.dev ||
|
|
18
|
+
before.ino !== opened.ino ||
|
|
19
|
+
after.dev !== opened.dev ||
|
|
20
|
+
after.ino !== opened.ino
|
|
21
|
+
) {
|
|
22
|
+
throw new Error(errorMessage);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertStableRegularFile(before: Stats, opened: Stats, after: Stats, errorMessage: string): void {
|
|
27
|
+
if (
|
|
28
|
+
before.isSymbolicLink() ||
|
|
29
|
+
!before.isFile() ||
|
|
30
|
+
after.isSymbolicLink() ||
|
|
31
|
+
!after.isFile() ||
|
|
32
|
+
before.dev !== opened.dev ||
|
|
33
|
+
before.ino !== opened.ino ||
|
|
34
|
+
after.dev !== opened.dev ||
|
|
35
|
+
after.ino !== opened.ino ||
|
|
36
|
+
opened.nlink !== 1
|
|
37
|
+
) {
|
|
38
|
+
throw new Error(errorMessage);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function lstatPrivateTarget(filePath: string, errorMessage: string): Promise<Stats> {
|
|
43
|
+
try {
|
|
44
|
+
return await lstat(filePath);
|
|
45
|
+
} catch {
|
|
46
|
+
throw new Error(errorMessage);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function openFlags(...flags: Array<number | undefined>): number {
|
|
51
|
+
let combined = 0;
|
|
52
|
+
for (const flag of flags) combined |= typeof flag === "number" ? flag : 0;
|
|
53
|
+
return combined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function assertContainedPath(trustedRoot: string, directory: string, errorMessage: string): void {
|
|
57
|
+
const relative = path.relative(path.resolve(trustedRoot), path.resolve(directory));
|
|
58
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
59
|
+
throw new Error(errorMessage);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function openDirectoryNoFollow(
|
|
64
|
+
directory: string,
|
|
65
|
+
errorMessage: string
|
|
66
|
+
): Promise<{
|
|
67
|
+
before: Stats;
|
|
68
|
+
opened: Stats;
|
|
69
|
+
handle: FileHandle;
|
|
70
|
+
}> {
|
|
71
|
+
const before = await lstat(directory);
|
|
72
|
+
if (before.isSymbolicLink() || !before.isDirectory()) throw new Error(errorMessage);
|
|
73
|
+
const handle = await open(
|
|
74
|
+
directory,
|
|
75
|
+
openFlags(fsConstants.O_RDONLY, fsConstants.O_DIRECTORY, fsConstants.O_NOFOLLOW)
|
|
76
|
+
);
|
|
77
|
+
try {
|
|
78
|
+
const opened = await handle.stat();
|
|
79
|
+
assertStableDirectory(before, opened, await lstat(directory), errorMessage);
|
|
80
|
+
return { before, opened, handle };
|
|
81
|
+
} catch (error) {
|
|
82
|
+
await handle.close().catch(() => undefined);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function closeDirectoryHandles(handles: FileHandle[]): Promise<void> {
|
|
88
|
+
for (const handle of handles.reverse()) await handle.close().catch(() => undefined);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function syncDirectoryHandle(handle: FileHandle): Promise<void> {
|
|
92
|
+
try {
|
|
93
|
+
await handle.sync();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
96
|
+
if (!UNSUPPORTED_DIRECTORY_SYNC_ERRORS.has(code ?? "")) throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function resolvePrivateDirectoryPath(
|
|
101
|
+
directory: string,
|
|
102
|
+
handle: FileHandle,
|
|
103
|
+
opened: Stats,
|
|
104
|
+
errorMessage: string,
|
|
105
|
+
platform: NodeJS.Platform = process.platform
|
|
106
|
+
): Promise<string> {
|
|
107
|
+
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
108
|
+
const pinnedPath = path.join(descriptorRoot, String(handle.fd));
|
|
109
|
+
const metadata = await stat(pinnedPath);
|
|
110
|
+
if (!metadata.isDirectory() || metadata.dev !== opened.dev || metadata.ino !== opened.ino) {
|
|
111
|
+
throw new Error(errorMessage);
|
|
112
|
+
}
|
|
113
|
+
return pinnedPath;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function supportsSupportPassportPrivateFiles(
|
|
117
|
+
platform: NodeJS.Platform = process.platform
|
|
118
|
+
): boolean {
|
|
119
|
+
return platform === "linux" || platform === "darwin" || platform === "freebsd" || platform === "openbsd";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function requirePrivateFileDescriptorRoot(platform: NodeJS.Platform, errorMessage: string): string {
|
|
123
|
+
if (platform === "linux") return "/proc/self/fd";
|
|
124
|
+
if (platform === "darwin" || platform === "freebsd" || platform === "openbsd") return "/dev/fd";
|
|
125
|
+
throw new Error(errorMessage);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function openStableDirectoryFromRoot(
|
|
129
|
+
trustedRoot: string,
|
|
130
|
+
directory: string,
|
|
131
|
+
errorMessage: string,
|
|
132
|
+
platform: NodeJS.Platform = process.platform
|
|
133
|
+
): Promise<{
|
|
134
|
+
before: Stats;
|
|
135
|
+
opened: Stats;
|
|
136
|
+
handle?: FileHandle;
|
|
137
|
+
handles: FileHandle[];
|
|
138
|
+
pinnedDirectory: string;
|
|
139
|
+
}> {
|
|
140
|
+
const root = path.resolve(trustedRoot);
|
|
141
|
+
const target = path.resolve(directory);
|
|
142
|
+
assertContainedPath(root, target, errorMessage);
|
|
143
|
+
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
144
|
+
const relative = path.relative(root, target);
|
|
145
|
+
const components = relative === "" ? [] : relative.split(path.sep);
|
|
146
|
+
const handles: FileHandle[] = [];
|
|
147
|
+
let currentPath = root;
|
|
148
|
+
try {
|
|
149
|
+
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
150
|
+
handles.push(current.handle);
|
|
151
|
+
for (const component of components) {
|
|
152
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
153
|
+
const child = await openDirectoryNoFollow(path.join(pinnedParent, component), errorMessage);
|
|
154
|
+
handles.push(child.handle);
|
|
155
|
+
assertStableDirectory(current.before, current.opened, await lstat(currentPath), errorMessage);
|
|
156
|
+
currentPath = path.join(currentPath, component);
|
|
157
|
+
current = child;
|
|
158
|
+
}
|
|
159
|
+
assertStableDirectory(current.before, current.opened, await lstat(target), errorMessage);
|
|
160
|
+
return {
|
|
161
|
+
...current,
|
|
162
|
+
handles,
|
|
163
|
+
pinnedDirectory: await resolvePrivateDirectoryPath(
|
|
164
|
+
target,
|
|
165
|
+
current.handle,
|
|
166
|
+
current.opened,
|
|
167
|
+
errorMessage,
|
|
168
|
+
platform
|
|
169
|
+
),
|
|
170
|
+
};
|
|
171
|
+
} catch (error) {
|
|
172
|
+
await closeDirectoryHandles(handles);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function ensurePrivateDirectoryNoFollow(
|
|
178
|
+
trustedRoot: string,
|
|
179
|
+
directory: string,
|
|
180
|
+
errorMessage: string,
|
|
181
|
+
syncVerifiedParent: (handle: FileHandle) => Promise<void> = syncDirectoryHandle,
|
|
182
|
+
hardenExistingChildren = true,
|
|
183
|
+
platform: NodeJS.Platform = process.platform
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
const root = path.resolve(trustedRoot);
|
|
186
|
+
const target = path.resolve(directory);
|
|
187
|
+
assertContainedPath(root, target, errorMessage);
|
|
188
|
+
const relative = path.relative(root, target);
|
|
189
|
+
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
190
|
+
const components = relative === "" ? [] : relative.split(path.sep);
|
|
191
|
+
const handles: FileHandle[] = [];
|
|
192
|
+
let currentPath = root;
|
|
193
|
+
try {
|
|
194
|
+
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
195
|
+
handles.push(current.handle);
|
|
196
|
+
for (const component of components) {
|
|
197
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
198
|
+
const childPath = path.join(pinnedParent, component);
|
|
199
|
+
let created = false;
|
|
200
|
+
try {
|
|
201
|
+
await mkdir(childPath, { mode: 0o700 });
|
|
202
|
+
created = true;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
205
|
+
}
|
|
206
|
+
await syncVerifiedParent(current.handle);
|
|
207
|
+
const child = await openDirectoryNoFollow(childPath, errorMessage);
|
|
208
|
+
handles.push(child.handle);
|
|
209
|
+
if (created || (hardenExistingChildren && (child.opened.mode & 0o777) !== 0o700)) {
|
|
210
|
+
await child.handle.chmod(0o700);
|
|
211
|
+
}
|
|
212
|
+
assertStableDirectory(current.before, current.opened, await lstat(currentPath), errorMessage);
|
|
213
|
+
currentPath = path.join(currentPath, component);
|
|
214
|
+
current = child;
|
|
215
|
+
}
|
|
216
|
+
assertStableDirectory(current.before, current.opened, await lstat(target), errorMessage);
|
|
217
|
+
} finally {
|
|
218
|
+
await closeDirectoryHandles(handles);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function ensurePrivateDirectoryTreeNoFollow(
|
|
223
|
+
directory: string,
|
|
224
|
+
errorMessage: string,
|
|
225
|
+
syncVerifiedParent: (handle: FileHandle) => Promise<void> = syncDirectoryHandle,
|
|
226
|
+
platform: NodeJS.Platform = process.platform
|
|
227
|
+
): Promise<void> {
|
|
228
|
+
const target = path.resolve(directory);
|
|
229
|
+
const filesystemRoot = path.parse(target).root;
|
|
230
|
+
const rootMetadata = await stat(filesystemRoot);
|
|
231
|
+
await ensurePrivateDirectoryNoFollow(
|
|
232
|
+
filesystemRoot,
|
|
233
|
+
target,
|
|
234
|
+
errorMessage,
|
|
235
|
+
async (handle) => {
|
|
236
|
+
try {
|
|
237
|
+
await syncVerifiedParent(handle);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
240
|
+
const metadata = await handle.stat();
|
|
241
|
+
if (
|
|
242
|
+
metadata.ino === rootMetadata.ino &&
|
|
243
|
+
metadata.dev === rootMetadata.dev &&
|
|
244
|
+
NON_WRITABLE_ANCESTOR_SYNC_ERRORS.has(code ?? "")
|
|
245
|
+
) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
false,
|
|
252
|
+
platform
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function withPrivateDirectoryNoFollow<T>(
|
|
257
|
+
trustedRoot: string,
|
|
258
|
+
directory: string,
|
|
259
|
+
errorMessage: string,
|
|
260
|
+
task: (pinnedDirectory: string) => Promise<T>,
|
|
261
|
+
platform: NodeJS.Platform = process.platform
|
|
262
|
+
): Promise<T> {
|
|
263
|
+
let directoryHandles: FileHandle[] = [];
|
|
264
|
+
try {
|
|
265
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
266
|
+
directoryHandles = stableDirectory.handles;
|
|
267
|
+
const result = await task(stableDirectory.pinnedDirectory);
|
|
268
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
269
|
+
return result;
|
|
270
|
+
} finally {
|
|
271
|
+
await closeDirectoryHandles(directoryHandles);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export async function readPrivateFileNoFollow(
|
|
276
|
+
directory: string,
|
|
277
|
+
filePath: string,
|
|
278
|
+
errorMessage: string,
|
|
279
|
+
trustedRoot = directory,
|
|
280
|
+
platform: NodeJS.Platform = process.platform
|
|
281
|
+
): Promise<string> {
|
|
282
|
+
if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
|
|
283
|
+
const targetName = path.basename(filePath);
|
|
284
|
+
let directoryHandles: FileHandle[] = [];
|
|
285
|
+
let fileHandle: FileHandle | undefined;
|
|
286
|
+
try {
|
|
287
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
288
|
+
directoryHandles = stableDirectory.handles;
|
|
289
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
290
|
+
const targetPath = path.join(pinnedDirectory, targetName);
|
|
291
|
+
const before = await lstat(targetPath);
|
|
292
|
+
if (before.isSymbolicLink() || !before.isFile()) throw new Error(errorMessage);
|
|
293
|
+
try {
|
|
294
|
+
fileHandle = await open(targetPath, openFlags(fsConstants.O_RDONLY, fsConstants.O_NOFOLLOW));
|
|
295
|
+
} catch (error) {
|
|
296
|
+
if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
const fileMetadata = await fileHandle.stat();
|
|
300
|
+
assertStableRegularFile(before, fileMetadata, await lstat(targetPath), errorMessage);
|
|
301
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
302
|
+
return await fileHandle.readFile("utf8");
|
|
303
|
+
} finally {
|
|
304
|
+
await fileHandle?.close().catch(() => undefined);
|
|
305
|
+
await closeDirectoryHandles(directoryHandles);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export async function appendPrivateFileNoFollow(
|
|
310
|
+
directory: string,
|
|
311
|
+
filePath: string,
|
|
312
|
+
content: string,
|
|
313
|
+
errorMessage: string,
|
|
314
|
+
trustedRoot = directory,
|
|
315
|
+
platform: NodeJS.Platform = process.platform,
|
|
316
|
+
openFile: PrivateFileOpener = open
|
|
317
|
+
): Promise<void> {
|
|
318
|
+
if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
|
|
319
|
+
const targetName = path.basename(filePath);
|
|
320
|
+
let directoryHandles: FileHandle[] = [];
|
|
321
|
+
try {
|
|
322
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
323
|
+
directoryHandles = stableDirectory.handles;
|
|
324
|
+
await appendPrivateFileAtPinnedDirectory(
|
|
325
|
+
stableDirectory.pinnedDirectory,
|
|
326
|
+
targetName,
|
|
327
|
+
content,
|
|
328
|
+
errorMessage,
|
|
329
|
+
stableDirectory.handle,
|
|
330
|
+
openFile
|
|
331
|
+
);
|
|
332
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
333
|
+
} finally {
|
|
334
|
+
await closeDirectoryHandles(directoryHandles);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async function appendPrivateFileAtPinnedDirectory(
|
|
339
|
+
pinnedDirectory: string,
|
|
340
|
+
targetName: string,
|
|
341
|
+
content: string,
|
|
342
|
+
errorMessage: string,
|
|
343
|
+
directoryHandle: FileHandle | undefined,
|
|
344
|
+
openFile: PrivateFileOpener
|
|
345
|
+
): Promise<void> {
|
|
346
|
+
if (
|
|
347
|
+
targetName.length === 0 ||
|
|
348
|
+
targetName === "." ||
|
|
349
|
+
targetName === ".." ||
|
|
350
|
+
path.basename(targetName) !== targetName
|
|
351
|
+
) {
|
|
352
|
+
throw new Error(errorMessage);
|
|
353
|
+
}
|
|
354
|
+
const targetPath = path.join(pinnedDirectory, targetName);
|
|
355
|
+
let fileHandle: FileHandle | undefined;
|
|
356
|
+
try {
|
|
357
|
+
try {
|
|
358
|
+
fileHandle = await openFile(
|
|
359
|
+
targetPath,
|
|
360
|
+
openFlags(fsConstants.O_WRONLY, fsConstants.O_APPEND, fsConstants.O_CREAT, fsConstants.O_NOFOLLOW),
|
|
361
|
+
0o600
|
|
362
|
+
);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
367
|
+
const targetMetadata = await lstatPrivateTarget(targetPath, errorMessage);
|
|
368
|
+
assertStableRegularFile(
|
|
369
|
+
targetMetadata,
|
|
370
|
+
await fileHandle.stat(),
|
|
371
|
+
await lstatPrivateTarget(targetPath, errorMessage),
|
|
372
|
+
errorMessage
|
|
373
|
+
);
|
|
374
|
+
await fileHandle.chmod(0o600);
|
|
375
|
+
await fileHandle.appendFile(content, "utf8");
|
|
376
|
+
await fileHandle.sync();
|
|
377
|
+
assertStableRegularFile(
|
|
378
|
+
targetMetadata,
|
|
379
|
+
await fileHandle.stat(),
|
|
380
|
+
await lstatPrivateTarget(targetPath, errorMessage),
|
|
381
|
+
errorMessage
|
|
382
|
+
);
|
|
383
|
+
if (directoryHandle) await syncDirectoryHandle(directoryHandle);
|
|
384
|
+
} finally {
|
|
385
|
+
await fileHandle?.close().catch(() => undefined);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export async function appendPrivateFileInPinnedDirectory(
|
|
390
|
+
pinnedDirectory: string,
|
|
391
|
+
fileName: string,
|
|
392
|
+
content: string,
|
|
393
|
+
errorMessage: string,
|
|
394
|
+
platform: NodeJS.Platform = process.platform
|
|
395
|
+
): Promise<void> {
|
|
396
|
+
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
397
|
+
const resolvedDirectory = path.resolve(pinnedDirectory);
|
|
398
|
+
const descriptor = path.relative(descriptorRoot, resolvedDirectory);
|
|
399
|
+
if (!/^\d+$/.test(descriptor)) throw new Error(errorMessage);
|
|
400
|
+
const before = await stat(resolvedDirectory);
|
|
401
|
+
if (!before.isDirectory()) throw new Error(errorMessage);
|
|
402
|
+
let directoryHandle: FileHandle | undefined;
|
|
403
|
+
try {
|
|
404
|
+
directoryHandle = await open(resolvedDirectory, openFlags(fsConstants.O_RDONLY, fsConstants.O_DIRECTORY));
|
|
405
|
+
const opened = await directoryHandle.stat();
|
|
406
|
+
assertStableDirectory(before, opened, await stat(resolvedDirectory), errorMessage);
|
|
407
|
+
await appendPrivateFileAtPinnedDirectory(resolvedDirectory, fileName, content, errorMessage, directoryHandle, open);
|
|
408
|
+
assertStableDirectory(before, opened, await stat(resolvedDirectory), errorMessage);
|
|
409
|
+
} finally {
|
|
410
|
+
await directoryHandle?.close().catch(() => undefined);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export async function writePrivateFileAtomicallyNoFollow(
|
|
415
|
+
directory: string,
|
|
416
|
+
filePath: string,
|
|
417
|
+
content: string,
|
|
418
|
+
errorMessage: string,
|
|
419
|
+
trustedRoot = directory,
|
|
420
|
+
platform: NodeJS.Platform = process.platform,
|
|
421
|
+
syncVerifiedDirectory: (handle: FileHandle) => Promise<void> = syncDirectoryHandle
|
|
422
|
+
): Promise<void> {
|
|
423
|
+
if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
|
|
424
|
+
const targetName = path.basename(filePath);
|
|
425
|
+
const tempName = `.${targetName}.${randomUUID()}.tmp`;
|
|
426
|
+
let tempPath: string | undefined;
|
|
427
|
+
let directoryHandles: FileHandle[] = [];
|
|
428
|
+
let fileHandle: FileHandle | undefined;
|
|
429
|
+
try {
|
|
430
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
431
|
+
directoryHandles = stableDirectory.handles;
|
|
432
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
433
|
+
tempPath = path.join(pinnedDirectory, tempName);
|
|
434
|
+
const targetPath = path.join(pinnedDirectory, targetName);
|
|
435
|
+
try {
|
|
436
|
+
fileHandle = await open(
|
|
437
|
+
tempPath,
|
|
438
|
+
openFlags(fsConstants.O_WRONLY, fsConstants.O_CREAT, fsConstants.O_EXCL, fsConstants.O_NOFOLLOW),
|
|
439
|
+
0o600
|
|
440
|
+
);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
const fileMetadata = await fileHandle.stat();
|
|
446
|
+
const tempMetadata = await lstat(tempPath);
|
|
447
|
+
assertStableRegularFile(tempMetadata, fileMetadata, await lstat(tempPath), errorMessage);
|
|
448
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
449
|
+
await fileHandle.chmod(0o600);
|
|
450
|
+
await fileHandle.writeFile(content, "utf8");
|
|
451
|
+
await fileHandle.sync();
|
|
452
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
453
|
+
await rename(tempPath, targetPath);
|
|
454
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
455
|
+
if (stableDirectory.handle) await syncVerifiedDirectory(stableDirectory.handle);
|
|
456
|
+
const committedMetadata = await lstatPrivateTarget(targetPath, errorMessage);
|
|
457
|
+
assertStableRegularFile(
|
|
458
|
+
committedMetadata,
|
|
459
|
+
await fileHandle.stat(),
|
|
460
|
+
await lstatPrivateTarget(targetPath, errorMessage),
|
|
461
|
+
errorMessage
|
|
462
|
+
);
|
|
463
|
+
} finally {
|
|
464
|
+
await fileHandle?.close().catch(() => undefined);
|
|
465
|
+
if (tempPath) await rm(tempPath, { force: true }).catch(() => undefined);
|
|
466
|
+
await closeDirectoryHandles(directoryHandles);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export async function removePrivateFilesNoFollow(
|
|
471
|
+
directory: string,
|
|
472
|
+
fileNames: string[],
|
|
473
|
+
errorMessage: string,
|
|
474
|
+
trustedRoot = directory,
|
|
475
|
+
platform: NodeJS.Platform = process.platform
|
|
476
|
+
): Promise<void> {
|
|
477
|
+
if (
|
|
478
|
+
fileNames.some(
|
|
479
|
+
(fileName) =>
|
|
480
|
+
fileName.length === 0 || fileName === "." || fileName === ".." || path.basename(fileName) !== fileName
|
|
481
|
+
)
|
|
482
|
+
) {
|
|
483
|
+
throw new Error(errorMessage);
|
|
484
|
+
}
|
|
485
|
+
let directoryHandles: FileHandle[] = [];
|
|
486
|
+
try {
|
|
487
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
488
|
+
directoryHandles = stableDirectory.handles;
|
|
489
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
490
|
+
for (const fileName of fileNames) {
|
|
491
|
+
await rm(path.join(pinnedDirectory, fileName), { force: true });
|
|
492
|
+
}
|
|
493
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
|
|
494
|
+
if (stableDirectory.handle) await syncDirectoryHandle(stableDirectory.handle);
|
|
495
|
+
} finally {
|
|
496
|
+
await closeDirectoryHandles(directoryHandles);
|
|
497
|
+
}
|
|
498
|
+
}
|