@cleocode/core 2026.9.8 → 2026.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/reconstruct.d.ts +13 -43
- package/dist/audit/reconstruct.d.ts.map +1 -1
- package/dist/audit/reconstruct.js +295 -290
- package/dist/audit/reconstruct.js.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +8 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/dispatch/mutate-projection.d.ts.map +1 -1
- package/dist/dispatch/mutate-projection.js +12 -0
- package/dist/dispatch/mutate-projection.js.map +1 -1
- package/dist/dispatch/mvi-projection.d.ts +29 -6
- package/dist/dispatch/mvi-projection.d.ts.map +1 -1
- package/dist/dispatch/mvi-projection.js +109 -139
- package/dist/dispatch/mvi-projection.js.map +1 -1
- package/dist/docs/docs-read-model.d.ts +3 -2
- package/dist/docs/docs-read-model.d.ts.map +1 -1
- package/dist/docs/docs-read-model.js +4 -3
- package/dist/docs/docs-read-model.js.map +1 -1
- package/dist/docs/export-document.js +5100 -2297
- package/dist/docs/export-document.js.map +3 -3
- package/dist/docs/projection.d.ts +67 -0
- package/dist/docs/projection.d.ts.map +1 -0
- package/dist/docs/projection.js +370 -0
- package/dist/docs/projection.js.map +1 -0
- package/dist/doctor/acceptance-drift.d.ts +6 -2
- package/dist/doctor/acceptance-drift.d.ts.map +1 -1
- package/dist/doctor/acceptance-drift.js +43 -5
- package/dist/doctor/acceptance-drift.js.map +1 -1
- package/dist/doctor/knowledge-summary.d.ts +41 -0
- package/dist/doctor/knowledge-summary.d.ts.map +1 -0
- package/dist/doctor/knowledge-summary.js +81 -0
- package/dist/doctor/knowledge-summary.js.map +1 -0
- package/dist/doctor/knowledge.d.ts +192 -0
- package/dist/doctor/knowledge.d.ts.map +1 -0
- package/dist/doctor/knowledge.js +1864 -0
- package/dist/doctor/knowledge.js.map +1 -0
- package/dist/git/work-tree.d.ts +64 -0
- package/dist/git/work-tree.d.ts.map +1 -0
- package/dist/git/work-tree.js +111 -0
- package/dist/git/work-tree.js.map +1 -0
- package/dist/hooks/handlers/task-hooks.d.ts +8 -5
- package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/task-hooks.js +15 -60
- package/dist/hooks/handlers/task-hooks.js.map +1 -1
- package/dist/identity/cleo-identity.d.ts +13 -0
- package/dist/identity/cleo-identity.d.ts.map +1 -1
- package/dist/identity/cleo-identity.js +20 -6
- package/dist/identity/cleo-identity.js.map +1 -1
- package/dist/injection.d.ts.map +1 -1
- package/dist/injection.js +29 -13
- package/dist/injection.js.map +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -1
- package/dist/lifecycle/verification/satisfies-validator.d.ts.map +1 -1
- package/dist/lifecycle/verification/satisfies-validator.js +124 -120
- package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
- package/dist/llm/plugin-facade.js +4018 -1726
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/memory/brain-backfill.d.ts +27 -12
- package/dist/memory/brain-backfill.d.ts.map +1 -1
- package/dist/memory/brain-backfill.js +325 -134
- package/dist/memory/brain-backfill.js.map +1 -1
- package/dist/memory/brain-doctor.d.ts +21 -0
- package/dist/memory/brain-doctor.d.ts.map +1 -1
- package/dist/memory/brain-doctor.js +111 -53
- package/dist/memory/brain-doctor.js.map +1 -1
- package/dist/memory/brain-links.d.ts.map +1 -1
- package/dist/memory/brain-links.js +31 -4
- package/dist/memory/brain-links.js.map +1 -1
- package/dist/memory/brain-reasoning.d.ts.map +1 -1
- package/dist/memory/brain-reasoning.js +28 -2
- package/dist/memory/brain-reasoning.js.map +1 -1
- package/dist/memory/brain-reconciler.d.ts +14 -19
- package/dist/memory/brain-reconciler.d.ts.map +1 -1
- package/dist/memory/brain-reconciler.js +70 -97
- package/dist/memory/brain-reconciler.js.map +1 -1
- package/dist/memory/brain-search.d.ts +2 -0
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/brain-search.js +59 -29
- package/dist/memory/brain-search.js.map +1 -1
- package/dist/memory/brain-similarity.d.ts.map +1 -1
- package/dist/memory/brain-similarity.js +5 -4
- package/dist/memory/brain-similarity.js.map +1 -1
- package/dist/memory/brain-stub-prune.d.ts +31 -9
- package/dist/memory/brain-stub-prune.d.ts.map +1 -1
- package/dist/memory/brain-stub-prune.js +65 -18
- package/dist/memory/brain-stub-prune.js.map +1 -1
- package/dist/memory/brain-writer-handlers.d.ts +10 -1
- package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
- package/dist/memory/brain-writer-handlers.js +23 -3
- package/dist/memory/brain-writer-handlers.js.map +1 -1
- package/dist/memory/brain-writer-thread.d.ts +11 -1
- package/dist/memory/brain-writer-thread.d.ts.map +1 -1
- package/dist/memory/brain-writer-thread.js +87 -20
- package/dist/memory/brain-writer-thread.js.map +1 -1
- package/dist/memory/brain-writer-worker.js +8 -1
- package/dist/memory/brain-writer-worker.js.map +1 -1
- package/dist/memory/decision-cross-link.d.ts +39 -0
- package/dist/memory/decision-cross-link.d.ts.map +1 -1
- package/dist/memory/decision-cross-link.js +316 -0
- package/dist/memory/decision-cross-link.js.map +1 -1
- package/dist/memory/decisions.d.ts +4 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/memory/decisions.js +7 -24
- package/dist/memory/decisions.js.map +1 -1
- package/dist/memory/dispatch-trace.d.ts +12 -5
- package/dist/memory/dispatch-trace.d.ts.map +1 -1
- package/dist/memory/dispatch-trace.js +17 -7
- package/dist/memory/dispatch-trace.js.map +1 -1
- package/dist/memory/eligibility.d.ts +51 -0
- package/dist/memory/eligibility.d.ts.map +1 -0
- package/dist/memory/eligibility.js +77 -0
- package/dist/memory/eligibility.js.map +1 -0
- package/dist/memory/engine-compat.d.ts +2 -0
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/engine-compat.js +10 -0
- package/dist/memory/engine-compat.js.map +1 -1
- package/dist/memory/extraction-gate.d.ts +21 -7
- package/dist/memory/extraction-gate.d.ts.map +1 -1
- package/dist/memory/extraction-gate.js +178 -216
- package/dist/memory/extraction-gate.js.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +38 -5
- package/dist/memory/graph-auto-populate.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.js +182 -60
- package/dist/memory/graph-auto-populate.js.map +1 -1
- package/dist/memory/graph-memory-bridge.d.ts +14 -1
- package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
- package/dist/memory/graph-memory-bridge.js +66 -28
- package/dist/memory/graph-memory-bridge.js.map +1 -1
- package/dist/memory/index.d.ts +1 -19
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +1 -50
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/learnings.d.ts.map +1 -1
- package/dist/memory/learnings.js +2 -23
- package/dist/memory/learnings.js.map +1 -1
- package/dist/memory/manifest-filter.d.ts +21 -0
- package/dist/memory/manifest-filter.d.ts.map +1 -0
- package/dist/memory/manifest-filter.js +51 -0
- package/dist/memory/manifest-filter.js.map +1 -0
- package/dist/memory/manifest-ingestion.d.ts +28 -30
- package/dist/memory/manifest-ingestion.d.ts.map +1 -1
- package/dist/memory/manifest-ingestion.js +104 -235
- package/dist/memory/manifest-ingestion.js.map +1 -1
- package/dist/memory/observer-reflector.d.ts +2 -0
- package/dist/memory/observer-reflector.d.ts.map +1 -1
- package/dist/memory/observer-reflector.js +6 -49
- package/dist/memory/observer-reflector.js.map +1 -1
- package/dist/memory/patterns.d.ts +10 -0
- package/dist/memory/patterns.d.ts.map +1 -1
- package/dist/memory/patterns.js +25 -33
- package/dist/memory/patterns.js.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.d.ts +72 -9
- package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.js +508 -412
- package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
- package/dist/memory/retrieval/build-retrieval-bundle.d.ts.map +1 -1
- package/dist/memory/retrieval/build-retrieval-bundle.js +16 -15
- package/dist/memory/retrieval/build-retrieval-bundle.js.map +1 -1
- package/dist/memory/retrieval/fetch.d.ts +7 -0
- package/dist/memory/retrieval/fetch.d.ts.map +1 -1
- package/dist/memory/retrieval/fetch.js +14 -11
- package/dist/memory/retrieval/fetch.js.map +1 -1
- package/dist/memory/retrieval/observe.d.ts +7 -1
- package/dist/memory/retrieval/observe.d.ts.map +1 -1
- package/dist/memory/retrieval/observe.js +58 -4
- package/dist/memory/retrieval/observe.js.map +1 -1
- package/dist/memory/retrieval/search.d.ts +14 -1
- package/dist/memory/retrieval/search.d.ts.map +1 -1
- package/dist/memory/retrieval/search.js +133 -69
- package/dist/memory/retrieval/search.js.map +1 -1
- package/dist/memory/retrieval/telemetry.d.ts +31 -0
- package/dist/memory/retrieval/telemetry.d.ts.map +1 -0
- package/dist/memory/retrieval/telemetry.js +53 -0
- package/dist/memory/retrieval/telemetry.js.map +1 -0
- package/dist/nexus/analyze-orchestrator.d.ts +41 -9
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +257 -58
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/api-contracts.d.ts +2 -6
- package/dist/nexus/api-contracts.d.ts.map +1 -1
- package/dist/nexus/api-contracts.js +11 -2
- package/dist/nexus/api-contracts.js.map +1 -1
- package/dist/nexus/context.d.ts.map +1 -1
- package/dist/nexus/context.js +14 -37
- package/dist/nexus/context.js.map +1 -1
- package/dist/nexus/diff.d.ts.map +1 -1
- package/dist/nexus/diff.js +2 -1
- package/dist/nexus/diff.js.map +1 -1
- package/dist/nexus/identity.d.ts +29 -3
- package/dist/nexus/identity.d.ts.map +1 -1
- package/dist/nexus/identity.js +63 -12
- package/dist/nexus/identity.js.map +1 -1
- package/dist/nexus/impact.d.ts +21 -19
- package/dist/nexus/impact.d.ts.map +1 -1
- package/dist/nexus/impact.js +96 -192
- package/dist/nexus/impact.js.map +1 -1
- package/dist/nexus/knowledge.d.ts +80 -0
- package/dist/nexus/knowledge.d.ts.map +1 -0
- package/dist/nexus/knowledge.js +786 -0
- package/dist/nexus/knowledge.js.map +1 -0
- package/dist/nexus/living-brain.d.ts +8 -5
- package/dist/nexus/living-brain.d.ts.map +1 -1
- package/dist/nexus/living-brain.js +252 -188
- package/dist/nexus/living-brain.js.map +1 -1
- package/dist/nexus/registry.d.ts +11 -2
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +144 -128
- package/dist/nexus/registry.js.map +1 -1
- package/dist/nexus/source-roots.d.ts +16 -0
- package/dist/nexus/source-roots.d.ts.map +1 -0
- package/dist/nexus/source-roots.js +174 -0
- package/dist/nexus/source-roots.js.map +1 -0
- package/dist/nexus/task-evidence.d.ts +22 -0
- package/dist/nexus/task-evidence.d.ts.map +1 -0
- package/dist/nexus/task-evidence.js +210 -0
- package/dist/nexus/task-evidence.js.map +1 -0
- package/dist/nexus/tasks-bridge.d.ts +14 -7
- package/dist/nexus/tasks-bridge.d.ts.map +1 -1
- package/dist/nexus/tasks-bridge.js +81 -40
- package/dist/nexus/tasks-bridge.js.map +1 -1
- package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
- package/dist/orchestrate/lifecycle-ops.js +170 -120
- package/dist/orchestrate/lifecycle-ops.js.map +1 -1
- package/dist/orchestrate/query-ops.d.ts +6 -7
- package/dist/orchestrate/query-ops.d.ts.map +1 -1
- package/dist/orchestrate/query-ops.js +509 -532
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestration/bootstrap.d.ts +15 -1
- package/dist/orchestration/bootstrap.d.ts.map +1 -1
- package/dist/orchestration/bootstrap.js +21 -10
- package/dist/orchestration/bootstrap.js.map +1 -1
- package/dist/orchestration/index.d.ts +35 -3
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js +64 -35
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/lead-rollup.d.ts +24 -1
- package/dist/orchestration/lead-rollup.d.ts.map +1 -1
- package/dist/orchestration/lead-rollup.js +31 -11
- package/dist/orchestration/lead-rollup.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +16 -41
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/orchestration/unblock.d.ts +15 -1
- package/dist/orchestration/unblock.d.ts.map +1 -1
- package/dist/orchestration/unblock.js +23 -12
- package/dist/orchestration/unblock.js.map +1 -1
- package/dist/orchestration/validate-spawn.d.ts.map +1 -1
- package/dist/orchestration/validate-spawn.js +3 -3
- package/dist/orchestration/validate-spawn.js.map +1 -1
- package/dist/orchestration/waves.d.ts +29 -13
- package/dist/orchestration/waves.d.ts.map +1 -1
- package/dist/orchestration/waves.js +43 -55
- package/dist/orchestration/waves.js.map +1 -1
- package/dist/paths.d.ts +17 -163
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +145 -496
- package/dist/paths.js.map +1 -1
- package/dist/project-info.d.ts +2 -11
- package/dist/project-info.d.ts.map +1 -1
- package/dist/project-info.js +3 -31
- package/dist/project-info.js.map +1 -1
- package/dist/project-scope.d.ts +239 -0
- package/dist/project-scope.d.ts.map +1 -0
- package/dist/project-scope.js +473 -0
- package/dist/project-scope.js.map +1 -0
- package/dist/release/pr-evidence.d.ts +16 -3
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +53 -7
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/render/nexus/graph/index.d.ts.map +1 -1
- package/dist/render/nexus/graph/index.js +4 -2
- package/dist/render/nexus/graph/index.js.map +1 -1
- package/dist/render/orchestration/audit-reconstruct.d.ts +11 -0
- package/dist/render/orchestration/audit-reconstruct.d.ts.map +1 -1
- package/dist/render/orchestration/audit-reconstruct.js +21 -6
- package/dist/render/orchestration/audit-reconstruct.js.map +1 -1
- package/dist/render/tasks/show.js +2 -1
- package/dist/render/tasks/show.js.map +1 -1
- package/dist/resources/spawn-wrapper.d.ts +82 -14
- package/dist/resources/spawn-wrapper.d.ts.map +1 -1
- package/dist/resources/spawn-wrapper.js +585 -27
- package/dist/resources/spawn-wrapper.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +4026 -1734
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sequence/index.d.ts.map +1 -1
- package/dist/sequence/index.js +76 -68
- package/dist/sequence/index.js.map +1 -1
- package/dist/session/canon-lint.d.ts +24 -3
- package/dist/session/canon-lint.d.ts.map +1 -1
- package/dist/session/canon-lint.js +72 -31
- package/dist/session/canon-lint.js.map +1 -1
- package/dist/session/engine-ops.d.ts +1 -4
- package/dist/session/engine-ops.d.ts.map +1 -1
- package/dist/session/engine-ops.js +6 -2
- package/dist/session/engine-ops.js.map +1 -1
- package/dist/sessions/briefing.d.ts +11 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +76 -8
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/handoff.d.ts +3 -1
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/handoff.js +2 -0
- package/dist/sessions/handoff.js.map +1 -1
- package/dist/shutdown-deadline.d.ts +24 -33
- package/dist/shutdown-deadline.d.ts.map +1 -1
- package/dist/shutdown-deadline.js +71 -14
- package/dist/shutdown-deadline.js.map +1 -1
- package/dist/shutdown.d.ts +21 -10
- package/dist/shutdown.d.ts.map +1 -1
- package/dist/shutdown.js +89 -42
- package/dist/shutdown.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/orchestrator/validator.d.ts +87 -20
- package/dist/skills/orchestrator/validator.d.ts.map +1 -1
- package/dist/skills/orchestrator/validator.js +330 -241
- package/dist/skills/orchestrator/validator.js.map +1 -1
- package/dist/store/agent-resolver.d.ts.map +1 -1
- package/dist/store/agent-resolver.js +16 -9
- package/dist/store/agent-resolver.js.map +1 -1
- package/dist/store/attachment-store.d.ts +4 -1
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +20 -3
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/store/background-jobs.d.ts +426 -105
- package/dist/store/background-jobs.d.ts.map +1 -1
- package/dist/store/background-jobs.js +1208 -252
- package/dist/store/background-jobs.js.map +1 -1
- package/dist/store/background-ops.d.ts +145 -8
- package/dist/store/background-ops.d.ts.map +1 -1
- package/dist/store/background-ops.js +459 -15
- package/dist/store/background-ops.js.map +1 -1
- package/dist/store/backup-inspect.d.ts +48 -4
- package/dist/store/backup-inspect.d.ts.map +1 -1
- package/dist/store/backup-inspect.js +384 -4
- package/dist/store/backup-inspect.js.map +1 -1
- package/dist/store/converters.d.ts.map +1 -1
- package/dist/store/converters.js +19 -2
- package/dist/store/converters.js.map +1 -1
- package/dist/store/data-accessor.d.ts +9 -1
- package/dist/store/data-accessor.d.ts.map +1 -1
- package/dist/store/data-accessor.js +22 -5
- package/dist/store/data-accessor.js.map +1 -1
- package/dist/store/data-safety-central.d.ts.map +1 -1
- package/dist/store/data-safety-central.js +272 -232
- package/dist/store/data-safety-central.js.map +1 -1
- package/dist/store/db-helpers.d.ts +19 -3
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/db-helpers.js +42 -58
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts +63 -4
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +166 -19
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/exodus/archive.d.ts +21 -7
- package/dist/store/exodus/archive.d.ts.map +1 -1
- package/dist/store/exodus/archive.js +60 -18
- package/dist/store/exodus/archive.js.map +1 -1
- package/dist/store/exodus/migrate.d.ts.map +1 -1
- package/dist/store/exodus/migrate.js +27 -23
- package/dist/store/exodus/migrate.js.map +1 -1
- package/dist/store/exodus/on-open.d.ts +16 -27
- package/dist/store/exodus/on-open.d.ts.map +1 -1
- package/dist/store/exodus/on-open.js +121 -169
- package/dist/store/exodus/on-open.js.map +1 -1
- package/dist/store/exodus/recovery.d.ts +90 -0
- package/dist/store/exodus/recovery.d.ts.map +1 -0
- package/dist/store/exodus/recovery.js +434 -0
- package/dist/store/exodus/recovery.js.map +1 -0
- package/dist/store/exodus/types.d.ts +15 -0
- package/dist/store/exodus/types.d.ts.map +1 -1
- package/dist/store/memory-accessor.d.ts +52 -3
- package/dist/store/memory-accessor.d.ts.map +1 -1
- package/dist/store/memory-accessor.js +122 -7
- package/dist/store/memory-accessor.js.map +1 -1
- package/dist/store/nexus-sqlite.d.ts +24 -6
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.js +39 -20
- package/dist/store/nexus-sqlite.js.map +1 -1
- package/dist/store/ports/domain-binding.d.ts.map +1 -1
- package/dist/store/ports/domain-binding.js +27 -5
- package/dist/store/ports/domain-binding.js.map +1 -1
- package/dist/store/schema/background-jobs.d.ts +181 -2
- package/dist/store/schema/background-jobs.d.ts.map +1 -1
- package/dist/store/schema/background-jobs.js +27 -3
- package/dist/store/schema/background-jobs.js.map +1 -1
- package/dist/store/schema/cleo-global/skills.d.ts +2 -2
- package/dist/store/schema/cleo-project/lifecycle.d.ts +2 -2
- package/dist/store/schema/cleo-project/runtime.d.ts +1 -1
- package/dist/store/schema/cleo-project/tasks-core.d.ts +1 -1
- package/dist/store/schema/lifecycle.d.ts +2 -2
- package/dist/store/schema/skills-schema.d.ts +2 -2
- package/dist/store/schema/tasks.d.ts +1 -1
- package/dist/store/session-store.d.ts +3 -1
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +252 -207
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts +39 -4
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +689 -527
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/tasks-sqlite.d.ts +3 -1
- package/dist/store/tasks-sqlite.d.ts.map +1 -1
- package/dist/store/tasks-sqlite.js +309 -279
- package/dist/store/tasks-sqlite.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +8 -8
- package/dist/store/writer-lease.d.ts +80 -8
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +503 -134
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/system/audit.d.ts +2 -1
- package/dist/system/audit.d.ts.map +1 -1
- package/dist/system/audit.js +118 -71
- package/dist/system/audit.js.map +1 -1
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/backup.js +5 -5
- package/dist/system/backup.js.map +1 -1
- package/dist/system/inject-generate.js +1 -1
- package/dist/task-work/index.d.ts +3 -0
- package/dist/task-work/index.d.ts.map +1 -1
- package/dist/task-work/index.js +34 -37
- package/dist/task-work/index.js.map +1 -1
- package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
- package/dist/tasks/ac-coverage-gate.js +4 -0
- package/dist/tasks/ac-coverage-gate.js.map +1 -1
- package/dist/tasks/ac-immutability.d.ts +7 -4
- package/dist/tasks/ac-immutability.d.ts.map +1 -1
- package/dist/tasks/ac-immutability.js +19 -17
- package/dist/tasks/ac-immutability.js.map +1 -1
- package/dist/tasks/ac-table.d.ts +5 -5
- package/dist/tasks/ac-table.d.ts.map +1 -1
- package/dist/tasks/ac-table.js +26 -12
- package/dist/tasks/ac-table.js.map +1 -1
- package/dist/tasks/acceptance-input.d.ts +52 -0
- package/dist/tasks/acceptance-input.d.ts.map +1 -0
- package/dist/tasks/acceptance-input.js +259 -0
- package/dist/tasks/acceptance-input.js.map +1 -0
- package/dist/tasks/add.d.ts +67 -29
- package/dist/tasks/add.d.ts.map +1 -1
- package/dist/tasks/add.js +255 -97
- package/dist/tasks/add.js.map +1 -1
- package/dist/tasks/complete.d.ts +16 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +152 -55
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/decompose.d.ts +103 -0
- package/dist/tasks/decompose.d.ts.map +1 -0
- package/dist/tasks/decompose.js +224 -0
- package/dist/tasks/decompose.js.map +1 -0
- package/dist/tasks/delete.d.ts +5 -8
- package/dist/tasks/delete.d.ts.map +1 -1
- package/dist/tasks/delete.js +4 -3
- package/dist/tasks/delete.js.map +1 -1
- package/dist/tasks/dependency-check.d.ts +59 -0
- package/dist/tasks/dependency-check.d.ts.map +1 -1
- package/dist/tasks/dependency-check.js +73 -0
- package/dist/tasks/dependency-check.js.map +1 -1
- package/dist/tasks/deps-ready.d.ts +13 -1
- package/dist/tasks/deps-ready.d.ts.map +1 -1
- package/dist/tasks/deps-ready.js +16 -5
- package/dist/tasks/deps-ready.js.map +1 -1
- package/dist/tasks/engine-converters.d.ts +7 -0
- package/dist/tasks/engine-converters.d.ts.map +1 -1
- package/dist/tasks/engine-converters.js +8 -1
- package/dist/tasks/engine-converters.js.map +1 -1
- package/dist/tasks/evidence.d.ts +54 -26
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +292 -40
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/find.d.ts +16 -12
- package/dist/tasks/find.d.ts.map +1 -1
- package/dist/tasks/find.js +107 -93
- package/dist/tasks/find.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts +88 -14
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +596 -253
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/hierarchy-policy.d.ts +8 -0
- package/dist/tasks/hierarchy-policy.d.ts.map +1 -1
- package/dist/tasks/hierarchy-policy.js +9 -2
- package/dist/tasks/hierarchy-policy.js.map +1 -1
- package/dist/tasks/hierarchy.d.ts +67 -1
- package/dist/tasks/hierarchy.d.ts.map +1 -1
- package/dist/tasks/hierarchy.js +81 -1
- package/dist/tasks/hierarchy.js.map +1 -1
- package/dist/tasks/index.d.ts +3 -0
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +3 -0
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/infer-add-params.d.ts +1 -20
- package/dist/tasks/infer-add-params.d.ts.map +1 -1
- package/dist/tasks/infer-add-params.js +2 -203
- package/dist/tasks/infer-add-params.js.map +1 -1
- package/dist/tasks/list.d.ts +7 -2
- package/dist/tasks/list.d.ts.map +1 -1
- package/dist/tasks/list.js +24 -36
- package/dist/tasks/list.js.map +1 -1
- package/dist/tasks/ops.d.ts +6 -61
- package/dist/tasks/ops.d.ts.map +1 -1
- package/dist/tasks/ops.js +5 -53
- package/dist/tasks/ops.js.map +1 -1
- package/dist/tasks/plan.d.ts +15 -1
- package/dist/tasks/plan.d.ts.map +1 -1
- package/dist/tasks/plan.js +23 -25
- package/dist/tasks/plan.js.map +1 -1
- package/dist/tasks/population.d.ts +46 -0
- package/dist/tasks/population.d.ts.map +1 -0
- package/dist/tasks/population.js +88 -0
- package/dist/tasks/population.js.map +1 -0
- package/dist/tasks/reconcile-scope.d.ts +157 -0
- package/dist/tasks/reconcile-scope.d.ts.map +1 -0
- package/dist/tasks/reconcile-scope.js +242 -0
- package/dist/tasks/reconcile-scope.js.map +1 -0
- package/dist/tasks/req.d.ts +51 -12
- package/dist/tasks/req.d.ts.map +1 -1
- package/dist/tasks/req.js +125 -69
- package/dist/tasks/req.js.map +1 -1
- package/dist/tasks/session-scope.d.ts +6 -31
- package/dist/tasks/session-scope.d.ts.map +1 -1
- package/dist/tasks/session-scope.js +3 -17
- package/dist/tasks/session-scope.js.map +1 -1
- package/dist/tasks/severity-attestation.d.ts +39 -10
- package/dist/tasks/severity-attestation.d.ts.map +1 -1
- package/dist/tasks/severity-attestation.js +75 -36
- package/dist/tasks/severity-attestation.js.map +1 -1
- package/dist/tasks/task-next.d.ts +5 -0
- package/dist/tasks/task-next.d.ts.map +1 -1
- package/dist/tasks/task-next.js +10 -9
- package/dist/tasks/task-next.js.map +1 -1
- package/dist/tasks/task-reparent.d.ts.map +1 -1
- package/dist/tasks/task-reparent.js +3 -13
- package/dist/tasks/task-reparent.js.map +1 -1
- package/dist/tasks/update.d.ts +20 -43
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +64 -49
- package/dist/tasks/update.js.map +1 -1
- package/dist/validation/engine-ops.d.ts +17 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +149 -11
- package/dist/validation/engine-ops.js.map +1 -1
- package/dist/workgraph/reparent-preflight.d.ts +1 -1
- package/dist/workgraph/reparent-preflight.d.ts.map +1 -1
- package/dist/workgraph/reparent-preflight.js +6 -1
- package/dist/workgraph/reparent-preflight.js.map +1 -1
- package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
- package/dist/workgraph/scaffold-apply.js +81 -76
- package/dist/workgraph/scaffold-apply.js.map +1 -1
- package/dist/workgraph/structure-validation.d.ts +1 -1
- package/dist/workgraph/structure-validation.d.ts.map +1 -1
- package/dist/workgraph/structure-validation.js +6 -1
- package/dist/workgraph/structure-validation.js.map +1 -1
- package/migrations/drizzle-tasks/20260919180000_t12263-job-ownership-fences/migration.sql +34 -0
- package/migrations/drizzle-tasks/20260919184500_t12265-durable-pending-proposals/migration.sql +4 -0
- package/package.json +18 -13
- package/schemas/config.schema.json +2 -2
- package/templates/CLEO-INJECTION.md +103 -101
|
@@ -65,7 +65,12 @@
|
|
|
65
65
|
* @task T11993
|
|
66
66
|
*/
|
|
67
67
|
import { spawn, spawnSync } from 'node:child_process';
|
|
68
|
+
import { randomUUID } from 'node:crypto';
|
|
68
69
|
import { readFileSync } from 'node:fs';
|
|
70
|
+
import { isAbsolute, join } from 'node:path';
|
|
71
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
72
|
+
import { z } from 'zod';
|
|
73
|
+
import { registerTeardownAbort } from '../teardown-signal.js';
|
|
69
74
|
// ---------------------------------------------------------------------------
|
|
70
75
|
// Constants
|
|
71
76
|
// ---------------------------------------------------------------------------
|
|
@@ -99,39 +104,101 @@ const OOM_AVOID_CLASSES = new Set(['daemon', 'db']);
|
|
|
99
104
|
// Internal helpers
|
|
100
105
|
// ---------------------------------------------------------------------------
|
|
101
106
|
/**
|
|
102
|
-
* Is `systemd-run` available
|
|
107
|
+
* Is `systemd-run` available for this manager context? Probed and cached per connection.
|
|
103
108
|
* Mirrors the probe in `packages/core/src/check/pr-gate.ts`.
|
|
104
109
|
*/
|
|
105
|
-
let
|
|
110
|
+
let _forcedSystemdRunAvailable;
|
|
111
|
+
const systemdProbeCache = new Map();
|
|
112
|
+
/** Construct a manager environment without mutating the caller or global state. */
|
|
113
|
+
function managerEnvironment(context, original) {
|
|
114
|
+
if (!context)
|
|
115
|
+
return original;
|
|
116
|
+
if (!isAbsolute(context.runtimeDirectory))
|
|
117
|
+
throw new TypeError('Systemd manager runtime directory must be absolute');
|
|
118
|
+
const address = context.busAddress ?? `unix:path=${join(context.runtimeDirectory, 'bus')}`;
|
|
119
|
+
if (!address.startsWith('unix:'))
|
|
120
|
+
throw new TypeError('Systemd manager bus must use a local Unix address');
|
|
121
|
+
return {
|
|
122
|
+
...original,
|
|
123
|
+
XDG_RUNTIME_DIR: context.runtimeDirectory,
|
|
124
|
+
DBUS_SESSION_BUS_ADDRESS: address,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Check the original launch boundary without claiming preemption of synchronous calls. */
|
|
128
|
+
function launchRemaining(execution) {
|
|
129
|
+
execution?.signal?.throwIfAborted();
|
|
130
|
+
if (!execution)
|
|
131
|
+
return 1000;
|
|
132
|
+
if (!Number.isSafeInteger(execution.deadlineAt))
|
|
133
|
+
throw new RangeError('Process deadline must be an absolute integer timestamp');
|
|
134
|
+
const remaining = execution.deadlineAt - Date.now();
|
|
135
|
+
if (remaining <= 0)
|
|
136
|
+
throw new Error('E_PROCESS_DEADLINE: original process launch deadline expired');
|
|
137
|
+
return remaining;
|
|
138
|
+
}
|
|
106
139
|
/**
|
|
107
|
-
* Check whether
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
140
|
+
* Check whether the selected local user manager can launch transient scopes.
|
|
141
|
+
* @param context - Explicit manager context; omitted callers retain ambient discovery.
|
|
142
|
+
* @param execution - Original deadline/cancellation; each synchronous probe uses its remaining budget.
|
|
143
|
+
* @returns Whether systemd binaries and the selected user bus answered the bounded probes.
|
|
144
|
+
* @remarks Availability does not prove that a later scope or resource bound was established.
|
|
145
|
+
* Probe results are keyed by platform, executable search path and manager connection,
|
|
146
|
+
* so an unavailable ambient context cannot poison a later explicit context.
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const available = hasSystemdRun({ runtimeDirectory: '/run/user/1000' });
|
|
150
|
+
* ```
|
|
112
151
|
*/
|
|
113
|
-
export function hasSystemdRun() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
152
|
+
export function hasSystemdRun(context, execution) {
|
|
153
|
+
launchRemaining(execution);
|
|
154
|
+
const env = managerEnvironment(context, process.env);
|
|
155
|
+
if (_forcedSystemdRunAvailable !== undefined)
|
|
156
|
+
return _forcedSystemdRunAvailable;
|
|
157
|
+
const key = JSON.stringify([
|
|
158
|
+
process.platform,
|
|
159
|
+
env['PATH'],
|
|
160
|
+
env['DBUS_SESSION_BUS_ADDRESS'],
|
|
161
|
+
env['XDG_RUNTIME_DIR'],
|
|
162
|
+
]);
|
|
163
|
+
const cached = systemdProbeCache.get(key);
|
|
164
|
+
if (cached !== undefined)
|
|
165
|
+
return cached;
|
|
166
|
+
let available = false;
|
|
167
|
+
if (process.platform === 'linux' && (env['DBUS_SESSION_BUS_ADDRESS'] || env['XDG_RUNTIME_DIR'])) {
|
|
168
|
+
const probe = spawnSync('systemd-run', ['--version'], {
|
|
169
|
+
env,
|
|
170
|
+
stdio: 'ignore',
|
|
171
|
+
timeout: Math.min(1000, launchRemaining(execution)),
|
|
172
|
+
});
|
|
173
|
+
launchRemaining(execution);
|
|
174
|
+
const bus = probe.status === 0
|
|
175
|
+
? spawnSync('systemctl', ['--user', 'show-environment'], {
|
|
176
|
+
env,
|
|
177
|
+
stdio: 'ignore',
|
|
178
|
+
timeout: Math.min(1000, launchRemaining(execution)),
|
|
179
|
+
})
|
|
180
|
+
: null;
|
|
181
|
+
launchRemaining(execution);
|
|
182
|
+
available = probe.status === 0 && bus?.status === 0;
|
|
119
183
|
}
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
184
|
+
// Bound contexts retained by long-lived callers with many synthetic workspaces.
|
|
185
|
+
if (systemdProbeCache.size >= 64)
|
|
186
|
+
systemdProbeCache.clear();
|
|
187
|
+
systemdProbeCache.set(key, available);
|
|
188
|
+
return available;
|
|
124
189
|
}
|
|
125
190
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
191
|
+
* Override availability for deterministic tests, or clear the override and cached probes.
|
|
192
|
+
* @param available - Forced availability; undefined restores real context-specific probing.
|
|
193
|
+
* @remarks Test overrides do not establish actual manager availability or containment.
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* _forceSystemdRunAvailable(undefined);
|
|
197
|
+
* ```
|
|
132
198
|
*/
|
|
133
199
|
export function _forceSystemdRunAvailable(available) {
|
|
134
|
-
|
|
200
|
+
_forcedSystemdRunAvailable = available;
|
|
201
|
+
systemdProbeCache.clear();
|
|
135
202
|
}
|
|
136
203
|
/** Whether we have already emitted the pgid-demotion log line for this process. */
|
|
137
204
|
let _pgidDemotionLogged = false;
|
|
@@ -205,10 +272,15 @@ let _scopeCounter = 0;
|
|
|
205
272
|
* @param args - Arguments to pass to the executable.
|
|
206
273
|
* @param opts - Wrapper options (scope class, resources, etc.).
|
|
207
274
|
* @returns Build result with final command, args, mode, and unit name.
|
|
275
|
+
* @remarks This only constructs launch arguments; it does not establish or inspect a scope.
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* const launch = buildSpawnArgs('node', ['--version']);
|
|
279
|
+
* ```
|
|
208
280
|
*/
|
|
209
281
|
export function buildSpawnArgs(command, args, opts = {}) {
|
|
210
282
|
const { scopeClass = 'agent', scopeId, resources = {}, noCoreFile = true } = opts;
|
|
211
|
-
if (!hasSystemdRun()) {
|
|
283
|
+
if (!hasSystemdRun(opts.systemdControl, opts.execution)) {
|
|
212
284
|
if (!_pgidDemotionLogged) {
|
|
213
285
|
_pgidDemotionLogged = true;
|
|
214
286
|
process.stderr.write('[cleo:spawn-wrapper] systemd-run unavailable — falling back to plain pgid spawn ' +
|
|
@@ -246,6 +318,11 @@ export function buildSpawnArgs(command, args, opts = {}) {
|
|
|
246
318
|
'-p',
|
|
247
319
|
'MemorySwapMax=0',
|
|
248
320
|
];
|
|
321
|
+
if (resources.tasksMax !== undefined) {
|
|
322
|
+
if (!Number.isSafeInteger(resources.tasksMax) || resources.tasksMax < 1)
|
|
323
|
+
throw new RangeError('Scope task limit must be a positive safe integer');
|
|
324
|
+
wrapArgs.push('-p', `TasksMax=${resources.tasksMax}`);
|
|
325
|
+
}
|
|
249
326
|
// MemoryHigh: only emit the directive when it is a real limit (not infinity).
|
|
250
327
|
if (highStr !== 'infinity') {
|
|
251
328
|
wrapArgs.push('-p', `MemoryHigh=${highStr}`);
|
|
@@ -290,10 +367,41 @@ export function buildSpawnArgs(command, args, opts = {}) {
|
|
|
290
367
|
* @param spawnOpts - Options forwarded to `child_process.spawn`.
|
|
291
368
|
* @param wrapOpts - Wrapper options (scope class, resources, etc.).
|
|
292
369
|
* @returns Wrapped spawn result.
|
|
370
|
+
* @remarks An explicit manager context applies only to the launcher. Child manager variables
|
|
371
|
+
* are restored before execution, while all other child environment values remain in env.
|
|
372
|
+
* Scope membership, limits and cleanup require independent observation.
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* const launched = spawnWrapped('node', ['--version'], { env: process.env });
|
|
376
|
+
* ```
|
|
293
377
|
*/
|
|
294
378
|
export function spawnWrapped(command, args, spawnOpts = {}, wrapOpts = {}) {
|
|
295
|
-
const
|
|
296
|
-
const
|
|
379
|
+
const execution = wrapOpts.execution ? { ...wrapOpts.execution } : undefined;
|
|
380
|
+
const options = {
|
|
381
|
+
...wrapOpts,
|
|
382
|
+
execution,
|
|
383
|
+
systemdControl: wrapOpts.systemdControl ? { ...wrapOpts.systemdControl } : undefined,
|
|
384
|
+
};
|
|
385
|
+
launchRemaining(execution);
|
|
386
|
+
const control = options.systemdControl;
|
|
387
|
+
const childEnvironment = spawnOpts?.env ?? process.env;
|
|
388
|
+
const controlled = control !== undefined && hasSystemdRun(control, execution);
|
|
389
|
+
// Only non-secret manager path/address values enter the env shim's argv.
|
|
390
|
+
// Arbitrary child environment (including credentials) stays in the environment.
|
|
391
|
+
const restored = [];
|
|
392
|
+
if (controlled) {
|
|
393
|
+
for (const key of ['XDG_RUNTIME_DIR', 'DBUS_SESSION_BUS_ADDRESS']) {
|
|
394
|
+
restored.push(`--unset=${key}`);
|
|
395
|
+
}
|
|
396
|
+
for (const key of ['XDG_RUNTIME_DIR', 'DBUS_SESSION_BUS_ADDRESS']) {
|
|
397
|
+
const value = childEnvironment[key];
|
|
398
|
+
if (value !== undefined)
|
|
399
|
+
restored.push(`${key}=${value}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const built = buildSpawnArgs(controlled ? 'env' : command, controlled ? [...restored, command, ...args] : args, options);
|
|
403
|
+
launchRemaining(execution);
|
|
404
|
+
const child = spawn(built.command, built.args, controlled ? { ...spawnOpts, env: managerEnvironment(control, childEnvironment) } : spawnOpts);
|
|
297
405
|
return {
|
|
298
406
|
child,
|
|
299
407
|
pid: child.pid,
|
|
@@ -301,4 +409,454 @@ export function spawnWrapped(command, args, spawnOpts = {}, wrapOpts = {}) {
|
|
|
301
409
|
unitName: built.unitName,
|
|
302
410
|
};
|
|
303
411
|
}
|
|
412
|
+
/** Transport reports target events separately from the enclosing scope wrapper. */
|
|
413
|
+
const CAPTURE_TRANSPORT = String.raw `
|
|
414
|
+
(() => {
|
|
415
|
+
const { spawn } = require('node:child_process');
|
|
416
|
+
const send = (event) => process.stdout.write(JSON.stringify(event) + '\n');
|
|
417
|
+
const [limitsJson, command, ...args] = process.argv.slice(1);
|
|
418
|
+
const limits = JSON.parse(limitsJson);
|
|
419
|
+
if (limits.memoryMaxBytes !== undefined || limits.tasksMax !== undefined) {
|
|
420
|
+
try {
|
|
421
|
+
const { readFileSync } = require('node:fs');
|
|
422
|
+
const { join } = require('node:path');
|
|
423
|
+
const entries = readFileSync('/proc/self/cgroup', 'utf8').trim().split('\n');
|
|
424
|
+
const unified = entries.find((entry) => entry.startsWith('0::'));
|
|
425
|
+
if (!unified) throw new Error('Unified cgroup membership unavailable');
|
|
426
|
+
const cgroup = unified.slice(3);
|
|
427
|
+
if (!cgroup.startsWith('/') || cgroup.split('/').includes('..') || !cgroup.split('/').includes(limits.unitName))
|
|
428
|
+
throw new Error('Transport is not inside the requested owned scope');
|
|
429
|
+
const readLimit = (name) => {
|
|
430
|
+
const value = readFileSync(join('/sys/fs/cgroup', cgroup, name), 'utf8').trim();
|
|
431
|
+
if (value === 'max') return null;
|
|
432
|
+
const limit = Number(value);
|
|
433
|
+
if (!Number.isSafeInteger(limit) || limit < 1) throw new Error('Invalid kernel limit: ' + name);
|
|
434
|
+
return limit;
|
|
435
|
+
};
|
|
436
|
+
const memoryMaxBytes = readLimit('memory.max');
|
|
437
|
+
const tasksMax = readLimit('pids.max');
|
|
438
|
+
if (limits.memoryMaxBytes !== undefined && (memoryMaxBytes === null || memoryMaxBytes > limits.memoryMaxBytes))
|
|
439
|
+
throw new Error('Requested memory ceiling was not observed');
|
|
440
|
+
if (limits.tasksMax !== undefined && (tasksMax === null || tasksMax > limits.tasksMax))
|
|
441
|
+
throw new Error('Requested task ceiling was not observed');
|
|
442
|
+
send({ type: 'resources', cgroup, memoryMaxBytes, tasksMax });
|
|
443
|
+
} catch (error) {
|
|
444
|
+
send({ type: 'resource-error', message: String(error.message) });
|
|
445
|
+
process.exitCode = 125;
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
450
|
+
child.once('spawn', () => send({ type: 'started', pid: child.pid }));
|
|
451
|
+
child.once('error', (error) => send({ type: 'error', message: String(error.code || '') + ': ' + error.message }));
|
|
452
|
+
for (const stream of ['stdout', 'stderr']) {
|
|
453
|
+
child[stream].on('data', (chunk) => {
|
|
454
|
+
for (let offset = 0; offset < chunk.length; offset += 16384)
|
|
455
|
+
send({ type: 'data', stream, bytes: chunk.subarray(offset, offset + 16384).toString('base64') });
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
child.once('close', (code, signal) => send({ type: 'closed', code, signal }));
|
|
459
|
+
})();
|
|
460
|
+
`;
|
|
461
|
+
/** Internal framing is validated before it can supply a target verdict. */
|
|
462
|
+
const captureFrameSchema = z.discriminatedUnion('type', [
|
|
463
|
+
z.object({ type: z.literal('resource-error'), message: z.string() }).strict(),
|
|
464
|
+
z
|
|
465
|
+
.object({
|
|
466
|
+
type: z.literal('resources'),
|
|
467
|
+
cgroup: z.string(),
|
|
468
|
+
memoryMaxBytes: z.number().int().positive().safe().nullable(),
|
|
469
|
+
tasksMax: z.number().int().positive().safe().nullable(),
|
|
470
|
+
})
|
|
471
|
+
.strict(),
|
|
472
|
+
z.object({ type: z.literal('started'), pid: z.number().int().positive() }).strict(),
|
|
473
|
+
z.object({ type: z.literal('error'), message: z.string() }).strict(),
|
|
474
|
+
z
|
|
475
|
+
.object({ type: z.literal('data'), stream: z.enum(['stdout', 'stderr']), bytes: z.string() })
|
|
476
|
+
.strict(),
|
|
477
|
+
z
|
|
478
|
+
.object({
|
|
479
|
+
type: z.literal('closed'),
|
|
480
|
+
code: z.number().int().nullable(),
|
|
481
|
+
signal: z.string().nullable(),
|
|
482
|
+
})
|
|
483
|
+
.strict(),
|
|
484
|
+
]);
|
|
485
|
+
/**
|
|
486
|
+
* Capture an owned process with a shared deadline, cancellation and bounded output.
|
|
487
|
+
* @param command - Exact executable; never interpreted as a shell expression.
|
|
488
|
+
* @param args - Exact target arguments; no environment values are added to argv.
|
|
489
|
+
* @param options - Explicit captured routing, environment and original execution budget.
|
|
490
|
+
* @returns Target events and independent transport, stop and cleanup diagnostics.
|
|
491
|
+
* @throws When admission inputs are invalid, the original context has ended, or the platform cannot provide process-group cleanup.
|
|
492
|
+
* @remarks A fixed Node transport distinguishes the requested process from systemd/sh
|
|
493
|
+
* launchers. Cleanup has a bounded additional manager timeout and can exceed the
|
|
494
|
+
* execution deadline. POSIX process groups cannot contain deliberately escaped
|
|
495
|
+
* descendants; Windows capture is refused. Requested hard memory/task limits require
|
|
496
|
+
* observation inside the exact owned cgroup before the target is launched. Calls without
|
|
497
|
+
* requests retain unverified memory limits. This is a transport, not a job scheduler.
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const result = await captureWrapped('node', ['check.mjs'], {
|
|
501
|
+
* cwd: projectRoot, env: {}, execution: { deadlineAt: Date.now() + 2000 },
|
|
502
|
+
* });
|
|
503
|
+
* if (result.started && !result.stopped && result.exitCode === 0) inspect(result);
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
export async function captureWrapped(command, args, options) {
|
|
507
|
+
if (process.platform === 'win32')
|
|
508
|
+
throw new Error('E_PROCESS_CONTAINMENT: bounded process-group capture is unavailable on Windows');
|
|
509
|
+
if (!isAbsolute(options.cwd))
|
|
510
|
+
throw new TypeError('Captured process cwd must be absolute');
|
|
511
|
+
const maxOutputBytes = options.maxOutputBytes ?? 1_048_576;
|
|
512
|
+
if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1)
|
|
513
|
+
throw new RangeError('Capture byte limit must be a positive safe integer');
|
|
514
|
+
const memoryMaxMb = options.memoryMaxMb;
|
|
515
|
+
const tasksMax = options.tasksMax;
|
|
516
|
+
for (const [name, value] of [
|
|
517
|
+
['memoryMaxMb', memoryMaxMb],
|
|
518
|
+
['tasksMax', tasksMax],
|
|
519
|
+
]) {
|
|
520
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 1))
|
|
521
|
+
throw new RangeError(`${name} must be a positive safe integer`);
|
|
522
|
+
}
|
|
523
|
+
const memoryMaxBytes = memoryMaxMb === undefined ? undefined : memoryMaxMb * 1024 * 1024;
|
|
524
|
+
if (memoryMaxBytes !== undefined && !Number.isSafeInteger(memoryMaxBytes))
|
|
525
|
+
throw new RangeError('Requested memory ceiling exceeds exact byte representation');
|
|
526
|
+
const scopeId = `${process.pid}-${randomUUID()}`;
|
|
527
|
+
const requestedLimits = { memoryMaxBytes, tasksMax, unitName: `cleo-tool-${scopeId}.scope` };
|
|
528
|
+
const requiresLimits = memoryMaxMb !== undefined || tasksMax !== undefined;
|
|
529
|
+
const execution = { ...options.execution };
|
|
530
|
+
const env = { ...options.env };
|
|
531
|
+
const systemdControl = options.systemdControl ? { ...options.systemdControl } : undefined;
|
|
532
|
+
const startedAt = Date.now();
|
|
533
|
+
launchRemaining(execution);
|
|
534
|
+
const controller = new AbortController();
|
|
535
|
+
const deregister = registerTeardownAbort(controller);
|
|
536
|
+
let owned;
|
|
537
|
+
try {
|
|
538
|
+
controller.signal.throwIfAborted();
|
|
539
|
+
owned = spawnWrapped(process.execPath, ['-e', CAPTURE_TRANSPORT, JSON.stringify(requestedLimits), command, ...args], {
|
|
540
|
+
cwd: options.cwd,
|
|
541
|
+
env,
|
|
542
|
+
detached: true,
|
|
543
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
544
|
+
}, {
|
|
545
|
+
scopeClass: 'tool',
|
|
546
|
+
scopeId,
|
|
547
|
+
resources: {
|
|
548
|
+
...(memoryMaxMb === undefined ? {} : { memoryMax: `${memoryMaxMb}M` }),
|
|
549
|
+
tasksMax,
|
|
550
|
+
},
|
|
551
|
+
execution,
|
|
552
|
+
systemdControl,
|
|
553
|
+
noCoreFile: false,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
deregister();
|
|
558
|
+
throw error;
|
|
559
|
+
}
|
|
560
|
+
return new Promise((resolve) => {
|
|
561
|
+
const { child } = owned;
|
|
562
|
+
let resourceLimits;
|
|
563
|
+
let started = false;
|
|
564
|
+
let targetPid = null;
|
|
565
|
+
let exitCode = null;
|
|
566
|
+
let signal = null;
|
|
567
|
+
let error = null;
|
|
568
|
+
let stopped = null;
|
|
569
|
+
let closedFrame = false;
|
|
570
|
+
let pending = '';
|
|
571
|
+
let outputBytes = 0;
|
|
572
|
+
let outputTruncated = false;
|
|
573
|
+
let wrapperError = '';
|
|
574
|
+
const stdout = [];
|
|
575
|
+
const stderr = [];
|
|
576
|
+
const cleanupErrors = [];
|
|
577
|
+
let cleanupObservation = 'unverified';
|
|
578
|
+
let terminated = false;
|
|
579
|
+
const terminate = () => {
|
|
580
|
+
if (terminated)
|
|
581
|
+
return;
|
|
582
|
+
terminated = true;
|
|
583
|
+
if (child.pid) {
|
|
584
|
+
try {
|
|
585
|
+
if (process.platform === 'win32')
|
|
586
|
+
child.kill('SIGKILL');
|
|
587
|
+
else {
|
|
588
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
589
|
+
cleanupObservation = 'process-group-signalled';
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
catch (cause) {
|
|
593
|
+
if (cause instanceof Error && 'code' in cause && cause.code === 'ESRCH')
|
|
594
|
+
cleanupObservation = 'process-group-absent';
|
|
595
|
+
else
|
|
596
|
+
cleanupErrors.push(String(cause));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (owned.unitName) {
|
|
600
|
+
const cleanup = spawnSync('systemctl', ['--user', 'kill', '--kill-whom=all', '--signal=SIGKILL', owned.unitName], {
|
|
601
|
+
env: managerEnvironment(systemdControl, env),
|
|
602
|
+
timeout: 1000,
|
|
603
|
+
maxBuffer: 4096,
|
|
604
|
+
encoding: 'utf8',
|
|
605
|
+
});
|
|
606
|
+
const observation = spawnSync('systemctl', ['--user', 'show', owned.unitName, '--property=LoadState', '--property=ActiveState'], {
|
|
607
|
+
env: managerEnvironment(systemdControl, env),
|
|
608
|
+
timeout: 1000,
|
|
609
|
+
maxBuffer: 4096,
|
|
610
|
+
encoding: 'utf8',
|
|
611
|
+
});
|
|
612
|
+
const inactive = observation.stdout
|
|
613
|
+
?.split('\n')
|
|
614
|
+
.some((line) => line === 'LoadState=not-found' ||
|
|
615
|
+
line === 'ActiveState=inactive' ||
|
|
616
|
+
line === 'ActiveState=failed');
|
|
617
|
+
if (!observation.error && inactive)
|
|
618
|
+
cleanupObservation = 'scope-terminal';
|
|
619
|
+
if (observation.error || !inactive)
|
|
620
|
+
cleanupErrors.push(`Scope cleanup unverified: ${observation.error?.message ??
|
|
621
|
+
observation.stderr?.trim() ??
|
|
622
|
+
cleanup.error?.message ??
|
|
623
|
+
cleanup.stderr?.trim() ??
|
|
624
|
+
'scope remains active'}`);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
const stop = (reason, diagnostic) => {
|
|
628
|
+
stopped ??= reason;
|
|
629
|
+
if (diagnostic)
|
|
630
|
+
error ??= diagnostic;
|
|
631
|
+
terminate();
|
|
632
|
+
};
|
|
633
|
+
const cancelled = () => stop(Date.now() >= execution.deadlineAt ? 'deadline' : 'cancelled');
|
|
634
|
+
const teardown = () => stop('teardown');
|
|
635
|
+
let timer;
|
|
636
|
+
const deadline = () => {
|
|
637
|
+
const remaining = execution.deadlineAt - Date.now();
|
|
638
|
+
if (remaining <= 0)
|
|
639
|
+
stop('deadline');
|
|
640
|
+
else
|
|
641
|
+
timer = setTimeout(deadline, Math.min(2_147_483_647, remaining));
|
|
642
|
+
};
|
|
643
|
+
deadline();
|
|
644
|
+
execution.signal?.addEventListener('abort', cancelled, { once: true });
|
|
645
|
+
controller.signal.addEventListener('abort', teardown, { once: true });
|
|
646
|
+
child.stdout?.setEncoding('utf8');
|
|
647
|
+
child.stdout?.on('data', (chunk) => {
|
|
648
|
+
pending += chunk;
|
|
649
|
+
for (;;) {
|
|
650
|
+
const end = pending.indexOf('\n');
|
|
651
|
+
if (end < 0)
|
|
652
|
+
break;
|
|
653
|
+
const line = pending.slice(0, end);
|
|
654
|
+
pending = pending.slice(end + 1);
|
|
655
|
+
if (line.length > 32768) {
|
|
656
|
+
stop('transport-error', 'Capture frame exceeds byte bound');
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
try {
|
|
660
|
+
const frame = captureFrameSchema.parse(JSON.parse(line));
|
|
661
|
+
if (closedFrame)
|
|
662
|
+
throw new Error('Capture frame received after target close');
|
|
663
|
+
switch (frame.type) {
|
|
664
|
+
case 'resource-error':
|
|
665
|
+
stop('resource-limit', `E_PROCESS_RESOURCE_LIMIT: ${frame.message}`);
|
|
666
|
+
break;
|
|
667
|
+
case 'resources':
|
|
668
|
+
if (!requiresLimits ||
|
|
669
|
+
resourceLimits ||
|
|
670
|
+
started ||
|
|
671
|
+
owned.mode !== 'systemd' ||
|
|
672
|
+
!frame.cgroup.split('/').includes(requestedLimits.unitName) ||
|
|
673
|
+
(memoryMaxBytes !== undefined &&
|
|
674
|
+
(frame.memoryMaxBytes === null || frame.memoryMaxBytes > memoryMaxBytes)) ||
|
|
675
|
+
(tasksMax !== undefined && (frame.tasksMax === null || frame.tasksMax > tasksMax)))
|
|
676
|
+
throw new Error('Invalid or insufficient resource observation');
|
|
677
|
+
resourceLimits = {
|
|
678
|
+
cgroup: frame.cgroup,
|
|
679
|
+
memoryMaxBytes: frame.memoryMaxBytes,
|
|
680
|
+
tasksMax: frame.tasksMax,
|
|
681
|
+
};
|
|
682
|
+
break;
|
|
683
|
+
case 'started':
|
|
684
|
+
if (requiresLimits && !resourceLimits)
|
|
685
|
+
throw new Error('Target started without required resource observation');
|
|
686
|
+
if (started || error)
|
|
687
|
+
throw new Error('Duplicate or contradictory target start');
|
|
688
|
+
started = true;
|
|
689
|
+
targetPid = frame.pid;
|
|
690
|
+
break;
|
|
691
|
+
case 'error':
|
|
692
|
+
error = frame.message;
|
|
693
|
+
break;
|
|
694
|
+
case 'closed':
|
|
695
|
+
closedFrame = true;
|
|
696
|
+
if (started) {
|
|
697
|
+
exitCode = frame.code;
|
|
698
|
+
signal = frame.signal;
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
case 'data': {
|
|
702
|
+
if (!started)
|
|
703
|
+
throw new Error('Output before target spawn');
|
|
704
|
+
const bytes = Buffer.from(frame.bytes, 'base64');
|
|
705
|
+
if (bytes.toString('base64') !== frame.bytes)
|
|
706
|
+
throw new Error('Malformed output encoding');
|
|
707
|
+
const allowed = Math.max(0, maxOutputBytes - outputBytes);
|
|
708
|
+
(frame.stream === 'stdout' ? stdout : stderr).push(bytes.subarray(0, allowed));
|
|
709
|
+
outputBytes += bytes.length;
|
|
710
|
+
if (outputBytes > maxOutputBytes) {
|
|
711
|
+
outputTruncated = true;
|
|
712
|
+
stop('output-limit');
|
|
713
|
+
}
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
catch (cause) {
|
|
719
|
+
stop('transport-error', `Invalid capture transport: ${String(cause)}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (pending.length > 32768)
|
|
723
|
+
stop('transport-error', 'Unterminated capture frame exceeds bound');
|
|
724
|
+
});
|
|
725
|
+
child.stderr?.on('data', (chunk) => {
|
|
726
|
+
wrapperError = Buffer.concat([Buffer.from(wrapperError), chunk])
|
|
727
|
+
.subarray(0, 4096)
|
|
728
|
+
.toString('utf8');
|
|
729
|
+
});
|
|
730
|
+
child.once('error', (cause) => {
|
|
731
|
+
error = `Capture launcher failed: ${cause.message}`;
|
|
732
|
+
});
|
|
733
|
+
child.once('close', (wrapperCode, wrapperSignal) => {
|
|
734
|
+
clearTimeout(timer);
|
|
735
|
+
execution.signal?.removeEventListener('abort', cancelled);
|
|
736
|
+
controller.signal.removeEventListener('abort', teardown);
|
|
737
|
+
deregister();
|
|
738
|
+
if (!stopped &&
|
|
739
|
+
(!closedFrame ||
|
|
740
|
+
(!started && !error) ||
|
|
741
|
+
pending.length ||
|
|
742
|
+
wrapperCode !== 0 ||
|
|
743
|
+
wrapperSignal)) {
|
|
744
|
+
stopped = 'transport-error';
|
|
745
|
+
error ??= `Capture transport incomplete (wrapper code ${wrapperCode}, signal ${wrapperSignal}): ${wrapperError}`;
|
|
746
|
+
}
|
|
747
|
+
terminate();
|
|
748
|
+
if (Buffer.byteLength(Buffer.concat(stdout).toString('utf8')) +
|
|
749
|
+
Buffer.byteLength(Buffer.concat(stderr).toString('utf8')) >
|
|
750
|
+
maxOutputBytes) {
|
|
751
|
+
outputTruncated = true;
|
|
752
|
+
stopped ??= 'output-limit';
|
|
753
|
+
}
|
|
754
|
+
const decodedStdout = new StringDecoder('utf8').write(Buffer.from(Buffer.concat(stdout).toString('utf8')).subarray(0, maxOutputBytes));
|
|
755
|
+
const decodedStderr = new StringDecoder('utf8').write(Buffer.from(Buffer.concat(stderr).toString('utf8')).subarray(0, maxOutputBytes - Buffer.byteLength(decodedStdout)));
|
|
756
|
+
resolve({
|
|
757
|
+
started,
|
|
758
|
+
targetPid,
|
|
759
|
+
exitCode,
|
|
760
|
+
signal,
|
|
761
|
+
error,
|
|
762
|
+
stopped,
|
|
763
|
+
stdout: decodedStdout,
|
|
764
|
+
stderr: decodedStderr,
|
|
765
|
+
outputTruncated,
|
|
766
|
+
durationMs: Date.now() - startedAt,
|
|
767
|
+
mode: owned.mode,
|
|
768
|
+
...(owned.unitName ? { unitName: owned.unitName } : {}),
|
|
769
|
+
nativeMemory: resourceLimits?.memoryMaxBytes ? 'observed-cgroup' : 'unverified',
|
|
770
|
+
...(resourceLimits ? { resourceLimits } : {}),
|
|
771
|
+
cleanupScope: 'process-group',
|
|
772
|
+
transportClosed: true,
|
|
773
|
+
targetCloseObserved: closedFrame,
|
|
774
|
+
cleanupObservation,
|
|
775
|
+
cleanupErrors,
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
if (execution.signal?.aborted)
|
|
779
|
+
cancelled();
|
|
780
|
+
if (controller.signal.aborted)
|
|
781
|
+
teardown();
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Create the parser's process-execution port using the canonical spawn service.
|
|
786
|
+
* @returns A launcher with per-process V8 limits and teardown cancellation.
|
|
787
|
+
* @remarks Explicit Node argv overrides inherited NODE_OPTIONS heap flags while
|
|
788
|
+
* preserving loader guards. Configured cgroup memory is not claimed as observed
|
|
789
|
+
* native-memory containment. The parser verifies its actual V8 limit at startup.
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* const parserExecution = createParserExecutionPort();
|
|
793
|
+
* ```
|
|
794
|
+
*/
|
|
795
|
+
export function createParserExecutionPort() {
|
|
796
|
+
return {
|
|
797
|
+
spawn(scriptPath, limits) {
|
|
798
|
+
const heapMb = limits.workerHeapMb ?? 128;
|
|
799
|
+
if (!Number.isSafeInteger(heapMb) || heapMb < 8 || heapMb > 512) {
|
|
800
|
+
throw new RangeError('Parser worker heap must be an integer from 8 to 512 MiB');
|
|
801
|
+
}
|
|
802
|
+
limits.signal?.throwIfAborted();
|
|
803
|
+
const controller = new AbortController();
|
|
804
|
+
const deregister = registerTeardownAbort(controller);
|
|
805
|
+
if (controller.signal.aborted) {
|
|
806
|
+
deregister();
|
|
807
|
+
controller.signal.throwIfAborted();
|
|
808
|
+
}
|
|
809
|
+
let owned;
|
|
810
|
+
try {
|
|
811
|
+
owned = spawnWrapped(process.execPath, [`--max-old-space-size=${heapMb}`, '--max-semi-space-size=8', scriptPath], {
|
|
812
|
+
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
813
|
+
detached: process.platform !== 'win32',
|
|
814
|
+
env: process.env,
|
|
815
|
+
}, { scopeClass: 'tool', resources: { memoryMax: `${Math.max(256, heapMb * 2)}M` } });
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
deregister();
|
|
819
|
+
throw error;
|
|
820
|
+
}
|
|
821
|
+
const { child } = owned;
|
|
822
|
+
// Drain diagnostics to avoid blocking a child on a full stderr pipe.
|
|
823
|
+
child.stderr?.on('data', () => undefined);
|
|
824
|
+
let exited = false;
|
|
825
|
+
const closed = new Promise((resolve) => {
|
|
826
|
+
child.once('close', () => {
|
|
827
|
+
exited = true;
|
|
828
|
+
resolve();
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
const stop = async () => {
|
|
832
|
+
if (!exited && child.pid) {
|
|
833
|
+
try {
|
|
834
|
+
if (process.platform === 'win32')
|
|
835
|
+
child.kill('SIGKILL');
|
|
836
|
+
else
|
|
837
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
838
|
+
}
|
|
839
|
+
catch (error) {
|
|
840
|
+
if (!(error instanceof Error) || !('code' in error) || error.code !== 'ESRCH')
|
|
841
|
+
throw error;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
await closed;
|
|
845
|
+
};
|
|
846
|
+
const abort = () => {
|
|
847
|
+
void stop().catch(() => undefined);
|
|
848
|
+
};
|
|
849
|
+
limits.signal?.addEventListener('abort', abort, { once: true });
|
|
850
|
+
controller.signal.addEventListener('abort', abort, { once: true });
|
|
851
|
+
child.once('close', () => {
|
|
852
|
+
limits.signal?.removeEventListener('abort', abort);
|
|
853
|
+
controller.signal.removeEventListener('abort', abort);
|
|
854
|
+
deregister();
|
|
855
|
+
});
|
|
856
|
+
if (limits.signal?.aborted || controller.signal.aborted)
|
|
857
|
+
abort();
|
|
858
|
+
return { child, heapMb, nativeMemory: 'unverified', stop };
|
|
859
|
+
},
|
|
860
|
+
};
|
|
861
|
+
}
|
|
304
862
|
//# sourceMappingURL=spawn-wrapper.js.map
|