oh-my-super-agent 0.1.0
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/.agents/agents/advisor.md +15 -0
- package/.agents/agents/conductor.md +32 -0
- package/.agents/agents/dreamer.md +13 -0
- package/.agents/agents/executor.md +17 -0
- package/.agents/agents/grader.md +15 -0
- package/.agents/config/endurance-policy.json +8 -0
- package/.agents/config/oms-config.json +119 -0
- package/.agents/config/release-policy.json +12 -0
- package/.agents/config/secret-scan-policy.json +7 -0
- package/.agents/config/verification-plan.json +33 -0
- package/.agents/registry.json +11 -0
- package/.agents/rules/constitution.md +7 -0
- package/.agents/skills/conductor/SKILL.md +23 -0
- package/.agents/skills/dream/SKILL.md +14 -0
- package/.agents/skills/executor/SKILL.md +13 -0
- package/.agents/skills/oms/SKILL.md +52 -0
- package/.agents/skills/quota-aware/SKILL.md +12 -0
- package/.agents/skills/schedule/SKILL.md +13 -0
- package/.agents/skills/team-protocol/SKILL.md +20 -0
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/src/audit/run-ledger.d.ts +183 -0
- package/dist/src/audit/run-ledger.js +1655 -0
- package/dist/src/audit/run-ledger.js.map +1 -0
- package/dist/src/cli.d.ts +61 -0
- package/dist/src/cli.js +2096 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/github-queue-probe.d.ts +26 -0
- package/dist/src/commands/github-queue-probe.js +51 -0
- package/dist/src/commands/github-queue-probe.js.map +1 -0
- package/dist/src/commands/trace-fixture.d.ts +20 -0
- package/dist/src/commands/trace-fixture.js +72 -0
- package/dist/src/commands/trace-fixture.js.map +1 -0
- package/dist/src/conductor/activity.d.ts +46 -0
- package/dist/src/conductor/activity.js +224 -0
- package/dist/src/conductor/activity.js.map +1 -0
- package/dist/src/conductor/bridge.d.ts +53 -0
- package/dist/src/conductor/bridge.js +652 -0
- package/dist/src/conductor/bridge.js.map +1 -0
- package/dist/src/conductor/notifications.d.ts +59 -0
- package/dist/src/conductor/notifications.js +381 -0
- package/dist/src/conductor/notifications.js.map +1 -0
- package/dist/src/conductor/recovery.d.ts +29 -0
- package/dist/src/conductor/recovery.js +198 -0
- package/dist/src/conductor/recovery.js.map +1 -0
- package/dist/src/conductor/start.d.ts +73 -0
- package/dist/src/conductor/start.js +519 -0
- package/dist/src/conductor/start.js.map +1 -0
- package/dist/src/conductor/waiter.d.ts +88 -0
- package/dist/src/conductor/waiter.js +427 -0
- package/dist/src/conductor/waiter.js.map +1 -0
- package/dist/src/config.d.ts +9 -0
- package/dist/src/config.js +40 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dispatch/advice-source.d.ts +61 -0
- package/dist/src/dispatch/advice-source.js +198 -0
- package/dist/src/dispatch/advice-source.js.map +1 -0
- package/dist/src/dispatch/advice-store.d.ts +41 -0
- package/dist/src/dispatch/advice-store.js +201 -0
- package/dist/src/dispatch/advice-store.js.map +1 -0
- package/dist/src/dispatch/advice.d.ts +34 -0
- package/dist/src/dispatch/advice.js +371 -0
- package/dist/src/dispatch/advice.js.map +1 -0
- package/dist/src/dispatch/authority.d.ts +124 -0
- package/dist/src/dispatch/authority.js +1202 -0
- package/dist/src/dispatch/authority.js.map +1 -0
- package/dist/src/dispatch/intent.d.ts +66 -0
- package/dist/src/dispatch/intent.js +71 -0
- package/dist/src/dispatch/intent.js.map +1 -0
- package/dist/src/dispatch/runtime-authority.d.ts +42 -0
- package/dist/src/dispatch/runtime-authority.js +52 -0
- package/dist/src/dispatch/runtime-authority.js.map +1 -0
- package/dist/src/doctor.d.ts +20 -0
- package/dist/src/doctor.js +85 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/domain/records.d.ts +44 -0
- package/dist/src/domain/records.js +138 -0
- package/dist/src/domain/records.js.map +1 -0
- package/dist/src/domain/role-policy.d.ts +67 -0
- package/dist/src/domain/role-policy.js +547 -0
- package/dist/src/domain/role-policy.js.map +1 -0
- package/dist/src/events/bus.d.ts +32 -0
- package/dist/src/events/bus.js +115 -0
- package/dist/src/events/bus.js.map +1 -0
- package/dist/src/events/kinds.d.ts +126 -0
- package/dist/src/events/kinds.js +14 -0
- package/dist/src/events/kinds.js.map +1 -0
- package/dist/src/evidence/immutable-file.d.ts +12 -0
- package/dist/src/evidence/immutable-file.js +141 -0
- package/dist/src/evidence/immutable-file.js.map +1 -0
- package/dist/src/evidence/store.d.ts +125 -0
- package/dist/src/evidence/store.js +1189 -0
- package/dist/src/evidence/store.js.map +1 -0
- package/dist/src/evidence/verdict.d.ts +17 -0
- package/dist/src/evidence/verdict.js +23 -0
- package/dist/src/evidence/verdict.js.map +1 -0
- package/dist/src/generation/agent.d.ts +5 -0
- package/dist/src/generation/agent.js +34 -0
- package/dist/src/generation/agent.js.map +1 -0
- package/dist/src/generation/frontmatter.d.ts +7 -0
- package/dist/src/generation/frontmatter.js +33 -0
- package/dist/src/generation/frontmatter.js.map +1 -0
- package/dist/src/github/adapter.d.ts +44 -0
- package/dist/src/github/adapter.js +592 -0
- package/dist/src/github/adapter.js.map +1 -0
- package/dist/src/github/dispatch-eligibility.d.ts +9 -0
- package/dist/src/github/dispatch-eligibility.js +94 -0
- package/dist/src/github/dispatch-eligibility.js.map +1 -0
- package/dist/src/github/projector.d.ts +23 -0
- package/dist/src/github/projector.js +181 -0
- package/dist/src/github/projector.js.map +1 -0
- package/dist/src/github/review.d.ts +39 -0
- package/dist/src/github/review.js +1450 -0
- package/dist/src/github/review.js.map +1 -0
- package/dist/src/github/sync.d.ts +21 -0
- package/dist/src/github/sync.js +518 -0
- package/dist/src/github/sync.js.map +1 -0
- package/dist/src/github/types.d.ts +134 -0
- package/dist/src/github/types.js +11 -0
- package/dist/src/github/types.js.map +1 -0
- package/dist/src/goals/artifact-store.d.ts +31 -0
- package/dist/src/goals/artifact-store.js +241 -0
- package/dist/src/goals/artifact-store.js.map +1 -0
- package/dist/src/goals/authority.d.ts +112 -0
- package/dist/src/goals/authority.js +621 -0
- package/dist/src/goals/authority.js.map +1 -0
- package/dist/src/goals/native-snapshot.d.ts +12 -0
- package/dist/src/goals/native-snapshot.js +53 -0
- package/dist/src/goals/native-snapshot.js.map +1 -0
- package/dist/src/goals/runtime-authority.d.ts +7 -0
- package/dist/src/goals/runtime-authority.js +20 -0
- package/dist/src/goals/runtime-authority.js.map +1 -0
- package/dist/src/health/release-assessment.d.ts +11 -0
- package/dist/src/health/release-assessment.js +83 -0
- package/dist/src/health/release-assessment.js.map +1 -0
- package/dist/src/health/release-policy.d.ts +16 -0
- package/dist/src/health/release-policy.js +40 -0
- package/dist/src/health/release-policy.js.map +1 -0
- package/dist/src/hooks/native.d.ts +30 -0
- package/dist/src/hooks/native.js +495 -0
- package/dist/src/hooks/native.js.map +1 -0
- package/dist/src/hooks/provider-runtime.d.ts +55 -0
- package/dist/src/hooks/provider-runtime.js +479 -0
- package/dist/src/hooks/provider-runtime.js.map +1 -0
- package/dist/src/hooks/provider.d.ts +102 -0
- package/dist/src/hooks/provider.js +358 -0
- package/dist/src/hooks/provider.js.map +1 -0
- package/dist/src/live/contact-journal.d.ts +73 -0
- package/dist/src/live/contact-journal.js +439 -0
- package/dist/src/live/contact-journal.js.map +1 -0
- package/dist/src/live-probes/gh-adapter.d.ts +28 -0
- package/dist/src/live-probes/gh-adapter.js +188 -0
- package/dist/src/live-probes/gh-adapter.js.map +1 -0
- package/dist/src/live-probes/github-queue.d.ts +1 -0
- package/dist/src/live-probes/github-queue.js +2 -0
- package/dist/src/live-probes/github-queue.js.map +1 -0
- package/dist/src/notifications/slack-request.d.ts +13 -0
- package/dist/src/notifications/slack-request.js +40 -0
- package/dist/src/notifications/slack-request.js.map +1 -0
- package/dist/src/outbox/processor.d.ts +27 -0
- package/dist/src/outbox/processor.js +63 -0
- package/dist/src/outbox/processor.js.map +1 -0
- package/dist/src/outbox/store.d.ts +158 -0
- package/dist/src/outbox/store.js +834 -0
- package/dist/src/outbox/store.js.map +1 -0
- package/dist/src/package-root.d.ts +1 -0
- package/dist/src/package-root.js +26 -0
- package/dist/src/package-root.js.map +1 -0
- package/dist/src/project.d.ts +12 -0
- package/dist/src/project.js +60 -0
- package/dist/src/project.js.map +1 -0
- package/dist/src/projection/generated-file.d.ts +15 -0
- package/dist/src/projection/generated-file.js +72 -0
- package/dist/src/projection/generated-file.js.map +1 -0
- package/dist/src/projection/project-workspace.d.ts +11 -0
- package/dist/src/projection/project-workspace.js +94 -0
- package/dist/src/projection/project-workspace.js.map +1 -0
- package/dist/src/projection/registry.d.ts +15 -0
- package/dist/src/projection/registry.js +59 -0
- package/dist/src/projection/registry.js.map +1 -0
- package/dist/src/projection/safe-path.d.ts +4 -0
- package/dist/src/projection/safe-path.js +106 -0
- package/dist/src/projection/safe-path.js.map +1 -0
- package/dist/src/providers/claude-interactive.d.ts +38 -0
- package/dist/src/providers/claude-interactive.js +287 -0
- package/dist/src/providers/claude-interactive.js.map +1 -0
- package/dist/src/providers/claude-structured-bridge.d.ts +32 -0
- package/dist/src/providers/claude-structured-bridge.js +215 -0
- package/dist/src/providers/claude-structured-bridge.js.map +1 -0
- package/dist/src/providers/claude-structured-completion.d.ts +23 -0
- package/dist/src/providers/claude-structured-completion.js +229 -0
- package/dist/src/providers/claude-structured-completion.js.map +1 -0
- package/dist/src/providers/claude-structured-pane.d.ts +23 -0
- package/dist/src/providers/claude-structured-pane.js +156 -0
- package/dist/src/providers/claude-structured-pane.js.map +1 -0
- package/dist/src/providers/claude-structured-store.d.ts +48 -0
- package/dist/src/providers/claude-structured-store.js +212 -0
- package/dist/src/providers/claude-structured-store.js.map +1 -0
- package/dist/src/providers/claude-structured.d.ts +56 -0
- package/dist/src/providers/claude-structured.js +250 -0
- package/dist/src/providers/claude-structured.js.map +1 -0
- package/dist/src/providers/codex-exec.d.ts +29 -0
- package/dist/src/providers/codex-exec.js +127 -0
- package/dist/src/providers/codex-exec.js.map +1 -0
- package/dist/src/providers/grading.d.ts +2 -0
- package/dist/src/providers/grading.js +20 -0
- package/dist/src/providers/grading.js.map +1 -0
- package/dist/src/providers/native-dispatch.d.ts +24 -0
- package/dist/src/providers/native-dispatch.js +34 -0
- package/dist/src/providers/native-dispatch.js.map +1 -0
- package/dist/src/providers/preflight.d.ts +65 -0
- package/dist/src/providers/preflight.js +256 -0
- package/dist/src/providers/preflight.js.map +1 -0
- package/dist/src/providers/process.d.ts +18 -0
- package/dist/src/providers/process.js +21 -0
- package/dist/src/providers/process.js.map +1 -0
- package/dist/src/queue/model.d.ts +37 -0
- package/dist/src/queue/model.js +51 -0
- package/dist/src/queue/model.js.map +1 -0
- package/dist/src/queue/service.d.ts +1 -0
- package/dist/src/queue/service.js +2 -0
- package/dist/src/queue/service.js.map +1 -0
- package/dist/src/reflections/store.d.ts +21 -0
- package/dist/src/reflections/store.js +60 -0
- package/dist/src/reflections/store.js.map +1 -0
- package/dist/src/reports/daily-trust.d.ts +11 -0
- package/dist/src/reports/daily-trust.js +98 -0
- package/dist/src/reports/daily-trust.js.map +1 -0
- package/dist/src/runs/controller.d.ts +187 -0
- package/dist/src/runs/controller.js +1182 -0
- package/dist/src/runs/controller.js.map +1 -0
- package/dist/src/runtime/composition.d.ts +9 -0
- package/dist/src/runtime/composition.js +35 -0
- package/dist/src/runtime/composition.js.map +1 -0
- package/dist/src/runtime/endurance-authorities.d.ts +44 -0
- package/dist/src/runtime/endurance-authorities.js +101 -0
- package/dist/src/runtime/endurance-authorities.js.map +1 -0
- package/dist/src/runtime/endurance-policy.d.ts +12 -0
- package/dist/src/runtime/endurance-policy.js +13 -0
- package/dist/src/runtime/endurance-policy.js.map +1 -0
- package/dist/src/runtime/lock.d.ts +63 -0
- package/dist/src/runtime/lock.js +577 -0
- package/dist/src/runtime/lock.js.map +1 -0
- package/dist/src/runtime/process-identity.d.ts +10 -0
- package/dist/src/runtime/process-identity.js +43 -0
- package/dist/src/runtime/process-identity.js.map +1 -0
- package/dist/src/runtime/quota.d.ts +130 -0
- package/dist/src/runtime/quota.js +590 -0
- package/dist/src/runtime/quota.js.map +1 -0
- package/dist/src/runtime/record-store.d.ts +45 -0
- package/dist/src/runtime/record-store.js +272 -0
- package/dist/src/runtime/record-store.js.map +1 -0
- package/dist/src/runtime/schema.d.ts +28 -0
- package/dist/src/runtime/schema.js +194 -0
- package/dist/src/runtime/schema.js.map +1 -0
- package/dist/src/runtime/stable-id.d.ts +3 -0
- package/dist/src/runtime/stable-id.js +27 -0
- package/dist/src/runtime/stable-id.js.map +1 -0
- package/dist/src/runtime/system-clock.d.ts +8 -0
- package/dist/src/runtime/system-clock.js +35 -0
- package/dist/src/runtime/system-clock.js.map +1 -0
- package/dist/src/schedule/service-plan.d.ts +58 -0
- package/dist/src/schedule/service-plan.js +327 -0
- package/dist/src/schedule/service-plan.js.map +1 -0
- package/dist/src/schedule/user-installation.d.ts +62 -0
- package/dist/src/schedule/user-installation.js +701 -0
- package/dist/src/schedule/user-installation.js.map +1 -0
- package/dist/src/schedule/user-plan.d.ts +47 -0
- package/dist/src/schedule/user-plan.js +286 -0
- package/dist/src/schedule/user-plan.js.map +1 -0
- package/dist/src/scheduler/minute.d.ts +31 -0
- package/dist/src/scheduler/minute.js +441 -0
- package/dist/src/scheduler/minute.js.map +1 -0
- package/dist/src/schemas/registry.d.ts +10 -0
- package/dist/src/schemas/registry.js +36 -0
- package/dist/src/schemas/registry.js.map +1 -0
- package/dist/src/security/secret-scan.d.ts +50 -0
- package/dist/src/security/secret-scan.js +384 -0
- package/dist/src/security/secret-scan.js.map +1 -0
- package/dist/src/sessions/registry.d.ts +89 -0
- package/dist/src/sessions/registry.js +443 -0
- package/dist/src/sessions/registry.js.map +1 -0
- package/dist/src/state.d.ts +27 -0
- package/dist/src/state.js +235 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/status/hud.d.ts +37 -0
- package/dist/src/status/hud.js +88 -0
- package/dist/src/status/hud.js.map +1 -0
- package/dist/src/team/plan.d.ts +31 -0
- package/dist/src/team/plan.js +184 -0
- package/dist/src/team/plan.js.map +1 -0
- package/dist/src/team/projection.d.ts +24 -0
- package/dist/src/team/projection.js +79 -0
- package/dist/src/team/projection.js.map +1 -0
- package/dist/src/tickets/admission.d.ts +34 -0
- package/dist/src/tickets/admission.js +169 -0
- package/dist/src/tickets/admission.js.map +1 -0
- package/dist/src/tickets/materializer.d.ts +1 -0
- package/dist/src/tickets/materializer.js +2 -0
- package/dist/src/tickets/materializer.js.map +1 -0
- package/dist/src/tickets/runtime-authority.d.ts +32 -0
- package/dist/src/tickets/runtime-authority.js +46 -0
- package/dist/src/tickets/runtime-authority.js.map +1 -0
- package/dist/src/tickets/store.d.ts +221 -0
- package/dist/src/tickets/store.js +2025 -0
- package/dist/src/tickets/store.js.map +1 -0
- package/dist/src/tracer/one-ticket.d.ts +58 -0
- package/dist/src/tracer/one-ticket.js +578 -0
- package/dist/src/tracer/one-ticket.js.map +1 -0
- package/dist/src/trust/assessment.d.ts +24 -0
- package/dist/src/trust/assessment.js +124 -0
- package/dist/src/trust/assessment.js.map +1 -0
- package/dist/src/trust/runs.d.ts +13 -0
- package/dist/src/trust/runs.js +46 -0
- package/dist/src/trust/runs.js.map +1 -0
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/fs.d.ts +13 -0
- package/dist/src/utils/fs.js +188 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/src/verification/authority.d.ts +28 -0
- package/dist/src/verification/authority.js +153 -0
- package/dist/src/verification/authority.js.map +1 -0
- package/dist/src/verification/grade-source.d.ts +17 -0
- package/dist/src/verification/grade-source.js +30 -0
- package/dist/src/verification/grade-source.js.map +1 -0
- package/dist/src/verification/plan.d.ts +20 -0
- package/dist/src/verification/plan.js +128 -0
- package/dist/src/verification/plan.js.map +1 -0
- package/dist/src/verification/protected-runner.d.ts +50 -0
- package/dist/src/verification/protected-runner.js +171 -0
- package/dist/src/verification/protected-runner.js.map +1 -0
- package/dist/src/verification/result-gate-store.d.ts +72 -0
- package/dist/src/verification/result-gate-store.js +193 -0
- package/dist/src/verification/result-gate-store.js.map +1 -0
- package/dist/src/verification/result-gate.d.ts +41 -0
- package/dist/src/verification/result-gate.js +731 -0
- package/dist/src/verification/result-gate.js.map +1 -0
- package/dist/src/verification/runner.d.ts +38 -0
- package/dist/src/verification/runner.js +143 -0
- package/dist/src/verification/runner.js.map +1 -0
- package/dist/src/verification/sandbox.d.ts +16 -0
- package/dist/src/verification/sandbox.js +199 -0
- package/dist/src/verification/sandbox.js.map +1 -0
- package/dist/src/verify.d.ts +8 -0
- package/dist/src/verify.js +35 -0
- package/dist/src/verify.js.map +1 -0
- package/dist/src/watchdog/reconcile.d.ts +42 -0
- package/dist/src/watchdog/reconcile.js +440 -0
- package/dist/src/watchdog/reconcile.js.map +1 -0
- package/dist/src/workers/bridge.d.ts +35 -0
- package/dist/src/workers/bridge.js +426 -0
- package/dist/src/workers/bridge.js.map +1 -0
- package/dist/src/workers/delivery.d.ts +31 -0
- package/dist/src/workers/delivery.js +876 -0
- package/dist/src/workers/delivery.js.map +1 -0
- package/dist/src/workers/panes.d.ts +32 -0
- package/dist/src/workers/panes.js +212 -0
- package/dist/src/workers/panes.js.map +1 -0
- package/dist/src/workers/registry.d.ts +80 -0
- package/dist/src/workers/registry.js +351 -0
- package/dist/src/workers/registry.js.map +1 -0
- package/dist/src/workers/runtime.d.ts +44 -0
- package/dist/src/workers/runtime.js +21 -0
- package/dist/src/workers/runtime.js.map +1 -0
- package/dist/src/worktrees/manager.d.ts +35 -0
- package/dist/src/worktrees/manager.js +315 -0
- package/dist/src/worktrees/manager.js.map +1 -0
- package/docs/ARCHITECTURE.md +323 -0
- package/docs/PLAN.md +216 -0
- package/docs/REFERENCES.md +42 -0
- package/docs/ROADMAP.md +327 -0
- package/docs/SECURITY.md +242 -0
- package/docs/adr/0001-tracer-first-file-backed-certification.md +41 -0
- package/docs/adr/0002-conductor-first-typed-dispatch.md +102 -0
- package/docs/adr/0003-mandatory-pre-dispatch-advice.md +59 -0
- package/docs/compatibility/claude-code-2.1.207.md +25 -0
- package/loop/contract.md +102 -0
- package/package.json +54 -0
- package/schemas/advisor-evidence.schema.json +54 -0
- package/schemas/advisor-output.schema.json +31 -0
- package/schemas/conductor-activity-payload.schema.json +38 -0
- package/schemas/conductor-decision.schema.json +29 -0
- package/schemas/conductor-dispatch-intent.schema.json +71 -0
- package/schemas/conductor-dispatch-preparation.schema.json +46 -0
- package/schemas/conductor-dispatch-proposal.schema.json +82 -0
- package/schemas/conductor-goal-proposal.schema.json +30 -0
- package/schemas/conductor-manage-worker.schema.json +17 -0
- package/schemas/conductor-notification-payload.schema.json +39 -0
- package/schemas/conductor-process-result.schema.json +14 -0
- package/schemas/conductor-route.schema.json +51 -0
- package/schemas/conductor-start-team.schema.json +16 -0
- package/schemas/conductor-waiter-payload.schema.json +68 -0
- package/schemas/deterministic-check.schema.json +107 -0
- package/schemas/dispatch-acknowledgement.schema.json +15 -0
- package/schemas/dispatch-advice-payload.schema.json +64 -0
- package/schemas/dispatch-payload.schema.json +131 -0
- package/schemas/dispatch-result.schema.json +28 -0
- package/schemas/endurance-policy.schema.json +16 -0
- package/schemas/events/dispatch-delivery-requested.schema.json +17 -0
- package/schemas/events/github-observed.schema.json +28 -0
- package/schemas/events/native-hook-observed.schema.json +35 -0
- package/schemas/events/provider-observed.schema.json +64 -0
- package/schemas/events/run-heartbeat-due.schema.json +10 -0
- package/schemas/events/slack-notification-requested.schema.json +14 -0
- package/schemas/events/structured-role-completed.schema.json +54 -0
- package/schemas/events/ticket-resume-due.schema.json +28 -0
- package/schemas/events/watchdog-lease-expired.schema.json +14 -0
- package/schemas/events/watchdog-outbox-stalled.schema.json +12 -0
- package/schemas/events/watchdog-session-stalled.schema.json +11 -0
- package/schemas/evidence-record.schema.json +37 -0
- package/schemas/executor-result.schema.json +17 -0
- package/schemas/github-observation.schema.json +33 -0
- package/schemas/github-watermark.schema.json +66 -0
- package/schemas/goal-artifact.schema.json +19 -0
- package/schemas/goal-create-input.schema.json +14 -0
- package/schemas/goal-executor-result.schema.json +18 -0
- package/schemas/goal-payload.schema.json +95 -0
- package/schemas/grader-output.schema.json +33 -0
- package/schemas/lease-expiry.schema.json +19 -0
- package/schemas/live-disclosure.schema.json +16 -0
- package/schemas/live-outcome.schema.json +52 -0
- package/schemas/native-goal-snapshot.schema.json +27 -0
- package/schemas/provider-observed.schema.json +64 -0
- package/schemas/quota-observation.schema.json +72 -0
- package/schemas/reflection-evidence.schema.json +61 -0
- package/schemas/reflection.schema.json +13 -0
- package/schemas/regression-request.schema.json +54 -0
- package/schemas/release-assessment.schema.json +34 -0
- package/schemas/release-policy.schema.json +30 -0
- package/schemas/result-gate-payload.schema.json +244 -0
- package/schemas/review-delivery-payload.schema.json +42 -0
- package/schemas/role-policy.schema.json +111 -0
- package/schemas/run-abort-input.schema.json +11 -0
- package/schemas/run-completion-proof.schema.json +62 -0
- package/schemas/run-ledger-entry.schema.json +78 -0
- package/schemas/run-manifest.schema.json +74 -0
- package/schemas/run-start-intent.schema.json +74 -0
- package/schemas/runtime-record.schema.json +111 -0
- package/schemas/service-plan.schema.json +35 -0
- package/schemas/session-payload.schema.json +80 -0
- package/schemas/structured-role-read-request.schema.json +14 -0
- package/schemas/structured-role-request.schema.json +44 -0
- package/schemas/structured-role-result.schema.json +38 -0
- package/schemas/structured-role-submit-result.schema.json +15 -0
- package/schemas/team-checkpoint.schema.json +42 -0
- package/schemas/team-plan.schema.json +30 -0
- package/schemas/ticket-content.schema.json +38 -0
- package/schemas/ticket-queue-payload.schema.json +191 -0
- package/schemas/trust-report.schema.json +68 -0
- package/schemas/trust-run.schema.json +47 -0
- package/schemas/ultragoal-checkpoint.schema.json +49 -0
- package/schemas/user-schedule-installation-v1.schema.json +32 -0
- package/schemas/user-schedule-installation.schema.json +38 -0
- package/schemas/user-schedule-plan.schema.json +39 -0
- package/schemas/verification-plan-evidence.schema.json +37 -0
- package/schemas/worker-acknowledge-dispatch.schema.json +12 -0
- package/schemas/worker-payload.schema.json +47 -0
- package/schemas/worker-read-dispatch.schema.json +11 -0
- package/schemas/worker-record-dispatch-result.schema.json +14 -0
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { readImmutableFile } from "../evidence/immutable-file.js";
|
|
5
|
+
import { runProcess } from "../providers/process.js";
|
|
6
|
+
import { acquireDirectoryLock } from "../runtime/lock.js";
|
|
7
|
+
import { assertSchema, loadJsonSchema } from "../runtime/schema.js";
|
|
8
|
+
import { isRecord, lstatIfExists, readJsonFileNoFollow, validateExistingDirectory, writeFileAtomic, writeJsonAtomicNoReplace, } from "../utils/fs.js";
|
|
9
|
+
import { assertGeneratedUserSchedule, USER_SCHEDULE_BLOCK_BEGIN, USER_SCHEDULE_BLOCK_END, userScheduleArtifactBaseName, userScheduleLaunchdLabel, userSchedulePackageRoot, normalizeUserScheduleEnvironmentPath, } from "./user-plan.js";
|
|
10
|
+
const INSTALLATION_FILE = "oms-user-schedule.json";
|
|
11
|
+
const MAX_MANAGER_OUTPUT_BYTES = 1024 * 1024;
|
|
12
|
+
const MAX_CRONTAB_BYTES = 1024 * 1024;
|
|
13
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
14
|
+
export function readInstalledUserSchedulePlanBinding(options) {
|
|
15
|
+
const installationsRoot = validateExistingDirectory(options.installationsRoot, "schedule installations directory");
|
|
16
|
+
const packageRoot = validateExistingDirectory(options.packageRoot, "package root");
|
|
17
|
+
const installationPath = join(installationsRoot, INSTALLATION_FILE);
|
|
18
|
+
const entry = lstatIfExists(installationPath);
|
|
19
|
+
if (!entry)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
22
|
+
throw new Error("user schedule installation is not a regular file");
|
|
23
|
+
const installation = parseInstallation(readJsonFileNoFollow(installationPath, 256 * 1024), packageRoot);
|
|
24
|
+
if (installation.schema_version === 1) {
|
|
25
|
+
return Object.freeze({ schema_version: 1, manager: installation.manager });
|
|
26
|
+
}
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
schema_version: 2,
|
|
29
|
+
manager: installation.manager,
|
|
30
|
+
executable_path: installation.executable_path,
|
|
31
|
+
environment_path: installation.environment_path,
|
|
32
|
+
state_root: installation.state_root,
|
|
33
|
+
workspace_root: installation.workspace_root,
|
|
34
|
+
repository: installation.repository,
|
|
35
|
+
github_poll_seconds: installation.github_poll_seconds,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function installUserSchedule(options) {
|
|
39
|
+
const captured = captureOptions(options);
|
|
40
|
+
const context = prepareContext(captured);
|
|
41
|
+
const installedAt = canonicalTimestamp((captured.clock ?? (() => new Date().toISOString()))());
|
|
42
|
+
const lock = acquireDirectoryLock({
|
|
43
|
+
locksRoot: validateExistingDirectory(join(context.generated.plan.state_root, "locks"), "schedule locks directory"),
|
|
44
|
+
lockId: "user-schedule",
|
|
45
|
+
ownerId: "oms-scheduler",
|
|
46
|
+
note: context.generated.digest,
|
|
47
|
+
});
|
|
48
|
+
try {
|
|
49
|
+
const existing = readInstallation(context);
|
|
50
|
+
if (existing?.schema_version === 1) {
|
|
51
|
+
throw new Error("legacy OMS user schedule is recorded; remove it before installing the version 2 schedule");
|
|
52
|
+
}
|
|
53
|
+
if (existing && !recordMatchesContext(existing, context)) {
|
|
54
|
+
throw new Error("a different OMS user schedule is already recorded; remove it before replacement");
|
|
55
|
+
}
|
|
56
|
+
const record = existing ?? createInstallationRecord(context, installedAt);
|
|
57
|
+
if (context.generated.plan.manager !== "cron")
|
|
58
|
+
ensureNativeArtifactDirectory(context);
|
|
59
|
+
installArtifacts(context);
|
|
60
|
+
if (!existing) {
|
|
61
|
+
assertSchema(record, installationSchema(context.packageRoot, 2), "user schedule installation");
|
|
62
|
+
writeJsonAtomicNoReplace(context.installationPath, record);
|
|
63
|
+
}
|
|
64
|
+
activateSchedule(context);
|
|
65
|
+
return Object.freeze({ status: existing ? "replayed" : "installed", record });
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
lock.release();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function statusUserSchedule(options) {
|
|
72
|
+
const context = prepareContext(captureOptions(options));
|
|
73
|
+
const record = readInstallation(context);
|
|
74
|
+
if (record?.schema_version === 1) {
|
|
75
|
+
const bindingsExact = legacyRecordMatchesContext(record, context);
|
|
76
|
+
const artifactsExact = bindingsExact && legacyArtifactsAreExact(record, context);
|
|
77
|
+
return freezeStatus(context, "drifted", true, artifactsExact, bindingsExact && artifactsExact && managerIsActive(context), record.plan_sha256);
|
|
78
|
+
}
|
|
79
|
+
const artifactsExact = artifactsAreExact(context);
|
|
80
|
+
const bindingsExact = record !== null && recordMatchesContext(record, context);
|
|
81
|
+
if (!record && !hasAnyArtifacts(context)) {
|
|
82
|
+
return freezeStatus(context, "absent", false, true, false);
|
|
83
|
+
}
|
|
84
|
+
if (!bindingsExact || !artifactsExact) {
|
|
85
|
+
return freezeStatus(context, "drifted", record !== null, artifactsExact, false);
|
|
86
|
+
}
|
|
87
|
+
return freezeStatus(context, "installed", true, true, managerIsActive(context));
|
|
88
|
+
}
|
|
89
|
+
export function removeUserSchedule(options) {
|
|
90
|
+
const context = prepareContext(captureOptions(options));
|
|
91
|
+
const lock = acquireDirectoryLock({
|
|
92
|
+
locksRoot: validateExistingDirectory(join(context.generated.plan.state_root, "locks"), "schedule locks directory"),
|
|
93
|
+
lockId: "user-schedule",
|
|
94
|
+
ownerId: "oms-scheduler",
|
|
95
|
+
note: context.generated.digest,
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
const record = readInstallation(context);
|
|
99
|
+
if (!record) {
|
|
100
|
+
if (hasAnyArtifacts(context))
|
|
101
|
+
throw new Error("refusing to remove an unrecorded user schedule");
|
|
102
|
+
return Object.freeze({ removed: false });
|
|
103
|
+
}
|
|
104
|
+
if (record.schema_version === 1) {
|
|
105
|
+
removeLegacyUserSchedule(context, record);
|
|
106
|
+
unlinkInstallationRecord(context);
|
|
107
|
+
return Object.freeze({ removed: true });
|
|
108
|
+
}
|
|
109
|
+
if (!recordMatchesContext(record, context))
|
|
110
|
+
throw new Error("recorded user schedule does not match removal authority");
|
|
111
|
+
assertArtifactsSafeToRemove(context);
|
|
112
|
+
deactivateSchedule(context);
|
|
113
|
+
removeArtifacts(context);
|
|
114
|
+
unlinkInstallationRecord(context);
|
|
115
|
+
return Object.freeze({ removed: true });
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
lock.release();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function captureOptions(options) {
|
|
122
|
+
return {
|
|
123
|
+
generated: options.generated,
|
|
124
|
+
packageRoot: options.packageRoot,
|
|
125
|
+
homeDirectory: options.homeDirectory,
|
|
126
|
+
installationsRoot: options.installationsRoot,
|
|
127
|
+
managerExecutablePath: options.managerExecutablePath,
|
|
128
|
+
runner: options.runner,
|
|
129
|
+
env: options.env,
|
|
130
|
+
clock: options.clock,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function prepareContext(captured) {
|
|
134
|
+
assertGeneratedUserSchedule(captured.generated);
|
|
135
|
+
const packageRoot = validateExistingDirectory(captured.packageRoot, "package root");
|
|
136
|
+
if (packageRoot !== userSchedulePackageRoot(captured.generated)) {
|
|
137
|
+
throw new Error("package root does not match the generated user schedule authority");
|
|
138
|
+
}
|
|
139
|
+
const homeDirectory = validateExistingDirectory(captured.homeDirectory, "user home directory");
|
|
140
|
+
const installationsRoot = validateExistingDirectory(captured.installationsRoot, "schedule installations directory");
|
|
141
|
+
const expectedInstallationsRoot = validateExistingDirectory(join(captured.generated.plan.state_root, "schedule-installations"), "runtime schedule installations directory");
|
|
142
|
+
if (installationsRoot !== expectedInstallationsRoot) {
|
|
143
|
+
throw new Error("schedule installations directory does not match the generated state root");
|
|
144
|
+
}
|
|
145
|
+
const managerExecutablePath = safeAbsolutePath(captured.managerExecutablePath, "schedule manager executable");
|
|
146
|
+
const userId = captured.generated.plan.manager === "launchd" ? currentUserId() : null;
|
|
147
|
+
return Object.freeze({
|
|
148
|
+
generated: captured.generated,
|
|
149
|
+
packageRoot,
|
|
150
|
+
homeDirectory,
|
|
151
|
+
installationsRoot,
|
|
152
|
+
installationPath: join(installationsRoot, INSTALLATION_FILE),
|
|
153
|
+
managerExecutablePath,
|
|
154
|
+
runner: captured.runner ?? runProcess,
|
|
155
|
+
env: managerEnvironment(captured.env ?? process.env, homeDirectory),
|
|
156
|
+
userId,
|
|
157
|
+
artifacts: Object.freeze(captured.generated.artifacts.map((artifact) => Object.freeze({
|
|
158
|
+
relative_path: artifact.relative_path,
|
|
159
|
+
installed_path: installedArtifactPath(captured.generated.plan.manager, homeDirectory, artifact),
|
|
160
|
+
sha256: artifact.sha256,
|
|
161
|
+
}))),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function createInstallationRecord(context, installedAt) {
|
|
165
|
+
return deepFreeze({
|
|
166
|
+
schema_version: 2,
|
|
167
|
+
manager: context.generated.plan.manager,
|
|
168
|
+
plan_sha256: context.generated.digest,
|
|
169
|
+
executable_path: context.generated.plan.executable_path,
|
|
170
|
+
environment_path: context.generated.plan.environment_path,
|
|
171
|
+
state_root: context.generated.plan.state_root,
|
|
172
|
+
workspace_root: context.generated.plan.workspace_root,
|
|
173
|
+
repository: context.generated.plan.repository,
|
|
174
|
+
github_poll_seconds: context.generated.plan.github_poll_seconds,
|
|
175
|
+
manager_executable_path: context.managerExecutablePath,
|
|
176
|
+
home_directory: context.homeDirectory,
|
|
177
|
+
user_id: context.userId,
|
|
178
|
+
installed_at: installedAt,
|
|
179
|
+
artifacts: context.artifacts,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function readInstallation(context) {
|
|
183
|
+
if (!lstatIfExists(context.installationPath))
|
|
184
|
+
return null;
|
|
185
|
+
const value = readJsonFileNoFollow(context.installationPath, 256 * 1024);
|
|
186
|
+
return parseInstallation(value, context.packageRoot);
|
|
187
|
+
}
|
|
188
|
+
function parseInstallation(value, packageRoot) {
|
|
189
|
+
if (!isRecord(value) || !Array.isArray(value.artifacts))
|
|
190
|
+
throw new Error("invalid user schedule installation");
|
|
191
|
+
const schemaVersion = value.schema_version;
|
|
192
|
+
if (schemaVersion !== 1 && schemaVersion !== 2)
|
|
193
|
+
throw new Error("unsupported user schedule installation version");
|
|
194
|
+
assertSchema(value, installationSchema(packageRoot, schemaVersion), "user schedule installation");
|
|
195
|
+
const artifacts = value.artifacts.map((entry, index) => {
|
|
196
|
+
if (!isRecord(entry))
|
|
197
|
+
throw new Error(`invalid user schedule installation artifact ${index}`);
|
|
198
|
+
return Object.freeze({
|
|
199
|
+
relative_path: readString(entry.relative_path, `installation artifact ${index} name`),
|
|
200
|
+
installed_path: entry.installed_path === null
|
|
201
|
+
? null
|
|
202
|
+
: safeAbsolutePath(readString(entry.installed_path, `installation artifact ${index} path`), "installed artifact"),
|
|
203
|
+
sha256: readSha256(entry.sha256, `installation artifact ${index} sha256`),
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
const shared = {
|
|
207
|
+
manager: readManager(value.manager),
|
|
208
|
+
plan_sha256: readSha256(value.plan_sha256, "installation plan sha256"),
|
|
209
|
+
manager_executable_path: safeAbsolutePath(readString(value.manager_executable_path, "installation manager executable"), "installation manager executable"),
|
|
210
|
+
home_directory: safeAbsolutePath(readString(value.home_directory, "installation home"), "installation home"),
|
|
211
|
+
user_id: value.user_id === null ? null : readNonNegativeInteger(value.user_id, "installation user id"),
|
|
212
|
+
installed_at: canonicalTimestamp(value.installed_at),
|
|
213
|
+
artifacts: Object.freeze(artifacts),
|
|
214
|
+
};
|
|
215
|
+
if (schemaVersion === 1)
|
|
216
|
+
return deepFreeze({ schema_version: 1, ...shared });
|
|
217
|
+
return deepFreeze({
|
|
218
|
+
schema_version: 2,
|
|
219
|
+
...shared,
|
|
220
|
+
executable_path: safeAbsolutePath(readString(value.executable_path, "installation OMS executable"), "installation OMS executable"),
|
|
221
|
+
environment_path: normalizeUserScheduleEnvironmentPath(readString(value.environment_path, "installation environment PATH", 16 * 1024)),
|
|
222
|
+
state_root: safeAbsolutePath(readString(value.state_root, "installation state root"), "installation state root"),
|
|
223
|
+
workspace_root: safeAbsolutePath(readString(value.workspace_root, "installation workspace root"), "installation workspace root"),
|
|
224
|
+
repository: readString(value.repository, "installation repository", 140),
|
|
225
|
+
github_poll_seconds: readPollSeconds(value.github_poll_seconds),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function recordMatchesContext(record, context) {
|
|
229
|
+
return record.manager === context.generated.plan.manager
|
|
230
|
+
&& record.plan_sha256 === context.generated.digest
|
|
231
|
+
&& record.executable_path === context.generated.plan.executable_path
|
|
232
|
+
&& record.environment_path === context.generated.plan.environment_path
|
|
233
|
+
&& record.state_root === context.generated.plan.state_root
|
|
234
|
+
&& record.workspace_root === context.generated.plan.workspace_root
|
|
235
|
+
&& record.repository === context.generated.plan.repository
|
|
236
|
+
&& record.github_poll_seconds === context.generated.plan.github_poll_seconds
|
|
237
|
+
&& record.manager_executable_path === context.managerExecutablePath
|
|
238
|
+
&& record.home_directory === context.homeDirectory
|
|
239
|
+
&& record.user_id === context.userId
|
|
240
|
+
&& JSON.stringify(record.artifacts) === JSON.stringify(context.artifacts);
|
|
241
|
+
}
|
|
242
|
+
function legacyRecordMatchesContext(record, context) {
|
|
243
|
+
if (record.manager !== context.generated.plan.manager
|
|
244
|
+
|| record.manager_executable_path !== context.managerExecutablePath
|
|
245
|
+
|| record.home_directory !== context.homeDirectory
|
|
246
|
+
|| record.user_id !== context.userId
|
|
247
|
+
|| record.artifacts.length !== context.artifacts.length)
|
|
248
|
+
return false;
|
|
249
|
+
return record.artifacts.every((artifact, index) => {
|
|
250
|
+
const expected = context.artifacts[index];
|
|
251
|
+
return expected !== undefined
|
|
252
|
+
&& artifact.relative_path === expected.relative_path
|
|
253
|
+
&& artifact.installed_path === expected.installed_path;
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function legacyArtifactsAreExact(record, context) {
|
|
257
|
+
if (record.manager === "cron") {
|
|
258
|
+
const managed = extractManagedBlock(readCrontab(context));
|
|
259
|
+
const expectedSha256 = record.artifacts[0]?.sha256;
|
|
260
|
+
return managed !== null && expectedSha256 !== undefined && sha256(`${managed}\n`) === expectedSha256;
|
|
261
|
+
}
|
|
262
|
+
return record.artifacts.every((artifact) => {
|
|
263
|
+
if (!artifact.installed_path)
|
|
264
|
+
return false;
|
|
265
|
+
const entry = lstatIfExists(artifact.installed_path);
|
|
266
|
+
if (!entry || entry.isSymbolicLink() || !entry.isFile())
|
|
267
|
+
return false;
|
|
268
|
+
try {
|
|
269
|
+
return sha256(readImmutableFile(artifact.installed_path, 1024 * 1024)) === artifact.sha256;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function removeLegacyUserSchedule(context, record) {
|
|
277
|
+
if (!legacyRecordMatchesContext(record, context)) {
|
|
278
|
+
throw new Error("legacy OMS user schedule does not match removal authority");
|
|
279
|
+
}
|
|
280
|
+
if (!legacyArtifactsAreSafeToRemove(record, context)) {
|
|
281
|
+
throw new Error("refusing to remove a changed legacy OMS user schedule");
|
|
282
|
+
}
|
|
283
|
+
deactivateSchedule(context);
|
|
284
|
+
if (record.manager === "cron") {
|
|
285
|
+
const existing = readCrontab(context);
|
|
286
|
+
writeCrontab(context, replaceManagedBlock(existing, null));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
for (const artifact of record.artifacts) {
|
|
290
|
+
if (artifact.installed_path && lstatIfExists(artifact.installed_path))
|
|
291
|
+
unlinkSync(artifact.installed_path);
|
|
292
|
+
}
|
|
293
|
+
if (record.manager === "systemd")
|
|
294
|
+
runRequired(context, ["--user", "daemon-reload"]);
|
|
295
|
+
}
|
|
296
|
+
function legacyArtifactsAreSafeToRemove(record, context) {
|
|
297
|
+
if (record.manager === "cron") {
|
|
298
|
+
const managed = extractManagedBlock(readCrontab(context));
|
|
299
|
+
const expectedSha256 = record.artifacts[0]?.sha256;
|
|
300
|
+
return managed === null || (expectedSha256 !== undefined && sha256(`${managed}\n`) === expectedSha256);
|
|
301
|
+
}
|
|
302
|
+
return record.artifacts.every((artifact) => {
|
|
303
|
+
if (!artifact.installed_path)
|
|
304
|
+
return false;
|
|
305
|
+
const entry = lstatIfExists(artifact.installed_path);
|
|
306
|
+
if (!entry)
|
|
307
|
+
return true;
|
|
308
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
309
|
+
return false;
|
|
310
|
+
try {
|
|
311
|
+
return sha256(readImmutableFile(artifact.installed_path, 1024 * 1024)) === artifact.sha256;
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function unlinkInstallationRecord(context) {
|
|
319
|
+
const entry = lstatIfExists(context.installationPath);
|
|
320
|
+
if (!entry || entry.isSymbolicLink() || !entry.isFile()) {
|
|
321
|
+
throw new Error("schedule installation record changed before removal");
|
|
322
|
+
}
|
|
323
|
+
unlinkSync(context.installationPath);
|
|
324
|
+
}
|
|
325
|
+
function installArtifacts(context) {
|
|
326
|
+
if (context.generated.plan.manager === "cron") {
|
|
327
|
+
const existing = readCrontab(context);
|
|
328
|
+
const expected = context.generated.artifacts[0]?.bytes.trimEnd();
|
|
329
|
+
if (!expected)
|
|
330
|
+
throw new Error("generated cron schedule is missing");
|
|
331
|
+
const managed = extractManagedBlock(existing);
|
|
332
|
+
if (managed !== null && managed !== expected)
|
|
333
|
+
throw new Error("existing OMS crontab block differs from generated schedule");
|
|
334
|
+
if (managed === null)
|
|
335
|
+
writeCrontab(context, replaceManagedBlock(existing, expected));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
for (const [index, artifact] of context.generated.artifacts.entries()) {
|
|
339
|
+
const installedPath = context.artifacts[index]?.installed_path;
|
|
340
|
+
if (!installedPath)
|
|
341
|
+
throw new Error("native schedule artifact path is missing");
|
|
342
|
+
const bytes = Buffer.from(artifact.bytes, "utf8");
|
|
343
|
+
const entry = lstatIfExists(installedPath);
|
|
344
|
+
if (entry) {
|
|
345
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
346
|
+
throw new Error(`schedule artifact is not a regular file: ${installedPath}`);
|
|
347
|
+
if (!readImmutableFile(installedPath, 1024 * 1024).equals(bytes)) {
|
|
348
|
+
throw new Error(`existing schedule artifact differs: ${artifact.relative_path}`);
|
|
349
|
+
}
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
writeFileAtomic(installedPath, artifact.bytes, 0o600);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function activateSchedule(context) {
|
|
356
|
+
if (context.generated.plan.manager === "cron")
|
|
357
|
+
return;
|
|
358
|
+
if (context.generated.plan.manager === "systemd") {
|
|
359
|
+
runRequired(context, ["--user", "daemon-reload"]);
|
|
360
|
+
runRequired(context, ["--user", "enable", "--now", ...timerUnitNames(context.generated)]);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const domain = launchdDomain(context);
|
|
364
|
+
for (const item of context.generated.plan.jobs) {
|
|
365
|
+
const label = userScheduleLaunchdLabel(item.id);
|
|
366
|
+
runTolerant(context, ["bootout", `${domain}/${label}`]);
|
|
367
|
+
const artifact = context.artifacts.find((candidate) => candidate.relative_path === `${label}.plist`);
|
|
368
|
+
if (!artifact?.installed_path)
|
|
369
|
+
throw new Error(`launchd artifact is missing: ${label}`);
|
|
370
|
+
runRequired(context, ["bootstrap", domain, artifact.installed_path]);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function deactivateSchedule(context) {
|
|
374
|
+
if (context.generated.plan.manager === "cron")
|
|
375
|
+
return;
|
|
376
|
+
if (context.generated.plan.manager === "systemd") {
|
|
377
|
+
for (const timer of timerUnitNames(context.generated)) {
|
|
378
|
+
runDeactivation(context, ["--user", "disable", "--now", timer]);
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const domain = launchdDomain(context);
|
|
383
|
+
for (const item of context.generated.plan.jobs) {
|
|
384
|
+
runDeactivation(context, ["bootout", `${domain}/${userScheduleLaunchdLabel(item.id)}`]);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function managerIsActive(context) {
|
|
388
|
+
if (context.generated.plan.manager === "cron")
|
|
389
|
+
return extractManagedBlock(readCrontab(context)) !== null;
|
|
390
|
+
if (context.generated.plan.manager === "systemd") {
|
|
391
|
+
return runForStatus(context, ["--user", "is-active", ...timerUnitNames(context.generated)]);
|
|
392
|
+
}
|
|
393
|
+
const domain = launchdDomain(context);
|
|
394
|
+
return context.generated.plan.jobs.every((item) => (runForStatus(context, ["print", `${domain}/${userScheduleLaunchdLabel(item.id)}`])));
|
|
395
|
+
}
|
|
396
|
+
function artifactsAreExact(context) {
|
|
397
|
+
if (context.generated.plan.manager === "cron") {
|
|
398
|
+
const expected = context.generated.artifacts[0]?.bytes.trimEnd();
|
|
399
|
+
return expected !== undefined && extractManagedBlock(readCrontab(context)) === expected;
|
|
400
|
+
}
|
|
401
|
+
return context.generated.artifacts.every((artifact, index) => {
|
|
402
|
+
const path = context.artifacts[index]?.installed_path;
|
|
403
|
+
if (!path)
|
|
404
|
+
return false;
|
|
405
|
+
const entry = lstatIfExists(path);
|
|
406
|
+
if (!entry || entry.isSymbolicLink() || !entry.isFile())
|
|
407
|
+
return false;
|
|
408
|
+
try {
|
|
409
|
+
return readImmutableFile(path, 1024 * 1024).equals(Buffer.from(artifact.bytes, "utf8"));
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
function hasAnyArtifacts(context) {
|
|
417
|
+
if (context.generated.plan.manager === "cron")
|
|
418
|
+
return extractManagedBlock(readCrontab(context)) !== null;
|
|
419
|
+
return context.artifacts.some((artifact) => artifact.installed_path !== null && lstatIfExists(artifact.installed_path));
|
|
420
|
+
}
|
|
421
|
+
function assertArtifactsSafeToRemove(context) {
|
|
422
|
+
if (context.generated.plan.manager === "cron") {
|
|
423
|
+
const managed = extractManagedBlock(readCrontab(context));
|
|
424
|
+
const expected = context.generated.artifacts[0]?.bytes.trimEnd();
|
|
425
|
+
if (managed !== null && managed !== expected)
|
|
426
|
+
throw new Error("refusing to remove a changed OMS crontab block");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
for (const [index, artifact] of context.generated.artifacts.entries()) {
|
|
430
|
+
const path = context.artifacts[index]?.installed_path;
|
|
431
|
+
if (!path)
|
|
432
|
+
throw new Error("native schedule artifact path is missing");
|
|
433
|
+
const entry = lstatIfExists(path);
|
|
434
|
+
if (!entry)
|
|
435
|
+
continue;
|
|
436
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
437
|
+
throw new Error(`refusing to remove changed schedule artifact: ${path}`);
|
|
438
|
+
if (!readImmutableFile(path, 1024 * 1024).equals(Buffer.from(artifact.bytes, "utf8"))) {
|
|
439
|
+
throw new Error(`refusing to remove changed schedule artifact: ${artifact.relative_path}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function removeArtifacts(context) {
|
|
444
|
+
if (context.generated.plan.manager === "cron") {
|
|
445
|
+
const existing = readCrontab(context);
|
|
446
|
+
const managed = extractManagedBlock(existing);
|
|
447
|
+
const expected = context.generated.artifacts[0]?.bytes.trimEnd();
|
|
448
|
+
if (managed !== null && managed !== expected)
|
|
449
|
+
throw new Error("refusing to remove a changed OMS crontab block");
|
|
450
|
+
if (managed !== null)
|
|
451
|
+
writeCrontab(context, replaceManagedBlock(existing, null));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
for (const [index, artifact] of context.artifacts.entries()) {
|
|
455
|
+
if (!artifact.installed_path || !lstatIfExists(artifact.installed_path))
|
|
456
|
+
continue;
|
|
457
|
+
const expected = context.generated.artifacts[index];
|
|
458
|
+
if (!expected || !readImmutableFile(artifact.installed_path, 1024 * 1024).equals(Buffer.from(expected.bytes, "utf8"))) {
|
|
459
|
+
throw new Error(`refusing to remove changed schedule artifact: ${artifact.relative_path}`);
|
|
460
|
+
}
|
|
461
|
+
unlinkSync(artifact.installed_path);
|
|
462
|
+
}
|
|
463
|
+
if (context.generated.plan.manager === "systemd")
|
|
464
|
+
runRequired(context, ["--user", "daemon-reload"]);
|
|
465
|
+
}
|
|
466
|
+
function ensureNativeArtifactDirectory(context) {
|
|
467
|
+
const segments = context.generated.plan.manager === "systemd"
|
|
468
|
+
? [".config", "systemd", "user"]
|
|
469
|
+
: ["Library", "LaunchAgents"];
|
|
470
|
+
let current = context.homeDirectory;
|
|
471
|
+
for (const segment of segments) {
|
|
472
|
+
current = join(current, segment);
|
|
473
|
+
const entry = lstatIfExists(current);
|
|
474
|
+
if (!entry)
|
|
475
|
+
mkdirSync(current, { mode: 0o700 });
|
|
476
|
+
const created = lstatIfExists(current);
|
|
477
|
+
if (!created || created.isSymbolicLink() || !created.isDirectory()) {
|
|
478
|
+
throw new Error(`user schedule directory is unsafe: ${current}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const canonical = validateExistingDirectory(current, "user schedule artifact directory");
|
|
482
|
+
if (canonical !== resolve(current))
|
|
483
|
+
throw new Error("user schedule artifact directory must not use symbolic links");
|
|
484
|
+
return canonical;
|
|
485
|
+
}
|
|
486
|
+
function installedArtifactPath(manager, homeDirectory, artifact) {
|
|
487
|
+
if (manager === "cron")
|
|
488
|
+
return null;
|
|
489
|
+
const root = manager === "systemd"
|
|
490
|
+
? join(homeDirectory, ".config", "systemd", "user")
|
|
491
|
+
: join(homeDirectory, "Library", "LaunchAgents");
|
|
492
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9.-]{0,127}$/.test(artifact.relative_path)) {
|
|
493
|
+
throw new Error("unsafe user schedule artifact name");
|
|
494
|
+
}
|
|
495
|
+
return join(root, artifact.relative_path);
|
|
496
|
+
}
|
|
497
|
+
function readCrontab(context) {
|
|
498
|
+
const result = runManager(context, ["-l"]);
|
|
499
|
+
if (result.error)
|
|
500
|
+
throw new Error("unable to start user crontab command");
|
|
501
|
+
if (result.status === 1 && result.signal === null && /no crontab/iu.test(result.stderr))
|
|
502
|
+
return "";
|
|
503
|
+
if (result.status !== 0 || result.signal !== null)
|
|
504
|
+
throw new Error("unable to read user crontab");
|
|
505
|
+
if (Buffer.byteLength(result.stdout, "utf8") > MAX_CRONTAB_BYTES)
|
|
506
|
+
throw new Error("user crontab exceeds size limit");
|
|
507
|
+
return result.stdout;
|
|
508
|
+
}
|
|
509
|
+
function writeCrontab(context, content) {
|
|
510
|
+
if (Buffer.byteLength(content, "utf8") > MAX_CRONTAB_BYTES)
|
|
511
|
+
throw new Error("user crontab exceeds size limit");
|
|
512
|
+
runRequired(context, ["-"], content);
|
|
513
|
+
}
|
|
514
|
+
function extractManagedBlock(existing) {
|
|
515
|
+
const begin = markerMatches(existing, USER_SCHEDULE_BLOCK_BEGIN);
|
|
516
|
+
const end = markerMatches(existing, USER_SCHEDULE_BLOCK_END);
|
|
517
|
+
if (begin.length === 0 && end.length === 0)
|
|
518
|
+
return null;
|
|
519
|
+
if (begin.length !== 1 || end.length !== 1 || begin[0] === undefined || end[0] === undefined || begin[0] >= end[0]) {
|
|
520
|
+
throw new Error("user crontab has malformed OMS schedule markers");
|
|
521
|
+
}
|
|
522
|
+
const endOffset = end[0] + USER_SCHEDULE_BLOCK_END.length;
|
|
523
|
+
return existing.slice(begin[0], endOffset);
|
|
524
|
+
}
|
|
525
|
+
function replaceManagedBlock(existing, block) {
|
|
526
|
+
const begin = markerMatches(existing, USER_SCHEDULE_BLOCK_BEGIN);
|
|
527
|
+
const end = markerMatches(existing, USER_SCHEDULE_BLOCK_END);
|
|
528
|
+
if (begin.length === 0 && end.length === 0) {
|
|
529
|
+
if (block === null)
|
|
530
|
+
return existing;
|
|
531
|
+
const prefix = existing.length === 0 ? "" : existing.endsWith("\n") ? existing : `${existing}\n`;
|
|
532
|
+
return `${prefix}${block}\n`;
|
|
533
|
+
}
|
|
534
|
+
if (begin.length !== 1 || end.length !== 1 || begin[0] === undefined || end[0] === undefined || begin[0] >= end[0]) {
|
|
535
|
+
throw new Error("user crontab has malformed OMS schedule markers");
|
|
536
|
+
}
|
|
537
|
+
let afterOffset = end[0] + USER_SCHEDULE_BLOCK_END.length;
|
|
538
|
+
if (existing[afterOffset] === "\n")
|
|
539
|
+
afterOffset += 1;
|
|
540
|
+
return `${existing.slice(0, begin[0])}${block === null ? "" : `${block}\n`}${existing.slice(afterOffset)}`;
|
|
541
|
+
}
|
|
542
|
+
function markerMatches(existing, marker) {
|
|
543
|
+
const indexes = [];
|
|
544
|
+
let offset = 0;
|
|
545
|
+
for (const line of existing.split("\n")) {
|
|
546
|
+
if (line === marker)
|
|
547
|
+
indexes.push(offset);
|
|
548
|
+
offset += line.length + 1;
|
|
549
|
+
}
|
|
550
|
+
return indexes;
|
|
551
|
+
}
|
|
552
|
+
function runManager(context, args, input) {
|
|
553
|
+
const request = Object.freeze({
|
|
554
|
+
command: context.managerExecutablePath,
|
|
555
|
+
args: Object.freeze([...args]),
|
|
556
|
+
cwd: context.homeDirectory,
|
|
557
|
+
env: context.env,
|
|
558
|
+
input,
|
|
559
|
+
timeoutMs: 30_000,
|
|
560
|
+
maxBufferBytes: MAX_MANAGER_OUTPUT_BYTES,
|
|
561
|
+
});
|
|
562
|
+
const result = context.runner(request);
|
|
563
|
+
if (Buffer.byteLength(result.stdout, "utf8") > MAX_MANAGER_OUTPUT_BYTES
|
|
564
|
+
|| Buffer.byteLength(result.stderr, "utf8") > MAX_MANAGER_OUTPUT_BYTES)
|
|
565
|
+
throw new Error("schedule manager output exceeds size limit");
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
function runRequired(context, args, input) {
|
|
569
|
+
const result = runManager(context, args, input);
|
|
570
|
+
if (result.error || result.status !== 0 || result.signal !== null)
|
|
571
|
+
throw new Error("user schedule manager command failed");
|
|
572
|
+
}
|
|
573
|
+
function runTolerant(context, args) {
|
|
574
|
+
const result = runManager(context, args);
|
|
575
|
+
if (result.error)
|
|
576
|
+
throw new Error("unable to start user schedule manager command");
|
|
577
|
+
}
|
|
578
|
+
function runDeactivation(context, args) {
|
|
579
|
+
const result = runManager(context, args);
|
|
580
|
+
if (result.error)
|
|
581
|
+
throw new Error("unable to start user schedule manager command");
|
|
582
|
+
if (result.status === 0 && result.signal === null)
|
|
583
|
+
return;
|
|
584
|
+
if (result.signal === null && result.status !== null && explicitlyAlreadyAbsent(context, result))
|
|
585
|
+
return;
|
|
586
|
+
throw new Error("user schedule manager deactivation failed");
|
|
587
|
+
}
|
|
588
|
+
function explicitlyAlreadyAbsent(context, result) {
|
|
589
|
+
const lines = `${result.stdout}\n${result.stderr}`
|
|
590
|
+
.split(/\r?\n/u)
|
|
591
|
+
.map((line) => line.trim())
|
|
592
|
+
.filter(Boolean);
|
|
593
|
+
if (lines.length === 0)
|
|
594
|
+
return false;
|
|
595
|
+
if (context.generated.plan.manager === "systemd") {
|
|
596
|
+
return lines.every((line) => /^(?:Failed to disable unit:\s*)?Unit (?:file )?[^\s]+ (?:does not exist|not found|not loaded)\.?$/iu.test(line));
|
|
597
|
+
}
|
|
598
|
+
if (context.generated.plan.manager === "launchd") {
|
|
599
|
+
return lines.every((line) => /^(?:Boot-out failed:\s*[0-9]+:\s*)?(?:Could not find specified service|No such process|Service is not loaded)\.?$/iu.test(line));
|
|
600
|
+
}
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
function runForStatus(context, args) {
|
|
604
|
+
const result = runManager(context, args);
|
|
605
|
+
return !result.error && result.status === 0 && result.signal === null;
|
|
606
|
+
}
|
|
607
|
+
function timerUnitNames(generated) {
|
|
608
|
+
return generated.plan.jobs.map((item) => `${userScheduleArtifactBaseName(item.id)}.timer`);
|
|
609
|
+
}
|
|
610
|
+
function launchdDomain(context) {
|
|
611
|
+
if (context.userId === null)
|
|
612
|
+
throw new Error("launchd user id is unavailable");
|
|
613
|
+
return `gui/${context.userId}`;
|
|
614
|
+
}
|
|
615
|
+
function currentUserId() {
|
|
616
|
+
const value = process.getuid?.();
|
|
617
|
+
if (!Number.isSafeInteger(value) || value === undefined || value < 0)
|
|
618
|
+
throw new Error("launchd requires a current user id");
|
|
619
|
+
return value;
|
|
620
|
+
}
|
|
621
|
+
function managerEnvironment(input, homeDirectory) {
|
|
622
|
+
const env = { HOME: homeDirectory };
|
|
623
|
+
for (const key of ["PATH", "USER", "LOGNAME", "LANG", "LC_ALL", "XDG_RUNTIME_DIR", "DBUS_SESSION_BUS_ADDRESS"]) {
|
|
624
|
+
if (input[key] !== undefined)
|
|
625
|
+
env[key] = input[key];
|
|
626
|
+
}
|
|
627
|
+
return Object.freeze(env);
|
|
628
|
+
}
|
|
629
|
+
function installationSchema(packageRoot, version) {
|
|
630
|
+
const fileName = version === 1
|
|
631
|
+
? "user-schedule-installation-v1.schema.json"
|
|
632
|
+
: "user-schedule-installation.schema.json";
|
|
633
|
+
return loadJsonSchema(join(packageRoot, "schemas", fileName));
|
|
634
|
+
}
|
|
635
|
+
function freezeStatus(context, state, recorded, artifactsExact, active, planSha256 = context.generated.digest) {
|
|
636
|
+
return Object.freeze({
|
|
637
|
+
state,
|
|
638
|
+
manager: context.generated.plan.manager,
|
|
639
|
+
plan_sha256: planSha256,
|
|
640
|
+
recorded,
|
|
641
|
+
artifacts_exact: artifactsExact,
|
|
642
|
+
active,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
function safeAbsolutePath(value, label) {
|
|
646
|
+
if (typeof value !== "string" || !isAbsolute(value) || /[\0\r\n]/u.test(value)) {
|
|
647
|
+
throw new Error(`${label} must be an absolute path without control characters`);
|
|
648
|
+
}
|
|
649
|
+
if (/(?:token|secret|password|api[_-]?key)=/iu.test(value))
|
|
650
|
+
throw new Error(`${label} appears to contain a secret`);
|
|
651
|
+
return value;
|
|
652
|
+
}
|
|
653
|
+
function readString(value, label, maxLength = 4096) {
|
|
654
|
+
if (typeof value !== "string" || value.length < 1 || value.length > maxLength) {
|
|
655
|
+
throw new Error(`${label} must be a string`);
|
|
656
|
+
}
|
|
657
|
+
return value;
|
|
658
|
+
}
|
|
659
|
+
function readPollSeconds(value) {
|
|
660
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 60 || value > 3_600) {
|
|
661
|
+
throw new Error("installation GitHub poll interval must be an integer from 60 to 3600 seconds");
|
|
662
|
+
}
|
|
663
|
+
return value;
|
|
664
|
+
}
|
|
665
|
+
function readManager(value) {
|
|
666
|
+
if (value !== "cron" && value !== "systemd" && value !== "launchd")
|
|
667
|
+
throw new Error("invalid installation manager");
|
|
668
|
+
return value;
|
|
669
|
+
}
|
|
670
|
+
function readSha256(value, label) {
|
|
671
|
+
if (typeof value !== "string" || !SHA256_PATTERN.test(value))
|
|
672
|
+
throw new Error(`${label} must be sha256`);
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
function sha256(value) {
|
|
676
|
+
return createHash("sha256").update(value).digest("hex");
|
|
677
|
+
}
|
|
678
|
+
function readNonNegativeInteger(value, label) {
|
|
679
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
680
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
681
|
+
}
|
|
682
|
+
return value;
|
|
683
|
+
}
|
|
684
|
+
function canonicalTimestamp(value) {
|
|
685
|
+
if (typeof value !== "string")
|
|
686
|
+
throw new Error("schedule timestamp must be a string");
|
|
687
|
+
const epoch = Date.parse(value);
|
|
688
|
+
if (!Number.isFinite(epoch) || new Date(epoch).toISOString() !== value) {
|
|
689
|
+
throw new Error("schedule timestamp must be canonical UTC");
|
|
690
|
+
}
|
|
691
|
+
return value;
|
|
692
|
+
}
|
|
693
|
+
function deepFreeze(value, seen = new Set()) {
|
|
694
|
+
if (typeof value !== "object" || value === null || seen.has(value))
|
|
695
|
+
return value;
|
|
696
|
+
seen.add(value);
|
|
697
|
+
for (const child of Object.values(value))
|
|
698
|
+
deepFreeze(child, seen);
|
|
699
|
+
return Object.freeze(value);
|
|
700
|
+
}
|
|
701
|
+
//# sourceMappingURL=user-installation.js.map
|