skillwiki 0.10.16 → 0.10.18
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/{chunk-OWPQJOFG.js → chunk-GNS2ZV5P.js} +44 -3
- package/dist/{chunk-24MVNAFJ.js → chunk-IAPD6YTC.js} +24 -55
- package/dist/{chunk-NMUYMNNB.js → chunk-IGCW3DR2.js} +1 -1
- package/dist/{chunk-UJLSUJB6.js → chunk-QJPCCW4N.js} +141 -80
- package/dist/{chunk-C5OLZRRM.js → chunk-Y6KRDGI2.js} +70 -2
- package/dist/cli.js +441 -467
- package/dist/{index-projection-MHXG7QO2.js → index-projection-DENEYZTK.js} +2 -2
- package/dist/{managed-write-preflight-M2HL6DDY.js → managed-write-preflight-LO4LZ6OQ.js} +3 -3
- package/dist/skillwiki-mcp.js +4 -4
- package/dist/vault-sync/scripts/generate-systemd-property-catalog.mjs +73 -0
- package/dist/vault-sync/scripts/wiki-snapshot.sh +280 -4
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
ExitCode,
|
|
4
4
|
FleetManifestSchema,
|
|
5
5
|
err,
|
|
6
|
+
isVaultSyncKey,
|
|
6
7
|
ok
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-Y6KRDGI2.js";
|
|
8
9
|
|
|
9
10
|
// src/utils/dotenv.ts
|
|
10
11
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -40,7 +41,7 @@ function parseDotenvText(text) {
|
|
|
40
41
|
if (eq <= 0) continue;
|
|
41
42
|
const key = line.slice(0, eq).trim();
|
|
42
43
|
const value = line.slice(eq + 1).trim();
|
|
43
|
-
if (!_whitelist.has(key) && !isValidWikiProfileKey(key)) continue;
|
|
44
|
+
if (!_whitelist.has(key) && !isValidWikiProfileKey(key) && !isVaultSyncKey(key)) continue;
|
|
44
45
|
if (value.length === 0) continue;
|
|
45
46
|
out[key] = value;
|
|
46
47
|
}
|
|
@@ -866,6 +867,45 @@ async function runVaultSyncPullHelper(input) {
|
|
|
866
867
|
});
|
|
867
868
|
}
|
|
868
869
|
|
|
870
|
+
// src/utils/snapshot-worktree.ts
|
|
871
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
872
|
+
import { join as join4, resolve } from "path";
|
|
873
|
+
function readSkillWikiConfig(path) {
|
|
874
|
+
try {
|
|
875
|
+
return parseDotenvText(readFileSync2(path, "utf8"));
|
|
876
|
+
} catch {
|
|
877
|
+
return {};
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
function readSnapshotProfile(path) {
|
|
881
|
+
const allowed = /* @__PURE__ */ new Set(["WIKI_GIT_WORKTREE", "SNAPSHOT_WORKTREE", "GIT_DIR"]);
|
|
882
|
+
const profile = {};
|
|
883
|
+
try {
|
|
884
|
+
for (const rawLine of readFileSync2(path, "utf8").split(/\r?\n/)) {
|
|
885
|
+
const line = rawLine.trim();
|
|
886
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
887
|
+
const eq = line.indexOf("=");
|
|
888
|
+
if (eq <= 0) continue;
|
|
889
|
+
const key = line.slice(0, eq).trim();
|
|
890
|
+
const value = line.slice(eq + 1).trim();
|
|
891
|
+
if (allowed.has(key) && value.length > 0) profile[key] = value;
|
|
892
|
+
}
|
|
893
|
+
} catch {
|
|
894
|
+
}
|
|
895
|
+
return profile;
|
|
896
|
+
}
|
|
897
|
+
function resolveConfiguredSnapshotWorktree(home) {
|
|
898
|
+
if (!home) return void 0;
|
|
899
|
+
const config = readSkillWikiConfig(join4(home, ".skillwiki", ".env"));
|
|
900
|
+
const explicit = config["vault_sync.snapshot_worktree"];
|
|
901
|
+
if (explicit) return resolve(explicit);
|
|
902
|
+
const snapshotProfile = config["vault_sync.snapshot_profile"];
|
|
903
|
+
if (!snapshotProfile) return void 0;
|
|
904
|
+
const profile = readSnapshotProfile(resolve(snapshotProfile));
|
|
905
|
+
const fromProfile = profile.WIKI_GIT_WORKTREE ?? profile.SNAPSHOT_WORKTREE ?? profile.GIT_DIR;
|
|
906
|
+
return fromProfile ? resolve(fromProfile) : void 0;
|
|
907
|
+
}
|
|
908
|
+
|
|
869
909
|
export {
|
|
870
910
|
CONFIG_KEYS,
|
|
871
911
|
isValidWikiProfileKey,
|
|
@@ -896,5 +936,6 @@ export {
|
|
|
896
936
|
markJournalSuperseded,
|
|
897
937
|
supersedeStaleReviewRequiredJournals,
|
|
898
938
|
resolveVaultSyncPullHelper,
|
|
899
|
-
runVaultSyncPullHelper
|
|
939
|
+
runVaultSyncPullHelper,
|
|
940
|
+
resolveConfiguredSnapshotWorktree
|
|
900
941
|
};
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
splitFrontmatter,
|
|
20
20
|
vaultIoConcurrency,
|
|
21
21
|
writeRootIndexProjection
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-IGCW3DR2.js";
|
|
23
23
|
import {
|
|
24
24
|
CONFIG_KEYS,
|
|
25
25
|
git,
|
|
@@ -29,11 +29,12 @@ import {
|
|
|
29
29
|
parseDotenvFile,
|
|
30
30
|
parseDotenvText,
|
|
31
31
|
profileKey,
|
|
32
|
+
resolveConfiguredSnapshotWorktree,
|
|
32
33
|
resolveVaultSyncPullHelper,
|
|
33
34
|
satelliteGateFromFleetLoad,
|
|
34
35
|
snapshotterAliasForLocalHost,
|
|
35
36
|
writeDotenv
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-GNS2ZV5P.js";
|
|
37
38
|
import {
|
|
38
39
|
CompoundSchema,
|
|
39
40
|
ExitCode,
|
|
@@ -44,8 +45,11 @@ import {
|
|
|
44
45
|
detectSchema,
|
|
45
46
|
err,
|
|
46
47
|
getErrorMessage,
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
isVaultSyncKey,
|
|
49
|
+
ok,
|
|
50
|
+
parseVaultSyncKeyValue,
|
|
51
|
+
systemdPropertyFor
|
|
52
|
+
} from "./chunk-Y6KRDGI2.js";
|
|
49
53
|
|
|
50
54
|
// src/commands/log-append.ts
|
|
51
55
|
import { readFile as readFile2, stat } from "fs/promises";
|
|
@@ -4774,7 +4778,7 @@ import { readFile as readFile15 } from "fs/promises";
|
|
|
4774
4778
|
import { existsSync as existsSync7 } from "fs";
|
|
4775
4779
|
import { join as join19 } from "path";
|
|
4776
4780
|
function validateKey(key) {
|
|
4777
|
-
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
4781
|
+
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key) || isVaultSyncKey(key);
|
|
4778
4782
|
}
|
|
4779
4783
|
function configPath(home) {
|
|
4780
4784
|
return join19(home, ".skillwiki", ".env");
|
|
@@ -4791,6 +4795,12 @@ async function runConfigSet(input) {
|
|
|
4791
4795
|
if (!validateKey(input.key)) {
|
|
4792
4796
|
return { exitCode: ExitCode.INVALID_CONFIG_KEY, result: err("INVALID_CONFIG_KEY", { key: input.key }) };
|
|
4793
4797
|
}
|
|
4798
|
+
if (isVaultSyncKey(input.key)) {
|
|
4799
|
+
const parsed = parseVaultSyncKeyValue(input.key, input.value);
|
|
4800
|
+
if (!parsed.ok) {
|
|
4801
|
+
return { exitCode: ExitCode.INVALID_CONFIG_VALUE, result: err("INVALID_CONFIG_VALUE", { key: input.key, value: input.value, message: parsed.message }) };
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4794
4804
|
const filePath = configPath(input.home);
|
|
4795
4805
|
try {
|
|
4796
4806
|
let originalContent;
|
|
@@ -6473,8 +6483,6 @@ function readVaultSyncConfig(home) {
|
|
|
6473
6483
|
let role;
|
|
6474
6484
|
let serviceScope;
|
|
6475
6485
|
let snapshotScript;
|
|
6476
|
-
let snapshotProfile;
|
|
6477
|
-
let snapshotWorktree;
|
|
6478
6486
|
for (const line of content.split(/\r?\n/)) {
|
|
6479
6487
|
const trimmed = line.trim();
|
|
6480
6488
|
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
@@ -6487,58 +6495,18 @@ function readVaultSyncConfig(home) {
|
|
|
6487
6495
|
if (k === "vault_sync.role") role = v;
|
|
6488
6496
|
if (k === "vault_sync.service_scope") serviceScope = v;
|
|
6489
6497
|
if (k === "vault_sync.snapshot_script") snapshotScript = v;
|
|
6490
|
-
if (k === "vault_sync.snapshot_profile") snapshotProfile = v;
|
|
6491
|
-
if (k === "vault_sync.snapshot_worktree") snapshotWorktree = v;
|
|
6492
6498
|
}
|
|
6493
|
-
return { installed, role, serviceScope, snapshotScript
|
|
6499
|
+
return { installed, role, serviceScope, snapshotScript };
|
|
6494
6500
|
} catch {
|
|
6495
6501
|
return { installed: false };
|
|
6496
6502
|
}
|
|
6497
6503
|
}
|
|
6498
|
-
function
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
for (const line of content.split(/\r?\n/)) {
|
|
6502
|
-
const trimmed = line.trim();
|
|
6503
|
-
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
6504
|
-
const eq = trimmed.indexOf("=");
|
|
6505
|
-
if (eq <= 0) continue;
|
|
6506
|
-
const key = trimmed.slice(0, eq).trim();
|
|
6507
|
-
if (!keys.includes(key)) continue;
|
|
6508
|
-
const value = trimmed.slice(eq + 1).trim();
|
|
6509
|
-
if (value.length > 0) return value;
|
|
6510
|
-
}
|
|
6511
|
-
} catch {
|
|
6512
|
-
}
|
|
6513
|
-
return void 0;
|
|
6514
|
-
}
|
|
6515
|
-
function resolveSnapshotGitWorktree(config) {
|
|
6516
|
-
if (config.snapshotWorktree) return config.snapshotWorktree;
|
|
6517
|
-
if (config.snapshotProfile) {
|
|
6518
|
-
const fromProfile = readKeyFromEnvFile(config.snapshotProfile, ["WIKI_GIT_WORKTREE", "SNAPSHOT_WORKTREE", "GIT_DIR"]);
|
|
6519
|
-
if (fromProfile) return fromProfile;
|
|
6520
|
-
}
|
|
6504
|
+
function resolveSnapshotGitWorktree(home) {
|
|
6505
|
+
const configured = resolveConfiguredSnapshotWorktree(home);
|
|
6506
|
+
if (configured) return configured;
|
|
6521
6507
|
const defaultPath = "/root/wiki-git";
|
|
6522
6508
|
return existsSync13(defaultPath) ? defaultPath : void 0;
|
|
6523
6509
|
}
|
|
6524
|
-
var SNAPSHOT_SEMANTIC_TO_SYSTEMD = {
|
|
6525
|
-
load_state: "LoadState",
|
|
6526
|
-
unit_file_state: "UnitFileState",
|
|
6527
|
-
active_state: "ActiveState",
|
|
6528
|
-
sub_state: "SubState",
|
|
6529
|
-
next_elapse: "NextElapseUSecRealtime",
|
|
6530
|
-
result: "Result",
|
|
6531
|
-
exec_main_status: "ExecMainStatus",
|
|
6532
|
-
exec_main_code: "ExecMainCode",
|
|
6533
|
-
active_enter_timestamp: "ActiveEnterTimestamp",
|
|
6534
|
-
inactive_enter_timestamp: "InactiveEnterTimestamp",
|
|
6535
|
-
exec_main_start_timestamp: "ExecMainStartTimestamp",
|
|
6536
|
-
exec_main_exit_timestamp: "ExecMainExitTimestamp",
|
|
6537
|
-
invocation_id: "InvocationID"
|
|
6538
|
-
};
|
|
6539
|
-
function semanticToSystemdProp(semantic) {
|
|
6540
|
-
return SNAPSHOT_SEMANTIC_TO_SYSTEMD[semantic];
|
|
6541
|
-
}
|
|
6542
6510
|
function normalizeSystemdValue(raw) {
|
|
6543
6511
|
if (raw == null) return void 0;
|
|
6544
6512
|
const v = raw.trim();
|
|
@@ -6577,7 +6545,7 @@ function snapshotProp(kind, prop, fixture, scope) {
|
|
|
6577
6545
|
return v == null ? void 0 : String(v);
|
|
6578
6546
|
}
|
|
6579
6547
|
const unit = kind === "timer" ? "wiki-snapshot.timer" : "wiki-snapshot.service";
|
|
6580
|
-
const liveProp =
|
|
6548
|
+
const liveProp = systemdPropertyFor(prop);
|
|
6581
6549
|
if (!liveProp) return void 0;
|
|
6582
6550
|
return systemctlShowProperty(scope, unit, liveProp);
|
|
6583
6551
|
}
|
|
@@ -6594,7 +6562,8 @@ function snapshotterHealthChecks(scope, logDir, env) {
|
|
|
6594
6562
|
const fixture = loadSnapshotFixture(env);
|
|
6595
6563
|
const cadence = fixture ? fixture.cadence_minutes : parseInt(env.VS_SNAPSHOT_CADENCE_MINUTES ?? "30", 10) || 30;
|
|
6596
6564
|
const timeout = fixture ? fixture.service_timeout_seconds : parseInt(env.VS_SNAPSHOT_SERVICE_TIMEOUT_SECONDS ?? "900", 10) || 900;
|
|
6597
|
-
const
|
|
6565
|
+
const injectedNowMs = env.VS_SNAPSHOT_HEALTH_NOW ? Date.parse(env.VS_SNAPSHOT_HEALTH_NOW) : Number.NaN;
|
|
6566
|
+
const nowMs = fixture ? Date.parse(fixture.now) : Number.isFinite(injectedNowMs) ? injectedNowMs : Date.now();
|
|
6598
6567
|
const warnAge = cadence * 2 + 15;
|
|
6599
6568
|
const errorAge = cadence * 4 + 15;
|
|
6600
6569
|
const tUnitfile = snapshotProp("timer", "unit_file_state", fixture, scope);
|
|
@@ -6781,7 +6750,7 @@ function vaultSyncChecks(input) {
|
|
|
6781
6750
|
execSync2(`launchctl print gui/${uid}/com.karlchow.wiki-push`, {
|
|
6782
6751
|
encoding: "utf8",
|
|
6783
6752
|
timeout: 2e3,
|
|
6784
|
-
stdio: ["pipe", "
|
|
6753
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
6785
6754
|
});
|
|
6786
6755
|
c2 = check(
|
|
6787
6756
|
"pass",
|
|
@@ -7134,7 +7103,7 @@ async function runDoctor(input) {
|
|
|
7134
7103
|
checks.push(check("error", "wiki_path_set", "WIKI_PATH configured", "No vault configured. Run `skillwiki init` or pass --vault."));
|
|
7135
7104
|
}
|
|
7136
7105
|
const resolvedPath = resolved.ok ? resolved.data.path : void 0;
|
|
7137
|
-
const gitCheckPath = vsConfig.role === "snapshotter" ? resolveSnapshotGitWorktree(
|
|
7106
|
+
const gitCheckPath = vsConfig.role === "snapshotter" ? resolveSnapshotGitWorktree(input.home) ?? resolvedPath : resolvedPath;
|
|
7138
7107
|
checks.push(checkWikiPathExists(resolvedPath));
|
|
7139
7108
|
checks.push(checkVaultStructure(resolvedPath));
|
|
7140
7109
|
checks.push(checkObsidianTemplates(resolvedPath));
|
|
@@ -6,17 +6,18 @@ import {
|
|
|
6
6
|
hasActiveGitSequencer,
|
|
7
7
|
hasUnmergedPaths,
|
|
8
8
|
loadFleetManifestAndHost,
|
|
9
|
+
resolveConfiguredSnapshotWorktree,
|
|
9
10
|
runVaultSyncPullHelper,
|
|
10
11
|
supersedeStaleReviewRequiredJournals
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GNS2ZV5P.js";
|
|
12
13
|
import {
|
|
13
14
|
ExitCode,
|
|
14
15
|
err,
|
|
15
16
|
ok
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-Y6KRDGI2.js";
|
|
17
18
|
|
|
18
19
|
// src/utils/managed-write-preflight.ts
|
|
19
|
-
import { existsSync as existsSync2
|
|
20
|
+
import { existsSync as existsSync2 } from "fs";
|
|
20
21
|
import { join as join2, resolve } from "path";
|
|
21
22
|
|
|
22
23
|
// src/utils/managed-write-lock.ts
|
|
@@ -149,7 +150,8 @@ function releaseManagedWriteLock(handle) {
|
|
|
149
150
|
|
|
150
151
|
// src/utils/managed-write-preflight.ts
|
|
151
152
|
var DEFAULT_DEPS = {
|
|
152
|
-
converge: (input) => runVaultSyncPullHelper(input)
|
|
153
|
+
converge: (input) => runVaultSyncPullHelper(input),
|
|
154
|
+
resolveConfiguredSnapshotWorktree
|
|
153
155
|
};
|
|
154
156
|
function preflightBlocker(vault) {
|
|
155
157
|
const unmerged = hasUnmergedPaths(vault);
|
|
@@ -171,19 +173,17 @@ function preflightBlocker(vault) {
|
|
|
171
173
|
if (op) return { reason: "review-required", operation_id: op };
|
|
172
174
|
return null;
|
|
173
175
|
}
|
|
174
|
-
function
|
|
175
|
-
|
|
176
|
-
if (!existsSync2(path)) return null;
|
|
177
|
-
return readFileSync2(path);
|
|
176
|
+
function hasFleetManifest(vault) {
|
|
177
|
+
return existsSync2(join2(vault, FLEET_REL_PATH));
|
|
178
178
|
}
|
|
179
179
|
function isGitVault(vault) {
|
|
180
180
|
return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
|
|
181
181
|
}
|
|
182
182
|
async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const mutationBlocker = preflightBlocker(
|
|
183
|
+
const mutationVault = resolve(input.vault);
|
|
184
|
+
let convergenceVault = input.convergenceVault && resolve(input.convergenceVault) !== mutationVault ? resolve(input.convergenceVault) : void 0;
|
|
185
|
+
let convergenceSource = convergenceVault ? "explicit" : "single-path";
|
|
186
|
+
const mutationBlocker = preflightBlocker(mutationVault);
|
|
187
187
|
if (mutationBlocker) {
|
|
188
188
|
return {
|
|
189
189
|
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
@@ -194,46 +194,27 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
194
194
|
})
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
|
-
if (convergenceVault) {
|
|
198
|
-
if (!isGitVault(convergenceVault)) {
|
|
199
|
-
return {
|
|
200
|
-
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
201
|
-
result: err("PREFLIGHT_FAILED", {
|
|
202
|
-
reason: "convergence-vault-not-git",
|
|
203
|
-
convergence_vault: convergenceVault
|
|
204
|
-
})
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const gitBlocker = preflightBlocker(convergenceVault);
|
|
208
|
-
if (gitBlocker) {
|
|
209
|
-
return {
|
|
210
|
-
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
211
|
-
result: err("PREFLIGHT_FAILED", {
|
|
212
|
-
reason: gitBlocker.reason,
|
|
213
|
-
operation_id: gitBlocker.operation_id,
|
|
214
|
-
unmerged_paths: gitBlocker.unmerged_paths,
|
|
215
|
-
convergence_vault: convergenceVault
|
|
216
|
-
})
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
197
|
const fleet = await loadFleetManifestAndHost({
|
|
221
|
-
vault,
|
|
198
|
+
vault: mutationVault,
|
|
222
199
|
hostId: input.hostId,
|
|
223
200
|
env: input.env,
|
|
224
201
|
home: input.home,
|
|
225
|
-
|
|
202
|
+
cwd: input.cwd,
|
|
203
|
+
osHostname: input.osHostname,
|
|
204
|
+
user: input.user
|
|
226
205
|
});
|
|
227
|
-
const dualPathMeta = convergenceVault ? { convergence_vault: convergenceVault } : {};
|
|
228
206
|
if (!fleet) {
|
|
229
|
-
const
|
|
207
|
+
const gitVault2 = isGitVault(mutationVault) ? mutationVault : null;
|
|
208
|
+
const head = gitVault2 ? git(gitVault2, ["rev-parse", "HEAD"]) || null : null;
|
|
230
209
|
return {
|
|
231
210
|
exitCode: ExitCode.OK,
|
|
232
211
|
result: ok({
|
|
233
212
|
mode: "standalone",
|
|
213
|
+
mutation_vault: mutationVault,
|
|
214
|
+
git_vault: gitVault2,
|
|
234
215
|
base_oid: head,
|
|
235
216
|
converged: false,
|
|
236
|
-
|
|
217
|
+
convergence_source: "single-path"
|
|
237
218
|
})
|
|
238
219
|
};
|
|
239
220
|
}
|
|
@@ -247,27 +228,6 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
247
228
|
})
|
|
248
229
|
};
|
|
249
230
|
}
|
|
250
|
-
if (convergenceVault && fleetManifestBytes(convergenceVault)) {
|
|
251
|
-
const convergeFleetCtx = await loadFleetManifestAndHost({
|
|
252
|
-
vault: convergenceVault,
|
|
253
|
-
hostId: input.hostId ?? fleet.hostId,
|
|
254
|
-
env: input.env,
|
|
255
|
-
home: input.home,
|
|
256
|
-
osHostname: input.osHostname
|
|
257
|
-
});
|
|
258
|
-
if (!convergeFleetCtx || convergeFleetCtx.identityStatus !== "known" || convergeFleetCtx.hostId !== fleet.hostId) {
|
|
259
|
-
return {
|
|
260
|
-
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
261
|
-
result: err("PREFLIGHT_FAILED", {
|
|
262
|
-
reason: "convergence-vault-identity-mismatch",
|
|
263
|
-
host_id: fleet.hostId,
|
|
264
|
-
convergence_host_id: convergeFleetCtx?.hostId,
|
|
265
|
-
convergence_identity_status: convergeFleetCtx?.identityStatus,
|
|
266
|
-
convergence_vault: convergenceVault
|
|
267
|
-
})
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
231
|
const host = fleet.manifest.hosts[fleet.hostId];
|
|
272
232
|
if (!host) {
|
|
273
233
|
return {
|
|
@@ -282,12 +242,101 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
282
242
|
result: ok({
|
|
283
243
|
mode: "immutable-record",
|
|
284
244
|
host_id: fleet.hostId,
|
|
245
|
+
mutation_vault: mutationVault,
|
|
246
|
+
git_vault: null,
|
|
285
247
|
base_oid: null,
|
|
286
248
|
converged: false,
|
|
287
|
-
...
|
|
249
|
+
...convergenceVault ? { convergence_vault: convergenceVault } : {},
|
|
250
|
+
convergence_source: convergenceSource
|
|
288
251
|
})
|
|
289
252
|
};
|
|
290
253
|
}
|
|
254
|
+
if (!convergenceVault && host.role === "snapshotter" && host.protected === true) {
|
|
255
|
+
const home = input.home ?? input.env?.HOME ?? process.env.HOME ?? "";
|
|
256
|
+
const configured = (deps.resolveConfiguredSnapshotWorktree ?? resolveConfiguredSnapshotWorktree)(home);
|
|
257
|
+
if (!configured) {
|
|
258
|
+
return {
|
|
259
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
260
|
+
result: err("PREFLIGHT_FAILED", {
|
|
261
|
+
reason: "convergence-vault-not-configured",
|
|
262
|
+
host_id: fleet.hostId,
|
|
263
|
+
mutation_vault: mutationVault
|
|
264
|
+
})
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
convergenceVault = resolve(configured);
|
|
268
|
+
convergenceSource = "configured";
|
|
269
|
+
if (convergenceVault === mutationVault) {
|
|
270
|
+
return {
|
|
271
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
272
|
+
result: err("PREFLIGHT_FAILED", {
|
|
273
|
+
reason: "convergence-vault-not-distinct",
|
|
274
|
+
host_id: fleet.hostId,
|
|
275
|
+
mutation_vault: mutationVault,
|
|
276
|
+
convergence_vault: convergenceVault
|
|
277
|
+
})
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const gitVault = convergenceVault ?? mutationVault;
|
|
282
|
+
if (convergenceVault) {
|
|
283
|
+
if (!isGitVault(convergenceVault)) {
|
|
284
|
+
return {
|
|
285
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
286
|
+
result: err("PREFLIGHT_FAILED", {
|
|
287
|
+
reason: "convergence-vault-not-git",
|
|
288
|
+
convergence_vault: convergenceVault
|
|
289
|
+
})
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const convergenceHasFleet = hasFleetManifest(convergenceVault);
|
|
293
|
+
if (convergenceSource === "configured" && !convergenceHasFleet) {
|
|
294
|
+
return {
|
|
295
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
296
|
+
result: err("PREFLIGHT_FAILED", {
|
|
297
|
+
reason: "convergence-vault-fleet-missing",
|
|
298
|
+
host_id: fleet.hostId,
|
|
299
|
+
convergence_vault: convergenceVault
|
|
300
|
+
})
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const gitBlocker = preflightBlocker(convergenceVault);
|
|
304
|
+
if (gitBlocker) {
|
|
305
|
+
return {
|
|
306
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
307
|
+
result: err("PREFLIGHT_FAILED", {
|
|
308
|
+
reason: gitBlocker.reason,
|
|
309
|
+
operation_id: gitBlocker.operation_id,
|
|
310
|
+
unmerged_paths: gitBlocker.unmerged_paths,
|
|
311
|
+
convergence_vault: convergenceVault
|
|
312
|
+
})
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (convergenceVault && hasFleetManifest(convergenceVault)) {
|
|
317
|
+
const convergeFleetCtx = await loadFleetManifestAndHost({
|
|
318
|
+
vault: convergenceVault,
|
|
319
|
+
hostId: input.hostId ?? fleet.hostId,
|
|
320
|
+
env: input.env,
|
|
321
|
+
home: input.home,
|
|
322
|
+
cwd: input.cwd,
|
|
323
|
+
osHostname: input.osHostname,
|
|
324
|
+
user: input.user
|
|
325
|
+
});
|
|
326
|
+
if (!convergeFleetCtx || convergeFleetCtx.identityStatus !== "known" || convergeFleetCtx.hostId !== fleet.hostId) {
|
|
327
|
+
return {
|
|
328
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
329
|
+
result: err("PREFLIGHT_FAILED", {
|
|
330
|
+
reason: "convergence-vault-identity-mismatch",
|
|
331
|
+
host_id: fleet.hostId,
|
|
332
|
+
convergence_host_id: convergeFleetCtx?.hostId,
|
|
333
|
+
convergence_identity_status: convergeFleetCtx?.identityStatus,
|
|
334
|
+
convergence_vault: convergenceVault
|
|
335
|
+
})
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const dualPathMeta = convergenceVault ? { convergence_vault: convergenceVault } : {};
|
|
291
340
|
const converge = await deps.converge({
|
|
292
341
|
vault: gitVault,
|
|
293
342
|
lockToken: convergenceVault ? void 0 : input.lockToken,
|
|
@@ -313,37 +362,51 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
313
362
|
result: ok({
|
|
314
363
|
mode: "git-writer",
|
|
315
364
|
host_id: fleet.hostId,
|
|
365
|
+
mutation_vault: mutationVault,
|
|
366
|
+
git_vault: gitVault,
|
|
316
367
|
base_oid: baseOid,
|
|
317
368
|
converged: true,
|
|
318
369
|
helper_path: converge.data.helper_path,
|
|
319
|
-
...dualPathMeta
|
|
370
|
+
...dualPathMeta,
|
|
371
|
+
convergence_source: convergenceSource
|
|
320
372
|
})
|
|
321
373
|
};
|
|
322
374
|
}
|
|
323
375
|
async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
|
|
324
|
-
const
|
|
376
|
+
const mutationVault = resolve(input.vault);
|
|
377
|
+
const lock = acquireManagedWriteLock(mutationVault, input.command);
|
|
325
378
|
if (!lock.ok) {
|
|
326
379
|
return { exitCode: ExitCode.SYNC_LOCK_HELD, result: lock };
|
|
327
380
|
}
|
|
328
|
-
|
|
381
|
+
const handle = lock.data;
|
|
329
382
|
try {
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
);
|
|
383
|
+
const preflightInput = {
|
|
384
|
+
vault: mutationVault,
|
|
385
|
+
command: input.command,
|
|
386
|
+
convergenceVault: input.convergenceVault,
|
|
387
|
+
hostId: input.hostId,
|
|
388
|
+
lockToken: handle.ownerToken,
|
|
389
|
+
env: input.env,
|
|
390
|
+
home: input.home,
|
|
391
|
+
cwd: input.cwd,
|
|
392
|
+
osHostname: input.osHostname,
|
|
393
|
+
user: input.user
|
|
394
|
+
};
|
|
395
|
+
const preflight = input.preflight ? await input.preflight(preflightInput) : await runManagedWritePreflight(preflightInput, deps);
|
|
343
396
|
if (!preflight.result.ok) {
|
|
344
397
|
return { exitCode: preflight.exitCode, result: preflight.result };
|
|
345
398
|
}
|
|
346
399
|
const receipt = preflight.result.data;
|
|
400
|
+
if (receipt.mutation_vault !== mutationVault) {
|
|
401
|
+
return {
|
|
402
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
403
|
+
result: err("PREFLIGHT_FAILED", {
|
|
404
|
+
reason: "mutation-vault-receipt-mismatch",
|
|
405
|
+
expected: mutationVault,
|
|
406
|
+
actual: receipt.mutation_vault
|
|
407
|
+
})
|
|
408
|
+
};
|
|
409
|
+
}
|
|
347
410
|
if (receipt.mode === "immutable-record" && !input.allowImmutableRecord) {
|
|
348
411
|
return {
|
|
349
412
|
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
@@ -361,8 +424,6 @@ async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
|
|
|
361
424
|
}
|
|
362
425
|
|
|
363
426
|
export {
|
|
364
|
-
acquireManagedWriteLock,
|
|
365
|
-
releaseManagedWriteLock,
|
|
366
427
|
runManagedWritePreflight,
|
|
367
428
|
runManagedWriteTransaction
|
|
368
429
|
};
|
|
@@ -55,7 +55,8 @@ var ExitCode = {
|
|
|
55
55
|
FLEET_MANIFEST_INVALID: 50,
|
|
56
56
|
SENSITIVE_CONTENT_DETECTED: 51,
|
|
57
57
|
FLEET_SATELLITE_HEALTH_FAILED: 52,
|
|
58
|
-
PROTECTED_SNAPSHOTTER_WRITE_BLOCKED: 53
|
|
58
|
+
PROTECTED_SNAPSHOTTER_WRITE_BLOCKED: 53,
|
|
59
|
+
INVALID_CONFIG_VALUE: 54
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
// ../shared/src/json-output.ts
|
|
@@ -285,6 +286,47 @@ var FleetManifestSchema = z.object({
|
|
|
285
286
|
}
|
|
286
287
|
}
|
|
287
288
|
});
|
|
289
|
+
var vaultSyncRole = z.enum(["leaf", "snapshotter", "none"]);
|
|
290
|
+
var vaultSyncScheduler = z.enum(["systemd", "launchd", "none"]);
|
|
291
|
+
var vaultSyncScope = z.enum(["user", "system", "none"]);
|
|
292
|
+
var vaultSyncBool = z.enum(["true", "false"]);
|
|
293
|
+
var vaultSyncDuration = z.union([
|
|
294
|
+
z.string().regex(/^[1-9][0-9]*[mhs]$/, "must be <positive-int>m|h|s (e.g. 15m, 300s) or none"),
|
|
295
|
+
z.literal("none")
|
|
296
|
+
]);
|
|
297
|
+
var vaultSyncPathOrNone = z.union([
|
|
298
|
+
absolutePath,
|
|
299
|
+
z.literal("none")
|
|
300
|
+
]);
|
|
301
|
+
var VaultSyncConfigSchema = z.object({
|
|
302
|
+
"vault_sync.installed": vaultSyncBool,
|
|
303
|
+
"vault_sync.role": vaultSyncRole,
|
|
304
|
+
"vault_sync.scheduler": vaultSyncScheduler,
|
|
305
|
+
"vault_sync.service_scope": vaultSyncScope,
|
|
306
|
+
"vault_sync.snapshot_profile": vaultSyncPathOrNone,
|
|
307
|
+
"vault_sync.snapshot_script": vaultSyncPathOrNone,
|
|
308
|
+
"vault_sync.snapshot_worktree": vaultSyncPathOrNone,
|
|
309
|
+
"vault_sync.fuse_refresh_enabled": vaultSyncBool,
|
|
310
|
+
"vault_sync.fuse_refresh_interval": vaultSyncDuration,
|
|
311
|
+
"vault_sync.fuse_max_dir_cache": vaultSyncDuration,
|
|
312
|
+
"vault_sync.fuse_service_scope": vaultSyncScope
|
|
313
|
+
}).strict();
|
|
314
|
+
var VAULT_SYNC_KEYS = Object.keys(VaultSyncConfigSchema.shape);
|
|
315
|
+
function isVaultSyncKey(key) {
|
|
316
|
+
return VAULT_SYNC_KEYS.includes(key);
|
|
317
|
+
}
|
|
318
|
+
function parseVaultSyncKeyValue(key, value) {
|
|
319
|
+
if (!isVaultSyncKey(key)) {
|
|
320
|
+
return { ok: false, message: `unknown vault_sync key: ${key}` };
|
|
321
|
+
}
|
|
322
|
+
const fieldSchema = VaultSyncConfigSchema.shape[key];
|
|
323
|
+
const parsed = fieldSchema.safeParse(value);
|
|
324
|
+
if (parsed.success) {
|
|
325
|
+
return { ok: true };
|
|
326
|
+
}
|
|
327
|
+
const first = parsed.error.issues[0];
|
|
328
|
+
return { ok: false, message: first?.message ?? "invalid value" };
|
|
329
|
+
}
|
|
288
330
|
function detectSchema(fm) {
|
|
289
331
|
const COMPOUND_TYPES = /* @__PURE__ */ new Set(["lesson", "pattern", "antipattern", "gotcha"]);
|
|
290
332
|
if (typeof fm.type === "string" && COMPOUND_TYPES.has(fm.type) && "project" in fm) return { schema: "compound" };
|
|
@@ -341,6 +383,29 @@ function getErrorMessage(e) {
|
|
|
341
383
|
return e instanceof Error ? e.message : String(e);
|
|
342
384
|
}
|
|
343
385
|
|
|
386
|
+
// ../shared/src/systemd-property-catalog.json
|
|
387
|
+
var systemd_property_catalog_default = {
|
|
388
|
+
load_state: "LoadState",
|
|
389
|
+
unit_file_state: "UnitFileState",
|
|
390
|
+
active_state: "ActiveState",
|
|
391
|
+
sub_state: "SubState",
|
|
392
|
+
next_elapse: "NextElapseUSecRealtime",
|
|
393
|
+
result: "Result",
|
|
394
|
+
exec_main_status: "ExecMainStatus",
|
|
395
|
+
exec_main_code: "ExecMainCode",
|
|
396
|
+
active_enter_timestamp: "ActiveEnterTimestamp",
|
|
397
|
+
inactive_enter_timestamp: "InactiveEnterTimestamp",
|
|
398
|
+
exec_main_start_timestamp: "ExecMainStartTimestamp",
|
|
399
|
+
exec_main_exit_timestamp: "ExecMainExitTimestamp",
|
|
400
|
+
invocation_id: "InvocationID"
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// ../shared/src/systemd-property-catalog.ts
|
|
404
|
+
var SYSTEMD_PROPERTY_CATALOG = Object.freeze({ ...systemd_property_catalog_default });
|
|
405
|
+
function systemdPropertyFor(semantic) {
|
|
406
|
+
return SYSTEMD_PROPERTY_CATALOG[semantic];
|
|
407
|
+
}
|
|
408
|
+
|
|
344
409
|
export {
|
|
345
410
|
ExitCode,
|
|
346
411
|
ok,
|
|
@@ -351,7 +416,10 @@ export {
|
|
|
351
416
|
CompoundSchema,
|
|
352
417
|
MetaSchema,
|
|
353
418
|
FleetManifestSchema,
|
|
419
|
+
isVaultSyncKey,
|
|
420
|
+
parseVaultSyncKeyValue,
|
|
354
421
|
detectSchema,
|
|
355
422
|
isBlockedHost,
|
|
356
|
-
getErrorMessage
|
|
423
|
+
getErrorMessage,
|
|
424
|
+
systemdPropertyFor
|
|
357
425
|
};
|