@kontextmind/kxm 0.7.176 → 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 +4 -4
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +4 -3
- package/plugins/kxm/dist/cli.js +2115 -219
- 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;
|
|
@@ -43129,8 +43130,12 @@ function finalizeTruncatedResult(prefix, prefixWidth, ellipsis, ellipsisWidth, m
|
|
|
43129
43130
|
return pad2 ? result + " ".repeat(Math.max(0, maxWidth - visibleWidth2)) : result;
|
|
43130
43131
|
}
|
|
43131
43132
|
function graphemeWidth(segment) {
|
|
43132
|
-
if (segment ===
|
|
43133
|
-
|
|
43133
|
+
if (segment.length === 1) {
|
|
43134
|
+
const code = segment.charCodeAt(0);
|
|
43135
|
+
if (code >= 32 && code <= 126)
|
|
43136
|
+
return 1;
|
|
43137
|
+
if (code === 9)
|
|
43138
|
+
return 3;
|
|
43134
43139
|
}
|
|
43135
43140
|
if (terminalSpacingMarkRegex.test(segment)) {
|
|
43136
43141
|
return [...segment].length;
|
|
@@ -43174,8 +43179,9 @@ function visibleWidth(str) {
|
|
|
43174
43179
|
if (str.length === 0) {
|
|
43175
43180
|
return 0;
|
|
43176
43181
|
}
|
|
43177
|
-
|
|
43178
|
-
|
|
43182
|
+
const asciiWidth = asciiVisibleWidth(str);
|
|
43183
|
+
if (asciiWidth !== -1) {
|
|
43184
|
+
return asciiWidth;
|
|
43179
43185
|
}
|
|
43180
43186
|
const cached = widthCache.get(str);
|
|
43181
43187
|
if (cached !== void 0) {
|
|
@@ -43185,19 +43191,22 @@ function visibleWidth(str) {
|
|
|
43185
43191
|
if (str.includes(" ")) {
|
|
43186
43192
|
clean = clean.replace(/\t/g, " ");
|
|
43187
43193
|
}
|
|
43188
|
-
|
|
43194
|
+
let escapeIndex = clean.indexOf("\x1B");
|
|
43195
|
+
if (escapeIndex !== -1) {
|
|
43189
43196
|
let stripped = "";
|
|
43190
|
-
let
|
|
43191
|
-
while (
|
|
43192
|
-
const
|
|
43193
|
-
if (
|
|
43194
|
-
|
|
43195
|
-
|
|
43197
|
+
let copyFrom = 0;
|
|
43198
|
+
while (escapeIndex !== -1) {
|
|
43199
|
+
const length = ansiCodeLength(clean, escapeIndex);
|
|
43200
|
+
if (length > 0) {
|
|
43201
|
+
stripped += clean.slice(copyFrom, escapeIndex);
|
|
43202
|
+
escapeIndex += length;
|
|
43203
|
+
copyFrom = escapeIndex;
|
|
43204
|
+
} else {
|
|
43205
|
+
escapeIndex++;
|
|
43196
43206
|
}
|
|
43197
|
-
|
|
43198
|
-
i++;
|
|
43207
|
+
escapeIndex = clean.indexOf("\x1B", escapeIndex);
|
|
43199
43208
|
}
|
|
43200
|
-
clean = stripped;
|
|
43209
|
+
clean = stripped + clean.slice(copyFrom);
|
|
43201
43210
|
}
|
|
43202
43211
|
let width = 0;
|
|
43203
43212
|
for (const { segment } of graphemeSegmenter.segment(clean)) {
|
|
@@ -43301,40 +43310,54 @@ function normalizeTerminalOutput(str) {
|
|
|
43301
43310
|
return result;
|
|
43302
43311
|
}
|
|
43303
43312
|
function extractAnsiCode(str, pos) {
|
|
43304
|
-
|
|
43305
|
-
|
|
43313
|
+
const length = ansiCodeLength(str, pos);
|
|
43314
|
+
return length > 0 ? { code: str.substring(pos, pos + length), length } : null;
|
|
43315
|
+
}
|
|
43316
|
+
function asciiVisibleWidth(str) {
|
|
43317
|
+
let width = 0;
|
|
43318
|
+
let i = 0;
|
|
43319
|
+
while (i < str.length) {
|
|
43320
|
+
const code = str.charCodeAt(i);
|
|
43321
|
+
if (code >= 32 && code <= 126) {
|
|
43322
|
+
width++;
|
|
43323
|
+
i++;
|
|
43324
|
+
} else if (code === 9) {
|
|
43325
|
+
width += 3;
|
|
43326
|
+
i++;
|
|
43327
|
+
} else if (code === 27) {
|
|
43328
|
+
const length = ansiCodeLength(str, i);
|
|
43329
|
+
if (length === 0)
|
|
43330
|
+
return -1;
|
|
43331
|
+
i += length;
|
|
43332
|
+
} else {
|
|
43333
|
+
return -1;
|
|
43334
|
+
}
|
|
43335
|
+
}
|
|
43336
|
+
return width;
|
|
43337
|
+
}
|
|
43338
|
+
function ansiCodeLength(str, pos) {
|
|
43339
|
+
if (pos >= str.length || str.charCodeAt(pos) !== 27)
|
|
43340
|
+
return 0;
|
|
43306
43341
|
const next = str[pos + 1];
|
|
43307
43342
|
if (next === "[") {
|
|
43308
|
-
let j2 = pos + 2;
|
|
43309
|
-
|
|
43310
|
-
|
|
43311
|
-
|
|
43312
|
-
return { code: str.substring(pos, j2 + 1), length: j2 + 1 - pos };
|
|
43313
|
-
return null;
|
|
43314
|
-
}
|
|
43315
|
-
if (next === "]") {
|
|
43316
|
-
let j2 = pos + 2;
|
|
43317
|
-
while (j2 < str.length) {
|
|
43318
|
-
if (str[j2] === "\x07")
|
|
43319
|
-
return { code: str.substring(pos, j2 + 1), length: j2 + 1 - pos };
|
|
43320
|
-
if (str[j2] === "\x1B" && str[j2 + 1] === "\\")
|
|
43321
|
-
return { code: str.substring(pos, j2 + 2), length: j2 + 2 - pos };
|
|
43322
|
-
j2++;
|
|
43343
|
+
for (let j2 = pos + 2; j2 < str.length; j2++) {
|
|
43344
|
+
const c = str.charCodeAt(j2);
|
|
43345
|
+
if (c === 109 || c === 71 || c === 75 || c === 72 || c === 74)
|
|
43346
|
+
return j2 + 1 - pos;
|
|
43323
43347
|
}
|
|
43324
|
-
return
|
|
43348
|
+
return 0;
|
|
43325
43349
|
}
|
|
43326
|
-
if (next === "_") {
|
|
43327
|
-
let j2 = pos + 2;
|
|
43328
|
-
|
|
43329
|
-
if (
|
|
43330
|
-
return
|
|
43331
|
-
if (
|
|
43332
|
-
return
|
|
43333
|
-
j2++;
|
|
43350
|
+
if (next === "]" || next === "_") {
|
|
43351
|
+
for (let j2 = pos + 2; j2 < str.length; j2++) {
|
|
43352
|
+
const c = str.charCodeAt(j2);
|
|
43353
|
+
if (c === 7)
|
|
43354
|
+
return j2 + 1 - pos;
|
|
43355
|
+
if (c === 27 && str[j2 + 1] === "\\")
|
|
43356
|
+
return j2 + 2 - pos;
|
|
43334
43357
|
}
|
|
43335
|
-
return
|
|
43358
|
+
return 0;
|
|
43336
43359
|
}
|
|
43337
|
-
return
|
|
43360
|
+
return 0;
|
|
43338
43361
|
}
|
|
43339
43362
|
function parseOsc8Hyperlink(ansiCode) {
|
|
43340
43363
|
if (!ansiCode.startsWith("\x1B]8;")) {
|
|
@@ -43578,15 +43601,16 @@ var AnsiCodeTracker = class {
|
|
|
43578
43601
|
}
|
|
43579
43602
|
};
|
|
43580
43603
|
function updateTrackerFromText(text, tracker) {
|
|
43581
|
-
let i =
|
|
43582
|
-
while (i
|
|
43583
|
-
const
|
|
43584
|
-
if (
|
|
43585
|
-
tracker.process(
|
|
43586
|
-
i +=
|
|
43604
|
+
let i = text.indexOf("\x1B");
|
|
43605
|
+
while (i !== -1) {
|
|
43606
|
+
const length = ansiCodeLength(text, i);
|
|
43607
|
+
if (length > 0) {
|
|
43608
|
+
tracker.process(text.substring(i, i + length));
|
|
43609
|
+
i += length;
|
|
43587
43610
|
} else {
|
|
43588
43611
|
i++;
|
|
43589
43612
|
}
|
|
43613
|
+
i = text.indexOf("\x1B", i);
|
|
43590
43614
|
}
|
|
43591
43615
|
}
|
|
43592
43616
|
function getActiveBackgroundAnsi(text) {
|
|
@@ -43594,6 +43618,10 @@ function getActiveBackgroundAnsi(text) {
|
|
|
43594
43618
|
updateTrackerFromText(text, tracker);
|
|
43595
43619
|
return tracker.getActiveBackgroundCode();
|
|
43596
43620
|
}
|
|
43621
|
+
function* graphemeSegments(text) {
|
|
43622
|
+
for (const { segment } of graphemeSegmenter.segment(text))
|
|
43623
|
+
yield segment;
|
|
43624
|
+
}
|
|
43597
43625
|
function splitIntoTokensWithAnsi(text) {
|
|
43598
43626
|
const tokens = [];
|
|
43599
43627
|
let current = "";
|
|
@@ -43615,13 +43643,17 @@ function splitIntoTokensWithAnsi(text) {
|
|
|
43615
43643
|
i += ansiResult.length;
|
|
43616
43644
|
continue;
|
|
43617
43645
|
}
|
|
43618
|
-
let end = i;
|
|
43619
|
-
while (end
|
|
43620
|
-
end
|
|
43646
|
+
let end = text.indexOf("\x1B", i + 1);
|
|
43647
|
+
while (end !== -1 && ansiCodeLength(text, end) === 0) {
|
|
43648
|
+
end = text.indexOf("\x1B", end + 1);
|
|
43621
43649
|
}
|
|
43622
|
-
|
|
43650
|
+
if (end === -1)
|
|
43651
|
+
end = text.length;
|
|
43652
|
+
const chunk = text.slice(i, end);
|
|
43653
|
+
const ascii = isPrintableAscii(chunk);
|
|
43654
|
+
for (const segment of ascii ? chunk : graphemeSegments(chunk)) {
|
|
43623
43655
|
const segmentIsSpace = segment === " ";
|
|
43624
|
-
if (!segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
43656
|
+
if (!ascii && !segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
43625
43657
|
flushCurrent();
|
|
43626
43658
|
const token = pendingAnsi + segment;
|
|
43627
43659
|
pendingAnsi = "";
|
|
@@ -43655,6 +43687,10 @@ function splitIntoTokensWithAnsi(text) {
|
|
|
43655
43687
|
}
|
|
43656
43688
|
return tokens;
|
|
43657
43689
|
}
|
|
43690
|
+
function flattenLines(lines) {
|
|
43691
|
+
for (const line of lines)
|
|
43692
|
+
Number(line);
|
|
43693
|
+
}
|
|
43658
43694
|
function wrapTextWithAnsi(text, width) {
|
|
43659
43695
|
if (!text) {
|
|
43660
43696
|
return [""];
|
|
@@ -43927,9 +43963,10 @@ function sliceWithWidth(line, startCol, length, strict = false) {
|
|
|
43927
43963
|
while (i < line.length) {
|
|
43928
43964
|
const ansi = extractAnsiCode(line, i);
|
|
43929
43965
|
if (ansi) {
|
|
43930
|
-
if (currentCol >= startCol && currentCol < endCol)
|
|
43931
|
-
result += ansi.code;
|
|
43932
|
-
|
|
43966
|
+
if (currentCol >= startCol && currentCol < endCol) {
|
|
43967
|
+
result += pendingAnsi + ansi.code;
|
|
43968
|
+
pendingAnsi = "";
|
|
43969
|
+
} else if (currentCol < startCol)
|
|
43933
43970
|
pendingAnsi += ansi.code;
|
|
43934
43971
|
i += ansi.length;
|
|
43935
43972
|
continue;
|
|
@@ -44016,8 +44053,19 @@ function extractSegments(line, beforeEnd, afterStart, afterLen, strictAfter = fa
|
|
|
44016
44053
|
// node_modules/@earendil-works/pi-tui/dist/autocomplete.js
|
|
44017
44054
|
var tokenStartRegex = new RegExp(`${autocompleteBoundaryRegex.source}$`, "u");
|
|
44018
44055
|
|
|
44056
|
+
// node_modules/@earendil-works/pi-tui/dist/oklab.js
|
|
44057
|
+
var K1 = 0.206;
|
|
44058
|
+
var K2 = 0.03;
|
|
44059
|
+
var K3 = (1 + K1) / (1 + K2);
|
|
44060
|
+
|
|
44061
|
+
// node_modules/@earendil-works/pi-tui/dist/colors.js
|
|
44062
|
+
var NUMBER_PATTERN = String.raw`[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?`;
|
|
44063
|
+
var OKLCH_PATTERN = new RegExp(`^oklch\\(\\s*(${NUMBER_PATTERN})(%)?\\s+(${NUMBER_PATTERN})\\s+(${NUMBER_PATTERN})(?:deg)?\\s*\\)$`, "i");
|
|
44064
|
+
var OKHSL_PATTERN = new RegExp(`^okhsl\\(\\s*(${NUMBER_PATTERN})(?:deg)?\\s+(${NUMBER_PATTERN})(%)?\\s+(${NUMBER_PATTERN})(%)?\\s*\\)$`, "i");
|
|
44065
|
+
var GRAY_VALUES = Array.from({ length: 24 }, (_2, index) => 8 + index * 10);
|
|
44066
|
+
|
|
44019
44067
|
// node_modules/@earendil-works/pi-tui/dist/tui.js
|
|
44020
|
-
import { performance } from "node:perf_hooks";
|
|
44068
|
+
import { performance as performance2 } from "node:perf_hooks";
|
|
44021
44069
|
|
|
44022
44070
|
// node_modules/@earendil-works/pi-tui/dist/keys.js
|
|
44023
44071
|
var _kittyProtocolActive = false;
|
|
@@ -44280,6 +44328,66 @@ var LEGACY_CTRL_SEQUENCES = {
|
|
|
44280
44328
|
home: ["\x1B[7^"],
|
|
44281
44329
|
end: ["\x1B[8^"]
|
|
44282
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
|
+
};
|
|
44283
44391
|
var matchesLegacySequence = (data, sequences) => sequences.includes(data);
|
|
44284
44392
|
var matchesLegacyModifierSequence = (data, key, modifier) => {
|
|
44285
44393
|
if (modifier === MODIFIERS.shift) {
|
|
@@ -44425,6 +44533,22 @@ function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier
|
|
|
44425
44533
|
return false;
|
|
44426
44534
|
return normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
|
|
44427
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
|
+
}
|
|
44428
44552
|
function parseKeyId(keyId) {
|
|
44429
44553
|
const parts = keyId.toLowerCase().split("+");
|
|
44430
44554
|
const key = parts[parts.length - 1];
|
|
@@ -44681,6 +44805,149 @@ function matchesKey(data, keyId) {
|
|
|
44681
44805
|
}
|
|
44682
44806
|
return false;
|
|
44683
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
|
+
}
|
|
44684
44951
|
var KITTY_CSI_U_REGEX = /^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/;
|
|
44685
44952
|
var KITTY_PRINTABLE_ALLOWED_MODIFIERS = MODIFIERS.shift | LOCK_MASK;
|
|
44686
44953
|
function decodeKittyPrintable(data) {
|
|
@@ -44729,17 +44996,18 @@ function parseOscHexChannel(channel) {
|
|
|
44729
44996
|
}
|
|
44730
44997
|
return Math.round(parseInt(channel, 16) / max * 255);
|
|
44731
44998
|
}
|
|
44732
|
-
var
|
|
44999
|
+
var OSC_COLOR_RESPONSE_PATTERN = /^\x1b\](?:(1[01])|4;(\d{1,3}));([^\x07\x1b]*)(?:\x07|\x1b\\)$/i;
|
|
44733
45000
|
var COLOR_SCHEME_REPORT_PATTERN = /^(?:\x1b\[\?997;(1|2)n)+$/;
|
|
44734
|
-
function
|
|
44735
|
-
|
|
44736
|
-
}
|
|
44737
|
-
function parseOsc11BackgroundColor(data) {
|
|
44738
|
-
const match = data.match(OSC11_BACKGROUND_COLOR_RESPONSE_PATTERN);
|
|
45001
|
+
function parseOscColorResponse(data) {
|
|
45002
|
+
const match = data.match(OSC_COLOR_RESPONSE_PATTERN);
|
|
44739
45003
|
if (!match) {
|
|
44740
45004
|
return void 0;
|
|
44741
45005
|
}
|
|
44742
|
-
const
|
|
45006
|
+
const target = match[1] === "10" ? "foreground" : match[1] === "11" ? "background" : Number.parseInt(match[2], 10);
|
|
45007
|
+
return { target, rgb: parseOscColorValue(match[3]) };
|
|
45008
|
+
}
|
|
45009
|
+
function parseOscColorValue(rawValue) {
|
|
45010
|
+
const value = rawValue.trim();
|
|
44743
45011
|
if (value.startsWith("#")) {
|
|
44744
45012
|
const hex = value.slice(1);
|
|
44745
45013
|
if (/^[0-9a-f]{6}$/i.test(hex)) {
|
|
@@ -44796,7 +45064,7 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink) {
|
|
|
44796
45064
|
const terminalEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || "";
|
|
44797
45065
|
const term = process.env.TERM?.toLowerCase() || "";
|
|
44798
45066
|
const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
|
|
44799
|
-
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
45067
|
+
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit" || term.endsWith("-direct");
|
|
44800
45068
|
const isWindowsConsole = process.platform === "win32";
|
|
44801
45069
|
if (process.env.TMUX || term.startsWith("tmux")) {
|
|
44802
45070
|
return { images: null, trueColor: hasTrueColorHint, hyperlinks: tmuxForwardsHyperlink() };
|
|
@@ -44880,12 +45148,23 @@ function deleteAllKittyPlacements() {
|
|
|
44880
45148
|
return "\x1B_Ga=d,d=a,q=2\x1B\\";
|
|
44881
45149
|
}
|
|
44882
45150
|
var kittyImageMetadata = /* @__PURE__ */ new Map();
|
|
45151
|
+
function getRegisteredKittyImageMetadataFromControls(controls) {
|
|
45152
|
+
const imageId = /(?:^|,)i=(\d+)(?:,|$)/.exec(controls)?.[1];
|
|
45153
|
+
return imageId === void 0 ? void 0 : kittyImageMetadata.get(Number.parseInt(imageId, 10));
|
|
45154
|
+
}
|
|
44883
45155
|
function getRegisteredKittyImageMetadata(line) {
|
|
44884
45156
|
const controls = /\x1b_G([^;]*);/.exec(line)?.[1];
|
|
44885
|
-
|
|
45157
|
+
return controls === void 0 ? void 0 : getRegisteredKittyImageMetadataFromControls(controls);
|
|
45158
|
+
}
|
|
45159
|
+
function getExplicitKittyImageRows(controls) {
|
|
45160
|
+
const value = /(?:^|,)r=(\d+)(?:,|$)/.exec(controls)?.[1];
|
|
45161
|
+
if (value === void 0)
|
|
44886
45162
|
return void 0;
|
|
44887
|
-
const
|
|
44888
|
-
return
|
|
45163
|
+
const rows = Number.parseInt(value, 10);
|
|
45164
|
+
return rows > 0 ? rows : void 0;
|
|
45165
|
+
}
|
|
45166
|
+
function getKittyImageRowsFromControls(controls, fallbackRows) {
|
|
45167
|
+
return getExplicitKittyImageRows(controls) ?? fallbackRows;
|
|
44889
45168
|
}
|
|
44890
45169
|
function getKittyImageMetadata(line) {
|
|
44891
45170
|
const metadata = getRegisteredKittyImageMetadata(line);
|
|
@@ -44918,10 +45197,21 @@ var KITTY_PLACEMENT_CONTROL_KEYS = /* @__PURE__ */ new Set([
|
|
|
44918
45197
|
"H",
|
|
44919
45198
|
"V"
|
|
44920
45199
|
]);
|
|
45200
|
+
function getKittyImagePlacementRows(line) {
|
|
45201
|
+
const controls = /\x1b_G([^;]*);/.exec(line)?.[1];
|
|
45202
|
+
if (controls === void 0)
|
|
45203
|
+
return void 0;
|
|
45204
|
+
const explicitRows = getExplicitKittyImageRows(controls);
|
|
45205
|
+
if (explicitRows !== void 0)
|
|
45206
|
+
return explicitRows;
|
|
45207
|
+
return getRegisteredKittyImageMetadataFromControls(controls)?.rows;
|
|
45208
|
+
}
|
|
44921
45209
|
function getKittyImagePlacement(line) {
|
|
44922
45210
|
const match = /\x1b_G([^;]*);/.exec(line);
|
|
44923
|
-
|
|
44924
|
-
|
|
45211
|
+
if (!match)
|
|
45212
|
+
return void 0;
|
|
45213
|
+
const metadata = getRegisteredKittyImageMetadataFromControls(match[1]);
|
|
45214
|
+
if (!metadata)
|
|
44925
45215
|
return void 0;
|
|
44926
45216
|
let commandStart = match.index;
|
|
44927
45217
|
let commandControls = match[1];
|
|
@@ -44948,6 +45238,7 @@ function getKittyImagePlacement(line) {
|
|
|
44948
45238
|
transmissionGeneration: metadata.transmissionGeneration,
|
|
44949
45239
|
transmissionBytes: transmissionEnd - match.index,
|
|
44950
45240
|
estimatedDecodedBytes: metadata.widthPx * metadata.heightPx * 4,
|
|
45241
|
+
rows: getKittyImageRowsFromControls(match[1], metadata.rows),
|
|
44951
45242
|
sequence,
|
|
44952
45243
|
replacementLine: `${line.slice(0, match.index)}${sequence}${line.slice(transmissionEnd)}`
|
|
44953
45244
|
};
|
|
@@ -44973,8 +45264,10 @@ function dispatchMouseEvent(component, event) {
|
|
|
44973
45264
|
const result = component.handleMouse?.(event);
|
|
44974
45265
|
if (!result)
|
|
44975
45266
|
return void 0;
|
|
44976
|
-
if ("target" in result)
|
|
44977
|
-
|
|
45267
|
+
if ("target" in result) {
|
|
45268
|
+
const forwarded = result;
|
|
45269
|
+
return forwarded.focus && component.handleInput ? { ...forwarded, focusTarget: component } : forwarded;
|
|
45270
|
+
}
|
|
44978
45271
|
if (!result.handled && !result.capture && !result.focus)
|
|
44979
45272
|
return void 0;
|
|
44980
45273
|
return {
|
|
@@ -44999,6 +45292,10 @@ function retargetMouseEvent(event, target) {
|
|
|
44999
45292
|
height: target.height
|
|
45000
45293
|
};
|
|
45001
45294
|
}
|
|
45295
|
+
var TERMINAL_PALETTE_SIZE = 16;
|
|
45296
|
+
var TERMINAL_COLOR_REPLY_COUNT = 2 + TERMINAL_PALETTE_SIZE;
|
|
45297
|
+
var TERMINAL_COLOR_QUERY = `\x1B]10;?\x07\x1B]11;?\x07${Array.from({ length: TERMINAL_PALETTE_SIZE }, (_2, index) => `\x1B]4;${index};?\x07`).join("")}\x1B[c`;
|
|
45298
|
+
var DEVICE_ATTRIBUTES_RESPONSE_PATTERN = /^\x1b\[\?[\d;]*c$/;
|
|
45002
45299
|
function isFocusable(component) {
|
|
45003
45300
|
return component !== null && "focused" in component;
|
|
45004
45301
|
}
|
|
@@ -45100,8 +45397,11 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45100
45397
|
clearOnShrink = false;
|
|
45101
45398
|
fullRedrawCount = 0;
|
|
45102
45399
|
stopped = false;
|
|
45103
|
-
|
|
45104
|
-
|
|
45400
|
+
/**
|
|
45401
|
+
* Color queries waiting for their DA1 reply, oldest first. Terminals answer in order, so color
|
|
45402
|
+
* replies belong to the oldest one. Queries stay here after a timeout to collect late replies.
|
|
45403
|
+
*/
|
|
45404
|
+
pendingTerminalColorQueries = [];
|
|
45105
45405
|
terminalColorSchemeListeners = /* @__PURE__ */ new Set();
|
|
45106
45406
|
terminalColorSchemeNotificationsEnabled = false;
|
|
45107
45407
|
/** Directory for debug/crash logs. When undefined, debug logging is disabled and crash dumps fall back to the OS temp directory. */
|
|
@@ -45143,7 +45443,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45143
45443
|
return;
|
|
45144
45444
|
this.showHardwareCursor = enabled;
|
|
45145
45445
|
if (!enabled) {
|
|
45146
|
-
this.
|
|
45446
|
+
this.hideTerminalCursor();
|
|
45147
45447
|
}
|
|
45148
45448
|
this.requestRender();
|
|
45149
45449
|
}
|
|
@@ -45279,7 +45579,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45279
45579
|
if (!options?.nonCapturing && this.isOverlayVisible(entry)) {
|
|
45280
45580
|
this.setFocus(component);
|
|
45281
45581
|
}
|
|
45282
|
-
this.
|
|
45582
|
+
this.hideTerminalCursor();
|
|
45283
45583
|
this.requestRender();
|
|
45284
45584
|
return {
|
|
45285
45585
|
hide: () => {
|
|
@@ -45293,7 +45593,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45293
45593
|
this.setFocus(topVisible?.component ?? entry.preFocus);
|
|
45294
45594
|
}
|
|
45295
45595
|
if (this.overlayStack.length === 0)
|
|
45296
|
-
this.
|
|
45596
|
+
this.hideTerminalCursor();
|
|
45297
45597
|
this.requestRender();
|
|
45298
45598
|
}
|
|
45299
45599
|
},
|
|
@@ -45372,9 +45672,14 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45372
45672
|
this.setFocus(topVisible?.component ?? overlay.preFocus);
|
|
45373
45673
|
}
|
|
45374
45674
|
if (this.overlayStack.length === 0)
|
|
45375
|
-
this.
|
|
45675
|
+
this.hideTerminalCursor();
|
|
45376
45676
|
this.requestRender();
|
|
45377
45677
|
}
|
|
45678
|
+
/** Hide the cursor while running. After stop(), the shell owns the cursor and it must stay visible. */
|
|
45679
|
+
hideTerminalCursor() {
|
|
45680
|
+
if (!this.stopped)
|
|
45681
|
+
this.terminal.hideCursor();
|
|
45682
|
+
}
|
|
45378
45683
|
/** Check if there are any visible overlays */
|
|
45379
45684
|
hasOverlay() {
|
|
45380
45685
|
return this.overlayStack.some((o) => this.isOverlayVisible(o));
|
|
@@ -45499,7 +45804,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45499
45804
|
this.resetRenderState();
|
|
45500
45805
|
this.renderRequested = false;
|
|
45501
45806
|
this.cancelRenderTimer();
|
|
45502
|
-
this.lastRenderAt =
|
|
45807
|
+
this.lastRenderAt = performance2.now();
|
|
45503
45808
|
this.doRender();
|
|
45504
45809
|
}
|
|
45505
45810
|
requestRender(force = false) {
|
|
@@ -45525,7 +45830,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45525
45830
|
return;
|
|
45526
45831
|
this.cancelRenderTimer();
|
|
45527
45832
|
this.renderRequested = false;
|
|
45528
|
-
this.lastRenderAt =
|
|
45833
|
+
this.lastRenderAt = performance2.now();
|
|
45529
45834
|
this.doRender();
|
|
45530
45835
|
});
|
|
45531
45836
|
}
|
|
@@ -45539,7 +45844,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45539
45844
|
if (this.stopped || this.renderTimer || !this.renderRequested) {
|
|
45540
45845
|
return;
|
|
45541
45846
|
}
|
|
45542
|
-
const elapsed =
|
|
45847
|
+
const elapsed = performance2.now() - this.lastRenderAt;
|
|
45543
45848
|
const delay = Math.max(0, _TuiBase.MIN_RENDER_INTERVAL_MS - elapsed);
|
|
45544
45849
|
this.renderTimer = setTimeout(() => {
|
|
45545
45850
|
this.renderTimer = void 0;
|
|
@@ -45547,7 +45852,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45547
45852
|
return;
|
|
45548
45853
|
}
|
|
45549
45854
|
this.renderRequested = false;
|
|
45550
|
-
this.lastRenderAt =
|
|
45855
|
+
this.lastRenderAt = performance2.now();
|
|
45551
45856
|
this.doRender();
|
|
45552
45857
|
if (this.renderRequested) {
|
|
45553
45858
|
this.scheduleRender();
|
|
@@ -45555,7 +45860,7 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45555
45860
|
}, delay);
|
|
45556
45861
|
}
|
|
45557
45862
|
handleTerminalInput(data) {
|
|
45558
|
-
if (this.
|
|
45863
|
+
if (this.consumeTerminalColorResponse(data)) {
|
|
45559
45864
|
return;
|
|
45560
45865
|
}
|
|
45561
45866
|
if (this.consumeTerminalColorSchemeReport(data)) {
|
|
@@ -45615,27 +45920,48 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45615
45920
|
this.requestImmediateRender();
|
|
45616
45921
|
}
|
|
45617
45922
|
}
|
|
45618
|
-
|
|
45619
|
-
|
|
45923
|
+
consumeTerminalColorResponse(data) {
|
|
45924
|
+
const query = this.pendingTerminalColorQueries[0];
|
|
45925
|
+
if (!query) {
|
|
45620
45926
|
return false;
|
|
45621
45927
|
}
|
|
45622
|
-
if (
|
|
45928
|
+
if (DEVICE_ATTRIBUTES_RESPONSE_PATTERN.test(data)) {
|
|
45929
|
+
this.pendingTerminalColorQueries.shift();
|
|
45930
|
+
this.completeTerminalColorQuery(query);
|
|
45931
|
+
return true;
|
|
45932
|
+
}
|
|
45933
|
+
const response = parseOscColorResponse(data);
|
|
45934
|
+
if (!response) {
|
|
45623
45935
|
return false;
|
|
45624
45936
|
}
|
|
45625
|
-
const rgb =
|
|
45626
|
-
|
|
45627
|
-
|
|
45628
|
-
|
|
45629
|
-
|
|
45630
|
-
|
|
45631
|
-
|
|
45632
|
-
|
|
45633
|
-
|
|
45634
|
-
query.
|
|
45635
|
-
|
|
45937
|
+
const { target, rgb } = response;
|
|
45938
|
+
const key = String(target);
|
|
45939
|
+
if (!query.deliver || query.replied.has(key)) {
|
|
45940
|
+
return true;
|
|
45941
|
+
}
|
|
45942
|
+
query.replied.add(key);
|
|
45943
|
+
if (target === "foreground") {
|
|
45944
|
+
query.foreground = rgb;
|
|
45945
|
+
} else if (target === "background") {
|
|
45946
|
+
query.background = rgb;
|
|
45947
|
+
} else if (target < TERMINAL_PALETTE_SIZE) {
|
|
45948
|
+
query.palette[target] = rgb;
|
|
45949
|
+
}
|
|
45950
|
+
if (query.replied.size === TERMINAL_COLOR_REPLY_COUNT) {
|
|
45951
|
+
this.completeTerminalColorQuery(query);
|
|
45636
45952
|
}
|
|
45637
45953
|
return true;
|
|
45638
45954
|
}
|
|
45955
|
+
terminalColorQueryResult(query) {
|
|
45956
|
+
const palette = query.palette.every((color) => color !== void 0) ? query.palette : void 0;
|
|
45957
|
+
return { foreground: query.foreground, background: query.background, palette };
|
|
45958
|
+
}
|
|
45959
|
+
completeTerminalColorQuery(query) {
|
|
45960
|
+
const deliver = query.deliver;
|
|
45961
|
+
query.deliver = void 0;
|
|
45962
|
+
clearTimeout(query.timer);
|
|
45963
|
+
deliver?.(this.terminalColorQueryResult(query));
|
|
45964
|
+
}
|
|
45639
45965
|
consumeTerminalColorSchemeReport(data) {
|
|
45640
45966
|
const scheme = parseTerminalColorSchemeReport(data);
|
|
45641
45967
|
if (!scheme) {
|
|
@@ -45844,56 +46170,27 @@ var TuiBase = class _TuiBase extends Container {
|
|
|
45844
46170
|
return null;
|
|
45845
46171
|
}
|
|
45846
46172
|
/**
|
|
45847
|
-
* Query the terminal's
|
|
45848
|
-
*
|
|
45849
|
-
*
|
|
46173
|
+
* Query the terminal's theme colors: the default foreground (OSC 10), the default background
|
|
46174
|
+
* (OSC 11), and ANSI colors 0-15 (OSC 4), followed by a DA1 request that marks the end of the
|
|
46175
|
+
* replies. Resolves when the DA1 reply or all color replies arrive, or when the timeout expires.
|
|
46176
|
+
* Colors the terminal did not report are undefined; the palette is only set when all 16 arrived.
|
|
46177
|
+
* @param timeoutMs Query timeout in milliseconds, for terminals that do not answer DA1 either.
|
|
46178
|
+
* @param onLateReply Receives the replies if the query completes after the timeout, e.g. over slow links.
|
|
45850
46179
|
*/
|
|
45851
|
-
|
|
46180
|
+
queryTerminalColors({ timeoutMs, onLateReply }) {
|
|
45852
46181
|
return new Promise((resolve35) => {
|
|
45853
46182
|
const query = {
|
|
45854
|
-
|
|
45855
|
-
|
|
46183
|
+
palette: Array.from({ length: TERMINAL_PALETTE_SIZE }, () => void 0),
|
|
46184
|
+
replied: /* @__PURE__ */ new Set(),
|
|
46185
|
+
deliver: resolve35,
|
|
45856
46186
|
timer: void 0
|
|
45857
46187
|
};
|
|
45858
46188
|
query.timer = setTimeout(() => {
|
|
45859
|
-
|
|
45860
|
-
|
|
45861
|
-
}
|
|
45862
|
-
query.settled = true;
|
|
45863
|
-
query.timer = void 0;
|
|
45864
|
-
query.resolve?.(void 0);
|
|
45865
|
-
query.resolve = void 0;
|
|
46189
|
+
query.deliver = onLateReply;
|
|
46190
|
+
resolve35(this.terminalColorQueryResult(query));
|
|
45866
46191
|
}, timeoutMs);
|
|
45867
|
-
this.
|
|
45868
|
-
this.
|
|
45869
|
-
this.terminal.write("\x1B]11;?\x07");
|
|
45870
|
-
});
|
|
45871
|
-
}
|
|
45872
|
-
/**
|
|
45873
|
-
* Query the terminal's color-scheme preference with DSR (`CSI ? 996 n`).
|
|
45874
|
-
* Terminals that support the color palette notification protocol reply with
|
|
45875
|
-
* `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
|
|
45876
|
-
*/
|
|
45877
|
-
queryTerminalColorScheme({ timeoutMs }) {
|
|
45878
|
-
return new Promise((resolve35) => {
|
|
45879
|
-
let settled = false;
|
|
45880
|
-
let timer;
|
|
45881
|
-
let unsubscribe = () => {
|
|
45882
|
-
};
|
|
45883
|
-
const settle = (scheme) => {
|
|
45884
|
-
if (settled)
|
|
45885
|
-
return;
|
|
45886
|
-
settled = true;
|
|
45887
|
-
if (timer) {
|
|
45888
|
-
clearTimeout(timer);
|
|
45889
|
-
timer = void 0;
|
|
45890
|
-
}
|
|
45891
|
-
unsubscribe();
|
|
45892
|
-
resolve35(scheme);
|
|
45893
|
-
};
|
|
45894
|
-
unsubscribe = this.onTerminalColorSchemeChange(settle);
|
|
45895
|
-
timer = setTimeout(() => settle(void 0), timeoutMs);
|
|
45896
|
-
this.terminal.write("\x1B[?996n");
|
|
46192
|
+
this.pendingTerminalColorQueries.push(query);
|
|
46193
|
+
this.terminal.write(TERMINAL_COLOR_QUERY);
|
|
45897
46194
|
});
|
|
45898
46195
|
}
|
|
45899
46196
|
};
|
|
@@ -45977,7 +46274,7 @@ var Box = class {
|
|
|
45977
46274
|
const lines = child.render(contentWidth);
|
|
45978
46275
|
mouseChildren.push({ component: child, height: lines.length });
|
|
45979
46276
|
for (const line of lines) {
|
|
45980
|
-
childLines.push(
|
|
46277
|
+
childLines.push(line);
|
|
45981
46278
|
}
|
|
45982
46279
|
}
|
|
45983
46280
|
this.mouseLayout = { width: contentWidth, children: mouseChildren };
|
|
@@ -45993,11 +46290,12 @@ var Box = class {
|
|
|
45993
46290
|
result.push(this.applyBg("", width));
|
|
45994
46291
|
}
|
|
45995
46292
|
for (const line of childLines) {
|
|
45996
|
-
result.push(this.applyBg(line, width));
|
|
46293
|
+
result.push(this.applyBg(leftPad + line, width));
|
|
45997
46294
|
}
|
|
45998
46295
|
for (let i = 0; i < this.paddingY; i++) {
|
|
45999
46296
|
result.push(this.applyBg("", width));
|
|
46000
46297
|
}
|
|
46298
|
+
flattenLines(result);
|
|
46001
46299
|
this.cache = { childLines, width, bgSample, lines: result };
|
|
46002
46300
|
return result;
|
|
46003
46301
|
}
|
|
@@ -46005,10 +46303,7 @@ var Box = class {
|
|
|
46005
46303
|
const visLen = visibleWidth(line);
|
|
46006
46304
|
const padNeeded = Math.max(0, width - visLen);
|
|
46007
46305
|
const padded = line + " ".repeat(padNeeded);
|
|
46008
|
-
|
|
46009
|
-
return applyBackgroundToLine(padded, width, this.bgFn);
|
|
46010
|
-
}
|
|
46011
|
-
return padded;
|
|
46306
|
+
return this.bgFn ? this.bgFn(padded) : padded;
|
|
46012
46307
|
}
|
|
46013
46308
|
};
|
|
46014
46309
|
|
|
@@ -46312,6 +46607,7 @@ var Text = class {
|
|
|
46312
46607
|
emptyLines.push(line);
|
|
46313
46608
|
}
|
|
46314
46609
|
const result = [...emptyLines, ...contentLines, ...emptyLines];
|
|
46610
|
+
flattenLines(result);
|
|
46315
46611
|
this.cachedText = this.text;
|
|
46316
46612
|
this.cachedWidth = width;
|
|
46317
46613
|
this.cachedLines = result;
|
|
@@ -46443,6 +46739,7 @@ function findWordForward(text, cursor2, options) {
|
|
|
46443
46739
|
var graphemeSegmenter2 = getGraphemeSegmenter();
|
|
46444
46740
|
var wordSegmenter3 = getWordSegmenter();
|
|
46445
46741
|
var unquotedAutocompleteSuffixRegex = new RegExp(`(?:(?!${autocompleteSeparatorRegex.source}).)*`, "u");
|
|
46742
|
+
var autocompleteTokenStartSource = `${autocompleteBoundaryRegex.source}[([{<\`]*`;
|
|
46446
46743
|
|
|
46447
46744
|
// node_modules/@earendil-works/pi-tui/dist/layout-node.js
|
|
46448
46745
|
var LAYOUT_NODE = /* @__PURE__ */ Symbol.for("@earendil-works/pi-tui/layout-node");
|
|
@@ -47733,6 +48030,8 @@ var ProcessTerminal = class {
|
|
|
47733
48030
|
_kittyProtocolActive = false;
|
|
47734
48031
|
_modifyOtherKeysActive = false;
|
|
47735
48032
|
keyboardProtocolPushed = false;
|
|
48033
|
+
/** DA1 replies owed to keyboard protocol queries. Later DA1 replies answer other queries and are forwarded. */
|
|
48034
|
+
pendingKeyboardProtocolDeviceAttributes = 0;
|
|
47736
48035
|
keyboardProtocolNegotiationBuffer = "";
|
|
47737
48036
|
keyboardProtocolBufferFlushTimer;
|
|
47738
48037
|
stdinBuffer;
|
|
@@ -47784,15 +48083,15 @@ var ProcessTerminal = class {
|
|
|
47784
48083
|
setupStdinBuffer() {
|
|
47785
48084
|
this.stdinBuffer = new StdinBuffer({ escapeTimeout: resolveEscapeTimeoutMs() });
|
|
47786
48085
|
this.stdinBuffer.on("data", (sequence) => {
|
|
47787
|
-
const
|
|
47788
|
-
if (
|
|
48086
|
+
const negotiation = this.readKeyboardProtocolNegotiationSequence(sequence);
|
|
48087
|
+
if (negotiation === "pending") {
|
|
47789
48088
|
this.scheduleKeyboardProtocolNegotiationBufferFlush();
|
|
47790
48089
|
return;
|
|
47791
48090
|
}
|
|
47792
|
-
if (this.handleKeyboardProtocolNegotiationSequence(
|
|
48091
|
+
if (negotiation && this.handleKeyboardProtocolNegotiationSequence(negotiation.parsed)) {
|
|
47793
48092
|
return;
|
|
47794
48093
|
}
|
|
47795
|
-
this.forwardInputSequence(sequence);
|
|
48094
|
+
this.forwardInputSequence(negotiation?.sequence ?? sequence);
|
|
47796
48095
|
});
|
|
47797
48096
|
this.stdinBuffer.on("paste", (content) => {
|
|
47798
48097
|
if (this.inputHandler) {
|
|
@@ -47820,13 +48119,17 @@ var ProcessTerminal = class {
|
|
|
47820
48119
|
this.setupStdinBuffer();
|
|
47821
48120
|
process.stdin.on("data", this.stdinDataHandler);
|
|
47822
48121
|
this.keyboardProtocolPushed = true;
|
|
48122
|
+
this.pendingKeyboardProtocolDeviceAttributes += 1;
|
|
47823
48123
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
47824
48124
|
process.stdout.write(KITTY_KEYBOARD_PROTOCOL_QUERY);
|
|
47825
48125
|
}
|
|
47826
48126
|
handleKeyboardProtocolNegotiationSequence(negotiationSequence) {
|
|
47827
|
-
if (!negotiationSequence)
|
|
47828
|
-
return false;
|
|
47829
48127
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
48128
|
+
if (negotiationSequence.type === "device-attributes") {
|
|
48129
|
+
if (this.pendingKeyboardProtocolDeviceAttributes === 0)
|
|
48130
|
+
return false;
|
|
48131
|
+
this.pendingKeyboardProtocolDeviceAttributes -= 1;
|
|
48132
|
+
}
|
|
47830
48133
|
if (negotiationSequence.type === "kitty-flags") {
|
|
47831
48134
|
if (negotiationSequence.flags !== 0) {
|
|
47832
48135
|
this.disableModifyOtherKeys();
|
|
@@ -47844,13 +48147,14 @@ var ProcessTerminal = class {
|
|
|
47844
48147
|
}
|
|
47845
48148
|
return true;
|
|
47846
48149
|
}
|
|
48150
|
+
/** Returns the parsed negotiation reply with its full (possibly reassembled) sequence. */
|
|
47847
48151
|
readKeyboardProtocolNegotiationSequence(sequence) {
|
|
47848
48152
|
if (this.keyboardProtocolNegotiationBuffer) {
|
|
47849
48153
|
const bufferedSequence = this.keyboardProtocolNegotiationBuffer + sequence;
|
|
47850
48154
|
const negotiationSequence2 = parseKeyboardProtocolNegotiationSequence(bufferedSequence);
|
|
47851
48155
|
if (negotiationSequence2) {
|
|
47852
48156
|
this.clearKeyboardProtocolNegotiationBuffer();
|
|
47853
|
-
return negotiationSequence2;
|
|
48157
|
+
return { parsed: negotiationSequence2, sequence: bufferedSequence };
|
|
47854
48158
|
}
|
|
47855
48159
|
if (isKeyboardProtocolNegotiationSequencePrefix(bufferedSequence)) {
|
|
47856
48160
|
this.setKeyboardProtocolNegotiationBuffer(bufferedSequence);
|
|
@@ -47860,7 +48164,7 @@ var ProcessTerminal = class {
|
|
|
47860
48164
|
}
|
|
47861
48165
|
const negotiationSequence = parseKeyboardProtocolNegotiationSequence(sequence);
|
|
47862
48166
|
if (negotiationSequence)
|
|
47863
|
-
return negotiationSequence;
|
|
48167
|
+
return { parsed: negotiationSequence, sequence };
|
|
47864
48168
|
if (isKeyboardProtocolNegotiationSequencePrefix(sequence)) {
|
|
47865
48169
|
this.setKeyboardProtocolNegotiationBuffer(sequence);
|
|
47866
48170
|
return "pending";
|
|
@@ -48677,6 +48981,61 @@ function getScrollViewsAt(frame, x2, y2) {
|
|
|
48677
48981
|
return result.map((entry) => entry.scrollView);
|
|
48678
48982
|
}
|
|
48679
48983
|
|
|
48984
|
+
// node_modules/@earendil-works/pi-tui/dist/wheel-scroll.js
|
|
48985
|
+
var BURST_GAP_MS = 5;
|
|
48986
|
+
var GESTURE_GAP_MS = 200;
|
|
48987
|
+
var REFERENCE_GAP_MS = 100;
|
|
48988
|
+
var MAX_AUTO_LINES = 6;
|
|
48989
|
+
function terminalAcceleratesWheel() {
|
|
48990
|
+
const env = process.env;
|
|
48991
|
+
return process.platform === "darwin" && env.SSH_CONNECTION === void 0 && env.SSH_CLIENT === void 0 && env.SSH_TTY === void 0;
|
|
48992
|
+
}
|
|
48993
|
+
var WheelScrollAccelerator = class {
|
|
48994
|
+
lines;
|
|
48995
|
+
accelerate;
|
|
48996
|
+
lastTime = Number.NEGATIVE_INFINITY;
|
|
48997
|
+
lastDirection = 0;
|
|
48998
|
+
averageGap;
|
|
48999
|
+
carry = 0;
|
|
49000
|
+
constructor(lines = "auto", accelerate = !terminalAcceleratesWheel()) {
|
|
49001
|
+
this.lines = lines;
|
|
49002
|
+
this.accelerate = accelerate;
|
|
49003
|
+
}
|
|
49004
|
+
setLines(lines) {
|
|
49005
|
+
this.lines = lines;
|
|
49006
|
+
this.reset();
|
|
49007
|
+
}
|
|
49008
|
+
/** Return the positive line count for a wheel event in `direction` at time `now` (milliseconds). */
|
|
49009
|
+
next(direction, now) {
|
|
49010
|
+
if (this.lines !== "auto")
|
|
49011
|
+
return Number.isFinite(this.lines) ? Math.max(1, Math.floor(this.lines)) : 1;
|
|
49012
|
+
if (!this.accelerate)
|
|
49013
|
+
return 1;
|
|
49014
|
+
const gap = now - this.lastTime;
|
|
49015
|
+
const sameGesture = direction === this.lastDirection && gap <= GESTURE_GAP_MS;
|
|
49016
|
+
this.lastTime = now;
|
|
49017
|
+
this.lastDirection = direction;
|
|
49018
|
+
if (!sameGesture) {
|
|
49019
|
+
this.averageGap = void 0;
|
|
49020
|
+
this.carry = 0;
|
|
49021
|
+
return 1;
|
|
49022
|
+
}
|
|
49023
|
+
if (gap < BURST_GAP_MS)
|
|
49024
|
+
return 1;
|
|
49025
|
+
this.averageGap = this.averageGap === void 0 ? gap : (this.averageGap + gap) / 2;
|
|
49026
|
+
const lines = Math.min(MAX_AUTO_LINES, Math.max(1, REFERENCE_GAP_MS / this.averageGap)) + this.carry;
|
|
49027
|
+
const whole = Math.floor(lines);
|
|
49028
|
+
this.carry = lines - whole;
|
|
49029
|
+
return whole;
|
|
49030
|
+
}
|
|
49031
|
+
reset() {
|
|
49032
|
+
this.lastTime = Number.NEGATIVE_INFINITY;
|
|
49033
|
+
this.lastDirection = 0;
|
|
49034
|
+
this.averageGap = void 0;
|
|
49035
|
+
this.carry = 0;
|
|
49036
|
+
}
|
|
49037
|
+
};
|
|
49038
|
+
|
|
48680
49039
|
// node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js
|
|
48681
49040
|
var ENTER_ALT_SCREEN = "\x1B[?1049h";
|
|
48682
49041
|
var EXIT_ALT_SCREEN = "\x1B[?1049l";
|
|
@@ -48736,7 +49095,7 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
48736
49095
|
mousePressPoint;
|
|
48737
49096
|
mousePressMoved = false;
|
|
48738
49097
|
lastComponentClick;
|
|
48739
|
-
|
|
49098
|
+
wheelScroll;
|
|
48740
49099
|
mouseEnabled;
|
|
48741
49100
|
searchMatchStyle;
|
|
48742
49101
|
searchCurrentMatchStyle;
|
|
@@ -48758,7 +49117,7 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
48758
49117
|
};
|
|
48759
49118
|
this.implicitScrollView = new ScrollView(this.implicitDocument, { follow: "end", primary: true });
|
|
48760
49119
|
this.flashes = new AltScreenFlashContainer(() => this.requestRender());
|
|
48761
|
-
this.
|
|
49120
|
+
this.wheelScroll = new WheelScrollAccelerator(options.wheelScrollLines ?? 1);
|
|
48762
49121
|
this.mouseEnabled = options.mouse ?? true;
|
|
48763
49122
|
this.searchMatchStyle = options.searchMatchStyle ?? ((text) => `\x1B[4m${text}\x1B[24m`);
|
|
48764
49123
|
this.searchCurrentMatchStyle = options.searchCurrentMatchStyle ?? ((text) => `\x1B[1;7m${text}\x1B[22;27m`);
|
|
@@ -48776,6 +49135,9 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
48776
49135
|
get isFollowingOutput() {
|
|
48777
49136
|
return this.getPrimaryScrollView().isFollowingEnd;
|
|
48778
49137
|
}
|
|
49138
|
+
setWheelScrollLines(lines) {
|
|
49139
|
+
this.wheelScroll.setLines(lines);
|
|
49140
|
+
}
|
|
48779
49141
|
getCopyOnSelect() {
|
|
48780
49142
|
return this.copyOnSelect;
|
|
48781
49143
|
}
|
|
@@ -48793,6 +49155,10 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
48793
49155
|
return false;
|
|
48794
49156
|
return this.copyTextToClipboard(text);
|
|
48795
49157
|
}
|
|
49158
|
+
/** The lines of the last rendered frame, one per terminal row, as written to the terminal. */
|
|
49159
|
+
getScreenLines() {
|
|
49160
|
+
return [...this.previousScreen];
|
|
49161
|
+
}
|
|
48796
49162
|
setLayoutRoot(component) {
|
|
48797
49163
|
if (this.layoutRoot === component)
|
|
48798
49164
|
return;
|
|
@@ -49135,9 +49501,9 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
49135
49501
|
return { consume: true };
|
|
49136
49502
|
const wheelEvent = this.parseWheelEvent(data);
|
|
49137
49503
|
if (wheelEvent) {
|
|
49138
|
-
const
|
|
49139
|
-
|
|
49140
|
-
});
|
|
49504
|
+
const lines = this.wheelScroll.next(wheelEvent.direction, performance.now());
|
|
49505
|
+
const wheelDelta = wheelEvent.direction * ((wheelEvent.button & 8) !== 0 ? lines * ALT_WHEEL_SCROLL_MULTIPLIER : lines);
|
|
49506
|
+
const event = this.createMouseEvent("wheel", wheelEvent.button, wheelEvent.x, wheelEvent.y, { wheelDelta });
|
|
49141
49507
|
const overlay = this.dispatchMouseToOverlay(event);
|
|
49142
49508
|
const result = overlay.result ?? (overlay.hit ? void 0 : this.dispatchMouseToLayout(event));
|
|
49143
49509
|
if (result) {
|
|
@@ -49147,7 +49513,7 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
49147
49513
|
}
|
|
49148
49514
|
if (this.shouldDeferViewportInputToOverlay())
|
|
49149
49515
|
return void 0;
|
|
49150
|
-
this.routeWheel(wheelEvent);
|
|
49516
|
+
this.routeWheel(wheelEvent, wheelDelta);
|
|
49151
49517
|
return { consume: true };
|
|
49152
49518
|
}
|
|
49153
49519
|
const mouseEvent = this.parseSgrMouseEvent(data);
|
|
@@ -49410,11 +49776,8 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
49410
49776
|
}
|
|
49411
49777
|
return void 0;
|
|
49412
49778
|
}
|
|
49413
|
-
|
|
49414
|
-
|
|
49415
|
-
}
|
|
49416
|
-
routeWheel(event) {
|
|
49417
|
-
let remaining = event.direction * this.getWheelScrollLines(event.button);
|
|
49779
|
+
routeWheel(event, delta) {
|
|
49780
|
+
let remaining = delta;
|
|
49418
49781
|
const seen = /* @__PURE__ */ new Set();
|
|
49419
49782
|
for (const scrollView of this.currentLayout ? getScrollViewsAt(this.currentLayout, event.x, event.y) : []) {
|
|
49420
49783
|
seen.add(scrollView);
|
|
@@ -50035,7 +50398,20 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
50035
50398
|
return sliceByColumn(line, 0, width, true);
|
|
50036
50399
|
});
|
|
50037
50400
|
const fullRedraw = this.previousScreen.length === 0 || this.previousScreenWidth !== width || this.previousScreenHeight !== height;
|
|
50038
|
-
const
|
|
50401
|
+
const changedRows = screen.map((line, row) => line !== this.previousScreen[row]);
|
|
50402
|
+
const imageAnchorsNeedRedraw = screen.some((line, row) => changedRows[row] && (isImageLine(line) || isImageLine(this.previousScreen[row] ?? "")));
|
|
50403
|
+
const isWezTerm = Boolean(process.env.WEZTERM_PANE) || process.env.TERM_PROGRAM?.toLowerCase() === "wezterm";
|
|
50404
|
+
const imageCellsNeedRedraw = !imageAnchorsNeedRedraw && isWezTerm && this.imageProtocol === "kitty" && changedRows.some(Boolean) && screen.some((line, row) => {
|
|
50405
|
+
const placementRows = getKittyImagePlacementRows(line);
|
|
50406
|
+
if (placementRows === void 0)
|
|
50407
|
+
return false;
|
|
50408
|
+
for (let coveredRow = row; coveredRow < row + placementRows; coveredRow++) {
|
|
50409
|
+
if (changedRows[coveredRow])
|
|
50410
|
+
return true;
|
|
50411
|
+
}
|
|
50412
|
+
return false;
|
|
50413
|
+
});
|
|
50414
|
+
const imagesNeedRedraw = imageAnchorsNeedRedraw || imageCellsNeedRedraw;
|
|
50039
50415
|
const redrawImages = fullRedraw || imagesNeedRedraw;
|
|
50040
50416
|
const hadUploadedKittyImages = this.uploadedKittyImages.size > 0;
|
|
50041
50417
|
const preparedKittyScreen = redrawImages && this.imageProtocol === "kitty" ? this.prepareKittyScreen(screen) : { lines: screen, evictedImageDeletion: "" };
|
|
@@ -50051,18 +50427,33 @@ var TuiAltScreen = class extends TuiBase {
|
|
|
50051
50427
|
buffer += deleteAllKittyPlacements();
|
|
50052
50428
|
}
|
|
50053
50429
|
buffer += preparedKittyScreen.evictedImageDeletion;
|
|
50054
|
-
const
|
|
50055
|
-
if (
|
|
50430
|
+
const drawKittyImagesLast = redrawImages && this.imageProtocol === "kitty" && screen.some(isImageLine) && isWezTerm;
|
|
50431
|
+
if (drawKittyImagesLast) {
|
|
50056
50432
|
for (let row = 0; row < height; row++) {
|
|
50057
50433
|
if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
|
|
50058
50434
|
continue;
|
|
50059
50435
|
buffer += `\x1B[${row + 1};1H\x1B[2K`;
|
|
50060
50436
|
}
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
|
|
50065
|
-
|
|
50437
|
+
for (let row = 0; row < height; row++) {
|
|
50438
|
+
if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
|
|
50439
|
+
continue;
|
|
50440
|
+
if (isImageLine(preparedKittyScreen.lines[row] ?? ""))
|
|
50441
|
+
continue;
|
|
50442
|
+
buffer += `\x1B[${row + 1};1H${preparedKittyScreen.lines[row] ?? ""}`;
|
|
50443
|
+
}
|
|
50444
|
+
for (let row = 0; row < height; row++) {
|
|
50445
|
+
if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
|
|
50446
|
+
continue;
|
|
50447
|
+
if (!isImageLine(preparedKittyScreen.lines[row] ?? ""))
|
|
50448
|
+
continue;
|
|
50449
|
+
buffer += `\x1B[${row + 1};1H${preparedKittyScreen.lines[row] ?? ""}`;
|
|
50450
|
+
}
|
|
50451
|
+
} else {
|
|
50452
|
+
for (let row = 0; row < height; row++) {
|
|
50453
|
+
if (!fullRedraw && !imagesNeedRedraw && screen[row] === this.previousScreen[row])
|
|
50454
|
+
continue;
|
|
50455
|
+
buffer += `\x1B[${row + 1};1H\x1B[2K${preparedKittyScreen.lines[row] ?? ""}`;
|
|
50456
|
+
}
|
|
50066
50457
|
}
|
|
50067
50458
|
if (cursorPos) {
|
|
50068
50459
|
buffer += `\x1B[${cursorPos.row + 1};${Math.min(width, cursorPos.col) + 1}H`;
|
|
@@ -50088,6 +50479,13 @@ import { join as join46, resolve as resolve27, dirname as dirname20 } from "node
|
|
|
50088
50479
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
50089
50480
|
|
|
50090
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";
|
|
50091
50489
|
var KXM_TUI_LIMITS = Object.freeze({
|
|
50092
50490
|
sourceMax: 128,
|
|
50093
50491
|
idMax: 128,
|
|
@@ -50109,8 +50507,231 @@ var KXM_TUI_LIMITS = Object.freeze({
|
|
|
50109
50507
|
sectionsMax: 32,
|
|
50110
50508
|
orderMax: 1e3
|
|
50111
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
|
+
}
|
|
50112
50731
|
|
|
50113
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;
|
|
50114
50735
|
var NAMED_KEYS = Object.freeze({
|
|
50115
50736
|
up: "up",
|
|
50116
50737
|
down: "down",
|
|
@@ -50128,8 +50749,109 @@ var NAMED_KEYS = Object.freeze({
|
|
|
50128
50749
|
backspace: "backspace",
|
|
50129
50750
|
delete: "delete"
|
|
50130
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
|
+
}
|
|
50131
50791
|
|
|
50132
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
|
+
}
|
|
50133
50855
|
var KXM_TUI_MARKS = Object.freeze({
|
|
50134
50856
|
pending: "\u25CB",
|
|
50135
50857
|
active: "\u25B6",
|
|
@@ -50194,9 +50916,368 @@ function createKxmTuiAnsiTheme(color) {
|
|
|
50194
50916
|
panelBg: (text) => `\x1B[48;5;236m${text}\x1B[0m`
|
|
50195
50917
|
};
|
|
50196
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
|
+
}
|
|
50197
51206
|
|
|
50198
51207
|
// packages/core/tui/src/tui/render.ts
|
|
50199
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
|
+
}
|
|
50200
51281
|
var STEP_MARKS = {
|
|
50201
51282
|
pending: "\xB7",
|
|
50202
51283
|
satisfied: KXM_TUI_MARKS.passed,
|
|
@@ -50204,12 +51285,494 @@ var STEP_MARKS = {
|
|
|
50204
51285
|
done: KXM_TUI_MARKS.passed,
|
|
50205
51286
|
failed: KXM_TUI_MARKS.failed
|
|
50206
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
|
+
}
|
|
50207
51409
|
|
|
50208
51410
|
// packages/core/tui/src/tui/panelComponent.ts
|
|
50209
51411
|
var KXM_TUI_PANEL_HELP = Object.freeze([
|
|
50210
51412
|
"\u2191\u2193 move \xB7 \u2190\u2192 or Tab panes \xB7 PgUp/PgDn section \xB7 enter edit or open list",
|
|
50211
51413
|
"type to filter a list \xB7 x clears a value \xB7 esc backs out \xB7 q quit \xB7 h help"
|
|
50212
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
|
+
}
|
|
50213
51776
|
|
|
50214
51777
|
// plugins/kxm/src/external-effects.ts
|
|
50215
51778
|
init_database();
|
|
@@ -52885,7 +54448,7 @@ init_routing();
|
|
|
52885
54448
|
init_prices();
|
|
52886
54449
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
52887
54450
|
import { createHash as createHash19 } from "node:crypto";
|
|
52888
|
-
import { existsSync as
|
|
54451
|
+
import { existsSync as existsSync49, mkdtempSync as mkdtempSync3, readFileSync as readFileSync43, rmSync as rmSync13 } from "node:fs";
|
|
52889
54452
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
52890
54453
|
import { basename as basename11, extname as extname3, join as join57, resolve as resolve33 } from "node:path";
|
|
52891
54454
|
import { createInterface as createInterface3 } from "node:readline";
|
|
@@ -54130,6 +55693,304 @@ function executeSshRun(params) {
|
|
|
54130
55693
|
// plugins/kxm/src/cli/system.ts
|
|
54131
55694
|
init_config();
|
|
54132
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
|
+
|
|
54133
55994
|
// plugins/kxm/src/autocomplete.ts
|
|
54134
55995
|
var TOP_LEVEL_COMMANDS = [
|
|
54135
55996
|
"init",
|
|
@@ -54183,7 +56044,7 @@ var SUBCOMMANDS = {
|
|
|
54183
56044
|
memory: ["brief", "note", "sync"],
|
|
54184
56045
|
routing: ["report", "benchmark"],
|
|
54185
56046
|
hub: ["view", "start", "stop", "bind", "unbind"],
|
|
54186
|
-
config: ["get", "set", "list"],
|
|
56047
|
+
config: ["get", "set", "edit", "list"],
|
|
54187
56048
|
goal: ["create", "list", "get"],
|
|
54188
56049
|
task: ["create", "list", "get", "run", "sync"],
|
|
54189
56050
|
studio: ["layout", "serve"],
|
|
@@ -54305,7 +56166,7 @@ _kxm() {
|
|
|
54305
56166
|
_arguments '1:shell:(bash zsh fish)'
|
|
54306
56167
|
;;
|
|
54307
56168
|
config)
|
|
54308
|
-
_arguments '1:action:(get set list)'
|
|
56169
|
+
_arguments '1:action:(get set edit list)'
|
|
54309
56170
|
;;
|
|
54310
56171
|
runs)
|
|
54311
56172
|
_arguments '1:subcommand:(status cancel list)'
|
|
@@ -54349,7 +56210,7 @@ complete -c kxm -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
|
54349
56210
|
complete -c kxm -n "__fish_seen_subcommand_from dash" -l screen -a "agents tasks workflows plans inbox procs spend"
|
|
54350
56211
|
|
|
54351
56212
|
# Config subcommand
|
|
54352
|
-
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"
|
|
54353
56214
|
|
|
54354
56215
|
# Runs subcommand
|
|
54355
56216
|
complete -c kxm -n "__fish_seen_subcommand_from runs" -a "status cancel list"
|
|
@@ -54369,7 +56230,7 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
54369
56230
|
}
|
|
54370
56231
|
|
|
54371
56232
|
// plugins/kxm/src/completion-install.ts
|
|
54372
|
-
import { existsSync as
|
|
56233
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync33, readFileSync as readFileSync42, writeFileSync as writeFileSync28 } from "node:fs";
|
|
54373
56234
|
import { homedir as homedir9 } from "node:os";
|
|
54374
56235
|
import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute11, join as join55, resolve as resolve32 } from "node:path";
|
|
54375
56236
|
var COMPLETION_MARKER = "# kxm completion";
|
|
@@ -54398,7 +56259,7 @@ function completionScriptPath(shell, options = {}) {
|
|
|
54398
56259
|
function bashRcCandidate(options) {
|
|
54399
56260
|
const home = effectiveHome(options);
|
|
54400
56261
|
const candidates = [join55(home, ".bashrc"), join55(home, ".bash_profile")];
|
|
54401
|
-
const existing = candidates.find((candidate) =>
|
|
56262
|
+
const existing = candidates.find((candidate) => existsSync47(candidate));
|
|
54402
56263
|
return existing ?? candidates[0];
|
|
54403
56264
|
}
|
|
54404
56265
|
function zshRcCandidate(options) {
|
|
@@ -54443,13 +56304,13 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
54443
56304
|
const scriptPath = completionScriptPath(shell, options);
|
|
54444
56305
|
const { rcFile } = completionRcTarget(shell, options);
|
|
54445
56306
|
const script = generateShellCompletion(shell);
|
|
54446
|
-
const existingScript =
|
|
54447
|
-
const existingScriptMatches = existingScript &&
|
|
56307
|
+
const existingScript = existsSync47(scriptPath);
|
|
56308
|
+
const existingScriptMatches = existingScript && readFileSync42(scriptPath, "utf8") === script;
|
|
54448
56309
|
let rcModified = false;
|
|
54449
56310
|
let alreadyInstalled = false;
|
|
54450
56311
|
if (shell === "fish") {
|
|
54451
56312
|
const fishTarget = fishCompletionTarget(shell, options);
|
|
54452
|
-
const fishInstalled =
|
|
56313
|
+
const fishInstalled = existsSync47(fishTarget) && readFileSync42(fishTarget, "utf8") === script;
|
|
54453
56314
|
alreadyInstalled = fishInstalled;
|
|
54454
56315
|
if (!options.dryRun && (!fishInstalled || options.overwrite)) {
|
|
54455
56316
|
mkdirSync33(resolve32(fishTarget, ".."), { recursive: true });
|
|
@@ -54466,7 +56327,7 @@ function installShellCompletion(shellInput, options = {}) {
|
|
|
54466
56327
|
}
|
|
54467
56328
|
if (rcFile) {
|
|
54468
56329
|
const line = sourceLine(shell, scriptPath);
|
|
54469
|
-
const rcContent =
|
|
56330
|
+
const rcContent = existsSync47(rcFile) ? readFileSync42(rcFile, "utf8") : "";
|
|
54470
56331
|
alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
|
|
54471
56332
|
}
|
|
54472
56333
|
if (!options.dryRun) {
|
|
@@ -54482,7 +56343,7 @@ ${marker}
|
|
|
54482
56343
|
${line}
|
|
54483
56344
|
`;
|
|
54484
56345
|
mkdirSync33(resolve32(rcFile, ".."), { recursive: true });
|
|
54485
|
-
writeFileSync28(rcFile, (
|
|
56346
|
+
writeFileSync28(rcFile, (existsSync47(rcFile) ? readFileSync42(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
|
|
54486
56347
|
rcModified = true;
|
|
54487
56348
|
}
|
|
54488
56349
|
}
|
|
@@ -54499,13 +56360,13 @@ function kxmBinDir(env = process.env) {
|
|
|
54499
56360
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
54500
56361
|
if (argv1 && isAbsolute11(argv1)) {
|
|
54501
56362
|
const dir = dirname24(argv1);
|
|
54502
|
-
if (
|
|
56363
|
+
if (existsSync47(join55(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
54503
56364
|
}
|
|
54504
56365
|
const pathEnv = env.PATH ?? "";
|
|
54505
56366
|
for (const part of pathEnv.split(delimiter)) {
|
|
54506
56367
|
if (!part) continue;
|
|
54507
56368
|
const candidate = resolve32(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
|
|
54508
|
-
if (
|
|
56369
|
+
if (existsSync47(candidate)) return resolve32(part);
|
|
54509
56370
|
}
|
|
54510
56371
|
return void 0;
|
|
54511
56372
|
}
|
|
@@ -54523,7 +56384,7 @@ function installPathEntry(shellInput, options = {}) {
|
|
|
54523
56384
|
}
|
|
54524
56385
|
const { rcFile } = completionRcTarget(shell, options);
|
|
54525
56386
|
const line = pathLine(binDir);
|
|
54526
|
-
const rcContent =
|
|
56387
|
+
const rcContent = existsSync47(rcFile ?? "") ? readFileSync42(rcFile, "utf8") : "";
|
|
54527
56388
|
const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
|
|
54528
56389
|
if (!options.dryRun && rcFile && !alreadyInstalled) {
|
|
54529
56390
|
mkdirSync33(dirname24(rcFile), { recursive: true });
|
|
@@ -54536,9 +56397,9 @@ ${line}
|
|
|
54536
56397
|
}
|
|
54537
56398
|
|
|
54538
56399
|
// plugins/kxm/src/init-guide-setup.ts
|
|
54539
|
-
var
|
|
56400
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
54540
56401
|
init_routes();
|
|
54541
|
-
import { existsSync as
|
|
56402
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync34, writeFileSync as writeFileSync29 } from "node:fs";
|
|
54542
56403
|
import { dirname as dirname25, join as join56 } from "node:path";
|
|
54543
56404
|
var ADMITTED_GUIDE_BINDINGS = Object.freeze({
|
|
54544
56405
|
"anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
@@ -54971,7 +56832,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
54971
56832
|
if (binding.harness === "pi" && !piMatch) {
|
|
54972
56833
|
files.push({
|
|
54973
56834
|
path: join56(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
54974
|
-
content: (0,
|
|
56835
|
+
content: (0, import_yaml22.stringify)(agentDocument(role, stage))
|
|
54975
56836
|
});
|
|
54976
56837
|
continue;
|
|
54977
56838
|
}
|
|
@@ -54996,15 +56857,15 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
54996
56857
|
}
|
|
54997
56858
|
files.push({
|
|
54998
56859
|
path: join56(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
54999
|
-
content: (0,
|
|
56860
|
+
content: (0, import_yaml22.stringify)(agentDocument(role, stage))
|
|
55000
56861
|
});
|
|
55001
56862
|
files.push({
|
|
55002
56863
|
path: join56(projectRoot, ".kxm", "models", `${routeId2}.yaml`),
|
|
55003
|
-
content: (0,
|
|
56864
|
+
content: (0, import_yaml22.stringify)(modelDocument)
|
|
55004
56865
|
});
|
|
55005
56866
|
files.push({
|
|
55006
56867
|
path: join56(projectRoot, ".kxm", "roles", `${role}.yaml`),
|
|
55007
|
-
content: (0,
|
|
56868
|
+
content: (0, import_yaml22.stringify)({
|
|
55008
56869
|
schema: "kxm.role.v2",
|
|
55009
56870
|
id: role,
|
|
55010
56871
|
purpose: writer ? "writer" : "experiment",
|
|
@@ -55017,7 +56878,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
55017
56878
|
for (const workflow of plan.workflows) {
|
|
55018
56879
|
files.push({
|
|
55019
56880
|
path: join56(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
55020
|
-
content: (0,
|
|
56881
|
+
content: (0, import_yaml22.stringify)(workflowDocument(workflow))
|
|
55021
56882
|
});
|
|
55022
56883
|
}
|
|
55023
56884
|
return files;
|
|
@@ -55035,14 +56896,14 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
|
|
|
55035
56896
|
policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
55036
56897
|
const path4 = join56(projectRoot, ".kxm", "routes.yaml");
|
|
55037
56898
|
mkdirSync34(dirname25(path4), { recursive: true });
|
|
55038
|
-
writeFileSync29(path4, (0,
|
|
56899
|
+
writeFileSync29(path4, (0, import_yaml22.stringify)(policy), "utf8");
|
|
55039
56900
|
return added;
|
|
55040
56901
|
}
|
|
55041
56902
|
function writeGuideSetupFiles(files) {
|
|
55042
56903
|
const written = [];
|
|
55043
56904
|
const existed = [];
|
|
55044
56905
|
for (const file of files) {
|
|
55045
|
-
if (
|
|
56906
|
+
if (existsSync48(file.path)) {
|
|
55046
56907
|
existed.push(file.path);
|
|
55047
56908
|
continue;
|
|
55048
56909
|
}
|
|
@@ -55123,7 +56984,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
55123
56984
|
for (const step of planned) {
|
|
55124
56985
|
if (step.kind === "verify") {
|
|
55125
56986
|
if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
|
|
55126
|
-
const actual =
|
|
56987
|
+
const actual = existsSync49(step.path) ? createHash19("sha256").update(readFileSync43(step.path)).digest("hex") : "missing";
|
|
55127
56988
|
return {
|
|
55128
56989
|
ok: false,
|
|
55129
56990
|
error: "release_digest_mismatch",
|
|
@@ -55451,12 +57312,12 @@ async function cmdValidate(runtime, fileFlag) {
|
|
|
55451
57312
|
}
|
|
55452
57313
|
const selectedFile = explicitFile || configuredFile;
|
|
55453
57314
|
const file = selectedFile ? resolve33(runtime.cwd, selectedFile) : void 0;
|
|
55454
|
-
if (file && !
|
|
57315
|
+
if (file && !existsSync49(file)) {
|
|
55455
57316
|
printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
|
|
55456
57317
|
return 1;
|
|
55457
57318
|
}
|
|
55458
57319
|
try {
|
|
55459
|
-
const raw = file ?
|
|
57320
|
+
const raw = file ? readFileSync43(file, "utf8") : inline;
|
|
55460
57321
|
if (explicitFile && file && !raw.trimStart().startsWith("[")) {
|
|
55461
57322
|
const value = parseRestrictedYaml2(raw, file);
|
|
55462
57323
|
const issues = new KxmSchemaRegistry().validate("workflow", value, file);
|
|
@@ -55631,6 +57492,37 @@ async function cmdConfigSet(runtime, key, value, options) {
|
|
|
55631
57492
|
return 1;
|
|
55632
57493
|
}
|
|
55633
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
|
+
}
|
|
55634
57526
|
async function cmdConfigList(runtime) {
|
|
55635
57527
|
try {
|
|
55636
57528
|
const config = loadKxmConfig(runtime.cwd);
|
|
@@ -55705,9 +57597,9 @@ async function maybeOfferCompletionInstall(runtime) {
|
|
|
55705
57597
|
if (!pathNeeded) {
|
|
55706
57598
|
const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
|
|
55707
57599
|
const { rcFile } = completionRcTarget(shell, { env: runtime.env });
|
|
55708
|
-
if (rcFile &&
|
|
57600
|
+
if (rcFile && existsSync49(rcFile)) {
|
|
55709
57601
|
try {
|
|
55710
|
-
if (
|
|
57602
|
+
if (readFileSync43(rcFile, "utf8").includes(scriptPath)) return;
|
|
55711
57603
|
} catch {
|
|
55712
57604
|
}
|
|
55713
57605
|
}
|
|
@@ -56082,6 +57974,7 @@ var DRY_RUN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
56082
57974
|
"hub unbind",
|
|
56083
57975
|
"config get",
|
|
56084
57976
|
"config set",
|
|
57977
|
+
"config edit",
|
|
56085
57978
|
"config list",
|
|
56086
57979
|
"goal create",
|
|
56087
57980
|
"goal list",
|
|
@@ -56654,7 +58547,7 @@ function createProgram(ctx, result, argv) {
|
|
|
56654
58547
|
});
|
|
56655
58548
|
const gate = addGlobalOptions(program2.command("gate").description("Validate definitions and operate evidence gates"));
|
|
56656
58549
|
gate.helpCommand("help", "Show gate help");
|
|
56657
|
-
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) {
|
|
56658
58551
|
result.code = await cmdValidate(runtimeFrom(ctx, this), options.file);
|
|
56659
58552
|
});
|
|
56660
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) {
|
|
@@ -56782,6 +58675,9 @@ function createProgram(ctx, result, argv) {
|
|
|
56782
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) {
|
|
56783
58676
|
result.code = await cmdConfigSet(runtimeFrom(ctx, this), key, value, options);
|
|
56784
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
|
+
});
|
|
56785
58681
|
addGlobalOptions(configCmd.command("list", { isDefault: true }).description("List resolved configuration values")).action(async function configListAction() {
|
|
56786
58682
|
result.code = await cmdConfigList(runtimeFrom(ctx, this));
|
|
56787
58683
|
});
|