@algolia/wizard 0.6.0 → 0.7.0-rc.53.39
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/README.md +1 -1
- package/dist/main.js +771 -495
- package/package.json +1 -3
package/dist/main.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/ui/App.tsx
|
|
7
|
-
import { Box as
|
|
7
|
+
import { Box as Box14, Text as Text14, useApp, useInput as useInput6, useWindowSize as useWindowSize8 } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/core/store.ts
|
|
10
10
|
import { create } from "zustand";
|
|
@@ -12,32 +12,86 @@ import { nanoid } from "nanoid";
|
|
|
12
12
|
|
|
13
13
|
// src/lib/algoliaCli.ts
|
|
14
14
|
import { spawn } from "node:child_process";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return require2.resolve("@algolia/cli/bin/run.js");
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
function npxArgs(args) {
|
|
17
|
+
return ["--yes", "@algolia/cli@latest", ...args];
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
var shell = process.platform === "win32";
|
|
20
|
+
function lineSplitter(emit) {
|
|
21
|
+
let buffer = "";
|
|
22
|
+
return {
|
|
23
|
+
push(chunk) {
|
|
24
|
+
buffer += chunk;
|
|
25
|
+
const lines = buffer.split("\n");
|
|
26
|
+
buffer = lines.pop() ?? "";
|
|
27
|
+
for (const line of lines) emit(line.replace(/\r$/, ""));
|
|
28
|
+
},
|
|
29
|
+
flush() {
|
|
30
|
+
if (buffer) emit(buffer.replace(/\r$/, ""));
|
|
31
|
+
buffer = "";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
var wizardSink = (stream, line) => {
|
|
36
|
+
if (!line.trim()) return;
|
|
37
|
+
useWizard.getState().pushCliOutput(stream, line);
|
|
38
|
+
};
|
|
39
|
+
var stderrSink = (stream, line) => {
|
|
40
|
+
if (stream === "stdout") return;
|
|
41
|
+
wizardSink(stream, line);
|
|
42
|
+
};
|
|
43
|
+
function runAlgoliaCli(args, { onOutput } = {}) {
|
|
44
|
+
const store2 = useWizard.getState();
|
|
45
|
+
const logId = store2.logStart("tool", `algolia ${args.join(" ")}`);
|
|
21
46
|
return new Promise((resolve4, reject) => {
|
|
22
|
-
const child = spawn(
|
|
47
|
+
const child = spawn("npx", npxArgs(args), { shell });
|
|
23
48
|
let stdout = "";
|
|
24
49
|
let stderr = "";
|
|
25
|
-
|
|
26
|
-
|
|
50
|
+
const splitters = {
|
|
51
|
+
stdout: lineSplitter((line) => onOutput?.("stdout", line)),
|
|
52
|
+
stderr: lineSplitter((line) => onOutput?.("stderr", line))
|
|
53
|
+
};
|
|
54
|
+
child.stdout.on("data", (chunk) => {
|
|
55
|
+
const text = String(chunk);
|
|
56
|
+
stdout += text;
|
|
57
|
+
splitters.stdout.push(text);
|
|
58
|
+
});
|
|
59
|
+
child.stderr.on("data", (chunk) => {
|
|
60
|
+
const text = String(chunk);
|
|
61
|
+
stderr += text;
|
|
62
|
+
splitters.stderr.push(text);
|
|
63
|
+
});
|
|
27
64
|
child.on("error", reject);
|
|
28
65
|
child.on("close", (code) => {
|
|
66
|
+
splitters.stdout.flush();
|
|
67
|
+
splitters.stderr.flush();
|
|
29
68
|
if (code === 0) {
|
|
30
69
|
resolve4(stdout);
|
|
31
70
|
} else {
|
|
32
|
-
const
|
|
71
|
+
const failed = stderr.trim();
|
|
72
|
+
let detail = "";
|
|
73
|
+
if (failed) {
|
|
74
|
+
detail = `: ${failed}`;
|
|
75
|
+
} else if (stdout.trim()) {
|
|
76
|
+
detail = " (no stderr; stdout withheld \u2014 it may contain credentials)";
|
|
77
|
+
}
|
|
33
78
|
reject(
|
|
34
79
|
new Error(
|
|
35
|
-
`Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail
|
|
80
|
+
`Algolia CLI \`${args.join(" ")}\` failed (exit ${code})${detail}`
|
|
36
81
|
)
|
|
37
82
|
);
|
|
38
83
|
}
|
|
39
84
|
});
|
|
40
|
-
})
|
|
85
|
+
}).then(
|
|
86
|
+
(out) => {
|
|
87
|
+
useWizard.getState().logEnd(logId, "success");
|
|
88
|
+
return out;
|
|
89
|
+
},
|
|
90
|
+
(err) => {
|
|
91
|
+
useWizard.getState().logEnd(logId, "error");
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
);
|
|
41
95
|
}
|
|
42
96
|
async function getUser() {
|
|
43
97
|
let raw;
|
|
@@ -52,19 +106,23 @@ async function getUser() {
|
|
|
52
106
|
return null;
|
|
53
107
|
}
|
|
54
108
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
child.on("error", reject);
|
|
63
|
-
child.on("close", (code) => {
|
|
64
|
-
if (code === 0) resolve4();
|
|
65
|
-
else reject(new Error(`Algolia authentication failed (exit ${code}).`));
|
|
66
|
-
});
|
|
109
|
+
var loginResultSchema = z.object({
|
|
110
|
+
success: z.boolean(),
|
|
111
|
+
email: z.string().optional()
|
|
112
|
+
});
|
|
113
|
+
async function runAuthLogin() {
|
|
114
|
+
const raw = await runAlgoliaCli(["auth", "login", "--non-interactive"], {
|
|
115
|
+
onOutput: stderrSink
|
|
67
116
|
});
|
|
117
|
+
let parsed;
|
|
118
|
+
try {
|
|
119
|
+
parsed = loginResultSchema.safeParse(JSON.parse(raw));
|
|
120
|
+
} catch {
|
|
121
|
+
parsed = void 0;
|
|
122
|
+
}
|
|
123
|
+
if (parsed?.success && !parsed.data.success) {
|
|
124
|
+
throw new Error("Algolia sign-in did not report success.");
|
|
125
|
+
}
|
|
68
126
|
}
|
|
69
127
|
|
|
70
128
|
// src/lib/auth.ts
|
|
@@ -171,6 +229,7 @@ function describeInputValue(value) {
|
|
|
171
229
|
return Array.isArray(value) ? value.join(", ") : value;
|
|
172
230
|
}
|
|
173
231
|
var NOTICE_INTERVAL_MS = 2e3;
|
|
232
|
+
var CLI_OUTPUT_LIMIT = 200;
|
|
174
233
|
var useWizard = create((set, get) => ({
|
|
175
234
|
phase: "idle",
|
|
176
235
|
homeScreen: "home",
|
|
@@ -182,10 +241,18 @@ var useWizard = create((set, get) => ({
|
|
|
182
241
|
notices: [],
|
|
183
242
|
_noticeQueue: [],
|
|
184
243
|
_noticeTimer: null,
|
|
244
|
+
cliOutput: [],
|
|
245
|
+
targetIndex: null,
|
|
185
246
|
logs: [],
|
|
186
247
|
error: null,
|
|
187
248
|
inputReq: null,
|
|
188
249
|
_resolve: null,
|
|
250
|
+
// Brackets a CLI subprocess that needs the screen. Sign-in happens after the
|
|
251
|
+
// welcome screen's enter, so `endAuth` lands on 'preflight', not 'idle':
|
|
252
|
+
// returning to 'idle' would put the welcome screen back up and ask the user
|
|
253
|
+
// to confirm the run a second time.
|
|
254
|
+
beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
|
|
255
|
+
endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
|
|
189
256
|
// Advances past the welcome screen. Only meaningful from 'idle' — once the
|
|
190
257
|
// workflow is running there's nothing left to confirm.
|
|
191
258
|
// Reset `homeScreen` so preflight shows Welcome, not the Learn more sub-view.
|
|
@@ -220,7 +287,13 @@ var useWizard = create((set, get) => ({
|
|
|
220
287
|
syncSteps: (steps, currentStepIndex) => set({ steps, currentStepIndex }),
|
|
221
288
|
setActiveStep: (index) => {
|
|
222
289
|
get()._clearNoticeQueue();
|
|
223
|
-
set({
|
|
290
|
+
set({
|
|
291
|
+
phase: "running",
|
|
292
|
+
currentStepIndex: index,
|
|
293
|
+
output: "",
|
|
294
|
+
notices: [],
|
|
295
|
+
cliOutput: []
|
|
296
|
+
});
|
|
224
297
|
},
|
|
225
298
|
setUser: (user2) => set({ user: user2 }),
|
|
226
299
|
appendToken: (text) => set((s) => ({ output: s.output + text })),
|
|
@@ -261,6 +334,16 @@ var useWizard = create((set, get) => ({
|
|
|
261
334
|
get()._clearNoticeQueue();
|
|
262
335
|
set({ notices: [] });
|
|
263
336
|
},
|
|
337
|
+
// Unthrottled, unlike `pushNotice`: these lines arrive at whatever rate the
|
|
338
|
+
// subprocess emits them, and holding them back would land output after the
|
|
339
|
+
// command it belongs to has already exited.
|
|
340
|
+
pushCliOutput: (stream, text) => set((s) => ({
|
|
341
|
+
cliOutput: [...s.cliOutput, { id: nanoid(), stream, text }].slice(
|
|
342
|
+
-CLI_OUTPUT_LIMIT
|
|
343
|
+
)
|
|
344
|
+
})),
|
|
345
|
+
clearCliOutput: () => set({ cliOutput: [] }),
|
|
346
|
+
setTargetIndex: (index) => set({ targetIndex: index }),
|
|
264
347
|
logStart: (kind, name, input) => {
|
|
265
348
|
const id = nanoid();
|
|
266
349
|
set((s) => ({
|
|
@@ -305,6 +388,8 @@ var useWizard = create((set, get) => ({
|
|
|
305
388
|
currentStepIndex: 0,
|
|
306
389
|
output: "",
|
|
307
390
|
notices: [],
|
|
391
|
+
cliOutput: [],
|
|
392
|
+
targetIndex: null,
|
|
308
393
|
logs: [],
|
|
309
394
|
error: null,
|
|
310
395
|
inputReq: null,
|
|
@@ -313,16 +398,100 @@ var useWizard = create((set, get) => ({
|
|
|
313
398
|
}
|
|
314
399
|
}));
|
|
315
400
|
|
|
401
|
+
// src/ui/CliOutput.tsx
|
|
402
|
+
import { Box, Text, useWindowSize } from "ink";
|
|
403
|
+
|
|
404
|
+
// src/ui/theme.ts
|
|
405
|
+
var MARKER = {
|
|
406
|
+
pending: "\u25CB",
|
|
407
|
+
running: "\u25D0",
|
|
408
|
+
done: "\u2713",
|
|
409
|
+
error: "\u2716"
|
|
410
|
+
};
|
|
411
|
+
var BRAND = "#003DFF";
|
|
412
|
+
var SECONDARY = "#5468FF";
|
|
413
|
+
var DANGER = "#F86E7E";
|
|
414
|
+
var COLORS = {
|
|
415
|
+
brand: BRAND,
|
|
416
|
+
primary: "#E6EDF3",
|
|
417
|
+
secondary: SECONDARY,
|
|
418
|
+
strong: "#FFFFFF",
|
|
419
|
+
muted: "#8B949E",
|
|
420
|
+
dim: "#484F58",
|
|
421
|
+
highlight: { bg: "#12331C", fg: "#4ADE80" },
|
|
422
|
+
badge: "#E3B341",
|
|
423
|
+
danger: DANGER,
|
|
424
|
+
success: "#4ADE80",
|
|
425
|
+
bg: {
|
|
426
|
+
main: "#0B0E14",
|
|
427
|
+
sidebar: "#14171E"
|
|
428
|
+
},
|
|
429
|
+
border: "#30363D",
|
|
430
|
+
accent: "#76A0FF",
|
|
431
|
+
status: {
|
|
432
|
+
pending: "gray",
|
|
433
|
+
running: "#76A0FF",
|
|
434
|
+
done: "#4ADE80",
|
|
435
|
+
error: DANGER
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// src/ui/CliOutput.tsx
|
|
440
|
+
import { jsxs } from "react/jsx-runtime";
|
|
441
|
+
var CLI_MARKER = "\u203A";
|
|
442
|
+
var RESERVED_ROWS = 16;
|
|
443
|
+
var MAX_ROWS = 12;
|
|
444
|
+
var PANEL_TEXT_WIDTH = 45;
|
|
445
|
+
var URL_PATTERN = /https?:\/\//;
|
|
446
|
+
function rowCost(text) {
|
|
447
|
+
return URL_PATTERN.test(text) ? Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH)) : 1;
|
|
448
|
+
}
|
|
449
|
+
function CliOutput() {
|
|
450
|
+
const cliOutput = useWizard((s) => s.cliOutput);
|
|
451
|
+
const { rows } = useWindowSize();
|
|
452
|
+
if (!cliOutput.length) return null;
|
|
453
|
+
const rowBudget = Math.min(Math.max(rows - RESERVED_ROWS, 3), MAX_ROWS);
|
|
454
|
+
const visible = [];
|
|
455
|
+
let usedRows = 0;
|
|
456
|
+
for (let i = cliOutput.length - 1; i >= 0; i--) {
|
|
457
|
+
const cost = rowCost(cliOutput[i].text);
|
|
458
|
+
if (usedRows + cost > rowBudget && visible.length > 0) break;
|
|
459
|
+
visible.unshift(cliOutput[i]);
|
|
460
|
+
usedRows += cost;
|
|
461
|
+
}
|
|
462
|
+
const hidden = cliOutput.length - visible.length;
|
|
463
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
464
|
+
hidden > 0 && /* @__PURE__ */ jsxs(Text, { color: COLORS.dim, children: [
|
|
465
|
+
"\u2191 ",
|
|
466
|
+
hidden,
|
|
467
|
+
" earlier line(s)"
|
|
468
|
+
] }),
|
|
469
|
+
visible.map((line) => /* @__PURE__ */ jsxs(
|
|
470
|
+
Text,
|
|
471
|
+
{
|
|
472
|
+
color: line.stream === "stderr" ? COLORS.muted : COLORS.dim,
|
|
473
|
+
wrap: URL_PATTERN.test(line.text) ? "wrap" : "truncate",
|
|
474
|
+
children: [
|
|
475
|
+
CLI_MARKER,
|
|
476
|
+
" ",
|
|
477
|
+
line.text
|
|
478
|
+
]
|
|
479
|
+
},
|
|
480
|
+
line.id
|
|
481
|
+
))
|
|
482
|
+
] });
|
|
483
|
+
}
|
|
484
|
+
|
|
316
485
|
// src/ui/Notices.tsx
|
|
317
|
-
import { Box as
|
|
486
|
+
import { Box as Box3, Text as Text3, useWindowSize as useWindowSize3 } from "ink";
|
|
318
487
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
319
488
|
|
|
320
489
|
// src/ui/Table.tsx
|
|
321
|
-
import { Box, Text, measureElement, useWindowSize } from "ink";
|
|
490
|
+
import { Box as Box2, Text as Text2, measureElement, useWindowSize as useWindowSize2 } from "ink";
|
|
322
491
|
import { useEffect, useRef, useState } from "react";
|
|
323
492
|
import { jsx } from "react/jsx-runtime";
|
|
324
493
|
function Table({ columns, rows }) {
|
|
325
|
-
const { columns: termCols } =
|
|
494
|
+
const { columns: termCols } = useWindowSize2();
|
|
326
495
|
const ref = useRef(null);
|
|
327
496
|
const [width, setWidth] = useState(0);
|
|
328
497
|
useEffect(() => {
|
|
@@ -330,7 +499,7 @@ function Table({ columns, rows }) {
|
|
|
330
499
|
}, [termCols, columns, rows]);
|
|
331
500
|
if (rows.length === 0) return null;
|
|
332
501
|
const lines = formatTable(columns, rows, width || void 0);
|
|
333
|
-
return /* @__PURE__ */ jsx(
|
|
502
|
+
return /* @__PURE__ */ jsx(Box2, { flexDirection: "column", ref, children: lines.map((line, i) => /* @__PURE__ */ jsx(Text2, { wrap: "truncate", children: line }, `tbl-${i}`)) });
|
|
334
503
|
}
|
|
335
504
|
function formatTable(columns, rows, width) {
|
|
336
505
|
const natural = columns.map(
|
|
@@ -370,48 +539,13 @@ function resize(widths, budget) {
|
|
|
370
539
|
}
|
|
371
540
|
var truncate = (s, width) => s.length <= width ? s : width <= 1 ? s.slice(0, width) : `${s.slice(0, width - 1)}\u2026`;
|
|
372
541
|
|
|
373
|
-
// src/ui/theme.ts
|
|
374
|
-
var MARKER = {
|
|
375
|
-
pending: "\u25CB",
|
|
376
|
-
running: "\u25D0",
|
|
377
|
-
done: "\u2713",
|
|
378
|
-
error: "\u2716"
|
|
379
|
-
};
|
|
380
|
-
var BRAND = "#003DFF";
|
|
381
|
-
var SECONDARY = "#5468FF";
|
|
382
|
-
var DANGER = "#F86E7E";
|
|
383
|
-
var COLORS = {
|
|
384
|
-
brand: BRAND,
|
|
385
|
-
primary: "#E6EDF3",
|
|
386
|
-
secondary: SECONDARY,
|
|
387
|
-
strong: "#FFFFFF",
|
|
388
|
-
muted: "#8B949E",
|
|
389
|
-
dim: "#484F58",
|
|
390
|
-
highlight: { bg: "#12331C", fg: "#4ADE80" },
|
|
391
|
-
badge: "#E3B341",
|
|
392
|
-
danger: DANGER,
|
|
393
|
-
success: "#4ADE80",
|
|
394
|
-
bg: {
|
|
395
|
-
main: "#0B0E14",
|
|
396
|
-
sidebar: "#14171E"
|
|
397
|
-
},
|
|
398
|
-
border: "#30363D",
|
|
399
|
-
accent: "#76A0FF",
|
|
400
|
-
status: {
|
|
401
|
-
pending: "gray",
|
|
402
|
-
running: "#76A0FF",
|
|
403
|
-
done: "#4ADE80",
|
|
404
|
-
error: DANGER
|
|
405
|
-
}
|
|
406
|
-
};
|
|
407
|
-
|
|
408
542
|
// src/ui/Notices.tsx
|
|
409
|
-
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
543
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
410
544
|
var AGENT_MARKER = "\u2726";
|
|
411
|
-
var
|
|
412
|
-
var
|
|
545
|
+
var RESERVED_ROWS2 = 14;
|
|
546
|
+
var PANEL_TEXT_WIDTH2 = 45;
|
|
413
547
|
function messageLineCount(text) {
|
|
414
|
-
return Math.max(1, Math.ceil(text.length /
|
|
548
|
+
return Math.max(1, Math.ceil(text.length / PANEL_TEXT_WIDTH2));
|
|
415
549
|
}
|
|
416
550
|
function noticeLineCount(notice) {
|
|
417
551
|
const messageLines = (notice.messages ?? []).reduce((sum, m) => {
|
|
@@ -422,7 +556,7 @@ function noticeLineCount(notice) {
|
|
|
422
556
|
return messageLines + tableLines;
|
|
423
557
|
}
|
|
424
558
|
function fitVisibleNotices(notices, windowRows) {
|
|
425
|
-
const budget = Math.max(windowRows -
|
|
559
|
+
const budget = Math.max(windowRows - RESERVED_ROWS2, 3);
|
|
426
560
|
let used = 0;
|
|
427
561
|
let count = 0;
|
|
428
562
|
for (let i = notices.length - 1; i >= 0; i--) {
|
|
@@ -455,7 +589,7 @@ function parseHex(hex) {
|
|
|
455
589
|
}
|
|
456
590
|
function Notices() {
|
|
457
591
|
const notices = useWizard((s) => s.notices);
|
|
458
|
-
const { rows: windowRows } =
|
|
592
|
+
const { rows: windowRows } = useWindowSize3();
|
|
459
593
|
const visible = fitVisibleNotices(notices, windowRows);
|
|
460
594
|
const [pulseStep, setPulseStep] = useState2(0);
|
|
461
595
|
useEffect2(() => {
|
|
@@ -472,14 +606,14 @@ function Notices() {
|
|
|
472
606
|
}, []);
|
|
473
607
|
if (!visible.length) return null;
|
|
474
608
|
const pulseColor = PULSE_COLORS[pulseStep];
|
|
475
|
-
return /* @__PURE__ */ jsx2(
|
|
609
|
+
return /* @__PURE__ */ jsx2(Box3, { flexDirection: "column", gap: 1, marginBottom: 1, children: visible.map((notice, i) => {
|
|
476
610
|
const isLatest = i === visible.length - 1;
|
|
477
|
-
return /* @__PURE__ */
|
|
611
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
|
|
478
612
|
notice.messages?.map((m, j) => {
|
|
479
613
|
const line = typeof m === "string" ? { text: m } : m;
|
|
480
614
|
const prefix = j === 0 ? `${AGENT_MARKER} ` : " ";
|
|
481
|
-
return /* @__PURE__ */
|
|
482
|
-
|
|
615
|
+
return /* @__PURE__ */ jsxs2(
|
|
616
|
+
Text3,
|
|
483
617
|
{
|
|
484
618
|
color: isLatest && !line.color ? pulseColor : line.color ?? COLORS.dim,
|
|
485
619
|
bold: line.bold,
|
|
@@ -497,37 +631,37 @@ function Notices() {
|
|
|
497
631
|
}
|
|
498
632
|
|
|
499
633
|
// src/ui/PromptInput.tsx
|
|
500
|
-
import { Box as
|
|
634
|
+
import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
|
|
501
635
|
import TextInput from "ink-text-input";
|
|
502
636
|
import { useState as useState4 } from "react";
|
|
503
637
|
|
|
504
638
|
// src/ui/NextAction.tsx
|
|
505
|
-
import { Box as
|
|
506
|
-
import { Fragment, jsx as jsx3, jsxs as
|
|
639
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
640
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
507
641
|
function NextAction({
|
|
508
642
|
action,
|
|
509
643
|
keyHint,
|
|
510
644
|
hierarchy = "primary"
|
|
511
645
|
}) {
|
|
512
|
-
return /* @__PURE__ */
|
|
513
|
-
hierarchy === "primary" && /* @__PURE__ */ jsx3(
|
|
514
|
-
hierarchy === "secondary" && /* @__PURE__ */
|
|
515
|
-
/* @__PURE__ */ jsx3(
|
|
516
|
-
/* @__PURE__ */ jsx3(
|
|
646
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
|
|
647
|
+
hierarchy === "primary" && /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
|
|
648
|
+
hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
649
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `>` }),
|
|
650
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, bold: true, children: action })
|
|
517
651
|
] }),
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
/* @__PURE__ */ jsx3(
|
|
520
|
-
/* @__PURE__ */ jsx3(
|
|
521
|
-
/* @__PURE__ */ jsx3(
|
|
522
|
-
/* @__PURE__ */ jsx3(
|
|
652
|
+
/* @__PURE__ */ jsxs3(Box4, { children: [
|
|
653
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: "press " }),
|
|
654
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `[` }),
|
|
655
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, children: keyHint }),
|
|
656
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `]` })
|
|
523
657
|
] })
|
|
524
658
|
] });
|
|
525
659
|
}
|
|
526
660
|
|
|
527
661
|
// src/ui/SelectPrompt.tsx
|
|
528
|
-
import { Box as
|
|
662
|
+
import { Box as Box5, Text as Text5, measureElement as measureElement2, useInput, useWindowSize as useWindowSize4 } from "ink";
|
|
529
663
|
import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
530
|
-
import { jsx as jsx4, jsxs as
|
|
664
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
531
665
|
var CANCEL = "cancel";
|
|
532
666
|
var ARROW_WIDTH = 4;
|
|
533
667
|
var COLUMN_GAP = 2;
|
|
@@ -564,7 +698,7 @@ function SelectPrompt({
|
|
|
564
698
|
if (multi) hints.push({ key: "[space]", label: "select" });
|
|
565
699
|
hints.push({ key: "[enter]", label: "confirm" });
|
|
566
700
|
const containerRef = useRef2(null);
|
|
567
|
-
const { columns } =
|
|
701
|
+
const { columns } = useWindowSize4();
|
|
568
702
|
const [width, setWidth] = useState3(columns);
|
|
569
703
|
useLayoutEffect(() => {
|
|
570
704
|
if (containerRef.current) {
|
|
@@ -609,53 +743,53 @@ function SelectPrompt({
|
|
|
609
743
|
}
|
|
610
744
|
}
|
|
611
745
|
});
|
|
612
|
-
return /* @__PURE__ */ jsx4(
|
|
613
|
-
error && /* @__PURE__ */ jsx4(
|
|
614
|
-
messages?.map((m, i) => /* @__PURE__ */ jsx4(
|
|
746
|
+
return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
|
|
747
|
+
error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
|
|
748
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
615
749
|
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
question && /* @__PURE__ */ jsx4(
|
|
618
|
-
helpText && /* @__PURE__ */ jsx4(
|
|
750
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
751
|
+
question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
|
|
752
|
+
helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
|
|
619
753
|
] }),
|
|
620
|
-
/* @__PURE__ */ jsx4(
|
|
754
|
+
/* @__PURE__ */ jsx4(Box5, { flexDirection: "column", children: rows.map((option, i) => {
|
|
621
755
|
const highlighted = i === index;
|
|
622
756
|
const isCancel = i === cancelIndex;
|
|
623
757
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
624
758
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
625
759
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
626
|
-
const label = /* @__PURE__ */
|
|
760
|
+
const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
|
|
627
761
|
highlighted ? "\u276F " : " ",
|
|
628
762
|
bullet,
|
|
629
763
|
option
|
|
630
764
|
] });
|
|
631
765
|
const isText = sec?.kind === "text";
|
|
632
|
-
return /* @__PURE__ */
|
|
633
|
-
|
|
766
|
+
return /* @__PURE__ */ jsxs4(
|
|
767
|
+
Box5,
|
|
634
768
|
{
|
|
635
769
|
width: isText ? "100%" : barWidth,
|
|
636
770
|
paddingX: 1,
|
|
637
771
|
paddingY: 1,
|
|
638
772
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
639
773
|
children: [
|
|
640
|
-
/* @__PURE__ */ jsx4(
|
|
641
|
-
isText && textWidth > 0 && /* @__PURE__ */ jsx4(
|
|
642
|
-
|
|
774
|
+
/* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
775
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
|
|
776
|
+
Text5,
|
|
643
777
|
{
|
|
644
778
|
wrap: "truncate",
|
|
645
779
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
646
780
|
children: sec.value
|
|
647
781
|
}
|
|
648
782
|
) }),
|
|
649
|
-
sec?.kind === "badge" && /* @__PURE__ */ jsx4(
|
|
783
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
650
784
|
]
|
|
651
785
|
},
|
|
652
786
|
`row-${i}`
|
|
653
787
|
);
|
|
654
788
|
}) }),
|
|
655
|
-
/* @__PURE__ */ jsx4(
|
|
789
|
+
/* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
656
790
|
i > 0 ? " " : "",
|
|
657
|
-
/* @__PURE__ */ jsx4(
|
|
658
|
-
/* @__PURE__ */
|
|
791
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
|
|
792
|
+
/* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
659
793
|
" ",
|
|
660
794
|
label
|
|
661
795
|
] })
|
|
@@ -664,7 +798,7 @@ function SelectPrompt({
|
|
|
664
798
|
}
|
|
665
799
|
|
|
666
800
|
// src/ui/PromptInput.tsx
|
|
667
|
-
import { jsx as jsx5, jsxs as
|
|
801
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
668
802
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
669
803
|
function EnterToContinuePrompt({
|
|
670
804
|
question,
|
|
@@ -675,10 +809,10 @@ function EnterToContinuePrompt({
|
|
|
675
809
|
if (key.return) onDecide(true);
|
|
676
810
|
else if (key.escape) onDecide(false);
|
|
677
811
|
});
|
|
678
|
-
return /* @__PURE__ */
|
|
679
|
-
messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
680
|
-
question && /* @__PURE__ */ jsx5(
|
|
681
|
-
/* @__PURE__ */
|
|
812
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
|
|
813
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
814
|
+
question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
|
|
815
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
|
|
682
816
|
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
|
|
683
817
|
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
684
818
|
] })
|
|
@@ -688,11 +822,11 @@ function PromptInput() {
|
|
|
688
822
|
const { phase, inputReq, submitInput } = useWizard();
|
|
689
823
|
const [draft, setDraft] = useState4("");
|
|
690
824
|
if (phase === "done" || phase === "error") {
|
|
691
|
-
return /* @__PURE__ */ jsx5(
|
|
825
|
+
return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
692
826
|
}
|
|
693
827
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
694
828
|
if (inputReq.promptType === "multipleChoice") {
|
|
695
|
-
return /* @__PURE__ */ jsx5(
|
|
829
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
696
830
|
SelectPrompt,
|
|
697
831
|
{
|
|
698
832
|
question: inputReq.prompt,
|
|
@@ -709,7 +843,7 @@ function PromptInput() {
|
|
|
709
843
|
) });
|
|
710
844
|
}
|
|
711
845
|
if (inputReq.promptType === "multiSelect") {
|
|
712
|
-
return /* @__PURE__ */ jsx5(
|
|
846
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
713
847
|
SelectPrompt,
|
|
714
848
|
{
|
|
715
849
|
multi: true,
|
|
@@ -724,7 +858,7 @@ function PromptInput() {
|
|
|
724
858
|
) });
|
|
725
859
|
}
|
|
726
860
|
if (inputReq.promptType === "notice") {
|
|
727
|
-
return /* @__PURE__ */ jsx5(
|
|
861
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
728
862
|
SelectPrompt,
|
|
729
863
|
{
|
|
730
864
|
question: inputReq.prompt,
|
|
@@ -746,7 +880,7 @@ function PromptInput() {
|
|
|
746
880
|
}
|
|
747
881
|
if (inputReq.promptType === "acceptReject") {
|
|
748
882
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
749
|
-
return /* @__PURE__ */ jsx5(
|
|
883
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
750
884
|
SelectPrompt,
|
|
751
885
|
{
|
|
752
886
|
question: inputReq.prompt,
|
|
@@ -757,11 +891,11 @@ function PromptInput() {
|
|
|
757
891
|
}
|
|
758
892
|
) });
|
|
759
893
|
}
|
|
760
|
-
return /* @__PURE__ */
|
|
761
|
-
inputReq.error && /* @__PURE__ */ jsx5(
|
|
762
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
763
|
-
/* @__PURE__ */
|
|
764
|
-
/* @__PURE__ */
|
|
894
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
895
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
|
|
896
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
897
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
898
|
+
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
|
|
765
899
|
inputReq.prompt,
|
|
766
900
|
" "
|
|
767
901
|
] }),
|
|
@@ -783,7 +917,7 @@ function PromptInput() {
|
|
|
783
917
|
// src/ui/Welcome.tsx
|
|
784
918
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
785
919
|
import { fileURLToPath } from "node:url";
|
|
786
|
-
import { Box as
|
|
920
|
+
import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
|
|
787
921
|
|
|
788
922
|
// src/ui/copy/welcome.ts
|
|
789
923
|
var sidebarItems = [
|
|
@@ -811,27 +945,27 @@ var sidebarItems = [
|
|
|
811
945
|
|
|
812
946
|
// src/ui/Welcome.tsx
|
|
813
947
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
814
|
-
import { jsx as jsx6, jsxs as
|
|
948
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
815
949
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
816
950
|
function SidebarItem({
|
|
817
951
|
title,
|
|
818
952
|
description
|
|
819
953
|
}) {
|
|
820
|
-
return /* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */ jsx6(
|
|
823
|
-
/* @__PURE__ */ jsx6(
|
|
954
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
955
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
956
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
|
|
957
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
|
|
824
958
|
] }),
|
|
825
|
-
/* @__PURE__ */
|
|
959
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
|
|
826
960
|
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
827
|
-
/* @__PURE__ */ jsx6(
|
|
961
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
|
|
828
962
|
] })
|
|
829
963
|
] });
|
|
830
964
|
}
|
|
831
965
|
function Welcome() {
|
|
832
966
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
833
967
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
834
|
-
const { rows } =
|
|
968
|
+
const { rows } = useWindowSize5();
|
|
835
969
|
useInput3((input, key) => {
|
|
836
970
|
if (key.return) confirmStart();
|
|
837
971
|
else if (input === "i") openLearnMore();
|
|
@@ -850,15 +984,15 @@ function Welcome() {
|
|
|
850
984
|
if (rows < 30) {
|
|
851
985
|
layout = scales["small"];
|
|
852
986
|
}
|
|
853
|
-
return /* @__PURE__ */
|
|
987
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
854
988
|
/* @__PURE__ */ jsx6(
|
|
855
|
-
|
|
989
|
+
Box7,
|
|
856
990
|
{
|
|
857
991
|
paddingY: layout.main.padding.y,
|
|
858
992
|
paddingX: layout.main.padding.x,
|
|
859
993
|
flexDirection: "column",
|
|
860
994
|
justifyContent: "center",
|
|
861
|
-
children: /* @__PURE__ */
|
|
995
|
+
children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
|
|
862
996
|
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
863
997
|
Image,
|
|
864
998
|
{
|
|
@@ -870,16 +1004,16 @@ function Welcome() {
|
|
|
870
1004
|
protocol: "halfBlock"
|
|
871
1005
|
}
|
|
872
1006
|
) }),
|
|
873
|
-
/* @__PURE__ */ jsx6(
|
|
874
|
-
/* @__PURE__ */
|
|
1007
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1008
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
|
|
875
1009
|
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
876
1010
|
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
877
1011
|
] })
|
|
878
1012
|
] })
|
|
879
1013
|
}
|
|
880
1014
|
),
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
|
|
1015
|
+
/* @__PURE__ */ jsxs6(
|
|
1016
|
+
Box7,
|
|
883
1017
|
{
|
|
884
1018
|
backgroundColor: COLORS.bg.sidebar,
|
|
885
1019
|
width: 40,
|
|
@@ -889,7 +1023,7 @@ function Welcome() {
|
|
|
889
1023
|
flexDirection: "column",
|
|
890
1024
|
justifyContent: "center",
|
|
891
1025
|
children: [
|
|
892
|
-
/* @__PURE__ */ jsx6(
|
|
1026
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
893
1027
|
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
894
1028
|
]
|
|
895
1029
|
}
|
|
@@ -899,7 +1033,7 @@ function Welcome() {
|
|
|
899
1033
|
|
|
900
1034
|
// src/ui/LearnMore.tsx
|
|
901
1035
|
import { Fragment as Fragment2 } from "react";
|
|
902
|
-
import { Box as
|
|
1036
|
+
import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
903
1037
|
|
|
904
1038
|
// src/ui/copy/learn-more.ts
|
|
905
1039
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -936,7 +1070,7 @@ var policyLinks = [
|
|
|
936
1070
|
];
|
|
937
1071
|
|
|
938
1072
|
// src/ui/LearnMore.tsx
|
|
939
|
-
import { jsx as jsx7, jsxs as
|
|
1073
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
940
1074
|
var TAG_COLORS = {
|
|
941
1075
|
READ: COLORS.success,
|
|
942
1076
|
WRITE: COLORS.badge,
|
|
@@ -952,25 +1086,25 @@ function NeverLine({
|
|
|
952
1086
|
}) {
|
|
953
1087
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
954
1088
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
955
|
-
return /* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */ jsx7(
|
|
1089
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1090
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
|
|
957
1091
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
958
|
-
segments.map((s, i) => /* @__PURE__ */ jsx7(
|
|
1092
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
959
1093
|
" ".repeat(rightPad),
|
|
960
|
-
/* @__PURE__ */ jsx7(
|
|
1094
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
|
|
961
1095
|
] });
|
|
962
1096
|
}
|
|
963
1097
|
function LearnMore() {
|
|
964
1098
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
965
1099
|
const backToHome = useWizard((s) => s.backToHome);
|
|
966
|
-
const { columns } =
|
|
1100
|
+
const { columns } = useWindowSize6();
|
|
967
1101
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
968
1102
|
useInput4((_input, key) => {
|
|
969
1103
|
if (key.escape) backToHome();
|
|
970
1104
|
else if (key.return) confirmStart();
|
|
971
1105
|
});
|
|
972
|
-
return /* @__PURE__ */
|
|
973
|
-
|
|
1106
|
+
return /* @__PURE__ */ jsxs7(
|
|
1107
|
+
Box8,
|
|
974
1108
|
{
|
|
975
1109
|
flexDirection: "column",
|
|
976
1110
|
paddingX: PADDING_X,
|
|
@@ -978,20 +1112,20 @@ function LearnMore() {
|
|
|
978
1112
|
width: "100%",
|
|
979
1113
|
gap: 1,
|
|
980
1114
|
children: [
|
|
981
|
-
/* @__PURE__ */ jsx7(
|
|
982
|
-
/* @__PURE__ */ jsx7(
|
|
983
|
-
/* @__PURE__ */ jsx7(
|
|
984
|
-
/* @__PURE__ */ jsx7(
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
/* @__PURE__ */ jsx7(
|
|
987
|
-
/* @__PURE__ */ jsx7(
|
|
988
|
-
/* @__PURE__ */ jsx7(
|
|
989
|
-
/* @__PURE__ */ jsx7(
|
|
1115
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1116
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
|
|
1117
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
1118
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1119
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1120
|
+
/* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1121
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1122
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1123
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
990
1124
|
] }) })
|
|
991
1125
|
] })
|
|
992
1126
|
] }, item.tag)) }),
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
/* @__PURE__ */ jsx7(
|
|
1127
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
1128
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
995
1129
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
996
1130
|
/* @__PURE__ */ jsx7(
|
|
997
1131
|
NeverLine,
|
|
@@ -1000,7 +1134,7 @@ function LearnMore() {
|
|
|
1000
1134
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1001
1135
|
}
|
|
1002
1136
|
),
|
|
1003
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1137
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1004
1138
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1005
1139
|
/* @__PURE__ */ jsx7(
|
|
1006
1140
|
NeverLine,
|
|
@@ -1015,23 +1149,23 @@ function LearnMore() {
|
|
|
1015
1149
|
)
|
|
1016
1150
|
] }, item)),
|
|
1017
1151
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1018
|
-
/* @__PURE__ */ jsx7(
|
|
1152
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1019
1153
|
] }),
|
|
1020
|
-
/* @__PURE__ */ jsx7(
|
|
1021
|
-
/* @__PURE__ */ jsx7(
|
|
1022
|
-
/* @__PURE__ */ jsx7(
|
|
1154
|
+
/* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1155
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1156
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
|
|
1023
1157
|
] }, link.label)) }),
|
|
1024
|
-
/* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1026
|
-
/* @__PURE__ */ jsx7(
|
|
1027
|
-
/* @__PURE__ */ jsx7(
|
|
1028
|
-
/* @__PURE__ */ jsx7(
|
|
1158
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1159
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1160
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1161
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
|
|
1162
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
|
|
1029
1163
|
] }),
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
/* @__PURE__ */ jsx7(
|
|
1032
|
-
/* @__PURE__ */ jsx7(
|
|
1033
|
-
/* @__PURE__ */ jsx7(
|
|
1034
|
-
/* @__PURE__ */ jsx7(
|
|
1164
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1165
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1166
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "enter" }),
|
|
1167
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
|
|
1168
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1035
1169
|
] })
|
|
1036
1170
|
] })
|
|
1037
1171
|
]
|
|
@@ -1040,10 +1174,10 @@ function LearnMore() {
|
|
|
1040
1174
|
}
|
|
1041
1175
|
|
|
1042
1176
|
// src/ui/Sidebar.tsx
|
|
1043
|
-
import { Box as
|
|
1177
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1044
1178
|
|
|
1045
1179
|
// src/ui/Steps.tsx
|
|
1046
|
-
import { Box as
|
|
1180
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1047
1181
|
import Spinner from "ink-spinner";
|
|
1048
1182
|
|
|
1049
1183
|
// src/core/persistence.ts
|
|
@@ -1072,11 +1206,11 @@ async function clearWorkflowState(workflowId) {
|
|
|
1072
1206
|
}
|
|
1073
1207
|
|
|
1074
1208
|
// src/ui/Steps.tsx
|
|
1075
|
-
import { jsx as jsx8, jsxs as
|
|
1209
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1076
1210
|
function Steps() {
|
|
1077
1211
|
const { steps } = useWizard();
|
|
1078
1212
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1079
|
-
return /* @__PURE__ */ jsx8(
|
|
1213
|
+
return /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status[s.status], children: [
|
|
1080
1214
|
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1081
1215
|
" ",
|
|
1082
1216
|
s.title
|
|
@@ -1086,7 +1220,7 @@ function CurrentStep() {
|
|
|
1086
1220
|
const { steps } = useWizard();
|
|
1087
1221
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1088
1222
|
if (!currentStep) return null;
|
|
1089
|
-
return /* @__PURE__ */
|
|
1223
|
+
return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
|
|
1090
1224
|
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
1091
1225
|
" ",
|
|
1092
1226
|
` ${currentStep.title}`
|
|
@@ -1094,19 +1228,19 @@ function CurrentStep() {
|
|
|
1094
1228
|
}
|
|
1095
1229
|
|
|
1096
1230
|
// src/ui/Progress.tsx
|
|
1097
|
-
import { Box as
|
|
1098
|
-
import { jsx as jsx9, jsxs as
|
|
1231
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1232
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1099
1233
|
function Progress() {
|
|
1100
1234
|
const { steps, currentStepIndex } = useWizard();
|
|
1101
1235
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1102
1236
|
if (visibleSteps.length === 0) return null;
|
|
1103
1237
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1104
1238
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1105
|
-
return /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */ jsx9(
|
|
1107
|
-
/* @__PURE__ */ jsx9(
|
|
1108
|
-
/* @__PURE__ */ jsx9(
|
|
1109
|
-
/* @__PURE__ */ jsx9(
|
|
1239
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1240
|
+
/* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
|
|
1241
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
|
|
1242
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
|
|
1243
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
|
|
1110
1244
|
] });
|
|
1111
1245
|
}
|
|
1112
1246
|
|
|
@@ -1117,10 +1251,10 @@ var sidebarCommands = [
|
|
|
1117
1251
|
];
|
|
1118
1252
|
|
|
1119
1253
|
// src/ui/Sidebar.tsx
|
|
1120
|
-
import { jsx as jsx10, jsxs as
|
|
1254
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1121
1255
|
function Sidebar() {
|
|
1122
|
-
return /* @__PURE__ */
|
|
1123
|
-
|
|
1256
|
+
return /* @__PURE__ */ jsxs10(
|
|
1257
|
+
Box11,
|
|
1124
1258
|
{
|
|
1125
1259
|
backgroundColor: "#14171E",
|
|
1126
1260
|
width: 30,
|
|
@@ -1129,16 +1263,16 @@ function Sidebar() {
|
|
|
1129
1263
|
flexDirection: "column",
|
|
1130
1264
|
justifyContent: "space-between",
|
|
1131
1265
|
children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
/* @__PURE__ */ jsx10(
|
|
1266
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1267
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1134
1268
|
/* @__PURE__ */ jsx10(Steps, {})
|
|
1135
1269
|
] }),
|
|
1136
|
-
/* @__PURE__ */
|
|
1270
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1137
1271
|
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1138
|
-
/* @__PURE__ */ jsx10(
|
|
1139
|
-
return /* @__PURE__ */
|
|
1140
|
-
/* @__PURE__ */ jsx10(
|
|
1141
|
-
/* @__PURE__ */ jsx10(
|
|
1272
|
+
/* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1273
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1274
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1275
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
|
|
1142
1276
|
] });
|
|
1143
1277
|
}) })
|
|
1144
1278
|
] })
|
|
@@ -1148,12 +1282,12 @@ function Sidebar() {
|
|
|
1148
1282
|
}
|
|
1149
1283
|
|
|
1150
1284
|
// src/ui/Ribbon.tsx
|
|
1151
|
-
import { Box as
|
|
1152
|
-
import { jsx as jsx11, jsxs as
|
|
1285
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1286
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1153
1287
|
function Ribbon() {
|
|
1154
1288
|
const firstCommand = sidebarCommands[0];
|
|
1155
|
-
return /* @__PURE__ */
|
|
1156
|
-
|
|
1289
|
+
return /* @__PURE__ */ jsxs11(
|
|
1290
|
+
Box12,
|
|
1157
1291
|
{
|
|
1158
1292
|
backgroundColor: "#14171E",
|
|
1159
1293
|
flexDirection: "row",
|
|
@@ -1163,9 +1297,9 @@ function Ribbon() {
|
|
|
1163
1297
|
children: [
|
|
1164
1298
|
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1165
1299
|
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */ jsx11(
|
|
1168
|
-
/* @__PURE__ */ jsx11(
|
|
1300
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1301
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1302
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
|
|
1169
1303
|
] })
|
|
1170
1304
|
]
|
|
1171
1305
|
}
|
|
@@ -1177,8 +1311,8 @@ import { useState as useState6 } from "react";
|
|
|
1177
1311
|
|
|
1178
1312
|
// src/ui/Logs.tsx
|
|
1179
1313
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
|
|
1180
|
-
import { Box as
|
|
1181
|
-
import { jsx as jsx12, jsxs as
|
|
1314
|
+
import { Box as Box13, Text as Text13, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1315
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1182
1316
|
var KIND_COLOR = {
|
|
1183
1317
|
tool: COLORS.primary,
|
|
1184
1318
|
prompt: COLORS.badge
|
|
@@ -1208,7 +1342,7 @@ function formatTimestamp(ms) {
|
|
|
1208
1342
|
}
|
|
1209
1343
|
function Logs() {
|
|
1210
1344
|
const logs = useWizard((s) => s.logs);
|
|
1211
|
-
const { rows, columns } =
|
|
1345
|
+
const { rows, columns } = useWindowSize7();
|
|
1212
1346
|
const viewportRef = useRef3(null);
|
|
1213
1347
|
const [viewportHeight, setViewportHeight] = useState5(0);
|
|
1214
1348
|
const [viewportWidth, setViewportWidth] = useState5(0);
|
|
@@ -1245,10 +1379,10 @@ function Logs() {
|
|
|
1245
1379
|
const visible = logs.slice(scrollOffset, scrollOffset + capacity);
|
|
1246
1380
|
const hiddenAbove = scrollOffset;
|
|
1247
1381
|
const hiddenBelow = logs.length - scrollOffset - visible.length;
|
|
1248
|
-
return /* @__PURE__ */
|
|
1249
|
-
logs.length === 0 && /* @__PURE__ */ jsx12(
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
hiddenAbove > 0 && /* @__PURE__ */
|
|
1382
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1383
|
+
logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
|
|
1384
|
+
/* @__PURE__ */ jsxs12(Box13, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
1385
|
+
hiddenAbove > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1252
1386
|
"\u2191 ",
|
|
1253
1387
|
hiddenAbove,
|
|
1254
1388
|
" more"
|
|
@@ -1263,20 +1397,20 @@ function Logs() {
|
|
|
1263
1397
|
const name = truncate2(entry.name, budget);
|
|
1264
1398
|
budget -= name.length;
|
|
1265
1399
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1266
|
-
return /* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */ jsx12(
|
|
1268
|
-
/* @__PURE__ */ jsx12(
|
|
1269
|
-
preview && /* @__PURE__ */ jsx12(
|
|
1270
|
-
durationText && /* @__PURE__ */ jsx12(
|
|
1400
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1401
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
|
|
1402
|
+
/* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1403
|
+
preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1404
|
+
durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
|
|
1271
1405
|
] }, entry.id);
|
|
1272
1406
|
}),
|
|
1273
|
-
hiddenBelow > 0 && /* @__PURE__ */
|
|
1407
|
+
hiddenBelow > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1274
1408
|
"\u2193 ",
|
|
1275
1409
|
hiddenBelow,
|
|
1276
1410
|
" more"
|
|
1277
1411
|
] })
|
|
1278
1412
|
] }),
|
|
1279
|
-
/* @__PURE__ */ jsx12(
|
|
1413
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1280
1414
|
] });
|
|
1281
1415
|
}
|
|
1282
1416
|
|
|
@@ -1300,11 +1434,11 @@ function track(event, payload) {
|
|
|
1300
1434
|
}
|
|
1301
1435
|
|
|
1302
1436
|
// src/ui/App.tsx
|
|
1303
|
-
import { jsx as jsx13, jsxs as
|
|
1437
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1304
1438
|
function App() {
|
|
1305
1439
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1306
1440
|
const { exit } = useApp();
|
|
1307
|
-
const { columns, rows } =
|
|
1441
|
+
const { columns, rows } = useWindowSize8();
|
|
1308
1442
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1309
1443
|
const finished = phase === "done" || phase === "error";
|
|
1310
1444
|
const currentStep = steps[currentStepIndex];
|
|
@@ -1317,7 +1451,7 @@ function App() {
|
|
|
1317
1451
|
{ isActive: finished }
|
|
1318
1452
|
);
|
|
1319
1453
|
useInput6((_input, key) => {
|
|
1320
|
-
if (phase === "idle" || phase === "
|
|
1454
|
+
if (phase === "idle" || phase === "authenticating") return;
|
|
1321
1455
|
if (key.tab) {
|
|
1322
1456
|
setShowLogs(!showLogs);
|
|
1323
1457
|
track("AI Wizard Interaction", {
|
|
@@ -1327,7 +1461,7 @@ function App() {
|
|
|
1327
1461
|
});
|
|
1328
1462
|
}
|
|
1329
1463
|
});
|
|
1330
|
-
const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1464
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1331
1465
|
useInput6((_input, key) => {
|
|
1332
1466
|
if (escOwnedElsewhere) return;
|
|
1333
1467
|
if (key.escape) {
|
|
@@ -1340,53 +1474,67 @@ function App() {
|
|
|
1340
1474
|
exit();
|
|
1341
1475
|
}
|
|
1342
1476
|
});
|
|
1343
|
-
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1477
|
+
const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1344
1478
|
const flexDirection = columns > 90 ? "row" : "column";
|
|
1345
1479
|
const showSidebar = flexDirection === "row";
|
|
1346
|
-
return
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1480
|
+
return (
|
|
1481
|
+
/* Exactly the viewport, clipped — never `minHeight`, which lets the frame
|
|
1482
|
+
grow past the terminal. Ink then abandons diffing to clear and repaint
|
|
1483
|
+
the whole screen, and the scrolling that frame causes throws off its
|
|
1484
|
+
cursor arithmetic: flicker and leftover rows, worst when a burst of CLI
|
|
1485
|
+
output is swapped out. Clipping drops the bottom of an over-tall frame;
|
|
1486
|
+
the per-panel row budgets are what keep it from coming to that. */
|
|
1487
|
+
/* @__PURE__ */ jsxs13(
|
|
1488
|
+
Box14,
|
|
1489
|
+
{
|
|
1490
|
+
backgroundColor: COLORS.bg.main,
|
|
1491
|
+
flexDirection: "row",
|
|
1492
|
+
width: columns,
|
|
1493
|
+
height: rows,
|
|
1494
|
+
overflow: "hidden",
|
|
1495
|
+
children: [
|
|
1496
|
+
mainWindowVisible && /* @__PURE__ */ jsxs13(
|
|
1497
|
+
Box14,
|
|
1498
|
+
{
|
|
1499
|
+
flexDirection,
|
|
1500
|
+
width: "100%",
|
|
1501
|
+
justifyContent: "space-between",
|
|
1502
|
+
children: [
|
|
1503
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
|
|
1504
|
+
/* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
|
|
1505
|
+
/* @__PURE__ */ jsxs13(
|
|
1506
|
+
Box14,
|
|
1507
|
+
{
|
|
1508
|
+
flexDirection: "column",
|
|
1509
|
+
paddingX: 4,
|
|
1510
|
+
paddingY: 2,
|
|
1511
|
+
width: showSidebar ? 70 : "100%",
|
|
1512
|
+
flexGrow: showSidebar ? 1 : 0,
|
|
1513
|
+
children: [
|
|
1514
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
1515
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1516
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1517
|
+
] }),
|
|
1518
|
+
/* @__PURE__ */ jsx13(CliOutput, {}),
|
|
1519
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1520
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1521
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1522
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
|
|
1523
|
+
"\u2716 ",
|
|
1524
|
+
error
|
|
1525
|
+
] }) })
|
|
1526
|
+
]
|
|
1527
|
+
}
|
|
1528
|
+
)
|
|
1529
|
+
),
|
|
1530
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1531
|
+
]
|
|
1532
|
+
}
|
|
1533
|
+
),
|
|
1534
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1535
|
+
]
|
|
1536
|
+
}
|
|
1537
|
+
)
|
|
1390
1538
|
);
|
|
1391
1539
|
}
|
|
1392
1540
|
|
|
@@ -1784,61 +1932,138 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1784
1932
|
}
|
|
1785
1933
|
}
|
|
1786
1934
|
|
|
1787
|
-
// src/lib/
|
|
1788
|
-
import {
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1935
|
+
// src/lib/algoliaApp.ts
|
|
1936
|
+
import { z as z4 } from "zod";
|
|
1937
|
+
var applicationSchema = z4.object({
|
|
1938
|
+
id: z4.string().min(1),
|
|
1939
|
+
name: z4.string().default(""),
|
|
1940
|
+
plan: z4.string().optional()
|
|
1941
|
+
});
|
|
1942
|
+
var listSchema = z4.array(
|
|
1943
|
+
z4.object({
|
|
1944
|
+
id: z4.string().min(1),
|
|
1945
|
+
name: z4.string().default(""),
|
|
1946
|
+
plan_label: z4.string().optional()
|
|
1947
|
+
}).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
|
|
1948
|
+
);
|
|
1949
|
+
async function currentApplication() {
|
|
1950
|
+
let raw;
|
|
1800
1951
|
try {
|
|
1801
|
-
|
|
1952
|
+
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
1802
1953
|
} catch {
|
|
1803
|
-
return
|
|
1954
|
+
return null;
|
|
1955
|
+
}
|
|
1956
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
1957
|
+
return parsed.success ? parsed.data : null;
|
|
1958
|
+
}
|
|
1959
|
+
async function requireApplication() {
|
|
1960
|
+
const app2 = await currentApplication();
|
|
1961
|
+
if (!app2) {
|
|
1962
|
+
throw new Error(
|
|
1963
|
+
"No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
return app2;
|
|
1967
|
+
}
|
|
1968
|
+
async function listApplications() {
|
|
1969
|
+
const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
|
|
1970
|
+
const parsed = listSchema.safeParse(parseJson(raw));
|
|
1971
|
+
if (!parsed.success) {
|
|
1972
|
+
throw new Error("Could not read the list of Algolia applications.");
|
|
1973
|
+
}
|
|
1974
|
+
return parsed.data;
|
|
1975
|
+
}
|
|
1976
|
+
async function selectApplication(id) {
|
|
1977
|
+
const raw = await runAlgoliaCli(
|
|
1978
|
+
["application", "select", "--non-interactive", "--app-id", id],
|
|
1979
|
+
{ onOutput: stderrSink }
|
|
1980
|
+
);
|
|
1981
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
1982
|
+
if (!parsed.success) {
|
|
1983
|
+
throw new Error(
|
|
1984
|
+
`Selected application ${id}, but the Algolia CLI returned an unreadable result.`
|
|
1985
|
+
);
|
|
1804
1986
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
name,
|
|
1809
|
-
appId: t.application_id,
|
|
1810
|
-
apiKey: t.api_key,
|
|
1811
|
-
isDefault: t.default === true
|
|
1812
|
-
}));
|
|
1813
|
-
profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
|
|
1814
|
-
return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
|
|
1815
|
-
}
|
|
1816
|
-
async function loadActiveProfile() {
|
|
1817
|
-
let profiles;
|
|
1987
|
+
return parsed.data;
|
|
1988
|
+
}
|
|
1989
|
+
function parseJson(text) {
|
|
1818
1990
|
try {
|
|
1819
|
-
|
|
1991
|
+
return JSON.parse(text);
|
|
1820
1992
|
} catch {
|
|
1821
|
-
|
|
1993
|
+
return void 0;
|
|
1822
1994
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// src/lib/algoliaAppPicker.ts
|
|
1998
|
+
function secondaryFor(app2) {
|
|
1999
|
+
return app2.plan ? { kind: "badge", value: app2.plan } : void 0;
|
|
2000
|
+
}
|
|
2001
|
+
function labelFor(app2) {
|
|
2002
|
+
return app2.name.trim() ? `${app2.name} \u2014 ${app2.id}` : app2.id;
|
|
2003
|
+
}
|
|
2004
|
+
function selectAndReport(app2) {
|
|
2005
|
+
useWizard.getState().pushCliOutput(
|
|
2006
|
+
"stdout",
|
|
2007
|
+
`Selecting ${labelFor(app2)} \u2014 provisioning its API key\u2026`
|
|
2008
|
+
);
|
|
2009
|
+
return selectApplication(app2.id);
|
|
2010
|
+
}
|
|
2011
|
+
async function promptForApplication() {
|
|
2012
|
+
const store2 = useWizard.getState();
|
|
2013
|
+
const apps = await listApplications();
|
|
2014
|
+
if (apps.length === 0) {
|
|
1825
2015
|
throw new Error(
|
|
1826
|
-
"
|
|
2016
|
+
"This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
if (apps.length === 1) {
|
|
2020
|
+
const only = apps[0];
|
|
2021
|
+
logger.info(
|
|
2022
|
+
{ app: only.id },
|
|
2023
|
+
"single application on the account; selecting it"
|
|
1827
2024
|
);
|
|
2025
|
+
return selectAndReport(only);
|
|
1828
2026
|
}
|
|
1829
|
-
|
|
2027
|
+
const messages = ["Which Algolia application should the wizard work in?"];
|
|
2028
|
+
for (; ; ) {
|
|
2029
|
+
const choice = await store2.requestUserInput({
|
|
2030
|
+
prompt: "Select an application",
|
|
2031
|
+
promptType: "multipleChoice",
|
|
2032
|
+
options: apps.map(labelFor),
|
|
2033
|
+
secondary: apps.map(secondaryFor),
|
|
2034
|
+
messages
|
|
2035
|
+
});
|
|
2036
|
+
const chosen = apps.find((app2) => labelFor(app2) === choice);
|
|
2037
|
+
if (!chosen) {
|
|
2038
|
+
throw new Error("Application picker received an unexpected selection");
|
|
2039
|
+
}
|
|
2040
|
+
try {
|
|
2041
|
+
return await selectAndReport(chosen);
|
|
2042
|
+
} catch (err) {
|
|
2043
|
+
logger.warn(
|
|
2044
|
+
{ app: chosen.id, err: err.message },
|
|
2045
|
+
"application select failed; re-prompting"
|
|
2046
|
+
);
|
|
2047
|
+
messages.push(
|
|
2048
|
+
`Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
async function ensureApplication() {
|
|
2054
|
+
return await currentApplication() ?? await promptForApplication();
|
|
1830
2055
|
}
|
|
1831
2056
|
|
|
1832
2057
|
// src/workflows/default.ts
|
|
1833
|
-
import { z as
|
|
2058
|
+
import { z as z27 } from "zod";
|
|
1834
2059
|
|
|
1835
2060
|
// src/actions/listIndices.ts
|
|
1836
|
-
import { z as
|
|
1837
|
-
var indicesListSchema =
|
|
1838
|
-
items:
|
|
1839
|
-
|
|
1840
|
-
name:
|
|
1841
|
-
entries:
|
|
2061
|
+
import { z as z5 } from "zod";
|
|
2062
|
+
var indicesListSchema = z5.object({
|
|
2063
|
+
items: z5.array(
|
|
2064
|
+
z5.object({
|
|
2065
|
+
name: z5.string(),
|
|
2066
|
+
entries: z5.number().default(0)
|
|
1842
2067
|
})
|
|
1843
2068
|
)
|
|
1844
2069
|
});
|
|
@@ -1909,12 +2134,12 @@ import "zod";
|
|
|
1909
2134
|
|
|
1910
2135
|
// src/lib/tools/listFiles.ts
|
|
1911
2136
|
import { tool } from "ai";
|
|
1912
|
-
import
|
|
2137
|
+
import z6 from "zod";
|
|
1913
2138
|
import { readdir } from "node:fs/promises";
|
|
1914
2139
|
|
|
1915
2140
|
// src/lib/tools/path.ts
|
|
1916
2141
|
import { lstat } from "node:fs/promises";
|
|
1917
|
-
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as
|
|
2142
|
+
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
|
|
1918
2143
|
function resolveInRoot(ctx, path) {
|
|
1919
2144
|
const target = resolve2(ctx.cwd, path);
|
|
1920
2145
|
const rel = relative(ctx.root, target);
|
|
@@ -1930,7 +2155,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1930
2155
|
let current = ctx.root;
|
|
1931
2156
|
const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
|
|
1932
2157
|
for (const part of parts) {
|
|
1933
|
-
current =
|
|
2158
|
+
current = join6(current, part);
|
|
1934
2159
|
try {
|
|
1935
2160
|
if ((await lstat(current)).isSymbolicLink()) return true;
|
|
1936
2161
|
} catch (err) {
|
|
@@ -1945,7 +2170,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1945
2170
|
function listFilesTool(ctx) {
|
|
1946
2171
|
return tool({
|
|
1947
2172
|
description: "List files in the current working directory",
|
|
1948
|
-
inputSchema:
|
|
2173
|
+
inputSchema: z6.object(),
|
|
1949
2174
|
execute: async () => {
|
|
1950
2175
|
logger.info("called listFiles tool");
|
|
1951
2176
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
@@ -1961,13 +2186,13 @@ function listFilesTool(ctx) {
|
|
|
1961
2186
|
|
|
1962
2187
|
// src/lib/tools/changeDirectory.ts
|
|
1963
2188
|
import { tool as tool2 } from "ai";
|
|
1964
|
-
import
|
|
2189
|
+
import z7 from "zod";
|
|
1965
2190
|
import { stat } from "node:fs/promises";
|
|
1966
2191
|
function changeDirectoryTool(ctx) {
|
|
1967
2192
|
return tool2({
|
|
1968
2193
|
description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
|
|
1969
|
-
inputSchema:
|
|
1970
|
-
path:
|
|
2194
|
+
inputSchema: z7.object({
|
|
2195
|
+
path: z7.string().describe("Directory to change into")
|
|
1971
2196
|
}),
|
|
1972
2197
|
execute: async ({ path }) => {
|
|
1973
2198
|
logger.info({ path }, "called changeDirectory tool");
|
|
@@ -1989,13 +2214,13 @@ function changeDirectoryTool(ctx) {
|
|
|
1989
2214
|
|
|
1990
2215
|
// src/lib/tools/reportStatus.ts
|
|
1991
2216
|
import { tool as tool3 } from "ai";
|
|
1992
|
-
import
|
|
2217
|
+
import z8 from "zod";
|
|
1993
2218
|
function reportStatusTool(output) {
|
|
1994
2219
|
return tool3({
|
|
1995
2220
|
description: "Report the status of your execution. Return a reason in case of failure.",
|
|
1996
|
-
inputSchema:
|
|
1997
|
-
status:
|
|
1998
|
-
reason:
|
|
2221
|
+
inputSchema: z8.object({
|
|
2222
|
+
status: z8.enum(["success", "fail"]),
|
|
2223
|
+
reason: z8.string().optional(),
|
|
1999
2224
|
output
|
|
2000
2225
|
}),
|
|
2001
2226
|
execute: async ({ status, reason, output: output2 }) => {
|
|
@@ -2007,8 +2232,8 @@ function reportStatusTool(output) {
|
|
|
2007
2232
|
|
|
2008
2233
|
// src/lib/tools/readFile.ts
|
|
2009
2234
|
import { tool as tool4 } from "ai";
|
|
2010
|
-
import
|
|
2011
|
-
import { readFile as
|
|
2235
|
+
import z9 from "zod";
|
|
2236
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2012
2237
|
|
|
2013
2238
|
// src/lib/tools/env.ts
|
|
2014
2239
|
import { basename } from "node:path";
|
|
@@ -2035,8 +2260,8 @@ function redactEnvValues(content) {
|
|
|
2035
2260
|
function readFileTool(ctx) {
|
|
2036
2261
|
return tool4({
|
|
2037
2262
|
description: "Read the contents of a file at the given path",
|
|
2038
|
-
inputSchema:
|
|
2039
|
-
filePath:
|
|
2263
|
+
inputSchema: z9.object({
|
|
2264
|
+
filePath: z9.string().describe("Path to the file to read")
|
|
2040
2265
|
}),
|
|
2041
2266
|
execute: async ({ filePath }) => {
|
|
2042
2267
|
if (++ctx.counts.read > ctx.limits.read) {
|
|
@@ -2046,7 +2271,7 @@ function readFileTool(ctx) {
|
|
|
2046
2271
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2047
2272
|
if (!resolved.ok) return resolved.error;
|
|
2048
2273
|
try {
|
|
2049
|
-
const content = await
|
|
2274
|
+
const content = await readFile3(resolved.target, "utf8");
|
|
2050
2275
|
return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
|
|
2051
2276
|
} catch (err) {
|
|
2052
2277
|
return `Error reading ${filePath}: ${err.message}`;
|
|
@@ -2057,15 +2282,15 @@ function readFileTool(ctx) {
|
|
|
2057
2282
|
|
|
2058
2283
|
// src/lib/tools/writeFile.ts
|
|
2059
2284
|
import { tool as tool5 } from "ai";
|
|
2060
|
-
import
|
|
2285
|
+
import z10 from "zod";
|
|
2061
2286
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2062
2287
|
import { dirname as dirname4 } from "node:path";
|
|
2063
2288
|
function writeFileTool(ctx) {
|
|
2064
2289
|
return tool5({
|
|
2065
2290
|
description: "Write content to a file at the given path, overwriting it. To set Algolia credentials in an env file, use writeCredentials instead of this tool.",
|
|
2066
|
-
inputSchema:
|
|
2067
|
-
filePath:
|
|
2068
|
-
content:
|
|
2291
|
+
inputSchema: z10.object({
|
|
2292
|
+
filePath: z10.string().describe("Path to the file to write"),
|
|
2293
|
+
content: z10.string().describe("Content to write to the file")
|
|
2069
2294
|
}),
|
|
2070
2295
|
execute: async ({ filePath, content }) => {
|
|
2071
2296
|
logger.info({ filePath }, "called writeFile tool");
|
|
@@ -2090,9 +2315,95 @@ function writeFileTool(ctx) {
|
|
|
2090
2315
|
|
|
2091
2316
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2092
2317
|
import { tool as tool6 } from "ai";
|
|
2093
|
-
import
|
|
2094
|
-
import { mkdir as mkdir4, readFile as
|
|
2318
|
+
import z12 from "zod";
|
|
2319
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2095
2320
|
import { dirname as dirname5 } from "node:path";
|
|
2321
|
+
|
|
2322
|
+
// src/lib/algoliaApiKey.ts
|
|
2323
|
+
import { z as z11 } from "zod";
|
|
2324
|
+
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
2325
|
+
var WRITE_ACLS = [
|
|
2326
|
+
"addObject",
|
|
2327
|
+
"deleteObject",
|
|
2328
|
+
"settings",
|
|
2329
|
+
"editSettings",
|
|
2330
|
+
"listIndexes"
|
|
2331
|
+
];
|
|
2332
|
+
var WRITE_ACL_SET = new Set(WRITE_ACLS);
|
|
2333
|
+
var apiKeySchema = z11.object({
|
|
2334
|
+
value: z11.string().min(1),
|
|
2335
|
+
acl: z11.array(z11.string()).default([]),
|
|
2336
|
+
indexes: z11.array(z11.string()).default([])
|
|
2337
|
+
});
|
|
2338
|
+
var apiKeyListSchema = z11.object({
|
|
2339
|
+
items: z11.array(apiKeySchema).optional(),
|
|
2340
|
+
keys: z11.array(apiKeySchema).optional()
|
|
2341
|
+
}).transform((o) => o.items ?? o.keys ?? []);
|
|
2342
|
+
var createdKeySchema = z11.object({
|
|
2343
|
+
key: z11.string().min(1).optional(),
|
|
2344
|
+
value: z11.string().min(1).optional()
|
|
2345
|
+
});
|
|
2346
|
+
function canReuse(key, index) {
|
|
2347
|
+
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
2348
|
+
}
|
|
2349
|
+
async function createSearchKey(index) {
|
|
2350
|
+
const stdout = await runAlgoliaCli([
|
|
2351
|
+
"apikeys",
|
|
2352
|
+
"create",
|
|
2353
|
+
"--indices",
|
|
2354
|
+
index,
|
|
2355
|
+
"--acl",
|
|
2356
|
+
"search,browse",
|
|
2357
|
+
"--description",
|
|
2358
|
+
`wizard search-only key for ${index}`,
|
|
2359
|
+
"-o",
|
|
2360
|
+
"json"
|
|
2361
|
+
]);
|
|
2362
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
|
|
2363
|
+
const created = key ?? value;
|
|
2364
|
+
if (!created) throw new Error("apikeys create returned no key value");
|
|
2365
|
+
return created;
|
|
2366
|
+
}
|
|
2367
|
+
function canReuseForWrites(key, index) {
|
|
2368
|
+
return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
|
|
2369
|
+
}
|
|
2370
|
+
async function resolveWriteKey(index) {
|
|
2371
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2372
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
|
|
2373
|
+
if (existing) {
|
|
2374
|
+
logger.info({ index }, "reusing existing write API key");
|
|
2375
|
+
return existing;
|
|
2376
|
+
}
|
|
2377
|
+
logger.info({ index }, "no reusable write key found; creating one");
|
|
2378
|
+
const created = await runAlgoliaCli([
|
|
2379
|
+
"apikeys",
|
|
2380
|
+
"create",
|
|
2381
|
+
"--indices",
|
|
2382
|
+
index,
|
|
2383
|
+
"--acl",
|
|
2384
|
+
WRITE_ACLS.join(","),
|
|
2385
|
+
"--description",
|
|
2386
|
+
`wizard write key for ${index}`,
|
|
2387
|
+
"-o",
|
|
2388
|
+
"json"
|
|
2389
|
+
]);
|
|
2390
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(created));
|
|
2391
|
+
const writeKey = key ?? value;
|
|
2392
|
+
if (!writeKey) throw new Error("apikeys create returned no key value");
|
|
2393
|
+
return writeKey;
|
|
2394
|
+
}
|
|
2395
|
+
async function resolveSearchOnlyKey(index) {
|
|
2396
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2397
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
|
|
2398
|
+
if (existing) {
|
|
2399
|
+
logger.info({ index }, "reusing existing search-only API key");
|
|
2400
|
+
return existing;
|
|
2401
|
+
}
|
|
2402
|
+
logger.info({ index }, "no reusable search-only key found; creating one");
|
|
2403
|
+
return createSearchKey(index);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2096
2407
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2097
2408
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
2098
2409
|
function appendEnv(content, entries) {
|
|
@@ -2106,9 +2417,9 @@ function hasEnv(content, name) {
|
|
|
2106
2417
|
}
|
|
2107
2418
|
function writeCredentialsTool(ctx) {
|
|
2108
2419
|
return tool6({
|
|
2109
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials
|
|
2110
|
-
inputSchema:
|
|
2111
|
-
filePath:
|
|
2420
|
+
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file (e.g. ".env"). If the file already defines ${APP_ID_VAR} or ${API_KEY_VAR}, the write is skipped and existing values are left untouched.`,
|
|
2421
|
+
inputSchema: z12.object({
|
|
2422
|
+
filePath: z12.string().describe(
|
|
2112
2423
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2113
2424
|
)
|
|
2114
2425
|
}),
|
|
@@ -2116,11 +2427,17 @@ function writeCredentialsTool(ctx) {
|
|
|
2116
2427
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2117
2428
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2118
2429
|
if (resolved.ok === false) return resolved.error;
|
|
2119
|
-
|
|
2430
|
+
const targetIndex = useWizard.getState().targetIndex;
|
|
2431
|
+
if (!targetIndex) {
|
|
2432
|
+
return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
|
|
2433
|
+
}
|
|
2434
|
+
let appId;
|
|
2435
|
+
let writeKey;
|
|
2120
2436
|
try {
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2437
|
+
appId = (await requireApplication()).id;
|
|
2438
|
+
writeKey = await resolveWriteKey(targetIndex);
|
|
2439
|
+
} catch (err) {
|
|
2440
|
+
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2124
2441
|
}
|
|
2125
2442
|
try {
|
|
2126
2443
|
if (await hasSymlinkParent(ctx, resolved.target)) {
|
|
@@ -2128,7 +2445,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2128
2445
|
}
|
|
2129
2446
|
let existing = "";
|
|
2130
2447
|
try {
|
|
2131
|
-
existing = await
|
|
2448
|
+
existing = await readFile4(resolved.target, "utf8");
|
|
2132
2449
|
} catch (err) {
|
|
2133
2450
|
if (err.code !== "ENOENT") throw err;
|
|
2134
2451
|
}
|
|
@@ -2139,8 +2456,8 @@ function writeCredentialsTool(ctx) {
|
|
|
2139
2456
|
return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
|
|
2140
2457
|
}
|
|
2141
2458
|
const envWithCredentials = appendEnv(existing, [
|
|
2142
|
-
[APP_ID_VAR,
|
|
2143
|
-
[API_KEY_VAR,
|
|
2459
|
+
[APP_ID_VAR, appId],
|
|
2460
|
+
[API_KEY_VAR, writeKey]
|
|
2144
2461
|
]);
|
|
2145
2462
|
await mkdir4(dirname5(resolved.target), { recursive: true });
|
|
2146
2463
|
await writeFile4(resolved.target, envWithCredentials, "utf8");
|
|
@@ -2154,16 +2471,16 @@ function writeCredentialsTool(ctx) {
|
|
|
2154
2471
|
|
|
2155
2472
|
// src/lib/tools/searchFiles.ts
|
|
2156
2473
|
import { tool as tool7 } from "ai";
|
|
2157
|
-
import
|
|
2158
|
-
import { readdir as readdir2, readFile as
|
|
2159
|
-
import { join as
|
|
2474
|
+
import z13 from "zod";
|
|
2475
|
+
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2476
|
+
import { join as join7 } from "node:path";
|
|
2160
2477
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2161
2478
|
async function walkFiles(dir) {
|
|
2162
2479
|
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2163
2480
|
const out = [];
|
|
2164
2481
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2165
2482
|
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2166
|
-
const full =
|
|
2483
|
+
const full = join7(dir, e.name);
|
|
2167
2484
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2168
2485
|
else if (e.isFile()) out.push(full);
|
|
2169
2486
|
}
|
|
@@ -2172,9 +2489,9 @@ async function walkFiles(dir) {
|
|
|
2172
2489
|
function searchFilesTool(ctx) {
|
|
2173
2490
|
return tool7({
|
|
2174
2491
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2175
|
-
inputSchema:
|
|
2176
|
-
query:
|
|
2177
|
-
path:
|
|
2492
|
+
inputSchema: z13.object({
|
|
2493
|
+
query: z13.string().describe("JavaScript RegExp pattern to search for"),
|
|
2494
|
+
path: z13.string().optional().describe("Directory to search in (default: cwd)")
|
|
2178
2495
|
}),
|
|
2179
2496
|
execute: async ({ query, path = "." }) => {
|
|
2180
2497
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2196,7 +2513,7 @@ function searchFilesTool(ctx) {
|
|
|
2196
2513
|
for (const file of await walkFiles(resolved.target)) {
|
|
2197
2514
|
let content;
|
|
2198
2515
|
try {
|
|
2199
|
-
content = await
|
|
2516
|
+
content = await readFile5(file, "utf8");
|
|
2200
2517
|
} catch {
|
|
2201
2518
|
continue;
|
|
2202
2519
|
}
|
|
@@ -2218,7 +2535,7 @@ function searchFilesTool(ctx) {
|
|
|
2218
2535
|
|
|
2219
2536
|
// src/lib/tools/verifyImplementation.ts
|
|
2220
2537
|
import { tool as tool8 } from "ai";
|
|
2221
|
-
import
|
|
2538
|
+
import z14 from "zod";
|
|
2222
2539
|
|
|
2223
2540
|
// src/lib/tools/utils/runCommand.ts
|
|
2224
2541
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -2240,9 +2557,9 @@ function runCommand(command, args, cwd) {
|
|
|
2240
2557
|
}
|
|
2241
2558
|
|
|
2242
2559
|
// src/lib/tools/utils/packageManager.ts
|
|
2243
|
-
import { readFile as
|
|
2560
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2244
2561
|
import { existsSync } from "node:fs";
|
|
2245
|
-
import { join as
|
|
2562
|
+
import { join as join8 } from "node:path";
|
|
2246
2563
|
var LOCKFILES = [
|
|
2247
2564
|
["pnpm-lock.yaml", "pnpm"],
|
|
2248
2565
|
["yarn.lock", "yarn"],
|
|
@@ -2251,13 +2568,13 @@ var LOCKFILES = [
|
|
|
2251
2568
|
["package-lock.json", "npm"]
|
|
2252
2569
|
];
|
|
2253
2570
|
async function readPackageJson(cwd = process.cwd()) {
|
|
2254
|
-
return JSON.parse(await
|
|
2571
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2255
2572
|
}
|
|
2256
2573
|
function packageManagerFrom(pkg) {
|
|
2257
2574
|
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2258
2575
|
}
|
|
2259
2576
|
function packageManagerFromLockfile(cwd) {
|
|
2260
|
-
return LOCKFILES.find(([file]) => existsSync(
|
|
2577
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2261
2578
|
}
|
|
2262
2579
|
async function detectPackageManager(cwd) {
|
|
2263
2580
|
try {
|
|
@@ -2298,7 +2615,7 @@ async function runRepoVerificationCheck() {
|
|
|
2298
2615
|
function verifyImplementationTool() {
|
|
2299
2616
|
return tool8({
|
|
2300
2617
|
description: "Run the repo's mechanical verification check for generated implementation changes. Detects lint/typecheck/check from package.json and returns structured pass/fail evidence for the verifier to interpret.",
|
|
2301
|
-
inputSchema:
|
|
2618
|
+
inputSchema: z14.object(),
|
|
2302
2619
|
execute: async () => {
|
|
2303
2620
|
logger.info("called verifyImplementation tool");
|
|
2304
2621
|
return runRepoVerificationCheck();
|
|
@@ -2312,7 +2629,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2312
2629
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2313
2630
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2314
2631
|
import { dirname as dirname6 } from "node:path";
|
|
2315
|
-
import
|
|
2632
|
+
import z15 from "zod";
|
|
2316
2633
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2317
2634
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2318
2635
|
var MAX_RECORDS = 100;
|
|
@@ -2324,17 +2641,17 @@ var anthropic = createAnthropic({
|
|
|
2324
2641
|
function generateRecordTool(ctx) {
|
|
2325
2642
|
return tool9({
|
|
2326
2643
|
description: "Generate realistic sample records for an entity and write them to a JSON file in the worktree. Provide the entity name and its attributes; this tool asks a model to invent varied, realistic values, each with a unique objectID, and returns the file path to read them from at runtime. Do not invent the record values or objectIDs yourself, and do not inline the returned records into the script \u2014 call this tool and read the file it writes.",
|
|
2327
|
-
inputSchema:
|
|
2328
|
-
entityName:
|
|
2329
|
-
attributes:
|
|
2330
|
-
count:
|
|
2331
|
-
hint:
|
|
2644
|
+
inputSchema: z15.object({
|
|
2645
|
+
entityName: z15.string().describe("Name of the entity to generate records for."),
|
|
2646
|
+
attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
|
|
2647
|
+
count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
2648
|
+
hint: z15.string().optional().describe("Optional context to steer realistic values.")
|
|
2332
2649
|
}),
|
|
2333
2650
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2334
2651
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2335
2652
|
try {
|
|
2336
|
-
const value =
|
|
2337
|
-
const recordSchema =
|
|
2653
|
+
const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
|
|
2654
|
+
const recordSchema = z15.object(
|
|
2338
2655
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2339
2656
|
);
|
|
2340
2657
|
const generateBatch = async (batchCount) => {
|
|
@@ -2344,8 +2661,8 @@ function generateRecordTool(ctx) {
|
|
|
2344
2661
|
const { output } = await generateText({
|
|
2345
2662
|
model: anthropic(RECORD_MODEL),
|
|
2346
2663
|
output: Output.object({
|
|
2347
|
-
schema:
|
|
2348
|
-
records:
|
|
2664
|
+
schema: z15.object({
|
|
2665
|
+
records: z15.array(recordSchema).length(batchCount)
|
|
2349
2666
|
})
|
|
2350
2667
|
}),
|
|
2351
2668
|
prompt: [
|
|
@@ -2403,12 +2720,12 @@ function generateRecordTool(ctx) {
|
|
|
2403
2720
|
|
|
2404
2721
|
// src/lib/tools/notifyUser.ts
|
|
2405
2722
|
import { tool as tool10 } from "ai";
|
|
2406
|
-
import
|
|
2723
|
+
import z16 from "zod";
|
|
2407
2724
|
function notifyUserTool() {
|
|
2408
2725
|
return tool10({
|
|
2409
2726
|
description: `Give the user a brief, high-level update on what you are currently doing or about to do next. This is for the big picture (e.g. "Reading through your data models", "Writing the search UI") \u2014 not granular detail like individual tool calls, which are already logged separately. Call it when you start a new phase of work or your focus shifts, just not on every step, enough to keep the user engaged. Don't say things like "starting", just describe what you are doing. Don't mention tool calls themselves, just general direction of the work.`,
|
|
2410
|
-
inputSchema:
|
|
2411
|
-
message:
|
|
2727
|
+
inputSchema: z16.object({
|
|
2728
|
+
message: z16.string().describe(
|
|
2412
2729
|
"Short, plain-language description of what you are doing now."
|
|
2413
2730
|
)
|
|
2414
2731
|
}),
|
|
@@ -2588,10 +2905,10 @@ async function runAgent(req) {
|
|
|
2588
2905
|
}
|
|
2589
2906
|
|
|
2590
2907
|
// src/actions/detectLanguage.ts
|
|
2591
|
-
import
|
|
2592
|
-
var detectLanguageSchema =
|
|
2593
|
-
languages:
|
|
2594
|
-
frameworks:
|
|
2908
|
+
import z19 from "zod";
|
|
2909
|
+
var detectLanguageSchema = z19.object({
|
|
2910
|
+
languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
|
|
2911
|
+
frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
|
|
2595
2912
|
});
|
|
2596
2913
|
var detectLanguage = () => runAgent({
|
|
2597
2914
|
instructions: [
|
|
@@ -2609,31 +2926,31 @@ var detectLanguage = () => runAgent({
|
|
|
2609
2926
|
});
|
|
2610
2927
|
|
|
2611
2928
|
// src/actions/analyzeCodebase.ts
|
|
2612
|
-
import
|
|
2929
|
+
import z20 from "zod";
|
|
2613
2930
|
var READONLY_TOOLS = [
|
|
2614
2931
|
"listFiles",
|
|
2615
2932
|
"changeDirectory",
|
|
2616
2933
|
"readFile",
|
|
2617
2934
|
"searchFiles"
|
|
2618
2935
|
];
|
|
2619
|
-
var ingestionAnalysisSchema =
|
|
2620
|
-
ingestionAnalysis:
|
|
2621
|
-
|
|
2622
|
-
name:
|
|
2623
|
-
paths:
|
|
2936
|
+
var ingestionAnalysisSchema = z20.object({
|
|
2937
|
+
ingestionAnalysis: z20.array(
|
|
2938
|
+
z20.object({
|
|
2939
|
+
name: z20.string(),
|
|
2940
|
+
paths: z20.array(z20.string()),
|
|
2624
2941
|
// indexable fields the agent found for this entity
|
|
2625
|
-
attributes:
|
|
2942
|
+
attributes: z20.array(z20.string())
|
|
2626
2943
|
})
|
|
2627
2944
|
)
|
|
2628
2945
|
});
|
|
2629
|
-
var searchImplementationAnalysisSchema =
|
|
2630
|
-
searchImplementationAnalysis:
|
|
2946
|
+
var searchImplementationAnalysisSchema = z20.object({
|
|
2947
|
+
searchImplementationAnalysis: z20.string()
|
|
2631
2948
|
});
|
|
2632
|
-
var verificationSchema =
|
|
2633
|
-
verification:
|
|
2949
|
+
var verificationSchema = z20.object({
|
|
2950
|
+
verification: z20.array(z20.string())
|
|
2634
2951
|
});
|
|
2635
2952
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
2636
|
-
var analyzeCodebaseSchema =
|
|
2953
|
+
var analyzeCodebaseSchema = z20.object({
|
|
2637
2954
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2638
2955
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
2639
2956
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -2695,7 +3012,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2695
3012
|
// package.json
|
|
2696
3013
|
var package_default = {
|
|
2697
3014
|
name: "@algolia/wizard",
|
|
2698
|
-
version: "0.
|
|
3015
|
+
version: "0.7.0-rc.53.39",
|
|
2699
3016
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2700
3017
|
type: "module",
|
|
2701
3018
|
engines: {
|
|
@@ -2743,7 +3060,6 @@ var package_default = {
|
|
|
2743
3060
|
dependencies: {
|
|
2744
3061
|
"@ai-sdk/anthropic": "^3.0.81",
|
|
2745
3062
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
2746
|
-
"@algolia/cli": "^5.11.0",
|
|
2747
3063
|
"@hono/node-server": "^2.0.10",
|
|
2748
3064
|
"@mishieck/ink-titled-box": "^0.4.2",
|
|
2749
3065
|
"@segment/analytics-node": "^3.1.0",
|
|
@@ -2758,7 +3074,6 @@ var package_default = {
|
|
|
2758
3074
|
nanoid: "^5.1.15",
|
|
2759
3075
|
pino: "^10.3.1",
|
|
2760
3076
|
react: "^19.2.7",
|
|
2761
|
-
toml: "^4.1.1",
|
|
2762
3077
|
varlock: "^1.5.1",
|
|
2763
3078
|
zod: "^4.4.3",
|
|
2764
3079
|
zustand: "^5.0.14"
|
|
@@ -2816,8 +3131,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
2816
3131
|
}
|
|
2817
3132
|
|
|
2818
3133
|
// src/actions/confirmLanguage.ts
|
|
2819
|
-
import
|
|
2820
|
-
var confirmLanguageSchema =
|
|
3134
|
+
import z22 from "zod";
|
|
3135
|
+
var confirmLanguageSchema = z22.object({
|
|
2821
3136
|
languages: detectLanguageSchema.shape.languages
|
|
2822
3137
|
});
|
|
2823
3138
|
async function confirmLanguage(ctx) {
|
|
@@ -2838,8 +3153,8 @@ async function confirmLanguage(ctx) {
|
|
|
2838
3153
|
}
|
|
2839
3154
|
|
|
2840
3155
|
// src/actions/confirmFramework.ts
|
|
2841
|
-
import
|
|
2842
|
-
var confirmFrameworkSchema =
|
|
3156
|
+
import z23 from "zod";
|
|
3157
|
+
var confirmFrameworkSchema = z23.object({
|
|
2843
3158
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
2844
3159
|
});
|
|
2845
3160
|
var CURATED_FRAMEWORKS = [
|
|
@@ -2967,8 +3282,8 @@ async function promptUser(ctx, params) {
|
|
|
2967
3282
|
}
|
|
2968
3283
|
|
|
2969
3284
|
// src/actions/confirmEntities.ts
|
|
2970
|
-
import
|
|
2971
|
-
var confirmEntitiesSchema =
|
|
3285
|
+
import z24 from "zod";
|
|
3286
|
+
var confirmEntitiesSchema = z24.object({
|
|
2972
3287
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
2973
3288
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2974
3289
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3038,15 +3353,15 @@ async function confirmEntities(ctx) {
|
|
|
3038
3353
|
}
|
|
3039
3354
|
|
|
3040
3355
|
// src/actions/review.ts
|
|
3041
|
-
import { z as
|
|
3042
|
-
var reviewSchema =
|
|
3356
|
+
import { z as z25 } from "zod";
|
|
3357
|
+
var reviewSchema = z25.object({
|
|
3043
3358
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3044
3359
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3045
3360
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3046
3361
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3047
|
-
summaryPoints:
|
|
3048
|
-
reviewPrompt:
|
|
3049
|
-
nextSteps:
|
|
3362
|
+
summaryPoints: z25.array(z25.string()),
|
|
3363
|
+
reviewPrompt: z25.string(),
|
|
3364
|
+
nextSteps: z25.array(z25.string())
|
|
3050
3365
|
});
|
|
3051
3366
|
function formatCompletedSteps(steps) {
|
|
3052
3367
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3097,16 +3412,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3097
3412
|
};
|
|
3098
3413
|
|
|
3099
3414
|
// src/actions/implement.ts
|
|
3100
|
-
import
|
|
3415
|
+
import z26 from "zod";
|
|
3101
3416
|
|
|
3102
3417
|
// src/lib/worktree.ts
|
|
3103
3418
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3104
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3419
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3105
3420
|
import {
|
|
3106
3421
|
basename as basename2,
|
|
3107
3422
|
dirname as dirname7,
|
|
3108
3423
|
isAbsolute as isAbsolute2,
|
|
3109
|
-
join as
|
|
3424
|
+
join as join9,
|
|
3110
3425
|
relative as relative2,
|
|
3111
3426
|
resolve as resolve3
|
|
3112
3427
|
} from "node:path";
|
|
@@ -3140,7 +3455,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3140
3455
|
return out.trim().length > 0;
|
|
3141
3456
|
}
|
|
3142
3457
|
async function pruneOldWorktrees(repoRoot) {
|
|
3143
|
-
const dir =
|
|
3458
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3144
3459
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3145
3460
|
for (const slug of stale) {
|
|
3146
3461
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3151,7 +3466,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3151
3466
|
"worktree",
|
|
3152
3467
|
"remove",
|
|
3153
3468
|
"--force",
|
|
3154
|
-
|
|
3469
|
+
join9(dir, slug)
|
|
3155
3470
|
]);
|
|
3156
3471
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3157
3472
|
} catch (err) {
|
|
@@ -3165,7 +3480,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3165
3480
|
async function createWorktree(repoRoot) {
|
|
3166
3481
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3167
3482
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3168
|
-
const path =
|
|
3483
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3169
3484
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3170
3485
|
await pruneOldWorktrees(repoRoot);
|
|
3171
3486
|
await mkdir6(dirname7(path), { recursive: true });
|
|
@@ -3285,8 +3600,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3285
3600
|
} catch {
|
|
3286
3601
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3287
3602
|
}
|
|
3288
|
-
const relPath =
|
|
3289
|
-
const dest =
|
|
3603
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3604
|
+
const dest = join9(worktreePath, relPath);
|
|
3290
3605
|
try {
|
|
3291
3606
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3292
3607
|
await copyFile(source, dest);
|
|
@@ -3302,10 +3617,10 @@ function hasEnvVar(content, name) {
|
|
|
3302
3617
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
3303
3618
|
}
|
|
3304
3619
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3305
|
-
const target =
|
|
3620
|
+
const target = join9(worktreePath, ".env");
|
|
3306
3621
|
let existing = "";
|
|
3307
3622
|
try {
|
|
3308
|
-
existing = await
|
|
3623
|
+
existing = await readFile7(target, "utf8");
|
|
3309
3624
|
} catch (err) {
|
|
3310
3625
|
if (err.code !== "ENOENT") throw err;
|
|
3311
3626
|
}
|
|
@@ -3373,63 +3688,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3373
3688
|
}
|
|
3374
3689
|
}
|
|
3375
3690
|
|
|
3376
|
-
// src/lib/algoliaApiKey.ts
|
|
3377
|
-
import { z as z23 } from "zod";
|
|
3378
|
-
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
3379
|
-
var apiKeySchema = z23.object({
|
|
3380
|
-
value: z23.string().min(1),
|
|
3381
|
-
acl: z23.array(z23.string()).default([]),
|
|
3382
|
-
indexes: z23.array(z23.string()).default([])
|
|
3383
|
-
});
|
|
3384
|
-
var apiKeyListSchema = z23.object({
|
|
3385
|
-
items: z23.array(apiKeySchema).optional(),
|
|
3386
|
-
keys: z23.array(apiKeySchema).optional()
|
|
3387
|
-
}).transform((o) => o.items ?? o.keys ?? []);
|
|
3388
|
-
var createdKeySchema = z23.object({
|
|
3389
|
-
key: z23.string().min(1).optional(),
|
|
3390
|
-
value: z23.string().min(1).optional()
|
|
3391
|
-
});
|
|
3392
|
-
function canReuse(key, index) {
|
|
3393
|
-
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
3394
|
-
}
|
|
3395
|
-
async function createSearchKey(index) {
|
|
3396
|
-
const stdout = await runAlgoliaCli([
|
|
3397
|
-
"apikeys",
|
|
3398
|
-
"create",
|
|
3399
|
-
"--indices",
|
|
3400
|
-
index,
|
|
3401
|
-
"--acl",
|
|
3402
|
-
"search,browse",
|
|
3403
|
-
"--description",
|
|
3404
|
-
`wizard search-only key for ${index}`,
|
|
3405
|
-
"-o",
|
|
3406
|
-
"json"
|
|
3407
|
-
]);
|
|
3408
|
-
const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
|
|
3409
|
-
const created = key ?? value;
|
|
3410
|
-
if (!created) throw new Error("apikeys create returned no key value");
|
|
3411
|
-
return created;
|
|
3412
|
-
}
|
|
3413
|
-
async function resolveSearchOnlyKey(index) {
|
|
3414
|
-
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
3415
|
-
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
|
|
3416
|
-
if (existing) {
|
|
3417
|
-
logger.info({ index }, "reusing existing search-only API key");
|
|
3418
|
-
return existing;
|
|
3419
|
-
}
|
|
3420
|
-
logger.info({ index }, "no reusable search-only key found; creating one");
|
|
3421
|
-
return createSearchKey(index);
|
|
3422
|
-
}
|
|
3423
|
-
|
|
3424
3691
|
// src/lib/algoliaDocs.ts
|
|
3425
3692
|
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3426
|
-
import { dirname as dirname8, join as
|
|
3693
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3427
3694
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3428
|
-
var DOCS_SUBPATH =
|
|
3695
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3429
3696
|
function findDocsDir() {
|
|
3430
3697
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3431
3698
|
for (; ; ) {
|
|
3432
|
-
const candidate =
|
|
3699
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3433
3700
|
if (existsSync2(candidate)) return candidate;
|
|
3434
3701
|
const parent = dirname8(dir);
|
|
3435
3702
|
if (parent === dir) return void 0;
|
|
@@ -3452,7 +3719,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3452
3719
|
);
|
|
3453
3720
|
return "";
|
|
3454
3721
|
}
|
|
3455
|
-
return readFileSync(
|
|
3722
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3456
3723
|
}
|
|
3457
3724
|
function getNamedDoc(name, language) {
|
|
3458
3725
|
const docsDir = findDocsDir();
|
|
@@ -3460,7 +3727,7 @@ function getNamedDoc(name, language) {
|
|
|
3460
3727
|
logger.warn("docs/algolia-sdk not found");
|
|
3461
3728
|
return "";
|
|
3462
3729
|
}
|
|
3463
|
-
const file =
|
|
3730
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3464
3731
|
if (!existsSync2(file)) {
|
|
3465
3732
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3466
3733
|
return "";
|
|
@@ -3487,50 +3754,50 @@ function shellQuote(value) {
|
|
|
3487
3754
|
}
|
|
3488
3755
|
|
|
3489
3756
|
// src/actions/implement.ts
|
|
3490
|
-
var implementSchema =
|
|
3491
|
-
filesChanged:
|
|
3492
|
-
summary:
|
|
3757
|
+
var implementSchema = z26.object({
|
|
3758
|
+
filesChanged: z26.array(z26.string()),
|
|
3759
|
+
summary: z26.string(),
|
|
3493
3760
|
// Absolute path to the throwaway worktree holding the generated changes, so
|
|
3494
3761
|
// the user can open it (`cd <worktreePath>`) or inspect the diff
|
|
3495
3762
|
// (`git -C <worktreePath> status/diff`).
|
|
3496
|
-
worktreePath:
|
|
3497
|
-
ingestCommand:
|
|
3763
|
+
worktreePath: z26.string().optional(),
|
|
3764
|
+
ingestCommand: z26.string().optional(),
|
|
3498
3765
|
// True when the user accepted the run-now prompt and the wizard executed the
|
|
3499
3766
|
// ingestion script; downstream steps use this to avoid telling the user to run
|
|
3500
3767
|
// a script that already ran.
|
|
3501
|
-
ingestScriptRan:
|
|
3768
|
+
ingestScriptRan: z26.boolean().optional(),
|
|
3502
3769
|
// Records ingested by the run-now execution, parsed from the script's
|
|
3503
3770
|
// machine-readable count line; absent when the script didn't run or emitted
|
|
3504
3771
|
// no parseable count.
|
|
3505
|
-
ingestRecordCount:
|
|
3772
|
+
ingestRecordCount: z26.number().optional(),
|
|
3506
3773
|
// Wall-clock duration of the run-now ingestion execution, in ms.
|
|
3507
|
-
ingestDurationMs:
|
|
3508
|
-
ingestionSource:
|
|
3774
|
+
ingestDurationMs: z26.number().optional(),
|
|
3775
|
+
ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
|
|
3509
3776
|
// Suggested names/values, built from framework detection. The search agent is
|
|
3510
3777
|
// instructed to rename the prefix if it doesn't match the project's build
|
|
3511
3778
|
// tool, so the names it actually wrote can differ — treat these as hints, not
|
|
3512
3779
|
// ground truth (the agent's summary carries the final names).
|
|
3513
|
-
searchEnvVars:
|
|
3514
|
-
|
|
3515
|
-
name:
|
|
3516
|
-
value:
|
|
3780
|
+
searchEnvVars: z26.array(
|
|
3781
|
+
z26.object({
|
|
3782
|
+
name: z26.string(),
|
|
3783
|
+
value: z26.string()
|
|
3517
3784
|
})
|
|
3518
3785
|
).optional()
|
|
3519
3786
|
});
|
|
3520
|
-
var implementationOutputSchema =
|
|
3521
|
-
summary:
|
|
3787
|
+
var implementationOutputSchema = z26.object({
|
|
3788
|
+
summary: z26.string(),
|
|
3522
3789
|
// Ingestion only: how to run the generated script, as a structured pair the
|
|
3523
3790
|
// wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
|
|
3524
3791
|
// command string. `runtime` is constrained to an allowlisted interpreter and
|
|
3525
3792
|
// `entrypoint` is validated to a worktree-relative path before execution, so
|
|
3526
3793
|
// the agent cannot inject extra commands or swap the interpreter.
|
|
3527
|
-
runtime:
|
|
3528
|
-
entrypoint:
|
|
3794
|
+
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3795
|
+
entrypoint: z26.string().optional()
|
|
3529
3796
|
});
|
|
3530
|
-
var verificationOutputSchema =
|
|
3531
|
-
summary:
|
|
3532
|
-
sufficient:
|
|
3533
|
-
additionalInstructions:
|
|
3797
|
+
var verificationOutputSchema = z26.object({
|
|
3798
|
+
summary: z26.string(),
|
|
3799
|
+
sufficient: z26.boolean(),
|
|
3800
|
+
additionalInstructions: z26.string().optional()
|
|
3534
3801
|
});
|
|
3535
3802
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3536
3803
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -3644,7 +3911,7 @@ function searchInstructions(input) {
|
|
|
3644
3911
|
doc,
|
|
3645
3912
|
`Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working SearchBox and Hits against the "${input.targetIndex}" index.`,
|
|
3646
3913
|
"Read the App ID and a search-only API key from public env vars; never hardcode them. A search-only key is safe to expose client-side.",
|
|
3647
|
-
// appId always resolves (
|
|
3914
|
+
// appId always resolves (requireApplication throws otherwise); only the
|
|
3648
3915
|
// search-only key is best-effort and can fall back to a placeholder.
|
|
3649
3916
|
`Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
|
|
3650
3917
|
// Names are fixed, not the agent's to rename: the wizard writes the
|
|
@@ -3793,6 +4060,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3793
4060
|
}
|
|
3794
4061
|
}
|
|
3795
4062
|
const targetIndex = selected?.selection;
|
|
4063
|
+
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
3796
4064
|
await assertGitRepoWithHead(repoRoot);
|
|
3797
4065
|
if (await isWorkingTreeDirty(repoRoot)) {
|
|
3798
4066
|
await confirmDirtyWorkingTree(ctx, repoRoot);
|
|
@@ -3803,7 +4071,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3803
4071
|
let appId;
|
|
3804
4072
|
let searchKey;
|
|
3805
4073
|
if (useCases.includes("search")) {
|
|
3806
|
-
appId = (await
|
|
4074
|
+
appId = (await requireApplication()).id;
|
|
3807
4075
|
try {
|
|
3808
4076
|
searchKey = await resolveSearchOnlyKey(targetIndex);
|
|
3809
4077
|
} catch (err) {
|
|
@@ -3914,7 +4182,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3914
4182
|
messages: []
|
|
3915
4183
|
}) === true;
|
|
3916
4184
|
if (runNow) {
|
|
3917
|
-
const
|
|
4185
|
+
const ingestApp = await requireApplication();
|
|
4186
|
+
const writeKey = await resolveWriteKey(targetIndex);
|
|
3918
4187
|
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
3919
4188
|
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
3920
4189
|
runtime: ingestRuntime,
|
|
@@ -3926,8 +4195,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3926
4195
|
ingestRuntime,
|
|
3927
4196
|
ingestEntrypoint,
|
|
3928
4197
|
{
|
|
3929
|
-
[APP_ID_VAR]:
|
|
3930
|
-
[API_KEY_VAR]:
|
|
4198
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4199
|
+
[API_KEY_VAR]: writeKey
|
|
3931
4200
|
}
|
|
3932
4201
|
);
|
|
3933
4202
|
ctx.logEnd(scriptLogId, run.ok ? "success" : "error");
|
|
@@ -4138,8 +4407,8 @@ var defaultWorkflow = {
|
|
|
4138
4407
|
defineStep({
|
|
4139
4408
|
id: "select-index",
|
|
4140
4409
|
title: "Set up index",
|
|
4141
|
-
outputSchema:
|
|
4142
|
-
selection:
|
|
4410
|
+
outputSchema: z27.object({
|
|
4411
|
+
selection: z27.string()
|
|
4143
4412
|
}),
|
|
4144
4413
|
run: (ctx) => selectIndexStep(ctx)
|
|
4145
4414
|
}),
|
|
@@ -4218,27 +4487,34 @@ if (!workflow) {
|
|
|
4218
4487
|
}
|
|
4219
4488
|
var store = useWizard.getState();
|
|
4220
4489
|
var instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4490
|
+
await store.waitForStart();
|
|
4221
4491
|
var user = await getUser();
|
|
4222
4492
|
if (!user) {
|
|
4223
|
-
|
|
4224
|
-
instance.cleanup();
|
|
4493
|
+
store.beginAuth();
|
|
4225
4494
|
try {
|
|
4226
4495
|
await runAuthLogin();
|
|
4227
4496
|
} catch (err) {
|
|
4228
|
-
|
|
4497
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4498
|
+
await instance.waitUntilExit();
|
|
4229
4499
|
process.exit(1);
|
|
4230
4500
|
}
|
|
4231
|
-
|
|
4501
|
+
store.endAuth();
|
|
4232
4502
|
user = await getUser();
|
|
4233
4503
|
if (!user) {
|
|
4234
4504
|
store.setError(
|
|
4235
|
-
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
|
|
4505
|
+
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
|
|
4236
4506
|
);
|
|
4237
4507
|
await instance.waitUntilExit();
|
|
4238
4508
|
process.exit(1);
|
|
4239
4509
|
}
|
|
4240
4510
|
}
|
|
4241
4511
|
store.setUser(user);
|
|
4242
|
-
var
|
|
4243
|
-
|
|
4244
|
-
|
|
4512
|
+
var app;
|
|
4513
|
+
try {
|
|
4514
|
+
app = await ensureApplication();
|
|
4515
|
+
} catch (err) {
|
|
4516
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4517
|
+
await instance.waitUntilExit();
|
|
4518
|
+
process.exit(1);
|
|
4519
|
+
}
|
|
4520
|
+
runWorkflow(workflow, app.id);
|