@kaynooo/cli-workspace 0.0.0-stage → 0.3.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 +1628 -0
- package/dist/cli.js +2414 -0
- package/dist/repos-cli.js +1684 -0
- package/dist/sweep-cli.js +1914 -0
- package/package.json +37 -4
|
@@ -0,0 +1,1914 @@
|
|
|
1
|
+
#!/usr/bin/env -S bun --no-env-file
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/sweep-cli.ts
|
|
5
|
+
import process15 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 process8 from "process";
|
|
17
|
+
import { parseArgs } from "util";
|
|
18
|
+
|
|
19
|
+
// ../kit/src/format.ts
|
|
20
|
+
import process6 from "process";
|
|
21
|
+
|
|
22
|
+
// ../kit/src/style.ts
|
|
23
|
+
import process5 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/theme.ts
|
|
287
|
+
import { existsSync as existsSync3, readFileSync, statSync } from "fs";
|
|
288
|
+
import { homedir as homedir2 } from "os";
|
|
289
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
290
|
+
import process4 from "process";
|
|
291
|
+
|
|
292
|
+
// ../kit/src/config.ts
|
|
293
|
+
import { existsSync } from "fs";
|
|
294
|
+
import { dirname, join, resolve } from "path";
|
|
295
|
+
import process2 from "process";
|
|
296
|
+
function findUp(names, from = process2.cwd(), stop = () => false) {
|
|
297
|
+
const wanted = typeof names === "string" ? [names] : names;
|
|
298
|
+
for (let dir = resolve(from);; dir = dirname(dir)) {
|
|
299
|
+
const found = wanted.map((name) => join(dir, name)).find((path) => existsSync(path));
|
|
300
|
+
if (found !== undefined)
|
|
301
|
+
return found;
|
|
302
|
+
if (stop(dir) || dirname(dir) === dir)
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ../kit/src/user-config.ts
|
|
308
|
+
import { existsSync as existsSync2 } from "fs";
|
|
309
|
+
import { homedir } from "os";
|
|
310
|
+
import { join as join2 } from "path";
|
|
311
|
+
import process3 from "process";
|
|
312
|
+
function configBase(where = {}) {
|
|
313
|
+
const xdg = where.xdg ?? (where.home === undefined ? process3.env.XDG_CONFIG_HOME : undefined);
|
|
314
|
+
return xdg !== undefined && xdg !== "" ? xdg : join2(where.home ?? homedir(), ".config");
|
|
315
|
+
}
|
|
316
|
+
function userConfigPath(bin, where = {}) {
|
|
317
|
+
return join2(configBase(where), "kaynooo-cli", `${bin}.json`);
|
|
318
|
+
}
|
|
319
|
+
function userConfigReadPath(bin, legacy = [], where = {}) {
|
|
320
|
+
const current = userConfigPath(bin, where);
|
|
321
|
+
if (existsSync2(current))
|
|
322
|
+
return current;
|
|
323
|
+
const base = configBase(where);
|
|
324
|
+
return legacy.map((parts) => join2(base, ...parts)).find((path) => existsSync2(path)) ?? current;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ../kit/src/theme.ts
|
|
328
|
+
var DEFAULT = {
|
|
329
|
+
colors: { accent: "cyan", id: "yellow", path: "blue", label: "dim", value: "bold", ok: "green", warn: "yellow", problem: "red", note: "dim", passed: "green", failed: "red", running: "cyan", queued: "yellow", skipped: "dim" },
|
|
330
|
+
glyphs: { ok: "\u2713", warn: "\u25B2", problem: "\u2717", note: "\u203A", passed: "\u2713", failed: "\u2717", running: "\u25CF", queued: "\u25CB", skipped: "-", bar: "\u2501" },
|
|
331
|
+
accents: ["cyan", "magenta", "green", "blue", "yellow", "redBright"],
|
|
332
|
+
bins: {}
|
|
333
|
+
};
|
|
334
|
+
var BRIGHT = { accent: "cyanBright", id: "yellowBright", path: "blueBright", label: "white", value: "bold whiteBright", ok: "greenBright", warn: "yellowBright", problem: "redBright", note: "white", passed: "greenBright", failed: "redBright", running: "cyanBright", queued: "yellowBright", skipped: "white" };
|
|
335
|
+
var MONO = { accent: "bold", id: "bold", path: "underline", label: "dim", value: "bold", ok: "bold", warn: "bold", problem: "bold", note: "dim", passed: "bold", failed: "bold", running: "bold", queued: "dim", skipped: "dim" };
|
|
336
|
+
var ASCII = { ok: "+", warn: "!", problem: "x", note: ">", passed: "+", failed: "x", running: "*", queued: "o", skipped: "-", bar: "#" };
|
|
337
|
+
var PRESETS = {
|
|
338
|
+
default: {},
|
|
339
|
+
"high-contrast": { colors: BRIGHT, accents: ["cyanBright", "magentaBright", "greenBright", "blueBright", "yellowBright", "redBright"] },
|
|
340
|
+
mono: { colors: MONO, accents: ["bold"] },
|
|
341
|
+
ascii: { glyphs: ASCII }
|
|
342
|
+
};
|
|
343
|
+
var STYLES = new Set(["bold", "dim", "italic", "underline", "inverse", "strikethrough", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray", "grey", "blackBright", "redBright", "greenBright", "yellowBright", "blueBright", "magentaBright", "cyanBright", "whiteBright"]);
|
|
344
|
+
var HEX = /^#[0-9a-f]{6}$/i;
|
|
345
|
+
var CONTROL = /\p{Cc}/u;
|
|
346
|
+
var MAX_FILE_BYTES = 64 * 1024;
|
|
347
|
+
var SPACES = /\s+/;
|
|
348
|
+
function isColorSpec(spec) {
|
|
349
|
+
if (typeof spec === "number")
|
|
350
|
+
return Number.isInteger(spec) && spec >= 0 && spec <= 255;
|
|
351
|
+
return typeof spec === "string" && spec.trim() !== "" && spec.trim().split(SPACES).every((token) => STYLES.has(token) || HEX.test(token));
|
|
352
|
+
}
|
|
353
|
+
function tinted(colors, spec) {
|
|
354
|
+
if (typeof spec === "number")
|
|
355
|
+
return colors.ansi256(spec);
|
|
356
|
+
return spec.trim().split(SPACES).reduce((chain, token) => HEX.test(token) ? chain.hex(token) : chain[token] ?? chain, colors);
|
|
357
|
+
}
|
|
358
|
+
function isRecord(value) {
|
|
359
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
360
|
+
}
|
|
361
|
+
function readThemeFile(path, warn) {
|
|
362
|
+
try {
|
|
363
|
+
const stat = statSync(path, { throwIfNoEntry: false });
|
|
364
|
+
if (stat === undefined)
|
|
365
|
+
return;
|
|
366
|
+
if (!stat.isFile() || stat.size > MAX_FILE_BYTES) {
|
|
367
|
+
warn(`${path} is not a regular file under ${MAX_FILE_BYTES / 1024} KiB, theme ignored`);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
371
|
+
if (isRecord(parsed))
|
|
372
|
+
return parsed;
|
|
373
|
+
warn(`${path} is not a JSON object, theme ignored`);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
warn(`${path} does not parse (${error instanceof Error ? error.message : String(error)}), theme ignored`);
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
function applyPreset(theme, name, warn, where) {
|
|
380
|
+
if (name === undefined)
|
|
381
|
+
return;
|
|
382
|
+
if (typeof name !== "string" || !Object.hasOwn(PRESETS, name)) {
|
|
383
|
+
warn(`${where}: no preset ${String(name)}, one of ${Object.keys(PRESETS).join(", ")}`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const preset = PRESETS[name];
|
|
387
|
+
Object.assign(theme.colors, preset.colors);
|
|
388
|
+
Object.assign(theme.glyphs, preset.glyphs);
|
|
389
|
+
if (preset.accents !== undefined)
|
|
390
|
+
theme.accents = [...preset.accents];
|
|
391
|
+
return name;
|
|
392
|
+
}
|
|
393
|
+
function applyFile(theme, file, where, warn) {
|
|
394
|
+
if (isRecord(file.colors)) {
|
|
395
|
+
for (const [key, value] of Object.entries(file.colors)) {
|
|
396
|
+
if (!Object.hasOwn(DEFAULT.colors, key))
|
|
397
|
+
warn(`${where}: no colour ${key}, one of ${Object.keys(DEFAULT.colors).join(", ")}`);
|
|
398
|
+
else if (!isColorSpec(value))
|
|
399
|
+
warn(`${where}: colour ${key} is not an ANSI name, \`#rrggbb\` or a number up to 255`);
|
|
400
|
+
else
|
|
401
|
+
theme.colors[key] = value;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (isRecord(file.glyphs)) {
|
|
405
|
+
for (const [key, value] of Object.entries(file.glyphs)) {
|
|
406
|
+
if (!Object.hasOwn(DEFAULT.glyphs, key))
|
|
407
|
+
warn(`${where}: no glyph ${key}, one of ${Object.keys(DEFAULT.glyphs).join(", ")}`);
|
|
408
|
+
else if (typeof value !== "string" || CONTROL.test(value) || Bun.stringWidth(value) !== 1)
|
|
409
|
+
warn(`${where}: glyph ${key} must be one visible character wide, with no control character`);
|
|
410
|
+
else
|
|
411
|
+
theme.glyphs[key] = value;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (Array.isArray(file.accents) && file.accents.length > 0 && file.accents.every(isColorSpec))
|
|
415
|
+
theme.accents = [...file.accents];
|
|
416
|
+
else if (file.accents !== undefined)
|
|
417
|
+
warn(`${where}: accents must be a non-empty list of colours`);
|
|
418
|
+
if (isRecord(file.bins)) {
|
|
419
|
+
for (const [bin, value] of Object.entries(file.bins)) {
|
|
420
|
+
if (isColorSpec(value))
|
|
421
|
+
theme.bins[bin] = value;
|
|
422
|
+
else
|
|
423
|
+
warn(`${where}: bin ${bin} has no valid colour`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function endsWalk(home) {
|
|
428
|
+
return (dir) => dir === resolve2(home) || existsSync3(join3(dir, ".git"));
|
|
429
|
+
}
|
|
430
|
+
var warned = new Set;
|
|
431
|
+
function warnOnce(message) {
|
|
432
|
+
if (warned.has(message))
|
|
433
|
+
return;
|
|
434
|
+
warned.add(message);
|
|
435
|
+
console.error(`warning: theme ${message}`);
|
|
436
|
+
}
|
|
437
|
+
function isPath(chosen) {
|
|
438
|
+
return chosen.includes("/") || chosen.endsWith(".json");
|
|
439
|
+
}
|
|
440
|
+
function loadTheme({ env = process4.env, from = process4.cwd(), home = homedir2(), warn = warnOnce } = {}) {
|
|
441
|
+
const theme = { colors: { ...DEFAULT.colors }, glyphs: { ...DEFAULT.glyphs }, accents: [...DEFAULT.accents], bins: Object.create(null) };
|
|
442
|
+
const sources = [];
|
|
443
|
+
const chosen = env.KIT_THEME ?? "";
|
|
444
|
+
const wanted = chosen === "" ? [userConfigPath("theme", { home, xdg: env.XDG_CONFIG_HOME }), findUp(join3(".kaynooo", "theme.json"), from, endsWalk(home))] : [];
|
|
445
|
+
if (isPath(chosen))
|
|
446
|
+
wanted.push(resolve2(from, chosen));
|
|
447
|
+
const files = [];
|
|
448
|
+
for (const path of wanted) {
|
|
449
|
+
const file = path === undefined ? undefined : readThemeFile(path, warn);
|
|
450
|
+
if (path !== undefined && file !== undefined)
|
|
451
|
+
files.push({ path, file });
|
|
452
|
+
}
|
|
453
|
+
let preset = "default";
|
|
454
|
+
if (chosen !== "" && !isPath(chosen)) {
|
|
455
|
+
preset = applyPreset(theme, chosen, warn, "KIT_THEME") ?? preset;
|
|
456
|
+
} else {
|
|
457
|
+
const named = files.findLast(({ file }) => file.preset !== undefined);
|
|
458
|
+
if (named !== undefined)
|
|
459
|
+
preset = applyPreset(theme, named.file.preset, warn, named.path) ?? preset;
|
|
460
|
+
}
|
|
461
|
+
for (const { path, file } of files) {
|
|
462
|
+
applyFile(theme, file, path, warn);
|
|
463
|
+
sources.push(path);
|
|
464
|
+
}
|
|
465
|
+
return { theme, sources, preset };
|
|
466
|
+
}
|
|
467
|
+
var current;
|
|
468
|
+
var currentKey = "";
|
|
469
|
+
function activeTheme() {
|
|
470
|
+
const key = `${process4.cwd()}\x00${process4.env.KIT_THEME ?? ""}\x00${process4.env.XDG_CONFIG_HOME ?? ""}`;
|
|
471
|
+
if (current === undefined || key !== currentKey) {
|
|
472
|
+
current = loadTheme();
|
|
473
|
+
currentKey = key;
|
|
474
|
+
}
|
|
475
|
+
return current;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ../kit/src/style.ts
|
|
479
|
+
var MARK_WORDS = { ok: "ok:", warn: "warning:", problem: "error:", note: "note:" };
|
|
480
|
+
function takesControlCodes(stream, env = process5.env) {
|
|
481
|
+
return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
|
|
482
|
+
}
|
|
483
|
+
function paint(stream, env = process5.env) {
|
|
484
|
+
return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
|
|
485
|
+
}
|
|
486
|
+
function marked(mark, text, stream, detail, env = process5.env) {
|
|
487
|
+
const colors = paint(stream, env);
|
|
488
|
+
let head = MARK_WORDS[mark];
|
|
489
|
+
if (takesControlCodes(stream, env)) {
|
|
490
|
+
const { theme } = activeTheme();
|
|
491
|
+
head = tinted(colors.bold, theme.colors[mark])(theme.glyphs[mark]);
|
|
492
|
+
}
|
|
493
|
+
return `${head} ${text}${detail === undefined ? "" : ` ${colors.dim(detail)}`}`;
|
|
494
|
+
}
|
|
495
|
+
function ok(text, detail) {
|
|
496
|
+
console.log(marked("ok", text, process5.stdout, detail));
|
|
497
|
+
}
|
|
498
|
+
function note(text, detail) {
|
|
499
|
+
console.log(marked("note", text, process5.stdout, detail));
|
|
500
|
+
}
|
|
501
|
+
function problem(text, detail) {
|
|
502
|
+
console.error(marked("problem", text, process5.stderr, detail));
|
|
503
|
+
}
|
|
504
|
+
function role(name, stream, env = process5.env) {
|
|
505
|
+
const colors = paint(stream, env);
|
|
506
|
+
return takesControlCodes(stream, env) ? tinted(colors, activeTheme().theme.colors[name]) : colors;
|
|
507
|
+
}
|
|
508
|
+
var STATUS_WORDS = {
|
|
509
|
+
passed: ["passed", "success", "succeeded", "ok"],
|
|
510
|
+
failed: ["failed", "failure", "error", "errored"],
|
|
511
|
+
running: ["running", "in_progress", "started"],
|
|
512
|
+
queued: ["queued", "waiting", "pending", "created", "blocked", "scheduled"],
|
|
513
|
+
skipped: ["skipped", "cancelled", "canceled", "manual"]
|
|
514
|
+
};
|
|
515
|
+
var STATUS_OF = new Map(Object.entries(STATUS_WORDS).flatMap(([status, words]) => words.map((word) => [word, status])));
|
|
516
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-hrsswv1w.js
|
|
517
|
+
function $e(t, e = false, i = 1) {
|
|
518
|
+
let n = typeof e === "object" ? e : { units: e ? "si" : "iec", dp: i }, { units: s = "iec", dp: u = 1 } = n, r = s === "si" ? 1000 : 1024;
|
|
519
|
+
if (Math.abs(t) < r)
|
|
520
|
+
return `${t} B`;
|
|
521
|
+
let c = s === "iec" ? ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] : ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], o = -1, l = 10 ** u;
|
|
522
|
+
do
|
|
523
|
+
t /= r, ++o;
|
|
524
|
+
while (Math.round(Math.abs(t) * l) / l >= r && o < c.length - 1);
|
|
525
|
+
return `${t.toFixed(u)} ${c[o]}`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// ../kit/src/format.ts
|
|
529
|
+
var UNITS = [["d", 86400], ["h", 3600], ["m", 60], ["s", 1]];
|
|
530
|
+
function formatSeconds(total) {
|
|
531
|
+
let rest = Math.max(0, Math.round(total));
|
|
532
|
+
const amounts = UNITS.map(([unit, size]) => {
|
|
533
|
+
const amount = Math.floor(rest / size);
|
|
534
|
+
rest -= amount * size;
|
|
535
|
+
return `${amount}${unit}`;
|
|
536
|
+
});
|
|
537
|
+
const first = amounts.findIndex((amount) => !amount.startsWith("0"));
|
|
538
|
+
if (first === -1)
|
|
539
|
+
return "0s";
|
|
540
|
+
const next = amounts[first + 1];
|
|
541
|
+
return next === undefined || next.startsWith("0") ? amounts[first] ?? "0s" : `${amounts[first]} ${next}`;
|
|
542
|
+
}
|
|
543
|
+
function formatDuration(ms) {
|
|
544
|
+
return ms < 60000 ? `${(Math.max(0, ms) / 1000).toFixed(1)}s` : formatSeconds(ms / 1000);
|
|
545
|
+
}
|
|
546
|
+
function upTo(text, separator) {
|
|
547
|
+
const end = text.indexOf(separator);
|
|
548
|
+
return end === -1 ? text : text.slice(0, end);
|
|
549
|
+
}
|
|
550
|
+
function formatAge(date, now = Date.now()) {
|
|
551
|
+
const at = typeof date === "number" ? date : Date.parse(date);
|
|
552
|
+
if (Number.isNaN(at))
|
|
553
|
+
return "-";
|
|
554
|
+
const seconds = (now - at) / 1000;
|
|
555
|
+
return seconds < 60 ? "just now" : `${upTo(formatSeconds(seconds), " ")} ago`;
|
|
556
|
+
}
|
|
557
|
+
function truncate(text, width) {
|
|
558
|
+
return text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
559
|
+
}
|
|
560
|
+
var LINE_BREAKS = /\s*\n\s*/g;
|
|
561
|
+
var ESCAPE = String.fromCharCode(27);
|
|
562
|
+
function textWidth(text) {
|
|
563
|
+
return Bun.stringWidth(text);
|
|
564
|
+
}
|
|
565
|
+
function table(rows, { cellWidth, header = false, align = [], fit, stream = process6.stdout } = {}) {
|
|
566
|
+
const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
|
|
567
|
+
const widths = [];
|
|
568
|
+
for (const row of cells) {
|
|
569
|
+
for (const [index, cell] of row.entries())
|
|
570
|
+
widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
|
|
571
|
+
}
|
|
572
|
+
const shown = fit === undefined ? cells : narrow(cells, widths, fit);
|
|
573
|
+
const colors = paint(stream);
|
|
574
|
+
const lines = shown.map((row) => row.map((cell, index) => {
|
|
575
|
+
const width = widths[index] ?? 0;
|
|
576
|
+
const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
|
|
577
|
+
if (align[index] === "right")
|
|
578
|
+
return `${gap}${cell}`;
|
|
579
|
+
return index === row.length - 1 ? cell : `${cell}${gap}`;
|
|
580
|
+
}).join(" ").trimEnd());
|
|
581
|
+
const rule = header && takesControlCodes(stream) && lines.length > 1 ? [colors.dim("\u2500".repeat(Math.max(...lines.map(textWidth))))] : [];
|
|
582
|
+
return lines.flatMap((line, index) => header && index === 0 ? [colors.bold(line), ...rule] : [line]).join(`
|
|
583
|
+
`);
|
|
584
|
+
}
|
|
585
|
+
function narrow(rows, widths, limit) {
|
|
586
|
+
const used = () => widths.reduce((sum, width) => sum + width, 0) + 2 * Math.max(0, widths.length - 1);
|
|
587
|
+
const cuttable = (index) => rows.every((row) => !(row[index] ?? "").includes(ESCAPE));
|
|
588
|
+
const floor = 8;
|
|
589
|
+
while (used() > limit) {
|
|
590
|
+
let widest = -1;
|
|
591
|
+
for (const [index, width] of widths.entries()) {
|
|
592
|
+
if (cuttable(index) && width > floor && width > (widths[widest] ?? 0))
|
|
593
|
+
widest = index;
|
|
594
|
+
}
|
|
595
|
+
if (widest === -1)
|
|
596
|
+
break;
|
|
597
|
+
widths[widest] = Math.max(floor, (widths[widest] ?? 0) - (used() - limit));
|
|
598
|
+
}
|
|
599
|
+
return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
|
|
600
|
+
}
|
|
601
|
+
function printJson(value) {
|
|
602
|
+
console.log(JSON.stringify(value, undefined, 2));
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ../kit/src/help.ts
|
|
606
|
+
import process7 from "process";
|
|
607
|
+
var OPTIONAL = /\[[^\]]+\]/g;
|
|
608
|
+
var USAGE_PREFIX = /^usage:\s*/i;
|
|
609
|
+
var FULL_STOP = /\.$/;
|
|
610
|
+
function usageLine(usage, colors = paint(process7.stdout)) {
|
|
611
|
+
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
612
|
+
}
|
|
613
|
+
function helpText(usage, options, colors = paint(process7.stdout), { examples = [], replaces, exits = [] } = {}) {
|
|
614
|
+
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
|
|
615
|
+
`);
|
|
616
|
+
}
|
|
617
|
+
function exitRows(exits, colors = paint(process7.stdout)) {
|
|
618
|
+
if (exits.length === 0)
|
|
619
|
+
return [];
|
|
620
|
+
const width = Math.max(...exits.map(([code]) => String(code).length));
|
|
621
|
+
return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
|
|
622
|
+
}
|
|
623
|
+
function replacesRows(replaces, colors = paint(process7.stdout)) {
|
|
624
|
+
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
625
|
+
}
|
|
626
|
+
function wantsHelpJson(env = process7.env) {
|
|
627
|
+
return env.KIT_HELP === "json";
|
|
628
|
+
}
|
|
629
|
+
function description(usage, { examples = [], replaces, exits } = {}, options) {
|
|
630
|
+
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
631
|
+
`);
|
|
632
|
+
const first = lines[0];
|
|
633
|
+
const colon = first.indexOf(": ");
|
|
634
|
+
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
635
|
+
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
636
|
+
return {
|
|
637
|
+
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
638
|
+
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
639
|
+
...replaces === undefined ? {} : { replaces },
|
|
640
|
+
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] }),
|
|
641
|
+
...exits === undefined ? {} : { exits: exits.map(([code, meaning]) => ({ code, meaning })) },
|
|
642
|
+
...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function exampleRows(examples, colors = paint(process7.stdout)) {
|
|
646
|
+
if (examples.length === 0)
|
|
647
|
+
return [];
|
|
648
|
+
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
649
|
+
}
|
|
650
|
+
function optionRows(options, colors = paint(process7.stdout)) {
|
|
651
|
+
const rows = Object.entries(options).map(([name, option]) => {
|
|
652
|
+
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
653
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
654
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
655
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
656
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
657
|
+
});
|
|
658
|
+
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
659
|
+
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// ../kit/src/text.ts
|
|
663
|
+
var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
|
|
664
|
+
function distance(a, b) {
|
|
665
|
+
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
666
|
+
for (const [i, left] of [...a].entries()) {
|
|
667
|
+
const current = [i + 1];
|
|
668
|
+
for (const [j, right] of [...b].entries())
|
|
669
|
+
current.push(Math.min((previous[j + 1] ?? 0) + 1, (current[j] ?? 0) + 1, (previous[j] ?? 0) + (left === right ? 0 : 1)));
|
|
670
|
+
previous = current;
|
|
671
|
+
}
|
|
672
|
+
return previous[b.length] ?? 0;
|
|
673
|
+
}
|
|
674
|
+
function closest(name, names) {
|
|
675
|
+
const started = names.find((known) => known.startsWith(name));
|
|
676
|
+
if (started !== undefined)
|
|
677
|
+
return started;
|
|
678
|
+
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
679
|
+
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
680
|
+
}
|
|
681
|
+
// ../kit/src/input.ts
|
|
682
|
+
var DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
|
|
683
|
+
var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
|
|
684
|
+
function duration(value, name, fallback, unit = "s") {
|
|
685
|
+
if (value === undefined)
|
|
686
|
+
return fallback;
|
|
687
|
+
const match = DURATION.exec(value.trim());
|
|
688
|
+
const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
|
|
689
|
+
if (ms < 1)
|
|
690
|
+
badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
|
|
691
|
+
return ms;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// ../kit/src/args.ts
|
|
695
|
+
var DASHES = /^-+/;
|
|
696
|
+
function jsonOption(holds) {
|
|
697
|
+
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
class Exit extends Error {
|
|
701
|
+
code;
|
|
702
|
+
hint;
|
|
703
|
+
constructor(message, code, hint) {
|
|
704
|
+
super(message);
|
|
705
|
+
this.code = code;
|
|
706
|
+
this.hint = hint;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function fail(message, code = 2, hint) {
|
|
710
|
+
throw new Exit(message, code, hint);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
class UsageError extends Exit {
|
|
714
|
+
constructor(message, hint) {
|
|
715
|
+
super(message, 2, hint);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function badUsage(message) {
|
|
719
|
+
throw new UsageError(message);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
class HelpShown extends Exit {
|
|
723
|
+
constructor() {
|
|
724
|
+
super("help shown", 0);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
class Completing {
|
|
729
|
+
options;
|
|
730
|
+
positionals;
|
|
731
|
+
candidates;
|
|
732
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
733
|
+
this.options = options;
|
|
734
|
+
this.positionals = positionals;
|
|
735
|
+
this.candidates = candidates;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
var completion = { active: false };
|
|
739
|
+
var OUTPUT_OPTIONS = {
|
|
740
|
+
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
741
|
+
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
742
|
+
};
|
|
743
|
+
function applyOutput(color, plain, env = process8.env) {
|
|
744
|
+
const when = plain === true ? "never" : color ?? "auto";
|
|
745
|
+
if (when === "auto")
|
|
746
|
+
return;
|
|
747
|
+
if (when !== "always" && when !== "never")
|
|
748
|
+
badUsage(`--color takes auto, always or never, not ${when}`);
|
|
749
|
+
if (when === "always") {
|
|
750
|
+
env.FORCE_COLOR = "1";
|
|
751
|
+
delete env.NO_COLOR;
|
|
752
|
+
} else {
|
|
753
|
+
env.NO_COLOR = "1";
|
|
754
|
+
delete env.FORCE_COLOR;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function parse(args, usage, options, settings = {}) {
|
|
758
|
+
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
759
|
+
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
760
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
761
|
+
if (completion.active)
|
|
762
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
763
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
764
|
+
let parsed;
|
|
765
|
+
try {
|
|
766
|
+
parsed = parseArgs({ args: joinDashValues(withBare(withAliases(own, all), all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
767
|
+
} catch (error) {
|
|
768
|
+
const message = parseError(error);
|
|
769
|
+
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
770
|
+
}
|
|
771
|
+
const values = parsed.values;
|
|
772
|
+
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
773
|
+
if (values.help === true) {
|
|
774
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings, listing)) : helpText(usage, listing, undefined, settings));
|
|
775
|
+
throw new HelpShown;
|
|
776
|
+
}
|
|
777
|
+
checkChoices(parsed.values, all);
|
|
778
|
+
checkConflicts(parsed.values, all);
|
|
779
|
+
if (settings.positionals !== undefined)
|
|
780
|
+
checkPositionals(parsed.positionals, settings.positionals);
|
|
781
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
782
|
+
}
|
|
783
|
+
function splitRest(args, passes) {
|
|
784
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
785
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
786
|
+
}
|
|
787
|
+
function splitTrailing(args, options, count) {
|
|
788
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
789
|
+
let seen = 0;
|
|
790
|
+
if (count === 0)
|
|
791
|
+
return { own: args, rest: undefined };
|
|
792
|
+
for (let at = 0;at < args.length; at++) {
|
|
793
|
+
const word = args[at];
|
|
794
|
+
if (word === "--")
|
|
795
|
+
break;
|
|
796
|
+
if (word.startsWith("-")) {
|
|
797
|
+
if (takesValue(word))
|
|
798
|
+
at++;
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
seen++;
|
|
802
|
+
if (seen === count)
|
|
803
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
804
|
+
}
|
|
805
|
+
return { own: args, rest: undefined };
|
|
806
|
+
}
|
|
807
|
+
function listed(options, allowNegative) {
|
|
808
|
+
if (!allowNegative)
|
|
809
|
+
return options;
|
|
810
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
811
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
812
|
+
return [[name, option]];
|
|
813
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
814
|
+
}));
|
|
815
|
+
}
|
|
816
|
+
function withAliases(args, options) {
|
|
817
|
+
const named = new Map(Object.entries(options).flatMap(([name, { aliases = [] }]) => aliases.map((alias) => [`--${alias}`, `--${name}`])));
|
|
818
|
+
if (named.size === 0)
|
|
819
|
+
return args;
|
|
820
|
+
const separator = args.indexOf("--");
|
|
821
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
822
|
+
return [...own.map((arg) => {
|
|
823
|
+
const at = arg.indexOf("=");
|
|
824
|
+
const current = named.get(at === -1 ? arg : arg.slice(0, at));
|
|
825
|
+
return current === undefined ? arg : `${current}${at === -1 ? "" : arg.slice(at)}`;
|
|
826
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
827
|
+
}
|
|
828
|
+
function withBare(args, options) {
|
|
829
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
830
|
+
if (bare.size === 0)
|
|
831
|
+
return args;
|
|
832
|
+
const separator = args.indexOf("--");
|
|
833
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
834
|
+
return [...own.flatMap((arg, index) => {
|
|
835
|
+
const optional = bare.get(arg);
|
|
836
|
+
const next = own[index + 1];
|
|
837
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
838
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
839
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
840
|
+
}
|
|
841
|
+
function checkConflicts(values, options) {
|
|
842
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
843
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
844
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
845
|
+
if (other !== undefined)
|
|
846
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
function checkChoices(values, options) {
|
|
850
|
+
for (const [name, option] of Object.entries(options)) {
|
|
851
|
+
if (option.choices === undefined)
|
|
852
|
+
continue;
|
|
853
|
+
const given = values[name];
|
|
854
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
855
|
+
if (typeof value === "string")
|
|
856
|
+
oneOf(value, name, option.choices);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function oneOf(value, name, choices) {
|
|
861
|
+
if (!choices.includes(value))
|
|
862
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
863
|
+
return value;
|
|
864
|
+
}
|
|
865
|
+
function checkPositionals(given, names) {
|
|
866
|
+
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
867
|
+
const required = names.filter((name) => !name.startsWith("[")).length;
|
|
868
|
+
if (given.length < required)
|
|
869
|
+
badUsage(`missing ${names[given.length]}`);
|
|
870
|
+
if (!variadic && given.length > names.length)
|
|
871
|
+
badUsage(`unexpected argument ${given[names.length]}`);
|
|
872
|
+
}
|
|
873
|
+
function joinDashValues(args, options) {
|
|
874
|
+
const longOf = new Map(Object.entries(options).flatMap(([name, option]) => option.short === undefined ? [[`--${name}`, name]] : [[`--${name}`, name], [`-${option.short}`, name]]));
|
|
875
|
+
const isOption = (arg) => arg === "--" || longOf.has(arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg);
|
|
876
|
+
const joined = [];
|
|
877
|
+
for (let i = 0;i < args.length; i++) {
|
|
878
|
+
const arg = args[i] ?? "";
|
|
879
|
+
if (arg === "--")
|
|
880
|
+
return [...joined, ...args.slice(i)];
|
|
881
|
+
const name = longOf.get(arg);
|
|
882
|
+
const next = args[i + 1];
|
|
883
|
+
if (name !== undefined && options[name]?.type === "string" && next !== undefined && next.startsWith("-") && next !== "-" && !isOption(next)) {
|
|
884
|
+
joined.push(`--${name}=${next}`);
|
|
885
|
+
i++;
|
|
886
|
+
} else {
|
|
887
|
+
joined.push(arg);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return joined;
|
|
891
|
+
}
|
|
892
|
+
function parseError(error) {
|
|
893
|
+
const message = upTo(error instanceof Error ? error.message : String(error), ". ");
|
|
894
|
+
return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
|
|
895
|
+
}
|
|
896
|
+
var UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
|
|
897
|
+
function nearestOption(message, options, allowNegative) {
|
|
898
|
+
const typed = UNKNOWN_OPTION.exec(message)?.[1];
|
|
899
|
+
if (typed === undefined)
|
|
900
|
+
return;
|
|
901
|
+
const word = typed.replace(DASHES, "");
|
|
902
|
+
if (word.length < 2 && !typed.startsWith("--"))
|
|
903
|
+
return;
|
|
904
|
+
const known = Object.entries(options).flatMap(([name, option]) => [
|
|
905
|
+
...option.short === undefined ? [] : [`-${option.short}`],
|
|
906
|
+
`--${name}`,
|
|
907
|
+
...allowNegative && option.type === "boolean" ? [`--no-${name}`] : []
|
|
908
|
+
]);
|
|
909
|
+
const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
|
|
910
|
+
return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
|
|
911
|
+
}
|
|
912
|
+
// ../kit/src/process.ts
|
|
913
|
+
import { constants } from "os";
|
|
914
|
+
import process9 from "process";
|
|
915
|
+
var KILL_GRACE_MS = 2000;
|
|
916
|
+
function exitCodeOf(code, signal) {
|
|
917
|
+
if (code !== null)
|
|
918
|
+
return code;
|
|
919
|
+
if (typeof signal === "number")
|
|
920
|
+
return 128 + signal;
|
|
921
|
+
return signal === null ? 129 : 128 + constants.signals[signal];
|
|
922
|
+
}
|
|
923
|
+
async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
|
|
924
|
+
const chunks = [];
|
|
925
|
+
const grouped = timeoutMs !== undefined || signal !== undefined || onStart !== undefined || onChunk !== undefined;
|
|
926
|
+
const proc = Bun.spawn(argv, { ...cwd === undefined ? {} : { cwd }, ...env === undefined ? {} : { env }, stdin, stdout: "pipe", stderr: "pipe", ...grouped ? { detached: true } : {} });
|
|
927
|
+
const state = { kept: 0, truncated: false, timedOut: false, failure: undefined, done: false };
|
|
928
|
+
const timers = [];
|
|
929
|
+
const killGroup = (killing) => {
|
|
930
|
+
if (state.done)
|
|
931
|
+
return;
|
|
932
|
+
try {
|
|
933
|
+
if (grouped)
|
|
934
|
+
process9.kill(-proc.pid, killing);
|
|
935
|
+
else
|
|
936
|
+
proc.kill(killing);
|
|
937
|
+
} catch {}
|
|
938
|
+
};
|
|
939
|
+
const stop = () => {
|
|
940
|
+
killGroup("SIGTERM");
|
|
941
|
+
timers.push(setTimeout(killGroup, KILL_GRACE_MS, "SIGKILL"));
|
|
942
|
+
};
|
|
943
|
+
const onAbort = () => stop();
|
|
944
|
+
if (timeoutMs !== undefined) {
|
|
945
|
+
timers.push(setTimeout(() => {
|
|
946
|
+
state.timedOut = true;
|
|
947
|
+
stop();
|
|
948
|
+
}, timeoutMs));
|
|
949
|
+
}
|
|
950
|
+
if (signal?.aborted === true)
|
|
951
|
+
onAbort();
|
|
952
|
+
else
|
|
953
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
954
|
+
onStart?.({ kill: (killing) => killGroup(killing) });
|
|
955
|
+
const drain = async (stream, name) => {
|
|
956
|
+
for await (const data of stream) {
|
|
957
|
+
if (state.failure !== undefined)
|
|
958
|
+
continue;
|
|
959
|
+
try {
|
|
960
|
+
onChunk?.({ stream: name, data });
|
|
961
|
+
} catch (error) {
|
|
962
|
+
state.failure = error;
|
|
963
|
+
stop();
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (state.kept + data.length > maxBytes) {
|
|
967
|
+
state.truncated = true;
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
state.kept += data.length;
|
|
971
|
+
chunks.push({ stream: name, data });
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
try {
|
|
975
|
+
await Promise.all([drain(proc.stdout, "stdout"), drain(proc.stderr, "stderr")]);
|
|
976
|
+
await proc.exited;
|
|
977
|
+
} finally {
|
|
978
|
+
state.done = true;
|
|
979
|
+
for (const timer of timers)
|
|
980
|
+
clearTimeout(timer);
|
|
981
|
+
signal?.removeEventListener("abort", onAbort);
|
|
982
|
+
}
|
|
983
|
+
if (state.failure !== undefined)
|
|
984
|
+
throw state.failure;
|
|
985
|
+
const joined = () => Buffer.concat(chunks.map(({ data }) => data));
|
|
986
|
+
let output;
|
|
987
|
+
let text;
|
|
988
|
+
return {
|
|
989
|
+
code: exitCodeOf(proc.exitCode, proc.signalCode),
|
|
990
|
+
chunks,
|
|
991
|
+
timedOut: state.timedOut,
|
|
992
|
+
truncated: state.truncated,
|
|
993
|
+
get output() {
|
|
994
|
+
return output ??= joined();
|
|
995
|
+
},
|
|
996
|
+
get text() {
|
|
997
|
+
return text ??= (output ??= joined()).toString();
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// ../kit/src/progress.ts
|
|
1003
|
+
var ESC = String.fromCharCode(27);
|
|
1004
|
+
var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
|
|
1005
|
+
var RESET_STYLE = `${ESC}[0m`;
|
|
1006
|
+
var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
1007
|
+
|
|
1008
|
+
// ../kit/src/tool.ts
|
|
1009
|
+
import process10 from "process";
|
|
1010
|
+
function cwdGone() {
|
|
1011
|
+
try {
|
|
1012
|
+
process10.cwd();
|
|
1013
|
+
return false;
|
|
1014
|
+
} catch {
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
class SpawnError extends Error {
|
|
1020
|
+
missing;
|
|
1021
|
+
constructor(message, missing) {
|
|
1022
|
+
super(message);
|
|
1023
|
+
this.missing = missing;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
function spawnFailure(command, error) {
|
|
1027
|
+
const gone = cwdGone();
|
|
1028
|
+
const missing = !gone && error?.code === "ENOENT";
|
|
1029
|
+
return new SpawnError(`${command}: ${error instanceof Error ? error.message : String(error)}${gone ? " (the working directory was removed)" : ""}`, missing);
|
|
1030
|
+
}
|
|
1031
|
+
async function runTool(label, argv, { timeoutMs, cwd, onStart } = {}) {
|
|
1032
|
+
let ran;
|
|
1033
|
+
try {
|
|
1034
|
+
ran = await runCommand(argv, { env: process10.env, ...timeoutMs === undefined ? {} : { timeoutMs }, ...cwd === undefined ? {} : { cwd }, ...onStart === undefined ? {} : { onStart } });
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
throw spawnFailure(label, error);
|
|
1037
|
+
}
|
|
1038
|
+
if (ran.timedOut)
|
|
1039
|
+
throw new Error(`${label}: no answer in ${formatDuration(timeoutMs ?? 0)}`);
|
|
1040
|
+
const decoders = { stdout: new TextDecoder, stderr: new TextDecoder };
|
|
1041
|
+
const streams = { stdout: "", stderr: "" };
|
|
1042
|
+
for (const { stream, data } of ran.chunks)
|
|
1043
|
+
streams[stream] += decoders[stream].decode(data, { stream: true });
|
|
1044
|
+
streams.stdout += decoders.stdout.decode();
|
|
1045
|
+
streams.stderr += decoders.stderr.decode();
|
|
1046
|
+
return { code: ran.code, ...streams };
|
|
1047
|
+
}
|
|
1048
|
+
// ../kit/src/cli.ts
|
|
1049
|
+
import { readFileSync as readFileSync2, realpathSync } from "fs";
|
|
1050
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
1051
|
+
import process12 from "process";
|
|
1052
|
+
|
|
1053
|
+
// ../kit/src/usage.ts
|
|
1054
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
1055
|
+
import { homedir as homedir3 } from "os";
|
|
1056
|
+
import { dirname as dirname2, isAbsolute, join as join4 } from "path";
|
|
1057
|
+
import process11 from "process";
|
|
1058
|
+
|
|
1059
|
+
// ../kit/src/logs.ts
|
|
1060
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
1061
|
+
var CLI_FOLDER = "kaynooo-cli";
|
|
1062
|
+
|
|
1063
|
+
// ../kit/src/usage.ts
|
|
1064
|
+
var UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
|
|
1065
|
+
var FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
|
|
1066
|
+
var LONGEST_NAME = 40;
|
|
1067
|
+
var usageError;
|
|
1068
|
+
function noteUsageError(message) {
|
|
1069
|
+
usageError = message;
|
|
1070
|
+
}
|
|
1071
|
+
function stateHome(env = process11.env) {
|
|
1072
|
+
const dir = env.XDG_STATE_HOME;
|
|
1073
|
+
return dir !== undefined && isAbsolute(dir) ? dir : join4(homedir3(), ".local", "state");
|
|
1074
|
+
}
|
|
1075
|
+
function usagePath(env = process11.env) {
|
|
1076
|
+
return join4(stateHome(env), CLI_FOLDER, "usage.jsonl");
|
|
1077
|
+
}
|
|
1078
|
+
function usageRecorded(env = process11.env) {
|
|
1079
|
+
const off = env.KAYNOOO_CLI_NO_USAGE;
|
|
1080
|
+
return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
|
|
1081
|
+
}
|
|
1082
|
+
function flagNames(argv) {
|
|
1083
|
+
const end = argv.indexOf("--");
|
|
1084
|
+
const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
|
|
1085
|
+
return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
|
|
1086
|
+
}
|
|
1087
|
+
function usageEntry(bin, command, argv, code, now = new Date) {
|
|
1088
|
+
const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
|
|
1089
|
+
usageError = undefined;
|
|
1090
|
+
return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
|
|
1091
|
+
}
|
|
1092
|
+
function recordUsage(entry, env = process11.env) {
|
|
1093
|
+
if (!usageRecorded(env))
|
|
1094
|
+
return;
|
|
1095
|
+
try {
|
|
1096
|
+
const path = usagePath(env);
|
|
1097
|
+
mkdirSync(dirname2(path), { recursive: true, mode: 448 });
|
|
1098
|
+
appendFileSync(path, `${JSON.stringify(entry)}
|
|
1099
|
+
`, { mode: 384 });
|
|
1100
|
+
} catch {}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// ../kit/src/cli.ts
|
|
1104
|
+
var LEVEL_OPTIONS = {
|
|
1105
|
+
...OUTPUT_OPTIONS,
|
|
1106
|
+
help: { type: "boolean", short: "h", description: "show this help" }
|
|
1107
|
+
};
|
|
1108
|
+
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
1109
|
+
async function commandRows(all, colors) {
|
|
1110
|
+
const commands = shown(all);
|
|
1111
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
1112
|
+
const groups = new Map([["Commands:", []]]);
|
|
1113
|
+
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
1114
|
+
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
1115
|
+
const heading = group === undefined ? "Commands:" : `${group}:`;
|
|
1116
|
+
const rows = groups.get(heading) ?? [];
|
|
1117
|
+
rows.push(` ${colors.cyan(name.padEnd(width))} ${colors.dim(summaries[index])}`);
|
|
1118
|
+
groups.set(heading, rows);
|
|
1119
|
+
}
|
|
1120
|
+
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
1121
|
+
}
|
|
1122
|
+
function shown(commands) {
|
|
1123
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
1124
|
+
}
|
|
1125
|
+
function takeOutputFlags(argv) {
|
|
1126
|
+
let index = 0;
|
|
1127
|
+
for (;index < argv.length; index++) {
|
|
1128
|
+
const word = argv[index];
|
|
1129
|
+
if (word === "--plain")
|
|
1130
|
+
applyOutput(undefined, true);
|
|
1131
|
+
else if (word.startsWith("--color="))
|
|
1132
|
+
applyOutput(word.slice("--color=".length), undefined);
|
|
1133
|
+
else if (word === "--color" && argv[index + 1] !== undefined)
|
|
1134
|
+
applyOutput(argv[++index], undefined);
|
|
1135
|
+
else
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
return argv.slice(index);
|
|
1139
|
+
}
|
|
1140
|
+
async function resolveSummary(summary) {
|
|
1141
|
+
return typeof summary === "string" ? summary : summary();
|
|
1142
|
+
}
|
|
1143
|
+
function scopeOf(commands, extra, aliases) {
|
|
1144
|
+
return { commands: typeof commands === "function" ? {} : commands, lazy: [...typeof commands === "function" ? [commands] : [], ...extra === undefined ? [] : [extra]], aliases };
|
|
1145
|
+
}
|
|
1146
|
+
async function expand(scope, words) {
|
|
1147
|
+
if (scope.lazy.length === 0)
|
|
1148
|
+
return scope;
|
|
1149
|
+
const commands = { ...scope.commands };
|
|
1150
|
+
for (const added of await Promise.all(scope.lazy.map(async (lazy) => lazy(words)))) {
|
|
1151
|
+
for (const [name, command] of Object.entries(added))
|
|
1152
|
+
commands[name] ??= command;
|
|
1153
|
+
}
|
|
1154
|
+
return { ...scope, commands, lazy: [] };
|
|
1155
|
+
}
|
|
1156
|
+
async function help2({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
|
|
1157
|
+
const colors = paint(stream);
|
|
1158
|
+
const prefix = path.join(" ");
|
|
1159
|
+
const summary = await resolveSummary(lazySummary);
|
|
1160
|
+
if (wantsHelpJson())
|
|
1161
|
+
return JSON.stringify(description(`${prefix} <command> [options]: ${summary}`, { examples, replaces }));
|
|
1162
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1163
|
+
const options = fallback !== undefined && "run" in fallback ? await optionsOf(fallback.run) : undefined;
|
|
1164
|
+
const own = Object.entries(options ?? {}).filter(([name]) => !(name in TOP_OPTIONS));
|
|
1165
|
+
return [
|
|
1166
|
+
header(colors, summary),
|
|
1167
|
+
"",
|
|
1168
|
+
usageLine(`${prefix} ${defaultCommand === undefined ? colors.cyan("<command>") : `[${colors.cyan("<command>")}]`} [options]`, colors),
|
|
1169
|
+
...replacesRows(replaces, colors),
|
|
1170
|
+
"",
|
|
1171
|
+
...await commandRows(commands, colors),
|
|
1172
|
+
...own.length === 0 ? [] : ["", `${colors.bold(`Options of ${defaultCommand}:`)} ${colors.dim("the command run when none is named")}`, ...optionRows(Object.fromEntries(own), colors)],
|
|
1173
|
+
"",
|
|
1174
|
+
colors.bold("Options:"),
|
|
1175
|
+
...optionRows(path.length === 1 ? TOP_OPTIONS : LEVEL_OPTIONS, colors),
|
|
1176
|
+
...exampleRows(examples ?? [], colors),
|
|
1177
|
+
"",
|
|
1178
|
+
`${colors.dim("Run")} ${prefix} ${colors.cyan("<command>")} --help ${colors.dim("for its options.")}`
|
|
1179
|
+
].join(`
|
|
1180
|
+
`);
|
|
1181
|
+
}
|
|
1182
|
+
function printError(prefix, message, hint, cause) {
|
|
1183
|
+
const colors = paint(process12.stderr);
|
|
1184
|
+
const styled = takesControlCodes(process12.stderr);
|
|
1185
|
+
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
1186
|
+
const under = (label, text, tint) => text === undefined ? "" : `
|
|
1187
|
+
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
1188
|
+
console.error(`${head} ${message}${under("cause", cause, colors.dim)}${under("hint", hint, colors.yellow)}`);
|
|
1189
|
+
}
|
|
1190
|
+
function printJsonError(message, hint, cause) {
|
|
1191
|
+
console.error(JSON.stringify({ error: message, ...hint === undefined ? {} : { hint }, ...cause === undefined ? {} : { cause } }));
|
|
1192
|
+
}
|
|
1193
|
+
function wantsJson(argv) {
|
|
1194
|
+
const end = argv.indexOf("--");
|
|
1195
|
+
return (end === -1 ? argv : argv.slice(0, end)).includes("--json");
|
|
1196
|
+
}
|
|
1197
|
+
function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
1198
|
+
if (error instanceof HelpShown)
|
|
1199
|
+
return 0;
|
|
1200
|
+
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
1201
|
+
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
1202
|
+
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
1203
|
+
if (error instanceof Exit && error.code === 2)
|
|
1204
|
+
noteUsageError(error.message);
|
|
1205
|
+
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
1206
|
+
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
1207
|
+
return error instanceof Exit ? error.code : 1;
|
|
1208
|
+
}
|
|
1209
|
+
async function dispatch(level, typed, { version, redact }) {
|
|
1210
|
+
const prefix = level.path.join(" ");
|
|
1211
|
+
let argv;
|
|
1212
|
+
try {
|
|
1213
|
+
argv = takeOutputFlags(typed);
|
|
1214
|
+
} catch (error) {
|
|
1215
|
+
return reportThrown(prefix, error);
|
|
1216
|
+
}
|
|
1217
|
+
const [name, ...args] = argv;
|
|
1218
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
1219
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
1220
|
+
try {
|
|
1221
|
+
return await fallback.run([]);
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (name === undefined && level.path.length > 1) {
|
|
1227
|
+
console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stderr));
|
|
1228
|
+
return 2;
|
|
1229
|
+
}
|
|
1230
|
+
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
1231
|
+
console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stdout));
|
|
1232
|
+
return 0;
|
|
1233
|
+
}
|
|
1234
|
+
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
1235
|
+
console.log(version);
|
|
1236
|
+
return 0;
|
|
1237
|
+
}
|
|
1238
|
+
let command = level.commands[level.aliases?.[name] ?? name];
|
|
1239
|
+
let rest = args;
|
|
1240
|
+
let ran = name;
|
|
1241
|
+
if (command === undefined && level.lazy.length > 0) {
|
|
1242
|
+
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
1243
|
+
command = level.commands[level.aliases?.[name] ?? name];
|
|
1244
|
+
}
|
|
1245
|
+
if (command === undefined && level.defaultCommand !== undefined) {
|
|
1246
|
+
command = level.commands[level.defaultCommand];
|
|
1247
|
+
rest = argv;
|
|
1248
|
+
ran = level.defaultCommand;
|
|
1249
|
+
}
|
|
1250
|
+
if (command === undefined) {
|
|
1251
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
1252
|
+
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
1253
|
+
return 2;
|
|
1254
|
+
}
|
|
1255
|
+
if ("commands" in command) {
|
|
1256
|
+
const { summary } = command;
|
|
1257
|
+
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 });
|
|
1258
|
+
}
|
|
1259
|
+
try {
|
|
1260
|
+
return await command.run(rest);
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
var COMPLETE = "__complete";
|
|
1266
|
+
var FILES = "__files__";
|
|
1267
|
+
async function completingOf(run) {
|
|
1268
|
+
completion.active = true;
|
|
1269
|
+
try {
|
|
1270
|
+
await run([]);
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
if (error instanceof Completing)
|
|
1273
|
+
return error;
|
|
1274
|
+
} finally {
|
|
1275
|
+
completion.active = false;
|
|
1276
|
+
}
|
|
1277
|
+
return new Completing({});
|
|
1278
|
+
}
|
|
1279
|
+
async function optionsOf(run) {
|
|
1280
|
+
return (await completingOf(run)).options;
|
|
1281
|
+
}
|
|
1282
|
+
function candidateLine(candidate) {
|
|
1283
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
1284
|
+
}
|
|
1285
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
1286
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
1287
|
+
let index = 0;
|
|
1288
|
+
let at = 0;
|
|
1289
|
+
while (at < before.length) {
|
|
1290
|
+
const word = before[at];
|
|
1291
|
+
if (word === "--") {
|
|
1292
|
+
index += before.length - at - 1;
|
|
1293
|
+
break;
|
|
1294
|
+
}
|
|
1295
|
+
if (!word.startsWith("-"))
|
|
1296
|
+
index++;
|
|
1297
|
+
else if (takesValue(word))
|
|
1298
|
+
at++;
|
|
1299
|
+
at++;
|
|
1300
|
+
}
|
|
1301
|
+
const last = positionals.at(-1);
|
|
1302
|
+
if (index < positionals.length)
|
|
1303
|
+
return positionals[index];
|
|
1304
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
1305
|
+
}
|
|
1306
|
+
async function leafCandidates(run, words) {
|
|
1307
|
+
const completing = await completingOf(run);
|
|
1308
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
1309
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
1310
|
+
const flag = words.at(-2);
|
|
1311
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
1312
|
+
if (option?.type === "string") {
|
|
1313
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
1314
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
1315
|
+
}
|
|
1316
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
1317
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
1318
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
1319
|
+
}
|
|
1320
|
+
async function candidates(scope, defaultCommand, words, before = []) {
|
|
1321
|
+
const [word = "", ...rest] = words;
|
|
1322
|
+
const listing = rest.length === 0 && !word.startsWith("-");
|
|
1323
|
+
let { commands, aliases } = scope;
|
|
1324
|
+
let command = commands[aliases?.[word] ?? word];
|
|
1325
|
+
if (listing || command === undefined) {
|
|
1326
|
+
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
1327
|
+
command = commands[aliases?.[word] ?? word];
|
|
1328
|
+
}
|
|
1329
|
+
if (listing) {
|
|
1330
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
1331
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1332
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
1333
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
1334
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
1335
|
+
}
|
|
1336
|
+
if (command === undefined) {
|
|
1337
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1338
|
+
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
1339
|
+
}
|
|
1340
|
+
return "commands" in command ? candidates(scopeOf(command.commands, command.extraCommands, command.aliases), undefined, rest, [...before, word]) : leafCandidates(command.run, rest);
|
|
1341
|
+
}
|
|
1342
|
+
async function printCandidates(found) {
|
|
1343
|
+
for (const line of await found)
|
|
1344
|
+
console.log(line);
|
|
1345
|
+
return 0;
|
|
1346
|
+
}
|
|
1347
|
+
function packageVersion(entry = Bun.main) {
|
|
1348
|
+
for (let dir = dirname3(realpathSync(entry));; dir = dirname3(dir)) {
|
|
1349
|
+
try {
|
|
1350
|
+
const { version } = JSON.parse(readFileSync2(join5(dir, "package.json"), "utf8"));
|
|
1351
|
+
if (typeof version === "string")
|
|
1352
|
+
return version;
|
|
1353
|
+
} catch {}
|
|
1354
|
+
if (dirname3(dir) === dir)
|
|
1355
|
+
return "0.0.0";
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
async function runCli(cli, argv = process12.argv.slice(2)) {
|
|
1359
|
+
const version = cli.version ?? packageVersion();
|
|
1360
|
+
if (argv[0] === COMPLETE)
|
|
1361
|
+
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
1362
|
+
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 });
|
|
1363
|
+
if (cli.usage !== false) {
|
|
1364
|
+
try {
|
|
1365
|
+
recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
|
|
1366
|
+
} catch {}
|
|
1367
|
+
}
|
|
1368
|
+
return code;
|
|
1369
|
+
}
|
|
1370
|
+
function commandPath(scope, argv) {
|
|
1371
|
+
const path = [];
|
|
1372
|
+
let level = scope;
|
|
1373
|
+
for (const word of argv.filter((each) => !each.startsWith("-"))) {
|
|
1374
|
+
const name = level.aliases !== undefined && Object.hasOwn(level.aliases, word) ? level.aliases[word] ?? word : word;
|
|
1375
|
+
const command = Object.hasOwn(level.commands, name) ? level.commands[name] : undefined;
|
|
1376
|
+
if (command === undefined)
|
|
1377
|
+
break;
|
|
1378
|
+
path.push(word);
|
|
1379
|
+
if (!("commands" in command))
|
|
1380
|
+
break;
|
|
1381
|
+
level = scopeOf(command.commands, command.extraCommands, command.aliases);
|
|
1382
|
+
}
|
|
1383
|
+
return path.length === 0 ? undefined : path.join(" ");
|
|
1384
|
+
}
|
|
1385
|
+
async function runMain({ name, version, run, redact, usage }, argv = process12.argv.slice(2)) {
|
|
1386
|
+
if (argv[0] === COMPLETE)
|
|
1387
|
+
return printCandidates(leafCandidates(run, argv.slice(1)));
|
|
1388
|
+
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
|
|
1389
|
+
console.log(version ?? packageVersion());
|
|
1390
|
+
return 0;
|
|
1391
|
+
}
|
|
1392
|
+
let code;
|
|
1393
|
+
try {
|
|
1394
|
+
code = await run(argv);
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
code = reportThrown(name, error, redact, wantsJson(argv));
|
|
1397
|
+
}
|
|
1398
|
+
if (usage !== false) {
|
|
1399
|
+
try {
|
|
1400
|
+
recordUsage(usageEntry(name, undefined, argv, code));
|
|
1401
|
+
} catch {}
|
|
1402
|
+
}
|
|
1403
|
+
return code;
|
|
1404
|
+
}
|
|
1405
|
+
// ../kit/src/secrets.ts
|
|
1406
|
+
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
1407
|
+
// ../kit/src/sessions.ts
|
|
1408
|
+
var {Glob } = globalThis.Bun;
|
|
1409
|
+
var SESSION_FILES = new Glob("*.json");
|
|
1410
|
+
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
1411
|
+
// ../kit/src/size.ts
|
|
1412
|
+
var SIZE = /^(\d+(?:\.\d+)?)\s*([kmgt]?)i?b?$/i;
|
|
1413
|
+
var POWERS = { "": 0, k: 1, m: 2, g: 3, t: 4 };
|
|
1414
|
+
function parseSize(text, bareUnit = 1) {
|
|
1415
|
+
const match = SIZE.exec(text.trim());
|
|
1416
|
+
if (match === null)
|
|
1417
|
+
return;
|
|
1418
|
+
const suffix = match[2].toLowerCase();
|
|
1419
|
+
return Math.round(Number(match[1]) * (suffix === "" ? bareUnit : 1024 ** POWERS[suffix]));
|
|
1420
|
+
}
|
|
1421
|
+
// src/sweep.ts
|
|
1422
|
+
import { existsSync as existsSync6, statSync as statSync2 } from "fs";
|
|
1423
|
+
import { homedir as homedir5 } from "os";
|
|
1424
|
+
import { join as join9, relative, resolve as resolve3 } from "path";
|
|
1425
|
+
|
|
1426
|
+
// ../kit/src/prompt.ts
|
|
1427
|
+
import process14 from "process";
|
|
1428
|
+
var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
|
|
1429
|
+
var CONTROL2 = /^[\x00-\x1F\x7F]/;
|
|
1430
|
+
var KEYS = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
|
|
1431
|
+
function readKeys(input) {
|
|
1432
|
+
return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL2.test(sequence) ? [] : [{ text: sequence }]));
|
|
1433
|
+
}
|
|
1434
|
+
async function confirm(question, { default: fallback, ...settings } = {}) {
|
|
1435
|
+
let answered = fallback;
|
|
1436
|
+
const choices = { true: "Y/n", false: "y/N", undefined: "y/n" }[String(fallback)];
|
|
1437
|
+
const accepted = await ask(question, settings, (colors, state) => [state === "asking" ? asking(colors, question, "", choices) : answer(colors, question, state, answered ? "yes" : "no")], (key) => {
|
|
1438
|
+
if (key === "cancel")
|
|
1439
|
+
return "cancel";
|
|
1440
|
+
if (key === "enter")
|
|
1441
|
+
return answered === undefined ? undefined : "enter";
|
|
1442
|
+
const letter = typeof key === "object" ? key.text.toLowerCase() : "";
|
|
1443
|
+
if (letter !== "y" && letter !== "n")
|
|
1444
|
+
return;
|
|
1445
|
+
answered = letter === "y";
|
|
1446
|
+
return "enter";
|
|
1447
|
+
});
|
|
1448
|
+
return accepted ? answered : undefined;
|
|
1449
|
+
}
|
|
1450
|
+
function asking(colors, question, typed, keys) {
|
|
1451
|
+
return `${colors.cyan("?")} ${question} ${typed === "" ? colors.dim(`(${keys})`) : typed}`;
|
|
1452
|
+
}
|
|
1453
|
+
function answer(colors, question, state, picked) {
|
|
1454
|
+
return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
|
|
1455
|
+
}
|
|
1456
|
+
async function ask(question, { hint, terminal = process14 }, render, press) {
|
|
1457
|
+
const { stdin, stderr } = terminal;
|
|
1458
|
+
if (stdin.isTTY !== true || stderr.isTTY !== true)
|
|
1459
|
+
fail(`cannot ask "${question}" off a terminal`, 2, hint);
|
|
1460
|
+
const colors = paint(stderr);
|
|
1461
|
+
let drawn = 0;
|
|
1462
|
+
let outcome;
|
|
1463
|
+
const draw = (state) => {
|
|
1464
|
+
const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
|
|
1465
|
+
stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
|
|
1466
|
+
`)}`);
|
|
1467
|
+
drawn = frame.length;
|
|
1468
|
+
};
|
|
1469
|
+
try {
|
|
1470
|
+
stdin.setRawMode(true);
|
|
1471
|
+
stdin.resume();
|
|
1472
|
+
stderr.write("\x1B[?25l");
|
|
1473
|
+
draw("asking");
|
|
1474
|
+
return await new Promise((resolve) => {
|
|
1475
|
+
function onData(data) {
|
|
1476
|
+
for (const key of readKeys(data.toString())) {
|
|
1477
|
+
const pressed = press(key);
|
|
1478
|
+
if (pressed !== undefined) {
|
|
1479
|
+
settle(pressed);
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
draw("asking");
|
|
1484
|
+
}
|
|
1485
|
+
function onEnd() {
|
|
1486
|
+
settle("cancel");
|
|
1487
|
+
}
|
|
1488
|
+
function settle(settled) {
|
|
1489
|
+
outcome = settled;
|
|
1490
|
+
stdin.off("data", onData);
|
|
1491
|
+
stdin.off("end", onEnd);
|
|
1492
|
+
resolve(settled === "enter");
|
|
1493
|
+
}
|
|
1494
|
+
stdin.on("data", onData);
|
|
1495
|
+
stdin.on("end", onEnd);
|
|
1496
|
+
});
|
|
1497
|
+
} finally {
|
|
1498
|
+
draw(outcome ?? "cancel");
|
|
1499
|
+
stderr.write(`
|
|
1500
|
+
\x1B[?25h`);
|
|
1501
|
+
stdin.setRawMode(false);
|
|
1502
|
+
stdin.pause();
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// src/apply.ts
|
|
1507
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1508
|
+
import { lstat as lstat3, rm } from "fs/promises";
|
|
1509
|
+
import { basename as basename2, join as join8 } from "path";
|
|
1510
|
+
|
|
1511
|
+
// src/sweep-scan.ts
|
|
1512
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1513
|
+
import { lstat as lstat2, readdir as readdir2, realpath } from "fs/promises";
|
|
1514
|
+
import { homedir as homedir4 } from "os";
|
|
1515
|
+
import { basename, dirname as dirname4, join as join7 } from "path";
|
|
1516
|
+
|
|
1517
|
+
// src/size.ts
|
|
1518
|
+
import { lstat, readdir } from "fs/promises";
|
|
1519
|
+
import { join as join6 } from "path";
|
|
1520
|
+
async function pooled(items, limit, work) {
|
|
1521
|
+
const results = Array.from({ length: items.length });
|
|
1522
|
+
let next = 0;
|
|
1523
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
1524
|
+
while (next < items.length) {
|
|
1525
|
+
const index = next++;
|
|
1526
|
+
results[index] = await work(items[index]);
|
|
1527
|
+
}
|
|
1528
|
+
}));
|
|
1529
|
+
return results;
|
|
1530
|
+
}
|
|
1531
|
+
var FS_LIMIT = 32;
|
|
1532
|
+
var waiting = [];
|
|
1533
|
+
var running = 0;
|
|
1534
|
+
async function gated(call) {
|
|
1535
|
+
if (running >= FS_LIMIT)
|
|
1536
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
1537
|
+
running++;
|
|
1538
|
+
try {
|
|
1539
|
+
return await call();
|
|
1540
|
+
} finally {
|
|
1541
|
+
running--;
|
|
1542
|
+
waiting.shift()?.();
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
async function sizeOf(folder) {
|
|
1546
|
+
const seen = new Set;
|
|
1547
|
+
let total = 0;
|
|
1548
|
+
const visit = async (dir) => {
|
|
1549
|
+
let entries;
|
|
1550
|
+
try {
|
|
1551
|
+
entries = await gated(async () => readdir(dir, { withFileTypes: true }));
|
|
1552
|
+
} catch {
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
await Promise.all(entries.map(async (entry) => {
|
|
1556
|
+
const path = join6(dir, entry.name);
|
|
1557
|
+
if (entry.isDirectory())
|
|
1558
|
+
return visit(path);
|
|
1559
|
+
try {
|
|
1560
|
+
const stat = await gated(async () => lstat(path));
|
|
1561
|
+
if (stat.nlink > 1) {
|
|
1562
|
+
const key = `${stat.dev}:${stat.ino}`;
|
|
1563
|
+
if (seen.has(key))
|
|
1564
|
+
return;
|
|
1565
|
+
seen.add(key);
|
|
1566
|
+
}
|
|
1567
|
+
total += stat.size;
|
|
1568
|
+
} catch {}
|
|
1569
|
+
}));
|
|
1570
|
+
};
|
|
1571
|
+
await visit(folder);
|
|
1572
|
+
return total;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// src/sweep-scan.ts
|
|
1576
|
+
var FOLDER_NAMES = new Set(["node_modules", ".nuxt", ".next", ".output", "dist", "build", ".turbo", ".svelte-kit", "target"]);
|
|
1577
|
+
var MAX_DEPTH = 3;
|
|
1578
|
+
var PROJECT_CACHE_NAMES = new Set([".turbo", ".nuxt", ".next", ".svelte-kit"]);
|
|
1579
|
+
var GLOBAL_CACHES = [".bun/install/cache", ".npm/_cacache", ".cache/pip", ".cache/uv", ".cache/ms-playwright", ".cache/puppeteer", ".cache/go-build", ".cache/typescript", ".cargo/registry"];
|
|
1580
|
+
function isCachePath(path, home = homedir4()) {
|
|
1581
|
+
return GLOBAL_CACHES.some((cache) => join7(home, cache) === path) || basename(path) === ".cache" && basename(dirname4(path)) === "node_modules";
|
|
1582
|
+
}
|
|
1583
|
+
async function listing(dir) {
|
|
1584
|
+
try {
|
|
1585
|
+
return await readdir2(dir, { withFileTypes: true });
|
|
1586
|
+
} catch {
|
|
1587
|
+
return [];
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async function addGlobalCaches(found, home) {
|
|
1591
|
+
for (const cache of GLOBAL_CACHES) {
|
|
1592
|
+
const path = join7(home, cache);
|
|
1593
|
+
try {
|
|
1594
|
+
const stat = await lstat2(path);
|
|
1595
|
+
if (stat.isSymbolicLink())
|
|
1596
|
+
found.skipped.push({ path, reason: "symlink" });
|
|
1597
|
+
else if (stat.isDirectory())
|
|
1598
|
+
found.candidates.push({ path, kind: "cache", global: true });
|
|
1599
|
+
} catch {}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
async function findCandidates(root, home = homedir4(), withGlobal = true) {
|
|
1603
|
+
const found = { candidates: [], skipped: [] };
|
|
1604
|
+
const visit = async (dir, depth) => {
|
|
1605
|
+
const entries = await listing(dir);
|
|
1606
|
+
for (const entry of entries) {
|
|
1607
|
+
const path = join7(dir, entry.name);
|
|
1608
|
+
if (entry.name === ".git")
|
|
1609
|
+
continue;
|
|
1610
|
+
if (entry.name === ".worktrees" || entry.name === "worktrees" && basename(dir) === ".claude") {
|
|
1611
|
+
if (!entry.isDirectory()) {
|
|
1612
|
+
found.skipped.push({ path, reason: "symlink" });
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
for (const worktree of await listing(path)) {
|
|
1616
|
+
if (worktree.isDirectory())
|
|
1617
|
+
found.candidates.push({ path: join7(path, worktree.name), kind: "worktree" });
|
|
1618
|
+
else if (worktree.isSymbolicLink())
|
|
1619
|
+
found.skipped.push({ path: join7(path, worktree.name), reason: "symlink" });
|
|
1620
|
+
}
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
if (FOLDER_NAMES.has(entry.name) && (entry.name !== "target" || existsSync4(join7(dir, "Cargo.toml")))) {
|
|
1624
|
+
if (entry.isSymbolicLink()) {
|
|
1625
|
+
found.skipped.push({ path, reason: "symlink" });
|
|
1626
|
+
} else if (entry.isDirectory()) {
|
|
1627
|
+
found.candidates.push({ path, kind: PROJECT_CACHE_NAMES.has(entry.name) ? "cache" : "folder" });
|
|
1628
|
+
if (entry.name === "node_modules") {
|
|
1629
|
+
const cache = (await listing(path)).find((inner) => inner.name === ".cache");
|
|
1630
|
+
if (cache?.isSymbolicLink())
|
|
1631
|
+
found.skipped.push({ path: join7(path, ".cache"), reason: "symlink" });
|
|
1632
|
+
else if (cache?.isDirectory())
|
|
1633
|
+
found.candidates.push({ path: join7(path, ".cache"), kind: "cache", inside: path });
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (entry.isDirectory() && depth < MAX_DEPTH)
|
|
1639
|
+
await visit(path, depth + 1);
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
await visit(root, 0);
|
|
1643
|
+
if (withGlobal)
|
|
1644
|
+
await addGlobalCaches(found, home);
|
|
1645
|
+
return found;
|
|
1646
|
+
}
|
|
1647
|
+
async function realOf(path) {
|
|
1648
|
+
try {
|
|
1649
|
+
return await realpath(path);
|
|
1650
|
+
} catch {
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
var HEADS_PREFIX = /^refs\/heads\//;
|
|
1655
|
+
var TRAILING_SLASH = /\/$/;
|
|
1656
|
+
async function gitIn(cwd, args) {
|
|
1657
|
+
try {
|
|
1658
|
+
const { code, stdout, stderr } = await runTool(`git ${args.join(" ")}`, ["git", "-C", cwd, ...args], { timeoutMs: 30000 });
|
|
1659
|
+
return { code, stdout: stdout.trim(), stderr };
|
|
1660
|
+
} catch (error) {
|
|
1661
|
+
return { code: -1, stdout: "", stderr: String(error) };
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function memo(make) {
|
|
1665
|
+
const cache = new Map;
|
|
1666
|
+
return async (key) => {
|
|
1667
|
+
let value = cache.get(key);
|
|
1668
|
+
if (value === undefined) {
|
|
1669
|
+
value = make(key);
|
|
1670
|
+
cache.set(key, value);
|
|
1671
|
+
}
|
|
1672
|
+
return value;
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
var NOT_A_REPO = /not a git repository/i;
|
|
1676
|
+
var toplevelOf = memo(async (dir) => {
|
|
1677
|
+
const { code, stdout, stderr } = await gitIn(dir, ["rev-parse", "--show-toplevel"]);
|
|
1678
|
+
if (code === 0 && stdout !== "")
|
|
1679
|
+
return { top: stdout };
|
|
1680
|
+
return NOT_A_REPO.test(stderr) ? "none" : "failed";
|
|
1681
|
+
});
|
|
1682
|
+
var lastCommitOf = memo(async (top) => {
|
|
1683
|
+
const { code, stdout } = await gitIn(top, ["log", "-1", "--format=%ct"]);
|
|
1684
|
+
return code === 0 && stdout !== "" ? Number(stdout) * 1000 : undefined;
|
|
1685
|
+
});
|
|
1686
|
+
var worktreesOf = memo(async (repo) => {
|
|
1687
|
+
const { code, stdout } = await gitIn(repo, ["worktree", "list", "--porcelain"]);
|
|
1688
|
+
const branches = new Map;
|
|
1689
|
+
if (code !== 0)
|
|
1690
|
+
return branches;
|
|
1691
|
+
for (const block of stdout.split(`
|
|
1692
|
+
|
|
1693
|
+
`)) {
|
|
1694
|
+
const lines = block.split(`
|
|
1695
|
+
`);
|
|
1696
|
+
const path = lines.find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
|
|
1697
|
+
const branch = lines.find((line) => line.startsWith("branch "))?.slice("branch ".length);
|
|
1698
|
+
if (path !== undefined)
|
|
1699
|
+
branches.set(await realOf(path) ?? path, branch);
|
|
1700
|
+
}
|
|
1701
|
+
return branches;
|
|
1702
|
+
});
|
|
1703
|
+
async function judgeFolder(path, kind, inside) {
|
|
1704
|
+
const parent = dirname4(path);
|
|
1705
|
+
const name = basename(path);
|
|
1706
|
+
const located = await toplevelOf(parent);
|
|
1707
|
+
if (located === "failed")
|
|
1708
|
+
return { skip: "git failed" };
|
|
1709
|
+
if (existsSync4(join7(path, ".git")))
|
|
1710
|
+
return { skip: "holds a git repository" };
|
|
1711
|
+
if (located === "none")
|
|
1712
|
+
return name === "node_modules" || inside !== undefined ? { entry: { path, kind, committedAt: undefined, action: "remove", inside } } : { skip: "not in a git repository" };
|
|
1713
|
+
const tracked = await gitIn(parent, ["ls-files", "--", name]);
|
|
1714
|
+
if (tracked.code !== 0 || tracked.stdout !== "")
|
|
1715
|
+
return { skip: "tracked by git" };
|
|
1716
|
+
if (name !== "node_modules" && inside === undefined && (await gitIn(parent, ["check-ignore", "-q", "--", name])).code !== 0)
|
|
1717
|
+
return { skip: "not ignored by git" };
|
|
1718
|
+
return { entry: { path, kind, committedAt: await lastCommitOf(located.top), action: "remove", inside } };
|
|
1719
|
+
}
|
|
1720
|
+
async function judgeWorktree(path) {
|
|
1721
|
+
const located = await toplevelOf(dirname4(path));
|
|
1722
|
+
if (located === "failed")
|
|
1723
|
+
return { skip: "git failed" };
|
|
1724
|
+
if (located === "none")
|
|
1725
|
+
return { skip: "not in a git repository" };
|
|
1726
|
+
const { top } = located;
|
|
1727
|
+
const real = await realOf(path);
|
|
1728
|
+
const branches = await worktreesOf(top);
|
|
1729
|
+
if (real === undefined || !branches.has(real))
|
|
1730
|
+
return { skip: "not a worktree of this repo" };
|
|
1731
|
+
const committedAt = await lastCommitOf(top);
|
|
1732
|
+
const keep = (reason) => ({ entry: { path, kind: "worktree", committedAt, action: "keep", reason, repo: top } });
|
|
1733
|
+
const branch = branches.get(real);
|
|
1734
|
+
if (branch === undefined)
|
|
1735
|
+
return keep("detached HEAD");
|
|
1736
|
+
const status = await gitIn(path, ["status", "--porcelain", "-z", "--ignored=matching"]);
|
|
1737
|
+
if (status.code !== 0)
|
|
1738
|
+
return keep("status failed");
|
|
1739
|
+
const changes = status.stdout.split("\x00").filter((line) => line !== "");
|
|
1740
|
+
if (changes.some((line) => !line.startsWith("!! ")))
|
|
1741
|
+
return keep("uncommitted changes");
|
|
1742
|
+
if (changes.some((line) => !line.endsWith("/") || !FOLDER_NAMES.has(basename(line.slice(3).replace(TRAILING_SLASH, "")))))
|
|
1743
|
+
return keep("ignored files (.env, local data) that removing would delete");
|
|
1744
|
+
const unique = await gitIn(path, ["log", "--format=%h", branch, "--not", "--remotes", `--exclude=${branch.replace(HEADS_PREFIX, "")}`, "--branches"]);
|
|
1745
|
+
if (unique.code !== 0)
|
|
1746
|
+
return keep("log failed");
|
|
1747
|
+
if (unique.stdout !== "")
|
|
1748
|
+
return keep(`${unique.stdout.split(`
|
|
1749
|
+
`).length} commits on no other branch or remote`);
|
|
1750
|
+
return { entry: { path, kind: "worktree", committedAt, action: "remove", repo: top } };
|
|
1751
|
+
}
|
|
1752
|
+
async function judge(candidate) {
|
|
1753
|
+
if (candidate.global === true)
|
|
1754
|
+
return { entry: { path: candidate.path, kind: candidate.kind, committedAt: undefined, action: "remove" } };
|
|
1755
|
+
return candidate.kind === "worktree" ? judgeWorktree(candidate.path) : judgeFolder(candidate.path, candidate.kind, candidate.inside);
|
|
1756
|
+
}
|
|
1757
|
+
async function inspect({ candidates, skipped }) {
|
|
1758
|
+
const entries = [];
|
|
1759
|
+
const rejected = [...skipped];
|
|
1760
|
+
await pooled(candidates, 8, async (candidate) => {
|
|
1761
|
+
const verdict = await judge(candidate);
|
|
1762
|
+
if ("skip" in verdict)
|
|
1763
|
+
rejected.push({ path: candidate.path, reason: verdict.skip });
|
|
1764
|
+
else
|
|
1765
|
+
entries.push({ ...verdict.entry, bytes: await sizeOf(candidate.path) });
|
|
1766
|
+
});
|
|
1767
|
+
const listed = new Set(entries.filter((entry) => entry.action === "remove").map((entry) => entry.path));
|
|
1768
|
+
return { entries: entries.filter((entry) => entry.inside === undefined || !listed.has(entry.inside)), skipped: rejected };
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// src/apply.ts
|
|
1772
|
+
async function removeFolder(entry) {
|
|
1773
|
+
try {
|
|
1774
|
+
const stat = await lstat3(entry.path);
|
|
1775
|
+
if (stat.isSymbolicLink() || !stat.isDirectory() || !(FOLDER_NAMES.has(basename2(entry.path)) || entry.kind === "cache" && isCachePath(entry.path)))
|
|
1776
|
+
return { path: entry.path, removed: false, error: "no longer a plain folder" };
|
|
1777
|
+
if (existsSync5(join8(entry.path, ".git")))
|
|
1778
|
+
return { path: entry.path, removed: false, error: "holds a git repository" };
|
|
1779
|
+
await rm(entry.path, { recursive: true, force: true });
|
|
1780
|
+
return { path: entry.path, removed: true };
|
|
1781
|
+
} catch (error) {
|
|
1782
|
+
return { path: entry.path, removed: false, error: error instanceof Error ? error.message : String(error) };
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
async function removeWorktree(entry) {
|
|
1786
|
+
if (entry.repo === undefined)
|
|
1787
|
+
return { path: entry.path, removed: false, error: "no repository known" };
|
|
1788
|
+
try {
|
|
1789
|
+
const ran = await runTool("git worktree remove", ["git", "-C", entry.repo, "worktree", "remove", entry.path]);
|
|
1790
|
+
return ran.code === 0 ? { path: entry.path, removed: true } : { path: entry.path, removed: false, error: ran.stderr.trim() };
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
return { path: entry.path, removed: false, error: String(error) };
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
async function applyAll(entries, done) {
|
|
1796
|
+
const outcomes = [];
|
|
1797
|
+
for (const entry of entries) {
|
|
1798
|
+
if (entry.action !== "remove")
|
|
1799
|
+
continue;
|
|
1800
|
+
const outcome = await (entry.kind === "worktree" ? removeWorktree(entry) : removeFolder(entry));
|
|
1801
|
+
outcomes.push(outcome);
|
|
1802
|
+
done(outcome);
|
|
1803
|
+
}
|
|
1804
|
+
return outcomes;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
// src/sweep.ts
|
|
1808
|
+
var LARGE = 100 * 1024 ** 2;
|
|
1809
|
+
var BIG = 1024 ** 3;
|
|
1810
|
+
var USAGE = `sweep [options] [root]: lists the regenerable folders (node_modules, .output, dist, build, target beside a Cargo.toml), the caches (.nuxt, .next, .turbo, .svelte-kit and node_modules/.cache per project; ~/.bun/install/cache, ~/.npm/_cacache, ~/.cache/{pip,uv,ms-playwright,puppeteer,go-build,typescript} and ~/.cargo/registry, exact paths only, listed only when no root is given or with --only caches) and the .worktrees/* and .claude/worktrees/* entries under root (~/Workspace by default, ${MAX_DEPTH} levels down), largest first with the age of the repo's last commit; --only worktrees, folders or caches keeps one kind
|
|
1811
|
+
|
|
1812
|
+
Dry unless --apply, which deletes after a confirm showing the total (read from the terminal; off one, pass --yes). A folder is deleted for good, not trashed. Never touched: a symlink, a folder git tracks files in, a build folder git does not ignore. A worktree is removed with git worktree remove, never forced, and only when it is clean and every commit of its branch is on another branch or a remote; the others stay listed as kept and count for nothing in the total. With --docker the Docker usage comes after the list, left to dclean.
|
|
1813
|
+
|
|
1814
|
+
Exits 0 when it listed, or removed every entry it was allowed to, 1 when a removal failed or the confirm was declined, 2 on a usage error, a missing root, or --apply off a terminal without --yes.`;
|
|
1815
|
+
var ONLY_KINDS = { worktrees: "worktree", folders: "folder", caches: "cache" };
|
|
1816
|
+
var OPTIONS = {
|
|
1817
|
+
apply: { type: "boolean", description: "delete what is listed as removable, after the confirm" },
|
|
1818
|
+
yes: { type: "boolean", short: "y", description: "with --apply, skip the confirm" },
|
|
1819
|
+
older: { type: "string", description: "only the entries whose repo last committed longer ago than this (30d, 12h)" },
|
|
1820
|
+
min: { type: "string", description: "only the entries of at least this size (500K, 10M, 1G)" },
|
|
1821
|
+
only: { type: "string", description: "worktrees, folders or caches: leave the other kinds out (.nuxt, .next, .turbo and .svelte-kit are caches; the global caches are listed with no root or with caches)" },
|
|
1822
|
+
docker: { type: "boolean", description: "add the docker system df output, which alone takes about ten seconds" },
|
|
1823
|
+
json: jsonOption("{root, entries: [{path, kind (folder, worktree or cache), bytes, committedAt, action (remove or keep), reason}], skipped: [{path, reason}], reclaimable, docker, removed?}")
|
|
1824
|
+
};
|
|
1825
|
+
async function dockerUsage() {
|
|
1826
|
+
if (Bun.which("docker") === null)
|
|
1827
|
+
return;
|
|
1828
|
+
try {
|
|
1829
|
+
const { code, stdout } = await runTool("docker system df", ["docker", "system", "df"], { timeoutMs: 1e4 });
|
|
1830
|
+
return code === 0 ? stdout.trimEnd() : undefined;
|
|
1831
|
+
} catch {
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
async function sweep(args) {
|
|
1836
|
+
const { values, positionals } = parse(args, USAGE, OPTIONS, { positionals: ["[root]"], replaces: "find ~/Workspace -name node_modules, du -sh and rm -rf by hand", examples: ["sweep", "sweep ~/Workspace --older 30d --min 100M --apply", "sweep --only worktrees --apply"] });
|
|
1837
|
+
if (positionals.length > 1)
|
|
1838
|
+
badUsage("give one root");
|
|
1839
|
+
const root = resolve3(positionals[0] ?? join9(homedir5(), "Workspace"));
|
|
1840
|
+
if (!existsSync6(root) || !statSync2(root).isDirectory())
|
|
1841
|
+
fail(`${root} is not a folder`);
|
|
1842
|
+
if (values.only !== undefined && !Object.hasOwn(ONLY_KINDS, values.only))
|
|
1843
|
+
badUsage(`--only takes worktrees, folders or caches, not ${values.only}`);
|
|
1844
|
+
const olderMs = values.older === undefined ? undefined : duration(values.older, "older", 0);
|
|
1845
|
+
const minBytes = values.min === undefined ? 0 : parseSize(values.min);
|
|
1846
|
+
if (minBytes === undefined)
|
|
1847
|
+
badUsage(`--min takes a size such as 500K, 10M or 1G, not ${values.min}`);
|
|
1848
|
+
const now = Date.now();
|
|
1849
|
+
const found = await findCandidates(root, homedir5(), positionals.length === 0 || values.only === "caches");
|
|
1850
|
+
const wanted = values.only === undefined ? found.candidates : found.candidates.filter((candidate) => candidate.kind === ONLY_KINDS[values.only]);
|
|
1851
|
+
const { entries: all, skipped } = await inspect({ candidates: wanted, skipped: found.skipped });
|
|
1852
|
+
const entries = all.filter((entry) => entry.bytes >= minBytes && (olderMs === undefined || entry.committedAt !== undefined && now - entry.committedAt >= olderMs)).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path));
|
|
1853
|
+
const removable = entries.filter((entry) => entry.action === "remove");
|
|
1854
|
+
const reclaimable = removable.reduce((sum, entry) => sum + entry.bytes, 0);
|
|
1855
|
+
const docker = values.docker === true ? await dockerUsage() : undefined;
|
|
1856
|
+
const json = values.json === true;
|
|
1857
|
+
if (values.apply === true && removable.length > 0 && values.yes !== true) {
|
|
1858
|
+
if (!json)
|
|
1859
|
+
print(root, entries, skipped.length, reclaimable, now);
|
|
1860
|
+
const agreed = await confirm(`Delete ${removable.length} ${removable.length === 1 ? "entry" : "entries"} (${$e(reclaimable)})?`, { default: false, hint: "pass --yes to skip the confirm" });
|
|
1861
|
+
if (agreed !== true) {
|
|
1862
|
+
if (json)
|
|
1863
|
+
fail("nothing deleted: the confirm was declined", 1, "pass --yes to skip the confirm");
|
|
1864
|
+
note("nothing deleted");
|
|
1865
|
+
return 1;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
const outcomes = values.apply === true ? await applyAll(removable, (outcome) => report(root, outcome, json)) : undefined;
|
|
1869
|
+
const failed = outcomes?.filter((outcome) => !outcome.removed).length ?? 0;
|
|
1870
|
+
if (json) {
|
|
1871
|
+
printJson({ root, entries: entries.map((entry) => ({ path: entry.path, kind: entry.kind, bytes: entry.bytes, committedAt: entry.committedAt === undefined ? null : new Date(entry.committedAt).toISOString(), action: entry.action, reason: entry.reason ?? null })), skipped, reclaimable, docker: docker ?? null, ...outcomes === undefined ? {} : { removed: outcomes } });
|
|
1872
|
+
return failed === 0 ? 0 : 1;
|
|
1873
|
+
}
|
|
1874
|
+
if (outcomes === undefined) {
|
|
1875
|
+
print(root, entries, skipped.length, reclaimable, now);
|
|
1876
|
+
if (removable.length > 0)
|
|
1877
|
+
console.log("dry run, pass --apply to delete");
|
|
1878
|
+
} else {
|
|
1879
|
+
const bytes = new Map(removable.map((entry) => [entry.path, entry.bytes]));
|
|
1880
|
+
note(`freed ${$e(outcomes.reduce((sum, outcome) => sum + (outcome.removed ? bytes.get(outcome.path) ?? 0 : 0), 0))}`);
|
|
1881
|
+
}
|
|
1882
|
+
if (docker !== undefined)
|
|
1883
|
+
console.log(`
|
|
1884
|
+
docker (left to dclean):
|
|
1885
|
+
${docker}`);
|
|
1886
|
+
return failed === 0 ? 0 : 1;
|
|
1887
|
+
}
|
|
1888
|
+
function report(root, outcome, json) {
|
|
1889
|
+
if (json)
|
|
1890
|
+
return;
|
|
1891
|
+
if (outcome.removed)
|
|
1892
|
+
ok(`removed ${relative(root, outcome.path)}`);
|
|
1893
|
+
else
|
|
1894
|
+
problem(`kept ${relative(root, outcome.path)}`, outcome.error);
|
|
1895
|
+
}
|
|
1896
|
+
function print(root, entries, skipped, reclaimable, now) {
|
|
1897
|
+
if (entries.length === 0) {
|
|
1898
|
+
console.log("nothing to sweep");
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
const colors = paint(process.stdout);
|
|
1902
|
+
const path = role("path", process.stdout);
|
|
1903
|
+
const sized = (bytes) => {
|
|
1904
|
+
if (bytes >= BIG)
|
|
1905
|
+
return colors.red($e(bytes));
|
|
1906
|
+
return bytes >= LARGE ? colors.yellow($e(bytes)) : $e(bytes);
|
|
1907
|
+
};
|
|
1908
|
+
console.log(table([["size", "age", "what", "path", "note"], ...entries.map((entry) => [sized(entry.bytes), entry.committedAt === undefined ? colors.dim("-") : colors.dim(formatAge(entry.committedAt, now)), entry.kind, path(relative(root, entry.path)), entry.action === "keep" ? colors.dim(`kept: ${entry.reason}`) : ""])], { header: true, align: ["right", "right", "left", "left", "left"] }));
|
|
1909
|
+
console.log(`
|
|
1910
|
+
${$e(reclaimable)} reclaimable${skipped === 0 ? "" : `, ${skipped} skipped (symlinks, tracked or not ignored by git)`}`);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// src/sweep-cli.ts
|
|
1914
|
+
process15.exitCode = await runMain({ name: "sweep", run: sweep });
|