@kontextmind/kxm 0.7.53 → 0.7.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +42 -1
- package/docs/agent-skills.md +1 -1
- package/docs/configuration.md +0 -3
- package/docs/contracts/lifecycles.md +16 -0
- package/docs/contracts/migration.md +50 -258
- package/docs/contracts/validation.md +15 -33
- package/docs/test-matrix.md +2 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +376 -2133
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +18 -98
- package/plugins/kxm/dist/runtime.js +50 -114
- package/plugins/kxm/dist/server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +4 -7
- package/plugins/kxm/src/autocomplete.ts +0 -3
- package/plugins/kxm/src/cli/project.ts +2 -133
- package/plugins/kxm/src/cli.ts +3 -24
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/init.ts +10 -6
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/pi-producer.ts +61 -17
- package/plugins/kxm/src/project-config.ts +54 -173
- package/schemas/README.md +0 -3
- package/plugins/kxm/src/migrate.ts +0 -1848
- package/schemas/migration-decision.schema.json +0 -26
- package/schemas/migration-plan.schema.json +0 -123
- package/schemas/migration-receipt.schema.json +0 -52
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -164,10 +164,10 @@ var require_code = __commonJS({
|
|
|
164
164
|
function interpolate(x2) {
|
|
165
165
|
return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2);
|
|
166
166
|
}
|
|
167
|
-
function
|
|
167
|
+
function stringify10(x2) {
|
|
168
168
|
return new _Code(safeStringify(x2));
|
|
169
169
|
}
|
|
170
|
-
exports.stringify =
|
|
170
|
+
exports.stringify = stringify10;
|
|
171
171
|
function safeStringify(x2) {
|
|
172
172
|
return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
173
173
|
}
|
|
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
|
|
|
2987
2987
|
const schOrFunc = root.refs[ref];
|
|
2988
2988
|
if (schOrFunc)
|
|
2989
2989
|
return schOrFunc;
|
|
2990
|
-
let _sch =
|
|
2990
|
+
let _sch = resolve30.call(this, root, ref);
|
|
2991
2991
|
if (_sch === void 0) {
|
|
2992
2992
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2993
2993
|
const { schemaId } = this.opts;
|
|
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
|
|
|
3014
3014
|
function sameSchemaEnv(s1, s2) {
|
|
3015
3015
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3016
3016
|
}
|
|
3017
|
-
function
|
|
3017
|
+
function resolve30(root, ref) {
|
|
3018
3018
|
let sch;
|
|
3019
3019
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3020
3020
|
ref = sch;
|
|
@@ -3839,7 +3839,7 @@ var require_fast_uri = __commonJS({
|
|
|
3839
3839
|
}
|
|
3840
3840
|
return uri;
|
|
3841
3841
|
}
|
|
3842
|
-
function
|
|
3842
|
+
function resolve30(baseURI, relativeURI, options) {
|
|
3843
3843
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3844
3844
|
const {
|
|
3845
3845
|
parsed: baseParsed,
|
|
@@ -4201,7 +4201,7 @@ var require_fast_uri = __commonJS({
|
|
|
4201
4201
|
var fastUri = {
|
|
4202
4202
|
SCHEMES,
|
|
4203
4203
|
normalize,
|
|
4204
|
-
resolve:
|
|
4204
|
+
resolve: resolve30,
|
|
4205
4205
|
resolveComponent,
|
|
4206
4206
|
equal,
|
|
4207
4207
|
serialize,
|
|
@@ -8918,7 +8918,7 @@ var require_stringify = __commonJS({
|
|
|
8918
8918
|
props.push(doc.directives.tagString(tag));
|
|
8919
8919
|
return props.join(" ");
|
|
8920
8920
|
}
|
|
8921
|
-
function
|
|
8921
|
+
function stringify10(item, ctx, onComment, onChompKeep) {
|
|
8922
8922
|
if (identity.isPair(item))
|
|
8923
8923
|
return item.toString(ctx, onComment, onChompKeep);
|
|
8924
8924
|
if (identity.isAlias(item)) {
|
|
@@ -8947,7 +8947,7 @@ var require_stringify = __commonJS({
|
|
|
8947
8947
|
${ctx.indent}${str}`;
|
|
8948
8948
|
}
|
|
8949
8949
|
exports.createStringifyContext = createStringifyContext;
|
|
8950
|
-
exports.stringify =
|
|
8950
|
+
exports.stringify = stringify10;
|
|
8951
8951
|
}
|
|
8952
8952
|
});
|
|
8953
8953
|
|
|
@@ -8957,7 +8957,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8957
8957
|
"use strict";
|
|
8958
8958
|
var identity = require_identity();
|
|
8959
8959
|
var Scalar = require_Scalar();
|
|
8960
|
-
var
|
|
8960
|
+
var stringify10 = require_stringify();
|
|
8961
8961
|
var stringifyComment = require_stringifyComment();
|
|
8962
8962
|
function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
|
|
8963
8963
|
const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
|
|
@@ -8979,7 +8979,7 @@ var require_stringifyPair = __commonJS({
|
|
|
8979
8979
|
});
|
|
8980
8980
|
let keyCommentDone = false;
|
|
8981
8981
|
let chompKeep = false;
|
|
8982
|
-
let str =
|
|
8982
|
+
let str = stringify10.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
8983
8983
|
if (!explicitKey && !ctx.inFlow && str.length > 1024) {
|
|
8984
8984
|
if (simpleKeys)
|
|
8985
8985
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
@@ -9031,7 +9031,7 @@ ${indent}:`;
|
|
|
9031
9031
|
ctx.indent = ctx.indent.substring(2);
|
|
9032
9032
|
}
|
|
9033
9033
|
let valueCommentDone = false;
|
|
9034
|
-
const valueStr =
|
|
9034
|
+
const valueStr = stringify10.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
|
|
9035
9035
|
let ws = " ";
|
|
9036
9036
|
if (keyComment || vsb || vcb) {
|
|
9037
9037
|
ws = vsb ? "\n" : "";
|
|
@@ -9172,7 +9172,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9172
9172
|
"use strict";
|
|
9173
9173
|
var log = require_log();
|
|
9174
9174
|
var merge = require_merge();
|
|
9175
|
-
var
|
|
9175
|
+
var stringify10 = require_stringify();
|
|
9176
9176
|
var identity = require_identity();
|
|
9177
9177
|
var toJS = require_toJS();
|
|
9178
9178
|
function addPairToJSMap(ctx, map, { key, value }) {
|
|
@@ -9208,7 +9208,7 @@ var require_addPairToJSMap = __commonJS({
|
|
|
9208
9208
|
if (typeof jsKey !== "object")
|
|
9209
9209
|
return String(jsKey);
|
|
9210
9210
|
if (identity.isNode(key) && ctx?.doc) {
|
|
9211
|
-
const strCtx =
|
|
9211
|
+
const strCtx = stringify10.createStringifyContext(ctx.doc, {});
|
|
9212
9212
|
strCtx.anchors = /* @__PURE__ */ new Set();
|
|
9213
9213
|
for (const node of ctx.anchors.keys())
|
|
9214
9214
|
strCtx.anchors.add(node.anchor);
|
|
@@ -9275,12 +9275,12 @@ var require_stringifyCollection = __commonJS({
|
|
|
9275
9275
|
"node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
|
|
9276
9276
|
"use strict";
|
|
9277
9277
|
var identity = require_identity();
|
|
9278
|
-
var
|
|
9278
|
+
var stringify10 = require_stringify();
|
|
9279
9279
|
var stringifyComment = require_stringifyComment();
|
|
9280
9280
|
function stringifyCollection(collection, ctx, options) {
|
|
9281
9281
|
const flow = ctx.inFlow ?? collection.flow;
|
|
9282
|
-
const
|
|
9283
|
-
return
|
|
9282
|
+
const stringify11 = flow ? stringifyFlowCollection : stringifyBlockCollection;
|
|
9283
|
+
return stringify11(collection, ctx, options);
|
|
9284
9284
|
}
|
|
9285
9285
|
function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
|
|
9286
9286
|
const { indent, options: { commentString } } = ctx;
|
|
@@ -9305,7 +9305,7 @@ var require_stringifyCollection = __commonJS({
|
|
|
9305
9305
|
}
|
|
9306
9306
|
}
|
|
9307
9307
|
chompKeep = false;
|
|
9308
|
-
let str2 =
|
|
9308
|
+
let str2 = stringify10.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
9309
9309
|
if (comment2)
|
|
9310
9310
|
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
|
|
9311
9311
|
if (chompKeep && comment2)
|
|
@@ -9372,7 +9372,7 @@ ${indent}${line}` : "\n";
|
|
|
9372
9372
|
}
|
|
9373
9373
|
if (comment)
|
|
9374
9374
|
reqNewline = true;
|
|
9375
|
-
let str =
|
|
9375
|
+
let str = stringify10.stringify(item, itemCtx, () => comment = null);
|
|
9376
9376
|
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
9377
9377
|
if (i < items.length - 1) {
|
|
9378
9378
|
str += ",";
|
|
@@ -10733,7 +10733,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10733
10733
|
"node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
|
|
10734
10734
|
"use strict";
|
|
10735
10735
|
var identity = require_identity();
|
|
10736
|
-
var
|
|
10736
|
+
var stringify10 = require_stringify();
|
|
10737
10737
|
var stringifyComment = require_stringifyComment();
|
|
10738
10738
|
function stringifyDocument(doc, options) {
|
|
10739
10739
|
const lines = [];
|
|
@@ -10748,7 +10748,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10748
10748
|
}
|
|
10749
10749
|
if (hasDirectives)
|
|
10750
10750
|
lines.push("---");
|
|
10751
|
-
const ctx =
|
|
10751
|
+
const ctx = stringify10.createStringifyContext(doc, options);
|
|
10752
10752
|
const { commentString } = ctx.options;
|
|
10753
10753
|
if (doc.commentBefore) {
|
|
10754
10754
|
if (lines.length !== 1)
|
|
@@ -10770,7 +10770,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10770
10770
|
contentComment = doc.contents.comment;
|
|
10771
10771
|
}
|
|
10772
10772
|
const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
|
|
10773
|
-
let body =
|
|
10773
|
+
let body = stringify10.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
|
|
10774
10774
|
if (contentComment)
|
|
10775
10775
|
body += stringifyComment.lineComment(body, "", commentString(contentComment));
|
|
10776
10776
|
if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
|
|
@@ -10778,7 +10778,7 @@ var require_stringifyDocument = __commonJS({
|
|
|
10778
10778
|
} else
|
|
10779
10779
|
lines.push(body);
|
|
10780
10780
|
} else {
|
|
10781
|
-
lines.push(
|
|
10781
|
+
lines.push(stringify10.stringify(doc.contents, ctx));
|
|
10782
10782
|
}
|
|
10783
10783
|
if (doc.directives?.docEnd) {
|
|
10784
10784
|
if (doc.comment) {
|
|
@@ -12914,7 +12914,7 @@ var require_cst_scalar = __commonJS({
|
|
|
12914
12914
|
var require_cst_stringify = __commonJS({
|
|
12915
12915
|
"node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
|
|
12916
12916
|
"use strict";
|
|
12917
|
-
var
|
|
12917
|
+
var stringify10 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
|
|
12918
12918
|
function stringifyToken(token) {
|
|
12919
12919
|
switch (token.type) {
|
|
12920
12920
|
case "block-scalar": {
|
|
@@ -12967,7 +12967,7 @@ var require_cst_stringify = __commonJS({
|
|
|
12967
12967
|
res += stringifyToken(value);
|
|
12968
12968
|
return res;
|
|
12969
12969
|
}
|
|
12970
|
-
exports.stringify =
|
|
12970
|
+
exports.stringify = stringify10;
|
|
12971
12971
|
}
|
|
12972
12972
|
});
|
|
12973
12973
|
|
|
@@ -14697,7 +14697,7 @@ var require_public_api = __commonJS({
|
|
|
14697
14697
|
}
|
|
14698
14698
|
return doc.toJS(Object.assign({ reviver: _reviver }, options));
|
|
14699
14699
|
}
|
|
14700
|
-
function
|
|
14700
|
+
function stringify10(value, replacer, options) {
|
|
14701
14701
|
let _replacer = null;
|
|
14702
14702
|
if (typeof replacer === "function" || Array.isArray(replacer)) {
|
|
14703
14703
|
_replacer = replacer;
|
|
@@ -14722,7 +14722,7 @@ var require_public_api = __commonJS({
|
|
|
14722
14722
|
exports.parse = parse12;
|
|
14723
14723
|
exports.parseAllDocuments = parseAllDocuments;
|
|
14724
14724
|
exports.parseDocument = parseDocument2;
|
|
14725
|
-
exports.stringify =
|
|
14725
|
+
exports.stringify = stringify10;
|
|
14726
14726
|
}
|
|
14727
14727
|
});
|
|
14728
14728
|
|
|
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
|
|
|
14779
14779
|
});
|
|
14780
14780
|
|
|
14781
14781
|
// plugins/kxm/src/cli.ts
|
|
14782
|
-
import { resolve as
|
|
14782
|
+
import { resolve as resolve29 } from "node:path";
|
|
14783
14783
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
14784
14784
|
|
|
14785
14785
|
// node_modules/commander/lib/error.js
|
|
@@ -19446,7 +19446,7 @@ var HubClient = class {
|
|
|
19446
19446
|
if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
|
|
19447
19447
|
const message = await this.getMessage(messageId);
|
|
19448
19448
|
if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
|
|
19449
|
-
await new Promise((
|
|
19449
|
+
await new Promise((resolve30, reject) => {
|
|
19450
19450
|
const onAbort = () => {
|
|
19451
19451
|
signal?.removeEventListener("abort", onAbort);
|
|
19452
19452
|
clearTimeout(timer);
|
|
@@ -19454,7 +19454,7 @@ var HubClient = class {
|
|
|
19454
19454
|
};
|
|
19455
19455
|
const timer = setTimeout(() => {
|
|
19456
19456
|
signal?.removeEventListener("abort", onAbort);
|
|
19457
|
-
|
|
19457
|
+
resolve30();
|
|
19458
19458
|
}, Math.min(500, Math.max(1, deadline - Date.now())));
|
|
19459
19459
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19460
19460
|
if (signal?.aborted) onAbort();
|
|
@@ -19509,7 +19509,7 @@ var HubClient = class {
|
|
|
19509
19509
|
} catch (error) {
|
|
19510
19510
|
if (this.stopped || error instanceof Error && error.name === "AbortError") return;
|
|
19511
19511
|
}
|
|
19512
|
-
if (!this.stopped) await new Promise((
|
|
19512
|
+
if (!this.stopped) await new Promise((resolve30) => setTimeout(resolve30, this.options.reconnectMs));
|
|
19513
19513
|
}
|
|
19514
19514
|
}
|
|
19515
19515
|
headers(includeIdentity = true) {
|
|
@@ -20960,7 +20960,7 @@ function defaultSpawn(command, args, options) {
|
|
|
20960
20960
|
if (options.signal?.aborted) {
|
|
20961
20961
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
20962
20962
|
}
|
|
20963
|
-
return new Promise((
|
|
20963
|
+
return new Promise((resolve30) => {
|
|
20964
20964
|
const stdout = [];
|
|
20965
20965
|
const stderr = [];
|
|
20966
20966
|
let outputBytes = 0;
|
|
@@ -20991,7 +20991,7 @@ function defaultSpawn(command, args, options) {
|
|
|
20991
20991
|
const started = Boolean(child?.pid);
|
|
20992
20992
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
20993
20993
|
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
20994
|
-
|
|
20994
|
+
resolve30({
|
|
20995
20995
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
20996
20996
|
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
20997
20997
|
code,
|
|
@@ -22060,9 +22060,6 @@ var KxmSchemaRegistry = class {
|
|
|
22060
22060
|
localBindingsValidator;
|
|
22061
22061
|
templateProvenanceValidator;
|
|
22062
22062
|
initOperationValidator;
|
|
22063
|
-
migrationPlanValidator;
|
|
22064
|
-
migrationDecisionValidator;
|
|
22065
|
-
migrationReceiptValidator;
|
|
22066
22063
|
permissionDiffValidator;
|
|
22067
22064
|
runEventValidator;
|
|
22068
22065
|
driveReceiptValidator;
|
|
@@ -22079,9 +22076,6 @@ var KxmSchemaRegistry = class {
|
|
|
22079
22076
|
const localBindingsFile = "local-repository-bindings.schema.json";
|
|
22080
22077
|
const templateProvenanceFile = "template-provenance.schema.json";
|
|
22081
22078
|
const initOperationFile = "init-operation.schema.json";
|
|
22082
|
-
const migrationPlanFile = "migration-plan.schema.json";
|
|
22083
|
-
const migrationDecisionFile = "migration-decision.schema.json";
|
|
22084
|
-
const migrationReceiptFile = "migration-receipt.schema.json";
|
|
22085
22079
|
const permissionDiffFile = "permission-diff.schema.json";
|
|
22086
22080
|
const runEventFile = "run-event.schema.json";
|
|
22087
22081
|
const driveReceiptFile = "drive-receipt.schema.json";
|
|
@@ -22090,9 +22084,6 @@ var KxmSchemaRegistry = class {
|
|
|
22090
22084
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, localBindingsFile)));
|
|
22091
22085
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, templateProvenanceFile)));
|
|
22092
22086
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, initOperationFile)));
|
|
22093
|
-
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationPlanFile)));
|
|
22094
|
-
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationDecisionFile)));
|
|
22095
|
-
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationReceiptFile)));
|
|
22096
22087
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, permissionDiffFile)));
|
|
22097
22088
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, runEventFile)));
|
|
22098
22089
|
this.ajv.addSchema(readJsonObject(join6(this.schemasDir, driveReceiptFile)));
|
|
@@ -22106,9 +22097,6 @@ var KxmSchemaRegistry = class {
|
|
|
22106
22097
|
const localBindingsValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${localBindingsFile}`);
|
|
22107
22098
|
const templateProvenanceValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${templateProvenanceFile}`);
|
|
22108
22099
|
const initOperationValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${initOperationFile}`);
|
|
22109
|
-
const migrationPlanValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationPlanFile}`);
|
|
22110
|
-
const migrationDecisionValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationDecisionFile}`);
|
|
22111
|
-
const migrationReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationReceiptFile}`);
|
|
22112
22100
|
const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
|
|
22113
22101
|
const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
|
|
22114
22102
|
const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
|
|
@@ -22117,9 +22105,6 @@ var KxmSchemaRegistry = class {
|
|
|
22117
22105
|
if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
|
|
22118
22106
|
if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
|
|
22119
22107
|
if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
|
|
22120
|
-
if (!migrationPlanValidator) throw new Error(`schema did not compile: ${migrationPlanFile}`);
|
|
22121
|
-
if (!migrationDecisionValidator) throw new Error(`schema did not compile: ${migrationDecisionFile}`);
|
|
22122
|
-
if (!migrationReceiptValidator) throw new Error(`schema did not compile: ${migrationReceiptFile}`);
|
|
22123
22108
|
if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
|
|
22124
22109
|
if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
|
|
22125
22110
|
if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
|
|
@@ -22128,9 +22113,6 @@ var KxmSchemaRegistry = class {
|
|
|
22128
22113
|
this.localBindingsValidator = localBindingsValidator;
|
|
22129
22114
|
this.templateProvenanceValidator = templateProvenanceValidator;
|
|
22130
22115
|
this.initOperationValidator = initOperationValidator;
|
|
22131
|
-
this.migrationPlanValidator = migrationPlanValidator;
|
|
22132
|
-
this.migrationDecisionValidator = migrationDecisionValidator;
|
|
22133
|
-
this.migrationReceiptValidator = migrationReceiptValidator;
|
|
22134
22116
|
this.permissionDiffValidator = permissionDiffValidator;
|
|
22135
22117
|
this.runEventValidator = runEventValidator;
|
|
22136
22118
|
this.driveReceiptValidator = driveReceiptValidator;
|
|
@@ -22156,15 +22138,6 @@ var KxmSchemaRegistry = class {
|
|
|
22156
22138
|
validateInitOperation(value, file) {
|
|
22157
22139
|
return this.validateAuxiliary(value, file, "kxm.init-operation.v1", this.initOperationValidator);
|
|
22158
22140
|
}
|
|
22159
|
-
validateMigrationPlan(value, file) {
|
|
22160
|
-
return this.validateAuxiliary(value, file, "kxm.migration-plan.v1", this.migrationPlanValidator);
|
|
22161
|
-
}
|
|
22162
|
-
validateMigrationDecision(value, file) {
|
|
22163
|
-
return this.validateAuxiliary(value, file, "kxm.migration-decision.v1", this.migrationDecisionValidator);
|
|
22164
|
-
}
|
|
22165
|
-
validateMigrationReceipt(value, file) {
|
|
22166
|
-
return this.validateAuxiliary(value, file, "kxm.migration-receipt.v1", this.migrationReceiptValidator);
|
|
22167
|
-
}
|
|
22168
22141
|
validatePermissionDiff(value, file) {
|
|
22169
22142
|
return this.validateAuxiliary(value, file, "kxm.permission-diff.v1", this.permissionDiffValidator);
|
|
22170
22143
|
}
|
|
@@ -22516,35 +22489,6 @@ function validateToolPolicy(resource, policy, label, issues) {
|
|
|
22516
22489
|
if (allowed.has(tool)) issues.push(issue2("semantic", "tool_policy_contradiction", resource.logicalPath, `${label} both allows and denies ${tool}`));
|
|
22517
22490
|
}
|
|
22518
22491
|
}
|
|
22519
|
-
function validateKxmResources(resources, options = {}) {
|
|
22520
|
-
const make = (logicalPath, kind, id, value) => ({
|
|
22521
|
-
kind,
|
|
22522
|
-
...id === void 0 ? {} : { id },
|
|
22523
|
-
file: logicalPath,
|
|
22524
|
-
logicalPath,
|
|
22525
|
-
value
|
|
22526
|
-
});
|
|
22527
|
-
assertNoRegisteredGates(options);
|
|
22528
|
-
let gateRegistry;
|
|
22529
|
-
let project;
|
|
22530
|
-
const repositories = /* @__PURE__ */ new Map();
|
|
22531
|
-
const agents = /* @__PURE__ */ new Map();
|
|
22532
|
-
const models = /* @__PURE__ */ new Map();
|
|
22533
|
-
const workflows = /* @__PURE__ */ new Map();
|
|
22534
|
-
const environments = [];
|
|
22535
|
-
for (const [logicalPath, resource] of resources) {
|
|
22536
|
-
const made = make(logicalPath, resource.kind, resource.id, resource.value);
|
|
22537
|
-
if (resource.kind === "project") project = made;
|
|
22538
|
-
else if (resource.kind === "repository" && resource.id) repositories.set(resource.id, made);
|
|
22539
|
-
else if (resource.kind === "agent" && resource.id) agents.set(resource.id, made);
|
|
22540
|
-
else if (resource.kind === "model" && resource.id) models.set(resource.id, made);
|
|
22541
|
-
else if (resource.kind === "workflow" && resource.id) workflows.set(resource.id, made);
|
|
22542
|
-
else if (resource.kind === "environment") environments.push(made);
|
|
22543
|
-
else if (resource.kind === "gate-registry") gateRegistry = made;
|
|
22544
|
-
}
|
|
22545
|
-
if (!project) return [issue2("discovery", "project_definition_missing", ".kxm/project.yaml", "resource set has no project definition")];
|
|
22546
|
-
return validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry);
|
|
22547
|
-
}
|
|
22548
22492
|
function validateAgentScope(agent, step, repositories, file, stepId, issues) {
|
|
22549
22493
|
validateToolScope(agent, step, file, stepId, issues);
|
|
22550
22494
|
const agentRepositories = objectValue(agent.value.repositories) ?? {};
|
|
@@ -22667,8 +22611,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
22667
22611
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
22668
22612
|
if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
22669
22613
|
}
|
|
22670
|
-
const
|
|
22671
|
-
const minimumPassed =
|
|
22614
|
+
const join49 = objectValue(step.join);
|
|
22615
|
+
const minimumPassed = join49 && typeof join49.minimumPassed === "number" ? join49.minimumPassed : void 0;
|
|
22672
22616
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
22673
22617
|
const distinctBy = names(assignment?.distinctBy);
|
|
22674
22618
|
if (distinctBy.length > 0) {
|
|
@@ -22956,11 +22900,14 @@ function assertNoRegisteredGates(options) {
|
|
|
22956
22900
|
function loadKxmProject(projectRoot, options = {}) {
|
|
22957
22901
|
assertNoRegisteredGates(options);
|
|
22958
22902
|
const root = resolve3(projectRoot);
|
|
22959
|
-
|
|
22960
|
-
if (
|
|
22961
|
-
|
|
22962
|
-
|
|
22963
|
-
|
|
22903
|
+
const legacyPresent = legacyConfigFilesAt(root);
|
|
22904
|
+
if (legacyPresent.length > 0) {
|
|
22905
|
+
throw new KxmConfigError(legacyPresent.map((file) => issue2(
|
|
22906
|
+
"semantic",
|
|
22907
|
+
"legacy_state_unsupported",
|
|
22908
|
+
file,
|
|
22909
|
+
"legacy JSON configuration is present and this build does not migrate it: delete these files once their YAML replacements exist in .kxm/, or initialise a fresh project"
|
|
22910
|
+
)));
|
|
22964
22911
|
}
|
|
22965
22912
|
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
22966
22913
|
const project = readResource(registry, root, join6(root, ".kxm", "project.yaml"), ".kxm/project.yaml", "project");
|
|
@@ -22973,9 +22920,6 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
22973
22920
|
earlyIssues.push(issue2("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
|
|
22974
22921
|
}
|
|
22975
22922
|
}
|
|
22976
|
-
if (migrationReceipt && stringValue(migrationReceipt.projectId) !== stringValue(project.value.id)) {
|
|
22977
|
-
earlyIssues.push(issue2("semantic", "migration_project_mismatch", KXM_MIGRATION_RECEIPT_PATH, "migration receipt belongs to a different project identity"));
|
|
22978
|
-
}
|
|
22979
22923
|
const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate) => stringValue(objectValue(candidate)?.id)).filter((candidate) => candidate !== void 0));
|
|
22980
22924
|
for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits3)) {
|
|
22981
22925
|
if (!resourceIdentifier(repositoryId)) {
|
|
@@ -23052,6 +22996,15 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
23052
22996
|
continue;
|
|
23053
22997
|
}
|
|
23054
22998
|
}
|
|
22999
|
+
const memberLegacy = binding === root ? [] : legacyConfigFilesAt(binding);
|
|
23000
|
+
if (memberLegacy.length > 0) {
|
|
23001
|
+
throw new KxmConfigError(memberLegacy.map((file) => issue2(
|
|
23002
|
+
"semantic",
|
|
23003
|
+
"legacy_state_unsupported",
|
|
23004
|
+
`${repositoryId}/${file}`,
|
|
23005
|
+
`repository ${repositoryId} holds legacy JSON configuration and this build does not migrate it: delete those files once their YAML replacements exist, or bind a clean worktree`
|
|
23006
|
+
)));
|
|
23007
|
+
}
|
|
23055
23008
|
const foldedBinding = canonicalHostPath(binding).toLocaleLowerCase("en-US");
|
|
23056
23009
|
const priorBinding = seenBindings.get(foldedBinding);
|
|
23057
23010
|
if (priorBinding && priorBinding !== repositoryId) {
|
|
@@ -23105,50 +23058,10 @@ function loadKxmProject(projectRoot, options = {}) {
|
|
|
23105
23058
|
environments,
|
|
23106
23059
|
...gateRegistry ? { gateRegistry } : {},
|
|
23107
23060
|
...templateProvenance === void 0 ? {} : { templateProvenance },
|
|
23108
|
-
...migrationReceipt === void 0 ? {} : { migrationReceipt },
|
|
23109
23061
|
resources,
|
|
23110
23062
|
configRevision: bundleRevision(resources)
|
|
23111
23063
|
};
|
|
23112
23064
|
}
|
|
23113
|
-
function verifyKxmMigrationReceiptTarget(root, receipt, resources, configRevision) {
|
|
23114
|
-
const issues = [];
|
|
23115
|
-
if (receipt.configRevision !== configRevision) {
|
|
23116
|
-
issues.push(issue2("semantic", "migration_target_changed", KXM_MIGRATION_RECEIPT_PATH, "target configuration revision no longer matches the migration receipt"));
|
|
23117
|
-
}
|
|
23118
|
-
const declared = /* @__PURE__ */ new Map();
|
|
23119
|
-
for (const candidate of Array.isArray(receipt.resources) ? receipt.resources : []) {
|
|
23120
|
-
if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
|
|
23121
|
-
const record = candidate;
|
|
23122
|
-
if (typeof record.path === "string") declared.set(record.path, record);
|
|
23123
|
-
}
|
|
23124
|
-
}
|
|
23125
|
-
const installedPaths = /* @__PURE__ */ new Set();
|
|
23126
|
-
for (const resource of resources) {
|
|
23127
|
-
const relative7 = resource.file.startsWith(root) ? relativePortable(root, resource.file) : void 0;
|
|
23128
|
-
if (!relative7) continue;
|
|
23129
|
-
installedPaths.add(relative7);
|
|
23130
|
-
const record = declared.get(relative7);
|
|
23131
|
-
if (!record) {
|
|
23132
|
-
issues.push(issue2("semantic", "migration_target_unreceipted", relative7, "installed resource is not covered by the migration receipt"));
|
|
23133
|
-
continue;
|
|
23134
|
-
}
|
|
23135
|
-
const current = hashFileRecord(root, relative7);
|
|
23136
|
-
if (!current || current.sha256 !== record.sha256 || current.bytes !== record.bytes) {
|
|
23137
|
-
issues.push(issue2("semantic", "migration_target_modified", relative7, "installed resource bytes differ from the migration receipt"));
|
|
23138
|
-
}
|
|
23139
|
-
}
|
|
23140
|
-
for (const path4 of declared.keys()) {
|
|
23141
|
-
if (!installedPaths.has(path4)) {
|
|
23142
|
-
issues.push(issue2("semantic", "migration_target_missing", path4, "receipt-covered resource is not part of the validated configuration"));
|
|
23143
|
-
}
|
|
23144
|
-
}
|
|
23145
|
-
return sortIssues2(issues);
|
|
23146
|
-
}
|
|
23147
|
-
function relativePortable(root, absolute) {
|
|
23148
|
-
const rel = relative(root, absolute);
|
|
23149
|
-
if (!rel || isAbsolute2(rel) || rel === ".." || rel.startsWith(`..${sep}`)) return void 0;
|
|
23150
|
-
return rel.split(sep).join("/");
|
|
23151
|
-
}
|
|
23152
23065
|
function legacyInputsAt(root) {
|
|
23153
23066
|
const candidates = [
|
|
23154
23067
|
".kxm/config/agents.json",
|
|
@@ -23158,7 +23071,6 @@ function legacyInputsAt(root) {
|
|
|
23158
23071
|
];
|
|
23159
23072
|
return candidates.filter((candidate) => existsSync6(join6(root, ...candidate.split("/"))));
|
|
23160
23073
|
}
|
|
23161
|
-
var KXM_MIGRATION_RECEIPT_PATH = ".kxm/migration-receipt.yaml";
|
|
23162
23074
|
var LEGACY_CONFIG_FILES = [".kxm/config/agents.json", ".kxm/config/gates.json"];
|
|
23163
23075
|
function legacyConfigFilesAt(root) {
|
|
23164
23076
|
const files = [];
|
|
@@ -23184,66 +23096,6 @@ function legacyConfigFilesAt(root) {
|
|
|
23184
23096
|
}
|
|
23185
23097
|
return files;
|
|
23186
23098
|
}
|
|
23187
|
-
function hashFileRecord(root, relativePath) {
|
|
23188
|
-
const absolute = join6(root, ...relativePath.split("/"));
|
|
23189
|
-
if (!existsSync6(absolute)) return void 0;
|
|
23190
|
-
const bytes = readFileSync5(absolute);
|
|
23191
|
-
return {
|
|
23192
|
-
path: relativePath,
|
|
23193
|
-
sha256: `sha256:${createHash6("sha256").update(bytes).digest("hex")}`,
|
|
23194
|
-
bytes: bytes.byteLength
|
|
23195
|
-
};
|
|
23196
|
-
}
|
|
23197
|
-
function migrationReceiptSelfHash(receipt) {
|
|
23198
|
-
const { receiptSha256: _ignored, ...unsigned } = receipt;
|
|
23199
|
-
return `sha256:${createHash6("sha256").update(kxmCanonicalJson(unsigned), "utf8").digest("hex")}`;
|
|
23200
|
-
}
|
|
23201
|
-
function readKxmMigrationReceipt(root, options = {}) {
|
|
23202
|
-
const receiptPath = join6(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
|
|
23203
|
-
const legacyFiles = legacyConfigFilesAt(root);
|
|
23204
|
-
if (!existsSync6(receiptPath)) {
|
|
23205
|
-
return {
|
|
23206
|
-
issues: legacyFiles.map((file) => issue2("semantic", "legacy_kxm_conflict", file, "legacy and KXM configuration cannot coexist before an accepted migration receipt"))
|
|
23207
|
-
};
|
|
23208
|
-
}
|
|
23209
|
-
const receiptStat = lstatSync(receiptPath);
|
|
23210
|
-
if (receiptStat.isSymbolicLink() || !receiptStat.isFile()) {
|
|
23211
|
-
return { issues: [issue2("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")] };
|
|
23212
|
-
}
|
|
23213
|
-
let receipt;
|
|
23214
|
-
try {
|
|
23215
|
-
receipt = parseRestrictedYaml2(readFileSync5(receiptPath), KXM_MIGRATION_RECEIPT_PATH);
|
|
23216
|
-
} catch (error) {
|
|
23217
|
-
if (error instanceof KxmConfigError) return { issues: [...error.issues] };
|
|
23218
|
-
throw error;
|
|
23219
|
-
}
|
|
23220
|
-
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
23221
|
-
const schemaIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
|
|
23222
|
-
if (schemaIssues.length > 0) return { issues: schemaIssues };
|
|
23223
|
-
if (receipt.receiptSha256 !== migrationReceiptSelfHash(receipt)) {
|
|
23224
|
-
return { issues: [issue2("semantic", "migration_receipt_hash_mismatch", KXM_MIGRATION_RECEIPT_PATH, "receipt self-hash does not match its content")] };
|
|
23225
|
-
}
|
|
23226
|
-
const sources = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate.path : void 0).filter((candidate) => typeof candidate === "string").sort(compareCodeUnits3);
|
|
23227
|
-
const issues = [];
|
|
23228
|
-
const declared = new Set(sources);
|
|
23229
|
-
for (const file of legacyFiles) {
|
|
23230
|
-
if (!declared.has(file)) {
|
|
23231
|
-
issues.push(issue2("semantic", "migration_source_unmigrated", file, "legacy configuration file is not covered by the migration receipt"));
|
|
23232
|
-
continue;
|
|
23233
|
-
}
|
|
23234
|
-
const record = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0).find((candidate) => candidate?.path === file);
|
|
23235
|
-
const current = hashFileRecord(root, file);
|
|
23236
|
-
if (!current || current.sha256 !== record?.sha256 || current.bytes !== record?.bytes) {
|
|
23237
|
-
issues.push(issue2("semantic", "migration_source_changed", file, "legacy configuration changed after the migration receipt was issued"));
|
|
23238
|
-
}
|
|
23239
|
-
}
|
|
23240
|
-
for (const file of sources) {
|
|
23241
|
-
if (!legacyFiles.includes(file)) {
|
|
23242
|
-
issues.push(issue2("semantic", "migration_source_missing", file, "receipt covers a legacy configuration file that no longer exists"));
|
|
23243
|
-
}
|
|
23244
|
-
}
|
|
23245
|
-
return issues.length > 0 ? { issues } : { receipt, issues: [] };
|
|
23246
|
-
}
|
|
23247
23099
|
function discoverLegacyRoot(start) {
|
|
23248
23100
|
let initial = resolve3(start);
|
|
23249
23101
|
if (existsSync6(initial) && !lstatSync(initial).isDirectory()) initial = dirname4(initial);
|
|
@@ -23260,16 +23112,32 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
|
|
|
23260
23112
|
return { mode: "ready", inspectedFrom, projectRoot, changesRequired: false, issues: [], legacyInputs: [], configRevision: bundle.configRevision };
|
|
23261
23113
|
} catch (error) {
|
|
23262
23114
|
if (!(error instanceof KxmConfigError)) throw error;
|
|
23115
|
+
const legacyIssues = error.issues.filter((candidate) => candidate.code === "legacy_state_unsupported");
|
|
23263
23116
|
const legacyInputs = legacyInputsAt(projectRoot);
|
|
23264
|
-
if (legacyInputs.length > 0) {
|
|
23265
|
-
return {
|
|
23117
|
+
if (legacyInputs.length > 0 || legacyIssues.length > 0) {
|
|
23118
|
+
return {
|
|
23119
|
+
mode: "legacy",
|
|
23120
|
+
inspectedFrom,
|
|
23121
|
+
projectRoot,
|
|
23122
|
+
legacyRoot: projectRoot,
|
|
23123
|
+
changesRequired: true,
|
|
23124
|
+
issues: [...error.issues, ...legacyInputs.length > 0 ? [issue2("discovery", "legacy_state_unsupported", ".kxm", "legacy configuration is present and this build does not migrate it: initialise a fresh project directory and carry over the YAML definitions you want to keep")] : []],
|
|
23125
|
+
legacyInputs: [.../* @__PURE__ */ new Set([...legacyInputs, ...legacyIssues.map((candidate) => candidate.file)])]
|
|
23126
|
+
};
|
|
23266
23127
|
}
|
|
23267
23128
|
return { mode: "repair", inspectedFrom, projectRoot, changesRequired: true, issues: error.issues, legacyInputs: [] };
|
|
23268
23129
|
}
|
|
23269
23130
|
}
|
|
23270
23131
|
const legacy = discoverLegacyRoot(inspectedFrom);
|
|
23271
23132
|
if (legacy) {
|
|
23272
|
-
return {
|
|
23133
|
+
return {
|
|
23134
|
+
mode: "legacy",
|
|
23135
|
+
inspectedFrom,
|
|
23136
|
+
legacyRoot: legacy.root,
|
|
23137
|
+
changesRequired: true,
|
|
23138
|
+
issues: [issue2("discovery", "legacy_state_unsupported", legacy.root, "legacy KXM/Mesh configuration discovered; this build does not migrate it \u2014 initialise a fresh project directory and copy the YAML definitions you want to keep")],
|
|
23139
|
+
legacyInputs: legacy.inputs
|
|
23140
|
+
};
|
|
23273
23141
|
}
|
|
23274
23142
|
const gitRoot = discoverGitRoot(inspectedFrom);
|
|
23275
23143
|
const candidateRoot = gitRoot ?? inspectedFrom;
|
|
@@ -23770,7 +23638,7 @@ function openDatabase(file, description, spec) {
|
|
|
23770
23638
|
throw databaseError(
|
|
23771
23639
|
"runtime_schema_outdated",
|
|
23772
23640
|
file,
|
|
23773
|
-
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (
|
|
23641
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
|
|
23774
23642
|
);
|
|
23775
23643
|
}
|
|
23776
23644
|
if (spec.tables) {
|
|
@@ -24532,7 +24400,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
24532
24400
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
24533
24401
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
24534
24402
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
24535
|
-
const
|
|
24403
|
+
const join49 = compileJoin(step, id, sink);
|
|
24536
24404
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
24537
24405
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
24538
24406
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
|
|
@@ -24560,7 +24428,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
24560
24428
|
transitions: orderedTransitions,
|
|
24561
24429
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
24562
24430
|
assignments,
|
|
24563
|
-
join:
|
|
24431
|
+
join: join49
|
|
24564
24432
|
};
|
|
24565
24433
|
if (kind === "agent" || kind === "moa") {
|
|
24566
24434
|
if (!agent) return void 0;
|
|
@@ -24610,15 +24478,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
24610
24478
|
};
|
|
24611
24479
|
}
|
|
24612
24480
|
function compileJoin(step, stepId, sink) {
|
|
24613
|
-
const
|
|
24614
|
-
if (!
|
|
24615
|
-
const declared = stringValue2(
|
|
24481
|
+
const join49 = objectValue2(step.join);
|
|
24482
|
+
if (!join49) return { strategy: "all" };
|
|
24483
|
+
const declared = stringValue2(join49.strategy);
|
|
24616
24484
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
24617
|
-
const minimumPassed = compileOptionalCount(
|
|
24485
|
+
const minimumPassed = compileOptionalCount(join49.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
24618
24486
|
const compiled = {
|
|
24619
24487
|
strategy,
|
|
24620
24488
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
24621
|
-
...typeof
|
|
24489
|
+
...typeof join49.cancelRemaining === "boolean" ? { cancelRemaining: join49.cancelRemaining } : {}
|
|
24622
24490
|
};
|
|
24623
24491
|
return compiled;
|
|
24624
24492
|
}
|
|
@@ -27847,7 +27715,7 @@ async function watchGithubChecks(input) {
|
|
|
27847
27715
|
}
|
|
27848
27716
|
const fetchImpl = input.fetchImpl ?? fetch;
|
|
27849
27717
|
const now = input.now ?? Date.now;
|
|
27850
|
-
const sleep = input.sleep ?? ((ms) => new Promise((
|
|
27718
|
+
const sleep = input.sleep ?? ((ms) => new Promise((resolve30) => setTimeout(resolve30, ms)));
|
|
27851
27719
|
const deadline = now() + input.timeoutMs;
|
|
27852
27720
|
const headers = {
|
|
27853
27721
|
authorization: `Bearer ${token}`,
|
|
@@ -28842,9 +28710,9 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
|
|
|
28842
28710
|
}
|
|
28843
28711
|
|
|
28844
28712
|
// plugins/kxm/src/cli/tasks.ts
|
|
28845
|
-
var
|
|
28846
|
-
import { existsSync as
|
|
28847
|
-
import { basename as basename6, join as
|
|
28713
|
+
var import_yaml12 = __toESM(require_dist(), 1);
|
|
28714
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, readdirSync as readdirSync8 } from "node:fs";
|
|
28715
|
+
import { basename as basename6, join as join27, resolve as resolve20 } from "node:path";
|
|
28848
28716
|
|
|
28849
28717
|
// plugins/kxm/src/suggest.ts
|
|
28850
28718
|
var WORKFLOW_PATTERNS = [
|
|
@@ -29854,7 +29722,7 @@ function createStudioServer(options = {}) {
|
|
|
29854
29722
|
}
|
|
29855
29723
|
|
|
29856
29724
|
// plugins/kxm/src/cli/project.ts
|
|
29857
|
-
import { join as
|
|
29725
|
+
import { join as join26, resolve as resolve19 } from "node:path";
|
|
29858
29726
|
import { createInterface as createInterface2 } from "node:readline";
|
|
29859
29727
|
|
|
29860
29728
|
// plugins/kxm/src/model-inventory.ts
|
|
@@ -31683,17 +31551,20 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
|
31683
31551
|
]);
|
|
31684
31552
|
const loaderOptions2 = configOptions(options, repositoryBindings);
|
|
31685
31553
|
const transactionOptions = repairOptions(options, repositoryBindings);
|
|
31554
|
+
const plan = planKxmInitialization(start, loaderOptions2);
|
|
31555
|
+
if (plan.mode === "legacy") {
|
|
31556
|
+
return { action: "planned", plan, ...plan.projectRoot ? { projectRoot: plan.projectRoot } : {}, files: [] };
|
|
31557
|
+
}
|
|
31686
31558
|
if (hasKxmInitTransaction(gitRoot)) {
|
|
31687
31559
|
const operation = inspectKxmInitTransaction(gitRoot, options.schemasDir);
|
|
31688
|
-
const plan2 = planKxmInitialization(start, loaderOptions2);
|
|
31689
31560
|
if (!operation) {
|
|
31690
|
-
if (options.dryRun) return { action: "planned", plan
|
|
31561
|
+
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, resumePending: true, files: [] };
|
|
31691
31562
|
if (!mutationLock) throw new Error("project mutation lock is required to clean an empty transaction");
|
|
31692
31563
|
resumeKxmInitTransaction(gitRoot, transactionOptions);
|
|
31693
31564
|
} else if (options.dryRun) {
|
|
31694
31565
|
return {
|
|
31695
31566
|
action: "planned",
|
|
31696
|
-
plan
|
|
31567
|
+
plan,
|
|
31697
31568
|
projectRoot: gitRoot,
|
|
31698
31569
|
resumePending: true,
|
|
31699
31570
|
transactionKind: operation.kind,
|
|
@@ -31742,10 +31613,6 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
|
|
|
31742
31613
|
};
|
|
31743
31614
|
}
|
|
31744
31615
|
}
|
|
31745
|
-
const plan = planKxmInitialization(start, loaderOptions2);
|
|
31746
|
-
if (plan.mode === "migrate") {
|
|
31747
|
-
return { action: "planned", plan, ...plan.projectRoot ? { projectRoot: plan.projectRoot } : {}, files: [] };
|
|
31748
|
-
}
|
|
31749
31616
|
if (plan.mode === "repair") {
|
|
31750
31617
|
const projectRoot = plan.projectRoot ?? gitRoot;
|
|
31751
31618
|
let templateRepair;
|
|
@@ -31894,1486 +31761,6 @@ function initializeKxmProject(start = process.cwd(), options = {}) {
|
|
|
31894
31761
|
return withKxmLocalBindingLock(gitRoot, storeOptions, (lock) => initializeKxmProjectAtGitRoot(start, gitRoot, options, lock));
|
|
31895
31762
|
}
|
|
31896
31763
|
|
|
31897
|
-
// plugins/kxm/src/migrate.ts
|
|
31898
|
-
var import_yaml12 = __toESM(require_dist(), 1);
|
|
31899
|
-
import { createHash as createHash13 } from "node:crypto";
|
|
31900
|
-
import { chmodSync as chmodSync6, closeSync as closeSync3, existsSync as existsSync23, fsyncSync as fsyncSync3, lstatSync as lstatSync8, mkdirSync as mkdirSync19, openSync as openSync3, readFileSync as readFileSync22, renameSync as renameSync7, rmSync as rmSync9, writeFileSync as writeFileSync18 } from "node:fs";
|
|
31901
|
-
import { join as join26 } from "node:path";
|
|
31902
|
-
var MAX_LEGACY_JSON_BYTES = 1024 * 1024;
|
|
31903
|
-
var MAX_LEGACY_JSON_DEPTH = 32;
|
|
31904
|
-
var MAX_LEGACY_JSON_NODES = 32768;
|
|
31905
|
-
var MAX_LEGACY_SOURCES = 64;
|
|
31906
|
-
function migrateIssue(phase, code, file, message) {
|
|
31907
|
-
return { phase, code, file, message };
|
|
31908
|
-
}
|
|
31909
|
-
function migrateFail(code, file, message) {
|
|
31910
|
-
throw new KxmConfigError([migrateIssue("parse", code, file, message)]);
|
|
31911
|
-
}
|
|
31912
|
-
function parseLegacyJson(text, label) {
|
|
31913
|
-
const bytes = Buffer.byteLength(text, "utf8");
|
|
31914
|
-
if (bytes > MAX_LEGACY_JSON_BYTES) migrateFail("document_too_large", label, `document exceeds ${MAX_LEGACY_JSON_BYTES} bytes`);
|
|
31915
|
-
const state = { nodes: 0 };
|
|
31916
|
-
const parseNode = (value, path4, depth) => {
|
|
31917
|
-
state.nodes += 1;
|
|
31918
|
-
if (state.nodes > MAX_LEGACY_JSON_NODES) migrateFail("node_limit", label, `document exceeds ${MAX_LEGACY_JSON_NODES} nodes`);
|
|
31919
|
-
if (depth > MAX_LEGACY_JSON_DEPTH) migrateFail("depth_limit", label, `document exceeds nesting depth ${MAX_LEGACY_JSON_DEPTH}`);
|
|
31920
|
-
if (value === null || typeof value === "boolean" || typeof value === "string") return value;
|
|
31921
|
-
if (typeof value === "number") {
|
|
31922
|
-
if (!Number.isFinite(value)) migrateFail("non_json_number", label, `${path4} is not a finite number`);
|
|
31923
|
-
return value;
|
|
31924
|
-
}
|
|
31925
|
-
if (Array.isArray(value)) {
|
|
31926
|
-
return value.map((entry, index) => parseNode(entry, `${path4}[${index}]`, depth + 1));
|
|
31927
|
-
}
|
|
31928
|
-
if (value && typeof value === "object") {
|
|
31929
|
-
const record = {};
|
|
31930
|
-
for (const [key, entry] of Object.entries(value)) record[key] = parseNode(entry, `${path4}.${key}`, depth + 1);
|
|
31931
|
-
return record;
|
|
31932
|
-
}
|
|
31933
|
-
migrateFail("non_json_value", label, `${path4} is not JSON-compatible`);
|
|
31934
|
-
};
|
|
31935
|
-
let raw;
|
|
31936
|
-
try {
|
|
31937
|
-
raw = JSON.parse(text);
|
|
31938
|
-
} catch (error) {
|
|
31939
|
-
migrateFail("invalid_json", label, error instanceof Error ? error.message : String(error));
|
|
31940
|
-
}
|
|
31941
|
-
detectDuplicateKeys(text, label);
|
|
31942
|
-
return parseNode(raw, "$", 0);
|
|
31943
|
-
}
|
|
31944
|
-
function detectDuplicateKeys(text, label) {
|
|
31945
|
-
const stack = [];
|
|
31946
|
-
let index = 0;
|
|
31947
|
-
const length = text.length;
|
|
31948
|
-
const skipWhitespace = () => {
|
|
31949
|
-
while (index < length && /[\s]/.test(text[index])) index += 1;
|
|
31950
|
-
};
|
|
31951
|
-
const parseString = () => {
|
|
31952
|
-
let result = "";
|
|
31953
|
-
index += 1;
|
|
31954
|
-
while (index < length) {
|
|
31955
|
-
const char = text[index];
|
|
31956
|
-
if (char === '"') {
|
|
31957
|
-
index += 1;
|
|
31958
|
-
return result;
|
|
31959
|
-
}
|
|
31960
|
-
if (char === "\\") {
|
|
31961
|
-
const escape2 = text[index + 1];
|
|
31962
|
-
if (escape2 === "u") {
|
|
31963
|
-
result += String.fromCharCode(Number.parseInt(text.slice(index + 2, index + 6), 16));
|
|
31964
|
-
index += 6;
|
|
31965
|
-
} else {
|
|
31966
|
-
const escapes = { '"': '"', "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: " " };
|
|
31967
|
-
result += escapes[escape2] ?? escape2;
|
|
31968
|
-
index += 2;
|
|
31969
|
-
}
|
|
31970
|
-
} else {
|
|
31971
|
-
result += char;
|
|
31972
|
-
index += 1;
|
|
31973
|
-
}
|
|
31974
|
-
}
|
|
31975
|
-
migrateFail("invalid_json", label, "unterminated string");
|
|
31976
|
-
};
|
|
31977
|
-
const skipValue = () => {
|
|
31978
|
-
skipWhitespace();
|
|
31979
|
-
const char = text[index];
|
|
31980
|
-
if (char === '"') {
|
|
31981
|
-
parseString();
|
|
31982
|
-
return;
|
|
31983
|
-
}
|
|
31984
|
-
if (char === "{" || char === "[") {
|
|
31985
|
-
parseContainer();
|
|
31986
|
-
return;
|
|
31987
|
-
}
|
|
31988
|
-
while (index < length && !/[\s,\]}]/.test(text[index])) index += 1;
|
|
31989
|
-
};
|
|
31990
|
-
const parseContainer = () => {
|
|
31991
|
-
const char = text[index];
|
|
31992
|
-
if (stack.length > MAX_LEGACY_JSON_DEPTH) migrateFail("depth_limit", label, `document exceeds nesting depth ${MAX_LEGACY_JSON_DEPTH}`);
|
|
31993
|
-
if (char === "{") {
|
|
31994
|
-
const keys = /* @__PURE__ */ new Set();
|
|
31995
|
-
stack.push(keys);
|
|
31996
|
-
index += 1;
|
|
31997
|
-
skipWhitespace();
|
|
31998
|
-
if (text[index] === "}") {
|
|
31999
|
-
index += 1;
|
|
32000
|
-
stack.pop();
|
|
32001
|
-
return;
|
|
32002
|
-
}
|
|
32003
|
-
for (; ; ) {
|
|
32004
|
-
skipWhitespace();
|
|
32005
|
-
if (text[index] !== '"') migrateFail("invalid_json", label, `expected object key at byte ${index}`);
|
|
32006
|
-
const key = parseString();
|
|
32007
|
-
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
32008
|
-
migrateFail("forbidden_key", label, `key ${JSON.stringify(key)} is forbidden`);
|
|
32009
|
-
}
|
|
32010
|
-
const frame = stack[stack.length - 1];
|
|
32011
|
-
if (frame instanceof Set) {
|
|
32012
|
-
if (frame.has(key)) migrateFail("duplicate_key", label, `duplicate key ${JSON.stringify(key)}`);
|
|
32013
|
-
frame.add(key);
|
|
32014
|
-
}
|
|
32015
|
-
skipWhitespace();
|
|
32016
|
-
if (text[index] !== ":") migrateFail("invalid_json", label, `expected ':' at byte ${index}`);
|
|
32017
|
-
index += 1;
|
|
32018
|
-
skipValue();
|
|
32019
|
-
skipWhitespace();
|
|
32020
|
-
if (text[index] === ",") {
|
|
32021
|
-
index += 1;
|
|
32022
|
-
continue;
|
|
32023
|
-
}
|
|
32024
|
-
if (text[index] === "}") {
|
|
32025
|
-
index += 1;
|
|
32026
|
-
stack.pop();
|
|
32027
|
-
return;
|
|
32028
|
-
}
|
|
32029
|
-
migrateFail("invalid_json", label, `expected ',' or '}' at byte ${index}`);
|
|
32030
|
-
}
|
|
32031
|
-
}
|
|
32032
|
-
if (char === "[") {
|
|
32033
|
-
stack.push("array");
|
|
32034
|
-
index += 1;
|
|
32035
|
-
skipWhitespace();
|
|
32036
|
-
if (text[index] === "]") {
|
|
32037
|
-
index += 1;
|
|
32038
|
-
stack.pop();
|
|
32039
|
-
return;
|
|
32040
|
-
}
|
|
32041
|
-
for (; ; ) {
|
|
32042
|
-
skipValue();
|
|
32043
|
-
skipWhitespace();
|
|
32044
|
-
if (text[index] === ",") {
|
|
32045
|
-
index += 1;
|
|
32046
|
-
continue;
|
|
32047
|
-
}
|
|
32048
|
-
if (text[index] === "]") {
|
|
32049
|
-
index += 1;
|
|
32050
|
-
stack.pop();
|
|
32051
|
-
return;
|
|
32052
|
-
}
|
|
32053
|
-
migrateFail("invalid_json", label, `expected ',' or ']' at byte ${index}`);
|
|
32054
|
-
}
|
|
32055
|
-
}
|
|
32056
|
-
};
|
|
32057
|
-
skipValue();
|
|
32058
|
-
skipWhitespace();
|
|
32059
|
-
if (index !== length) migrateFail("invalid_json", label, `trailing content at byte ${index}`);
|
|
32060
|
-
}
|
|
32061
|
-
function sha256Of(input) {
|
|
32062
|
-
return `sha256:${createHash13("sha256").update(input).digest("hex")}`;
|
|
32063
|
-
}
|
|
32064
|
-
function readKxmLegacySources(root) {
|
|
32065
|
-
const files = legacyConfigFilesAt(root);
|
|
32066
|
-
if (files.length > MAX_LEGACY_SOURCES) {
|
|
32067
|
-
throw new KxmConfigError([migrateIssue("discovery", "legacy_source_limit", ".kxm/config", `legacy configuration exceeds ${MAX_LEGACY_SOURCES} files`)]);
|
|
32068
|
-
}
|
|
32069
|
-
return files.map((path4) => {
|
|
32070
|
-
const absolute = join26(root, ...path4.split("/"));
|
|
32071
|
-
const stat = lstatSync8(absolute);
|
|
32072
|
-
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
32073
|
-
throw new KxmConfigError([migrateIssue("path", "legacy_source_invalid", path4, "legacy configuration must be a regular file, not a link")]);
|
|
32074
|
-
}
|
|
32075
|
-
const buffer = readFileSync22(absolute);
|
|
32076
|
-
let text;
|
|
32077
|
-
try {
|
|
32078
|
-
text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
|
|
32079
|
-
} catch {
|
|
32080
|
-
throw new KxmConfigError([migrateIssue("parse", "invalid_utf8", path4, "document is not valid UTF-8")]);
|
|
32081
|
-
}
|
|
32082
|
-
return {
|
|
32083
|
-
path: path4,
|
|
32084
|
-
sha256: sha256Of(buffer),
|
|
32085
|
-
bytes: buffer.byteLength,
|
|
32086
|
-
value: parseLegacyJson(text, path4)
|
|
32087
|
-
};
|
|
32088
|
-
});
|
|
32089
|
-
}
|
|
32090
|
-
function legacySourceDigest(sources) {
|
|
32091
|
-
return sha256Of(kxmCanonicalJson(sources.map((source) => ({ path: source.path, sha256: source.sha256, bytes: source.bytes }))));
|
|
32092
|
-
}
|
|
32093
|
-
var IDENTIFIER2 = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
|
|
32094
|
-
var KNOWN_AGENT_FIELDS = /* @__PURE__ */ new Set(["name", "kind", "driver", "model", "thinking", "purpose"]);
|
|
32095
|
-
var KNOWN_GATE_FIELDS = /* @__PURE__ */ new Set(["name", "kind", "driver", "purpose"]);
|
|
32096
|
-
var KNOWN_WORKFLOW_FIELDS = /* @__PURE__ */ new Set([
|
|
32097
|
-
"id",
|
|
32098
|
-
"source",
|
|
32099
|
-
"project",
|
|
32100
|
-
"target",
|
|
32101
|
-
"secret",
|
|
32102
|
-
"secretEnv",
|
|
32103
|
-
"signalSecret",
|
|
32104
|
-
"signalSecretEnv",
|
|
32105
|
-
"event",
|
|
32106
|
-
"filter",
|
|
32107
|
-
"delivery",
|
|
32108
|
-
"ttlMs",
|
|
32109
|
-
"promptTemplate",
|
|
32110
|
-
"stages",
|
|
32111
|
-
"maxTransitions",
|
|
32112
|
-
"reproOracle",
|
|
32113
|
-
"planHash",
|
|
32114
|
-
"requirePlanHash"
|
|
32115
|
-
]);
|
|
32116
|
-
var KNOWN_STAGE_FIELDS = /* @__PURE__ */ new Set(["id", "label", "instructions", "requiredEvidence", "maxAttempts", "area", "evidencePolicies", "on", "maxTransitions"]);
|
|
32117
|
-
var KNOWN_POLICY_FIELDS = /* @__PURE__ */ new Set(["kind", "minProducers", "eligibleAgents", "acceptedStatuses", "degradation"]);
|
|
32118
|
-
var KNOWN_TRANSITION_FIELDS = /* @__PURE__ */ new Set(["target", "maxTransitions"]);
|
|
32119
|
-
function compareCodeUnits6(left, right) {
|
|
32120
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
32121
|
-
}
|
|
32122
|
-
function asObject2(value) {
|
|
32123
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
32124
|
-
}
|
|
32125
|
-
function asString(value) {
|
|
32126
|
-
return typeof value === "string" ? value : void 0;
|
|
32127
|
-
}
|
|
32128
|
-
function normalizeIdentifier(raw) {
|
|
32129
|
-
return raw.toLocaleLowerCase("en-US").replace(/[^a-z0-9_-]+/g, "-").replace(/[-_]{2,}/g, "-").replace(/^[-_]+/, "").replace(/[-_]+$/, "").replace(/^(?=[0-9])/, "id-");
|
|
32130
|
-
}
|
|
32131
|
-
function pointerEscape2(segment) {
|
|
32132
|
-
const escaped = segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
32133
|
-
if (escaped.length <= 200) return escaped;
|
|
32134
|
-
const digest = createHash13("sha256").update(segment, "utf8").digest("hex").slice(0, 8);
|
|
32135
|
-
return `${escaped.slice(0, 180)}~digest-${digest}`;
|
|
32136
|
-
}
|
|
32137
|
-
function elide(raw, max = 120) {
|
|
32138
|
-
return raw.length <= max ? raw : `${raw.slice(0, max - 15)}\u2026[${raw.length}]`;
|
|
32139
|
-
}
|
|
32140
|
-
function keyFragment(raw) {
|
|
32141
|
-
const full = normalizeIdentifier(raw) || "unnamed";
|
|
32142
|
-
const normalized = full.length > 96 ? `${full.slice(0, 96)}-${full.length}` : full;
|
|
32143
|
-
const digest = createHash13("sha256").update(raw, "utf8").digest("hex").slice(0, 8);
|
|
32144
|
-
return `${normalized}-${digest}`;
|
|
32145
|
-
}
|
|
32146
|
-
function recordUnmapped(plan, sourcePath, pointer, value, sensitive) {
|
|
32147
|
-
const digestInput = sensitive ? { redacted: true, bytes: Buffer.byteLength(kxmCanonicalJson(value === void 0 ? null : value), "utf8") } : value === void 0 ? null : value;
|
|
32148
|
-
plan.unmapped.push({
|
|
32149
|
-
sourcePath,
|
|
32150
|
-
jsonPointer: pointer,
|
|
32151
|
-
valueSha256: sha256Of(kxmCanonicalJson(digestInput)),
|
|
32152
|
-
sensitive
|
|
32153
|
-
});
|
|
32154
|
-
}
|
|
32155
|
-
function recordUnknownFields(plan, sourcePath, pointer, record, known) {
|
|
32156
|
-
for (const key of Object.keys(record).sort(compareCodeUnits6)) {
|
|
32157
|
-
if (!known.has(key)) recordUnmapped(plan, sourcePath, `${pointer}/${pointerEscape2(key)}`, record[key], false);
|
|
32158
|
-
}
|
|
32159
|
-
}
|
|
32160
|
-
function mapIdentity(plan, sourcePath, raw, kind, taken, decisions, dropPointer, dropValue) {
|
|
32161
|
-
const normalized = normalizeIdentifier(raw);
|
|
32162
|
-
const usable = (candidate) => IDENTIFIER2.test(candidate) && candidate.length <= 64;
|
|
32163
|
-
const adopt = (candidate) => {
|
|
32164
|
-
const foldedCandidate = candidate.toLocaleLowerCase("en-US");
|
|
32165
|
-
const priorCandidate = taken.get(foldedCandidate);
|
|
32166
|
-
if (priorCandidate !== void 0 && priorCandidate !== raw) return { outcome: "taken" };
|
|
32167
|
-
if (priorCandidate === raw) return { outcome: "duplicate" };
|
|
32168
|
-
taken.set(foldedCandidate, raw);
|
|
32169
|
-
plan.renames.push({ from: elide(raw), to: candidate, kind });
|
|
32170
|
-
return { outcome: "adopted", id: candidate };
|
|
32171
|
-
};
|
|
32172
|
-
const reportDuplicate = () => {
|
|
32173
|
-
const decided = resolveDecision(
|
|
32174
|
-
plan,
|
|
32175
|
-
`identity-duplicate:${kind}:${keyFragment(raw)}`,
|
|
32176
|
-
"unsupported",
|
|
32177
|
-
sourcePath,
|
|
32178
|
-
`${kind} name ${JSON.stringify(elide(raw, 200))} is declared more than once; confirm the duplicate definition is dropped`,
|
|
32179
|
-
["drop"],
|
|
32180
|
-
decisions
|
|
32181
|
-
);
|
|
32182
|
-
recordUnmapped(plan, sourcePath, dropPointer ?? `/duplicates/${pointerEscape2(raw)}`, dropValue ?? raw, false);
|
|
32183
|
-
return decided === "drop" ? { status: "dropped" } : { status: "pending" };
|
|
32184
|
-
};
|
|
32185
|
-
if (!normalized || !usable(normalized)) {
|
|
32186
|
-
const supplied = resolveDecision(
|
|
32187
|
-
plan,
|
|
32188
|
-
`identity:${kind}:${keyFragment(raw)}`,
|
|
32189
|
-
"identity",
|
|
32190
|
-
sourcePath,
|
|
32191
|
-
`${kind} name ${JSON.stringify(elide(raw, 200))} cannot be normalized to a KXM identifier; supply a decision with the target identifier`,
|
|
32192
|
-
["<identifier>"],
|
|
32193
|
-
decisions
|
|
32194
|
-
);
|
|
32195
|
-
if (typeof supplied === "string" && usable(supplied)) {
|
|
32196
|
-
const adopted = adopt(supplied);
|
|
32197
|
-
if (adopted.outcome === "adopted") return { status: "adopted", id: adopted.id };
|
|
32198
|
-
if (adopted.outcome === "duplicate") return reportDuplicate();
|
|
32199
|
-
plan.ambiguities.push({
|
|
32200
|
-
key: `identity:${kind}:${keyFragment(raw)}`,
|
|
32201
|
-
category: "identity",
|
|
32202
|
-
sourcePath,
|
|
32203
|
-
message: `decision-supplied identifier ${JSON.stringify(elide(supplied, 200))} for ${kind} ${JSON.stringify(elide(raw, 200))} collides with another migrated identity`,
|
|
32204
|
-
allowedValues: ["<identifier>"]
|
|
32205
|
-
});
|
|
32206
|
-
}
|
|
32207
|
-
return { status: "pending" };
|
|
32208
|
-
}
|
|
32209
|
-
const folded = normalized.toLocaleLowerCase("en-US");
|
|
32210
|
-
const prior = taken.get(folded);
|
|
32211
|
-
if (prior && prior !== raw) {
|
|
32212
|
-
const supplied = resolveDecision(
|
|
32213
|
-
plan,
|
|
32214
|
-
`identity-collision:${kind}:${folded}-${keyFragment(raw)}`,
|
|
32215
|
-
"identity",
|
|
32216
|
-
sourcePath,
|
|
32217
|
-
`${kind} names ${JSON.stringify(elide(prior, 200))} and ${JSON.stringify(elide(raw, 200))} collide after normalization; supply a distinct identifier for ${JSON.stringify(elide(raw, 200))}`,
|
|
32218
|
-
["<identifier>"],
|
|
32219
|
-
decisions
|
|
32220
|
-
);
|
|
32221
|
-
if (typeof supplied === "string" && usable(supplied)) {
|
|
32222
|
-
const adopted = adopt(supplied);
|
|
32223
|
-
if (adopted.outcome === "adopted") return { status: "adopted", id: adopted.id };
|
|
32224
|
-
if (adopted.outcome === "duplicate") return reportDuplicate();
|
|
32225
|
-
plan.ambiguities.push({
|
|
32226
|
-
key: `identity-collision:${kind}:${folded}-${keyFragment(raw)}`,
|
|
32227
|
-
category: "identity",
|
|
32228
|
-
sourcePath,
|
|
32229
|
-
message: `decision-supplied identifier ${JSON.stringify(elide(supplied, 200))} for ${kind} ${JSON.stringify(elide(raw, 200))} collides with another migrated identity`,
|
|
32230
|
-
allowedValues: ["<identifier>"]
|
|
32231
|
-
});
|
|
32232
|
-
}
|
|
32233
|
-
return { status: "pending" };
|
|
32234
|
-
}
|
|
32235
|
-
if (prior === raw) return reportDuplicate();
|
|
32236
|
-
taken.set(folded, raw);
|
|
32237
|
-
if (normalized !== raw && !plan.renames.some((rename) => rename.kind === kind && rename.from === raw)) {
|
|
32238
|
-
plan.renames.push({ from: elide(raw), to: normalized, kind });
|
|
32239
|
-
}
|
|
32240
|
-
return { status: "adopted", id: normalized };
|
|
32241
|
-
}
|
|
32242
|
-
function decisionValueAllowed(key, value, allowed) {
|
|
32243
|
-
if (value === void 0) return false;
|
|
32244
|
-
if (allowed.some((candidate) => candidate === "<identifier>")) {
|
|
32245
|
-
return typeof value === "string" && IDENTIFIER2.test(value) && value.length <= 64;
|
|
32246
|
-
}
|
|
32247
|
-
if (allowed.some((candidate) => candidate === "<provider/model>")) {
|
|
32248
|
-
if (typeof value !== "string") return false;
|
|
32249
|
-
const separator = value.indexOf("/");
|
|
32250
|
-
const provider = separator > 0 ? normalizeIdentifier(value.slice(0, separator)) : "";
|
|
32251
|
-
const modelId2 = separator > 0 ? value.slice(separator + 1) : "";
|
|
32252
|
-
return IDENTIFIER2.test(provider) && provider.length <= 64 && modelId2.length >= 1 && modelId2.length <= 200;
|
|
32253
|
-
}
|
|
32254
|
-
if (key.startsWith("backedge:")) {
|
|
32255
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 100;
|
|
32256
|
-
}
|
|
32257
|
-
if (key.startsWith("budget:")) {
|
|
32258
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 1e3;
|
|
32259
|
-
}
|
|
32260
|
-
if (key.startsWith("coordinator:") || key.startsWith("producer:")) {
|
|
32261
|
-
if (allowed.some((candidate) => candidate === value)) return true;
|
|
32262
|
-
return allowed.length >= 64 && typeof value === "string" && IDENTIFIER2.test(value) && value.length <= 64;
|
|
32263
|
-
}
|
|
32264
|
-
return allowed.some((candidate) => candidate === value);
|
|
32265
|
-
}
|
|
32266
|
-
function resolveDecision(plan, key, category, sourcePath, message, allowedValues, decisions) {
|
|
32267
|
-
plan.decisionKeys.add(key);
|
|
32268
|
-
plan.decisionAllowedValues.set(key, allowedValues);
|
|
32269
|
-
if (Object.hasOwn(decisions, key)) {
|
|
32270
|
-
const value = decisions[key];
|
|
32271
|
-
if (decisionValueAllowed(key, value, allowedValues)) return value;
|
|
32272
|
-
if (!plan.invalidDecisions.includes(key)) plan.invalidDecisions.push(key);
|
|
32273
|
-
}
|
|
32274
|
-
if (!plan.ambiguities.some((ambiguity) => ambiguity.key === key)) {
|
|
32275
|
-
plan.ambiguities.push({ key, category, sourcePath, message, allowedValues });
|
|
32276
|
-
}
|
|
32277
|
-
return void 0;
|
|
32278
|
-
}
|
|
32279
|
-
function convertAgentsRoster(plan, source, decisions, agentNames) {
|
|
32280
|
-
const agents = /* @__PURE__ */ new Map();
|
|
32281
|
-
const models = /* @__PURE__ */ new Map();
|
|
32282
|
-
const rawNames = /* @__PURE__ */ new Map();
|
|
32283
|
-
const pendingRawNames = /* @__PURE__ */ new Set();
|
|
32284
|
-
const modelNames = /* @__PURE__ */ new Map();
|
|
32285
|
-
let identityPending = false;
|
|
32286
|
-
const root = asObject2(source.value);
|
|
32287
|
-
if (!root) {
|
|
32288
|
-
resolveDecision(
|
|
32289
|
-
plan,
|
|
32290
|
-
`unsupported:${source.path}:/`,
|
|
32291
|
-
"unsupported",
|
|
32292
|
-
source.path,
|
|
32293
|
-
"agents.json must be a JSON object with an agents array; confirm it is dropped from conversion",
|
|
32294
|
-
["drop"],
|
|
32295
|
-
decisions
|
|
32296
|
-
);
|
|
32297
|
-
return { agents, models, rawNames, pendingRawNames, identityPending };
|
|
32298
|
-
}
|
|
32299
|
-
recordUnknownFields(plan, source.path, "", root, /* @__PURE__ */ new Set(["schema", "project", "host", "agents", "ownership"]));
|
|
32300
|
-
if (root.ownership !== void 0) recordUnmapped(plan, source.path, "/ownership", root.ownership, false);
|
|
32301
|
-
if (root.host !== void 0) recordUnmapped(plan, source.path, "/host", root.host, false);
|
|
32302
|
-
if (root.project !== void 0) recordUnmapped(plan, source.path, "/project", root.project, false);
|
|
32303
|
-
if (root.schema !== void 0 && root.schema !== "kxm.agents.v1") recordUnmapped(plan, source.path, "/schema", root.schema, false);
|
|
32304
|
-
const list = Array.isArray(root.agents) ? root.agents : [];
|
|
32305
|
-
for (const [index, entry] of list.entries()) {
|
|
32306
|
-
const pointer = `/agents/${index}`;
|
|
32307
|
-
const record = asObject2(entry);
|
|
32308
|
-
if (!record) {
|
|
32309
|
-
recordUnmapped(plan, source.path, pointer, entry, false);
|
|
32310
|
-
continue;
|
|
32311
|
-
}
|
|
32312
|
-
recordUnknownFields(plan, source.path, pointer, record, KNOWN_AGENT_FIELDS);
|
|
32313
|
-
const name = asString(record.name);
|
|
32314
|
-
const kind = asString(record.kind) ?? "agent";
|
|
32315
|
-
const driver = asString(record.driver) ?? "ai";
|
|
32316
|
-
if (!name || kind !== "agent" || driver !== "ai") {
|
|
32317
|
-
resolveDecision(
|
|
32318
|
-
plan,
|
|
32319
|
-
`unsupported:${source.path}:${pointer}`,
|
|
32320
|
-
"unsupported",
|
|
32321
|
-
source.path,
|
|
32322
|
-
`roster entry ${index} (${name === void 0 ? "unnamed" : elide(name, 200)}) has kind ${elide(kind, 64)}/driver ${elide(driver, 64)}; only agent/ai entries convert losslessly \u2014 confirm the entry is dropped`,
|
|
32323
|
-
["drop"],
|
|
32324
|
-
decisions
|
|
32325
|
-
);
|
|
32326
|
-
recordUnmapped(plan, source.path, pointer, record, false);
|
|
32327
|
-
continue;
|
|
32328
|
-
}
|
|
32329
|
-
resolveDecision(
|
|
32330
|
-
plan,
|
|
32331
|
-
`permission:${keyFragment(name)}`,
|
|
32332
|
-
"permission",
|
|
32333
|
-
source.path,
|
|
32334
|
-
`approve the narrowed tool/repository/network ceiling for agent ${elide(name, 200)}`,
|
|
32335
|
-
["approve"],
|
|
32336
|
-
decisions
|
|
32337
|
-
);
|
|
32338
|
-
const agentIdentity = mapIdentity(plan, source.path, name, "agent", agentNames, decisions, pointer, record);
|
|
32339
|
-
if (agentIdentity.status === "pending") {
|
|
32340
|
-
identityPending = true;
|
|
32341
|
-
pendingRawNames.add(name);
|
|
32342
|
-
}
|
|
32343
|
-
if (agentIdentity.status !== "adopted") continue;
|
|
32344
|
-
const id = agentIdentity.id;
|
|
32345
|
-
const agent = {
|
|
32346
|
-
schema: "kxm.agent.v1",
|
|
32347
|
-
purpose: asString(record.purpose) ?? `Migrated legacy agent ${elide(name, 200)}.`
|
|
32348
|
-
};
|
|
32349
|
-
const model = asString(record.model);
|
|
32350
|
-
if (model) {
|
|
32351
|
-
const applyModel = (candidate) => {
|
|
32352
|
-
const separator = candidate.indexOf("/");
|
|
32353
|
-
if (separator <= 0) return false;
|
|
32354
|
-
const provider = normalizeIdentifier(candidate.slice(0, separator));
|
|
32355
|
-
const modelId2 = candidate.slice(separator + 1);
|
|
32356
|
-
if (IDENTIFIER2.test(provider) && provider.length <= 64 && modelId2.length >= 1 && modelId2.length <= 200) {
|
|
32357
|
-
agent.model = { provider, model: modelId2 };
|
|
32358
|
-
return true;
|
|
32359
|
-
}
|
|
32360
|
-
return false;
|
|
32361
|
-
};
|
|
32362
|
-
if (!applyModel(model)) {
|
|
32363
|
-
const supplied = resolveDecision(
|
|
32364
|
-
plan,
|
|
32365
|
-
`model:${keyFragment(name)}`,
|
|
32366
|
-
"identity",
|
|
32367
|
-
source.path,
|
|
32368
|
-
`agent ${elide(name, 200)} model ${JSON.stringify(elide(model, 200))} does not map to a provider identifier + model id pair; supply <provider>/<model>`,
|
|
32369
|
-
["<provider/model>"],
|
|
32370
|
-
decisions
|
|
32371
|
-
);
|
|
32372
|
-
if (typeof supplied === "string" && supplied !== model) {
|
|
32373
|
-
applyModel(supplied);
|
|
32374
|
-
} else if (typeof supplied !== "string") {
|
|
32375
|
-
recordUnmapped(plan, source.path, `${pointer}/model`, model, false);
|
|
32376
|
-
}
|
|
32377
|
-
}
|
|
32378
|
-
}
|
|
32379
|
-
const thinking = asString(record.thinking);
|
|
32380
|
-
if (thinking) {
|
|
32381
|
-
const selector = asObject2(agent.model);
|
|
32382
|
-
if (selector && typeof selector.profile !== "string") {
|
|
32383
|
-
const profileIdentity = mapIdentity(plan, source.path, `${id}-primary`, "model", modelNames, decisions);
|
|
32384
|
-
const profileId = profileIdentity.status === "adopted" ? profileIdentity.id : void 0;
|
|
32385
|
-
if (profileId) {
|
|
32386
|
-
const profile = {
|
|
32387
|
-
schema: "kxm.model.v1",
|
|
32388
|
-
provider: selector.provider,
|
|
32389
|
-
model: selector.model,
|
|
32390
|
-
thinking
|
|
32391
|
-
};
|
|
32392
|
-
models.set(profileId, profile);
|
|
32393
|
-
agent.model = { profile: profileId };
|
|
32394
|
-
}
|
|
32395
|
-
} else {
|
|
32396
|
-
recordUnmapped(plan, source.path, `${pointer}/thinking`, thinking, false);
|
|
32397
|
-
}
|
|
32398
|
-
}
|
|
32399
|
-
agent.tools = { preset: "workspace-writer" };
|
|
32400
|
-
agent.defaultRepositoryAccess = "none";
|
|
32401
|
-
agent.network = "provider-only";
|
|
32402
|
-
plan.permissionChanges.push({
|
|
32403
|
-
sourcePath: source.path,
|
|
32404
|
-
targetPath: `.kxm/agents/${id}.yaml`,
|
|
32405
|
-
direction: "unknown",
|
|
32406
|
-
summary: `agent ${elide(name, 200)}: legacy workers used launch-time tool flags the converter cannot observe; migrated ceiling is workspace-writer tools, no repository access, provider-only network`,
|
|
32407
|
-
decisionKey: `permission:${keyFragment(name)}`
|
|
32408
|
-
});
|
|
32409
|
-
agents.set(id, agent);
|
|
32410
|
-
if (!rawNames.has(name)) rawNames.set(name, id);
|
|
32411
|
-
}
|
|
32412
|
-
return { agents, models, rawNames, pendingRawNames, identityPending };
|
|
32413
|
-
}
|
|
32414
|
-
var IMPLEMENTED_GATES = /* @__PURE__ */ new Set(["test", "scm-delivery"]);
|
|
32415
|
-
function convertGatesRoster(plan, source, decisions) {
|
|
32416
|
-
const root = asObject2(source.value);
|
|
32417
|
-
if (!root) {
|
|
32418
|
-
resolveDecision(
|
|
32419
|
-
plan,
|
|
32420
|
-
`unsupported:${source.path}:/`,
|
|
32421
|
-
"unsupported",
|
|
32422
|
-
source.path,
|
|
32423
|
-
"gates.json must be a JSON object with a gates array; confirm it is dropped from conversion",
|
|
32424
|
-
["drop"],
|
|
32425
|
-
decisions
|
|
32426
|
-
);
|
|
32427
|
-
return;
|
|
32428
|
-
}
|
|
32429
|
-
recordUnknownFields(plan, source.path, "", root, /* @__PURE__ */ new Set(["schema", "gates"]));
|
|
32430
|
-
if (root.schema !== void 0 && root.schema !== "kxm.gates.v1") recordUnmapped(plan, source.path, "/schema", root.schema, false);
|
|
32431
|
-
const list = Array.isArray(root.gates) ? root.gates : [];
|
|
32432
|
-
for (const [index, entry] of list.entries()) {
|
|
32433
|
-
const pointer = `/gates/${index}`;
|
|
32434
|
-
const record = asObject2(entry);
|
|
32435
|
-
if (!record) {
|
|
32436
|
-
recordUnmapped(plan, source.path, pointer, entry, false);
|
|
32437
|
-
continue;
|
|
32438
|
-
}
|
|
32439
|
-
recordUnknownFields(plan, source.path, pointer, record, KNOWN_GATE_FIELDS);
|
|
32440
|
-
const name = asString(record.name) ?? `gate-${index}`;
|
|
32441
|
-
if (!IMPLEMENTED_GATES.has(name)) {
|
|
32442
|
-
resolveDecision(
|
|
32443
|
-
plan,
|
|
32444
|
-
`gate:${keyFragment(name)}`,
|
|
32445
|
-
"unsupported",
|
|
32446
|
-
source.path,
|
|
32447
|
-
`gate ${JSON.stringify(elide(name, 200))} has no registered deterministic runner in KXM; it is preserved in the migration report only`,
|
|
32448
|
-
["report-only"],
|
|
32449
|
-
decisions
|
|
32450
|
-
);
|
|
32451
|
-
recordUnmapped(plan, source.path, pointer, entry, false);
|
|
32452
|
-
}
|
|
32453
|
-
}
|
|
32454
|
-
}
|
|
32455
|
-
function mapStageRef(context, raw) {
|
|
32456
|
-
return context.renamesByKind.get(raw) ?? normalizeIdentifier(raw);
|
|
32457
|
-
}
|
|
32458
|
-
function convertTransition(plan, source, context, stageId, stageKey, outcome, rawOutcome, rawValue, decisions, stagePointer) {
|
|
32459
|
-
const pointer = `${stagePointer}/on/${pointerEscape2(rawOutcome)}`;
|
|
32460
|
-
const rule = typeof rawValue === "string" ? { target: rawValue } : asObject2(rawValue);
|
|
32461
|
-
if (!rule) {
|
|
32462
|
-
recordUnmapped(plan, source.path, pointer, rawValue, false);
|
|
32463
|
-
return void 0;
|
|
32464
|
-
}
|
|
32465
|
-
if (typeof rawValue !== "string") recordUnknownFields(plan, source.path, pointer, rule, KNOWN_TRANSITION_FIELDS);
|
|
32466
|
-
const target = asString(rule.target);
|
|
32467
|
-
if (!target) {
|
|
32468
|
-
recordUnmapped(plan, source.path, pointer, rawValue, false);
|
|
32469
|
-
return void 0;
|
|
32470
|
-
}
|
|
32471
|
-
const sourceIndex = context.stageIds.indexOf(stageId);
|
|
32472
|
-
if (target === "$terminal") {
|
|
32473
|
-
const chosen = resolveDecision(
|
|
32474
|
-
plan,
|
|
32475
|
-
`terminal:${context.workflowKey}:${stageKey}:${outcome}`,
|
|
32476
|
-
"terminal-status",
|
|
32477
|
-
source.path,
|
|
32478
|
-
`legacy transition ${context.workflowId}/${stageId} outcome ${outcome} ends the run; choose the KXM terminal status (legacy semantics completed the run even on failure outcomes)`,
|
|
32479
|
-
outcome === "passed" ? ["completed"] : ["completed", "failed", "cancelled"],
|
|
32480
|
-
decisions
|
|
32481
|
-
);
|
|
32482
|
-
const status = typeof chosen === "string" && ["completed", "failed", "cancelled"].includes(chosen) ? chosen : "completed";
|
|
32483
|
-
const objectRule2 = asObject2(rawValue);
|
|
32484
|
-
const result = { target: "$terminal", terminalStatus: status };
|
|
32485
|
-
if (typeof objectRule2?.maxTransitions === "number") {
|
|
32486
|
-
if (Number.isInteger(objectRule2.maxTransitions) && objectRule2.maxTransitions >= 1 && objectRule2.maxTransitions <= 100) {
|
|
32487
|
-
result.maxTransitions = objectRule2.maxTransitions;
|
|
32488
|
-
} else {
|
|
32489
|
-
recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, objectRule2.maxTransitions, false);
|
|
32490
|
-
}
|
|
32491
|
-
}
|
|
32492
|
-
return result;
|
|
32493
|
-
}
|
|
32494
|
-
const targetId = mapStageRef(context, target);
|
|
32495
|
-
const targetIndex = context.stageIds.indexOf(targetId);
|
|
32496
|
-
if (targetIndex < 0) {
|
|
32497
|
-
resolveDecision(
|
|
32498
|
-
plan,
|
|
32499
|
-
`transition-target:${context.workflowKey}:${stageKey}:${outcome}`,
|
|
32500
|
-
"identity",
|
|
32501
|
-
source.path,
|
|
32502
|
-
`transition target ${JSON.stringify(elide(target, 200))} is not a stage of workflow ${context.workflowId}; confirm the edge is dropped`,
|
|
32503
|
-
["drop"],
|
|
32504
|
-
decisions
|
|
32505
|
-
);
|
|
32506
|
-
recordUnmapped(plan, source.path, pointer, rawValue, false);
|
|
32507
|
-
return void 0;
|
|
32508
|
-
}
|
|
32509
|
-
const objectRule = asObject2(rawValue);
|
|
32510
|
-
let explicitBudget;
|
|
32511
|
-
if (typeof objectRule?.maxTransitions === "number") {
|
|
32512
|
-
if (Number.isInteger(objectRule.maxTransitions) && objectRule.maxTransitions >= 1 && objectRule.maxTransitions <= 100) {
|
|
32513
|
-
explicitBudget = objectRule.maxTransitions;
|
|
32514
|
-
} else {
|
|
32515
|
-
recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, objectRule.maxTransitions, false);
|
|
32516
|
-
}
|
|
32517
|
-
}
|
|
32518
|
-
const isBackEdge = targetIndex <= sourceIndex;
|
|
32519
|
-
if (isBackEdge && explicitBudget === void 0) {
|
|
32520
|
-
const chosen = resolveDecision(
|
|
32521
|
-
plan,
|
|
32522
|
-
`backedge:${context.workflowKey}:${stageKey}:${outcome}`,
|
|
32523
|
-
"transition-budget",
|
|
32524
|
-
source.path,
|
|
32525
|
-
`back-edge ${context.workflowId}/${stageId} -> ${targetId} (outcome ${outcome}) has no per-edge budget in legacy; KXM requires maxTransitions (1-100)`,
|
|
32526
|
-
[2, 3, 4, 5],
|
|
32527
|
-
decisions
|
|
32528
|
-
);
|
|
32529
|
-
const budget = typeof chosen === "number" && Number.isInteger(chosen) && chosen >= 1 && chosen <= 100 ? chosen : 2;
|
|
32530
|
-
return { target: targetId, maxTransitions: budget };
|
|
32531
|
-
}
|
|
32532
|
-
if (explicitBudget !== void 0) return { target: targetId, maxTransitions: explicitBudget };
|
|
32533
|
-
return targetId;
|
|
32534
|
-
}
|
|
32535
|
-
function convertWorkflowFile(plan, source, decisions, agentIds, agentRawNames, pendingAgentRawNames, rosterIdentityPending, workflowNames) {
|
|
32536
|
-
const workflows = /* @__PURE__ */ new Map();
|
|
32537
|
-
const list = Array.isArray(source.value) ? source.value : [source.value];
|
|
32538
|
-
for (const [definitionIndex, entry] of list.entries()) {
|
|
32539
|
-
const definition = asObject2(entry);
|
|
32540
|
-
const basePointer = Array.isArray(source.value) ? `/${definitionIndex}` : "";
|
|
32541
|
-
if (!definition) {
|
|
32542
|
-
recordUnmapped(plan, source.path, basePointer || "/", entry, false);
|
|
32543
|
-
continue;
|
|
32544
|
-
}
|
|
32545
|
-
recordUnknownFields(plan, source.path, basePointer, definition, KNOWN_WORKFLOW_FIELDS);
|
|
32546
|
-
const fileStem = source.path.slice(source.path.lastIndexOf("/") + 1).replace(/\.json$/u, "");
|
|
32547
|
-
const rawId = asString(definition.id) ?? `${fileStem}-${definitionIndex}`;
|
|
32548
|
-
const workflowIdentity = mapIdentity(plan, source.path, rawId, "workflow", workflowNames, decisions, basePointer || "/", definition);
|
|
32549
|
-
if (workflowIdentity.status === "dropped") continue;
|
|
32550
|
-
const workflowId = workflowIdentity.status === "adopted" ? workflowIdentity.id : `preview-${(normalizeIdentifier(rawId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash13("sha256").update(`${rawId}#${source.path}#${definitionIndex}`, "utf8").digest("hex").slice(0, 8)}`;
|
|
32551
|
-
const workflowKey = keyFragment(rawId);
|
|
32552
|
-
for (const field of ["source", "project", "event", "filter", "delivery", "ttlMs", "promptTemplate"]) {
|
|
32553
|
-
if (definition[field] !== void 0) recordUnmapped(plan, source.path, `${basePointer}/${field}`, definition[field], false);
|
|
32554
|
-
}
|
|
32555
|
-
for (const field of ["secret", "signalSecret", "secretEnv", "signalSecretEnv"]) {
|
|
32556
|
-
if (definition[field] !== void 0) {
|
|
32557
|
-
recordUnmapped(plan, source.path, `${basePointer}/${field}`, definition[field], true);
|
|
32558
|
-
resolveDecision(
|
|
32559
|
-
plan,
|
|
32560
|
-
`secret:${workflowKey}:${field}`,
|
|
32561
|
-
"secret-reference",
|
|
32562
|
-
source.path,
|
|
32563
|
-
`workflow ${workflowId} field ${field} configures a webhook secret; KXM workflows never embed secrets \u2014 confirm the field is dropped and re-granted through scoped secret references when the trigger surface lands`,
|
|
32564
|
-
["drop"],
|
|
32565
|
-
decisions
|
|
32566
|
-
);
|
|
32567
|
-
}
|
|
32568
|
-
}
|
|
32569
|
-
const rawTarget = asString(definition.target);
|
|
32570
|
-
const normalizedTarget = rawTarget ? normalizeIdentifier(rawTarget) : void 0;
|
|
32571
|
-
let stepAgent = rawTarget !== void 0 ? agentRawNames.get(rawTarget) : void 0;
|
|
32572
|
-
if (!stepAgent && rawTarget !== void 0 && pendingAgentRawNames.has(rawTarget)) {
|
|
32573
|
-
stepAgent = void 0;
|
|
32574
|
-
} else if (!stepAgent && rawTarget !== void 0) {
|
|
32575
|
-
const foldedTarget = rawTarget.toLocaleLowerCase("en-US");
|
|
32576
|
-
for (const [rawName, id] of agentRawNames) {
|
|
32577
|
-
if (rawName.toLocaleLowerCase("en-US") === foldedTarget) {
|
|
32578
|
-
stepAgent = id;
|
|
32579
|
-
break;
|
|
32580
|
-
}
|
|
32581
|
-
}
|
|
32582
|
-
}
|
|
32583
|
-
if (!stepAgent && agentRawNames.size === 0 && normalizedTarget && agentIds.has(normalizedTarget)) {
|
|
32584
|
-
stepAgent = normalizedTarget;
|
|
32585
|
-
}
|
|
32586
|
-
const targetIsPendingAgent = rawTarget !== void 0 && [...pendingAgentRawNames].some((pending) => pending === rawTarget || pending.toLocaleLowerCase("en-US") === rawTarget.toLocaleLowerCase("en-US"));
|
|
32587
|
-
if (!stepAgent && targetIsPendingAgent) {
|
|
32588
|
-
stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
|
|
32589
|
-
}
|
|
32590
|
-
if (!stepAgent) {
|
|
32591
|
-
if (agentIds.size === 0 && rosterIdentityPending) {
|
|
32592
|
-
stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
|
|
32593
|
-
} else if (agentIds.size === 0) {
|
|
32594
|
-
const dropped = resolveDecision(
|
|
32595
|
-
plan,
|
|
32596
|
-
`coordinator:${workflowKey}`,
|
|
32597
|
-
"identity",
|
|
32598
|
-
source.path,
|
|
32599
|
-
`workflow ${workflowId} target ${JSON.stringify(rawTarget === void 0 ? "" : elide(rawTarget, 200))} matches no migrated agent and the roster is empty; confirm the workflow is dropped`,
|
|
32600
|
-
["drop"],
|
|
32601
|
-
decisions
|
|
32602
|
-
);
|
|
32603
|
-
if (dropped === "drop") continue;
|
|
32604
|
-
stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
|
|
32605
|
-
} else {
|
|
32606
|
-
const chosen = resolveDecision(
|
|
32607
|
-
plan,
|
|
32608
|
-
`coordinator:${workflowKey}`,
|
|
32609
|
-
"identity",
|
|
32610
|
-
source.path,
|
|
32611
|
-
`workflow ${workflowId} target ${JSON.stringify(rawTarget === void 0 ? "" : elide(rawTarget, 200))} is not a migrated agent; choose the roster agent that executes its steps`,
|
|
32612
|
-
[...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64),
|
|
32613
|
-
decisions
|
|
32614
|
-
);
|
|
32615
|
-
if (typeof chosen === "string" && agentIds.has(chosen)) {
|
|
32616
|
-
stepAgent = chosen;
|
|
32617
|
-
if (rawTarget) plan.renames.push({ from: elide(rawTarget), to: chosen, kind: "agent" });
|
|
32618
|
-
} else {
|
|
32619
|
-
if (chosen !== void 0 && !plan.invalidDecisions.includes(`coordinator:${workflowKey}`)) {
|
|
32620
|
-
plan.invalidDecisions.push(`coordinator:${workflowKey}`);
|
|
32621
|
-
}
|
|
32622
|
-
if (chosen !== void 0 && !plan.ambiguities.some((ambiguity) => ambiguity.key === `coordinator:${workflowKey}`)) {
|
|
32623
|
-
plan.ambiguities.push({
|
|
32624
|
-
key: `coordinator:${workflowKey}`,
|
|
32625
|
-
category: "identity",
|
|
32626
|
-
sourcePath: source.path,
|
|
32627
|
-
message: `decision value ${JSON.stringify(typeof chosen === "string" ? elide(chosen, 200) : chosen)} for workflow ${workflowId} is not a migrated agent`,
|
|
32628
|
-
allowedValues: [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64)
|
|
32629
|
-
});
|
|
32630
|
-
}
|
|
32631
|
-
stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
|
|
32632
|
-
}
|
|
32633
|
-
}
|
|
32634
|
-
}
|
|
32635
|
-
const rawStages = Array.isArray(definition.stages) ? definition.stages : [];
|
|
32636
|
-
const stageIds = [];
|
|
32637
|
-
const stageRename = /* @__PURE__ */ new Map();
|
|
32638
|
-
const stepIdByIndex = /* @__PURE__ */ new Map();
|
|
32639
|
-
const stageKeyByRaw = /* @__PURE__ */ new Map();
|
|
32640
|
-
const droppedStageIndexes = /* @__PURE__ */ new Set();
|
|
32641
|
-
const takenSteps = /* @__PURE__ */ new Map();
|
|
32642
|
-
for (const [stageIndex, stageEntry] of rawStages.entries()) {
|
|
32643
|
-
const stage = asObject2(stageEntry);
|
|
32644
|
-
if (!stage) {
|
|
32645
|
-
recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stageEntry, false);
|
|
32646
|
-
droppedStageIndexes.add(stageIndex);
|
|
32647
|
-
continue;
|
|
32648
|
-
}
|
|
32649
|
-
const rawStageId = asString(stage.id);
|
|
32650
|
-
if (!rawStageId) {
|
|
32651
|
-
recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stage, false);
|
|
32652
|
-
droppedStageIndexes.add(stageIndex);
|
|
32653
|
-
continue;
|
|
32654
|
-
}
|
|
32655
|
-
const stepIdentity = mapIdentity(plan, source.path, rawStageId, "step", takenSteps, decisions, `${basePointer}/stages/${stageIndex}`, stage);
|
|
32656
|
-
if (stepIdentity.status === "dropped") {
|
|
32657
|
-
droppedStageIndexes.add(stageIndex);
|
|
32658
|
-
continue;
|
|
32659
|
-
}
|
|
32660
|
-
const stepId = stepIdentity.status === "adopted" ? stepIdentity.id : `preview-${(normalizeIdentifier(rawStageId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash13("sha256").update(`${rawStageId}#${stageIndex}`, "utf8").digest("hex").slice(0, 8)}`;
|
|
32661
|
-
const stepKey = keyFragment(rawStageId);
|
|
32662
|
-
stageKeyByRaw.set(rawStageId, stepKey);
|
|
32663
|
-
if (!asString(stage.instructions)) {
|
|
32664
|
-
resolveDecision(
|
|
32665
|
-
plan,
|
|
32666
|
-
`instructions:${workflowKey}:${stepKey}`,
|
|
32667
|
-
"unsupported",
|
|
32668
|
-
source.path,
|
|
32669
|
-
`stage ${stepId} has no instructions; KXM agent steps require executable instructions \u2014 confirm the stage is dropped`,
|
|
32670
|
-
["drop"],
|
|
32671
|
-
decisions
|
|
32672
|
-
);
|
|
32673
|
-
recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stage, false);
|
|
32674
|
-
droppedStageIndexes.add(stageIndex);
|
|
32675
|
-
continue;
|
|
32676
|
-
}
|
|
32677
|
-
if (!stageRename.has(rawStageId)) stageRename.set(rawStageId, stepId);
|
|
32678
|
-
stepIdByIndex.set(stageIndex, stepId);
|
|
32679
|
-
stageIds.push(stepId);
|
|
32680
|
-
}
|
|
32681
|
-
const context = { workflowId, workflowKey, stageIds, renamesByKind: stageRename };
|
|
32682
|
-
const steps = [];
|
|
32683
|
-
for (const [stageIndex, stageEntry] of rawStages.entries()) {
|
|
32684
|
-
if (droppedStageIndexes.has(stageIndex)) continue;
|
|
32685
|
-
const stage = asObject2(stageEntry);
|
|
32686
|
-
if (!stage) continue;
|
|
32687
|
-
const rawStageId = asString(stage.id);
|
|
32688
|
-
if (!rawStageId) continue;
|
|
32689
|
-
const stepId = stepIdByIndex.get(stageIndex);
|
|
32690
|
-
if (!stepId) continue;
|
|
32691
|
-
const stepKey = stageKeyByRaw.get(rawStageId) ?? keyFragment(rawStageId);
|
|
32692
|
-
const pointer = `${basePointer}/stages/${stageIndex}`;
|
|
32693
|
-
recordUnknownFields(plan, source.path, pointer, stage, KNOWN_STAGE_FIELDS);
|
|
32694
|
-
const label = asString(stage.label);
|
|
32695
|
-
const instructions = asString(stage.instructions);
|
|
32696
|
-
if (!instructions) continue;
|
|
32697
|
-
const step = {
|
|
32698
|
-
id: stepId,
|
|
32699
|
-
kind: "agent",
|
|
32700
|
-
agent: stepAgent
|
|
32701
|
-
};
|
|
32702
|
-
if (label) step.description = label;
|
|
32703
|
-
let maxAttempts = 3;
|
|
32704
|
-
if (typeof stage.maxAttempts === "number" && Number.isInteger(stage.maxAttempts) && stage.maxAttempts >= 1 && stage.maxAttempts <= 20) {
|
|
32705
|
-
maxAttempts = stage.maxAttempts;
|
|
32706
|
-
} else if (stage.maxAttempts !== void 0) {
|
|
32707
|
-
recordUnmapped(plan, source.path, `${pointer}/maxAttempts`, stage.maxAttempts, false);
|
|
32708
|
-
}
|
|
32709
|
-
step.maxAttempts = maxAttempts;
|
|
32710
|
-
step.instructions = instructions;
|
|
32711
|
-
const requiredEvidence = [];
|
|
32712
|
-
const policyProducers = /* @__PURE__ */ new Set();
|
|
32713
|
-
let policyProducerMinimum = 0;
|
|
32714
|
-
const rawEvidence = Array.isArray(stage.requiredEvidence) ? stage.requiredEvidence : [];
|
|
32715
|
-
const seenKeys = /* @__PURE__ */ new Set();
|
|
32716
|
-
for (const [evidenceIndex, evidenceEntry] of rawEvidence.entries()) {
|
|
32717
|
-
const rawKey = asString(evidenceEntry);
|
|
32718
|
-
if (!rawKey) {
|
|
32719
|
-
recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
|
|
32720
|
-
continue;
|
|
32721
|
-
}
|
|
32722
|
-
const key = normalizeIdentifier(rawKey);
|
|
32723
|
-
if (!IDENTIFIER2.test(key) || key.length > 64) {
|
|
32724
|
-
recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
|
|
32725
|
-
continue;
|
|
32726
|
-
}
|
|
32727
|
-
if (seenKeys.has(key)) {
|
|
32728
|
-
recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
|
|
32729
|
-
continue;
|
|
32730
|
-
}
|
|
32731
|
-
if (key !== rawKey) plan.renames.push({ from: elide(rawKey), to: key, kind: "evidence" });
|
|
32732
|
-
seenKeys.add(key);
|
|
32733
|
-
requiredEvidence.push({ key, kind: "assignment-result" });
|
|
32734
|
-
}
|
|
32735
|
-
const policies = asObject2(stage.evidencePolicies);
|
|
32736
|
-
if (policies) {
|
|
32737
|
-
const seenPolicies = /* @__PURE__ */ new Set();
|
|
32738
|
-
for (const [rawPolicyKey, policyEntry] of Object.entries(policies).sort(([left], [right]) => compareCodeUnits6(left, right))) {
|
|
32739
|
-
const policy = asObject2(policyEntry);
|
|
32740
|
-
const policyKey = normalizeIdentifier(rawPolicyKey);
|
|
32741
|
-
if (seenPolicies.has(policyKey)) {
|
|
32742
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
|
|
32743
|
-
continue;
|
|
32744
|
-
}
|
|
32745
|
-
seenPolicies.add(policyKey);
|
|
32746
|
-
const requirement = requiredEvidence.find((candidate) => candidate.key === policyKey);
|
|
32747
|
-
if (!policy || !requirement) {
|
|
32748
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
|
|
32749
|
-
continue;
|
|
32750
|
-
}
|
|
32751
|
-
recordUnknownFields(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policy, KNOWN_POLICY_FIELDS);
|
|
32752
|
-
let minProducers = 1;
|
|
32753
|
-
if (typeof policy.minProducers === "number" && Number.isInteger(policy.minProducers) && policy.minProducers >= 1 && policy.minProducers <= 16) {
|
|
32754
|
-
minProducers = policy.minProducers;
|
|
32755
|
-
} else if (policy.minProducers !== void 0) {
|
|
32756
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/minProducers`, policy.minProducers, false);
|
|
32757
|
-
}
|
|
32758
|
-
if (policy.eligibleAgents !== void 0 && !Array.isArray(policy.eligibleAgents)) {
|
|
32759
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents`, policy.eligibleAgents, false);
|
|
32760
|
-
}
|
|
32761
|
-
const eligible = Array.isArray(policy.eligibleAgents) ? policy.eligibleAgents : [];
|
|
32762
|
-
const eligibleIds = [];
|
|
32763
|
-
for (const [eligibleIndex, candidate] of eligible.entries()) {
|
|
32764
|
-
const rawName = asString(candidate);
|
|
32765
|
-
if (!rawName) {
|
|
32766
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents/${eligibleIndex}`, candidate, false);
|
|
32767
|
-
continue;
|
|
32768
|
-
}
|
|
32769
|
-
const rawBound = pendingAgentRawNames.has(rawName) ? void 0 : agentRawNames.get(rawName) ?? [...agentRawNames].find(([candidateName]) => candidateName.toLocaleLowerCase("en-US") === rawName.toLocaleLowerCase("en-US"))?.[1];
|
|
32770
|
-
if (rawBound) {
|
|
32771
|
-
if (!eligibleIds.includes(rawBound)) eligibleIds.push(rawBound);
|
|
32772
|
-
continue;
|
|
32773
|
-
}
|
|
32774
|
-
if ([...pendingAgentRawNames].some((pending) => pending === rawName || pending.toLocaleLowerCase("en-US") === rawName.toLocaleLowerCase("en-US"))) {
|
|
32775
|
-
const previewId = normalizeIdentifier(rawName);
|
|
32776
|
-
if (IDENTIFIER2.test(previewId) && previewId.length <= 64 && !eligibleIds.includes(previewId)) {
|
|
32777
|
-
eligibleIds.push(previewId);
|
|
32778
|
-
}
|
|
32779
|
-
continue;
|
|
32780
|
-
}
|
|
32781
|
-
const normalized = normalizeIdentifier(rawName);
|
|
32782
|
-
if (agentRawNames.size === 0 && agentIds.has(normalized)) {
|
|
32783
|
-
if (!eligibleIds.includes(normalized)) eligibleIds.push(normalized);
|
|
32784
|
-
continue;
|
|
32785
|
-
}
|
|
32786
|
-
if (agentIds.size === 0) {
|
|
32787
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents/${eligibleIndex}`, candidate, false);
|
|
32788
|
-
continue;
|
|
32789
|
-
}
|
|
32790
|
-
const mapped = resolveDecision(
|
|
32791
|
-
plan,
|
|
32792
|
-
`producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`,
|
|
32793
|
-
"evidence-policy",
|
|
32794
|
-
source.path,
|
|
32795
|
-
`evidence policy on ${stepId}.${policyKey} names producer ${JSON.stringify(elide(rawName, 200))} which is not a migrated agent; choose the roster agent that takes this producer role`,
|
|
32796
|
-
[...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64),
|
|
32797
|
-
decisions
|
|
32798
|
-
);
|
|
32799
|
-
if (typeof mapped === "string" && agentIds.has(mapped)) {
|
|
32800
|
-
plan.renames.push({ from: elide(rawName), to: mapped, kind: "agent" });
|
|
32801
|
-
if (!eligibleIds.includes(mapped)) eligibleIds.push(mapped);
|
|
32802
|
-
} else if (mapped !== void 0) {
|
|
32803
|
-
if (!plan.invalidDecisions.includes(`producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`)) {
|
|
32804
|
-
plan.invalidDecisions.push(`producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`);
|
|
32805
|
-
}
|
|
32806
|
-
if (!plan.ambiguities.some((ambiguity) => ambiguity.key === `producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`)) {
|
|
32807
|
-
plan.ambiguities.push({
|
|
32808
|
-
key: `producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`,
|
|
32809
|
-
category: "evidence-policy",
|
|
32810
|
-
sourcePath: source.path,
|
|
32811
|
-
message: `decision value ${JSON.stringify(typeof mapped === "string" ? elide(mapped, 200) : mapped)} for producer ${JSON.stringify(elide(rawName, 200))} is not a migrated agent`,
|
|
32812
|
-
allowedValues: [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64)
|
|
32813
|
-
});
|
|
32814
|
-
}
|
|
32815
|
-
}
|
|
32816
|
-
}
|
|
32817
|
-
const acceptedStatusesMalformed = policy.acceptedStatuses !== void 0 && !Array.isArray(policy.acceptedStatuses);
|
|
32818
|
-
if (acceptedStatusesMalformed) {
|
|
32819
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/acceptedStatuses`, policy.acceptedStatuses, false);
|
|
32820
|
-
}
|
|
32821
|
-
const accepted = acceptedStatusesMalformed ? [] : Array.isArray(policy.acceptedStatuses) ? policy.acceptedStatuses : ["replied"];
|
|
32822
|
-
if (Array.isArray(policy.acceptedStatuses) && policy.acceptedStatuses.length === 0) {
|
|
32823
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/acceptedStatuses`, policy.acceptedStatuses, false);
|
|
32824
|
-
}
|
|
32825
|
-
let acceptedStatuses = accepted.length > 0 && accepted.every((status) => status === "replied") ? ["passed"] : void 0;
|
|
32826
|
-
if (!acceptedStatuses) {
|
|
32827
|
-
const chosen = resolveDecision(
|
|
32828
|
-
plan,
|
|
32829
|
-
`statuses:${workflowKey}:${stepKey}:${policyKey}`,
|
|
32830
|
-
"evidence-policy",
|
|
32831
|
-
source.path,
|
|
32832
|
-
acceptedStatusesMalformed ? `evidence policy ${policyKey} acceptedStatuses is malformed (not an array) and was preserved in the report; confirm mapping the policy to KXM 'passed'` : `evidence policy ${policyKey} accepted statuses ${JSON.stringify(elide(accepted.map((candidate) => String(candidate)).join(","), 200))} cannot map losslessly; confirm mapping to KXM 'passed'`,
|
|
32833
|
-
["passed"],
|
|
32834
|
-
decisions
|
|
32835
|
-
);
|
|
32836
|
-
if (chosen === "passed") acceptedStatuses = ["passed"];
|
|
32837
|
-
}
|
|
32838
|
-
if (acceptedStatuses) {
|
|
32839
|
-
plan.permissionChanges.push({
|
|
32840
|
-
sourcePath: source.path,
|
|
32841
|
-
targetPath: `.kxm/workflows/${workflowId}.yaml`,
|
|
32842
|
-
direction: "narrowing",
|
|
32843
|
-
summary: `evidence ${policyKey} on ${stepId}: legacy peer-reply policy (status 'replied') migrates to assignment-result producer policy requiring 'passed'`,
|
|
32844
|
-
decisionKey: `evidence:${workflowKey}:${stepKey}:${policyKey}`
|
|
32845
|
-
});
|
|
32846
|
-
plan.permissionChanges.push({
|
|
32847
|
-
sourcePath: source.path,
|
|
32848
|
-
targetPath: `.kxm/workflows/${workflowId}.yaml`,
|
|
32849
|
-
direction: "expansion",
|
|
32850
|
-
summary: `step ${stepId}: assignment pool widens from the single legacy coordinator to coordinator plus evidence producers so KXM producer evidence stays in scope`,
|
|
32851
|
-
decisionKey: `evidence:${workflowKey}:${stepKey}:${policyKey}`
|
|
32852
|
-
});
|
|
32853
|
-
resolveDecision(
|
|
32854
|
-
plan,
|
|
32855
|
-
`evidence:${workflowKey}:${stepKey}:${policyKey}`,
|
|
32856
|
-
"evidence-policy",
|
|
32857
|
-
source.path,
|
|
32858
|
-
`approve strengthening ${stepId}.${policyKey} from 'peer replied' to 'peer assignment passed' and widening the step assignment pool to include the producers`,
|
|
32859
|
-
["approve"],
|
|
32860
|
-
decisions
|
|
32861
|
-
);
|
|
32862
|
-
}
|
|
32863
|
-
if (eligibleIds.length > 0 && acceptedStatuses) {
|
|
32864
|
-
requirement.kind = "assignment-result";
|
|
32865
|
-
requirement.producerPolicy = {
|
|
32866
|
-
minimumProducers: minProducers,
|
|
32867
|
-
eligibleAgents: eligibleIds,
|
|
32868
|
-
acceptedStatuses
|
|
32869
|
-
};
|
|
32870
|
-
for (const producer of eligibleIds) policyProducers.add(producer);
|
|
32871
|
-
policyProducerMinimum = Math.max(policyProducerMinimum, minProducers);
|
|
32872
|
-
const degradation = asObject2(policy.degradation);
|
|
32873
|
-
if (degradation && degradation.minProducers !== void 0) {
|
|
32874
|
-
if (typeof degradation.minProducers === "number" && Number.isInteger(degradation.minProducers) && degradation.minProducers >= 1 && degradation.minProducers <= 15 && degradation.minProducers < minProducers) {
|
|
32875
|
-
requirement.producerPolicy.degradation = { minimumProducers: degradation.minProducers };
|
|
32876
|
-
} else {
|
|
32877
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/degradation`, policy.degradation, false);
|
|
32878
|
-
}
|
|
32879
|
-
}
|
|
32880
|
-
} else {
|
|
32881
|
-
recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
|
|
32882
|
-
}
|
|
32883
|
-
}
|
|
32884
|
-
}
|
|
32885
|
-
if (requiredEvidence.length > 0) step.requiredEvidence = requiredEvidence;
|
|
32886
|
-
if (policyProducers.size > 0) {
|
|
32887
|
-
const pool = [stepAgent, ...[...policyProducers].filter((candidate) => candidate !== stepAgent).sort(compareCodeUnits6)];
|
|
32888
|
-
step.assignments = {
|
|
32889
|
-
allowedAgents: pool,
|
|
32890
|
-
minimum: 1,
|
|
32891
|
-
target: Math.max(1, policyProducerMinimum),
|
|
32892
|
-
maximum: pool.length,
|
|
32893
|
-
maxParallel: pool.length
|
|
32894
|
-
};
|
|
32895
|
-
}
|
|
32896
|
-
const on = asObject2(stage.on);
|
|
32897
|
-
const transitions = {};
|
|
32898
|
-
if (on) {
|
|
32899
|
-
const seenOutcomes = /* @__PURE__ */ new Set();
|
|
32900
|
-
for (const [rawOutcome, rawTransition] of Object.entries(on).sort(([left], [right]) => compareCodeUnits6(left, right))) {
|
|
32901
|
-
const outcome = normalizeIdentifier(rawOutcome);
|
|
32902
|
-
if (!IDENTIFIER2.test(outcome) || outcome.length > 64) {
|
|
32903
|
-
recordUnmapped(plan, source.path, `${pointer}/on/${pointerEscape2(rawOutcome)}`, rawTransition, false);
|
|
32904
|
-
continue;
|
|
32905
|
-
}
|
|
32906
|
-
if (seenOutcomes.has(outcome)) {
|
|
32907
|
-
recordUnmapped(plan, source.path, `${pointer}/on/${pointerEscape2(rawOutcome)}`, rawTransition, false);
|
|
32908
|
-
continue;
|
|
32909
|
-
}
|
|
32910
|
-
seenOutcomes.add(outcome);
|
|
32911
|
-
if (outcome !== rawOutcome) plan.renames.push({ from: elide(rawOutcome), to: outcome, kind: "outcome" });
|
|
32912
|
-
const converted = convertTransition(plan, source, context, stepId, stepKey, outcome, rawOutcome, rawTransition, decisions, pointer);
|
|
32913
|
-
if (converted !== void 0) transitions[outcome] = converted;
|
|
32914
|
-
}
|
|
32915
|
-
if (transitions.passed === void 0) {
|
|
32916
|
-
const nextId = stageIds[stageIds.indexOf(stepId) + 1];
|
|
32917
|
-
transitions.passed = nextId ?? { target: "$terminal", terminalStatus: "completed" };
|
|
32918
|
-
}
|
|
32919
|
-
} else {
|
|
32920
|
-
const nextId = stageIds[stageIds.indexOf(stepId) + 1];
|
|
32921
|
-
transitions.passed = nextId ?? { target: "$terminal", terminalStatus: "completed" };
|
|
32922
|
-
}
|
|
32923
|
-
step.on = transitions;
|
|
32924
|
-
if (typeof stage.maxTransitions === "number") {
|
|
32925
|
-
recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, stage.maxTransitions, false);
|
|
32926
|
-
}
|
|
32927
|
-
if (stage.area !== void 0) recordUnmapped(plan, source.path, `${pointer}/area`, stage.area, false);
|
|
32928
|
-
steps.push(step);
|
|
32929
|
-
}
|
|
32930
|
-
if (steps.length === 0) {
|
|
32931
|
-
resolveDecision(
|
|
32932
|
-
plan,
|
|
32933
|
-
`stages:${workflowKey}`,
|
|
32934
|
-
"unsupported",
|
|
32935
|
-
source.path,
|
|
32936
|
-
`workflow ${workflowId} produced no convertible steps; confirm it is dropped`,
|
|
32937
|
-
["drop"],
|
|
32938
|
-
decisions
|
|
32939
|
-
);
|
|
32940
|
-
continue;
|
|
32941
|
-
}
|
|
32942
|
-
const workflow = {
|
|
32943
|
-
schema: "kxm.workflow.v1",
|
|
32944
|
-
coordinator: stepAgent,
|
|
32945
|
-
steps
|
|
32946
|
-
};
|
|
32947
|
-
if (typeof definition.maxTransitions === "number" && Number.isInteger(definition.maxTransitions) && definition.maxTransitions >= 1 && definition.maxTransitions <= 1e3) {
|
|
32948
|
-
workflow.limits = { maxTransitions: definition.maxTransitions };
|
|
32949
|
-
} else if (definition.maxTransitions !== void 0) {
|
|
32950
|
-
recordUnmapped(plan, source.path, `${basePointer}/maxTransitions`, definition.maxTransitions, false);
|
|
32951
|
-
}
|
|
32952
|
-
if (workflow.limits === void 0) {
|
|
32953
|
-
const hasBackEdge = steps.some((step) => Object.values(asObject2(step.on) ?? {}).some((rule) => {
|
|
32954
|
-
const target = typeof rule === "string" ? rule : asObject2(rule)?.target;
|
|
32955
|
-
return typeof target === "string" && target !== "$terminal" && stageIds.indexOf(target) <= stageIds.indexOf(String(step.id));
|
|
32956
|
-
}));
|
|
32957
|
-
if (hasBackEdge) {
|
|
32958
|
-
const chosen = resolveDecision(
|
|
32959
|
-
plan,
|
|
32960
|
-
`budget:${workflowKey}`,
|
|
32961
|
-
"transition-budget",
|
|
32962
|
-
source.path,
|
|
32963
|
-
`workflow ${workflowId} has back-edges but no legacy global maxTransitions; KXM requires limits.maxTransitions (1-1000)`,
|
|
32964
|
-
[28],
|
|
32965
|
-
decisions
|
|
32966
|
-
);
|
|
32967
|
-
workflow.limits = { maxTransitions: typeof chosen === "number" && Number.isInteger(chosen) && chosen >= 1 && chosen <= 1e3 ? chosen : 28 };
|
|
32968
|
-
}
|
|
32969
|
-
}
|
|
32970
|
-
for (const oracleField of ["reproOracle", "planHash"]) {
|
|
32971
|
-
const oracle = asObject2(definition[oracleField]);
|
|
32972
|
-
if (!oracle) continue;
|
|
32973
|
-
const oracleStage = asString(oracle.stageId);
|
|
32974
|
-
const oracleEvidence = asString(oracle.evidenceKey);
|
|
32975
|
-
if (!oracleStage || !oracleEvidence) {
|
|
32976
|
-
recordUnmapped(plan, source.path, `${basePointer}/${oracleField}`, definition[oracleField], false);
|
|
32977
|
-
continue;
|
|
32978
|
-
}
|
|
32979
|
-
const mappedStage = context.renamesByKind.get(oracleStage);
|
|
32980
|
-
if (!mappedStage) {
|
|
32981
|
-
if (!context.stageIds.includes(normalizeIdentifier(oracleStage))) {
|
|
32982
|
-
recordUnmapped(plan, source.path, `${basePointer}/${oracleField}`, definition[oracleField], false);
|
|
32983
|
-
continue;
|
|
32984
|
-
}
|
|
32985
|
-
}
|
|
32986
|
-
workflow[oracleField] = {
|
|
32987
|
-
stageId: mappedStage ?? normalizeIdentifier(oracleStage),
|
|
32988
|
-
evidenceKey: normalizeIdentifier(oracleEvidence)
|
|
32989
|
-
};
|
|
32990
|
-
}
|
|
32991
|
-
if (Array.isArray(definition.requirePlanHash)) {
|
|
32992
|
-
const mapped = [];
|
|
32993
|
-
for (const [requireIndex, candidate] of definition.requirePlanHash.entries()) {
|
|
32994
|
-
const rawStage = asString(candidate);
|
|
32995
|
-
if (!rawStage) {
|
|
32996
|
-
recordUnmapped(plan, source.path, `${basePointer}/requirePlanHash/${requireIndex}`, candidate, false);
|
|
32997
|
-
continue;
|
|
32998
|
-
}
|
|
32999
|
-
const mappedStage = context.renamesByKind.get(rawStage);
|
|
33000
|
-
if (!mappedStage && !context.stageIds.includes(normalizeIdentifier(rawStage))) {
|
|
33001
|
-
recordUnmapped(plan, source.path, `${basePointer}/requirePlanHash/${requireIndex}`, candidate, false);
|
|
33002
|
-
continue;
|
|
33003
|
-
}
|
|
33004
|
-
mapped.push(mappedStage ?? normalizeIdentifier(rawStage));
|
|
33005
|
-
}
|
|
33006
|
-
if (mapped.length > 0) workflow.requirePlanHash = [...new Set(mapped)].sort(compareCodeUnits6);
|
|
33007
|
-
}
|
|
33008
|
-
workflows.set(workflowId, workflow);
|
|
33009
|
-
}
|
|
33010
|
-
return workflows;
|
|
33011
|
-
}
|
|
33012
|
-
var PROJECT_ID2 = /^prj_[A-Za-z0-9][A-Za-z0-9_-]{5,127}$/;
|
|
33013
|
-
function sortedRecord(map, prefix) {
|
|
33014
|
-
return new Map([...map.entries()].sort(([left], [right]) => compareCodeUnits6(left, right)).map(([key, value]) => [`${prefix}${key}.yaml`, value]));
|
|
33015
|
-
}
|
|
33016
|
-
function migrationGitRoot(start) {
|
|
33017
|
-
const root = discoverGitRoot(start);
|
|
33018
|
-
if (!root) {
|
|
33019
|
-
throw new KxmConfigError([migrateIssue("discovery", "git_root_required", ".", "kxm migrate must run inside the authoritative Git worktree")]);
|
|
33020
|
-
}
|
|
33021
|
-
return root;
|
|
33022
|
-
}
|
|
33023
|
-
function planKxmMigration(projectRoot, options = {}) {
|
|
33024
|
-
const root = migrationGitRoot(projectRoot);
|
|
33025
|
-
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
33026
|
-
const sources = readKxmLegacySources(root);
|
|
33027
|
-
if (sources.length === 0) {
|
|
33028
|
-
throw new KxmConfigError([migrateIssue("discovery", "legacy_sources_missing", ".kxm/config", "no legacy configuration files found to migrate")]);
|
|
33029
|
-
}
|
|
33030
|
-
const decisions = options.decisions ?? {};
|
|
33031
|
-
const plan = { ambiguities: [], invalidDecisions: [], decisionKeys: /* @__PURE__ */ new Set(), decisionAllowedValues: /* @__PURE__ */ new Map(), unmapped: [], renames: [], permissionChanges: [] };
|
|
33032
|
-
const agentNames = /* @__PURE__ */ new Map();
|
|
33033
|
-
const workflowNames = /* @__PURE__ */ new Map();
|
|
33034
|
-
const agentsSource = sources.find((source) => source.path === ".kxm/config/agents.json");
|
|
33035
|
-
const gatesSource = sources.find((source) => source.path === ".kxm/config/gates.json");
|
|
33036
|
-
const converted = agentsSource ? convertAgentsRoster(plan, agentsSource, decisions, agentNames) : { agents: /* @__PURE__ */ new Map(), models: /* @__PURE__ */ new Map(), rawNames: /* @__PURE__ */ new Map(), pendingRawNames: /* @__PURE__ */ new Set(), identityPending: false };
|
|
33037
|
-
if (!agentsSource) {
|
|
33038
|
-
resolveDecision(
|
|
33039
|
-
plan,
|
|
33040
|
-
"agents-missing",
|
|
33041
|
-
"unsupported",
|
|
33042
|
-
".kxm/config/agents.json",
|
|
33043
|
-
"no agents.json roster; workflows convert against an empty agent set \u2014 confirm conversion continues",
|
|
33044
|
-
["continue"],
|
|
33045
|
-
decisions
|
|
33046
|
-
);
|
|
33047
|
-
}
|
|
33048
|
-
if (gatesSource) convertGatesRoster(plan, gatesSource, decisions);
|
|
33049
|
-
const workflows = /* @__PURE__ */ new Map();
|
|
33050
|
-
for (const source of sources) {
|
|
33051
|
-
if (!source.path.startsWith(".kxm/config/workflows/")) continue;
|
|
33052
|
-
const convertedWorkflows = convertWorkflowFile(
|
|
33053
|
-
plan,
|
|
33054
|
-
source,
|
|
33055
|
-
decisions,
|
|
33056
|
-
converted.agents,
|
|
33057
|
-
converted.rawNames,
|
|
33058
|
-
converted.pendingRawNames,
|
|
33059
|
-
converted.identityPending,
|
|
33060
|
-
workflowNames
|
|
33061
|
-
);
|
|
33062
|
-
for (const [id, workflow] of convertedWorkflows) workflows.set(id, workflow);
|
|
33063
|
-
}
|
|
33064
|
-
const projectId = options.projectId ?? `prj_mig${sha256Of(legacySourceDigest(sources)).slice(7, 39)}`;
|
|
33065
|
-
if (!PROJECT_ID2.test(projectId)) {
|
|
33066
|
-
throw new KxmConfigError([migrateIssue("semantic", "project_id_invalid", ".kxm/project.yaml", "project ID must satisfy the kxm.project.v1 opaque ID grammar and use the prj_ prefix")]);
|
|
33067
|
-
}
|
|
33068
|
-
const projectName = options.projectName ?? "Migrated legacy project";
|
|
33069
|
-
if (projectName.length < 1 || projectName.length > 120 || /[\u0000\r\n]/.test(projectName)) {
|
|
33070
|
-
throw new KxmConfigError([migrateIssue("semantic", "project_name_invalid", ".kxm/project.yaml", "project name must contain 1-120 characters on one line")]);
|
|
33071
|
-
}
|
|
33072
|
-
const resources = /* @__PURE__ */ new Map();
|
|
33073
|
-
resources.set(".kxm/project.yaml", {
|
|
33074
|
-
schema: "kxm.project.v1",
|
|
33075
|
-
id: projectId,
|
|
33076
|
-
name: projectName,
|
|
33077
|
-
...workflows.size > 0 ? { defaultWorkflow: [...workflows.keys()].sort(compareCodeUnits6)[0] } : {},
|
|
33078
|
-
defaultExecutor: "local",
|
|
33079
|
-
repositories: [{ id: "control", role: "control", required: true, pathHint: "." }],
|
|
33080
|
-
workspace: {
|
|
33081
|
-
dirtySnapshot: {
|
|
33082
|
-
untracked: "ask",
|
|
33083
|
-
dirtySubmodules: "fail"
|
|
33084
|
-
}
|
|
33085
|
-
}
|
|
33086
|
-
});
|
|
33087
|
-
resources.set(".kxm/repo/repo.yaml", {
|
|
33088
|
-
schema: "kxm.repository.v1",
|
|
33089
|
-
projectId,
|
|
33090
|
-
repositoryId: "control",
|
|
33091
|
-
description: "Authoritative project configuration and repository content.",
|
|
33092
|
-
defaultAccess: "write"
|
|
33093
|
-
});
|
|
33094
|
-
for (const [path4, value] of sortedRecord(converted.agents, ".kxm/agents/")) resources.set(path4, value);
|
|
33095
|
-
for (const [path4, value] of sortedRecord(converted.models, ".kxm/models/")) resources.set(path4, value);
|
|
33096
|
-
for (const [path4, value] of sortedRecord(workflows, ".kxm/workflows/")) resources.set(path4, value);
|
|
33097
|
-
const schemaIssues = [];
|
|
33098
|
-
const kindOf = (path4) => path4 === ".kxm/project.yaml" ? "project" : path4.endsWith("repo.yaml") ? "repository" : path4.startsWith(".kxm/agents/") ? "agent" : path4.startsWith(".kxm/models/") ? "model" : "workflow";
|
|
33099
|
-
const idOf = (path4) => path4 === ".kxm/project.yaml" || path4.endsWith("repo.yaml") ? void 0 : path4.slice(path4.lastIndexOf("/") + 1, -".yaml".length);
|
|
33100
|
-
const semanticResources = /* @__PURE__ */ new Map();
|
|
33101
|
-
for (const [path4, value] of [...resources.entries()].sort(([left], [right]) => compareCodeUnits6(left, right))) {
|
|
33102
|
-
const kind = kindOf(path4);
|
|
33103
|
-
schemaIssues.push(...registry.validate(kind, value, path4));
|
|
33104
|
-
const id = idOf(path4);
|
|
33105
|
-
semanticResources.set(path4, kind === "repository" ? { kind, id: "control", value } : { kind, ...id === void 0 ? {} : { id }, value });
|
|
33106
|
-
}
|
|
33107
|
-
if (schemaIssues.length > 0) throw new KxmConfigError(schemaIssues);
|
|
33108
|
-
if (plan.ambiguities.length === 0) {
|
|
33109
|
-
const semanticIssues = validateKxmResources(semanticResources, options);
|
|
33110
|
-
if (semanticIssues.length > 0) throw new KxmConfigError(semanticIssues);
|
|
33111
|
-
}
|
|
33112
|
-
const planRecord = {
|
|
33113
|
-
schema: "kxm.migration-plan.v1",
|
|
33114
|
-
projectId,
|
|
33115
|
-
projectName,
|
|
33116
|
-
sourceDigest: legacySourceDigest(sources),
|
|
33117
|
-
sources: sources.map((source) => ({ path: source.path, sha256: source.sha256, bytes: source.bytes })),
|
|
33118
|
-
resources: [...resources.keys()].sort(compareCodeUnits6).map((path4) => {
|
|
33119
|
-
const bytes = Buffer.from((0, import_yaml12.stringify)(resources.get(path4), { lineWidth: 0 }), "utf8");
|
|
33120
|
-
return { path: path4, sha256: sha256Of(bytes), bytes: bytes.byteLength };
|
|
33121
|
-
}),
|
|
33122
|
-
ambiguities: [...plan.ambiguities].sort((left, right) => compareCodeUnits6(left.key, right.key)).map((ambiguity) => ({
|
|
33123
|
-
key: ambiguity.key,
|
|
33124
|
-
category: ambiguity.category,
|
|
33125
|
-
sourcePath: ambiguity.sourcePath,
|
|
33126
|
-
message: ambiguity.message,
|
|
33127
|
-
allowedValues: [...ambiguity.allowedValues]
|
|
33128
|
-
})),
|
|
33129
|
-
unmapped: [...plan.unmapped].sort((left, right) => compareCodeUnits6(left.sourcePath, right.sourcePath) || compareCodeUnits6(left.jsonPointer, right.jsonPointer)).map((entry) => ({ ...entry })),
|
|
33130
|
-
renames: [...new Map(plan.renames.map((rename) => [JSON.stringify([rename.kind, rename.from, rename.to]), rename])).values()].sort((left, right) => compareCodeUnits6(left.kind, right.kind) || compareCodeUnits6(left.from, right.from)).map((entry) => ({ ...entry })),
|
|
33131
|
-
permissionChanges: [...plan.permissionChanges].sort((left, right) => compareCodeUnits6(left.decisionKey, right.decisionKey)).map((entry) => ({ ...entry })),
|
|
33132
|
-
canApply: plan.ambiguities.length === 0
|
|
33133
|
-
};
|
|
33134
|
-
const planIssues = registry.validateMigrationPlan(planRecord, "kxm.migration-plan.v1");
|
|
33135
|
-
if (planIssues.length > 0) throw new KxmConfigError(planIssues);
|
|
33136
|
-
return {
|
|
33137
|
-
plan: planRecord,
|
|
33138
|
-
resources,
|
|
33139
|
-
ambiguities: plan.ambiguities,
|
|
33140
|
-
decisionKeys: plan.decisionKeys,
|
|
33141
|
-
decisionAllowedValues: plan.decisionAllowedValues,
|
|
33142
|
-
invalidDecisions: plan.invalidDecisions,
|
|
33143
|
-
decisions,
|
|
33144
|
-
sources
|
|
33145
|
-
};
|
|
33146
|
-
}
|
|
33147
|
-
function readKxmMigrationDecisions(path4, schemasDir) {
|
|
33148
|
-
if (!existsSync23(path4)) {
|
|
33149
|
-
throw new KxmConfigError([migrateIssue("discovery", "decisions_missing", path4, "migration decisions file does not exist")]);
|
|
33150
|
-
}
|
|
33151
|
-
const registry = new KxmSchemaRegistry(schemasDir);
|
|
33152
|
-
const value = parseRestrictedYaml2(readFileSync22(path4), path4);
|
|
33153
|
-
const issues = registry.validateMigrationDecision(value, path4);
|
|
33154
|
-
if (issues.length > 0) throw new KxmConfigError(issues);
|
|
33155
|
-
return value;
|
|
33156
|
-
}
|
|
33157
|
-
function resolutionsOf(decisions) {
|
|
33158
|
-
const resolutions = decisions.resolutions;
|
|
33159
|
-
if (!resolutions || typeof resolutions !== "object" || Array.isArray(resolutions)) return {};
|
|
33160
|
-
return { ...resolutions };
|
|
33161
|
-
}
|
|
33162
|
-
function syncDirectory3(path4) {
|
|
33163
|
-
let descriptor;
|
|
33164
|
-
try {
|
|
33165
|
-
descriptor = openSync3(path4, "r");
|
|
33166
|
-
fsyncSync3(descriptor);
|
|
33167
|
-
} catch (error) {
|
|
33168
|
-
const code = error.code;
|
|
33169
|
-
if (process.platform !== "win32" && code !== "EINVAL" && code !== "ENOTSUP" && code !== "EISDIR") throw error;
|
|
33170
|
-
} finally {
|
|
33171
|
-
if (descriptor !== void 0) closeSync3(descriptor);
|
|
33172
|
-
}
|
|
33173
|
-
}
|
|
33174
|
-
function writeDurable2(path4, content) {
|
|
33175
|
-
const temp = `${path4}.kxm-migration-tmp-${process.pid}-${createHash13("sha256").update(`${path4}${Date.now()}${Math.random()}`, "utf8").digest("hex").slice(0, 12)}`;
|
|
33176
|
-
let created = false;
|
|
33177
|
-
try {
|
|
33178
|
-
const descriptor = openSync3(temp, "wx", 384);
|
|
33179
|
-
created = true;
|
|
33180
|
-
try {
|
|
33181
|
-
writeFileSync18(descriptor, content);
|
|
33182
|
-
fsyncSync3(descriptor);
|
|
33183
|
-
} finally {
|
|
33184
|
-
closeSync3(descriptor);
|
|
33185
|
-
}
|
|
33186
|
-
renameSync7(temp, path4);
|
|
33187
|
-
try {
|
|
33188
|
-
chmodSync6(path4, 384);
|
|
33189
|
-
} catch {
|
|
33190
|
-
}
|
|
33191
|
-
syncDirectory3(join26(path4, ".."));
|
|
33192
|
-
} finally {
|
|
33193
|
-
if (created && existsSync23(temp)) rmSync9(temp, { force: true });
|
|
33194
|
-
}
|
|
33195
|
-
}
|
|
33196
|
-
function applyKxmMigration(projectRoot, options = {}) {
|
|
33197
|
-
const root = migrationGitRoot(projectRoot);
|
|
33198
|
-
const storeOptions = {
|
|
33199
|
-
...options.localStateRoot === void 0 ? {} : { stateRoot: options.localStateRoot },
|
|
33200
|
-
...options.schemasDir === void 0 ? {} : { schemasDir: options.schemasDir }
|
|
33201
|
-
};
|
|
33202
|
-
const execute = (lock) => {
|
|
33203
|
-
const receiptAbsolute = join26(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
|
|
33204
|
-
if (existsSync23(receiptAbsolute)) {
|
|
33205
|
-
const stat = lstatSync8(receiptAbsolute);
|
|
33206
|
-
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
33207
|
-
throw new KxmConfigError([migrateIssue("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")]);
|
|
33208
|
-
}
|
|
33209
|
-
const existing = loadKxmProject(root, options);
|
|
33210
|
-
return {
|
|
33211
|
-
action: "already-migrated",
|
|
33212
|
-
projectRoot: root,
|
|
33213
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33214
|
-
configRevision: existing.configRevision,
|
|
33215
|
-
files: []
|
|
33216
|
-
};
|
|
33217
|
-
}
|
|
33218
|
-
const decisionsRecord = options.decisionsFile ? readKxmMigrationDecisions(options.decisionsFile, options.schemasDir) : void 0;
|
|
33219
|
-
const decisions = decisionsRecord ? resolutionsOf(decisionsRecord) : options.decisions ?? {};
|
|
33220
|
-
const first = planKxmMigration(root, { ...options, decisions });
|
|
33221
|
-
if (decisionsRecord) {
|
|
33222
|
-
if (decisionsRecord.projectId !== first.plan.projectId) {
|
|
33223
|
-
throw new KxmConfigError([migrateIssue("semantic", "decision_project_mismatch", options.decisionsFile ?? "<decisions>", "decisions file projectId does not match the recomputed plan")]);
|
|
33224
|
-
}
|
|
33225
|
-
if (decisionsRecord.projectName !== first.plan.projectName) {
|
|
33226
|
-
throw new KxmConfigError([migrateIssue("semantic", "decision_project_mismatch", options.decisionsFile ?? "<decisions>", "decisions file projectName does not match the recomputed plan")]);
|
|
33227
|
-
}
|
|
33228
|
-
if (decisionsRecord.sourceDigest !== first.plan.sourceDigest) {
|
|
33229
|
-
throw new KxmConfigError([migrateIssue("semantic", "decision_source_changed", options.decisionsFile ?? "<decisions>", "legacy sources changed after the decisions file was reviewed")]);
|
|
33230
|
-
}
|
|
33231
|
-
}
|
|
33232
|
-
if (Object.keys(decisions).length > 0) {
|
|
33233
|
-
const universe = planKxmMigration(root, { ...options, decisions: {} });
|
|
33234
|
-
const unknown = [];
|
|
33235
|
-
const invalid = [];
|
|
33236
|
-
for (const [key, value] of Object.entries(decisions)) {
|
|
33237
|
-
if (first.decisionKeys.has(key)) continue;
|
|
33238
|
-
const universeAllowed = universe.decisionAllowedValues.get(key);
|
|
33239
|
-
if (!universeAllowed) {
|
|
33240
|
-
unknown.push(key);
|
|
33241
|
-
} else if (!decisionValueAllowed(key, value, universeAllowed)) {
|
|
33242
|
-
invalid.push(key);
|
|
33243
|
-
}
|
|
33244
|
-
}
|
|
33245
|
-
if (unknown.length > 0) {
|
|
33246
|
-
throw new KxmConfigError(unknown.sort(compareCodeUnits6).map((key) => migrateIssue("semantic", "decision_unknown", key, "decision key does not match any current ambiguity")));
|
|
33247
|
-
}
|
|
33248
|
-
const allInvalid = [.../* @__PURE__ */ new Set([...invalid, ...first.invalidDecisions])].sort(compareCodeUnits6);
|
|
33249
|
-
if (allInvalid.length > 0) {
|
|
33250
|
-
throw new KxmConfigError(allInvalid.map((key) => migrateIssue("semantic", "decision_value_invalid", key, "decision value is not one of the plan's allowed values")));
|
|
33251
|
-
}
|
|
33252
|
-
}
|
|
33253
|
-
if (first.ambiguities.length > 0) {
|
|
33254
|
-
return { action: "planned", projectRoot: root, files: [], plan: first.plan };
|
|
33255
|
-
}
|
|
33256
|
-
const files = [...first.resources.entries()].sort(([left], [right]) => compareCodeUnits6(left, right)).map(([path4, value]) => ({ path: path4, bytes: Buffer.from((0, import_yaml12.stringify)(value, { lineWidth: 0 }), "utf8") }));
|
|
33257
|
-
const collisions = files.filter((file) => existsSync23(join26(root, ...file.path.split("/"))));
|
|
33258
|
-
if (collisions.length > 0) {
|
|
33259
|
-
throw new KxmConfigError(collisions.map((file) => migrateIssue("semantic", "migration_target_exists", file.path, "target resource already exists; refusing to overwrite")));
|
|
33260
|
-
}
|
|
33261
|
-
const parentIssues = [];
|
|
33262
|
-
for (const file of files) {
|
|
33263
|
-
const segments = file.path.split("/");
|
|
33264
|
-
let current = root;
|
|
33265
|
-
for (const segment of segments.slice(0, -1)) {
|
|
33266
|
-
current = join26(current, segment);
|
|
33267
|
-
if (!existsSync23(current)) continue;
|
|
33268
|
-
const stat = lstatSync8(current);
|
|
33269
|
-
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
33270
|
-
parentIssues.push(migrateIssue("path", "migration_target_parent_invalid", file.path, `migration target parent ${segment} must be a regular directory, not a link`));
|
|
33271
|
-
break;
|
|
33272
|
-
}
|
|
33273
|
-
}
|
|
33274
|
-
}
|
|
33275
|
-
if (parentIssues.length > 0) throw new KxmConfigError(parentIssues);
|
|
33276
|
-
if (options.dryRun) {
|
|
33277
|
-
return { action: "planned", projectRoot: root, files: files.map((file) => file.path), plan: first.plan };
|
|
33278
|
-
}
|
|
33279
|
-
if (!lock) throw new Error("project mutation lock is required to apply a migration");
|
|
33280
|
-
const written = [];
|
|
33281
|
-
try {
|
|
33282
|
-
for (const file of files) {
|
|
33283
|
-
const absolute = join26(root, ...file.path.split("/"));
|
|
33284
|
-
mkdirSync19(join26(absolute, ".."), { recursive: true });
|
|
33285
|
-
written.push(absolute);
|
|
33286
|
-
writeDurable2(absolute, file.bytes);
|
|
33287
|
-
}
|
|
33288
|
-
const bundle = loadKxmProject(root, { ...options, allowUnreceiptedLegacyConfig: true });
|
|
33289
|
-
const receipt = {
|
|
33290
|
-
schema: "kxm.migration-receipt.v1",
|
|
33291
|
-
migrationId: `mig_${sha256Of(`${first.plan.sourceDigest}
|
|
33292
|
-
${bundle.configRevision}`).slice(7, 39)}`,
|
|
33293
|
-
projectId: String(first.plan.projectId),
|
|
33294
|
-
sourceDigest: String(first.plan.sourceDigest),
|
|
33295
|
-
decisionDigest: sha256Of(kxmCanonicalJson(decisionsRecord ?? { schema: "kxm.migration-decision.v1", projectId: first.plan.projectId, projectName: first.plan.projectName, sourceDigest: first.plan.sourceDigest, resolutions: decisions })),
|
|
33296
|
-
configRevision: bundle.configRevision,
|
|
33297
|
-
sources: first.plan.sources,
|
|
33298
|
-
resources: files.map((file) => ({ path: file.path, sha256: sha256Of(file.bytes), bytes: file.bytes.byteLength }))
|
|
33299
|
-
};
|
|
33300
|
-
receipt.receiptSha256 = migrationReceiptSelfHash(receipt);
|
|
33301
|
-
const registry = new KxmSchemaRegistry(options.schemasDir);
|
|
33302
|
-
const receiptIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
|
|
33303
|
-
if (receiptIssues.length > 0) throw new KxmConfigError(receiptIssues);
|
|
33304
|
-
written.push(receiptAbsolute);
|
|
33305
|
-
writeDurable2(receiptAbsolute, Buffer.from((0, import_yaml12.stringify)(receipt, { lineWidth: 0 }), "utf8"));
|
|
33306
|
-
const verified = loadKxmProject(root, options);
|
|
33307
|
-
return {
|
|
33308
|
-
action: "applied",
|
|
33309
|
-
projectRoot: root,
|
|
33310
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33311
|
-
configRevision: verified.configRevision,
|
|
33312
|
-
files: files.map((file) => file.path),
|
|
33313
|
-
plan: first.plan
|
|
33314
|
-
};
|
|
33315
|
-
} catch (error) {
|
|
33316
|
-
for (const absolute of written.reverse()) {
|
|
33317
|
-
try {
|
|
33318
|
-
rmSync9(absolute, { force: true });
|
|
33319
|
-
} catch {
|
|
33320
|
-
}
|
|
33321
|
-
}
|
|
33322
|
-
throw error;
|
|
33323
|
-
}
|
|
33324
|
-
};
|
|
33325
|
-
if (options.dryRun) return execute(void 0);
|
|
33326
|
-
return withKxmLocalBindingLock(root, storeOptions, (lock) => execute(lock));
|
|
33327
|
-
}
|
|
33328
|
-
function verifyKxmMigration(projectRoot, options = {}) {
|
|
33329
|
-
const root = migrationGitRoot(projectRoot);
|
|
33330
|
-
const receiptAbsolute = join26(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
|
|
33331
|
-
if (!existsSync23(receiptAbsolute)) {
|
|
33332
|
-
return {
|
|
33333
|
-
ok: false,
|
|
33334
|
-
projectRoot: root,
|
|
33335
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33336
|
-
issues: [migrateIssue("discovery", "migration_receipt_missing", KXM_MIGRATION_RECEIPT_PATH, "no migration receipt exists")]
|
|
33337
|
-
};
|
|
33338
|
-
}
|
|
33339
|
-
try {
|
|
33340
|
-
const bundle = loadKxmProject(root, options);
|
|
33341
|
-
const receipt = bundle.migrationReceipt;
|
|
33342
|
-
if (!receipt) {
|
|
33343
|
-
if (legacyConfigFilesAt(root).length === 0) {
|
|
33344
|
-
const direct = readKxmMigrationReceipt(root, options);
|
|
33345
|
-
return {
|
|
33346
|
-
ok: false,
|
|
33347
|
-
projectRoot: root,
|
|
33348
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33349
|
-
configRevision: bundle.configRevision,
|
|
33350
|
-
issues: direct.issues.length > 0 ? direct.issues : [migrateIssue("semantic", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "receipt did not validate")]
|
|
33351
|
-
};
|
|
33352
|
-
}
|
|
33353
|
-
return {
|
|
33354
|
-
ok: false,
|
|
33355
|
-
projectRoot: root,
|
|
33356
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33357
|
-
configRevision: bundle.configRevision,
|
|
33358
|
-
issues: [migrateIssue("semantic", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "receipt did not validate during project load")]
|
|
33359
|
-
};
|
|
33360
|
-
}
|
|
33361
|
-
const targetIssues = verifyKxmMigrationReceiptTarget(root, receipt, bundle.resources, bundle.configRevision);
|
|
33362
|
-
return {
|
|
33363
|
-
ok: targetIssues.length === 0,
|
|
33364
|
-
projectRoot: root,
|
|
33365
|
-
receiptPath: KXM_MIGRATION_RECEIPT_PATH,
|
|
33366
|
-
configRevision: bundle.configRevision,
|
|
33367
|
-
issues: targetIssues
|
|
33368
|
-
};
|
|
33369
|
-
} catch (error) {
|
|
33370
|
-
if (error instanceof KxmConfigError) {
|
|
33371
|
-
return { ok: false, projectRoot: root, receiptPath: KXM_MIGRATION_RECEIPT_PATH, issues: error.issues };
|
|
33372
|
-
}
|
|
33373
|
-
throw error;
|
|
33374
|
-
}
|
|
33375
|
-
}
|
|
33376
|
-
|
|
33377
31764
|
// plugins/kxm/src/cli/project.ts
|
|
33378
31765
|
var kxmDriveCliSeams = {};
|
|
33379
31766
|
function initPlanPayload(plan) {
|
|
@@ -33471,7 +31858,7 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
33471
31858
|
if (runtime.dryRun) {
|
|
33472
31859
|
return finishInit(0, `init plan: ${initialized.plan.mode}`);
|
|
33473
31860
|
}
|
|
33474
|
-
const next = initialized.plan.mode === "
|
|
31861
|
+
const next = initialized.plan.mode === "legacy" ? "legacy state is not migrated by this build: initialise a fresh project directory and copy the YAML definitions you want to keep" : initialized.repairPlan?.issues.length ? "managed-template repair is blocked by conflicts or authority changes; local files were preserved" : "partial or provenance-free KXM state requires explicit repair; no files were overwritten";
|
|
33475
31862
|
return finishInit(1, next);
|
|
33476
31863
|
} catch (error) {
|
|
33477
31864
|
if (error instanceof KxmConfigError) {
|
|
@@ -33491,141 +31878,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
33491
31878
|
return 1;
|
|
33492
31879
|
}
|
|
33493
31880
|
}
|
|
33494
|
-
async function cmdKxmMigratePlan(runtime) {
|
|
33495
|
-
if (runtime.workspaceFlag !== void 0) {
|
|
33496
|
-
print(runtime.io, runtime.json, {
|
|
33497
|
-
ok: false,
|
|
33498
|
-
command: "migrate plan",
|
|
33499
|
-
error: "workspace_option_unsupported"
|
|
33500
|
-
}, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
|
|
33501
|
-
return 2;
|
|
33502
|
-
}
|
|
33503
|
-
try {
|
|
33504
|
-
const result = planKxmMigration(runtime.cwd, {});
|
|
33505
|
-
const ambiguities = result.plan.ambiguities ?? [];
|
|
33506
|
-
const unmapped = result.plan.unmapped ?? [];
|
|
33507
|
-
const payload = {
|
|
33508
|
-
ok: result.plan.canApply === true,
|
|
33509
|
-
command: "migrate plan",
|
|
33510
|
-
plan: result.plan,
|
|
33511
|
-
plannedOnly: result.plan.canApply !== true
|
|
33512
|
-
};
|
|
33513
|
-
if (result.plan.canApply === true) {
|
|
33514
|
-
print(runtime.io, runtime.json, payload, `migration plan: ${ambiguities.length} ambiguities, ${unmapped.length} preserved fields; ready to apply`);
|
|
33515
|
-
return 0;
|
|
33516
|
-
}
|
|
33517
|
-
print(
|
|
33518
|
-
runtime.io,
|
|
33519
|
-
runtime.json,
|
|
33520
|
-
payload,
|
|
33521
|
-
`migration plan requires ${ambiguities.length} reviewed decision(s):
|
|
33522
|
-
${ambiguities.map((candidate) => ` - ${candidate.key}: ${candidate.message}`).join("\n")}`
|
|
33523
|
-
);
|
|
33524
|
-
return 1;
|
|
33525
|
-
} catch (error) {
|
|
33526
|
-
if (error instanceof KxmConfigError) {
|
|
33527
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate plan", error: "migration_plan_failed", issues: error.issues }, `migration plan failed: ${error.message}`);
|
|
33528
|
-
return 1;
|
|
33529
|
-
}
|
|
33530
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate plan", error: "migration_plan_io_failed" }, "migration plan failed because a local filesystem operation did not complete");
|
|
33531
|
-
return 1;
|
|
33532
|
-
}
|
|
33533
|
-
}
|
|
33534
|
-
async function cmdKxmMigrateApply(runtime, options) {
|
|
33535
|
-
if (runtime.workspaceFlag !== void 0) {
|
|
33536
|
-
print(runtime.io, runtime.json, {
|
|
33537
|
-
ok: false,
|
|
33538
|
-
command: "migrate apply",
|
|
33539
|
-
error: "workspace_option_unsupported"
|
|
33540
|
-
}, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
|
|
33541
|
-
return 2;
|
|
33542
|
-
}
|
|
33543
|
-
try {
|
|
33544
|
-
const result = applyKxmMigration(runtime.cwd, {
|
|
33545
|
-
...options.decisions?.trim() ? { decisionsFile: options.decisions.trim() } : {},
|
|
33546
|
-
...options.projectId?.trim() ? { projectId: options.projectId.trim() } : {},
|
|
33547
|
-
...options.name?.trim() ? { projectName: options.name.trim() } : {},
|
|
33548
|
-
localStateRoot: kxmUserStateRoot({ env: runtime.env }),
|
|
33549
|
-
dryRun: runtime.dryRun
|
|
33550
|
-
});
|
|
33551
|
-
const payload = {
|
|
33552
|
-
ok: result.action !== "planned" || runtime.dryRun === true && result.plan?.canApply === true,
|
|
33553
|
-
command: "migrate apply",
|
|
33554
|
-
action: result.action,
|
|
33555
|
-
files: result.files,
|
|
33556
|
-
...result.configRevision ? { configRevision: result.configRevision } : {},
|
|
33557
|
-
...result.receiptPath ? { receiptPath: result.receiptPath } : {},
|
|
33558
|
-
plannedOnly: result.action === "planned"
|
|
33559
|
-
};
|
|
33560
|
-
if (result.action === "applied") {
|
|
33561
|
-
print(runtime.io, runtime.json, payload, `migration applied: ${result.files.length} resources installed, receipt at ${result.receiptPath ?? ""}`);
|
|
33562
|
-
return 0;
|
|
33563
|
-
}
|
|
33564
|
-
if (result.action === "already-migrated") {
|
|
33565
|
-
print(runtime.io, runtime.json, payload, "migration receipt already exists; nothing to apply");
|
|
33566
|
-
return 0;
|
|
33567
|
-
}
|
|
33568
|
-
if (runtime.dryRun && result.plan?.canApply === true) {
|
|
33569
|
-
print(runtime.io, runtime.json, payload, `migration dry run: ${result.files.length} resources would be installed`);
|
|
33570
|
-
return 0;
|
|
33571
|
-
}
|
|
33572
|
-
const ambiguities = result.plan?.ambiguities ?? [];
|
|
33573
|
-
print(
|
|
33574
|
-
runtime.io,
|
|
33575
|
-
runtime.json,
|
|
33576
|
-
{ ...payload, plan: result.plan },
|
|
33577
|
-
`migration blocked by ${ambiguities.length} unresolved decision(s); review 'kxm migrate plan' and pass --decisions:
|
|
33578
|
-
${ambiguities.map((candidate) => ` - ${candidate.key}: ${candidate.message}`).join("\n")}`
|
|
33579
|
-
);
|
|
33580
|
-
return 1;
|
|
33581
|
-
} catch (error) {
|
|
33582
|
-
if (error instanceof KxmConfigError) {
|
|
33583
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate apply", error: "migration_apply_failed", issues: error.issues }, `migration apply failed: ${error.message}`);
|
|
33584
|
-
return 1;
|
|
33585
|
-
}
|
|
33586
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate apply", error: "migration_apply_io_failed" }, "migration apply failed because a local filesystem operation did not complete");
|
|
33587
|
-
return 1;
|
|
33588
|
-
}
|
|
33589
|
-
}
|
|
33590
|
-
async function cmdKxmMigrateVerify(runtime) {
|
|
33591
|
-
if (runtime.workspaceFlag !== void 0) {
|
|
33592
|
-
print(runtime.io, runtime.json, {
|
|
33593
|
-
ok: false,
|
|
33594
|
-
command: "migrate verify",
|
|
33595
|
-
error: "workspace_option_unsupported"
|
|
33596
|
-
}, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
|
|
33597
|
-
return 2;
|
|
33598
|
-
}
|
|
33599
|
-
let result;
|
|
33600
|
-
try {
|
|
33601
|
-
result = verifyKxmMigration(runtime.cwd, {});
|
|
33602
|
-
} catch (error) {
|
|
33603
|
-
if (error instanceof KxmConfigError) {
|
|
33604
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate verify", error: "migration_verify_failed", issues: error.issues }, `migration verification failed: ${error.message}`);
|
|
33605
|
-
return 1;
|
|
33606
|
-
}
|
|
33607
|
-
print(runtime.io, runtime.json, { ok: false, command: "migrate verify", error: "migration_verify_io_failed" }, "migration verification failed because a local filesystem operation did not complete");
|
|
33608
|
-
return 1;
|
|
33609
|
-
}
|
|
33610
|
-
const payload = {
|
|
33611
|
-
ok: result.ok,
|
|
33612
|
-
command: "migrate verify",
|
|
33613
|
-
...result.configRevision ? { configRevision: result.configRevision } : {},
|
|
33614
|
-
issues: result.issues
|
|
33615
|
-
};
|
|
33616
|
-
if (result.ok) {
|
|
33617
|
-
print(runtime.io, runtime.json, payload, `migration receipt verified: legacy sources unchanged, target bundle matches ${result.configRevision ?? ""}`);
|
|
33618
|
-
return 0;
|
|
33619
|
-
}
|
|
33620
|
-
print(runtime.io, runtime.json, payload, `migration verification failed:
|
|
33621
|
-
${result.issues.map((issue3) => ` - ${issue3.file}: ${issue3.code}: ${issue3.message}`).join("\n")}`);
|
|
33622
|
-
return 1;
|
|
33623
|
-
}
|
|
33624
31881
|
async function cmdBackup(runtime, options) {
|
|
33625
31882
|
try {
|
|
33626
31883
|
const { manifest, outDir } = createBackup({
|
|
33627
31884
|
projectRoot: runtime.cwd,
|
|
33628
|
-
...options.out ? { outDir:
|
|
31885
|
+
...options.out ? { outDir: resolve19(runtime.cwd, options.out) } : {}
|
|
33629
31886
|
});
|
|
33630
31887
|
const payload = {
|
|
33631
31888
|
ok: true,
|
|
@@ -33637,7 +31894,7 @@ async function cmdBackup(runtime, options) {
|
|
|
33637
31894
|
const summary = [
|
|
33638
31895
|
`Created SQLite backup with ${manifest.stores.length} store(s):`,
|
|
33639
31896
|
...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
|
|
33640
|
-
`Manifest: ${
|
|
31897
|
+
`Manifest: ${join26(outDir, "manifest.json")}`
|
|
33641
31898
|
].join("\n");
|
|
33642
31899
|
print(runtime.io, runtime.json, payload, summary);
|
|
33643
31900
|
return 0;
|
|
@@ -33652,7 +31909,7 @@ async function cmdBackup(runtime, options) {
|
|
|
33652
31909
|
}
|
|
33653
31910
|
async function cmdRestore(runtime, manifestArg) {
|
|
33654
31911
|
try {
|
|
33655
|
-
const result = restoreBackup(
|
|
31912
|
+
const result = restoreBackup(resolve19(runtime.cwd, manifestArg), {
|
|
33656
31913
|
projectRoot: runtime.cwd
|
|
33657
31914
|
});
|
|
33658
31915
|
const payload = {
|
|
@@ -33857,7 +32114,7 @@ function parseDriveWaitTimeoutMs(raw) {
|
|
|
33857
32114
|
return value;
|
|
33858
32115
|
}
|
|
33859
32116
|
function sleepMs(ms) {
|
|
33860
|
-
return new Promise((
|
|
32117
|
+
return new Promise((resolve30) => setTimeout(resolve30, ms));
|
|
33861
32118
|
}
|
|
33862
32119
|
function driveWaitCompleted(drive) {
|
|
33863
32120
|
return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
|
|
@@ -34002,7 +32259,7 @@ async function selectInventoryModel(runtime, requested) {
|
|
|
34002
32259
|
if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
|
|
34003
32260
|
runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
|
|
34004
32261
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
34005
|
-
const answer = await new Promise((
|
|
32262
|
+
const answer = await new Promise((resolve30) => rl.once("line", resolve30));
|
|
34006
32263
|
rl.close();
|
|
34007
32264
|
const index = Number.parseInt(answer.trim(), 10) - 1;
|
|
34008
32265
|
return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
|
|
@@ -34028,7 +32285,7 @@ async function cmdModelsScreen(runtime) {
|
|
|
34028
32285
|
return 2;
|
|
34029
32286
|
}
|
|
34030
32287
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
34031
|
-
const ask = (q2) => new Promise((
|
|
32288
|
+
const ask = (q2) => new Promise((resolve30) => rl.question(q2, resolve30));
|
|
34032
32289
|
try {
|
|
34033
32290
|
while (true) {
|
|
34034
32291
|
const policy = loadRoutePolicy(runtime.dirs.workdir);
|
|
@@ -34336,12 +32593,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
|
|
|
34336
32593
|
const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
|
|
34337
32594
|
let filePath = workflowPath;
|
|
34338
32595
|
if (!filePath) {
|
|
34339
|
-
filePath =
|
|
32596
|
+
filePath = resolve20(projectRoot, ".kxm", "workflows", "default.yaml");
|
|
34340
32597
|
}
|
|
34341
32598
|
let yamlContent;
|
|
34342
32599
|
let workflowId = "default";
|
|
34343
|
-
if (
|
|
34344
|
-
yamlContent =
|
|
32600
|
+
if (existsSync23(filePath)) {
|
|
32601
|
+
yamlContent = readFileSync22(filePath, "utf8");
|
|
34345
32602
|
} else {
|
|
34346
32603
|
yamlContent = `schema: kxm.workflow.v1
|
|
34347
32604
|
description: Feature implementation workflow
|
|
@@ -34381,7 +32638,7 @@ steps:
|
|
|
34381
32638
|
maxTransitions: 2
|
|
34382
32639
|
`;
|
|
34383
32640
|
}
|
|
34384
|
-
const parsedYaml = (0,
|
|
32641
|
+
const parsedYaml = (0, import_yaml12.parse)(yamlContent);
|
|
34385
32642
|
const plan = compileKxmWorkflow({ id: workflowId, value: parsedYaml });
|
|
34386
32643
|
const layout = generateStudioLayout(plan);
|
|
34387
32644
|
print(runtime.io, runtime.json, { ok: true, command: "studio layout", layout }, JSON.stringify(layout, null, 2));
|
|
@@ -34414,16 +32671,16 @@ async function cmdStudioServe(runtime, options) {
|
|
|
34414
32671
|
sessionToken,
|
|
34415
32672
|
planProvider: () => {
|
|
34416
32673
|
try {
|
|
34417
|
-
const wfDir =
|
|
34418
|
-
const defaultPath =
|
|
34419
|
-
let targetPath =
|
|
34420
|
-
if (!targetPath &&
|
|
32674
|
+
const wfDir = join27(runtime.cwd, ".kxm", "workflows");
|
|
32675
|
+
const defaultPath = join27(wfDir, "default.yaml");
|
|
32676
|
+
let targetPath = existsSync23(defaultPath) ? defaultPath : void 0;
|
|
32677
|
+
if (!targetPath && existsSync23(wfDir)) {
|
|
34421
32678
|
const yml = readdirSync8(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
34422
|
-
if (yml) targetPath =
|
|
32679
|
+
if (yml) targetPath = join27(wfDir, yml);
|
|
34423
32680
|
}
|
|
34424
|
-
if (targetPath &&
|
|
34425
|
-
const raw =
|
|
34426
|
-
const parsed = (0,
|
|
32681
|
+
if (targetPath && existsSync23(targetPath)) {
|
|
32682
|
+
const raw = readFileSync22(targetPath, "utf8");
|
|
32683
|
+
const parsed = (0, import_yaml12.parse)(raw);
|
|
34427
32684
|
const wfId = basename6(targetPath).replace(/\.(yaml|yml)$/, "");
|
|
34428
32685
|
return compileKxmWorkflow({ id: wfId, value: parsed });
|
|
34429
32686
|
}
|
|
@@ -34472,14 +32729,14 @@ Press Ctrl+C to stop.
|
|
|
34472
32729
|
}
|
|
34473
32730
|
|
|
34474
32731
|
// plugins/kxm/src/cli/context-skills.ts
|
|
34475
|
-
import { readFileSync as
|
|
34476
|
-
import { join as
|
|
32732
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
32733
|
+
import { join as join31, relative as relative5 } from "node:path";
|
|
34477
32734
|
|
|
34478
32735
|
// plugins/kxm/src/memory.ts
|
|
34479
|
-
var
|
|
32736
|
+
var import_yaml13 = __toESM(require_dist(), 1);
|
|
34480
32737
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
34481
|
-
import { existsSync as
|
|
34482
|
-
import { extname as extname2, join as
|
|
32738
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync19, readdirSync as readdirSync9, readFileSync as readFileSync23, writeFileSync as writeFileSync18 } from "node:fs";
|
|
32739
|
+
import { extname as extname2, join as join28, resolve as resolve21 } from "node:path";
|
|
34483
32740
|
var MEMORY_SCHEMA = "kxm.memory.v1";
|
|
34484
32741
|
var MEMORY_BRIEF_SCHEMA = "kxm.memory-brief.v1";
|
|
34485
32742
|
var MEMORY_MARKER_START = "<!-- kxm:memory:start -->";
|
|
@@ -34512,7 +32769,7 @@ function parseFrontmatter(content) {
|
|
|
34512
32769
|
}
|
|
34513
32770
|
function parseMemoryRecord(raw, filename = "memory.md") {
|
|
34514
32771
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
34515
|
-
const data = (0,
|
|
32772
|
+
const data = (0, import_yaml13.parse)(frontmatter);
|
|
34516
32773
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
34517
32774
|
throw new Error(`invalid YAML frontmatter in ${filename}`);
|
|
34518
32775
|
}
|
|
@@ -34592,7 +32849,7 @@ function formatMemoryRecord(record) {
|
|
|
34592
32849
|
lifecycle: record.lifecycle,
|
|
34593
32850
|
evidenceRefs: record.evidenceRefs
|
|
34594
32851
|
};
|
|
34595
|
-
const yamlText = (0,
|
|
32852
|
+
const yamlText = (0, import_yaml13.stringify)(frontmatter).trim();
|
|
34596
32853
|
const bodyText = record.body?.trim() ?? "";
|
|
34597
32854
|
return bodyText ? `---
|
|
34598
32855
|
${yamlText}
|
|
@@ -34605,21 +32862,21 @@ ${yamlText}
|
|
|
34605
32862
|
`;
|
|
34606
32863
|
}
|
|
34607
32864
|
function memoryDirectories(repoRoot) {
|
|
34608
|
-
const root =
|
|
34609
|
-
const memoryDir =
|
|
34610
|
-
const candidatesDir =
|
|
32865
|
+
const root = resolve21(repoRoot);
|
|
32866
|
+
const memoryDir = join28(root, ".kxm", "memory");
|
|
32867
|
+
const candidatesDir = join28(memoryDir, "candidates");
|
|
34611
32868
|
return { memoryDir, candidatesDir };
|
|
34612
32869
|
}
|
|
34613
32870
|
function loadAuthoredMemory(repoRoot) {
|
|
34614
32871
|
const { memoryDir } = memoryDirectories(repoRoot);
|
|
34615
|
-
if (!
|
|
32872
|
+
if (!existsSync24(memoryDir)) return [];
|
|
34616
32873
|
const records = [];
|
|
34617
32874
|
const entries = readdirSync9(memoryDir, { withFileTypes: true });
|
|
34618
32875
|
for (const entry of entries) {
|
|
34619
32876
|
if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
34620
|
-
const fullPath =
|
|
32877
|
+
const fullPath = join28(memoryDir, entry.name);
|
|
34621
32878
|
try {
|
|
34622
|
-
const text =
|
|
32879
|
+
const text = readFileSync23(fullPath, "utf8");
|
|
34623
32880
|
const record = parseMemoryRecord(text, entry.name);
|
|
34624
32881
|
if (record.lifecycle === "active") {
|
|
34625
32882
|
records.push(record);
|
|
@@ -34633,7 +32890,7 @@ function loadAuthoredMemory(repoRoot) {
|
|
|
34633
32890
|
}
|
|
34634
32891
|
function createMemoryNote(repoRoot, summary, options = {}) {
|
|
34635
32892
|
const { candidatesDir } = memoryDirectories(repoRoot);
|
|
34636
|
-
|
|
32893
|
+
mkdirSync19(candidatesDir, { recursive: true });
|
|
34637
32894
|
const scope = options.scope ?? "project";
|
|
34638
32895
|
if (!VALID_SCOPES.has(scope)) {
|
|
34639
32896
|
throw new Error(`invalid memory note scope: ${scope}; must be one of agent, project, run, operator`);
|
|
@@ -34658,8 +32915,8 @@ function createMemoryNote(repoRoot, summary, options = {}) {
|
|
|
34658
32915
|
evidenceRefs: [],
|
|
34659
32916
|
...options.body ? { body: options.body.trim() } : {}
|
|
34660
32917
|
};
|
|
34661
|
-
const targetPath =
|
|
34662
|
-
|
|
32918
|
+
const targetPath = join28(candidatesDir, `${id}.md`);
|
|
32919
|
+
writeFileSync18(targetPath, formatMemoryRecord(record), "utf8");
|
|
34663
32920
|
return { record, path: targetPath };
|
|
34664
32921
|
}
|
|
34665
32922
|
function generateMemoryBrief(repoRoot) {
|
|
@@ -34695,8 +32952,8 @@ function formatHarnessMemoryBlock(records) {
|
|
|
34695
32952
|
}
|
|
34696
32953
|
function updateHarnessDocument(filePath, block, defaultHeader) {
|
|
34697
32954
|
let original = "";
|
|
34698
|
-
if (
|
|
34699
|
-
original =
|
|
32955
|
+
if (existsSync24(filePath)) {
|
|
32956
|
+
original = readFileSync23(filePath, "utf8");
|
|
34700
32957
|
}
|
|
34701
32958
|
let updated = "";
|
|
34702
32959
|
if (original.includes(MEMORY_MARKER_START) && original.includes(MEMORY_MARKER_END)) {
|
|
@@ -34724,25 +32981,25 @@ ${block}
|
|
|
34724
32981
|
`;
|
|
34725
32982
|
}
|
|
34726
32983
|
if (updated !== original) {
|
|
34727
|
-
|
|
32984
|
+
writeFileSync18(filePath, updated, "utf8");
|
|
34728
32985
|
return true;
|
|
34729
32986
|
}
|
|
34730
32987
|
return false;
|
|
34731
32988
|
}
|
|
34732
32989
|
function syncHarnessMemory(repoRoot) {
|
|
34733
|
-
const root =
|
|
32990
|
+
const root = resolve21(repoRoot);
|
|
34734
32991
|
const records = loadAuthoredMemory(root);
|
|
34735
32992
|
const block = formatHarnessMemoryBlock(records);
|
|
34736
32993
|
const updated = [];
|
|
34737
32994
|
const created = [];
|
|
34738
|
-
const agentsPath =
|
|
32995
|
+
const agentsPath = join28(root, "AGENTS.md");
|
|
34739
32996
|
const agentsHeader = "# AGENTS\n\nFollow project instructions.\n";
|
|
34740
|
-
const agentsExisted =
|
|
32997
|
+
const agentsExisted = existsSync24(agentsPath);
|
|
34741
32998
|
if (updateHarnessDocument(agentsPath, block, agentsHeader)) {
|
|
34742
32999
|
if (agentsExisted) updated.push("AGENTS.md");
|
|
34743
33000
|
else created.push("AGENTS.md");
|
|
34744
33001
|
}
|
|
34745
|
-
const claudePath =
|
|
33002
|
+
const claudePath = join28(root, "CLAUDE.md");
|
|
34746
33003
|
const claudeHeader = `# KXM (Claude)
|
|
34747
33004
|
|
|
34748
33005
|
Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
|
|
@@ -34754,12 +33011,12 @@ rotation, not a sole writer. If \`grok\` is logged out, never bill Grok through
|
|
|
34754
33011
|
use a relief route Tracking **admits**, or stop and name the limits hit. Your reviews are
|
|
34755
33012
|
artifacts, not hub \`peer-reply\` evidence.
|
|
34756
33013
|
`;
|
|
34757
|
-
const claudeExisted =
|
|
33014
|
+
const claudeExisted = existsSync24(claudePath);
|
|
34758
33015
|
if (updateHarnessDocument(claudePath, block, claudeHeader)) {
|
|
34759
33016
|
if (claudeExisted) updated.push("CLAUDE.md");
|
|
34760
33017
|
else created.push("CLAUDE.md");
|
|
34761
33018
|
}
|
|
34762
|
-
const geminiPath =
|
|
33019
|
+
const geminiPath = join28(root, "GEMINI.md");
|
|
34763
33020
|
const geminiHeader = `# KXM (Gemini / Antigravity)
|
|
34764
33021
|
|
|
34765
33022
|
Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
|
|
@@ -34770,7 +33027,7 @@ Google goes through the \`antigravity\` **Pi provider** (Tracking \u2192 Decided
|
|
|
34770
33027
|
Current admissions come from Tracking and \`kxm harness list\`. Starting rotation
|
|
34771
33028
|
remains Grok.
|
|
34772
33029
|
`;
|
|
34773
|
-
const geminiExisted =
|
|
33030
|
+
const geminiExisted = existsSync24(geminiPath);
|
|
34774
33031
|
if (updateHarnessDocument(geminiPath, block, geminiHeader)) {
|
|
34775
33032
|
if (geminiExisted) updated.push("GEMINI.md");
|
|
34776
33033
|
else created.push("GEMINI.md");
|
|
@@ -34779,10 +33036,10 @@ remains Grok.
|
|
|
34779
33036
|
}
|
|
34780
33037
|
|
|
34781
33038
|
// plugins/kxm/src/skills.ts
|
|
34782
|
-
var
|
|
34783
|
-
import { createHash as
|
|
34784
|
-
import { existsSync as
|
|
34785
|
-
import { join as
|
|
33039
|
+
var import_yaml14 = __toESM(require_dist(), 1);
|
|
33040
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
33041
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync20, readdirSync as readdirSync10, readFileSync as readFileSync24, renameSync as renameSync7, rmSync as rmSync9, statSync as statSync3, writeFileSync as writeFileSync19 } from "node:fs";
|
|
33042
|
+
import { join as join29 } from "node:path";
|
|
34786
33043
|
var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
|
|
34787
33044
|
var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
|
|
34788
33045
|
var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
|
|
@@ -34805,7 +33062,7 @@ var SkillLifecycleError = class extends Error {
|
|
|
34805
33062
|
}
|
|
34806
33063
|
};
|
|
34807
33064
|
function skillContentSha256(content) {
|
|
34808
|
-
return
|
|
33065
|
+
return createHash13("sha256").update(content, "utf8").digest("hex");
|
|
34809
33066
|
}
|
|
34810
33067
|
function skillIdFor(name, contentSha256) {
|
|
34811
33068
|
const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
@@ -34823,7 +33080,7 @@ function parseSkillFrontmatter(content) {
|
|
|
34823
33080
|
return { frontmatter: null, body: content };
|
|
34824
33081
|
}
|
|
34825
33082
|
try {
|
|
34826
|
-
const parsed = (0,
|
|
33083
|
+
const parsed = (0, import_yaml14.parse)(rawFm);
|
|
34827
33084
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
34828
33085
|
return { frontmatter: parsed, body: rawBody };
|
|
34829
33086
|
}
|
|
@@ -34876,48 +33133,48 @@ var SkillLifecycle = class {
|
|
|
34876
33133
|
this.allowOptimizationEvals = options.allowOptimizationEvals === true;
|
|
34877
33134
|
}
|
|
34878
33135
|
dir(state) {
|
|
34879
|
-
return
|
|
33136
|
+
return join29(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
|
|
34880
33137
|
}
|
|
34881
33138
|
historyFile(id) {
|
|
34882
|
-
return
|
|
33139
|
+
return join29(this.root, "history", `${id}.jsonl`);
|
|
34883
33140
|
}
|
|
34884
33141
|
paths(state, id) {
|
|
34885
|
-
const dir =
|
|
34886
|
-
return { dir, metadata:
|
|
33142
|
+
const dir = join29(this.dir(state), id);
|
|
33143
|
+
return { dir, metadata: join29(dir, "metadata.json"), skill: join29(dir, "SKILL.md") };
|
|
34887
33144
|
}
|
|
34888
33145
|
appendHistory(id, record) {
|
|
34889
|
-
|
|
33146
|
+
mkdirSync20(join29(this.root, "history"), { recursive: true });
|
|
34890
33147
|
const line = `${JSON.stringify(record)}
|
|
34891
33148
|
`;
|
|
34892
|
-
if (
|
|
34893
|
-
const existing =
|
|
33149
|
+
if (existsSync25(this.historyFile(id))) {
|
|
33150
|
+
const existing = readFileSync24(this.historyFile(id), "utf8");
|
|
34894
33151
|
const lines = existing.split("\n").filter((entry) => entry.trim());
|
|
34895
|
-
|
|
33152
|
+
writeFileSync19(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
|
|
34896
33153
|
} else {
|
|
34897
|
-
|
|
33154
|
+
writeFileSync19(this.historyFile(id), line);
|
|
34898
33155
|
}
|
|
34899
33156
|
}
|
|
34900
33157
|
history(id) {
|
|
34901
33158
|
const file = this.historyFile(id);
|
|
34902
|
-
if (!
|
|
34903
|
-
return
|
|
33159
|
+
if (!existsSync25(file)) return [];
|
|
33160
|
+
return readFileSync24(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
|
|
34904
33161
|
}
|
|
34905
33162
|
readMetadata(state, id) {
|
|
34906
33163
|
const { metadata } = this.paths(state, id);
|
|
34907
|
-
if (!
|
|
33164
|
+
if (!existsSync25(metadata)) {
|
|
34908
33165
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
|
|
34909
33166
|
}
|
|
34910
|
-
return JSON.parse(
|
|
33167
|
+
return JSON.parse(readFileSync24(metadata, "utf8"));
|
|
34911
33168
|
}
|
|
34912
33169
|
move(from, to, id) {
|
|
34913
|
-
const fromDir =
|
|
34914
|
-
const toDir =
|
|
34915
|
-
if (!
|
|
33170
|
+
const fromDir = join29(this.dir(from), id);
|
|
33171
|
+
const toDir = join29(this.dir(to), id);
|
|
33172
|
+
if (!existsSync25(fromDir)) {
|
|
34916
33173
|
throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
|
|
34917
33174
|
}
|
|
34918
|
-
|
|
34919
|
-
if (
|
|
34920
|
-
|
|
33175
|
+
mkdirSync20(this.dir(to), { recursive: true });
|
|
33176
|
+
if (existsSync25(toDir)) rmSync9(toDir, { recursive: true, force: true });
|
|
33177
|
+
renameSync7(fromDir, toDir);
|
|
34921
33178
|
}
|
|
34922
33179
|
/** Submit a new skill candidate. Content is redacted of secret material at
|
|
34923
33180
|
* creation; the ID is derived from name + content hash. */
|
|
@@ -34959,7 +33216,7 @@ var SkillLifecycle = class {
|
|
|
34959
33216
|
const contentSha256 = skillContentSha256(content);
|
|
34960
33217
|
const id = skillIdFor(name, contentSha256);
|
|
34961
33218
|
const { dir, metadata, skill } = this.paths("candidate", id);
|
|
34962
|
-
if (
|
|
33219
|
+
if (existsSync25(metadata)) {
|
|
34963
33220
|
throw new SkillLifecycleError(
|
|
34964
33221
|
"skill_candidate_exists",
|
|
34965
33222
|
`identical candidate ${id} already exists; changed behavior requires changed content`
|
|
@@ -34978,9 +33235,9 @@ var SkillLifecycle = class {
|
|
|
34978
33235
|
createdAt: this.now(),
|
|
34979
33236
|
...input.supersedes ? { supersedes: input.supersedes } : {}
|
|
34980
33237
|
};
|
|
34981
|
-
|
|
34982
|
-
|
|
34983
|
-
|
|
33238
|
+
mkdirSync20(dir, { recursive: true });
|
|
33239
|
+
writeFileSync19(skill, content);
|
|
33240
|
+
writeFileSync19(metadata, `${JSON.stringify(record, null, 2)}
|
|
34984
33241
|
`);
|
|
34985
33242
|
this.appendHistory(id, { schema: "kxm.skill-history-event.v1", event: "candidate_created", by: createdBy, supersedes: input.supersedes, at: record.createdAt });
|
|
34986
33243
|
return record;
|
|
@@ -35069,18 +33326,18 @@ var SkillLifecycle = class {
|
|
|
35069
33326
|
};
|
|
35070
33327
|
const candidatePaths = this.paths("candidate", candidateId);
|
|
35071
33328
|
const promotedPaths = this.paths("promoted", candidateId);
|
|
35072
|
-
|
|
35073
|
-
const skillContent =
|
|
35074
|
-
const metadataContent =
|
|
35075
|
-
|
|
35076
|
-
|
|
35077
|
-
const patchesDir =
|
|
35078
|
-
|
|
35079
|
-
const patchPath =
|
|
33329
|
+
mkdirSync20(promotedPaths.dir, { recursive: true });
|
|
33330
|
+
const skillContent = readFileSync24(candidatePaths.skill, "utf8");
|
|
33331
|
+
const metadataContent = readFileSync24(candidatePaths.metadata, "utf8");
|
|
33332
|
+
writeFileSync19(promotedPaths.skill, skillContent);
|
|
33333
|
+
writeFileSync19(promotedPaths.metadata, metadataContent);
|
|
33334
|
+
const patchesDir = join29(this.root, "patches");
|
|
33335
|
+
mkdirSync20(patchesDir, { recursive: true });
|
|
33336
|
+
const patchPath = join29(patchesDir, `${candidateId}.patch`);
|
|
35080
33337
|
const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
|
|
35081
33338
|
const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
|
|
35082
33339
|
const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
|
|
35083
|
-
|
|
33340
|
+
writeFileSync19(patchPath, patch, "utf8");
|
|
35084
33341
|
this.appendHistory(candidateId, record);
|
|
35085
33342
|
return { ...metadata, patch, patchPath };
|
|
35086
33343
|
}
|
|
@@ -35104,7 +33361,7 @@ var SkillLifecycle = class {
|
|
|
35104
33361
|
verify(state, id) {
|
|
35105
33362
|
const metadata = this.readMetadata(state, id);
|
|
35106
33363
|
const { skill } = this.paths(state, id);
|
|
35107
|
-
const content =
|
|
33364
|
+
const content = readFileSync24(skill, "utf8");
|
|
35108
33365
|
if (skillContentSha256(content) !== metadata.contentSha256) {
|
|
35109
33366
|
throw new SkillLifecycleError(
|
|
35110
33367
|
"skill_integrity_violation",
|
|
@@ -35122,7 +33379,7 @@ var SkillLifecycle = class {
|
|
|
35122
33379
|
}
|
|
35123
33380
|
list(state) {
|
|
35124
33381
|
const dir = this.dir(state);
|
|
35125
|
-
if (!
|
|
33382
|
+
if (!existsSync25(dir)) return [];
|
|
35126
33383
|
const ids = readdirSorted(dir);
|
|
35127
33384
|
return ids.map((id) => {
|
|
35128
33385
|
try {
|
|
@@ -35135,7 +33392,7 @@ var SkillLifecycle = class {
|
|
|
35135
33392
|
read(state, id) {
|
|
35136
33393
|
const metadata = this.readMetadata(state, id);
|
|
35137
33394
|
const { skill } = this.paths(state, id);
|
|
35138
|
-
return { metadata, content:
|
|
33395
|
+
return { metadata, content: readFileSync24(skill, "utf8") };
|
|
35139
33396
|
}
|
|
35140
33397
|
};
|
|
35141
33398
|
function boundedList(value, field) {
|
|
@@ -35150,18 +33407,18 @@ function boundedList(value, field) {
|
|
|
35150
33407
|
return [...new Set(refs)];
|
|
35151
33408
|
}
|
|
35152
33409
|
function readdirSorted(dir) {
|
|
35153
|
-
return readdirSync10(dir).filter((entry) => statSync3(
|
|
33410
|
+
return readdirSync10(dir).filter((entry) => statSync3(join29(dir, entry)).isDirectory()).sort();
|
|
35154
33411
|
}
|
|
35155
33412
|
|
|
35156
33413
|
// plugins/kxm/src/wiki.ts
|
|
35157
|
-
import { mkdirSync as
|
|
35158
|
-
import { join as
|
|
33414
|
+
import { mkdirSync as mkdirSync21, writeFileSync as writeFileSync20 } from "node:fs";
|
|
33415
|
+
import { join as join30 } from "node:path";
|
|
35159
33416
|
function writeCompiledWiki(root, wiki) {
|
|
35160
33417
|
const written = [];
|
|
35161
33418
|
for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
|
|
35162
|
-
const absolute =
|
|
35163
|
-
|
|
35164
|
-
|
|
33419
|
+
const absolute = join30(root, relativePath);
|
|
33420
|
+
mkdirSync21(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
|
|
33421
|
+
writeFileSync20(absolute, content);
|
|
35165
33422
|
written.push(relativePath);
|
|
35166
33423
|
}
|
|
35167
33424
|
return written;
|
|
@@ -35327,7 +33584,7 @@ function skillStateFromFlag(value) {
|
|
|
35327
33584
|
throw new Error(`invalid skill state ${value}`);
|
|
35328
33585
|
}
|
|
35329
33586
|
function skillsRoot(runtime) {
|
|
35330
|
-
return
|
|
33587
|
+
return join31(runtime.dirs.workdir, ".kxm", "skills");
|
|
35331
33588
|
}
|
|
35332
33589
|
function csv(value) {
|
|
35333
33590
|
if (!value) return void 0;
|
|
@@ -35344,7 +33601,7 @@ async function cmdSkillsCreate(runtime, options) {
|
|
|
35344
33601
|
const metadata = lifecycle.create({
|
|
35345
33602
|
name: options.name,
|
|
35346
33603
|
description: options.description ?? "",
|
|
35347
|
-
content:
|
|
33604
|
+
content: readFileSync25(options.file, "utf8"),
|
|
35348
33605
|
createdBy: options.createdBy,
|
|
35349
33606
|
sources: {
|
|
35350
33607
|
runIds: csv(options.run) ?? [],
|
|
@@ -35502,8 +33759,8 @@ async function cmdMemorySync(runtime) {
|
|
|
35502
33759
|
|
|
35503
33760
|
// plugins/kxm/src/cli/hub.ts
|
|
35504
33761
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
35505
|
-
import { existsSync as
|
|
35506
|
-
import { join as
|
|
33762
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync25, readFileSync as readFileSync31, readdirSync as readdirSync12, rmSync as rmSync10, writeFileSync as writeFileSync23 } from "node:fs";
|
|
33763
|
+
import { join as join41, resolve as resolve24 } from "node:path";
|
|
35507
33764
|
|
|
35508
33765
|
// node_modules/marked/lib/marked.esm.js
|
|
35509
33766
|
function M() {
|
|
@@ -39665,10 +37922,10 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39665
37922
|
* @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
|
|
39666
37923
|
*/
|
|
39667
37924
|
queryTerminalBackgroundColor({ timeoutMs }) {
|
|
39668
|
-
return new Promise((
|
|
37925
|
+
return new Promise((resolve30) => {
|
|
39669
37926
|
const query = {
|
|
39670
37927
|
settled: false,
|
|
39671
|
-
resolve:
|
|
37928
|
+
resolve: resolve30,
|
|
39672
37929
|
timer: void 0
|
|
39673
37930
|
};
|
|
39674
37931
|
query.timer = setTimeout(() => {
|
|
@@ -39691,7 +37948,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39691
37948
|
* `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
|
|
39692
37949
|
*/
|
|
39693
37950
|
queryTerminalColorScheme({ timeoutMs }) {
|
|
39694
|
-
return new Promise((
|
|
37951
|
+
return new Promise((resolve30) => {
|
|
39695
37952
|
let settled = false;
|
|
39696
37953
|
let timer;
|
|
39697
37954
|
let unsubscribe = () => {
|
|
@@ -39705,7 +37962,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
39705
37962
|
timer = void 0;
|
|
39706
37963
|
}
|
|
39707
37964
|
unsubscribe();
|
|
39708
|
-
|
|
37965
|
+
resolve30(scheme);
|
|
39709
37966
|
};
|
|
39710
37967
|
unsubscribe = this.onTerminalColorSchemeChange(settle);
|
|
39711
37968
|
timer = setTimeout(() => settle(void 0), timeoutMs);
|
|
@@ -41436,7 +39693,7 @@ import * as path2 from "node:path";
|
|
|
41436
39693
|
|
|
41437
39694
|
// node_modules/@earendil-works/pi-tui/dist/native-module-path.js
|
|
41438
39695
|
import { createRequire as createRequire2 } from "node:module";
|
|
41439
|
-
import { dirname as dirname17, join as
|
|
39696
|
+
import { dirname as dirname17, join as join32 } from "node:path";
|
|
41440
39697
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
41441
39698
|
var moduleRequire = createRequire2(import.meta.url);
|
|
41442
39699
|
var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
|
|
@@ -41445,10 +39702,10 @@ function getNativeModuleCandidates(nativePath, options = {}) {
|
|
|
41445
39702
|
const candidates = [];
|
|
41446
39703
|
try {
|
|
41447
39704
|
const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
|
|
41448
|
-
candidates.push(
|
|
39705
|
+
candidates.push(join32(dirname17(packageEntry), "..", nativePath));
|
|
41449
39706
|
} catch {
|
|
41450
39707
|
}
|
|
41451
|
-
candidates.push(
|
|
39708
|
+
candidates.push(join32(moduleDir, "..", nativePath), join32(moduleDir, nativePath), join32(dirname17(options.execPath ?? process.execPath), nativePath));
|
|
41452
39709
|
return Array.from(new Set(candidates));
|
|
41453
39710
|
}
|
|
41454
39711
|
|
|
@@ -41789,7 +40046,7 @@ var ProcessTerminal = class {
|
|
|
41789
40046
|
break;
|
|
41790
40047
|
if (now - lastDataTime >= idleMs)
|
|
41791
40048
|
break;
|
|
41792
|
-
await new Promise((
|
|
40049
|
+
await new Promise((resolve30) => setTimeout(resolve30, Math.min(idleMs, timeLeft)));
|
|
41793
40050
|
}
|
|
41794
40051
|
} finally {
|
|
41795
40052
|
process.stdin.removeListener("data", onData);
|
|
@@ -43904,8 +42161,8 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
43904
42161
|
var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
|
|
43905
42162
|
|
|
43906
42163
|
// plugins/kxm/src/tui.ts
|
|
43907
|
-
import { mkdirSync as
|
|
43908
|
-
import { join as
|
|
42164
|
+
import { mkdirSync as mkdirSync22 } from "node:fs";
|
|
42165
|
+
import { join as join36, resolve as resolve23, dirname as dirname18 } from "node:path";
|
|
43909
42166
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
43910
42167
|
|
|
43911
42168
|
// packages/core/tui/src/types/surface.ts
|
|
@@ -44040,9 +42297,9 @@ function slugifyBranchPart(text, maxLength = 40) {
|
|
|
44040
42297
|
}
|
|
44041
42298
|
|
|
44042
42299
|
// plugins/kxm/src/local-snapshot.ts
|
|
44043
|
-
import { existsSync as
|
|
42300
|
+
import { existsSync as existsSync26, readdirSync as readdirSync11, readFileSync as readFileSync26 } from "node:fs";
|
|
44044
42301
|
import { homedir as homedir6 } from "node:os";
|
|
44045
|
-
import { isAbsolute as isAbsolute8, join as
|
|
42302
|
+
import { isAbsolute as isAbsolute8, join as join35, resolve as resolve22 } from "node:path";
|
|
44046
42303
|
function processExists2(pid) {
|
|
44047
42304
|
try {
|
|
44048
42305
|
process.kill(pid, 0);
|
|
@@ -44162,36 +42419,36 @@ function readOpenMessageMetadata(database) {
|
|
|
44162
42419
|
return messages;
|
|
44163
42420
|
}
|
|
44164
42421
|
function resolveKxmSnapshotPaths(cwd, env = process.env) {
|
|
44165
|
-
const stateDir = env.KXM_STATE_DIR?.trim() ||
|
|
42422
|
+
const stateDir = env.KXM_STATE_DIR?.trim() || join35(cwd, ".kxm", "state");
|
|
44166
42423
|
const configured = env.KXM_DATA_PATH?.trim();
|
|
44167
|
-
const dataPath = configured ?
|
|
42424
|
+
const dataPath = configured ? resolve22(cwd, configured) : join35(stateDir, "kxm.db");
|
|
44168
42425
|
return { dataPath, stateDir };
|
|
44169
42426
|
}
|
|
44170
42427
|
function resolveKxmStateRoot(stateDir, options) {
|
|
44171
|
-
if (options?.kxmStateRoot &&
|
|
44172
|
-
return
|
|
42428
|
+
if (options?.kxmStateRoot && existsSync26(options.kxmStateRoot)) {
|
|
42429
|
+
return resolve22(options.kxmStateRoot);
|
|
44173
42430
|
}
|
|
44174
|
-
if (
|
|
42431
|
+
if (existsSync26(join35(stateDir, "runtime", "registry.db")) || existsSync26(join35(stateDir, "runtime", "projects"))) {
|
|
44175
42432
|
return stateDir;
|
|
44176
42433
|
}
|
|
44177
42434
|
const env = options?.env ?? process.env;
|
|
44178
42435
|
const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
|
|
44179
|
-
if (explicit && isAbsolute8(explicit) &&
|
|
44180
|
-
return
|
|
42436
|
+
if (explicit && isAbsolute8(explicit) && existsSync26(resolve22(explicit))) {
|
|
42437
|
+
return resolve22(explicit);
|
|
44181
42438
|
}
|
|
44182
42439
|
let base;
|
|
44183
42440
|
if (process.platform === "win32") {
|
|
44184
42441
|
const localAppData = env.LOCALAPPDATA?.trim();
|
|
44185
|
-
base = localAppData && isAbsolute8(localAppData) ? localAppData :
|
|
44186
|
-
base =
|
|
42442
|
+
base = localAppData && isAbsolute8(localAppData) ? localAppData : join35(homedir6(), "AppData", "Local");
|
|
42443
|
+
base = resolve22(base, "KXM");
|
|
44187
42444
|
} else if (process.platform === "darwin") {
|
|
44188
|
-
base =
|
|
42445
|
+
base = resolve22(homedir6(), "Library", "Application Support", "KXM");
|
|
44189
42446
|
} else {
|
|
44190
42447
|
const xdgState = env.XDG_STATE_HOME?.trim();
|
|
44191
|
-
base = xdgState && isAbsolute8(xdgState) ? xdgState :
|
|
44192
|
-
base =
|
|
42448
|
+
base = xdgState && isAbsolute8(xdgState) ? xdgState : join35(homedir6(), ".local", "state");
|
|
42449
|
+
base = resolve22(base, "kxm");
|
|
44193
42450
|
}
|
|
44194
|
-
if (
|
|
42451
|
+
if (existsSync26(base)) return base;
|
|
44195
42452
|
return void 0;
|
|
44196
42453
|
}
|
|
44197
42454
|
function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
@@ -44202,7 +42459,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44202
42459
|
let legacyRuns = [];
|
|
44203
42460
|
let legacyRunTotal = 0;
|
|
44204
42461
|
let plans = [];
|
|
44205
|
-
if (
|
|
42462
|
+
if (existsSync26(dataPath)) {
|
|
44206
42463
|
hasLegacy = true;
|
|
44207
42464
|
const database = new DatabaseSync(dataPath, { readOnly: true });
|
|
44208
42465
|
try {
|
|
@@ -44222,11 +42479,11 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44222
42479
|
let kxmRunTotal = 0;
|
|
44223
42480
|
const kxmStateRoot = resolveKxmStateRoot(stateDir, options);
|
|
44224
42481
|
if (kxmStateRoot) {
|
|
44225
|
-
const runtimeDir =
|
|
44226
|
-
const registryDbPath =
|
|
44227
|
-
const projectsDir =
|
|
42482
|
+
const runtimeDir = join35(kxmStateRoot, "runtime");
|
|
42483
|
+
const registryDbPath = join35(runtimeDir, "registry.db");
|
|
42484
|
+
const projectsDir = join35(runtimeDir, "projects");
|
|
44228
42485
|
const projectKeys = /* @__PURE__ */ new Set();
|
|
44229
|
-
if (
|
|
42486
|
+
if (existsSync26(registryDbPath)) {
|
|
44230
42487
|
hasKxm = true;
|
|
44231
42488
|
try {
|
|
44232
42489
|
const regDb = new DatabaseSync(registryDbPath, { readOnly: true });
|
|
@@ -44242,7 +42499,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44242
42499
|
} catch {
|
|
44243
42500
|
}
|
|
44244
42501
|
}
|
|
44245
|
-
if (
|
|
42502
|
+
if (existsSync26(projectsDir)) {
|
|
44246
42503
|
try {
|
|
44247
42504
|
for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
|
|
44248
42505
|
if (entry.isDirectory()) {
|
|
@@ -44253,8 +42510,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44253
42510
|
}
|
|
44254
42511
|
}
|
|
44255
42512
|
for (const key of projectKeys) {
|
|
44256
|
-
const eventDbPath =
|
|
44257
|
-
if (
|
|
42513
|
+
const eventDbPath = join35(projectsDir, key, "run-events.db");
|
|
42514
|
+
if (existsSync26(eventDbPath)) {
|
|
44258
42515
|
hasKxm = true;
|
|
44259
42516
|
try {
|
|
44260
42517
|
const eventDb = new DatabaseSync(eventDbPath, { readOnly: true });
|
|
@@ -44284,10 +42541,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44284
42541
|
}
|
|
44285
42542
|
}
|
|
44286
42543
|
const pids = [];
|
|
44287
|
-
if (
|
|
42544
|
+
if (existsSync26(stateDir)) {
|
|
44288
42545
|
for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
|
|
44289
42546
|
try {
|
|
44290
|
-
const record = JSON.parse(
|
|
42547
|
+
const record = JSON.parse(readFileSync26(join35(stateDir, file), "utf8"));
|
|
44291
42548
|
pids.push({
|
|
44292
42549
|
file,
|
|
44293
42550
|
...record.role ? { role: record.role } : {},
|
|
@@ -44326,8 +42583,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
|
|
|
44326
42583
|
} else {
|
|
44327
42584
|
source = "legacy";
|
|
44328
42585
|
}
|
|
44329
|
-
const telemetryFile =
|
|
44330
|
-
const spend =
|
|
42586
|
+
const telemetryFile = join35(stateDir, "telemetry.jsonl");
|
|
42587
|
+
const spend = existsSync26(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
|
|
44331
42588
|
return {
|
|
44332
42589
|
source,
|
|
44333
42590
|
agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
|
|
@@ -44461,11 +42718,11 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
|
|
|
44461
42718
|
const cleanRunId = runId.replace(/^run_/, "");
|
|
44462
42719
|
const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
|
|
44463
42720
|
const branchName = `kxm/run-${cleanRunId}-${desc}`;
|
|
44464
|
-
const relativeWtPath =
|
|
44465
|
-
const absoluteWtPath =
|
|
42721
|
+
const relativeWtPath = join36(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
|
|
42722
|
+
const absoluteWtPath = resolve23(repoRoot, relativeWtPath);
|
|
44466
42723
|
const jumpCommand = `cd "${absoluteWtPath}"`;
|
|
44467
42724
|
try {
|
|
44468
|
-
|
|
42725
|
+
mkdirSync22(dirname18(absoluteWtPath), { recursive: true });
|
|
44469
42726
|
const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
|
|
44470
42727
|
let wtRes;
|
|
44471
42728
|
if (branchCheck.status === 0) {
|
|
@@ -44855,11 +43112,11 @@ async function readJson(response) {
|
|
|
44855
43112
|
}
|
|
44856
43113
|
async function waitForReconnect(signal, milliseconds = 1e3) {
|
|
44857
43114
|
if (signal.aborted) return;
|
|
44858
|
-
await new Promise((
|
|
43115
|
+
await new Promise((resolve30) => {
|
|
44859
43116
|
const done = () => {
|
|
44860
43117
|
clearTimeout(timer);
|
|
44861
43118
|
signal.removeEventListener("abort", done);
|
|
44862
|
-
|
|
43119
|
+
resolve30();
|
|
44863
43120
|
};
|
|
44864
43121
|
const timer = setTimeout(done, milliseconds);
|
|
44865
43122
|
timer.unref();
|
|
@@ -45222,8 +43479,8 @@ async function runMeshTui(input) {
|
|
|
45222
43479
|
// plugins/kxm/src/session-work.ts
|
|
45223
43480
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
45224
43481
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
45225
|
-
import { existsSync as
|
|
45226
|
-
import { join as
|
|
43482
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync23, readFileSync as readFileSync27, renameSync as renameSync8, writeFileSync as writeFileSync21 } from "node:fs";
|
|
43483
|
+
import { join as join37 } from "node:path";
|
|
45227
43484
|
var MAX_SESSION_BRIEF_TASKS = 5;
|
|
45228
43485
|
var MAX_SESSION_BRIEF_PLANS = 5;
|
|
45229
43486
|
var SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
|
|
@@ -45380,10 +43637,10 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
|
|
|
45380
43637
|
};
|
|
45381
43638
|
}
|
|
45382
43639
|
function readCachedSessionBrief(stateDir) {
|
|
45383
|
-
const file =
|
|
43640
|
+
const file = join37(stateDir, "session-brief.json");
|
|
45384
43641
|
try {
|
|
45385
|
-
if (!
|
|
45386
|
-
const raw =
|
|
43642
|
+
if (!existsSync27(file)) return void 0;
|
|
43643
|
+
const raw = readFileSync27(file, "utf8");
|
|
45387
43644
|
const parsed = JSON.parse(raw);
|
|
45388
43645
|
if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
|
|
45389
43646
|
const ageMs = Date.now() - Date.parse(parsed.generatedAt);
|
|
@@ -45398,18 +43655,18 @@ function readCachedSessionBrief(stateDir) {
|
|
|
45398
43655
|
}
|
|
45399
43656
|
function writeCachedSessionBrief(stateDir, brief) {
|
|
45400
43657
|
try {
|
|
45401
|
-
|
|
45402
|
-
const file =
|
|
43658
|
+
mkdirSync23(stateDir, { recursive: true, mode: 448 });
|
|
43659
|
+
const file = join37(stateDir, "session-brief.json");
|
|
45403
43660
|
const tmp = `${file}.tmp.${randomUUID13().slice(0, 8)}`;
|
|
45404
|
-
|
|
45405
|
-
|
|
43661
|
+
writeFileSync21(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
|
|
43662
|
+
renameSync8(tmp, file);
|
|
45406
43663
|
} catch {
|
|
45407
43664
|
}
|
|
45408
43665
|
}
|
|
45409
43666
|
function estimateSessionCost(stateDir) {
|
|
45410
43667
|
try {
|
|
45411
|
-
const telemetryFile =
|
|
45412
|
-
if (!
|
|
43668
|
+
const telemetryFile = join37(stateDir, "telemetry.jsonl");
|
|
43669
|
+
if (!existsSync27(telemetryFile)) return void 0;
|
|
45413
43670
|
const records = readRoutingRecords(telemetryFile);
|
|
45414
43671
|
if (records.length === 0) return void 0;
|
|
45415
43672
|
let totalCost = 0;
|
|
@@ -45577,11 +43834,11 @@ function formatSessionBriefText(brief) {
|
|
|
45577
43834
|
}
|
|
45578
43835
|
|
|
45579
43836
|
// plugins/kxm/src/kxm-install-kind.ts
|
|
45580
|
-
import { existsSync as
|
|
45581
|
-
import { dirname as dirname19, join as
|
|
43837
|
+
import { existsSync as existsSync28, readFileSync as readFileSync28 } from "node:fs";
|
|
43838
|
+
import { dirname as dirname19, join as join38 } from "node:path";
|
|
45582
43839
|
function packageIsKxm(root) {
|
|
45583
43840
|
try {
|
|
45584
|
-
const pkg = JSON.parse(
|
|
43841
|
+
const pkg = JSON.parse(readFileSync28(join38(root, "package.json"), "utf8"));
|
|
45585
43842
|
return pkg.name === "@kontextmind/kxm";
|
|
45586
43843
|
} catch {
|
|
45587
43844
|
return false;
|
|
@@ -45630,12 +43887,12 @@ function report(kind, root) {
|
|
|
45630
43887
|
return { kind, root, instruction: installKindInstruction(kind, root) };
|
|
45631
43888
|
}
|
|
45632
43889
|
function classifyInstallRoot(probe) {
|
|
45633
|
-
const piGitRoot =
|
|
43890
|
+
const piGitRoot = join38(probe.homeDir, ".pi", "agent", "git");
|
|
45634
43891
|
if (isUnder(probe.repoRoot, piGitRoot, probe.platform)) return report("pi-git", probe.repoRoot);
|
|
45635
|
-
const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() ||
|
|
45636
|
-
const pluginsRoot =
|
|
43892
|
+
const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join38(probe.homeDir, ".claude");
|
|
43893
|
+
const pluginsRoot = join38(claudeHome, "plugins");
|
|
45637
43894
|
if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
|
|
45638
|
-
const hasGit =
|
|
43895
|
+
const hasGit = existsSync28(join38(probe.repoRoot, ".git"));
|
|
45639
43896
|
if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
|
|
45640
43897
|
if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
|
|
45641
43898
|
return report("npm-package", probe.repoRoot);
|
|
@@ -45655,15 +43912,15 @@ function resolveInstallKind(probe, npmGlobalRoot) {
|
|
|
45655
43912
|
}
|
|
45656
43913
|
|
|
45657
43914
|
// plugins/kxm/src/kxm-update-config.ts
|
|
45658
|
-
var
|
|
45659
|
-
import { existsSync as
|
|
45660
|
-
import { join as
|
|
43915
|
+
var import_yaml15 = __toESM(require_dist(), 1);
|
|
43916
|
+
import { existsSync as existsSync29, readFileSync as readFileSync29 } from "node:fs";
|
|
43917
|
+
import { join as join39 } from "node:path";
|
|
45661
43918
|
function loadKxmUpdateConfig(env = process.env) {
|
|
45662
|
-
const path4 =
|
|
45663
|
-
if (!
|
|
43919
|
+
const path4 = join39(kxmUserStateRoot({ env }), "update.yaml");
|
|
43920
|
+
if (!existsSync29(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
|
|
45664
43921
|
let parsed;
|
|
45665
43922
|
try {
|
|
45666
|
-
parsed = (0,
|
|
43923
|
+
parsed = (0, import_yaml15.parse)(readFileSync29(path4, "utf8"));
|
|
45667
43924
|
} catch {
|
|
45668
43925
|
throw new KxmUpdateConfigError("update.yaml is not valid YAML");
|
|
45669
43926
|
}
|
|
@@ -45689,8 +43946,8 @@ function loadKxmUpdateConfig(env = process.env) {
|
|
|
45689
43946
|
}
|
|
45690
43947
|
|
|
45691
43948
|
// plugins/kxm/src/session.ts
|
|
45692
|
-
import { existsSync as
|
|
45693
|
-
import { join as
|
|
43949
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync22 } from "node:fs";
|
|
43950
|
+
import { join as join40 } from "node:path";
|
|
45694
43951
|
var SESSION_SCHEMA = "kxm.session.v1";
|
|
45695
43952
|
var SessionConfigError = class extends Error {
|
|
45696
43953
|
code = "session_config_invalid";
|
|
@@ -45700,12 +43957,12 @@ var SessionConfigError = class extends Error {
|
|
|
45700
43957
|
}
|
|
45701
43958
|
};
|
|
45702
43959
|
function workflowAssetDirs(assetsDir, workflowId) {
|
|
45703
|
-
const root =
|
|
45704
|
-
return [root,
|
|
43960
|
+
const root = join40(assetsDir, "workflows", workflowId);
|
|
43961
|
+
return [root, join40(root, "inputs"), join40(root, "outputs"), join40(root, "generated")];
|
|
45705
43962
|
}
|
|
45706
43963
|
function sessionAssetDirs(assetsDir, sessionId) {
|
|
45707
|
-
const root =
|
|
45708
|
-
return [root,
|
|
43964
|
+
const root = join40(assetsDir, "sessions", sessionId);
|
|
43965
|
+
return [root, join40(root, "inputs"), join40(root, "outputs")];
|
|
45709
43966
|
}
|
|
45710
43967
|
function rosterNames(configDir) {
|
|
45711
43968
|
return [...loadRosterMap(configDir).values()].map((row) => String(row.name));
|
|
@@ -45755,7 +44012,7 @@ function workerFromRosterRow(name, row, project) {
|
|
|
45755
44012
|
function loadRosterMap(configDir) {
|
|
45756
44013
|
const byName = /* @__PURE__ */ new Map();
|
|
45757
44014
|
for (const [file, key] of [["agents.json", "agents"], ["gates.json", "gates"]]) {
|
|
45758
|
-
const path4 =
|
|
44015
|
+
const path4 = join40(configDir, file);
|
|
45759
44016
|
for (const row of loadRoster(path4, key)) {
|
|
45760
44017
|
const name = String(row.name).trim();
|
|
45761
44018
|
const lowered = name.toLowerCase();
|
|
@@ -45769,10 +44026,10 @@ function loadRosterMap(configDir) {
|
|
|
45769
44026
|
return byName;
|
|
45770
44027
|
}
|
|
45771
44028
|
function loadRoster(path4, key) {
|
|
45772
|
-
if (!
|
|
44029
|
+
if (!existsSync30(path4)) return [];
|
|
45773
44030
|
let parsed;
|
|
45774
44031
|
try {
|
|
45775
|
-
parsed = JSON.parse(
|
|
44032
|
+
parsed = JSON.parse(readFileSync30(path4, "utf8"));
|
|
45776
44033
|
} catch (error) {
|
|
45777
44034
|
throw new SessionConfigError(
|
|
45778
44035
|
`${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -45797,7 +44054,7 @@ function loadRoster(path4, key) {
|
|
|
45797
44054
|
});
|
|
45798
44055
|
}
|
|
45799
44056
|
function createSession(input) {
|
|
45800
|
-
const assetDir = input.mode === "workflow" && input.workflowId ?
|
|
44057
|
+
const assetDir = input.mode === "workflow" && input.workflowId ? join40("assets", "workflows", input.workflowId) : join40("assets", "sessions", input.id);
|
|
45801
44058
|
return {
|
|
45802
44059
|
schema: SESSION_SCHEMA,
|
|
45803
44060
|
id: input.id,
|
|
@@ -45810,11 +44067,11 @@ function createSession(input) {
|
|
|
45810
44067
|
};
|
|
45811
44068
|
}
|
|
45812
44069
|
function writeSession(assetsDir, session, dryRun = false) {
|
|
45813
|
-
const dir =
|
|
45814
|
-
const path4 =
|
|
44070
|
+
const dir = join40(assetsDir, "sessions", session.id);
|
|
44071
|
+
const path4 = join40(dir, "session.json");
|
|
45815
44072
|
if (!dryRun) {
|
|
45816
|
-
|
|
45817
|
-
|
|
44073
|
+
mkdirSync24(dir, { recursive: true });
|
|
44074
|
+
writeFileSync22(path4, `${JSON.stringify(session, null, 2)}
|
|
45818
44075
|
`, { encoding: "utf8" });
|
|
45819
44076
|
}
|
|
45820
44077
|
return path4;
|
|
@@ -45842,22 +44099,22 @@ function installProbeFrom(runtime) {
|
|
|
45842
44099
|
const partial = runtime.io.installProbe ?? {};
|
|
45843
44100
|
return {
|
|
45844
44101
|
moduleDir: partial.moduleDir ?? dirname20(fileURLToPath3(import.meta.url)),
|
|
45845
|
-
repoRoot: partial.repoRoot ??
|
|
44102
|
+
repoRoot: partial.repoRoot ?? resolve24("."),
|
|
45846
44103
|
homeDir: partial.homeDir ?? homedir7(),
|
|
45847
44104
|
platform: partial.platform ?? process.platform,
|
|
45848
44105
|
env: partial.env ?? runtime.env
|
|
45849
44106
|
};
|
|
45850
44107
|
}
|
|
45851
44108
|
function warnIgnoredProjectUpdateYaml(runtime) {
|
|
45852
|
-
const projectFile =
|
|
45853
|
-
if (!
|
|
45854
|
-
const userFile =
|
|
44109
|
+
const projectFile = join41(runtime.dirs.workspace, "update.yaml");
|
|
44110
|
+
if (!existsSync31(projectFile)) return;
|
|
44111
|
+
const userFile = join41(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
45855
44112
|
runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
|
|
45856
44113
|
`);
|
|
45857
44114
|
}
|
|
45858
44115
|
async function refreshKxmUpdateNotice(runtime, config) {
|
|
45859
44116
|
const resolved = config ?? loadKxmUpdateConfig(runtime.env);
|
|
45860
|
-
const current = readInstalledKxmVersion(
|
|
44117
|
+
const current = readInstalledKxmVersion(resolve24("."));
|
|
45861
44118
|
const fetched = await fetchLatestKxmVersion(resolved.source, runtime.env, runtime.fetchImpl);
|
|
45862
44119
|
const notice = noticeFromVersions(current, fetched.latest, resolved, fetched.error, fetched.asset);
|
|
45863
44120
|
writeUpdateCache(runtime.dirs.state, notice);
|
|
@@ -45890,7 +44147,7 @@ async function cmdDash(runtime, options = {}) {
|
|
|
45890
44147
|
return 2;
|
|
45891
44148
|
}
|
|
45892
44149
|
const screen = requested;
|
|
45893
|
-
const dataPath =
|
|
44150
|
+
const dataPath = resolve24(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
|
|
45894
44151
|
if (runtime.dryRun) {
|
|
45895
44152
|
print(runtime.io, runtime.json, {
|
|
45896
44153
|
ok: true,
|
|
@@ -45936,7 +44193,7 @@ async function cmdHub(runtime) {
|
|
|
45936
44193
|
config = loadKxmUpdateConfig(runtime.env);
|
|
45937
44194
|
} catch (error) {
|
|
45938
44195
|
if (error instanceof KxmUpdateConfigError) {
|
|
45939
|
-
const yamlPath =
|
|
44196
|
+
const yamlPath = join41(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
|
|
45940
44197
|
runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
|
|
45941
44198
|
`);
|
|
45942
44199
|
} else {
|
|
@@ -46123,7 +44380,7 @@ async function cmdWorker(runtime, options) {
|
|
|
46123
44380
|
return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
|
|
46124
44381
|
}
|
|
46125
44382
|
async function cmdStop(runtime, waitMsFlag) {
|
|
46126
|
-
const pids =
|
|
44383
|
+
const pids = existsSync31(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
|
|
46127
44384
|
if (runtime.dryRun) {
|
|
46128
44385
|
print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
|
|
46129
44386
|
return 0;
|
|
@@ -46138,7 +44395,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46138
44395
|
const orphans = [];
|
|
46139
44396
|
for (const file of pids) {
|
|
46140
44397
|
try {
|
|
46141
|
-
const record = JSON.parse(
|
|
44398
|
+
const record = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
|
|
46142
44399
|
const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
|
|
46143
44400
|
const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
|
|
46144
44401
|
if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
|
|
@@ -46158,7 +44415,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46158
44415
|
ignored.push(file);
|
|
46159
44416
|
continue;
|
|
46160
44417
|
}
|
|
46161
|
-
|
|
44418
|
+
writeFileSync23(join41(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
46162
44419
|
`, { encoding: "utf8", mode: 384 });
|
|
46163
44420
|
requested.push(file);
|
|
46164
44421
|
records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
|
|
@@ -46176,7 +44433,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46176
44433
|
while (Date.now() <= deadline && stopped.size < requested.length) {
|
|
46177
44434
|
for (const [file, record] of records) {
|
|
46178
44435
|
try {
|
|
46179
|
-
const current = JSON.parse(
|
|
44436
|
+
const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
|
|
46180
44437
|
if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
|
|
46181
44438
|
} catch {
|
|
46182
44439
|
stopped.add(file);
|
|
@@ -46189,7 +44446,7 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46189
44446
|
for (const file of orphans) {
|
|
46190
44447
|
while (Date.now() <= deadlineOrphans) {
|
|
46191
44448
|
try {
|
|
46192
|
-
const current = JSON.parse(
|
|
44449
|
+
const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
|
|
46193
44450
|
if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
|
|
46194
44451
|
} catch {
|
|
46195
44452
|
break;
|
|
@@ -46197,14 +44454,14 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46197
44454
|
await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
|
|
46198
44455
|
}
|
|
46199
44456
|
try {
|
|
46200
|
-
const current = JSON.parse(
|
|
44457
|
+
const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
|
|
46201
44458
|
if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
|
|
46202
44459
|
try {
|
|
46203
44460
|
process.kill(current.serverPid, "SIGKILL");
|
|
46204
44461
|
} catch {
|
|
46205
44462
|
}
|
|
46206
44463
|
}
|
|
46207
|
-
|
|
44464
|
+
rmSync10(join41(runtime.dirs.state, file), { force: true });
|
|
46208
44465
|
} catch {
|
|
46209
44466
|
}
|
|
46210
44467
|
stopped.add(file);
|
|
@@ -46215,11 +44472,11 @@ async function cmdStop(runtime, waitMsFlag) {
|
|
|
46215
44472
|
}
|
|
46216
44473
|
async function cmdSessionStatus(runtime) {
|
|
46217
44474
|
const stateDir = runtime.dirs.state;
|
|
46218
|
-
const names3 =
|
|
44475
|
+
const names3 = existsSync31(stateDir) ? readdirSync12(stateDir) : [];
|
|
46219
44476
|
const claims = [];
|
|
46220
44477
|
for (const file of names3.filter((name) => name.endsWith(".pid"))) {
|
|
46221
44478
|
try {
|
|
46222
|
-
const record = JSON.parse(
|
|
44479
|
+
const record = JSON.parse(readFileSync31(join41(stateDir, file), "utf8"));
|
|
46223
44480
|
claims.push({
|
|
46224
44481
|
file,
|
|
46225
44482
|
role: record.role,
|
|
@@ -46234,7 +44491,7 @@ async function cmdSessionStatus(runtime) {
|
|
|
46234
44491
|
const recoveries = [];
|
|
46235
44492
|
for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
|
|
46236
44493
|
try {
|
|
46237
|
-
const envelope = JSON.parse(
|
|
44494
|
+
const envelope = JSON.parse(readFileSync31(join41(stateDir, file), "utf8"));
|
|
46238
44495
|
recoveries.push({
|
|
46239
44496
|
file,
|
|
46240
44497
|
reason: envelope.reason,
|
|
@@ -46339,7 +44596,7 @@ async function cmdSessionBrief(runtime, options = {}) {
|
|
|
46339
44596
|
print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
|
|
46340
44597
|
return 0;
|
|
46341
44598
|
}
|
|
46342
|
-
const dataPath =
|
|
44599
|
+
const dataPath = resolve24(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
|
|
46343
44600
|
const env = {
|
|
46344
44601
|
...runtime.env,
|
|
46345
44602
|
KXM_STATE_DIR: runtime.dirs.state,
|
|
@@ -46416,7 +44673,7 @@ async function cmdSessionStart(runtime, options) {
|
|
|
46416
44673
|
...workflowId ? workflowAssetDirs(runtime.dirs.assets, workflowId) : []
|
|
46417
44674
|
];
|
|
46418
44675
|
if (!runtime.dryRun) {
|
|
46419
|
-
for (const directory of created)
|
|
44676
|
+
for (const directory of created) mkdirSync25(directory, { recursive: true });
|
|
46420
44677
|
writeSession(runtime.dirs.assets, session);
|
|
46421
44678
|
}
|
|
46422
44679
|
print(runtime.io, runtime.json, {
|
|
@@ -46431,16 +44688,16 @@ async function cmdSessionStart(runtime, options) {
|
|
|
46431
44688
|
|
|
46432
44689
|
// plugins/kxm/src/cli/system.ts
|
|
46433
44690
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
46434
|
-
import { createHash as
|
|
46435
|
-
import { existsSync as
|
|
44691
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
44692
|
+
import { existsSync as existsSync37, mkdtempSync as mkdtempSync2, readFileSync as readFileSync36, rmSync as rmSync12 } from "node:fs";
|
|
46436
44693
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
46437
|
-
import { join as
|
|
44694
|
+
import { join as join47, resolve as resolve28 } from "node:path";
|
|
46438
44695
|
import { createInterface as createInterface3 } from "node:readline";
|
|
46439
44696
|
|
|
46440
44697
|
// plugins/kxm/src/improve.ts
|
|
46441
|
-
import { createHash as
|
|
46442
|
-
import { existsSync as
|
|
46443
|
-
import { join as
|
|
44698
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
44699
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync26, writeFileSync as writeFileSync24 } from "node:fs";
|
|
44700
|
+
import { join as join42, relative as relative6, resolve as resolve25 } from "node:path";
|
|
46444
44701
|
var CANDIDATE_SCHEMA = "kxm.candidate.v1";
|
|
46445
44702
|
var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
|
|
46446
44703
|
function classifyCandidateKind(stepId, agentRole) {
|
|
@@ -46586,7 +44843,7 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
46586
44843
|
const isCandidate = recurrence >= minRecurrence && verifyPassRate >= minPassRate;
|
|
46587
44844
|
const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
|
|
46588
44845
|
const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
|
|
46589
|
-
const keyHash =
|
|
44846
|
+
const keyHash = createHash14("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
|
|
46590
44847
|
const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
|
|
46591
44848
|
rows.push({
|
|
46592
44849
|
workflowHash: group.workflowHash,
|
|
@@ -46611,8 +44868,8 @@ function groupRoutingRecords(records, options = {}) {
|
|
|
46611
44868
|
});
|
|
46612
44869
|
}
|
|
46613
44870
|
function buildImprovementReport(records, options = {}) {
|
|
46614
|
-
const projectRoot = options.projectRoot ?
|
|
46615
|
-
const candidatesDir = options.candidatesDir ?
|
|
44871
|
+
const projectRoot = options.projectRoot ? resolve25(options.projectRoot) : process.cwd();
|
|
44872
|
+
const candidatesDir = options.candidatesDir ? resolve25(options.candidatesDir) : join42(projectRoot, ".kxm", "candidates");
|
|
46616
44873
|
const groups = groupRoutingRecords(records, options);
|
|
46617
44874
|
const candidates = [];
|
|
46618
44875
|
for (const group of groups) {
|
|
@@ -46624,7 +44881,7 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46624
44881
|
group.workflowHash
|
|
46625
44882
|
);
|
|
46626
44883
|
const diffFileName = `${group.candidateId}.diff`;
|
|
46627
|
-
const diffFilePath =
|
|
44884
|
+
const diffFilePath = join42(candidatesDir, diffFileName);
|
|
46628
44885
|
const relDiffPath = relative6(projectRoot, diffFilePath).replace(/\\/g, "/");
|
|
46629
44886
|
const candidate = {
|
|
46630
44887
|
schema: CANDIDATE_SCHEMA,
|
|
@@ -46646,12 +44903,12 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46646
44903
|
createdAt: nowIso()
|
|
46647
44904
|
};
|
|
46648
44905
|
if (!options.dryRun) {
|
|
46649
|
-
if (!
|
|
46650
|
-
|
|
44906
|
+
if (!existsSync32(candidatesDir)) {
|
|
44907
|
+
mkdirSync26(candidatesDir, { recursive: true });
|
|
46651
44908
|
}
|
|
46652
|
-
|
|
46653
|
-
const jsonFilePath =
|
|
46654
|
-
|
|
44909
|
+
writeFileSync24(diffFilePath, diff, "utf8");
|
|
44910
|
+
const jsonFilePath = join42(candidatesDir, `${group.candidateId}.json`);
|
|
44911
|
+
writeFileSync24(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
|
|
46655
44912
|
}
|
|
46656
44913
|
candidates.push(candidate);
|
|
46657
44914
|
}
|
|
@@ -46666,10 +44923,10 @@ function buildImprovementReport(records, options = {}) {
|
|
|
46666
44923
|
}
|
|
46667
44924
|
function writeImprovementReport(improvementsDir, report2, dryRun = false) {
|
|
46668
44925
|
const stamp = report2.createdAt.replace(/[:.]/g, "-");
|
|
46669
|
-
const path4 =
|
|
44926
|
+
const path4 = join42(improvementsDir, `${stamp}.json`);
|
|
46670
44927
|
if (!dryRun) {
|
|
46671
|
-
|
|
46672
|
-
|
|
44928
|
+
mkdirSync26(improvementsDir, { recursive: true });
|
|
44929
|
+
writeFileSync24(path4, `${JSON.stringify(report2, null, 2)}
|
|
46673
44930
|
`, { encoding: "utf8" });
|
|
46674
44931
|
}
|
|
46675
44932
|
return path4;
|
|
@@ -46708,9 +44965,9 @@ function formatImprovementReport(report2) {
|
|
|
46708
44965
|
}
|
|
46709
44966
|
|
|
46710
44967
|
// plugins/kxm/src/modes.ts
|
|
46711
|
-
var
|
|
46712
|
-
import { existsSync as
|
|
46713
|
-
import { join as
|
|
44968
|
+
var import_yaml16 = __toESM(require_dist(), 1);
|
|
44969
|
+
import { existsSync as existsSync33, readFileSync as readFileSync33 } from "node:fs";
|
|
44970
|
+
import { join as join43 } from "node:path";
|
|
46714
44971
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
46715
44972
|
schema: "kxm.modes.v1",
|
|
46716
44973
|
majorModes: {
|
|
@@ -46773,13 +45030,13 @@ function loadModesConfig(projectRoot) {
|
|
|
46773
45030
|
if (!projectRoot) {
|
|
46774
45031
|
return DEFAULT_MODES_CONFIG;
|
|
46775
45032
|
}
|
|
46776
|
-
const modesPath =
|
|
46777
|
-
if (!
|
|
45033
|
+
const modesPath = join43(projectRoot, ".kxm", "modes.yaml");
|
|
45034
|
+
if (!existsSync33(modesPath)) {
|
|
46778
45035
|
return DEFAULT_MODES_CONFIG;
|
|
46779
45036
|
}
|
|
46780
45037
|
try {
|
|
46781
|
-
const raw =
|
|
46782
|
-
const parsed = (0,
|
|
45038
|
+
const raw = readFileSync33(modesPath, "utf8");
|
|
45039
|
+
const parsed = (0, import_yaml16.parse)(raw);
|
|
46783
45040
|
if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
|
|
46784
45041
|
return {
|
|
46785
45042
|
schema: "kxm.modes.v1",
|
|
@@ -46864,11 +45121,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
46864
45121
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
46865
45122
|
});
|
|
46866
45123
|
for (const relPath of resolved.contextFiles) {
|
|
46867
|
-
const fullPath =
|
|
45124
|
+
const fullPath = join43(projectRoot, relPath);
|
|
46868
45125
|
let chars = 0;
|
|
46869
|
-
if (
|
|
45126
|
+
if (existsSync33(fullPath)) {
|
|
46870
45127
|
try {
|
|
46871
|
-
chars =
|
|
45128
|
+
chars = readFileSync33(fullPath, "utf8").length;
|
|
46872
45129
|
} catch {
|
|
46873
45130
|
chars = 0;
|
|
46874
45131
|
}
|
|
@@ -47000,9 +45257,9 @@ ${divider}
|
|
|
47000
45257
|
|
|
47001
45258
|
// plugins/kxm/src/ssh-remote.ts
|
|
47002
45259
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
47003
|
-
import { existsSync as
|
|
45260
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync27, readFileSync as readFileSync34, readdirSync as readdirSync13, rmSync as rmSync11, statSync as statSync5 } from "node:fs";
|
|
47004
45261
|
import { homedir as homedir8 } from "node:os";
|
|
47005
|
-
import { join as
|
|
45262
|
+
import { join as join44, resolve as resolve26 } from "node:path";
|
|
47006
45263
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
47007
45264
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
47008
45265
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -47028,12 +45285,12 @@ function truncateSshOutput(raw) {
|
|
|
47028
45285
|
return { text, truncated };
|
|
47029
45286
|
}
|
|
47030
45287
|
function parseSshConfig(configPath) {
|
|
47031
|
-
const targetPath = configPath ??
|
|
47032
|
-
if (!
|
|
45288
|
+
const targetPath = configPath ?? join44(homedir8(), ".ssh", "config");
|
|
45289
|
+
if (!existsSync34(targetPath)) {
|
|
47033
45290
|
return [];
|
|
47034
45291
|
}
|
|
47035
45292
|
try {
|
|
47036
|
-
const content =
|
|
45293
|
+
const content = readFileSync34(targetPath, "utf-8");
|
|
47037
45294
|
const lines = content.split("\n");
|
|
47038
45295
|
const hosts = [];
|
|
47039
45296
|
let currentHosts = [];
|
|
@@ -47110,15 +45367,15 @@ function resolveSshHostG(host, execFn = spawnSync7) {
|
|
|
47110
45367
|
}
|
|
47111
45368
|
}
|
|
47112
45369
|
function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
47113
|
-
const resolved =
|
|
47114
|
-
if (!
|
|
47115
|
-
|
|
45370
|
+
const resolved = resolve26(socketDir);
|
|
45371
|
+
if (!existsSync34(resolved)) {
|
|
45372
|
+
mkdirSync27(resolved, { recursive: true, mode: 448 });
|
|
47116
45373
|
}
|
|
47117
45374
|
return resolved;
|
|
47118
45375
|
}
|
|
47119
45376
|
function buildSshArgs(options) {
|
|
47120
45377
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
47121
|
-
const controlPath =
|
|
45378
|
+
const controlPath = join44(socketDir, "%C");
|
|
47122
45379
|
const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
47123
45380
|
const args = [
|
|
47124
45381
|
"-o",
|
|
@@ -47143,7 +45400,7 @@ function buildSshArgs(options) {
|
|
|
47143
45400
|
}
|
|
47144
45401
|
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
|
|
47145
45402
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
47146
|
-
const controlPath =
|
|
45403
|
+
const controlPath = join44(resolvedDir, "%C");
|
|
47147
45404
|
try {
|
|
47148
45405
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
47149
45406
|
encoding: "utf-8"
|
|
@@ -47155,7 +45412,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
47155
45412
|
}
|
|
47156
45413
|
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
|
|
47157
45414
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
47158
|
-
const controlPath =
|
|
45415
|
+
const controlPath = join44(resolvedDir, "%C");
|
|
47159
45416
|
try {
|
|
47160
45417
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
47161
45418
|
encoding: "utf-8"
|
|
@@ -47367,7 +45624,6 @@ function executeSshRun(params) {
|
|
|
47367
45624
|
// plugins/kxm/src/autocomplete.ts
|
|
47368
45625
|
var TOP_LEVEL_COMMANDS = [
|
|
47369
45626
|
"init",
|
|
47370
|
-
"migrate",
|
|
47371
45627
|
"backup",
|
|
47372
45628
|
"restore",
|
|
47373
45629
|
"run",
|
|
@@ -47400,7 +45656,6 @@ var TOP_LEVEL_COMMANDS = [
|
|
|
47400
45656
|
];
|
|
47401
45657
|
var SUBCOMMANDS = {
|
|
47402
45658
|
runs: ["status", "cancel", "list"],
|
|
47403
|
-
migrate: ["plan", "apply", "verify"],
|
|
47404
45659
|
harness: ["list"],
|
|
47405
45660
|
runtime: ["start", "status", "stop"],
|
|
47406
45661
|
trust: ["diff", "check"],
|
|
@@ -47492,7 +45747,6 @@ _kxm() {
|
|
|
47492
45747
|
local -a commands
|
|
47493
45748
|
commands=(
|
|
47494
45749
|
'init:Create or validate a KXM project'
|
|
47495
|
-
'migrate:Plan, apply, and verify legacy configuration migration'
|
|
47496
45750
|
'backup:Create a verified SQLite backup manifest'
|
|
47497
45751
|
'restore:Restore SQLite stores from a backup manifest'
|
|
47498
45752
|
'run:Create a KXM workflow run'
|
|
@@ -47601,9 +45855,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
47601
45855
|
}
|
|
47602
45856
|
|
|
47603
45857
|
// plugins/kxm/src/completion-install.ts
|
|
47604
|
-
import { existsSync as
|
|
45858
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync35, writeFileSync as writeFileSync25 } from "node:fs";
|
|
47605
45859
|
import { homedir as homedir9 } from "node:os";
|
|
47606
|
-
import { basename as basename9, delimiter, dirname as dirname22, isAbsolute as isAbsolute9, join as
|
|
45860
|
+
import { basename as basename9, delimiter, dirname as dirname22, isAbsolute as isAbsolute9, join as join45, resolve as resolve27 } from "node:path";
|
|
47607
45861
|
var COMPLETION_MARKER = "# kxm completion";
|
|
47608
45862
|
var PATH_MARKER = "# kxm path";
|
|
47609
45863
|
function detectShell(env = process.env, platform = process.platform) {
|
|
@@ -47619,31 +45873,31 @@ function effectiveHome(options) {
|
|
|
47619
45873
|
const env = options.env ?? process.env;
|
|
47620
45874
|
if (options.homeDir) return options.homeDir;
|
|
47621
45875
|
const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
|
|
47622
|
-
return envHome && envHome.length > 0 ?
|
|
45876
|
+
return envHome && envHome.length > 0 ? resolve27(envHome) : homedir9();
|
|
47623
45877
|
}
|
|
47624
45878
|
function completionScriptPath(shell, options = {}) {
|
|
47625
45879
|
const env = options.env ?? process.env;
|
|
47626
45880
|
const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
|
|
47627
|
-
const base = explicit && explicit.length > 0 ?
|
|
47628
|
-
return
|
|
45881
|
+
const base = explicit && explicit.length > 0 ? resolve27(explicit) : resolve27(effectiveHome(options), ".config", "kxm");
|
|
45882
|
+
return join45(base, "completions", `kxm.${shell}`);
|
|
47629
45883
|
}
|
|
47630
45884
|
function bashRcCandidate(options) {
|
|
47631
45885
|
const home = effectiveHome(options);
|
|
47632
|
-
const candidates = [
|
|
47633
|
-
const existing = candidates.find((candidate) =>
|
|
45886
|
+
const candidates = [join45(home, ".bashrc"), join45(home, ".bash_profile")];
|
|
45887
|
+
const existing = candidates.find((candidate) => existsSync35(candidate));
|
|
47634
45888
|
return existing ?? candidates[0];
|
|
47635
45889
|
}
|
|
47636
45890
|
function zshRcCandidate(options) {
|
|
47637
45891
|
const env = options.env ?? process.env;
|
|
47638
45892
|
const home = effectiveHome(options);
|
|
47639
|
-
if (env.ZDOTDIR?.trim()) return
|
|
47640
|
-
return
|
|
45893
|
+
if (env.ZDOTDIR?.trim()) return join45(resolve27(env.ZDOTDIR.trim()), ".zshrc");
|
|
45894
|
+
return join45(home, ".zshrc");
|
|
47641
45895
|
}
|
|
47642
45896
|
function fishCompletionTarget(shell, options) {
|
|
47643
45897
|
const env = options.env ?? process.env;
|
|
47644
45898
|
const home = effectiveHome(options);
|
|
47645
|
-
if (env.XDG_CONFIG_HOME?.trim()) return
|
|
47646
|
-
return
|
|
45899
|
+
if (env.XDG_CONFIG_HOME?.trim()) return join45(resolve27(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
|
|
45900
|
+
return join45(home, ".config", "fish", "completions", "kxm.fish");
|
|
47647
45901
|
}
|
|
47648
45902
|
function completionRcTarget(shell, options = {}) {
|
|
47649
45903
|
if (shell === "fish") {
|
|
@@ -47675,17 +45929,17 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47675
45929
|
const scriptPath = completionScriptPath(shell, options);
|
|
47676
45930
|
const { rcFile } = completionRcTarget(shell, options);
|
|
47677
45931
|
const script = generateShellCompletion(shell);
|
|
47678
|
-
const existingScript =
|
|
47679
|
-
const existingScriptMatches = existingScript &&
|
|
45932
|
+
const existingScript = existsSync35(scriptPath);
|
|
45933
|
+
const existingScriptMatches = existingScript && readFileSync35(scriptPath, "utf8") === script;
|
|
47680
45934
|
let rcModified = false;
|
|
47681
45935
|
let alreadyInstalled = false;
|
|
47682
45936
|
if (shell === "fish") {
|
|
47683
45937
|
const fishTarget = fishCompletionTarget(shell, options);
|
|
47684
|
-
const fishInstalled =
|
|
45938
|
+
const fishInstalled = existsSync35(fishTarget) && readFileSync35(fishTarget, "utf8") === script;
|
|
47685
45939
|
alreadyInstalled = fishInstalled;
|
|
47686
45940
|
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
47687
|
-
|
|
47688
|
-
|
|
45941
|
+
mkdirSync28(resolve27(fishTarget, ".."), { recursive: true });
|
|
45942
|
+
writeFileSync25(fishTarget, script, { encoding: "utf8" });
|
|
47689
45943
|
}
|
|
47690
45944
|
return {
|
|
47691
45945
|
ok: true,
|
|
@@ -47698,13 +45952,13 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47698
45952
|
}
|
|
47699
45953
|
if (rcFile) {
|
|
47700
45954
|
const line = sourceLine(shell, scriptPath);
|
|
47701
|
-
const rcContent =
|
|
45955
|
+
const rcContent = existsSync35(rcFile) ? readFileSync35(rcFile, "utf8") : "";
|
|
47702
45956
|
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
47703
45957
|
}
|
|
47704
45958
|
if (!options.dryRun) {
|
|
47705
45959
|
if (!existingScriptMatches || options.overwrite) {
|
|
47706
|
-
|
|
47707
|
-
|
|
45960
|
+
mkdirSync28(resolve27(scriptPath, ".."), { recursive: true });
|
|
45961
|
+
writeFileSync25(scriptPath, script, { encoding: "utf8" });
|
|
47708
45962
|
}
|
|
47709
45963
|
if (rcFile && !alreadyInstalled) {
|
|
47710
45964
|
const line = sourceLine(shell, scriptPath);
|
|
@@ -47713,8 +45967,8 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
47713
45967
|
${marker}
|
|
47714
45968
|
${line}
|
|
47715
45969
|
`;
|
|
47716
|
-
|
|
47717
|
-
|
|
45970
|
+
mkdirSync28(resolve27(rcFile, ".."), { recursive: true });
|
|
45971
|
+
writeFileSync25(rcFile, (existsSync35(rcFile) ? readFileSync35(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
47718
45972
|
rcModified = true;
|
|
47719
45973
|
}
|
|
47720
45974
|
}
|
|
@@ -47731,13 +45985,13 @@ function kxmBinDir(env = process.env) {
|
|
|
47731
45985
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
47732
45986
|
if (argv1 && isAbsolute9(argv1)) {
|
|
47733
45987
|
const dir = dirname22(argv1);
|
|
47734
|
-
if (
|
|
45988
|
+
if (existsSync35(join45(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
47735
45989
|
}
|
|
47736
45990
|
const pathEnv = env.PATH ?? "";
|
|
47737
45991
|
for (const part of pathEnv.split(delimiter)) {
|
|
47738
45992
|
if (!part) continue;
|
|
47739
|
-
const candidate =
|
|
47740
|
-
if (
|
|
45993
|
+
const candidate = resolve27(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
45994
|
+
if (existsSync35(candidate)) return resolve27(part);
|
|
47741
45995
|
}
|
|
47742
45996
|
return void 0;
|
|
47743
45997
|
}
|
|
@@ -47755,11 +46009,11 @@ function installPathEntry(shellInput, options = {}) {
|
|
|
47755
46009
|
}
|
|
47756
46010
|
const { rcFile } = completionRcTarget(shell, options);
|
|
47757
46011
|
const line = pathLine(binDir);
|
|
47758
|
-
const rcContent =
|
|
46012
|
+
const rcContent = existsSync35(rcFile ?? "") ? readFileSync35(rcFile, "utf8") : "";
|
|
47759
46013
|
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
47760
46014
|
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
47761
|
-
|
|
47762
|
-
|
|
46015
|
+
mkdirSync28(dirname22(rcFile), { recursive: true });
|
|
46016
|
+
writeFileSync25(rcFile, rcContent + `
|
|
47763
46017
|
${PATH_MARKER}
|
|
47764
46018
|
${line}
|
|
47765
46019
|
`, { encoding: "utf8" });
|
|
@@ -47768,9 +46022,9 @@ ${line}
|
|
|
47768
46022
|
}
|
|
47769
46023
|
|
|
47770
46024
|
// plugins/kxm/src/init-guide-setup.ts
|
|
47771
|
-
var
|
|
47772
|
-
import { existsSync as
|
|
47773
|
-
import { dirname as dirname23, join as
|
|
46025
|
+
var import_yaml17 = __toESM(require_dist(), 1);
|
|
46026
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
46027
|
+
import { dirname as dirname23, join as join46 } from "node:path";
|
|
47774
46028
|
var NATIVE_VENDOR_HARNESS = Object.freeze({
|
|
47775
46029
|
anthropic: "claude",
|
|
47776
46030
|
openai: "codex",
|
|
@@ -48169,14 +46423,14 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
48169
46423
|
const stage = roleStages.get(role);
|
|
48170
46424
|
if (!stage) continue;
|
|
48171
46425
|
files.push({
|
|
48172
|
-
path:
|
|
48173
|
-
content: (0,
|
|
46426
|
+
path: join46(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
46427
|
+
content: (0, import_yaml17.stringify)(agentDocument(role, stage, binding))
|
|
48174
46428
|
});
|
|
48175
46429
|
}
|
|
48176
46430
|
for (const workflow of plan.workflows) {
|
|
48177
46431
|
files.push({
|
|
48178
|
-
path:
|
|
48179
|
-
content: (0,
|
|
46432
|
+
path: join46(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
46433
|
+
content: (0, import_yaml17.stringify)(workflowDocument(workflow))
|
|
48180
46434
|
});
|
|
48181
46435
|
}
|
|
48182
46436
|
return files;
|
|
@@ -48185,12 +46439,12 @@ function writeGuideSetupFiles(files) {
|
|
|
48185
46439
|
const written = [];
|
|
48186
46440
|
const existed = [];
|
|
48187
46441
|
for (const file of files) {
|
|
48188
|
-
if (
|
|
46442
|
+
if (existsSync36(file.path)) {
|
|
48189
46443
|
existed.push(file.path);
|
|
48190
46444
|
continue;
|
|
48191
46445
|
}
|
|
48192
|
-
|
|
48193
|
-
|
|
46446
|
+
mkdirSync29(dirname23(file.path), { recursive: true });
|
|
46447
|
+
writeFileSync26(file.path, file.content, "utf8");
|
|
48194
46448
|
written.push(file.path);
|
|
48195
46449
|
}
|
|
48196
46450
|
return { written, existed };
|
|
@@ -48253,7 +46507,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
48253
46507
|
detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
|
|
48254
46508
|
};
|
|
48255
46509
|
}
|
|
48256
|
-
const releaseDir = mkdtempSync2(
|
|
46510
|
+
const releaseDir = mkdtempSync2(join47(tmpdir2(), "kxm-pkg-update-"));
|
|
48257
46511
|
try {
|
|
48258
46512
|
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
|
|
48259
46513
|
if (runtime.dryRun) {
|
|
@@ -48262,7 +46516,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
48262
46516
|
for (const step of planned) {
|
|
48263
46517
|
if (step.kind === "verify") {
|
|
48264
46518
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
48265
|
-
const actual =
|
|
46519
|
+
const actual = existsSync37(step.path) ? createHash15("sha256").update(readFileSync36(step.path)).digest("hex") : "missing";
|
|
48266
46520
|
return {
|
|
48267
46521
|
ok: false,
|
|
48268
46522
|
error: "release_digest_mismatch",
|
|
@@ -48279,7 +46533,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
48279
46533
|
}
|
|
48280
46534
|
return { ok: true, detail: `installed ${notice.latest} from ${notice.source}` };
|
|
48281
46535
|
} finally {
|
|
48282
|
-
|
|
46536
|
+
rmSync12(releaseDir, { recursive: true, force: true });
|
|
48283
46537
|
}
|
|
48284
46538
|
}
|
|
48285
46539
|
async function cmdExplain(runtime, options) {
|
|
@@ -48527,13 +46781,13 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
48527
46781
|
return 2;
|
|
48528
46782
|
}
|
|
48529
46783
|
const selectedFile = explicitFile || configuredFile;
|
|
48530
|
-
const file = selectedFile ?
|
|
48531
|
-
if (file && !
|
|
46784
|
+
const file = selectedFile ? resolve28(runtime.cwd, selectedFile) : void 0;
|
|
46785
|
+
if (file && !existsSync37(file)) {
|
|
48532
46786
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
48533
46787
|
return 1;
|
|
48534
46788
|
}
|
|
48535
46789
|
try {
|
|
48536
|
-
const raw = file ?
|
|
46790
|
+
const raw = file ? readFileSync36(file, "utf8") : inline;
|
|
48537
46791
|
const warnings = [];
|
|
48538
46792
|
const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
|
|
48539
46793
|
const secretEnvs = definitions.map((definition) => ({
|
|
@@ -48558,7 +46812,7 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
48558
46812
|
}
|
|
48559
46813
|
async function cmdArtifactsExist(runtime, pathFlag) {
|
|
48560
46814
|
const worker = gateOf(runtime, "artifacts-exist");
|
|
48561
|
-
const checked = verifyArtifactExists(runtime.dirs.assets,
|
|
46815
|
+
const checked = verifyArtifactExists(runtime.dirs.assets, resolve28(runtime.cwd, pathFlag));
|
|
48562
46816
|
if (!checked.ok) {
|
|
48563
46817
|
printWorker(
|
|
48564
46818
|
runtime,
|
|
@@ -48577,15 +46831,15 @@ async function cmdArtifactsExist(runtime, pathFlag) {
|
|
|
48577
46831
|
return 0;
|
|
48578
46832
|
}
|
|
48579
46833
|
async function cmdImprove(runtime, options = {}) {
|
|
48580
|
-
const file = options.file ?
|
|
48581
|
-
const routingRecords =
|
|
48582
|
-
const candidatesDir = options.outDir ?
|
|
46834
|
+
const file = options.file ? resolve28(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
|
|
46835
|
+
const routingRecords = existsSync37(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
|
|
46836
|
+
const candidatesDir = options.outDir ? resolve28(runtime.cwd, options.outDir) : join47(runtime.cwd, ".kxm", "candidates");
|
|
48583
46837
|
const report2 = buildImprovementReport(routingRecords, {
|
|
48584
46838
|
candidatesDir,
|
|
48585
46839
|
projectRoot: runtime.cwd,
|
|
48586
46840
|
dryRun: runtime.dryRun
|
|
48587
46841
|
});
|
|
48588
|
-
const reportDir =
|
|
46842
|
+
const reportDir = join47(runtime.dirs.assets, "improvements");
|
|
48589
46843
|
const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
|
|
48590
46844
|
const text = formatImprovementReport(report2);
|
|
48591
46845
|
print(runtime.io, runtime.json, {
|
|
@@ -48717,9 +46971,9 @@ async function maybeOfferCompletionInstall(runtime) {
|
|
|
48717
46971
|
if (!pathNeeded) {
|
|
48718
46972
|
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
48719
46973
|
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
48720
|
-
if (rcFile &&
|
|
46974
|
+
if (rcFile && existsSync37(rcFile)) {
|
|
48721
46975
|
try {
|
|
48722
|
-
if (
|
|
46976
|
+
if (readFileSync36(rcFile, "utf8").includes(scriptPath)) return;
|
|
48723
46977
|
} catch {
|
|
48724
46978
|
}
|
|
48725
46979
|
}
|
|
@@ -48851,7 +47105,7 @@ async function cmdRoutingReport(runtime, options) {
|
|
|
48851
47105
|
let catalog;
|
|
48852
47106
|
if (includeEquivalentListCost) {
|
|
48853
47107
|
try {
|
|
48854
|
-
const pricesPath = options.prices ?
|
|
47108
|
+
const pricesPath = options.prices ? resolve28(runtime.cwd, options.prices) : join47(runtime.dirs.workspace, "prices.yaml");
|
|
48855
47109
|
catalog = loadPriceCatalog(pricesPath);
|
|
48856
47110
|
} catch {
|
|
48857
47111
|
}
|
|
@@ -49083,23 +47337,12 @@ function createProgram(ctx, result) {
|
|
|
49083
47337
|
writeErr: (text) => ctx.io.stderr(text)
|
|
49084
47338
|
}).helpCommand("help", "Show help");
|
|
49085
47339
|
addGlobalOptions(program2);
|
|
49086
|
-
program2.command("init").description("Create, validate, or
|
|
47340
|
+
program2.command("init").description("Create, validate, repair, or join a KXM project").option("--json", "Print machine-readable JSON").option("--dry-run", "Plan without making changes").option("--name <name>", "Project display name for a new project").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--repository <id=absolute-path>", "Bind a member repository outside Git configuration", (value, previous) => [...previous, value], []).action(async function initAction(options) {
|
|
49087
47341
|
result.code = await cmdKxmInit(runtimeFrom(ctx, this), options, {
|
|
49088
47342
|
maybeOfferCompletionInstall,
|
|
49089
47343
|
maybeOfferGuideSetup
|
|
49090
47344
|
});
|
|
49091
47345
|
});
|
|
49092
|
-
const migrate = addGlobalOptions(program2.command("migrate").description("Plan, apply, and verify legacy JSON configuration migration"));
|
|
49093
|
-
migrate.helpCommand("help", "Show migrate help");
|
|
49094
|
-
addGlobalOptions(migrate.command("plan").description("Compute the deterministic legacy-to-KXM migration plan without writes")).action(async function migratePlanAction() {
|
|
49095
|
-
result.code = await cmdKxmMigratePlan(runtimeFrom(ctx, this));
|
|
49096
|
-
});
|
|
49097
|
-
addGlobalOptions(migrate.command("apply").description("Install a reviewed migration with a hash-linked receipt")).option("--decisions <file>", "Reviewed kxm.migration-decision.v1 YAML file").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--name <name>", "Project display name").action(async function migrateApplyAction(options) {
|
|
49098
|
-
result.code = await cmdKxmMigrateApply(runtimeFrom(ctx, this), options);
|
|
49099
|
-
});
|
|
49100
|
-
addGlobalOptions(migrate.command("verify").description("Verify a migration receipt against current sources and target bundle")).action(async function migrateVerifyAction() {
|
|
49101
|
-
result.code = await cmdKxmMigrateVerify(runtimeFrom(ctx, this));
|
|
49102
|
-
});
|
|
49103
47346
|
addGlobalOptions(program2.command("backup").description("Create a verified SQLite backup of all stores with a hashed manifest")).option("--out <dir>", "Directory to write backup and manifest").action(async function backupAction(options) {
|
|
49104
47347
|
result.code = await cmdBackup(runtimeFrom(ctx, this), options);
|
|
49105
47348
|
});
|
|
@@ -49556,7 +47799,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
|
|
|
49556
47799
|
throw error;
|
|
49557
47800
|
}
|
|
49558
47801
|
}
|
|
49559
|
-
if (process.argv[1] &&
|
|
47802
|
+
if (process.argv[1] && resolve29(process.argv[1]) === fileURLToPath4(import.meta.url)) {
|
|
49560
47803
|
const code = await runCli(process.argv.slice(2));
|
|
49561
47804
|
process.exitCode = code;
|
|
49562
47805
|
}
|