@kaynooo/cli-workspace 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +63 -2
- package/dist/audit-cli.js +1462 -0
- package/dist/cli.js +2217 -0
- package/dist/repos-cli.js +1496 -0
- package/dist/sweep-cli.js +1739 -0
- package/package.json +37 -4
|
@@ -0,0 +1,1496 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun --no-env-file
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/repos-cli.ts
|
|
5
|
+
import process14 from "process";
|
|
6
|
+
|
|
7
|
+
// ../kit/src/advisory.ts
|
|
8
|
+
var SEVERITIES = ["low", "moderate", "high", "critical"];
|
|
9
|
+
function severityRank(severity) {
|
|
10
|
+
return SEVERITIES.indexOf(severity);
|
|
11
|
+
}
|
|
12
|
+
function isIgnored(advisory, ignore) {
|
|
13
|
+
return ignore.includes(advisory.ghsa ?? "") || ignore.includes(String(advisory.id));
|
|
14
|
+
}
|
|
15
|
+
// ../kit/src/args.ts
|
|
16
|
+
import process5 from "process";
|
|
17
|
+
import { parseArgs } from "util";
|
|
18
|
+
|
|
19
|
+
// ../kit/src/format.ts
|
|
20
|
+
import process3 from "process";
|
|
21
|
+
|
|
22
|
+
// ../kit/src/style.ts
|
|
23
|
+
import process2 from "process";
|
|
24
|
+
|
|
25
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
26
|
+
function x(s) {
|
|
27
|
+
let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
|
|
28
|
+
if (!/^[a-f0-9]+$/.test(r))
|
|
29
|
+
return [0, 0, 0];
|
|
30
|
+
let t = r.length, n = (c) => Number.parseInt(c, 16);
|
|
31
|
+
if (t === 3 || t === 4) {
|
|
32
|
+
let c = n(r[0] + r[0]), u = n(r[1] + r[1]), h = n(r[2] + r[2]), g = t === 4 ? n(r[3] + r[3]) : undefined;
|
|
33
|
+
return g !== undefined ? [c, u, h, g] : [c, u, h];
|
|
34
|
+
}
|
|
35
|
+
if (t !== 6 && t !== 8)
|
|
36
|
+
throw Error(`Invalid hex color length: ${t}`);
|
|
37
|
+
let a = n(r.slice(0, 2)), b = n(r.slice(2, 4)), o = n(r.slice(4, 6)), y = t === 8 ? n(r.slice(6, 8)) : undefined;
|
|
38
|
+
if ([a, b, o].some(Number.isNaN) || y !== undefined && Number.isNaN(y))
|
|
39
|
+
throw Error(`Invalid hex color value: ${s}`);
|
|
40
|
+
return y !== undefined ? [a, b, o, y] : [a, b, o];
|
|
41
|
+
}
|
|
42
|
+
function A(s) {
|
|
43
|
+
let [r, t, n, a] = s;
|
|
44
|
+
if (a)
|
|
45
|
+
return `#${(r << 24 | t << 16 | n << 8 | Math.round((a ?? 1) * 255)).toString(16).padStart(8, "0").toUpperCase()}`;
|
|
46
|
+
return `#${(r << 16 | t << 8 | n).toString(16).padStart(6, "0").toUpperCase()}`;
|
|
47
|
+
}
|
|
48
|
+
function R(s, r, t) {
|
|
49
|
+
s = (s % 360 + 360) % 360, t /= 100;
|
|
50
|
+
let n = r * Math.min(t, 1 - t) / 100, a = (b) => {
|
|
51
|
+
let o = (b + s / 30) % 12, y = t - n * Math.max(Math.min(o - 3, 9 - o, 1), -1);
|
|
52
|
+
return Math.round(255 * y);
|
|
53
|
+
};
|
|
54
|
+
return [a(0), a(8), a(4)];
|
|
55
|
+
}
|
|
56
|
+
function k(s) {
|
|
57
|
+
let [r, t, n] = s;
|
|
58
|
+
r /= 255, t /= 255, n /= 255;
|
|
59
|
+
let a = Math.max(r, t, n), b = Math.min(r, t, n), o = 0, y = 0, c = (a + b) / 2;
|
|
60
|
+
if (a !== b) {
|
|
61
|
+
let u = a - b;
|
|
62
|
+
if (y = c > 0 && c < 1 ? u / (1 - Math.abs(2 * c - 1)) : 0, a === r)
|
|
63
|
+
o = (t - n) / u + (t < n ? 6 : 0);
|
|
64
|
+
else if (a === t)
|
|
65
|
+
o = (n - r) / u + 2;
|
|
66
|
+
else
|
|
67
|
+
o = (r - t) / u + 4;
|
|
68
|
+
o /= 6;
|
|
69
|
+
}
|
|
70
|
+
return [Math.round(o * 360), Math.round(y * 100), Math.round(c * 100)];
|
|
71
|
+
}
|
|
72
|
+
function H(s, r, t) {
|
|
73
|
+
s = (s % 360 + 360) % 360, r /= 100, t /= 100;
|
|
74
|
+
let n = (a) => {
|
|
75
|
+
let b = (a + s / 60) % 6, o = t - t * r * Math.max(Math.min(b, 4 - b, 1), 0);
|
|
76
|
+
return Math.round(255 * o);
|
|
77
|
+
};
|
|
78
|
+
return [n(5), n(3), n(1)];
|
|
79
|
+
}
|
|
80
|
+
function f(s) {
|
|
81
|
+
let [r, t, n] = s;
|
|
82
|
+
r /= 255, t /= 255, n /= 255;
|
|
83
|
+
let a = Math.max(r, t, n), b = Math.min(r, t, n), o = a - b, y = 0, c = a === 0 ? 0 : o / a, u = a;
|
|
84
|
+
if (o !== 0) {
|
|
85
|
+
if (a === r)
|
|
86
|
+
y = (t - n) / o + (t < n ? 6 : 0);
|
|
87
|
+
else if (a === t)
|
|
88
|
+
y = (n - r) / o + 2;
|
|
89
|
+
else
|
|
90
|
+
y = (r - t) / o + 4;
|
|
91
|
+
y /= 6;
|
|
92
|
+
}
|
|
93
|
+
return [Math.round(y * 360), Math.round(c * 100), Math.round(u * 100)];
|
|
94
|
+
}
|
|
95
|
+
function O(s) {
|
|
96
|
+
let r = s[0] / 255, t = s[1] / 255, n = s[2] / 255, a = 1 - Math.max(r, t, n);
|
|
97
|
+
if (a === 1)
|
|
98
|
+
return [0, 0, 0, 100];
|
|
99
|
+
let b = (1 - r - a) / (1 - a), o = (1 - t - a) / (1 - a), y = (1 - n - a) / (1 - a);
|
|
100
|
+
return [Math.round(b * 100), Math.round(o * 100), Math.round(y * 100), Math.round(a * 100)];
|
|
101
|
+
}
|
|
102
|
+
function v(s, r, t, n) {
|
|
103
|
+
return s /= 100, r /= 100, t /= 100, n /= 100, [Math.round(255 * (1 - s) * (1 - n)), Math.round(255 * (1 - r) * (1 - n)), Math.round(255 * (1 - t) * (1 - n))];
|
|
104
|
+
}
|
|
105
|
+
var m = (s) => s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
106
|
+
var p = (s) => s <= 0.0031308 ? 12.92 * s : 1.055 * s ** 0.4166666666666667 - 0.055;
|
|
107
|
+
var i = (s, r) => Math.round(s * 10 ** r) / 10 ** r;
|
|
108
|
+
function T(s) {
|
|
109
|
+
let r = m(s[0] / 255), t = m(s[1] / 255), n = m(s[2] / 255), a = Math.cbrt(0.4122214708 * r + 0.5363325363 * t + 0.0514459929 * n), b = Math.cbrt(0.2119034982 * r + 0.6806995451 * t + 0.1073969566 * n), o = Math.cbrt(0.0883024619 * r + 0.2817188376 * t + 0.6299787005 * n);
|
|
110
|
+
return [i(0.2104542553 * a + 0.793617785 * b - 0.0040720468 * o, 4), i(1.9779984951 * a - 2.428592205 * b + 0.4505937099 * o, 4), i(0.0259040371 * a + 0.7827717662 * b - 0.808675766 * o, 4)];
|
|
111
|
+
}
|
|
112
|
+
function M(s, r, t) {
|
|
113
|
+
let n = (s + 0.3963377774 * r + 0.2158037573 * t) ** 3, a = (s - 0.1055613458 * r - 0.0638541728 * t) ** 3, b = (s - 0.0894841775 * r - 1.291485548 * t) ** 3, o = p(4.0767416621 * n - 3.3077115913 * a + 0.2309699292 * b), y = p(-1.2684380046 * n + 2.6097574011 * a - 0.3413193965 * b), c = p(-0.0041960863 * n - 0.7034186147 * a + 1.707614701 * b), u = (h) => Math.min(255, Math.max(0, Math.round(h * 255)));
|
|
114
|
+
return [u(o), u(y), u(c)];
|
|
115
|
+
}
|
|
116
|
+
function d(s) {
|
|
117
|
+
let [r, t, n] = T(s), a = Math.sqrt(t * t + n * n), b = (Math.atan2(n, t) * 180 / Math.PI + 360) % 360;
|
|
118
|
+
return [i(r, 4), i(a, 4), i(b, 2)];
|
|
119
|
+
}
|
|
120
|
+
function C(s, r, t) {
|
|
121
|
+
let n = t * Math.PI / 180;
|
|
122
|
+
return M(s, r * Math.cos(n), r * Math.sin(n));
|
|
123
|
+
}
|
|
124
|
+
function l(s) {
|
|
125
|
+
let r = s / 255;
|
|
126
|
+
return r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055) ** 2.4;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class e {
|
|
130
|
+
r;
|
|
131
|
+
g;
|
|
132
|
+
b;
|
|
133
|
+
a;
|
|
134
|
+
constructor(s, r, t, n = 1) {
|
|
135
|
+
this.r = s, this.g = r, this.b = t, this.a = n;
|
|
136
|
+
}
|
|
137
|
+
static from(s, r) {
|
|
138
|
+
if (s === "hex") {
|
|
139
|
+
let [t, n, a, b] = x(r);
|
|
140
|
+
return new e(t, n, a, b);
|
|
141
|
+
}
|
|
142
|
+
if (s === "rgb") {
|
|
143
|
+
if (Array.isArray(r)) {
|
|
144
|
+
let [o, y, c, u] = r;
|
|
145
|
+
return new e(o, y, c, u);
|
|
146
|
+
}
|
|
147
|
+
let { r: t, g: n, b: a, a: b } = r;
|
|
148
|
+
return new e(t, n, a, b);
|
|
149
|
+
}
|
|
150
|
+
if (s === "hsl") {
|
|
151
|
+
let t, n, a, b;
|
|
152
|
+
if (Array.isArray(r))
|
|
153
|
+
[t, n, a, b] = r;
|
|
154
|
+
else
|
|
155
|
+
({ h: t, s: n, l: a, a: b } = r);
|
|
156
|
+
let [o, y, c] = R(t, n, a);
|
|
157
|
+
return new e(o, y, c, b);
|
|
158
|
+
}
|
|
159
|
+
if (s === "hsv") {
|
|
160
|
+
let t, n, a, b;
|
|
161
|
+
if (Array.isArray(r))
|
|
162
|
+
[t, n, a, b] = r;
|
|
163
|
+
else
|
|
164
|
+
({ h: t, s: n, v: a, a: b } = r);
|
|
165
|
+
let [o, y, c] = H(t, n, a);
|
|
166
|
+
return new e(o, y, c, b);
|
|
167
|
+
}
|
|
168
|
+
if (s === "cmyk") {
|
|
169
|
+
let t, n, a, b, o;
|
|
170
|
+
if (Array.isArray(r))
|
|
171
|
+
[t, n, a, b, o] = r;
|
|
172
|
+
else
|
|
173
|
+
({ c: t, m: n, y: a, k: b, a: o } = r);
|
|
174
|
+
let [y, c, u] = v(t, n, a, b);
|
|
175
|
+
return new e(y, c, u, o);
|
|
176
|
+
}
|
|
177
|
+
if (s === "oklch") {
|
|
178
|
+
let t, n, a, b;
|
|
179
|
+
if (Array.isArray(r))
|
|
180
|
+
[t, n, a, b] = r;
|
|
181
|
+
else
|
|
182
|
+
({ l: t, c: n, h: a, a: b } = r);
|
|
183
|
+
let [o, y, c] = C(t, n, a);
|
|
184
|
+
return new e(o, y, c, b);
|
|
185
|
+
}
|
|
186
|
+
if (s === "oklab") {
|
|
187
|
+
let t, n, a, b;
|
|
188
|
+
if (Array.isArray(r))
|
|
189
|
+
[t, n, a, b] = r;
|
|
190
|
+
else
|
|
191
|
+
({ l: t, a: n, b: a, alpha: b } = r);
|
|
192
|
+
let [o, y, c] = M(t, n, a);
|
|
193
|
+
return new e(o, y, c, b);
|
|
194
|
+
}
|
|
195
|
+
throw Error("Invalid input");
|
|
196
|
+
}
|
|
197
|
+
to(s) {
|
|
198
|
+
switch (s) {
|
|
199
|
+
case "rgb":
|
|
200
|
+
return [this.r, this.g, this.b];
|
|
201
|
+
case "rgba":
|
|
202
|
+
return [this.r, this.g, this.b, this.a];
|
|
203
|
+
case "hex":
|
|
204
|
+
return A([this.r, this.g, this.b]);
|
|
205
|
+
case "hexa":
|
|
206
|
+
return A([this.r, this.g, this.b, this.a]);
|
|
207
|
+
case "hsl":
|
|
208
|
+
return k([this.r, this.g, this.b]);
|
|
209
|
+
case "hsla":
|
|
210
|
+
return [...k([this.r, this.g, this.b]), this.a];
|
|
211
|
+
case "hsv":
|
|
212
|
+
return f([this.r, this.g, this.b]);
|
|
213
|
+
case "hsva":
|
|
214
|
+
return [...f([this.r, this.g, this.b]), this.a];
|
|
215
|
+
case "cmyk":
|
|
216
|
+
return O([this.r, this.g, this.b]);
|
|
217
|
+
case "cmyka":
|
|
218
|
+
return [...O([this.r, this.g, this.b]), this.a];
|
|
219
|
+
case "oklch":
|
|
220
|
+
return d([this.r, this.g, this.b]);
|
|
221
|
+
case "oklcha":
|
|
222
|
+
return [...d([this.r, this.g, this.b]), this.a];
|
|
223
|
+
case "oklab":
|
|
224
|
+
return T([this.r, this.g, this.b]);
|
|
225
|
+
case "oklaba":
|
|
226
|
+
return [...T([this.r, this.g, this.b]), this.a];
|
|
227
|
+
default:
|
|
228
|
+
return [this.r, this.g, this.b];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
getLuminance() {
|
|
232
|
+
let s = l(this.r), r = l(this.g), t = l(this.b);
|
|
233
|
+
return 0.2126 * s + 0.7152 * r + 0.0722 * t;
|
|
234
|
+
}
|
|
235
|
+
contrastRatio(s) {
|
|
236
|
+
let r = this.getLuminance(), t = s.getLuminance(), [n, a] = r >= t ? [r, t] : [t, r];
|
|
237
|
+
return (n + 0.05) / (a + 0.05);
|
|
238
|
+
}
|
|
239
|
+
bestContrastForeground() {
|
|
240
|
+
let s = new e(255, 255, 255, 1), r = new e(0, 0, 0, 1), t = this.contrastRatio(s), n = this.contrastRatio(r);
|
|
241
|
+
return t >= n ? s : r;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
|
|
246
|
+
var r = 39;
|
|
247
|
+
var o = 49;
|
|
248
|
+
var f2 = "\x1B[";
|
|
249
|
+
var C2 = { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29], black: [30, r], red: [31, r], green: [32, r], yellow: [33, r], blue: [34, r], magenta: [35, r], cyan: [36, r], white: [37, r], gray: [90, r], grey: [90, r], blackBright: [90, r], redBright: [91, r], greenBright: [92, r], yellowBright: [93, r], blueBright: [94, r], magentaBright: [95, r], cyanBright: [96, r], whiteBright: [97, r], bgBlack: [40, o], bgRed: [41, o], bgGreen: [42, o], bgYellow: [43, o], bgBlue: [44, o], bgMagenta: [45, o], bgCyan: [46, o], bgWhite: [47, o], bgBlackBright: [100, o], bgRedBright: [101, o], bgGreenBright: [102, o], bgYellowBright: [103, o], bgBlueBright: [104, o], bgMagentaBright: [105, o], bgCyanBright: [106, o], bgWhiteBright: [107, o], rgb: (i, n, a, l = "fg") => [`${l === "fg" ? 38 : 48};2;${i};${n};${a}`, l === "fg" ? r : o], hex: (i, n = "fg") => {
|
|
250
|
+
let [a, l, s] = e.from("hex", i).to("rgb");
|
|
251
|
+
return [`${n === "fg" ? 38 : 48};2;${a};${l};${s}`, n === "fg" ? r : o];
|
|
252
|
+
}, ansi256: (i, n = "fg") => [`${n === "fg" ? 38 : 48};5;${i}`, n === "fg" ? r : o] };
|
|
253
|
+
var b = new Set(["", "1", "2", "3", "true"]);
|
|
254
|
+
function h(i, n = globalThis.process?.env ?? {}) {
|
|
255
|
+
if (n.FORCE_COLOR !== undefined)
|
|
256
|
+
return b.has(n.FORCE_COLOR);
|
|
257
|
+
if (n.NO_COLOR !== undefined || n.NODE_DISABLE_COLORS !== undefined || n.TERM === "dumb")
|
|
258
|
+
return false;
|
|
259
|
+
return i?.isTTY === true;
|
|
260
|
+
}
|
|
261
|
+
function d2(i, n, a) {
|
|
262
|
+
if (!h(a.stream ?? globalThis.process?.stdout, a.env))
|
|
263
|
+
return n;
|
|
264
|
+
let l = "", s = "";
|
|
265
|
+
for (let g = 0, y = i.length;g < y; g++) {
|
|
266
|
+
let [c, p] = i[g];
|
|
267
|
+
l += `${f2}${c}m`, s = `${f2}${p}m${s}`;
|
|
268
|
+
}
|
|
269
|
+
return l + n + s;
|
|
270
|
+
}
|
|
271
|
+
function u(i, n = []) {
|
|
272
|
+
return new Proxy((l) => d2(n, l, i), { get(l, s) {
|
|
273
|
+
let g = C2[s];
|
|
274
|
+
if (Array.isArray(g))
|
|
275
|
+
return u(i, [...n, g]);
|
|
276
|
+
if (typeof g === "function")
|
|
277
|
+
return (...y) => u(i, [...n, g(...y)]);
|
|
278
|
+
throw Error(`Unknown style: ${String(s)}`);
|
|
279
|
+
} });
|
|
280
|
+
}
|
|
281
|
+
function le(i = {}) {
|
|
282
|
+
return u(i);
|
|
283
|
+
}
|
|
284
|
+
var t = le();
|
|
285
|
+
|
|
286
|
+
// ../kit/src/style.ts
|
|
287
|
+
var MARKS = {
|
|
288
|
+
ok: { symbol: "\u2713", word: "ok:", paint: (colors) => colors.green },
|
|
289
|
+
warn: { symbol: "!", word: "warning:", paint: (colors) => colors.yellow },
|
|
290
|
+
problem: { symbol: "\u2717", word: "error:", paint: (colors) => colors.red },
|
|
291
|
+
note: { symbol: "\xB7", word: "note:", paint: (colors) => colors.dim }
|
|
292
|
+
};
|
|
293
|
+
function takesControlCodes(stream, env = process2.env) {
|
|
294
|
+
return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
|
|
295
|
+
}
|
|
296
|
+
function paint(stream, env = process2.env) {
|
|
297
|
+
return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
|
|
298
|
+
}
|
|
299
|
+
function marked(mark, text, stream, detail, env = process2.env) {
|
|
300
|
+
const colors = paint(stream, env);
|
|
301
|
+
const { symbol, word, paint: tint } = MARKS[mark];
|
|
302
|
+
const head = takesControlCodes(stream, env) ? tint(colors)(symbol) : word;
|
|
303
|
+
return `${head} ${text}${detail === undefined ? "" : ` ${colors.dim(detail)}`}`;
|
|
304
|
+
}
|
|
305
|
+
function ok(text, detail) {
|
|
306
|
+
console.log(marked("ok", text, process2.stdout, detail));
|
|
307
|
+
}
|
|
308
|
+
function note(text, detail) {
|
|
309
|
+
console.log(marked("note", text, process2.stdout, detail));
|
|
310
|
+
}
|
|
311
|
+
function problem(text, detail) {
|
|
312
|
+
console.error(marked("problem", text, process2.stderr, detail));
|
|
313
|
+
}
|
|
314
|
+
var ROLES = {
|
|
315
|
+
accent: (colors) => colors.cyan,
|
|
316
|
+
id: (colors) => colors.yellow,
|
|
317
|
+
path: (colors) => colors.blue,
|
|
318
|
+
label: (colors) => colors.dim,
|
|
319
|
+
value: (colors) => colors.bold
|
|
320
|
+
};
|
|
321
|
+
function role(name, stream, env = process2.env) {
|
|
322
|
+
return ROLES[name](paint(stream, env));
|
|
323
|
+
}
|
|
324
|
+
var STATUSES = {
|
|
325
|
+
passed: { symbol: "\u2713", words: ["passed", "success", "succeeded", "ok"], paint: (colors) => colors.green },
|
|
326
|
+
failed: { symbol: "\u2717", words: ["failed", "failure", "error", "errored"], paint: (colors) => colors.red },
|
|
327
|
+
running: { symbol: "\u25CF", words: ["running", "in_progress", "started"], paint: (colors) => colors.cyan },
|
|
328
|
+
queued: { symbol: "\u25CB", words: ["queued", "waiting", "pending", "created", "blocked", "scheduled"], paint: (colors) => colors.yellow },
|
|
329
|
+
skipped: { symbol: "-", words: ["skipped", "cancelled", "canceled", "manual"], paint: (colors) => colors.dim }
|
|
330
|
+
};
|
|
331
|
+
var STATUS_OF = new Map(Object.entries(STATUSES).flatMap(([status, { words }]) => words.map((word) => [word, status])));
|
|
332
|
+
|
|
333
|
+
// ../kit/src/format.ts
|
|
334
|
+
var UNITS = [["d", 86400], ["h", 3600], ["m", 60], ["s", 1]];
|
|
335
|
+
function formatSeconds(total) {
|
|
336
|
+
let rest = Math.max(0, Math.round(total));
|
|
337
|
+
const amounts = UNITS.map(([unit, size]) => {
|
|
338
|
+
const amount = Math.floor(rest / size);
|
|
339
|
+
rest -= amount * size;
|
|
340
|
+
return `${amount}${unit}`;
|
|
341
|
+
});
|
|
342
|
+
const first = amounts.findIndex((amount) => !amount.startsWith("0"));
|
|
343
|
+
if (first === -1)
|
|
344
|
+
return "0s";
|
|
345
|
+
const next = amounts[first + 1];
|
|
346
|
+
return next === undefined || next.startsWith("0") ? amounts[first] ?? "0s" : `${amounts[first]} ${next}`;
|
|
347
|
+
}
|
|
348
|
+
function formatDuration(ms) {
|
|
349
|
+
return ms < 60000 ? `${(Math.max(0, ms) / 1000).toFixed(1)}s` : formatSeconds(ms / 1000);
|
|
350
|
+
}
|
|
351
|
+
function upTo(text, separator) {
|
|
352
|
+
const end = text.indexOf(separator);
|
|
353
|
+
return end === -1 ? text : text.slice(0, end);
|
|
354
|
+
}
|
|
355
|
+
function formatAge(date, now = Date.now()) {
|
|
356
|
+
const at = typeof date === "number" ? date : Date.parse(date);
|
|
357
|
+
if (Number.isNaN(at))
|
|
358
|
+
return "-";
|
|
359
|
+
const seconds = (now - at) / 1000;
|
|
360
|
+
return seconds < 60 ? "just now" : `${upTo(formatSeconds(seconds), " ")} ago`;
|
|
361
|
+
}
|
|
362
|
+
function truncate(text, width) {
|
|
363
|
+
return text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
364
|
+
}
|
|
365
|
+
var LINE_BREAKS = /\s*\n\s*/g;
|
|
366
|
+
var ESCAPE = String.fromCharCode(27);
|
|
367
|
+
function textWidth(text) {
|
|
368
|
+
return Bun.stringWidth(text);
|
|
369
|
+
}
|
|
370
|
+
function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
|
|
371
|
+
const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
|
|
372
|
+
const widths = [];
|
|
373
|
+
for (const row of cells) {
|
|
374
|
+
for (const [index, cell] of row.entries())
|
|
375
|
+
widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
|
|
376
|
+
}
|
|
377
|
+
const shown = fit === undefined ? cells : narrow(cells, widths, fit);
|
|
378
|
+
const colors = paint(process3.stdout);
|
|
379
|
+
const lines = shown.map((row) => row.map((cell, index) => {
|
|
380
|
+
const width = widths[index] ?? 0;
|
|
381
|
+
const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
|
|
382
|
+
if (align[index] === "right")
|
|
383
|
+
return `${gap}${cell}`;
|
|
384
|
+
return index === row.length - 1 ? cell : `${cell}${gap}`;
|
|
385
|
+
}).join(" ").trimEnd());
|
|
386
|
+
return lines.map((line, index) => header && index === 0 ? colors.bold(line) : line).join(`
|
|
387
|
+
`);
|
|
388
|
+
}
|
|
389
|
+
function narrow(rows, widths, limit) {
|
|
390
|
+
const used = () => widths.reduce((sum, width) => sum + width, 0) + 2 * Math.max(0, widths.length - 1);
|
|
391
|
+
const cuttable = (index) => rows.every((row) => !(row[index] ?? "").includes(ESCAPE));
|
|
392
|
+
const floor = 8;
|
|
393
|
+
while (used() > limit) {
|
|
394
|
+
let widest = -1;
|
|
395
|
+
for (const [index, width] of widths.entries()) {
|
|
396
|
+
if (cuttable(index) && width > floor && width > (widths[widest] ?? 0))
|
|
397
|
+
widest = index;
|
|
398
|
+
}
|
|
399
|
+
if (widest === -1)
|
|
400
|
+
break;
|
|
401
|
+
widths[widest] = Math.max(floor, (widths[widest] ?? 0) - (used() - limit));
|
|
402
|
+
}
|
|
403
|
+
return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
|
|
404
|
+
}
|
|
405
|
+
function printJson(value) {
|
|
406
|
+
console.log(JSON.stringify(value, undefined, 2));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ../kit/src/help.ts
|
|
410
|
+
import process4 from "process";
|
|
411
|
+
var OPTIONAL = /\[[^\]]+\]/g;
|
|
412
|
+
var USAGE_PREFIX = /^usage:\s*/i;
|
|
413
|
+
var FULL_STOP = /\.$/;
|
|
414
|
+
function usageLine(usage, colors = paint(process4.stdout)) {
|
|
415
|
+
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
416
|
+
}
|
|
417
|
+
function helpText(usage, options, colors = paint(process4.stdout), { examples = [], replaces, exits = [] } = {}) {
|
|
418
|
+
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
|
|
419
|
+
`);
|
|
420
|
+
}
|
|
421
|
+
function exitRows(exits, colors = paint(process4.stdout)) {
|
|
422
|
+
if (exits.length === 0)
|
|
423
|
+
return [];
|
|
424
|
+
const width = Math.max(...exits.map(([code]) => String(code).length));
|
|
425
|
+
return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
|
|
426
|
+
}
|
|
427
|
+
function replacesRows(replaces, colors = paint(process4.stdout)) {
|
|
428
|
+
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
429
|
+
}
|
|
430
|
+
function wantsHelpJson(env = process4.env) {
|
|
431
|
+
return env.KIT_HELP === "json";
|
|
432
|
+
}
|
|
433
|
+
function description(usage, { examples = [], replaces, exits } = {}, options) {
|
|
434
|
+
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
435
|
+
`);
|
|
436
|
+
const first = lines[0];
|
|
437
|
+
const colon = first.indexOf(": ");
|
|
438
|
+
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
439
|
+
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
440
|
+
return {
|
|
441
|
+
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
442
|
+
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
443
|
+
...replaces === undefined ? {} : { replaces },
|
|
444
|
+
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] }),
|
|
445
|
+
...exits === undefined ? {} : { exits: exits.map(([code, meaning]) => ({ code, meaning })) },
|
|
446
|
+
...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function exampleRows(examples, colors = paint(process4.stdout)) {
|
|
450
|
+
if (examples.length === 0)
|
|
451
|
+
return [];
|
|
452
|
+
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
453
|
+
}
|
|
454
|
+
function optionRows(options, colors = paint(process4.stdout)) {
|
|
455
|
+
const rows = Object.entries(options).map(([name, option]) => {
|
|
456
|
+
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
457
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
458
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
459
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
460
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
461
|
+
});
|
|
462
|
+
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
463
|
+
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// ../kit/src/text.ts
|
|
467
|
+
var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
|
|
468
|
+
function distance(a, b) {
|
|
469
|
+
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
470
|
+
for (const [i, left] of [...a].entries()) {
|
|
471
|
+
const current = [i + 1];
|
|
472
|
+
for (const [j, right] of [...b].entries())
|
|
473
|
+
current.push(Math.min((previous[j + 1] ?? 0) + 1, (current[j] ?? 0) + 1, (previous[j] ?? 0) + (left === right ? 0 : 1)));
|
|
474
|
+
previous = current;
|
|
475
|
+
}
|
|
476
|
+
return previous[b.length] ?? 0;
|
|
477
|
+
}
|
|
478
|
+
function closest(name, names) {
|
|
479
|
+
const started = names.find((known) => known.startsWith(name));
|
|
480
|
+
if (started !== undefined)
|
|
481
|
+
return started;
|
|
482
|
+
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
483
|
+
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
484
|
+
}
|
|
485
|
+
// ../kit/src/input.ts
|
|
486
|
+
var DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
|
|
487
|
+
var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
|
|
488
|
+
function duration(value, name, fallback, unit = "s") {
|
|
489
|
+
if (value === undefined)
|
|
490
|
+
return fallback;
|
|
491
|
+
const match = DURATION.exec(value.trim());
|
|
492
|
+
const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
|
|
493
|
+
if (ms < 1)
|
|
494
|
+
badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
|
|
495
|
+
return ms;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// ../kit/src/args.ts
|
|
499
|
+
var DASHES = /^-+/;
|
|
500
|
+
function jsonOption(holds) {
|
|
501
|
+
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
class Exit extends Error {
|
|
505
|
+
code;
|
|
506
|
+
hint;
|
|
507
|
+
constructor(message, code, hint) {
|
|
508
|
+
super(message);
|
|
509
|
+
this.code = code;
|
|
510
|
+
this.hint = hint;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function fail(message, code = 2, hint) {
|
|
514
|
+
throw new Exit(message, code, hint);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
class UsageError extends Exit {
|
|
518
|
+
constructor(message, hint) {
|
|
519
|
+
super(message, 2, hint);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function badUsage(message) {
|
|
523
|
+
throw new UsageError(message);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
class HelpShown extends Exit {
|
|
527
|
+
constructor() {
|
|
528
|
+
super("help shown", 0);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
class Completing {
|
|
533
|
+
options;
|
|
534
|
+
positionals;
|
|
535
|
+
candidates;
|
|
536
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
537
|
+
this.options = options;
|
|
538
|
+
this.positionals = positionals;
|
|
539
|
+
this.candidates = candidates;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
var completion = { active: false };
|
|
543
|
+
var OUTPUT_OPTIONS = {
|
|
544
|
+
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
545
|
+
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
546
|
+
};
|
|
547
|
+
function applyOutput(color, plain, env = process5.env) {
|
|
548
|
+
const when = plain === true ? "never" : color ?? "auto";
|
|
549
|
+
if (when === "auto")
|
|
550
|
+
return;
|
|
551
|
+
if (when !== "always" && when !== "never")
|
|
552
|
+
badUsage(`--color takes auto, always or never, not ${when}`);
|
|
553
|
+
if (when === "always") {
|
|
554
|
+
env.FORCE_COLOR = "1";
|
|
555
|
+
delete env.NO_COLOR;
|
|
556
|
+
} else {
|
|
557
|
+
env.NO_COLOR = "1";
|
|
558
|
+
delete env.FORCE_COLOR;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function parse(args, usage, options, settings = {}) {
|
|
562
|
+
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
563
|
+
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
564
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
565
|
+
if (completion.active)
|
|
566
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
567
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
568
|
+
let parsed;
|
|
569
|
+
try {
|
|
570
|
+
parsed = parseArgs({ args: joinDashValues(withBare(withAliases(own, all), all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
571
|
+
} catch (error) {
|
|
572
|
+
const message = parseError(error);
|
|
573
|
+
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
574
|
+
}
|
|
575
|
+
const values = parsed.values;
|
|
576
|
+
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
577
|
+
if (values.help === true) {
|
|
578
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings, listing)) : helpText(usage, listing, undefined, settings));
|
|
579
|
+
throw new HelpShown;
|
|
580
|
+
}
|
|
581
|
+
checkChoices(parsed.values, all);
|
|
582
|
+
checkConflicts(parsed.values, all);
|
|
583
|
+
if (settings.positionals !== undefined)
|
|
584
|
+
checkPositionals(parsed.positionals, settings.positionals);
|
|
585
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
586
|
+
}
|
|
587
|
+
function splitRest(args, passes) {
|
|
588
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
589
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
590
|
+
}
|
|
591
|
+
function splitTrailing(args, options, count) {
|
|
592
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
593
|
+
let seen = 0;
|
|
594
|
+
if (count === 0)
|
|
595
|
+
return { own: args, rest: undefined };
|
|
596
|
+
for (let at = 0;at < args.length; at++) {
|
|
597
|
+
const word = args[at];
|
|
598
|
+
if (word === "--")
|
|
599
|
+
break;
|
|
600
|
+
if (word.startsWith("-")) {
|
|
601
|
+
if (takesValue(word))
|
|
602
|
+
at++;
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
seen++;
|
|
606
|
+
if (seen === count)
|
|
607
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
608
|
+
}
|
|
609
|
+
return { own: args, rest: undefined };
|
|
610
|
+
}
|
|
611
|
+
function listed(options, allowNegative) {
|
|
612
|
+
if (!allowNegative)
|
|
613
|
+
return options;
|
|
614
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
615
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
616
|
+
return [[name, option]];
|
|
617
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
618
|
+
}));
|
|
619
|
+
}
|
|
620
|
+
function withAliases(args, options) {
|
|
621
|
+
const named = new Map(Object.entries(options).flatMap(([name, { aliases = [] }]) => aliases.map((alias) => [`--${alias}`, `--${name}`])));
|
|
622
|
+
if (named.size === 0)
|
|
623
|
+
return args;
|
|
624
|
+
const separator = args.indexOf("--");
|
|
625
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
626
|
+
return [...own.map((arg) => {
|
|
627
|
+
const at = arg.indexOf("=");
|
|
628
|
+
const current = named.get(at === -1 ? arg : arg.slice(0, at));
|
|
629
|
+
return current === undefined ? arg : `${current}${at === -1 ? "" : arg.slice(at)}`;
|
|
630
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
631
|
+
}
|
|
632
|
+
function withBare(args, options) {
|
|
633
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
634
|
+
if (bare.size === 0)
|
|
635
|
+
return args;
|
|
636
|
+
const separator = args.indexOf("--");
|
|
637
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
638
|
+
return [...own.flatMap((arg, index) => {
|
|
639
|
+
const optional = bare.get(arg);
|
|
640
|
+
const next = own[index + 1];
|
|
641
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
642
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
643
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
644
|
+
}
|
|
645
|
+
function checkConflicts(values, options) {
|
|
646
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
647
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
648
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
649
|
+
if (other !== undefined)
|
|
650
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function checkChoices(values, options) {
|
|
654
|
+
for (const [name, option] of Object.entries(options)) {
|
|
655
|
+
if (option.choices === undefined)
|
|
656
|
+
continue;
|
|
657
|
+
const given = values[name];
|
|
658
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
659
|
+
if (typeof value === "string")
|
|
660
|
+
oneOf(value, name, option.choices);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function oneOf(value, name, choices) {
|
|
665
|
+
if (!choices.includes(value))
|
|
666
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
667
|
+
return value;
|
|
668
|
+
}
|
|
669
|
+
function checkPositionals(given, names) {
|
|
670
|
+
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
671
|
+
const required = names.filter((name) => !name.startsWith("[")).length;
|
|
672
|
+
if (given.length < required)
|
|
673
|
+
badUsage(`missing ${names[given.length]}`);
|
|
674
|
+
if (!variadic && given.length > names.length)
|
|
675
|
+
badUsage(`unexpected argument ${given[names.length]}`);
|
|
676
|
+
}
|
|
677
|
+
function joinDashValues(args, options) {
|
|
678
|
+
const longOf = new Map(Object.entries(options).flatMap(([name, option]) => option.short === undefined ? [[`--${name}`, name]] : [[`--${name}`, name], [`-${option.short}`, name]]));
|
|
679
|
+
const isOption = (arg) => arg === "--" || longOf.has(arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg);
|
|
680
|
+
const joined = [];
|
|
681
|
+
for (let i = 0;i < args.length; i++) {
|
|
682
|
+
const arg = args[i] ?? "";
|
|
683
|
+
if (arg === "--")
|
|
684
|
+
return [...joined, ...args.slice(i)];
|
|
685
|
+
const name = longOf.get(arg);
|
|
686
|
+
const next = args[i + 1];
|
|
687
|
+
if (name !== undefined && options[name]?.type === "string" && next !== undefined && next.startsWith("-") && next !== "-" && !isOption(next)) {
|
|
688
|
+
joined.push(`--${name}=${next}`);
|
|
689
|
+
i++;
|
|
690
|
+
} else {
|
|
691
|
+
joined.push(arg);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return joined;
|
|
695
|
+
}
|
|
696
|
+
function parseError(error) {
|
|
697
|
+
const message = upTo(error instanceof Error ? error.message : String(error), ". ");
|
|
698
|
+
return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
|
|
699
|
+
}
|
|
700
|
+
var UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
|
|
701
|
+
function nearestOption(message, options, allowNegative) {
|
|
702
|
+
const typed = UNKNOWN_OPTION.exec(message)?.[1];
|
|
703
|
+
if (typed === undefined)
|
|
704
|
+
return;
|
|
705
|
+
const word = typed.replace(DASHES, "");
|
|
706
|
+
if (word.length < 2 && !typed.startsWith("--"))
|
|
707
|
+
return;
|
|
708
|
+
const known = Object.entries(options).flatMap(([name, option]) => [
|
|
709
|
+
...option.short === undefined ? [] : [`-${option.short}`],
|
|
710
|
+
`--${name}`,
|
|
711
|
+
...allowNegative && option.type === "boolean" ? [`--no-${name}`] : []
|
|
712
|
+
]);
|
|
713
|
+
const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
|
|
714
|
+
return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
|
|
715
|
+
}
|
|
716
|
+
// ../kit/src/process.ts
|
|
717
|
+
import { constants } from "os";
|
|
718
|
+
import process6 from "process";
|
|
719
|
+
var KILL_GRACE_MS = 2000;
|
|
720
|
+
function exitCodeOf(code, signal) {
|
|
721
|
+
if (code !== null)
|
|
722
|
+
return code;
|
|
723
|
+
if (typeof signal === "number")
|
|
724
|
+
return 128 + signal;
|
|
725
|
+
return signal === null ? 129 : 128 + constants.signals[signal];
|
|
726
|
+
}
|
|
727
|
+
async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
|
|
728
|
+
const chunks = [];
|
|
729
|
+
const grouped = timeoutMs !== undefined || signal !== undefined || onStart !== undefined || onChunk !== undefined;
|
|
730
|
+
const proc = Bun.spawn(argv, { ...cwd === undefined ? {} : { cwd }, ...env === undefined ? {} : { env }, stdin, stdout: "pipe", stderr: "pipe", ...grouped ? { detached: true } : {} });
|
|
731
|
+
const state = { kept: 0, truncated: false, timedOut: false, failure: undefined, done: false };
|
|
732
|
+
const timers = [];
|
|
733
|
+
const killGroup = (killing) => {
|
|
734
|
+
if (state.done)
|
|
735
|
+
return;
|
|
736
|
+
try {
|
|
737
|
+
if (grouped)
|
|
738
|
+
process6.kill(-proc.pid, killing);
|
|
739
|
+
else
|
|
740
|
+
proc.kill(killing);
|
|
741
|
+
} catch {}
|
|
742
|
+
};
|
|
743
|
+
const stop = () => {
|
|
744
|
+
killGroup("SIGTERM");
|
|
745
|
+
timers.push(setTimeout(killGroup, KILL_GRACE_MS, "SIGKILL"));
|
|
746
|
+
};
|
|
747
|
+
const onAbort = () => stop();
|
|
748
|
+
if (timeoutMs !== undefined) {
|
|
749
|
+
timers.push(setTimeout(() => {
|
|
750
|
+
state.timedOut = true;
|
|
751
|
+
stop();
|
|
752
|
+
}, timeoutMs));
|
|
753
|
+
}
|
|
754
|
+
if (signal?.aborted === true)
|
|
755
|
+
onAbort();
|
|
756
|
+
else
|
|
757
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
758
|
+
onStart?.({ kill: (killing) => killGroup(killing) });
|
|
759
|
+
const drain = async (stream, name) => {
|
|
760
|
+
for await (const data of stream) {
|
|
761
|
+
if (state.failure !== undefined)
|
|
762
|
+
continue;
|
|
763
|
+
try {
|
|
764
|
+
onChunk?.({ stream: name, data });
|
|
765
|
+
} catch (error) {
|
|
766
|
+
state.failure = error;
|
|
767
|
+
stop();
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (state.kept + data.length > maxBytes) {
|
|
771
|
+
state.truncated = true;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
state.kept += data.length;
|
|
775
|
+
chunks.push({ stream: name, data });
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
try {
|
|
779
|
+
await Promise.all([drain(proc.stdout, "stdout"), drain(proc.stderr, "stderr")]);
|
|
780
|
+
await proc.exited;
|
|
781
|
+
} finally {
|
|
782
|
+
state.done = true;
|
|
783
|
+
for (const timer of timers)
|
|
784
|
+
clearTimeout(timer);
|
|
785
|
+
signal?.removeEventListener("abort", onAbort);
|
|
786
|
+
}
|
|
787
|
+
if (state.failure !== undefined)
|
|
788
|
+
throw state.failure;
|
|
789
|
+
const joined = () => Buffer.concat(chunks.map(({ data }) => data));
|
|
790
|
+
let output;
|
|
791
|
+
let text;
|
|
792
|
+
return {
|
|
793
|
+
code: exitCodeOf(proc.exitCode, proc.signalCode),
|
|
794
|
+
chunks,
|
|
795
|
+
timedOut: state.timedOut,
|
|
796
|
+
truncated: state.truncated,
|
|
797
|
+
get output() {
|
|
798
|
+
return output ??= joined();
|
|
799
|
+
},
|
|
800
|
+
get text() {
|
|
801
|
+
return text ??= (output ??= joined()).toString();
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// ../kit/src/progress.ts
|
|
807
|
+
var ESC = String.fromCharCode(27);
|
|
808
|
+
var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
|
|
809
|
+
var RESET_STYLE = `${ESC}[0m`;
|
|
810
|
+
var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
811
|
+
|
|
812
|
+
// ../kit/src/tool.ts
|
|
813
|
+
import process7 from "process";
|
|
814
|
+
function cwdGone() {
|
|
815
|
+
try {
|
|
816
|
+
process7.cwd();
|
|
817
|
+
return false;
|
|
818
|
+
} catch {
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
class SpawnError extends Error {
|
|
824
|
+
missing;
|
|
825
|
+
constructor(message, missing) {
|
|
826
|
+
super(message);
|
|
827
|
+
this.missing = missing;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function spawnFailure(command, error) {
|
|
831
|
+
const gone = cwdGone();
|
|
832
|
+
const missing = !gone && error?.code === "ENOENT";
|
|
833
|
+
return new SpawnError(`${command}: ${error instanceof Error ? error.message : String(error)}${gone ? " (the working directory was removed)" : ""}`, missing);
|
|
834
|
+
}
|
|
835
|
+
async function runTool(label, argv, { timeoutMs, cwd, onStart } = {}) {
|
|
836
|
+
let ran;
|
|
837
|
+
try {
|
|
838
|
+
ran = await runCommand(argv, { env: process7.env, ...timeoutMs === undefined ? {} : { timeoutMs }, ...cwd === undefined ? {} : { cwd }, ...onStart === undefined ? {} : { onStart } });
|
|
839
|
+
} catch (error) {
|
|
840
|
+
throw spawnFailure(label, error);
|
|
841
|
+
}
|
|
842
|
+
if (ran.timedOut)
|
|
843
|
+
throw new Error(`${label}: no answer in ${formatDuration(timeoutMs ?? 0)}`);
|
|
844
|
+
const decoders = { stdout: new TextDecoder, stderr: new TextDecoder };
|
|
845
|
+
const streams = { stdout: "", stderr: "" };
|
|
846
|
+
for (const { stream, data } of ran.chunks)
|
|
847
|
+
streams[stream] += decoders[stream].decode(data, { stream: true });
|
|
848
|
+
streams.stdout += decoders.stdout.decode();
|
|
849
|
+
streams.stderr += decoders.stderr.decode();
|
|
850
|
+
return { code: ran.code, ...streams };
|
|
851
|
+
}
|
|
852
|
+
// ../kit/src/cli.ts
|
|
853
|
+
import { readFileSync, realpathSync } from "fs";
|
|
854
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
855
|
+
import process9 from "process";
|
|
856
|
+
|
|
857
|
+
// ../kit/src/usage.ts
|
|
858
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
859
|
+
import { homedir } from "os";
|
|
860
|
+
import { dirname, isAbsolute, join } from "path";
|
|
861
|
+
import process8 from "process";
|
|
862
|
+
|
|
863
|
+
// ../kit/src/logs.ts
|
|
864
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
865
|
+
var CLI_FOLDER = "kaynooo-cli";
|
|
866
|
+
|
|
867
|
+
// ../kit/src/usage.ts
|
|
868
|
+
var UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
|
|
869
|
+
var FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
|
|
870
|
+
var LONGEST_NAME = 40;
|
|
871
|
+
var usageError;
|
|
872
|
+
function noteUsageError(message) {
|
|
873
|
+
usageError = message;
|
|
874
|
+
}
|
|
875
|
+
function stateHome(env = process8.env) {
|
|
876
|
+
const dir = env.XDG_STATE_HOME;
|
|
877
|
+
return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
|
|
878
|
+
}
|
|
879
|
+
function usagePath(env = process8.env) {
|
|
880
|
+
return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
|
|
881
|
+
}
|
|
882
|
+
function usageRecorded(env = process8.env) {
|
|
883
|
+
const off = env.KAYNOOO_CLI_NO_USAGE;
|
|
884
|
+
return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
|
|
885
|
+
}
|
|
886
|
+
function flagNames(argv) {
|
|
887
|
+
const end = argv.indexOf("--");
|
|
888
|
+
const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
|
|
889
|
+
return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
|
|
890
|
+
}
|
|
891
|
+
function usageEntry(bin, command, argv, code, now = new Date) {
|
|
892
|
+
const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
|
|
893
|
+
usageError = undefined;
|
|
894
|
+
return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
|
|
895
|
+
}
|
|
896
|
+
function recordUsage(entry, env = process8.env) {
|
|
897
|
+
if (!usageRecorded(env))
|
|
898
|
+
return;
|
|
899
|
+
try {
|
|
900
|
+
const path = usagePath(env);
|
|
901
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
902
|
+
appendFileSync(path, `${JSON.stringify(entry)}
|
|
903
|
+
`, { mode: 384 });
|
|
904
|
+
} catch {}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// ../kit/src/cli.ts
|
|
908
|
+
var LEVEL_OPTIONS = {
|
|
909
|
+
...OUTPUT_OPTIONS,
|
|
910
|
+
help: { type: "boolean", short: "h", description: "show this help" }
|
|
911
|
+
};
|
|
912
|
+
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
913
|
+
async function commandRows(all, colors) {
|
|
914
|
+
const commands = shown(all);
|
|
915
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
916
|
+
const groups = new Map([["Commands:", []]]);
|
|
917
|
+
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
918
|
+
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
919
|
+
const heading = group === undefined ? "Commands:" : `${group}:`;
|
|
920
|
+
const rows = groups.get(heading) ?? [];
|
|
921
|
+
rows.push(` ${colors.cyan(name.padEnd(width))} ${colors.dim(summaries[index])}`);
|
|
922
|
+
groups.set(heading, rows);
|
|
923
|
+
}
|
|
924
|
+
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
925
|
+
}
|
|
926
|
+
function shown(commands) {
|
|
927
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
928
|
+
}
|
|
929
|
+
function takeOutputFlags(argv) {
|
|
930
|
+
let index = 0;
|
|
931
|
+
for (;index < argv.length; index++) {
|
|
932
|
+
const word = argv[index];
|
|
933
|
+
if (word === "--plain")
|
|
934
|
+
applyOutput(undefined, true);
|
|
935
|
+
else if (word.startsWith("--color="))
|
|
936
|
+
applyOutput(word.slice("--color=".length), undefined);
|
|
937
|
+
else if (word === "--color" && argv[index + 1] !== undefined)
|
|
938
|
+
applyOutput(argv[++index], undefined);
|
|
939
|
+
else
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
return argv.slice(index);
|
|
943
|
+
}
|
|
944
|
+
async function resolveSummary(summary) {
|
|
945
|
+
return typeof summary === "string" ? summary : summary();
|
|
946
|
+
}
|
|
947
|
+
function scopeOf(commands, extra, aliases) {
|
|
948
|
+
return { commands: typeof commands === "function" ? {} : commands, lazy: [...typeof commands === "function" ? [commands] : [], ...extra === undefined ? [] : [extra]], aliases };
|
|
949
|
+
}
|
|
950
|
+
async function expand(scope, words) {
|
|
951
|
+
if (scope.lazy.length === 0)
|
|
952
|
+
return scope;
|
|
953
|
+
const commands = { ...scope.commands };
|
|
954
|
+
for (const added of await Promise.all(scope.lazy.map(async (lazy) => lazy(words)))) {
|
|
955
|
+
for (const [name, command] of Object.entries(added))
|
|
956
|
+
commands[name] ??= command;
|
|
957
|
+
}
|
|
958
|
+
return { ...scope, commands, lazy: [] };
|
|
959
|
+
}
|
|
960
|
+
async function help2({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
|
|
961
|
+
const colors = paint(stream);
|
|
962
|
+
const prefix = path.join(" ");
|
|
963
|
+
const summary = await resolveSummary(lazySummary);
|
|
964
|
+
if (wantsHelpJson())
|
|
965
|
+
return JSON.stringify(description(`${prefix} <command> [options]: ${summary}`, { examples, replaces }));
|
|
966
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
967
|
+
const options = fallback !== undefined && "run" in fallback ? await optionsOf(fallback.run) : undefined;
|
|
968
|
+
const own = Object.entries(options ?? {}).filter(([name]) => !(name in TOP_OPTIONS));
|
|
969
|
+
return [
|
|
970
|
+
header(colors, summary),
|
|
971
|
+
"",
|
|
972
|
+
usageLine(`${prefix} ${defaultCommand === undefined ? colors.cyan("<command>") : `[${colors.cyan("<command>")}]`} [options]`, colors),
|
|
973
|
+
...replacesRows(replaces, colors),
|
|
974
|
+
"",
|
|
975
|
+
...await commandRows(commands, colors),
|
|
976
|
+
...own.length === 0 ? [] : ["", `${colors.bold(`Options of ${defaultCommand}:`)} ${colors.dim("the command run when none is named")}`, ...optionRows(Object.fromEntries(own), colors)],
|
|
977
|
+
"",
|
|
978
|
+
colors.bold("Options:"),
|
|
979
|
+
...optionRows(path.length === 1 ? TOP_OPTIONS : LEVEL_OPTIONS, colors),
|
|
980
|
+
...exampleRows(examples ?? [], colors),
|
|
981
|
+
"",
|
|
982
|
+
`${colors.dim("Run")} ${prefix} ${colors.cyan("<command>")} --help ${colors.dim("for its options.")}`
|
|
983
|
+
].join(`
|
|
984
|
+
`);
|
|
985
|
+
}
|
|
986
|
+
function printError(prefix, message, hint, cause) {
|
|
987
|
+
const colors = paint(process9.stderr);
|
|
988
|
+
const styled = takesControlCodes(process9.stderr);
|
|
989
|
+
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
990
|
+
const under = (label, text, tint) => text === undefined ? "" : `
|
|
991
|
+
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
992
|
+
console.error(`${head} ${message}${under("cause", cause, colors.dim)}${under("hint", hint, colors.yellow)}`);
|
|
993
|
+
}
|
|
994
|
+
function printJsonError(message, hint, cause) {
|
|
995
|
+
console.error(JSON.stringify({ error: message, ...hint === undefined ? {} : { hint }, ...cause === undefined ? {} : { cause } }));
|
|
996
|
+
}
|
|
997
|
+
function wantsJson(argv) {
|
|
998
|
+
const end = argv.indexOf("--");
|
|
999
|
+
return (end === -1 ? argv : argv.slice(0, end)).includes("--json");
|
|
1000
|
+
}
|
|
1001
|
+
function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
1002
|
+
if (error instanceof HelpShown)
|
|
1003
|
+
return 0;
|
|
1004
|
+
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
1005
|
+
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
1006
|
+
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
1007
|
+
if (error instanceof Exit && error.code === 2)
|
|
1008
|
+
noteUsageError(error.message);
|
|
1009
|
+
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
1010
|
+
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
1011
|
+
return error instanceof Exit ? error.code : 1;
|
|
1012
|
+
}
|
|
1013
|
+
async function dispatch(level, typed, { version, redact }) {
|
|
1014
|
+
const prefix = level.path.join(" ");
|
|
1015
|
+
let argv;
|
|
1016
|
+
try {
|
|
1017
|
+
argv = takeOutputFlags(typed);
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
return reportThrown(prefix, error);
|
|
1020
|
+
}
|
|
1021
|
+
const [name, ...args] = argv;
|
|
1022
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
1023
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
1024
|
+
try {
|
|
1025
|
+
return await fallback.run([]);
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (name === undefined && level.path.length > 1) {
|
|
1031
|
+
console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process9.stderr));
|
|
1032
|
+
return 2;
|
|
1033
|
+
}
|
|
1034
|
+
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
1035
|
+
console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process9.stdout));
|
|
1036
|
+
return 0;
|
|
1037
|
+
}
|
|
1038
|
+
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
1039
|
+
console.log(version);
|
|
1040
|
+
return 0;
|
|
1041
|
+
}
|
|
1042
|
+
let command = level.commands[level.aliases?.[name] ?? name];
|
|
1043
|
+
let rest = args;
|
|
1044
|
+
let ran = name;
|
|
1045
|
+
if (command === undefined && level.lazy.length > 0) {
|
|
1046
|
+
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
1047
|
+
command = level.commands[level.aliases?.[name] ?? name];
|
|
1048
|
+
}
|
|
1049
|
+
if (command === undefined && level.defaultCommand !== undefined) {
|
|
1050
|
+
command = level.commands[level.defaultCommand];
|
|
1051
|
+
rest = argv;
|
|
1052
|
+
ran = level.defaultCommand;
|
|
1053
|
+
}
|
|
1054
|
+
if (command === undefined) {
|
|
1055
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
1056
|
+
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
1057
|
+
return 2;
|
|
1058
|
+
}
|
|
1059
|
+
if ("commands" in command) {
|
|
1060
|
+
const { summary } = command;
|
|
1061
|
+
return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, runBare: false, examples: command.examples, replaces: undefined }, rest, { version, redact });
|
|
1062
|
+
}
|
|
1063
|
+
try {
|
|
1064
|
+
return await command.run(rest);
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
var COMPLETE = "__complete";
|
|
1070
|
+
var FILES = "__files__";
|
|
1071
|
+
async function completingOf(run) {
|
|
1072
|
+
completion.active = true;
|
|
1073
|
+
try {
|
|
1074
|
+
await run([]);
|
|
1075
|
+
} catch (error) {
|
|
1076
|
+
if (error instanceof Completing)
|
|
1077
|
+
return error;
|
|
1078
|
+
} finally {
|
|
1079
|
+
completion.active = false;
|
|
1080
|
+
}
|
|
1081
|
+
return new Completing({});
|
|
1082
|
+
}
|
|
1083
|
+
async function optionsOf(run) {
|
|
1084
|
+
return (await completingOf(run)).options;
|
|
1085
|
+
}
|
|
1086
|
+
function candidateLine(candidate) {
|
|
1087
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
1088
|
+
}
|
|
1089
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
1090
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
1091
|
+
let index = 0;
|
|
1092
|
+
let at = 0;
|
|
1093
|
+
while (at < before.length) {
|
|
1094
|
+
const word = before[at];
|
|
1095
|
+
if (word === "--") {
|
|
1096
|
+
index += before.length - at - 1;
|
|
1097
|
+
break;
|
|
1098
|
+
}
|
|
1099
|
+
if (!word.startsWith("-"))
|
|
1100
|
+
index++;
|
|
1101
|
+
else if (takesValue(word))
|
|
1102
|
+
at++;
|
|
1103
|
+
at++;
|
|
1104
|
+
}
|
|
1105
|
+
const last = positionals.at(-1);
|
|
1106
|
+
if (index < positionals.length)
|
|
1107
|
+
return positionals[index];
|
|
1108
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
1109
|
+
}
|
|
1110
|
+
async function leafCandidates(run, words) {
|
|
1111
|
+
const completing = await completingOf(run);
|
|
1112
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
1113
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
1114
|
+
const flag = words.at(-2);
|
|
1115
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
1116
|
+
if (option?.type === "string") {
|
|
1117
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
1118
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
1119
|
+
}
|
|
1120
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
1121
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
1122
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
1123
|
+
}
|
|
1124
|
+
async function candidates(scope, defaultCommand, words, before = []) {
|
|
1125
|
+
const [word = "", ...rest] = words;
|
|
1126
|
+
const listing = rest.length === 0 && !word.startsWith("-");
|
|
1127
|
+
let { commands, aliases } = scope;
|
|
1128
|
+
let command = commands[aliases?.[word] ?? word];
|
|
1129
|
+
if (listing || command === undefined) {
|
|
1130
|
+
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
1131
|
+
command = commands[aliases?.[word] ?? word];
|
|
1132
|
+
}
|
|
1133
|
+
if (listing) {
|
|
1134
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
1135
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1136
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
1137
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
1138
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
1139
|
+
}
|
|
1140
|
+
if (command === undefined) {
|
|
1141
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1142
|
+
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
1143
|
+
}
|
|
1144
|
+
return "commands" in command ? candidates(scopeOf(command.commands, command.extraCommands, command.aliases), undefined, rest, [...before, word]) : leafCandidates(command.run, rest);
|
|
1145
|
+
}
|
|
1146
|
+
async function printCandidates(found) {
|
|
1147
|
+
for (const line of await found)
|
|
1148
|
+
console.log(line);
|
|
1149
|
+
return 0;
|
|
1150
|
+
}
|
|
1151
|
+
function packageVersion(entry = Bun.main) {
|
|
1152
|
+
for (let dir = dirname2(realpathSync(entry));; dir = dirname2(dir)) {
|
|
1153
|
+
try {
|
|
1154
|
+
const { version } = JSON.parse(readFileSync(join2(dir, "package.json"), "utf8"));
|
|
1155
|
+
if (typeof version === "string")
|
|
1156
|
+
return version;
|
|
1157
|
+
} catch {}
|
|
1158
|
+
if (dirname2(dir) === dir)
|
|
1159
|
+
return "0.0.0";
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
async function runCli(cli, argv = process9.argv.slice(2)) {
|
|
1163
|
+
const version = cli.version ?? packageVersion();
|
|
1164
|
+
if (argv[0] === COMPLETE)
|
|
1165
|
+
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
1166
|
+
const code = await dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(version)} ${about}`, summary: cli.about, ...scopeOf(cli.commands, cli.extraCommands, cli.aliases), defaultCommand: cli.defaultCommand, runBare: cli.runBare === true, examples: cli.examples, replaces: cli.replaces }, argv, { version, redact: cli.redact });
|
|
1167
|
+
if (cli.usage !== false) {
|
|
1168
|
+
try {
|
|
1169
|
+
recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
|
|
1170
|
+
} catch {}
|
|
1171
|
+
}
|
|
1172
|
+
return code;
|
|
1173
|
+
}
|
|
1174
|
+
function commandPath(scope, argv) {
|
|
1175
|
+
const path = [];
|
|
1176
|
+
let level = scope;
|
|
1177
|
+
for (const word of argv.filter((each) => !each.startsWith("-"))) {
|
|
1178
|
+
const name = level.aliases !== undefined && Object.hasOwn(level.aliases, word) ? level.aliases[word] ?? word : word;
|
|
1179
|
+
const command = Object.hasOwn(level.commands, name) ? level.commands[name] : undefined;
|
|
1180
|
+
if (command === undefined)
|
|
1181
|
+
break;
|
|
1182
|
+
path.push(word);
|
|
1183
|
+
if (!("commands" in command))
|
|
1184
|
+
break;
|
|
1185
|
+
level = scopeOf(command.commands, command.extraCommands, command.aliases);
|
|
1186
|
+
}
|
|
1187
|
+
return path.length === 0 ? undefined : path.join(" ");
|
|
1188
|
+
}
|
|
1189
|
+
async function runMain({ name, version, run, redact, usage }, argv = process9.argv.slice(2)) {
|
|
1190
|
+
if (argv[0] === COMPLETE)
|
|
1191
|
+
return printCandidates(leafCandidates(run, argv.slice(1)));
|
|
1192
|
+
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
|
|
1193
|
+
console.log(version ?? packageVersion());
|
|
1194
|
+
return 0;
|
|
1195
|
+
}
|
|
1196
|
+
let code;
|
|
1197
|
+
try {
|
|
1198
|
+
code = await run(argv);
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
code = reportThrown(name, error, redact, wantsJson(argv));
|
|
1201
|
+
}
|
|
1202
|
+
if (usage !== false) {
|
|
1203
|
+
try {
|
|
1204
|
+
recordUsage(usageEntry(name, undefined, argv, code));
|
|
1205
|
+
} catch {}
|
|
1206
|
+
}
|
|
1207
|
+
return code;
|
|
1208
|
+
}
|
|
1209
|
+
// ../kit/src/secrets.ts
|
|
1210
|
+
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
1211
|
+
// ../kit/src/sessions.ts
|
|
1212
|
+
var {Glob } = globalThis.Bun;
|
|
1213
|
+
var SESSION_FILES = new Glob("*.json");
|
|
1214
|
+
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
1215
|
+
// ../kit/src/size.ts
|
|
1216
|
+
var SIZE = /^(\d+(?:\.\d+)?)\s*([kmgt]?)i?b?$/i;
|
|
1217
|
+
var POWERS = { "": 0, k: 1, m: 2, g: 3, t: 4 };
|
|
1218
|
+
function parseSize(text, bareUnit = 1) {
|
|
1219
|
+
const match = SIZE.exec(text.trim());
|
|
1220
|
+
if (match === null)
|
|
1221
|
+
return;
|
|
1222
|
+
const suffix = match[2].toLowerCase();
|
|
1223
|
+
return Math.round(Number(match[1]) * (suffix === "" ? bareUnit : 1024 ** POWERS[suffix]));
|
|
1224
|
+
}
|
|
1225
|
+
// ../kit/src/user-config.ts
|
|
1226
|
+
import { existsSync } from "fs";
|
|
1227
|
+
import { homedir as homedir2 } from "os";
|
|
1228
|
+
import { join as join3 } from "path";
|
|
1229
|
+
import process10 from "process";
|
|
1230
|
+
function configBase(where = {}) {
|
|
1231
|
+
const xdg = where.xdg ?? (where.home === undefined ? process10.env.XDG_CONFIG_HOME : undefined);
|
|
1232
|
+
return xdg !== undefined && xdg !== "" ? xdg : join3(where.home ?? homedir2(), ".config");
|
|
1233
|
+
}
|
|
1234
|
+
function userConfigPath(bin, where = {}) {
|
|
1235
|
+
return join3(configBase(where), "kaynooo-cli", `${bin}.json`);
|
|
1236
|
+
}
|
|
1237
|
+
function userConfigReadPath(bin, legacy = [], where = {}) {
|
|
1238
|
+
const current = userConfigPath(bin, where);
|
|
1239
|
+
if (existsSync(current))
|
|
1240
|
+
return current;
|
|
1241
|
+
const base = configBase(where);
|
|
1242
|
+
return legacy.map((parts) => join3(base, ...parts)).find((path) => existsSync(path)) ?? current;
|
|
1243
|
+
}
|
|
1244
|
+
// src/repos.ts
|
|
1245
|
+
import { existsSync as existsSync3, statSync } from "fs";
|
|
1246
|
+
import { homedir as homedir3 } from "os";
|
|
1247
|
+
import { join as join5, relative, resolve } from "path";
|
|
1248
|
+
import process13 from "process";
|
|
1249
|
+
|
|
1250
|
+
// src/repos-scan.ts
|
|
1251
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1252
|
+
import { readdir } from "fs/promises";
|
|
1253
|
+
import { basename, join as join4 } from "path";
|
|
1254
|
+
import process12 from "process";
|
|
1255
|
+
var MAX_DEPTH = 3;
|
|
1256
|
+
var CONCURRENCY = 8;
|
|
1257
|
+
var GIT_MS = 1e4;
|
|
1258
|
+
var FETCH_MS = 30000;
|
|
1259
|
+
var AHEAD_BEHIND = /\+(\d+) -(\d+)/;
|
|
1260
|
+
var PACKAGE_TAG = /^(.+)-v\d/;
|
|
1261
|
+
var REASONS = ["dirty", "unpushed", "unreleased", "behind"];
|
|
1262
|
+
function reasonsOf(repo) {
|
|
1263
|
+
const reasons = [];
|
|
1264
|
+
if (repo.dirty > 0)
|
|
1265
|
+
reasons.push("dirty");
|
|
1266
|
+
if ((repo.ahead ?? 0) > 0)
|
|
1267
|
+
reasons.push("unpushed");
|
|
1268
|
+
if ((repo.behind ?? 0) > 0)
|
|
1269
|
+
reasons.push("behind");
|
|
1270
|
+
if ((repo.unreleased ?? 0) > 0)
|
|
1271
|
+
reasons.push("unreleased");
|
|
1272
|
+
return reasons;
|
|
1273
|
+
}
|
|
1274
|
+
async function findRepos(root, skip) {
|
|
1275
|
+
if (existsSync2(join4(root, ".git")))
|
|
1276
|
+
return [root];
|
|
1277
|
+
const found = [];
|
|
1278
|
+
async function walk(dir, depth) {
|
|
1279
|
+
let entries;
|
|
1280
|
+
try {
|
|
1281
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
1282
|
+
} catch {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
1286
|
+
if (!entry.isDirectory() || entry.name === "node_modules" || entry.name.startsWith(".") || skip.has(entry.name))
|
|
1287
|
+
continue;
|
|
1288
|
+
const path = join4(dir, entry.name);
|
|
1289
|
+
if (existsSync2(join4(path, ".git")))
|
|
1290
|
+
found.push(path);
|
|
1291
|
+
else if (depth < MAX_DEPTH)
|
|
1292
|
+
await walk(path, depth + 1);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
await walk(root, 1);
|
|
1296
|
+
return found;
|
|
1297
|
+
}
|
|
1298
|
+
async function read(path, args, timeoutMs = GIT_MS) {
|
|
1299
|
+
try {
|
|
1300
|
+
const ran = await runCommand(["git", "--no-optional-locks", ...args], { cwd: path, env: { ...process12.env, GIT_TERMINAL_PROMPT: "0" }, timeoutMs });
|
|
1301
|
+
return ran.code === 0 && !ran.timedOut ? ran.text.trim() : undefined;
|
|
1302
|
+
} catch {
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
async function commitsSince(path, tag, folder) {
|
|
1307
|
+
const count = await read(path, ["rev-list", "--count", `${tag}..HEAD`, ...folder === undefined ? [] : ["--", folder]]);
|
|
1308
|
+
return Number(count ?? 0);
|
|
1309
|
+
}
|
|
1310
|
+
async function packageReleases(path) {
|
|
1311
|
+
const names = new Set;
|
|
1312
|
+
for (const tag of (await read(path, ["tag", "--list"]) ?? "").split(`
|
|
1313
|
+
`)) {
|
|
1314
|
+
const name = PACKAGE_TAG.exec(tag)?.[1];
|
|
1315
|
+
if (name !== undefined)
|
|
1316
|
+
names.add(name);
|
|
1317
|
+
}
|
|
1318
|
+
const releases = [];
|
|
1319
|
+
for (const name of [...names].toSorted()) {
|
|
1320
|
+
const folder = [join4("packages", name), name].find((candidate) => existsSync2(join4(path, candidate)));
|
|
1321
|
+
const tag = folder === undefined ? undefined : await read(path, ["describe", "--tags", "--abbrev=0", "--match", `${name}-v*`]);
|
|
1322
|
+
if (folder !== undefined && tag !== undefined && tag !== "")
|
|
1323
|
+
releases.push({ name, tag, unreleased: await commitsSince(path, tag, folder) });
|
|
1324
|
+
}
|
|
1325
|
+
return releases;
|
|
1326
|
+
}
|
|
1327
|
+
async function inspect(path, fetch) {
|
|
1328
|
+
const fetched = fetch ? await read(path, ["fetch", "--quiet"], FETCH_MS) : "";
|
|
1329
|
+
const [status, worktrees, packages] = await Promise.all([
|
|
1330
|
+
read(path, ["status", "--porcelain=v2", "--branch"]),
|
|
1331
|
+
read(path, ["worktree", "list", "--porcelain"]),
|
|
1332
|
+
packageReleases(path)
|
|
1333
|
+
]);
|
|
1334
|
+
const state = { name: basename(path), path, branch: "?", upstream: null, dirty: 0, ahead: null, behind: null, worktrees: 0, tag: null, unreleased: null, packages, fetchFailed: fetched === undefined, unreadable: status === undefined };
|
|
1335
|
+
for (const line of (status ?? "").split(`
|
|
1336
|
+
`)) {
|
|
1337
|
+
if (line.startsWith("# branch.head "))
|
|
1338
|
+
state.branch = line.slice("# branch.head ".length);
|
|
1339
|
+
else if (line.startsWith("# branch.upstream "))
|
|
1340
|
+
state.upstream = line.slice("# branch.upstream ".length);
|
|
1341
|
+
else if (line.startsWith("# branch.ab "))
|
|
1342
|
+
[state.ahead, state.behind] = AHEAD_BEHIND.exec(line)?.slice(1).map(Number);
|
|
1343
|
+
else if (line !== "" && !line.startsWith("#"))
|
|
1344
|
+
state.dirty++;
|
|
1345
|
+
}
|
|
1346
|
+
state.worktrees = Math.max(0, (worktrees ?? "").split(`
|
|
1347
|
+
`).filter((line) => line.startsWith("worktree ")).length - 1);
|
|
1348
|
+
if (packages.length > 0) {
|
|
1349
|
+
state.unreleased = packages.reduce((sum, release) => sum + release.unreleased, 0);
|
|
1350
|
+
} else {
|
|
1351
|
+
const tag = await read(path, ["describe", "--tags", "--abbrev=0"]);
|
|
1352
|
+
if (tag !== undefined && tag !== "") {
|
|
1353
|
+
state.tag = tag;
|
|
1354
|
+
state.unreleased = await commitsSince(path, tag);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
return state;
|
|
1358
|
+
}
|
|
1359
|
+
function pullBlocker(repo) {
|
|
1360
|
+
if (repo.unreadable)
|
|
1361
|
+
return "unreadable";
|
|
1362
|
+
if (repo.fetchFailed)
|
|
1363
|
+
return "fetch failed";
|
|
1364
|
+
if (repo.branch === "(detached)")
|
|
1365
|
+
return "detached HEAD";
|
|
1366
|
+
if (repo.upstream === null)
|
|
1367
|
+
return "no upstream";
|
|
1368
|
+
if (repo.dirty > 0)
|
|
1369
|
+
return `${repo.dirty} dirty ${repo.dirty === 1 ? "file" : "files"}`;
|
|
1370
|
+
return (repo.ahead ?? 0) > 0 ? `diverged, ${repo.ahead} ahead` : undefined;
|
|
1371
|
+
}
|
|
1372
|
+
function firstLine(text) {
|
|
1373
|
+
const end = text.indexOf(`
|
|
1374
|
+
`);
|
|
1375
|
+
return end === -1 ? text : text.slice(0, end);
|
|
1376
|
+
}
|
|
1377
|
+
async function fastForward(path) {
|
|
1378
|
+
try {
|
|
1379
|
+
const ran = await runCommand(["git", "--no-optional-locks", "merge", "--ff-only", "--quiet", "@{upstream}"], { cwd: path, env: { ...process12.env, GIT_TERMINAL_PROMPT: "0" }, timeoutMs: GIT_MS });
|
|
1380
|
+
if (ran.timedOut)
|
|
1381
|
+
return "timed out";
|
|
1382
|
+
return ran.code === 0 ? undefined : firstLine(ran.text.trim()) || `exit ${ran.code}`;
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
return error instanceof Error ? error.message : String(error);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async function pool(items, limit, task) {
|
|
1388
|
+
const results = new Array(items.length);
|
|
1389
|
+
let next = 0;
|
|
1390
|
+
async function worker() {
|
|
1391
|
+
while (next < items.length) {
|
|
1392
|
+
const index = next++;
|
|
1393
|
+
results[index] = await task(items[index]);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
1397
|
+
return results;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/repos.ts
|
|
1401
|
+
var USAGE = `repos [options] [root]: the git repositories under root (~/Workspace by default) that need attention, with their branch, dirty files, ahead/behind their upstream, linked worktrees and commits since their latest tag
|
|
1402
|
+
|
|
1403
|
+
A repository with nothing to report is left out unless --all. A monorepo tagging its packages <folder>-v<version> counts the unreleased commits per package folder. The walk goes ${MAX_DEPTH} levels deep, stops at a .git and skips node_modules, hidden folders and the names in "skip" of ~/.config/kaynooo-cli/repos.json.
|
|
1404
|
+
|
|
1405
|
+
Ahead and behind are those of the last fetch unless --fetch refreshes the upstreams first, which reaches the remote.
|
|
1406
|
+
--pull fetches, then fast-forwards the repositories listed that are clean, on a branch and only behind their upstream, and touches nothing else: a dirty, ahead, diverged or detached one is reported as skipped. A repository pulled is listed again as it now stands.
|
|
1407
|
+
--path prints only the paths of the repositories listed, one per line, for xargs -d '\\n'.`;
|
|
1408
|
+
var EXITS = [[0, "it listed the repositories, including when none needs attention"], [1, "a fast-forward --pull attempted failed"], [2, "a usage error, an unreadable config or a root that is no folder"]];
|
|
1409
|
+
var OPTIONS = {
|
|
1410
|
+
all: { type: "boolean", short: "a", description: "list the repositories with nothing to report too" },
|
|
1411
|
+
only: { type: "string", choices: REASONS, description: "only the repositories with this reason: dirty, unpushed, unreleased or behind" },
|
|
1412
|
+
fetch: { type: "boolean", description: "refresh the upstreams first (reaches the remote)" },
|
|
1413
|
+
pull: { type: "boolean", description: "fetch, then fast-forward the clean repositories listed that are only behind their upstream (reaches the remote)" },
|
|
1414
|
+
path: { type: "boolean", conflicts: ["json"], description: "print only the paths of the repositories listed, one per line" },
|
|
1415
|
+
json: jsonOption("[{name, path, branch, upstream, dirty, ahead, behind, worktrees, tag, unreleased, packages, reasons, fetchFailed, unreadable}]")
|
|
1416
|
+
};
|
|
1417
|
+
async function skipped() {
|
|
1418
|
+
const file = userConfigReadPath("repos");
|
|
1419
|
+
if (!existsSync3(file))
|
|
1420
|
+
return new Set;
|
|
1421
|
+
try {
|
|
1422
|
+
const config = await Bun.file(file).json();
|
|
1423
|
+
return new Set(Array.isArray(config.skip) ? config.skip.filter((name) => typeof name === "string") : []);
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
fail(`${file} does not parse: ${error instanceof Error ? error.message : String(error)}`);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
function releasedCell(repo) {
|
|
1429
|
+
if (repo.packages.length > 0)
|
|
1430
|
+
return repo.packages.filter((release) => release.unreleased > 0).map((release) => `${release.name} ${release.unreleased}`).join(", ") || "-";
|
|
1431
|
+
return repo.tag === null || repo.unreleased === 0 ? "-" : `${repo.unreleased} since ${repo.tag}`;
|
|
1432
|
+
}
|
|
1433
|
+
function row(repo, shown) {
|
|
1434
|
+
const tint = role("path", process13.stdout);
|
|
1435
|
+
const ahead = repo.ahead ?? 0;
|
|
1436
|
+
const behind = repo.behind ?? 0;
|
|
1437
|
+
const sync = repo.upstream === null ? "no upstream" : [ahead > 0 ? `+${ahead}` : "", behind > 0 ? `-${behind}` : ""].filter(Boolean).join(" ") || "-";
|
|
1438
|
+
return [tint(shown), repo.unreadable ? "? (unreadable)" : repo.branch, repo.dirty === 0 ? "-" : String(repo.dirty), sync, repo.worktrees === 0 ? "-" : String(repo.worktrees), releasedCell(repo)];
|
|
1439
|
+
}
|
|
1440
|
+
async function repos(args) {
|
|
1441
|
+
const { values, positionals } = parse(args, USAGE, OPTIONS, { positionals: ["[root]"], exits: EXITS, replaces: "a cd, git status, git log and git tag loop over each repository of the workspace", examples: ["repos", "repos ~/Workspace --only unreleased --fetch", ["repos --only behind --pull", "fast-forward what is behind and clean"], ["repos --only dirty --path", "the paths, for xargs"]] });
|
|
1442
|
+
const root = resolve(positionals[0] ?? join5(homedir3(), "Workspace"));
|
|
1443
|
+
if (!existsSync3(root) || !statSync(root).isDirectory())
|
|
1444
|
+
fail(`no folder ${root}`);
|
|
1445
|
+
const only = values.only === undefined ? undefined : oneOf(values.only, "only", REASONS);
|
|
1446
|
+
const paths = await findRepos(root, await skipped());
|
|
1447
|
+
const states = await pool(paths, CONCURRENCY, async (path) => inspect(path, values.fetch === true || values.pull === true));
|
|
1448
|
+
const keeps = (repo) => {
|
|
1449
|
+
const reasons = reasonsOf(repo);
|
|
1450
|
+
return only === undefined ? values.all === true || reasons.length > 0 || repo.unreadable : reasons.includes(only);
|
|
1451
|
+
};
|
|
1452
|
+
let failedPulls = 0;
|
|
1453
|
+
if (values.pull === true) {
|
|
1454
|
+
const log = values.json === true || values.path === true ? console.error : console.log;
|
|
1455
|
+
for (const [index, repo] of states.entries()) {
|
|
1456
|
+
if (!keeps(repo) || (repo.behind ?? 0) === 0)
|
|
1457
|
+
continue;
|
|
1458
|
+
const blocker = pullBlocker(repo);
|
|
1459
|
+
const refused = blocker === undefined ? await fastForward(repo.path) : undefined;
|
|
1460
|
+
if (blocker !== undefined) {
|
|
1461
|
+
log(marked("note", `${repo.name} skipped`, process13.stdout, blocker));
|
|
1462
|
+
} else if (refused !== undefined) {
|
|
1463
|
+
failedPulls++;
|
|
1464
|
+
log(marked("problem", `${repo.name} not pulled`, process13.stdout, refused));
|
|
1465
|
+
} else {
|
|
1466
|
+
log(marked("ok", `${repo.name} pulled`, process13.stdout, `${repo.behind} ${repo.behind === 1 ? "commit" : "commits"}`));
|
|
1467
|
+
states[index] = await inspect(repo.path, false);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
const kept = states.filter(keeps);
|
|
1472
|
+
if (values.path === true) {
|
|
1473
|
+
for (const repo of kept)
|
|
1474
|
+
console.log(repo.path);
|
|
1475
|
+
return failedPulls > 0 ? 1 : 0;
|
|
1476
|
+
}
|
|
1477
|
+
if (values.json === true) {
|
|
1478
|
+
printJson(kept.map((repo) => ({ ...repo, reasons: reasonsOf(repo) })));
|
|
1479
|
+
return failedPulls > 0 ? 1 : 0;
|
|
1480
|
+
}
|
|
1481
|
+
const label = role("label", process13.stdout);
|
|
1482
|
+
if (kept.length === 0) {
|
|
1483
|
+
console.log(label(`nothing needs attention in ${states.length} repositories`));
|
|
1484
|
+
return failedPulls > 0 ? 1 : 0;
|
|
1485
|
+
}
|
|
1486
|
+
console.log(table([["repo", "branch", "dirty", "ahead/behind", "worktrees", "unreleased"], ...kept.map((repo) => row(repo, relative(root, repo.path) || repo.name))], { header: true, align: ["left", "left", "right", "left", "right", "left"] }));
|
|
1487
|
+
const failed = kept.filter((repo) => repo.fetchFailed).map((repo) => repo.name);
|
|
1488
|
+
if (failed.length > 0)
|
|
1489
|
+
console.log(label(`fetch failed for ${failed.join(", ")}`));
|
|
1490
|
+
if (values.fetch !== true)
|
|
1491
|
+
console.log(label("ahead/behind as of the last fetch, --fetch refreshes them"));
|
|
1492
|
+
return failedPulls > 0 ? 1 : 0;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
// src/repos-cli.ts
|
|
1496
|
+
process14.exitCode = await runMain({ name: "repos", run: repos });
|