@algolia/wizard 0.8.0-rc.49.42 → 0.8.0-rc.58.43
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 +871 -679
- 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 store = useWizard.getState();
|
|
45
|
+
const logId = store.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: (user) => set({ user }),
|
|
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,41 +631,43 @@ 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;
|
|
534
668
|
var BAR_PADDING = 2;
|
|
669
|
+
var ROW_HEIGHT = 3;
|
|
670
|
+
var INDICATOR_ROWS = 2;
|
|
535
671
|
function fittedWidth(node, columns) {
|
|
536
672
|
let left = 0;
|
|
537
673
|
for (let n = node; n; n = n.parentNode) {
|
|
@@ -564,13 +700,18 @@ function SelectPrompt({
|
|
|
564
700
|
if (multi) hints.push({ key: "[space]", label: "select" });
|
|
565
701
|
hints.push({ key: "[enter]", label: "confirm" });
|
|
566
702
|
const containerRef = useRef2(null);
|
|
567
|
-
const
|
|
703
|
+
const viewportRef = useRef2(null);
|
|
704
|
+
const { columns, rows: windowRows } = useWindowSize4();
|
|
568
705
|
const [width, setWidth] = useState3(columns);
|
|
706
|
+
const [viewportHeight, setViewportHeight] = useState3(null);
|
|
569
707
|
useLayoutEffect(() => {
|
|
570
708
|
if (containerRef.current) {
|
|
571
709
|
setWidth(fittedWidth(containerRef.current, columns));
|
|
572
710
|
}
|
|
573
|
-
|
|
711
|
+
if (viewportRef.current) {
|
|
712
|
+
setViewportHeight(measureElement2(viewportRef.current).height);
|
|
713
|
+
}
|
|
714
|
+
}, [columns, windowRows, error, question, helpText, messages, table]);
|
|
574
715
|
const inner = Math.max(width - BAR_PADDING, 0);
|
|
575
716
|
const labelWidth = Math.min(
|
|
576
717
|
ARROW_WIDTH + (multi ? 2 : 0) + Math.max(0, ...rows.map((opt) => opt.length)) + COLUMN_GAP,
|
|
@@ -586,6 +727,22 @@ function SelectPrompt({
|
|
|
586
727
|
const barWidth = Math.min(labelWidth + badgeWidth + BAR_PADDING, width);
|
|
587
728
|
const barLabelWidth = Math.max(barWidth - BAR_PADDING - badgeWidth, 0);
|
|
588
729
|
const textWidth = inner - labelWidth;
|
|
730
|
+
const capacity = viewportHeight === null || rows.length * ROW_HEIGHT <= viewportHeight ? rows.length : Math.max(Math.floor((viewportHeight - INDICATOR_ROWS) / ROW_HEIGHT), 1);
|
|
731
|
+
const maxOffset = Math.max(rows.length - capacity, 0);
|
|
732
|
+
const [offset, setOffset] = useState3(0);
|
|
733
|
+
useLayoutEffect(() => {
|
|
734
|
+
setOffset((o) => {
|
|
735
|
+
const clamped = Math.min(o, maxOffset);
|
|
736
|
+
if (index < clamped) return index;
|
|
737
|
+
if (index >= clamped + capacity) {
|
|
738
|
+
return Math.min(index - capacity + 1, maxOffset);
|
|
739
|
+
}
|
|
740
|
+
return clamped;
|
|
741
|
+
});
|
|
742
|
+
}, [index, capacity, maxOffset]);
|
|
743
|
+
const visible = rows.slice(offset, offset + capacity);
|
|
744
|
+
const hiddenAbove = offset;
|
|
745
|
+
const hiddenBelow = rows.length - offset - visible.length;
|
|
589
746
|
useInput((input, key) => {
|
|
590
747
|
if (rows.length === 0) return;
|
|
591
748
|
if (key.upArrow || input === "k") {
|
|
@@ -609,62 +766,77 @@ function SelectPrompt({
|
|
|
609
766
|
}
|
|
610
767
|
}
|
|
611
768
|
});
|
|
612
|
-
return /* @__PURE__ */ jsx4(
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
769
|
+
return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
|
|
770
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, flexShrink: 0, children: [
|
|
771
|
+
error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
|
|
772
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
773
|
+
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
774
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
775
|
+
question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
|
|
776
|
+
helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
|
|
777
|
+
] })
|
|
619
778
|
] }),
|
|
620
|
-
/* @__PURE__ */
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
779
|
+
/* @__PURE__ */ jsxs4(Box5, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
780
|
+
hiddenAbove > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
781
|
+
"\u2191 ",
|
|
782
|
+
hiddenAbove,
|
|
783
|
+
" more"
|
|
784
|
+
] }),
|
|
785
|
+
visible.map((option, visibleIndex) => {
|
|
786
|
+
const i = offset + visibleIndex;
|
|
787
|
+
const highlighted = i === index;
|
|
788
|
+
const isCancel = i === cancelIndex;
|
|
789
|
+
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
790
|
+
const sec = isCancel ? void 0 : secondary?.[i];
|
|
791
|
+
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
792
|
+
const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
|
|
793
|
+
highlighted ? "\u276F " : " ",
|
|
794
|
+
bullet,
|
|
795
|
+
option
|
|
796
|
+
] });
|
|
797
|
+
const isText = sec?.kind === "text";
|
|
798
|
+
return /* @__PURE__ */ jsxs4(
|
|
799
|
+
Box5,
|
|
800
|
+
{
|
|
801
|
+
width: isText ? "100%" : barWidth,
|
|
802
|
+
paddingX: 1,
|
|
803
|
+
paddingY: 1,
|
|
804
|
+
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
805
|
+
children: [
|
|
806
|
+
/* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
807
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
|
|
808
|
+
Text5,
|
|
809
|
+
{
|
|
810
|
+
wrap: "truncate",
|
|
811
|
+
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
812
|
+
children: sec.value
|
|
813
|
+
}
|
|
814
|
+
) }),
|
|
815
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
816
|
+
]
|
|
817
|
+
},
|
|
818
|
+
`row-${i}`
|
|
819
|
+
);
|
|
820
|
+
}),
|
|
821
|
+
hiddenBelow > 0 && /* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
822
|
+
"\u2193 ",
|
|
823
|
+
hiddenBelow,
|
|
824
|
+
" more"
|
|
825
|
+
] })
|
|
826
|
+
] }),
|
|
827
|
+
/* @__PURE__ */ jsx4(Box5, { flexShrink: 0, children: /* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
656
828
|
i > 0 ? " " : "",
|
|
657
|
-
/* @__PURE__ */ jsx4(
|
|
658
|
-
/* @__PURE__ */
|
|
829
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
|
|
830
|
+
/* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
659
831
|
" ",
|
|
660
832
|
label
|
|
661
833
|
] })
|
|
662
|
-
] }, label)) })
|
|
834
|
+
] }, label)) }) })
|
|
663
835
|
] }) });
|
|
664
836
|
}
|
|
665
837
|
|
|
666
838
|
// src/ui/PromptInput.tsx
|
|
667
|
-
import { jsx as jsx5, jsxs as
|
|
839
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
668
840
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
669
841
|
function EnterToContinuePrompt({
|
|
670
842
|
question,
|
|
@@ -675,10 +847,10 @@ function EnterToContinuePrompt({
|
|
|
675
847
|
if (key.return) onDecide(true);
|
|
676
848
|
else if (key.escape) onDecide(false);
|
|
677
849
|
});
|
|
678
|
-
return /* @__PURE__ */
|
|
679
|
-
messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
680
|
-
question && /* @__PURE__ */ jsx5(
|
|
681
|
-
/* @__PURE__ */
|
|
850
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
|
|
851
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
852
|
+
question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
|
|
853
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
|
|
682
854
|
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
|
|
683
855
|
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
684
856
|
] })
|
|
@@ -688,11 +860,11 @@ function PromptInput() {
|
|
|
688
860
|
const { phase, inputReq, submitInput } = useWizard();
|
|
689
861
|
const [draft, setDraft] = useState4("");
|
|
690
862
|
if (phase === "done" || phase === "error") {
|
|
691
|
-
return /* @__PURE__ */ jsx5(
|
|
863
|
+
return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
692
864
|
}
|
|
693
865
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
694
866
|
if (inputReq.promptType === "multipleChoice") {
|
|
695
|
-
return /* @__PURE__ */ jsx5(
|
|
867
|
+
return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
696
868
|
SelectPrompt,
|
|
697
869
|
{
|
|
698
870
|
question: inputReq.prompt,
|
|
@@ -709,7 +881,7 @@ function PromptInput() {
|
|
|
709
881
|
) });
|
|
710
882
|
}
|
|
711
883
|
if (inputReq.promptType === "multiSelect") {
|
|
712
|
-
return /* @__PURE__ */ jsx5(
|
|
884
|
+
return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
713
885
|
SelectPrompt,
|
|
714
886
|
{
|
|
715
887
|
multi: true,
|
|
@@ -724,7 +896,7 @@ function PromptInput() {
|
|
|
724
896
|
) });
|
|
725
897
|
}
|
|
726
898
|
if (inputReq.promptType === "notice") {
|
|
727
|
-
return /* @__PURE__ */ jsx5(
|
|
899
|
+
return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
728
900
|
SelectPrompt,
|
|
729
901
|
{
|
|
730
902
|
question: inputReq.prompt,
|
|
@@ -746,7 +918,7 @@ function PromptInput() {
|
|
|
746
918
|
}
|
|
747
919
|
if (inputReq.promptType === "acceptReject") {
|
|
748
920
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
749
|
-
return /* @__PURE__ */ jsx5(
|
|
921
|
+
return /* @__PURE__ */ jsx5(Box6, { flexGrow: 1, children: /* @__PURE__ */ jsx5(
|
|
750
922
|
SelectPrompt,
|
|
751
923
|
{
|
|
752
924
|
question: inputReq.prompt,
|
|
@@ -757,11 +929,11 @@ function PromptInput() {
|
|
|
757
929
|
}
|
|
758
930
|
) });
|
|
759
931
|
}
|
|
760
|
-
return /* @__PURE__ */
|
|
761
|
-
inputReq.error && /* @__PURE__ */ jsx5(
|
|
762
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
763
|
-
/* @__PURE__ */
|
|
764
|
-
/* @__PURE__ */
|
|
932
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
933
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
|
|
934
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
935
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
936
|
+
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
|
|
765
937
|
inputReq.prompt,
|
|
766
938
|
" "
|
|
767
939
|
] }),
|
|
@@ -783,7 +955,7 @@ function PromptInput() {
|
|
|
783
955
|
// src/ui/Welcome.tsx
|
|
784
956
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
785
957
|
import { fileURLToPath } from "node:url";
|
|
786
|
-
import { Box as
|
|
958
|
+
import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
|
|
787
959
|
|
|
788
960
|
// src/ui/copy/welcome.ts
|
|
789
961
|
var sidebarItems = [
|
|
@@ -811,27 +983,27 @@ var sidebarItems = [
|
|
|
811
983
|
|
|
812
984
|
// src/ui/Welcome.tsx
|
|
813
985
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
814
|
-
import { jsx as jsx6, jsxs as
|
|
986
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
815
987
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
816
988
|
function SidebarItem({
|
|
817
989
|
title,
|
|
818
990
|
description
|
|
819
991
|
}) {
|
|
820
|
-
return /* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */ jsx6(
|
|
823
|
-
/* @__PURE__ */ jsx6(
|
|
992
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
993
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
994
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
|
|
995
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
|
|
824
996
|
] }),
|
|
825
|
-
/* @__PURE__ */
|
|
997
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
|
|
826
998
|
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
827
|
-
/* @__PURE__ */ jsx6(
|
|
999
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
|
|
828
1000
|
] })
|
|
829
1001
|
] });
|
|
830
1002
|
}
|
|
831
1003
|
function Welcome() {
|
|
832
1004
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
833
1005
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
834
|
-
const { rows } =
|
|
1006
|
+
const { rows } = useWindowSize5();
|
|
835
1007
|
useInput3((input, key) => {
|
|
836
1008
|
if (key.return) confirmStart();
|
|
837
1009
|
else if (input === "i") openLearnMore();
|
|
@@ -850,15 +1022,15 @@ function Welcome() {
|
|
|
850
1022
|
if (rows < 30) {
|
|
851
1023
|
layout = scales["small"];
|
|
852
1024
|
}
|
|
853
|
-
return /* @__PURE__ */
|
|
1025
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
854
1026
|
/* @__PURE__ */ jsx6(
|
|
855
|
-
|
|
1027
|
+
Box7,
|
|
856
1028
|
{
|
|
857
1029
|
paddingY: layout.main.padding.y,
|
|
858
1030
|
paddingX: layout.main.padding.x,
|
|
859
1031
|
flexDirection: "column",
|
|
860
1032
|
justifyContent: "center",
|
|
861
|
-
children: /* @__PURE__ */
|
|
1033
|
+
children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
|
|
862
1034
|
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
863
1035
|
Image,
|
|
864
1036
|
{
|
|
@@ -870,16 +1042,16 @@ function Welcome() {
|
|
|
870
1042
|
protocol: "halfBlock"
|
|
871
1043
|
}
|
|
872
1044
|
) }),
|
|
873
|
-
/* @__PURE__ */ jsx6(
|
|
874
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1046
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
|
|
875
1047
|
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
876
1048
|
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
877
1049
|
] })
|
|
878
1050
|
] })
|
|
879
1051
|
}
|
|
880
1052
|
),
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
|
|
1053
|
+
/* @__PURE__ */ jsxs6(
|
|
1054
|
+
Box7,
|
|
883
1055
|
{
|
|
884
1056
|
backgroundColor: COLORS.bg.sidebar,
|
|
885
1057
|
width: 40,
|
|
@@ -889,7 +1061,7 @@ function Welcome() {
|
|
|
889
1061
|
flexDirection: "column",
|
|
890
1062
|
justifyContent: "center",
|
|
891
1063
|
children: [
|
|
892
|
-
/* @__PURE__ */ jsx6(
|
|
1064
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
893
1065
|
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
894
1066
|
]
|
|
895
1067
|
}
|
|
@@ -899,7 +1071,7 @@ function Welcome() {
|
|
|
899
1071
|
|
|
900
1072
|
// src/ui/LearnMore.tsx
|
|
901
1073
|
import { Fragment as Fragment2 } from "react";
|
|
902
|
-
import { Box as
|
|
1074
|
+
import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
903
1075
|
|
|
904
1076
|
// src/ui/copy/learn-more.ts
|
|
905
1077
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -936,7 +1108,7 @@ var policyLinks = [
|
|
|
936
1108
|
];
|
|
937
1109
|
|
|
938
1110
|
// src/ui/LearnMore.tsx
|
|
939
|
-
import { jsx as jsx7, jsxs as
|
|
1111
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
940
1112
|
var TAG_COLORS = {
|
|
941
1113
|
READ: COLORS.success,
|
|
942
1114
|
WRITE: COLORS.badge,
|
|
@@ -952,25 +1124,25 @@ function NeverLine({
|
|
|
952
1124
|
}) {
|
|
953
1125
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
954
1126
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
955
|
-
return /* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */ jsx7(
|
|
1127
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1128
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
|
|
957
1129
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
958
|
-
segments.map((s, i) => /* @__PURE__ */ jsx7(
|
|
1130
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
959
1131
|
" ".repeat(rightPad),
|
|
960
|
-
/* @__PURE__ */ jsx7(
|
|
1132
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
|
|
961
1133
|
] });
|
|
962
1134
|
}
|
|
963
1135
|
function LearnMore() {
|
|
964
1136
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
965
1137
|
const backToHome = useWizard((s) => s.backToHome);
|
|
966
|
-
const { columns } =
|
|
1138
|
+
const { columns } = useWindowSize6();
|
|
967
1139
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
968
1140
|
useInput4((_input, key) => {
|
|
969
1141
|
if (key.escape) backToHome();
|
|
970
1142
|
else if (key.return) confirmStart();
|
|
971
1143
|
});
|
|
972
|
-
return /* @__PURE__ */
|
|
973
|
-
|
|
1144
|
+
return /* @__PURE__ */ jsxs7(
|
|
1145
|
+
Box8,
|
|
974
1146
|
{
|
|
975
1147
|
flexDirection: "column",
|
|
976
1148
|
paddingX: PADDING_X,
|
|
@@ -978,20 +1150,20 @@ function LearnMore() {
|
|
|
978
1150
|
width: "100%",
|
|
979
1151
|
gap: 1,
|
|
980
1152
|
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(
|
|
1153
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1154
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
|
|
1155
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
1156
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1157
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1158
|
+
/* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1159
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1160
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1161
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
990
1162
|
] }) })
|
|
991
1163
|
] })
|
|
992
1164
|
] }, item.tag)) }),
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
/* @__PURE__ */ jsx7(
|
|
1165
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
1166
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
995
1167
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
996
1168
|
/* @__PURE__ */ jsx7(
|
|
997
1169
|
NeverLine,
|
|
@@ -1000,7 +1172,7 @@ function LearnMore() {
|
|
|
1000
1172
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1001
1173
|
}
|
|
1002
1174
|
),
|
|
1003
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1175
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1004
1176
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1005
1177
|
/* @__PURE__ */ jsx7(
|
|
1006
1178
|
NeverLine,
|
|
@@ -1015,23 +1187,23 @@ function LearnMore() {
|
|
|
1015
1187
|
)
|
|
1016
1188
|
] }, item)),
|
|
1017
1189
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1018
|
-
/* @__PURE__ */ jsx7(
|
|
1190
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1019
1191
|
] }),
|
|
1020
|
-
/* @__PURE__ */ jsx7(
|
|
1021
|
-
/* @__PURE__ */ jsx7(
|
|
1022
|
-
/* @__PURE__ */ jsx7(
|
|
1192
|
+
/* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1193
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1194
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
|
|
1023
1195
|
] }, link.label)) }),
|
|
1024
|
-
/* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1026
|
-
/* @__PURE__ */ jsx7(
|
|
1027
|
-
/* @__PURE__ */ jsx7(
|
|
1028
|
-
/* @__PURE__ */ jsx7(
|
|
1196
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1197
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1198
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1199
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
|
|
1200
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
|
|
1029
1201
|
] }),
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
/* @__PURE__ */ jsx7(
|
|
1032
|
-
/* @__PURE__ */ jsx7(
|
|
1033
|
-
/* @__PURE__ */ jsx7(
|
|
1034
|
-
/* @__PURE__ */ jsx7(
|
|
1202
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1203
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1204
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "enter" }),
|
|
1205
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
|
|
1206
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1035
1207
|
] })
|
|
1036
1208
|
] })
|
|
1037
1209
|
]
|
|
@@ -1040,10 +1212,10 @@ function LearnMore() {
|
|
|
1040
1212
|
}
|
|
1041
1213
|
|
|
1042
1214
|
// src/ui/Sidebar.tsx
|
|
1043
|
-
import { Box as
|
|
1215
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1044
1216
|
|
|
1045
1217
|
// src/ui/Steps.tsx
|
|
1046
|
-
import { Box as
|
|
1218
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1047
1219
|
import Spinner from "ink-spinner";
|
|
1048
1220
|
|
|
1049
1221
|
// src/core/persistence.ts
|
|
@@ -1072,11 +1244,11 @@ async function clearWorkflowState(workflowId) {
|
|
|
1072
1244
|
}
|
|
1073
1245
|
|
|
1074
1246
|
// src/ui/Steps.tsx
|
|
1075
|
-
import { jsx as jsx8, jsxs as
|
|
1247
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1076
1248
|
function Steps() {
|
|
1077
1249
|
const { steps } = useWizard();
|
|
1078
1250
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1079
|
-
return /* @__PURE__ */ jsx8(
|
|
1251
|
+
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
1252
|
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1081
1253
|
" ",
|
|
1082
1254
|
s.title
|
|
@@ -1086,7 +1258,7 @@ function CurrentStep() {
|
|
|
1086
1258
|
const { steps } = useWizard();
|
|
1087
1259
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1088
1260
|
if (!currentStep) return null;
|
|
1089
|
-
return /* @__PURE__ */
|
|
1261
|
+
return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
|
|
1090
1262
|
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
1091
1263
|
" ",
|
|
1092
1264
|
` ${currentStep.title}`
|
|
@@ -1094,19 +1266,19 @@ function CurrentStep() {
|
|
|
1094
1266
|
}
|
|
1095
1267
|
|
|
1096
1268
|
// src/ui/Progress.tsx
|
|
1097
|
-
import { Box as
|
|
1098
|
-
import { jsx as jsx9, jsxs as
|
|
1269
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1270
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1099
1271
|
function Progress() {
|
|
1100
1272
|
const { steps, currentStepIndex } = useWizard();
|
|
1101
1273
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1102
1274
|
if (visibleSteps.length === 0) return null;
|
|
1103
1275
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1104
1276
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1105
|
-
return /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */ jsx9(
|
|
1107
|
-
/* @__PURE__ */ jsx9(
|
|
1108
|
-
/* @__PURE__ */ jsx9(
|
|
1109
|
-
/* @__PURE__ */ jsx9(
|
|
1277
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1278
|
+
/* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
|
|
1279
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
|
|
1280
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
|
|
1281
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
|
|
1110
1282
|
] });
|
|
1111
1283
|
}
|
|
1112
1284
|
|
|
@@ -1117,10 +1289,10 @@ var sidebarCommands = [
|
|
|
1117
1289
|
];
|
|
1118
1290
|
|
|
1119
1291
|
// src/ui/Sidebar.tsx
|
|
1120
|
-
import { jsx as jsx10, jsxs as
|
|
1292
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1121
1293
|
function Sidebar() {
|
|
1122
|
-
return /* @__PURE__ */
|
|
1123
|
-
|
|
1294
|
+
return /* @__PURE__ */ jsxs10(
|
|
1295
|
+
Box11,
|
|
1124
1296
|
{
|
|
1125
1297
|
backgroundColor: "#14171E",
|
|
1126
1298
|
width: 30,
|
|
@@ -1129,16 +1301,16 @@ function Sidebar() {
|
|
|
1129
1301
|
flexDirection: "column",
|
|
1130
1302
|
justifyContent: "space-between",
|
|
1131
1303
|
children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
/* @__PURE__ */ jsx10(
|
|
1304
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1305
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1134
1306
|
/* @__PURE__ */ jsx10(Steps, {})
|
|
1135
1307
|
] }),
|
|
1136
|
-
/* @__PURE__ */
|
|
1308
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1137
1309
|
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1138
|
-
/* @__PURE__ */ jsx10(
|
|
1139
|
-
return /* @__PURE__ */
|
|
1140
|
-
/* @__PURE__ */ jsx10(
|
|
1141
|
-
/* @__PURE__ */ jsx10(
|
|
1310
|
+
/* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1311
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1312
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1313
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
|
|
1142
1314
|
] });
|
|
1143
1315
|
}) })
|
|
1144
1316
|
] })
|
|
@@ -1148,12 +1320,12 @@ function Sidebar() {
|
|
|
1148
1320
|
}
|
|
1149
1321
|
|
|
1150
1322
|
// src/ui/Ribbon.tsx
|
|
1151
|
-
import { Box as
|
|
1152
|
-
import { jsx as jsx11, jsxs as
|
|
1323
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1324
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1153
1325
|
function Ribbon() {
|
|
1154
1326
|
const firstCommand = sidebarCommands[0];
|
|
1155
|
-
return /* @__PURE__ */
|
|
1156
|
-
|
|
1327
|
+
return /* @__PURE__ */ jsxs11(
|
|
1328
|
+
Box12,
|
|
1157
1329
|
{
|
|
1158
1330
|
backgroundColor: "#14171E",
|
|
1159
1331
|
flexDirection: "row",
|
|
@@ -1163,9 +1335,9 @@ function Ribbon() {
|
|
|
1163
1335
|
children: [
|
|
1164
1336
|
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1165
1337
|
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */ jsx11(
|
|
1168
|
-
/* @__PURE__ */ jsx11(
|
|
1338
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1339
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1340
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
|
|
1169
1341
|
] })
|
|
1170
1342
|
]
|
|
1171
1343
|
}
|
|
@@ -1177,8 +1349,8 @@ import { useState as useState6 } from "react";
|
|
|
1177
1349
|
|
|
1178
1350
|
// src/ui/Logs.tsx
|
|
1179
1351
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
|
|
1180
|
-
import { Box as
|
|
1181
|
-
import { jsx as jsx12, jsxs as
|
|
1352
|
+
import { Box as Box13, Text as Text13, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1353
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1182
1354
|
var KIND_COLOR = {
|
|
1183
1355
|
tool: COLORS.primary,
|
|
1184
1356
|
prompt: COLORS.badge
|
|
@@ -1208,7 +1380,7 @@ function formatTimestamp(ms) {
|
|
|
1208
1380
|
}
|
|
1209
1381
|
function Logs() {
|
|
1210
1382
|
const logs = useWizard((s) => s.logs);
|
|
1211
|
-
const { rows, columns } =
|
|
1383
|
+
const { rows, columns } = useWindowSize7();
|
|
1212
1384
|
const viewportRef = useRef3(null);
|
|
1213
1385
|
const [viewportHeight, setViewportHeight] = useState5(0);
|
|
1214
1386
|
const [viewportWidth, setViewportWidth] = useState5(0);
|
|
@@ -1245,10 +1417,10 @@ function Logs() {
|
|
|
1245
1417
|
const visible = logs.slice(scrollOffset, scrollOffset + capacity);
|
|
1246
1418
|
const hiddenAbove = scrollOffset;
|
|
1247
1419
|
const hiddenBelow = logs.length - scrollOffset - visible.length;
|
|
1248
|
-
return /* @__PURE__ */
|
|
1249
|
-
logs.length === 0 && /* @__PURE__ */ jsx12(
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
hiddenAbove > 0 && /* @__PURE__ */
|
|
1420
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1421
|
+
logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
|
|
1422
|
+
/* @__PURE__ */ jsxs12(Box13, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
1423
|
+
hiddenAbove > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1252
1424
|
"\u2191 ",
|
|
1253
1425
|
hiddenAbove,
|
|
1254
1426
|
" more"
|
|
@@ -1263,20 +1435,20 @@ function Logs() {
|
|
|
1263
1435
|
const name = truncate2(entry.name, budget);
|
|
1264
1436
|
budget -= name.length;
|
|
1265
1437
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1266
|
-
return /* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */ jsx12(
|
|
1268
|
-
/* @__PURE__ */ jsx12(
|
|
1269
|
-
preview && /* @__PURE__ */ jsx12(
|
|
1270
|
-
durationText && /* @__PURE__ */ jsx12(
|
|
1438
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1439
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
|
|
1440
|
+
/* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1441
|
+
preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1442
|
+
durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
|
|
1271
1443
|
] }, entry.id);
|
|
1272
1444
|
}),
|
|
1273
|
-
hiddenBelow > 0 && /* @__PURE__ */
|
|
1445
|
+
hiddenBelow > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1274
1446
|
"\u2193 ",
|
|
1275
1447
|
hiddenBelow,
|
|
1276
1448
|
" more"
|
|
1277
1449
|
] })
|
|
1278
1450
|
] }),
|
|
1279
|
-
/* @__PURE__ */ jsx12(
|
|
1451
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1280
1452
|
] });
|
|
1281
1453
|
}
|
|
1282
1454
|
|
|
@@ -1468,11 +1640,11 @@ function track(event, payload) {
|
|
|
1468
1640
|
}
|
|
1469
1641
|
|
|
1470
1642
|
// src/ui/App.tsx
|
|
1471
|
-
import { jsx as jsx13, jsxs as
|
|
1643
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1472
1644
|
function App() {
|
|
1473
1645
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1474
1646
|
const { exit } = useApp();
|
|
1475
|
-
const { columns, rows } =
|
|
1647
|
+
const { columns, rows } = useWindowSize8();
|
|
1476
1648
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1477
1649
|
const finished = phase === "done" || phase === "error";
|
|
1478
1650
|
const currentStep = steps[currentStepIndex];
|
|
@@ -1485,7 +1657,7 @@ function App() {
|
|
|
1485
1657
|
{ isActive: finished }
|
|
1486
1658
|
);
|
|
1487
1659
|
useInput6((_input, key) => {
|
|
1488
|
-
if (phase === "idle" || phase === "
|
|
1660
|
+
if (phase === "idle" || phase === "authenticating") return;
|
|
1489
1661
|
if (key.tab) {
|
|
1490
1662
|
setShowLogs(!showLogs);
|
|
1491
1663
|
track("AI Wizard Interaction", {
|
|
@@ -1495,7 +1667,7 @@ function App() {
|
|
|
1495
1667
|
});
|
|
1496
1668
|
}
|
|
1497
1669
|
});
|
|
1498
|
-
const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1670
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1499
1671
|
useInput6((_input, key) => {
|
|
1500
1672
|
if (escOwnedElsewhere) return;
|
|
1501
1673
|
if (key.escape) {
|
|
@@ -1508,53 +1680,70 @@ function App() {
|
|
|
1508
1680
|
exit();
|
|
1509
1681
|
}
|
|
1510
1682
|
});
|
|
1511
|
-
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1683
|
+
const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1512
1684
|
const flexDirection = columns > 90 ? "row" : "column";
|
|
1513
1685
|
const showSidebar = flexDirection === "row";
|
|
1514
|
-
return
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1686
|
+
return (
|
|
1687
|
+
/* Exactly the viewport, clipped — never `minHeight`, which lets the frame
|
|
1688
|
+
grow past the terminal. Ink then abandons diffing to clear and repaint
|
|
1689
|
+
the whole screen, and the scrolling that frame causes throws off its
|
|
1690
|
+
cursor arithmetic: flicker and leftover rows, worst when a burst of CLI
|
|
1691
|
+
output is swapped out. Clipping drops the bottom of an over-tall frame;
|
|
1692
|
+
the per-panel row budgets are what keep it from coming to that. */
|
|
1693
|
+
/* @__PURE__ */ jsxs13(
|
|
1694
|
+
Box14,
|
|
1695
|
+
{
|
|
1696
|
+
backgroundColor: COLORS.bg.main,
|
|
1697
|
+
flexDirection: "row",
|
|
1698
|
+
width: columns,
|
|
1699
|
+
height: rows,
|
|
1700
|
+
overflow: "hidden",
|
|
1701
|
+
children: [
|
|
1702
|
+
mainWindowVisible && /* @__PURE__ */ jsxs13(
|
|
1703
|
+
Box14,
|
|
1704
|
+
{
|
|
1705
|
+
flexDirection,
|
|
1706
|
+
width: "100%",
|
|
1707
|
+
justifyContent: "space-between",
|
|
1708
|
+
children: [
|
|
1709
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
|
|
1710
|
+
/* Fill the space the sidebar/ribbon leaves — width beside the
|
|
1711
|
+
sidebar, height above the ribbon. The height matters even
|
|
1712
|
+
stacked: it is what the prompt's scrolling list measures itself
|
|
1713
|
+
against (see SelectPrompt). */
|
|
1714
|
+
/* @__PURE__ */ jsxs13(
|
|
1715
|
+
Box14,
|
|
1716
|
+
{
|
|
1717
|
+
flexDirection: "column",
|
|
1718
|
+
paddingX: 4,
|
|
1719
|
+
paddingY: 2,
|
|
1720
|
+
width: showSidebar ? 70 : "100%",
|
|
1721
|
+
flexGrow: 1,
|
|
1722
|
+
children: [
|
|
1723
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
1724
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1725
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1726
|
+
] }),
|
|
1727
|
+
/* @__PURE__ */ jsx13(CliOutput, {}),
|
|
1728
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1729
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1730
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1731
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
|
|
1732
|
+
"\u2716 ",
|
|
1733
|
+
error
|
|
1734
|
+
] }) })
|
|
1735
|
+
]
|
|
1736
|
+
}
|
|
1737
|
+
)
|
|
1738
|
+
),
|
|
1739
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1740
|
+
]
|
|
1741
|
+
}
|
|
1742
|
+
),
|
|
1743
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1744
|
+
]
|
|
1745
|
+
}
|
|
1746
|
+
)
|
|
1558
1747
|
);
|
|
1559
1748
|
}
|
|
1560
1749
|
|
|
@@ -1568,8 +1757,7 @@ var configFile = () => join5(stateDir(), "config.json");
|
|
|
1568
1757
|
var DEFAULT_CONFIG = {
|
|
1569
1758
|
version: 1,
|
|
1570
1759
|
aiConsent: false,
|
|
1571
|
-
workflowsRun: []
|
|
1572
|
-
searchApiKeys: {}
|
|
1760
|
+
workflowsRun: []
|
|
1573
1761
|
};
|
|
1574
1762
|
async function loadConfig() {
|
|
1575
1763
|
try {
|
|
@@ -1588,38 +1776,6 @@ async function recordWorkflowRun(workflowId, completedAt) {
|
|
|
1588
1776
|
config.workflowsRun.push({ workflowId, completedAt });
|
|
1589
1777
|
await saveConfig(config);
|
|
1590
1778
|
}
|
|
1591
|
-
function isStoredSearchKey(value) {
|
|
1592
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1593
|
-
const { appId, key } = value;
|
|
1594
|
-
return typeof appId === "string" && !!appId && typeof key === "string" && !!key;
|
|
1595
|
-
}
|
|
1596
|
-
function storedSearchKeys(config) {
|
|
1597
|
-
const stored = config.searchApiKeys;
|
|
1598
|
-
if (typeof stored !== "object" || stored === null || Array.isArray(stored)) {
|
|
1599
|
-
return {};
|
|
1600
|
-
}
|
|
1601
|
-
return stored;
|
|
1602
|
-
}
|
|
1603
|
-
async function getStoredSearchKey(index, appId) {
|
|
1604
|
-
const entry = storedSearchKeys(await loadConfig())[index];
|
|
1605
|
-
if (!isStoredSearchKey(entry) || entry.appId !== appId) return void 0;
|
|
1606
|
-
return entry.key;
|
|
1607
|
-
}
|
|
1608
|
-
async function storeSearchKey(index, appId, key) {
|
|
1609
|
-
const config = await loadConfig();
|
|
1610
|
-
config.searchApiKeys = {
|
|
1611
|
-
...storedSearchKeys(config),
|
|
1612
|
-
[index]: { appId, key }
|
|
1613
|
-
};
|
|
1614
|
-
await saveConfig(config);
|
|
1615
|
-
}
|
|
1616
|
-
async function forgetSearchKey(index) {
|
|
1617
|
-
const config = await loadConfig();
|
|
1618
|
-
const remaining = { ...storedSearchKeys(config) };
|
|
1619
|
-
delete remaining[index];
|
|
1620
|
-
config.searchApiKeys = remaining;
|
|
1621
|
-
await saveConfig(config);
|
|
1622
|
-
}
|
|
1623
1779
|
|
|
1624
1780
|
// src/core/orchestrator.ts
|
|
1625
1781
|
function defineStep(step) {
|
|
@@ -1823,61 +1979,138 @@ async function runWorkflow(workflow, appId) {
|
|
|
1823
1979
|
}
|
|
1824
1980
|
}
|
|
1825
1981
|
|
|
1826
|
-
// src/lib/
|
|
1827
|
-
import {
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1982
|
+
// src/lib/algoliaApp.ts
|
|
1983
|
+
import { z as z4 } from "zod";
|
|
1984
|
+
var applicationSchema = z4.object({
|
|
1985
|
+
id: z4.string().min(1),
|
|
1986
|
+
name: z4.string().default(""),
|
|
1987
|
+
plan: z4.string().optional()
|
|
1988
|
+
});
|
|
1989
|
+
var listSchema = z4.array(
|
|
1990
|
+
z4.object({
|
|
1991
|
+
id: z4.string().min(1),
|
|
1992
|
+
name: z4.string().default(""),
|
|
1993
|
+
plan_label: z4.string().optional()
|
|
1994
|
+
}).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
|
|
1995
|
+
);
|
|
1996
|
+
async function currentApplication() {
|
|
1997
|
+
let raw;
|
|
1839
1998
|
try {
|
|
1840
|
-
|
|
1999
|
+
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
1841
2000
|
} catch {
|
|
1842
|
-
return
|
|
2001
|
+
return null;
|
|
2002
|
+
}
|
|
2003
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2004
|
+
return parsed.success ? parsed.data : null;
|
|
2005
|
+
}
|
|
2006
|
+
async function requireApplication() {
|
|
2007
|
+
const app = await currentApplication();
|
|
2008
|
+
if (!app) {
|
|
2009
|
+
throw new Error(
|
|
2010
|
+
"No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
return app;
|
|
2014
|
+
}
|
|
2015
|
+
async function listApplications() {
|
|
2016
|
+
const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
|
|
2017
|
+
const parsed = listSchema.safeParse(parseJson(raw));
|
|
2018
|
+
if (!parsed.success) {
|
|
2019
|
+
throw new Error("Could not read the list of Algolia applications.");
|
|
2020
|
+
}
|
|
2021
|
+
return parsed.data;
|
|
2022
|
+
}
|
|
2023
|
+
async function selectApplication(id) {
|
|
2024
|
+
const raw = await runAlgoliaCli(
|
|
2025
|
+
["application", "select", "--non-interactive", "--app-id", id],
|
|
2026
|
+
{ onOutput: stderrSink }
|
|
2027
|
+
);
|
|
2028
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2029
|
+
if (!parsed.success) {
|
|
2030
|
+
throw new Error(
|
|
2031
|
+
`Selected application ${id}, but the Algolia CLI returned an unreadable result.`
|
|
2032
|
+
);
|
|
1843
2033
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
name,
|
|
1848
|
-
appId: t.application_id,
|
|
1849
|
-
apiKey: t.api_key,
|
|
1850
|
-
isDefault: t.default === true
|
|
1851
|
-
}));
|
|
1852
|
-
profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
|
|
1853
|
-
return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
|
|
1854
|
-
}
|
|
1855
|
-
async function loadActiveProfile() {
|
|
1856
|
-
let profiles;
|
|
2034
|
+
return parsed.data;
|
|
2035
|
+
}
|
|
2036
|
+
function parseJson(text) {
|
|
1857
2037
|
try {
|
|
1858
|
-
|
|
2038
|
+
return JSON.parse(text);
|
|
1859
2039
|
} catch {
|
|
1860
|
-
|
|
2040
|
+
return void 0;
|
|
1861
2041
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
// src/lib/algoliaAppPicker.ts
|
|
2045
|
+
function secondaryFor(app) {
|
|
2046
|
+
return app.plan ? { kind: "badge", value: app.plan } : void 0;
|
|
2047
|
+
}
|
|
2048
|
+
function labelFor(app) {
|
|
2049
|
+
return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
|
|
2050
|
+
}
|
|
2051
|
+
function selectAndReport(app) {
|
|
2052
|
+
useWizard.getState().pushCliOutput(
|
|
2053
|
+
"stdout",
|
|
2054
|
+
`Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
|
|
2055
|
+
);
|
|
2056
|
+
return selectApplication(app.id);
|
|
2057
|
+
}
|
|
2058
|
+
async function promptForApplication() {
|
|
2059
|
+
const store = useWizard.getState();
|
|
2060
|
+
const apps = await listApplications();
|
|
2061
|
+
if (apps.length === 0) {
|
|
1864
2062
|
throw new Error(
|
|
1865
|
-
"
|
|
2063
|
+
"This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
if (apps.length === 1) {
|
|
2067
|
+
const only = apps[0];
|
|
2068
|
+
logger.info(
|
|
2069
|
+
{ app: only.id },
|
|
2070
|
+
"single application on the account; selecting it"
|
|
1866
2071
|
);
|
|
2072
|
+
return selectAndReport(only);
|
|
2073
|
+
}
|
|
2074
|
+
const messages = ["Which Algolia application should the wizard work in?"];
|
|
2075
|
+
for (; ; ) {
|
|
2076
|
+
const choice = await store.requestUserInput({
|
|
2077
|
+
prompt: "Select an application",
|
|
2078
|
+
promptType: "multipleChoice",
|
|
2079
|
+
options: apps.map(labelFor),
|
|
2080
|
+
secondary: apps.map(secondaryFor),
|
|
2081
|
+
messages
|
|
2082
|
+
});
|
|
2083
|
+
const chosen = apps.find((app) => labelFor(app) === choice);
|
|
2084
|
+
if (!chosen) {
|
|
2085
|
+
throw new Error("Application picker received an unexpected selection");
|
|
2086
|
+
}
|
|
2087
|
+
try {
|
|
2088
|
+
return await selectAndReport(chosen);
|
|
2089
|
+
} catch (err) {
|
|
2090
|
+
logger.warn(
|
|
2091
|
+
{ app: chosen.id, err: err.message },
|
|
2092
|
+
"application select failed; re-prompting"
|
|
2093
|
+
);
|
|
2094
|
+
messages.push(
|
|
2095
|
+
`Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
1867
2098
|
}
|
|
1868
|
-
|
|
2099
|
+
}
|
|
2100
|
+
async function ensureApplication() {
|
|
2101
|
+
return await currentApplication() ?? await promptForApplication();
|
|
1869
2102
|
}
|
|
1870
2103
|
|
|
1871
2104
|
// src/workflows/default.ts
|
|
1872
|
-
import { z as
|
|
2105
|
+
import { z as z27 } from "zod";
|
|
1873
2106
|
|
|
1874
2107
|
// src/actions/listIndices.ts
|
|
1875
|
-
import { z as
|
|
1876
|
-
var indicesListSchema =
|
|
1877
|
-
items:
|
|
1878
|
-
|
|
1879
|
-
name:
|
|
1880
|
-
entries:
|
|
2108
|
+
import { z as z5 } from "zod";
|
|
2109
|
+
var indicesListSchema = z5.object({
|
|
2110
|
+
items: z5.array(
|
|
2111
|
+
z5.object({
|
|
2112
|
+
name: z5.string(),
|
|
2113
|
+
entries: z5.number().default(0)
|
|
1881
2114
|
})
|
|
1882
2115
|
)
|
|
1883
2116
|
});
|
|
@@ -1948,12 +2181,12 @@ import "zod";
|
|
|
1948
2181
|
|
|
1949
2182
|
// src/lib/tools/listFiles.ts
|
|
1950
2183
|
import { tool } from "ai";
|
|
1951
|
-
import
|
|
2184
|
+
import z6 from "zod";
|
|
1952
2185
|
import { readdir } from "node:fs/promises";
|
|
1953
2186
|
|
|
1954
2187
|
// src/lib/tools/path.ts
|
|
1955
2188
|
import { lstat } from "node:fs/promises";
|
|
1956
|
-
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as
|
|
2189
|
+
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
|
|
1957
2190
|
function resolveInRoot(ctx, path) {
|
|
1958
2191
|
const target = resolve2(ctx.cwd, path);
|
|
1959
2192
|
const rel = relative(ctx.root, target);
|
|
@@ -1969,7 +2202,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1969
2202
|
let current = ctx.root;
|
|
1970
2203
|
const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
|
|
1971
2204
|
for (const part of parts) {
|
|
1972
|
-
current =
|
|
2205
|
+
current = join6(current, part);
|
|
1973
2206
|
try {
|
|
1974
2207
|
if ((await lstat(current)).isSymbolicLink()) return true;
|
|
1975
2208
|
} catch (err) {
|
|
@@ -1984,7 +2217,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1984
2217
|
function listFilesTool(ctx) {
|
|
1985
2218
|
return tool({
|
|
1986
2219
|
description: "List files in the current working directory",
|
|
1987
|
-
inputSchema:
|
|
2220
|
+
inputSchema: z6.object(),
|
|
1988
2221
|
execute: async () => {
|
|
1989
2222
|
logger.info("called listFiles tool");
|
|
1990
2223
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
@@ -2000,13 +2233,13 @@ function listFilesTool(ctx) {
|
|
|
2000
2233
|
|
|
2001
2234
|
// src/lib/tools/changeDirectory.ts
|
|
2002
2235
|
import { tool as tool2 } from "ai";
|
|
2003
|
-
import
|
|
2236
|
+
import z7 from "zod";
|
|
2004
2237
|
import { stat } from "node:fs/promises";
|
|
2005
2238
|
function changeDirectoryTool(ctx) {
|
|
2006
2239
|
return tool2({
|
|
2007
2240
|
description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
|
|
2008
|
-
inputSchema:
|
|
2009
|
-
path:
|
|
2241
|
+
inputSchema: z7.object({
|
|
2242
|
+
path: z7.string().describe("Directory to change into")
|
|
2010
2243
|
}),
|
|
2011
2244
|
execute: async ({ path }) => {
|
|
2012
2245
|
logger.info({ path }, "called changeDirectory tool");
|
|
@@ -2028,13 +2261,13 @@ function changeDirectoryTool(ctx) {
|
|
|
2028
2261
|
|
|
2029
2262
|
// src/lib/tools/reportStatus.ts
|
|
2030
2263
|
import { tool as tool3 } from "ai";
|
|
2031
|
-
import
|
|
2264
|
+
import z8 from "zod";
|
|
2032
2265
|
function reportStatusTool(output) {
|
|
2033
2266
|
return tool3({
|
|
2034
2267
|
description: "Report the status of your execution. Return a reason in case of failure.",
|
|
2035
|
-
inputSchema:
|
|
2036
|
-
status:
|
|
2037
|
-
reason:
|
|
2268
|
+
inputSchema: z8.object({
|
|
2269
|
+
status: z8.enum(["success", "fail"]),
|
|
2270
|
+
reason: z8.string().optional(),
|
|
2038
2271
|
output
|
|
2039
2272
|
}),
|
|
2040
2273
|
execute: async ({ status, reason, output: output2 }) => {
|
|
@@ -2046,8 +2279,8 @@ function reportStatusTool(output) {
|
|
|
2046
2279
|
|
|
2047
2280
|
// src/lib/tools/readFile.ts
|
|
2048
2281
|
import { tool as tool4 } from "ai";
|
|
2049
|
-
import
|
|
2050
|
-
import { readFile as
|
|
2282
|
+
import z9 from "zod";
|
|
2283
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2051
2284
|
|
|
2052
2285
|
// src/lib/tools/env.ts
|
|
2053
2286
|
import { basename } from "node:path";
|
|
@@ -2074,8 +2307,8 @@ function redactEnvValues(content) {
|
|
|
2074
2307
|
function readFileTool(ctx) {
|
|
2075
2308
|
return tool4({
|
|
2076
2309
|
description: "Read the contents of a file at the given path",
|
|
2077
|
-
inputSchema:
|
|
2078
|
-
filePath:
|
|
2310
|
+
inputSchema: z9.object({
|
|
2311
|
+
filePath: z9.string().describe("Path to the file to read")
|
|
2079
2312
|
}),
|
|
2080
2313
|
execute: async ({ filePath }) => {
|
|
2081
2314
|
if (++ctx.counts.read > ctx.limits.read) {
|
|
@@ -2085,7 +2318,7 @@ function readFileTool(ctx) {
|
|
|
2085
2318
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2086
2319
|
if (!resolved.ok) return resolved.error;
|
|
2087
2320
|
try {
|
|
2088
|
-
const content = await
|
|
2321
|
+
const content = await readFile3(resolved.target, "utf8");
|
|
2089
2322
|
return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
|
|
2090
2323
|
} catch (err) {
|
|
2091
2324
|
return `Error reading ${filePath}: ${err.message}`;
|
|
@@ -2096,15 +2329,15 @@ function readFileTool(ctx) {
|
|
|
2096
2329
|
|
|
2097
2330
|
// src/lib/tools/writeFile.ts
|
|
2098
2331
|
import { tool as tool5 } from "ai";
|
|
2099
|
-
import
|
|
2332
|
+
import z10 from "zod";
|
|
2100
2333
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2101
2334
|
import { dirname as dirname4 } from "node:path";
|
|
2102
2335
|
function writeFileTool(ctx) {
|
|
2103
2336
|
return tool5({
|
|
2104
2337
|
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.",
|
|
2105
|
-
inputSchema:
|
|
2106
|
-
filePath:
|
|
2107
|
-
content:
|
|
2338
|
+
inputSchema: z10.object({
|
|
2339
|
+
filePath: z10.string().describe("Path to the file to write"),
|
|
2340
|
+
content: z10.string().describe("Content to write to the file")
|
|
2108
2341
|
}),
|
|
2109
2342
|
execute: async ({ filePath, content }) => {
|
|
2110
2343
|
logger.info({ filePath }, "called writeFile tool");
|
|
@@ -2129,9 +2362,95 @@ function writeFileTool(ctx) {
|
|
|
2129
2362
|
|
|
2130
2363
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2131
2364
|
import { tool as tool6 } from "ai";
|
|
2132
|
-
import
|
|
2133
|
-
import { mkdir as mkdir4, readFile as
|
|
2365
|
+
import z12 from "zod";
|
|
2366
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2134
2367
|
import { dirname as dirname5 } from "node:path";
|
|
2368
|
+
|
|
2369
|
+
// src/lib/algoliaApiKey.ts
|
|
2370
|
+
import { z as z11 } from "zod";
|
|
2371
|
+
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
2372
|
+
var WRITE_ACLS = [
|
|
2373
|
+
"addObject",
|
|
2374
|
+
"deleteObject",
|
|
2375
|
+
"settings",
|
|
2376
|
+
"editSettings",
|
|
2377
|
+
"listIndexes"
|
|
2378
|
+
];
|
|
2379
|
+
var WRITE_ACL_SET = new Set(WRITE_ACLS);
|
|
2380
|
+
var apiKeySchema = z11.object({
|
|
2381
|
+
value: z11.string().min(1),
|
|
2382
|
+
acl: z11.array(z11.string()).default([]),
|
|
2383
|
+
indexes: z11.array(z11.string()).default([])
|
|
2384
|
+
});
|
|
2385
|
+
var apiKeyListSchema = z11.object({
|
|
2386
|
+
items: z11.array(apiKeySchema).optional(),
|
|
2387
|
+
keys: z11.array(apiKeySchema).optional()
|
|
2388
|
+
}).transform((o) => o.items ?? o.keys ?? []);
|
|
2389
|
+
var createdKeySchema = z11.object({
|
|
2390
|
+
key: z11.string().min(1).optional(),
|
|
2391
|
+
value: z11.string().min(1).optional()
|
|
2392
|
+
});
|
|
2393
|
+
function canReuse(key, index) {
|
|
2394
|
+
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
2395
|
+
}
|
|
2396
|
+
async function createSearchKey(index) {
|
|
2397
|
+
const stdout = await runAlgoliaCli([
|
|
2398
|
+
"apikeys",
|
|
2399
|
+
"create",
|
|
2400
|
+
"--indices",
|
|
2401
|
+
index,
|
|
2402
|
+
"--acl",
|
|
2403
|
+
"search,browse",
|
|
2404
|
+
"--description",
|
|
2405
|
+
`wizard search-only key for ${index}`,
|
|
2406
|
+
"-o",
|
|
2407
|
+
"json"
|
|
2408
|
+
]);
|
|
2409
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
|
|
2410
|
+
const created = key ?? value;
|
|
2411
|
+
if (!created) throw new Error("apikeys create returned no key value");
|
|
2412
|
+
return created;
|
|
2413
|
+
}
|
|
2414
|
+
function canReuseForWrites(key, index) {
|
|
2415
|
+
return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
|
|
2416
|
+
}
|
|
2417
|
+
async function resolveWriteKey(index) {
|
|
2418
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2419
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
|
|
2420
|
+
if (existing) {
|
|
2421
|
+
logger.info({ index }, "reusing existing write API key");
|
|
2422
|
+
return existing;
|
|
2423
|
+
}
|
|
2424
|
+
logger.info({ index }, "no reusable write key found; creating one");
|
|
2425
|
+
const created = await runAlgoliaCli([
|
|
2426
|
+
"apikeys",
|
|
2427
|
+
"create",
|
|
2428
|
+
"--indices",
|
|
2429
|
+
index,
|
|
2430
|
+
"--acl",
|
|
2431
|
+
WRITE_ACLS.join(","),
|
|
2432
|
+
"--description",
|
|
2433
|
+
`wizard write key for ${index}`,
|
|
2434
|
+
"-o",
|
|
2435
|
+
"json"
|
|
2436
|
+
]);
|
|
2437
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(created));
|
|
2438
|
+
const writeKey = key ?? value;
|
|
2439
|
+
if (!writeKey) throw new Error("apikeys create returned no key value");
|
|
2440
|
+
return writeKey;
|
|
2441
|
+
}
|
|
2442
|
+
async function resolveSearchOnlyKey(index) {
|
|
2443
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2444
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
|
|
2445
|
+
if (existing) {
|
|
2446
|
+
logger.info({ index }, "reusing existing search-only API key");
|
|
2447
|
+
return existing;
|
|
2448
|
+
}
|
|
2449
|
+
logger.info({ index }, "no reusable search-only key found; creating one");
|
|
2450
|
+
return createSearchKey(index);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2135
2454
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2136
2455
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
2137
2456
|
function appendEnv(content, entries) {
|
|
@@ -2145,9 +2464,9 @@ function hasEnv(content, name) {
|
|
|
2145
2464
|
}
|
|
2146
2465
|
function writeCredentialsTool(ctx) {
|
|
2147
2466
|
return tool6({
|
|
2148
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials
|
|
2149
|
-
inputSchema:
|
|
2150
|
-
filePath:
|
|
2467
|
+
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.`,
|
|
2468
|
+
inputSchema: z12.object({
|
|
2469
|
+
filePath: z12.string().describe(
|
|
2151
2470
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2152
2471
|
)
|
|
2153
2472
|
}),
|
|
@@ -2155,11 +2474,17 @@ function writeCredentialsTool(ctx) {
|
|
|
2155
2474
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2156
2475
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2157
2476
|
if (resolved.ok === false) return resolved.error;
|
|
2158
|
-
|
|
2477
|
+
const targetIndex = useWizard.getState().targetIndex;
|
|
2478
|
+
if (!targetIndex) {
|
|
2479
|
+
return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
|
|
2480
|
+
}
|
|
2481
|
+
let appId;
|
|
2482
|
+
let writeKey;
|
|
2159
2483
|
try {
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2484
|
+
appId = (await requireApplication()).id;
|
|
2485
|
+
writeKey = await resolveWriteKey(targetIndex);
|
|
2486
|
+
} catch (err) {
|
|
2487
|
+
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2163
2488
|
}
|
|
2164
2489
|
try {
|
|
2165
2490
|
if (await hasSymlinkParent(ctx, resolved.target)) {
|
|
@@ -2167,7 +2492,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2167
2492
|
}
|
|
2168
2493
|
let existing = "";
|
|
2169
2494
|
try {
|
|
2170
|
-
existing = await
|
|
2495
|
+
existing = await readFile4(resolved.target, "utf8");
|
|
2171
2496
|
} catch (err) {
|
|
2172
2497
|
if (err.code !== "ENOENT") throw err;
|
|
2173
2498
|
}
|
|
@@ -2178,8 +2503,8 @@ function writeCredentialsTool(ctx) {
|
|
|
2178
2503
|
return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
|
|
2179
2504
|
}
|
|
2180
2505
|
const envWithCredentials = appendEnv(existing, [
|
|
2181
|
-
[APP_ID_VAR,
|
|
2182
|
-
[API_KEY_VAR,
|
|
2506
|
+
[APP_ID_VAR, appId],
|
|
2507
|
+
[API_KEY_VAR, writeKey]
|
|
2183
2508
|
]);
|
|
2184
2509
|
await mkdir4(dirname5(resolved.target), { recursive: true });
|
|
2185
2510
|
await writeFile4(resolved.target, envWithCredentials, "utf8");
|
|
@@ -2193,16 +2518,16 @@ function writeCredentialsTool(ctx) {
|
|
|
2193
2518
|
|
|
2194
2519
|
// src/lib/tools/searchFiles.ts
|
|
2195
2520
|
import { tool as tool7 } from "ai";
|
|
2196
|
-
import
|
|
2197
|
-
import { readdir as readdir2, readFile as
|
|
2198
|
-
import { join as
|
|
2521
|
+
import z13 from "zod";
|
|
2522
|
+
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2523
|
+
import { join as join7 } from "node:path";
|
|
2199
2524
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2200
2525
|
async function walkFiles(dir) {
|
|
2201
2526
|
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2202
2527
|
const out = [];
|
|
2203
2528
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2204
2529
|
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2205
|
-
const full =
|
|
2530
|
+
const full = join7(dir, e.name);
|
|
2206
2531
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2207
2532
|
else if (e.isFile()) out.push(full);
|
|
2208
2533
|
}
|
|
@@ -2211,9 +2536,9 @@ async function walkFiles(dir) {
|
|
|
2211
2536
|
function searchFilesTool(ctx) {
|
|
2212
2537
|
return tool7({
|
|
2213
2538
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2214
|
-
inputSchema:
|
|
2215
|
-
query:
|
|
2216
|
-
path:
|
|
2539
|
+
inputSchema: z13.object({
|
|
2540
|
+
query: z13.string().describe("JavaScript RegExp pattern to search for"),
|
|
2541
|
+
path: z13.string().optional().describe("Directory to search in (default: cwd)")
|
|
2217
2542
|
}),
|
|
2218
2543
|
execute: async ({ query, path = "." }) => {
|
|
2219
2544
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2235,7 +2560,7 @@ function searchFilesTool(ctx) {
|
|
|
2235
2560
|
for (const file of await walkFiles(resolved.target)) {
|
|
2236
2561
|
let content;
|
|
2237
2562
|
try {
|
|
2238
|
-
content = await
|
|
2563
|
+
content = await readFile5(file, "utf8");
|
|
2239
2564
|
} catch {
|
|
2240
2565
|
continue;
|
|
2241
2566
|
}
|
|
@@ -2257,7 +2582,7 @@ function searchFilesTool(ctx) {
|
|
|
2257
2582
|
|
|
2258
2583
|
// src/lib/tools/verifyImplementation.ts
|
|
2259
2584
|
import { tool as tool8 } from "ai";
|
|
2260
|
-
import
|
|
2585
|
+
import z14 from "zod";
|
|
2261
2586
|
|
|
2262
2587
|
// src/lib/tools/utils/runCommand.ts
|
|
2263
2588
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -2279,9 +2604,9 @@ function runCommand(command, args, cwd) {
|
|
|
2279
2604
|
}
|
|
2280
2605
|
|
|
2281
2606
|
// src/lib/tools/utils/packageManager.ts
|
|
2282
|
-
import { readFile as
|
|
2607
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2283
2608
|
import { existsSync } from "node:fs";
|
|
2284
|
-
import { join as
|
|
2609
|
+
import { join as join8 } from "node:path";
|
|
2285
2610
|
var LOCKFILES = [
|
|
2286
2611
|
["pnpm-lock.yaml", "pnpm"],
|
|
2287
2612
|
["yarn.lock", "yarn"],
|
|
@@ -2290,13 +2615,13 @@ var LOCKFILES = [
|
|
|
2290
2615
|
["package-lock.json", "npm"]
|
|
2291
2616
|
];
|
|
2292
2617
|
async function readPackageJson(cwd = process.cwd()) {
|
|
2293
|
-
return JSON.parse(await
|
|
2618
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2294
2619
|
}
|
|
2295
2620
|
function packageManagerFrom(pkg) {
|
|
2296
2621
|
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2297
2622
|
}
|
|
2298
2623
|
function packageManagerFromLockfile(cwd) {
|
|
2299
|
-
return LOCKFILES.find(([file]) => existsSync(
|
|
2624
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2300
2625
|
}
|
|
2301
2626
|
async function detectPackageManager(cwd) {
|
|
2302
2627
|
try {
|
|
@@ -2337,7 +2662,7 @@ async function runRepoVerificationCheck() {
|
|
|
2337
2662
|
function verifyImplementationTool() {
|
|
2338
2663
|
return tool8({
|
|
2339
2664
|
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.",
|
|
2340
|
-
inputSchema:
|
|
2665
|
+
inputSchema: z14.object(),
|
|
2341
2666
|
execute: async () => {
|
|
2342
2667
|
logger.info("called verifyImplementation tool");
|
|
2343
2668
|
return runRepoVerificationCheck();
|
|
@@ -2351,7 +2676,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2351
2676
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2352
2677
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2353
2678
|
import { dirname as dirname6 } from "node:path";
|
|
2354
|
-
import
|
|
2679
|
+
import z15 from "zod";
|
|
2355
2680
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2356
2681
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2357
2682
|
var MAX_RECORDS = 100;
|
|
@@ -2363,17 +2688,17 @@ var anthropic = createAnthropic({
|
|
|
2363
2688
|
function generateRecordTool(ctx) {
|
|
2364
2689
|
return tool9({
|
|
2365
2690
|
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.",
|
|
2366
|
-
inputSchema:
|
|
2367
|
-
entityName:
|
|
2368
|
-
attributes:
|
|
2369
|
-
count:
|
|
2370
|
-
hint:
|
|
2691
|
+
inputSchema: z15.object({
|
|
2692
|
+
entityName: z15.string().describe("Name of the entity to generate records for."),
|
|
2693
|
+
attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
|
|
2694
|
+
count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
2695
|
+
hint: z15.string().optional().describe("Optional context to steer realistic values.")
|
|
2371
2696
|
}),
|
|
2372
2697
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2373
2698
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2374
2699
|
try {
|
|
2375
|
-
const value =
|
|
2376
|
-
const recordSchema =
|
|
2700
|
+
const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
|
|
2701
|
+
const recordSchema = z15.object(
|
|
2377
2702
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2378
2703
|
);
|
|
2379
2704
|
const generateBatch = async (batchCount) => {
|
|
@@ -2383,8 +2708,8 @@ function generateRecordTool(ctx) {
|
|
|
2383
2708
|
const { output } = await generateText({
|
|
2384
2709
|
model: anthropic(RECORD_MODEL),
|
|
2385
2710
|
output: Output.object({
|
|
2386
|
-
schema:
|
|
2387
|
-
records:
|
|
2711
|
+
schema: z15.object({
|
|
2712
|
+
records: z15.array(recordSchema).length(batchCount)
|
|
2388
2713
|
})
|
|
2389
2714
|
}),
|
|
2390
2715
|
prompt: [
|
|
@@ -2442,12 +2767,12 @@ function generateRecordTool(ctx) {
|
|
|
2442
2767
|
|
|
2443
2768
|
// src/lib/tools/notifyUser.ts
|
|
2444
2769
|
import { tool as tool10 } from "ai";
|
|
2445
|
-
import
|
|
2770
|
+
import z16 from "zod";
|
|
2446
2771
|
function notifyUserTool() {
|
|
2447
2772
|
return tool10({
|
|
2448
2773
|
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.`,
|
|
2449
|
-
inputSchema:
|
|
2450
|
-
message:
|
|
2774
|
+
inputSchema: z16.object({
|
|
2775
|
+
message: z16.string().describe(
|
|
2451
2776
|
"Short, plain-language description of what you are doing now."
|
|
2452
2777
|
)
|
|
2453
2778
|
}),
|
|
@@ -2627,10 +2952,10 @@ async function runAgent(req) {
|
|
|
2627
2952
|
}
|
|
2628
2953
|
|
|
2629
2954
|
// src/actions/detectLanguage.ts
|
|
2630
|
-
import
|
|
2631
|
-
var detectLanguageSchema =
|
|
2632
|
-
languages:
|
|
2633
|
-
frameworks:
|
|
2955
|
+
import z19 from "zod";
|
|
2956
|
+
var detectLanguageSchema = z19.object({
|
|
2957
|
+
languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
|
|
2958
|
+
frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
|
|
2634
2959
|
});
|
|
2635
2960
|
var detectLanguage = () => runAgent({
|
|
2636
2961
|
instructions: [
|
|
@@ -2648,31 +2973,31 @@ var detectLanguage = () => runAgent({
|
|
|
2648
2973
|
});
|
|
2649
2974
|
|
|
2650
2975
|
// src/actions/analyzeCodebase.ts
|
|
2651
|
-
import
|
|
2976
|
+
import z20 from "zod";
|
|
2652
2977
|
var READONLY_TOOLS = [
|
|
2653
2978
|
"listFiles",
|
|
2654
2979
|
"changeDirectory",
|
|
2655
2980
|
"readFile",
|
|
2656
2981
|
"searchFiles"
|
|
2657
2982
|
];
|
|
2658
|
-
var ingestionAnalysisSchema =
|
|
2659
|
-
ingestionAnalysis:
|
|
2660
|
-
|
|
2661
|
-
name:
|
|
2662
|
-
paths:
|
|
2983
|
+
var ingestionAnalysisSchema = z20.object({
|
|
2984
|
+
ingestionAnalysis: z20.array(
|
|
2985
|
+
z20.object({
|
|
2986
|
+
name: z20.string(),
|
|
2987
|
+
paths: z20.array(z20.string()),
|
|
2663
2988
|
// indexable fields the agent found for this entity
|
|
2664
|
-
attributes:
|
|
2989
|
+
attributes: z20.array(z20.string())
|
|
2665
2990
|
})
|
|
2666
2991
|
)
|
|
2667
2992
|
});
|
|
2668
|
-
var searchImplementationAnalysisSchema =
|
|
2669
|
-
searchImplementationAnalysis:
|
|
2993
|
+
var searchImplementationAnalysisSchema = z20.object({
|
|
2994
|
+
searchImplementationAnalysis: z20.string()
|
|
2670
2995
|
});
|
|
2671
|
-
var verificationSchema =
|
|
2672
|
-
verification:
|
|
2996
|
+
var verificationSchema = z20.object({
|
|
2997
|
+
verification: z20.array(z20.string())
|
|
2673
2998
|
});
|
|
2674
2999
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
2675
|
-
var analyzeCodebaseSchema =
|
|
3000
|
+
var analyzeCodebaseSchema = z20.object({
|
|
2676
3001
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2677
3002
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
2678
3003
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -2734,7 +3059,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2734
3059
|
// package.json
|
|
2735
3060
|
var package_default = {
|
|
2736
3061
|
name: "@algolia/wizard",
|
|
2737
|
-
version: "0.8.0-rc.
|
|
3062
|
+
version: "0.8.0-rc.58.43",
|
|
2738
3063
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2739
3064
|
type: "module",
|
|
2740
3065
|
engines: {
|
|
@@ -2782,7 +3107,6 @@ var package_default = {
|
|
|
2782
3107
|
dependencies: {
|
|
2783
3108
|
"@ai-sdk/anthropic": "^3.0.81",
|
|
2784
3109
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
2785
|
-
"@algolia/cli": "^5.15.0",
|
|
2786
3110
|
"@hono/node-server": "^2.0.10",
|
|
2787
3111
|
"@mishieck/ink-titled-box": "^0.4.2",
|
|
2788
3112
|
"@segment/analytics-node": "^3.1.0",
|
|
@@ -2797,7 +3121,6 @@ var package_default = {
|
|
|
2797
3121
|
nanoid: "^5.1.15",
|
|
2798
3122
|
pino: "^10.3.1",
|
|
2799
3123
|
react: "^19.2.7",
|
|
2800
|
-
toml: "^4.1.1",
|
|
2801
3124
|
varlock: "^1.5.1",
|
|
2802
3125
|
zod: "^4.4.3",
|
|
2803
3126
|
zustand: "^5.0.14"
|
|
@@ -2855,8 +3178,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
2855
3178
|
}
|
|
2856
3179
|
|
|
2857
3180
|
// src/actions/confirmLanguage.ts
|
|
2858
|
-
import
|
|
2859
|
-
var confirmLanguageSchema =
|
|
3181
|
+
import z22 from "zod";
|
|
3182
|
+
var confirmLanguageSchema = z22.object({
|
|
2860
3183
|
languages: detectLanguageSchema.shape.languages
|
|
2861
3184
|
});
|
|
2862
3185
|
async function confirmLanguage(ctx) {
|
|
@@ -2877,8 +3200,8 @@ async function confirmLanguage(ctx) {
|
|
|
2877
3200
|
}
|
|
2878
3201
|
|
|
2879
3202
|
// src/actions/confirmFramework.ts
|
|
2880
|
-
import
|
|
2881
|
-
var confirmFrameworkSchema =
|
|
3203
|
+
import z23 from "zod";
|
|
3204
|
+
var confirmFrameworkSchema = z23.object({
|
|
2882
3205
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
2883
3206
|
});
|
|
2884
3207
|
var CURATED_FRAMEWORKS = [
|
|
@@ -3006,8 +3329,8 @@ async function promptUser(ctx, params) {
|
|
|
3006
3329
|
}
|
|
3007
3330
|
|
|
3008
3331
|
// src/actions/confirmEntities.ts
|
|
3009
|
-
import
|
|
3010
|
-
var confirmEntitiesSchema =
|
|
3332
|
+
import z24 from "zod";
|
|
3333
|
+
var confirmEntitiesSchema = z24.object({
|
|
3011
3334
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
3012
3335
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3013
3336
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3077,15 +3400,15 @@ async function confirmEntities(ctx) {
|
|
|
3077
3400
|
}
|
|
3078
3401
|
|
|
3079
3402
|
// src/actions/review.ts
|
|
3080
|
-
import { z as
|
|
3081
|
-
var reviewSchema =
|
|
3403
|
+
import { z as z25 } from "zod";
|
|
3404
|
+
var reviewSchema = z25.object({
|
|
3082
3405
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3083
3406
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3084
3407
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3085
3408
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3086
|
-
summaryPoints:
|
|
3087
|
-
reviewPrompt:
|
|
3088
|
-
nextSteps:
|
|
3409
|
+
summaryPoints: z25.array(z25.string()),
|
|
3410
|
+
reviewPrompt: z25.string(),
|
|
3411
|
+
nextSteps: z25.array(z25.string())
|
|
3089
3412
|
});
|
|
3090
3413
|
function formatCompletedSteps(steps) {
|
|
3091
3414
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3136,16 +3459,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3136
3459
|
};
|
|
3137
3460
|
|
|
3138
3461
|
// src/actions/implement.ts
|
|
3139
|
-
import
|
|
3462
|
+
import z26 from "zod";
|
|
3140
3463
|
|
|
3141
3464
|
// src/lib/worktree.ts
|
|
3142
3465
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3143
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3466
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3144
3467
|
import {
|
|
3145
3468
|
basename as basename2,
|
|
3146
3469
|
dirname as dirname7,
|
|
3147
3470
|
isAbsolute as isAbsolute2,
|
|
3148
|
-
join as
|
|
3471
|
+
join as join9,
|
|
3149
3472
|
relative as relative2,
|
|
3150
3473
|
resolve as resolve3
|
|
3151
3474
|
} from "node:path";
|
|
@@ -3179,7 +3502,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3179
3502
|
return out.trim().length > 0;
|
|
3180
3503
|
}
|
|
3181
3504
|
async function pruneOldWorktrees(repoRoot) {
|
|
3182
|
-
const dir =
|
|
3505
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3183
3506
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3184
3507
|
for (const slug of stale) {
|
|
3185
3508
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3190,7 +3513,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3190
3513
|
"worktree",
|
|
3191
3514
|
"remove",
|
|
3192
3515
|
"--force",
|
|
3193
|
-
|
|
3516
|
+
join9(dir, slug)
|
|
3194
3517
|
]);
|
|
3195
3518
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3196
3519
|
} catch (err) {
|
|
@@ -3204,7 +3527,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3204
3527
|
async function createWorktree(repoRoot) {
|
|
3205
3528
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3206
3529
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3207
|
-
const path =
|
|
3530
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3208
3531
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3209
3532
|
await pruneOldWorktrees(repoRoot);
|
|
3210
3533
|
await mkdir6(dirname7(path), { recursive: true });
|
|
@@ -3324,8 +3647,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3324
3647
|
} catch {
|
|
3325
3648
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3326
3649
|
}
|
|
3327
|
-
const relPath =
|
|
3328
|
-
const dest =
|
|
3650
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3651
|
+
const dest = join9(worktreePath, relPath);
|
|
3329
3652
|
try {
|
|
3330
3653
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3331
3654
|
await copyFile(source, dest);
|
|
@@ -3340,28 +3663,11 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3340
3663
|
function hasEnvVar(content, name) {
|
|
3341
3664
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
3342
3665
|
}
|
|
3343
|
-
async function readEnvVar(worktreePath, name) {
|
|
3344
|
-
let content;
|
|
3345
|
-
try {
|
|
3346
|
-
content = await readFile8(join10(worktreePath, ".env"), "utf8");
|
|
3347
|
-
} catch (err) {
|
|
3348
|
-
if (err.code !== "ENOENT") throw err;
|
|
3349
|
-
return void 0;
|
|
3350
|
-
}
|
|
3351
|
-
const match = new RegExp(
|
|
3352
|
-
`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*(.*)$`,
|
|
3353
|
-
"m"
|
|
3354
|
-
).exec(content);
|
|
3355
|
-
if (!match) return void 0;
|
|
3356
|
-
const value = match[1].trim().replace(/^(['"])(.*)\1$/, "$2").trim();
|
|
3357
|
-
if (!value || value.startsWith("<")) return void 0;
|
|
3358
|
-
return value;
|
|
3359
|
-
}
|
|
3360
3666
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3361
|
-
const target =
|
|
3667
|
+
const target = join9(worktreePath, ".env");
|
|
3362
3668
|
let existing = "";
|
|
3363
3669
|
try {
|
|
3364
|
-
existing = await
|
|
3670
|
+
existing = await readFile7(target, "utf8");
|
|
3365
3671
|
} catch (err) {
|
|
3366
3672
|
if (err.code !== "ENOENT") throw err;
|
|
3367
3673
|
}
|
|
@@ -3429,85 +3735,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3429
3735
|
}
|
|
3430
3736
|
}
|
|
3431
3737
|
|
|
3432
|
-
// src/lib/algoliaApiKey.ts
|
|
3433
|
-
import { z as z23 } from "zod";
|
|
3434
|
-
var createdKeySchema = z23.object({
|
|
3435
|
-
key: z23.string().min(1).optional(),
|
|
3436
|
-
value: z23.string().min(1).optional()
|
|
3437
|
-
}).transform((o) => o.key ?? o.value);
|
|
3438
|
-
async function createSearchOnlyKey(index) {
|
|
3439
|
-
logger.info({ index }, "creating a search-only API key");
|
|
3440
|
-
const stdout = await runAlgoliaCli([
|
|
3441
|
-
"apikeys",
|
|
3442
|
-
"create",
|
|
3443
|
-
"--acl",
|
|
3444
|
-
"search",
|
|
3445
|
-
"--indices",
|
|
3446
|
-
index,
|
|
3447
|
-
"--description",
|
|
3448
|
-
`Algolia Wizard search-only key for ${index}`,
|
|
3449
|
-
"-o",
|
|
3450
|
-
"json"
|
|
3451
|
-
]);
|
|
3452
|
-
let payload;
|
|
3453
|
-
try {
|
|
3454
|
-
payload = JSON.parse(stdout);
|
|
3455
|
-
} catch {
|
|
3456
|
-
throw new Error("apikeys create returned output that is not valid JSON");
|
|
3457
|
-
}
|
|
3458
|
-
const created = createdKeySchema.parse(payload);
|
|
3459
|
-
if (!created) throw new Error("apikeys create returned no key value");
|
|
3460
|
-
return created;
|
|
3461
|
-
}
|
|
3462
|
-
async function apiKeyExists(key) {
|
|
3463
|
-
try {
|
|
3464
|
-
await runAlgoliaCli(["apikeys", "get", key, "-o", "json"]);
|
|
3465
|
-
return true;
|
|
3466
|
-
} catch (err) {
|
|
3467
|
-
return !/does not exist|not found|404/i.test(err.message);
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
async function resolveSearchOnlyKey(index, appId, envKey) {
|
|
3471
|
-
if (envKey) {
|
|
3472
|
-
await recordSearchKey(index, appId, envKey);
|
|
3473
|
-
return { key: envKey, source: "env" };
|
|
3474
|
-
}
|
|
3475
|
-
const stored = await getStoredSearchKey(index, appId);
|
|
3476
|
-
if (stored) {
|
|
3477
|
-
if (await apiKeyExists(stored)) {
|
|
3478
|
-
logger.info({ index, appId }, "reusing the stored search-only API key");
|
|
3479
|
-
return { key: stored, source: "config" };
|
|
3480
|
-
}
|
|
3481
|
-
logger.warn(
|
|
3482
|
-
{ index, appId },
|
|
3483
|
-
"the stored search-only API key no longer exists; creating a replacement"
|
|
3484
|
-
);
|
|
3485
|
-
await forgetSearchKey(index);
|
|
3486
|
-
}
|
|
3487
|
-
const key = await createSearchOnlyKey(index);
|
|
3488
|
-
await recordSearchKey(index, appId, key);
|
|
3489
|
-
return { key, source: "created" };
|
|
3490
|
-
}
|
|
3491
|
-
async function recordSearchKey(index, appId, key) {
|
|
3492
|
-
try {
|
|
3493
|
-
await storeSearchKey(index, appId, key);
|
|
3494
|
-
} catch (err) {
|
|
3495
|
-
logger.warn(
|
|
3496
|
-
{ err: err.message, index },
|
|
3497
|
-
"could not record the search-only API key; a later run may create another"
|
|
3498
|
-
);
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
3738
|
// src/lib/algoliaDocs.ts
|
|
3503
3739
|
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3504
|
-
import { dirname as dirname8, join as
|
|
3740
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3505
3741
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3506
|
-
var DOCS_SUBPATH =
|
|
3742
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3507
3743
|
function findDocsDir() {
|
|
3508
3744
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3509
3745
|
for (; ; ) {
|
|
3510
|
-
const candidate =
|
|
3746
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3511
3747
|
if (existsSync2(candidate)) return candidate;
|
|
3512
3748
|
const parent = dirname8(dir);
|
|
3513
3749
|
if (parent === dir) return void 0;
|
|
@@ -3530,7 +3766,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3530
3766
|
);
|
|
3531
3767
|
return "";
|
|
3532
3768
|
}
|
|
3533
|
-
return readFileSync(
|
|
3769
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3534
3770
|
}
|
|
3535
3771
|
function getNamedDoc(name, language) {
|
|
3536
3772
|
const docsDir = findDocsDir();
|
|
@@ -3538,7 +3774,7 @@ function getNamedDoc(name, language) {
|
|
|
3538
3774
|
logger.warn("docs/algolia-sdk not found");
|
|
3539
3775
|
return "";
|
|
3540
3776
|
}
|
|
3541
|
-
const file =
|
|
3777
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3542
3778
|
if (!existsSync2(file)) {
|
|
3543
3779
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3544
3780
|
return "";
|
|
@@ -3565,50 +3801,50 @@ function shellQuote(value) {
|
|
|
3565
3801
|
}
|
|
3566
3802
|
|
|
3567
3803
|
// src/actions/implement.ts
|
|
3568
|
-
var implementSchema =
|
|
3569
|
-
filesChanged:
|
|
3570
|
-
summary:
|
|
3804
|
+
var implementSchema = z26.object({
|
|
3805
|
+
filesChanged: z26.array(z26.string()),
|
|
3806
|
+
summary: z26.string(),
|
|
3571
3807
|
// Absolute path to the throwaway worktree holding the generated changes, so
|
|
3572
3808
|
// the user can open it (`cd <worktreePath>`) or inspect the diff
|
|
3573
3809
|
// (`git -C <worktreePath> status/diff`).
|
|
3574
|
-
worktreePath:
|
|
3575
|
-
ingestCommand:
|
|
3810
|
+
worktreePath: z26.string().optional(),
|
|
3811
|
+
ingestCommand: z26.string().optional(),
|
|
3576
3812
|
// True when the user accepted the run-now prompt and the wizard executed the
|
|
3577
3813
|
// ingestion script; downstream steps use this to avoid telling the user to run
|
|
3578
3814
|
// a script that already ran.
|
|
3579
|
-
ingestScriptRan:
|
|
3815
|
+
ingestScriptRan: z26.boolean().optional(),
|
|
3580
3816
|
// Records ingested by the run-now execution, parsed from the script's
|
|
3581
3817
|
// machine-readable count line; absent when the script didn't run or emitted
|
|
3582
3818
|
// no parseable count.
|
|
3583
|
-
ingestRecordCount:
|
|
3819
|
+
ingestRecordCount: z26.number().optional(),
|
|
3584
3820
|
// Wall-clock duration of the run-now ingestion execution, in ms.
|
|
3585
|
-
ingestDurationMs:
|
|
3586
|
-
ingestionSource:
|
|
3821
|
+
ingestDurationMs: z26.number().optional(),
|
|
3822
|
+
ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
|
|
3587
3823
|
// Suggested names/values, built from framework detection. The search agent is
|
|
3588
3824
|
// instructed to rename the prefix if it doesn't match the project's build
|
|
3589
3825
|
// tool, so the names it actually wrote can differ — treat these as hints, not
|
|
3590
3826
|
// ground truth (the agent's summary carries the final names).
|
|
3591
|
-
searchEnvVars:
|
|
3592
|
-
|
|
3593
|
-
name:
|
|
3594
|
-
value:
|
|
3827
|
+
searchEnvVars: z26.array(
|
|
3828
|
+
z26.object({
|
|
3829
|
+
name: z26.string(),
|
|
3830
|
+
value: z26.string()
|
|
3595
3831
|
})
|
|
3596
3832
|
).optional()
|
|
3597
3833
|
});
|
|
3598
|
-
var implementationOutputSchema =
|
|
3599
|
-
summary:
|
|
3834
|
+
var implementationOutputSchema = z26.object({
|
|
3835
|
+
summary: z26.string(),
|
|
3600
3836
|
// Ingestion only: how to run the generated script, as a structured pair the
|
|
3601
3837
|
// wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
|
|
3602
3838
|
// command string. `runtime` is constrained to an allowlisted interpreter and
|
|
3603
3839
|
// `entrypoint` is validated to a worktree-relative path before execution, so
|
|
3604
3840
|
// the agent cannot inject extra commands or swap the interpreter.
|
|
3605
|
-
runtime:
|
|
3606
|
-
entrypoint:
|
|
3841
|
+
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3842
|
+
entrypoint: z26.string().optional()
|
|
3607
3843
|
});
|
|
3608
|
-
var verificationOutputSchema =
|
|
3609
|
-
summary:
|
|
3610
|
-
sufficient:
|
|
3611
|
-
additionalInstructions:
|
|
3844
|
+
var verificationOutputSchema = z26.object({
|
|
3845
|
+
summary: z26.string(),
|
|
3846
|
+
sufficient: z26.boolean(),
|
|
3847
|
+
additionalInstructions: z26.string().optional()
|
|
3612
3848
|
});
|
|
3613
3849
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3614
3850
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -3651,22 +3887,15 @@ function publicEnvPrefix(language) {
|
|
|
3651
3887
|
}
|
|
3652
3888
|
return "PUBLIC_";
|
|
3653
3889
|
}
|
|
3654
|
-
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
3655
|
-
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
3656
|
-
function appIdVar(language) {
|
|
3657
|
-
return `${publicEnvPrefix(language)}${APP_ID_VAR_SUFFIX}`;
|
|
3658
|
-
}
|
|
3659
|
-
function searchKeyVar(language) {
|
|
3660
|
-
return `${publicEnvPrefix(language)}${SEARCH_KEY_VAR_SUFFIX}`;
|
|
3661
|
-
}
|
|
3662
3890
|
function searchEnvVars(language, appId, searchKey) {
|
|
3891
|
+
const prefix = publicEnvPrefix(language);
|
|
3663
3892
|
return [
|
|
3664
3893
|
{
|
|
3665
|
-
name:
|
|
3894
|
+
name: `${prefix}ALGOLIA_APP_ID`,
|
|
3666
3895
|
value: appId ?? "<your-algolia-app-id>"
|
|
3667
3896
|
},
|
|
3668
3897
|
{
|
|
3669
|
-
name:
|
|
3898
|
+
name: `${prefix}ALGOLIA_SEARCH_API_KEY`,
|
|
3670
3899
|
value: searchKey ?? "<your-algolia-search-only-api-key>"
|
|
3671
3900
|
}
|
|
3672
3901
|
];
|
|
@@ -3728,13 +3957,15 @@ function searchInstructions(input) {
|
|
|
3728
3957
|
"Follow the Algolia JS SDK reference below for client setup and InstantSearch wiring; prefer it over prior knowledge:",
|
|
3729
3958
|
doc,
|
|
3730
3959
|
`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.`,
|
|
3731
|
-
// The key is provisioned only after verification passes, so the agent never
|
|
3732
|
-
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
3733
|
-
// decide whether a key already exists, and an agent-invented value there
|
|
3734
|
-
// would be reused as if it were real.
|
|
3735
|
-
`Add Algolia App ID "${input.appId}"; leave the search-only key as a placeholder. Do not create or edit .env \u2014 the wizard writes the resolved key there itself.`,
|
|
3736
|
-
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
3737
3960
|
"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.",
|
|
3961
|
+
// appId always resolves (requireApplication throws otherwise); only the
|
|
3962
|
+
// search-only key is best-effort and can fall back to a placeholder.
|
|
3963
|
+
`Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
|
|
3964
|
+
// Names are fixed, not the agent's to rename: the wizard writes the
|
|
3965
|
+
// resolved app id / search-only key into ".env" under these exact names
|
|
3966
|
+
// right after this step, so a renamed prefix here would leave the code
|
|
3967
|
+
// reading a var the wizard never wrote.
|
|
3968
|
+
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
3738
3969
|
'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
|
|
3739
3970
|
"The summary should be extremely concise; do not mention env var setup or manual testing steps \u2014 the wizard writes the resolved credentials to .env and reports that separately."
|
|
3740
3971
|
];
|
|
@@ -3876,6 +4107,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3876
4107
|
}
|
|
3877
4108
|
}
|
|
3878
4109
|
const targetIndex = selected?.selection;
|
|
4110
|
+
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
3879
4111
|
await assertGitRepoWithHead(repoRoot);
|
|
3880
4112
|
if (await isWorkingTreeDirty(repoRoot)) {
|
|
3881
4113
|
await confirmDirtyWorkingTree(ctx, repoRoot);
|
|
@@ -3884,8 +4116,17 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3884
4116
|
const confirmed2 = normalized.confirmedEntities;
|
|
3885
4117
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
3886
4118
|
let appId;
|
|
4119
|
+
let searchKey;
|
|
3887
4120
|
if (useCases.includes("search")) {
|
|
3888
|
-
appId = (await
|
|
4121
|
+
appId = (await requireApplication()).id;
|
|
4122
|
+
try {
|
|
4123
|
+
searchKey = await resolveSearchOnlyKey(targetIndex);
|
|
4124
|
+
} catch (err) {
|
|
4125
|
+
logger.warn(
|
|
4126
|
+
{ err: err.message },
|
|
4127
|
+
"implement: could not resolve a search-only API key; the agent will scaffold a placeholder"
|
|
4128
|
+
);
|
|
4129
|
+
}
|
|
3889
4130
|
}
|
|
3890
4131
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
3891
4132
|
try {
|
|
@@ -3917,9 +4158,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3917
4158
|
targetIndex,
|
|
3918
4159
|
language,
|
|
3919
4160
|
appId,
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
searchEnvVars: searchEnvVars(language, appId),
|
|
4161
|
+
searchKey,
|
|
4162
|
+
searchEnvVars: searchEnvVars(language, appId, searchKey),
|
|
3923
4163
|
ingestDir: INGEST_DIR,
|
|
3924
4164
|
ingestionSource,
|
|
3925
4165
|
uploadFilePath,
|
|
@@ -3929,24 +4169,6 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3929
4169
|
};
|
|
3930
4170
|
const summaries = [];
|
|
3931
4171
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
3932
|
-
let envSearchKey;
|
|
3933
|
-
let envAppIdMismatch = false;
|
|
3934
|
-
if (useCases.includes("search") && appId) {
|
|
3935
|
-
const envAppId = await readEnvVar(worktree, appIdVar(language));
|
|
3936
|
-
if (envAppId === appId) {
|
|
3937
|
-
envSearchKey = await readEnvVar(worktree, searchKeyVar(language));
|
|
3938
|
-
} else if (envAppId) {
|
|
3939
|
-
envAppIdMismatch = true;
|
|
3940
|
-
summaries.push(
|
|
3941
|
-
`\u26A0\uFE0F .env already sets ${appIdVar(language)}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVar(language)} and ${searchKeyVar(language)} by hand, or searches will fail.`
|
|
3942
|
-
);
|
|
3943
|
-
logger.warn(
|
|
3944
|
-
{ envAppId, appId },
|
|
3945
|
-
"implement: .env holds credentials for a different Algolia application; not reusing its search key"
|
|
3946
|
-
);
|
|
3947
|
-
}
|
|
3948
|
-
}
|
|
3949
|
-
let finalSearchEnvVars = input.searchEnvVars;
|
|
3950
4172
|
let agentRuns = 0;
|
|
3951
4173
|
let ingestRuntime;
|
|
3952
4174
|
let ingestEntrypoint;
|
|
@@ -4007,7 +4229,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4007
4229
|
messages: []
|
|
4008
4230
|
}) === true;
|
|
4009
4231
|
if (runNow) {
|
|
4010
|
-
const
|
|
4232
|
+
const ingestApp = await requireApplication();
|
|
4233
|
+
const writeKey = await resolveWriteKey(targetIndex);
|
|
4011
4234
|
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
4012
4235
|
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
4013
4236
|
runtime: ingestRuntime,
|
|
@@ -4019,8 +4242,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4019
4242
|
ingestRuntime,
|
|
4020
4243
|
ingestEntrypoint,
|
|
4021
4244
|
{
|
|
4022
|
-
[APP_ID_VAR]:
|
|
4023
|
-
[API_KEY_VAR]:
|
|
4245
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4246
|
+
[API_KEY_VAR]: writeKey
|
|
4024
4247
|
}
|
|
4025
4248
|
);
|
|
4026
4249
|
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
@@ -4139,29 +4362,7 @@ ${run2.output}` : status;
|
|
|
4139
4362
|
}
|
|
4140
4363
|
extraInstructions = verificationRetryInstructions(verification);
|
|
4141
4364
|
}
|
|
4142
|
-
|
|
4143
|
-
let searchKeyError;
|
|
4144
|
-
if (appId) {
|
|
4145
|
-
try {
|
|
4146
|
-
const resolved = await resolveSearchOnlyKey(
|
|
4147
|
-
targetIndex,
|
|
4148
|
-
appId,
|
|
4149
|
-
envSearchKey
|
|
4150
|
-
);
|
|
4151
|
-
searchKey = resolved.key;
|
|
4152
|
-
summaries.push(
|
|
4153
|
-
resolved.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4154
|
-
);
|
|
4155
|
-
} catch (err) {
|
|
4156
|
-
searchKeyError = err.message;
|
|
4157
|
-
logger.warn(
|
|
4158
|
-
{ err: searchKeyError },
|
|
4159
|
-
"implement: could not provision a search-only API key; the .env value stays a placeholder"
|
|
4160
|
-
);
|
|
4161
|
-
}
|
|
4162
|
-
}
|
|
4163
|
-
finalSearchEnvVars = searchEnvVars(language, appId, searchKey);
|
|
4164
|
-
const resolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
4365
|
+
const resolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
4165
4366
|
(v) => !v.value.startsWith("<")
|
|
4166
4367
|
);
|
|
4167
4368
|
if (resolvedSearchEnvVars.length > 0) {
|
|
@@ -4172,29 +4373,13 @@ ${run2.output}` : status;
|
|
|
4172
4373
|
if (written.length > 0) {
|
|
4173
4374
|
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
4174
4375
|
}
|
|
4175
|
-
const stale = [];
|
|
4176
|
-
for (const v of resolvedSearchEnvVars) {
|
|
4177
|
-
if (written.includes(v.name)) continue;
|
|
4178
|
-
const current = await readEnvVar(worktree, v.name);
|
|
4179
|
-
if (current && current !== v.value) stale.push(v);
|
|
4180
|
-
}
|
|
4181
|
-
if (stale.length > 0 && !envAppIdMismatch) {
|
|
4182
|
-
summaries.push(
|
|
4183
|
-
`\u26A0\uFE0F .env already assigns a different value to ${stale.map((v) => `${v.name} (should be ${v.value})`).join(", ")} \u2014 the wizard left it alone. Fix it by hand, or searches will fail.`
|
|
4184
|
-
);
|
|
4185
|
-
logger.warn(
|
|
4186
|
-
{ vars: stale.map((v) => v.name) },
|
|
4187
|
-
"implement: .env holds different values for the resolved search credentials; not overwriting them"
|
|
4188
|
-
);
|
|
4189
|
-
}
|
|
4190
4376
|
}
|
|
4191
|
-
const unresolvedSearchEnvVars =
|
|
4377
|
+
const unresolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
4192
4378
|
(v) => v.value.startsWith("<")
|
|
4193
4379
|
);
|
|
4194
4380
|
if (unresolvedSearchEnvVars.length > 0) {
|
|
4195
4381
|
summaries.push(
|
|
4196
|
-
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.`
|
|
4197
|
-
(searchKeyError ? ` Reason: ${searchKeyError}` : "")
|
|
4382
|
+
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.`
|
|
4198
4383
|
);
|
|
4199
4384
|
}
|
|
4200
4385
|
} else {
|
|
@@ -4226,7 +4411,7 @@ ${run2.output}` : status;
|
|
|
4226
4411
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4227
4412
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
4228
4413
|
} : {},
|
|
4229
|
-
...useCases.includes("search") ? { searchEnvVars:
|
|
4414
|
+
...useCases.includes("search") ? { searchEnvVars: input.searchEnvVars } : {}
|
|
4230
4415
|
};
|
|
4231
4416
|
} finally {
|
|
4232
4417
|
process.chdir(repoRoot);
|
|
@@ -4269,8 +4454,8 @@ var defaultWorkflow = {
|
|
|
4269
4454
|
defineStep({
|
|
4270
4455
|
id: "select-index",
|
|
4271
4456
|
title: "Set up index",
|
|
4272
|
-
outputSchema:
|
|
4273
|
-
selection:
|
|
4457
|
+
outputSchema: z27.object({
|
|
4458
|
+
selection: z27.string()
|
|
4274
4459
|
}),
|
|
4275
4460
|
run: (ctx) => selectIndexStep(ctx)
|
|
4276
4461
|
}),
|
|
@@ -4549,7 +4734,7 @@ function parseCliArgs(argv) {
|
|
|
4549
4734
|
|
|
4550
4735
|
// src/lib/resetState.ts
|
|
4551
4736
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4552
|
-
import { join as
|
|
4737
|
+
import { join as join11 } from "node:path";
|
|
4553
4738
|
var KEEP = ["wizard.log"];
|
|
4554
4739
|
async function resetProjectState() {
|
|
4555
4740
|
const dir = stateDir();
|
|
@@ -4561,7 +4746,7 @@ async function resetProjectState() {
|
|
|
4561
4746
|
}
|
|
4562
4747
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4563
4748
|
await Promise.all(
|
|
4564
|
-
targets.map((name) => rm2(
|
|
4749
|
+
targets.map((name) => rm2(join11(dir, name), { recursive: true, force: true }))
|
|
4565
4750
|
);
|
|
4566
4751
|
return { dir, removed: targets };
|
|
4567
4752
|
}
|
|
@@ -4616,31 +4801,38 @@ ${formatStepList(workflow)}`);
|
|
|
4616
4801
|
}
|
|
4617
4802
|
async function run(workflow) {
|
|
4618
4803
|
const store = useWizard.getState();
|
|
4619
|
-
|
|
4804
|
+
const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4805
|
+
await store.waitForStart();
|
|
4620
4806
|
let user = await getUser();
|
|
4621
4807
|
if (!user) {
|
|
4622
|
-
|
|
4623
|
-
instance.cleanup();
|
|
4808
|
+
store.beginAuth();
|
|
4624
4809
|
try {
|
|
4625
4810
|
await runAuthLogin();
|
|
4626
4811
|
} catch (err) {
|
|
4627
|
-
|
|
4812
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4813
|
+
await instance.waitUntilExit();
|
|
4628
4814
|
process.exit(1);
|
|
4629
4815
|
}
|
|
4630
|
-
|
|
4816
|
+
store.endAuth();
|
|
4631
4817
|
user = await getUser();
|
|
4632
4818
|
if (!user) {
|
|
4633
4819
|
store.setError(
|
|
4634
|
-
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
|
|
4820
|
+
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
|
|
4635
4821
|
);
|
|
4636
4822
|
await instance.waitUntilExit();
|
|
4637
4823
|
process.exit(1);
|
|
4638
4824
|
}
|
|
4639
4825
|
}
|
|
4640
4826
|
store.setUser(user);
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4827
|
+
let app;
|
|
4828
|
+
try {
|
|
4829
|
+
app = await ensureApplication();
|
|
4830
|
+
} catch (err) {
|
|
4831
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4832
|
+
await instance.waitUntilExit();
|
|
4833
|
+
process.exit(1);
|
|
4834
|
+
}
|
|
4835
|
+
runWorkflow(workflow, app.id);
|
|
4644
4836
|
}
|
|
4645
4837
|
var started = await startup();
|
|
4646
4838
|
if (typeof started === "number") {
|