@algolia/wizard 0.120.0 → 0.122.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.
- package/dist/main.js +117 -22
- 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) =>
|
|
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.
|
|
2575
|
+
version: "0.122.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
|
|
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 (
|
|
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",
|
|
@@ -5612,11 +5684,19 @@ function incompleteCauseOf(stepCount, maxSteps, finishReason) {
|
|
|
5612
5684
|
if (finishReason !== "stop") return "stopped";
|
|
5613
5685
|
return null;
|
|
5614
5686
|
}
|
|
5687
|
+
var NO_FRONTEND_REASON = "no_frontend";
|
|
5688
|
+
var NO_FRONTEND_MESSAGE = "No frontend found in this folder. `cd` into the directory where your frontend lives (in a monorepo, the app folder, usually the one with that app's `package.json`), then rerun the Wizard.";
|
|
5689
|
+
function userFacingAgentFailure(reason) {
|
|
5690
|
+
if (reason?.trim().toLowerCase() === NO_FRONTEND_REASON) {
|
|
5691
|
+
return NO_FRONTEND_MESSAGE;
|
|
5692
|
+
}
|
|
5693
|
+
return `Agent reported failure: ${reason ?? "no reason given"}`;
|
|
5694
|
+
}
|
|
5615
5695
|
var AgentReportedFailureError = class extends Error {
|
|
5616
5696
|
reason;
|
|
5617
5697
|
output;
|
|
5618
5698
|
constructor(reason, output) {
|
|
5619
|
-
super(
|
|
5699
|
+
super(userFacingAgentFailure(reason));
|
|
5620
5700
|
this.reason = reason;
|
|
5621
5701
|
this.output = output;
|
|
5622
5702
|
}
|
|
@@ -5799,7 +5879,7 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
|
|
|
5799
5879
|
...continuation.messages,
|
|
5800
5880
|
{ role: "user", content: ENDED_TURN_NUDGE }
|
|
5801
5881
|
] : [{ role: "user", content: INITIAL_PROMPT }];
|
|
5802
|
-
const stream = await agent.stream({ prompt });
|
|
5882
|
+
const stream = await agent.stream({ prompt, abortSignal: req.abortSignal });
|
|
5803
5883
|
let chunks = [];
|
|
5804
5884
|
const chunkLimit = 5;
|
|
5805
5885
|
let streamError;
|
|
@@ -5820,6 +5900,10 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
|
|
|
5820
5900
|
if (chunks.length) {
|
|
5821
5901
|
logger.debug(chunks.join(""));
|
|
5822
5902
|
}
|
|
5903
|
+
if (req.abortSignal?.aborted) {
|
|
5904
|
+
logger.info({ operation: req.operation }, "runAgent aborted");
|
|
5905
|
+
req.abortSignal.throwIfAborted();
|
|
5906
|
+
}
|
|
5823
5907
|
if (streamError !== void 0) {
|
|
5824
5908
|
logger.error({ err: streamError }, "agent stream reported an error");
|
|
5825
5909
|
if (isProxyInactivityRejection(streamError)) {
|
|
@@ -5827,7 +5911,7 @@ async function runAgentAttempt(req, attempt, retryReason, continuation) {
|
|
|
5827
5911
|
}
|
|
5828
5912
|
const budgetMessage = budgetExceededMessage(streamError);
|
|
5829
5913
|
if (budgetMessage) throw new Error(budgetMessage, { cause: streamError });
|
|
5830
|
-
throw streamError;
|
|
5914
|
+
throw rateLimitError(streamError) ?? streamError;
|
|
5831
5915
|
}
|
|
5832
5916
|
const end = Date.now();
|
|
5833
5917
|
const usage = await stream.totalUsage;
|
|
@@ -5960,9 +6044,10 @@ function prioritizeFrontendFrameworks(result) {
|
|
|
5960
6044
|
);
|
|
5961
6045
|
return { ...result, frameworks: [...frontend, ...remaining] };
|
|
5962
6046
|
}
|
|
5963
|
-
var detectLanguage = async () => {
|
|
6047
|
+
var detectLanguage = async (abortSignal) => {
|
|
5964
6048
|
const result = await runAgent({
|
|
5965
6049
|
operation: "language-detection",
|
|
6050
|
+
abortSignal,
|
|
5966
6051
|
instructions: [
|
|
5967
6052
|
"Analyze the codebase and determine the programming languages and frameworks used",
|
|
5968
6053
|
"If a superset language is found, exclude the subset language (e.g. TypeScript over JavaScript).",
|
|
@@ -6058,20 +6143,21 @@ var MODE_CONFIG = {
|
|
|
6058
6143
|
outputSchema: verificationSchema
|
|
6059
6144
|
}
|
|
6060
6145
|
};
|
|
6061
|
-
function runMode(mode, extraInstructions = []) {
|
|
6146
|
+
function runMode(mode, extraInstructions = [], abortSignal) {
|
|
6062
6147
|
const { instructions, outputSchema } = MODE_CONFIG[mode];
|
|
6063
6148
|
return runAgent({
|
|
6064
6149
|
operation: `code-analysis-${mode}`,
|
|
6065
6150
|
instructions: [...instructions, ...extraInstructions],
|
|
6066
6151
|
tools: READONLY_TOOLS,
|
|
6067
6152
|
outputSchema,
|
|
6068
|
-
modelProfile: "analysis" /* analysis
|
|
6153
|
+
modelProfile: "analysis" /* analysis */,
|
|
6154
|
+
abortSignal
|
|
6069
6155
|
});
|
|
6070
6156
|
}
|
|
6071
|
-
async function runAnalysis(mode, extraInstructions = []) {
|
|
6157
|
+
async function runAnalysis(mode, extraInstructions = [], abortSignal) {
|
|
6072
6158
|
const modes = mode ? [mode] : ["ingestion", "searchImplementation", "verification"];
|
|
6073
6159
|
const results = await Promise.all(
|
|
6074
|
-
modes.map((m) => runMode(m, extraInstructions))
|
|
6160
|
+
modes.map((m) => runMode(m, extraInstructions, abortSignal))
|
|
6075
6161
|
);
|
|
6076
6162
|
return Object.assign({}, ...results);
|
|
6077
6163
|
}
|
|
@@ -6085,9 +6171,14 @@ var projectScanSchema = detectLanguageSchema.extend({
|
|
|
6085
6171
|
});
|
|
6086
6172
|
async function projectScan(ctx) {
|
|
6087
6173
|
const startedAt = Date.now();
|
|
6174
|
+
const controller = new AbortController();
|
|
6175
|
+
const abortOthersOnFailure = (run2) => run2.catch((err) => {
|
|
6176
|
+
controller.abort();
|
|
6177
|
+
throw err;
|
|
6178
|
+
});
|
|
6088
6179
|
const [detected, analyses] = await Promise.all([
|
|
6089
|
-
detectLanguage(),
|
|
6090
|
-
runAnalysis()
|
|
6180
|
+
abortOthersOnFailure(detectLanguage(controller.signal)),
|
|
6181
|
+
abortOthersOnFailure(runAnalysis(void 0, [], controller.signal))
|
|
6091
6182
|
]);
|
|
6092
6183
|
const entities = toEntitySummary(analyses.ingestionAnalysis ?? []);
|
|
6093
6184
|
track("AI Wizard Scan Completed", {
|
|
@@ -6540,7 +6631,7 @@ async function assertGitRepoWithHead(repoRoot) {
|
|
|
6540
6631
|
await git(["-C", repoRoot, "rev-parse", "HEAD"]);
|
|
6541
6632
|
} catch {
|
|
6542
6633
|
throw new Error(
|
|
6543
|
-
"
|
|
6634
|
+
"This folder must be a Git repository with at least one commit so you can review and revert Wizard changes. Run `git init`, commit your project files, then rerun."
|
|
6544
6635
|
);
|
|
6545
6636
|
}
|
|
6546
6637
|
}
|
|
@@ -7341,7 +7432,7 @@ function searchInstructions(input) {
|
|
|
7341
7432
|
"Match the application styles closely, including light, dark, and high-contrast themes the project already supports.",
|
|
7342
7433
|
"Give the search input frame the same height, corner radius, border color and width, background, and focus style as the input it replaces. When it replaces nothing, copy them from the form controls beside it. Read these values from their source (the theme, the component props, or the CSS). Do not keep the widget theme defaults, for example a 44px height and 4px corners, and do not invent colors. When the app theme exists only in code, take the values from that theme object.",
|
|
7343
7434
|
"When you add UI text to a localized dictionary or message file, add the same keys, translated, to every locale the project supports.",
|
|
7344
|
-
|
|
7435
|
+
`Before reporting, mount the component. Do not run a type check, build, or test command unless a later instruction allows it: the validation pass runs the type check, after the developer agrees to it. If a tool limit is reached or an error remains unresolved, keep the implementation, report status=success, and name the remaining issue in the summary so validation can drive the next pass. Report status=fail only when no search implementation can be written. When this folder has no frontend to build search UI in (for example a monorepo root or a backend-only package), use reason exactly \`${NO_FRONTEND_REASON}\`. For any other blocker, use a short concrete reason.`,
|
|
7345
7436
|
"The summary should be extremely concise; do not mention manual testing steps.",
|
|
7346
7437
|
...input.uiTemplate ? [
|
|
7347
7438
|
"For the playbook recap, report uiFiles as every file you created or edited for this UI. Each entry needs a short action label and its repo-relative path. Report devCommand only when you found the project\u2019s existing command for starting the app locally. Do not invent a command, host, or port."
|
|
@@ -10501,7 +10592,7 @@ function Ribbon() {
|
|
|
10501
10592
|
}
|
|
10502
10593
|
|
|
10503
10594
|
// src/ui/App.tsx
|
|
10504
|
-
import { useState as useState14 } from "react";
|
|
10595
|
+
import { useEffect as useEffect5, useState as useState14 } from "react";
|
|
10505
10596
|
|
|
10506
10597
|
// src/ui/Logs.tsx
|
|
10507
10598
|
import { Box as Box21, Text as Text22, useInput as useInput9 } from "ink";
|
|
@@ -11135,9 +11226,13 @@ function App() {
|
|
|
11135
11226
|
workflow,
|
|
11136
11227
|
settingUpAppId,
|
|
11137
11228
|
loadingMessage,
|
|
11138
|
-
review: review2
|
|
11229
|
+
review: review2,
|
|
11230
|
+
exitRequested
|
|
11139
11231
|
} = useWizard();
|
|
11140
11232
|
const { exit } = useApp2();
|
|
11233
|
+
useEffect5(() => {
|
|
11234
|
+
if (exitRequested) exit();
|
|
11235
|
+
}, [exitRequested, exit]);
|
|
11141
11236
|
const { columns, rows } = useWindowSize8();
|
|
11142
11237
|
useRepaintOnResize();
|
|
11143
11238
|
const compactRun = isCompactTerminal(columns, rows);
|
|
@@ -11158,7 +11253,7 @@ function App() {
|
|
|
11158
11253
|
const holdForTip = !compactRun && tipsActive && isAwaitingUserInput && tipState === "typing";
|
|
11159
11254
|
const showTips = !compactRun && tipsActive && (phase === "running" && currentStep?.status === "running" && !loadingMessage && !settingUpAppId || holdForTip);
|
|
11160
11255
|
const showNoticesInMain = !tipsActive;
|
|
11161
|
-
const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2;
|
|
11256
|
+
const showNotices = !compactRun && (!isAwaitingUserInput || holdForTip) && !review2 && !exitRequested;
|
|
11162
11257
|
useInput10(
|
|
11163
11258
|
(_input, key) => {
|
|
11164
11259
|
if (key.return) {
|
|
@@ -11572,7 +11667,7 @@ async function promptResumeOrStartNew(session, next) {
|
|
|
11572
11667
|
// package.json with { type: 'json' }
|
|
11573
11668
|
var package_default2 = {
|
|
11574
11669
|
name: "@algolia/wizard",
|
|
11575
|
-
version: "0.
|
|
11670
|
+
version: "0.122.0",
|
|
11576
11671
|
description: "Magically implement Algolia functionality in your codebase",
|
|
11577
11672
|
type: "module",
|
|
11578
11673
|
engines: {
|