@algolia/wizard 0.8.0-rc.49.42 → 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 +800 -649
- 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
|
|
|
@@ -1568,8 +1716,7 @@ var configFile = () => join5(stateDir(), "config.json");
|
|
|
1568
1716
|
var DEFAULT_CONFIG = {
|
|
1569
1717
|
version: 1,
|
|
1570
1718
|
aiConsent: false,
|
|
1571
|
-
workflowsRun: []
|
|
1572
|
-
searchApiKeys: {}
|
|
1719
|
+
workflowsRun: []
|
|
1573
1720
|
};
|
|
1574
1721
|
async function loadConfig() {
|
|
1575
1722
|
try {
|
|
@@ -1588,38 +1735,6 @@ async function recordWorkflowRun(workflowId, completedAt) {
|
|
|
1588
1735
|
config.workflowsRun.push({ workflowId, completedAt });
|
|
1589
1736
|
await saveConfig(config);
|
|
1590
1737
|
}
|
|
1591
|
-
function isStoredSearchKey(value) {
|
|
1592
|
-
if (typeof value !== "object" || value === null) return false;
|
|
1593
|
-
const { appId, key } = value;
|
|
1594
|
-
return typeof appId === "string" && !!appId && typeof key === "string" && !!key;
|
|
1595
|
-
}
|
|
1596
|
-
function storedSearchKeys(config) {
|
|
1597
|
-
const stored = config.searchApiKeys;
|
|
1598
|
-
if (typeof stored !== "object" || stored === null || Array.isArray(stored)) {
|
|
1599
|
-
return {};
|
|
1600
|
-
}
|
|
1601
|
-
return stored;
|
|
1602
|
-
}
|
|
1603
|
-
async function getStoredSearchKey(index, appId) {
|
|
1604
|
-
const entry = storedSearchKeys(await loadConfig())[index];
|
|
1605
|
-
if (!isStoredSearchKey(entry) || entry.appId !== appId) return void 0;
|
|
1606
|
-
return entry.key;
|
|
1607
|
-
}
|
|
1608
|
-
async function storeSearchKey(index, appId, key) {
|
|
1609
|
-
const config = await loadConfig();
|
|
1610
|
-
config.searchApiKeys = {
|
|
1611
|
-
...storedSearchKeys(config),
|
|
1612
|
-
[index]: { appId, key }
|
|
1613
|
-
};
|
|
1614
|
-
await saveConfig(config);
|
|
1615
|
-
}
|
|
1616
|
-
async function forgetSearchKey(index) {
|
|
1617
|
-
const config = await loadConfig();
|
|
1618
|
-
const remaining = { ...storedSearchKeys(config) };
|
|
1619
|
-
delete remaining[index];
|
|
1620
|
-
config.searchApiKeys = remaining;
|
|
1621
|
-
await saveConfig(config);
|
|
1622
|
-
}
|
|
1623
1738
|
|
|
1624
1739
|
// src/core/orchestrator.ts
|
|
1625
1740
|
function defineStep(step) {
|
|
@@ -1823,61 +1938,138 @@ async function runWorkflow(workflow, appId) {
|
|
|
1823
1938
|
}
|
|
1824
1939
|
}
|
|
1825
1940
|
|
|
1826
|
-
// src/lib/
|
|
1827
|
-
import {
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
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;
|
|
1839
1957
|
try {
|
|
1840
|
-
|
|
1958
|
+
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
1841
1959
|
} catch {
|
|
1842
|
-
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
|
+
);
|
|
1843
1992
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
name,
|
|
1848
|
-
appId: t.application_id,
|
|
1849
|
-
apiKey: t.api_key,
|
|
1850
|
-
isDefault: t.default === true
|
|
1851
|
-
}));
|
|
1852
|
-
profiles.sort((a, b) => Number(b.isDefault) - Number(a.isDefault));
|
|
1853
|
-
return profiles.map(({ name, appId, apiKey }) => ({ name, appId, apiKey }));
|
|
1854
|
-
}
|
|
1855
|
-
async function loadActiveProfile() {
|
|
1856
|
-
let profiles;
|
|
1993
|
+
return parsed.data;
|
|
1994
|
+
}
|
|
1995
|
+
function parseJson(text) {
|
|
1857
1996
|
try {
|
|
1858
|
-
|
|
1997
|
+
return JSON.parse(text);
|
|
1859
1998
|
} catch {
|
|
1860
|
-
|
|
1999
|
+
return void 0;
|
|
1861
2000
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
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) {
|
|
1864
2021
|
throw new Error(
|
|
1865
|
-
"
|
|
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."
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
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"
|
|
1866
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
|
+
}
|
|
1867
2057
|
}
|
|
1868
|
-
|
|
2058
|
+
}
|
|
2059
|
+
async function ensureApplication() {
|
|
2060
|
+
return await currentApplication() ?? await promptForApplication();
|
|
1869
2061
|
}
|
|
1870
2062
|
|
|
1871
2063
|
// src/workflows/default.ts
|
|
1872
|
-
import { z as
|
|
2064
|
+
import { z as z27 } from "zod";
|
|
1873
2065
|
|
|
1874
2066
|
// src/actions/listIndices.ts
|
|
1875
|
-
import { z as
|
|
1876
|
-
var indicesListSchema =
|
|
1877
|
-
items:
|
|
1878
|
-
|
|
1879
|
-
name:
|
|
1880
|
-
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)
|
|
1881
2073
|
})
|
|
1882
2074
|
)
|
|
1883
2075
|
});
|
|
@@ -1948,12 +2140,12 @@ import "zod";
|
|
|
1948
2140
|
|
|
1949
2141
|
// src/lib/tools/listFiles.ts
|
|
1950
2142
|
import { tool } from "ai";
|
|
1951
|
-
import
|
|
2143
|
+
import z6 from "zod";
|
|
1952
2144
|
import { readdir } from "node:fs/promises";
|
|
1953
2145
|
|
|
1954
2146
|
// src/lib/tools/path.ts
|
|
1955
2147
|
import { lstat } from "node:fs/promises";
|
|
1956
|
-
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";
|
|
1957
2149
|
function resolveInRoot(ctx, path) {
|
|
1958
2150
|
const target = resolve2(ctx.cwd, path);
|
|
1959
2151
|
const rel = relative(ctx.root, target);
|
|
@@ -1969,7 +2161,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1969
2161
|
let current = ctx.root;
|
|
1970
2162
|
const parts = relative(ctx.root, dirname3(target)).split(sep).filter(Boolean);
|
|
1971
2163
|
for (const part of parts) {
|
|
1972
|
-
current =
|
|
2164
|
+
current = join6(current, part);
|
|
1973
2165
|
try {
|
|
1974
2166
|
if ((await lstat(current)).isSymbolicLink()) return true;
|
|
1975
2167
|
} catch (err) {
|
|
@@ -1984,7 +2176,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
1984
2176
|
function listFilesTool(ctx) {
|
|
1985
2177
|
return tool({
|
|
1986
2178
|
description: "List files in the current working directory",
|
|
1987
|
-
inputSchema:
|
|
2179
|
+
inputSchema: z6.object(),
|
|
1988
2180
|
execute: async () => {
|
|
1989
2181
|
logger.info("called listFiles tool");
|
|
1990
2182
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
@@ -2000,13 +2192,13 @@ function listFilesTool(ctx) {
|
|
|
2000
2192
|
|
|
2001
2193
|
// src/lib/tools/changeDirectory.ts
|
|
2002
2194
|
import { tool as tool2 } from "ai";
|
|
2003
|
-
import
|
|
2195
|
+
import z7 from "zod";
|
|
2004
2196
|
import { stat } from "node:fs/promises";
|
|
2005
2197
|
function changeDirectoryTool(ctx) {
|
|
2006
2198
|
return tool2({
|
|
2007
2199
|
description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
|
|
2008
|
-
inputSchema:
|
|
2009
|
-
path:
|
|
2200
|
+
inputSchema: z7.object({
|
|
2201
|
+
path: z7.string().describe("Directory to change into")
|
|
2010
2202
|
}),
|
|
2011
2203
|
execute: async ({ path }) => {
|
|
2012
2204
|
logger.info({ path }, "called changeDirectory tool");
|
|
@@ -2028,13 +2220,13 @@ function changeDirectoryTool(ctx) {
|
|
|
2028
2220
|
|
|
2029
2221
|
// src/lib/tools/reportStatus.ts
|
|
2030
2222
|
import { tool as tool3 } from "ai";
|
|
2031
|
-
import
|
|
2223
|
+
import z8 from "zod";
|
|
2032
2224
|
function reportStatusTool(output) {
|
|
2033
2225
|
return tool3({
|
|
2034
2226
|
description: "Report the status of your execution. Return a reason in case of failure.",
|
|
2035
|
-
inputSchema:
|
|
2036
|
-
status:
|
|
2037
|
-
reason:
|
|
2227
|
+
inputSchema: z8.object({
|
|
2228
|
+
status: z8.enum(["success", "fail"]),
|
|
2229
|
+
reason: z8.string().optional(),
|
|
2038
2230
|
output
|
|
2039
2231
|
}),
|
|
2040
2232
|
execute: async ({ status, reason, output: output2 }) => {
|
|
@@ -2046,8 +2238,8 @@ function reportStatusTool(output) {
|
|
|
2046
2238
|
|
|
2047
2239
|
// src/lib/tools/readFile.ts
|
|
2048
2240
|
import { tool as tool4 } from "ai";
|
|
2049
|
-
import
|
|
2050
|
-
import { readFile as
|
|
2241
|
+
import z9 from "zod";
|
|
2242
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2051
2243
|
|
|
2052
2244
|
// src/lib/tools/env.ts
|
|
2053
2245
|
import { basename } from "node:path";
|
|
@@ -2074,8 +2266,8 @@ function redactEnvValues(content) {
|
|
|
2074
2266
|
function readFileTool(ctx) {
|
|
2075
2267
|
return tool4({
|
|
2076
2268
|
description: "Read the contents of a file at the given path",
|
|
2077
|
-
inputSchema:
|
|
2078
|
-
filePath:
|
|
2269
|
+
inputSchema: z9.object({
|
|
2270
|
+
filePath: z9.string().describe("Path to the file to read")
|
|
2079
2271
|
}),
|
|
2080
2272
|
execute: async ({ filePath }) => {
|
|
2081
2273
|
if (++ctx.counts.read > ctx.limits.read) {
|
|
@@ -2085,7 +2277,7 @@ function readFileTool(ctx) {
|
|
|
2085
2277
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2086
2278
|
if (!resolved.ok) return resolved.error;
|
|
2087
2279
|
try {
|
|
2088
|
-
const content = await
|
|
2280
|
+
const content = await readFile3(resolved.target, "utf8");
|
|
2089
2281
|
return isEnvFile(resolved.target) ? redactEnvValues(content) : content;
|
|
2090
2282
|
} catch (err) {
|
|
2091
2283
|
return `Error reading ${filePath}: ${err.message}`;
|
|
@@ -2096,15 +2288,15 @@ function readFileTool(ctx) {
|
|
|
2096
2288
|
|
|
2097
2289
|
// src/lib/tools/writeFile.ts
|
|
2098
2290
|
import { tool as tool5 } from "ai";
|
|
2099
|
-
import
|
|
2291
|
+
import z10 from "zod";
|
|
2100
2292
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2101
2293
|
import { dirname as dirname4 } from "node:path";
|
|
2102
2294
|
function writeFileTool(ctx) {
|
|
2103
2295
|
return tool5({
|
|
2104
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.",
|
|
2105
|
-
inputSchema:
|
|
2106
|
-
filePath:
|
|
2107
|
-
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")
|
|
2108
2300
|
}),
|
|
2109
2301
|
execute: async ({ filePath, content }) => {
|
|
2110
2302
|
logger.info({ filePath }, "called writeFile tool");
|
|
@@ -2129,9 +2321,95 @@ function writeFileTool(ctx) {
|
|
|
2129
2321
|
|
|
2130
2322
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2131
2323
|
import { tool as tool6 } from "ai";
|
|
2132
|
-
import
|
|
2133
|
-
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";
|
|
2134
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
|
|
2135
2413
|
var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
|
|
2136
2414
|
var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
|
|
2137
2415
|
function appendEnv(content, entries) {
|
|
@@ -2145,9 +2423,9 @@ function hasEnv(content, name) {
|
|
|
2145
2423
|
}
|
|
2146
2424
|
function writeCredentialsTool(ctx) {
|
|
2147
2425
|
return tool6({
|
|
2148
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) into the given env file. The credentials
|
|
2149
|
-
inputSchema:
|
|
2150
|
-
filePath:
|
|
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(
|
|
2151
2429
|
'Path to the env file to write credentials into (e.g. ".env")'
|
|
2152
2430
|
)
|
|
2153
2431
|
}),
|
|
@@ -2155,11 +2433,17 @@ function writeCredentialsTool(ctx) {
|
|
|
2155
2433
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2156
2434
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2157
2435
|
if (resolved.ok === false) return resolved.error;
|
|
2158
|
-
|
|
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;
|
|
2159
2442
|
try {
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
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.`;
|
|
2163
2447
|
}
|
|
2164
2448
|
try {
|
|
2165
2449
|
if (await hasSymlinkParent(ctx, resolved.target)) {
|
|
@@ -2167,7 +2451,7 @@ function writeCredentialsTool(ctx) {
|
|
|
2167
2451
|
}
|
|
2168
2452
|
let existing = "";
|
|
2169
2453
|
try {
|
|
2170
|
-
existing = await
|
|
2454
|
+
existing = await readFile4(resolved.target, "utf8");
|
|
2171
2455
|
} catch (err) {
|
|
2172
2456
|
if (err.code !== "ENOENT") throw err;
|
|
2173
2457
|
}
|
|
@@ -2178,8 +2462,8 @@ function writeCredentialsTool(ctx) {
|
|
|
2178
2462
|
return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
|
|
2179
2463
|
}
|
|
2180
2464
|
const envWithCredentials = appendEnv(existing, [
|
|
2181
|
-
[APP_ID_VAR,
|
|
2182
|
-
[API_KEY_VAR,
|
|
2465
|
+
[APP_ID_VAR, appId],
|
|
2466
|
+
[API_KEY_VAR, writeKey]
|
|
2183
2467
|
]);
|
|
2184
2468
|
await mkdir4(dirname5(resolved.target), { recursive: true });
|
|
2185
2469
|
await writeFile4(resolved.target, envWithCredentials, "utf8");
|
|
@@ -2193,16 +2477,16 @@ function writeCredentialsTool(ctx) {
|
|
|
2193
2477
|
|
|
2194
2478
|
// src/lib/tools/searchFiles.ts
|
|
2195
2479
|
import { tool as tool7 } from "ai";
|
|
2196
|
-
import
|
|
2197
|
-
import { readdir as readdir2, readFile as
|
|
2198
|
-
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";
|
|
2199
2483
|
var MAX_QUERY_LENGTH = 1e3;
|
|
2200
2484
|
async function walkFiles(dir) {
|
|
2201
2485
|
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
2202
2486
|
const out = [];
|
|
2203
2487
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
2204
2488
|
if (e.name.startsWith(".") || skip.has(e.name)) continue;
|
|
2205
|
-
const full =
|
|
2489
|
+
const full = join7(dir, e.name);
|
|
2206
2490
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
2207
2491
|
else if (e.isFile()) out.push(full);
|
|
2208
2492
|
}
|
|
@@ -2211,9 +2495,9 @@ async function walkFiles(dir) {
|
|
|
2211
2495
|
function searchFilesTool(ctx) {
|
|
2212
2496
|
return tool7({
|
|
2213
2497
|
description: "Search file contents for a JavaScript regular expression (RegExp syntax, not grep/PCRE). Returns matching lines as file:line:text.",
|
|
2214
|
-
inputSchema:
|
|
2215
|
-
query:
|
|
2216
|
-
path:
|
|
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)")
|
|
2217
2501
|
}),
|
|
2218
2502
|
execute: async ({ query, path = "." }) => {
|
|
2219
2503
|
logger.info({ query, path }, "called searchFiles tool");
|
|
@@ -2235,7 +2519,7 @@ function searchFilesTool(ctx) {
|
|
|
2235
2519
|
for (const file of await walkFiles(resolved.target)) {
|
|
2236
2520
|
let content;
|
|
2237
2521
|
try {
|
|
2238
|
-
content = await
|
|
2522
|
+
content = await readFile5(file, "utf8");
|
|
2239
2523
|
} catch {
|
|
2240
2524
|
continue;
|
|
2241
2525
|
}
|
|
@@ -2257,7 +2541,7 @@ function searchFilesTool(ctx) {
|
|
|
2257
2541
|
|
|
2258
2542
|
// src/lib/tools/verifyImplementation.ts
|
|
2259
2543
|
import { tool as tool8 } from "ai";
|
|
2260
|
-
import
|
|
2544
|
+
import z14 from "zod";
|
|
2261
2545
|
|
|
2262
2546
|
// src/lib/tools/utils/runCommand.ts
|
|
2263
2547
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -2279,9 +2563,9 @@ function runCommand(command, args, cwd) {
|
|
|
2279
2563
|
}
|
|
2280
2564
|
|
|
2281
2565
|
// src/lib/tools/utils/packageManager.ts
|
|
2282
|
-
import { readFile as
|
|
2566
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2283
2567
|
import { existsSync } from "node:fs";
|
|
2284
|
-
import { join as
|
|
2568
|
+
import { join as join8 } from "node:path";
|
|
2285
2569
|
var LOCKFILES = [
|
|
2286
2570
|
["pnpm-lock.yaml", "pnpm"],
|
|
2287
2571
|
["yarn.lock", "yarn"],
|
|
@@ -2290,13 +2574,13 @@ var LOCKFILES = [
|
|
|
2290
2574
|
["package-lock.json", "npm"]
|
|
2291
2575
|
];
|
|
2292
2576
|
async function readPackageJson(cwd = process.cwd()) {
|
|
2293
|
-
return JSON.parse(await
|
|
2577
|
+
return JSON.parse(await readFile6(join8(cwd, "package.json"), "utf8"));
|
|
2294
2578
|
}
|
|
2295
2579
|
function packageManagerFrom(pkg) {
|
|
2296
2580
|
return pkg.packageManager?.split("@")[0] ?? "npm";
|
|
2297
2581
|
}
|
|
2298
2582
|
function packageManagerFromLockfile(cwd) {
|
|
2299
|
-
return LOCKFILES.find(([file]) => existsSync(
|
|
2583
|
+
return LOCKFILES.find(([file]) => existsSync(join8(cwd, file)))?.[1];
|
|
2300
2584
|
}
|
|
2301
2585
|
async function detectPackageManager(cwd) {
|
|
2302
2586
|
try {
|
|
@@ -2337,7 +2621,7 @@ async function runRepoVerificationCheck() {
|
|
|
2337
2621
|
function verifyImplementationTool() {
|
|
2338
2622
|
return tool8({
|
|
2339
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.",
|
|
2340
|
-
inputSchema:
|
|
2624
|
+
inputSchema: z14.object(),
|
|
2341
2625
|
execute: async () => {
|
|
2342
2626
|
logger.info("called verifyImplementation tool");
|
|
2343
2627
|
return runRepoVerificationCheck();
|
|
@@ -2351,7 +2635,7 @@ import { createAnthropic } from "@ai-sdk/anthropic";
|
|
|
2351
2635
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2352
2636
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "node:fs/promises";
|
|
2353
2637
|
import { dirname as dirname6 } from "node:path";
|
|
2354
|
-
import
|
|
2638
|
+
import z15 from "zod";
|
|
2355
2639
|
var DATA_DIR = ".algolia-wizard/data";
|
|
2356
2640
|
var RECORD_MODEL = "claude-haiku-4-5";
|
|
2357
2641
|
var MAX_RECORDS = 100;
|
|
@@ -2363,17 +2647,17 @@ var anthropic = createAnthropic({
|
|
|
2363
2647
|
function generateRecordTool(ctx) {
|
|
2364
2648
|
return tool9({
|
|
2365
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.",
|
|
2366
|
-
inputSchema:
|
|
2367
|
-
entityName:
|
|
2368
|
-
attributes:
|
|
2369
|
-
count:
|
|
2370
|
-
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.")
|
|
2371
2655
|
}),
|
|
2372
2656
|
execute: async ({ entityName, attributes, count, hint }) => {
|
|
2373
2657
|
logger.info({ entityName, count }, "called generateRecord tool");
|
|
2374
2658
|
try {
|
|
2375
|
-
const value =
|
|
2376
|
-
const recordSchema =
|
|
2659
|
+
const value = z15.union([z15.string(), z15.number(), z15.boolean(), z15.null()]);
|
|
2660
|
+
const recordSchema = z15.object(
|
|
2377
2661
|
Object.fromEntries(attributes.map((attr) => [attr, value]))
|
|
2378
2662
|
);
|
|
2379
2663
|
const generateBatch = async (batchCount) => {
|
|
@@ -2383,8 +2667,8 @@ function generateRecordTool(ctx) {
|
|
|
2383
2667
|
const { output } = await generateText({
|
|
2384
2668
|
model: anthropic(RECORD_MODEL),
|
|
2385
2669
|
output: Output.object({
|
|
2386
|
-
schema:
|
|
2387
|
-
records:
|
|
2670
|
+
schema: z15.object({
|
|
2671
|
+
records: z15.array(recordSchema).length(batchCount)
|
|
2388
2672
|
})
|
|
2389
2673
|
}),
|
|
2390
2674
|
prompt: [
|
|
@@ -2442,12 +2726,12 @@ function generateRecordTool(ctx) {
|
|
|
2442
2726
|
|
|
2443
2727
|
// src/lib/tools/notifyUser.ts
|
|
2444
2728
|
import { tool as tool10 } from "ai";
|
|
2445
|
-
import
|
|
2729
|
+
import z16 from "zod";
|
|
2446
2730
|
function notifyUserTool() {
|
|
2447
2731
|
return tool10({
|
|
2448
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.`,
|
|
2449
|
-
inputSchema:
|
|
2450
|
-
message:
|
|
2733
|
+
inputSchema: z16.object({
|
|
2734
|
+
message: z16.string().describe(
|
|
2451
2735
|
"Short, plain-language description of what you are doing now."
|
|
2452
2736
|
)
|
|
2453
2737
|
}),
|
|
@@ -2627,10 +2911,10 @@ async function runAgent(req) {
|
|
|
2627
2911
|
}
|
|
2628
2912
|
|
|
2629
2913
|
// src/actions/detectLanguage.ts
|
|
2630
|
-
import
|
|
2631
|
-
var detectLanguageSchema =
|
|
2632
|
-
languages:
|
|
2633
|
-
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() }))
|
|
2634
2918
|
});
|
|
2635
2919
|
var detectLanguage = () => runAgent({
|
|
2636
2920
|
instructions: [
|
|
@@ -2648,31 +2932,31 @@ var detectLanguage = () => runAgent({
|
|
|
2648
2932
|
});
|
|
2649
2933
|
|
|
2650
2934
|
// src/actions/analyzeCodebase.ts
|
|
2651
|
-
import
|
|
2935
|
+
import z20 from "zod";
|
|
2652
2936
|
var READONLY_TOOLS = [
|
|
2653
2937
|
"listFiles",
|
|
2654
2938
|
"changeDirectory",
|
|
2655
2939
|
"readFile",
|
|
2656
2940
|
"searchFiles"
|
|
2657
2941
|
];
|
|
2658
|
-
var ingestionAnalysisSchema =
|
|
2659
|
-
ingestionAnalysis:
|
|
2660
|
-
|
|
2661
|
-
name:
|
|
2662
|
-
paths:
|
|
2942
|
+
var ingestionAnalysisSchema = z20.object({
|
|
2943
|
+
ingestionAnalysis: z20.array(
|
|
2944
|
+
z20.object({
|
|
2945
|
+
name: z20.string(),
|
|
2946
|
+
paths: z20.array(z20.string()),
|
|
2663
2947
|
// indexable fields the agent found for this entity
|
|
2664
|
-
attributes:
|
|
2948
|
+
attributes: z20.array(z20.string())
|
|
2665
2949
|
})
|
|
2666
2950
|
)
|
|
2667
2951
|
});
|
|
2668
|
-
var searchImplementationAnalysisSchema =
|
|
2669
|
-
searchImplementationAnalysis:
|
|
2952
|
+
var searchImplementationAnalysisSchema = z20.object({
|
|
2953
|
+
searchImplementationAnalysis: z20.string()
|
|
2670
2954
|
});
|
|
2671
|
-
var verificationSchema =
|
|
2672
|
-
verification:
|
|
2955
|
+
var verificationSchema = z20.object({
|
|
2956
|
+
verification: z20.array(z20.string())
|
|
2673
2957
|
});
|
|
2674
2958
|
var confirmedEntitiesFieldSchema = ingestionAnalysisSchema.shape.ingestionAnalysis.optional();
|
|
2675
|
-
var analyzeCodebaseSchema =
|
|
2959
|
+
var analyzeCodebaseSchema = z20.object({
|
|
2676
2960
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
2677
2961
|
searchImplementationAnalysis: searchImplementationAnalysisSchema.shape.searchImplementationAnalysis.optional(),
|
|
2678
2962
|
verification: verificationSchema.shape.verification.optional(),
|
|
@@ -2734,7 +3018,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2734
3018
|
// package.json
|
|
2735
3019
|
var package_default = {
|
|
2736
3020
|
name: "@algolia/wizard",
|
|
2737
|
-
version: "0.8.0-rc.
|
|
3021
|
+
version: "0.8.0-rc.53.46",
|
|
2738
3022
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2739
3023
|
type: "module",
|
|
2740
3024
|
engines: {
|
|
@@ -2782,7 +3066,6 @@ var package_default = {
|
|
|
2782
3066
|
dependencies: {
|
|
2783
3067
|
"@ai-sdk/anthropic": "^3.0.81",
|
|
2784
3068
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
2785
|
-
"@algolia/cli": "^5.15.0",
|
|
2786
3069
|
"@hono/node-server": "^2.0.10",
|
|
2787
3070
|
"@mishieck/ink-titled-box": "^0.4.2",
|
|
2788
3071
|
"@segment/analytics-node": "^3.1.0",
|
|
@@ -2797,7 +3080,6 @@ var package_default = {
|
|
|
2797
3080
|
nanoid: "^5.1.15",
|
|
2798
3081
|
pino: "^10.3.1",
|
|
2799
3082
|
react: "^19.2.7",
|
|
2800
|
-
toml: "^4.1.1",
|
|
2801
3083
|
varlock: "^1.5.1",
|
|
2802
3084
|
zod: "^4.4.3",
|
|
2803
3085
|
zustand: "^5.0.14"
|
|
@@ -2855,8 +3137,8 @@ async function askList(ctx, prompt, { required = false } = {}) {
|
|
|
2855
3137
|
}
|
|
2856
3138
|
|
|
2857
3139
|
// src/actions/confirmLanguage.ts
|
|
2858
|
-
import
|
|
2859
|
-
var confirmLanguageSchema =
|
|
3140
|
+
import z22 from "zod";
|
|
3141
|
+
var confirmLanguageSchema = z22.object({
|
|
2860
3142
|
languages: detectLanguageSchema.shape.languages
|
|
2861
3143
|
});
|
|
2862
3144
|
async function confirmLanguage(ctx) {
|
|
@@ -2877,8 +3159,8 @@ async function confirmLanguage(ctx) {
|
|
|
2877
3159
|
}
|
|
2878
3160
|
|
|
2879
3161
|
// src/actions/confirmFramework.ts
|
|
2880
|
-
import
|
|
2881
|
-
var confirmFrameworkSchema =
|
|
3162
|
+
import z23 from "zod";
|
|
3163
|
+
var confirmFrameworkSchema = z23.object({
|
|
2882
3164
|
frameworks: detectLanguageSchema.shape.frameworks
|
|
2883
3165
|
});
|
|
2884
3166
|
var CURATED_FRAMEWORKS = [
|
|
@@ -3006,8 +3288,8 @@ async function promptUser(ctx, params) {
|
|
|
3006
3288
|
}
|
|
3007
3289
|
|
|
3008
3290
|
// src/actions/confirmEntities.ts
|
|
3009
|
-
import
|
|
3010
|
-
var confirmEntitiesSchema =
|
|
3291
|
+
import z24 from "zod";
|
|
3292
|
+
var confirmEntitiesSchema = z24.object({
|
|
3011
3293
|
// Final detection — the focused re-run may supersede project-scan's.
|
|
3012
3294
|
ingestionAnalysis: ingestionAnalysisSchema.shape.ingestionAnalysis.optional(),
|
|
3013
3295
|
confirmedEntities: confirmedEntitiesFieldSchema
|
|
@@ -3077,15 +3359,15 @@ async function confirmEntities(ctx) {
|
|
|
3077
3359
|
}
|
|
3078
3360
|
|
|
3079
3361
|
// src/actions/review.ts
|
|
3080
|
-
import { z as
|
|
3081
|
-
var reviewSchema =
|
|
3362
|
+
import { z as z25 } from "zod";
|
|
3363
|
+
var reviewSchema = z25.object({
|
|
3082
3364
|
// Broad, high-level takeaways grouped by theme (e.g. ingestion, search UI),
|
|
3083
3365
|
// not one entry per workflow step — a step's raw output can be a long,
|
|
3084
3366
|
// multi-paragraph blob (see implement.ts's summaries.join), and mirroring
|
|
3085
3367
|
// that 1:1 is what made the old per-step summary an unreadable wall of text.
|
|
3086
|
-
summaryPoints:
|
|
3087
|
-
reviewPrompt:
|
|
3088
|
-
nextSteps:
|
|
3368
|
+
summaryPoints: z25.array(z25.string()),
|
|
3369
|
+
reviewPrompt: z25.string(),
|
|
3370
|
+
nextSteps: z25.array(z25.string())
|
|
3089
3371
|
});
|
|
3090
3372
|
function formatCompletedSteps(steps) {
|
|
3091
3373
|
if (!steps.length) return "(no prior steps completed)";
|
|
@@ -3136,16 +3418,16 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
3136
3418
|
};
|
|
3137
3419
|
|
|
3138
3420
|
// src/actions/implement.ts
|
|
3139
|
-
import
|
|
3421
|
+
import z26 from "zod";
|
|
3140
3422
|
|
|
3141
3423
|
// src/lib/worktree.ts
|
|
3142
3424
|
import { execFile, spawn as spawn3 } from "node:child_process";
|
|
3143
|
-
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";
|
|
3144
3426
|
import {
|
|
3145
3427
|
basename as basename2,
|
|
3146
3428
|
dirname as dirname7,
|
|
3147
3429
|
isAbsolute as isAbsolute2,
|
|
3148
|
-
join as
|
|
3430
|
+
join as join9,
|
|
3149
3431
|
relative as relative2,
|
|
3150
3432
|
resolve as resolve3
|
|
3151
3433
|
} from "node:path";
|
|
@@ -3179,7 +3461,7 @@ async function isWorkingTreeDirty(repoRoot) {
|
|
|
3179
3461
|
return out.trim().length > 0;
|
|
3180
3462
|
}
|
|
3181
3463
|
async function pruneOldWorktrees(repoRoot) {
|
|
3182
|
-
const dir =
|
|
3464
|
+
const dir = join9(stateDir(repoRoot), "worktrees");
|
|
3183
3465
|
const stale = (await readdir3(dir).catch(() => [])).filter((name) => /^wizard-implement-\d+$/.test(name)).sort().reverse().slice(MAX_WIZARD_WORKTREES - 1);
|
|
3184
3466
|
for (const slug of stale) {
|
|
3185
3467
|
const branch = slug.replace("wizard-implement-", WIZARD_BRANCH_PREFIX);
|
|
@@ -3190,7 +3472,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3190
3472
|
"worktree",
|
|
3191
3473
|
"remove",
|
|
3192
3474
|
"--force",
|
|
3193
|
-
|
|
3475
|
+
join9(dir, slug)
|
|
3194
3476
|
]);
|
|
3195
3477
|
await git(["-C", repoRoot, "branch", "-D", branch]);
|
|
3196
3478
|
} catch (err) {
|
|
@@ -3204,7 +3486,7 @@ async function pruneOldWorktrees(repoRoot) {
|
|
|
3204
3486
|
async function createWorktree(repoRoot) {
|
|
3205
3487
|
const branch = `${WIZARD_BRANCH_PREFIX}${Date.now()}`;
|
|
3206
3488
|
const dirSlug = branch.replace(/\//g, "-");
|
|
3207
|
-
const path =
|
|
3489
|
+
const path = join9(stateDir(repoRoot), "worktrees", dirSlug);
|
|
3208
3490
|
await git(["-C", repoRoot, "worktree", "prune"]);
|
|
3209
3491
|
await pruneOldWorktrees(repoRoot);
|
|
3210
3492
|
await mkdir6(dirname7(path), { recursive: true });
|
|
@@ -3324,8 +3606,8 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3324
3606
|
} catch {
|
|
3325
3607
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
3326
3608
|
}
|
|
3327
|
-
const relPath =
|
|
3328
|
-
const dest =
|
|
3609
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
3610
|
+
const dest = join9(worktreePath, relPath);
|
|
3329
3611
|
try {
|
|
3330
3612
|
await mkdir6(dirname7(dest), { recursive: true });
|
|
3331
3613
|
await copyFile(source, dest);
|
|
@@ -3340,28 +3622,11 @@ async function copyUploadIntoWorktree(repoRoot, worktreePath, ingestDir, sourceP
|
|
|
3340
3622
|
function hasEnvVar(content, name) {
|
|
3341
3623
|
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
3342
3624
|
}
|
|
3343
|
-
async function readEnvVar(worktreePath, name) {
|
|
3344
|
-
let content;
|
|
3345
|
-
try {
|
|
3346
|
-
content = await readFile8(join10(worktreePath, ".env"), "utf8");
|
|
3347
|
-
} catch (err) {
|
|
3348
|
-
if (err.code !== "ENOENT") throw err;
|
|
3349
|
-
return void 0;
|
|
3350
|
-
}
|
|
3351
|
-
const match = new RegExp(
|
|
3352
|
-
`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*(.*)$`,
|
|
3353
|
-
"m"
|
|
3354
|
-
).exec(content);
|
|
3355
|
-
if (!match) return void 0;
|
|
3356
|
-
const value = match[1].trim().replace(/^(['"])(.*)\1$/, "$2").trim();
|
|
3357
|
-
if (!value || value.startsWith("<")) return void 0;
|
|
3358
|
-
return value;
|
|
3359
|
-
}
|
|
3360
3625
|
async function writeSearchEnvValues(worktreePath, vars) {
|
|
3361
|
-
const target =
|
|
3626
|
+
const target = join9(worktreePath, ".env");
|
|
3362
3627
|
let existing = "";
|
|
3363
3628
|
try {
|
|
3364
|
-
existing = await
|
|
3629
|
+
existing = await readFile7(target, "utf8");
|
|
3365
3630
|
} catch (err) {
|
|
3366
3631
|
if (err.code !== "ENOENT") throw err;
|
|
3367
3632
|
}
|
|
@@ -3429,85 +3694,15 @@ async function confirmDirtyWorkingTree(ctx, repoRoot) {
|
|
|
3429
3694
|
}
|
|
3430
3695
|
}
|
|
3431
3696
|
|
|
3432
|
-
// src/lib/algoliaApiKey.ts
|
|
3433
|
-
import { z as z23 } from "zod";
|
|
3434
|
-
var createdKeySchema = z23.object({
|
|
3435
|
-
key: z23.string().min(1).optional(),
|
|
3436
|
-
value: z23.string().min(1).optional()
|
|
3437
|
-
}).transform((o) => o.key ?? o.value);
|
|
3438
|
-
async function createSearchOnlyKey(index) {
|
|
3439
|
-
logger.info({ index }, "creating a search-only API key");
|
|
3440
|
-
const stdout = await runAlgoliaCli([
|
|
3441
|
-
"apikeys",
|
|
3442
|
-
"create",
|
|
3443
|
-
"--acl",
|
|
3444
|
-
"search",
|
|
3445
|
-
"--indices",
|
|
3446
|
-
index,
|
|
3447
|
-
"--description",
|
|
3448
|
-
`Algolia Wizard search-only key for ${index}`,
|
|
3449
|
-
"-o",
|
|
3450
|
-
"json"
|
|
3451
|
-
]);
|
|
3452
|
-
let payload;
|
|
3453
|
-
try {
|
|
3454
|
-
payload = JSON.parse(stdout);
|
|
3455
|
-
} catch {
|
|
3456
|
-
throw new Error("apikeys create returned output that is not valid JSON");
|
|
3457
|
-
}
|
|
3458
|
-
const created = createdKeySchema.parse(payload);
|
|
3459
|
-
if (!created) throw new Error("apikeys create returned no key value");
|
|
3460
|
-
return created;
|
|
3461
|
-
}
|
|
3462
|
-
async function apiKeyExists(key) {
|
|
3463
|
-
try {
|
|
3464
|
-
await runAlgoliaCli(["apikeys", "get", key, "-o", "json"]);
|
|
3465
|
-
return true;
|
|
3466
|
-
} catch (err) {
|
|
3467
|
-
return !/does not exist|not found|404/i.test(err.message);
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
async function resolveSearchOnlyKey(index, appId, envKey) {
|
|
3471
|
-
if (envKey) {
|
|
3472
|
-
await recordSearchKey(index, appId, envKey);
|
|
3473
|
-
return { key: envKey, source: "env" };
|
|
3474
|
-
}
|
|
3475
|
-
const stored = await getStoredSearchKey(index, appId);
|
|
3476
|
-
if (stored) {
|
|
3477
|
-
if (await apiKeyExists(stored)) {
|
|
3478
|
-
logger.info({ index, appId }, "reusing the stored search-only API key");
|
|
3479
|
-
return { key: stored, source: "config" };
|
|
3480
|
-
}
|
|
3481
|
-
logger.warn(
|
|
3482
|
-
{ index, appId },
|
|
3483
|
-
"the stored search-only API key no longer exists; creating a replacement"
|
|
3484
|
-
);
|
|
3485
|
-
await forgetSearchKey(index);
|
|
3486
|
-
}
|
|
3487
|
-
const key = await createSearchOnlyKey(index);
|
|
3488
|
-
await recordSearchKey(index, appId, key);
|
|
3489
|
-
return { key, source: "created" };
|
|
3490
|
-
}
|
|
3491
|
-
async function recordSearchKey(index, appId, key) {
|
|
3492
|
-
try {
|
|
3493
|
-
await storeSearchKey(index, appId, key);
|
|
3494
|
-
} catch (err) {
|
|
3495
|
-
logger.warn(
|
|
3496
|
-
{ err: err.message, index },
|
|
3497
|
-
"could not record the search-only API key; a later run may create another"
|
|
3498
|
-
);
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
3697
|
// src/lib/algoliaDocs.ts
|
|
3503
3698
|
import { readFileSync, readdirSync, existsSync as existsSync2 } from "node:fs";
|
|
3504
|
-
import { dirname as dirname8, join as
|
|
3699
|
+
import { dirname as dirname8, join as join10 } from "node:path";
|
|
3505
3700
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3506
|
-
var DOCS_SUBPATH =
|
|
3701
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
3507
3702
|
function findDocsDir() {
|
|
3508
3703
|
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
3509
3704
|
for (; ; ) {
|
|
3510
|
-
const candidate =
|
|
3705
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
3511
3706
|
if (existsSync2(candidate)) return candidate;
|
|
3512
3707
|
const parent = dirname8(dir);
|
|
3513
3708
|
if (parent === dir) return void 0;
|
|
@@ -3530,7 +3725,7 @@ function loadAlgoliaDoc(language) {
|
|
|
3530
3725
|
);
|
|
3531
3726
|
return "";
|
|
3532
3727
|
}
|
|
3533
|
-
return readFileSync(
|
|
3728
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
3534
3729
|
}
|
|
3535
3730
|
function getNamedDoc(name, language) {
|
|
3536
3731
|
const docsDir = findDocsDir();
|
|
@@ -3538,7 +3733,7 @@ function getNamedDoc(name, language) {
|
|
|
3538
3733
|
logger.warn("docs/algolia-sdk not found");
|
|
3539
3734
|
return "";
|
|
3540
3735
|
}
|
|
3541
|
-
const file =
|
|
3736
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
3542
3737
|
if (!existsSync2(file)) {
|
|
3543
3738
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
3544
3739
|
return "";
|
|
@@ -3565,50 +3760,50 @@ function shellQuote(value) {
|
|
|
3565
3760
|
}
|
|
3566
3761
|
|
|
3567
3762
|
// src/actions/implement.ts
|
|
3568
|
-
var implementSchema =
|
|
3569
|
-
filesChanged:
|
|
3570
|
-
summary:
|
|
3763
|
+
var implementSchema = z26.object({
|
|
3764
|
+
filesChanged: z26.array(z26.string()),
|
|
3765
|
+
summary: z26.string(),
|
|
3571
3766
|
// Absolute path to the throwaway worktree holding the generated changes, so
|
|
3572
3767
|
// the user can open it (`cd <worktreePath>`) or inspect the diff
|
|
3573
3768
|
// (`git -C <worktreePath> status/diff`).
|
|
3574
|
-
worktreePath:
|
|
3575
|
-
ingestCommand:
|
|
3769
|
+
worktreePath: z26.string().optional(),
|
|
3770
|
+
ingestCommand: z26.string().optional(),
|
|
3576
3771
|
// True when the user accepted the run-now prompt and the wizard executed the
|
|
3577
3772
|
// ingestion script; downstream steps use this to avoid telling the user to run
|
|
3578
3773
|
// a script that already ran.
|
|
3579
|
-
ingestScriptRan:
|
|
3774
|
+
ingestScriptRan: z26.boolean().optional(),
|
|
3580
3775
|
// Records ingested by the run-now execution, parsed from the script's
|
|
3581
3776
|
// machine-readable count line; absent when the script didn't run or emitted
|
|
3582
3777
|
// no parseable count.
|
|
3583
|
-
ingestRecordCount:
|
|
3778
|
+
ingestRecordCount: z26.number().optional(),
|
|
3584
3779
|
// Wall-clock duration of the run-now ingestion execution, in ms.
|
|
3585
|
-
ingestDurationMs:
|
|
3586
|
-
ingestionSource:
|
|
3780
|
+
ingestDurationMs: z26.number().optional(),
|
|
3781
|
+
ingestionSource: z26.enum(["local", "fileUpload", "generated"]),
|
|
3587
3782
|
// Suggested names/values, built from framework detection. The search agent is
|
|
3588
3783
|
// instructed to rename the prefix if it doesn't match the project's build
|
|
3589
3784
|
// tool, so the names it actually wrote can differ — treat these as hints, not
|
|
3590
3785
|
// ground truth (the agent's summary carries the final names).
|
|
3591
|
-
searchEnvVars:
|
|
3592
|
-
|
|
3593
|
-
name:
|
|
3594
|
-
value:
|
|
3786
|
+
searchEnvVars: z26.array(
|
|
3787
|
+
z26.object({
|
|
3788
|
+
name: z26.string(),
|
|
3789
|
+
value: z26.string()
|
|
3595
3790
|
})
|
|
3596
3791
|
).optional()
|
|
3597
3792
|
});
|
|
3598
|
-
var implementationOutputSchema =
|
|
3599
|
-
summary:
|
|
3793
|
+
var implementationOutputSchema = z26.object({
|
|
3794
|
+
summary: z26.string(),
|
|
3600
3795
|
// Ingestion only: how to run the generated script, as a structured pair the
|
|
3601
3796
|
// wizard turns into an argv (`<runtime> <entrypoint>`) — never a free-form
|
|
3602
3797
|
// command string. `runtime` is constrained to an allowlisted interpreter and
|
|
3603
3798
|
// `entrypoint` is validated to a worktree-relative path before execution, so
|
|
3604
3799
|
// the agent cannot inject extra commands or swap the interpreter.
|
|
3605
|
-
runtime:
|
|
3606
|
-
entrypoint:
|
|
3800
|
+
runtime: z26.enum(INGEST_RUNTIMES).optional(),
|
|
3801
|
+
entrypoint: z26.string().optional()
|
|
3607
3802
|
});
|
|
3608
|
-
var verificationOutputSchema =
|
|
3609
|
-
summary:
|
|
3610
|
-
sufficient:
|
|
3611
|
-
additionalInstructions:
|
|
3803
|
+
var verificationOutputSchema = z26.object({
|
|
3804
|
+
summary: z26.string(),
|
|
3805
|
+
sufficient: z26.boolean(),
|
|
3806
|
+
additionalInstructions: z26.string().optional()
|
|
3612
3807
|
});
|
|
3613
3808
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
3614
3809
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -3651,22 +3846,15 @@ function publicEnvPrefix(language) {
|
|
|
3651
3846
|
}
|
|
3652
3847
|
return "PUBLIC_";
|
|
3653
3848
|
}
|
|
3654
|
-
var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
|
|
3655
|
-
var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
|
|
3656
|
-
function appIdVar(language) {
|
|
3657
|
-
return `${publicEnvPrefix(language)}${APP_ID_VAR_SUFFIX}`;
|
|
3658
|
-
}
|
|
3659
|
-
function searchKeyVar(language) {
|
|
3660
|
-
return `${publicEnvPrefix(language)}${SEARCH_KEY_VAR_SUFFIX}`;
|
|
3661
|
-
}
|
|
3662
3849
|
function searchEnvVars(language, appId, searchKey) {
|
|
3850
|
+
const prefix = publicEnvPrefix(language);
|
|
3663
3851
|
return [
|
|
3664
3852
|
{
|
|
3665
|
-
name:
|
|
3853
|
+
name: `${prefix}ALGOLIA_APP_ID`,
|
|
3666
3854
|
value: appId ?? "<your-algolia-app-id>"
|
|
3667
3855
|
},
|
|
3668
3856
|
{
|
|
3669
|
-
name:
|
|
3857
|
+
name: `${prefix}ALGOLIA_SEARCH_API_KEY`,
|
|
3670
3858
|
value: searchKey ?? "<your-algolia-search-only-api-key>"
|
|
3671
3859
|
}
|
|
3672
3860
|
];
|
|
@@ -3728,13 +3916,15 @@ function searchInstructions(input) {
|
|
|
3728
3916
|
"Follow the Algolia JS SDK reference below for client setup and InstantSearch wiring; prefer it over prior knowledge:",
|
|
3729
3917
|
doc,
|
|
3730
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.`,
|
|
3731
|
-
// The key is provisioned only after verification passes, so the agent never
|
|
3732
|
-
// sees one. It must also leave .env alone: the wizard reads that file to
|
|
3733
|
-
// decide whether a key already exists, and an agent-invented value there
|
|
3734
|
-
// would be reused as if it were real.
|
|
3735
|
-
`Add Algolia App ID "${input.appId}"; leave the search-only key as a placeholder. Do not create or edit .env \u2014 the wizard writes the resolved key there itself.`,
|
|
3736
|
-
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
3737
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.",
|
|
3920
|
+
// appId always resolves (requireApplication throws otherwise); only the
|
|
3921
|
+
// search-only key is best-effort and can fall back to a placeholder.
|
|
3922
|
+
`Values: App ID "${input.appId}", search-only key ${input.searchKey ? `"${input.searchKey}"` : "(placeholder for the developer to fill in)"}.`,
|
|
3923
|
+
// Names are fixed, not the agent's to rename: the wizard writes the
|
|
3924
|
+
// resolved app id / search-only key into ".env" under these exact names
|
|
3925
|
+
// right after this step, so a renamed prefix here would leave the code
|
|
3926
|
+
// reading a var the wizard never wrote.
|
|
3927
|
+
`Use exactly these public env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
3738
3928
|
'Add any Algolia/InstantSearch packages you import to package.json "dependencies" with a valid version range; the wizard installs them in the worktree after you finish.',
|
|
3739
3929
|
"The summary should be extremely concise; do not mention env var setup or manual testing steps \u2014 the wizard writes the resolved credentials to .env and reports that separately."
|
|
3740
3930
|
];
|
|
@@ -3876,6 +4066,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3876
4066
|
}
|
|
3877
4067
|
}
|
|
3878
4068
|
const targetIndex = selected?.selection;
|
|
4069
|
+
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
3879
4070
|
await assertGitRepoWithHead(repoRoot);
|
|
3880
4071
|
if (await isWorkingTreeDirty(repoRoot)) {
|
|
3881
4072
|
await confirmDirtyWorkingTree(ctx, repoRoot);
|
|
@@ -3884,8 +4075,17 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3884
4075
|
const confirmed2 = normalized.confirmedEntities;
|
|
3885
4076
|
const searchLocation = normalized.searchImplementationAnalysis;
|
|
3886
4077
|
let appId;
|
|
4078
|
+
let searchKey;
|
|
3887
4079
|
if (useCases.includes("search")) {
|
|
3888
|
-
appId = (await
|
|
4080
|
+
appId = (await requireApplication()).id;
|
|
4081
|
+
try {
|
|
4082
|
+
searchKey = await resolveSearchOnlyKey(targetIndex);
|
|
4083
|
+
} catch (err) {
|
|
4084
|
+
logger.warn(
|
|
4085
|
+
{ err: err.message },
|
|
4086
|
+
"implement: could not resolve a search-only API key; the agent will scaffold a placeholder"
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
3889
4089
|
}
|
|
3890
4090
|
const worktree = existingWorktreePath ?? (await createWorktree(repoRoot)).path;
|
|
3891
4091
|
try {
|
|
@@ -3917,9 +4117,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3917
4117
|
targetIndex,
|
|
3918
4118
|
language,
|
|
3919
4119
|
appId,
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
searchEnvVars: searchEnvVars(language, appId),
|
|
4120
|
+
searchKey,
|
|
4121
|
+
searchEnvVars: searchEnvVars(language, appId, searchKey),
|
|
3923
4122
|
ingestDir: INGEST_DIR,
|
|
3924
4123
|
ingestionSource,
|
|
3925
4124
|
uploadFilePath,
|
|
@@ -3929,24 +4128,6 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3929
4128
|
};
|
|
3930
4129
|
const summaries = [];
|
|
3931
4130
|
if (uploadWarning) summaries.push(uploadWarning);
|
|
3932
|
-
let envSearchKey;
|
|
3933
|
-
let envAppIdMismatch = false;
|
|
3934
|
-
if (useCases.includes("search") && appId) {
|
|
3935
|
-
const envAppId = await readEnvVar(worktree, appIdVar(language));
|
|
3936
|
-
if (envAppId === appId) {
|
|
3937
|
-
envSearchKey = await readEnvVar(worktree, searchKeyVar(language));
|
|
3938
|
-
} else if (envAppId) {
|
|
3939
|
-
envAppIdMismatch = true;
|
|
3940
|
-
summaries.push(
|
|
3941
|
-
`\u26A0\uFE0F .env already sets ${appIdVar(language)}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVar(language)} and ${searchKeyVar(language)} by hand, or searches will fail.`
|
|
3942
|
-
);
|
|
3943
|
-
logger.warn(
|
|
3944
|
-
{ envAppId, appId },
|
|
3945
|
-
"implement: .env holds credentials for a different Algolia application; not reusing its search key"
|
|
3946
|
-
);
|
|
3947
|
-
}
|
|
3948
|
-
}
|
|
3949
|
-
let finalSearchEnvVars = input.searchEnvVars;
|
|
3950
4131
|
let agentRuns = 0;
|
|
3951
4132
|
let ingestRuntime;
|
|
3952
4133
|
let ingestEntrypoint;
|
|
@@ -4007,7 +4188,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4007
4188
|
messages: []
|
|
4008
4189
|
}) === true;
|
|
4009
4190
|
if (runNow) {
|
|
4010
|
-
const
|
|
4191
|
+
const ingestApp = await requireApplication();
|
|
4192
|
+
const writeKey = await resolveWriteKey(targetIndex);
|
|
4011
4193
|
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
4012
4194
|
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
4013
4195
|
runtime: ingestRuntime,
|
|
@@ -4019,8 +4201,8 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
4019
4201
|
ingestRuntime,
|
|
4020
4202
|
ingestEntrypoint,
|
|
4021
4203
|
{
|
|
4022
|
-
[APP_ID_VAR]:
|
|
4023
|
-
[API_KEY_VAR]:
|
|
4204
|
+
[APP_ID_VAR]: ingestApp.id,
|
|
4205
|
+
[API_KEY_VAR]: writeKey
|
|
4024
4206
|
}
|
|
4025
4207
|
);
|
|
4026
4208
|
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
@@ -4139,29 +4321,7 @@ ${run2.output}` : status;
|
|
|
4139
4321
|
}
|
|
4140
4322
|
extraInstructions = verificationRetryInstructions(verification);
|
|
4141
4323
|
}
|
|
4142
|
-
|
|
4143
|
-
let searchKeyError;
|
|
4144
|
-
if (appId) {
|
|
4145
|
-
try {
|
|
4146
|
-
const resolved = await resolveSearchOnlyKey(
|
|
4147
|
-
targetIndex,
|
|
4148
|
-
appId,
|
|
4149
|
-
envSearchKey
|
|
4150
|
-
);
|
|
4151
|
-
searchKey = resolved.key;
|
|
4152
|
-
summaries.push(
|
|
4153
|
-
resolved.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4154
|
-
);
|
|
4155
|
-
} catch (err) {
|
|
4156
|
-
searchKeyError = err.message;
|
|
4157
|
-
logger.warn(
|
|
4158
|
-
{ err: searchKeyError },
|
|
4159
|
-
"implement: could not provision a search-only API key; the .env value stays a placeholder"
|
|
4160
|
-
);
|
|
4161
|
-
}
|
|
4162
|
-
}
|
|
4163
|
-
finalSearchEnvVars = searchEnvVars(language, appId, searchKey);
|
|
4164
|
-
const resolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
4324
|
+
const resolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
4165
4325
|
(v) => !v.value.startsWith("<")
|
|
4166
4326
|
);
|
|
4167
4327
|
if (resolvedSearchEnvVars.length > 0) {
|
|
@@ -4172,29 +4332,13 @@ ${run2.output}` : status;
|
|
|
4172
4332
|
if (written.length > 0) {
|
|
4173
4333
|
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
4174
4334
|
}
|
|
4175
|
-
const stale = [];
|
|
4176
|
-
for (const v of resolvedSearchEnvVars) {
|
|
4177
|
-
if (written.includes(v.name)) continue;
|
|
4178
|
-
const current = await readEnvVar(worktree, v.name);
|
|
4179
|
-
if (current && current !== v.value) stale.push(v);
|
|
4180
|
-
}
|
|
4181
|
-
if (stale.length > 0 && !envAppIdMismatch) {
|
|
4182
|
-
summaries.push(
|
|
4183
|
-
`\u26A0\uFE0F .env already assigns a different value to ${stale.map((v) => `${v.name} (should be ${v.value})`).join(", ")} \u2014 the wizard left it alone. Fix it by hand, or searches will fail.`
|
|
4184
|
-
);
|
|
4185
|
-
logger.warn(
|
|
4186
|
-
{ vars: stale.map((v) => v.name) },
|
|
4187
|
-
"implement: .env holds different values for the resolved search credentials; not overwriting them"
|
|
4188
|
-
);
|
|
4189
|
-
}
|
|
4190
4335
|
}
|
|
4191
|
-
const unresolvedSearchEnvVars =
|
|
4336
|
+
const unresolvedSearchEnvVars = input.searchEnvVars.filter(
|
|
4192
4337
|
(v) => v.value.startsWith("<")
|
|
4193
4338
|
);
|
|
4194
4339
|
if (unresolvedSearchEnvVars.length > 0) {
|
|
4195
4340
|
summaries.push(
|
|
4196
|
-
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.`
|
|
4197
|
-
(searchKeyError ? ` Reason: ${searchKeyError}` : "")
|
|
4341
|
+
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.`
|
|
4198
4342
|
);
|
|
4199
4343
|
}
|
|
4200
4344
|
} else {
|
|
@@ -4226,7 +4370,7 @@ ${run2.output}` : status;
|
|
|
4226
4370
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
4227
4371
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
4228
4372
|
} : {},
|
|
4229
|
-
...useCases.includes("search") ? { searchEnvVars:
|
|
4373
|
+
...useCases.includes("search") ? { searchEnvVars: input.searchEnvVars } : {}
|
|
4230
4374
|
};
|
|
4231
4375
|
} finally {
|
|
4232
4376
|
process.chdir(repoRoot);
|
|
@@ -4269,8 +4413,8 @@ var defaultWorkflow = {
|
|
|
4269
4413
|
defineStep({
|
|
4270
4414
|
id: "select-index",
|
|
4271
4415
|
title: "Set up index",
|
|
4272
|
-
outputSchema:
|
|
4273
|
-
selection:
|
|
4416
|
+
outputSchema: z27.object({
|
|
4417
|
+
selection: z27.string()
|
|
4274
4418
|
}),
|
|
4275
4419
|
run: (ctx) => selectIndexStep(ctx)
|
|
4276
4420
|
}),
|
|
@@ -4549,7 +4693,7 @@ function parseCliArgs(argv) {
|
|
|
4549
4693
|
|
|
4550
4694
|
// src/lib/resetState.ts
|
|
4551
4695
|
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4552
|
-
import { join as
|
|
4696
|
+
import { join as join11 } from "node:path";
|
|
4553
4697
|
var KEEP = ["wizard.log"];
|
|
4554
4698
|
async function resetProjectState() {
|
|
4555
4699
|
const dir = stateDir();
|
|
@@ -4561,7 +4705,7 @@ async function resetProjectState() {
|
|
|
4561
4705
|
}
|
|
4562
4706
|
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4563
4707
|
await Promise.all(
|
|
4564
|
-
targets.map((name) => rm2(
|
|
4708
|
+
targets.map((name) => rm2(join11(dir, name), { recursive: true, force: true }))
|
|
4565
4709
|
);
|
|
4566
4710
|
return { dir, removed: targets };
|
|
4567
4711
|
}
|
|
@@ -4616,31 +4760,38 @@ ${formatStepList(workflow)}`);
|
|
|
4616
4760
|
}
|
|
4617
4761
|
async function run(workflow) {
|
|
4618
4762
|
const store = useWizard.getState();
|
|
4619
|
-
|
|
4763
|
+
const instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4764
|
+
await store.waitForStart();
|
|
4620
4765
|
let user = await getUser();
|
|
4621
4766
|
if (!user) {
|
|
4622
|
-
|
|
4623
|
-
instance.cleanup();
|
|
4767
|
+
store.beginAuth();
|
|
4624
4768
|
try {
|
|
4625
4769
|
await runAuthLogin();
|
|
4626
4770
|
} catch (err) {
|
|
4627
|
-
|
|
4771
|
+
store.setError(err instanceof Error ? err.message : String(err));
|
|
4772
|
+
await instance.waitUntilExit();
|
|
4628
4773
|
process.exit(1);
|
|
4629
4774
|
}
|
|
4630
|
-
|
|
4775
|
+
store.endAuth();
|
|
4631
4776
|
user = await getUser();
|
|
4632
4777
|
if (!user) {
|
|
4633
4778
|
store.setError(
|
|
4634
|
-
"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."
|
|
4635
4780
|
);
|
|
4636
4781
|
await instance.waitUntilExit();
|
|
4637
4782
|
process.exit(1);
|
|
4638
4783
|
}
|
|
4639
4784
|
}
|
|
4640
4785
|
store.setUser(user);
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
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);
|
|
4644
4795
|
}
|
|
4645
4796
|
var started = await startup();
|
|
4646
4797
|
if (typeof started === "number") {
|