@algolia/wizard 0.7.0 → 0.8.0-rc.53.46
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 +774 -498
- 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,37 +631,37 @@ function Notices() {
|
|
|
497
631
|
}
|
|
498
632
|
|
|
499
633
|
// src/ui/PromptInput.tsx
|
|
500
|
-
import { Box as
|
|
634
|
+
import { Box as Box6, Text as Text6, useInput as useInput2 } from "ink";
|
|
501
635
|
import TextInput from "ink-text-input";
|
|
502
636
|
import { useState as useState4 } from "react";
|
|
503
637
|
|
|
504
638
|
// src/ui/NextAction.tsx
|
|
505
|
-
import { Box as
|
|
506
|
-
import { Fragment, jsx as jsx3, jsxs as
|
|
639
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
640
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
507
641
|
function NextAction({
|
|
508
642
|
action,
|
|
509
643
|
keyHint,
|
|
510
644
|
hierarchy = "primary"
|
|
511
645
|
}) {
|
|
512
|
-
return /* @__PURE__ */
|
|
513
|
-
hierarchy === "primary" && /* @__PURE__ */ jsx3(
|
|
514
|
-
hierarchy === "secondary" && /* @__PURE__ */
|
|
515
|
-
/* @__PURE__ */ jsx3(
|
|
516
|
-
/* @__PURE__ */ jsx3(
|
|
646
|
+
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "row", gap: 1, children: [
|
|
647
|
+
hierarchy === "primary" && /* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `> ${action}` }),
|
|
648
|
+
hierarchy === "secondary" && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
649
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.success, bold: true, children: `>` }),
|
|
650
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, bold: true, children: action })
|
|
517
651
|
] }),
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
/* @__PURE__ */ jsx3(
|
|
520
|
-
/* @__PURE__ */ jsx3(
|
|
521
|
-
/* @__PURE__ */ jsx3(
|
|
522
|
-
/* @__PURE__ */ jsx3(
|
|
652
|
+
/* @__PURE__ */ jsxs3(Box4, { children: [
|
|
653
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: "press " }),
|
|
654
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `[` }),
|
|
655
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.primary, children: keyHint }),
|
|
656
|
+
/* @__PURE__ */ jsx3(Text4, { color: COLORS.muted, children: `]` })
|
|
523
657
|
] })
|
|
524
658
|
] });
|
|
525
659
|
}
|
|
526
660
|
|
|
527
661
|
// src/ui/SelectPrompt.tsx
|
|
528
|
-
import { Box as
|
|
662
|
+
import { Box as Box5, Text as Text5, measureElement as measureElement2, useInput, useWindowSize as useWindowSize4 } from "ink";
|
|
529
663
|
import { useLayoutEffect, useRef as useRef2, useState as useState3 } from "react";
|
|
530
|
-
import { jsx as jsx4, jsxs as
|
|
664
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
531
665
|
var CANCEL = "cancel";
|
|
532
666
|
var ARROW_WIDTH = 4;
|
|
533
667
|
var COLUMN_GAP = 2;
|
|
@@ -564,7 +698,7 @@ function SelectPrompt({
|
|
|
564
698
|
if (multi) hints.push({ key: "[space]", label: "select" });
|
|
565
699
|
hints.push({ key: "[enter]", label: "confirm" });
|
|
566
700
|
const containerRef = useRef2(null);
|
|
567
|
-
const { columns } =
|
|
701
|
+
const { columns } = useWindowSize4();
|
|
568
702
|
const [width, setWidth] = useState3(columns);
|
|
569
703
|
useLayoutEffect(() => {
|
|
570
704
|
if (containerRef.current) {
|
|
@@ -609,53 +743,53 @@ function SelectPrompt({
|
|
|
609
743
|
}
|
|
610
744
|
}
|
|
611
745
|
});
|
|
612
|
-
return /* @__PURE__ */ jsx4(
|
|
613
|
-
error && /* @__PURE__ */ jsx4(
|
|
614
|
-
messages?.map((m, i) => /* @__PURE__ */ jsx4(
|
|
746
|
+
return /* @__PURE__ */ jsx4(Box5, { ref: containerRef, flexGrow: 1, children: /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", gap: 1, width, children: [
|
|
747
|
+
error && /* @__PURE__ */ jsx4(Text5, { color: COLORS.danger, children: error }),
|
|
748
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
615
749
|
table && /* @__PURE__ */ jsx4(Table, { columns: table.columns, rows: table.rows }),
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
question && /* @__PURE__ */ jsx4(
|
|
618
|
-
helpText && /* @__PURE__ */ jsx4(
|
|
750
|
+
/* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
|
|
751
|
+
question && /* @__PURE__ */ jsx4(Text5, { color: COLORS.muted, children: question }),
|
|
752
|
+
helpText && /* @__PURE__ */ jsx4(Text5, { color: COLORS.dim, children: helpText })
|
|
619
753
|
] }),
|
|
620
|
-
/* @__PURE__ */ jsx4(
|
|
754
|
+
/* @__PURE__ */ jsx4(Box5, { flexDirection: "column", children: rows.map((option, i) => {
|
|
621
755
|
const highlighted = i === index;
|
|
622
756
|
const isCancel = i === cancelIndex;
|
|
623
757
|
const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
|
|
624
758
|
const sec = isCancel ? void 0 : secondary?.[i];
|
|
625
759
|
const labelColor = highlighted ? COLORS.highlight.fg : void 0;
|
|
626
|
-
const label = /* @__PURE__ */
|
|
760
|
+
const label = /* @__PURE__ */ jsxs4(Text5, { color: labelColor, wrap: "truncate", children: [
|
|
627
761
|
highlighted ? "\u276F " : " ",
|
|
628
762
|
bullet,
|
|
629
763
|
option
|
|
630
764
|
] });
|
|
631
765
|
const isText = sec?.kind === "text";
|
|
632
|
-
return /* @__PURE__ */
|
|
633
|
-
|
|
766
|
+
return /* @__PURE__ */ jsxs4(
|
|
767
|
+
Box5,
|
|
634
768
|
{
|
|
635
769
|
width: isText ? "100%" : barWidth,
|
|
636
770
|
paddingX: 1,
|
|
637
771
|
paddingY: 1,
|
|
638
772
|
backgroundColor: highlighted ? COLORS.highlight.bg : void 0,
|
|
639
773
|
children: [
|
|
640
|
-
/* @__PURE__ */ jsx4(
|
|
641
|
-
isText && textWidth > 0 && /* @__PURE__ */ jsx4(
|
|
642
|
-
|
|
774
|
+
/* @__PURE__ */ jsx4(Box5, { width: isText ? labelWidth : barLabelWidth, children: label }),
|
|
775
|
+
isText && textWidth > 0 && /* @__PURE__ */ jsx4(Box5, { width: textWidth, children: /* @__PURE__ */ jsx4(
|
|
776
|
+
Text5,
|
|
643
777
|
{
|
|
644
778
|
wrap: "truncate",
|
|
645
779
|
color: highlighted ? COLORS.primary : COLORS.muted,
|
|
646
780
|
children: sec.value
|
|
647
781
|
}
|
|
648
782
|
) }),
|
|
649
|
-
sec?.kind === "badge" && /* @__PURE__ */ jsx4(
|
|
783
|
+
sec?.kind === "badge" && /* @__PURE__ */ jsx4(Box5, { width: badgeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx4(Text5, { color: COLORS.badge, wrap: "truncate", children: sec.value }) })
|
|
650
784
|
]
|
|
651
785
|
},
|
|
652
786
|
`row-${i}`
|
|
653
787
|
);
|
|
654
788
|
}) }),
|
|
655
|
-
/* @__PURE__ */ jsx4(
|
|
789
|
+
/* @__PURE__ */ jsx4(Text5, { children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
|
|
656
790
|
i > 0 ? " " : "",
|
|
657
|
-
/* @__PURE__ */ jsx4(
|
|
658
|
-
/* @__PURE__ */
|
|
791
|
+
/* @__PURE__ */ jsx4(Text5, { color: COLORS.primary, children: key }),
|
|
792
|
+
/* @__PURE__ */ jsxs4(Text5, { color: COLORS.dim, children: [
|
|
659
793
|
" ",
|
|
660
794
|
label
|
|
661
795
|
] })
|
|
@@ -664,7 +798,7 @@ function SelectPrompt({
|
|
|
664
798
|
}
|
|
665
799
|
|
|
666
800
|
// src/ui/PromptInput.tsx
|
|
667
|
-
import { jsx as jsx5, jsxs as
|
|
801
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
668
802
|
var ACCEPT_REJECT_OPTIONS = ["Accept", "Reject"];
|
|
669
803
|
function EnterToContinuePrompt({
|
|
670
804
|
question,
|
|
@@ -675,10 +809,10 @@ function EnterToContinuePrompt({
|
|
|
675
809
|
if (key.return) onDecide(true);
|
|
676
810
|
else if (key.escape) onDecide(false);
|
|
677
811
|
});
|
|
678
|
-
return /* @__PURE__ */
|
|
679
|
-
messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
680
|
-
question && /* @__PURE__ */ jsx5(
|
|
681
|
-
/* @__PURE__ */
|
|
812
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
|
|
813
|
+
messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
814
|
+
question && /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, children: question }),
|
|
815
|
+
/* @__PURE__ */ jsxs5(Box6, { gap: 1, flexDirection: "column", children: [
|
|
682
816
|
/* @__PURE__ */ jsx5(NextAction, { action: "continue", keyHint: "enter" }),
|
|
683
817
|
/* @__PURE__ */ jsx5(NextAction, { action: "decline", keyHint: "esc", hierarchy: "secondary" })
|
|
684
818
|
] })
|
|
@@ -688,11 +822,11 @@ function PromptInput() {
|
|
|
688
822
|
const { phase, inputReq, submitInput } = useWizard();
|
|
689
823
|
const [draft, setDraft] = useState4("");
|
|
690
824
|
if (phase === "done" || phase === "error") {
|
|
691
|
-
return /* @__PURE__ */ jsx5(
|
|
825
|
+
return /* @__PURE__ */ jsx5(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text6, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
|
|
692
826
|
}
|
|
693
827
|
if (phase !== "awaitingInput" || !inputReq) return null;
|
|
694
828
|
if (inputReq.promptType === "multipleChoice") {
|
|
695
|
-
return /* @__PURE__ */ jsx5(
|
|
829
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
696
830
|
SelectPrompt,
|
|
697
831
|
{
|
|
698
832
|
question: inputReq.prompt,
|
|
@@ -709,7 +843,7 @@ function PromptInput() {
|
|
|
709
843
|
) });
|
|
710
844
|
}
|
|
711
845
|
if (inputReq.promptType === "multiSelect") {
|
|
712
|
-
return /* @__PURE__ */ jsx5(
|
|
846
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
713
847
|
SelectPrompt,
|
|
714
848
|
{
|
|
715
849
|
multi: true,
|
|
@@ -724,7 +858,7 @@ function PromptInput() {
|
|
|
724
858
|
) });
|
|
725
859
|
}
|
|
726
860
|
if (inputReq.promptType === "notice") {
|
|
727
|
-
return /* @__PURE__ */ jsx5(
|
|
861
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
728
862
|
SelectPrompt,
|
|
729
863
|
{
|
|
730
864
|
question: inputReq.prompt,
|
|
@@ -746,7 +880,7 @@ function PromptInput() {
|
|
|
746
880
|
}
|
|
747
881
|
if (inputReq.promptType === "acceptReject") {
|
|
748
882
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
749
|
-
return /* @__PURE__ */ jsx5(
|
|
883
|
+
return /* @__PURE__ */ jsx5(Box6, { children: /* @__PURE__ */ jsx5(
|
|
750
884
|
SelectPrompt,
|
|
751
885
|
{
|
|
752
886
|
question: inputReq.prompt,
|
|
@@ -757,11 +891,11 @@ function PromptInput() {
|
|
|
757
891
|
}
|
|
758
892
|
) });
|
|
759
893
|
}
|
|
760
|
-
return /* @__PURE__ */
|
|
761
|
-
inputReq.error && /* @__PURE__ */ jsx5(
|
|
762
|
-
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(
|
|
763
|
-
/* @__PURE__ */
|
|
764
|
-
/* @__PURE__ */
|
|
894
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
895
|
+
inputReq.error && /* @__PURE__ */ jsx5(Text6, { color: COLORS.danger, children: inputReq.error }),
|
|
896
|
+
inputReq.messages?.map((m, i) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: m }, `msg-${i}`)),
|
|
897
|
+
/* @__PURE__ */ jsxs5(Box6, { children: [
|
|
898
|
+
/* @__PURE__ */ jsxs5(Text6, { color: COLORS.primary, children: [
|
|
765
899
|
inputReq.prompt,
|
|
766
900
|
" "
|
|
767
901
|
] }),
|
|
@@ -783,7 +917,7 @@ function PromptInput() {
|
|
|
783
917
|
// src/ui/Welcome.tsx
|
|
784
918
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
785
919
|
import { fileURLToPath } from "node:url";
|
|
786
|
-
import { Box as
|
|
920
|
+
import { Box as Box7, Spacer, Text as Text7, useInput as useInput3, useWindowSize as useWindowSize5 } from "ink";
|
|
787
921
|
|
|
788
922
|
// src/ui/copy/welcome.ts
|
|
789
923
|
var sidebarItems = [
|
|
@@ -811,27 +945,27 @@ var sidebarItems = [
|
|
|
811
945
|
|
|
812
946
|
// src/ui/Welcome.tsx
|
|
813
947
|
import Image, { InkPictureProvider } from "ink-picture";
|
|
814
|
-
import { jsx as jsx6, jsxs as
|
|
948
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
815
949
|
var IMAGE_PATH = join3(dirname2(fileURLToPath(import.meta.url)), "algolia.png");
|
|
816
950
|
function SidebarItem({
|
|
817
951
|
title,
|
|
818
952
|
description
|
|
819
953
|
}) {
|
|
820
|
-
return /* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */ jsx6(
|
|
823
|
-
/* @__PURE__ */ jsx6(
|
|
954
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", children: [
|
|
955
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, children: [
|
|
956
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.success, children: "\u2192" }),
|
|
957
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.strong, bold: true, children: title })
|
|
824
958
|
] }),
|
|
825
|
-
/* @__PURE__ */
|
|
959
|
+
/* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", gap: 2, children: [
|
|
826
960
|
/* @__PURE__ */ jsx6(Spacer, {}),
|
|
827
|
-
/* @__PURE__ */ jsx6(
|
|
961
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: description })
|
|
828
962
|
] })
|
|
829
963
|
] });
|
|
830
964
|
}
|
|
831
965
|
function Welcome() {
|
|
832
966
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
833
967
|
const openLearnMore = useWizard((s) => s.openLearnMore);
|
|
834
|
-
const { rows } =
|
|
968
|
+
const { rows } = useWindowSize5();
|
|
835
969
|
useInput3((input, key) => {
|
|
836
970
|
if (key.return) confirmStart();
|
|
837
971
|
else if (input === "i") openLearnMore();
|
|
@@ -850,15 +984,15 @@ function Welcome() {
|
|
|
850
984
|
if (rows < 30) {
|
|
851
985
|
layout = scales["small"];
|
|
852
986
|
}
|
|
853
|
-
return /* @__PURE__ */
|
|
987
|
+
return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", width: "100%", children: [
|
|
854
988
|
/* @__PURE__ */ jsx6(
|
|
855
|
-
|
|
989
|
+
Box7,
|
|
856
990
|
{
|
|
857
991
|
paddingY: layout.main.padding.y,
|
|
858
992
|
paddingX: layout.main.padding.x,
|
|
859
993
|
flexDirection: "column",
|
|
860
994
|
justifyContent: "center",
|
|
861
|
-
children: /* @__PURE__ */
|
|
995
|
+
children: /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", gap: 2, children: [
|
|
862
996
|
/* @__PURE__ */ jsx6(InkPictureProvider, { children: /* @__PURE__ */ jsx6(
|
|
863
997
|
Image,
|
|
864
998
|
{
|
|
@@ -870,16 +1004,16 @@ function Welcome() {
|
|
|
870
1004
|
protocol: "halfBlock"
|
|
871
1005
|
}
|
|
872
1006
|
) }),
|
|
873
|
-
/* @__PURE__ */ jsx6(
|
|
874
|
-
/* @__PURE__ */
|
|
1007
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "\u2726 From zero \u2192 working search in ~10 minutes" }),
|
|
1008
|
+
/* @__PURE__ */ jsxs6(Box7, { gap: 1, flexDirection: "column", children: [
|
|
875
1009
|
/* @__PURE__ */ jsx6(NextAction, { action: "start wizard", keyHint: "enter" }),
|
|
876
1010
|
/* @__PURE__ */ jsx6(NextAction, { action: "learn more", keyHint: "i", hierarchy: "secondary" })
|
|
877
1011
|
] })
|
|
878
1012
|
] })
|
|
879
1013
|
}
|
|
880
1014
|
),
|
|
881
|
-
/* @__PURE__ */
|
|
882
|
-
|
|
1015
|
+
/* @__PURE__ */ jsxs6(
|
|
1016
|
+
Box7,
|
|
883
1017
|
{
|
|
884
1018
|
backgroundColor: COLORS.bg.sidebar,
|
|
885
1019
|
width: 40,
|
|
@@ -889,7 +1023,7 @@ function Welcome() {
|
|
|
889
1023
|
flexDirection: "column",
|
|
890
1024
|
justifyContent: "center",
|
|
891
1025
|
children: [
|
|
892
|
-
/* @__PURE__ */ jsx6(
|
|
1026
|
+
/* @__PURE__ */ jsx6(Text7, { color: COLORS.muted, children: "WHAT THIS WIZARD WILL DO" }),
|
|
893
1027
|
sidebarItems.map((i, idx) => /* @__PURE__ */ jsx6(SidebarItem, { title: i.title, description: i.description }, idx))
|
|
894
1028
|
]
|
|
895
1029
|
}
|
|
@@ -899,7 +1033,7 @@ function Welcome() {
|
|
|
899
1033
|
|
|
900
1034
|
// src/ui/LearnMore.tsx
|
|
901
1035
|
import { Fragment as Fragment2 } from "react";
|
|
902
|
-
import { Box as
|
|
1036
|
+
import { Box as Box8, Text as Text8, useInput as useInput4, useWindowSize as useWindowSize6 } from "ink";
|
|
903
1037
|
|
|
904
1038
|
// src/ui/copy/learn-more.ts
|
|
905
1039
|
var accessIntro = "Everything runs locally on your machine. Nothing is written or sent without an explicit yes from you.";
|
|
@@ -936,7 +1070,7 @@ var policyLinks = [
|
|
|
936
1070
|
];
|
|
937
1071
|
|
|
938
1072
|
// src/ui/LearnMore.tsx
|
|
939
|
-
import { jsx as jsx7, jsxs as
|
|
1073
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
940
1074
|
var TAG_COLORS = {
|
|
941
1075
|
READ: COLORS.success,
|
|
942
1076
|
WRITE: COLORS.badge,
|
|
@@ -952,25 +1086,25 @@ function NeverLine({
|
|
|
952
1086
|
}) {
|
|
953
1087
|
const used = segments.reduce((n, s) => n + s.text.length, 0);
|
|
954
1088
|
const rightPad = Math.max(0, width - 2 - NEVER_BOX_PAD_X - used);
|
|
955
|
-
return /* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */ jsx7(
|
|
1089
|
+
return /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1090
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" }),
|
|
957
1091
|
" ".repeat(NEVER_BOX_PAD_X),
|
|
958
|
-
segments.map((s, i) => /* @__PURE__ */ jsx7(
|
|
1092
|
+
segments.map((s, i) => /* @__PURE__ */ jsx7(Text8, { color: s.color, bold: s.bold, children: s.text }, i)),
|
|
959
1093
|
" ".repeat(rightPad),
|
|
960
|
-
/* @__PURE__ */ jsx7(
|
|
1094
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: "\u2502" })
|
|
961
1095
|
] });
|
|
962
1096
|
}
|
|
963
1097
|
function LearnMore() {
|
|
964
1098
|
const confirmStart = useWizard((s) => s.confirmStart);
|
|
965
1099
|
const backToHome = useWizard((s) => s.backToHome);
|
|
966
|
-
const { columns } =
|
|
1100
|
+
const { columns } = useWindowSize6();
|
|
967
1101
|
const dividerWidth = Math.max(0, columns - PADDING_X * 2);
|
|
968
1102
|
useInput4((_input, key) => {
|
|
969
1103
|
if (key.escape) backToHome();
|
|
970
1104
|
else if (key.return) confirmStart();
|
|
971
1105
|
});
|
|
972
|
-
return /* @__PURE__ */
|
|
973
|
-
|
|
1106
|
+
return /* @__PURE__ */ jsxs7(
|
|
1107
|
+
Box8,
|
|
974
1108
|
{
|
|
975
1109
|
flexDirection: "column",
|
|
976
1110
|
paddingX: PADDING_X,
|
|
@@ -978,20 +1112,20 @@ function LearnMore() {
|
|
|
978
1112
|
width: "100%",
|
|
979
1113
|
gap: 1,
|
|
980
1114
|
children: [
|
|
981
|
-
/* @__PURE__ */ jsx7(
|
|
982
|
-
/* @__PURE__ */ jsx7(
|
|
983
|
-
/* @__PURE__ */ jsx7(
|
|
984
|
-
/* @__PURE__ */ jsx7(
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
/* @__PURE__ */ jsx7(
|
|
987
|
-
/* @__PURE__ */ jsx7(
|
|
988
|
-
/* @__PURE__ */ jsx7(
|
|
989
|
-
/* @__PURE__ */ jsx7(
|
|
1115
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: "What algolia wizard accesses" }),
|
|
1116
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: accessIntro }),
|
|
1117
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: accessItems.map((item) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", marginTop: 1, children: [
|
|
1118
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.border, children: "\u2500".repeat(dividerWidth) }),
|
|
1119
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, marginTop: 1, children: [
|
|
1120
|
+
/* @__PURE__ */ jsx7(Box8, { width: TAG_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ jsx7(Text8, { color: TAG_COLORS[item.tag], bold: true, children: `[${item.tag}]` }) }),
|
|
1121
|
+
/* @__PURE__ */ jsx7(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
1122
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: item.title }),
|
|
1123
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: ` \u2014 ${item.description}` })
|
|
990
1124
|
] }) })
|
|
991
1125
|
] })
|
|
992
1126
|
] }, item.tag)) }),
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
/* @__PURE__ */ jsx7(
|
|
1127
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
|
|
1128
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u256D${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256E` }),
|
|
995
1129
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
996
1130
|
/* @__PURE__ */ jsx7(
|
|
997
1131
|
NeverLine,
|
|
@@ -1000,7 +1134,7 @@ function LearnMore() {
|
|
|
1000
1134
|
segments: [{ text: "I NEVER", color: COLORS.danger, bold: true }]
|
|
1001
1135
|
}
|
|
1002
1136
|
),
|
|
1003
|
-
neverItems.map((item) => /* @__PURE__ */
|
|
1137
|
+
neverItems.map((item) => /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1004
1138
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1005
1139
|
/* @__PURE__ */ jsx7(
|
|
1006
1140
|
NeverLine,
|
|
@@ -1015,23 +1149,23 @@ function LearnMore() {
|
|
|
1015
1149
|
)
|
|
1016
1150
|
] }, item)),
|
|
1017
1151
|
/* @__PURE__ */ jsx7(NeverLine, { width: dividerWidth }),
|
|
1018
|
-
/* @__PURE__ */ jsx7(
|
|
1152
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.danger, children: `\u2570${"\u2500".repeat(Math.max(0, dividerWidth - 2))}\u256F` })
|
|
1019
1153
|
] }),
|
|
1020
|
-
/* @__PURE__ */ jsx7(
|
|
1021
|
-
/* @__PURE__ */ jsx7(
|
|
1022
|
-
/* @__PURE__ */ jsx7(
|
|
1154
|
+
/* @__PURE__ */ jsx7(Box8, { marginTop: 1, flexDirection: "column", children: policyLinks.map((link) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1155
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.strong, bold: true, children: `${link.label}:` }),
|
|
1156
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.accent, children: link.url })
|
|
1023
1157
|
] }, link.label)) }),
|
|
1024
|
-
/* @__PURE__ */
|
|
1025
|
-
/* @__PURE__ */
|
|
1026
|
-
/* @__PURE__ */ jsx7(
|
|
1027
|
-
/* @__PURE__ */ jsx7(
|
|
1028
|
-
/* @__PURE__ */ jsx7(
|
|
1158
|
+
/* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "row", gap: 3, children: [
|
|
1159
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1160
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1161
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "esc" }),
|
|
1162
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "] back" })
|
|
1029
1163
|
] }),
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
/* @__PURE__ */ jsx7(
|
|
1032
|
-
/* @__PURE__ */ jsx7(
|
|
1033
|
-
/* @__PURE__ */ jsx7(
|
|
1034
|
-
/* @__PURE__ */ jsx7(
|
|
1164
|
+
/* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
|
|
1165
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "[" }),
|
|
1166
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.primary, children: "enter" }),
|
|
1167
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.muted, children: "]" }),
|
|
1168
|
+
/* @__PURE__ */ jsx7(Text8, { color: COLORS.success, bold: true, children: "start wizard" })
|
|
1035
1169
|
] })
|
|
1036
1170
|
] })
|
|
1037
1171
|
]
|
|
@@ -1040,10 +1174,10 @@ function LearnMore() {
|
|
|
1040
1174
|
}
|
|
1041
1175
|
|
|
1042
1176
|
// src/ui/Sidebar.tsx
|
|
1043
|
-
import { Box as
|
|
1177
|
+
import { Box as Box11, Text as Text11 } from "ink";
|
|
1044
1178
|
|
|
1045
1179
|
// src/ui/Steps.tsx
|
|
1046
|
-
import { Box as
|
|
1180
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
1047
1181
|
import Spinner from "ink-spinner";
|
|
1048
1182
|
|
|
1049
1183
|
// src/core/persistence.ts
|
|
@@ -1072,11 +1206,11 @@ async function clearWorkflowState(workflowId) {
|
|
|
1072
1206
|
}
|
|
1073
1207
|
|
|
1074
1208
|
// src/ui/Steps.tsx
|
|
1075
|
-
import { jsx as jsx8, jsxs as
|
|
1209
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1076
1210
|
function Steps() {
|
|
1077
1211
|
const { steps } = useWizard();
|
|
1078
1212
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1079
|
-
return /* @__PURE__ */ jsx8(
|
|
1213
|
+
return /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx8(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status[s.status], children: [
|
|
1080
1214
|
s.status === "running" ? /* @__PURE__ */ jsx8(Spinner, { type: "dots" }) : MARKER[s.status],
|
|
1081
1215
|
" ",
|
|
1082
1216
|
s.title
|
|
@@ -1086,7 +1220,7 @@ function CurrentStep() {
|
|
|
1086
1220
|
const { steps } = useWizard();
|
|
1087
1221
|
const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
|
|
1088
1222
|
if (!currentStep) return null;
|
|
1089
|
-
return /* @__PURE__ */
|
|
1223
|
+
return /* @__PURE__ */ jsxs8(Text9, { color: COLORS.status.running, children: [
|
|
1090
1224
|
/* @__PURE__ */ jsx8(Spinner, { type: "dots" }),
|
|
1091
1225
|
" ",
|
|
1092
1226
|
` ${currentStep.title}`
|
|
@@ -1094,19 +1228,19 @@ function CurrentStep() {
|
|
|
1094
1228
|
}
|
|
1095
1229
|
|
|
1096
1230
|
// src/ui/Progress.tsx
|
|
1097
|
-
import { Box as
|
|
1098
|
-
import { jsx as jsx9, jsxs as
|
|
1231
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1232
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1099
1233
|
function Progress() {
|
|
1100
1234
|
const { steps, currentStepIndex } = useWizard();
|
|
1101
1235
|
const visibleSteps = steps.filter(isStepVisible);
|
|
1102
1236
|
if (visibleSteps.length === 0) return null;
|
|
1103
1237
|
const visibleCountThroughCurrent = steps.slice(0, currentStepIndex + 1).filter(isStepVisible).length;
|
|
1104
1238
|
const activeStepNumber = Math.max(1, visibleCountThroughCurrent);
|
|
1105
|
-
return /* @__PURE__ */
|
|
1106
|
-
/* @__PURE__ */ jsx9(
|
|
1107
|
-
/* @__PURE__ */ jsx9(
|
|
1108
|
-
/* @__PURE__ */ jsx9(
|
|
1109
|
-
/* @__PURE__ */ jsx9(
|
|
1239
|
+
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", gap: 1, children: [
|
|
1240
|
+
/* @__PURE__ */ jsx9(Text10, { color: COLORS.muted, children: "STEP" }),
|
|
1241
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: activeStepNumber }),
|
|
1242
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: "/" }),
|
|
1243
|
+
/* @__PURE__ */ jsx9(Text10, { bold: true, children: visibleSteps.length })
|
|
1110
1244
|
] });
|
|
1111
1245
|
}
|
|
1112
1246
|
|
|
@@ -1117,10 +1251,10 @@ var sidebarCommands = [
|
|
|
1117
1251
|
];
|
|
1118
1252
|
|
|
1119
1253
|
// src/ui/Sidebar.tsx
|
|
1120
|
-
import { jsx as jsx10, jsxs as
|
|
1254
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1121
1255
|
function Sidebar() {
|
|
1122
|
-
return /* @__PURE__ */
|
|
1123
|
-
|
|
1256
|
+
return /* @__PURE__ */ jsxs10(
|
|
1257
|
+
Box11,
|
|
1124
1258
|
{
|
|
1125
1259
|
backgroundColor: "#14171E",
|
|
1126
1260
|
width: 30,
|
|
@@ -1129,16 +1263,16 @@ function Sidebar() {
|
|
|
1129
1263
|
flexDirection: "column",
|
|
1130
1264
|
justifyContent: "space-between",
|
|
1131
1265
|
children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
/* @__PURE__ */ jsx10(
|
|
1266
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1267
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: "PROGRESS" }),
|
|
1134
1268
|
/* @__PURE__ */ jsx10(Steps, {})
|
|
1135
1269
|
] }),
|
|
1136
|
-
/* @__PURE__ */
|
|
1270
|
+
/* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
|
|
1137
1271
|
/* @__PURE__ */ jsx10(Progress, {}),
|
|
1138
|
-
/* @__PURE__ */ jsx10(
|
|
1139
|
-
return /* @__PURE__ */
|
|
1140
|
-
/* @__PURE__ */ jsx10(
|
|
1141
|
-
/* @__PURE__ */ jsx10(
|
|
1272
|
+
/* @__PURE__ */ jsx10(Box11, { flexDirection: "column", children: sidebarCommands.map((c) => {
|
|
1273
|
+
return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", gap: 1, children: [
|
|
1274
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.primary, children: `[${c.keyHint}]` }),
|
|
1275
|
+
/* @__PURE__ */ jsx10(Text11, { color: COLORS.muted, children: c.description })
|
|
1142
1276
|
] });
|
|
1143
1277
|
}) })
|
|
1144
1278
|
] })
|
|
@@ -1148,12 +1282,12 @@ function Sidebar() {
|
|
|
1148
1282
|
}
|
|
1149
1283
|
|
|
1150
1284
|
// src/ui/Ribbon.tsx
|
|
1151
|
-
import { Box as
|
|
1152
|
-
import { jsx as jsx11, jsxs as
|
|
1285
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
1286
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1153
1287
|
function Ribbon() {
|
|
1154
1288
|
const firstCommand = sidebarCommands[0];
|
|
1155
|
-
return /* @__PURE__ */
|
|
1156
|
-
|
|
1289
|
+
return /* @__PURE__ */ jsxs11(
|
|
1290
|
+
Box12,
|
|
1157
1291
|
{
|
|
1158
1292
|
backgroundColor: "#14171E",
|
|
1159
1293
|
flexDirection: "row",
|
|
@@ -1163,9 +1297,9 @@ function Ribbon() {
|
|
|
1163
1297
|
children: [
|
|
1164
1298
|
/* @__PURE__ */ jsx11(Progress, {}),
|
|
1165
1299
|
/* @__PURE__ */ jsx11(CurrentStep, {}),
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */ jsx11(
|
|
1168
|
-
/* @__PURE__ */ jsx11(
|
|
1300
|
+
/* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", gap: 1, children: [
|
|
1301
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.primary, children: `[${firstCommand.keyHint}]` }),
|
|
1302
|
+
/* @__PURE__ */ jsx11(Text12, { color: COLORS.muted, children: firstCommand.description })
|
|
1169
1303
|
] })
|
|
1170
1304
|
]
|
|
1171
1305
|
}
|
|
@@ -1177,8 +1311,8 @@ import { useState as useState6 } from "react";
|
|
|
1177
1311
|
|
|
1178
1312
|
// src/ui/Logs.tsx
|
|
1179
1313
|
import { useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState5 } from "react";
|
|
1180
|
-
import { Box as
|
|
1181
|
-
import { jsx as jsx12, jsxs as
|
|
1314
|
+
import { Box as Box13, Text as Text13, measureElement as measureElement3, useInput as useInput5, useWindowSize as useWindowSize7 } from "ink";
|
|
1315
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1182
1316
|
var KIND_COLOR = {
|
|
1183
1317
|
tool: COLORS.primary,
|
|
1184
1318
|
prompt: COLORS.badge
|
|
@@ -1208,7 +1342,7 @@ function formatTimestamp(ms) {
|
|
|
1208
1342
|
}
|
|
1209
1343
|
function Logs() {
|
|
1210
1344
|
const logs = useWizard((s) => s.logs);
|
|
1211
|
-
const { rows, columns } =
|
|
1345
|
+
const { rows, columns } = useWindowSize7();
|
|
1212
1346
|
const viewportRef = useRef3(null);
|
|
1213
1347
|
const [viewportHeight, setViewportHeight] = useState5(0);
|
|
1214
1348
|
const [viewportWidth, setViewportWidth] = useState5(0);
|
|
@@ -1245,10 +1379,10 @@ function Logs() {
|
|
|
1245
1379
|
const visible = logs.slice(scrollOffset, scrollOffset + capacity);
|
|
1246
1380
|
const hiddenAbove = scrollOffset;
|
|
1247
1381
|
const hiddenBelow = logs.length - scrollOffset - visible.length;
|
|
1248
|
-
return /* @__PURE__ */
|
|
1249
|
-
logs.length === 0 && /* @__PURE__ */ jsx12(
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
hiddenAbove > 0 && /* @__PURE__ */
|
|
1382
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 4, paddingY: 2, flexGrow: 1, children: [
|
|
1383
|
+
logs.length === 0 && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "No logs yet." }),
|
|
1384
|
+
/* @__PURE__ */ jsxs12(Box13, { ref: viewportRef, flexDirection: "column", flexGrow: 1, children: [
|
|
1385
|
+
hiddenAbove > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1252
1386
|
"\u2191 ",
|
|
1253
1387
|
hiddenAbove,
|
|
1254
1388
|
" more"
|
|
@@ -1263,20 +1397,20 @@ function Logs() {
|
|
|
1263
1397
|
const name = truncate2(entry.name, budget);
|
|
1264
1398
|
budget -= name.length;
|
|
1265
1399
|
const preview = rawPreview ? truncate2(rawPreview, budget) : "";
|
|
1266
|
-
return /* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */ jsx12(
|
|
1268
|
-
/* @__PURE__ */ jsx12(
|
|
1269
|
-
preview && /* @__PURE__ */ jsx12(
|
|
1270
|
-
durationText && /* @__PURE__ */ jsx12(
|
|
1400
|
+
return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", gap: ROW_GAP, children: [
|
|
1401
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: timestamp }),
|
|
1402
|
+
/* @__PURE__ */ jsx12(Text13, { color: logNameColor(entry), wrap: "truncate", children: name }),
|
|
1403
|
+
preview && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, wrap: "truncate", children: preview }),
|
|
1404
|
+
durationText && /* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: durationText })
|
|
1271
1405
|
] }, entry.id);
|
|
1272
1406
|
}),
|
|
1273
|
-
hiddenBelow > 0 && /* @__PURE__ */
|
|
1407
|
+
hiddenBelow > 0 && /* @__PURE__ */ jsxs12(Text13, { color: COLORS.dim, children: [
|
|
1274
1408
|
"\u2193 ",
|
|
1275
1409
|
hiddenBelow,
|
|
1276
1410
|
" more"
|
|
1277
1411
|
] })
|
|
1278
1412
|
] }),
|
|
1279
|
-
/* @__PURE__ */ jsx12(
|
|
1413
|
+
/* @__PURE__ */ jsx12(Text13, { color: COLORS.dim, children: "\u2191/\u2193 scroll" })
|
|
1280
1414
|
] });
|
|
1281
1415
|
}
|
|
1282
1416
|
|
|
@@ -1468,11 +1602,11 @@ function track(event, payload) {
|
|
|
1468
1602
|
}
|
|
1469
1603
|
|
|
1470
1604
|
// src/ui/App.tsx
|
|
1471
|
-
import { jsx as jsx13, jsxs as
|
|
1605
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1472
1606
|
function App() {
|
|
1473
1607
|
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1474
1608
|
const { exit } = useApp();
|
|
1475
|
-
const { columns, rows } =
|
|
1609
|
+
const { columns, rows } = useWindowSize8();
|
|
1476
1610
|
const [showLogs, setShowLogs] = useState6(false);
|
|
1477
1611
|
const finished = phase === "done" || phase === "error";
|
|
1478
1612
|
const currentStep = steps[currentStepIndex];
|
|
@@ -1485,7 +1619,7 @@ function App() {
|
|
|
1485
1619
|
{ isActive: finished }
|
|
1486
1620
|
);
|
|
1487
1621
|
useInput6((_input, key) => {
|
|
1488
|
-
if (phase === "idle" || phase === "
|
|
1622
|
+
if (phase === "idle" || phase === "authenticating") return;
|
|
1489
1623
|
if (key.tab) {
|
|
1490
1624
|
setShowLogs(!showLogs);
|
|
1491
1625
|
track("AI Wizard Interaction", {
|
|
@@ -1495,7 +1629,7 @@ function App() {
|
|
|
1495
1629
|
});
|
|
1496
1630
|
}
|
|
1497
1631
|
});
|
|
1498
|
-
const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1632
|
+
const escOwnedElsewhere = phase === "idle" || phase === "authenticating" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1499
1633
|
useInput6((_input, key) => {
|
|
1500
1634
|
if (escOwnedElsewhere) return;
|
|
1501
1635
|
if (key.escape) {
|
|
@@ -1508,53 +1642,67 @@ function App() {
|
|
|
1508
1642
|
exit();
|
|
1509
1643
|
}
|
|
1510
1644
|
});
|
|
1511
|
-
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1645
|
+
const mainWindowVisible = phase === "authenticating" || phase === "preflight" || phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1512
1646
|
const flexDirection = columns > 90 ? "row" : "column";
|
|
1513
1647
|
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
|
-
|
|
1648
|
+
return (
|
|
1649
|
+
/* Exactly the viewport, clipped — never `minHeight`, which lets the frame
|
|
1650
|
+
grow past the terminal. Ink then abandons diffing to clear and repaint
|
|
1651
|
+
the whole screen, and the scrolling that frame causes throws off its
|
|
1652
|
+
cursor arithmetic: flicker and leftover rows, worst when a burst of CLI
|
|
1653
|
+
output is swapped out. Clipping drops the bottom of an over-tall frame;
|
|
1654
|
+
the per-panel row budgets are what keep it from coming to that. */
|
|
1655
|
+
/* @__PURE__ */ jsxs13(
|
|
1656
|
+
Box14,
|
|
1657
|
+
{
|
|
1658
|
+
backgroundColor: COLORS.bg.main,
|
|
1659
|
+
flexDirection: "row",
|
|
1660
|
+
width: columns,
|
|
1661
|
+
height: rows,
|
|
1662
|
+
overflow: "hidden",
|
|
1663
|
+
children: [
|
|
1664
|
+
mainWindowVisible && /* @__PURE__ */ jsxs13(
|
|
1665
|
+
Box14,
|
|
1666
|
+
{
|
|
1667
|
+
flexDirection,
|
|
1668
|
+
width: "100%",
|
|
1669
|
+
justifyContent: "space-between",
|
|
1670
|
+
children: [
|
|
1671
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
|
|
1672
|
+
/* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
|
|
1673
|
+
/* @__PURE__ */ jsxs13(
|
|
1674
|
+
Box14,
|
|
1675
|
+
{
|
|
1676
|
+
flexDirection: "column",
|
|
1677
|
+
paddingX: 4,
|
|
1678
|
+
paddingY: 2,
|
|
1679
|
+
width: showSidebar ? 70 : "100%",
|
|
1680
|
+
flexGrow: showSidebar ? 1 : 0,
|
|
1681
|
+
children: [
|
|
1682
|
+
phase === "authenticating" && /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
1683
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.strong, bold: true, children: "Signing in to Algolia" }),
|
|
1684
|
+
/* @__PURE__ */ jsx13(Text14, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ jsx13(CliOutput, {}),
|
|
1687
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1688
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1689
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1690
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: COLORS.status.error, children: [
|
|
1691
|
+
"\u2716 ",
|
|
1692
|
+
error
|
|
1693
|
+
] }) })
|
|
1694
|
+
]
|
|
1695
|
+
}
|
|
1696
|
+
)
|
|
1697
|
+
),
|
|
1698
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1699
|
+
]
|
|
1700
|
+
}
|
|
1701
|
+
),
|
|
1702
|
+
phase === "idle" && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1703
|
+
]
|
|
1704
|
+
}
|
|
1705
|
+
)
|
|
1558
1706
|
);
|
|
1559
1707
|
}
|
|
1560
1708
|
|
|
@@ -1790,61 +1938,138 @@ async function runWorkflow(workflow, appId) {
|
|
|
1790
1938
|
}
|
|
1791
1939
|
}
|
|
1792
1940
|
|
|
1793
|
-
// src/lib/
|
|
1794
|
-
import {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1941
|
+
// src/lib/algoliaApp.ts
|
|
1942
|
+
import { z as z4 } from "zod";
|
|
1943
|
+
var applicationSchema = z4.object({
|
|
1944
|
+
id: z4.string().min(1),
|
|
1945
|
+
name: z4.string().default(""),
|
|
1946
|
+
plan: z4.string().optional()
|
|
1947
|
+
});
|
|
1948
|
+
var listSchema = z4.array(
|
|
1949
|
+
z4.object({
|
|
1950
|
+
id: z4.string().min(1),
|
|
1951
|
+
name: z4.string().default(""),
|
|
1952
|
+
plan_label: z4.string().optional()
|
|
1953
|
+
}).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
|
|
1954
|
+
);
|
|
1955
|
+
async function currentApplication() {
|
|
1956
|
+
let raw;
|
|
1806
1957
|
try {
|
|
1807
|
-
|
|
1958
|
+
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
1808
1959
|
} catch {
|
|
1809
|
-
return
|
|
1960
|
+
return null;
|
|
1961
|
+
}
|
|
1962
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
1963
|
+
return parsed.success ? parsed.data : null;
|
|
1964
|
+
}
|
|
1965
|
+
async function requireApplication() {
|
|
1966
|
+
const app = await currentApplication();
|
|
1967
|
+
if (!app) {
|
|
1968
|
+
throw new Error(
|
|
1969
|
+
"No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
return app;
|
|
1973
|
+
}
|
|
1974
|
+
async function listApplications() {
|
|
1975
|
+
const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
|
|
1976
|
+
const parsed = listSchema.safeParse(parseJson(raw));
|
|
1977
|
+
if (!parsed.success) {
|
|
1978
|
+
throw new Error("Could not read the list of Algolia applications.");
|
|
1979
|
+
}
|
|
1980
|
+
return parsed.data;
|
|
1981
|
+
}
|
|
1982
|
+
async function selectApplication(id) {
|
|
1983
|
+
const raw = await runAlgoliaCli(
|
|
1984
|
+
["application", "select", "--non-interactive", "--app-id", id],
|
|
1985
|
+
{ onOutput: stderrSink }
|
|
1986
|
+
);
|
|
1987
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
1988
|
+
if (!parsed.success) {
|
|
1989
|
+
throw new Error(
|
|
1990
|
+
`Selected application ${id}, but the Algolia CLI returned an unreadable result.`
|
|
1991
|
+
);
|
|
1810
1992
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
name,
|
|
1815
|
-
appId: t.application_id,
|
|
1816
|
-
apiKey: t.api_key,
|
|
1817
|
-
isDefault: t.default === true
|
|
1818
|
-
}));
|
|
1819
|
-
profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
|
|
1820
|
-
return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
|
|
1821
|
-
}
|
|
1822
|
-
async function loadActiveProfile() {
|
|
1823
|
-
let profiles;
|
|
1993
|
+
return parsed.data;
|
|
1994
|
+
}
|
|
1995
|
+
function parseJson(text) {
|
|
1824
1996
|
try {
|
|
1825
|
-
|
|
1997
|
+
return JSON.parse(text);
|
|
1826
1998
|
} catch {
|
|
1827
|
-
|
|
1999
|
+
return void 0;
|
|
1828
2000
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
// src/lib/algoliaAppPicker.ts
|
|
2004
|
+
function secondaryFor(app) {
|
|
2005
|
+
return app.plan ? { kind: "badge", value: app.plan } : void 0;
|
|
2006
|
+
}
|
|
2007
|
+
function labelFor(app) {
|
|
2008
|
+
return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
|
|
2009
|
+
}
|
|
2010
|
+
function selectAndReport(app) {
|
|
2011
|
+
useWizard.getState().pushCliOutput(
|
|
2012
|
+
"stdout",
|
|
2013
|
+
`Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
|
|
2014
|
+
);
|
|
2015
|
+
return selectApplication(app.id);
|
|
2016
|
+
}
|
|
2017
|
+
async function promptForApplication() {
|
|
2018
|
+
const store = useWizard.getState();
|
|
2019
|
+
const apps = await listApplications();
|
|
2020
|
+
if (apps.length === 0) {
|
|
1831
2021
|
throw new Error(
|
|
1832
|
-
"
|
|
2022
|
+
"This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
|
|
1833
2023
|
);
|
|
1834
2024
|
}
|
|
1835
|
-
|
|
2025
|
+
if (apps.length === 1) {
|
|
2026
|
+
const only = apps[0];
|
|
2027
|
+
logger.info(
|
|
2028
|
+
{ app: only.id },
|
|
2029
|
+
"single application on the account; selecting it"
|
|
2030
|
+
);
|
|
2031
|
+
return selectAndReport(only);
|
|
2032
|
+
}
|
|
2033
|
+
const messages = ["Which Algolia application should the wizard work in?"];
|
|
2034
|
+
for (; ; ) {
|
|
2035
|
+
const choice = await store.requestUserInput({
|
|
2036
|
+
prompt: "Select an application",
|
|
2037
|
+
promptType: "multipleChoice",
|
|
2038
|
+
options: apps.map(labelFor),
|
|
2039
|
+
secondary: apps.map(secondaryFor),
|
|
2040
|
+
messages
|
|
2041
|
+
});
|
|
2042
|
+
const chosen = apps.find((app) => labelFor(app) === choice);
|
|
2043
|
+
if (!chosen) {
|
|
2044
|
+
throw new Error("Application picker received an unexpected selection");
|
|
2045
|
+
}
|
|
2046
|
+
try {
|
|
2047
|
+
return await selectAndReport(chosen);
|
|
2048
|
+
} catch (err) {
|
|
2049
|
+
logger.warn(
|
|
2050
|
+
{ app: chosen.id, err: err.message },
|
|
2051
|
+
"application select failed; re-prompting"
|
|
2052
|
+
);
|
|
2053
|
+
messages.push(
|
|
2054
|
+
`Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
async function ensureApplication() {
|
|
2060
|
+
return await currentApplication() ?? await promptForApplication();
|
|
1836
2061
|
}
|
|
1837
2062
|
|
|
1838
2063
|
// src/workflows/default.ts
|
|
1839
|
-
import { z as
|
|
2064
|
+
import { z as z27 } from "zod";
|
|
1840
2065
|
|
|
1841
2066
|
// src/actions/listIndices.ts
|
|
1842
|
-
import { z as
|
|
1843
|
-
var indicesListSchema =
|
|
1844
|
-
items:
|
|
1845
|
-
|
|
1846
|
-
name:
|
|
1847
|
-
entries:
|
|
2067
|
+
import { z as z5 } from "zod";
|
|
2068
|
+
var indicesListSchema = z5.object({
|
|
2069
|
+
items: z5.array(
|
|
2070
|
+
z5.object({
|
|
2071
|
+
name: z5.string(),
|
|
2072
|
+
entries: z5.number().default(0)
|
|
1848
2073
|
})
|
|
1849
2074
|
)
|
|
1850
2075
|
});
|
|
@@ -1915,12 +2140,12 @@ import "zod";
|
|
|
1915
2140
|
|
|
1916
2141
|
// src/lib/tools/listFiles.ts
|
|
1917
2142
|
import { tool } from "ai";
|
|
1918
|
-
import
|
|
2143
|
+
import z6 from "zod";
|
|
1919
2144
|
import { readdir } from "node:fs/promises";
|
|
1920
2145
|
|
|
1921
2146
|
// src/lib/tools/path.ts
|
|
1922
2147
|
import { lstat } from "node:fs/promises";
|
|
1923
|
-
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as
|
|
2148
|
+
import { resolve as resolve2, relative, isAbsolute, dirname as dirname3, join as join6, sep } from "node:path";
|
|
1924
2149
|
function resolveInRoot(ctx, path) {
|
|
1925
2150
|
const target = resolve2(ctx.cwd, path);
|
|
1926
2151
|
const rel = relative(ctx.root, target);
|
|
@@ -1936,7 +2161,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1936
2161
|
let current = ctx.root;
|
|
1937
2162
|
const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
|
|
1938
2163
|
for (const part of parts) {
|
|
1939
|
-
current =
|
|
2164
|
+
current = join6(current, part);
|
|
1940
2165
|
try {
|
|
1941
2166
|
if ((await lstat(current)).isSymbolicLink()) return true;
|
|
1942
2167
|
} catch (err) {
|
|
@@ -1951,7 +2176,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1951
2176
|
function listFilesTool(ctx) {
|
|
1952
2177
|
return tool({
|
|
1953
2178
|
description: "List files in the current working directory",
|
|
1954
|
-
inputSchema:
|
|
2179
|
+
inputSchema: z6.object(),
|
|
1955
2180
|
execute: async () => {
|
|
1956
2181
|
logger.info("called listFiles tool");
|
|
1957
2182
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
@@ -1967,13 +2192,13 @@ function listFilesTool(ctx) {
|
|
|
1967
2192
|
|
|
1968
2193
|
// src/lib/tools/changeDirectory.ts
|
|
1969
2194
|
import { tool as tool2 } from "ai";
|
|
1970
|
-
import
|
|
2195
|
+
import z7 from "zod";
|
|
1971
2196
|
import { stat } from "node:fs/promises";
|
|
1972
2197
|
function changeDirectoryTool(ctx) {
|
|
1973
2198
|
return tool2({
|
|
1974
2199
|
description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
|
|
1975
|
-
inputSchema:
|
|
1976
|
-
path:
|
|
2200
|
+
inputSchema: z7.object({
|
|
2201
|
+
path: z7.string().describe("Directory to change into")
|
|
1977
2202
|
}),
|
|
1978
2203
|
execute: async ({ path }) => {
|
|
1979
2204
|
logger.info({ path }, "called changeDirectory tool");
|
|
@@ -1995,13 +2220,13 @@ function changeDirectoryTool(ctx) {
|
|
|
1995
2220
|
|
|
1996
2221
|
// src/lib/tools/reportStatus.ts
|
|
1997
2222
|
import { tool as tool3 } from "ai";
|
|
1998
|
-
import
|
|
2223
|
+
import z8 from "zod";
|
|
1999
2224
|
function reportStatusTool(output) {
|
|
2000
2225
|
return tool3({
|
|
2001
2226
|
description: "Report the status of your execution. Return a reason in case of failure.",
|
|
2002
|
-
inputSchema:
|
|
2003
|
-
status:
|
|
2004
|
-
reason:
|
|
2227
|
+
inputSchema: z8.object({
|
|
2228
|
+
status: z8.enum(["success", "fail"]),
|
|
2229
|
+
reason: z8.string().optional(),
|
|
2005
2230
|
output
|
|
2006
2231
|
}),
|
|
2007
2232
|
execute: async ({ status, reason, output: output2 }) => {
|
|
@@ -2013,8 +2238,8 @@ function reportStatusTool(output) {
|
|
|
2013
2238
|
|
|
2014
2239
|
// src/lib/tools/readFile.ts
|
|
2015
2240
|
import { tool as tool4 } from "ai";
|
|
2016
|
-
import
|
|
2017
|
-
import { readFile as
|
|
2241
|
+
import z9 from "zod";
|
|
2242
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2018
2243
|
|
|
2019
2244
|
// src/lib/tools/env.ts
|
|
2020
2245
|
import { basename } from "node:path";
|
|
@@ -2041,8 +2266,8 @@ function redactEnvValues(content) {
|
|
|
2041
2266
|
function readFileTool(ctx) {
|
|
2042
2267
|
return tool4({
|
|
2043
2268
|
description: "Read the contents of a file at the given path",
|
|
2044
|
-
inputSchema:
|
|
2045
|
-
filePath:
|
|
2269
|
+
inputSchema: z9.object({
|
|
2270
|
+
filePath: z9.string().describe("Path to the file to read")
|
|
2046
2271
|
}),
|
|
2047
2272
|
execute: async ({ filePath }) => {
|
|
2048
2273
|
if (++ctx.counts.read > ctx.limits.read) {
|
|
@@ -2052,7 +2277,7 @@ function readFileTool(ctx) {
|
|
|
2052
2277
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2053
2278
|
if (!resolved.ok) return resolved.error;
|
|
2054
2279
|
try {
|
|
2055
|
-
const content = await
|
|
2280
|
+
const content = await readFile3(resolved.target, "utf8");
|
|
2056
2281
|
return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
|
|
2057
2282
|
} catch (err) {
|
|
2058
2283
|
return `Error reading ${filePath}: ${err.message}`;
|
|
@@ -2063,15 +2288,15 @@ function readFileTool(ctx) {
|
|
|
2063
2288
|
|
|
2064
2289
|
// src/lib/tools/writeFile.ts
|
|
2065
2290
|
import { tool as tool5 } from "ai";
|
|
2066
|
-
import
|
|
2291
|
+
import z10 from "zod";
|
|
2067
2292
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2068
2293
|
import { dirname as dirname4 } from "node:path";
|
|
2069
2294
|
function writeFileTool(ctx) {
|
|
2070
2295
|
return tool5({
|
|
2071
2296
|
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.",
|
|
2072
|
-
inputSchema:
|
|
2073
|
-
filePath:
|
|
2074
|
-
content:
|
|
2297
|
+
inputSchema: z10.object({
|
|
2298
|
+
filePath: z10.string().describe("Path to the file to write"),
|
|
2299
|
+
content: z10.string().describe("Content to write to the file")
|
|
2075
2300
|
}),
|
|
2076
2301
|
execute: async ({ filePath, content }) => {
|
|
2077
2302
|
logger.info({ filePath }, "called writeFile tool");
|
|
@@ -2096,9 +2321,95 @@ function writeFileTool(ctx) {
|
|
|
2096
2321
|
|
|
2097
2322
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2098
2323
|
import { tool as tool6 } from "ai";
|
|
2099
|
-
import
|
|
2100
|
-
import { mkdir as mkdir4, readFile as
|
|
2324
|
+
import z12 from "zod";
|
|
2325
|
+
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2101
2326
|
import { dirname as dirname5 } from "node:path";
|
|
2327
|
+
|
|
2328
|
+
// src/lib/algoliaApiKey.ts
|
|
2329
|
+
import { z as z11 } from "zod";
|
|
2330
|
+
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
2331
|
+
var WRITE_ACLS = [
|
|
2332
|
+
"addObject",
|
|
2333
|
+
"deleteObject",
|
|
2334
|
+
"settings",
|
|
2335
|
+
"editSettings",
|
|
2336
|
+
"listIndexes"
|
|
2337
|
+
];
|
|
2338
|
+
var WRITE_ACL_SET = new Set(WRITE_ACLS);
|
|
2339
|
+
var apiKeySchema = z11.object({
|
|
2340
|
+
value: z11.string().min(1),
|
|
2341
|
+
acl: z11.array(z11.string()).default([]),
|
|
2342
|
+
indexes: z11.array(z11.string()).default([])
|
|
2343
|
+
});
|
|
2344
|
+
var apiKeyListSchema = z11.object({
|
|
2345
|
+
items: z11.array(apiKeySchema).optional(),
|
|
2346
|
+
keys: z11.array(apiKeySchema).optional()
|
|
2347
|
+
}).transform((o) => o.items ?? o.keys ?? []);
|
|
2348
|
+
var createdKeySchema = z11.object({
|
|
2349
|
+
key: z11.string().min(1).optional(),
|
|
2350
|
+
value: z11.string().min(1).optional()
|
|
2351
|
+
});
|
|
2352
|
+
function canReuse(key, index) {
|
|
2353
|
+
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
2354
|
+
}
|
|
2355
|
+
async function createSearchKey(index) {
|
|
2356
|
+
const stdout = await runAlgoliaCli([
|
|
2357
|
+
"apikeys",
|
|
2358
|
+
"create",
|
|
2359
|
+
"--indices",
|
|
2360
|
+
index,
|
|
2361
|
+
"--acl",
|
|
2362
|
+
"search,browse",
|
|
2363
|
+
"--description",
|
|
2364
|
+
`wizard search-only key for ${index}`,
|
|
2365
|
+
"-o",
|
|
2366
|
+
"json"
|
|
2367
|
+
]);
|
|
2368
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
|
|
2369
|
+
const created = key ?? value;
|
|
2370
|
+
if (!created) throw new Error("apikeys create returned no key value");
|
|
2371
|
+
return created;
|
|
2372
|
+
}
|
|
2373
|
+
function canReuseForWrites(key, index) {
|
|
2374
|
+
return WRITE_ACLS.every((acl) => key.acl.includes(acl)) && key.acl.every((acl) => WRITE_ACL_SET.has(acl)) && key.indexes.includes(index);
|
|
2375
|
+
}
|
|
2376
|
+
async function resolveWriteKey(index) {
|
|
2377
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2378
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key2) => canReuseForWrites(key2, index))?.value;
|
|
2379
|
+
if (existing) {
|
|
2380
|
+
logger.info({ index }, "reusing existing write API key");
|
|
2381
|
+
return existing;
|
|
2382
|
+
}
|
|
2383
|
+
logger.info({ index }, "no reusable write key found; creating one");
|
|
2384
|
+
const created = await runAlgoliaCli([
|
|
2385
|
+
"apikeys",
|
|
2386
|
+
"create",
|
|
2387
|
+
"--indices",
|
|
2388
|
+
index,
|
|
2389
|
+
"--acl",
|
|
2390
|
+
WRITE_ACLS.join(","),
|
|
2391
|
+
"--description",
|
|
2392
|
+
`wizard write key for ${index}`,
|
|
2393
|
+
"-o",
|
|
2394
|
+
"json"
|
|
2395
|
+
]);
|
|
2396
|
+
const { key, value } = createdKeySchema.parse(JSON.parse(created));
|
|
2397
|
+
const writeKey = key ?? value;
|
|
2398
|
+
if (!writeKey) throw new Error("apikeys create returned no key value");
|
|
2399
|
+
return writeKey;
|
|
2400
|
+
}
|
|
2401
|
+
async function resolveSearchOnlyKey(index) {
|
|
2402
|
+
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
2403
|
+
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
|
|
2404
|
+
if (existing) {
|
|
2405
|
+
logger.info({ index }, "reusing existing search-only API key");
|
|
2406
|
+
return existing;
|
|
2407
|
+
}
|
|
2408
|
+
logger.info({ index }, "no reusable search-only key found; creating one");
|
|
2409
|
+
return createSearchKey(index);
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2102
2413
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2103
2414
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
2104
2415
|
function appendEnv(content, entries) {
|
|
@@ -2112,9 +2423,9 @@ function hasEnv(content, name) {
|
|
|
2112
2423
|
}
|
|
2113
2424
|
function writeCredentialsTool(ctx) {
|
|
2114
2425
|
return tool6({
|
|
2115
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials
|
|
2116
|
-
inputSchema:
|
|
2117
|
-
filePath:
|
|
2426
|
+
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.`,
|
|
2427
|
+
inputSchema: z12.object({
|
|
2428
|
+
filePath: z12.string().describe(
|
|
2118
2429
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2119
2430
|
)
|
|
2120
2431
|
}),
|
|
@@ -2122,11 +2433,17 @@ function writeCredentialsTool(ctx) {
|
|
|
2122
2433
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2123
2434
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2124
2435
|
if (resolved.ok === false) return resolved.error;
|
|
2125
|
-
|
|
2436
|
+
const targetIndex = useWizard.getState().targetIndex;
|
|
2437
|
+
if (!targetIndex) {
|
|
2438
|
+
return "Error: no target index is set for this run, so a scoped write key cannot be provisioned.";
|
|
2439
|
+
}
|
|
2440
|
+
let appId;
|
|
2441
|
+
let writeKey;
|
|
2126
2442
|
try {
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2443
|
+
appId = (await requireApplication()).id;
|
|
2444
|
+
writeKey = await resolveWriteKey(targetIndex);
|
|
2445
|
+
} catch (err) {
|
|
2446
|
+
return `Error: could not resolve Algolia credentials (${err.message}). Ask the user to authenticate with the Algolia CLI first.`;
|
|
2130
2447
|
}
|
|
2131
2448
|
try {
|
|
2132
2449
|
if (await hasSymlinkParent(ctx, resolved.target)) {
|
|
@@ -2134,7 +2451,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2134
2451
|
}
|
|
2135
2452
|
let existing = "";
|
|
2136
2453
|
try {
|
|
2137
|
-
existing = await
|
|
2454
|
+
existing = await readFile4(resolved.target, "utf8");
|
|
2138
2455
|
} catch (err) {
|
|
2139
2456
|
if (err.code !== "ENOENT") throw err;
|
|
2140
2457
|
}
|
|
@@ -2145,8 +2462,8 @@ function writeCredentialsTool(ctx) {
|
|
|
2145
2462
|
return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
|
|
2146
2463
|
}
|
|
2147
2464
|
const envWithCredentials = appendEnv(existing, [
|
|
2148
|
-
[APP_ID_VAR,
|
|
2149
|
-
[API_KEY_VAR,
|
|
2465
|
+
[APP_ID_VAR, appId],
|
|
2466
|
+
[API_KEY_VAR, writeKey]
|
|
2150
2467
|
]);
|
|
2151
2468
|
await mkdir4(dirname5(resolved.target), { recursive: true });
|
|
2152
2469
|
await writeFile4(resolved.target, envWithCredentials, "utf8");
|
|
@@ -2160,16 +2477,16 @@ function writeCredentialsTool(ctx) {
|
|
|
2160
2477
|
|
|
2161
2478
|
// src/lib/tools/searchFiles.ts
|
|
2162
2479
|
import { tool as tool7 } from "ai";
|
|
2163
|
-
import
|
|
2164
|
-
import { readdir as readdir2, readFile as
|
|
2165
|
-
import { join as
|
|
2480
|
+
import z13 from "zod";
|
|
2481
|
+
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
2482
|
+
import { join as join7 } from "node:path";
|
|
2166
2483
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2167
2484
|
async function walkFiles(dir) {
|
|
2168
2485
|
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2169
2486
|
const out = [];
|
|
2170
2487
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2171
2488
|
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2172
|
-
const full =
|
|
2489
|
+
const full = join7(dir, e.name);
|
|
2173
2490
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2174
2491
|
else if (e.isFile()) out.push(full);
|
|
2175
2492
|
}
|
|
@@ -2178,9 +2495,9 @@ async function walkFiles(dir) {
|
|
|
2178
2495
|
function searchFilesTool(ctx) {
|
|
2179
2496
|
return tool7({
|
|
2180
2497
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2181
|
-
inputSchema:
|
|
2182
|
-
query:
|
|
2183
|
-
path:
|
|
2498
|
+
inputSchema: z13.object({
|
|
2499
|
+
query: z13.string().describe("JavaScript RegExp pattern to search for"),
|
|
2500
|
+
path: z13.string().optional().describe("Directory to search in (default: cwd)")
|
|
2184
2501
|
}),
|
|
2185
2502
|
execute: async ({ query, path = "." }) => {
|
|
2186
2503
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2202,7 +2519,7 @@ function searchFilesTool(ctx) {
|
|
|
2202
2519
|
for (const file of await walkFiles(resolved.target)) {
|
|
2203
2520
|
let content;
|
|
2204
2521
|
try {
|
|
2205
|
-
content = await
|
|
2522
|
+
content = await readFile5(file, "utf8");
|
|
2206
2523
|
} catch {
|
|
2207
2524
|
continue;
|
|
2208
2525
|
}
|
|
@@ -2224,7 +2541,7 @@ function searchFilesTool(ctx) {
|
|
|
2224
2541
|
|
|
2225
2542
|
// src/lib/tools/verifyImplementation.ts
|
|
2226
2543
|
import { tool as tool8 } from "ai";
|
|
2227
|
-
import
|
|
2544
|
+
import z14 from "zod";
|
|
2228
2545
|
|
|
2229
2546
|
// src/lib/tools/utils/runCommand.ts
|
|
2230
2547
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -2246,9 +2563,9 @@ function runCommand(command, args, cwd) {
|
|
|
2246
2563
|
}
|
|
2247
2564
|
|
|
2248
2565
|
// src/lib/tools/utils/packageManager.ts
|
|
2249
|
-
import { readFile as
|
|
2566
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2250
2567
|
import { existsSync } from "node:fs";
|
|
2251
|
-
import { join as
|
|
2568
|
+
import { join as join8 } from "node:path";
|
|
2252
2569
|
var LOCKFILES = [
|
|
2253
2570
|
["pnpm-lock.yaml", "pnpm"],
|
|
2254
2571
|
["yarn.lock", "yarn"],
|
|
@@ -2257,13 +2574,13 @@ var LOCKFILES = [
|
|
|
2257
2574
|
["package-lock.json", "npm"]
|
|
2258
2575
|
];
|
|
2259
2576
|
async function readPackageJson(cwd = process.cwd()) {
|
|
2260
|
-
return JSON.parse(await
|
|
2577
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2261
2578
|
}
|
|
2262
2579
|
function packageManagerFrom(pkg) {
|
|
2263
2580
|
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2264
2581
|
}
|
|
2265
2582
|
function packageManagerFromLockfile(cwd) {
|
|
2266
|
-
return LOCKFILES.find(([file]) => existsSync(
|
|
2583
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2267
2584
|
}
|
|
2268
2585
|
async function detectPackageManager(cwd) {
|
|
2269
2586
|
try {
|
|
@@ -2304,7 +2621,7 @@ async function runRepoVerificationCheck() {
|
|
|
2304
2621
|
function verifyImplementationTool() {
|
|
2305
2622
|
return tool8({
|
|
2306
2623
|
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.",
|
|
2307
|
-
inputSchema:
|
|
2624
|
+
inputSchema: z14.object(),
|
|
2308
2625
|
execute: async () => {
|
|
2309
2626
|
logger.info("called verifyImplementation tool");
|
|
2310
2627
|
return runRepoVerificationCheck();
|
|
@@ -2318,7 +2635,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2318
2635
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2319
2636
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2320
2637
|
import { dirname as dirname6 } from "node:path";
|
|
2321
|
-
import
|
|
2638
|
+
import z15 from "zod";
|
|
2322
2639
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2323
2640
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2324
2641
|
var MAX_RECORDS = 100;
|
|
@@ -2330,17 +2647,17 @@ var anthropic = createAnthropic({
|
|
|
2330
2647
|
function generateRecordTool(ctx) {
|
|
2331
2648
|
return tool9({
|
|
2332
2649
|
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.",
|
|
2333
|
-
inputSchema:
|
|
2334
|
-
entityName:
|
|
2335
|
-
attributes:
|
|
2336
|
-
count:
|
|
2337
|
-
hint:
|
|
2650
|
+
inputSchema: z15.object({
|
|
2651
|
+
entityName: z15.string().describe("Name of the entity to generate records for."),
|
|
2652
|
+
attributes: z15.array(z15.string()).describe("Attribute names each record must contain."),
|
|
2653
|
+
count: z15.number().int().min(1).max(MAX_RECORDS).default(10).describe(`How many records to generate (max ${MAX_RECORDS}).`),
|
|
2654
|
+
hint: z15.string().optional().describe("Optional context to steer realistic values.")
|
|
2338
2655
|
}),
|
|
2339
2656
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2340
2657
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2341
2658
|
try {
|
|
2342
|
-
const value =
|
|
2343
|
-
const recordSchema =
|
|
2659
|
+
const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
|
|
2660
|
+
const recordSchema = z15.object(
|
|
2344
2661
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2345
2662
|
);
|
|
2346
2663
|
const generateBatch = async (batchCount) => {
|
|
@@ -2350,8 +2667,8 @@ function generateRecordTool(ctx) {
|
|
|
2350
2667
|
const { output } = await generateText({
|
|
2351
2668
|
model: anthropic(RECORD_MODEL),
|
|
2352
2669
|
output: Output.object({
|
|
2353
|
-
schema:
|
|
2354
|
-
records:
|
|
2670
|
+
schema: z15.object({
|
|
2671
|
+
records: z15.array(recordSchema).length(batchCount)
|
|
2355
2672
|
})
|
|
2356
2673
|
}),
|
|
2357
2674
|
prompt: [
|
|
@@ -2409,12 +2726,12 @@ function generateRecordTool(ctx) {
|
|
|
2409
2726
|
|
|
2410
2727
|
// src/lib/tools/notifyUser.ts
|
|
2411
2728
|
import { tool as tool10 } from "ai";
|
|
2412
|
-
import
|
|
2729
|
+
import z16 from "zod";
|
|
2413
2730
|
function notifyUserTool() {
|
|
2414
2731
|
return tool10({
|
|
2415
2732
|
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.`,
|
|
2416
|
-
inputSchema:
|
|
2417
|
-
message:
|
|
2733
|
+
inputSchema: z16.object({
|
|
2734
|
+
message: z16.string().describe(
|
|
2418
2735
|
"Short, plain-language description of what you are doing now."
|
|
2419
2736
|
)
|
|
2420
2737
|
}),
|
|
@@ -2594,10 +2911,10 @@ async function runAgent(req) {
|
|
|
2594
2911
|
}
|
|
2595
2912
|
|
|
2596
2913
|
// src/actions/detectLanguage.ts
|
|
2597
|
-
import
|
|
2598
|
-
var detectLanguageSchema =
|
|
2599
|
-
languages:
|
|
2600
|
-
frameworks:
|
|
2914
|
+
import z19 from "zod";
|
|
2915
|
+
var detectLanguageSchema = z19.object({
|
|
2916
|
+
languages: z19.array(z19.object({ name: z19.string(), version: z19.string() })),
|
|
2917
|
+
frameworks: z19.array(z19.object({ name: z19.string(), version: z19.string() }))
|
|
2601
2918
|
});
|
|
2602
2919
|
var detectLanguage = () => runAgent({
|
|
2603
2920
|
instructions: [
|
|
@@ -2615,31 +2932,31 @@ var detectLanguage = () => runAgent({
|
|
|
2615
2932
|
});
|
|
2616
2933
|
|
|
2617
2934
|
// src/actions/analyzeCodebase.ts
|
|
2618
|
-
import
|
|
2935
|
+
import z20 from "zod";
|
|
2619
2936
|
var READONLY_TOOLS = [
|
|
2620
2937
|
"listFiles",
|
|
2621
2938
|
"changeDirectory",
|
|
2622
2939
|
"readFile",
|
|
2623
2940
|
"searchFiles"
|
|
2624
2941
|
];
|
|
2625
|
-
var ingestionAnalysisSchema =
|
|
2626
|
-
ingestionAnalysis:
|
|
2627
|
-
|
|
2628
|
-
name:
|
|
2629
|
-
paths:
|
|
2942
|
+
var ingestionAnalysisSchema = z20.object({
|
|
2943
|
+
ingestionAnalysis: z20.array(
|
|
2944
|
+
z20.object({
|
|
2945
|
+
name: z20.string(),
|
|
2946
|
+
paths: z20.array(z20.string()),
|
|
2630
2947
|
// indexable fields the agent found for this entity
|
|
2631
|
-
attributes:
|
|
2948
|
+
attributes: z20.array(z20.string())
|
|
2632
2949
|
})
|
|
2633
2950
|
)
|
|
2634
2951
|
});
|
|
2635
|
-
var searchImplementationAnalysisSchema =
|
|
2636
|
-
searchImplementationAnalysis:
|
|
2952
|
+
var searchImplementationAnalysisSchema = z20.object({
|
|
2953
|
+
searchImplementationAnalysis: z20.string()
|
|
2637
2954
|
});
|
|
2638
|
-
var verificationSchema =
|
|
2639
|
-
verification:
|
|
2955
|
+
var verificationSchema = z20.object({
|
|
2956
|
+
verification: z20.array(z20.string())
|
|
2640
2957
|
});
|
|
2641
2958
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
2642
|
-
var analyzeCodebaseSchema =
|
|
2959
|
+
var analyzeCodebaseSchema = z20.object({
|
|
2643
2960
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2644
2961
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
2645
2962
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -2701,7 +3018,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2701
3018
|
// package.json
|
|
2702
3019
|
var package_default = {
|
|
2703
3020
|
name: "@algolia/wizard",
|
|
2704
|
-
version: "0.
|
|
3021
|
+
version: "0.8.0-rc.53.46",
|
|
2705
3022
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2706
3023
|
type: "module",
|
|
2707
3024
|
engines: {
|
|
@@ -2749,7 +3066,6 @@ var package_default = {
|
|
|
2749
3066
|
dependencies: {
|
|
2750
3067
|
"@ai-sdk/anthropic": "^3.0.81",
|
|
2751
3068
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
2752
|
-
"@algolia/cli": "^5.11.0",
|
|
2753
3069
|
"@hono/node-server": "^2.0.10",
|
|
2754
3070
|
"@mishieck/ink-titled-box": "^0.4.2",
|
|
2755
3071
|
"@segment/analytics-node": "^3.1.0",
|
|
@@ -2764,7 +3080,6 @@ var package_default = {
|
|
|
2764
3080
|
nanoid: "^5.1.15",
|
|
2765
3081
|
pino: "^10.3.1",
|
|
2766
3082
|
react: "^19.2.7",
|
|
2767
|
-
toml: "^4.1.1",
|
|
2768
3083
|
varlock: "^1.5.1",
|
|
2769
3084
|
zod: "^4.4.3",
|
|
2770
3085
|
zustand: "^5.0.14"
|
|
@@ -2822,8 +3137,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
2822
3137
|
}
|
|
2823
3138
|
|
|
2824
3139
|
// src/actions/confirmLanguage.ts
|
|
2825
|
-
import
|
|
2826
|
-
var confirmLanguageSchema =
|
|
3140
|
+
import z22 from "zod";
|
|
3141
|
+
var confirmLanguageSchema = z22.object({
|
|
2827
3142
|
languages: detectLanguageSchema.shape.languages
|
|
2828
3143
|
});
|
|
2829
3144
|
async function confirmLanguage(ctx) {
|
|
@@ -2844,8 +3159,8 @@ async function confirmLanguage(ctx) {
|
|
|
2844
3159
|
}
|
|
2845
3160
|
|
|
2846
3161
|
// src/actions/confirmFramework.ts
|
|
2847
|
-
import
|
|
2848
|
-
var confirmFrameworkSchema =
|
|
3162
|
+
import z23 from "zod";
|
|
3163
|
+
var confirmFrameworkSchema = z23.object({
|
|
2849
3164
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
2850
3165
|
});
|
|
2851
3166
|
var CURATED_FRAMEWORKS = [
|
|
@@ -2973,8 +3288,8 @@ async function promptUser(ctx, params) {
|
|
|
2973
3288
|
}
|
|
2974
3289
|
|
|
2975
3290
|
// src/actions/confirmEntities.ts
|
|
2976
|
-
import
|
|
2977
|
-
var confirmEntitiesSchema =
|
|
3291
|
+
import z24 from "zod";
|
|
3292
|
+
var confirmEntitiesSchema = z24.object({
|
|
2978
3293
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
2979
3294
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2980
3295
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3044,15 +3359,15 @@ async function confirmEntities(ctx) {
|
|
|
3044
3359
|
}
|
|
3045
3360
|
|
|
3046
3361
|
// src/actions/review.ts
|
|
3047
|
-
import { z as
|
|
3048
|
-
var reviewSchema =
|
|
3362
|
+
import { z as z25 } from "zod";
|
|
3363
|
+
var reviewSchema = z25.object({
|
|
3049
3364
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3050
3365
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3051
3366
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3052
3367
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3053
|
-
summaryPoints:
|
|
3054
|
-
reviewPrompt:
|
|
3055
|
-
nextSteps:
|
|
3368
|
+
summaryPoints: z25.array(z25.string()),
|
|
3369
|
+
reviewPrompt: z25.string(),
|
|
3370
|
+
nextSteps: z25.array(z25.string())
|
|
3056
3371
|
});
|
|
3057
3372
|
function formatCompletedSteps(steps) {
|
|
3058
3373
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3103,16 +3418,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3103
3418
|
};
|
|
3104
3419
|
|
|
3105
3420
|
// src/actions/implement.ts
|
|
3106
|
-
import
|
|
3421
|
+
import z26 from "zod";
|
|
3107
3422
|
|
|
3108
3423
|
// src/lib/worktree.ts
|
|
3109
3424
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3110
|
-
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as
|
|
3425
|
+
import { copyFile, mkdir as mkdir6, readdir as readdir3, readFile as readFile7, stat as stat2, writeFile as writeFile6 } from "node:fs/promises";
|
|
3111
3426
|
import {
|
|
3112
3427
|
basename as basename2,
|
|
3113
3428
|
dirname as dirname7,
|
|
3114
3429
|
isAbsolute as isAbsolute2,
|
|
3115
|
-
join as
|
|
3430
|
+
join as join9,
|
|
3116
3431
|
relative as relative2,
|
|
3117
3432
|
resolve as resolve3
|
|
3118
3433
|
} from "node:path";
|
|
@@ -3146,7 +3461,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3146
3461
|
return out.trim().length > 0;
|
|
3147
3462
|
}
|
|
3148
3463
|
async function pruneOldWorktrees(repoRoot) {
|
|
3149
|
-
const dir =
|
|
3464
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3150
3465
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3151
3466
|
for (const slug of stale) {
|
|
3152
3467
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3157,7 +3472,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3157
3472
|
"worktree",
|
|
3158
3473
|
"remove",
|
|
3159
3474
|
"--force",
|
|
3160
|
-
|
|
3475
|
+
join9(dir, slug)
|
|
3161
3476
|
]);
|
|
3162
3477
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3163
3478
|
} catch (err) {
|
|
@@ -3171,7 +3486,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3171
3486
|
async function createWorktree(repoRoot) {
|
|
3172
3487
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3173
3488
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3174
|
-
const path =
|
|
3489
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3175
3490
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3176
3491
|
await pruneOldWorktrees(repoRoot);
|
|
3177
3492
|
await mkdir6(dirname7(path), { recursive: true });
|
|
@@ -3291,8 +3606,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3291
3606
|
} catch {
|
|
3292
3607
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3293
3608
|
}
|
|
3294
|
-
const relPath =
|
|
3295
|
-
const dest =
|
|
3609
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3610
|
+
const dest = join9(worktreePath, relPath);
|
|
3296
3611
|
try {
|
|
3297
3612
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3298
3613
|
await copyFile(source, dest);
|
|
@@ -3308,10 +3623,10 @@ function hasEnvVar(content, name) {
|
|
|
3308
3623
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
3309
3624
|
}
|
|
3310
3625
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3311
|
-
const target =
|
|
3626
|
+
const target = join9(worktreePath, ".env");
|
|
3312
3627
|
let existing = "";
|
|
3313
3628
|
try {
|
|
3314
|
-
existing = await
|
|
3629
|
+
existing = await readFile7(target, "utf8");
|
|
3315
3630
|
} catch (err) {
|
|
3316
3631
|
if (err.code !== "ENOENT") throw err;
|
|
3317
3632
|
}
|
|
@@ -3379,63 +3694,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3379
3694
|
}
|
|
3380
3695
|
}
|
|
3381
3696
|
|
|
3382
|
-
// src/lib/algoliaApiKey.ts
|
|
3383
|
-
import { z as z23 } from "zod";
|
|
3384
|
-
var SAFE_ACLS = /* @__PURE__ */ new Set(["search", "browse", "listIndexes"]);
|
|
3385
|
-
var apiKeySchema = z23.object({
|
|
3386
|
-
value: z23.string().min(1),
|
|
3387
|
-
acl: z23.array(z23.string()).default([]),
|
|
3388
|
-
indexes: z23.array(z23.string()).default([])
|
|
3389
|
-
});
|
|
3390
|
-
var apiKeyListSchema = z23.object({
|
|
3391
|
-
items: z23.array(apiKeySchema).optional(),
|
|
3392
|
-
keys: z23.array(apiKeySchema).optional()
|
|
3393
|
-
}).transform((o) => o.items ?? o.keys ?? []);
|
|
3394
|
-
var createdKeySchema = z23.object({
|
|
3395
|
-
key: z23.string().min(1).optional(),
|
|
3396
|
-
value: z23.string().min(1).optional()
|
|
3397
|
-
});
|
|
3398
|
-
function canReuse(key, index) {
|
|
3399
|
-
return key.acl.includes("search") && key.acl.every((acl) => SAFE_ACLS.has(acl)) && (key.indexes.length === 0 || key.indexes.includes("*") || key.indexes.includes(index));
|
|
3400
|
-
}
|
|
3401
|
-
async function createSearchKey(index) {
|
|
3402
|
-
const stdout = await runAlgoliaCli([
|
|
3403
|
-
"apikeys",
|
|
3404
|
-
"create",
|
|
3405
|
-
"--indices",
|
|
3406
|
-
index,
|
|
3407
|
-
"--acl",
|
|
3408
|
-
"search,browse",
|
|
3409
|
-
"--description",
|
|
3410
|
-
`wizard search-only key for ${index}`,
|
|
3411
|
-
"-o",
|
|
3412
|
-
"json"
|
|
3413
|
-
]);
|
|
3414
|
-
const { key, value } = createdKeySchema.parse(JSON.parse(stdout));
|
|
3415
|
-
const created = key ?? value;
|
|
3416
|
-
if (!created) throw new Error("apikeys create returned no key value");
|
|
3417
|
-
return created;
|
|
3418
|
-
}
|
|
3419
|
-
async function resolveSearchOnlyKey(index) {
|
|
3420
|
-
const stdout = await runAlgoliaCli(["apikeys", "list", "-o", "json"]);
|
|
3421
|
-
const existing = apiKeyListSchema.parse(JSON.parse(stdout)).find((key) => canReuse(key, index))?.value;
|
|
3422
|
-
if (existing) {
|
|
3423
|
-
logger.info({ index }, "reusing existing search-only API key");
|
|
3424
|
-
return existing;
|
|
3425
|
-
}
|
|
3426
|
-
logger.info({ index }, "no reusable search-only key found; creating one");
|
|
3427
|
-
return createSearchKey(index);
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
3697
|
// src/lib/algoliaDocs.ts
|
|
3431
3698
|
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3432
|
-
import { dirname as dirname8, join as
|
|
3699
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3433
3700
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3434
|
-
var DOCS_SUBPATH =
|
|
3701
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3435
3702
|
function findDocsDir() {
|
|
3436
3703
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3437
3704
|
for (; ; ) {
|
|
3438
|
-
const candidate =
|
|
3705
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3439
3706
|
if (existsSync2(candidate)) return candidate;
|
|
3440
3707
|
const parent = dirname8(dir);
|
|
3441
3708
|
if (parent === dir) return void 0;
|
|
@@ -3458,7 +3725,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3458
3725
|
);
|
|
3459
3726
|
return "";
|
|
3460
3727
|
}
|
|
3461
|
-
return readFileSync(
|
|
3728
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3462
3729
|
}
|
|
3463
3730
|
function getNamedDoc(name, language) {
|
|
3464
3731
|
const docsDir = findDocsDir();
|
|
@@ -3466,7 +3733,7 @@ function getNamedDoc(name, language) {
|
|
|
3466
3733
|
logger.warn("docs/algolia-sdk not found");
|
|
3467
3734
|
return "";
|
|
3468
3735
|
}
|
|
3469
|
-
const file =
|
|
3736
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3470
3737
|
if (!existsSync2(file)) {
|
|
3471
3738
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3472
3739
|
return "";
|
|
@@ -3493,50 +3760,50 @@ function shellQuote(value) {
|
|
|
3493
3760
|
}
|
|
3494
3761
|
|
|
3495
3762
|
// src/actions/implement.ts
|
|
3496
|
-
var implementSchema =
|
|
3497
|
-
filesChanged:
|
|
3498
|
-
summary:
|
|
3763
|
+
var implementSchema = z26.object({
|
|
3764
|
+
filesChanged: z26.array(z26.string()),
|
|
3765
|
+
summary: z26.string(),
|
|
3499
3766
|
// Absolute path to the throwaway worktree holding the generated changes, so
|
|
3500
3767
|
// the user can open it (`cd <worktreePath>`) or inspect the diff
|
|
3501
3768
|
// (`git -C <worktreePath> status/diff`).
|
|
3502
|
-
worktreePath:
|
|
3503
|
-
ingestCommand:
|
|
3769
|
+
worktreePath: z26.string().optional(),
|
|
3770
|
+
ingestCommand: z26.string().optional(),
|
|
3504
3771
|
// True when the user accepted the run-now prompt and the wizard executed the
|
|
3505
3772
|
// ingestion script; downstream steps use this to avoid telling the user to run
|
|
3506
3773
|
// a script that already ran.
|
|
3507
|
-
ingestScriptRan:
|
|
3774
|
+
ingestScriptRan: z26.boolean().optional(),
|
|
3508
3775
|
// Records ingested by the run-now execution, parsed from the script's
|
|
3509
3776
|
// machine-readable count line; absent when the script didn't run or emitted
|
|
3510
3777
|
// no parseable count.
|
|
3511
|
-
ingestRecordCount:
|
|
3778
|
+
ingestRecordCount: z26.number().optional(),
|
|
3512
3779
|
// Wall-clock duration of the run-now ingestion execution, in ms.
|
|
3513
|
-
ingestDurationMs:
|
|
3514
|
-
ingestionSource:
|
|
3780
|
+
ingestDurationMs: z26.number().optional(),
|
|
3781
|
+
ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
|
|
3515
3782
|
// Suggested names/values, built from framework detection. The search agent is
|
|
3516
3783
|
// instructed to rename the prefix if it doesn't match the project's build
|
|
3517
3784
|
// tool, so the names it actually wrote can differ — treat these as hints, not
|
|
3518
3785
|
// ground truth (the agent's summary carries the final names).
|
|
3519
|
-
searchEnvVars:
|
|
3520
|
-
|
|
3521
|
-
name:
|
|
3522
|
-
value:
|
|
3786
|
+
searchEnvVars: z26.array(
|
|
3787
|
+
z26.object({
|
|
3788
|
+
name: z26.string(),
|
|
3789
|
+
value: z26.string()
|
|
3523
3790
|
})
|
|
3524
3791
|
).optional()
|
|
3525
3792
|
});
|
|
3526
|
-
var implementationOutputSchema =
|
|
3527
|
-
summary:
|
|
3793
|
+
var implementationOutputSchema = z26.object({
|
|
3794
|
+
summary: z26.string(),
|
|
3528
3795
|
// Ingestion only: how to run the generated script, as a structured pair the
|
|
3529
3796
|
// wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
|
|
3530
3797
|
// command string. `runtime` is constrained to an allowlisted interpreter and
|
|
3531
3798
|
// `entrypoint` is validated to a worktree-relative path before execution, so
|
|
3532
3799
|
// the agent cannot inject extra commands or swap the interpreter.
|
|
3533
|
-
runtime:
|
|
3534
|
-
entrypoint:
|
|
3800
|
+
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3801
|
+
entrypoint: z26.string().optional()
|
|
3535
3802
|
});
|
|
3536
|
-
var verificationOutputSchema =
|
|
3537
|
-
summary:
|
|
3538
|
-
sufficient:
|
|
3539
|
-
additionalInstructions:
|
|
3803
|
+
var verificationOutputSchema = z26.object({
|
|
3804
|
+
summary: z26.string(),
|
|
3805
|
+
sufficient: z26.boolean(),
|
|
3806
|
+
additionalInstructions: z26.string().optional()
|
|
3540
3807
|
});
|
|
3541
3808
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3542
3809
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -3650,7 +3917,7 @@ function searchInstructions(input) {
|
|
|
3650
3917
|
doc,
|
|
3651
3918
|
`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.`,
|
|
3652
3919
|
"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.",
|
|
3653
|
-
// appId always resolves (
|
|
3920
|
+
// appId always resolves (requireApplication throws otherwise); only the
|
|
3654
3921
|
// search-only key is best-effort and can fall back to a placeholder.
|
|
3655
3922
|
`Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
|
|
3656
3923
|
// Names are fixed, not the agent's to rename: the wizard writes the
|
|
@@ -3799,6 +4066,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3799
4066
|
}
|
|
3800
4067
|
}
|
|
3801
4068
|
const targetIndex = selected?.selection;
|
|
4069
|
+
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
3802
4070
|
await assertGitRepoWithHead(repoRoot);
|
|
3803
4071
|
if (await isWorkingTreeDirty(repoRoot)) {
|
|
3804
4072
|
await confirmDirtyWorkingTree(ctx, repoRoot);
|
|
@@ -3809,7 +4077,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3809
4077
|
let appId;
|
|
3810
4078
|
let searchKey;
|
|
3811
4079
|
if (useCases.includes("search")) {
|
|
3812
|
-
appId = (await
|
|
4080
|
+
appId = (await requireApplication()).id;
|
|
3813
4081
|
try {
|
|
3814
4082
|
searchKey = await resolveSearchOnlyKey(targetIndex);
|
|
3815
4083
|
} catch (err) {
|
|
@@ -3920,7 +4188,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3920
4188
|
messages: []
|
|
3921
4189
|
}) === true;
|
|
3922
4190
|
if (runNow) {
|
|
3923
|
-
const
|
|
4191
|
+
const ingestApp = await requireApplication();
|
|
4192
|
+
const writeKey = await resolveWriteKey(targetIndex);
|
|
3924
4193
|
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
3925
4194
|
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
3926
4195
|
runtime: ingestRuntime,
|
|
@@ -3932,8 +4201,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3932
4201
|
ingestRuntime,
|
|
3933
4202
|
ingestEntrypoint,
|
|
3934
4203
|
{
|
|
3935
|
-
[APP_ID_VAR]:
|
|
3936
|
-
[API_KEY_VAR]:
|
|
4204
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4205
|
+
[API_KEY_VAR]: writeKey
|
|
3937
4206
|
}
|
|
3938
4207
|
);
|
|
3939
4208
|
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
@@ -4144,8 +4413,8 @@ var defaultWorkflow = {
|
|
|
4144
4413
|
defineStep({
|
|
4145
4414
|
id: "select-index",
|
|
4146
4415
|
title: "Set up index",
|
|
4147
|
-
outputSchema:
|
|
4148
|
-
selection:
|
|
4416
|
+
outputSchema: z27.object({
|
|
4417
|
+
selection: z27.string()
|
|
4149
4418
|
}),
|
|
4150
4419
|
run: (ctx) => selectIndexStep(ctx)
|
|
4151
4420
|
}),
|
|
@@ -4424,7 +4693,7 @@ function parseCliArgs(argv) {
|
|
|
4424
4693
|
|
|
4425
4694
|
// src/lib/resetState.ts
|
|
4426
4695
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4427
|
-
import { join as
|
|
4696
|
+
import { join as join11 } from "node:path";
|
|
4428
4697
|
var KEEP = ["wizard.log"];
|
|
4429
4698
|
async function resetProjectState() {
|
|
4430
4699
|
const dir = stateDir();
|
|
@@ -4436,7 +4705,7 @@ async function resetProjectState() {
|
|
|
4436
4705
|
}
|
|
4437
4706
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4438
4707
|
await Promise.all(
|
|
4439
|
-
targets.map((name) => rm2(
|
|
4708
|
+
targets.map((name) => rm2(join11(dir, name), { recursive: true, force: true }))
|
|
4440
4709
|
);
|
|
4441
4710
|
return { dir, removed: targets };
|
|
4442
4711
|
}
|
|
@@ -4491,31 +4760,38 @@ ${formatStepList(workflow)}`);
|
|
|
4491
4760
|
}
|
|
4492
4761
|
async function run(workflow) {
|
|
4493
4762
|
const store = useWizard.getState();
|
|
4494
|
-
|
|
4763
|
+
const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4764
|
+
await store.waitForStart();
|
|
4495
4765
|
let user = await getUser();
|
|
4496
4766
|
if (!user) {
|
|
4497
|
-
|
|
4498
|
-
instance.cleanup();
|
|
4767
|
+
store.beginAuth();
|
|
4499
4768
|
try {
|
|
4500
4769
|
await runAuthLogin();
|
|
4501
4770
|
} catch (err) {
|
|
4502
|
-
|
|
4771
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4772
|
+
await instance.waitUntilExit();
|
|
4503
4773
|
process.exit(1);
|
|
4504
4774
|
}
|
|
4505
|
-
|
|
4775
|
+
store.endAuth();
|
|
4506
4776
|
user = await getUser();
|
|
4507
4777
|
if (!user) {
|
|
4508
4778
|
store.setError(
|
|
4509
|
-
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
|
|
4779
|
+
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
|
|
4510
4780
|
);
|
|
4511
4781
|
await instance.waitUntilExit();
|
|
4512
4782
|
process.exit(1);
|
|
4513
4783
|
}
|
|
4514
4784
|
}
|
|
4515
4785
|
store.setUser(user);
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4786
|
+
let app;
|
|
4787
|
+
try {
|
|
4788
|
+
app = await ensureApplication();
|
|
4789
|
+
} catch (err) {
|
|
4790
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4791
|
+
await instance.waitUntilExit();
|
|
4792
|
+
process.exit(1);
|
|
4793
|
+
}
|
|
4794
|
+
runWorkflow(workflow, app.id);
|
|
4519
4795
|
}
|
|
4520
4796
|
var started = await startup();
|
|
4521
4797
|
if (typeof started === "number") {
|