@algolia/wizard 0.120.0 → 0.121.0

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 +106 -19
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -325,6 +325,7 @@ var useWizard = create((set, get) => ({
325
325
  logs: [],
326
326
  _outputLogCount: 0,
327
327
  error: null,
328
+ exitRequested: false,
328
329
  inputReq: null,
329
330
  _resolve: null,
330
331
  settingUpAppId: null,
@@ -488,7 +489,15 @@ var useWizard = create((set, get) => ({
488
489
  get().logEnd(id, "success");
489
490
  },
490
491
  setDone: () => set({ phase: "done", loadingMessage: null }),
491
- setError: (message) => set({ phase: "error", error: message, loadingMessage: null }),
492
+ setError: (message, options) => {
493
+ if (options?.exit) get()._clearNoticeQueue();
494
+ set({
495
+ phase: "error",
496
+ error: message,
497
+ loadingMessage: null,
498
+ exitRequested: options?.exit ?? false
499
+ });
500
+ },
492
501
  reset: () => {
493
502
  get()._clearNoticeQueue();
494
503
  get()._resolve?.(false);
@@ -508,6 +517,7 @@ var useWizard = create((set, get) => ({
508
517
  logs: [],
509
518
  _outputLogCount: 0,
510
519
  error: null,
520
+ exitRequested: false,
511
521
  inputReq: null,
512
522
  _resolve: null,
513
523
  settingUpAppId: null,
@@ -1460,12 +1470,13 @@ function EnterToContinuePrompt({
1460
1470
  ] });
1461
1471
  }
1462
1472
  function PromptInput() {
1463
- const { phase, inputReq, submitInput } = useWizard();
1473
+ const { phase, inputReq, submitInput, exitRequested } = useWizard();
1464
1474
  const [draft, setDraft] = useState9("");
1465
1475
  const [submittedReq, setSubmittedReq] = useState9(null);
1466
1476
  const promptKey = inputReq && `${inputReq.promptType}:${inputReq.prompt}`;
1467
1477
  const submitting = inputReq != null && submittedReq === inputReq;
1468
1478
  if (phase === "done" || phase === "error") {
1479
+ if (exitRequested) return null;
1469
1480
  return /* @__PURE__ */ jsx10(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text12, { color: "gray", dimColor: true, children: "Press Enter or Esc to exit" }) });
1470
1481
  }
1471
1482
  if (phase !== "awaitingInput" || !inputReq) return null;
@@ -2544,10 +2555,24 @@ async function ensureApplication(resumeAppId) {
2544
2555
  }
2545
2556
  }
2546
2557
 
2558
+ // src/lib/rateLimit.ts
2559
+ var RateLimitError = class extends Error {
2560
+ constructor(retryAfterSeconds, options) {
2561
+ super(rateLimitMessage(retryAfterSeconds), options);
2562
+ this.retryAfterSeconds = retryAfterSeconds;
2563
+ this.name = "RateLimitError";
2564
+ }
2565
+ retryAfterSeconds;
2566
+ };
2567
+ function rateLimitMessage(retryAfterSeconds) {
2568
+ const when = retryAfterSeconds === void 0 ? "in a moment" : `in ${retryAfterSeconds} ${retryAfterSeconds === 1 ? "second" : "seconds"}`;
2569
+ return `You've hit the rate limit. Try again ${when}.`;
2570
+ }
2571
+
2547
2572
  // package.json
2548
2573
  var package_default = {
2549
2574
  name: "@algolia/wizard",
2550
- version: "0.120.0",
2575
+ version: "0.121.0",
2551
2576
  description: "Magically implement Algolia functionality in your codebase",
2552
2577
  type: "module",
2553
2578
  engines: {
@@ -2638,6 +2663,16 @@ var RestartWorkflowSignal = class extends Error {
2638
2663
  }
2639
2664
  appId;
2640
2665
  };
2666
+ var ExitWizardSignal = class extends Error {
2667
+ constructor(original) {
2668
+ super(original instanceof Error ? original.message : String(original), {
2669
+ cause: original
2670
+ });
2671
+ this.original = original;
2672
+ this.name = "ExitWizardSignal";
2673
+ }
2674
+ original;
2675
+ };
2641
2676
  function ensureExecutedStepCount(state) {
2642
2677
  if (state.executedStepCount == null) {
2643
2678
  state.executedStepCount = state.steps.filter(
@@ -2828,6 +2863,29 @@ async function rewindTo(state, targetIndex, fromIndex) {
2828
2863
  store.syncSteps([...state.steps], targetIndex);
2829
2864
  await persistState(state);
2830
2865
  }
2866
+ var RATE_LIMIT_RETRY_OPTION = "Retry";
2867
+ var RATE_LIMIT_EXIT_OPTION = "Exit";
2868
+ async function runStepWithRateLimitRecovery(state, step, ctx) {
2869
+ for (; ; ) {
2870
+ try {
2871
+ return await step.run(ctx);
2872
+ } catch (err) {
2873
+ if (!(err instanceof RateLimitError)) throw err;
2874
+ const choice = await useWizard.getState().requestUserInput({
2875
+ prompt: "Retry the step, or exit the wizard?",
2876
+ promptType: "multipleChoice",
2877
+ options: [RATE_LIMIT_RETRY_OPTION, RATE_LIMIT_EXIT_OPTION],
2878
+ alert: {
2879
+ variant: "error",
2880
+ title: "Too many requests",
2881
+ message: err.message
2882
+ }
2883
+ });
2884
+ if (choice !== RATE_LIMIT_RETRY_OPTION) throw new ExitWizardSignal(err);
2885
+ ctx = await makeContext(state, true);
2886
+ }
2887
+ }
2888
+ }
2831
2889
  async function runStep(state, index, step) {
2832
2890
  const store = useWizard.getState();
2833
2891
  const record = state.steps[index];
@@ -2854,7 +2912,7 @@ async function runStep(state, index, step) {
2854
2912
  const stopSavingCost = saveCostDuringStep(state);
2855
2913
  let output;
2856
2914
  try {
2857
- const raw = await step.run(ctx);
2915
+ const raw = await runStepWithRateLimitRecovery(state, step, ctx);
2858
2916
  output = step.outputSchema.parse(raw);
2859
2917
  await step.onStepComplete?.(output, ctx);
2860
2918
  } catch (err) {
@@ -3014,7 +3072,9 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
3014
3072
  await recordWorkflowRun(workflow.id, nowIso());
3015
3073
  await clearWorkflowState(workflow.id);
3016
3074
  store.setDone();
3017
- } catch (err) {
3075
+ } catch (thrown) {
3076
+ const exitRequested = thrown instanceof ExitWizardSignal;
3077
+ const err = exitRequested ? thrown.original : thrown;
3018
3078
  const message = err instanceof Error ? err.message : String(err);
3019
3079
  const failed = await loadWorkflowState(workflow.id);
3020
3080
  const failedAppId = failed ? runAppId(failed) : startWorkflowAppId;
@@ -3063,7 +3123,7 @@ async function runWorkflow(workflow, startWorkflowAppId, options) {
3063
3123
  ...errorType ? { error_type: errorType } : {},
3064
3124
  ...errorCode2 ? { error_code: errorCode2 } : {}
3065
3125
  });
3066
- store.setError(message);
3126
+ store.setError(message, { exit: exitRequested });
3067
3127
  }
3068
3128
  }
3069
3129
 
@@ -5559,6 +5619,7 @@ var GLOBAL_BUDGET_USER_MESSAGE = "The wizard reached its daily AI usage limit fo
5559
5619
  var PROXY_AUTH_REJECTED_HEADER2 = "x-wizard-proxy-auth";
5560
5620
  var PROXY_REJECTION_BODY = "You shall not pass!";
5561
5621
  var BUDGET_EXCEEDED_HEADER = "x-wizard-budget-exceeded";
5622
+ var RETRY_AFTER_HEADER = "x-wizard-retry-after";
5562
5623
  function lastCallError(err) {
5563
5624
  const cause = RetryError.isInstance(err) ? err.lastError : err;
5564
5625
  return APICallError.isInstance(cause) ? cause : void 0;
@@ -5574,6 +5635,17 @@ function budgetExceededMessage(err) {
5574
5635
  if (scope === "global") return GLOBAL_BUDGET_USER_MESSAGE;
5575
5636
  return void 0;
5576
5637
  }
5638
+ function rateLimitError(err) {
5639
+ const cause = lastCallError(err);
5640
+ if (cause?.statusCode !== 429) return void 0;
5641
+ const headers = cause.responseHeaders;
5642
+ const seconds = Number(
5643
+ headers?.[RETRY_AFTER_HEADER] ?? headers?.["retry-after"]
5644
+ );
5645
+ return new RateLimitError(seconds > 0 ? Math.ceil(seconds) : void 0, {
5646
+ cause: err
5647
+ });
5648
+ }
5577
5649
  var INCOMPLETE_CAUSES = {
5578
5650
  stepLimit: {
5579
5651
  kind: "missingReportAtStepLimit",
@@ -5799,7 +5871,7 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
5799
5871
  ...continuation.messages,
5800
5872
  { role: "user", content: ENDED_TURN_NUDGE }
5801
5873
  ] : [{ role: "user", content: INITIAL_PROMPT }];
5802
- const stream = await agent.stream({ prompt });
5874
+ const stream = await agent.stream({ prompt, abortSignal: req.abortSignal });
5803
5875
  let chunks = [];
5804
5876
  const chunkLimit = 5;
5805
5877
  let streamError;
@@ -5820,6 +5892,10 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
5820
5892
  if (chunks.length) {
5821
5893
  logger.debug(chunks.join(""));
5822
5894
  }
5895
+ if (req.abortSignal?.aborted) {
5896
+ logger.info({ operation: req.operation }, "runAgent aborted");
5897
+ req.abortSignal.throwIfAborted();
5898
+ }
5823
5899
  if (streamError !== void 0) {
5824
5900
  logger.error({ err: streamError }, "agent stream reported an error");
5825
5901
  if (isProxyInactivityRejection(streamError)) {
@@ -5827,7 +5903,7 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
5827
5903
  }
5828
5904
  const budgetMessage = budgetExceededMessage(streamError);
5829
5905
  if (budgetMessage) throw new Error(budgetMessage, { cause: streamError });
5830
- throw streamError;
5906
+ throw rateLimitError(streamError) ?? streamError;
5831
5907
  }
5832
5908
  const end = Date.now();
5833
5909
  const usage = await stream.totalUsage;
@@ -5960,9 +6036,10 @@ function prioritizeFrontendFrameworks(result) {
5960
6036
  );
5961
6037
  return { ...result, frameworks: [...frontend, ...remaining] };
5962
6038
  }
5963
- var detectLanguage = async () => {
6039
+ var detectLanguage = async (abortSignal) => {
5964
6040
  const result = await runAgent({
5965
6041
  operation: "language-detection",
6042
+ abortSignal,
5966
6043
  instructions: [
5967
6044
  "Analyze the codebase and determine the programming languages and frameworks used",
5968
6045
  "If a superset language is found, exclude the subset language (e.g. TypeScript over JavaScript).",
@@ -6058,20 +6135,21 @@ var MODE_CONFIG = {
6058
6135
  outputSchema: verificationSchema
6059
6136
  }
6060
6137
  };
6061
- function runMode(mode, extraInstructions = []) {
6138
+ function runMode(mode, extraInstructions = [], abortSignal) {
6062
6139
  const { instructions, outputSchema } = MODE_CONFIG[mode];
6063
6140
  return runAgent({
6064
6141
  operation: `code-analysis-${mode}`,
6065
6142
  instructions: [...instructions, ...extraInstructions],
6066
6143
  tools: READONLY_TOOLS,
6067
6144
  outputSchema,
6068
- modelProfile: "analysis" /* analysis */
6145
+ modelProfile: "analysis" /* analysis */,
6146
+ abortSignal
6069
6147
  });
6070
6148
  }
6071
- async function runAnalysis(mode, extraInstructions = []) {
6149
+ async function runAnalysis(mode, extraInstructions = [], abortSignal) {
6072
6150
  const modes = mode ? [mode] : ["ingestion", "searchImplementation", "verification"];
6073
6151
  const results = await Promise.all(
6074
- modes.map((m) => runMode(m, extraInstructions))
6152
+ modes.map((m) => runMode(m, extraInstructions, abortSignal))
6075
6153
  );
6076
6154
  return Object.assign({}, ...results);
6077
6155
  }
@@ -6085,9 +6163,14 @@ var projectScanSchema = detectLanguageSchema.extend({
6085
6163
  });
6086
6164
  async function projectScan(ctx) {
6087
6165
  const startedAt = Date.now();
6166
+ const controller = new AbortController();
6167
+ const abortOthersOnFailure = (run2) => run2.catch((err) => {
6168
+ controller.abort();
6169
+ throw err;
6170
+ });
6088
6171
  const [detected, analyses] = await Promise.all([
6089
- detectLanguage(),
6090
- runAnalysis()
6172
+ abortOthersOnFailure(detectLanguage(controller.signal)),
6173
+ abortOthersOnFailure(runAnalysis(void 0, [], controller.signal))
6091
6174
  ]);
6092
6175
  const entities = toEntitySummary(analyses.ingestionAnalysis ?? []);
6093
6176
  track("AI Wizard Scan Completed", {
@@ -10501,7 +10584,7 @@ function Ribbon() {
10501
10584
  }
10502
10585
 
10503
10586
  // src/ui/App.tsx
10504
- import { useState as useState14 } from "react";
10587
+ import { useEffect as useEffect5, useState as useState14 } from "react";
10505
10588
 
10506
10589
  // src/ui/Logs.tsx
10507
10590
  import { Box as Box21, Text as Text22, useInput as useInput9 } from "ink";
@@ -11135,9 +11218,13 @@ function App() {
11135
11218
  workflow,
11136
11219
  settingUpAppId,
11137
11220
  loadingMessage,
11138
- review: review2
11221
+ review: review2,
11222
+ exitRequested
11139
11223
  } = useWizard();
11140
11224
  const { exit } = useApp2();
11225
+ useEffect5(() => {
11226
+ if (exitRequested) exit();
11227
+ }, [exitRequested, exit]);
11141
11228
  const { columns, rows } = useWindowSize8();
11142
11229
  useRepaintOnResize();
11143
11230
  const compactRun = isCompactTerminal(columns, rows);
@@ -11158,7 +11245,7 @@ function App() {
11158
11245
  const holdForTip = !compactRun && tipsActive && isAwaitingUserInput && tipState === "typing";
11159
11246
  const showTips = !compactRun && tipsActive && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || holdForTip);
11160
11247
  const showNoticesInMain = !tipsActive;
11161
- const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2;
11248
+ const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2 && !exitRequested;
11162
11249
  useInput10(
11163
11250
  (_input, key) => {
11164
11251
  if (key.return) {
@@ -11572,7 +11659,7 @@ async function promptResumeOrStartNew(session, next) {
11572
11659
  // package.json with { type: 'json' }
11573
11660
  var package_default2 = {
11574
11661
  name: "@algolia/wizard",
11575
- version: "0.120.0",
11662
+ version: "0.121.0",
11576
11663
  description: "Magically implement Algolia functionality in your codebase",
11577
11664
  type: "module",
11578
11665
  engines: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.120.0",
3
+ "version": "0.121.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {