@algolia/wizard 0.37.0 → 0.38.0-rc.131.266

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 +245 -159
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -258,10 +258,14 @@ var useWizard = create((set, get) => ({
258
258
  error: null,
259
259
  inputReq: null,
260
260
  _resolve: null,
261
+ settingUpAppId: null,
262
+ setSettingUpApp: (appId) => set({ settingUpAppId: appId }),
261
263
  // `endAuth` lands on 'preflight', not 'idle': sign-in happens after the
262
264
  // welcome screen, so going back would gate the run a second time.
263
265
  beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
264
- endAuth: () => set((s) => s.phase === "authenticating" ? { phase: "preflight" } : {}),
266
+ endAuth: () => set(
267
+ (s) => s.phase === "authenticating" ? { phase: "preflight", cliOutput: [] } : {}
268
+ ),
265
269
  confirmStart: () => set(
266
270
  (s) => s.phase === "idle" ? { phase: "preflight", homeScreen: "home" } : {}
267
271
  ),
@@ -405,7 +409,8 @@ var useWizard = create((set, get) => ({
405
409
  logs: [],
406
410
  error: null,
407
411
  inputReq: null,
408
- _resolve: null
412
+ _resolve: null,
413
+ settingUpAppId: null
409
414
  });
410
415
  }
411
416
  }));
