knodin 0.8.7 → 0.9.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/dist/bin/cli.js +307 -85
- package/dist/fixtures/update-verification/config/application.xml +4 -0
- package/dist/fixtures/update-verification/dbt/manifest.json +17 -0
- package/dist/fixtures/update-verification/dbt/models/accounts.sql +1 -0
- package/dist/fixtures/update-verification/dbt/models/contacts.sql +1 -0
- package/dist/fixtures/update-verification/fixture.json +147 -0
- package/dist/fixtures/update-verification/force-app/main/default/classes/UpdateFixture.cls +14 -0
- package/dist/fixtures/update-verification/force-app/main/default/flows/Update_Verification.flow-meta.xml +9 -0
- package/dist/fixtures/update-verification/package.json +5 -0
- package/dist/fixtures/update-verification/prisma/schema.prisma +10 -0
- package/dist/fixtures/update-verification/sql/functions.sql +16 -0
- package/dist/fixtures/update-verification/src/CsharpFixture.cs +6 -0
- package/dist/fixtures/update-verification/src/JavaFixture.java +9 -0
- package/dist/fixtures/update-verification/src/javascript.js +7 -0
- package/dist/fixtures/update-verification/src/python_fixture.py +6 -0
- package/dist/fixtures/update-verification/src/typescript.ts +7 -0
- package/dist/fixtures/update-verification/terraform/main.tf +7 -0
- package/dist/fixtures/update-verification/terraform/modules/verified-child/main.tf +3 -0
- package/dist/src/agent-integration.js +26 -3
- package/dist/src/backup-retention.js +811 -0
- package/dist/src/cli-model.js +35 -8
- package/dist/src/docs-sections.js +1 -0
- package/dist/src/doctor.js +142 -12
- package/dist/src/engine/embeddings.js +8 -1
- package/dist/src/engine/index.js +355 -292
- package/dist/src/init.js +222 -179
- package/dist/src/manager-update.js +455 -0
- package/dist/src/release-preflight.js +106 -95
- package/dist/src/repair-lease.js +77 -8
- package/dist/src/response-budget.js +5 -1
- package/dist/src/server.js +18 -5
- package/dist/src/shared-index/compatibility.js +4 -2
- package/dist/src/tools/knodin-tools.js +69 -10
- package/dist/src/update-ceremony.js +20 -24
- package/dist/src/update-coordination.js +158 -0
- package/dist/src/update-executor.js +336 -0
- package/dist/src/update-verifier.js +358 -0
- package/docs/BACKUP-RETENTION.md +54 -0
- package/docs/CLI.md +6 -0
- package/docs/DOCTOR-AND-UPDATES.md +44 -12
- package/docs/SIGNED-UPDATES.md +29 -22
- package/docs/releases/0.9.0.md +43 -0
- package/package.json +7 -1
- package/roadmap/competitive-roadmap.md +8 -0
package/dist/bin/cli.js
CHANGED
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
import { spawn, spawnSync } from "node:child_process";
|
|
15
15
|
import fs from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
|
-
import readline from "node:readline/promises";
|
|
18
17
|
import { fileURLToPath } from "node:url";
|
|
19
18
|
import { canUseSessionContextCache, parseClaudeHookPayload, readSessionContextCache, recordClaudeLifecycleEvent, renderSessionContext, SESSION_CONTEXT_BUDGET_LINE, sessionStartHookOutput, writeSessionContextCache, } from "../src/agent-events.js";
|
|
20
19
|
import { inspectClaudeAgentHooks, installClaudeAgentHooks, uninstallClaudeAgentHooks, } from "../src/agent-hooks.js";
|
|
21
20
|
import { detectSupportedAgents, parseInitScope, } from "../src/agent-integration.js";
|
|
22
21
|
import { refreshExternalGraphArtifacts, writeArtifactRefreshRecord, } from "../src/artifact-refresh.js";
|
|
22
|
+
import { formatBackupHuman, installBackupRetention, listBackups, maybeRunOpportunisticRetention, pruneBackups, removeBackupRetention, retentionDoctor, retentionStatus, runInstalledRetention, } from "../src/backup-retention.js";
|
|
23
23
|
import { checkIndexed, extractPositionals, extractRepoFlag, parseReviewArgs, planIndex, resolveCliRuntimeCommand, resolveRepo, } from "../src/cli-args.js";
|
|
24
24
|
import { helpCommandPath, parseCliInvocation, renderCliHelp } from "../src/cli-model.js";
|
|
25
25
|
import { buildKnodinContext } from "../src/context.js";
|
|
@@ -33,14 +33,16 @@ import { diagnoseFailure, } from "../src/failure-diagnosis.js";
|
|
|
33
33
|
import { gitExecutable } from "../src/git-executable.js";
|
|
34
34
|
import { decorateGraphQueryResult, inspectGraphQueryHealth } from "../src/graph-query-health.js";
|
|
35
35
|
import { createIndexActivityReporter } from "../src/index-activity.js";
|
|
36
|
-
import {
|
|
36
|
+
import { InitializationHealthError, initializeRepository, inspectRepositoryIntegrationStatus, readRepositoryIntegrationConfig, refreshFromGitEvent, } from "../src/init.js";
|
|
37
37
|
import { createInitProgressRenderer } from "../src/init-progress.js";
|
|
38
38
|
import { attachLifecycleHealth, attachRepairLifecycle } from "../src/lifecycle-health.js";
|
|
39
|
+
import { acknowledgeUpdateFailure, queryOwnerAvailability, readManagerUpdateState, readUpdateJournal, resolveManagerOwnership, setManualPin, unpinUpdate, updateAttention, writeManagerUpdateState, } from "../src/manager-update.js";
|
|
39
40
|
import { addMirror, listMirrors, refreshMirror, removeMirror } from "../src/mirror.js";
|
|
40
41
|
import { compressOutput, compressOutputFile, deleteOutputArtifact, readOutputArtifact, } from "../src/output-compression.js";
|
|
41
42
|
import { clearTelemetry, exportTelemetry, readTelemetryRecords, telemetryStatus, writeTelemetryReport, } from "../src/output-telemetry.js";
|
|
42
43
|
import { auditPullRequests } from "../src/pr-triage.js";
|
|
43
44
|
import { deliverProgressiveEvidence, } from "../src/progressive-evidence.js";
|
|
45
|
+
import { acquireRepairLease } from "../src/repair-lease.js";
|
|
44
46
|
import { createRepairPlan, createRepairProgressRenderer, parseRepairCliArgs, resolveRepairProgressMode, serializeRepairJsonlRecord, } from "../src/repair-progress.js";
|
|
45
47
|
import { runRepositoryInitializationProcess } from "../src/repository-init-process.js";
|
|
46
48
|
import { detectRepositorySignals, discoverRepositories, formatRepositoryHuman, initializeRepositories, inventoryRepository, parseFleetInitArgs, parseRepositoryCommandArgs, repositorySignalInspectionLimit, searchRepositories, withRepositorySignals, } from "../src/repository-management.js";
|
|
@@ -49,6 +51,8 @@ import { appendSessionEvent, clearSessionTelemetry, disableSessionTelemetry, ena
|
|
|
49
51
|
import { inspectKnodinSkills, installKnodinSkills, KNODIN_SKILLS, removeKnodinSkills, } from "../src/skill-management.js";
|
|
50
52
|
import { configuredRepositoryInitMemoryLimitBytes, enrichSystemRelationships, incorporateSystemQueryEvidence, indexModeForPath, loadSystemConfiguration, queryConfiguredSystem, systemMembershipsForPath, validateSystemHealth, } from "../src/system-config.js";
|
|
51
53
|
import { applySystemPlan, planSystemImport, planSystemLink, planSystemRelate, planSystemUnlink, planSystemUnrelate, } from "../src/system-management.js";
|
|
54
|
+
import { coordinationStatus } from "../src/update-coordination.js";
|
|
55
|
+
import { certifyAutomaticUpdates, executeManagerUpdate, recoverInterruptedUpdate, } from "../src/update-executor.js";
|
|
52
56
|
import { applyTrustedUpdate, checkTrustedUpdate, claimScheduledUpdateCheck, detectUpdateInstallMethod, explainTrustedUpdate, releaseScheduledUpdateCheck, rollbackTrustedUpdate, trustedUpdateStatus, } from "../src/update-policy.js";
|
|
53
57
|
import { KNODIN_VERSION } from "../src/version.js";
|
|
54
58
|
import { writeVisualization, } from "../src/visualization.js";
|
|
@@ -65,44 +69,6 @@ function explicitScope(args) {
|
|
|
65
69
|
const equals = args.find((argument) => argument.startsWith("--scope="));
|
|
66
70
|
return equals ? parseInitScope(equals.slice("--scope=".length)) : null;
|
|
67
71
|
}
|
|
68
|
-
async function chooseInitScope(args, repo) {
|
|
69
|
-
const selected = explicitScope(args);
|
|
70
|
-
if (selected)
|
|
71
|
-
return selected;
|
|
72
|
-
if (detectTrackedTeamIntegration(repo)) {
|
|
73
|
-
process.stderr.write("[init:scope] Detected tracked knodin team integration; preserving team scope\n");
|
|
74
|
-
return "team";
|
|
75
|
-
}
|
|
76
|
-
if (!process.stdin.isTTY || !process.stderr.isTTY)
|
|
77
|
-
return "personal";
|
|
78
|
-
const prompt = readline.createInterface({
|
|
79
|
-
input: process.stdin,
|
|
80
|
-
output: process.stderr,
|
|
81
|
-
});
|
|
82
|
-
try {
|
|
83
|
-
const answer = await prompt.question([
|
|
84
|
-
"How should knodin integrate with coding agents?",
|
|
85
|
-
" 1. Personal (recommended) — all detected agents; Git stays clean",
|
|
86
|
-
" 2. Team — create commit-ready shared configuration",
|
|
87
|
-
" 3. CLI-only — agents will not discover or invoke knodin automatically",
|
|
88
|
-
"Select [1]: ",
|
|
89
|
-
].join("\n"));
|
|
90
|
-
if (!answer.trim() || answer.trim() === "1")
|
|
91
|
-
return "personal";
|
|
92
|
-
if (answer.trim() === "2")
|
|
93
|
-
return "team";
|
|
94
|
-
if (answer.trim() === "3") {
|
|
95
|
-
const confirmation = await prompt.question("CLI-only requires manual knodin commands. Continue? [y/N] ");
|
|
96
|
-
if (!/^y(?:es)?$/i.test(confirmation.trim()))
|
|
97
|
-
throw new Error("knodin init: CLI-only selection cancelled");
|
|
98
|
-
return "cli-only";
|
|
99
|
-
}
|
|
100
|
-
return parseInitScope(answer.trim());
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
prompt.close();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
72
|
function integrationAgents(repo) {
|
|
107
73
|
const previous = readRepositoryIntegrationConfig(repo)?.agents ?? [];
|
|
108
74
|
const repositoryDetected = inspectRepositoryIntegrationStatus(repo)?.agents ?? [];
|
|
@@ -151,7 +117,7 @@ function formatConfigureHuman(result) {
|
|
|
151
117
|
const externalOutcomeLines = result.paths.externalConfigurationOutcomes
|
|
152
118
|
.map(({ system, state }) => `\nExternal configuration outcome: ${system} ${state} (external mutation not locally observable)`)
|
|
153
119
|
.join("");
|
|
154
|
-
return `${result.message}\nAgent integration: ${result.paths.scope} — ${configured}${failures}${filesystemMutationLines}${externalOutcomeLines}\nGraph initialization:
|
|
120
|
+
return `${result.message}\nAgent integration: ${result.paths.scope} — ${configured}${failures}${filesystemMutationLines}${externalOutcomeLines}\nGraph initialization: refreshed and verified\nLifecycle refresh: ${refresh}\nNext: ${result.nextAction}\n`;
|
|
155
121
|
}
|
|
156
122
|
function formatRepairHuman(result) {
|
|
157
123
|
const coverage = result.after.coverage;
|
|
@@ -705,6 +671,26 @@ async function main() {
|
|
|
705
671
|
// `--json` is a shared output flag. Repair owns its richer --json/--jsonl
|
|
706
672
|
// parser; all other commands receive their original arguments minus it.
|
|
707
673
|
const rest = cmd === "repair" ? rawRest : rawRest.filter((argument) => argument !== "--json");
|
|
674
|
+
let recoveryJournal = readUpdateJournal();
|
|
675
|
+
const recoverySafeStates = new Set(["planned", "candidate-verified", "rollback-verified"]);
|
|
676
|
+
if (recoveryJournal && !recoverySafeStates.has(recoveryJournal.state) && cmd !== "doctor") {
|
|
677
|
+
const recovery = await recoverInterruptedUpdate({ runtimeCommand, env: process.env });
|
|
678
|
+
recoveryJournal = readUpdateJournal();
|
|
679
|
+
if (recovery?.status === "terminal-unhealthy" && cmd !== "update") {
|
|
680
|
+
throw new Error(`knodin update recovery is terminal-unhealthy: ${recovery.reason ?? "candidate and rollback verification failed"}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (recoveryJournal &&
|
|
684
|
+
!recoverySafeStates.has(recoveryJournal.state) &&
|
|
685
|
+
cmd !== "update" &&
|
|
686
|
+
cmd !== "doctor") {
|
|
687
|
+
throw new Error(`knodin is in unverified update state ${recoveryJournal.state}; run \`knodin update status\` for recovery guidance`);
|
|
688
|
+
}
|
|
689
|
+
if (!jsonOutput && cmd !== "update") {
|
|
690
|
+
const attention = updateAttention(readManagerUpdateState());
|
|
691
|
+
if (attention)
|
|
692
|
+
process.stderr.write(`${attention}\n`);
|
|
693
|
+
}
|
|
708
694
|
const selector = {
|
|
709
695
|
identity: selectorValue("--identity"),
|
|
710
696
|
file: selectorValue("--file"),
|
|
@@ -826,7 +812,11 @@ async function main() {
|
|
|
826
812
|
repositories,
|
|
827
813
|
};
|
|
828
814
|
const boundedOutput = plan.signals
|
|
829
|
-
? applyResponseBudget(output, "repositories:discover", {
|
|
815
|
+
? applyResponseBudget(output, "repositories:discover", {
|
|
816
|
+
bytes: plan.byteBudget,
|
|
817
|
+
tokens: plan.tokenBudget,
|
|
818
|
+
items: plan.itemBudget,
|
|
819
|
+
}, { bytes: 65_536, tokens: 16_384, items: 100 })
|
|
830
820
|
: output;
|
|
831
821
|
process.stdout.write(`${JSON.stringify(boundedOutput, null, plan.json ? 0 : 2)}\n`);
|
|
832
822
|
process.exitCode = discovery.issues.length > 0 ? 1 : 0;
|
|
@@ -903,7 +893,12 @@ async function main() {
|
|
|
903
893
|
}));
|
|
904
894
|
}
|
|
905
895
|
await engine.close();
|
|
906
|
-
const output = {
|
|
896
|
+
const output = {
|
|
897
|
+
schemaVersion: 1,
|
|
898
|
+
command: "init",
|
|
899
|
+
...summary,
|
|
900
|
+
repositories,
|
|
901
|
+
};
|
|
907
902
|
process.stdout.write(plan.json ? `${JSON.stringify(output)}\n` : formatRepositoryHuman(summary));
|
|
908
903
|
process.exitCode = summary.exitCode;
|
|
909
904
|
return;
|
|
@@ -1010,13 +1005,15 @@ async function main() {
|
|
|
1010
1005
|
if (cmd === "update") {
|
|
1011
1006
|
if (repoFlag !== undefined)
|
|
1012
1007
|
throw new Error("knodin update does not accept --repo");
|
|
1013
|
-
const [action, ...
|
|
1014
|
-
if (!action
|
|
1015
|
-
throw new Error("knodin update requires
|
|
1016
|
-
}
|
|
1017
|
-
if (unsupported.length > 0)
|
|
1018
|
-
throw new Error(`knodin update ${action}: unknown argument ${unsupported[0]}`);
|
|
1008
|
+
const [action, ...actionArguments] = rest;
|
|
1009
|
+
if (!action)
|
|
1010
|
+
throw new Error("knodin update requires an action");
|
|
1019
1011
|
const method = detectUpdateInstallMethod(runtimeCommand);
|
|
1012
|
+
const owner = resolveManagerOwnership({
|
|
1013
|
+
runtimeCommand,
|
|
1014
|
+
executablePath: runtimeCommand.at(-1),
|
|
1015
|
+
env: process.env,
|
|
1016
|
+
});
|
|
1020
1017
|
const runManager = async (argv) => {
|
|
1021
1018
|
const [executable, ...arguments_] = argv;
|
|
1022
1019
|
if (!executable)
|
|
@@ -1052,8 +1049,16 @@ async function main() {
|
|
|
1052
1049
|
healthCheck,
|
|
1053
1050
|
};
|
|
1054
1051
|
let output;
|
|
1055
|
-
if (action === "status")
|
|
1056
|
-
output =
|
|
1052
|
+
if (action === "status") {
|
|
1053
|
+
output = {
|
|
1054
|
+
...trustedUpdateStatus(options),
|
|
1055
|
+
owner,
|
|
1056
|
+
managerState: readManagerUpdateState(),
|
|
1057
|
+
journal: readUpdateJournal(),
|
|
1058
|
+
coordination: coordinationStatus(),
|
|
1059
|
+
attention: updateAttention(readManagerUpdateState()),
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1057
1062
|
else if (action === "check") {
|
|
1058
1063
|
try {
|
|
1059
1064
|
output = await checkTrustedUpdate(options);
|
|
@@ -1067,11 +1072,178 @@ async function main() {
|
|
|
1067
1072
|
output = explainTrustedUpdate(options);
|
|
1068
1073
|
else if (action === "apply")
|
|
1069
1074
|
output = await applyTrustedUpdate(options);
|
|
1070
|
-
else
|
|
1075
|
+
else if (action === "rollback")
|
|
1071
1076
|
output = await rollbackTrustedUpdate(options);
|
|
1077
|
+
else if (action === "available") {
|
|
1078
|
+
output = await queryOwnerAvailability({ installedVersion: KNODIN_VERSION, owner });
|
|
1079
|
+
const availability = output;
|
|
1080
|
+
const state = readManagerUpdateState();
|
|
1081
|
+
if (availability.latestVersion &&
|
|
1082
|
+
state.pin?.kind === "automatic-rollback" &&
|
|
1083
|
+
state.pin.acknowledgedFailedVersion === state.pin.failedVersion &&
|
|
1084
|
+
availability.latestVersion !== state.pin.failedVersion) {
|
|
1085
|
+
writeManagerUpdateState({
|
|
1086
|
+
...state,
|
|
1087
|
+
lastAvailableVersion: availability.latestVersion,
|
|
1088
|
+
pin: { ...state.pin, newerAvailableVersion: availability.latestVersion },
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
else if (action === "upgrade") {
|
|
1093
|
+
const apply = actionArguments.includes("--apply");
|
|
1094
|
+
const explicitVersion = actionArguments.find((argument) => argument !== "--apply");
|
|
1095
|
+
let targetVersion = explicitVersion;
|
|
1096
|
+
if (!targetVersion) {
|
|
1097
|
+
const availability = await queryOwnerAvailability({
|
|
1098
|
+
installedVersion: KNODIN_VERSION,
|
|
1099
|
+
owner,
|
|
1100
|
+
});
|
|
1101
|
+
if (!availability.latestVersion)
|
|
1102
|
+
output = availability;
|
|
1103
|
+
else
|
|
1104
|
+
targetVersion = availability.latestVersion;
|
|
1105
|
+
}
|
|
1106
|
+
if (targetVersion)
|
|
1107
|
+
output = await executeManagerUpdate({
|
|
1108
|
+
currentVersion: KNODIN_VERSION,
|
|
1109
|
+
targetVersion,
|
|
1110
|
+
runtimeCommand,
|
|
1111
|
+
env: process.env,
|
|
1112
|
+
apply,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
else if (action === "pin") {
|
|
1116
|
+
const apply = actionArguments.includes("--apply");
|
|
1117
|
+
const version = actionArguments.find((argument) => argument !== "--apply");
|
|
1118
|
+
if (!version)
|
|
1119
|
+
throw new Error("knodin update pin requires a version");
|
|
1120
|
+
output = apply
|
|
1121
|
+
? await executeManagerUpdate({
|
|
1122
|
+
currentVersion: KNODIN_VERSION,
|
|
1123
|
+
targetVersion: version,
|
|
1124
|
+
runtimeCommand,
|
|
1125
|
+
env: process.env,
|
|
1126
|
+
apply: true,
|
|
1127
|
+
pinKind: "manual",
|
|
1128
|
+
})
|
|
1129
|
+
: setManualPin(version);
|
|
1130
|
+
}
|
|
1131
|
+
else if (action === "unpin")
|
|
1132
|
+
output = unpinUpdate();
|
|
1133
|
+
else if (action === "acknowledge")
|
|
1134
|
+
output = acknowledgeUpdateFailure();
|
|
1135
|
+
else if (action === "auto") {
|
|
1136
|
+
const subaction = actionArguments[0];
|
|
1137
|
+
if (!subaction || !["status", "enable", "disable", "run"].includes(subaction))
|
|
1138
|
+
throw new Error("knodin update auto requires status, enable, disable, or run");
|
|
1139
|
+
const state = readManagerUpdateState();
|
|
1140
|
+
if (subaction === "status")
|
|
1141
|
+
output = state;
|
|
1142
|
+
else if (subaction === "disable") {
|
|
1143
|
+
output = { ...state, autoEnabled: false };
|
|
1144
|
+
writeManagerUpdateState(output);
|
|
1145
|
+
}
|
|
1146
|
+
else if (subaction === "enable") {
|
|
1147
|
+
if (state.platformAutoEligible) {
|
|
1148
|
+
output = { ...state, autoEnabled: true };
|
|
1149
|
+
writeManagerUpdateState(output);
|
|
1150
|
+
}
|
|
1151
|
+
else
|
|
1152
|
+
output = await certifyAutomaticUpdates({
|
|
1153
|
+
currentVersion: KNODIN_VERSION,
|
|
1154
|
+
runtimeCommand,
|
|
1155
|
+
env: process.env,
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
else if (!state.autoEnabled || !state.platformAutoEligible) {
|
|
1159
|
+
output = { ...state, status: "disabled" };
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
const availability = await queryOwnerAvailability({
|
|
1163
|
+
installedVersion: KNODIN_VERSION,
|
|
1164
|
+
owner,
|
|
1165
|
+
});
|
|
1166
|
+
output =
|
|
1167
|
+
availability.status === "available" &&
|
|
1168
|
+
availability.autoEligible &&
|
|
1169
|
+
availability.latestVersion
|
|
1170
|
+
? await executeManagerUpdate({
|
|
1171
|
+
currentVersion: KNODIN_VERSION,
|
|
1172
|
+
targetVersion: availability.latestVersion,
|
|
1173
|
+
runtimeCommand,
|
|
1174
|
+
env: process.env,
|
|
1175
|
+
apply: true,
|
|
1176
|
+
})
|
|
1177
|
+
: availability;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
else
|
|
1181
|
+
throw new Error(`knodin update: unknown action ${action}`);
|
|
1072
1182
|
process.stdout.write(jsonOutput ? `${JSON.stringify(output)}\n` : formatGenericHuman("update", output));
|
|
1073
1183
|
return;
|
|
1074
1184
|
}
|
|
1185
|
+
if (cmd === "backups") {
|
|
1186
|
+
const action = invocation.commandPath[1];
|
|
1187
|
+
const retentionAction = invocation.commandPath[2];
|
|
1188
|
+
const roots = invocation.positionals;
|
|
1189
|
+
const defaultRoots = () => {
|
|
1190
|
+
if (roots.length > 0)
|
|
1191
|
+
return roots;
|
|
1192
|
+
const target = resolveRepo(repoFlag, process.cwd());
|
|
1193
|
+
if (!target.ok)
|
|
1194
|
+
throw new Error(target.error);
|
|
1195
|
+
return [target.repo];
|
|
1196
|
+
};
|
|
1197
|
+
const depth = invocation.options.depth;
|
|
1198
|
+
const retentionDays = invocation.options.retentionDays;
|
|
1199
|
+
const keepNewest = invocation.options.keepNewest;
|
|
1200
|
+
let output;
|
|
1201
|
+
if (action === "list")
|
|
1202
|
+
output = listBackups(defaultRoots(), { depth });
|
|
1203
|
+
else if (action === "prune") {
|
|
1204
|
+
output = pruneBackups(defaultRoots(), {
|
|
1205
|
+
depth,
|
|
1206
|
+
retentionDays,
|
|
1207
|
+
keepNewest,
|
|
1208
|
+
apply: invocation.options.apply === true,
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
else if (action === "retention" && retentionAction === "install") {
|
|
1212
|
+
output = installBackupRetention(roots, {
|
|
1213
|
+
depth,
|
|
1214
|
+
retentionDays,
|
|
1215
|
+
keepNewest,
|
|
1216
|
+
schedule: invocation.options.schedule,
|
|
1217
|
+
dryRun: invocation.options.dryRun === true,
|
|
1218
|
+
launcher: runtimeCommand,
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
else if (action === "retention" && retentionAction === "status") {
|
|
1222
|
+
output = retentionStatus();
|
|
1223
|
+
}
|
|
1224
|
+
else if (action === "retention" && retentionAction === "doctor") {
|
|
1225
|
+
output = retentionDoctor();
|
|
1226
|
+
}
|
|
1227
|
+
else if (action === "retention" && retentionAction === "remove") {
|
|
1228
|
+
output = removeBackupRetention();
|
|
1229
|
+
}
|
|
1230
|
+
else if (action === "retention" && retentionAction === "run") {
|
|
1231
|
+
output = runInstalledRetention();
|
|
1232
|
+
}
|
|
1233
|
+
else {
|
|
1234
|
+
throw new Error("knodin backups requires list, prune, or retention install|status|doctor|remove");
|
|
1235
|
+
}
|
|
1236
|
+
if (invocation.options.jsonl === true) {
|
|
1237
|
+
const records = output.backups ??
|
|
1238
|
+
output.candidates ?? [output];
|
|
1239
|
+
for (const record of records)
|
|
1240
|
+
process.stdout.write(`${JSON.stringify(record)}\n`);
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
process.stdout.write(jsonOutput ? `${JSON.stringify(output)}\n` : formatBackupHuman("backups", output));
|
|
1244
|
+
}
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1075
1247
|
if (process.stdout.isTTY &&
|
|
1076
1248
|
claimScheduledUpdateCheck({
|
|
1077
1249
|
currentVersion: KNODIN_VERSION,
|
|
@@ -1123,6 +1295,14 @@ async function main() {
|
|
|
1123
1295
|
process.exit(1);
|
|
1124
1296
|
}
|
|
1125
1297
|
const repo = resolved.repo;
|
|
1298
|
+
if (cmd !== "doctor") {
|
|
1299
|
+
try {
|
|
1300
|
+
maybeRunOpportunisticRetention(repo);
|
|
1301
|
+
}
|
|
1302
|
+
catch {
|
|
1303
|
+
// Best-effort cleanup must never make an ordinary command fail.
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1126
1306
|
if (cmd === "agent-event" && !fs.existsSync(resolveDbPath(repo)))
|
|
1127
1307
|
return;
|
|
1128
1308
|
if (cmd === "doctor") {
|
|
@@ -1246,7 +1426,10 @@ async function main() {
|
|
|
1246
1426
|
const input = positionals[0] === "create" ? (positionals[1] ?? "-") : (positionals[0] ?? "-");
|
|
1247
1427
|
compressionResult =
|
|
1248
1428
|
input === "-"
|
|
1249
|
-
? compressOutput(repo, {
|
|
1429
|
+
? compressOutput(repo, {
|
|
1430
|
+
...request,
|
|
1431
|
+
text: await readBoundedStdin(maxInputBytes),
|
|
1432
|
+
})
|
|
1250
1433
|
: compressOutputFile(repo, input, request);
|
|
1251
1434
|
}
|
|
1252
1435
|
if (action === "diagnose" &&
|
|
@@ -1325,7 +1508,10 @@ async function main() {
|
|
|
1325
1508
|
if (existing.state !== "configured")
|
|
1326
1509
|
throw new Error("knodin shared configure --disable requires a valid existing configuration");
|
|
1327
1510
|
writeSharedIndexConfig(repo, { ...existing.config, enabled: false });
|
|
1328
|
-
result = {
|
|
1511
|
+
result = {
|
|
1512
|
+
state: "configured",
|
|
1513
|
+
config: { ...existing.config, enabled: false },
|
|
1514
|
+
};
|
|
1329
1515
|
break;
|
|
1330
1516
|
}
|
|
1331
1517
|
const required = (name) => {
|
|
@@ -1360,7 +1546,11 @@ async function main() {
|
|
|
1360
1546
|
},
|
|
1361
1547
|
};
|
|
1362
1548
|
const target = writeSharedIndexConfig(repo, config);
|
|
1363
|
-
result = {
|
|
1549
|
+
result = {
|
|
1550
|
+
state: "configured",
|
|
1551
|
+
path: target,
|
|
1552
|
+
config: loadSharedIndexConfig(repo),
|
|
1553
|
+
};
|
|
1364
1554
|
break;
|
|
1365
1555
|
}
|
|
1366
1556
|
if (action === "publisher-metadata") {
|
|
@@ -1394,7 +1584,10 @@ async function main() {
|
|
|
1394
1584
|
if (!bundlePath)
|
|
1395
1585
|
throw new Error("knodin shared publisher-finalize requires <bundle>");
|
|
1396
1586
|
const bundle = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), bundlePath), "utf8"));
|
|
1397
|
-
const pointer = finalizeSharedPublisherBundle({
|
|
1587
|
+
const pointer = finalizeSharedPublisherBundle({
|
|
1588
|
+
config: configuration.config,
|
|
1589
|
+
bundle,
|
|
1590
|
+
});
|
|
1398
1591
|
result = { bundle, pointer: JSON.parse(pointer.toString("utf8")) };
|
|
1399
1592
|
break;
|
|
1400
1593
|
}
|
|
@@ -1421,12 +1614,19 @@ async function main() {
|
|
|
1421
1614
|
let credentialProbe = "not-requested";
|
|
1422
1615
|
if (invocation.options.probe === true) {
|
|
1423
1616
|
if (configuration.state !== "configured" || !configuration.config.enabled) {
|
|
1424
|
-
credentialProbe = {
|
|
1617
|
+
credentialProbe = {
|
|
1618
|
+
state: "unavailable",
|
|
1619
|
+
category: "configuration",
|
|
1620
|
+
};
|
|
1425
1621
|
}
|
|
1426
1622
|
else {
|
|
1427
1623
|
const store = createSharedObjectStore(configuration.config);
|
|
1428
1624
|
try {
|
|
1429
|
-
const current = spawnSync(gitExecutable(), ["symbolic-ref", "--quiet", "--short", "HEAD"], {
|
|
1625
|
+
const current = spawnSync(gitExecutable(), ["symbolic-ref", "--quiet", "--short", "HEAD"], {
|
|
1626
|
+
cwd: repo,
|
|
1627
|
+
encoding: "utf8",
|
|
1628
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1629
|
+
}).stdout.trim();
|
|
1430
1630
|
const branch = configuredBranchCandidates(configuration.config, current || undefined)[0];
|
|
1431
1631
|
if (!store || !branch)
|
|
1432
1632
|
throw new SharedIndexError("configuration", "shared-index probe has no configured local branch candidate");
|
|
@@ -1703,7 +1903,11 @@ async function main() {
|
|
|
1703
1903
|
const [systemId, ...repositories] = invocation.positionals;
|
|
1704
1904
|
if (!systemId)
|
|
1705
1905
|
throw new Error("knodin system unlink requires <system-id>");
|
|
1706
|
-
const plan = planSystemUnlink({
|
|
1906
|
+
const plan = planSystemUnlink({
|
|
1907
|
+
systemId,
|
|
1908
|
+
repositories,
|
|
1909
|
+
repoPath: repo,
|
|
1910
|
+
});
|
|
1707
1911
|
result = invocation.options.dryRun === true ? plan : applySystemPlan(plan);
|
|
1708
1912
|
break;
|
|
1709
1913
|
}
|
|
@@ -1822,32 +2026,39 @@ async function main() {
|
|
|
1822
2026
|
break;
|
|
1823
2027
|
}
|
|
1824
2028
|
case "hook-refresh": {
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
event
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2029
|
+
const lifecycleLease = acquireRepairLease(repo, "lifecycle-hook");
|
|
2030
|
+
try {
|
|
2031
|
+
const [kind, first, second] = rest;
|
|
2032
|
+
let event;
|
|
2033
|
+
if (kind === "commit")
|
|
2034
|
+
event = { kind };
|
|
2035
|
+
else if (kind === "checkout" && first && second) {
|
|
2036
|
+
event = { kind, before: first, after: second };
|
|
2037
|
+
}
|
|
2038
|
+
else if (kind === "merge" && first && second) {
|
|
2039
|
+
event = { kind, before: first, after: second };
|
|
2040
|
+
}
|
|
2041
|
+
else if (kind === "rewrite" && first) {
|
|
2042
|
+
event = { kind, inputPath: first };
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
throw new Error("knodin hook-refresh: invalid lifecycle event");
|
|
2046
|
+
}
|
|
2047
|
+
const shared = await opportunisticSharedRestore();
|
|
2048
|
+
const indexed = shared?.restored
|
|
2049
|
+
? []
|
|
2050
|
+
: await refreshFromGitEvent(repo, event, (target, files) => engine.index(target, files));
|
|
2051
|
+
result = { indexed, ...(shared ? { sharedRestore: shared } : {}) };
|
|
1837
2052
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
2053
|
+
finally {
|
|
2054
|
+
lifecycleLease.release();
|
|
1840
2055
|
}
|
|
1841
|
-
const shared = await opportunisticSharedRestore();
|
|
1842
|
-
const indexed = shared?.restored
|
|
1843
|
-
? []
|
|
1844
|
-
: await refreshFromGitEvent(repo, event, (target, files) => engine.index(target, files));
|
|
1845
|
-
result = { indexed, ...(shared ? { sharedRestore: shared } : {}) };
|
|
1846
2056
|
break;
|
|
1847
2057
|
}
|
|
1848
2058
|
case "init": {
|
|
1849
|
-
const
|
|
1850
|
-
const
|
|
2059
|
+
const requestedScope = explicitScope(rawRest);
|
|
2060
|
+
const scope = requestedScope ?? readRepositoryIntegrationConfig(repo)?.scope ?? "cli-only";
|
|
2061
|
+
const agents = requestedScope && scope !== "team" ? integrationAgents(repo) : [];
|
|
1851
2062
|
const renderer = createInitRenderer();
|
|
1852
2063
|
const activity = createIndexActivityReporter(repo);
|
|
1853
2064
|
activity.start();
|
|
@@ -1865,6 +2076,7 @@ async function main() {
|
|
|
1865
2076
|
index: (target, options) => engine.index(target, undefined, false, options),
|
|
1866
2077
|
scope,
|
|
1867
2078
|
agents,
|
|
2079
|
+
configureIntegration: requestedScope !== null,
|
|
1868
2080
|
onProgress: (event) => {
|
|
1869
2081
|
activity.update(event);
|
|
1870
2082
|
renderer.onProgress(event);
|
|
@@ -1909,16 +2121,17 @@ async function main() {
|
|
|
1909
2121
|
const agents = scope === "team" ? [] : integrationAgents(repo);
|
|
1910
2122
|
const paths = await initializeRepository(repo, {
|
|
1911
2123
|
command: runtimeCommand,
|
|
1912
|
-
index:
|
|
2124
|
+
index: (target, options) => engine.index(target, undefined, false, options),
|
|
1913
2125
|
scope,
|
|
1914
2126
|
agents,
|
|
1915
2127
|
allowTrackedTransition: true,
|
|
1916
2128
|
auditConfigurationChanges: true,
|
|
2129
|
+
configureIntegration: true,
|
|
1917
2130
|
});
|
|
1918
2131
|
result = {
|
|
1919
2132
|
status: "success",
|
|
1920
2133
|
message: `knodin agent integration changed to ${scope}.`,
|
|
1921
|
-
graphInitialization: "
|
|
2134
|
+
graphInitialization: "refreshed",
|
|
1922
2135
|
nextAction: "run `knodin status` and reload the configured client",
|
|
1923
2136
|
paths,
|
|
1924
2137
|
};
|
|
@@ -2068,7 +2281,9 @@ async function main() {
|
|
|
2068
2281
|
}
|
|
2069
2282
|
const progressMode = resolveRepairProgressMode(options, process.env, process.stderr.isTTY);
|
|
2070
2283
|
const renderer = progressMode === "tty"
|
|
2071
|
-
? createProgressWorkerRenderer("repair-progress-worker", {
|
|
2284
|
+
? createProgressWorkerRenderer("repair-progress-worker", {
|
|
2285
|
+
type: "start",
|
|
2286
|
+
})
|
|
2072
2287
|
: (() => {
|
|
2073
2288
|
const direct = createRepairProgressRenderer({
|
|
2074
2289
|
mode: progressMode,
|
|
@@ -2509,7 +2724,10 @@ async function main() {
|
|
|
2509
2724
|
else if (action === "export")
|
|
2510
2725
|
result = exportTelemetry(repo, readTelemetryRecords(repo, input, retentionDays), selectorValue("--output"), readSessionEvents(repo, retentionDays));
|
|
2511
2726
|
else if (action === "clear")
|
|
2512
|
-
result = {
|
|
2727
|
+
result = {
|
|
2728
|
+
...clearTelemetry(repo, input),
|
|
2729
|
+
session: clearSessionTelemetry(repo),
|
|
2730
|
+
};
|
|
2513
2731
|
else
|
|
2514
2732
|
throw new Error("knodin telemetry requires enable, disable, status, report, export, or clear");
|
|
2515
2733
|
break;
|
|
@@ -2581,7 +2799,11 @@ async function main() {
|
|
|
2581
2799
|
result =
|
|
2582
2800
|
action === "preview"
|
|
2583
2801
|
? persistDiagnosticsPreview(repo, options)
|
|
2584
|
-
: collectDiagnostics(repo, {
|
|
2802
|
+
: collectDiagnostics(repo, {
|
|
2803
|
+
...options,
|
|
2804
|
+
outputPath: outputOption,
|
|
2805
|
+
previewId,
|
|
2806
|
+
});
|
|
2585
2807
|
}
|
|
2586
2808
|
else {
|
|
2587
2809
|
throw new Error("knodin diagnostics requires enable, status, preview, archive, inspect, clear, or disable");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"metadata": { "dbt_schema_version": "https://schemas.getdbt.com/dbt/manifest/v12.json" },
|
|
3
|
+
"nodes": {
|
|
4
|
+
"model.fixture.accounts": {
|
|
5
|
+
"resource_type": "model",
|
|
6
|
+
"name": "accounts",
|
|
7
|
+
"raw_code": "select 1 as account_id",
|
|
8
|
+
"depends_on": { "nodes": [] }
|
|
9
|
+
},
|
|
10
|
+
"model.fixture.contacts": {
|
|
11
|
+
"resource_type": "model",
|
|
12
|
+
"name": "contacts",
|
|
13
|
+
"raw_code": "select account_id from accounts",
|
|
14
|
+
"depends_on": { "nodes": ["model.fixture.accounts"] }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
select 1 as account_id
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
select account_id from {{ ref('accounts') }}
|