@algolia/wizard 0.40.0 → 0.41.0-rc.131.290

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.
Files changed (2) hide show
  1. package/dist/main.js +403 -235
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -5,7 +5,7 @@ import { render } from "ink";
5
5
 
6
6
  // src/ui/App.tsx
7
7
  import { Box as Box20, Text as Text20, useApp, useInput as useInput7, useWindowSize as useWindowSize7 } from "ink";
8
- import Spinner2 from "ink-spinner";
8
+ import Spinner3 from "ink-spinner";
9
9
 
10
10
  // src/core/store.ts
11
11
  import { create } from "zustand";
@@ -259,10 +259,14 @@ var useWizard = create((set, get) => ({
259
259
  error: null,
260
260
  inputReq: null,
261
261
  _resolve: null,
262
+ settingUpAppId: null,
263
+ setSettingUpApp: (appId) => set({ settingUpAppId: appId }),
262
264
  // `endAuth` lands on 'preflight', not 'idle': sign-in happens after the
263
265
  // welcome screen, so going back would gate the run a second time.
264
266
  beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
265
- endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
267
+ endAuth: () => set(
268
+ (s) => s.phase === "authenticating" ? { phase: "preflight", cliOutput: [] } : {}
269
+ ),
266
270
  confirmStart: () => set(
267
271
  (s) => s.phase === "idle" ? { phase: "preflight", homeScreen: "home" } : {}
268
272
  ),
@@ -409,7 +413,8 @@ var useWizard = create((set, get) => ({
409
413
  logs: [],
410
414
  error: null,
411
415
  inputReq: null,
412
- _resolve: null
416
+ _resolve: null,
417
+ settingUpAppId: null
413
418
  });
414
419
  }
415
420
  }));
@@ -637,6 +642,7 @@ function Notices({
637
642
 
638
643
  // src/ui/PromptInput.tsx
639
644
  import { Box as Box9, Text as Text9 } from "ink";
645
+ import Spinner from "ink-spinner";
640
646
  import TextInput from "ink-text-input";
641
647
  import { useState as useState6 } from "react";
642
648
 
@@ -657,25 +663,27 @@ function SelectRow({
657
663
  labelWidth,
658
664
  highlightBackground = true,
659
665
  usePadding = false,
666
+ disabled = false,
660
667
  children
661
668
  }) {
662
- const labelColor = highlighted ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
669
+ const active = highlighted && !disabled;
670
+ const labelColor = disabled ? COLORS.dim : active ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
663
671
  return /* @__PURE__ */ jsxs3(
664
672
  Box4,
665
673
  {
666
674
  width,
667
675
  paddingX: usePadding ? 1 : 0,
668
676
  paddingY: usePadding ? 1 : 0,
669
- backgroundColor: highlighted && highlightBackground ? COLORS.highlight.bg : void 0,
677
+ backgroundColor: active && highlightBackground ? COLORS.highlight.bg : void 0,
670
678
  children: [
671
679
  /* @__PURE__ */ jsx3(Box4, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
672
680
  Text4,
673
681
  {
674
682
  color: labelColor,
675
- bold: highlighted && !highlightBackground,
683
+ bold: active && !highlightBackground,
676
684
  wrap: "truncate",
677
685
  children: [
678
- highlighted ? "\u276F " : " ",
686
+ active ? "\u276F " : " ",
679
687
  label
680
688
  ]
681
689
  }
@@ -875,6 +883,14 @@ var ARROW_WIDTH = 4;
875
883
  var COLUMN_GAP = 2;
876
884
  var BAR_PADDING = 2;
877
885
  var ROW_HEIGHT = 3;
886
+ function nextSelectableIndex(start, dir, rowCount, isDisabled) {
887
+ let i = start;
888
+ for (let step = 0; step < rowCount; step++) {
889
+ i = (i + dir + rowCount) % rowCount;
890
+ if (!isDisabled(i)) return i;
891
+ }
892
+ return start;
893
+ }
878
894
  function SelectPrompt({
879
895
  options,
880
896
  onSelect,
@@ -886,15 +902,18 @@ function SelectPrompt({
886
902
  multi,
887
903
  cancelable,
888
904
  secondary,
905
+ disabled,
889
906
  defaultSelectedIndex = 0
890
907
  }) {
891
- const [index, setIndex] = useState5(
892
- () => defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0
893
- );
894
- const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
895
908
  const hasCancel = Boolean(multi || cancelable);
896
909
  const rows = hasCancel ? [...options, "Cancel"] : options;
897
910
  const cancelIndex = hasCancel ? options.length : -1;
911
+ const isDisabled = (i) => i !== cancelIndex && Boolean(disabled?.[i]);
912
+ const [index, setIndex] = useState5(() => {
913
+ const start = defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0;
914
+ return isDisabled(start) ? nextSelectableIndex(start, 1, rows.length, isDisabled) : start;
915
+ });
916
+ const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
898
917
  const hints = [];
899
918
  if (rows.length > 1) hints.push({ key: "[\u2191] [\u2193]", label: "move" });
900
919
  if (multi) hints.push({ key: "[space]", label: "select" });
@@ -934,9 +953,15 @@ function SelectPrompt({
934
953
  useInput2((input, key) => {
935
954
  if (rows.length === 0) return;
936
955
  if (key.upArrow || input === "k") {
937
- setIndex((i) => (i - 1 + rows.length) % rows.length);
956
+ setIndex((i) => {
957
+ const next = (i - 1 + rows.length) % rows.length;
958
+ return isDisabled(next) ? i : next;
959
+ });
938
960
  } else if (key.downArrow || input === "j") {
939
- setIndex((i) => (i + 1) % rows.length);
961
+ setIndex((i) => {
962
+ const next = (i + 1) % rows.length;
963
+ return isDisabled(next) ? i : next;
964
+ });
940
965
  } else if (multi && input === " " && index !== cancelIndex) {
941
966
  setChecked((prev) => {
942
967
  const next = new Set(prev);
@@ -947,6 +972,7 @@ function SelectPrompt({
947
972
  } else if (key.return) {
948
973
  if (index === cancelIndex) {
949
974
  onSelect(CANCEL);
975
+ } else if (isDisabled(index)) {
950
976
  } else if (!multi) {
951
977
  onSelect(options[index]);
952
978
  } else if (checked.size > 0) {
@@ -971,10 +997,12 @@ function SelectPrompt({
971
997
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
972
998
  const sec = isCancel ? void 0 : secondary?.[i];
973
999
  const isText = sec?.kind === "text";
1000
+ const rowDisabled = isDisabled(i);
974
1001
  return /* @__PURE__ */ jsxs7(
975
1002
  SelectRow,
976
1003
  {
977
1004
  highlighted,
1005
+ disabled: rowDisabled,
978
1006
  width: isText ? "100%" : barWidth,
979
1007
  labelWidth: isText ? labelWidth : barLabelWidth,
980
1008
  label: `${bullet}${option}`,
@@ -984,7 +1012,7 @@ function SelectPrompt({
984
1012
  Text8,
985
1013
  {
986
1014
  wrap: "truncate",
987
- color: highlighted ? COLORS.primary : COLORS.muted,
1015
+ color: rowDisabled ? COLORS.dim : highlighted ? COLORS.primary : COLORS.muted,
988
1016
  children: sec.value
989
1017
  }
990
1018
  ) }),
@@ -1031,11 +1059,23 @@ function EnterToContinuePrompt({
1031
1059
  function PromptInput() {
1032
1060
  const { phase, inputReq, submitInput } = useWizard();
1033
1061
  const [draft, setDraft] = useState6("");
1062
+ const [submittedReq, setSubmittedReq] = useState6(null);
1034
1063
  const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1064
+ const submitting = inputReq != null && submittedReq === inputReq;
1035
1065
  if (phase === "done" || phase === "error") {
1036
1066
  return /* @__PURE__ */ jsx7(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text9, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1037
1067
  }
1038
1068
  if (phase !== "awaitingInput" || !inputReq) return null;
1069
+ if (submitting) {
1070
+ return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.strong, bold: true, children: [
1071
+ /* @__PURE__ */ jsx7(Spinner, { type: "dots" }),
1072
+ " Continuing\u2026"
1073
+ ] }) });
1074
+ }
1075
+ const handleSubmit = (value) => {
1076
+ setSubmittedReq(inputReq);
1077
+ submitInput(value);
1078
+ };
1039
1079
  if (inputReq.promptType === "multipleChoice") {
1040
1080
  return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
1041
1081
  SelectPrompt,
@@ -1046,10 +1086,11 @@ function PromptInput() {
1046
1086
  table: inputReq.table,
1047
1087
  options: inputReq.options,
1048
1088
  secondary: inputReq.secondary,
1089
+ disabled: inputReq.disabled,
1049
1090
  defaultSelectedIndex: inputReq.defaultSelectedIndex,
1050
1091
  cancelable: inputReq.cancelable,
1051
1092
  error: inputReq.error,
1052
- onSelect: submitInput
1093
+ onSelect: handleSubmit
1053
1094
  },
1054
1095
  promptKey
1055
1096
  ) });
@@ -1065,7 +1106,7 @@ function PromptInput() {
1065
1106
  table: inputReq.table,
1066
1107
  options: inputReq.options,
1067
1108
  error: inputReq.error,
1068
- onSelect: submitInput
1109
+ onSelect: handleSubmit
1069
1110
  },
1070
1111
  promptKey
1071
1112
  ) });
@@ -1077,7 +1118,7 @@ function PromptInput() {
1077
1118
  question: inputReq.prompt,
1078
1119
  messages: inputReq.messages,
1079
1120
  options: ["Continue"],
1080
- onSelect: submitInput
1121
+ onSelect: handleSubmit
1081
1122
  },
1082
1123
  promptKey
1083
1124
  ) });
@@ -1104,7 +1145,7 @@ function PromptInput() {
1104
1145
  messages: inputReq.messages,
1105
1146
  options: labels,
1106
1147
  secondary: inputReq.secondary,
1107
- onSelect: (value) => submitInput(value === labels[0])
1148
+ onSelect: (value) => handleSubmit(value === labels[0])
1108
1149
  },
1109
1150
  promptKey
1110
1151
  ) });
@@ -1417,6 +1458,44 @@ function trackActionEnd(ctx) {
1417
1458
  metricTags(ctx.workflowId, ctx.actionId)
1418
1459
  );
1419
1460
  }
1461
+ var MAX_ATTRIBUTE_LENGTH = 500;
1462
+ function truncate2(value) {
1463
+ return value.length <= MAX_ATTRIBUTE_LENGTH ? value : `${value.slice(0, MAX_ATTRIBUTE_LENGTH - 3)}...`;
1464
+ }
1465
+ function readString(source, key) {
1466
+ const value = Reflect.get(source, key);
1467
+ return typeof value === "string" && value.length > 0 ? value : void 0;
1468
+ }
1469
+ function causeAttributes(cause) {
1470
+ if (cause instanceof Error) {
1471
+ return {
1472
+ error_cause_name: cause.name,
1473
+ error_cause: truncate2(cause.message)
1474
+ };
1475
+ }
1476
+ return typeof cause === "string" ? { error_cause: truncate2(cause) } : {};
1477
+ }
1478
+ function providerAttributes(source) {
1479
+ const statusCode = Reflect.get(source, "statusCode");
1480
+ const isRetryable = Reflect.get(source, "isRetryable");
1481
+ const url = readString(source, "url");
1482
+ const responseBody = readString(source, "responseBody");
1483
+ return {
1484
+ ...typeof statusCode === "number" && Number.isFinite(statusCode) && { error_status_code: statusCode },
1485
+ ...typeof isRetryable === "boolean" && { error_retryable: isRetryable },
1486
+ ...url && { error_url: truncate2(url) },
1487
+ ...responseBody && { error_response_body: truncate2(responseBody) }
1488
+ };
1489
+ }
1490
+ function errorAttributes(err) {
1491
+ if (!(err instanceof Error)) return {};
1492
+ const { cause } = err;
1493
+ return {
1494
+ error_name: err.name,
1495
+ ...causeAttributes(cause),
1496
+ ...providerAttributes(cause instanceof Error ? cause : err)
1497
+ };
1498
+ }
1420
1499
  function trackActionError(ctx) {
1421
1500
  const attributes = {
1422
1501
  event: "wizard.action.error",
@@ -1424,7 +1503,8 @@ function trackActionError(ctx) {
1424
1503
  action_id: ctx.actionId,
1425
1504
  action_title: ctx.actionTitle,
1426
1505
  error: ctx.error,
1427
- app_id: ctx.appId
1506
+ app_id: ctx.appId,
1507
+ ...errorAttributes(ctx.cause)
1428
1508
  };
1429
1509
  emitTelemetryLog(
1430
1510
  "error",
@@ -1460,7 +1540,8 @@ function trackWorkflowError(ctx) {
1460
1540
  workflow_id: ctx.workflowId,
1461
1541
  action_id: ctx.actionId,
1462
1542
  error: ctx.error,
1463
- app_id: ctx.appId
1543
+ app_id: ctx.appId,
1544
+ ...errorAttributes(ctx.cause)
1464
1545
  };
1465
1546
  emitTelemetryLog(
1466
1547
  "error",
@@ -1524,6 +1605,10 @@ function reconcileWorkflowState(state, workflow) {
1524
1605
  }
1525
1606
  return state;
1526
1607
  }
1608
+ async function loadResumableState(workflow) {
1609
+ const persisted = await loadWorkflowState(workflow.id);
1610
+ return persisted ? reconcileWorkflowState(persisted, workflow) : null;
1611
+ }
1527
1612
  function initWorkflowState(workflow, now) {
1528
1613
  return {
1529
1614
  workflowId: workflow.id,
@@ -1626,11 +1711,11 @@ async function runStep(state, index, step, appId) {
1626
1711
  durationMs: Date.now() - startedAt
1627
1712
  });
1628
1713
  }
1629
- async function runWorkflow(workflow, appId) {
1714
+ async function runWorkflow(workflow, appId, resumableState) {
1630
1715
  const store = useWizard.getState();
1631
1716
  try {
1632
- const persisted = await loadWorkflowState(workflow.id);
1633
- const state = (persisted && reconcileWorkflowState(persisted, workflow)) ?? initWorkflowState(workflow, nowIso());
1717
+ const resolved2 = resumableState !== void 0 ? resumableState : await loadResumableState(workflow);
1718
+ const state = resolved2 ?? initWorkflowState(workflow, nowIso());
1634
1719
  ensureExecutedStepCount(state);
1635
1720
  store.startWorkflow(
1636
1721
  {
@@ -1686,14 +1771,16 @@ async function runWorkflow(workflow, appId) {
1686
1771
  appId,
1687
1772
  actionId: failedActionId,
1688
1773
  actionTitle: failedActionTitle,
1689
- error: message
1774
+ error: message,
1775
+ cause: err
1690
1776
  });
1691
1777
  }
1692
1778
  trackWorkflowError({
1693
1779
  workflowId: workflow.id,
1694
1780
  appId,
1695
1781
  error: message,
1696
- actionId: failedActionId
1782
+ actionId: failedActionId,
1783
+ cause: err
1697
1784
  });
1698
1785
  track("Error", {
1699
1786
  step,
@@ -1720,14 +1807,89 @@ async function listIndices() {
1720
1807
  return items.map((i) => ({ name: i.name, entries: i.entries })).sort((a, b) => a.name.localeCompare(b.name));
1721
1808
  }
1722
1809
 
1810
+ // src/lib/algoliaApp.ts
1811
+ import { z as z5 } from "zod";
1812
+ var applicationSchema = z5.object({
1813
+ id: z5.string().min(1),
1814
+ name: z5.string().default(""),
1815
+ plan: z5.string().optional()
1816
+ });
1817
+ var listSchema = z5.array(
1818
+ z5.object({
1819
+ id: z5.string().min(1),
1820
+ name: z5.string().default(""),
1821
+ plan_label: z5.string().optional(),
1822
+ status: z5.string().optional(),
1823
+ acl: z5.array(z5.string()).optional()
1824
+ }).transform(({ id, name, plan_label, status, acl }) => ({
1825
+ id,
1826
+ name,
1827
+ plan: plan_label,
1828
+ status,
1829
+ acl
1830
+ }))
1831
+ );
1832
+ function canSelectApplication(app) {
1833
+ return app.status === "active" && (app.acl ?? []).includes("keys");
1834
+ }
1835
+ async function currentApplication() {
1836
+ let raw;
1837
+ try {
1838
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1839
+ } catch {
1840
+ return null;
1841
+ }
1842
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1843
+ return parsed.success ? parsed.data : null;
1844
+ }
1845
+ async function requireApplication() {
1846
+ const app = await currentApplication();
1847
+ if (!app) {
1848
+ throw new Error(
1849
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1850
+ );
1851
+ }
1852
+ return app;
1853
+ }
1854
+ async function listApplications() {
1855
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1856
+ const parsed = listSchema.safeParse(parseJson(raw));
1857
+ if (!parsed.success) {
1858
+ throw new Error("Could not read the list of Algolia applications.");
1859
+ }
1860
+ return parsed.data;
1861
+ }
1862
+ async function selectApplication(id) {
1863
+ const raw = await runAlgoliaCli(
1864
+ ["application", "select", "--non-interactive", "--app-id", id],
1865
+ { onOutput: stderrSink }
1866
+ );
1867
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1868
+ if (!parsed.success) {
1869
+ throw new Error(
1870
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1871
+ );
1872
+ }
1873
+ return parsed.data;
1874
+ }
1875
+ function parseJson(text) {
1876
+ try {
1877
+ return JSON.parse(text);
1878
+ } catch {
1879
+ return void 0;
1880
+ }
1881
+ }
1882
+
1723
1883
  // src/actions/selectIndex.ts
1724
1884
  var CREATE_NEW_INDEX = "Create a new index\u2026";
1725
1885
  var selectIndexStep = async (ctx) => {
1886
+ await requireApplication();
1726
1887
  const indices = await listIndices();
1727
1888
  const names = indices.map((i) => i.name);
1728
1889
  const hasIndices = names.length > 0;
1729
1890
  let error;
1730
- for (; ; ) {
1891
+ let chosen;
1892
+ while (chosen === void 0) {
1731
1893
  const selection = hasIndices ? await ctx.requestUserInput({
1732
1894
  prompt: "Which index do you want to ingest into?",
1733
1895
  promptType: "multipleChoice",
@@ -1746,9 +1908,9 @@ var selectIndexStep = async (ctx) => {
1746
1908
  if (typeof selection !== "string") {
1747
1909
  throw new Error("selectIndex received an unexpected non-text result");
1748
1910
  }
1749
- let chosen;
1911
+ let candidate;
1750
1912
  if (!hasIndices) {
1751
- chosen = selection.trim();
1913
+ candidate = selection.trim();
1752
1914
  } else if (selection === CREATE_NEW_INDEX) {
1753
1915
  const name = await ctx.requestUserInput({
1754
1916
  prompt: "Name the new index:",
@@ -1758,21 +1920,28 @@ var selectIndexStep = async (ctx) => {
1758
1920
  if (typeof name !== "string") {
1759
1921
  throw new Error("selectIndex received an unexpected non-text result");
1760
1922
  }
1761
- chosen = name.trim();
1923
+ candidate = name.trim();
1762
1924
  } else {
1763
- chosen = selection;
1925
+ candidate = selection;
1764
1926
  }
1765
- if (!chosen) {
1927
+ if (!candidate) {
1766
1928
  error = "Index name cannot be empty.";
1767
1929
  continue;
1768
1930
  }
1769
- ctx.setUserInput("index", chosen);
1770
- return { selection: chosen };
1931
+ chosen = candidate;
1771
1932
  }
1933
+ ctx.setUserInput("index", chosen);
1934
+ return { selection: chosen };
1772
1935
  };
1773
1936
 
1774
1937
  // src/lib/agent.ts
1775
- import { ToolLoopAgent, hasToolCall, Output as Output3 } from "ai";
1938
+ import {
1939
+ ToolLoopAgent,
1940
+ hasToolCall,
1941
+ Output as Output3,
1942
+ APICallError,
1943
+ NoOutputGeneratedError
1944
+ } from "ai";
1776
1945
  import { createAnthropic as createAnthropic3 } from "@ai-sdk/anthropic";
1777
1946
  import "zod";
1778
1947
 
@@ -1781,7 +1950,7 @@ import "zod";
1781
1950
 
1782
1951
  // src/lib/tools/listFiles.ts
1783
1952
  import { tool } from "ai";
1784
- import z5 from "zod";
1953
+ import z6 from "zod";
1785
1954
  import { readdir } from "node:fs/promises";
1786
1955
 
1787
1956
  // src/lib/tools/path.ts
@@ -1817,8 +1986,8 @@ async function hasSymlinkParent(ctx, target) {
1817
1986
  function listFilesTool(ctx) {
1818
1987
  return tool({
1819
1988
  description: 'List files in a directory (default: the current working directory). Pass path to list a subdirectory directly \u2014 e.g. "packages/api" \u2014 without first changeDirectory-ing into it.',
1820
- inputSchema: z5.object({
1821
- path: z5.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1989
+ inputSchema: z6.object({
1990
+ path: z6.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1822
1991
  }),
1823
1992
  execute: async ({ path = "." }) => {
1824
1993
  logger.info({ path }, "called listFiles tool");
@@ -1839,13 +2008,13 @@ function listFilesTool(ctx) {
1839
2008
 
1840
2009
  // src/lib/tools/changeDirectory.ts
1841
2010
  import { tool as tool2 } from "ai";
1842
- import z6 from "zod";
2011
+ import z7 from "zod";
1843
2012
  import { stat } from "node:fs/promises";
1844
2013
  function changeDirectoryTool(ctx) {
1845
2014
  return tool2({
1846
2015
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1847
- inputSchema: z6.object({
1848
- path: z6.string().describe("Directory to change into")
2016
+ inputSchema: z7.object({
2017
+ path: z7.string().describe("Directory to change into")
1849
2018
  }),
1850
2019
  execute: async ({ path }) => {
1851
2020
  logger.info({ path }, "called changeDirectory tool");
@@ -1867,13 +2036,13 @@ function changeDirectoryTool(ctx) {
1867
2036
 
1868
2037
  // src/lib/tools/reportStatus.ts
1869
2038
  import { tool as tool3 } from "ai";
1870
- import z7 from "zod";
2039
+ import z8 from "zod";
1871
2040
  function reportStatusTool(output) {
1872
2041
  return tool3({
1873
2042
  description: "Report the status of your execution. Return a reason in case of failure.",
1874
- inputSchema: z7.object({
1875
- status: z7.enum(["success", "fail"]),
1876
- reason: z7.string().optional(),
2043
+ inputSchema: z8.object({
2044
+ status: z8.enum(["success", "fail"]),
2045
+ reason: z8.string().optional(),
1877
2046
  output
1878
2047
  }),
1879
2048
  execute: async ({ status, reason, output: output2 }) => {
@@ -1885,7 +2054,7 @@ function reportStatusTool(output) {
1885
2054
 
1886
2055
  // src/lib/tools/readFile.ts
1887
2056
  import { tool as tool4 } from "ai";
1888
- import z8 from "zod";
2057
+ import z9 from "zod";
1889
2058
  import { readFile as readFile3 } from "node:fs/promises";
1890
2059
 
1891
2060
  // src/lib/tools/env.ts
@@ -1913,8 +2082,8 @@ function redactEnvValues(content) {
1913
2082
  function readFileTool(ctx) {
1914
2083
  return tool4({
1915
2084
  description: "Read the contents of a file at the given path",
1916
- inputSchema: z8.object({
1917
- filePath: z8.string().describe("Path to the file to read")
2085
+ inputSchema: z9.object({
2086
+ filePath: z9.string().describe("Path to the file to read")
1918
2087
  }),
1919
2088
  execute: async ({ filePath }) => {
1920
2089
  if (++ctx.counts.read > ctx.limits.read) {
@@ -1935,15 +2104,15 @@ function readFileTool(ctx) {
1935
2104
 
1936
2105
  // src/lib/tools/writeFile.ts
1937
2106
  import { tool as tool5 } from "ai";
1938
- import z9 from "zod";
2107
+ import z10 from "zod";
1939
2108
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
1940
2109
  import { dirname as dirname3 } from "node:path";
1941
2110
  function writeFileTool(ctx) {
1942
2111
  return tool5({
1943
2112
  description: "Write content to a file at the given path, overwriting it. Writes to secret env files (.env, .env.local, etc.) are refused by this tool \u2014 call it anyway and the tool will tell you how to proceed.",
1944
- inputSchema: z9.object({
1945
- filePath: z9.string().describe("Path to the file to write"),
1946
- content: z9.string().describe("Content to write to the file")
2113
+ inputSchema: z10.object({
2114
+ filePath: z10.string().describe("Path to the file to write"),
2115
+ content: z10.string().describe("Content to write to the file")
1947
2116
  }),
1948
2117
  execute: async ({ filePath, content }) => {
1949
2118
  logger.info({ filePath }, "called writeFile tool");
@@ -1973,68 +2142,6 @@ import z13 from "zod";
1973
2142
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
1974
2143
  import { dirname as dirname4, relative as relative3 } from "node:path";
1975
2144
 
1976
- // src/lib/algoliaApp.ts
1977
- import { z as z10 } from "zod";
1978
- var applicationSchema = z10.object({
1979
- id: z10.string().min(1),
1980
- name: z10.string().default(""),
1981
- plan: z10.string().optional()
1982
- });
1983
- var listSchema = z10.array(
1984
- z10.object({
1985
- id: z10.string().min(1),
1986
- name: z10.string().default(""),
1987
- plan_label: z10.string().optional()
1988
- }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1989
- );
1990
- async function currentApplication() {
1991
- let raw;
1992
- try {
1993
- raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1994
- } catch {
1995
- return null;
1996
- }
1997
- const parsed = applicationSchema.safeParse(parseJson(raw));
1998
- return parsed.success ? parsed.data : null;
1999
- }
2000
- async function requireApplication() {
2001
- const app = await currentApplication();
2002
- if (!app) {
2003
- throw new Error(
2004
- "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
2005
- );
2006
- }
2007
- return app;
2008
- }
2009
- async function listApplications() {
2010
- const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
2011
- const parsed = listSchema.safeParse(parseJson(raw));
2012
- if (!parsed.success) {
2013
- throw new Error("Could not read the list of Algolia applications.");
2014
- }
2015
- return parsed.data;
2016
- }
2017
- async function selectApplication(id) {
2018
- const raw = await runAlgoliaCli(
2019
- ["application", "select", "--non-interactive", "--app-id", id],
2020
- { onOutput: stderrSink }
2021
- );
2022
- const parsed = applicationSchema.safeParse(parseJson(raw));
2023
- if (!parsed.success) {
2024
- throw new Error(
2025
- `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
2026
- );
2027
- }
2028
- return parsed.data;
2029
- }
2030
- function parseJson(text) {
2031
- try {
2032
- return JSON.parse(text);
2033
- } catch {
2034
- return void 0;
2035
- }
2036
- }
2037
-
2038
2145
  // src/lib/algoliaApiKey.ts
2039
2146
  import { z as z12 } from "zod";
2040
2147
 
@@ -2042,13 +2149,13 @@ import { z as z12 } from "zod";
2042
2149
  import { deletePassword, getPassword, setPassword } from "cross-keychain";
2043
2150
  import { z as z11 } from "zod";
2044
2151
  var SERVICE = "algolia-wizard";
2045
- var ACCOUNT = "api-keys";
2152
+ var account = (userId) => `api-keys:${userId}`;
2046
2153
  var storedKeysSchema = z11.record(z11.string(), z11.string());
2047
2154
  function entryId(kind, index, appId) {
2048
2155
  return `${kind}:${appId}:${index}`;
2049
2156
  }
2050
- async function loadKeys() {
2051
- const raw = await getPassword(SERVICE, ACCOUNT);
2157
+ async function loadKeys(userId) {
2158
+ const raw = await getPassword(SERVICE, account(userId));
2052
2159
  if (!raw) return {};
2053
2160
  let payload;
2054
2161
  try {
@@ -2070,47 +2177,47 @@ function serialized(op) {
2070
2177
  });
2071
2178
  return next;
2072
2179
  }
2073
- async function readStoredKey(kind, index, appId) {
2180
+ async function readStoredKey(kind, userId, index, appId) {
2074
2181
  try {
2075
- return (await loadKeys())[entryId(kind, index, appId)] ?? null;
2182
+ return (await loadKeys(userId))[entryId(kind, index, appId)] ?? null;
2076
2183
  } catch (err) {
2077
2184
  logger.warn(
2078
- { err: err.message, kind, index, appId },
2185
+ { err: err.message, kind, index, appId, userId },
2079
2186
  "could not read the API key from the keychain"
2080
2187
  );
2081
2188
  return null;
2082
2189
  }
2083
2190
  }
2084
- function storeKey(kind, index, appId, value) {
2191
+ function storeKey(kind, userId, index, appId, value) {
2085
2192
  return serialized(async () => {
2086
2193
  const id = entryId(kind, index, appId);
2087
2194
  try {
2088
- const keys = await loadKeys();
2195
+ const keys = await loadKeys(userId);
2089
2196
  await setPassword(
2090
2197
  SERVICE,
2091
- ACCOUNT,
2198
+ account(userId),
2092
2199
  JSON.stringify({ ...keys, [id]: value })
2093
2200
  );
2094
- if ((await loadKeys())[id] !== value) {
2201
+ if ((await loadKeys(userId))[id] !== value) {
2095
2202
  throw new Error("the keychain did not store the value");
2096
2203
  }
2097
2204
  } catch (err) {
2098
2205
  logger.warn(
2099
- { err: err.message, kind, index, appId },
2206
+ { err: err.message, kind, index, appId, userId },
2100
2207
  "could not store the API key in the keychain; the next run will create another"
2101
2208
  );
2102
2209
  }
2103
2210
  });
2104
2211
  }
2105
- function deleteStoredKeys() {
2212
+ function deleteStoredKeys(userId) {
2106
2213
  return serialized(async () => {
2107
2214
  try {
2108
- await deletePassword(SERVICE, ACCOUNT);
2215
+ await deletePassword(SERVICE, account(userId));
2109
2216
  } catch (err) {
2110
2217
  const message = err.message;
2111
2218
  if (/not found/i.test(message)) return;
2112
2219
  logger.warn(
2113
- { err: message },
2220
+ { err: message, userId },
2114
2221
  "could not delete the API keys from the keychain"
2115
2222
  );
2116
2223
  }
@@ -2118,6 +2225,16 @@ function deleteStoredKeys() {
2118
2225
  }
2119
2226
 
2120
2227
  // src/lib/algoliaApiKey.ts
2228
+ function requireUserId() {
2229
+ const userId = useWizard.getState().user?.userId;
2230
+ if (!userId) {
2231
+ throw new Error("No Algolia user is signed in; cannot scope the API key.");
2232
+ }
2233
+ return userId;
2234
+ }
2235
+ async function currentUserId() {
2236
+ return useWizard.getState().user?.userId ?? (await getUser())?.userId ?? null;
2237
+ }
2121
2238
  var WRITE_ACLS = [
2122
2239
  "addObject",
2123
2240
  "deleteObject",
@@ -2164,23 +2281,30 @@ async function keyExists(key) {
2164
2281
  var resolved = /* @__PURE__ */ new Map();
2165
2282
  async function forgetResolvedKeys() {
2166
2283
  resolved.clear();
2167
- await deleteStoredKeys();
2284
+ const userId = await currentUserId();
2285
+ if (userId) await deleteStoredKeys(userId);
2168
2286
  }
2169
2287
  function resolveKey(kind, index, appId, acls, description) {
2170
- const cacheKey = `${kind}:${appId}:${index}`;
2288
+ const userId = requireUserId();
2289
+ const cacheKey = `${userId}:${kind}:${appId}:${index}`;
2171
2290
  const cached = resolved.get(cacheKey);
2172
2291
  if (cached) return cached;
2173
- const pending = provisionKey(kind, index, appId, acls, description).catch(
2174
- (err) => {
2175
- resolved.delete(cacheKey);
2176
- throw err;
2177
- }
2178
- );
2292
+ const pending = provisionKey(
2293
+ kind,
2294
+ userId,
2295
+ index,
2296
+ appId,
2297
+ acls,
2298
+ description
2299
+ ).catch((err) => {
2300
+ resolved.delete(cacheKey);
2301
+ throw err;
2302
+ });
2179
2303
  resolved.set(cacheKey, pending);
2180
2304
  return pending;
2181
2305
  }
2182
- async function provisionKey(kind, index, appId, acls, description) {
2183
- const stored = await readStoredKey(kind, index, appId);
2306
+ async function provisionKey(kind, userId, index, appId, acls, description) {
2307
+ const stored = await readStoredKey(kind, userId, index, appId);
2184
2308
  if (stored) {
2185
2309
  if (await keyExists(stored)) {
2186
2310
  logger.info({ kind, index, appId }, "reusing the stored API key");
@@ -2192,7 +2316,7 @@ async function provisionKey(kind, index, appId, acls, description) {
2192
2316
  );
2193
2317
  }
2194
2318
  const key = await createKey(index, acls, description);
2195
- await storeKey(kind, index, appId, key);
2319
+ await storeKey(kind, userId, index, appId, key);
2196
2320
  return { key, source: "created" };
2197
2321
  }
2198
2322
  function resolveWriteKey(index, appId) {
@@ -2635,17 +2759,38 @@ function storeApproval(root) {
2635
2759
  return "approve";
2636
2760
  };
2637
2761
  }
2638
- var EXPLORATORY_COMMANDS = /* @__PURE__ */ new Set(["ls", "find", "tree", "dir"]);
2762
+ var FILE_IO_COMMANDS = /* @__PURE__ */ new Set([
2763
+ "ls",
2764
+ "find",
2765
+ "fd",
2766
+ "tree",
2767
+ "dir",
2768
+ "locate",
2769
+ "cat",
2770
+ "head",
2771
+ "tail",
2772
+ "less",
2773
+ "more",
2774
+ "tac",
2775
+ "nl",
2776
+ "grep",
2777
+ "egrep",
2778
+ "fgrep",
2779
+ "rg",
2780
+ "ack",
2781
+ "ag",
2782
+ "xxd",
2783
+ "hexdump",
2784
+ "od",
2785
+ "strings"
2786
+ ]);
2639
2787
  function commandSegments(command) {
2640
2788
  return command.split(/&&|;|\|/).map((segment) => segment.trim());
2641
2789
  }
2642
- function isExploratoryCommand(command) {
2643
- return commandSegments(command).map((segment) => segment.split(/\s+/)[0]).some((word) => word !== void 0 && EXPLORATORY_COMMANDS.has(word));
2790
+ function isFileIOCommand(command) {
2791
+ return commandSegments(command).map((segment) => segment.split(/\s+/)[0]).some((word) => word !== void 0 && FILE_IO_COMMANDS.has(word));
2644
2792
  }
2645
2793
  var READ_ONLY_BINARIES = /* @__PURE__ */ new Set([
2646
- "cat",
2647
- "head",
2648
- "tail",
2649
2794
  "wc",
2650
2795
  "pwd",
2651
2796
  "echo",
@@ -2656,10 +2801,6 @@ var READ_ONLY_BINARIES = /* @__PURE__ */ new Set([
2656
2801
  "which",
2657
2802
  "file",
2658
2803
  "stat",
2659
- "grep",
2660
- "egrep",
2661
- "fgrep",
2662
- "rg",
2663
2804
  "diff"
2664
2805
  ]);
2665
2806
  var READ_ONLY_NO_ARGS_BINARIES = /* @__PURE__ */ new Set(["env", "printenv"]);
@@ -2865,7 +3006,7 @@ async function approveAndRun(ctx, command, cwd, explanation) {
2865
3006
  }
2866
3007
  function runShellTool(ctx, createModel = defaultCreateModel) {
2867
3008
  return tool8({
2868
- description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. Do not use this to find or read files \u2014 use listFiles, searchFiles, and readFile instead of ls/find/cat/head/tail/grep/rg. This tool refuses ls/find/tree/dir outright; a read command like cat is not refused (some read-only commands run without approval, see below), but it's still the wrong tool for reading a file \u2014 the dedicated tools exist for that and won't count against this tool's command budget. The user approves any command that could change the project before it runs, so write a clear `explanation`. A command judged read-only (inspection, or running tests/typecheck/lint without an autofix flag, in any language) runs immediately without approval. If the user rejects a command, do not retry it \u2014 propose a different approach.",
3009
+ description: "Run a shell command in the project. Use this for anything the project needs done in its own ecosystem: installing dependencies, running a script you wrote, running the project's lint/typecheck/test commands. Do not use this to find or read files \u2014 use listFiles, searchFiles, and readFile instead of shell file-listing, reading, or search commands (ls/find/tree/cat/head/tail/grep/rg and similar). This tool refuses all of those outright: the dedicated tools enforce the project-root boundary and secret redaction that a raw shell read would bypass, and they won't count against this tool's command budget. The user approves any command that could change the project before it runs, so write a clear `explanation`. A command judged read-only (inspection, or running tests/typecheck/lint without an autofix flag, in any language) runs immediately without approval. If the user rejects a command, do not retry it \u2014 propose a different approach.",
2869
3010
  inputSchema: z15.object({
2870
3011
  command: z15.string().describe(
2871
3012
  "The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
@@ -2883,8 +3024,8 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
2883
3024
  }
2884
3025
  const resolved2 = resolveInRoot(ctx, cwd ?? ".");
2885
3026
  if (!resolved2.ok) return resolved2.error;
2886
- if (isExploratoryCommand(command)) {
2887
- return "Refused: use listFiles or searchFiles to find files, and readFile to read one, instead of ls/find/tree/dir.";
3027
+ if (isFileIOCommand(command)) {
3028
+ return "Refused: use listFiles or searchFiles to find files, and readFile to read one, instead of a shell file-listing, reading, or search command (ls/find/fd/tree/dir/locate/cat/head/tail/less/more/tac/nl/grep/egrep/fgrep/rg/ack/ag/xxd/hexdump/od/strings).";
2888
3029
  }
2889
3030
  logger.info({ command, cwd: resolved2.target }, "called runShell tool");
2890
3031
  const fast = fastPathSafety(command);
@@ -3139,21 +3280,31 @@ var MODEL_BY_SIZE = {
3139
3280
  var MISSING_REPORT_STATUS_ERROR_MESSAGE = "Agent finished without calling reportStatus";
3140
3281
  var MISSING_REPORT_USER_MESSAGE = "This step ran into a problem finishing. Run the wizard again to retry it.";
3141
3282
  var REPORT_STATUS_RETRIES = 2;
3283
+ var PROVIDER_ERROR_USER_MESSAGE = "The AI service had trouble responding. Run the wizard again to retry this step.";
3284
+ function retryKind(err) {
3285
+ if (err instanceof Error && err.message === MISSING_REPORT_STATUS_ERROR_MESSAGE) {
3286
+ return "missingReport";
3287
+ }
3288
+ if (NoOutputGeneratedError.isInstance(err)) return "transientProvider";
3289
+ if (APICallError.isInstance(err) && err.isRetryable) {
3290
+ return "transientProvider";
3291
+ }
3292
+ return null;
3293
+ }
3294
+ function exhaustedError(kind, err) {
3295
+ return kind === "missingReport" ? new Error(MISSING_REPORT_USER_MESSAGE) : new Error(PROVIDER_ERROR_USER_MESSAGE, { cause: err });
3296
+ }
3142
3297
  async function runAgent(req) {
3143
3298
  for (let attempt = 0; attempt <= REPORT_STATUS_RETRIES; attempt++) {
3144
3299
  try {
3145
3300
  return await runAgentAttempt(req, attempt);
3146
3301
  } catch (err) {
3147
- const isMissingReport = err instanceof Error && err.message === MISSING_REPORT_STATUS_ERROR_MESSAGE;
3148
- if (!isMissingReport) {
3149
- throw err;
3150
- }
3151
- if (attempt === REPORT_STATUS_RETRIES) {
3152
- throw new Error(MISSING_REPORT_USER_MESSAGE);
3153
- }
3302
+ const kind = retryKind(err);
3303
+ if (!kind) throw err;
3304
+ if (attempt === REPORT_STATUS_RETRIES) throw exhaustedError(kind, err);
3154
3305
  logger.warn(
3155
- { attempt: attempt + 1 },
3156
- "retrying runAgent after missing reportStatus"
3306
+ { attempt: attempt + 1, err },
3307
+ kind === "missingReport" ? "retrying runAgent after missing reportStatus" : "retrying runAgent after a transient provider error"
3157
3308
  );
3158
3309
  }
3159
3310
  }
@@ -3216,18 +3367,28 @@ async function runAgentAttempt(req, attempt) {
3216
3367
  });
3217
3368
  let chunks = [];
3218
3369
  const chunkLimit = 5;
3219
- for await (const chunk of stream.textStream) {
3370
+ let streamError;
3371
+ for await (const part of stream.fullStream) {
3372
+ if (part.type === "error") {
3373
+ streamError = part.error;
3374
+ continue;
3375
+ }
3376
+ if (part.type !== "text-delta") continue;
3220
3377
  if (chunks.length < chunkLimit) {
3221
- chunks.push(chunk);
3378
+ chunks.push(part.text);
3222
3379
  continue;
3223
3380
  }
3224
- chunks.push(chunk);
3381
+ chunks.push(part.text);
3225
3382
  logger.debug(chunks.join(""));
3226
3383
  chunks = [];
3227
3384
  }
3228
3385
  if (chunks.length) {
3229
3386
  logger.debug(chunks.join(""));
3230
3387
  }
3388
+ if (streamError !== void 0) {
3389
+ logger.error({ err: streamError }, "agent stream reported an error");
3390
+ throw streamError;
3391
+ }
3231
3392
  const end = Date.now();
3232
3393
  const usage = await stream.totalUsage;
3233
3394
  logger.info(
@@ -3387,7 +3548,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3387
3548
  // package.json
3388
3549
  var package_default = {
3389
3550
  name: "@algolia/wizard",
3390
- version: "0.40.0",
3551
+ version: "0.41.0-rc.131.290",
3391
3552
  description: "Magically implement Algolia functionality in your codebase",
3392
3553
  type: "module",
3393
3554
  engines: {
@@ -4258,6 +4419,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4258
4419
  if (useCases.includes("ingestion")) {
4259
4420
  ingestAppId = appId ?? (await requireApplication()).id;
4260
4421
  }
4422
+ let ingestWriteKey;
4423
+ if (useCases.includes("ingestion") && ingestAppId) {
4424
+ ingestWriteKey = (await resolveWriteKey(targetIndex, ingestAppId)).key;
4425
+ }
4261
4426
  let uploadFilePath;
4262
4427
  let uploadWarning;
4263
4428
  if (ingestionSource === "fileUpload") {
@@ -4320,9 +4485,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4320
4485
  let ingestDurationMs;
4321
4486
  let ingestOutcomeMessage;
4322
4487
  const ingestKeyAppId = ingestAppId;
4323
- const ingestionTools = ingestKeyAppId ? makeToolContext(repoRoot, async () => ({
4488
+ const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, async () => ({
4324
4489
  [APP_ID_VAR]: ingestKeyAppId,
4325
- [API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
4490
+ [API_KEY_VAR]: ingestWriteKey,
4326
4491
  [INDEX_NAME_VAR]: targetIndex
4327
4492
  })) : void 0;
4328
4493
  const searchTools = makeToolContext(repoRoot);
@@ -4917,13 +5082,13 @@ import { Box as Box15, Text as Text15 } from "ink";
4917
5082
 
4918
5083
  // src/ui/Steps.tsx
4919
5084
  import { Box as Box13, Text as Text13 } from "ink";
4920
- import Spinner from "ink-spinner";
5085
+ import Spinner2 from "ink-spinner";
4921
5086
  import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
4922
5087
  function Steps() {
4923
5088
  const { steps } = useWizard();
4924
5089
  const visibleSteps = steps.filter(isStepVisible);
4925
5090
  return /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", gap: 1, children: visibleSteps.map((s) => /* @__PURE__ */ jsx11(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status[s.status], children: [
4926
- s.status === "running" ? /* @__PURE__ */ jsx11(Spinner, { type: "dots" }) : MARKER[s.status],
5091
+ s.status === "running" ? /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) : MARKER[s.status],
4927
5092
  " ",
4928
5093
  s.title
4929
5094
  ] }) }, s.id)) });
@@ -4933,7 +5098,7 @@ function CurrentStep() {
4933
5098
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
4934
5099
  if (!currentStep) return null;
4935
5100
  return /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.running, children: [
4936
- /* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
5101
+ /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
4937
5102
  " ",
4938
5103
  ` ${currentStep.title}`
4939
5104
  ] });
@@ -5036,7 +5201,7 @@ function logNameColor(entry) {
5036
5201
  return STATUS_COLOR[entry.status] ?? KIND_COLOR[entry.kind];
5037
5202
  }
5038
5203
  var ROW_GAP = 1;
5039
- function truncate2(str, maxWidth) {
5204
+ function truncate3(str, maxWidth) {
5040
5205
  if (maxWidth <= 0) return "";
5041
5206
  return str.length > maxWidth ? `${str.slice(0, maxWidth - 1)}\u2026` : str;
5042
5207
  }
@@ -5068,9 +5233,9 @@ function Logs() {
5068
5233
  const partCount = 2 + (rawPreview ? 1 : 0) + (durationText ? 1 : 0);
5069
5234
  const gaps = (partCount - 1) * ROW_GAP;
5070
5235
  let budget = scroll.width - timestamp.length - durationText.length - gaps;
5071
- const name = truncate2(entry.name, budget);
5236
+ const name = truncate3(entry.name, budget);
5072
5237
  budget -= name.length;
5073
- const preview = rawPreview ? truncate2(rawPreview, budget) : "";
5238
+ const preview = rawPreview ? truncate3(rawPreview, budget) : "";
5074
5239
  return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", gap: ROW_GAP, children: [
5075
5240
  /* @__PURE__ */ jsx15(Text17, { color: COLORS.dim, children: timestamp }),
5076
5241
  /* @__PURE__ */ jsx15(Text17, { color: logNameColor(entry), wrap: "truncate", children: name }),
@@ -5478,6 +5643,7 @@ function App() {
5478
5643
  inputReq,
5479
5644
  user,
5480
5645
  workflow,
5646
+ settingUpAppId,
5481
5647
  review: review2
5482
5648
  } = useWizard();
5483
5649
  const { exit } = useApp();
@@ -5493,7 +5659,7 @@ function App() {
5493
5659
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
5494
5660
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
5495
5661
  const holdForTip = stepHasTips && promptPending && tipState === "revealing";
5496
- const showTips = stepHasTips && (phase === "running" || isCommandApprovalPrompt || holdForTip);
5662
+ const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
5497
5663
  const showNoticesInMain = !stepHasTips;
5498
5664
  const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
5499
5665
  useInput7(
@@ -5564,20 +5730,26 @@ function App() {
5564
5730
  children: [
5565
5731
  /* @__PURE__ */ jsxs18(Box20, { flexGrow: 2, flexDirection: "column", children: [
5566
5732
  phase === "preflight" && !user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5567
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5733
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5568
5734
  " Signing in to Algolia"
5569
5735
  ] }) }),
5570
5736
  phase === "preflight" && user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5571
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5737
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5572
5738
  " Getting things ready"
5573
5739
  ] }) }),
5574
5740
  phase === "authenticating" && /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", marginBottom: 1, children: [
5575
5741
  /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5576
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5742
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5577
5743
  " Signing in to Algolia"
5578
5744
  ] }),
5579
5745
  /* @__PURE__ */ jsx18(Text20, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
5580
5746
  ] }),
5747
+ settingUpAppId && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5748
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5749
+ " Setting up Wizard using app",
5750
+ " ",
5751
+ settingUpAppId
5752
+ ] }) }),
5581
5753
  /* @__PURE__ */ jsx18(CliOutput, {}),
5582
5754
  showTips && currentStep && /* @__PURE__ */ jsx18(
5583
5755
  Tips,
@@ -5648,50 +5820,72 @@ function readValue(raw) {
5648
5820
  }
5649
5821
 
5650
5822
  // src/lib/algoliaAppPicker.ts
5651
- function secondaryFor(app) {
5823
+ function blockReasonFor(app) {
5824
+ return app.status !== "active" ? "Paused" : "Missing permissions";
5825
+ }
5826
+ function secondaryFor(app, highlightId) {
5827
+ if (!canSelectApplication(app)) {
5828
+ return { kind: "text", value: blockReasonFor(app) };
5829
+ }
5830
+ if (app.id === highlightId) {
5831
+ return { kind: "badge", value: "[DETECTED]" };
5832
+ }
5652
5833
  return app.plan ? { kind: "badge", value: app.plan } : void 0;
5653
5834
  }
5654
5835
  function labelFor(app) {
5655
5836
  return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
5656
5837
  }
5657
- function selectAndReport(app) {
5658
- useWizard.getState().pushCliOutput(
5659
- "stdout",
5660
- `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
5661
- );
5662
- return selectApplication(app.id);
5838
+ async function isEligible(app) {
5839
+ const apps = await listApplications();
5840
+ const full = apps.find((candidate) => candidate.id === app.id);
5841
+ return full != null && canSelectApplication(full);
5663
5842
  }
5664
- async function promptForApplication(leadIn = []) {
5843
+ async function selectAndReport(app) {
5665
5844
  const store = useWizard.getState();
5666
- const apps = await listApplications();
5667
- if (apps.length === 0) {
5845
+ store.setSettingUpApp(app.id);
5846
+ try {
5847
+ return await selectApplication(app.id);
5848
+ } finally {
5849
+ store.setSettingUpApp(null);
5850
+ }
5851
+ }
5852
+ async function promptForApplication(highlight) {
5853
+ const store = useWizard.getState();
5854
+ const unordered = await listApplications();
5855
+ if (unordered.length === 0) {
5668
5856
  throw new Error(
5669
5857
  "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
5670
5858
  );
5671
5859
  }
5672
- if (apps.length === 1) {
5673
- const only = apps[0];
5674
- logger.info(
5675
- { app: only.id },
5676
- "single application on the account; selecting it"
5860
+ if (!unordered.some(canSelectApplication)) {
5861
+ throw new Error(
5862
+ "None of the applications on this Algolia account are usable \u2014 each is either paused or missing key-management access. Activate one, or grant it the `keys` ACL, then restart the wizard."
5677
5863
  );
5678
- for (const line of leadIn) store.pushCliOutput("stdout", line);
5679
- return selectAndReport(only);
5680
5864
  }
5681
- const messages = [
5682
- ...leadIn,
5683
- "Which Algolia application should the wizard work in?"
5865
+ const apps = [
5866
+ ...unordered.filter(canSelectApplication),
5867
+ ...unordered.filter((app) => !canSelectApplication(app))
5684
5868
  ];
5869
+ const envApp = highlight ? unordered.find((app) => app.id === highlight.id) : void 0;
5870
+ const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
5871
+ const messages = [];
5872
+ if (highlight && highlightIndex < 0) {
5873
+ messages.push(
5874
+ envApp ? `Could not use ${highlight.id} from ${highlight.file} \u2014 it\u2019s ${blockReasonFor(envApp).toLowerCase()}. Pick another below.` : `Could not use ${highlight.id} from ${highlight.file} \u2014 it may have been removed, or this account may not have access to it.`
5875
+ );
5876
+ }
5685
5877
  for (; ; ) {
5686
5878
  const choice = await store.requestUserInput({
5687
- prompt: "Select an application",
5879
+ prompt: "Which Algolia application should the wizard work in?",
5688
5880
  promptType: "multipleChoice",
5689
5881
  options: apps.map(labelFor),
5690
- secondary: apps.map(secondaryFor),
5882
+ secondary: apps.map((app) => secondaryFor(app, highlight?.id)),
5883
+ disabled: apps.map((app) => !canSelectApplication(app)),
5884
+ ...highlightIndex >= 0 ? { defaultSelectedIndex: highlightIndex } : {},
5691
5885
  messages
5692
5886
  });
5693
5887
  const chosen = apps.find((app) => labelFor(app) === choice);
5694
- if (!chosen) {
5888
+ if (!chosen || !canSelectApplication(chosen)) {
5695
5889
  throw new Error("Application picker received an unexpected selection");
5696
5890
  }
5697
5891
  try {
@@ -5707,42 +5901,15 @@ async function promptForApplication(leadIn = []) {
5707
5901
  }
5708
5902
  }
5709
5903
  }
5710
- async function confirmEnvApplication(env, current) {
5711
- const useEnv = `Use ${env.id} (from ${env.file})`;
5712
- const choice = await useWizard.getState().requestUserInput({
5713
- prompt: "Select an application",
5714
- promptType: "multipleChoice",
5715
- options: [
5716
- useEnv,
5717
- current ? `Use ${labelFor(current)} (already selected)` : "Pick a different application"
5718
- ],
5719
- messages: [
5720
- `${env.file} already sets ${env.name}=${env.id}.`,
5721
- "Which Algolia application should the wizard work in?"
5722
- ]
5723
- });
5724
- return choice === useEnv;
5725
- }
5726
- async function selectEnvApplication(env) {
5727
- try {
5728
- return await selectAndReport({ id: env.id, name: "" });
5729
- } catch (err) {
5730
- logger.warn(
5731
- { app: env.id, err: err.message },
5732
- "could not select the application named in env; falling back to the picker"
5733
- );
5734
- return promptForApplication([
5735
- `Could not select ${env.id} from ${env.file} \u2014 it may have been removed, or this account may not have access to it.`
5736
- ]);
5737
- }
5738
- }
5739
- async function ensureApplication() {
5904
+ async function ensureApplication(isResuming) {
5740
5905
  const current = await currentApplication();
5741
- const env = await findEnvApplicationId();
5742
- if (env && env.id !== current?.id && await confirmEnvApplication(env, current)) {
5743
- return selectEnvApplication(env);
5906
+ if (isResuming) {
5907
+ if (current && await isEligible(current)) return current;
5908
+ return promptForApplication();
5744
5909
  }
5745
- return current ?? await promptForApplication();
5910
+ const env = await findEnvApplicationId();
5911
+ if (!env) return promptForApplication();
5912
+ return promptForApplication({ id: env.id, file: env.file });
5746
5913
  }
5747
5914
 
5748
5915
  // src/lib/seed.ts
@@ -6106,14 +6273,15 @@ async function run(workflow) {
6106
6273
  }
6107
6274
  store.setUser(user);
6108
6275
  let app;
6276
+ const resumableState = await loadResumableState(workflow);
6109
6277
  try {
6110
- app = await ensureApplication();
6278
+ app = await ensureApplication(resumableState != null);
6111
6279
  } catch (err) {
6112
6280
  store.setError(err instanceof Error ? err.message : String(err));
6113
6281
  await instance.waitUntilExit();
6114
6282
  process.exit(1);
6115
6283
  }
6116
- runWorkflow(workflow, app.id);
6284
+ runWorkflow(workflow, app.id, resumableState);
6117
6285
  }
6118
6286
  await requestTerminalSize();
6119
6287
  var started = await startup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.40.0",
3
+ "version": "0.41.0-rc.131.290",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {