@algolia/wizard 0.6.0 → 0.7.0-rc.57.36
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 +14 -0
- package/dist/main.js +516 -235
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,8 +12,22 @@ npx @algolia/wizard
|
|
|
12
12
|
|
|
13
13
|
# run a specific workflow by id
|
|
14
14
|
npx @algolia/wizard <workflow-id>
|
|
15
|
+
|
|
16
|
+
# see all options
|
|
17
|
+
npx @algolia/wizard --help
|
|
15
18
|
```
|
|
16
19
|
|
|
20
|
+
### Options
|
|
21
|
+
|
|
22
|
+
| Flag | Effect |
|
|
23
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| `--seed <step-id>` | Start the workflow at the step with this id (e.g. `--seed ingestion`), with the earlier steps pre-filled with test data. Pass with no value to print the step ids. |
|
|
25
|
+
| `--no-telemetry` | Send no telemetry or analytics for this run. |
|
|
26
|
+
| `--reset-on-run` | Wipe this project's wizard state (run state, AI-changes consent, worktrees) before starting, so the run behaves like a first-ever run. Credentials are untouched. |
|
|
27
|
+
| `-h`, `--help` | Print usage. |
|
|
28
|
+
|
|
29
|
+
`--seed` pre-fills the earlier steps with fabricated data so a single step can be exercised without running the whole workflow — useful for testing a step, not for a real implementation. It replaces any in-progress run for that workflow and pre-grants the AI-changes consent. See [Starting mid-workflow](CONTRIBUTING.md#starting-mid-workflow---seed).
|
|
30
|
+
|
|
17
31
|
On first run, Wizard checks for an Algolia profile. If none exists it launches `algolia auth login` and writes a default profile.
|
|
18
32
|
|
|
19
33
|
You'll also be asked once to consent to AI-authored changes to the repository.
|
package/dist/main.js
CHANGED
|
@@ -76,9 +76,9 @@ function refreshAuthToken() {
|
|
|
76
76
|
inFlightRefresh ??= (async () => {
|
|
77
77
|
try {
|
|
78
78
|
const raw = await runAlgoliaCli(["auth", "get", "--with-access-token"]);
|
|
79
|
-
const
|
|
80
|
-
useWizard.getState().setUser(
|
|
81
|
-
return
|
|
79
|
+
const user = toUserInfo(JSON.parse(raw));
|
|
80
|
+
useWizard.getState().setUser(user);
|
|
81
|
+
return user.token;
|
|
82
82
|
} catch {
|
|
83
83
|
return null;
|
|
84
84
|
} finally {
|
|
@@ -210,9 +210,9 @@ var useWizard = create((set, get) => ({
|
|
|
210
210
|
}
|
|
211
211
|
});
|
|
212
212
|
}),
|
|
213
|
-
startWorkflow: (
|
|
213
|
+
startWorkflow: (workflow, steps) => set({
|
|
214
214
|
phase: "running",
|
|
215
|
-
workflow
|
|
215
|
+
workflow,
|
|
216
216
|
steps,
|
|
217
217
|
currentStepIndex: 0,
|
|
218
218
|
error: null
|
|
@@ -222,7 +222,7 @@ var useWizard = create((set, get) => ({
|
|
|
222
222
|
get()._clearNoticeQueue();
|
|
223
223
|
set({ phase: "running", currentStepIndex: index, output: "", notices: [] });
|
|
224
224
|
},
|
|
225
|
-
setUser: (
|
|
225
|
+
setUser: (user) => set({ user }),
|
|
226
226
|
appendToken: (text) => set((s) => ({ output: s.output + text })),
|
|
227
227
|
clearOutput: () => set({ output: "" }),
|
|
228
228
|
// Renders the first notice of a burst immediately, then holds later
|
|
@@ -1282,147 +1282,13 @@ function Logs() {
|
|
|
1282
1282
|
|
|
1283
1283
|
// src/lib/events.ts
|
|
1284
1284
|
import "zod";
|
|
1285
|
-
function track(event, payload) {
|
|
1286
|
-
const token = getAuthToken();
|
|
1287
|
-
if (!token) return;
|
|
1288
|
-
const userId = useWizard.getState().user?.userId;
|
|
1289
|
-
if (!userId) return;
|
|
1290
|
-
void proxyFetch(`${PROXY_BASE_URL}/events`, {
|
|
1291
|
-
method: "POST",
|
|
1292
|
-
headers: {
|
|
1293
|
-
"content-type": "application/json",
|
|
1294
|
-
authorization: `Bearer ${token}`
|
|
1295
|
-
},
|
|
1296
|
-
body: JSON.stringify({ userId, event, properties: payload })
|
|
1297
|
-
}).catch((err) => {
|
|
1298
|
-
logger.warn({ err, event }, "failed to send analytics event");
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
// src/ui/App.tsx
|
|
1303
|
-
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1304
|
-
function App() {
|
|
1305
|
-
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1306
|
-
const { exit } = useApp();
|
|
1307
|
-
const { columns, rows } = useWindowSize7();
|
|
1308
|
-
const [showLogs, setShowLogs] = useState6(false);
|
|
1309
|
-
const finished = phase === "done" || phase === "error";
|
|
1310
|
-
const currentStep = steps[currentStepIndex];
|
|
1311
|
-
useInput6(
|
|
1312
|
-
(_input, key) => {
|
|
1313
|
-
if (key.return) {
|
|
1314
|
-
exit();
|
|
1315
|
-
}
|
|
1316
|
-
},
|
|
1317
|
-
{ isActive: finished }
|
|
1318
|
-
);
|
|
1319
|
-
useInput6((_input, key) => {
|
|
1320
|
-
if (phase === "idle" || phase === "preflight") return;
|
|
1321
|
-
if (key.tab) {
|
|
1322
|
-
setShowLogs(!showLogs);
|
|
1323
|
-
track("AI Wizard Interaction", {
|
|
1324
|
-
context: "global",
|
|
1325
|
-
key: "tab",
|
|
1326
|
-
currentStep: currentStep?.id
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
});
|
|
1330
|
-
const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1331
|
-
useInput6((_input, key) => {
|
|
1332
|
-
if (escOwnedElsewhere) return;
|
|
1333
|
-
if (key.escape) {
|
|
1334
|
-
track("AI Wizard Interaction", {
|
|
1335
|
-
context: "global",
|
|
1336
|
-
key: "esc",
|
|
1337
|
-
// No step is active until `startWorkflow` — report the phase instead.
|
|
1338
|
-
currentStep: currentStep?.id ?? phase
|
|
1339
|
-
});
|
|
1340
|
-
exit();
|
|
1341
|
-
}
|
|
1342
|
-
});
|
|
1343
|
-
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1344
|
-
const flexDirection = columns > 90 ? "row" : "column";
|
|
1345
|
-
const showSidebar = flexDirection === "row";
|
|
1346
|
-
return /* @__PURE__ */ jsxs12(
|
|
1347
|
-
Box13,
|
|
1348
|
-
{
|
|
1349
|
-
backgroundColor: COLORS.bg.main,
|
|
1350
|
-
flexDirection: "row",
|
|
1351
|
-
width: columns,
|
|
1352
|
-
minHeight: rows,
|
|
1353
|
-
children: [
|
|
1354
|
-
mainWindowVisible && /* @__PURE__ */ jsxs12(
|
|
1355
|
-
Box13,
|
|
1356
|
-
{
|
|
1357
|
-
flexDirection,
|
|
1358
|
-
width: "100%",
|
|
1359
|
-
justifyContent: "space-between",
|
|
1360
|
-
children: [
|
|
1361
|
-
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
|
|
1362
|
-
/* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
|
|
1363
|
-
/* @__PURE__ */ jsxs12(
|
|
1364
|
-
Box13,
|
|
1365
|
-
{
|
|
1366
|
-
flexDirection: "column",
|
|
1367
|
-
paddingX: 4,
|
|
1368
|
-
paddingY: 2,
|
|
1369
|
-
width: showSidebar ? 70 : "100%",
|
|
1370
|
-
flexGrow: showSidebar ? 1 : 0,
|
|
1371
|
-
children: [
|
|
1372
|
-
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1373
|
-
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1374
|
-
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1375
|
-
phase === "error" && error && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.error, children: [
|
|
1376
|
-
"\u2716 ",
|
|
1377
|
-
error
|
|
1378
|
-
] }) })
|
|
1379
|
-
]
|
|
1380
|
-
}
|
|
1381
|
-
)
|
|
1382
|
-
),
|
|
1383
|
-
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1384
|
-
]
|
|
1385
|
-
}
|
|
1386
|
-
),
|
|
1387
|
-
(phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1388
|
-
]
|
|
1389
|
-
}
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
// src/core/orchestrator.ts
|
|
1394
|
-
import "zod";
|
|
1395
|
-
|
|
1396
|
-
// src/core/config.ts
|
|
1397
|
-
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1398
|
-
import { join as join5 } from "node:path";
|
|
1399
|
-
var configFile = () => join5(stateDir(), "config.json");
|
|
1400
|
-
var DEFAULT_CONFIG = {
|
|
1401
|
-
version: 1,
|
|
1402
|
-
aiConsent: false,
|
|
1403
|
-
workflowsRun: []
|
|
1404
|
-
};
|
|
1405
|
-
async function loadConfig() {
|
|
1406
|
-
try {
|
|
1407
|
-
const raw = await readFile2(configFile(), "utf8");
|
|
1408
|
-
return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
|
|
1409
|
-
} catch {
|
|
1410
|
-
return { ...DEFAULT_CONFIG };
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
async function saveConfig(config) {
|
|
1414
|
-
await mkdir2(stateDir(), { recursive: true });
|
|
1415
|
-
await writeFile2(configFile(), JSON.stringify(config, null, 2), "utf8");
|
|
1416
|
-
}
|
|
1417
|
-
async function recordWorkflowRun(workflowId, completedAt) {
|
|
1418
|
-
const config = await loadConfig();
|
|
1419
|
-
config.workflowsRun.push({ workflowId, completedAt });
|
|
1420
|
-
await saveConfig(config);
|
|
1421
|
-
}
|
|
1422
1285
|
|
|
1423
1286
|
// src/lib/telemetry.ts
|
|
1287
|
+
function isTelemetryOptedOut() {
|
|
1288
|
+
return process.env.WIZARD_TELEMETRY === "false";
|
|
1289
|
+
}
|
|
1424
1290
|
function isTelemetryEnabled() {
|
|
1425
|
-
return Boolean(getAuthToken()) && !process.env.VITEST &&
|
|
1291
|
+
return Boolean(getAuthToken()) && !process.env.VITEST && !isTelemetryOptedOut();
|
|
1426
1292
|
}
|
|
1427
1293
|
function fireAndForget(promise) {
|
|
1428
1294
|
void promise.catch(() => {
|
|
@@ -1582,6 +1448,146 @@ function trackWorkflowError(ctx) {
|
|
|
1582
1448
|
);
|
|
1583
1449
|
}
|
|
1584
1450
|
|
|
1451
|
+
// src/lib/events.ts
|
|
1452
|
+
function track(event, payload) {
|
|
1453
|
+
if (isTelemetryOptedOut()) return;
|
|
1454
|
+
const token = getAuthToken();
|
|
1455
|
+
if (!token) return;
|
|
1456
|
+
const userId = useWizard.getState().user?.userId;
|
|
1457
|
+
if (!userId) return;
|
|
1458
|
+
void proxyFetch(`${PROXY_BASE_URL}/events`, {
|
|
1459
|
+
method: "POST",
|
|
1460
|
+
headers: {
|
|
1461
|
+
"content-type": "application/json",
|
|
1462
|
+
authorization: `Bearer ${token}`
|
|
1463
|
+
},
|
|
1464
|
+
body: JSON.stringify({ userId, event, properties: payload })
|
|
1465
|
+
}).catch((err) => {
|
|
1466
|
+
logger.warn({ err, event }, "failed to send analytics event");
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/ui/App.tsx
|
|
1471
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1472
|
+
function App() {
|
|
1473
|
+
const { phase, error, homeScreen, currentStepIndex, steps, inputReq } = useWizard();
|
|
1474
|
+
const { exit } = useApp();
|
|
1475
|
+
const { columns, rows } = useWindowSize7();
|
|
1476
|
+
const [showLogs, setShowLogs] = useState6(false);
|
|
1477
|
+
const finished = phase === "done" || phase === "error";
|
|
1478
|
+
const currentStep = steps[currentStepIndex];
|
|
1479
|
+
useInput6(
|
|
1480
|
+
(_input, key) => {
|
|
1481
|
+
if (key.return) {
|
|
1482
|
+
exit();
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
{ isActive: finished }
|
|
1486
|
+
);
|
|
1487
|
+
useInput6((_input, key) => {
|
|
1488
|
+
if (phase === "idle" || phase === "preflight") return;
|
|
1489
|
+
if (key.tab) {
|
|
1490
|
+
setShowLogs(!showLogs);
|
|
1491
|
+
track("AI Wizard Interaction", {
|
|
1492
|
+
context: "global",
|
|
1493
|
+
key: "tab",
|
|
1494
|
+
currentStep: currentStep?.id
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
const escOwnedElsewhere = phase === "idle" || phase === "awaitingInput" && inputReq?.promptType === "enterToContinue";
|
|
1499
|
+
useInput6((_input, key) => {
|
|
1500
|
+
if (escOwnedElsewhere) return;
|
|
1501
|
+
if (key.escape) {
|
|
1502
|
+
track("AI Wizard Interaction", {
|
|
1503
|
+
context: "global",
|
|
1504
|
+
key: "esc",
|
|
1505
|
+
// No step is active until `startWorkflow` — report the phase instead.
|
|
1506
|
+
currentStep: currentStep?.id ?? phase
|
|
1507
|
+
});
|
|
1508
|
+
exit();
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
const mainWindowVisible = phase === "running" || phase === "awaitingInput" || phase === "error" || phase === "done";
|
|
1512
|
+
const flexDirection = columns > 90 ? "row" : "column";
|
|
1513
|
+
const showSidebar = flexDirection === "row";
|
|
1514
|
+
return /* @__PURE__ */ jsxs12(
|
|
1515
|
+
Box13,
|
|
1516
|
+
{
|
|
1517
|
+
backgroundColor: COLORS.bg.main,
|
|
1518
|
+
flexDirection: "row",
|
|
1519
|
+
width: columns,
|
|
1520
|
+
minHeight: rows,
|
|
1521
|
+
children: [
|
|
1522
|
+
mainWindowVisible && /* @__PURE__ */ jsxs12(
|
|
1523
|
+
Box13,
|
|
1524
|
+
{
|
|
1525
|
+
flexDirection,
|
|
1526
|
+
width: "100%",
|
|
1527
|
+
justifyContent: "space-between",
|
|
1528
|
+
children: [
|
|
1529
|
+
showLogs ? /* @__PURE__ */ jsx13(Logs, {}) : (
|
|
1530
|
+
/* Fill the width beside the sidebar; row layout only (would grow vertically when stacked). */
|
|
1531
|
+
/* @__PURE__ */ jsxs12(
|
|
1532
|
+
Box13,
|
|
1533
|
+
{
|
|
1534
|
+
flexDirection: "column",
|
|
1535
|
+
paddingX: 4,
|
|
1536
|
+
paddingY: 2,
|
|
1537
|
+
width: showSidebar ? 70 : "100%",
|
|
1538
|
+
flexGrow: showSidebar ? 1 : 0,
|
|
1539
|
+
children: [
|
|
1540
|
+
/* @__PURE__ */ jsx13(Notices, {}),
|
|
1541
|
+
/* @__PURE__ */ jsx13(PromptInput, {}),
|
|
1542
|
+
phase === "running" && showSidebar && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(CurrentStep, {}) }),
|
|
1543
|
+
phase === "error" && error && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.error, children: [
|
|
1544
|
+
"\u2716 ",
|
|
1545
|
+
error
|
|
1546
|
+
] }) })
|
|
1547
|
+
]
|
|
1548
|
+
}
|
|
1549
|
+
)
|
|
1550
|
+
),
|
|
1551
|
+
showSidebar ? /* @__PURE__ */ jsx13(Sidebar, {}) : /* @__PURE__ */ jsx13(Ribbon, {})
|
|
1552
|
+
]
|
|
1553
|
+
}
|
|
1554
|
+
),
|
|
1555
|
+
(phase === "idle" || phase === "preflight") && (homeScreen === "learnMore" ? /* @__PURE__ */ jsx13(LearnMore, {}) : /* @__PURE__ */ jsx13(Welcome, {}))
|
|
1556
|
+
]
|
|
1557
|
+
}
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// src/core/orchestrator.ts
|
|
1562
|
+
import "zod";
|
|
1563
|
+
|
|
1564
|
+
// src/core/config.ts
|
|
1565
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1566
|
+
import { join as join5 } from "node:path";
|
|
1567
|
+
var configFile = () => join5(stateDir(), "config.json");
|
|
1568
|
+
var DEFAULT_CONFIG = {
|
|
1569
|
+
version: 1,
|
|
1570
|
+
aiConsent: false,
|
|
1571
|
+
workflowsRun: []
|
|
1572
|
+
};
|
|
1573
|
+
async function loadConfig() {
|
|
1574
|
+
try {
|
|
1575
|
+
const raw = await readFile2(configFile(), "utf8");
|
|
1576
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
|
|
1577
|
+
} catch {
|
|
1578
|
+
return { ...DEFAULT_CONFIG };
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
async function saveConfig(config) {
|
|
1582
|
+
await mkdir2(stateDir(), { recursive: true });
|
|
1583
|
+
await writeFile2(configFile(), JSON.stringify(config, null, 2), "utf8");
|
|
1584
|
+
}
|
|
1585
|
+
async function recordWorkflowRun(workflowId, completedAt) {
|
|
1586
|
+
const config = await loadConfig();
|
|
1587
|
+
config.workflowsRun.push({ workflowId, completedAt });
|
|
1588
|
+
await saveConfig(config);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1585
1591
|
// src/core/orchestrator.ts
|
|
1586
1592
|
function defineStep(step) {
|
|
1587
1593
|
return { visible: true, ...step };
|
|
@@ -1594,23 +1600,23 @@ function ensureExecutedStepCount(state) {
|
|
|
1594
1600
|
).length;
|
|
1595
1601
|
}
|
|
1596
1602
|
}
|
|
1597
|
-
function reconcileWorkflowState(state,
|
|
1603
|
+
function reconcileWorkflowState(state, workflow) {
|
|
1598
1604
|
const persistedIds = state.steps.map((s) => s.id).join("\n");
|
|
1599
|
-
const definedIds =
|
|
1605
|
+
const definedIds = workflow.steps.map((s) => s.id).join("\n");
|
|
1600
1606
|
if (persistedIds !== definedIds) return null;
|
|
1601
1607
|
for (let i = 0; i < state.steps.length; i++) {
|
|
1602
|
-
state.steps[i].title =
|
|
1603
|
-
state.steps[i].visible =
|
|
1608
|
+
state.steps[i].title = workflow.steps[i].title;
|
|
1609
|
+
state.steps[i].visible = workflow.steps[i].visible;
|
|
1604
1610
|
}
|
|
1605
1611
|
return state;
|
|
1606
1612
|
}
|
|
1607
|
-
function initWorkflowState(
|
|
1613
|
+
function initWorkflowState(workflow, now) {
|
|
1608
1614
|
return {
|
|
1609
|
-
workflowId:
|
|
1615
|
+
workflowId: workflow.id,
|
|
1610
1616
|
startedAt: now,
|
|
1611
1617
|
updatedAt: now,
|
|
1612
1618
|
currentStepIndex: 0,
|
|
1613
|
-
steps:
|
|
1619
|
+
steps: workflow.steps.map((s) => ({
|
|
1614
1620
|
id: s.id,
|
|
1615
1621
|
title: s.title,
|
|
1616
1622
|
visible: s.visible,
|
|
@@ -1623,8 +1629,8 @@ function initWorkflowState(workflow2, now) {
|
|
|
1623
1629
|
async function ensureConsent() {
|
|
1624
1630
|
const config = await loadConfig();
|
|
1625
1631
|
if (config.aiConsent) return;
|
|
1626
|
-
const
|
|
1627
|
-
const answer = await
|
|
1632
|
+
const store = useWizard.getState();
|
|
1633
|
+
const answer = await store.requestUserInput({
|
|
1628
1634
|
prompt: "Wizard will make AI-authored changes to this repository.",
|
|
1629
1635
|
promptType: "enterToContinue",
|
|
1630
1636
|
options: []
|
|
@@ -1670,7 +1676,7 @@ async function makeContext(state) {
|
|
|
1670
1676
|
};
|
|
1671
1677
|
}
|
|
1672
1678
|
async function runStep(state, index, step, appId) {
|
|
1673
|
-
const
|
|
1679
|
+
const store = useWizard.getState();
|
|
1674
1680
|
const record = state.steps[index];
|
|
1675
1681
|
const startedAt = Date.now();
|
|
1676
1682
|
trackActionStart({
|
|
@@ -1685,8 +1691,8 @@ async function runStep(state, index, step, appId) {
|
|
|
1685
1691
|
state.executedStepCount = (state.executedStepCount ?? 0) + 1;
|
|
1686
1692
|
}
|
|
1687
1693
|
state.updatedAt = nowIso();
|
|
1688
|
-
|
|
1689
|
-
|
|
1694
|
+
store.setActiveStep(index);
|
|
1695
|
+
store.syncSteps([...state.steps], index);
|
|
1690
1696
|
await saveWorkflowState(state);
|
|
1691
1697
|
await markInteraction();
|
|
1692
1698
|
const ctx = await makeContext(state);
|
|
@@ -1696,7 +1702,7 @@ async function runStep(state, index, step, appId) {
|
|
|
1696
1702
|
record.status = "done";
|
|
1697
1703
|
record.output = output;
|
|
1698
1704
|
state.updatedAt = nowIso();
|
|
1699
|
-
|
|
1705
|
+
store.syncSteps([...state.steps], index);
|
|
1700
1706
|
await saveWorkflowState(state);
|
|
1701
1707
|
trackActionEnd({
|
|
1702
1708
|
workflowId: state.workflowId,
|
|
@@ -1706,29 +1712,29 @@ async function runStep(state, index, step, appId) {
|
|
|
1706
1712
|
durationMs: Date.now() - startedAt
|
|
1707
1713
|
});
|
|
1708
1714
|
}
|
|
1709
|
-
async function runWorkflow(
|
|
1710
|
-
const
|
|
1715
|
+
async function runWorkflow(workflow, appId) {
|
|
1716
|
+
const store = useWizard.getState();
|
|
1711
1717
|
try {
|
|
1712
|
-
const persisted = await loadWorkflowState(
|
|
1713
|
-
const state = (persisted && reconcileWorkflowState(persisted,
|
|
1718
|
+
const persisted = await loadWorkflowState(workflow.id);
|
|
1719
|
+
const state = (persisted && reconcileWorkflowState(persisted, workflow)) ?? initWorkflowState(workflow, nowIso());
|
|
1714
1720
|
ensureExecutedStepCount(state);
|
|
1715
|
-
|
|
1721
|
+
store.startWorkflow(
|
|
1716
1722
|
{
|
|
1717
|
-
id:
|
|
1718
|
-
title:
|
|
1719
|
-
description:
|
|
1723
|
+
id: workflow.id,
|
|
1724
|
+
title: workflow.title,
|
|
1725
|
+
description: workflow.description
|
|
1720
1726
|
},
|
|
1721
1727
|
[...state.steps]
|
|
1722
1728
|
);
|
|
1723
1729
|
await ensureConsent();
|
|
1724
|
-
trackWorkflowStart({ workflowId:
|
|
1725
|
-
for (let i = state.currentStepIndex; i <
|
|
1726
|
-
await runStep(state, i,
|
|
1730
|
+
trackWorkflowStart({ workflowId: workflow.id, appId });
|
|
1731
|
+
for (let i = state.currentStepIndex; i < workflow.steps.length; i++) {
|
|
1732
|
+
await runStep(state, i, workflow.steps[i], appId);
|
|
1727
1733
|
}
|
|
1728
1734
|
const totalDurationMs = Date.now() - Date.parse(state.startedAt);
|
|
1729
1735
|
const stepCount = state.executedStepCount ?? state.steps.filter((s) => s.status === "done" && isStepVisible(s)).length;
|
|
1730
1736
|
trackWizardComplete({
|
|
1731
|
-
workflowId:
|
|
1737
|
+
workflowId: workflow.id,
|
|
1732
1738
|
appId,
|
|
1733
1739
|
total_duration: Math.round(totalDurationMs / 1e3),
|
|
1734
1740
|
total_steps: stepCount
|
|
@@ -1737,12 +1743,12 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1737
1743
|
total_duration_ms: totalDurationMs,
|
|
1738
1744
|
step_count: stepCount
|
|
1739
1745
|
});
|
|
1740
|
-
await recordWorkflowRun(
|
|
1741
|
-
await clearWorkflowState(
|
|
1742
|
-
|
|
1746
|
+
await recordWorkflowRun(workflow.id, nowIso());
|
|
1747
|
+
await clearWorkflowState(workflow.id);
|
|
1748
|
+
store.setDone();
|
|
1743
1749
|
} catch (err) {
|
|
1744
1750
|
const message = err instanceof Error ? err.message : String(err);
|
|
1745
|
-
const state = await loadWorkflowState(
|
|
1751
|
+
const state = await loadWorkflowState(workflow.id);
|
|
1746
1752
|
let step = "unknown";
|
|
1747
1753
|
let failedActionId;
|
|
1748
1754
|
let failedActionTitle;
|
|
@@ -1754,7 +1760,7 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1754
1760
|
record.status = "error";
|
|
1755
1761
|
record.error = message;
|
|
1756
1762
|
await saveWorkflowState(state);
|
|
1757
|
-
|
|
1763
|
+
store.syncSteps([...state.steps], state.currentStepIndex);
|
|
1758
1764
|
}
|
|
1759
1765
|
failedActionId = record.id;
|
|
1760
1766
|
failedActionTitle = record.title;
|
|
@@ -1762,7 +1768,7 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1762
1768
|
}
|
|
1763
1769
|
if (failedActionId && failedActionTitle) {
|
|
1764
1770
|
trackActionError({
|
|
1765
|
-
workflowId:
|
|
1771
|
+
workflowId: workflow.id,
|
|
1766
1772
|
appId,
|
|
1767
1773
|
actionId: failedActionId,
|
|
1768
1774
|
actionTitle: failedActionTitle,
|
|
@@ -1770,7 +1776,7 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1770
1776
|
});
|
|
1771
1777
|
}
|
|
1772
1778
|
trackWorkflowError({
|
|
1773
|
-
workflowId:
|
|
1779
|
+
workflowId: workflow.id,
|
|
1774
1780
|
appId,
|
|
1775
1781
|
error: message,
|
|
1776
1782
|
actionId: failedActionId
|
|
@@ -1780,7 +1786,7 @@ async function runWorkflow(workflow2, appId) {
|
|
|
1780
1786
|
error: message,
|
|
1781
1787
|
product_area: "AI Wizard"
|
|
1782
1788
|
});
|
|
1783
|
-
|
|
1789
|
+
store.setError(message);
|
|
1784
1790
|
}
|
|
1785
1791
|
}
|
|
1786
1792
|
|
|
@@ -1820,13 +1826,13 @@ async function loadActiveProfile() {
|
|
|
1820
1826
|
} catch {
|
|
1821
1827
|
profiles = [];
|
|
1822
1828
|
}
|
|
1823
|
-
const
|
|
1824
|
-
if (!
|
|
1829
|
+
const profile = profiles[0];
|
|
1830
|
+
if (!profile) {
|
|
1825
1831
|
throw new Error(
|
|
1826
1832
|
"No Algolia profile is configured. Run `npx @algolia/cli auth login` to authenticate."
|
|
1827
1833
|
);
|
|
1828
1834
|
}
|
|
1829
|
-
return
|
|
1835
|
+
return profile;
|
|
1830
1836
|
}
|
|
1831
1837
|
|
|
1832
1838
|
// src/workflows/default.ts
|
|
@@ -2116,9 +2122,9 @@ function writeCredentialsTool(ctx) {
|
|
|
2116
2122
|
logger.info({ filePath }, "called writeCredentials tool");
|
|
2117
2123
|
const resolved = resolveInRoot(ctx, filePath);
|
|
2118
2124
|
if (resolved.ok === false) return resolved.error;
|
|
2119
|
-
let
|
|
2125
|
+
let profile;
|
|
2120
2126
|
try {
|
|
2121
|
-
|
|
2127
|
+
profile = await loadActiveProfile();
|
|
2122
2128
|
} catch {
|
|
2123
2129
|
return "Error: no Algolia profile is configured, so credentials cannot be written. Ask the user to authenticate with the Algolia CLI first.";
|
|
2124
2130
|
}
|
|
@@ -2139,8 +2145,8 @@ function writeCredentialsTool(ctx) {
|
|
|
2139
2145
|
return `Skipped: ${filePath} already defines ${present.join(" and ")}. Leaving existing credentials untouched.`;
|
|
2140
2146
|
}
|
|
2141
2147
|
const envWithCredentials = appendEnv(existing, [
|
|
2142
|
-
[APP_ID_VAR,
|
|
2143
|
-
[API_KEY_VAR,
|
|
2148
|
+
[APP_ID_VAR, profile.appId],
|
|
2149
|
+
[API_KEY_VAR, profile.apiKey]
|
|
2144
2150
|
]);
|
|
2145
2151
|
await mkdir4(dirname5(resolved.target), { recursive: true });
|
|
2146
2152
|
await writeFile4(resolved.target, envWithCredentials, "utf8");
|
|
@@ -2695,7 +2701,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
2695
2701
|
// package.json
|
|
2696
2702
|
var package_default = {
|
|
2697
2703
|
name: "@algolia/wizard",
|
|
2698
|
-
version: "0.
|
|
2704
|
+
version: "0.7.0-rc.57.36",
|
|
2699
2705
|
description: "Magically implement Algolia functionality in your codebase",
|
|
2700
2706
|
type: "module",
|
|
2701
2707
|
engines: {
|
|
@@ -3914,27 +3920,27 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3914
3920
|
messages: []
|
|
3915
3921
|
}) === true;
|
|
3916
3922
|
if (runNow) {
|
|
3917
|
-
const
|
|
3923
|
+
const profile = await loadActiveProfile();
|
|
3918
3924
|
ctx.notify({ messages: [`Writing records to "${targetIndex}"\u2026`] });
|
|
3919
3925
|
const scriptLogId = ctx.logStart("runIngestScript", {
|
|
3920
3926
|
runtime: ingestRuntime,
|
|
3921
3927
|
entrypoint: ingestEntrypoint
|
|
3922
3928
|
});
|
|
3923
3929
|
const startedAt = Date.now();
|
|
3924
|
-
const
|
|
3930
|
+
const run2 = await runIngestScript(
|
|
3925
3931
|
worktree,
|
|
3926
3932
|
ingestRuntime,
|
|
3927
3933
|
ingestEntrypoint,
|
|
3928
3934
|
{
|
|
3929
|
-
[APP_ID_VAR]:
|
|
3930
|
-
[API_KEY_VAR]:
|
|
3935
|
+
[APP_ID_VAR]: profile.appId,
|
|
3936
|
+
[API_KEY_VAR]: profile.apiKey
|
|
3931
3937
|
}
|
|
3932
3938
|
);
|
|
3933
|
-
ctx.logEnd(scriptLogId,
|
|
3934
|
-
ingestScriptRan =
|
|
3939
|
+
ctx.logEnd(scriptLogId, run2.ok ? "success" : "error");
|
|
3940
|
+
ingestScriptRan = run2.ran && run2.ok;
|
|
3935
3941
|
if (ingestScriptRan) {
|
|
3936
3942
|
ingestDurationMs = Date.now() - startedAt;
|
|
3937
|
-
ingestRecordCount = parseIngestRecordCount(
|
|
3943
|
+
ingestRecordCount = parseIngestRecordCount(run2.output);
|
|
3938
3944
|
if (ingestRecordCount != null) {
|
|
3939
3945
|
track("AI Wizard Ingest Successful", {
|
|
3940
3946
|
entity_name: confirmed2?.map((e) => e.name).join(", ") || "unknown",
|
|
@@ -3945,43 +3951,43 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
|
|
|
3945
3951
|
}
|
|
3946
3952
|
let summaryLine;
|
|
3947
3953
|
let outcomeMessage;
|
|
3948
|
-
if (!
|
|
3949
|
-
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${
|
|
3950
|
-
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${
|
|
3954
|
+
if (!run2.ran) {
|
|
3955
|
+
summaryLine = `\u26A0\uFE0F Skipped running the ingestion script: ${run2.reason}`;
|
|
3956
|
+
outcomeMessage = `\u26A0\uFE0F The ingestion script did not run: ${run2.reason}`;
|
|
3951
3957
|
logger.warn(
|
|
3952
3958
|
{
|
|
3953
3959
|
runtime: ingestRuntime,
|
|
3954
3960
|
entrypoint: ingestEntrypoint,
|
|
3955
|
-
reason:
|
|
3961
|
+
reason: run2.reason
|
|
3956
3962
|
},
|
|
3957
3963
|
"implement: refused to auto-run ingestion script"
|
|
3958
3964
|
);
|
|
3959
3965
|
track("Error", {
|
|
3960
3966
|
step: "Push Data",
|
|
3961
|
-
error: `ingestion script skipped: ${
|
|
3967
|
+
error: `ingestion script skipped: ${run2.reason}`,
|
|
3962
3968
|
product_area: "AI Wizard"
|
|
3963
3969
|
});
|
|
3964
|
-
} else if (
|
|
3970
|
+
} else if (run2.ok) {
|
|
3965
3971
|
const status = "Ingestion run: succeeded.";
|
|
3966
|
-
summaryLine =
|
|
3967
|
-
${
|
|
3972
|
+
summaryLine = run2.output ? `${status}
|
|
3973
|
+
${run2.output}` : status;
|
|
3968
3974
|
outcomeMessage = `\u2705 Ingestion succeeded${ingestRecordCount != null ? ` \u2014 ${ingestRecordCount} record(s) indexed.` : "."}`;
|
|
3969
3975
|
} else {
|
|
3970
3976
|
const status = "\u26A0\uFE0F Ingestion run failed:";
|
|
3971
|
-
summaryLine =
|
|
3972
|
-
${
|
|
3973
|
-
outcomeMessage = `\u274C Ingestion failed.${
|
|
3977
|
+
summaryLine = run2.output ? `${status}
|
|
3978
|
+
${run2.output}` : status;
|
|
3979
|
+
outcomeMessage = `\u274C Ingestion failed.${run2.output ? ` ${run2.output}` : ""}`;
|
|
3974
3980
|
logger.warn(
|
|
3975
3981
|
{
|
|
3976
3982
|
runtime: ingestRuntime,
|
|
3977
3983
|
entrypoint: ingestEntrypoint,
|
|
3978
|
-
output:
|
|
3984
|
+
output: run2.output
|
|
3979
3985
|
},
|
|
3980
3986
|
"implement: ingestion script run failed"
|
|
3981
3987
|
);
|
|
3982
3988
|
track("Error", {
|
|
3983
3989
|
step: "Push Data",
|
|
3984
|
-
error:
|
|
3990
|
+
error: run2.output || "ingestion script exited non-zero",
|
|
3985
3991
|
product_area: "AI Wizard"
|
|
3986
3992
|
});
|
|
3987
3993
|
}
|
|
@@ -4171,10 +4177,10 @@ var defaultWorkflow = {
|
|
|
4171
4177
|
ctx.notify({
|
|
4172
4178
|
messages: ["Building your Algolia search experience\u2026"]
|
|
4173
4179
|
});
|
|
4174
|
-
const
|
|
4180
|
+
const ingestion2 = ctx.getStepOutput(
|
|
4175
4181
|
"ingestion"
|
|
4176
4182
|
);
|
|
4177
|
-
return implement(ctx, ["search"],
|
|
4183
|
+
return implement(ctx, ["search"], ingestion2?.worktreePath);
|
|
4178
4184
|
}
|
|
4179
4185
|
}),
|
|
4180
4186
|
defineStep({
|
|
@@ -4185,14 +4191,14 @@ var defaultWorkflow = {
|
|
|
4185
4191
|
ctx.notify({
|
|
4186
4192
|
messages: ["Summarizing what we did\u2026"]
|
|
4187
4193
|
});
|
|
4188
|
-
const
|
|
4194
|
+
const ingestion2 = ctx.getStepOutput(
|
|
4189
4195
|
"ingestion"
|
|
4190
4196
|
);
|
|
4191
4197
|
return reviewStep(ctx, {
|
|
4192
4198
|
// The ingestion step already showed the user the exact `ingestCommand`
|
|
4193
4199
|
// and worktree path as a notice, so nextSteps must not restate it —
|
|
4194
4200
|
// an LLM-paraphrased command risks being wrong.
|
|
4195
|
-
nextStepsGuidance:
|
|
4201
|
+
nextStepsGuidance: ingestion2?.ingestScriptRan ? "The wizard already ran the ingestion script and records are in the index. Do NOT tell the user to run it again; instead point them at the target index to confirm the records. Do not restate the ingestion command \u2014 the wizard already showed it to them." : "Tell the user to run the ingestion script; do not restate the exact command \u2014 the wizard already showed it to them above."
|
|
4196
4202
|
});
|
|
4197
4203
|
}
|
|
4198
4204
|
})
|
|
@@ -4207,38 +4213,313 @@ function getWorkflow(id) {
|
|
|
4207
4213
|
return workflows[id];
|
|
4208
4214
|
}
|
|
4209
4215
|
|
|
4216
|
+
// src/lib/seed.ts
|
|
4217
|
+
var projectScan2 = {
|
|
4218
|
+
languages: [{ name: "TypeScript", version: "5.7.2" }],
|
|
4219
|
+
frameworks: [{ name: "Next.js", version: "15.1.0" }],
|
|
4220
|
+
ingestionAnalysis: [
|
|
4221
|
+
{
|
|
4222
|
+
name: "Product",
|
|
4223
|
+
paths: ["src/models/product.ts"],
|
|
4224
|
+
attributes: ["id", "name", "description", "price", "category", "brand"]
|
|
4225
|
+
},
|
|
4226
|
+
{
|
|
4227
|
+
name: "Article",
|
|
4228
|
+
paths: ["src/models/article.ts"],
|
|
4229
|
+
attributes: ["id", "title", "body", "author", "tags", "publishedAt"]
|
|
4230
|
+
}
|
|
4231
|
+
],
|
|
4232
|
+
searchImplementationAnalysis: "src/components/Header.tsx",
|
|
4233
|
+
verification: ["pnpm typecheck", "pnpm lint"]
|
|
4234
|
+
};
|
|
4235
|
+
var confirmLanguage2 = {
|
|
4236
|
+
languages: projectScan2.languages
|
|
4237
|
+
};
|
|
4238
|
+
var confirmEntities2 = {
|
|
4239
|
+
ingestionAnalysis: projectScan2.ingestionAnalysis,
|
|
4240
|
+
confirmedEntities: [projectScan2.ingestionAnalysis[0]]
|
|
4241
|
+
};
|
|
4242
|
+
var selectIndex = {
|
|
4243
|
+
selection: "wizard_seed_products"
|
|
4244
|
+
};
|
|
4245
|
+
var ingestion = {
|
|
4246
|
+
filesChanged: ["algolia/ingest.mjs", "algolia/records.json", "package.json"],
|
|
4247
|
+
summary: "Generated sample Product records and an ingestion script that pushes them to the target index with algoliasearch.",
|
|
4248
|
+
ingestCommand: "node algolia/ingest.mjs",
|
|
4249
|
+
ingestScriptRan: true,
|
|
4250
|
+
ingestRecordCount: 25,
|
|
4251
|
+
ingestDurationMs: 4200,
|
|
4252
|
+
ingestionSource: "generated"
|
|
4253
|
+
};
|
|
4254
|
+
var confirmFramework2 = {
|
|
4255
|
+
frameworks: projectScan2.frameworks
|
|
4256
|
+
};
|
|
4257
|
+
var search = {
|
|
4258
|
+
filesChanged: ["src/components/Search.tsx", "src/components/Header.tsx", ".env"],
|
|
4259
|
+
summary: "Added an InstantSearch-powered search box and results list, mounted in the shared header component.",
|
|
4260
|
+
ingestionSource: "generated",
|
|
4261
|
+
searchEnvVars: [
|
|
4262
|
+
{ name: "NEXT_PUBLIC_ALGOLIA_APP_ID", value: "SEEDAPPID" },
|
|
4263
|
+
{ name: "NEXT_PUBLIC_ALGOLIA_SEARCH_KEY", value: "seedsearchkey" }
|
|
4264
|
+
]
|
|
4265
|
+
};
|
|
4266
|
+
var review = {
|
|
4267
|
+
summaryPoints: [
|
|
4268
|
+
"Ingested 25 generated Product records into wizard_seed_products.",
|
|
4269
|
+
"Added an InstantSearch search experience to the shared header."
|
|
4270
|
+
],
|
|
4271
|
+
reviewPrompt: "Review the Algolia ingestion and search changes in this worktree.",
|
|
4272
|
+
nextSteps: ["Point the ingestion script at your real product data."]
|
|
4273
|
+
};
|
|
4274
|
+
var SEEDS = {
|
|
4275
|
+
default: {
|
|
4276
|
+
"project-scan": projectScan2,
|
|
4277
|
+
"confirm-language": confirmLanguage2,
|
|
4278
|
+
"confirm-entities": confirmEntities2,
|
|
4279
|
+
"select-index": selectIndex,
|
|
4280
|
+
ingestion,
|
|
4281
|
+
"confirm-framework": confirmFramework2,
|
|
4282
|
+
search,
|
|
4283
|
+
review
|
|
4284
|
+
}
|
|
4285
|
+
};
|
|
4286
|
+
var SEED_USER_INPUTS = {
|
|
4287
|
+
default: {
|
|
4288
|
+
index: selectIndex.selection,
|
|
4289
|
+
confirmedEntities: confirmEntities2.confirmedEntities,
|
|
4290
|
+
ingestionAnalysis: confirmEntities2.ingestionAnalysis,
|
|
4291
|
+
implementation: "success"
|
|
4292
|
+
}
|
|
4293
|
+
};
|
|
4294
|
+
function formatStepList(workflow, startIndex) {
|
|
4295
|
+
return workflow.steps.map((step, i) => {
|
|
4296
|
+
const marker = i === startIndex ? "\u2192" : " ";
|
|
4297
|
+
const hidden = step.visible ? "" : " (hidden)";
|
|
4298
|
+
return ` ${marker} ${step.id} \u2014 ${step.title}${hidden}`;
|
|
4299
|
+
}).join("\n");
|
|
4300
|
+
}
|
|
4301
|
+
var nowIso2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
4302
|
+
function buildSeedState(workflow, startIndex) {
|
|
4303
|
+
const seeds = SEEDS[workflow.id] ?? {};
|
|
4304
|
+
const now = nowIso2();
|
|
4305
|
+
const steps = workflow.steps.map((step, i) => {
|
|
4306
|
+
const base = {
|
|
4307
|
+
id: step.id,
|
|
4308
|
+
title: step.title,
|
|
4309
|
+
visible: step.visible,
|
|
4310
|
+
status: i < startIndex ? "done" : "pending"
|
|
4311
|
+
};
|
|
4312
|
+
if (i >= startIndex) return base;
|
|
4313
|
+
if (!(step.id in seeds)) {
|
|
4314
|
+
throw new Error(
|
|
4315
|
+
`No seed data for step "${step.id}" (step ${i + 1} of ${workflow.id}). Add it to SEEDS['${workflow.id}'] in src/lib/seed.ts.`
|
|
4316
|
+
);
|
|
4317
|
+
}
|
|
4318
|
+
let output;
|
|
4319
|
+
try {
|
|
4320
|
+
output = step.outputSchema.parse(seeds[step.id]);
|
|
4321
|
+
} catch (err) {
|
|
4322
|
+
throw new Error(
|
|
4323
|
+
`Seed data for step "${step.id}" does not match its outputSchema: ` + (err instanceof Error ? err.message : String(err))
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
return { ...base, output };
|
|
4327
|
+
});
|
|
4328
|
+
return {
|
|
4329
|
+
workflowId: workflow.id,
|
|
4330
|
+
startedAt: now,
|
|
4331
|
+
updatedAt: now,
|
|
4332
|
+
currentStepIndex: startIndex,
|
|
4333
|
+
steps,
|
|
4334
|
+
algoliaState: {},
|
|
4335
|
+
userInputs: startIndex > 0 ? { ...SEED_USER_INPUTS[workflow.id] } : {}
|
|
4336
|
+
};
|
|
4337
|
+
}
|
|
4338
|
+
async function seedWorkflowState(workflow, value) {
|
|
4339
|
+
if (value === null || value.trim() === "") {
|
|
4340
|
+
throw new Error("--seed requires a step id.");
|
|
4341
|
+
}
|
|
4342
|
+
const stepId = value.trim();
|
|
4343
|
+
const startIndex = workflow.steps.findIndex((step) => step.id === stepId);
|
|
4344
|
+
if (startIndex === -1) {
|
|
4345
|
+
throw new Error(
|
|
4346
|
+
`--seed must name a step in "${workflow.id}", got "${stepId}".` + (/^\d+$/.test(stepId) ? " Step numbers are not accepted \u2014 pass the id." : "")
|
|
4347
|
+
);
|
|
4348
|
+
}
|
|
4349
|
+
const messages = [];
|
|
4350
|
+
const config = await loadConfig();
|
|
4351
|
+
if (!config.aiConsent) {
|
|
4352
|
+
config.aiConsent = true;
|
|
4353
|
+
await saveConfig(config);
|
|
4354
|
+
messages.push("Seed: set aiConsent in the project config (skips the consent prompt).");
|
|
4355
|
+
}
|
|
4356
|
+
if (startIndex === 0) {
|
|
4357
|
+
await clearWorkflowState(workflow.id);
|
|
4358
|
+
messages.push(
|
|
4359
|
+
`Seed: "${stepId}" is the first step, so this is a fresh run; cleared any existing state for "${workflow.id}".`,
|
|
4360
|
+
formatStepList(workflow, startIndex)
|
|
4361
|
+
);
|
|
4362
|
+
return { messages };
|
|
4363
|
+
}
|
|
4364
|
+
const existing = await loadWorkflowState(workflow.id);
|
|
4365
|
+
if (existing && existing.currentStepIndex > 0) {
|
|
4366
|
+
const wasAt = workflow.steps[existing.currentStepIndex];
|
|
4367
|
+
messages.push(
|
|
4368
|
+
`Seed: discarded in-progress state for "${workflow.id}" (was at ${wasAt ? `"${wasAt.id}"` : "the end"}).`
|
|
4369
|
+
);
|
|
4370
|
+
}
|
|
4371
|
+
const state = buildSeedState(workflow, startIndex);
|
|
4372
|
+
await saveWorkflowState(state);
|
|
4373
|
+
messages.push(
|
|
4374
|
+
`Seed: "${workflow.id}" will resume at "${stepId}" with ${startIndex} step(s) pre-filled.`,
|
|
4375
|
+
formatStepList(workflow, startIndex)
|
|
4376
|
+
);
|
|
4377
|
+
return { messages };
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
// src/lib/cli.ts
|
|
4381
|
+
var USAGE = `Usage: wizard [workflow-id] [options]
|
|
4382
|
+
|
|
4383
|
+
Options:
|
|
4384
|
+
--seed <step-id> Start the workflow at the step with this id, with earlier
|
|
4385
|
+
steps pre-filled with test data. Pass with no value to print
|
|
4386
|
+
the step ids. See CONTRIBUTING.md.
|
|
4387
|
+
--no-telemetry Send no telemetry or analytics for this run.
|
|
4388
|
+
--reset-on-run Wipe this project's wizard state (run state, AI consent,
|
|
4389
|
+
worktrees) before starting, so the run behaves like a
|
|
4390
|
+
first-ever run. Algolia credentials are not touched.
|
|
4391
|
+
-h, --help Print this message.`;
|
|
4392
|
+
function parseCliArgs(argv) {
|
|
4393
|
+
const positionals = [];
|
|
4394
|
+
let seed;
|
|
4395
|
+
let telemetry = true;
|
|
4396
|
+
let resetOnRun = false;
|
|
4397
|
+
let help = false;
|
|
4398
|
+
for (let i = 0; i < argv.length; i++) {
|
|
4399
|
+
const arg = argv[i];
|
|
4400
|
+
if (arg === "--seed") {
|
|
4401
|
+
const next = argv[i + 1];
|
|
4402
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
4403
|
+
seed = next;
|
|
4404
|
+
i++;
|
|
4405
|
+
} else {
|
|
4406
|
+
seed = null;
|
|
4407
|
+
}
|
|
4408
|
+
} else if (arg.startsWith("--seed=")) {
|
|
4409
|
+
seed = arg.slice("--seed=".length);
|
|
4410
|
+
} else if (arg === "--no-telemetry") {
|
|
4411
|
+
telemetry = false;
|
|
4412
|
+
} else if (arg === "--reset-on-run") {
|
|
4413
|
+
resetOnRun = true;
|
|
4414
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
4415
|
+
help = true;
|
|
4416
|
+
} else if (arg.startsWith("-")) {
|
|
4417
|
+
throw new Error(`Unknown option "${arg}".`);
|
|
4418
|
+
} else {
|
|
4419
|
+
positionals.push(arg);
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
return { positionals, seed, telemetry, resetOnRun, help };
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
// src/lib/resetState.ts
|
|
4426
|
+
import { readdir as readdir4, rm as rm2 } from "node:fs/promises";
|
|
4427
|
+
import { join as join12 } from "node:path";
|
|
4428
|
+
var KEEP = ["wizard.log"];
|
|
4429
|
+
async function resetProjectState() {
|
|
4430
|
+
const dir = stateDir();
|
|
4431
|
+
let entries;
|
|
4432
|
+
try {
|
|
4433
|
+
entries = await readdir4(dir);
|
|
4434
|
+
} catch {
|
|
4435
|
+
return { dir, removed: [] };
|
|
4436
|
+
}
|
|
4437
|
+
const targets = entries.filter((name) => !KEEP.includes(name));
|
|
4438
|
+
await Promise.all(
|
|
4439
|
+
targets.map((name) => rm2(join12(dir, name), { recursive: true, force: true }))
|
|
4440
|
+
);
|
|
4441
|
+
return { dir, removed: targets };
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4210
4444
|
// src/main.tsx
|
|
4211
4445
|
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
if (!workflow) {
|
|
4215
|
-
const available = Object.keys(workflows).join(", ");
|
|
4216
|
-
console.error(`Unknown workflow "${requestedId}". Available: ${available}`);
|
|
4217
|
-
process.exit(1);
|
|
4218
|
-
}
|
|
4219
|
-
var store = useWizard.getState();
|
|
4220
|
-
var instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4221
|
-
var user = await getUser();
|
|
4222
|
-
if (!user) {
|
|
4223
|
-
await instance.waitUntilRenderFlush();
|
|
4224
|
-
instance.cleanup();
|
|
4446
|
+
async function startup() {
|
|
4447
|
+
let args;
|
|
4225
4448
|
try {
|
|
4226
|
-
|
|
4449
|
+
args = parseCliArgs(process.argv.slice(2));
|
|
4227
4450
|
} catch (err) {
|
|
4228
4451
|
console.error(err instanceof Error ? err.message : String(err));
|
|
4229
|
-
|
|
4452
|
+
console.error(`
|
|
4453
|
+
${USAGE}`);
|
|
4454
|
+
return 1;
|
|
4230
4455
|
}
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4456
|
+
if (args.help) {
|
|
4457
|
+
console.log(USAGE);
|
|
4458
|
+
return 0;
|
|
4459
|
+
}
|
|
4460
|
+
if (!args.telemetry) {
|
|
4461
|
+
process.env.WIZARD_TELEMETRY = "false";
|
|
4462
|
+
console.log(
|
|
4463
|
+
"Telemetry and analytics disabled for this run (--no-telemetry)."
|
|
4236
4464
|
);
|
|
4237
|
-
await instance.waitUntilExit();
|
|
4238
|
-
process.exit(1);
|
|
4239
4465
|
}
|
|
4466
|
+
const requestedId = args.positionals[0] ?? defaultWorkflow.id;
|
|
4467
|
+
const workflow = getWorkflow(requestedId);
|
|
4468
|
+
if (!workflow) {
|
|
4469
|
+
const available = Object.keys(workflows).join(", ");
|
|
4470
|
+
console.error(`Unknown workflow "${requestedId}". Available: ${available}`);
|
|
4471
|
+
return 1;
|
|
4472
|
+
}
|
|
4473
|
+
if (args.resetOnRun) {
|
|
4474
|
+
const { dir, removed } = await resetProjectState();
|
|
4475
|
+
console.log(
|
|
4476
|
+
removed.length > 0 ? `Reset wizard state in ${dir}: ${removed.join(", ")}` : `No wizard state to reset in ${dir}`
|
|
4477
|
+
);
|
|
4478
|
+
}
|
|
4479
|
+
if (args.seed !== void 0) {
|
|
4480
|
+
try {
|
|
4481
|
+
const { messages } = await seedWorkflowState(workflow, args.seed);
|
|
4482
|
+
for (const message of messages) console.log(message);
|
|
4483
|
+
} catch (err) {
|
|
4484
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
4485
|
+
console.error(`Steps in "${workflow.id}":
|
|
4486
|
+
${formatStepList(workflow)}`);
|
|
4487
|
+
return 1;
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
return workflow;
|
|
4491
|
+
}
|
|
4492
|
+
async function run(workflow) {
|
|
4493
|
+
const store = useWizard.getState();
|
|
4494
|
+
let instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4495
|
+
let user = await getUser();
|
|
4496
|
+
if (!user) {
|
|
4497
|
+
await instance.waitUntilRenderFlush();
|
|
4498
|
+
instance.cleanup();
|
|
4499
|
+
try {
|
|
4500
|
+
await runAuthLogin();
|
|
4501
|
+
} catch (err) {
|
|
4502
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
4503
|
+
process.exit(1);
|
|
4504
|
+
}
|
|
4505
|
+
instance = render(/* @__PURE__ */ jsx14(App, {}), { incrementalRendering: true });
|
|
4506
|
+
user = await getUser();
|
|
4507
|
+
if (!user) {
|
|
4508
|
+
store.setError(
|
|
4509
|
+
"Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli auth login` directly."
|
|
4510
|
+
);
|
|
4511
|
+
await instance.waitUntilExit();
|
|
4512
|
+
process.exit(1);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
store.setUser(user);
|
|
4516
|
+
const profile = await loadActiveProfile();
|
|
4517
|
+
await store.waitForStart();
|
|
4518
|
+
runWorkflow(workflow, profile?.appId);
|
|
4519
|
+
}
|
|
4520
|
+
var started = await startup();
|
|
4521
|
+
if (typeof started === "number") {
|
|
4522
|
+
process.exitCode = started;
|
|
4523
|
+
} else {
|
|
4524
|
+
await run(started);
|
|
4240
4525
|
}
|
|
4241
|
-
store.setUser(user);
|
|
4242
|
-
var profile = await loadActiveProfile();
|
|
4243
|
-
await store.waitForStart();
|
|
4244
|
-
runWorkflow(workflow, profile?.appId);
|