@kaynooo/cli-docker 0.2.0 → 0.4.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/dist/sf-cli.js ADDED
@@ -0,0 +1,1339 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/sf-cli.ts
5
+ import process11 from "process";
6
+
7
+ // ../kit/src/args.ts
8
+ import process4 from "process";
9
+ import { parseArgs } from "util";
10
+
11
+ // ../kit/src/style.ts
12
+ import process2 from "process";
13
+
14
+ // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
15
+ function x(s) {
16
+ let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
17
+ if (!/^[a-f0-9]+$/.test(r))
18
+ return [0, 0, 0];
19
+ let t = r.length, n = (c) => Number.parseInt(c, 16);
20
+ if (t === 3 || t === 4) {
21
+ 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;
22
+ return g !== undefined ? [c, u, h, g] : [c, u, h];
23
+ }
24
+ if (t !== 6 && t !== 8)
25
+ throw Error(`Invalid hex color length: ${t}`);
26
+ 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;
27
+ if ([a, b, o].some(Number.isNaN) || y !== undefined && Number.isNaN(y))
28
+ throw Error(`Invalid hex color value: ${s}`);
29
+ return y !== undefined ? [a, b, o, y] : [a, b, o];
30
+ }
31
+ function A(s) {
32
+ let [r, t, n, a] = s;
33
+ if (a)
34
+ return `#${(r << 24 | t << 16 | n << 8 | Math.round((a ?? 1) * 255)).toString(16).padStart(8, "0").toUpperCase()}`;
35
+ return `#${(r << 16 | t << 8 | n).toString(16).padStart(6, "0").toUpperCase()}`;
36
+ }
37
+ function R(s, r, t) {
38
+ s = (s % 360 + 360) % 360, t /= 100;
39
+ let n = r * Math.min(t, 1 - t) / 100, a = (b) => {
40
+ let o = (b + s / 30) % 12, y = t - n * Math.max(Math.min(o - 3, 9 - o, 1), -1);
41
+ return Math.round(255 * y);
42
+ };
43
+ return [a(0), a(8), a(4)];
44
+ }
45
+ function k(s) {
46
+ let [r, t, n] = s;
47
+ r /= 255, t /= 255, n /= 255;
48
+ let a = Math.max(r, t, n), b = Math.min(r, t, n), o = 0, y = 0, c = (a + b) / 2;
49
+ if (a !== b) {
50
+ let u = a - b;
51
+ if (y = c > 0 && c < 1 ? u / (1 - Math.abs(2 * c - 1)) : 0, a === r)
52
+ o = (t - n) / u + (t < n ? 6 : 0);
53
+ else if (a === t)
54
+ o = (n - r) / u + 2;
55
+ else
56
+ o = (r - t) / u + 4;
57
+ o /= 6;
58
+ }
59
+ return [Math.round(o * 360), Math.round(y * 100), Math.round(c * 100)];
60
+ }
61
+ function H(s, r, t) {
62
+ s = (s % 360 + 360) % 360, r /= 100, t /= 100;
63
+ let n = (a) => {
64
+ let b = (a + s / 60) % 6, o = t - t * r * Math.max(Math.min(b, 4 - b, 1), 0);
65
+ return Math.round(255 * o);
66
+ };
67
+ return [n(5), n(3), n(1)];
68
+ }
69
+ function f(s) {
70
+ let [r, t, n] = s;
71
+ r /= 255, t /= 255, n /= 255;
72
+ 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;
73
+ if (o !== 0) {
74
+ if (a === r)
75
+ y = (t - n) / o + (t < n ? 6 : 0);
76
+ else if (a === t)
77
+ y = (n - r) / o + 2;
78
+ else
79
+ y = (r - t) / o + 4;
80
+ y /= 6;
81
+ }
82
+ return [Math.round(y * 360), Math.round(c * 100), Math.round(u * 100)];
83
+ }
84
+ function O(s) {
85
+ let r = s[0] / 255, t = s[1] / 255, n = s[2] / 255, a = 1 - Math.max(r, t, n);
86
+ if (a === 1)
87
+ return [0, 0, 0, 100];
88
+ let b = (1 - r - a) / (1 - a), o = (1 - t - a) / (1 - a), y = (1 - n - a) / (1 - a);
89
+ return [Math.round(b * 100), Math.round(o * 100), Math.round(y * 100), Math.round(a * 100)];
90
+ }
91
+ function v(s, r, t, n) {
92
+ 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))];
93
+ }
94
+ var m = (s) => s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
95
+ var p = (s) => s <= 0.0031308 ? 12.92 * s : 1.055 * s ** 0.4166666666666667 - 0.055;
96
+ var i = (s, r) => Math.round(s * 10 ** r) / 10 ** r;
97
+ function T(s) {
98
+ 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);
99
+ 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)];
100
+ }
101
+ function M(s, r, t) {
102
+ 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)));
103
+ return [u(o), u(y), u(c)];
104
+ }
105
+ function d(s) {
106
+ let [r, t, n] = T(s), a = Math.sqrt(t * t + n * n), b = (Math.atan2(n, t) * 180 / Math.PI + 360) % 360;
107
+ return [i(r, 4), i(a, 4), i(b, 2)];
108
+ }
109
+ function C(s, r, t) {
110
+ let n = t * Math.PI / 180;
111
+ return M(s, r * Math.cos(n), r * Math.sin(n));
112
+ }
113
+ function l(s) {
114
+ let r = s / 255;
115
+ return r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055) ** 2.4;
116
+ }
117
+
118
+ class e {
119
+ r;
120
+ g;
121
+ b;
122
+ a;
123
+ constructor(s, r, t, n = 1) {
124
+ this.r = s, this.g = r, this.b = t, this.a = n;
125
+ }
126
+ static from(s, r) {
127
+ if (s === "hex") {
128
+ let [t, n, a, b] = x(r);
129
+ return new e(t, n, a, b);
130
+ }
131
+ if (s === "rgb") {
132
+ if (Array.isArray(r)) {
133
+ let [o, y, c, u] = r;
134
+ return new e(o, y, c, u);
135
+ }
136
+ let { r: t, g: n, b: a, a: b } = r;
137
+ return new e(t, n, a, b);
138
+ }
139
+ if (s === "hsl") {
140
+ let t, n, a, b;
141
+ if (Array.isArray(r))
142
+ [t, n, a, b] = r;
143
+ else
144
+ ({ h: t, s: n, l: a, a: b } = r);
145
+ let [o, y, c] = R(t, n, a);
146
+ return new e(o, y, c, b);
147
+ }
148
+ if (s === "hsv") {
149
+ let t, n, a, b;
150
+ if (Array.isArray(r))
151
+ [t, n, a, b] = r;
152
+ else
153
+ ({ h: t, s: n, v: a, a: b } = r);
154
+ let [o, y, c] = H(t, n, a);
155
+ return new e(o, y, c, b);
156
+ }
157
+ if (s === "cmyk") {
158
+ let t, n, a, b, o;
159
+ if (Array.isArray(r))
160
+ [t, n, a, b, o] = r;
161
+ else
162
+ ({ c: t, m: n, y: a, k: b, a: o } = r);
163
+ let [y, c, u] = v(t, n, a, b);
164
+ return new e(y, c, u, o);
165
+ }
166
+ if (s === "oklch") {
167
+ let t, n, a, b;
168
+ if (Array.isArray(r))
169
+ [t, n, a, b] = r;
170
+ else
171
+ ({ l: t, c: n, h: a, a: b } = r);
172
+ let [o, y, c] = C(t, n, a);
173
+ return new e(o, y, c, b);
174
+ }
175
+ if (s === "oklab") {
176
+ let t, n, a, b;
177
+ if (Array.isArray(r))
178
+ [t, n, a, b] = r;
179
+ else
180
+ ({ l: t, a: n, b: a, alpha: b } = r);
181
+ let [o, y, c] = M(t, n, a);
182
+ return new e(o, y, c, b);
183
+ }
184
+ throw Error("Invalid input");
185
+ }
186
+ to(s) {
187
+ switch (s) {
188
+ case "rgb":
189
+ return [this.r, this.g, this.b];
190
+ case "rgba":
191
+ return [this.r, this.g, this.b, this.a];
192
+ case "hex":
193
+ return A([this.r, this.g, this.b]);
194
+ case "hexa":
195
+ return A([this.r, this.g, this.b, this.a]);
196
+ case "hsl":
197
+ return k([this.r, this.g, this.b]);
198
+ case "hsla":
199
+ return [...k([this.r, this.g, this.b]), this.a];
200
+ case "hsv":
201
+ return f([this.r, this.g, this.b]);
202
+ case "hsva":
203
+ return [...f([this.r, this.g, this.b]), this.a];
204
+ case "cmyk":
205
+ return O([this.r, this.g, this.b]);
206
+ case "cmyka":
207
+ return [...O([this.r, this.g, this.b]), this.a];
208
+ case "oklch":
209
+ return d([this.r, this.g, this.b]);
210
+ case "oklcha":
211
+ return [...d([this.r, this.g, this.b]), this.a];
212
+ case "oklab":
213
+ return T([this.r, this.g, this.b]);
214
+ case "oklaba":
215
+ return [...T([this.r, this.g, this.b]), this.a];
216
+ default:
217
+ return [this.r, this.g, this.b];
218
+ }
219
+ }
220
+ getLuminance() {
221
+ let s = l(this.r), r = l(this.g), t = l(this.b);
222
+ return 0.2126 * s + 0.7152 * r + 0.0722 * t;
223
+ }
224
+ contrastRatio(s) {
225
+ let r = this.getLuminance(), t = s.getLuminance(), [n, a] = r >= t ? [r, t] : [t, r];
226
+ return (n + 0.05) / (a + 0.05);
227
+ }
228
+ bestContrastForeground() {
229
+ let s = new e(255, 255, 255, 1), r = new e(0, 0, 0, 1), t = this.contrastRatio(s), n = this.contrastRatio(r);
230
+ return t >= n ? s : r;
231
+ }
232
+ }
233
+
234
+ // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
235
+ var r = 39;
236
+ var o = 49;
237
+ var f2 = "\x1B[";
238
+ 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") => {
239
+ let [a, l, s] = e.from("hex", i).to("rgb");
240
+ return [`${n === "fg" ? 38 : 48};2;${a};${l};${s}`, n === "fg" ? r : o];
241
+ }, ansi256: (i, n = "fg") => [`${n === "fg" ? 38 : 48};5;${i}`, n === "fg" ? r : o] };
242
+ var b = new Set(["", "1", "2", "3", "true"]);
243
+ function h(i, n = globalThis.process?.env ?? {}) {
244
+ if (n.FORCE_COLOR !== undefined)
245
+ return b.has(n.FORCE_COLOR);
246
+ if (n.NO_COLOR !== undefined || n.NODE_DISABLE_COLORS !== undefined || n.TERM === "dumb")
247
+ return false;
248
+ return i?.isTTY === true;
249
+ }
250
+ function d2(i, n, a) {
251
+ if (!h(a.stream ?? globalThis.process?.stdout, a.env))
252
+ return n;
253
+ let l = "", s = "";
254
+ for (let g = 0, y = i.length;g < y; g++) {
255
+ let [c, p] = i[g];
256
+ l += `${f2}${c}m`, s = `${f2}${p}m${s}`;
257
+ }
258
+ return l + n + s;
259
+ }
260
+ function u(i, n = []) {
261
+ return new Proxy((l) => d2(n, l, i), { get(l, s) {
262
+ let g = C2[s];
263
+ if (Array.isArray(g))
264
+ return u(i, [...n, g]);
265
+ if (typeof g === "function")
266
+ return (...y) => u(i, [...n, g(...y)]);
267
+ throw Error(`Unknown style: ${String(s)}`);
268
+ } });
269
+ }
270
+ function le(i = {}) {
271
+ return u(i);
272
+ }
273
+ var t = le();
274
+
275
+ // ../kit/src/style.ts
276
+ function takesControlCodes(stream, env = process2.env) {
277
+ return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
278
+ }
279
+ function paint(stream, env = process2.env) {
280
+ return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
281
+ }
282
+ var STATUSES = {
283
+ passed: { symbol: "\u2713", words: ["passed", "success", "succeeded", "ok"], paint: (colors) => colors.green },
284
+ failed: { symbol: "\u2717", words: ["failed", "failure", "error", "errored"], paint: (colors) => colors.red },
285
+ running: { symbol: "\u25CF", words: ["running", "in_progress", "started"], paint: (colors) => colors.cyan },
286
+ queued: { symbol: "\u25CB", words: ["queued", "waiting", "pending", "created", "blocked", "scheduled"], paint: (colors) => colors.yellow },
287
+ skipped: { symbol: "-", words: ["skipped", "cancelled", "canceled", "manual"], paint: (colors) => colors.dim }
288
+ };
289
+ var STATUS_OF = new Map(Object.entries(STATUSES).flatMap(([status, { words }]) => words.map((word) => [word, status])));
290
+
291
+ // ../kit/src/format.ts
292
+ function upTo(text, separator) {
293
+ const end = text.indexOf(separator);
294
+ return end === -1 ? text : text.slice(0, end);
295
+ }
296
+ var ESCAPE = String.fromCharCode(27);
297
+ function printJson(value) {
298
+ console.log(JSON.stringify(value, undefined, 2));
299
+ }
300
+
301
+ // ../kit/src/help.ts
302
+ import process3 from "process";
303
+ var OPTIONAL = /\[[^\]]+\]/g;
304
+ var USAGE_PREFIX = /^usage:\s*/i;
305
+ var FULL_STOP = /\.$/;
306
+ function usageLine(usage, colors = paint(process3.stdout)) {
307
+ return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
308
+ }
309
+ function helpText(usage, options, colors = paint(process3.stdout), { examples = [], replaces } = {}) {
310
+ return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
311
+ `);
312
+ }
313
+ function replacesRows(replaces, colors = paint(process3.stdout)) {
314
+ return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
315
+ }
316
+ function wantsHelpJson(env = process3.env) {
317
+ return env.KIT_HELP === "json";
318
+ }
319
+ function description(usage, { examples = [], replaces } = {}) {
320
+ const lines = usage.replace(USAGE_PREFIX, "").split(`
321
+ `);
322
+ const first = lines[0];
323
+ const colon = first.indexOf(": ");
324
+ const prose = lines[lines.indexOf("") + 1] ?? "";
325
+ const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
326
+ return {
327
+ synopsis: colon === -1 ? first : first.slice(0, colon),
328
+ summary: colon === -1 ? fallback : first.slice(colon + 2),
329
+ ...replaces === undefined ? {} : { replaces },
330
+ examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
331
+ };
332
+ }
333
+ function exampleRows(examples, colors = paint(process3.stdout)) {
334
+ if (examples.length === 0)
335
+ return [];
336
+ return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
337
+ }
338
+ function optionRows(options, colors = paint(process3.stdout)) {
339
+ const rows = Object.entries(options).map(([name, option]) => {
340
+ const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
341
+ const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
342
+ const placeholder = option.optional === undefined ? named : `[${named}]`;
343
+ const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
344
+ return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
345
+ });
346
+ const width = Math.max(0, ...rows.map((row) => row.width));
347
+ return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
348
+ }
349
+
350
+ // ../kit/src/text.ts
351
+ var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
352
+ function stripAnsi(text) {
353
+ return text.replace(ANSI, "");
354
+ }
355
+ function distance(a, b) {
356
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
357
+ for (const [i, left] of [...a].entries()) {
358
+ const current = [i + 1];
359
+ for (const [j, right] of [...b].entries())
360
+ current.push(Math.min((previous[j + 1] ?? 0) + 1, (current[j] ?? 0) + 1, (previous[j] ?? 0) + (left === right ? 0 : 1)));
361
+ previous = current;
362
+ }
363
+ return previous[b.length] ?? 0;
364
+ }
365
+ function closest(name, names) {
366
+ const started = names.find((known) => known.startsWith(name));
367
+ if (started !== undefined)
368
+ return started;
369
+ const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
370
+ return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
371
+ }
372
+
373
+ // ../kit/src/args.ts
374
+ var DASHES = /^-+/;
375
+ function jsonOption(holds) {
376
+ return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
377
+ }
378
+
379
+ class Exit extends Error {
380
+ code;
381
+ hint;
382
+ constructor(message, code, hint) {
383
+ super(message);
384
+ this.code = code;
385
+ this.hint = hint;
386
+ }
387
+ }
388
+ function fail(message, code = 2, hint) {
389
+ throw new Exit(message, code, hint);
390
+ }
391
+
392
+ class UsageError extends Exit {
393
+ constructor(message, hint) {
394
+ super(message, 2, hint);
395
+ }
396
+ }
397
+ function badUsage(message) {
398
+ throw new UsageError(message);
399
+ }
400
+
401
+ class HelpShown extends Exit {
402
+ constructor() {
403
+ super("help shown", 0);
404
+ }
405
+ }
406
+
407
+ class Completing {
408
+ options;
409
+ positionals;
410
+ candidates;
411
+ constructor(options, positionals = [], candidates = {}) {
412
+ this.options = options;
413
+ this.positionals = positionals;
414
+ this.candidates = candidates;
415
+ }
416
+ }
417
+ var completion = { active: false };
418
+ var OUTPUT_OPTIONS = {
419
+ color: { type: "string", description: "auto (on a terminal alone), always or never" },
420
+ plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
421
+ };
422
+ function applyOutput(color, plain, env = process4.env) {
423
+ const when = plain === true ? "never" : color ?? "auto";
424
+ if (when === "auto")
425
+ return;
426
+ if (when !== "always" && when !== "never")
427
+ badUsage(`--color takes auto, always or never, not ${when}`);
428
+ if (when === "always") {
429
+ env.FORCE_COLOR = "1";
430
+ delete env.NO_COLOR;
431
+ } else {
432
+ env.NO_COLOR = "1";
433
+ delete env.FORCE_COLOR;
434
+ }
435
+ }
436
+ function parse(args, usage, options, settings = {}) {
437
+ const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
438
+ const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
439
+ const listing = listed(all, settings.allowNegative === true);
440
+ if (completion.active)
441
+ throw new Completing(listing, settings.positionals, settings.candidates);
442
+ const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
443
+ let parsed;
444
+ try {
445
+ parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
446
+ } catch (error) {
447
+ const message = parseError(error);
448
+ throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
449
+ }
450
+ const values = parsed.values;
451
+ applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
452
+ if (values.help === true) {
453
+ console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
454
+ throw new HelpShown;
455
+ }
456
+ checkChoices(parsed.values, all);
457
+ checkConflicts(parsed.values, all);
458
+ if (settings.positionals !== undefined)
459
+ checkPositionals(parsed.positionals, settings.positionals);
460
+ return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
461
+ }
462
+ function splitRest(args, passes) {
463
+ const separator = passes ? args.indexOf("--") : -1;
464
+ return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
465
+ }
466
+ function splitTrailing(args, options, count) {
467
+ const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
468
+ let seen = 0;
469
+ if (count === 0)
470
+ return { own: args, rest: undefined };
471
+ for (let at = 0;at < args.length; at++) {
472
+ const word = args[at];
473
+ if (word === "--")
474
+ break;
475
+ if (word.startsWith("-")) {
476
+ if (takesValue(word))
477
+ at++;
478
+ continue;
479
+ }
480
+ seen++;
481
+ if (seen === count)
482
+ return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
483
+ }
484
+ return { own: args, rest: undefined };
485
+ }
486
+ function listed(options, allowNegative) {
487
+ if (!allowNegative)
488
+ return options;
489
+ return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
490
+ if (option.type !== "boolean" || option.negative === undefined)
491
+ return [[name, option]];
492
+ return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
493
+ }));
494
+ }
495
+ function withBare(args, options) {
496
+ const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
497
+ if (bare.size === 0)
498
+ return args;
499
+ const separator = args.indexOf("--");
500
+ const own = separator === -1 ? args : args.slice(0, separator);
501
+ return [...own.flatMap((arg, index) => {
502
+ const optional = bare.get(arg);
503
+ const next = own[index + 1];
504
+ const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
505
+ return optional === undefined || value ? [arg] : [arg, optional.bare];
506
+ }), ...separator === -1 ? [] : args.slice(separator)];
507
+ }
508
+ function checkConflicts(values, options) {
509
+ const given = (name) => values[name] !== undefined && values[name] !== false;
510
+ for (const [name, { conflicts = [] }] of Object.entries(options)) {
511
+ const other = given(name) ? conflicts.find(given) : undefined;
512
+ if (other !== undefined)
513
+ badUsage(`--${name} cannot be used with --${other}`);
514
+ }
515
+ }
516
+ function checkChoices(values, options) {
517
+ for (const [name, option] of Object.entries(options)) {
518
+ if (option.choices === undefined)
519
+ continue;
520
+ const given = values[name];
521
+ for (const value of Array.isArray(given) ? given : [given]) {
522
+ if (typeof value === "string")
523
+ oneOf(value, name, option.choices);
524
+ }
525
+ }
526
+ }
527
+ function oneOf(value, name, choices) {
528
+ if (!choices.includes(value))
529
+ badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
530
+ return value;
531
+ }
532
+ function checkPositionals(given, names) {
533
+ const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
534
+ const required = names.filter((name) => !name.startsWith("[")).length;
535
+ if (given.length < required)
536
+ badUsage(`missing ${names[given.length]}`);
537
+ if (!variadic && given.length > names.length)
538
+ badUsage(`unexpected argument ${given[names.length]}`);
539
+ }
540
+ function joinDashValues(args, options) {
541
+ const longOf = new Map(Object.entries(options).flatMap(([name, option]) => option.short === undefined ? [[`--${name}`, name]] : [[`--${name}`, name], [`-${option.short}`, name]]));
542
+ const isOption = (arg) => arg === "--" || longOf.has(arg.includes("=") ? arg.slice(0, arg.indexOf("=")) : arg);
543
+ const joined = [];
544
+ for (let i = 0;i < args.length; i++) {
545
+ const arg = args[i] ?? "";
546
+ if (arg === "--")
547
+ return [...joined, ...args.slice(i)];
548
+ const name = longOf.get(arg);
549
+ const next = args[i + 1];
550
+ if (name !== undefined && options[name]?.type === "string" && next !== undefined && next.startsWith("-") && next !== "-" && !isOption(next)) {
551
+ joined.push(`--${name}=${next}`);
552
+ i++;
553
+ } else {
554
+ joined.push(arg);
555
+ }
556
+ }
557
+ return joined;
558
+ }
559
+ function parseError(error) {
560
+ const message = upTo(error instanceof Error ? error.message : String(error), ". ");
561
+ return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
562
+ }
563
+ var UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
564
+ function nearestOption(message, options, allowNegative) {
565
+ const typed = UNKNOWN_OPTION.exec(message)?.[1];
566
+ if (typed === undefined)
567
+ return;
568
+ const word = typed.replace(DASHES, "");
569
+ if (word.length < 2 && !typed.startsWith("--"))
570
+ return;
571
+ const known = Object.entries(options).flatMap(([name, option]) => [
572
+ ...option.short === undefined ? [] : [`-${option.short}`],
573
+ `--${name}`,
574
+ ...allowNegative && option.type === "boolean" ? [`--no-${name}`] : []
575
+ ]);
576
+ const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
577
+ return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
578
+ }
579
+ // ../kit/src/progress.ts
580
+ var ESC = String.fromCharCode(27);
581
+ var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
582
+ var RESET_STYLE = `${ESC}[0m`;
583
+ var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
584
+ // ../kit/src/cli.ts
585
+ import process6 from "process";
586
+
587
+ // ../kit/src/usage.ts
588
+ import { appendFileSync, mkdirSync } from "fs";
589
+ import { homedir } from "os";
590
+ import { dirname, isAbsolute, join } from "path";
591
+ import process5 from "process";
592
+
593
+ // ../kit/src/logs.ts
594
+ var DAY_MS = 24 * 60 * 60 * 1000;
595
+ var CLI_FOLDER = "kaynooo-cli";
596
+
597
+ // ../kit/src/usage.ts
598
+ var UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
599
+ var FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
600
+ var LONGEST_NAME = 40;
601
+ var usageError;
602
+ function noteUsageError(message) {
603
+ usageError = message;
604
+ }
605
+ function stateHome(env = process5.env) {
606
+ const dir = env.XDG_STATE_HOME;
607
+ return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
608
+ }
609
+ function usagePath(env = process5.env) {
610
+ return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
611
+ }
612
+ function usageRecorded(env = process5.env) {
613
+ const off = env.KAYNOOO_CLI_NO_USAGE;
614
+ return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
615
+ }
616
+ function flagNames(argv) {
617
+ const end = argv.indexOf("--");
618
+ const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
619
+ return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
620
+ }
621
+ function usageEntry(bin, command, argv, code, now = new Date) {
622
+ const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
623
+ usageError = undefined;
624
+ return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
625
+ }
626
+ function recordUsage(entry, env = process5.env) {
627
+ if (!usageRecorded(env))
628
+ return;
629
+ try {
630
+ const path = usagePath(env);
631
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
632
+ appendFileSync(path, `${JSON.stringify(entry)}
633
+ `, { mode: 384 });
634
+ } catch {}
635
+ }
636
+
637
+ // ../kit/src/cli.ts
638
+ var LEVEL_OPTIONS = {
639
+ ...OUTPUT_OPTIONS,
640
+ help: { type: "boolean", short: "h", description: "show this help" }
641
+ };
642
+ var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
643
+ function printError(prefix, message, hint, cause) {
644
+ const colors = paint(process6.stderr);
645
+ const styled = takesControlCodes(process6.stderr);
646
+ const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
647
+ const under = (label, text, tint) => text === undefined ? "" : `
648
+ ${styled ? tint(label) : `${label}:`} ${text}`;
649
+ console.error(`${head} ${message}${under("cause", cause, colors.dim)}${under("hint", hint, colors.yellow)}`);
650
+ }
651
+ function printJsonError(message, hint, cause) {
652
+ console.error(JSON.stringify({ error: message, ...hint === undefined ? {} : { hint }, ...cause === undefined ? {} : { cause } }));
653
+ }
654
+ function wantsJson(argv) {
655
+ const end = argv.indexOf("--");
656
+ return (end === -1 ? argv : argv.slice(0, end)).includes("--json");
657
+ }
658
+ function reportThrown(prefix, error, redact = (text) => text, json = false) {
659
+ if (error instanceof HelpShown)
660
+ return 0;
661
+ const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
662
+ const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
663
+ const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
664
+ if (error instanceof UsageError)
665
+ noteUsageError(error.message);
666
+ const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
667
+ report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
668
+ return error instanceof Exit ? error.code : 1;
669
+ }
670
+ var COMPLETE = "__complete";
671
+ var FILES = "__files__";
672
+ async function completingOf(run) {
673
+ completion.active = true;
674
+ try {
675
+ await run([]);
676
+ } catch (error) {
677
+ if (error instanceof Completing)
678
+ return error;
679
+ } finally {
680
+ completion.active = false;
681
+ }
682
+ return new Completing({});
683
+ }
684
+ function candidateLine(candidate) {
685
+ return typeof candidate === "string" ? candidate : candidate.join("\t");
686
+ }
687
+ function positionalBeingTyped(before, { options, positionals }) {
688
+ const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
689
+ let index = 0;
690
+ let at = 0;
691
+ while (at < before.length) {
692
+ const word = before[at];
693
+ if (word === "--") {
694
+ index += before.length - at - 1;
695
+ break;
696
+ }
697
+ if (!word.startsWith("-"))
698
+ index++;
699
+ else if (takesValue(word))
700
+ at++;
701
+ at++;
702
+ }
703
+ const last = positionals.at(-1);
704
+ if (index < positionals.length)
705
+ return positionals[index];
706
+ return last?.replace("]", "").endsWith("...") === true ? last : undefined;
707
+ }
708
+ async function leafCandidates(run, words) {
709
+ const completing = await completingOf(run);
710
+ if ((words.at(-1) ?? "").startsWith("-"))
711
+ return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
712
+ const flag = words.at(-2);
713
+ const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
714
+ if (option?.type === "string") {
715
+ const values = option.choices === undefined ? await option.candidates?.() : option.choices;
716
+ return values === undefined ? [FILES] : values.map(candidateLine);
717
+ }
718
+ const name = positionalBeingTyped(words.slice(0, -1), completing);
719
+ const values = name === undefined ? undefined : await completing.candidates[name]?.();
720
+ return values === undefined ? [FILES] : values.map(candidateLine);
721
+ }
722
+ async function printCandidates(found) {
723
+ for (const line of await found)
724
+ console.log(line);
725
+ return 0;
726
+ }
727
+ async function runMain({ name, version, run, redact, usage }, argv = process6.argv.slice(2)) {
728
+ if (argv[0] === COMPLETE)
729
+ return printCandidates(leafCandidates(run, argv.slice(1)));
730
+ if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
731
+ console.log(version);
732
+ return 0;
733
+ }
734
+ let code;
735
+ try {
736
+ code = await run(argv);
737
+ } catch (error) {
738
+ code = reportThrown(name, error, redact, wantsJson(argv));
739
+ }
740
+ if (usage !== false)
741
+ recordUsage(usageEntry(name, undefined, argv, code));
742
+ return code;
743
+ }
744
+ // ../kit/src/secrets.ts
745
+ var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
746
+ // ../kit/src/sessions.ts
747
+ var {Glob } = globalThis.Bun;
748
+ var SESSION_FILES = new Glob("*.json");
749
+ var ACCOUNT_DIRS = new Glob("*/sessions");
750
+ // package.json
751
+ var version = "0.4.0";
752
+
753
+ // src/sf.ts
754
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
755
+ import { homedir as homedir3 } from "os";
756
+ import { dirname as dirname4, join as join4, resolve as resolve2 } from "path";
757
+ import process10 from "process";
758
+
759
+ // ../kit/src/fzf.ts
760
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
761
+ import { homedir as homedir2 } from "os";
762
+ import { dirname as dirname2, join as join2 } from "path";
763
+ import process8 from "process";
764
+ var REMEMBERED = 20;
765
+ function table(rows) {
766
+ const widths = rows[0]?.cells.map((_, index) => Math.max(...rows.map((row) => Bun.stringWidth(row.cells[index])))) ?? [];
767
+ return rows.map((row) => [
768
+ ...row.hidden,
769
+ ...row.cells.map((cell, index) => index === row.cells.length - 1 ? cell.trimEnd() : `${cell}${" ".repeat(widths[index] + 1 - Bun.stringWidth(cell))}`)
770
+ ].join("\t"));
771
+ }
772
+ function fzf(rows, options) {
773
+ const hidden = options.hidden ?? rows[0]?.hidden.length ?? 0;
774
+ const proc = Bun.spawnSync([
775
+ "fzf",
776
+ "--prompt",
777
+ options.prompt,
778
+ ...options.header === undefined ? [] : ["--header", options.header],
779
+ ...(options.expect ?? []).length > 0 ? ["--expect", (options.expect ?? []).join(",")] : [],
780
+ "--ansi",
781
+ "--reverse",
782
+ "--height",
783
+ options.height ?? "100%",
784
+ "--delimiter",
785
+ "\t",
786
+ "--with-nth",
787
+ `${hidden + 1}..`,
788
+ "--nth",
789
+ `1..${options.searched}`,
790
+ "--tabstop",
791
+ "1",
792
+ "--tiebreak",
793
+ "chunk,index",
794
+ ...options.preview === undefined ? [] : ["--preview", options.preview, "--preview-window", "down,40%"],
795
+ ...(options.bind ?? []).flatMap((bind) => ["--bind", bind]),
796
+ ...options.query === true ? ["--print-query"] : []
797
+ ], {
798
+ stdin: new TextEncoder().encode(table(rows).join(`
799
+ `)),
800
+ stdout: "pipe",
801
+ stderr: "inherit"
802
+ });
803
+ return picked(proc.stdout.toString(), hidden, options);
804
+ }
805
+ function picked(output, hidden, options) {
806
+ const lines = output.split(`
807
+ `);
808
+ const query = options.query === true ? lines.shift() : "";
809
+ const [key, line] = (options.expect ?? []).length > 0 ? [lines[0], lines[1]] : ["", lines[0]];
810
+ if (line !== undefined && line !== "")
811
+ return { key, hidden: line.split("\t").slice(0, hidden), query };
812
+ return options.query === true && key !== "" ? { key, hidden: [], query } : undefined;
813
+ }
814
+ function heightFor(count) {
815
+ return String(Math.max(15, Math.ceil((count + 4) / 0.6)));
816
+ }
817
+ function historyFile(name) {
818
+ return join2(process8.env.XDG_CACHE_HOME ?? join2(homedir2(), ".cache"), name, "history.json");
819
+ }
820
+ function history(name) {
821
+ try {
822
+ const file = historyFile(name);
823
+ return existsSync(file) ? JSON.parse(readFileSync(file, "utf-8")) : {};
824
+ } catch {
825
+ return {};
826
+ }
827
+ }
828
+ function runs(name) {
829
+ return history(name)[process8.cwd()] ?? [];
830
+ }
831
+ function record(name, run) {
832
+ const all = history(name);
833
+ const file = historyFile(name);
834
+ all[process8.cwd()] = [run, ...(all[process8.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
835
+ mkdirSync2(dirname2(file), { recursive: true });
836
+ writeFileSync(file, JSON.stringify(all));
837
+ }
838
+ function byRecency(items, previous, idOf) {
839
+ const rank = new Map(previous.map((run, index) => [run.id, index]));
840
+ const recent = items.filter((item) => rank.has(idOf(item))).sort((a, b) => rank.get(idOf(a)) - rank.get(idOf(b)));
841
+ return [...recent, ...items.filter((item) => !rank.has(idOf(item)))];
842
+ }
843
+ function word(arg) {
844
+ if (typeof arg === "string")
845
+ return `=${arg}`;
846
+ return "given" in arg ? `+${arg.given}` : `?${arg.ask}`;
847
+ }
848
+ function emit(mode, typed, recorded, command) {
849
+ const words = command.map(word);
850
+ console.log([mode, typed, recorded, ...words].join(`
851
+ `));
852
+ }
853
+ function list(rows) {
854
+ console.log(JSON.stringify(rows));
855
+ }
856
+
857
+ // ../kit/src/prompt.ts
858
+ import process9 from "process";
859
+ var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
860
+ var CONTROL = /^[\x00-\x1F\x7F]/;
861
+ var KEYS = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
862
+ function readKeys(input) {
863
+ return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL.test(sequence) ? [] : [{ text: sequence }]));
864
+ }
865
+ async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
866
+ let value = "";
867
+ let invalid;
868
+ const taken = () => value === "" ? fallback ?? "" : value;
869
+ const shown = () => mask ? "*".repeat([...taken()].length) : taken();
870
+ const accepted = await ask(question, settings, (colors, state, { columns }) => {
871
+ if (state !== "asking")
872
+ return [answer(colors, question, state, shown())];
873
+ const characters = [...shown()];
874
+ const room = Math.max(columns - question.length - 5, 1);
875
+ const tail = characters.length > room ? `\u2026${characters.slice(1 - room).join("")}` : shown();
876
+ const typed = value === "" ? colors.dim(tail) : tail;
877
+ return [`${colors.cyan("?")} ${question} ${typed}${colors.dim("\u2588")}`, ...invalid === undefined ? [] : [` ${colors.red(invalid)}`]];
878
+ }, (key) => {
879
+ if (key === "cancel")
880
+ return "cancel";
881
+ invalid = undefined;
882
+ if (key === "enter") {
883
+ invalid = validate?.(taken());
884
+ return invalid === undefined ? "enter" : undefined;
885
+ }
886
+ if (key === "erase")
887
+ value = [...value].slice(0, -1).join("");
888
+ else if (typeof key === "object")
889
+ value += key.text;
890
+ return;
891
+ });
892
+ return accepted ? taken() : undefined;
893
+ }
894
+ function answer(colors, question, state, picked) {
895
+ return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
896
+ }
897
+ async function ask(question, { hint, terminal = process9 }, render, press) {
898
+ const { stdin, stderr } = terminal;
899
+ if (stdin.isTTY !== true || stderr.isTTY !== true)
900
+ fail(`cannot ask "${question}" off a terminal`, 2, hint);
901
+ const colors = paint(stderr);
902
+ let drawn = 0;
903
+ let outcome;
904
+ const draw = (state) => {
905
+ const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
906
+ stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
907
+ `)}`);
908
+ drawn = frame.length;
909
+ };
910
+ try {
911
+ stdin.setRawMode(true);
912
+ stdin.resume();
913
+ stderr.write("\x1B[?25l");
914
+ draw("asking");
915
+ return await new Promise((resolve) => {
916
+ function onData(data) {
917
+ for (const key of readKeys(data.toString())) {
918
+ const pressed = press(key);
919
+ if (pressed !== undefined) {
920
+ settle(pressed);
921
+ return;
922
+ }
923
+ }
924
+ draw("asking");
925
+ }
926
+ function onEnd() {
927
+ settle("cancel");
928
+ }
929
+ function settle(settled) {
930
+ outcome = settled;
931
+ stdin.off("data", onData);
932
+ stdin.off("end", onEnd);
933
+ resolve(settled === "enter");
934
+ }
935
+ stdin.on("data", onData);
936
+ stdin.on("end", onEnd);
937
+ });
938
+ } finally {
939
+ draw(outcome ?? "cancel");
940
+ stderr.write(`
941
+ \x1B[?25h`);
942
+ stdin.setRawMode(false);
943
+ stdin.pause();
944
+ }
945
+ }
946
+
947
+ // src/stack.ts
948
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
949
+ import { dirname as dirname3, join as join3, relative, resolve } from "path";
950
+ var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
951
+ var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
952
+ var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
953
+ var SKIPPED = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
954
+ var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
955
+ function from(dir, command) {
956
+ return dir === "." ? command : ["env", "-C", dir, ...command];
957
+ }
958
+ function children(dir = ".") {
959
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED.has(child.name)).map((child) => child.name).sort();
960
+ }
961
+ function up(dir) {
962
+ const dirs = [];
963
+ for (let current = dir;; current = join3(current, "..")) {
964
+ dirs.push(current);
965
+ const absolute = resolve(current);
966
+ if (existsSync2(join3(current, ".git")) || dirname3(absolute) === absolute)
967
+ return dirs;
968
+ }
969
+ }
970
+ function stackAt(dir) {
971
+ return { dir, run: from(dir, ["docker", "compose"]), list: { argv: ["docker", "compose"], cwd: resolve(dir), env: process.env } };
972
+ }
973
+ function holdsStack(dir) {
974
+ if (COMPOSE_FILES.some((name) => existsSync2(join3(dir, name))))
975
+ return true;
976
+ try {
977
+ return COMPOSE_FILE_VAR.test(readFileSync2(join3(dir, ".env"), "utf-8"));
978
+ } catch {
979
+ return false;
980
+ }
981
+ }
982
+ function nearestStack(dir) {
983
+ const found = up(dir).find(holdsStack);
984
+ return found === undefined ? undefined : stackAt(found);
985
+ }
986
+ function stacks() {
987
+ const here = nearestStack(".");
988
+ const below = children().filter(holdsStack);
989
+ return [...here ? [here] : [], ...below.filter((dir) => dir !== here?.dir).map(stackAt)];
990
+ }
991
+ function labelled(text) {
992
+ const projects = new Map;
993
+ for (const line of text.split(`
994
+ `)) {
995
+ const [project, files, envFiles = "", workdir = ""] = line.split("\t");
996
+ if (project !== undefined && project !== "" && files !== undefined && files !== "" && workdir !== "" && !projects.has(project))
997
+ projects.set(project, { project, files: files.split(","), envFiles: envFiles.split(",").filter(Boolean), workdir });
998
+ }
999
+ return [...projects.values()];
1000
+ }
1001
+ async function labels(project) {
1002
+ const filter = project === undefined ? [] : ["--filter", `label=com.docker.compose.project=${project}`];
1003
+ const proc = Bun.spawn(["docker", "ps", "--all", ...filter, "--format", LABEL_FORMAT], { stdout: "pipe", stderr: "ignore" });
1004
+ if (await proc.exited !== 0)
1005
+ return [];
1006
+ return labelled(await new Response(proc.stdout).text()).filter((stack) => [...stack.files, ...stack.envFiles].every((file) => existsSync2(file)));
1007
+ }
1008
+ function labelledStack(dir, { project, files, envFiles, workdir }) {
1009
+ const options = (paths) => ["-p", project, ...files.flatMap((file) => ["-f", paths(file)]), ...envFiles.flatMap((file) => ["--env-file", paths(file)])];
1010
+ return {
1011
+ dir,
1012
+ project,
1013
+ run: from(dir, ["docker", "compose", ...options((file) => relative(workdir, file))]),
1014
+ list: { argv: ["docker", "compose", ...options((file) => file)], cwd: workdir, env: process.env }
1015
+ };
1016
+ }
1017
+ function stackOf(recorded) {
1018
+ return labelledStack(relative(".", recorded.workdir) || ".", recorded);
1019
+ }
1020
+ async function recordedNearby() {
1021
+ const dirs = new Set([...up("."), ...children()].map((dir) => resolve(dir)));
1022
+ return (await labels()).filter((stack) => dirs.has(stack.workdir));
1023
+ }
1024
+ async function attempt(stack, args) {
1025
+ const proc = Bun.spawn([...stack.list.argv, ...args], { cwd: stack.list.cwd, env: stack.list.env, stdout: "pipe", stderr: "pipe" });
1026
+ const [text, stderr, code] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
1027
+ if (code === 0)
1028
+ return { text, error: undefined };
1029
+ const last = stderr.trim().split(`
1030
+ `).at(-1) ?? "";
1031
+ return { text: "", error: last === "" ? `exit ${code}` : last };
1032
+ }
1033
+ async function output(stack, args) {
1034
+ return (await attempt(stack, args)).text;
1035
+ }
1036
+ function containers(text) {
1037
+ const trimmed = text.trim();
1038
+ if (!trimmed)
1039
+ return [];
1040
+ return trimmed.startsWith("[") ? JSON.parse(trimmed) : trimmed.split(`
1041
+ `).map((line) => JSON.parse(line));
1042
+ }
1043
+ async function services(stack) {
1044
+ const [config, ps] = await Promise.all([output(stack, ["config", "--format", "json"]), output(stack, ["ps", "--all", "--format", "json"])]);
1045
+ const parsed = config ? JSON.parse(config) : {};
1046
+ const declared = parsed.services ?? {};
1047
+ const project = stack.project ?? parsed.name ?? "";
1048
+ const running = new Map(containers(ps).map((container) => [container.Service, container]));
1049
+ return Object.entries(declared).map(([name, service]) => ({
1050
+ stack,
1051
+ project,
1052
+ name,
1053
+ state: running.get(name)?.State ?? "none",
1054
+ status: running.get(name)?.Status ?? "",
1055
+ ports: (service.ports ?? []).filter((port) => (port.published ?? "") !== "").map((port) => `${port.published}->${port.target}`).join(" "),
1056
+ image: service.image ?? "built"
1057
+ }));
1058
+ }
1059
+ async function allServices() {
1060
+ const [listed, recorded] = await Promise.all([Promise.all(stacks().map(services)), recordedNearby()]);
1061
+ const found = listed.flat();
1062
+ const known = new Set(found.map((service) => service.project));
1063
+ const others = recorded.filter((stack) => !known.has(stack.project));
1064
+ return [...found, ...(await Promise.all(others.map(async (stack) => services(stackOf(stack))))).flat()];
1065
+ }
1066
+ function named(service) {
1067
+ return service.stack.project === undefined ? service.name : `${service.stack.project}:${service.name}`;
1068
+ }
1069
+ function key(service) {
1070
+ return service.stack.dir === "." ? named(service) : `${service.stack.dir}/${named(service)}`;
1071
+ }
1072
+ async function shellOf(service) {
1073
+ return await output(service.stack, ["exec", "-T", service.name, "sh", "-c", "command -v bash"]) ? "bash" : "sh";
1074
+ }
1075
+ async function commandFor(service, action) {
1076
+ const run = service.stack.run;
1077
+ switch (action) {
1078
+ case "shell":
1079
+ return [...run, "exec", service.name, await shellOf(service)];
1080
+ case "logs":
1081
+ return [...run, "logs", "-f", "--tail=200", service.name];
1082
+ case "restart":
1083
+ return [...run, "restart", service.name];
1084
+ case "up":
1085
+ return [...run, "up", "-d", service.name];
1086
+ case "stop":
1087
+ return [...run, "stop", service.name];
1088
+ case "build":
1089
+ return [...run, "up", "-d", "--build", service.name];
1090
+ }
1091
+ }
1092
+ function defaultAction(service) {
1093
+ return service.state === "running" ? "shell" : "up";
1094
+ }
1095
+ function stateColor(state) {
1096
+ if (state === "running")
1097
+ return "green";
1098
+ if (state === "exited" || state === "dead")
1099
+ return "red";
1100
+ return state === "none" ? "dim" : "yellow";
1101
+ }
1102
+ async function serviceNamed(dir, name, project) {
1103
+ const [recorded] = project === "" ? [] : await labels(project);
1104
+ const stack = recorded === undefined ? stackAt(dir) : labelledStack(dir, recorded);
1105
+ return (await services(stack)).find((candidate) => candidate.name === name);
1106
+ }
1107
+ async function logsOf(stack, name) {
1108
+ return output(stack, ["logs", "--tail=30", "--no-log-prefix", name]);
1109
+ }
1110
+
1111
+ // src/sf.ts
1112
+ var usage2 = `sf [command] [args...]
1113
+ sf -
1114
+
1115
+ Picks a Symfony console command with fzf and runs it, asking for each required argument it needs. The commands last run come first, per folder, and the picker opens on the list cached from the last run while the console answers.
1116
+ The console is the nearest bin/console up to the repository root, then those of the child folders (keyed \`api/cache:clear\`). It runs in the php service of the nearest compose stack when there is one, else with the local php; the container's working directory is taken to be the app.
1117
+ \`sf -\` runs the last command again with the arguments it ran with, \`sf <command> [args...]\` runs without the picker: a name the list does not hold goes to the console as is, which resolves \`c:c\` to \`cache:clear\` itself. Without fzf or a terminal it lists the commands instead.
1118
+ The hidden flags --list, --names, --rows, --show, --record and --emit are for term, tmux and the shell completions.
1119
+
1120
+ Exits with the code of the console, 1 when there is no bin/console here.`;
1121
+ var colors = paint({ isTTY: true });
1122
+ var MARKUP = /<(info|comment|question|error)>(.*?)<\/\1>/gs;
1123
+ var TAGS = { info: "green", comment: "yellow", question: "cyan", error: "red" };
1124
+ var LINKS = /<\/?href[^>]*>/g;
1125
+ function marked2(text) {
1126
+ return text.replace(MARKUP, (_, tag, inner) => colors[TAGS[tag]](inner)).replace(LINKS, "");
1127
+ }
1128
+ function argumentsOf(command) {
1129
+ return Array.isArray(command.definition.arguments) ? [] : Object.values(command.definition.arguments);
1130
+ }
1131
+ function optionsOf(command) {
1132
+ return Array.isArray(command.definition.options) ? [] : Object.values(command.definition.options);
1133
+ }
1134
+ function phpService(names) {
1135
+ return names.find((name) => name === "php") ?? names.find((name) => name.includes("php"));
1136
+ }
1137
+ async function consoleAt(dir) {
1138
+ const stack = nearestStack(dir);
1139
+ const php = stack === undefined ? undefined : phpService((await services(stack)).map((service) => service.name));
1140
+ if (stack !== undefined && php !== undefined)
1141
+ return { dir, run: [...stack.run, "exec", php, "bin/console"], list: { ...stack.list, argv: [...stack.list.argv, "exec", "-T", php, "bin/console"] } };
1142
+ return { dir, run: from(dir, ["php", "bin/console"]), list: { argv: ["php", "bin/console"], cwd: resolve2(dir), env: process10.env } };
1143
+ }
1144
+ async function consoles() {
1145
+ const here = up(".").find((dir) => existsSync3(join4(dir, "bin/console")));
1146
+ const dirs = [...here === undefined ? [] : [here], ...children().filter((dir) => existsSync3(join4(dir, "bin/console")))];
1147
+ return Promise.all([...new Set(dirs)].map(consoleAt));
1148
+ }
1149
+ function cached(dir) {
1150
+ return join4(process10.env.XDG_CACHE_HOME ?? join4(homedir3(), ".cache"), "sf", `${resolve2(dir).replaceAll("/", "%")}.json`);
1151
+ }
1152
+ function fromCache(dir) {
1153
+ try {
1154
+ return existsSync3(cached(dir)) ? JSON.parse(readFileSync3(cached(dir), "utf-8")) : undefined;
1155
+ } catch {
1156
+ return;
1157
+ }
1158
+ }
1159
+ async function live(app) {
1160
+ const proc = Bun.spawn([...app.list.argv, "list", "--format=json"], { cwd: app.list.cwd, env: app.list.env, stdout: "pipe", stderr: "pipe" });
1161
+ const text = await new Response(proc.stdout).text();
1162
+ if (await proc.exited !== 0)
1163
+ return fromCache(app.dir) ?? [];
1164
+ const commands = JSON.parse(text).commands.filter((command) => !command.hidden);
1165
+ mkdirSync3(dirname4(cached(app.dir)), { recursive: true });
1166
+ writeFileSync2(cached(app.dir), JSON.stringify(commands));
1167
+ return commands;
1168
+ }
1169
+ async function entries(fresh) {
1170
+ const found = await consoles();
1171
+ const lists = await Promise.all(found.map(async (app) => fresh ? live(app) : fromCache(app.dir) ?? live(app)));
1172
+ return found.flatMap((app, index) => lists[index].map((command) => ({ app, command })));
1173
+ }
1174
+ function key2(entry) {
1175
+ return entry.app.dir === "." ? entry.command.name : `${entry.app.dir}/${entry.command.name}`;
1176
+ }
1177
+ function named2(name) {
1178
+ const colon = name.lastIndexOf(":");
1179
+ return colon === -1 ? name : `${colors.dim(name.slice(0, colon + 1))}${name.slice(colon + 1)}`;
1180
+ }
1181
+ function signature(command) {
1182
+ return argumentsOf(command).map((argument) => `${argument.is_required ? "<" : "["}${argument.name}${argument.is_array ? "..." : ""}${argument.is_required ? ">" : "]"}`).join(" ");
1183
+ }
1184
+ function rows(found) {
1185
+ return byRecency(found, runs("sf"), key2).map((entry) => ({
1186
+ hidden: [entry.app.dir, entry.command.name],
1187
+ cells: [entry.app.dir === "." ? named2(entry.command.name) : `${colors.dim(`${entry.app.dir}/`)}${named2(entry.command.name)}`, colors.cyan(signature(entry.command)), colors.dim(entry.command.description)]
1188
+ }));
1189
+ }
1190
+ function preview(dir, name) {
1191
+ const command = (fromCache(dir) ?? []).find((candidate) => candidate.name === name);
1192
+ if (!command)
1193
+ return;
1194
+ const lines = [
1195
+ colors.bold(command.description),
1196
+ "",
1197
+ colors.yellow("Usage:"),
1198
+ ...command.usage.map((line) => ` ${line}`),
1199
+ ...argumentsOf(command).length ? ["", colors.yellow("Arguments:"), ...argumentsOf(command).map((argument) => ` ${colors.green(argument.name.padEnd(20))} ${argument.description}`)] : [],
1200
+ ...optionsOf(command).length ? ["", colors.yellow("Options:"), ...optionsOf(command).map((option) => ` ${colors.green(`--${option.name}${option.accept_value ? "=" : ""}`.padEnd(20))} ${option.description}`)] : [],
1201
+ ...command.help ? ["", colors.yellow("Help:"), command.help] : []
1202
+ ];
1203
+ console.log(marked2(lines.join(`
1204
+ `)));
1205
+ }
1206
+ function commandFor2(entry, given) {
1207
+ const prompts = argumentsOf(entry.command).filter((argument) => argument.is_required).slice(given.length);
1208
+ return [...entry.app.run, entry.command.name, ...given.map((value) => ({ given: value })), ...prompts.map((argument) => ({ ask: argument.name }))];
1209
+ }
1210
+ async function answered(command, ask) {
1211
+ const argv = [];
1212
+ const given = [];
1213
+ for (const word of command) {
1214
+ if (typeof word === "string") {
1215
+ argv.push(word);
1216
+ continue;
1217
+ }
1218
+ const value = "given" in word ? word.given : await ask(word.ask);
1219
+ if (value === undefined)
1220
+ return;
1221
+ argv.push(value);
1222
+ given.push(value);
1223
+ }
1224
+ return { argv, given };
1225
+ }
1226
+ function interactive() {
1227
+ return process10.stdin.isTTY && process10.stderr.isTTY;
1228
+ }
1229
+ async function execute(emitting, typed, recorded, command) {
1230
+ if (emitting) {
1231
+ emit("run", typed, recorded, command);
1232
+ return 0;
1233
+ }
1234
+ if (!interactive() && command.some((word) => typeof word === "object" && ("ask" in word)))
1235
+ fail("a required argument is missing and there is no terminal to ask for it", 1);
1236
+ const done = await answered(command, async (name) => input2(`${name}:`));
1237
+ if (done === undefined)
1238
+ return 1;
1239
+ if (recorded !== "")
1240
+ record("sf", { id: recorded, args: done.given });
1241
+ return Bun.spawn(done.argv, { stdio: ["inherit", "inherit", "inherit"] }).exited;
1242
+ }
1243
+ function listing(found) {
1244
+ const sorted = byRecency(found, runs("sf"), key2);
1245
+ const labels = sorted.map((entry) => [key2(entry), signature(entry.command)].filter(Boolean).join(" "));
1246
+ const width = Math.max(...labels.map((label) => label.length));
1247
+ return sorted.map((entry, index) => `${labels[index].padEnd(width)} ${entry.command.description}`.trimEnd()).join(`
1248
+ `);
1249
+ }
1250
+ async function hidden(flag, rest) {
1251
+ if (flag === "--show") {
1252
+ preview(rest[0] ?? ".", rest[1] ?? "");
1253
+ return 0;
1254
+ }
1255
+ if (flag === "--record") {
1256
+ record("sf", { id: rest[0] ?? "", args: rest.slice(1) });
1257
+ return 0;
1258
+ }
1259
+ if (flag === "--rows") {
1260
+ console.log(table(rows(await entries(true))).join(`
1261
+ `));
1262
+ return 0;
1263
+ }
1264
+ if (flag !== "--list" && flag !== "--names")
1265
+ return;
1266
+ const refreshed = flag === "--list" && (await consoles()).some((found) => fromCache(found.dir) !== undefined);
1267
+ const found = await entries(false);
1268
+ if (flag === "--names") {
1269
+ console.log(found.map((entry) => `${key2(entry)} ${entry.command.description}`).join(`
1270
+ `));
1271
+ return 0;
1272
+ }
1273
+ if (refreshed)
1274
+ Bun.spawn([process10.execPath, process10.argv[1], "--rows"], { stdio: ["ignore", "ignore", "ignore"] }).unref();
1275
+ list(byRecency(found, runs("sf"), key2).map((entry) => ({
1276
+ label: [key2(entry), signature(entry.command)].filter(Boolean).join(" "),
1277
+ hint: entry.command.description,
1278
+ typed: key2(entry),
1279
+ preview: ["--show", entry.app.dir, entry.command.name]
1280
+ })));
1281
+ return 0;
1282
+ }
1283
+ async function sf(args) {
1284
+ const [flag = ""] = args;
1285
+ const flagged = await hidden(flag, args.slice(1));
1286
+ if (flagged !== undefined)
1287
+ return flagged;
1288
+ const emitting = flag === "--emit";
1289
+ const words = emitting ? args.slice(1) : args;
1290
+ const last = words[0] === "-";
1291
+ const parsed = parse(last ? [] : words, usage2, {}, {
1292
+ positionals: ["[command]"],
1293
+ trailing: true,
1294
+ replaces: "bin/console typed in the php container, with the command names and arguments looked up by hand",
1295
+ examples: ["sf", ["sf cache:clear", "run it without the picker"], ["sf c:c --env=prod", "the console resolves abbreviations itself"], ["sf -", "the last command again, with its arguments"]]
1296
+ });
1297
+ const [command] = parsed.positionals;
1298
+ const given = last ? words.slice(1) : parsed.rest ?? [];
1299
+ const refreshed = command === undefined && !last && (await consoles()).some((found) => fromCache(found.dir) !== undefined);
1300
+ const found = await entries(false);
1301
+ if (last) {
1302
+ const previous = runs("sf")[0];
1303
+ const entry = previous && found.find((candidate) => key2(candidate) === previous.id);
1304
+ if (entry === undefined)
1305
+ fail(previous ? `${previous.id} is gone` : "nothing run here yet", 1);
1306
+ return execute(emitting, key2(entry), key2(entry), commandFor2(entry, [...previous.args, ...given]));
1307
+ }
1308
+ if (command !== undefined) {
1309
+ const entry = found.find((candidate) => key2(candidate) === command) ?? found.find((candidate) => candidate.command.name === command);
1310
+ if (entry !== undefined)
1311
+ return execute(emitting, key2(entry), key2(entry), commandFor2(entry, given));
1312
+ const [first] = await consoles();
1313
+ if (first === undefined)
1314
+ fail("no bin/console here", 1);
1315
+ return execute(emitting, command, "", [...first.run, command, ...given.map((value) => ({ given: value }))]);
1316
+ }
1317
+ if (found.length === 0)
1318
+ fail("no bin/console here, or it listed no command", 1);
1319
+ if (!interactive() || Bun.which("fzf") === null) {
1320
+ if (emitting)
1321
+ fail("the picker needs a terminal and fzf", 1);
1322
+ if (interactive())
1323
+ console.error("fzf is not installed, listing the commands instead");
1324
+ console.log(listing(found));
1325
+ return 0;
1326
+ }
1327
+ const self = `${process10.execPath} ${process10.argv[1]}`;
1328
+ const picked = fzf(rows(found), {
1329
+ prompt: "sf> ",
1330
+ searched: 1,
1331
+ preview: `${self} --show {1} {2}`,
1332
+ bind: refreshed ? [`load:unbind(load)+reload-sync(${self} --rows)`] : []
1333
+ });
1334
+ const entry = picked && (await entries(false)).find((candidate) => candidate.app.dir === picked.hidden[0] && candidate.command.name === picked.hidden[1]);
1335
+ return entry === undefined ? 1 : execute(emitting, key2(entry), key2(entry), commandFor2(entry, []));
1336
+ }
1337
+
1338
+ // src/sf-cli.ts
1339
+ process11.exitCode = await runMain({ name: "sf", version, run: sf });