terminal-pilot 0.0.16 → 0.0.17
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/dist/cli.js +2324 -677
- package/dist/cli.js.map +4 -4
- package/dist/commands/close-session.js.map +2 -2
- package/dist/commands/create-session.js.map +2 -2
- package/dist/commands/fill.js.map +2 -2
- package/dist/commands/get-session.js.map +2 -2
- package/dist/commands/index.js.map +2 -2
- package/dist/commands/install.js.map +2 -2
- package/dist/commands/list-sessions.js.map +2 -2
- package/dist/commands/press-key.js.map +2 -2
- package/dist/commands/read-history.js.map +2 -2
- package/dist/commands/read-screen.js.map +2 -2
- package/dist/commands/resize.js.map +2 -2
- package/dist/commands/screenshot.js.map +2 -2
- package/dist/commands/send-signal.js.map +2 -2
- package/dist/commands/type.js.map +2 -2
- package/dist/commands/uninstall.js.map +2 -2
- package/dist/commands/wait-for-exit.js.map +2 -2
- package/dist/commands/wait-for.js.map +2 -2
- package/dist/testing/cli-repl.js +2325 -677
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +2334 -686
- package/dist/testing/qa-cli.js.map +4 -4
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -1,65 +1,68 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
2
7
|
|
|
3
8
|
// src/cli.ts
|
|
4
9
|
import { realpath } from "node:fs/promises";
|
|
5
|
-
import
|
|
10
|
+
import path14 from "node:path";
|
|
6
11
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
7
12
|
|
|
8
13
|
// ../toolcraft/src/cli.ts
|
|
9
|
-
import { access as access2, readFile as
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
14
|
+
import { access as access2, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
15
|
+
import path9 from "node:path";
|
|
16
|
+
import {
|
|
17
|
+
Command as CommanderCommand,
|
|
18
|
+
CommanderError,
|
|
19
|
+
InvalidArgumentError,
|
|
20
|
+
Option
|
|
21
|
+
} from "commander";
|
|
12
22
|
|
|
13
23
|
// ../design-system/src/tokens/colors.ts
|
|
14
24
|
import chalk from "chalk";
|
|
15
25
|
var dark = {
|
|
16
|
-
header: (
|
|
17
|
-
divider: (
|
|
18
|
-
prompt: (
|
|
19
|
-
number: (
|
|
20
|
-
intro: (
|
|
26
|
+
header: (text5) => chalk.magentaBright.bold(text5),
|
|
27
|
+
divider: (text5) => chalk.dim(text5),
|
|
28
|
+
prompt: (text5) => chalk.cyan(text5),
|
|
29
|
+
number: (text5) => chalk.cyanBright(text5),
|
|
30
|
+
intro: (text5) => chalk.bgMagenta.white(` Poe - ${text5} `),
|
|
21
31
|
resolvedSymbol: chalk.magenta("\u25C7"),
|
|
22
32
|
errorSymbol: chalk.red("\u25A0"),
|
|
23
|
-
accent: (
|
|
24
|
-
muted: (
|
|
25
|
-
success: (
|
|
26
|
-
warning: (
|
|
27
|
-
error: (
|
|
28
|
-
info: (
|
|
29
|
-
badge: (
|
|
33
|
+
accent: (text5) => chalk.cyan(text5),
|
|
34
|
+
muted: (text5) => chalk.dim(text5),
|
|
35
|
+
success: (text5) => chalk.green(text5),
|
|
36
|
+
warning: (text5) => chalk.yellow(text5),
|
|
37
|
+
error: (text5) => chalk.red(text5),
|
|
38
|
+
info: (text5) => chalk.magenta(text5),
|
|
39
|
+
badge: (text5) => chalk.bgYellow.black(` ${text5} `)
|
|
30
40
|
};
|
|
31
41
|
var light = {
|
|
32
|
-
header: (
|
|
33
|
-
divider: (
|
|
34
|
-
prompt: (
|
|
35
|
-
number: (
|
|
36
|
-
intro: (
|
|
42
|
+
header: (text5) => chalk.hex("#a200ff").bold(text5),
|
|
43
|
+
divider: (text5) => chalk.hex("#666666")(text5),
|
|
44
|
+
prompt: (text5) => chalk.hex("#006699").bold(text5),
|
|
45
|
+
number: (text5) => chalk.hex("#0077cc").bold(text5),
|
|
46
|
+
intro: (text5) => chalk.bgHex("#a200ff").white(` Poe - ${text5} `),
|
|
37
47
|
resolvedSymbol: chalk.hex("#a200ff")("\u25C7"),
|
|
38
48
|
errorSymbol: chalk.hex("#cc0000")("\u25A0"),
|
|
39
|
-
accent: (
|
|
40
|
-
muted: (
|
|
41
|
-
success: (
|
|
42
|
-
warning: (
|
|
43
|
-
error: (
|
|
44
|
-
info: (
|
|
45
|
-
badge: (
|
|
49
|
+
accent: (text5) => chalk.hex("#006699").bold(text5),
|
|
50
|
+
muted: (text5) => chalk.hex("#666666")(text5),
|
|
51
|
+
success: (text5) => chalk.hex("#008800")(text5),
|
|
52
|
+
warning: (text5) => chalk.hex("#cc6600")(text5),
|
|
53
|
+
error: (text5) => chalk.hex("#cc0000")(text5),
|
|
54
|
+
info: (text5) => chalk.hex("#a200ff")(text5),
|
|
55
|
+
badge: (text5) => chalk.bgHex("#cc6600").white(` ${text5} `)
|
|
46
56
|
};
|
|
47
57
|
|
|
48
58
|
// ../design-system/src/tokens/typography.ts
|
|
49
59
|
import chalk2 from "chalk";
|
|
50
60
|
var typography = {
|
|
51
|
-
bold: (
|
|
52
|
-
dim: (
|
|
53
|
-
italic: (
|
|
54
|
-
underline: (
|
|
55
|
-
strikethrough: (
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// ../design-system/src/tokens/widths.ts
|
|
59
|
-
var widths = {
|
|
60
|
-
header: 60,
|
|
61
|
-
helpColumn: 24,
|
|
62
|
-
maxLine: 80
|
|
61
|
+
bold: (text5) => chalk2.bold(text5),
|
|
62
|
+
dim: (text5) => chalk2.dim(text5),
|
|
63
|
+
italic: (text5) => chalk2.italic(text5),
|
|
64
|
+
underline: (text5) => chalk2.underline(text5),
|
|
65
|
+
strikethrough: (text5) => chalk2.strikethrough(text5)
|
|
63
66
|
};
|
|
64
67
|
|
|
65
68
|
// ../design-system/src/components/text.ts
|
|
@@ -153,6 +156,12 @@ var text = {
|
|
|
153
156
|
if (format === "markdown") return `**${content}**`;
|
|
154
157
|
return typography.bold(content);
|
|
155
158
|
},
|
|
159
|
+
sectionHeader(content) {
|
|
160
|
+
const format = resolveOutputFormat();
|
|
161
|
+
if (format === "json") return content;
|
|
162
|
+
if (format === "markdown") return `## ${content}`;
|
|
163
|
+
return typography.bold(content.toUpperCase());
|
|
164
|
+
},
|
|
156
165
|
command(content) {
|
|
157
166
|
const format = resolveOutputFormat();
|
|
158
167
|
if (format === "json") return content;
|
|
@@ -465,19 +474,248 @@ function createLogger(emitter) {
|
|
|
465
474
|
var logger = createLogger();
|
|
466
475
|
|
|
467
476
|
// ../design-system/src/components/help-formatter.ts
|
|
468
|
-
function
|
|
469
|
-
|
|
470
|
-
|
|
477
|
+
function stripAnsi2(value) {
|
|
478
|
+
let output = "";
|
|
479
|
+
for (let index = 0; index < value.length; index += 1)
|
|
480
|
+
if (value[index] === "\x1B" && value[index + 1] === "[")
|
|
481
|
+
while (index < value.length && value[index] !== "m") index += 1;
|
|
482
|
+
else output += value[index];
|
|
483
|
+
return output;
|
|
484
|
+
}
|
|
485
|
+
function visibleWidth(value) {
|
|
486
|
+
return stripAnsi2(value).length;
|
|
487
|
+
}
|
|
488
|
+
function clamp(value, min, max) {
|
|
489
|
+
return Math.min(Math.max(value, min), max);
|
|
490
|
+
}
|
|
491
|
+
function padEndVisible(value, width) {
|
|
492
|
+
return value + " ".repeat(Math.max(0, width - visibleWidth(value)));
|
|
493
|
+
}
|
|
494
|
+
function isWhitespace(char) {
|
|
495
|
+
return char === " " || char === "\n" || char === " " || char === "\r";
|
|
496
|
+
}
|
|
497
|
+
function splitWords(value) {
|
|
498
|
+
const words = [];
|
|
499
|
+
let word = "";
|
|
500
|
+
for (const char of value) {
|
|
501
|
+
if (isWhitespace(char)) {
|
|
502
|
+
if (word) {
|
|
503
|
+
words.push(word);
|
|
504
|
+
word = "";
|
|
505
|
+
}
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
word += char;
|
|
509
|
+
}
|
|
510
|
+
if (word) {
|
|
511
|
+
words.push(word);
|
|
512
|
+
}
|
|
513
|
+
return words;
|
|
514
|
+
}
|
|
515
|
+
function wrapWords(value, width) {
|
|
516
|
+
const words = splitWords(value);
|
|
517
|
+
if (words.length === 0) {
|
|
518
|
+
return [""];
|
|
519
|
+
}
|
|
520
|
+
const lines = [];
|
|
521
|
+
let line = "";
|
|
522
|
+
for (const word of words) {
|
|
523
|
+
if (!line) {
|
|
524
|
+
line = word;
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
if (visibleWidth(line) + 1 + visibleWidth(word) <= width) {
|
|
528
|
+
line += ` ${word}`;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
lines.push(line);
|
|
532
|
+
line = word;
|
|
533
|
+
}
|
|
534
|
+
lines.push(line);
|
|
535
|
+
return lines;
|
|
471
536
|
}
|
|
472
|
-
function
|
|
473
|
-
const
|
|
474
|
-
|
|
537
|
+
function formatColumns(opts) {
|
|
538
|
+
const { rows } = opts;
|
|
539
|
+
if (rows.length === 0) {
|
|
540
|
+
return "";
|
|
541
|
+
}
|
|
542
|
+
const totalWidth = opts.totalWidth ?? process.stdout.columns ?? 100;
|
|
543
|
+
const minLeftWidth = opts.minLeftWidth ?? 12;
|
|
544
|
+
const maxLeftWidth = opts.maxLeftWidth ?? 32;
|
|
545
|
+
const gap = opts.gap ?? 3;
|
|
546
|
+
const indent = opts.indent ?? 2;
|
|
547
|
+
const maxLeftContentWidth = Math.max(...rows.map((row) => visibleWidth(row.left)));
|
|
548
|
+
const leftWidth = clamp(maxLeftContentWidth + gap, minLeftWidth, maxLeftWidth);
|
|
549
|
+
const rightWidth = Math.max(20, totalWidth - leftWidth - indent);
|
|
550
|
+
const firstIndent = " ".repeat(indent);
|
|
551
|
+
const continuationIndent = " ".repeat(indent + leftWidth);
|
|
552
|
+
return rows.flatMap((row) => {
|
|
553
|
+
if (row.right.length === 0) {
|
|
554
|
+
return [`${firstIndent}${row.left}`];
|
|
555
|
+
}
|
|
556
|
+
const rightLines = wrapWords(row.right, rightWidth);
|
|
557
|
+
if (visibleWidth(row.left) > leftWidth) {
|
|
558
|
+
return [
|
|
559
|
+
`${firstIndent}${row.left}`,
|
|
560
|
+
...rightLines.map((line) => `${continuationIndent}${line}`)
|
|
561
|
+
];
|
|
562
|
+
}
|
|
563
|
+
const firstLine = `${firstIndent}${padEndVisible(row.left, leftWidth)}${rightLines[0]}`;
|
|
564
|
+
const continuationLines = rightLines.slice(1).map((line) => `${continuationIndent}${line}`);
|
|
565
|
+
return [firstLine, ...continuationLines];
|
|
566
|
+
}).join("\n");
|
|
475
567
|
}
|
|
476
568
|
function formatCommandList(commands) {
|
|
477
|
-
return
|
|
569
|
+
return formatColumns({
|
|
570
|
+
rows: commands.map((cmd) => ({
|
|
571
|
+
left: text.command(cmd.name),
|
|
572
|
+
right: cmd.description
|
|
573
|
+
}))
|
|
574
|
+
});
|
|
478
575
|
}
|
|
479
576
|
function formatOptionList(options) {
|
|
480
|
-
return
|
|
577
|
+
return formatColumns({
|
|
578
|
+
rows: options.map((opt) => ({
|
|
579
|
+
left: text.option(opt.flags),
|
|
580
|
+
right: opt.description
|
|
581
|
+
}))
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// ../design-system/src/components/help-formatter-plain.ts
|
|
586
|
+
var help_formatter_plain_exports = {};
|
|
587
|
+
__export(help_formatter_plain_exports, {
|
|
588
|
+
formatColumns: () => formatColumns2,
|
|
589
|
+
formatCommandList: () => formatCommandList2,
|
|
590
|
+
formatOptionList: () => formatOptionList2
|
|
591
|
+
});
|
|
592
|
+
function stripAnsi3(value) {
|
|
593
|
+
let output = "";
|
|
594
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
595
|
+
if (value[index] === "\x1B") {
|
|
596
|
+
if (value[index + 1] === "[") {
|
|
597
|
+
index += 2;
|
|
598
|
+
while (index < value.length) {
|
|
599
|
+
const code = value.charCodeAt(index);
|
|
600
|
+
if (code >= 64 && code <= 126) {
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
index += 1;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
output += value[index];
|
|
609
|
+
}
|
|
610
|
+
return output;
|
|
611
|
+
}
|
|
612
|
+
function toAscii(value) {
|
|
613
|
+
let output = "";
|
|
614
|
+
const stripped = stripAnsi3(value);
|
|
615
|
+
for (let index = 0; index < stripped.length; index += 1) {
|
|
616
|
+
const code = stripped.charCodeAt(index);
|
|
617
|
+
output += code <= 127 ? stripped[index] : "?";
|
|
618
|
+
}
|
|
619
|
+
return output;
|
|
620
|
+
}
|
|
621
|
+
function clamp2(value, min, max) {
|
|
622
|
+
return Math.min(Math.max(value, min), max);
|
|
623
|
+
}
|
|
624
|
+
function padEndVisible2(value, width) {
|
|
625
|
+
return value + " ".repeat(Math.max(0, width - value.length));
|
|
626
|
+
}
|
|
627
|
+
function isWhitespace2(char) {
|
|
628
|
+
return char === " " || char === "\n" || char === " " || char === "\r";
|
|
629
|
+
}
|
|
630
|
+
function splitWords2(value) {
|
|
631
|
+
const words = [];
|
|
632
|
+
let word = "";
|
|
633
|
+
for (const char of value) {
|
|
634
|
+
if (isWhitespace2(char)) {
|
|
635
|
+
if (word) {
|
|
636
|
+
words.push(word);
|
|
637
|
+
word = "";
|
|
638
|
+
}
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
word += char;
|
|
642
|
+
}
|
|
643
|
+
if (word) {
|
|
644
|
+
words.push(word);
|
|
645
|
+
}
|
|
646
|
+
return words;
|
|
647
|
+
}
|
|
648
|
+
function wrapWords2(value, width) {
|
|
649
|
+
const words = splitWords2(value);
|
|
650
|
+
if (words.length === 0) {
|
|
651
|
+
return [""];
|
|
652
|
+
}
|
|
653
|
+
const lines = [];
|
|
654
|
+
let line = "";
|
|
655
|
+
for (const word of words) {
|
|
656
|
+
if (!line) {
|
|
657
|
+
line = word;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (line.length + 1 + word.length <= width) {
|
|
661
|
+
line += ` ${word}`;
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
lines.push(line);
|
|
665
|
+
line = word;
|
|
666
|
+
}
|
|
667
|
+
lines.push(line);
|
|
668
|
+
return lines;
|
|
669
|
+
}
|
|
670
|
+
function formatColumns2(opts) {
|
|
671
|
+
const rows = opts.rows.map((row) => ({
|
|
672
|
+
left: toAscii(row.left),
|
|
673
|
+
right: toAscii(row.right)
|
|
674
|
+
}));
|
|
675
|
+
if (rows.length === 0) {
|
|
676
|
+
return "";
|
|
677
|
+
}
|
|
678
|
+
const totalWidth = opts.totalWidth ?? process.stdout.columns ?? 100;
|
|
679
|
+
const minLeftWidth = opts.minLeftWidth ?? 12;
|
|
680
|
+
const maxLeftWidth = opts.maxLeftWidth ?? 32;
|
|
681
|
+
const gap = opts.gap ?? 3;
|
|
682
|
+
const indent = opts.indent ?? 2;
|
|
683
|
+
const maxLeftContentWidth = Math.max(...rows.map((row) => row.left.length));
|
|
684
|
+
const leftWidth = clamp2(maxLeftContentWidth + gap, minLeftWidth, maxLeftWidth);
|
|
685
|
+
const rightWidth = Math.max(20, totalWidth - leftWidth - indent);
|
|
686
|
+
const firstIndent = " ".repeat(indent);
|
|
687
|
+
const continuationIndent = " ".repeat(indent + leftWidth);
|
|
688
|
+
return rows.flatMap((row) => {
|
|
689
|
+
if (row.right.length === 0) {
|
|
690
|
+
return [`${firstIndent}${row.left}`];
|
|
691
|
+
}
|
|
692
|
+
const rightLines = wrapWords2(row.right, rightWidth);
|
|
693
|
+
if (row.left.length > leftWidth) {
|
|
694
|
+
return [
|
|
695
|
+
`${firstIndent}${row.left}`,
|
|
696
|
+
...rightLines.map((line) => `${continuationIndent}${line}`)
|
|
697
|
+
];
|
|
698
|
+
}
|
|
699
|
+
const firstLine = `${firstIndent}${padEndVisible2(row.left, leftWidth)}${rightLines[0]}`;
|
|
700
|
+
const continuationLines = rightLines.slice(1).map((line) => `${continuationIndent}${line}`);
|
|
701
|
+
return [firstLine, ...continuationLines];
|
|
702
|
+
}).join("\n");
|
|
703
|
+
}
|
|
704
|
+
function formatCommandList2(commands) {
|
|
705
|
+
return formatColumns2({
|
|
706
|
+
rows: commands.map((cmd) => ({
|
|
707
|
+
left: cmd.name,
|
|
708
|
+
right: cmd.description
|
|
709
|
+
}))
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
function formatOptionList2(options) {
|
|
713
|
+
return formatColumns2({
|
|
714
|
+
rows: options.map((opt) => ({
|
|
715
|
+
left: opt.flags,
|
|
716
|
+
right: opt.description
|
|
717
|
+
}))
|
|
718
|
+
});
|
|
481
719
|
}
|
|
482
720
|
|
|
483
721
|
// ../design-system/src/components/table.ts
|
|
@@ -1330,134 +1568,1124 @@ function getCommandSourcePath(command) {
|
|
|
1330
1568
|
// ../task-list/src/open.ts
|
|
1331
1569
|
import * as fsPromises2 from "node:fs/promises";
|
|
1332
1570
|
|
|
1333
|
-
// ../task-list/src/
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
// ../file-lock/src/lock.ts
|
|
1337
|
-
import * as fsPromises from "node:fs/promises";
|
|
1338
|
-
import * as os from "node:os";
|
|
1339
|
-
var LockTimeoutError = class extends Error {
|
|
1340
|
-
constructor(message2) {
|
|
1341
|
-
super(message2);
|
|
1342
|
-
this.name = "LockTimeoutError";
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
function createAbortError() {
|
|
1346
|
-
const error2 = new Error("The operation was aborted.");
|
|
1347
|
-
error2.name = "AbortError";
|
|
1348
|
-
return error2;
|
|
1571
|
+
// ../task-list/src/state-machine.ts
|
|
1572
|
+
function isRecord(value) {
|
|
1573
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1349
1574
|
}
|
|
1350
|
-
function
|
|
1351
|
-
|
|
1352
|
-
|
|
1575
|
+
function isStateList(value) {
|
|
1576
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1577
|
+
}
|
|
1578
|
+
function canFireFromState(event, fromState) {
|
|
1579
|
+
if (event.from === "*") {
|
|
1580
|
+
return event.to !== fromState;
|
|
1353
1581
|
}
|
|
1582
|
+
return event.from.includes(fromState);
|
|
1354
1583
|
}
|
|
1355
|
-
function
|
|
1356
|
-
if (!
|
|
1357
|
-
|
|
1584
|
+
function validateMachine(machine) {
|
|
1585
|
+
if (!isRecord(machine)) {
|
|
1586
|
+
throw new TypeError("State machine must be an object.");
|
|
1358
1587
|
}
|
|
1359
|
-
if (
|
|
1360
|
-
|
|
1588
|
+
if (!isStateList(machine.states)) {
|
|
1589
|
+
throw new TypeError("State machine states must be a string array.");
|
|
1361
1590
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
resolve();
|
|
1366
|
-
}, ms);
|
|
1367
|
-
const onAbort = () => {
|
|
1368
|
-
clearTimeout(timeoutId);
|
|
1369
|
-
signal.removeEventListener("abort", onAbort);
|
|
1370
|
-
reject(createAbortError());
|
|
1371
|
-
};
|
|
1372
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
|
-
function backoff(attempt, minTimeout, maxTimeout) {
|
|
1376
|
-
const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
|
|
1377
|
-
return delay + Math.random() * delay * 0.1;
|
|
1378
|
-
}
|
|
1379
|
-
function hasErrorCode(error2, code) {
|
|
1380
|
-
return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
|
|
1381
|
-
}
|
|
1382
|
-
function hasAnyErrorCode(error2, codes) {
|
|
1383
|
-
return codes.some((code) => hasErrorCode(error2, code));
|
|
1384
|
-
}
|
|
1385
|
-
function isPidRunning(pid) {
|
|
1386
|
-
try {
|
|
1387
|
-
process.kill(pid, 0);
|
|
1388
|
-
return true;
|
|
1389
|
-
} catch (error2) {
|
|
1390
|
-
return !hasErrorCode(error2, "ESRCH");
|
|
1591
|
+
const states = new Set(machine.states);
|
|
1592
|
+
if (typeof machine.initial !== "string") {
|
|
1593
|
+
throw new TypeError("State machine initial must be a string.");
|
|
1391
1594
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1595
|
+
if (!states.has(machine.initial)) {
|
|
1596
|
+
throw new Error(`Initial state "${machine.initial}" is not declared.`);
|
|
1597
|
+
}
|
|
1598
|
+
if (!isRecord(machine.events)) {
|
|
1599
|
+
throw new TypeError("State machine events must be an object.");
|
|
1600
|
+
}
|
|
1601
|
+
for (const [eventName, event] of Object.entries(machine.events)) {
|
|
1602
|
+
if (!isRecord(event)) {
|
|
1603
|
+
throw new TypeError(`Event "${eventName}" must be an object.`);
|
|
1604
|
+
}
|
|
1605
|
+
if (event.from !== "*" && !isStateList(event.from)) {
|
|
1606
|
+
throw new TypeError(`Event "${eventName}" has an invalid "from" definition.`);
|
|
1607
|
+
}
|
|
1608
|
+
if (typeof event.to !== "string") {
|
|
1609
|
+
throw new TypeError(`Event "${eventName}" target state must be a string.`);
|
|
1610
|
+
}
|
|
1611
|
+
if (!states.has(event.to)) {
|
|
1612
|
+
throw new Error(`Event "${eventName}" references unknown target state "${event.to}".`);
|
|
1613
|
+
}
|
|
1614
|
+
if (event.from !== "*") {
|
|
1615
|
+
for (const fromState of event.from) {
|
|
1616
|
+
if (!states.has(fromState)) {
|
|
1617
|
+
throw new Error(`Event "${eventName}" references unknown source state "${fromState}".`);
|
|
1618
|
+
}
|
|
1413
1619
|
}
|
|
1414
1620
|
}
|
|
1415
|
-
await sleep(25 * 2 ** attempt, signal);
|
|
1416
1621
|
}
|
|
1417
1622
|
}
|
|
1418
|
-
function
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
if (
|
|
1422
|
-
|
|
1423
|
-
}
|
|
1424
|
-
const { host, pid } = parsed;
|
|
1425
|
-
if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
|
|
1426
|
-
return {
|
|
1427
|
-
host,
|
|
1428
|
-
pid
|
|
1429
|
-
};
|
|
1623
|
+
function eventsFromState(machine, fromState) {
|
|
1624
|
+
const events = [];
|
|
1625
|
+
for (const [eventName, event] of Object.entries(machine.events)) {
|
|
1626
|
+
if (canFireFromState(event, fromState)) {
|
|
1627
|
+
events.push(eventName);
|
|
1430
1628
|
}
|
|
1431
|
-
} catch (ignoredError) {
|
|
1432
|
-
void ignoredError;
|
|
1433
1629
|
}
|
|
1434
|
-
return
|
|
1630
|
+
return events;
|
|
1435
1631
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
}
|
|
1440
|
-
try {
|
|
1441
|
-
return parseLockMetadata(await fs2.readFile(lockPath, "utf8"));
|
|
1442
|
-
} catch (error2) {
|
|
1443
|
-
if (hasErrorCode(error2, "ENOENT")) {
|
|
1444
|
-
return null;
|
|
1445
|
-
}
|
|
1632
|
+
function findEvent(machine, fromState, eventName) {
|
|
1633
|
+
const event = machine.events[eventName];
|
|
1634
|
+
if (event === void 0) {
|
|
1446
1635
|
return void 0;
|
|
1447
1636
|
}
|
|
1637
|
+
return canFireFromState(event, fromState) ? event : void 0;
|
|
1448
1638
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1639
|
+
|
|
1640
|
+
// ../task-list/src/types.ts
|
|
1641
|
+
var TaskNotFoundError = class extends Error {
|
|
1642
|
+
constructor(message2 = "Task not found.") {
|
|
1643
|
+
super(message2);
|
|
1644
|
+
this.name = "TaskNotFoundError";
|
|
1453
1645
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1646
|
+
};
|
|
1647
|
+
var TaskAlreadyExistsError = class extends Error {
|
|
1648
|
+
constructor(message2 = "Task already exists.") {
|
|
1649
|
+
super(message2);
|
|
1650
|
+
this.name = "TaskAlreadyExistsError";
|
|
1456
1651
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1652
|
+
};
|
|
1653
|
+
var InvalidTransitionError = class extends Error {
|
|
1654
|
+
task;
|
|
1655
|
+
event;
|
|
1656
|
+
to;
|
|
1657
|
+
reason;
|
|
1658
|
+
constructor(messageOrOptions = "Invalid task transition.") {
|
|
1659
|
+
const options = typeof messageOrOptions === "string" ? {
|
|
1660
|
+
reason: messageOrOptions
|
|
1661
|
+
} : messageOrOptions;
|
|
1662
|
+
super(options.reason);
|
|
1663
|
+
this.name = "InvalidTransitionError";
|
|
1664
|
+
this.task = options.task;
|
|
1665
|
+
this.event = options.event;
|
|
1666
|
+
this.to = options.to;
|
|
1667
|
+
this.reason = options.reason;
|
|
1668
|
+
}
|
|
1669
|
+
};
|
|
1670
|
+
var MalformedTaskError = class extends Error {
|
|
1671
|
+
constructor(message2 = "Malformed task.") {
|
|
1672
|
+
super(message2);
|
|
1673
|
+
this.name = "MalformedTaskError";
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
var OrderMismatchError = class extends Error {
|
|
1677
|
+
missing;
|
|
1678
|
+
extra;
|
|
1679
|
+
constructor(options) {
|
|
1680
|
+
const parts = [];
|
|
1681
|
+
if (options.missing.length > 0) {
|
|
1682
|
+
parts.push(`missing ${options.missing.map((id) => `"${id}"`).join(", ")}`);
|
|
1683
|
+
}
|
|
1684
|
+
if (options.extra.length > 0) {
|
|
1685
|
+
parts.push(`extra ${options.extra.map((id) => `"${id}"`).join(", ")}`);
|
|
1686
|
+
}
|
|
1687
|
+
super(`reorder requires the exact set of active task ids: ${parts.join("; ")}.`);
|
|
1688
|
+
this.name = "OrderMismatchError";
|
|
1689
|
+
this.missing = options.missing;
|
|
1690
|
+
this.extra = options.extra;
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
var AnchorNotFoundError = class extends Error {
|
|
1694
|
+
anchor;
|
|
1695
|
+
constructor(anchor) {
|
|
1696
|
+
super(`Anchor task "${anchor}" not found.`);
|
|
1697
|
+
this.name = "AnchorNotFoundError";
|
|
1698
|
+
this.anchor = anchor;
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1702
|
+
// ../task-list/src/backends/gh-issues-client.ts
|
|
1703
|
+
import { text as text4 } from "node:stream/consumers";
|
|
1704
|
+
|
|
1705
|
+
// ../process-runner/src/docker/context.ts
|
|
1706
|
+
import { execSync } from "node:child_process";
|
|
1707
|
+
|
|
1708
|
+
// ../process-runner/src/docker/engine.ts
|
|
1709
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
1710
|
+
|
|
1711
|
+
// ../process-runner/src/docker/docker-runner.ts
|
|
1712
|
+
import * as childProcess from "node:child_process";
|
|
1713
|
+
import { randomBytes } from "node:crypto";
|
|
1714
|
+
|
|
1715
|
+
// ../process-runner/src/docker/args.ts
|
|
1716
|
+
import path2 from "node:path";
|
|
1717
|
+
|
|
1718
|
+
// ../process-runner/src/docker/docker-execution-env.ts
|
|
1719
|
+
import { createHash, randomBytes as randomBytes2 } from "node:crypto";
|
|
1720
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
1721
|
+
import { readFile } from "node:fs/promises";
|
|
1722
|
+
import { tmpdir } from "node:os";
|
|
1723
|
+
import path3 from "node:path";
|
|
1724
|
+
|
|
1725
|
+
// ../process-runner/src/host/host-runner.ts
|
|
1726
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
1727
|
+
function createHostRunner(options = {}) {
|
|
1728
|
+
const detached = options.detached === true;
|
|
1729
|
+
return {
|
|
1730
|
+
name: "host",
|
|
1731
|
+
exec(spec) {
|
|
1732
|
+
const stdinMode = spec.stdin ?? "ignore";
|
|
1733
|
+
const stdoutMode = spec.stdout ?? "pipe";
|
|
1734
|
+
const stderrMode = spec.stderr ?? "pipe";
|
|
1735
|
+
const stdio = stdinMode === "inherit" && stdoutMode === "inherit" && stderrMode === "inherit" ? "inherit" : [stdinMode, stdoutMode, stderrMode];
|
|
1736
|
+
const child = spawnChildProcess(spec.command, spec.args ?? [], {
|
|
1737
|
+
cwd: spec.cwd,
|
|
1738
|
+
env: spec.env,
|
|
1739
|
+
stdio,
|
|
1740
|
+
...detached ? { detached: true } : {}
|
|
1741
|
+
});
|
|
1742
|
+
if (detached) {
|
|
1743
|
+
child.unref();
|
|
1744
|
+
}
|
|
1745
|
+
const kill = (signal) => {
|
|
1746
|
+
if (detached && process.platform !== "win32" && child.pid !== void 0) {
|
|
1747
|
+
process.kill(-child.pid, signal);
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
child.kill(signal);
|
|
1751
|
+
};
|
|
1752
|
+
let settled = false;
|
|
1753
|
+
let resolveResult = null;
|
|
1754
|
+
const result = new Promise((resolve) => {
|
|
1755
|
+
resolveResult = resolve;
|
|
1756
|
+
});
|
|
1757
|
+
const cleanupAbort = bindAbortSignal(spec.signal, () => {
|
|
1758
|
+
kill("SIGTERM");
|
|
1759
|
+
});
|
|
1760
|
+
child.once("close", (code) => {
|
|
1761
|
+
if (settled) return;
|
|
1762
|
+
settled = true;
|
|
1763
|
+
cleanupAbort();
|
|
1764
|
+
resolveResult?.({ exitCode: code ?? 1 });
|
|
1765
|
+
});
|
|
1766
|
+
child.once("error", () => {
|
|
1767
|
+
if (settled) return;
|
|
1768
|
+
settled = true;
|
|
1769
|
+
cleanupAbort();
|
|
1770
|
+
resolveResult?.({ exitCode: 1 });
|
|
1771
|
+
});
|
|
1772
|
+
return {
|
|
1773
|
+
pid: child.pid ?? null,
|
|
1774
|
+
stdin: child.stdin,
|
|
1775
|
+
stdout: child.stdout,
|
|
1776
|
+
stderr: child.stderr,
|
|
1777
|
+
result,
|
|
1778
|
+
kill
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
function bindAbortSignal(signal, onAbort) {
|
|
1784
|
+
if (signal === void 0) {
|
|
1785
|
+
return () => {
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
if (signal.aborted) {
|
|
1789
|
+
onAbort();
|
|
1790
|
+
return () => {
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1794
|
+
return () => {
|
|
1795
|
+
signal.removeEventListener("abort", onAbort);
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// ../process-runner/src/testing/mock-runner.ts
|
|
1800
|
+
import { Readable, Writable } from "node:stream";
|
|
1801
|
+
|
|
1802
|
+
// ../task-list/src/backends/gh-issues-client.ts
|
|
1803
|
+
var DEFAULT_ENDPOINT = "https://api.github.com/graphql";
|
|
1804
|
+
var USER_AGENT = "poe-code-task-list/0.0.1";
|
|
1805
|
+
var AUTH_ERROR = "gh auth token failed; install gh, run 'gh auth login', or pass auth: { token }";
|
|
1806
|
+
function createGhClient(options) {
|
|
1807
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
1808
|
+
const endpoint = options.endpoint || DEFAULT_ENDPOINT;
|
|
1809
|
+
return {
|
|
1810
|
+
async graphql(query, variables) {
|
|
1811
|
+
const response = await fetchImpl(endpoint, {
|
|
1812
|
+
method: "POST",
|
|
1813
|
+
headers: {
|
|
1814
|
+
Authorization: `Bearer ${options.token}`,
|
|
1815
|
+
"Content-Type": "application/json",
|
|
1816
|
+
"User-Agent": USER_AGENT
|
|
1817
|
+
},
|
|
1818
|
+
body: JSON.stringify({ query, variables })
|
|
1819
|
+
});
|
|
1820
|
+
const body = await response.text();
|
|
1821
|
+
if (response.status !== 200) {
|
|
1822
|
+
throw new Error(`GitHub GraphQL request failed with status ${response.status}: ${body}`);
|
|
1823
|
+
}
|
|
1824
|
+
const parsed = JSON.parse(body);
|
|
1825
|
+
const firstError = parsed.errors?.[0];
|
|
1826
|
+
if (firstError !== void 0) {
|
|
1827
|
+
throw new Error(firstError.message ?? "GitHub GraphQL request failed");
|
|
1828
|
+
}
|
|
1829
|
+
return parsed.data;
|
|
1830
|
+
}
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
async function resolveAuth(options) {
|
|
1834
|
+
if (options.explicitToken !== void 0) {
|
|
1835
|
+
return options.explicitToken;
|
|
1836
|
+
}
|
|
1837
|
+
const runner = options.runner ?? createHostRunner();
|
|
1838
|
+
const handle = runner.exec({
|
|
1839
|
+
command: "gh",
|
|
1840
|
+
args: ["auth", "token"],
|
|
1841
|
+
stdout: "pipe",
|
|
1842
|
+
stderr: "pipe"
|
|
1843
|
+
});
|
|
1844
|
+
const [stdout, , result] = await Promise.all([
|
|
1845
|
+
handle.stdout === null ? Promise.resolve("") : text4(handle.stdout),
|
|
1846
|
+
handle.stderr === null ? Promise.resolve("") : text4(handle.stderr),
|
|
1847
|
+
handle.result
|
|
1848
|
+
]);
|
|
1849
|
+
const token = stdout.trim();
|
|
1850
|
+
if (result.exitCode !== 0 || token.length === 0) {
|
|
1851
|
+
throw new Error(AUTH_ERROR);
|
|
1852
|
+
}
|
|
1853
|
+
return token;
|
|
1854
|
+
}
|
|
1855
|
+
function resolveEndpoint(options = {}) {
|
|
1856
|
+
const env = options.env ?? process.env;
|
|
1857
|
+
const host = env.GH_HOST;
|
|
1858
|
+
if (host !== void 0 && host !== "" && host !== "github.com") {
|
|
1859
|
+
return `https://${host}/api/graphql`;
|
|
1860
|
+
}
|
|
1861
|
+
return DEFAULT_ENDPOINT;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// ../task-list/src/backends/utils.ts
|
|
1865
|
+
import path4 from "node:path";
|
|
1866
|
+
function compareCreated(left, right) {
|
|
1867
|
+
const leftCreated = typeof left.raw.created === "string" ? left.raw.created : "";
|
|
1868
|
+
const rightCreated = typeof right.raw.created === "string" ? right.raw.created : "";
|
|
1869
|
+
if (leftCreated === "" && rightCreated === "") {
|
|
1870
|
+
return left.task.qualifiedId.localeCompare(right.task.qualifiedId);
|
|
1871
|
+
}
|
|
1872
|
+
if (leftCreated === "") return 1;
|
|
1873
|
+
if (rightCreated === "") return -1;
|
|
1874
|
+
return leftCreated.localeCompare(rightCreated);
|
|
1875
|
+
}
|
|
1876
|
+
function applyOrder(entries, order) {
|
|
1877
|
+
if (order === "alphabetical") {
|
|
1878
|
+
return sortTasks(entries.map((entry) => entry.task));
|
|
1879
|
+
}
|
|
1880
|
+
if (order === "created") {
|
|
1881
|
+
return [...entries].sort(compareCreated).map((entry) => entry.task);
|
|
1882
|
+
}
|
|
1883
|
+
return entries.map((entry) => entry.task);
|
|
1884
|
+
}
|
|
1885
|
+
var tmpFileCounter = 0;
|
|
1886
|
+
function hasErrorCode(error2, code) {
|
|
1887
|
+
return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
|
|
1888
|
+
}
|
|
1889
|
+
function isRecord2(value) {
|
|
1890
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1891
|
+
}
|
|
1892
|
+
function sortStrings(values) {
|
|
1893
|
+
return [...values].sort((left, right) => left.localeCompare(right));
|
|
1894
|
+
}
|
|
1895
|
+
function sortTasks(tasks) {
|
|
1896
|
+
return [...tasks].sort((left, right) => left.qualifiedId.localeCompare(right.qualifiedId));
|
|
1897
|
+
}
|
|
1898
|
+
function validateTaskId(id) {
|
|
1899
|
+
if (id.length === 0 || id.startsWith(".") || id.includes("/") || id.includes("\\") || id.includes("..")) {
|
|
1900
|
+
throw new Error(`Invalid task id "${id}".`);
|
|
1901
|
+
}
|
|
1902
|
+
return id;
|
|
1903
|
+
}
|
|
1904
|
+
async function statIfExists(fs2, filePath) {
|
|
1905
|
+
try {
|
|
1906
|
+
return await fs2.stat(filePath);
|
|
1907
|
+
} catch (error2) {
|
|
1908
|
+
if (hasErrorCode(error2, "ENOENT")) {
|
|
1909
|
+
return void 0;
|
|
1910
|
+
}
|
|
1911
|
+
throw error2;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
async function writeAtomically(fs2, filePath, content) {
|
|
1915
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${tmpFileCounter}`;
|
|
1916
|
+
tmpFileCounter += 1;
|
|
1917
|
+
await fs2.mkdir(path4.dirname(filePath), { recursive: true });
|
|
1918
|
+
try {
|
|
1919
|
+
await fs2.writeFile(tempPath, content, { encoding: "utf8", flag: "wx" });
|
|
1920
|
+
await fs2.rename(tempPath, filePath);
|
|
1921
|
+
} catch (error2) {
|
|
1922
|
+
try {
|
|
1923
|
+
await fs2.unlink(tempPath);
|
|
1924
|
+
} catch (unlinkError) {
|
|
1925
|
+
if (!hasErrorCode(unlinkError, "ENOENT")) {
|
|
1926
|
+
throw unlinkError;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
throw error2;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// ../task-list/src/backends/gh-issues.ts
|
|
1934
|
+
var PROJECT_ORGANIZATION_QUERY = `query Project($owner: String!, $number: Int!) {
|
|
1935
|
+
organization(login: $owner) {
|
|
1936
|
+
projectV2(number: $number) {
|
|
1937
|
+
id
|
|
1938
|
+
title
|
|
1939
|
+
field(name: "Status") {
|
|
1940
|
+
... on ProjectV2SingleSelectField {
|
|
1941
|
+
id
|
|
1942
|
+
options { id name }
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}`;
|
|
1948
|
+
var PROJECT_USER_QUERY = `query Project($owner: String!, $number: Int!) {
|
|
1949
|
+
user(login: $owner) {
|
|
1950
|
+
projectV2(number: $number) {
|
|
1951
|
+
id
|
|
1952
|
+
title
|
|
1953
|
+
field(name: "Status") {
|
|
1954
|
+
... on ProjectV2SingleSelectField {
|
|
1955
|
+
id
|
|
1956
|
+
options { id name }
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}`;
|
|
1962
|
+
var PROJECT_ITEMS_QUERY = `query Items($projectId: ID!, $after: String) {
|
|
1963
|
+
node(id: $projectId) {
|
|
1964
|
+
... on ProjectV2 {
|
|
1965
|
+
items(first: 100, after: $after) {
|
|
1966
|
+
nodes {
|
|
1967
|
+
id
|
|
1968
|
+
content {
|
|
1969
|
+
__typename
|
|
1970
|
+
... on Issue {
|
|
1971
|
+
number
|
|
1972
|
+
title
|
|
1973
|
+
body
|
|
1974
|
+
url
|
|
1975
|
+
createdAt
|
|
1976
|
+
labels(first: 50) { nodes { name } }
|
|
1977
|
+
assignees(first: 20) { nodes { login } }
|
|
1978
|
+
milestone { title }
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
fieldValueByName(name: "Status") {
|
|
1982
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
1983
|
+
name
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
pageInfo { hasNextPage endCursor }
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}`;
|
|
1992
|
+
var ISSUE_QUERY = `query Issue($owner: String!, $repo: String!, $number: Int!) {
|
|
1993
|
+
repository(owner: $owner, name: $repo) {
|
|
1994
|
+
issue(number: $number) {
|
|
1995
|
+
number
|
|
1996
|
+
title
|
|
1997
|
+
body
|
|
1998
|
+
url
|
|
1999
|
+
createdAt
|
|
2000
|
+
labels(first: 50) { nodes { name } }
|
|
2001
|
+
assignees(first: 20) { nodes { login } }
|
|
2002
|
+
milestone { title }
|
|
2003
|
+
projectItems(first: 10) {
|
|
2004
|
+
nodes {
|
|
2005
|
+
id
|
|
2006
|
+
project { id }
|
|
2007
|
+
fieldValueByName(name: "Status") {
|
|
2008
|
+
... on ProjectV2ItemFieldSingleSelectValue {
|
|
2009
|
+
name
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}`;
|
|
2017
|
+
var REPOSITORY_QUERY = `query Repository($owner: String!, $repo: String!) {
|
|
2018
|
+
repository(owner: $owner, name: $repo) {
|
|
2019
|
+
id
|
|
2020
|
+
}
|
|
2021
|
+
}`;
|
|
2022
|
+
var ISSUE_ID_QUERY = `query IssueId($owner: String!, $repo: String!, $number: Int!) {
|
|
2023
|
+
repository(owner: $owner, name: $repo) {
|
|
2024
|
+
issue(number: $number) {
|
|
2025
|
+
id
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}`;
|
|
2029
|
+
var ISSUE_PROJECT_ITEM_QUERY = `query IssueProjectItem($owner: String!, $repo: String!, $number: Int!) {
|
|
2030
|
+
repository(owner: $owner, name: $repo) {
|
|
2031
|
+
issue(number: $number) {
|
|
2032
|
+
id
|
|
2033
|
+
projectItems(first: 10) {
|
|
2034
|
+
nodes {
|
|
2035
|
+
id
|
|
2036
|
+
project { id }
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}`;
|
|
2042
|
+
var CREATE_ISSUE_MUTATION = `mutation CreateIssue($input: CreateIssueInput!) {
|
|
2043
|
+
createIssue(input: $input) {
|
|
2044
|
+
issue {
|
|
2045
|
+
id
|
|
2046
|
+
number
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}`;
|
|
2050
|
+
var ADD_PROJECT_ITEM_MUTATION = `mutation AddProjectItem($input: AddProjectV2ItemByIdInput!) {
|
|
2051
|
+
addProjectV2ItemById(input: $input) {
|
|
2052
|
+
item {
|
|
2053
|
+
id
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}`;
|
|
2057
|
+
var UPDATE_STATUS_MUTATION = `mutation UpdateProjectItemStatus($input: UpdateProjectV2ItemFieldValueInput!) {
|
|
2058
|
+
updateProjectV2ItemFieldValue(input: $input) {
|
|
2059
|
+
projectV2Item {
|
|
2060
|
+
id
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}`;
|
|
2064
|
+
var UPDATE_ISSUE_MUTATION = `mutation UpdateIssue($input: UpdateIssueInput!) {
|
|
2065
|
+
updateIssue(input: $input) {
|
|
2066
|
+
issue {
|
|
2067
|
+
id
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}`;
|
|
2071
|
+
var UPDATE_PROJECT_ITEM_POSITION_MUTATION = `mutation UpdateProjectItemPosition($input: UpdateProjectV2ItemPositionInput!) {
|
|
2072
|
+
updateProjectV2ItemPosition(input: $input) {
|
|
2073
|
+
clientMutationId
|
|
2074
|
+
}
|
|
2075
|
+
}`;
|
|
2076
|
+
var DELETE_PROJECT_ITEM_MUTATION = `mutation DeleteProjectItem($input: DeleteProjectV2ItemInput!) {
|
|
2077
|
+
deleteProjectV2Item(input: $input) {
|
|
2078
|
+
deletedItemId
|
|
2079
|
+
}
|
|
2080
|
+
}`;
|
|
2081
|
+
async function ghIssuesBackend(deps) {
|
|
2082
|
+
const client = createGhClient({
|
|
2083
|
+
token: deps.token,
|
|
2084
|
+
endpoint: deps.endpoint,
|
|
2085
|
+
fetch: deps.fetch
|
|
2086
|
+
});
|
|
2087
|
+
const listName = `${deps.project.owner}/${deps.project.number}`;
|
|
2088
|
+
const variables = {
|
|
2089
|
+
owner: deps.project.owner,
|
|
2090
|
+
number: deps.project.number
|
|
2091
|
+
};
|
|
2092
|
+
const organizationResult = await client.graphql(
|
|
2093
|
+
PROJECT_ORGANIZATION_QUERY,
|
|
2094
|
+
variables
|
|
2095
|
+
);
|
|
2096
|
+
let project = organizationResult.organization?.projectV2 ?? null;
|
|
2097
|
+
if (project === null) {
|
|
2098
|
+
const userResult = await client.graphql(PROJECT_USER_QUERY, variables);
|
|
2099
|
+
project = userResult.user?.projectV2 ?? null;
|
|
2100
|
+
}
|
|
2101
|
+
if (project === null) {
|
|
2102
|
+
throw new Error(`Project ${listName} not found or inaccessible.`);
|
|
2103
|
+
}
|
|
2104
|
+
const field = project.field;
|
|
2105
|
+
if (!isStatusField(field)) {
|
|
2106
|
+
throw new Error(`Project ${listName} has no Status field; gh-issues requires one.`);
|
|
2107
|
+
}
|
|
2108
|
+
if (field.options.length === 0) {
|
|
2109
|
+
throw new Error(`Project ${listName} Status field has no options.`);
|
|
2110
|
+
}
|
|
2111
|
+
const session = createSession(project, field);
|
|
2112
|
+
const repoParts = parseRepo(deps.repo);
|
|
2113
|
+
const context = {
|
|
2114
|
+
client,
|
|
2115
|
+
repoOwner: repoParts.owner,
|
|
2116
|
+
repoName: repoParts.name,
|
|
2117
|
+
issueIds: /* @__PURE__ */ new Map()
|
|
2118
|
+
};
|
|
2119
|
+
function list(name) {
|
|
2120
|
+
assertSingleList(name, listName);
|
|
2121
|
+
return createTasksView(listName, session, context);
|
|
2122
|
+
}
|
|
2123
|
+
return {
|
|
2124
|
+
list,
|
|
2125
|
+
async lists() {
|
|
2126
|
+
return [listName];
|
|
2127
|
+
},
|
|
2128
|
+
async allTasks(filter) {
|
|
2129
|
+
return list(listName).all(filter);
|
|
2130
|
+
},
|
|
2131
|
+
async get(qualifiedId) {
|
|
2132
|
+
const id = parseQualifiedId(qualifiedId, listName);
|
|
2133
|
+
return list(listName).get(id);
|
|
2134
|
+
},
|
|
2135
|
+
async moveBetweenLists(_qualifiedId, _targetList) {
|
|
2136
|
+
throw singleListError(listName);
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
function createSession(project, field) {
|
|
2141
|
+
const statusOptions = new Map(field.options.map((option) => [option.name, option.id]));
|
|
2142
|
+
const states = field.options.map((option) => option.name);
|
|
2143
|
+
const events = Object.fromEntries(
|
|
2144
|
+
states.map((state) => [state, Object.freeze({ from: "*", to: state })])
|
|
2145
|
+
);
|
|
2146
|
+
const stateMachine = Object.freeze({
|
|
2147
|
+
states: Object.freeze([...states]),
|
|
2148
|
+
initial: states[0],
|
|
2149
|
+
events: Object.freeze(events)
|
|
2150
|
+
});
|
|
2151
|
+
return Object.freeze({
|
|
2152
|
+
projectId: project.id,
|
|
2153
|
+
statusFieldId: field.id,
|
|
2154
|
+
statusOptions,
|
|
2155
|
+
stateMachine
|
|
2156
|
+
});
|
|
2157
|
+
}
|
|
2158
|
+
function createTasksView(name, session, context) {
|
|
2159
|
+
return {
|
|
2160
|
+
name,
|
|
2161
|
+
stateMachine: session.stateMachine,
|
|
2162
|
+
async all(filter) {
|
|
2163
|
+
if (filter?.includeArchived === true) {
|
|
2164
|
+
return [];
|
|
2165
|
+
}
|
|
2166
|
+
const tasks = await fetchProjectTasks(name, session, context);
|
|
2167
|
+
const filteredTasks = filter?.state === void 0 ? tasks : tasks.filter((task) => task.state === filter.state);
|
|
2168
|
+
if (filter?.order === "alphabetical") {
|
|
2169
|
+
return sortTasks(filteredTasks);
|
|
2170
|
+
}
|
|
2171
|
+
if (filter?.order === "created") {
|
|
2172
|
+
return applyOrder(
|
|
2173
|
+
filteredTasks.map((task) => ({
|
|
2174
|
+
task,
|
|
2175
|
+
raw: {
|
|
2176
|
+
created: task.metadata.created
|
|
2177
|
+
}
|
|
2178
|
+
})),
|
|
2179
|
+
"created"
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
return filteredTasks;
|
|
2183
|
+
},
|
|
2184
|
+
async get(id) {
|
|
2185
|
+
return fetchIssueTask(id, name, session, context);
|
|
2186
|
+
},
|
|
2187
|
+
/**
|
|
2188
|
+
* GitHub Issues assigns the issue number, so TaskCreate.id is intentionally ignored.
|
|
2189
|
+
*/
|
|
2190
|
+
async create(input) {
|
|
2191
|
+
const repositoryId = await resolveRepositoryId(context);
|
|
2192
|
+
const created = await context.client.graphql(CREATE_ISSUE_MUTATION, {
|
|
2193
|
+
input: {
|
|
2194
|
+
repositoryId,
|
|
2195
|
+
title: input.name,
|
|
2196
|
+
body: input.description ?? ""
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
const issue = created.createIssue?.issue;
|
|
2200
|
+
const issueId = issue?.id ?? null;
|
|
2201
|
+
const issueNumber = issue?.number ?? null;
|
|
2202
|
+
if (issueId === null || issueNumber === null) {
|
|
2203
|
+
throw new Error("GitHub createIssue response did not include issue id and number.");
|
|
2204
|
+
}
|
|
2205
|
+
context.issueIds.set(issueNumber, issueId);
|
|
2206
|
+
const added = await context.client.graphql(
|
|
2207
|
+
ADD_PROJECT_ITEM_MUTATION,
|
|
2208
|
+
{
|
|
2209
|
+
input: {
|
|
2210
|
+
projectId: session.projectId,
|
|
2211
|
+
contentId: issueId
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
);
|
|
2215
|
+
const projectItemId = added.addProjectV2ItemById?.item?.id ?? null;
|
|
2216
|
+
if (projectItemId === null) {
|
|
2217
|
+
throw new Error("GitHub addProjectV2ItemById response did not include project item id.");
|
|
2218
|
+
}
|
|
2219
|
+
await updateProjectItemStatus(projectItemId, session.stateMachine.initial, session, context);
|
|
2220
|
+
return fetchIssueTask(String(issueNumber), name, session, context);
|
|
2221
|
+
},
|
|
2222
|
+
async update(id, patch) {
|
|
2223
|
+
const input = {};
|
|
2224
|
+
if (patch.name !== void 0) {
|
|
2225
|
+
input.title = patch.name;
|
|
2226
|
+
}
|
|
2227
|
+
if (patch.description !== void 0) {
|
|
2228
|
+
input.body = patch.description;
|
|
2229
|
+
}
|
|
2230
|
+
if (Object.keys(input).length > 0) {
|
|
2231
|
+
input.id = await resolveIssueId(id, name, context);
|
|
2232
|
+
await context.client.graphql(UPDATE_ISSUE_MUTATION, {
|
|
2233
|
+
input
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
return fetchIssueTask(id, name, session, context);
|
|
2237
|
+
},
|
|
2238
|
+
async fire(id, event, _opts) {
|
|
2239
|
+
if (!session.statusOptions.has(event)) {
|
|
2240
|
+
throw new InvalidTransitionError({
|
|
2241
|
+
event,
|
|
2242
|
+
to: event,
|
|
2243
|
+
reason: `Unknown gh-issues Status state "${event}".`
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
const projectItemId = await resolveProjectItemId(id, name, session, context);
|
|
2247
|
+
await updateProjectItemStatus(projectItemId, event, session, context);
|
|
2248
|
+
return fetchIssueTask(id, name, session, context);
|
|
2249
|
+
},
|
|
2250
|
+
async canFire(id, event) {
|
|
2251
|
+
return findEvent(session.stateMachine, id, event) !== void 0;
|
|
2252
|
+
},
|
|
2253
|
+
async events(id) {
|
|
2254
|
+
return eventsFromState(session.stateMachine, id);
|
|
2255
|
+
},
|
|
2256
|
+
async delete(id) {
|
|
2257
|
+
const projectItemId = await resolveProjectItemId(id, name, session, context);
|
|
2258
|
+
await context.client.graphql(DELETE_PROJECT_ITEM_MUTATION, {
|
|
2259
|
+
input: {
|
|
2260
|
+
projectId: session.projectId,
|
|
2261
|
+
itemId: projectItemId
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
},
|
|
2265
|
+
async move(id, anchor) {
|
|
2266
|
+
const projectItemId = await resolveProjectItemId(id, name, session, context);
|
|
2267
|
+
const afterId = await resolveMoveAfterId(id, anchor, name, session, context);
|
|
2268
|
+
await updateProjectItemPosition(projectItemId, afterId, session, context);
|
|
2269
|
+
return fetchIssueTask(id, name, session, context);
|
|
2270
|
+
},
|
|
2271
|
+
async reorder(ids) {
|
|
2272
|
+
const currentTasks = await fetchProjectTasks(name, session, context);
|
|
2273
|
+
const currentIds = currentTasks.map((task) => task.id);
|
|
2274
|
+
const currentSet = new Set(currentIds);
|
|
2275
|
+
const inputSet = new Set(ids);
|
|
2276
|
+
const seenInputIds = /* @__PURE__ */ new Set();
|
|
2277
|
+
const missing = currentIds.filter((id) => !inputSet.has(id));
|
|
2278
|
+
const extra = ids.filter((id) => {
|
|
2279
|
+
if (!currentSet.has(id)) {
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2282
|
+
if (seenInputIds.has(id)) {
|
|
2283
|
+
return true;
|
|
2284
|
+
}
|
|
2285
|
+
seenInputIds.add(id);
|
|
2286
|
+
return false;
|
|
2287
|
+
});
|
|
2288
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
2289
|
+
throw new OrderMismatchError({ missing, extra });
|
|
2290
|
+
}
|
|
2291
|
+
const itemIdsByTaskId = new Map(
|
|
2292
|
+
currentTasks.map((task) => [task.id, projectItemIdFromTask(task)])
|
|
2293
|
+
);
|
|
2294
|
+
let afterId = null;
|
|
2295
|
+
for (const id of ids) {
|
|
2296
|
+
const projectItemId = itemIdsByTaskId.get(id);
|
|
2297
|
+
if (projectItemId === void 0) {
|
|
2298
|
+
throw new OrderMismatchError({ missing: [id], extra: [] });
|
|
2299
|
+
}
|
|
2300
|
+
await updateProjectItemPosition(projectItemId, afterId, session, context);
|
|
2301
|
+
afterId = projectItemId;
|
|
2302
|
+
}
|
|
2303
|
+
return fetchProjectTasks(name, session, context);
|
|
2304
|
+
}
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
async function resolveRepositoryId(context) {
|
|
2308
|
+
if (context.repositoryId !== void 0) {
|
|
2309
|
+
return context.repositoryId;
|
|
2310
|
+
}
|
|
2311
|
+
const result = await context.client.graphql(REPOSITORY_QUERY, {
|
|
2312
|
+
owner: context.repoOwner,
|
|
2313
|
+
repo: context.repoName
|
|
2314
|
+
});
|
|
2315
|
+
const repositoryId = result.repository?.id ?? null;
|
|
2316
|
+
if (repositoryId === null) {
|
|
2317
|
+
throw new Error(
|
|
2318
|
+
`Repository ${context.repoOwner}/${context.repoName} not found or inaccessible.`
|
|
2319
|
+
);
|
|
2320
|
+
}
|
|
2321
|
+
context.repositoryId = repositoryId;
|
|
2322
|
+
return repositoryId;
|
|
2323
|
+
}
|
|
2324
|
+
async function resolveIssueId(id, listName, context) {
|
|
2325
|
+
const issueNumber = parseIssueNumber(id, listName);
|
|
2326
|
+
const cachedIssueId = context.issueIds.get(issueNumber);
|
|
2327
|
+
if (cachedIssueId !== void 0) {
|
|
2328
|
+
return cachedIssueId;
|
|
2329
|
+
}
|
|
2330
|
+
const result = await context.client.graphql(ISSUE_ID_QUERY, {
|
|
2331
|
+
owner: context.repoOwner,
|
|
2332
|
+
repo: context.repoName,
|
|
2333
|
+
number: issueNumber
|
|
2334
|
+
});
|
|
2335
|
+
const issueId = result.repository?.issue?.id ?? null;
|
|
2336
|
+
if (issueId === null) {
|
|
2337
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2338
|
+
}
|
|
2339
|
+
context.issueIds.set(issueNumber, issueId);
|
|
2340
|
+
return issueId;
|
|
2341
|
+
}
|
|
2342
|
+
async function resolveProjectItemId(id, listName, session, context) {
|
|
2343
|
+
const issueNumber = parseIssueNumber(id, listName);
|
|
2344
|
+
const result = await context.client.graphql(ISSUE_PROJECT_ITEM_QUERY, {
|
|
2345
|
+
owner: context.repoOwner,
|
|
2346
|
+
repo: context.repoName,
|
|
2347
|
+
number: issueNumber
|
|
2348
|
+
});
|
|
2349
|
+
const issue = result.repository?.issue ?? null;
|
|
2350
|
+
if (issue === null) {
|
|
2351
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2352
|
+
}
|
|
2353
|
+
if (issue.id !== void 0 && issue.id !== null) {
|
|
2354
|
+
context.issueIds.set(issueNumber, issue.id);
|
|
2355
|
+
}
|
|
2356
|
+
const projectItem = issue.projectItems?.nodes?.find((item) => item.project?.id === session.projectId) ?? null;
|
|
2357
|
+
if (projectItem === null) {
|
|
2358
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2359
|
+
}
|
|
2360
|
+
return projectItem.id;
|
|
2361
|
+
}
|
|
2362
|
+
async function updateProjectItemStatus(projectItemId, state, session, context) {
|
|
2363
|
+
const optionId = session.statusOptions.get(state);
|
|
2364
|
+
if (optionId === void 0) {
|
|
2365
|
+
throw new InvalidTransitionError({
|
|
2366
|
+
to: state,
|
|
2367
|
+
reason: `Unknown gh-issues Status state "${state}".`
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
await context.client.graphql(UPDATE_STATUS_MUTATION, {
|
|
2371
|
+
input: {
|
|
2372
|
+
projectId: session.projectId,
|
|
2373
|
+
itemId: projectItemId,
|
|
2374
|
+
fieldId: session.statusFieldId,
|
|
2375
|
+
value: {
|
|
2376
|
+
singleSelectOptionId: optionId
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
async function updateProjectItemPosition(projectItemId, afterId, session, context) {
|
|
2382
|
+
await context.client.graphql(UPDATE_PROJECT_ITEM_POSITION_MUTATION, {
|
|
2383
|
+
input: {
|
|
2384
|
+
projectId: session.projectId,
|
|
2385
|
+
itemId: projectItemId,
|
|
2386
|
+
afterId
|
|
2387
|
+
}
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
async function resolveMoveAfterId(movingId, anchor, listName, session, context) {
|
|
2391
|
+
if ("position" in anchor) {
|
|
2392
|
+
if (anchor.position === "top") {
|
|
2393
|
+
return null;
|
|
2394
|
+
}
|
|
2395
|
+
const tasks2 = await fetchProjectTasks(listName, session, context);
|
|
2396
|
+
for (let index = tasks2.length - 1; index >= 0; index -= 1) {
|
|
2397
|
+
const task = tasks2[index];
|
|
2398
|
+
if (task.id !== movingId) {
|
|
2399
|
+
return projectItemIdFromTask(task);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
return null;
|
|
2403
|
+
}
|
|
2404
|
+
const anchorId = "before" in anchor ? anchor.before : anchor.after;
|
|
2405
|
+
let anchorProjectItemId;
|
|
2406
|
+
try {
|
|
2407
|
+
anchorProjectItemId = await resolveProjectItemId(anchorId, listName, session, context);
|
|
2408
|
+
} catch (error2) {
|
|
2409
|
+
if (error2 instanceof TaskNotFoundError) {
|
|
2410
|
+
throw new AnchorNotFoundError(anchorId);
|
|
2411
|
+
}
|
|
2412
|
+
throw error2;
|
|
2413
|
+
}
|
|
2414
|
+
if ("after" in anchor) {
|
|
2415
|
+
return anchorProjectItemId;
|
|
2416
|
+
}
|
|
2417
|
+
const tasks = await fetchProjectTasks(listName, session, context);
|
|
2418
|
+
const anchorIndex = tasks.findIndex((task) => task.id === anchorId);
|
|
2419
|
+
if (anchorIndex < 0) {
|
|
2420
|
+
throw new AnchorNotFoundError(anchorId);
|
|
2421
|
+
}
|
|
2422
|
+
for (let index = anchorIndex - 1; index >= 0; index -= 1) {
|
|
2423
|
+
const predecessor = tasks[index];
|
|
2424
|
+
if (predecessor.id !== movingId) {
|
|
2425
|
+
return projectItemIdFromTask(predecessor);
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
return null;
|
|
2429
|
+
}
|
|
2430
|
+
async function fetchProjectTasks(listName, session, context) {
|
|
2431
|
+
const tasks = [];
|
|
2432
|
+
let after = null;
|
|
2433
|
+
do {
|
|
2434
|
+
const result = await context.client.graphql(
|
|
2435
|
+
PROJECT_ITEMS_QUERY,
|
|
2436
|
+
{
|
|
2437
|
+
projectId: session.projectId,
|
|
2438
|
+
after
|
|
2439
|
+
}
|
|
2440
|
+
);
|
|
2441
|
+
const items = result.node?.items;
|
|
2442
|
+
for (const item of items?.nodes ?? []) {
|
|
2443
|
+
const task = mapProjectItemToTask(item, listName, session);
|
|
2444
|
+
if (task !== null) {
|
|
2445
|
+
tasks.push(task);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
after = items?.pageInfo?.hasNextPage === true ? items.pageInfo.endCursor ?? null : null;
|
|
2449
|
+
} while (after !== null);
|
|
2450
|
+
return tasks;
|
|
2451
|
+
}
|
|
2452
|
+
async function fetchIssueTask(id, listName, session, context) {
|
|
2453
|
+
const issueNumber = parseIssueNumber(id, listName);
|
|
2454
|
+
const result = await context.client.graphql(ISSUE_QUERY, {
|
|
2455
|
+
owner: context.repoOwner,
|
|
2456
|
+
repo: context.repoName,
|
|
2457
|
+
number: issueNumber
|
|
2458
|
+
});
|
|
2459
|
+
const issue = result.repository?.issue ?? null;
|
|
2460
|
+
if (issue === null) {
|
|
2461
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2462
|
+
}
|
|
2463
|
+
const projectItem = issue.projectItems?.nodes?.find((item) => item.project?.id === session.projectId) ?? null;
|
|
2464
|
+
if (projectItem === null) {
|
|
2465
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2466
|
+
}
|
|
2467
|
+
return mapIssueToTask({
|
|
2468
|
+
issue,
|
|
2469
|
+
projectItemId: projectItem.id,
|
|
2470
|
+
statusName: projectItem.fieldValueByName?.name ?? null,
|
|
2471
|
+
listName,
|
|
2472
|
+
initialState: session.stateMachine.initial
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2475
|
+
function parseIssueNumber(id, listName) {
|
|
2476
|
+
const issueNumber = Number(id);
|
|
2477
|
+
if (!Number.isInteger(issueNumber) || issueNumber < 1) {
|
|
2478
|
+
throw new TaskNotFoundError(`Task "${listName}/${id}" not found.`);
|
|
2479
|
+
}
|
|
2480
|
+
return issueNumber;
|
|
2481
|
+
}
|
|
2482
|
+
function mapProjectItemToTask(item, listName, session) {
|
|
2483
|
+
const content = item.content;
|
|
2484
|
+
if (!isIssueNode(content)) {
|
|
2485
|
+
return null;
|
|
2486
|
+
}
|
|
2487
|
+
return mapIssueToTask({
|
|
2488
|
+
issue: content,
|
|
2489
|
+
projectItemId: item.id,
|
|
2490
|
+
statusName: item.fieldValueByName?.name ?? null,
|
|
2491
|
+
listName,
|
|
2492
|
+
initialState: session.stateMachine.initial
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
function isIssueNode(value) {
|
|
2496
|
+
return isRecord3(value) && value.__typename === "Issue" && typeof value.number === "number" && typeof value.title === "string" && typeof value.url === "string" && typeof value.createdAt === "string";
|
|
2497
|
+
}
|
|
2498
|
+
function mapIssueToTask(options) {
|
|
2499
|
+
const id = String(options.issue.number);
|
|
2500
|
+
const labels = (options.issue.labels?.nodes ?? []).filter((node) => node !== null).map((node) => node.name);
|
|
2501
|
+
const assignees = (options.issue.assignees?.nodes ?? []).filter((node) => node !== null).map((node) => node.login);
|
|
2502
|
+
return {
|
|
2503
|
+
list: options.listName,
|
|
2504
|
+
id,
|
|
2505
|
+
qualifiedId: `${options.listName}/${id}`,
|
|
2506
|
+
name: options.issue.title,
|
|
2507
|
+
description: options.issue.body ?? "",
|
|
2508
|
+
state: options.statusName ?? options.initialState,
|
|
2509
|
+
metadata: {
|
|
2510
|
+
url: options.issue.url,
|
|
2511
|
+
labels,
|
|
2512
|
+
assignees,
|
|
2513
|
+
milestone: options.issue.milestone?.title ?? null,
|
|
2514
|
+
projectItemId: options.projectItemId,
|
|
2515
|
+
created: options.issue.createdAt
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
function projectItemIdFromTask(task) {
|
|
2520
|
+
const projectItemId = task.metadata.projectItemId;
|
|
2521
|
+
if (typeof projectItemId !== "string") {
|
|
2522
|
+
throw new Error(`Task "${task.qualifiedId}" is missing GitHub project item metadata.`);
|
|
2523
|
+
}
|
|
2524
|
+
return projectItemId;
|
|
2525
|
+
}
|
|
2526
|
+
function parseRepo(repo) {
|
|
2527
|
+
const parts = repo.split("/");
|
|
2528
|
+
if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
|
|
2529
|
+
throw new Error(`Invalid GitHub repository "${repo}". Expected "owner/name".`);
|
|
2530
|
+
}
|
|
2531
|
+
return {
|
|
2532
|
+
owner: parts[0],
|
|
2533
|
+
name: parts[1]
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
function isRecord3(value) {
|
|
2537
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2538
|
+
}
|
|
2539
|
+
function isStatusOption(value) {
|
|
2540
|
+
return isRecord3(value) && typeof value.id === "string" && typeof value.name === "string";
|
|
2541
|
+
}
|
|
2542
|
+
function isStatusField(value) {
|
|
2543
|
+
return isRecord3(value) && typeof value.id === "string" && Array.isArray(value.options) && value.options.every(isStatusOption);
|
|
2544
|
+
}
|
|
2545
|
+
function assertSingleList(name, listName) {
|
|
2546
|
+
if (name !== listName) {
|
|
2547
|
+
throw singleListError(listName);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
function singleListError(listName) {
|
|
2551
|
+
return new Error(`gh-issues backend has a single list ${listName}`);
|
|
2552
|
+
}
|
|
2553
|
+
function parseQualifiedId(qualifiedId, listName) {
|
|
2554
|
+
const prefix = `${listName}/`;
|
|
2555
|
+
if (!qualifiedId.startsWith(prefix) || qualifiedId.length === prefix.length) {
|
|
2556
|
+
throw new Error(`Invalid qualified task id "${qualifiedId}".`);
|
|
2557
|
+
}
|
|
2558
|
+
return qualifiedId.slice(prefix.length);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// ../task-list/src/backends/markdown-dir.ts
|
|
2562
|
+
import path5 from "node:path";
|
|
2563
|
+
|
|
2564
|
+
// ../file-lock/src/lock.ts
|
|
2565
|
+
import * as fsPromises from "node:fs/promises";
|
|
2566
|
+
import * as os from "node:os";
|
|
2567
|
+
var LockTimeoutError = class extends Error {
|
|
2568
|
+
constructor(message2) {
|
|
2569
|
+
super(message2);
|
|
2570
|
+
this.name = "LockTimeoutError";
|
|
2571
|
+
}
|
|
2572
|
+
};
|
|
2573
|
+
function createAbortError() {
|
|
2574
|
+
const error2 = new Error("The operation was aborted.");
|
|
2575
|
+
error2.name = "AbortError";
|
|
2576
|
+
return error2;
|
|
2577
|
+
}
|
|
2578
|
+
function throwIfAborted(signal) {
|
|
2579
|
+
if (signal?.aborted) {
|
|
2580
|
+
throw createAbortError();
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function sleep(ms, signal) {
|
|
2584
|
+
if (!signal) {
|
|
2585
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2586
|
+
}
|
|
2587
|
+
if (signal.aborted) {
|
|
2588
|
+
return Promise.reject(createAbortError());
|
|
2589
|
+
}
|
|
2590
|
+
return new Promise((resolve, reject) => {
|
|
2591
|
+
const timeoutId = setTimeout(() => {
|
|
2592
|
+
signal.removeEventListener("abort", onAbort);
|
|
2593
|
+
resolve();
|
|
2594
|
+
}, ms);
|
|
2595
|
+
const onAbort = () => {
|
|
2596
|
+
clearTimeout(timeoutId);
|
|
2597
|
+
signal.removeEventListener("abort", onAbort);
|
|
2598
|
+
reject(createAbortError());
|
|
2599
|
+
};
|
|
2600
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
function backoff(attempt, minTimeout, maxTimeout) {
|
|
2604
|
+
const delay = Math.min(maxTimeout, minTimeout * 2 ** attempt);
|
|
2605
|
+
return delay + Math.random() * delay * 0.1;
|
|
2606
|
+
}
|
|
2607
|
+
function hasErrorCode2(error2, code) {
|
|
2608
|
+
return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
|
|
2609
|
+
}
|
|
2610
|
+
function hasAnyErrorCode(error2, codes) {
|
|
2611
|
+
return codes.some((code) => hasErrorCode2(error2, code));
|
|
2612
|
+
}
|
|
2613
|
+
function isPidRunning(pid) {
|
|
2614
|
+
try {
|
|
2615
|
+
process.kill(pid, 0);
|
|
2616
|
+
return true;
|
|
2617
|
+
} catch (error2) {
|
|
2618
|
+
return !hasErrorCode2(error2, "ESRCH");
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
function createDefaultFs() {
|
|
2622
|
+
return {
|
|
2623
|
+
open: (path15, flags) => fsPromises.open(path15, flags),
|
|
2624
|
+
readFile: (path15, encoding) => fsPromises.readFile(path15, encoding),
|
|
2625
|
+
stat: fsPromises.stat,
|
|
2626
|
+
unlink: fsPromises.unlink
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
async function removeLockFile(fs2, lockPath, signal) {
|
|
2630
|
+
for (let attempt = 0; attempt <= 4; attempt += 1) {
|
|
2631
|
+
throwIfAborted(signal);
|
|
2632
|
+
try {
|
|
2633
|
+
await fs2.unlink(lockPath);
|
|
2634
|
+
return;
|
|
2635
|
+
} catch (error2) {
|
|
2636
|
+
if (hasErrorCode2(error2, "ENOENT")) {
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
if (!hasAnyErrorCode(error2, ["EPERM", "EBUSY"]) || attempt === 4) {
|
|
2640
|
+
throw error2;
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
await sleep(25 * 2 ** attempt, signal);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
function parseLockMetadata(content) {
|
|
2647
|
+
try {
|
|
2648
|
+
const parsed = JSON.parse(content);
|
|
2649
|
+
if (!parsed || typeof parsed !== "object" || !("host" in parsed) || !("pid" in parsed)) {
|
|
2650
|
+
return void 0;
|
|
2651
|
+
}
|
|
2652
|
+
const { host, pid } = parsed;
|
|
2653
|
+
if (typeof host === "string" && typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0) {
|
|
2654
|
+
return {
|
|
2655
|
+
host,
|
|
2656
|
+
pid
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
} catch (ignoredError) {
|
|
2660
|
+
void ignoredError;
|
|
2661
|
+
}
|
|
2662
|
+
return void 0;
|
|
2663
|
+
}
|
|
2664
|
+
async function readLockMetadata(fs2, lockPath) {
|
|
2665
|
+
if (!fs2.readFile) {
|
|
2666
|
+
return void 0;
|
|
2667
|
+
}
|
|
2668
|
+
try {
|
|
2669
|
+
return parseLockMetadata(await fs2.readFile(lockPath, "utf8"));
|
|
2670
|
+
} catch (error2) {
|
|
2671
|
+
if (hasErrorCode2(error2, "ENOENT")) {
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
return void 0;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
async function shouldReclaimLock(options) {
|
|
2678
|
+
const metadata = await readLockMetadata(options.fs, options.lockPath);
|
|
2679
|
+
if (metadata === null) {
|
|
2680
|
+
return "missing";
|
|
2681
|
+
}
|
|
2682
|
+
if (metadata?.host === os.hostname()) {
|
|
2683
|
+
return !options.isPidRunning(metadata.pid);
|
|
2684
|
+
}
|
|
2685
|
+
return Date.now() - options.stat.mtimeMs > options.staleMs;
|
|
2686
|
+
}
|
|
2687
|
+
async function writeLockMetadata(handle) {
|
|
2688
|
+
try {
|
|
1461
2689
|
await handle.writeFile(
|
|
1462
2690
|
JSON.stringify({ pid: process.pid, host: os.hostname(), acquiredAt: (/* @__PURE__ */ new Date()).toISOString() }),
|
|
1463
2691
|
{ encoding: "utf8" }
|
|
@@ -1494,7 +2722,7 @@ async function acquireFileLock(filePath, options = {}) {
|
|
|
1494
2722
|
await removeLockFile(fs2, lockPath, options.signal);
|
|
1495
2723
|
};
|
|
1496
2724
|
} catch (error2) {
|
|
1497
|
-
if (!
|
|
2725
|
+
if (!hasErrorCode2(error2, "EEXIST")) {
|
|
1498
2726
|
throw error2;
|
|
1499
2727
|
}
|
|
1500
2728
|
}
|
|
@@ -1502,7 +2730,7 @@ async function acquireFileLock(filePath, options = {}) {
|
|
|
1502
2730
|
try {
|
|
1503
2731
|
stat3 = await fs2.stat(lockPath);
|
|
1504
2732
|
} catch (statError) {
|
|
1505
|
-
if (
|
|
2733
|
+
if (hasErrorCode2(statError, "ENOENT")) {
|
|
1506
2734
|
continue;
|
|
1507
2735
|
}
|
|
1508
2736
|
throw statError;
|
|
@@ -1568,112 +2796,6 @@ var task_schema_default = {
|
|
|
1568
2796
|
additionalProperties: true
|
|
1569
2797
|
};
|
|
1570
2798
|
|
|
1571
|
-
// ../task-list/src/state-machine.ts
|
|
1572
|
-
function isRecord(value) {
|
|
1573
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1574
|
-
}
|
|
1575
|
-
function isStateList(value) {
|
|
1576
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1577
|
-
}
|
|
1578
|
-
function canFireFromState(event, fromState) {
|
|
1579
|
-
if (event.from === "*") {
|
|
1580
|
-
return event.to !== fromState;
|
|
1581
|
-
}
|
|
1582
|
-
return event.from.includes(fromState);
|
|
1583
|
-
}
|
|
1584
|
-
function validateMachine(machine) {
|
|
1585
|
-
if (!isRecord(machine)) {
|
|
1586
|
-
throw new TypeError("State machine must be an object.");
|
|
1587
|
-
}
|
|
1588
|
-
if (!isStateList(machine.states)) {
|
|
1589
|
-
throw new TypeError("State machine states must be a string array.");
|
|
1590
|
-
}
|
|
1591
|
-
const states = new Set(machine.states);
|
|
1592
|
-
if (typeof machine.initial !== "string") {
|
|
1593
|
-
throw new TypeError("State machine initial must be a string.");
|
|
1594
|
-
}
|
|
1595
|
-
if (!states.has(machine.initial)) {
|
|
1596
|
-
throw new Error(`Initial state "${machine.initial}" is not declared.`);
|
|
1597
|
-
}
|
|
1598
|
-
if (!isRecord(machine.events)) {
|
|
1599
|
-
throw new TypeError("State machine events must be an object.");
|
|
1600
|
-
}
|
|
1601
|
-
for (const [eventName, event] of Object.entries(machine.events)) {
|
|
1602
|
-
if (!isRecord(event)) {
|
|
1603
|
-
throw new TypeError(`Event "${eventName}" must be an object.`);
|
|
1604
|
-
}
|
|
1605
|
-
if (event.from !== "*" && !isStateList(event.from)) {
|
|
1606
|
-
throw new TypeError(`Event "${eventName}" has an invalid "from" definition.`);
|
|
1607
|
-
}
|
|
1608
|
-
if (typeof event.to !== "string") {
|
|
1609
|
-
throw new TypeError(`Event "${eventName}" target state must be a string.`);
|
|
1610
|
-
}
|
|
1611
|
-
if (!states.has(event.to)) {
|
|
1612
|
-
throw new Error(`Event "${eventName}" references unknown target state "${event.to}".`);
|
|
1613
|
-
}
|
|
1614
|
-
if (event.from !== "*") {
|
|
1615
|
-
for (const fromState of event.from) {
|
|
1616
|
-
if (!states.has(fromState)) {
|
|
1617
|
-
throw new Error(`Event "${eventName}" references unknown source state "${fromState}".`);
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
function eventsFromState(machine, fromState) {
|
|
1624
|
-
const events = [];
|
|
1625
|
-
for (const [eventName, event] of Object.entries(machine.events)) {
|
|
1626
|
-
if (canFireFromState(event, fromState)) {
|
|
1627
|
-
events.push(eventName);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
return events;
|
|
1631
|
-
}
|
|
1632
|
-
function findEvent(machine, fromState, eventName) {
|
|
1633
|
-
const event = machine.events[eventName];
|
|
1634
|
-
if (event === void 0) {
|
|
1635
|
-
return void 0;
|
|
1636
|
-
}
|
|
1637
|
-
return canFireFromState(event, fromState) ? event : void 0;
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
// ../task-list/src/types.ts
|
|
1641
|
-
var TaskNotFoundError = class extends Error {
|
|
1642
|
-
constructor(message2 = "Task not found.") {
|
|
1643
|
-
super(message2);
|
|
1644
|
-
this.name = "TaskNotFoundError";
|
|
1645
|
-
}
|
|
1646
|
-
};
|
|
1647
|
-
var TaskAlreadyExistsError = class extends Error {
|
|
1648
|
-
constructor(message2 = "Task already exists.") {
|
|
1649
|
-
super(message2);
|
|
1650
|
-
this.name = "TaskAlreadyExistsError";
|
|
1651
|
-
}
|
|
1652
|
-
};
|
|
1653
|
-
var InvalidTransitionError = class extends Error {
|
|
1654
|
-
task;
|
|
1655
|
-
event;
|
|
1656
|
-
to;
|
|
1657
|
-
reason;
|
|
1658
|
-
constructor(messageOrOptions = "Invalid task transition.") {
|
|
1659
|
-
const options = typeof messageOrOptions === "string" ? {
|
|
1660
|
-
reason: messageOrOptions
|
|
1661
|
-
} : messageOrOptions;
|
|
1662
|
-
super(options.reason);
|
|
1663
|
-
this.name = "InvalidTransitionError";
|
|
1664
|
-
this.task = options.task;
|
|
1665
|
-
this.event = options.event;
|
|
1666
|
-
this.to = options.to;
|
|
1667
|
-
this.reason = options.reason;
|
|
1668
|
-
}
|
|
1669
|
-
};
|
|
1670
|
-
var MalformedTaskError = class extends Error {
|
|
1671
|
-
constructor(message2 = "Malformed task.") {
|
|
1672
|
-
super(message2);
|
|
1673
|
-
this.name = "MalformedTaskError";
|
|
1674
|
-
}
|
|
1675
|
-
};
|
|
1676
|
-
|
|
1677
2799
|
// ../task-list/src/state.ts
|
|
1678
2800
|
var defaultStateMachine = {
|
|
1679
2801
|
initial: "draft",
|
|
@@ -1709,64 +2831,17 @@ function resolveStateMachine(stateMachine) {
|
|
|
1709
2831
|
return stateMachine ?? defaultStateMachine;
|
|
1710
2832
|
}
|
|
1711
2833
|
|
|
1712
|
-
// ../task-list/src/backends/utils.ts
|
|
1713
|
-
import path2 from "node:path";
|
|
1714
|
-
var tmpFileCounter = 0;
|
|
1715
|
-
function hasErrorCode2(error2, code) {
|
|
1716
|
-
return !!error2 && typeof error2 === "object" && "code" in error2 && error2.code === code;
|
|
1717
|
-
}
|
|
1718
|
-
function isRecord2(value) {
|
|
1719
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1720
|
-
}
|
|
1721
|
-
function sortStrings(values) {
|
|
1722
|
-
return [...values].sort((left, right) => left.localeCompare(right));
|
|
1723
|
-
}
|
|
1724
|
-
function sortTasks(tasks) {
|
|
1725
|
-
return [...tasks].sort((left, right) => left.qualifiedId.localeCompare(right.qualifiedId));
|
|
1726
|
-
}
|
|
1727
|
-
function validateTaskId(id) {
|
|
1728
|
-
if (id.length === 0 || id.startsWith(".") || id.includes("/") || id.includes("\\") || id.includes("..")) {
|
|
1729
|
-
throw new Error(`Invalid task id "${id}".`);
|
|
1730
|
-
}
|
|
1731
|
-
return id;
|
|
1732
|
-
}
|
|
1733
|
-
async function statIfExists(fs2, filePath) {
|
|
1734
|
-
try {
|
|
1735
|
-
return await fs2.stat(filePath);
|
|
1736
|
-
} catch (error2) {
|
|
1737
|
-
if (hasErrorCode2(error2, "ENOENT")) {
|
|
1738
|
-
return void 0;
|
|
1739
|
-
}
|
|
1740
|
-
throw error2;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
async function writeAtomically(fs2, filePath, content) {
|
|
1744
|
-
const tempPath = `${filePath}.tmp-${process.pid}-${tmpFileCounter}`;
|
|
1745
|
-
tmpFileCounter += 1;
|
|
1746
|
-
await fs2.mkdir(path2.dirname(filePath), { recursive: true });
|
|
1747
|
-
try {
|
|
1748
|
-
await fs2.writeFile(tempPath, content, { encoding: "utf8", flag: "wx" });
|
|
1749
|
-
await fs2.rename(tempPath, filePath);
|
|
1750
|
-
} catch (error2) {
|
|
1751
|
-
try {
|
|
1752
|
-
await fs2.unlink(tempPath);
|
|
1753
|
-
} catch (unlinkError) {
|
|
1754
|
-
if (!hasErrorCode2(unlinkError, "ENOENT")) {
|
|
1755
|
-
throw unlinkError;
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
throw error2;
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
2834
|
// ../task-list/src/backends/markdown-dir.ts
|
|
1763
2835
|
var ARCHIVE_DIRECTORY_NAME = "archive";
|
|
1764
2836
|
var MARKDOWN_EXTENSION = ".md";
|
|
1765
2837
|
var TASK_KIND = "task";
|
|
1766
2838
|
var TASK_VERSION = 1;
|
|
1767
2839
|
var TASK_SCHEMA_ID = task_schema_default.$id;
|
|
2840
|
+
var ORDER_LOCK_FILENAME = ".order.lock";
|
|
2841
|
+
var MIN_PREFIX_WIDTH = 2;
|
|
1768
2842
|
var RESERVED_FRONTMATTER_KEYS = /* @__PURE__ */ new Set([
|
|
1769
2843
|
"$schema",
|
|
2844
|
+
"created",
|
|
1770
2845
|
"description",
|
|
1771
2846
|
"kind",
|
|
1772
2847
|
"name",
|
|
@@ -1779,7 +2854,7 @@ function validateListName(name) {
|
|
|
1779
2854
|
}
|
|
1780
2855
|
return name;
|
|
1781
2856
|
}
|
|
1782
|
-
function
|
|
2857
|
+
function parseQualifiedId2(qualifiedId) {
|
|
1783
2858
|
const separatorIndex = qualifiedId.indexOf("/");
|
|
1784
2859
|
if (separatorIndex <= 0 || separatorIndex !== qualifiedId.lastIndexOf("/") || separatorIndex === qualifiedId.length - 1) {
|
|
1785
2860
|
throw new Error(`Invalid qualified task id "${qualifiedId}".`);
|
|
@@ -1790,16 +2865,16 @@ function parseQualifiedId(qualifiedId) {
|
|
|
1790
2865
|
};
|
|
1791
2866
|
}
|
|
1792
2867
|
function listPath(rootPath, list) {
|
|
1793
|
-
return
|
|
2868
|
+
return path5.join(rootPath, list);
|
|
1794
2869
|
}
|
|
1795
2870
|
function archiveDirectoryPath(rootPath, list) {
|
|
1796
|
-
return
|
|
2871
|
+
return path5.join(listPath(rootPath, list), ARCHIVE_DIRECTORY_NAME);
|
|
1797
2872
|
}
|
|
1798
|
-
function
|
|
1799
|
-
return
|
|
2873
|
+
function activeTaskFilename(id, order, width) {
|
|
2874
|
+
return `${String(order).padStart(width, "0")}-${id}${MARKDOWN_EXTENSION}`;
|
|
1800
2875
|
}
|
|
1801
2876
|
function archivedTaskPath(rootPath, list, id) {
|
|
1802
|
-
return
|
|
2877
|
+
return path5.join(archiveDirectoryPath(rootPath, list), `${id}${MARKDOWN_EXTENSION}`);
|
|
1803
2878
|
}
|
|
1804
2879
|
function isMarkdownFile(entryName) {
|
|
1805
2880
|
return entryName.endsWith(MARKDOWN_EXTENSION);
|
|
@@ -1810,6 +2885,27 @@ function isHiddenEntry(entryName) {
|
|
|
1810
2885
|
function isLockFile(entryName) {
|
|
1811
2886
|
return entryName.endsWith(".lock");
|
|
1812
2887
|
}
|
|
2888
|
+
function isValidTaskIdShape(id) {
|
|
2889
|
+
return id.length > 0 && !id.startsWith(".") && !id.includes("/") && !id.includes("\\") && !id.includes("..");
|
|
2890
|
+
}
|
|
2891
|
+
function parseActiveFilename(entryName) {
|
|
2892
|
+
if (!isMarkdownFile(entryName)) return void 0;
|
|
2893
|
+
const stem = entryName.slice(0, -MARKDOWN_EXTENSION.length);
|
|
2894
|
+
const match = /^(\d+)-(.+)$/.exec(stem);
|
|
2895
|
+
if (match) {
|
|
2896
|
+
const id = match[2];
|
|
2897
|
+
if (isValidTaskIdShape(id)) {
|
|
2898
|
+
return { id, order: Number.parseInt(match[1], 10) };
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
if (isValidTaskIdShape(stem)) {
|
|
2902
|
+
return { id: stem, order: null };
|
|
2903
|
+
}
|
|
2904
|
+
return void 0;
|
|
2905
|
+
}
|
|
2906
|
+
function padWidthForCount(count) {
|
|
2907
|
+
return Math.max(MIN_PREFIX_WIDTH, String(Math.max(count, 1)).length);
|
|
2908
|
+
}
|
|
1813
2909
|
function malformedTask(filePath, field) {
|
|
1814
2910
|
return new MalformedTaskError(`Malformed task "${filePath}": invalid "${field}".`);
|
|
1815
2911
|
}
|
|
@@ -1903,7 +2999,7 @@ async function readDirectoryNames(fs2, directoryPath) {
|
|
|
1903
2999
|
try {
|
|
1904
3000
|
return sortStrings(await fs2.readdir(directoryPath));
|
|
1905
3001
|
} catch (error2) {
|
|
1906
|
-
if (
|
|
3002
|
+
if (hasErrorCode(error2, "ENOENT")) {
|
|
1907
3003
|
return [];
|
|
1908
3004
|
}
|
|
1909
3005
|
throw error2;
|
|
@@ -1927,22 +3023,31 @@ async function readTaskFile(fs2, list, id, filePath, validStates) {
|
|
|
1927
3023
|
task: createTask(list, id, frontmatter, document.body)
|
|
1928
3024
|
};
|
|
1929
3025
|
}
|
|
3026
|
+
async function findActiveTaskFilename(fs2, listDirectoryPath, id) {
|
|
3027
|
+
const entries = await readDirectoryNames(fs2, listDirectoryPath);
|
|
3028
|
+
for (const entryName of entries) {
|
|
3029
|
+
if (isHiddenEntry(entryName) || isLockFile(entryName)) continue;
|
|
3030
|
+
const parsed = parseActiveFilename(entryName);
|
|
3031
|
+
if (parsed?.id === id) {
|
|
3032
|
+
return entryName;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
return void 0;
|
|
3036
|
+
}
|
|
1930
3037
|
async function findTaskLocation(fs2, rootPath, list, id) {
|
|
1931
|
-
const
|
|
1932
|
-
const
|
|
1933
|
-
if (
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
3038
|
+
const listDirectoryPath = listPath(rootPath, list);
|
|
3039
|
+
const activeName = await findActiveTaskFilename(fs2, listDirectoryPath, id);
|
|
3040
|
+
if (activeName) {
|
|
3041
|
+
const activePath = path5.join(listDirectoryPath, activeName);
|
|
3042
|
+
const activeStat = await statIfExists(fs2, activePath);
|
|
3043
|
+
if (activeStat?.isFile()) {
|
|
3044
|
+
return { archived: false, path: activePath };
|
|
3045
|
+
}
|
|
1938
3046
|
}
|
|
1939
3047
|
const archivedPath = archivedTaskPath(rootPath, list, id);
|
|
1940
3048
|
const archivedStat = await statIfExists(fs2, archivedPath);
|
|
1941
3049
|
if (archivedStat?.isFile()) {
|
|
1942
|
-
return {
|
|
1943
|
-
archived: true,
|
|
1944
|
-
path: archivedPath
|
|
1945
|
-
};
|
|
3050
|
+
return { archived: true, path: archivedPath };
|
|
1946
3051
|
}
|
|
1947
3052
|
return void 0;
|
|
1948
3053
|
}
|
|
@@ -1971,6 +3076,7 @@ function createdFrontmatter(defaults, input, initialState) {
|
|
|
1971
3076
|
frontmatter[key2] = value;
|
|
1972
3077
|
}
|
|
1973
3078
|
}
|
|
3079
|
+
frontmatter.created = (/* @__PURE__ */ new Date()).toISOString();
|
|
1974
3080
|
return frontmatter;
|
|
1975
3081
|
}
|
|
1976
3082
|
function updatedFrontmatter(existingFrontmatter, task, patch) {
|
|
@@ -2015,35 +3121,95 @@ function assertCreateDoesNotSetState(input) {
|
|
|
2015
3121
|
);
|
|
2016
3122
|
}
|
|
2017
3123
|
}
|
|
3124
|
+
function assertCreateHasId(input) {
|
|
3125
|
+
if (input.id === void 0) {
|
|
3126
|
+
throw new Error("id is required for markdown-dir backend");
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
2018
3129
|
function assertUpdateDoesNotSetState(patch) {
|
|
2019
3130
|
if (Object.prototype.hasOwnProperty.call(patch, "state")) {
|
|
2020
3131
|
throw new Error('Tasks.update() does not accept "state"; use fire() to change task state.');
|
|
2021
3132
|
}
|
|
2022
3133
|
}
|
|
2023
|
-
function
|
|
3134
|
+
function createTasksView2(deps, list) {
|
|
2024
3135
|
const listDirectoryPath = listPath(deps.path, list);
|
|
2025
3136
|
const stateMachine = resolveStateMachine(deps.stateMachine);
|
|
2026
3137
|
const validStates = new Set(stateMachine.states);
|
|
2027
|
-
async function
|
|
2028
|
-
const entries = await readDirectoryNames(deps.fs,
|
|
2029
|
-
const
|
|
3138
|
+
async function readActiveEntries() {
|
|
3139
|
+
const entries = await readDirectoryNames(deps.fs, listDirectoryPath);
|
|
3140
|
+
const result = [];
|
|
2030
3141
|
for (const entryName of entries) {
|
|
2031
|
-
if (isHiddenEntry(entryName) || isLockFile(entryName)
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
const entryPath =
|
|
3142
|
+
if (isHiddenEntry(entryName) || isLockFile(entryName)) continue;
|
|
3143
|
+
const parsed = parseActiveFilename(entryName);
|
|
3144
|
+
if (!parsed) continue;
|
|
3145
|
+
const entryPath = path5.join(listDirectoryPath, entryName);
|
|
2035
3146
|
const entryStat = await statIfExists(deps.fs, entryPath);
|
|
2036
|
-
if (!entryStat?.isFile())
|
|
2037
|
-
|
|
2038
|
-
|
|
3147
|
+
if (!entryStat?.isFile()) continue;
|
|
3148
|
+
result.push({ id: parsed.id, order: parsed.order, filename: entryName });
|
|
3149
|
+
}
|
|
3150
|
+
result.sort((left, right) => {
|
|
3151
|
+
const leftOrder = left.order ?? Number.POSITIVE_INFINITY;
|
|
3152
|
+
const rightOrder = right.order ?? Number.POSITIVE_INFINITY;
|
|
3153
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
3154
|
+
return left.filename.localeCompare(right.filename);
|
|
3155
|
+
});
|
|
3156
|
+
return result;
|
|
3157
|
+
}
|
|
3158
|
+
async function readActiveTasks() {
|
|
3159
|
+
const entries = await readActiveEntries();
|
|
3160
|
+
const tasks = /* @__PURE__ */ new Map();
|
|
3161
|
+
for (const entry of entries) {
|
|
3162
|
+
const filePath = path5.join(listDirectoryPath, entry.filename);
|
|
3163
|
+
const file = await readTaskFile(deps.fs, list, entry.id, filePath, validStates);
|
|
3164
|
+
tasks.set(entry.id, { task: file.task, raw: file.frontmatter });
|
|
3165
|
+
}
|
|
3166
|
+
return { entries, tasks };
|
|
3167
|
+
}
|
|
3168
|
+
async function readArchivedTasks() {
|
|
3169
|
+
const archivePath = archiveDirectoryPath(deps.path, list);
|
|
3170
|
+
const entries = await readDirectoryNames(deps.fs, archivePath);
|
|
3171
|
+
const result = [];
|
|
3172
|
+
for (const entryName of entries) {
|
|
3173
|
+
if (isHiddenEntry(entryName) || isLockFile(entryName) || !isMarkdownFile(entryName)) continue;
|
|
3174
|
+
const entryPath = path5.join(archivePath, entryName);
|
|
3175
|
+
const entryStat = await statIfExists(deps.fs, entryPath);
|
|
3176
|
+
if (!entryStat?.isFile()) continue;
|
|
2039
3177
|
const id = entryName.slice(0, -MARKDOWN_EXTENSION.length);
|
|
2040
|
-
|
|
3178
|
+
const file = await readTaskFile(deps.fs, list, id, entryPath, validStates);
|
|
3179
|
+
result.push({ task: file.task, raw: file.frontmatter });
|
|
3180
|
+
}
|
|
3181
|
+
return result.sort((left, right) => left.task.qualifiedId.localeCompare(right.task.qualifiedId));
|
|
3182
|
+
}
|
|
3183
|
+
async function rewriteListPrefixes(orderedIds) {
|
|
3184
|
+
const entries = await readActiveEntries();
|
|
3185
|
+
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
3186
|
+
const width = padWidthForCount(orderedIds.length);
|
|
3187
|
+
for (let index = 0; index < orderedIds.length; index += 1) {
|
|
3188
|
+
const id = orderedIds[index];
|
|
3189
|
+
const entry = byId.get(id);
|
|
3190
|
+
if (!entry) continue;
|
|
3191
|
+
const desiredFilename = activeTaskFilename(id, index + 1, width);
|
|
3192
|
+
if (entry.filename !== desiredFilename) {
|
|
3193
|
+
const fromPath = path5.join(listDirectoryPath, entry.filename);
|
|
3194
|
+
const stagingPath = path5.join(listDirectoryPath, `${desiredFilename}.staging-${process.pid}-${index}`);
|
|
3195
|
+
await deps.fs.rename(fromPath, stagingPath);
|
|
3196
|
+
entry.filename = path5.basename(stagingPath);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
const refreshed = await readDirectoryNames(deps.fs, listDirectoryPath);
|
|
3200
|
+
for (const entryName of refreshed) {
|
|
3201
|
+
const stagingMatch = /\.staging-\d+-\d+$/.exec(entryName);
|
|
3202
|
+
if (!stagingMatch) continue;
|
|
3203
|
+
const desiredName = entryName.slice(0, stagingMatch.index);
|
|
3204
|
+
await deps.fs.rename(
|
|
3205
|
+
path5.join(listDirectoryPath, entryName),
|
|
3206
|
+
path5.join(listDirectoryPath, desiredName)
|
|
3207
|
+
);
|
|
2041
3208
|
}
|
|
2042
|
-
return sortTasks(tasks);
|
|
2043
3209
|
}
|
|
2044
|
-
async function
|
|
2045
|
-
|
|
2046
|
-
const release = await acquireFileLock(
|
|
3210
|
+
async function withListLock(action) {
|
|
3211
|
+
await deps.fs.mkdir(listDirectoryPath, { recursive: true });
|
|
3212
|
+
const release = await acquireFileLock(path5.join(listDirectoryPath, ORDER_LOCK_FILENAME), {
|
|
2047
3213
|
fs: deps.fs,
|
|
2048
3214
|
staleMs: deps.lockStaleMs,
|
|
2049
3215
|
retries: deps.lockRetries
|
|
@@ -2054,6 +3220,10 @@ function createTasksView(deps, list) {
|
|
|
2054
3220
|
await release();
|
|
2055
3221
|
}
|
|
2056
3222
|
}
|
|
3223
|
+
async function withTaskLock(id, action) {
|
|
3224
|
+
validateTaskId(id);
|
|
3225
|
+
return withListLock(action);
|
|
3226
|
+
}
|
|
2057
3227
|
async function getTaskFile(id) {
|
|
2058
3228
|
validateTaskId(id);
|
|
2059
3229
|
return readTaskAtLocation(deps.fs, deps.path, list, id, validStates);
|
|
@@ -2074,27 +3244,41 @@ function createTasksView(deps, list) {
|
|
|
2074
3244
|
name: list,
|
|
2075
3245
|
stateMachine,
|
|
2076
3246
|
async all(filter) {
|
|
2077
|
-
const activeTasks = await
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2080
|
-
|
|
2081
|
-
return
|
|
2082
|
-
}
|
|
2083
|
-
|
|
3247
|
+
const { entries: activeEntries, tasks: activeTasks } = await readActiveTasks();
|
|
3248
|
+
const archivedEntries = filter?.includeArchived ? await readArchivedTasks() : [];
|
|
3249
|
+
const orderedActive = activeEntries.map((entry) => activeTasks.get(entry.id)).filter((entry) => {
|
|
3250
|
+
if (filter?.state && entry.task.state !== filter.state) return false;
|
|
3251
|
+
return true;
|
|
3252
|
+
});
|
|
3253
|
+
const filteredArchived = archivedEntries.filter((entry) => {
|
|
3254
|
+
if (filter?.state && entry.task.state !== filter.state) return false;
|
|
3255
|
+
return true;
|
|
3256
|
+
});
|
|
3257
|
+
const orderedActiveTasks = applyOrder(orderedActive, filter?.order);
|
|
3258
|
+
return [...orderedActiveTasks, ...filteredArchived.map((entry) => entry.task)];
|
|
2084
3259
|
},
|
|
2085
3260
|
async get(id) {
|
|
2086
3261
|
return (await getTaskFile(id)).task;
|
|
2087
3262
|
},
|
|
2088
3263
|
async create(input) {
|
|
2089
3264
|
assertCreateDoesNotSetState(input);
|
|
3265
|
+
assertCreateHasId(input);
|
|
2090
3266
|
validateTaskId(input.id);
|
|
2091
3267
|
await deps.fs.mkdir(listDirectoryPath, { recursive: true });
|
|
2092
|
-
return
|
|
3268
|
+
return withListLock(async () => {
|
|
2093
3269
|
const existing = await findTaskLocation(deps.fs, deps.path, list, input.id);
|
|
2094
3270
|
if (existing) {
|
|
2095
3271
|
throw new TaskAlreadyExistsError(`Task "${list}/${input.id}" already exists.`);
|
|
2096
3272
|
}
|
|
2097
|
-
const
|
|
3273
|
+
const activeEntries = await readActiveEntries();
|
|
3274
|
+
const maxOrder = activeEntries.reduce(
|
|
3275
|
+
(max, entry) => entry.order !== null && entry.order > max ? entry.order : max,
|
|
3276
|
+
0
|
|
3277
|
+
);
|
|
3278
|
+
const nextOrder = maxOrder + 1;
|
|
3279
|
+
const width = padWidthForCount(activeEntries.length + 1);
|
|
3280
|
+
const filename = activeTaskFilename(input.id, nextOrder, width);
|
|
3281
|
+
const targetPath = path5.join(listDirectoryPath, filename);
|
|
2098
3282
|
const frontmatter = createdFrontmatter(deps.defaults, input, stateMachine.initial);
|
|
2099
3283
|
const description = input.description ?? "";
|
|
2100
3284
|
await writeAtomically(deps.fs, targetPath, serializeTaskDocument(frontmatter, description));
|
|
@@ -2170,14 +3354,60 @@ function createTasksView(deps, list) {
|
|
|
2170
3354
|
}
|
|
2171
3355
|
await deps.fs.unlink(location.path);
|
|
2172
3356
|
});
|
|
3357
|
+
},
|
|
3358
|
+
async move(id, anchor) {
|
|
3359
|
+
validateTaskId(id);
|
|
3360
|
+
return withListLock(async () => {
|
|
3361
|
+
const { entries, tasks } = await readActiveTasks();
|
|
3362
|
+
const fromIndex = entries.findIndex((entry) => entry.id === id);
|
|
3363
|
+
if (fromIndex < 0) {
|
|
3364
|
+
throw new TaskNotFoundError(`Task "${list}/${id}" not found.`);
|
|
3365
|
+
}
|
|
3366
|
+
const ordered = entries.map((entry) => entry.id);
|
|
3367
|
+
ordered.splice(fromIndex, 1);
|
|
3368
|
+
let insertIndex;
|
|
3369
|
+
if ("position" in anchor) {
|
|
3370
|
+
insertIndex = anchor.position === "top" ? 0 : ordered.length;
|
|
3371
|
+
} else {
|
|
3372
|
+
const anchorId = "before" in anchor ? anchor.before : anchor.after;
|
|
3373
|
+
const anchorIndex = ordered.indexOf(anchorId);
|
|
3374
|
+
if (anchorIndex < 0) {
|
|
3375
|
+
throw new AnchorNotFoundError(anchorId);
|
|
3376
|
+
}
|
|
3377
|
+
insertIndex = "before" in anchor ? anchorIndex : anchorIndex + 1;
|
|
3378
|
+
}
|
|
3379
|
+
ordered.splice(insertIndex, 0, id);
|
|
3380
|
+
await rewriteListPrefixes(ordered);
|
|
3381
|
+
return tasks.get(id).task;
|
|
3382
|
+
});
|
|
3383
|
+
},
|
|
3384
|
+
async reorder(ids) {
|
|
3385
|
+
for (const id of ids) {
|
|
3386
|
+
validateTaskId(id);
|
|
3387
|
+
}
|
|
3388
|
+
return withListLock(async () => {
|
|
3389
|
+
const { entries, tasks } = await readActiveTasks();
|
|
3390
|
+
const currentIds = entries.map((entry) => entry.id);
|
|
3391
|
+
const currentSet = new Set(currentIds);
|
|
3392
|
+
const inputSet = new Set(ids);
|
|
3393
|
+
const missing = currentIds.filter((id) => !inputSet.has(id));
|
|
3394
|
+
const extra = ids.filter((id) => !currentSet.has(id));
|
|
3395
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
3396
|
+
throw new OrderMismatchError({ missing, extra });
|
|
3397
|
+
}
|
|
3398
|
+
await rewriteListPrefixes(ids);
|
|
3399
|
+
return ids.map((id) => tasks.get(id).task);
|
|
3400
|
+
});
|
|
2173
3401
|
}
|
|
2174
3402
|
};
|
|
2175
3403
|
}
|
|
2176
3404
|
async function markdownDirBackend(deps) {
|
|
2177
3405
|
await ensureRootPath(deps);
|
|
3406
|
+
const stateMachine = resolveStateMachine(deps.stateMachine);
|
|
3407
|
+
const validStates = new Set(stateMachine.states);
|
|
2178
3408
|
const list = (name) => {
|
|
2179
3409
|
const listName = validateListName(name);
|
|
2180
|
-
return
|
|
3410
|
+
return createTasksView2(deps, listName);
|
|
2181
3411
|
};
|
|
2182
3412
|
const lists = async () => {
|
|
2183
3413
|
const entries = await readDirectoryNames(deps.fs, deps.path);
|
|
@@ -2186,7 +3416,7 @@ async function markdownDirBackend(deps) {
|
|
|
2186
3416
|
if (entryName === ARCHIVE_DIRECTORY_NAME || isHiddenEntry(entryName) || isLockFile(entryName)) {
|
|
2187
3417
|
continue;
|
|
2188
3418
|
}
|
|
2189
|
-
const entryPath =
|
|
3419
|
+
const entryPath = path5.join(deps.path, entryName);
|
|
2190
3420
|
const entryStat = await statIfExists(deps.fs, entryPath);
|
|
2191
3421
|
if (entryStat?.isDirectory()) {
|
|
2192
3422
|
result.push(entryName);
|
|
@@ -2200,22 +3430,70 @@ async function markdownDirBackend(deps) {
|
|
|
2200
3430
|
for (const taskListName of allLists) {
|
|
2201
3431
|
tasks.push(...await list(taskListName).all(filter));
|
|
2202
3432
|
}
|
|
2203
|
-
return
|
|
3433
|
+
return tasks;
|
|
2204
3434
|
};
|
|
2205
3435
|
const get = async (qualifiedId) => {
|
|
2206
|
-
const { list: listName, id } =
|
|
3436
|
+
const { list: listName, id } = parseQualifiedId2(qualifiedId);
|
|
2207
3437
|
return list(listName).get(id);
|
|
2208
3438
|
};
|
|
3439
|
+
const moveBetweenLists = async (qualifiedId, targetList) => {
|
|
3440
|
+
const { list: sourceListName, id } = parseQualifiedId2(qualifiedId);
|
|
3441
|
+
const targetListName = validateListName(targetList);
|
|
3442
|
+
if (sourceListName === targetListName) {
|
|
3443
|
+
const file2 = await readTaskAtLocation(deps.fs, deps.path, sourceListName, id, validStates);
|
|
3444
|
+
return file2.task;
|
|
3445
|
+
}
|
|
3446
|
+
const targetExisting = await findTaskLocation(deps.fs, deps.path, targetListName, id);
|
|
3447
|
+
if (targetExisting) {
|
|
3448
|
+
throw new TaskAlreadyExistsError(`Task "${targetListName}/${id}" already exists.`);
|
|
3449
|
+
}
|
|
3450
|
+
const sourceLocation = await findTaskLocation(deps.fs, deps.path, sourceListName, id);
|
|
3451
|
+
if (!sourceLocation) {
|
|
3452
|
+
throw new TaskNotFoundError(`Task "${sourceListName}/${id}" not found.`);
|
|
3453
|
+
}
|
|
3454
|
+
const targetListDir = listPath(deps.path, targetListName);
|
|
3455
|
+
await deps.fs.mkdir(targetListDir, { recursive: true });
|
|
3456
|
+
const targetEntries = await (async () => {
|
|
3457
|
+
const out = [];
|
|
3458
|
+
const names = await readDirectoryNames(deps.fs, targetListDir);
|
|
3459
|
+
for (const entryName of names) {
|
|
3460
|
+
if (isHiddenEntry(entryName) || isLockFile(entryName)) continue;
|
|
3461
|
+
const parsed = parseActiveFilename(entryName);
|
|
3462
|
+
if (!parsed) continue;
|
|
3463
|
+
out.push({ id: parsed.id, order: parsed.order, filename: entryName });
|
|
3464
|
+
}
|
|
3465
|
+
return out;
|
|
3466
|
+
})();
|
|
3467
|
+
if (sourceLocation.archived) {
|
|
3468
|
+
const archivedTargetDir = archiveDirectoryPath(deps.path, targetListName);
|
|
3469
|
+
await deps.fs.mkdir(archivedTargetDir, { recursive: true });
|
|
3470
|
+
const archivedTargetPath = archivedTaskPath(deps.path, targetListName, id);
|
|
3471
|
+
await deps.fs.rename(sourceLocation.path, archivedTargetPath);
|
|
3472
|
+
const file2 = await readTaskFile(deps.fs, targetListName, id, archivedTargetPath, validStates);
|
|
3473
|
+
return file2.task;
|
|
3474
|
+
}
|
|
3475
|
+
const maxOrder = targetEntries.reduce(
|
|
3476
|
+
(max, entry) => entry.order !== null && entry.order > max ? entry.order : max,
|
|
3477
|
+
0
|
|
3478
|
+
);
|
|
3479
|
+
const width = padWidthForCount(targetEntries.length + 1);
|
|
3480
|
+
const targetFilename = activeTaskFilename(id, maxOrder + 1, width);
|
|
3481
|
+
const targetPath = path5.join(targetListDir, targetFilename);
|
|
3482
|
+
await deps.fs.rename(sourceLocation.path, targetPath);
|
|
3483
|
+
const file = await readTaskFile(deps.fs, targetListName, id, targetPath, validStates);
|
|
3484
|
+
return file.task;
|
|
3485
|
+
};
|
|
2209
3486
|
return {
|
|
2210
3487
|
list,
|
|
2211
3488
|
lists,
|
|
2212
3489
|
allTasks,
|
|
2213
|
-
get
|
|
3490
|
+
get,
|
|
3491
|
+
moveBetweenLists
|
|
2214
3492
|
};
|
|
2215
3493
|
}
|
|
2216
3494
|
|
|
2217
3495
|
// ../task-list/src/backends/yaml-file.ts
|
|
2218
|
-
import { parseDocument as parseDocument2 } from "yaml";
|
|
3496
|
+
import { isMap, parseDocument as parseDocument2 } from "yaml";
|
|
2219
3497
|
|
|
2220
3498
|
// ../task-list/src/schema/store.schema.json
|
|
2221
3499
|
var store_schema_default = {
|
|
@@ -2258,7 +3536,15 @@ var STORE_VERSION = 1;
|
|
|
2258
3536
|
var TASK_KIND2 = "task";
|
|
2259
3537
|
var TASK_SCHEMA_ID2 = task_schema_default.$id;
|
|
2260
3538
|
var TASK_VERSION2 = 1;
|
|
2261
|
-
var RESERVED_TASK_KEYS = /* @__PURE__ */ new Set([
|
|
3539
|
+
var RESERVED_TASK_KEYS = /* @__PURE__ */ new Set([
|
|
3540
|
+
"$schema",
|
|
3541
|
+
"created",
|
|
3542
|
+
"description",
|
|
3543
|
+
"kind",
|
|
3544
|
+
"name",
|
|
3545
|
+
"state",
|
|
3546
|
+
"version"
|
|
3547
|
+
]);
|
|
2262
3548
|
function malformedStore(filePath, field) {
|
|
2263
3549
|
return new MalformedTaskError(`Malformed task store "${filePath}": invalid "${field}".`);
|
|
2264
3550
|
}
|
|
@@ -2271,7 +3557,7 @@ function validateListName2(name) {
|
|
|
2271
3557
|
}
|
|
2272
3558
|
return name;
|
|
2273
3559
|
}
|
|
2274
|
-
function
|
|
3560
|
+
function parseQualifiedId3(qualifiedId) {
|
|
2275
3561
|
const separatorIndex = qualifiedId.indexOf("/");
|
|
2276
3562
|
if (separatorIndex <= 0 || separatorIndex !== qualifiedId.lastIndexOf("/") || separatorIndex === qualifiedId.length - 1) {
|
|
2277
3563
|
throw new Error(`Invalid qualified task id "${qualifiedId}".`);
|
|
@@ -2329,6 +3615,7 @@ function createTaskRecord(defaults, input, initialState) {
|
|
|
2329
3615
|
taskRecord[key2] = value;
|
|
2330
3616
|
}
|
|
2331
3617
|
}
|
|
3618
|
+
taskRecord.created = (/* @__PURE__ */ new Date()).toISOString();
|
|
2332
3619
|
return taskRecord;
|
|
2333
3620
|
}
|
|
2334
3621
|
function assertCreateDoesNotSetState2(input) {
|
|
@@ -2338,6 +3625,11 @@ function assertCreateDoesNotSetState2(input) {
|
|
|
2338
3625
|
);
|
|
2339
3626
|
}
|
|
2340
3627
|
}
|
|
3628
|
+
function assertCreateHasId2(input) {
|
|
3629
|
+
if (input.id === void 0) {
|
|
3630
|
+
throw new Error("id is required for yaml-file backend");
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
2341
3633
|
function assertUpdateDoesNotSetState2(patch) {
|
|
2342
3634
|
if (Object.prototype.hasOwnProperty.call(patch, "state")) {
|
|
2343
3635
|
throw new Error('Tasks.update() does not accept "state"; use fire() to change task state.');
|
|
@@ -2486,6 +3778,48 @@ function getTaskOrThrow(store, list, id) {
|
|
|
2486
3778
|
}
|
|
2487
3779
|
return taskRecord;
|
|
2488
3780
|
}
|
|
3781
|
+
function getListNode(document, list) {
|
|
3782
|
+
const lists = document.get("lists");
|
|
3783
|
+
if (!isMap(lists)) {
|
|
3784
|
+
return void 0;
|
|
3785
|
+
}
|
|
3786
|
+
const listNode = lists.get(list);
|
|
3787
|
+
if (!isMap(listNode)) {
|
|
3788
|
+
return void 0;
|
|
3789
|
+
}
|
|
3790
|
+
return listNode;
|
|
3791
|
+
}
|
|
3792
|
+
function pairKey(pair) {
|
|
3793
|
+
const key2 = pair.key;
|
|
3794
|
+
if (typeof key2 === "string") {
|
|
3795
|
+
return key2;
|
|
3796
|
+
}
|
|
3797
|
+
if (key2 && typeof key2 === "object" && "value" in key2 && typeof key2.value === "string") {
|
|
3798
|
+
return key2.value;
|
|
3799
|
+
}
|
|
3800
|
+
return void 0;
|
|
3801
|
+
}
|
|
3802
|
+
function findItemIndex(listNode, id) {
|
|
3803
|
+
return listNode.items.findIndex((pair) => pairKey(pair) === id);
|
|
3804
|
+
}
|
|
3805
|
+
function activeItemIds(listNode, validStates) {
|
|
3806
|
+
const ids = [];
|
|
3807
|
+
for (const pair of listNode.items) {
|
|
3808
|
+
const id = pairKey(pair);
|
|
3809
|
+
if (id === void 0) continue;
|
|
3810
|
+
const value = pair.value;
|
|
3811
|
+
let state;
|
|
3812
|
+
if (value && typeof value === "object" && "get" in value && typeof value.get === "function") {
|
|
3813
|
+
state = value.get("state");
|
|
3814
|
+
} else if (isRecord2(value)) {
|
|
3815
|
+
state = value.state;
|
|
3816
|
+
}
|
|
3817
|
+
if (typeof state === "string" && validStates.has(state) && state !== "archived") {
|
|
3818
|
+
ids.push(id);
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
return ids;
|
|
3822
|
+
}
|
|
2489
3823
|
async function ensureStorePath(deps) {
|
|
2490
3824
|
if (!deps.create) {
|
|
2491
3825
|
await deps.fs.stat(deps.path);
|
|
@@ -2523,7 +3857,7 @@ async function withStoreLock(deps, action) {
|
|
|
2523
3857
|
await release();
|
|
2524
3858
|
}
|
|
2525
3859
|
}
|
|
2526
|
-
function
|
|
3860
|
+
function createTasksView3(deps, list) {
|
|
2527
3861
|
const stateMachine = resolveStateMachine(deps.stateMachine);
|
|
2528
3862
|
const validStates = new Set(stateMachine.states);
|
|
2529
3863
|
async function readTasks(filter) {
|
|
@@ -2532,8 +3866,11 @@ function createTasksView2(deps, list) {
|
|
|
2532
3866
|
if (!listRecord) {
|
|
2533
3867
|
return [];
|
|
2534
3868
|
}
|
|
2535
|
-
const
|
|
2536
|
-
|
|
3869
|
+
const entries = Object.entries(listRecord).map(([id, taskRecord]) => ({
|
|
3870
|
+
task: createTask2(list, id, taskRecord),
|
|
3871
|
+
raw: taskRecord
|
|
3872
|
+
})).filter(({ task }) => matchesFilter(task, filter));
|
|
3873
|
+
return applyOrder(entries, filter?.order);
|
|
2537
3874
|
}
|
|
2538
3875
|
function assertFireableTaskEvent(task, eventName) {
|
|
2539
3876
|
const event = findEvent(stateMachine, task.state, eventName);
|
|
@@ -2560,6 +3897,7 @@ function createTasksView2(deps, list) {
|
|
|
2560
3897
|
},
|
|
2561
3898
|
async create(input) {
|
|
2562
3899
|
assertCreateDoesNotSetState2(input);
|
|
3900
|
+
assertCreateHasId2(input);
|
|
2563
3901
|
validateTaskId(input.id);
|
|
2564
3902
|
return withStoreLock(deps, async () => {
|
|
2565
3903
|
const { document, store } = await readStore(deps.fs, deps.path, validStates);
|
|
@@ -2648,6 +3986,72 @@ function createTasksView2(deps, list) {
|
|
|
2648
3986
|
document.deleteIn(["lists", list, id]);
|
|
2649
3987
|
await writeAtomically(deps.fs, deps.path, serializeDocument(document));
|
|
2650
3988
|
});
|
|
3989
|
+
},
|
|
3990
|
+
async move(id, anchor) {
|
|
3991
|
+
validateTaskId(id);
|
|
3992
|
+
return withStoreLock(deps, async () => {
|
|
3993
|
+
const { document, store } = await readStore(deps.fs, deps.path, validStates);
|
|
3994
|
+
const taskRecord = getTaskOrThrow(store, list, id);
|
|
3995
|
+
const listNode = getListNode(document, list);
|
|
3996
|
+
if (!listNode) {
|
|
3997
|
+
throw new TaskNotFoundError(`Task "${list}/${id}" not found.`);
|
|
3998
|
+
}
|
|
3999
|
+
const fromIndex = findItemIndex(listNode, id);
|
|
4000
|
+
if (fromIndex < 0) {
|
|
4001
|
+
throw new TaskNotFoundError(`Task "${list}/${id}" not found.`);
|
|
4002
|
+
}
|
|
4003
|
+
const [movedPair] = listNode.items.splice(fromIndex, 1);
|
|
4004
|
+
let insertIndex;
|
|
4005
|
+
if ("position" in anchor) {
|
|
4006
|
+
insertIndex = anchor.position === "top" ? 0 : listNode.items.length;
|
|
4007
|
+
} else {
|
|
4008
|
+
const anchorId = "before" in anchor ? anchor.before : anchor.after;
|
|
4009
|
+
const anchorIndex = findItemIndex(listNode, anchorId);
|
|
4010
|
+
if (anchorIndex < 0) {
|
|
4011
|
+
listNode.items.splice(fromIndex, 0, movedPair);
|
|
4012
|
+
throw new AnchorNotFoundError(anchorId);
|
|
4013
|
+
}
|
|
4014
|
+
insertIndex = "before" in anchor ? anchorIndex : anchorIndex + 1;
|
|
4015
|
+
}
|
|
4016
|
+
listNode.items.splice(insertIndex, 0, movedPair);
|
|
4017
|
+
await writeAtomically(deps.fs, deps.path, serializeDocument(document));
|
|
4018
|
+
return createTask2(list, id, taskRecord);
|
|
4019
|
+
});
|
|
4020
|
+
},
|
|
4021
|
+
async reorder(ids) {
|
|
4022
|
+
for (const id of ids) {
|
|
4023
|
+
validateTaskId(id);
|
|
4024
|
+
}
|
|
4025
|
+
return withStoreLock(deps, async () => {
|
|
4026
|
+
const { document, store } = await readStore(deps.fs, deps.path, validStates);
|
|
4027
|
+
const listNode = getListNode(document, list);
|
|
4028
|
+
if (!listNode) {
|
|
4029
|
+
throw new OrderMismatchError({ missing: [...ids], extra: [] });
|
|
4030
|
+
}
|
|
4031
|
+
const currentActive = activeItemIds(listNode, validStates);
|
|
4032
|
+
const currentSet = new Set(currentActive);
|
|
4033
|
+
const inputSet = new Set(ids);
|
|
4034
|
+
const missing = currentActive.filter((id) => !inputSet.has(id));
|
|
4035
|
+
const extra = ids.filter((id) => !currentSet.has(id));
|
|
4036
|
+
if (missing.length > 0 || extra.length > 0) {
|
|
4037
|
+
throw new OrderMismatchError({ missing, extra });
|
|
4038
|
+
}
|
|
4039
|
+
const archivedPairs = listNode.items.filter((pair) => {
|
|
4040
|
+
const id = pairKey(pair);
|
|
4041
|
+
return id !== void 0 && !currentSet.has(id);
|
|
4042
|
+
});
|
|
4043
|
+
const orderedActive = ids.map((id) => {
|
|
4044
|
+
const pair = listNode.items.find((p) => pairKey(p) === id);
|
|
4045
|
+
if (!pair) {
|
|
4046
|
+
throw new OrderMismatchError({ missing: [id], extra: [] });
|
|
4047
|
+
}
|
|
4048
|
+
return pair;
|
|
4049
|
+
});
|
|
4050
|
+
listNode.items.splice(0, listNode.items.length, ...orderedActive, ...archivedPairs);
|
|
4051
|
+
await writeAtomically(deps.fs, deps.path, serializeDocument(document));
|
|
4052
|
+
const tasks = ids.map((id) => createTask2(list, id, getTaskOrThrow(store, list, id)));
|
|
4053
|
+
return tasks;
|
|
4054
|
+
});
|
|
2651
4055
|
}
|
|
2652
4056
|
};
|
|
2653
4057
|
}
|
|
@@ -2657,7 +4061,7 @@ async function yamlFileBackend(deps) {
|
|
|
2657
4061
|
const validStates = new Set(stateMachine.states);
|
|
2658
4062
|
const list = (name) => {
|
|
2659
4063
|
const listName = validateListName2(name);
|
|
2660
|
-
return
|
|
4064
|
+
return createTasksView3({ ...deps, stateMachine }, listName);
|
|
2661
4065
|
};
|
|
2662
4066
|
const lists = async () => {
|
|
2663
4067
|
const { store } = await readStore(deps.fs, deps.path, validStates);
|
|
@@ -2665,26 +4069,47 @@ async function yamlFileBackend(deps) {
|
|
|
2665
4069
|
};
|
|
2666
4070
|
const allTasks = async (filter) => {
|
|
2667
4071
|
const { store } = await readStore(deps.fs, deps.path, validStates);
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
4072
|
+
const result = [];
|
|
4073
|
+
const listNames = sortStrings(Object.keys(getListsRecord(store)));
|
|
4074
|
+
for (const listName of listNames) {
|
|
4075
|
+
const listRecord = getListsRecord(store)[listName];
|
|
4076
|
+
if (!isRecord2(listRecord)) continue;
|
|
4077
|
+
const entries = Object.entries(listRecord).map(([id, taskRecord]) => ({
|
|
4078
|
+
task: createTask2(listName, id, taskRecord),
|
|
4079
|
+
raw: taskRecord
|
|
4080
|
+
})).filter(({ task }) => matchesFilter(task, filter));
|
|
4081
|
+
result.push(...applyOrder(entries, filter?.order));
|
|
2676
4082
|
}
|
|
2677
|
-
return
|
|
4083
|
+
return result;
|
|
2678
4084
|
};
|
|
2679
4085
|
const get = async (qualifiedId) => {
|
|
2680
|
-
const { list: listName, id } =
|
|
4086
|
+
const { list: listName, id } = parseQualifiedId3(qualifiedId);
|
|
2681
4087
|
return list(listName).get(id);
|
|
2682
4088
|
};
|
|
4089
|
+
const moveBetweenLists = async (qualifiedId, targetList) => {
|
|
4090
|
+
const { list: sourceListName, id } = parseQualifiedId3(qualifiedId);
|
|
4091
|
+
const targetListName = validateListName2(targetList);
|
|
4092
|
+
return withStoreLock(deps, async () => {
|
|
4093
|
+
const { document, store } = await readStore(deps.fs, deps.path, validStates);
|
|
4094
|
+
const taskRecord = getTaskOrThrow(store, sourceListName, id);
|
|
4095
|
+
if (sourceListName === targetListName) {
|
|
4096
|
+
return createTask2(targetListName, id, taskRecord);
|
|
4097
|
+
}
|
|
4098
|
+
if (getTaskRecord(store, targetListName, id)) {
|
|
4099
|
+
throw new TaskAlreadyExistsError(`Task "${targetListName}/${id}" already exists.`);
|
|
4100
|
+
}
|
|
4101
|
+
document.deleteIn(["lists", sourceListName, id]);
|
|
4102
|
+
document.setIn(["lists", targetListName, id], taskRecord);
|
|
4103
|
+
await writeAtomically(deps.fs, deps.path, serializeDocument(document));
|
|
4104
|
+
return createTask2(targetListName, id, taskRecord);
|
|
4105
|
+
});
|
|
4106
|
+
};
|
|
2683
4107
|
return {
|
|
2684
4108
|
list,
|
|
2685
4109
|
lists,
|
|
2686
4110
|
allTasks,
|
|
2687
|
-
get
|
|
4111
|
+
get,
|
|
4112
|
+
moveBetweenLists
|
|
2688
4113
|
};
|
|
2689
4114
|
}
|
|
2690
4115
|
|
|
@@ -2699,10 +4124,18 @@ function createDefaultFs2() {
|
|
|
2699
4124
|
return fsPromises2;
|
|
2700
4125
|
}
|
|
2701
4126
|
async function openTaskList(options) {
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
4127
|
+
switch (options.type) {
|
|
4128
|
+
case "markdown-dir":
|
|
4129
|
+
case "yaml-file":
|
|
4130
|
+
return openFileBackend(options);
|
|
4131
|
+
case "gh-issues":
|
|
4132
|
+
return openGhIssuesBackend(options);
|
|
4133
|
+
default:
|
|
4134
|
+
throw new Error(`Unknown task list backend type "${options.type}".`);
|
|
2705
4135
|
}
|
|
4136
|
+
}
|
|
4137
|
+
async function openFileBackend(options) {
|
|
4138
|
+
const factory = backendFactories[options.type];
|
|
2706
4139
|
const stateMachine = resolveStateMachine(options.stateMachine);
|
|
2707
4140
|
validateMachine(stateMachine);
|
|
2708
4141
|
const deps = {
|
|
@@ -2718,9 +4151,23 @@ async function openTaskList(options) {
|
|
|
2718
4151
|
};
|
|
2719
4152
|
return factory(deps);
|
|
2720
4153
|
}
|
|
4154
|
+
async function openGhIssuesBackend(options) {
|
|
4155
|
+
const token = await resolveAuth({ explicitToken: options.auth?.token });
|
|
4156
|
+
const endpoint = resolveEndpoint();
|
|
4157
|
+
return ghIssuesBackend({
|
|
4158
|
+
repo: options.repo,
|
|
4159
|
+
project: options.project,
|
|
4160
|
+
defaults: {
|
|
4161
|
+
metadata: { ...options.defaults?.metadata ?? {} }
|
|
4162
|
+
},
|
|
4163
|
+
token,
|
|
4164
|
+
endpoint,
|
|
4165
|
+
fetch: options.fetch
|
|
4166
|
+
});
|
|
4167
|
+
}
|
|
2721
4168
|
|
|
2722
4169
|
// ../toolcraft/src/human-in-loop/approval-tasks.ts
|
|
2723
|
-
import { randomBytes } from "node:crypto";
|
|
4170
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
2724
4171
|
|
|
2725
4172
|
// ../toolcraft/src/human-in-loop/state-machine.ts
|
|
2726
4173
|
var approvalStateMachine = {
|
|
@@ -2803,7 +4250,7 @@ function isListValidated(runtimeOptions, listName) {
|
|
|
2803
4250
|
return validatedListsByRuntime.get(runtimeOptions)?.has(listName) ?? false;
|
|
2804
4251
|
}
|
|
2805
4252
|
function createApprovalRecord(payload, enqueuedAt) {
|
|
2806
|
-
const approvalId = `${enqueuedAt.slice(0, 19).replaceAll(":", "-")}-${
|
|
4253
|
+
const approvalId = `${enqueuedAt.slice(0, 19).replaceAll(":", "-")}-${randomBytes3(3).toString("hex")}`;
|
|
2807
4254
|
return {
|
|
2808
4255
|
approvalId,
|
|
2809
4256
|
name: `${payload.commandPath} (${enqueuedAt})`,
|
|
@@ -2886,7 +4333,7 @@ function areEqualStrings(left, right) {
|
|
|
2886
4333
|
}
|
|
2887
4334
|
|
|
2888
4335
|
// ../toolcraft/src/human-in-loop/runner.ts
|
|
2889
|
-
import { access, readFile as
|
|
4336
|
+
import { access, readFile as readFile3, writeFile } from "node:fs/promises";
|
|
2890
4337
|
|
|
2891
4338
|
// ../toolcraft/src/human-in-loop/default-provider.ts
|
|
2892
4339
|
import process2 from "node:process";
|
|
@@ -2994,13 +4441,13 @@ function defaultProviderForPlatform() {
|
|
|
2994
4441
|
}
|
|
2995
4442
|
|
|
2996
4443
|
// ../toolcraft/src/human-in-loop/spawn.ts
|
|
2997
|
-
import { spawn } from "node:child_process";
|
|
4444
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2998
4445
|
function spawnApprovalRunner(approvalId, runtimeOptions, spawnFn) {
|
|
2999
4446
|
const { execPath, entryArgs } = runtimeOptions.binPath ?? {
|
|
3000
4447
|
execPath: process.execPath,
|
|
3001
4448
|
entryArgs: [process.argv[1]]
|
|
3002
4449
|
};
|
|
3003
|
-
const fn = spawnFn ??
|
|
4450
|
+
const fn = spawnFn ?? spawn2;
|
|
3004
4451
|
const child = fn(execPath, [...entryArgs, "approvals", "run", approvalId], {
|
|
3005
4452
|
detached: true,
|
|
3006
4453
|
stdio: "ignore",
|
|
@@ -3185,13 +4632,13 @@ function createHandlerContext(command, params17) {
|
|
|
3185
4632
|
}
|
|
3186
4633
|
function createFs() {
|
|
3187
4634
|
return {
|
|
3188
|
-
readFile: async (
|
|
3189
|
-
writeFile: async (
|
|
3190
|
-
await writeFile(
|
|
4635
|
+
readFile: async (path15, encoding = "utf8") => readFile3(path15, { encoding }),
|
|
4636
|
+
writeFile: async (path15, contents) => {
|
|
4637
|
+
await writeFile(path15, contents);
|
|
3191
4638
|
},
|
|
3192
|
-
exists: async (
|
|
4639
|
+
exists: async (path15) => {
|
|
3193
4640
|
try {
|
|
3194
|
-
await access(
|
|
4641
|
+
await access(path15);
|
|
3195
4642
|
return true;
|
|
3196
4643
|
} catch {
|
|
3197
4644
|
return false;
|
|
@@ -3444,22 +4891,22 @@ function escapeMarkdownCell(value) {
|
|
|
3444
4891
|
|
|
3445
4892
|
// ../toolcraft/src/mcp-proxy.ts
|
|
3446
4893
|
import { existsSync as existsSync2 } from "node:fs";
|
|
3447
|
-
import { mkdir, readFile as
|
|
3448
|
-
import
|
|
4894
|
+
import { mkdir, readFile as readFile4, rename, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
4895
|
+
import path8 from "node:path";
|
|
3449
4896
|
|
|
3450
4897
|
// ../tiny-mcp-client/src/internal.ts
|
|
3451
|
-
import { spawn as
|
|
4898
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
3452
4899
|
import { PassThrough as PassThrough2 } from "node:stream";
|
|
3453
4900
|
|
|
3454
4901
|
// ../mcp-oauth/dist/client/auth-store-session-store.js
|
|
3455
4902
|
import crypto from "node:crypto";
|
|
3456
|
-
import
|
|
4903
|
+
import path7 from "node:path";
|
|
3457
4904
|
|
|
3458
4905
|
// ../auth-store/src/encrypted-file-store.ts
|
|
3459
|
-
import { createCipheriv, createDecipheriv, randomBytes as
|
|
4906
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes4, scrypt } from "node:crypto";
|
|
3460
4907
|
import { promises as fs } from "node:fs";
|
|
3461
4908
|
import { homedir, hostname as hostname2, userInfo } from "node:os";
|
|
3462
|
-
import
|
|
4909
|
+
import path6 from "node:path";
|
|
3463
4910
|
var derivedKeyCache = /* @__PURE__ */ new Map();
|
|
3464
4911
|
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
3465
4912
|
var ENCRYPTION_VERSION = 1;
|
|
@@ -3477,13 +4924,13 @@ var EncryptedFileStore = class {
|
|
|
3477
4924
|
constructor(input) {
|
|
3478
4925
|
this.fs = input.fs ?? fs;
|
|
3479
4926
|
this.salt = input.salt;
|
|
3480
|
-
this.filePath = input.filePath ??
|
|
4927
|
+
this.filePath = input.filePath ?? path6.join(
|
|
3481
4928
|
(input.getHomeDirectory ?? homedir)(),
|
|
3482
4929
|
input.defaultDirectory ?? ".auth-store",
|
|
3483
4930
|
input.defaultFileName ?? "credentials.enc"
|
|
3484
4931
|
);
|
|
3485
4932
|
this.getMachineIdentity = input.getMachineIdentity ?? defaultMachineIdentity;
|
|
3486
|
-
this.getRandomBytes = input.getRandomBytes ??
|
|
4933
|
+
this.getRandomBytes = input.getRandomBytes ?? randomBytes4;
|
|
3487
4934
|
}
|
|
3488
4935
|
async get() {
|
|
3489
4936
|
let rawDocument;
|
|
@@ -3530,7 +4977,7 @@ var EncryptedFileStore = class {
|
|
|
3530
4977
|
authTag: authTag.toString("base64"),
|
|
3531
4978
|
ciphertext: ciphertext.toString("base64")
|
|
3532
4979
|
};
|
|
3533
|
-
await this.fs.mkdir(
|
|
4980
|
+
await this.fs.mkdir(path6.dirname(this.filePath), { recursive: true });
|
|
3534
4981
|
await this.fs.writeFile(this.filePath, JSON.stringify(document), {
|
|
3535
4982
|
encoding: "utf8"
|
|
3536
4983
|
});
|
|
@@ -3581,7 +5028,7 @@ async function deriveEncryptionKey(getMachineIdentity, salt) {
|
|
|
3581
5028
|
function parseEncryptedDocument(raw) {
|
|
3582
5029
|
try {
|
|
3583
5030
|
const parsed = JSON.parse(raw);
|
|
3584
|
-
if (!
|
|
5031
|
+
if (!isRecord4(parsed)) {
|
|
3585
5032
|
return null;
|
|
3586
5033
|
}
|
|
3587
5034
|
if (parsed.version !== ENCRYPTION_VERSION) {
|
|
@@ -3600,7 +5047,7 @@ function parseEncryptedDocument(raw) {
|
|
|
3600
5047
|
return null;
|
|
3601
5048
|
}
|
|
3602
5049
|
}
|
|
3603
|
-
function
|
|
5050
|
+
function isRecord4(value) {
|
|
3604
5051
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
3605
5052
|
}
|
|
3606
5053
|
function isNotFoundError(error2) {
|
|
@@ -3610,7 +5057,7 @@ function isNotFoundError(error2) {
|
|
|
3610
5057
|
}
|
|
3611
5058
|
|
|
3612
5059
|
// ../auth-store/src/keychain-store.ts
|
|
3613
|
-
import { spawn as
|
|
5060
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3614
5061
|
var SECURITY_CLI = "security";
|
|
3615
5062
|
var KEYCHAIN_ITEM_NOT_FOUND_EXIT_CODE = 44;
|
|
3616
5063
|
var KeychainStore = class {
|
|
@@ -3674,7 +5121,7 @@ var KeychainStore = class {
|
|
|
3674
5121
|
};
|
|
3675
5122
|
function runSecurityCommand(command, args) {
|
|
3676
5123
|
return new Promise((resolve) => {
|
|
3677
|
-
const child =
|
|
5124
|
+
const child = spawn3(command, args, {
|
|
3678
5125
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3679
5126
|
});
|
|
3680
5127
|
let stdout = "";
|
|
@@ -3837,7 +5284,7 @@ function createAuthStoreClientStore(options) {
|
|
|
3837
5284
|
}
|
|
3838
5285
|
function createNamedSecretStore(key2, options, defaults) {
|
|
3839
5286
|
const hash = crypto.createHash("sha256").update(key2).digest("hex");
|
|
3840
|
-
const parsedFilePath = options.fileStore?.filePath === void 0 ? null :
|
|
5287
|
+
const parsedFilePath = options.fileStore?.filePath === void 0 ? null : path7.parse(options.fileStore.filePath);
|
|
3841
5288
|
const fileStore = {
|
|
3842
5289
|
...options.fileStore,
|
|
3843
5290
|
salt: options.fileStore?.salt ?? defaults.salt,
|
|
@@ -4043,8 +5490,8 @@ function validateAuthorizationCallbackParameters(callback, expected) {
|
|
|
4043
5490
|
}
|
|
4044
5491
|
return callback.code;
|
|
4045
5492
|
}
|
|
4046
|
-
function escapeHtml(
|
|
4047
|
-
return
|
|
5493
|
+
function escapeHtml(text5) {
|
|
5494
|
+
return text5.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
4048
5495
|
}
|
|
4049
5496
|
function buildSuccessPage(landingPage) {
|
|
4050
5497
|
const title = landingPage?.title ?? "Connected";
|
|
@@ -5431,7 +6878,7 @@ var ERROR_INVALID_REQUEST = -32600;
|
|
|
5431
6878
|
var ERROR_METHOD_NOT_FOUND = -32601;
|
|
5432
6879
|
var ERROR_INTERNAL = -32603;
|
|
5433
6880
|
function defaultStdioSpawn(command, args, options) {
|
|
5434
|
-
return
|
|
6881
|
+
return spawn4(command, args, options);
|
|
5435
6882
|
}
|
|
5436
6883
|
function defaultHttpTransportFetch(input, init) {
|
|
5437
6884
|
return fetch(input, init);
|
|
@@ -6742,9 +8189,9 @@ function mergeJsonSchemas(base, overlay) {
|
|
|
6742
8189
|
...mergedRequired === void 0 ? {} : { required: mergedRequired }
|
|
6743
8190
|
};
|
|
6744
8191
|
}
|
|
6745
|
-
function hasSelfReferencingRef(schema, root,
|
|
8192
|
+
function hasSelfReferencingRef(schema, root, path15 = "#", activePaths = /* @__PURE__ */ new Set()) {
|
|
6746
8193
|
const nextActivePaths = new Set(activePaths);
|
|
6747
|
-
nextActivePaths.add(
|
|
8194
|
+
nextActivePaths.add(path15);
|
|
6748
8195
|
const localRefPath = getLocalRefPath(schema.$ref);
|
|
6749
8196
|
if (localRefPath !== void 0) {
|
|
6750
8197
|
if (nextActivePaths.has(localRefPath)) {
|
|
@@ -6755,13 +8202,13 @@ function hasSelfReferencingRef(schema, root, path13 = "#", activePaths = /* @__P
|
|
|
6755
8202
|
return true;
|
|
6756
8203
|
}
|
|
6757
8204
|
}
|
|
6758
|
-
if (schema.items !== void 0 && hasSelfReferencingRef(schema.items, root, `${
|
|
8205
|
+
if (schema.items !== void 0 && hasSelfReferencingRef(schema.items, root, `${path15}/items`, nextActivePaths)) {
|
|
6759
8206
|
return true;
|
|
6760
8207
|
}
|
|
6761
8208
|
if (typeof schema.additionalProperties === "object" && schema.additionalProperties !== null && hasSelfReferencingRef(
|
|
6762
8209
|
schema.additionalProperties,
|
|
6763
8210
|
root,
|
|
6764
|
-
`${
|
|
8211
|
+
`${path15}/additionalProperties`,
|
|
6765
8212
|
nextActivePaths
|
|
6766
8213
|
)) {
|
|
6767
8214
|
return true;
|
|
@@ -6770,7 +8217,7 @@ function hasSelfReferencingRef(schema, root, path13 = "#", activePaths = /* @__P
|
|
|
6770
8217
|
if (hasSelfReferencingRef(
|
|
6771
8218
|
childSchema,
|
|
6772
8219
|
root,
|
|
6773
|
-
`${
|
|
8220
|
+
`${path15}/properties/${escapeJsonPointerSegment(key2)}`,
|
|
6774
8221
|
nextActivePaths
|
|
6775
8222
|
)) {
|
|
6776
8223
|
return true;
|
|
@@ -6780,19 +8227,19 @@ function hasSelfReferencingRef(schema, root, path13 = "#", activePaths = /* @__P
|
|
|
6780
8227
|
if (hasSelfReferencingRef(
|
|
6781
8228
|
childSchema,
|
|
6782
8229
|
root,
|
|
6783
|
-
`${
|
|
8230
|
+
`${path15}/$defs/${escapeJsonPointerSegment(key2)}`,
|
|
6784
8231
|
nextActivePaths
|
|
6785
8232
|
)) {
|
|
6786
8233
|
return true;
|
|
6787
8234
|
}
|
|
6788
8235
|
}
|
|
6789
8236
|
for (const [index, childSchema] of (schema.oneOf ?? []).entries()) {
|
|
6790
|
-
if (hasSelfReferencingRef(childSchema, root, `${
|
|
8237
|
+
if (hasSelfReferencingRef(childSchema, root, `${path15}/oneOf/${index}`, nextActivePaths)) {
|
|
6791
8238
|
return true;
|
|
6792
8239
|
}
|
|
6793
8240
|
}
|
|
6794
8241
|
for (const [index, childSchema] of (schema.anyOf ?? []).entries()) {
|
|
6795
|
-
if (hasSelfReferencingRef(childSchema, root, `${
|
|
8242
|
+
if (hasSelfReferencingRef(childSchema, root, `${path15}/anyOf/${index}`, nextActivePaths)) {
|
|
6796
8243
|
return true;
|
|
6797
8244
|
}
|
|
6798
8245
|
}
|
|
@@ -6808,14 +8255,14 @@ function getLocalRefPath(ref) {
|
|
|
6808
8255
|
return ref.startsWith("#/") ? ref : void 0;
|
|
6809
8256
|
}
|
|
6810
8257
|
function resolveLocalRef(root, ref) {
|
|
6811
|
-
const
|
|
6812
|
-
if (
|
|
8258
|
+
const path15 = getLocalRefPath(ref);
|
|
8259
|
+
if (path15 === void 0) {
|
|
6813
8260
|
return void 0;
|
|
6814
8261
|
}
|
|
6815
|
-
if (
|
|
8262
|
+
if (path15 === "#") {
|
|
6816
8263
|
return root;
|
|
6817
8264
|
}
|
|
6818
|
-
const segments =
|
|
8265
|
+
const segments = path15.slice(2).split("/").map(unescapeJsonPointerSegment);
|
|
6819
8266
|
let current = root;
|
|
6820
8267
|
for (const segment of segments) {
|
|
6821
8268
|
if (Array.isArray(current)) {
|
|
@@ -7053,7 +8500,7 @@ async function ensureConnected(connection) {
|
|
|
7053
8500
|
}
|
|
7054
8501
|
async function readCache(cachePath) {
|
|
7055
8502
|
try {
|
|
7056
|
-
const raw = await
|
|
8503
|
+
const raw = await readFile4(cachePath, "utf8");
|
|
7057
8504
|
const parsed = JSON.parse(raw);
|
|
7058
8505
|
if (parsed === null || typeof parsed !== "object" || !Array.isArray(parsed.tools) || parsed.upstream === void 0 || typeof parsed.upstream.name !== "string" || typeof parsed.upstream.version !== "string") {
|
|
7059
8506
|
return void 0;
|
|
@@ -7074,7 +8521,7 @@ async function readCache(cachePath) {
|
|
|
7074
8521
|
}
|
|
7075
8522
|
}
|
|
7076
8523
|
async function writeCache(cachePath, cache) {
|
|
7077
|
-
const directory =
|
|
8524
|
+
const directory = path8.dirname(cachePath);
|
|
7078
8525
|
const tempPath = `${cachePath}.tmp`;
|
|
7079
8526
|
await mkdir(directory, { recursive: true });
|
|
7080
8527
|
await writeFile2(tempPath, `${JSON.stringify(cache, null, 2)}
|
|
@@ -7217,14 +8664,14 @@ function collectProxyGroups(root) {
|
|
|
7217
8664
|
}
|
|
7218
8665
|
function resolveCachePath(name, projectRoot) {
|
|
7219
8666
|
if (projectRoot !== void 0) {
|
|
7220
|
-
return
|
|
8667
|
+
return path8.join(projectRoot, ".toolcraft", "mcp", `${name}.json`);
|
|
7221
8668
|
}
|
|
7222
8669
|
let current = process.cwd();
|
|
7223
8670
|
while (true) {
|
|
7224
|
-
if (existsSync2(
|
|
7225
|
-
return
|
|
8671
|
+
if (existsSync2(path8.join(current, "package.json"))) {
|
|
8672
|
+
return path8.join(current, ".toolcraft", "mcp", `${name}.json`);
|
|
7226
8673
|
}
|
|
7227
|
-
const parent =
|
|
8674
|
+
const parent = path8.dirname(current);
|
|
7228
8675
|
if (parent === current) {
|
|
7229
8676
|
throw new Error(
|
|
7230
8677
|
`Could not find package.json above "${process.cwd()}" while resolving MCP cache path.`
|
|
@@ -7276,9 +8723,49 @@ function getExpectedNumberDescription(schema) {
|
|
|
7276
8723
|
}
|
|
7277
8724
|
|
|
7278
8725
|
// ../toolcraft/src/renderer.ts
|
|
8726
|
+
import YAML from "yaml";
|
|
7279
8727
|
function isObject(value) {
|
|
7280
8728
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7281
8729
|
}
|
|
8730
|
+
function isMcpCallToolResult(value) {
|
|
8731
|
+
if (!isObject(value)) {
|
|
8732
|
+
return false;
|
|
8733
|
+
}
|
|
8734
|
+
const hasContent = Array.isArray(value.content);
|
|
8735
|
+
const hasStructured = value.structuredContent !== void 0;
|
|
8736
|
+
if (!hasContent && !hasStructured) {
|
|
8737
|
+
return false;
|
|
8738
|
+
}
|
|
8739
|
+
return Object.keys(value).every(
|
|
8740
|
+
(key2) => key2 === "content" || key2 === "structuredContent" || key2 === "isError" || key2 === "_meta"
|
|
8741
|
+
);
|
|
8742
|
+
}
|
|
8743
|
+
function isMcpTextContent(value) {
|
|
8744
|
+
return isObject(value) && value.type === "text" && typeof value.text === "string";
|
|
8745
|
+
}
|
|
8746
|
+
function extractMcpPayload(envelope) {
|
|
8747
|
+
const structuredContent = envelope.structuredContent;
|
|
8748
|
+
if (isObject(structuredContent) && "result" in structuredContent) {
|
|
8749
|
+
return structuredContent.result;
|
|
8750
|
+
}
|
|
8751
|
+
if (structuredContent !== void 0) {
|
|
8752
|
+
return structuredContent;
|
|
8753
|
+
}
|
|
8754
|
+
if (Array.isArray(envelope.content)) {
|
|
8755
|
+
const text5 = envelope.content.filter(isMcpTextContent).map((block) => block.text).join("\n");
|
|
8756
|
+
return text5.length > 0 ? text5 : void 0;
|
|
8757
|
+
}
|
|
8758
|
+
return void 0;
|
|
8759
|
+
}
|
|
8760
|
+
function unwrapMcpEnvelope(result) {
|
|
8761
|
+
if (!isMcpCallToolResult(result)) {
|
|
8762
|
+
return { result, mcpError: false };
|
|
8763
|
+
}
|
|
8764
|
+
return {
|
|
8765
|
+
result: extractMcpPayload(result),
|
|
8766
|
+
mcpError: result.isError === true
|
|
8767
|
+
};
|
|
8768
|
+
}
|
|
7282
8769
|
function isArrayOfObjects(value) {
|
|
7283
8770
|
return Array.isArray(value) && value.every((entry) => isObject(entry));
|
|
7284
8771
|
}
|
|
@@ -7302,30 +8789,6 @@ function stringifyJson(value, spaces) {
|
|
|
7302
8789
|
return String(value);
|
|
7303
8790
|
}
|
|
7304
8791
|
}
|
|
7305
|
-
function renderObjectTable(result, primitives) {
|
|
7306
|
-
const rows = Object.entries(result).map(([key2, value]) => ({
|
|
7307
|
-
key: key2,
|
|
7308
|
-
value: stringifyValue2(value)
|
|
7309
|
-
}));
|
|
7310
|
-
return primitives.renderTable({
|
|
7311
|
-
theme: primitives.getTheme(),
|
|
7312
|
-
columns: [
|
|
7313
|
-
{
|
|
7314
|
-
name: "key",
|
|
7315
|
-
title: "Key",
|
|
7316
|
-
alignment: "left",
|
|
7317
|
-
maxLen: Math.max("Key".length, ...rows.map((row) => row.key.length))
|
|
7318
|
-
},
|
|
7319
|
-
{
|
|
7320
|
-
name: "value",
|
|
7321
|
-
title: "Value",
|
|
7322
|
-
alignment: "left",
|
|
7323
|
-
maxLen: Math.max("Value".length, ...rows.map((row) => row.value.length))
|
|
7324
|
-
}
|
|
7325
|
-
],
|
|
7326
|
-
rows
|
|
7327
|
-
});
|
|
7328
|
-
}
|
|
7329
8792
|
function renderObjectMarkdown(result) {
|
|
7330
8793
|
return Object.entries(result).map(([key2, value]) => `- ${key2}: ${stringifyValue2(value)}`).join("\n");
|
|
7331
8794
|
}
|
|
@@ -7338,29 +8801,6 @@ function getColumnNames(rows) {
|
|
|
7338
8801
|
}
|
|
7339
8802
|
return [...names];
|
|
7340
8803
|
}
|
|
7341
|
-
function renderArrayTable(result, primitives) {
|
|
7342
|
-
if (result.length === 0) {
|
|
7343
|
-
return "[]";
|
|
7344
|
-
}
|
|
7345
|
-
const columnNames = getColumnNames(result);
|
|
7346
|
-
return primitives.renderTable({
|
|
7347
|
-
theme: primitives.getTheme(),
|
|
7348
|
-
columns: columnNames.map((name) => ({
|
|
7349
|
-
name,
|
|
7350
|
-
title: name,
|
|
7351
|
-
alignment: "left",
|
|
7352
|
-
maxLen: Math.max(
|
|
7353
|
-
name.length,
|
|
7354
|
-
...result.map((row) => name in row ? stringifyValue2(row[name]).length : 0)
|
|
7355
|
-
)
|
|
7356
|
-
})),
|
|
7357
|
-
rows: result.map(
|
|
7358
|
-
(row) => Object.fromEntries(
|
|
7359
|
-
columnNames.map((name) => [name, name in row ? stringifyValue2(row[name]) : ""])
|
|
7360
|
-
)
|
|
7361
|
-
)
|
|
7362
|
-
});
|
|
7363
|
-
}
|
|
7364
8804
|
function renderArrayMarkdown(result) {
|
|
7365
8805
|
if (result.length === 0) {
|
|
7366
8806
|
return "[]";
|
|
@@ -7373,7 +8813,7 @@ function renderArrayMarkdown(result) {
|
|
|
7373
8813
|
);
|
|
7374
8814
|
return [header, separator, ...rows].join("\n");
|
|
7375
8815
|
}
|
|
7376
|
-
function autoRender(result, output,
|
|
8816
|
+
function autoRender(result, output, _primitives) {
|
|
7377
8817
|
if (result === null || result === void 0) {
|
|
7378
8818
|
if (output === "json") {
|
|
7379
8819
|
return stringifyJson({ ok: true }, 2);
|
|
@@ -7386,14 +8826,16 @@ function autoRender(result, output, primitives) {
|
|
|
7386
8826
|
}
|
|
7387
8827
|
return result;
|
|
7388
8828
|
}
|
|
8829
|
+
if (output === "rich" && Array.isArray(result) && result.every((value) => typeof value === "string")) {
|
|
8830
|
+
return result.join("\n");
|
|
8831
|
+
}
|
|
7389
8832
|
if (isObject(result)) {
|
|
7390
|
-
if (output === "rich") {
|
|
7391
|
-
return renderObjectTable(result, primitives);
|
|
7392
|
-
}
|
|
7393
8833
|
if (output === "md") {
|
|
7394
8834
|
return renderObjectMarkdown(result);
|
|
7395
8835
|
}
|
|
7396
|
-
|
|
8836
|
+
if (output === "json") {
|
|
8837
|
+
return stringifyJson(result, 2);
|
|
8838
|
+
}
|
|
7397
8839
|
}
|
|
7398
8840
|
if (isArrayOfObjects(result)) {
|
|
7399
8841
|
if (output === "md") {
|
|
@@ -7402,20 +8844,36 @@ function autoRender(result, output, primitives) {
|
|
|
7402
8844
|
if (output === "json") {
|
|
7403
8845
|
return stringifyJson(result, 2);
|
|
7404
8846
|
}
|
|
7405
|
-
|
|
8847
|
+
}
|
|
8848
|
+
if (output === "rich") {
|
|
8849
|
+
return YAML.stringify(result);
|
|
7406
8850
|
}
|
|
7407
8851
|
return stringifyJson(result, 2);
|
|
7408
8852
|
}
|
|
7409
|
-
function renderResult(command, result, output, primitives, write2 = (chunk) => {
|
|
8853
|
+
function renderResult(command, result, output, primitives, write2 = (chunk, stream = "stdout") => {
|
|
8854
|
+
if (stream === "stderr") {
|
|
8855
|
+
process.stderr.write(chunk);
|
|
8856
|
+
return;
|
|
8857
|
+
}
|
|
7410
8858
|
process.stdout.write(chunk);
|
|
7411
8859
|
}) {
|
|
8860
|
+
const unwrapped = unwrapMcpEnvelope(result);
|
|
8861
|
+
result = unwrapped.result;
|
|
8862
|
+
if (unwrapped.mcpError) {
|
|
8863
|
+
const payload2 = autoRender(result, output, primitives);
|
|
8864
|
+
if (payload2.length > 0) {
|
|
8865
|
+
write2(`${payload2}
|
|
8866
|
+
`, "stderr");
|
|
8867
|
+
}
|
|
8868
|
+
return { mcpError: true };
|
|
8869
|
+
}
|
|
7412
8870
|
if (output === "json" && command.render?.json) {
|
|
7413
8871
|
const payload2 = command.render.json(result, primitives);
|
|
7414
8872
|
if (payload2 !== void 0) {
|
|
7415
8873
|
write2(`${stringifyJson(payload2, 2)}
|
|
7416
8874
|
`);
|
|
7417
8875
|
}
|
|
7418
|
-
return;
|
|
8876
|
+
return { mcpError: false };
|
|
7419
8877
|
}
|
|
7420
8878
|
if (output === "md" && command.render?.markdown) {
|
|
7421
8879
|
const payload2 = command.render.markdown(result, primitives);
|
|
@@ -7423,17 +8881,18 @@ function renderResult(command, result, output, primitives, write2 = (chunk) => {
|
|
|
7423
8881
|
write2(`${payload2}
|
|
7424
8882
|
`);
|
|
7425
8883
|
}
|
|
7426
|
-
return;
|
|
8884
|
+
return { mcpError: false };
|
|
7427
8885
|
}
|
|
7428
8886
|
if (output === "rich" && command.render?.rich) {
|
|
7429
8887
|
command.render.rich(result, primitives);
|
|
7430
|
-
return;
|
|
8888
|
+
return { mcpError: false };
|
|
7431
8889
|
}
|
|
7432
8890
|
const payload = autoRender(result, output, primitives);
|
|
7433
8891
|
if (payload.length > 0) {
|
|
7434
8892
|
write2(`${payload}
|
|
7435
8893
|
`);
|
|
7436
8894
|
}
|
|
8895
|
+
return { mcpError: false };
|
|
7437
8896
|
}
|
|
7438
8897
|
|
|
7439
8898
|
// ../toolcraft/src/cli.ts
|
|
@@ -7444,7 +8903,7 @@ function inferProgramName(argv) {
|
|
|
7444
8903
|
if (typeof entrypoint !== "string" || entrypoint.length === 0) {
|
|
7445
8904
|
return "toolcraft";
|
|
7446
8905
|
}
|
|
7447
|
-
const parsed =
|
|
8906
|
+
const parsed = path9.parse(entrypoint);
|
|
7448
8907
|
return parsed.name.length > 0 ? parsed.name : "toolcraft";
|
|
7449
8908
|
}
|
|
7450
8909
|
function normalizeRoots(roots, argv) {
|
|
@@ -7466,7 +8925,7 @@ function unwrapOptional(schema) {
|
|
|
7466
8925
|
}
|
|
7467
8926
|
return schema;
|
|
7468
8927
|
}
|
|
7469
|
-
function
|
|
8928
|
+
function splitWords3(value) {
|
|
7470
8929
|
const words = [];
|
|
7471
8930
|
let current = "";
|
|
7472
8931
|
for (let index = 0; index < value.length; index += 1) {
|
|
@@ -7500,13 +8959,13 @@ function splitWords(value) {
|
|
|
7500
8959
|
}
|
|
7501
8960
|
function formatSegment(segment, casing) {
|
|
7502
8961
|
const separator = casing === "snake" ? "_" : "-";
|
|
7503
|
-
return
|
|
8962
|
+
return splitWords3(segment).join(separator);
|
|
7504
8963
|
}
|
|
7505
|
-
function toOptionFlag(
|
|
7506
|
-
return `--${
|
|
8964
|
+
function toOptionFlag(path15, casing) {
|
|
8965
|
+
return `--${path15.map((segment) => formatSegment(segment, casing)).join(".")}`;
|
|
7507
8966
|
}
|
|
7508
|
-
function toOptionAttribute(
|
|
7509
|
-
return
|
|
8967
|
+
function toOptionAttribute(path15, casing) {
|
|
8968
|
+
return path15.map((segment) => {
|
|
7510
8969
|
const formatted = formatSegment(segment, casing);
|
|
7511
8970
|
if (casing === "snake") {
|
|
7512
8971
|
return formatted;
|
|
@@ -7517,23 +8976,23 @@ function toOptionAttribute(path13, casing) {
|
|
|
7517
8976
|
).join("");
|
|
7518
8977
|
}).join(".");
|
|
7519
8978
|
}
|
|
7520
|
-
function toDisplayPath(
|
|
7521
|
-
return
|
|
8979
|
+
function toDisplayPath(path15) {
|
|
8980
|
+
return path15.join(".");
|
|
7522
8981
|
}
|
|
7523
|
-
function toUnionKindControlPath(
|
|
7524
|
-
if (
|
|
8982
|
+
function toUnionKindControlPath(path15) {
|
|
8983
|
+
if (path15.length === 0) {
|
|
7525
8984
|
return ["kind"];
|
|
7526
8985
|
}
|
|
7527
|
-
const head =
|
|
7528
|
-
const tail =
|
|
8986
|
+
const head = path15.slice(0, -1);
|
|
8987
|
+
const tail = path15[path15.length - 1] ?? "";
|
|
7529
8988
|
return [...head, `${tail}Kind`];
|
|
7530
8989
|
}
|
|
7531
|
-
function toUnionKindDisplayPath(
|
|
7532
|
-
if (
|
|
8990
|
+
function toUnionKindDisplayPath(path15) {
|
|
8991
|
+
if (path15.length === 0) {
|
|
7533
8992
|
return "kind";
|
|
7534
8993
|
}
|
|
7535
|
-
const head =
|
|
7536
|
-
const tail =
|
|
8994
|
+
const head = path15.slice(0, -1);
|
|
8995
|
+
const tail = path15[path15.length - 1] ?? "";
|
|
7537
8996
|
return [...head, `${tail}-kind`].join(".");
|
|
7538
8997
|
}
|
|
7539
8998
|
function createSyntheticEnumSchema(values) {
|
|
@@ -7549,19 +9008,26 @@ function getRequiredBranchFingerprint(branch, casing) {
|
|
|
7549
9008
|
const requiredKeys = Object.entries(branch.shape).filter(([, schema]) => schema.kind !== "optional").map(([key2]) => formatSegment(key2, casing)).sort();
|
|
7550
9009
|
return requiredKeys.join("+");
|
|
7551
9010
|
}
|
|
7552
|
-
function collectFields(schema, casing, globalLongOptionFlags,
|
|
9011
|
+
function collectFields(schema, casing, globalLongOptionFlags, path15 = [], inheritedOptional = false, variantContext) {
|
|
7553
9012
|
const collected = {
|
|
7554
9013
|
dynamicFields: [],
|
|
7555
9014
|
fields: [],
|
|
7556
9015
|
variants: []
|
|
7557
9016
|
};
|
|
7558
9017
|
for (const [key2, rawChildSchema] of Object.entries(schema.shape)) {
|
|
7559
|
-
const nextPath = [...
|
|
9018
|
+
const nextPath = [...path15, key2];
|
|
7560
9019
|
const runtimeOptional = inheritedOptional || rawChildSchema.kind === "optional";
|
|
7561
9020
|
const childSchema = unwrapOptional(rawChildSchema);
|
|
7562
9021
|
const requiredWhenActive = rawChildSchema.kind !== "optional" && childSchema.default === void 0;
|
|
7563
9022
|
if (childSchema.kind === "object") {
|
|
7564
|
-
const nested = collectFields(
|
|
9023
|
+
const nested = collectFields(
|
|
9024
|
+
childSchema,
|
|
9025
|
+
casing,
|
|
9026
|
+
globalLongOptionFlags,
|
|
9027
|
+
nextPath,
|
|
9028
|
+
runtimeOptional,
|
|
9029
|
+
variantContext
|
|
9030
|
+
);
|
|
7565
9031
|
collected.dynamicFields.push(...nested.dynamicFields);
|
|
7566
9032
|
collected.fields.push(...nested.fields);
|
|
7567
9033
|
collected.variants.push(...nested.variants);
|
|
@@ -7575,7 +9041,11 @@ function collectFields(schema, casing, globalLongOptionFlags, path13 = [], inher
|
|
|
7575
9041
|
path: [...nextPath, childSchema.discriminator],
|
|
7576
9042
|
displayPath: toDisplayPath([...nextPath, childSchema.discriminator]),
|
|
7577
9043
|
optionAttribute: toOptionAttribute([...nextPath, childSchema.discriminator], casing),
|
|
7578
|
-
commanderOptionAttribute: toCommanderOptionAttribute(
|
|
9044
|
+
commanderOptionAttribute: toCommanderOptionAttribute(
|
|
9045
|
+
[...nextPath, childSchema.discriminator],
|
|
9046
|
+
casing,
|
|
9047
|
+
globalLongOptionFlags
|
|
9048
|
+
),
|
|
7579
9049
|
optionFlag: toOptionFlag([...nextPath, childSchema.discriminator], casing),
|
|
7580
9050
|
shortFlag: void 0,
|
|
7581
9051
|
schema: createSyntheticEnumSchema(branchIds),
|
|
@@ -7616,13 +9086,19 @@ function collectFields(schema, casing, globalLongOptionFlags, path13 = [], inher
|
|
|
7616
9086
|
const variantId = `${toDisplayPath(nextPath)}:union`;
|
|
7617
9087
|
const controlPath = toUnionKindControlPath(nextPath);
|
|
7618
9088
|
const controlDisplayPath = toUnionKindDisplayPath(nextPath);
|
|
7619
|
-
const branchIds = childSchema.branches.map(
|
|
9089
|
+
const branchIds = childSchema.branches.map(
|
|
9090
|
+
(branch) => getRequiredBranchFingerprint(branch, casing)
|
|
9091
|
+
);
|
|
7620
9092
|
const controlField = {
|
|
7621
9093
|
id: controlDisplayPath,
|
|
7622
9094
|
path: controlPath,
|
|
7623
9095
|
displayPath: controlDisplayPath,
|
|
7624
9096
|
optionAttribute: toOptionAttribute(controlPath, casing),
|
|
7625
|
-
commanderOptionAttribute: toCommanderOptionAttribute(
|
|
9097
|
+
commanderOptionAttribute: toCommanderOptionAttribute(
|
|
9098
|
+
controlPath,
|
|
9099
|
+
casing,
|
|
9100
|
+
globalLongOptionFlags
|
|
9101
|
+
),
|
|
7626
9102
|
optionFlag: toOptionFlag(controlPath, casing),
|
|
7627
9103
|
shortFlag: void 0,
|
|
7628
9104
|
schema: createSyntheticEnumSchema(branchIds),
|
|
@@ -7719,9 +9195,9 @@ function collectFields(schema, casing, globalLongOptionFlags, path13 = [], inher
|
|
|
7719
9195
|
}
|
|
7720
9196
|
return collected;
|
|
7721
9197
|
}
|
|
7722
|
-
function toCommanderOptionAttribute(
|
|
7723
|
-
const optionAttribute = toOptionAttribute(
|
|
7724
|
-
const optionFlag = toOptionFlag(
|
|
9198
|
+
function toCommanderOptionAttribute(path15, casing, globalLongOptionFlags) {
|
|
9199
|
+
const optionAttribute = toOptionAttribute(path15, casing);
|
|
9200
|
+
const optionFlag = toOptionFlag(path15, casing);
|
|
7725
9201
|
if (!globalLongOptionFlags.has(optionFlag)) {
|
|
7726
9202
|
return optionAttribute;
|
|
7727
9203
|
}
|
|
@@ -7869,7 +9345,9 @@ function parseArrayValue(value, schema, label) {
|
|
|
7869
9345
|
if (itemSchema.kind === "array" || itemSchema.kind === "object") {
|
|
7870
9346
|
throw new UserError(`Array parameter "${label}" must use scalar items.`);
|
|
7871
9347
|
}
|
|
7872
|
-
return splitArrayInput(value).map(
|
|
9348
|
+
return splitArrayInput(value).map(
|
|
9349
|
+
(item) => parseScalarValue(item, itemSchema, label)
|
|
9350
|
+
);
|
|
7873
9351
|
}
|
|
7874
9352
|
function createOption(field, globalLongOptionFlags) {
|
|
7875
9353
|
const flags = formatOptionFlags(field, globalLongOptionFlags);
|
|
@@ -7893,7 +9371,11 @@ function createOption(field, globalLongOptionFlags) {
|
|
|
7893
9371
|
return [
|
|
7894
9372
|
createCommanderOption(`${flags} <value...>`, field.description, field).argParser(
|
|
7895
9373
|
(value, previous = []) => {
|
|
7896
|
-
const parsed = parseArrayValue(
|
|
9374
|
+
const parsed = parseArrayValue(
|
|
9375
|
+
value,
|
|
9376
|
+
field.schema,
|
|
9377
|
+
field.displayPath
|
|
9378
|
+
);
|
|
7897
9379
|
if (parsed === null) {
|
|
7898
9380
|
return NULL_OPTION_VALUE;
|
|
7899
9381
|
}
|
|
@@ -7920,7 +9402,9 @@ function createOption(field, globalLongOptionFlags) {
|
|
|
7920
9402
|
}
|
|
7921
9403
|
var ALWAYS_GLOBAL_LONG_OPTION_FLAGS = ["--yes", "--output", "--verbose"];
|
|
7922
9404
|
function getGlobalLongOptionFlags(presetsEnabled) {
|
|
7923
|
-
return new Set(
|
|
9405
|
+
return new Set(
|
|
9406
|
+
presetsEnabled ? ["--preset", ...ALWAYS_GLOBAL_LONG_OPTION_FLAGS] : ALWAYS_GLOBAL_LONG_OPTION_FLAGS
|
|
9407
|
+
);
|
|
7924
9408
|
}
|
|
7925
9409
|
function createCommanderOption(flags, description, field) {
|
|
7926
9410
|
const option = new Option(flags, description);
|
|
@@ -7981,11 +9465,7 @@ function resolveHelpTarget(root, argv, scope, rootDisplayName) {
|
|
|
7981
9465
|
if (current.kind !== "group") {
|
|
7982
9466
|
break;
|
|
7983
9467
|
}
|
|
7984
|
-
const child = findVisibleChild(
|
|
7985
|
-
current,
|
|
7986
|
-
token,
|
|
7987
|
-
scope
|
|
7988
|
-
);
|
|
9468
|
+
const child = findVisibleChild(current, token, scope);
|
|
7989
9469
|
if (child === void 0) {
|
|
7990
9470
|
break;
|
|
7991
9471
|
}
|
|
@@ -7997,32 +9477,23 @@ function resolveHelpTarget(root, argv, scope, rootDisplayName) {
|
|
|
7997
9477
|
node: current
|
|
7998
9478
|
};
|
|
7999
9479
|
}
|
|
8000
|
-
function describeSchemaType(schema) {
|
|
8001
|
-
switch (schema.kind) {
|
|
8002
|
-
case "string":
|
|
8003
|
-
return "string";
|
|
8004
|
-
case "number":
|
|
8005
|
-
return "number";
|
|
8006
|
-
case "boolean":
|
|
8007
|
-
return "boolean";
|
|
8008
|
-
case "enum":
|
|
8009
|
-
return "value";
|
|
8010
|
-
case "array":
|
|
8011
|
-
return `${describeSchemaType(unwrapOptional(schema.item))}...`;
|
|
8012
|
-
case "json":
|
|
8013
|
-
return "json";
|
|
8014
|
-
default:
|
|
8015
|
-
throw new UserError("Unsupported CLI schema kind.");
|
|
8016
|
-
}
|
|
8017
|
-
}
|
|
8018
9480
|
function formatHelpFieldFlags(field, globalLongOptionFlags) {
|
|
8019
9481
|
if (field.positionalIndex !== void 0) {
|
|
8020
9482
|
return formatPositionalToken(field);
|
|
8021
9483
|
}
|
|
8022
9484
|
if (field.schema.kind === "boolean") {
|
|
8023
|
-
|
|
9485
|
+
if (field.defaultValue === true) {
|
|
9486
|
+
return `--no-${field.optionFlag.slice(2)}`;
|
|
9487
|
+
}
|
|
9488
|
+
return formatOptionFlags(field, globalLongOptionFlags);
|
|
8024
9489
|
}
|
|
8025
|
-
return `${formatOptionFlags(field, globalLongOptionFlags)} <${
|
|
9490
|
+
return `${formatOptionFlags(field, globalLongOptionFlags)} <${describeHelpValueToken(
|
|
9491
|
+
field.schema,
|
|
9492
|
+
{
|
|
9493
|
+
displayPath: field.displayPath,
|
|
9494
|
+
optionFlag: field.optionFlag
|
|
9495
|
+
}
|
|
9496
|
+
)}>`;
|
|
8026
9497
|
}
|
|
8027
9498
|
function appendHelpMetadata(description, metadata) {
|
|
8028
9499
|
if (metadata.length === 0) {
|
|
@@ -8044,6 +9515,87 @@ function formatHelpFieldDescription(field) {
|
|
|
8044
9515
|
}
|
|
8045
9516
|
return appendHelpMetadata(description, metadata);
|
|
8046
9517
|
}
|
|
9518
|
+
function describeKnownStringFormat(format) {
|
|
9519
|
+
switch (format) {
|
|
9520
|
+
case "date":
|
|
9521
|
+
return "date";
|
|
9522
|
+
case "date-time":
|
|
9523
|
+
return "datetime";
|
|
9524
|
+
case "uri":
|
|
9525
|
+
return "url";
|
|
9526
|
+
case "email":
|
|
9527
|
+
return "email";
|
|
9528
|
+
default:
|
|
9529
|
+
return void 0;
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9532
|
+
function describeKnownStringPattern(pattern) {
|
|
9533
|
+
if (pattern === void 0) {
|
|
9534
|
+
return void 0;
|
|
9535
|
+
}
|
|
9536
|
+
if (pattern === "^\\d{4}-\\d{2}-\\d{2}$") {
|
|
9537
|
+
return "YYYY-MM-DD";
|
|
9538
|
+
}
|
|
9539
|
+
if (pattern.startsWith("^\\d{4}-\\d{2}-\\d{2}T")) {
|
|
9540
|
+
return "YYYY-MM-DDTHH:MM:SS";
|
|
9541
|
+
}
|
|
9542
|
+
return void 0;
|
|
9543
|
+
}
|
|
9544
|
+
function stripLongOptionPrefix(optionFlag) {
|
|
9545
|
+
return optionFlag.startsWith("--") ? optionFlag.slice(2) : optionFlag;
|
|
9546
|
+
}
|
|
9547
|
+
function getLastSegment(value, separator) {
|
|
9548
|
+
const segments = value.split(separator);
|
|
9549
|
+
return segments[segments.length - 1] ?? value;
|
|
9550
|
+
}
|
|
9551
|
+
function matchesFieldNameSuffix(name, suffix) {
|
|
9552
|
+
const lowerName = name.toLowerCase();
|
|
9553
|
+
const lowerSuffix = suffix.toLowerCase();
|
|
9554
|
+
return lowerName === lowerSuffix || name.endsWith(suffix) || lowerName.endsWith(`-${lowerSuffix}`) || lowerName.endsWith(`_${lowerSuffix}`);
|
|
9555
|
+
}
|
|
9556
|
+
function describeFieldNameValueToken(displayPath, optionFlag) {
|
|
9557
|
+
const displayName = getLastSegment(displayPath, ".");
|
|
9558
|
+
const optionName = getLastSegment(stripLongOptionPrefix(optionFlag), ".");
|
|
9559
|
+
const candidates = [displayName, optionName];
|
|
9560
|
+
const suffixTokens = [
|
|
9561
|
+
["Path", "path"],
|
|
9562
|
+
["Paths", "path"],
|
|
9563
|
+
["File", "path"],
|
|
9564
|
+
["Files", "path"],
|
|
9565
|
+
["Url", "url"],
|
|
9566
|
+
["Email", "email"],
|
|
9567
|
+
["Name", "name"],
|
|
9568
|
+
["Id", "id"]
|
|
9569
|
+
];
|
|
9570
|
+
for (const [suffix, token] of suffixTokens) {
|
|
9571
|
+
if (candidates.some((candidate) => matchesFieldNameSuffix(candidate, suffix))) {
|
|
9572
|
+
return token;
|
|
9573
|
+
}
|
|
9574
|
+
}
|
|
9575
|
+
return void 0;
|
|
9576
|
+
}
|
|
9577
|
+
function describeHelpValueToken(schema, field) {
|
|
9578
|
+
if (schema.kind === "array") {
|
|
9579
|
+
const itemSchema = unwrapOptional(schema.item);
|
|
9580
|
+
if (itemSchema.kind === "array" || itemSchema.kind === "object") {
|
|
9581
|
+
return "value...";
|
|
9582
|
+
}
|
|
9583
|
+
return `${describeHelpValueToken(itemSchema, field)}...`;
|
|
9584
|
+
}
|
|
9585
|
+
if (schema.kind === "json") {
|
|
9586
|
+
return "json";
|
|
9587
|
+
}
|
|
9588
|
+
if (schema.kind === "string") {
|
|
9589
|
+
const metadataToken = describeKnownStringFormat(schema.format) ?? describeKnownStringPattern(schema.pattern);
|
|
9590
|
+
if (metadataToken !== void 0) {
|
|
9591
|
+
return metadataToken;
|
|
9592
|
+
}
|
|
9593
|
+
}
|
|
9594
|
+
if (schema.kind === "enum") {
|
|
9595
|
+
return "value";
|
|
9596
|
+
}
|
|
9597
|
+
return describeFieldNameValueToken(field.displayPath, field.optionFlag) ?? "value";
|
|
9598
|
+
}
|
|
8047
9599
|
function describeDynamicFieldType(field) {
|
|
8048
9600
|
if (field.schema.kind === "record") {
|
|
8049
9601
|
const valueSchema = unwrapOptional(field.schema.value);
|
|
@@ -8051,12 +9603,18 @@ function describeDynamicFieldType(field) {
|
|
|
8051
9603
|
return "json";
|
|
8052
9604
|
}
|
|
8053
9605
|
if (valueSchema.kind === "array") {
|
|
8054
|
-
return
|
|
9606
|
+
return describeHelpValueToken(valueSchema, {
|
|
9607
|
+
displayPath: field.optionPathDisplay,
|
|
9608
|
+
optionFlag: field.optionFlag
|
|
9609
|
+
});
|
|
8055
9610
|
}
|
|
8056
9611
|
if (valueSchema.kind === "object") {
|
|
8057
9612
|
return "value";
|
|
8058
9613
|
}
|
|
8059
|
-
return
|
|
9614
|
+
return describeHelpValueToken(valueSchema, {
|
|
9615
|
+
displayPath: field.optionPathDisplay,
|
|
9616
|
+
optionFlag: field.optionFlag
|
|
9617
|
+
});
|
|
8060
9618
|
}
|
|
8061
9619
|
return "value";
|
|
8062
9620
|
}
|
|
@@ -8078,7 +9636,9 @@ function collectDynamicObjectHelpRows(schema, casing, optionPrefix, displayPrefi
|
|
|
8078
9636
|
const displayPath = `${displayPrefix}.${key2}`;
|
|
8079
9637
|
const description = childSchema.description ?? displayPath;
|
|
8080
9638
|
if (childSchema.kind === "object") {
|
|
8081
|
-
rows.push(
|
|
9639
|
+
rows.push(
|
|
9640
|
+
...collectDynamicObjectHelpRows(childSchema, casing, optionFlag, displayPath, metadata)
|
|
9641
|
+
);
|
|
8082
9642
|
continue;
|
|
8083
9643
|
}
|
|
8084
9644
|
if (childSchema.kind === "record") {
|
|
@@ -8115,7 +9675,10 @@ function collectDynamicObjectHelpRows(schema, casing, optionPrefix, displayPrefi
|
|
|
8115
9675
|
continue;
|
|
8116
9676
|
}
|
|
8117
9677
|
rows.push({
|
|
8118
|
-
flags: childSchema.kind === "boolean" ?
|
|
9678
|
+
flags: childSchema.kind === "boolean" ? childSchema.default === true ? `--no-${optionFlag.slice(2)}` : optionFlag : `${optionFlag} <${describeHelpValueToken(childSchema, {
|
|
9679
|
+
displayPath,
|
|
9680
|
+
optionFlag
|
|
9681
|
+
})}>`,
|
|
8119
9682
|
description: appendHelpMetadata(description, metadata)
|
|
8120
9683
|
});
|
|
8121
9684
|
}
|
|
@@ -8147,10 +9710,12 @@ function formatDynamicHelpFields(field, casing) {
|
|
|
8147
9710
|
);
|
|
8148
9711
|
}
|
|
8149
9712
|
}
|
|
8150
|
-
return [
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
9713
|
+
return [
|
|
9714
|
+
{
|
|
9715
|
+
flags: `${field.optionFlag} <${describeDynamicFieldType(field)}>`,
|
|
9716
|
+
description: appendHelpMetadata(field.description ?? field.optionPathDisplay, metadata)
|
|
9717
|
+
}
|
|
9718
|
+
];
|
|
8154
9719
|
}
|
|
8155
9720
|
function formatSecretRows(secrets) {
|
|
8156
9721
|
return Object.values(secrets).map((secret) => ({
|
|
@@ -8181,29 +9746,25 @@ function formatCommandParameterTokens(command, casing, globalLongOptionFlags) {
|
|
|
8181
9746
|
formatHelpFieldFlags(field, globalLongOptionFlags),
|
|
8182
9747
|
field.positionalIndex === void 0 && (field.optional || field.hasDefault)
|
|
8183
9748
|
)
|
|
8184
|
-
).concat(
|
|
9749
|
+
).concat(
|
|
9750
|
+
collected.dynamicFields.flatMap((field) => formatCommandDynamicParameterTokens(field, casing))
|
|
9751
|
+
);
|
|
8185
9752
|
}
|
|
8186
|
-
function formatCommandRowName(node,
|
|
9753
|
+
function formatCommandRowName(node, casing, globalLongOptionFlags) {
|
|
8187
9754
|
const baseName = node.aliases.length === 0 ? node.name : `${node.name} (${node.aliases.join(", ")})`;
|
|
8188
9755
|
const parameterTokens = node.kind === "command" ? formatCommandParameterTokens(node, casing, globalLongOptionFlags) : [];
|
|
8189
9756
|
const name = parameterTokens.length === 0 ? baseName : `${baseName} ${parameterTokens.join(" ")}`;
|
|
8190
|
-
return
|
|
9757
|
+
return name;
|
|
8191
9758
|
}
|
|
8192
|
-
function formatCommandRows(group, scope, casing, globalLongOptionFlags
|
|
8193
|
-
return getVisibleChildren(group, scope).
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
};
|
|
8198
|
-
if (child.kind === "command") {
|
|
8199
|
-
return [row];
|
|
8200
|
-
}
|
|
8201
|
-
return [row, ...formatCommandRows(child, scope, casing, globalLongOptionFlags, depth + 1)];
|
|
8202
|
-
});
|
|
9759
|
+
function formatCommandRows(group, scope, casing, globalLongOptionFlags) {
|
|
9760
|
+
return getVisibleChildren(group, scope).map((child) => ({
|
|
9761
|
+
name: formatCommandRowName(child, casing, globalLongOptionFlags),
|
|
9762
|
+
description: child.description ?? ""
|
|
9763
|
+
}));
|
|
8203
9764
|
}
|
|
8204
|
-
function formatGlobalOptionRows(
|
|
9765
|
+
function formatGlobalOptionRows(ctx) {
|
|
8205
9766
|
const rows = [];
|
|
8206
|
-
if (presetsEnabled) {
|
|
9767
|
+
if (ctx.presetsEnabled) {
|
|
8207
9768
|
rows.push({
|
|
8208
9769
|
flags: "--preset <path>",
|
|
8209
9770
|
description: "Load parameter defaults from a JSON file"
|
|
@@ -8216,14 +9777,10 @@ function formatGlobalOptionRows(showVersion, presetsEnabled) {
|
|
|
8216
9777
|
},
|
|
8217
9778
|
{
|
|
8218
9779
|
flags: "--output <format>",
|
|
8219
|
-
description: "Output format
|
|
8220
|
-
},
|
|
8221
|
-
{
|
|
8222
|
-
flags: "-h, --help",
|
|
8223
|
-
description: "Show help"
|
|
9780
|
+
description: "Output format: rich, md, json."
|
|
8224
9781
|
}
|
|
8225
9782
|
);
|
|
8226
|
-
if (showVersion) {
|
|
9783
|
+
if (ctx.showVersion) {
|
|
8227
9784
|
rows.push({
|
|
8228
9785
|
flags: "--version",
|
|
8229
9786
|
description: "Show version"
|
|
@@ -8234,34 +9791,43 @@ function formatGlobalOptionRows(showVersion, presetsEnabled) {
|
|
|
8234
9791
|
function renderHelpSections(sections) {
|
|
8235
9792
|
return sections.filter((section) => section.length > 0).join("\n\n");
|
|
8236
9793
|
}
|
|
9794
|
+
function formatHelpCommandList(rows) {
|
|
9795
|
+
return process.stdout.isTTY === false ? help_formatter_plain_exports.formatCommandList(rows) : formatCommandList(rows);
|
|
9796
|
+
}
|
|
9797
|
+
function formatHelpOptionList(rows) {
|
|
9798
|
+
return process.stdout.isTTY === false ? help_formatter_plain_exports.formatOptionList(rows) : formatOptionList(rows);
|
|
9799
|
+
}
|
|
8237
9800
|
function buildUsageLine(breadcrumb, rootUsageName, suffix) {
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
const
|
|
8242
|
-
return
|
|
9801
|
+
const visibleBreadcrumb = breadcrumb.filter((segment) => segment.length > 0);
|
|
9802
|
+
const usageBreadcrumb = breadcrumb[0] === "" ? [rootUsageName, ...visibleBreadcrumb] : visibleBreadcrumb;
|
|
9803
|
+
const subPath = usageBreadcrumb.slice(1).join(" ");
|
|
9804
|
+
const tokens = [rootUsageName, subPath, suffix].filter((segment) => segment.length > 0);
|
|
9805
|
+
return tokens.join(" ");
|
|
8243
9806
|
}
|
|
8244
|
-
function renderGroupHelp(group, breadcrumb, scope, casing,
|
|
9807
|
+
function renderGroupHelp(group, breadcrumb, scope, casing, globalOptions, rootUsageName) {
|
|
8245
9808
|
const sections = [];
|
|
8246
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled);
|
|
9809
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
8247
9810
|
const commandRows = formatCommandRows(group, scope, casing, globalLongOptionFlags);
|
|
8248
9811
|
if (commandRows.length > 0) {
|
|
8249
|
-
sections.push(`${text.
|
|
8250
|
-
${
|
|
9812
|
+
sections.push(`${text.sectionHeader("Commands")}
|
|
9813
|
+
${formatHelpCommandList(commandRows)}`);
|
|
8251
9814
|
}
|
|
8252
|
-
sections.push(
|
|
8253
|
-
|
|
9815
|
+
sections.push(
|
|
9816
|
+
`${text.sectionHeader("Options")}
|
|
9817
|
+
${formatHelpOptionList(formatGlobalOptionRows(globalOptions))}`
|
|
9818
|
+
);
|
|
8254
9819
|
return renderHelpDocument({
|
|
8255
9820
|
breadcrumb,
|
|
8256
|
-
|
|
9821
|
+
rootUsageName,
|
|
9822
|
+
usageLine: buildUsageLine(breadcrumb, rootUsageName, "[command] [options]"),
|
|
8257
9823
|
description: group.description,
|
|
8258
9824
|
requiresAuth: group.requires?.auth === true,
|
|
8259
9825
|
sections
|
|
8260
9826
|
});
|
|
8261
9827
|
}
|
|
8262
|
-
function renderLeafHelp(command, breadcrumb, casing,
|
|
9828
|
+
function renderLeafHelp(command, breadcrumb, casing, globalOptions, rootUsageName) {
|
|
8263
9829
|
const sections = [];
|
|
8264
|
-
const globalLongOptionFlags = getGlobalLongOptionFlags(presetsEnabled);
|
|
9830
|
+
const globalLongOptionFlags = getGlobalLongOptionFlags(globalOptions.presetsEnabled);
|
|
8265
9831
|
const collected = collectFields(command.params, casing, globalLongOptionFlags);
|
|
8266
9832
|
const fields = assignPositionals(collected.fields, command.positional);
|
|
8267
9833
|
const optionRows = fields.map((field) => ({
|
|
@@ -8269,20 +9835,25 @@ function renderLeafHelp(command, breadcrumb, casing, presetsEnabled, rootUsageNa
|
|
|
8269
9835
|
description: formatHelpFieldDescription(field)
|
|
8270
9836
|
})).concat(collected.dynamicFields.flatMap((field) => formatDynamicHelpFields(field, casing)));
|
|
8271
9837
|
if (optionRows.length > 0) {
|
|
8272
|
-
sections.push(`${text.
|
|
8273
|
-
${
|
|
9838
|
+
sections.push(`${text.sectionHeader("Options")}
|
|
9839
|
+
${formatHelpOptionList(optionRows)}`);
|
|
8274
9840
|
}
|
|
8275
|
-
sections.push(
|
|
8276
|
-
|
|
9841
|
+
sections.push(
|
|
9842
|
+
`${text.sectionHeader("Options")}
|
|
9843
|
+
${formatHelpOptionList(formatGlobalOptionRows(globalOptions))}`
|
|
9844
|
+
);
|
|
8277
9845
|
const secretRows = formatSecretRows(command.secrets);
|
|
8278
9846
|
if (secretRows.length > 0) {
|
|
8279
|
-
sections.push(
|
|
8280
|
-
|
|
9847
|
+
sections.push(
|
|
9848
|
+
`${text.sectionHeader("Secrets (environment)")}
|
|
9849
|
+
${formatHelpOptionList(secretRows)}`
|
|
9850
|
+
);
|
|
8281
9851
|
}
|
|
8282
9852
|
const positionalFields = fields.filter((f) => f.positionalIndex !== void 0);
|
|
8283
9853
|
const usageSuffix = positionalFields.length > 0 ? `[options] ${positionalFields.map(formatPositionalToken).join(" ")}` : "[options]";
|
|
8284
9854
|
return renderHelpDocument({
|
|
8285
9855
|
breadcrumb,
|
|
9856
|
+
rootUsageName,
|
|
8286
9857
|
usageLine: buildUsageLine(breadcrumb, rootUsageName, usageSuffix),
|
|
8287
9858
|
description: command.description,
|
|
8288
9859
|
requiresAuth: command.requires?.auth === true,
|
|
@@ -8290,17 +9861,21 @@ ${formatOptionList(secretRows)}`);
|
|
|
8290
9861
|
});
|
|
8291
9862
|
}
|
|
8292
9863
|
function renderHelpDocument(input) {
|
|
8293
|
-
const
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
9864
|
+
const title = input.breadcrumb.filter((segment) => segment.length > 0).join(" ") || input.rootUsageName;
|
|
9865
|
+
const description = input.description ?? "";
|
|
9866
|
+
const sentenceEndIndex = description.indexOf(". ");
|
|
9867
|
+
const headingDescription = sentenceEndIndex === -1 ? description : description.slice(0, sentenceEndIndex + 1);
|
|
9868
|
+
const remainingDescription = sentenceEndIndex === -1 ? "" : description.slice(sentenceEndIndex + 2);
|
|
9869
|
+
const heading = headingDescription.length > 0 ? `${title} \u2014 ${headingDescription}` : title;
|
|
9870
|
+
const lines = [text.heading(heading), ""];
|
|
9871
|
+
if (remainingDescription.length > 0) {
|
|
9872
|
+
lines.push(remainingDescription, "");
|
|
9873
|
+
}
|
|
9874
|
+
lines.push(`Usage: ${text.usageCommand(input.usageLine)}`, "");
|
|
8300
9875
|
if (input.requiresAuth) {
|
|
8301
9876
|
lines.push("Requires: authentication");
|
|
8302
9877
|
}
|
|
8303
|
-
if (input.
|
|
9878
|
+
if (input.requiresAuth) {
|
|
8304
9879
|
lines.push("");
|
|
8305
9880
|
}
|
|
8306
9881
|
lines.push(renderHelpSections(input.sections));
|
|
@@ -8311,16 +9886,28 @@ async function renderGeneratedHelp(root, argv, options) {
|
|
|
8311
9886
|
const target = resolveHelpTarget(root, argv, "cli", options.rootDisplayName);
|
|
8312
9887
|
const output = resolveHelpOutput(argv);
|
|
8313
9888
|
const casing = options.casing ?? "kebab";
|
|
9889
|
+
const rootUsageName = options.rootUsageName ?? inferProgramName(argv);
|
|
8314
9890
|
await withOutputFormat2(output, async () => {
|
|
8315
9891
|
const rendered = target.node.kind === "group" ? renderGroupHelp(
|
|
8316
9892
|
target.node,
|
|
8317
9893
|
target.breadcrumb,
|
|
8318
9894
|
"cli",
|
|
8319
9895
|
casing,
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
9896
|
+
{
|
|
9897
|
+
showVersion: options.version !== void 0,
|
|
9898
|
+
presetsEnabled: options.presets === true
|
|
9899
|
+
},
|
|
9900
|
+
rootUsageName
|
|
9901
|
+
) : renderLeafHelp(
|
|
9902
|
+
target.node,
|
|
9903
|
+
target.breadcrumb,
|
|
9904
|
+
casing,
|
|
9905
|
+
{
|
|
9906
|
+
showVersion: options.version !== void 0,
|
|
9907
|
+
presetsEnabled: options.presets === true
|
|
9908
|
+
},
|
|
9909
|
+
rootUsageName
|
|
9910
|
+
);
|
|
8324
9911
|
process.stdout.write(rendered);
|
|
8325
9912
|
});
|
|
8326
9913
|
}
|
|
@@ -8373,7 +9960,16 @@ function createNodeCommand(node, casing, globalLongOptionFlags, execute, presets
|
|
|
8373
9960
|
if (!isNodeVisibleInScope(node, "cli")) {
|
|
8374
9961
|
return null;
|
|
8375
9962
|
}
|
|
8376
|
-
const visibleChildren = node.children.map(
|
|
9963
|
+
const visibleChildren = node.children.map(
|
|
9964
|
+
(child) => createNodeCommand(
|
|
9965
|
+
child,
|
|
9966
|
+
casing,
|
|
9967
|
+
globalLongOptionFlags,
|
|
9968
|
+
execute,
|
|
9969
|
+
presetsEnabled,
|
|
9970
|
+
nextPathSegments
|
|
9971
|
+
)
|
|
9972
|
+
).filter((child) => child !== null);
|
|
8377
9973
|
const group = new CommanderCommand(node.name);
|
|
8378
9974
|
if (node.description !== void 0) {
|
|
8379
9975
|
group.description(node.description);
|
|
@@ -8398,13 +9994,15 @@ function addGlobalOptions(command, presetsEnabled) {
|
|
|
8398
9994
|
if (value === "markdown") {
|
|
8399
9995
|
return "md";
|
|
8400
9996
|
}
|
|
8401
|
-
throw new InvalidArgumentError(
|
|
9997
|
+
throw new InvalidArgumentError(
|
|
9998
|
+
'Invalid value for "--output". Expected one of: rich, md, markdown, json.'
|
|
9999
|
+
);
|
|
8402
10000
|
}).option("--verbose", "Print stack traces for unexpected errors.");
|
|
8403
10001
|
}
|
|
8404
|
-
function setNestedValue(target,
|
|
10002
|
+
function setNestedValue(target, path15, value) {
|
|
8405
10003
|
let cursor = target;
|
|
8406
|
-
for (let index = 0; index <
|
|
8407
|
-
const segment =
|
|
10004
|
+
for (let index = 0; index < path15.length - 1; index += 1) {
|
|
10005
|
+
const segment = path15[index] ?? "";
|
|
8408
10006
|
const existing = cursor[segment];
|
|
8409
10007
|
if (typeof existing === "object" && existing !== null) {
|
|
8410
10008
|
cursor = existing;
|
|
@@ -8414,7 +10012,7 @@ function setNestedValue(target, path13, value) {
|
|
|
8414
10012
|
cursor[segment] = next;
|
|
8415
10013
|
cursor = next;
|
|
8416
10014
|
}
|
|
8417
|
-
const leaf =
|
|
10015
|
+
const leaf = path15[path15.length - 1];
|
|
8418
10016
|
if (leaf !== void 0) {
|
|
8419
10017
|
cursor[leaf] = value;
|
|
8420
10018
|
}
|
|
@@ -8516,13 +10114,13 @@ async function withOutputFormat2(output, fn) {
|
|
|
8516
10114
|
}
|
|
8517
10115
|
function createFs2() {
|
|
8518
10116
|
return {
|
|
8519
|
-
readFile: async (
|
|
8520
|
-
writeFile: async (
|
|
8521
|
-
await writeFile3(
|
|
10117
|
+
readFile: async (path15, encoding = "utf8") => readFile5(path15, { encoding }),
|
|
10118
|
+
writeFile: async (path15, contents) => {
|
|
10119
|
+
await writeFile3(path15, contents);
|
|
8522
10120
|
},
|
|
8523
|
-
exists: async (
|
|
10121
|
+
exists: async (path15) => {
|
|
8524
10122
|
try {
|
|
8525
|
-
await access2(
|
|
10123
|
+
await access2(path15);
|
|
8526
10124
|
return true;
|
|
8527
10125
|
} catch {
|
|
8528
10126
|
return false;
|
|
@@ -8543,9 +10141,9 @@ function isPlainObject2(value) {
|
|
|
8543
10141
|
function hasFieldValue(value) {
|
|
8544
10142
|
return value !== void 0;
|
|
8545
10143
|
}
|
|
8546
|
-
function hasNestedField(fields,
|
|
10144
|
+
function hasNestedField(fields, path15) {
|
|
8547
10145
|
return fields.some(
|
|
8548
|
-
(field) =>
|
|
10146
|
+
(field) => path15.length < field.path.length && path15.every((segment, index) => field.path[index] === segment)
|
|
8549
10147
|
);
|
|
8550
10148
|
}
|
|
8551
10149
|
function describeExpectedPresetValue(schema) {
|
|
@@ -8602,7 +10200,12 @@ function validatePresetFieldValue(value, field, presetPath) {
|
|
|
8602
10200
|
return value;
|
|
8603
10201
|
}
|
|
8604
10202
|
if (field.schema.kind !== "array") {
|
|
8605
|
-
return validatePresetScalarValue(
|
|
10203
|
+
return validatePresetScalarValue(
|
|
10204
|
+
value,
|
|
10205
|
+
field.schema,
|
|
10206
|
+
field.displayPath,
|
|
10207
|
+
presetPath
|
|
10208
|
+
);
|
|
8606
10209
|
}
|
|
8607
10210
|
const itemSchema = unwrapOptional(field.schema.item);
|
|
8608
10211
|
if (itemSchema.kind === "array" || itemSchema.kind === "object") {
|
|
@@ -8620,7 +10223,7 @@ function validatePresetFieldValue(value, field, presetPath) {
|
|
|
8620
10223
|
async function loadPresetValues(fields, presetPath) {
|
|
8621
10224
|
let rawPreset;
|
|
8622
10225
|
try {
|
|
8623
|
-
rawPreset = await
|
|
10226
|
+
rawPreset = await readFile5(presetPath, {
|
|
8624
10227
|
encoding: "utf8"
|
|
8625
10228
|
});
|
|
8626
10229
|
} catch (error2) {
|
|
@@ -8641,9 +10244,9 @@ async function loadPresetValues(fields, presetPath) {
|
|
|
8641
10244
|
}
|
|
8642
10245
|
const fieldByPath = new Map(fields.map((field) => [field.displayPath, field]));
|
|
8643
10246
|
const presetValues = {};
|
|
8644
|
-
function visitObject(current,
|
|
10247
|
+
function visitObject(current, path15) {
|
|
8645
10248
|
for (const [key2, value] of Object.entries(current)) {
|
|
8646
|
-
const nextPath = [...
|
|
10249
|
+
const nextPath = [...path15, key2];
|
|
8647
10250
|
const displayPath = toDisplayPath(nextPath);
|
|
8648
10251
|
const field = fieldByPath.get(displayPath);
|
|
8649
10252
|
if (field !== void 0) {
|
|
@@ -8702,7 +10305,9 @@ function matchesFixtureValue(expected, actual) {
|
|
|
8702
10305
|
if (!isPlainObject2(actual)) {
|
|
8703
10306
|
return false;
|
|
8704
10307
|
}
|
|
8705
|
-
return Object.entries(expected).every(
|
|
10308
|
+
return Object.entries(expected).every(
|
|
10309
|
+
([key2, value]) => matchesFixtureValue(value, actual[key2])
|
|
10310
|
+
);
|
|
8706
10311
|
}
|
|
8707
10312
|
return Object.is(expected, actual);
|
|
8708
10313
|
}
|
|
@@ -8740,7 +10345,9 @@ function createFixtureResponse(response) {
|
|
|
8740
10345
|
}
|
|
8741
10346
|
function createFixtureFetch(entries) {
|
|
8742
10347
|
return async (input, init) => {
|
|
8743
|
-
const method = normalizeHttpMethod(
|
|
10348
|
+
const method = normalizeHttpMethod(
|
|
10349
|
+
init?.method ?? (input instanceof Request ? input.method : void 0)
|
|
10350
|
+
);
|
|
8744
10351
|
const url = getFetchUrl(input);
|
|
8745
10352
|
const match = entries?.find((entry) => {
|
|
8746
10353
|
const requestMethod = normalizeHttpMethod(entry.request.method);
|
|
@@ -8785,7 +10392,9 @@ function resolveFixtureMethodResult(methodName, definition, args) {
|
|
|
8785
10392
|
}
|
|
8786
10393
|
const explicitMatcher = isPlainObject2(entry.request) ? entry.request : void 0;
|
|
8787
10394
|
const matcher = explicitMatcher ?? Object.fromEntries(
|
|
8788
|
-
Object.entries(entry).filter(
|
|
10395
|
+
Object.entries(entry).filter(
|
|
10396
|
+
([key2]) => key2 !== "result" && key2 !== "response" && key2 !== "error"
|
|
10397
|
+
)
|
|
8789
10398
|
);
|
|
8790
10399
|
const firstArg = args[0];
|
|
8791
10400
|
let matched = false;
|
|
@@ -8841,8 +10450,8 @@ function createFixtureService(definition) {
|
|
|
8841
10450
|
);
|
|
8842
10451
|
}
|
|
8843
10452
|
function resolveFixturePath(commandPath) {
|
|
8844
|
-
const parsed =
|
|
8845
|
-
return
|
|
10453
|
+
const parsed = path9.parse(commandPath);
|
|
10454
|
+
return path9.join(parsed.dir, `${parsed.name}.fixture.json`);
|
|
8846
10455
|
}
|
|
8847
10456
|
function selectFixtureScenario(scenarios, selector) {
|
|
8848
10457
|
if (isNumericFixtureSelector(selector)) {
|
|
@@ -8864,12 +10473,14 @@ function selectFixtureScenario(scenarios, selector) {
|
|
|
8864
10473
|
async function loadFixtureScenario(command, selector) {
|
|
8865
10474
|
const commandPath = getCommandSourcePath(command);
|
|
8866
10475
|
if (commandPath === void 0) {
|
|
8867
|
-
throw new UserError(
|
|
10476
|
+
throw new UserError(
|
|
10477
|
+
`Fixture mode could not determine the source file for command "${command.name}".`
|
|
10478
|
+
);
|
|
8868
10479
|
}
|
|
8869
10480
|
const fixturePath = resolveFixturePath(commandPath);
|
|
8870
10481
|
let rawFixture;
|
|
8871
10482
|
try {
|
|
8872
|
-
rawFixture = await
|
|
10483
|
+
rawFixture = await readFile5(fixturePath, {
|
|
8873
10484
|
encoding: "utf8"
|
|
8874
10485
|
});
|
|
8875
10486
|
} catch {
|
|
@@ -8965,8 +10576,8 @@ function validateServices(services) {
|
|
|
8965
10576
|
}
|
|
8966
10577
|
}
|
|
8967
10578
|
}
|
|
8968
|
-
function getNestedValue(target,
|
|
8969
|
-
return
|
|
10579
|
+
function getNestedValue(target, path15) {
|
|
10580
|
+
return path15.reduce(
|
|
8970
10581
|
(current, segment) => current !== null && typeof current === "object" ? current[segment] : void 0,
|
|
8971
10582
|
target
|
|
8972
10583
|
);
|
|
@@ -9065,7 +10676,13 @@ function resolveDynamicLeaf(schema, rawSegments, casing, outputPath = [], displa
|
|
|
9065
10676
|
if (formatSegment(key2, casing) !== head) {
|
|
9066
10677
|
continue;
|
|
9067
10678
|
}
|
|
9068
|
-
return resolveDynamicLeaf(
|
|
10679
|
+
return resolveDynamicLeaf(
|
|
10680
|
+
childSchema,
|
|
10681
|
+
rest,
|
|
10682
|
+
casing,
|
|
10683
|
+
[...outputPath, key2],
|
|
10684
|
+
[...displayPath, key2]
|
|
10685
|
+
);
|
|
9069
10686
|
}
|
|
9070
10687
|
throw new UserError(`Unknown parameter "${[...displayPath, head].join(".")}".`);
|
|
9071
10688
|
}
|
|
@@ -9082,13 +10699,23 @@ function resolveDynamicLeaf(schema, rawSegments, casing, outputPath = [], displa
|
|
|
9082
10699
|
case "array": {
|
|
9083
10700
|
const itemSchema = unwrapOptional(unwrappedSchema.item);
|
|
9084
10701
|
if (itemSchema.kind !== "object") {
|
|
9085
|
-
throw new UserError(
|
|
10702
|
+
throw new UserError(
|
|
10703
|
+
`Array parameter "${toDisplayPath(displayPath)}" must use object items.`
|
|
10704
|
+
);
|
|
9086
10705
|
}
|
|
9087
10706
|
const [head, ...rest] = rawSegments;
|
|
9088
10707
|
if (head === void 0 || !isNumericFixtureSelector(head)) {
|
|
9089
|
-
throw new UserError(
|
|
10708
|
+
throw new UserError(
|
|
10709
|
+
`Array parameter "${toDisplayPath(displayPath)}" must use numeric indices.`
|
|
10710
|
+
);
|
|
9090
10711
|
}
|
|
9091
|
-
return resolveDynamicLeaf(
|
|
10712
|
+
return resolveDynamicLeaf(
|
|
10713
|
+
itemSchema,
|
|
10714
|
+
rest,
|
|
10715
|
+
casing,
|
|
10716
|
+
[...outputPath, head],
|
|
10717
|
+
[...displayPath, head]
|
|
10718
|
+
);
|
|
9092
10719
|
}
|
|
9093
10720
|
default:
|
|
9094
10721
|
throw new UserError(`Unknown parameter "${[...displayPath, ...rawSegments].join(".")}".`);
|
|
@@ -9124,7 +10751,9 @@ function finalizeDynamicValue(schema, value, displayPath) {
|
|
|
9124
10751
|
}
|
|
9125
10752
|
for (let index = 0; index < indices.length; index += 1) {
|
|
9126
10753
|
if (indices[index] !== index) {
|
|
9127
|
-
throw new UserError(
|
|
10754
|
+
throw new UserError(
|
|
10755
|
+
`Array parameter "${displayPath}" must use contiguous indices starting at 0.`
|
|
10756
|
+
);
|
|
9128
10757
|
}
|
|
9129
10758
|
}
|
|
9130
10759
|
return indices.map(
|
|
@@ -9180,7 +10809,9 @@ function finalizeDynamicValue(schema, value, displayPath) {
|
|
|
9180
10809
|
function parseDynamicValues(dynamicFields, rawArgv, casing) {
|
|
9181
10810
|
const rawValues = /* @__PURE__ */ new Map();
|
|
9182
10811
|
const providedFieldIds = /* @__PURE__ */ new Set();
|
|
9183
|
-
const sortedFields = [...dynamicFields].sort(
|
|
10812
|
+
const sortedFields = [...dynamicFields].sort(
|
|
10813
|
+
(left, right) => right.optionPath.length - left.optionPath.length
|
|
10814
|
+
);
|
|
9184
10815
|
for (let index = 0; index < rawArgv.length; index += 1) {
|
|
9185
10816
|
const token = rawArgv[index] ?? "";
|
|
9186
10817
|
if (!token.startsWith("--")) {
|
|
@@ -9267,7 +10898,9 @@ async function enforceVariantConstraints(params17, fields, dynamicFields, varian
|
|
|
9267
10898
|
);
|
|
9268
10899
|
}
|
|
9269
10900
|
}
|
|
9270
|
-
const invalidDynamicFieldId = branch.dynamicFieldIds.find(
|
|
10901
|
+
const invalidDynamicFieldId = branch.dynamicFieldIds.find(
|
|
10902
|
+
(fieldId) => providedDynamicFieldIds.has(fieldId)
|
|
10903
|
+
);
|
|
9271
10904
|
if (invalidDynamicFieldId !== void 0) {
|
|
9272
10905
|
const field = dynamicFieldById.get(invalidDynamicFieldId);
|
|
9273
10906
|
if (field !== void 0) {
|
|
@@ -9461,7 +11094,12 @@ async function executeCommand(state, services, requirementOptions, runtimeOption
|
|
|
9461
11094
|
throw new UserError("Operation cancelled.");
|
|
9462
11095
|
}
|
|
9463
11096
|
}
|
|
9464
|
-
const result = await invokeWithHumanInLoop(
|
|
11097
|
+
const result = await invokeWithHumanInLoop(
|
|
11098
|
+
state.command,
|
|
11099
|
+
context,
|
|
11100
|
+
runtimeOptions,
|
|
11101
|
+
state.commandPath
|
|
11102
|
+
);
|
|
9465
11103
|
if (output === "rich" && runtime.isFixture) {
|
|
9466
11104
|
writeRichHeader(`${state.command.name} (fixture)`);
|
|
9467
11105
|
}
|
|
@@ -9469,7 +11107,10 @@ async function executeCommand(state, services, requirementOptions, runtimeOption
|
|
|
9469
11107
|
renderHumanInLoopPending(result);
|
|
9470
11108
|
return;
|
|
9471
11109
|
}
|
|
9472
|
-
renderResult(state.command, result, output, primitives);
|
|
11110
|
+
const renderStatus = renderResult(state.command, result, output, primitives);
|
|
11111
|
+
if (renderStatus.mcpError) {
|
|
11112
|
+
process.exitCode = 1;
|
|
11113
|
+
}
|
|
9473
11114
|
});
|
|
9474
11115
|
}
|
|
9475
11116
|
function handleRunError(error2, verbose) {
|
|
@@ -9531,7 +11172,13 @@ async function runCLI(roots, options = {}) {
|
|
|
9531
11172
|
await executeCommand(state, servicesWithBuiltIns, requirementOptions, runtimeOptions);
|
|
9532
11173
|
};
|
|
9533
11174
|
for (const child of root.children) {
|
|
9534
|
-
const command = createNodeCommand(
|
|
11175
|
+
const command = createNodeCommand(
|
|
11176
|
+
child,
|
|
11177
|
+
casing,
|
|
11178
|
+
globalLongOptionFlags,
|
|
11179
|
+
execute,
|
|
11180
|
+
presetsEnabled
|
|
11181
|
+
);
|
|
9535
11182
|
if (command === null) {
|
|
9536
11183
|
continue;
|
|
9537
11184
|
}
|
|
@@ -9572,7 +11219,7 @@ var DCS = 144;
|
|
|
9572
11219
|
var SOS = 152;
|
|
9573
11220
|
var PM = 158;
|
|
9574
11221
|
var APC = 159;
|
|
9575
|
-
function
|
|
11222
|
+
function stripAnsi4(input) {
|
|
9576
11223
|
let output = "";
|
|
9577
11224
|
for (let index = 0; index < input.length; index += 1) {
|
|
9578
11225
|
const code = input.charCodeAt(index);
|
|
@@ -10261,7 +11908,7 @@ var TerminalScreen = class {
|
|
|
10261
11908
|
cursor,
|
|
10262
11909
|
size
|
|
10263
11910
|
}) {
|
|
10264
|
-
this.lines = Object.freeze(lines.map((line) =>
|
|
11911
|
+
this.lines = Object.freeze(lines.map((line) => stripAnsi4(line)));
|
|
10265
11912
|
this.rawLines = Object.freeze([...rawLines]);
|
|
10266
11913
|
this.cursor = Object.freeze({ ...cursor });
|
|
10267
11914
|
this.size = Object.freeze({ ...size });
|
|
@@ -10346,14 +11993,14 @@ var TerminalSession = class {
|
|
|
10346
11993
|
});
|
|
10347
11994
|
});
|
|
10348
11995
|
}
|
|
10349
|
-
async type(
|
|
10350
|
-
for (const character of
|
|
11996
|
+
async type(text5) {
|
|
11997
|
+
for (const character of text5) {
|
|
10351
11998
|
await this.send(character);
|
|
10352
11999
|
await sleep2(TYPE_DELAY_MS);
|
|
10353
12000
|
}
|
|
10354
12001
|
}
|
|
10355
|
-
async fill(
|
|
10356
|
-
await this.send(
|
|
12002
|
+
async fill(text5) {
|
|
12003
|
+
await this.send(text5.replace(/\r?\n/g, "\r"));
|
|
10357
12004
|
}
|
|
10358
12005
|
async press(key2) {
|
|
10359
12006
|
await this.send(keyToSequence(key2));
|
|
@@ -10411,7 +12058,7 @@ var TerminalSession = class {
|
|
|
10411
12058
|
});
|
|
10412
12059
|
}
|
|
10413
12060
|
async history(opts) {
|
|
10414
|
-
const normalized = normalizeHistoryBuffer(
|
|
12061
|
+
const normalized = normalizeHistoryBuffer(stripAnsi4(this.rawBuffer));
|
|
10415
12062
|
const lines = splitHistoryLines(normalized);
|
|
10416
12063
|
if (opts?.last === void 0) {
|
|
10417
12064
|
return lines;
|
|
@@ -10517,7 +12164,7 @@ function isMissingFileError(error2) {
|
|
|
10517
12164
|
return "code" in error2 && error2.code === "ENOENT";
|
|
10518
12165
|
}
|
|
10519
12166
|
function matchPattern(buffer, pattern) {
|
|
10520
|
-
const clean = normalizeHistoryBuffer(
|
|
12167
|
+
const clean = normalizeHistoryBuffer(stripAnsi4(buffer));
|
|
10521
12168
|
for (const line of clean.split("\n")) {
|
|
10522
12169
|
if (typeof pattern === "string") {
|
|
10523
12170
|
if (line.includes(pattern)) return line;
|
|
@@ -10777,7 +12424,7 @@ var params2 = S.Object({
|
|
|
10777
12424
|
rows: S.Optional(S.Number({ description: "Terminal height in rows" })),
|
|
10778
12425
|
observe: S.Optional(S.Boolean({ description: "Mirror PTY output to stderr" }))
|
|
10779
12426
|
});
|
|
10780
|
-
var
|
|
12427
|
+
var createSession2 = defineCommand({
|
|
10781
12428
|
name: "create-session",
|
|
10782
12429
|
description: "Spawn an interactive CLI in a PTY",
|
|
10783
12430
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -10829,7 +12476,7 @@ var getSession = defineCommand({
|
|
|
10829
12476
|
|
|
10830
12477
|
// ../agent-skill-config/src/configs.ts
|
|
10831
12478
|
import os2 from "node:os";
|
|
10832
|
-
import
|
|
12479
|
+
import path10 from "node:path";
|
|
10833
12480
|
|
|
10834
12481
|
// ../agent-defs/src/agents/claude-code.ts
|
|
10835
12482
|
var claudeCodeAgent = {
|
|
@@ -11350,20 +12997,20 @@ function getConfigFormat(pathOrFormat) {
|
|
|
11350
12997
|
}
|
|
11351
12998
|
return formatRegistry[formatName];
|
|
11352
12999
|
}
|
|
11353
|
-
function detectFormat(
|
|
11354
|
-
const ext = getExtension(
|
|
13000
|
+
function detectFormat(path15) {
|
|
13001
|
+
const ext = getExtension(path15);
|
|
11355
13002
|
return extensionMap[ext];
|
|
11356
13003
|
}
|
|
11357
|
-
function getExtension(
|
|
11358
|
-
const lastDot =
|
|
13004
|
+
function getExtension(path15) {
|
|
13005
|
+
const lastDot = path15.lastIndexOf(".");
|
|
11359
13006
|
if (lastDot === -1) {
|
|
11360
13007
|
return "";
|
|
11361
13008
|
}
|
|
11362
|
-
return
|
|
13009
|
+
return path15.slice(lastDot).toLowerCase();
|
|
11363
13010
|
}
|
|
11364
13011
|
|
|
11365
13012
|
// ../config-mutations/src/execution/path-utils.ts
|
|
11366
|
-
import
|
|
13013
|
+
import path11 from "node:path";
|
|
11367
13014
|
function expandHome(targetPath, homeDir) {
|
|
11368
13015
|
if (!targetPath?.startsWith("~")) {
|
|
11369
13016
|
return targetPath;
|
|
@@ -11380,7 +13027,7 @@ function expandHome(targetPath, homeDir) {
|
|
|
11380
13027
|
remainder = remainder.slice(1);
|
|
11381
13028
|
}
|
|
11382
13029
|
}
|
|
11383
|
-
return remainder.length === 0 ? homeDir :
|
|
13030
|
+
return remainder.length === 0 ? homeDir : path11.join(homeDir, remainder);
|
|
11384
13031
|
}
|
|
11385
13032
|
function validateHomePath(targetPath) {
|
|
11386
13033
|
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
@@ -11398,12 +13045,12 @@ function resolvePath(rawPath, homeDir, pathMapper) {
|
|
|
11398
13045
|
if (!pathMapper) {
|
|
11399
13046
|
return expanded;
|
|
11400
13047
|
}
|
|
11401
|
-
const rawDirectory =
|
|
13048
|
+
const rawDirectory = path11.dirname(expanded);
|
|
11402
13049
|
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
11403
13050
|
targetDirectory: rawDirectory
|
|
11404
13051
|
});
|
|
11405
|
-
const filename =
|
|
11406
|
-
return filename.length === 0 ? mappedDirectory :
|
|
13052
|
+
const filename = path11.basename(expanded);
|
|
13053
|
+
return filename.length === 0 ? mappedDirectory : path11.join(mappedDirectory, filename);
|
|
11407
13054
|
}
|
|
11408
13055
|
|
|
11409
13056
|
// ../config-mutations/src/fs-utils.ts
|
|
@@ -12010,8 +13657,8 @@ import Mustache2 from "mustache";
|
|
|
12010
13657
|
var originalEscape = Mustache2.escape;
|
|
12011
13658
|
|
|
12012
13659
|
// ../agent-skill-config/src/templates.ts
|
|
12013
|
-
import { readFile as
|
|
12014
|
-
import
|
|
13660
|
+
import { readFile as readFile6, stat as stat2 } from "node:fs/promises";
|
|
13661
|
+
import path12 from "node:path";
|
|
12015
13662
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12016
13663
|
|
|
12017
13664
|
// ../agent-skill-config/src/apply.ts
|
|
@@ -12070,9 +13717,9 @@ async function installSkill(agentId, skill, options) {
|
|
|
12070
13717
|
|
|
12071
13718
|
// src/commands/installer.ts
|
|
12072
13719
|
import os3 from "node:os";
|
|
12073
|
-
import
|
|
13720
|
+
import path13 from "node:path";
|
|
12074
13721
|
import * as nodeFs from "node:fs/promises";
|
|
12075
|
-
import { readFile as
|
|
13722
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
12076
13723
|
var DEFAULT_INSTALL_AGENT = "claude-code";
|
|
12077
13724
|
var DEFAULT_INSTALL_SCOPE = "local";
|
|
12078
13725
|
var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
|
|
@@ -12122,7 +13769,7 @@ async function loadTerminalPilotTemplate() {
|
|
|
12122
13769
|
];
|
|
12123
13770
|
for (const candidate of candidates) {
|
|
12124
13771
|
try {
|
|
12125
|
-
terminalPilotTemplateCache = await
|
|
13772
|
+
terminalPilotTemplateCache = await readFile7(candidate, "utf8");
|
|
12126
13773
|
return terminalPilotTemplateCache;
|
|
12127
13774
|
} catch (error2) {
|
|
12128
13775
|
if (!isNotFoundError2(error2)) {
|
|
@@ -12137,7 +13784,7 @@ function resolveHomeRelativePath(targetPath, homeDir) {
|
|
|
12137
13784
|
return homeDir;
|
|
12138
13785
|
}
|
|
12139
13786
|
if (targetPath.startsWith("~/")) {
|
|
12140
|
-
return
|
|
13787
|
+
return path13.join(homeDir, targetPath.slice(2));
|
|
12141
13788
|
}
|
|
12142
13789
|
return targetPath;
|
|
12143
13790
|
}
|
|
@@ -12147,12 +13794,12 @@ function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
|
|
|
12147
13794
|
throwUnsupportedAgent(agent);
|
|
12148
13795
|
}
|
|
12149
13796
|
return {
|
|
12150
|
-
displayPath:
|
|
13797
|
+
displayPath: path13.join(
|
|
12151
13798
|
scope === "global" ? config.globalSkillDir : config.localSkillDir,
|
|
12152
13799
|
TERMINAL_PILOT_SKILL_NAME
|
|
12153
13800
|
),
|
|
12154
|
-
fullPath:
|
|
12155
|
-
scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) :
|
|
13801
|
+
fullPath: path13.join(
|
|
13802
|
+
scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path13.resolve(cwd, config.localSkillDir),
|
|
12156
13803
|
TERMINAL_PILOT_SKILL_NAME
|
|
12157
13804
|
)
|
|
12158
13805
|
};
|
|
@@ -12344,17 +13991,17 @@ function colorsEqual(left, right) {
|
|
|
12344
13991
|
}
|
|
12345
13992
|
return false;
|
|
12346
13993
|
}
|
|
12347
|
-
function pushRun(runs, style,
|
|
12348
|
-
if (
|
|
13994
|
+
function pushRun(runs, style, text5) {
|
|
13995
|
+
if (text5.length === 0) {
|
|
12349
13996
|
return;
|
|
12350
13997
|
}
|
|
12351
13998
|
const previous = runs.at(-1);
|
|
12352
|
-
if (previous &&
|
|
12353
|
-
previous.text +=
|
|
13999
|
+
if (previous && text5 !== "\n" && previous.text !== "\n" && stylesEqual(previous, style)) {
|
|
14000
|
+
previous.text += text5;
|
|
12354
14001
|
return;
|
|
12355
14002
|
}
|
|
12356
14003
|
runs.push({
|
|
12357
|
-
text:
|
|
14004
|
+
text: text5,
|
|
12358
14005
|
fg: style.fg,
|
|
12359
14006
|
bg: style.bg,
|
|
12360
14007
|
bold: style.bold,
|
|
@@ -12953,10 +14600,10 @@ function measureLines(lines) {
|
|
|
12953
14600
|
0
|
|
12954
14601
|
);
|
|
12955
14602
|
}
|
|
12956
|
-
function displayWidth(
|
|
14603
|
+
function displayWidth(text5) {
|
|
12957
14604
|
const segmenter = new Intl.Segmenter();
|
|
12958
14605
|
let width = 0;
|
|
12959
|
-
for (const { segment } of segmenter.segment(
|
|
14606
|
+
for (const { segment } of segmenter.segment(text5)) {
|
|
12960
14607
|
const cp = segment.codePointAt(0);
|
|
12961
14608
|
width += cp !== void 0 && isWideCodePoint(cp) ? 2 : 1;
|
|
12962
14609
|
}
|
|
@@ -13220,7 +14867,7 @@ var waitForExit2 = defineCommand({
|
|
|
13220
14867
|
var children = [
|
|
13221
14868
|
install,
|
|
13222
14869
|
uninstall,
|
|
13223
|
-
|
|
14870
|
+
createSession2,
|
|
13224
14871
|
fill,
|
|
13225
14872
|
type,
|
|
13226
14873
|
pressKey,
|
|
@@ -13265,7 +14912,7 @@ async function isDirectExecution(argv) {
|
|
|
13265
14912
|
try {
|
|
13266
14913
|
const modulePath = fileURLToPath5(import.meta.url);
|
|
13267
14914
|
const [resolvedEntryPoint, resolvedModulePath] = await Promise.all([
|
|
13268
|
-
realpath(
|
|
14915
|
+
realpath(path14.resolve(entryPoint)),
|
|
13269
14916
|
realpath(modulePath)
|
|
13270
14917
|
]);
|
|
13271
14918
|
return resolvedEntryPoint === resolvedModulePath;
|