@mnstry/atelier 0.2.0-alpha.11 → 0.2.0-alpha.13
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 +1005 -0
- package/README.md +149 -9
- package/contracts/atelier-adoption-policy.v2.schema.json +55 -0
- package/contracts/atelier-architecture.v1.schema.json +258 -0
- package/contracts/atelier-boundary-delegations.v1.schema.json +161 -0
- package/contracts/atelier-boundary-owner-keys.v1.schema.json +98 -0
- package/contracts/atelier-build.v1.schema.json +1076 -0
- package/contracts/atelier-capability.v1.schema.json +1252 -0
- package/contracts/atelier-coordination.v1.schema.json +1050 -0
- package/contracts/atelier-evidence-navigation.v1.schema.json +603 -0
- package/contracts/atelier-harness.v1.schema.json +234 -0
- package/contracts/atelier-ingestion.v1.schema.json +806 -0
- package/contracts/atelier-inquiry.v1.schema.json +1772 -0
- package/contracts/atelier-instruction-adoption.v1.schema.json +490 -0
- package/contracts/atelier-interaction.v1.schema.json +553 -0
- package/contracts/atelier-judgment.v1.schema.json +690 -0
- package/contracts/atelier-knowledge.v1.schema.json +1512 -0
- package/contracts/atelier-learning.v1.schema.json +1095 -0
- package/contracts/atelier-migration.v3.schema.json +64 -0
- package/contracts/atelier-obsidian-generation-manifest.v2.schema.json +372 -0
- package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
- package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
- package/contracts/atelier-template-adoption.v1.schema.json +223 -0
- package/contracts/atelier-template-profile.v1.schema.json +1168 -0
- package/contracts/atelier-trackable.v1.schema.json +781 -0
- package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
- package/contracts/compat-baseline.json +1 -1
- package/docs/architecture.md +89 -0
- package/docs/assurance-controls.md +4 -3
- package/docs/blocks/capability-stewardship.md +17 -0
- package/docs/blocks/claims.md +16 -3
- package/docs/blocks/inquiry-harnesses.md +19 -0
- package/docs/blocks/learning-harnesses.md +17 -0
- package/docs/capability-stewardship.md +269 -0
- package/docs/coordination.md +51 -0
- package/docs/decision-practices.md +111 -0
- package/docs/decisions.md +29 -0
- package/docs/evidence-navigation.md +237 -0
- package/docs/exact-upgrades.md +3 -2
- package/docs/ingestion.md +181 -0
- package/docs/inquiry-harnesses.md +211 -0
- package/docs/install.md +80 -3
- package/docs/intake-and-guides.md +5 -0
- package/docs/integration-contract-decisions.md +24 -2
- package/docs/interaction-and-reflection.md +72 -0
- package/docs/knowledge-graph.md +20 -0
- package/docs/knowledge-health/api.md +30 -0
- package/docs/knowledge-health/assessment/api.md +96 -0
- package/docs/knowledge-health/assessment/capabilities.md +40 -0
- package/docs/knowledge-health/assessment/evaluation.md +27 -0
- package/docs/knowledge-health/assessment/facilitator.md +30 -0
- package/docs/knowledge-health/assessment/quickstart.md +61 -0
- package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
- package/docs/knowledge-health/devday/facilitator.md +16 -0
- package/docs/knowledge-health/devday/participant-response.md +22 -0
- package/docs/knowledge-health/devday/public-default.md +107 -0
- package/docs/knowledge-health/devday/quickstart.md +18 -0
- package/docs/knowledge-health/devday/support.md +22 -0
- package/docs/knowledge-health/exercise.md +17 -0
- package/docs/knowledge-health/facilitator.md +20 -0
- package/docs/knowledge-health/quickstart.md +94 -0
- package/docs/knowledge-health/readme.md +15 -0
- package/docs/knowledge-health/support.md +34 -0
- package/docs/knowledge-setup.md +248 -0
- package/docs/knowledge-workspace.md +264 -0
- package/docs/learning-harnesses.md +238 -0
- package/docs/learning.md +215 -0
- package/docs/local-services.md +242 -26
- package/docs/obsidian-contract.md +878 -36
- package/docs/obsidian-plugin.md +619 -0
- package/docs/obsidian.md +809 -64
- package/docs/optional-ingestion.md +83 -0
- package/docs/personal-workspace.md +257 -0
- package/docs/practical-judgment.md +92 -0
- package/docs/progressive-ingestion-acceptance.md +89 -0
- package/docs/progressive-ingestion.md +235 -0
- package/docs/project-command-inventory.md +1 -0
- package/docs/release-engineering.md +56 -1
- package/docs/repo-boundary-guard.md +94 -0
- package/docs/research-starter-quickstart.md +66 -0
- package/docs/research-starter.md +114 -0
- package/docs/semantic-ingestion.md +198 -0
- package/docs/semantic-operation.md +317 -0
- package/docs/skill-steward.md +6 -0
- package/docs/template-profile.md +164 -0
- package/docs/template-upgrades.md +114 -0
- package/docs/trackables.md +111 -0
- package/docs/upgrade.md +68 -3
- package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
- package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
- package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
- package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
- package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
- package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
- package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
- package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
- package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
- package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
- package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
- package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
- package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
- package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
- package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
- package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
- package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
- package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
- package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
- package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
- package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
- package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
- package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
- package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
- package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
- package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
- package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
- package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
- package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
- package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
- package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
- package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
- package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
- package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
- package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
- package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
- package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
- package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
- package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
- package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
- package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
- package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
- package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
- package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
- package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
- package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
- package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
- package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
- package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
- package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
- package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
- package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
- package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
- package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
- package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
- package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
- package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
- package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
- package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
- package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
- package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
- package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
- package/fixtures/atelier-interaction/act/valid/document.json +24 -0
- package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
- package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
- package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
- package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
- package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
- package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
- package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
- package/fixtures/atelier-judgment/case/valid/document.json +75 -0
- package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
- package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
- package/fixtures/atelier-judgment/native-decision.md +3 -0
- package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
- package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
- package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
- package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
- package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
- package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
- package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
- package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
- package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
- package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
- package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
- package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
- package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
- package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-learning/invalid/authority.json +14 -0
- package/fixtures/atelier-learning/valid/capture.json +13 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
- package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
- package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
- package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
- package/fixtures/atelier-trackable/request/valid/document.json +22 -0
- package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
- package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
- package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
- package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
- package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
- package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
- package/fixtures/capability-packages/adoption.example.json +70 -0
- package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
- package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
- package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
- package/fixtures/capability-packages/research/capability-package.json +88 -0
- package/fixtures/capability-packages/research/capability-release.json +134 -0
- package/fixtures/capability-packages/research/evaluation.md +15 -0
- package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
- package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
- package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
- package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
- package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
- package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
- package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
- package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
- package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
- package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
- package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
- package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
- package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
- package/fixtures/evidence-navigation/valid/claim.json +36 -0
- package/fixtures/evidence-navigation/valid/host.json +16 -0
- package/fixtures/evidence-navigation/valid/profile.json +16 -0
- package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
- package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
- package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
- package/fixtures/harnesses/learning-cycle.json +780 -0
- package/fixtures/inquiry/workshop.json +675 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
- package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
- package/fixtures/knowledge-health/calibration-suite.json +26 -0
- package/fixtures/knowledge-health/calibration-trial.json +37 -0
- package/fixtures/knowledge-health/corrected/checklist.md +18 -0
- package/fixtures/knowledge-health/devday/participant-record.json +19 -0
- package/fixtures/knowledge-health/expected-summary.json +27 -0
- package/fixtures/knowledge-health/workshop/README.md +20 -0
- package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
- package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
- package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
- package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
- package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
- package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
- package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
- package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/flat-note-path.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/hidden-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/identity-in-the-body.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/link-breaking-name.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-identity-region.v2.json +156 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-layout-version.v2.json +159 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/note-named-like-a-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/unknown-region-kind.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/v1-contract-version.v2.json +161 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/valid/three-notes.v2.json +160 -0
- package/fixtures/obsidian/edits/workspace.json +1 -1
- package/fixtures/obsidian/materialization/expected-full.json +21 -21
- package/fixtures/obsidian/materialization/expected-full.layout-1.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +7 -7
- package/fixtures/obsidian/materialization/expected-scoped.layout-1.json +39 -0
- package/fixtures/personal-workspace/overlay.json +24 -0
- package/fixtures/templates/local-library.v1.json +75 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
- package/package.json +64 -6
- package/plugins/obsidian/main.js +444 -0
- package/plugins/obsidian/manifest.json +9 -0
- package/plugins/obsidian/styles.css +22 -0
- package/skills/claude/atelier-build-harness/SKILL.md +59 -0
- package/skills/claude/atelier-coordination/SKILL.md +32 -0
- package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/claude/atelier-interaction/SKILL.md +34 -0
- package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/claude/atelier-research-harness/SKILL.md +43 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
- package/skills/claude/atelier-trackables/SKILL.md +40 -0
- package/skills/codex/atelier-build-harness/SKILL.md +59 -0
- package/skills/codex/atelier-coordination/SKILL.md +32 -0
- package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/codex/atelier-interaction/SKILL.md +34 -0
- package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/codex/atelier-research-harness/SKILL.md +43 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
- package/skills/codex/atelier-trackables/SKILL.md +40 -0
- package/src/architecture/catalog.mjs +355 -0
- package/src/architecture/index.mjs +93 -0
- package/src/boundary/content-rules.mjs +4 -2
- package/src/boundary/delegation-command.mjs +158 -0
- package/src/boundary/delegation.mjs +396 -0
- package/src/boundary/policy.mjs +52 -10
- package/src/build/git.mjs +18 -0
- package/src/build/index.mjs +3 -0
- package/src/build/ledger.mjs +88 -0
- package/src/capabilities/adoption.mjs +381 -0
- package/src/capabilities/evidence.mjs +140 -0
- package/src/capabilities/files.mjs +124 -0
- package/src/capabilities/index.mjs +4 -0
- package/src/capabilities/instructions.mjs +250 -0
- package/src/capabilities/package.mjs +136 -0
- package/src/cli/command-failure.mjs +36 -0
- package/src/cli/execute-command.mjs +16 -2
- package/src/cli/run.mjs +65 -9
- package/src/coauthor/store.mjs +43 -2
- package/src/collaboration/proposals.mjs +189 -0
- package/src/commands/architecture.mjs +27 -0
- package/src/commands/boundary.mjs +3 -0
- package/src/commands/capability.mjs +73 -0
- package/src/commands/enroll.mjs +3 -0
- package/src/commands/harness.mjs +79 -0
- package/src/commands/ingest.mjs +39 -0
- package/src/commands/init.mjs +4 -1
- package/src/commands/inquiry.mjs +66 -0
- package/src/commands/knowledge-semantic.mjs +128 -0
- package/src/commands/knowledge.mjs +119 -0
- package/src/commands/learn.mjs +61 -0
- package/src/commands/obsidian.mjs +832 -62
- package/src/commands/practice.mjs +24 -0
- package/src/commands/setup.mjs +22 -2
- package/src/commands/trackable.mjs +19 -0
- package/src/contracts/corpus.mjs +36 -0
- package/src/coordination/index.mjs +110 -0
- package/src/egress/forbidden-egress.mjs +2 -0
- package/src/evidence-navigation/contracts.mjs +60 -0
- package/src/evidence-navigation/evaluate.mjs +69 -0
- package/src/evidence-navigation/generation.mjs +22 -0
- package/src/evidence-navigation/index.mjs +2 -0
- package/src/evidence-navigation/local.mjs +167 -0
- package/src/evidence-navigation/schema.generated.mjs +604 -0
- package/src/evidence-navigation/types.mjs +12 -0
- package/src/export/atelier-export-contract.mjs +35 -8
- package/src/graph/enroll.mjs +248 -0
- package/src/graph/graph.mjs +17 -1
- package/src/graph/knowledge-graph.mjs +58 -8
- package/src/harnesses/contracts.mjs +41 -0
- package/src/harnesses/exchange.mjs +154 -0
- package/src/harnesses/feedback.mjs +16 -0
- package/src/harnesses/history.mjs +37 -0
- package/src/harnesses/index.mjs +4 -0
- package/src/harnesses/store.mjs +29 -0
- package/src/ingestion/contracts.mjs +41 -0
- package/src/ingestion/evaluation.mjs +119 -0
- package/src/ingestion/optional/composition/index.mjs +171 -0
- package/src/ingestion/optional/evaluation/index.mjs +219 -0
- package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
- package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
- package/src/ingestion/optional/graphify/graphify_supplier.py +933 -0
- package/src/ingestion/optional/graphify/json_bridge.py +163 -0
- package/src/ingestion/optional/graphify/query_receiving.py +209 -0
- package/src/ingestion/optional/index.mjs +9 -0
- package/src/ingestion/optional/jev/bridge.mjs +166 -0
- package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
- package/src/ingestion/optional/jev/judgments.mjs +174 -0
- package/src/ingestion/optional/jev/native-translation.mjs +32 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
- package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
- package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
- package/src/ingestion/optional/vanilla/index.mjs +218 -0
- package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
- package/src/ingestion/processors.mjs +218 -0
- package/src/ingestion/semantic.mjs +203 -0
- package/src/ingestion/store.mjs +372 -0
- package/src/inquiry/beliefs.mjs +32 -0
- package/src/inquiry/contracts.mjs +23 -0
- package/src/inquiry/index.mjs +5 -0
- package/src/inquiry/ledger.mjs +135 -0
- package/src/inquiry/lenses.mjs +15 -0
- package/src/inquiry/projection.mjs +77 -0
- package/src/inquiry/store.mjs +35 -0
- package/src/intake/read-scope.mjs +12 -0
- package/src/intake/store.mjs +69 -6
- package/src/interaction/index.mjs +91 -0
- package/src/judgment/index.mjs +82 -0
- package/src/judgment/practice-evaluation.mjs +70 -0
- package/src/judgment/practice.mjs +144 -0
- package/src/knowledge/context.mjs +217 -0
- package/src/knowledge/index.mjs +5 -0
- package/src/knowledge/ingestion.mjs +79 -0
- package/src/knowledge/intake.mjs +33 -0
- package/src/knowledge/ledger.mjs +110 -0
- package/src/knowledge/participatory/guidance.mjs +36 -0
- package/src/knowledge/participatory/guided-contribution.mjs +44 -0
- package/src/knowledge/participatory/index.mjs +2 -0
- package/src/knowledge/participatory/installed-workshop.mjs +108 -0
- package/src/knowledge/plan.mjs +67 -0
- package/src/knowledge/projection.mjs +43 -0
- package/src/knowledge/semantic-operation.mjs +567 -0
- package/src/knowledge/sessions.mjs +467 -0
- package/src/knowledge/workspace.mjs +295 -0
- package/src/knowledge-health/assessment.mjs +165 -0
- package/src/knowledge-health/capabilities.mjs +350 -0
- package/src/knowledge-health/context.mjs +62 -0
- package/src/knowledge-health/example.mjs +47 -0
- package/src/knowledge-health/index.mjs +10 -0
- package/src/knowledge-health/owner-join.mjs +88 -0
- package/src/knowledge-health/proof/common.mjs +24 -0
- package/src/knowledge-health/proof/installed-check.mjs +100 -0
- package/src/knowledge-health/proof/native-readback.mjs +37 -0
- package/src/knowledge-health/proof/rehearse.mjs +68 -0
- package/src/knowledge-health/proof/validate-participant.mjs +17 -0
- package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
- package/src/knowledge-health/proof/workshop-check.mjs +36 -0
- package/src/knowledge-health/rule-identity.mjs +44 -0
- package/src/knowledge-health/workshop-composition.mjs +19 -0
- package/src/knowledge-health/workshop-profile.mjs +176 -0
- package/src/knowledge-health/workshop.mjs +99 -0
- package/src/learning/adapters.mjs +68 -0
- package/src/learning/assistance.mjs +127 -0
- package/src/learning/contracts.mjs +65 -0
- package/src/learning/store.mjs +272 -0
- package/src/personal-workspace/index.mjs +391 -0
- package/src/personal-workspace/selection.mjs +385 -0
- package/src/project/config.mjs +8 -0
- package/src/project/durable-state.mjs +3 -1
- package/src/project/file-class.mjs +1 -1
- package/src/project/private-state.mjs +10 -1
- package/src/projection/obsidian/contracts.mjs +224 -18
- package/src/projection/obsidian/edits/apply.mjs +63 -19
- package/src/projection/obsidian/edits/regions.mjs +137 -42
- package/src/projection/obsidian/materialize/index.mjs +6 -3
- package/src/projection/obsidian/materialize/path-registry.mjs +443 -57
- package/src/projection/obsidian/materialize/prepare-view.mjs +382 -131
- package/src/projection/obsidian/materialize/redaction.mjs +327 -0
- package/src/projection/obsidian/materialize/settings.mjs +113 -34
- package/src/projection/obsidian/personal-workspace.mjs +436 -0
- package/src/projection/obsidian/plugin-bridge/bundle.mjs +50 -0
- package/src/projection/obsidian/plugin-bridge/channel.mjs +174 -0
- package/src/projection/obsidian/plugin-bridge/index.mjs +10 -0
- package/src/projection/obsidian/proposals/adapter.mjs +47 -8
- package/src/projection/obsidian/proposals/observation.mjs +20 -7
- package/src/projection/obsidian/publication/bridge-script.mjs +11 -5
- package/src/projection/obsidian/publication/index.mjs +2 -1
- package/src/projection/obsidian/publication/publisher.mjs +312 -36
- package/src/projection/obsidian/publication/transport.mjs +63 -8
- package/src/projection/obsidian/publication/vault-list.mjs +200 -0
- package/src/projection/obsidian/recovery/index.mjs +4 -1
- package/src/projection/obsidian/recovery/restart.mjs +3 -1
- package/src/projection/obsidian/recovery/store.mjs +168 -8
- package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
- package/src/projection/obsidian/selection-ui/focus.mjs +14 -7
- package/src/projection/obsidian/selection-ui/selection-state.mjs +2 -2
- package/src/projection/obsidian/selection-ui/selection.mjs +8 -3
- package/src/readiness/readiness.mjs +10 -3
- package/src/reflection/index.mjs +56 -0
- package/src/runtime/obsidian/app-capability.mjs +104 -12
- package/src/runtime/obsidian/app-production-seams.mjs +161 -42
- package/src/runtime/obsidian/app-registration.mjs +313 -0
- package/src/runtime/obsidian/app-restart.mjs +154 -0
- package/src/runtime/obsidian/engine.mjs +288 -29
- package/src/runtime/obsidian/extension-points.mjs +1 -1
- package/src/runtime/obsidian/index.mjs +15 -8
- package/src/runtime/obsidian/launch-plan.mjs +91 -0
- package/src/runtime/obsidian/lifecycle.mjs +193 -22
- package/src/runtime/obsidian/login-item.mjs +332 -0
- package/src/runtime/obsidian/machine-settings.mjs +119 -12
- package/src/runtime/obsidian/opening.mjs +369 -63
- package/src/runtime/obsidian/pending-edits.mjs +10 -0
- package/src/runtime/obsidian/pipeline.mjs +71 -3
- package/src/runtime/obsidian/plugin-channel.mjs +289 -0
- package/src/runtime/obsidian/plugin-choice.mjs +182 -0
- package/src/runtime/obsidian/plugin-drift.mjs +124 -0
- package/src/runtime/obsidian/plugin-presence.mjs +61 -0
- package/src/runtime/obsidian/process-identity.mjs +20 -10
- package/src/runtime/obsidian/project-views.mjs +282 -0
- package/src/runtime/obsidian/questions.mjs +48 -0
- package/src/runtime/obsidian/release-watch.mjs +56 -0
- package/src/runtime/obsidian/service-main.mjs +113 -17
- package/src/runtime/obsidian/service-manager-production.mjs +61 -0
- package/src/runtime/obsidian/service-managers.mjs +275 -0
- package/src/runtime/obsidian/service-record.mjs +105 -5
- package/src/runtime/obsidian/service-server.mjs +50 -12
- package/src/runtime/obsidian/service.mjs +134 -7
- package/src/runtime/obsidian/startup-adapters.mjs +59 -21
- package/src/runtime/obsidian/state-store.mjs +13 -1
- package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
- package/src/runtime/obsidian/vault-location.mjs +300 -0
- package/src/runtime/obsidian/view-counts.mjs +34 -0
- package/src/server/local-sidecar.mjs +193 -7
- package/src/server/server.mjs +45 -6
- package/src/template-bindings/project.mjs +92 -0
- package/src/templates/profile.mjs +225 -0
- package/src/trackables/domain.mjs +175 -0
- package/src/trackables/store.mjs +114 -0
- package/src/ui/knowledge-page.mjs +1388 -0
- package/src/upgrade/explanation.mjs +1 -1
- package/src/upgrade/template-participant.mjs +171 -0
- package/src/upgrade/transaction.mjs +72 -25
- package/src/upgrade/upgrade.mjs +2 -1
- package/templates/external-project-workspace/pack-lifecycle.json +6 -1
- package/templates/knowledge-workspace/.gitattributes +2 -0
- package/templates/knowledge-workspace/AGENTS.md +27 -0
- package/templates/knowledge-workspace/README.md +44 -0
- package/templates/knowledge-workspace/atelier.project.json +22 -0
- package/templates/knowledge-workspace/gitignore +6 -0
- package/templates/knowledge-workspace/knowledge-plan.json +111 -0
- package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
- package/templates/knowledge-workspace/records/forecast.md +22 -0
- package/templates/knowledge-workspace/records/inspection.md +25 -0
- package/templates/knowledge-workspace/records/telescope.md +24 -0
- package/templates/knowledge-workspace/repo-access.v1.json +9 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { identityLineTexts } from '../contracts.mjs'
|
|
2
|
+
import { refuse } from './byte-lens.mjs'
|
|
3
|
+
|
|
4
|
+
// The redaction guard: the last check before a prepared view is returned. It
|
|
5
|
+
// judges the assembled result, every note included, whether the note was
|
|
6
|
+
// emitted on this call or reused from the preparation cache, and refuses the
|
|
7
|
+
// view with `redaction-failure`, naming the in-view note (or file) and the
|
|
8
|
+
// rule, never the value. See "The redaction guard" in docs/obsidian-contract.md.
|
|
9
|
+
//
|
|
10
|
+
// allow-list every path the emitter wrote (note and attachment paths,
|
|
11
|
+
// rewritten link and embed targets, link targets in generated
|
|
12
|
+
// regions) is one allocated to this view, and each identity
|
|
13
|
+
// block names exactly its own note
|
|
14
|
+
// deny-list the free text of the generated regions, read as a reader
|
|
15
|
+
// sees it, names no unambiguous identifier of anything the
|
|
16
|
+
// audience may not see (refused); an ambiguous one, and any
|
|
17
|
+
// identifier of what the audience may see but the view does
|
|
18
|
+
// not select, is reported
|
|
19
|
+
// coverage both run over every note of the view
|
|
20
|
+
//
|
|
21
|
+
// REDACTION_RULES are the rules production uses. The mutation controls in
|
|
22
|
+
// test/obsidian-materialization.test.mjs replace one at a time through
|
|
23
|
+
// createViewPreparationForOracleTests to prove each is load-bearing.
|
|
24
|
+
|
|
25
|
+
const EXT = 'mnstry.atelier.obsidian'
|
|
26
|
+
const RELATIONS_HEADING_LINE = '## Relations (generated)'
|
|
27
|
+
const RELATIONS_NOTE_LINE = '%% Generated from the canonical graph. Edits to this section are not applied to any source. %%'
|
|
28
|
+
const RELATION_OUT = /^- [a-z_]+ → \[\[([^[\]|]*)\|(.*)\]\]$/
|
|
29
|
+
const RELATION_IN = /^- [a-z_]+ ← (.*)$/
|
|
30
|
+
const OUTSIDE_COUNT = /^Relationships leading outside this view: [0-9]+$/
|
|
31
|
+
const ORIGINAL_LINK = /^- Original: \[\[([^[\]|]*)\|Open the original file\]\]$/
|
|
32
|
+
const EMBED_LINE = /^!\[\[([^[\]|]*)\]\]$/
|
|
33
|
+
// Every link a generated region holds: a wikilink or embed not escaped by a
|
|
34
|
+
// backslash, and a Markdown link target. Generated free text escapes `[`, `]`
|
|
35
|
+
// and `|`, so only links the emitter wrote remain.
|
|
36
|
+
const GENERATED_WIKILINK = /(?<!\\)!?\[\[([^[\]\n]*?)\]\]/g
|
|
37
|
+
const GENERATED_MARKDOWN_LINK = /(?<!\\)\]\(([^()\n]*)\)/g
|
|
38
|
+
|
|
39
|
+
const linesOf = (text) => text.split('\n').map((line) => (line.endsWith('\r') ? line.slice(0, -1) : line))
|
|
40
|
+
|
|
41
|
+
// Generated prose escapes Markdown punctuation with a backslash (see plainText
|
|
42
|
+
// in prepare-view.mjs). A reader sees the text without those backslashes, and
|
|
43
|
+
// the deny-list reads it the same way.
|
|
44
|
+
const ESCAPED = /\\([\\`*_[\]<>#|^$%~=!&])/g
|
|
45
|
+
const asRead = (text) => text.replace(ESCAPED, '$1')
|
|
46
|
+
|
|
47
|
+
// An identity or a path matches only as a whole token, never inside a longer
|
|
48
|
+
// one. A letter or digit beside the match, or one of `. _ : / -` joined to a
|
|
49
|
+
// letter or digit beside it, continues the token; sentence punctuation after
|
|
50
|
+
// it (a full stop, a colon before a space) does not.
|
|
51
|
+
const ALNUM = /^[\p{L}\p{N}]$/u
|
|
52
|
+
const JOINERS = new Set(['.', '_', ':', '/', '-'])
|
|
53
|
+
|
|
54
|
+
function codePointAt(text, index) {
|
|
55
|
+
if (index < 0 || index >= text.length) return ''
|
|
56
|
+
const code = text.codePointAt(index)
|
|
57
|
+
// A low surrogate is the second half of the character before it.
|
|
58
|
+
if (code >= 0xdc00 && code <= 0xdfff && index > 0) return String.fromCodePoint(text.codePointAt(index - 1))
|
|
59
|
+
return String.fromCodePoint(code)
|
|
60
|
+
}
|
|
61
|
+
const isAlnumAt = (text, index) => ALNUM.test(codePointAt(text, index))
|
|
62
|
+
const continuesAfter = (text, end) => isAlnumAt(text, end) || (JOINERS.has(text[end]) && isAlnumAt(text, end + 1))
|
|
63
|
+
const continuesBefore = (text, start) => isAlnumAt(text, start - 1) || (JOINERS.has(text[start - 1]) && isAlnumAt(text, start - 2))
|
|
64
|
+
|
|
65
|
+
const REFUSE = 3
|
|
66
|
+
const DIAGNOSE = 2
|
|
67
|
+
const NOTICE = 1
|
|
68
|
+
const UNITS = 0x10000
|
|
69
|
+
|
|
70
|
+
// One Aho–Corasick automaton over every value, read in UTF-16 code units, with
|
|
71
|
+
// the token boundary tested at each hit: building it is linear in the values,
|
|
72
|
+
// and a text is read once, whatever their number. `refuse` values refuse a
|
|
73
|
+
// view; `diagnose` and `notice` values are reported, each under its own code.
|
|
74
|
+
// The matcher answers the strongest of what a text holds ('refuse', 'diagnose',
|
|
75
|
+
// 'notice' or null). Values and texts are compared in NFC.
|
|
76
|
+
export function createDenyMatcher({ refuse: refused = [], diagnose = [], notice = [] } = {}) {
|
|
77
|
+
const next = new Map()
|
|
78
|
+
const parent = [0]
|
|
79
|
+
const unit = [0]
|
|
80
|
+
const depth = [0]
|
|
81
|
+
const output = [null]
|
|
82
|
+
const add = (raw, strength) => {
|
|
83
|
+
if (typeof raw !== 'string' || raw === '') return
|
|
84
|
+
const value = raw.normalize('NFC')
|
|
85
|
+
let state = 0
|
|
86
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
87
|
+
const key = state * UNITS + value.charCodeAt(index)
|
|
88
|
+
let child = next.get(key)
|
|
89
|
+
if (child === undefined) {
|
|
90
|
+
child = parent.length
|
|
91
|
+
next.set(key, child)
|
|
92
|
+
parent.push(state)
|
|
93
|
+
unit.push(value.charCodeAt(index))
|
|
94
|
+
depth.push(depth[state] + 1)
|
|
95
|
+
output.push(null)
|
|
96
|
+
}
|
|
97
|
+
state = child
|
|
98
|
+
}
|
|
99
|
+
if (output[state] === null) output[state] = { length: value.length, strength }
|
|
100
|
+
else output[state].strength = Math.max(output[state].strength, strength)
|
|
101
|
+
}
|
|
102
|
+
for (const value of refused) add(value, REFUSE)
|
|
103
|
+
for (const value of diagnose) add(value, DIAGNOSE)
|
|
104
|
+
for (const value of notice) add(value, NOTICE)
|
|
105
|
+
// Failure and output links, state by state in order of depth.
|
|
106
|
+
const fail = new Int32Array(parent.length)
|
|
107
|
+
const outputLink = new Int32Array(parent.length).fill(-1)
|
|
108
|
+
const byDepth = []
|
|
109
|
+
for (let state = 1; state < parent.length; state += 1) (byDepth[depth[state]] ??= []).push(state)
|
|
110
|
+
for (const states of byDepth) {
|
|
111
|
+
for (const state of states ?? []) {
|
|
112
|
+
let target = 0
|
|
113
|
+
if (parent[state] !== 0) {
|
|
114
|
+
let from = fail[parent[state]]
|
|
115
|
+
while (from !== 0 && !next.has(from * UNITS + unit[state])) from = fail[from]
|
|
116
|
+
target = next.get(from * UNITS + unit[state]) ?? 0
|
|
117
|
+
}
|
|
118
|
+
fail[state] = target
|
|
119
|
+
outputLink[state] = output[target] !== null ? target : outputLink[target]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return function match(raw) {
|
|
123
|
+
const text = String(raw).normalize('NFC')
|
|
124
|
+
let state = 0
|
|
125
|
+
let found = 0
|
|
126
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
127
|
+
const code = text.charCodeAt(index)
|
|
128
|
+
while (state !== 0 && !next.has(state * UNITS + code)) state = fail[state]
|
|
129
|
+
state = next.get(state * UNITS + code) ?? 0
|
|
130
|
+
for (let hit = output[state] !== null ? state : outputLink[state]; hit > 0; hit = outputLink[hit]) {
|
|
131
|
+
const { length, strength } = output[hit]
|
|
132
|
+
if (strength <= found || continuesBefore(text, index - length + 1) || continuesAfter(text, index + 1)) continue
|
|
133
|
+
found = strength
|
|
134
|
+
if (found === REFUSE) return 'refuse'
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return found === REFUSE ? 'refuse' : found === DIAGNOSE ? 'diagnose' : found === NOTICE ? 'notice' : null
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// What one generated region of one note says: the link targets it holds and
|
|
142
|
+
// its free text, the author's words that reached it. A line that is not one
|
|
143
|
+
// of the shapes the emitter writes is free text as a whole.
|
|
144
|
+
function readGeneratedRegion({ kind, text, fence, own }) {
|
|
145
|
+
const links = [...text.matchAll(GENERATED_WIKILINK)].map((match) => match[1].split('|')[0].split('#')[0])
|
|
146
|
+
.concat([...text.matchAll(GENERATED_MARKDOWN_LINK)].map((match) => match[1]))
|
|
147
|
+
const freeText = []
|
|
148
|
+
for (const line of linesOf(text)) {
|
|
149
|
+
if (line === '' || line === fence) continue
|
|
150
|
+
if (kind === 'representation') {
|
|
151
|
+
if (line.startsWith('# ')) freeText.push(line.slice(2))
|
|
152
|
+
else if (line === own.format || line === own.source || ORIGINAL_LINK.test(line) || EMBED_LINE.test(line)) continue
|
|
153
|
+
else if (line.startsWith('- Tags: ')) freeText.push(line.slice('- Tags: '.length))
|
|
154
|
+
else freeText.push(line)
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
if (line === RELATIONS_HEADING_LINE || line === RELATIONS_NOTE_LINE || OUTSIDE_COUNT.test(line)) continue
|
|
158
|
+
const outgoing = RELATION_OUT.exec(line)
|
|
159
|
+
if (outgoing) { freeText.push(outgoing[2]); continue }
|
|
160
|
+
const incoming = RELATION_IN.exec(line)
|
|
161
|
+
if (incoming) { freeText.push(incoming[1]); continue }
|
|
162
|
+
freeText.push(line.replace(GENERATED_WIKILINK, ' '))
|
|
163
|
+
}
|
|
164
|
+
return { links, freeText: freeText.map(asRead) }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Whether an identity region holds exactly the identity lines of its own note
|
|
168
|
+
// and nothing but the front-matter delimiters, the comment delimiters of the
|
|
169
|
+
// end-of-note placement, blank lines and a fence closure around them.
|
|
170
|
+
function identityRegionIsOwn(text, node, fence) {
|
|
171
|
+
const expected = identityLineTexts(node)
|
|
172
|
+
const lines = linesOf(text)
|
|
173
|
+
const named = lines.filter((line) => line.startsWith('atelier-'))
|
|
174
|
+
if (named.length !== expected.length || named.some((line, index) => line !== expected[index])) return false
|
|
175
|
+
return lines.every((line) => named.includes(line) || line === '' || line === '---' || line === '%%' || (fence !== null && line === fence))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Everything the rules judge about one note, read once.
|
|
179
|
+
export function readNoteForRedaction({ note, bytes, node, own }) {
|
|
180
|
+
const regions = []
|
|
181
|
+
for (const region of note.regions.generated) {
|
|
182
|
+
if (region.kind === 'identity') continue
|
|
183
|
+
const fence = region.ext?.[EXT]?.fenceClosure?.fence ?? null
|
|
184
|
+
regions.push(readGeneratedRegion({ kind: region.kind, text: bytes.subarray(region.range.start, region.range.end).toString('utf8'), fence, own }))
|
|
185
|
+
}
|
|
186
|
+
const identity = note.regions.identity
|
|
187
|
+
const identityRegion = note.regions.generated.find((region) => region.kind === 'identity')
|
|
188
|
+
const identityFence = identityRegion?.ext?.[EXT]?.fenceClosure?.fence ?? null
|
|
189
|
+
return {
|
|
190
|
+
note,
|
|
191
|
+
links: regions.flatMap((region) => region.links),
|
|
192
|
+
freeText: regions.flatMap((region) => region.freeText),
|
|
193
|
+
identity: identity === undefined ? null : { own: identityRegionIsOwn(bytes.subarray(identity.start, identity.end).toString('utf8'), node, identityFence) },
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// A refusal names the rule and the in-view note (or file) it concerns, never
|
|
198
|
+
// the value: `where` is a path allocated to this view, or null when the only
|
|
199
|
+
// path at hand is the one in question.
|
|
200
|
+
const refuseAt = (rule, where, message) => refuse('redaction-failure', where === null ? `${message} (${rule})` : `${message} (${rule}, ${where})`,
|
|
201
|
+
{ rule, ...(where === null ? {} : where.endsWith('.md') ? { notePath: where } : { filePath: where }) })
|
|
202
|
+
|
|
203
|
+
// Rule 1. `view.allocatedPathOf(nodeId)` is the path allocated to a node of
|
|
204
|
+
// this view (null for any other), `view.attachments` the files it holds and
|
|
205
|
+
// `view.linkTargets` every spelling a rewritten or generated link of this
|
|
206
|
+
// view may use.
|
|
207
|
+
function allowList({ manifest, notes, read, view, layoutVersion }) {
|
|
208
|
+
const judgedIds = new Set(notes.map((note) => note.nodeId))
|
|
209
|
+
const pathOfNode = new Map(manifest.notes.map((note) => [note.nodeId, note.path]))
|
|
210
|
+
for (const note of notes) {
|
|
211
|
+
if (view.allocatedPathOf(note.nodeId) !== note.path) refuseAt('allow-list', view.allocatedPathOf(note.nodeId), 'a note path is not the one allocated to a note of this view')
|
|
212
|
+
for (const embed of note.ext?.[EXT]?.assetEmbeds ?? []) {
|
|
213
|
+
if (!view.attachments.has(embed.attachment)) refuseAt('allow-list', note.path, 'an embed names a file that is not part of this view')
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
for (const attachment of manifest.attachments) {
|
|
217
|
+
if (!view.attachments.has(attachment.path)) refuseAt('allow-list', null, 'an attachment is not a file of this view')
|
|
218
|
+
}
|
|
219
|
+
for (const link of manifest.links.filter((item) => judgedIds.has(item.sourceNodeId))) {
|
|
220
|
+
for (const inversion of link.inversions ?? []) checkInversion(inversion, pathOfNode.get(link.sourceNodeId), view)
|
|
221
|
+
}
|
|
222
|
+
for (const note of notes) {
|
|
223
|
+
for (const embed of note.ext?.[EXT]?.assetEmbeds ?? []) for (const inversion of embed.inversions) checkInversion(inversion, note.path, view)
|
|
224
|
+
}
|
|
225
|
+
for (const entry of read) {
|
|
226
|
+
for (const target of entry.links) if (!view.linkTargets.has(target)) refuseAt('allow-list', entry.note.path, 'a generated link names a file that is not part of this view')
|
|
227
|
+
if (layoutVersion >= 2 && (entry.identity === null || !entry.identity.own)) refuseAt('identity-block', entry.note.path, 'an identity block does not name exactly its own note')
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function checkInversion(inversion, notePath, view) {
|
|
232
|
+
const ext = inversion.ext?.[EXT] ?? {}
|
|
233
|
+
const emitted = Buffer.from(ext.emitted ?? '', 'base64url').toString('utf8')
|
|
234
|
+
// The words an author chose, appended after a rewritten wikilink target: authored bytes, not a path.
|
|
235
|
+
if ((ext.original ?? '') === '' && emitted.startsWith('|')) return
|
|
236
|
+
if (!view.linkTargets.has(emitted)) refuseAt('allow-list', notePath, 'an emitted link names a file that is not part of this view')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Rule 2. A refused value refuses the view and names the note whose generated
|
|
240
|
+
// region holds it; a reported one is recorded through `report`, once a note
|
|
241
|
+
// and code, and the view goes on: an ambiguous identifier of what the audience
|
|
242
|
+
// may not see (a bare word, a root file name) as bare-identity-in-generated-text,
|
|
243
|
+
// an identifier of what the audience may see but the view does not select as
|
|
244
|
+
// unselected-identity-in-generated-text.
|
|
245
|
+
const REPORTED = { diagnose: 'bare-identity-in-generated-text', notice: 'unselected-identity-in-generated-text' }
|
|
246
|
+
|
|
247
|
+
function denyList({ read, deny, report }) {
|
|
248
|
+
for (const entry of read) {
|
|
249
|
+
const reported = new Set()
|
|
250
|
+
for (const text of entry.freeText) {
|
|
251
|
+
const found = deny(text)
|
|
252
|
+
if (found === 'refuse') refuseAt('deny-list', entry.note.path, 'generated text names an identity that is not part of this view')
|
|
253
|
+
const code = REPORTED[found]
|
|
254
|
+
if (code !== undefined && !reported.has(code)) {
|
|
255
|
+
reported.add(code)
|
|
256
|
+
report({ code, rule: 'deny-list', repoId: entry.note.repoId, nodeId: entry.note.nodeId, notePath: entry.note.path })
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export const REDACTION_RULES = Object.freeze({
|
|
263
|
+
// Rule 3: which notes the other rules judge. Every note of the view.
|
|
264
|
+
judged: ({ notes }) => notes,
|
|
265
|
+
allowList,
|
|
266
|
+
denyList,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// Returns what rule 2 reported.
|
|
270
|
+
export function assertViewRedaction({ manifest, files, reused, nodeOf, ownOf, view, deny, layoutVersion }, rules = REDACTION_RULES) {
|
|
271
|
+
const notes = rules.judged({ notes: manifest.notes, reused })
|
|
272
|
+
const byPath = new Map(files.map((file) => [file.path, file.bytes]))
|
|
273
|
+
const read = notes.map((note) => readNoteForRedaction({ note, bytes: byPath.get(note.path), node: nodeOf(note.nodeId), own: ownOf(note.nodeId) }))
|
|
274
|
+
const reported = []
|
|
275
|
+
rules.allowList({ manifest, notes, read, view, layoutVersion })
|
|
276
|
+
rules.denyList({ read, deny, report: (item) => reported.push(item) })
|
|
277
|
+
return reported
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Layout 1 only, as the earlier release checked it. Two rules, because the two
|
|
281
|
+
// kinds of bytes differ: a substring the emitter itself produced from an
|
|
282
|
+
// allocated path (note and attachment paths, rewritten link targets) may name
|
|
283
|
+
// only a note or asset of this view; free text in a generated region (titles,
|
|
284
|
+
// tags, source paths, which are the author's words) is refused only when it
|
|
285
|
+
// names an identity of the census that is outside this view. Author text that
|
|
286
|
+
// merely looks like an identity suffix, such as a content-hashed asset name,
|
|
287
|
+
// is not a redaction failure.
|
|
288
|
+
export function assertOnlyVaultIdentities({ manifest, files, vaultSuffixes, forbiddenSuffixes }) {
|
|
289
|
+
const index = (suffixes) => {
|
|
290
|
+
const byPrefix = new Map()
|
|
291
|
+
for (const full of suffixes) byPrefix.set(full.slice(0, 12), [...(byPrefix.get(full.slice(0, 12)) ?? []), full])
|
|
292
|
+
return (suffix) => (byPrefix.get(suffix.slice(0, 12)) ?? []).some((full) => full.startsWith(suffix))
|
|
293
|
+
}
|
|
294
|
+
const allowed = index(vaultSuffixes)
|
|
295
|
+
const forbidden = index(forbiddenSuffixes)
|
|
296
|
+
const suffixesIn = (text) => [...text.matchAll(/--([0-9a-f]{12,64})(?![0-9a-f])/g)].map((match) => match[1])
|
|
297
|
+
// An emitted path ends in the allocated suffix (before the extension); the
|
|
298
|
+
// readable part before it is the author's title and may look like anything.
|
|
299
|
+
// A wikilink rewrite may append `|<the author's own words>`; only the part
|
|
300
|
+
// before the first `|` is an emitted path.
|
|
301
|
+
const checkEmitted = (text) => {
|
|
302
|
+
for (const segment of text.split('|')[0].split('#')[0].split('/')) {
|
|
303
|
+
const trailing = /--([0-9a-f]{12,64})(?:\.[^./]+)?$/.exec(segment)
|
|
304
|
+
if (trailing && !allowed(trailing[1])) refuse('redaction-failure', 'an emitted path names a note that is not part of this view')
|
|
305
|
+
}
|
|
306
|
+
for (const suffix of suffixesIn(text)) if (forbidden(suffix)) refuse('redaction-failure', 'an emitted path names a note that is not part of this view')
|
|
307
|
+
}
|
|
308
|
+
const checkText = (text) => {
|
|
309
|
+
for (const suffix of suffixesIn(text)) if (forbidden(suffix)) refuse('redaction-failure', 'generated output names a note that is not part of this view')
|
|
310
|
+
}
|
|
311
|
+
const byPath = new Map(files.map((file) => [file.path, file.bytes]))
|
|
312
|
+
for (const note of manifest.notes) {
|
|
313
|
+
const bytes = byPath.get(note.path)
|
|
314
|
+
for (const region of note.regions.generated) checkText(bytes.subarray(region.range.start, region.range.end).toString('utf8'))
|
|
315
|
+
}
|
|
316
|
+
for (const link of manifest.links) {
|
|
317
|
+
for (const inversion of link.inversions ?? []) checkEmitted(Buffer.from(inversion.ext[EXT].emitted, 'base64url').toString('utf8'))
|
|
318
|
+
}
|
|
319
|
+
for (const note of manifest.notes) {
|
|
320
|
+
for (const embed of note.ext[EXT].assetEmbeds ?? []) {
|
|
321
|
+
checkEmitted(embed.attachment)
|
|
322
|
+
for (const inversion of embed.inversions) checkEmitted(Buffer.from(inversion.ext[EXT].emitted, 'base64url').toString('utf8'))
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
for (const note of manifest.notes) checkEmitted(note.path)
|
|
326
|
+
for (const attachment of manifest.attachments) checkEmitted(attachment.path)
|
|
327
|
+
}
|
|
@@ -1,55 +1,134 @@
|
|
|
1
|
+
import { ObsidianContractRefusal } from '../contracts.mjs'
|
|
2
|
+
import { PLUGIN_DATA_FILE, PLUGIN_DIRECTORY, PLUGIN_ID, PLUGIN_SOURCE_FILES } from '../plugin-bridge/channel.mjs'
|
|
1
3
|
import { refuse, sha256Digest } from './byte-lens.mjs'
|
|
2
4
|
|
|
3
|
-
// Policy-owned Obsidian settings. Atelier owns exactly
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// the
|
|
7
|
-
//
|
|
5
|
+
// Policy-owned Obsidian settings. Atelier owns exactly these, and nothing
|
|
6
|
+
// else under the settings directory:
|
|
7
|
+
//
|
|
8
|
+
// core-plugins.json two keys: the built-in Sync and Publish core
|
|
9
|
+
// plugins stay disabled, because a generated view
|
|
10
|
+
// must not leave the machine through the editor
|
|
11
|
+
// community-plugins.json one entry: Atelier's own plugin is enabled
|
|
12
|
+
// plugins/atelier-projection/{manifest.json,main.js,styles.css,data.json}
|
|
13
|
+
// Atelier's own plugin, whole files
|
|
14
|
+
//
|
|
15
|
+
// Every other path under the settings directory, every other key of
|
|
16
|
+
// core-plugins.json and every other entry of community-plugins.json belongs to
|
|
17
|
+
// the person using the vault and is never prepared, replaced or removed here.
|
|
18
|
+
// The two settings files are merged with the bytes on disk when they are
|
|
19
|
+
// published; the plugin files are Atelier's bytes and their digests are pinned
|
|
20
|
+
// in the generation manifest.
|
|
21
|
+
//
|
|
22
|
+
// The community entry is Atelier's only while the person wants the plugin in
|
|
23
|
+
// that vault. The caller says so per view (see plugin-choice.mjs): the list is
|
|
24
|
+
// then prepared from the exact bytes it held when that was decided, and it is
|
|
25
|
+
// published only over exactly those bytes; or the list is withheld and never
|
|
26
|
+
// touched.
|
|
8
27
|
|
|
9
28
|
export const SETTINGS_ROOT = '.obsidian'
|
|
10
29
|
export const POLICY_SETTINGS_PATH = `${SETTINGS_ROOT}/core-plugins.json`
|
|
11
30
|
export const POLICY_DISABLED_CORE_PLUGINS = Object.freeze(['publish', 'sync'])
|
|
31
|
+
export const COMMUNITY_PLUGINS_PATH = `${SETTINGS_ROOT}/community-plugins.json`
|
|
32
|
+
export const POLICY_ENABLED_COMMUNITY_PLUGINS = Object.freeze([PLUGIN_ID])
|
|
33
|
+
export const POLICY_SETTINGS_PATHS = Object.freeze([POLICY_SETTINGS_PATH, COMMUNITY_PLUGINS_PATH])
|
|
34
|
+
export const PLUGIN_OWNED_PATHS = Object.freeze([...PLUGIN_SOURCE_FILES, PLUGIN_DATA_FILE].map((name) => `${PLUGIN_DIRECTORY}/${name}`))
|
|
12
35
|
|
|
13
36
|
export function isPolicySettingsPath(relativePath) {
|
|
14
|
-
return relativePath
|
|
37
|
+
return POLICY_SETTINGS_PATHS.includes(relativePath)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isPluginOwnedPath(relativePath) {
|
|
41
|
+
return PLUGIN_OWNED_PATHS.includes(relativePath)
|
|
15
42
|
}
|
|
16
43
|
|
|
17
44
|
export function isUserOwnedSettingsPath(relativePath) {
|
|
18
|
-
return (relativePath === SETTINGS_ROOT || relativePath.startsWith(`${SETTINGS_ROOT}/`)) && !isPolicySettingsPath(relativePath)
|
|
45
|
+
return (relativePath === SETTINGS_ROOT || relativePath.startsWith(`${SETTINGS_ROOT}/`)) && !isPolicySettingsPath(relativePath) && !isPluginOwnedPath(relativePath)
|
|
19
46
|
}
|
|
20
47
|
|
|
21
48
|
const serialize = (value) => Buffer.from(`${JSON.stringify(value, null, 2)}\n`, 'utf8')
|
|
22
49
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
function parseExisting(existing, label) {
|
|
51
|
+
if (!Buffer.isBuffer(existing)) refuse('invalid-settings', 'existing settings must be supplied as a Buffer')
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(existing))
|
|
54
|
+
} catch {
|
|
55
|
+
return refuse('invalid-settings', `existing ${label} do not parse; they are left alone`)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// core-plugins.json: the owned keys set to false, every other key kept.
|
|
60
|
+
function coreCandidate(existing) {
|
|
61
|
+
if (existing === null || existing === undefined) return serialize(Object.fromEntries(POLICY_DISABLED_CORE_PLUGINS.map((id) => [id, false])))
|
|
62
|
+
const current = parseExisting(existing, 'core plugin settings')
|
|
63
|
+
if (Array.isArray(current)) {
|
|
64
|
+
// Older list form: membership means enabled.
|
|
65
|
+
const kept = current.filter((id) => !POLICY_DISABLED_CORE_PLUGINS.includes(id))
|
|
66
|
+
return kept.length === current.length ? existing : serialize(kept)
|
|
67
|
+
}
|
|
68
|
+
if (current !== null && typeof current === 'object') {
|
|
69
|
+
const satisfied = POLICY_DISABLED_CORE_PLUGINS.every((id) => current[id] === false)
|
|
70
|
+
return satisfied ? existing : serialize({ ...current, ...Object.fromEntries(POLICY_DISABLED_CORE_PLUGINS.map((id) => [id, false])) })
|
|
71
|
+
}
|
|
72
|
+
return refuse('invalid-settings', 'existing core plugin settings have an unknown shape; they are left alone')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// community-plugins.json: the list of enabled plugins, in the person's order,
|
|
76
|
+
// with Atelier's own appended when it is missing.
|
|
77
|
+
function communityCandidate(existing) {
|
|
78
|
+
if (existing === null || existing === undefined) return serialize([...POLICY_ENABLED_COMMUNITY_PLUGINS])
|
|
79
|
+
const current = parseExisting(existing, 'community plugin settings')
|
|
80
|
+
if (!Array.isArray(current)) refuse('invalid-settings', 'existing community plugin settings are not a list; they are left alone')
|
|
81
|
+
const missing = POLICY_ENABLED_COMMUNITY_PLUGINS.filter((id) => !current.includes(id))
|
|
82
|
+
return missing.length === 0 ? existing : serialize([...current, ...missing])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const CANDIDATES = Object.freeze({ [POLICY_SETTINGS_PATH]: coreCandidate, [COMMUNITY_PLUGINS_PATH]: communityCandidate })
|
|
86
|
+
const OWNED = Object.freeze({ [POLICY_SETTINGS_PATH]: { ownedKeys: [...POLICY_DISABLED_CORE_PLUGINS] }, [COMMUNITY_PLUGINS_PATH]: { ownedEntries: [...POLICY_ENABLED_COMMUNITY_PLUGINS] } })
|
|
87
|
+
|
|
88
|
+
// The bytes one policy settings file should hold, given its bytes on disk now.
|
|
89
|
+
// Bytes that already satisfy the policy are returned untouched.
|
|
90
|
+
export function preparePolicySettingsFile(relativePath, existing = null) {
|
|
91
|
+
if (!isPolicySettingsPath(relativePath)) refuse('invalid-settings', 'only a policy settings file is prepared here')
|
|
92
|
+
const bytes = CANDIDATES[relativePath](existing)
|
|
93
|
+
return { path: relativePath, kind: 'settings', bytes, digest: sha256Digest(bytes) }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The community plugin list as it will be published from the bytes it held
|
|
97
|
+
// when the decision was made. `expectedDigest` binds the publication to those
|
|
98
|
+
// bytes (null: to the file being absent). A list whose shape the policy does
|
|
99
|
+
// not know is left alone when it is published; its own bytes stand for it here.
|
|
100
|
+
function prepareCommunityFile(existing) {
|
|
101
|
+
let file
|
|
102
|
+
try { file = preparePolicySettingsFile(COMMUNITY_PLUGINS_PATH, existing) } catch (error) {
|
|
103
|
+
if (!(error instanceof ObsidianContractRefusal) || existing === null) throw error
|
|
104
|
+
file = { path: COMMUNITY_PLUGINS_PATH, kind: 'settings', bytes: existing, digest: sha256Digest(existing) }
|
|
105
|
+
}
|
|
106
|
+
return { ...file, expectedDigest: existing === null ? null : sha256Digest(existing) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// `existing` is the current bytes of core-plugins.json when the vault already
|
|
110
|
+
// has one, so keys the person set survive. `plugin`, when given, is Atelier's
|
|
111
|
+
// plugin for this vault (preparePluginFiles): its files are carried and their
|
|
112
|
+
// digests are pinned. `plugin.community` says what happens to its entry:
|
|
113
|
+
//
|
|
114
|
+
// absent the entry is offered; the list is merged with the bytes on disk when published
|
|
115
|
+
// { entry: 'owned', existing: bytes|null } the entry is owned; the list is prepared from these bytes and published only over them
|
|
116
|
+
// { entry: 'withheld', reason } the list is not carried at all: the person turned the plugin off, or it cannot be read
|
|
117
|
+
export function prepareSettings({ existing = null, plugin = null } = {}) {
|
|
118
|
+
if (plugin !== null && (!Array.isArray(plugin.files) || plugin.files.some((file) => file.kind !== 'plugin' || !isPluginOwnedPath(file.path)))) {
|
|
119
|
+
refuse('invalid-settings', 'plugin files must be Atelier\'s own plugin files')
|
|
120
|
+
}
|
|
121
|
+
const community = plugin?.community ?? null
|
|
122
|
+
if (community !== null && !((community.entry === 'owned' && (community.existing === null || Buffer.isBuffer(community.existing))) || community.entry === 'withheld')) {
|
|
123
|
+
refuse('invalid-settings', 'the community entry is owned from known bytes, or withheld')
|
|
48
124
|
}
|
|
125
|
+
const listed = plugin === null ? [] : community === null ? [preparePolicySettingsFile(COMMUNITY_PLUGINS_PATH, null)] : community.entry === 'owned' ? [prepareCommunityFile(community.existing)] : []
|
|
126
|
+
const settings = [preparePolicySettingsFile(POLICY_SETTINGS_PATH, existing), ...listed]
|
|
49
127
|
return {
|
|
50
|
-
files: [
|
|
128
|
+
files: [...settings, ...(plugin === null ? [] : plugin.files)],
|
|
51
129
|
ownership: {
|
|
52
|
-
policyOwned:
|
|
130
|
+
policyOwned: settings.map((file) => ({ path: file.path, ...OWNED[file.path], digest: file.digest, byteLength: file.bytes.length, ...(Object.hasOwn(file, 'expectedDigest') ? { expectedDigest: file.expectedDigest } : {}) })),
|
|
131
|
+
...(plugin === null ? {} : { pluginOwned: { ...plugin.ownership, entry: community?.entry === 'withheld' ? 'withheld' : 'owned', ...(community?.entry === 'withheld' ? { withheldBecause: String(community.reason ?? 'unstated') } : {}) } }),
|
|
53
132
|
userOwnedRoot: SETTINGS_ROOT,
|
|
54
133
|
rule: 'every-other-path-and-key-is-user-owned',
|
|
55
134
|
},
|