@kontextmind/kxm 0.7.104 → 0.7.105
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/CHANGELOG.md +12 -0
- package/docs/contributing/test-matrix.md +4 -1
- package/docs/reference/cli-reference.md +42 -35
- package/docs/reference/workflow-definitions.md +4 -4
- package/docs/start/first-workflow.md +6 -2
- package/docs/start/quickstart-claude-code.md +10 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +1361 -709
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +1 -1
- package/plugins/kxm/dist/runtime.js +3 -3
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/project.ts +66 -18
- package/plugins/kxm/src/cli/system.ts +19 -1
- package/plugins/kxm/src/cli/tasks.ts +55 -31
- package/plugins/kxm/src/cli/workflows.ts +52 -54
- package/plugins/kxm/src/cli.ts +1 -1
- package/plugins/kxm/src/engine.ts +79 -3
- package/plugins/kxm/src/harness.ts +6 -5
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/suggest.ts +132 -79
- package/plugins/kxm/src/workflow-manager.ts +42 -19
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 = resolve34.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 resolve34(root, ref) {
|
|
3018
3018
|
let sch;
|
|
3019
3019
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3020
3020
|
ref = sch;
|
|
@@ -3835,11 +3835,11 @@ var require_fast_uri = __commonJS({
|
|
|
3835
3835
|
normalizeString(uri, options);
|
|
3836
3836
|
} else if (typeof uri === "object") {
|
|
3837
3837
|
uri = /** @type {T} */
|
|
3838
|
-
|
|
3838
|
+
parse11(serialize(uri, options), options);
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
3842
|
-
function
|
|
3842
|
+
function resolve34(baseURI, relativeURI, options) {
|
|
3843
3843
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3844
3844
|
const {
|
|
3845
3845
|
parsed: baseParsed,
|
|
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
|
|
|
3875
3875
|
function resolveComponent(base, relative8, options, skipNormalization) {
|
|
3876
3876
|
const target = {};
|
|
3877
3877
|
if (!skipNormalization) {
|
|
3878
|
-
base =
|
|
3879
|
-
relative8 =
|
|
3878
|
+
base = parse11(serialize(base, options), options);
|
|
3879
|
+
relative8 = parse11(serialize(relative8, options), options);
|
|
3880
3880
|
}
|
|
3881
3881
|
options = options || {};
|
|
3882
3882
|
if (!options.tolerant && relative8.scheme) {
|
|
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
|
|
|
4168
4168
|
}
|
|
4169
4169
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
4170
4170
|
}
|
|
4171
|
-
function
|
|
4171
|
+
function parse11(uri, opts) {
|
|
4172
4172
|
return parseWithStatus(uri, opts).parsed;
|
|
4173
4173
|
}
|
|
4174
4174
|
function normalizeString(uri, opts) {
|
|
@@ -4201,11 +4201,11 @@ var require_fast_uri = __commonJS({
|
|
|
4201
4201
|
var fastUri = {
|
|
4202
4202
|
SCHEMES,
|
|
4203
4203
|
normalize,
|
|
4204
|
-
resolve:
|
|
4204
|
+
resolve: resolve34,
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
4208
|
-
parse:
|
|
4208
|
+
parse: parse11
|
|
4209
4209
|
};
|
|
4210
4210
|
module.exports = fastUri;
|
|
4211
4211
|
module.exports.default = fastUri;
|
|
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
|
|
|
14678
14678
|
}
|
|
14679
14679
|
return doc;
|
|
14680
14680
|
}
|
|
14681
|
-
function
|
|
14681
|
+
function parse11(src, reviver, options) {
|
|
14682
14682
|
let _reviver = void 0;
|
|
14683
14683
|
if (typeof reviver === "function") {
|
|
14684
14684
|
_reviver = reviver;
|
|
@@ -14719,7 +14719,7 @@ var require_public_api = __commonJS({
|
|
|
14719
14719
|
return value.toString(options);
|
|
14720
14720
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
14721
14721
|
}
|
|
14722
|
-
exports.parse =
|
|
14722
|
+
exports.parse = parse11;
|
|
14723
14723
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14724
14724
|
exports.parseDocument = parseDocument2;
|
|
14725
14725
|
exports.stringify = stringify12;
|
|
@@ -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 resolve33 } from "node:path";
|
|
14783
14783
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
14784
14784
|
|
|
14785
14785
|
// node_modules/commander/lib/error.js
|
|
@@ -19751,7 +19751,7 @@ var HubClient = class {
|
|
|
19751
19751
|
if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
|
|
19752
19752
|
const message = await this.getMessage(messageId);
|
|
19753
19753
|
if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
|
|
19754
|
-
await new Promise((
|
|
19754
|
+
await new Promise((resolve34, reject) => {
|
|
19755
19755
|
const onAbort = () => {
|
|
19756
19756
|
signal?.removeEventListener("abort", onAbort);
|
|
19757
19757
|
clearTimeout(timer);
|
|
@@ -19759,7 +19759,7 @@ var HubClient = class {
|
|
|
19759
19759
|
};
|
|
19760
19760
|
const timer = setTimeout(() => {
|
|
19761
19761
|
signal?.removeEventListener("abort", onAbort);
|
|
19762
|
-
|
|
19762
|
+
resolve34();
|
|
19763
19763
|
}, Math.min(500, Math.max(1, deadline - Date.now())));
|
|
19764
19764
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19765
19765
|
if (signal?.aborted) onAbort();
|
|
@@ -19814,7 +19814,7 @@ var HubClient = class {
|
|
|
19814
19814
|
} catch (error) {
|
|
19815
19815
|
if (this.stopped || error instanceof Error && error.name === "AbortError") return;
|
|
19816
19816
|
}
|
|
19817
|
-
if (!this.stopped) await new Promise((
|
|
19817
|
+
if (!this.stopped) await new Promise((resolve34) => setTimeout(resolve34, this.options.reconnectMs));
|
|
19818
19818
|
}
|
|
19819
19819
|
}
|
|
19820
19820
|
headers(includeIdentity = true) {
|
|
@@ -21350,7 +21350,7 @@ function defaultSpawn(command, args, options) {
|
|
|
21350
21350
|
if (options.signal?.aborted) {
|
|
21351
21351
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
21352
21352
|
}
|
|
21353
|
-
return new Promise((
|
|
21353
|
+
return new Promise((resolve34) => {
|
|
21354
21354
|
const stdout = [];
|
|
21355
21355
|
const stderr = [];
|
|
21356
21356
|
let outputBytes = 0;
|
|
@@ -21381,7 +21381,7 @@ function defaultSpawn(command, args, options) {
|
|
|
21381
21381
|
const started = Boolean(child?.pid);
|
|
21382
21382
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
21383
21383
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
21384
|
-
|
|
21384
|
+
resolve34({
|
|
21385
21385
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
21386
21386
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
21387
21387
|
code,
|
|
@@ -21790,6 +21790,12 @@ var WRITER_ONESHOT_ARGS = Object.freeze({
|
|
|
21790
21790
|
pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
|
|
21791
21791
|
grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
|
|
21792
21792
|
});
|
|
21793
|
+
function oneShotWriterArgs(harness) {
|
|
21794
|
+
return Object.hasOwn(WRITER_ONESHOT_ARGS, harness) ? WRITER_ONESHOT_ARGS[harness] : void 0;
|
|
21795
|
+
}
|
|
21796
|
+
function oneShotPermissionArgs(harness, permission) {
|
|
21797
|
+
return permission === "edit" ? oneShotWriterArgs(harness) : oneShotReadOnlyArgs(harness);
|
|
21798
|
+
}
|
|
21793
21799
|
var BUILTIN_HARNESSES = Object.freeze([
|
|
21794
21800
|
{
|
|
21795
21801
|
id: "pi",
|
|
@@ -22167,7 +22173,7 @@ function probeEntry(entry, runCommand, timeoutMs, platform, probe = {}) {
|
|
|
22167
22173
|
return {
|
|
22168
22174
|
id: entry.id,
|
|
22169
22175
|
label: entry.label,
|
|
22170
|
-
default: entry.
|
|
22176
|
+
default: entry.id === (probe.defaultHarness ?? DEFAULT_HARNESS),
|
|
22171
22177
|
mode: entry.mode,
|
|
22172
22178
|
detected,
|
|
22173
22179
|
authenticated,
|
|
@@ -22188,7 +22194,7 @@ function probeHarnesses(options = {}) {
|
|
|
22188
22194
|
const runCommand = options.runCommand ?? defaultRunner(options.env ?? process.env);
|
|
22189
22195
|
const platform = options.platform ?? process.platform;
|
|
22190
22196
|
return {
|
|
22191
|
-
defaultHarness: DEFAULT_HARNESS,
|
|
22197
|
+
defaultHarness: options.defaultHarness ?? DEFAULT_HARNESS,
|
|
22192
22198
|
harnesses: BUILTIN_HARNESSES.map((entry) => probeEntry(entry, runCommand, timeoutMs, platform, options))
|
|
22193
22199
|
};
|
|
22194
22200
|
}
|
|
@@ -22432,7 +22438,7 @@ function formatHarnessInventory(inventory) {
|
|
|
22432
22438
|
].join(" ");
|
|
22433
22439
|
});
|
|
22434
22440
|
return [
|
|
22435
|
-
`default harness: ${inventory.defaultHarness} (
|
|
22441
|
+
`default harness: ${inventory.defaultHarness} (used when an agent omits harness:)`,
|
|
22436
22442
|
"enable/disable = Git YAML (.kxm/agents, .kxm/models) or the harness's own plugin CLI",
|
|
22437
22443
|
"governed kxm skills are not auto-updated",
|
|
22438
22444
|
header,
|
|
@@ -23094,8 +23100,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
23094
23100
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
23095
23101
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
23096
23102
|
}
|
|
23097
|
-
const
|
|
23098
|
-
const minimumPassed =
|
|
23103
|
+
const join52 = objectValue(step.join);
|
|
23104
|
+
const minimumPassed = join52 && typeof join52.minimumPassed === "number" ? join52.minimumPassed : void 0;
|
|
23099
23105
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
23100
23106
|
const distinctBy = names(assignment?.distinctBy);
|
|
23101
23107
|
if (distinctBy.length > 0) {
|
|
@@ -23659,9 +23665,9 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
|
|
|
23659
23665
|
|
|
23660
23666
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
23661
23667
|
import { spawn as spawn2 } from "node:child_process";
|
|
23662
|
-
import { createHash as
|
|
23663
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
23664
|
-
import { dirname as dirname11, isAbsolute as isAbsolute7, join as
|
|
23668
|
+
import { createHash as createHash14, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
23669
|
+
import { chmodSync as chmodSync4, existsSync as existsSync17, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync15, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
23670
|
+
import { dirname as dirname11, isAbsolute as isAbsolute7, join as join17 } from "node:path";
|
|
23665
23671
|
|
|
23666
23672
|
// plugins/kxm/src/runtime-store.ts
|
|
23667
23673
|
import { existsSync as existsSync10, lstatSync as lstatSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
@@ -25062,6 +25068,9 @@ var EVENT_STORE_TABLES = {
|
|
|
25062
25068
|
};
|
|
25063
25069
|
var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
|
|
25064
25070
|
|
|
25071
|
+
// plugins/kxm/src/engine-plan.ts
|
|
25072
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
25073
|
+
|
|
25065
25074
|
// plugins/kxm/src/engine-compile.ts
|
|
25066
25075
|
var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
|
|
25067
25076
|
var WORKFLOW_SCHEMA = "kxm.workflow.v1";
|
|
@@ -25210,7 +25219,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
25210
25219
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
25211
25220
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
25212
25221
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
25213
|
-
const
|
|
25222
|
+
const join52 = compileJoin(step, id, sink);
|
|
25214
25223
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
25215
25224
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
25216
25225
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
|
|
@@ -25238,7 +25247,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
25238
25247
|
transitions: orderedTransitions,
|
|
25239
25248
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
25240
25249
|
assignments,
|
|
25241
|
-
join:
|
|
25250
|
+
join: join52
|
|
25242
25251
|
};
|
|
25243
25252
|
if (kind === "agent" || kind === "moa") {
|
|
25244
25253
|
if (!agent) return void 0;
|
|
@@ -25288,15 +25297,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
25288
25297
|
};
|
|
25289
25298
|
}
|
|
25290
25299
|
function compileJoin(step, stepId, sink) {
|
|
25291
|
-
const
|
|
25292
|
-
if (!
|
|
25293
|
-
const declared = stringValue2(
|
|
25300
|
+
const join52 = objectValue2(step.join);
|
|
25301
|
+
if (!join52) return { strategy: "all" };
|
|
25302
|
+
const declared = stringValue2(join52.strategy);
|
|
25294
25303
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
25295
|
-
const minimumPassed = compileOptionalCount(
|
|
25304
|
+
const minimumPassed = compileOptionalCount(join52.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
25296
25305
|
const compiled = {
|
|
25297
25306
|
strategy,
|
|
25298
25307
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
25299
|
-
...typeof
|
|
25308
|
+
...typeof join52.cancelRemaining === "boolean" ? { cancelRemaining: join52.cancelRemaining } : {}
|
|
25300
25309
|
};
|
|
25301
25310
|
return compiled;
|
|
25302
25311
|
}
|
|
@@ -25565,7 +25574,110 @@ function pushIssue(sink, issue3) {
|
|
|
25565
25574
|
sink.issues.push({ workflowId: sink.workflowId, ...issue3 });
|
|
25566
25575
|
}
|
|
25567
25576
|
|
|
25577
|
+
// plugins/kxm/src/engine-plan.ts
|
|
25578
|
+
function kxmSha256(input) {
|
|
25579
|
+
return `sha256:${createHash11("sha256").update(input, "utf8").digest("hex")}`;
|
|
25580
|
+
}
|
|
25581
|
+
function parseGateDefinition(raw, gateId, runId) {
|
|
25582
|
+
const value = asObject(raw, runId, `gates.definitions.${gateId}`);
|
|
25583
|
+
const kind = asString(value.kind, runId, `gates.definitions.${gateId}.kind`);
|
|
25584
|
+
if (kind === "command") {
|
|
25585
|
+
assertExactKeys(value, ["kind", "argv", "timeoutMs"], ["cwd"], runId, `gates.definitions.${gateId}`);
|
|
25586
|
+
const argv = asStringArray(value.argv, runId, `gates.definitions.${gateId}.argv`);
|
|
25587
|
+
if (argv.length < 1 || argv.length > 64) {
|
|
25588
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv length is invalid`);
|
|
25589
|
+
}
|
|
25590
|
+
for (const [index, entry] of argv.entries()) {
|
|
25591
|
+
if (entry.length === 0 || entry.length > 4096 || entry.includes("\0")) {
|
|
25592
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv[${index}] is invalid`);
|
|
25593
|
+
}
|
|
25594
|
+
}
|
|
25595
|
+
const executable = argv[0];
|
|
25596
|
+
if (executable === "." || executable === ".." || executable.includes("\\") || !executable.startsWith("/") && executable.includes("/")) {
|
|
25597
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} argv[0] must be a bare executable or absolute POSIX path`);
|
|
25598
|
+
}
|
|
25599
|
+
const timeoutMs = value.timeoutMs;
|
|
25600
|
+
if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) {
|
|
25601
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.timeoutMs is invalid`);
|
|
25602
|
+
}
|
|
25603
|
+
if (value.cwd !== void 0 && value.cwd !== "control") {
|
|
25604
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.cwd is invalid`);
|
|
25605
|
+
}
|
|
25606
|
+
return {
|
|
25607
|
+
kind: "command",
|
|
25608
|
+
argv,
|
|
25609
|
+
timeoutMs,
|
|
25610
|
+
...value.cwd === "control" ? { cwd: "control" } : {}
|
|
25611
|
+
};
|
|
25612
|
+
}
|
|
25613
|
+
if (kind === "artifacts-exist") {
|
|
25614
|
+
assertExactKeys(value, ["kind", "paths"], [], runId, `gates.definitions.${gateId}`);
|
|
25615
|
+
const paths = asStringArray(value.paths, runId, `gates.definitions.${gateId}.paths`);
|
|
25616
|
+
if (paths.length < 1 || paths.length > 64) {
|
|
25617
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths length is invalid`);
|
|
25618
|
+
}
|
|
25619
|
+
if (new Set(paths).size !== paths.length) {
|
|
25620
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths are not unique`);
|
|
25621
|
+
}
|
|
25622
|
+
for (const path4 of paths) {
|
|
25623
|
+
if (path4 === "." || !kxmPortablePath(path4) || path4.startsWith("/") || path4.includes("\\")) {
|
|
25624
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} path is not a portable relative path`);
|
|
25625
|
+
}
|
|
25626
|
+
}
|
|
25627
|
+
return { kind: "artifacts-exist", paths };
|
|
25628
|
+
}
|
|
25629
|
+
if (kind === "reserved") {
|
|
25630
|
+
assertExactKeys(value, ["kind"], [], runId, `gates.definitions.${gateId}`);
|
|
25631
|
+
return { kind: "reserved" };
|
|
25632
|
+
}
|
|
25633
|
+
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} kind is invalid`);
|
|
25634
|
+
}
|
|
25635
|
+
function asObject(value, runId, label) {
|
|
25636
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
25637
|
+
throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
|
|
25638
|
+
}
|
|
25639
|
+
return value;
|
|
25640
|
+
}
|
|
25641
|
+
function assertExactKeys(value, required, optional, runId, label) {
|
|
25642
|
+
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
25643
|
+
for (const key of required) {
|
|
25644
|
+
if (!(key in value)) throw runtimeError("run_plan_corrupt", runId, `${label} is missing ${key}`);
|
|
25645
|
+
}
|
|
25646
|
+
for (const key of Object.keys(value)) {
|
|
25647
|
+
if (!allowed.has(key)) throw runtimeError("run_plan_corrupt", runId, `${label} has unexpected key ${key}`);
|
|
25648
|
+
}
|
|
25649
|
+
}
|
|
25650
|
+
function asString(value, runId, label) {
|
|
25651
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
25652
|
+
throw runtimeError("run_plan_corrupt", runId, `${label} is not a non-empty string`);
|
|
25653
|
+
}
|
|
25654
|
+
return value;
|
|
25655
|
+
}
|
|
25656
|
+
function asStringArray(value, runId, label) {
|
|
25657
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
25658
|
+
throw runtimeError("run_plan_corrupt", runId, `${label} is not a string array`);
|
|
25659
|
+
}
|
|
25660
|
+
return [...value];
|
|
25661
|
+
}
|
|
25662
|
+
|
|
25663
|
+
// plugins/kxm/src/gate-hash.ts
|
|
25664
|
+
function gateRegistryHash(registryValue) {
|
|
25665
|
+
return kxmSha256(kxmCanonicalJson(registryValue));
|
|
25666
|
+
}
|
|
25667
|
+
|
|
25568
25668
|
// plugins/kxm/src/runtime-service.ts
|
|
25669
|
+
function objectValue3(value) {
|
|
25670
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
25671
|
+
}
|
|
25672
|
+
function kxmProjectAdmissionLimits(bundle) {
|
|
25673
|
+
const limits = objectValue3(bundle.project.value.limits);
|
|
25674
|
+
const maxConcurrentRuns = typeof limits?.maxConcurrentRuns === "number" && Number.isInteger(limits.maxConcurrentRuns) && limits.maxConcurrentRuns >= 1 ? limits.maxConcurrentRuns : 1;
|
|
25675
|
+
return {
|
|
25676
|
+
maxConcurrentRuns,
|
|
25677
|
+
...typeof limits?.maxRunDurationMs === "number" ? { maxRunDurationMs: limits.maxRunDurationMs } : {},
|
|
25678
|
+
...typeof limits?.maxAgentTimeMs === "number" ? { maxAgentTimeMs: limits.maxAgentTimeMs } : {}
|
|
25679
|
+
};
|
|
25680
|
+
}
|
|
25569
25681
|
var RUNTIME_EPOCH_NS = process.hrtime.bigint();
|
|
25570
25682
|
|
|
25571
25683
|
// plugins/kxm/src/oneshot-evidence.ts
|
|
@@ -25575,7 +25687,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
|
25575
25687
|
|
|
25576
25688
|
// plugins/kxm/src/prices.ts
|
|
25577
25689
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
25578
|
-
import { createHash as
|
|
25690
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
25579
25691
|
import { existsSync as existsSync11, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
25580
25692
|
import { join as join11 } from "node:path";
|
|
25581
25693
|
|
|
@@ -25638,7 +25750,7 @@ function hashPriceCatalog(catalog) {
|
|
|
25638
25750
|
}))
|
|
25639
25751
|
})).sort((a, b2) => a.id.localeCompare(b2.id))
|
|
25640
25752
|
};
|
|
25641
|
-
return
|
|
25753
|
+
return createHash12("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
|
|
25642
25754
|
}
|
|
25643
25755
|
function parsePriceCatalog(text) {
|
|
25644
25756
|
const parsed = (0, import_yaml3.parse)(text);
|
|
@@ -25776,6 +25888,8 @@ function acknowledgePriceCatalog(projectRoot, now = /* @__PURE__ */ new Date())
|
|
|
25776
25888
|
|
|
25777
25889
|
// plugins/kxm/src/engine.ts
|
|
25778
25890
|
var import_yaml7 = __toESM(require_dist(), 1);
|
|
25891
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
25892
|
+
import { join as join15 } from "node:path";
|
|
25779
25893
|
|
|
25780
25894
|
// plugins/kxm/src/routes.ts
|
|
25781
25895
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
@@ -25835,6 +25949,10 @@ function listRoleBindings(root) {
|
|
|
25835
25949
|
}
|
|
25836
25950
|
return result;
|
|
25837
25951
|
}
|
|
25952
|
+
function isRouteAdmitted(root, modelId2) {
|
|
25953
|
+
const policy = loadRoutePolicy(root);
|
|
25954
|
+
return policy.admitted.includes(modelId2) && !policy.disabled.includes(modelId2);
|
|
25955
|
+
}
|
|
25838
25956
|
function listInventoryModels(root) {
|
|
25839
25957
|
const path4 = join12(root, ".kxm", "models", "inventory.yaml");
|
|
25840
25958
|
if (!existsSync12(path4)) return [];
|
|
@@ -26120,7 +26238,7 @@ function syncHarnessMemory(repoRoot, options = {}) {
|
|
|
26120
26238
|
|
|
26121
26239
|
// plugins/kxm/src/skills.ts
|
|
26122
26240
|
var import_yaml6 = __toESM(require_dist(), 1);
|
|
26123
|
-
import { createHash as
|
|
26241
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
26124
26242
|
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync10 } from "node:fs";
|
|
26125
26243
|
import { dirname as dirname9, join as join14 } from "node:path";
|
|
26126
26244
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
@@ -26145,7 +26263,7 @@ var SkillLifecycleError = class extends Error {
|
|
|
26145
26263
|
}
|
|
26146
26264
|
};
|
|
26147
26265
|
function skillContentSha256(content) {
|
|
26148
|
-
return
|
|
26266
|
+
return createHash13("sha256").update(content, "utf8").digest("hex");
|
|
26149
26267
|
}
|
|
26150
26268
|
function skillIdFor(name, contentSha256) {
|
|
26151
26269
|
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -27159,13 +27277,418 @@ function formatRoutingReport(report2, options = {}) {
|
|
|
27159
27277
|
}
|
|
27160
27278
|
|
|
27161
27279
|
// plugins/kxm/src/engine.ts
|
|
27280
|
+
function resolveProducerRoute(projectRoot, step, agentId) {
|
|
27281
|
+
let agentModel;
|
|
27282
|
+
const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
27283
|
+
if (existsSync15(agentFile)) {
|
|
27284
|
+
try {
|
|
27285
|
+
const parsed = (0, import_yaml7.parse)(readFileSync13(agentFile, "utf8"));
|
|
27286
|
+
if (typeof parsed?.model === "string") {
|
|
27287
|
+
agentModel = parsed.model;
|
|
27288
|
+
} else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
|
|
27289
|
+
const declared = parsed.model;
|
|
27290
|
+
const providerOk = typeof declared.provider === "string" && declared.provider.length > 0 && !declared.provider.includes("/");
|
|
27291
|
+
const modelOk = typeof declared.model === "string" && declared.model.length > 0 && !declared.model.startsWith("/") && !declared.model.endsWith("/");
|
|
27292
|
+
if (providerOk && modelOk) {
|
|
27293
|
+
agentModel = `${declared.provider}/${declared.model}`;
|
|
27294
|
+
} else {
|
|
27295
|
+
return {
|
|
27296
|
+
error: {
|
|
27297
|
+
reason: "step_unsupported",
|
|
27298
|
+
field: "model",
|
|
27299
|
+
detail: "producer_route_unsupported: invalid model declaration"
|
|
27300
|
+
}
|
|
27301
|
+
};
|
|
27302
|
+
}
|
|
27303
|
+
}
|
|
27304
|
+
} catch {
|
|
27305
|
+
}
|
|
27306
|
+
}
|
|
27307
|
+
let selector = typeof step.model === "string" ? step.model : agentModel;
|
|
27308
|
+
if (!selector) {
|
|
27309
|
+
if (agentId === "implementer") {
|
|
27310
|
+
selector = "xai/grok-4.6";
|
|
27311
|
+
}
|
|
27312
|
+
}
|
|
27313
|
+
if (!selector) {
|
|
27314
|
+
return {
|
|
27315
|
+
error: {
|
|
27316
|
+
reason: "step_unsupported",
|
|
27317
|
+
field: "model",
|
|
27318
|
+
detail: "producer_route_unsupported: agent or step has no model declared"
|
|
27319
|
+
}
|
|
27320
|
+
};
|
|
27321
|
+
}
|
|
27322
|
+
const slash = selector.indexOf("/");
|
|
27323
|
+
if (slash <= 0 || slash === selector.length - 1) {
|
|
27324
|
+
return {
|
|
27325
|
+
error: {
|
|
27326
|
+
reason: "step_unsupported",
|
|
27327
|
+
field: "model",
|
|
27328
|
+
detail: `producer_route_unsupported: invalid selector '${selector}'`
|
|
27329
|
+
}
|
|
27330
|
+
};
|
|
27331
|
+
}
|
|
27332
|
+
if (!isRouteAdmitted(projectRoot, selector)) {
|
|
27333
|
+
return {
|
|
27334
|
+
error: {
|
|
27335
|
+
reason: "step_unsupported",
|
|
27336
|
+
field: "model",
|
|
27337
|
+
detail: `producer_route_unsupported: model '${selector}' is not admitted`
|
|
27338
|
+
}
|
|
27339
|
+
};
|
|
27340
|
+
}
|
|
27341
|
+
const role = agentId === "implementer" ? "writer" : agentId;
|
|
27342
|
+
const roleBindings = listRoleBindings(projectRoot);
|
|
27343
|
+
const roster = roleBindings[role];
|
|
27344
|
+
if (roster && !roster.includes(selector)) {
|
|
27345
|
+
return {
|
|
27346
|
+
error: {
|
|
27347
|
+
reason: "step_unsupported",
|
|
27348
|
+
field: "model",
|
|
27349
|
+
detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`
|
|
27350
|
+
}
|
|
27351
|
+
};
|
|
27352
|
+
}
|
|
27353
|
+
const provider = selector.slice(0, slash);
|
|
27354
|
+
const model = selector.slice(slash + 1);
|
|
27355
|
+
return { provider, model, selector };
|
|
27356
|
+
}
|
|
27162
27357
|
var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSha256", "objectiveSha256", "stepWrites"]);
|
|
27163
27358
|
var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
|
|
27359
|
+
function kxmLiveRunPrerequisites(bundle, workflowId, projectRoot) {
|
|
27360
|
+
const workflow = bundle.workflows.get(workflowId);
|
|
27361
|
+
if (!workflow) throw runtimeError("run_workflow_unknown", workflowId, `workflow ${workflowId} does not exist in this project`);
|
|
27362
|
+
const plan = compileKxmWorkflow({ id: workflowId, value: workflow.value, logicalPath: workflow.logicalPath });
|
|
27363
|
+
const envelope = { plan, projectLimits: kxmProjectAdmissionLimits(bundle), gates: pinnedGatesForCompiledPlan(plan, bundle, projectRoot) };
|
|
27364
|
+
const prerequisites = [];
|
|
27365
|
+
const limit = unsupportedLimit(envelope);
|
|
27366
|
+
if (limit) prerequisites.push(limit);
|
|
27367
|
+
const visited = /* @__PURE__ */ new Set();
|
|
27368
|
+
const pending = [plan.entryStepId];
|
|
27369
|
+
while (pending.length > 0) {
|
|
27370
|
+
const stepId = pending.pop();
|
|
27371
|
+
if (visited.has(stepId)) continue;
|
|
27372
|
+
visited.add(stepId);
|
|
27373
|
+
const step = plan.steps[stepId];
|
|
27374
|
+
for (const transition2 of Object.values(step.transitions)) {
|
|
27375
|
+
if (transition2.to === "step") pending.push(transition2.target);
|
|
27376
|
+
}
|
|
27377
|
+
const unsupported = step.kind === "gate" ? unsupportedGateStep(plan, step, envelope, { projectRoot }) ?? starterGatePrerequisite(step, envelope.gates, projectRoot) : unsupportedStep(plan, step, "oneshot");
|
|
27378
|
+
if (unsupported) {
|
|
27379
|
+
prerequisites.push({ ...unsupported, stepId });
|
|
27380
|
+
continue;
|
|
27381
|
+
}
|
|
27382
|
+
if (step.kind !== "agent" && step.kind !== "moa") continue;
|
|
27383
|
+
const agentIds = step.assignments.allowedAgents.length > 0 ? step.assignments.allowedAgents : [step.agent];
|
|
27384
|
+
for (const agentId of agentIds) {
|
|
27385
|
+
const agent = bundle.agents.get(agentId);
|
|
27386
|
+
const harness = String(agent?.value.harness ?? bundle.project.value.defaultHarness ?? "pi");
|
|
27387
|
+
const permission = Object.values(step.repositories).includes("write") ? "edit" : "read-only";
|
|
27388
|
+
if (!BUILTIN_HARNESSES.some((entry) => entry.id === harness && entry.oneShot) || !oneShotPermissionArgs(harness, permission)) {
|
|
27389
|
+
prerequisites.push({ reason: "step_unsupported", stepId, field: "harness", detail: `${agentId}: ${harness} has no audited ${permission} one-shot profile; use a supported workflow or execute this work directly in ${harness}, without substituting another harness` });
|
|
27390
|
+
continue;
|
|
27391
|
+
}
|
|
27392
|
+
const route = resolveProducerRoute(projectRoot, step, agentId);
|
|
27393
|
+
if ("error" in route) {
|
|
27394
|
+
prerequisites.push({ ...route.error, stepId, detail: `${agentId}: ${route.error.detail}; configure its model in .kxm/agents/${agentId}.yaml and admit the installed model with kxm routes admit --model <provider/model>` });
|
|
27395
|
+
continue;
|
|
27396
|
+
}
|
|
27397
|
+
const writeRefusal = unsupportedLiveWrite(projectRoot, step, agentId, route.selector, envelope.projectLimits.maxConcurrentRuns);
|
|
27398
|
+
if (writeRefusal) {
|
|
27399
|
+
prerequisites.push({ ...writeRefusal, stepId });
|
|
27400
|
+
continue;
|
|
27401
|
+
}
|
|
27402
|
+
const compatible = validateHarnessModelPair(harness, route);
|
|
27403
|
+
if (!compatible.valid) {
|
|
27404
|
+
prerequisites.push({ reason: "step_unsupported", stepId, field: "model", detail: `${agentId}: ${compatible.message ?? `${harness} cannot run ${route.selector}`}; configure a model supported by ${harness} in .kxm/agents/${agentId}.yaml` });
|
|
27405
|
+
}
|
|
27406
|
+
}
|
|
27407
|
+
}
|
|
27408
|
+
return prerequisites;
|
|
27409
|
+
}
|
|
27410
|
+
function starterGatePrerequisite(step, gates, projectRoot) {
|
|
27411
|
+
const definition = gates.definitions[step.gate];
|
|
27412
|
+
if (!definition || definition.kind !== "command" || definition.argv.length !== 2 || definition.argv[0] !== "npm" || definition.argv[1] !== "test") return void 0;
|
|
27413
|
+
let testScript;
|
|
27414
|
+
try {
|
|
27415
|
+
const manifest = JSON.parse(readFileSync13(join15(projectRoot, "package.json"), "utf8"));
|
|
27416
|
+
if (manifest && typeof manifest === "object" && "scripts" in manifest) {
|
|
27417
|
+
const scripts = manifest.scripts;
|
|
27418
|
+
if (scripts && typeof scripts === "object" && "test" in scripts) testScript = scripts.test;
|
|
27419
|
+
}
|
|
27420
|
+
} catch {
|
|
27421
|
+
}
|
|
27422
|
+
if (typeof testScript !== "string" || testScript.trim().length === 0) {
|
|
27423
|
+
return { reason: "gate_unsupported", field: `gates.${step.gate}.argv`, detail: `gate ${step.gate} runs npm test but this repository has no readable package.json with scripts.test; configure .kxm/gates.yaml gates.${step.gate}.argv for the repository's actual test runner before driving this workflow` };
|
|
27424
|
+
}
|
|
27425
|
+
return void 0;
|
|
27426
|
+
}
|
|
27427
|
+
function unsupportedLimit(envelope) {
|
|
27428
|
+
if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
|
|
27429
|
+
return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
27430
|
+
}
|
|
27431
|
+
if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
|
|
27432
|
+
return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
|
|
27433
|
+
}
|
|
27434
|
+
return void 0;
|
|
27435
|
+
}
|
|
27436
|
+
function unsupportedStep(plan, step, producerId) {
|
|
27437
|
+
if (step.kind === "gate") throw runtimeError("run_plan_corrupt", plan.workflowId, `unsupportedStep called on gate without envelope; use unsupportedGateStep instead`);
|
|
27438
|
+
if (step.kind !== "agent" && step.kind !== "moa" && step.kind !== "approval" && step.kind !== "wait") {
|
|
27439
|
+
return { reason: "step_unsupported", field: "kind", detail: `step kind ${step.kind} is not executed in this slice` };
|
|
27440
|
+
}
|
|
27441
|
+
if (step.assignments.maxAttemptsPerAssignment > 2) {
|
|
27442
|
+
return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "only up to two physical attempts are supported in this slice" };
|
|
27443
|
+
}
|
|
27444
|
+
if (step.join.strategy !== "all" && step.join.strategy !== "all-settled") {
|
|
27445
|
+
return { reason: "step_unsupported", field: "join.strategy", detail: "only join all or all-settled is supported" };
|
|
27446
|
+
}
|
|
27447
|
+
if (step.join.strategy === "all" && step.join.minimumPassed !== void 0) {
|
|
27448
|
+
return { reason: "step_unsupported", field: "join.minimumPassed", detail: "minimumPassed requires join strategy all-settled" };
|
|
27449
|
+
}
|
|
27450
|
+
if (step.join.cancelRemaining) {
|
|
27451
|
+
return { reason: "step_unsupported", field: "join.cancelRemaining", detail: "cancelRemaining is not executed in this slice" };
|
|
27452
|
+
}
|
|
27453
|
+
if (step.assignments.distinctBy.some((d2) => d2 !== "provider")) {
|
|
27454
|
+
return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "only distinctBy provider is supported in this slice" };
|
|
27455
|
+
}
|
|
27456
|
+
if (step.assignments.maxWriteRepositories !== void 0 && step.assignments.maxWriteRepositories > 1) {
|
|
27457
|
+
return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "at most one write repository is supported in this slice" };
|
|
27458
|
+
}
|
|
27459
|
+
for (const evidence of step.requiredEvidence) {
|
|
27460
|
+
if (!evidence || !["artifact", "assignment-result", "gate", "approval", "receipt"].includes(evidence.kind)) {
|
|
27461
|
+
return { reason: "step_unsupported", field: "requiredEvidence", detail: `evidence kind '${evidence?.kind}' is not supported in this slice` };
|
|
27462
|
+
}
|
|
27463
|
+
}
|
|
27464
|
+
if (step.timeoutMs !== void 0 && step.timeoutMs <= 0) {
|
|
27465
|
+
return { reason: "step_unsupported", field: "timeoutMs", detail: "timeoutMs must be positive" };
|
|
27466
|
+
}
|
|
27467
|
+
if (step.safeSpeculation) return { reason: "step_unsupported", field: "safeSpeculation", detail: "speculation is not executed in this slice" };
|
|
27468
|
+
if (step.tools) return { reason: "step_unsupported", field: "tools", detail: "tools are not executed in this slice" };
|
|
27469
|
+
if (step.secrets.length > 0) return { reason: "step_unsupported", field: "secrets", detail: "secrets are not executed in this slice" };
|
|
27470
|
+
for (const [repoId, access] of Object.entries(step.repositories)) {
|
|
27471
|
+
if (access !== "read" && access !== "write" && access !== "none") {
|
|
27472
|
+
return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
|
|
27473
|
+
}
|
|
27474
|
+
}
|
|
27475
|
+
return void 0;
|
|
27476
|
+
}
|
|
27477
|
+
function readYamlRecord(path4) {
|
|
27478
|
+
if (!existsSync15(path4)) return void 0;
|
|
27479
|
+
try {
|
|
27480
|
+
const parsed = (0, import_yaml7.parse)(readFileSync13(path4, "utf8"));
|
|
27481
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
27482
|
+
} catch {
|
|
27483
|
+
return void 0;
|
|
27484
|
+
}
|
|
27485
|
+
return void 0;
|
|
27486
|
+
}
|
|
27487
|
+
function agentHarness(projectRoot, agentId) {
|
|
27488
|
+
const harness = readYamlRecord(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
|
|
27489
|
+
return typeof harness === "string" && harness.length > 0 ? harness : void 0;
|
|
27490
|
+
}
|
|
27491
|
+
function projectDefaultHarness(projectRoot) {
|
|
27492
|
+
const harness = readYamlRecord(join15(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
|
|
27493
|
+
return typeof harness === "string" && harness.length > 0 ? harness : "pi";
|
|
27494
|
+
}
|
|
27495
|
+
function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
|
|
27496
|
+
if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
|
|
27497
|
+
if (step.assignments.maximum !== 1) {
|
|
27498
|
+
return {
|
|
27499
|
+
reason: "step_unsupported",
|
|
27500
|
+
field: "assignments.maximum",
|
|
27501
|
+
detail: "live write steps run a single assignment; the checkout witness cannot attribute edits between writers"
|
|
27502
|
+
};
|
|
27503
|
+
}
|
|
27504
|
+
if (maxConcurrentRuns !== 1) {
|
|
27505
|
+
return {
|
|
27506
|
+
reason: "step_unsupported",
|
|
27507
|
+
field: "limits.maxConcurrentRuns",
|
|
27508
|
+
detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
|
|
27509
|
+
};
|
|
27510
|
+
}
|
|
27511
|
+
const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
|
|
27512
|
+
if (!oneShotWriterArgs(harness)) {
|
|
27513
|
+
return {
|
|
27514
|
+
reason: "step_unsupported",
|
|
27515
|
+
field: "repositories",
|
|
27516
|
+
detail: `live write steps require an audited writer profile; ${harness} has none`
|
|
27517
|
+
};
|
|
27518
|
+
}
|
|
27519
|
+
const rosterPath = join15(projectRoot, ".kxm", "roster.yaml");
|
|
27520
|
+
if (!existsSync15(rosterPath)) return void 0;
|
|
27521
|
+
const roster = readYamlRecord(rosterPath);
|
|
27522
|
+
if (!roster || roster.schema !== "kxm.developer-roster.v1") {
|
|
27523
|
+
return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
|
|
27524
|
+
}
|
|
27525
|
+
const routes = roster.routes;
|
|
27526
|
+
const lineup = roster.lineup;
|
|
27527
|
+
const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
|
|
27528
|
+
if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
|
|
27529
|
+
return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
|
|
27530
|
+
}
|
|
27531
|
+
const allowed = writerIds.some((id) => {
|
|
27532
|
+
if (typeof id !== "string") return false;
|
|
27533
|
+
const route = routes[id];
|
|
27534
|
+
if (!route || typeof route !== "object" || Array.isArray(route)) return false;
|
|
27535
|
+
const record = route;
|
|
27536
|
+
if (record.harness !== harness || record.status !== "admitted") return false;
|
|
27537
|
+
if (!Array.isArray(record.permissions) || !record.permissions.includes("edit")) return false;
|
|
27538
|
+
const model = typeof record.model === "string" ? record.model : "";
|
|
27539
|
+
const vendor = typeof record.vendor === "string" ? record.vendor : "";
|
|
27540
|
+
return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
|
|
27541
|
+
});
|
|
27542
|
+
if (!allowed) {
|
|
27543
|
+
return {
|
|
27544
|
+
reason: "step_unsupported",
|
|
27545
|
+
field: "model",
|
|
27546
|
+
detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
|
|
27547
|
+
};
|
|
27548
|
+
}
|
|
27549
|
+
return void 0;
|
|
27550
|
+
}
|
|
27551
|
+
function unsupportedGateStep(plan, step, envelope, context) {
|
|
27552
|
+
if (!step.gate) return { reason: "step_unsupported", field: "gate", detail: "gate step is missing gate id" };
|
|
27553
|
+
if (envelope.gates.registry === null) {
|
|
27554
|
+
return { reason: "step_unsupported", field: "gate", detail: `step ${step.id} refers to a gate but registry is null` };
|
|
27555
|
+
}
|
|
27556
|
+
const definition = envelope.gates.definitions[step.gate];
|
|
27557
|
+
if (!definition) {
|
|
27558
|
+
return { reason: "step_unsupported", field: "gate", detail: `gate definition ${step.gate} not found in registry` };
|
|
27559
|
+
}
|
|
27560
|
+
if (definition.kind === "reserved") {
|
|
27561
|
+
return { reason: "gate_unsupported", field: "gate", detail: `gate ${step.gate} is reserved and cannot be executed` };
|
|
27562
|
+
}
|
|
27563
|
+
if (step.assignments.allowedAgents && step.assignments.allowedAgents.length > 0) {
|
|
27564
|
+
return { reason: "step_unsupported", field: "assignments.allowedAgents", detail: "gate steps do not support allowedAgents" };
|
|
27565
|
+
}
|
|
27566
|
+
if (step.assignments.minimum !== 1 || step.assignments.target !== 1 || step.assignments.maximum !== 1) {
|
|
27567
|
+
return { reason: "step_unsupported", field: "assignments", detail: "gate steps require exactly 1 agent assignment" };
|
|
27568
|
+
}
|
|
27569
|
+
if (step.assignments.maxParallel !== 1) {
|
|
27570
|
+
return { reason: "step_unsupported", field: "assignments.maxParallel", detail: "gate steps require maxParallel of 1" };
|
|
27571
|
+
}
|
|
27572
|
+
if (step.assignments.maxAttemptsPerAssignment !== 1) {
|
|
27573
|
+
return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "gate steps require maxAttemptsPerAssignment of 1" };
|
|
27574
|
+
}
|
|
27575
|
+
if (step.assignments.distinctBy && step.assignments.distinctBy.length > 0) {
|
|
27576
|
+
return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "gate steps do not support distinctBy" };
|
|
27577
|
+
}
|
|
27578
|
+
if (step.assignments.maxWriteRepositories) {
|
|
27579
|
+
return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "gate steps do not support maxWriteRepositories" };
|
|
27580
|
+
}
|
|
27581
|
+
if (step.join.strategy !== "all") {
|
|
27582
|
+
return { reason: "step_unsupported", field: "join", detail: `gate steps require join strategy 'all', not '${step.join.strategy}'` };
|
|
27583
|
+
}
|
|
27584
|
+
if (step.join.minimumPassed !== void 0) {
|
|
27585
|
+
return { reason: "step_unsupported", field: "join", detail: "gate steps do not support minimumPassed" };
|
|
27586
|
+
}
|
|
27587
|
+
if (step.join.cancelRemaining) {
|
|
27588
|
+
return { reason: "step_unsupported", field: "join", detail: "gate steps do not support cancelRemaining" };
|
|
27589
|
+
}
|
|
27590
|
+
for (const evidence of step.requiredEvidence) {
|
|
27591
|
+
if (!evidence || !["gate", "artifact", "receipt", "assignment-result", "approval"].includes(evidence.kind)) {
|
|
27592
|
+
return { reason: "step_unsupported", field: "requiredEvidence", detail: `gate steps do not support evidence kind '${evidence?.kind}'` };
|
|
27593
|
+
}
|
|
27594
|
+
}
|
|
27595
|
+
if (step.safeSpeculation) {
|
|
27596
|
+
return { reason: "step_unsupported", field: "safeSpeculation", detail: "gate steps do not support safeSpeculation" };
|
|
27597
|
+
}
|
|
27598
|
+
if (step.tools) {
|
|
27599
|
+
return { reason: "step_unsupported", field: "tools", detail: "gate steps do not support tools" };
|
|
27600
|
+
}
|
|
27601
|
+
if (step.secrets && step.secrets.length > 0) {
|
|
27602
|
+
return { reason: "step_unsupported", field: "secrets", detail: "gate steps do not support secrets" };
|
|
27603
|
+
}
|
|
27604
|
+
if (step.model) {
|
|
27605
|
+
return { reason: "step_unsupported", field: "model", detail: "gate steps do not support model" };
|
|
27606
|
+
}
|
|
27607
|
+
if (plan.reproOracle?.stageId === step.id) {
|
|
27608
|
+
return { reason: "step_unsupported", field: "reproOracle", detail: "gate steps cannot be repro oracle" };
|
|
27609
|
+
}
|
|
27610
|
+
if (plan.planHash?.stageId === step.id) {
|
|
27611
|
+
return { reason: "step_unsupported", field: "planHash", detail: "gate steps cannot be plan hash oracle" };
|
|
27612
|
+
}
|
|
27613
|
+
if (step.timeoutMs !== void 0) {
|
|
27614
|
+
if (definition.kind === "artifacts-exist") {
|
|
27615
|
+
return { reason: "step_unsupported", field: "timeoutMs", detail: "artifacts-exist gates do not support timeoutMs" };
|
|
27616
|
+
}
|
|
27617
|
+
if (definition.kind === "command" && definition.timeoutMs !== void 0 && step.timeoutMs < definition.timeoutMs) {
|
|
27618
|
+
return { reason: "step_unsupported", field: "timeoutMs", detail: "command step timeoutMs narrower than the registry timeout is refused" };
|
|
27619
|
+
}
|
|
27620
|
+
}
|
|
27621
|
+
const repositories = step.repositories;
|
|
27622
|
+
if (!repositories || Object.keys(repositories).length === 0) {
|
|
27623
|
+
return { reason: "step_unsupported", field: "repositories", detail: "gate steps require repository declarations" };
|
|
27624
|
+
}
|
|
27625
|
+
if (definition.kind === "artifacts-exist") {
|
|
27626
|
+
const controlAccess = repositories.control;
|
|
27627
|
+
if (controlAccess !== "read" && controlAccess !== "write") {
|
|
27628
|
+
return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require control repository access as read or write" };
|
|
27629
|
+
}
|
|
27630
|
+
for (const [repoId, access] of Object.entries(repositories)) {
|
|
27631
|
+
if (repoId !== "control" && access !== "none") {
|
|
27632
|
+
return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require other repositories to have access 'none'" };
|
|
27633
|
+
}
|
|
27634
|
+
}
|
|
27635
|
+
} else if (definition.kind === "command") {
|
|
27636
|
+
for (const [repoId, access] of Object.entries(repositories)) {
|
|
27637
|
+
if (access !== "read" && access !== "write" && access !== "none") {
|
|
27638
|
+
return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
|
|
27639
|
+
}
|
|
27640
|
+
}
|
|
27641
|
+
const hasWrite = Object.values(repositories).some((access) => access === "write");
|
|
27642
|
+
if (!hasWrite) {
|
|
27643
|
+
return { reason: "step_unsupported", field: "repositories", detail: "command gates require at least one repository with write access" };
|
|
27644
|
+
}
|
|
27645
|
+
}
|
|
27646
|
+
const expectPass = step.expect === "pass";
|
|
27647
|
+
if (expectPass) {
|
|
27648
|
+
if (!step.outcomes.includes("passed")) {
|
|
27649
|
+
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required outcome passed; spell it as 'passed'" };
|
|
27650
|
+
}
|
|
27651
|
+
if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
|
|
27652
|
+
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required failure outcome; declare implementation-failure or failed" };
|
|
27653
|
+
}
|
|
27654
|
+
} else {
|
|
27655
|
+
if (!step.outcomes.includes("repro-missing")) {
|
|
27656
|
+
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required outcome repro-missing; spell it as 'repro-missing'" };
|
|
27657
|
+
}
|
|
27658
|
+
if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
|
|
27659
|
+
return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required failure outcome; declare implementation-failure or failed" };
|
|
27660
|
+
}
|
|
27661
|
+
}
|
|
27662
|
+
if (context && projectRuntimeKey(context.projectRoot) !== envelope.gates.controlRoot.projectKey) {
|
|
27663
|
+
throw runtimeError("run_owner_mismatch", plan.workflowId, `gate control root does not match project`);
|
|
27664
|
+
}
|
|
27665
|
+
if (definition.kind === "artifacts-exist" && step.expect === "fail") {
|
|
27666
|
+
return { reason: "step_unsupported", field: "expect", detail: "artifacts-exist gates cannot expect fail" };
|
|
27667
|
+
}
|
|
27668
|
+
return void 0;
|
|
27669
|
+
}
|
|
27670
|
+
function pinnedGatesForCompiledPlan(plan, bundle, projectRoot) {
|
|
27671
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
27672
|
+
for (const step of Object.values(plan.steps)) {
|
|
27673
|
+
if (step.kind === "gate") referenced.add(step.gate);
|
|
27674
|
+
}
|
|
27675
|
+
const definitions = /* @__PURE__ */ Object.create(null);
|
|
27676
|
+
const registryValue = bundle.gateRegistry?.value;
|
|
27677
|
+
const gatesObject = registryValue && typeof registryValue.gates === "object" && registryValue.gates && !Array.isArray(registryValue.gates) ? registryValue.gates : {};
|
|
27678
|
+
for (const gateId of referenced) {
|
|
27679
|
+
definitions[gateId] = parseGateDefinition(gatesObject[gateId], gateId, plan.workflowId);
|
|
27680
|
+
}
|
|
27681
|
+
return {
|
|
27682
|
+
registry: referenced.size > 0 && bundle.gateRegistry ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : bundle.gateRegistry && referenced.size === 0 ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : null,
|
|
27683
|
+
definitions,
|
|
27684
|
+
controlRoot: { repositoryId: "control", projectKey: projectRuntimeKey(projectRoot) }
|
|
27685
|
+
};
|
|
27686
|
+
}
|
|
27164
27687
|
|
|
27165
27688
|
// plugins/kxm/src/hub-binding.ts
|
|
27166
|
-
import { existsSync as
|
|
27689
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync14, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
|
|
27167
27690
|
import { homedir as homedir4 } from "node:os";
|
|
27168
|
-
import { dirname as dirname10, isAbsolute as isAbsolute6, join as
|
|
27691
|
+
import { dirname as dirname10, isAbsolute as isAbsolute6, join as join16, resolve as resolve11 } from "node:path";
|
|
27169
27692
|
var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
|
|
27170
27693
|
var HUB_HEALTH_PROBE_MS = 300;
|
|
27171
27694
|
var HubBindingError = class extends Error {
|
|
@@ -27182,16 +27705,16 @@ function resolveUserStateRoot2(env) {
|
|
|
27182
27705
|
}
|
|
27183
27706
|
if (process.platform === "win32") {
|
|
27184
27707
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
27185
|
-
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData :
|
|
27708
|
+
const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join16(homedir4(), "AppData", "Local");
|
|
27186
27709
|
return resolve11(base2, "KXM");
|
|
27187
27710
|
}
|
|
27188
27711
|
if (process.platform === "darwin") return resolve11(homedir4(), "Library", "Application Support", "KXM");
|
|
27189
27712
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
27190
|
-
const base = xdgState && isAbsolute6(xdgState) ? xdgState :
|
|
27713
|
+
const base = xdgState && isAbsolute6(xdgState) ? xdgState : join16(homedir4(), ".local", "state");
|
|
27191
27714
|
return resolve11(base, "kxm");
|
|
27192
27715
|
}
|
|
27193
27716
|
function hubBindingFile(env = process.env) {
|
|
27194
|
-
return
|
|
27717
|
+
return join16(resolveUserStateRoot2(env), "hub-binding.json");
|
|
27195
27718
|
}
|
|
27196
27719
|
function validateHubUrl(raw) {
|
|
27197
27720
|
let parsed;
|
|
@@ -27227,10 +27750,10 @@ function isAbortError(error) {
|
|
|
27227
27750
|
}
|
|
27228
27751
|
function readHubBinding(env = process.env) {
|
|
27229
27752
|
const file = hubBindingFile(env);
|
|
27230
|
-
if (!
|
|
27753
|
+
if (!existsSync16(file)) return void 0;
|
|
27231
27754
|
let parsed;
|
|
27232
27755
|
try {
|
|
27233
|
-
parsed = JSON.parse(
|
|
27756
|
+
parsed = JSON.parse(readFileSync14(file, "utf8"));
|
|
27234
27757
|
} catch {
|
|
27235
27758
|
throw new HubBindingError(`malformed hub binding at ${file}`);
|
|
27236
27759
|
}
|
|
@@ -27253,7 +27776,7 @@ function readHubBinding(env = process.env) {
|
|
|
27253
27776
|
function writeHubBinding(record, env = process.env) {
|
|
27254
27777
|
const file = hubBindingFile(env);
|
|
27255
27778
|
mkdirSync10(dirname10(file), { recursive: true, mode: 448 });
|
|
27256
|
-
const temporary =
|
|
27779
|
+
const temporary = join16(dirname10(file), `.hub-binding-${process.pid}.tmp`);
|
|
27257
27780
|
try {
|
|
27258
27781
|
writeFileSync11(temporary, `${JSON.stringify(record, null, 2)}
|
|
27259
27782
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -27302,7 +27825,7 @@ var DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
27302
27825
|
|
|
27303
27826
|
// plugins/kxm/src/runtime-supervisor.ts
|
|
27304
27827
|
function kxmSupervisorTokenFile(paths) {
|
|
27305
|
-
return
|
|
27828
|
+
return join17(paths.runtimeDir, "supervisor.token");
|
|
27306
27829
|
}
|
|
27307
27830
|
function readKxmSupervisorToken(paths) {
|
|
27308
27831
|
const file = kxmSupervisorTokenFile(paths);
|
|
@@ -27311,7 +27834,7 @@ function readKxmSupervisorToken(paths) {
|
|
|
27311
27834
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
27312
27835
|
throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
|
|
27313
27836
|
}
|
|
27314
|
-
const token =
|
|
27837
|
+
const token = readFileSync15(file, "utf8").trim();
|
|
27315
27838
|
return token.length >= 32 ? token : void 0;
|
|
27316
27839
|
}
|
|
27317
27840
|
function processAlive(pid) {
|
|
@@ -27325,11 +27848,11 @@ function processAlive(pid) {
|
|
|
27325
27848
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
27326
27849
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
27327
27850
|
function supervisorErrorFile(paths) {
|
|
27328
|
-
return
|
|
27851
|
+
return join17(paths.runtimeDir, "supervisor.error");
|
|
27329
27852
|
}
|
|
27330
27853
|
function clearSupervisorError(paths) {
|
|
27331
27854
|
const file = supervisorErrorFile(paths);
|
|
27332
|
-
if (
|
|
27855
|
+
if (existsSync17(file)) rmSync5(file, { force: true });
|
|
27333
27856
|
}
|
|
27334
27857
|
function readRecentSupervisorError(paths) {
|
|
27335
27858
|
const file = supervisorErrorFile(paths);
|
|
@@ -27338,7 +27861,7 @@ function readRecentSupervisorError(paths) {
|
|
|
27338
27861
|
const ageMs = Date.now() - stat.mtimeMs;
|
|
27339
27862
|
if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
|
|
27340
27863
|
try {
|
|
27341
|
-
return
|
|
27864
|
+
return readFileSync15(file, "utf8").trim();
|
|
27342
27865
|
} catch {
|
|
27343
27866
|
return void 0;
|
|
27344
27867
|
}
|
|
@@ -27359,7 +27882,7 @@ function supervisorStatusOf(record) {
|
|
|
27359
27882
|
};
|
|
27360
27883
|
}
|
|
27361
27884
|
function kxmSupervisorStatus(paths, options = {}) {
|
|
27362
|
-
if (!
|
|
27885
|
+
if (!existsSync17(paths.registryDb)) return { running: false };
|
|
27363
27886
|
if (options.readOnly) {
|
|
27364
27887
|
const database = openReadOnlyDatabase(paths.registryDb);
|
|
27365
27888
|
try {
|
|
@@ -27425,7 +27948,7 @@ async function ensureKxmSupervisor(options = {}) {
|
|
|
27425
27948
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
27426
27949
|
}
|
|
27427
27950
|
clearSupervisorError(paths);
|
|
27428
|
-
const scriptPath =
|
|
27951
|
+
const scriptPath = join17(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
|
|
27429
27952
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
27430
27953
|
const child = spawn2(process.execPath, [script], {
|
|
27431
27954
|
detached: true,
|
|
@@ -27489,11 +28012,11 @@ function handoffSuffix(handoff) {
|
|
|
27489
28012
|
|
|
27490
28013
|
// plugins/kxm/src/cli/types.ts
|
|
27491
28014
|
import { spawn as spawn3 } from "node:child_process";
|
|
27492
|
-
import { join as
|
|
28015
|
+
import { join as join19, resolve as resolve12 } from "node:path";
|
|
27493
28016
|
|
|
27494
28017
|
// plugins/kxm/src/telemetry.ts
|
|
27495
|
-
import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as
|
|
27496
|
-
import { dirname as dirname12, join as
|
|
28018
|
+
import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as readFileSync16 } from "node:fs";
|
|
28019
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
27497
28020
|
var TELEMETRY_SCHEMA = "kxm.telemetry.v1";
|
|
27498
28021
|
function inferImprovementTarget(input) {
|
|
27499
28022
|
const explicit = input.env?.KXM_IMPROVE_TARGET?.trim();
|
|
@@ -27508,7 +28031,7 @@ function appendTelemetry(path4, event) {
|
|
|
27508
28031
|
`, { encoding: "utf8", mode: 384 });
|
|
27509
28032
|
}
|
|
27510
28033
|
function telemetryPath(logsDir) {
|
|
27511
|
-
return
|
|
28034
|
+
return join18(logsDir, "telemetry.jsonl");
|
|
27512
28035
|
}
|
|
27513
28036
|
function makeTelemetryEvent(input) {
|
|
27514
28037
|
return {
|
|
@@ -27523,7 +28046,7 @@ function makeTelemetryEvent(input) {
|
|
|
27523
28046
|
function readRoutingRecords(path4) {
|
|
27524
28047
|
const records = [];
|
|
27525
28048
|
try {
|
|
27526
|
-
const raw =
|
|
28049
|
+
const raw = readFileSync16(path4, "utf8");
|
|
27527
28050
|
for (const line of raw.split(/\r?\n/)) {
|
|
27528
28051
|
if (!line.trim()) continue;
|
|
27529
28052
|
try {
|
|
@@ -27602,7 +28125,7 @@ function workerResult(worker, payload) {
|
|
|
27602
28125
|
// plugins/kxm/src/cli/types.ts
|
|
27603
28126
|
function spawnScript(scriptName, extraEnv = {}) {
|
|
27604
28127
|
return new Promise((resolveExit) => {
|
|
27605
|
-
const child = spawn3(process.execPath, [
|
|
28128
|
+
const child = spawn3(process.execPath, [join19(findKxmRepoRoot(import.meta.url), "scripts", scriptName)], {
|
|
27606
28129
|
stdio: "inherit",
|
|
27607
28130
|
env: { ...process.env, ...extraEnv }
|
|
27608
28131
|
});
|
|
@@ -27703,10 +28226,10 @@ function workspaceDirs(cwd, workspaceFlag, env) {
|
|
|
27703
28226
|
return {
|
|
27704
28227
|
workdir,
|
|
27705
28228
|
workspace,
|
|
27706
|
-
config: derive ?
|
|
27707
|
-
logs: derive ?
|
|
27708
|
-
assets: derive ?
|
|
27709
|
-
state: derive ?
|
|
28229
|
+
config: derive ? join19(workspace, "config") : resolve12(workdir, env.KXM_CONFIG_DIR?.trim() || join19(workspace, "config")),
|
|
28230
|
+
logs: derive ? join19(workspace, "logs") : resolve12(workdir, env.KXM_LOGS_DIR?.trim() || join19(workspace, "logs")),
|
|
28231
|
+
assets: derive ? join19(workspace, "assets") : resolve12(workdir, env.KXM_ASSETS_DIR?.trim() || join19(workspace, "assets")),
|
|
28232
|
+
state: derive ? join19(workspace, "state") : resolve12(workdir, env.KXM_STATE_DIR?.trim() || join19(workspace, "state"))
|
|
27710
28233
|
};
|
|
27711
28234
|
}
|
|
27712
28235
|
function maskEnvName(name) {
|
|
@@ -27774,20 +28297,20 @@ function workspaceEnv(runtime) {
|
|
|
27774
28297
|
// plugins/kxm/src/cli/roles.ts
|
|
27775
28298
|
var import_yaml10 = __toESM(require_dist(), 1);
|
|
27776
28299
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
27777
|
-
import { existsSync as
|
|
27778
|
-
import { join as
|
|
28300
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
|
|
28301
|
+
import { join as join22, resolve as resolve15 } from "node:path";
|
|
27779
28302
|
import { createInterface } from "node:readline";
|
|
27780
28303
|
|
|
27781
28304
|
// plugins/kxm/src/role.ts
|
|
27782
28305
|
var import_yaml9 = __toESM(require_dist(), 1);
|
|
27783
|
-
import { existsSync as
|
|
27784
|
-
import { join as
|
|
28306
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync18, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
|
|
28307
|
+
import { join as join21 } from "node:path";
|
|
27785
28308
|
|
|
27786
28309
|
// plugins/kxm/src/config.ts
|
|
27787
28310
|
var import_yaml8 = __toESM(require_dist(), 1);
|
|
27788
|
-
import { existsSync as
|
|
28311
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
27789
28312
|
import { homedir as homedir5 } from "node:os";
|
|
27790
|
-
import { dirname as dirname13, join as
|
|
28313
|
+
import { dirname as dirname13, join as join20, resolve as resolve13 } from "node:path";
|
|
27791
28314
|
var KXM_CONFIG_SCHEMA = "kxm.config.v1";
|
|
27792
28315
|
var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
|
|
27793
28316
|
var DEFAULT_KXM_CONFIG = {
|
|
@@ -27842,7 +28365,7 @@ var DEFAULT_KXM_CONFIG = {
|
|
|
27842
28365
|
};
|
|
27843
28366
|
function userConfigDirectory(overrideDir) {
|
|
27844
28367
|
if (overrideDir) return resolve13(overrideDir);
|
|
27845
|
-
return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() ||
|
|
28368
|
+
return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() || join20(homedir5(), ".config", "kxm"));
|
|
27846
28369
|
}
|
|
27847
28370
|
function repoConfigDirectory(repoRoot) {
|
|
27848
28371
|
return resolve13(repoRoot, ".kxm");
|
|
@@ -27889,14 +28412,14 @@ function normalizeImprovementConfig(raw) {
|
|
|
27889
28412
|
}
|
|
27890
28413
|
function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
27891
28414
|
const userDir = userConfigDirectory(options.userConfigDir);
|
|
27892
|
-
const userConfigFile =
|
|
28415
|
+
const userConfigFile = join20(userDir, "config.yaml");
|
|
27893
28416
|
const repoDir = repoConfigDirectory(repoRoot);
|
|
27894
|
-
const repoConfigFile =
|
|
28417
|
+
const repoConfigFile = join20(repoDir, "config.yaml");
|
|
27895
28418
|
let userRaw = {};
|
|
27896
28419
|
let userLoadedPath;
|
|
27897
|
-
if (
|
|
28420
|
+
if (existsSync18(userConfigFile)) {
|
|
27898
28421
|
try {
|
|
27899
|
-
const text =
|
|
28422
|
+
const text = readFileSync17(userConfigFile, "utf8");
|
|
27900
28423
|
userRaw = (0, import_yaml8.parse)(text) ?? {};
|
|
27901
28424
|
userLoadedPath = userConfigFile;
|
|
27902
28425
|
} catch (error) {
|
|
@@ -27905,9 +28428,9 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
27905
28428
|
}
|
|
27906
28429
|
let repoRaw = {};
|
|
27907
28430
|
let repoLoadedPath;
|
|
27908
|
-
if (
|
|
28431
|
+
if (existsSync18(repoConfigFile)) {
|
|
27909
28432
|
try {
|
|
27910
|
-
const text =
|
|
28433
|
+
const text = readFileSync17(repoConfigFile, "utf8");
|
|
27911
28434
|
repoRaw = (0, import_yaml8.parse)(text) ?? {};
|
|
27912
28435
|
repoLoadedPath = repoConfigFile;
|
|
27913
28436
|
} catch (error) {
|
|
@@ -27943,7 +28466,7 @@ function getKxmConfigValue(config, keyPath) {
|
|
|
27943
28466
|
return current;
|
|
27944
28467
|
}
|
|
27945
28468
|
function kxmConfigFileForScope(repoRoot, scope, userConfigDir) {
|
|
27946
|
-
return scope === "user" ?
|
|
28469
|
+
return scope === "user" ? join20(userConfigDirectory(userConfigDir), "config.yaml") : join20(repoConfigDirectory(repoRoot), "config.yaml");
|
|
27947
28470
|
}
|
|
27948
28471
|
function configKeyParts(keyPath) {
|
|
27949
28472
|
const parts = keyPath.split(".");
|
|
@@ -27954,9 +28477,9 @@ function configKeyParts(keyPath) {
|
|
|
27954
28477
|
}
|
|
27955
28478
|
function readConfigFile(targetFile2) {
|
|
27956
28479
|
let existing = {};
|
|
27957
|
-
if (
|
|
28480
|
+
if (existsSync18(targetFile2)) {
|
|
27958
28481
|
try {
|
|
27959
|
-
existing = (0, import_yaml8.parse)(
|
|
28482
|
+
existing = (0, import_yaml8.parse)(readFileSync17(targetFile2, "utf8")) ?? {};
|
|
27960
28483
|
} catch {
|
|
27961
28484
|
existing = {};
|
|
27962
28485
|
}
|
|
@@ -28074,21 +28597,21 @@ var DEFAULT_ROLES = {
|
|
|
28074
28597
|
};
|
|
28075
28598
|
function rolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
28076
28599
|
if (scope === "global") {
|
|
28077
|
-
return
|
|
28600
|
+
return join21(userConfigDirectory(userConfigDir), "roles");
|
|
28078
28601
|
}
|
|
28079
|
-
return
|
|
28602
|
+
return join21(repoConfigDirectory(repoRoot), "roles");
|
|
28080
28603
|
}
|
|
28081
28604
|
function ensureRolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
28082
28605
|
const dir = rolesDirectory(scope, repoRoot, userConfigDir);
|
|
28083
|
-
if (!
|
|
28606
|
+
if (!existsSync19(dir)) {
|
|
28084
28607
|
mkdirSync14(dir, { recursive: true });
|
|
28085
28608
|
}
|
|
28086
28609
|
return dir;
|
|
28087
28610
|
}
|
|
28088
28611
|
function parseRoleFile(filePath) {
|
|
28089
|
-
if (!
|
|
28612
|
+
if (!existsSync19(filePath)) return void 0;
|
|
28090
28613
|
try {
|
|
28091
|
-
const raw =
|
|
28614
|
+
const raw = readFileSync18(filePath, "utf8");
|
|
28092
28615
|
const parsed = (0, import_yaml9.parse)(raw);
|
|
28093
28616
|
if (!parsed || typeof parsed !== "object" || parsed.schema !== KXM_ROLE_SCHEMA) {
|
|
28094
28617
|
return void 0;
|
|
@@ -28115,10 +28638,10 @@ function listRoles(options = {}) {
|
|
|
28115
28638
|
const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
|
|
28116
28639
|
const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
|
|
28117
28640
|
const localRoles = /* @__PURE__ */ new Map();
|
|
28118
|
-
if (scopeFilter !== "global" &&
|
|
28641
|
+
if (scopeFilter !== "global" && existsSync19(localDir)) {
|
|
28119
28642
|
for (const entry of readdirSync6(localDir)) {
|
|
28120
28643
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
28121
|
-
const filePath =
|
|
28644
|
+
const filePath = join21(localDir, entry);
|
|
28122
28645
|
const parsed = parseRoleFile(filePath);
|
|
28123
28646
|
if (parsed) {
|
|
28124
28647
|
localRoles.set(parsed.id, { role: parsed, filePath });
|
|
@@ -28127,10 +28650,10 @@ function listRoles(options = {}) {
|
|
|
28127
28650
|
}
|
|
28128
28651
|
}
|
|
28129
28652
|
const globalRoles = /* @__PURE__ */ new Map();
|
|
28130
|
-
if (scopeFilter !== "local" &&
|
|
28653
|
+
if (scopeFilter !== "local" && existsSync19(globalDir)) {
|
|
28131
28654
|
for (const entry of readdirSync6(globalDir)) {
|
|
28132
28655
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
28133
|
-
const filePath =
|
|
28656
|
+
const filePath = join21(globalDir, entry);
|
|
28134
28657
|
const parsed = parseRoleFile(filePath);
|
|
28135
28658
|
if (parsed) {
|
|
28136
28659
|
globalRoles.set(parsed.id, { role: parsed, filePath });
|
|
@@ -28178,13 +28701,13 @@ function getRole(roleId, options = {}) {
|
|
|
28178
28701
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28179
28702
|
if (scope !== "global") {
|
|
28180
28703
|
const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
|
|
28181
|
-
const localFile =
|
|
28704
|
+
const localFile = join21(localDir, `${roleId}.yaml`);
|
|
28182
28705
|
const parsed = parseRoleFile(localFile);
|
|
28183
28706
|
if (parsed) return { role: parsed, scope: "local", filePath: localFile };
|
|
28184
28707
|
}
|
|
28185
28708
|
if (scope !== "local") {
|
|
28186
28709
|
const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
|
|
28187
|
-
const globalFile =
|
|
28710
|
+
const globalFile = join21(globalDir, `${roleId}.yaml`);
|
|
28188
28711
|
const parsed = parseRoleFile(globalFile);
|
|
28189
28712
|
if (parsed) return { role: parsed, scope: "global", filePath: globalFile };
|
|
28190
28713
|
}
|
|
@@ -28194,8 +28717,8 @@ function addRole(role, options = {}) {
|
|
|
28194
28717
|
const scope = options.scope ?? "local";
|
|
28195
28718
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28196
28719
|
const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
28197
|
-
const filePath =
|
|
28198
|
-
if (
|
|
28720
|
+
const filePath = join21(dir, `${role.id}.yaml`);
|
|
28721
|
+
if (existsSync19(filePath) && !options.overwrite) {
|
|
28199
28722
|
throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
|
|
28200
28723
|
}
|
|
28201
28724
|
if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(role), "utf8");
|
|
@@ -28205,8 +28728,8 @@ function removeRole(roleId, options = {}) {
|
|
|
28205
28728
|
const scope = options.scope ?? "local";
|
|
28206
28729
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28207
28730
|
const dir = rolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
28208
|
-
const filePath =
|
|
28209
|
-
if (!
|
|
28731
|
+
const filePath = join21(dir, `${roleId}.yaml`);
|
|
28732
|
+
if (!existsSync19(filePath)) {
|
|
28210
28733
|
throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
|
|
28211
28734
|
}
|
|
28212
28735
|
if (options.dryRun) return { id: roleId, removed: false, filePath, scope };
|
|
@@ -28228,7 +28751,7 @@ function modifyRole(roleId, updates, options = {}) {
|
|
|
28228
28751
|
const scope = options.scope ?? target.scope;
|
|
28229
28752
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28230
28753
|
const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
|
|
28231
|
-
const filePath =
|
|
28754
|
+
const filePath = join21(dir, `${roleId}.yaml`);
|
|
28232
28755
|
if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(updated), "utf8");
|
|
28233
28756
|
return { id: roleId, role: updated, filePath, scope };
|
|
28234
28757
|
}
|
|
@@ -28277,18 +28800,18 @@ var DEFAULT_ROLE_SEATS = {
|
|
|
28277
28800
|
};
|
|
28278
28801
|
function findExistingRoleHostsFile(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
28279
28802
|
const dir = scope === "global" ? userConfigDirectory(userConfigDir) : repoConfigDirectory(repoRoot);
|
|
28280
|
-
const yamlPath =
|
|
28281
|
-
const ymlPath =
|
|
28282
|
-
const jsonPath =
|
|
28283
|
-
if (
|
|
28284
|
-
if (
|
|
28285
|
-
if (
|
|
28803
|
+
const yamlPath = join21(dir, "role-hosts.yaml");
|
|
28804
|
+
const ymlPath = join21(dir, "role-hosts.yml");
|
|
28805
|
+
const jsonPath = join21(dir, "role-hosts.json");
|
|
28806
|
+
if (existsSync19(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
|
|
28807
|
+
if (existsSync19(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
|
|
28808
|
+
if (existsSync19(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
|
|
28286
28809
|
return { filePath: yamlPath, exists: false, format: "yaml" };
|
|
28287
28810
|
}
|
|
28288
28811
|
function parseRoleHostsFile(filePath) {
|
|
28289
|
-
if (!
|
|
28812
|
+
if (!existsSync19(filePath)) return void 0;
|
|
28290
28813
|
try {
|
|
28291
|
-
const raw =
|
|
28814
|
+
const raw = readFileSync18(filePath, "utf8");
|
|
28292
28815
|
const parsed = (0, import_yaml9.parse)(raw);
|
|
28293
28816
|
if (!parsed || typeof parsed !== "object") return void 0;
|
|
28294
28817
|
if (parsed.schema && parsed.schema !== KXM_ROLE_HOSTS_SCHEMA) return void 0;
|
|
@@ -28361,9 +28884,9 @@ function saveRoleHostsConfig(config, options = {}) {
|
|
|
28361
28884
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
28362
28885
|
const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot);
|
|
28363
28886
|
const format = options.format ?? "yaml";
|
|
28364
|
-
const filePath =
|
|
28887
|
+
const filePath = join21(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
|
|
28365
28888
|
if (options.dryRun) return { filePath, scope };
|
|
28366
|
-
if (!
|
|
28889
|
+
if (!existsSync19(dir)) {
|
|
28367
28890
|
mkdirSync14(dir, { recursive: true });
|
|
28368
28891
|
}
|
|
28369
28892
|
const payload = {
|
|
@@ -28613,7 +29136,7 @@ async function cmdRoleAdd(runtime, roleId, options) {
|
|
|
28613
29136
|
let roleDef;
|
|
28614
29137
|
if (options.file) {
|
|
28615
29138
|
const filePath = resolve15(runtime.cwd, options.file);
|
|
28616
|
-
const content =
|
|
29139
|
+
const content = readFileSync19(filePath, "utf8");
|
|
28617
29140
|
roleDef = (0, import_yaml10.parse)(content);
|
|
28618
29141
|
roleDef.id = roleId;
|
|
28619
29142
|
} else {
|
|
@@ -28942,8 +29465,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
28942
29465
|
return 1;
|
|
28943
29466
|
}
|
|
28944
29467
|
}
|
|
28945
|
-
const dbPath =
|
|
28946
|
-
if (
|
|
29468
|
+
const dbPath = join22(runtime.cwd, ".kxm", "state", "kxm.db");
|
|
29469
|
+
if (existsSync20(dbPath)) {
|
|
28947
29470
|
try {
|
|
28948
29471
|
const database = runtime.dryRun ? openReadOnlyDatabase(dbPath) : new DatabaseSync(dbPath);
|
|
28949
29472
|
try {
|
|
@@ -29015,8 +29538,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
|
|
|
29015
29538
|
// plugins/kxm/src/cli/workflows.ts
|
|
29016
29539
|
var import_yaml12 = __toESM(require_dist(), 1);
|
|
29017
29540
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
29018
|
-
import { existsSync as
|
|
29019
|
-
import { join as
|
|
29541
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
|
|
29542
|
+
import { join as join24, resolve as resolve17 } from "node:path";
|
|
29020
29543
|
|
|
29021
29544
|
// plugins/kxm/src/retrospective.ts
|
|
29022
29545
|
import { mkdirSync as mkdirSync15, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "node:fs";
|
|
@@ -29344,7 +29867,7 @@ async function watchGithubChecks(input) {
|
|
|
29344
29867
|
}
|
|
29345
29868
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
29346
29869
|
const now = input.now ?? Date.now;
|
|
29347
|
-
const sleep = input.sleep ?? ((ms) => new Promise((
|
|
29870
|
+
const sleep = input.sleep ?? ((ms) => new Promise((resolve34) => setTimeout(resolve34, ms)));
|
|
29348
29871
|
const deadline = now() + input.timeoutMs;
|
|
29349
29872
|
const headers = {
|
|
29350
29873
|
authorization: `Bearer ${token}`,
|
|
@@ -29443,8 +29966,22 @@ async function watchGithubChecks(input) {
|
|
|
29443
29966
|
|
|
29444
29967
|
// plugins/kxm/src/workflow-manager.ts
|
|
29445
29968
|
var import_yaml11 = __toESM(require_dist(), 1);
|
|
29446
|
-
import { existsSync as
|
|
29447
|
-
import { join as
|
|
29969
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
|
|
29970
|
+
import { join as join23 } from "node:path";
|
|
29971
|
+
function assertWorkflowId(workflowId) {
|
|
29972
|
+
if (!kxmResourceIdentifier(workflowId)) {
|
|
29973
|
+
throw new Error(`workflow_id_invalid: '${workflowId}' must be a flat lowercase slug of at most 64 characters, starting with a letter and using letters, digits, single hyphens or underscores; paths and platform-reserved names are not allowed (use 'bug-fix', not 'software-engineering/bug-fix')`);
|
|
29974
|
+
}
|
|
29975
|
+
}
|
|
29976
|
+
var workflowSchemaRegistry;
|
|
29977
|
+
function workflowPayload(workflowId, content, filePath) {
|
|
29978
|
+
const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
|
|
29979
|
+
const value = parseRestrictedYaml2(payload, filePath);
|
|
29980
|
+
const issues = (workflowSchemaRegistry ??= new KxmSchemaRegistry()).validate("workflow", value, filePath);
|
|
29981
|
+
if (issues.length > 0) throw new KxmConfigError(issues);
|
|
29982
|
+
compileKxmWorkflow({ id: workflowId, value, logicalPath: filePath });
|
|
29983
|
+
return payload;
|
|
29984
|
+
}
|
|
29448
29985
|
var completed = () => ({ target: "$terminal", terminalStatus: "completed" });
|
|
29449
29986
|
var failed = () => ({ target: "$terminal", terminalStatus: "failed" });
|
|
29450
29987
|
function verifyStep(retryStep) {
|
|
@@ -29581,24 +30118,22 @@ function scaffoldWorkflowDefinition(description) {
|
|
|
29581
30118
|
var DEFAULT_WORKFLOW_TEMPLATE = WORKFLOW_TEMPLATES["implement-and-verify"];
|
|
29582
30119
|
function workflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
29583
30120
|
if (scope === "global") {
|
|
29584
|
-
return
|
|
30121
|
+
return join23(userConfigDirectory(userConfigDir), "workflows");
|
|
29585
30122
|
}
|
|
29586
|
-
return
|
|
30123
|
+
return join23(repoConfigDirectory(repoRoot), "workflows");
|
|
29587
30124
|
}
|
|
29588
30125
|
function ensureWorkflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
|
|
29589
30126
|
const dir = workflowsDirectory(scope, repoRoot, userConfigDir);
|
|
29590
|
-
if (!
|
|
30127
|
+
if (!existsSync21(dir)) {
|
|
29591
30128
|
mkdirSync16(dir, { recursive: true });
|
|
29592
30129
|
}
|
|
29593
30130
|
return dir;
|
|
29594
30131
|
}
|
|
29595
30132
|
function parseWorkflowFile(filePath) {
|
|
29596
|
-
if (!
|
|
30133
|
+
if (!existsSync21(filePath)) return void 0;
|
|
29597
30134
|
try {
|
|
29598
|
-
const raw =
|
|
29599
|
-
|
|
29600
|
-
if (!parsed || typeof parsed !== "object") return void 0;
|
|
29601
|
-
return parsed;
|
|
30135
|
+
const raw = readFileSync20(filePath, "utf8");
|
|
30136
|
+
return parseRestrictedYaml2(raw, filePath);
|
|
29602
30137
|
} catch {
|
|
29603
30138
|
return void 0;
|
|
29604
30139
|
}
|
|
@@ -29609,26 +30144,26 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
29609
30144
|
const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
|
|
29610
30145
|
const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
|
|
29611
30146
|
const localDefs = /* @__PURE__ */ new Map();
|
|
29612
|
-
if (scopeFilter !== "global" &&
|
|
30147
|
+
if (scopeFilter !== "global" && existsSync21(localDir)) {
|
|
29613
30148
|
for (const entry of readdirSync7(localDir)) {
|
|
29614
|
-
|
|
29615
|
-
|
|
30149
|
+
const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
|
|
30150
|
+
if (id && kxmResourceIdentifier(id)) {
|
|
30151
|
+
const filePath = join23(localDir, entry);
|
|
29616
30152
|
const def = parseWorkflowFile(filePath);
|
|
29617
30153
|
if (def) {
|
|
29618
|
-
const id = def.id || entry.replace(/\.ya?ml$/i, "");
|
|
29619
30154
|
localDefs.set(id, { def, filePath });
|
|
29620
30155
|
}
|
|
29621
30156
|
}
|
|
29622
30157
|
}
|
|
29623
30158
|
}
|
|
29624
30159
|
const globalDefs = /* @__PURE__ */ new Map();
|
|
29625
|
-
if (scopeFilter !== "local" &&
|
|
30160
|
+
if (scopeFilter !== "local" && existsSync21(globalDir)) {
|
|
29626
30161
|
for (const entry of readdirSync7(globalDir)) {
|
|
29627
|
-
|
|
29628
|
-
|
|
30162
|
+
const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
|
|
30163
|
+
if (id && kxmResourceIdentifier(id)) {
|
|
30164
|
+
const filePath = join23(globalDir, entry);
|
|
29629
30165
|
const def = parseWorkflowFile(filePath);
|
|
29630
30166
|
if (def) {
|
|
29631
|
-
const id = def.id || entry.replace(/\.ya?ml$/i, "");
|
|
29632
30167
|
globalDefs.set(id, { def, filePath });
|
|
29633
30168
|
}
|
|
29634
30169
|
}
|
|
@@ -29639,7 +30174,7 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
29639
30174
|
const steps = Array.isArray(def.steps) ? def.steps : [];
|
|
29640
30175
|
for (const step of steps) {
|
|
29641
30176
|
if (step && typeof step === "object") {
|
|
29642
|
-
const role = step.
|
|
30177
|
+
const role = step.agent;
|
|
29643
30178
|
if (typeof role === "string" && !roles.includes(role)) {
|
|
29644
30179
|
roles.push(role);
|
|
29645
30180
|
}
|
|
@@ -29677,40 +30212,46 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
29677
30212
|
return result;
|
|
29678
30213
|
}
|
|
29679
30214
|
function getWorkflowDefinition(workflowId, options = {}) {
|
|
30215
|
+
assertWorkflowId(workflowId);
|
|
29680
30216
|
const scope = options.scope ?? "all";
|
|
29681
30217
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
29682
30218
|
if (scope !== "global") {
|
|
29683
30219
|
const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
|
|
29684
|
-
const localFile =
|
|
30220
|
+
const localFile = join23(localDir, `${workflowId}.yaml`);
|
|
29685
30221
|
const parsed = parseWorkflowFile(localFile);
|
|
29686
30222
|
if (parsed) return { workflow: parsed, scope: "local", filePath: localFile };
|
|
29687
30223
|
}
|
|
29688
30224
|
if (scope !== "local") {
|
|
29689
30225
|
const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
|
|
29690
|
-
const globalFile =
|
|
30226
|
+
const globalFile = join23(globalDir, `${workflowId}.yaml`);
|
|
29691
30227
|
const parsed = parseWorkflowFile(globalFile);
|
|
29692
30228
|
if (parsed) return { workflow: parsed, scope: "global", filePath: globalFile };
|
|
29693
30229
|
}
|
|
29694
30230
|
return void 0;
|
|
29695
30231
|
}
|
|
29696
30232
|
function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE, options = {}) {
|
|
30233
|
+
assertWorkflowId(workflowId);
|
|
29697
30234
|
const scope = options.scope ?? "local";
|
|
29698
30235
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
29699
|
-
const dir =
|
|
29700
|
-
const filePath =
|
|
29701
|
-
if (
|
|
30236
|
+
const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
30237
|
+
const filePath = join23(dir, `${workflowId}.yaml`);
|
|
30238
|
+
if (existsSync21(filePath) && !options.overwrite) {
|
|
29702
30239
|
throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
|
|
29703
30240
|
}
|
|
29704
|
-
const payload =
|
|
29705
|
-
if (!options.dryRun)
|
|
30241
|
+
const payload = workflowPayload(workflowId, content, filePath);
|
|
30242
|
+
if (!options.dryRun) {
|
|
30243
|
+
ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
30244
|
+
writeFileSync16(filePath, payload, "utf8");
|
|
30245
|
+
}
|
|
29706
30246
|
return { id: workflowId, filePath, scope };
|
|
29707
30247
|
}
|
|
29708
30248
|
function removeWorkflowDefinition(workflowId, options = {}) {
|
|
30249
|
+
assertWorkflowId(workflowId);
|
|
29709
30250
|
const scope = options.scope ?? "local";
|
|
29710
30251
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
29711
30252
|
const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
29712
|
-
const filePath =
|
|
29713
|
-
if (!
|
|
30253
|
+
const filePath = join23(dir, `${workflowId}.yaml`);
|
|
30254
|
+
if (!existsSync21(filePath)) {
|
|
29714
30255
|
throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
|
|
29715
30256
|
}
|
|
29716
30257
|
if (options.dryRun) return { id: workflowId, removed: false, filePath, scope };
|
|
@@ -29728,16 +30269,20 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
|
|
|
29728
30269
|
};
|
|
29729
30270
|
const scope = options.scope ?? target.scope;
|
|
29730
30271
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
29731
|
-
const dir =
|
|
29732
|
-
const filePath =
|
|
29733
|
-
|
|
30272
|
+
const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
30273
|
+
const filePath = join23(dir, `${workflowId}.yaml`);
|
|
30274
|
+
const payload = workflowPayload(workflowId, updated, filePath);
|
|
30275
|
+
if (!options.dryRun) {
|
|
30276
|
+
ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
|
|
30277
|
+
writeFileSync16(filePath, payload, "utf8");
|
|
30278
|
+
}
|
|
29734
30279
|
return { id: workflowId, workflow: updated, filePath, scope };
|
|
29735
30280
|
}
|
|
29736
30281
|
|
|
29737
30282
|
// plugins/kxm/src/cli/workflows.ts
|
|
29738
30283
|
var CLI_NAME = "kxm";
|
|
29739
30284
|
function localWorkflowSnapshot(dataPath, runId) {
|
|
29740
|
-
if (!
|
|
30285
|
+
if (!existsSync22(dataPath)) throw new Error("state_database_not_found");
|
|
29741
30286
|
const database = openReadOnlyDatabase(dataPath);
|
|
29742
30287
|
try {
|
|
29743
30288
|
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();
|
|
@@ -29817,7 +30362,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
|
|
|
29817
30362
|
let raw;
|
|
29818
30363
|
if (file) {
|
|
29819
30364
|
try {
|
|
29820
|
-
raw =
|
|
30365
|
+
raw = readFileSync21(resolve17(runtime.cwd, file), "utf8");
|
|
29821
30366
|
} catch {
|
|
29822
30367
|
throw new Error("workflow definition file is unavailable");
|
|
29823
30368
|
}
|
|
@@ -29865,60 +30410,56 @@ async function cmdWorkflowDefinitions(runtime, options) {
|
|
|
29865
30410
|
}
|
|
29866
30411
|
async function cmdWorkflowAdd(runtime, workflowId, options) {
|
|
29867
30412
|
const scope = options.scope ?? "local";
|
|
29868
|
-
const refuse = (error,
|
|
29869
|
-
print(runtime.io, runtime.json, { ok: false, command: "workflow add", error }, `workflow add failed: ${
|
|
29870
|
-
return
|
|
30413
|
+
const refuse = (error, message, code = 2) => {
|
|
30414
|
+
print(runtime.io, runtime.json, { ok: false, command: "workflow add", error, message }, `workflow add failed: ${message}`);
|
|
30415
|
+
return code;
|
|
29871
30416
|
};
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
if (options.
|
|
29875
|
-
|
|
29876
|
-
|
|
29877
|
-
|
|
29878
|
-
|
|
29879
|
-
|
|
29880
|
-
|
|
29881
|
-
|
|
29882
|
-
|
|
29883
|
-
|
|
29884
|
-
|
|
29885
|
-
id,
|
|
29886
|
-
|
|
29887
|
-
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
29891
|
-
|
|
29892
|
-
|
|
29893
|
-
const
|
|
29894
|
-
|
|
29895
|
-
|
|
30417
|
+
try {
|
|
30418
|
+
let content;
|
|
30419
|
+
if (options.template !== void 0) {
|
|
30420
|
+
if (options.file !== void 0 || options.pick !== void 0) {
|
|
30421
|
+
return refuse("workflow_add_conflict", "--template cannot be combined with --file or --pick");
|
|
30422
|
+
}
|
|
30423
|
+
if (!workflowId) return refuse("workflow_id_required", "usage: kxm workflow add <workflowId> --template <name>");
|
|
30424
|
+
const template = Object.hasOwn(WORKFLOW_TEMPLATES, options.template) ? WORKFLOW_TEMPLATES[options.template] : void 0;
|
|
30425
|
+
if (!template) {
|
|
30426
|
+
return refuse("workflow_template_unknown", `unknown template ${options.template}; choose ${Object.keys(WORKFLOW_TEMPLATES).join(", ")}`);
|
|
30427
|
+
}
|
|
30428
|
+
content = { ...template, ...options.description !== void 0 ? { description: options.description } : {} };
|
|
30429
|
+
} else if (!workflowId || options.pick) {
|
|
30430
|
+
const candidates = Object.entries(WORKFLOW_TEMPLATES).map(([id, tmpl]) => ({
|
|
30431
|
+
id,
|
|
30432
|
+
description: String(tmpl.description ?? id),
|
|
30433
|
+
label: "template",
|
|
30434
|
+
payload: tmpl
|
|
30435
|
+
}));
|
|
30436
|
+
if (scope === "local") {
|
|
30437
|
+
const globalDefs = listWorkflowDefinitions({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
30438
|
+
for (const gd of globalDefs) {
|
|
30439
|
+
if (!candidates.some((c) => c.id === gd.id)) {
|
|
30440
|
+
candidates.push({ id: gd.id, description: gd.description, label: "global", payload: gd.filePath });
|
|
30441
|
+
}
|
|
29896
30442
|
}
|
|
29897
30443
|
}
|
|
29898
|
-
|
|
29899
|
-
|
|
29900
|
-
|
|
29901
|
-
if (!workflowId) {
|
|
29902
|
-
runtime.io.stderr("workflow add failed: missing workflowId or pick selection\n");
|
|
29903
|
-
return 1;
|
|
30444
|
+
const picked = await resolvePickItem(runtime.io, `Select a workflow template to add (${scope})`, candidates, options.pick, runtime.env);
|
|
30445
|
+
if (!picked) {
|
|
30446
|
+
return refuse("workflow_id_required", "provide a workflowId or select an available workflow with --pick <id>", 1);
|
|
29904
30447
|
}
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
content = {
|
|
29909
|
-
...picked.payload,
|
|
29910
|
-
|
|
29911
|
-
};
|
|
30448
|
+
workflowId ??= picked.id;
|
|
30449
|
+
if (options.file === void 0) {
|
|
30450
|
+
const pickedContent = typeof picked.payload === "string" ? readFileSync21(picked.payload, "utf8") : picked.payload;
|
|
30451
|
+
content = options.description !== void 0 ? {
|
|
30452
|
+
...typeof pickedContent === "string" ? parseRestrictedYaml2(pickedContent, String(picked.payload)) : pickedContent,
|
|
30453
|
+
description: options.description
|
|
30454
|
+
} : pickedContent;
|
|
29912
30455
|
}
|
|
29913
30456
|
}
|
|
29914
|
-
|
|
29915
|
-
|
|
29916
|
-
|
|
29917
|
-
|
|
29918
|
-
|
|
29919
|
-
|
|
29920
|
-
}
|
|
29921
|
-
try {
|
|
30457
|
+
if (options.file !== void 0) {
|
|
30458
|
+
const filePath = resolve17(runtime.cwd, options.file);
|
|
30459
|
+
content = readFileSync21(filePath, "utf8");
|
|
30460
|
+
} else if (!content) {
|
|
30461
|
+
content = scaffoldWorkflowDefinition(options.description || `Workflow ${workflowId}`);
|
|
30462
|
+
}
|
|
29922
30463
|
const document = typeof content === "string" ? content : (0, import_yaml12.stringify)(content);
|
|
29923
30464
|
let repoRoot = runtime.cwd;
|
|
29924
30465
|
if (scope === "local") {
|
|
@@ -29931,7 +30472,7 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
|
|
|
29931
30472
|
print(
|
|
29932
30473
|
runtime.io,
|
|
29933
30474
|
runtime.json,
|
|
29934
|
-
{ ok: false, command: "workflow add", error: "workflow_invalid", issues },
|
|
30475
|
+
{ ok: false, command: "workflow add", error: "workflow_invalid", message: "with this workflow the project would not load, so nothing was written", issues },
|
|
29935
30476
|
`workflow add failed: with this workflow the project would not load, so nothing was written
|
|
29936
30477
|
${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join("\n")}`
|
|
29937
30478
|
);
|
|
@@ -29959,9 +30500,7 @@ ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join
|
|
|
29959
30500
|
);
|
|
29960
30501
|
return 0;
|
|
29961
30502
|
} catch (err) {
|
|
29962
|
-
|
|
29963
|
-
`);
|
|
29964
|
-
return 1;
|
|
30503
|
+
return refuse("workflow_add_failed", err instanceof Error ? err.message : String(err), 1);
|
|
29965
30504
|
}
|
|
29966
30505
|
}
|
|
29967
30506
|
async function cmdWorkflowRemove(runtime, workflowId, options) {
|
|
@@ -30093,7 +30632,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
|
|
|
30093
30632
|
}
|
|
30094
30633
|
let payload;
|
|
30095
30634
|
try {
|
|
30096
|
-
const raw = payloadFlag.startsWith("@") ?
|
|
30635
|
+
const raw = payloadFlag.startsWith("@") ? readFileSync21(resolve17(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
|
|
30097
30636
|
const value = JSON.parse(raw);
|
|
30098
30637
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
|
|
30099
30638
|
payload = value;
|
|
@@ -30160,7 +30699,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
|
|
|
30160
30699
|
}
|
|
30161
30700
|
}
|
|
30162
30701
|
async function cmdWorkflowInspect(runtime, action, runId) {
|
|
30163
|
-
const dataPath =
|
|
30702
|
+
const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
|
|
30164
30703
|
if (action === "get" && !runId) {
|
|
30165
30704
|
runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
|
|
30166
30705
|
`);
|
|
@@ -30329,14 +30868,14 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
30329
30868
|
return 2;
|
|
30330
30869
|
}
|
|
30331
30870
|
const snapshotFlag = String(options.input || "");
|
|
30332
|
-
const snapshotPath = snapshotFlag ?
|
|
30871
|
+
const snapshotPath = snapshotFlag ? resolve17(runtime.cwd, snapshotFlag) : "";
|
|
30333
30872
|
let snapshot;
|
|
30334
30873
|
try {
|
|
30335
30874
|
if (snapshotPath) {
|
|
30336
|
-
if (!
|
|
30337
|
-
snapshot = JSON.parse(
|
|
30875
|
+
if (!existsSync22(snapshotPath)) throw new Error("snapshot_missing");
|
|
30876
|
+
snapshot = JSON.parse(readFileSync21(snapshotPath, "utf8"));
|
|
30338
30877
|
} else {
|
|
30339
|
-
const dataPath =
|
|
30878
|
+
const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
|
|
30340
30879
|
const local = localWorkflowSnapshot(dataPath, runId);
|
|
30341
30880
|
if (!local.runs[0]) throw new Error("workflow_not_found");
|
|
30342
30881
|
snapshot = { run: local.runs[0], journal: local.journal };
|
|
@@ -30348,8 +30887,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
30348
30887
|
return 1;
|
|
30349
30888
|
}
|
|
30350
30889
|
const doc = buildRetrospective(snapshot.run, snapshot.journal);
|
|
30351
|
-
const outDir =
|
|
30352
|
-
const assetsRoot =
|
|
30890
|
+
const outDir = resolve17(runtime.cwd, String(options.outDir || join24(runtime.dirs.assets, "retrospectives")));
|
|
30891
|
+
const assetsRoot = resolve17(runtime.dirs.assets);
|
|
30353
30892
|
const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
|
|
30354
30893
|
if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
|
|
30355
30894
|
print(runtime.io, runtime.json, { ok: false, command: "retrospective export", error: "output_outside_workspace_assets" }, "retrospectives must stay under the workspace assets directory");
|
|
@@ -30366,133 +30905,174 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
30366
30905
|
|
|
30367
30906
|
// plugins/kxm/src/cli/tasks.ts
|
|
30368
30907
|
var import_yaml15 = __toESM(require_dist(), 1);
|
|
30369
|
-
import { existsSync as
|
|
30370
|
-
import { basename as basename7, join as
|
|
30908
|
+
import { existsSync as existsSync28, readFileSync as readFileSync27, readdirSync as readdirSync10 } from "node:fs";
|
|
30909
|
+
import { basename as basename7, join as join32, resolve as resolve23 } from "node:path";
|
|
30371
30910
|
|
|
30372
30911
|
// plugins/kxm/src/suggest.ts
|
|
30373
30912
|
var WORKFLOW_PATTERNS = [
|
|
30374
30913
|
{
|
|
30375
|
-
id: "
|
|
30914
|
+
id: "bug-fix",
|
|
30915
|
+
template: "implement-and-verify",
|
|
30376
30916
|
area: "software-engineering",
|
|
30377
30917
|
keywords: ["fix", "bug", "flaky", "failure", "timeout", "error", "repro", "crash", "broken", "hang"],
|
|
30378
|
-
skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"]
|
|
30379
|
-
defaultCommand: (p) => `kxm run software-engineering/bug-fix "${p}"`
|
|
30918
|
+
skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"]
|
|
30380
30919
|
},
|
|
30381
30920
|
{
|
|
30382
|
-
id: "
|
|
30921
|
+
id: "feature-implementation",
|
|
30922
|
+
template: "implement-and-verify",
|
|
30383
30923
|
area: "software-engineering",
|
|
30384
30924
|
keywords: ["feature", "implement", "add", "build", "create", "develop", "support", "endpoint", "ui", "tui"],
|
|
30385
|
-
skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"]
|
|
30386
|
-
defaultCommand: (p) => `kxm run software-engineering/feature-implementation "${p}"`
|
|
30925
|
+
skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"]
|
|
30387
30926
|
},
|
|
30388
30927
|
{
|
|
30389
|
-
id: "
|
|
30928
|
+
id: "refactoring",
|
|
30929
|
+
template: "dual-critic-review",
|
|
30390
30930
|
area: "software-engineering",
|
|
30391
30931
|
keywords: ["refactor", "cleanup", "reorganize", "modularize", "deduplicate", "split", "simplify", "deprecate"],
|
|
30392
|
-
skills: ["kxm-workflow", "kxm-runs"]
|
|
30393
|
-
defaultCommand: (p) => `kxm run software-engineering/refactoring "${p}"`
|
|
30932
|
+
skills: ["kxm-workflow", "kxm-runs"]
|
|
30394
30933
|
},
|
|
30395
30934
|
{
|
|
30396
|
-
id: "
|
|
30935
|
+
id: "vulnerability-remediation",
|
|
30936
|
+
template: "dual-critic-review",
|
|
30397
30937
|
area: "security-reliability",
|
|
30398
30938
|
keywords: ["cve", "vulnerability", "security", "exploit", "sanitize", "leak", "secret", "injection", "redact", "auth"],
|
|
30399
|
-
skills: ["kxm-workflow", "kxm-definitions"]
|
|
30400
|
-
defaultCommand: (p) => `kxm run security-reliability/vulnerability-remediation "${p}"`
|
|
30939
|
+
skills: ["kxm-workflow", "kxm-definitions"]
|
|
30401
30940
|
},
|
|
30402
30941
|
{
|
|
30403
|
-
id: "
|
|
30942
|
+
id: "reliability-hardening",
|
|
30943
|
+
template: "dual-critic-review",
|
|
30404
30944
|
area: "security-reliability",
|
|
30405
30945
|
keywords: ["idempotency", "retry", "circuit-breaker", "cas", "lock", "concurrency", "deadlock", "race", "crash-recovery"],
|
|
30406
|
-
skills: ["kxm-workflow", "kxm-peer"]
|
|
30407
|
-
defaultCommand: (p) => `kxm run security-reliability/reliability-hardening "${p}"`
|
|
30946
|
+
skills: ["kxm-workflow", "kxm-peer"]
|
|
30408
30947
|
},
|
|
30409
30948
|
{
|
|
30410
|
-
id: "
|
|
30949
|
+
id: "pipeline-migration",
|
|
30950
|
+
template: "implement-and-verify",
|
|
30411
30951
|
area: "data-analytics",
|
|
30412
30952
|
keywords: ["database", "sqlite", "migration", "pipeline", "schema", "transform", "table", "wal", "foreign", "cascading"],
|
|
30413
|
-
skills: ["kxm-runs", "kxm-context-memory"]
|
|
30414
|
-
defaultCommand: (p) => `kxm run data-analytics/pipeline-migration "${p}"`
|
|
30953
|
+
skills: ["kxm-runs", "kxm-context-memory"]
|
|
30415
30954
|
},
|
|
30416
30955
|
{
|
|
30417
|
-
id: "
|
|
30956
|
+
id: "architecture-spike",
|
|
30957
|
+
template: "spec-and-plan",
|
|
30418
30958
|
area: "research-strategy",
|
|
30419
30959
|
keywords: ["spike", "investigate", "prototype", "research", "feasibility", "benchmark", "explore", "evaluate"],
|
|
30420
|
-
skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"]
|
|
30421
|
-
defaultCommand: (p) => `kxm run research-strategy/architecture-spike "${p}"`
|
|
30960
|
+
skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"]
|
|
30422
30961
|
}
|
|
30423
30962
|
];
|
|
30424
30963
|
function suggestWorkflowAndRoles(prompt, options = {}) {
|
|
30425
30964
|
const normalized = prompt.toLowerCase();
|
|
30426
30965
|
const words = normalized.split(/\W+/).filter(Boolean);
|
|
30427
|
-
|
|
30428
|
-
let
|
|
30429
|
-
|
|
30966
|
+
const claudeOnly = /\bclaude(?:\s+code)?(?:[\s-]+only|\s+exclusively)\b|\b(?:only|exclusively)(?:\s+(?:use|using|with|via))?\s+claude(?:\s+code)?\b/i.test(prompt);
|
|
30967
|
+
let bestPattern = WORKFLOW_PATTERNS[1];
|
|
30968
|
+
let bestScore = 0;
|
|
30969
|
+
const reasons = [];
|
|
30430
30970
|
for (const pattern of WORKFLOW_PATTERNS) {
|
|
30431
|
-
|
|
30432
|
-
|
|
30433
|
-
|
|
30434
|
-
if (words.includes(keyword) || normalized.includes(keyword)) {
|
|
30435
|
-
score += 1;
|
|
30436
|
-
matchedWords.push(keyword);
|
|
30437
|
-
}
|
|
30438
|
-
}
|
|
30439
|
-
if (score > bestScore) {
|
|
30440
|
-
bestScore = score;
|
|
30971
|
+
const matchedWords = pattern.keywords.filter((keyword) => words.includes(keyword) || normalized.includes(keyword));
|
|
30972
|
+
if (matchedWords.length > bestScore) {
|
|
30973
|
+
bestScore = matchedWords.length;
|
|
30441
30974
|
bestPattern = pattern;
|
|
30442
|
-
|
|
30443
|
-
|
|
30975
|
+
reasons.length = 0;
|
|
30976
|
+
reasons.push(`Matched keywords: ${matchedWords.join(", ")}`);
|
|
30444
30977
|
}
|
|
30445
30978
|
}
|
|
30446
|
-
if (bestScore
|
|
30447
|
-
|
|
30448
|
-
|
|
30449
|
-
|
|
30450
|
-
const
|
|
30451
|
-
(options.availableHarnesses ?? []).filter((h) => h.auth === "active" || h.auth === "ready" || h.auth === "configured").map((h) => h.harness.toLowerCase())
|
|
30452
|
-
);
|
|
30453
|
-
const hasClaude = activeHarnesses.size === 0 || activeHarnesses.has("claude");
|
|
30454
|
-
const hasGrok = activeHarnesses.size === 0 || activeHarnesses.has("grok");
|
|
30455
|
-
const hasCodex = activeHarnesses.size === 0 || activeHarnesses.has("codex");
|
|
30456
|
-
const planner = hasClaude ? { harness: "claude", model: "fable", role: "planner" } : { harness: "codex", model: "gpt-5.6-sol", role: "planner" };
|
|
30457
|
-
const writer = hasGrok ? { harness: "grok", model: "grok-4.6", role: "writer" } : activeHarnesses.has("agy") ? { harness: "agy", model: "gemini-2.5-pro", role: "writer" } : { harness: "pi", model: "qwen/qwen3-coder-plus", role: "writer" };
|
|
30458
|
-
const critics = [
|
|
30459
|
-
{ harness: "claude", model: "fable", role: "reviewer-arch" },
|
|
30460
|
-
{ harness: "codex", model: "gpt-5.6-sol", role: "reviewer-cli" }
|
|
30461
|
-
];
|
|
30462
|
-
return {
|
|
30979
|
+
if (bestScore === 0) reasons.push("Default fallback: general feature implementation workflow");
|
|
30980
|
+
if (claudeOnly) reasons.push("Explicit Claude-only constraint; other harnesses are excluded");
|
|
30981
|
+
const template = WORKFLOW_TEMPLATES[bestPattern.template];
|
|
30982
|
+
const steps = template.steps;
|
|
30983
|
+
const base = {
|
|
30463
30984
|
workflowId: bestPattern.id,
|
|
30985
|
+
template: bestPattern.template,
|
|
30464
30986
|
area: bestPattern.area,
|
|
30465
30987
|
confidence: bestScore > 0 ? Math.min(1, 0.5 + bestScore * 0.15) : 0.5,
|
|
30466
|
-
reasons
|
|
30988
|
+
reasons,
|
|
30467
30989
|
suggestedSkills: bestPattern.skills,
|
|
30468
|
-
|
|
30469
|
-
|
|
30470
|
-
|
|
30471
|
-
|
|
30472
|
-
|
|
30473
|
-
|
|
30474
|
-
|
|
30990
|
+
suggestedCommand: `kxm workflow add ${bestPattern.id} --template ${bestPattern.template}`
|
|
30991
|
+
};
|
|
30992
|
+
const writeStep = steps.find((step) => step.kind === "agent" && Object.values(step.repositories ?? {}).includes("write"));
|
|
30993
|
+
const available = (options.availableHarnesses ?? []).filter((entry) => BUILTIN_HARNESS_IDS.includes(entry.id) && entry.detected && entry.authenticated === true && entry.dispatch?.supported === true && entry.dispatch.status === "yes" && (!claudeOnly || entry.id === "claude"));
|
|
30994
|
+
const selected = available.find((entry) => entry.id === "claude") ?? available[0];
|
|
30995
|
+
if (!selected) {
|
|
30996
|
+
const relevant = (options.availableHarnesses ?? []).filter((entry) => !claudeOnly || entry.id === "claude");
|
|
30997
|
+
const detail = relevant.map((entry) => `${entry.id}: ${entry.dispatch?.reason ?? (!entry.detected ? "not_detected" : entry.authenticated !== true ? "authentication_unverified" : "dispatch_unavailable")}`).join("; ");
|
|
30998
|
+
return {
|
|
30999
|
+
...base,
|
|
31000
|
+
roles: [],
|
|
31001
|
+
execution: {
|
|
31002
|
+
supported: false,
|
|
31003
|
+
error: "harness_unavailable",
|
|
31004
|
+
reason: `${claudeOnly ? "Claude-only execution requires Claude Code" : "Execution requires a supported harness"} detected, authenticated, and ready for dispatch. ${detail || "No verified harness inventory is available."}`,
|
|
31005
|
+
nextSteps: claudeOnly ? ["Install Claude Code if missing, authenticate with claude auth login, then confirm claude auth status and kxm harness list. Other harnesses will not be substituted."] : ["Install and authenticate a supported harness, then run kxm harness list to check detection, authentication, and dispatch readiness. Pi requires a configured model/provider auth context."]
|
|
31006
|
+
}
|
|
31007
|
+
};
|
|
31008
|
+
}
|
|
31009
|
+
if (writeStep && !oneShotWriterArgs(selected.id)) {
|
|
31010
|
+
return {
|
|
31011
|
+
...base,
|
|
31012
|
+
roles: [],
|
|
31013
|
+
execution: {
|
|
31014
|
+
supported: false,
|
|
31015
|
+
error: "live_write_unsupported",
|
|
31016
|
+
reason: `${bestPattern.template} requires repository writes at step ${writeStep.id}, but the selected ${selected.id} harness has no audited live writer profile. No other harness will be substituted.`,
|
|
31017
|
+
nextSteps: [
|
|
31018
|
+
`Run the implementation directly in ${claudeOnly ? "Claude Code" : selected.id} in the target worktree, and run the repository's actual verification command.`,
|
|
31019
|
+
`For read-only KXM planning, try kxm suggest "Investigate an architecture spike${claudeOnly ? " using Claude only" : ""}"; the shipped spec-and-plan template does not implement changes.`,
|
|
31020
|
+
"Installing a definition or creating a run does not execute it. Simulation is not evidence of a bug fix or workflow completion."
|
|
31021
|
+
]
|
|
31022
|
+
}
|
|
31023
|
+
};
|
|
31024
|
+
}
|
|
31025
|
+
const roles = [];
|
|
31026
|
+
for (const step of steps) {
|
|
31027
|
+
if (!step.agent) continue;
|
|
31028
|
+
const role = roles.find((entry) => entry.agent === step.agent);
|
|
31029
|
+
if (role) role.role += `, ${step.id}`;
|
|
31030
|
+
else roles.push({ agent: step.agent, harness: selected.id, role: step.id });
|
|
31031
|
+
}
|
|
31032
|
+
const shell = process.platform === "win32" ? "powershell" : "posix";
|
|
31033
|
+
const quotedPrompt = shell === "powershell" ? `'${prompt.replace(/['\u2018-\u201b]/g, "$&$&")}'` : `'${prompt.replace(/'/g, "'\\''")}'`;
|
|
31034
|
+
return {
|
|
31035
|
+
...base,
|
|
31036
|
+
roles,
|
|
31037
|
+
execution: {
|
|
31038
|
+
supported: true,
|
|
31039
|
+
prerequisites: [
|
|
31040
|
+
"Run kxm init in the target repository if needed, then install the exact template with the suggested command. If that workflow ID already exists, stop and review it; the commands below apply only to a newly installed template, not an existing definition with potentially different agents or permissions.",
|
|
31041
|
+
...roles.map((role) => `Set harness: ${role.harness} and model: <your authenticated compatible model selector> in .kxm/agents/${role.agent}.yaml. Admit that exact selector in .kxm/routes.yaml and ensure it is not disabled; this recommendation does not choose a model or change routing.`),
|
|
31042
|
+
...writeStep ? [
|
|
31043
|
+
`Keep each writer step at assignments.maximum: 1 and set limits.maxConcurrentRuns: 1 in .kxm/project.yaml; the live ${selected.id} writer profile requires a lone writer in the checkout.`,
|
|
31044
|
+
`If .kxm/roster.yaml exists, it must be readable kxm.developer-roster.v1 with a lineup.writer route matching harness: ${selected.id} and the selected writer model (model or vendor/model), status: admitted, and permissions containing edit.`,
|
|
31045
|
+
"Configure the test gate in .kxm/gates.yaml with kind: command and argv for this repository's actual verification command. A scaffold/example command or simulation is not evidence that the implementation works."
|
|
31046
|
+
] : [],
|
|
31047
|
+
`Validate the installed workflow with kxm gate validate --file .kxm/workflows/${bestPattern.id}.yaml and the project configuration with kxm init --dry-run before creating a run.`
|
|
31048
|
+
],
|
|
31049
|
+
shell,
|
|
31050
|
+
createCommand: `kxm run ${bestPattern.id} -- ${quotedPrompt}`,
|
|
31051
|
+
driveCommand: "kxm runs drive <runId> --wait",
|
|
31052
|
+
statusCommand: "kxm runs status <runId>",
|
|
31053
|
+
receiptCommand: "kxm runs receipt <runId>"
|
|
31054
|
+
}
|
|
30475
31055
|
};
|
|
30476
31056
|
}
|
|
30477
31057
|
|
|
30478
31058
|
// plugins/kxm/src/task-manager.ts
|
|
30479
31059
|
var import_yaml13 = __toESM(require_dist(), 1);
|
|
30480
31060
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
30481
|
-
import { existsSync as
|
|
30482
|
-
import { join as
|
|
31061
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync22, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
|
|
31062
|
+
import { join as join25, resolve as resolve18 } from "node:path";
|
|
30483
31063
|
var GOAL_SCHEMA = "kxm.goal.v1";
|
|
30484
31064
|
var TASK_SCHEMA = "kxm.task.v1";
|
|
30485
31065
|
function goalsDirectory(repoRoot) {
|
|
30486
|
-
return
|
|
31066
|
+
return resolve18(repoRoot, ".kxm", "goals");
|
|
30487
31067
|
}
|
|
30488
31068
|
function tasksDirectory(repoRoot) {
|
|
30489
|
-
return
|
|
31069
|
+
return resolve18(repoRoot, ".kxm", "tasks");
|
|
30490
31070
|
}
|
|
30491
31071
|
function goalFilePath(repoRoot, goalId) {
|
|
30492
|
-
return
|
|
31072
|
+
return join25(goalsDirectory(repoRoot), `${goalId}.yaml`);
|
|
30493
31073
|
}
|
|
30494
31074
|
function taskFilePath(repoRoot, taskId) {
|
|
30495
|
-
return
|
|
31075
|
+
return join25(tasksDirectory(repoRoot), `${taskId}.yaml`);
|
|
30496
31076
|
}
|
|
30497
31077
|
function createGoal(repoRoot, input, options = {}) {
|
|
30498
31078
|
const id = `goal_${randomUUID8().replaceAll("-", "").slice(0, 12)}`;
|
|
@@ -30515,13 +31095,13 @@ function createGoal(repoRoot, input, options = {}) {
|
|
|
30515
31095
|
}
|
|
30516
31096
|
function listGoals(repoRoot) {
|
|
30517
31097
|
const dir = goalsDirectory(repoRoot);
|
|
30518
|
-
if (!
|
|
31098
|
+
if (!existsSync23(dir)) return [];
|
|
30519
31099
|
const goals = [];
|
|
30520
31100
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
30521
31101
|
for (const entry of entries) {
|
|
30522
31102
|
if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
|
|
30523
31103
|
try {
|
|
30524
|
-
const text =
|
|
31104
|
+
const text = readFileSync22(join25(dir, entry.name), "utf8");
|
|
30525
31105
|
const doc = (0, import_yaml13.parse)(text);
|
|
30526
31106
|
if (doc && doc.schema === GOAL_SCHEMA) goals.push(doc);
|
|
30527
31107
|
} catch {
|
|
@@ -30559,13 +31139,13 @@ function createTask(repoRoot, input, options = {}) {
|
|
|
30559
31139
|
}
|
|
30560
31140
|
function listTasks(repoRoot, options = {}) {
|
|
30561
31141
|
const dir = tasksDirectory(repoRoot);
|
|
30562
|
-
if (!
|
|
31142
|
+
if (!existsSync23(dir)) return [];
|
|
30563
31143
|
const tasks = [];
|
|
30564
31144
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
30565
31145
|
for (const entry of entries) {
|
|
30566
31146
|
if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
|
|
30567
31147
|
try {
|
|
30568
|
-
const text =
|
|
31148
|
+
const text = readFileSync22(join25(dir, entry.name), "utf8");
|
|
30569
31149
|
const doc = (0, import_yaml13.parse)(text);
|
|
30570
31150
|
if (doc && doc.schema === TASK_SCHEMA) {
|
|
30571
31151
|
if (options.goalId && doc.goalId !== options.goalId) continue;
|
|
@@ -30580,27 +31160,16 @@ function listTasks(repoRoot, options = {}) {
|
|
|
30580
31160
|
}
|
|
30581
31161
|
function getTask(repoRoot, taskId) {
|
|
30582
31162
|
const dir = tasksDirectory(repoRoot);
|
|
30583
|
-
const filePath =
|
|
30584
|
-
if (!
|
|
31163
|
+
const filePath = join25(dir, `${taskId}.yaml`);
|
|
31164
|
+
if (!existsSync23(filePath)) return void 0;
|
|
30585
31165
|
try {
|
|
30586
|
-
const text =
|
|
31166
|
+
const text = readFileSync22(filePath, "utf8");
|
|
30587
31167
|
const doc = (0, import_yaml13.parse)(text);
|
|
30588
31168
|
return doc && doc.schema === TASK_SCHEMA ? doc : void 0;
|
|
30589
31169
|
} catch {
|
|
30590
31170
|
return void 0;
|
|
30591
31171
|
}
|
|
30592
31172
|
}
|
|
30593
|
-
function updateTaskStatus(repoRoot, taskId, status, options = {}) {
|
|
30594
|
-
const task = getTask(repoRoot, taskId);
|
|
30595
|
-
if (!task) {
|
|
30596
|
-
throw new Error(`Task ${taskId} not found`);
|
|
30597
|
-
}
|
|
30598
|
-
task.status = status;
|
|
30599
|
-
if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
|
|
30600
|
-
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
30601
|
-
if (!options.dryRun) writeFileSync17(taskFilePath(repoRoot, taskId), (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
|
|
30602
|
-
return task;
|
|
30603
|
-
}
|
|
30604
31173
|
function syncTaskWithTracker(repoRoot, taskId, options = {}) {
|
|
30605
31174
|
const task = getTask(repoRoot, taskId);
|
|
30606
31175
|
if (!task) {
|
|
@@ -31380,13 +31949,13 @@ function createStudioServer(options = {}) {
|
|
|
31380
31949
|
}
|
|
31381
31950
|
|
|
31382
31951
|
// plugins/kxm/src/cli/project.ts
|
|
31383
|
-
import { existsSync as
|
|
31384
|
-
import { dirname as
|
|
31952
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
31953
|
+
import { dirname as dirname17, join as join31, resolve as resolve22 } from "node:path";
|
|
31385
31954
|
import { createInterface as createInterface2 } from "node:readline";
|
|
31386
31955
|
|
|
31387
31956
|
// plugins/kxm/src/hub-autostart.ts
|
|
31388
|
-
import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as
|
|
31389
|
-
import { join as
|
|
31957
|
+
import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as readFileSync23 } from "node:fs";
|
|
31958
|
+
import { join as join26, resolve as resolve19 } from "node:path";
|
|
31390
31959
|
function defaultProcessExists(pid) {
|
|
31391
31960
|
try {
|
|
31392
31961
|
process.kill(pid, 0);
|
|
@@ -31398,7 +31967,7 @@ function defaultProcessExists(pid) {
|
|
|
31398
31967
|
function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
|
|
31399
31968
|
let parsed;
|
|
31400
31969
|
try {
|
|
31401
|
-
parsed = JSON.parse(
|
|
31970
|
+
parsed = JSON.parse(readFileSync23(join26(stateDir, "hub.pid"), "utf8"));
|
|
31402
31971
|
} catch {
|
|
31403
31972
|
return void 0;
|
|
31404
31973
|
}
|
|
@@ -31416,7 +31985,7 @@ function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
|
|
|
31416
31985
|
// plugins/kxm/src/model-inventory.ts
|
|
31417
31986
|
var import_yaml14 = __toESM(require_dist(), 1);
|
|
31418
31987
|
import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync18 } from "node:fs";
|
|
31419
|
-
import { join as
|
|
31988
|
+
import { join as join27 } from "node:path";
|
|
31420
31989
|
var OR_URL = "https://openrouter.ai/api/v1/models";
|
|
31421
31990
|
var NOUS_URL = "https://inference-api.nousresearch.com/v1/models";
|
|
31422
31991
|
function price(value) {
|
|
@@ -31502,41 +32071,41 @@ async function refreshModelInventory(options) {
|
|
|
31502
32071
|
sources.nous = { url: nousUrl, ok: !nous.error, ...nous.error ? { error: nous.error } : {} };
|
|
31503
32072
|
for (const item of nous.data) add(item, "nous", "discount");
|
|
31504
32073
|
const inventory = { schema: "kxm.model-inventory.v1", fetchedAt, currency: "USD", sources, models: [...byId.values()].sort((a, b2) => a.id.localeCompare(b2.id)) };
|
|
31505
|
-
mkdirSync19(
|
|
31506
|
-
writeFileSync18(
|
|
32074
|
+
mkdirSync19(join27(options.outputRoot, ".kxm", "models"), { recursive: true });
|
|
32075
|
+
writeFileSync18(join27(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml14.stringify)(inventory), "utf8");
|
|
31507
32076
|
return inventory;
|
|
31508
32077
|
}
|
|
31509
32078
|
|
|
31510
32079
|
// plugins/kxm/src/init.ts
|
|
31511
32080
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
31512
|
-
import { existsSync as
|
|
31513
|
-
import { basename as basename6, join as
|
|
32081
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
32082
|
+
import { basename as basename6, join as join30 } from "node:path";
|
|
31514
32083
|
|
|
31515
32084
|
// plugins/kxm/src/repair.ts
|
|
31516
32085
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
31517
32086
|
import {
|
|
31518
32087
|
chmodSync as chmodSync5,
|
|
31519
32088
|
closeSync as closeSync3,
|
|
31520
|
-
existsSync as
|
|
32089
|
+
existsSync as existsSync25,
|
|
31521
32090
|
fsyncSync as fsyncSync2,
|
|
31522
32091
|
linkSync,
|
|
31523
32092
|
lstatSync as lstatSync7,
|
|
31524
32093
|
mkdirSync as mkdirSync21,
|
|
31525
32094
|
openSync as openSync3,
|
|
31526
|
-
readFileSync as
|
|
32095
|
+
readFileSync as readFileSync25,
|
|
31527
32096
|
readdirSync as readdirSync9,
|
|
31528
32097
|
renameSync as renameSync7,
|
|
31529
32098
|
rmSync as rmSync9,
|
|
31530
32099
|
writeFileSync as writeFileSync20
|
|
31531
32100
|
} from "node:fs";
|
|
31532
|
-
import { dirname as
|
|
32101
|
+
import { dirname as dirname16, isAbsolute as isAbsolute8, join as join29, relative as relative5, resolve as resolve21 } from "node:path";
|
|
31533
32102
|
|
|
31534
32103
|
// plugins/kxm/src/permission.ts
|
|
31535
32104
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
31536
|
-
import { createHash as
|
|
31537
|
-
import { existsSync as
|
|
32105
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
32106
|
+
import { existsSync as existsSync24, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
|
|
31538
32107
|
import { tmpdir } from "node:os";
|
|
31539
|
-
import { dirname as
|
|
32108
|
+
import { dirname as dirname15, join as join28, resolve as resolve20, sep as sep4 } from "node:path";
|
|
31540
32109
|
var PROSE_FIELDS = {
|
|
31541
32110
|
project: /* @__PURE__ */ new Set(["name", "description"]),
|
|
31542
32111
|
repository: /* @__PURE__ */ new Set(["description"]),
|
|
@@ -31552,7 +32121,7 @@ var UNTRACKED_RANK = { "tracked-only": 0, ask: 1, bounded: 2 };
|
|
|
31552
32121
|
function canonical(value) {
|
|
31553
32122
|
return kxmCanonicalJson(value === void 0 ? null : value);
|
|
31554
32123
|
}
|
|
31555
|
-
function
|
|
32124
|
+
function asObject2(value) {
|
|
31556
32125
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
31557
32126
|
}
|
|
31558
32127
|
function valuesOf3(value, key) {
|
|
@@ -31568,12 +32137,12 @@ function pointerEscape(segment) {
|
|
|
31568
32137
|
function secretGrantKeys(grants) {
|
|
31569
32138
|
const counts = /* @__PURE__ */ new Map();
|
|
31570
32139
|
for (const grant of grants) {
|
|
31571
|
-
const ref = stringValue3(
|
|
32140
|
+
const ref = stringValue3(asObject2(grant)?.ref);
|
|
31572
32141
|
if (ref) counts.set(ref, (counts.get(ref) ?? 0) + 1);
|
|
31573
32142
|
}
|
|
31574
32143
|
const keys = /* @__PURE__ */ new Map();
|
|
31575
32144
|
for (const [index, grant] of grants.entries()) {
|
|
31576
|
-
const ref = stringValue3(
|
|
32145
|
+
const ref = stringValue3(asObject2(grant)?.ref);
|
|
31577
32146
|
keys.set(index, ref && counts.get(ref) === 1 ? `by-ref/${pointerEscape(ref)}` : String(index));
|
|
31578
32147
|
}
|
|
31579
32148
|
return keys;
|
|
@@ -31593,21 +32162,21 @@ function kxmAuthorityEntries(resource) {
|
|
|
31593
32162
|
const declarations = valuesOf3(value, "repositories");
|
|
31594
32163
|
const declarationIds = /* @__PURE__ */ new Map();
|
|
31595
32164
|
for (const entry of declarations) {
|
|
31596
|
-
const id = stringValue3(
|
|
32165
|
+
const id = stringValue3(asObject2(entry)?.id);
|
|
31597
32166
|
if (id) declarationIds.set(id, (declarationIds.get(id) ?? 0) + 1);
|
|
31598
32167
|
}
|
|
31599
32168
|
for (const [index, repository] of declarations.entries()) {
|
|
31600
|
-
const entry =
|
|
32169
|
+
const entry = asObject2(repository);
|
|
31601
32170
|
if (!entry) continue;
|
|
31602
32171
|
const id = stringValue3(entry.id);
|
|
31603
32172
|
const key = id && declarationIds.get(id) === 1 ? `by-id/${pointerEscape(id)}` : String(index);
|
|
31604
32173
|
push(`/repositories/${key}`, "resource-shape", entry);
|
|
31605
32174
|
}
|
|
31606
|
-
const workspace =
|
|
32175
|
+
const workspace = asObject2(value.workspace);
|
|
31607
32176
|
if (workspace) push("/workspace", "snapshot-policy", workspace);
|
|
31608
|
-
const sync =
|
|
32177
|
+
const sync = asObject2(value.sync);
|
|
31609
32178
|
if (sync) push("/sync", "sync-policy", sync);
|
|
31610
|
-
const limits =
|
|
32179
|
+
const limits = asObject2(value.limits);
|
|
31611
32180
|
if (limits) push("/limits", "budget", limits);
|
|
31612
32181
|
break;
|
|
31613
32182
|
}
|
|
@@ -31625,7 +32194,7 @@ function kxmAuthorityEntries(resource) {
|
|
|
31625
32194
|
push("/harness", "harness", value.harness ?? "pi");
|
|
31626
32195
|
push("/tools", "tools", value.tools);
|
|
31627
32196
|
push("/defaultRepositoryAccess", "repository-access", value.defaultRepositoryAccess);
|
|
31628
|
-
const repositories =
|
|
32197
|
+
const repositories = asObject2(value.repositories);
|
|
31629
32198
|
for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
|
|
31630
32199
|
push(`/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
|
|
31631
32200
|
}
|
|
@@ -31635,7 +32204,7 @@ function kxmAuthorityEntries(resource) {
|
|
|
31635
32204
|
}
|
|
31636
32205
|
push("/network", "network", value.network);
|
|
31637
32206
|
push("/resultSchema", "delivery", value.resultSchema);
|
|
31638
|
-
const session =
|
|
32207
|
+
const session = asObject2(value.session);
|
|
31639
32208
|
if (session) push("/session", "resource-shape", session);
|
|
31640
32209
|
break;
|
|
31641
32210
|
}
|
|
@@ -31647,15 +32216,15 @@ function kxmAuthorityEntries(resource) {
|
|
|
31647
32216
|
push("/capabilities", "model", value.capabilities);
|
|
31648
32217
|
push("/priority", "model", value.priority);
|
|
31649
32218
|
push("/fallbacks", "model", value.fallbacks);
|
|
31650
|
-
const limits =
|
|
32219
|
+
const limits = asObject2(value.limits);
|
|
31651
32220
|
if (limits) push("/limits", "budget", limits);
|
|
31652
32221
|
break;
|
|
31653
32222
|
}
|
|
31654
32223
|
case "environment": {
|
|
31655
32224
|
push("/path", "environment", value.path);
|
|
31656
|
-
const envValues =
|
|
32225
|
+
const envValues = asObject2(value.values);
|
|
31657
32226
|
for (const key of Object.keys(envValues ?? {}).sort()) {
|
|
31658
|
-
const entry =
|
|
32227
|
+
const entry = asObject2(envValues?.[key]);
|
|
31659
32228
|
const projected = entry ? { ...entry, value: entry.value === void 0 ? void 0 : "<redacted>" } : envValues?.[key];
|
|
31660
32229
|
push(`/values/${pointerEscape(key)}`, "environment", projected);
|
|
31661
32230
|
}
|
|
@@ -31667,9 +32236,9 @@ function kxmAuthorityEntries(resource) {
|
|
|
31667
32236
|
}
|
|
31668
32237
|
case "gate-registry": {
|
|
31669
32238
|
push("/schema", "resource-shape", value.schema);
|
|
31670
|
-
const gates =
|
|
32239
|
+
const gates = asObject2(value.gates) ?? {};
|
|
31671
32240
|
for (const id of Object.keys(gates).sort()) {
|
|
31672
|
-
const definition =
|
|
32241
|
+
const definition = asObject2(gates[id]);
|
|
31673
32242
|
const path4 = `/gates/${pointerEscape(id)}`;
|
|
31674
32243
|
if (definition?.kind === "command") {
|
|
31675
32244
|
const { timeoutMs, ...authority } = definition;
|
|
@@ -31681,13 +32250,13 @@ function kxmAuthorityEntries(resource) {
|
|
|
31681
32250
|
}
|
|
31682
32251
|
case "workflow": {
|
|
31683
32252
|
push("/coordinator", "resource-shape", value.coordinator);
|
|
31684
|
-
const limits =
|
|
32253
|
+
const limits = asObject2(value.limits);
|
|
31685
32254
|
if (limits) push("/limits", "budget", limits);
|
|
31686
32255
|
if (value.reproOracle !== void 0) push("/reproOracle", "evidence-quorum", value.reproOracle);
|
|
31687
32256
|
if (value.planHash !== void 0) push("/planHash", "evidence-quorum", value.planHash);
|
|
31688
32257
|
if (value.requirePlanHash !== void 0) push("/requirePlanHash", "evidence-quorum", value.requirePlanHash);
|
|
31689
32258
|
for (const [index, stepEntry] of valuesOf3(value, "steps").entries()) {
|
|
31690
|
-
const step =
|
|
32259
|
+
const step = asObject2(stepEntry);
|
|
31691
32260
|
if (!step) continue;
|
|
31692
32261
|
const stepPath = `/steps/${index}`;
|
|
31693
32262
|
push(`${stepPath}/id`, "resource-shape", step.id);
|
|
@@ -31699,7 +32268,7 @@ function kxmAuthorityEntries(resource) {
|
|
|
31699
32268
|
push(`${stepPath}/model`, "model", step.model);
|
|
31700
32269
|
push(`${stepPath}/maxAttempts`, "budget", step.maxAttempts);
|
|
31701
32270
|
push(`${stepPath}/timeoutMs`, "budget", step.timeoutMs);
|
|
31702
|
-
const repositories =
|
|
32271
|
+
const repositories = asObject2(step.repositories);
|
|
31703
32272
|
for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
|
|
31704
32273
|
push(`${stepPath}/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
|
|
31705
32274
|
}
|
|
@@ -31729,7 +32298,7 @@ function kxmProseEntries(resource) {
|
|
|
31729
32298
|
}
|
|
31730
32299
|
if (resource.kind === "workflow") {
|
|
31731
32300
|
for (const [index, stepEntry] of valuesOf3(resource.value, "steps").entries()) {
|
|
31732
|
-
const step =
|
|
32301
|
+
const step = asObject2(stepEntry);
|
|
31733
32302
|
if (!step) continue;
|
|
31734
32303
|
for (const field of ["description", "instructions"]) {
|
|
31735
32304
|
if (step[field] !== void 0) entries.set(`/steps/${index}/${field}`, canonical(step[field]));
|
|
@@ -31739,7 +32308,7 @@ function kxmProseEntries(resource) {
|
|
|
31739
32308
|
return entries;
|
|
31740
32309
|
}
|
|
31741
32310
|
function valueHash(canonicalValue) {
|
|
31742
|
-
return `sha256:${
|
|
32311
|
+
return `sha256:${createHash15("sha256").update(canonicalValue, "utf8").digest("hex")}`;
|
|
31743
32312
|
}
|
|
31744
32313
|
function rankDirection(rank, baseValue, candidateValue) {
|
|
31745
32314
|
const base = JSON.parse(baseValue);
|
|
@@ -31760,8 +32329,8 @@ function numericBoundDirection(baseValue, candidateValue) {
|
|
|
31760
32329
|
return "neutral";
|
|
31761
32330
|
}
|
|
31762
32331
|
function budgetDirection(baseValue, candidateValue) {
|
|
31763
|
-
const base =
|
|
31764
|
-
const candidate =
|
|
32332
|
+
const base = asObject2(JSON.parse(baseValue));
|
|
32333
|
+
const candidate = asObject2(JSON.parse(candidateValue));
|
|
31765
32334
|
if (!base || !candidate) return "expansion";
|
|
31766
32335
|
let sawExpansion = false;
|
|
31767
32336
|
let sawNarrowing = false;
|
|
@@ -31774,8 +32343,8 @@ function budgetDirection(baseValue, candidateValue) {
|
|
|
31774
32343
|
return sawNarrowing ? "narrowing" : "neutral";
|
|
31775
32344
|
}
|
|
31776
32345
|
function quorumDirection(baseValue, candidateValue) {
|
|
31777
|
-
const base =
|
|
31778
|
-
const candidate =
|
|
32346
|
+
const base = asObject2(JSON.parse(baseValue));
|
|
32347
|
+
const candidate = asObject2(JSON.parse(candidateValue));
|
|
31779
32348
|
if (!base || !candidate) return "expansion";
|
|
31780
32349
|
if (base.strategy !== void 0 || candidate.strategy !== void 0) return "expansion";
|
|
31781
32350
|
if (base.stageId !== void 0 || candidate.stageId !== void 0) return "expansion";
|
|
@@ -31789,13 +32358,13 @@ function quorumDirection(baseValue, candidateValue) {
|
|
|
31789
32358
|
const CLASSIFIED = /* @__PURE__ */ new Set(["key", "kind", "minimum", "reusableAcrossAttempts", "strategy", "stageId", "evidenceKey"]);
|
|
31790
32359
|
const CLASSIFIED_POLICY = /* @__PURE__ */ new Set(["minimumProducers", "eligibleAgents", "degradation"]);
|
|
31791
32360
|
const residualOf = (value) => {
|
|
31792
|
-
const parsed =
|
|
32361
|
+
const parsed = asObject2(JSON.parse(value));
|
|
31793
32362
|
if (!parsed) return null;
|
|
31794
32363
|
const residual = {};
|
|
31795
32364
|
for (const [key, entry] of Object.entries(parsed)) {
|
|
31796
32365
|
if (CLASSIFIED.has(key)) continue;
|
|
31797
32366
|
if (key === "producerPolicy") {
|
|
31798
|
-
const policy =
|
|
32367
|
+
const policy = asObject2(entry);
|
|
31799
32368
|
if (!policy) {
|
|
31800
32369
|
residual[key] = entry;
|
|
31801
32370
|
continue;
|
|
@@ -31808,8 +32377,8 @@ function quorumDirection(baseValue, candidateValue) {
|
|
|
31808
32377
|
return residual;
|
|
31809
32378
|
};
|
|
31810
32379
|
if (kxmCanonicalJson(residualOf(baseValue)) !== kxmCanonicalJson(residualOf(candidateValue))) return "expansion";
|
|
31811
|
-
const basePolicy =
|
|
31812
|
-
const candidatePolicy =
|
|
32380
|
+
const basePolicy = asObject2(base.producerPolicy);
|
|
32381
|
+
const candidatePolicy = asObject2(candidate.producerPolicy);
|
|
31813
32382
|
if (!basePolicy && !candidatePolicy) return sawNarrowing ? "narrowing" : "neutral";
|
|
31814
32383
|
if (!basePolicy || !candidatePolicy) return "expansion";
|
|
31815
32384
|
const basePolicyMinimum = typeof basePolicy.minimumProducers === "number" ? basePolicy.minimumProducers : 1;
|
|
@@ -31818,8 +32387,8 @@ function quorumDirection(baseValue, candidateValue) {
|
|
|
31818
32387
|
const baseEligible = new Set((Array.isArray(basePolicy.eligibleAgents) ? basePolicy.eligibleAgents : []).map(String));
|
|
31819
32388
|
const candidateEligible = new Set((Array.isArray(candidatePolicy.eligibleAgents) ? candidatePolicy.eligibleAgents : []).map(String));
|
|
31820
32389
|
for (const agent of baseEligible) if (!candidateEligible.has(agent)) return "expansion";
|
|
31821
|
-
const baseDegradation =
|
|
31822
|
-
const candidateDegradation =
|
|
32390
|
+
const baseDegradation = asObject2(basePolicy.degradation);
|
|
32391
|
+
const candidateDegradation = asObject2(candidatePolicy.degradation);
|
|
31823
32392
|
if (!baseDegradation && candidateDegradation) return "expansion";
|
|
31824
32393
|
if (baseDegradation && candidateDegradation) {
|
|
31825
32394
|
const baseFloor = typeof baseDegradation.minimumProducers === "number" ? baseDegradation.minimumProducers : 1;
|
|
@@ -31833,11 +32402,11 @@ function quorumDirection(baseValue, candidateValue) {
|
|
|
31833
32402
|
return sawNarrowing ? "narrowing" : "neutral";
|
|
31834
32403
|
}
|
|
31835
32404
|
function snapshotDirection(baseValue, candidateValue) {
|
|
31836
|
-
const base =
|
|
31837
|
-
const candidate =
|
|
32405
|
+
const base = asObject2(JSON.parse(baseValue));
|
|
32406
|
+
const candidate = asObject2(JSON.parse(candidateValue));
|
|
31838
32407
|
if (!base || !candidate) return "expansion";
|
|
31839
|
-
const baseSnapshot =
|
|
31840
|
-
const candidateSnapshot =
|
|
32408
|
+
const baseSnapshot = asObject2(base.dirtySnapshot);
|
|
32409
|
+
const candidateSnapshot = asObject2(candidate.dirtySnapshot);
|
|
31841
32410
|
if (!baseSnapshot || !candidateSnapshot) return "expansion";
|
|
31842
32411
|
const untracked = rankDirection(UNTRACKED_RANK, canonical(baseSnapshot.untracked), canonical(candidateSnapshot.untracked));
|
|
31843
32412
|
if (untracked === "expansion") return "expansion";
|
|
@@ -31891,8 +32460,8 @@ function classifyChange(field, baseValue, candidateValue, context) {
|
|
|
31891
32460
|
case "snapshot-policy":
|
|
31892
32461
|
return snapshotDirection(baseValue, candidateValue);
|
|
31893
32462
|
case "secrets": {
|
|
31894
|
-
const base =
|
|
31895
|
-
const candidate =
|
|
32463
|
+
const base = asObject2(JSON.parse(baseValue));
|
|
32464
|
+
const candidate = asObject2(JSON.parse(candidateValue));
|
|
31896
32465
|
if (base && candidate && base.ref === candidate.ref) {
|
|
31897
32466
|
const { required: _b, ...baseRest } = base;
|
|
31898
32467
|
const { required: _c, ...candidateRest } = candidate;
|
|
@@ -32057,9 +32626,9 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32057
32626
|
const tree = resolveTreeRevision(root, revision);
|
|
32058
32627
|
const listing = gitBuffer(root, ["ls-tree", "-r", "-z", "--name-only", tree]).toString("utf8");
|
|
32059
32628
|
const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
32060
|
-
const shadow = mkdtempSync(
|
|
32061
|
-
const shadowResolved =
|
|
32062
|
-
const shadowMembersDir = `.kxm-shadow-members-${
|
|
32629
|
+
const shadow = mkdtempSync(join28(tmpdir(), "kxm-permission-base-"));
|
|
32630
|
+
const shadowResolved = resolve20(shadow);
|
|
32631
|
+
const shadowMembersDir = `.kxm-shadow-members-${createHash15("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
|
|
32063
32632
|
try {
|
|
32064
32633
|
for (const path4 of paths) {
|
|
32065
32634
|
const segments = path4.split("/");
|
|
@@ -32071,8 +32640,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32071
32640
|
message: "Git tree entry contains a traversal or non-portable segment"
|
|
32072
32641
|
}]);
|
|
32073
32642
|
}
|
|
32074
|
-
const absolute =
|
|
32075
|
-
const resolved =
|
|
32643
|
+
const absolute = join28(shadowResolved, ...segments);
|
|
32644
|
+
const resolved = resolve20(absolute);
|
|
32076
32645
|
if (!resolved.startsWith(`${shadowResolved}${sep4}`)) {
|
|
32077
32646
|
throw new KxmConfigError([{
|
|
32078
32647
|
phase: "path",
|
|
@@ -32082,11 +32651,11 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32082
32651
|
}]);
|
|
32083
32652
|
}
|
|
32084
32653
|
const content = gitBuffer(root, ["cat-file", "blob", `${tree}:${path4}`]);
|
|
32085
|
-
mkdirSync20(
|
|
32654
|
+
mkdirSync20(dirname15(absolute), { recursive: true });
|
|
32086
32655
|
writeFileSync19(absolute, content);
|
|
32087
32656
|
}
|
|
32088
32657
|
initShadowGitRoot(shadowResolved);
|
|
32089
|
-
const baseProjectFile =
|
|
32658
|
+
const baseProjectFile = join28(shadowResolved, ".kxm", "project.yaml");
|
|
32090
32659
|
let baseProject;
|
|
32091
32660
|
try {
|
|
32092
32661
|
baseProject = loadBaseProjectDeclarations(baseProjectFile);
|
|
@@ -32116,8 +32685,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32116
32685
|
}]);
|
|
32117
32686
|
}
|
|
32118
32687
|
const boundPath = options.repositoryBindings?.[member.repositoryId];
|
|
32119
|
-
const memberWorktree = boundPath ?
|
|
32120
|
-
if (!memberWorktree || !
|
|
32688
|
+
const memberWorktree = boundPath ? resolve20(boundPath) : member.pathHint ? resolve20(root, ...member.pathHint.split("/")) : void 0;
|
|
32689
|
+
if (!memberWorktree || !existsSync24(memberWorktree)) {
|
|
32121
32690
|
if (member.required === false) continue;
|
|
32122
32691
|
throw new KxmConfigError([{
|
|
32123
32692
|
phase: "discovery",
|
|
@@ -32164,8 +32733,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32164
32733
|
const memberTree = git(memberWorktree, ["rev-parse", "--verify", "--quiet", `${pinnedCommit}^{tree}`]).trim();
|
|
32165
32734
|
const memberListing = gitBuffer(memberWorktree, ["ls-tree", "-r", "-z", "--name-only", memberTree]).toString("utf8");
|
|
32166
32735
|
const memberPaths = memberListing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
32167
|
-
const memberShadowRoot =
|
|
32168
|
-
if (!
|
|
32736
|
+
const memberShadowRoot = join28(shadowResolved, shadowMembersDir, member.repositoryId);
|
|
32737
|
+
if (!resolve20(memberShadowRoot).startsWith(`${resolve20(shadowResolved, shadowMembersDir)}${sep4}`)) {
|
|
32169
32738
|
throw new KxmConfigError([{
|
|
32170
32739
|
phase: "path",
|
|
32171
32740
|
code: "git_tree_path_invalid",
|
|
@@ -32178,13 +32747,13 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
|
|
|
32178
32747
|
if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || /[\\:]/.test(segment))) {
|
|
32179
32748
|
throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry contains a traversal or non-portable segment" }]);
|
|
32180
32749
|
}
|
|
32181
|
-
const absolute =
|
|
32182
|
-
const resolved =
|
|
32183
|
-
if (!resolved.startsWith(`${
|
|
32750
|
+
const absolute = join28(memberShadowRoot, ...segments);
|
|
32751
|
+
const resolved = resolve20(absolute);
|
|
32752
|
+
if (!resolved.startsWith(`${resolve20(memberShadowRoot)}${sep4}`)) {
|
|
32184
32753
|
throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry escapes the shadow root" }]);
|
|
32185
32754
|
}
|
|
32186
32755
|
const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
|
|
32187
|
-
mkdirSync20(
|
|
32756
|
+
mkdirSync20(dirname15(absolute), { recursive: true });
|
|
32188
32757
|
writeFileSync19(absolute, content);
|
|
32189
32758
|
}
|
|
32190
32759
|
initShadowGitRoot(memberShadowRoot);
|
|
@@ -32206,8 +32775,8 @@ function portableMemberPathHint(pathHint) {
|
|
|
32206
32775
|
return pathHint !== "." && kxmPortablePath(pathHint);
|
|
32207
32776
|
}
|
|
32208
32777
|
function loadBaseProjectDeclarations(projectFile) {
|
|
32209
|
-
if (!
|
|
32210
|
-
const value = parseRestrictedYaml2(
|
|
32778
|
+
if (!existsSync24(projectFile)) return [];
|
|
32779
|
+
const value = parseRestrictedYaml2(readFileSync24(projectFile, "utf8"), ".kxm/project.yaml");
|
|
32211
32780
|
const repositories = Array.isArray(value?.repositories) ? value.repositories : [];
|
|
32212
32781
|
const members = [];
|
|
32213
32782
|
for (const entry of repositories) {
|
|
@@ -32310,7 +32879,7 @@ function writeDurableNew(file, bytes, mode) {
|
|
|
32310
32879
|
} finally {
|
|
32311
32880
|
if (descriptor !== void 0) closeSync3(descriptor);
|
|
32312
32881
|
}
|
|
32313
|
-
syncDirectory2(
|
|
32882
|
+
syncDirectory2(dirname16(file));
|
|
32314
32883
|
}
|
|
32315
32884
|
function writeDurable(file, bytes, mode) {
|
|
32316
32885
|
let descriptor;
|
|
@@ -32327,27 +32896,27 @@ function writeDurable(file, bytes, mode) {
|
|
|
32327
32896
|
} finally {
|
|
32328
32897
|
if (descriptor !== void 0) closeSync3(descriptor);
|
|
32329
32898
|
}
|
|
32330
|
-
syncDirectory2(
|
|
32899
|
+
syncDirectory2(dirname16(file));
|
|
32331
32900
|
}
|
|
32332
32901
|
function sameHostPath3(left, right) {
|
|
32333
|
-
const first =
|
|
32334
|
-
const second =
|
|
32902
|
+
const first = resolve21(left);
|
|
32903
|
+
const second = resolve21(right);
|
|
32335
32904
|
return process.platform === "win32" ? first.toLocaleLowerCase("en-US") === second.toLocaleLowerCase("en-US") : first === second;
|
|
32336
32905
|
}
|
|
32337
32906
|
function transactionRoot(projectRoot) {
|
|
32338
|
-
return
|
|
32907
|
+
return join29(resolve21(projectRoot), TRANSACTION_NAME);
|
|
32339
32908
|
}
|
|
32340
32909
|
function operationFile(projectRoot) {
|
|
32341
|
-
return
|
|
32910
|
+
return join29(transactionRoot(projectRoot), OPERATION_FILE);
|
|
32342
32911
|
}
|
|
32343
32912
|
function targetFile(projectRoot, portablePath2) {
|
|
32344
|
-
return
|
|
32913
|
+
return join29(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
|
|
32345
32914
|
}
|
|
32346
32915
|
function shadowRoot(projectRoot) {
|
|
32347
|
-
return
|
|
32916
|
+
return join29(transactionRoot(projectRoot), "shadow");
|
|
32348
32917
|
}
|
|
32349
32918
|
function backupFile(projectRoot, portablePath2) {
|
|
32350
|
-
return
|
|
32919
|
+
return join29(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
|
|
32351
32920
|
}
|
|
32352
32921
|
function portableManagedPath(path4) {
|
|
32353
32922
|
return path4.startsWith(".kxm/") && path4 !== KXM_TEMPLATE_PROVENANCE_PATH && path4.length <= 1024 && !path4.includes("\\") && !isAbsolute8(path4) && PORTABLE_PATH.test(path4) && !/[<>:"|?*]/.test(path4);
|
|
@@ -32356,24 +32925,24 @@ function readRegularBounded(file, label) {
|
|
|
32356
32925
|
const stat = lstatSync7(file);
|
|
32357
32926
|
if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_file_invalid", label, "managed file must be a regular file, not a link or directory", "path");
|
|
32358
32927
|
if (stat.size > MAX_MANAGED_FILE_BYTES) fail3("repair_file_too_large", label, `managed file exceeds ${MAX_MANAGED_FILE_BYTES} bytes`, "parse");
|
|
32359
|
-
return
|
|
32928
|
+
return readFileSync25(file);
|
|
32360
32929
|
}
|
|
32361
32930
|
function readOptionalManaged(projectRoot, path4) {
|
|
32362
|
-
const file =
|
|
32363
|
-
if (!
|
|
32364
|
-
let parent =
|
|
32365
|
-
const boundary =
|
|
32931
|
+
const file = join29(projectRoot, ...path4.split("/"));
|
|
32932
|
+
if (!existsSync25(file)) return void 0;
|
|
32933
|
+
let parent = dirname16(file);
|
|
32934
|
+
const boundary = resolve21(projectRoot);
|
|
32366
32935
|
while (parent !== boundary) {
|
|
32367
32936
|
const stat = lstatSync7(parent);
|
|
32368
32937
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_parent_invalid", path4, "managed file parents must be regular directories", "path");
|
|
32369
|
-
parent =
|
|
32938
|
+
parent = dirname16(parent);
|
|
32370
32939
|
}
|
|
32371
32940
|
return readRegularBounded(file, path4);
|
|
32372
32941
|
}
|
|
32373
32942
|
function readProjectAndProvenance(projectRoot, schemasDir) {
|
|
32374
|
-
const projectFile =
|
|
32375
|
-
const provenanceFile =
|
|
32376
|
-
if (!
|
|
32943
|
+
const projectFile = join29(projectRoot, ".kxm", "project.yaml");
|
|
32944
|
+
const provenanceFile = join29(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
|
|
32945
|
+
if (!existsSync25(projectFile) || !existsSync25(provenanceFile)) return void 0;
|
|
32377
32946
|
const projectBytes = readOptionalManaged(projectRoot, ".kxm/project.yaml");
|
|
32378
32947
|
const provenanceBytes = readOptionalManaged(projectRoot, KXM_TEMPLATE_PROVENANCE_PATH);
|
|
32379
32948
|
if (!projectBytes || !provenanceBytes) return void 0;
|
|
@@ -32409,7 +32978,7 @@ function readProjectAndProvenance(projectRoot, schemasDir) {
|
|
|
32409
32978
|
}
|
|
32410
32979
|
function planKxmTemplateRepair(projectRoot, options = {}) {
|
|
32411
32980
|
assertNoRegisteredGates(options);
|
|
32412
|
-
const root =
|
|
32981
|
+
const root = resolve21(projectRoot);
|
|
32413
32982
|
const source = readProjectAndProvenance(root, options.schemasDir);
|
|
32414
32983
|
if (!source) return void 0;
|
|
32415
32984
|
const target = renderKxmTemplate(
|
|
@@ -32537,7 +33106,7 @@ function operationPlanSha(operation) {
|
|
|
32537
33106
|
function writeOperation(projectRoot, operation) {
|
|
32538
33107
|
const root = transactionRoot(projectRoot);
|
|
32539
33108
|
const file = operationFile(projectRoot);
|
|
32540
|
-
const temporary =
|
|
33109
|
+
const temporary = join29(root, `.operation-${operation.operationId}-${randomUUID10()}.tmp`);
|
|
32541
33110
|
try {
|
|
32542
33111
|
writeDurableNew(temporary, Buffer.from(`${JSON.stringify(operation, null, 2)}
|
|
32543
33112
|
`, "utf8"), 384);
|
|
@@ -32554,11 +33123,11 @@ function updatePhase(projectRoot, operation, phase) {
|
|
|
32554
33123
|
}
|
|
32555
33124
|
function readOperation(projectRoot, schemasDir) {
|
|
32556
33125
|
const root = transactionRoot(projectRoot);
|
|
32557
|
-
if (!
|
|
33126
|
+
if (!existsSync25(root)) return void 0;
|
|
32558
33127
|
const stat = lstatSync7(root);
|
|
32559
33128
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("init_transaction_invalid", TRANSACTION_NAME, "initialization transaction must be a regular directory", "path");
|
|
32560
33129
|
const file = operationFile(projectRoot);
|
|
32561
|
-
if (!
|
|
33130
|
+
if (!existsSync25(file)) {
|
|
32562
33131
|
const entries = readdirSync9(root, { withFileTypes: true });
|
|
32563
33132
|
const recoverable = entries.every((entry) => entry.isFile() && /^\.operation-op_[a-f0-9]{32}-[a-f0-9-]{36}\.tmp$/.test(entry.name));
|
|
32564
33133
|
if (recoverable) return void 0;
|
|
@@ -32586,15 +33155,15 @@ function readOperation(projectRoot, schemasDir) {
|
|
|
32586
33155
|
return operation;
|
|
32587
33156
|
}
|
|
32588
33157
|
function hasKxmInitTransaction(projectRoot) {
|
|
32589
|
-
return
|
|
33158
|
+
return existsSync25(transactionRoot(projectRoot));
|
|
32590
33159
|
}
|
|
32591
33160
|
function readTarget(projectRoot, path4) {
|
|
32592
|
-
return readOptionalManaged(
|
|
33161
|
+
return readOptionalManaged(join29(transactionRoot(projectRoot), "targets"), path4);
|
|
32593
33162
|
}
|
|
32594
33163
|
function writeTarget(projectRoot, path4, bytes) {
|
|
32595
33164
|
if (bytes.byteLength > MAX_MANAGED_FILE_BYTES) fail3("template_target_too_large", path4, `target exceeds ${MAX_MANAGED_FILE_BYTES} bytes`);
|
|
32596
33165
|
const file = targetFile(projectRoot, path4);
|
|
32597
|
-
mkdirSync21(
|
|
33166
|
+
mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
|
|
32598
33167
|
writeDurableNew(file, bytes, 384);
|
|
32599
33168
|
}
|
|
32600
33169
|
function writeAndVerifyBackups(projectRoot, operation) {
|
|
@@ -32606,7 +33175,7 @@ function writeAndVerifyBackups(projectRoot, operation) {
|
|
|
32606
33175
|
fail3("repair_preimage_changed", entry.path, "destination changed before transaction preparation; local bytes were preserved");
|
|
32607
33176
|
}
|
|
32608
33177
|
const file = backupFile(projectRoot, entry.path);
|
|
32609
|
-
mkdirSync21(
|
|
33178
|
+
mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
|
|
32610
33179
|
writeDurableNew(file, current, 384);
|
|
32611
33180
|
}
|
|
32612
33181
|
}
|
|
@@ -32621,7 +33190,7 @@ function writeKnownBackups(projectRoot, operation) {
|
|
|
32621
33190
|
fail3("init_transaction_backup_source_invalid", entry.path, "supported source template cannot reproduce the pinned preimage");
|
|
32622
33191
|
}
|
|
32623
33192
|
const file = backupFile(projectRoot, entry.path);
|
|
32624
|
-
mkdirSync21(
|
|
33193
|
+
mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
|
|
32625
33194
|
writeDurableNew(file, bytes, 384);
|
|
32626
33195
|
}
|
|
32627
33196
|
}
|
|
@@ -32629,7 +33198,7 @@ function verifyBackups(projectRoot, operation) {
|
|
|
32629
33198
|
if (operation.kind !== "repair") return;
|
|
32630
33199
|
for (const entry of operation.files) {
|
|
32631
33200
|
if (entry.observedSha256 === null) continue;
|
|
32632
|
-
const bytes = readOptionalManaged(
|
|
33201
|
+
const bytes = readOptionalManaged(join29(transactionRoot(projectRoot), "backups"), entry.path);
|
|
32633
33202
|
if (!bytes || kxmContentSha256(bytes) !== entry.observedSha256) {
|
|
32634
33203
|
fail3("init_transaction_backup_invalid", entry.path, "pinned preimage backup is missing or corrupt");
|
|
32635
33204
|
}
|
|
@@ -32663,10 +33232,10 @@ function sourceConfigFiles(projectRoot) {
|
|
|
32663
33232
|
".kxm/repo/repo.yaml",
|
|
32664
33233
|
".kxm/repo/env.yaml"
|
|
32665
33234
|
];
|
|
32666
|
-
const files = fixed.filter((path4) =>
|
|
33235
|
+
const files = fixed.filter((path4) => existsSync25(join29(projectRoot, ...path4.split("/"))));
|
|
32667
33236
|
for (const directory of ["agents", "models", "workflows"]) {
|
|
32668
|
-
const absolute =
|
|
32669
|
-
if (!
|
|
33237
|
+
const absolute = join29(projectRoot, ".kxm", directory);
|
|
33238
|
+
if (!existsSync25(absolute)) continue;
|
|
32670
33239
|
const stat = lstatSync7(absolute);
|
|
32671
33240
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_directory_invalid", `.kxm/${directory}`, "configuration directory must be regular", "path");
|
|
32672
33241
|
for (const entry of readdirSync9(absolute, { withFileTypes: true })) {
|
|
@@ -32687,8 +33256,8 @@ function absoluteMemberBindings(projectRoot, project, explicit = {}) {
|
|
|
32687
33256
|
const repository = candidate;
|
|
32688
33257
|
if (repository.role !== "member" || typeof repository.id !== "string" || Object.hasOwn(bindings, repository.id)) continue;
|
|
32689
33258
|
if (typeof repository.pathHint === "string") {
|
|
32690
|
-
const candidate2 =
|
|
32691
|
-
if (
|
|
33259
|
+
const candidate2 = resolve21(projectRoot, ...repository.pathHint.split("/"));
|
|
33260
|
+
if (existsSync25(candidate2)) bindings[repository.id] = candidate2;
|
|
32692
33261
|
}
|
|
32693
33262
|
}
|
|
32694
33263
|
return bindings;
|
|
@@ -32702,9 +33271,9 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
32702
33271
|
const bytes = readOptionalManaged(projectRoot, path4);
|
|
32703
33272
|
if (!bytes) fail3("repair_snapshot_file_missing", path4, "configuration changed during shadow preparation");
|
|
32704
33273
|
total += bytes.byteLength;
|
|
32705
|
-
const source =
|
|
32706
|
-
const destination =
|
|
32707
|
-
mkdirSync21(
|
|
33274
|
+
const source = join29(projectRoot, ...path4.split("/"));
|
|
33275
|
+
const destination = join29(shadow, ...path4.split("/"));
|
|
33276
|
+
mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
|
|
32708
33277
|
writeDurableNew(destination, bytes, lstatSync7(source).mode & 511);
|
|
32709
33278
|
if (total > MAX_MANAGED_TOTAL_BYTES) fail3("repair_snapshot_too_large", ".kxm", `configuration snapshot exceeds ${MAX_MANAGED_TOTAL_BYTES} bytes`, "parse");
|
|
32710
33279
|
}
|
|
@@ -32712,12 +33281,12 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
32712
33281
|
for (const entry of operation.files) {
|
|
32713
33282
|
const bytes = readTarget(projectRoot, entry.path);
|
|
32714
33283
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
32715
|
-
const destination =
|
|
32716
|
-
mkdirSync21(
|
|
32717
|
-
const mode =
|
|
33284
|
+
const destination = join29(shadow, ...entry.path.split("/"));
|
|
33285
|
+
mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
|
|
33286
|
+
const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
|
|
32718
33287
|
writeDurable(destination, bytes, mode);
|
|
32719
33288
|
}
|
|
32720
|
-
const project = parseRestrictedYaml2(
|
|
33289
|
+
const project = parseRestrictedYaml2(readFileSync25(join29(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
|
|
32721
33290
|
const bound = absoluteMemberBindings(projectRoot, project, options.repositoryBindings);
|
|
32722
33291
|
loadKxmProject(shadow, { ...loaderOptions(options), repositoryBindings: bound });
|
|
32723
33292
|
const provenance = readProjectAndProvenance(shadow, options.schemasDir)?.provenance;
|
|
@@ -32726,7 +33295,7 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
32726
33295
|
}
|
|
32727
33296
|
}
|
|
32728
33297
|
function createOperation(projectRoot, kind, rendered, files, sourceTemplateRevision) {
|
|
32729
|
-
const root =
|
|
33298
|
+
const root = resolve21(projectRoot);
|
|
32730
33299
|
const operationWithoutHash = {
|
|
32731
33300
|
schema: "kxm.init-operation.v1",
|
|
32732
33301
|
operationId: `op_${randomUUID10().replaceAll("-", "")}`,
|
|
@@ -32755,7 +33324,7 @@ function prepareOperation(projectRoot, operation, rendered, options) {
|
|
|
32755
33324
|
const transaction = transactionRoot(projectRoot);
|
|
32756
33325
|
validateOperationRecord(operation, options.schemasDir);
|
|
32757
33326
|
validateOperationIntent(projectRoot, operation);
|
|
32758
|
-
if (
|
|
33327
|
+
if (existsSync25(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
|
|
32759
33328
|
mkdirSync21(transaction, { mode: 448 });
|
|
32760
33329
|
syncDirectory2(projectRoot);
|
|
32761
33330
|
writeOperation(projectRoot, operation);
|
|
@@ -32777,25 +33346,25 @@ function prepareOperation(projectRoot, operation, rendered, options) {
|
|
|
32777
33346
|
}
|
|
32778
33347
|
}
|
|
32779
33348
|
function ensureDestinationParents(projectRoot, path4) {
|
|
32780
|
-
const boundary =
|
|
33349
|
+
const boundary = resolve21(projectRoot);
|
|
32781
33350
|
const segments = path4.split("/").slice(0, -1);
|
|
32782
33351
|
let current = boundary;
|
|
32783
33352
|
for (const segment of segments) {
|
|
32784
|
-
current =
|
|
32785
|
-
if (!
|
|
33353
|
+
current = join29(current, segment);
|
|
33354
|
+
if (!existsSync25(current)) mkdirSync21(current, { mode: 448 });
|
|
32786
33355
|
const stat = lstatSync7(current);
|
|
32787
33356
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_destination_parent_invalid", path4, "destination parent must be a regular directory", "path");
|
|
32788
33357
|
}
|
|
32789
33358
|
}
|
|
32790
33359
|
function destinationSha(projectRoot, path4) {
|
|
32791
|
-
const file =
|
|
32792
|
-
if (!
|
|
33360
|
+
const file = join29(projectRoot, ...path4.split("/"));
|
|
33361
|
+
if (!existsSync25(file)) return void 0;
|
|
32793
33362
|
return kxmContentSha256(readRegularBounded(file, path4));
|
|
32794
33363
|
}
|
|
32795
33364
|
function requireConditionalLinkSupport(directory, operationId, path4) {
|
|
32796
33365
|
const nonce = randomUUID10();
|
|
32797
|
-
const source =
|
|
32798
|
-
const target =
|
|
33366
|
+
const source = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
|
|
33367
|
+
const target = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
|
|
32799
33368
|
try {
|
|
32800
33369
|
writeDurableNew(source, Buffer.from("kxm-link-probe\n", "utf8"), 384);
|
|
32801
33370
|
try {
|
|
@@ -32810,19 +33379,19 @@ function requireConditionalLinkSupport(directory, operationId, path4) {
|
|
|
32810
33379
|
}
|
|
32811
33380
|
}
|
|
32812
33381
|
function atomicInstallTarget(projectRoot, operation, entry) {
|
|
32813
|
-
const destination =
|
|
33382
|
+
const destination = join29(projectRoot, ...entry.path.split("/"));
|
|
32814
33383
|
ensureDestinationParents(projectRoot, entry.path);
|
|
32815
|
-
const directory =
|
|
33384
|
+
const directory = dirname16(destination);
|
|
32816
33385
|
const temporaryPrefix = `.kxm-repair-${operation.operationId}-`;
|
|
32817
33386
|
const pathKey = kxmContentSha256(entry.path).slice("sha256:".length, "sha256:".length + 16);
|
|
32818
|
-
const displaced =
|
|
32819
|
-
if (
|
|
33387
|
+
const displaced = join29(directory, `${temporaryPrefix}${pathKey}.preimage`);
|
|
33388
|
+
if (existsSync25(displaced)) {
|
|
32820
33389
|
const stat = lstatSync7(displaced);
|
|
32821
33390
|
if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_preimage_temporary_invalid", entry.path, "stale displaced preimage is not a regular file", "path");
|
|
32822
|
-
if (entry.observedSha256 === null || kxmContentSha256(
|
|
33391
|
+
if (entry.observedSha256 === null || kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
|
|
32823
33392
|
fail3("repair_preimage_temporary_mismatch", entry.path, "stale displaced preimage does not match the pinned operation");
|
|
32824
33393
|
}
|
|
32825
|
-
if (!
|
|
33394
|
+
if (!existsSync25(destination)) {
|
|
32826
33395
|
try {
|
|
32827
33396
|
linkSync(displaced, destination);
|
|
32828
33397
|
} catch {
|
|
@@ -32837,7 +33406,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
32837
33406
|
const owned = candidate.name.startsWith(temporaryPrefix) && (candidate.name.endsWith(".tmp") || candidate.name.endsWith(".link-source") || candidate.name.endsWith(".link-target"));
|
|
32838
33407
|
if (!owned) continue;
|
|
32839
33408
|
if (!candidate.isFile()) fail3("repair_temporary_invalid", entry.path, "stale operation temporary is not a regular file", "path");
|
|
32840
|
-
rmSync9(
|
|
33409
|
+
rmSync9(join29(directory, candidate.name), { force: true });
|
|
32841
33410
|
}
|
|
32842
33411
|
const current = destinationSha(projectRoot, entry.path);
|
|
32843
33412
|
if (current === entry.targetSha256) return;
|
|
@@ -32847,15 +33416,15 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
32847
33416
|
requireConditionalLinkSupport(directory, operation.operationId, entry.path);
|
|
32848
33417
|
const bytes = readTarget(projectRoot, entry.path);
|
|
32849
33418
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
32850
|
-
const temporary =
|
|
32851
|
-
const mode =
|
|
33419
|
+
const temporary = join29(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
|
|
33420
|
+
const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
|
|
32852
33421
|
try {
|
|
32853
33422
|
writeDurableNew(temporary, bytes, mode);
|
|
32854
33423
|
if (entry.observedSha256 !== null) {
|
|
32855
33424
|
renameSync7(destination, displaced);
|
|
32856
33425
|
syncDirectory2(directory);
|
|
32857
|
-
if (kxmContentSha256(
|
|
32858
|
-
if (!
|
|
33426
|
+
if (kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
|
|
33427
|
+
if (!existsSync25(destination)) {
|
|
32859
33428
|
try {
|
|
32860
33429
|
linkSync(displaced, destination);
|
|
32861
33430
|
} catch {
|
|
@@ -32871,7 +33440,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
32871
33440
|
try {
|
|
32872
33441
|
linkSync(temporary, destination);
|
|
32873
33442
|
} catch {
|
|
32874
|
-
if (!
|
|
33443
|
+
if (!existsSync25(destination) && existsSync25(displaced)) {
|
|
32875
33444
|
try {
|
|
32876
33445
|
linkSync(displaced, destination);
|
|
32877
33446
|
} catch {
|
|
@@ -32893,12 +33462,12 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
32893
33462
|
if (destinationSha(projectRoot, entry.path) !== entry.targetSha256) fail3("repair_install_verification_failed", entry.path, "installed target hash does not match the pinned operation");
|
|
32894
33463
|
}
|
|
32895
33464
|
function installedCreateMatches(projectRoot, operation) {
|
|
32896
|
-
const configRoot =
|
|
32897
|
-
if (!
|
|
33465
|
+
const configRoot = join29(projectRoot, ".kxm");
|
|
33466
|
+
if (!existsSync25(configRoot)) return false;
|
|
32898
33467
|
const actual = [];
|
|
32899
33468
|
const visit2 = (directory) => {
|
|
32900
33469
|
for (const entry of readdirSync9(directory, { withFileTypes: true })) {
|
|
32901
|
-
const absolute =
|
|
33470
|
+
const absolute = join29(directory, entry.name);
|
|
32902
33471
|
if (entry.isSymbolicLink()) fail3("create_resume_link", relativeConfigPath(projectRoot, absolute), "created configuration must not contain links", "path");
|
|
32903
33472
|
if (entry.isDirectory()) visit2(absolute);
|
|
32904
33473
|
else if (entry.isFile()) actual.push(relativeConfigPath(projectRoot, absolute));
|
|
@@ -32911,15 +33480,15 @@ function installedCreateMatches(projectRoot, operation) {
|
|
|
32911
33480
|
return actual.length === expected.length && actual.every((path4, index) => path4 === expected[index]) && operation.files.every((entry) => destinationSha(projectRoot, entry.path) === entry.targetSha256);
|
|
32912
33481
|
}
|
|
32913
33482
|
function relativeConfigPath(projectRoot, file) {
|
|
32914
|
-
return relative5(
|
|
33483
|
+
return relative5(resolve21(projectRoot), file).replaceAll("\\", "/");
|
|
32915
33484
|
}
|
|
32916
33485
|
function syncTreeDirectories(root) {
|
|
32917
|
-
if (!
|
|
33486
|
+
if (!existsSync25(root)) return;
|
|
32918
33487
|
const directories = [];
|
|
32919
33488
|
const visit2 = (directory) => {
|
|
32920
33489
|
directories.push(directory);
|
|
32921
33490
|
for (const entry of readdirSync9(directory, { withFileTypes: true })) {
|
|
32922
|
-
if (entry.isDirectory()) visit2(
|
|
33491
|
+
if (entry.isDirectory()) visit2(join29(directory, entry.name));
|
|
32923
33492
|
}
|
|
32924
33493
|
};
|
|
32925
33494
|
visit2(root);
|
|
@@ -32933,7 +33502,7 @@ function rebuildCreateShadow(projectRoot, operation, options) {
|
|
|
32933
33502
|
if (stat.isSymbolicLink() || !stat.isDirectory() || !installedCreateMatches(shadow, operation)) {
|
|
32934
33503
|
fail3("create_shadow_invalid", TRANSACTION_NAME, "reconstructed create shadow does not exactly match pinned target files", "path");
|
|
32935
33504
|
}
|
|
32936
|
-
syncTreeDirectories(
|
|
33505
|
+
syncTreeDirectories(join29(shadow, ".kxm"));
|
|
32937
33506
|
}
|
|
32938
33507
|
function applyOperation(projectRoot, original, options, resumed) {
|
|
32939
33508
|
assertNoRegisteredGates(options);
|
|
@@ -32943,10 +33512,10 @@ function applyOperation(projectRoot, original, options, resumed) {
|
|
|
32943
33512
|
verifyBackups(projectRoot, operation);
|
|
32944
33513
|
operation = updatePhase(projectRoot, operation, "applying");
|
|
32945
33514
|
if (operation.kind === "create") {
|
|
32946
|
-
const destination =
|
|
32947
|
-
if (!
|
|
33515
|
+
const destination = join29(projectRoot, ".kxm");
|
|
33516
|
+
if (!existsSync25(destination)) {
|
|
32948
33517
|
rebuildCreateShadow(projectRoot, operation, effectiveOptions);
|
|
32949
|
-
renameSync7(
|
|
33518
|
+
renameSync7(join29(shadowRoot(projectRoot), ".kxm"), destination);
|
|
32950
33519
|
syncDirectory2(projectRoot);
|
|
32951
33520
|
} else if (!installedCreateMatches(projectRoot, operation)) {
|
|
32952
33521
|
fail3("create_resume_conflict", ".kxm", "existing project configuration does not match the pinned create operation");
|
|
@@ -32973,7 +33542,7 @@ function applyOperation(projectRoot, original, options, resumed) {
|
|
|
32973
33542
|
operation = updatePhase(projectRoot, operation, "verified");
|
|
32974
33543
|
if (options.testFaultAt === "verified") throw new Error("injected init fault after verification");
|
|
32975
33544
|
const files = operation.files.map((entry) => entry.path);
|
|
32976
|
-
syncTreeDirectories(
|
|
33545
|
+
syncTreeDirectories(join29(projectRoot, ".kxm"));
|
|
32977
33546
|
return { kind: operation.kind, resumed, bundle, files };
|
|
32978
33547
|
}
|
|
32979
33548
|
function prepareAndApplyKxmCreate(projectRoot, rendered, options = {}) {
|
|
@@ -33088,7 +33657,7 @@ function finishPreparingOperation(projectRoot, operation, options) {
|
|
|
33088
33657
|
const rendered = rendererForOperation(operation);
|
|
33089
33658
|
if (!rendered) fail3("init_transaction_renderer_unavailable", TRANSACTION_NAME, "this binary cannot reproduce the pinned pre-application target");
|
|
33090
33659
|
for (const directory of ["targets", "backups", "shadow"]) {
|
|
33091
|
-
rmSync9(
|
|
33660
|
+
rmSync9(join29(transactionRoot(projectRoot), directory), { recursive: true, force: true });
|
|
33092
33661
|
}
|
|
33093
33662
|
for (const entry of operation.files) {
|
|
33094
33663
|
const bytes = rendered.files.get(entry.path);
|
|
@@ -33107,7 +33676,7 @@ function resumeKxmInitTransaction(projectRoot, options = {}) {
|
|
|
33107
33676
|
let operation = readOperation(projectRoot, options.schemasDir);
|
|
33108
33677
|
if (!operation) {
|
|
33109
33678
|
const root = transactionRoot(projectRoot);
|
|
33110
|
-
if (
|
|
33679
|
+
if (existsSync25(root)) {
|
|
33111
33680
|
rmSync9(root, { recursive: true, force: true });
|
|
33112
33681
|
syncDirectory2(projectRoot);
|
|
33113
33682
|
}
|
|
@@ -33123,7 +33692,7 @@ function commitKxmInitTransaction(projectRoot, schemasDir) {
|
|
|
33123
33692
|
fail3("init_transaction_not_verified", TRANSACTION_NAME, "initialization transaction cannot commit before installed state is verified");
|
|
33124
33693
|
}
|
|
33125
33694
|
const transaction = transactionRoot(projectRoot);
|
|
33126
|
-
const retired =
|
|
33695
|
+
const retired = join29(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID10()}`);
|
|
33127
33696
|
renameSync7(transaction, retired);
|
|
33128
33697
|
syncDirectory2(projectRoot);
|
|
33129
33698
|
rmSync9(retired, { recursive: true, force: true });
|
|
@@ -33232,7 +33801,7 @@ function validateBindingIdentities(bundle, persisted, explicit) {
|
|
|
33232
33801
|
}
|
|
33233
33802
|
function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
33234
33803
|
const storeOptions = bindingStoreOptions(options);
|
|
33235
|
-
const persisted =
|
|
33804
|
+
const persisted = existsSync26(join30(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
|
|
33236
33805
|
const repositoryBindings = Object.fromEntries([
|
|
33237
33806
|
...Object.entries(persisted?.repositories ?? {}),
|
|
33238
33807
|
...Object.entries(options.repositoryBindings ?? {})
|
|
@@ -33420,7 +33989,7 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
|
33420
33989
|
const files = [...rendered.files.keys()];
|
|
33421
33990
|
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
|
|
33422
33991
|
if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
|
|
33423
|
-
if (
|
|
33992
|
+
if (existsSync26(join30(gitRoot, ".kxm"))) {
|
|
33424
33993
|
throw new KxmConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
|
|
33425
33994
|
}
|
|
33426
33995
|
const created = prepareAndApplyKxmCreate(gitRoot, rendered, transactionOptions);
|
|
@@ -33457,7 +34026,7 @@ function hubSourceReason(status) {
|
|
|
33457
34026
|
if (status === 404) return "hub_not_found";
|
|
33458
34027
|
return `hub_http_${status}`;
|
|
33459
34028
|
}
|
|
33460
|
-
function
|
|
34029
|
+
function asString2(value) {
|
|
33461
34030
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
33462
34031
|
}
|
|
33463
34032
|
function isObject(value) {
|
|
@@ -33505,37 +34074,37 @@ async function assembleTenantStatus(input) {
|
|
|
33505
34074
|
project: body.project,
|
|
33506
34075
|
fetchedAt: body.fetchedAt,
|
|
33507
34076
|
agents: body.agents.filter(isObject).map((agent) => ({
|
|
33508
|
-
id:
|
|
33509
|
-
name:
|
|
34077
|
+
id: asString2(agent.id) ?? "",
|
|
34078
|
+
name: asString2(agent.name) ?? "",
|
|
33510
34079
|
online: agent.online === true
|
|
33511
34080
|
})),
|
|
33512
34081
|
openMessageTotal: body.openMessageTotal,
|
|
33513
34082
|
runTotal: body.runTotal,
|
|
33514
34083
|
runs: runRows.map((run) => {
|
|
33515
34084
|
const stages = Array.isArray(run.stages) ? run.stages.filter(isObject).map((stage) => ({
|
|
33516
|
-
id:
|
|
33517
|
-
status:
|
|
34085
|
+
id: asString2(stage.id) ?? "",
|
|
34086
|
+
status: asString2(stage.status) ?? "unknown",
|
|
33518
34087
|
...typeof stage.attempts === "number" ? { attempts: stage.attempts } : {}
|
|
33519
34088
|
})) : void 0;
|
|
33520
34089
|
const done = stages?.filter((stage) => stage.status === "passed" || stage.status === "failed" || stage.status === "warning").length;
|
|
33521
34090
|
return {
|
|
33522
|
-
id:
|
|
33523
|
-
status:
|
|
33524
|
-
definitionId:
|
|
33525
|
-
...
|
|
33526
|
-
...
|
|
33527
|
-
...
|
|
34091
|
+
id: asString2(run.id) ?? "",
|
|
34092
|
+
status: asString2(run.status) ?? "unknown",
|
|
34093
|
+
definitionId: asString2(run.definitionId) ?? "",
|
|
34094
|
+
...asString2(run.currentStage) ? { currentStage: asString2(run.currentStage) } : {},
|
|
34095
|
+
...asString2(run.targetAgentName) ? { targetAgentName: asString2(run.targetAgentName) } : {},
|
|
34096
|
+
...asString2(run.updatedAt) ? { updatedAt: asString2(run.updatedAt) } : {},
|
|
33528
34097
|
...stages !== void 0 ? { progress: { done: done ?? 0, total: stages.length }, stages } : {},
|
|
33529
34098
|
source: "hub-projection"
|
|
33530
34099
|
};
|
|
33531
34100
|
}),
|
|
33532
34101
|
plans: (Array.isArray(body.plans) ? body.plans : []).filter(isObject).map((plan) => ({
|
|
33533
|
-
id:
|
|
33534
|
-
runId:
|
|
33535
|
-
summary:
|
|
33536
|
-
createdAt:
|
|
33537
|
-
...
|
|
33538
|
-
...
|
|
34102
|
+
id: asString2(plan.id) ?? "",
|
|
34103
|
+
runId: asString2(plan.runId) ?? "",
|
|
34104
|
+
summary: asString2(plan.summary) ?? "",
|
|
34105
|
+
createdAt: asString2(plan.createdAt) ?? "",
|
|
34106
|
+
...asString2(plan.stageId) ? { stageId: asString2(plan.stageId) } : {},
|
|
34107
|
+
...asString2(plan.severity) ? { severity: asString2(plan.severity) } : {}
|
|
33539
34108
|
}))
|
|
33540
34109
|
};
|
|
33541
34110
|
return { state: "ok", observedAt: attemptedAt, value };
|
|
@@ -33676,6 +34245,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
33676
34245
|
localStateRoot: kxmUserStateRoot({ env: runtime.env }),
|
|
33677
34246
|
dryRun: runtime.dryRun
|
|
33678
34247
|
});
|
|
34248
|
+
const starterGuidance = initialized.action === "created" || initialized.action === "planned" && initialized.plan.mode === "create" ? [
|
|
34249
|
+
"defaultHarness: pi and the npm test gate are generic starter settings, not repository detection.",
|
|
34250
|
+
"For Claude, set defaultHarness: claude in .kxm/project.yaml, update any explicit harness overrides in .kxm/agents/*.yaml, and configure compatible agent models.",
|
|
34251
|
+
"For .NET or other non-npm repositories, set gates.test.argv in .kxm/gates.yaml to the repository's actual test runner before driving a workflow."
|
|
34252
|
+
] : [];
|
|
33679
34253
|
const payload = {
|
|
33680
34254
|
ok: initialized.action !== "planned" || runtime.dryRun,
|
|
33681
34255
|
command: "init",
|
|
@@ -33688,10 +34262,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
33688
34262
|
...initialized.repairPlan === void 0 ? {} : { repairPlan: initialized.repairPlan },
|
|
33689
34263
|
...initialized.resumePending === void 0 ? {} : { resumePending: initialized.resumePending },
|
|
33690
34264
|
...initialized.transactionKind === void 0 ? {} : { transactionKind: initialized.transactionKind },
|
|
33691
|
-
plannedOnly: initialized.action === "planned"
|
|
34265
|
+
plannedOnly: initialized.action === "planned",
|
|
34266
|
+
...starterGuidance.length > 0 ? { guidance: starterGuidance } : {}
|
|
33692
34267
|
};
|
|
33693
34268
|
const finishInit = (code, text) => {
|
|
33694
|
-
print(runtime.io, runtime.json, payload, text);
|
|
34269
|
+
print(runtime.io, runtime.json, payload, [text, ...starterGuidance].join("\n"));
|
|
33695
34270
|
return code;
|
|
33696
34271
|
};
|
|
33697
34272
|
if (initialized.action === "created") {
|
|
@@ -33745,7 +34320,7 @@ async function cmdBackup(runtime, options) {
|
|
|
33745
34320
|
projectRoot: backupProjectRoot(runtime),
|
|
33746
34321
|
env: runtime.env,
|
|
33747
34322
|
...options.allProjects === true ? { allProjects: true } : {},
|
|
33748
|
-
...options.out ? { outDir:
|
|
34323
|
+
...options.out ? { outDir: resolve22(runtime.cwd, options.out) } : {}
|
|
33749
34324
|
};
|
|
33750
34325
|
if (runtime.dryRun) {
|
|
33751
34326
|
const plan = planBackup(backupOptions);
|
|
@@ -33753,8 +34328,8 @@ async function cmdBackup(runtime, options) {
|
|
|
33753
34328
|
runtime,
|
|
33754
34329
|
{ command: "backup", scope: plan.scope, outDir: plan.outDir, stores: plan.stores, files: plan.files },
|
|
33755
34330
|
[
|
|
33756
|
-
...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target:
|
|
33757
|
-
{ action: "write", target:
|
|
34331
|
+
...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join31(plan.outDir, entry.backupFile) })),
|
|
34332
|
+
{ action: "write", target: join31(plan.outDir, "manifest.json") }
|
|
33758
34333
|
],
|
|
33759
34334
|
`back up ${plan.stores.length} store(s) and ${plan.files.length} file(s) (${plan.scope} scope) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
|
|
33760
34335
|
);
|
|
@@ -33773,7 +34348,7 @@ async function cmdBackup(runtime, options) {
|
|
|
33773
34348
|
complete ? `Created SQLite backup with ${manifest.stores.length} store(s) (${manifest.scope ?? "project"} scope):` : `Backup is incomplete (${manifest.omitted?.length ?? 0} omitted); not ok:`,
|
|
33774
34349
|
...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
|
|
33775
34350
|
...(manifest.omitted ?? []).map((id) => ` - omitted ${id}`),
|
|
33776
|
-
`Manifest: ${
|
|
34351
|
+
`Manifest: ${join31(outDir, "manifest.json")}`
|
|
33777
34352
|
].join("\n");
|
|
33778
34353
|
print(runtime.io, runtime.json, payload, summary);
|
|
33779
34354
|
return complete ? 0 : 1;
|
|
@@ -33807,7 +34382,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
|
|
|
33807
34382
|
}
|
|
33808
34383
|
for (const store of plan.stores) {
|
|
33809
34384
|
if (store.storeId !== "hub-store") continue;
|
|
33810
|
-
const claim = readLiveHubClaim(
|
|
34385
|
+
const claim = readLiveHubClaim(dirname17(store.targetPath));
|
|
33811
34386
|
if (claim) {
|
|
33812
34387
|
throw databaseError(
|
|
33813
34388
|
"restore_hub_running",
|
|
@@ -33819,7 +34394,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
|
|
|
33819
34394
|
}
|
|
33820
34395
|
async function cmdRestore(runtime, manifestArg, options = {}) {
|
|
33821
34396
|
try {
|
|
33822
|
-
const plan = planRestore(
|
|
34397
|
+
const plan = planRestore(resolve22(runtime.cwd, manifestArg), {
|
|
33823
34398
|
projectRoot: backupProjectRoot(runtime),
|
|
33824
34399
|
env: runtime.env,
|
|
33825
34400
|
...options.allProjects === true ? { allProjects: true } : {}
|
|
@@ -33839,7 +34414,7 @@ async function cmdRestore(runtime, manifestArg, options = {}) {
|
|
|
33839
34414
|
[
|
|
33840
34415
|
...plan.stores.flatMap((store) => [
|
|
33841
34416
|
{ action: "write", target: store.targetPath },
|
|
33842
|
-
...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) =>
|
|
34417
|
+
...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync27(file)).map((file) => ({ action: "delete", target: file }))
|
|
33843
34418
|
]),
|
|
33844
34419
|
...plan.files.map((file) => ({ action: "write", target: file.targetPath }))
|
|
33845
34420
|
],
|
|
@@ -33920,11 +34495,17 @@ async function readSupervisor(runtime, command) {
|
|
|
33920
34495
|
const attached = await attachKxmSupervisor({ env: runtime.env });
|
|
33921
34496
|
return attached ?? refuseDryRun(runtime.io, runtime.json, command, "the Runtime supervisor is not running and --dry-run will not start it");
|
|
33922
34497
|
}
|
|
33923
|
-
|
|
33924
|
-
|
|
33925
|
-
|
|
34498
|
+
function runEngineNotice(runId, defaultHarness, prerequisites) {
|
|
34499
|
+
return [
|
|
34500
|
+
`No steps executed. Project default harness: ${defaultHarness}; per-agent harness settings take precedence.`,
|
|
34501
|
+
...prerequisites.map((item) => `Live prerequisite${item.stepId ? ` (${item.stepId})` : ""}: ${item.detail}`),
|
|
34502
|
+
"Live execution uses one-shot harness calls; no hub or Pi worker is required. Check installation/authentication with kxm harness list.",
|
|
34503
|
+
`${prerequisites.length > 0 ? "Resolve the prerequisites above, then execute" : "Execute"}: kxm runs drive ${runId} --wait`,
|
|
34504
|
+
`Inspect: kxm runs status ${runId} --json; receipt: kxm runs receipt ${runId} --json; cancel: kxm runs cancel ${runId}`,
|
|
34505
|
+
"These are local Runtime runs, not webhook workflows; use kxm runs, not kxm workflow get."
|
|
34506
|
+
].join("\n");
|
|
33926
34507
|
}
|
|
33927
|
-
function resolveKxmRunTarget(runtime, workflow) {
|
|
34508
|
+
function resolveKxmRunTarget(runtime, workflow, forTask = false) {
|
|
33928
34509
|
if (runtime.workspaceFlag !== void 0) {
|
|
33929
34510
|
print(runtime.io, runtime.json, {
|
|
33930
34511
|
ok: false,
|
|
@@ -33933,7 +34514,7 @@ function resolveKxmRunTarget(runtime, workflow) {
|
|
|
33933
34514
|
}, "kxm run discovers the authoritative project from the current directory; --workspace is not supported");
|
|
33934
34515
|
return 2;
|
|
33935
34516
|
}
|
|
33936
|
-
if (!workflow) {
|
|
34517
|
+
if (!workflow && !forTask) {
|
|
33937
34518
|
print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required" }, "usage: kxm run <workflow> [prompt]");
|
|
33938
34519
|
return 2;
|
|
33939
34520
|
}
|
|
@@ -33943,15 +34524,30 @@ function resolveKxmRunTarget(runtime, workflow) {
|
|
|
33943
34524
|
return 1;
|
|
33944
34525
|
}
|
|
33945
34526
|
const bundle = loadKxmProject(projectRoot, {});
|
|
33946
|
-
|
|
33947
|
-
|
|
34527
|
+
const workflowId = workflow ?? String(bundle.project.value.defaultWorkflow ?? "default");
|
|
34528
|
+
if (!bundle.workflows.has(workflowId)) {
|
|
34529
|
+
print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow: workflowId }, `workflow ${workflowId} does not exist in this project`);
|
|
34530
|
+
return 1;
|
|
34531
|
+
}
|
|
34532
|
+
const defaultHarness = String(bundle.project.value.defaultHarness ?? "pi");
|
|
34533
|
+
const prerequisites = kxmLiveRunPrerequisites(bundle, workflowId, projectRoot);
|
|
34534
|
+
if (forTask && prerequisites.length > 0) {
|
|
34535
|
+
print(runtime.io, runtime.json, {
|
|
34536
|
+
ok: false,
|
|
34537
|
+
command: "task run",
|
|
34538
|
+
error: "run_execution_unavailable",
|
|
34539
|
+
workflowId,
|
|
34540
|
+
defaultHarness,
|
|
34541
|
+
execution: { status: "not_started", mode: "live", prerequisites }
|
|
34542
|
+
}, `task run refused; no run created or task changed (default harness: ${defaultHarness}).
|
|
34543
|
+
${prerequisites.map((item) => `${item.stepId ?? workflowId}: ${item.detail}`).join("\n")}`);
|
|
33948
34544
|
return 1;
|
|
33949
34545
|
}
|
|
33950
|
-
return { projectRoot, workflowId
|
|
34546
|
+
return { projectRoot, workflowId, configRevision: bundle.configRevision, defaultHarness, prerequisites };
|
|
33951
34547
|
}
|
|
33952
|
-
async function cmdKxmRun(runtime, workflow, promptParts) {
|
|
34548
|
+
async function cmdKxmRun(runtime, workflow, promptParts, forTask = false) {
|
|
33953
34549
|
try {
|
|
33954
|
-
const target = resolveKxmRunTarget(runtime, workflow);
|
|
34550
|
+
const target = resolveKxmRunTarget(runtime, workflow, forTask);
|
|
33955
34551
|
if (typeof target === "number") return target;
|
|
33956
34552
|
const { projectRoot } = target;
|
|
33957
34553
|
if (runtime.dryRun) {
|
|
@@ -33974,12 +34570,24 @@ async function cmdKxmRun(runtime, workflow, promptParts) {
|
|
|
33974
34570
|
print(runtime.io, runtime.json, {
|
|
33975
34571
|
ok: true,
|
|
33976
34572
|
command: "run",
|
|
33977
|
-
|
|
34573
|
+
execution: {
|
|
34574
|
+
status: "not_started",
|
|
34575
|
+
mode: "live",
|
|
34576
|
+
defaultHarness: target.defaultHarness,
|
|
34577
|
+
authentication: "not_checked",
|
|
34578
|
+
prerequisites: target.prerequisites,
|
|
34579
|
+
nextSteps: {
|
|
34580
|
+
harnesses: "kxm harness list",
|
|
34581
|
+
drive: `kxm runs drive ${run.runId} --wait`,
|
|
34582
|
+
status: `kxm runs status ${run.runId} --json`,
|
|
34583
|
+
receipt: `kxm runs receipt ${run.runId} --json`
|
|
34584
|
+
}
|
|
34585
|
+
},
|
|
33978
34586
|
idempotent: acceptance.idempotent === true,
|
|
33979
34587
|
run,
|
|
33980
34588
|
supervisor: { runtimeId: supervisor.runtimeId, port: supervisor.port, started: supervisor.started }
|
|
33981
34589
|
}, `run ${run.status}: ${run.runId} (home ${run.homeRuntimeId.slice(0, 12)}\u2026, config ${run.configRevision.slice(0, 19)}\u2026)
|
|
33982
|
-
${runEngineNotice(run.runId)}`);
|
|
34590
|
+
${runEngineNotice(run.runId, target.defaultHarness, target.prerequisites)}`);
|
|
33983
34591
|
return 0;
|
|
33984
34592
|
} catch (error) {
|
|
33985
34593
|
if (error instanceof KxmConfigError) {
|
|
@@ -34063,7 +34671,7 @@ function parseDriveWaitTimeoutMs(raw) {
|
|
|
34063
34671
|
return value;
|
|
34064
34672
|
}
|
|
34065
34673
|
function sleepMs(ms) {
|
|
34066
|
-
return new Promise((
|
|
34674
|
+
return new Promise((resolve34) => setTimeout(resolve34, ms));
|
|
34067
34675
|
}
|
|
34068
34676
|
function driveWaitCompleted(drive) {
|
|
34069
34677
|
return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
|
|
@@ -34260,9 +34868,20 @@ async function cmdTenantStatus(runtime) {
|
|
|
34260
34868
|
}
|
|
34261
34869
|
}
|
|
34262
34870
|
async function cmdHarnessList(runtime) {
|
|
34263
|
-
|
|
34264
|
-
|
|
34265
|
-
|
|
34871
|
+
try {
|
|
34872
|
+
const projectRoot = discoverKxmProjectRoot(runtime.cwd);
|
|
34873
|
+
const defaultHarness = projectRoot ? String(loadKxmProject(projectRoot).project.value.defaultHarness ?? "pi") : void 0;
|
|
34874
|
+
const inventory = await probeHarnessesAsync({ env: runtime.env, defaultHarness });
|
|
34875
|
+
print(runtime.io, runtime.json, { ok: true, command: "harness list", ...inventory }, formatHarnessInventory(inventory));
|
|
34876
|
+
return 0;
|
|
34877
|
+
} catch (error) {
|
|
34878
|
+
if (error instanceof KxmConfigError) {
|
|
34879
|
+
print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_failed", issues: error.issues }, `harness list failed: ${error.message}`);
|
|
34880
|
+
return 1;
|
|
34881
|
+
}
|
|
34882
|
+
print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_io_failed" }, "harness list failed because a local operation did not complete");
|
|
34883
|
+
return 1;
|
|
34884
|
+
}
|
|
34266
34885
|
}
|
|
34267
34886
|
async function selectInventoryModel(runtime, requested) {
|
|
34268
34887
|
const models = listInventoryModels(runtime.dirs.workdir);
|
|
@@ -34270,7 +34889,7 @@ async function selectInventoryModel(runtime, requested) {
|
|
|
34270
34889
|
if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
|
|
34271
34890
|
runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
|
|
34272
34891
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
34273
|
-
const answer = await new Promise((
|
|
34892
|
+
const answer = await new Promise((resolve34) => rl.once("line", resolve34));
|
|
34274
34893
|
rl.close();
|
|
34275
34894
|
const index = Number.parseInt(answer.trim(), 10) - 1;
|
|
34276
34895
|
return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
|
|
@@ -34296,7 +34915,7 @@ async function cmdModelsScreen(runtime) {
|
|
|
34296
34915
|
return 2;
|
|
34297
34916
|
}
|
|
34298
34917
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
34299
|
-
const ask = (q2) => new Promise((
|
|
34918
|
+
const ask = (q2) => new Promise((resolve34) => rl.question(q2, resolve34));
|
|
34300
34919
|
try {
|
|
34301
34920
|
while (true) {
|
|
34302
34921
|
const policy = loadRoutePolicy(runtime.dirs.workdir);
|
|
@@ -34473,39 +35092,64 @@ async function cmdKxmRunReceipt(runtime, runId, options = {}) {
|
|
|
34473
35092
|
}
|
|
34474
35093
|
|
|
34475
35094
|
// plugins/kxm/src/cli/tasks.ts
|
|
34476
|
-
async function cmdSuggest(runtime, promptParts) {
|
|
35095
|
+
async function cmdSuggest(runtime, promptParts, probeHarnesses2 = probeHarnessesAsync) {
|
|
34477
35096
|
try {
|
|
34478
35097
|
const prompt = promptParts.join(" ").trim();
|
|
34479
35098
|
if (!prompt) {
|
|
34480
|
-
runtime.io.
|
|
35099
|
+
print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "prompt_required" }, "prompt must be non-empty");
|
|
34481
35100
|
return 2;
|
|
34482
35101
|
}
|
|
34483
|
-
const inventory = await
|
|
34484
|
-
const
|
|
34485
|
-
|
|
34486
|
-
|
|
34487
|
-
|
|
34488
|
-
|
|
35102
|
+
const inventory = runtime.dryRun ? void 0 : await probeHarnesses2({ env: runtime.env });
|
|
35103
|
+
const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses: inventory?.harnesses });
|
|
35104
|
+
if (suggestion.execution.supported && [".yaml", ".yml"].some((extension) => existsSync28(join32(runtime.cwd, ".kxm", "workflows", `${suggestion.workflowId}${extension}`)))) {
|
|
35105
|
+
suggestion.roles = [];
|
|
35106
|
+
suggestion.execution = {
|
|
35107
|
+
supported: false,
|
|
35108
|
+
error: "workflow_already_exists",
|
|
35109
|
+
reason: `Workflow ${suggestion.workflowId} already exists; its agents and permissions may differ from the recommended template.`,
|
|
35110
|
+
nextSteps: ["Review the existing definition and every agent's harness/model before running it, or install the recommended template under a fresh flat ID. No existing workflow is overwritten or recommended for execution."]
|
|
35111
|
+
};
|
|
35112
|
+
}
|
|
35113
|
+
const execution = suggestion.execution;
|
|
34489
35114
|
const text = [
|
|
34490
35115
|
`Suggested Workflow: ${suggestion.workflowId} (${suggestion.area})`,
|
|
35116
|
+
`Template: ${suggestion.template}`,
|
|
34491
35117
|
`Confidence: ${(suggestion.confidence * 100).toFixed(0)}%`,
|
|
34492
35118
|
`Reasons: ${suggestion.reasons.join("; ")}`,
|
|
34493
35119
|
`Suggested Skills: ${suggestion.suggestedSkills.join(", ") || "none"}`,
|
|
34494
|
-
|
|
34495
|
-
|
|
34496
|
-
`
|
|
34497
|
-
|
|
34498
|
-
|
|
34499
|
-
|
|
34500
|
-
|
|
34501
|
-
|
|
35120
|
+
"",
|
|
35121
|
+
"Install definition only (requires kxm init; does not execute):",
|
|
35122
|
+
` ${suggestion.suggestedCommand}`,
|
|
35123
|
+
"",
|
|
35124
|
+
...execution.supported ? [
|
|
35125
|
+
"Suggested agent routing (not applied):",
|
|
35126
|
+
...suggestion.roles.map((role) => ` ${role.agent}: ${role.harness} (${role.role}; use a configured compatible model)`),
|
|
35127
|
+
"Prerequisites:",
|
|
35128
|
+
...execution.prerequisites.map((step) => ` ${step}`),
|
|
35129
|
+
`Create a run only (${execution.shell}; does not execute steps):`,
|
|
35130
|
+
` ${execution.createCommand}`,
|
|
35131
|
+
"Then drive the returned run ID with live calls and inspect its result:",
|
|
35132
|
+
` ${execution.driveCommand}`,
|
|
35133
|
+
` ${execution.statusCommand}`,
|
|
35134
|
+
` ${execution.receiptCommand}`
|
|
35135
|
+
] : [
|
|
35136
|
+
`Execution unavailable: ${execution.reason}`,
|
|
35137
|
+
...execution.nextSteps.map((step) => ` ${step}`),
|
|
35138
|
+
...runtime.dryRun ? ["Harness authentication was not probed during --dry-run."] : []
|
|
35139
|
+
]
|
|
34502
35140
|
].join("\n");
|
|
34503
|
-
print(runtime.io, runtime.json, {
|
|
34504
|
-
|
|
35141
|
+
print(runtime.io, runtime.json, {
|
|
35142
|
+
ok: execution.supported,
|
|
35143
|
+
command: "suggest",
|
|
35144
|
+
prompt,
|
|
35145
|
+
...suggestion,
|
|
35146
|
+
...runtime.dryRun ? { dryRun: true } : {},
|
|
35147
|
+
...!execution.supported ? { error: execution.error } : {}
|
|
35148
|
+
}, text);
|
|
35149
|
+
return execution.supported ? 0 : 1;
|
|
34505
35150
|
} catch (error) {
|
|
34506
35151
|
const message = error instanceof Error ? error.message : String(error);
|
|
34507
|
-
runtime.io.
|
|
34508
|
-
`);
|
|
35152
|
+
print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "suggest_failed", detail: message }, `suggest failed: ${message}`);
|
|
34509
35153
|
return 1;
|
|
34510
35154
|
}
|
|
34511
35155
|
}
|
|
@@ -34625,27 +35269,21 @@ async function cmdTaskRun(runtime, taskId) {
|
|
|
34625
35269
|
`);
|
|
34626
35270
|
return 1;
|
|
34627
35271
|
}
|
|
34628
|
-
const workflow = task.assignedWorkflow
|
|
35272
|
+
const workflow = task.assignedWorkflow;
|
|
34629
35273
|
if (runtime.dryRun) {
|
|
34630
|
-
const target = resolveKxmRunTarget(runtime, workflow);
|
|
35274
|
+
const target = resolveKxmRunTarget(runtime, workflow, true);
|
|
34631
35275
|
if (typeof target === "number") return target;
|
|
34632
|
-
const started = updateTaskStatus(runtime.cwd, taskId, "in_progress", { dryRun: true });
|
|
34633
35276
|
printPlan(
|
|
34634
35277
|
runtime,
|
|
34635
|
-
{ command: "task run", taskId, ...target, status:
|
|
35278
|
+
{ command: "task run", taskId, ...target, status: task.status, execution: { status: "not_started", mode: "live" } },
|
|
34636
35279
|
[
|
|
34637
|
-
{ action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" }
|
|
34638
|
-
{ action: "write", target: taskFilePath(runtime.cwd, taskId) }
|
|
35280
|
+
{ action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" }
|
|
34639
35281
|
],
|
|
34640
|
-
`
|
|
35282
|
+
`create workflow ${target.workflowId} for task ${taskId}; task status stays ${task.status} until work actually starts`
|
|
34641
35283
|
);
|
|
34642
35284
|
return 0;
|
|
34643
35285
|
}
|
|
34644
|
-
|
|
34645
|
-
if (exitCode === 0) {
|
|
34646
|
-
updateTaskStatus(runtime.cwd, taskId, "in_progress");
|
|
34647
|
-
}
|
|
34648
|
-
return exitCode;
|
|
35286
|
+
return await cmdKxmRun(runtime, workflow, [task.objective], true);
|
|
34649
35287
|
} catch (error) {
|
|
34650
35288
|
const message = error instanceof Error ? error.message : String(error);
|
|
34651
35289
|
runtime.io.stderr(`task run failed: ${message}
|
|
@@ -34679,12 +35317,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
|
|
|
34679
35317
|
const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
|
|
34680
35318
|
let filePath = workflowPath;
|
|
34681
35319
|
if (!filePath) {
|
|
34682
|
-
filePath =
|
|
35320
|
+
filePath = resolve23(projectRoot, ".kxm", "workflows", "default.yaml");
|
|
34683
35321
|
}
|
|
34684
35322
|
let yamlContent;
|
|
34685
35323
|
let workflowId = "default";
|
|
34686
|
-
if (
|
|
34687
|
-
yamlContent =
|
|
35324
|
+
if (existsSync28(filePath)) {
|
|
35325
|
+
yamlContent = readFileSync27(filePath, "utf8");
|
|
34688
35326
|
} else {
|
|
34689
35327
|
yamlContent = `schema: kxm.workflow.v1
|
|
34690
35328
|
description: Feature implementation workflow
|
|
@@ -34757,15 +35395,15 @@ async function cmdStudioServe(runtime, options) {
|
|
|
34757
35395
|
sessionToken,
|
|
34758
35396
|
planProvider: () => {
|
|
34759
35397
|
try {
|
|
34760
|
-
const wfDir =
|
|
34761
|
-
const defaultPath =
|
|
34762
|
-
let targetPath =
|
|
34763
|
-
if (!targetPath &&
|
|
35398
|
+
const wfDir = join32(runtime.cwd, ".kxm", "workflows");
|
|
35399
|
+
const defaultPath = join32(wfDir, "default.yaml");
|
|
35400
|
+
let targetPath = existsSync28(defaultPath) ? defaultPath : void 0;
|
|
35401
|
+
if (!targetPath && existsSync28(wfDir)) {
|
|
34764
35402
|
const yml = readdirSync10(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
34765
|
-
if (yml) targetPath =
|
|
35403
|
+
if (yml) targetPath = join32(wfDir, yml);
|
|
34766
35404
|
}
|
|
34767
|
-
if (targetPath &&
|
|
34768
|
-
const raw =
|
|
35405
|
+
if (targetPath && existsSync28(targetPath)) {
|
|
35406
|
+
const raw = readFileSync27(targetPath, "utf8");
|
|
34769
35407
|
const parsed = (0, import_yaml15.parse)(raw);
|
|
34770
35408
|
const wfId = basename7(targetPath).replace(/\.(yaml|yml)$/, "");
|
|
34771
35409
|
return compileKxmWorkflow({ id: wfId, value: parsed });
|
|
@@ -34815,16 +35453,16 @@ Press Ctrl+C to stop.
|
|
|
34815
35453
|
}
|
|
34816
35454
|
|
|
34817
35455
|
// plugins/kxm/src/cli/context-skills.ts
|
|
34818
|
-
import { readFileSync as
|
|
34819
|
-
import { join as
|
|
35456
|
+
import { readFileSync as readFileSync28 } from "node:fs";
|
|
35457
|
+
import { join as join34, relative as relative6, resolve as resolve24 } from "node:path";
|
|
34820
35458
|
|
|
34821
35459
|
// plugins/kxm/src/wiki.ts
|
|
34822
35460
|
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync21 } from "node:fs";
|
|
34823
|
-
import { join as
|
|
35461
|
+
import { join as join33 } from "node:path";
|
|
34824
35462
|
function writeCompiledWiki(root, wiki) {
|
|
34825
35463
|
const written = [];
|
|
34826
35464
|
for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
|
|
34827
|
-
const absolute =
|
|
35465
|
+
const absolute = join33(root, relativePath);
|
|
34828
35466
|
mkdirSync23(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
|
|
34829
35467
|
writeFileSync21(absolute, content);
|
|
34830
35468
|
written.push(relativePath);
|
|
@@ -34976,7 +35614,7 @@ async function cmdContextWikiCompile(runtime, project, options) {
|
|
|
34976
35614
|
printPlan(
|
|
34977
35615
|
runtime,
|
|
34978
35616
|
{ command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, outDir },
|
|
34979
|
-
compiled.pages.map((page) => ({ action: "write", target:
|
|
35617
|
+
compiled.pages.map((page) => ({ action: "write", target: resolve24(outDir, page.path) })),
|
|
34980
35618
|
`write ${compiled.pages.length} compiled wiki page(s) to ${outDir}`
|
|
34981
35619
|
);
|
|
34982
35620
|
return 0;
|
|
@@ -35011,7 +35649,7 @@ function skillStateFromFlag(value) {
|
|
|
35011
35649
|
throw new Error(`invalid skill state ${value}`);
|
|
35012
35650
|
}
|
|
35013
35651
|
function skillsRoot(runtime) {
|
|
35014
|
-
return
|
|
35652
|
+
return join34(runtime.dirs.workdir, ".kxm", "skills");
|
|
35015
35653
|
}
|
|
35016
35654
|
function csv(value) {
|
|
35017
35655
|
if (!value) return void 0;
|
|
@@ -35028,7 +35666,7 @@ async function cmdSkillsCreate(runtime, options) {
|
|
|
35028
35666
|
const metadata = lifecycle.create({
|
|
35029
35667
|
name: options.name,
|
|
35030
35668
|
description: options.description ?? "",
|
|
35031
|
-
content:
|
|
35669
|
+
content: readFileSync28(options.file, "utf8"),
|
|
35032
35670
|
createdBy: options.createdBy,
|
|
35033
35671
|
sources: {
|
|
35034
35672
|
runIds: csv(options.run) ?? [],
|
|
@@ -35190,7 +35828,7 @@ async function cmdMemorySync(runtime) {
|
|
|
35190
35828
|
printPlan(
|
|
35191
35829
|
runtime,
|
|
35192
35830
|
{ command: "memory sync", ...result },
|
|
35193
|
-
result.updated.map((file) => ({ action: "write", target:
|
|
35831
|
+
result.updated.map((file) => ({ action: "write", target: join34(runtime.cwd, file) })),
|
|
35194
35832
|
"regenerate the project memory block in the instruction files this project already has"
|
|
35195
35833
|
);
|
|
35196
35834
|
return 0;
|
|
@@ -35218,8 +35856,8 @@ ${lines.join("\n")}`
|
|
|
35218
35856
|
|
|
35219
35857
|
// plugins/kxm/src/cli/hub.ts
|
|
35220
35858
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
35221
|
-
import { existsSync as
|
|
35222
|
-
import { join as
|
|
35859
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync27, readFileSync as readFileSync34, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
|
|
35860
|
+
import { join as join44, resolve as resolve27 } from "node:path";
|
|
35223
35861
|
|
|
35224
35862
|
// node_modules/marked/lib/marked.esm.js
|
|
35225
35863
|
function M() {
|
|
@@ -39381,10 +40019,10 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39381
40019
|
* @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
|
|
39382
40020
|
*/
|
|
39383
40021
|
queryTerminalBackgroundColor({ timeoutMs }) {
|
|
39384
|
-
return new Promise((
|
|
40022
|
+
return new Promise((resolve34) => {
|
|
39385
40023
|
const query = {
|
|
39386
40024
|
settled: false,
|
|
39387
|
-
resolve:
|
|
40025
|
+
resolve: resolve34,
|
|
39388
40026
|
timer: void 0
|
|
39389
40027
|
};
|
|
39390
40028
|
query.timer = setTimeout(() => {
|
|
@@ -39407,7 +40045,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39407
40045
|
* `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
|
|
39408
40046
|
*/
|
|
39409
40047
|
queryTerminalColorScheme({ timeoutMs }) {
|
|
39410
|
-
return new Promise((
|
|
40048
|
+
return new Promise((resolve34) => {
|
|
39411
40049
|
let settled = false;
|
|
39412
40050
|
let timer;
|
|
39413
40051
|
let unsubscribe = () => {
|
|
@@ -39421,7 +40059,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39421
40059
|
timer = void 0;
|
|
39422
40060
|
}
|
|
39423
40061
|
unsubscribe();
|
|
39424
|
-
|
|
40062
|
+
resolve34(scheme);
|
|
39425
40063
|
};
|
|
39426
40064
|
unsubscribe = this.onTerminalColorSchemeChange(settle);
|
|
39427
40065
|
timer = setTimeout(() => settle(void 0), timeoutMs);
|
|
@@ -41152,19 +41790,19 @@ import * as path2 from "node:path";
|
|
|
41152
41790
|
|
|
41153
41791
|
// node_modules/@earendil-works/pi-tui/dist/native-module-path.js
|
|
41154
41792
|
import { createRequire as createRequire2 } from "node:module";
|
|
41155
|
-
import { dirname as
|
|
41793
|
+
import { dirname as dirname18, join as join35 } from "node:path";
|
|
41156
41794
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
41157
41795
|
var moduleRequire = createRequire2(import.meta.url);
|
|
41158
41796
|
var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
|
|
41159
41797
|
function getNativeModuleCandidates(nativePath, options = {}) {
|
|
41160
|
-
const moduleDir =
|
|
41798
|
+
const moduleDir = dirname18(fileURLToPath2(options.moduleUrl ?? import.meta.url));
|
|
41161
41799
|
const candidates = [];
|
|
41162
41800
|
try {
|
|
41163
41801
|
const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
|
|
41164
|
-
candidates.push(
|
|
41802
|
+
candidates.push(join35(dirname18(packageEntry), "..", nativePath));
|
|
41165
41803
|
} catch {
|
|
41166
41804
|
}
|
|
41167
|
-
candidates.push(
|
|
41805
|
+
candidates.push(join35(moduleDir, "..", nativePath), join35(moduleDir, nativePath), join35(dirname18(options.execPath ?? process.execPath), nativePath));
|
|
41168
41806
|
return Array.from(new Set(candidates));
|
|
41169
41807
|
}
|
|
41170
41808
|
|
|
@@ -41505,7 +42143,7 @@ var ProcessTerminal = class {
|
|
|
41505
42143
|
break;
|
|
41506
42144
|
if (now - lastDataTime >= idleMs)
|
|
41507
42145
|
break;
|
|
41508
|
-
await new Promise((
|
|
42146
|
+
await new Promise((resolve34) => setTimeout(resolve34, Math.min(idleMs, timeLeft)));
|
|
41509
42147
|
}
|
|
41510
42148
|
} finally {
|
|
41511
42149
|
process.stdin.removeListener("data", onData);
|
|
@@ -43621,7 +44259,7 @@ var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
|
|
|
43621
44259
|
|
|
43622
44260
|
// plugins/kxm/src/tui.ts
|
|
43623
44261
|
import { mkdirSync as mkdirSync24 } from "node:fs";
|
|
43624
|
-
import { join as
|
|
44262
|
+
import { join as join39, resolve as resolve26, dirname as dirname19 } from "node:path";
|
|
43625
44263
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
43626
44264
|
|
|
43627
44265
|
// packages/core/tui/src/types/surface.ts
|
|
@@ -43762,9 +44400,9 @@ var SHARED_EFFECT_KINDS = Object.freeze([
|
|
|
43762
44400
|
]);
|
|
43763
44401
|
|
|
43764
44402
|
// plugins/kxm/src/local-snapshot.ts
|
|
43765
|
-
import { existsSync as
|
|
44403
|
+
import { existsSync as existsSync29, readdirSync as readdirSync11, readFileSync as readFileSync29 } from "node:fs";
|
|
43766
44404
|
import { homedir as homedir6 } from "node:os";
|
|
43767
|
-
import { isAbsolute as isAbsolute9, join as
|
|
44405
|
+
import { isAbsolute as isAbsolute9, join as join38, resolve as resolve25 } from "node:path";
|
|
43768
44406
|
var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
|
|
43769
44407
|
var RUNTIME_PROJECT_KEY = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
43770
44408
|
function processExists2(pid) {
|
|
@@ -43889,36 +44527,36 @@ function readOpenMessageMetadata(database, where = OPEN_MESSAGE_WHERE, params =
|
|
|
43889
44527
|
return messages;
|
|
43890
44528
|
}
|
|
43891
44529
|
function resolveKxmSnapshotPaths(cwd, env = process.env) {
|
|
43892
|
-
const stateDir = env.KXM_STATE_DIR?.trim() ||
|
|
44530
|
+
const stateDir = env.KXM_STATE_DIR?.trim() || join38(cwd, ".kxm", "state");
|
|
43893
44531
|
const configured = env.KXM_DATA_PATH?.trim();
|
|
43894
|
-
const dataPath = configured ?
|
|
44532
|
+
const dataPath = configured ? resolve25(cwd, configured) : join38(stateDir, "kxm.db");
|
|
43895
44533
|
return { dataPath, stateDir };
|
|
43896
44534
|
}
|
|
43897
44535
|
function resolveKxmStateRoot(stateDir, options) {
|
|
43898
|
-
if (options?.kxmStateRoot &&
|
|
43899
|
-
return
|
|
44536
|
+
if (options?.kxmStateRoot && existsSync29(options.kxmStateRoot)) {
|
|
44537
|
+
return resolve25(options.kxmStateRoot);
|
|
43900
44538
|
}
|
|
43901
|
-
if (
|
|
44539
|
+
if (existsSync29(join38(stateDir, "runtime", "registry.db")) || existsSync29(join38(stateDir, "runtime", "projects"))) {
|
|
43902
44540
|
return stateDir;
|
|
43903
44541
|
}
|
|
43904
44542
|
const env = options?.env ?? process.env;
|
|
43905
44543
|
const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
|
|
43906
|
-
if (explicit && isAbsolute9(explicit) &&
|
|
43907
|
-
return
|
|
44544
|
+
if (explicit && isAbsolute9(explicit) && existsSync29(resolve25(explicit))) {
|
|
44545
|
+
return resolve25(explicit);
|
|
43908
44546
|
}
|
|
43909
44547
|
let base;
|
|
43910
44548
|
if (process.platform === "win32") {
|
|
43911
44549
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
43912
|
-
base = localAppData && isAbsolute9(localAppData) ? localAppData :
|
|
43913
|
-
base =
|
|
44550
|
+
base = localAppData && isAbsolute9(localAppData) ? localAppData : join38(homedir6(), "AppData", "Local");
|
|
44551
|
+
base = resolve25(base, "KXM");
|
|
43914
44552
|
} else if (process.platform === "darwin") {
|
|
43915
|
-
base =
|
|
44553
|
+
base = resolve25(homedir6(), "Library", "Application Support", "KXM");
|
|
43916
44554
|
} else {
|
|
43917
44555
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
43918
|
-
base = xdgState && isAbsolute9(xdgState) ? xdgState :
|
|
43919
|
-
base =
|
|
44556
|
+
base = xdgState && isAbsolute9(xdgState) ? xdgState : join38(homedir6(), ".local", "state");
|
|
44557
|
+
base = resolve25(base, "kxm");
|
|
43920
44558
|
}
|
|
43921
|
-
if (
|
|
44559
|
+
if (existsSync29(base)) return base;
|
|
43922
44560
|
return void 0;
|
|
43923
44561
|
}
|
|
43924
44562
|
function readRuntimeRuns(eventDb, projectId) {
|
|
@@ -43951,7 +44589,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
43951
44589
|
let legacyRuns = [];
|
|
43952
44590
|
let legacyRunTotal = 0;
|
|
43953
44591
|
let plans = [];
|
|
43954
|
-
if (
|
|
44592
|
+
if (existsSync29(dataPath)) {
|
|
43955
44593
|
hasLegacy = true;
|
|
43956
44594
|
const database = openReadOnlyDatabase(dataPath);
|
|
43957
44595
|
try {
|
|
@@ -43982,11 +44620,11 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
43982
44620
|
let kxmRunTotal = 0;
|
|
43983
44621
|
const kxmStateRoot = scope && !scope.runtimeProjectId ? void 0 : resolveKxmStateRoot(stateDir, options);
|
|
43984
44622
|
if (kxmStateRoot) {
|
|
43985
|
-
const runtimeDir =
|
|
43986
|
-
const registryDbPath =
|
|
43987
|
-
const projectsDir =
|
|
44623
|
+
const runtimeDir = join38(kxmStateRoot, "runtime");
|
|
44624
|
+
const registryDbPath = join38(runtimeDir, "registry.db");
|
|
44625
|
+
const projectsDir = join38(runtimeDir, "projects");
|
|
43988
44626
|
const projectKeys = /* @__PURE__ */ new Set();
|
|
43989
|
-
if (
|
|
44627
|
+
if (existsSync29(registryDbPath)) {
|
|
43990
44628
|
hasKxm = true;
|
|
43991
44629
|
try {
|
|
43992
44630
|
const regDb = openReadOnlyDatabase(registryDbPath);
|
|
@@ -44002,7 +44640,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44002
44640
|
} catch {
|
|
44003
44641
|
}
|
|
44004
44642
|
}
|
|
44005
|
-
if (!scope &&
|
|
44643
|
+
if (!scope && existsSync29(projectsDir)) {
|
|
44006
44644
|
try {
|
|
44007
44645
|
for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
|
|
44008
44646
|
if (entry.isDirectory()) {
|
|
@@ -44014,8 +44652,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44014
44652
|
}
|
|
44015
44653
|
for (const key of projectKeys) {
|
|
44016
44654
|
if (scope && !RUNTIME_PROJECT_KEY.test(key)) continue;
|
|
44017
|
-
const eventDbPath =
|
|
44018
|
-
if (
|
|
44655
|
+
const eventDbPath = join38(projectsDir, key, "run-events.db");
|
|
44656
|
+
if (existsSync29(eventDbPath)) {
|
|
44019
44657
|
hasKxm = true;
|
|
44020
44658
|
try {
|
|
44021
44659
|
const eventDb = openReadOnlyDatabase(eventDbPath);
|
|
@@ -44033,10 +44671,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44033
44671
|
}
|
|
44034
44672
|
}
|
|
44035
44673
|
const pids = [];
|
|
44036
|
-
if (
|
|
44674
|
+
if (existsSync29(stateDir)) {
|
|
44037
44675
|
for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
|
|
44038
44676
|
try {
|
|
44039
|
-
const record = JSON.parse(
|
|
44677
|
+
const record = JSON.parse(readFileSync29(join38(stateDir, file), "utf8"));
|
|
44040
44678
|
pids.push({
|
|
44041
44679
|
file,
|
|
44042
44680
|
...record.role ? { role: record.role } : {},
|
|
@@ -44075,8 +44713,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44075
44713
|
} else {
|
|
44076
44714
|
source = "legacy";
|
|
44077
44715
|
}
|
|
44078
|
-
const telemetryFile =
|
|
44079
|
-
const spend =
|
|
44716
|
+
const telemetryFile = join38(stateDir, "telemetry.jsonl");
|
|
44717
|
+
const spend = existsSync29(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
|
|
44080
44718
|
return {
|
|
44081
44719
|
source,
|
|
44082
44720
|
agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
|
|
@@ -44210,11 +44848,11 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
|
|
|
44210
44848
|
const cleanRunId = runId.replace(/^run_/, "");
|
|
44211
44849
|
const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
|
|
44212
44850
|
const branchName = `kxm/run-${cleanRunId}-${desc}`;
|
|
44213
|
-
const relativeWtPath =
|
|
44214
|
-
const absoluteWtPath =
|
|
44851
|
+
const relativeWtPath = join39(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
|
|
44852
|
+
const absoluteWtPath = resolve26(repoRoot, relativeWtPath);
|
|
44215
44853
|
const jumpCommand = `cd "${absoluteWtPath}"`;
|
|
44216
44854
|
try {
|
|
44217
|
-
mkdirSync24(
|
|
44855
|
+
mkdirSync24(dirname19(absoluteWtPath), { recursive: true });
|
|
44218
44856
|
const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
|
|
44219
44857
|
let wtRes;
|
|
44220
44858
|
if (branchCheck.status === 0) {
|
|
@@ -44612,11 +45250,11 @@ async function readJson(response) {
|
|
|
44612
45250
|
}
|
|
44613
45251
|
async function waitForReconnect(signal, milliseconds = 1e3) {
|
|
44614
45252
|
if (signal.aborted) return;
|
|
44615
|
-
await new Promise((
|
|
45253
|
+
await new Promise((resolve34) => {
|
|
44616
45254
|
const done = () => {
|
|
44617
45255
|
clearTimeout(timer);
|
|
44618
45256
|
signal.removeEventListener("abort", done);
|
|
44619
|
-
|
|
45257
|
+
resolve34();
|
|
44620
45258
|
};
|
|
44621
45259
|
const timer = setTimeout(done, milliseconds);
|
|
44622
45260
|
timer.unref();
|
|
@@ -44979,8 +45617,8 @@ async function runMeshTui(input) {
|
|
|
44979
45617
|
// plugins/kxm/src/session-work.ts
|
|
44980
45618
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
44981
45619
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
44982
|
-
import { existsSync as
|
|
44983
|
-
import { join as
|
|
45620
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync25, readFileSync as readFileSync30, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
|
|
45621
|
+
import { join as join40 } from "node:path";
|
|
44984
45622
|
var MAX_SESSION_BRIEF_TASKS = 5;
|
|
44985
45623
|
var MAX_SESSION_BRIEF_PLANS = 5;
|
|
44986
45624
|
var SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
|
|
@@ -45137,10 +45775,10 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
|
|
|
45137
45775
|
};
|
|
45138
45776
|
}
|
|
45139
45777
|
function readCachedSessionBrief(stateDir) {
|
|
45140
|
-
const file =
|
|
45778
|
+
const file = join40(stateDir, "session-brief.json");
|
|
45141
45779
|
try {
|
|
45142
|
-
if (!
|
|
45143
|
-
const raw =
|
|
45780
|
+
if (!existsSync30(file)) return void 0;
|
|
45781
|
+
const raw = readFileSync30(file, "utf8");
|
|
45144
45782
|
const parsed = JSON.parse(raw);
|
|
45145
45783
|
if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
|
|
45146
45784
|
const ageMs = Date.now() - Date.parse(parsed.generatedAt);
|
|
@@ -45156,7 +45794,7 @@ function readCachedSessionBrief(stateDir) {
|
|
|
45156
45794
|
function writeCachedSessionBrief(stateDir, brief) {
|
|
45157
45795
|
try {
|
|
45158
45796
|
mkdirSync25(stateDir, { recursive: true, mode: 448 });
|
|
45159
|
-
const file =
|
|
45797
|
+
const file = join40(stateDir, "session-brief.json");
|
|
45160
45798
|
const tmp = `${file}.tmp.${randomUUID12().slice(0, 8)}`;
|
|
45161
45799
|
writeFileSync22(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
|
|
45162
45800
|
renameSync8(tmp, file);
|
|
@@ -45165,8 +45803,8 @@ function writeCachedSessionBrief(stateDir, brief) {
|
|
|
45165
45803
|
}
|
|
45166
45804
|
function estimateSessionCost(stateDir) {
|
|
45167
45805
|
try {
|
|
45168
|
-
const telemetryFile =
|
|
45169
|
-
if (!
|
|
45806
|
+
const telemetryFile = join40(stateDir, "telemetry.jsonl");
|
|
45807
|
+
if (!existsSync30(telemetryFile)) return void 0;
|
|
45170
45808
|
const records = readRoutingRecords(telemetryFile);
|
|
45171
45809
|
if (records.length === 0) return void 0;
|
|
45172
45810
|
let totalCost = 0;
|
|
@@ -45334,11 +45972,11 @@ function formatSessionBriefText(brief) {
|
|
|
45334
45972
|
}
|
|
45335
45973
|
|
|
45336
45974
|
// plugins/kxm/src/kxm-install-kind.ts
|
|
45337
|
-
import { existsSync as
|
|
45338
|
-
import { dirname as
|
|
45975
|
+
import { existsSync as existsSync31, readFileSync as readFileSync31 } from "node:fs";
|
|
45976
|
+
import { dirname as dirname20, join as join41 } from "node:path";
|
|
45339
45977
|
function packageIsKxm(root) {
|
|
45340
45978
|
try {
|
|
45341
|
-
const pkg = JSON.parse(
|
|
45979
|
+
const pkg = JSON.parse(readFileSync31(join41(root, "package.json"), "utf8"));
|
|
45342
45980
|
return pkg.name === "@kontextmind/kxm";
|
|
45343
45981
|
} catch {
|
|
45344
45982
|
return false;
|
|
@@ -45387,12 +46025,12 @@ function report(kind, root) {
|
|
|
45387
46025
|
return { kind, root, instruction: installKindInstruction(kind, root) };
|
|
45388
46026
|
}
|
|
45389
46027
|
function classifyInstallRoot(probe) {
|
|
45390
|
-
const piGitRoot =
|
|
46028
|
+
const piGitRoot = join41(probe.homeDir, ".pi", "agent", "git");
|
|
45391
46029
|
if (isUnder(probe.repoRoot, piGitRoot, probe.platform)) return report("pi-git", probe.repoRoot);
|
|
45392
|
-
const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
45393
|
-
const pluginsRoot =
|
|
46030
|
+
const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join41(probe.homeDir, ".claude");
|
|
46031
|
+
const pluginsRoot = join41(claudeHome, "plugins");
|
|
45394
46032
|
if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
|
|
45395
|
-
const hasGit =
|
|
46033
|
+
const hasGit = existsSync31(join41(probe.repoRoot, ".git"));
|
|
45396
46034
|
if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
|
|
45397
46035
|
if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
|
|
45398
46036
|
return report("npm-package", probe.repoRoot);
|
|
@@ -45406,21 +46044,21 @@ function resolveInstallKind(probe, npmGlobalRoot) {
|
|
|
45406
46044
|
if (typeof globalRoot !== "string" || globalRoot.trim() === "") {
|
|
45407
46045
|
return report("unknown", classified.root);
|
|
45408
46046
|
}
|
|
45409
|
-
const expected =
|
|
46047
|
+
const expected = dirname20(dirname20(classified.root));
|
|
45410
46048
|
if (sameHostPath2(globalRoot.trim(), expected, probe.platform)) return report("npm-global", classified.root);
|
|
45411
46049
|
return report("npm-local", classified.root);
|
|
45412
46050
|
}
|
|
45413
46051
|
|
|
45414
46052
|
// plugins/kxm/src/kxm-update-config.ts
|
|
45415
46053
|
var import_yaml16 = __toESM(require_dist(), 1);
|
|
45416
|
-
import { existsSync as
|
|
45417
|
-
import { join as
|
|
46054
|
+
import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
|
|
46055
|
+
import { join as join42 } from "node:path";
|
|
45418
46056
|
function loadKxmUpdateConfig(env = process.env) {
|
|
45419
|
-
const path4 =
|
|
45420
|
-
if (!
|
|
46057
|
+
const path4 = join42(kxmUserStateRoot({ env }), "update.yaml");
|
|
46058
|
+
if (!existsSync32(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
|
|
45421
46059
|
let parsed;
|
|
45422
46060
|
try {
|
|
45423
|
-
parsed = (0, import_yaml16.parse)(
|
|
46061
|
+
parsed = (0, import_yaml16.parse)(readFileSync32(path4, "utf8"));
|
|
45424
46062
|
} catch {
|
|
45425
46063
|
throw new KxmUpdateConfigError("update.yaml is not valid YAML");
|
|
45426
46064
|
}
|
|
@@ -45446,8 +46084,8 @@ function loadKxmUpdateConfig(env = process.env) {
|
|
|
45446
46084
|
}
|
|
45447
46085
|
|
|
45448
46086
|
// plugins/kxm/src/session.ts
|
|
45449
|
-
import { existsSync as
|
|
45450
|
-
import { join as
|
|
46087
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync33, writeFileSync as writeFileSync23 } from "node:fs";
|
|
46088
|
+
import { join as join43 } from "node:path";
|
|
45451
46089
|
var SESSION_SCHEMA = "kxm.session.v1";
|
|
45452
46090
|
var SessionConfigError = class extends Error {
|
|
45453
46091
|
code = "session_config_invalid";
|
|
@@ -45457,12 +46095,12 @@ var SessionConfigError = class extends Error {
|
|
|
45457
46095
|
}
|
|
45458
46096
|
};
|
|
45459
46097
|
function workflowAssetDirs(assetsDir, workflowId) {
|
|
45460
|
-
const root =
|
|
45461
|
-
return [root,
|
|
46098
|
+
const root = join43(assetsDir, "workflows", workflowId);
|
|
46099
|
+
return [root, join43(root, "inputs"), join43(root, "outputs"), join43(root, "generated")];
|
|
45462
46100
|
}
|
|
45463
46101
|
function sessionAssetDirs(assetsDir, sessionId) {
|
|
45464
|
-
const root =
|
|
45465
|
-
return [root,
|
|
46102
|
+
const root = join43(assetsDir, "sessions", sessionId);
|
|
46103
|
+
return [root, join43(root, "inputs"), join43(root, "outputs")];
|
|
45466
46104
|
}
|
|
45467
46105
|
function rosterNames(configDir) {
|
|
45468
46106
|
return [...loadRosterMap(configDir).values()].map((row) => String(row.name));
|
|
@@ -45512,7 +46150,7 @@ function workerFromRosterRow(name, row, project) {
|
|
|
45512
46150
|
function loadRosterMap(configDir) {
|
|
45513
46151
|
const byName = /* @__PURE__ */ new Map();
|
|
45514
46152
|
for (const [file, key] of [["agents.json", "agents"], ["gates.json", "gates"]]) {
|
|
45515
|
-
const path4 =
|
|
46153
|
+
const path4 = join43(configDir, file);
|
|
45516
46154
|
for (const row of loadRoster(path4, key)) {
|
|
45517
46155
|
const name = String(row.name).trim();
|
|
45518
46156
|
const lowered = name.toLowerCase();
|
|
@@ -45526,10 +46164,10 @@ function loadRosterMap(configDir) {
|
|
|
45526
46164
|
return byName;
|
|
45527
46165
|
}
|
|
45528
46166
|
function loadRoster(path4, key) {
|
|
45529
|
-
if (!
|
|
46167
|
+
if (!existsSync33(path4)) return [];
|
|
45530
46168
|
let parsed;
|
|
45531
46169
|
try {
|
|
45532
|
-
parsed = JSON.parse(
|
|
46170
|
+
parsed = JSON.parse(readFileSync33(path4, "utf8"));
|
|
45533
46171
|
} catch (error) {
|
|
45534
46172
|
throw new SessionConfigError(
|
|
45535
46173
|
`${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -45554,7 +46192,7 @@ function loadRoster(path4, key) {
|
|
|
45554
46192
|
});
|
|
45555
46193
|
}
|
|
45556
46194
|
function createSession(input) {
|
|
45557
|
-
const assetDir = input.mode === "workflow" && input.workflowId ?
|
|
46195
|
+
const assetDir = input.mode === "workflow" && input.workflowId ? join43("assets", "workflows", input.workflowId) : join43("assets", "sessions", input.id);
|
|
45558
46196
|
return {
|
|
45559
46197
|
schema: SESSION_SCHEMA,
|
|
45560
46198
|
id: input.id,
|
|
@@ -45567,8 +46205,8 @@ function createSession(input) {
|
|
|
45567
46205
|
};
|
|
45568
46206
|
}
|
|
45569
46207
|
function writeSession(assetsDir, session, dryRun = false) {
|
|
45570
|
-
const dir =
|
|
45571
|
-
const path4 =
|
|
46208
|
+
const dir = join43(assetsDir, "sessions", session.id);
|
|
46209
|
+
const path4 = join43(dir, "session.json");
|
|
45572
46210
|
if (!dryRun) {
|
|
45573
46211
|
mkdirSync26(dir, { recursive: true });
|
|
45574
46212
|
writeFileSync23(path4, `${JSON.stringify(session, null, 2)}
|
|
@@ -45579,7 +46217,7 @@ function writeSession(assetsDir, session, dryRun = false) {
|
|
|
45579
46217
|
|
|
45580
46218
|
// plugins/kxm/src/cli/hub.ts
|
|
45581
46219
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
45582
|
-
import { dirname as
|
|
46220
|
+
import { dirname as dirname21 } from "node:path";
|
|
45583
46221
|
import { homedir as homedir7 } from "node:os";
|
|
45584
46222
|
async function hubGet(url, fetchImpl) {
|
|
45585
46223
|
try {
|
|
@@ -45597,7 +46235,7 @@ async function hubGet(url, fetchImpl) {
|
|
|
45597
46235
|
}
|
|
45598
46236
|
function installProbeFrom(runtime) {
|
|
45599
46237
|
const partial = runtime.io.installProbe ?? {};
|
|
45600
|
-
const moduleDir = partial.moduleDir ??
|
|
46238
|
+
const moduleDir = partial.moduleDir ?? dirname21(fileURLToPath3(import.meta.url));
|
|
45601
46239
|
return {
|
|
45602
46240
|
moduleDir,
|
|
45603
46241
|
// Classify the install that is **running**, never the directory the caller
|
|
@@ -45618,13 +46256,13 @@ function moduleInstallRoot() {
|
|
|
45618
46256
|
try {
|
|
45619
46257
|
return findKxmRepoRoot(import.meta.url);
|
|
45620
46258
|
} catch {
|
|
45621
|
-
return
|
|
46259
|
+
return dirname21(fileURLToPath3(import.meta.url));
|
|
45622
46260
|
}
|
|
45623
46261
|
}
|
|
45624
46262
|
function warnIgnoredProjectUpdateYaml(runtime) {
|
|
45625
|
-
const projectFile =
|
|
45626
|
-
if (!
|
|
45627
|
-
const userFile =
|
|
46263
|
+
const projectFile = join44(runtime.dirs.workspace, "update.yaml");
|
|
46264
|
+
if (!existsSync34(projectFile)) return;
|
|
46265
|
+
const userFile = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
45628
46266
|
runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
|
|
45629
46267
|
`);
|
|
45630
46268
|
}
|
|
@@ -45672,7 +46310,7 @@ async function cmdDash(runtime, options = {}) {
|
|
|
45672
46310
|
return 2;
|
|
45673
46311
|
}
|
|
45674
46312
|
const screen = requested;
|
|
45675
|
-
const dataPath =
|
|
46313
|
+
const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
|
|
45676
46314
|
if (runtime.dryRun) {
|
|
45677
46315
|
print(runtime.io, runtime.json, {
|
|
45678
46316
|
ok: true,
|
|
@@ -45718,7 +46356,7 @@ async function cmdHub(runtime) {
|
|
|
45718
46356
|
config = loadKxmUpdateConfig(runtime.env);
|
|
45719
46357
|
} catch (error) {
|
|
45720
46358
|
if (error instanceof KxmUpdateConfigError) {
|
|
45721
|
-
const yamlPath =
|
|
46359
|
+
const yamlPath = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
45722
46360
|
runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
|
|
45723
46361
|
`);
|
|
45724
46362
|
} else {
|
|
@@ -45905,7 +46543,7 @@ async function cmdWorker(runtime, options) {
|
|
|
45905
46543
|
return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
|
|
45906
46544
|
}
|
|
45907
46545
|
async function cmdStop(runtime, waitMsFlag) {
|
|
45908
|
-
const pids =
|
|
46546
|
+
const pids = existsSync34(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
|
|
45909
46547
|
if (runtime.dryRun) {
|
|
45910
46548
|
print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
|
|
45911
46549
|
return 0;
|
|
@@ -45920,7 +46558,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45920
46558
|
const orphans = [];
|
|
45921
46559
|
for (const file of pids) {
|
|
45922
46560
|
try {
|
|
45923
|
-
const record = JSON.parse(
|
|
46561
|
+
const record = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
|
|
45924
46562
|
const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
|
|
45925
46563
|
const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
|
|
45926
46564
|
if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
|
|
@@ -45940,7 +46578,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45940
46578
|
ignored.push(file);
|
|
45941
46579
|
continue;
|
|
45942
46580
|
}
|
|
45943
|
-
writeFileSync24(
|
|
46581
|
+
writeFileSync24(join44(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
45944
46582
|
`, { encoding: "utf8", mode: 384 });
|
|
45945
46583
|
requested.push(file);
|
|
45946
46584
|
records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
|
|
@@ -45958,7 +46596,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45958
46596
|
while (Date.now() <= deadline && stopped.size < requested.length) {
|
|
45959
46597
|
for (const [file, record] of records) {
|
|
45960
46598
|
try {
|
|
45961
|
-
const current = JSON.parse(
|
|
46599
|
+
const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
|
|
45962
46600
|
if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
|
|
45963
46601
|
} catch {
|
|
45964
46602
|
stopped.add(file);
|
|
@@ -45971,7 +46609,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45971
46609
|
for (const file of orphans) {
|
|
45972
46610
|
while (Date.now() <= deadlineOrphans) {
|
|
45973
46611
|
try {
|
|
45974
|
-
const current = JSON.parse(
|
|
46612
|
+
const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
|
|
45975
46613
|
if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
|
|
45976
46614
|
} catch {
|
|
45977
46615
|
break;
|
|
@@ -45979,14 +46617,14 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45979
46617
|
await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
|
|
45980
46618
|
}
|
|
45981
46619
|
try {
|
|
45982
|
-
const current = JSON.parse(
|
|
46620
|
+
const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
|
|
45983
46621
|
if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
|
|
45984
46622
|
try {
|
|
45985
46623
|
process.kill(current.serverPid, "SIGKILL");
|
|
45986
46624
|
} catch {
|
|
45987
46625
|
}
|
|
45988
46626
|
}
|
|
45989
|
-
rmSync11(
|
|
46627
|
+
rmSync11(join44(runtime.dirs.state, file), { force: true });
|
|
45990
46628
|
} catch {
|
|
45991
46629
|
}
|
|
45992
46630
|
stopped.add(file);
|
|
@@ -45997,11 +46635,11 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
45997
46635
|
}
|
|
45998
46636
|
async function cmdSessionStatus(runtime) {
|
|
45999
46637
|
const stateDir = runtime.dirs.state;
|
|
46000
|
-
const names3 =
|
|
46638
|
+
const names3 = existsSync34(stateDir) ? readdirSync12(stateDir) : [];
|
|
46001
46639
|
const claims = [];
|
|
46002
46640
|
for (const file of names3.filter((name) => name.endsWith(".pid"))) {
|
|
46003
46641
|
try {
|
|
46004
|
-
const record = JSON.parse(
|
|
46642
|
+
const record = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
|
|
46005
46643
|
claims.push({
|
|
46006
46644
|
file,
|
|
46007
46645
|
role: record.role,
|
|
@@ -46016,7 +46654,7 @@ async function cmdSessionStatus(runtime) {
|
|
|
46016
46654
|
const recoveries = [];
|
|
46017
46655
|
for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
|
|
46018
46656
|
try {
|
|
46019
|
-
const envelope = JSON.parse(
|
|
46657
|
+
const envelope = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
|
|
46020
46658
|
recoveries.push({
|
|
46021
46659
|
file,
|
|
46022
46660
|
reason: envelope.reason,
|
|
@@ -46042,7 +46680,7 @@ async function cmdSessionStatus(runtime) {
|
|
|
46042
46680
|
async function cmdAuthToken(runtime, options = {}) {
|
|
46043
46681
|
const tokenFile = sessionTokenPath(runtime.env.KXM_USER_CONFIG_DIR);
|
|
46044
46682
|
if (options.clear && runtime.dryRun) {
|
|
46045
|
-
const present =
|
|
46683
|
+
const present = existsSync34(tokenFile);
|
|
46046
46684
|
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");
|
|
46047
46685
|
return 0;
|
|
46048
46686
|
}
|
|
@@ -46131,7 +46769,7 @@ async function cmdSessionBrief(runtime, options = {}) {
|
|
|
46131
46769
|
print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
|
|
46132
46770
|
return 0;
|
|
46133
46771
|
}
|
|
46134
|
-
const dataPath =
|
|
46772
|
+
const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
|
|
46135
46773
|
const env = {
|
|
46136
46774
|
...runtime.env,
|
|
46137
46775
|
KXM_STATE_DIR: runtime.dirs.state,
|
|
@@ -46157,7 +46795,7 @@ async function cmdSessionBrief(runtime, options = {}) {
|
|
|
46157
46795
|
writeCache: !runtime.dryRun
|
|
46158
46796
|
});
|
|
46159
46797
|
if (runtime.dryRun) {
|
|
46160
|
-
if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target:
|
|
46798
|
+
if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join44(runtime.dirs.state, "session-brief.json") });
|
|
46161
46799
|
print(runtime.io, runtime.json, { ...brief, dryRun: true, planned }, [
|
|
46162
46800
|
options.status ? brief.statusLine : formatSessionBriefText(brief),
|
|
46163
46801
|
...planned.map((change) => `dry run: would ${change.action} ${change.target}`)
|
|
@@ -46232,16 +46870,16 @@ async function cmdSessionStart(runtime, options) {
|
|
|
46232
46870
|
|
|
46233
46871
|
// plugins/kxm/src/cli/system.ts
|
|
46234
46872
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
46235
|
-
import { createHash as
|
|
46236
|
-
import { existsSync as
|
|
46873
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
46874
|
+
import { existsSync as existsSync41, mkdtempSync as mkdtempSync3, readFileSync as readFileSync39, rmSync as rmSync13 } from "node:fs";
|
|
46237
46875
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
46238
|
-
import { join as
|
|
46876
|
+
import { basename as basename10, extname as extname3, join as join50, resolve as resolve32 } from "node:path";
|
|
46239
46877
|
import { createInterface as createInterface3 } from "node:readline";
|
|
46240
46878
|
|
|
46241
46879
|
// plugins/kxm/src/improve.ts
|
|
46242
|
-
import { createHash as
|
|
46243
|
-
import { existsSync as
|
|
46244
|
-
import { join as
|
|
46880
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
46881
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
|
|
46882
|
+
import { join as join45, relative as relative7, resolve as resolve28 } from "node:path";
|
|
46245
46883
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
46246
46884
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
46247
46885
|
function classifyCandidateKind(stepId, agentRole) {
|
|
@@ -46456,7 +47094,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
46456
47094
|
}
|
|
46457
47095
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
46458
47096
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
46459
|
-
const keyHash =
|
|
47097
|
+
const keyHash = createHash16("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
46460
47098
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
46461
47099
|
keyed.push({
|
|
46462
47100
|
key: group.key,
|
|
@@ -46496,8 +47134,8 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
46496
47134
|
}).map((entry) => entry.row);
|
|
46497
47135
|
}
|
|
46498
47136
|
function buildImprovementReport(records, options = {}) {
|
|
46499
|
-
const projectRoot = options.projectRoot ?
|
|
46500
|
-
const candidatesDir = options.candidatesDir ?
|
|
47137
|
+
const projectRoot = options.projectRoot ? resolve28(options.projectRoot) : process.cwd();
|
|
47138
|
+
const candidatesDir = options.candidatesDir ? resolve28(options.candidatesDir) : join45(projectRoot, ".kxm", "candidates");
|
|
46501
47139
|
const promotionPolicy = options.promotionPolicy ?? "manual_pr";
|
|
46502
47140
|
const groups = groupRoutingRecords(records, options);
|
|
46503
47141
|
const candidates = [];
|
|
@@ -46511,7 +47149,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46511
47149
|
group.workflowId
|
|
46512
47150
|
);
|
|
46513
47151
|
const diffFileName = `${group.candidateId}.diff`;
|
|
46514
|
-
const diffFilePath =
|
|
47152
|
+
const diffFilePath = join45(candidatesDir, diffFileName);
|
|
46515
47153
|
const relDiffPath = relative7(projectRoot, diffFilePath).replace(/\\/g, "/");
|
|
46516
47154
|
const candidate = {
|
|
46517
47155
|
schema: CANDIDATE_SCHEMA,
|
|
@@ -46533,11 +47171,11 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46533
47171
|
createdAt: nowIso()
|
|
46534
47172
|
};
|
|
46535
47173
|
if (!options.dryRun) {
|
|
46536
|
-
if (!
|
|
47174
|
+
if (!existsSync35(candidatesDir)) {
|
|
46537
47175
|
mkdirSync28(candidatesDir, { recursive: true });
|
|
46538
47176
|
}
|
|
46539
47177
|
writeFileSync25(diffFilePath, diff, "utf8");
|
|
46540
|
-
const jsonFilePath =
|
|
47178
|
+
const jsonFilePath = join45(candidatesDir, `${group.candidateId}.json`);
|
|
46541
47179
|
writeFileSync25(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
|
|
46542
47180
|
}
|
|
46543
47181
|
candidates.push(candidate);
|
|
@@ -46566,7 +47204,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46566
47204
|
}
|
|
46567
47205
|
function writeImprovementReport(improvementsDir, report2, dryRun = false) {
|
|
46568
47206
|
const stamp = report2.createdAt.replace(/[:.]/g, "-");
|
|
46569
|
-
const path4 =
|
|
47207
|
+
const path4 = join45(improvementsDir, `${stamp}.json`);
|
|
46570
47208
|
if (!dryRun) {
|
|
46571
47209
|
mkdirSync28(improvementsDir, { recursive: true });
|
|
46572
47210
|
writeFileSync25(path4, `${JSON.stringify(report2, null, 2)}
|
|
@@ -46645,8 +47283,8 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {})
|
|
|
46645
47283
|
}
|
|
46646
47284
|
|
|
46647
47285
|
// plugins/kxm/src/improve-sources.ts
|
|
46648
|
-
import { existsSync as
|
|
46649
|
-
import { resolve as
|
|
47286
|
+
import { existsSync as existsSync36 } from "node:fs";
|
|
47287
|
+
import { resolve as resolve29 } from "node:path";
|
|
46650
47288
|
var ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM events WHERE event_type IN ('routing.attempt.recorded','step.entered','run.status_changed') ORDER BY run_id, sequence";
|
|
46651
47289
|
var SIMULATED_HARNESS = "driver-simulated";
|
|
46652
47290
|
function parsePayload(text) {
|
|
@@ -46668,7 +47306,7 @@ function readEngineRoutingRecords(path4) {
|
|
|
46668
47306
|
undecided: 0,
|
|
46669
47307
|
duplicatesDropped: 0
|
|
46670
47308
|
};
|
|
46671
|
-
if (!
|
|
47309
|
+
if (!existsSync36(path4)) return { records: [], source };
|
|
46672
47310
|
source.exists = true;
|
|
46673
47311
|
let rows;
|
|
46674
47312
|
let database;
|
|
@@ -46744,7 +47382,7 @@ function readEngineRoutingRecords(path4) {
|
|
|
46744
47382
|
};
|
|
46745
47383
|
}
|
|
46746
47384
|
function readJsonlSource(kind, path4) {
|
|
46747
|
-
const exists =
|
|
47385
|
+
const exists = existsSync36(path4);
|
|
46748
47386
|
const records = exists ? readRoutingRecords(path4).map((entry) => entry.routing) : [];
|
|
46749
47387
|
return { records, source: { kind, path: path4, exists, records: records.length, duplicatesDropped: 0 } };
|
|
46750
47388
|
}
|
|
@@ -46757,7 +47395,7 @@ function loadRoutingSources(options) {
|
|
|
46757
47395
|
const projectRoot = discoverKxmProjectRoot(options.cwd);
|
|
46758
47396
|
const reads = [];
|
|
46759
47397
|
if (options.file !== void 0) {
|
|
46760
|
-
reads.push(readJsonlSource("file",
|
|
47398
|
+
reads.push(readJsonlSource("file", resolve29(options.cwd, options.file)));
|
|
46761
47399
|
} else {
|
|
46762
47400
|
if (projectRoot !== void 0) reads.push(readEngineRoutingRecords(kxmProjectRunEventsPath(projectRoot, options.env)));
|
|
46763
47401
|
reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
|
|
@@ -46788,8 +47426,8 @@ function loadRoutingSources(options) {
|
|
|
46788
47426
|
|
|
46789
47427
|
// plugins/kxm/src/modes.ts
|
|
46790
47428
|
var import_yaml17 = __toESM(require_dist(), 1);
|
|
46791
|
-
import { existsSync as
|
|
46792
|
-
import { join as
|
|
47429
|
+
import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
|
|
47430
|
+
import { join as join46 } from "node:path";
|
|
46793
47431
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
46794
47432
|
schema: "kxm.modes.v1",
|
|
46795
47433
|
majorModes: {
|
|
@@ -46852,12 +47490,12 @@ function loadModesConfig(projectRoot) {
|
|
|
46852
47490
|
if (!projectRoot) {
|
|
46853
47491
|
return DEFAULT_MODES_CONFIG;
|
|
46854
47492
|
}
|
|
46855
|
-
const modesPath =
|
|
46856
|
-
if (!
|
|
47493
|
+
const modesPath = join46(projectRoot, ".kxm", "modes.yaml");
|
|
47494
|
+
if (!existsSync37(modesPath)) {
|
|
46857
47495
|
return DEFAULT_MODES_CONFIG;
|
|
46858
47496
|
}
|
|
46859
47497
|
try {
|
|
46860
|
-
const raw =
|
|
47498
|
+
const raw = readFileSync36(modesPath, "utf8");
|
|
46861
47499
|
const parsed = (0, import_yaml17.parse)(raw);
|
|
46862
47500
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
46863
47501
|
return {
|
|
@@ -46943,11 +47581,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
46943
47581
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
46944
47582
|
});
|
|
46945
47583
|
for (const relPath of resolved.contextFiles) {
|
|
46946
|
-
const fullPath =
|
|
47584
|
+
const fullPath = join46(projectRoot, relPath);
|
|
46947
47585
|
let chars = 0;
|
|
46948
|
-
if (
|
|
47586
|
+
if (existsSync37(fullPath)) {
|
|
46949
47587
|
try {
|
|
46950
|
-
chars =
|
|
47588
|
+
chars = readFileSync36(fullPath, "utf8").length;
|
|
46951
47589
|
} catch {
|
|
46952
47590
|
chars = 0;
|
|
46953
47591
|
}
|
|
@@ -47079,9 +47717,9 @@ ${divider}
|
|
|
47079
47717
|
|
|
47080
47718
|
// plugins/kxm/src/ssh-remote.ts
|
|
47081
47719
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
47082
|
-
import { existsSync as
|
|
47720
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync29, readFileSync as readFileSync37, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
|
|
47083
47721
|
import { homedir as homedir8 } from "node:os";
|
|
47084
|
-
import { join as
|
|
47722
|
+
import { join as join47, resolve as resolve30 } from "node:path";
|
|
47085
47723
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
47086
47724
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
47087
47725
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -47107,12 +47745,12 @@ function truncateSshOutput(raw) {
|
|
|
47107
47745
|
return { text, truncated };
|
|
47108
47746
|
}
|
|
47109
47747
|
function parseSshConfig(configPath) {
|
|
47110
|
-
const targetPath = configPath ??
|
|
47111
|
-
if (!
|
|
47748
|
+
const targetPath = configPath ?? join47(homedir8(), ".ssh", "config");
|
|
47749
|
+
if (!existsSync38(targetPath)) {
|
|
47112
47750
|
return [];
|
|
47113
47751
|
}
|
|
47114
47752
|
try {
|
|
47115
|
-
const content =
|
|
47753
|
+
const content = readFileSync37(targetPath, "utf-8");
|
|
47116
47754
|
const lines = content.split("\n");
|
|
47117
47755
|
const hosts = [];
|
|
47118
47756
|
let currentHosts = [];
|
|
@@ -47189,15 +47827,15 @@ function resolveSshHostG(host, execFn = spawnSync7) {
|
|
|
47189
47827
|
}
|
|
47190
47828
|
}
|
|
47191
47829
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
47192
|
-
const resolved =
|
|
47193
|
-
if (!
|
|
47830
|
+
const resolved = resolve30(socketDir);
|
|
47831
|
+
if (!existsSync38(resolved)) {
|
|
47194
47832
|
mkdirSync29(resolved, { recursive: true, mode: 448 });
|
|
47195
47833
|
}
|
|
47196
47834
|
return resolved;
|
|
47197
47835
|
}
|
|
47198
47836
|
function buildSshArgs(options) {
|
|
47199
47837
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
47200
|
-
const controlPath =
|
|
47838
|
+
const controlPath = join47(socketDir, "%C");
|
|
47201
47839
|
const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
47202
47840
|
const args = [
|
|
47203
47841
|
"-o",
|
|
@@ -47222,7 +47860,7 @@ function buildSshArgs(options) {
|
|
|
47222
47860
|
}
|
|
47223
47861
|
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
|
|
47224
47862
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
47225
|
-
const controlPath =
|
|
47863
|
+
const controlPath = join47(resolvedDir, "%C");
|
|
47226
47864
|
try {
|
|
47227
47865
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
47228
47866
|
encoding: "utf-8"
|
|
@@ -47234,7 +47872,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
47234
47872
|
}
|
|
47235
47873
|
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
|
|
47236
47874
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
47237
|
-
const controlPath =
|
|
47875
|
+
const controlPath = join47(resolvedDir, "%C");
|
|
47238
47876
|
try {
|
|
47239
47877
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
47240
47878
|
encoding: "utf-8"
|
|
@@ -47679,9 +48317,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
47679
48317
|
}
|
|
47680
48318
|
|
|
47681
48319
|
// plugins/kxm/src/completion-install.ts
|
|
47682
|
-
import { existsSync as
|
|
48320
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync26 } from "node:fs";
|
|
47683
48321
|
import { homedir as homedir9 } from "node:os";
|
|
47684
|
-
import { basename as basename9, delimiter, dirname as
|
|
48322
|
+
import { basename as basename9, delimiter, dirname as dirname23, isAbsolute as isAbsolute10, join as join48, resolve as resolve31 } from "node:path";
|
|
47685
48323
|
var COMPLETION_MARKER = "# kxm completion";
|
|
47686
48324
|
var PATH_MARKER = "# kxm path";
|
|
47687
48325
|
function detectShell(env = process.env, platform = process.platform) {
|
|
@@ -47697,31 +48335,31 @@ function effectiveHome(options) {
|
|
|
47697
48335
|
const env = options.env ?? process.env;
|
|
47698
48336
|
if (options.homeDir) return options.homeDir;
|
|
47699
48337
|
const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
|
|
47700
|
-
return envHome && envHome.length > 0 ?
|
|
48338
|
+
return envHome && envHome.length > 0 ? resolve31(envHome) : homedir9();
|
|
47701
48339
|
}
|
|
47702
48340
|
function completionScriptPath(shell, options = {}) {
|
|
47703
48341
|
const env = options.env ?? process.env;
|
|
47704
48342
|
const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
|
|
47705
|
-
const base = explicit && explicit.length > 0 ?
|
|
47706
|
-
return
|
|
48343
|
+
const base = explicit && explicit.length > 0 ? resolve31(explicit) : resolve31(effectiveHome(options), ".config", "kxm");
|
|
48344
|
+
return join48(base, "completions", `kxm.${shell}`);
|
|
47707
48345
|
}
|
|
47708
48346
|
function bashRcCandidate(options) {
|
|
47709
48347
|
const home = effectiveHome(options);
|
|
47710
|
-
const candidates = [
|
|
47711
|
-
const existing = candidates.find((candidate) =>
|
|
48348
|
+
const candidates = [join48(home, ".bashrc"), join48(home, ".bash_profile")];
|
|
48349
|
+
const existing = candidates.find((candidate) => existsSync39(candidate));
|
|
47712
48350
|
return existing ?? candidates[0];
|
|
47713
48351
|
}
|
|
47714
48352
|
function zshRcCandidate(options) {
|
|
47715
48353
|
const env = options.env ?? process.env;
|
|
47716
48354
|
const home = effectiveHome(options);
|
|
47717
|
-
if (env.ZDOTDIR?.trim()) return
|
|
47718
|
-
return
|
|
48355
|
+
if (env.ZDOTDIR?.trim()) return join48(resolve31(env.ZDOTDIR.trim()), ".zshrc");
|
|
48356
|
+
return join48(home, ".zshrc");
|
|
47719
48357
|
}
|
|
47720
48358
|
function fishCompletionTarget(shell, options) {
|
|
47721
48359
|
const env = options.env ?? process.env;
|
|
47722
48360
|
const home = effectiveHome(options);
|
|
47723
|
-
if (env.XDG_CONFIG_HOME?.trim()) return
|
|
47724
|
-
return
|
|
48361
|
+
if (env.XDG_CONFIG_HOME?.trim()) return join48(resolve31(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
|
|
48362
|
+
return join48(home, ".config", "fish", "completions", "kxm.fish");
|
|
47725
48363
|
}
|
|
47726
48364
|
function completionRcTarget(shell, options = {}) {
|
|
47727
48365
|
if (shell === "fish") {
|
|
@@ -47753,16 +48391,16 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47753
48391
|
const scriptPath = completionScriptPath(shell, options);
|
|
47754
48392
|
const { rcFile } = completionRcTarget(shell, options);
|
|
47755
48393
|
const script = generateShellCompletion(shell);
|
|
47756
|
-
const existingScript =
|
|
47757
|
-
const existingScriptMatches = existingScript &&
|
|
48394
|
+
const existingScript = existsSync39(scriptPath);
|
|
48395
|
+
const existingScriptMatches = existingScript && readFileSync38(scriptPath, "utf8") === script;
|
|
47758
48396
|
let rcModified = false;
|
|
47759
48397
|
let alreadyInstalled = false;
|
|
47760
48398
|
if (shell === "fish") {
|
|
47761
48399
|
const fishTarget = fishCompletionTarget(shell, options);
|
|
47762
|
-
const fishInstalled =
|
|
48400
|
+
const fishInstalled = existsSync39(fishTarget) && readFileSync38(fishTarget, "utf8") === script;
|
|
47763
48401
|
alreadyInstalled = fishInstalled;
|
|
47764
48402
|
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
47765
|
-
mkdirSync30(
|
|
48403
|
+
mkdirSync30(resolve31(fishTarget, ".."), { recursive: true });
|
|
47766
48404
|
writeFileSync26(fishTarget, script, { encoding: "utf8" });
|
|
47767
48405
|
}
|
|
47768
48406
|
return {
|
|
@@ -47776,12 +48414,12 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47776
48414
|
}
|
|
47777
48415
|
if (rcFile) {
|
|
47778
48416
|
const line = sourceLine(shell, scriptPath);
|
|
47779
|
-
const rcContent =
|
|
48417
|
+
const rcContent = existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "";
|
|
47780
48418
|
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
47781
48419
|
}
|
|
47782
48420
|
if (!options.dryRun) {
|
|
47783
48421
|
if (!existingScriptMatches || options.overwrite) {
|
|
47784
|
-
mkdirSync30(
|
|
48422
|
+
mkdirSync30(resolve31(scriptPath, ".."), { recursive: true });
|
|
47785
48423
|
writeFileSync26(scriptPath, script, { encoding: "utf8" });
|
|
47786
48424
|
}
|
|
47787
48425
|
if (rcFile && !alreadyInstalled) {
|
|
@@ -47791,8 +48429,8 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47791
48429
|
${marker}
|
|
47792
48430
|
${line}
|
|
47793
48431
|
`;
|
|
47794
|
-
mkdirSync30(
|
|
47795
|
-
writeFileSync26(rcFile, (
|
|
48432
|
+
mkdirSync30(resolve31(rcFile, ".."), { recursive: true });
|
|
48433
|
+
writeFileSync26(rcFile, (existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
47796
48434
|
rcModified = true;
|
|
47797
48435
|
}
|
|
47798
48436
|
}
|
|
@@ -47808,14 +48446,14 @@ ${line}
|
|
|
47808
48446
|
function kxmBinDir(env = process.env) {
|
|
47809
48447
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
47810
48448
|
if (argv1 && isAbsolute10(argv1)) {
|
|
47811
|
-
const dir =
|
|
47812
|
-
if (
|
|
48449
|
+
const dir = dirname23(argv1);
|
|
48450
|
+
if (existsSync39(join48(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
47813
48451
|
}
|
|
47814
48452
|
const pathEnv = env.PATH ?? "";
|
|
47815
48453
|
for (const part of pathEnv.split(delimiter)) {
|
|
47816
48454
|
if (!part) continue;
|
|
47817
|
-
const candidate =
|
|
47818
|
-
if (
|
|
48455
|
+
const candidate = resolve31(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
48456
|
+
if (existsSync39(candidate)) return resolve31(part);
|
|
47819
48457
|
}
|
|
47820
48458
|
return void 0;
|
|
47821
48459
|
}
|
|
@@ -47833,10 +48471,10 @@ function installPathEntry(shellInput, options = {}) {
|
|
|
47833
48471
|
}
|
|
47834
48472
|
const { rcFile } = completionRcTarget(shell, options);
|
|
47835
48473
|
const line = pathLine(binDir);
|
|
47836
|
-
const rcContent =
|
|
48474
|
+
const rcContent = existsSync39(rcFile ?? "") ? readFileSync38(rcFile, "utf8") : "";
|
|
47837
48475
|
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
47838
48476
|
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
47839
|
-
mkdirSync30(
|
|
48477
|
+
mkdirSync30(dirname23(rcFile), { recursive: true });
|
|
47840
48478
|
writeFileSync26(rcFile, rcContent + `
|
|
47841
48479
|
${PATH_MARKER}
|
|
47842
48480
|
${line}
|
|
@@ -47847,8 +48485,8 @@ ${line}
|
|
|
47847
48485
|
|
|
47848
48486
|
// plugins/kxm/src/init-guide-setup.ts
|
|
47849
48487
|
var import_yaml18 = __toESM(require_dist(), 1);
|
|
47850
|
-
import { existsSync as
|
|
47851
|
-
import { dirname as
|
|
48488
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync31, writeFileSync as writeFileSync27 } from "node:fs";
|
|
48489
|
+
import { dirname as dirname24, join as join49 } from "node:path";
|
|
47852
48490
|
var ADMITTED_GUIDE_BINDINGS = Object.freeze({
|
|
47853
48491
|
"anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
47854
48492
|
"anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
@@ -48239,13 +48877,13 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
48239
48877
|
const stage = roleStages.get(role);
|
|
48240
48878
|
if (!stage) continue;
|
|
48241
48879
|
files.push({
|
|
48242
|
-
path:
|
|
48880
|
+
path: join49(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
48243
48881
|
content: (0, import_yaml18.stringify)(agentDocument(role, stage, binding))
|
|
48244
48882
|
});
|
|
48245
48883
|
}
|
|
48246
48884
|
for (const workflow of plan.workflows) {
|
|
48247
48885
|
files.push({
|
|
48248
|
-
path:
|
|
48886
|
+
path: join49(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
48249
48887
|
content: (0, import_yaml18.stringify)(workflowDocument(workflow))
|
|
48250
48888
|
});
|
|
48251
48889
|
}
|
|
@@ -48262,8 +48900,8 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
|
|
|
48262
48900
|
}
|
|
48263
48901
|
if (added.length === 0) return added;
|
|
48264
48902
|
policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
48265
|
-
const path4 =
|
|
48266
|
-
mkdirSync31(
|
|
48903
|
+
const path4 = join49(projectRoot, ".kxm", "routes.yaml");
|
|
48904
|
+
mkdirSync31(dirname24(path4), { recursive: true });
|
|
48267
48905
|
writeFileSync27(path4, (0, import_yaml18.stringify)(policy), "utf8");
|
|
48268
48906
|
return added;
|
|
48269
48907
|
}
|
|
@@ -48271,11 +48909,11 @@ function writeGuideSetupFiles(files) {
|
|
|
48271
48909
|
const written = [];
|
|
48272
48910
|
const existed = [];
|
|
48273
48911
|
for (const file of files) {
|
|
48274
|
-
if (
|
|
48912
|
+
if (existsSync40(file.path)) {
|
|
48275
48913
|
existed.push(file.path);
|
|
48276
48914
|
continue;
|
|
48277
48915
|
}
|
|
48278
|
-
mkdirSync31(
|
|
48916
|
+
mkdirSync31(dirname24(file.path), { recursive: true });
|
|
48279
48917
|
writeFileSync27(file.path, file.content, "utf8");
|
|
48280
48918
|
written.push(file.path);
|
|
48281
48919
|
}
|
|
@@ -48339,7 +48977,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
48339
48977
|
detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
|
|
48340
48978
|
};
|
|
48341
48979
|
}
|
|
48342
|
-
const releaseDir = mkdtempSync3(
|
|
48980
|
+
const releaseDir = mkdtempSync3(join50(tmpdir2(), "kxm-pkg-update-"));
|
|
48343
48981
|
try {
|
|
48344
48982
|
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
|
|
48345
48983
|
if (runtime.dryRun) {
|
|
@@ -48348,7 +48986,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
48348
48986
|
for (const step of planned) {
|
|
48349
48987
|
if (step.kind === "verify") {
|
|
48350
48988
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
48351
|
-
const actual =
|
|
48989
|
+
const actual = existsSync41(step.path) ? createHash17("sha256").update(readFileSync39(step.path)).digest("hex") : "missing";
|
|
48352
48990
|
return {
|
|
48353
48991
|
ok: false,
|
|
48354
48992
|
error: "release_digest_mismatch",
|
|
@@ -48625,13 +49263,27 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
48625
49263
|
return 2;
|
|
48626
49264
|
}
|
|
48627
49265
|
const selectedFile = explicitFile || configuredFile;
|
|
48628
|
-
const file = selectedFile ?
|
|
48629
|
-
if (file && !
|
|
49266
|
+
const file = selectedFile ? resolve32(runtime.cwd, selectedFile) : void 0;
|
|
49267
|
+
if (file && !existsSync41(file)) {
|
|
48630
49268
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
48631
49269
|
return 1;
|
|
48632
49270
|
}
|
|
48633
49271
|
try {
|
|
48634
|
-
const raw = file ?
|
|
49272
|
+
const raw = file ? readFileSync39(file, "utf8") : inline;
|
|
49273
|
+
if (explicitFile && file && !raw.trimStart().startsWith("[")) {
|
|
49274
|
+
const value = parseRestrictedYaml2(raw, file);
|
|
49275
|
+
const issues = new KxmSchemaRegistry().validate("workflow", value, file);
|
|
49276
|
+
if (issues.length > 0) throw new KxmConfigError(issues);
|
|
49277
|
+
const id = basename10(file, extname3(file));
|
|
49278
|
+
compileKxmWorkflow({ id, value, logicalPath: file });
|
|
49279
|
+
printWorker(
|
|
49280
|
+
runtime,
|
|
49281
|
+
worker,
|
|
49282
|
+
{ ok: true, command: "validate", source: "file", file, workflows: [{ id, schema: value.schema }], warnings: [] },
|
|
49283
|
+
`validated local workflow ${id} (schema and transitions); kxm init validates project references`
|
|
49284
|
+
);
|
|
49285
|
+
return 0;
|
|
49286
|
+
}
|
|
48635
49287
|
const warnings = [];
|
|
48636
49288
|
const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
|
|
48637
49289
|
const secretEnvs = definitions.map((definition) => ({
|
|
@@ -48656,7 +49308,7 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
48656
49308
|
}
|
|
48657
49309
|
async function cmdArtifactsExist(runtime, pathFlag) {
|
|
48658
49310
|
const worker = gateOf(runtime, "artifacts-exist");
|
|
48659
|
-
const checked = verifyArtifactExists(runtime.dirs.assets,
|
|
49311
|
+
const checked = verifyArtifactExists(runtime.dirs.assets, resolve32(runtime.cwd, pathFlag));
|
|
48660
49312
|
if (!checked.ok) {
|
|
48661
49313
|
printWorker(
|
|
48662
49314
|
runtime,
|
|
@@ -48712,7 +49364,7 @@ async function cmdImprove(runtime, options = {}) {
|
|
|
48712
49364
|
print(runtime.io, runtime.json, { ok: false, command: "improve", error: "config_invalid", detail: message }, `config_invalid: ${message}`);
|
|
48713
49365
|
return 1;
|
|
48714
49366
|
}
|
|
48715
|
-
const candidatesDir = options.outDir ?
|
|
49367
|
+
const candidatesDir = options.outDir ? resolve32(runtime.cwd, options.outDir) : join50(root, ".kxm", "candidates");
|
|
48716
49368
|
const report2 = buildImprovementReport(loaded.records, {
|
|
48717
49369
|
candidatesDir,
|
|
48718
49370
|
projectRoot: root,
|
|
@@ -48721,7 +49373,7 @@ async function cmdImprove(runtime, options = {}) {
|
|
|
48721
49373
|
autoThreshold: config.improvement.autoThreshold,
|
|
48722
49374
|
halfLifeDays: config.improvement.telemetryHalfLifeDays
|
|
48723
49375
|
});
|
|
48724
|
-
const reportDir =
|
|
49376
|
+
const reportDir = join50(runtime.dirs.assets, "improvements");
|
|
48725
49377
|
const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
|
|
48726
49378
|
const text = [
|
|
48727
49379
|
"Sources:",
|
|
@@ -48865,9 +49517,9 @@ async function maybeOfferCompletionInstall(runtime) {
|
|
|
48865
49517
|
if (!pathNeeded) {
|
|
48866
49518
|
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
48867
49519
|
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
48868
|
-
if (rcFile &&
|
|
49520
|
+
if (rcFile && existsSync41(rcFile)) {
|
|
48869
49521
|
try {
|
|
48870
|
-
if (
|
|
49522
|
+
if (readFileSync39(rcFile, "utf8").includes(scriptPath)) return;
|
|
48871
49523
|
} catch {
|
|
48872
49524
|
}
|
|
48873
49525
|
}
|
|
@@ -49030,7 +49682,7 @@ async function cmdRoutingReport(runtime, options) {
|
|
|
49030
49682
|
let catalog;
|
|
49031
49683
|
if (includeEquivalentListCost) {
|
|
49032
49684
|
try {
|
|
49033
|
-
const pricesPath = options.prices ?
|
|
49685
|
+
const pricesPath = options.prices ? resolve32(runtime.cwd, options.prices) : join50(runtime.dirs.workspace, "prices.yaml");
|
|
49034
49686
|
catalog = loadPriceCatalog(pricesPath);
|
|
49035
49687
|
} catch {
|
|
49036
49688
|
}
|
|
@@ -49414,7 +50066,7 @@ function createProgram(ctx, result) {
|
|
|
49414
50066
|
addGlobalOptions(program2.command("restore <manifest>").description("Restore this project's SQLite stores from a verified backup manifest; refuses while the Runtime or hub is running")).option("--all-projects", "Also restore the shared Runtime registry and other projects' event stores the backup holds").action(async function restoreAction(manifest, options) {
|
|
49415
50067
|
result.code = await cmdRestore(runtimeFrom(ctx, this), manifest, options);
|
|
49416
50068
|
});
|
|
49417
|
-
addGlobalOptions(program2.command("run").description("Create a KXM run
|
|
50069
|
+
addGlobalOptions(program2.command("run").description("Create a KXM run without executing steps; follow its prerequisites, then kxm runs drive <runId> --wait").argument("[workflow]", "Workflow id to run").argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)").action(async function runAction(workflow2, promptParts) {
|
|
49418
50070
|
result.code = await cmdKxmRun(runtimeFrom(ctx, this), workflow2, promptParts);
|
|
49419
50071
|
}));
|
|
49420
50072
|
const runCmd = addGlobalOptions(program2.command("runs").description("Inspect KXM runs"));
|
|
@@ -49879,7 +50531,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
|
|
|
49879
50531
|
throw error;
|
|
49880
50532
|
}
|
|
49881
50533
|
}
|
|
49882
|
-
if (process.argv[1] &&
|
|
50534
|
+
if (process.argv[1] && resolve33(process.argv[1]) === fileURLToPath4(import.meta.url)) {
|
|
49883
50535
|
const code = await runCli(process.argv.slice(2));
|
|
49884
50536
|
process.exitCode = code;
|
|
49885
50537
|
}
|