attention-manifest-protocol 0.4.2 → 0.4.3
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/CHANGELOG.md +341 -293
- package/CHANGELOG.zh-CN.md +41 -8
- package/README.md +250 -171
- package/README.zh-CN.md +251 -171
- package/lib/assignment/assign.d.ts +102 -0
- package/lib/assignment/assign.js +556 -0
- package/lib/assignment/binding.d.ts +103 -0
- package/lib/assignment/binding.js +575 -0
- package/lib/assignment/errors.d.ts +14 -0
- package/lib/assignment/errors.js +16 -0
- package/lib/assignment/guards.d.ts +18 -0
- package/lib/assignment/guards.js +28 -0
- package/lib/assignment/index.d.ts +10 -0
- package/lib/assignment/index.js +10 -0
- package/lib/assignment/types.d.ts +169 -0
- package/lib/assignment/types.js +1 -0
- package/lib/attention/budget.d.ts +1 -1
- package/lib/attention/budget.js +31 -9
- package/lib/bayes/engine.js +2 -1
- package/lib/bayes/reliability.js +25 -10
- package/lib/boundary/frame.d.ts +10 -4
- package/lib/boundary/frame.js +346 -109
- package/lib/boundary/kinds.d.ts +11 -0
- package/lib/boundary/kinds.js +33 -0
- package/lib/boundary/refTable.d.ts +19 -9
- package/lib/boundary/refTable.js +128 -30
- package/lib/boundary/toolCall.d.ts +22 -10
- package/lib/boundary/toolCall.js +798 -97
- package/lib/concept/conceptBudget.js +10 -1
- package/lib/concurrency/idempotency.d.ts +4 -3
- package/lib/concurrency/idempotency.js +57 -13
- package/lib/concurrency/retry.js +18 -3
- package/lib/context/agentContext.d.ts +13 -0
- package/lib/context/agentContext.js +14 -1
- package/lib/context/ast.d.ts +4 -1
- package/lib/context/ast.js +13 -3
- package/lib/context/contentStore.js +22 -9
- package/lib/context/contextBuilder.d.ts +457 -0
- package/lib/context/contextBuilder.js +1395 -0
- package/lib/context/contextPackage.d.ts +214 -0
- package/lib/context/contextPackage.js +191 -0
- package/lib/context/index.d.ts +14 -3
- package/lib/context/index.js +9 -1
- package/lib/context/relevance.d.ts +171 -0
- package/lib/context/relevance.js +250 -0
- package/lib/context/workspaceDiff.d.ts +105 -0
- package/lib/context/workspaceDiff.js +238 -0
- package/lib/context/workspaceService.d.ts +236 -1
- package/lib/context/workspaceService.js +1501 -73
- package/lib/cooperation/collaboration.d.ts +9 -0
- package/lib/cooperation/session.d.ts +4 -4
- package/lib/cooperation/session.js +3 -3
- package/lib/database/adapter/filesystemStore.d.ts +145 -0
- package/lib/database/adapter/filesystemStore.js +321 -0
- package/lib/database/adapter/index.d.ts +2 -0
- package/lib/database/adapter/index.js +2 -0
- package/lib/database/adapter/workspaceSessionStore.d.ts +118 -0
- package/lib/database/adapter/workspaceSessionStore.js +120 -0
- package/lib/database/entity/fileGraph.d.ts +53 -0
- package/lib/database/entity/fileGraph.js +15 -0
- package/lib/database/entity/fileTree.d.ts +76 -0
- package/lib/database/entity/fileTree.js +39 -0
- package/lib/database/entity/hook.d.ts +29 -0
- package/lib/database/entity/hook.js +18 -0
- package/lib/database/entity/index.d.ts +4 -0
- package/lib/database/entity/index.js +4 -0
- package/lib/database/entity/workspaceSession.d.ts +53 -0
- package/lib/database/entity/workspaceSession.js +28 -0
- package/lib/database/repository/fileAnnotationRepository.d.ts +75 -0
- package/lib/database/repository/fileAnnotationRepository.js +109 -0
- package/lib/database/repository/fileGraphRepository.d.ts +188 -0
- package/lib/database/repository/fileGraphRepository.js +648 -0
- package/lib/database/repository/fileNodeRepository.d.ts +116 -0
- package/lib/database/repository/fileNodeRepository.js +263 -0
- package/lib/database/repository/fileOutlineRepository.d.ts +40 -0
- package/lib/database/repository/fileOutlineRepository.js +104 -0
- package/lib/database/repository/fileWriteRepository.d.ts +41 -0
- package/lib/database/repository/fileWriteRepository.js +93 -0
- package/lib/database/repository/hookRepository.d.ts +67 -0
- package/lib/database/repository/hookRepository.js +150 -0
- package/lib/database/repository/index.d.ts +7 -0
- package/lib/database/repository/index.js +7 -0
- package/lib/database/repository/workspaceSessionRepository.d.ts +134 -0
- package/lib/database/repository/workspaceSessionRepository.js +327 -0
- package/lib/database/service/database.d.ts +14 -0
- package/lib/database/service/database.js +21 -0
- package/lib/database/sql/schema.js +187 -0
- package/lib/database/store/database.js +6 -1
- package/lib/delegate/team.d.ts +5 -0
- package/lib/delegate/team.js +83 -20
- package/lib/discover/classify.d.ts +20 -0
- package/lib/discover/classify.js +85 -0
- package/lib/discover/host.d.ts +20 -0
- package/lib/discover/host.js +89 -0
- package/lib/discover/index.d.ts +11 -0
- package/lib/discover/index.js +11 -0
- package/lib/discover/kinds.d.ts +14 -0
- package/lib/discover/kinds.js +23 -0
- package/lib/discover/tree.d.ts +7 -0
- package/lib/discover/tree.js +432 -0
- package/lib/discover/types.d.ts +115 -0
- package/lib/discover/types.js +1 -0
- package/lib/error/boundaryRejection.d.ts +1 -1
- package/lib/evaluation/agreement.js +2 -2
- package/lib/execution/handles.js +6 -0
- package/lib/execution/pin.js +2 -2
- package/lib/file/cfb.d.ts +56 -0
- package/lib/file/cfb.js +396 -0
- package/lib/file/dependency.d.ts +151 -0
- package/lib/file/dependency.js +568 -0
- package/lib/file/directory.d.ts +34 -0
- package/lib/file/directory.js +141 -0
- package/lib/file/errors.d.ts +20 -0
- package/lib/file/errors.js +18 -0
- package/lib/file/exportGraph.d.ts +31 -0
- package/lib/file/exportGraph.js +554 -0
- package/lib/file/file.d.ts +40 -0
- package/lib/file/file.js +339 -0
- package/lib/file/importGraph.d.ts +121 -0
- package/lib/file/importGraph.js +1022 -0
- package/lib/file/index.d.ts +45 -0
- package/lib/file/index.js +30 -0
- package/lib/file/metadata.d.ts +66 -0
- package/lib/file/metadata.js +470 -0
- package/lib/file/path.d.ts +60 -0
- package/lib/file/path.js +259 -0
- package/lib/file/read.d.ts +168 -0
- package/lib/file/read.js +541 -0
- package/lib/file/runtime.d.ts +95 -0
- package/lib/file/runtime.js +150 -0
- package/lib/file/service.d.ts +227 -0
- package/lib/file/service.js +663 -0
- package/lib/file/syntaxTree.d.ts +7 -0
- package/lib/file/syntaxTree.js +38 -0
- package/lib/file/time.d.ts +38 -0
- package/lib/file/time.js +44 -0
- package/lib/file/tree.d.ts +53 -0
- package/lib/file/tree.js +417 -0
- package/lib/file/types.d.ts +371 -0
- package/lib/file/types.js +1 -0
- package/lib/file/write.d.ts +159 -0
- package/lib/file/write.js +1028 -0
- package/lib/file/zip.d.ts +35 -0
- package/lib/file/zip.js +349 -0
- package/lib/fingerprint/algorithm.d.ts +97 -0
- package/lib/fingerprint/algorithm.js +271 -0
- package/lib/fingerprint/bytes.d.ts +18 -0
- package/lib/fingerprint/bytes.js +31 -0
- package/lib/fingerprint/error.d.ts +73 -0
- package/lib/fingerprint/error.js +86 -0
- package/lib/fingerprint/file.d.ts +71 -0
- package/lib/fingerprint/file.js +254 -0
- package/lib/fingerprint/index.d.ts +37 -0
- package/lib/fingerprint/index.js +12 -0
- package/lib/fingerprint/json.d.ts +41 -0
- package/lib/fingerprint/json.js +94 -0
- package/lib/fingerprint/number.d.ts +32 -0
- package/lib/fingerprint/number.js +95 -0
- package/lib/fingerprint/policy.d.ts +54 -0
- package/lib/fingerprint/policy.js +50 -0
- package/lib/fingerprint/preimage.d.ts +84 -0
- package/lib/fingerprint/preimage.js +133 -0
- package/lib/fingerprint/raw.d.ts +26 -0
- package/lib/fingerprint/raw.js +45 -0
- package/lib/fingerprint/ref.d.ts +39 -0
- package/lib/fingerprint/ref.js +178 -0
- package/lib/fingerprint/text.d.ts +42 -0
- package/lib/fingerprint/text.js +155 -0
- package/lib/fingerprint/tree.d.ts +56 -0
- package/lib/fingerprint/tree.js +288 -0
- package/lib/fingerprint/verify.d.ts +20 -0
- package/lib/fingerprint/verify.js +99 -0
- package/lib/global/filesystem.d.ts +43 -0
- package/lib/global/filesystem.js +75 -0
- package/lib/global/index.d.ts +8 -0
- package/lib/global/index.js +8 -0
- package/lib/global/service.d.ts +51 -0
- package/lib/global/service.js +98 -0
- package/lib/governance/transport.js +18 -6
- package/lib/hook/ast.d.ts +29 -0
- package/lib/hook/ast.js +22 -0
- package/lib/hook/code.d.ts +55 -0
- package/lib/hook/code.js +47 -0
- package/lib/hook/dependency.d.ts +45 -0
- package/lib/hook/dependency.js +28 -0
- package/lib/hook/diff.d.ts +114 -0
- package/lib/hook/diff.js +327 -0
- package/lib/hook/exportation.d.ts +31 -0
- package/lib/hook/exportation.js +24 -0
- package/lib/hook/file.d.ts +44 -0
- package/lib/hook/file.js +154 -0
- package/lib/hook/importation.d.ts +33 -0
- package/lib/hook/importation.js +22 -0
- package/lib/hook/index.d.ts +30 -0
- package/lib/hook/index.js +23 -0
- package/lib/hook/outline.d.ts +35 -0
- package/lib/hook/outline.js +27 -0
- package/lib/hook/project.d.ts +65 -0
- package/lib/hook/project.js +217 -0
- package/lib/hook/types.d.ts +114 -0
- package/lib/hook/types.js +407 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +9 -0
- package/lib/lock/lease.js +28 -3
- package/lib/manifest/cardSign.d.ts +1 -1
- package/lib/manifest/cardSign.js +37 -7
- package/lib/manifest/federation.js +13 -6
- package/lib/manifest/trust.d.ts +2 -0
- package/lib/manifest/trust.js +27 -6
- package/lib/module/index.d.ts +4 -0
- package/lib/module/index.js +4 -0
- package/lib/module/mcp/client.js +21 -2
- package/lib/module/mcp/filesystemServer.d.ts +24 -0
- package/lib/module/mcp/filesystemServer.js +102 -0
- package/lib/module/mcp/http.js +32 -19
- package/lib/module/mcp/workspaceServer.js +20 -3
- package/lib/module/tool/filesystemExecutor.d.ts +18 -0
- package/lib/module/tool/filesystemExecutor.js +49 -0
- package/lib/module/tool/globalExecutor.d.ts +17 -0
- package/lib/module/tool/globalExecutor.js +39 -0
- package/lib/module/tool/globalFilesystemExecutor.d.ts +18 -0
- package/lib/module/tool/globalFilesystemExecutor.js +49 -0
- package/lib/module/tool/workspaceExecutor.d.ts +9 -0
- package/lib/module/tool/workspaceExecutor.js +16 -3
- package/lib/naming/aliases.d.ts +48 -0
- package/lib/naming/aliases.js +79 -0
- package/lib/naming/catalog.d.ts +117 -0
- package/lib/naming/catalog.js +108 -0
- package/lib/naming/grammar.d.ts +67 -0
- package/lib/naming/grammar.js +212 -0
- package/lib/naming/ids.d.ts +157 -0
- package/lib/naming/ids.js +119 -0
- package/lib/naming/index.d.ts +13 -0
- package/lib/naming/index.js +13 -0
- package/lib/outline/index.d.ts +9 -0
- package/lib/outline/index.js +8 -0
- package/lib/outline/office.d.ts +160 -0
- package/lib/outline/office.js +2375 -0
- package/lib/outline/officeBridge.d.ts +32 -0
- package/lib/outline/officeBridge.js +27 -0
- package/lib/outline/outline.d.ts +159 -0
- package/lib/outline/outline.js +837 -0
- package/lib/outline/outlineIndex.d.ts +97 -0
- package/lib/outline/outlineIndex.js +325 -0
- package/lib/outline/outlineKeywords.d.ts +59 -0
- package/lib/outline/outlineKeywords.js +143 -0
- package/lib/outline/outlineStrategies.d.ts +414 -0
- package/lib/outline/outlineStrategies.js +8324 -0
- package/lib/outline/reactOutline.d.ts +37 -0
- package/lib/outline/reactOutline.js +1070 -0
- package/lib/outline/shellOutline.d.ts +65 -0
- package/lib/outline/shellOutline.js +873 -0
- package/lib/outline/sqlOutline.d.ts +170 -0
- package/lib/outline/sqlOutline.js +1536 -0
- package/lib/outline/strategy.d.ts +46 -0
- package/lib/outline/strategy.js +1 -0
- package/lib/outline/types.d.ts +95 -0
- package/lib/outline/types.js +1 -0
- package/lib/outline/visualBasicOutline.d.ts +27 -0
- package/lib/outline/visualBasicOutline.js +750 -0
- package/lib/protocol/filesystem.d.ts +55 -0
- package/lib/protocol/filesystem.js +105 -0
- package/lib/protocol/global.d.ts +42 -0
- package/lib/protocol/global.js +63 -0
- package/lib/protocol/globalFilesystem.d.ts +40 -0
- package/lib/protocol/globalFilesystem.js +59 -0
- package/lib/protocol/index.d.ts +4 -0
- package/lib/protocol/index.js +4 -0
- package/lib/protocol/protocol.d.ts +4 -0
- package/lib/protocol/wire.js +5 -2
- package/lib/protocol/workspace.d.ts +305 -0
- package/lib/protocol/workspace.js +258 -0
- package/lib/receipt/receipt.d.ts +8 -0
- package/lib/registry/content.js +9 -3
- package/lib/runtime/kernelRuntime.js +9 -1
- package/lib/scenario/scenario.js +5 -4
- package/lib/semantic/canonical.d.ts +61 -0
- package/lib/semantic/canonical.js +135 -8
- package/lib/semantic/jsonValue.d.ts +31 -0
- package/lib/semantic/jsonValue.js +122 -0
- package/lib/semantic/sha256.js +3 -0
- package/lib/semantic/time.d.ts +14 -0
- package/lib/semantic/time.js +56 -0
- package/lib/session/context.d.ts +76 -0
- package/lib/session/context.js +419 -0
- package/lib/session/errors.d.ts +24 -0
- package/lib/session/errors.js +22 -0
- package/lib/session/identity.d.ts +24 -0
- package/lib/session/identity.js +56 -0
- package/lib/session/index.d.ts +14 -0
- package/lib/session/index.js +12 -0
- package/lib/session/registry.d.ts +130 -0
- package/lib/session/registry.js +908 -0
- package/lib/session/types.d.ts +227 -0
- package/lib/session/types.js +14 -0
- package/lib/structure/index.d.ts +1 -0
- package/lib/structure/index.js +1 -0
- package/lib/structure/trie.d.ts +88 -0
- package/lib/structure/trie.js +183 -0
- package/lib/syntax/byteIndex.d.ts +44 -0
- package/lib/syntax/byteIndex.js +148 -0
- package/lib/syntax/charset.d.ts +88 -0
- package/lib/syntax/charset.js +276 -0
- package/lib/syntax/index.d.ts +6 -0
- package/lib/syntax/index.js +5 -0
- package/lib/syntax/syntaxRegistry.d.ts +150 -0
- package/lib/syntax/syntaxRegistry.js +314 -0
- package/lib/syntax/syntaxTree.d.ts +106 -0
- package/lib/syntax/syntaxTree.js +668 -0
- package/lib/syntax/types.d.ts +15 -0
- package/lib/syntax/types.js +1 -0
- package/lib/types/common.d.ts +1 -1
- package/package.json +72 -193
- package/.editorconfig +0 -12
- package/.prettierignore +0 -17
- package/MCP-Token/347/250/216/344/270/216/344/270/213/344/270/200/344/273/243/345/267/245/345/205/267/345/215/217/350/256/256.html +0 -1763
- package/docs/annotation-campaign-A.csv +0 -54
- package/docs/annotation-campaign-B.csv +0 -54
- package/docs/annotation-campaign.csv +0 -54
- package/docs/eval/annotations-A.jsonl +0 -16
- package/docs/eval/annotations.json +0 -122
- package/docs/eval/recorded-labels.json +0 -12
- package/docs/eval/replay-cases.json +0 -14
- package/docs/eval/replay-labels-b.json +0 -14
- package/docs/examples/attention-request.json +0 -16
- package/docs/examples/checkpoint.json +0 -15
- package/docs/examples/claim.json +0 -66
- package/docs/examples/collaboration.json +0 -66
- package/docs/examples/concept-checkpoint.json +0 -15
- package/docs/examples/concept-prune-plan.json +0 -45
- package/docs/examples/federation.json +0 -126
- package/docs/examples/frame-chain.json +0 -77
- package/docs/examples/ledger-snapshot.json +0 -108
- package/docs/examples/manifest.json +0 -77
- package/docs/examples/message.json +0 -19
- package/docs/examples/other-model.json +0 -45
- package/docs/examples/plan.json +0 -50
- package/docs/examples/prune-plan.json +0 -48
- package/docs/examples/receipt.json +0 -22
- package/docs/examples/runtime-run.json +0 -573
- package/docs/examples/team.json +0 -66
- package/docs/examples/verb-note.json +0 -11
- package/docs/probes/bayes-scores.json +0 -1452
- package/docs/probes/calibration-imported.json +0 -141
- package/docs/probes/context-ast-deepseek.json +0 -1577
- package/docs/probes/deepseek-probe.json +0 -917
- package/docs/probes/eval-recorded-replay.json +0 -597
- package/docs/probes/eval-replay.json +0 -124
- package/docs/probes/eval-summary.json +0 -42
- package/docs/probes/runtime-eval.json +0 -4230
- package/docs/schemas/collaboration.schema.json +0 -73
- package/docs/schemas/common.schema.json +0 -125
- package/docs/schemas/compaction.schema.json +0 -121
- package/docs/schemas/concept-compaction.schema.json +0 -73
- package/docs/schemas/concept.schema.json +0 -97
- package/docs/schemas/epistemic.schema.json +0 -178
- package/docs/schemas/federation.schema.json +0 -66
- package/docs/schemas/frame.schema.json +0 -86
- package/docs/schemas/ledger-snapshot.schema.json +0 -30
- package/docs/schemas/manifest.schema.json +0 -169
- package/docs/schemas/message.schema.json +0 -41
- package/docs/schemas/plan.schema.json +0 -189
- package/docs/schemas/receipt.schema.json +0 -45
- package/docs/schemas/social.schema.json +0 -153
- package/docs/schemas/team.schema.json +0 -56
- package/docs/schemas/verb.schema.json +0 -120
- package/eslint.config.mts +0 -96
- package/index.html +0 -13
- package/lib/context/workspaceProtocol.d.ts +0 -30
- package/lib/context/workspaceProtocol.js +0 -49
- package/prettier.config.mts +0 -37
- package/public/favicon.svg +0 -1
- package/public/icons.svg +0 -24
- package/src/attention/attention.ts +0 -347
- package/src/attention/budget.ts +0 -72
- package/src/attention/cognition.ts +0 -519
- package/src/attention/index.ts +0 -8
- package/src/attention/interrupts.ts +0 -228
- package/src/attention/resource.ts +0 -316
- package/src/attention/social.ts +0 -217
- package/src/attention/socialTypes.ts +0 -115
- package/src/authz/authz.ts +0 -305
- package/src/authz/index.ts +0 -1
- package/src/bayes/calibration.ts +0 -204
- package/src/bayes/calibrationRegistry.ts +0 -111
- package/src/bayes/calibrationSelection.ts +0 -154
- package/src/bayes/engine.ts +0 -532
- package/src/bayes/index.ts +0 -6
- package/src/bayes/reliability.ts +0 -62
- package/src/bayes/scopedPriors.ts +0 -88
- package/src/boundary/frame.ts +0 -322
- package/src/boundary/ids.ts +0 -14
- package/src/boundary/index.ts +0 -12
- package/src/boundary/refTable.ts +0 -125
- package/src/boundary/toolCall.ts +0 -212
- package/src/concept/concept.ts +0 -75
- package/src/concept/conceptBudget.ts +0 -346
- package/src/concept/conceptBudgetTypes.ts +0 -79
- package/src/concept/index.ts +0 -3
- package/src/concurrency/idempotency.ts +0 -41
- package/src/concurrency/index.ts +0 -5
- package/src/concurrency/mutex.ts +0 -26
- package/src/concurrency/namedLocks.ts +0 -35
- package/src/concurrency/retry.ts +0 -50
- package/src/concurrency/worker.ts +0 -128
- package/src/context/agentContext.ts +0 -308
- package/src/context/ast.ts +0 -499
- package/src/context/contentStore.ts +0 -75
- package/src/context/context.ts +0 -65
- package/src/context/contextIndex.ts +0 -85
- package/src/context/document.ts +0 -137
- package/src/context/fileStorePath.ts +0 -14
- package/src/context/index.ts +0 -43
- package/src/context/lifecycle.ts +0 -221
- package/src/context/projections.ts +0 -151
- package/src/context/workspace.ts +0 -422
- package/src/context/workspaceProtocol.ts +0 -112
- package/src/context/workspaceService.ts +0 -423
- package/src/cooperation/board.ts +0 -104
- package/src/cooperation/collaboration.ts +0 -60
- package/src/cooperation/index.ts +0 -6
- package/src/cooperation/merge.ts +0 -190
- package/src/cooperation/metrics.ts +0 -57
- package/src/cooperation/session.ts +0 -131
- package/src/cooperation/unified.ts +0 -56
- package/src/counter.ts +0 -9
- package/src/database/adapter/adapters.ts +0 -35
- package/src/database/adapter/index.ts +0 -4
- package/src/database/adapter/runtimeSink.ts +0 -74
- package/src/database/adapter/sessionStore.ts +0 -32
- package/src/database/adapter/turnStore.ts +0 -23
- package/src/database/entity/agent.ts +0 -199
- package/src/database/entity/entity.ts +0 -1046
- package/src/database/entity/handle.ts +0 -79
- package/src/database/entity/index.ts +0 -32
- package/src/database/entity/message.ts +0 -74
- package/src/database/entity/meta.ts +0 -11
- package/src/database/entity/object.ts +0 -42
- package/src/database/entity/outbox.ts +0 -15
- package/src/database/entity/process.ts +0 -65
- package/src/database/entity/registry.ts +0 -155
- package/src/database/entity/runtimeApproval.ts +0 -30
- package/src/database/entity/runtimeSession.ts +0 -13
- package/src/database/entity/runtimeTurn.ts +0 -15
- package/src/database/entity/thread.ts +0 -85
- package/src/database/entity/window.ts +0 -85
- package/src/database/entity/worker.ts +0 -90
- package/src/database/index.ts +0 -6
- package/src/database/repository/addressSpaceRepository.ts +0 -72
- package/src/database/repository/agentRepository.ts +0 -460
- package/src/database/repository/base.ts +0 -41
- package/src/database/repository/executionRepository.ts +0 -477
- package/src/database/repository/handleRepository.ts +0 -64
- package/src/database/repository/index.ts +0 -20
- package/src/database/repository/messageRepository.ts +0 -52
- package/src/database/repository/objectRepository.ts +0 -69
- package/src/database/repository/outboxRepository.ts +0 -63
- package/src/database/repository/processRepository.ts +0 -58
- package/src/database/repository/registryRepository.ts +0 -119
- package/src/database/repository/repository.ts +0 -491
- package/src/database/repository/runtimeApprovalRepository.ts +0 -44
- package/src/database/repository/runtimeSessionRepository.ts +0 -27
- package/src/database/repository/runtimeTurnRepository.ts +0 -39
- package/src/database/repository/socialRepository.ts +0 -185
- package/src/database/repository/threadRepository.ts +0 -57
- package/src/database/repository/windowRepository.ts +0 -88
- package/src/database/repository/workerRepository.ts +0 -69
- package/src/database/service/database.ts +0 -90
- package/src/database/service/handleService.ts +0 -48
- package/src/database/service/index.ts +0 -16
- package/src/database/service/messageService.ts +0 -61
- package/src/database/service/objectService.ts +0 -59
- package/src/database/service/processService.ts +0 -61
- package/src/database/service/registryServices.ts +0 -88
- package/src/database/service/service.ts +0 -183
- package/src/database/service/stepService.ts +0 -53
- package/src/database/service/threadService.ts +0 -65
- package/src/database/service/windowService.ts +0 -30
- package/src/database/service/workerService.ts +0 -57
- package/src/database/sql/index.ts +0 -3
- package/src/database/sql/schema.ts +0 -418
- package/src/database/sql/sql.ts +0 -543
- package/src/database/store/database.ts +0 -210
- package/src/database/store/index.ts +0 -3
- package/src/database/store/store.ts +0 -266
- package/src/delegate/index.ts +0 -2
- package/src/delegate/team.ts +0 -268
- package/src/delegate/teamTypes.ts +0 -47
- package/src/diagnosis/checklist.ts +0 -74
- package/src/diagnosis/context.ts +0 -93
- package/src/diagnosis/frame.ts +0 -256
- package/src/diagnosis/index.ts +0 -5
- package/src/diagnosis/router.ts +0 -72
- package/src/diagnosis/types.ts +0 -89
- package/src/error/agentError.ts +0 -16
- package/src/error/ampError.ts +0 -20
- package/src/error/authzError.ts +0 -14
- package/src/error/boundaryRejection.ts +0 -20
- package/src/error/index.ts +0 -7
- package/src/error/kernelError.ts +0 -27
- package/src/error/mailboxError.ts +0 -9
- package/src/error/windowError.ts +0 -12
- package/src/evaluation/agreement.ts +0 -96
- package/src/evaluation/index.ts +0 -2
- package/src/evaluation/labels.ts +0 -241
- package/src/evidence/evidenceGraph.ts +0 -205
- package/src/evidence/index.ts +0 -4
- package/src/evidence/revalidation.ts +0 -191
- package/src/execution/exec.ts +0 -408
- package/src/execution/handles.ts +0 -65
- package/src/execution/index.ts +0 -3
- package/src/execution/pin.ts +0 -154
- package/src/frame/frame.ts +0 -70
- package/src/frame/index.ts +0 -1
- package/src/goal/goal.ts +0 -43
- package/src/goal/index.ts +0 -2
- package/src/goal/plan.ts +0 -196
- package/src/governance/attentionManifest.ts +0 -802
- package/src/governance/audit.ts +0 -362
- package/src/governance/budget.ts +0 -628
- package/src/governance/context.ts +0 -356
- package/src/governance/control.ts +0 -159
- package/src/governance/cooperation.ts +0 -479
- package/src/governance/credential.ts +0 -359
- package/src/governance/httpServer.ts +0 -99
- package/src/governance/index.ts +0 -32
- package/src/governance/legal.ts +0 -574
- package/src/governance/matrix.ts +0 -460
- package/src/governance/perspectives.ts +0 -406
- package/src/governance/signing.ts +0 -407
- package/src/governance/store.ts +0 -125
- package/src/governance/switching.ts +0 -459
- package/src/governance/toolPolicy.ts +0 -80
- package/src/governance/transport.ts +0 -204
- package/src/governance/types.ts +0 -380
- package/src/governance/workflow.ts +0 -743
- package/src/index.ts +0 -11
- package/src/interaction/index.ts +0 -1
- package/src/interaction/interaction.ts +0 -391
- package/src/ipc/index.ts +0 -4
- package/src/ipc/mailbox.ts +0 -132
- package/src/ipc/memorySocialStore.ts +0 -93
- package/src/ipc/social.ts +0 -93
- package/src/kernel/index.ts +0 -14
- package/src/kernel/scheduler.ts +0 -498
- package/src/ledger/checkpoint.ts +0 -304
- package/src/ledger/index.ts +0 -4
- package/src/ledger/ledger.ts +0 -31
- package/src/ledger/persistence.ts +0 -159
- package/src/ledger/share.ts +0 -80
- package/src/lock/index.ts +0 -2
- package/src/lock/lease.ts +0 -74
- package/src/main.ts +0 -199
- package/src/manifest/agentCard.ts +0 -72
- package/src/manifest/cardSign.ts +0 -87
- package/src/manifest/federation.ts +0 -237
- package/src/manifest/federationTypes.ts +0 -55
- package/src/manifest/index.ts +0 -8
- package/src/manifest/keys.ts +0 -643
- package/src/manifest/manifest.ts +0 -114
- package/src/manifest/sign.ts +0 -134
- package/src/manifest/trust.ts +0 -146
- package/src/mission/index.ts +0 -2
- package/src/mission/missionQueue.ts +0 -75
- package/src/module/index.ts +0 -9
- package/src/module/llm/deepseek.ts +0 -214
- package/src/module/llm/llm.ts +0 -84
- package/src/module/mcp/client.ts +0 -110
- package/src/module/mcp/http.ts +0 -102
- package/src/module/mcp/protocol.ts +0 -77
- package/src/module/mcp/server.ts +0 -148
- package/src/module/mcp/transport.ts +0 -30
- package/src/module/mcp/workspaceServer.ts +0 -111
- package/src/module/sandbox/sandbox.ts +0 -29
- package/src/module/tool/mcpExecutor.ts +0 -54
- package/src/module/tool/policy.ts +0 -52
- package/src/module/tool/redact.ts +0 -29
- package/src/module/tool/tool.ts +0 -53
- package/src/module/tool/workspaceExecutor.ts +0 -35
- package/src/policy/guard.ts +0 -240
- package/src/policy/index.ts +0 -2
- package/src/policy/policy.ts +0 -561
- package/src/process/agent.ts +0 -341
- package/src/process/agentService.ts +0 -236
- package/src/process/agentStore.ts +0 -378
- package/src/process/executionStore.ts +0 -151
- package/src/process/index.ts +0 -5
- package/src/process/process.ts +0 -240
- package/src/process/transitions.ts +0 -19
- package/src/progress/index.ts +0 -2
- package/src/progress/progress.ts +0 -85
- package/src/protocol/a2a.ts +0 -120
- package/src/protocol/channel.ts +0 -44
- package/src/protocol/envelope.ts +0 -655
- package/src/protocol/httpChannel.ts +0 -87
- package/src/protocol/index.ts +0 -6
- package/src/protocol/protocol.ts +0 -138
- package/src/protocol/wire.ts +0 -60
- package/src/receipt/index.ts +0 -1
- package/src/receipt/receipt.ts +0 -47
- package/src/registry/content.ts +0 -69
- package/src/registry/index.ts +0 -23
- package/src/registry/registry.ts +0 -599
- package/src/runtime/action.ts +0 -87
- package/src/runtime/behavior.ts +0 -330
- package/src/runtime/behaviorTuning.ts +0 -163
- package/src/runtime/cognitiveRuntime.ts +0 -703
- package/src/runtime/escalation.ts +0 -35
- package/src/runtime/governedTask.ts +0 -233
- package/src/runtime/index.ts +0 -15
- package/src/runtime/kernelRuntime.ts +0 -144
- package/src/runtime/operation.ts +0 -35
- package/src/runtime/replay.ts +0 -71
- package/src/runtime/routing.ts +0 -58
- package/src/runtime/safety.ts +0 -48
- package/src/runtime/session.ts +0 -341
- package/src/runtime/sessionStore.ts +0 -43
- package/src/runtime/telemetry.ts +0 -55
- package/src/runtime/turnStore.ts +0 -30
- package/src/scenario/index.ts +0 -1
- package/src/scenario/scenario.ts +0 -299
- package/src/semantic/canonical.ts +0 -24
- package/src/semantic/index.ts +0 -2
- package/src/semantic/sha256.ts +0 -82
- package/src/storage/appendLog.ts +0 -372
- package/src/storage/fileAppendSink.ts +0 -40
- package/src/storage/index.ts +0 -2
- package/src/store/bindCache.ts +0 -22
- package/src/store/cache.ts +0 -191
- package/src/store/context.ts +0 -31
- package/src/store/handleStore.ts +0 -31
- package/src/store/index.ts +0 -9
- package/src/store/messageStore.ts +0 -31
- package/src/store/objectStore.ts +0 -31
- package/src/store/processStore.ts +0 -31
- package/src/store/threadStore.ts +0 -31
- package/src/store/windowStore.ts +0 -31
- package/src/structure/abstractSyntaxTree.ts +0 -493
- package/src/structure/avlTree.ts +0 -408
- package/src/structure/binarySearchTree.ts +0 -352
- package/src/structure/binaryTree.ts +0 -525
- package/src/structure/bloomFilter.ts +0 -231
- package/src/structure/compare.ts +0 -110
- package/src/structure/cuckooFilter.ts +0 -337
- package/src/structure/deque.ts +0 -223
- package/src/structure/filterKey.ts +0 -36
- package/src/structure/hash.ts +0 -279
- package/src/structure/heap.ts +0 -268
- package/src/structure/index.ts +0 -20
- package/src/structure/iterator.ts +0 -22
- package/src/structure/lru.ts +0 -619
- package/src/structure/ordered.ts +0 -399
- package/src/structure/priority.ts +0 -915
- package/src/structure/queue.ts +0 -168
- package/src/structure/redBlackTree.ts +0 -680
- package/src/structure/segmentTree.ts +0 -248
- package/src/structure/stack.ts +0 -118
- package/src/structure/unordered.ts +0 -407
- package/src/style.css +0 -278
- package/src/thread/index.ts +0 -2
- package/src/thread/thread.ts +0 -176
- package/src/thread/transitions.ts +0 -17
- package/src/types/common.ts +0 -119
- package/src/types/index.ts +0 -23
- package/src/warrant/epistemic.ts +0 -154
- package/src/warrant/index.ts +0 -3
- package/src/warrant/inquiry.ts +0 -166
- package/src/warrant/warrant.ts +0 -179
- package/src/window/compaction.ts +0 -403
- package/src/window/compactionTypes.ts +0 -119
- package/src/window/index.ts +0 -7
- package/src/window/page.ts +0 -18
- package/src/window/window.ts +0 -244
- package/tools/_helpers.ts +0 -34
- package/tools/a2a-check.ts +0 -110
- package/tools/a2a-flow-check.ts +0 -78
- package/tools/agent-card-check.ts +0 -76
- package/tools/agent-check.ts +0 -236
- package/tools/agent-store-check.ts +0 -153
- package/tools/agreement-check.ts +0 -27
- package/tools/annotation-server-check.ts +0 -48
- package/tools/annotation-server.ts +0 -230
- package/tools/annotation-status.ts +0 -65
- package/tools/approval-access-check.ts +0 -66
- package/tools/arch-check.ts +0 -72
- package/tools/attention-core-check.ts +0 -48
- package/tools/attention-resource-check.ts +0 -169
- package/tools/audit-check.ts +0 -207
- package/tools/authority-check.ts +0 -124
- package/tools/authz-check.ts +0 -138
- package/tools/bayes-check.ts +0 -162
- package/tools/bayes-probe.ts +0 -154
- package/tools/behavior-tuning-check.ts +0 -53
- package/tools/board-check.ts +0 -87
- package/tools/boundary-check.ts +0 -199
- package/tools/bundle-trust-check.ts +0 -96
- package/tools/calibrate-imported.ts +0 -108
- package/tools/calibration-check.ts +0 -44
- package/tools/calibration-registry-check.ts +0 -40
- package/tools/calibration-sim.ts +0 -40
- package/tools/card-sign-check.ts +0 -71
- package/tools/card-trust-check.ts +0 -78
- package/tools/channel-check.ts +0 -86
- package/tools/checkpoint-check.ts +0 -620
- package/tools/cleanup-style-configs.ts +0 -29
- package/tools/cognition-check.ts +0 -648
- package/tools/cognitive-runtime-check.ts +0 -609
- package/tools/collaboration-demo.ts +0 -78
- package/tools/compaction-check.ts +0 -266
- package/tools/concept-check.ts +0 -260
- package/tools/concurrency-check.ts +0 -242
- package/tools/conformance-runtime.ts +0 -389
- package/tools/conformance.ts +0 -651
- package/tools/context-ast-check.ts +0 -90
- package/tools/context-ast-deepseek.ts +0 -242
- package/tools/context-graph-bench.ts +0 -50
- package/tools/context-graph-check.ts +0 -229
- package/tools/context-index-check.ts +0 -48
- package/tools/context-lifecycle-check.ts +0 -69
- package/tools/context-projection-check.ts +0 -73
- package/tools/context-scope-check.ts +0 -134
- package/tools/context-storage-check.ts +0 -113
- package/tools/cooperation-check.ts +0 -517
- package/tools/cooperation-http-check.ts +0 -125
- package/tools/deepseek-llm-check.ts +0 -147
- package/tools/deepseek-probe.ts +0 -494
- package/tools/diagnosis-check.ts +0 -109
- package/tools/diagnosis-router-check.ts +0 -50
- package/tools/document-context-check.ts +0 -237
- package/tools/envelope-check.ts +0 -659
- package/tools/eslint-rules/index.ts +0 -26
- package/tools/eslint-rules/rules/jsdoc-comment-format.ts +0 -105
- package/tools/eslint-rules/rules/no-function-keyword.ts +0 -60
- package/tools/eslint-rules/rules/no-multiline-parameters.ts +0 -71
- package/tools/eslint-rules/rules/no-multiline-type-parameters.ts +0 -57
- package/tools/eslint-rules/rules/no-undefined-comparison.ts +0 -115
- package/tools/eslint-rules/rules/prefer-template-for-string-conversion.ts +0 -97
- package/tools/eslint-rules/rules/require-typeof-parentheses.ts +0 -49
- package/tools/eslint-rules/rules/require-variable-type.ts +0 -36
- package/tools/eslint-rules/rules/variable-naming.ts +0 -175
- package/tools/eval-recorded-replay.ts +0 -215
- package/tools/eval-replay.ts +0 -139
- package/tools/eval-summary.ts +0 -136
- package/tools/evidence-revalidation-check.ts +0 -235
- package/tools/exec-check.ts +0 -217
- package/tools/federation-check.ts +0 -166
- package/tools/federation-demo.ts +0 -45
- package/tools/filter-check.ts +0 -243
- package/tools/governance-check.ts +0 -680
- package/tools/governance-store-check.ts +0 -131
- package/tools/governed-task-check.ts +0 -227
- package/tools/guard-check.ts +0 -122
- package/tools/http-channel-check.ts +0 -125
- package/tools/import-campaign.ts +0 -28
- package/tools/inquiry-check.ts +0 -126
- package/tools/interaction-check.ts +0 -171
- package/tools/interrupts-check.ts +0 -173
- package/tools/ipc-check.ts +0 -170
- package/tools/kernel-async-check.ts +0 -136
- package/tools/kernel-check.ts +0 -369
- package/tools/kernel-runtime-check.ts +0 -276
- package/tools/keys-check.ts +0 -559
- package/tools/label-audit.ts +0 -70
- package/tools/labels-workflow-check.ts +0 -49
- package/tools/linear-structure-check.ts +0 -524
- package/tools/live-exec.ts +0 -62
- package/tools/live-governance-eval.ts +0 -813
- package/tools/live-legacy.ts +0 -50
- package/tools/lru-check.ts +0 -182
- package/tools/make-annotation-campaign.ts +0 -57
- package/tools/mcp-executor-check.ts +0 -33
- package/tools/merge-annotations.ts +0 -32
- package/tools/merge-check.ts +0 -98
- package/tools/metrics-check.ts +0 -84
- package/tools/net-probe.ts +0 -107
- package/tools/oauth-probe.ts +0 -117
- package/tools/outbox-check.ts +0 -50
- package/tools/persistence-check.ts +0 -144
- package/tools/pin-check.ts +0 -116
- package/tools/platform-check.ts +0 -281
- package/tools/policy-check.ts +0 -514
- package/tools/prettier-plugin/index.mts +0 -84
- package/tools/priority-check.ts +0 -627
- package/tools/probe-context7.ts +0 -63
- package/tools/registry-check.ts +0 -252
- package/tools/reliability-check.ts +0 -25
- package/tools/retry-check.ts +0 -85
- package/tools/rewrite-declaration-extensions.ts +0 -98
- package/tools/runtime-check.ts +0 -165
- package/tools/runtime-deepseek-check.ts +0 -85
- package/tools/runtime-eval.ts +0 -358
- package/tools/scenario-check.ts +0 -161
- package/tools/scenario-incident.ts +0 -162
- package/tools/scoped-priors-check.ts +0 -35
- package/tools/scoped-priors-pooling.ts +0 -34
- package/tools/segment-ast-check.ts +0 -649
- package/tools/session-check.ts +0 -196
- package/tools/session-store-check.ts +0 -73
- package/tools/share-check.ts +0 -88
- package/tools/sign-check.ts +0 -95
- package/tools/signing-check.ts +0 -275
- package/tools/snapshot-demo.ts +0 -65
- package/tools/social-check.ts +0 -146
- package/tools/structure-compare-check.ts +0 -95
- package/tools/support/documentScanner.ts +0 -577
- package/tools/support/texScanner.ts +0 -631
- package/tools/support/workspaceScanner.ts +0 -476
- package/tools/team-check.ts +0 -163
- package/tools/team-demo.ts +0 -36
- package/tools/telemetry-check.ts +0 -44
- package/tools/tex-context-check.ts +0 -263
- package/tools/tool-gate-check.ts +0 -98
- package/tools/tree-structure-check.ts +0 -786
- package/tools/trust-flow-check.ts +0 -158
- package/tools/turn-store-check.ts +0 -57
- package/tools/unified-check.ts +0 -83
- package/tools/validate.ts +0 -309
- package/tools/wire-check.ts +0 -96
- package/tools/workspace-agent-check.ts +0 -486
- package/tools/workspace-agent-demo.ts +0 -62
- package/tools/workspace-agent-server.ts +0 -101
- package/tools/workspace-context-check.ts +0 -331
- package/tools/workspace-context.ts +0 -131
- package/tsconfig.build.json +0 -14
- package/tsconfig.json +0 -24
- package/tsconfig.tools.json +0 -31
- package/viz/context-ast.json +0 -1577
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic workspace writes with compare-and-swap guards and a write journal.
|
|
3
|
+
*
|
|
4
|
+
* The write path is intentionally conservative:
|
|
5
|
+
*
|
|
6
|
+
* - the target is resolved through the session sandbox before any I/O;
|
|
7
|
+
* - the current fingerprint is computed from the bytes that are actually
|
|
8
|
+
* present, so `expectFingerprint` is a real CAS rather than a cached guess;
|
|
9
|
+
* - the new bytes go to a same-directory `.amp-*.tmp` file that is `fsync`ed
|
|
10
|
+
* and renamed over the target, so a reader never observes a half-written
|
|
11
|
+
* file;
|
|
12
|
+
* - the parent chain is created only when `createParents` is set, and every
|
|
13
|
+
* created level is re-checked against the sandbox;
|
|
14
|
+
* - `dryRun` performs every check and builds a projected node without touching
|
|
15
|
+
* the disk.
|
|
16
|
+
*
|
|
17
|
+
* In-process writers serialize on `(sessionId, rootId, relativePath)`. That is
|
|
18
|
+
* not a cross-process lock: a second process is caught by the CAS fingerprint,
|
|
19
|
+
* which is why the journal records both the previous and the next fingerprint.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
22
|
+
import { closeSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { dirname, join } from "node:path";
|
|
24
|
+
import { NamedLocks } from "../concurrency/namedLocks.js";
|
|
25
|
+
import { rawDigest } from "../fingerprint/raw.js";
|
|
26
|
+
import { countLines, decodeText, detectCharset, encodeText } from "../syntax/charset.js";
|
|
27
|
+
import { fail, isFilesystemError } from "./errors.js";
|
|
28
|
+
import { buildFileNode } from "./file.js";
|
|
29
|
+
import { describeFile, extensionOfPath, nameOfPath } from "./metadata.js";
|
|
30
|
+
import { byteRangeFingerprint, locateLineSpan } from "./read.js";
|
|
31
|
+
import { creationTime, formatIsoTime, formatNodeTime } from "./time.js";
|
|
32
|
+
/**
|
|
33
|
+
* Default ceiling for the resulting file size, in bytes.
|
|
34
|
+
*/
|
|
35
|
+
const DefaultWriteBytes = 4 * 1024 * 1024;
|
|
36
|
+
/**
|
|
37
|
+
* Hard ceiling for the resulting file size, regardless of host configuration.
|
|
38
|
+
*/
|
|
39
|
+
const MaxWriteBytes = 64 * 1024 * 1024;
|
|
40
|
+
/**
|
|
41
|
+
* Chunk size used when fingerprinting an existing file without loading it.
|
|
42
|
+
*/
|
|
43
|
+
const HashChunkBytes = 64 * 1024;
|
|
44
|
+
/**
|
|
45
|
+
* Extract an `errno` code from an unknown thrown value without trusting it.
|
|
46
|
+
*/
|
|
47
|
+
const errorCode = (error) => {
|
|
48
|
+
if (typeof (error) !== "object" || error === null) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
const code = error.code;
|
|
52
|
+
return typeof (code) === "string" ? code : "";
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Translate a host filesystem failure into the closed domain taxonomy. The
|
|
56
|
+
* original message is dropped on purpose: it embeds absolute paths.
|
|
57
|
+
*/
|
|
58
|
+
const mapFsError = (error) => {
|
|
59
|
+
if (isFilesystemError(error)) {
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
switch (errorCode(error)) {
|
|
63
|
+
case "ENOENT": {
|
|
64
|
+
return fail("not_found");
|
|
65
|
+
}
|
|
66
|
+
case "EEXIST": {
|
|
67
|
+
return fail("already_exists");
|
|
68
|
+
}
|
|
69
|
+
case "EACCES":
|
|
70
|
+
case "EPERM":
|
|
71
|
+
case "EROFS": {
|
|
72
|
+
return fail("permission_denied");
|
|
73
|
+
}
|
|
74
|
+
case "EISDIR": {
|
|
75
|
+
return fail("not_file");
|
|
76
|
+
}
|
|
77
|
+
case "ENOTDIR": {
|
|
78
|
+
return fail("not_directory");
|
|
79
|
+
}
|
|
80
|
+
case "ENAMETOOLONG": {
|
|
81
|
+
return fail("invalid_path");
|
|
82
|
+
}
|
|
83
|
+
case "ELOOP": {
|
|
84
|
+
return fail("symlink_denied");
|
|
85
|
+
}
|
|
86
|
+
case "ENOSPC":
|
|
87
|
+
case "EDQUOT": {
|
|
88
|
+
return fail("quota_exceeded");
|
|
89
|
+
}
|
|
90
|
+
default: {
|
|
91
|
+
return fail("internal_error");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Run a best-effort cleanup without masking the failure that caused it.
|
|
97
|
+
*/
|
|
98
|
+
const quietly = (action) => {
|
|
99
|
+
try {
|
|
100
|
+
action();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Resolve a positive safe integer option, clamping it to `maximum`.
|
|
108
|
+
*
|
|
109
|
+
* @param value - Caller value, or `undefined` for the default.
|
|
110
|
+
* @param fallback - Default when the value is absent.
|
|
111
|
+
* @param maximum - Inclusive upper bound.
|
|
112
|
+
* @returns The effective value.
|
|
113
|
+
*/
|
|
114
|
+
const positiveInteger = (value, fallback, maximum) => {
|
|
115
|
+
if (value === (void 0)) {
|
|
116
|
+
return fallback;
|
|
117
|
+
}
|
|
118
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
119
|
+
return fail("invalid_arguments");
|
|
120
|
+
}
|
|
121
|
+
return Math.min(value, maximum);
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* `lstat` a path that may legitimately be absent.
|
|
125
|
+
*/
|
|
126
|
+
const statIfExists = (absolute) => {
|
|
127
|
+
try {
|
|
128
|
+
return lstatSync(absolute);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (errorCode(error) === "ENOENT") {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return mapFsError(error);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* `lstat` a path that must exist.
|
|
139
|
+
*/
|
|
140
|
+
const statOrFail = (absolute) => {
|
|
141
|
+
try {
|
|
142
|
+
return lstatSync(absolute);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return mapFsError(error);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Read a file as bytes, translating a failure instead of leaking it.
|
|
150
|
+
*/
|
|
151
|
+
const readRegularFile = (absolute) => {
|
|
152
|
+
try {
|
|
153
|
+
return readFileSync(absolute);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
return mapFsError(error);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Hash a file through a fixed-size buffer. Used for the previous fingerprint so
|
|
161
|
+
* overwriting a large file does not require loading it into memory.
|
|
162
|
+
*/
|
|
163
|
+
const hashRegularFile = (absolute) => {
|
|
164
|
+
const hash = createHash("sha256");
|
|
165
|
+
const buffer = Buffer.allocUnsafe(HashChunkBytes);
|
|
166
|
+
let descriptor = (void 0);
|
|
167
|
+
try {
|
|
168
|
+
descriptor = openSync(absolute, "r");
|
|
169
|
+
let count = readSync(descriptor, buffer, 0, buffer.byteLength, null);
|
|
170
|
+
while (count > 0) {
|
|
171
|
+
hash.update(buffer.subarray(0, count));
|
|
172
|
+
count = readSync(descriptor, buffer, 0, buffer.byteLength, null);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
return mapFsError(error);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
if (descriptor !== (void 0)) {
|
|
180
|
+
const openDescriptor = descriptor;
|
|
181
|
+
quietly(() => closeSync(openDescriptor));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return `sha256:${hash.digest("hex")}`;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Decode with a detected charset. A decoder failure is reported as
|
|
188
|
+
* `charset_unsupported`; a classified domain error passes through unchanged.
|
|
189
|
+
*/
|
|
190
|
+
const decodePreviewText = (bytes, charset) => {
|
|
191
|
+
try {
|
|
192
|
+
return decodeText(bytes, charset);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (isFilesystemError(error)) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
return fail("charset_unsupported");
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Build the node a successful write would produce, without writing.
|
|
203
|
+
*
|
|
204
|
+
* When the target exists its current node is the base, so descriptions and
|
|
205
|
+
* timestamps survive; only the content-derived fields are projected from the
|
|
206
|
+
* new bytes. A new target gets a minimal node whose timestamps are "now",
|
|
207
|
+
* because there is no file to inspect yet; its descriptive fields are still
|
|
208
|
+
* derived from the new bytes instead of being left empty.
|
|
209
|
+
*/
|
|
210
|
+
const previewFileNode = (rooted, bytes, timeZone) => {
|
|
211
|
+
const detected = detectCharset(bytes);
|
|
212
|
+
const charset = detected.binary ? "binary" : detected.charset;
|
|
213
|
+
const lines = detected.binary ? 0n : countLines(decodePreviewText(bytes, charset));
|
|
214
|
+
const fingerprint = rawDigest(bytes);
|
|
215
|
+
const byteCount = BigInt(bytes.byteLength);
|
|
216
|
+
const current = statIfExists(rooted.absolute);
|
|
217
|
+
if (current !== (void 0) && current.isFile()) {
|
|
218
|
+
const existing = buildFileNode(rooted.root, rooted.relative, { timeZone });
|
|
219
|
+
return { ...existing, charset, lines, bytes: byteCount, fingerprint };
|
|
220
|
+
}
|
|
221
|
+
const described = describeFile(rooted.absolute, rooted.relative, charset, bytes);
|
|
222
|
+
const now = new Date();
|
|
223
|
+
return {
|
|
224
|
+
type: "File",
|
|
225
|
+
path: rooted.relative,
|
|
226
|
+
name: nameOfPath(rooted.relative),
|
|
227
|
+
extension: extensionOfPath(rooted.relative),
|
|
228
|
+
children: [],
|
|
229
|
+
fingerprint,
|
|
230
|
+
charset,
|
|
231
|
+
lines,
|
|
232
|
+
bytes: byteCount,
|
|
233
|
+
create: formatNodeTime(now, timeZone),
|
|
234
|
+
modify: formatNodeTime(now, timeZone),
|
|
235
|
+
description: described.description,
|
|
236
|
+
summary: described.summary,
|
|
237
|
+
purpose: described.purpose,
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Normalize and validate a write mode. `upsert` is the default because a write
|
|
242
|
+
* is an assignment: it should not fail merely because the file is new.
|
|
243
|
+
*/
|
|
244
|
+
const normalizeMode = (mode) => {
|
|
245
|
+
if (mode === (void 0)) {
|
|
246
|
+
return "upsert";
|
|
247
|
+
}
|
|
248
|
+
switch (mode) {
|
|
249
|
+
case "create":
|
|
250
|
+
case "overwrite":
|
|
251
|
+
case "upsert":
|
|
252
|
+
case "append": {
|
|
253
|
+
return mode;
|
|
254
|
+
}
|
|
255
|
+
default: {
|
|
256
|
+
return fail("invalid_arguments");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* Validate a text encoding name.
|
|
262
|
+
*/
|
|
263
|
+
const normalizeEncoding = (encoding) => {
|
|
264
|
+
if (encoding === (void 0)) {
|
|
265
|
+
return "utf-8";
|
|
266
|
+
}
|
|
267
|
+
switch (encoding) {
|
|
268
|
+
case "utf-8":
|
|
269
|
+
case "utf-8-bom":
|
|
270
|
+
case "utf-16le":
|
|
271
|
+
case "utf-16be":
|
|
272
|
+
case "latin1": {
|
|
273
|
+
return encoding;
|
|
274
|
+
}
|
|
275
|
+
default: {
|
|
276
|
+
return fail("invalid_arguments");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Encode the request content. Raw bytes may only be written as UTF-8 (the
|
|
282
|
+
* default); declaring another encoding for already-encoded bytes is a caller
|
|
283
|
+
* bug, not something to guess about.
|
|
284
|
+
*/
|
|
285
|
+
const encodeContent = (content, encoding) => {
|
|
286
|
+
if (typeof (content) === "string") {
|
|
287
|
+
return encodeText(content, normalizeEncoding(encoding));
|
|
288
|
+
}
|
|
289
|
+
if (content instanceof Uint8Array) {
|
|
290
|
+
if (encoding !== (void 0) && encoding !== "utf-8") {
|
|
291
|
+
return fail("invalid_arguments");
|
|
292
|
+
}
|
|
293
|
+
return content;
|
|
294
|
+
}
|
|
295
|
+
return fail("invalid_arguments");
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Enforce the compare-and-swap precondition. `undefined` skips the check,
|
|
299
|
+
* `null` requires an absent file and a string must match exactly.
|
|
300
|
+
*/
|
|
301
|
+
const checkCas = (expected, previous) => {
|
|
302
|
+
if (expected === (void 0)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (expected !== null && typeof (expected) !== "string") {
|
|
306
|
+
return fail("invalid_arguments");
|
|
307
|
+
}
|
|
308
|
+
if (expected === null) {
|
|
309
|
+
if (previous !== null) {
|
|
310
|
+
return fail("fingerprint_mismatch");
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (previous !== expected) {
|
|
315
|
+
return fail("fingerprint_mismatch");
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Enforce the mode precondition against the observed file state.
|
|
320
|
+
*/
|
|
321
|
+
const checkMode = (mode, exists) => {
|
|
322
|
+
if (mode === "create" && exists) {
|
|
323
|
+
return fail("already_exists");
|
|
324
|
+
}
|
|
325
|
+
if (mode === "overwrite" && !exists) {
|
|
326
|
+
return fail("not_found");
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Assemble the bytes that end up on disk. Append reads the previous bytes and
|
|
331
|
+
* concatenates; every other mode writes exactly the encoded fragment. The
|
|
332
|
+
* size ceiling applies to the result, so an append cannot grow past it.
|
|
333
|
+
*/
|
|
334
|
+
const finalContent = (absolute, encoded, mode, current, maxBytes) => {
|
|
335
|
+
if (mode === "append" && current !== (void 0)) {
|
|
336
|
+
if (current.size + encoded.byteLength > maxBytes) {
|
|
337
|
+
return fail("quota_exceeded");
|
|
338
|
+
}
|
|
339
|
+
const previous = readRegularFile(absolute);
|
|
340
|
+
const combined = new Uint8Array(previous.byteLength + encoded.byteLength);
|
|
341
|
+
combined.set(previous, 0);
|
|
342
|
+
combined.set(encoded, previous.byteLength);
|
|
343
|
+
return combined;
|
|
344
|
+
}
|
|
345
|
+
if (encoded.byteLength > maxBytes) {
|
|
346
|
+
return fail("quota_exceeded");
|
|
347
|
+
}
|
|
348
|
+
return encoded;
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Create one missing directory level after checking it against the sandbox.
|
|
352
|
+
*/
|
|
353
|
+
const makeDirectoryChain = (sandbox, root, relative) => {
|
|
354
|
+
const segments = relative.split("/");
|
|
355
|
+
for (let index = 1; index <= segments.length; index += 1) {
|
|
356
|
+
const current = segments.slice(0, index).join("/");
|
|
357
|
+
const absolute = join(root.path, current);
|
|
358
|
+
if (!sandbox.contains(root.path, absolute)) {
|
|
359
|
+
return fail("path_escape");
|
|
360
|
+
}
|
|
361
|
+
sandbox.revalidate({ root, relative: current, absolute });
|
|
362
|
+
const stat = statIfExists(absolute);
|
|
363
|
+
if (stat !== (void 0)) {
|
|
364
|
+
if (!stat.isDirectory()) {
|
|
365
|
+
return fail("not_directory");
|
|
366
|
+
}
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
mkdirSync(absolute);
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
if (errorCode(error) !== "EEXIST") {
|
|
374
|
+
return mapFsError(error);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Validate the existing prefix of a directory chain without creating anything.
|
|
381
|
+
* Used by `dryRun`, which must reject a non-directory ancestor but must not
|
|
382
|
+
* mutate the disk.
|
|
383
|
+
*/
|
|
384
|
+
const validateDirectoryChain = (sandbox, root, relative) => {
|
|
385
|
+
const segments = relative.split("/");
|
|
386
|
+
for (let index = 1; index <= segments.length; index += 1) {
|
|
387
|
+
const current = segments.slice(0, index).join("/");
|
|
388
|
+
const absolute = join(root.path, current);
|
|
389
|
+
if (!sandbox.contains(root.path, absolute)) {
|
|
390
|
+
return fail("path_escape");
|
|
391
|
+
}
|
|
392
|
+
sandbox.revalidate({ root, relative: current, absolute });
|
|
393
|
+
const stat = statIfExists(absolute);
|
|
394
|
+
if (stat === (void 0)) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (!stat.isDirectory()) {
|
|
398
|
+
return fail("not_directory");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Ensure the target's parent directory exists (or would be creatable).
|
|
404
|
+
*/
|
|
405
|
+
const prepareParent = (sandbox, rooted, createParents, materialize) => {
|
|
406
|
+
const segments = rooted.relative.split("/");
|
|
407
|
+
if (segments.length <= 1) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const parent = segments.slice(0, -1).join("/");
|
|
411
|
+
if (createParents) {
|
|
412
|
+
if (materialize) {
|
|
413
|
+
makeDirectoryChain(sandbox, rooted.root, parent);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
validateDirectoryChain(sandbox, rooted.root, parent);
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const absolute = join(rooted.root.path, parent);
|
|
421
|
+
if (!sandbox.contains(rooted.root.path, absolute)) {
|
|
422
|
+
return fail("path_escape");
|
|
423
|
+
}
|
|
424
|
+
sandbox.revalidate({ root: rooted.root, relative: parent, absolute });
|
|
425
|
+
const stat = statIfExists(absolute);
|
|
426
|
+
if (stat === (void 0)) {
|
|
427
|
+
return fail("not_found");
|
|
428
|
+
}
|
|
429
|
+
if (!stat.isDirectory()) {
|
|
430
|
+
return fail("not_directory");
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* Write bytes through a same-directory temporary file: write, `fsync`, close,
|
|
435
|
+
* then `rename` over the target. A failure before the rename removes the
|
|
436
|
+
* temporary file; the target is never truncated in place.
|
|
437
|
+
*/
|
|
438
|
+
const atomicWrite = (absolute, bytes, modeBits) => {
|
|
439
|
+
const temp = join(dirname(absolute), `.amp-${randomBytes(8).toString("hex")}.tmp`);
|
|
440
|
+
let descriptor = (void 0);
|
|
441
|
+
let renamed = false;
|
|
442
|
+
try {
|
|
443
|
+
descriptor = openSync(temp, "wx", modeBits === (void 0) ? 0o666 : modeBits);
|
|
444
|
+
writeFileSync(descriptor, bytes);
|
|
445
|
+
fsyncSync(descriptor);
|
|
446
|
+
closeSync(descriptor);
|
|
447
|
+
descriptor = (void 0);
|
|
448
|
+
renameSync(temp, absolute);
|
|
449
|
+
renamed = true;
|
|
450
|
+
}
|
|
451
|
+
finally {
|
|
452
|
+
if (descriptor !== (void 0)) {
|
|
453
|
+
const openDescriptor = descriptor;
|
|
454
|
+
quietly(() => closeSync(openDescriptor));
|
|
455
|
+
}
|
|
456
|
+
if (!renamed) {
|
|
457
|
+
quietly(() => rmSync(temp, { force: true }));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Write locks keyed by `(sessionId, rootId, relativePath)`.
|
|
463
|
+
*
|
|
464
|
+
* The class is exported so a host can own one registry and share it with other
|
|
465
|
+
* workspace components; {@link writeFile} uses a process-wide registry because
|
|
466
|
+
* its frozen signature carries no lock parameter.
|
|
467
|
+
*/
|
|
468
|
+
export class WriteLocks {
|
|
469
|
+
locks = new NamedLocks();
|
|
470
|
+
async run(name, fn) {
|
|
471
|
+
return this.locks.run(name, fn);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Number of distinct lock names seen so far.
|
|
475
|
+
*/
|
|
476
|
+
get size() {
|
|
477
|
+
return this.locks.size;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Process-wide lock registry used by {@link writeFile}.
|
|
482
|
+
*/
|
|
483
|
+
const WriteLockRegistry = new WriteLocks();
|
|
484
|
+
/**
|
|
485
|
+
* Run the checks and the write while holding the path lock. Journal creation is
|
|
486
|
+
* metadata only; the callback and the tree refresh run after the lock is
|
|
487
|
+
* released so a callback can safely write the same path again.
|
|
488
|
+
*/
|
|
489
|
+
const writeLocked = (context) => {
|
|
490
|
+
context.sandbox.revalidate(context.rooted);
|
|
491
|
+
const current = statIfExists(context.rooted.absolute);
|
|
492
|
+
if (current !== (void 0)) {
|
|
493
|
+
if (current.isSymbolicLink()) {
|
|
494
|
+
return fail("symlink_denied");
|
|
495
|
+
}
|
|
496
|
+
if (!current.isFile()) {
|
|
497
|
+
return fail("not_file");
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const previousFingerprint = current === (void 0) ? null : hashRegularFile(context.rooted.absolute);
|
|
501
|
+
checkCas(context.request.expectFingerprint, previousFingerprint);
|
|
502
|
+
checkMode(context.mode, current !== (void 0));
|
|
503
|
+
const encoded = encodeContent(context.content, context.request.encoding);
|
|
504
|
+
const bytes = finalContent(context.rooted.absolute, encoded, context.mode, current, context.maxBytes);
|
|
505
|
+
prepareParent(context.sandbox, context.rooted, context.createParents, !context.dryRun);
|
|
506
|
+
const created = current === (void 0);
|
|
507
|
+
context.sandbox.revalidate(context.rooted);
|
|
508
|
+
if (context.dryRun) {
|
|
509
|
+
const preview = previewFileNode(context.rooted, bytes, context.timeZone);
|
|
510
|
+
return {
|
|
511
|
+
result: {
|
|
512
|
+
node: preview,
|
|
513
|
+
previousFingerprint,
|
|
514
|
+
created,
|
|
515
|
+
dryRun: true,
|
|
516
|
+
bytesWritten: BigInt(bytes.byteLength),
|
|
517
|
+
revision: preview.fingerprint,
|
|
518
|
+
},
|
|
519
|
+
journal: (void 0),
|
|
520
|
+
relative: context.rooted.relative,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
atomicWrite(context.rooted.absolute, bytes, current === (void 0) ? (void 0) : current.mode & 0o777);
|
|
524
|
+
context.sandbox.revalidate(context.rooted);
|
|
525
|
+
const nextStat = statOrFail(context.rooted.absolute);
|
|
526
|
+
const node = fileNodeFromBytes(context.rooted, bytes, nextStat, context.timeZone);
|
|
527
|
+
let journal = (void 0);
|
|
528
|
+
if (context.options.journal !== (void 0)) {
|
|
529
|
+
journal = {
|
|
530
|
+
id: `w.${randomBytes(8).toString("hex")}`,
|
|
531
|
+
sessionId: context.sessionId,
|
|
532
|
+
rootId: context.rooted.root.id,
|
|
533
|
+
path: context.rooted.relative,
|
|
534
|
+
mode: context.mode,
|
|
535
|
+
previousFingerprint,
|
|
536
|
+
nextFingerprint: rawDigest(bytes),
|
|
537
|
+
bytes: BigInt(bytes.byteLength),
|
|
538
|
+
at: formatIsoTime(new Date()),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
result: {
|
|
543
|
+
node,
|
|
544
|
+
previousFingerprint,
|
|
545
|
+
created,
|
|
546
|
+
dryRun: false,
|
|
547
|
+
bytesWritten: BigInt(bytes.byteLength),
|
|
548
|
+
revision: node.fingerprint,
|
|
549
|
+
},
|
|
550
|
+
journal,
|
|
551
|
+
relative: context.rooted.relative,
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
/**
|
|
555
|
+
* Publish the journal record and refresh the tree after the lock is released.
|
|
556
|
+
*/
|
|
557
|
+
const publishWrite = (commit, options) => {
|
|
558
|
+
if (commit.result.dryRun) {
|
|
559
|
+
return commit.result;
|
|
560
|
+
}
|
|
561
|
+
if (options.journal !== (void 0) && commit.journal !== (void 0)) {
|
|
562
|
+
options.journal(commit.journal);
|
|
563
|
+
}
|
|
564
|
+
const tree = options.tree;
|
|
565
|
+
if (tree === (void 0)) {
|
|
566
|
+
return commit.result;
|
|
567
|
+
}
|
|
568
|
+
return { ...commit.result, revision: tree.refresh(commit.relative).revision };
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* Write one workspace file atomically.
|
|
572
|
+
*
|
|
573
|
+
* Order of checks matches the contract: resolve, lock, read the current file
|
|
574
|
+
* and its fingerprint, CAS, mode, content encoding, size ceiling, parent
|
|
575
|
+
* directory, then the atomic rename. A `dryRun` stops right after the parent
|
|
576
|
+
* check and returns the projected node instead of writing.
|
|
577
|
+
*
|
|
578
|
+
* @param sandbox - Session sandbox that owns path resolution.
|
|
579
|
+
* @param sessionId - Public session handle used for the lock and the journal.
|
|
580
|
+
* @param request - Path, content, mode, CAS precondition and dry-run flag.
|
|
581
|
+
* @param options - Size ceiling, journal sink, tree and timestamp mode.
|
|
582
|
+
* @returns The new node, previous fingerprint and write metadata.
|
|
583
|
+
*/
|
|
584
|
+
export const writeFile = async (sandbox, sessionId, request, options = {}) => {
|
|
585
|
+
if (typeof (sessionId) !== "string" || sessionId.length === 0 || sessionId.includes("\u0000")) {
|
|
586
|
+
return fail("invalid_arguments");
|
|
587
|
+
}
|
|
588
|
+
if (typeof (request) !== "object" || request === null) {
|
|
589
|
+
return fail("invalid_arguments");
|
|
590
|
+
}
|
|
591
|
+
const settings = options === (void 0) || options === null ? {} : options;
|
|
592
|
+
const mode = normalizeMode(request.mode);
|
|
593
|
+
const maxBytes = positiveInteger(settings.maxBytes, DefaultWriteBytes, MaxWriteBytes);
|
|
594
|
+
const rooted = sandbox.resolve(request.path, mode === "create" ? "create" : "write");
|
|
595
|
+
if (rooted.relative === ".") {
|
|
596
|
+
return fail("not_file");
|
|
597
|
+
}
|
|
598
|
+
const content = request.content;
|
|
599
|
+
const snapshotContent = typeof (content) === "string" ? content : content instanceof Uint8Array ? new Uint8Array(content) : content;
|
|
600
|
+
const timeZone = settings.timeZone === "utc" ? "utc" : "local";
|
|
601
|
+
const context = {
|
|
602
|
+
sandbox,
|
|
603
|
+
sessionId,
|
|
604
|
+
rooted,
|
|
605
|
+
mode,
|
|
606
|
+
request,
|
|
607
|
+
content: snapshotContent,
|
|
608
|
+
options: settings,
|
|
609
|
+
maxBytes,
|
|
610
|
+
dryRun: request.dryRun === true,
|
|
611
|
+
createParents: request.createParents === true,
|
|
612
|
+
timeZone,
|
|
613
|
+
};
|
|
614
|
+
const lockName = `${sessionId}\u0000${rooted.root.id}\u0000${rooted.relative}`;
|
|
615
|
+
const commit = await WriteLockRegistry.run(lockName, () => writeLocked(context)).catch((error) => mapFsError(error));
|
|
616
|
+
try {
|
|
617
|
+
return publishWrite(commit, settings);
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
return mapFsError(error);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
/**
|
|
624
|
+
* Validate a raw byte offset.
|
|
625
|
+
*/
|
|
626
|
+
const patchByteOffset = (value) => {
|
|
627
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
628
|
+
return fail("invalid_arguments");
|
|
629
|
+
}
|
|
630
|
+
return value;
|
|
631
|
+
};
|
|
632
|
+
/**
|
|
633
|
+
* Validate a 1-based start line.
|
|
634
|
+
*/
|
|
635
|
+
const patchStartLine = (value) => {
|
|
636
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
637
|
+
return fail("invalid_arguments");
|
|
638
|
+
}
|
|
639
|
+
return value;
|
|
640
|
+
};
|
|
641
|
+
/**
|
|
642
|
+
* Validate an inclusive end line; `0` is the insertion point before line 1.
|
|
643
|
+
*/
|
|
644
|
+
const patchEndLine = (value) => {
|
|
645
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
646
|
+
return fail("invalid_arguments");
|
|
647
|
+
}
|
|
648
|
+
return value;
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* A range patch cannot be unsafe: the whole-file fingerprint is mandatory.
|
|
652
|
+
*/
|
|
653
|
+
const requiredFingerprint = (value) => typeof (value) === "string" && value.length > 0 ? value : fail("invalid_arguments");
|
|
654
|
+
/**
|
|
655
|
+
* Optional secondary range fingerprint.
|
|
656
|
+
*/
|
|
657
|
+
const optionalFingerprint = (value) => value === (void 0) ? (void 0) : requiredFingerprint(value);
|
|
658
|
+
/**
|
|
659
|
+
* Encode line replacement content in the file's own charset without a BOM. A
|
|
660
|
+
* BOM belongs to the file, never to an inserted fragment.
|
|
661
|
+
*/
|
|
662
|
+
const encodeLineBody = (text, charset) => {
|
|
663
|
+
switch (charset) {
|
|
664
|
+
case "utf-8":
|
|
665
|
+
case "utf-8-bom": {
|
|
666
|
+
return new TextEncoder().encode(text);
|
|
667
|
+
}
|
|
668
|
+
case "utf-16le": {
|
|
669
|
+
return new Uint8Array(Buffer.from(text, "utf16le"));
|
|
670
|
+
}
|
|
671
|
+
case "utf-16be": {
|
|
672
|
+
const body = Buffer.from(text, "utf16le");
|
|
673
|
+
body.swap16();
|
|
674
|
+
return new Uint8Array(body);
|
|
675
|
+
}
|
|
676
|
+
case "latin1": {
|
|
677
|
+
return encodeText(text, "latin1");
|
|
678
|
+
}
|
|
679
|
+
default: {
|
|
680
|
+
return fail("charset_unsupported");
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* Replace one validated raw byte span in memory.
|
|
686
|
+
*/
|
|
687
|
+
const spliceBytes = (bytes, startByte, endByte, replacement) => {
|
|
688
|
+
const result = new Uint8Array(bytes.byteLength - (endByte - startByte) + replacement.byteLength);
|
|
689
|
+
result.set(bytes.subarray(0, startByte), 0);
|
|
690
|
+
result.set(replacement, startByte);
|
|
691
|
+
result.set(bytes.subarray(endByte), startByte + replacement.byteLength);
|
|
692
|
+
return result;
|
|
693
|
+
};
|
|
694
|
+
/**
|
|
695
|
+
* Build the node for already-known bytes plus a stat. Used by range patches so
|
|
696
|
+
* a committed write does not have to read and hash the file a third time.
|
|
697
|
+
*/
|
|
698
|
+
const fileNodeFromBytes = (rooted, bytes, stat, timeZone) => {
|
|
699
|
+
const detected = detectCharset(bytes);
|
|
700
|
+
let charset = detected.binary ? "binary" : detected.charset;
|
|
701
|
+
let lines = 0n;
|
|
702
|
+
if (charset !== "binary") {
|
|
703
|
+
try {
|
|
704
|
+
lines = countLines(decodeText(bytes, charset));
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
charset = "binary";
|
|
708
|
+
lines = 0n;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
const described = describeFile(rooted.absolute, rooted.relative, charset, bytes);
|
|
712
|
+
return {
|
|
713
|
+
type: "File",
|
|
714
|
+
path: rooted.relative,
|
|
715
|
+
name: nameOfPath(rooted.relative),
|
|
716
|
+
extension: extensionOfPath(rooted.relative),
|
|
717
|
+
children: [],
|
|
718
|
+
fingerprint: rawDigest(bytes),
|
|
719
|
+
charset,
|
|
720
|
+
lines,
|
|
721
|
+
bytes: BigInt(bytes.byteLength),
|
|
722
|
+
create: formatNodeTime(creationTime(stat), timeZone),
|
|
723
|
+
modify: formatNodeTime(new Date(stat.mtimeMs), timeZone),
|
|
724
|
+
description: described.description,
|
|
725
|
+
summary: described.summary,
|
|
726
|
+
purpose: described.purpose,
|
|
727
|
+
};
|
|
728
|
+
};
|
|
729
|
+
/**
|
|
730
|
+
* Execute one patch while the path lock is held. The file must already exist;
|
|
731
|
+
* the whole-file fingerprint is checked first, then the optional range
|
|
732
|
+
* fingerprint, then the in-memory splice and the atomic rename.
|
|
733
|
+
*/
|
|
734
|
+
const patchLocked = (context, planner) => {
|
|
735
|
+
context.sandbox.revalidate(context.rooted);
|
|
736
|
+
const current = statIfExists(context.rooted.absolute);
|
|
737
|
+
if (current === (void 0)) {
|
|
738
|
+
return fail("not_found");
|
|
739
|
+
}
|
|
740
|
+
if (current.isSymbolicLink()) {
|
|
741
|
+
return fail("symlink_denied");
|
|
742
|
+
}
|
|
743
|
+
if (!current.isFile()) {
|
|
744
|
+
return fail("not_file");
|
|
745
|
+
}
|
|
746
|
+
if (current.size > MaxWriteBytes) {
|
|
747
|
+
return fail("quota_exceeded");
|
|
748
|
+
}
|
|
749
|
+
const bytes = readRegularFile(context.rooted.absolute);
|
|
750
|
+
if (bytes.byteLength > MaxWriteBytes) {
|
|
751
|
+
return fail("quota_exceeded");
|
|
752
|
+
}
|
|
753
|
+
const previousFingerprint = rawDigest(bytes);
|
|
754
|
+
if (previousFingerprint !== context.expectFingerprint) {
|
|
755
|
+
return fail("fingerprint_mismatch");
|
|
756
|
+
}
|
|
757
|
+
const plan = planner(bytes);
|
|
758
|
+
if (!Number.isSafeInteger(plan.startByte) || plan.startByte < 0 || !Number.isSafeInteger(plan.endByte) || plan.endByte < plan.startByte || plan.endByte > bytes.byteLength) {
|
|
759
|
+
return fail("invalid_arguments");
|
|
760
|
+
}
|
|
761
|
+
const previousRangeFingerprint = byteRangeFingerprint(plan.startByte, plan.endByte, bytes.subarray(plan.startByte, plan.endByte));
|
|
762
|
+
if (context.expectRangeFingerprint !== (void 0) && previousRangeFingerprint !== context.expectRangeFingerprint) {
|
|
763
|
+
return fail("fingerprint_mismatch");
|
|
764
|
+
}
|
|
765
|
+
const nextBytes = spliceBytes(bytes, plan.startByte, plan.endByte, plan.replacement);
|
|
766
|
+
if (nextBytes.byteLength > context.maxBytes) {
|
|
767
|
+
return fail("quota_exceeded");
|
|
768
|
+
}
|
|
769
|
+
const nextFingerprint = rawDigest(nextBytes);
|
|
770
|
+
const nextStartByte = plan.startByte;
|
|
771
|
+
const nextEndByte = plan.startByte + plan.replacement.byteLength;
|
|
772
|
+
const nextRangeFingerprint = byteRangeFingerprint(nextStartByte, nextEndByte, nextBytes.subarray(nextStartByte, nextEndByte));
|
|
773
|
+
const previousTotalBytes = BigInt(bytes.byteLength);
|
|
774
|
+
const nextTotalBytes = BigInt(nextBytes.byteLength);
|
|
775
|
+
const baseResult = {
|
|
776
|
+
node: fileNodeFromBytes(context.rooted, nextBytes, current, context.timeZone),
|
|
777
|
+
previousFingerprint,
|
|
778
|
+
created: false,
|
|
779
|
+
dryRun: context.dryRun,
|
|
780
|
+
bytesWritten: nextTotalBytes,
|
|
781
|
+
revision: nextFingerprint,
|
|
782
|
+
kind: context.kind,
|
|
783
|
+
startByte: plan.startByte,
|
|
784
|
+
endByte: plan.endByte,
|
|
785
|
+
startLine: plan.startLine,
|
|
786
|
+
endLine: plan.endLine,
|
|
787
|
+
previousRangeFingerprint,
|
|
788
|
+
nextRangeFingerprint,
|
|
789
|
+
nextStartByte,
|
|
790
|
+
nextEndByte,
|
|
791
|
+
previousTotalBytes,
|
|
792
|
+
nextTotalBytes,
|
|
793
|
+
};
|
|
794
|
+
if (context.dryRun) {
|
|
795
|
+
return { result: baseResult, journal: (void 0), patchJournal: (void 0), relative: context.rooted.relative };
|
|
796
|
+
}
|
|
797
|
+
context.sandbox.revalidate(context.rooted);
|
|
798
|
+
atomicWrite(context.rooted.absolute, nextBytes, current.mode & 0o777);
|
|
799
|
+
context.sandbox.revalidate(context.rooted);
|
|
800
|
+
const nextStat = statOrFail(context.rooted.absolute);
|
|
801
|
+
const node = fileNodeFromBytes(context.rooted, nextBytes, nextStat, context.timeZone);
|
|
802
|
+
const id = `w.${randomBytes(8).toString("hex")}`;
|
|
803
|
+
const at = formatIsoTime(new Date());
|
|
804
|
+
const journal = {
|
|
805
|
+
id,
|
|
806
|
+
sessionId: context.sessionId,
|
|
807
|
+
rootId: context.rooted.root.id,
|
|
808
|
+
path: context.rooted.relative,
|
|
809
|
+
mode: "overwrite",
|
|
810
|
+
previousFingerprint,
|
|
811
|
+
nextFingerprint,
|
|
812
|
+
bytes: nextTotalBytes,
|
|
813
|
+
at,
|
|
814
|
+
};
|
|
815
|
+
const patchJournal = {
|
|
816
|
+
...journal,
|
|
817
|
+
kind: context.kind,
|
|
818
|
+
startByte: plan.startByte,
|
|
819
|
+
endByte: plan.endByte,
|
|
820
|
+
startLine: plan.startLine,
|
|
821
|
+
endLine: plan.endLine,
|
|
822
|
+
previousRangeFingerprint,
|
|
823
|
+
nextRangeFingerprint,
|
|
824
|
+
nextStartByte,
|
|
825
|
+
nextEndByte,
|
|
826
|
+
previousTotalBytes,
|
|
827
|
+
nextTotalBytes,
|
|
828
|
+
};
|
|
829
|
+
return { result: { ...baseResult, node, revision: node.fingerprint }, journal, patchJournal, relative: context.rooted.relative };
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* Publish the journal entries and refresh the tree after the lock is released.
|
|
833
|
+
* Journal callbacks must not run under the non-reentrant write lock: a callback
|
|
834
|
+
* that writes the same path would otherwise deadlock.
|
|
835
|
+
*/
|
|
836
|
+
const publishPatch = (commit, options) => {
|
|
837
|
+
if (commit.result.dryRun) {
|
|
838
|
+
return commit.result;
|
|
839
|
+
}
|
|
840
|
+
if (options.journal !== (void 0) && commit.journal !== (void 0)) {
|
|
841
|
+
options.journal(commit.journal);
|
|
842
|
+
}
|
|
843
|
+
if (options.patchJournal !== (void 0) && commit.patchJournal !== (void 0)) {
|
|
844
|
+
options.patchJournal(commit.patchJournal);
|
|
845
|
+
}
|
|
846
|
+
const tree = options.tree;
|
|
847
|
+
if (tree === (void 0)) {
|
|
848
|
+
return commit.result;
|
|
849
|
+
}
|
|
850
|
+
return { ...commit.result, revision: tree.refresh(commit.relative).revision };
|
|
851
|
+
};
|
|
852
|
+
/**
|
|
853
|
+
* Replace a raw byte range of an existing file.
|
|
854
|
+
*
|
|
855
|
+
* The whole-file `expectFingerprint` is mandatory and is verified from the bytes
|
|
856
|
+
* read under the lock; `expectRangeFingerprint` is a secondary check. The new
|
|
857
|
+
* file is assembled in memory, written through a same-directory temporary file,
|
|
858
|
+
* `fsync`ed and renamed, so a reader never observes a partial file.
|
|
859
|
+
*
|
|
860
|
+
* @param sandbox - Session sandbox that owns path resolution.
|
|
861
|
+
* @param sessionId - Public session handle used for the lock and journal.
|
|
862
|
+
* @param request - Path, half-open byte range, replacement bytes and CAS guards.
|
|
863
|
+
* @param options - Size ceiling, journal sinks, tree and timestamp mode.
|
|
864
|
+
* @returns Patch result with both fingerprint pairs and the new node.
|
|
865
|
+
*/
|
|
866
|
+
export const useByte = async (sandbox, sessionId, request, options = {}) => {
|
|
867
|
+
if (typeof (sessionId) !== "string" || sessionId.length === 0 || sessionId.includes("\u0000")) {
|
|
868
|
+
return fail("invalid_arguments");
|
|
869
|
+
}
|
|
870
|
+
if (typeof (request) !== "object" || request === null) {
|
|
871
|
+
return fail("invalid_arguments");
|
|
872
|
+
}
|
|
873
|
+
const settings = options === (void 0) || options === null ? {} : options;
|
|
874
|
+
const expectFingerprint = requiredFingerprint(request.expectFingerprint);
|
|
875
|
+
const expectRangeFingerprint = optionalFingerprint(request.expectRangeFingerprint);
|
|
876
|
+
if (!(request.content instanceof Uint8Array)) {
|
|
877
|
+
return fail("invalid_arguments");
|
|
878
|
+
}
|
|
879
|
+
const replacement = new Uint8Array(request.content);
|
|
880
|
+
const startByte = patchByteOffset(request.startByte);
|
|
881
|
+
const endByte = patchByteOffset(request.endByte);
|
|
882
|
+
if (endByte < startByte) {
|
|
883
|
+
return fail("invalid_arguments");
|
|
884
|
+
}
|
|
885
|
+
const maxBytes = positiveInteger(settings.maxBytes, DefaultWriteBytes, MaxWriteBytes);
|
|
886
|
+
const rooted = sandbox.resolve(request.path, "write");
|
|
887
|
+
if (rooted.relative === ".") {
|
|
888
|
+
return fail("not_file");
|
|
889
|
+
}
|
|
890
|
+
const context = {
|
|
891
|
+
sandbox,
|
|
892
|
+
sessionId,
|
|
893
|
+
rooted,
|
|
894
|
+
kind: "byte",
|
|
895
|
+
expectFingerprint,
|
|
896
|
+
expectRangeFingerprint,
|
|
897
|
+
dryRun: request.dryRun === true,
|
|
898
|
+
maxBytes,
|
|
899
|
+
timeZone: settings.timeZone === "utc" ? "utc" : "local",
|
|
900
|
+
};
|
|
901
|
+
const planner = (bytes) => {
|
|
902
|
+
if (endByte > bytes.byteLength) {
|
|
903
|
+
return fail("invalid_arguments");
|
|
904
|
+
}
|
|
905
|
+
return { kind: "byte", startByte, endByte, replacement };
|
|
906
|
+
};
|
|
907
|
+
const lockName = `${sessionId}\u0000${rooted.root.id}\u0000${rooted.relative}`;
|
|
908
|
+
const commit = await WriteLockRegistry.run(lockName, () => patchLocked(context, planner)).catch((error) => mapFsError(error));
|
|
909
|
+
try {
|
|
910
|
+
return publishPatch(commit, settings);
|
|
911
|
+
}
|
|
912
|
+
catch (error) {
|
|
913
|
+
return mapFsError(error);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
/**
|
|
917
|
+
* Replace a line range of an existing text file.
|
|
918
|
+
*
|
|
919
|
+
* Line numbers are 1-based and inclusive. The replaced span starts at the first
|
|
920
|
+
* character of `startLine` and includes the terminator of `endLine` when one
|
|
921
|
+
* exists, which means `content` is inserted verbatim: include the desired
|
|
922
|
+
* newline when more lines follow. Use `endLine = startLine - 1` to insert.
|
|
923
|
+
*
|
|
924
|
+
* @param sandbox - Session sandbox that owns path resolution.
|
|
925
|
+
* @param sessionId - Public session handle used for the lock and journal.
|
|
926
|
+
* @param request - Path, inclusive line range, replacement text and CAS guards.
|
|
927
|
+
* @param options - Size ceiling, journal sinks, tree and timestamp mode.
|
|
928
|
+
* @returns Patch result with both fingerprint pairs and the new node.
|
|
929
|
+
*/
|
|
930
|
+
export const useLine = async (sandbox, sessionId, request, options = {}) => {
|
|
931
|
+
if (typeof (sessionId) !== "string" || sessionId.length === 0 || sessionId.includes("\u0000")) {
|
|
932
|
+
return fail("invalid_arguments");
|
|
933
|
+
}
|
|
934
|
+
if (typeof (request) !== "object" || request === null) {
|
|
935
|
+
return fail("invalid_arguments");
|
|
936
|
+
}
|
|
937
|
+
const settings = options === (void 0) || options === null ? {} : options;
|
|
938
|
+
const expectFingerprint = requiredFingerprint(request.expectFingerprint);
|
|
939
|
+
const expectRangeFingerprint = optionalFingerprint(request.expectRangeFingerprint);
|
|
940
|
+
if (typeof (request.content) !== "string") {
|
|
941
|
+
return fail("invalid_arguments");
|
|
942
|
+
}
|
|
943
|
+
const content = request.content;
|
|
944
|
+
const startLine = patchStartLine(request.startLine);
|
|
945
|
+
const endLine = patchEndLine(request.endLine);
|
|
946
|
+
if (endLine < startLine - 1) {
|
|
947
|
+
return fail("invalid_arguments");
|
|
948
|
+
}
|
|
949
|
+
const maxBytes = positiveInteger(settings.maxBytes, DefaultWriteBytes, MaxWriteBytes);
|
|
950
|
+
const rooted = sandbox.resolve(request.path, "write");
|
|
951
|
+
if (rooted.relative === ".") {
|
|
952
|
+
return fail("not_file");
|
|
953
|
+
}
|
|
954
|
+
const context = {
|
|
955
|
+
sandbox,
|
|
956
|
+
sessionId,
|
|
957
|
+
rooted,
|
|
958
|
+
kind: "line",
|
|
959
|
+
expectFingerprint,
|
|
960
|
+
expectRangeFingerprint,
|
|
961
|
+
dryRun: request.dryRun === true,
|
|
962
|
+
maxBytes,
|
|
963
|
+
timeZone: settings.timeZone === "utc" ? "utc" : "local",
|
|
964
|
+
};
|
|
965
|
+
const planner = (bytes) => {
|
|
966
|
+
const span = locateLineSpan(bytes, startLine, endLine);
|
|
967
|
+
return {
|
|
968
|
+
kind: "line",
|
|
969
|
+
startByte: span.startByte,
|
|
970
|
+
endByte: span.endByte,
|
|
971
|
+
replacement: encodeLineBody(content, span.charset),
|
|
972
|
+
startLine,
|
|
973
|
+
endLine,
|
|
974
|
+
};
|
|
975
|
+
};
|
|
976
|
+
const lockName = `${sessionId}\u0000${rooted.root.id}\u0000${rooted.relative}`;
|
|
977
|
+
const commit = await WriteLockRegistry.run(lockName, () => patchLocked(context, planner)).catch((error) => mapFsError(error));
|
|
978
|
+
try {
|
|
979
|
+
return publishPatch(commit, settings);
|
|
980
|
+
}
|
|
981
|
+
catch (error) {
|
|
982
|
+
return mapFsError(error);
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
/**
|
|
986
|
+
* Create a directory inside a root. `create` requires the parent to exist and
|
|
987
|
+
* fails when the target is already present; `ensure` creates the whole chain
|
|
988
|
+
* and treats an existing directory as success. The root itself is always a
|
|
989
|
+
* valid target for `ensure` and never for `create`.
|
|
990
|
+
*
|
|
991
|
+
* @param sandbox - Session sandbox that owns path resolution.
|
|
992
|
+
* @param path - Root-relative POSIX directory path.
|
|
993
|
+
* @param mode - `create` or `ensure`; defaults to `ensure`.
|
|
994
|
+
* @returns The resolved path of the directory.
|
|
995
|
+
*/
|
|
996
|
+
export const ensureDirectory = (sandbox, path, mode = "ensure") => {
|
|
997
|
+
if (mode !== "create" && mode !== "ensure") {
|
|
998
|
+
return fail("invalid_arguments");
|
|
999
|
+
}
|
|
1000
|
+
const rooted = sandbox.resolve(path, "create");
|
|
1001
|
+
if (rooted.relative === ".") {
|
|
1002
|
+
return rooted;
|
|
1003
|
+
}
|
|
1004
|
+
if (mode === "ensure") {
|
|
1005
|
+
makeDirectoryChain(sandbox, rooted.root, rooted.relative);
|
|
1006
|
+
sandbox.revalidate(rooted);
|
|
1007
|
+
return rooted;
|
|
1008
|
+
}
|
|
1009
|
+
if (statIfExists(rooted.absolute) !== (void 0)) {
|
|
1010
|
+
return fail("already_exists");
|
|
1011
|
+
}
|
|
1012
|
+
const parent = dirname(rooted.absolute);
|
|
1013
|
+
if (!sandbox.contains(rooted.root.path, parent)) {
|
|
1014
|
+
return fail("path_escape");
|
|
1015
|
+
}
|
|
1016
|
+
const parentStat = statOrFail(parent);
|
|
1017
|
+
if (!parentStat.isDirectory()) {
|
|
1018
|
+
return fail("not_directory");
|
|
1019
|
+
}
|
|
1020
|
+
try {
|
|
1021
|
+
mkdirSync(rooted.absolute);
|
|
1022
|
+
}
|
|
1023
|
+
catch (error) {
|
|
1024
|
+
return mapFsError(error);
|
|
1025
|
+
}
|
|
1026
|
+
sandbox.revalidate(rooted);
|
|
1027
|
+
return rooted;
|
|
1028
|
+
};
|