@kontextmind/kxm 0.7.177 → 0.7.178
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 +10 -0
- package/docs/architecture/index.md +2 -1
- package/docs/architecture/inventory.md +8 -6
- package/docs/contributing/development.md +4 -2
- package/docs/contributing/test-matrix.md +1 -0
- package/docs/contributing/tui-components.md +22 -4
- package/docs/reference/cli-reference.md +29 -1
- package/docs/reference/config-reference.md +17 -3
- package/package.json +2 -2
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +4 -3
- package/plugins/kxm/dist/cli.js +1771 -46
- package/plugins/kxm/dist/extension.js +11238 -1470
- package/plugins/kxm/dist/mcp-server.js +3 -2
- package/plugins/kxm/dist/runtime-supervisor.js +2 -1
- package/plugins/kxm/dist/runtime.js +2 -1
- package/plugins/kxm/dist/server.js +2 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/autocomplete.ts +3 -3
- package/plugins/kxm/src/cli/system.ts +37 -0
- package/plugins/kxm/src/cli.ts +7 -1
- package/plugins/kxm/src/config-editor.ts +479 -0
- package/plugins/kxm/src/config.ts +3 -2
- package/plugins/kxm/src/extension.ts +9 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/session-work.ts +2 -1
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -8880,7 +8880,7 @@ var require_public_api = __commonJS({
|
|
|
8880
8880
|
}
|
|
8881
8881
|
return doc;
|
|
8882
8882
|
}
|
|
8883
|
-
function
|
|
8883
|
+
function parse13(src, reviver, options) {
|
|
8884
8884
|
let _reviver = void 0;
|
|
8885
8885
|
if (typeof reviver === "function") {
|
|
8886
8886
|
_reviver = reviver;
|
|
@@ -8921,7 +8921,7 @@ var require_public_api = __commonJS({
|
|
|
8921
8921
|
return value.toString(options);
|
|
8922
8922
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
8923
8923
|
}
|
|
8924
|
-
exports.parse =
|
|
8924
|
+
exports.parse = parse13;
|
|
8925
8925
|
exports.parseAllDocuments = parseAllDocuments;
|
|
8926
8926
|
exports.parseDocument = parseDocument3;
|
|
8927
8927
|
exports.stringify = stringify12;
|
|
@@ -9184,6 +9184,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
9184
9184
|
const userConfigFile = join3(userDir, "config.yaml");
|
|
9185
9185
|
const repoDir = repoConfigDirectory(repoRoot);
|
|
9186
9186
|
const repoConfigFile = join3(repoDir, "config.yaml");
|
|
9187
|
+
const includeProject = options.layers !== "user";
|
|
9187
9188
|
let userRaw = {};
|
|
9188
9189
|
let userLoadedPath;
|
|
9189
9190
|
if (existsSync3(userConfigFile)) {
|
|
@@ -9197,7 +9198,7 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
|
|
|
9197
9198
|
}
|
|
9198
9199
|
let repoRaw = {};
|
|
9199
9200
|
let repoLoadedPath;
|
|
9200
|
-
if (existsSync3(repoConfigFile)) {
|
|
9201
|
+
if (includeProject && existsSync3(repoConfigFile)) {
|
|
9201
9202
|
try {
|
|
9202
9203
|
const text = readFileSync2(repoConfigFile, "utf8");
|
|
9203
9204
|
repoRaw = (0, import_yaml.parse)(text) ?? {};
|
|
@@ -14043,7 +14044,7 @@ var require_fast_uri = __commonJS({
|
|
|
14043
14044
|
normalizeString(uri, options);
|
|
14044
14045
|
} else if (typeof uri === "object") {
|
|
14045
14046
|
uri = /** @type {T} */
|
|
14046
|
-
|
|
14047
|
+
parse13(serialize(uri, options), options);
|
|
14047
14048
|
}
|
|
14048
14049
|
return uri;
|
|
14049
14050
|
}
|
|
@@ -14083,8 +14084,8 @@ var require_fast_uri = __commonJS({
|
|
|
14083
14084
|
function resolveComponent(base, relative8, options, skipNormalization) {
|
|
14084
14085
|
const target = {};
|
|
14085
14086
|
if (!skipNormalization) {
|
|
14086
|
-
base =
|
|
14087
|
-
relative8 =
|
|
14087
|
+
base = parse13(serialize(base, options), options);
|
|
14088
|
+
relative8 = parse13(serialize(relative8, options), options);
|
|
14088
14089
|
}
|
|
14089
14090
|
options = options || {};
|
|
14090
14091
|
if (!options.tolerant && relative8.scheme) {
|
|
@@ -14376,7 +14377,7 @@ var require_fast_uri = __commonJS({
|
|
|
14376
14377
|
}
|
|
14377
14378
|
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
|
|
14378
14379
|
}
|
|
14379
|
-
function
|
|
14380
|
+
function parse13(uri, opts) {
|
|
14380
14381
|
return parseWithStatus(uri, opts).parsed;
|
|
14381
14382
|
}
|
|
14382
14383
|
function normalizeString(uri, opts) {
|
|
@@ -14413,7 +14414,7 @@ var require_fast_uri = __commonJS({
|
|
|
14413
14414
|
resolveComponent,
|
|
14414
14415
|
equal,
|
|
14415
14416
|
serialize,
|
|
14416
|
-
parse:
|
|
14417
|
+
parse: parse13
|
|
14417
14418
|
};
|
|
14418
14419
|
module.exports = fastUri;
|
|
14419
14420
|
module.exports.default = fastUri;
|
|
@@ -19547,7 +19548,7 @@ function resolveDispatchStatus(entry, detected, authenticated, issues) {
|
|
|
19547
19548
|
return { status: "no", supported: false, reason: "not_authenticated" };
|
|
19548
19549
|
}
|
|
19549
19550
|
if (authenticated === null) {
|
|
19550
|
-
const authIssue = issues.find((
|
|
19551
|
+
const authIssue = issues.find((issue5) => issue5 === "auth_context_required" || issue5 === "auth_unknown" || issue5 === "auth_unparsed" || issue5.startsWith("auth_"));
|
|
19551
19552
|
return { status: "no", supported: false, reason: authIssue ?? issues[0] ?? "auth_unknown" };
|
|
19552
19553
|
}
|
|
19553
19554
|
return { status: "yes", supported: true };
|
|
@@ -21473,7 +21474,7 @@ var init_project_config = __esm({
|
|
|
21473
21474
|
issues;
|
|
21474
21475
|
constructor(issues) {
|
|
21475
21476
|
const sorted = sortIssues3(issues);
|
|
21476
|
-
super(sorted.map((
|
|
21477
|
+
super(sorted.map((issue5) => `${issue5.file}: ${issue5.code}: ${issue5.message}`).join("\n"));
|
|
21477
21478
|
this.name = "KxmConfigError";
|
|
21478
21479
|
this.issues = sorted;
|
|
21479
21480
|
}
|
|
@@ -22356,8 +22357,8 @@ import {
|
|
|
22356
22357
|
} from "node:fs";
|
|
22357
22358
|
import { basename as basename3, dirname as dirname9, isAbsolute as isAbsolute5, join as join12, relative as relative3, resolve as resolve10, sep as sep3 } from "node:path";
|
|
22358
22359
|
function databaseError(code, file, message) {
|
|
22359
|
-
const
|
|
22360
|
-
return new KxmConfigError([
|
|
22360
|
+
const issue5 = { phase: "semantic", code, file, message };
|
|
22361
|
+
return new KxmConfigError([issue5]);
|
|
22361
22362
|
}
|
|
22362
22363
|
function checkedParent(path4, description) {
|
|
22363
22364
|
const parent = dirname9(path4);
|
|
@@ -25451,8 +25452,8 @@ function sortIssues4(issues) {
|
|
|
25451
25452
|
return compareCodeUnits5(left.code, right.code);
|
|
25452
25453
|
});
|
|
25453
25454
|
}
|
|
25454
|
-
function pushIssue(sink,
|
|
25455
|
-
sink.issues.push({ workflowId: sink.workflowId, ...
|
|
25455
|
+
function pushIssue(sink, issue5) {
|
|
25456
|
+
sink.issues.push({ workflowId: sink.workflowId, ...issue5 });
|
|
25456
25457
|
}
|
|
25457
25458
|
var KXM_COMPILED_WORKFLOW_SCHEMA, WORKFLOW_SCHEMA, STEP_ID_PATTERN, SUPPORTED_STEP_KINDS, TERMINAL_STATUSES, JOIN_STRATEGIES, EVIDENCE_KINDS, REPOSITORY_ACCESS, DISTINCT_BY, BANNED_RUNTIME_HINT, KxmEngineCompileError;
|
|
25458
25459
|
var init_engine_compile = __esm({
|
|
@@ -25472,7 +25473,7 @@ var init_engine_compile = __esm({
|
|
|
25472
25473
|
issues;
|
|
25473
25474
|
constructor(issues) {
|
|
25474
25475
|
const sorted = sortIssues4(issues);
|
|
25475
|
-
super(sorted.map((
|
|
25476
|
+
super(sorted.map((issue5) => `${issue5.workflowId}: ${issue5.code}: ${issue5.message}`).join("\n"));
|
|
25476
25477
|
this.name = "KxmEngineCompileError";
|
|
25477
25478
|
this.issues = sorted;
|
|
25478
25479
|
}
|
|
@@ -31226,7 +31227,7 @@ async function cmdKxmInit(runtime, options, postHooks) {
|
|
|
31226
31227
|
return finishInit(0, `init plan: ${initialized.plan.mode}`);
|
|
31227
31228
|
}
|
|
31228
31229
|
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";
|
|
31229
|
-
return finishInit(1, [next, ...initialized.plan.issues.map((
|
|
31230
|
+
return finishInit(1, [next, ...initialized.plan.issues.map((issue5) => `${issue5.file}: ${issue5.code}: ${issue5.message}`)].join("\n"));
|
|
31230
31231
|
} catch (error) {
|
|
31231
31232
|
if (error instanceof KxmConfigError) {
|
|
31232
31233
|
print(runtime.io, runtime.json, {
|
|
@@ -40186,7 +40187,7 @@ async function cmdContextWikiLint(runtime, project) {
|
|
|
40186
40187
|
const compiled = response.body;
|
|
40187
40188
|
const issues = compiled.lint;
|
|
40188
40189
|
print(runtime.io, runtime.json, { ok: issues.length === 0, command: "context wiki-lint", project, issues, audit: compiled.audit }, issues.length === 0 ? "wiki lint clean" : `wiki lint found ${issues.length} issue(s)`);
|
|
40189
|
-
return issues.every((
|
|
40190
|
+
return issues.every((issue5) => issue5.severity !== "error") ? 0 : 1;
|
|
40190
40191
|
}
|
|
40191
40192
|
function skillStateFromFlag(value) {
|
|
40192
40193
|
if (value === "candidate" || value === "promoted" || value === "quarantined" || value === "rejected") return value;
|
|
@@ -44327,6 +44328,66 @@ var LEGACY_CTRL_SEQUENCES = {
|
|
|
44327
44328
|
home: ["\x1B[7^"],
|
|
44328
44329
|
end: ["\x1B[8^"]
|
|
44329
44330
|
};
|
|
44331
|
+
var LEGACY_SEQUENCE_KEY_IDS = {
|
|
44332
|
+
"\x1BOA": "up",
|
|
44333
|
+
"\x1BOB": "down",
|
|
44334
|
+
"\x1BOC": "right",
|
|
44335
|
+
"\x1BOD": "left",
|
|
44336
|
+
"\x1BOH": "home",
|
|
44337
|
+
"\x1BOF": "end",
|
|
44338
|
+
"\x1B[E": "clear",
|
|
44339
|
+
"\x1BOE": "clear",
|
|
44340
|
+
"\x1BOe": "ctrl+clear",
|
|
44341
|
+
"\x1B[e": "shift+clear",
|
|
44342
|
+
"\x1B[2~": "insert",
|
|
44343
|
+
"\x1B[2$": "shift+insert",
|
|
44344
|
+
"\x1B[2^": "ctrl+insert",
|
|
44345
|
+
"\x1B[3$": "shift+delete",
|
|
44346
|
+
"\x1B[3^": "ctrl+delete",
|
|
44347
|
+
"\x1B[[5~": "pageUp",
|
|
44348
|
+
"\x1B[[6~": "pageDown",
|
|
44349
|
+
"\x1B[a": "shift+up",
|
|
44350
|
+
"\x1B[b": "shift+down",
|
|
44351
|
+
"\x1B[c": "shift+right",
|
|
44352
|
+
"\x1B[d": "shift+left",
|
|
44353
|
+
"\x1BOa": "ctrl+up",
|
|
44354
|
+
"\x1BOb": "ctrl+down",
|
|
44355
|
+
"\x1BOc": "ctrl+right",
|
|
44356
|
+
"\x1BOd": "ctrl+left",
|
|
44357
|
+
"\x1B[5$": "shift+pageUp",
|
|
44358
|
+
"\x1B[6$": "shift+pageDown",
|
|
44359
|
+
"\x1B[7$": "shift+home",
|
|
44360
|
+
"\x1B[8$": "shift+end",
|
|
44361
|
+
"\x1B[5^": "ctrl+pageUp",
|
|
44362
|
+
"\x1B[6^": "ctrl+pageDown",
|
|
44363
|
+
"\x1B[7^": "ctrl+home",
|
|
44364
|
+
"\x1B[8^": "ctrl+end",
|
|
44365
|
+
"\x1BOP": "f1",
|
|
44366
|
+
"\x1BOQ": "f2",
|
|
44367
|
+
"\x1BOR": "f3",
|
|
44368
|
+
"\x1BOS": "f4",
|
|
44369
|
+
"\x1B[11~": "f1",
|
|
44370
|
+
"\x1B[12~": "f2",
|
|
44371
|
+
"\x1B[13~": "f3",
|
|
44372
|
+
"\x1B[14~": "f4",
|
|
44373
|
+
"\x1B[[A": "f1",
|
|
44374
|
+
"\x1B[[B": "f2",
|
|
44375
|
+
"\x1B[[C": "f3",
|
|
44376
|
+
"\x1B[[D": "f4",
|
|
44377
|
+
"\x1B[[E": "f5",
|
|
44378
|
+
"\x1B[15~": "f5",
|
|
44379
|
+
"\x1B[17~": "f6",
|
|
44380
|
+
"\x1B[18~": "f7",
|
|
44381
|
+
"\x1B[19~": "f8",
|
|
44382
|
+
"\x1B[20~": "f9",
|
|
44383
|
+
"\x1B[21~": "f10",
|
|
44384
|
+
"\x1B[23~": "f11",
|
|
44385
|
+
"\x1B[24~": "f12",
|
|
44386
|
+
"\x1Bb": "alt+left",
|
|
44387
|
+
"\x1Bf": "alt+right",
|
|
44388
|
+
"\x1Bp": "alt+up",
|
|
44389
|
+
"\x1Bn": "alt+down"
|
|
44390
|
+
};
|
|
44330
44391
|
var matchesLegacySequence = (data, sequences) => sequences.includes(data);
|
|
44331
44392
|
var matchesLegacyModifierSequence = (data, key, modifier) => {
|
|
44332
44393
|
if (modifier === MODIFIERS.shift) {
|
|
@@ -44472,6 +44533,22 @@ function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier
|
|
|
44472
44533
|
return false;
|
|
44473
44534
|
return normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
|
|
44474
44535
|
}
|
|
44536
|
+
function formatKeyNameWithModifiers(keyName, modifier) {
|
|
44537
|
+
const mods = [];
|
|
44538
|
+
const effectiveMod = modifier & ~LOCK_MASK;
|
|
44539
|
+
const supportedModifierMask = MODIFIERS.shift | MODIFIERS.ctrl | MODIFIERS.alt | MODIFIERS.super;
|
|
44540
|
+
if ((effectiveMod & ~supportedModifierMask) !== 0)
|
|
44541
|
+
return void 0;
|
|
44542
|
+
if (effectiveMod & MODIFIERS.shift)
|
|
44543
|
+
mods.push("shift");
|
|
44544
|
+
if (effectiveMod & MODIFIERS.ctrl)
|
|
44545
|
+
mods.push("ctrl");
|
|
44546
|
+
if (effectiveMod & MODIFIERS.alt)
|
|
44547
|
+
mods.push("alt");
|
|
44548
|
+
if (effectiveMod & MODIFIERS.super)
|
|
44549
|
+
mods.push("super");
|
|
44550
|
+
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
44551
|
+
}
|
|
44475
44552
|
function parseKeyId(keyId) {
|
|
44476
44553
|
const parts = keyId.toLowerCase().split("+");
|
|
44477
44554
|
const key = parts[parts.length - 1];
|
|
@@ -44728,6 +44805,149 @@ function matchesKey(data, keyId) {
|
|
|
44728
44805
|
}
|
|
44729
44806
|
return false;
|
|
44730
44807
|
}
|
|
44808
|
+
function formatParsedKey(codepoint, modifier, baseLayoutKey) {
|
|
44809
|
+
const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
|
|
44810
|
+
const identityCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizedCodepoint, modifier);
|
|
44811
|
+
const isLatinLetter = identityCodepoint >= 97 && identityCodepoint <= 122;
|
|
44812
|
+
const isDigit = identityCodepoint >= 48 && identityCodepoint <= 57;
|
|
44813
|
+
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(identityCodepoint));
|
|
44814
|
+
const effectiveCodepoint = isLatinLetter || isDigit || isKnownSymbol ? identityCodepoint : baseLayoutKey ?? identityCodepoint;
|
|
44815
|
+
let keyName;
|
|
44816
|
+
if (effectiveCodepoint === CODEPOINTS.escape)
|
|
44817
|
+
keyName = "escape";
|
|
44818
|
+
else if (effectiveCodepoint === CODEPOINTS.tab)
|
|
44819
|
+
keyName = "tab";
|
|
44820
|
+
else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter)
|
|
44821
|
+
keyName = "enter";
|
|
44822
|
+
else if (effectiveCodepoint === CODEPOINTS.space)
|
|
44823
|
+
keyName = "space";
|
|
44824
|
+
else if (effectiveCodepoint === CODEPOINTS.backspace)
|
|
44825
|
+
keyName = "backspace";
|
|
44826
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete)
|
|
44827
|
+
keyName = "delete";
|
|
44828
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert)
|
|
44829
|
+
keyName = "insert";
|
|
44830
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home)
|
|
44831
|
+
keyName = "home";
|
|
44832
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end)
|
|
44833
|
+
keyName = "end";
|
|
44834
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp)
|
|
44835
|
+
keyName = "pageUp";
|
|
44836
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown)
|
|
44837
|
+
keyName = "pageDown";
|
|
44838
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.up)
|
|
44839
|
+
keyName = "up";
|
|
44840
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.down)
|
|
44841
|
+
keyName = "down";
|
|
44842
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.left)
|
|
44843
|
+
keyName = "left";
|
|
44844
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.right)
|
|
44845
|
+
keyName = "right";
|
|
44846
|
+
else if (effectiveCodepoint >= 48 && effectiveCodepoint <= 57)
|
|
44847
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
44848
|
+
else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122)
|
|
44849
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
44850
|
+
else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
|
|
44851
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
44852
|
+
if (!keyName)
|
|
44853
|
+
return void 0;
|
|
44854
|
+
return formatKeyNameWithModifiers(keyName, modifier);
|
|
44855
|
+
}
|
|
44856
|
+
function parseKey(data) {
|
|
44857
|
+
const kitty = parseKittySequence(data);
|
|
44858
|
+
if (kitty) {
|
|
44859
|
+
return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
|
|
44860
|
+
}
|
|
44861
|
+
const modifyOtherKeys = parseModifyOtherKeysSequence(data);
|
|
44862
|
+
if (modifyOtherKeys) {
|
|
44863
|
+
return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
|
|
44864
|
+
}
|
|
44865
|
+
if (_kittyProtocolActive) {
|
|
44866
|
+
if (data === "\x1B\r" || data === "\n")
|
|
44867
|
+
return "shift+enter";
|
|
44868
|
+
}
|
|
44869
|
+
const legacySequenceKeyId = LEGACY_SEQUENCE_KEY_IDS[data];
|
|
44870
|
+
if (legacySequenceKeyId)
|
|
44871
|
+
return legacySequenceKeyId;
|
|
44872
|
+
if (data === "\x1B")
|
|
44873
|
+
return "escape";
|
|
44874
|
+
if (data === "")
|
|
44875
|
+
return "ctrl+\\";
|
|
44876
|
+
if (data === "")
|
|
44877
|
+
return "ctrl+]";
|
|
44878
|
+
if (data === "")
|
|
44879
|
+
return "ctrl+-";
|
|
44880
|
+
if (data === "\x1B\x1B")
|
|
44881
|
+
return "ctrl+alt+[";
|
|
44882
|
+
if (data === "\x1B")
|
|
44883
|
+
return "ctrl+alt+\\";
|
|
44884
|
+
if (data === "\x1B")
|
|
44885
|
+
return "ctrl+alt+]";
|
|
44886
|
+
if (data === "\x1B")
|
|
44887
|
+
return "ctrl+alt+-";
|
|
44888
|
+
if (data === " ")
|
|
44889
|
+
return "tab";
|
|
44890
|
+
if (data === "\r" || !_kittyProtocolActive && data === "\n" || data === "\x1BOM")
|
|
44891
|
+
return "enter";
|
|
44892
|
+
if (data === "\0")
|
|
44893
|
+
return "ctrl+space";
|
|
44894
|
+
if (data === " ")
|
|
44895
|
+
return "space";
|
|
44896
|
+
if (data === "\x7F")
|
|
44897
|
+
return "backspace";
|
|
44898
|
+
if (data === "\b")
|
|
44899
|
+
return isWindowsTerminalSession() ? "ctrl+backspace" : "backspace";
|
|
44900
|
+
if (data === "\x1B[Z")
|
|
44901
|
+
return "shift+tab";
|
|
44902
|
+
if (!_kittyProtocolActive && data === "\x1B\r")
|
|
44903
|
+
return "alt+enter";
|
|
44904
|
+
if (!_kittyProtocolActive && data === "\x1B ")
|
|
44905
|
+
return "alt+space";
|
|
44906
|
+
if (data === "\x1B\x7F" || data === "\x1B\b")
|
|
44907
|
+
return "alt+backspace";
|
|
44908
|
+
if (!_kittyProtocolActive && data === "\x1BB")
|
|
44909
|
+
return "alt+left";
|
|
44910
|
+
if (!_kittyProtocolActive && data === "\x1BF")
|
|
44911
|
+
return "alt+right";
|
|
44912
|
+
if (!_kittyProtocolActive && data.length === 2 && data[0] === "\x1B") {
|
|
44913
|
+
const code = data.charCodeAt(1);
|
|
44914
|
+
if (code >= 1 && code <= 26) {
|
|
44915
|
+
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
44916
|
+
}
|
|
44917
|
+
const key = String.fromCharCode(code);
|
|
44918
|
+
if (code >= 97 && code <= 122 || code >= 48 && code <= 57 || SYMBOL_KEYS.has(key)) {
|
|
44919
|
+
return `alt+${key}`;
|
|
44920
|
+
}
|
|
44921
|
+
}
|
|
44922
|
+
if (data === "\x1B[A")
|
|
44923
|
+
return "up";
|
|
44924
|
+
if (data === "\x1B[B")
|
|
44925
|
+
return "down";
|
|
44926
|
+
if (data === "\x1B[C")
|
|
44927
|
+
return "right";
|
|
44928
|
+
if (data === "\x1B[D")
|
|
44929
|
+
return "left";
|
|
44930
|
+
if (data === "\x1B[H" || data === "\x1BOH")
|
|
44931
|
+
return "home";
|
|
44932
|
+
if (data === "\x1B[F" || data === "\x1BOF")
|
|
44933
|
+
return "end";
|
|
44934
|
+
if (data === "\x1B[3~")
|
|
44935
|
+
return "delete";
|
|
44936
|
+
if (data === "\x1B[5~")
|
|
44937
|
+
return "pageUp";
|
|
44938
|
+
if (data === "\x1B[6~")
|
|
44939
|
+
return "pageDown";
|
|
44940
|
+
if (data.length === 1) {
|
|
44941
|
+
const code = data.charCodeAt(0);
|
|
44942
|
+
if (code >= 1 && code <= 26) {
|
|
44943
|
+
return `ctrl+${String.fromCharCode(code + 96)}`;
|
|
44944
|
+
}
|
|
44945
|
+
if (code >= 32 && code <= 126) {
|
|
44946
|
+
return data;
|
|
44947
|
+
}
|
|
44948
|
+
}
|
|
44949
|
+
return void 0;
|
|
44950
|
+
}
|
|
44731
44951
|
var KITTY_CSI_U_REGEX = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/;
|
|
44732
44952
|
var KITTY_PRINTABLE_ALLOWED_MODIFIERS = MODIFIERS.shift | LOCK_MASK;
|
|
44733
44953
|
function decodeKittyPrintable(data) {
|
|
@@ -50259,6 +50479,13 @@ import { join as join46, resolve as resolve27, dirname as dirname20 } from "node
|
|
|
50259
50479
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
50260
50480
|
|
|
50261
50481
|
// packages/core/tui/src/types/surface.ts
|
|
50482
|
+
var KXM_TUI_FIELD_KINDS = ["text", "enum", "choice", "info"];
|
|
50483
|
+
var KXM_TUI_STATUSES = ["ready", "available", "blocked"];
|
|
50484
|
+
var KXM_TUI_STEP_STATES = ["pending", "satisfied", "running", "done", "failed"];
|
|
50485
|
+
var KXM_TUI_NOTICE_LEVELS = ["info", "error"];
|
|
50486
|
+
var KXM_TUI_SET_ACTION = "set";
|
|
50487
|
+
var KXM_TUI_CLEAR_ACTION = "clear";
|
|
50488
|
+
var KXM_TUI_ABORT_ACTION = "abort";
|
|
50262
50489
|
var KXM_TUI_LIMITS = Object.freeze({
|
|
50263
50490
|
sourceMax: 128,
|
|
50264
50491
|
idMax: 128,
|
|
@@ -50280,8 +50507,231 @@ var KXM_TUI_LIMITS = Object.freeze({
|
|
|
50280
50507
|
sectionsMax: 32,
|
|
50281
50508
|
orderMax: 1e3
|
|
50282
50509
|
});
|
|
50510
|
+
var ID_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/;
|
|
50511
|
+
var SOURCE_PATTERN = /^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/;
|
|
50512
|
+
var KEY_PATH_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.[\]-]*$/;
|
|
50513
|
+
var ACTION_KEY_PATTERN = /^[A-Za-z0-9]$/;
|
|
50514
|
+
function issue4(path4, code, message) {
|
|
50515
|
+
return { path: path4, code, message };
|
|
50516
|
+
}
|
|
50517
|
+
function isPlainObject(value) {
|
|
50518
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
50519
|
+
}
|
|
50520
|
+
function boundedText(issues, path4, value, max, required2) {
|
|
50521
|
+
if (value === void 0) {
|
|
50522
|
+
if (required2) issues.push(issue4(path4, "required", `${path4} is required`));
|
|
50523
|
+
return;
|
|
50524
|
+
}
|
|
50525
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
50526
|
+
issues.push(issue4(path4, "invalid_string", `${path4} must be a non-empty string`));
|
|
50527
|
+
return;
|
|
50528
|
+
}
|
|
50529
|
+
if (value.length > max) {
|
|
50530
|
+
issues.push(issue4(path4, "too_long", `${path4} exceeds ${max} characters`));
|
|
50531
|
+
return;
|
|
50532
|
+
}
|
|
50533
|
+
if (/[\u0000-\u0008\u000a-\u001f\u007f]/u.test(value)) {
|
|
50534
|
+
issues.push(issue4(path4, "control_character", `${path4} must not contain control characters`));
|
|
50535
|
+
}
|
|
50536
|
+
}
|
|
50537
|
+
function optionalEnum(issues, path4, value, allowed) {
|
|
50538
|
+
if (value === void 0) return;
|
|
50539
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
50540
|
+
issues.push(issue4(path4, "invalid_choice", `${path4} must be one of ${allowed.join(", ")}`));
|
|
50541
|
+
}
|
|
50542
|
+
}
|
|
50543
|
+
function ratioField(issues, path4, value) {
|
|
50544
|
+
if (value === void 0) return;
|
|
50545
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
|
|
50546
|
+
issues.push(issue4(path4, "invalid_ratio", `${path4} must be a finite number between 0 and 1`));
|
|
50547
|
+
}
|
|
50548
|
+
}
|
|
50549
|
+
function validateChoice(issues, path4, raw) {
|
|
50550
|
+
if (!isPlainObject(raw)) {
|
|
50551
|
+
issues.push(issue4(path4, "invalid_object", `${path4} must be an object`));
|
|
50552
|
+
return;
|
|
50553
|
+
}
|
|
50554
|
+
boundedText(issues, `${path4}.id`, raw.id, KXM_TUI_LIMITS.choiceIdMax, true);
|
|
50555
|
+
if (typeof raw.id === "string" && /[\s]/u.test(raw.id)) {
|
|
50556
|
+
issues.push(issue4(`${path4}.id`, "invalid_choice_id", `${path4}.id must not contain whitespace`));
|
|
50557
|
+
}
|
|
50558
|
+
boundedText(issues, `${path4}.label`, raw.label, KXM_TUI_LIMITS.choiceLabelMax, true);
|
|
50559
|
+
boundedText(issues, `${path4}.detail`, raw.detail, KXM_TUI_LIMITS.shortDetailMax, false);
|
|
50560
|
+
boundedText(issues, `${path4}.group`, raw.group, KXM_TUI_LIMITS.shortDetailMax, false);
|
|
50561
|
+
boundedText(issues, `${path4}.statusText`, raw.statusText, KXM_TUI_LIMITS.shortDetailMax, false);
|
|
50562
|
+
boundedText(issues, `${path4}.action`, raw.action, KXM_TUI_LIMITS.idMax, false);
|
|
50563
|
+
optionalEnum(issues, `${path4}.status`, raw.status, KXM_TUI_STATUSES);
|
|
50564
|
+
}
|
|
50565
|
+
function validateAction(issues, path4, raw) {
|
|
50566
|
+
if (!isPlainObject(raw)) {
|
|
50567
|
+
issues.push(issue4(path4, "invalid_object", `${path4} must be an object`));
|
|
50568
|
+
return;
|
|
50569
|
+
}
|
|
50570
|
+
if (typeof raw.key !== "string" || !ACTION_KEY_PATTERN.test(raw.key)) {
|
|
50571
|
+
issues.push(issue4(`${path4}.key`, "invalid_action_key", `${path4}.key must be a single alphanumeric key`));
|
|
50572
|
+
}
|
|
50573
|
+
boundedText(issues, `${path4}.label`, raw.label, KXM_TUI_LIMITS.shortDetailMax, true);
|
|
50574
|
+
boundedText(issues, `${path4}.action`, raw.action, KXM_TUI_LIMITS.idMax, true);
|
|
50575
|
+
}
|
|
50576
|
+
function validateStep(issues, path4, raw) {
|
|
50577
|
+
if (!isPlainObject(raw)) {
|
|
50578
|
+
issues.push(issue4(path4, "invalid_object", `${path4} must be an object`));
|
|
50579
|
+
return;
|
|
50580
|
+
}
|
|
50581
|
+
boundedText(issues, `${path4}.label`, raw.label, KXM_TUI_LIMITS.shortDetailMax, true);
|
|
50582
|
+
boundedText(issues, `${path4}.detail`, raw.detail, KXM_TUI_LIMITS.stepDetailMax, false);
|
|
50583
|
+
optionalEnum(issues, `${path4}.state`, raw.state, KXM_TUI_STEP_STATES);
|
|
50584
|
+
if (raw.state === void 0) issues.push(issue4(`${path4}.state`, "required", `${path4}.state is required`));
|
|
50585
|
+
}
|
|
50586
|
+
function validateField(issues, path4, raw) {
|
|
50587
|
+
if (!isPlainObject(raw)) {
|
|
50588
|
+
issues.push(issue4(path4, "invalid_object", `${path4} must be an object`));
|
|
50589
|
+
return;
|
|
50590
|
+
}
|
|
50591
|
+
boundedText(issues, `${path4}.id`, raw.id, KXM_TUI_LIMITS.idMax, true);
|
|
50592
|
+
if (typeof raw.id === "string" && !ID_PATTERN.test(raw.id)) {
|
|
50593
|
+
issues.push(issue4(`${path4}.id`, "invalid_id", `${path4}.id must match ${ID_PATTERN}`));
|
|
50594
|
+
}
|
|
50595
|
+
boundedText(issues, `${path4}.label`, raw.label, KXM_TUI_LIMITS.labelMax, true);
|
|
50596
|
+
if (raw.kind === void 0) {
|
|
50597
|
+
issues.push(issue4(`${path4}.kind`, "required", `${path4}.kind is required`));
|
|
50598
|
+
} else {
|
|
50599
|
+
optionalEnum(issues, `${path4}.kind`, raw.kind, KXM_TUI_FIELD_KINDS);
|
|
50600
|
+
}
|
|
50601
|
+
boundedText(issues, `${path4}.value`, raw.value, KXM_TUI_LIMITS.valueMax, false);
|
|
50602
|
+
boundedText(issues, `${path4}.placeholder`, raw.placeholder, KXM_TUI_LIMITS.shortDetailMax, false);
|
|
50603
|
+
boundedText(issues, `${path4}.detail`, raw.detail, KXM_TUI_LIMITS.detailMax, false);
|
|
50604
|
+
boundedText(issues, `${path4}.statusText`, raw.statusText, KXM_TUI_LIMITS.statusTextMax, false);
|
|
50605
|
+
if (raw.updatedAt !== void 0) {
|
|
50606
|
+
boundedText(issues, `${path4}.updatedAt`, raw.updatedAt, 64, false);
|
|
50607
|
+
if (typeof raw.updatedAt === "string" && !Number.isFinite(Date.parse(raw.updatedAt))) {
|
|
50608
|
+
issues.push(issue4(`${path4}.updatedAt`, "invalid_timestamp", `${path4}.updatedAt must be an ISO timestamp`));
|
|
50609
|
+
}
|
|
50610
|
+
}
|
|
50611
|
+
optionalEnum(issues, `${path4}.status`, raw.status, KXM_TUI_STATUSES);
|
|
50612
|
+
if (raw.keyPath !== void 0) {
|
|
50613
|
+
boundedText(issues, `${path4}.keyPath`, raw.keyPath, KXM_TUI_LIMITS.keyPathMax, false);
|
|
50614
|
+
if (typeof raw.keyPath === "string" && !KEY_PATH_PATTERN.test(raw.keyPath)) {
|
|
50615
|
+
issues.push(issue4(`${path4}.keyPath`, "invalid_key_path", `${path4}.keyPath must be a dotted config key`));
|
|
50616
|
+
}
|
|
50617
|
+
}
|
|
50618
|
+
for (const listKey of ["busy", "awaitingInput"]) {
|
|
50619
|
+
if (raw[listKey] !== void 0 && typeof raw[listKey] !== "boolean") {
|
|
50620
|
+
issues.push(issue4(`${path4}.${listKey}`, "invalid_boolean", `${path4}.${listKey} must be a boolean`));
|
|
50621
|
+
}
|
|
50622
|
+
}
|
|
50623
|
+
if (raw.progress !== void 0) {
|
|
50624
|
+
if (!isPlainObject(raw.progress)) {
|
|
50625
|
+
issues.push(issue4(`${path4}.progress`, "invalid_object", `${path4}.progress must be an object`));
|
|
50626
|
+
} else {
|
|
50627
|
+
boundedText(issues, `${path4}.progress.label`, raw.progress.label, KXM_TUI_LIMITS.shortDetailMax, true);
|
|
50628
|
+
ratioField(issues, `${path4}.progress.ratio`, raw.progress.ratio);
|
|
50629
|
+
}
|
|
50630
|
+
}
|
|
50631
|
+
for (const [listKey, max, check] of [
|
|
50632
|
+
["choices", KXM_TUI_LIMITS.choicesMax, validateChoice],
|
|
50633
|
+
["actions", KXM_TUI_LIMITS.actionsMax, validateAction],
|
|
50634
|
+
["steps", KXM_TUI_LIMITS.stepsMax, validateStep]
|
|
50635
|
+
]) {
|
|
50636
|
+
const list = raw[listKey];
|
|
50637
|
+
if (list === void 0) continue;
|
|
50638
|
+
if (!Array.isArray(list) || list.length > max) {
|
|
50639
|
+
issues.push(issue4(`${path4}.${listKey}`, "invalid_list", `${path4}.${listKey} must be an array of at most ${max}`));
|
|
50640
|
+
continue;
|
|
50641
|
+
}
|
|
50642
|
+
list.forEach((entry, index) => check(issues, `${path4}.${listKey}[${index}]`, entry));
|
|
50643
|
+
}
|
|
50644
|
+
const output = raw.output;
|
|
50645
|
+
if (output !== void 0) {
|
|
50646
|
+
if (!Array.isArray(output) || output.length > KXM_TUI_LIMITS.outputMaxLines) {
|
|
50647
|
+
issues.push(issue4(`${path4}.output`, "invalid_list", `${path4}.output must be an array of at most ${KXM_TUI_LIMITS.outputMaxLines} lines`));
|
|
50648
|
+
} else {
|
|
50649
|
+
output.forEach((line, index) => {
|
|
50650
|
+
if (typeof line !== "string") issues.push(issue4(`${path4}.output[${index}]`, "invalid_string", "output lines must be strings"));
|
|
50651
|
+
else if (line.length > KXM_TUI_LIMITS.outputLineMax) issues.push(issue4(`${path4}.output[${index}]`, "too_long", "output line is too long"));
|
|
50652
|
+
});
|
|
50653
|
+
}
|
|
50654
|
+
}
|
|
50655
|
+
}
|
|
50656
|
+
function validateSection(issues, path4, raw) {
|
|
50657
|
+
if (!isPlainObject(raw)) {
|
|
50658
|
+
issues.push(issue4(path4, "invalid_object", `${path4} must be an object`));
|
|
50659
|
+
return;
|
|
50660
|
+
}
|
|
50661
|
+
boundedText(issues, `${path4}.id`, raw.id, KXM_TUI_LIMITS.idMax, true);
|
|
50662
|
+
if (typeof raw.id === "string" && !ID_PATTERN.test(raw.id)) {
|
|
50663
|
+
issues.push(issue4(`${path4}.id`, "invalid_id", `${path4}.id must match ${ID_PATTERN}`));
|
|
50664
|
+
}
|
|
50665
|
+
boundedText(issues, `${path4}.title`, raw.title, KXM_TUI_LIMITS.labelMax, true);
|
|
50666
|
+
boundedText(issues, `${path4}.detail`, raw.detail, KXM_TUI_LIMITS.shortDetailMax, false);
|
|
50667
|
+
boundedText(issues, `${path4}.notice`, raw.notice, KXM_TUI_LIMITS.detailMax, false);
|
|
50668
|
+
optionalEnum(issues, `${path4}.noticeLevel`, raw.noticeLevel, KXM_TUI_NOTICE_LEVELS);
|
|
50669
|
+
if (typeof raw.order !== "number" || !Number.isInteger(raw.order) || raw.order < 0 || raw.order > KXM_TUI_LIMITS.orderMax) {
|
|
50670
|
+
issues.push(issue4(`${path4}.order`, "invalid_order", `${path4}.order must be an integer between 0 and ${KXM_TUI_LIMITS.orderMax}`));
|
|
50671
|
+
}
|
|
50672
|
+
if (!Array.isArray(raw.fields) || raw.fields.length > KXM_TUI_LIMITS.fieldsMax) {
|
|
50673
|
+
issues.push(issue4(`${path4}.fields`, "invalid_list", `${path4}.fields must be an array of at most ${KXM_TUI_LIMITS.fieldsMax}`));
|
|
50674
|
+
return;
|
|
50675
|
+
}
|
|
50676
|
+
const seen = /* @__PURE__ */ new Set();
|
|
50677
|
+
raw.fields.forEach((field, index) => {
|
|
50678
|
+
const fieldPath = `${path4}.fields[${index}]`;
|
|
50679
|
+
validateField(issues, fieldPath, field);
|
|
50680
|
+
if (isPlainObject(field) && typeof field.id === "string") {
|
|
50681
|
+
if (seen.has(field.id)) issues.push(issue4(`${fieldPath}.id`, "duplicate_id", `${field.id} is published twice in ${path4}`));
|
|
50682
|
+
seen.add(field.id);
|
|
50683
|
+
}
|
|
50684
|
+
});
|
|
50685
|
+
}
|
|
50686
|
+
function validateKxmTuiSurface(sections) {
|
|
50687
|
+
if (!Array.isArray(sections)) return [issue4("sections", "invalid_list", "sections must be an array")];
|
|
50688
|
+
if (sections.length > KXM_TUI_LIMITS.sectionsMax) {
|
|
50689
|
+
return [issue4("sections", "too_many", `sections must number at most ${KXM_TUI_LIMITS.sectionsMax}`)];
|
|
50690
|
+
}
|
|
50691
|
+
const issues = [];
|
|
50692
|
+
const seenSources = /* @__PURE__ */ new Map();
|
|
50693
|
+
sections.forEach((section, index) => {
|
|
50694
|
+
const path4 = `sections[${index}]`;
|
|
50695
|
+
validateSection(issues, path4, section);
|
|
50696
|
+
if (!isPlainObject(section)) return;
|
|
50697
|
+
boundedText(issues, `${path4}.source`, section.source, KXM_TUI_LIMITS.sourceMax, true);
|
|
50698
|
+
if (typeof section.source === "string" && !SOURCE_PATTERN.test(section.source)) {
|
|
50699
|
+
issues.push(issue4(`${path4}.source`, "invalid_source", `${path4}.source must be an addressable owner id`));
|
|
50700
|
+
}
|
|
50701
|
+
if (typeof section.source === "string" && typeof section.id === "string") {
|
|
50702
|
+
const ids = seenSources.get(section.source) ?? /* @__PURE__ */ new Set();
|
|
50703
|
+
seenSources.set(section.source, ids);
|
|
50704
|
+
if (ids.has(section.id)) issues.push(issue4(`${path4}.id`, "duplicate_section", `${section.source}/${section.id} is published twice`));
|
|
50705
|
+
ids.add(section.id);
|
|
50706
|
+
}
|
|
50707
|
+
});
|
|
50708
|
+
return issues;
|
|
50709
|
+
}
|
|
50710
|
+
function isSelectableField(field) {
|
|
50711
|
+
return field !== void 0 && field.kind !== "info";
|
|
50712
|
+
}
|
|
50713
|
+
function groupedKxmTuiChoices(field) {
|
|
50714
|
+
const choices = field.choices ?? [];
|
|
50715
|
+
if (choices.length === 0) return [];
|
|
50716
|
+
if (choices.every((choice) => choice.group === void 0)) return [{ group: "", choices }];
|
|
50717
|
+
const groups = [];
|
|
50718
|
+
const index = /* @__PURE__ */ new Map();
|
|
50719
|
+
for (const choice of choices) {
|
|
50720
|
+
const group = choice.group ?? "";
|
|
50721
|
+
let bucket = index.get(group);
|
|
50722
|
+
if (!bucket) {
|
|
50723
|
+
bucket = [];
|
|
50724
|
+
index.set(group, bucket);
|
|
50725
|
+
groups.push({ group, choices: bucket });
|
|
50726
|
+
}
|
|
50727
|
+
bucket.push(choice);
|
|
50728
|
+
}
|
|
50729
|
+
return groups;
|
|
50730
|
+
}
|
|
50283
50731
|
|
|
50284
50732
|
// packages/core/tui/src/tui/keys.ts
|
|
50733
|
+
var KXM_TUI_INPUT_MAX_LENGTH = 4096;
|
|
50734
|
+
var CONTROL_BYTES = /[\u0000-\u0008\u000a-\u001f\u007f\u0080-\u009f]/u;
|
|
50285
50735
|
var NAMED_KEYS = Object.freeze({
|
|
50286
50736
|
up: "up",
|
|
50287
50737
|
down: "down",
|
|
@@ -50299,8 +50749,109 @@ var NAMED_KEYS = Object.freeze({
|
|
|
50299
50749
|
backspace: "backspace",
|
|
50300
50750
|
delete: "delete"
|
|
50301
50751
|
});
|
|
50752
|
+
var CONTROL_BYTES_GLOBAL = /[\u0000-\u0008\u000a-\u001f\u007f\u0080-\u009f]/gu;
|
|
50753
|
+
function normalizeInputText(value) {
|
|
50754
|
+
return value.replace(CONTROL_BYTES_GLOBAL, " ").replace(/ {2,}/gu, " ").trim().slice(0, KXM_TUI_INPUT_MAX_LENGTH);
|
|
50755
|
+
}
|
|
50756
|
+
function decodeKxmTuiInput(data) {
|
|
50757
|
+
if (typeof data !== "string" || data.length === 0) return { kind: "ignored" };
|
|
50758
|
+
const named = parseKey(data);
|
|
50759
|
+
if (named === "ctrl+c") return { kind: "ctrlC" };
|
|
50760
|
+
if (named) {
|
|
50761
|
+
const kind = NAMED_KEYS[named.toLowerCase()];
|
|
50762
|
+
if (kind) return { kind };
|
|
50763
|
+
if (named === "space") return { kind: "text", text: " " };
|
|
50764
|
+
if (Array.from(named).length === 1 && !CONTROL_BYTES.test(named)) {
|
|
50765
|
+
return { kind: "text", text: named };
|
|
50766
|
+
}
|
|
50767
|
+
return { kind: "ignored" };
|
|
50768
|
+
}
|
|
50769
|
+
const kitty = decodeKittyPrintable(data);
|
|
50770
|
+
if (kitty) {
|
|
50771
|
+
const text = normalizeInputText(kitty);
|
|
50772
|
+
return text ? { kind: "text", text } : { kind: "ignored" };
|
|
50773
|
+
}
|
|
50774
|
+
if (data.startsWith("\x1B")) return { kind: "ignored" };
|
|
50775
|
+
const pasted = normalizeInputText(data);
|
|
50776
|
+
return pasted ? { kind: "text", text: pasted } : { kind: "ignored" };
|
|
50777
|
+
}
|
|
50778
|
+
function deleteBackward(text) {
|
|
50779
|
+
const units = Array.from(text);
|
|
50780
|
+
units.pop();
|
|
50781
|
+
return units.join("");
|
|
50782
|
+
}
|
|
50783
|
+
function insertAt(text, caret, insert, maxLength = KXM_TUI_INPUT_MAX_LENGTH) {
|
|
50784
|
+
const units = Array.from(text);
|
|
50785
|
+
const at2 = Math.max(0, Math.min(caret, units.length));
|
|
50786
|
+
const added = Array.from(insert);
|
|
50787
|
+
const merged = [...units.slice(0, at2), ...added, ...units.slice(at2)].join("");
|
|
50788
|
+
const next = merged.length > maxLength ? merged.slice(0, maxLength) : merged;
|
|
50789
|
+
return { text: next, caret: Math.min(next.length, at2 + added.length) };
|
|
50790
|
+
}
|
|
50302
50791
|
|
|
50303
50792
|
// packages/core/tui/src/tui/layout.ts
|
|
50793
|
+
var KXM_TUI_ELLIPSIS = "\u2026";
|
|
50794
|
+
function fitText(text, width) {
|
|
50795
|
+
if (width <= 0) return "";
|
|
50796
|
+
if (visibleWidth(text) <= width) return text;
|
|
50797
|
+
if (!text.includes("\x1B")) {
|
|
50798
|
+
let out = "";
|
|
50799
|
+
for (const unit of Array.from(text)) {
|
|
50800
|
+
if (visibleWidth(out) + visibleWidth(unit) > width - KXM_TUI_ELLIPSIS.length) break;
|
|
50801
|
+
out += unit;
|
|
50802
|
+
}
|
|
50803
|
+
return out + KXM_TUI_ELLIPSIS;
|
|
50804
|
+
}
|
|
50805
|
+
return truncateToWidth(text, width, KXM_TUI_ELLIPSIS);
|
|
50806
|
+
}
|
|
50807
|
+
function alignRight(left, right, width) {
|
|
50808
|
+
if (width <= 0) return "";
|
|
50809
|
+
const detail = fitText(right, Math.max(0, width - 2));
|
|
50810
|
+
const detailWidth = visibleWidth(detail);
|
|
50811
|
+
const leftWidth = Math.max(1, width - detailWidth - 1);
|
|
50812
|
+
const fitted = fitText(left, leftWidth);
|
|
50813
|
+
const filler = Math.max(1, width - visibleWidth(fitted) - detailWidth);
|
|
50814
|
+
return `${fitted}${" ".repeat(filler)}${detail}`;
|
|
50815
|
+
}
|
|
50816
|
+
function padRight(text, width) {
|
|
50817
|
+
const missing = width - visibleWidth(text);
|
|
50818
|
+
return missing > 0 ? text + " ".repeat(missing) : text;
|
|
50819
|
+
}
|
|
50820
|
+
function wrapPlain(text, width) {
|
|
50821
|
+
if (width <= 0) return [];
|
|
50822
|
+
const lines = [];
|
|
50823
|
+
let current = "";
|
|
50824
|
+
for (const word of text.split(/\s+/u).filter(Boolean)) {
|
|
50825
|
+
if (!current.length) current = word;
|
|
50826
|
+
else if (visibleWidth(current) + 1 + visibleWidth(word) <= width) current = `${current} ${word}`;
|
|
50827
|
+
else {
|
|
50828
|
+
lines.push(current);
|
|
50829
|
+
current = word;
|
|
50830
|
+
}
|
|
50831
|
+
}
|
|
50832
|
+
if (current.length) lines.push(current);
|
|
50833
|
+
return lines.length ? lines : [];
|
|
50834
|
+
}
|
|
50835
|
+
function formatAge(iso, nowMs) {
|
|
50836
|
+
if (!iso) return "\u2014";
|
|
50837
|
+
const at2 = Date.parse(iso);
|
|
50838
|
+
if (!Number.isFinite(at2)) return "\u2014";
|
|
50839
|
+
const seconds = Math.floor((nowMs - at2) / 1e3);
|
|
50840
|
+
if (seconds < 0) return "\u2014";
|
|
50841
|
+
if (seconds < 60) return `${seconds}s`;
|
|
50842
|
+
const minutes = Math.floor(seconds / 60);
|
|
50843
|
+
if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, "0")}s`;
|
|
50844
|
+
const hours = Math.floor(minutes / 60);
|
|
50845
|
+
if (hours < 24) return `${hours}h${String(minutes % 60).padStart(2, "0")}m`;
|
|
50846
|
+
const days = Math.floor(hours / 24);
|
|
50847
|
+
return `${days}d${String(hours % 24).padStart(2, "0")}h`;
|
|
50848
|
+
}
|
|
50849
|
+
function progressBar(ratio, width = 10) {
|
|
50850
|
+
const cells = Math.max(1, width);
|
|
50851
|
+
if (ratio === void 0 || !Number.isFinite(ratio)) return "\u2591".repeat(cells);
|
|
50852
|
+
const filled = Math.round(Math.max(0, Math.min(1, ratio)) * cells);
|
|
50853
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(Math.max(0, cells - filled));
|
|
50854
|
+
}
|
|
50304
50855
|
var KXM_TUI_MARKS = Object.freeze({
|
|
50305
50856
|
pending: "\u25CB",
|
|
50306
50857
|
active: "\u25B6",
|
|
@@ -50365,9 +50916,368 @@ function createKxmTuiAnsiTheme(color) {
|
|
|
50365
50916
|
panelBg: (text) => `\x1B[48;5;236m${text}\x1B[0m`
|
|
50366
50917
|
};
|
|
50367
50918
|
}
|
|
50919
|
+
function createDefaultKxmTuiTheme(env = process.env) {
|
|
50920
|
+
return createKxmTuiAnsiTheme(!(env.NO_COLOR || env.KXM_TUI_NO_COLOR));
|
|
50921
|
+
}
|
|
50922
|
+
|
|
50923
|
+
// packages/core/tui/src/tui/panel.ts
|
|
50924
|
+
var PANE_KEYS = /* @__PURE__ */ new Set(["j", "k", "h", "l"]);
|
|
50925
|
+
function initialKxmTuiPanelState(sections) {
|
|
50926
|
+
return {
|
|
50927
|
+
pane: "sections",
|
|
50928
|
+
mode: "browse",
|
|
50929
|
+
sectionIndex: 0,
|
|
50930
|
+
fieldIndex: firstSelectableFieldIndex(sections[0]?.fields ?? []),
|
|
50931
|
+
choiceIndex: 0,
|
|
50932
|
+
draft: "",
|
|
50933
|
+
caret: 0,
|
|
50934
|
+
filter: "",
|
|
50935
|
+
filtering: false,
|
|
50936
|
+
help: false,
|
|
50937
|
+
pending: []
|
|
50938
|
+
};
|
|
50939
|
+
}
|
|
50940
|
+
function pendingKey(invocation) {
|
|
50941
|
+
return `${invocation.source}\0${invocation.sectionId}\0${invocation.fieldId}`;
|
|
50942
|
+
}
|
|
50943
|
+
function firstSelectableFieldIndex(fields) {
|
|
50944
|
+
const index = fields.findIndex(isSelectableField);
|
|
50945
|
+
return index < 0 ? 0 : index;
|
|
50946
|
+
}
|
|
50947
|
+
function visibleChoices(field, filter) {
|
|
50948
|
+
const choices = field?.choices ?? [];
|
|
50949
|
+
const needle = filter.trim().toLowerCase();
|
|
50950
|
+
if (!needle) return [...choices];
|
|
50951
|
+
return choices.filter(
|
|
50952
|
+
(choice) => `${choice.group ?? ""} ${choice.label} ${choice.id} ${choice.detail ?? ""}`.toLowerCase().includes(needle)
|
|
50953
|
+
);
|
|
50954
|
+
}
|
|
50955
|
+
function currentSection(sections, state) {
|
|
50956
|
+
return sections[state.sectionIndex];
|
|
50957
|
+
}
|
|
50958
|
+
function currentField(sections, state) {
|
|
50959
|
+
return currentSection(sections, state)?.fields[state.fieldIndex];
|
|
50960
|
+
}
|
|
50961
|
+
function reconcileKxmTuiPanelState(state, sections) {
|
|
50962
|
+
const sectionIndex = Math.min(state.sectionIndex, Math.max(0, sections.length - 1));
|
|
50963
|
+
const fields = sections[sectionIndex]?.fields ?? [];
|
|
50964
|
+
let fieldIndex = Math.min(state.fieldIndex, Math.max(0, fields.length - 1));
|
|
50965
|
+
if (!isSelectableField(fields[fieldIndex])) fieldIndex = firstSelectableFieldIndex(fields);
|
|
50966
|
+
const field = fields[fieldIndex];
|
|
50967
|
+
const keepsEdit = state.mode === "edit" && (field?.statusText !== void 0 || field?.awaitingInput === true);
|
|
50968
|
+
return {
|
|
50969
|
+
...state,
|
|
50970
|
+
sectionIndex,
|
|
50971
|
+
fieldIndex,
|
|
50972
|
+
mode: keepsEdit ? "edit" : "browse",
|
|
50973
|
+
choiceIndex: 0,
|
|
50974
|
+
pending: []
|
|
50975
|
+
};
|
|
50976
|
+
}
|
|
50977
|
+
function reduceKxmTuiInput(state, sections, input) {
|
|
50978
|
+
return reduceDecoded(state, sections, decodeKxmTuiInput(input));
|
|
50979
|
+
}
|
|
50980
|
+
function invokeEffect(state, section, field, action, value) {
|
|
50981
|
+
const invocation = {
|
|
50982
|
+
source: section.source,
|
|
50983
|
+
sectionId: section.id,
|
|
50984
|
+
fieldId: field.id,
|
|
50985
|
+
action,
|
|
50986
|
+
...value === void 0 ? {} : { value }
|
|
50987
|
+
};
|
|
50988
|
+
const key = pendingKey(invocation);
|
|
50989
|
+
return {
|
|
50990
|
+
state: { ...state, pending: state.pending.includes(key) ? state.pending : [...state.pending, key] },
|
|
50991
|
+
effects: [{ type: "invoke", invocation }]
|
|
50992
|
+
};
|
|
50993
|
+
}
|
|
50994
|
+
function moveSection(state, sections, delta) {
|
|
50995
|
+
if (sections.length === 0) return state;
|
|
50996
|
+
const next = Math.max(0, Math.min(state.sectionIndex + delta, sections.length - 1));
|
|
50997
|
+
if (next === state.sectionIndex) return state;
|
|
50998
|
+
return {
|
|
50999
|
+
...state,
|
|
51000
|
+
sectionIndex: next,
|
|
51001
|
+
fieldIndex: firstSelectableFieldIndex(sections[next]?.fields ?? []),
|
|
51002
|
+
mode: "browse",
|
|
51003
|
+
choiceIndex: 0,
|
|
51004
|
+
draft: "",
|
|
51005
|
+
caret: 0,
|
|
51006
|
+
filter: "",
|
|
51007
|
+
filtering: false
|
|
51008
|
+
};
|
|
51009
|
+
}
|
|
51010
|
+
function moveField(state, fields, delta) {
|
|
51011
|
+
const step = delta < 0 ? -1 : 1;
|
|
51012
|
+
let next = state.fieldIndex + step;
|
|
51013
|
+
while (next >= 0 && next < fields.length && !isSelectableField(fields[next])) next += step;
|
|
51014
|
+
if (next < 0 || next >= fields.length) return state;
|
|
51015
|
+
return {
|
|
51016
|
+
...state,
|
|
51017
|
+
fieldIndex: next,
|
|
51018
|
+
mode: "browse",
|
|
51019
|
+
choiceIndex: 0,
|
|
51020
|
+
draft: "",
|
|
51021
|
+
caret: 0,
|
|
51022
|
+
filter: "",
|
|
51023
|
+
filtering: false
|
|
51024
|
+
};
|
|
51025
|
+
}
|
|
51026
|
+
function moveChoice(state, count, delta) {
|
|
51027
|
+
if (count === 0) return state;
|
|
51028
|
+
return { ...state, choiceIndex: Math.max(0, Math.min(state.choiceIndex + delta, count - 1)) };
|
|
51029
|
+
}
|
|
51030
|
+
function choiceAt(field, state) {
|
|
51031
|
+
const choices = visibleChoices(field, state.filter);
|
|
51032
|
+
return choices[Math.max(0, Math.min(state.choiceIndex, choices.length - 1))];
|
|
51033
|
+
}
|
|
51034
|
+
function enterField(state, section, field) {
|
|
51035
|
+
if (!field) return { state, effects: [] };
|
|
51036
|
+
if (field.kind === "choice") {
|
|
51037
|
+
if ((field.choices?.length ?? 0) === 0) {
|
|
51038
|
+
return { state, effects: [{ type: "notice", message: `${field.label} has nothing to offer yet` }] };
|
|
51039
|
+
}
|
|
51040
|
+
return { state: { ...state, mode: "choices", choiceIndex: 0 }, effects: [] };
|
|
51041
|
+
}
|
|
51042
|
+
if (field.kind === "enum") {
|
|
51043
|
+
const choices = field.choices ?? [];
|
|
51044
|
+
if (choices.length === 0) {
|
|
51045
|
+
return { state, effects: [{ type: "notice", message: `${field.label} has no values published` }] };
|
|
51046
|
+
}
|
|
51047
|
+
const current = choices.findIndex((choice) => choice.id === field.value);
|
|
51048
|
+
const next = choices[(current + 1) % choices.length];
|
|
51049
|
+
if (next.status === "blocked") {
|
|
51050
|
+
return { state, effects: [{ type: "notice", message: next.statusText ?? `${next.label} is unavailable` }] };
|
|
51051
|
+
}
|
|
51052
|
+
return invokeEffect(state, section, field, next.action ?? KXM_TUI_SET_ACTION, next.id);
|
|
51053
|
+
}
|
|
51054
|
+
if (field.kind !== "text") return { state, effects: [] };
|
|
51055
|
+
return { state: { ...state, mode: "edit", draft: field.value ?? "", caret: (field.value ?? "").length }, effects: [] };
|
|
51056
|
+
}
|
|
51057
|
+
function reduceDecoded(state, sections, decoded) {
|
|
51058
|
+
const section = sections[state.sectionIndex];
|
|
51059
|
+
const field = currentField(sections, state);
|
|
51060
|
+
if (decoded.kind === "ctrlC") {
|
|
51061
|
+
if (field?.busy && section) {
|
|
51062
|
+
return invokeEffect(state, section, field, KXM_TUI_ABORT_ACTION);
|
|
51063
|
+
}
|
|
51064
|
+
return { state, effects: [{ type: "quit" }] };
|
|
51065
|
+
}
|
|
51066
|
+
if (!section) return { state, effects: [{ type: "quit" }] };
|
|
51067
|
+
if (state.mode === "edit") return reduceEdit(state, section, field, decoded);
|
|
51068
|
+
if (state.mode === "choices") return reduceChoices(state, section, field, decoded);
|
|
51069
|
+
return reduceBrowse(state, sections, section, field, decoded);
|
|
51070
|
+
}
|
|
51071
|
+
function caretSource(text, caret) {
|
|
51072
|
+
return Array.from(text).slice(0, caret).join("").length;
|
|
51073
|
+
}
|
|
51074
|
+
function reduceEdit(state, section, field, decoded) {
|
|
51075
|
+
if (!field) return { state: { ...state, mode: "browse" }, effects: [] };
|
|
51076
|
+
switch (decoded.kind) {
|
|
51077
|
+
case "escape":
|
|
51078
|
+
return { state: { ...state, mode: "browse", draft: "", caret: 0 }, effects: [] };
|
|
51079
|
+
case "enter": {
|
|
51080
|
+
const value = normalizeInputText(state.draft).trim();
|
|
51081
|
+
if (!value) return { state: { ...state, mode: "browse", draft: "", caret: 0 }, effects: [] };
|
|
51082
|
+
const step = invokeEffect(state, section, field, KXM_TUI_SET_ACTION, value);
|
|
51083
|
+
return { state: { ...step.state, mode: "browse", draft: "", caret: 0 }, effects: step.effects };
|
|
51084
|
+
}
|
|
51085
|
+
case "backspace": {
|
|
51086
|
+
if (state.caret === 0) return { state, effects: [] };
|
|
51087
|
+
const cut = caretSource(state.draft, state.caret);
|
|
51088
|
+
const before = deleteBackward(state.draft.slice(0, cut));
|
|
51089
|
+
const draft = `${before}${state.draft.slice(cut)}`;
|
|
51090
|
+
return { state: { ...state, draft, caret: Array.from(before).length }, effects: [] };
|
|
51091
|
+
}
|
|
51092
|
+
case "delete": {
|
|
51093
|
+
const units = Array.from(state.draft);
|
|
51094
|
+
const text = [...units.slice(0, state.caret), ...units.slice(state.caret + 1)].join("");
|
|
51095
|
+
return { state: { ...state, draft: text }, effects: [] };
|
|
51096
|
+
}
|
|
51097
|
+
case "left":
|
|
51098
|
+
return { state: { ...state, caret: Math.max(0, state.caret - 1) }, effects: [] };
|
|
51099
|
+
case "right":
|
|
51100
|
+
return { state: { ...state, caret: Math.min(Array.from(state.draft).length, state.caret + 1) }, effects: [] };
|
|
51101
|
+
case "home":
|
|
51102
|
+
return { state: { ...state, caret: 0 }, effects: [] };
|
|
51103
|
+
case "end":
|
|
51104
|
+
return { state: { ...state, caret: Array.from(state.draft).length }, effects: [] };
|
|
51105
|
+
case "text": {
|
|
51106
|
+
const text = decoded.text ?? "";
|
|
51107
|
+
if (!text) return { state, effects: [] };
|
|
51108
|
+
const inserted = insertAt(state.draft, state.caret, text);
|
|
51109
|
+
return { state: { ...state, draft: inserted.text, caret: inserted.caret }, effects: [] };
|
|
51110
|
+
}
|
|
51111
|
+
default:
|
|
51112
|
+
return { state, effects: [] };
|
|
51113
|
+
}
|
|
51114
|
+
}
|
|
51115
|
+
function reduceChoices(state, section, field, decoded) {
|
|
51116
|
+
if (!field) return { state: { ...state, mode: "browse" }, effects: [] };
|
|
51117
|
+
const choices = visibleChoices(field, state.filter);
|
|
51118
|
+
switch (decoded.kind) {
|
|
51119
|
+
case "escape":
|
|
51120
|
+
return { state: { ...state, mode: "browse", filter: "", filtering: false, choiceIndex: 0 }, effects: [] };
|
|
51121
|
+
case "up":
|
|
51122
|
+
return { state: moveChoice(state, choices.length, -1), effects: [] };
|
|
51123
|
+
case "down":
|
|
51124
|
+
return { state: moveChoice(state, choices.length, 1), effects: [] };
|
|
51125
|
+
case "pageUp":
|
|
51126
|
+
return { state: moveChoice(state, choices.length, -8), effects: [] };
|
|
51127
|
+
case "pageDown":
|
|
51128
|
+
return { state: moveChoice(state, choices.length, 8), effects: [] };
|
|
51129
|
+
case "enter": {
|
|
51130
|
+
const choice = choiceAt(field, state);
|
|
51131
|
+
if (!choice) return { state, effects: [] };
|
|
51132
|
+
if (choice.status === "blocked") {
|
|
51133
|
+
return { state, effects: [{ type: "notice", message: choice.statusText ?? `${choice.label} is unavailable` }] };
|
|
51134
|
+
}
|
|
51135
|
+
const clearing = (choice.action ?? KXM_TUI_SET_ACTION) === KXM_TUI_CLEAR_ACTION;
|
|
51136
|
+
const step = invokeEffect(
|
|
51137
|
+
state,
|
|
51138
|
+
section,
|
|
51139
|
+
field,
|
|
51140
|
+
choice.action ?? KXM_TUI_SET_ACTION,
|
|
51141
|
+
clearing ? void 0 : choice.id
|
|
51142
|
+
);
|
|
51143
|
+
return { state: { ...step.state, mode: "browse", filter: "", filtering: false }, effects: step.effects };
|
|
51144
|
+
}
|
|
51145
|
+
case "backspace": {
|
|
51146
|
+
if (state.filter.length === 0) return { state: { ...state, filtering: false }, effects: [] };
|
|
51147
|
+
return { state: { ...state, filter: deleteBackward(state.filter), choiceIndex: 0 }, effects: [] };
|
|
51148
|
+
}
|
|
51149
|
+
case "text": {
|
|
51150
|
+
const text = normalizeInputText(decoded.text ?? "");
|
|
51151
|
+
if (!text) return { state, effects: [] };
|
|
51152
|
+
return { state: { ...state, filtering: true, filter: `${state.filter}${text}`, choiceIndex: 0 }, effects: [] };
|
|
51153
|
+
}
|
|
51154
|
+
default:
|
|
51155
|
+
return { state, effects: [] };
|
|
51156
|
+
}
|
|
51157
|
+
}
|
|
51158
|
+
function reduceBrowse(state, sections, section, field, decoded) {
|
|
51159
|
+
if (state.help && (decoded.kind === "escape" || decoded.kind === "enter")) {
|
|
51160
|
+
return { state: { ...state, help: false }, effects: [] };
|
|
51161
|
+
}
|
|
51162
|
+
if (state.help) return { state, effects: [] };
|
|
51163
|
+
if (decoded.kind === "text") {
|
|
51164
|
+
const key = decoded.text ?? "";
|
|
51165
|
+
if (key === "q") return { state, effects: [{ type: "quit" }] };
|
|
51166
|
+
if (key === "h" || key === "?") return { state: { ...state, help: true }, effects: [] };
|
|
51167
|
+
const action = field?.actions?.find((candidate) => candidate.key === key);
|
|
51168
|
+
if (action && field) return invokeEffect(state, section, field, action.action);
|
|
51169
|
+
if ((key === "x" || key === "d") && field && field.value !== void 0 && field.kind !== "info") {
|
|
51170
|
+
return invokeEffect(state, section, field, KXM_TUI_CLEAR_ACTION);
|
|
51171
|
+
}
|
|
51172
|
+
if (PANE_KEYS.has(key)) {
|
|
51173
|
+
if (key === "h") return { state: { ...state, pane: "sections" }, effects: [] };
|
|
51174
|
+
if (key === "l") return { state: { ...state, pane: "fields" }, effects: [] };
|
|
51175
|
+
const delta = key === "k" ? -1 : 1;
|
|
51176
|
+
return {
|
|
51177
|
+
state: state.pane === "fields" ? moveField(state, section.fields, delta) : moveSection(state, sections, delta),
|
|
51178
|
+
effects: []
|
|
51179
|
+
};
|
|
51180
|
+
}
|
|
51181
|
+
return { state, effects: [] };
|
|
51182
|
+
}
|
|
51183
|
+
switch (decoded.kind) {
|
|
51184
|
+
case "escape":
|
|
51185
|
+
return state.pane === "fields" ? { state: { ...state, pane: "sections", mode: "browse" }, effects: [] } : { state, effects: [{ type: "quit" }] };
|
|
51186
|
+
case "tab":
|
|
51187
|
+
return { state: { ...state, pane: state.pane === "fields" ? "sections" : "fields" }, effects: [] };
|
|
51188
|
+
case "right":
|
|
51189
|
+
return { state: { ...state, pane: "fields" }, effects: [] };
|
|
51190
|
+
case "left":
|
|
51191
|
+
return { state: { ...state, pane: "sections" }, effects: [] };
|
|
51192
|
+
case "up":
|
|
51193
|
+
return { state: state.pane === "fields" ? moveField(state, section.fields, -1) : moveSection(state, sections, -1), effects: [] };
|
|
51194
|
+
case "down":
|
|
51195
|
+
return { state: state.pane === "fields" ? moveField(state, section.fields, 1) : moveSection(state, sections, 1), effects: [] };
|
|
51196
|
+
case "pageUp":
|
|
51197
|
+
return { state: moveSection(state, sections, -1), effects: [] };
|
|
51198
|
+
case "pageDown":
|
|
51199
|
+
return { state: moveSection(state, sections, 1), effects: [] };
|
|
51200
|
+
case "enter":
|
|
51201
|
+
return state.pane === "fields" ? enterField(state, section, field) : { state: { ...state, pane: "fields" }, effects: [] };
|
|
51202
|
+
default:
|
|
51203
|
+
return { state, effects: [] };
|
|
51204
|
+
}
|
|
51205
|
+
}
|
|
50368
51206
|
|
|
50369
51207
|
// packages/core/tui/src/tui/render.ts
|
|
50370
51208
|
var SECTION_PANE_RATIO = 1 / 3;
|
|
51209
|
+
var MIN_PANE_WIDTH = 16;
|
|
51210
|
+
var PROGRESS_WIDTH = 20;
|
|
51211
|
+
var OUTPUT_TAIL = 6;
|
|
51212
|
+
var SPLIT_MIN_WIDTH = 76;
|
|
51213
|
+
function statusMark(status, theme, fallback) {
|
|
51214
|
+
switch (status) {
|
|
51215
|
+
case "ready":
|
|
51216
|
+
return theme.fg("success", KXM_TUI_MARKS.ready);
|
|
51217
|
+
case "available":
|
|
51218
|
+
return theme.fg("accent", KXM_TUI_MARKS.available);
|
|
51219
|
+
case "blocked":
|
|
51220
|
+
return theme.fg("error", KXM_TUI_MARKS.blocked);
|
|
51221
|
+
default:
|
|
51222
|
+
return theme.dim(fallback);
|
|
51223
|
+
}
|
|
51224
|
+
}
|
|
51225
|
+
function cursorMark(active, theme) {
|
|
51226
|
+
return active ? theme.cursor(KXM_TUI_MARKS.cursor) : KXM_TUI_MARKS.blankCursor;
|
|
51227
|
+
}
|
|
51228
|
+
function paneSplit(width) {
|
|
51229
|
+
const third = Math.max(MIN_PANE_WIDTH, Math.floor(width * SECTION_PANE_RATIO));
|
|
51230
|
+
const sections = Math.min(third, Math.max(MIN_PANE_WIDTH, Math.floor(width / 2)));
|
|
51231
|
+
return { sections, fields: Math.max(MIN_PANE_WIDTH, width - sections - 1) };
|
|
51232
|
+
}
|
|
51233
|
+
function fieldIsPending(state, section, field) {
|
|
51234
|
+
return state.pending.includes(pendingKey({ source: section.source, sectionId: section.id, fieldId: field.id }));
|
|
51235
|
+
}
|
|
51236
|
+
function sectionRow(section, focused, pane, theme, width) {
|
|
51237
|
+
const cursor2 = cursorMark(focused && pane === "sections", theme);
|
|
51238
|
+
const flag2 = section.notice ? theme.fg(section.noticeLevel === "error" ? "error" : "warning", "!") : " ";
|
|
51239
|
+
const count = section.fields.length;
|
|
51240
|
+
const unset = section.fields.filter((field) => field.kind !== "info" && field.value === void 0).length;
|
|
51241
|
+
const right = unset > 0 ? `${unset} unset / ${count}` : `${count} field${count === 1 ? "" : "s"}`;
|
|
51242
|
+
return fitText(alignRight(`${cursor2} ${flag2} ${section.title}`, right, width), width);
|
|
51243
|
+
}
|
|
51244
|
+
function choiceSuffix(choice) {
|
|
51245
|
+
const parts = [];
|
|
51246
|
+
if (choice.detail) parts.push(choice.detail);
|
|
51247
|
+
if (choice.statusText) parts.push(choice.statusText);
|
|
51248
|
+
return parts.join(" \xB7 ");
|
|
51249
|
+
}
|
|
51250
|
+
function choiceRows(field, state, theme, width) {
|
|
51251
|
+
const shown = visibleChoices(field, state.filter);
|
|
51252
|
+
const lines = [];
|
|
51253
|
+
if (state.filtering) {
|
|
51254
|
+
lines.push(fitText(` ${theme.dim(`filter: ${state.filter || "(type to filter \xB7 esc clears)"}`)}`, width));
|
|
51255
|
+
}
|
|
51256
|
+
if (shown.length === 0) {
|
|
51257
|
+
lines.push(fitText(` ${theme.dim("no match")}`, width));
|
|
51258
|
+
return lines;
|
|
51259
|
+
}
|
|
51260
|
+
let position = -1;
|
|
51261
|
+
for (const group of groupedKxmTuiChoices(field)) {
|
|
51262
|
+
const filtered = shown.filter((choice) => (choice.group ?? "") === group.group);
|
|
51263
|
+
if (filtered.length === 0) continue;
|
|
51264
|
+
if (group.group) lines.push(fitText(` ${theme.key(group.group.toUpperCase())}`, width));
|
|
51265
|
+
for (const choice of filtered) {
|
|
51266
|
+
position += 1;
|
|
51267
|
+
lines.push(
|
|
51268
|
+
fitText(
|
|
51269
|
+
alignRight(
|
|
51270
|
+
`${cursorMark(position === state.choiceIndex, theme)} ${statusMark(choice.status, theme, KXM_TUI_MARKS.pending)} ${choice.label}`,
|
|
51271
|
+
choiceSuffix(choice),
|
|
51272
|
+
width
|
|
51273
|
+
),
|
|
51274
|
+
width
|
|
51275
|
+
)
|
|
51276
|
+
);
|
|
51277
|
+
}
|
|
51278
|
+
}
|
|
51279
|
+
return lines;
|
|
51280
|
+
}
|
|
50371
51281
|
var STEP_MARKS = {
|
|
50372
51282
|
pending: "\xB7",
|
|
50373
51283
|
satisfied: KXM_TUI_MARKS.passed,
|
|
@@ -50375,12 +51285,494 @@ var STEP_MARKS = {
|
|
|
50375
51285
|
done: KXM_TUI_MARKS.passed,
|
|
50376
51286
|
failed: KXM_TUI_MARKS.failed
|
|
50377
51287
|
};
|
|
51288
|
+
function stepColor(state, theme) {
|
|
51289
|
+
switch (state) {
|
|
51290
|
+
case "running":
|
|
51291
|
+
return (text) => theme.fg("accent", text);
|
|
51292
|
+
case "done":
|
|
51293
|
+
return (text) => theme.fg("success", text);
|
|
51294
|
+
case "failed":
|
|
51295
|
+
return (text) => theme.fg("error", text);
|
|
51296
|
+
default:
|
|
51297
|
+
return theme.dim;
|
|
51298
|
+
}
|
|
51299
|
+
}
|
|
51300
|
+
function stepRows(steps, theme, width) {
|
|
51301
|
+
return steps.map((step) => {
|
|
51302
|
+
const paint = stepColor(step.state, theme);
|
|
51303
|
+
return fitText(alignRight(` ${paint(STEP_MARKS[step.state])} ${step.label}`, step.detail ?? "", width), width);
|
|
51304
|
+
});
|
|
51305
|
+
}
|
|
51306
|
+
function progressRow(field, theme, width) {
|
|
51307
|
+
const progress = field.progress;
|
|
51308
|
+
if (!progress) return "";
|
|
51309
|
+
const ratio = progress.ratio;
|
|
51310
|
+
const right = ratio === void 0 ? "working" : `${Math.round(ratio * 100)}%`;
|
|
51311
|
+
return fitText(alignRight(` ${progress.label}`, `${progressBar(ratio, PROGRESS_WIDTH)} ${right}`, width), width);
|
|
51312
|
+
}
|
|
51313
|
+
function focusedFieldDetail(field, state, theme, width) {
|
|
51314
|
+
const lines = [];
|
|
51315
|
+
if (field.keyPath) lines.push(fitText(` ${theme.dim(`key: ${field.keyPath}`)}`, width));
|
|
51316
|
+
for (const line of wrapPlain(field.detail ?? "", width - 3)) {
|
|
51317
|
+
if (line) lines.push(fitText(` ${theme.dim(line)}`, width));
|
|
51318
|
+
}
|
|
51319
|
+
for (const line of wrapPlain(field.statusText ?? "", width - 3)) {
|
|
51320
|
+
if (line) lines.push(fitText(` ${theme.fg("error", line)}`, width));
|
|
51321
|
+
}
|
|
51322
|
+
const progress = progressRow(field, theme, width);
|
|
51323
|
+
if (progress) lines.push(progress);
|
|
51324
|
+
if (field.steps?.length) lines.push(...stepRows(field.steps, theme, width));
|
|
51325
|
+
if (field.output?.length) {
|
|
51326
|
+
lines.push(...field.output.slice(-OUTPUT_TAIL).map((line) => fitText(` ${theme.dim(line)}`, width)));
|
|
51327
|
+
}
|
|
51328
|
+
if (field.actions?.length) {
|
|
51329
|
+
lines.push(fitText(` ${theme.key(field.actions.map((action) => `${action.key} ${action.label}`).join(" \xB7 "))}`, width));
|
|
51330
|
+
}
|
|
51331
|
+
if (state.mode === "choices") lines.push(...choiceRows(field, state, theme, width));
|
|
51332
|
+
if (state.mode === "edit" || field.awaitingInput) {
|
|
51333
|
+
const draft = state.mode === "edit" ? `${state.draft.slice(0, state.caret)}|${state.draft.slice(state.caret)}` : "|";
|
|
51334
|
+
lines.push(fitText(` ${theme.dim("\u203A")} ${draft}`, width));
|
|
51335
|
+
}
|
|
51336
|
+
return lines;
|
|
51337
|
+
}
|
|
51338
|
+
function fieldRows(section, state, theme, width, nowMs) {
|
|
51339
|
+
const lines = [];
|
|
51340
|
+
section.fields.forEach((field, index) => {
|
|
51341
|
+
const focused = state.pane === "fields" && state.fieldIndex === index;
|
|
51342
|
+
if (field.kind === "info") {
|
|
51343
|
+
lines.push(fitText(` ${theme.dim(field.label)} ${theme.dim(field.value ?? field.placeholder ?? "")}`, width));
|
|
51344
|
+
return;
|
|
51345
|
+
}
|
|
51346
|
+
const pending = fieldIsPending(state, section, field);
|
|
51347
|
+
const mark = statusMark(field.status, theme, pending ? "\u25CC" : KXM_TUI_MARKS.pending);
|
|
51348
|
+
const value = pending && field.value === void 0 ? theme.dim("saving\u2026") : field.value;
|
|
51349
|
+
const shown = value ?? theme.dim(field.placeholder ?? "not set");
|
|
51350
|
+
const age2 = field.updatedAt ? theme.dim(` (${formatAge(field.updatedAt, nowMs)})`) : "";
|
|
51351
|
+
lines.push(fitText(`${cursorMark(focused, theme)} ${mark} ${alignRight(field.label, `${shown}${age2}`, width - 4)}`, width));
|
|
51352
|
+
if (focused) lines.push(...focusedFieldDetail(field, state, theme, width));
|
|
51353
|
+
});
|
|
51354
|
+
return lines;
|
|
51355
|
+
}
|
|
51356
|
+
function noticeRows(section, theme, width) {
|
|
51357
|
+
if (!section?.notice) return [];
|
|
51358
|
+
const token = section.noticeLevel === "error" ? "error" : "warning";
|
|
51359
|
+
return wrapPlain(section.notice, width - 2).map((line) => fitText(theme.fg(token, line), width));
|
|
51360
|
+
}
|
|
51361
|
+
function singlePaneLines(sections, state, theme, width, nowMs) {
|
|
51362
|
+
const section = sections[state.sectionIndex];
|
|
51363
|
+
if (state.pane === "fields" && section) {
|
|
51364
|
+
return [
|
|
51365
|
+
fitText(theme.headerBg(alignRight(` ${section.title}`, section.source, width)), width),
|
|
51366
|
+
...noticeRows(section, theme, width),
|
|
51367
|
+
...fieldRows(section, state, theme, width, nowMs)
|
|
51368
|
+
];
|
|
51369
|
+
}
|
|
51370
|
+
return [
|
|
51371
|
+
fitText(theme.headerBg(alignRight(" SECTIONS", `${sections.length}`, width)), width),
|
|
51372
|
+
...sections.map((candidate, index) => sectionRow(candidate, index === state.sectionIndex, state.pane, theme, width))
|
|
51373
|
+
];
|
|
51374
|
+
}
|
|
51375
|
+
function twoPaneLines(sections, state, theme, width, nowMs) {
|
|
51376
|
+
const split = paneSplit(width);
|
|
51377
|
+
const left = [fitText(theme.key(" SECTIONS"), split.sections)];
|
|
51378
|
+
sections.forEach(
|
|
51379
|
+
(candidate, index) => left.push(sectionRow(candidate, index === state.sectionIndex, state.pane, theme, split.sections))
|
|
51380
|
+
);
|
|
51381
|
+
const right = [];
|
|
51382
|
+
const section = sections[state.sectionIndex];
|
|
51383
|
+
if (section) {
|
|
51384
|
+
right.push(fitText(alignRight(` ${section.title}`, section.source, split.fields), split.fields));
|
|
51385
|
+
if (section.detail) right.push(fitText(` ${theme.dim(section.detail)}`, split.fields));
|
|
51386
|
+
right.push(...noticeRows(section, theme, split.fields));
|
|
51387
|
+
right.push(...fieldRows(section, state, theme, split.fields, nowMs));
|
|
51388
|
+
} else {
|
|
51389
|
+
right.push(fitText(theme.dim("no surface is published"), split.fields));
|
|
51390
|
+
}
|
|
51391
|
+
const rows = [];
|
|
51392
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
51393
|
+
rows.push(fitText(`${padRight(left[index] ?? "", split.sections)} ${right[index] ?? ""}`, width));
|
|
51394
|
+
}
|
|
51395
|
+
return rows;
|
|
51396
|
+
}
|
|
51397
|
+
function renderKxmTuiPanel(sections, state, theme, options, width = 120) {
|
|
51398
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
51399
|
+
const title = `${theme.cursor(options.title)}${options.breadcrumb ? ` ${theme.dim(options.breadcrumb)}` : ""}`;
|
|
51400
|
+
const header = [fitText(title, width)];
|
|
51401
|
+
if (state.help) {
|
|
51402
|
+
const help = (options.helpLines ?? ["no help published"]).flatMap((line) => wrapPlain(line, Math.max(1, width - 2)));
|
|
51403
|
+
return [...header, ...help.map((line) => fitText(line, width)), fitText(theme.dim("esc close help"), width)];
|
|
51404
|
+
}
|
|
51405
|
+
const body = width < SPLIT_MIN_WIDTH ? singlePaneLines(sections, state, theme, width, nowMs) : twoPaneLines(sections, state, theme, width, nowMs);
|
|
51406
|
+
const footer = options.footer ? [fitText(theme.dim(options.footer), width)] : [];
|
|
51407
|
+
return [...header, ...body, ...footer];
|
|
51408
|
+
}
|
|
50378
51409
|
|
|
50379
51410
|
// packages/core/tui/src/tui/panelComponent.ts
|
|
50380
51411
|
var KXM_TUI_PANEL_HELP = Object.freeze([
|
|
50381
51412
|
"\u2191\u2193 move \xB7 \u2190\u2192 or Tab panes \xB7 PgUp/PgDn section \xB7 enter edit or open list",
|
|
50382
51413
|
"type to filter a list \xB7 x clears a value \xB7 esc backs out \xB7 q quit \xB7 h help"
|
|
50383
51414
|
]);
|
|
51415
|
+
function rowsComponent() {
|
|
51416
|
+
const stack = new VStack([], { gap: 0 });
|
|
51417
|
+
const scroll = new ScrollView(stack, { primary: true, overscroll: "contain", scrollbar: "auto" });
|
|
51418
|
+
const fill = (rows) => {
|
|
51419
|
+
stack.clear();
|
|
51420
|
+
for (const row of rows) stack.addChild(new TruncatedText(row, 0, 0));
|
|
51421
|
+
};
|
|
51422
|
+
return { stack, scroll, fill };
|
|
51423
|
+
}
|
|
51424
|
+
var KxmTuiPanelComponent = class {
|
|
51425
|
+
root = new VStack([], { gap: 0 });
|
|
51426
|
+
options;
|
|
51427
|
+
body = rowsComponent();
|
|
51428
|
+
sections;
|
|
51429
|
+
state;
|
|
51430
|
+
notice;
|
|
51431
|
+
unsubscribe;
|
|
51432
|
+
constructor(options) {
|
|
51433
|
+
this.options = options;
|
|
51434
|
+
this.sections = options.registry.getSections();
|
|
51435
|
+
this.state = initialKxmTuiPanelState(this.sections);
|
|
51436
|
+
this.unsubscribe = options.registry.subscribe(() => this.refresh());
|
|
51437
|
+
this.rebuild();
|
|
51438
|
+
}
|
|
51439
|
+
getState() {
|
|
51440
|
+
return this.state;
|
|
51441
|
+
}
|
|
51442
|
+
getSections() {
|
|
51443
|
+
return this.sections;
|
|
51444
|
+
}
|
|
51445
|
+
/** Point the cursor at a field, e.g. after opening a named section. */
|
|
51446
|
+
focus(sectionId, fieldId) {
|
|
51447
|
+
const sectionIndex = this.sections.findIndex((section) => section.id === sectionId);
|
|
51448
|
+
if (sectionIndex < 0) return;
|
|
51449
|
+
const fields = this.sections[sectionIndex]?.fields ?? [];
|
|
51450
|
+
const fieldIndex = fieldId ? Math.max(0, fields.findIndex((field) => field.id === fieldId)) : this.state.fieldIndex;
|
|
51451
|
+
this.state = { ...this.state, pane: "fields", sectionIndex, fieldIndex };
|
|
51452
|
+
this.rebuild();
|
|
51453
|
+
this.options.requestRender();
|
|
51454
|
+
}
|
|
51455
|
+
setStatus(message) {
|
|
51456
|
+
this.notice = message;
|
|
51457
|
+
this.rebuild();
|
|
51458
|
+
this.options.requestRender();
|
|
51459
|
+
}
|
|
51460
|
+
refresh() {
|
|
51461
|
+
const next = this.options.registry.getSections();
|
|
51462
|
+
this.sections = next;
|
|
51463
|
+
this.state = reconcileKxmTuiPanelState(this.state, next);
|
|
51464
|
+
this.rebuild();
|
|
51465
|
+
this.options.requestRender();
|
|
51466
|
+
}
|
|
51467
|
+
handleInput(data) {
|
|
51468
|
+
if (matchesKey(data, Key.pageUp) || matchesKey(data, Key.ctrl("u"))) {
|
|
51469
|
+
this.body.scroll.scrollBy(-(this.viewport() - 2));
|
|
51470
|
+
this.options.requestRender();
|
|
51471
|
+
return;
|
|
51472
|
+
}
|
|
51473
|
+
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("d"))) {
|
|
51474
|
+
this.body.scroll.scrollBy(this.viewport() - 2);
|
|
51475
|
+
this.options.requestRender();
|
|
51476
|
+
return;
|
|
51477
|
+
}
|
|
51478
|
+
const step = reduceKxmTuiInput(this.state, this.sections, data);
|
|
51479
|
+
this.state = step.state;
|
|
51480
|
+
for (const effect of step.effects) this.run(effect);
|
|
51481
|
+
this.rebuild();
|
|
51482
|
+
this.options.requestRender();
|
|
51483
|
+
}
|
|
51484
|
+
viewport() {
|
|
51485
|
+
const height = this.body.scroll.viewportHeight;
|
|
51486
|
+
return height > 3 ? height : 10;
|
|
51487
|
+
}
|
|
51488
|
+
run(effect) {
|
|
51489
|
+
if (effect.type === "quit") {
|
|
51490
|
+
this.options.onQuit();
|
|
51491
|
+
return;
|
|
51492
|
+
}
|
|
51493
|
+
if (effect.type === "notice") {
|
|
51494
|
+
this.notice = effect.message;
|
|
51495
|
+
return;
|
|
51496
|
+
}
|
|
51497
|
+
this.notice = void 0;
|
|
51498
|
+
void this.options.registry.invoke(effect.invocation).then((result) => {
|
|
51499
|
+
if (!result.ok) this.setStatus(result.message ?? "the owner refused that change");
|
|
51500
|
+
else if (this.notice === void 0) this.setStatus(void 0);
|
|
51501
|
+
});
|
|
51502
|
+
}
|
|
51503
|
+
footer() {
|
|
51504
|
+
const status = this.notice ?? this.options.statusText?.();
|
|
51505
|
+
const base = "\u2191\u2193 \xB7 \u2190\u2192 panes \xB7 enter edit \xB7 x clear \xB7 esc back \xB7 h help \xB7 q quit";
|
|
51506
|
+
return status ? `${base} ${status}` : base;
|
|
51507
|
+
}
|
|
51508
|
+
rebuild() {
|
|
51509
|
+
const theme = this.options.theme;
|
|
51510
|
+
const width = this.options.getWidth?.() ?? 120;
|
|
51511
|
+
const title = new Box(1, 0, theme.headerBg);
|
|
51512
|
+
title.addChild(new HStack([
|
|
51513
|
+
{ component: new Text(theme.cursor(this.options.title), 0, 0), basis: 12, shrink: 1, minSize: 6 },
|
|
51514
|
+
{
|
|
51515
|
+
component: new Text(
|
|
51516
|
+
this.options.breadcrumb ? theme.dim(fitText(this.options.breadcrumb, Math.max(4, width - 16))) : "",
|
|
51517
|
+
0,
|
|
51518
|
+
0
|
|
51519
|
+
),
|
|
51520
|
+
grow: 1,
|
|
51521
|
+
shrink: 1,
|
|
51522
|
+
minSize: 8
|
|
51523
|
+
}
|
|
51524
|
+
], { gap: 2 }));
|
|
51525
|
+
this.body.fill(
|
|
51526
|
+
renderKxmTuiPanel(this.sections, this.state, theme, {
|
|
51527
|
+
title: this.options.title,
|
|
51528
|
+
...this.options.breadcrumb ? { breadcrumb: this.options.breadcrumb } : {},
|
|
51529
|
+
helpLines: this.options.helpLines ?? KXM_TUI_PANEL_HELP,
|
|
51530
|
+
footer: this.footer()
|
|
51531
|
+
}, width)
|
|
51532
|
+
);
|
|
51533
|
+
const panel = new Box(0, 0, theme.panelBg);
|
|
51534
|
+
panel.addChild(this.body.scroll);
|
|
51535
|
+
this.root.clear();
|
|
51536
|
+
this.root.addChild(title, { basis: "auto", shrink: 0 });
|
|
51537
|
+
this.root.addChild(panel, { basis: "auto", grow: 1, shrink: 1, minSize: 1 });
|
|
51538
|
+
}
|
|
51539
|
+
handleMouse(event) {
|
|
51540
|
+
return this.root.handleMouse?.(event);
|
|
51541
|
+
}
|
|
51542
|
+
invalidate() {
|
|
51543
|
+
this.root.invalidate();
|
|
51544
|
+
}
|
|
51545
|
+
render(width) {
|
|
51546
|
+
return this.root.render(width);
|
|
51547
|
+
}
|
|
51548
|
+
dispose() {
|
|
51549
|
+
this.unsubscribe();
|
|
51550
|
+
this.unsubscribe = () => void 0;
|
|
51551
|
+
}
|
|
51552
|
+
};
|
|
51553
|
+
|
|
51554
|
+
// packages/core/tui/src/services/registry.ts
|
|
51555
|
+
function describe(error) {
|
|
51556
|
+
if (error instanceof Error && error.message) return error.message.split(/\r?\n/u)[0].slice(0, KXM_TUI_LIMITS.detailMax);
|
|
51557
|
+
return String(error).split(/\r?\n/u)[0].slice(0, KXM_TUI_LIMITS.detailMax);
|
|
51558
|
+
}
|
|
51559
|
+
function normalizeSource(source) {
|
|
51560
|
+
if (!/^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/u.test(source) || source.length > KXM_TUI_LIMITS.sourceMax) {
|
|
51561
|
+
throw new Error(`invalid kxm tui surface source: ${source.slice(0, 64)}`);
|
|
51562
|
+
}
|
|
51563
|
+
return source;
|
|
51564
|
+
}
|
|
51565
|
+
function createKxmTuiRegistry(options = {}) {
|
|
51566
|
+
const contributions = /* @__PURE__ */ new Map();
|
|
51567
|
+
const notices = /* @__PURE__ */ new Map();
|
|
51568
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
51569
|
+
const published = /* @__PURE__ */ new Map();
|
|
51570
|
+
const issues = /* @__PURE__ */ new Map();
|
|
51571
|
+
let generation = 0;
|
|
51572
|
+
let disposed = false;
|
|
51573
|
+
let epoch = 0;
|
|
51574
|
+
const report2 = (error, context) => {
|
|
51575
|
+
if (options.onInternalError) options.onInternalError(error, context);
|
|
51576
|
+
};
|
|
51577
|
+
const emit = (event) => {
|
|
51578
|
+
for (const listener of Array.from(listeners)) {
|
|
51579
|
+
try {
|
|
51580
|
+
listener(event);
|
|
51581
|
+
} catch (error) {
|
|
51582
|
+
report2(error, "listener");
|
|
51583
|
+
}
|
|
51584
|
+
}
|
|
51585
|
+
};
|
|
51586
|
+
function readOwner(source, force) {
|
|
51587
|
+
if (!force) {
|
|
51588
|
+
const cached = published.get(source);
|
|
51589
|
+
if (cached) return cached;
|
|
51590
|
+
}
|
|
51591
|
+
const contribution = contributions.get(source);
|
|
51592
|
+
if (!contribution) {
|
|
51593
|
+
published.set(source, []);
|
|
51594
|
+
return [];
|
|
51595
|
+
}
|
|
51596
|
+
let sections;
|
|
51597
|
+
try {
|
|
51598
|
+
const raw = contribution.listSections();
|
|
51599
|
+
const ownerIssues = validateKxmTuiSurface(raw);
|
|
51600
|
+
if (ownerIssues.length > 0) {
|
|
51601
|
+
issues.set(source, ownerIssues);
|
|
51602
|
+
sections = raw.filter((section) => Boolean(section) && typeof section === "object").map((section) => ({
|
|
51603
|
+
...section,
|
|
51604
|
+
notice: section.notice ?? `published surface was refused: ${ownerIssues[0].code} at ${ownerIssues[0].path}`,
|
|
51605
|
+
noticeLevel: "error"
|
|
51606
|
+
}));
|
|
51607
|
+
} else {
|
|
51608
|
+
issues.delete(source);
|
|
51609
|
+
sections = raw;
|
|
51610
|
+
}
|
|
51611
|
+
} catch (error) {
|
|
51612
|
+
issues.set(source, [{ path: source, code: "list_sections_failed", message: describe(error) }]);
|
|
51613
|
+
report2(error, `listSections:${source}`);
|
|
51614
|
+
sections = [{ source, id: "error", title: source, order: 900, notice: describe(error), noticeLevel: "error", fields: [] }];
|
|
51615
|
+
}
|
|
51616
|
+
const pending = notices.get(source);
|
|
51617
|
+
const withNotice = pending !== void 0 && sections.length > 0 ? sections.map((section, index) => index === 0 ? { ...section, notice: pending, noticeLevel: "error" } : section) : sections;
|
|
51618
|
+
published.set(source, withNotice);
|
|
51619
|
+
return withNotice;
|
|
51620
|
+
}
|
|
51621
|
+
function merged() {
|
|
51622
|
+
const all = [];
|
|
51623
|
+
for (const source of contributions.keys()) all.push(...readOwner(source, false));
|
|
51624
|
+
return all.slice().sort((a, b2) => a.order - b2.order || a.source.localeCompare(b2.source) || a.title.localeCompare(b2.title));
|
|
51625
|
+
}
|
|
51626
|
+
return {
|
|
51627
|
+
register(contribution) {
|
|
51628
|
+
if (disposed) throw new Error("kxm tui registry is disposed");
|
|
51629
|
+
const source = normalizeSource(contribution.source);
|
|
51630
|
+
notices.delete(source);
|
|
51631
|
+
published.delete(source);
|
|
51632
|
+
issues.delete(source);
|
|
51633
|
+
contributions.set(source, { ...contribution, source });
|
|
51634
|
+
emit({ reason: "publish", source });
|
|
51635
|
+
},
|
|
51636
|
+
unregister(source) {
|
|
51637
|
+
contributions.delete(source);
|
|
51638
|
+
published.delete(source);
|
|
51639
|
+
issues.delete(source);
|
|
51640
|
+
notices.delete(source);
|
|
51641
|
+
emit({ reason: "publish", source });
|
|
51642
|
+
},
|
|
51643
|
+
getSections() {
|
|
51644
|
+
return merged();
|
|
51645
|
+
},
|
|
51646
|
+
getIssues() {
|
|
51647
|
+
return [...issues.values()].flat();
|
|
51648
|
+
},
|
|
51649
|
+
async invoke(invocation) {
|
|
51650
|
+
if (disposed) return { ok: false, message: "kxm tui registry is disposed" };
|
|
51651
|
+
const contribution = contributions.get(invocation.source);
|
|
51652
|
+
if (!contribution) {
|
|
51653
|
+
emit({ reason: "refused", source: invocation.source, message: "unknown owner" });
|
|
51654
|
+
return { ok: false, message: `${invocation.source} does not own a surface here` };
|
|
51655
|
+
}
|
|
51656
|
+
const handler = contribution.handlers[invocation.action];
|
|
51657
|
+
if (!handler) {
|
|
51658
|
+
const message = `${invocation.source} does not accept "${invocation.action}"`;
|
|
51659
|
+
emit({ reason: "refused", source: invocation.source, message });
|
|
51660
|
+
return { ok: false, message };
|
|
51661
|
+
}
|
|
51662
|
+
const currentEpoch = epoch;
|
|
51663
|
+
try {
|
|
51664
|
+
await handler({
|
|
51665
|
+
sectionId: invocation.sectionId,
|
|
51666
|
+
fieldId: invocation.fieldId,
|
|
51667
|
+
action: invocation.action,
|
|
51668
|
+
...invocation.value === void 0 ? {} : { value: invocation.value }
|
|
51669
|
+
});
|
|
51670
|
+
} catch (error) {
|
|
51671
|
+
const failure = contribution.describeError?.(error, invocation.action) ?? describe(error);
|
|
51672
|
+
notices.set(invocation.source, failure);
|
|
51673
|
+
emit({ reason: "failed", source: invocation.source, message: failure });
|
|
51674
|
+
readOwner(invocation.source, true);
|
|
51675
|
+
generation += 1;
|
|
51676
|
+
return { ok: false, message: failure };
|
|
51677
|
+
}
|
|
51678
|
+
if (currentEpoch !== epoch) {
|
|
51679
|
+
readOwner(invocation.source, true);
|
|
51680
|
+
return { ok: false, message: "stale write result was discarded" };
|
|
51681
|
+
}
|
|
51682
|
+
notices.delete(invocation.source);
|
|
51683
|
+
readOwner(invocation.source, true);
|
|
51684
|
+
generation += 1;
|
|
51685
|
+
emit({ reason: "publish", source: invocation.source });
|
|
51686
|
+
return { ok: true };
|
|
51687
|
+
},
|
|
51688
|
+
subscribe(listener) {
|
|
51689
|
+
listeners.add(listener);
|
|
51690
|
+
return () => listeners.delete(listener);
|
|
51691
|
+
},
|
|
51692
|
+
getGeneration() {
|
|
51693
|
+
return generation;
|
|
51694
|
+
},
|
|
51695
|
+
hasSource(source) {
|
|
51696
|
+
return contributions.has(source);
|
|
51697
|
+
},
|
|
51698
|
+
listSources() {
|
|
51699
|
+
return [...contributions.keys()];
|
|
51700
|
+
},
|
|
51701
|
+
dispose() {
|
|
51702
|
+
if (disposed) return;
|
|
51703
|
+
disposed = true;
|
|
51704
|
+
epoch += 1;
|
|
51705
|
+
listeners.clear();
|
|
51706
|
+
contributions.clear();
|
|
51707
|
+
published.clear();
|
|
51708
|
+
issues.clear();
|
|
51709
|
+
notices.clear();
|
|
51710
|
+
}
|
|
51711
|
+
};
|
|
51712
|
+
}
|
|
51713
|
+
|
|
51714
|
+
// packages/core/tui/src/adapters/terminal.ts
|
|
51715
|
+
function renderKxmTuiPanelFrame(input) {
|
|
51716
|
+
const theme = input.theme ?? createPlainKxmTuiTheme();
|
|
51717
|
+
const lines = renderKxmTuiPanel(
|
|
51718
|
+
input.registry.getSections(),
|
|
51719
|
+
initialKxmTuiPanelState(input.registry.getSections()),
|
|
51720
|
+
theme,
|
|
51721
|
+
{ title: input.title, footer: input.footer ?? "non-interactive frame", nowMs: input.nowMs ?? Date.now() },
|
|
51722
|
+
input.width ?? 100
|
|
51723
|
+
);
|
|
51724
|
+
return `${lines.map((line) => stripTerminalSequences(line)).join("\n")}
|
|
51725
|
+
`;
|
|
51726
|
+
}
|
|
51727
|
+
async function runKxmTuiPanel(input) {
|
|
51728
|
+
const theme = input.theme ?? createDefaultKxmTuiTheme();
|
|
51729
|
+
const tty = input.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
51730
|
+
if (!tty) {
|
|
51731
|
+
input.stdout(renderKxmTuiPanelFrame({
|
|
51732
|
+
registry: input.registry,
|
|
51733
|
+
title: input.title,
|
|
51734
|
+
theme,
|
|
51735
|
+
...input.now === void 0 ? {} : { nowMs: input.now() },
|
|
51736
|
+
...input.breadcrumb ? { footer: input.breadcrumb } : {}
|
|
51737
|
+
}));
|
|
51738
|
+
return 0;
|
|
51739
|
+
}
|
|
51740
|
+
const terminal = input.terminal ?? new ProcessTerminal();
|
|
51741
|
+
const tui = new TuiAltScreen(terminal, false, void 0, { mouse: true });
|
|
51742
|
+
let timer;
|
|
51743
|
+
let stopped = false;
|
|
51744
|
+
let finished = () => void 0;
|
|
51745
|
+
const done = new Promise((resolve35) => {
|
|
51746
|
+
finished = resolve35;
|
|
51747
|
+
});
|
|
51748
|
+
const stop = () => {
|
|
51749
|
+
if (stopped) return;
|
|
51750
|
+
stopped = true;
|
|
51751
|
+
if (timer) clearInterval(timer);
|
|
51752
|
+
input.abort?.removeEventListener("abort", stop);
|
|
51753
|
+
panel.dispose();
|
|
51754
|
+
tui.stop();
|
|
51755
|
+
finished();
|
|
51756
|
+
};
|
|
51757
|
+
const panel = new KxmTuiPanelComponent({
|
|
51758
|
+
registry: input.registry,
|
|
51759
|
+
theme,
|
|
51760
|
+
title: input.title,
|
|
51761
|
+
...input.breadcrumb ? { breadcrumb: input.breadcrumb } : {},
|
|
51762
|
+
...input.helpLines ? { helpLines: input.helpLines } : {},
|
|
51763
|
+
requestRender: () => tui.requestRender(),
|
|
51764
|
+
onQuit: stop,
|
|
51765
|
+
getWidth: () => terminal.columns
|
|
51766
|
+
});
|
|
51767
|
+
input.abort?.addEventListener("abort", stop, { once: true });
|
|
51768
|
+
tui.setLayoutRoot(panel.root);
|
|
51769
|
+
tui.setFocus(panel);
|
|
51770
|
+
tui.start();
|
|
51771
|
+
timer = setInterval(() => panel.refresh(), input.pollMs ?? 2e3);
|
|
51772
|
+
timer.unref?.();
|
|
51773
|
+
await done;
|
|
51774
|
+
return stopped && input.abort?.aborted ? 130 : 0;
|
|
51775
|
+
}
|
|
50384
51776
|
|
|
50385
51777
|
// plugins/kxm/src/external-effects.ts
|
|
50386
51778
|
init_database();
|
|
@@ -53056,7 +54448,7 @@ init_routing();
|
|
|
53056
54448
|
init_prices();
|
|
53057
54449
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
53058
54450
|
import { createHash as createHash19 } from "node:crypto";
|
|
53059
|
-
import { existsSync as
|
|
54451
|
+
import { existsSync as existsSync49, mkdtempSync as mkdtempSync3, readFileSync as readFileSync43, rmSync as rmSync13 } from "node:fs";
|
|
53060
54452
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
53061
54453
|
import { basename as basename11, extname as extname3, join as join57, resolve as resolve33 } from "node:path";
|
|
53062
54454
|
import { createInterface as createInterface3 } from "node:readline";
|
|
@@ -54301,6 +55693,304 @@ function executeSshRun(params) {
|
|
|
54301
55693
|
// plugins/kxm/src/cli/system.ts
|
|
54302
55694
|
init_config();
|
|
54303
55695
|
|
|
55696
|
+
// plugins/kxm/src/config-editor.ts
|
|
55697
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
55698
|
+
import { existsSync as existsSync46, readFileSync as readFileSync41 } from "node:fs";
|
|
55699
|
+
init_config();
|
|
55700
|
+
var KXM_CONFIG_EDITOR_SOURCE = "kxm/config";
|
|
55701
|
+
var INTEGER_TEXT = /^(0|[1-9][0-9]*)$/;
|
|
55702
|
+
var DECIMAL_TEXT = /^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$/;
|
|
55703
|
+
var TEXT_VALUE = /^[A-Za-z0-9@][A-Za-z0-9@._:/-]{0,127}$/;
|
|
55704
|
+
var SCREEN_CHOICES = ["agents", "tasks", "workflows", "plans", "inbox", "procs", "spend"];
|
|
55705
|
+
var SECTIONS = [
|
|
55706
|
+
{
|
|
55707
|
+
id: "user",
|
|
55708
|
+
title: "User",
|
|
55709
|
+
order: 10,
|
|
55710
|
+
fields: [
|
|
55711
|
+
{ id: "user-theme", label: "Theme", keyPath: "user.theme", spec: { kind: "enum", choices: ["dark", "light", "minimal"] } },
|
|
55712
|
+
{ id: "user-token-budget", label: "Token budget", keyPath: "user.tokenBudget", spec: { kind: "number", integer: true, min: 1, max: 2e6 } }
|
|
55713
|
+
]
|
|
55714
|
+
},
|
|
55715
|
+
{
|
|
55716
|
+
id: "defaults",
|
|
55717
|
+
title: "Defaults",
|
|
55718
|
+
order: 20,
|
|
55719
|
+
fields: [
|
|
55720
|
+
{ id: "defaults-workflow", label: "Workflow", keyPath: "defaults.workflow", spec: { kind: "text" } },
|
|
55721
|
+
{ id: "defaults-harness", label: "Harness", keyPath: "defaults.harness", spec: { kind: "text" } }
|
|
55722
|
+
]
|
|
55723
|
+
},
|
|
55724
|
+
{
|
|
55725
|
+
id: "dash",
|
|
55726
|
+
title: "Dashboard",
|
|
55727
|
+
order: 30,
|
|
55728
|
+
fields: [
|
|
55729
|
+
{ id: "dash-screen", label: "Default screen", keyPath: "dash.defaultScreen", spec: { kind: "enum", choices: SCREEN_CHOICES } },
|
|
55730
|
+
{ id: "dash-refresh", label: "Refresh interval", keyPath: "dash.refreshIntervalMs", spec: { kind: "number", integer: true, min: 250, max: 36e5 } },
|
|
55731
|
+
{ id: "dash-auto-open", label: "Auto-open", keyPath: "dash.autoOpen", spec: { kind: "enum", choices: ["true", "false"] } }
|
|
55732
|
+
]
|
|
55733
|
+
},
|
|
55734
|
+
{
|
|
55735
|
+
id: "sync",
|
|
55736
|
+
title: "Sync",
|
|
55737
|
+
order: 40,
|
|
55738
|
+
fields: [
|
|
55739
|
+
{ id: "sync-tracker", label: "Default tracker", keyPath: "sync.defaultTracker", spec: { kind: "enum", choices: ["github", "jira", "none"] } }
|
|
55740
|
+
]
|
|
55741
|
+
},
|
|
55742
|
+
{
|
|
55743
|
+
id: "hub",
|
|
55744
|
+
title: "Hub",
|
|
55745
|
+
order: 50,
|
|
55746
|
+
fields: [
|
|
55747
|
+
{
|
|
55748
|
+
id: "hub-auto-start",
|
|
55749
|
+
label: "Auto-start",
|
|
55750
|
+
keyPath: "hub.autoStart",
|
|
55751
|
+
spec: { kind: "enum", choices: ["background", "off"] },
|
|
55752
|
+
note: "Hub URLs and key references stay out of this panel."
|
|
55753
|
+
}
|
|
55754
|
+
]
|
|
55755
|
+
},
|
|
55756
|
+
{
|
|
55757
|
+
id: "improvement",
|
|
55758
|
+
title: "Improvement",
|
|
55759
|
+
order: 60,
|
|
55760
|
+
fields: [
|
|
55761
|
+
{ id: "improvement-policy", label: "Promotion policy", keyPath: "improvement.promotionPolicy", spec: { kind: "enum", choices: ["manual_pr", "critic_quorum", "auto_threshold"] } },
|
|
55762
|
+
{ id: "improvement-half-life", label: "Telemetry half-life", keyPath: "improvement.telemetryHalfLifeDays", spec: { kind: "number", integer: false, min: 1e-6, max: 3650 } },
|
|
55763
|
+
{ id: "improvement-min-runs", label: "Minimum runs", keyPath: "improvement.autoThreshold.minRuns", spec: { kind: "number", integer: true, min: 1, max: 1e6 } },
|
|
55764
|
+
{ id: "improvement-min-pass-rate", label: "Minimum pass rate", keyPath: "improvement.autoThreshold.minPassRate", spec: { kind: "number", integer: false, min: 0, max: 1 } },
|
|
55765
|
+
{ id: "improvement-min-cost-savings", label: "Minimum cost savings", keyPath: "improvement.autoThreshold.minCostSavings", spec: { kind: "number", integer: false, min: 0, max: 1e6 } }
|
|
55766
|
+
]
|
|
55767
|
+
},
|
|
55768
|
+
{
|
|
55769
|
+
id: "routing",
|
|
55770
|
+
title: "Routing",
|
|
55771
|
+
order: 70,
|
|
55772
|
+
fields: [
|
|
55773
|
+
{
|
|
55774
|
+
id: "routing-shadow",
|
|
55775
|
+
label: "Shadow execution",
|
|
55776
|
+
keyPath: "routing.shadowExecution.enabled",
|
|
55777
|
+
spec: { kind: "enum", choices: ["true", "false"] },
|
|
55778
|
+
note: "Strategy, fallback, and context limits stay in role and route files."
|
|
55779
|
+
},
|
|
55780
|
+
{ id: "routing-sample-rate", label: "Sample rate", keyPath: "routing.shadowExecution.sampleRate", spec: { kind: "number", integer: false, min: 0, max: 1 } },
|
|
55781
|
+
{ id: "routing-breaker-mode", label: "Breaker mode", keyPath: "routing.circuitBreaker.mode", spec: { kind: "enum", choices: ["soft_demotion", "quarantine"] } },
|
|
55782
|
+
{ id: "routing-failure-threshold", label: "Failure threshold", keyPath: "routing.circuitBreaker.failureThreshold", spec: { kind: "number", integer: true, min: 1, max: 1e4 } },
|
|
55783
|
+
{ id: "routing-window-seconds", label: "Window seconds", keyPath: "routing.circuitBreaker.windowSeconds", spec: { kind: "number", integer: true, min: 1, max: 31536e3 } },
|
|
55784
|
+
{ id: "routing-cooldown-seconds", label: "Cooldown seconds", keyPath: "routing.circuitBreaker.cooldownSeconds", spec: { kind: "number", integer: true, min: 0, max: 31536e3 } },
|
|
55785
|
+
{ id: "routing-penalty", label: "Penalty multiplier", keyPath: "routing.circuitBreaker.penaltyMultiplier", spec: { kind: "number", integer: false, min: 1e-6, max: 1e3 } }
|
|
55786
|
+
]
|
|
55787
|
+
},
|
|
55788
|
+
{
|
|
55789
|
+
id: "telemetry",
|
|
55790
|
+
title: "Telemetry",
|
|
55791
|
+
order: 80,
|
|
55792
|
+
fields: [
|
|
55793
|
+
{ id: "telemetry-federated", label: "Federated", keyPath: "telemetry.federated", spec: { kind: "enum", choices: ["true", "false"] } },
|
|
55794
|
+
{ id: "telemetry-anonymize", label: "Anonymize", keyPath: "telemetry.anonymize", spec: { kind: "enum", choices: ["true", "false"] } }
|
|
55795
|
+
]
|
|
55796
|
+
}
|
|
55797
|
+
];
|
|
55798
|
+
var KxmConfigEditorError = class extends Error {
|
|
55799
|
+
code = "config_edit_refused";
|
|
55800
|
+
constructor(message) {
|
|
55801
|
+
super(message);
|
|
55802
|
+
this.name = "KxmConfigEditorError";
|
|
55803
|
+
}
|
|
55804
|
+
};
|
|
55805
|
+
function createKxmConfigEditorContribution(target) {
|
|
55806
|
+
return {
|
|
55807
|
+
source: KXM_CONFIG_EDITOR_SOURCE,
|
|
55808
|
+
republishAfterWrite: true,
|
|
55809
|
+
listSections: () => listConfigEditorSections(target),
|
|
55810
|
+
handlers: {
|
|
55811
|
+
[KXM_TUI_SET_ACTION]: (input) => {
|
|
55812
|
+
writeConfigEditorValue(target, input.sectionId, input.fieldId, input.value);
|
|
55813
|
+
},
|
|
55814
|
+
[KXM_TUI_CLEAR_ACTION]: (input) => {
|
|
55815
|
+
clearConfigEditorValue(target, input.sectionId, input.fieldId);
|
|
55816
|
+
}
|
|
55817
|
+
},
|
|
55818
|
+
describeError: (error) => error instanceof Error && error.message ? error.message : "config edit failed"
|
|
55819
|
+
};
|
|
55820
|
+
}
|
|
55821
|
+
function scopeFile(target) {
|
|
55822
|
+
return kxmConfigFileForScope(target.cwd, target.scope, target.userConfigDir);
|
|
55823
|
+
}
|
|
55824
|
+
function scopePhrase(scope) {
|
|
55825
|
+
return scope === "project" ? "the project file .kxm/config.yaml" : "the user file config.yaml";
|
|
55826
|
+
}
|
|
55827
|
+
function listConfigEditorSections(target) {
|
|
55828
|
+
const config = loadKxmConfig(target.cwd, {
|
|
55829
|
+
...target.userConfigDir === void 0 ? {} : { userConfigDir: target.userConfigDir },
|
|
55830
|
+
layers: target.scope === "user" ? "user" : "all"
|
|
55831
|
+
});
|
|
55832
|
+
const owned = readScopeDocument(scopeFile(target));
|
|
55833
|
+
const where = scopePhrase(target.scope);
|
|
55834
|
+
return SECTIONS.map((section) => {
|
|
55835
|
+
const fields = section.fields.map((field) => publishField(field, config, owned, where));
|
|
55836
|
+
const diagnosis = fields.find((field) => field.statusText)?.statusText;
|
|
55837
|
+
return {
|
|
55838
|
+
source: KXM_CONFIG_EDITOR_SOURCE,
|
|
55839
|
+
id: section.id,
|
|
55840
|
+
title: section.title,
|
|
55841
|
+
order: section.order,
|
|
55842
|
+
detail: `kxm.config.v1 \xB7 ${target.scope} scope. Re-read from disk after each write.`,
|
|
55843
|
+
...diagnosis === void 0 ? {} : { notice: diagnosis, noticeLevel: "error" },
|
|
55844
|
+
fields
|
|
55845
|
+
};
|
|
55846
|
+
});
|
|
55847
|
+
}
|
|
55848
|
+
function publishField(field, config, owned, where) {
|
|
55849
|
+
const effective = getKxmConfigValue(config, field.keyPath);
|
|
55850
|
+
const stored = ownValue(owned, field.keyPath);
|
|
55851
|
+
const shown = displayScalar(effective);
|
|
55852
|
+
const inherited = !stored.present;
|
|
55853
|
+
const diagnosis = storedDiagnosis(field, stored, effective);
|
|
55854
|
+
const detail = [
|
|
55855
|
+
`Writes ${where}.`,
|
|
55856
|
+
inherited ? "Inherited." : "Set in this scope.",
|
|
55857
|
+
"Clear removes only this override.",
|
|
55858
|
+
field.note ?? "",
|
|
55859
|
+
diagnosis ?? ""
|
|
55860
|
+
].filter((part) => part.length > 0).join(" ");
|
|
55861
|
+
const choices = field.spec.kind === "enum" ? field.spec.choices.map((choice) => ({ id: choice, label: choice })) : void 0;
|
|
55862
|
+
return {
|
|
55863
|
+
id: field.id,
|
|
55864
|
+
label: field.label,
|
|
55865
|
+
kind: field.spec.kind === "enum" ? "enum" : "text",
|
|
55866
|
+
keyPath: field.keyPath,
|
|
55867
|
+
detail,
|
|
55868
|
+
status: "ready",
|
|
55869
|
+
...shown === void 0 ? {} : { value: shown },
|
|
55870
|
+
...choices === void 0 ? {} : { choices },
|
|
55871
|
+
...diagnosis === void 0 ? {} : { statusText: diagnosis }
|
|
55872
|
+
};
|
|
55873
|
+
}
|
|
55874
|
+
function displayScalar(value) {
|
|
55875
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
55876
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
55877
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
55878
|
+
return void 0;
|
|
55879
|
+
}
|
|
55880
|
+
function isBooleanEnum(spec) {
|
|
55881
|
+
return spec.kind === "enum" && spec.choices.length === 2 && spec.choices.every((choice) => choice === "true" || choice === "false");
|
|
55882
|
+
}
|
|
55883
|
+
function storedTypeMatches(field, value) {
|
|
55884
|
+
if (field.spec.kind === "number") {
|
|
55885
|
+
return typeof value === "number" && Number.isFinite(value) && (!field.spec.integer || Number.isInteger(value));
|
|
55886
|
+
}
|
|
55887
|
+
if (isBooleanEnum(field.spec)) return typeof value === "boolean";
|
|
55888
|
+
return typeof value === "string";
|
|
55889
|
+
}
|
|
55890
|
+
function storedDiagnosis(field, stored, effective) {
|
|
55891
|
+
if (!stored.present) return void 0;
|
|
55892
|
+
if (!storedTypeMatches(field, stored.value)) return "stored type does not match this field";
|
|
55893
|
+
if (field.spec.kind === "enum" && !isBooleanEnum(field.spec) && !field.spec.choices.includes(stored.value)) {
|
|
55894
|
+
return "stored value is not a supported scalar";
|
|
55895
|
+
}
|
|
55896
|
+
if (field.spec.kind === "number") {
|
|
55897
|
+
const value = stored.value;
|
|
55898
|
+
if (value < field.spec.min || value > field.spec.max) return "stored value is not a supported scalar";
|
|
55899
|
+
}
|
|
55900
|
+
if (stored.value !== effective) return "stored value is not a supported scalar";
|
|
55901
|
+
return void 0;
|
|
55902
|
+
}
|
|
55903
|
+
function writeConfigEditorValue(target, sectionId, fieldId, raw) {
|
|
55904
|
+
const field = requireField(sectionId, fieldId);
|
|
55905
|
+
const value = parseEditorValue(field, raw);
|
|
55906
|
+
setKxmConfigValue(target.cwd, field.keyPath, value, {
|
|
55907
|
+
scope: target.scope,
|
|
55908
|
+
...target.userConfigDir === void 0 ? {} : { userConfigDir: target.userConfigDir }
|
|
55909
|
+
});
|
|
55910
|
+
}
|
|
55911
|
+
function clearConfigEditorValue(target, sectionId, fieldId) {
|
|
55912
|
+
const field = requireField(sectionId, fieldId);
|
|
55913
|
+
deleteKxmConfigValue(target.cwd, field.keyPath, {
|
|
55914
|
+
scope: target.scope,
|
|
55915
|
+
...target.userConfigDir === void 0 ? {} : { userConfigDir: target.userConfigDir }
|
|
55916
|
+
});
|
|
55917
|
+
}
|
|
55918
|
+
function requireField(sectionId, fieldId) {
|
|
55919
|
+
const section = SECTIONS.find((item) => item.id === sectionId);
|
|
55920
|
+
const field = section?.fields.find((item) => item.id === fieldId);
|
|
55921
|
+
if (!field) throw new KxmConfigEditorError("unknown configuration field");
|
|
55922
|
+
return field;
|
|
55923
|
+
}
|
|
55924
|
+
function parseEditorValue(field, raw) {
|
|
55925
|
+
if (raw === void 0) throw new KxmConfigEditorError("a value is required");
|
|
55926
|
+
if (raw.length === 0 || raw.length > 128 || /[\u0000-\u001f\u007f]/u.test(raw)) {
|
|
55927
|
+
throw new KxmConfigEditorError("value must be a single bounded line");
|
|
55928
|
+
}
|
|
55929
|
+
if (field.spec.kind === "enum") {
|
|
55930
|
+
if (!field.spec.choices.includes(raw)) throw new KxmConfigEditorError("value is not a supported choice");
|
|
55931
|
+
if (isBooleanEnum(field.spec)) return raw === "true";
|
|
55932
|
+
return raw;
|
|
55933
|
+
}
|
|
55934
|
+
if (field.spec.kind === "text") {
|
|
55935
|
+
if (!TEXT_VALUE.test(raw)) throw new KxmConfigEditorError("value must be a single bounded line");
|
|
55936
|
+
return raw;
|
|
55937
|
+
}
|
|
55938
|
+
if (!(field.spec.integer ? INTEGER_TEXT : DECIMAL_TEXT).test(raw)) {
|
|
55939
|
+
throw new KxmConfigEditorError("value must be a finite number in range");
|
|
55940
|
+
}
|
|
55941
|
+
const parsed = Number(raw);
|
|
55942
|
+
if (!Number.isFinite(parsed) || field.spec.integer && !Number.isInteger(parsed) || parsed < field.spec.min || parsed > field.spec.max) {
|
|
55943
|
+
throw new KxmConfigEditorError("value must be a finite number in range");
|
|
55944
|
+
}
|
|
55945
|
+
return parsed;
|
|
55946
|
+
}
|
|
55947
|
+
function readScopeDocument(file) {
|
|
55948
|
+
if (!existsSync46(file)) return void 0;
|
|
55949
|
+
const parsed = (0, import_yaml21.parse)(readFileSync41(file, "utf8"));
|
|
55950
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
55951
|
+
throw new KxmConfigEditorError(`config file ${file} must be a mapping`);
|
|
55952
|
+
}
|
|
55953
|
+
return parsed;
|
|
55954
|
+
}
|
|
55955
|
+
function ownValue(root, keyPath) {
|
|
55956
|
+
if (!root) return { present: false, value: void 0 };
|
|
55957
|
+
let cursor2 = root;
|
|
55958
|
+
for (const part of keyPath.split(".")) {
|
|
55959
|
+
if (!cursor2 || typeof cursor2 !== "object" || Array.isArray(cursor2)) return { present: false, value: void 0 };
|
|
55960
|
+
const record = cursor2;
|
|
55961
|
+
if (!Object.prototype.hasOwnProperty.call(record, part)) return { present: false, value: void 0 };
|
|
55962
|
+
cursor2 = record[part];
|
|
55963
|
+
}
|
|
55964
|
+
return { present: true, value: cursor2 };
|
|
55965
|
+
}
|
|
55966
|
+
var EDITOR_HELP = [
|
|
55967
|
+
"\u2191\u2193 move \xB7 enter edits \xB7 x clears this scope's override \xB7 q quits",
|
|
55968
|
+
"Writes are checked, saved, then re-read. A refused edit leaves the file unchanged."
|
|
55969
|
+
];
|
|
55970
|
+
var FRAME_NOW_MS = Date.UTC(2026, 9, 11);
|
|
55971
|
+
async function runKxmConfigEditor(input) {
|
|
55972
|
+
const registry = createKxmTuiRegistry();
|
|
55973
|
+
try {
|
|
55974
|
+
registry.register(createKxmConfigEditorContribution(input.target));
|
|
55975
|
+
registry.getSections();
|
|
55976
|
+
const failed2 = registry.getIssues()[0];
|
|
55977
|
+
if (failed2) throw new KxmConfigEditorError(failed2.message);
|
|
55978
|
+
const interactive = input.isTty && !input.dryRun;
|
|
55979
|
+
return await runKxmTuiPanel({
|
|
55980
|
+
registry,
|
|
55981
|
+
title: "KXM configuration",
|
|
55982
|
+
breadcrumb: `kxm.config.v1 \xB7 ${input.target.scope} scope`,
|
|
55983
|
+
helpLines: EDITOR_HELP,
|
|
55984
|
+
stdout: input.stdout,
|
|
55985
|
+
isTty: interactive,
|
|
55986
|
+
...interactive ? {} : { theme: createPlainKxmTuiTheme() },
|
|
55987
|
+
now: () => FRAME_NOW_MS
|
|
55988
|
+
});
|
|
55989
|
+
} finally {
|
|
55990
|
+
registry.dispose();
|
|
55991
|
+
}
|
|
55992
|
+
}
|
|
55993
|
+
|
|
54304
55994
|
// plugins/kxm/src/autocomplete.ts
|
|
54305
55995
|
var TOP_LEVEL_COMMANDS = [
|
|
54306
55996
|
"init",
|
|
@@ -54354,7 +56044,7 @@ var SUBCOMMANDS = {
|
|
|
54354
56044
|
memory: ["brief", "note", "sync"],
|
|
54355
56045
|
routing: ["report", "benchmark"],
|
|
54356
56046
|
hub: ["view", "start", "stop", "bind", "unbind"],
|
|
54357
|
-
config: ["get", "set", "list"],
|
|
56047
|
+
config: ["get", "set", "edit", "list"],
|
|
54358
56048
|
goal: ["create", "list", "get"],
|
|
54359
56049
|
task: ["create", "list", "get", "run", "sync"],
|
|
54360
56050
|
studio: ["layout", "serve"],
|
|
@@ -54476,7 +56166,7 @@ _kxm() {
|
|
|
54476
56166
|
_arguments '1:shell:(bash zsh fish)'
|
|
54477
56167
|
;;
|
|
54478
56168
|
config)
|
|
54479
|
-
_arguments '1:action:(get set list)'
|
|
56169
|
+
_arguments '1:action:(get set edit list)'
|
|
54480
56170
|
;;
|
|
54481
56171
|
runs)
|
|
54482
56172
|
_arguments '1:subcommand:(status cancel list)'
|
|
@@ -54520,7 +56210,7 @@ complete -c kxm -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
|
54520
56210
|
complete -c kxm -n "__fish_seen_subcommand_from dash" -l screen -a "agents tasks workflows plans inbox procs spend"
|
|
54521
56211
|
|
|
54522
56212
|
# Config subcommand
|
|
54523
|
-
complete -c kxm -n "__fish_seen_subcommand_from config" -a "get set list"
|
|
56213
|
+
complete -c kxm -n "__fish_seen_subcommand_from config" -a "get set edit list"
|
|
54524
56214
|
|
|
54525
56215
|
# Runs subcommand
|
|
54526
56216
|
complete -c kxm -n "__fish_seen_subcommand_from runs" -a "status cancel list"
|
|
@@ -54540,7 +56230,7 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
54540
56230
|
}
|
|
54541
56231
|
|
|
54542
56232
|
// plugins/kxm/src/completion-install.ts
|
|
54543
|
-
import { existsSync as
|
|
56233
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync42, writeFileSync as writeFileSync28 } from "node:fs";
|
|
54544
56234
|
import { homedir as homedir9 } from "node:os";
|
|
54545
56235
|
import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute11, join as join55, resolve as resolve32 } from "node:path";
|
|
54546
56236
|
var COMPLETION_MARKER = "# kxm completion";
|
|
@@ -54569,7 +56259,7 @@ function completionScriptPath(shell, options = {}) {
|
|
|
54569
56259
|
function bashRcCandidate(options) {
|
|
54570
56260
|
const home = effectiveHome(options);
|
|
54571
56261
|
const candidates = [join55(home, ".bashrc"), join55(home, ".bash_profile")];
|
|
54572
|
-
const existing = candidates.find((candidate) =>
|
|
56262
|
+
const existing = candidates.find((candidate) => existsSync47(candidate));
|
|
54573
56263
|
return existing ?? candidates[0];
|
|
54574
56264
|
}
|
|
54575
56265
|
function zshRcCandidate(options) {
|
|
@@ -54614,13 +56304,13 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
54614
56304
|
const scriptPath = completionScriptPath(shell, options);
|
|
54615
56305
|
const { rcFile } = completionRcTarget(shell, options);
|
|
54616
56306
|
const script = generateShellCompletion(shell);
|
|
54617
|
-
const existingScript =
|
|
54618
|
-
const existingScriptMatches = existingScript &&
|
|
56307
|
+
const existingScript = existsSync47(scriptPath);
|
|
56308
|
+
const existingScriptMatches = existingScript && readFileSync42(scriptPath, "utf8") === script;
|
|
54619
56309
|
let rcModified = false;
|
|
54620
56310
|
let alreadyInstalled = false;
|
|
54621
56311
|
if (shell === "fish") {
|
|
54622
56312
|
const fishTarget = fishCompletionTarget(shell, options);
|
|
54623
|
-
const fishInstalled =
|
|
56313
|
+
const fishInstalled = existsSync47(fishTarget) && readFileSync42(fishTarget, "utf8") === script;
|
|
54624
56314
|
alreadyInstalled = fishInstalled;
|
|
54625
56315
|
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
54626
56316
|
mkdirSync33(resolve32(fishTarget, ".."), { recursive: true });
|
|
@@ -54637,7 +56327,7 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
54637
56327
|
}
|
|
54638
56328
|
if (rcFile) {
|
|
54639
56329
|
const line = sourceLine(shell, scriptPath);
|
|
54640
|
-
const rcContent =
|
|
56330
|
+
const rcContent = existsSync47(rcFile) ? readFileSync42(rcFile, "utf8") : "";
|
|
54641
56331
|
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
54642
56332
|
}
|
|
54643
56333
|
if (!options.dryRun) {
|
|
@@ -54653,7 +56343,7 @@ ${marker}
|
|
|
54653
56343
|
${line}
|
|
54654
56344
|
`;
|
|
54655
56345
|
mkdirSync33(resolve32(rcFile, ".."), { recursive: true });
|
|
54656
|
-
writeFileSync28(rcFile, (
|
|
56346
|
+
writeFileSync28(rcFile, (existsSync47(rcFile) ? readFileSync42(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
54657
56347
|
rcModified = true;
|
|
54658
56348
|
}
|
|
54659
56349
|
}
|
|
@@ -54670,13 +56360,13 @@ function kxmBinDir(env = process.env) {
|
|
|
54670
56360
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
54671
56361
|
if (argv1 && isAbsolute11(argv1)) {
|
|
54672
56362
|
const dir = dirname24(argv1);
|
|
54673
|
-
if (
|
|
56363
|
+
if (existsSync47(join55(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
54674
56364
|
}
|
|
54675
56365
|
const pathEnv = env.PATH ?? "";
|
|
54676
56366
|
for (const part of pathEnv.split(delimiter)) {
|
|
54677
56367
|
if (!part) continue;
|
|
54678
56368
|
const candidate = resolve32(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
54679
|
-
if (
|
|
56369
|
+
if (existsSync47(candidate)) return resolve32(part);
|
|
54680
56370
|
}
|
|
54681
56371
|
return void 0;
|
|
54682
56372
|
}
|
|
@@ -54694,7 +56384,7 @@ function installPathEntry(shellInput, options = {}) {
|
|
|
54694
56384
|
}
|
|
54695
56385
|
const { rcFile } = completionRcTarget(shell, options);
|
|
54696
56386
|
const line = pathLine(binDir);
|
|
54697
|
-
const rcContent =
|
|
56387
|
+
const rcContent = existsSync47(rcFile ?? "") ? readFileSync42(rcFile, "utf8") : "";
|
|
54698
56388
|
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
54699
56389
|
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
54700
56390
|
mkdirSync33(dirname24(rcFile), { recursive: true });
|
|
@@ -54707,9 +56397,9 @@ ${line}
|
|
|
54707
56397
|
}
|
|
54708
56398
|
|
|
54709
56399
|
// plugins/kxm/src/init-guide-setup.ts
|
|
54710
|
-
var
|
|
56400
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
54711
56401
|
init_routes();
|
|
54712
|
-
import { existsSync as
|
|
56402
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync34, writeFileSync as writeFileSync29 } from "node:fs";
|
|
54713
56403
|
import { dirname as dirname25, join as join56 } from "node:path";
|
|
54714
56404
|
var ADMITTED_GUIDE_BINDINGS = Object.freeze({
|
|
54715
56405
|
"anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
@@ -55142,7 +56832,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
55142
56832
|
if (binding.harness === "pi" && !piMatch) {
|
|
55143
56833
|
files.push({
|
|
55144
56834
|
path: join56(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
55145
|
-
content: (0,
|
|
56835
|
+
content: (0, import_yaml22.stringify)(agentDocument(role, stage))
|
|
55146
56836
|
});
|
|
55147
56837
|
continue;
|
|
55148
56838
|
}
|
|
@@ -55167,15 +56857,15 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
55167
56857
|
}
|
|
55168
56858
|
files.push({
|
|
55169
56859
|
path: join56(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
55170
|
-
content: (0,
|
|
56860
|
+
content: (0, import_yaml22.stringify)(agentDocument(role, stage))
|
|
55171
56861
|
});
|
|
55172
56862
|
files.push({
|
|
55173
56863
|
path: join56(projectRoot, ".kxm", "models", `${routeId2}.yaml`),
|
|
55174
|
-
content: (0,
|
|
56864
|
+
content: (0, import_yaml22.stringify)(modelDocument)
|
|
55175
56865
|
});
|
|
55176
56866
|
files.push({
|
|
55177
56867
|
path: join56(projectRoot, ".kxm", "roles", `${role}.yaml`),
|
|
55178
|
-
content: (0,
|
|
56868
|
+
content: (0, import_yaml22.stringify)({
|
|
55179
56869
|
schema: "kxm.role.v2",
|
|
55180
56870
|
id: role,
|
|
55181
56871
|
purpose: writer ? "writer" : "experiment",
|
|
@@ -55188,7 +56878,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
55188
56878
|
for (const workflow of plan.workflows) {
|
|
55189
56879
|
files.push({
|
|
55190
56880
|
path: join56(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
55191
|
-
content: (0,
|
|
56881
|
+
content: (0, import_yaml22.stringify)(workflowDocument(workflow))
|
|
55192
56882
|
});
|
|
55193
56883
|
}
|
|
55194
56884
|
return files;
|
|
@@ -55206,14 +56896,14 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
|
|
|
55206
56896
|
policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
55207
56897
|
const path4 = join56(projectRoot, ".kxm", "routes.yaml");
|
|
55208
56898
|
mkdirSync34(dirname25(path4), { recursive: true });
|
|
55209
|
-
writeFileSync29(path4, (0,
|
|
56899
|
+
writeFileSync29(path4, (0, import_yaml22.stringify)(policy), "utf8");
|
|
55210
56900
|
return added;
|
|
55211
56901
|
}
|
|
55212
56902
|
function writeGuideSetupFiles(files) {
|
|
55213
56903
|
const written = [];
|
|
55214
56904
|
const existed = [];
|
|
55215
56905
|
for (const file of files) {
|
|
55216
|
-
if (
|
|
56906
|
+
if (existsSync48(file.path)) {
|
|
55217
56907
|
existed.push(file.path);
|
|
55218
56908
|
continue;
|
|
55219
56909
|
}
|
|
@@ -55294,7 +56984,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
55294
56984
|
for (const step of planned) {
|
|
55295
56985
|
if (step.kind === "verify") {
|
|
55296
56986
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
55297
|
-
const actual =
|
|
56987
|
+
const actual = existsSync49(step.path) ? createHash19("sha256").update(readFileSync43(step.path)).digest("hex") : "missing";
|
|
55298
56988
|
return {
|
|
55299
56989
|
ok: false,
|
|
55300
56990
|
error: "release_digest_mismatch",
|
|
@@ -55622,12 +57312,12 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
55622
57312
|
}
|
|
55623
57313
|
const selectedFile = explicitFile || configuredFile;
|
|
55624
57314
|
const file = selectedFile ? resolve33(runtime.cwd, selectedFile) : void 0;
|
|
55625
|
-
if (file && !
|
|
57315
|
+
if (file && !existsSync49(file)) {
|
|
55626
57316
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
55627
57317
|
return 1;
|
|
55628
57318
|
}
|
|
55629
57319
|
try {
|
|
55630
|
-
const raw = file ?
|
|
57320
|
+
const raw = file ? readFileSync43(file, "utf8") : inline;
|
|
55631
57321
|
if (explicitFile && file && !raw.trimStart().startsWith("[")) {
|
|
55632
57322
|
const value = parseRestrictedYaml2(raw, file);
|
|
55633
57323
|
const issues = new KxmSchemaRegistry().validate("workflow", value, file);
|
|
@@ -55802,6 +57492,37 @@ async function cmdConfigSet(runtime, key, value, options) {
|
|
|
55802
57492
|
return 1;
|
|
55803
57493
|
}
|
|
55804
57494
|
}
|
|
57495
|
+
async function cmdConfigEdit(runtime, options, isTty = Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
57496
|
+
const scope = options.scope;
|
|
57497
|
+
if (scope !== "project" && scope !== "user") {
|
|
57498
|
+
const detail = "config edit requires --scope project or --scope user";
|
|
57499
|
+
print(runtime.io, runtime.json, { ok: false, command: "config edit", error: "config_edit_unsupported", detail }, detail);
|
|
57500
|
+
return 2;
|
|
57501
|
+
}
|
|
57502
|
+
if (runtime.json) {
|
|
57503
|
+
const detail = "config edit does not print machine-readable output";
|
|
57504
|
+
print(runtime.io, runtime.json, { ok: false, command: "config edit", error: "config_edit_unsupported", detail }, detail);
|
|
57505
|
+
return 2;
|
|
57506
|
+
}
|
|
57507
|
+
try {
|
|
57508
|
+
const userConfigDir = userConfigDirForEnv(runtime.env);
|
|
57509
|
+
return await runKxmConfigEditor({
|
|
57510
|
+
target: {
|
|
57511
|
+
cwd: runtime.cwd,
|
|
57512
|
+
scope,
|
|
57513
|
+
...userConfigDir === void 0 ? {} : { userConfigDir }
|
|
57514
|
+
},
|
|
57515
|
+
stdout: (text) => runtime.io.stdout(text),
|
|
57516
|
+
isTty,
|
|
57517
|
+
dryRun: runtime.dryRun
|
|
57518
|
+
});
|
|
57519
|
+
} catch (error) {
|
|
57520
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57521
|
+
runtime.io.stderr(`config edit failed: ${message}
|
|
57522
|
+
`);
|
|
57523
|
+
return 1;
|
|
57524
|
+
}
|
|
57525
|
+
}
|
|
55805
57526
|
async function cmdConfigList(runtime) {
|
|
55806
57527
|
try {
|
|
55807
57528
|
const config = loadKxmConfig(runtime.cwd);
|
|
@@ -55876,9 +57597,9 @@ async function maybeOfferCompletionInstall(runtime) {
|
|
|
55876
57597
|
if (!pathNeeded) {
|
|
55877
57598
|
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
55878
57599
|
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
55879
|
-
if (rcFile &&
|
|
57600
|
+
if (rcFile && existsSync49(rcFile)) {
|
|
55880
57601
|
try {
|
|
55881
|
-
if (
|
|
57602
|
+
if (readFileSync43(rcFile, "utf8").includes(scriptPath)) return;
|
|
55882
57603
|
} catch {
|
|
55883
57604
|
}
|
|
55884
57605
|
}
|
|
@@ -56253,6 +57974,7 @@ var DRY_RUN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
56253
57974
|
"hub unbind",
|
|
56254
57975
|
"config get",
|
|
56255
57976
|
"config set",
|
|
57977
|
+
"config edit",
|
|
56256
57978
|
"config list",
|
|
56257
57979
|
"goal create",
|
|
56258
57980
|
"goal list",
|
|
@@ -56825,7 +58547,7 @@ function createProgram(ctx, result, argv) {
|
|
|
56825
58547
|
});
|
|
56826
58548
|
const gate = addGlobalOptions(program2.command("gate").description("Validate definitions and operate evidence gates"));
|
|
56827
58549
|
gate.helpCommand("help", "Show gate help");
|
|
56828
|
-
addGlobalOptions(gate.command("validate").description("Parse workflow definitions without printing secrets")).option("--file <path>", "Workflow definition file").action(async function
|
|
58550
|
+
addGlobalOptions(gate.command("validate").description("Parse workflow definitions without printing secrets")).option("--file <path>", "Workflow definition file").action(async function validateAction2(options) {
|
|
56829
58551
|
result.code = await cmdValidate(runtimeFrom(ctx, this), options.file);
|
|
56830
58552
|
});
|
|
56831
58553
|
addGlobalOptions(gate.command("artifacts-exist").description("Verify a non-empty file under workspace assets")).requiredOption("--path <file>", "Artifact file under workspace assets").action(async function artifactsExistAction(options) {
|
|
@@ -56953,6 +58675,9 @@ function createProgram(ctx, result, argv) {
|
|
|
56953
58675
|
addGlobalOptions(configCmd.command("set <key> <value>").description("Set a configuration value")).option("--scope <scope>", "Configuration scope: user or project (default: project)", "project").action(async function configSetAction(key, value, options) {
|
|
56954
58676
|
result.code = await cmdConfigSet(runtimeFrom(ctx, this), key, value, options);
|
|
56955
58677
|
});
|
|
58678
|
+
addGlobalOptions(configCmd.command("edit").description("Edit supported non-secret settings in the terminal panel")).option("--scope <scope>", "Write scope: project or user").action(async function configEditAction(options) {
|
|
58679
|
+
result.code = await cmdConfigEdit(runtimeFrom(ctx, this), options);
|
|
58680
|
+
});
|
|
56956
58681
|
addGlobalOptions(configCmd.command("list", { isDefault: true }).description("List resolved configuration values")).action(async function configListAction() {
|
|
56957
58682
|
result.code = await cmdConfigList(runtimeFrom(ctx, this));
|
|
56958
58683
|
});
|