@algolia/wizard 0.52.0-rc.136.364 → 0.52.0-rc.151.365

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 +216 -230
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -239,9 +239,6 @@ function describeInputValue(value) {
239
239
  }
240
240
  var NOTICE_INTERVAL_MS = 2e3;
241
241
  var CLI_OUTPUT_LIMIT = 200;
242
- function commandApprovalKey(command, cwd) {
243
- return `${cwd}\0${command}`;
244
- }
245
242
  var useWizard = create((set, get) => ({
246
243
  phase: "idle",
247
244
  homeScreen: "home",
@@ -359,17 +356,10 @@ var useWizard = create((set, get) => ({
359
356
  setTargetIndex: (index) => set({ targetIndex: index }),
360
357
  recordWrittenFile: (path) => set((s) => ({ writtenFiles: [...s.writtenFiles, path] })),
361
358
  clearWrittenFiles: () => set({ writtenFiles: [] }),
362
- isCommandApproved: (command, cwd) => get().approvedCommands.has(commandApprovalKey(command, cwd)),
359
+ isCommandApproved: (command, cwd) => get().approvedCommands.has(`${cwd}\0${command}`),
363
360
  approveCommand: (command, cwd) => set((s) => ({
364
- approvedCommands: new Set(s.approvedCommands).add(
365
- commandApprovalKey(command, cwd)
366
- )
361
+ approvedCommands: new Set(s.approvedCommands).add(`${cwd}\0${command}`)
367
362
  })),
368
- revokeCommand: (command, cwd) => set((s) => {
369
- const approvedCommands = new Set(s.approvedCommands);
370
- approvedCommands.delete(commandApprovalKey(command, cwd));
371
- return { approvedCommands };
372
- }),
373
363
  logStart: (kind, name, input) => {
374
364
  const id = nanoid();
375
365
  set((s) => ({
@@ -748,11 +738,9 @@ function DecisionSelect({
748
738
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
749
739
  function CommandApproval({
750
740
  command,
751
- messages,
752
741
  onDecide
753
742
  }) {
754
743
  return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
755
- messages?.map((message, index) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: message }, `msg-${index}`)),
756
744
  /* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, bold: true, children: "Run this command?" }),
757
745
  /* @__PURE__ */ jsxs5(
758
746
  Box6,
@@ -1169,6 +1157,7 @@ function PromptInput() {
1169
1157
  return /* @__PURE__ */ jsx8(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx8(
1170
1158
  SelectPrompt,
1171
1159
  {
1160
+ question: inputReq.prompt,
1172
1161
  messages: inputReq.messages,
1173
1162
  options: ["Continue"],
1174
1163
  onSelect: handleSubmit
@@ -1187,14 +1176,7 @@ function PromptInput() {
1187
1176
  );
1188
1177
  }
1189
1178
  if (inputReq.promptType === "commandApproval" && inputReq.command) {
1190
- return /* @__PURE__ */ jsx8(
1191
- CommandApproval,
1192
- {
1193
- command: inputReq.command,
1194
- messages: inputReq.messages,
1195
- onDecide: submitInput
1196
- }
1197
- );
1179
+ return /* @__PURE__ */ jsx8(CommandApproval, { command: inputReq.command, onDecide: submitInput });
1198
1180
  }
1199
1181
  if (inputReq.promptType === "acceptReject") {
1200
1182
  const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
@@ -2716,16 +2698,9 @@ function searchFilesTool(ctx) {
2716
2698
  }
2717
2699
 
2718
2700
  // src/lib/tools/runShell.ts
2719
- import { tool as tool9, generateText, Output } from "ai";
2701
+ import { tool as tool8, generateText, Output } from "ai";
2720
2702
  import { createAnthropic } from "@ai-sdk/anthropic";
2721
- import z16 from "zod";
2722
- import { relative as relative5 } from "node:path";
2723
-
2724
- // src/lib/tools/reviewScript.ts
2725
- import { tool as tool8 } from "ai";
2726
2703
  import z15 from "zod";
2727
- import { createHash } from "node:crypto";
2728
- import { readFile as readFile7, stat as stat2 } from "node:fs/promises";
2729
2704
  import { relative as relative4 } from "node:path";
2730
2705
 
2731
2706
  // src/lib/tools/utils/prompt.ts
@@ -2737,116 +2712,6 @@ function serializePrompt(work) {
2737
2712
  return result;
2738
2713
  }
2739
2714
 
2740
- // src/lib/tools/reviewScript.ts
2741
- var reviewedCommandApprovals = /* @__PURE__ */ new WeakMap();
2742
- async function fileDigest(filePath) {
2743
- const content = await readFile7(filePath);
2744
- return createHash("sha256").update(content).digest("hex");
2745
- }
2746
- function grantReviewedCommandApproval(ctx, approval) {
2747
- reviewedCommandApprovals.set(ctx, approval);
2748
- }
2749
- function getReviewedCommandApproval(ctx, command, cwd) {
2750
- const approval = reviewedCommandApprovals.get(ctx);
2751
- if (approval?.command !== command || approval.cwd !== cwd) return void 0;
2752
- return approval;
2753
- }
2754
- function clearReviewedCommandApproval(ctx) {
2755
- reviewedCommandApprovals.delete(ctx);
2756
- }
2757
- function revokeCommandApprovals(ctx, command, cwd) {
2758
- clearReviewedCommandApproval(ctx);
2759
- useWizard.getState().revokeCommand(command, cwd);
2760
- }
2761
- async function reviewedCommandApprovalIsCurrent(approval) {
2762
- const currentDigest = await fileDigest(approval.filePath).catch(() => null);
2763
- return currentDigest === approval.digest;
2764
- }
2765
- function reviewScriptTool(ctx) {
2766
- return tool8({
2767
- description: "Call this as soon as the ingestion script is finished and before you run it. It summarizes the records that will be written, then asks the user to review the script and approve the exact command that will run it, so a matching runShell call needs no second approval.",
2768
- inputSchema: z15.object({
2769
- filePath: z15.string().describe(
2770
- "Path to the script to review, relative to the project root."
2771
- ),
2772
- command: z15.string().min(1).describe("The exact command that runShell will use to run the script."),
2773
- cwd: z15.string().describe(
2774
- "The exact working directory that runShell will use, relative to the current tool working directory."
2775
- ),
2776
- entityName: z15.string().trim().min(1).max(100).regex(/^[^\r\n]+$/).describe("The exact entity name selected by the user."),
2777
- usesGeneratedRecords: z15.boolean().describe(
2778
- "Whether the script reads generated sample records instead of the selected real data source."
2779
- )
2780
- }),
2781
- execute: async ({
2782
- filePath,
2783
- command,
2784
- cwd,
2785
- entityName,
2786
- usesGeneratedRecords
2787
- }) => {
2788
- logger.info(
2789
- { filePath, command, cwd, entityName, usesGeneratedRecords },
2790
- "called reviewScript tool"
2791
- );
2792
- const resolved2 = resolveInRoot(ctx, filePath);
2793
- if (resolved2.ok === false) return resolved2.error;
2794
- const resolvedCwd = resolveInRoot(ctx, cwd);
2795
- if (resolvedCwd.ok === false) return resolvedCwd.error;
2796
- const displayCwd = relative4(ctx.root, resolvedCwd.target) || ".";
2797
- const displayFilePath = relative4(ctx.root, resolved2.target);
2798
- const ingestionSummary = usesGeneratedRecords ? `This will push ${entityName} records from generated sample data to Algolia. You can replace the sample dataset with your real data source in ${displayFilePath}.` : `This will push ${entityName} records from your selected source to Algolia.`;
2799
- return serializePrompt(async () => {
2800
- revokeCommandApprovals(ctx, command, resolvedCwd.target);
2801
- const isFile = await stat2(resolved2.target).then(
2802
- (s) => s.isFile(),
2803
- () => false
2804
- );
2805
- if (!isFile) {
2806
- return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
2807
- }
2808
- const reviewedDigest = await fileDigest(resolved2.target).catch(
2809
- () => null
2810
- );
2811
- if (reviewedDigest === null) {
2812
- return `Refused: ${filePath} could not be read. Write the script first, then review it again.`;
2813
- }
2814
- const answer = await useWizard.getState().requestUserInput({
2815
- prompt: "Run this command?",
2816
- promptType: "commandApproval",
2817
- options: [],
2818
- messages: [
2819
- `We have created your ingestion script. Please review it at ${displayFilePath} before approving.`
2820
- ],
2821
- command: {
2822
- command,
2823
- cwd: displayCwd,
2824
- explanation: ingestionSummary
2825
- }
2826
- });
2827
- if (answer !== "approve") {
2828
- revokeCommandApprovals(ctx, command, resolvedCwd.target);
2829
- return "The user declined to run this ingestion script. Do not run it.";
2830
- }
2831
- const approvedDigest = await fileDigest(resolved2.target).catch(
2832
- () => null
2833
- );
2834
- if (approvedDigest !== reviewedDigest) {
2835
- return "The ingestion script changed while it was being reviewed. Call reviewScript again before running it.";
2836
- }
2837
- ctx.reviewed.push(resolved2.target);
2838
- grantReviewedCommandApproval(ctx, {
2839
- command,
2840
- cwd: resolvedCwd.target,
2841
- filePath: resolved2.target,
2842
- digest: reviewedDigest
2843
- });
2844
- return "The user reviewed the script and approved this exact command. Run it now with the same cwd.";
2845
- });
2846
- }
2847
- });
2848
- }
2849
-
2850
2715
  // src/lib/tools/utils/runShell.ts
2851
2716
  import { spawn as spawn2 } from "node:child_process";
2852
2717
 
@@ -2945,7 +2810,7 @@ function storeApproval(root) {
2945
2810
  return async (req) => {
2946
2811
  const store = useWizard.getState();
2947
2812
  if (store.isCommandApproved(req.command, req.cwd)) return "approve";
2948
- const rel = relative5(root, req.cwd);
2813
+ const rel = relative4(root, req.cwd);
2949
2814
  const answer = await store.requestUserInput({
2950
2815
  prompt: "Run this command?",
2951
2816
  promptType: "commandApproval",
@@ -2960,7 +2825,6 @@ function storeApproval(root) {
2960
2825
  return "approve";
2961
2826
  };
2962
2827
  }
2963
- var SUCCESS_EXIT_CODE = 0;
2964
2828
  var FILE_IO_COMMANDS = /* @__PURE__ */ new Set([
2965
2829
  "ls",
2966
2830
  "find",
@@ -3099,9 +2963,9 @@ function fastPathSafety(command) {
3099
2963
  return void 0;
3100
2964
  }
3101
2965
  var CLASSIFIER_MODEL = "claude-haiku-4-5";
3102
- var commandSafetySchema = z16.object({
3103
- safe: z16.boolean(),
3104
- reason: z16.string().describe("One short sentence explaining the verdict.")
2966
+ var commandSafetySchema = z15.object({
2967
+ safe: z15.boolean(),
2968
+ reason: z15.string().describe("One short sentence explaining the verdict.")
3105
2969
  });
3106
2970
  function defaultCreateModel() {
3107
2971
  const token = getAuthToken();
@@ -3161,15 +3025,12 @@ async function classifyCommandSafety(createModel, command, cwd, explanation, app
3161
3025
  return false;
3162
3026
  }
3163
3027
  }
3164
- async function resolveShellEnv(ctx, command) {
3165
- return ctx.shell.env().catch((err) => {
3028
+ async function runAndRecord(ctx, command, cwd) {
3029
+ useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
3030
+ const env = await ctx.shell.env().catch((err) => {
3166
3031
  logger.warn({ err, command }, "runShell: could not resolve command env");
3167
3032
  return {};
3168
3033
  });
3169
- }
3170
- async function runAndRecord(ctx, command, cwd, resolvedEnv) {
3171
- useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
3172
- const env = resolvedEnv ?? await resolveShellEnv(ctx, command);
3173
3034
  const run2 = await (ctx.shell.run ?? runShell)(command, {
3174
3035
  cwd,
3175
3036
  env,
@@ -3200,19 +3061,6 @@ async function runAndRecord(ctx, command, cwd, resolvedEnv) {
3200
3061
  output: run2.output
3201
3062
  };
3202
3063
  }
3203
- async function runReviewedCommand(ctx, command, cwd) {
3204
- const approval = getReviewedCommandApproval(ctx, command, cwd);
3205
- if (!approval) return void 0;
3206
- const env = await resolveShellEnv(ctx, command);
3207
- if (!await reviewedCommandApprovalIsCurrent(approval)) {
3208
- return `Refused: ${relative5(ctx.root, approval.filePath)} changed after review. Call reviewScript again before running it.`;
3209
- }
3210
- const result = await runAndRecord(ctx, command, cwd, env);
3211
- if (result.exitCode === SUCCESS_EXIT_CODE && !result.timedOut) {
3212
- clearReviewedCommandApproval(ctx);
3213
- }
3214
- return result;
3215
- }
3216
3064
  async function approveAndRun(ctx, command, cwd, explanation) {
3217
3065
  const decision = await ctx.shell.approve({ command, cwd, explanation });
3218
3066
  if (decision === "reject") {
@@ -3223,16 +3071,16 @@ async function approveAndRun(ctx, command, cwd, explanation) {
3223
3071
  return runAndRecord(ctx, command, cwd);
3224
3072
  }
3225
3073
  function runShellTool(ctx, createModel = defaultCreateModel) {
3226
- return tool9({
3074
+ return tool8({
3227
3075
  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.",
3228
- inputSchema: z16.object({
3229
- command: z16.string().describe(
3076
+ inputSchema: z15.object({
3077
+ command: z15.string().describe(
3230
3078
  "The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
3231
3079
  ),
3232
- cwd: z16.string().optional().describe(
3080
+ cwd: z15.string().optional().describe(
3233
3081
  "Directory to run in, relative to the project root. Defaults to the project root."
3234
3082
  ),
3235
- explanation: z16.string().describe(
3083
+ explanation: z15.string().describe(
3236
3084
  "One short line telling the user what this command does and why, including any side effect (e.g. writes records to Algolia). This is what they approve against."
3237
3085
  )
3238
3086
  }),
@@ -3241,22 +3089,16 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
3241
3089
  return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
3242
3090
  }
3243
3091
  const resolved2 = resolveInRoot(ctx, cwd ?? ".");
3244
- if (resolved2.ok === false) return resolved2.error;
3092
+ if (!resolved2.ok) return resolved2.error;
3245
3093
  if (isFileIOCommand(command)) {
3246
3094
  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).";
3247
3095
  }
3248
3096
  logger.info({ command, cwd: resolved2.target }, "called runShell tool");
3249
- const reviewed = await serializePrompt(
3250
- () => runReviewedCommand(ctx, command, resolved2.target)
3251
- );
3252
- if (reviewed !== void 0) return reviewed;
3253
3097
  const fast = fastPathSafety(command);
3254
3098
  let isSafe = fast;
3255
- let usedStoredApproval = false;
3256
3099
  if (isSafe === void 0) {
3257
3100
  const store = useWizard.getState();
3258
- usedStoredApproval = store.isCommandApproved(command, resolved2.target);
3259
- isSafe = usedStoredApproval;
3101
+ isSafe = store.isCommandApproved(command, resolved2.target);
3260
3102
  if (!isSafe) {
3261
3103
  isSafe = await classifyCommandSafety(
3262
3104
  createModel,
@@ -3267,18 +3109,54 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
3267
3109
  );
3268
3110
  }
3269
3111
  }
3270
- return serializePrompt(async () => {
3271
- const lateReview = await runReviewedCommand(
3272
- ctx,
3273
- command,
3274
- resolved2.target
3112
+ if (isSafe) {
3113
+ return serializePrompt(
3114
+ () => runAndRecord(ctx, command, resolved2.target)
3275
3115
  );
3276
- if (lateReview !== void 0) return lateReview;
3277
- const storedApprovalIsCurrent = !usedStoredApproval || useWizard.getState().isCommandApproved(command, resolved2.target);
3278
- if (isSafe && storedApprovalIsCurrent) {
3279
- return runAndRecord(ctx, command, resolved2.target);
3280
- }
3281
- return approveAndRun(ctx, command, resolved2.target, explanation);
3116
+ }
3117
+ return serializePrompt(
3118
+ () => approveAndRun(ctx, command, resolved2.target, explanation)
3119
+ );
3120
+ }
3121
+ });
3122
+ }
3123
+
3124
+ // src/lib/tools/reviewScript.ts
3125
+ import { tool as tool9 } from "ai";
3126
+ import z16 from "zod";
3127
+ import { stat as stat2 } from "node:fs/promises";
3128
+ import { relative as relative5 } from "node:path";
3129
+ function reviewScriptTool(ctx) {
3130
+ return tool9({
3131
+ description: "Call this as soon as the ingestion script is finished and before you run it, so the user can review it.It shows the file path and returns once they have confirmed reviewing it.",
3132
+ inputSchema: z16.object({
3133
+ filePath: z16.string().describe(
3134
+ "Path to the script to review, relative to the project root."
3135
+ )
3136
+ }),
3137
+ execute: async ({ filePath }) => {
3138
+ logger.info({ filePath }, "called reviewScript tool");
3139
+ const resolved2 = resolveInRoot(ctx, filePath);
3140
+ if (resolved2.ok === false) return resolved2.error;
3141
+ const isFile = await stat2(resolved2.target).then(
3142
+ (s) => s.isFile(),
3143
+ () => false
3144
+ );
3145
+ if (!isFile) {
3146
+ return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
3147
+ }
3148
+ return serializePrompt(async () => {
3149
+ await useWizard.getState().requestUserInput({
3150
+ prompt: "",
3151
+ promptType: "enterToContinue",
3152
+ options: [],
3153
+ messages: [
3154
+ `Please review ${relative5(ctx.root, resolved2.target)} before it runs:`,
3155
+ resolved2.target
3156
+ ]
3157
+ });
3158
+ ctx.reviewed.push(resolved2.target);
3159
+ return "The user has reviewed the script. You may run it now.";
3282
3160
  });
3283
3161
  }
3284
3162
  });
@@ -3773,7 +3651,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3773
3651
  // package.json
3774
3652
  var package_default = {
3775
3653
  name: "@algolia/wizard",
3776
- version: "0.52.0-rc.136.364",
3654
+ version: "0.52.0-rc.151.365",
3777
3655
  description: "Magically implement Algolia functionality in your codebase",
3778
3656
  type: "module",
3779
3657
  engines: {
@@ -4475,8 +4353,8 @@ function ingestionInstructions(input) {
4475
4353
  ...algoliaClientDoc(input),
4476
4354
  "Install the Algolia client with the project's own package manager via runShell, declaring it in whatever manifest the project uses (e.g. package.json, requirements.txt, Gemfile, go.mod, composer.json) so the dependency is not just installed ad hoc.",
4477
4355
  `If the script loads its env vars from a file (e.g. via dotenv or an equivalent for its language) rather than the process environment directly, decide that up front and call writeCredentials on that file before you finish the script \u2014 do not wait to discover the need for it by having a writeFile call refused.`,
4478
- `When the script is finished, decide the exact command and cwd you will use to run it, then call reviewScript with that filePath, command, cwd, the exact selected entityName, and usesGeneratedRecords set to ${input.ingestionSource === "generated"}. Wait for the developer to review it because running it writes records to a live index. Do not run it before that call returns.`,
4479
- 'Then, without changing directories, run the script yourself via runShell using the exact same command and cwd. Report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
4356
+ "When the script is finished, call reviewScript with its path and wait: running it writes records to a live index, so the developer reads it first. Do not run it before that call returns.",
4357
+ 'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
4480
4358
  "The summary should be extremely concise.",
4481
4359
  ...sourceSpecificInstructions(input)
4482
4360
  ] : []
@@ -4856,33 +4734,19 @@ ${detail}` : ""}`
4856
4734
  product_area: "AI Wizard"
4857
4735
  });
4858
4736
  }
4859
- if (ingestScriptRan) {
4860
- await ctx.requestUserInput({
4861
- prompt: "Continue",
4862
- promptType: "notice",
4863
- options: [],
4864
- messages: [
4865
- ingestOutcomeMessage,
4866
- "View your ingested records in the Algolia dashboard:",
4867
- "https://dashboard.algolia.com/explorer/browse",
4868
- "When you're ready to continue, we'll move on to search implementation."
4869
- ]
4870
- });
4871
- } else {
4872
- const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
4873
- await ctx.requestUserInput({
4874
- prompt: "",
4875
- promptType: "enterToContinue",
4876
- options: [],
4877
- // One message per line: Notices.tsx counts a message as one wrapped
4878
- // line, so an embedded newline overflows the panel's height accounting.
4879
- messages: [
4880
- ingestOutcomeMessage,
4881
- ...ingestFailureDetail?.split("\n").filter((l) => l.trim()) ?? [],
4882
- ...commandMessages
4883
- ]
4884
- });
4885
- }
4737
+ const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
4738
+ await ctx.requestUserInput({
4739
+ prompt: "",
4740
+ promptType: "enterToContinue",
4741
+ options: [],
4742
+ // One message per line: Notices.tsx counts a message as one wrapped
4743
+ // line, so an embedded newline overflows the panel's height accounting.
4744
+ messages: [
4745
+ ingestOutcomeMessage,
4746
+ ...ingestFailureDetail?.split("\n").filter((l) => l.trim()) ?? [],
4747
+ ...commandMessages
4748
+ ]
4749
+ });
4886
4750
  }
4887
4751
  let searchConfigFile;
4888
4752
  if (useCases.includes("search")) {
@@ -6240,7 +6104,7 @@ function App() {
6240
6104
  }
6241
6105
 
6242
6106
  // src/lib/envAppId.ts
6243
- import { readFile as readFile8 } from "node:fs/promises";
6107
+ import { readFile as readFile7 } from "node:fs/promises";
6244
6108
  import { join as join12 } from "node:path";
6245
6109
  var ENV_FILES = [".env", ".env.local"];
6246
6110
  var APP_ID_LINE = /^[ \t]*(?:export[ \t]+)?([A-Z0-9_]*ALGOLIA_APP(?:LICATION)?_ID)[ \t]*=[ \t]*(.*)$/gm;
@@ -6248,7 +6112,7 @@ async function findEnvApplicationId(root = process.cwd()) {
6248
6112
  for (const file of ENV_FILES) {
6249
6113
  let content;
6250
6114
  try {
6251
- content = await readFile8(join12(root, file), "utf8");
6115
+ content = await readFile7(join12(root, file), "utf8");
6252
6116
  } catch (err) {
6253
6117
  if (err.code !== "ENOENT") {
6254
6118
  logger.warn(
@@ -6598,8 +6462,118 @@ function delay(ms) {
6598
6462
  });
6599
6463
  }
6600
6464
 
6465
+ // package.json with { type: 'json' }
6466
+ var package_default2 = {
6467
+ name: "@algolia/wizard",
6468
+ version: "0.52.0-rc.151.365",
6469
+ description: "Magically implement Algolia functionality in your codebase",
6470
+ type: "module",
6471
+ engines: {
6472
+ node: ">=24"
6473
+ },
6474
+ bin: {
6475
+ wizard: "dist/main.js"
6476
+ },
6477
+ files: [
6478
+ "dist",
6479
+ "docs"
6480
+ ],
6481
+ scripts: {
6482
+ "build:proxy": "node scripts/build.mjs proxy",
6483
+ build: "node scripts/build.mjs",
6484
+ "dev:proxy": "touch .env && NODE_OPTIONS=--use-system-ca tsx watch --env-file=.env src/proxy/index.ts",
6485
+ dev: "touch .env && tsx --env-file=.env ./src/main.tsx",
6486
+ "env:load": "pnpm exec -- varlock load",
6487
+ prepare: "husky",
6488
+ prepublishOnly: "pnpm build",
6489
+ reset: "tsx ./scripts/reset-state.ts",
6490
+ "test:fixtures": "touch .env && tsx --env-file=.env ./fixtures/run-fixtures.ts",
6491
+ "test:tools": "tsx ./tool-evals/toolEval.ts",
6492
+ "test:tools:improve": "tsx ./tool-evals/improveFromPlan.ts",
6493
+ test: "vitest",
6494
+ typecheck: "tsc --noEmit -p tsconfig.json"
6495
+ },
6496
+ keywords: [],
6497
+ author: "",
6498
+ license: "ISC",
6499
+ packageManager: "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
6500
+ devDependencies: {
6501
+ "@clack/prompts": "^1.6.0",
6502
+ "@types/ioredis-mock": "^8.2.7",
6503
+ "@types/node": "^25.9.1",
6504
+ "@types/react": "^19.2.16",
6505
+ esbuild: "^0.28.0",
6506
+ husky: "^9.1.7",
6507
+ "ink-testing-library": "^4.0.0",
6508
+ "ioredis-mock": "^8.13.1",
6509
+ "lint-staged": "^17.0.8",
6510
+ tsx: "^4.22.3",
6511
+ typescript: "^6.0.3",
6512
+ vitest: "^4.1.8"
6513
+ },
6514
+ dependencies: {
6515
+ "@ai-sdk/anthropic": "^3.0.81",
6516
+ "@ai-sdk/openai-compatible": "^2.0.47",
6517
+ "@hono/node-server": "^2.0.10",
6518
+ "@segment/analytics-node": "^3.1.0",
6519
+ ai: "^6.0.190",
6520
+ "cross-keychain": "^1.1.0",
6521
+ dotenv: "^17.4.2",
6522
+ hono: "^4.12.27",
6523
+ ink: "^7.0.5",
6524
+ "ink-picture": "^2.1.0",
6525
+ "ink-spinner": "^5.0.0",
6526
+ ioredis: "^5.11.1",
6527
+ nanoid: "^5.1.15",
6528
+ pino: "^10.3.1",
6529
+ react: "^19.2.7",
6530
+ "terminal-link": "^5.0.0",
6531
+ varlock: "^1.5.1",
6532
+ zod: "^4.4.3",
6533
+ zustand: "^5.0.14"
6534
+ }
6535
+ };
6536
+
6537
+ // src/lib/checkVersion.ts
6538
+ function parseVersion(version) {
6539
+ const [major = 0, minor = 0, patch = 0] = version.split("-")[0].split(".").map((part) => Number.parseInt(part, 10) || 0);
6540
+ return [major, minor, patch];
6541
+ }
6542
+ function isOlder(local, remote) {
6543
+ const a = parseVersion(local);
6544
+ const b = parseVersion(remote);
6545
+ for (let i = 0; i < 3; i++) {
6546
+ if (a[i] !== b[i]) return a[i] < b[i];
6547
+ }
6548
+ return false;
6549
+ }
6550
+ async function checkVersion() {
6551
+ try {
6552
+ const res = await fetch(
6553
+ "https://registry.npmjs.org/@algolia/wizard/latest",
6554
+ { signal: AbortSignal.timeout(3e3) }
6555
+ );
6556
+ if (!res.ok) {
6557
+ throw Error(`npmjs registry responded ${res.status}`);
6558
+ }
6559
+ const { version } = await res.json();
6560
+ if (typeof version !== "string") {
6561
+ throw Error("npmjs registry returned no version");
6562
+ }
6563
+ return isOlder(package_default2.version, version);
6564
+ } catch {
6565
+ logger.error("Could not resolve remote version number from npmjs registry");
6566
+ return false;
6567
+ }
6568
+ }
6569
+
6601
6570
  // src/main.tsx
6602
6571
  import { jsx as jsx20 } from "react/jsx-runtime";
6572
+ async function exitProcessWithError(store, instance, message) {
6573
+ store.setError(message);
6574
+ await instance.waitUntilExit();
6575
+ process.exit(1);
6576
+ }
6603
6577
  async function startup() {
6604
6578
  setProjectRoot(process.cwd());
6605
6579
  let args;
@@ -6652,6 +6626,14 @@ async function run(opts) {
6652
6626
  const store = useWizard.getState();
6653
6627
  const instance = render(/* @__PURE__ */ jsx20(App, {}), { incrementalRendering: true });
6654
6628
  await store.waitForStart();
6629
+ const wizardOutdated = await checkVersion();
6630
+ if (wizardOutdated) {
6631
+ await exitProcessWithError(
6632
+ store,
6633
+ instance,
6634
+ "You have loaded an old version of the Wizard. Please use @algolia/wizard@latest"
6635
+ );
6636
+ }
6655
6637
  store.syncSteps(
6656
6638
  workflow.steps.map((s) => ({
6657
6639
  id: s.id,
@@ -6667,18 +6649,20 @@ async function run(opts) {
6667
6649
  try {
6668
6650
  await runAuthLogin();
6669
6651
  } catch (err) {
6670
- store.setError(err instanceof Error ? err.message : String(err));
6671
- await instance.waitUntilExit();
6672
- process.exit(1);
6652
+ await exitProcessWithError(
6653
+ store,
6654
+ instance,
6655
+ err instanceof Error ? err.message : String(err)
6656
+ );
6673
6657
  }
6674
6658
  store.endAuth();
6675
6659
  user = await getUser();
6676
6660
  if (!user) {
6677
- store.setError(
6661
+ await exitProcessWithError(
6662
+ store,
6663
+ instance,
6678
6664
  "Authentication completed but no Algolia user was returned. Try running `npx @algolia/cli@latest auth login` directly."
6679
6665
  );
6680
- await instance.waitUntilExit();
6681
- process.exit(1);
6682
6666
  }
6683
6667
  }
6684
6668
  store.setUser(user);
@@ -6688,12 +6672,14 @@ async function run(opts) {
6688
6672
  app = await ensureApplication(
6689
6673
  resumableState ? runAppId(resumableState) : void 0
6690
6674
  );
6675
+ runWorkflow(workflow, app.id, { skipResumePrompt, resumableState });
6691
6676
  } catch (err) {
6692
- store.setError(err instanceof Error ? err.message : String(err));
6693
- await instance.waitUntilExit();
6694
- process.exit(1);
6677
+ await exitProcessWithError(
6678
+ store,
6679
+ instance,
6680
+ err instanceof Error ? err.message : String(err)
6681
+ );
6695
6682
  }
6696
- runWorkflow(workflow, app.id, { skipResumePrompt, resumableState });
6697
6683
  }
6698
6684
  await requestTerminalSize();
6699
6685
  var started = await startup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.52.0-rc.136.364",
3
+ "version": "0.52.0-rc.151.365",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {