@algolia/wizard 0.46.0 → 0.48.0-rc.141.344

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/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,10 +953,10 @@ 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) => nextSelectableIndex(i, -1, rows.length, isDisabled));
938
957
  } else if (key.downArrow || input === "j") {
939
- setIndex((i) => (i + 1) % rows.length);
940
- } else if (multi && input === " " && index !== cancelIndex) {
958
+ setIndex((i) => nextSelectableIndex(i, 1, rows.length, isDisabled));
959
+ } else if (multi && input === " " && index !== cancelIndex && !isDisabled(index)) {
941
960
  setChecked((prev) => {
942
961
  const next = new Set(prev);
943
962
  if (next.has(index)) next.delete(index);
@@ -947,10 +966,12 @@ function SelectPrompt({
947
966
  } else if (key.return) {
948
967
  if (index === cancelIndex) {
949
968
  onSelect(CANCEL);
969
+ } else if (isDisabled(index)) {
950
970
  } else if (!multi) {
951
971
  onSelect(options[index]);
952
- } else if (checked.size > 0) {
953
- onSelect(options.filter((_, i) => checked.has(i)));
972
+ } else {
973
+ const picked = options.filter((_, i) => checked.has(i) && !isDisabled(i));
974
+ if (picked.length > 0) onSelect(picked);
954
975
  }
955
976
  }
956
977
  });
@@ -971,10 +992,12 @@ function SelectPrompt({
971
992
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
972
993
  const sec = isCancel ? void 0 : secondary?.[i];
973
994
  const isText = sec?.kind === "text";
995
+ const rowDisabled = isDisabled(i);
974
996
  return /* @__PURE__ */ jsxs7(
975
997
  SelectRow,
976
998
  {
977
999
  highlighted,
1000
+ disabled: rowDisabled,
978
1001
  width: isText ? "100%" : barWidth,
979
1002
  labelWidth: isText ? labelWidth : barLabelWidth,
980
1003
  label: `${bullet}${option}`,
@@ -984,7 +1007,7 @@ function SelectPrompt({
984
1007
  Text8,
985
1008
  {
986
1009
  wrap: "truncate",
987
- color: highlighted ? COLORS.primary : COLORS.muted,
1010
+ color: rowDisabled ? COLORS.dim : highlighted ? COLORS.primary : COLORS.muted,
988
1011
  children: sec.value
989
1012
  }
990
1013
  ) }),
@@ -1031,11 +1054,23 @@ function EnterToContinuePrompt({
1031
1054
  function PromptInput() {
1032
1055
  const { phase, inputReq, submitInput } = useWizard();
1033
1056
  const [draft, setDraft] = useState6("");
1057
+ const [submittedReq, setSubmittedReq] = useState6(null);
1034
1058
  const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1059
+ const submitting = inputReq != null && submittedReq === inputReq;
1035
1060
  if (phase === "done" || phase === "error") {
1036
1061
  return /* @__PURE__ */ jsx7(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text9, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1037
1062
  }
1038
1063
  if (phase !== "awaitingInput" || !inputReq) return null;
1064
+ if (submitting) {
1065
+ return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsxs8(Text9, { color: COLORS.strong, bold: true, children: [
1066
+ /* @__PURE__ */ jsx7(Spinner, { type: "dots" }),
1067
+ " Continuing\u2026"
1068
+ ] }) });
1069
+ }
1070
+ const handleSubmit = (value) => {
1071
+ setSubmittedReq(inputReq);
1072
+ submitInput(value);
1073
+ };
1039
1074
  if (inputReq.promptType === "multipleChoice") {
1040
1075
  return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
1041
1076
  SelectPrompt,
@@ -1046,10 +1081,11 @@ function PromptInput() {
1046
1081
  table: inputReq.table,
1047
1082
  options: inputReq.options,
1048
1083
  secondary: inputReq.secondary,
1084
+ disabled: inputReq.disabled,
1049
1085
  defaultSelectedIndex: inputReq.defaultSelectedIndex,
1050
1086
  cancelable: inputReq.cancelable,
1051
1087
  error: inputReq.error,
1052
- onSelect: submitInput
1088
+ onSelect: handleSubmit
1053
1089
  },
1054
1090
  promptKey
1055
1091
  ) });
@@ -1065,7 +1101,7 @@ function PromptInput() {
1065
1101
  table: inputReq.table,
1066
1102
  options: inputReq.options,
1067
1103
  error: inputReq.error,
1068
- onSelect: submitInput
1104
+ onSelect: handleSubmit
1069
1105
  },
1070
1106
  promptKey
1071
1107
  ) });
@@ -1077,7 +1113,7 @@ function PromptInput() {
1077
1113
  question: inputReq.prompt,
1078
1114
  messages: inputReq.messages,
1079
1115
  options: ["Continue"],
1080
- onSelect: submitInput
1116
+ onSelect: handleSubmit
1081
1117
  },
1082
1118
  promptKey
1083
1119
  ) });
@@ -1104,7 +1140,7 @@ function PromptInput() {
1104
1140
  messages: inputReq.messages,
1105
1141
  options: labels,
1106
1142
  secondary: inputReq.secondary,
1107
- onSelect: (value) => submitInput(value === labels[0])
1143
+ onSelect: (value) => handleSubmit(value === labels[0])
1108
1144
  },
1109
1145
  promptKey
1110
1146
  ) });
@@ -1285,6 +1321,10 @@ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, rm } f
1285
1321
  import { join as join4 } from "node:path";
1286
1322
  var isStepVisible = (s) => s.visible !== false;
1287
1323
  var stateFile = (workflowId) => join4(stateDir(), `state-${workflowId}.json`);
1324
+ function runAppId(state) {
1325
+ const appId = state.algoliaState.appId;
1326
+ return typeof appId === "string" && appId ? appId : void 0;
1327
+ }
1288
1328
  async function loadWorkflowState(workflowId) {
1289
1329
  try {
1290
1330
  const raw = await readFile2(stateFile(workflowId), "utf8");
@@ -1564,7 +1604,11 @@ function reconcileWorkflowState(state, workflow) {
1564
1604
  }
1565
1605
  return state;
1566
1606
  }
1567
- function initWorkflowState(workflow, now) {
1607
+ async function loadResumableState(workflow) {
1608
+ const persisted = await loadWorkflowState(workflow.id);
1609
+ return persisted ? reconcileWorkflowState(persisted, workflow) : null;
1610
+ }
1611
+ function initWorkflowState(workflow, now, appId) {
1568
1612
  return {
1569
1613
  workflowId: workflow.id,
1570
1614
  startedAt: now,
@@ -1576,7 +1620,7 @@ function initWorkflowState(workflow, now) {
1576
1620
  visible: s.visible,
1577
1621
  status: "pending"
1578
1622
  })),
1579
- algoliaState: {},
1623
+ algoliaState: appId ? { appId } : {},
1580
1624
  userInputs: {}
1581
1625
  };
1582
1626
  }
@@ -1666,11 +1710,11 @@ async function runStep(state, index, step, appId) {
1666
1710
  durationMs: Date.now() - startedAt
1667
1711
  });
1668
1712
  }
1669
- async function runWorkflow(workflow, appId) {
1713
+ async function runWorkflow(workflow, appId, resumableState) {
1670
1714
  const store = useWizard.getState();
1671
1715
  try {
1672
- const persisted = await loadWorkflowState(workflow.id);
1673
- const state = (persisted && reconcileWorkflowState(persisted, workflow)) ?? initWorkflowState(workflow, nowIso());
1716
+ const resolved2 = resumableState !== void 0 ? resumableState : await loadResumableState(workflow);
1717
+ const state = resolved2 ?? initWorkflowState(workflow, nowIso(), appId);
1674
1718
  ensureExecutedStepCount(state);
1675
1719
  store.startWorkflow(
1676
1720
  {
@@ -1762,14 +1806,89 @@ async function listIndices() {
1762
1806
  return items.map((i) => ({ name: i.name, entries: i.entries })).sort((a, b) => a.name.localeCompare(b.name));
1763
1807
  }
1764
1808
 
1809
+ // src/lib/algoliaApp.ts
1810
+ import { z as z5 } from "zod";
1811
+ var applicationSchema = z5.object({
1812
+ id: z5.string().min(1),
1813
+ name: z5.string().default(""),
1814
+ plan: z5.string().optional()
1815
+ });
1816
+ var listSchema = z5.array(
1817
+ z5.object({
1818
+ id: z5.string().min(1),
1819
+ name: z5.string().default(""),
1820
+ plan_label: z5.string().optional(),
1821
+ status: z5.string().optional(),
1822
+ acl: z5.array(z5.string()).optional()
1823
+ }).transform(({ id, name, plan_label, status, acl }) => ({
1824
+ id,
1825
+ name,
1826
+ plan: plan_label,
1827
+ status,
1828
+ acl
1829
+ }))
1830
+ );
1831
+ function canSelectApplication(app) {
1832
+ return app.status === "active" && (app.acl ?? []).includes("keys");
1833
+ }
1834
+ async function currentApplication() {
1835
+ let raw;
1836
+ try {
1837
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1838
+ } catch {
1839
+ return null;
1840
+ }
1841
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1842
+ return parsed.success ? parsed.data : null;
1843
+ }
1844
+ async function requireApplication() {
1845
+ const app = await currentApplication();
1846
+ if (!app) {
1847
+ throw new Error(
1848
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1849
+ );
1850
+ }
1851
+ return app;
1852
+ }
1853
+ async function listApplications() {
1854
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1855
+ const parsed = listSchema.safeParse(parseJson(raw));
1856
+ if (!parsed.success) {
1857
+ throw new Error("Could not read the list of Algolia applications.");
1858
+ }
1859
+ return parsed.data;
1860
+ }
1861
+ async function selectApplication(id) {
1862
+ const raw = await runAlgoliaCli(
1863
+ ["application", "select", "--non-interactive", "--app-id", id],
1864
+ { onOutput: stderrSink }
1865
+ );
1866
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1867
+ if (!parsed.success) {
1868
+ throw new Error(
1869
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1870
+ );
1871
+ }
1872
+ return parsed.data;
1873
+ }
1874
+ function parseJson(text) {
1875
+ try {
1876
+ return JSON.parse(text);
1877
+ } catch {
1878
+ return void 0;
1879
+ }
1880
+ }
1881
+
1765
1882
  // src/actions/selectIndex.ts
1766
1883
  var CREATE_NEW_INDEX = "Create a new index\u2026";
1767
1884
  var selectIndexStep = async (ctx) => {
1885
+ await requireApplication();
1768
1886
  const indices = await listIndices();
1769
1887
  const names = indices.map((i) => i.name);
1770
1888
  const hasIndices = names.length > 0;
1771
1889
  let error;
1772
- for (; ; ) {
1890
+ let chosen;
1891
+ while (chosen === void 0) {
1773
1892
  const selection = hasIndices ? await ctx.requestUserInput({
1774
1893
  prompt: "Which index do you want to ingest into?",
1775
1894
  promptType: "multipleChoice",
@@ -1788,9 +1907,9 @@ var selectIndexStep = async (ctx) => {
1788
1907
  if (typeof selection !== "string") {
1789
1908
  throw new Error("selectIndex received an unexpected non-text result");
1790
1909
  }
1791
- let chosen;
1910
+ let candidate;
1792
1911
  if (!hasIndices) {
1793
- chosen = selection.trim();
1912
+ candidate = selection.trim();
1794
1913
  } else if (selection === CREATE_NEW_INDEX) {
1795
1914
  const name = await ctx.requestUserInput({
1796
1915
  prompt: "Name the new index:",
@@ -1800,17 +1919,18 @@ var selectIndexStep = async (ctx) => {
1800
1919
  if (typeof name !== "string") {
1801
1920
  throw new Error("selectIndex received an unexpected non-text result");
1802
1921
  }
1803
- chosen = name.trim();
1922
+ candidate = name.trim();
1804
1923
  } else {
1805
- chosen = selection;
1924
+ candidate = selection;
1806
1925
  }
1807
- if (!chosen) {
1926
+ if (!candidate) {
1808
1927
  error = "Index name cannot be empty.";
1809
1928
  continue;
1810
1929
  }
1811
- ctx.setUserInput("index", chosen);
1812
- return { selection: chosen };
1930
+ chosen = candidate;
1813
1931
  }
1932
+ ctx.setUserInput("index", chosen);
1933
+ return { selection: chosen };
1814
1934
  };
1815
1935
 
1816
1936
  // src/lib/agent.ts
@@ -1829,7 +1949,7 @@ import "zod";
1829
1949
 
1830
1950
  // src/lib/tools/listFiles.ts
1831
1951
  import { tool } from "ai";
1832
- import z5 from "zod";
1952
+ import z6 from "zod";
1833
1953
  import { readdir } from "node:fs/promises";
1834
1954
 
1835
1955
  // src/lib/tools/path.ts
@@ -1865,8 +1985,8 @@ async function hasSymlinkParent(ctx, target) {
1865
1985
  function listFilesTool(ctx) {
1866
1986
  return tool({
1867
1987
  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.',
1868
- inputSchema: z5.object({
1869
- path: z5.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1988
+ inputSchema: z6.object({
1989
+ path: z6.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1870
1990
  }),
1871
1991
  execute: async ({ path = "." }) => {
1872
1992
  logger.info({ path }, "called listFiles tool");
@@ -1887,13 +2007,13 @@ function listFilesTool(ctx) {
1887
2007
 
1888
2008
  // src/lib/tools/changeDirectory.ts
1889
2009
  import { tool as tool2 } from "ai";
1890
- import z6 from "zod";
2010
+ import z7 from "zod";
1891
2011
  import { stat } from "node:fs/promises";
1892
2012
  function changeDirectoryTool(ctx) {
1893
2013
  return tool2({
1894
2014
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1895
- inputSchema: z6.object({
1896
- path: z6.string().describe("Directory to change into")
2015
+ inputSchema: z7.object({
2016
+ path: z7.string().describe("Directory to change into")
1897
2017
  }),
1898
2018
  execute: async ({ path }) => {
1899
2019
  logger.info({ path }, "called changeDirectory tool");
@@ -1915,13 +2035,13 @@ function changeDirectoryTool(ctx) {
1915
2035
 
1916
2036
  // src/lib/tools/reportStatus.ts
1917
2037
  import { tool as tool3 } from "ai";
1918
- import z7 from "zod";
2038
+ import z8 from "zod";
1919
2039
  function reportStatusTool(output) {
1920
2040
  return tool3({
1921
2041
  description: "Report the status of your execution. Return a reason in case of failure.",
1922
- inputSchema: z7.object({
1923
- status: z7.enum(["success", "fail"]),
1924
- reason: z7.string().optional(),
2042
+ inputSchema: z8.object({
2043
+ status: z8.enum(["success", "fail"]),
2044
+ reason: z8.string().optional(),
1925
2045
  output
1926
2046
  }),
1927
2047
  execute: async ({ status, reason, output: output2 }) => {
@@ -1933,7 +2053,7 @@ function reportStatusTool(output) {
1933
2053
 
1934
2054
  // src/lib/tools/readFile.ts
1935
2055
  import { tool as tool4 } from "ai";
1936
- import z8 from "zod";
2056
+ import z9 from "zod";
1937
2057
  import { readFile as readFile3 } from "node:fs/promises";
1938
2058
 
1939
2059
  // src/lib/tools/env.ts
@@ -1961,8 +2081,8 @@ function redactEnvValues(content) {
1961
2081
  function readFileTool(ctx) {
1962
2082
  return tool4({
1963
2083
  description: "Read the contents of a file at the given path",
1964
- inputSchema: z8.object({
1965
- filePath: z8.string().describe("Path to the file to read")
2084
+ inputSchema: z9.object({
2085
+ filePath: z9.string().describe("Path to the file to read")
1966
2086
  }),
1967
2087
  execute: async ({ filePath }) => {
1968
2088
  if (++ctx.counts.read > ctx.limits.read) {
@@ -1983,15 +2103,15 @@ function readFileTool(ctx) {
1983
2103
 
1984
2104
  // src/lib/tools/writeFile.ts
1985
2105
  import { tool as tool5 } from "ai";
1986
- import z9 from "zod";
2106
+ import z10 from "zod";
1987
2107
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
1988
2108
  import { dirname as dirname3 } from "node:path";
1989
2109
  function writeFileTool(ctx) {
1990
2110
  return tool5({
1991
2111
  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.",
1992
- inputSchema: z9.object({
1993
- filePath: z9.string().describe("Path to the file to write"),
1994
- content: z9.string().describe("Content to write to the file")
2112
+ inputSchema: z10.object({
2113
+ filePath: z10.string().describe("Path to the file to write"),
2114
+ content: z10.string().describe("Content to write to the file")
1995
2115
  }),
1996
2116
  execute: async ({ filePath, content }) => {
1997
2117
  logger.info({ filePath }, "called writeFile tool");
@@ -2021,68 +2141,6 @@ import z13 from "zod";
2021
2141
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
2022
2142
  import { dirname as dirname4, relative as relative3 } from "node:path";
2023
2143
 
2024
- // src/lib/algoliaApp.ts
2025
- import { z as z10 } from "zod";
2026
- var applicationSchema = z10.object({
2027
- id: z10.string().min(1),
2028
- name: z10.string().default(""),
2029
- plan: z10.string().optional()
2030
- });
2031
- var listSchema = z10.array(
2032
- z10.object({
2033
- id: z10.string().min(1),
2034
- name: z10.string().default(""),
2035
- plan_label: z10.string().optional()
2036
- }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
2037
- );
2038
- async function currentApplication() {
2039
- let raw;
2040
- try {
2041
- raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
2042
- } catch {
2043
- return null;
2044
- }
2045
- const parsed = applicationSchema.safeParse(parseJson(raw));
2046
- return parsed.success ? parsed.data : null;
2047
- }
2048
- async function requireApplication() {
2049
- const app = await currentApplication();
2050
- if (!app) {
2051
- throw new Error(
2052
- "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
2053
- );
2054
- }
2055
- return app;
2056
- }
2057
- async function listApplications() {
2058
- const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
2059
- const parsed = listSchema.safeParse(parseJson(raw));
2060
- if (!parsed.success) {
2061
- throw new Error("Could not read the list of Algolia applications.");
2062
- }
2063
- return parsed.data;
2064
- }
2065
- async function selectApplication(id) {
2066
- const raw = await runAlgoliaCli(
2067
- ["application", "select", "--non-interactive", "--app-id", id],
2068
- { onOutput: stderrSink }
2069
- );
2070
- const parsed = applicationSchema.safeParse(parseJson(raw));
2071
- if (!parsed.success) {
2072
- throw new Error(
2073
- `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
2074
- );
2075
- }
2076
- return parsed.data;
2077
- }
2078
- function parseJson(text) {
2079
- try {
2080
- return JSON.parse(text);
2081
- } catch {
2082
- return void 0;
2083
- }
2084
- }
2085
-
2086
2144
  // src/lib/algoliaApiKey.ts
2087
2145
  import { z as z12 } from "zod";
2088
2146
 
@@ -2090,13 +2148,13 @@ import { z as z12 } from "zod";
2090
2148
  import { deletePassword, getPassword, setPassword } from "cross-keychain";
2091
2149
  import { z as z11 } from "zod";
2092
2150
  var SERVICE = "algolia-wizard";
2093
- var ACCOUNT = "api-keys";
2151
+ var account = (userId) => `api-keys:${userId}`;
2094
2152
  var storedKeysSchema = z11.record(z11.string(), z11.string());
2095
2153
  function entryId(kind, index, appId) {
2096
2154
  return `${kind}:${appId}:${index}`;
2097
2155
  }
2098
- async function loadKeys() {
2099
- const raw = await getPassword(SERVICE, ACCOUNT);
2156
+ async function loadKeys(userId) {
2157
+ const raw = await getPassword(SERVICE, account(userId));
2100
2158
  if (!raw) return {};
2101
2159
  let payload;
2102
2160
  try {
@@ -2118,47 +2176,47 @@ function serialized(op) {
2118
2176
  });
2119
2177
  return next;
2120
2178
  }
2121
- async function readStoredKey(kind, index, appId) {
2179
+ async function readStoredKey(kind, userId, index, appId) {
2122
2180
  try {
2123
- return (await loadKeys())[entryId(kind, index, appId)] ?? null;
2181
+ return (await loadKeys(userId))[entryId(kind, index, appId)] ?? null;
2124
2182
  } catch (err) {
2125
2183
  logger.warn(
2126
- { err: err.message, kind, index, appId },
2184
+ { err: err.message, kind, index, appId, userId },
2127
2185
  "could not read the API key from the keychain"
2128
2186
  );
2129
2187
  return null;
2130
2188
  }
2131
2189
  }
2132
- function storeKey(kind, index, appId, value) {
2190
+ function storeKey(kind, userId, index, appId, value) {
2133
2191
  return serialized(async () => {
2134
2192
  const id = entryId(kind, index, appId);
2135
2193
  try {
2136
- const keys = await loadKeys();
2194
+ const keys = await loadKeys(userId);
2137
2195
  await setPassword(
2138
2196
  SERVICE,
2139
- ACCOUNT,
2197
+ account(userId),
2140
2198
  JSON.stringify({ ...keys, [id]: value })
2141
2199
  );
2142
- if ((await loadKeys())[id] !== value) {
2200
+ if ((await loadKeys(userId))[id] !== value) {
2143
2201
  throw new Error("the keychain did not store the value");
2144
2202
  }
2145
2203
  } catch (err) {
2146
2204
  logger.warn(
2147
- { err: err.message, kind, index, appId },
2205
+ { err: err.message, kind, index, appId, userId },
2148
2206
  "could not store the API key in the keychain; the next run will create another"
2149
2207
  );
2150
2208
  }
2151
2209
  });
2152
2210
  }
2153
- function deleteStoredKeys() {
2211
+ function deleteStoredKeys(userId) {
2154
2212
  return serialized(async () => {
2155
2213
  try {
2156
- await deletePassword(SERVICE, ACCOUNT);
2214
+ await deletePassword(SERVICE, account(userId));
2157
2215
  } catch (err) {
2158
2216
  const message = err.message;
2159
2217
  if (/not found/i.test(message)) return;
2160
2218
  logger.warn(
2161
- { err: message },
2219
+ { err: message, userId },
2162
2220
  "could not delete the API keys from the keychain"
2163
2221
  );
2164
2222
  }
@@ -2166,6 +2224,16 @@ function deleteStoredKeys() {
2166
2224
  }
2167
2225
 
2168
2226
  // src/lib/algoliaApiKey.ts
2227
+ function requireUserId() {
2228
+ const userId = useWizard.getState().user?.userId;
2229
+ if (!userId) {
2230
+ throw new Error("No Algolia user is signed in; cannot scope the API key.");
2231
+ }
2232
+ return userId;
2233
+ }
2234
+ async function currentUserId() {
2235
+ return useWizard.getState().user?.userId ?? (await getUser())?.userId ?? null;
2236
+ }
2169
2237
  var WRITE_ACLS = [
2170
2238
  "addObject",
2171
2239
  "deleteObject",
@@ -2212,23 +2280,30 @@ async function keyExists(key) {
2212
2280
  var resolved = /* @__PURE__ */ new Map();
2213
2281
  async function forgetResolvedKeys() {
2214
2282
  resolved.clear();
2215
- await deleteStoredKeys();
2283
+ const userId = await currentUserId();
2284
+ if (userId) await deleteStoredKeys(userId);
2216
2285
  }
2217
2286
  function resolveKey(kind, index, appId, acls, description) {
2218
- const cacheKey = `${kind}:${appId}:${index}`;
2287
+ const userId = requireUserId();
2288
+ const cacheKey = `${userId}:${kind}:${appId}:${index}`;
2219
2289
  const cached = resolved.get(cacheKey);
2220
2290
  if (cached) return cached;
2221
- const pending = provisionKey(kind, index, appId, acls, description).catch(
2222
- (err) => {
2223
- resolved.delete(cacheKey);
2224
- throw err;
2225
- }
2226
- );
2291
+ const pending = provisionKey(
2292
+ kind,
2293
+ userId,
2294
+ index,
2295
+ appId,
2296
+ acls,
2297
+ description
2298
+ ).catch((err) => {
2299
+ resolved.delete(cacheKey);
2300
+ throw err;
2301
+ });
2227
2302
  resolved.set(cacheKey, pending);
2228
2303
  return pending;
2229
2304
  }
2230
- async function provisionKey(kind, index, appId, acls, description) {
2231
- const stored = await readStoredKey(kind, index, appId);
2305
+ async function provisionKey(kind, userId, index, appId, acls, description) {
2306
+ const stored = await readStoredKey(kind, userId, index, appId);
2232
2307
  if (stored) {
2233
2308
  if (await keyExists(stored)) {
2234
2309
  logger.info({ kind, index, appId }, "reusing the stored API key");
@@ -2240,7 +2315,7 @@ async function provisionKey(kind, index, appId, acls, description) {
2240
2315
  );
2241
2316
  }
2242
2317
  const key = await createKey(index, acls, description);
2243
- await storeKey(kind, index, appId, key);
2318
+ await storeKey(kind, userId, index, appId, key);
2244
2319
  return { key, source: "created" };
2245
2320
  }
2246
2321
  function resolveWriteKey(index, appId) {
@@ -3509,7 +3584,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3509
3584
  // package.json
3510
3585
  var package_default = {
3511
3586
  name: "@algolia/wizard",
3512
- version: "0.46.0",
3587
+ version: "0.48.0-rc.141.344",
3513
3588
  description: "Magically implement Algolia functionality in your codebase",
3514
3589
  type: "module",
3515
3590
  engines: {
@@ -4224,6 +4299,7 @@ function searchInstructions(input) {
4224
4299
  return [
4225
4300
  "Implement an in-app Algolia search experience.",
4226
4301
  `Build the search UI for ${input.searchUiTarget}.`,
4302
+ "Search UI only \u2014 the index is already populated. Do not create or modify ingestion scripts, rake/manage/CLI tasks, migrations, seeders, or other data-loading code, even if the data looks incomplete.",
4227
4303
  ...doc ? [
4228
4304
  "Follow the Algolia SDK reference below for client setup, search UI wiring, and Insights instrumentation \u2014 Insights is required, not optional; prefer the reference over prior knowledge:",
4229
4305
  doc
@@ -4231,9 +4307,12 @@ function searchInstructions(input) {
4231
4307
  "No bundled Algolia SDK reference exists for this stack, so rely on the project's own conventions and Algolia's official client for its language. Do not invent APIs \u2014 keep to the documented search endpoint and its parameters."
4232
4308
  ],
4233
4309
  `Create the search experience as its own component in a new file, following the project's existing component conventions (location, naming, styling approach). Do not write it inline into an existing file.`,
4234
- `Import and render that new component from ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results list against the target index.`,
4310
+ `Import and render that new component from ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results panel against the target index.`,
4235
4311
  "If a search box already exists, replace its usage with an import and render of your new component; remove the old implementation.",
4236
4312
  "When rendering results with an existing shared component (e.g. a card), import and reuse that component rather than inlining its markup \u2014 inlining silently drops the styles and behavior its own file provides.",
4313
+ "Meet WCAG AA contrast (4.5:1 body text, 3:1 large text/icons) between the panel's text and its own background, and give the input and the active result a focus indicator visible against whatever sits behind it. Style the panel through the widget's class and CSS-variable overrides (or, when hand-built, the app's existing theme tokens) \u2014 never assume a light surface or reuse the surrounding page's colors unchanged inside the panel.",
4314
+ "Vendor theme CSS, and any CSS you write against vendor class names, must be global: import the theme from the component's own script module or the app's global stylesheet, and put overrides in a global block (Astro <style is:global>, an unscoped Vue block, a plain global CSS file). Never a framework-scoped style block or a CSS Module \u2014 scoping rewrites the vendor selectors and the widget's runtime DOM carries no scope attribute, so not one rule matches: styling silently does nothing and the build still passes.",
4315
+ "The panel takes its width from the input by default, so a small input makes it unreadably narrow: give it a min-width of 320px independent of the input, anchored to the input edge it opens from so widening does not push it off-screen.",
4237
4316
  `Define ${SEARCH_CONFIG_APP_ID}, ${SEARCH_CONFIG_SEARCH_KEY}, and ${SEARCH_CONFIG_INDEX_NAME} as exported constants in a module that fits this project's existing conventions for shared client-side config \u2014 reuse an existing one if it already holds config like this, or add a small new one otherwise. These are PUBLIC values, safe to commit and expose client-side: never read them from an environment variable or a .env* file, and never hardcode them anywhere except in that one module (import them wherever the search client needs them).`,
4238
4317
  `Set ${SEARCH_CONFIG_APP_ID} to "${input.appId}" and ${SEARCH_CONFIG_INDEX_NAME} to "${input.targetIndex}".`,
4239
4318
  input.searchKey ? `Set ${SEARCH_CONFIG_SEARCH_KEY} to "${input.searchKey}".` : `A real search-only key could not be provisioned${input.searchKeyError ? ` (${input.searchKeyError})` : ""} \u2014 set ${SEARCH_CONFIG_SEARCH_KEY} to the placeholder "${SEARCH_KEY_PLACEHOLDER}" and add a prominent TODO for the developer to fill in a real one.`,
@@ -4435,6 +4514,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4435
4514
  if (useCases.includes("ingestion")) {
4436
4515
  ingestAppId = appId ?? (await requireApplication()).id;
4437
4516
  }
4517
+ let ingestWriteKey;
4518
+ if (useCases.includes("ingestion") && ingestAppId) {
4519
+ ingestWriteKey = (await resolveWriteKey(targetIndex, ingestAppId)).key;
4520
+ }
4438
4521
  let uploadFilePath;
4439
4522
  let uploadWarning;
4440
4523
  if (ingestionSource === "fileUpload") {
@@ -4497,9 +4580,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4497
4580
  let ingestDurationMs;
4498
4581
  let ingestOutcomeMessage;
4499
4582
  const ingestKeyAppId = ingestAppId;
4500
- const ingestionTools = ingestKeyAppId ? makeToolContext(repoRoot, async () => ({
4583
+ const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, async () => ({
4501
4584
  [APP_ID_VAR]: ingestKeyAppId,
4502
- [API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
4585
+ [API_KEY_VAR]: ingestWriteKey,
4503
4586
  [INDEX_NAME_VAR]: targetIndex
4504
4587
  })) : void 0;
4505
4588
  const searchTools = makeToolContext(repoRoot);
@@ -5094,13 +5177,13 @@ import { Box as Box15, Text as Text15 } from "ink";
5094
5177
 
5095
5178
  // src/ui/Steps.tsx
5096
5179
  import { Box as Box13, Text as Text13 } from "ink";
5097
- import Spinner from "ink-spinner";
5180
+ import Spinner2 from "ink-spinner";
5098
5181
  import { jsx as jsx11, jsxs as jsxs12 } from "react/jsx-runtime";
5099
5182
  function Steps() {
5100
5183
  const { steps } = useWizard();
5101
5184
  const visibleSteps = steps.filter(isStepVisible);
5102
5185
  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: [
5103
- s.status === "running" ? /* @__PURE__ */ jsx11(Spinner, { type: "dots" }) : MARKER[s.status],
5186
+ s.status === "running" ? /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) : MARKER[s.status],
5104
5187
  " ",
5105
5188
  s.title
5106
5189
  ] }) }, s.id)) });
@@ -5110,7 +5193,7 @@ function CurrentStep() {
5110
5193
  const currentStep = steps.filter(isStepVisible).find((s) => s.status === "running");
5111
5194
  if (!currentStep) return null;
5112
5195
  return /* @__PURE__ */ jsxs12(Text13, { color: COLORS.status.running, children: [
5113
- /* @__PURE__ */ jsx11(Spinner, { type: "dots" }),
5196
+ /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
5114
5197
  " ",
5115
5198
  ` ${currentStep.title}`
5116
5199
  ] });
@@ -5655,6 +5738,7 @@ function App() {
5655
5738
  inputReq,
5656
5739
  user,
5657
5740
  workflow,
5741
+ settingUpAppId,
5658
5742
  review: review2
5659
5743
  } = useWizard();
5660
5744
  const { exit } = useApp();
@@ -5670,7 +5754,7 @@ function App() {
5670
5754
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
5671
5755
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
5672
5756
  const holdForTip = stepHasTips && promptPending && tipState === "revealing";
5673
- const showTips = stepHasTips && (phase === "running" || isCommandApprovalPrompt || holdForTip);
5757
+ const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
5674
5758
  const showNoticesInMain = !stepHasTips;
5675
5759
  const showNotices = (!isAwaitingUserInput || holdForTip) && !review2;
5676
5760
  useInput7(
@@ -5741,20 +5825,26 @@ function App() {
5741
5825
  children: [
5742
5826
  /* @__PURE__ */ jsxs18(Box20, { flexGrow: 2, flexDirection: "column", children: [
5743
5827
  phase === "preflight" && !user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5744
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5828
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5745
5829
  " Signing in to Algolia"
5746
5830
  ] }) }),
5747
5831
  phase === "preflight" && user && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5748
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5832
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5749
5833
  " Getting things ready"
5750
5834
  ] }) }),
5751
5835
  phase === "authenticating" && /* @__PURE__ */ jsxs18(Box20, { flexDirection: "column", marginBottom: 1, children: [
5752
5836
  /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5753
- /* @__PURE__ */ jsx18(Spinner2, { type: "dots" }),
5837
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5754
5838
  " Signing in to Algolia"
5755
5839
  ] }),
5756
5840
  /* @__PURE__ */ jsx18(Text20, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
5757
5841
  ] }),
5842
+ settingUpAppId && /* @__PURE__ */ jsx18(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs18(Text20, { color: COLORS.strong, bold: true, children: [
5843
+ /* @__PURE__ */ jsx18(Spinner3, { type: "dots" }),
5844
+ " Setting up Wizard using app",
5845
+ " ",
5846
+ settingUpAppId
5847
+ ] }) }),
5758
5848
  /* @__PURE__ */ jsx18(CliOutput, {}),
5759
5849
  showTips && currentStep && /* @__PURE__ */ jsx18(
5760
5850
  Tips,
@@ -5825,54 +5915,71 @@ function readValue(raw) {
5825
5915
  }
5826
5916
 
5827
5917
  // src/lib/algoliaAppPicker.ts
5828
- function secondaryFor(app) {
5918
+ function blockReasonFor(app) {
5919
+ return app.status !== "active" ? "Paused" : "Missing permissions";
5920
+ }
5921
+ function secondaryFor(app, highlightId) {
5922
+ if (!canSelectApplication(app)) {
5923
+ return { kind: "text", value: blockReasonFor(app) };
5924
+ }
5925
+ if (app.id === highlightId) {
5926
+ return { kind: "badge", value: "[DETECTED]" };
5927
+ }
5829
5928
  return app.plan ? { kind: "badge", value: app.plan } : void 0;
5830
5929
  }
5831
5930
  function labelFor(app) {
5832
5931
  return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
5833
5932
  }
5834
- function selectAndReport(app) {
5835
- useWizard.getState().pushCliOutput(
5836
- "stdout",
5837
- `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
5838
- );
5839
- return selectApplication(app.id);
5933
+ async function selectAndReport(id) {
5934
+ const store = useWizard.getState();
5935
+ store.setSettingUpApp(id);
5936
+ try {
5937
+ return await selectApplication(id);
5938
+ } finally {
5939
+ store.setSettingUpApp(null);
5940
+ }
5840
5941
  }
5841
- async function promptForApplication(leadIn = []) {
5942
+ async function promptForApplication(highlight) {
5842
5943
  const store = useWizard.getState();
5843
- const apps = await listApplications();
5844
- if (apps.length === 0) {
5944
+ const unordered = await listApplications();
5945
+ if (unordered.length === 0) {
5845
5946
  throw new Error(
5846
5947
  "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
5847
5948
  );
5848
5949
  }
5849
- if (apps.length === 1) {
5850
- const only = apps[0];
5851
- logger.info(
5852
- { app: only.id },
5853
- "single application on the account; selecting it"
5950
+ if (!unordered.some(canSelectApplication)) {
5951
+ throw new Error(
5952
+ "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."
5854
5953
  );
5855
- for (const line of leadIn) store.pushCliOutput("stdout", line);
5856
- return selectAndReport(only);
5857
5954
  }
5858
- const messages = [
5859
- ...leadIn,
5860
- "Which Algolia application should the wizard work in?"
5955
+ const apps = [
5956
+ ...unordered.filter(canSelectApplication),
5957
+ ...unordered.filter((app) => !canSelectApplication(app))
5861
5958
  ];
5959
+ const envApp = highlight ? unordered.find((app) => app.id === highlight.id) : void 0;
5960
+ const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
5961
+ const messages = [];
5962
+ if (highlight && highlightIndex < 0) {
5963
+ messages.push(
5964
+ 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.`
5965
+ );
5966
+ }
5862
5967
  for (; ; ) {
5863
5968
  const choice = await store.requestUserInput({
5864
- prompt: "Select an application",
5969
+ prompt: "Which Algolia application should the wizard work in?",
5865
5970
  promptType: "multipleChoice",
5866
5971
  options: apps.map(labelFor),
5867
- secondary: apps.map(secondaryFor),
5972
+ secondary: apps.map((app) => secondaryFor(app, highlight?.id)),
5973
+ disabled: apps.map((app) => !canSelectApplication(app)),
5974
+ ...highlightIndex >= 0 ? { defaultSelectedIndex: highlightIndex } : {},
5868
5975
  messages
5869
5976
  });
5870
5977
  const chosen = apps.find((app) => labelFor(app) === choice);
5871
- if (!chosen) {
5978
+ if (!chosen || !canSelectApplication(chosen)) {
5872
5979
  throw new Error("Application picker received an unexpected selection");
5873
5980
  }
5874
5981
  try {
5875
- return await selectAndReport(chosen);
5982
+ return await selectAndReport(chosen.id);
5876
5983
  } catch (err) {
5877
5984
  logger.warn(
5878
5985
  { app: chosen.id, err: err.message },
@@ -5884,42 +5991,11 @@ async function promptForApplication(leadIn = []) {
5884
5991
  }
5885
5992
  }
5886
5993
  }
5887
- async function confirmEnvApplication(env, current) {
5888
- const useEnv = `Use ${env.id} (from ${env.file})`;
5889
- const choice = await useWizard.getState().requestUserInput({
5890
- prompt: "Select an application",
5891
- promptType: "multipleChoice",
5892
- options: [
5893
- useEnv,
5894
- current ? `Use ${labelFor(current)} (already selected)` : "Pick a different application"
5895
- ],
5896
- messages: [
5897
- `${env.file} already sets ${env.name}=${env.id}.`,
5898
- "Which Algolia application should the wizard work in?"
5899
- ]
5900
- });
5901
- return choice === useEnv;
5902
- }
5903
- async function selectEnvApplication(env) {
5904
- try {
5905
- return await selectAndReport({ id: env.id, name: "" });
5906
- } catch (err) {
5907
- logger.warn(
5908
- { app: env.id, err: err.message },
5909
- "could not select the application named in env; falling back to the picker"
5910
- );
5911
- return promptForApplication([
5912
- `Could not select ${env.id} from ${env.file} \u2014 it may have been removed, or this account may not have access to it.`
5913
- ]);
5914
- }
5915
- }
5916
- async function ensureApplication() {
5917
- const current = await currentApplication();
5994
+ async function ensureApplication(resumeAppId) {
5995
+ if (resumeAppId) return selectAndReport(resumeAppId);
5918
5996
  const env = await findEnvApplicationId();
5919
- if (env && env.id !== current?.id && await confirmEnvApplication(env, current)) {
5920
- return selectEnvApplication(env);
5921
- }
5922
- return current ?? await promptForApplication();
5997
+ if (!env) return promptForApplication();
5998
+ return promptForApplication({ id: env.id, file: env.file });
5923
5999
  }
5924
6000
 
5925
6001
  // src/lib/seed.ts
@@ -6283,14 +6359,17 @@ async function run(workflow) {
6283
6359
  }
6284
6360
  store.setUser(user);
6285
6361
  let app;
6362
+ const resumableState = await loadResumableState(workflow);
6286
6363
  try {
6287
- app = await ensureApplication();
6364
+ app = await ensureApplication(
6365
+ resumableState ? runAppId(resumableState) : void 0
6366
+ );
6288
6367
  } catch (err) {
6289
6368
  store.setError(err instanceof Error ? err.message : String(err));
6290
6369
  await instance.waitUntilExit();
6291
6370
  process.exit(1);
6292
6371
  }
6293
- runWorkflow(workflow, app.id);
6372
+ runWorkflow(workflow, app.id, resumableState);
6294
6373
  }
6295
6374
  await requestTerminalSize();
6296
6375
  var started = await startup();