@kontextmind/kxm 0.7.86 → 0.7.88
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/.claude-plugin/marketplace.json +1 -1
- package/docs/configuration.md +2 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +897 -500
- package/plugins/kxm/dist/core.js +2 -2
- package/plugins/kxm/dist/extension.js +72 -63
- package/plugins/kxm/dist/mcp-server.js +3 -3
- package/plugins/kxm/dist/runtime.js +44 -26
- package/plugins/kxm/dist/server.js +56 -31
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/context-skills.ts +51 -6
- package/plugins/kxm/src/cli/hub.ts +39 -14
- package/plugins/kxm/src/cli/project.ts +78 -23
- package/plugins/kxm/src/cli/roles.ts +48 -4
- package/plugins/kxm/src/cli/system.ts +18 -1
- package/plugins/kxm/src/cli/tasks.ts +34 -5
- package/plugins/kxm/src/cli/types.ts +30 -0
- package/plugins/kxm/src/cli/workflows.ts +20 -4
- package/plugins/kxm/src/cli.ts +57 -0
- package/plugins/kxm/src/commands.ts +2 -2
- package/plugins/kxm/src/config.ts +4 -3
- package/plugins/kxm/src/context/providers.ts +4 -0
- package/plugins/kxm/src/database.ts +72 -33
- package/plugins/kxm/src/hub.ts +26 -8
- package/plugins/kxm/src/local-snapshot.ts +4 -4
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/memory.ts +10 -7
- package/plugins/kxm/src/role.ts +18 -6
- package/plugins/kxm/src/session-work.ts +4 -1
- package/plugins/kxm/src/skills.ts +29 -15
- package/plugins/kxm/src/sqlite.ts +18 -0
- package/plugins/kxm/src/state.ts +4 -1
- package/plugins/kxm/src/task-manager.ts +20 -13
- package/plugins/kxm/src/workflow-manager.ts +12 -4
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
|
|
|
2987
2987
|
const schOrFunc = root.refs[ref];
|
|
2988
2988
|
if (schOrFunc)
|
|
2989
2989
|
return schOrFunc;
|
|
2990
|
-
let _sch =
|
|
2990
|
+
let _sch = resolve32.call(this, root, ref);
|
|
2991
2991
|
if (_sch === void 0) {
|
|
2992
2992
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2993
2993
|
const { schemaId } = this.opts;
|
|
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
|
|
|
3014
3014
|
function sameSchemaEnv(s1, s2) {
|
|
3015
3015
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3016
3016
|
}
|
|
3017
|
-
function
|
|
3017
|
+
function resolve32(root, ref) {
|
|
3018
3018
|
let sch;
|
|
3019
3019
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3020
3020
|
ref = sch;
|
|
@@ -3839,7 +3839,7 @@ var require_fast_uri = __commonJS({
|
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
3842
|
-
function
|
|
3842
|
+
function resolve32(baseURI, relativeURI, options) {
|
|
3843
3843
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3844
3844
|
const {
|
|
3845
3845
|
parsed: baseParsed,
|
|
@@ -4201,7 +4201,7 @@ var require_fast_uri = __commonJS({
|
|
|
4201
4201
|
var fastUri = {
|
|
4202
4202
|
SCHEMES,
|
|
4203
4203
|
normalize,
|
|
4204
|
-
resolve:
|
|
4204
|
+
resolve: resolve32,
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
|
|
|
14779
14779
|
});
|
|
14780
14780
|
|
|
14781
14781
|
// plugins/kxm/src/cli.ts
|
|
14782
|
-
import { resolve as
|
|
14782
|
+
import { resolve as resolve31 } from "node:path";
|
|
14783
14783
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
14784
14784
|
|
|
14785
14785
|
// node_modules/commander/lib/error.js
|
|
@@ -19501,7 +19501,7 @@ var HubClient = class {
|
|
|
19501
19501
|
if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
|
|
19502
19502
|
const message = await this.getMessage(messageId);
|
|
19503
19503
|
if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
|
|
19504
|
-
await new Promise((
|
|
19504
|
+
await new Promise((resolve32, reject) => {
|
|
19505
19505
|
const onAbort = () => {
|
|
19506
19506
|
signal?.removeEventListener("abort", onAbort);
|
|
19507
19507
|
clearTimeout(timer);
|
|
@@ -19509,7 +19509,7 @@ var HubClient = class {
|
|
|
19509
19509
|
};
|
|
19510
19510
|
const timer = setTimeout(() => {
|
|
19511
19511
|
signal?.removeEventListener("abort", onAbort);
|
|
19512
|
-
|
|
19512
|
+
resolve32();
|
|
19513
19513
|
}, Math.min(500, Math.max(1, deadline - Date.now())));
|
|
19514
19514
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19515
19515
|
if (signal?.aborted) onAbort();
|
|
@@ -19564,7 +19564,7 @@ var HubClient = class {
|
|
|
19564
19564
|
} catch (error) {
|
|
19565
19565
|
if (this.stopped || error instanceof Error && error.name === "AbortError") return;
|
|
19566
19566
|
}
|
|
19567
|
-
if (!this.stopped) await new Promise((
|
|
19567
|
+
if (!this.stopped) await new Promise((resolve32) => setTimeout(resolve32, this.options.reconnectMs));
|
|
19568
19568
|
}
|
|
19569
19569
|
}
|
|
19570
19570
|
headers(includeIdentity = true) {
|
|
@@ -20436,8 +20436,8 @@ var AGENT_COMMANDS = [
|
|
|
20436
20436
|
summary: { type: "string", description: "Promotion summary" },
|
|
20437
20437
|
authority: {
|
|
20438
20438
|
type: "string",
|
|
20439
|
-
enum: ["
|
|
20440
|
-
description: "Authority class"
|
|
20439
|
+
enum: ["evidence", "hypothesis"],
|
|
20440
|
+
description: "Authority class; an agent's proposal is peer origin, so evidence at most"
|
|
20441
20441
|
},
|
|
20442
20442
|
confidence: {
|
|
20443
20443
|
type: "string",
|
|
@@ -21036,7 +21036,7 @@ function defaultSpawn(command, args, options) {
|
|
|
21036
21036
|
if (options.signal?.aborted) {
|
|
21037
21037
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
21038
21038
|
}
|
|
21039
|
-
return new Promise((
|
|
21039
|
+
return new Promise((resolve32) => {
|
|
21040
21040
|
const stdout = [];
|
|
21041
21041
|
const stderr = [];
|
|
21042
21042
|
let outputBytes = 0;
|
|
@@ -21067,7 +21067,7 @@ function defaultSpawn(command, args, options) {
|
|
|
21067
21067
|
const started = Boolean(child?.pid);
|
|
21068
21068
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
21069
21069
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
21070
|
-
|
|
21070
|
+
resolve32({
|
|
21071
21071
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
21072
21072
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
21073
21073
|
code,
|
|
@@ -23280,13 +23280,13 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
|
|
|
23280
23280
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
23281
23281
|
import { spawn as spawn2 } from "node:child_process";
|
|
23282
23282
|
import { createHash as createHash12, createHmac, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
23283
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
23283
|
+
import { chmodSync as chmodSync4, existsSync as existsSync14, lstatSync as lstatSync6, mkdirSync as mkdirSync9, readFileSync as readFileSync12, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "node:fs";
|
|
23284
23284
|
import { dirname as dirname9, isAbsolute as isAbsolute6, join as join13 } from "node:path";
|
|
23285
23285
|
|
|
23286
23286
|
// plugins/kxm/src/runtime-store.ts
|
|
23287
23287
|
import { createHash as createHash10, randomUUID as randomUUID4 } from "node:crypto";
|
|
23288
|
-
import { existsSync as
|
|
23289
|
-
import { dirname as dirname7, join as join9, resolve as
|
|
23288
|
+
import { existsSync as existsSync10, lstatSync as lstatSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23289
|
+
import { dirname as dirname7, join as join9, resolve as resolve7 } from "node:path";
|
|
23290
23290
|
|
|
23291
23291
|
// plugins/kxm/src/bindings.ts
|
|
23292
23292
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -23294,7 +23294,7 @@ import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypt
|
|
|
23294
23294
|
import {
|
|
23295
23295
|
chmodSync as chmodSync2,
|
|
23296
23296
|
closeSync,
|
|
23297
|
-
existsSync as
|
|
23297
|
+
existsSync as existsSync8,
|
|
23298
23298
|
fsyncSync,
|
|
23299
23299
|
lstatSync as lstatSync2,
|
|
23300
23300
|
mkdirSync as mkdirSync4,
|
|
@@ -23308,7 +23308,10 @@ import {
|
|
|
23308
23308
|
import { homedir as homedir3 } from "node:os";
|
|
23309
23309
|
|
|
23310
23310
|
// plugins/kxm/src/sqlite.ts
|
|
23311
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
23311
23312
|
import { createRequire } from "node:module";
|
|
23313
|
+
import { resolve as resolve4 } from "node:path";
|
|
23314
|
+
import { pathToFileURL } from "node:url";
|
|
23312
23315
|
var requireFromHere = createRequire(import.meta.url);
|
|
23313
23316
|
function loadNative() {
|
|
23314
23317
|
try {
|
|
@@ -23345,9 +23348,15 @@ var DatabaseSync = class {
|
|
|
23345
23348
|
this.inner.close();
|
|
23346
23349
|
}
|
|
23347
23350
|
};
|
|
23351
|
+
function openReadOnlyDatabase(path4) {
|
|
23352
|
+
if (existsSync7(`${path4}-wal`)) return new DatabaseSync(path4, { readOnly: true });
|
|
23353
|
+
const uri = pathToFileURL(resolve4(path4));
|
|
23354
|
+
uri.searchParams.set("immutable", "1");
|
|
23355
|
+
return new DatabaseSync(uri.href, { readOnly: true });
|
|
23356
|
+
}
|
|
23348
23357
|
|
|
23349
23358
|
// plugins/kxm/src/bindings.ts
|
|
23350
|
-
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join7, parse, relative as relative2, resolve as
|
|
23359
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join7, parse, relative as relative2, resolve as resolve5, sep as sep2 } from "node:path";
|
|
23351
23360
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
23352
23361
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
23353
23362
|
var activeLocks = /* @__PURE__ */ new WeakSet();
|
|
@@ -23358,7 +23367,7 @@ function bindingError(phase, code, message) {
|
|
|
23358
23367
|
throw new KxmConfigError([bindingIssue(phase, code, message)]);
|
|
23359
23368
|
}
|
|
23360
23369
|
function canonicalHostPath2(path4) {
|
|
23361
|
-
const absolute =
|
|
23370
|
+
const absolute = resolve5(path4);
|
|
23362
23371
|
try {
|
|
23363
23372
|
return realpathSync2.native(absolute);
|
|
23364
23373
|
} catch {
|
|
@@ -23377,17 +23386,17 @@ function kxmUserStateRoot(options = {}) {
|
|
|
23377
23386
|
const explicit = options.stateRoot ?? env.KXM_STATE_HOME?.trim();
|
|
23378
23387
|
if (explicit) {
|
|
23379
23388
|
if (!isAbsolute3(explicit)) bindingError("path", "local_state_root_not_absolute", "KXM_STATE_HOME must be an absolute host-local path");
|
|
23380
|
-
return
|
|
23389
|
+
return resolve5(explicit);
|
|
23381
23390
|
}
|
|
23382
23391
|
if (platform === "win32") {
|
|
23383
23392
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
23384
23393
|
const base2 = localAppData && isAbsolute3(localAppData) ? localAppData : join7(home, "AppData", "Local");
|
|
23385
|
-
return
|
|
23394
|
+
return resolve5(base2, "KXM");
|
|
23386
23395
|
}
|
|
23387
|
-
if (platform === "darwin") return
|
|
23396
|
+
if (platform === "darwin") return resolve5(home, "Library", "Application Support", "KXM");
|
|
23388
23397
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
23389
23398
|
const base = xdgState && isAbsolute3(xdgState) ? xdgState : join7(home, ".local", "state");
|
|
23390
|
-
return
|
|
23399
|
+
return resolve5(base, "kxm");
|
|
23391
23400
|
}
|
|
23392
23401
|
function projectBindingKey(projectRoot, platform = process.platform) {
|
|
23393
23402
|
const canonical2 = canonicalHostPath2(projectRoot);
|
|
@@ -23399,7 +23408,7 @@ function kxmLocalBindingFile(projectRoot, options = {}) {
|
|
|
23399
23408
|
return join7(stateRoot, "projects", projectBindingKey(projectRoot, options.platform), "repository-bindings.json");
|
|
23400
23409
|
}
|
|
23401
23410
|
function checkedDirectory(path4, description) {
|
|
23402
|
-
if (!
|
|
23411
|
+
if (!existsSync8(path4)) return false;
|
|
23403
23412
|
const stat = lstatSync2(path4);
|
|
23404
23413
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
23405
23414
|
bindingError("path", "local_binding_directory_invalid", `${description} must be a regular directory, not a link or file`);
|
|
@@ -23431,10 +23440,10 @@ function asRecord2(value, file, projectRoot, schemasDir) {
|
|
|
23431
23440
|
};
|
|
23432
23441
|
}
|
|
23433
23442
|
function readKxmLocalBindings(projectRoot, options = {}) {
|
|
23434
|
-
const root =
|
|
23443
|
+
const root = resolve5(projectRoot);
|
|
23435
23444
|
const stateRoot = kxmUserStateRoot(options);
|
|
23436
23445
|
const file = kxmLocalBindingFile(root, options);
|
|
23437
|
-
if (!existingBindingDirectory(file, stateRoot) || !
|
|
23446
|
+
if (!existingBindingDirectory(file, stateRoot) || !existsSync8(file)) return void 0;
|
|
23438
23447
|
const stat = lstatSync2(file);
|
|
23439
23448
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
23440
23449
|
bindingError("path", "local_binding_file_invalid", "binding record must be a regular file, not a link or directory");
|
|
@@ -23482,7 +23491,7 @@ function syncDirectory(path4) {
|
|
|
23482
23491
|
}
|
|
23483
23492
|
}
|
|
23484
23493
|
function createDirectoryIfMissing(path4) {
|
|
23485
|
-
if (
|
|
23494
|
+
if (existsSync8(path4)) return;
|
|
23486
23495
|
try {
|
|
23487
23496
|
mkdirSync4(path4, { mode: 448 });
|
|
23488
23497
|
syncDirectory(dirname5(path4));
|
|
@@ -23492,8 +23501,8 @@ function createDirectoryIfMissing(path4) {
|
|
|
23492
23501
|
}
|
|
23493
23502
|
function ensureDurableDirectory(path4, description) {
|
|
23494
23503
|
const missing = [];
|
|
23495
|
-
let current =
|
|
23496
|
-
while (!
|
|
23504
|
+
let current = resolve5(path4);
|
|
23505
|
+
while (!existsSync8(current)) {
|
|
23497
23506
|
missing.push(current);
|
|
23498
23507
|
const parent = dirname5(current);
|
|
23499
23508
|
if (parent === current) bindingError("path", "local_binding_directory_invalid", `${description} has no existing filesystem ancestor`);
|
|
@@ -23518,7 +23527,7 @@ function ensureBindingDirectory(file, stateRoot) {
|
|
|
23518
23527
|
checkedDirectory(project, "local project binding directory");
|
|
23519
23528
|
}
|
|
23520
23529
|
function assertNoLinkedDirectoryComponents(path4, description) {
|
|
23521
|
-
const absolute =
|
|
23530
|
+
const absolute = resolve5(path4);
|
|
23522
23531
|
const filesystemRoot = parse(absolute).root;
|
|
23523
23532
|
let current = filesystemRoot;
|
|
23524
23533
|
const remainder = relative2(filesystemRoot, absolute);
|
|
@@ -23544,14 +23553,14 @@ function projectOperationLockFile(projectRoot) {
|
|
|
23544
23553
|
if (result.status !== 0 || result.error || !result.stdout.trim()) {
|
|
23545
23554
|
bindingError("discovery", "project_operation_lock_unavailable", "cannot resolve the authoritative Git metadata path for project locking");
|
|
23546
23555
|
}
|
|
23547
|
-
const gitDirectory =
|
|
23556
|
+
const gitDirectory = resolve5(result.stdout.trim());
|
|
23548
23557
|
assertNoLinkedDirectoryComponents(gitDirectory, "authoritative Git metadata path");
|
|
23549
23558
|
const gitStat = lstatSync2(gitDirectory);
|
|
23550
23559
|
if (gitStat.isSymbolicLink() || !gitStat.isDirectory()) {
|
|
23551
23560
|
bindingError("path", "project_operation_lock_parent_invalid", "authoritative Git metadata must resolve to a regular directory");
|
|
23552
23561
|
}
|
|
23553
23562
|
const lockDirectory = join7(gitDirectory, "kxm");
|
|
23554
|
-
if (
|
|
23563
|
+
if (existsSync8(lockDirectory)) {
|
|
23555
23564
|
const stat = lstatSync2(lockDirectory);
|
|
23556
23565
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
23557
23566
|
bindingError("path", "project_operation_lock_parent_invalid", "Git-local KXM lock directory must be regular, not a link or file");
|
|
@@ -23563,9 +23572,9 @@ function projectOperationLockFile(projectRoot) {
|
|
|
23563
23572
|
return join7(lockDirectory, "project-operation-lock.sqlite");
|
|
23564
23573
|
}
|
|
23565
23574
|
function withKxmLocalBindingLock(projectRoot, options, callback) {
|
|
23566
|
-
const root =
|
|
23575
|
+
const root = resolve5(projectRoot);
|
|
23567
23576
|
const file = projectOperationLockFile(root);
|
|
23568
|
-
if (
|
|
23577
|
+
if (existsSync8(file)) {
|
|
23569
23578
|
const stat = lstatSync2(file);
|
|
23570
23579
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
23571
23580
|
bindingError("path", "local_binding_lock_invalid", "project-operation lock must be a regular file, not a link or directory");
|
|
@@ -23598,7 +23607,7 @@ function withKxmLocalBindingLock(projectRoot, options, callback) {
|
|
|
23598
23607
|
}
|
|
23599
23608
|
}
|
|
23600
23609
|
function planKxmLocalBindings(projectRoot, projectId, repositories, options = {}) {
|
|
23601
|
-
const root =
|
|
23610
|
+
const root = resolve5(projectRoot);
|
|
23602
23611
|
const file = kxmLocalBindingFile(root, options);
|
|
23603
23612
|
const record = normalizedRecord(root, projectId, repositories, options.schemasDir);
|
|
23604
23613
|
const existing = readKxmLocalBindings(root, options);
|
|
@@ -23608,7 +23617,7 @@ function planKxmLocalBindings(projectRoot, projectId, repositories, options = {}
|
|
|
23608
23617
|
return { file, written: !existing || !recordsEqual(existing, record), record };
|
|
23609
23618
|
}
|
|
23610
23619
|
function writeKxmLocalBindings(projectRoot, projectId, repositories, options = {}, lock) {
|
|
23611
|
-
const root =
|
|
23620
|
+
const root = resolve5(projectRoot);
|
|
23612
23621
|
if (!lock) {
|
|
23613
23622
|
return withKxmLocalBindingLock(root, options, (acquired) => writeKxmLocalBindings(root, projectId, repositories, options, acquired));
|
|
23614
23623
|
}
|
|
@@ -23726,7 +23735,7 @@ import { createHash as createHash9, randomBytes } from "node:crypto";
|
|
|
23726
23735
|
import {
|
|
23727
23736
|
chmodSync as chmodSync3,
|
|
23728
23737
|
copyFileSync,
|
|
23729
|
-
existsSync as
|
|
23738
|
+
existsSync as existsSync9,
|
|
23730
23739
|
lstatSync as lstatSync3,
|
|
23731
23740
|
mkdirSync as mkdirSync5,
|
|
23732
23741
|
readdirSync as readdirSync2,
|
|
@@ -23734,14 +23743,14 @@ import {
|
|
|
23734
23743
|
unlinkSync as unlinkSync2,
|
|
23735
23744
|
writeFileSync as writeFileSync5
|
|
23736
23745
|
} from "node:fs";
|
|
23737
|
-
import { basename as basename3, dirname as dirname6, join as join8, resolve as
|
|
23746
|
+
import { basename as basename3, dirname as dirname6, join as join8, resolve as resolve6 } from "node:path";
|
|
23738
23747
|
function databaseError(code, file, message) {
|
|
23739
23748
|
const issue3 = { phase: "semantic", code, file, message };
|
|
23740
23749
|
return new KxmConfigError([issue3]);
|
|
23741
23750
|
}
|
|
23742
23751
|
function checkedParent(path4, description) {
|
|
23743
23752
|
const parent = dirname6(path4);
|
|
23744
|
-
if (!
|
|
23753
|
+
if (!existsSync9(parent)) mkdirSync5(parent, { recursive: true, mode: 448 });
|
|
23745
23754
|
const stat = lstatSync3(parent, { throwIfNoEntry: false });
|
|
23746
23755
|
if (!stat || stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
23747
23756
|
throw databaseError("runtime_path_invalid", description, `${description} parent must be a regular directory, not a link`);
|
|
@@ -23878,14 +23887,14 @@ function fileSha256(filePath) {
|
|
|
23878
23887
|
return `sha256:${createHash9("sha256").update(bytes).digest("hex")}`;
|
|
23879
23888
|
}
|
|
23880
23889
|
function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
23881
|
-
const resolvedSource =
|
|
23882
|
-
const resolvedTarget =
|
|
23890
|
+
const resolvedSource = resolve6(sourcePath);
|
|
23891
|
+
const resolvedTarget = resolve6(targetPath);
|
|
23883
23892
|
const sourceStat = lstatSync3(resolvedSource, { throwIfNoEntry: false });
|
|
23884
23893
|
if (!sourceStat || !sourceStat.isFile() || sourceStat.isSymbolicLink()) {
|
|
23885
23894
|
throw databaseError("runtime_path_invalid", resolvedSource, `source database ${resolvedSource} must be a regular file, not a link or directory`);
|
|
23886
23895
|
}
|
|
23887
23896
|
checkedParent(resolvedTarget, "backup target");
|
|
23888
|
-
if (
|
|
23897
|
+
if (existsSync9(resolvedTarget)) {
|
|
23889
23898
|
unlinkSync2(resolvedTarget);
|
|
23890
23899
|
}
|
|
23891
23900
|
const sourceDb = new DatabaseSync(resolvedSource);
|
|
@@ -23929,8 +23938,8 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
|
23929
23938
|
};
|
|
23930
23939
|
}
|
|
23931
23940
|
function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVersion, maxSupportedVersion) {
|
|
23932
|
-
const resolvedBackup =
|
|
23933
|
-
const resolvedTarget =
|
|
23941
|
+
const resolvedBackup = resolve6(backupPath);
|
|
23942
|
+
const resolvedTarget = resolve6(targetPath);
|
|
23934
23943
|
const backupStat = lstatSync3(resolvedBackup, { throwIfNoEntry: false });
|
|
23935
23944
|
if (!backupStat || !backupStat.isFile() || backupStat.isSymbolicLink()) {
|
|
23936
23945
|
throw databaseError("runtime_path_invalid", resolvedBackup, `backup database ${resolvedBackup} must be a regular file, not a link or directory`);
|
|
@@ -23963,7 +23972,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
23963
23972
|
}
|
|
23964
23973
|
checkedParent(resolvedTarget, "restore target");
|
|
23965
23974
|
for (const file of [resolvedTarget, `${resolvedTarget}-wal`, `${resolvedTarget}-shm`]) {
|
|
23966
|
-
if (
|
|
23975
|
+
if (existsSync9(file)) {
|
|
23967
23976
|
try {
|
|
23968
23977
|
unlinkSync2(file);
|
|
23969
23978
|
} catch {
|
|
@@ -24003,22 +24012,22 @@ function kxmBackupCeiling(storeId) {
|
|
|
24003
24012
|
return KXM_BACKUP_CEILINGS[storeId] ?? KXM_BACKUP_CEILINGS["hub-store"];
|
|
24004
24013
|
}
|
|
24005
24014
|
function discoverProjectStores(projectRoot, options = {}) {
|
|
24006
|
-
const root =
|
|
24015
|
+
const root = resolve6(projectRoot);
|
|
24007
24016
|
const stores = [];
|
|
24008
|
-
const hubPath = options.hubDataPath ?
|
|
24009
|
-
if (
|
|
24017
|
+
const hubPath = options.hubDataPath ? resolve6(options.hubDataPath) : join8(root, ".kxm", "state", "kxm.db");
|
|
24018
|
+
if (existsSync9(hubPath)) {
|
|
24010
24019
|
stores.push({ storeId: "hub-store", sourcePath: hubPath, maxSupportedVersion: kxmBackupCeiling("hub-store") });
|
|
24011
24020
|
}
|
|
24012
24021
|
const registryPath = join8(root, ".kxm", "runtime", "registry.db");
|
|
24013
|
-
if (
|
|
24022
|
+
if (existsSync9(registryPath)) {
|
|
24014
24023
|
stores.push({ storeId: "registry", sourcePath: registryPath, maxSupportedVersion: kxmBackupCeiling("registry") });
|
|
24015
24024
|
}
|
|
24016
24025
|
const bindingsPath = join8(root, ".kxm", "runtime", "bindings.db");
|
|
24017
|
-
if (
|
|
24026
|
+
if (existsSync9(bindingsPath)) {
|
|
24018
24027
|
stores.push({ storeId: "binding-store", sourcePath: bindingsPath, maxSupportedVersion: kxmBackupCeiling("binding-store") });
|
|
24019
24028
|
}
|
|
24020
24029
|
const eventsDir = join8(root, ".kxm", "runtime", "events");
|
|
24021
|
-
if (
|
|
24030
|
+
if (existsSync9(eventsDir)) {
|
|
24022
24031
|
const entries = readdirSync2(eventsDir, { withFileTypes: true });
|
|
24023
24032
|
for (const entry of entries) {
|
|
24024
24033
|
if (entry.isFile() && entry.name.endsWith(".db")) {
|
|
@@ -24033,38 +24042,43 @@ function discoverProjectStores(projectRoot, options = {}) {
|
|
|
24033
24042
|
}
|
|
24034
24043
|
return stores;
|
|
24035
24044
|
}
|
|
24036
|
-
function
|
|
24037
|
-
const projectRoot = options.projectRoot ?
|
|
24045
|
+
function planBackup(options = {}) {
|
|
24046
|
+
const projectRoot = options.projectRoot ? resolve6(options.projectRoot) : process.cwd();
|
|
24038
24047
|
const stores = discoverProjectStores(projectRoot, {
|
|
24039
24048
|
...options.hubDataPath !== void 0 ? { hubDataPath: options.hubDataPath } : {}
|
|
24040
24049
|
});
|
|
24041
24050
|
if (stores.length === 0) {
|
|
24042
24051
|
throw databaseError("backup_no_stores", projectRoot, "no existing SQLite stores found to backup");
|
|
24043
24052
|
}
|
|
24044
|
-
const
|
|
24045
|
-
const timestamp =
|
|
24046
|
-
const
|
|
24047
|
-
const outDir = options.outDir ? resolve5(options.outDir) : join8(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
|
|
24048
|
-
if (!existsSync8(outDir)) {
|
|
24049
|
-
mkdirSync5(outDir, { recursive: true, mode: 448 });
|
|
24050
|
-
}
|
|
24051
|
-
const backedUpStores = [];
|
|
24053
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24054
|
+
const timestamp = createdAt.replace(/[:.]/g, "-");
|
|
24055
|
+
const outDir = options.outDir ? resolve6(options.outDir) : join8(projectRoot, ".kxm", "backups", `backup-${timestamp}`);
|
|
24052
24056
|
const usedFilenames = /* @__PURE__ */ new Set();
|
|
24053
|
-
|
|
24057
|
+
const planned = stores.map((store) => {
|
|
24054
24058
|
let filename = basename3(store.sourcePath);
|
|
24055
24059
|
if (usedFilenames.has(filename)) {
|
|
24056
24060
|
const sanitizedId = store.storeId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
24057
24061
|
filename = `${sanitizedId}-${filename}`;
|
|
24058
24062
|
}
|
|
24059
24063
|
usedFilenames.add(filename);
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24064
|
+
return { storeId: store.storeId, sourcePath: store.sourcePath, backupFile: filename };
|
|
24065
|
+
});
|
|
24066
|
+
return { projectRoot, outDir, createdAt, stores: planned };
|
|
24067
|
+
}
|
|
24068
|
+
function createBackup(options = {}) {
|
|
24069
|
+
const { projectRoot, outDir, createdAt, stores } = planBackup(options);
|
|
24070
|
+
const backupId = `bk_${randomBytes(8).toString("hex")}`;
|
|
24071
|
+
if (!existsSync9(outDir)) {
|
|
24072
|
+
mkdirSync5(outDir, { recursive: true, mode: 448 });
|
|
24073
|
+
}
|
|
24074
|
+
const backedUpStores = [];
|
|
24075
|
+
for (const store of stores) {
|
|
24076
|
+
backedUpStores.push(backupDatabaseFile(store.sourcePath, join8(outDir, store.backupFile), store.storeId));
|
|
24063
24077
|
}
|
|
24064
24078
|
const manifest = {
|
|
24065
24079
|
schema: "kxm.backup-manifest.v1",
|
|
24066
24080
|
backupId,
|
|
24067
|
-
createdAt
|
|
24081
|
+
createdAt,
|
|
24068
24082
|
projectRoot,
|
|
24069
24083
|
stores: backedUpStores
|
|
24070
24084
|
};
|
|
@@ -24076,8 +24090,8 @@ function createBackup(options = {}) {
|
|
|
24076
24090
|
writeFileSync5(manifestPath, finalJson, "utf8");
|
|
24077
24091
|
return { manifest, outDir };
|
|
24078
24092
|
}
|
|
24079
|
-
function
|
|
24080
|
-
let manifestPath =
|
|
24093
|
+
function planRestore(manifestPathOrDir, options = {}) {
|
|
24094
|
+
let manifestPath = resolve6(manifestPathOrDir);
|
|
24081
24095
|
const stat = lstatSync3(manifestPath, { throwIfNoEntry: false });
|
|
24082
24096
|
if (!stat) {
|
|
24083
24097
|
throw databaseError("runtime_path_invalid", manifestPath, `manifest path ${manifestPath} does not exist`);
|
|
@@ -24085,7 +24099,7 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
24085
24099
|
if (stat.isDirectory()) {
|
|
24086
24100
|
manifestPath = join8(manifestPath, "manifest.json");
|
|
24087
24101
|
}
|
|
24088
|
-
if (!
|
|
24102
|
+
if (!existsSync9(manifestPath)) {
|
|
24089
24103
|
throw databaseError("runtime_path_invalid", manifestPath, `backup manifest ${manifestPath} not found`);
|
|
24090
24104
|
}
|
|
24091
24105
|
const manifestDir = dirname6(manifestPath);
|
|
@@ -24099,10 +24113,10 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
24099
24113
|
if (manifest.schema !== "kxm.backup-manifest.v1" || !Array.isArray(manifest.stores) || manifest.stores.length === 0) {
|
|
24100
24114
|
throw databaseError("restore_manifest_invalid", manifestPath, "manifest is not a valid kxm.backup-manifest.v1 document");
|
|
24101
24115
|
}
|
|
24102
|
-
const
|
|
24116
|
+
const stores = [];
|
|
24103
24117
|
for (const store of manifest.stores) {
|
|
24104
24118
|
const backupFilePath = join8(manifestDir, store.backupFile);
|
|
24105
|
-
if (!
|
|
24119
|
+
if (!existsSync9(backupFilePath)) {
|
|
24106
24120
|
throw databaseError("restore_file_missing", backupFilePath, `backup file ${store.backupFile} missing from ${manifestDir}`);
|
|
24107
24121
|
}
|
|
24108
24122
|
const actualSha256 = fileSha256(backupFilePath);
|
|
@@ -24113,31 +24127,42 @@ function restoreBackup(manifestPathOrDir, options = {}) {
|
|
|
24113
24127
|
`backup file ${store.backupFile} sha256 ${actualSha256} does not match manifest hash ${store.sha256}`
|
|
24114
24128
|
);
|
|
24115
24129
|
}
|
|
24116
|
-
const
|
|
24130
|
+
const maxSupportedVersion = kxmBackupCeiling(store.storeId);
|
|
24131
|
+
if (store.schemaVersion > maxSupportedVersion) {
|
|
24132
|
+
throw databaseError(
|
|
24133
|
+
"runtime_schema_newer",
|
|
24134
|
+
backupFilePath,
|
|
24135
|
+
`backup store ${store.storeId} schema version ${store.schemaVersion} is newer than supported maximum ${maxSupportedVersion}`
|
|
24136
|
+
);
|
|
24137
|
+
}
|
|
24117
24138
|
let targetPath = store.sourcePath;
|
|
24118
24139
|
if (options.projectRoot && manifest.projectRoot && targetPath.startsWith(manifest.projectRoot)) {
|
|
24119
24140
|
const rel = targetPath.slice(manifest.projectRoot.length).replace(/^[\\/]+/, "");
|
|
24120
|
-
targetPath = join8(
|
|
24121
|
-
}
|
|
24122
|
-
|
|
24123
|
-
backupFilePath,
|
|
24124
|
-
targetPath,
|
|
24125
|
-
store.storeId,
|
|
24126
|
-
store.schemaVersion,
|
|
24127
|
-
maxSupported
|
|
24128
|
-
);
|
|
24129
|
-
restoredStores.push(result);
|
|
24141
|
+
targetPath = join8(resolve6(options.projectRoot), rel);
|
|
24142
|
+
}
|
|
24143
|
+
stores.push({ storeId: store.storeId, backupFilePath, targetPath, schemaVersion: store.schemaVersion, maxSupportedVersion });
|
|
24130
24144
|
}
|
|
24145
|
+
return { manifestPath, backupId: manifest.backupId, stores };
|
|
24146
|
+
}
|
|
24147
|
+
function restoreBackup(manifestPathOrDir, options = {}) {
|
|
24148
|
+
const plan = planRestore(manifestPathOrDir, options);
|
|
24149
|
+
const restoredStores = plan.stores.map((store) => restoreDatabaseFile(
|
|
24150
|
+
store.backupFilePath,
|
|
24151
|
+
store.targetPath,
|
|
24152
|
+
store.storeId,
|
|
24153
|
+
store.schemaVersion,
|
|
24154
|
+
store.maxSupportedVersion
|
|
24155
|
+
));
|
|
24131
24156
|
return {
|
|
24132
|
-
manifestPath,
|
|
24133
|
-
backupId:
|
|
24157
|
+
manifestPath: plan.manifestPath,
|
|
24158
|
+
backupId: plan.backupId,
|
|
24134
24159
|
restoredStores
|
|
24135
24160
|
};
|
|
24136
24161
|
}
|
|
24137
24162
|
|
|
24138
24163
|
// plugins/kxm/src/runtime-store.ts
|
|
24139
24164
|
function kxmRuntimePaths(options = {}) {
|
|
24140
|
-
const stateRoot = options.stateRoot ?
|
|
24165
|
+
const stateRoot = options.stateRoot ? resolve7(options.stateRoot) : kxmUserStateRoot({ ...options.env ? { env: options.env } : {}, ...options.homeDir ? { homeDir: options.homeDir } : {} });
|
|
24141
24166
|
const runtimeDir = join9(stateRoot, "runtime");
|
|
24142
24167
|
return {
|
|
24143
24168
|
stateRoot,
|
|
@@ -24155,9 +24180,9 @@ function runtimeError(code, file, message) {
|
|
|
24155
24180
|
function projectRuntimeKey(projectRoot) {
|
|
24156
24181
|
let canonical2;
|
|
24157
24182
|
try {
|
|
24158
|
-
canonical2 = realpathSync3.native(
|
|
24183
|
+
canonical2 = realpathSync3.native(resolve7(projectRoot));
|
|
24159
24184
|
} catch {
|
|
24160
|
-
canonical2 =
|
|
24185
|
+
canonical2 = resolve7(projectRoot);
|
|
24161
24186
|
}
|
|
24162
24187
|
const folded = process.platform === "win32" ? canonical2.toLocaleLowerCase("en-US") : canonical2;
|
|
24163
24188
|
return createHash10("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
@@ -24191,7 +24216,7 @@ var KxmRuntimeRegistry = class {
|
|
|
24191
24216
|
path;
|
|
24192
24217
|
database;
|
|
24193
24218
|
constructor(path4) {
|
|
24194
|
-
this.path =
|
|
24219
|
+
this.path = resolve7(path4);
|
|
24195
24220
|
this.database = openDatabase(this.path, "runtime registry", {
|
|
24196
24221
|
schema: REGISTRY_SCHEMA,
|
|
24197
24222
|
version: KXM_REGISTRY_SCHEMA_VERSION,
|
|
@@ -24290,7 +24315,7 @@ var KxmRuntimeRegistry = class {
|
|
|
24290
24315
|
return rows.map((row) => ({ projectRoot: row.project_root, projectId: row.project_id }));
|
|
24291
24316
|
}
|
|
24292
24317
|
registerProject(registration) {
|
|
24293
|
-
const projectRoot =
|
|
24318
|
+
const projectRoot = resolve7(registration.projectRoot);
|
|
24294
24319
|
const projectKey = projectRuntimeKey(projectRoot);
|
|
24295
24320
|
this.database.exec("BEGIN IMMEDIATE");
|
|
24296
24321
|
try {
|
|
@@ -24994,7 +25019,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
|
24994
25019
|
// plugins/kxm/src/prices.ts
|
|
24995
25020
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
24996
25021
|
import { createHash as createHash11 } from "node:crypto";
|
|
24997
|
-
import { existsSync as
|
|
25022
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, statSync } from "node:fs";
|
|
24998
25023
|
import { join as join10 } from "node:path";
|
|
24999
25024
|
|
|
25000
25025
|
// plugins/kxm/src/price-calc.ts
|
|
@@ -25147,8 +25172,8 @@ function parsePriceCatalog(text) {
|
|
|
25147
25172
|
return catalog;
|
|
25148
25173
|
}
|
|
25149
25174
|
function loadPriceCatalog(rootOrPath) {
|
|
25150
|
-
const candidatePath =
|
|
25151
|
-
if (!candidatePath || !
|
|
25175
|
+
const candidatePath = existsSync11(join10(rootOrPath, ".kxm", "prices.yaml")) ? join10(rootOrPath, ".kxm", "prices.yaml") : existsSync11(join10(rootOrPath, "prices.yaml")) ? join10(rootOrPath, "prices.yaml") : existsSync11(rootOrPath) && statSync(rootOrPath).isFile() ? rootOrPath : void 0;
|
|
25176
|
+
if (!candidatePath || !existsSync11(candidatePath)) {
|
|
25152
25177
|
return void 0;
|
|
25153
25178
|
}
|
|
25154
25179
|
const content = readFileSync9(candidatePath, "utf8");
|
|
@@ -25172,14 +25197,14 @@ var import_yaml5 = __toESM(require_dist(), 1);
|
|
|
25172
25197
|
|
|
25173
25198
|
// plugins/kxm/src/routes.ts
|
|
25174
25199
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
25175
|
-
import { existsSync as
|
|
25200
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10, mkdirSync as mkdirSync7, writeFileSync as writeFileSync7, readdirSync as readdirSync3 } from "node:fs";
|
|
25176
25201
|
import { join as join11 } from "node:path";
|
|
25177
25202
|
var RETIRED_POLICY = ".kxm/producers.yaml";
|
|
25178
25203
|
var empty = () => ({ schema: "kxm.routes.v2", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), admitted: [], disabled: [], roles: {} });
|
|
25179
25204
|
function loadRoutePolicy(root) {
|
|
25180
|
-
if (
|
|
25205
|
+
if (existsSync12(join11(root, RETIRED_POLICY))) throw new Error(`retired ${RETIRED_POLICY} present; use .kxm/routes.yaml (kxm.routes.v2)`);
|
|
25181
25206
|
const path4 = join11(root, ".kxm", "routes.yaml");
|
|
25182
|
-
if (!
|
|
25207
|
+
if (!existsSync12(path4)) return empty();
|
|
25183
25208
|
const value = (0, import_yaml4.parse)(readFileSync10(path4, "utf8"));
|
|
25184
25209
|
if (value?.schema !== "kxm.routes.v2" || !Array.isArray(value.admitted)) throw new Error("invalid .kxm/routes.yaml");
|
|
25185
25210
|
return { schema: "kxm.routes.v2", updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : (/* @__PURE__ */ new Date()).toISOString(), admitted: value.admitted.filter((x2) => typeof x2 === "string"), disabled: Array.isArray(value.disabled) ? value.disabled.filter((x2) => typeof x2 === "string") : [], roles: value.roles && typeof value.roles === "object" ? Object.fromEntries(Object.entries(value.roles).filter(([, v2]) => Array.isArray(v2)).map(([k, v2]) => [k, v2.filter((x2) => typeof x2 === "string")])) : {} };
|
|
@@ -25197,7 +25222,7 @@ function setRouteState(root, model, state, role, removeRole2 = false) {
|
|
|
25197
25222
|
if (!/^[a-z][a-z0-9_-]{0,63}$/i.test(role)) throw new Error("invalid role id");
|
|
25198
25223
|
const rolePath = join11(root, ".kxm", "roles", `${role}.yaml`);
|
|
25199
25224
|
let roleFile = {};
|
|
25200
|
-
if (
|
|
25225
|
+
if (existsSync12(rolePath)) {
|
|
25201
25226
|
const parsed = (0, import_yaml4.parse)(readFileSync10(rolePath, "utf8"));
|
|
25202
25227
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) roleFile = parsed;
|
|
25203
25228
|
}
|
|
@@ -25220,7 +25245,7 @@ function setRouteState(root, model, state, role, removeRole2 = false) {
|
|
|
25220
25245
|
function listRoleBindings(root) {
|
|
25221
25246
|
const dir = join11(root, ".kxm", "roles");
|
|
25222
25247
|
const result = {};
|
|
25223
|
-
if (!
|
|
25248
|
+
if (!existsSync12(dir)) return result;
|
|
25224
25249
|
for (const file of readdirSync3(dir).filter((name) => name.endsWith(".yaml"))) {
|
|
25225
25250
|
const role = file.slice(0, -5);
|
|
25226
25251
|
const value = (0, import_yaml4.parse)(readFileSync10(join11(dir, file), "utf8"));
|
|
@@ -25230,21 +25255,21 @@ function listRoleBindings(root) {
|
|
|
25230
25255
|
}
|
|
25231
25256
|
function listInventoryModels(root) {
|
|
25232
25257
|
const path4 = join11(root, ".kxm", "models", "inventory.yaml");
|
|
25233
|
-
if (!
|
|
25258
|
+
if (!existsSync12(path4)) return [];
|
|
25234
25259
|
const value = (0, import_yaml4.parse)(readFileSync10(path4, "utf8"));
|
|
25235
25260
|
return (value.models ?? []).map((m2) => m2.id).filter((id) => typeof id === "string");
|
|
25236
25261
|
}
|
|
25237
25262
|
|
|
25238
25263
|
// plugins/kxm/src/artifacts-exist.ts
|
|
25239
25264
|
import { lstatSync as lstatSync5, realpathSync as realpathSync4, statSync as statSync2 } from "node:fs";
|
|
25240
|
-
import { isAbsolute as isAbsolute4, relative as relative3, resolve as
|
|
25265
|
+
import { isAbsolute as isAbsolute4, relative as relative3, resolve as resolve8 } from "node:path";
|
|
25241
25266
|
function staysUnder(root, candidate) {
|
|
25242
25267
|
const child = relative3(root, candidate);
|
|
25243
25268
|
return child === "" || !isAbsolute4(child) && child !== ".." && !child.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`);
|
|
25244
25269
|
}
|
|
25245
25270
|
function verifyArtifactExists(rootInput, pathInput) {
|
|
25246
|
-
const lexicalRoot =
|
|
25247
|
-
const lexicalPath =
|
|
25271
|
+
const lexicalRoot = resolve8(rootInput);
|
|
25272
|
+
const lexicalPath = resolve8(pathInput);
|
|
25248
25273
|
if (!staysUnder(lexicalRoot, lexicalPath)) {
|
|
25249
25274
|
return { ok: false, error: "artifact_outside_workspace_assets", path: lexicalPath };
|
|
25250
25275
|
}
|
|
@@ -25863,9 +25888,9 @@ function formatRoutingReport(report2, options = {}) {
|
|
|
25863
25888
|
}
|
|
25864
25889
|
|
|
25865
25890
|
// plugins/kxm/src/hub-binding.ts
|
|
25866
|
-
import { existsSync as
|
|
25891
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
25867
25892
|
import { homedir as homedir4 } from "node:os";
|
|
25868
|
-
import { dirname as dirname8, isAbsolute as isAbsolute5, join as join12, resolve as
|
|
25893
|
+
import { dirname as dirname8, isAbsolute as isAbsolute5, join as join12, resolve as resolve9 } from "node:path";
|
|
25869
25894
|
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
25870
25895
|
var HUB_HEALTH_PROBE_MS = 300;
|
|
25871
25896
|
var HubBindingError = class extends Error {
|
|
@@ -25878,17 +25903,17 @@ function resolveUserStateRoot2(env) {
|
|
|
25878
25903
|
const explicit = env.KXM_STATE_HOME?.trim();
|
|
25879
25904
|
if (explicit) {
|
|
25880
25905
|
if (!isAbsolute5(explicit)) throw new HubBindingError("local_state_root_not_absolute");
|
|
25881
|
-
return
|
|
25906
|
+
return resolve9(explicit);
|
|
25882
25907
|
}
|
|
25883
25908
|
if (process.platform === "win32") {
|
|
25884
25909
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
25885
25910
|
const base2 = localAppData && isAbsolute5(localAppData) ? localAppData : join12(homedir4(), "AppData", "Local");
|
|
25886
|
-
return
|
|
25911
|
+
return resolve9(base2, "KXM");
|
|
25887
25912
|
}
|
|
25888
|
-
if (process.platform === "darwin") return
|
|
25913
|
+
if (process.platform === "darwin") return resolve9(homedir4(), "Library", "Application Support", "KXM");
|
|
25889
25914
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
25890
25915
|
const base = xdgState && isAbsolute5(xdgState) ? xdgState : join12(homedir4(), ".local", "state");
|
|
25891
|
-
return
|
|
25916
|
+
return resolve9(base, "kxm");
|
|
25892
25917
|
}
|
|
25893
25918
|
function hubBindingFile(env = process.env) {
|
|
25894
25919
|
return join12(resolveUserStateRoot2(env), "hub-binding.json");
|
|
@@ -25927,7 +25952,7 @@ function isAbortError(error) {
|
|
|
25927
25952
|
}
|
|
25928
25953
|
function readHubBinding(env = process.env) {
|
|
25929
25954
|
const file = hubBindingFile(env);
|
|
25930
|
-
if (!
|
|
25955
|
+
if (!existsSync13(file)) return void 0;
|
|
25931
25956
|
let parsed;
|
|
25932
25957
|
try {
|
|
25933
25958
|
parsed = JSON.parse(readFileSync11(file, "utf8"));
|
|
@@ -26029,7 +26054,7 @@ function supervisorErrorFile(paths) {
|
|
|
26029
26054
|
}
|
|
26030
26055
|
function clearSupervisorError(paths) {
|
|
26031
26056
|
const file = supervisorErrorFile(paths);
|
|
26032
|
-
if (
|
|
26057
|
+
if (existsSync14(file)) rmSync4(file, { force: true });
|
|
26033
26058
|
}
|
|
26034
26059
|
function readRecentSupervisorError(paths) {
|
|
26035
26060
|
const file = supervisorErrorFile(paths);
|
|
@@ -26044,7 +26069,7 @@ function readRecentSupervisorError(paths) {
|
|
|
26044
26069
|
}
|
|
26045
26070
|
}
|
|
26046
26071
|
function kxmSupervisorStatus(paths) {
|
|
26047
|
-
if (!
|
|
26072
|
+
if (!existsSync14(paths.registryDb)) return { running: false };
|
|
26048
26073
|
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
26049
26074
|
try {
|
|
26050
26075
|
const record = registry.supervisor();
|
|
@@ -26168,7 +26193,7 @@ async function kxmRuntimeRequest(handle, method, path4, body) {
|
|
|
26168
26193
|
|
|
26169
26194
|
// plugins/kxm/src/cli/types.ts
|
|
26170
26195
|
import { spawn as spawn3 } from "node:child_process";
|
|
26171
|
-
import { join as join15, resolve as
|
|
26196
|
+
import { join as join15, resolve as resolve10 } from "node:path";
|
|
26172
26197
|
|
|
26173
26198
|
// plugins/kxm/src/telemetry.ts
|
|
26174
26199
|
import { appendFileSync, mkdirSync as mkdirSync10, readFileSync as readFileSync13 } from "node:fs";
|
|
@@ -26314,6 +26339,17 @@ function print(io, jsonMode, payload, text) {
|
|
|
26314
26339
|
if (tagged.ok === false) io.stderr(line);
|
|
26315
26340
|
else io.stdout(line);
|
|
26316
26341
|
}
|
|
26342
|
+
function printPlan(runtime, payload, planned, text) {
|
|
26343
|
+
print(runtime.io, runtime.json, { ok: true, ...payload, dryRun: true, planned }, [
|
|
26344
|
+
`dry run: ${text}`,
|
|
26345
|
+
...planned.length === 0 ? [" no changes"] : planned.map((change) => ` would ${change.action} ${change.target}`)
|
|
26346
|
+
].join("\n"));
|
|
26347
|
+
}
|
|
26348
|
+
var DRY_RUN_UNSUPPORTED = "dry_run_unsupported";
|
|
26349
|
+
function refuseDryRun(io, jsonMode, command, reason) {
|
|
26350
|
+
print(io, jsonMode, { ok: false, command, dryRun: true, error: DRY_RUN_UNSUPPORTED, detail: reason }, `kxm ${command} --dry-run refused: ${reason}`);
|
|
26351
|
+
return 2;
|
|
26352
|
+
}
|
|
26317
26353
|
function printWorker(runtime, worker, payload, text, outcome) {
|
|
26318
26354
|
const sessionId = runtime.env.KXM_SESSION_ID?.trim();
|
|
26319
26355
|
const envelope = workerResult(worker, {
|
|
@@ -26365,16 +26401,16 @@ function redactCliValue(value, field = "") {
|
|
|
26365
26401
|
return value;
|
|
26366
26402
|
}
|
|
26367
26403
|
function workspaceDirs(cwd, workspaceFlag, env) {
|
|
26368
|
-
const workdir =
|
|
26369
|
-
const workspace =
|
|
26404
|
+
const workdir = resolve10(env.KXM_WORKDIR?.trim() || cwd);
|
|
26405
|
+
const workspace = resolve10(workdir, workspaceFlag || env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
|
|
26370
26406
|
const derive = workspaceFlag !== void 0;
|
|
26371
26407
|
return {
|
|
26372
26408
|
workdir,
|
|
26373
26409
|
workspace,
|
|
26374
|
-
config: derive ? join15(workspace, "config") :
|
|
26375
|
-
logs: derive ? join15(workspace, "logs") :
|
|
26376
|
-
assets: derive ? join15(workspace, "assets") :
|
|
26377
|
-
state: derive ? join15(workspace, "state") :
|
|
26410
|
+
config: derive ? join15(workspace, "config") : resolve10(workdir, env.KXM_CONFIG_DIR?.trim() || join15(workspace, "config")),
|
|
26411
|
+
logs: derive ? join15(workspace, "logs") : resolve10(workdir, env.KXM_LOGS_DIR?.trim() || join15(workspace, "logs")),
|
|
26412
|
+
assets: derive ? join15(workspace, "assets") : resolve10(workdir, env.KXM_ASSETS_DIR?.trim() || join15(workspace, "assets")),
|
|
26413
|
+
state: derive ? join15(workspace, "state") : resolve10(workdir, env.KXM_STATE_DIR?.trim() || join15(workspace, "state"))
|
|
26378
26414
|
};
|
|
26379
26415
|
}
|
|
26380
26416
|
function maskEnvName(name) {
|
|
@@ -26442,20 +26478,20 @@ function workspaceEnv(runtime) {
|
|
|
26442
26478
|
// plugins/kxm/src/cli/roles.ts
|
|
26443
26479
|
var import_yaml8 = __toESM(require_dist(), 1);
|
|
26444
26480
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
26445
|
-
import { existsSync as
|
|
26446
|
-
import { join as join18, resolve as
|
|
26481
|
+
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "node:fs";
|
|
26482
|
+
import { join as join18, resolve as resolve13 } from "node:path";
|
|
26447
26483
|
import { createInterface } from "node:readline";
|
|
26448
26484
|
|
|
26449
26485
|
// plugins/kxm/src/role.ts
|
|
26450
26486
|
var import_yaml7 = __toESM(require_dist(), 1);
|
|
26451
|
-
import { existsSync as
|
|
26487
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync12, readdirSync as readdirSync4, readFileSync as readFileSync15, rmSync as rmSync5, writeFileSync as writeFileSync11 } from "node:fs";
|
|
26452
26488
|
import { join as join17 } from "node:path";
|
|
26453
26489
|
|
|
26454
26490
|
// plugins/kxm/src/config.ts
|
|
26455
26491
|
var import_yaml6 = __toESM(require_dist(), 1);
|
|
26456
|
-
import { existsSync as
|
|
26492
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
|
|
26457
26493
|
import { homedir as homedir5 } from "node:os";
|
|
26458
|
-
import { dirname as dirname11, join as join16, resolve as
|
|
26494
|
+
import { dirname as dirname11, join as join16, resolve as resolve11 } from "node:path";
|
|
26459
26495
|
var KXM_CONFIG_SCHEMA = "kxm.config.v1";
|
|
26460
26496
|
var DEFAULT_KXM_CONFIG = {
|
|
26461
26497
|
schema: KXM_CONFIG_SCHEMA,
|
|
@@ -26508,11 +26544,11 @@ var DEFAULT_KXM_CONFIG = {
|
|
|
26508
26544
|
}
|
|
26509
26545
|
};
|
|
26510
26546
|
function userConfigDirectory(overrideDir) {
|
|
26511
|
-
if (overrideDir) return
|
|
26512
|
-
return
|
|
26547
|
+
if (overrideDir) return resolve11(overrideDir);
|
|
26548
|
+
return resolve11(process.env.KXM_USER_CONFIG_DIR?.trim() || join16(homedir5(), ".config", "kxm"));
|
|
26513
26549
|
}
|
|
26514
26550
|
function repoConfigDirectory(repoRoot) {
|
|
26515
|
-
return
|
|
26551
|
+
return resolve11(repoRoot, ".kxm");
|
|
26516
26552
|
}
|
|
26517
26553
|
function deepMerge(target, source) {
|
|
26518
26554
|
const result = { ...target };
|
|
@@ -26537,7 +26573,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
26537
26573
|
const repoConfigFile = join16(repoDir, "config.yaml");
|
|
26538
26574
|
let userRaw = {};
|
|
26539
26575
|
let userLoadedPath;
|
|
26540
|
-
if (
|
|
26576
|
+
if (existsSync15(userConfigFile)) {
|
|
26541
26577
|
try {
|
|
26542
26578
|
const text = readFileSync14(userConfigFile, "utf8");
|
|
26543
26579
|
userRaw = (0, import_yaml6.parse)(text) ?? {};
|
|
@@ -26548,7 +26584,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
26548
26584
|
}
|
|
26549
26585
|
let repoRaw = {};
|
|
26550
26586
|
let repoLoadedPath;
|
|
26551
|
-
if (
|
|
26587
|
+
if (existsSync15(repoConfigFile)) {
|
|
26552
26588
|
try {
|
|
26553
26589
|
const text = readFileSync14(repoConfigFile, "utf8");
|
|
26554
26590
|
repoRaw = (0, import_yaml6.parse)(text) ?? {};
|
|
@@ -26597,7 +26633,7 @@ function configKeyParts(keyPath) {
|
|
|
26597
26633
|
}
|
|
26598
26634
|
function readConfigFile(targetFile2) {
|
|
26599
26635
|
let existing = {};
|
|
26600
|
-
if (
|
|
26636
|
+
if (existsSync15(targetFile2)) {
|
|
26601
26637
|
try {
|
|
26602
26638
|
existing = (0, import_yaml6.parse)(readFileSync14(targetFile2, "utf8")) ?? {};
|
|
26603
26639
|
} catch {
|
|
@@ -26623,7 +26659,8 @@ function setKxmConfigValue(repoRoot, keyPath, value, options = {}) {
|
|
|
26623
26659
|
cursor2 = cursor2[p];
|
|
26624
26660
|
}
|
|
26625
26661
|
cursor2[parts[parts.length - 1]] = value;
|
|
26626
|
-
writeConfigFile(targetFile2, existing);
|
|
26662
|
+
if (!options.dryRun) writeConfigFile(targetFile2, existing);
|
|
26663
|
+
return { file: targetFile2 };
|
|
26627
26664
|
}
|
|
26628
26665
|
function formatKxmConfig(config) {
|
|
26629
26666
|
const display = {
|
|
@@ -26722,13 +26759,13 @@ function rolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
|
26722
26759
|
}
|
|
26723
26760
|
function ensureRolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
26724
26761
|
const dir = rolesDirectory(scope, repoRoot, userConfigDir);
|
|
26725
|
-
if (!
|
|
26762
|
+
if (!existsSync16(dir)) {
|
|
26726
26763
|
mkdirSync12(dir, { recursive: true });
|
|
26727
26764
|
}
|
|
26728
26765
|
return dir;
|
|
26729
26766
|
}
|
|
26730
26767
|
function parseRoleFile(filePath) {
|
|
26731
|
-
if (!
|
|
26768
|
+
if (!existsSync16(filePath)) return void 0;
|
|
26732
26769
|
try {
|
|
26733
26770
|
const raw = readFileSync15(filePath, "utf8");
|
|
26734
26771
|
const parsed = (0, import_yaml7.parse)(raw);
|
|
@@ -26757,7 +26794,7 @@ function listRoles(options = {}) {
|
|
|
26757
26794
|
const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
|
|
26758
26795
|
const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
|
|
26759
26796
|
const localRoles = /* @__PURE__ */ new Map();
|
|
26760
|
-
if (scopeFilter !== "global" &&
|
|
26797
|
+
if (scopeFilter !== "global" && existsSync16(localDir)) {
|
|
26761
26798
|
for (const entry of readdirSync4(localDir)) {
|
|
26762
26799
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
26763
26800
|
const filePath = join17(localDir, entry);
|
|
@@ -26769,7 +26806,7 @@ function listRoles(options = {}) {
|
|
|
26769
26806
|
}
|
|
26770
26807
|
}
|
|
26771
26808
|
const globalRoles = /* @__PURE__ */ new Map();
|
|
26772
|
-
if (scopeFilter !== "local" &&
|
|
26809
|
+
if (scopeFilter !== "local" && existsSync16(globalDir)) {
|
|
26773
26810
|
for (const entry of readdirSync4(globalDir)) {
|
|
26774
26811
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
26775
26812
|
const filePath = join17(globalDir, entry);
|
|
@@ -26835,12 +26872,12 @@ function getRole(roleId, options = {}) {
|
|
|
26835
26872
|
function addRole(role, options = {}) {
|
|
26836
26873
|
const scope = options.scope ?? "local";
|
|
26837
26874
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
26838
|
-
const dir = ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
26875
|
+
const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
26839
26876
|
const filePath = join17(dir, `${role.id}.yaml`);
|
|
26840
|
-
if (
|
|
26877
|
+
if (existsSync16(filePath) && !options.overwrite) {
|
|
26841
26878
|
throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
|
|
26842
26879
|
}
|
|
26843
|
-
writeFileSync11(filePath, (0, import_yaml7.stringify)(role), "utf8");
|
|
26880
|
+
if (!options.dryRun) writeFileSync11(filePath, (0, import_yaml7.stringify)(role), "utf8");
|
|
26844
26881
|
return { id: role.id, filePath, scope };
|
|
26845
26882
|
}
|
|
26846
26883
|
function removeRole(roleId, options = {}) {
|
|
@@ -26848,9 +26885,10 @@ function removeRole(roleId, options = {}) {
|
|
|
26848
26885
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
26849
26886
|
const dir = rolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
26850
26887
|
const filePath = join17(dir, `${roleId}.yaml`);
|
|
26851
|
-
if (!
|
|
26888
|
+
if (!existsSync16(filePath)) {
|
|
26852
26889
|
throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
|
|
26853
26890
|
}
|
|
26891
|
+
if (options.dryRun) return { id: roleId, removed: false, filePath, scope };
|
|
26854
26892
|
rmSync5(filePath);
|
|
26855
26893
|
return { id: roleId, removed: true, filePath, scope };
|
|
26856
26894
|
}
|
|
@@ -26868,9 +26906,9 @@ function modifyRole(roleId, updates, options = {}) {
|
|
|
26868
26906
|
};
|
|
26869
26907
|
const scope = options.scope ?? target.scope;
|
|
26870
26908
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
26871
|
-
const dir = ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
26909
|
+
const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
26872
26910
|
const filePath = join17(dir, `${roleId}.yaml`);
|
|
26873
|
-
writeFileSync11(filePath, (0, import_yaml7.stringify)(updated), "utf8");
|
|
26911
|
+
if (!options.dryRun) writeFileSync11(filePath, (0, import_yaml7.stringify)(updated), "utf8");
|
|
26874
26912
|
return { id: roleId, role: updated, filePath, scope };
|
|
26875
26913
|
}
|
|
26876
26914
|
var KXM_ROLE_HOSTS_SCHEMA = "kxm.role-hosts.v1";
|
|
@@ -26921,13 +26959,13 @@ function findExistingRoleHostsFile(scope, repoRoot = process.cwd(), userConfigDi
|
|
|
26921
26959
|
const yamlPath = join17(dir, "role-hosts.yaml");
|
|
26922
26960
|
const ymlPath = join17(dir, "role-hosts.yml");
|
|
26923
26961
|
const jsonPath = join17(dir, "role-hosts.json");
|
|
26924
|
-
if (
|
|
26925
|
-
if (
|
|
26926
|
-
if (
|
|
26962
|
+
if (existsSync16(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
|
|
26963
|
+
if (existsSync16(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
|
|
26964
|
+
if (existsSync16(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
|
|
26927
26965
|
return { filePath: yamlPath, exists: false, format: "yaml" };
|
|
26928
26966
|
}
|
|
26929
26967
|
function parseRoleHostsFile(filePath) {
|
|
26930
|
-
if (!
|
|
26968
|
+
if (!existsSync16(filePath)) return void 0;
|
|
26931
26969
|
try {
|
|
26932
26970
|
const raw = readFileSync15(filePath, "utf8");
|
|
26933
26971
|
const parsed = (0, import_yaml7.parse)(raw);
|
|
@@ -27001,11 +27039,12 @@ function saveRoleHostsConfig(config, options = {}) {
|
|
|
27001
27039
|
const scope = options.scope ?? "local";
|
|
27002
27040
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
27003
27041
|
const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot);
|
|
27004
|
-
if (!existsSync15(dir)) {
|
|
27005
|
-
mkdirSync12(dir, { recursive: true });
|
|
27006
|
-
}
|
|
27007
27042
|
const format = options.format ?? "yaml";
|
|
27008
27043
|
const filePath = join17(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
|
|
27044
|
+
if (options.dryRun) return { filePath, scope };
|
|
27045
|
+
if (!existsSync16(dir)) {
|
|
27046
|
+
mkdirSync12(dir, { recursive: true });
|
|
27047
|
+
}
|
|
27009
27048
|
const payload = {
|
|
27010
27049
|
schema: KXM_ROLE_HOSTS_SCHEMA,
|
|
27011
27050
|
seats: config.seats ?? {},
|
|
@@ -27040,7 +27079,8 @@ function setRoleSeatHost(seatId, host, options = {}) {
|
|
|
27040
27079
|
scope,
|
|
27041
27080
|
repoRoot: options.repoRoot,
|
|
27042
27081
|
userConfigDir: options.userConfigDir,
|
|
27043
|
-
format: options.format
|
|
27082
|
+
format: options.format,
|
|
27083
|
+
dryRun: options.dryRun
|
|
27044
27084
|
});
|
|
27045
27085
|
return { filePath: saved.filePath, seatId, binding, scope };
|
|
27046
27086
|
}
|
|
@@ -27226,8 +27266,13 @@ async function cmdRoleAdd(runtime, roleId, options) {
|
|
|
27226
27266
|
scope,
|
|
27227
27267
|
repoRoot: runtime.cwd,
|
|
27228
27268
|
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
27229
|
-
overwrite: options.overwrite
|
|
27269
|
+
overwrite: options.overwrite,
|
|
27270
|
+
dryRun: runtime.dryRun
|
|
27230
27271
|
});
|
|
27272
|
+
if (runtime.dryRun) {
|
|
27273
|
+
printPlan(runtime, { command: "role add", roleId, ...res }, [{ action: "write", target: res.filePath }], `add role '${roleId}' to ${res.scope}`);
|
|
27274
|
+
return 0;
|
|
27275
|
+
}
|
|
27231
27276
|
print(
|
|
27232
27277
|
runtime.io,
|
|
27233
27278
|
runtime.json,
|
|
@@ -27246,7 +27291,7 @@ async function cmdRoleAdd(runtime, roleId, options) {
|
|
|
27246
27291
|
}
|
|
27247
27292
|
let roleDef;
|
|
27248
27293
|
if (options.file) {
|
|
27249
|
-
const filePath =
|
|
27294
|
+
const filePath = resolve13(runtime.cwd, options.file);
|
|
27250
27295
|
const content = readFileSync16(filePath, "utf8");
|
|
27251
27296
|
roleDef = (0, import_yaml8.parse)(content);
|
|
27252
27297
|
roleDef.id = roleId;
|
|
@@ -27266,8 +27311,13 @@ async function cmdRoleAdd(runtime, roleId, options) {
|
|
|
27266
27311
|
scope,
|
|
27267
27312
|
repoRoot: runtime.cwd,
|
|
27268
27313
|
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
27269
|
-
overwrite: options.overwrite
|
|
27314
|
+
overwrite: options.overwrite,
|
|
27315
|
+
dryRun: runtime.dryRun
|
|
27270
27316
|
});
|
|
27317
|
+
if (runtime.dryRun) {
|
|
27318
|
+
printPlan(runtime, { command: "role add", roleId, ...res }, [{ action: "write", target: res.filePath }], `add role '${roleId}' to ${res.scope}`);
|
|
27319
|
+
return 0;
|
|
27320
|
+
}
|
|
27271
27321
|
print(
|
|
27272
27322
|
runtime.io,
|
|
27273
27323
|
runtime.json,
|
|
@@ -27314,8 +27364,13 @@ async function cmdRoleRemove(runtime, roleId, options) {
|
|
|
27314
27364
|
const res = removeRole(roleId, {
|
|
27315
27365
|
scope,
|
|
27316
27366
|
repoRoot: runtime.cwd,
|
|
27317
|
-
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
27367
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
27368
|
+
dryRun: runtime.dryRun
|
|
27318
27369
|
});
|
|
27370
|
+
if (runtime.dryRun) {
|
|
27371
|
+
printPlan(runtime, { command: "role remove", roleId, ...res }, [{ action: "delete", target: res.filePath }], `remove role '${roleId}' from ${res.scope}`);
|
|
27372
|
+
return 0;
|
|
27373
|
+
}
|
|
27319
27374
|
print(
|
|
27320
27375
|
runtime.io,
|
|
27321
27376
|
runtime.json,
|
|
@@ -27393,8 +27448,13 @@ async function cmdRoleModify(runtime, roleId, options) {
|
|
|
27393
27448
|
const res = modifyRole(roleId, updates, {
|
|
27394
27449
|
scope: options.scope,
|
|
27395
27450
|
repoRoot: runtime.cwd,
|
|
27396
|
-
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
27451
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
27452
|
+
dryRun: runtime.dryRun
|
|
27397
27453
|
});
|
|
27454
|
+
if (runtime.dryRun) {
|
|
27455
|
+
printPlan(runtime, { command: "role modify", roleId, ...res }, [{ action: "write", target: res.filePath }], `modify role '${roleId}' in ${res.scope}`);
|
|
27456
|
+
return 0;
|
|
27457
|
+
}
|
|
27398
27458
|
print(
|
|
27399
27459
|
runtime.io,
|
|
27400
27460
|
runtime.json,
|
|
@@ -27482,8 +27542,18 @@ async function cmdRoleSetHost(runtime, seatId, host, options = {}) {
|
|
|
27482
27542
|
effort: options.effort,
|
|
27483
27543
|
scope: options.scope ?? "local",
|
|
27484
27544
|
repoRoot: runtime.cwd,
|
|
27485
|
-
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
27545
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
27546
|
+
dryRun: runtime.dryRun
|
|
27486
27547
|
});
|
|
27548
|
+
if (runtime.dryRun) {
|
|
27549
|
+
printPlan(
|
|
27550
|
+
runtime,
|
|
27551
|
+
{ command: "role set-host", seatId, host, binding: result.binding, filePath: result.filePath, scope: result.scope },
|
|
27552
|
+
[{ action: "write", target: result.filePath }],
|
|
27553
|
+
`bind seat '${seatId}' to host '${host}'`
|
|
27554
|
+
);
|
|
27555
|
+
return 0;
|
|
27556
|
+
}
|
|
27487
27557
|
print(
|
|
27488
27558
|
runtime.io,
|
|
27489
27559
|
runtime.json,
|
|
@@ -27515,7 +27585,12 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
27515
27585
|
const projectRoot = discoverKxmProjectRoot(runtime.cwd);
|
|
27516
27586
|
if (projectRoot && /^run_[a-f0-9]{32}$/i.test(runId)) {
|
|
27517
27587
|
if (runtime.dryRun) {
|
|
27518
|
-
|
|
27588
|
+
printPlan(
|
|
27589
|
+
runtime,
|
|
27590
|
+
{ command: "role resume", runId, ruling: effectiveRuling },
|
|
27591
|
+
[{ action: "request", target: `POST kxm-runtime /v1/runs/${runId}/signal (audit_escalation unblock)` }],
|
|
27592
|
+
`resume KXM run ${runId}`
|
|
27593
|
+
);
|
|
27519
27594
|
return 0;
|
|
27520
27595
|
}
|
|
27521
27596
|
try {
|
|
@@ -27547,9 +27622,9 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
27547
27622
|
}
|
|
27548
27623
|
}
|
|
27549
27624
|
const dbPath = join18(runtime.cwd, ".kxm", "state", "kxm.db");
|
|
27550
|
-
if (
|
|
27625
|
+
if (existsSync17(dbPath)) {
|
|
27551
27626
|
try {
|
|
27552
|
-
const database = new DatabaseSync(dbPath);
|
|
27627
|
+
const database = runtime.dryRun ? openReadOnlyDatabase(dbPath) : new DatabaseSync(dbPath);
|
|
27553
27628
|
try {
|
|
27554
27629
|
const row = database.prepare("SELECT record FROM workflow_runs WHERE id = ?").get(runId);
|
|
27555
27630
|
if (!row) {
|
|
@@ -27560,6 +27635,15 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
27560
27635
|
const run = JSON.parse(row.record);
|
|
27561
27636
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
27562
27637
|
const resumeResult = resumeWorkflowFromRuling(run, effectiveRuling, now);
|
|
27638
|
+
if (runtime.dryRun) {
|
|
27639
|
+
printPlan(
|
|
27640
|
+
runtime,
|
|
27641
|
+
{ command: "role resume", runId, stageId: resumeResult.stageId, ruling: effectiveRuling, status: resumeResult.run.status },
|
|
27642
|
+
[{ action: "write", target: `${dbPath} (workflow_runs ${runId}, one workflow_journal decision)` }],
|
|
27643
|
+
`resume workflow run ${runId} (stage: ${resumeResult.stageId})`
|
|
27644
|
+
);
|
|
27645
|
+
return 0;
|
|
27646
|
+
}
|
|
27563
27647
|
database.prepare("UPDATE workflow_runs SET record = ? WHERE id = ?").run(
|
|
27564
27648
|
JSON.stringify(resumeResult.run),
|
|
27565
27649
|
runId
|
|
@@ -27609,12 +27693,12 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
27609
27693
|
|
|
27610
27694
|
// plugins/kxm/src/cli/workflows.ts
|
|
27611
27695
|
import { createHmac as createHmac3, randomUUID as randomUUID7 } from "node:crypto";
|
|
27612
|
-
import { existsSync as
|
|
27613
|
-
import { join as join20, resolve as
|
|
27696
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
|
|
27697
|
+
import { join as join20, resolve as resolve16 } from "node:path";
|
|
27614
27698
|
|
|
27615
27699
|
// plugins/kxm/src/retrospective.ts
|
|
27616
27700
|
import { mkdirSync as mkdirSync13, renameSync as renameSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
27617
|
-
import { resolve as
|
|
27701
|
+
import { resolve as resolve14 } from "node:path";
|
|
27618
27702
|
var MAX_RETROSPECTIVE_ENTRIES = 500;
|
|
27619
27703
|
var SAFE_RUN_ID = /^run_[A-Za-z0-9_-]{1,120}$/;
|
|
27620
27704
|
function durationMs(startedAt, completedAt) {
|
|
@@ -27827,9 +27911,9 @@ function renderRetrospectiveMarkdown(doc) {
|
|
|
27827
27911
|
function writeRetrospective(outDir, doc) {
|
|
27828
27912
|
if (!SAFE_RUN_ID.test(doc.runId)) throw new Error("invalid retrospective run id");
|
|
27829
27913
|
mkdirSync13(outDir, { recursive: true });
|
|
27830
|
-
const root =
|
|
27831
|
-
const jsonPath =
|
|
27832
|
-
const mdPath =
|
|
27914
|
+
const root = resolve14(outDir);
|
|
27915
|
+
const jsonPath = resolve14(root, `${doc.runId}.json`);
|
|
27916
|
+
const mdPath = resolve14(root, `${doc.runId}.md`);
|
|
27833
27917
|
const prefix = `${root}${process.platform === "win32" ? "\\" : "/"}`;
|
|
27834
27918
|
if (!jsonPath.startsWith(prefix) || !mdPath.startsWith(prefix)) throw new Error("retrospective path escaped output directory");
|
|
27835
27919
|
const jsonTmp = `${jsonPath}.tmp`;
|
|
@@ -27929,7 +28013,7 @@ async function watchGithubChecks(input) {
|
|
|
27929
28013
|
}
|
|
27930
28014
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
27931
28015
|
const now = input.now ?? Date.now;
|
|
27932
|
-
const sleep = input.sleep ?? ((ms) => new Promise((
|
|
28016
|
+
const sleep = input.sleep ?? ((ms) => new Promise((resolve32) => setTimeout(resolve32, ms)));
|
|
27933
28017
|
const deadline = now() + input.timeoutMs;
|
|
27934
28018
|
const headers = {
|
|
27935
28019
|
authorization: `Bearer ${token}`,
|
|
@@ -28028,7 +28112,7 @@ async function watchGithubChecks(input) {
|
|
|
28028
28112
|
|
|
28029
28113
|
// plugins/kxm/src/workflow-manager.ts
|
|
28030
28114
|
var import_yaml9 = __toESM(require_dist(), 1);
|
|
28031
|
-
import { existsSync as
|
|
28115
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync5, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync13 } from "node:fs";
|
|
28032
28116
|
import { join as join19 } from "node:path";
|
|
28033
28117
|
var WORKFLOW_TEMPLATES = {
|
|
28034
28118
|
"implement-and-verify": {
|
|
@@ -28186,13 +28270,13 @@ function workflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
|
28186
28270
|
}
|
|
28187
28271
|
function ensureWorkflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
28188
28272
|
const dir = workflowsDirectory(scope, repoRoot, userConfigDir);
|
|
28189
|
-
if (!
|
|
28273
|
+
if (!existsSync18(dir)) {
|
|
28190
28274
|
mkdirSync14(dir, { recursive: true });
|
|
28191
28275
|
}
|
|
28192
28276
|
return dir;
|
|
28193
28277
|
}
|
|
28194
28278
|
function parseWorkflowFile(filePath) {
|
|
28195
|
-
if (!
|
|
28279
|
+
if (!existsSync18(filePath)) return void 0;
|
|
28196
28280
|
try {
|
|
28197
28281
|
const raw = readFileSync17(filePath, "utf8");
|
|
28198
28282
|
const parsed = (0, import_yaml9.parse)(raw);
|
|
@@ -28208,7 +28292,7 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
28208
28292
|
const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
|
|
28209
28293
|
const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
|
|
28210
28294
|
const localDefs = /* @__PURE__ */ new Map();
|
|
28211
|
-
if (scopeFilter !== "global" &&
|
|
28295
|
+
if (scopeFilter !== "global" && existsSync18(localDir)) {
|
|
28212
28296
|
for (const entry of readdirSync5(localDir)) {
|
|
28213
28297
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
28214
28298
|
const filePath = join19(localDir, entry);
|
|
@@ -28221,7 +28305,7 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
28221
28305
|
}
|
|
28222
28306
|
}
|
|
28223
28307
|
const globalDefs = /* @__PURE__ */ new Map();
|
|
28224
|
-
if (scopeFilter !== "local" &&
|
|
28308
|
+
if (scopeFilter !== "local" && existsSync18(globalDir)) {
|
|
28225
28309
|
for (const entry of readdirSync5(globalDir)) {
|
|
28226
28310
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
28227
28311
|
const filePath = join19(globalDir, entry);
|
|
@@ -28295,13 +28379,13 @@ function getWorkflowDefinition(workflowId, options = {}) {
|
|
|
28295
28379
|
function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE, options = {}) {
|
|
28296
28380
|
const scope = options.scope ?? "local";
|
|
28297
28381
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28298
|
-
const dir = ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
28382
|
+
const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
28299
28383
|
const filePath = join19(dir, `${workflowId}.yaml`);
|
|
28300
|
-
if (
|
|
28384
|
+
if (existsSync18(filePath) && !options.overwrite) {
|
|
28301
28385
|
throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
|
|
28302
28386
|
}
|
|
28303
28387
|
const payload = typeof content === "string" ? content : (0, import_yaml9.stringify)(content);
|
|
28304
|
-
writeFileSync13(filePath, payload, "utf8");
|
|
28388
|
+
if (!options.dryRun) writeFileSync13(filePath, payload, "utf8");
|
|
28305
28389
|
return { id: workflowId, filePath, scope };
|
|
28306
28390
|
}
|
|
28307
28391
|
function removeWorkflowDefinition(workflowId, options = {}) {
|
|
@@ -28309,9 +28393,10 @@ function removeWorkflowDefinition(workflowId, options = {}) {
|
|
|
28309
28393
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28310
28394
|
const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
28311
28395
|
const filePath = join19(dir, `${workflowId}.yaml`);
|
|
28312
|
-
if (!
|
|
28396
|
+
if (!existsSync18(filePath)) {
|
|
28313
28397
|
throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
|
|
28314
28398
|
}
|
|
28399
|
+
if (options.dryRun) return { id: workflowId, removed: false, filePath, scope };
|
|
28315
28400
|
rmSync6(filePath);
|
|
28316
28401
|
return { id: workflowId, removed: true, filePath, scope };
|
|
28317
28402
|
}
|
|
@@ -28326,17 +28411,17 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
|
|
|
28326
28411
|
};
|
|
28327
28412
|
const scope = options.scope ?? target.scope;
|
|
28328
28413
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28329
|
-
const dir = ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
28414
|
+
const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
28330
28415
|
const filePath = join19(dir, `${workflowId}.yaml`);
|
|
28331
|
-
writeFileSync13(filePath, (0, import_yaml9.stringify)(updated), "utf8");
|
|
28416
|
+
if (!options.dryRun) writeFileSync13(filePath, (0, import_yaml9.stringify)(updated), "utf8");
|
|
28332
28417
|
return { id: workflowId, workflow: updated, filePath, scope };
|
|
28333
28418
|
}
|
|
28334
28419
|
|
|
28335
28420
|
// plugins/kxm/src/cli/workflows.ts
|
|
28336
28421
|
var CLI_NAME = "kxm";
|
|
28337
28422
|
function localWorkflowSnapshot(dataPath, runId) {
|
|
28338
|
-
if (!
|
|
28339
|
-
const database =
|
|
28423
|
+
if (!existsSync19(dataPath)) throw new Error("state_database_not_found");
|
|
28424
|
+
const database = openReadOnlyDatabase(dataPath);
|
|
28340
28425
|
try {
|
|
28341
28426
|
const rows = runId ? database.prepare("SELECT record FROM workflow_runs WHERE id = ?").all(runId) : database.prepare("SELECT record FROM workflow_runs ORDER BY rowid DESC LIMIT 200").all();
|
|
28342
28427
|
const runs = rows.map((row) => JSON.parse(row.record));
|
|
@@ -28412,7 +28497,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
|
|
|
28412
28497
|
let raw;
|
|
28413
28498
|
if (file) {
|
|
28414
28499
|
try {
|
|
28415
|
-
raw = readFileSync18(
|
|
28500
|
+
raw = readFileSync18(resolve16(runtime.cwd, file), "utf8");
|
|
28416
28501
|
} catch {
|
|
28417
28502
|
throw new Error("workflow definition file is unavailable");
|
|
28418
28503
|
}
|
|
@@ -28493,7 +28578,7 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
|
|
|
28493
28578
|
}
|
|
28494
28579
|
}
|
|
28495
28580
|
if (options.file) {
|
|
28496
|
-
const filePath =
|
|
28581
|
+
const filePath = resolve16(runtime.cwd, options.file);
|
|
28497
28582
|
content = readFileSync18(filePath, "utf8");
|
|
28498
28583
|
} else if (!content) {
|
|
28499
28584
|
content = {
|
|
@@ -28517,8 +28602,13 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
|
|
|
28517
28602
|
scope,
|
|
28518
28603
|
repoRoot: runtime.cwd,
|
|
28519
28604
|
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
28520
|
-
overwrite: options.overwrite
|
|
28605
|
+
overwrite: options.overwrite,
|
|
28606
|
+
dryRun: runtime.dryRun
|
|
28521
28607
|
});
|
|
28608
|
+
if (runtime.dryRun) {
|
|
28609
|
+
printPlan(runtime, { command: "workflow add", workflowId, ...res }, [{ action: "write", target: res.filePath }], `add workflow '${workflowId}' to ${res.scope}`);
|
|
28610
|
+
return 0;
|
|
28611
|
+
}
|
|
28522
28612
|
print(
|
|
28523
28613
|
runtime.io,
|
|
28524
28614
|
runtime.json,
|
|
@@ -28565,8 +28655,13 @@ async function cmdWorkflowRemove(runtime, workflowId, options) {
|
|
|
28565
28655
|
const res = removeWorkflowDefinition(workflowId, {
|
|
28566
28656
|
scope,
|
|
28567
28657
|
repoRoot: runtime.cwd,
|
|
28568
|
-
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
28658
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
28659
|
+
dryRun: runtime.dryRun
|
|
28569
28660
|
});
|
|
28661
|
+
if (runtime.dryRun) {
|
|
28662
|
+
printPlan(runtime, { command: "workflow remove", workflowId, ...res }, [{ action: "delete", target: res.filePath }], `remove workflow '${workflowId}' from ${res.scope}`);
|
|
28663
|
+
return 0;
|
|
28664
|
+
}
|
|
28570
28665
|
print(
|
|
28571
28666
|
runtime.io,
|
|
28572
28667
|
runtime.json,
|
|
@@ -28614,9 +28709,14 @@ async function cmdWorkflowModify(runtime, workflowId, options) {
|
|
|
28614
28709
|
{
|
|
28615
28710
|
scope: options.scope,
|
|
28616
28711
|
repoRoot: runtime.cwd,
|
|
28617
|
-
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
28712
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
28713
|
+
dryRun: runtime.dryRun
|
|
28618
28714
|
}
|
|
28619
28715
|
);
|
|
28716
|
+
if (runtime.dryRun) {
|
|
28717
|
+
printPlan(runtime, { command: "workflow modify", workflowId, ...res }, [{ action: "write", target: res.filePath }], `modify workflow '${workflowId}' in ${res.scope}`);
|
|
28718
|
+
return 0;
|
|
28719
|
+
}
|
|
28620
28720
|
print(
|
|
28621
28721
|
runtime.io,
|
|
28622
28722
|
runtime.json,
|
|
@@ -28652,7 +28752,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
|
|
|
28652
28752
|
}
|
|
28653
28753
|
let payload;
|
|
28654
28754
|
try {
|
|
28655
|
-
const raw = payloadFlag.startsWith("@") ? readFileSync18(
|
|
28755
|
+
const raw = payloadFlag.startsWith("@") ? readFileSync18(resolve16(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
|
|
28656
28756
|
const value = JSON.parse(raw);
|
|
28657
28757
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
|
|
28658
28758
|
payload = value;
|
|
@@ -28719,7 +28819,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
|
|
|
28719
28819
|
}
|
|
28720
28820
|
}
|
|
28721
28821
|
async function cmdWorkflowInspect(runtime, action, runId) {
|
|
28722
|
-
const dataPath =
|
|
28822
|
+
const dataPath = resolve16(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join20(runtime.dirs.state, "kxm.db"));
|
|
28723
28823
|
if (action === "get" && !runId) {
|
|
28724
28824
|
runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
|
|
28725
28825
|
`);
|
|
@@ -28761,7 +28861,7 @@ async function cmdSignal(runtime, runId, signalKey, status, summary, evidenceArg
|
|
|
28761
28861
|
const projectRoot = discoverKxmProjectRoot(runtime.cwd);
|
|
28762
28862
|
if (projectRoot && /^run_[a-f0-9]{32}$/i.test(runId)) {
|
|
28763
28863
|
if (runtime.dryRun) {
|
|
28764
|
-
printWorker(runtime, worker, { ok: true, command: "signal", runId, signalKey, status, summary, evidence }, "would post signal to KXM run");
|
|
28864
|
+
printWorker(runtime, worker, { ok: true, command: "signal", dryRun: true, runId, signalKey, status, summary, evidence }, "would post signal to KXM run");
|
|
28765
28865
|
return 0;
|
|
28766
28866
|
}
|
|
28767
28867
|
const deliveryId2 = String(deliveryIdFlag || `cli-signal:${randomUUID7()}`);
|
|
@@ -28797,7 +28897,7 @@ async function cmdSignal(runtime, runId, signalKey, status, summary, evidenceArg
|
|
|
28797
28897
|
return 2;
|
|
28798
28898
|
}
|
|
28799
28899
|
if (runtime.dryRun) {
|
|
28800
|
-
printWorker(runtime, worker, { ok: true, command: "signal", runId, signalKey, status, summary, evidence }, "would post signed signal");
|
|
28900
|
+
printWorker(runtime, worker, { ok: true, command: "signal", dryRun: true, runId, signalKey, status, summary, evidence }, "would post signed signal");
|
|
28801
28901
|
return 0;
|
|
28802
28902
|
}
|
|
28803
28903
|
const deliveryId = String(deliveryIdFlag || `cli-signal:${randomUUID7()}`);
|
|
@@ -28888,14 +28988,14 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
28888
28988
|
return 2;
|
|
28889
28989
|
}
|
|
28890
28990
|
const snapshotFlag = String(options.input || "");
|
|
28891
|
-
const snapshotPath = snapshotFlag ?
|
|
28991
|
+
const snapshotPath = snapshotFlag ? resolve16(runtime.cwd, snapshotFlag) : "";
|
|
28892
28992
|
let snapshot;
|
|
28893
28993
|
try {
|
|
28894
28994
|
if (snapshotPath) {
|
|
28895
|
-
if (!
|
|
28995
|
+
if (!existsSync19(snapshotPath)) throw new Error("snapshot_missing");
|
|
28896
28996
|
snapshot = JSON.parse(readFileSync18(snapshotPath, "utf8"));
|
|
28897
28997
|
} else {
|
|
28898
|
-
const dataPath =
|
|
28998
|
+
const dataPath = resolve16(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join20(runtime.dirs.state, "kxm.db"));
|
|
28899
28999
|
const local = localWorkflowSnapshot(dataPath, runId);
|
|
28900
29000
|
if (!local.runs[0]) throw new Error("workflow_not_found");
|
|
28901
29001
|
snapshot = { run: local.runs[0], journal: local.journal };
|
|
@@ -28907,8 +29007,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
28907
29007
|
return 1;
|
|
28908
29008
|
}
|
|
28909
29009
|
const doc = buildRetrospective(snapshot.run, snapshot.journal);
|
|
28910
|
-
const outDir =
|
|
28911
|
-
const assetsRoot =
|
|
29010
|
+
const outDir = resolve16(runtime.cwd, String(options.outDir || join20(runtime.dirs.assets, "retrospectives")));
|
|
29011
|
+
const assetsRoot = resolve16(runtime.dirs.assets);
|
|
28912
29012
|
const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
|
|
28913
29013
|
if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
|
|
28914
29014
|
print(runtime.io, runtime.json, { ok: false, command: "retrospective export", error: "output_outside_workspace_assets" }, "retrospectives must stay under the workspace assets directory");
|
|
@@ -28925,8 +29025,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
28925
29025
|
|
|
28926
29026
|
// plugins/kxm/src/cli/tasks.ts
|
|
28927
29027
|
var import_yaml12 = __toESM(require_dist(), 1);
|
|
28928
|
-
import { existsSync as
|
|
28929
|
-
import { basename as basename6, join as join27, resolve as
|
|
29028
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "node:fs";
|
|
29029
|
+
import { basename as basename6, join as join27, resolve as resolve21 } from "node:path";
|
|
28930
29030
|
|
|
28931
29031
|
// plugins/kxm/src/suggest.ts
|
|
28932
29032
|
var WORKFLOW_PATTERNS = [
|
|
@@ -29037,19 +29137,23 @@ function suggestWorkflowAndRoles(prompt, options = {}) {
|
|
|
29037
29137
|
// plugins/kxm/src/task-manager.ts
|
|
29038
29138
|
var import_yaml10 = __toESM(require_dist(), 1);
|
|
29039
29139
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
29040
|
-
import { existsSync as
|
|
29041
|
-
import { join as join21, resolve as
|
|
29140
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync19, readdirSync as readdirSync6, writeFileSync as writeFileSync14 } from "node:fs";
|
|
29141
|
+
import { join as join21, resolve as resolve17 } from "node:path";
|
|
29042
29142
|
var GOAL_SCHEMA = "kxm.goal.v1";
|
|
29043
29143
|
var TASK_SCHEMA = "kxm.task.v1";
|
|
29044
29144
|
function goalsDirectory(repoRoot) {
|
|
29045
|
-
return
|
|
29145
|
+
return resolve17(repoRoot, ".kxm", "goals");
|
|
29046
29146
|
}
|
|
29047
29147
|
function tasksDirectory(repoRoot) {
|
|
29048
|
-
return
|
|
29148
|
+
return resolve17(repoRoot, ".kxm", "tasks");
|
|
29049
29149
|
}
|
|
29050
|
-
function
|
|
29051
|
-
|
|
29052
|
-
|
|
29150
|
+
function goalFilePath(repoRoot, goalId) {
|
|
29151
|
+
return join21(goalsDirectory(repoRoot), `${goalId}.yaml`);
|
|
29152
|
+
}
|
|
29153
|
+
function taskFilePath(repoRoot, taskId) {
|
|
29154
|
+
return join21(tasksDirectory(repoRoot), `${taskId}.yaml`);
|
|
29155
|
+
}
|
|
29156
|
+
function createGoal(repoRoot, input, options = {}) {
|
|
29053
29157
|
const id = `goal_${randomUUID8().replaceAll("-", "").slice(0, 12)}`;
|
|
29054
29158
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29055
29159
|
const record = {
|
|
@@ -29063,13 +29167,14 @@ function createGoal(repoRoot, input) {
|
|
|
29063
29167
|
createdAt: now,
|
|
29064
29168
|
updatedAt: now
|
|
29065
29169
|
};
|
|
29066
|
-
|
|
29067
|
-
|
|
29170
|
+
if (options.dryRun) return record;
|
|
29171
|
+
mkdirSync15(goalsDirectory(repoRoot), { recursive: true });
|
|
29172
|
+
writeFileSync14(goalFilePath(repoRoot, id), (0, import_yaml10.stringify)(record).trim() + "\n", "utf8");
|
|
29068
29173
|
return record;
|
|
29069
29174
|
}
|
|
29070
29175
|
function listGoals(repoRoot) {
|
|
29071
29176
|
const dir = goalsDirectory(repoRoot);
|
|
29072
|
-
if (!
|
|
29177
|
+
if (!existsSync20(dir)) return [];
|
|
29073
29178
|
const goals = [];
|
|
29074
29179
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
29075
29180
|
for (const entry of entries) {
|
|
@@ -29084,9 +29189,7 @@ function listGoals(repoRoot) {
|
|
|
29084
29189
|
}
|
|
29085
29190
|
return goals.sort((a, b2) => a.createdAt.localeCompare(b2.createdAt));
|
|
29086
29191
|
}
|
|
29087
|
-
function createTask(repoRoot, input) {
|
|
29088
|
-
const dir = tasksDirectory(repoRoot);
|
|
29089
|
-
mkdirSync15(dir, { recursive: true });
|
|
29192
|
+
function createTask(repoRoot, input, options = {}) {
|
|
29090
29193
|
const id = `task_${randomUUID8().replaceAll("-", "").slice(0, 12)}`;
|
|
29091
29194
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29092
29195
|
const record = {
|
|
@@ -29108,13 +29211,14 @@ function createTask(repoRoot, input) {
|
|
|
29108
29211
|
createdAt: now,
|
|
29109
29212
|
updatedAt: now
|
|
29110
29213
|
};
|
|
29111
|
-
|
|
29112
|
-
|
|
29214
|
+
if (options.dryRun) return record;
|
|
29215
|
+
mkdirSync15(tasksDirectory(repoRoot), { recursive: true });
|
|
29216
|
+
writeFileSync14(taskFilePath(repoRoot, id), (0, import_yaml10.stringify)(record).trim() + "\n", "utf8");
|
|
29113
29217
|
return record;
|
|
29114
29218
|
}
|
|
29115
29219
|
function listTasks(repoRoot, options = {}) {
|
|
29116
29220
|
const dir = tasksDirectory(repoRoot);
|
|
29117
|
-
if (!
|
|
29221
|
+
if (!existsSync20(dir)) return [];
|
|
29118
29222
|
const tasks = [];
|
|
29119
29223
|
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
29120
29224
|
for (const entry of entries) {
|
|
@@ -29136,7 +29240,7 @@ function listTasks(repoRoot, options = {}) {
|
|
|
29136
29240
|
function getTask(repoRoot, taskId) {
|
|
29137
29241
|
const dir = tasksDirectory(repoRoot);
|
|
29138
29242
|
const filePath = join21(dir, `${taskId}.yaml`);
|
|
29139
|
-
if (!
|
|
29243
|
+
if (!existsSync20(filePath)) return void 0;
|
|
29140
29244
|
try {
|
|
29141
29245
|
const text = readFileSync19(filePath, "utf8");
|
|
29142
29246
|
const doc = (0, import_yaml10.parse)(text);
|
|
@@ -29153,8 +29257,7 @@ function updateTaskStatus(repoRoot, taskId, status, options = {}) {
|
|
|
29153
29257
|
task.status = status;
|
|
29154
29258
|
if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
|
|
29155
29259
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29156
|
-
|
|
29157
|
-
writeFileSync14(filePath, (0, import_yaml10.stringify)(task).trim() + "\n", "utf8");
|
|
29260
|
+
if (!options.dryRun) writeFileSync14(taskFilePath(repoRoot, taskId), (0, import_yaml10.stringify)(task).trim() + "\n", "utf8");
|
|
29158
29261
|
return task;
|
|
29159
29262
|
}
|
|
29160
29263
|
function syncTaskWithTracker(repoRoot, taskId, options = {}) {
|
|
@@ -29169,8 +29272,7 @@ function syncTaskWithTracker(repoRoot, taskId, options = {}) {
|
|
|
29169
29272
|
task.trackerSync.syncStatus = "synced";
|
|
29170
29273
|
task.trackerSync.lastSyncedAt = now;
|
|
29171
29274
|
task.updatedAt = now;
|
|
29172
|
-
|
|
29173
|
-
writeFileSync14(filePath, (0, import_yaml10.stringify)(task).trim() + "\n", "utf8");
|
|
29275
|
+
if (!options.dryRun) writeFileSync14(taskFilePath(repoRoot, taskId), (0, import_yaml10.stringify)(task).trim() + "\n", "utf8");
|
|
29174
29276
|
return task;
|
|
29175
29277
|
}
|
|
29176
29278
|
|
|
@@ -29936,7 +30038,8 @@ function createStudioServer(options = {}) {
|
|
|
29936
30038
|
}
|
|
29937
30039
|
|
|
29938
30040
|
// plugins/kxm/src/cli/project.ts
|
|
29939
|
-
import {
|
|
30041
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
30042
|
+
import { join as join26, resolve as resolve20 } from "node:path";
|
|
29940
30043
|
import { createInterface as createInterface2 } from "node:readline";
|
|
29941
30044
|
|
|
29942
30045
|
// plugins/kxm/src/model-inventory.ts
|
|
@@ -30035,7 +30138,7 @@ async function refreshModelInventory(options) {
|
|
|
30035
30138
|
|
|
30036
30139
|
// plugins/kxm/src/init.ts
|
|
30037
30140
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
30038
|
-
import { existsSync as
|
|
30141
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
30039
30142
|
import { basename as basename5, join as join25 } from "node:path";
|
|
30040
30143
|
|
|
30041
30144
|
// plugins/kxm/src/repair.ts
|
|
@@ -30043,7 +30146,7 @@ import { randomUUID as randomUUID10 } from "node:crypto";
|
|
|
30043
30146
|
import {
|
|
30044
30147
|
chmodSync as chmodSync5,
|
|
30045
30148
|
closeSync as closeSync2,
|
|
30046
|
-
existsSync as
|
|
30149
|
+
existsSync as existsSync22,
|
|
30047
30150
|
fsyncSync as fsyncSync2,
|
|
30048
30151
|
linkSync,
|
|
30049
30152
|
lstatSync as lstatSync7,
|
|
@@ -30055,14 +30158,14 @@ import {
|
|
|
30055
30158
|
rmSync as rmSync8,
|
|
30056
30159
|
writeFileSync as writeFileSync17
|
|
30057
30160
|
} from "node:fs";
|
|
30058
|
-
import { dirname as dirname15, isAbsolute as isAbsolute7, join as join24, relative as relative4, resolve as
|
|
30161
|
+
import { dirname as dirname15, isAbsolute as isAbsolute7, join as join24, relative as relative4, resolve as resolve19 } from "node:path";
|
|
30059
30162
|
|
|
30060
30163
|
// plugins/kxm/src/permission.ts
|
|
30061
30164
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
30062
30165
|
import { createHash as createHash13 } from "node:crypto";
|
|
30063
|
-
import { existsSync as
|
|
30166
|
+
import { existsSync as existsSync21, mkdtempSync, mkdirSync as mkdirSync17, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
30064
30167
|
import { tmpdir } from "node:os";
|
|
30065
|
-
import { dirname as dirname14, join as join23, resolve as
|
|
30168
|
+
import { dirname as dirname14, join as join23, resolve as resolve18, sep as sep3 } from "node:path";
|
|
30066
30169
|
var PROSE_FIELDS = {
|
|
30067
30170
|
project: /* @__PURE__ */ new Set(["name", "description"]),
|
|
30068
30171
|
repository: /* @__PURE__ */ new Set(["description"]),
|
|
@@ -30584,7 +30687,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30584
30687
|
const listing = gitBuffer(root, ["ls-tree", "-r", "-z", "--name-only", tree]).toString("utf8");
|
|
30585
30688
|
const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
30586
30689
|
const shadow = mkdtempSync(join23(tmpdir(), "kxm-permission-base-"));
|
|
30587
|
-
const shadowResolved =
|
|
30690
|
+
const shadowResolved = resolve18(shadow);
|
|
30588
30691
|
const shadowMembersDir = `.kxm-shadow-members-${createHash13("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
|
|
30589
30692
|
try {
|
|
30590
30693
|
for (const path4 of paths) {
|
|
@@ -30598,7 +30701,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30598
30701
|
}]);
|
|
30599
30702
|
}
|
|
30600
30703
|
const absolute = join23(shadowResolved, ...segments);
|
|
30601
|
-
const resolved =
|
|
30704
|
+
const resolved = resolve18(absolute);
|
|
30602
30705
|
if (!resolved.startsWith(`${shadowResolved}${sep3}`)) {
|
|
30603
30706
|
throw new KxmConfigError([{
|
|
30604
30707
|
phase: "path",
|
|
@@ -30642,8 +30745,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30642
30745
|
}]);
|
|
30643
30746
|
}
|
|
30644
30747
|
const boundPath = options.repositoryBindings?.[member.repositoryId];
|
|
30645
|
-
const memberWorktree = boundPath ?
|
|
30646
|
-
if (!memberWorktree || !
|
|
30748
|
+
const memberWorktree = boundPath ? resolve18(boundPath) : member.pathHint ? resolve18(root, ...member.pathHint.split("/")) : void 0;
|
|
30749
|
+
if (!memberWorktree || !existsSync21(memberWorktree)) {
|
|
30647
30750
|
if (member.required === false) continue;
|
|
30648
30751
|
throw new KxmConfigError([{
|
|
30649
30752
|
phase: "discovery",
|
|
@@ -30691,7 +30794,7 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30691
30794
|
const memberListing = gitBuffer(memberWorktree, ["ls-tree", "-r", "-z", "--name-only", memberTree]).toString("utf8");
|
|
30692
30795
|
const memberPaths = memberListing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
30693
30796
|
const memberShadowRoot = join23(shadowResolved, shadowMembersDir, member.repositoryId);
|
|
30694
|
-
if (!
|
|
30797
|
+
if (!resolve18(memberShadowRoot).startsWith(`${resolve18(shadowResolved, shadowMembersDir)}${sep3}`)) {
|
|
30695
30798
|
throw new KxmConfigError([{
|
|
30696
30799
|
phase: "path",
|
|
30697
30800
|
code: "git_tree_path_invalid",
|
|
@@ -30705,8 +30808,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
30705
30808
|
throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry contains a traversal or non-portable segment" }]);
|
|
30706
30809
|
}
|
|
30707
30810
|
const absolute = join23(memberShadowRoot, ...segments);
|
|
30708
|
-
const resolved =
|
|
30709
|
-
if (!resolved.startsWith(`${
|
|
30811
|
+
const resolved = resolve18(absolute);
|
|
30812
|
+
if (!resolved.startsWith(`${resolve18(memberShadowRoot)}${sep3}`)) {
|
|
30710
30813
|
throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry escapes the shadow root" }]);
|
|
30711
30814
|
}
|
|
30712
30815
|
const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
|
|
@@ -30732,7 +30835,7 @@ function portableMemberPathHint(pathHint) {
|
|
|
30732
30835
|
return pathHint !== "." && kxmPortablePath(pathHint);
|
|
30733
30836
|
}
|
|
30734
30837
|
function loadBaseProjectDeclarations(projectFile) {
|
|
30735
|
-
if (!
|
|
30838
|
+
if (!existsSync21(projectFile)) return [];
|
|
30736
30839
|
const value = parseRestrictedYaml2(readFileSync20(projectFile, "utf8"), ".kxm/project.yaml");
|
|
30737
30840
|
const repositories = Array.isArray(value?.repositories) ? value.repositories : [];
|
|
30738
30841
|
const members = [];
|
|
@@ -30856,12 +30959,12 @@ function writeDurable(file, bytes, mode) {
|
|
|
30856
30959
|
syncDirectory2(dirname15(file));
|
|
30857
30960
|
}
|
|
30858
30961
|
function sameHostPath3(left, right) {
|
|
30859
|
-
const first =
|
|
30860
|
-
const second =
|
|
30962
|
+
const first = resolve19(left);
|
|
30963
|
+
const second = resolve19(right);
|
|
30861
30964
|
return process.platform === "win32" ? first.toLocaleLowerCase("en-US") === second.toLocaleLowerCase("en-US") : first === second;
|
|
30862
30965
|
}
|
|
30863
30966
|
function transactionRoot(projectRoot) {
|
|
30864
|
-
return join24(
|
|
30967
|
+
return join24(resolve19(projectRoot), TRANSACTION_NAME);
|
|
30865
30968
|
}
|
|
30866
30969
|
function operationFile(projectRoot) {
|
|
30867
30970
|
return join24(transactionRoot(projectRoot), OPERATION_FILE);
|
|
@@ -30886,9 +30989,9 @@ function readRegularBounded(file, label) {
|
|
|
30886
30989
|
}
|
|
30887
30990
|
function readOptionalManaged(projectRoot, path4) {
|
|
30888
30991
|
const file = join24(projectRoot, ...path4.split("/"));
|
|
30889
|
-
if (!
|
|
30992
|
+
if (!existsSync22(file)) return void 0;
|
|
30890
30993
|
let parent = dirname15(file);
|
|
30891
|
-
const boundary =
|
|
30994
|
+
const boundary = resolve19(projectRoot);
|
|
30892
30995
|
while (parent !== boundary) {
|
|
30893
30996
|
const stat = lstatSync7(parent);
|
|
30894
30997
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_parent_invalid", path4, "managed file parents must be regular directories", "path");
|
|
@@ -30899,7 +31002,7 @@ function readOptionalManaged(projectRoot, path4) {
|
|
|
30899
31002
|
function readProjectAndProvenance(projectRoot, schemasDir) {
|
|
30900
31003
|
const projectFile = join24(projectRoot, ".kxm", "project.yaml");
|
|
30901
31004
|
const provenanceFile = join24(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
|
|
30902
|
-
if (!
|
|
31005
|
+
if (!existsSync22(projectFile) || !existsSync22(provenanceFile)) return void 0;
|
|
30903
31006
|
const projectBytes = readOptionalManaged(projectRoot, ".kxm/project.yaml");
|
|
30904
31007
|
const provenanceBytes = readOptionalManaged(projectRoot, KXM_TEMPLATE_PROVENANCE_PATH);
|
|
30905
31008
|
if (!projectBytes || !provenanceBytes) return void 0;
|
|
@@ -30935,7 +31038,7 @@ function readProjectAndProvenance(projectRoot, schemasDir) {
|
|
|
30935
31038
|
}
|
|
30936
31039
|
function planKxmTemplateRepair(projectRoot, options = {}) {
|
|
30937
31040
|
assertNoRegisteredGates(options);
|
|
30938
|
-
const root =
|
|
31041
|
+
const root = resolve19(projectRoot);
|
|
30939
31042
|
const source = readProjectAndProvenance(root, options.schemasDir);
|
|
30940
31043
|
if (!source) return void 0;
|
|
30941
31044
|
const target = renderKxmTemplate(
|
|
@@ -31080,11 +31183,11 @@ function updatePhase(projectRoot, operation, phase) {
|
|
|
31080
31183
|
}
|
|
31081
31184
|
function readOperation(projectRoot, schemasDir) {
|
|
31082
31185
|
const root = transactionRoot(projectRoot);
|
|
31083
|
-
if (!
|
|
31186
|
+
if (!existsSync22(root)) return void 0;
|
|
31084
31187
|
const stat = lstatSync7(root);
|
|
31085
31188
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("init_transaction_invalid", TRANSACTION_NAME, "initialization transaction must be a regular directory", "path");
|
|
31086
31189
|
const file = operationFile(projectRoot);
|
|
31087
|
-
if (!
|
|
31190
|
+
if (!existsSync22(file)) {
|
|
31088
31191
|
const entries = readdirSync7(root, { withFileTypes: true });
|
|
31089
31192
|
const recoverable = entries.every((entry) => entry.isFile() && /^\.operation-op_[a-f0-9]{32}-[a-f0-9-]{36}\.tmp$/.test(entry.name));
|
|
31090
31193
|
if (recoverable) return void 0;
|
|
@@ -31112,7 +31215,7 @@ function readOperation(projectRoot, schemasDir) {
|
|
|
31112
31215
|
return operation;
|
|
31113
31216
|
}
|
|
31114
31217
|
function hasKxmInitTransaction(projectRoot) {
|
|
31115
|
-
return
|
|
31218
|
+
return existsSync22(transactionRoot(projectRoot));
|
|
31116
31219
|
}
|
|
31117
31220
|
function readTarget(projectRoot, path4) {
|
|
31118
31221
|
return readOptionalManaged(join24(transactionRoot(projectRoot), "targets"), path4);
|
|
@@ -31189,10 +31292,10 @@ function sourceConfigFiles(projectRoot) {
|
|
|
31189
31292
|
".kxm/repo/repo.yaml",
|
|
31190
31293
|
".kxm/repo/env.yaml"
|
|
31191
31294
|
];
|
|
31192
|
-
const files = fixed.filter((path4) =>
|
|
31295
|
+
const files = fixed.filter((path4) => existsSync22(join24(projectRoot, ...path4.split("/"))));
|
|
31193
31296
|
for (const directory of ["agents", "models", "workflows"]) {
|
|
31194
31297
|
const absolute = join24(projectRoot, ".kxm", directory);
|
|
31195
|
-
if (!
|
|
31298
|
+
if (!existsSync22(absolute)) continue;
|
|
31196
31299
|
const stat = lstatSync7(absolute);
|
|
31197
31300
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_directory_invalid", `.kxm/${directory}`, "configuration directory must be regular", "path");
|
|
31198
31301
|
for (const entry of readdirSync7(absolute, { withFileTypes: true })) {
|
|
@@ -31213,8 +31316,8 @@ function absoluteMemberBindings(projectRoot, project, explicit = {}) {
|
|
|
31213
31316
|
const repository = candidate;
|
|
31214
31317
|
if (repository.role !== "member" || typeof repository.id !== "string" || Object.hasOwn(bindings, repository.id)) continue;
|
|
31215
31318
|
if (typeof repository.pathHint === "string") {
|
|
31216
|
-
const candidate2 =
|
|
31217
|
-
if (
|
|
31319
|
+
const candidate2 = resolve19(projectRoot, ...repository.pathHint.split("/"));
|
|
31320
|
+
if (existsSync22(candidate2)) bindings[repository.id] = candidate2;
|
|
31218
31321
|
}
|
|
31219
31322
|
}
|
|
31220
31323
|
return bindings;
|
|
@@ -31240,7 +31343,7 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
31240
31343
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
31241
31344
|
const destination = join24(shadow, ...entry.path.split("/"));
|
|
31242
31345
|
mkdirSync18(dirname15(destination), { recursive: true, mode: 448 });
|
|
31243
|
-
const mode =
|
|
31346
|
+
const mode = existsSync22(destination) ? lstatSync7(destination).mode & 511 : 420;
|
|
31244
31347
|
writeDurable(destination, bytes, mode);
|
|
31245
31348
|
}
|
|
31246
31349
|
const project = parseRestrictedYaml2(readFileSync21(join24(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
|
|
@@ -31252,7 +31355,7 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
31252
31355
|
}
|
|
31253
31356
|
}
|
|
31254
31357
|
function createOperation(projectRoot, kind, rendered, files, sourceTemplateRevision) {
|
|
31255
|
-
const root =
|
|
31358
|
+
const root = resolve19(projectRoot);
|
|
31256
31359
|
const operationWithoutHash = {
|
|
31257
31360
|
schema: "kxm.init-operation.v1",
|
|
31258
31361
|
operationId: `op_${randomUUID10().replaceAll("-", "")}`,
|
|
@@ -31281,7 +31384,7 @@ function prepareOperation(projectRoot, operation, rendered, options) {
|
|
|
31281
31384
|
const transaction = transactionRoot(projectRoot);
|
|
31282
31385
|
validateOperationRecord(operation, options.schemasDir);
|
|
31283
31386
|
validateOperationIntent(projectRoot, operation);
|
|
31284
|
-
if (
|
|
31387
|
+
if (existsSync22(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
|
|
31285
31388
|
mkdirSync18(transaction, { mode: 448 });
|
|
31286
31389
|
syncDirectory2(projectRoot);
|
|
31287
31390
|
writeOperation(projectRoot, operation);
|
|
@@ -31303,19 +31406,19 @@ function prepareOperation(projectRoot, operation, rendered, options) {
|
|
|
31303
31406
|
}
|
|
31304
31407
|
}
|
|
31305
31408
|
function ensureDestinationParents(projectRoot, path4) {
|
|
31306
|
-
const boundary =
|
|
31409
|
+
const boundary = resolve19(projectRoot);
|
|
31307
31410
|
const segments = path4.split("/").slice(0, -1);
|
|
31308
31411
|
let current = boundary;
|
|
31309
31412
|
for (const segment of segments) {
|
|
31310
31413
|
current = join24(current, segment);
|
|
31311
|
-
if (!
|
|
31414
|
+
if (!existsSync22(current)) mkdirSync18(current, { mode: 448 });
|
|
31312
31415
|
const stat = lstatSync7(current);
|
|
31313
31416
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_destination_parent_invalid", path4, "destination parent must be a regular directory", "path");
|
|
31314
31417
|
}
|
|
31315
31418
|
}
|
|
31316
31419
|
function destinationSha(projectRoot, path4) {
|
|
31317
31420
|
const file = join24(projectRoot, ...path4.split("/"));
|
|
31318
|
-
if (!
|
|
31421
|
+
if (!existsSync22(file)) return void 0;
|
|
31319
31422
|
return kxmContentSha256(readRegularBounded(file, path4));
|
|
31320
31423
|
}
|
|
31321
31424
|
function requireConditionalLinkSupport(directory, operationId, path4) {
|
|
@@ -31342,13 +31445,13 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
31342
31445
|
const temporaryPrefix = `.kxm-repair-${operation.operationId}-`;
|
|
31343
31446
|
const pathKey = kxmContentSha256(entry.path).slice("sha256:".length, "sha256:".length + 16);
|
|
31344
31447
|
const displaced = join24(directory, `${temporaryPrefix}${pathKey}.preimage`);
|
|
31345
|
-
if (
|
|
31448
|
+
if (existsSync22(displaced)) {
|
|
31346
31449
|
const stat = lstatSync7(displaced);
|
|
31347
31450
|
if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_preimage_temporary_invalid", entry.path, "stale displaced preimage is not a regular file", "path");
|
|
31348
31451
|
if (entry.observedSha256 === null || kxmContentSha256(readFileSync21(displaced)) !== entry.observedSha256) {
|
|
31349
31452
|
fail3("repair_preimage_temporary_mismatch", entry.path, "stale displaced preimage does not match the pinned operation");
|
|
31350
31453
|
}
|
|
31351
|
-
if (!
|
|
31454
|
+
if (!existsSync22(destination)) {
|
|
31352
31455
|
try {
|
|
31353
31456
|
linkSync(displaced, destination);
|
|
31354
31457
|
} catch {
|
|
@@ -31374,14 +31477,14 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
31374
31477
|
const bytes = readTarget(projectRoot, entry.path);
|
|
31375
31478
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
31376
31479
|
const temporary = join24(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
|
|
31377
|
-
const mode =
|
|
31480
|
+
const mode = existsSync22(destination) ? lstatSync7(destination).mode & 511 : 420;
|
|
31378
31481
|
try {
|
|
31379
31482
|
writeDurableNew(temporary, bytes, mode);
|
|
31380
31483
|
if (entry.observedSha256 !== null) {
|
|
31381
31484
|
renameSync6(destination, displaced);
|
|
31382
31485
|
syncDirectory2(directory);
|
|
31383
31486
|
if (kxmContentSha256(readFileSync21(displaced)) !== entry.observedSha256) {
|
|
31384
|
-
if (!
|
|
31487
|
+
if (!existsSync22(destination)) {
|
|
31385
31488
|
try {
|
|
31386
31489
|
linkSync(displaced, destination);
|
|
31387
31490
|
} catch {
|
|
@@ -31397,7 +31500,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
31397
31500
|
try {
|
|
31398
31501
|
linkSync(temporary, destination);
|
|
31399
31502
|
} catch {
|
|
31400
|
-
if (!
|
|
31503
|
+
if (!existsSync22(destination) && existsSync22(displaced)) {
|
|
31401
31504
|
try {
|
|
31402
31505
|
linkSync(displaced, destination);
|
|
31403
31506
|
} catch {
|
|
@@ -31420,7 +31523,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
31420
31523
|
}
|
|
31421
31524
|
function installedCreateMatches(projectRoot, operation) {
|
|
31422
31525
|
const configRoot = join24(projectRoot, ".kxm");
|
|
31423
|
-
if (!
|
|
31526
|
+
if (!existsSync22(configRoot)) return false;
|
|
31424
31527
|
const actual = [];
|
|
31425
31528
|
const visit2 = (directory) => {
|
|
31426
31529
|
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
@@ -31437,10 +31540,10 @@ function installedCreateMatches(projectRoot, operation) {
|
|
|
31437
31540
|
return actual.length === expected.length && actual.every((path4, index) => path4 === expected[index]) && operation.files.every((entry) => destinationSha(projectRoot, entry.path) === entry.targetSha256);
|
|
31438
31541
|
}
|
|
31439
31542
|
function relativeConfigPath(projectRoot, file) {
|
|
31440
|
-
return relative4(
|
|
31543
|
+
return relative4(resolve19(projectRoot), file).replaceAll("\\", "/");
|
|
31441
31544
|
}
|
|
31442
31545
|
function syncTreeDirectories(root) {
|
|
31443
|
-
if (!
|
|
31546
|
+
if (!existsSync22(root)) return;
|
|
31444
31547
|
const directories = [];
|
|
31445
31548
|
const visit2 = (directory) => {
|
|
31446
31549
|
directories.push(directory);
|
|
@@ -31470,7 +31573,7 @@ function applyOperation(projectRoot, original, options, resumed) {
|
|
|
31470
31573
|
operation = updatePhase(projectRoot, operation, "applying");
|
|
31471
31574
|
if (operation.kind === "create") {
|
|
31472
31575
|
const destination = join24(projectRoot, ".kxm");
|
|
31473
|
-
if (!
|
|
31576
|
+
if (!existsSync22(destination)) {
|
|
31474
31577
|
rebuildCreateShadow(projectRoot, operation, effectiveOptions);
|
|
31475
31578
|
renameSync6(join24(shadowRoot(projectRoot), ".kxm"), destination);
|
|
31476
31579
|
syncDirectory2(projectRoot);
|
|
@@ -31633,7 +31736,7 @@ function resumeKxmInitTransaction(projectRoot, options = {}) {
|
|
|
31633
31736
|
let operation = readOperation(projectRoot, options.schemasDir);
|
|
31634
31737
|
if (!operation) {
|
|
31635
31738
|
const root = transactionRoot(projectRoot);
|
|
31636
|
-
if (
|
|
31739
|
+
if (existsSync22(root)) {
|
|
31637
31740
|
rmSync8(root, { recursive: true, force: true });
|
|
31638
31741
|
syncDirectory2(projectRoot);
|
|
31639
31742
|
}
|
|
@@ -31758,7 +31861,7 @@ function validateBindingIdentities(bundle, persisted, explicit) {
|
|
|
31758
31861
|
}
|
|
31759
31862
|
function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
31760
31863
|
const storeOptions = bindingStoreOptions(options);
|
|
31761
|
-
const persisted =
|
|
31864
|
+
const persisted = existsSync23(join25(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
|
|
31762
31865
|
const repositoryBindings = Object.fromEntries([
|
|
31763
31866
|
...Object.entries(persisted?.repositories ?? {}),
|
|
31764
31867
|
...Object.entries(options.repositoryBindings ?? {})
|
|
@@ -31946,7 +32049,7 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
|
31946
32049
|
const files = [...rendered.files.keys()];
|
|
31947
32050
|
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
|
|
31948
32051
|
if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
|
|
31949
|
-
if (
|
|
32052
|
+
if (existsSync23(join25(gitRoot, ".kxm"))) {
|
|
31950
32053
|
throw new KxmConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
|
|
31951
32054
|
}
|
|
31952
32055
|
const created = prepareAndApplyKxmCreate(gitRoot, rendered, transactionOptions);
|
|
@@ -32264,10 +32367,24 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
32264
32367
|
}
|
|
32265
32368
|
async function cmdBackup(runtime, options) {
|
|
32266
32369
|
try {
|
|
32267
|
-
const
|
|
32370
|
+
const backupOptions = {
|
|
32268
32371
|
projectRoot: runtime.cwd,
|
|
32269
|
-
...options.out ? { outDir:
|
|
32270
|
-
}
|
|
32372
|
+
...options.out ? { outDir: resolve20(runtime.cwd, options.out) } : {}
|
|
32373
|
+
};
|
|
32374
|
+
if (runtime.dryRun) {
|
|
32375
|
+
const plan = planBackup(backupOptions);
|
|
32376
|
+
printPlan(
|
|
32377
|
+
runtime,
|
|
32378
|
+
{ command: "backup", outDir: plan.outDir, stores: plan.stores },
|
|
32379
|
+
[
|
|
32380
|
+
...plan.stores.map((store) => ({ action: "write", target: join26(plan.outDir, store.backupFile) })),
|
|
32381
|
+
{ action: "write", target: join26(plan.outDir, "manifest.json") }
|
|
32382
|
+
],
|
|
32383
|
+
`back up ${plan.stores.length} store(s) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
|
|
32384
|
+
);
|
|
32385
|
+
return 0;
|
|
32386
|
+
}
|
|
32387
|
+
const { manifest, outDir } = createBackup(backupOptions);
|
|
32271
32388
|
const payload = {
|
|
32272
32389
|
ok: true,
|
|
32273
32390
|
command: "backup",
|
|
@@ -32293,7 +32410,25 @@ async function cmdBackup(runtime, options) {
|
|
|
32293
32410
|
}
|
|
32294
32411
|
async function cmdRestore(runtime, manifestArg) {
|
|
32295
32412
|
try {
|
|
32296
|
-
|
|
32413
|
+
if (runtime.dryRun) {
|
|
32414
|
+
const plan = planRestore(resolve20(runtime.cwd, manifestArg), { projectRoot: runtime.cwd });
|
|
32415
|
+
printPlan(
|
|
32416
|
+
runtime,
|
|
32417
|
+
{
|
|
32418
|
+
command: "restore",
|
|
32419
|
+
backupId: plan.backupId,
|
|
32420
|
+
manifestPath: plan.manifestPath,
|
|
32421
|
+
stores: plan.stores.map(({ storeId, targetPath, schemaVersion }) => ({ storeId, targetPath, schemaVersion }))
|
|
32422
|
+
},
|
|
32423
|
+
plan.stores.flatMap((store) => [
|
|
32424
|
+
{ action: "write", target: store.targetPath },
|
|
32425
|
+
...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync24(file)).map((file) => ({ action: "delete", target: file }))
|
|
32426
|
+
]),
|
|
32427
|
+
`restore ${plan.stores.length} store(s) from ${plan.manifestPath}; digests verified against the manifest`
|
|
32428
|
+
);
|
|
32429
|
+
return 0;
|
|
32430
|
+
}
|
|
32431
|
+
const result = restoreBackup(resolve20(runtime.cwd, manifestArg), {
|
|
32297
32432
|
projectRoot: runtime.cwd
|
|
32298
32433
|
});
|
|
32299
32434
|
const payload = {
|
|
@@ -32362,9 +32497,14 @@ trust check failed: review every expansion above before merging`);
|
|
|
32362
32497
|
return 1;
|
|
32363
32498
|
}
|
|
32364
32499
|
}
|
|
32500
|
+
async function readSupervisor(runtime, command) {
|
|
32501
|
+
if (!runtime.dryRun) return await (kxmDriveCliSeams.ensureSupervisor ?? ensureKxmSupervisor)({ env: runtime.env });
|
|
32502
|
+
const attached = await attachKxmSupervisor({ env: runtime.env });
|
|
32503
|
+
return attached ?? refuseDryRun(runtime.io, runtime.json, command, "the Runtime supervisor is not running and --dry-run will not start it");
|
|
32504
|
+
}
|
|
32365
32505
|
var RUN_ENGINE_PHASE = "pre-3a";
|
|
32366
32506
|
var RUN_ENGINE_NOTICE = "runs remain created until the run engine lands; no steps execute yet";
|
|
32367
|
-
|
|
32507
|
+
function resolveKxmRunTarget(runtime, workflow) {
|
|
32368
32508
|
if (runtime.workspaceFlag !== void 0) {
|
|
32369
32509
|
print(runtime.io, runtime.json, {
|
|
32370
32510
|
ok: false,
|
|
@@ -32377,33 +32517,37 @@ async function cmdKxmRun(runtime, workflow, promptParts) {
|
|
|
32377
32517
|
print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required" }, "usage: kxm run <workflow> [prompt]");
|
|
32378
32518
|
return 2;
|
|
32379
32519
|
}
|
|
32520
|
+
const projectRoot = discoverKxmProjectRoot(runtime.cwd);
|
|
32521
|
+
if (!projectRoot) {
|
|
32522
|
+
print(runtime.io, runtime.json, { ok: false, command: "run", error: "project_required" }, "kxm run requires a KXM project (run kxm init first)");
|
|
32523
|
+
return 1;
|
|
32524
|
+
}
|
|
32525
|
+
const bundle = loadKxmProject(projectRoot, {});
|
|
32526
|
+
if (!bundle.workflows.has(workflow)) {
|
|
32527
|
+
print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow }, `workflow ${workflow} does not exist in this project`);
|
|
32528
|
+
return 1;
|
|
32529
|
+
}
|
|
32530
|
+
return { projectRoot, workflowId: workflow, configRevision: bundle.configRevision };
|
|
32531
|
+
}
|
|
32532
|
+
async function cmdKxmRun(runtime, workflow, promptParts) {
|
|
32380
32533
|
try {
|
|
32381
|
-
const
|
|
32382
|
-
if (
|
|
32383
|
-
|
|
32384
|
-
return 1;
|
|
32385
|
-
}
|
|
32386
|
-
const bundle = loadKxmProject(projectRoot, {});
|
|
32387
|
-
if (!bundle.workflows.has(workflow)) {
|
|
32388
|
-
print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow }, `workflow ${workflow} does not exist in this project`);
|
|
32389
|
-
return 1;
|
|
32390
|
-
}
|
|
32534
|
+
const target = resolveKxmRunTarget(runtime, workflow);
|
|
32535
|
+
if (typeof target === "number") return target;
|
|
32536
|
+
const { projectRoot } = target;
|
|
32391
32537
|
if (runtime.dryRun) {
|
|
32392
32538
|
print(runtime.io, runtime.json, {
|
|
32393
32539
|
ok: true,
|
|
32394
32540
|
command: "run",
|
|
32395
32541
|
dryRun: true,
|
|
32396
|
-
|
|
32397
|
-
|
|
32398
|
-
configRevision: bundle.configRevision
|
|
32399
|
-
}, `run plan: workflow ${workflow} at ${bundle.configRevision.slice(0, 19)}\u2026 (no run created)`);
|
|
32542
|
+
...target
|
|
32543
|
+
}, `run plan: workflow ${target.workflowId} at ${target.configRevision.slice(0, 19)}\u2026 (no run created)`);
|
|
32400
32544
|
return 0;
|
|
32401
32545
|
}
|
|
32402
32546
|
const supervisor = await ensureKxmSupervisor({ env: runtime.env });
|
|
32403
32547
|
const prompt = promptParts.join(" ").trim();
|
|
32404
32548
|
const acceptance = await kxmRuntimeRequest(supervisor, "POST", "/v1/runs", {
|
|
32405
32549
|
projectRoot,
|
|
32406
|
-
workflowId:
|
|
32550
|
+
workflowId: target.workflowId,
|
|
32407
32551
|
prompt
|
|
32408
32552
|
});
|
|
32409
32553
|
const run = acceptance.run;
|
|
@@ -32467,7 +32611,8 @@ async function cmdKxmRunStatus(runtime, runId) {
|
|
|
32467
32611
|
print(runtime.io, runtime.json, { ok: false, command: "runs status", error: "project_required" }, "kxm runs status requires a KXM project (run kxm init first)");
|
|
32468
32612
|
return 1;
|
|
32469
32613
|
}
|
|
32470
|
-
const supervisor = await (
|
|
32614
|
+
const supervisor = await readSupervisor(runtime, "runs status");
|
|
32615
|
+
if (typeof supervisor === "number") return supervisor;
|
|
32471
32616
|
const result = await (kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest)(supervisor, "GET", `/v1/runs/${encodeURIComponent(runId)}?projectRoot=${encodeURIComponent(projectRoot)}`);
|
|
32472
32617
|
const run = result.run;
|
|
32473
32618
|
const drive = result.drive;
|
|
@@ -32498,7 +32643,7 @@ function parseDriveWaitTimeoutMs(raw) {
|
|
|
32498
32643
|
return value;
|
|
32499
32644
|
}
|
|
32500
32645
|
function sleepMs(ms) {
|
|
32501
|
-
return new Promise((
|
|
32646
|
+
return new Promise((resolve32) => setTimeout(resolve32, ms));
|
|
32502
32647
|
}
|
|
32503
32648
|
function driveWaitCompleted(drive) {
|
|
32504
32649
|
return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
|
|
@@ -32613,7 +32758,8 @@ async function cmdKxmRunList(runtime) {
|
|
|
32613
32758
|
}
|
|
32614
32759
|
const bundle = loadKxmProject(projectRoot, {});
|
|
32615
32760
|
const projectId = String(bundle.project.value.id);
|
|
32616
|
-
const supervisor = await
|
|
32761
|
+
const supervisor = await readSupervisor(runtime, "runs list");
|
|
32762
|
+
if (typeof supervisor === "number") return supervisor;
|
|
32617
32763
|
const result = await kxmRuntimeRequest(supervisor, "GET", `/v1/projects/${encodeURIComponent(projectId)}/runs?projectRoot=${encodeURIComponent(projectRoot)}`);
|
|
32618
32764
|
const runs = result.runs ?? [];
|
|
32619
32765
|
print(
|
|
@@ -32704,7 +32850,7 @@ async function selectInventoryModel(runtime, requested) {
|
|
|
32704
32850
|
if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
|
|
32705
32851
|
runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
|
|
32706
32852
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
32707
|
-
const answer = await new Promise((
|
|
32853
|
+
const answer = await new Promise((resolve32) => rl.once("line", resolve32));
|
|
32708
32854
|
rl.close();
|
|
32709
32855
|
const index = Number.parseInt(answer.trim(), 10) - 1;
|
|
32710
32856
|
return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
|
|
@@ -32730,7 +32876,7 @@ async function cmdModelsScreen(runtime) {
|
|
|
32730
32876
|
return 2;
|
|
32731
32877
|
}
|
|
32732
32878
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
32733
|
-
const ask = (q2) => new Promise((
|
|
32879
|
+
const ask = (q2) => new Promise((resolve32) => rl.question(q2, resolve32));
|
|
32734
32880
|
try {
|
|
32735
32881
|
while (true) {
|
|
32736
32882
|
const policy = loadRoutePolicy(runtime.dirs.workdir);
|
|
@@ -32872,7 +33018,8 @@ async function cmdKxmRunReceipt(runtime, runId, options = {}) {
|
|
|
32872
33018
|
print(runtime.io, runtime.json, { ok: false, command: "runs receipt", error: "project_required" }, "kxm runs receipt requires a KXM project (run kxm init first)");
|
|
32873
33019
|
return 1;
|
|
32874
33020
|
}
|
|
32875
|
-
const supervisor = await (
|
|
33021
|
+
const supervisor = await readSupervisor(runtime, "runs receipt");
|
|
33022
|
+
if (typeof supervisor === "number") return supervisor;
|
|
32876
33023
|
const result = await (kxmDriveCliSeams.runtimeRequest ?? kxmRuntimeRequest)(
|
|
32877
33024
|
supervisor,
|
|
32878
33025
|
"GET",
|
|
@@ -32949,7 +33096,11 @@ async function cmdGoalCreate(runtime, title, options) {
|
|
|
32949
33096
|
area: options.area,
|
|
32950
33097
|
successMetrics: options.metric,
|
|
32951
33098
|
targetDate: options.targetDate
|
|
32952
|
-
});
|
|
33099
|
+
}, { dryRun: runtime.dryRun });
|
|
33100
|
+
if (runtime.dryRun) {
|
|
33101
|
+
printPlan(runtime, { command: "goal create", goal }, [{ action: "write", target: goalFilePath(runtime.cwd, goal.id) }], `create goal ${goal.title} (the id is assigned when it is created)`);
|
|
33102
|
+
return 0;
|
|
33103
|
+
}
|
|
32953
33104
|
print(
|
|
32954
33105
|
runtime.io,
|
|
32955
33106
|
runtime.json,
|
|
@@ -32985,7 +33136,11 @@ async function cmdTaskCreate(runtime, title, options) {
|
|
|
32985
33136
|
objective: options.objective ?? title,
|
|
32986
33137
|
assignedWorkflow: options.workflow,
|
|
32987
33138
|
trackerSync: options.tracker && options.issue ? { tracker: options.tracker, issueKey: options.issue } : void 0
|
|
32988
|
-
});
|
|
33139
|
+
}, { dryRun: runtime.dryRun });
|
|
33140
|
+
if (runtime.dryRun) {
|
|
33141
|
+
printPlan(runtime, { command: "task create", task }, [{ action: "write", target: taskFilePath(runtime.cwd, task.id) }], `create task ${task.title} [${task.status}] (the id is assigned when it is created)`);
|
|
33142
|
+
return 0;
|
|
33143
|
+
}
|
|
32989
33144
|
print(
|
|
32990
33145
|
runtime.io,
|
|
32991
33146
|
runtime.json,
|
|
@@ -33051,6 +33206,21 @@ async function cmdTaskRun(runtime, taskId) {
|
|
|
33051
33206
|
return 1;
|
|
33052
33207
|
}
|
|
33053
33208
|
const workflow = task.assignedWorkflow ?? "default";
|
|
33209
|
+
if (runtime.dryRun) {
|
|
33210
|
+
const target = resolveKxmRunTarget(runtime, workflow);
|
|
33211
|
+
if (typeof target === "number") return target;
|
|
33212
|
+
const started = updateTaskStatus(runtime.cwd, taskId, "in_progress", { dryRun: true });
|
|
33213
|
+
printPlan(
|
|
33214
|
+
runtime,
|
|
33215
|
+
{ command: "task run", taskId, ...target, status: started.status },
|
|
33216
|
+
[
|
|
33217
|
+
{ action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" },
|
|
33218
|
+
{ action: "write", target: taskFilePath(runtime.cwd, taskId) }
|
|
33219
|
+
],
|
|
33220
|
+
`run workflow ${workflow} for task ${taskId}, then mark it ${started.status}`
|
|
33221
|
+
);
|
|
33222
|
+
return 0;
|
|
33223
|
+
}
|
|
33054
33224
|
const exitCode = await cmdKxmRun(runtime, workflow, [task.objective]);
|
|
33055
33225
|
if (exitCode === 0) {
|
|
33056
33226
|
updateTaskStatus(runtime.cwd, taskId, "in_progress");
|
|
@@ -33065,7 +33235,11 @@ async function cmdTaskRun(runtime, taskId) {
|
|
|
33065
33235
|
}
|
|
33066
33236
|
async function cmdTaskSync(runtime, taskId) {
|
|
33067
33237
|
try {
|
|
33068
|
-
const synced = syncTaskWithTracker(runtime.cwd, taskId);
|
|
33238
|
+
const synced = syncTaskWithTracker(runtime.cwd, taskId, { dryRun: runtime.dryRun });
|
|
33239
|
+
if (runtime.dryRun) {
|
|
33240
|
+
printPlan(runtime, { command: "task sync", task: synced }, [{ action: "write", target: taskFilePath(runtime.cwd, taskId) }], `mark task ${taskId} synced with ${synced.trackerSync?.tracker} #${synced.trackerSync?.issueKey}`);
|
|
33241
|
+
return 0;
|
|
33242
|
+
}
|
|
33069
33243
|
print(
|
|
33070
33244
|
runtime.io,
|
|
33071
33245
|
runtime.json,
|
|
@@ -33085,12 +33259,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
|
|
|
33085
33259
|
const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
|
|
33086
33260
|
let filePath = workflowPath;
|
|
33087
33261
|
if (!filePath) {
|
|
33088
|
-
filePath =
|
|
33262
|
+
filePath = resolve21(projectRoot, ".kxm", "workflows", "default.yaml");
|
|
33089
33263
|
}
|
|
33090
33264
|
let yamlContent;
|
|
33091
33265
|
let workflowId = "default";
|
|
33092
|
-
if (
|
|
33093
|
-
yamlContent =
|
|
33266
|
+
if (existsSync25(filePath)) {
|
|
33267
|
+
yamlContent = readFileSync23(filePath, "utf8");
|
|
33094
33268
|
} else {
|
|
33095
33269
|
yamlContent = `schema: kxm.workflow.v1
|
|
33096
33270
|
description: Feature implementation workflow
|
|
@@ -33165,13 +33339,13 @@ async function cmdStudioServe(runtime, options) {
|
|
|
33165
33339
|
try {
|
|
33166
33340
|
const wfDir = join27(runtime.cwd, ".kxm", "workflows");
|
|
33167
33341
|
const defaultPath = join27(wfDir, "default.yaml");
|
|
33168
|
-
let targetPath =
|
|
33169
|
-
if (!targetPath &&
|
|
33342
|
+
let targetPath = existsSync25(defaultPath) ? defaultPath : void 0;
|
|
33343
|
+
if (!targetPath && existsSync25(wfDir)) {
|
|
33170
33344
|
const yml = readdirSync8(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
33171
33345
|
if (yml) targetPath = join27(wfDir, yml);
|
|
33172
33346
|
}
|
|
33173
|
-
if (targetPath &&
|
|
33174
|
-
const raw =
|
|
33347
|
+
if (targetPath && existsSync25(targetPath)) {
|
|
33348
|
+
const raw = readFileSync23(targetPath, "utf8");
|
|
33175
33349
|
const parsed = (0, import_yaml12.parse)(raw);
|
|
33176
33350
|
const wfId = basename6(targetPath).replace(/\.(yaml|yml)$/, "");
|
|
33177
33351
|
return compileKxmWorkflow({ id: wfId, value: parsed });
|
|
@@ -33221,14 +33395,14 @@ Press Ctrl+C to stop.
|
|
|
33221
33395
|
}
|
|
33222
33396
|
|
|
33223
33397
|
// plugins/kxm/src/cli/context-skills.ts
|
|
33224
|
-
import { readFileSync as
|
|
33225
|
-
import { join as join31, relative as relative5 } from "node:path";
|
|
33398
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
33399
|
+
import { join as join31, relative as relative5, resolve as resolve23 } from "node:path";
|
|
33226
33400
|
|
|
33227
33401
|
// plugins/kxm/src/memory.ts
|
|
33228
33402
|
var import_yaml13 = __toESM(require_dist(), 1);
|
|
33229
33403
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
33230
|
-
import { existsSync as
|
|
33231
|
-
import { extname as extname2, join as join28, resolve as
|
|
33404
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync20, readdirSync as readdirSync9, readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "node:fs";
|
|
33405
|
+
import { extname as extname2, join as join28, resolve as resolve22 } from "node:path";
|
|
33232
33406
|
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
33233
33407
|
var MEMORY_BRIEF_SCHEMA = "kxm.memory-brief.v1";
|
|
33234
33408
|
var MEMORY_MARKER_START = "<!-- kxm:memory:start -->";
|
|
@@ -33354,21 +33528,21 @@ ${yamlText}
|
|
|
33354
33528
|
`;
|
|
33355
33529
|
}
|
|
33356
33530
|
function memoryDirectories(repoRoot) {
|
|
33357
|
-
const root =
|
|
33531
|
+
const root = resolve22(repoRoot);
|
|
33358
33532
|
const memoryDir = join28(root, ".kxm", "memory");
|
|
33359
33533
|
const candidatesDir = join28(memoryDir, "candidates");
|
|
33360
33534
|
return { memoryDir, candidatesDir };
|
|
33361
33535
|
}
|
|
33362
33536
|
function loadAuthoredMemory(repoRoot) {
|
|
33363
33537
|
const { memoryDir } = memoryDirectories(repoRoot);
|
|
33364
|
-
if (!
|
|
33538
|
+
if (!existsSync26(memoryDir)) return [];
|
|
33365
33539
|
const records = [];
|
|
33366
33540
|
const entries = readdirSync9(memoryDir, { withFileTypes: true });
|
|
33367
33541
|
for (const entry of entries) {
|
|
33368
33542
|
if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
33369
33543
|
const fullPath = join28(memoryDir, entry.name);
|
|
33370
33544
|
try {
|
|
33371
|
-
const text =
|
|
33545
|
+
const text = readFileSync24(fullPath, "utf8");
|
|
33372
33546
|
const record = parseMemoryRecord(text, entry.name);
|
|
33373
33547
|
if (record.lifecycle === "active") {
|
|
33374
33548
|
records.push(record);
|
|
@@ -33382,7 +33556,6 @@ function loadAuthoredMemory(repoRoot) {
|
|
|
33382
33556
|
}
|
|
33383
33557
|
function createMemoryNote(repoRoot, summary, options = {}) {
|
|
33384
33558
|
const { candidatesDir } = memoryDirectories(repoRoot);
|
|
33385
|
-
mkdirSync19(candidatesDir, { recursive: true });
|
|
33386
33559
|
const scope = options.scope ?? "project";
|
|
33387
33560
|
if (!VALID_SCOPES.has(scope)) {
|
|
33388
33561
|
throw new Error(`invalid memory note scope: ${scope}; must be one of agent, project, run, operator`);
|
|
@@ -33408,6 +33581,8 @@ function createMemoryNote(repoRoot, summary, options = {}) {
|
|
|
33408
33581
|
...options.body ? { body: options.body.trim() } : {}
|
|
33409
33582
|
};
|
|
33410
33583
|
const targetPath = join28(candidatesDir, `${id}.md`);
|
|
33584
|
+
if (options.dryRun) return { record, path: targetPath };
|
|
33585
|
+
mkdirSync20(candidatesDir, { recursive: true });
|
|
33411
33586
|
writeFileSync18(targetPath, formatMemoryRecord(record), "utf8");
|
|
33412
33587
|
return { record, path: targetPath };
|
|
33413
33588
|
}
|
|
@@ -33442,10 +33617,10 @@ function formatHarnessMemoryBlock(records) {
|
|
|
33442
33617
|
contentLines.push(MEMORY_MARKER_END);
|
|
33443
33618
|
return contentLines.join("\n");
|
|
33444
33619
|
}
|
|
33445
|
-
function updateHarnessDocument(filePath, block, defaultHeader) {
|
|
33620
|
+
function updateHarnessDocument(filePath, block, defaultHeader, dryRun = false) {
|
|
33446
33621
|
let original = "";
|
|
33447
|
-
if (
|
|
33448
|
-
original =
|
|
33622
|
+
if (existsSync26(filePath)) {
|
|
33623
|
+
original = readFileSync24(filePath, "utf8");
|
|
33449
33624
|
}
|
|
33450
33625
|
let updated = "";
|
|
33451
33626
|
if (original.includes(MEMORY_MARKER_START) && original.includes(MEMORY_MARKER_END)) {
|
|
@@ -33473,21 +33648,21 @@ ${block}
|
|
|
33473
33648
|
`;
|
|
33474
33649
|
}
|
|
33475
33650
|
if (updated !== original) {
|
|
33476
|
-
writeFileSync18(filePath, updated, "utf8");
|
|
33651
|
+
if (!dryRun) writeFileSync18(filePath, updated, "utf8");
|
|
33477
33652
|
return true;
|
|
33478
33653
|
}
|
|
33479
33654
|
return false;
|
|
33480
33655
|
}
|
|
33481
|
-
function syncHarnessMemory(repoRoot) {
|
|
33482
|
-
const root =
|
|
33656
|
+
function syncHarnessMemory(repoRoot, options = {}) {
|
|
33657
|
+
const root = resolve22(repoRoot);
|
|
33483
33658
|
const records = loadAuthoredMemory(root);
|
|
33484
33659
|
const block = formatHarnessMemoryBlock(records);
|
|
33485
33660
|
const updated = [];
|
|
33486
33661
|
const created = [];
|
|
33487
33662
|
const agentsPath = join28(root, "AGENTS.md");
|
|
33488
33663
|
const agentsHeader = "# AGENTS\n\nFollow project instructions.\n";
|
|
33489
|
-
const agentsExisted =
|
|
33490
|
-
if (updateHarnessDocument(agentsPath, block, agentsHeader)) {
|
|
33664
|
+
const agentsExisted = existsSync26(agentsPath);
|
|
33665
|
+
if (updateHarnessDocument(agentsPath, block, agentsHeader, options.dryRun)) {
|
|
33491
33666
|
if (agentsExisted) updated.push("AGENTS.md");
|
|
33492
33667
|
else created.push("AGENTS.md");
|
|
33493
33668
|
}
|
|
@@ -33503,8 +33678,8 @@ rotation, not a sole writer. If \`grok\` is logged out, never bill Grok through
|
|
|
33503
33678
|
use a relief route Tracking **admits**, or stop and name the limits hit. Your reviews are
|
|
33504
33679
|
artifacts, not hub \`peer-reply\` evidence.
|
|
33505
33680
|
`;
|
|
33506
|
-
const claudeExisted =
|
|
33507
|
-
if (updateHarnessDocument(claudePath, block, claudeHeader)) {
|
|
33681
|
+
const claudeExisted = existsSync26(claudePath);
|
|
33682
|
+
if (updateHarnessDocument(claudePath, block, claudeHeader, options.dryRun)) {
|
|
33508
33683
|
if (claudeExisted) updated.push("CLAUDE.md");
|
|
33509
33684
|
else created.push("CLAUDE.md");
|
|
33510
33685
|
}
|
|
@@ -33519,8 +33694,8 @@ Google goes through the \`antigravity\` **Pi provider** (Tracking \u2192 Decided
|
|
|
33519
33694
|
Current admissions come from Tracking and \`kxm harness list\`. Starting rotation
|
|
33520
33695
|
remains Grok.
|
|
33521
33696
|
`;
|
|
33522
|
-
const geminiExisted =
|
|
33523
|
-
if (updateHarnessDocument(geminiPath, block, geminiHeader)) {
|
|
33697
|
+
const geminiExisted = existsSync26(geminiPath);
|
|
33698
|
+
if (updateHarnessDocument(geminiPath, block, geminiHeader, options.dryRun)) {
|
|
33524
33699
|
if (geminiExisted) updated.push("GEMINI.md");
|
|
33525
33700
|
else created.push("GEMINI.md");
|
|
33526
33701
|
}
|
|
@@ -33530,8 +33705,8 @@ remains Grok.
|
|
|
33530
33705
|
// plugins/kxm/src/skills.ts
|
|
33531
33706
|
var import_yaml14 = __toESM(require_dist(), 1);
|
|
33532
33707
|
import { createHash as createHash14 } from "node:crypto";
|
|
33533
|
-
import { existsSync as
|
|
33534
|
-
import { join as join29 } from "node:path";
|
|
33708
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync21, readdirSync as readdirSync10, readFileSync as readFileSync25, renameSync as renameSync7, rmSync as rmSync10, statSync as statSync3, writeFileSync as writeFileSync19 } from "node:fs";
|
|
33709
|
+
import { dirname as dirname17, join as join29 } from "node:path";
|
|
33535
33710
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
33536
33711
|
var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
|
|
33537
33712
|
var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
|
|
@@ -33619,10 +33794,22 @@ var SkillLifecycle = class {
|
|
|
33619
33794
|
root;
|
|
33620
33795
|
now;
|
|
33621
33796
|
allowOptimizationEvals;
|
|
33797
|
+
dryRun;
|
|
33798
|
+
/** What a `dryRun` lifecycle would have written or moved, in order. */
|
|
33799
|
+
planned = [];
|
|
33622
33800
|
constructor(root, options = {}) {
|
|
33623
33801
|
this.root = root;
|
|
33624
33802
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
33625
33803
|
this.allowOptimizationEvals = options.allowOptimizationEvals === true;
|
|
33804
|
+
this.dryRun = options.dryRun === true;
|
|
33805
|
+
}
|
|
33806
|
+
write(file, content) {
|
|
33807
|
+
if (this.dryRun) {
|
|
33808
|
+
this.planned.push({ action: "write", target: file });
|
|
33809
|
+
return;
|
|
33810
|
+
}
|
|
33811
|
+
mkdirSync21(dirname17(file), { recursive: true });
|
|
33812
|
+
writeFileSync19(file, content);
|
|
33626
33813
|
}
|
|
33627
33814
|
dir(state) {
|
|
33628
33815
|
return join29(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
|
|
@@ -33635,37 +33822,40 @@ var SkillLifecycle = class {
|
|
|
33635
33822
|
return { dir, metadata: join29(dir, "metadata.json"), skill: join29(dir, "SKILL.md") };
|
|
33636
33823
|
}
|
|
33637
33824
|
appendHistory(id, record) {
|
|
33638
|
-
mkdirSync20(join29(this.root, "history"), { recursive: true });
|
|
33639
33825
|
const line = `${JSON.stringify(record)}
|
|
33640
33826
|
`;
|
|
33641
|
-
if (
|
|
33642
|
-
const existing =
|
|
33827
|
+
if (existsSync27(this.historyFile(id))) {
|
|
33828
|
+
const existing = readFileSync25(this.historyFile(id), "utf8");
|
|
33643
33829
|
const lines = existing.split("\n").filter((entry) => entry.trim());
|
|
33644
|
-
|
|
33830
|
+
this.write(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
|
|
33645
33831
|
} else {
|
|
33646
|
-
|
|
33832
|
+
this.write(this.historyFile(id), line);
|
|
33647
33833
|
}
|
|
33648
33834
|
}
|
|
33649
33835
|
history(id) {
|
|
33650
33836
|
const file = this.historyFile(id);
|
|
33651
|
-
if (!
|
|
33652
|
-
return
|
|
33837
|
+
if (!existsSync27(file)) return [];
|
|
33838
|
+
return readFileSync25(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
33653
33839
|
}
|
|
33654
33840
|
readMetadata(state, id) {
|
|
33655
33841
|
const { metadata } = this.paths(state, id);
|
|
33656
|
-
if (!
|
|
33842
|
+
if (!existsSync27(metadata)) {
|
|
33657
33843
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
|
|
33658
33844
|
}
|
|
33659
|
-
return JSON.parse(
|
|
33845
|
+
return JSON.parse(readFileSync25(metadata, "utf8"));
|
|
33660
33846
|
}
|
|
33661
33847
|
move(from, to, id) {
|
|
33662
33848
|
const fromDir = join29(this.dir(from), id);
|
|
33663
33849
|
const toDir = join29(this.dir(to), id);
|
|
33664
|
-
if (!
|
|
33850
|
+
if (!existsSync27(fromDir)) {
|
|
33665
33851
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
|
|
33666
33852
|
}
|
|
33667
|
-
|
|
33668
|
-
|
|
33853
|
+
if (this.dryRun) {
|
|
33854
|
+
this.planned.push({ action: "move", target: `${fromDir} -> ${toDir}` });
|
|
33855
|
+
return;
|
|
33856
|
+
}
|
|
33857
|
+
mkdirSync21(this.dir(to), { recursive: true });
|
|
33858
|
+
if (existsSync27(toDir)) rmSync10(toDir, { recursive: true, force: true });
|
|
33669
33859
|
renameSync7(fromDir, toDir);
|
|
33670
33860
|
}
|
|
33671
33861
|
/** Submit a new skill candidate. Content is redacted of secret material at
|
|
@@ -33707,8 +33897,8 @@ var SkillLifecycle = class {
|
|
|
33707
33897
|
if (!harness) throw new SkillLifecycleError("invalid_skill_compatibility", "compatibility.harness is required");
|
|
33708
33898
|
const contentSha256 = skillContentSha256(content);
|
|
33709
33899
|
const id = skillIdFor(name, contentSha256);
|
|
33710
|
-
const {
|
|
33711
|
-
if (
|
|
33900
|
+
const { metadata, skill } = this.paths("candidate", id);
|
|
33901
|
+
if (existsSync27(metadata)) {
|
|
33712
33902
|
throw new SkillLifecycleError(
|
|
33713
33903
|
"skill_candidate_exists",
|
|
33714
33904
|
`identical candidate ${id} already exists; changed behavior requires changed content`
|
|
@@ -33727,9 +33917,8 @@ var SkillLifecycle = class {
|
|
|
33727
33917
|
createdAt: this.now(),
|
|
33728
33918
|
...input.supersedes ? { supersedes: input.supersedes } : {}
|
|
33729
33919
|
};
|
|
33730
|
-
|
|
33731
|
-
|
|
33732
|
-
writeFileSync19(metadata, `${JSON.stringify(record, null, 2)}
|
|
33920
|
+
this.write(skill, content);
|
|
33921
|
+
this.write(metadata, `${JSON.stringify(record, null, 2)}
|
|
33733
33922
|
`);
|
|
33734
33923
|
this.appendHistory(id, { schema: "kxm.skill-history-event.v1", event: "candidate_created", by: createdBy, supersedes: input.supersedes, at: record.createdAt });
|
|
33735
33924
|
return record;
|
|
@@ -33818,18 +34007,15 @@ var SkillLifecycle = class {
|
|
|
33818
34007
|
};
|
|
33819
34008
|
const candidatePaths = this.paths("candidate", candidateId);
|
|
33820
34009
|
const promotedPaths = this.paths("promoted", candidateId);
|
|
33821
|
-
|
|
33822
|
-
const
|
|
33823
|
-
|
|
33824
|
-
|
|
33825
|
-
|
|
33826
|
-
const patchesDir = join29(this.root, "patches");
|
|
33827
|
-
mkdirSync20(patchesDir, { recursive: true });
|
|
33828
|
-
const patchPath = join29(patchesDir, `${candidateId}.patch`);
|
|
34010
|
+
const skillContent = readFileSync25(candidatePaths.skill, "utf8");
|
|
34011
|
+
const metadataContent = readFileSync25(candidatePaths.metadata, "utf8");
|
|
34012
|
+
this.write(promotedPaths.skill, skillContent);
|
|
34013
|
+
this.write(promotedPaths.metadata, metadataContent);
|
|
34014
|
+
const patchPath = join29(this.root, "patches", `${candidateId}.patch`);
|
|
33829
34015
|
const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
|
|
33830
34016
|
const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
|
|
33831
34017
|
const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
|
|
33832
|
-
|
|
34018
|
+
this.write(patchPath, patch);
|
|
33833
34019
|
this.appendHistory(candidateId, record);
|
|
33834
34020
|
return { ...metadata, patch, patchPath };
|
|
33835
34021
|
}
|
|
@@ -33853,7 +34039,7 @@ var SkillLifecycle = class {
|
|
|
33853
34039
|
verify(state, id) {
|
|
33854
34040
|
const metadata = this.readMetadata(state, id);
|
|
33855
34041
|
const { skill } = this.paths(state, id);
|
|
33856
|
-
const content =
|
|
34042
|
+
const content = readFileSync25(skill, "utf8");
|
|
33857
34043
|
if (skillContentSha256(content) !== metadata.contentSha256) {
|
|
33858
34044
|
throw new SkillLifecycleError(
|
|
33859
34045
|
"skill_integrity_violation",
|
|
@@ -33871,7 +34057,7 @@ var SkillLifecycle = class {
|
|
|
33871
34057
|
}
|
|
33872
34058
|
list(state) {
|
|
33873
34059
|
const dir = this.dir(state);
|
|
33874
|
-
if (!
|
|
34060
|
+
if (!existsSync27(dir)) return [];
|
|
33875
34061
|
const ids = readdirSorted(dir);
|
|
33876
34062
|
return ids.map((id) => {
|
|
33877
34063
|
try {
|
|
@@ -33884,7 +34070,7 @@ var SkillLifecycle = class {
|
|
|
33884
34070
|
read(state, id) {
|
|
33885
34071
|
const metadata = this.readMetadata(state, id);
|
|
33886
34072
|
const { skill } = this.paths(state, id);
|
|
33887
|
-
return { metadata, content:
|
|
34073
|
+
return { metadata, content: readFileSync25(skill, "utf8") };
|
|
33888
34074
|
}
|
|
33889
34075
|
};
|
|
33890
34076
|
function boundedList(value, field) {
|
|
@@ -33903,13 +34089,13 @@ function readdirSorted(dir) {
|
|
|
33903
34089
|
}
|
|
33904
34090
|
|
|
33905
34091
|
// plugins/kxm/src/wiki.ts
|
|
33906
|
-
import { mkdirSync as
|
|
34092
|
+
import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync20 } from "node:fs";
|
|
33907
34093
|
import { join as join30 } from "node:path";
|
|
33908
34094
|
function writeCompiledWiki(root, wiki) {
|
|
33909
34095
|
const written = [];
|
|
33910
34096
|
for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
|
|
33911
34097
|
const absolute = join30(root, relativePath);
|
|
33912
|
-
|
|
34098
|
+
mkdirSync22(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
|
|
33913
34099
|
writeFileSync20(absolute, content);
|
|
33914
34100
|
written.push(relativePath);
|
|
33915
34101
|
}
|
|
@@ -34012,6 +34198,15 @@ async function cmdContextPromote(runtime, project, proposalId, options) {
|
|
|
34012
34198
|
runtime.io.stderr("context promote --evidence must contain at least one durable evidence reference\n");
|
|
34013
34199
|
return 2;
|
|
34014
34200
|
}
|
|
34201
|
+
if (runtime.dryRun) {
|
|
34202
|
+
printPlan(
|
|
34203
|
+
runtime,
|
|
34204
|
+
{ command: "context promote", project, proposalId, evidence },
|
|
34205
|
+
[{ action: "request", target: `POST ${runtime.serverUrl.replace(/\/$/, "")}/v1/context/state/promote` }],
|
|
34206
|
+
`promote proposal ${proposalId} in ${project}`
|
|
34207
|
+
);
|
|
34208
|
+
return 0;
|
|
34209
|
+
}
|
|
34015
34210
|
const response = await hubContextPost({
|
|
34016
34211
|
serverUrl: runtime.serverUrl,
|
|
34017
34212
|
path: "/v1/context/state/promote",
|
|
@@ -34046,6 +34241,16 @@ async function cmdContextWikiCompile(runtime, project, options) {
|
|
|
34046
34241
|
return 1;
|
|
34047
34242
|
}
|
|
34048
34243
|
const compiled = response.body;
|
|
34244
|
+
if (options.out && runtime.dryRun) {
|
|
34245
|
+
const outDir = options.out;
|
|
34246
|
+
printPlan(
|
|
34247
|
+
runtime,
|
|
34248
|
+
{ command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, outDir },
|
|
34249
|
+
compiled.pages.map((page) => ({ action: "write", target: resolve23(outDir, page.path) })),
|
|
34250
|
+
`write ${compiled.pages.length} compiled wiki page(s) to ${outDir}`
|
|
34251
|
+
);
|
|
34252
|
+
return 0;
|
|
34253
|
+
}
|
|
34049
34254
|
let written = [];
|
|
34050
34255
|
if (options.out) {
|
|
34051
34256
|
const pages = new Map(compiled.pages.map((page) => [page.path, page.content]));
|
|
@@ -34093,7 +34298,7 @@ async function cmdSkillsCreate(runtime, options) {
|
|
|
34093
34298
|
const metadata = lifecycle.create({
|
|
34094
34299
|
name: options.name,
|
|
34095
34300
|
description: options.description ?? "",
|
|
34096
|
-
content:
|
|
34301
|
+
content: readFileSync26(options.file, "utf8"),
|
|
34097
34302
|
createdBy: options.createdBy,
|
|
34098
34303
|
sources: {
|
|
34099
34304
|
runIds: csv(options.run) ?? [],
|
|
@@ -34113,7 +34318,7 @@ async function cmdSkillsCreate(runtime, options) {
|
|
|
34113
34318
|
}
|
|
34114
34319
|
}
|
|
34115
34320
|
async function cmdSkillsEvaluate(runtime, skillId, options) {
|
|
34116
|
-
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
|
|
34321
|
+
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
|
|
34117
34322
|
try {
|
|
34118
34323
|
const outcome = lifecycle.evaluate(skillId, {
|
|
34119
34324
|
kind: options.kind,
|
|
@@ -34122,6 +34327,10 @@ async function cmdSkillsEvaluate(runtime, skillId, options) {
|
|
|
34122
34327
|
...options.score !== void 0 ? { score: Number(options.score) } : {},
|
|
34123
34328
|
...options.details ? { details: options.details } : {}
|
|
34124
34329
|
});
|
|
34330
|
+
if (runtime.dryRun) {
|
|
34331
|
+
printPlan(runtime, { command: "skills evaluate", skillId, quarantined: outcome.quarantined, evaluation: outcome.evaluation }, lifecycle.planned, `record ${options.kind} evaluation${outcome.quarantined ? " (candidate quarantined)" : ""}`);
|
|
34332
|
+
return 0;
|
|
34333
|
+
}
|
|
34125
34334
|
print(runtime.io, runtime.json, { ok: true, command: "skills evaluate", skillId, quarantined: outcome.quarantined, evaluation: outcome.evaluation }, `recorded ${options.kind} evaluation${outcome.quarantined ? " (candidate quarantined)" : ""}`);
|
|
34126
34335
|
return 0;
|
|
34127
34336
|
} catch (error) {
|
|
@@ -34132,13 +34341,17 @@ async function cmdSkillsEvaluate(runtime, skillId, options) {
|
|
|
34132
34341
|
}
|
|
34133
34342
|
}
|
|
34134
34343
|
async function cmdSkillsPromote(runtime, skillId, options) {
|
|
34135
|
-
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
|
|
34344
|
+
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
|
|
34136
34345
|
try {
|
|
34137
34346
|
const promoted = lifecycle.promote(skillId, {
|
|
34138
34347
|
decidedBy: options.decidedBy,
|
|
34139
34348
|
reason: options.reason ?? "passed protected evaluation",
|
|
34140
34349
|
evidenceRefs: csv(options.evidence) ?? []
|
|
34141
34350
|
});
|
|
34351
|
+
if (runtime.dryRun) {
|
|
34352
|
+
printPlan(runtime, { command: "skills promote", skillId, metadata: promoted, patchPath: promoted.patchPath }, lifecycle.planned, `promote skill ${skillId}`);
|
|
34353
|
+
return 0;
|
|
34354
|
+
}
|
|
34142
34355
|
print(runtime.io, runtime.json, { ok: true, command: "skills promote", skillId, metadata: promoted, patchPath: promoted.patchPath }, `promoted skill ${skillId} (patch: ${promoted.patchPath})`);
|
|
34143
34356
|
return 0;
|
|
34144
34357
|
} catch (error) {
|
|
@@ -34149,9 +34362,13 @@ async function cmdSkillsPromote(runtime, skillId, options) {
|
|
|
34149
34362
|
}
|
|
34150
34363
|
}
|
|
34151
34364
|
async function cmdSkillsReject(runtime, skillId, options) {
|
|
34152
|
-
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
|
|
34365
|
+
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
|
|
34153
34366
|
try {
|
|
34154
34367
|
const metadata = lifecycle.reject(skillId, { decidedBy: options.decidedBy, reason: options.reason ?? "rejected" });
|
|
34368
|
+
if (runtime.dryRun) {
|
|
34369
|
+
printPlan(runtime, { command: "skills reject", skillId, metadata }, lifecycle.planned, `reject skill ${skillId} (history retained)`);
|
|
34370
|
+
return 0;
|
|
34371
|
+
}
|
|
34155
34372
|
print(runtime.io, runtime.json, { ok: true, command: "skills reject", skillId, metadata }, `rejected skill ${skillId} (history retained)`);
|
|
34156
34373
|
return 0;
|
|
34157
34374
|
} catch (error) {
|
|
@@ -34214,9 +34431,14 @@ async function cmdMemoryNote(runtime, fact, options) {
|
|
|
34214
34431
|
const { record, path: path4 } = createMemoryNote(runtime.cwd, fact, {
|
|
34215
34432
|
scope: options.scope ?? "project",
|
|
34216
34433
|
...options.kind !== void 0 ? { kind: options.kind } : {},
|
|
34217
|
-
...options.body !== void 0 ? { body: options.body } : {}
|
|
34434
|
+
...options.body !== void 0 ? { body: options.body } : {},
|
|
34435
|
+
dryRun: runtime.dryRun
|
|
34218
34436
|
});
|
|
34219
34437
|
const relPath = relative5(runtime.cwd, path4);
|
|
34438
|
+
if (runtime.dryRun) {
|
|
34439
|
+
printPlan(runtime, { command: "memory note", candidate: record, path: relPath }, [{ action: "write", target: path4 }], `record memory candidate in ${relPath} (the id is assigned when it is recorded)`);
|
|
34440
|
+
return 0;
|
|
34441
|
+
}
|
|
34220
34442
|
print(
|
|
34221
34443
|
runtime.io,
|
|
34222
34444
|
runtime.json,
|
|
@@ -34233,7 +34455,16 @@ async function cmdMemoryNote(runtime, fact, options) {
|
|
|
34233
34455
|
}
|
|
34234
34456
|
async function cmdMemorySync(runtime) {
|
|
34235
34457
|
try {
|
|
34236
|
-
const result = syncHarnessMemory(runtime.cwd);
|
|
34458
|
+
const result = syncHarnessMemory(runtime.cwd, { dryRun: runtime.dryRun });
|
|
34459
|
+
if (runtime.dryRun) {
|
|
34460
|
+
printPlan(
|
|
34461
|
+
runtime,
|
|
34462
|
+
{ command: "memory sync", ...result },
|
|
34463
|
+
[...result.created, ...result.updated].map((file) => ({ action: "write", target: join31(runtime.cwd, file) })),
|
|
34464
|
+
"regenerate project memory blocks in AGENTS.md, CLAUDE.md, and GEMINI.md"
|
|
34465
|
+
);
|
|
34466
|
+
return 0;
|
|
34467
|
+
}
|
|
34237
34468
|
print(
|
|
34238
34469
|
runtime.io,
|
|
34239
34470
|
runtime.json,
|
|
@@ -34251,8 +34482,8 @@ async function cmdMemorySync(runtime) {
|
|
|
34251
34482
|
|
|
34252
34483
|
// plugins/kxm/src/cli/hub.ts
|
|
34253
34484
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
34254
|
-
import { existsSync as
|
|
34255
|
-
import { join as join41, resolve as
|
|
34485
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync32, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync23 } from "node:fs";
|
|
34486
|
+
import { join as join41, resolve as resolve26 } from "node:path";
|
|
34256
34487
|
|
|
34257
34488
|
// node_modules/marked/lib/marked.esm.js
|
|
34258
34489
|
function M() {
|
|
@@ -38414,10 +38645,10 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
38414
38645
|
* @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
|
|
38415
38646
|
*/
|
|
38416
38647
|
queryTerminalBackgroundColor({ timeoutMs }) {
|
|
38417
|
-
return new Promise((
|
|
38648
|
+
return new Promise((resolve32) => {
|
|
38418
38649
|
const query = {
|
|
38419
38650
|
settled: false,
|
|
38420
|
-
resolve:
|
|
38651
|
+
resolve: resolve32,
|
|
38421
38652
|
timer: void 0
|
|
38422
38653
|
};
|
|
38423
38654
|
query.timer = setTimeout(() => {
|
|
@@ -38440,7 +38671,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
38440
38671
|
* `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
|
|
38441
38672
|
*/
|
|
38442
38673
|
queryTerminalColorScheme({ timeoutMs }) {
|
|
38443
|
-
return new Promise((
|
|
38674
|
+
return new Promise((resolve32) => {
|
|
38444
38675
|
let settled = false;
|
|
38445
38676
|
let timer;
|
|
38446
38677
|
let unsubscribe = () => {
|
|
@@ -38454,7 +38685,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
38454
38685
|
timer = void 0;
|
|
38455
38686
|
}
|
|
38456
38687
|
unsubscribe();
|
|
38457
|
-
|
|
38688
|
+
resolve32(scheme);
|
|
38458
38689
|
};
|
|
38459
38690
|
unsubscribe = this.onTerminalColorSchemeChange(settle);
|
|
38460
38691
|
timer = setTimeout(() => settle(void 0), timeoutMs);
|
|
@@ -40185,19 +40416,19 @@ import * as path2 from "node:path";
|
|
|
40185
40416
|
|
|
40186
40417
|
// node_modules/@earendil-works/pi-tui/dist/native-module-path.js
|
|
40187
40418
|
import { createRequire as createRequire2 } from "node:module";
|
|
40188
|
-
import { dirname as
|
|
40419
|
+
import { dirname as dirname18, join as join32 } from "node:path";
|
|
40189
40420
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40190
40421
|
var moduleRequire = createRequire2(import.meta.url);
|
|
40191
40422
|
var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
|
|
40192
40423
|
function getNativeModuleCandidates(nativePath, options = {}) {
|
|
40193
|
-
const moduleDir =
|
|
40424
|
+
const moduleDir = dirname18(fileURLToPath2(options.moduleUrl ?? import.meta.url));
|
|
40194
40425
|
const candidates = [];
|
|
40195
40426
|
try {
|
|
40196
40427
|
const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
|
|
40197
|
-
candidates.push(join32(
|
|
40428
|
+
candidates.push(join32(dirname18(packageEntry), "..", nativePath));
|
|
40198
40429
|
} catch {
|
|
40199
40430
|
}
|
|
40200
|
-
candidates.push(join32(moduleDir, "..", nativePath), join32(moduleDir, nativePath), join32(
|
|
40431
|
+
candidates.push(join32(moduleDir, "..", nativePath), join32(moduleDir, nativePath), join32(dirname18(options.execPath ?? process.execPath), nativePath));
|
|
40201
40432
|
return Array.from(new Set(candidates));
|
|
40202
40433
|
}
|
|
40203
40434
|
|
|
@@ -40538,7 +40769,7 @@ var ProcessTerminal = class {
|
|
|
40538
40769
|
break;
|
|
40539
40770
|
if (now - lastDataTime >= idleMs)
|
|
40540
40771
|
break;
|
|
40541
|
-
await new Promise((
|
|
40772
|
+
await new Promise((resolve32) => setTimeout(resolve32, Math.min(idleMs, timeLeft)));
|
|
40542
40773
|
}
|
|
40543
40774
|
} finally {
|
|
40544
40775
|
process.stdin.removeListener("data", onData);
|
|
@@ -42653,8 +42884,8 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
42653
42884
|
var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
|
|
42654
42885
|
|
|
42655
42886
|
// plugins/kxm/src/tui.ts
|
|
42656
|
-
import { mkdirSync as
|
|
42657
|
-
import { join as join36, resolve as
|
|
42887
|
+
import { mkdirSync as mkdirSync23 } from "node:fs";
|
|
42888
|
+
import { join as join36, resolve as resolve25, dirname as dirname19 } from "node:path";
|
|
42658
42889
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
42659
42890
|
|
|
42660
42891
|
// packages/core/tui/src/types/surface.ts
|
|
@@ -42795,9 +43026,9 @@ var SHARED_EFFECT_KINDS = Object.freeze([
|
|
|
42795
43026
|
]);
|
|
42796
43027
|
|
|
42797
43028
|
// plugins/kxm/src/local-snapshot.ts
|
|
42798
|
-
import { existsSync as
|
|
43029
|
+
import { existsSync as existsSync28, readdirSync as readdirSync11, readFileSync as readFileSync27 } from "node:fs";
|
|
42799
43030
|
import { homedir as homedir6 } from "node:os";
|
|
42800
|
-
import { isAbsolute as isAbsolute8, join as join35, resolve as
|
|
43031
|
+
import { isAbsolute as isAbsolute8, join as join35, resolve as resolve24 } from "node:path";
|
|
42801
43032
|
function processExists2(pid) {
|
|
42802
43033
|
try {
|
|
42803
43034
|
process.kill(pid, 0);
|
|
@@ -42919,34 +43150,34 @@ function readOpenMessageMetadata(database) {
|
|
|
42919
43150
|
function resolveKxmSnapshotPaths(cwd, env = process.env) {
|
|
42920
43151
|
const stateDir = env.KXM_STATE_DIR?.trim() || join35(cwd, ".kxm", "state");
|
|
42921
43152
|
const configured = env.KXM_DATA_PATH?.trim();
|
|
42922
|
-
const dataPath = configured ?
|
|
43153
|
+
const dataPath = configured ? resolve24(cwd, configured) : join35(stateDir, "kxm.db");
|
|
42923
43154
|
return { dataPath, stateDir };
|
|
42924
43155
|
}
|
|
42925
43156
|
function resolveKxmStateRoot(stateDir, options) {
|
|
42926
|
-
if (options?.kxmStateRoot &&
|
|
42927
|
-
return
|
|
43157
|
+
if (options?.kxmStateRoot && existsSync28(options.kxmStateRoot)) {
|
|
43158
|
+
return resolve24(options.kxmStateRoot);
|
|
42928
43159
|
}
|
|
42929
|
-
if (
|
|
43160
|
+
if (existsSync28(join35(stateDir, "runtime", "registry.db")) || existsSync28(join35(stateDir, "runtime", "projects"))) {
|
|
42930
43161
|
return stateDir;
|
|
42931
43162
|
}
|
|
42932
43163
|
const env = options?.env ?? process.env;
|
|
42933
43164
|
const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
|
|
42934
|
-
if (explicit && isAbsolute8(explicit) &&
|
|
42935
|
-
return
|
|
43165
|
+
if (explicit && isAbsolute8(explicit) && existsSync28(resolve24(explicit))) {
|
|
43166
|
+
return resolve24(explicit);
|
|
42936
43167
|
}
|
|
42937
43168
|
let base;
|
|
42938
43169
|
if (process.platform === "win32") {
|
|
42939
43170
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
42940
43171
|
base = localAppData && isAbsolute8(localAppData) ? localAppData : join35(homedir6(), "AppData", "Local");
|
|
42941
|
-
base =
|
|
43172
|
+
base = resolve24(base, "KXM");
|
|
42942
43173
|
} else if (process.platform === "darwin") {
|
|
42943
|
-
base =
|
|
43174
|
+
base = resolve24(homedir6(), "Library", "Application Support", "KXM");
|
|
42944
43175
|
} else {
|
|
42945
43176
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
42946
43177
|
base = xdgState && isAbsolute8(xdgState) ? xdgState : join35(homedir6(), ".local", "state");
|
|
42947
|
-
base =
|
|
43178
|
+
base = resolve24(base, "kxm");
|
|
42948
43179
|
}
|
|
42949
|
-
if (
|
|
43180
|
+
if (existsSync28(base)) return base;
|
|
42950
43181
|
return void 0;
|
|
42951
43182
|
}
|
|
42952
43183
|
function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
@@ -42957,9 +43188,9 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
42957
43188
|
let legacyRuns = [];
|
|
42958
43189
|
let legacyRunTotal = 0;
|
|
42959
43190
|
let plans = [];
|
|
42960
|
-
if (
|
|
43191
|
+
if (existsSync28(dataPath)) {
|
|
42961
43192
|
hasLegacy = true;
|
|
42962
|
-
const database =
|
|
43193
|
+
const database = openReadOnlyDatabase(dataPath);
|
|
42963
43194
|
try {
|
|
42964
43195
|
database.exec("PRAGMA busy_timeout = 5000");
|
|
42965
43196
|
agents = readJsonRows(database, "SELECT record FROM agents");
|
|
@@ -42981,10 +43212,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
42981
43212
|
const registryDbPath = join35(runtimeDir, "registry.db");
|
|
42982
43213
|
const projectsDir = join35(runtimeDir, "projects");
|
|
42983
43214
|
const projectKeys = /* @__PURE__ */ new Set();
|
|
42984
|
-
if (
|
|
43215
|
+
if (existsSync28(registryDbPath)) {
|
|
42985
43216
|
hasKxm = true;
|
|
42986
43217
|
try {
|
|
42987
|
-
const regDb =
|
|
43218
|
+
const regDb = openReadOnlyDatabase(registryDbPath);
|
|
42988
43219
|
try {
|
|
42989
43220
|
regDb.exec("PRAGMA busy_timeout = 5000");
|
|
42990
43221
|
const pRows = regDb.prepare("SELECT project_key FROM projects").all();
|
|
@@ -42997,7 +43228,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
42997
43228
|
} catch {
|
|
42998
43229
|
}
|
|
42999
43230
|
}
|
|
43000
|
-
if (
|
|
43231
|
+
if (existsSync28(projectsDir)) {
|
|
43001
43232
|
try {
|
|
43002
43233
|
for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
|
|
43003
43234
|
if (entry.isDirectory()) {
|
|
@@ -43009,10 +43240,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
43009
43240
|
}
|
|
43010
43241
|
for (const key of projectKeys) {
|
|
43011
43242
|
const eventDbPath = join35(projectsDir, key, "run-events.db");
|
|
43012
|
-
if (
|
|
43243
|
+
if (existsSync28(eventDbPath)) {
|
|
43013
43244
|
hasKxm = true;
|
|
43014
43245
|
try {
|
|
43015
|
-
const eventDb =
|
|
43246
|
+
const eventDb = openReadOnlyDatabase(eventDbPath);
|
|
43016
43247
|
try {
|
|
43017
43248
|
eventDb.exec("PRAGMA busy_timeout = 5000");
|
|
43018
43249
|
const runRows = eventDb.prepare(`
|
|
@@ -43039,10 +43270,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
43039
43270
|
}
|
|
43040
43271
|
}
|
|
43041
43272
|
const pids = [];
|
|
43042
|
-
if (
|
|
43273
|
+
if (existsSync28(stateDir)) {
|
|
43043
43274
|
for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
|
|
43044
43275
|
try {
|
|
43045
|
-
const record = JSON.parse(
|
|
43276
|
+
const record = JSON.parse(readFileSync27(join35(stateDir, file), "utf8"));
|
|
43046
43277
|
pids.push({
|
|
43047
43278
|
file,
|
|
43048
43279
|
...record.role ? { role: record.role } : {},
|
|
@@ -43082,7 +43313,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
43082
43313
|
source = "legacy";
|
|
43083
43314
|
}
|
|
43084
43315
|
const telemetryFile = join35(stateDir, "telemetry.jsonl");
|
|
43085
|
-
const spend =
|
|
43316
|
+
const spend = existsSync28(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
|
|
43086
43317
|
return {
|
|
43087
43318
|
source,
|
|
43088
43319
|
agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
|
|
@@ -43217,10 +43448,10 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
|
|
|
43217
43448
|
const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
|
|
43218
43449
|
const branchName = `kxm/run-${cleanRunId}-${desc}`;
|
|
43219
43450
|
const relativeWtPath = join36(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
|
|
43220
|
-
const absoluteWtPath =
|
|
43451
|
+
const absoluteWtPath = resolve25(repoRoot, relativeWtPath);
|
|
43221
43452
|
const jumpCommand = `cd "${absoluteWtPath}"`;
|
|
43222
43453
|
try {
|
|
43223
|
-
|
|
43454
|
+
mkdirSync23(dirname19(absoluteWtPath), { recursive: true });
|
|
43224
43455
|
const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
|
|
43225
43456
|
let wtRes;
|
|
43226
43457
|
if (branchCheck.status === 0) {
|
|
@@ -43618,11 +43849,11 @@ async function readJson(response) {
|
|
|
43618
43849
|
}
|
|
43619
43850
|
async function waitForReconnect(signal, milliseconds = 1e3) {
|
|
43620
43851
|
if (signal.aborted) return;
|
|
43621
|
-
await new Promise((
|
|
43852
|
+
await new Promise((resolve32) => {
|
|
43622
43853
|
const done = () => {
|
|
43623
43854
|
clearTimeout(timer);
|
|
43624
43855
|
signal.removeEventListener("abort", done);
|
|
43625
|
-
|
|
43856
|
+
resolve32();
|
|
43626
43857
|
};
|
|
43627
43858
|
const timer = setTimeout(done, milliseconds);
|
|
43628
43859
|
timer.unref();
|
|
@@ -43985,7 +44216,7 @@ async function runMeshTui(input) {
|
|
|
43985
44216
|
// plugins/kxm/src/session-work.ts
|
|
43986
44217
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
43987
44218
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
43988
|
-
import { existsSync as
|
|
44219
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync24, readFileSync as readFileSync28, renameSync as renameSync8, writeFileSync as writeFileSync21 } from "node:fs";
|
|
43989
44220
|
import { join as join37 } from "node:path";
|
|
43990
44221
|
var MAX_SESSION_BRIEF_TASKS = 5;
|
|
43991
44222
|
var MAX_SESSION_BRIEF_PLANS = 5;
|
|
@@ -44145,8 +44376,8 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
|
|
|
44145
44376
|
function readCachedSessionBrief(stateDir) {
|
|
44146
44377
|
const file = join37(stateDir, "session-brief.json");
|
|
44147
44378
|
try {
|
|
44148
|
-
if (!
|
|
44149
|
-
const raw =
|
|
44379
|
+
if (!existsSync29(file)) return void 0;
|
|
44380
|
+
const raw = readFileSync28(file, "utf8");
|
|
44150
44381
|
const parsed = JSON.parse(raw);
|
|
44151
44382
|
if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
|
|
44152
44383
|
const ageMs = Date.now() - Date.parse(parsed.generatedAt);
|
|
@@ -44161,7 +44392,7 @@ function readCachedSessionBrief(stateDir) {
|
|
|
44161
44392
|
}
|
|
44162
44393
|
function writeCachedSessionBrief(stateDir, brief) {
|
|
44163
44394
|
try {
|
|
44164
|
-
|
|
44395
|
+
mkdirSync24(stateDir, { recursive: true, mode: 448 });
|
|
44165
44396
|
const file = join37(stateDir, "session-brief.json");
|
|
44166
44397
|
const tmp = `${file}.tmp.${randomUUID13().slice(0, 8)}`;
|
|
44167
44398
|
writeFileSync21(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
|
|
@@ -44172,7 +44403,7 @@ function writeCachedSessionBrief(stateDir, brief) {
|
|
|
44172
44403
|
function estimateSessionCost(stateDir) {
|
|
44173
44404
|
try {
|
|
44174
44405
|
const telemetryFile = join37(stateDir, "telemetry.jsonl");
|
|
44175
|
-
if (!
|
|
44406
|
+
if (!existsSync29(telemetryFile)) return void 0;
|
|
44176
44407
|
const records = readRoutingRecords(telemetryFile);
|
|
44177
44408
|
if (records.length === 0) return void 0;
|
|
44178
44409
|
let totalCost = 0;
|
|
@@ -44276,7 +44507,7 @@ function loadSessionBrief(cwd, env = process.env, current, hub, options = {}) {
|
|
|
44276
44507
|
options.sessionToken
|
|
44277
44508
|
);
|
|
44278
44509
|
}
|
|
44279
|
-
writeCachedSessionBrief(paths.stateDir, brief);
|
|
44510
|
+
if (options.writeCache !== false) writeCachedSessionBrief(paths.stateDir, brief);
|
|
44280
44511
|
return brief;
|
|
44281
44512
|
}
|
|
44282
44513
|
async function loadSessionBriefAsync(cwd, env = process.env, current, hub, options = {}) {
|
|
@@ -44340,11 +44571,11 @@ function formatSessionBriefText(brief) {
|
|
|
44340
44571
|
}
|
|
44341
44572
|
|
|
44342
44573
|
// plugins/kxm/src/kxm-install-kind.ts
|
|
44343
|
-
import { existsSync as
|
|
44344
|
-
import { dirname as
|
|
44574
|
+
import { existsSync as existsSync30, readFileSync as readFileSync29 } from "node:fs";
|
|
44575
|
+
import { dirname as dirname20, join as join38 } from "node:path";
|
|
44345
44576
|
function packageIsKxm(root) {
|
|
44346
44577
|
try {
|
|
44347
|
-
const pkg = JSON.parse(
|
|
44578
|
+
const pkg = JSON.parse(readFileSync29(join38(root, "package.json"), "utf8"));
|
|
44348
44579
|
return pkg.name === "@kontextmind/kxm";
|
|
44349
44580
|
} catch {
|
|
44350
44581
|
return false;
|
|
@@ -44398,7 +44629,7 @@ function classifyInstallRoot(probe) {
|
|
|
44398
44629
|
const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join38(probe.homeDir, ".claude");
|
|
44399
44630
|
const pluginsRoot = join38(claudeHome, "plugins");
|
|
44400
44631
|
if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
|
|
44401
|
-
const hasGit =
|
|
44632
|
+
const hasGit = existsSync30(join38(probe.repoRoot, ".git"));
|
|
44402
44633
|
if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
|
|
44403
44634
|
if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
|
|
44404
44635
|
return report("npm-package", probe.repoRoot);
|
|
@@ -44412,21 +44643,21 @@ function resolveInstallKind(probe, npmGlobalRoot) {
|
|
|
44412
44643
|
if (typeof globalRoot !== "string" || globalRoot.trim() === "") {
|
|
44413
44644
|
return report("unknown", classified.root);
|
|
44414
44645
|
}
|
|
44415
|
-
const expected =
|
|
44646
|
+
const expected = dirname20(dirname20(classified.root));
|
|
44416
44647
|
if (sameHostPath2(globalRoot.trim(), expected, probe.platform)) return report("npm-global", classified.root);
|
|
44417
44648
|
return report("npm-local", classified.root);
|
|
44418
44649
|
}
|
|
44419
44650
|
|
|
44420
44651
|
// plugins/kxm/src/kxm-update-config.ts
|
|
44421
44652
|
var import_yaml15 = __toESM(require_dist(), 1);
|
|
44422
|
-
import { existsSync as
|
|
44653
|
+
import { existsSync as existsSync31, readFileSync as readFileSync30 } from "node:fs";
|
|
44423
44654
|
import { join as join39 } from "node:path";
|
|
44424
44655
|
function loadKxmUpdateConfig(env = process.env) {
|
|
44425
44656
|
const path4 = join39(kxmUserStateRoot({ env }), "update.yaml");
|
|
44426
|
-
if (!
|
|
44657
|
+
if (!existsSync31(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
|
|
44427
44658
|
let parsed;
|
|
44428
44659
|
try {
|
|
44429
|
-
parsed = (0, import_yaml15.parse)(
|
|
44660
|
+
parsed = (0, import_yaml15.parse)(readFileSync30(path4, "utf8"));
|
|
44430
44661
|
} catch {
|
|
44431
44662
|
throw new KxmUpdateConfigError("update.yaml is not valid YAML");
|
|
44432
44663
|
}
|
|
@@ -44452,7 +44683,7 @@ function loadKxmUpdateConfig(env = process.env) {
|
|
|
44452
44683
|
}
|
|
44453
44684
|
|
|
44454
44685
|
// plugins/kxm/src/session.ts
|
|
44455
|
-
import { existsSync as
|
|
44686
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync25, readFileSync as readFileSync31, writeFileSync as writeFileSync22 } from "node:fs";
|
|
44456
44687
|
import { join as join40 } from "node:path";
|
|
44457
44688
|
var SESSION_SCHEMA = "kxm.session.v1";
|
|
44458
44689
|
var SessionConfigError = class extends Error {
|
|
@@ -44532,10 +44763,10 @@ function loadRosterMap(configDir) {
|
|
|
44532
44763
|
return byName;
|
|
44533
44764
|
}
|
|
44534
44765
|
function loadRoster(path4, key) {
|
|
44535
|
-
if (!
|
|
44766
|
+
if (!existsSync32(path4)) return [];
|
|
44536
44767
|
let parsed;
|
|
44537
44768
|
try {
|
|
44538
|
-
parsed = JSON.parse(
|
|
44769
|
+
parsed = JSON.parse(readFileSync31(path4, "utf8"));
|
|
44539
44770
|
} catch (error) {
|
|
44540
44771
|
throw new SessionConfigError(
|
|
44541
44772
|
`${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -44576,7 +44807,7 @@ function writeSession(assetsDir, session, dryRun = false) {
|
|
|
44576
44807
|
const dir = join40(assetsDir, "sessions", session.id);
|
|
44577
44808
|
const path4 = join40(dir, "session.json");
|
|
44578
44809
|
if (!dryRun) {
|
|
44579
|
-
|
|
44810
|
+
mkdirSync25(dir, { recursive: true });
|
|
44580
44811
|
writeFileSync22(path4, `${JSON.stringify(session, null, 2)}
|
|
44581
44812
|
`, { encoding: "utf8" });
|
|
44582
44813
|
}
|
|
@@ -44585,7 +44816,7 @@ function writeSession(assetsDir, session, dryRun = false) {
|
|
|
44585
44816
|
|
|
44586
44817
|
// plugins/kxm/src/cli/hub.ts
|
|
44587
44818
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
44588
|
-
import { dirname as
|
|
44819
|
+
import { dirname as dirname21 } from "node:path";
|
|
44589
44820
|
import { homedir as homedir7 } from "node:os";
|
|
44590
44821
|
async function hubGet(url, fetchImpl) {
|
|
44591
44822
|
try {
|
|
@@ -44603,7 +44834,7 @@ async function hubGet(url, fetchImpl) {
|
|
|
44603
44834
|
}
|
|
44604
44835
|
function installProbeFrom(runtime) {
|
|
44605
44836
|
const partial = runtime.io.installProbe ?? {};
|
|
44606
|
-
const moduleDir = partial.moduleDir ??
|
|
44837
|
+
const moduleDir = partial.moduleDir ?? dirname21(fileURLToPath3(import.meta.url));
|
|
44607
44838
|
return {
|
|
44608
44839
|
moduleDir,
|
|
44609
44840
|
// Classify the install that is **running**, never the directory the caller
|
|
@@ -44624,12 +44855,12 @@ function moduleInstallRoot() {
|
|
|
44624
44855
|
try {
|
|
44625
44856
|
return findKxmRepoRoot(import.meta.url);
|
|
44626
44857
|
} catch {
|
|
44627
|
-
return
|
|
44858
|
+
return dirname21(fileURLToPath3(import.meta.url));
|
|
44628
44859
|
}
|
|
44629
44860
|
}
|
|
44630
44861
|
function warnIgnoredProjectUpdateYaml(runtime) {
|
|
44631
44862
|
const projectFile = join41(runtime.dirs.workspace, "update.yaml");
|
|
44632
|
-
if (!
|
|
44863
|
+
if (!existsSync33(projectFile)) return;
|
|
44633
44864
|
const userFile = join41(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
44634
44865
|
runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
|
|
44635
44866
|
`);
|
|
@@ -44648,7 +44879,7 @@ async function refreshKxmUpdateNotice(runtime, config, current) {
|
|
|
44648
44879
|
}
|
|
44649
44880
|
const fetched = await fetchLatestKxmVersion(resolved.source, runtime.env, runtime.fetchImpl);
|
|
44650
44881
|
const notice = noticeFromVersions(installed, fetched.latest, resolved, fetched.error, fetched.asset);
|
|
44651
|
-
writeUpdateCache(runtime.dirs.state, notice);
|
|
44882
|
+
if (!runtime.dryRun) writeUpdateCache(runtime.dirs.state, notice);
|
|
44652
44883
|
return notice;
|
|
44653
44884
|
}
|
|
44654
44885
|
async function cmdStatus(runtime) {
|
|
@@ -44678,7 +44909,7 @@ async function cmdDash(runtime, options = {}) {
|
|
|
44678
44909
|
return 2;
|
|
44679
44910
|
}
|
|
44680
44911
|
const screen = requested;
|
|
44681
|
-
const dataPath =
|
|
44912
|
+
const dataPath = resolve26(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
|
|
44682
44913
|
if (runtime.dryRun) {
|
|
44683
44914
|
print(runtime.io, runtime.json, {
|
|
44684
44915
|
ok: true,
|
|
@@ -44911,7 +45142,7 @@ async function cmdWorker(runtime, options) {
|
|
|
44911
45142
|
return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
|
|
44912
45143
|
}
|
|
44913
45144
|
async function cmdStop(runtime, waitMsFlag) {
|
|
44914
|
-
const pids =
|
|
45145
|
+
const pids = existsSync33(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
|
|
44915
45146
|
if (runtime.dryRun) {
|
|
44916
45147
|
print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
|
|
44917
45148
|
return 0;
|
|
@@ -44926,7 +45157,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
44926
45157
|
const orphans = [];
|
|
44927
45158
|
for (const file of pids) {
|
|
44928
45159
|
try {
|
|
44929
|
-
const record = JSON.parse(
|
|
45160
|
+
const record = JSON.parse(readFileSync32(join41(runtime.dirs.state, file), "utf8"));
|
|
44930
45161
|
const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
|
|
44931
45162
|
const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
|
|
44932
45163
|
if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
|
|
@@ -44964,7 +45195,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
44964
45195
|
while (Date.now() <= deadline && stopped.size < requested.length) {
|
|
44965
45196
|
for (const [file, record] of records) {
|
|
44966
45197
|
try {
|
|
44967
|
-
const current = JSON.parse(
|
|
45198
|
+
const current = JSON.parse(readFileSync32(join41(runtime.dirs.state, file), "utf8"));
|
|
44968
45199
|
if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
|
|
44969
45200
|
} catch {
|
|
44970
45201
|
stopped.add(file);
|
|
@@ -44977,7 +45208,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
44977
45208
|
for (const file of orphans) {
|
|
44978
45209
|
while (Date.now() <= deadlineOrphans) {
|
|
44979
45210
|
try {
|
|
44980
|
-
const current = JSON.parse(
|
|
45211
|
+
const current = JSON.parse(readFileSync32(join41(runtime.dirs.state, file), "utf8"));
|
|
44981
45212
|
if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
|
|
44982
45213
|
} catch {
|
|
44983
45214
|
break;
|
|
@@ -44985,14 +45216,14 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
44985
45216
|
await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
|
|
44986
45217
|
}
|
|
44987
45218
|
try {
|
|
44988
|
-
const current = JSON.parse(
|
|
45219
|
+
const current = JSON.parse(readFileSync32(join41(runtime.dirs.state, file), "utf8"));
|
|
44989
45220
|
if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
|
|
44990
45221
|
try {
|
|
44991
45222
|
process.kill(current.serverPid, "SIGKILL");
|
|
44992
45223
|
} catch {
|
|
44993
45224
|
}
|
|
44994
45225
|
}
|
|
44995
|
-
|
|
45226
|
+
rmSync11(join41(runtime.dirs.state, file), { force: true });
|
|
44996
45227
|
} catch {
|
|
44997
45228
|
}
|
|
44998
45229
|
stopped.add(file);
|
|
@@ -45003,11 +45234,11 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45003
45234
|
}
|
|
45004
45235
|
async function cmdSessionStatus(runtime) {
|
|
45005
45236
|
const stateDir = runtime.dirs.state;
|
|
45006
|
-
const names3 =
|
|
45237
|
+
const names3 = existsSync33(stateDir) ? readdirSync12(stateDir) : [];
|
|
45007
45238
|
const claims = [];
|
|
45008
45239
|
for (const file of names3.filter((name) => name.endsWith(".pid"))) {
|
|
45009
45240
|
try {
|
|
45010
|
-
const record = JSON.parse(
|
|
45241
|
+
const record = JSON.parse(readFileSync32(join41(stateDir, file), "utf8"));
|
|
45011
45242
|
claims.push({
|
|
45012
45243
|
file,
|
|
45013
45244
|
role: record.role,
|
|
@@ -45022,7 +45253,7 @@ async function cmdSessionStatus(runtime) {
|
|
|
45022
45253
|
const recoveries = [];
|
|
45023
45254
|
for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
|
|
45024
45255
|
try {
|
|
45025
|
-
const envelope = JSON.parse(
|
|
45256
|
+
const envelope = JSON.parse(readFileSync32(join41(stateDir, file), "utf8"));
|
|
45026
45257
|
recoveries.push({
|
|
45027
45258
|
file,
|
|
45028
45259
|
reason: envelope.reason,
|
|
@@ -45046,6 +45277,12 @@ async function cmdSessionStatus(runtime) {
|
|
|
45046
45277
|
return 0;
|
|
45047
45278
|
}
|
|
45048
45279
|
async function cmdAuthToken(runtime, options = {}) {
|
|
45280
|
+
const tokenFile = sessionTokenPath(runtime.env.KXM_USER_CONFIG_DIR);
|
|
45281
|
+
if (options.clear && runtime.dryRun) {
|
|
45282
|
+
const present = existsSync33(tokenFile);
|
|
45283
|
+
printPlan(runtime, { command: "auth token", cleared: present }, present ? [{ action: "delete", target: tokenFile }] : [], present ? "clear the session token from disk" : "no session token file to clear");
|
|
45284
|
+
return 0;
|
|
45285
|
+
}
|
|
45049
45286
|
if (options.clear) {
|
|
45050
45287
|
const cleared = clearSessionTokenFromDisk({ userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45051
45288
|
print(
|
|
@@ -45101,33 +45338,37 @@ async function cmdAuthToken(runtime, options = {}) {
|
|
|
45101
45338
|
);
|
|
45102
45339
|
return 1;
|
|
45103
45340
|
}
|
|
45341
|
+
const existingToken = options.issue ? void 0 : readSessionTokenFromDisk({ userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45342
|
+
if (runtime.dryRun && !existingToken) {
|
|
45343
|
+
printPlan(runtime, { command: "auth token" }, [{ action: "write", target: tokenFile }], "issue an operator session token and persist it");
|
|
45344
|
+
return 0;
|
|
45345
|
+
}
|
|
45104
45346
|
let token;
|
|
45105
|
-
if (
|
|
45347
|
+
if (existingToken) {
|
|
45348
|
+
token = existingToken.token;
|
|
45349
|
+
} else {
|
|
45106
45350
|
token = mintSessionToken({ preset: "operator" });
|
|
45107
45351
|
persistSessionTokenToDisk(token, { userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45108
|
-
} else {
|
|
45109
|
-
const existing = readSessionTokenFromDisk({ userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45110
|
-
if (existing) {
|
|
45111
|
-
token = existing.token;
|
|
45112
|
-
} else {
|
|
45113
|
-
token = mintSessionToken({ preset: "operator" });
|
|
45114
|
-
persistSessionTokenToDisk(token, { userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45115
|
-
}
|
|
45116
45352
|
}
|
|
45117
45353
|
print(runtime.io, runtime.json, { ok: true, command: "auth token", token }, token);
|
|
45118
45354
|
return 0;
|
|
45119
45355
|
}
|
|
45120
45356
|
async function cmdSessionBrief(runtime, options = {}) {
|
|
45121
45357
|
const existing = readSessionTokenFromDisk({ userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45122
|
-
const
|
|
45123
|
-
|
|
45358
|
+
const planned = existing ? [] : [{ action: "write", target: sessionTokenPath(runtime.env.KXM_USER_CONFIG_DIR) }];
|
|
45359
|
+
const sessionToken = existing?.token ?? (runtime.dryRun ? void 0 : mintSessionToken({ preset: "operator" }));
|
|
45360
|
+
if (!existing && sessionToken) {
|
|
45124
45361
|
persistSessionTokenToDisk(sessionToken, { userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45125
45362
|
}
|
|
45126
45363
|
if (options.token) {
|
|
45364
|
+
if (!sessionToken) {
|
|
45365
|
+
printPlan(runtime, { command: "session brief" }, planned, "issue an operator session token and persist it");
|
|
45366
|
+
return 0;
|
|
45367
|
+
}
|
|
45127
45368
|
print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
|
|
45128
45369
|
return 0;
|
|
45129
45370
|
}
|
|
45130
|
-
const dataPath =
|
|
45371
|
+
const dataPath = resolve26(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
|
|
45131
45372
|
const env = {
|
|
45132
45373
|
...runtime.env,
|
|
45133
45374
|
KXM_STATE_DIR: runtime.dirs.state,
|
|
@@ -45149,8 +45390,17 @@ async function cmdSessionBrief(runtime, options = {}) {
|
|
|
45149
45390
|
}
|
|
45150
45391
|
const brief = await loadSessionBriefAsync(runtime.dirs.workdir, env, void 0, hub, {
|
|
45151
45392
|
fetchImpl: runtime.fetchImpl,
|
|
45152
|
-
sessionToken
|
|
45393
|
+
...sessionToken ? { sessionToken } : {},
|
|
45394
|
+
writeCache: !runtime.dryRun
|
|
45153
45395
|
});
|
|
45396
|
+
if (runtime.dryRun) {
|
|
45397
|
+
if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join41(runtime.dirs.state, "session-brief.json") });
|
|
45398
|
+
print(runtime.io, runtime.json, { ...brief, dryRun: true, planned }, [
|
|
45399
|
+
options.status ? brief.statusLine : formatSessionBriefText(brief),
|
|
45400
|
+
...planned.map((change) => `dry run: would ${change.action} ${change.target}`)
|
|
45401
|
+
].join("\n"));
|
|
45402
|
+
return 0;
|
|
45403
|
+
}
|
|
45154
45404
|
if (options.status) {
|
|
45155
45405
|
print(runtime.io, runtime.json, brief, brief.statusLine);
|
|
45156
45406
|
return 0;
|
|
@@ -45204,7 +45454,7 @@ async function cmdSessionStart(runtime, options) {
|
|
|
45204
45454
|
...workflowId ? workflowAssetDirs(runtime.dirs.assets, workflowId) : []
|
|
45205
45455
|
];
|
|
45206
45456
|
if (!runtime.dryRun) {
|
|
45207
|
-
for (const directory of created)
|
|
45457
|
+
for (const directory of created) mkdirSync26(directory, { recursive: true });
|
|
45208
45458
|
writeSession(runtime.dirs.assets, session);
|
|
45209
45459
|
}
|
|
45210
45460
|
print(runtime.io, runtime.json, {
|
|
@@ -45220,15 +45470,15 @@ async function cmdSessionStart(runtime, options) {
|
|
|
45220
45470
|
// plugins/kxm/src/cli/system.ts
|
|
45221
45471
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
45222
45472
|
import { createHash as createHash16 } from "node:crypto";
|
|
45223
|
-
import { existsSync as
|
|
45473
|
+
import { existsSync as existsSync39, mkdtempSync as mkdtempSync3, readFileSync as readFileSync37, rmSync as rmSync13 } from "node:fs";
|
|
45224
45474
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
45225
|
-
import { join as join47, resolve as
|
|
45475
|
+
import { join as join47, resolve as resolve30 } from "node:path";
|
|
45226
45476
|
import { createInterface as createInterface3 } from "node:readline";
|
|
45227
45477
|
|
|
45228
45478
|
// plugins/kxm/src/improve.ts
|
|
45229
45479
|
import { createHash as createHash15 } from "node:crypto";
|
|
45230
|
-
import { existsSync as
|
|
45231
|
-
import { join as join42, relative as relative6, resolve as
|
|
45480
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync27, writeFileSync as writeFileSync24 } from "node:fs";
|
|
45481
|
+
import { join as join42, relative as relative6, resolve as resolve27 } from "node:path";
|
|
45232
45482
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
45233
45483
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
45234
45484
|
function classifyCandidateKind(stepId, agentRole) {
|
|
@@ -45399,8 +45649,8 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
45399
45649
|
});
|
|
45400
45650
|
}
|
|
45401
45651
|
function buildImprovementReport(records, options = {}) {
|
|
45402
|
-
const projectRoot = options.projectRoot ?
|
|
45403
|
-
const candidatesDir = options.candidatesDir ?
|
|
45652
|
+
const projectRoot = options.projectRoot ? resolve27(options.projectRoot) : process.cwd();
|
|
45653
|
+
const candidatesDir = options.candidatesDir ? resolve27(options.candidatesDir) : join42(projectRoot, ".kxm", "candidates");
|
|
45404
45654
|
const groups = groupRoutingRecords(records, options);
|
|
45405
45655
|
const candidates = [];
|
|
45406
45656
|
for (const group of groups) {
|
|
@@ -45434,8 +45684,8 @@ function buildImprovementReport(records, options = {}) {
|
|
|
45434
45684
|
createdAt: nowIso()
|
|
45435
45685
|
};
|
|
45436
45686
|
if (!options.dryRun) {
|
|
45437
|
-
if (!
|
|
45438
|
-
|
|
45687
|
+
if (!existsSync34(candidatesDir)) {
|
|
45688
|
+
mkdirSync27(candidatesDir, { recursive: true });
|
|
45439
45689
|
}
|
|
45440
45690
|
writeFileSync24(diffFilePath, diff, "utf8");
|
|
45441
45691
|
const jsonFilePath = join42(candidatesDir, `${group.candidateId}.json`);
|
|
@@ -45456,7 +45706,7 @@ function writeImprovementReport(improvementsDir, report2, dryRun = false) {
|
|
|
45456
45706
|
const stamp = report2.createdAt.replace(/[:.]/g, "-");
|
|
45457
45707
|
const path4 = join42(improvementsDir, `${stamp}.json`);
|
|
45458
45708
|
if (!dryRun) {
|
|
45459
|
-
|
|
45709
|
+
mkdirSync27(improvementsDir, { recursive: true });
|
|
45460
45710
|
writeFileSync24(path4, `${JSON.stringify(report2, null, 2)}
|
|
45461
45711
|
`, { encoding: "utf8" });
|
|
45462
45712
|
}
|
|
@@ -45497,7 +45747,7 @@ function formatImprovementReport(report2) {
|
|
|
45497
45747
|
|
|
45498
45748
|
// plugins/kxm/src/modes.ts
|
|
45499
45749
|
var import_yaml16 = __toESM(require_dist(), 1);
|
|
45500
|
-
import { existsSync as
|
|
45750
|
+
import { existsSync as existsSync35, readFileSync as readFileSync34 } from "node:fs";
|
|
45501
45751
|
import { join as join43 } from "node:path";
|
|
45502
45752
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
45503
45753
|
schema: "kxm.modes.v1",
|
|
@@ -45562,11 +45812,11 @@ function loadModesConfig(projectRoot) {
|
|
|
45562
45812
|
return DEFAULT_MODES_CONFIG;
|
|
45563
45813
|
}
|
|
45564
45814
|
const modesPath = join43(projectRoot, ".kxm", "modes.yaml");
|
|
45565
|
-
if (!
|
|
45815
|
+
if (!existsSync35(modesPath)) {
|
|
45566
45816
|
return DEFAULT_MODES_CONFIG;
|
|
45567
45817
|
}
|
|
45568
45818
|
try {
|
|
45569
|
-
const raw =
|
|
45819
|
+
const raw = readFileSync34(modesPath, "utf8");
|
|
45570
45820
|
const parsed = (0, import_yaml16.parse)(raw);
|
|
45571
45821
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
45572
45822
|
return {
|
|
@@ -45654,9 +45904,9 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
45654
45904
|
for (const relPath of resolved.contextFiles) {
|
|
45655
45905
|
const fullPath = join43(projectRoot, relPath);
|
|
45656
45906
|
let chars = 0;
|
|
45657
|
-
if (
|
|
45907
|
+
if (existsSync35(fullPath)) {
|
|
45658
45908
|
try {
|
|
45659
|
-
chars =
|
|
45909
|
+
chars = readFileSync34(fullPath, "utf8").length;
|
|
45660
45910
|
} catch {
|
|
45661
45911
|
chars = 0;
|
|
45662
45912
|
}
|
|
@@ -45788,9 +46038,9 @@ ${divider}
|
|
|
45788
46038
|
|
|
45789
46039
|
// plugins/kxm/src/ssh-remote.ts
|
|
45790
46040
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
45791
|
-
import { existsSync as
|
|
46041
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync28, readFileSync as readFileSync35, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
|
|
45792
46042
|
import { homedir as homedir8 } from "node:os";
|
|
45793
|
-
import { join as join44, resolve as
|
|
46043
|
+
import { join as join44, resolve as resolve28 } from "node:path";
|
|
45794
46044
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
45795
46045
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
45796
46046
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -45817,11 +46067,11 @@ function truncateSshOutput(raw) {
|
|
|
45817
46067
|
}
|
|
45818
46068
|
function parseSshConfig(configPath) {
|
|
45819
46069
|
const targetPath = configPath ?? join44(homedir8(), ".ssh", "config");
|
|
45820
|
-
if (!
|
|
46070
|
+
if (!existsSync36(targetPath)) {
|
|
45821
46071
|
return [];
|
|
45822
46072
|
}
|
|
45823
46073
|
try {
|
|
45824
|
-
const content =
|
|
46074
|
+
const content = readFileSync35(targetPath, "utf-8");
|
|
45825
46075
|
const lines = content.split("\n");
|
|
45826
46076
|
const hosts = [];
|
|
45827
46077
|
let currentHosts = [];
|
|
@@ -45898,9 +46148,9 @@ function resolveSshHostG(host, execFn = spawnSync7) {
|
|
|
45898
46148
|
}
|
|
45899
46149
|
}
|
|
45900
46150
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
45901
|
-
const resolved =
|
|
45902
|
-
if (!
|
|
45903
|
-
|
|
46151
|
+
const resolved = resolve28(socketDir);
|
|
46152
|
+
if (!existsSync36(resolved)) {
|
|
46153
|
+
mkdirSync28(resolved, { recursive: true, mode: 448 });
|
|
45904
46154
|
}
|
|
45905
46155
|
return resolved;
|
|
45906
46156
|
}
|
|
@@ -46388,9 +46638,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
46388
46638
|
}
|
|
46389
46639
|
|
|
46390
46640
|
// plugins/kxm/src/completion-install.ts
|
|
46391
|
-
import { existsSync as
|
|
46641
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync25 } from "node:fs";
|
|
46392
46642
|
import { homedir as homedir9 } from "node:os";
|
|
46393
|
-
import { basename as basename9, delimiter, dirname as
|
|
46643
|
+
import { basename as basename9, delimiter, dirname as dirname23, isAbsolute as isAbsolute9, join as join45, resolve as resolve29 } from "node:path";
|
|
46394
46644
|
var COMPLETION_MARKER = "# kxm completion";
|
|
46395
46645
|
var PATH_MARKER = "# kxm path";
|
|
46396
46646
|
function detectShell(env = process.env, platform = process.platform) {
|
|
@@ -46406,30 +46656,30 @@ function effectiveHome(options) {
|
|
|
46406
46656
|
const env = options.env ?? process.env;
|
|
46407
46657
|
if (options.homeDir) return options.homeDir;
|
|
46408
46658
|
const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
|
|
46409
|
-
return envHome && envHome.length > 0 ?
|
|
46659
|
+
return envHome && envHome.length > 0 ? resolve29(envHome) : homedir9();
|
|
46410
46660
|
}
|
|
46411
46661
|
function completionScriptPath(shell, options = {}) {
|
|
46412
46662
|
const env = options.env ?? process.env;
|
|
46413
46663
|
const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
|
|
46414
|
-
const base = explicit && explicit.length > 0 ?
|
|
46664
|
+
const base = explicit && explicit.length > 0 ? resolve29(explicit) : resolve29(effectiveHome(options), ".config", "kxm");
|
|
46415
46665
|
return join45(base, "completions", `kxm.${shell}`);
|
|
46416
46666
|
}
|
|
46417
46667
|
function bashRcCandidate(options) {
|
|
46418
46668
|
const home = effectiveHome(options);
|
|
46419
46669
|
const candidates = [join45(home, ".bashrc"), join45(home, ".bash_profile")];
|
|
46420
|
-
const existing = candidates.find((candidate) =>
|
|
46670
|
+
const existing = candidates.find((candidate) => existsSync37(candidate));
|
|
46421
46671
|
return existing ?? candidates[0];
|
|
46422
46672
|
}
|
|
46423
46673
|
function zshRcCandidate(options) {
|
|
46424
46674
|
const env = options.env ?? process.env;
|
|
46425
46675
|
const home = effectiveHome(options);
|
|
46426
|
-
if (env.ZDOTDIR?.trim()) return join45(
|
|
46676
|
+
if (env.ZDOTDIR?.trim()) return join45(resolve29(env.ZDOTDIR.trim()), ".zshrc");
|
|
46427
46677
|
return join45(home, ".zshrc");
|
|
46428
46678
|
}
|
|
46429
46679
|
function fishCompletionTarget(shell, options) {
|
|
46430
46680
|
const env = options.env ?? process.env;
|
|
46431
46681
|
const home = effectiveHome(options);
|
|
46432
|
-
if (env.XDG_CONFIG_HOME?.trim()) return join45(
|
|
46682
|
+
if (env.XDG_CONFIG_HOME?.trim()) return join45(resolve29(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
|
|
46433
46683
|
return join45(home, ".config", "fish", "completions", "kxm.fish");
|
|
46434
46684
|
}
|
|
46435
46685
|
function completionRcTarget(shell, options = {}) {
|
|
@@ -46462,16 +46712,16 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
46462
46712
|
const scriptPath = completionScriptPath(shell, options);
|
|
46463
46713
|
const { rcFile } = completionRcTarget(shell, options);
|
|
46464
46714
|
const script = generateShellCompletion(shell);
|
|
46465
|
-
const existingScript =
|
|
46466
|
-
const existingScriptMatches = existingScript &&
|
|
46715
|
+
const existingScript = existsSync37(scriptPath);
|
|
46716
|
+
const existingScriptMatches = existingScript && readFileSync36(scriptPath, "utf8") === script;
|
|
46467
46717
|
let rcModified = false;
|
|
46468
46718
|
let alreadyInstalled = false;
|
|
46469
46719
|
if (shell === "fish") {
|
|
46470
46720
|
const fishTarget = fishCompletionTarget(shell, options);
|
|
46471
|
-
const fishInstalled =
|
|
46721
|
+
const fishInstalled = existsSync37(fishTarget) && readFileSync36(fishTarget, "utf8") === script;
|
|
46472
46722
|
alreadyInstalled = fishInstalled;
|
|
46473
46723
|
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
46474
|
-
|
|
46724
|
+
mkdirSync29(resolve29(fishTarget, ".."), { recursive: true });
|
|
46475
46725
|
writeFileSync25(fishTarget, script, { encoding: "utf8" });
|
|
46476
46726
|
}
|
|
46477
46727
|
return {
|
|
@@ -46485,12 +46735,12 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
46485
46735
|
}
|
|
46486
46736
|
if (rcFile) {
|
|
46487
46737
|
const line = sourceLine(shell, scriptPath);
|
|
46488
|
-
const rcContent =
|
|
46738
|
+
const rcContent = existsSync37(rcFile) ? readFileSync36(rcFile, "utf8") : "";
|
|
46489
46739
|
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
46490
46740
|
}
|
|
46491
46741
|
if (!options.dryRun) {
|
|
46492
46742
|
if (!existingScriptMatches || options.overwrite) {
|
|
46493
|
-
|
|
46743
|
+
mkdirSync29(resolve29(scriptPath, ".."), { recursive: true });
|
|
46494
46744
|
writeFileSync25(scriptPath, script, { encoding: "utf8" });
|
|
46495
46745
|
}
|
|
46496
46746
|
if (rcFile && !alreadyInstalled) {
|
|
@@ -46500,8 +46750,8 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
46500
46750
|
${marker}
|
|
46501
46751
|
${line}
|
|
46502
46752
|
`;
|
|
46503
|
-
|
|
46504
|
-
writeFileSync25(rcFile, (
|
|
46753
|
+
mkdirSync29(resolve29(rcFile, ".."), { recursive: true });
|
|
46754
|
+
writeFileSync25(rcFile, (existsSync37(rcFile) ? readFileSync36(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
46505
46755
|
rcModified = true;
|
|
46506
46756
|
}
|
|
46507
46757
|
}
|
|
@@ -46517,14 +46767,14 @@ ${line}
|
|
|
46517
46767
|
function kxmBinDir(env = process.env) {
|
|
46518
46768
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
46519
46769
|
if (argv1 && isAbsolute9(argv1)) {
|
|
46520
|
-
const dir =
|
|
46521
|
-
if (
|
|
46770
|
+
const dir = dirname23(argv1);
|
|
46771
|
+
if (existsSync37(join45(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
46522
46772
|
}
|
|
46523
46773
|
const pathEnv = env.PATH ?? "";
|
|
46524
46774
|
for (const part of pathEnv.split(delimiter)) {
|
|
46525
46775
|
if (!part) continue;
|
|
46526
|
-
const candidate =
|
|
46527
|
-
if (
|
|
46776
|
+
const candidate = resolve29(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
46777
|
+
if (existsSync37(candidate)) return resolve29(part);
|
|
46528
46778
|
}
|
|
46529
46779
|
return void 0;
|
|
46530
46780
|
}
|
|
@@ -46542,10 +46792,10 @@ function installPathEntry(shellInput, options = {}) {
|
|
|
46542
46792
|
}
|
|
46543
46793
|
const { rcFile } = completionRcTarget(shell, options);
|
|
46544
46794
|
const line = pathLine(binDir);
|
|
46545
|
-
const rcContent =
|
|
46795
|
+
const rcContent = existsSync37(rcFile ?? "") ? readFileSync36(rcFile, "utf8") : "";
|
|
46546
46796
|
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
46547
46797
|
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
46548
|
-
|
|
46798
|
+
mkdirSync29(dirname23(rcFile), { recursive: true });
|
|
46549
46799
|
writeFileSync25(rcFile, rcContent + `
|
|
46550
46800
|
${PATH_MARKER}
|
|
46551
46801
|
${line}
|
|
@@ -46556,8 +46806,8 @@ ${line}
|
|
|
46556
46806
|
|
|
46557
46807
|
// plugins/kxm/src/init-guide-setup.ts
|
|
46558
46808
|
var import_yaml17 = __toESM(require_dist(), 1);
|
|
46559
|
-
import { existsSync as
|
|
46560
|
-
import { dirname as
|
|
46809
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync30, writeFileSync as writeFileSync26 } from "node:fs";
|
|
46810
|
+
import { dirname as dirname24, join as join46 } from "node:path";
|
|
46561
46811
|
var NATIVE_VENDOR_HARNESS = Object.freeze({
|
|
46562
46812
|
anthropic: "claude",
|
|
46563
46813
|
openai: "codex",
|
|
@@ -46972,11 +47222,11 @@ function writeGuideSetupFiles(files) {
|
|
|
46972
47222
|
const written = [];
|
|
46973
47223
|
const existed = [];
|
|
46974
47224
|
for (const file of files) {
|
|
46975
|
-
if (
|
|
47225
|
+
if (existsSync38(file.path)) {
|
|
46976
47226
|
existed.push(file.path);
|
|
46977
47227
|
continue;
|
|
46978
47228
|
}
|
|
46979
|
-
|
|
47229
|
+
mkdirSync30(dirname24(file.path), { recursive: true });
|
|
46980
47230
|
writeFileSync26(file.path, file.content, "utf8");
|
|
46981
47231
|
written.push(file.path);
|
|
46982
47232
|
}
|
|
@@ -47040,7 +47290,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
47040
47290
|
detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
|
|
47041
47291
|
};
|
|
47042
47292
|
}
|
|
47043
|
-
const releaseDir =
|
|
47293
|
+
const releaseDir = mkdtempSync3(join47(tmpdir2(), "kxm-pkg-update-"));
|
|
47044
47294
|
try {
|
|
47045
47295
|
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
|
|
47046
47296
|
if (runtime.dryRun) {
|
|
@@ -47049,7 +47299,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
47049
47299
|
for (const step of planned) {
|
|
47050
47300
|
if (step.kind === "verify") {
|
|
47051
47301
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
47052
|
-
const actual =
|
|
47302
|
+
const actual = existsSync39(step.path) ? createHash16("sha256").update(readFileSync37(step.path)).digest("hex") : "missing";
|
|
47053
47303
|
return {
|
|
47054
47304
|
ok: false,
|
|
47055
47305
|
error: "release_digest_mismatch",
|
|
@@ -47066,7 +47316,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
47066
47316
|
}
|
|
47067
47317
|
return { ok: true, detail: `installed ${notice.latest} from ${notice.source}` };
|
|
47068
47318
|
} finally {
|
|
47069
|
-
|
|
47319
|
+
rmSync13(releaseDir, { recursive: true, force: true });
|
|
47070
47320
|
}
|
|
47071
47321
|
}
|
|
47072
47322
|
async function cmdExplain(runtime, options) {
|
|
@@ -47104,6 +47354,10 @@ async function cmdSshInfo(runtime, host) {
|
|
|
47104
47354
|
}
|
|
47105
47355
|
async function cmdSshRun(runtime, host, commandParts, options) {
|
|
47106
47356
|
const command = commandParts.join(" ");
|
|
47357
|
+
if (runtime.dryRun) {
|
|
47358
|
+
printPlan(runtime, { command: "ssh run", host, remoteCommand: command, sudo: options.sudo === true }, [{ action: "ssh", target: `${host}: ${options.sudo ? "sudo " : ""}${command}` }], `run a command on ${host}`);
|
|
47359
|
+
return 0;
|
|
47360
|
+
}
|
|
47107
47361
|
const receipt = executeSshRun({
|
|
47108
47362
|
action: "command",
|
|
47109
47363
|
host,
|
|
@@ -47122,6 +47376,10 @@ async function cmdSshRun(runtime, host, commandParts, options) {
|
|
|
47122
47376
|
}
|
|
47123
47377
|
async function cmdSshFile(runtime, host, filePath, options) {
|
|
47124
47378
|
const op = options.read ? "read" : options.append ? "append" : "write";
|
|
47379
|
+
if (runtime.dryRun) {
|
|
47380
|
+
printPlan(runtime, { command: "ssh file", host, path: filePath, op, sudo: options.sudo === true }, [{ action: "ssh", target: `${host}: ${op} ${filePath}` }], `${op} ${filePath} on ${host}`);
|
|
47381
|
+
return 0;
|
|
47382
|
+
}
|
|
47125
47383
|
const receipt = executeSshRun({
|
|
47126
47384
|
action: "file",
|
|
47127
47385
|
host,
|
|
@@ -47145,6 +47403,10 @@ async function cmdSshFile(runtime, host, filePath, options) {
|
|
|
47145
47403
|
return receipt.ok ? 0 : receipt.exitCode || 1;
|
|
47146
47404
|
}
|
|
47147
47405
|
async function cmdSshClose(runtime, host) {
|
|
47406
|
+
if (runtime.dryRun) {
|
|
47407
|
+
printPlan(runtime, { command: "ssh close", host }, [{ action: "ssh", target: `${host}: close the ControlMaster socket` }], `close the ControlMaster socket for ${host}`);
|
|
47408
|
+
return 0;
|
|
47409
|
+
}
|
|
47148
47410
|
const closed = closeControlSocket(host);
|
|
47149
47411
|
if (runtime.json) {
|
|
47150
47412
|
print(runtime.io, runtime.json, { ok: true, closed, command: "ssh close", host }, "");
|
|
@@ -47314,13 +47576,13 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
47314
47576
|
return 2;
|
|
47315
47577
|
}
|
|
47316
47578
|
const selectedFile = explicitFile || configuredFile;
|
|
47317
|
-
const file = selectedFile ?
|
|
47318
|
-
if (file && !
|
|
47579
|
+
const file = selectedFile ? resolve30(runtime.cwd, selectedFile) : void 0;
|
|
47580
|
+
if (file && !existsSync39(file)) {
|
|
47319
47581
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
47320
47582
|
return 1;
|
|
47321
47583
|
}
|
|
47322
47584
|
try {
|
|
47323
|
-
const raw = file ?
|
|
47585
|
+
const raw = file ? readFileSync37(file, "utf8") : inline;
|
|
47324
47586
|
const warnings = [];
|
|
47325
47587
|
const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
|
|
47326
47588
|
const secretEnvs = definitions.map((definition) => ({
|
|
@@ -47345,7 +47607,7 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
47345
47607
|
}
|
|
47346
47608
|
async function cmdArtifactsExist(runtime, pathFlag) {
|
|
47347
47609
|
const worker = gateOf(runtime, "artifacts-exist");
|
|
47348
|
-
const checked = verifyArtifactExists(runtime.dirs.assets,
|
|
47610
|
+
const checked = verifyArtifactExists(runtime.dirs.assets, resolve30(runtime.cwd, pathFlag));
|
|
47349
47611
|
if (!checked.ok) {
|
|
47350
47612
|
printWorker(
|
|
47351
47613
|
runtime,
|
|
@@ -47364,9 +47626,9 @@ async function cmdArtifactsExist(runtime, pathFlag) {
|
|
|
47364
47626
|
return 0;
|
|
47365
47627
|
}
|
|
47366
47628
|
async function cmdImprove(runtime, options = {}) {
|
|
47367
|
-
const file = options.file ?
|
|
47368
|
-
const routingRecords =
|
|
47369
|
-
const candidatesDir = options.outDir ?
|
|
47629
|
+
const file = options.file ? resolve30(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
|
|
47630
|
+
const routingRecords = existsSync39(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
|
|
47631
|
+
const candidatesDir = options.outDir ? resolve30(runtime.cwd, options.outDir) : join47(runtime.cwd, ".kxm", "candidates");
|
|
47370
47632
|
const report2 = buildImprovementReport(routingRecords, {
|
|
47371
47633
|
candidatesDir,
|
|
47372
47634
|
projectRoot: runtime.cwd,
|
|
@@ -47415,7 +47677,11 @@ async function cmdConfigSet(runtime, key, value, options) {
|
|
|
47415
47677
|
parsedVal = JSON.parse(value);
|
|
47416
47678
|
} catch {
|
|
47417
47679
|
}
|
|
47418
|
-
setKxmConfigValue(runtime.cwd, key, parsedVal, { scope });
|
|
47680
|
+
const { file } = setKxmConfigValue(runtime.cwd, key, parsedVal, { scope, dryRun: runtime.dryRun });
|
|
47681
|
+
if (runtime.dryRun) {
|
|
47682
|
+
printPlan(runtime, { command: "config set", key, value: parsedVal, scope }, [{ action: "write", target: file }], `set ${key} = ${value} in ${scope} config`);
|
|
47683
|
+
return 0;
|
|
47684
|
+
}
|
|
47419
47685
|
print(
|
|
47420
47686
|
runtime.io,
|
|
47421
47687
|
runtime.json,
|
|
@@ -47504,9 +47770,9 @@ async function maybeOfferCompletionInstall(runtime) {
|
|
|
47504
47770
|
if (!pathNeeded) {
|
|
47505
47771
|
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
47506
47772
|
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
47507
|
-
if (rcFile &&
|
|
47773
|
+
if (rcFile && existsSync39(rcFile)) {
|
|
47508
47774
|
try {
|
|
47509
|
-
if (
|
|
47775
|
+
if (readFileSync37(rcFile, "utf8").includes(scriptPath)) return;
|
|
47510
47776
|
} catch {
|
|
47511
47777
|
}
|
|
47512
47778
|
}
|
|
@@ -47638,7 +47904,7 @@ async function cmdRoutingReport(runtime, options) {
|
|
|
47638
47904
|
let catalog;
|
|
47639
47905
|
if (includeEquivalentListCost) {
|
|
47640
47906
|
try {
|
|
47641
|
-
const pricesPath = options.prices ?
|
|
47907
|
+
const pricesPath = options.prices ? resolve30(runtime.cwd, options.prices) : join47(runtime.dirs.workspace, "prices.yaml");
|
|
47642
47908
|
catalog = loadPriceCatalog(pricesPath);
|
|
47643
47909
|
} catch {
|
|
47644
47910
|
}
|
|
@@ -47735,6 +48001,129 @@ var USAGE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
47735
48001
|
"commander.invalidOptionArgument",
|
|
47736
48002
|
"commander.optionMissingArgument"
|
|
47737
48003
|
]);
|
|
48004
|
+
var DRY_RUN_COMMANDS = /* @__PURE__ */ new Set([
|
|
48005
|
+
"init",
|
|
48006
|
+
"backup",
|
|
48007
|
+
"restore",
|
|
48008
|
+
"run",
|
|
48009
|
+
"explain",
|
|
48010
|
+
"suggest",
|
|
48011
|
+
"update",
|
|
48012
|
+
"dash",
|
|
48013
|
+
"completion",
|
|
48014
|
+
"completion install",
|
|
48015
|
+
"runs status",
|
|
48016
|
+
"runs drive",
|
|
48017
|
+
"runs receipt",
|
|
48018
|
+
"runs cancel",
|
|
48019
|
+
"runs list",
|
|
48020
|
+
"tenant status",
|
|
48021
|
+
"models inventory-refresh",
|
|
48022
|
+
"harness list",
|
|
48023
|
+
"auth token",
|
|
48024
|
+
"routes list",
|
|
48025
|
+
"routes count",
|
|
48026
|
+
"routes admit",
|
|
48027
|
+
"routes disable",
|
|
48028
|
+
"runtime start",
|
|
48029
|
+
"runtime status",
|
|
48030
|
+
"runtime sync-retry",
|
|
48031
|
+
"runtime stop",
|
|
48032
|
+
"trust diff",
|
|
48033
|
+
"trust check",
|
|
48034
|
+
"agent worker",
|
|
48035
|
+
"session status",
|
|
48036
|
+
"session brief",
|
|
48037
|
+
"session token",
|
|
48038
|
+
"session start",
|
|
48039
|
+
"session stop",
|
|
48040
|
+
"peer list",
|
|
48041
|
+
"peer send",
|
|
48042
|
+
"peer get",
|
|
48043
|
+
"peer await",
|
|
48044
|
+
"peer cancel",
|
|
48045
|
+
"peer fanout",
|
|
48046
|
+
"peer inbox",
|
|
48047
|
+
"peer reply",
|
|
48048
|
+
"workflow list",
|
|
48049
|
+
"workflow get",
|
|
48050
|
+
"workflow checkpoint",
|
|
48051
|
+
"workflow record",
|
|
48052
|
+
"workflow wait",
|
|
48053
|
+
"workflow signal",
|
|
48054
|
+
"workflow start",
|
|
48055
|
+
"workflow export",
|
|
48056
|
+
"workflow definitions",
|
|
48057
|
+
"workflow add",
|
|
48058
|
+
"workflow remove",
|
|
48059
|
+
"workflow modify",
|
|
48060
|
+
"role list",
|
|
48061
|
+
"role get",
|
|
48062
|
+
"role add",
|
|
48063
|
+
"role remove",
|
|
48064
|
+
"role modify",
|
|
48065
|
+
"role hosts",
|
|
48066
|
+
"role set-host",
|
|
48067
|
+
"role resume",
|
|
48068
|
+
"gate validate",
|
|
48069
|
+
"gate artifacts-exist",
|
|
48070
|
+
"gate degrade",
|
|
48071
|
+
"gate signal",
|
|
48072
|
+
"gate github watch",
|
|
48073
|
+
"improve report",
|
|
48074
|
+
"context get",
|
|
48075
|
+
"context recall",
|
|
48076
|
+
"context state",
|
|
48077
|
+
"context episode",
|
|
48078
|
+
"context promote",
|
|
48079
|
+
"context explain",
|
|
48080
|
+
"context wiki-compile",
|
|
48081
|
+
"context wiki-lint",
|
|
48082
|
+
"skills create",
|
|
48083
|
+
"skills evaluate",
|
|
48084
|
+
"skills promote",
|
|
48085
|
+
"skills reject",
|
|
48086
|
+
"skills list",
|
|
48087
|
+
"skills verify",
|
|
48088
|
+
"memory brief",
|
|
48089
|
+
"memory note",
|
|
48090
|
+
"memory sync",
|
|
48091
|
+
"routing report",
|
|
48092
|
+
"routing benchmark",
|
|
48093
|
+
"ssh info",
|
|
48094
|
+
"ssh run",
|
|
48095
|
+
"ssh file",
|
|
48096
|
+
"ssh close",
|
|
48097
|
+
"hub view",
|
|
48098
|
+
"hub start",
|
|
48099
|
+
"hub stop",
|
|
48100
|
+
"hub bind",
|
|
48101
|
+
"hub unbind",
|
|
48102
|
+
"config get",
|
|
48103
|
+
"config set",
|
|
48104
|
+
"config list",
|
|
48105
|
+
"goal create",
|
|
48106
|
+
"goal list",
|
|
48107
|
+
"task create",
|
|
48108
|
+
"task list",
|
|
48109
|
+
"task get",
|
|
48110
|
+
"task run",
|
|
48111
|
+
"task sync",
|
|
48112
|
+
"studio layout",
|
|
48113
|
+
"studio serve"
|
|
48114
|
+
]);
|
|
48115
|
+
var DryRunRefused = class extends Error {
|
|
48116
|
+
code;
|
|
48117
|
+
constructor(code) {
|
|
48118
|
+
super("dry_run_unsupported");
|
|
48119
|
+
this.code = code;
|
|
48120
|
+
}
|
|
48121
|
+
};
|
|
48122
|
+
function commandPath(command) {
|
|
48123
|
+
const names3 = [];
|
|
48124
|
+
for (let current = command; current?.parent; current = current.parent) names3.unshift(current.name());
|
|
48125
|
+
return names3.join(" ");
|
|
48126
|
+
}
|
|
47738
48127
|
function addGlobalOptions(command) {
|
|
47739
48128
|
return command.option("--json", "Print machine-readable JSON").option("--dry-run", "Plan without making changes").option("--workspace <dir>", "Workspace directory");
|
|
47740
48129
|
}
|
|
@@ -47870,6 +48259,13 @@ function createProgram(ctx, result) {
|
|
|
47870
48259
|
writeErr: (text) => ctx.io.stderr(text)
|
|
47871
48260
|
}).helpCommand("help", "Show help");
|
|
47872
48261
|
addGlobalOptions(program2);
|
|
48262
|
+
program2.hook("preAction", (_program, actionCommand) => {
|
|
48263
|
+
const opts = actionCommand.optsWithGlobals();
|
|
48264
|
+
if (!opts.dryRun) return;
|
|
48265
|
+
const path4 = commandPath(actionCommand);
|
|
48266
|
+
if (DRY_RUN_COMMANDS.has(path4)) return;
|
|
48267
|
+
throw new DryRunRefused(refuseDryRun(ctx.io, Boolean(opts.json), path4, "this command cannot plan without making changes; rerun without --dry-run"));
|
|
48268
|
+
});
|
|
47873
48269
|
program2.command("init").description("Create, validate, repair, or join a KXM project").option("--json", "Print machine-readable JSON").option("--dry-run", "Plan without making changes").option("--name <name>", "Project display name for a new project").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--repository <id=absolute-path>", "Bind a member repository outside Git configuration", (value, previous) => [...previous, value], []).action(async function initAction(options) {
|
|
47874
48270
|
result.code = await cmdKxmInit(runtimeFrom(ctx, this), options, {
|
|
47875
48271
|
maybeOfferCompletionInstall,
|
|
@@ -48337,10 +48733,11 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
|
|
|
48337
48733
|
return result.code;
|
|
48338
48734
|
} catch (error) {
|
|
48339
48735
|
if (error instanceof CommanderError) return mapCommanderError(error);
|
|
48736
|
+
if (error instanceof DryRunRefused) return error.code;
|
|
48340
48737
|
throw error;
|
|
48341
48738
|
}
|
|
48342
48739
|
}
|
|
48343
|
-
if (process.argv[1] &&
|
|
48740
|
+
if (process.argv[1] && resolve31(process.argv[1]) === fileURLToPath4(import.meta.url)) {
|
|
48344
48741
|
const code = await runCli(process.argv.slice(2));
|
|
48345
48742
|
process.exitCode = code;
|
|
48346
48743
|
}
|