@kal-elsam/kairo-runtime 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/README.md +937 -0
- package/bin/harness.js +11 -0
- package/bin/kairo-runtime.js +11 -0
- package/bin/kairo.js +11 -0
- package/global-template/components/catalog.json +25 -0
- package/global-template/components/orchestrator/orchestrator.md +55 -0
- package/global-template/components/sdd-core/handoff.md +38 -0
- package/global-template/components/sdd-core/spec-sizing.md +38 -0
- package/global-template/components/sdd-core/workflow.md +31 -0
- package/global-template/core/orchestrator.md +55 -0
- package/package.json +64 -0
- package/prompts/HARNESS_ENFORCEMENT_UPGRADE.md +11 -0
- package/prompts/HARNESS_ENTERPRISE.md +33 -0
- package/prompts/HARNESS_INSTALLER_MASTER.md +853 -0
- package/prompts/HARNESS_MINIMAL.md +30 -0
- package/prompts/HARNESS_SPEC_SIZING_UPGRADE.md +41 -0
- package/prompts/HARNESS_STANDARD.md +35 -0
- package/prompts/HARNESS_UNIVERSAL_PARITY.md +56 -0
- package/repo-template/.claude/agents/adapter-parity-reviewer.md +17 -0
- package/repo-template/.claude/agents/architect.md +17 -0
- package/repo-template/.claude/agents/eval-engineer.md +17 -0
- package/repo-template/.claude/agents/reviewer.md +17 -0
- package/repo-template/.claude/agents/sdd-reviewer.md +17 -0
- package/repo-template/.claude/agents/sdd-spec-writer.md +17 -0
- package/repo-template/.claude/skills/checkpoint/SKILL.md +13 -0
- package/repo-template/.claude/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.claude/skills/evals/SKILL.md +13 -0
- package/repo-template/.claude/skills/loop-engineering/SKILL.md +16 -0
- package/repo-template/.claude/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.claude/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.claude/skills/sdd/SKILL.md +13 -0
- package/repo-template/.claude/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.claude/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.claude/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.claude/skills/tdd/SKILL.md +13 -0
- package/repo-template/.claude/skills/trust-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/adapter-parity/SKILL.md +29 -0
- package/repo-template/.codex/skills/checkpoint/SKILL.md +36 -0
- package/repo-template/.codex/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.codex/skills/evals/SKILL.md +28 -0
- package/repo-template/.codex/skills/loop-engineering/SKILL.md +24 -0
- package/repo-template/.codex/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/sdd/SKILL.md +30 -0
- package/repo-template/.codex/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.codex/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.codex/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.codex/skills/tdd/SKILL.md +27 -0
- package/repo-template/.codex/skills/trust-review/SKILL.md +3 -0
- package/repo-template/.cursor/agents/adapter-parity-reviewer.md +29 -0
- package/repo-template/.cursor/agents/architect.md +27 -0
- package/repo-template/.cursor/agents/context-engineer.md +27 -0
- package/repo-template/.cursor/agents/debugger.md +27 -0
- package/repo-template/.cursor/agents/eval-engineer.md +27 -0
- package/repo-template/.cursor/agents/harness-orchestrator.md +27 -0
- package/repo-template/.cursor/agents/loop-controller.md +17 -0
- package/repo-template/.cursor/agents/loop-debugger.md +16 -0
- package/repo-template/.cursor/agents/reviewer.md +27 -0
- package/repo-template/.cursor/agents/sdd/sdd-acceptance-reviewer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-implementation-guardian.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-intake-analyst.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-orchestrator.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-spec-reviewer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-spec-writer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-task-breakdown.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-technical-planner.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-test-planner.md +31 -0
- package/repo-template/.cursor/agents/spec-intake-analyst.md +14 -0
- package/repo-template/.cursor/agents/test-engineer.md +27 -0
- package/repo-template/.cursor/commands/adapter-parity.md +34 -0
- package/repo-template/.cursor/commands/checkpoint.md +30 -0
- package/repo-template/.cursor/commands/debug.md +13 -0
- package/repo-template/.cursor/commands/init-eval.md +12 -0
- package/repo-template/.cursor/commands/init-feature.md +12 -0
- package/repo-template/.cursor/commands/init-harness.md +13 -0
- package/repo-template/.cursor/commands/loop-debug.md +3 -0
- package/repo-template/.cursor/commands/loop-review.md +3 -0
- package/repo-template/.cursor/commands/loop-run.md +5 -0
- package/repo-template/.cursor/commands/review.md +21 -0
- package/repo-template/.cursor/commands/sdd-intake.md +18 -0
- package/repo-template/.cursor/commands/sdd-plan.md +18 -0
- package/repo-template/.cursor/commands/sdd-spec.md +18 -0
- package/repo-template/.cursor/commands/sdd-tasks.md +18 -0
- package/repo-template/.cursor/commands/sdd-validate.md +18 -0
- package/repo-template/.cursor/commands/spec-escalate.md +9 -0
- package/repo-template/.cursor/commands/spec-intake.md +10 -0
- package/repo-template/.cursor/commands/spec-size.md +21 -0
- package/repo-template/.cursor/rules/adapter-parity.mdc +20 -0
- package/repo-template/.cursor/rules/api.mdc +12 -0
- package/repo-template/.cursor/rules/core.mdc +21 -0
- package/repo-template/.cursor/rules/enforcement.mdc +16 -0
- package/repo-template/.cursor/rules/evals.mdc +10 -0
- package/repo-template/.cursor/rules/git.mdc +19 -0
- package/repo-template/.cursor/rules/harness.mdc +14 -0
- package/repo-template/.cursor/rules/loops.mdc +17 -0
- package/repo-template/.cursor/rules/testing.mdc +13 -0
- package/repo-template/.cursor/rules/ui.mdc +13 -0
- package/repo-template/.cursor/skills/api-design/SKILL.md +23 -0
- package/repo-template/.cursor/skills/architecture/SKILL.md +23 -0
- package/repo-template/.cursor/skills/code-review/SKILL.md +23 -0
- package/repo-template/.cursor/skills/context-graph/SKILL.md +23 -0
- package/repo-template/.cursor/skills/debugging/SKILL.md +23 -0
- package/repo-template/.cursor/skills/evals/SKILL.md +23 -0
- package/repo-template/.cursor/skills/git-workflow/SKILL.md +23 -0
- package/repo-template/.cursor/skills/testing/SKILL.md +23 -0
- package/repo-template/.cursor/skills/ui-design/SKILL.md +23 -0
- package/repo-template/.gentle-ai/README.md +37 -0
- package/repo-template/.gentle-ai/loops/README.md +27 -0
- package/repo-template/.gentle-ai/loops/sdd-tdd-repair.loop.md +27 -0
- package/repo-template/.gentle-ai/model-routing.md +25 -0
- package/repo-template/.github/copilot-instructions.md +37 -0
- package/repo-template/.github/dependabot.yml +11 -0
- package/repo-template/.github/instructions/harness.instructions.md +11 -0
- package/repo-template/.github/workflows/harness-quality-gate.yml +67 -0
- package/repo-template/.github/workflows/harness-security-gate.yml +32 -0
- package/repo-template/.harness/spec-sizing.json +33 -0
- package/repo-template/.opencode/README.md +71 -0
- package/repo-template/.opencode/agents/adapter-parity-reviewer.md +23 -0
- package/repo-template/.opencode/agents/architect.md +38 -0
- package/repo-template/.opencode/agents/checkpoint-reviewer.md +38 -0
- package/repo-template/.opencode/agents/eval-engineer.md +38 -0
- package/repo-template/.opencode/agents/loop-controller.md +38 -0
- package/repo-template/.opencode/agents/loop-debugger.md +38 -0
- package/repo-template/.opencode/agents/model-router.md +38 -0
- package/repo-template/.opencode/agents/sdd-orchestrator.md +38 -0
- package/repo-template/.opencode/agents/sdd-spec-reviewer.md +38 -0
- package/repo-template/.opencode/agents/spec-intake-analyst.md +19 -0
- package/repo-template/.opencode/agents/tdd-engineer.md +38 -0
- package/repo-template/.opencode/commands/checkpoint.md +3 -0
- package/repo-template/.opencode/commands/eval-design.md +3 -0
- package/repo-template/.opencode/commands/maintainability-review.md +3 -0
- package/repo-template/.opencode/commands/quality-gate.md +3 -0
- package/repo-template/.opencode/commands/spec-escalate.md +9 -0
- package/repo-template/.opencode/commands/spec-intake.md +10 -0
- package/repo-template/.opencode/commands/spec-size.md +21 -0
- package/repo-template/.opencode/commands/trust-review.md +3 -0
- package/repo-template/.opencode/loops/ai-behavior-improvement.loop.md +15 -0
- package/repo-template/.opencode/loops/bug-repair.loop.md +16 -0
- package/repo-template/.opencode/loops/documentation-sync.loop.md +13 -0
- package/repo-template/.opencode/loops/feature-development.loop.md +25 -0
- package/repo-template/.opencode/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.opencode/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.opencode/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.pi/README.md +21 -0
- package/repo-template/.pi/prompts/checkpoint.md +11 -0
- package/repo-template/.pi/prompts/loop-run.md +10 -0
- package/repo-template/.pi/prompts/sdd-plan.md +11 -0
- package/repo-template/.pi/prompts/sdd-spec.md +13 -0
- package/repo-template/.pi/prompts/spec-escalate.md +9 -0
- package/repo-template/.pi/prompts/spec-intake.md +10 -0
- package/repo-template/.pi/prompts/spec-size.md +21 -0
- package/repo-template/.pi/skills/adapter-parity/SKILL.md +7 -0
- package/repo-template/.pi/skills/checkpoint/SKILL.md +7 -0
- package/repo-template/.pi/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.pi/skills/evals/SKILL.md +7 -0
- package/repo-template/.pi/skills/loop-engineering/SKILL.md +7 -0
- package/repo-template/.pi/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.pi/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.pi/skills/sdd/SKILL.md +7 -0
- package/repo-template/.pi/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.pi/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.pi/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.pi/skills/tdd/SKILL.md +7 -0
- package/repo-template/.pi/skills/trust-review/SKILL.md +3 -0
- package/repo-template/AGENT.md +8 -0
- package/repo-template/AGENTS.md +165 -0
- package/repo-template/CLAUDE.md +9 -0
- package/repo-template/GEMINI.md +9 -0
- package/repo-template/docs/ai/adapter-parity.md +135 -0
- package/repo-template/docs/ai/agent-workflow.md +45 -0
- package/repo-template/docs/ai/api.md +28 -0
- package/repo-template/docs/ai/architecture.md +53 -0
- package/repo-template/docs/ai/context-budget.md +54 -0
- package/repo-template/docs/ai/context-graph.md +39 -0
- package/repo-template/docs/ai/conventions.md +34 -0
- package/repo-template/docs/ai/decision-log.md +5 -0
- package/repo-template/docs/ai/enforcement.md +63 -0
- package/repo-template/docs/ai/eval-strategy.md +42 -0
- package/repo-template/docs/ai/evals.md +44 -0
- package/repo-template/docs/ai/git-workflow.md +35 -0
- package/repo-template/docs/ai/governance.md +61 -0
- package/repo-template/docs/ai/harness.md +49 -0
- package/repo-template/docs/ai/installer-cli.md +34 -0
- package/repo-template/docs/ai/loop-log.md +5 -0
- package/repo-template/docs/ai/loop-observability.md +57 -0
- package/repo-template/docs/ai/loop-policy.md +86 -0
- package/repo-template/docs/ai/loops.md +95 -0
- package/repo-template/docs/ai/maintainability-gates.md +26 -0
- package/repo-template/docs/ai/memory.md +71 -0
- package/repo-template/docs/ai/model-policy.md +105 -0
- package/repo-template/docs/ai/observability-runtime.md +48 -0
- package/repo-template/docs/ai/provider-routing.md +138 -0
- package/repo-template/docs/ai/quality-gates.md +48 -0
- package/repo-template/docs/ai/rollback-runtime.md +25 -0
- package/repo-template/docs/ai/security.md +12 -0
- package/repo-template/docs/ai/spec-driven-development.md +124 -0
- package/repo-template/docs/ai/spec-escalation.md +44 -0
- package/repo-template/docs/ai/spec-intake.md +46 -0
- package/repo-template/docs/ai/spec-sizing.md +207 -0
- package/repo-template/docs/ai/test-driven-development.md +35 -0
- package/repo-template/docs/ai/testing.md +28 -0
- package/repo-template/docs/ai/tool-adapters.md +186 -0
- package/repo-template/docs/ai/trust-allowlist.md +5 -0
- package/repo-template/docs/ai/trust-policy.md +41 -0
- package/repo-template/docs/ai/ui.md +17 -0
- package/repo-template/docs/skills/adapter-parity-review.md +46 -0
- package/repo-template/docs/skills/api-design.md +37 -0
- package/repo-template/docs/skills/architecture.md +37 -0
- package/repo-template/docs/skills/code-review.md +37 -0
- package/repo-template/docs/skills/context-graph.md +38 -0
- package/repo-template/docs/skills/debugging.md +37 -0
- package/repo-template/docs/skills/eval-design.md +12 -0
- package/repo-template/docs/skills/evals.md +37 -0
- package/repo-template/docs/skills/git-workflow.md +37 -0
- package/repo-template/docs/skills/look-trust-review.md +12 -0
- package/repo-template/docs/skills/loop-debugging.md +16 -0
- package/repo-template/docs/skills/loop-design.md +39 -0
- package/repo-template/docs/skills/loop-retrospective.md +15 -0
- package/repo-template/docs/skills/loop-review.md +16 -0
- package/repo-template/docs/skills/maintainability-review.md +12 -0
- package/repo-template/docs/skills/model-selection.md +36 -0
- package/repo-template/docs/skills/quality-gate-review.md +12 -0
- package/repo-template/docs/skills/spec-complexity-classifier.md +41 -0
- package/repo-template/docs/skills/spec-escalation-review.md +34 -0
- package/repo-template/docs/skills/spec-intake.md +25 -0
- package/repo-template/docs/skills/testing.md +37 -0
- package/repo-template/docs/skills/tool-adapter-sync.md +36 -0
- package/repo-template/docs/skills/ui-design.md +37 -0
- package/repo-template/docs/specs/README.md +9 -0
- package/repo-template/docs/specs/templates/basic-spec.md +41 -0
- package/repo-template/docs/specs/templates/complex-spec.md +145 -0
- package/repo-template/docs/specs/templates/standard-spec.md +58 -0
- package/repo-template/evals/README.md +11 -0
- package/repo-template/evals/golden/sample-golden.json +12 -0
- package/repo-template/evals/loop-regression/README.md +11 -0
- package/repo-template/evals/regression/sample-regression.json +12 -0
- package/repo-template/evals/schema/sample-schema.json +12 -0
- package/repo-template/evals/tool-calls/sample-tool-calls.json +12 -0
- package/repo-template/opencode.json.sample +29 -0
- package/repo-template/scripts/harness/backup.mjs +7 -0
- package/repo-template/scripts/harness/doctor.mjs +20 -0
- package/repo-template/scripts/harness/run-evals.mjs +27 -0
- package/repo-template/setup-agent-links.sh +54 -0
- package/scripts/check-published-release.mjs +127 -0
- package/scripts/check-release-commit.mjs +32 -0
- package/scripts/install.sh +225 -0
- package/scripts/installer-smoke-test.sh +155 -0
- package/scripts/lib/attribution-guard.mjs +59 -0
- package/scripts/registry-smoke-test.sh +149 -0
- package/scripts/smoke-test.sh +197 -0
- package/scripts/ux-smoke-test.sh +140 -0
- package/src/cli.js +625 -0
- package/src/doctor.js +68 -0
- package/src/global/adapter-context.js +27 -0
- package/src/global/adapter-matrix.js +38 -0
- package/src/global/adapters/claude.js +8 -0
- package/src/global/adapters/codex.js +8 -0
- package/src/global/adapters/cursor.js +8 -0
- package/src/global/adapters/opencode.js +8 -0
- package/src/global/agents.js +7 -0
- package/src/global/apply-confirmation.js +62 -0
- package/src/global/backups.js +54 -0
- package/src/global/brand/cli.js +52 -0
- package/src/global/brand/index.js +85 -0
- package/src/global/clack/setup-preview.js +48 -0
- package/src/global/clack/setup-wizard-constants.js +6 -0
- package/src/global/clack/setup-wizard.js +207 -0
- package/src/global/clack/theme.js +145 -0
- package/src/global/component-authoring.js +121 -0
- package/src/global/component-builders.js +43 -0
- package/src/global/component-distribution.js +270 -0
- package/src/global/component-installer.js +106 -0
- package/src/global/component-paths.js +22 -0
- package/src/global/component-registry.js +89 -0
- package/src/global/components/orchestrator.js +14 -0
- package/src/global/components/sdd-core.js +17 -0
- package/src/global/diff.js +233 -0
- package/src/global/drift.js +264 -0
- package/src/global/explain.js +144 -0
- package/src/global/global-cli.js +1145 -0
- package/src/global/global-doctor.js +67 -0
- package/src/global/global-installer.js +284 -0
- package/src/global/history.js +313 -0
- package/src/global/ink/run-setup-ink.js +77 -0
- package/src/global/ink/setup-app.js +278 -0
- package/src/global/ink/setup-routing.js +51 -0
- package/src/global/ink/setup-state.js +180 -0
- package/src/global/ink/terminal.js +13 -0
- package/src/global/json-output.js +22 -0
- package/src/global/load-component-catalog.js +41 -0
- package/src/global/load-workspace-component-catalog.js +123 -0
- package/src/global/managed-body.js +28 -0
- package/src/global/managed-config-adapter.js +134 -0
- package/src/global/managed-section.js +73 -0
- package/src/global/npm-registry.js +25 -0
- package/src/global/paths.js +22 -0
- package/src/global/policy.js +344 -0
- package/src/global/preflight.js +65 -0
- package/src/global/registry.js +79 -0
- package/src/global/report.js +110 -0
- package/src/global/rollback.js +126 -0
- package/src/global/setup.js +284 -0
- package/src/global/state-migration.js +65 -0
- package/src/global/state.js +50 -0
- package/src/global/status.js +94 -0
- package/src/global/sync.js +129 -0
- package/src/global/upgrade.js +145 -0
- package/src/harness-files.js +145 -0
- package/src/harness-updater.js +120 -0
- package/src/hash.js +5 -0
- package/src/manifest.js +36 -0
- package/src/project-detection.js +105 -0
- package/src/template-installer.js +72 -0
- package/src/text-template.js +24 -0
- package/src/workspace-cli.js +115 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { listBackupSnapshots } from "./backups.js";
|
|
2
|
+
import { buildAdapterContext } from "./adapter-context.js";
|
|
3
|
+
import { detectGlobalDrift, hasRepairableDrift } from "./drift.js";
|
|
4
|
+
import { harnessHomePaths } from "./paths.js";
|
|
5
|
+
import { resolveComponent } from "./component-registry.js";
|
|
6
|
+
import { readGlobalState } from "./state.js";
|
|
7
|
+
import { formatCliCommand } from "./brand/cli.js";
|
|
8
|
+
|
|
9
|
+
export async function runGlobalDoctorChecks(homeDir, { packageRoot, workspaceRoot = null } = {}) {
|
|
10
|
+
const paths = harnessHomePaths(homeDir);
|
|
11
|
+
const state = await readGlobalState(paths.statePath);
|
|
12
|
+
const installedComponents = (state?.components ?? []).map((entry) => resolveComponent(entry.id, { workspaceRoot }));
|
|
13
|
+
const context = buildAdapterContext({
|
|
14
|
+
homeDir,
|
|
15
|
+
packageName: state?.packageName ?? "",
|
|
16
|
+
packageRoot,
|
|
17
|
+
workspaceRoot,
|
|
18
|
+
components: installedComponents
|
|
19
|
+
});
|
|
20
|
+
const checks = packageRoot
|
|
21
|
+
? await detectGlobalDrift({ homeDir, paths, state, packageRoot, workspaceRoot, context })
|
|
22
|
+
: [stateOnlyCheck(state)];
|
|
23
|
+
|
|
24
|
+
if (packageRoot) {
|
|
25
|
+
checks.push(await backupsCheck(paths));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const hasMissing = checks.some((check) => check.status === "missing");
|
|
29
|
+
const hasStale = checks.some((check) => check.status === "stale");
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
checks,
|
|
33
|
+
ok: !hasMissing && !hasStale,
|
|
34
|
+
hasDrift: hasRepairableDrift(checks),
|
|
35
|
+
state,
|
|
36
|
+
paths
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stateOnlyCheck(state) {
|
|
41
|
+
if (!state) {
|
|
42
|
+
return {
|
|
43
|
+
name: "~/.harness/state.json",
|
|
44
|
+
status: "missing",
|
|
45
|
+
category: "state",
|
|
46
|
+
detail: `Not found. Run "${formatCliCommand("install")}" to configure the local ecosystem.`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
name: "~/.harness/state.json",
|
|
52
|
+
status: "ok",
|
|
53
|
+
category: "state",
|
|
54
|
+
detail: `cliVersion=${state.cliVersion ?? "unknown"}`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function backupsCheck(paths) {
|
|
59
|
+
const snapshots = await listBackupSnapshots(paths.backupsDir);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
name: "~/.harness/backups",
|
|
63
|
+
status: "ok",
|
|
64
|
+
category: "backups",
|
|
65
|
+
detail: snapshots.length > 0 ? `${snapshots.length} snapshot(s)` : "No snapshots yet."
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { buildAdapterContext } from "./adapter-context.js";
|
|
4
|
+
import { backupTimestamp } from "./backups.js";
|
|
5
|
+
import { buildComponentStateEntries, resolveTargetComponents } from "./component-registry.js";
|
|
6
|
+
import { installComponentAssets, repairComponentAssets } from "./component-installer.js";
|
|
7
|
+
import { detectGlobalDrift, hasRepairableDrift } from "./drift.js";
|
|
8
|
+
import { harnessHomePaths } from "./paths.js";
|
|
9
|
+
import {
|
|
10
|
+
buildAdapterStateEntry,
|
|
11
|
+
detectInstalledAdapters,
|
|
12
|
+
resolveAdapter,
|
|
13
|
+
resolveTargetAdapters
|
|
14
|
+
} from "./registry.js";
|
|
15
|
+
import { createGlobalState, readGlobalState, writeGlobalState } from "./state.js";
|
|
16
|
+
import {
|
|
17
|
+
getInstalledAdapterIds,
|
|
18
|
+
getInstalledComponentIds,
|
|
19
|
+
normalizeGlobalState
|
|
20
|
+
} from "./state-migration.js";
|
|
21
|
+
import { formatCliCommand } from "./brand/cli.js";
|
|
22
|
+
|
|
23
|
+
export async function installGlobalHarness({
|
|
24
|
+
packageRoot,
|
|
25
|
+
packageName,
|
|
26
|
+
cliVersion,
|
|
27
|
+
homeDir,
|
|
28
|
+
workspaceRoot = null,
|
|
29
|
+
agents = null,
|
|
30
|
+
components = null,
|
|
31
|
+
noDefaultComponents = false,
|
|
32
|
+
dryRun = false
|
|
33
|
+
}) {
|
|
34
|
+
const paths = harnessHomePaths(homeDir);
|
|
35
|
+
const timestamp = backupTimestamp();
|
|
36
|
+
const targetComponents = resolveTargetComponents({ components, noDefaultComponents, workspaceRoot });
|
|
37
|
+
const context = buildAdapterContext({
|
|
38
|
+
homeDir,
|
|
39
|
+
packageName,
|
|
40
|
+
packageRoot,
|
|
41
|
+
workspaceRoot,
|
|
42
|
+
components: targetComponents,
|
|
43
|
+
dryRun,
|
|
44
|
+
timestamp
|
|
45
|
+
});
|
|
46
|
+
const targetAdapters = resolveTargetAdapters(context, agents);
|
|
47
|
+
const result = {
|
|
48
|
+
scope: "agent-global",
|
|
49
|
+
homeDir,
|
|
50
|
+
stateRoot: paths.root,
|
|
51
|
+
agents: targetAdapters.map((adapter) => adapter.id),
|
|
52
|
+
components: targetComponents.map((component) => component.id),
|
|
53
|
+
coreFiles: [],
|
|
54
|
+
assetsRepaired: [],
|
|
55
|
+
assetsUnchanged: [],
|
|
56
|
+
configsCreated: [],
|
|
57
|
+
configsUpdated: [],
|
|
58
|
+
configsRepaired: [],
|
|
59
|
+
configsUnchanged: [],
|
|
60
|
+
backups: [],
|
|
61
|
+
driftDetected: false
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const componentFiles = await installComponentAssets({
|
|
65
|
+
packageRoot,
|
|
66
|
+
workspaceRoot,
|
|
67
|
+
paths,
|
|
68
|
+
components: targetComponents,
|
|
69
|
+
dryRun
|
|
70
|
+
});
|
|
71
|
+
result.coreFiles.push(...Object.keys(componentFiles));
|
|
72
|
+
result.assetsRepaired.push(...Object.keys(componentFiles));
|
|
73
|
+
|
|
74
|
+
await applyAdapterPlans({ context, targetAdapters, result });
|
|
75
|
+
|
|
76
|
+
if (!dryRun) {
|
|
77
|
+
await persistGlobalState({
|
|
78
|
+
paths,
|
|
79
|
+
packageName,
|
|
80
|
+
cliVersion,
|
|
81
|
+
targetAdapters,
|
|
82
|
+
targetComponents,
|
|
83
|
+
homeDir,
|
|
84
|
+
componentFiles,
|
|
85
|
+
backups: result.backups
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function updateGlobalHarness(options) {
|
|
93
|
+
const paths = harnessHomePaths(options.homeDir);
|
|
94
|
+
const state = await readGlobalState(paths.statePath);
|
|
95
|
+
|
|
96
|
+
if (!state) {
|
|
97
|
+
throw new Error(`No global state found at ~/.harness/state.json. Run "${formatCliCommand("install")}" first.`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return syncGlobalHarness({
|
|
101
|
+
...options,
|
|
102
|
+
agents: options.agents ?? getInstalledAdapterIds(state),
|
|
103
|
+
components: options.components ?? getInstalledComponentIds(state)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function syncGlobalHarness({
|
|
108
|
+
packageRoot,
|
|
109
|
+
packageName,
|
|
110
|
+
cliVersion,
|
|
111
|
+
homeDir,
|
|
112
|
+
workspaceRoot = null,
|
|
113
|
+
agents,
|
|
114
|
+
components,
|
|
115
|
+
dryRun = false
|
|
116
|
+
}) {
|
|
117
|
+
const paths = harnessHomePaths(homeDir);
|
|
118
|
+
const state = await readGlobalState(paths.statePath);
|
|
119
|
+
const timestamp = backupTimestamp();
|
|
120
|
+
const targetComponents = resolveTargetComponents({ components, workspaceRoot });
|
|
121
|
+
const context = buildAdapterContext({
|
|
122
|
+
homeDir,
|
|
123
|
+
packageName: packageName ?? state.packageName,
|
|
124
|
+
packageRoot,
|
|
125
|
+
workspaceRoot,
|
|
126
|
+
components: targetComponents,
|
|
127
|
+
dryRun,
|
|
128
|
+
timestamp
|
|
129
|
+
});
|
|
130
|
+
const targetAdapters = resolveTargetAdapters(context, agents);
|
|
131
|
+
const driftChecks = await detectGlobalDrift({ homeDir, paths, state, packageRoot, workspaceRoot, context });
|
|
132
|
+
const result = {
|
|
133
|
+
scope: "agent-global",
|
|
134
|
+
homeDir,
|
|
135
|
+
stateRoot: paths.root,
|
|
136
|
+
agents: targetAdapters.map((adapter) => adapter.id),
|
|
137
|
+
components: targetComponents.map((component) => component.id),
|
|
138
|
+
coreFiles: [],
|
|
139
|
+
assetsRepaired: [],
|
|
140
|
+
assetsUnchanged: [],
|
|
141
|
+
configsCreated: [],
|
|
142
|
+
configsUpdated: [],
|
|
143
|
+
configsRepaired: [],
|
|
144
|
+
configsUnchanged: [],
|
|
145
|
+
backups: [],
|
|
146
|
+
driftDetected: hasRepairableDrift(driftChecks),
|
|
147
|
+
repairs: driftChecks.filter((check) => check.status === "missing" || check.status === "stale")
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const { coreFiles, repaired, unchanged } = await repairComponentAssets({
|
|
151
|
+
packageRoot,
|
|
152
|
+
workspaceRoot,
|
|
153
|
+
paths,
|
|
154
|
+
components: targetComponents,
|
|
155
|
+
state,
|
|
156
|
+
dryRun
|
|
157
|
+
});
|
|
158
|
+
result.coreFiles = Object.keys(coreFiles);
|
|
159
|
+
result.assetsRepaired = repaired;
|
|
160
|
+
result.assetsUnchanged = unchanged;
|
|
161
|
+
|
|
162
|
+
const driftedConfigs = new Set(
|
|
163
|
+
driftChecks
|
|
164
|
+
.filter((check) =>
|
|
165
|
+
(check.category === "managed_section" || check.category === "component_section")
|
|
166
|
+
&& (check.status === "missing" || check.status === "stale")
|
|
167
|
+
&& check.configFile)
|
|
168
|
+
.map((check) => check.configFile)
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
for (const adapter of targetAdapters) {
|
|
172
|
+
const needsRepair = driftedConfigs.has(adapter.assets.configFile);
|
|
173
|
+
if (!needsRepair) {
|
|
174
|
+
result.configsUnchanged.push(adapter.assets.configFile);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const plan = adapter.plan(context);
|
|
179
|
+
const applied = await adapter.apply(context, plan);
|
|
180
|
+
|
|
181
|
+
if (applied.backupPath) result.backups.push(applied.backupPath);
|
|
182
|
+
if (applied.action === "create") result.configsCreated.push(applied.configFile);
|
|
183
|
+
else if (applied.action === "update") result.configsUpdated.push(applied.configFile);
|
|
184
|
+
if (applied.action !== "unchanged") result.configsRepaired.push(applied.configFile);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!dryRun) {
|
|
188
|
+
await persistGlobalState({
|
|
189
|
+
paths,
|
|
190
|
+
packageName: packageName ?? state.packageName,
|
|
191
|
+
cliVersion,
|
|
192
|
+
targetAdapters,
|
|
193
|
+
targetComponents,
|
|
194
|
+
homeDir,
|
|
195
|
+
componentFiles: coreFiles,
|
|
196
|
+
backups: [...(state.backups ?? []), ...result.backups],
|
|
197
|
+
installedAt: state.installedAt
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function uninstallGlobalHarness({ homeDir, dryRun = false }) {
|
|
205
|
+
const paths = harnessHomePaths(homeDir);
|
|
206
|
+
const rawState = await readGlobalState(paths.statePath);
|
|
207
|
+
const state = normalizeGlobalState(rawState);
|
|
208
|
+
const timestamp = backupTimestamp();
|
|
209
|
+
const installedComponents = state?.components?.map((entry) => entry.id) ?? [];
|
|
210
|
+
const context = buildAdapterContext({
|
|
211
|
+
homeDir,
|
|
212
|
+
packageName: state?.packageName ?? "",
|
|
213
|
+
components: installedComponents.map((id) => ({ id })),
|
|
214
|
+
dryRun,
|
|
215
|
+
timestamp
|
|
216
|
+
});
|
|
217
|
+
const adapterIds = state?.adapters?.map((entry) => entry.id)
|
|
218
|
+
?? detectInstalledAdapters(context);
|
|
219
|
+
const result = {
|
|
220
|
+
scope: "agent-global",
|
|
221
|
+
agents: adapterIds,
|
|
222
|
+
components: installedComponents,
|
|
223
|
+
configsCleaned: [],
|
|
224
|
+
backups: [],
|
|
225
|
+
stateRemoved: false
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
for (const adapterId of adapterIds) {
|
|
229
|
+
const adapter = resolveAdapter(adapterId);
|
|
230
|
+
const stateEntry = state?.adapters?.find((entry) => entry.id === adapterId);
|
|
231
|
+
const removed = await adapter.uninstall(context, stateEntry);
|
|
232
|
+
|
|
233
|
+
if (removed.backupPath) result.backups.push(removed.backupPath);
|
|
234
|
+
if (removed.cleaned) result.configsCleaned.push(removed.configFile);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (state && !dryRun) {
|
|
238
|
+
await rm(paths.statePath, { force: true });
|
|
239
|
+
await rm(join(paths.root, "components"), { recursive: true, force: true });
|
|
240
|
+
await rm(paths.coreDir, { recursive: true, force: true });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
result.stateRemoved = Boolean(state);
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function applyAdapterPlans({ context, targetAdapters, result }) {
|
|
248
|
+
for (const adapter of targetAdapters) {
|
|
249
|
+
const plan = adapter.plan(context);
|
|
250
|
+
const applied = await adapter.apply(context, plan);
|
|
251
|
+
|
|
252
|
+
if (applied.backupPath) result.backups.push(applied.backupPath);
|
|
253
|
+
|
|
254
|
+
if (applied.action === "create") result.configsCreated.push(applied.configFile);
|
|
255
|
+
else if (applied.action === "update") result.configsUpdated.push(applied.configFile);
|
|
256
|
+
else if (applied.action === "unchanged") result.configsUnchanged.push(applied.configFile);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function persistGlobalState({
|
|
261
|
+
paths,
|
|
262
|
+
packageName,
|
|
263
|
+
cliVersion,
|
|
264
|
+
targetAdapters,
|
|
265
|
+
targetComponents,
|
|
266
|
+
homeDir,
|
|
267
|
+
componentFiles,
|
|
268
|
+
backups,
|
|
269
|
+
installedAt
|
|
270
|
+
}) {
|
|
271
|
+
const existingState = installedAt ? { installedAt } : await readGlobalState(paths.statePath);
|
|
272
|
+
const adapterEntries = targetAdapters.map((adapter) => buildAdapterStateEntry(adapter, homeDir));
|
|
273
|
+
const componentEntries = buildComponentStateEntries(targetComponents, targetAdapters);
|
|
274
|
+
|
|
275
|
+
await writeGlobalState(paths.statePath, createGlobalState({
|
|
276
|
+
packageName,
|
|
277
|
+
cliVersion,
|
|
278
|
+
adapters: adapterEntries,
|
|
279
|
+
components: componentEntries,
|
|
280
|
+
coreFiles: componentFiles,
|
|
281
|
+
backups,
|
|
282
|
+
installedAt: existingState?.installedAt
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { harnessHomePaths } from "./paths.js";
|
|
4
|
+
import { buildEffectivePolicy, loadConsentAudit } from "./policy.js";
|
|
5
|
+
import { buildStatusReport } from "./status.js";
|
|
6
|
+
|
|
7
|
+
export function getHistoryPath(homeDir) {
|
|
8
|
+
return harnessHomePaths(homeDir).historyPath;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildCheckSnapshot(counts) {
|
|
12
|
+
if (!counts) return null;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
ok: counts.ok ?? 0,
|
|
16
|
+
missing: counts.missing ?? 0,
|
|
17
|
+
stale: counts.stale ?? 0,
|
|
18
|
+
warning: counts.warning ?? 0
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createHistoryEvent({ cliVersion, ...fields }) {
|
|
23
|
+
return {
|
|
24
|
+
timestamp: new Date().toISOString(),
|
|
25
|
+
cliVersion,
|
|
26
|
+
...fields
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function appendHistoryEvent(homeDir, event) {
|
|
31
|
+
const { root, historyPath } = harnessHomePaths(homeDir);
|
|
32
|
+
await mkdir(root, { recursive: true });
|
|
33
|
+
await appendFile(historyPath, `${JSON.stringify(event)}\n`, "utf8");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function readHistoryEvents(homeDir, { limit = null, command = null, action = null } = {}) {
|
|
37
|
+
const { historyPath } = harnessHomePaths(homeDir);
|
|
38
|
+
|
|
39
|
+
if (!existsSync(historyPath)) {
|
|
40
|
+
return { events: [], warnings: [] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const content = await readFile(historyPath, "utf8");
|
|
44
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
45
|
+
const events = [];
|
|
46
|
+
const warnings = [];
|
|
47
|
+
|
|
48
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
49
|
+
try {
|
|
50
|
+
events.push(JSON.parse(lines[index]));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
warnings.push({ line: index + 1, message: error.message });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let filtered = events;
|
|
57
|
+
|
|
58
|
+
if (command) {
|
|
59
|
+
filtered = filtered.filter((event) => event.command === command);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (action) {
|
|
63
|
+
filtered = filtered.filter((event) => event.action === action);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (limit != null && limit > 0) {
|
|
67
|
+
return { events: filtered.slice(-limit), warnings };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { events: filtered, warnings };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function readLastHistoryEvent(homeDir, { command = null, action = null } = {}) {
|
|
74
|
+
const { events, warnings } = await readHistoryEvents(homeDir, { command, action });
|
|
75
|
+
return {
|
|
76
|
+
event: events.length > 0 ? events[events.length - 1] : null,
|
|
77
|
+
warnings
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function resolveHistoryPolicy(homeDir) {
|
|
82
|
+
const policy = await buildEffectivePolicy(homeDir);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
profile: policy.profile,
|
|
86
|
+
applyMode: policy.applyMode,
|
|
87
|
+
preflight: policy.preflight,
|
|
88
|
+
source: policy.source
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function resolveConsentSource(homeDir, consentOptions) {
|
|
93
|
+
const consent = await loadConsentAudit(homeDir, consentOptions);
|
|
94
|
+
return consent.consentSource;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function buildConsentOptions(options, { applying, dryRun }) {
|
|
98
|
+
return {
|
|
99
|
+
yes: options.yes,
|
|
100
|
+
confirm: options.confirm,
|
|
101
|
+
yesExplicit: options.yesExplicit,
|
|
102
|
+
confirmExplicit: options.confirmExplicit,
|
|
103
|
+
preflight: options.preflight,
|
|
104
|
+
preflightExplicit: options.preflightExplicit,
|
|
105
|
+
interactive: options.interactive,
|
|
106
|
+
applying,
|
|
107
|
+
dryRun,
|
|
108
|
+
json: options.json
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function recordEvent(homeDir, cliVersion, fields) {
|
|
113
|
+
const policy = fields.policy ?? await resolveHistoryPolicy(homeDir);
|
|
114
|
+
|
|
115
|
+
await appendHistoryEvent(homeDir, createHistoryEvent({
|
|
116
|
+
cliVersion,
|
|
117
|
+
policy,
|
|
118
|
+
consentSource: fields.consentSource ?? "none",
|
|
119
|
+
agents: fields.agents ?? null,
|
|
120
|
+
components: fields.components ?? null,
|
|
121
|
+
checksBefore: fields.checksBefore ?? null,
|
|
122
|
+
checksAfter: fields.checksAfter ?? null,
|
|
123
|
+
backupsCreated: fields.backupsCreated ?? null,
|
|
124
|
+
snapshotsUsed: fields.snapshotsUsed ?? null,
|
|
125
|
+
command: fields.command,
|
|
126
|
+
action: fields.action,
|
|
127
|
+
wrote: fields.wrote,
|
|
128
|
+
dryRun: fields.dryRun ?? false
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function recordSetupHistory(homeDir, {
|
|
133
|
+
cliVersion,
|
|
134
|
+
options,
|
|
135
|
+
outcome,
|
|
136
|
+
checksBefore = null,
|
|
137
|
+
packageRoot,
|
|
138
|
+
workspaceRoot
|
|
139
|
+
}) {
|
|
140
|
+
if (options.dryRun) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (outcome.cancelled) {
|
|
145
|
+
await recordEvent(homeDir, cliVersion, {
|
|
146
|
+
command: "setup",
|
|
147
|
+
action: "cancelled",
|
|
148
|
+
wrote: false,
|
|
149
|
+
dryRun: false,
|
|
150
|
+
checksBefore
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const consentSource = await resolveConsentSource(
|
|
156
|
+
homeDir,
|
|
157
|
+
buildConsentOptions(options, { applying: true, dryRun: false })
|
|
158
|
+
);
|
|
159
|
+
const postReport = await buildStatusReport(homeDir, { packageRoot, workspaceRoot });
|
|
160
|
+
|
|
161
|
+
await recordEvent(homeDir, cliVersion, {
|
|
162
|
+
command: "setup",
|
|
163
|
+
action: "applied",
|
|
164
|
+
wrote: true,
|
|
165
|
+
dryRun: false,
|
|
166
|
+
consentSource,
|
|
167
|
+
agents: outcome.result.agents,
|
|
168
|
+
components: outcome.result.components,
|
|
169
|
+
checksBefore,
|
|
170
|
+
checksAfter: buildCheckSnapshot(postReport.counts),
|
|
171
|
+
backupsCreated: outcome.result.backups
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function recordSyncHistory(homeDir, {
|
|
176
|
+
cliVersion,
|
|
177
|
+
options,
|
|
178
|
+
outcome,
|
|
179
|
+
checksBefore,
|
|
180
|
+
packageRoot,
|
|
181
|
+
workspaceRoot
|
|
182
|
+
}) {
|
|
183
|
+
if (options.dryRun || outcome.action === "setup-required" || outcome.action === "noop" || outcome.action === "plan") {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (outcome.action === "cancelled") {
|
|
188
|
+
await recordEvent(homeDir, cliVersion, {
|
|
189
|
+
command: "sync",
|
|
190
|
+
action: "cancelled",
|
|
191
|
+
wrote: false,
|
|
192
|
+
dryRun: false,
|
|
193
|
+
checksBefore
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const consentSource = await resolveConsentSource(
|
|
199
|
+
homeDir,
|
|
200
|
+
buildConsentOptions(options, { applying: true, dryRun: false })
|
|
201
|
+
);
|
|
202
|
+
const postReport = await buildStatusReport(homeDir, { packageRoot, workspaceRoot });
|
|
203
|
+
|
|
204
|
+
await recordEvent(homeDir, cliVersion, {
|
|
205
|
+
command: "sync",
|
|
206
|
+
action: "repaired",
|
|
207
|
+
wrote: outcome.wrote,
|
|
208
|
+
dryRun: false,
|
|
209
|
+
consentSource,
|
|
210
|
+
agents: outcome.result?.agents ?? null,
|
|
211
|
+
components: outcome.result?.components ?? null,
|
|
212
|
+
checksBefore,
|
|
213
|
+
checksAfter: buildCheckSnapshot(postReport.counts),
|
|
214
|
+
backupsCreated: outcome.result?.backups ?? null
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function recordUpgradeHistory(homeDir, {
|
|
219
|
+
cliVersion,
|
|
220
|
+
options,
|
|
221
|
+
outcome,
|
|
222
|
+
checksBefore = null,
|
|
223
|
+
packageRoot,
|
|
224
|
+
workspaceRoot
|
|
225
|
+
}) {
|
|
226
|
+
if (outcome.cancelled) {
|
|
227
|
+
await recordEvent(homeDir, cliVersion, {
|
|
228
|
+
command: "upgrade",
|
|
229
|
+
action: "cancelled",
|
|
230
|
+
wrote: false,
|
|
231
|
+
dryRun: false,
|
|
232
|
+
checksBefore
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (outcome.dryRun) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const consentSource = await resolveConsentSource(
|
|
242
|
+
homeDir,
|
|
243
|
+
buildConsentOptions(options, { applying: true, dryRun: false })
|
|
244
|
+
);
|
|
245
|
+
const postReport = await buildStatusReport(homeDir, { packageRoot, workspaceRoot });
|
|
246
|
+
|
|
247
|
+
await recordEvent(homeDir, cliVersion, {
|
|
248
|
+
command: "upgrade",
|
|
249
|
+
action: "applied",
|
|
250
|
+
wrote: true,
|
|
251
|
+
dryRun: false,
|
|
252
|
+
consentSource,
|
|
253
|
+
agents: outcome.result?.agents ?? null,
|
|
254
|
+
components: outcome.result?.components ?? null,
|
|
255
|
+
checksBefore,
|
|
256
|
+
checksAfter: buildCheckSnapshot(postReport.counts),
|
|
257
|
+
backupsCreated: outcome.result?.backups ?? null
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function recordRollbackHistory(homeDir, {
|
|
262
|
+
cliVersion,
|
|
263
|
+
snapshot,
|
|
264
|
+
result
|
|
265
|
+
}) {
|
|
266
|
+
await recordEvent(homeDir, cliVersion, {
|
|
267
|
+
command: "rollback",
|
|
268
|
+
action: result.noop ? "noop" : "applied",
|
|
269
|
+
wrote: !result.noop,
|
|
270
|
+
dryRun: false,
|
|
271
|
+
snapshotsUsed: [snapshot],
|
|
272
|
+
backupsCreated: result.safetyBackup ? [result.safetyBackup] : null
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export async function recordUninstallHistory(homeDir, {
|
|
277
|
+
cliVersion,
|
|
278
|
+
options,
|
|
279
|
+
result
|
|
280
|
+
}) {
|
|
281
|
+
if (options.dryRun) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
await recordEvent(homeDir, cliVersion, {
|
|
286
|
+
command: "uninstall",
|
|
287
|
+
action: "applied",
|
|
288
|
+
wrote: true,
|
|
289
|
+
dryRun: false,
|
|
290
|
+
backupsCreated: result.backups
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function recordPolicyHistory(homeDir, {
|
|
295
|
+
cliVersion,
|
|
296
|
+
action
|
|
297
|
+
}) {
|
|
298
|
+
await recordEvent(homeDir, cliVersion, {
|
|
299
|
+
command: "policy",
|
|
300
|
+
action,
|
|
301
|
+
wrote: true,
|
|
302
|
+
dryRun: false
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function formatHistoryEvent(event) {
|
|
307
|
+
const wrote = event.wrote ? "yes" : "no";
|
|
308
|
+
const agents = Array.isArray(event.agents) ? event.agents.join(",") : "-";
|
|
309
|
+
const components = Array.isArray(event.components) ? event.components.join(",") : "-";
|
|
310
|
+
const consent = event.consentSource ?? "none";
|
|
311
|
+
|
|
312
|
+
return `${event.timestamp} ${event.command.padEnd(8)} ${String(event.action).padEnd(10)} wrote=${wrote} consent=${consent} agents=${agents} components=${components}`;
|
|
313
|
+
}
|