@kontextmind/kxm 0.7.0 → 0.7.5
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/.kxm/roles/writer.yaml +2 -0
- package/docs/README.md +3 -0
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
- package/docs/agent-skills.md +19 -2
- package/docs/browser-automation.md +116 -0
- package/docs/configuration.md +10 -1
- package/docs/getting-started.md +21 -0
- package/docs/kb/how-credentials-retrieved-safely.md +31 -0
- package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
- package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
- package/docs/kb/how-to-resume-after-mfa.md +28 -0
- package/docs/kb/how-to-take-over-session.md +32 -0
- package/docs/kb/why-authentication-disappeared.md +32 -0
- package/docs/kb/why-automation-opened-different-browser.md +32 -0
- package/docs/kb/why-session-viewer-cannot-control.md +31 -0
- package/docs/operations.md +24 -0
- package/docs/prompts/browser-annotate-feedback.md +41 -0
- package/docs/prompts/browser-diagnose-recover.md +38 -0
- package/docs/prompts/browser-explore.md +42 -0
- package/docs/prompts/browser-repro-fix.md +48 -0
- package/docs/prompts/browser-start.md +41 -0
- package/docs/prompts/browser-takeover.md +50 -0
- package/docs/skills/repo-work-delivery.md +5 -0
- package/docs/skills.md +2 -0
- package/docs/troubleshooting.md +22 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +2257 -439
- package/plugins/kxm/dist/core.js +57 -0
- package/plugins/kxm/dist/extension.js +40 -3
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +572 -17
- package/plugins/kxm/dist/server.js +129 -4
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +136 -13
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/hints.json +30 -0
- package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
- package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +9 -2
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/browser.ts +603 -0
- package/plugins/kxm/src/cli.ts +507 -9
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/external-effects.ts +1 -1
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/protocol.ts +111 -0
- package/plugins/kxm/src/role.ts +335 -0
- package/plugins/kxm/src/runtime.ts +1 -0
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/vnext-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +38 -1
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +16 -0
- package/plugins/kxm/src/vnext-harness.ts +4 -2
- package/plugins/kxm/src/vnext-oneshot-process.ts +21 -4
- package/plugins/kxm/src/vnext-oneshot-producer.ts +18 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +1 -1
- package/plugins/kxm/src/workflow-tui.ts +1 -1
- package/plugins/kxm/src/workflow.ts +144 -0
- package/scripts/kxm-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +145 -3
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -0
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -1504,7 +1504,7 @@ var require_stringify = __commonJS({
|
|
|
1504
1504
|
props.push(doc.directives.tagString(tag));
|
|
1505
1505
|
return props.join(" ");
|
|
1506
1506
|
}
|
|
1507
|
-
function
|
|
1507
|
+
function stringify11(item, ctx, onComment, onChompKeep) {
|
|
1508
1508
|
if (identity.isPair(item))
|
|
1509
1509
|
return item.toString(ctx, onComment, onChompKeep);
|
|
1510
1510
|
if (identity.isAlias(item)) {
|
|
@@ -1533,7 +1533,7 @@ var require_stringify = __commonJS({
|
|
|
1533
1533
|
${ctx.indent}${str}`;
|
|
1534
1534
|
}
|
|
1535
1535
|
exports.createStringifyContext = createStringifyContext;
|
|
1536
|
-
exports.stringify =
|
|
1536
|
+
exports.stringify = stringify11;
|
|
1537
1537
|
}
|
|
1538
1538
|
});
|
|
1539
1539
|
|
|
@@ -1543,7 +1543,7 @@ var require_stringifyPair = __commonJS({
|
|
|
1543
1543
|
"use strict";
|
|
1544
1544
|
var identity = require_identity();
|
|
1545
1545
|
var Scalar = require_Scalar();
|
|
1546
|
-
var
|
|
1546
|
+
var stringify11 = require_stringify();
|
|
1547
1547
|
var stringifyComment = require_stringifyComment();
|
|
1548
1548
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
|
1549
1549
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
|
@@ -1565,7 +1565,7 @@ var require_stringifyPair = __commonJS({
|
|
|
1565
1565
|
});
|
|
1566
1566
|
let keyCommentDone = false;
|
|
1567
1567
|
let chompKeep = false;
|
|
1568
|
-
let str =
|
|
1568
|
+
let str = stringify11.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
1569
1569
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
|
1570
1570
|
if (simpleKeys)
|
|
1571
1571
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
@@ -1617,7 +1617,7 @@ ${indent}:`;
|
|
|
1617
1617
|
ctx.indent = ctx.indent.substring(2);
|
|
1618
1618
|
}
|
|
1619
1619
|
let valueCommentDone = false;
|
|
1620
|
-
const valueStr =
|
|
1620
|
+
const valueStr = stringify11.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
|
|
1621
1621
|
let ws = " ";
|
|
1622
1622
|
if (keyComment || vsb || vcb) {
|
|
1623
1623
|
ws = vsb ? "\n" : "";
|
|
@@ -1758,7 +1758,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
1758
1758
|
"use strict";
|
|
1759
1759
|
var log = require_log();
|
|
1760
1760
|
var merge = require_merge();
|
|
1761
|
-
var
|
|
1761
|
+
var stringify11 = require_stringify();
|
|
1762
1762
|
var identity = require_identity();
|
|
1763
1763
|
var toJS = require_toJS();
|
|
1764
1764
|
function addPairToJSMap(ctx, map, { key, value }) {
|
|
@@ -1794,7 +1794,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
1794
1794
|
if (typeof jsKey !== "object")
|
|
1795
1795
|
return String(jsKey);
|
|
1796
1796
|
if (identity.isNode(key) && ctx?.doc) {
|
|
1797
|
-
const strCtx =
|
|
1797
|
+
const strCtx = stringify11.createStringifyContext(ctx.doc, {});
|
|
1798
1798
|
strCtx.anchors = /* @__PURE__ */ new Set();
|
|
1799
1799
|
for (const node of ctx.anchors.keys())
|
|
1800
1800
|
strCtx.anchors.add(node.anchor);
|
|
@@ -1861,12 +1861,12 @@ var require_stringifyCollection = __commonJS({
|
|
|
1861
1861
|
"node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
|
|
1862
1862
|
"use strict";
|
|
1863
1863
|
var identity = require_identity();
|
|
1864
|
-
var
|
|
1864
|
+
var stringify11 = require_stringify();
|
|
1865
1865
|
var stringifyComment = require_stringifyComment();
|
|
1866
1866
|
function stringifyCollection(collection, ctx, options) {
|
|
1867
1867
|
const flow = ctx.inFlow ?? collection.flow;
|
|
1868
|
-
const
|
|
1869
|
-
return
|
|
1868
|
+
const stringify12 = flow ? stringifyFlowCollection : stringifyBlockCollection;
|
|
1869
|
+
return stringify12(collection, ctx, options);
|
|
1870
1870
|
}
|
|
1871
1871
|
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
|
|
1872
1872
|
const { indent, options: { commentString } } = ctx;
|
|
@@ -1891,7 +1891,7 @@ var require_stringifyCollection = __commonJS({
|
|
|
1891
1891
|
}
|
|
1892
1892
|
}
|
|
1893
1893
|
chompKeep = false;
|
|
1894
|
-
let str2 =
|
|
1894
|
+
let str2 = stringify11.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
1895
1895
|
if (comment2)
|
|
1896
1896
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
1897
1897
|
if (chompKeep && comment2)
|
|
@@ -1958,7 +1958,7 @@ ${indent}${line}` : "\n";
|
|
|
1958
1958
|
}
|
|
1959
1959
|
if (comment)
|
|
1960
1960
|
reqNewline = true;
|
|
1961
|
-
let str =
|
|
1961
|
+
let str = stringify11.stringify(item, itemCtx, () => comment = null);
|
|
1962
1962
|
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
1963
1963
|
if (i < items.length - 1) {
|
|
1964
1964
|
str += ",";
|
|
@@ -3319,7 +3319,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
3319
3319
|
"node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
|
|
3320
3320
|
"use strict";
|
|
3321
3321
|
var identity = require_identity();
|
|
3322
|
-
var
|
|
3322
|
+
var stringify11 = require_stringify();
|
|
3323
3323
|
var stringifyComment = require_stringifyComment();
|
|
3324
3324
|
function stringifyDocument(doc, options) {
|
|
3325
3325
|
const lines = [];
|
|
@@ -3334,7 +3334,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
3334
3334
|
}
|
|
3335
3335
|
if (hasDirectives)
|
|
3336
3336
|
lines.push("---");
|
|
3337
|
-
const ctx =
|
|
3337
|
+
const ctx = stringify11.createStringifyContext(doc, options);
|
|
3338
3338
|
const { commentString } = ctx.options;
|
|
3339
3339
|
if (doc.commentBefore) {
|
|
3340
3340
|
if (lines.length !== 1)
|
|
@@ -3356,7 +3356,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
3356
3356
|
contentComment = doc.contents.comment;
|
|
3357
3357
|
}
|
|
3358
3358
|
const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
|
|
3359
|
-
let body =
|
|
3359
|
+
let body = stringify11.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
3360
3360
|
if (contentComment)
|
|
3361
3361
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
3362
3362
|
if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
|
|
@@ -3364,7 +3364,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
3364
3364
|
} else
|
|
3365
3365
|
lines.push(body);
|
|
3366
3366
|
} else {
|
|
3367
|
-
lines.push(
|
|
3367
|
+
lines.push(stringify11.stringify(doc.contents, ctx));
|
|
3368
3368
|
}
|
|
3369
3369
|
if (doc.directives?.docEnd) {
|
|
3370
3370
|
if (doc.comment) {
|
|
@@ -5499,7 +5499,7 @@ var require_cst_scalar = __commonJS({
|
|
|
5499
5499
|
var require_cst_stringify = __commonJS({
|
|
5500
5500
|
"node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
|
|
5501
5501
|
"use strict";
|
|
5502
|
-
var
|
|
5502
|
+
var stringify11 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
|
|
5503
5503
|
function stringifyToken(token) {
|
|
5504
5504
|
switch (token.type) {
|
|
5505
5505
|
case "block-scalar": {
|
|
@@ -5552,7 +5552,7 @@ var require_cst_stringify = __commonJS({
|
|
|
5552
5552
|
res += stringifyToken(value);
|
|
5553
5553
|
return res;
|
|
5554
5554
|
}
|
|
5555
|
-
exports.stringify =
|
|
5555
|
+
exports.stringify = stringify11;
|
|
5556
5556
|
}
|
|
5557
5557
|
});
|
|
5558
5558
|
|
|
@@ -7282,7 +7282,7 @@ var require_public_api = __commonJS({
|
|
|
7282
7282
|
}
|
|
7283
7283
|
return doc.toJS(Object.assign({ reviver: _reviver }, options));
|
|
7284
7284
|
}
|
|
7285
|
-
function
|
|
7285
|
+
function stringify11(value, replacer, options) {
|
|
7286
7286
|
let _replacer = null;
|
|
7287
7287
|
if (typeof replacer === "function" || Array.isArray(replacer)) {
|
|
7288
7288
|
_replacer = replacer;
|
|
@@ -7307,7 +7307,7 @@ var require_public_api = __commonJS({
|
|
|
7307
7307
|
exports.parse = parse11;
|
|
7308
7308
|
exports.parseAllDocuments = parseAllDocuments;
|
|
7309
7309
|
exports.parseDocument = parseDocument2;
|
|
7310
|
-
exports.stringify =
|
|
7310
|
+
exports.stringify = stringify11;
|
|
7311
7311
|
}
|
|
7312
7312
|
});
|
|
7313
7313
|
|
|
@@ -7491,10 +7491,10 @@ var require_code = __commonJS({
|
|
|
7491
7491
|
function interpolate(x2) {
|
|
7492
7492
|
return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2);
|
|
7493
7493
|
}
|
|
7494
|
-
function
|
|
7494
|
+
function stringify11(x2) {
|
|
7495
7495
|
return new _Code(safeStringify(x2));
|
|
7496
7496
|
}
|
|
7497
|
-
exports.stringify =
|
|
7497
|
+
exports.stringify = stringify11;
|
|
7498
7498
|
function safeStringify(x2) {
|
|
7499
7499
|
return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
7500
7500
|
}
|
|
@@ -10314,7 +10314,7 @@ var require_compile = __commonJS({
|
|
|
10314
10314
|
const schOrFunc = root.refs[ref];
|
|
10315
10315
|
if (schOrFunc)
|
|
10316
10316
|
return schOrFunc;
|
|
10317
|
-
let _sch =
|
|
10317
|
+
let _sch = resolve23.call(this, root, ref);
|
|
10318
10318
|
if (_sch === void 0) {
|
|
10319
10319
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
10320
10320
|
const { schemaId } = this.opts;
|
|
@@ -10341,7 +10341,7 @@ var require_compile = __commonJS({
|
|
|
10341
10341
|
function sameSchemaEnv(s1, s2) {
|
|
10342
10342
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
10343
10343
|
}
|
|
10344
|
-
function
|
|
10344
|
+
function resolve23(root, ref) {
|
|
10345
10345
|
let sch;
|
|
10346
10346
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
10347
10347
|
ref = sch;
|
|
@@ -11166,7 +11166,7 @@ var require_fast_uri = __commonJS({
|
|
|
11166
11166
|
}
|
|
11167
11167
|
return uri;
|
|
11168
11168
|
}
|
|
11169
|
-
function
|
|
11169
|
+
function resolve23(baseURI, relativeURI, options) {
|
|
11170
11170
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
11171
11171
|
const {
|
|
11172
11172
|
parsed: baseParsed,
|
|
@@ -11528,7 +11528,7 @@ var require_fast_uri = __commonJS({
|
|
|
11528
11528
|
var fastUri = {
|
|
11529
11529
|
SCHEMES,
|
|
11530
11530
|
normalize,
|
|
11531
|
-
resolve:
|
|
11531
|
+
resolve: resolve23,
|
|
11532
11532
|
resolveComponent,
|
|
11533
11533
|
equal,
|
|
11534
11534
|
serialize,
|
|
@@ -14777,11 +14777,51 @@ var require__ = __commonJS({
|
|
|
14777
14777
|
|
|
14778
14778
|
// plugins/kxm/src/cli.ts
|
|
14779
14779
|
import { spawn as spawn2, spawnSync as spawnSync8 } from "node:child_process";
|
|
14780
|
-
import { createHash as
|
|
14781
|
-
import { existsSync as
|
|
14782
|
-
import { homedir as
|
|
14783
|
-
import { basename as
|
|
14784
|
-
|
|
14780
|
+
import { createHash as createHash16, createHmac as createHmac3, randomUUID as randomUUID12 } from "node:crypto";
|
|
14781
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, readdirSync as readdirSync11, rmSync as rmSync10, writeFileSync as writeFileSync26 } from "node:fs";
|
|
14782
|
+
import { homedir as homedir7, tmpdir as tmpdir2 } from "node:os";
|
|
14783
|
+
import { basename as basename7, dirname as dirname21, join as join36, relative as relative6, resolve as resolve22 } from "node:path";
|
|
14784
|
+
|
|
14785
|
+
// plugins/kxm/src/sqlite.ts
|
|
14786
|
+
import { createRequire } from "node:module";
|
|
14787
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
14788
|
+
function loadNative() {
|
|
14789
|
+
try {
|
|
14790
|
+
const mod = requireFromHere("node:sqlite");
|
|
14791
|
+
if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
|
|
14792
|
+
} catch {
|
|
14793
|
+
}
|
|
14794
|
+
try {
|
|
14795
|
+
const mod = requireFromHere("bun:sqlite");
|
|
14796
|
+
const Ctor = mod?.DatabaseSync ?? mod?.Database;
|
|
14797
|
+
if (Ctor) return { Ctor, bun: true };
|
|
14798
|
+
} catch {
|
|
14799
|
+
}
|
|
14800
|
+
throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
|
|
14801
|
+
}
|
|
14802
|
+
var native = loadNative();
|
|
14803
|
+
var DatabaseSync = class {
|
|
14804
|
+
inner;
|
|
14805
|
+
constructor(path4, options) {
|
|
14806
|
+
let normalized = options;
|
|
14807
|
+
if (native.bun && options) {
|
|
14808
|
+
const { readOnly, ...rest } = options;
|
|
14809
|
+
normalized = readOnly === void 0 ? rest : { ...rest, readonly: readOnly };
|
|
14810
|
+
}
|
|
14811
|
+
this.inner = normalized === void 0 ? new native.Ctor(path4) : new native.Ctor(path4, normalized);
|
|
14812
|
+
}
|
|
14813
|
+
prepare(sql) {
|
|
14814
|
+
return this.inner.prepare(sql);
|
|
14815
|
+
}
|
|
14816
|
+
exec(sql) {
|
|
14817
|
+
return this.inner.exec(sql);
|
|
14818
|
+
}
|
|
14819
|
+
close() {
|
|
14820
|
+
this.inner.close();
|
|
14821
|
+
}
|
|
14822
|
+
};
|
|
14823
|
+
|
|
14824
|
+
// plugins/kxm/src/cli.ts
|
|
14785
14825
|
import { createInterface } from "node:readline";
|
|
14786
14826
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
14787
14827
|
|
|
@@ -18491,7 +18531,6 @@ import {
|
|
|
18491
18531
|
writeFileSync as writeFileSync2
|
|
18492
18532
|
} from "node:fs";
|
|
18493
18533
|
import { basename as basename3, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
|
|
18494
|
-
import { DatabaseSync } from "node:sqlite";
|
|
18495
18534
|
|
|
18496
18535
|
// plugins/kxm/src/vnext-config.ts
|
|
18497
18536
|
var import__ = __toESM(require__(), 1);
|
|
@@ -19074,7 +19113,9 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
19074
19113
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
19075
19114
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
19076
19115
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
19077
|
-
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"])
|
|
19116
|
+
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
19117
|
+
agy: Object.freeze(["--mode", "plan", "--sandbox", "--disable-slash-commands"]),
|
|
19118
|
+
kimi: Object.freeze(["--plan"])
|
|
19078
19119
|
});
|
|
19079
19120
|
function oneShotReadOnlyArgs(harness) {
|
|
19080
19121
|
return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
|
|
@@ -19128,7 +19169,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
19128
19169
|
authArgs: ["provider", "list"],
|
|
19129
19170
|
update: { self: ["upgrade"] },
|
|
19130
19171
|
oneShot: {
|
|
19131
|
-
argv: ["--output-format", "stream-json", "-p"],
|
|
19172
|
+
argv: [...oneShotReadOnlyArgs("kimi"), "--output-format", "stream-json", "-p"],
|
|
19132
19173
|
promptVia: "arg",
|
|
19133
19174
|
outputFormat: "stream-json",
|
|
19134
19175
|
usageParser: parseKimiOneShotUsage
|
|
@@ -19191,7 +19232,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
19191
19232
|
authArgs: ["models"],
|
|
19192
19233
|
update: { self: ["update"] },
|
|
19193
19234
|
oneShot: {
|
|
19194
|
-
argv: ["--output-format", "json", "-p"],
|
|
19235
|
+
argv: [...oneShotReadOnlyArgs("agy"), "--output-format", "json", "-p"],
|
|
19195
19236
|
promptVia: "arg",
|
|
19196
19237
|
outputFormat: "json",
|
|
19197
19238
|
usageParser: parseAgyOneShotUsage
|
|
@@ -20323,8 +20364,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
20323
20364
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
20324
20365
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
20325
20366
|
}
|
|
20326
|
-
const
|
|
20327
|
-
const minimumPassed =
|
|
20367
|
+
const join37 = objectValue(step.join);
|
|
20368
|
+
const minimumPassed = join37 && typeof join37.minimumPassed === "number" ? join37.minimumPassed : void 0;
|
|
20328
20369
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
20329
20370
|
const distinctBy = names(assignment?.distinctBy);
|
|
20330
20371
|
if (distinctBy.length > 0) {
|
|
@@ -20469,7 +20510,7 @@ function validateModelReferences(models, issues) {
|
|
|
20469
20510
|
};
|
|
20470
20511
|
for (const id of models.keys()) walk(id, []);
|
|
20471
20512
|
}
|
|
20472
|
-
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry) {
|
|
20513
|
+
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot) {
|
|
20473
20514
|
const issues = [];
|
|
20474
20515
|
const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
|
|
20475
20516
|
const repositoryIds = /* @__PURE__ */ new Set();
|
|
@@ -20537,6 +20578,33 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
20537
20578
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
20538
20579
|
if (!workflows.has(defaultWorkflow)) issues.push(issue2("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
20539
20580
|
for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
|
|
20581
|
+
if (projectRoot) {
|
|
20582
|
+
const writerRolePath = join2(projectRoot, ".kxm", "roles", "writer.yaml");
|
|
20583
|
+
const implementerAgent = agents.get("implementer") ?? agents.get("writer");
|
|
20584
|
+
if (existsSync3(writerRolePath) && implementerAgent) {
|
|
20585
|
+
try {
|
|
20586
|
+
const rawRole = parseRestrictedYaml2(readFileSync2(writerRolePath, "utf8"));
|
|
20587
|
+
const roleObj = objectValue(rawRole);
|
|
20588
|
+
const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
|
|
20589
|
+
const enabledRosterModels = rosterEntries.filter((entry) => entry.enabled !== false).map((entry) => stringValue(entry.model)).filter((m2) => Boolean(m2));
|
|
20590
|
+
const agentModelObj = objectValue(implementerAgent.value.model);
|
|
20591
|
+
const agentModelStr = stringValue(implementerAgent.value.model);
|
|
20592
|
+
const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
|
|
20593
|
+
const agentModel = agentModelObj ? stringValue(agentModelObj.model) : agentModelStr;
|
|
20594
|
+
const canonicalAgentModel = agentProvider && agentModel ? `${agentProvider}/${agentModel}` : agentModel;
|
|
20595
|
+
if (enabledRosterModels.length > 0 && canonicalAgentModel) {
|
|
20596
|
+
const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
|
|
20597
|
+
if (!matches) {
|
|
20598
|
+
issues.push(issue2("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
|
|
20599
|
+
}
|
|
20600
|
+
}
|
|
20601
|
+
} catch (error) {
|
|
20602
|
+
if (error instanceof VnextConfigError) {
|
|
20603
|
+
issues.push(...error.issues);
|
|
20604
|
+
}
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20540
20608
|
return sortIssues2(issues);
|
|
20541
20609
|
}
|
|
20542
20610
|
function vnextCanonicalJson(value) {
|
|
@@ -20721,7 +20789,7 @@ function loadVnextProject(projectRoot, options = {}) {
|
|
|
20721
20789
|
}
|
|
20722
20790
|
}
|
|
20723
20791
|
if (loadIssues.length > 0) throw new VnextConfigError(loadIssues);
|
|
20724
|
-
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry);
|
|
20792
|
+
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root);
|
|
20725
20793
|
if (issues.length > 0) throw new VnextConfigError(issues);
|
|
20726
20794
|
const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits3(left.logicalPath, right.logicalPath));
|
|
20727
20795
|
return {
|
|
@@ -21360,7 +21428,11 @@ function verifyArtifactExists(rootInput, pathInput) {
|
|
|
21360
21428
|
}
|
|
21361
21429
|
}
|
|
21362
21430
|
|
|
21431
|
+
// plugins/kxm/src/workflow.ts
|
|
21432
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
21433
|
+
|
|
21363
21434
|
// plugins/kxm/src/protocol.ts
|
|
21435
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
21364
21436
|
var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
|
|
21365
21437
|
var MIN_MESSAGE_TTL_MS = 1e3;
|
|
21366
21438
|
var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
@@ -21381,6 +21453,9 @@ var ProtocolError = class extends Error {
|
|
|
21381
21453
|
function nowIso() {
|
|
21382
21454
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
21383
21455
|
}
|
|
21456
|
+
function newId(prefix) {
|
|
21457
|
+
return `${prefix}_${randomUUID2().replaceAll("-", "")}`;
|
|
21458
|
+
}
|
|
21384
21459
|
function requireString(value, field, options = {}) {
|
|
21385
21460
|
if (typeof value !== "string") {
|
|
21386
21461
|
throw new ProtocolError(400, `${field} must be a string`);
|
|
@@ -21394,6 +21469,65 @@ function requireString(value, field, options = {}) {
|
|
|
21394
21469
|
}
|
|
21395
21470
|
return result;
|
|
21396
21471
|
}
|
|
21472
|
+
function optionalString(value, field, max) {
|
|
21473
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
21474
|
+
return requireString(value, field, { max });
|
|
21475
|
+
}
|
|
21476
|
+
var TERMINAL_RECEIPT_SCHEMA = "kxm.terminal-receipt.v1";
|
|
21477
|
+
var VALID_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["accepted", "audit_escalation", "rejected", "error"]);
|
|
21478
|
+
function validateTerminalReceipt(value) {
|
|
21479
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
21480
|
+
throw new ProtocolError(400, "terminal receipt must be an object", "invalid_terminal_receipt");
|
|
21481
|
+
}
|
|
21482
|
+
const record = value;
|
|
21483
|
+
if (record.schema !== void 0 && record.schema !== TERMINAL_RECEIPT_SCHEMA) {
|
|
21484
|
+
throw new ProtocolError(400, `terminal receipt schema must be ${TERMINAL_RECEIPT_SCHEMA}`, "invalid_terminal_receipt");
|
|
21485
|
+
}
|
|
21486
|
+
const status = record.status;
|
|
21487
|
+
if (!status || !VALID_TERMINAL_STATUSES.has(status)) {
|
|
21488
|
+
throw new ProtocolError(
|
|
21489
|
+
400,
|
|
21490
|
+
`terminal receipt status must be one of: ${Array.from(VALID_TERMINAL_STATUSES).join(", ")}`,
|
|
21491
|
+
"invalid_terminal_receipt"
|
|
21492
|
+
);
|
|
21493
|
+
}
|
|
21494
|
+
const seat = requireString(record.seat, "seat", { max: 64 });
|
|
21495
|
+
const runId = requireString(record.runId, "runId", { max: 128 });
|
|
21496
|
+
const stageId = requireString(record.stageId, "stageId", { max: 128 });
|
|
21497
|
+
const timestamp = requireString(record.timestamp, "timestamp", { max: 64 });
|
|
21498
|
+
const host = requireString(record.host, "host", { max: 64 });
|
|
21499
|
+
const model = requireString(record.model, "model", { max: 128 });
|
|
21500
|
+
let evidence;
|
|
21501
|
+
if (record.evidence !== void 0) {
|
|
21502
|
+
if (!record.evidence || typeof record.evidence !== "object" || Array.isArray(record.evidence)) {
|
|
21503
|
+
throw new ProtocolError(400, "terminal receipt evidence must be an object", "invalid_terminal_receipt");
|
|
21504
|
+
}
|
|
21505
|
+
evidence = record.evidence;
|
|
21506
|
+
}
|
|
21507
|
+
let metrics;
|
|
21508
|
+
if (record.metrics !== void 0) {
|
|
21509
|
+
if (!record.metrics || typeof record.metrics !== "object" || Array.isArray(record.metrics)) {
|
|
21510
|
+
throw new ProtocolError(400, "terminal receipt metrics must be an object", "invalid_terminal_receipt");
|
|
21511
|
+
}
|
|
21512
|
+
metrics = record.metrics;
|
|
21513
|
+
}
|
|
21514
|
+
const escalationReason = optionalString(record.escalationReason, "escalationReason", 1024);
|
|
21515
|
+
const ruling = optionalString(record.ruling, "ruling", 2048);
|
|
21516
|
+
return {
|
|
21517
|
+
schema: TERMINAL_RECEIPT_SCHEMA,
|
|
21518
|
+
status,
|
|
21519
|
+
seat,
|
|
21520
|
+
runId,
|
|
21521
|
+
stageId,
|
|
21522
|
+
timestamp,
|
|
21523
|
+
host,
|
|
21524
|
+
model,
|
|
21525
|
+
...evidence ? { evidence } : {},
|
|
21526
|
+
...metrics ? { metrics } : {},
|
|
21527
|
+
...escalationReason ? { escalationReason } : {},
|
|
21528
|
+
...ruling ? { ruling } : {}
|
|
21529
|
+
};
|
|
21530
|
+
}
|
|
21397
21531
|
|
|
21398
21532
|
// plugins/kxm/src/workflow.ts
|
|
21399
21533
|
var PROMOTABLE_JOURNAL_CATEGORIES = ["skill-candidate", "hypothesis", "experiment"];
|
|
@@ -21430,6 +21564,20 @@ function stringArray(value, name) {
|
|
|
21430
21564
|
function canonicalWorkflowEvidenceKey(value) {
|
|
21431
21565
|
return value.trim().replace(/\s+/gu, " ").toLowerCase();
|
|
21432
21566
|
}
|
|
21567
|
+
function normalizeWorkflowEvidence(value) {
|
|
21568
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
21569
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
21570
|
+
for (const [requirement, candidate] of Object.entries(value)) {
|
|
21571
|
+
const key = canonicalWorkflowEvidenceKey(requirement);
|
|
21572
|
+
if (!key) continue;
|
|
21573
|
+
const values = Array.isArray(candidate) ? candidate : [candidate];
|
|
21574
|
+
const safeValues = values.filter((item) => typeof item === "string" && Boolean(item.trim())).map((item) => item.trim());
|
|
21575
|
+
if (safeValues.length > 0) {
|
|
21576
|
+
normalized.set(key, [.../* @__PURE__ */ new Set([...normalized.get(key) ?? [], ...safeValues])]);
|
|
21577
|
+
}
|
|
21578
|
+
}
|
|
21579
|
+
return Object.fromEntries(normalized);
|
|
21580
|
+
}
|
|
21433
21581
|
function normalizeVerifiedWorkflowEvidence(value) {
|
|
21434
21582
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
21435
21583
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -21445,6 +21593,105 @@ function normalizeVerifiedWorkflowEvidence(value) {
|
|
|
21445
21593
|
}
|
|
21446
21594
|
return Object.fromEntries(result);
|
|
21447
21595
|
}
|
|
21596
|
+
function mergeVerifiedWorkflowEvidence(current, incoming = {}) {
|
|
21597
|
+
const merged = new Map(Object.entries(normalizeVerifiedWorkflowEvidence(current)));
|
|
21598
|
+
for (const [rawRequirement, snapshots] of Object.entries(incoming)) {
|
|
21599
|
+
const requirement = canonicalWorkflowEvidenceKey(rawRequirement);
|
|
21600
|
+
if (!requirement) continue;
|
|
21601
|
+
const values = [...merged.get(requirement) ?? []];
|
|
21602
|
+
for (const snapshot of snapshots) {
|
|
21603
|
+
if (!values.some((candidate) => candidate.messageId === snapshot.messageId)) values.push(snapshot);
|
|
21604
|
+
}
|
|
21605
|
+
if (values.length) merged.set(requirement, values);
|
|
21606
|
+
}
|
|
21607
|
+
return Object.fromEntries(merged);
|
|
21608
|
+
}
|
|
21609
|
+
function mergeWorkflowEvidence(current, incoming = {}) {
|
|
21610
|
+
const merged = new Map(Object.entries(normalizeWorkflowEvidence(current)));
|
|
21611
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21612
|
+
for (const [rawRequirement, rawValue] of Object.entries(incoming)) {
|
|
21613
|
+
const requirement = canonicalWorkflowEvidenceKey(rawRequirement);
|
|
21614
|
+
if (!requirement || typeof rawValue !== "string" || !rawValue.trim()) {
|
|
21615
|
+
throw new ProtocolError(400, "evidence must contain non-empty keyed string values", "invalid_workflow_evidence");
|
|
21616
|
+
}
|
|
21617
|
+
if (seen.has(requirement)) {
|
|
21618
|
+
throw new ProtocolError(
|
|
21619
|
+
400,
|
|
21620
|
+
`evidence contains duplicate normalized requirement identity: ${requirement}`,
|
|
21621
|
+
"invalid_workflow_evidence"
|
|
21622
|
+
);
|
|
21623
|
+
}
|
|
21624
|
+
seen.add(requirement);
|
|
21625
|
+
const value = rawValue.trim();
|
|
21626
|
+
const values = merged.get(requirement) ?? [];
|
|
21627
|
+
if (!values.includes(value)) values.push(value);
|
|
21628
|
+
merged.set(requirement, values);
|
|
21629
|
+
}
|
|
21630
|
+
return Object.fromEntries(merged);
|
|
21631
|
+
}
|
|
21632
|
+
function activeWorkflowAttempt(stage) {
|
|
21633
|
+
return stage.attempts + 1;
|
|
21634
|
+
}
|
|
21635
|
+
function validIsoTimestamp(value) {
|
|
21636
|
+
if (!value) return void 0;
|
|
21637
|
+
const parsed = Date.parse(value);
|
|
21638
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
21639
|
+
}
|
|
21640
|
+
function peerEvidenceRequirementStatus(stage, requirementKey, runId, verifiedEvidence = normalizeVerifiedWorkflowEvidence(stage.verifiedEvidence)) {
|
|
21641
|
+
const canonicalKey = canonicalWorkflowEvidenceKey(requirementKey);
|
|
21642
|
+
const policy = stage.resolvedEvidencePolicies?.[canonicalKey];
|
|
21643
|
+
if (!policy) return void 0;
|
|
21644
|
+
const attempt = activeWorkflowAttempt(stage);
|
|
21645
|
+
const eligibleIds = new Set(policy.eligibleProducers.map((producer) => producer.id));
|
|
21646
|
+
const producers = /* @__PURE__ */ new Set();
|
|
21647
|
+
for (const snapshot of verifiedEvidence[canonicalKey] ?? []) {
|
|
21648
|
+
if (snapshot.schema === "pi-mesh.verified-peer-evidence.v1" && snapshot.status === "replied" && snapshot.context?.schema === "pi-mesh.workflow-message-context.v1" && snapshot.context.runId === runId && snapshot.context.stageId === stage.id && snapshot.context.requirementKey === canonicalKey && snapshot.context.attempt === attempt && eligibleIds.has(snapshot.producerId) && /^[a-f0-9]{64}$/.test(snapshot.requestSha256) && /^[a-f0-9]{64}$/.test(snapshot.replySha256) && validIsoTimestamp(snapshot.createdAt) !== void 0 && validIsoTimestamp(snapshot.replyCreatedAt) !== void 0 && validIsoTimestamp(snapshot.repliedAt) !== void 0 && validIsoTimestamp(snapshot.verifiedAt) !== void 0) producers.add(snapshot.producerId);
|
|
21649
|
+
}
|
|
21650
|
+
const approval = stage.degradationApprovals?.find(
|
|
21651
|
+
(candidate) => candidate.requirementKey === canonicalKey && candidate.attempt === attempt
|
|
21652
|
+
);
|
|
21653
|
+
const effectiveMinProducers = approval?.approvedMinProducers ?? policy.minProducers;
|
|
21654
|
+
return {
|
|
21655
|
+
requirementKey: canonicalKey,
|
|
21656
|
+
policyMinProducers: policy.minProducers,
|
|
21657
|
+
effectiveMinProducers,
|
|
21658
|
+
producers: [...producers],
|
|
21659
|
+
met: producers.size >= effectiveMinProducers,
|
|
21660
|
+
degraded: Boolean(approval && producers.size < policy.minProducers && producers.size >= effectiveMinProducers),
|
|
21661
|
+
...approval ? { approval } : {}
|
|
21662
|
+
};
|
|
21663
|
+
}
|
|
21664
|
+
function requireCompleteEvidence(stage, evidence, verifiedEvidence, runId) {
|
|
21665
|
+
const missing = [];
|
|
21666
|
+
const peerStatuses = [];
|
|
21667
|
+
for (const rawRequirement of stage.requiredEvidence) {
|
|
21668
|
+
const requirement = canonicalWorkflowEvidenceKey(rawRequirement);
|
|
21669
|
+
const peerStatus = peerEvidenceRequirementStatus(stage, requirement, runId, verifiedEvidence);
|
|
21670
|
+
if (peerStatus) {
|
|
21671
|
+
peerStatuses.push(peerStatus);
|
|
21672
|
+
if (!peerStatus.met) missing.push(requirement);
|
|
21673
|
+
} else if (stage.evidencePolicies?.[requirement]) {
|
|
21674
|
+
throw new ProtocolError(
|
|
21675
|
+
409,
|
|
21676
|
+
`stage ${stage.id} evidence policy ${requirement} was not resolved when the run started`,
|
|
21677
|
+
"workflow_evidence_policy_unresolved"
|
|
21678
|
+
);
|
|
21679
|
+
} else if (!evidence[requirement]?.length) {
|
|
21680
|
+
missing.push(requirement);
|
|
21681
|
+
}
|
|
21682
|
+
}
|
|
21683
|
+
if (missing.length === 0) return peerStatuses;
|
|
21684
|
+
throw new ProtocolError(
|
|
21685
|
+
400,
|
|
21686
|
+
`stage ${stage.id} is missing required evidence: ${missing.join(", ")}`,
|
|
21687
|
+
"workflow_evidence_incomplete",
|
|
21688
|
+
{
|
|
21689
|
+
missingRequirements: missing,
|
|
21690
|
+
providedRequirements: Object.keys(evidence),
|
|
21691
|
+
peerRequirements: peerStatuses
|
|
21692
|
+
}
|
|
21693
|
+
);
|
|
21694
|
+
}
|
|
21448
21695
|
function parseWorkflowEvidencePolicies(value, stageId, requiredEvidence, workflowId, targetName, warn) {
|
|
21449
21696
|
if (value === void 0) return void 0;
|
|
21450
21697
|
const rawPolicies = object(value, `stage ${stageId} evidencePolicies`);
|
|
@@ -21617,12 +21864,17 @@ function parseWorkflowDefinitions(raw, environment = process.env, onWarning) {
|
|
|
21617
21864
|
if (stageMaxTransitions !== void 0 && (!Number.isInteger(stageMaxTransitions) || stageMaxTransitions < 1 || stageMaxTransitions > 100)) {
|
|
21618
21865
|
throw new Error(`stage ${stageId} maxTransitions must be an integer between 1 and 100`);
|
|
21619
21866
|
}
|
|
21867
|
+
const autoResumeLimit = stage.autoResumeLimit;
|
|
21868
|
+
if (autoResumeLimit !== void 0 && (!Number.isInteger(autoResumeLimit) || autoResumeLimit < 1 || autoResumeLimit > 20)) {
|
|
21869
|
+
throw new Error(`stage ${stageId} autoResumeLimit must be an integer between 1 and 20`);
|
|
21870
|
+
}
|
|
21620
21871
|
return {
|
|
21621
21872
|
id: stageId,
|
|
21622
21873
|
label: requireString(stage.label ?? stageId, "stage.label", { max: 128 }),
|
|
21623
21874
|
instructions: requireString(stage.instructions, "stage.instructions", { max: 4e3 }),
|
|
21624
21875
|
requiredEvidence,
|
|
21625
21876
|
maxAttempts,
|
|
21877
|
+
...autoResumeLimit !== void 0 ? { autoResumeLimit } : {},
|
|
21626
21878
|
...area ? { area } : {},
|
|
21627
21879
|
...evidencePolicies ? { evidencePolicies } : {},
|
|
21628
21880
|
...on ? { on } : {},
|
|
@@ -21726,9 +21978,298 @@ function parseOutcomeMap(stageId, raw) {
|
|
|
21726
21978
|
}
|
|
21727
21979
|
return map;
|
|
21728
21980
|
}
|
|
21981
|
+
function resolveOutcomeRule(stage, outcomeKey) {
|
|
21982
|
+
const raw = stage.on?.[outcomeKey];
|
|
21983
|
+
return raw === void 0 ? void 0 : normalizeOutcomeValue(raw, `stage ${stage.id} on.${outcomeKey}`);
|
|
21984
|
+
}
|
|
21985
|
+
function transitionCounts(run, fromStage, outcome, target) {
|
|
21986
|
+
const records = run.transitions ?? [];
|
|
21987
|
+
return {
|
|
21988
|
+
total: records.length,
|
|
21989
|
+
fromStage: records.filter((record) => record.fromStage === fromStage).length,
|
|
21990
|
+
forEdge: records.filter((record) => record.fromStage === fromStage && record.outcome === outcome && record.toStage === target).length
|
|
21991
|
+
};
|
|
21992
|
+
}
|
|
21993
|
+
function recordTransition(run, fromStage, rule, outcome, attempt, evidenceKeys, timestamp) {
|
|
21994
|
+
const record = {
|
|
21995
|
+
id: newId("trans"),
|
|
21996
|
+
fromStage,
|
|
21997
|
+
toStage: rule.target,
|
|
21998
|
+
outcome,
|
|
21999
|
+
attempt,
|
|
22000
|
+
evidenceKeys,
|
|
22001
|
+
at: timestamp
|
|
22002
|
+
};
|
|
22003
|
+
run.transitions = [...run.transitions ?? [], record];
|
|
22004
|
+
return record;
|
|
22005
|
+
}
|
|
22006
|
+
function enterStage(run, stage, timestamp) {
|
|
22007
|
+
stage.status = "in_progress";
|
|
22008
|
+
stage.attempts = 0;
|
|
22009
|
+
stage.evidence = {};
|
|
22010
|
+
stage.verifiedEvidence = {};
|
|
22011
|
+
stage.startedAt = timestamp;
|
|
22012
|
+
stage.updatedAt = timestamp;
|
|
22013
|
+
delete stage.summary;
|
|
22014
|
+
run.currentStage = stage.id;
|
|
22015
|
+
run.updatedAt = timestamp;
|
|
22016
|
+
}
|
|
22017
|
+
function takeDeclaredTransition(run, stage, rule, outcome, summary, attempt, timestamp, evidenceKeys) {
|
|
22018
|
+
const definitionBudget = run.maxTransitions;
|
|
22019
|
+
const counts = transitionCounts(run, stage.id, outcome, rule.target);
|
|
22020
|
+
const edgeExhausted = rule.maxTransitions !== void 0 && counts.forEdge >= rule.maxTransitions;
|
|
22021
|
+
const stageExhausted = stage.maxTransitions !== void 0 && counts.fromStage >= stage.maxTransitions;
|
|
22022
|
+
const globalExhausted = definitionBudget !== void 0 && counts.total >= definitionBudget;
|
|
22023
|
+
if (edgeExhausted || stageExhausted || globalExhausted) {
|
|
22024
|
+
stage.completedAt = timestamp;
|
|
22025
|
+
run.status = "failed";
|
|
22026
|
+
delete run.currentStage;
|
|
22027
|
+
run.updatedAt = timestamp;
|
|
22028
|
+
return {
|
|
22029
|
+
retry: false,
|
|
22030
|
+
completed: false,
|
|
22031
|
+
run,
|
|
22032
|
+
exhausted: true
|
|
22033
|
+
};
|
|
22034
|
+
}
|
|
22035
|
+
const record = recordTransition(run, stage.id, rule, outcome, attempt, evidenceKeys.slice(0, 32), timestamp);
|
|
22036
|
+
if (rule.target === WORKFLOW_TERMINAL_TARGET) {
|
|
22037
|
+
stage.completedAt = timestamp;
|
|
22038
|
+
run.status = "completed";
|
|
22039
|
+
delete run.currentStage;
|
|
22040
|
+
run.completedAt = timestamp;
|
|
22041
|
+
run.updatedAt = timestamp;
|
|
22042
|
+
return { retry: false, completed: true, run, transition: record };
|
|
22043
|
+
}
|
|
22044
|
+
const target = run.stages.find((candidate) => candidate.id === rule.target);
|
|
22045
|
+
if (!target) {
|
|
22046
|
+
run.status = "failed";
|
|
22047
|
+
delete run.currentStage;
|
|
22048
|
+
return { retry: false, completed: false, run, exhausted: true };
|
|
22049
|
+
}
|
|
22050
|
+
stage.summary = summary;
|
|
22051
|
+
enterStage(run, target, timestamp);
|
|
22052
|
+
return { retry: false, completed: false, run, transition: record };
|
|
22053
|
+
}
|
|
22054
|
+
function evidenceValueSha256(evidence, key) {
|
|
22055
|
+
const values = evidence[canonicalWorkflowEvidenceKey(key)];
|
|
22056
|
+
if (!values || values.length === 0) return void 0;
|
|
22057
|
+
return createHash4("sha256").update([...values].sort().join("\n"), "utf8").digest("hex");
|
|
22058
|
+
}
|
|
22059
|
+
function enforceOracles(run, stageId, evidence) {
|
|
22060
|
+
if (run.oracle) {
|
|
22061
|
+
const presented = evidenceValueSha256(evidence, run.oracle.evidenceKey);
|
|
22062
|
+
if (presented !== void 0 && presented !== run.oracle.sha256) {
|
|
22063
|
+
throw new ProtocolError(
|
|
22064
|
+
400,
|
|
22065
|
+
`evidence ${run.oracle.evidenceKey} does not match the immutable reproduction oracle captured at ${run.oracle.capturedAt}; the confirmed reproduction may not be weakened`,
|
|
22066
|
+
"weakened_reproduction"
|
|
22067
|
+
);
|
|
22068
|
+
}
|
|
22069
|
+
}
|
|
22070
|
+
if (run.requirePlanHash?.includes(stageId) && !run.planHash) {
|
|
22071
|
+
throw new ProtocolError(
|
|
22072
|
+
400,
|
|
22073
|
+
`stage ${stageId} requires an approved plan hash before it can checkpoint`,
|
|
22074
|
+
"plan_hash_required"
|
|
22075
|
+
);
|
|
22076
|
+
}
|
|
22077
|
+
}
|
|
22078
|
+
function captureOracles(run, stageId, evidence, timestamp) {
|
|
22079
|
+
if (run.reproOracle?.stageId === stageId) {
|
|
22080
|
+
const sha256 = evidenceValueSha256(evidence, run.reproOracle.evidenceKey);
|
|
22081
|
+
if (sha256 !== void 0) {
|
|
22082
|
+
run.oracle = { evidenceKey: run.reproOracle.evidenceKey, sha256, capturedAt: timestamp };
|
|
22083
|
+
}
|
|
22084
|
+
}
|
|
22085
|
+
if (run.planHashConfig?.stageId === stageId) {
|
|
22086
|
+
const sha256 = evidenceValueSha256(evidence, run.planHashConfig.evidenceKey);
|
|
22087
|
+
if (sha256 !== void 0) {
|
|
22088
|
+
run.planHash = { evidenceKey: run.planHashConfig.evidenceKey, sha256, capturedAt: timestamp };
|
|
22089
|
+
}
|
|
22090
|
+
}
|
|
22091
|
+
}
|
|
22092
|
+
function checkpointRun(run, stageId, status, summary, evidence, timestamp, verifiedEvidence = {}, outcome) {
|
|
22093
|
+
if (run.status !== "running") throw new ProtocolError(409, `workflow is ${run.status}`, "workflow_terminal");
|
|
22094
|
+
const stage = run.stages.find((candidate) => candidate.id === stageId);
|
|
22095
|
+
if (!stage) throw new ProtocolError(404, `workflow stage not found: ${stageId}`, "workflow_stage_not_found");
|
|
22096
|
+
if (stage.id !== run.currentStage || stage.status !== "in_progress") {
|
|
22097
|
+
throw new ProtocolError(409, `stage ${stageId} is not currently active`, "workflow_stage_out_of_order");
|
|
22098
|
+
}
|
|
22099
|
+
const accumulatedEvidence = mergeWorkflowEvidence(stage.evidence, evidence);
|
|
22100
|
+
const accumulatedVerifiedEvidence = mergeVerifiedWorkflowEvidence(stage.verifiedEvidence, verifiedEvidence);
|
|
22101
|
+
enforceOracles(run, stageId, accumulatedEvidence);
|
|
22102
|
+
const peerStatuses = status === "passed" ? requireCompleteEvidence(stage, accumulatedEvidence, accumulatedVerifiedEvidence, run.id) : [];
|
|
22103
|
+
const degradedRequirements = peerStatuses.filter((peerStatus) => peerStatus.degraded);
|
|
22104
|
+
stage.attempts += 1;
|
|
22105
|
+
stage.summary = summary;
|
|
22106
|
+
if (status === "passed") {
|
|
22107
|
+
stage.evidence = accumulatedEvidence;
|
|
22108
|
+
if (Object.keys(accumulatedVerifiedEvidence).length) stage.verifiedEvidence = accumulatedVerifiedEvidence;
|
|
22109
|
+
captureOracles(run, stageId, accumulatedEvidence, timestamp);
|
|
22110
|
+
if (degradedRequirements.length) {
|
|
22111
|
+
stage.degraded = true;
|
|
22112
|
+
stage.degradedRequirements = degradedRequirements.map((peerStatus) => peerStatus.requirementKey);
|
|
22113
|
+
}
|
|
22114
|
+
}
|
|
22115
|
+
stage.updatedAt = timestamp;
|
|
22116
|
+
run.updatedAt = timestamp;
|
|
22117
|
+
if (status !== "passed") {
|
|
22118
|
+
stage.status = status;
|
|
22119
|
+
if (stage.attempts >= stage.maxAttempts) {
|
|
22120
|
+
stage.completedAt = timestamp;
|
|
22121
|
+
run.status = "failed";
|
|
22122
|
+
delete run.currentStage;
|
|
22123
|
+
return { retry: false, completed: false, run };
|
|
22124
|
+
}
|
|
22125
|
+
if (stage.autoResumeLimit !== void 0 && stage.attempts >= stage.autoResumeLimit) {
|
|
22126
|
+
stage.status = "in_progress";
|
|
22127
|
+
const reason = summary || `autoResumeLimit of ${stage.autoResumeLimit} reached on stage ${stage.id}`;
|
|
22128
|
+
const receipt = validateTerminalReceipt({
|
|
22129
|
+
schema: TERMINAL_RECEIPT_SCHEMA,
|
|
22130
|
+
status: "audit_escalation",
|
|
22131
|
+
seat: stage.id,
|
|
22132
|
+
runId: run.id,
|
|
22133
|
+
stageId: stage.id,
|
|
22134
|
+
timestamp,
|
|
22135
|
+
host: "pi",
|
|
22136
|
+
model: "default",
|
|
22137
|
+
escalationReason: reason
|
|
22138
|
+
});
|
|
22139
|
+
const expiresAt = new Date(Date.parse(timestamp) + 24 * 60 * 60 * 1e3).toISOString();
|
|
22140
|
+
waitForWorkflowSignal(run, stage.id, "audit_escalation", reason, timestamp, expiresAt);
|
|
22141
|
+
stage.receipt = receipt;
|
|
22142
|
+
stage.auditEscalation = {
|
|
22143
|
+
reason,
|
|
22144
|
+
timestamp,
|
|
22145
|
+
receipt
|
|
22146
|
+
};
|
|
22147
|
+
return { retry: false, completed: false, run };
|
|
22148
|
+
}
|
|
22149
|
+
stage.status = status;
|
|
22150
|
+
const outcomeKey = outcome ?? status;
|
|
22151
|
+
const rule = resolveOutcomeRule(stage, outcomeKey);
|
|
22152
|
+
if (rule && stage.attempts < stage.maxAttempts) {
|
|
22153
|
+
const result = takeDeclaredTransition(run, stage, rule, outcomeKey, summary, stage.attempts, timestamp, Object.keys(accumulatedEvidence));
|
|
22154
|
+
if (result.transition !== void 0 && !result.completed && rule.target !== stage.id) {
|
|
22155
|
+
stage.status = "pending";
|
|
22156
|
+
}
|
|
22157
|
+
return result;
|
|
22158
|
+
}
|
|
22159
|
+
stage.status = "in_progress";
|
|
22160
|
+
return { retry: true, completed: false, run };
|
|
22161
|
+
}
|
|
22162
|
+
stage.status = "passed";
|
|
22163
|
+
stage.completedAt = timestamp;
|
|
22164
|
+
const passedRule = resolveOutcomeRule(stage, outcome ?? "passed");
|
|
22165
|
+
if (passedRule) {
|
|
22166
|
+
return takeDeclaredTransition(run, stage, passedRule, outcome ?? "passed", summary, stage.attempts, timestamp, Object.keys(accumulatedEvidence));
|
|
22167
|
+
}
|
|
22168
|
+
const next = run.stages.find((candidate) => candidate.status === "pending");
|
|
22169
|
+
if (next) {
|
|
22170
|
+
next.status = "in_progress";
|
|
22171
|
+
next.startedAt = timestamp;
|
|
22172
|
+
next.updatedAt = timestamp;
|
|
22173
|
+
run.currentStage = next.id;
|
|
22174
|
+
return {
|
|
22175
|
+
retry: false,
|
|
22176
|
+
completed: false,
|
|
22177
|
+
run,
|
|
22178
|
+
...degradedRequirements.length ? { degraded: true } : {}
|
|
22179
|
+
};
|
|
22180
|
+
}
|
|
22181
|
+
run.status = "completed";
|
|
22182
|
+
delete run.currentStage;
|
|
22183
|
+
run.completedAt = timestamp;
|
|
22184
|
+
return {
|
|
22185
|
+
retry: false,
|
|
22186
|
+
completed: true,
|
|
22187
|
+
run,
|
|
22188
|
+
...degradedRequirements.length ? { degraded: true } : {}
|
|
22189
|
+
};
|
|
22190
|
+
}
|
|
22191
|
+
function waitForWorkflowSignal(run, stageId, signalKey, summary, timestamp, expiresAt, evidence = {}, verifiedEvidence = {}) {
|
|
22192
|
+
if (run.status !== "running") throw new ProtocolError(409, `workflow is ${run.status}`, "workflow_not_running");
|
|
22193
|
+
const stage = run.stages.find((candidate) => candidate.id === stageId);
|
|
22194
|
+
if (!stage) throw new ProtocolError(404, `workflow stage not found: ${stageId}`, "workflow_stage_not_found");
|
|
22195
|
+
if (stage.id !== run.currentStage || stage.status !== "in_progress") {
|
|
22196
|
+
throw new ProtocolError(409, `stage ${stageId} is not currently active`, "workflow_stage_out_of_order");
|
|
22197
|
+
}
|
|
22198
|
+
if (Date.parse(expiresAt) <= Date.parse(timestamp)) {
|
|
22199
|
+
throw new ProtocolError(400, "workflow signal expiry must be in the future", "workflow_wait_invalid");
|
|
22200
|
+
}
|
|
22201
|
+
stage.evidence = mergeWorkflowEvidence(stage.evidence, evidence);
|
|
22202
|
+
const accumulatedVerifiedEvidence = mergeVerifiedWorkflowEvidence(stage.verifiedEvidence, verifiedEvidence);
|
|
22203
|
+
if (Object.keys(accumulatedVerifiedEvidence).length) stage.verifiedEvidence = accumulatedVerifiedEvidence;
|
|
22204
|
+
stage.status = "waiting";
|
|
22205
|
+
stage.updatedAt = timestamp;
|
|
22206
|
+
run.status = "waiting";
|
|
22207
|
+
run.waiting = { stageId, signalKey, summary, createdAt: timestamp, expiresAt };
|
|
22208
|
+
run.updatedAt = timestamp;
|
|
22209
|
+
return run;
|
|
22210
|
+
}
|
|
22211
|
+
function resumeWorkflowFromSignal(run, signalKey, status, summary, evidence, timestamp) {
|
|
22212
|
+
if (run.status !== "waiting" || !run.waiting) {
|
|
22213
|
+
throw new ProtocolError(409, `workflow is ${run.status}`, "workflow_not_waiting");
|
|
22214
|
+
}
|
|
22215
|
+
if (run.waiting.signalKey !== signalKey) {
|
|
22216
|
+
throw new ProtocolError(409, `workflow is waiting for ${run.waiting.signalKey}`, "workflow_signal_mismatch");
|
|
22217
|
+
}
|
|
22218
|
+
const stageId = run.waiting.stageId;
|
|
22219
|
+
const stage = run.stages.find((candidate) => candidate.id === stageId);
|
|
22220
|
+
if (!stage || stage.id !== run.currentStage || stage.status !== "waiting") {
|
|
22221
|
+
throw new ProtocolError(409, "workflow wait state is inconsistent", "workflow_wait_inconsistent");
|
|
22222
|
+
}
|
|
22223
|
+
const accumulatedEvidence = mergeWorkflowEvidence(stage.evidence, evidence);
|
|
22224
|
+
if (status === "passed") {
|
|
22225
|
+
requireCompleteEvidence(
|
|
22226
|
+
stage,
|
|
22227
|
+
accumulatedEvidence,
|
|
22228
|
+
normalizeVerifiedWorkflowEvidence(stage.verifiedEvidence),
|
|
22229
|
+
run.id
|
|
22230
|
+
);
|
|
22231
|
+
}
|
|
22232
|
+
run.status = "running";
|
|
22233
|
+
stage.status = "in_progress";
|
|
22234
|
+
delete run.waiting;
|
|
22235
|
+
const result = checkpointRun(run, stageId, status, summary, evidence, timestamp);
|
|
22236
|
+
return { ...result, stageId };
|
|
22237
|
+
}
|
|
22238
|
+
function resumeWorkflowFromRuling(run, ruling, timestamp, options = {}) {
|
|
22239
|
+
if (run.status !== "waiting" || !run.waiting) {
|
|
22240
|
+
throw new ProtocolError(409, `workflow is ${run.status}`, "workflow_not_waiting");
|
|
22241
|
+
}
|
|
22242
|
+
if (run.waiting.signalKey !== "audit_escalation") {
|
|
22243
|
+
throw new ProtocolError(
|
|
22244
|
+
409,
|
|
22245
|
+
`workflow is waiting for signal '${run.waiting.signalKey}', not 'audit_escalation'`,
|
|
22246
|
+
"workflow_signal_mismatch"
|
|
22247
|
+
);
|
|
22248
|
+
}
|
|
22249
|
+
const stageId = run.waiting.stageId;
|
|
22250
|
+
const stage = run.stages.find((s) => s.id === stageId);
|
|
22251
|
+
if (!stage) throw new ProtocolError(404, `workflow stage not found: ${stageId}`, "workflow_stage_not_found");
|
|
22252
|
+
const status = options.status ?? "passed";
|
|
22253
|
+
const evidence = options.evidence ?? {};
|
|
22254
|
+
if (stage.auditEscalation) {
|
|
22255
|
+
stage.auditEscalation.ruling = ruling;
|
|
22256
|
+
if (stage.auditEscalation.receipt) {
|
|
22257
|
+
stage.auditEscalation.receipt.ruling = ruling;
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
stage.summary = `Resumed by operator ruling: ${ruling}`;
|
|
22261
|
+
return resumeWorkflowFromSignal(
|
|
22262
|
+
run,
|
|
22263
|
+
"audit_escalation",
|
|
22264
|
+
status,
|
|
22265
|
+
`Resumed: ${ruling}`,
|
|
22266
|
+
evidence,
|
|
22267
|
+
timestamp
|
|
22268
|
+
);
|
|
22269
|
+
}
|
|
21729
22270
|
|
|
21730
22271
|
// plugins/kxm/src/github-watch.ts
|
|
21731
|
-
import { createHmac, randomUUID as
|
|
22272
|
+
import { createHmac, randomUUID as randomUUID3 } from "node:crypto";
|
|
21732
22273
|
async function fetchWithTimeout(fetchImpl, input, init, timeoutMs) {
|
|
21733
22274
|
const controller = new AbortController();
|
|
21734
22275
|
const timer = setTimeout(() => controller.abort(), Math.max(1, timeoutMs));
|
|
@@ -21814,7 +22355,7 @@ async function watchGithubChecks(input) {
|
|
|
21814
22355
|
}
|
|
21815
22356
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
21816
22357
|
const now = input.now ?? Date.now;
|
|
21817
|
-
const sleep = input.sleep ?? ((ms) => new Promise((
|
|
22358
|
+
const sleep = input.sleep ?? ((ms) => new Promise((resolve23) => setTimeout(resolve23, ms)));
|
|
21818
22359
|
const deadline = now() + input.timeoutMs;
|
|
21819
22360
|
const headers = {
|
|
21820
22361
|
authorization: `Bearer ${token}`,
|
|
@@ -21827,7 +22368,7 @@ async function watchGithubChecks(input) {
|
|
|
21827
22368
|
"workflow.signal": input.signalKey
|
|
21828
22369
|
};
|
|
21829
22370
|
const explicitDeliveryId = input.deliveryId?.trim();
|
|
21830
|
-
const deliveryGeneration =
|
|
22371
|
+
const deliveryGeneration = randomUUID3();
|
|
21831
22372
|
let deliveryId = explicitDeliveryId || `github-watch:${deliveryGeneration}:pr-${input.pr}`;
|
|
21832
22373
|
const deliver = async (status, summary, evidence) => {
|
|
21833
22374
|
const boundedEvidence = Object.fromEntries(Object.entries({ ...contextEvidence, ...evidence }).slice(0, 64));
|
|
@@ -22143,7 +22684,7 @@ function writeRetrospective(outDir, doc) {
|
|
|
22143
22684
|
|
|
22144
22685
|
// plugins/kxm/src/skills.ts
|
|
22145
22686
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
22146
|
-
import { createHash as
|
|
22687
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
22147
22688
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, rmSync, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22148
22689
|
import { join as join4 } from "node:path";
|
|
22149
22690
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
@@ -22168,7 +22709,7 @@ var SkillLifecycleError = class extends Error {
|
|
|
22168
22709
|
}
|
|
22169
22710
|
};
|
|
22170
22711
|
function skillContentSha256(content) {
|
|
22171
|
-
return
|
|
22712
|
+
return createHash5("sha256").update(content, "utf8").digest("hex");
|
|
22172
22713
|
}
|
|
22173
22714
|
function skillIdFor(name, contentSha256) {
|
|
22174
22715
|
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -23229,7 +23770,7 @@ function readRoutingRecords(path4) {
|
|
|
23229
23770
|
|
|
23230
23771
|
// plugins/kxm/src/prices.ts
|
|
23231
23772
|
var import_yaml5 = __toESM(require_dist(), 1);
|
|
23232
|
-
import { createHash as
|
|
23773
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
23233
23774
|
import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
|
|
23234
23775
|
import { join as join7 } from "node:path";
|
|
23235
23776
|
function hashPriceCatalog(catalog) {
|
|
@@ -23251,7 +23792,7 @@ function hashPriceCatalog(catalog) {
|
|
|
23251
23792
|
}))
|
|
23252
23793
|
})).sort((a, b2) => a.id.localeCompare(b2.id))
|
|
23253
23794
|
};
|
|
23254
|
-
return
|
|
23795
|
+
return createHash6("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
|
|
23255
23796
|
}
|
|
23256
23797
|
function parsePriceCatalog(text) {
|
|
23257
23798
|
const parsed = (0, import_yaml5.parse)(text);
|
|
@@ -23414,7 +23955,7 @@ async function refreshModelInventory(options) {
|
|
|
23414
23955
|
if (item.fast === true) current.capabilities.speed = { fast: true, tiers: ["fast"], source };
|
|
23415
23956
|
byId.set(key, current);
|
|
23416
23957
|
};
|
|
23417
|
-
const
|
|
23958
|
+
const native2 = (command, args, source) => {
|
|
23418
23959
|
const result = spawnSync3(command, args, { encoding: "utf8", timeout: 2e4, shell: false });
|
|
23419
23960
|
const ok = result.status === 0;
|
|
23420
23961
|
sources[source] = { url: `${command} ${args.join(" ")}`, ok, ...ok ? {} : { error: (result.stderr || "command failed").trim().slice(0, 240) } };
|
|
@@ -23428,9 +23969,9 @@ async function refreshModelInventory(options) {
|
|
|
23428
23969
|
}
|
|
23429
23970
|
}
|
|
23430
23971
|
};
|
|
23431
|
-
|
|
23432
|
-
|
|
23433
|
-
|
|
23972
|
+
native2("pi", ["--list-models"], "pi");
|
|
23973
|
+
native2("grok", ["models"], "grok");
|
|
23974
|
+
native2("agy", ["models"], "agy");
|
|
23434
23975
|
const openrouter = await fetchModels(env.KXM_OPENROUTER_MODELS_URL?.trim() || OR_URL, env.OPENROUTER_API_KEY);
|
|
23435
23976
|
const openrouterUrl = env.KXM_OPENROUTER_MODELS_URL?.trim() || OR_URL;
|
|
23436
23977
|
sources.openrouter = { url: openrouterUrl, ok: !openrouter.error, ...openrouter.error ? { error: openrouter.error } : {} };
|
|
@@ -23647,7 +24188,7 @@ function writeSession(assetsDir, session, dryRun = false) {
|
|
|
23647
24188
|
}
|
|
23648
24189
|
|
|
23649
24190
|
// plugins/kxm/src/improve.ts
|
|
23650
|
-
import { createHash as
|
|
24191
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
23651
24192
|
import { existsSync as existsSync9, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
|
|
23652
24193
|
import { join as join11, relative as relative3, resolve as resolve6 } from "node:path";
|
|
23653
24194
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
@@ -23795,7 +24336,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
23795
24336
|
const isCandidate = recurrence >= minRecurrence && verifyPassRate >= minPassRate;
|
|
23796
24337
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
23797
24338
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
23798
|
-
const keyHash =
|
|
24339
|
+
const keyHash = createHash7("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
23799
24340
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
23800
24341
|
rows.push({
|
|
23801
24342
|
workflowHash: group.workflowHash,
|
|
@@ -28076,10 +28617,10 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
28076
28617
|
* @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
|
|
28077
28618
|
*/
|
|
28078
28619
|
queryTerminalBackgroundColor({ timeoutMs }) {
|
|
28079
|
-
return new Promise((
|
|
28620
|
+
return new Promise((resolve23) => {
|
|
28080
28621
|
const query = {
|
|
28081
28622
|
settled: false,
|
|
28082
|
-
resolve:
|
|
28623
|
+
resolve: resolve23,
|
|
28083
28624
|
timer: void 0
|
|
28084
28625
|
};
|
|
28085
28626
|
query.timer = setTimeout(() => {
|
|
@@ -28102,7 +28643,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
28102
28643
|
* `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
|
|
28103
28644
|
*/
|
|
28104
28645
|
queryTerminalColorScheme({ timeoutMs }) {
|
|
28105
|
-
return new Promise((
|
|
28646
|
+
return new Promise((resolve23) => {
|
|
28106
28647
|
let settled = false;
|
|
28107
28648
|
let timer;
|
|
28108
28649
|
let unsubscribe = () => {
|
|
@@ -28116,7 +28657,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
28116
28657
|
timer = void 0;
|
|
28117
28658
|
}
|
|
28118
28659
|
unsubscribe();
|
|
28119
|
-
|
|
28660
|
+
resolve23(scheme);
|
|
28120
28661
|
};
|
|
28121
28662
|
unsubscribe = this.onTerminalColorSchemeChange(settle);
|
|
28122
28663
|
timer = setTimeout(() => settle(void 0), timeoutMs);
|
|
@@ -29838,18 +30379,18 @@ var StdinBuffer = class extends EventEmitter2 {
|
|
|
29838
30379
|
|
|
29839
30380
|
// node_modules/@earendil-works/pi-tui/dist/terminal.js
|
|
29840
30381
|
import * as fs2 from "node:fs";
|
|
29841
|
-
import { createRequire as
|
|
30382
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
29842
30383
|
import * as path3 from "node:path";
|
|
29843
30384
|
|
|
29844
30385
|
// node_modules/@earendil-works/pi-tui/dist/native-modifiers.js
|
|
29845
|
-
import { createRequire as
|
|
30386
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
29846
30387
|
import * as path2 from "node:path";
|
|
29847
30388
|
|
|
29848
30389
|
// node_modules/@earendil-works/pi-tui/dist/native-module-path.js
|
|
29849
|
-
import { createRequire } from "node:module";
|
|
30390
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
29850
30391
|
import { dirname as dirname6, join as join12 } from "node:path";
|
|
29851
30392
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29852
|
-
var moduleRequire =
|
|
30393
|
+
var moduleRequire = createRequire2(import.meta.url);
|
|
29853
30394
|
var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
|
|
29854
30395
|
function getNativeModuleCandidates(nativePath, options = {}) {
|
|
29855
30396
|
const moduleDir = dirname6(fileURLToPath2(options.moduleUrl ?? import.meta.url));
|
|
@@ -29864,7 +30405,7 @@ function getNativeModuleCandidates(nativePath, options = {}) {
|
|
|
29864
30405
|
}
|
|
29865
30406
|
|
|
29866
30407
|
// node_modules/@earendil-works/pi-tui/dist/native-modifiers.js
|
|
29867
|
-
var cjsRequire =
|
|
30408
|
+
var cjsRequire = createRequire3(import.meta.url);
|
|
29868
30409
|
var nativeModifiersHelper;
|
|
29869
30410
|
function isNativeModifiersHelper(value) {
|
|
29870
30411
|
if (typeof value !== "object" || value === null)
|
|
@@ -29911,7 +30452,7 @@ function isNativeModifierPressed(key) {
|
|
|
29911
30452
|
}
|
|
29912
30453
|
|
|
29913
30454
|
// node_modules/@earendil-works/pi-tui/dist/terminal.js
|
|
29914
|
-
var cjsRequire2 =
|
|
30455
|
+
var cjsRequire2 = createRequire4(import.meta.url);
|
|
29915
30456
|
var TERMINAL_PROGRESS_KEEPALIVE_MS = 1e3;
|
|
29916
30457
|
var TERMINAL_PROGRESS_ACTIVE_SEQUENCE = "\x1B]9;4;3\x07";
|
|
29917
30458
|
var TERMINAL_PROGRESS_CLEAR_SEQUENCE = "\x1B]9;4;0\x07";
|
|
@@ -30200,7 +30741,7 @@ var ProcessTerminal = class {
|
|
|
30200
30741
|
break;
|
|
30201
30742
|
if (now - lastDataTime >= idleMs)
|
|
30202
30743
|
break;
|
|
30203
|
-
await new Promise((
|
|
30744
|
+
await new Promise((resolve23) => setTimeout(resolve23, Math.min(idleMs, timeLeft)));
|
|
30204
30745
|
}
|
|
30205
30746
|
} finally {
|
|
30206
30747
|
process.stdin.removeListener("data", onData);
|
|
@@ -30813,10 +31354,10 @@ function paintBox(box, screen, totalWidth) {
|
|
|
30813
31354
|
const firstRow = Math.max(box.rect.y, box.clip.y, 0);
|
|
30814
31355
|
const lastRow = Math.min(box.rect.y + box.rect.height, box.clip.y + box.clip.height, screen.length);
|
|
30815
31356
|
for (let row = firstRow; row < lastRow; row++) {
|
|
30816
|
-
const
|
|
30817
|
-
if (
|
|
31357
|
+
const sourceLine2 = box.lines[offset + row - box.rect.y];
|
|
31358
|
+
if (sourceLine2 === void 0)
|
|
30818
31359
|
continue;
|
|
30819
|
-
let line =
|
|
31360
|
+
let line = sourceLine2.replace(OSC133_ZONE_PREFIX, "");
|
|
30820
31361
|
const imageMetadata = getKittyImageMetadata(line);
|
|
30821
31362
|
if (imageMetadata) {
|
|
30822
31363
|
const clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);
|
|
@@ -32320,7 +32861,6 @@ import { join as join16, resolve as resolve8, dirname as dirname7 } from "node:p
|
|
|
32320
32861
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
32321
32862
|
|
|
32322
32863
|
// plugins/kxm/src/external-effects.ts
|
|
32323
|
-
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
32324
32864
|
function slugifyBranchPart(text, maxLength = 40) {
|
|
32325
32865
|
const cleaned = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
32326
32866
|
if (cleaned.length <= maxLength) return cleaned;
|
|
@@ -32331,7 +32871,6 @@ function slugifyBranchPart(text, maxLength = 40) {
|
|
|
32331
32871
|
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync10 } from "node:fs";
|
|
32332
32872
|
import { homedir } from "node:os";
|
|
32333
32873
|
import { isAbsolute as isAbsolute3, join as join15, resolve as resolve7 } from "node:path";
|
|
32334
|
-
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
32335
32874
|
function processExists(pid) {
|
|
32336
32875
|
try {
|
|
32337
32876
|
process.kill(pid, 0);
|
|
@@ -32493,7 +33032,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
32493
33032
|
let plans = [];
|
|
32494
33033
|
if (existsSync10(dataPath)) {
|
|
32495
33034
|
hasLegacy = true;
|
|
32496
|
-
const database = new
|
|
33035
|
+
const database = new DatabaseSync(dataPath, { readOnly: true });
|
|
32497
33036
|
try {
|
|
32498
33037
|
database.exec("PRAGMA busy_timeout = 5000");
|
|
32499
33038
|
agents = readJsonRows(database, "SELECT record FROM agents");
|
|
@@ -32518,7 +33057,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
32518
33057
|
if (existsSync10(registryDbPath)) {
|
|
32519
33058
|
hasVnext = true;
|
|
32520
33059
|
try {
|
|
32521
|
-
const regDb = new
|
|
33060
|
+
const regDb = new DatabaseSync(registryDbPath, { readOnly: true });
|
|
32522
33061
|
try {
|
|
32523
33062
|
regDb.exec("PRAGMA busy_timeout = 5000");
|
|
32524
33063
|
const pRows = regDb.prepare("SELECT project_key FROM projects").all();
|
|
@@ -32546,7 +33085,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
32546
33085
|
if (existsSync10(eventDbPath)) {
|
|
32547
33086
|
hasVnext = true;
|
|
32548
33087
|
try {
|
|
32549
|
-
const eventDb = new
|
|
33088
|
+
const eventDb = new DatabaseSync(eventDbPath, { readOnly: true });
|
|
32550
33089
|
try {
|
|
32551
33090
|
eventDb.exec("PRAGMA busy_timeout = 5000");
|
|
32552
33091
|
const runRows = eventDb.prepare(`
|
|
@@ -33153,11 +33692,11 @@ async function readJson(response) {
|
|
|
33153
33692
|
}
|
|
33154
33693
|
async function waitForReconnect(signal, milliseconds = 1e3) {
|
|
33155
33694
|
if (signal.aborted) return;
|
|
33156
|
-
await new Promise((
|
|
33695
|
+
await new Promise((resolve23) => {
|
|
33157
33696
|
const done = () => {
|
|
33158
33697
|
clearTimeout(timer);
|
|
33159
33698
|
signal.removeEventListener("abort", done);
|
|
33160
|
-
|
|
33699
|
+
resolve23();
|
|
33161
33700
|
};
|
|
33162
33701
|
const timer = setTimeout(done, milliseconds);
|
|
33163
33702
|
timer.unref();
|
|
@@ -33519,12 +34058,12 @@ async function runMeshTui(input) {
|
|
|
33519
34058
|
|
|
33520
34059
|
// plugins/kxm/src/session-work.ts
|
|
33521
34060
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
33522
|
-
import { randomUUID as
|
|
34061
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
33523
34062
|
import { existsSync as existsSync13, mkdirSync as mkdirSync14, readFileSync as readFileSync13, renameSync as renameSync4, writeFileSync as writeFileSync12 } from "node:fs";
|
|
33524
34063
|
import { join as join19 } from "node:path";
|
|
33525
34064
|
|
|
33526
34065
|
// plugins/kxm/src/kxm-update.ts
|
|
33527
|
-
import { createHash as
|
|
34066
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
33528
34067
|
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync12 } from "node:fs";
|
|
33529
34068
|
import { join as join17 } from "node:path";
|
|
33530
34069
|
var KXM_UPDATE_SCHEMA = "kxm.update.v1";
|
|
@@ -33677,7 +34216,7 @@ async function fetchLatestKxmVersion(source, env, fetchImpl = fetch) {
|
|
|
33677
34216
|
function verifyReleaseAssetDigest(path4, sha256) {
|
|
33678
34217
|
if (!existsSync11(path4)) return false;
|
|
33679
34218
|
try {
|
|
33680
|
-
const actual =
|
|
34219
|
+
const actual = createHash8("sha256").update(readFileSync11(path4)).digest("hex");
|
|
33681
34220
|
return actual === sha256.toLowerCase();
|
|
33682
34221
|
} catch {
|
|
33683
34222
|
return false;
|
|
@@ -34002,7 +34541,7 @@ function writeCachedSessionBrief(stateDir, brief) {
|
|
|
34002
34541
|
try {
|
|
34003
34542
|
mkdirSync14(stateDir, { recursive: true, mode: 448 });
|
|
34004
34543
|
const file = join19(stateDir, "session-brief.json");
|
|
34005
|
-
const tmp = `${file}.tmp.${
|
|
34544
|
+
const tmp = `${file}.tmp.${randomUUID4().slice(0, 8)}`;
|
|
34006
34545
|
writeFileSync12(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
|
|
34007
34546
|
renameSync4(tmp, file);
|
|
34008
34547
|
} catch {
|
|
@@ -34185,7 +34724,7 @@ import { join as join21 } from "node:path";
|
|
|
34185
34724
|
|
|
34186
34725
|
// plugins/kxm/src/vnext-bindings.ts
|
|
34187
34726
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
34188
|
-
import { createHash as
|
|
34727
|
+
import { createHash as createHash9, randomUUID as randomUUID5 } from "node:crypto";
|
|
34189
34728
|
import {
|
|
34190
34729
|
chmodSync as chmodSync2,
|
|
34191
34730
|
closeSync,
|
|
@@ -34201,7 +34740,6 @@ import {
|
|
|
34201
34740
|
writeFileSync as writeFileSync13
|
|
34202
34741
|
} from "node:fs";
|
|
34203
34742
|
import { homedir as homedir3 } from "node:os";
|
|
34204
|
-
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
34205
34743
|
import { dirname as dirname9, isAbsolute as isAbsolute5, join as join20, parse as parse5, relative as relative4, resolve as resolve10, sep as sep2 } from "node:path";
|
|
34206
34744
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
34207
34745
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
@@ -34247,7 +34785,7 @@ function vnextUserStateRoot(options = {}) {
|
|
|
34247
34785
|
function projectBindingKey(projectRoot, platform = process.platform) {
|
|
34248
34786
|
const canonical2 = canonicalHostPath2(projectRoot);
|
|
34249
34787
|
const keyInput = platform === "win32" ? canonical2.toLocaleLowerCase("en-US") : canonical2;
|
|
34250
|
-
return
|
|
34788
|
+
return createHash9("sha256").update(keyInput, "utf8").digest("hex");
|
|
34251
34789
|
}
|
|
34252
34790
|
function vnextLocalBindingFile(projectRoot, options = {}) {
|
|
34253
34791
|
const stateRoot = vnextUserStateRoot(options);
|
|
@@ -34426,7 +34964,7 @@ function withVnextLocalBindingLock(projectRoot, options, callback) {
|
|
|
34426
34964
|
bindingError("path", "local_binding_lock_invalid", "project-operation lock must be a regular file, not a link or directory");
|
|
34427
34965
|
}
|
|
34428
34966
|
}
|
|
34429
|
-
const database = new
|
|
34967
|
+
const database = new DatabaseSync(file);
|
|
34430
34968
|
try {
|
|
34431
34969
|
chmodSync2(file, 384);
|
|
34432
34970
|
} catch {
|
|
@@ -34474,7 +35012,7 @@ function writeVnextLocalBindings(projectRoot, projectId, repositories, options =
|
|
|
34474
35012
|
const { file, record } = planned;
|
|
34475
35013
|
if (!planned.written) return planned;
|
|
34476
35014
|
ensureBindingDirectory(file, vnextUserStateRoot(options));
|
|
34477
|
-
const temporary = join20(dirname9(file), `.repository-bindings-${process.pid}-${
|
|
35015
|
+
const temporary = join20(dirname9(file), `.repository-bindings-${process.pid}-${randomUUID5()}.tmp`);
|
|
34478
35016
|
let descriptor;
|
|
34479
35017
|
try {
|
|
34480
35018
|
descriptor = openSync(temporary, "wx", 384);
|
|
@@ -34605,36 +35143,481 @@ function resolveInstallKind(probe, npmGlobalRoot) {
|
|
|
34605
35143
|
return report("npm-local", classified.root);
|
|
34606
35144
|
}
|
|
34607
35145
|
|
|
35146
|
+
// plugins/kxm/src/init-guide-setup.ts
|
|
35147
|
+
var import_yaml9 = __toESM(require_dist(), 1);
|
|
35148
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
35149
|
+
import { dirname as dirname11, join as join23 } from "node:path";
|
|
35150
|
+
var NATIVE_VENDOR_HARNESS = Object.freeze({
|
|
35151
|
+
anthropic: "claude",
|
|
35152
|
+
openai: "codex",
|
|
35153
|
+
"x-ai": "grok",
|
|
35154
|
+
xai: "grok",
|
|
35155
|
+
google: "agy",
|
|
35156
|
+
moonshotai: "kimi",
|
|
35157
|
+
moonshot: "kimi",
|
|
35158
|
+
deepseek: "deepseek"
|
|
35159
|
+
});
|
|
35160
|
+
var GUIDE_WORKFLOWS = [
|
|
35161
|
+
{
|
|
35162
|
+
slug: "build-feature",
|
|
35163
|
+
area: "software-engineering",
|
|
35164
|
+
name: "Build Feature",
|
|
35165
|
+
summary: "PRD ingestion \u2192 system planning \u2192 spec/contract \u2192 scaffold \u2192 backend \u2192 frontend \u2192 SDK integration",
|
|
35166
|
+
stages: [
|
|
35167
|
+
{
|
|
35168
|
+
slug: "system-planning",
|
|
35169
|
+
title: "System planning",
|
|
35170
|
+
role: "lead-systems-planner",
|
|
35171
|
+
domain: "Decomposes requirements into an executable, dependency-ordered DAG of modules, tasks, and verification gates.",
|
|
35172
|
+
candidates: [
|
|
35173
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35174
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35175
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35176
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35177
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" }
|
|
35178
|
+
]
|
|
35179
|
+
},
|
|
35180
|
+
{
|
|
35181
|
+
slug: "spec-contract",
|
|
35182
|
+
title: "Spec and contract",
|
|
35183
|
+
role: "spec-contract-generator",
|
|
35184
|
+
domain: "Authors zero-ambiguity API contracts, type definitions, and schema validation rules before implementation begins.",
|
|
35185
|
+
candidates: [
|
|
35186
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35187
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35188
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
35189
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35190
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" }
|
|
35191
|
+
]
|
|
35192
|
+
},
|
|
35193
|
+
{
|
|
35194
|
+
slug: "scaffold",
|
|
35195
|
+
title: "Scaffold",
|
|
35196
|
+
role: "scaffold-build-specialist",
|
|
35197
|
+
domain: "Provisions monorepo workspace topologies, linters, Docker multi-stage builds, and CI pipelines.",
|
|
35198
|
+
candidates: [
|
|
35199
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35200
|
+
{ vendor: "mistralai", model: "devstral-2512" },
|
|
35201
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35202
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
35203
|
+
{ vendor: "kwaipilot", model: "kat-coder-pro-v2.5" }
|
|
35204
|
+
]
|
|
35205
|
+
},
|
|
35206
|
+
{
|
|
35207
|
+
slug: "backend-data",
|
|
35208
|
+
title: "Backend and data",
|
|
35209
|
+
role: "backend-data-implementer",
|
|
35210
|
+
domain: "Implements domain entities, SQL queries/migrations, ORM persistence, concurrency primitives, and transactional boundaries.",
|
|
35211
|
+
candidates: [
|
|
35212
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35213
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35214
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35215
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
35216
|
+
{ vendor: "kwaipilot", model: "kat-coder-pro-v2.5" }
|
|
35217
|
+
]
|
|
35218
|
+
},
|
|
35219
|
+
{
|
|
35220
|
+
slug: "frontend",
|
|
35221
|
+
title: "Frontend",
|
|
35222
|
+
role: "frontend-fullstack-implementer",
|
|
35223
|
+
domain: "Constructs stateful UI components, routing, client cache management, responsive styles, and accessible interactions.",
|
|
35224
|
+
candidates: [
|
|
35225
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" },
|
|
35226
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
35227
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35228
|
+
{ vendor: "bytedance-seed", model: "seed-2.0-code" },
|
|
35229
|
+
{ vendor: "openai", model: "gpt-5.3-codex" }
|
|
35230
|
+
]
|
|
35231
|
+
},
|
|
35232
|
+
{
|
|
35233
|
+
slug: "sdk-integration",
|
|
35234
|
+
title: "SDK integration",
|
|
35235
|
+
role: "sdk-integration-specialist",
|
|
35236
|
+
domain: "Wires external APIs, authentication flows (OAuth2/OIDC), webhook validation, cloud SDKs, and async worker queues.",
|
|
35237
|
+
candidates: [
|
|
35238
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35239
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35240
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35241
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35242
|
+
{ vendor: "google", model: "gemini-3.8-flash" }
|
|
35243
|
+
]
|
|
35244
|
+
}
|
|
35245
|
+
]
|
|
35246
|
+
},
|
|
35247
|
+
{
|
|
35248
|
+
slug: "refactor-repair-regressions",
|
|
35249
|
+
area: "software-engineering",
|
|
35250
|
+
name: "Refactor and Repair Regressions",
|
|
35251
|
+
summary: "Codebase smell analysis \u2192 modular decomposition \u2192 surgical multi-file transforms \u2192 equivalence verification",
|
|
35252
|
+
stages: [
|
|
35253
|
+
{
|
|
35254
|
+
slug: "refactor-architecture",
|
|
35255
|
+
title: "Refactor architecture",
|
|
35256
|
+
role: "refactoring-architect",
|
|
35257
|
+
domain: "Analyzes code coupling, designs clean modular boundaries, and authors step-by-step non-breaking migration plans.",
|
|
35258
|
+
candidates: [
|
|
35259
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35260
|
+
{ vendor: "z-ai", model: "glm-5.3" },
|
|
35261
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35262
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35263
|
+
{ vendor: "moonshotai", model: "kimi-k3" }
|
|
35264
|
+
]
|
|
35265
|
+
},
|
|
35266
|
+
{
|
|
35267
|
+
slug: "code-transform",
|
|
35268
|
+
title: "Code transform",
|
|
35269
|
+
role: "code-transform-implementer",
|
|
35270
|
+
domain: "Executes surgical multi-file edits, applying design patterns while preserving comments and formatting.",
|
|
35271
|
+
candidates: [
|
|
35272
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35273
|
+
{ vendor: "relace", model: "relace-apply-3" },
|
|
35274
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
35275
|
+
{ vendor: "morph", model: "morph-v3-large" },
|
|
35276
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" }
|
|
35277
|
+
]
|
|
35278
|
+
},
|
|
35279
|
+
{
|
|
35280
|
+
slug: "equivalence-verification",
|
|
35281
|
+
title: "Equivalence verification",
|
|
35282
|
+
role: "semantic-equivalence-verifier",
|
|
35283
|
+
domain: "Critic-with-gate: reviews public contract compliance and behavioral equivalence; deterministic checks remain the fixed witness gate and are never delegated to an LLM.",
|
|
35284
|
+
candidates: [
|
|
35285
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35286
|
+
{ vendor: "openai", model: "o3-mini-high" },
|
|
35287
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35288
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35289
|
+
{ vendor: "mistralai", model: "devstral-2512" }
|
|
35290
|
+
]
|
|
35291
|
+
}
|
|
35292
|
+
]
|
|
35293
|
+
},
|
|
35294
|
+
{
|
|
35295
|
+
slug: "stabilize-flaky-tests",
|
|
35296
|
+
area: "software-engineering",
|
|
35297
|
+
name: "Stabilize Flaky Tests",
|
|
35298
|
+
summary: "Flakiness root-cause extraction \u2192 deterministic mock/async hardening",
|
|
35299
|
+
stages: [
|
|
35300
|
+
{
|
|
35301
|
+
slug: "flakiness-diagnosis",
|
|
35302
|
+
title: "Flakiness diagnosis",
|
|
35303
|
+
role: "concurrency-flakiness-detective",
|
|
35304
|
+
domain: "Untangles timing hazards, async wait conditions, shared state pollution, and unhandled promise rejections.",
|
|
35305
|
+
candidates: [
|
|
35306
|
+
{ vendor: "openai", model: "o3" },
|
|
35307
|
+
{ vendor: "deepseek", model: "deepseek-r1-0528" },
|
|
35308
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
35309
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35310
|
+
{ vendor: "openai", model: "gpt-6-astra" }
|
|
35311
|
+
]
|
|
35312
|
+
}
|
|
35313
|
+
]
|
|
35314
|
+
},
|
|
35315
|
+
{
|
|
35316
|
+
slug: "design-software-system",
|
|
35317
|
+
area: "software-engineering",
|
|
35318
|
+
name: "Design Software System",
|
|
35319
|
+
summary: "Architecture \u2192 database topology \u2192 threat modeling \u2192 IaC \u2192 reliability review \u2192 protocol audit",
|
|
35320
|
+
stages: [
|
|
35321
|
+
{
|
|
35322
|
+
slug: "distributed-architecture",
|
|
35323
|
+
title: "Distributed architecture",
|
|
35324
|
+
role: "principal-distributed-architect",
|
|
35325
|
+
domain: "Evaluates CAP/PACELC trade-offs, consensus protocols (Raft, Sagas), partition boundaries, and authors primary RFCs.",
|
|
35326
|
+
candidates: [
|
|
35327
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35328
|
+
{ vendor: "openai", model: "o3" },
|
|
35329
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35330
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35331
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" }
|
|
35332
|
+
]
|
|
35333
|
+
},
|
|
35334
|
+
{
|
|
35335
|
+
slug: "database-topology",
|
|
35336
|
+
title: "Database topology",
|
|
35337
|
+
role: "scalability-database-topologist",
|
|
35338
|
+
domain: "Designs relational/NoSQL schemas, horizontal sharding keys, caching topologies, and zero-downtime migration plans.",
|
|
35339
|
+
candidates: [
|
|
35340
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35341
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35342
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35343
|
+
{ vendor: "qwen", model: "qwen3.8-2.4t-a95b" },
|
|
35344
|
+
{ vendor: "x-ai", model: "grok-4.6" }
|
|
35345
|
+
]
|
|
35346
|
+
},
|
|
35347
|
+
{
|
|
35348
|
+
slug: "threat-modeling",
|
|
35349
|
+
title: "Threat modeling",
|
|
35350
|
+
role: "security-architect-threat-modeler",
|
|
35351
|
+
domain: "Conducts STRIDE threat assessments, reviews IAM least-privilege policies, mTLS boundaries, and zero-trust architectures.",
|
|
35352
|
+
candidates: [
|
|
35353
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35354
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35355
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35356
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35357
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" }
|
|
35358
|
+
]
|
|
35359
|
+
},
|
|
35360
|
+
{
|
|
35361
|
+
slug: "iac-topology",
|
|
35362
|
+
title: "IaC topology",
|
|
35363
|
+
role: "iac-cloud-topology-engineer",
|
|
35364
|
+
domain: "Generates declarative Terraform/OpenTofu, Pulumi, Kubernetes CRDs, and VPC networking definitions.",
|
|
35365
|
+
candidates: [
|
|
35366
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35367
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35368
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
35369
|
+
{ vendor: "google", model: "gemini-3.8-flash" },
|
|
35370
|
+
{ vendor: "mistralai", model: "devstral-2512" }
|
|
35371
|
+
]
|
|
35372
|
+
},
|
|
35373
|
+
{
|
|
35374
|
+
slug: "reliability-review",
|
|
35375
|
+
title: "Reliability review",
|
|
35376
|
+
role: "reliability-chaos-reviewer",
|
|
35377
|
+
domain: "Evaluates single points of failure, RPO/RTO metrics, cascading timeouts, circuit breakers, and active-active failovers.",
|
|
35378
|
+
candidates: [
|
|
35379
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35380
|
+
{ vendor: "openai", model: "o3" },
|
|
35381
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35382
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35383
|
+
{ vendor: "moonshotai", model: "kimi-k3" }
|
|
35384
|
+
]
|
|
35385
|
+
},
|
|
35386
|
+
{
|
|
35387
|
+
slug: "protocol-audit",
|
|
35388
|
+
title: "Protocol audit",
|
|
35389
|
+
role: "architecture-critic-protocol-auditor",
|
|
35390
|
+
domain: "Independent peer-review; challenges unvalidated assumptions, prevents architectural drift, and verifies contracts.",
|
|
35391
|
+
candidates: [
|
|
35392
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35393
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35394
|
+
{ vendor: "z-ai", model: "glm-5.3" },
|
|
35395
|
+
{ vendor: "openai", model: "gpt-6-astra-pro" },
|
|
35396
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" }
|
|
35397
|
+
]
|
|
35398
|
+
}
|
|
35399
|
+
]
|
|
35400
|
+
},
|
|
35401
|
+
{
|
|
35402
|
+
slug: "maintain-documentation",
|
|
35403
|
+
area: "software-engineering",
|
|
35404
|
+
name: "Maintain Documentation",
|
|
35405
|
+
summary: "Accuracy audit \u2192 documentation write \u2192 review",
|
|
35406
|
+
stages: [
|
|
35407
|
+
{
|
|
35408
|
+
slug: "accuracy-audit",
|
|
35409
|
+
title: "Accuracy audit",
|
|
35410
|
+
role: "documentation-accuracy-auditor",
|
|
35411
|
+
domain: "Read-only audit of developer and operator documentation against CLI --help outputs, source environment variables, tool schemas, and repository configuration.",
|
|
35412
|
+
candidates: [
|
|
35413
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35414
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35415
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35416
|
+
{ vendor: "qwen", model: "qwen3.8-max-0902" },
|
|
35417
|
+
{ vendor: "z-ai", model: "glm-5.3" }
|
|
35418
|
+
]
|
|
35419
|
+
},
|
|
35420
|
+
{
|
|
35421
|
+
slug: "documentation-write",
|
|
35422
|
+
title: "Documentation write",
|
|
35423
|
+
role: "documentation-writer",
|
|
35424
|
+
domain: "Authors concise, accurate developer and operator documentation with clear terminology, correct command flags, and consistent formatting without marketing drift.",
|
|
35425
|
+
candidates: [
|
|
35426
|
+
{ vendor: "x-ai", model: "grok-4.6" },
|
|
35427
|
+
{ vendor: "qwen", model: "qwen3-coder-plus" },
|
|
35428
|
+
{ vendor: "anthropic", model: "claude-sonnet-5" },
|
|
35429
|
+
{ vendor: "openai", model: "gpt-5.3-codex" },
|
|
35430
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" }
|
|
35431
|
+
]
|
|
35432
|
+
},
|
|
35433
|
+
{
|
|
35434
|
+
slug: "review",
|
|
35435
|
+
title: "Review",
|
|
35436
|
+
role: "documentation-reviewer",
|
|
35437
|
+
domain: "Critic-with-gate: the architecture critic verifies authority and permission wording; the CLI critic verifies command/flag accuracy; deterministic docs lint remains the fixed witness gate.",
|
|
35438
|
+
candidates: [
|
|
35439
|
+
{ vendor: "anthropic", model: "claude-fable-5.1" },
|
|
35440
|
+
{ vendor: "openai", model: "gpt-5.6-sol" },
|
|
35441
|
+
{ vendor: "anthropic", model: "claude-opus-5" },
|
|
35442
|
+
{ vendor: "deepseek", model: "deepseek-v4-pro-0813" },
|
|
35443
|
+
{ vendor: "mistralai", model: "devstral-2512" }
|
|
35444
|
+
]
|
|
35445
|
+
}
|
|
35446
|
+
]
|
|
35447
|
+
}
|
|
35448
|
+
];
|
|
35449
|
+
function authenticatedHarnesses(inventory) {
|
|
35450
|
+
return new Set(
|
|
35451
|
+
inventory.harnesses.filter((entry) => entry.detected && entry.authenticated === true).map((entry) => entry.id)
|
|
35452
|
+
);
|
|
35453
|
+
}
|
|
35454
|
+
function resolveCandidate(candidates, eligible) {
|
|
35455
|
+
for (const candidate of candidates) {
|
|
35456
|
+
const nativeHarness = NATIVE_VENDOR_HARNESS[candidate.vendor];
|
|
35457
|
+
if (nativeHarness) {
|
|
35458
|
+
if (eligible.has(nativeHarness)) {
|
|
35459
|
+
return { harness: nativeHarness, provider: NATIVE_HARNESS_PROVIDERS[nativeHarness], model: candidate.model };
|
|
35460
|
+
}
|
|
35461
|
+
continue;
|
|
35462
|
+
}
|
|
35463
|
+
if (eligible.has("pi")) {
|
|
35464
|
+
return { harness: "pi", provider: "openrouter", model: `${candidate.vendor}/${candidate.model}` };
|
|
35465
|
+
}
|
|
35466
|
+
}
|
|
35467
|
+
return void 0;
|
|
35468
|
+
}
|
|
35469
|
+
function planGuideSetup(options) {
|
|
35470
|
+
const eligible = authenticatedHarnesses(options.inventory);
|
|
35471
|
+
const agents = /* @__PURE__ */ new Map();
|
|
35472
|
+
const workflows = [];
|
|
35473
|
+
const skipped = [];
|
|
35474
|
+
const wanted = new Set(options.selected);
|
|
35475
|
+
for (const workflow of GUIDE_WORKFLOWS) {
|
|
35476
|
+
if (!wanted.has(workflow.slug)) continue;
|
|
35477
|
+
let covered = true;
|
|
35478
|
+
for (const stage of workflow.stages) {
|
|
35479
|
+
if (agents.has(stage.role)) continue;
|
|
35480
|
+
const binding = resolveCandidate(stage.candidates, eligible);
|
|
35481
|
+
if (!binding) {
|
|
35482
|
+
skipped.push({
|
|
35483
|
+
workflow: workflow.slug,
|
|
35484
|
+
stage: stage.slug,
|
|
35485
|
+
role: stage.role,
|
|
35486
|
+
reason: "no guide candidate has an authenticated harness (see `kxm harness list`)"
|
|
35487
|
+
});
|
|
35488
|
+
covered = false;
|
|
35489
|
+
continue;
|
|
35490
|
+
}
|
|
35491
|
+
agents.set(stage.role, binding);
|
|
35492
|
+
}
|
|
35493
|
+
if (covered) workflows.push(workflow);
|
|
35494
|
+
else skipped.push({ workflow: workflow.slug, stage: "*", role: "-", reason: "workflow skipped: at least one stage has no eligible agent" });
|
|
35495
|
+
}
|
|
35496
|
+
return { agents, workflows, skipped };
|
|
35497
|
+
}
|
|
35498
|
+
var WRITER_ROLE_PATTERN = /(writer|implementer|scaffold|backend|frontend|sdk-integration|iac-cloud|code-transform|build-specialist)/;
|
|
35499
|
+
function isWriterRole(roleSlug) {
|
|
35500
|
+
return WRITER_ROLE_PATTERN.test(roleSlug);
|
|
35501
|
+
}
|
|
35502
|
+
function agentDocument(roleSlug, stage, binding) {
|
|
35503
|
+
const writer = isWriterRole(roleSlug);
|
|
35504
|
+
return {
|
|
35505
|
+
schema: "kxm.agent.v1",
|
|
35506
|
+
purpose: `${stage.domain} (workflow-guide ${stage.title}; candidate verification is the operator's responsibility)`,
|
|
35507
|
+
harness: binding.harness,
|
|
35508
|
+
model: { provider: binding.provider, model: binding.model },
|
|
35509
|
+
tools: { preset: writer ? "workspace-writer" : "read-only" },
|
|
35510
|
+
defaultRepositoryAccess: writer ? "none" : "read",
|
|
35511
|
+
repositories: { control: writer ? "write" : "read" },
|
|
35512
|
+
network: "provider-only",
|
|
35513
|
+
resultSchema: "kxm.assignment-result.v1"
|
|
35514
|
+
};
|
|
35515
|
+
}
|
|
35516
|
+
function workflowDocument(workflow) {
|
|
35517
|
+
const steps = workflow.stages.map((stage, index) => {
|
|
35518
|
+
const last = index === workflow.stages.length - 1;
|
|
35519
|
+
return {
|
|
35520
|
+
id: stage.slug,
|
|
35521
|
+
kind: "agent",
|
|
35522
|
+
agent: stage.role,
|
|
35523
|
+
maxAttempts: 2,
|
|
35524
|
+
on: {
|
|
35525
|
+
passed: last ? { target: "$terminal", terminalStatus: "completed" } : workflow.stages[index + 1].slug,
|
|
35526
|
+
failed: { target: "$terminal", terminalStatus: "failed" }
|
|
35527
|
+
}
|
|
35528
|
+
};
|
|
35529
|
+
});
|
|
35530
|
+
return {
|
|
35531
|
+
schema: "kxm.workflow.v1",
|
|
35532
|
+
description: `${workflow.name} (${workflow.area}, workflow-guide): ${workflow.summary}`,
|
|
35533
|
+
coordinator: "coordinator",
|
|
35534
|
+
limits: { maxTransitions: Math.max(4, workflow.stages.length * 4) },
|
|
35535
|
+
steps
|
|
35536
|
+
};
|
|
35537
|
+
}
|
|
35538
|
+
function renderGuideSetupFiles(projectRoot, plan) {
|
|
35539
|
+
const files = [];
|
|
35540
|
+
const roleStages = /* @__PURE__ */ new Map();
|
|
35541
|
+
for (const workflow of plan.workflows) {
|
|
35542
|
+
for (const stage of workflow.stages) roleStages.set(stage.role, stage);
|
|
35543
|
+
}
|
|
35544
|
+
for (const [role, binding] of [...plan.agents.entries()].sort(([a], [b2]) => a.localeCompare(b2))) {
|
|
35545
|
+
const stage = roleStages.get(role);
|
|
35546
|
+
if (!stage) continue;
|
|
35547
|
+
files.push({
|
|
35548
|
+
path: join23(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
35549
|
+
content: (0, import_yaml9.stringify)(agentDocument(role, stage, binding))
|
|
35550
|
+
});
|
|
35551
|
+
}
|
|
35552
|
+
for (const workflow of plan.workflows) {
|
|
35553
|
+
files.push({
|
|
35554
|
+
path: join23(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
35555
|
+
content: (0, import_yaml9.stringify)(workflowDocument(workflow))
|
|
35556
|
+
});
|
|
35557
|
+
}
|
|
35558
|
+
return files;
|
|
35559
|
+
}
|
|
35560
|
+
function writeGuideSetupFiles(files) {
|
|
35561
|
+
const written = [];
|
|
35562
|
+
const existed = [];
|
|
35563
|
+
for (const file of files) {
|
|
35564
|
+
if (existsSync17(file.path)) {
|
|
35565
|
+
existed.push(file.path);
|
|
35566
|
+
continue;
|
|
35567
|
+
}
|
|
35568
|
+
mkdirSync16(dirname11(file.path), { recursive: true });
|
|
35569
|
+
writeFileSync14(file.path, file.content, "utf8");
|
|
35570
|
+
written.push(file.path);
|
|
35571
|
+
}
|
|
35572
|
+
return { written, existed };
|
|
35573
|
+
}
|
|
35574
|
+
function parseGuideSelection(input, catalog = GUIDE_WORKFLOWS) {
|
|
35575
|
+
const trimmed = input.trim().toLowerCase();
|
|
35576
|
+
if (!trimmed || trimmed === "none" || trimmed === "n" || trimmed === "no") return [];
|
|
35577
|
+
if (trimmed === "all") return catalog.map((workflow) => workflow.slug);
|
|
35578
|
+
const selected = /* @__PURE__ */ new Set();
|
|
35579
|
+
for (const token of trimmed.split(/[,\s]+/).filter(Boolean)) {
|
|
35580
|
+
const index = Number.parseInt(token, 10);
|
|
35581
|
+
if (Number.isFinite(index) && index >= 1 && index <= catalog.length) {
|
|
35582
|
+
selected.add(catalog[index - 1].slug);
|
|
35583
|
+
continue;
|
|
35584
|
+
}
|
|
35585
|
+
const match = catalog.find((workflow) => workflow.slug === token);
|
|
35586
|
+
if (match) selected.add(match.slug);
|
|
35587
|
+
}
|
|
35588
|
+
return [...selected];
|
|
35589
|
+
}
|
|
35590
|
+
|
|
34608
35591
|
// plugins/kxm/src/vnext-init.ts
|
|
34609
|
-
import { randomUUID as
|
|
34610
|
-
import { existsSync as
|
|
34611
|
-
import { basename as basename5, join as
|
|
35592
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
35593
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
35594
|
+
import { basename as basename5, join as join26 } from "node:path";
|
|
34612
35595
|
|
|
34613
35596
|
// plugins/kxm/src/vnext-repair.ts
|
|
34614
|
-
import { randomUUID as
|
|
35597
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
34615
35598
|
import {
|
|
34616
35599
|
chmodSync as chmodSync3,
|
|
34617
35600
|
closeSync as closeSync2,
|
|
34618
|
-
existsSync as
|
|
35601
|
+
existsSync as existsSync19,
|
|
34619
35602
|
fsyncSync as fsyncSync2,
|
|
34620
35603
|
linkSync,
|
|
34621
35604
|
lstatSync as lstatSync5,
|
|
34622
|
-
mkdirSync as
|
|
35605
|
+
mkdirSync as mkdirSync18,
|
|
34623
35606
|
openSync as openSync2,
|
|
34624
35607
|
readFileSync as readFileSync18,
|
|
34625
35608
|
readdirSync as readdirSync7,
|
|
34626
35609
|
renameSync as renameSync6,
|
|
34627
35610
|
rmSync as rmSync5,
|
|
34628
|
-
writeFileSync as
|
|
35611
|
+
writeFileSync as writeFileSync16
|
|
34629
35612
|
} from "node:fs";
|
|
34630
|
-
import { dirname as
|
|
35613
|
+
import { dirname as dirname13, isAbsolute as isAbsolute6, join as join25, relative as relative5, resolve as resolve12 } from "node:path";
|
|
34631
35614
|
|
|
34632
35615
|
// plugins/kxm/src/vnext-permission.ts
|
|
34633
35616
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
34634
|
-
import { createHash as
|
|
34635
|
-
import { existsSync as
|
|
35617
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
35618
|
+
import { existsSync as existsSync18, mkdtempSync, mkdirSync as mkdirSync17, readFileSync as readFileSync17, rmSync as rmSync4, writeFileSync as writeFileSync15 } from "node:fs";
|
|
34636
35619
|
import { tmpdir } from "node:os";
|
|
34637
|
-
import { dirname as
|
|
35620
|
+
import { dirname as dirname12, join as join24, resolve as resolve11, sep as sep3 } from "node:path";
|
|
34638
35621
|
var PROSE_FIELDS = {
|
|
34639
35622
|
project: /* @__PURE__ */ new Set(["name", "description"]),
|
|
34640
35623
|
repository: /* @__PURE__ */ new Set(["description"]),
|
|
@@ -34837,7 +35820,7 @@ function vnextProseEntries(resource) {
|
|
|
34837
35820
|
return entries;
|
|
34838
35821
|
}
|
|
34839
35822
|
function valueHash(canonicalValue) {
|
|
34840
|
-
return `sha256:${
|
|
35823
|
+
return `sha256:${createHash10("sha256").update(canonicalValue, "utf8").digest("hex")}`;
|
|
34841
35824
|
}
|
|
34842
35825
|
function rankDirection(rank, baseValue, candidateValue) {
|
|
34843
35826
|
const base = JSON.parse(baseValue);
|
|
@@ -35155,9 +36138,9 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35155
36138
|
const tree = resolveTreeRevision(root, revision);
|
|
35156
36139
|
const listing = gitBuffer(root, ["ls-tree", "-r", "-z", "--name-only", tree]).toString("utf8");
|
|
35157
36140
|
const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
35158
|
-
const shadow = mkdtempSync(
|
|
36141
|
+
const shadow = mkdtempSync(join24(tmpdir(), "kxm-permission-base-"));
|
|
35159
36142
|
const shadowResolved = resolve11(shadow);
|
|
35160
|
-
const shadowMembersDir = `.kxm-shadow-members-${
|
|
36143
|
+
const shadowMembersDir = `.kxm-shadow-members-${createHash10("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
|
|
35161
36144
|
try {
|
|
35162
36145
|
for (const path4 of paths) {
|
|
35163
36146
|
const segments = path4.split("/");
|
|
@@ -35169,7 +36152,7 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35169
36152
|
message: "Git tree entry contains a traversal or non-portable segment"
|
|
35170
36153
|
}]);
|
|
35171
36154
|
}
|
|
35172
|
-
const absolute =
|
|
36155
|
+
const absolute = join24(shadowResolved, ...segments);
|
|
35173
36156
|
const resolved = resolve11(absolute);
|
|
35174
36157
|
if (!resolved.startsWith(`${shadowResolved}${sep3}`)) {
|
|
35175
36158
|
throw new VnextConfigError([{
|
|
@@ -35180,11 +36163,11 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35180
36163
|
}]);
|
|
35181
36164
|
}
|
|
35182
36165
|
const content = gitBuffer(root, ["cat-file", "blob", `${tree}:${path4}`]);
|
|
35183
|
-
|
|
35184
|
-
|
|
36166
|
+
mkdirSync17(dirname12(absolute), { recursive: true });
|
|
36167
|
+
writeFileSync15(absolute, content);
|
|
35185
36168
|
}
|
|
35186
36169
|
initShadowGitRoot(shadowResolved);
|
|
35187
|
-
const baseProjectFile =
|
|
36170
|
+
const baseProjectFile = join24(shadowResolved, ".kxm", "project.yaml");
|
|
35188
36171
|
let baseProject;
|
|
35189
36172
|
try {
|
|
35190
36173
|
baseProject = loadBaseProjectDeclarations(baseProjectFile);
|
|
@@ -35215,7 +36198,7 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35215
36198
|
}
|
|
35216
36199
|
const boundPath = options.repositoryBindings?.[member.repositoryId];
|
|
35217
36200
|
const memberWorktree = boundPath ? resolve11(boundPath) : member.pathHint ? resolve11(root, ...member.pathHint.split("/")) : void 0;
|
|
35218
|
-
if (!memberWorktree || !
|
|
36201
|
+
if (!memberWorktree || !existsSync18(memberWorktree)) {
|
|
35219
36202
|
if (member.required === false) continue;
|
|
35220
36203
|
throw new VnextConfigError([{
|
|
35221
36204
|
phase: "discovery",
|
|
@@ -35262,7 +36245,7 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35262
36245
|
const memberTree = git(memberWorktree, ["rev-parse", "--verify", "--quiet", `${pinnedCommit}^{tree}`]).trim();
|
|
35263
36246
|
const memberListing = gitBuffer(memberWorktree, ["ls-tree", "-r", "-z", "--name-only", memberTree]).toString("utf8");
|
|
35264
36247
|
const memberPaths = memberListing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
|
|
35265
|
-
const memberShadowRoot =
|
|
36248
|
+
const memberShadowRoot = join24(shadowResolved, shadowMembersDir, member.repositoryId);
|
|
35266
36249
|
if (!resolve11(memberShadowRoot).startsWith(`${resolve11(shadowResolved, shadowMembersDir)}${sep3}`)) {
|
|
35267
36250
|
throw new VnextConfigError([{
|
|
35268
36251
|
phase: "path",
|
|
@@ -35276,14 +36259,14 @@ function loadVnextProjectAtRevision(root, revision, options = {}) {
|
|
|
35276
36259
|
if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || /[\\:]/.test(segment))) {
|
|
35277
36260
|
throw new VnextConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry contains a traversal or non-portable segment" }]);
|
|
35278
36261
|
}
|
|
35279
|
-
const absolute =
|
|
36262
|
+
const absolute = join24(memberShadowRoot, ...segments);
|
|
35280
36263
|
const resolved = resolve11(absolute);
|
|
35281
36264
|
if (!resolved.startsWith(`${resolve11(memberShadowRoot)}${sep3}`)) {
|
|
35282
36265
|
throw new VnextConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry escapes the shadow root" }]);
|
|
35283
36266
|
}
|
|
35284
36267
|
const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
|
|
35285
|
-
|
|
35286
|
-
|
|
36268
|
+
mkdirSync17(dirname12(absolute), { recursive: true });
|
|
36269
|
+
writeFileSync15(absolute, content);
|
|
35287
36270
|
}
|
|
35288
36271
|
initShadowGitRoot(memberShadowRoot);
|
|
35289
36272
|
shadowBindings[member.repositoryId] = memberShadowRoot;
|
|
@@ -35304,7 +36287,7 @@ function portableMemberPathHint(pathHint) {
|
|
|
35304
36287
|
return pathHint !== "." && vnextPortablePath(pathHint);
|
|
35305
36288
|
}
|
|
35306
36289
|
function loadBaseProjectDeclarations(projectFile) {
|
|
35307
|
-
if (!
|
|
36290
|
+
if (!existsSync18(projectFile)) return [];
|
|
35308
36291
|
const value = parseRestrictedYaml2(readFileSync17(projectFile, "utf8"), ".kxm/project.yaml");
|
|
35309
36292
|
const repositories = Array.isArray(value?.repositories) ? value.repositories : [];
|
|
35310
36293
|
const members = [];
|
|
@@ -35397,7 +36380,7 @@ function writeDurableNew(file, bytes, mode) {
|
|
|
35397
36380
|
let descriptor;
|
|
35398
36381
|
try {
|
|
35399
36382
|
descriptor = openSync2(file, "wx", mode);
|
|
35400
|
-
|
|
36383
|
+
writeFileSync16(descriptor, bytes);
|
|
35401
36384
|
fsyncSync2(descriptor);
|
|
35402
36385
|
closeSync2(descriptor);
|
|
35403
36386
|
descriptor = void 0;
|
|
@@ -35408,13 +36391,13 @@ function writeDurableNew(file, bytes, mode) {
|
|
|
35408
36391
|
} finally {
|
|
35409
36392
|
if (descriptor !== void 0) closeSync2(descriptor);
|
|
35410
36393
|
}
|
|
35411
|
-
syncDirectory2(
|
|
36394
|
+
syncDirectory2(dirname13(file));
|
|
35412
36395
|
}
|
|
35413
36396
|
function writeDurable(file, bytes, mode) {
|
|
35414
36397
|
let descriptor;
|
|
35415
36398
|
try {
|
|
35416
36399
|
descriptor = openSync2(file, "w", mode);
|
|
35417
|
-
|
|
36400
|
+
writeFileSync16(descriptor, bytes);
|
|
35418
36401
|
fsyncSync2(descriptor);
|
|
35419
36402
|
closeSync2(descriptor);
|
|
35420
36403
|
descriptor = void 0;
|
|
@@ -35425,7 +36408,7 @@ function writeDurable(file, bytes, mode) {
|
|
|
35425
36408
|
} finally {
|
|
35426
36409
|
if (descriptor !== void 0) closeSync2(descriptor);
|
|
35427
36410
|
}
|
|
35428
|
-
syncDirectory2(
|
|
36411
|
+
syncDirectory2(dirname13(file));
|
|
35429
36412
|
}
|
|
35430
36413
|
function sameHostPath3(left, right) {
|
|
35431
36414
|
const first = resolve12(left);
|
|
@@ -35433,19 +36416,19 @@ function sameHostPath3(left, right) {
|
|
|
35433
36416
|
return process.platform === "win32" ? first.toLocaleLowerCase("en-US") === second.toLocaleLowerCase("en-US") : first === second;
|
|
35434
36417
|
}
|
|
35435
36418
|
function transactionRoot(projectRoot) {
|
|
35436
|
-
return
|
|
36419
|
+
return join25(resolve12(projectRoot), TRANSACTION_NAME);
|
|
35437
36420
|
}
|
|
35438
36421
|
function operationFile(projectRoot) {
|
|
35439
|
-
return
|
|
36422
|
+
return join25(transactionRoot(projectRoot), OPERATION_FILE);
|
|
35440
36423
|
}
|
|
35441
36424
|
function targetFile(projectRoot, portablePath2) {
|
|
35442
|
-
return
|
|
36425
|
+
return join25(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
|
|
35443
36426
|
}
|
|
35444
36427
|
function shadowRoot(projectRoot) {
|
|
35445
|
-
return
|
|
36428
|
+
return join25(transactionRoot(projectRoot), "shadow");
|
|
35446
36429
|
}
|
|
35447
36430
|
function backupFile(projectRoot, portablePath2) {
|
|
35448
|
-
return
|
|
36431
|
+
return join25(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
|
|
35449
36432
|
}
|
|
35450
36433
|
function portableManagedPath(path4) {
|
|
35451
36434
|
return path4.startsWith(".kxm/") && path4 !== VNEXT_TEMPLATE_PROVENANCE_PATH && path4.length <= 1024 && !path4.includes("\\") && !isAbsolute6(path4) && PORTABLE_PATH.test(path4) && !/[<>:"|?*]/.test(path4);
|
|
@@ -35457,21 +36440,21 @@ function readRegularBounded(file, label) {
|
|
|
35457
36440
|
return readFileSync18(file);
|
|
35458
36441
|
}
|
|
35459
36442
|
function readOptionalManaged(projectRoot, path4) {
|
|
35460
|
-
const file =
|
|
35461
|
-
if (!
|
|
35462
|
-
let parent =
|
|
36443
|
+
const file = join25(projectRoot, ...path4.split("/"));
|
|
36444
|
+
if (!existsSync19(file)) return void 0;
|
|
36445
|
+
let parent = dirname13(file);
|
|
35463
36446
|
const boundary = resolve12(projectRoot);
|
|
35464
36447
|
while (parent !== boundary) {
|
|
35465
36448
|
const stat = lstatSync5(parent);
|
|
35466
36449
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_parent_invalid", path4, "managed file parents must be regular directories", "path");
|
|
35467
|
-
parent =
|
|
36450
|
+
parent = dirname13(parent);
|
|
35468
36451
|
}
|
|
35469
36452
|
return readRegularBounded(file, path4);
|
|
35470
36453
|
}
|
|
35471
36454
|
function readProjectAndProvenance(projectRoot, schemasDir) {
|
|
35472
|
-
const projectFile =
|
|
35473
|
-
const provenanceFile =
|
|
35474
|
-
if (!
|
|
36455
|
+
const projectFile = join25(projectRoot, ".kxm", "project.yaml");
|
|
36456
|
+
const provenanceFile = join25(projectRoot, ...VNEXT_TEMPLATE_PROVENANCE_PATH.split("/"));
|
|
36457
|
+
if (!existsSync19(projectFile) || !existsSync19(provenanceFile)) return void 0;
|
|
35475
36458
|
const projectBytes = readOptionalManaged(projectRoot, ".kxm/project.yaml");
|
|
35476
36459
|
const provenanceBytes = readOptionalManaged(projectRoot, VNEXT_TEMPLATE_PROVENANCE_PATH);
|
|
35477
36460
|
if (!projectBytes || !provenanceBytes) return void 0;
|
|
@@ -35635,7 +36618,7 @@ function operationPlanSha(operation) {
|
|
|
35635
36618
|
function writeOperation(projectRoot, operation) {
|
|
35636
36619
|
const root = transactionRoot(projectRoot);
|
|
35637
36620
|
const file = operationFile(projectRoot);
|
|
35638
|
-
const temporary =
|
|
36621
|
+
const temporary = join25(root, `.operation-${operation.operationId}-${randomUUID6()}.tmp`);
|
|
35639
36622
|
try {
|
|
35640
36623
|
writeDurableNew(temporary, Buffer.from(`${JSON.stringify(operation, null, 2)}
|
|
35641
36624
|
`, "utf8"), 384);
|
|
@@ -35652,11 +36635,11 @@ function updatePhase(projectRoot, operation, phase) {
|
|
|
35652
36635
|
}
|
|
35653
36636
|
function readOperation(projectRoot, schemasDir) {
|
|
35654
36637
|
const root = transactionRoot(projectRoot);
|
|
35655
|
-
if (!
|
|
36638
|
+
if (!existsSync19(root)) return void 0;
|
|
35656
36639
|
const stat = lstatSync5(root);
|
|
35657
36640
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("init_transaction_invalid", TRANSACTION_NAME, "initialization transaction must be a regular directory", "path");
|
|
35658
36641
|
const file = operationFile(projectRoot);
|
|
35659
|
-
if (!
|
|
36642
|
+
if (!existsSync19(file)) {
|
|
35660
36643
|
const entries = readdirSync7(root, { withFileTypes: true });
|
|
35661
36644
|
const recoverable = entries.every((entry) => entry.isFile() && /^\.operation-op_[a-f0-9]{32}-[a-f0-9-]{36}\.tmp$/.test(entry.name));
|
|
35662
36645
|
if (recoverable) return void 0;
|
|
@@ -35684,15 +36667,15 @@ function readOperation(projectRoot, schemasDir) {
|
|
|
35684
36667
|
return operation;
|
|
35685
36668
|
}
|
|
35686
36669
|
function hasVnextInitTransaction(projectRoot) {
|
|
35687
|
-
return
|
|
36670
|
+
return existsSync19(transactionRoot(projectRoot));
|
|
35688
36671
|
}
|
|
35689
36672
|
function readTarget(projectRoot, path4) {
|
|
35690
|
-
return readOptionalManaged(
|
|
36673
|
+
return readOptionalManaged(join25(transactionRoot(projectRoot), "targets"), path4);
|
|
35691
36674
|
}
|
|
35692
36675
|
function writeTarget(projectRoot, path4, bytes) {
|
|
35693
36676
|
if (bytes.byteLength > MAX_MANAGED_FILE_BYTES) fail3("template_target_too_large", path4, `target exceeds ${MAX_MANAGED_FILE_BYTES} bytes`);
|
|
35694
36677
|
const file = targetFile(projectRoot, path4);
|
|
35695
|
-
|
|
36678
|
+
mkdirSync18(dirname13(file), { recursive: true, mode: 448 });
|
|
35696
36679
|
writeDurableNew(file, bytes, 384);
|
|
35697
36680
|
}
|
|
35698
36681
|
function writeAndVerifyBackups(projectRoot, operation) {
|
|
@@ -35704,7 +36687,7 @@ function writeAndVerifyBackups(projectRoot, operation) {
|
|
|
35704
36687
|
fail3("repair_preimage_changed", entry.path, "destination changed before transaction preparation; local bytes were preserved");
|
|
35705
36688
|
}
|
|
35706
36689
|
const file = backupFile(projectRoot, entry.path);
|
|
35707
|
-
|
|
36690
|
+
mkdirSync18(dirname13(file), { recursive: true, mode: 448 });
|
|
35708
36691
|
writeDurableNew(file, current, 384);
|
|
35709
36692
|
}
|
|
35710
36693
|
}
|
|
@@ -35719,7 +36702,7 @@ function writeKnownBackups(projectRoot, operation) {
|
|
|
35719
36702
|
fail3("init_transaction_backup_source_invalid", entry.path, "supported source template cannot reproduce the pinned preimage");
|
|
35720
36703
|
}
|
|
35721
36704
|
const file = backupFile(projectRoot, entry.path);
|
|
35722
|
-
|
|
36705
|
+
mkdirSync18(dirname13(file), { recursive: true, mode: 448 });
|
|
35723
36706
|
writeDurableNew(file, bytes, 384);
|
|
35724
36707
|
}
|
|
35725
36708
|
}
|
|
@@ -35727,7 +36710,7 @@ function verifyBackups(projectRoot, operation) {
|
|
|
35727
36710
|
if (operation.kind !== "repair") return;
|
|
35728
36711
|
for (const entry of operation.files) {
|
|
35729
36712
|
if (entry.observedSha256 === null) continue;
|
|
35730
|
-
const bytes = readOptionalManaged(
|
|
36713
|
+
const bytes = readOptionalManaged(join25(transactionRoot(projectRoot), "backups"), entry.path);
|
|
35731
36714
|
if (!bytes || vnextContentSha256(bytes) !== entry.observedSha256) {
|
|
35732
36715
|
fail3("init_transaction_backup_invalid", entry.path, "pinned preimage backup is missing or corrupt");
|
|
35733
36716
|
}
|
|
@@ -35761,10 +36744,10 @@ function sourceConfigFiles(projectRoot) {
|
|
|
35761
36744
|
".kxm/repo/repo.yaml",
|
|
35762
36745
|
".kxm/repo/env.yaml"
|
|
35763
36746
|
];
|
|
35764
|
-
const files = fixed.filter((path4) =>
|
|
36747
|
+
const files = fixed.filter((path4) => existsSync19(join25(projectRoot, ...path4.split("/"))));
|
|
35765
36748
|
for (const directory of ["agents", "models", "workflows"]) {
|
|
35766
|
-
const absolute =
|
|
35767
|
-
if (!
|
|
36749
|
+
const absolute = join25(projectRoot, ".kxm", directory);
|
|
36750
|
+
if (!existsSync19(absolute)) continue;
|
|
35768
36751
|
const stat = lstatSync5(absolute);
|
|
35769
36752
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_directory_invalid", `.kxm/${directory}`, "configuration directory must be regular", "path");
|
|
35770
36753
|
for (const entry of readdirSync7(absolute, { withFileTypes: true })) {
|
|
@@ -35786,23 +36769,23 @@ function absoluteMemberBindings(projectRoot, project, explicit = {}) {
|
|
|
35786
36769
|
if (repository.role !== "member" || typeof repository.id !== "string" || Object.hasOwn(bindings, repository.id)) continue;
|
|
35787
36770
|
if (typeof repository.pathHint === "string") {
|
|
35788
36771
|
const candidate2 = resolve12(projectRoot, ...repository.pathHint.split("/"));
|
|
35789
|
-
if (
|
|
36772
|
+
if (existsSync19(candidate2)) bindings[repository.id] = candidate2;
|
|
35790
36773
|
}
|
|
35791
36774
|
}
|
|
35792
36775
|
return bindings;
|
|
35793
36776
|
}
|
|
35794
36777
|
function prepareShadow(projectRoot, operation, options) {
|
|
35795
36778
|
const shadow = shadowRoot(projectRoot);
|
|
35796
|
-
|
|
36779
|
+
mkdirSync18(shadow, { recursive: true, mode: 448 });
|
|
35797
36780
|
let total = 0;
|
|
35798
36781
|
if (operation.kind === "repair") {
|
|
35799
36782
|
for (const path4 of sourceConfigFiles(projectRoot)) {
|
|
35800
36783
|
const bytes = readOptionalManaged(projectRoot, path4);
|
|
35801
36784
|
if (!bytes) fail3("repair_snapshot_file_missing", path4, "configuration changed during shadow preparation");
|
|
35802
36785
|
total += bytes.byteLength;
|
|
35803
|
-
const source =
|
|
35804
|
-
const destination =
|
|
35805
|
-
|
|
36786
|
+
const source = join25(projectRoot, ...path4.split("/"));
|
|
36787
|
+
const destination = join25(shadow, ...path4.split("/"));
|
|
36788
|
+
mkdirSync18(dirname13(destination), { recursive: true, mode: 448 });
|
|
35806
36789
|
writeDurableNew(destination, bytes, lstatSync5(source).mode & 511);
|
|
35807
36790
|
if (total > MAX_MANAGED_TOTAL_BYTES) fail3("repair_snapshot_too_large", ".kxm", `configuration snapshot exceeds ${MAX_MANAGED_TOTAL_BYTES} bytes`, "parse");
|
|
35808
36791
|
}
|
|
@@ -35810,12 +36793,12 @@ function prepareShadow(projectRoot, operation, options) {
|
|
|
35810
36793
|
for (const entry of operation.files) {
|
|
35811
36794
|
const bytes = readTarget(projectRoot, entry.path);
|
|
35812
36795
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
35813
|
-
const destination =
|
|
35814
|
-
|
|
35815
|
-
const mode =
|
|
36796
|
+
const destination = join25(shadow, ...entry.path.split("/"));
|
|
36797
|
+
mkdirSync18(dirname13(destination), { recursive: true, mode: 448 });
|
|
36798
|
+
const mode = existsSync19(destination) ? lstatSync5(destination).mode & 511 : 420;
|
|
35816
36799
|
writeDurable(destination, bytes, mode);
|
|
35817
36800
|
}
|
|
35818
|
-
const project = parseRestrictedYaml2(readFileSync18(
|
|
36801
|
+
const project = parseRestrictedYaml2(readFileSync18(join25(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
|
|
35819
36802
|
const bound = absoluteMemberBindings(projectRoot, project, options.repositoryBindings);
|
|
35820
36803
|
loadVnextProject(shadow, { ...loaderOptions(options), repositoryBindings: bound });
|
|
35821
36804
|
const provenance = readProjectAndProvenance(shadow, options.schemasDir)?.provenance;
|
|
@@ -35827,7 +36810,7 @@ function createOperation(projectRoot, kind, rendered, files, sourceTemplateRevis
|
|
|
35827
36810
|
const root = resolve12(projectRoot);
|
|
35828
36811
|
const operationWithoutHash = {
|
|
35829
36812
|
schema: "kxm.init-operation.v1",
|
|
35830
|
-
operationId: `op_${
|
|
36813
|
+
operationId: `op_${randomUUID6().replaceAll("-", "")}`,
|
|
35831
36814
|
kind,
|
|
35832
36815
|
projectRoot: root,
|
|
35833
36816
|
projectId: rendered.projectId,
|
|
@@ -35853,8 +36836,8 @@ function prepareOperation(projectRoot, operation, rendered, options) {
|
|
|
35853
36836
|
const transaction = transactionRoot(projectRoot);
|
|
35854
36837
|
validateOperationRecord(operation, options.schemasDir);
|
|
35855
36838
|
validateOperationIntent(projectRoot, operation);
|
|
35856
|
-
if (
|
|
35857
|
-
|
|
36839
|
+
if (existsSync19(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
|
|
36840
|
+
mkdirSync18(transaction, { mode: 448 });
|
|
35858
36841
|
syncDirectory2(projectRoot);
|
|
35859
36842
|
writeOperation(projectRoot, operation);
|
|
35860
36843
|
try {
|
|
@@ -35879,21 +36862,21 @@ function ensureDestinationParents(projectRoot, path4) {
|
|
|
35879
36862
|
const segments = path4.split("/").slice(0, -1);
|
|
35880
36863
|
let current = boundary;
|
|
35881
36864
|
for (const segment of segments) {
|
|
35882
|
-
current =
|
|
35883
|
-
if (!
|
|
36865
|
+
current = join25(current, segment);
|
|
36866
|
+
if (!existsSync19(current)) mkdirSync18(current, { mode: 448 });
|
|
35884
36867
|
const stat = lstatSync5(current);
|
|
35885
36868
|
if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_destination_parent_invalid", path4, "destination parent must be a regular directory", "path");
|
|
35886
36869
|
}
|
|
35887
36870
|
}
|
|
35888
36871
|
function destinationSha(projectRoot, path4) {
|
|
35889
|
-
const file =
|
|
35890
|
-
if (!
|
|
36872
|
+
const file = join25(projectRoot, ...path4.split("/"));
|
|
36873
|
+
if (!existsSync19(file)) return void 0;
|
|
35891
36874
|
return vnextContentSha256(readRegularBounded(file, path4));
|
|
35892
36875
|
}
|
|
35893
36876
|
function requireConditionalLinkSupport(directory, operationId, path4) {
|
|
35894
|
-
const nonce =
|
|
35895
|
-
const source =
|
|
35896
|
-
const target =
|
|
36877
|
+
const nonce = randomUUID6();
|
|
36878
|
+
const source = join25(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
|
|
36879
|
+
const target = join25(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
|
|
35897
36880
|
try {
|
|
35898
36881
|
writeDurableNew(source, Buffer.from("kxm-link-probe\n", "utf8"), 384);
|
|
35899
36882
|
try {
|
|
@@ -35908,19 +36891,19 @@ function requireConditionalLinkSupport(directory, operationId, path4) {
|
|
|
35908
36891
|
}
|
|
35909
36892
|
}
|
|
35910
36893
|
function atomicInstallTarget(projectRoot, operation, entry) {
|
|
35911
|
-
const destination =
|
|
36894
|
+
const destination = join25(projectRoot, ...entry.path.split("/"));
|
|
35912
36895
|
ensureDestinationParents(projectRoot, entry.path);
|
|
35913
|
-
const directory =
|
|
36896
|
+
const directory = dirname13(destination);
|
|
35914
36897
|
const temporaryPrefix = `.kxm-repair-${operation.operationId}-`;
|
|
35915
36898
|
const pathKey = vnextContentSha256(entry.path).slice("sha256:".length, "sha256:".length + 16);
|
|
35916
|
-
const displaced =
|
|
35917
|
-
if (
|
|
36899
|
+
const displaced = join25(directory, `${temporaryPrefix}${pathKey}.preimage`);
|
|
36900
|
+
if (existsSync19(displaced)) {
|
|
35918
36901
|
const stat = lstatSync5(displaced);
|
|
35919
36902
|
if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_preimage_temporary_invalid", entry.path, "stale displaced preimage is not a regular file", "path");
|
|
35920
36903
|
if (entry.observedSha256 === null || vnextContentSha256(readFileSync18(displaced)) !== entry.observedSha256) {
|
|
35921
36904
|
fail3("repair_preimage_temporary_mismatch", entry.path, "stale displaced preimage does not match the pinned operation");
|
|
35922
36905
|
}
|
|
35923
|
-
if (!
|
|
36906
|
+
if (!existsSync19(destination)) {
|
|
35924
36907
|
try {
|
|
35925
36908
|
linkSync(displaced, destination);
|
|
35926
36909
|
} catch {
|
|
@@ -35935,7 +36918,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
35935
36918
|
const owned = candidate.name.startsWith(temporaryPrefix) && (candidate.name.endsWith(".tmp") || candidate.name.endsWith(".link-source") || candidate.name.endsWith(".link-target"));
|
|
35936
36919
|
if (!owned) continue;
|
|
35937
36920
|
if (!candidate.isFile()) fail3("repair_temporary_invalid", entry.path, "stale operation temporary is not a regular file", "path");
|
|
35938
|
-
rmSync5(
|
|
36921
|
+
rmSync5(join25(directory, candidate.name), { force: true });
|
|
35939
36922
|
}
|
|
35940
36923
|
const current = destinationSha(projectRoot, entry.path);
|
|
35941
36924
|
if (current === entry.targetSha256) return;
|
|
@@ -35945,15 +36928,15 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
35945
36928
|
requireConditionalLinkSupport(directory, operation.operationId, entry.path);
|
|
35946
36929
|
const bytes = readTarget(projectRoot, entry.path);
|
|
35947
36930
|
if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
|
|
35948
|
-
const temporary =
|
|
35949
|
-
const mode =
|
|
36931
|
+
const temporary = join25(directory, `${temporaryPrefix}${randomUUID6()}.tmp`);
|
|
36932
|
+
const mode = existsSync19(destination) ? lstatSync5(destination).mode & 511 : 420;
|
|
35950
36933
|
try {
|
|
35951
36934
|
writeDurableNew(temporary, bytes, mode);
|
|
35952
36935
|
if (entry.observedSha256 !== null) {
|
|
35953
36936
|
renameSync6(destination, displaced);
|
|
35954
36937
|
syncDirectory2(directory);
|
|
35955
36938
|
if (vnextContentSha256(readFileSync18(displaced)) !== entry.observedSha256) {
|
|
35956
|
-
if (!
|
|
36939
|
+
if (!existsSync19(destination)) {
|
|
35957
36940
|
try {
|
|
35958
36941
|
linkSync(displaced, destination);
|
|
35959
36942
|
} catch {
|
|
@@ -35969,7 +36952,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
35969
36952
|
try {
|
|
35970
36953
|
linkSync(temporary, destination);
|
|
35971
36954
|
} catch {
|
|
35972
|
-
if (!
|
|
36955
|
+
if (!existsSync19(destination) && existsSync19(displaced)) {
|
|
35973
36956
|
try {
|
|
35974
36957
|
linkSync(displaced, destination);
|
|
35975
36958
|
} catch {
|
|
@@ -35991,12 +36974,12 @@ function atomicInstallTarget(projectRoot, operation, entry) {
|
|
|
35991
36974
|
if (destinationSha(projectRoot, entry.path) !== entry.targetSha256) fail3("repair_install_verification_failed", entry.path, "installed target hash does not match the pinned operation");
|
|
35992
36975
|
}
|
|
35993
36976
|
function installedCreateMatches(projectRoot, operation) {
|
|
35994
|
-
const configRoot =
|
|
35995
|
-
if (!
|
|
36977
|
+
const configRoot = join25(projectRoot, ".kxm");
|
|
36978
|
+
if (!existsSync19(configRoot)) return false;
|
|
35996
36979
|
const actual = [];
|
|
35997
36980
|
const visit2 = (directory) => {
|
|
35998
36981
|
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
35999
|
-
const absolute =
|
|
36982
|
+
const absolute = join25(directory, entry.name);
|
|
36000
36983
|
if (entry.isSymbolicLink()) fail3("create_resume_link", relativeConfigPath(projectRoot, absolute), "created configuration must not contain links", "path");
|
|
36001
36984
|
if (entry.isDirectory()) visit2(absolute);
|
|
36002
36985
|
else if (entry.isFile()) actual.push(relativeConfigPath(projectRoot, absolute));
|
|
@@ -36012,12 +36995,12 @@ function relativeConfigPath(projectRoot, file) {
|
|
|
36012
36995
|
return relative5(resolve12(projectRoot), file).replaceAll("\\", "/");
|
|
36013
36996
|
}
|
|
36014
36997
|
function syncTreeDirectories(root) {
|
|
36015
|
-
if (!
|
|
36998
|
+
if (!existsSync19(root)) return;
|
|
36016
36999
|
const directories = [];
|
|
36017
37000
|
const visit2 = (directory) => {
|
|
36018
37001
|
directories.push(directory);
|
|
36019
37002
|
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
36020
|
-
if (entry.isDirectory()) visit2(
|
|
37003
|
+
if (entry.isDirectory()) visit2(join25(directory, entry.name));
|
|
36021
37004
|
}
|
|
36022
37005
|
};
|
|
36023
37006
|
visit2(root);
|
|
@@ -36031,7 +37014,7 @@ function rebuildCreateShadow(projectRoot, operation, options) {
|
|
|
36031
37014
|
if (stat.isSymbolicLink() || !stat.isDirectory() || !installedCreateMatches(shadow, operation)) {
|
|
36032
37015
|
fail3("create_shadow_invalid", TRANSACTION_NAME, "reconstructed create shadow does not exactly match pinned target files", "path");
|
|
36033
37016
|
}
|
|
36034
|
-
syncTreeDirectories(
|
|
37017
|
+
syncTreeDirectories(join25(shadow, ".kxm"));
|
|
36035
37018
|
}
|
|
36036
37019
|
function applyOperation(projectRoot, original, options, resumed) {
|
|
36037
37020
|
assertNoRegisteredGates(options);
|
|
@@ -36041,10 +37024,10 @@ function applyOperation(projectRoot, original, options, resumed) {
|
|
|
36041
37024
|
verifyBackups(projectRoot, operation);
|
|
36042
37025
|
operation = updatePhase(projectRoot, operation, "applying");
|
|
36043
37026
|
if (operation.kind === "create") {
|
|
36044
|
-
const destination =
|
|
36045
|
-
if (!
|
|
37027
|
+
const destination = join25(projectRoot, ".kxm");
|
|
37028
|
+
if (!existsSync19(destination)) {
|
|
36046
37029
|
rebuildCreateShadow(projectRoot, operation, effectiveOptions);
|
|
36047
|
-
renameSync6(
|
|
37030
|
+
renameSync6(join25(shadowRoot(projectRoot), ".kxm"), destination);
|
|
36048
37031
|
syncDirectory2(projectRoot);
|
|
36049
37032
|
} else if (!installedCreateMatches(projectRoot, operation)) {
|
|
36050
37033
|
fail3("create_resume_conflict", ".kxm", "existing project configuration does not match the pinned create operation");
|
|
@@ -36071,7 +37054,7 @@ function applyOperation(projectRoot, original, options, resumed) {
|
|
|
36071
37054
|
operation = updatePhase(projectRoot, operation, "verified");
|
|
36072
37055
|
if (options.testFaultAt === "verified") throw new Error("injected init fault after verification");
|
|
36073
37056
|
const files = operation.files.map((entry) => entry.path);
|
|
36074
|
-
syncTreeDirectories(
|
|
37057
|
+
syncTreeDirectories(join25(projectRoot, ".kxm"));
|
|
36075
37058
|
return { kind: operation.kind, resumed, bundle, files };
|
|
36076
37059
|
}
|
|
36077
37060
|
function prepareAndApplyVnextCreate(projectRoot, rendered, options = {}) {
|
|
@@ -36186,7 +37169,7 @@ function finishPreparingOperation(projectRoot, operation, options) {
|
|
|
36186
37169
|
const rendered = rendererForOperation(operation);
|
|
36187
37170
|
if (!rendered) fail3("init_transaction_renderer_unavailable", TRANSACTION_NAME, "this binary cannot reproduce the pinned pre-application target");
|
|
36188
37171
|
for (const directory of ["targets", "backups", "shadow"]) {
|
|
36189
|
-
rmSync5(
|
|
37172
|
+
rmSync5(join25(transactionRoot(projectRoot), directory), { recursive: true, force: true });
|
|
36190
37173
|
}
|
|
36191
37174
|
for (const entry of operation.files) {
|
|
36192
37175
|
const bytes = rendered.files.get(entry.path);
|
|
@@ -36205,7 +37188,7 @@ function resumeVnextInitTransaction(projectRoot, options = {}) {
|
|
|
36205
37188
|
let operation = readOperation(projectRoot, options.schemasDir);
|
|
36206
37189
|
if (!operation) {
|
|
36207
37190
|
const root = transactionRoot(projectRoot);
|
|
36208
|
-
if (
|
|
37191
|
+
if (existsSync19(root)) {
|
|
36209
37192
|
rmSync5(root, { recursive: true, force: true });
|
|
36210
37193
|
syncDirectory2(projectRoot);
|
|
36211
37194
|
}
|
|
@@ -36221,7 +37204,7 @@ function commitVnextInitTransaction(projectRoot, schemasDir) {
|
|
|
36221
37204
|
fail3("init_transaction_not_verified", TRANSACTION_NAME, "initialization transaction cannot commit before installed state is verified");
|
|
36222
37205
|
}
|
|
36223
37206
|
const transaction = transactionRoot(projectRoot);
|
|
36224
|
-
const retired =
|
|
37207
|
+
const retired = join25(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID6()}`);
|
|
36225
37208
|
renameSync6(transaction, retired);
|
|
36226
37209
|
syncDirectory2(projectRoot);
|
|
36227
37210
|
rmSync5(retired, { recursive: true, force: true });
|
|
@@ -36249,7 +37232,7 @@ function normalizedProjectName(root, requested) {
|
|
|
36249
37232
|
return name;
|
|
36250
37233
|
}
|
|
36251
37234
|
function generatedProjectId(requested) {
|
|
36252
|
-
const id = requested?.trim() || `prj_${
|
|
37235
|
+
const id = requested?.trim() || `prj_${randomUUID7().replaceAll("-", "")}`;
|
|
36253
37236
|
if (!PROJECT_ID.test(id) || id.length > 144) {
|
|
36254
37237
|
throw new VnextConfigError([initIssue("project_id_invalid", ".kxm/project.yaml", "project ID must satisfy the kxm.project.v1 opaque ID grammar and use the prj_ prefix")]);
|
|
36255
37238
|
}
|
|
@@ -36330,7 +37313,7 @@ function validateBindingIdentities(bundle, persisted, explicit) {
|
|
|
36330
37313
|
}
|
|
36331
37314
|
function initializeVnextProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
36332
37315
|
const storeOptions = bindingStoreOptions(options);
|
|
36333
|
-
const persisted =
|
|
37316
|
+
const persisted = existsSync20(join26(gitRoot, ".kxm", "project.yaml")) ? readVnextLocalBindings(gitRoot, storeOptions) : void 0;
|
|
36334
37317
|
const repositoryBindings = Object.fromEntries([
|
|
36335
37318
|
...Object.entries(persisted?.repositories ?? {}),
|
|
36336
37319
|
...Object.entries(options.repositoryBindings ?? {})
|
|
@@ -36519,7 +37502,7 @@ function initializeVnextProjectAtGitRoot(start, gitRoot, options, mutationLock)
|
|
|
36519
37502
|
const files = [...rendered.files.keys()];
|
|
36520
37503
|
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
|
|
36521
37504
|
if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
|
|
36522
|
-
if (
|
|
37505
|
+
if (existsSync20(join26(gitRoot, ".kxm"))) {
|
|
36523
37506
|
throw new VnextConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
|
|
36524
37507
|
}
|
|
36525
37508
|
const created = prepareAndApplyVnextCreate(gitRoot, rendered, transactionOptions);
|
|
@@ -36549,10 +37532,10 @@ function initializeVnextProject(start = process.cwd(), options = {}) {
|
|
|
36549
37532
|
}
|
|
36550
37533
|
|
|
36551
37534
|
// plugins/kxm/src/vnext-migrate.ts
|
|
36552
|
-
var
|
|
36553
|
-
import { createHash as
|
|
36554
|
-
import { chmodSync as chmodSync4, closeSync as closeSync3, existsSync as
|
|
36555
|
-
import { join as
|
|
37535
|
+
var import_yaml10 = __toESM(require_dist(), 1);
|
|
37536
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
37537
|
+
import { chmodSync as chmodSync4, closeSync as closeSync3, existsSync as existsSync21, fsyncSync as fsyncSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync19, openSync as openSync3, readFileSync as readFileSync19, renameSync as renameSync7, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
|
|
37538
|
+
import { join as join27 } from "node:path";
|
|
36556
37539
|
var MAX_LEGACY_JSON_BYTES = 1024 * 1024;
|
|
36557
37540
|
var MAX_LEGACY_JSON_DEPTH = 32;
|
|
36558
37541
|
var MAX_LEGACY_JSON_NODES = 32768;
|
|
@@ -36713,7 +37696,7 @@ function detectDuplicateKeys(text, label) {
|
|
|
36713
37696
|
if (index !== length) migrateFail("invalid_json", label, `trailing content at byte ${index}`);
|
|
36714
37697
|
}
|
|
36715
37698
|
function sha256Of(input) {
|
|
36716
|
-
return `sha256:${
|
|
37699
|
+
return `sha256:${createHash11("sha256").update(input).digest("hex")}`;
|
|
36717
37700
|
}
|
|
36718
37701
|
function readVnextLegacySources(root) {
|
|
36719
37702
|
const files = legacyConfigFilesAt(root);
|
|
@@ -36721,7 +37704,7 @@ function readVnextLegacySources(root) {
|
|
|
36721
37704
|
throw new VnextConfigError([migrateIssue("discovery", "legacy_source_limit", ".kxm/config", `legacy configuration exceeds ${MAX_LEGACY_SOURCES} files`)]);
|
|
36722
37705
|
}
|
|
36723
37706
|
return files.map((path4) => {
|
|
36724
|
-
const absolute =
|
|
37707
|
+
const absolute = join27(root, ...path4.split("/"));
|
|
36725
37708
|
const stat = lstatSync6(absolute);
|
|
36726
37709
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
36727
37710
|
throw new VnextConfigError([migrateIssue("path", "legacy_source_invalid", path4, "legacy configuration must be a regular file, not a link")]);
|
|
@@ -36785,7 +37768,7 @@ function normalizeIdentifier(raw) {
|
|
|
36785
37768
|
function pointerEscape2(segment) {
|
|
36786
37769
|
const escaped = segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
36787
37770
|
if (escaped.length <= 200) return escaped;
|
|
36788
|
-
const digest =
|
|
37771
|
+
const digest = createHash11("sha256").update(segment, "utf8").digest("hex").slice(0, 8);
|
|
36789
37772
|
return `${escaped.slice(0, 180)}~digest-${digest}`;
|
|
36790
37773
|
}
|
|
36791
37774
|
function elide(raw, max = 120) {
|
|
@@ -36794,7 +37777,7 @@ function elide(raw, max = 120) {
|
|
|
36794
37777
|
function keyFragment(raw) {
|
|
36795
37778
|
const full = normalizeIdentifier(raw) || "unnamed";
|
|
36796
37779
|
const normalized = full.length > 96 ? `${full.slice(0, 96)}-${full.length}` : full;
|
|
36797
|
-
const digest =
|
|
37780
|
+
const digest = createHash11("sha256").update(raw, "utf8").digest("hex").slice(0, 8);
|
|
36798
37781
|
return `${normalized}-${digest}`;
|
|
36799
37782
|
}
|
|
36800
37783
|
function recordUnmapped(plan, sourcePath, pointer, value, sensitive) {
|
|
@@ -37201,7 +38184,7 @@ function convertWorkflowFile(plan, source, decisions, agentIds, agentRawNames, p
|
|
|
37201
38184
|
const rawId = asString(definition.id) ?? `${fileStem}-${definitionIndex}`;
|
|
37202
38185
|
const workflowIdentity = mapIdentity(plan, source.path, rawId, "workflow", workflowNames, decisions, basePointer || "/", definition);
|
|
37203
38186
|
if (workflowIdentity.status === "dropped") continue;
|
|
37204
|
-
const workflowId = workflowIdentity.status === "adopted" ? workflowIdentity.id : `preview-${(normalizeIdentifier(rawId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${
|
|
38187
|
+
const workflowId = workflowIdentity.status === "adopted" ? workflowIdentity.id : `preview-${(normalizeIdentifier(rawId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash11("sha256").update(`${rawId}#${source.path}#${definitionIndex}`, "utf8").digest("hex").slice(0, 8)}`;
|
|
37205
38188
|
const workflowKey = keyFragment(rawId);
|
|
37206
38189
|
for (const field of ["source", "project", "event", "filter", "delivery", "ttlMs", "promptTemplate"]) {
|
|
37207
38190
|
if (definition[field] !== void 0) recordUnmapped(plan, source.path, `${basePointer}/${field}`, definition[field], false);
|
|
@@ -37311,7 +38294,7 @@ function convertWorkflowFile(plan, source, decisions, agentIds, agentRawNames, p
|
|
|
37311
38294
|
droppedStageIndexes.add(stageIndex);
|
|
37312
38295
|
continue;
|
|
37313
38296
|
}
|
|
37314
|
-
const stepId = stepIdentity.status === "adopted" ? stepIdentity.id : `preview-${(normalizeIdentifier(rawStageId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${
|
|
38297
|
+
const stepId = stepIdentity.status === "adopted" ? stepIdentity.id : `preview-${(normalizeIdentifier(rawStageId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash11("sha256").update(`${rawStageId}#${stageIndex}`, "utf8").digest("hex").slice(0, 8)}`;
|
|
37315
38298
|
const stepKey = keyFragment(rawStageId);
|
|
37316
38299
|
stageKeyByRaw.set(rawStageId, stepKey);
|
|
37317
38300
|
if (!asString(stage.instructions)) {
|
|
@@ -37770,7 +38753,7 @@ function planVnextMigration(projectRoot, options = {}) {
|
|
|
37770
38753
|
sourceDigest: legacySourceDigest(sources),
|
|
37771
38754
|
sources: sources.map((source) => ({ path: source.path, sha256: source.sha256, bytes: source.bytes })),
|
|
37772
38755
|
resources: [...resources.keys()].sort(compareCodeUnits5).map((path4) => {
|
|
37773
|
-
const bytes = Buffer.from((0,
|
|
38756
|
+
const bytes = Buffer.from((0, import_yaml10.stringify)(resources.get(path4), { lineWidth: 0 }), "utf8");
|
|
37774
38757
|
return { path: path4, sha256: sha256Of(bytes), bytes: bytes.byteLength };
|
|
37775
38758
|
}),
|
|
37776
38759
|
ambiguities: [...plan.ambiguities].sort((left, right) => compareCodeUnits5(left.key, right.key)).map((ambiguity) => ({
|
|
@@ -37799,7 +38782,7 @@ function planVnextMigration(projectRoot, options = {}) {
|
|
|
37799
38782
|
};
|
|
37800
38783
|
}
|
|
37801
38784
|
function readVnextMigrationDecisions(path4, schemasDir) {
|
|
37802
|
-
if (!
|
|
38785
|
+
if (!existsSync21(path4)) {
|
|
37803
38786
|
throw new VnextConfigError([migrateIssue("discovery", "decisions_missing", path4, "migration decisions file does not exist")]);
|
|
37804
38787
|
}
|
|
37805
38788
|
const registry = new VnextSchemaRegistry(schemasDir);
|
|
@@ -37826,13 +38809,13 @@ function syncDirectory3(path4) {
|
|
|
37826
38809
|
}
|
|
37827
38810
|
}
|
|
37828
38811
|
function writeDurable2(path4, content) {
|
|
37829
|
-
const temp = `${path4}.kxm-migration-tmp-${process.pid}-${
|
|
38812
|
+
const temp = `${path4}.kxm-migration-tmp-${process.pid}-${createHash11("sha256").update(`${path4}${Date.now()}${Math.random()}`, "utf8").digest("hex").slice(0, 12)}`;
|
|
37830
38813
|
let created = false;
|
|
37831
38814
|
try {
|
|
37832
38815
|
const descriptor = openSync3(temp, "wx", 384);
|
|
37833
38816
|
created = true;
|
|
37834
38817
|
try {
|
|
37835
|
-
|
|
38818
|
+
writeFileSync17(descriptor, content);
|
|
37836
38819
|
fsyncSync3(descriptor);
|
|
37837
38820
|
} finally {
|
|
37838
38821
|
closeSync3(descriptor);
|
|
@@ -37842,9 +38825,9 @@ function writeDurable2(path4, content) {
|
|
|
37842
38825
|
chmodSync4(path4, 384);
|
|
37843
38826
|
} catch {
|
|
37844
38827
|
}
|
|
37845
|
-
syncDirectory3(
|
|
38828
|
+
syncDirectory3(join27(path4, ".."));
|
|
37846
38829
|
} finally {
|
|
37847
|
-
if (created &&
|
|
38830
|
+
if (created && existsSync21(temp)) rmSync6(temp, { force: true });
|
|
37848
38831
|
}
|
|
37849
38832
|
}
|
|
37850
38833
|
function applyVnextMigration(projectRoot, options = {}) {
|
|
@@ -37854,8 +38837,8 @@ function applyVnextMigration(projectRoot, options = {}) {
|
|
|
37854
38837
|
...options.schemasDir === void 0 ? {} : { schemasDir: options.schemasDir }
|
|
37855
38838
|
};
|
|
37856
38839
|
const execute = (lock) => {
|
|
37857
|
-
const receiptAbsolute =
|
|
37858
|
-
if (
|
|
38840
|
+
const receiptAbsolute = join27(root, ...VNEXT_MIGRATION_RECEIPT_PATH.split("/"));
|
|
38841
|
+
if (existsSync21(receiptAbsolute)) {
|
|
37859
38842
|
const stat = lstatSync6(receiptAbsolute);
|
|
37860
38843
|
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
37861
38844
|
throw new VnextConfigError([migrateIssue("path", "migration_receipt_invalid", VNEXT_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")]);
|
|
@@ -37907,8 +38890,8 @@ function applyVnextMigration(projectRoot, options = {}) {
|
|
|
37907
38890
|
if (first.ambiguities.length > 0) {
|
|
37908
38891
|
return { action: "planned", projectRoot: root, files: [], plan: first.plan };
|
|
37909
38892
|
}
|
|
37910
|
-
const files = [...first.resources.entries()].sort(([left], [right]) => compareCodeUnits5(left, right)).map(([path4, value]) => ({ path: path4, bytes: Buffer.from((0,
|
|
37911
|
-
const collisions = files.filter((file) =>
|
|
38893
|
+
const files = [...first.resources.entries()].sort(([left], [right]) => compareCodeUnits5(left, right)).map(([path4, value]) => ({ path: path4, bytes: Buffer.from((0, import_yaml10.stringify)(value, { lineWidth: 0 }), "utf8") }));
|
|
38894
|
+
const collisions = files.filter((file) => existsSync21(join27(root, ...file.path.split("/"))));
|
|
37912
38895
|
if (collisions.length > 0) {
|
|
37913
38896
|
throw new VnextConfigError(collisions.map((file) => migrateIssue("semantic", "migration_target_exists", file.path, "target resource already exists; refusing to overwrite")));
|
|
37914
38897
|
}
|
|
@@ -37917,8 +38900,8 @@ function applyVnextMigration(projectRoot, options = {}) {
|
|
|
37917
38900
|
const segments = file.path.split("/");
|
|
37918
38901
|
let current = root;
|
|
37919
38902
|
for (const segment of segments.slice(0, -1)) {
|
|
37920
|
-
current =
|
|
37921
|
-
if (!
|
|
38903
|
+
current = join27(current, segment);
|
|
38904
|
+
if (!existsSync21(current)) continue;
|
|
37922
38905
|
const stat = lstatSync6(current);
|
|
37923
38906
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
37924
38907
|
parentIssues.push(migrateIssue("path", "migration_target_parent_invalid", file.path, `migration target parent ${segment} must be a regular directory, not a link`));
|
|
@@ -37934,8 +38917,8 @@ function applyVnextMigration(projectRoot, options = {}) {
|
|
|
37934
38917
|
const written = [];
|
|
37935
38918
|
try {
|
|
37936
38919
|
for (const file of files) {
|
|
37937
|
-
const absolute =
|
|
37938
|
-
|
|
38920
|
+
const absolute = join27(root, ...file.path.split("/"));
|
|
38921
|
+
mkdirSync19(join27(absolute, ".."), { recursive: true });
|
|
37939
38922
|
written.push(absolute);
|
|
37940
38923
|
writeDurable2(absolute, file.bytes);
|
|
37941
38924
|
}
|
|
@@ -37956,7 +38939,7 @@ ${bundle.configRevision}`).slice(7, 39)}`,
|
|
|
37956
38939
|
const receiptIssues = registry.validateMigrationReceipt(receipt, VNEXT_MIGRATION_RECEIPT_PATH);
|
|
37957
38940
|
if (receiptIssues.length > 0) throw new VnextConfigError(receiptIssues);
|
|
37958
38941
|
written.push(receiptAbsolute);
|
|
37959
|
-
writeDurable2(receiptAbsolute, Buffer.from((0,
|
|
38942
|
+
writeDurable2(receiptAbsolute, Buffer.from((0, import_yaml10.stringify)(receipt, { lineWidth: 0 }), "utf8"));
|
|
37960
38943
|
const verified = loadVnextProject(root, options);
|
|
37961
38944
|
return {
|
|
37962
38945
|
action: "applied",
|
|
@@ -37981,8 +38964,8 @@ ${bundle.configRevision}`).slice(7, 39)}`,
|
|
|
37981
38964
|
}
|
|
37982
38965
|
function verifyVnextMigration(projectRoot, options = {}) {
|
|
37983
38966
|
const root = migrationGitRoot(projectRoot);
|
|
37984
|
-
const receiptAbsolute =
|
|
37985
|
-
if (!
|
|
38967
|
+
const receiptAbsolute = join27(root, ...VNEXT_MIGRATION_RECEIPT_PATH.split("/"));
|
|
38968
|
+
if (!existsSync21(receiptAbsolute)) {
|
|
37986
38969
|
return {
|
|
37987
38970
|
ok: false,
|
|
37988
38971
|
projectRoot: root,
|
|
@@ -38030,23 +39013,23 @@ function verifyVnextMigration(projectRoot, options = {}) {
|
|
|
38030
39013
|
|
|
38031
39014
|
// plugins/kxm/src/vnext-runtime-supervisor.ts
|
|
38032
39015
|
import { spawn } from "node:child_process";
|
|
38033
|
-
import { createHash as
|
|
38034
|
-
import { chmodSync as chmodSync5, existsSync as
|
|
38035
|
-
import { dirname as
|
|
39016
|
+
import { createHash as createHash13, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
39017
|
+
import { chmodSync as chmodSync5, existsSync as existsSync23, lstatSync as lstatSync8, mkdirSync as mkdirSync21, readFileSync as readFileSync21, renameSync as renameSync8, rmSync as rmSync7, writeFileSync as writeFileSync19 } from "node:fs";
|
|
39018
|
+
import { dirname as dirname15, isAbsolute as isAbsolute7, join as join29, resolve as resolve15 } from "node:path";
|
|
38036
39019
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
38037
39020
|
|
|
38038
39021
|
// plugins/kxm/src/vnext-runtime-store.ts
|
|
38039
|
-
import { createHash as
|
|
38040
|
-
import { existsSync as
|
|
38041
|
-
import { dirname as
|
|
39022
|
+
import { createHash as createHash12, randomUUID as randomUUID8 } from "node:crypto";
|
|
39023
|
+
import { existsSync as existsSync22, lstatSync as lstatSync7, mkdirSync as mkdirSync20, readFileSync as readFileSync20, realpathSync as realpathSync4, writeFileSync as writeFileSync18 } from "node:fs";
|
|
39024
|
+
import { dirname as dirname14, join as join28, resolve as resolve14 } from "node:path";
|
|
38042
39025
|
function vnextRuntimePaths(options = {}) {
|
|
38043
39026
|
const stateRoot = options.stateRoot ? resolve14(options.stateRoot) : vnextUserStateRoot({ ...options.env ? { env: options.env } : {}, ...options.homeDir ? { homeDir: options.homeDir } : {} });
|
|
38044
|
-
const runtimeDir =
|
|
39027
|
+
const runtimeDir = join28(stateRoot, "runtime");
|
|
38045
39028
|
return {
|
|
38046
39029
|
stateRoot,
|
|
38047
39030
|
runtimeDir,
|
|
38048
|
-
registryDb:
|
|
38049
|
-
projectsDir:
|
|
39031
|
+
registryDb: join28(runtimeDir, "registry.db"),
|
|
39032
|
+
projectsDir: join28(runtimeDir, "projects")
|
|
38050
39033
|
};
|
|
38051
39034
|
}
|
|
38052
39035
|
function runtimeIssue(phase, code, file, message) {
|
|
@@ -38063,7 +39046,7 @@ function projectRuntimeKey(projectRoot) {
|
|
|
38063
39046
|
canonical2 = resolve14(projectRoot);
|
|
38064
39047
|
}
|
|
38065
39048
|
const folded = process.platform === "win32" ? canonical2.toLocaleLowerCase("en-US") : canonical2;
|
|
38066
|
-
return
|
|
39049
|
+
return createHash12("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
38067
39050
|
}
|
|
38068
39051
|
var VNEXT_REGISTRY_SCHEMA_VERSION = 1;
|
|
38069
39052
|
var REGISTRY_TABLES = {
|
|
@@ -38405,7 +39388,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
38405
39388
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
38406
39389
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
38407
39390
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
38408
|
-
const
|
|
39391
|
+
const join37 = compileJoin(step, id, sink);
|
|
38409
39392
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
38410
39393
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
38411
39394
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits6);
|
|
@@ -38433,7 +39416,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
38433
39416
|
transitions: orderedTransitions,
|
|
38434
39417
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
38435
39418
|
assignments,
|
|
38436
|
-
join:
|
|
39419
|
+
join: join37
|
|
38437
39420
|
};
|
|
38438
39421
|
if (kind === "agent" || kind === "moa") {
|
|
38439
39422
|
if (!agent) return void 0;
|
|
@@ -38483,15 +39466,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
38483
39466
|
};
|
|
38484
39467
|
}
|
|
38485
39468
|
function compileJoin(step, stepId, sink) {
|
|
38486
|
-
const
|
|
38487
|
-
if (!
|
|
38488
|
-
const declared = stringValue3(
|
|
39469
|
+
const join37 = objectValue2(step.join);
|
|
39470
|
+
if (!join37) return { strategy: "all" };
|
|
39471
|
+
const declared = stringValue3(join37.strategy);
|
|
38489
39472
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
38490
|
-
const minimumPassed = compileOptionalCount(
|
|
39473
|
+
const minimumPassed = compileOptionalCount(join37.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
38491
39474
|
const compiled = {
|
|
38492
39475
|
strategy,
|
|
38493
39476
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
38494
|
-
...typeof
|
|
39477
|
+
...typeof join37.cancelRemaining === "boolean" ? { cancelRemaining: join37.cancelRemaining } : {}
|
|
38495
39478
|
};
|
|
38496
39479
|
return compiled;
|
|
38497
39480
|
}
|
|
@@ -38768,12 +39751,27 @@ var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
|
38768
39751
|
var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
38769
39752
|
|
|
38770
39753
|
// plugins/kxm/src/vnext-engine.ts
|
|
38771
|
-
var
|
|
39754
|
+
var import_yaml11 = __toESM(require_dist(), 1);
|
|
39755
|
+
|
|
39756
|
+
// plugins/kxm/src/safety-integrity.ts
|
|
39757
|
+
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
39758
|
+
/^\s*rm\s+.*-[a-zA-Z]*r[a-zA-Z]*f/i,
|
|
39759
|
+
/^\s*rm\s+.*-[a-zA-Z]*f[a-zA-Z]*r/i,
|
|
39760
|
+
/^\s*rm\s+.*(-[a-zA-Z]*r[a-zA-Z]*\s+.*-[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*\s+.*-[a-zA-Z]*r[a-zA-Z]*)/i,
|
|
39761
|
+
/^\s*git\s+reset\s+--hard/i,
|
|
39762
|
+
/^\s*git\s+clean\s+-[a-zA-Z]*f/i,
|
|
39763
|
+
/^\s*git\s+checkout\s+--\s+/i,
|
|
39764
|
+
/^\s*git\s+restore\s+(\.|\*|--staged\s+(\.|\*))/i
|
|
39765
|
+
]);
|
|
39766
|
+
var INSECURE_SSH_HOST_KEY_PATTERNS = Object.freeze([
|
|
39767
|
+
/StrictHostKeyChecking=(accept-new|no|off)/i,
|
|
39768
|
+
/UserKnownHostsFile=\/dev\/null/i
|
|
39769
|
+
]);
|
|
38772
39770
|
|
|
38773
39771
|
// plugins/kxm/src/vnext-runtime-supervisor.ts
|
|
38774
39772
|
var repoRoot = resolve15(fileURLToPath3(new URL("../../../", import.meta.url)));
|
|
38775
39773
|
function vnextSupervisorTokenFile(paths) {
|
|
38776
|
-
return
|
|
39774
|
+
return join29(paths.runtimeDir, "supervisor.token");
|
|
38777
39775
|
}
|
|
38778
39776
|
function readVnextSupervisorToken(paths) {
|
|
38779
39777
|
const file = vnextSupervisorTokenFile(paths);
|
|
@@ -38796,11 +39794,11 @@ function processAlive(pid) {
|
|
|
38796
39794
|
var HEARTBEAT_STALE_MS = 15e3;
|
|
38797
39795
|
var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
|
|
38798
39796
|
function supervisorErrorFile(paths) {
|
|
38799
|
-
return
|
|
39797
|
+
return join29(paths.runtimeDir, "supervisor.error");
|
|
38800
39798
|
}
|
|
38801
39799
|
function clearSupervisorError(paths) {
|
|
38802
39800
|
const file = supervisorErrorFile(paths);
|
|
38803
|
-
if (
|
|
39801
|
+
if (existsSync23(file)) rmSync7(file, { force: true });
|
|
38804
39802
|
}
|
|
38805
39803
|
function readRecentSupervisorError(paths) {
|
|
38806
39804
|
const file = supervisorErrorFile(paths);
|
|
@@ -38815,7 +39813,7 @@ function readRecentSupervisorError(paths) {
|
|
|
38815
39813
|
}
|
|
38816
39814
|
}
|
|
38817
39815
|
function vnextSupervisorStatus(paths) {
|
|
38818
|
-
if (!
|
|
39816
|
+
if (!existsSync23(paths.registryDb)) return { running: false };
|
|
38819
39817
|
const registry = new VnextRuntimeRegistry(paths.registryDb);
|
|
38820
39818
|
try {
|
|
38821
39819
|
const record = registry.supervisor();
|
|
@@ -38876,7 +39874,7 @@ async function ensureVnextSupervisor(options = {}) {
|
|
|
38876
39874
|
throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
|
|
38877
39875
|
}
|
|
38878
39876
|
clearSupervisorError(paths);
|
|
38879
|
-
const scriptPath =
|
|
39877
|
+
const scriptPath = join29(repoRoot, "scripts", "kxm-runtime-supervisor.mjs");
|
|
38880
39878
|
const spawnImpl = options.spawnImpl ?? ((script, env) => {
|
|
38881
39879
|
const child = spawn(process.execPath, [script], {
|
|
38882
39880
|
detached: true,
|
|
@@ -38928,7 +39926,7 @@ async function vnextRuntimeRequest(handle, method, path4, body) {
|
|
|
38928
39926
|
}
|
|
38929
39927
|
|
|
38930
39928
|
// plugins/kxm/src/client.ts
|
|
38931
|
-
import { createHash as
|
|
39929
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
38932
39930
|
var MeshWaitError = class extends Error {
|
|
38933
39931
|
waitStatus;
|
|
38934
39932
|
constructor(waitStatus, messageId) {
|
|
@@ -38965,7 +39963,7 @@ function fanoutIdempotencyKey(prefix, target, correlationId, workflowContext) {
|
|
|
38965
39963
|
correlationId: correlationId ?? null,
|
|
38966
39964
|
target: target.toLowerCase()
|
|
38967
39965
|
});
|
|
38968
|
-
return `fanout:${
|
|
39966
|
+
return `fanout:${createHash14("sha256").update(scope).digest("hex")}`;
|
|
38969
39967
|
}
|
|
38970
39968
|
var HubHttpError = class extends Error {
|
|
38971
39969
|
statusCode;
|
|
@@ -39183,7 +40181,7 @@ var HubClient = class {
|
|
|
39183
40181
|
if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
|
|
39184
40182
|
const message = await this.getMessage(messageId);
|
|
39185
40183
|
if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
|
|
39186
|
-
await new Promise((
|
|
40184
|
+
await new Promise((resolve23, reject) => {
|
|
39187
40185
|
const onAbort = () => {
|
|
39188
40186
|
signal?.removeEventListener("abort", onAbort);
|
|
39189
40187
|
clearTimeout(timer);
|
|
@@ -39191,7 +40189,7 @@ var HubClient = class {
|
|
|
39191
40189
|
};
|
|
39192
40190
|
const timer = setTimeout(() => {
|
|
39193
40191
|
signal?.removeEventListener("abort", onAbort);
|
|
39194
|
-
|
|
40192
|
+
resolve23();
|
|
39195
40193
|
}, Math.min(500, Math.max(1, deadline - Date.now())));
|
|
39196
40194
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
39197
40195
|
if (signal?.aborted) onAbort();
|
|
@@ -39246,7 +40244,7 @@ var HubClient = class {
|
|
|
39246
40244
|
} catch (error) {
|
|
39247
40245
|
if (this.stopped || error instanceof Error && error.name === "AbortError") return;
|
|
39248
40246
|
}
|
|
39249
|
-
if (!this.stopped) await new Promise((
|
|
40247
|
+
if (!this.stopped) await new Promise((resolve23) => setTimeout(resolve23, this.options.reconnectMs));
|
|
39250
40248
|
}
|
|
39251
40249
|
}
|
|
39252
40250
|
headers(includeIdentity = true) {
|
|
@@ -39328,15 +40326,15 @@ var HubClient = class {
|
|
|
39328
40326
|
};
|
|
39329
40327
|
|
|
39330
40328
|
// plugins/kxm/src/commands.ts
|
|
39331
|
-
import { createHash as
|
|
39332
|
-
import { chmodSync as chmodSync6, existsSync as
|
|
40329
|
+
import { createHash as createHash15, randomUUID as randomUUID9, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
40330
|
+
import { chmodSync as chmodSync6, existsSync as existsSync24, mkdirSync as mkdirSync22, readFileSync as readFileSync22, unlinkSync as unlinkSync2, writeFileSync as writeFileSync20 } from "node:fs";
|
|
39333
40331
|
import { homedir as homedir4 } from "node:os";
|
|
39334
|
-
import { dirname as
|
|
40332
|
+
import { dirname as dirname16, join as join30, resolve as resolve16 } from "node:path";
|
|
39335
40333
|
function requiredString(value, name) {
|
|
39336
40334
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
39337
40335
|
return value.trim();
|
|
39338
40336
|
}
|
|
39339
|
-
function
|
|
40337
|
+
function optionalString2(value) {
|
|
39340
40338
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
39341
40339
|
}
|
|
39342
40340
|
function asRecord2(value) {
|
|
@@ -39387,7 +40385,7 @@ async function reconcileInbox(client, inbox, notifiedInbox) {
|
|
|
39387
40385
|
);
|
|
39388
40386
|
}
|
|
39389
40387
|
function resolveProject(client, projectArg) {
|
|
39390
|
-
const proj =
|
|
40388
|
+
const proj = optionalString2(projectArg) ?? client.agent?.project;
|
|
39391
40389
|
if (!proj) {
|
|
39392
40390
|
throw new Error('missing required parameter "project"');
|
|
39393
40391
|
}
|
|
@@ -39452,9 +40450,9 @@ var AGENT_COMMANDS = [
|
|
|
39452
40450
|
additionalProperties: false
|
|
39453
40451
|
},
|
|
39454
40452
|
async execute(client, args) {
|
|
39455
|
-
const delivery =
|
|
39456
|
-
const correlationId =
|
|
39457
|
-
const idempotencyKey =
|
|
40453
|
+
const delivery = optionalString2(args.delivery);
|
|
40454
|
+
const correlationId = optionalString2(args.correlationId);
|
|
40455
|
+
const idempotencyKey = optionalString2(args.idempotencyKey);
|
|
39458
40456
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
39459
40457
|
const message = await client.send({
|
|
39460
40458
|
target: requiredString(args.target, "target"),
|
|
@@ -39535,8 +40533,8 @@ var AGENT_COMMANDS = [
|
|
|
39535
40533
|
responses: await client.fanout({
|
|
39536
40534
|
targets,
|
|
39537
40535
|
content: requiredString(args.content, "content"),
|
|
39538
|
-
...
|
|
39539
|
-
...
|
|
40536
|
+
...optionalString2(args.correlationId) ? { correlationId: optionalString2(args.correlationId) } : {},
|
|
40537
|
+
...optionalString2(args.idempotencyKeyPrefix) ? { idempotencyKeyPrefix: optionalString2(args.idempotencyKeyPrefix) } : {},
|
|
39540
40538
|
...optionalWorkflowContext(args.workflowContext) ? { workflowContext: optionalWorkflowContext(args.workflowContext) } : {},
|
|
39541
40539
|
...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
|
|
39542
40540
|
...typeof args.timeoutMs === "number" ? { timeoutMs: args.timeoutMs } : {},
|
|
@@ -39785,9 +40783,9 @@ var AGENT_COMMANDS = [
|
|
|
39785
40783
|
return await client.recordWorkflowEntry(requiredString(args.runId, "runId"), {
|
|
39786
40784
|
category: requiredString(args.category, "category"),
|
|
39787
40785
|
area: requiredString(args.area, "area"),
|
|
39788
|
-
...
|
|
40786
|
+
...optionalString2(args.severity) ? { severity: optionalString2(args.severity) } : {},
|
|
39789
40787
|
summary: requiredString(args.summary, "summary"),
|
|
39790
|
-
...
|
|
40788
|
+
...optionalString2(args.details) ? { details: optionalString2(args.details) } : {},
|
|
39791
40789
|
...Array.isArray(args.evidence) ? { evidence: args.evidence } : {},
|
|
39792
40790
|
...Array.isArray(args.relatedEntryIds) ? { relatedEntryIds: args.relatedEntryIds } : {}
|
|
39793
40791
|
});
|
|
@@ -39903,8 +40901,8 @@ var AGENT_COMMANDS = [
|
|
|
39903
40901
|
project: resolveProject(client, args.project),
|
|
39904
40902
|
role: requiredString(args.role, "role"),
|
|
39905
40903
|
task: requiredString(args.task, "task"),
|
|
39906
|
-
...
|
|
39907
|
-
...
|
|
40904
|
+
...optionalString2(args.workflowRunId) ? { workflowRunId: optionalString2(args.workflowRunId) } : {},
|
|
40905
|
+
...optionalString2(args.stageId) ? { stageId: optionalString2(args.stageId) } : {},
|
|
39908
40906
|
...typeof args.budgetTokens === "number" ? { budgetTokens: args.budgetTokens } : {},
|
|
39909
40907
|
...Array.isArray(args.includeKinds) ? { includeKinds: args.includeKinds } : {}
|
|
39910
40908
|
});
|
|
@@ -39929,7 +40927,7 @@ var AGENT_COMMANDS = [
|
|
|
39929
40927
|
async execute(client, args) {
|
|
39930
40928
|
return await client.contextRecall({
|
|
39931
40929
|
project: resolveProject(client, args.project),
|
|
39932
|
-
...
|
|
40930
|
+
...optionalString2(args.query) ? { query: optionalString2(args.query) } : {},
|
|
39933
40931
|
...Array.isArray(args.kinds) ? { kinds: args.kinds } : {},
|
|
39934
40932
|
...typeof args.limit === "number" ? { limit: args.limit } : {}
|
|
39935
40933
|
});
|
|
@@ -39955,7 +40953,7 @@ var AGENT_COMMANDS = [
|
|
|
39955
40953
|
return await client.contextState({
|
|
39956
40954
|
project: resolveProject(client, args.project),
|
|
39957
40955
|
key: requiredString(args.key, "key"),
|
|
39958
|
-
...
|
|
40956
|
+
...optionalString2(args.asOf) ? { asOf: optionalString2(args.asOf) } : {}
|
|
39959
40957
|
});
|
|
39960
40958
|
}
|
|
39961
40959
|
},
|
|
@@ -39976,7 +40974,7 @@ var AGENT_COMMANDS = [
|
|
|
39976
40974
|
async execute(client, args) {
|
|
39977
40975
|
return await client.contextEpisode({
|
|
39978
40976
|
project: resolveProject(client, args.project),
|
|
39979
|
-
...
|
|
40977
|
+
...optionalString2(args.workflowRunId) ? { workflowRunId: optionalString2(args.workflowRunId) } : {}
|
|
39980
40978
|
});
|
|
39981
40979
|
}
|
|
39982
40980
|
},
|
|
@@ -40052,7 +41050,7 @@ function mintSessionToken(input) {
|
|
|
40052
41050
|
const expiresAt = input?.expiresAt ?? new Date(Date.now() + ttlMs).toISOString();
|
|
40053
41051
|
const payload = {
|
|
40054
41052
|
schema: "kxm.session-token.v1",
|
|
40055
|
-
sessionId: input?.sessionId ?? `session-${
|
|
41053
|
+
sessionId: input?.sessionId ?? `session-${randomUUID9()}`,
|
|
40056
41054
|
issuedAt,
|
|
40057
41055
|
expiresAt,
|
|
40058
41056
|
...input?.agentName ? { agentName: input.agentName } : {},
|
|
@@ -40080,17 +41078,17 @@ function parseSessionToken(token) {
|
|
|
40080
41078
|
}
|
|
40081
41079
|
function resolveUserConfigDirectory(overrideDir) {
|
|
40082
41080
|
if (overrideDir) return resolve16(overrideDir);
|
|
40083
|
-
return resolve16(process.env.KXM_USER_CONFIG_DIR?.trim() ||
|
|
41081
|
+
return resolve16(process.env.KXM_USER_CONFIG_DIR?.trim() || join30(homedir4(), ".config", "kxm"));
|
|
40084
41082
|
}
|
|
40085
41083
|
function sessionTokenPath(userConfigDir) {
|
|
40086
|
-
return
|
|
41084
|
+
return join30(resolveUserConfigDirectory(userConfigDir), "session.token");
|
|
40087
41085
|
}
|
|
40088
41086
|
function persistSessionTokenToDisk(token, options) {
|
|
40089
41087
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
40090
|
-
const dir =
|
|
40091
|
-
|
|
41088
|
+
const dir = dirname16(filePath);
|
|
41089
|
+
mkdirSync22(dir, { recursive: true, mode: 448 });
|
|
40092
41090
|
const mode = options?.mode ?? 384;
|
|
40093
|
-
|
|
41091
|
+
writeFileSync20(filePath, `${token.trim()}
|
|
40094
41092
|
`, { encoding: "utf8", mode });
|
|
40095
41093
|
try {
|
|
40096
41094
|
chmodSync6(filePath, mode);
|
|
@@ -40100,7 +41098,7 @@ function persistSessionTokenToDisk(token, options) {
|
|
|
40100
41098
|
}
|
|
40101
41099
|
function readSessionTokenFromDisk(options) {
|
|
40102
41100
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
40103
|
-
if (!
|
|
41101
|
+
if (!existsSync24(filePath)) return void 0;
|
|
40104
41102
|
try {
|
|
40105
41103
|
const token = readFileSync22(filePath, "utf8").trim();
|
|
40106
41104
|
if (!token) return void 0;
|
|
@@ -40113,7 +41111,7 @@ function readSessionTokenFromDisk(options) {
|
|
|
40113
41111
|
}
|
|
40114
41112
|
function clearSessionTokenFromDisk(options) {
|
|
40115
41113
|
const filePath = sessionTokenPath(options?.userConfigDir);
|
|
40116
|
-
if (!
|
|
41114
|
+
if (!existsSync24(filePath)) return false;
|
|
40117
41115
|
try {
|
|
40118
41116
|
unlinkSync2(filePath);
|
|
40119
41117
|
return true;
|
|
@@ -40212,7 +41210,7 @@ function enforceToolPolicy(commandName, env = process.env, options) {
|
|
|
40212
41210
|
return { allowed: true };
|
|
40213
41211
|
}
|
|
40214
41212
|
const tokenFile = sessionTokenPath(env.KXM_USER_CONFIG_DIR);
|
|
40215
|
-
if (
|
|
41213
|
+
if (existsSync24(tokenFile)) {
|
|
40216
41214
|
let tokenRaw;
|
|
40217
41215
|
try {
|
|
40218
41216
|
tokenRaw = readFileSync22(tokenFile, "utf8").trim();
|
|
@@ -40236,10 +41234,10 @@ function enforceToolPolicy(commandName, env = process.env, options) {
|
|
|
40236
41234
|
}
|
|
40237
41235
|
|
|
40238
41236
|
// plugins/kxm/src/config.ts
|
|
40239
|
-
var
|
|
40240
|
-
import { existsSync as
|
|
41237
|
+
var import_yaml12 = __toESM(require_dist(), 1);
|
|
41238
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync23, readFileSync as readFileSync23, writeFileSync as writeFileSync21 } from "node:fs";
|
|
40241
41239
|
import { homedir as homedir5 } from "node:os";
|
|
40242
|
-
import { dirname as
|
|
41240
|
+
import { dirname as dirname17, join as join31, resolve as resolve17 } from "node:path";
|
|
40243
41241
|
var KXM_CONFIG_SCHEMA = "kxm.config.v1";
|
|
40244
41242
|
var DEFAULT_KXM_CONFIG = {
|
|
40245
41243
|
schema: KXM_CONFIG_SCHEMA,
|
|
@@ -40290,7 +41288,7 @@ var DEFAULT_KXM_CONFIG = {
|
|
|
40290
41288
|
};
|
|
40291
41289
|
function userConfigDirectory(overrideDir) {
|
|
40292
41290
|
if (overrideDir) return resolve17(overrideDir);
|
|
40293
|
-
return resolve17(process.env.KXM_USER_CONFIG_DIR?.trim() ||
|
|
41291
|
+
return resolve17(process.env.KXM_USER_CONFIG_DIR?.trim() || join31(homedir5(), ".config", "kxm"));
|
|
40294
41292
|
}
|
|
40295
41293
|
function repoConfigDirectory(repoRoot3) {
|
|
40296
41294
|
return resolve17(repoRoot3, ".kxm");
|
|
@@ -40309,15 +41307,15 @@ function deepMerge(target, source) {
|
|
|
40309
41307
|
}
|
|
40310
41308
|
function loadKxmConfig(repoRoot3 = process.cwd(), options = {}) {
|
|
40311
41309
|
const userDir = userConfigDirectory(options.userConfigDir);
|
|
40312
|
-
const userConfigFile =
|
|
41310
|
+
const userConfigFile = join31(userDir, "config.yaml");
|
|
40313
41311
|
const repoDir = repoConfigDirectory(repoRoot3);
|
|
40314
|
-
const repoConfigFile =
|
|
41312
|
+
const repoConfigFile = join31(repoDir, "config.yaml");
|
|
40315
41313
|
let userRaw = {};
|
|
40316
41314
|
let userLoadedPath;
|
|
40317
|
-
if (
|
|
41315
|
+
if (existsSync25(userConfigFile)) {
|
|
40318
41316
|
try {
|
|
40319
41317
|
const text = readFileSync23(userConfigFile, "utf8");
|
|
40320
|
-
userRaw = (0,
|
|
41318
|
+
userRaw = (0, import_yaml12.parse)(text) ?? {};
|
|
40321
41319
|
userLoadedPath = userConfigFile;
|
|
40322
41320
|
} catch (error) {
|
|
40323
41321
|
throw new Error(`invalid user config YAML at ${userConfigFile}`, { cause: error });
|
|
@@ -40325,10 +41323,10 @@ function loadKxmConfig(repoRoot3 = process.cwd(), options = {}) {
|
|
|
40325
41323
|
}
|
|
40326
41324
|
let repoRaw = {};
|
|
40327
41325
|
let repoLoadedPath;
|
|
40328
|
-
if (
|
|
41326
|
+
if (existsSync25(repoConfigFile)) {
|
|
40329
41327
|
try {
|
|
40330
41328
|
const text = readFileSync23(repoConfigFile, "utf8");
|
|
40331
|
-
repoRaw = (0,
|
|
41329
|
+
repoRaw = (0, import_yaml12.parse)(text) ?? {};
|
|
40332
41330
|
repoLoadedPath = repoConfigFile;
|
|
40333
41331
|
} catch (error) {
|
|
40334
41332
|
throw new Error(`invalid project config YAML at ${repoConfigFile}`, { cause: error });
|
|
@@ -40363,12 +41361,12 @@ function getKxmConfigValue(config, keyPath) {
|
|
|
40363
41361
|
}
|
|
40364
41362
|
function setKxmConfigValue(repoRoot3, keyPath, value, options = {}) {
|
|
40365
41363
|
const scope = options.scope ?? "project";
|
|
40366
|
-
const targetFile2 = scope === "user" ?
|
|
40367
|
-
|
|
41364
|
+
const targetFile2 = scope === "user" ? join31(userConfigDirectory(options.userConfigDir), "config.yaml") : join31(repoConfigDirectory(repoRoot3), "config.yaml");
|
|
41365
|
+
mkdirSync23(dirname17(targetFile2), { recursive: true });
|
|
40368
41366
|
let existing = {};
|
|
40369
|
-
if (
|
|
41367
|
+
if (existsSync25(targetFile2)) {
|
|
40370
41368
|
try {
|
|
40371
|
-
existing = (0,
|
|
41369
|
+
existing = (0, import_yaml12.parse)(readFileSync23(targetFile2, "utf8")) ?? {};
|
|
40372
41370
|
} catch {
|
|
40373
41371
|
existing = {};
|
|
40374
41372
|
}
|
|
@@ -40386,7 +41384,7 @@ function setKxmConfigValue(repoRoot3, keyPath, value, options = {}) {
|
|
|
40386
41384
|
cursor2 = cursor2[p];
|
|
40387
41385
|
}
|
|
40388
41386
|
cursor2[parts[parts.length - 1]] = value;
|
|
40389
|
-
|
|
41387
|
+
writeFileSync21(targetFile2, (0, import_yaml12.stringify)(existing).trim() + "\n", "utf8");
|
|
40390
41388
|
}
|
|
40391
41389
|
function formatKxmConfig(config) {
|
|
40392
41390
|
const display = {
|
|
@@ -40397,7 +41395,7 @@ function formatKxmConfig(config) {
|
|
|
40397
41395
|
sync: config.sync,
|
|
40398
41396
|
loadedFrom: config.loadedFrom
|
|
40399
41397
|
};
|
|
40400
|
-
return (0,
|
|
41398
|
+
return (0, import_yaml12.stringify)(display).trim();
|
|
40401
41399
|
}
|
|
40402
41400
|
|
|
40403
41401
|
// plugins/kxm/src/autocomplete.ts
|
|
@@ -40455,7 +41453,7 @@ var SUBCOMMANDS = {
|
|
|
40455
41453
|
goal: ["create", "list", "get"],
|
|
40456
41454
|
task: ["create", "list", "get", "run", "sync"],
|
|
40457
41455
|
studio: ["layout", "serve"],
|
|
40458
|
-
role: ["list", "get", "add", "remove", "modify"]
|
|
41456
|
+
role: ["list", "get", "add", "remove", "modify", "hosts", "set-host", "resume"]
|
|
40459
41457
|
};
|
|
40460
41458
|
function generateShellCompletion(shell) {
|
|
40461
41459
|
switch (shell) {
|
|
@@ -40636,6 +41634,173 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
40636
41634
|
`;
|
|
40637
41635
|
}
|
|
40638
41636
|
|
|
41637
|
+
// plugins/kxm/src/completion-install.ts
|
|
41638
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync24, readFileSync as readFileSync24, writeFileSync as writeFileSync22 } from "node:fs";
|
|
41639
|
+
import { homedir as homedir6 } from "node:os";
|
|
41640
|
+
import { basename as basename6, delimiter, dirname as dirname18, isAbsolute as isAbsolute8, join as join32, resolve as resolve18 } from "node:path";
|
|
41641
|
+
var COMPLETION_MARKER = "# kxm completion";
|
|
41642
|
+
var PATH_MARKER = "# kxm path";
|
|
41643
|
+
function detectShell(env = process.env, platform = process.platform) {
|
|
41644
|
+
const shellPath = env.SHELL?.trim() || (process.platform === "win32" ? void 0 : env.SHELL?.trim());
|
|
41645
|
+
const name = shellPath ? basename6(shellPath).toLowerCase() : "";
|
|
41646
|
+
if (name === "bash" || name === "zsh" || name === "fish") return name;
|
|
41647
|
+
if (name.endsWith("bash") || name.includes("bash")) return "bash";
|
|
41648
|
+
if (name.endsWith("zsh")) return "zsh";
|
|
41649
|
+
if (name.endsWith("fish")) return "fish";
|
|
41650
|
+
return "unknown";
|
|
41651
|
+
}
|
|
41652
|
+
function effectiveHome(options) {
|
|
41653
|
+
const env = options.env ?? process.env;
|
|
41654
|
+
if (options.homeDir) return options.homeDir;
|
|
41655
|
+
const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
|
|
41656
|
+
return envHome && envHome.length > 0 ? resolve18(envHome) : homedir6();
|
|
41657
|
+
}
|
|
41658
|
+
function completionScriptPath(shell, options = {}) {
|
|
41659
|
+
const env = options.env ?? process.env;
|
|
41660
|
+
const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
|
|
41661
|
+
const base = explicit && explicit.length > 0 ? resolve18(explicit) : resolve18(effectiveHome(options), ".config", "kxm");
|
|
41662
|
+
return join32(base, "completions", `kxm.${shell}`);
|
|
41663
|
+
}
|
|
41664
|
+
function bashRcCandidate(options) {
|
|
41665
|
+
const home = effectiveHome(options);
|
|
41666
|
+
const candidates = [join32(home, ".bashrc"), join32(home, ".bash_profile")];
|
|
41667
|
+
const existing = candidates.find((candidate) => existsSync26(candidate));
|
|
41668
|
+
return existing ?? candidates[0];
|
|
41669
|
+
}
|
|
41670
|
+
function zshRcCandidate(options) {
|
|
41671
|
+
const env = options.env ?? process.env;
|
|
41672
|
+
const home = effectiveHome(options);
|
|
41673
|
+
if (env.ZDOTDIR?.trim()) return join32(resolve18(env.ZDOTDIR.trim()), ".zshrc");
|
|
41674
|
+
return join32(home, ".zshrc");
|
|
41675
|
+
}
|
|
41676
|
+
function fishCompletionTarget(shell, options) {
|
|
41677
|
+
const env = options.env ?? process.env;
|
|
41678
|
+
const home = effectiveHome(options);
|
|
41679
|
+
if (env.XDG_CONFIG_HOME?.trim()) return join32(resolve18(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
|
|
41680
|
+
return join32(home, ".config", "fish", "completions", "kxm.fish");
|
|
41681
|
+
}
|
|
41682
|
+
function completionRcTarget(shell, options = {}) {
|
|
41683
|
+
if (shell === "fish") {
|
|
41684
|
+
return { rcFile: void 0 };
|
|
41685
|
+
}
|
|
41686
|
+
if (shell === "zsh") {
|
|
41687
|
+
return { rcFile: zshRcCandidate(options) };
|
|
41688
|
+
}
|
|
41689
|
+
return { rcFile: bashRcCandidate(options) };
|
|
41690
|
+
}
|
|
41691
|
+
function sourceLine(shell, scriptPath) {
|
|
41692
|
+
if (shell === "fish") return `source ${scriptPath}`;
|
|
41693
|
+
if (shell === "zsh") return `[[ -f ${scriptPath} ]] && source ${scriptPath}`;
|
|
41694
|
+
return `[[ -f ${scriptPath} ]] && source ${scriptPath}`;
|
|
41695
|
+
}
|
|
41696
|
+
function installShellCompletion(shellInput, options = {}) {
|
|
41697
|
+
const shell = shellInput?.trim() && shellInput !== "auto" ? shellInput.trim() : detectShell(options.env ?? process.env, options.platform ?? process.platform);
|
|
41698
|
+
if (shell !== "bash" && shell !== "zsh" && shell !== "fish") {
|
|
41699
|
+
return {
|
|
41700
|
+
ok: false,
|
|
41701
|
+
shell: "unknown",
|
|
41702
|
+
scriptPath: "",
|
|
41703
|
+
rcFile: void 0,
|
|
41704
|
+
rcModified: false,
|
|
41705
|
+
alreadyInstalled: false,
|
|
41706
|
+
reason: "shell_not_detected"
|
|
41707
|
+
};
|
|
41708
|
+
}
|
|
41709
|
+
const scriptPath = completionScriptPath(shell, options);
|
|
41710
|
+
const { rcFile } = completionRcTarget(shell, options);
|
|
41711
|
+
const script = generateShellCompletion(shell);
|
|
41712
|
+
const existingScript = existsSync26(scriptPath);
|
|
41713
|
+
const existingScriptMatches = existingScript && readFileSync24(scriptPath, "utf8") === script;
|
|
41714
|
+
let rcModified = false;
|
|
41715
|
+
let alreadyInstalled = false;
|
|
41716
|
+
if (shell === "fish") {
|
|
41717
|
+
const fishTarget = fishCompletionTarget(shell, options);
|
|
41718
|
+
const fishInstalled = existsSync26(fishTarget) && readFileSync24(fishTarget, "utf8") === script;
|
|
41719
|
+
alreadyInstalled = fishInstalled;
|
|
41720
|
+
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
41721
|
+
mkdirSync24(resolve18(fishTarget, ".."), { recursive: true });
|
|
41722
|
+
writeFileSync22(fishTarget, script, { encoding: "utf8" });
|
|
41723
|
+
}
|
|
41724
|
+
return {
|
|
41725
|
+
ok: true,
|
|
41726
|
+
shell,
|
|
41727
|
+
scriptPath: fishTarget,
|
|
41728
|
+
rcFile: void 0,
|
|
41729
|
+
rcModified: !fishInstalled,
|
|
41730
|
+
alreadyInstalled
|
|
41731
|
+
};
|
|
41732
|
+
}
|
|
41733
|
+
if (rcFile) {
|
|
41734
|
+
const line = sourceLine(shell, scriptPath);
|
|
41735
|
+
const rcContent = existsSync26(rcFile) ? readFileSync24(rcFile, "utf8") : "";
|
|
41736
|
+
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
41737
|
+
}
|
|
41738
|
+
if (!options.dryRun) {
|
|
41739
|
+
if (!existingScriptMatches || options.overwrite) {
|
|
41740
|
+
mkdirSync24(resolve18(scriptPath, ".."), { recursive: true });
|
|
41741
|
+
writeFileSync22(scriptPath, script, { encoding: "utf8" });
|
|
41742
|
+
}
|
|
41743
|
+
if (rcFile && !alreadyInstalled) {
|
|
41744
|
+
const line = sourceLine(shell, scriptPath);
|
|
41745
|
+
const marker = `${COMPLETION_MARKER} (${(/* @__PURE__ */ new Date()).toISOString()})`;
|
|
41746
|
+
const stanza = `
|
|
41747
|
+
${marker}
|
|
41748
|
+
${line}
|
|
41749
|
+
`;
|
|
41750
|
+
mkdirSync24(resolve18(rcFile, ".."), { recursive: true });
|
|
41751
|
+
writeFileSync22(rcFile, (existsSync26(rcFile) ? readFileSync24(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
41752
|
+
rcModified = true;
|
|
41753
|
+
}
|
|
41754
|
+
}
|
|
41755
|
+
return {
|
|
41756
|
+
ok: true,
|
|
41757
|
+
shell,
|
|
41758
|
+
scriptPath,
|
|
41759
|
+
rcFile,
|
|
41760
|
+
rcModified,
|
|
41761
|
+
alreadyInstalled
|
|
41762
|
+
};
|
|
41763
|
+
}
|
|
41764
|
+
function kxmBinDir(env = process.env) {
|
|
41765
|
+
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
41766
|
+
if (argv1 && isAbsolute8(argv1)) {
|
|
41767
|
+
const dir = dirname18(argv1);
|
|
41768
|
+
if (existsSync26(join32(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
41769
|
+
}
|
|
41770
|
+
const pathEnv = env.PATH ?? "";
|
|
41771
|
+
for (const part of pathEnv.split(delimiter)) {
|
|
41772
|
+
if (!part) continue;
|
|
41773
|
+
const candidate = resolve18(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
41774
|
+
if (existsSync26(candidate)) return resolve18(part);
|
|
41775
|
+
}
|
|
41776
|
+
return void 0;
|
|
41777
|
+
}
|
|
41778
|
+
function pathLine(binDir) {
|
|
41779
|
+
return `export PATH="${binDir}:$PATH" # kxm`;
|
|
41780
|
+
}
|
|
41781
|
+
function installPathEntry(shellInput, options = {}) {
|
|
41782
|
+
const shell = shellInput?.trim() && shellInput !== "auto" ? shellInput.trim() : detectShell(options.env ?? process.env, options.platform ?? process.platform);
|
|
41783
|
+
if (shell !== "bash" && shell !== "zsh") {
|
|
41784
|
+
return { ok: false, binDir: void 0, rcFile: void 0, rcModified: false, alreadyInstalled: false, reason: "shell_not_supported" };
|
|
41785
|
+
}
|
|
41786
|
+
const binDir = options.binDir ?? kxmBinDir(options.env ?? process.env);
|
|
41787
|
+
if (!binDir) {
|
|
41788
|
+
return { ok: false, binDir: void 0, rcFile: void 0, rcModified: false, alreadyInstalled: false, reason: "kxm_not_found" };
|
|
41789
|
+
}
|
|
41790
|
+
const { rcFile } = completionRcTarget(shell, options);
|
|
41791
|
+
const line = pathLine(binDir);
|
|
41792
|
+
const rcContent = existsSync26(rcFile ?? "") ? readFileSync24(rcFile, "utf8") : "";
|
|
41793
|
+
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
41794
|
+
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
41795
|
+
mkdirSync24(dirname18(rcFile), { recursive: true });
|
|
41796
|
+
writeFileSync22(rcFile, rcContent + `
|
|
41797
|
+
${PATH_MARKER}
|
|
41798
|
+
${line}
|
|
41799
|
+
`, { encoding: "utf8" });
|
|
41800
|
+
}
|
|
41801
|
+
return { ok: true, binDir, rcFile, rcModified: !alreadyInstalled && !options.dryRun, alreadyInstalled };
|
|
41802
|
+
}
|
|
41803
|
+
|
|
40639
41804
|
// plugins/kxm/src/suggest.ts
|
|
40640
41805
|
var WORKFLOW_PATTERNS = [
|
|
40641
41806
|
{
|
|
@@ -40743,22 +41908,22 @@ function suggestWorkflowAndRoles(prompt, options = {}) {
|
|
|
40743
41908
|
}
|
|
40744
41909
|
|
|
40745
41910
|
// plugins/kxm/src/task-manager.ts
|
|
40746
|
-
var
|
|
40747
|
-
import { randomUUID as
|
|
40748
|
-
import { existsSync as
|
|
40749
|
-
import { join as
|
|
41911
|
+
var import_yaml13 = __toESM(require_dist(), 1);
|
|
41912
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
41913
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync25, readFileSync as readFileSync25, readdirSync as readdirSync8, writeFileSync as writeFileSync23 } from "node:fs";
|
|
41914
|
+
import { join as join33, resolve as resolve19 } from "node:path";
|
|
40750
41915
|
var GOAL_SCHEMA = "kxm.goal.v1";
|
|
40751
41916
|
var TASK_SCHEMA = "kxm.task.v1";
|
|
40752
41917
|
function goalsDirectory(repoRoot3) {
|
|
40753
|
-
return
|
|
41918
|
+
return resolve19(repoRoot3, ".kxm", "goals");
|
|
40754
41919
|
}
|
|
40755
41920
|
function tasksDirectory(repoRoot3) {
|
|
40756
|
-
return
|
|
41921
|
+
return resolve19(repoRoot3, ".kxm", "tasks");
|
|
40757
41922
|
}
|
|
40758
41923
|
function createGoal(repoRoot3, input) {
|
|
40759
41924
|
const dir = goalsDirectory(repoRoot3);
|
|
40760
|
-
|
|
40761
|
-
const id = `goal_${
|
|
41925
|
+
mkdirSync25(dir, { recursive: true });
|
|
41926
|
+
const id = `goal_${randomUUID10().replaceAll("-", "").slice(0, 12)}`;
|
|
40762
41927
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
40763
41928
|
const record = {
|
|
40764
41929
|
schema: GOAL_SCHEMA,
|
|
@@ -40771,20 +41936,20 @@ function createGoal(repoRoot3, input) {
|
|
|
40771
41936
|
createdAt: now,
|
|
40772
41937
|
updatedAt: now
|
|
40773
41938
|
};
|
|
40774
|
-
const filePath =
|
|
40775
|
-
|
|
41939
|
+
const filePath = join33(dir, `${id}.yaml`);
|
|
41940
|
+
writeFileSync23(filePath, (0, import_yaml13.stringify)(record).trim() + "\n", "utf8");
|
|
40776
41941
|
return record;
|
|
40777
41942
|
}
|
|
40778
41943
|
function listGoals(repoRoot3) {
|
|
40779
41944
|
const dir = goalsDirectory(repoRoot3);
|
|
40780
|
-
if (!
|
|
41945
|
+
if (!existsSync27(dir)) return [];
|
|
40781
41946
|
const goals = [];
|
|
40782
41947
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
40783
41948
|
for (const entry of entries) {
|
|
40784
41949
|
if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
|
|
40785
41950
|
try {
|
|
40786
|
-
const text =
|
|
40787
|
-
const doc = (0,
|
|
41951
|
+
const text = readFileSync25(join33(dir, entry.name), "utf8");
|
|
41952
|
+
const doc = (0, import_yaml13.parse)(text);
|
|
40788
41953
|
if (doc && doc.schema === GOAL_SCHEMA) goals.push(doc);
|
|
40789
41954
|
} catch {
|
|
40790
41955
|
}
|
|
@@ -40794,8 +41959,8 @@ function listGoals(repoRoot3) {
|
|
|
40794
41959
|
}
|
|
40795
41960
|
function createTask(repoRoot3, input) {
|
|
40796
41961
|
const dir = tasksDirectory(repoRoot3);
|
|
40797
|
-
|
|
40798
|
-
const id = `task_${
|
|
41962
|
+
mkdirSync25(dir, { recursive: true });
|
|
41963
|
+
const id = `task_${randomUUID10().replaceAll("-", "").slice(0, 12)}`;
|
|
40799
41964
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
40800
41965
|
const record = {
|
|
40801
41966
|
schema: TASK_SCHEMA,
|
|
@@ -40816,20 +41981,20 @@ function createTask(repoRoot3, input) {
|
|
|
40816
41981
|
createdAt: now,
|
|
40817
41982
|
updatedAt: now
|
|
40818
41983
|
};
|
|
40819
|
-
const filePath =
|
|
40820
|
-
|
|
41984
|
+
const filePath = join33(dir, `${id}.yaml`);
|
|
41985
|
+
writeFileSync23(filePath, (0, import_yaml13.stringify)(record).trim() + "\n", "utf8");
|
|
40821
41986
|
return record;
|
|
40822
41987
|
}
|
|
40823
41988
|
function listTasks(repoRoot3, options = {}) {
|
|
40824
41989
|
const dir = tasksDirectory(repoRoot3);
|
|
40825
|
-
if (!
|
|
41990
|
+
if (!existsSync27(dir)) return [];
|
|
40826
41991
|
const tasks = [];
|
|
40827
41992
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
40828
41993
|
for (const entry of entries) {
|
|
40829
41994
|
if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
|
|
40830
41995
|
try {
|
|
40831
|
-
const text =
|
|
40832
|
-
const doc = (0,
|
|
41996
|
+
const text = readFileSync25(join33(dir, entry.name), "utf8");
|
|
41997
|
+
const doc = (0, import_yaml13.parse)(text);
|
|
40833
41998
|
if (doc && doc.schema === TASK_SCHEMA) {
|
|
40834
41999
|
if (options.goalId && doc.goalId !== options.goalId) continue;
|
|
40835
42000
|
if (options.status && doc.status !== options.status) continue;
|
|
@@ -40843,11 +42008,11 @@ function listTasks(repoRoot3, options = {}) {
|
|
|
40843
42008
|
}
|
|
40844
42009
|
function getTask(repoRoot3, taskId) {
|
|
40845
42010
|
const dir = tasksDirectory(repoRoot3);
|
|
40846
|
-
const filePath =
|
|
40847
|
-
if (!
|
|
42011
|
+
const filePath = join33(dir, `${taskId}.yaml`);
|
|
42012
|
+
if (!existsSync27(filePath)) return void 0;
|
|
40848
42013
|
try {
|
|
40849
|
-
const text =
|
|
40850
|
-
const doc = (0,
|
|
42014
|
+
const text = readFileSync25(filePath, "utf8");
|
|
42015
|
+
const doc = (0, import_yaml13.parse)(text);
|
|
40851
42016
|
return doc && doc.schema === TASK_SCHEMA ? doc : void 0;
|
|
40852
42017
|
} catch {
|
|
40853
42018
|
return void 0;
|
|
@@ -40861,8 +42026,8 @@ function updateTaskStatus(repoRoot3, taskId, status, options = {}) {
|
|
|
40861
42026
|
task.status = status;
|
|
40862
42027
|
if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
|
|
40863
42028
|
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
40864
|
-
const filePath =
|
|
40865
|
-
|
|
42029
|
+
const filePath = join33(tasksDirectory(repoRoot3), `${taskId}.yaml`);
|
|
42030
|
+
writeFileSync23(filePath, (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
|
|
40866
42031
|
return task;
|
|
40867
42032
|
}
|
|
40868
42033
|
function syncTaskWithTracker(repoRoot3, taskId, options = {}) {
|
|
@@ -40877,17 +42042,17 @@ function syncTaskWithTracker(repoRoot3, taskId, options = {}) {
|
|
|
40877
42042
|
task.trackerSync.syncStatus = "synced";
|
|
40878
42043
|
task.trackerSync.lastSyncedAt = now;
|
|
40879
42044
|
task.updatedAt = now;
|
|
40880
|
-
const filePath =
|
|
40881
|
-
|
|
42045
|
+
const filePath = join33(tasksDirectory(repoRoot3), `${taskId}.yaml`);
|
|
42046
|
+
writeFileSync23(filePath, (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
|
|
40882
42047
|
return task;
|
|
40883
42048
|
}
|
|
40884
42049
|
|
|
40885
42050
|
// plugins/kxm/src/cli.ts
|
|
40886
|
-
var
|
|
42051
|
+
var import_yaml16 = __toESM(require_dist(), 1);
|
|
40887
42052
|
|
|
40888
42053
|
// plugins/kxm/src/studio-layout.ts
|
|
40889
42054
|
import { createServer } from "node:http";
|
|
40890
|
-
import { randomUUID as
|
|
42055
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
40891
42056
|
var STUDIO_LAYOUT_SCHEMA = "kxm.studio-layout.v1";
|
|
40892
42057
|
function generateStudioLayout(plan, state) {
|
|
40893
42058
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -41150,7 +42315,7 @@ function createStudioServer(options = {}) {
|
|
|
41150
42315
|
}));
|
|
41151
42316
|
return;
|
|
41152
42317
|
}
|
|
41153
|
-
const mutationId = `mut_${
|
|
42318
|
+
const mutationId = `mut_${randomUUID11().replaceAll("-", "").slice(0, 12)}`;
|
|
41154
42319
|
if (options.onMutation) {
|
|
41155
42320
|
try {
|
|
41156
42321
|
const outcome = await options.onMutation(command, args);
|
|
@@ -41647,9 +42812,9 @@ function createStudioServer(options = {}) {
|
|
|
41647
42812
|
}
|
|
41648
42813
|
|
|
41649
42814
|
// plugins/kxm/src/role.ts
|
|
41650
|
-
var
|
|
41651
|
-
import { existsSync as
|
|
41652
|
-
import { join as
|
|
42815
|
+
var import_yaml14 = __toESM(require_dist(), 1);
|
|
42816
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync26, readdirSync as readdirSync9, readFileSync as readFileSync26, rmSync as rmSync8, writeFileSync as writeFileSync24 } from "node:fs";
|
|
42817
|
+
import { join as join34 } from "node:path";
|
|
41653
42818
|
var KXM_ROLE_SCHEMA = "kxm.role.v1";
|
|
41654
42819
|
var DEFAULT_ROLES = {
|
|
41655
42820
|
writer: {
|
|
@@ -41728,22 +42893,22 @@ var DEFAULT_ROLES = {
|
|
|
41728
42893
|
};
|
|
41729
42894
|
function rolesDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
|
|
41730
42895
|
if (scope === "global") {
|
|
41731
|
-
return
|
|
42896
|
+
return join34(userConfigDirectory(userConfigDir), "roles");
|
|
41732
42897
|
}
|
|
41733
|
-
return
|
|
42898
|
+
return join34(repoConfigDirectory(repoRoot3), "roles");
|
|
41734
42899
|
}
|
|
41735
42900
|
function ensureRolesDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
|
|
41736
42901
|
const dir = rolesDirectory(scope, repoRoot3, userConfigDir);
|
|
41737
|
-
if (!
|
|
41738
|
-
|
|
42902
|
+
if (!existsSync28(dir)) {
|
|
42903
|
+
mkdirSync26(dir, { recursive: true });
|
|
41739
42904
|
}
|
|
41740
42905
|
return dir;
|
|
41741
42906
|
}
|
|
41742
42907
|
function parseRoleFile(filePath) {
|
|
41743
|
-
if (!
|
|
42908
|
+
if (!existsSync28(filePath)) return void 0;
|
|
41744
42909
|
try {
|
|
41745
|
-
const raw =
|
|
41746
|
-
const parsed = (0,
|
|
42910
|
+
const raw = readFileSync26(filePath, "utf8");
|
|
42911
|
+
const parsed = (0, import_yaml14.parse)(raw);
|
|
41747
42912
|
if (!parsed || typeof parsed !== "object" || parsed.schema !== KXM_ROLE_SCHEMA) {
|
|
41748
42913
|
return void 0;
|
|
41749
42914
|
}
|
|
@@ -41769,10 +42934,10 @@ function listRoles(options = {}) {
|
|
|
41769
42934
|
const globalDir = rolesDirectory("global", repoRoot3, options.userConfigDir);
|
|
41770
42935
|
const localDir = rolesDirectory("local", repoRoot3, options.userConfigDir);
|
|
41771
42936
|
const localRoles = /* @__PURE__ */ new Map();
|
|
41772
|
-
if (scopeFilter !== "global" &&
|
|
42937
|
+
if (scopeFilter !== "global" && existsSync28(localDir)) {
|
|
41773
42938
|
for (const entry of readdirSync9(localDir)) {
|
|
41774
42939
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
41775
|
-
const filePath =
|
|
42940
|
+
const filePath = join34(localDir, entry);
|
|
41776
42941
|
const parsed = parseRoleFile(filePath);
|
|
41777
42942
|
if (parsed) {
|
|
41778
42943
|
localRoles.set(parsed.id, { role: parsed, filePath });
|
|
@@ -41781,10 +42946,10 @@ function listRoles(options = {}) {
|
|
|
41781
42946
|
}
|
|
41782
42947
|
}
|
|
41783
42948
|
const globalRoles = /* @__PURE__ */ new Map();
|
|
41784
|
-
if (scopeFilter !== "local" &&
|
|
42949
|
+
if (scopeFilter !== "local" && existsSync28(globalDir)) {
|
|
41785
42950
|
for (const entry of readdirSync9(globalDir)) {
|
|
41786
42951
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
41787
|
-
const filePath =
|
|
42952
|
+
const filePath = join34(globalDir, entry);
|
|
41788
42953
|
const parsed = parseRoleFile(filePath);
|
|
41789
42954
|
if (parsed) {
|
|
41790
42955
|
globalRoles.set(parsed.id, { role: parsed, filePath });
|
|
@@ -41832,13 +42997,13 @@ function getRole(roleId, options = {}) {
|
|
|
41832
42997
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
41833
42998
|
if (scope !== "global") {
|
|
41834
42999
|
const localDir = rolesDirectory("local", repoRoot3, options.userConfigDir);
|
|
41835
|
-
const localFile =
|
|
43000
|
+
const localFile = join34(localDir, `${roleId}.yaml`);
|
|
41836
43001
|
const parsed = parseRoleFile(localFile);
|
|
41837
43002
|
if (parsed) return { role: parsed, scope: "local", filePath: localFile };
|
|
41838
43003
|
}
|
|
41839
43004
|
if (scope !== "local") {
|
|
41840
43005
|
const globalDir = rolesDirectory("global", repoRoot3, options.userConfigDir);
|
|
41841
|
-
const globalFile =
|
|
43006
|
+
const globalFile = join34(globalDir, `${roleId}.yaml`);
|
|
41842
43007
|
const parsed = parseRoleFile(globalFile);
|
|
41843
43008
|
if (parsed) return { role: parsed, scope: "global", filePath: globalFile };
|
|
41844
43009
|
}
|
|
@@ -41848,19 +43013,19 @@ function addRole(role, options = {}) {
|
|
|
41848
43013
|
const scope = options.scope ?? "local";
|
|
41849
43014
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
41850
43015
|
const dir = ensureRolesDirectory(scope, repoRoot3, options.userConfigDir);
|
|
41851
|
-
const filePath =
|
|
41852
|
-
if (
|
|
43016
|
+
const filePath = join34(dir, `${role.id}.yaml`);
|
|
43017
|
+
if (existsSync28(filePath) && !options.overwrite) {
|
|
41853
43018
|
throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
|
|
41854
43019
|
}
|
|
41855
|
-
|
|
43020
|
+
writeFileSync24(filePath, (0, import_yaml14.stringify)(role), "utf8");
|
|
41856
43021
|
return { id: role.id, filePath, scope };
|
|
41857
43022
|
}
|
|
41858
43023
|
function removeRole(roleId, options = {}) {
|
|
41859
43024
|
const scope = options.scope ?? "local";
|
|
41860
43025
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
41861
43026
|
const dir = rolesDirectory(scope, repoRoot3, options.userConfigDir);
|
|
41862
|
-
const filePath =
|
|
41863
|
-
if (!
|
|
43027
|
+
const filePath = join34(dir, `${roleId}.yaml`);
|
|
43028
|
+
if (!existsSync28(filePath)) {
|
|
41864
43029
|
throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
|
|
41865
43030
|
}
|
|
41866
43031
|
rmSync8(filePath);
|
|
@@ -41881,15 +43046,236 @@ function modifyRole(roleId, updates, options = {}) {
|
|
|
41881
43046
|
const scope = options.scope ?? target.scope;
|
|
41882
43047
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
41883
43048
|
const dir = ensureRolesDirectory(scope, repoRoot3, options.userConfigDir);
|
|
41884
|
-
const filePath =
|
|
41885
|
-
|
|
43049
|
+
const filePath = join34(dir, `${roleId}.yaml`);
|
|
43050
|
+
writeFileSync24(filePath, (0, import_yaml14.stringify)(updated), "utf8");
|
|
41886
43051
|
return { id: roleId, role: updated, filePath, scope };
|
|
41887
43052
|
}
|
|
43053
|
+
var KXM_ROLE_HOSTS_SCHEMA = "kxm.role-hosts.v1";
|
|
43054
|
+
var DEFAULT_ROLE_SEATS = {
|
|
43055
|
+
planner: {
|
|
43056
|
+
seatId: "planner",
|
|
43057
|
+
description: "Architecture breakdown, requirement decomposition, and safety boundary definition.",
|
|
43058
|
+
defaultModel: "anthropic/claude-fable-5.1",
|
|
43059
|
+
defaultHost: "pi",
|
|
43060
|
+
allowedTools: ["view_file", "grep_search", "find_by_name", "list_dir"],
|
|
43061
|
+
requiredEvidenceKind: "architecture_review"
|
|
43062
|
+
},
|
|
43063
|
+
writer: {
|
|
43064
|
+
seatId: "writer",
|
|
43065
|
+
description: "Primary implementation agent. Writes code, refactors components, and authors unit tests.",
|
|
43066
|
+
defaultModel: "x-ai/grok-4.6",
|
|
43067
|
+
defaultHost: "grok",
|
|
43068
|
+
allowedTools: ["view_file", "replace_file_content", "write_to_file", "run_command"],
|
|
43069
|
+
requiredEvidenceKind: "git_diff"
|
|
43070
|
+
},
|
|
43071
|
+
"critic-arch": {
|
|
43072
|
+
seatId: "critic-arch",
|
|
43073
|
+
description: "Independent critic reviewing implementation diffs for architectural integrity.",
|
|
43074
|
+
defaultModel: "anthropic/claude-fable-5.1",
|
|
43075
|
+
defaultHost: "pi",
|
|
43076
|
+
allowedTools: ["view_file", "grep_search"],
|
|
43077
|
+
requiredEvidenceKind: "architecture_review"
|
|
43078
|
+
},
|
|
43079
|
+
"critic-cli": {
|
|
43080
|
+
seatId: "critic-cli",
|
|
43081
|
+
description: "CLI, documentation, and developer ergonomics critic.",
|
|
43082
|
+
defaultModel: "openai/gpt-5.6-sol",
|
|
43083
|
+
defaultHost: "pi",
|
|
43084
|
+
allowedTools: ["view_file", "grep_search"],
|
|
43085
|
+
requiredEvidenceKind: "cli_review"
|
|
43086
|
+
},
|
|
43087
|
+
verifier: {
|
|
43088
|
+
seatId: "verifier",
|
|
43089
|
+
description: "Deterministic gate evaluation runner.",
|
|
43090
|
+
defaultModel: "evaluator",
|
|
43091
|
+
defaultHost: "pi",
|
|
43092
|
+
allowedTools: ["run_command"],
|
|
43093
|
+
requiredEvidenceKind: "test_run"
|
|
43094
|
+
}
|
|
43095
|
+
};
|
|
43096
|
+
function findExistingRoleHostsFile(scope, repoRoot3 = process.cwd(), userConfigDir) {
|
|
43097
|
+
const dir = scope === "global" ? userConfigDirectory(userConfigDir) : repoConfigDirectory(repoRoot3);
|
|
43098
|
+
const yamlPath = join34(dir, "role-hosts.yaml");
|
|
43099
|
+
const ymlPath = join34(dir, "role-hosts.yml");
|
|
43100
|
+
const jsonPath = join34(dir, "role-hosts.json");
|
|
43101
|
+
if (existsSync28(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
|
|
43102
|
+
if (existsSync28(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
|
|
43103
|
+
if (existsSync28(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
|
|
43104
|
+
return { filePath: yamlPath, exists: false, format: "yaml" };
|
|
43105
|
+
}
|
|
43106
|
+
function parseRoleHostsFile(filePath) {
|
|
43107
|
+
if (!existsSync28(filePath)) return void 0;
|
|
43108
|
+
try {
|
|
43109
|
+
const raw = readFileSync26(filePath, "utf8");
|
|
43110
|
+
const parsed = (0, import_yaml14.parse)(raw);
|
|
43111
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
43112
|
+
if (parsed.schema && parsed.schema !== KXM_ROLE_HOSTS_SCHEMA) return void 0;
|
|
43113
|
+
return {
|
|
43114
|
+
schema: KXM_ROLE_HOSTS_SCHEMA,
|
|
43115
|
+
seats: parsed.seats && typeof parsed.seats === "object" ? parsed.seats : {},
|
|
43116
|
+
hostProviders: parsed.hostProviders && typeof parsed.hostProviders === "object" ? parsed.hostProviders : {}
|
|
43117
|
+
};
|
|
43118
|
+
} catch {
|
|
43119
|
+
return void 0;
|
|
43120
|
+
}
|
|
43121
|
+
}
|
|
43122
|
+
function loadRoleHostsConfig(options = {}) {
|
|
43123
|
+
const scopeFilter = options.scope ?? "all";
|
|
43124
|
+
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
43125
|
+
let localConfig;
|
|
43126
|
+
let localPath;
|
|
43127
|
+
if (scopeFilter !== "global") {
|
|
43128
|
+
const localFound = findExistingRoleHostsFile("local", repoRoot3, options.userConfigDir);
|
|
43129
|
+
if (localFound.exists) {
|
|
43130
|
+
localConfig = parseRoleHostsFile(localFound.filePath);
|
|
43131
|
+
localPath = localFound.filePath;
|
|
43132
|
+
}
|
|
43133
|
+
}
|
|
43134
|
+
let globalConfig;
|
|
43135
|
+
let globalPath;
|
|
43136
|
+
if (scopeFilter !== "local") {
|
|
43137
|
+
const globalFound = findExistingRoleHostsFile("global", repoRoot3, options.userConfigDir);
|
|
43138
|
+
if (globalFound.exists) {
|
|
43139
|
+
globalConfig = parseRoleHostsFile(globalFound.filePath);
|
|
43140
|
+
globalPath = globalFound.filePath;
|
|
43141
|
+
}
|
|
43142
|
+
}
|
|
43143
|
+
if (scopeFilter === "local") {
|
|
43144
|
+
return {
|
|
43145
|
+
config: localConfig ?? { schema: KXM_ROLE_HOSTS_SCHEMA, seats: {}, hostProviders: {} },
|
|
43146
|
+
filePath: localPath,
|
|
43147
|
+
scope: localPath ? "local" : "default"
|
|
43148
|
+
};
|
|
43149
|
+
}
|
|
43150
|
+
if (scopeFilter === "global") {
|
|
43151
|
+
return {
|
|
43152
|
+
config: globalConfig ?? { schema: KXM_ROLE_HOSTS_SCHEMA, seats: {}, hostProviders: {} },
|
|
43153
|
+
filePath: globalPath,
|
|
43154
|
+
scope: globalPath ? "global" : "default"
|
|
43155
|
+
};
|
|
43156
|
+
}
|
|
43157
|
+
const mergedSeats = {
|
|
43158
|
+
...globalConfig?.seats ?? {},
|
|
43159
|
+
...localConfig?.seats ?? {}
|
|
43160
|
+
};
|
|
43161
|
+
const mergedHostProviders = {
|
|
43162
|
+
...globalConfig?.hostProviders ?? {},
|
|
43163
|
+
...localConfig?.hostProviders ?? {}
|
|
43164
|
+
};
|
|
43165
|
+
const primaryPath = localPath ?? globalPath;
|
|
43166
|
+
const primaryScope = localPath ? "local" : globalPath ? "global" : "default";
|
|
43167
|
+
return {
|
|
43168
|
+
config: {
|
|
43169
|
+
schema: KXM_ROLE_HOSTS_SCHEMA,
|
|
43170
|
+
seats: mergedSeats,
|
|
43171
|
+
hostProviders: mergedHostProviders
|
|
43172
|
+
},
|
|
43173
|
+
filePath: primaryPath,
|
|
43174
|
+
scope: primaryScope
|
|
43175
|
+
};
|
|
43176
|
+
}
|
|
43177
|
+
function saveRoleHostsConfig(config, options = {}) {
|
|
43178
|
+
const scope = options.scope ?? "local";
|
|
43179
|
+
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
43180
|
+
const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot3);
|
|
43181
|
+
if (!existsSync28(dir)) {
|
|
43182
|
+
mkdirSync26(dir, { recursive: true });
|
|
43183
|
+
}
|
|
43184
|
+
const format = options.format ?? "yaml";
|
|
43185
|
+
const filePath = join34(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
|
|
43186
|
+
const payload = {
|
|
43187
|
+
schema: KXM_ROLE_HOSTS_SCHEMA,
|
|
43188
|
+
seats: config.seats ?? {},
|
|
43189
|
+
...config.hostProviders && Object.keys(config.hostProviders).length > 0 ? { hostProviders: config.hostProviders } : {}
|
|
43190
|
+
};
|
|
43191
|
+
const content = format === "json" ? JSON.stringify(payload, null, 2) + "\n" : (0, import_yaml14.stringify)(payload);
|
|
43192
|
+
writeFileSync24(filePath, content, "utf8");
|
|
43193
|
+
return { filePath, scope };
|
|
43194
|
+
}
|
|
43195
|
+
function setRoleSeatHost(seatId, host, options = {}) {
|
|
43196
|
+
const scope = options.scope ?? "local";
|
|
43197
|
+
const current = loadRoleHostsConfig({
|
|
43198
|
+
scope,
|
|
43199
|
+
repoRoot: options.repoRoot,
|
|
43200
|
+
userConfigDir: options.userConfigDir
|
|
43201
|
+
});
|
|
43202
|
+
const seats = { ...current.config.seats ?? {} };
|
|
43203
|
+
const existing = seats[seatId] ?? {};
|
|
43204
|
+
const binding = {
|
|
43205
|
+
...existing,
|
|
43206
|
+
host,
|
|
43207
|
+
...options.model !== void 0 ? { model: options.model } : {},
|
|
43208
|
+
...options.effort !== void 0 ? { effort: options.effort } : {}
|
|
43209
|
+
};
|
|
43210
|
+
seats[seatId] = binding;
|
|
43211
|
+
const updatedConfig = {
|
|
43212
|
+
...current.config,
|
|
43213
|
+
schema: KXM_ROLE_HOSTS_SCHEMA,
|
|
43214
|
+
seats
|
|
43215
|
+
};
|
|
43216
|
+
const saved = saveRoleHostsConfig(updatedConfig, {
|
|
43217
|
+
scope,
|
|
43218
|
+
repoRoot: options.repoRoot,
|
|
43219
|
+
userConfigDir: options.userConfigDir,
|
|
43220
|
+
format: options.format
|
|
43221
|
+
});
|
|
43222
|
+
return { filePath: saved.filePath, seatId, binding, scope };
|
|
43223
|
+
}
|
|
43224
|
+
function resolveRoleSeat(seatId, options = {}) {
|
|
43225
|
+
const hostsConfig = loadRoleHostsConfig({
|
|
43226
|
+
scope: "all",
|
|
43227
|
+
repoRoot: options.repoRoot,
|
|
43228
|
+
userConfigDir: options.userConfigDir
|
|
43229
|
+
}).config;
|
|
43230
|
+
const configuredSeat = hostsConfig.seats?.[seatId];
|
|
43231
|
+
const defaultSeat = DEFAULT_ROLE_SEATS[seatId];
|
|
43232
|
+
const roleDef = getRole(seatId, {
|
|
43233
|
+
scope: "all",
|
|
43234
|
+
repoRoot: options.repoRoot,
|
|
43235
|
+
userConfigDir: options.userConfigDir
|
|
43236
|
+
})?.role ?? DEFAULT_ROLES[seatId];
|
|
43237
|
+
const primaryRoster = roleDef?.roster?.[0];
|
|
43238
|
+
let host = "pi";
|
|
43239
|
+
let source = "fallback";
|
|
43240
|
+
if (options.hostOverride) {
|
|
43241
|
+
host = options.hostOverride;
|
|
43242
|
+
source = "override";
|
|
43243
|
+
} else if (configuredSeat?.host) {
|
|
43244
|
+
host = configuredSeat.host;
|
|
43245
|
+
source = "role-hosts";
|
|
43246
|
+
} else if (defaultSeat?.defaultHost) {
|
|
43247
|
+
host = defaultSeat.defaultHost;
|
|
43248
|
+
source = "seat-default";
|
|
43249
|
+
} else if (primaryRoster?.harness) {
|
|
43250
|
+
host = primaryRoster.harness;
|
|
43251
|
+
source = "role-roster";
|
|
43252
|
+
}
|
|
43253
|
+
let model;
|
|
43254
|
+
if (options.modelOverride) {
|
|
43255
|
+
model = options.modelOverride;
|
|
43256
|
+
} else if (configuredSeat?.model) {
|
|
43257
|
+
model = configuredSeat.model;
|
|
43258
|
+
} else if (defaultSeat?.defaultModel) {
|
|
43259
|
+
model = defaultSeat.defaultModel;
|
|
43260
|
+
} else if (primaryRoster?.model) {
|
|
43261
|
+
model = primaryRoster.model;
|
|
43262
|
+
}
|
|
43263
|
+
const provider = hostsConfig.hostProviders?.[host] ?? roleDef?.roster?.find((r) => r.harness === host)?.provider;
|
|
43264
|
+
const effort = configuredSeat?.effort ?? roleDef?.roster?.find((r) => r.harness === host)?.effort;
|
|
43265
|
+
return {
|
|
43266
|
+
seatId,
|
|
43267
|
+
host,
|
|
43268
|
+
model,
|
|
43269
|
+
provider,
|
|
43270
|
+
effort,
|
|
43271
|
+
source
|
|
43272
|
+
};
|
|
43273
|
+
}
|
|
41888
43274
|
|
|
41889
43275
|
// plugins/kxm/src/workflow-manager.ts
|
|
41890
|
-
var
|
|
41891
|
-
import { existsSync as
|
|
41892
|
-
import { join as
|
|
43276
|
+
var import_yaml15 = __toESM(require_dist(), 1);
|
|
43277
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync27, readdirSync as readdirSync10, readFileSync as readFileSync27, rmSync as rmSync9, writeFileSync as writeFileSync25 } from "node:fs";
|
|
43278
|
+
import { join as join35 } from "node:path";
|
|
41893
43279
|
var WORKFLOW_TEMPLATES = {
|
|
41894
43280
|
"implement-and-verify": {
|
|
41895
43281
|
schema: "kxm.workflow.v1",
|
|
@@ -42040,22 +43426,22 @@ var WORKFLOW_TEMPLATES = {
|
|
|
42040
43426
|
var DEFAULT_WORKFLOW_TEMPLATE = WORKFLOW_TEMPLATES["implement-and-verify"];
|
|
42041
43427
|
function workflowsDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
|
|
42042
43428
|
if (scope === "global") {
|
|
42043
|
-
return
|
|
43429
|
+
return join35(userConfigDirectory(userConfigDir), "workflows");
|
|
42044
43430
|
}
|
|
42045
|
-
return
|
|
43431
|
+
return join35(repoConfigDirectory(repoRoot3), "workflows");
|
|
42046
43432
|
}
|
|
42047
43433
|
function ensureWorkflowsDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
|
|
42048
43434
|
const dir = workflowsDirectory(scope, repoRoot3, userConfigDir);
|
|
42049
|
-
if (!
|
|
42050
|
-
|
|
43435
|
+
if (!existsSync29(dir)) {
|
|
43436
|
+
mkdirSync27(dir, { recursive: true });
|
|
42051
43437
|
}
|
|
42052
43438
|
return dir;
|
|
42053
43439
|
}
|
|
42054
43440
|
function parseWorkflowFile(filePath) {
|
|
42055
|
-
if (!
|
|
43441
|
+
if (!existsSync29(filePath)) return void 0;
|
|
42056
43442
|
try {
|
|
42057
|
-
const raw =
|
|
42058
|
-
const parsed = (0,
|
|
43443
|
+
const raw = readFileSync27(filePath, "utf8");
|
|
43444
|
+
const parsed = (0, import_yaml15.parse)(raw);
|
|
42059
43445
|
if (!parsed || typeof parsed !== "object") return void 0;
|
|
42060
43446
|
return parsed;
|
|
42061
43447
|
} catch {
|
|
@@ -42068,10 +43454,10 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
42068
43454
|
const globalDir = workflowsDirectory("global", repoRoot3, options.userConfigDir);
|
|
42069
43455
|
const localDir = workflowsDirectory("local", repoRoot3, options.userConfigDir);
|
|
42070
43456
|
const localDefs = /* @__PURE__ */ new Map();
|
|
42071
|
-
if (scopeFilter !== "global" &&
|
|
43457
|
+
if (scopeFilter !== "global" && existsSync29(localDir)) {
|
|
42072
43458
|
for (const entry of readdirSync10(localDir)) {
|
|
42073
43459
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
42074
|
-
const filePath =
|
|
43460
|
+
const filePath = join35(localDir, entry);
|
|
42075
43461
|
const def = parseWorkflowFile(filePath);
|
|
42076
43462
|
if (def) {
|
|
42077
43463
|
const id = def.id || entry.replace(/\.ya?ml$/i, "");
|
|
@@ -42081,10 +43467,10 @@ function listWorkflowDefinitions(options = {}) {
|
|
|
42081
43467
|
}
|
|
42082
43468
|
}
|
|
42083
43469
|
const globalDefs = /* @__PURE__ */ new Map();
|
|
42084
|
-
if (scopeFilter !== "local" &&
|
|
43470
|
+
if (scopeFilter !== "local" && existsSync29(globalDir)) {
|
|
42085
43471
|
for (const entry of readdirSync10(globalDir)) {
|
|
42086
43472
|
if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
|
|
42087
|
-
const filePath =
|
|
43473
|
+
const filePath = join35(globalDir, entry);
|
|
42088
43474
|
const def = parseWorkflowFile(filePath);
|
|
42089
43475
|
if (def) {
|
|
42090
43476
|
const id = def.id || entry.replace(/\.ya?ml$/i, "");
|
|
@@ -42140,13 +43526,13 @@ function getWorkflowDefinition(workflowId, options = {}) {
|
|
|
42140
43526
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
42141
43527
|
if (scope !== "global") {
|
|
42142
43528
|
const localDir = workflowsDirectory("local", repoRoot3, options.userConfigDir);
|
|
42143
|
-
const localFile =
|
|
43529
|
+
const localFile = join35(localDir, `${workflowId}.yaml`);
|
|
42144
43530
|
const parsed = parseWorkflowFile(localFile);
|
|
42145
43531
|
if (parsed) return { workflow: parsed, scope: "local", filePath: localFile };
|
|
42146
43532
|
}
|
|
42147
43533
|
if (scope !== "local") {
|
|
42148
43534
|
const globalDir = workflowsDirectory("global", repoRoot3, options.userConfigDir);
|
|
42149
|
-
const globalFile =
|
|
43535
|
+
const globalFile = join35(globalDir, `${workflowId}.yaml`);
|
|
42150
43536
|
const parsed = parseWorkflowFile(globalFile);
|
|
42151
43537
|
if (parsed) return { workflow: parsed, scope: "global", filePath: globalFile };
|
|
42152
43538
|
}
|
|
@@ -42156,20 +43542,20 @@ function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE,
|
|
|
42156
43542
|
const scope = options.scope ?? "local";
|
|
42157
43543
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
42158
43544
|
const dir = ensureWorkflowsDirectory(scope, repoRoot3, options.userConfigDir);
|
|
42159
|
-
const filePath =
|
|
42160
|
-
if (
|
|
43545
|
+
const filePath = join35(dir, `${workflowId}.yaml`);
|
|
43546
|
+
if (existsSync29(filePath) && !options.overwrite) {
|
|
42161
43547
|
throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
|
|
42162
43548
|
}
|
|
42163
|
-
const payload = typeof content === "string" ? content : (0,
|
|
42164
|
-
|
|
43549
|
+
const payload = typeof content === "string" ? content : (0, import_yaml15.stringify)(content);
|
|
43550
|
+
writeFileSync25(filePath, payload, "utf8");
|
|
42165
43551
|
return { id: workflowId, filePath, scope };
|
|
42166
43552
|
}
|
|
42167
43553
|
function removeWorkflowDefinition(workflowId, options = {}) {
|
|
42168
43554
|
const scope = options.scope ?? "local";
|
|
42169
43555
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
42170
43556
|
const dir = workflowsDirectory(scope, repoRoot3, options.userConfigDir);
|
|
42171
|
-
const filePath =
|
|
42172
|
-
if (!
|
|
43557
|
+
const filePath = join35(dir, `${workflowId}.yaml`);
|
|
43558
|
+
if (!existsSync29(filePath)) {
|
|
42173
43559
|
throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
|
|
42174
43560
|
}
|
|
42175
43561
|
rmSync9(filePath);
|
|
@@ -42187,14 +43573,14 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
|
|
|
42187
43573
|
const scope = options.scope ?? target.scope;
|
|
42188
43574
|
const repoRoot3 = options.repoRoot ?? process.cwd();
|
|
42189
43575
|
const dir = ensureWorkflowsDirectory(scope, repoRoot3, options.userConfigDir);
|
|
42190
|
-
const filePath =
|
|
42191
|
-
|
|
43576
|
+
const filePath = join35(dir, `${workflowId}.yaml`);
|
|
43577
|
+
writeFileSync25(filePath, (0, import_yaml15.stringify)(updated), "utf8");
|
|
42192
43578
|
return { id: workflowId, workflow: updated, filePath, scope };
|
|
42193
43579
|
}
|
|
42194
43580
|
|
|
42195
43581
|
// plugins/kxm/src/cli.ts
|
|
42196
43582
|
var CLI_NAME = "kxm";
|
|
42197
|
-
var repoRoot2 =
|
|
43583
|
+
var repoRoot2 = resolve22(fileURLToPath4(new URL("../../../", import.meta.url)));
|
|
42198
43584
|
var USAGE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
42199
43585
|
"commander.help",
|
|
42200
43586
|
"commander.helpDisplayed",
|
|
@@ -42210,7 +43596,7 @@ var USAGE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
42210
43596
|
]);
|
|
42211
43597
|
function spawnScript(scriptName, extraEnv = {}) {
|
|
42212
43598
|
return new Promise((resolveExit) => {
|
|
42213
|
-
const child = spawn2(process.execPath, [
|
|
43599
|
+
const child = spawn2(process.execPath, [join36(repoRoot2, "scripts", scriptName)], {
|
|
42214
43600
|
stdio: "inherit",
|
|
42215
43601
|
env: { ...process.env, ...extraEnv }
|
|
42216
43602
|
});
|
|
@@ -42294,16 +43680,16 @@ function redactCliValue(value, field = "") {
|
|
|
42294
43680
|
return value;
|
|
42295
43681
|
}
|
|
42296
43682
|
function workspaceDirs(cwd, workspaceFlag, env) {
|
|
42297
|
-
const workdir =
|
|
42298
|
-
const workspace =
|
|
43683
|
+
const workdir = resolve22(env.KXM_WORKDIR?.trim() || cwd);
|
|
43684
|
+
const workspace = resolve22(workdir, workspaceFlag || env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
|
|
42299
43685
|
const derive = workspaceFlag !== void 0;
|
|
42300
43686
|
return {
|
|
42301
43687
|
workdir,
|
|
42302
43688
|
workspace,
|
|
42303
|
-
config: derive ?
|
|
42304
|
-
logs: derive ?
|
|
42305
|
-
assets: derive ?
|
|
42306
|
-
state: derive ?
|
|
43689
|
+
config: derive ? join36(workspace, "config") : resolve22(workdir, env.KXM_CONFIG_DIR?.trim() || join36(workspace, "config")),
|
|
43690
|
+
logs: derive ? join36(workspace, "logs") : resolve22(workdir, env.KXM_LOGS_DIR?.trim() || join36(workspace, "logs")),
|
|
43691
|
+
assets: derive ? join36(workspace, "assets") : resolve22(workdir, env.KXM_ASSETS_DIR?.trim() || join36(workspace, "assets")),
|
|
43692
|
+
state: derive ? join36(workspace, "state") : resolve22(workdir, env.KXM_STATE_DIR?.trim() || join36(workspace, "state"))
|
|
42307
43693
|
};
|
|
42308
43694
|
}
|
|
42309
43695
|
function maskEnvName(name) {
|
|
@@ -42363,8 +43749,8 @@ async function hubGet(url, fetchImpl) {
|
|
|
42363
43749
|
}
|
|
42364
43750
|
}
|
|
42365
43751
|
function localWorkflowSnapshot(dataPath, runId) {
|
|
42366
|
-
if (!
|
|
42367
|
-
const database = new
|
|
43752
|
+
if (!existsSync30(dataPath)) throw new Error("state_database_not_found");
|
|
43753
|
+
const database = new DatabaseSync(dataPath, { readOnly: true });
|
|
42368
43754
|
try {
|
|
42369
43755
|
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();
|
|
42370
43756
|
const runs = rows.map((row) => JSON.parse(row.record));
|
|
@@ -42469,7 +43855,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
|
|
|
42469
43855
|
let raw;
|
|
42470
43856
|
if (file) {
|
|
42471
43857
|
try {
|
|
42472
|
-
raw =
|
|
43858
|
+
raw = readFileSync28(resolve22(runtime.cwd, file), "utf8");
|
|
42473
43859
|
} catch {
|
|
42474
43860
|
throw new Error("workflow definition file is unavailable");
|
|
42475
43861
|
}
|
|
@@ -42565,9 +43951,13 @@ async function cmdVnextInit(runtime, options) {
|
|
|
42565
43951
|
return code;
|
|
42566
43952
|
};
|
|
42567
43953
|
if (initialized.action === "created") {
|
|
43954
|
+
await maybeOfferCompletionInstall(runtime);
|
|
43955
|
+
await maybeOfferGuideSetup(runtime);
|
|
42568
43956
|
return finishInit(0, `initialized vNext project at ${initialized.projectRoot ?? runtime.cwd}`);
|
|
42569
43957
|
}
|
|
42570
43958
|
if (initialized.action === "joined") {
|
|
43959
|
+
await maybeOfferCompletionInstall(runtime);
|
|
43960
|
+
await maybeOfferGuideSetup(runtime);
|
|
42571
43961
|
return finishInit(0, `joined vNext project at ${initialized.projectRoot ?? runtime.cwd}`);
|
|
42572
43962
|
}
|
|
42573
43963
|
if (initialized.action === "repaired") {
|
|
@@ -42736,7 +44126,7 @@ async function cmdBackup(runtime, options) {
|
|
|
42736
44126
|
try {
|
|
42737
44127
|
const { manifest, outDir } = createBackup({
|
|
42738
44128
|
projectRoot: runtime.cwd,
|
|
42739
|
-
...options.out ? { outDir:
|
|
44129
|
+
...options.out ? { outDir: resolve22(runtime.cwd, options.out) } : {}
|
|
42740
44130
|
});
|
|
42741
44131
|
const payload = {
|
|
42742
44132
|
ok: true,
|
|
@@ -42748,7 +44138,7 @@ async function cmdBackup(runtime, options) {
|
|
|
42748
44138
|
const summary = [
|
|
42749
44139
|
`Created SQLite backup with ${manifest.stores.length} store(s):`,
|
|
42750
44140
|
...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
|
|
42751
|
-
`Manifest: ${
|
|
44141
|
+
`Manifest: ${join36(outDir, "manifest.json")}`
|
|
42752
44142
|
].join("\n");
|
|
42753
44143
|
print(runtime.io, runtime.json, payload, summary);
|
|
42754
44144
|
return 0;
|
|
@@ -42763,7 +44153,7 @@ async function cmdBackup(runtime, options) {
|
|
|
42763
44153
|
}
|
|
42764
44154
|
async function cmdRestore(runtime, manifestArg) {
|
|
42765
44155
|
try {
|
|
42766
|
-
const result = restoreBackup(
|
|
44156
|
+
const result = restoreBackup(resolve22(runtime.cwd, manifestArg), {
|
|
42767
44157
|
projectRoot: runtime.cwd
|
|
42768
44158
|
});
|
|
42769
44159
|
const payload = {
|
|
@@ -43008,9 +44398,9 @@ async function cmdHarnessList(runtime) {
|
|
|
43008
44398
|
function installProbeFrom(runtime) {
|
|
43009
44399
|
const partial = runtime.io.installProbe ?? {};
|
|
43010
44400
|
return {
|
|
43011
|
-
moduleDir: partial.moduleDir ??
|
|
44401
|
+
moduleDir: partial.moduleDir ?? dirname21(fileURLToPath4(import.meta.url)),
|
|
43012
44402
|
repoRoot: partial.repoRoot ?? repoRoot2,
|
|
43013
|
-
homeDir: partial.homeDir ??
|
|
44403
|
+
homeDir: partial.homeDir ?? homedir7(),
|
|
43014
44404
|
platform: partial.platform ?? process.platform,
|
|
43015
44405
|
env: partial.env ?? runtime.env
|
|
43016
44406
|
};
|
|
@@ -43039,9 +44429,9 @@ function npmGlobalRootFn(runtime) {
|
|
|
43039
44429
|
};
|
|
43040
44430
|
}
|
|
43041
44431
|
function warnIgnoredProjectUpdateYaml(runtime) {
|
|
43042
|
-
const projectFile =
|
|
43043
|
-
if (!
|
|
43044
|
-
const userFile =
|
|
44432
|
+
const projectFile = join36(runtime.dirs.workspace, "update.yaml");
|
|
44433
|
+
if (!existsSync30(projectFile)) return;
|
|
44434
|
+
const userFile = join36(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
43045
44435
|
runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
|
|
43046
44436
|
`);
|
|
43047
44437
|
}
|
|
@@ -43070,7 +44460,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
43070
44460
|
detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
|
|
43071
44461
|
};
|
|
43072
44462
|
}
|
|
43073
|
-
const releaseDir = mkdtempSync2(
|
|
44463
|
+
const releaseDir = mkdtempSync2(join36(tmpdir2(), "kxm-pkg-update-"));
|
|
43074
44464
|
try {
|
|
43075
44465
|
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
|
|
43076
44466
|
if (runtime.dryRun) {
|
|
@@ -43079,7 +44469,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
43079
44469
|
for (const step of planned) {
|
|
43080
44470
|
if (step.kind === "verify") {
|
|
43081
44471
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
43082
|
-
const actual =
|
|
44472
|
+
const actual = existsSync30(step.path) ? createHash16("sha256").update(readFileSync28(step.path)).digest("hex") : "missing";
|
|
43083
44473
|
return {
|
|
43084
44474
|
ok: false,
|
|
43085
44475
|
error: "release_digest_mismatch",
|
|
@@ -43105,7 +44495,7 @@ async function selectProducerModel(runtime, requested) {
|
|
|
43105
44495
|
if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
|
|
43106
44496
|
runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
|
|
43107
44497
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
43108
|
-
const answer = await new Promise((
|
|
44498
|
+
const answer = await new Promise((resolve23) => rl.once("line", resolve23));
|
|
43109
44499
|
rl.close();
|
|
43110
44500
|
const index = Number.parseInt(answer.trim(), 10) - 1;
|
|
43111
44501
|
return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
|
|
@@ -43131,7 +44521,7 @@ async function cmdModelsScreen(runtime) {
|
|
|
43131
44521
|
return 2;
|
|
43132
44522
|
}
|
|
43133
44523
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
43134
|
-
const ask = (q2) => new Promise((
|
|
44524
|
+
const ask = (q2) => new Promise((resolve23) => rl.question(q2, resolve23));
|
|
43135
44525
|
try {
|
|
43136
44526
|
while (true) {
|
|
43137
44527
|
const policy = loadProducerPolicy(runtime.dirs.workdir);
|
|
@@ -43373,13 +44763,13 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
43373
44763
|
return 2;
|
|
43374
44764
|
}
|
|
43375
44765
|
const selectedFile = explicitFile || configuredFile;
|
|
43376
|
-
const file = selectedFile ?
|
|
43377
|
-
if (file && !
|
|
44766
|
+
const file = selectedFile ? resolve22(runtime.cwd, selectedFile) : void 0;
|
|
44767
|
+
if (file && !existsSync30(file)) {
|
|
43378
44768
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
43379
44769
|
return 1;
|
|
43380
44770
|
}
|
|
43381
44771
|
try {
|
|
43382
|
-
const raw = file ?
|
|
44772
|
+
const raw = file ? readFileSync28(file, "utf8") : inline;
|
|
43383
44773
|
const warnings = [];
|
|
43384
44774
|
const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
|
|
43385
44775
|
const secretEnvs = definitions.map((definition) => ({
|
|
@@ -43404,7 +44794,7 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
43404
44794
|
}
|
|
43405
44795
|
async function cmdArtifactsExist(runtime, pathFlag) {
|
|
43406
44796
|
const worker = gateOf(runtime, "artifacts-exist");
|
|
43407
|
-
const checked = verifyArtifactExists(runtime.dirs.assets,
|
|
44797
|
+
const checked = verifyArtifactExists(runtime.dirs.assets, resolve22(runtime.cwd, pathFlag));
|
|
43408
44798
|
if (!checked.ok) {
|
|
43409
44799
|
printWorker(
|
|
43410
44800
|
runtime,
|
|
@@ -43436,7 +44826,7 @@ async function cmdDash(runtime, options = {}) {
|
|
|
43436
44826
|
return 2;
|
|
43437
44827
|
}
|
|
43438
44828
|
const screen = requested;
|
|
43439
|
-
const dataPath =
|
|
44829
|
+
const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
|
|
43440
44830
|
if (runtime.dryRun) {
|
|
43441
44831
|
print(runtime.io, runtime.json, {
|
|
43442
44832
|
ok: true,
|
|
@@ -43452,7 +44842,7 @@ async function cmdDash(runtime, options = {}) {
|
|
|
43452
44842
|
runtime.io.stderr("kxm dash does not support --json; use kxm hub view\n");
|
|
43453
44843
|
return 2;
|
|
43454
44844
|
}
|
|
43455
|
-
const project = runtime.env.KXM_PROJECT?.trim() ||
|
|
44845
|
+
const project = runtime.env.KXM_PROJECT?.trim() || basename7(runtime.dirs.workdir) || "project";
|
|
43456
44846
|
const authToken = runtime.env.KXM_AUTH_TOKEN?.trim();
|
|
43457
44847
|
return await runMeshTui({
|
|
43458
44848
|
serverUrl: runtime.serverUrl,
|
|
@@ -43482,7 +44872,7 @@ async function cmdHub(runtime) {
|
|
|
43482
44872
|
config = loadKxmUpdateConfig(runtime.env);
|
|
43483
44873
|
} catch (error) {
|
|
43484
44874
|
if (error instanceof KxmUpdateConfigError) {
|
|
43485
|
-
const yamlPath =
|
|
44875
|
+
const yamlPath = join36(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
43486
44876
|
runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
|
|
43487
44877
|
`);
|
|
43488
44878
|
} else {
|
|
@@ -43618,7 +45008,7 @@ async function cmdWorker(runtime, options) {
|
|
|
43618
45008
|
return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
|
|
43619
45009
|
}
|
|
43620
45010
|
async function cmdStop(runtime, waitMsFlag) {
|
|
43621
|
-
const pids =
|
|
45011
|
+
const pids = existsSync30(runtime.dirs.state) ? readdirSync11(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
|
|
43622
45012
|
if (runtime.dryRun) {
|
|
43623
45013
|
print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
|
|
43624
45014
|
return 0;
|
|
@@ -43630,16 +45020,30 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
43630
45020
|
const requested = [];
|
|
43631
45021
|
const ignored = [];
|
|
43632
45022
|
const records = /* @__PURE__ */ new Map();
|
|
45023
|
+
const orphans = [];
|
|
43633
45024
|
for (const file of pids) {
|
|
43634
45025
|
try {
|
|
43635
|
-
const record = JSON.parse(
|
|
45026
|
+
const record = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
|
|
43636
45027
|
const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
|
|
43637
45028
|
const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
|
|
43638
|
-
if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole
|
|
45029
|
+
if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
|
|
45030
|
+
ignored.push(file);
|
|
45031
|
+
continue;
|
|
45032
|
+
}
|
|
45033
|
+
if (!processExists2(record.pid)) {
|
|
45034
|
+
if (file === "hub.pid" && Number.isInteger(record.serverPid) && record.serverPid > 0 && record.serverPid !== record.pid && processExists2(record.serverPid)) {
|
|
45035
|
+
try {
|
|
45036
|
+
process.kill(record.serverPid, "SIGTERM");
|
|
45037
|
+
} catch {
|
|
45038
|
+
}
|
|
45039
|
+
orphans.push(file);
|
|
45040
|
+
records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
|
|
45041
|
+
continue;
|
|
45042
|
+
}
|
|
43639
45043
|
ignored.push(file);
|
|
43640
45044
|
continue;
|
|
43641
45045
|
}
|
|
43642
|
-
|
|
45046
|
+
writeFileSync26(join36(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
43643
45047
|
`, { encoding: "utf8", mode: 384 });
|
|
43644
45048
|
requested.push(file);
|
|
43645
45049
|
records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
|
|
@@ -43647,7 +45051,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
43647
45051
|
ignored.push(file);
|
|
43648
45052
|
}
|
|
43649
45053
|
}
|
|
43650
|
-
if (requested.length === 0) {
|
|
45054
|
+
if (requested.length === 0 && orphans.length === 0) {
|
|
43651
45055
|
print(runtime.io, runtime.json, { ok: false, command: "stop", requested, ignored }, "no current managed processes found");
|
|
43652
45056
|
return 1;
|
|
43653
45057
|
}
|
|
@@ -43657,7 +45061,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
43657
45061
|
while (Date.now() <= deadline && stopped.size < requested.length) {
|
|
43658
45062
|
for (const [file, record] of records) {
|
|
43659
45063
|
try {
|
|
43660
|
-
const current = JSON.parse(
|
|
45064
|
+
const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
|
|
43661
45065
|
if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists2(record.pid)) stopped.add(file);
|
|
43662
45066
|
} catch {
|
|
43663
45067
|
stopped.add(file);
|
|
@@ -43666,17 +45070,41 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
43666
45070
|
if (stopped.size < requested.length) await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
|
|
43667
45071
|
}
|
|
43668
45072
|
const timedOut = requested.filter((file) => !stopped.has(file));
|
|
45073
|
+
const deadlineOrphans = Date.now() + Math.min(1e4, waitMs);
|
|
45074
|
+
for (const file of orphans) {
|
|
45075
|
+
while (Date.now() <= deadlineOrphans) {
|
|
45076
|
+
try {
|
|
45077
|
+
const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
|
|
45078
|
+
if (!Number.isInteger(current.serverPid) || !processExists2(current.serverPid)) break;
|
|
45079
|
+
} catch {
|
|
45080
|
+
break;
|
|
45081
|
+
}
|
|
45082
|
+
await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
|
|
45083
|
+
}
|
|
45084
|
+
try {
|
|
45085
|
+
const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
|
|
45086
|
+
if (Number.isInteger(current.serverPid) && processExists2(current.serverPid)) {
|
|
45087
|
+
try {
|
|
45088
|
+
process.kill(current.serverPid, "SIGKILL");
|
|
45089
|
+
} catch {
|
|
45090
|
+
}
|
|
45091
|
+
}
|
|
45092
|
+
rmSync10(join36(runtime.dirs.state, file), { force: true });
|
|
45093
|
+
} catch {
|
|
45094
|
+
}
|
|
45095
|
+
stopped.add(file);
|
|
45096
|
+
}
|
|
43669
45097
|
const ok = timedOut.length === 0;
|
|
43670
|
-
print(runtime.io, runtime.json, { ok, command: "stop", requested, stopped: [...stopped], timedOut, ignored }, ok ? "managed processes stopped" : "stop request timed out");
|
|
45098
|
+
print(runtime.io, runtime.json, { ok, command: "stop", requested, stopped: [...stopped], timedOut, ...orphans.length ? { orphans } : {}, ignored }, ok ? "managed processes stopped" : "stop request timed out");
|
|
43671
45099
|
return ok ? 0 : 1;
|
|
43672
45100
|
}
|
|
43673
45101
|
async function cmdSessionStatus(runtime) {
|
|
43674
45102
|
const stateDir = runtime.dirs.state;
|
|
43675
|
-
const names3 =
|
|
45103
|
+
const names3 = existsSync30(stateDir) ? readdirSync11(stateDir) : [];
|
|
43676
45104
|
const claims = [];
|
|
43677
45105
|
for (const file of names3.filter((name) => name.endsWith(".pid"))) {
|
|
43678
45106
|
try {
|
|
43679
|
-
const record = JSON.parse(
|
|
45107
|
+
const record = JSON.parse(readFileSync28(join36(stateDir, file), "utf8"));
|
|
43680
45108
|
claims.push({
|
|
43681
45109
|
file,
|
|
43682
45110
|
role: record.role,
|
|
@@ -43691,7 +45119,7 @@ async function cmdSessionStatus(runtime) {
|
|
|
43691
45119
|
const recoveries = [];
|
|
43692
45120
|
for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
|
|
43693
45121
|
try {
|
|
43694
|
-
const envelope = JSON.parse(
|
|
45122
|
+
const envelope = JSON.parse(readFileSync28(join36(stateDir, file), "utf8"));
|
|
43695
45123
|
recoveries.push({
|
|
43696
45124
|
file,
|
|
43697
45125
|
reason: envelope.reason,
|
|
@@ -43796,7 +45224,7 @@ async function cmdSessionBrief(runtime, options = {}) {
|
|
|
43796
45224
|
print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
|
|
43797
45225
|
return 0;
|
|
43798
45226
|
}
|
|
43799
|
-
const dataPath =
|
|
45227
|
+
const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
|
|
43800
45228
|
const env = {
|
|
43801
45229
|
...runtime.env,
|
|
43802
45230
|
KXM_STATE_DIR: runtime.dirs.state,
|
|
@@ -43835,7 +45263,7 @@ Session token: ${sessionToken}
|
|
|
43835
45263
|
return 0;
|
|
43836
45264
|
}
|
|
43837
45265
|
async function cmdSessionStart(runtime, options) {
|
|
43838
|
-
const id = options.id?.trim() || `session_${
|
|
45266
|
+
const id = options.id?.trim() || `session_${randomUUID12().replaceAll("-", "").slice(0, 12)}`;
|
|
43839
45267
|
const workflowId = options.workflow?.trim();
|
|
43840
45268
|
const mix = options.mix?.trim();
|
|
43841
45269
|
if (workflowId && mix) {
|
|
@@ -43872,7 +45300,7 @@ async function cmdSessionStart(runtime, options) {
|
|
|
43872
45300
|
...workflowId ? workflowAssetDirs(runtime.dirs.assets, workflowId) : []
|
|
43873
45301
|
];
|
|
43874
45302
|
if (!runtime.dryRun) {
|
|
43875
|
-
for (const directory of created)
|
|
45303
|
+
for (const directory of created) mkdirSync28(directory, { recursive: true });
|
|
43876
45304
|
writeSession(runtime.dirs.assets, session);
|
|
43877
45305
|
}
|
|
43878
45306
|
print(runtime.io, runtime.json, {
|
|
@@ -43885,15 +45313,15 @@ async function cmdSessionStart(runtime, options) {
|
|
|
43885
45313
|
return 0;
|
|
43886
45314
|
}
|
|
43887
45315
|
async function cmdImprove(runtime, options = {}) {
|
|
43888
|
-
const file = options.file ?
|
|
43889
|
-
const routingRecords =
|
|
43890
|
-
const candidatesDir = options.outDir ?
|
|
45316
|
+
const file = options.file ? resolve22(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
|
|
45317
|
+
const routingRecords = existsSync30(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
|
|
45318
|
+
const candidatesDir = options.outDir ? resolve22(runtime.cwd, options.outDir) : join36(runtime.cwd, ".kxm", "candidates");
|
|
43891
45319
|
const report2 = buildImprovementReport(routingRecords, {
|
|
43892
45320
|
candidatesDir,
|
|
43893
45321
|
projectRoot: runtime.cwd,
|
|
43894
45322
|
dryRun: runtime.dryRun
|
|
43895
45323
|
});
|
|
43896
|
-
const reportDir =
|
|
45324
|
+
const reportDir = join36(runtime.dirs.assets, "improvements");
|
|
43897
45325
|
const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
|
|
43898
45326
|
const text = formatImprovementReport(report2);
|
|
43899
45327
|
print(runtime.io, runtime.json, {
|
|
@@ -44052,7 +45480,7 @@ function skillStateFromFlag(value) {
|
|
|
44052
45480
|
throw new Error(`invalid skill state ${value}`);
|
|
44053
45481
|
}
|
|
44054
45482
|
function skillsRoot(runtime) {
|
|
44055
|
-
return
|
|
45483
|
+
return join36(runtime.dirs.workdir, ".kxm", "skills");
|
|
44056
45484
|
}
|
|
44057
45485
|
function csv(value) {
|
|
44058
45486
|
if (!value) return void 0;
|
|
@@ -44069,7 +45497,7 @@ async function cmdSkillsCreate(runtime, options) {
|
|
|
44069
45497
|
const metadata = lifecycle.create({
|
|
44070
45498
|
name: options.name,
|
|
44071
45499
|
description: options.description ?? "",
|
|
44072
|
-
content:
|
|
45500
|
+
content: readFileSync28(options.file, "utf8"),
|
|
44073
45501
|
createdBy: options.createdBy,
|
|
44074
45502
|
sources: {
|
|
44075
45503
|
runIds: csv(options.run) ?? [],
|
|
@@ -44295,6 +45723,177 @@ async function cmdCompletion(runtime, shell) {
|
|
|
44295
45723
|
return 1;
|
|
44296
45724
|
}
|
|
44297
45725
|
}
|
|
45726
|
+
async function cmdCompletionInstall(runtime, options) {
|
|
45727
|
+
const installOptions = {
|
|
45728
|
+
env: runtime.env,
|
|
45729
|
+
configDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
45730
|
+
platform: process.platform,
|
|
45731
|
+
dryRun: runtime.dryRun
|
|
45732
|
+
};
|
|
45733
|
+
const report2 = installShellCompletion(options.shell, installOptions);
|
|
45734
|
+
const pathReport = options.path === false ? void 0 : installPathEntry(options.shell, installOptions);
|
|
45735
|
+
if (!report2.ok) {
|
|
45736
|
+
print(runtime.io, runtime.json, {
|
|
45737
|
+
ok: false,
|
|
45738
|
+
command: "completion install",
|
|
45739
|
+
error: report2.reason ?? "shell_not_detected"
|
|
45740
|
+
}, "could not detect your shell; pass --shell bash, zsh, or fish");
|
|
45741
|
+
return 1;
|
|
45742
|
+
}
|
|
45743
|
+
const shellNote = report2.shell;
|
|
45744
|
+
const applyNote = runtime.dryRun ? "planned; rerun without --dry-run to apply" : report2.alreadyInstalled ? "already installed" : "installed; restart your shell or open a new terminal to activate";
|
|
45745
|
+
const pathNote = pathReport ? pathReport.ok ? runtime.dryRun ? `; PATH entry for ${pathReport.binDir} planned` : pathReport.alreadyInstalled ? "; kxm already on PATH" : `; PATH entry for ${pathReport.binDir} added to ${pathReport.rcFile}` : void 0 : void 0;
|
|
45746
|
+
print(runtime.io, runtime.json, {
|
|
45747
|
+
ok: true,
|
|
45748
|
+
command: "completion install",
|
|
45749
|
+
shell: shellNote,
|
|
45750
|
+
scriptPath: report2.scriptPath,
|
|
45751
|
+
...report2.rcFile ? { rcFile: report2.rcFile } : {},
|
|
45752
|
+
rcModified: report2.rcModified,
|
|
45753
|
+
alreadyInstalled: report2.alreadyInstalled,
|
|
45754
|
+
...pathReport ? { path: pathReport } : {},
|
|
45755
|
+
dryRun: runtime.dryRun === true
|
|
45756
|
+
}, `kxm ${shellNote} completion: ${applyNote}${report2.rcFile ? ` (rc: ${report2.rcFile})` : ""}${pathNote ?? ""}`);
|
|
45757
|
+
return 0;
|
|
45758
|
+
}
|
|
45759
|
+
async function maybeOfferCompletionInstall(runtime) {
|
|
45760
|
+
if (runtime.json || runtime.dryRun) return;
|
|
45761
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
45762
|
+
if (runtime.env.KXM_SKIP_COMPLETION_PROMPT?.trim()) return;
|
|
45763
|
+
const shell = detectShell(runtime.env, process.platform);
|
|
45764
|
+
if (shell !== "bash" && shell !== "zsh" && shell !== "fish") return;
|
|
45765
|
+
const binDir = kxmBinDir(runtime.env);
|
|
45766
|
+
const pathNeeded = shell !== "fish" && binDir && !(runtime.env.PATH ?? "").split(":").includes(binDir);
|
|
45767
|
+
if (!pathNeeded) {
|
|
45768
|
+
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
45769
|
+
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
45770
|
+
if (rcFile && existsSync30(rcFile)) {
|
|
45771
|
+
try {
|
|
45772
|
+
if (readFileSync28(rcFile, "utf8").includes(scriptPath)) return;
|
|
45773
|
+
} catch {
|
|
45774
|
+
}
|
|
45775
|
+
}
|
|
45776
|
+
}
|
|
45777
|
+
const question = pathNeeded ? `
|
|
45778
|
+
Install ${shell} tab completion for kxm and add ${binDir} to PATH? [Y/n] ` : `
|
|
45779
|
+
Install ${shell} tab completion for kxm? [y/N] `;
|
|
45780
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
45781
|
+
await new Promise((resolvePrompt) => {
|
|
45782
|
+
rl.question(question, (answer) => {
|
|
45783
|
+
rl.close();
|
|
45784
|
+
const trimmed = answer.trim().toLowerCase();
|
|
45785
|
+
const accept = pathNeeded ? trimmed !== "n" && trimmed !== "no" : trimmed === "y" || trimmed === "yes";
|
|
45786
|
+
if (accept) {
|
|
45787
|
+
try {
|
|
45788
|
+
const report2 = installShellCompletion(shell, {
|
|
45789
|
+
env: runtime.env,
|
|
45790
|
+
configDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
45791
|
+
platform: process.platform
|
|
45792
|
+
});
|
|
45793
|
+
if (report2.ok) {
|
|
45794
|
+
runtime.io.stdout(`completion installed for ${report2.shell}`);
|
|
45795
|
+
} else {
|
|
45796
|
+
runtime.io.stdout(`completion install skipped: ${report2.reason ?? "unknown"}
|
|
45797
|
+
`);
|
|
45798
|
+
resolvePrompt();
|
|
45799
|
+
return;
|
|
45800
|
+
}
|
|
45801
|
+
} catch {
|
|
45802
|
+
runtime.io.stdout("completion install skipped: local filesystem operation did not complete\n");
|
|
45803
|
+
resolvePrompt();
|
|
45804
|
+
return;
|
|
45805
|
+
}
|
|
45806
|
+
if (pathNeeded && binDir) {
|
|
45807
|
+
try {
|
|
45808
|
+
const pathReport = installPathEntry(shell, {
|
|
45809
|
+
env: runtime.env,
|
|
45810
|
+
configDir: runtime.env.KXM_USER_CONFIG_DIR,
|
|
45811
|
+
platform: process.platform,
|
|
45812
|
+
binDir
|
|
45813
|
+
});
|
|
45814
|
+
runtime.io.stdout(pathReport.ok && pathReport.rcModified ? `; ${binDir} added to PATH in ${pathReport.rcFile}` : "; kxm already on PATH");
|
|
45815
|
+
} catch {
|
|
45816
|
+
runtime.io.stdout("; PATH entry skipped: local filesystem operation did not complete");
|
|
45817
|
+
}
|
|
45818
|
+
}
|
|
45819
|
+
runtime.io.stdout("; restart your shell or open a new terminal to activate\n");
|
|
45820
|
+
} else {
|
|
45821
|
+
runtime.io.stdout("skipped; run `kxm completion install` anytime\n");
|
|
45822
|
+
}
|
|
45823
|
+
resolvePrompt();
|
|
45824
|
+
});
|
|
45825
|
+
});
|
|
45826
|
+
}
|
|
45827
|
+
var GUIDE_SETUP_OPT_OUT_ENV = "KXM_SKIP_GUIDE_SETUP_PROMPT";
|
|
45828
|
+
async function askYesNo(question) {
|
|
45829
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
45830
|
+
return new Promise((resolvePrompt) => {
|
|
45831
|
+
rl.question(question, (answer) => {
|
|
45832
|
+
rl.close();
|
|
45833
|
+
const trimmed = answer.trim().toLowerCase();
|
|
45834
|
+
resolvePrompt(trimmed === "y" || trimmed === "yes");
|
|
45835
|
+
});
|
|
45836
|
+
});
|
|
45837
|
+
}
|
|
45838
|
+
async function askLine(question) {
|
|
45839
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
45840
|
+
return new Promise((resolvePrompt) => {
|
|
45841
|
+
rl.question(question, (answer) => {
|
|
45842
|
+
rl.close();
|
|
45843
|
+
resolvePrompt(answer);
|
|
45844
|
+
});
|
|
45845
|
+
});
|
|
45846
|
+
}
|
|
45847
|
+
async function maybeOfferGuideSetup(runtime) {
|
|
45848
|
+
if (runtime.json || runtime.dryRun) return;
|
|
45849
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
45850
|
+
if (runtime.env[GUIDE_SETUP_OPT_OUT_ENV]?.trim()) return;
|
|
45851
|
+
let inventory;
|
|
45852
|
+
try {
|
|
45853
|
+
inventory = probeHarnesses({ env: runtime.env });
|
|
45854
|
+
} catch {
|
|
45855
|
+
return;
|
|
45856
|
+
}
|
|
45857
|
+
const authenticated = inventory.harnesses.filter((entry) => entry.detected && entry.authenticated === true);
|
|
45858
|
+
if (authenticated.length === 0) {
|
|
45859
|
+
runtime.io.stdout("no authenticated harnesses detected; skipping workflow-guide setup (see `kxm harness list`)\n");
|
|
45860
|
+
return;
|
|
45861
|
+
}
|
|
45862
|
+
const harnessList = authenticated.map((entry) => entry.id).join(", ");
|
|
45863
|
+
const accept = await askYesNo(`
|
|
45864
|
+
Set up workflow-guide agents and workflows for authenticated harnesses (${harnessList})? [y/N] `);
|
|
45865
|
+
if (!accept) {
|
|
45866
|
+
runtime.io.stdout(`skipped; set ${GUIDE_SETUP_OPT_OUT_ENV}=1 to suppress this offer, or re-run on a fresh project
|
|
45867
|
+
`);
|
|
45868
|
+
return;
|
|
45869
|
+
}
|
|
45870
|
+
const lines = ["", "Workflow-guide software-engineering workflows (docs/workflow-guide.md):"];
|
|
45871
|
+
GUIDE_WORKFLOWS.forEach((workflow, index) => {
|
|
45872
|
+
lines.push(` ${index + 1}) ${workflow.slug.padEnd(32)} ${workflow.summary}`);
|
|
45873
|
+
});
|
|
45874
|
+
runtime.io.stdout(`${lines.join("\n")}
|
|
45875
|
+
`);
|
|
45876
|
+
const answer = await askLine("Install which workflows? (numbers or slugs, comma-separated, 'all', or 'none'): ");
|
|
45877
|
+
const selected = parseGuideSelection(answer);
|
|
45878
|
+
if (selected.length === 0) {
|
|
45879
|
+
runtime.io.stdout("no workflows selected; nothing written\n");
|
|
45880
|
+
return;
|
|
45881
|
+
}
|
|
45882
|
+
const plan = planGuideSetup({ inventory, selected });
|
|
45883
|
+
const files = renderGuideSetupFiles(runtime.cwd, plan);
|
|
45884
|
+
const report2 = writeGuideSetupFiles(files);
|
|
45885
|
+
for (const file of report2.written) runtime.io.stdout(`wrote ${file}
|
|
45886
|
+
`);
|
|
45887
|
+
for (const file of report2.existed) runtime.io.stdout(`kept existing ${file} (not overwritten)
|
|
45888
|
+
`);
|
|
45889
|
+
for (const skip of plan.skipped) {
|
|
45890
|
+
runtime.io.stdout(`skipped ${skip.workflow}/${skip.role}: ${skip.reason}
|
|
45891
|
+
`);
|
|
45892
|
+
}
|
|
45893
|
+
if (report2.written.length > 0) {
|
|
45894
|
+
runtime.io.stdout("inspect with `kxm workflow definitions`; guide candidates are dated research \u2014 verify before dispatch\n");
|
|
45895
|
+
}
|
|
45896
|
+
}
|
|
44298
45897
|
async function cmdSuggest(runtime, promptParts) {
|
|
44299
45898
|
try {
|
|
44300
45899
|
const prompt = promptParts.join(" ").trim();
|
|
@@ -44474,12 +46073,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
|
|
|
44474
46073
|
const projectRoot = discoverVnextProjectRoot(runtime.cwd) ?? runtime.cwd;
|
|
44475
46074
|
let filePath = workflowPath;
|
|
44476
46075
|
if (!filePath) {
|
|
44477
|
-
filePath =
|
|
46076
|
+
filePath = resolve22(projectRoot, ".kxm", "workflows", "default.yaml");
|
|
44478
46077
|
}
|
|
44479
46078
|
let yamlContent;
|
|
44480
46079
|
let workflowId = "default";
|
|
44481
|
-
if (
|
|
44482
|
-
yamlContent =
|
|
46080
|
+
if (existsSync30(filePath)) {
|
|
46081
|
+
yamlContent = readFileSync28(filePath, "utf8");
|
|
44483
46082
|
} else {
|
|
44484
46083
|
yamlContent = `schema: kxm.workflow.v1
|
|
44485
46084
|
description: Feature implementation workflow
|
|
@@ -44519,7 +46118,7 @@ steps:
|
|
|
44519
46118
|
maxTransitions: 2
|
|
44520
46119
|
`;
|
|
44521
46120
|
}
|
|
44522
|
-
const parsedYaml = (0,
|
|
46121
|
+
const parsedYaml = (0, import_yaml16.parse)(yamlContent);
|
|
44523
46122
|
const plan = compileVnextWorkflow({ id: workflowId, value: parsedYaml });
|
|
44524
46123
|
const layout = generateStudioLayout(plan);
|
|
44525
46124
|
print(runtime.io, runtime.json, { ok: true, command: "studio layout", layout }, JSON.stringify(layout, null, 2));
|
|
@@ -44552,17 +46151,17 @@ async function cmdStudioServe(runtime, options) {
|
|
|
44552
46151
|
sessionToken,
|
|
44553
46152
|
planProvider: () => {
|
|
44554
46153
|
try {
|
|
44555
|
-
const wfDir =
|
|
44556
|
-
const defaultPath =
|
|
44557
|
-
let targetPath =
|
|
44558
|
-
if (!targetPath &&
|
|
46154
|
+
const wfDir = join36(runtime.cwd, ".kxm", "workflows");
|
|
46155
|
+
const defaultPath = join36(wfDir, "default.yaml");
|
|
46156
|
+
let targetPath = existsSync30(defaultPath) ? defaultPath : void 0;
|
|
46157
|
+
if (!targetPath && existsSync30(wfDir)) {
|
|
44559
46158
|
const yml = readdirSync11(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
44560
|
-
if (yml) targetPath =
|
|
46159
|
+
if (yml) targetPath = join36(wfDir, yml);
|
|
44561
46160
|
}
|
|
44562
|
-
if (targetPath &&
|
|
44563
|
-
const raw =
|
|
44564
|
-
const parsed = (0,
|
|
44565
|
-
const wfId =
|
|
46161
|
+
if (targetPath && existsSync30(targetPath)) {
|
|
46162
|
+
const raw = readFileSync28(targetPath, "utf8");
|
|
46163
|
+
const parsed = (0, import_yaml16.parse)(raw);
|
|
46164
|
+
const wfId = basename7(targetPath).replace(/\.(yaml|yml)$/, "");
|
|
44566
46165
|
return compileVnextWorkflow({ id: wfId, value: parsed });
|
|
44567
46166
|
}
|
|
44568
46167
|
} catch {
|
|
@@ -44647,7 +46246,7 @@ async function cmdRoleGet(runtime, roleId, options) {
|
|
|
44647
46246
|
runtime.io,
|
|
44648
46247
|
runtime.json,
|
|
44649
46248
|
{ ok: true, command: "role get", roleId, scope: result.scope, filePath: result.filePath, role: result.role },
|
|
44650
|
-
(0,
|
|
46249
|
+
(0, import_yaml16.stringify)(result.role)
|
|
44651
46250
|
);
|
|
44652
46251
|
return 0;
|
|
44653
46252
|
}
|
|
@@ -44758,9 +46357,9 @@ async function cmdRoleAdd(runtime, roleId, options) {
|
|
|
44758
46357
|
}
|
|
44759
46358
|
let roleDef;
|
|
44760
46359
|
if (options.file) {
|
|
44761
|
-
const filePath =
|
|
44762
|
-
const content =
|
|
44763
|
-
roleDef = (0,
|
|
46360
|
+
const filePath = resolve22(runtime.cwd, options.file);
|
|
46361
|
+
const content = readFileSync28(filePath, "utf8");
|
|
46362
|
+
roleDef = (0, import_yaml16.parse)(content);
|
|
44764
46363
|
roleDef.id = roleId;
|
|
44765
46364
|
} else {
|
|
44766
46365
|
const skills = options.skills ? options.skills.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
@@ -44921,6 +46520,203 @@ async function cmdRoleModify(runtime, roleId, options) {
|
|
|
44921
46520
|
return 1;
|
|
44922
46521
|
}
|
|
44923
46522
|
}
|
|
46523
|
+
async function cmdRoleHosts(runtime, options = {}) {
|
|
46524
|
+
const hostsConfig = loadRoleHostsConfig({
|
|
46525
|
+
scope: options.scope,
|
|
46526
|
+
repoRoot: runtime.cwd,
|
|
46527
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
46528
|
+
});
|
|
46529
|
+
const seatIds = Array.from(
|
|
46530
|
+
/* @__PURE__ */ new Set([
|
|
46531
|
+
...Object.keys(DEFAULT_ROLE_SEATS),
|
|
46532
|
+
...Object.keys(hostsConfig.config.seats ?? {})
|
|
46533
|
+
])
|
|
46534
|
+
).sort();
|
|
46535
|
+
const seats = seatIds.map((seatId) => {
|
|
46536
|
+
const resolved = resolveRoleSeat(seatId, {
|
|
46537
|
+
repoRoot: runtime.cwd,
|
|
46538
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
46539
|
+
});
|
|
46540
|
+
return {
|
|
46541
|
+
seatId,
|
|
46542
|
+
host: resolved.host,
|
|
46543
|
+
model: resolved.model,
|
|
46544
|
+
provider: resolved.provider,
|
|
46545
|
+
effort: resolved.effort,
|
|
46546
|
+
source: resolved.source,
|
|
46547
|
+
configuredHost: hostsConfig.config.seats?.[seatId]?.host,
|
|
46548
|
+
configuredModel: hostsConfig.config.seats?.[seatId]?.model
|
|
46549
|
+
};
|
|
46550
|
+
});
|
|
46551
|
+
if (runtime.json) {
|
|
46552
|
+
print(
|
|
46553
|
+
runtime.io,
|
|
46554
|
+
true,
|
|
46555
|
+
{
|
|
46556
|
+
ok: true,
|
|
46557
|
+
command: "role hosts",
|
|
46558
|
+
scope: hostsConfig.scope,
|
|
46559
|
+
filePath: hostsConfig.filePath,
|
|
46560
|
+
seats,
|
|
46561
|
+
hostProviders: hostsConfig.config.hostProviders ?? {}
|
|
46562
|
+
},
|
|
46563
|
+
""
|
|
46564
|
+
);
|
|
46565
|
+
return 0;
|
|
46566
|
+
}
|
|
46567
|
+
const lines = [
|
|
46568
|
+
`ROLE SEATS (${hostsConfig.scope}${hostsConfig.filePath ? ` at ${hostsConfig.filePath}` : ""}):`
|
|
46569
|
+
];
|
|
46570
|
+
for (const s of seats) {
|
|
46571
|
+
const modelStr = s.model ? ` [${s.model}]` : "";
|
|
46572
|
+
const sourceTag = `(via ${s.source})`;
|
|
46573
|
+
lines.push(` ${s.seatId.padEnd(16)} -> host: ${s.host.padEnd(12)} ${modelStr.padEnd(30)} ${sourceTag}`);
|
|
46574
|
+
}
|
|
46575
|
+
if (hostsConfig.config.hostProviders && Object.keys(hostsConfig.config.hostProviders).length > 0) {
|
|
46576
|
+
lines.push("\nHOST PROVIDERS:");
|
|
46577
|
+
for (const [h, p] of Object.entries(hostsConfig.config.hostProviders)) {
|
|
46578
|
+
lines.push(` ${h.padEnd(16)} -> provider: ${p}`);
|
|
46579
|
+
}
|
|
46580
|
+
}
|
|
46581
|
+
print(runtime.io, false, {}, `${lines.join("\n")}
|
|
46582
|
+
`);
|
|
46583
|
+
return 0;
|
|
46584
|
+
}
|
|
46585
|
+
async function cmdRoleSetHost(runtime, seatId, host, options = {}) {
|
|
46586
|
+
if (!seatId || !host) {
|
|
46587
|
+
runtime.io.stderr("kxm role set-host requires <seatId> and <host>\n");
|
|
46588
|
+
return 1;
|
|
46589
|
+
}
|
|
46590
|
+
try {
|
|
46591
|
+
const result = setRoleSeatHost(seatId, host, {
|
|
46592
|
+
model: options.model,
|
|
46593
|
+
effort: options.effort,
|
|
46594
|
+
scope: options.scope ?? "local",
|
|
46595
|
+
repoRoot: runtime.cwd,
|
|
46596
|
+
userConfigDir: runtime.env.KXM_USER_CONFIG_DIR
|
|
46597
|
+
});
|
|
46598
|
+
print(
|
|
46599
|
+
runtime.io,
|
|
46600
|
+
runtime.json,
|
|
46601
|
+
{
|
|
46602
|
+
ok: true,
|
|
46603
|
+
command: "role set-host",
|
|
46604
|
+
seatId,
|
|
46605
|
+
host,
|
|
46606
|
+
binding: result.binding,
|
|
46607
|
+
filePath: result.filePath,
|
|
46608
|
+
scope: result.scope
|
|
46609
|
+
},
|
|
46610
|
+
`Bound seat '${seatId}' to host '${host}' in ${result.filePath}
|
|
46611
|
+
`
|
|
46612
|
+
);
|
|
46613
|
+
return 0;
|
|
46614
|
+
} catch (err) {
|
|
46615
|
+
runtime.io.stderr(`role set-host failed: ${err.message}
|
|
46616
|
+
`);
|
|
46617
|
+
return 1;
|
|
46618
|
+
}
|
|
46619
|
+
}
|
|
46620
|
+
async function cmdRoleResume(runtime, runId, ruling) {
|
|
46621
|
+
if (!runId) {
|
|
46622
|
+
runtime.io.stderr("kxm role resume requires <runId>\n");
|
|
46623
|
+
return 1;
|
|
46624
|
+
}
|
|
46625
|
+
const effectiveRuling = ruling?.trim() || "operator_ruling: waived and resumed";
|
|
46626
|
+
const projectRoot = discoverVnextProjectRoot(runtime.cwd);
|
|
46627
|
+
if (projectRoot && /^run_[a-f0-9]{32}$/i.test(runId)) {
|
|
46628
|
+
if (runtime.dryRun) {
|
|
46629
|
+
print(runtime.io, runtime.json, { ok: true, command: "role resume", runId, ruling: effectiveRuling }, `would resume vNext run ${runId}`);
|
|
46630
|
+
return 0;
|
|
46631
|
+
}
|
|
46632
|
+
try {
|
|
46633
|
+
const supervisor = await ensureVnextSupervisor({ env: runtime.env });
|
|
46634
|
+
const posted = await vnextRuntimeRequest(
|
|
46635
|
+
supervisor,
|
|
46636
|
+
"POST",
|
|
46637
|
+
`/v1/runs/${encodeURIComponent(runId)}/signal?projectRoot=${encodeURIComponent(projectRoot)}`,
|
|
46638
|
+
{
|
|
46639
|
+
signalKey: "audit_escalation",
|
|
46640
|
+
status: "passed",
|
|
46641
|
+
summary: effectiveRuling,
|
|
46642
|
+
action: "unblock"
|
|
46643
|
+
}
|
|
46644
|
+
);
|
|
46645
|
+
print(
|
|
46646
|
+
runtime.io,
|
|
46647
|
+
runtime.json,
|
|
46648
|
+
{ ok: true, command: "role resume", runId, ruling: effectiveRuling, unblocked: posted.unblocked === true },
|
|
46649
|
+
`Resumed vNext run ${runId} with ruling: ${effectiveRuling}
|
|
46650
|
+
`
|
|
46651
|
+
);
|
|
46652
|
+
return 0;
|
|
46653
|
+
} catch (error) {
|
|
46654
|
+
const msg = error instanceof Error ? error.message : "resume_failed";
|
|
46655
|
+
print(runtime.io, runtime.json, { ok: false, command: "role resume", error: "resume_failed", detail: msg }, `resume vNext run failed: ${msg}
|
|
46656
|
+
`);
|
|
46657
|
+
return 1;
|
|
46658
|
+
}
|
|
46659
|
+
}
|
|
46660
|
+
const dbPath = join36(runtime.cwd, ".kxm", "state", "kxm.db");
|
|
46661
|
+
if (existsSync30(dbPath)) {
|
|
46662
|
+
try {
|
|
46663
|
+
const database = new DatabaseSync(dbPath);
|
|
46664
|
+
try {
|
|
46665
|
+
const row = database.prepare("SELECT record FROM workflow_runs WHERE id = ?").get(runId);
|
|
46666
|
+
if (!row) {
|
|
46667
|
+
runtime.io.stderr(`kxm: workflow run '${runId}' not found in ${dbPath}
|
|
46668
|
+
`);
|
|
46669
|
+
return 1;
|
|
46670
|
+
}
|
|
46671
|
+
const run = JSON.parse(row.record);
|
|
46672
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
46673
|
+
const resumeResult = resumeWorkflowFromRuling(run, effectiveRuling, now);
|
|
46674
|
+
database.prepare("UPDATE workflow_runs SET record = ? WHERE id = ?").run(
|
|
46675
|
+
JSON.stringify(resumeResult.run),
|
|
46676
|
+
runId
|
|
46677
|
+
);
|
|
46678
|
+
const journalId = randomUUID12();
|
|
46679
|
+
const journalPayload = {
|
|
46680
|
+
id: journalId,
|
|
46681
|
+
runId,
|
|
46682
|
+
stageId: resumeResult.stageId,
|
|
46683
|
+
category: "decision",
|
|
46684
|
+
area: "workflow",
|
|
46685
|
+
summary: `Role resume ruling: ${effectiveRuling}`,
|
|
46686
|
+
details: { ruling: effectiveRuling },
|
|
46687
|
+
evidence: {},
|
|
46688
|
+
createdAt: now
|
|
46689
|
+
};
|
|
46690
|
+
database.prepare(
|
|
46691
|
+
"INSERT INTO workflow_journal (id, run_id, category, area, record) VALUES (?, ?, ?, ?, ?)"
|
|
46692
|
+
).run(
|
|
46693
|
+
journalId,
|
|
46694
|
+
runId,
|
|
46695
|
+
"decision",
|
|
46696
|
+
"workflow",
|
|
46697
|
+
JSON.stringify(journalPayload)
|
|
46698
|
+
);
|
|
46699
|
+
print(
|
|
46700
|
+
runtime.io,
|
|
46701
|
+
runtime.json,
|
|
46702
|
+
{ ok: true, command: "role resume", runId, stageId: resumeResult.stageId, ruling: effectiveRuling, status: resumeResult.run.status },
|
|
46703
|
+
`Resumed workflow run ${runId} (stage: ${resumeResult.stageId}) with ruling: ${effectiveRuling}
|
|
46704
|
+
`
|
|
46705
|
+
);
|
|
46706
|
+
return 0;
|
|
46707
|
+
} finally {
|
|
46708
|
+
database.close();
|
|
46709
|
+
}
|
|
46710
|
+
} catch (err) {
|
|
46711
|
+
runtime.io.stderr(`role resume failed: ${err.message}
|
|
46712
|
+
`);
|
|
46713
|
+
return 1;
|
|
46714
|
+
}
|
|
46715
|
+
}
|
|
46716
|
+
runtime.io.stderr(`kxm: no active run store or database found for run '${runId}'
|
|
46717
|
+
`);
|
|
46718
|
+
return 1;
|
|
46719
|
+
}
|
|
44924
46720
|
async function cmdWorkflowDefinitions(runtime, options) {
|
|
44925
46721
|
const workflows = listWorkflowDefinitions({
|
|
44926
46722
|
scope: options.scope,
|
|
@@ -44980,8 +46776,8 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
|
|
|
44980
46776
|
}
|
|
44981
46777
|
}
|
|
44982
46778
|
if (options.file) {
|
|
44983
|
-
const filePath =
|
|
44984
|
-
content =
|
|
46779
|
+
const filePath = resolve22(runtime.cwd, options.file);
|
|
46780
|
+
content = readFileSync28(filePath, "utf8");
|
|
44985
46781
|
} else if (!content) {
|
|
44986
46782
|
content = {
|
|
44987
46783
|
schema: "kxm.workflow.v1",
|
|
@@ -45125,7 +46921,7 @@ async function cmdRoutingReport(runtime, options) {
|
|
|
45125
46921
|
let catalog;
|
|
45126
46922
|
if (includeEquivalentListCost) {
|
|
45127
46923
|
try {
|
|
45128
|
-
const pricesPath = options.prices ?
|
|
46924
|
+
const pricesPath = options.prices ? resolve22(runtime.cwd, options.prices) : join36(runtime.dirs.workspace, "prices.yaml");
|
|
45129
46925
|
catalog = loadPriceCatalog(pricesPath);
|
|
45130
46926
|
} catch {
|
|
45131
46927
|
}
|
|
@@ -45208,7 +47004,7 @@ async function cmdRoutingBenchmark(runtime, options) {
|
|
|
45208
47004
|
}
|
|
45209
47005
|
async function cmdWorkflowStart(runtime, definitionIdArg, options) {
|
|
45210
47006
|
const definitionId = definitionIdArg || runtime.env.KXM_WORKFLOW_ID?.trim();
|
|
45211
|
-
const deliveryId = String(options.deliveryId || `cli-${
|
|
47007
|
+
const deliveryId = String(options.deliveryId || `cli-${randomUUID12()}`);
|
|
45212
47008
|
const event = options.event;
|
|
45213
47009
|
const payloadFlag = options.payload ?? "{}";
|
|
45214
47010
|
if (!definitionId) {
|
|
@@ -45227,7 +47023,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
|
|
|
45227
47023
|
}
|
|
45228
47024
|
let payload;
|
|
45229
47025
|
try {
|
|
45230
|
-
const raw = payloadFlag.startsWith("@") ?
|
|
47026
|
+
const raw = payloadFlag.startsWith("@") ? readFileSync28(resolve22(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
|
|
45231
47027
|
const value = JSON.parse(raw);
|
|
45232
47028
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
|
|
45233
47029
|
payload = value;
|
|
@@ -45294,7 +47090,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
|
|
|
45294
47090
|
}
|
|
45295
47091
|
}
|
|
45296
47092
|
async function cmdWorkflowInspect(runtime, action, runId) {
|
|
45297
|
-
const dataPath =
|
|
47093
|
+
const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
|
|
45298
47094
|
if (action === "get" && !runId) {
|
|
45299
47095
|
runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
|
|
45300
47096
|
`);
|
|
@@ -45339,7 +47135,7 @@ async function cmdSignal(runtime, runId, signalKey, status, summary, evidenceArg
|
|
|
45339
47135
|
printWorker(runtime, worker, { ok: true, command: "signal", runId, signalKey, status, summary, evidence }, "would post signal to vNext run");
|
|
45340
47136
|
return 0;
|
|
45341
47137
|
}
|
|
45342
|
-
const deliveryId2 = String(deliveryIdFlag || `cli-signal:${
|
|
47138
|
+
const deliveryId2 = String(deliveryIdFlag || `cli-signal:${randomUUID12()}`);
|
|
45343
47139
|
try {
|
|
45344
47140
|
const supervisor = await ensureVnextSupervisor({ env: runtime.env });
|
|
45345
47141
|
const posted = await vnextRuntimeRequest(
|
|
@@ -45375,7 +47171,7 @@ async function cmdSignal(runtime, runId, signalKey, status, summary, evidenceArg
|
|
|
45375
47171
|
printWorker(runtime, worker, { ok: true, command: "signal", runId, signalKey, status, summary, evidence }, "would post signed signal");
|
|
45376
47172
|
return 0;
|
|
45377
47173
|
}
|
|
45378
|
-
const deliveryId = String(deliveryIdFlag || `cli-signal:${
|
|
47174
|
+
const deliveryId = String(deliveryIdFlag || `cli-signal:${randomUUID12()}`);
|
|
45379
47175
|
try {
|
|
45380
47176
|
const posted = await postWorkflowSignal({
|
|
45381
47177
|
serverUrl: runtime.serverUrl,
|
|
@@ -45463,14 +47259,14 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
45463
47259
|
return 2;
|
|
45464
47260
|
}
|
|
45465
47261
|
const snapshotFlag = String(options.input || "");
|
|
45466
|
-
const snapshotPath = snapshotFlag ?
|
|
47262
|
+
const snapshotPath = snapshotFlag ? resolve22(runtime.cwd, snapshotFlag) : "";
|
|
45467
47263
|
let snapshot;
|
|
45468
47264
|
try {
|
|
45469
47265
|
if (snapshotPath) {
|
|
45470
|
-
if (!
|
|
45471
|
-
snapshot = JSON.parse(
|
|
47266
|
+
if (!existsSync30(snapshotPath)) throw new Error("snapshot_missing");
|
|
47267
|
+
snapshot = JSON.parse(readFileSync28(snapshotPath, "utf8"));
|
|
45472
47268
|
} else {
|
|
45473
|
-
const dataPath =
|
|
47269
|
+
const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
|
|
45474
47270
|
const local = localWorkflowSnapshot(dataPath, runId);
|
|
45475
47271
|
if (!local.runs[0]) throw new Error("workflow_not_found");
|
|
45476
47272
|
snapshot = { run: local.runs[0], journal: local.journal };
|
|
@@ -45482,8 +47278,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
45482
47278
|
return 1;
|
|
45483
47279
|
}
|
|
45484
47280
|
const doc = buildRetrospective(snapshot.run, snapshot.journal);
|
|
45485
|
-
const outDir =
|
|
45486
|
-
const assetsRoot =
|
|
47281
|
+
const outDir = resolve22(runtime.cwd, String(options.outDir || join36(runtime.dirs.assets, "retrospectives")));
|
|
47282
|
+
const assetsRoot = resolve22(runtime.dirs.assets);
|
|
45487
47283
|
const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
|
|
45488
47284
|
if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
|
|
45489
47285
|
print(runtime.io, runtime.json, { ok: false, command: "retrospective export", error: "output_outside_workspace_assets" }, "retrospectives must stay under the workspace assets directory");
|
|
@@ -45499,7 +47295,7 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
45499
47295
|
}
|
|
45500
47296
|
async function ensureCliClient(runtime) {
|
|
45501
47297
|
const serverUrl = runtime.serverUrl;
|
|
45502
|
-
const project = runtime.env.KXM_PROJECT?.trim() ||
|
|
47298
|
+
const project = runtime.env.KXM_PROJECT?.trim() || basename7(runtime.dirs.workspace || runtime.cwd);
|
|
45503
47299
|
const name = runtime.env.KXM_AGENT_NAME?.trim() || `cli-${process.pid}`;
|
|
45504
47300
|
const purpose = runtime.env.KXM_AGENT_PURPOSE?.trim() || "CLI agent client";
|
|
45505
47301
|
const authToken = runtime.env.KXM_AUTH_TOKEN?.trim();
|
|
@@ -45840,6 +47636,15 @@ function createProgram(ctx, result) {
|
|
|
45840
47636
|
addGlobalOptions(role.command("modify [roleId]").description("Modify an existing role definition")).option("--description <text>", "Updated description").option("--add-skill <skill>", "Skill to add").option("--remove-skill <skill>", "Skill to remove").option("--add-model <harness:model>", "Model to add to roster").option("--remove-model <model>", "Model to remove from roster").option("--scope <scope>", "Configuration scope: global or local").option("--pick [selection]", "Pick a role to modify (index or id)").action(async function roleModifyAction(roleId, options) {
|
|
45841
47637
|
result.code = await cmdRoleModify(runtimeFrom(ctx, this), roleId, options ?? {});
|
|
45842
47638
|
});
|
|
47639
|
+
addGlobalOptions(role.command("hosts").description("List role seats and resolved execution hosts from .kxm/role-hosts.yaml")).option("--scope <scope>", "Filter by scope: all, global, or local", "all").action(async function roleHostsAction(options) {
|
|
47640
|
+
result.code = await cmdRoleHosts(runtimeFrom(ctx, this), options);
|
|
47641
|
+
});
|
|
47642
|
+
addGlobalOptions(role.command("set-host <seatId> <host>").description("Bind a role seat to a host in .kxm/role-hosts.yaml")).option("--model <model>", "Model identifier for this seat").option("--effort <effort>", "Effort level: low, medium, high, xhigh").option("--scope <scope>", "Configuration scope: global or local (default: local)", "local").action(async function roleSetHostAction(seatId, host, options) {
|
|
47643
|
+
result.code = await cmdRoleSetHost(runtimeFrom(ctx, this), seatId, host, options);
|
|
47644
|
+
});
|
|
47645
|
+
addGlobalOptions(role.command("resume <runId> [ruling]").description("Resume an audit-escalated role run with an operator directive")).action(async function roleResumeAction(runId, ruling) {
|
|
47646
|
+
result.code = await cmdRoleResume(runtimeFrom(ctx, this), runId, ruling);
|
|
47647
|
+
});
|
|
45843
47648
|
const gate = addGlobalOptions(program2.command("gate").description("Validate definitions and operate evidence gates"));
|
|
45844
47649
|
gate.helpCommand("help", "Show gate help");
|
|
45845
47650
|
addGlobalOptions(gate.command("validate").description("Parse workflow definitions without printing secrets")).option("--file <path>", "Workflow definition file").action(async function validateAction(options) {
|
|
@@ -45954,8 +47759,21 @@ function createProgram(ctx, result) {
|
|
|
45954
47759
|
addGlobalOptions(configCmd.command("list", { isDefault: true }).description("List resolved configuration values")).action(async function configListAction() {
|
|
45955
47760
|
result.code = await cmdConfigList(runtimeFrom(ctx, this));
|
|
45956
47761
|
});
|
|
45957
|
-
addGlobalOptions(program2.command("completion
|
|
45958
|
-
|
|
47762
|
+
const completionCmd = addGlobalOptions(program2.command("completion [shell]").description("Generate shell completion script, or install it into the current shell")).action(async function completionAction(shell) {
|
|
47763
|
+
if (shell && shell !== "install") {
|
|
47764
|
+
result.code = await cmdCompletion(runtimeFrom(ctx, this), shell);
|
|
47765
|
+
return;
|
|
47766
|
+
}
|
|
47767
|
+
if (shell === "install") {
|
|
47768
|
+
result.code = await cmdCompletionInstall(runtimeFrom(ctx, this), this.opts());
|
|
47769
|
+
return;
|
|
47770
|
+
}
|
|
47771
|
+
ctx.io.stderr("usage: kxm completion <bash|zsh|fish> | kxm completion install [--shell <shell>] [--no-path]\n");
|
|
47772
|
+
result.code = 2;
|
|
47773
|
+
});
|
|
47774
|
+
completionCmd.helpCommand("help", "Show completion help");
|
|
47775
|
+
addGlobalOptions(completionCmd.command("install").description("Install tab completion for the detected or given shell and ensure kxm is on PATH")).option("--shell <shell>", "Shell to install for (bash, zsh, fish; default: detect from $SHELL)").option("--no-path", "Only install completion; do not add a PATH entry").action(async function completionInstallAction(options) {
|
|
47776
|
+
result.code = await cmdCompletionInstall(runtimeFrom(ctx, this), options);
|
|
45959
47777
|
});
|
|
45960
47778
|
addGlobalOptions(program2.command("suggest <prompt...>").description("Recommend workflow, area, roles, and skills from a prompt or issue description")).action(async function suggestAction(promptParts) {
|
|
45961
47779
|
result.code = await cmdSuggest(runtimeFrom(ctx, this), promptParts);
|
|
@@ -46046,7 +47864,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
|
|
|
46046
47864
|
throw error;
|
|
46047
47865
|
}
|
|
46048
47866
|
}
|
|
46049
|
-
if (process.argv[1] &&
|
|
47867
|
+
if (process.argv[1] && resolve22(process.argv[1]) === fileURLToPath4(import.meta.url)) {
|
|
46050
47868
|
const code = await runCli(process.argv.slice(2));
|
|
46051
47869
|
process.exitCode = code;
|
|
46052
47870
|
}
|