@@ -653,25 +658,27 @@ function SelectRow({
653
658
  labelWidth,
654
659
  highlightBackground = true,
655
660
  usePadding = false,
661
+ disabled = false,
656
662
  children
657
663
  }) {
658
- const labelColor = highlighted ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
664
+ const active = highlighted && !disabled;
665
+ const labelColor = disabled ? COLORS.dim : active ? highlightBackground ? COLORS.highlight.fg : COLORS.success : COLORS.primary;
659
666
  return /* @__PURE__ */ jsxs3(
660
667
  Box4,
661
668
  {
662
669
  width,
663
670
  paddingX: usePadding ? 1 : 0,
664
671
  paddingY: usePadding ? 1 : 0,
665
- backgroundColor: highlighted && highlightBackground ? COLORS.highlight.bg : void 0,
672
+ backgroundColor: active && highlightBackground ? COLORS.highlight.bg : void 0,
666
673
  children: [
667
674
  /* @__PURE__ */ jsx3(Box4, { width: labelWidth, children: /* @__PURE__ */ jsxs3(
668
675
  Text4,
669
676
  {
670
677
  color: labelColor,
671
- bold: highlighted && !highlightBackground,
678
+ bold: active && !highlightBackground,
672
679
  wrap: "truncate",
673
680
  children: [
674
- highlighted ? "\u276F " : " ",
681
+ active ? "\u276F " : " ",
675
682
  label
676
683
  ]
677
684
  }
@@ -870,6 +877,14 @@ var ARROW_WIDTH = 4;
870
877
  var COLUMN_GAP = 2;
871
878
  var BAR_PADDING = 2;
872
879
  var ROW_HEIGHT = 3;
880
+ function nextSelectableIndex(start, dir, rowCount, isDisabled) {
881
+ let i = start;
882
+ for (let step = 0; step < rowCount; step++) {
883
+ i = (i + dir + rowCount) % rowCount;
884
+ if (!isDisabled(i)) return i;
885
+ }
886
+ return start;
887
+ }
873
888
  function SelectPrompt({
874
889
  options,
875
890
  onSelect,
@@ -881,15 +896,18 @@ function SelectPrompt({
881
896
  multi,
882
897
  cancelable,
883
898
  secondary,
899
+ disabled,
884
900
  defaultSelectedIndex = 0
885
901
  }) {
886
- const [index, setIndex] = useState5(
887
- () => defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0
888
- );
889
- const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
890
902
  const hasCancel = Boolean(multi || cancelable);
891
903
  const rows = hasCancel ? [...options, "Cancel"] : options;
892
904
  const cancelIndex = hasCancel ? options.length : -1;
905
+ const isDisabled = (i) => i !== cancelIndex && Boolean(disabled?.[i]);
906
+ const [index, setIndex] = useState5(() => {
907
+ const start = defaultSelectedIndex > 0 && defaultSelectedIndex < options.length ? defaultSelectedIndex : 0;
908
+ return isDisabled(start) ? nextSelectableIndex(start, 1, rows.length, isDisabled) : start;
909
+ });
910
+ const [checked, setChecked] = useState5(() => /* @__PURE__ */ new Set());
893
911
  const hints = [];
894
912
  if (rows.length > 1) hints.push({ key: "[\u2191] [\u2193]", label: "move" });
895
913
  if (multi) hints.push({ key: "[space]", label: "select" });
@@ -929,9 +947,15 @@ function SelectPrompt({
929
947
  useInput2((input, key) => {
930
948
  if (rows.length === 0) return;
931
949
  if (key.upArrow || input === "k") {
932
- setIndex((i) => (i - 1 + rows.length) % rows.length);
950
+ setIndex((i) => {
951
+ const next = (i - 1 + rows.length) % rows.length;
952
+ return isDisabled(next) ? i : next;
953
+ });
933
954
  } else if (key.downArrow || input === "j") {
934
- setIndex((i) => (i + 1) % rows.length);
955
+ setIndex((i) => {
956
+ const next = (i + 1) % rows.length;
957
+ return isDisabled(next) ? i : next;
958
+ });
935
959
  } else if (multi && input === " " && index !== cancelIndex) {
936
960
  setChecked((prev) => {
937
961
  const next = new Set(prev);
@@ -942,6 +966,7 @@ function SelectPrompt({
942
966
  } else if (key.return) {
943
967
  if (index === cancelIndex) {
944
968
  onSelect(CANCEL);
969
+ } else if (isDisabled(index)) {
945
970
  } else if (!multi) {
946
971
  onSelect(options[index]);
947
972
  } else if (checked.size > 0) {
@@ -966,10 +991,12 @@ function SelectPrompt({
966
991
  const bullet = multi && !isCancel ? checked.has(i) ? "\u25CF " : "\u25CB " : "";
967
992
  const sec = isCancel ? void 0 : secondary?.[i];
968
993
  const isText = sec?.kind === "text";
994
+ const rowDisabled = isDisabled(i);
969
995
  return /* @__PURE__ */ jsxs7(
970
996
  SelectRow,
971
997
  {
972
998
  highlighted,
999
+ disabled: rowDisabled,
973
1000
  width: isText ? "100%" : barWidth,
974
1001
  labelWidth: isText ? labelWidth : barLabelWidth,
975
1002
  label: `${bullet}${option}`,
@@ -979,7 +1006,7 @@ function SelectPrompt({
979
1006
  Text8,
980
1007
  {
981
1008
  wrap: "truncate",
982
- color: highlighted ? COLORS.primary : COLORS.muted,
1009
+ color: rowDisabled ? COLORS.dim : highlighted ? COLORS.primary : COLORS.muted,
983
1010
  children: sec.value
984
1011
  }
985
1012
  ) }),
@@ -1041,6 +1068,7 @@ function PromptInput() {
1041
1068
  table: inputReq.table,
1042
1069
  options: inputReq.options,
1043
1070
  secondary: inputReq.secondary,
1071
+ disabled: inputReq.disabled,
1044
1072
  defaultSelectedIndex: inputReq.defaultSelectedIndex,
1045
1073
  cancelable: inputReq.cancelable,
1046
1074
  error: inputReq.error,
@@ -1112,7 +1140,7 @@ function PromptInput() {
1112
1140
  inputReq.prompt,
1113
1141
  " "
1114
1142
  ] }),
1115
- /* @__PURE__ */ jsx7(
1143
+ /* @__PURE__ */ jsx7(Text9, { color: COLORS.primary, children: /* @__PURE__ */ jsx7(
1116
1144
  TextInput,
1117
1145
  {
1118
1146
  value: draft,
@@ -1122,7 +1150,7 @@ function PromptInput() {
1122
1150
  setDraft("");
1123
1151
  }
1124
1152
  }
1125
- )
1153
+ ) })
1126
1154
  ] })
1127
1155
  ] });
1128
1156
  }
@@ -1404,6 +1432,10 @@ function reconcileWorkflowState(state, workflow) {
1404
1432
  }
1405
1433
  return state;
1406
1434
  }
1435
+ async function isResumableWorkflow(workflow) {
1436
+ const persisted = await loadWorkflowState(workflow.id);
1437
+ return persisted != null && reconcileWorkflowState(persisted, workflow) != null;
1438
+ }
1407
1439
  function initWorkflowState(workflow, now) {
1408
1440
  return {
1409
1441
  workflowId: workflow.id,
@@ -1600,14 +1632,89 @@ async function listIndices() {
1600
1632
  return items.map((i) => ({ name: i.name, entries: i.entries })).sort((a, b) => a.name.localeCompare(b.name));
1601
1633
  }
1602
1634
 
1635
+ // src/lib/algoliaApp.ts
1636
+ import { z as z5 } from "zod";
1637
+ var applicationSchema = z5.object({
1638
+ id: z5.string().min(1),
1639
+ name: z5.string().default(""),
1640
+ plan: z5.string().optional()
1641
+ });
1642
+ var listSchema = z5.array(
1643
+ z5.object({
1644
+ id: z5.string().min(1),
1645
+ name: z5.string().default(""),
1646
+ plan_label: z5.string().optional(),
1647
+ status: z5.string().optional(),
1648
+ acl: z5.array(z5.string()).optional()
1649
+ }).transform(({ id, name, plan_label, status, acl }) => ({
1650
+ id,
1651
+ name,
1652
+ plan: plan_label,
1653
+ status,
1654
+ acl
1655
+ }))
1656
+ );
1657
+ function canSelectApplication(app) {
1658
+ return app.status === "active" && (app.acl ?? []).includes("keys");
1659
+ }
1660
+ async function currentApplication() {
1661
+ let raw;
1662
+ try {
1663
+ raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1664
+ } catch {
1665
+ return null;
1666
+ }
1667
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1668
+ return parsed.success ? parsed.data : null;
1669
+ }
1670
+ async function requireApplication() {
1671
+ const app = await currentApplication();
1672
+ if (!app) {
1673
+ throw new Error(
1674
+ "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1675
+ );
1676
+ }
1677
+ return app;
1678
+ }
1679
+ async function listApplications() {
1680
+ const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1681
+ const parsed = listSchema.safeParse(parseJson(raw));
1682
+ if (!parsed.success) {
1683
+ throw new Error("Could not read the list of Algolia applications.");
1684
+ }
1685
+ return parsed.data;
1686
+ }
1687
+ async function selectApplication(id) {
1688
+ const raw = await runAlgoliaCli(
1689
+ ["application", "select", "--non-interactive", "--app-id", id],
1690
+ { onOutput: stderrSink }
1691
+ );
1692
+ const parsed = applicationSchema.safeParse(parseJson(raw));
1693
+ if (!parsed.success) {
1694
+ throw new Error(
1695
+ `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1696
+ );
1697
+ }
1698
+ return parsed.data;
1699
+ }
1700
+ function parseJson(text) {
1701
+ try {
1702
+ return JSON.parse(text);
1703
+ } catch {
1704
+ return void 0;
1705
+ }
1706
+ }
1707
+
1603
1708
  // src/actions/selectIndex.ts
1604
1709
  var CREATE_NEW_INDEX = "Create a new index\u2026";
1605
1710
  var selectIndexStep = async (ctx) => {
1711
+ await requireApplication();
1606
1712
  const indices = await listIndices();
1607
1713
  const names = indices.map((i) => i.name);
1608
1714
  const hasIndices = names.length > 0;
1609
1715
  let error;
1610
- for (; ; ) {
1716
+ let chosen;
1717
+ while (chosen === void 0) {
1611
1718
  const selection = hasIndices ? await ctx.requestUserInput({
1612
1719
  prompt: "Which index do you want to ingest into?",
1613
1720
  promptType: "multipleChoice",
@@ -1626,9 +1733,9 @@ var selectIndexStep = async (ctx) => {
1626
1733
  if (typeof selection !== "string") {
1627
1734
  throw new Error("selectIndex received an unexpected non-text result");
1628
1735
  }
1629
- let chosen;
1736
+ let candidate;
1630
1737
  if (!hasIndices) {
1631
- chosen = selection.trim();
1738
+ candidate = selection.trim();
1632
1739
  } else if (selection === CREATE_NEW_INDEX) {
1633
1740
  const name = await ctx.requestUserInput({
1634
1741
  prompt: "Name the new index:",
@@ -1638,17 +1745,18 @@ var selectIndexStep = async (ctx) => {
1638
1745
  if (typeof name !== "string") {
1639
1746
  throw new Error("selectIndex received an unexpected non-text result");
1640
1747
  }
1641
- chosen = name.trim();
1748
+ candidate = name.trim();
1642
1749
  } else {
1643
- chosen = selection;
1750
+ candidate = selection;
1644
1751
  }
1645
- if (!chosen) {
1752
+ if (!candidate) {
1646
1753
  error = "Index name cannot be empty.";
1647
1754
  continue;
1648
1755
  }
1649
- ctx.setUserInput("index", chosen);
1650
- return { selection: chosen };
1756
+ chosen = candidate;
1651
1757
  }
1758
+ ctx.setUserInput("index", chosen);
1759
+ return { selection: chosen };
1652
1760
  };
1653
1761
 
1654
1762
  // src/lib/agent.ts
@@ -1661,7 +1769,7 @@ import "zod";
1661
1769
 
1662
1770
  // src/lib/tools/listFiles.ts
1663
1771
  import { tool } from "ai";
1664
- import z5 from "zod";
1772
+ import z6 from "zod";
1665
1773
  import { readdir } from "node:fs/promises";
1666
1774
 
1667
1775
  // src/lib/tools/path.ts
@@ -1697,8 +1805,8 @@ async function hasSymlinkParent(ctx, target) {
1697
1805
  function listFilesTool(ctx) {
1698
1806
  return tool({
1699
1807
  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.',
1700
- inputSchema: z5.object({
1701
- path: z5.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1808
+ inputSchema: z6.object({
1809
+ path: z6.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
1702
1810
  }),
1703
1811
  execute: async ({ path = "." }) => {
1704
1812
  logger.info({ path }, "called listFiles tool");
@@ -1719,13 +1827,13 @@ function listFilesTool(ctx) {
1719
1827
 
1720
1828
  // src/lib/tools/changeDirectory.ts
1721
1829
  import { tool as tool2 } from "ai";
1722
- import z6 from "zod";
1830
+ import z7 from "zod";
1723
1831
  import { stat } from "node:fs/promises";
1724
1832
  function changeDirectoryTool(ctx) {
1725
1833
  return tool2({
1726
1834
  description: "Change the current working directory. Subsequent file operations resolve relative to it. Returns the new working directory.",
1727
- inputSchema: z6.object({
1728
- path: z6.string().describe("Directory to change into")
1835
+ inputSchema: z7.object({
1836
+ path: z7.string().describe("Directory to change into")
1729
1837
  }),
1730
1838
  execute: async ({ path }) => {
1731
1839
  logger.info({ path }, "called changeDirectory tool");
@@ -1747,13 +1855,13 @@ function changeDirectoryTool(ctx) {
1747
1855
 
1748
1856
  // src/lib/tools/reportStatus.ts
1749
1857
  import { tool as tool3 } from "ai";
1750
- import z7 from "zod";
1858
+ import z8 from "zod";
1751
1859
  function reportStatusTool(output) {
1752
1860
  return tool3({
1753
1861
  description: "Report the status of your execution. Return a reason in case of failure.",
1754
- inputSchema: z7.object({
1755
- status: z7.enum(["success", "fail"]),
1756
- reason: z7.string().optional(),
1862
+ inputSchema: z8.object({
1863
+ status: z8.enum(["success", "fail"]),
1864
+ reason: z8.string().optional(),
1757
1865
  output
1758
1866
  }),
1759
1867
  execute: async ({ status, reason, output: output2 }) => {
@@ -1765,7 +1873,7 @@ function reportStatusTool(output) {
1765
1873
 
1766
1874
  // src/lib/tools/readFile.ts
1767
1875
  import { tool as tool4 } from "ai";
1768
- import z8 from "zod";
1876
+ import z9 from "zod";
1769
1877
  import { readFile as readFile3 } from "node:fs/promises";
1770
1878
 
1771
1879
  // src/lib/tools/env.ts
@@ -1793,8 +1901,8 @@ function redactEnvValues(content) {
1793
1901
  function readFileTool(ctx) {
1794
1902
  return tool4({
1795
1903
  description: "Read the contents of a file at the given path",
1796
- inputSchema: z8.object({
1797
- filePath: z8.string().describe("Path to the file to read")
1904
+ inputSchema: z9.object({
1905
+ filePath: z9.string().describe("Path to the file to read")
1798
1906
  }),
1799
1907
  execute: async ({ filePath }) => {
1800
1908
  if (++ctx.counts.read > ctx.limits.read) {
@@ -1815,15 +1923,15 @@ function readFileTool(ctx) {
1815
1923
 
1816
1924
  // src/lib/tools/writeFile.ts
1817
1925
  import { tool as tool5 } from "ai";
1818
- import z9 from "zod";
1926
+ import z10 from "zod";
1819
1927
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
1820
1928
  import { dirname as dirname3 } from "node:path";
1821
1929
  function writeFileTool(ctx) {
1822
1930
  return tool5({
1823
1931
  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.",
1824
- inputSchema: z9.object({
1825
- filePath: z9.string().describe("Path to the file to write"),
1826
- content: z9.string().describe("Content to write to the file")
1932
+ inputSchema: z10.object({
1933
+ filePath: z10.string().describe("Path to the file to write"),
1934
+ content: z10.string().describe("Content to write to the file")
1827
1935
  }),
1828
1936
  execute: async ({ filePath, content }) => {
1829
1937
  logger.info({ filePath }, "called writeFile tool");
@@ -1853,68 +1961,6 @@ import z13 from "zod";
1853
1961
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
1854
1962
  import { dirname as dirname4, relative as relative3 } from "node:path";
1855
1963
 
1856
- // src/lib/algoliaApp.ts
1857
- import { z as z10 } from "zod";
1858
- var applicationSchema = z10.object({
1859
- id: z10.string().min(1),
1860
- name: z10.string().default(""),
1861
- plan: z10.string().optional()
1862
- });
1863
- var listSchema = z10.array(
1864
- z10.object({
1865
- id: z10.string().min(1),
1866
- name: z10.string().default(""),
1867
- plan_label: z10.string().optional()
1868
- }).transform(({ id, name, plan_label }) => ({ id, name, plan: plan_label }))
1869
- );
1870
- async function currentApplication() {
1871
- let raw;
1872
- try {
1873
- raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
1874
- } catch {
1875
- return null;
1876
- }
1877
- const parsed = applicationSchema.safeParse(parseJson(raw));
1878
- return parsed.success ? parsed.data : null;
1879
- }
1880
- async function requireApplication() {
1881
- const app = await currentApplication();
1882
- if (!app) {
1883
- throw new Error(
1884
- "No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
1885
- );
1886
- }
1887
- return app;
1888
- }
1889
- async function listApplications() {
1890
- const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
1891
- const parsed = listSchema.safeParse(parseJson(raw));
1892
- if (!parsed.success) {
1893
- throw new Error("Could not read the list of Algolia applications.");
1894
- }
1895
- return parsed.data;
1896
- }
1897
- async function selectApplication(id) {
1898
- const raw = await runAlgoliaCli(
1899
- ["application", "select", "--non-interactive", "--app-id", id],
1900
- { onOutput: stderrSink }
1901
- );
1902
- const parsed = applicationSchema.safeParse(parseJson(raw));
1903
- if (!parsed.success) {
1904
- throw new Error(
1905
- `Selected application ${id}, but the Algolia CLI returned an unreadable result.`
1906
- );
1907
- }
1908
- return parsed.data;
1909
- }
1910
- function parseJson(text) {
1911
- try {
1912
- return JSON.parse(text);
1913
- } catch {
1914
- return void 0;
1915
- }
1916
- }
1917
-
1918
1964
  // src/lib/algoliaApiKey.ts
1919
1965
  import { z as z12 } from "zod";
1920
1966
 
@@ -1922,13 +1968,13 @@ import { z as z12 } from "zod";
1922
1968
  import { deletePassword, getPassword, setPassword } from "cross-keychain";
1923
1969
  import { z as z11 } from "zod";
1924
1970
  var SERVICE = "algolia-wizard";
1925
- var ACCOUNT = "api-keys";
1971
+ var account = (userId) => `api-keys:${userId}`;
1926
1972
  var storedKeysSchema = z11.record(z11.string(), z11.string());
1927
1973
  function entryId(kind, index, appId) {
1928
1974
  return `${kind}:${appId}:${index}`;
1929
1975
  }
1930
- async function loadKeys() {
1931
- const raw = await getPassword(SERVICE, ACCOUNT);
1976
+ async function loadKeys(userId) {
1977
+ const raw = await getPassword(SERVICE, account(userId));
1932
1978
  if (!raw) return {};
1933
1979
  let payload;
1934
1980
  try {
@@ -1950,47 +1996,47 @@ function serialized(op) {
1950
1996
  });
1951
1997
  return next;
1952
1998
  }
1953
- async function readStoredKey(kind, index, appId) {
1999
+ async function readStoredKey(kind, userId, index, appId) {
1954
2000
  try {
1955
- return (await loadKeys())[entryId(kind, index, appId)] ?? null;
2001
+ return (await loadKeys(userId))[entryId(kind, index, appId)] ?? null;
1956
2002
  } catch (err) {
1957
2003
  logger.warn(
1958
- { err: err.message, kind, index, appId },
2004
+ { err: err.message, kind, index, appId, userId },
1959
2005
  "could not read the API key from the keychain"
1960
2006
  );
1961
2007
  return null;
1962
2008
  }
1963
2009
  }
1964
- function storeKey(kind, index, appId, value) {
2010
+ function storeKey(kind, userId, index, appId, value) {
1965
2011
  return serialized(async () => {
1966
2012
  const id = entryId(kind, index, appId);
1967
2013
  try {
1968
- const keys = await loadKeys();
2014
+ const keys = await loadKeys(userId);
1969
2015
  await setPassword(
1970
2016
  SERVICE,
1971
- ACCOUNT,
2017
+ account(userId),
1972
2018
  JSON.stringify({ ...keys, [id]: value })
1973
2019
  );
1974
- if ((await loadKeys())[id] !== value) {
2020
+ if ((await loadKeys(userId))[id] !== value) {
1975
2021
  throw new Error("the keychain did not store the value");
1976
2022
  }
1977
2023
  } catch (err) {
1978
2024
  logger.warn(
1979
- { err: err.message, kind, index, appId },
2025
+ { err: err.message, kind, index, appId, userId },
1980
2026
  "could not store the API key in the keychain; the next run will create another"
1981
2027
  );
1982
2028
  }
1983
2029
  });
1984
2030
  }
1985
- function deleteStoredKeys() {
2031
+ function deleteStoredKeys(userId) {
1986
2032
  return serialized(async () => {
1987
2033
  try {
1988
- await deletePassword(SERVICE, ACCOUNT);
2034
+ await deletePassword(SERVICE, account(userId));
1989
2035
  } catch (err) {
1990
2036
  const message = err.message;
1991
2037
  if (/not found/i.test(message)) return;
1992
2038
  logger.warn(
1993
- { err: message },
2039
+ { err: message, userId },
1994
2040
  "could not delete the API keys from the keychain"
1995
2041
  );
1996
2042
  }
@@ -1998,6 +2044,16 @@ function deleteStoredKeys() {
1998
2044
  }
1999
2045
 
2000
2046
  // src/lib/algoliaApiKey.ts
2047
+ function requireUserId() {
2048
+ const userId = useWizard.getState().user?.userId;
2049
+ if (!userId) {
2050
+ throw new Error("No Algolia user is signed in; cannot scope the API key.");
2051
+ }
2052
+ return userId;
2053
+ }
2054
+ async function currentUserId() {
2055
+ return useWizard.getState().user?.userId ?? (await getUser())?.userId ?? null;
2056
+ }
2001
2057
  var WRITE_ACLS = [
2002
2058
  "addObject",
2003
2059
  "deleteObject",
@@ -2044,23 +2100,30 @@ async function keyExists(key) {
2044
2100
  var resolved = /* @__PURE__ */ new Map();
2045
2101
  async function forgetResolvedKeys() {
2046
2102
  resolved.clear();
2047
- await deleteStoredKeys();
2103
+ const userId = await currentUserId();
2104
+ if (userId) await deleteStoredKeys(userId);
2048
2105
  }
2049
2106
  function resolveKey(kind, index, appId, acls, description) {
2050
- const cacheKey = `${kind}:${appId}:${index}`;
2107
+ const userId = requireUserId();
2108
+ const cacheKey = `${userId}:${kind}:${appId}:${index}`;
2051
2109
  const cached = resolved.get(cacheKey);
2052
2110
  if (cached) return cached;
2053
- const pending = provisionKey(kind, index, appId, acls, description).catch(
2054
- (err) => {
2055
- resolved.delete(cacheKey);
2056
- throw err;
2057
- }
2058
- );
2111
+ const pending = provisionKey(
2112
+ kind,
2113
+ userId,
2114
+ index,
2115
+ appId,
2116
+ acls,
2117
+ description
2118
+ ).catch((err) => {
2119
+ resolved.delete(cacheKey);
2120
+ throw err;
2121
+ });
2059
2122
  resolved.set(cacheKey, pending);
2060
2123
  return pending;
2061
2124
  }
2062
- async function provisionKey(kind, index, appId, acls, description) {
2063
- const stored = await readStoredKey(kind, index, appId);
2125
+ async function provisionKey(kind, userId, index, appId, acls, description) {
2126
+ const stored = await readStoredKey(kind, userId, index, appId);
2064
2127
  if (stored) {
2065
2128
  if (await keyExists(stored)) {
2066
2129
  logger.info({ kind, index, appId }, "reusing the stored API key");
@@ -2072,7 +2135,7 @@ async function provisionKey(kind, index, appId, acls, description) {
2072
2135
  );
2073
2136
  }
2074
2137
  const key = await createKey(index, acls, description);
2075
- await storeKey(kind, index, appId, key);
2138
+ await storeKey(kind, userId, index, appId, key);
2076
2139
  return { key, source: "created" };
2077
2140
  }
2078
2141
  function resolveWriteKey(index, appId) {
@@ -3267,7 +3330,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3267
3330
  // package.json
3268
3331
  var package_default = {
3269
3332
  name: "@algolia/wizard",
3270
- version: "0.37.0",
3333
+ version: "0.38.0-rc.131.266",
3271
3334
  description: "Magically implement Algolia functionality in your codebase",
3272
3335
  type: "module",
3273
3336
  engines: {
@@ -4154,6 +4217,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4154
4217
  if (useCases.includes("ingestion")) {
4155
4218
  ingestAppId = appId ?? (await requireApplication()).id;
4156
4219
  }
4220
+ let ingestWriteKey;
4221
+ if (useCases.includes("ingestion") && ingestAppId) {
4222
+ ingestWriteKey = (await resolveWriteKey(targetIndex, ingestAppId)).key;
4223
+ }
4157
4224
  let uploadFilePath;
4158
4225
  let uploadWarning;
4159
4226
  if (ingestionSource === "fileUpload") {
@@ -4216,9 +4283,9 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
4216
4283
  let ingestDurationMs;
4217
4284
  let ingestOutcomeMessage;
4218
4285
  const ingestKeyAppId = ingestAppId;
4219
- const ingestionTools = ingestKeyAppId ? makeToolContext(repoRoot, async () => ({
4286
+ const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, async () => ({
4220
4287
  [APP_ID_VAR]: ingestKeyAppId,
4221
- [API_KEY_VAR]: (await resolveWriteKey(targetIndex, ingestKeyAppId)).key,
4288
+ [API_KEY_VAR]: ingestWriteKey,
4222
4289
  [INDEX_NAME_VAR]: targetIndex
4223
4290
  })) : void 0;
4224
4291
  const searchTools = makeToolContext(repoRoot);
@@ -5373,7 +5440,8 @@ function App() {
5373
5440
  steps,
5374
5441
  inputReq,
5375
5442
  user,
5376
- workflow
5443
+ workflow,
5444
+ settingUpAppId
5377
5445
  } = useWizard();
5378
5446
  const { exit } = useApp();
5379
5447
  const { columns, rows } = useWindowSize7();
@@ -5388,7 +5456,7 @@ function App() {
5388
5456
  const isCommandApprovalPrompt = isAwaitingUserInput && inputReq?.promptType === "commandApproval";
5389
5457
  const promptPending = isAwaitingUserInput && !isCommandApprovalPrompt;
5390
5458
  const holdForTip = stepHasTips && promptPending && tipState === "revealing";
5391
- const showTips = stepHasTips && (phase === "running" || isCommandApprovalPrompt || holdForTip);
5459
+ const showTips = stepHasTips && (phase === "running" && currentStep?.status === "running" || isCommandApprovalPrompt || holdForTip);
5392
5460
  const showNoticesInMain = !stepHasTips;
5393
5461
  const showNotices = !isAwaitingUserInput || holdForTip;
5394
5462
  useInput6(
@@ -5473,6 +5541,12 @@ function App() {
5473
5541
  ] }),
5474
5542
  /* @__PURE__ */ jsx17(Text19, { color: COLORS.muted, children: "A browser window will open \u2014 complete sign-in there." })
5475
5543
  ] }),
5544
+ settingUpAppId && /* @__PURE__ */ jsx17(Box19, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs17(Text19, { color: COLORS.strong, bold: true, children: [
5545
+ /* @__PURE__ */ jsx17(Spinner2, { type: "dots" }),
5546
+ " Setting up Wizard using app",
5547
+ " ",
5548
+ settingUpAppId
5549
+ ] }) }),
5476
5550
  /* @__PURE__ */ jsx17(CliOutput, {}),
5477
5551
  showTips && currentStep && /* @__PURE__ */ jsx17(
5478
5552
  Tips,
@@ -5542,50 +5616,56 @@ function readValue(raw) {
5542
5616
  }
5543
5617
 
5544
5618
  // src/lib/algoliaAppPicker.ts
5619
+ function blockReasonFor(app) {
5620
+ return app.status !== "active" ? "Inactive" : "Missing permissions";
5621
+ }
5545
5622
  function secondaryFor(app) {
5623
+ if (!canSelectApplication(app)) {
5624
+ return { kind: "text", value: blockReasonFor(app) };
5625
+ }
5546
5626
  return app.plan ? { kind: "badge", value: app.plan } : void 0;
5547
5627
  }
5548
5628
  function labelFor(app) {
5549
5629
  return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
5550
5630
  }
5551
- function selectAndReport(app) {
5552
- useWizard.getState().pushCliOutput(
5553
- "stdout",
5554
- `Selecting ${labelFor(app)} \u2014 provisioning its API key\u2026`
5555
- );
5556
- return selectApplication(app.id);
5631
+ async function selectAndReport(app) {
5632
+ const store = useWizard.getState();
5633
+ store.setSettingUpApp(app.id);
5634
+ try {
5635
+ return await selectApplication(app.id);
5636
+ } finally {
5637
+ store.setSettingUpApp(null);
5638
+ }
5557
5639
  }
5558
5640
  async function promptForApplication(leadIn = []) {
5559
5641
  const store = useWizard.getState();
5560
- const apps = await listApplications();
5561
- if (apps.length === 0) {
5642
+ const unordered = await listApplications();
5643
+ if (unordered.length === 0) {
5562
5644
  throw new Error(
5563
5645
  "This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
5564
5646
  );
5565
5647
  }
5566
- if (apps.length === 1) {
5567
- const only = apps[0];
5568
- logger.info(
5569
- { app: only.id },
5570
- "single application on the account; selecting it"
5648
+ if (!unordered.some(canSelectApplication)) {
5649
+ throw new Error(
5650
+ "None of the applications on this Algolia account are usable \u2014 each is either inactive or missing key-management access. Activate one, or grant it the `keys` ACL, then restart the wizard."
5571
5651
  );
5572
- for (const line of leadIn) store.pushCliOutput("stdout", line);
5573
- return selectAndReport(only);
5574
5652
  }
5575
- const messages = [
5576
- ...leadIn,
5577
- "Which Algolia application should the wizard work in?"
5653
+ const apps = [
5654
+ ...unordered.filter(canSelectApplication),
5655
+ ...unordered.filter((app) => !canSelectApplication(app))
5578
5656
  ];
5657
+ const messages = [...leadIn];
5579
5658
  for (; ; ) {
5580
5659
  const choice = await store.requestUserInput({
5581
- prompt: "Select an application",
5660
+ prompt: "Which Algolia application should the wizard work in?",
5582
5661
  promptType: "multipleChoice",
5583
5662
  options: apps.map(labelFor),
5584
5663
  secondary: apps.map(secondaryFor),
5664
+ disabled: apps.map((app) => !canSelectApplication(app)),
5585
5665
  messages
5586
5666
  });
5587
5667
  const chosen = apps.find((app) => labelFor(app) === choice);
5588
- if (!chosen) {
5668
+ if (!chosen || !canSelectApplication(chosen)) {
5589
5669
  throw new Error("Application picker received an unexpected selection");
5590
5670
  }
5591
5671
  try {
@@ -5612,10 +5692,11 @@ async function confirmEnvApplication(env, current) {
5612
5692
  ],
5613
5693
  messages: [
5614
5694
  `${env.file} already sets ${env.name}=${env.id}.`,
5615
- "Which Algolia application should the wizard work in?"
5695
+ "Which Algolia application would you like to use?"
5616
5696
  ]
5617
5697
  });
5618
- return choice === useEnv;
5698
+ if (choice === useEnv) return selectEnvApplication(env);
5699
+ return current ? selectAndReport(current) : null;
5619
5700
  }
5620
5701
  async function selectEnvApplication(env) {
5621
5702
  try {
@@ -5630,13 +5711,17 @@ async function selectEnvApplication(env) {
5630
5711
  ]);
5631
5712
  }
5632
5713
  }
5633
- async function ensureApplication() {
5714
+ async function ensureApplication(isResuming) {
5634
5715
  const current = await currentApplication();
5716
+ if (isResuming) {
5717
+ return current ?? await promptForApplication();
5718
+ }
5635
5719
  const env = await findEnvApplicationId();
5636
- if (env && env.id !== current?.id && await confirmEnvApplication(env, current)) {
5637
- return selectEnvApplication(env);
5720
+ if (env && env.id !== current?.id) {
5721
+ const chosen = await confirmEnvApplication(env, current);
5722
+ if (chosen) return chosen;
5638
5723
  }
5639
- return current ?? await promptForApplication();
5724
+ return promptForApplication();
5640
5725
  }
5641
5726
 
5642
5727
  // src/lib/seed.ts
@@ -6001,7 +6086,8 @@ async function run(workflow) {
6001
6086
  store.setUser(user);
6002
6087
  let app;
6003
6088
  try {
6004
- app = await ensureApplication();
6089
+ const resuming = await isResumableWorkflow(workflow);
6090
+ app = await ensureApplication(resuming);
6005
6091
  } catch (err) {
6006
6092
  store.setError(err instanceof Error ? err.message : String(err));
6007
6093
  await instance.waitUntilExit();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.37.0",
3
+ "version": "0.38.0-rc.131.266",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {