@algolia/wizard 0.45.0 → 0.46.0-rc.136.345
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 +216 -84
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -239,6 +239,9 @@ 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
|
+
}
|
|
242
245
|
var useWizard = create((set, get) => ({
|
|
243
246
|
phase: "idle",
|
|
244
247
|
homeScreen: "home",
|
|
@@ -352,10 +355,17 @@ var useWizard = create((set, get) => ({
|
|
|
352
355
|
setTargetIndex: (index) => set({ targetIndex: index }),
|
|
353
356
|
recordWrittenFile: (path) => set((s) => ({ writtenFiles: [...s.writtenFiles, path] })),
|
|
354
357
|
clearWrittenFiles: () => set({ writtenFiles: [] }),
|
|
355
|
-
isCommandApproved: (command, cwd) => get().approvedCommands.has(
|
|
358
|
+
isCommandApproved: (command, cwd) => get().approvedCommands.has(commandApprovalKey(command, cwd)),
|
|
356
359
|
approveCommand: (command, cwd) => set((s) => ({
|
|
357
|
-
approvedCommands: new Set(s.approvedCommands).add(
|
|
360
|
+
approvedCommands: new Set(s.approvedCommands).add(
|
|
361
|
+
commandApprovalKey(command, cwd)
|
|
362
|
+
)
|
|
358
363
|
})),
|
|
364
|
+
revokeCommand: (command, cwd) => set((s) => {
|
|
365
|
+
const approvedCommands = new Set(s.approvedCommands);
|
|
366
|
+
approvedCommands.delete(commandApprovalKey(command, cwd));
|
|
367
|
+
return { approvedCommands };
|
|
368
|
+
}),
|
|
359
369
|
logStart: (kind, name, input) => {
|
|
360
370
|
const id = nanoid();
|
|
361
371
|
set((s) => ({
|
|
@@ -731,9 +741,11 @@ function DecisionSelect({
|
|
|
731
741
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
732
742
|
function CommandApproval({
|
|
733
743
|
command,
|
|
744
|
+
messages,
|
|
734
745
|
onDecide
|
|
735
746
|
}) {
|
|
736
747
|
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", gap: 1, children: [
|
|
748
|
+
messages?.map((message, index) => /* @__PURE__ */ jsx5(Text6, { color: COLORS.muted, children: message }, `msg-${index}`)),
|
|
737
749
|
/* @__PURE__ */ jsx5(Text6, { color: COLORS.primary, bold: true, children: "Run this command?" }),
|
|
738
750
|
/* @__PURE__ */ jsxs5(
|
|
739
751
|
Box6,
|
|
@@ -1074,7 +1086,6 @@ function PromptInput() {
|
|
|
1074
1086
|
return /* @__PURE__ */ jsx7(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx7(
|
|
1075
1087
|
SelectPrompt,
|
|
1076
1088
|
{
|
|
1077
|
-
question: inputReq.prompt,
|
|
1078
1089
|
messages: inputReq.messages,
|
|
1079
1090
|
options: ["Continue"],
|
|
1080
1091
|
onSelect: submitInput
|
|
@@ -1093,7 +1104,14 @@ function PromptInput() {
|
|
|
1093
1104
|
);
|
|
1094
1105
|
}
|
|
1095
1106
|
if (inputReq.promptType === "commandApproval" && inputReq.command) {
|
|
1096
|
-
return /* @__PURE__ */ jsx7(
|
|
1107
|
+
return /* @__PURE__ */ jsx7(
|
|
1108
|
+
CommandApproval,
|
|
1109
|
+
{
|
|
1110
|
+
command: inputReq.command,
|
|
1111
|
+
messages: inputReq.messages,
|
|
1112
|
+
onDecide: submitInput
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1097
1115
|
}
|
|
1098
1116
|
if (inputReq.promptType === "acceptReject") {
|
|
1099
1117
|
const labels = inputReq.options?.length ? inputReq.options : ACCEPT_REJECT_OPTIONS;
|
|
@@ -2556,9 +2574,16 @@ function searchFilesTool(ctx) {
|
|
|
2556
2574
|
}
|
|
2557
2575
|
|
|
2558
2576
|
// src/lib/tools/runShell.ts
|
|
2559
|
-
import { tool as
|
|
2577
|
+
import { tool as tool9, generateText, Output } from "ai";
|
|
2560
2578
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
2579
|
+
import z16 from "zod";
|
|
2580
|
+
import { relative as relative5 } from "node:path";
|
|
2581
|
+
|
|
2582
|
+
// src/lib/tools/reviewScript.ts
|
|
2583
|
+
import { tool as tool8 } from "ai";
|
|
2561
2584
|
import z15 from "zod";
|
|
2585
|
+
import { createHash } from "node:crypto";
|
|
2586
|
+
import { readFile as readFile7, stat as stat2 } from "node:fs/promises";
|
|
2562
2587
|
import { relative as relative4 } from "node:path";
|
|
2563
2588
|
|
|
2564
2589
|
// src/lib/tools/utils/prompt.ts
|
|
@@ -2570,6 +2595,116 @@ function serializePrompt(work) {
|
|
|
2570
2595
|
return result;
|
|
2571
2596
|
}
|
|
2572
2597
|
|
|
2598
|
+
// src/lib/tools/reviewScript.ts
|
|
2599
|
+
var reviewedCommandApprovals = /* @__PURE__ */ new WeakMap();
|
|
2600
|
+
async function fileDigest(filePath) {
|
|
2601
|
+
const content = await readFile7(filePath);
|
|
2602
|
+
return createHash("sha256").update(content).digest("hex");
|
|
2603
|
+
}
|
|
2604
|
+
function grantReviewedCommandApproval(ctx, approval) {
|
|
2605
|
+
reviewedCommandApprovals.set(ctx, approval);
|
|
2606
|
+
}
|
|
2607
|
+
function getReviewedCommandApproval(ctx, command, cwd) {
|
|
2608
|
+
const approval = reviewedCommandApprovals.get(ctx);
|
|
2609
|
+
if (approval?.command !== command || approval.cwd !== cwd) return void 0;
|
|
2610
|
+
return approval;
|
|
2611
|
+
}
|
|
2612
|
+
function clearReviewedCommandApproval(ctx) {
|
|
2613
|
+
reviewedCommandApprovals.delete(ctx);
|
|
2614
|
+
}
|
|
2615
|
+
function revokeCommandApprovals(ctx, command, cwd) {
|
|
2616
|
+
clearReviewedCommandApproval(ctx);
|
|
2617
|
+
useWizard.getState().revokeCommand(command, cwd);
|
|
2618
|
+
}
|
|
2619
|
+
async function reviewedCommandApprovalIsCurrent(approval) {
|
|
2620
|
+
const currentDigest = await fileDigest(approval.filePath).catch(() => null);
|
|
2621
|
+
return currentDigest === approval.digest;
|
|
2622
|
+
}
|
|
2623
|
+
function reviewScriptTool(ctx) {
|
|
2624
|
+
return tool8({
|
|
2625
|
+
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.",
|
|
2626
|
+
inputSchema: z15.object({
|
|
2627
|
+
filePath: z15.string().describe(
|
|
2628
|
+
"Path to the script to review, relative to the project root."
|
|
2629
|
+
),
|
|
2630
|
+
command: z15.string().min(1).describe("The exact command that runShell will use to run the script."),
|
|
2631
|
+
cwd: z15.string().describe(
|
|
2632
|
+
"The exact working directory that runShell will use, relative to the current tool working directory."
|
|
2633
|
+
),
|
|
2634
|
+
entityName: z15.string().trim().min(1).max(100).regex(/^[^\r\n]+$/).describe("The exact entity name selected by the user."),
|
|
2635
|
+
usesGeneratedRecords: z15.boolean().describe(
|
|
2636
|
+
"Whether the script reads generated sample records instead of the selected real data source."
|
|
2637
|
+
)
|
|
2638
|
+
}),
|
|
2639
|
+
execute: async ({
|
|
2640
|
+
filePath,
|
|
2641
|
+
command,
|
|
2642
|
+
cwd,
|
|
2643
|
+
entityName,
|
|
2644
|
+
usesGeneratedRecords
|
|
2645
|
+
}) => {
|
|
2646
|
+
logger.info(
|
|
2647
|
+
{ filePath, command, cwd, entityName, usesGeneratedRecords },
|
|
2648
|
+
"called reviewScript tool"
|
|
2649
|
+
);
|
|
2650
|
+
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2651
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
2652
|
+
const resolvedCwd = resolveInRoot(ctx, cwd);
|
|
2653
|
+
if (resolvedCwd.ok === false) return resolvedCwd.error;
|
|
2654
|
+
const displayCwd = relative4(ctx.root, resolvedCwd.target) || ".";
|
|
2655
|
+
const displayFilePath = relative4(ctx.root, resolved2.target);
|
|
2656
|
+
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.`;
|
|
2657
|
+
return serializePrompt(async () => {
|
|
2658
|
+
revokeCommandApprovals(ctx, command, resolvedCwd.target);
|
|
2659
|
+
const isFile = await stat2(resolved2.target).then(
|
|
2660
|
+
(s) => s.isFile(),
|
|
2661
|
+
() => false
|
|
2662
|
+
);
|
|
2663
|
+
if (!isFile) {
|
|
2664
|
+
return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
|
|
2665
|
+
}
|
|
2666
|
+
const reviewedDigest = await fileDigest(resolved2.target).catch(
|
|
2667
|
+
() => null
|
|
2668
|
+
);
|
|
2669
|
+
if (reviewedDigest === null) {
|
|
2670
|
+
return `Refused: ${filePath} could not be read. Write the script first, then review it again.`;
|
|
2671
|
+
}
|
|
2672
|
+
const answer = await useWizard.getState().requestUserInput({
|
|
2673
|
+
prompt: "Run this command?",
|
|
2674
|
+
promptType: "commandApproval",
|
|
2675
|
+
options: [],
|
|
2676
|
+
messages: [
|
|
2677
|
+
`We have created your ingestion script. Please review it at ${displayFilePath} before approving.`
|
|
2678
|
+
],
|
|
2679
|
+
command: {
|
|
2680
|
+
command,
|
|
2681
|
+
cwd: displayCwd,
|
|
2682
|
+
explanation: ingestionSummary
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
if (answer !== "approve") {
|
|
2686
|
+
revokeCommandApprovals(ctx, command, resolvedCwd.target);
|
|
2687
|
+
return "The user declined to run this ingestion script. Do not run it.";
|
|
2688
|
+
}
|
|
2689
|
+
const approvedDigest = await fileDigest(resolved2.target).catch(
|
|
2690
|
+
() => null
|
|
2691
|
+
);
|
|
2692
|
+
if (approvedDigest !== reviewedDigest) {
|
|
2693
|
+
return "The ingestion script changed while it was being reviewed. Call reviewScript again before running it.";
|
|
2694
|
+
}
|
|
2695
|
+
ctx.reviewed.push(resolved2.target);
|
|
2696
|
+
grantReviewedCommandApproval(ctx, {
|
|
2697
|
+
command,
|
|
2698
|
+
cwd: resolvedCwd.target,
|
|
2699
|
+
filePath: resolved2.target,
|
|
2700
|
+
digest: reviewedDigest
|
|
2701
|
+
});
|
|
2702
|
+
return "The user reviewed the script and approved this exact command. Run it now with the same cwd.";
|
|
2703
|
+
});
|
|
2704
|
+
}
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2573
2708
|
// src/lib/tools/utils/runShell.ts
|
|
2574
2709
|
import { spawn as spawn2 } from "node:child_process";
|
|
2575
2710
|
|
|
@@ -2668,7 +2803,7 @@ function storeApproval(root) {
|
|
|
2668
2803
|
return async (req) => {
|
|
2669
2804
|
const store = useWizard.getState();
|
|
2670
2805
|
if (store.isCommandApproved(req.command, req.cwd)) return "approve";
|
|
2671
|
-
const rel =
|
|
2806
|
+
const rel = relative5(root, req.cwd);
|
|
2672
2807
|
const answer = await store.requestUserInput({
|
|
2673
2808
|
prompt: "Run this command?",
|
|
2674
2809
|
promptType: "commandApproval",
|
|
@@ -2821,9 +2956,9 @@ function fastPathSafety(command) {
|
|
|
2821
2956
|
return void 0;
|
|
2822
2957
|
}
|
|
2823
2958
|
var CLASSIFIER_MODEL = "claude-haiku-4-5";
|
|
2824
|
-
var commandSafetySchema =
|
|
2825
|
-
safe:
|
|
2826
|
-
reason:
|
|
2959
|
+
var commandSafetySchema = z16.object({
|
|
2960
|
+
safe: z16.boolean(),
|
|
2961
|
+
reason: z16.string().describe("One short sentence explaining the verdict.")
|
|
2827
2962
|
});
|
|
2828
2963
|
function defaultCreateModel() {
|
|
2829
2964
|
const token = getAuthToken();
|
|
@@ -2883,12 +3018,15 @@ async function classifyCommandSafety(createModel, command, cwd, explanation, app
|
|
|
2883
3018
|
return false;
|
|
2884
3019
|
}
|
|
2885
3020
|
}
|
|
2886
|
-
async function
|
|
2887
|
-
|
|
2888
|
-
const env = await ctx.shell.env().catch((err) => {
|
|
3021
|
+
async function resolveShellEnv(ctx, command) {
|
|
3022
|
+
return ctx.shell.env().catch((err) => {
|
|
2889
3023
|
logger.warn({ err, command }, "runShell: could not resolve command env");
|
|
2890
3024
|
return {};
|
|
2891
3025
|
});
|
|
3026
|
+
}
|
|
3027
|
+
async function runAndRecord(ctx, command, cwd, resolvedEnv) {
|
|
3028
|
+
useWizard.getState().pushNotice({ messages: [`Running: ${command}`] });
|
|
3029
|
+
const env = resolvedEnv ?? await resolveShellEnv(ctx, command);
|
|
2892
3030
|
const run2 = await (ctx.shell.run ?? runShell)(command, {
|
|
2893
3031
|
cwd,
|
|
2894
3032
|
env,
|
|
@@ -2919,6 +3057,16 @@ async function runAndRecord(ctx, command, cwd) {
|
|
|
2919
3057
|
output: run2.output
|
|
2920
3058
|
};
|
|
2921
3059
|
}
|
|
3060
|
+
async function runReviewedCommand(ctx, command, cwd) {
|
|
3061
|
+
const approval = getReviewedCommandApproval(ctx, command, cwd);
|
|
3062
|
+
if (!approval) return void 0;
|
|
3063
|
+
const env = await resolveShellEnv(ctx, command);
|
|
3064
|
+
if (!await reviewedCommandApprovalIsCurrent(approval)) {
|
|
3065
|
+
return `Refused: ${relative5(ctx.root, approval.filePath)} changed after review. Call reviewScript again before running it.`;
|
|
3066
|
+
}
|
|
3067
|
+
clearReviewedCommandApproval(ctx);
|
|
3068
|
+
return runAndRecord(ctx, command, cwd, env);
|
|
3069
|
+
}
|
|
2922
3070
|
async function approveAndRun(ctx, command, cwd, explanation) {
|
|
2923
3071
|
const decision = await ctx.shell.approve({ command, cwd, explanation });
|
|
2924
3072
|
if (decision === "reject") {
|
|
@@ -2929,16 +3077,16 @@ async function approveAndRun(ctx, command, cwd, explanation) {
|
|
|
2929
3077
|
return runAndRecord(ctx, command, cwd);
|
|
2930
3078
|
}
|
|
2931
3079
|
function runShellTool(ctx, createModel = defaultCreateModel) {
|
|
2932
|
-
return
|
|
3080
|
+
return tool9({
|
|
2933
3081
|
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.",
|
|
2934
|
-
inputSchema:
|
|
2935
|
-
command:
|
|
3082
|
+
inputSchema: z16.object({
|
|
3083
|
+
command: z16.string().describe(
|
|
2936
3084
|
"The command to run, exactly as it would be typed in a shell. Pipes, && and redirects are allowed."
|
|
2937
3085
|
),
|
|
2938
|
-
cwd:
|
|
3086
|
+
cwd: z16.string().optional().describe(
|
|
2939
3087
|
"Directory to run in, relative to the project root. Defaults to the project root."
|
|
2940
3088
|
),
|
|
2941
|
-
explanation:
|
|
3089
|
+
explanation: z16.string().describe(
|
|
2942
3090
|
"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."
|
|
2943
3091
|
)
|
|
2944
3092
|
}),
|
|
@@ -2947,16 +3095,22 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
|
|
|
2947
3095
|
return `Refused: command limit (${ctx.limits.shell}) reached. Stop running commands and report what you have.`;
|
|
2948
3096
|
}
|
|
2949
3097
|
const resolved2 = resolveInRoot(ctx, cwd ?? ".");
|
|
2950
|
-
if (
|
|
3098
|
+
if (resolved2.ok === false) return resolved2.error;
|
|
2951
3099
|
if (isFileIOCommand(command)) {
|
|
2952
3100
|
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).";
|
|
2953
3101
|
}
|
|
2954
3102
|
logger.info({ command, cwd: resolved2.target }, "called runShell tool");
|
|
3103
|
+
const reviewed = await serializePrompt(
|
|
3104
|
+
() => runReviewedCommand(ctx, command, resolved2.target)
|
|
3105
|
+
);
|
|
3106
|
+
if (reviewed !== void 0) return reviewed;
|
|
2955
3107
|
const fast = fastPathSafety(command);
|
|
2956
3108
|
let isSafe = fast;
|
|
3109
|
+
let usedStoredApproval = false;
|
|
2957
3110
|
if (isSafe === void 0) {
|
|
2958
3111
|
const store = useWizard.getState();
|
|
2959
|
-
|
|
3112
|
+
usedStoredApproval = store.isCommandApproved(command, resolved2.target);
|
|
3113
|
+
isSafe = usedStoredApproval;
|
|
2960
3114
|
if (!isSafe) {
|
|
2961
3115
|
isSafe = await classifyCommandSafety(
|
|
2962
3116
|
createModel,
|
|
@@ -2967,54 +3121,18 @@ function runShellTool(ctx, createModel = defaultCreateModel) {
|
|
|
2967
3121
|
);
|
|
2968
3122
|
}
|
|
2969
3123
|
}
|
|
2970
|
-
if (isSafe) {
|
|
2971
|
-
return serializePrompt(
|
|
2972
|
-
() => runAndRecord(ctx, command, resolved2.target)
|
|
2973
|
-
);
|
|
2974
|
-
}
|
|
2975
|
-
return serializePrompt(
|
|
2976
|
-
() => approveAndRun(ctx, command, resolved2.target, explanation)
|
|
2977
|
-
);
|
|
2978
|
-
}
|
|
2979
|
-
});
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
// src/lib/tools/reviewScript.ts
|
|
2983
|
-
import { tool as tool9 } from "ai";
|
|
2984
|
-
import z16 from "zod";
|
|
2985
|
-
import { stat as stat2 } from "node:fs/promises";
|
|
2986
|
-
import { relative as relative5 } from "node:path";
|
|
2987
|
-
function reviewScriptTool(ctx) {
|
|
2988
|
-
return tool9({
|
|
2989
|
-
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.",
|
|
2990
|
-
inputSchema: z16.object({
|
|
2991
|
-
filePath: z16.string().describe(
|
|
2992
|
-
"Path to the script to review, relative to the project root."
|
|
2993
|
-
)
|
|
2994
|
-
}),
|
|
2995
|
-
execute: async ({ filePath }) => {
|
|
2996
|
-
logger.info({ filePath }, "called reviewScript tool");
|
|
2997
|
-
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2998
|
-
if (resolved2.ok === false) return resolved2.error;
|
|
2999
|
-
const isFile = await stat2(resolved2.target).then(
|
|
3000
|
-
(s) => s.isFile(),
|
|
3001
|
-
() => false
|
|
3002
|
-
);
|
|
3003
|
-
if (!isFile) {
|
|
3004
|
-
return `Refused: ${filePath} is not a file. Write the script first, then review the path you wrote.`;
|
|
3005
|
-
}
|
|
3006
3124
|
return serializePrompt(async () => {
|
|
3007
|
-
await
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
return
|
|
3125
|
+
const lateReview = await runReviewedCommand(
|
|
3126
|
+
ctx,
|
|
3127
|
+
command,
|
|
3128
|
+
resolved2.target
|
|
3129
|
+
);
|
|
3130
|
+
if (lateReview !== void 0) return lateReview;
|
|
3131
|
+
const storedApprovalIsCurrent = !usedStoredApproval || useWizard.getState().isCommandApproved(command, resolved2.target);
|
|
3132
|
+
if (isSafe && storedApprovalIsCurrent) {
|
|
3133
|
+
return runAndRecord(ctx, command, resolved2.target);
|
|
3134
|
+
}
|
|
3135
|
+
return approveAndRun(ctx, command, resolved2.target, explanation);
|
|
3018
3136
|
});
|
|
3019
3137
|
}
|
|
3020
3138
|
});
|
|
@@ -3509,7 +3627,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3509
3627
|
// package.json
|
|
3510
3628
|
var package_default = {
|
|
3511
3629
|
name: "@algolia/wizard",
|
|
3512
|
-
version: "0.
|
|
3630
|
+
version: "0.46.0-rc.136.345",
|
|
3513
3631
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3514
3632
|
type: "module",
|
|
3515
3633
|
engines: {
|
|
@@ -4212,8 +4330,8 @@ function ingestionInstructions(input) {
|
|
|
4212
4330
|
...algoliaClientDoc(input),
|
|
4213
4331
|
"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.",
|
|
4214
4332
|
`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.`,
|
|
4215
|
-
|
|
4216
|
-
'Then run the script yourself via runShell
|
|
4333
|
+
`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.`,
|
|
4334
|
+
'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.',
|
|
4217
4335
|
"The summary should be extremely concise.",
|
|
4218
4336
|
...sourceSpecificInstructions(input)
|
|
4219
4337
|
] : []
|
|
@@ -4585,19 +4703,33 @@ ${detail}` : ""}`
|
|
|
4585
4703
|
product_area: "AI Wizard"
|
|
4586
4704
|
});
|
|
4587
4705
|
}
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
}
|
|
4706
|
+
if (ingestScriptRan) {
|
|
4707
|
+
await ctx.requestUserInput({
|
|
4708
|
+
prompt: "Continue",
|
|
4709
|
+
promptType: "notice",
|
|
4710
|
+
options: [],
|
|
4711
|
+
messages: [
|
|
4712
|
+
ingestOutcomeMessage,
|
|
4713
|
+
"View your ingested records in the Algolia dashboard:",
|
|
4714
|
+
"https://dashboard.algolia.com/explorer/browse",
|
|
4715
|
+
"When you're ready to continue, we'll move on to search implementation."
|
|
4716
|
+
]
|
|
4717
|
+
});
|
|
4718
|
+
} else {
|
|
4719
|
+
const commandMessages = ingestCommand ? [`Ingestion command: ${ingestCommand}`] : [];
|
|
4720
|
+
await ctx.requestUserInput({
|
|
4721
|
+
prompt: "",
|
|
4722
|
+
promptType: "enterToContinue",
|
|
4723
|
+
options: [],
|
|
4724
|
+
// One message per line: Notices.tsx counts a message as one wrapped
|
|
4725
|
+
// line, so an embedded newline overflows the panel's height accounting.
|
|
4726
|
+
messages: [
|
|
4727
|
+
ingestOutcomeMessage,
|
|
4728
|
+
...ingestFailureDetail?.split("\n").filter((l) => l.trim()) ?? [],
|
|
4729
|
+
...commandMessages
|
|
4730
|
+
]
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4601
4733
|
}
|
|
4602
4734
|
let searchConfigFile;
|
|
4603
4735
|
if (useCases.includes("search")) {
|
|
@@ -5789,7 +5921,7 @@ function App() {
|
|
|
5789
5921
|
}
|
|
5790
5922
|
|
|
5791
5923
|
// src/lib/envAppId.ts
|
|
5792
|
-
import { readFile as
|
|
5924
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
5793
5925
|
import { join as join12 } from "node:path";
|
|
5794
5926
|
var ENV_FILES = [".env", ".env.local"];
|
|
5795
5927
|
var APP_ID_LINE = /^[ \t]*(?:export[ \t]+)?([A-Z0-9_]*ALGOLIA_APP(?:LICATION)?_ID)[ \t]*=[ \t]*(.*)$/gm;
|
|
@@ -5797,7 +5929,7 @@ async function findEnvApplicationId(root = process.cwd()) {
|
|
|
5797
5929
|
for (const file of ENV_FILES) {
|
|
5798
5930
|
let content;
|
|
5799
5931
|
try {
|
|
5800
|
-
content = await
|
|
5932
|
+
content = await readFile8(join12(root, file), "utf8");
|
|
5801
5933
|
} catch (err) {
|
|
5802
5934
|
if (err.code !== "ENOENT") {
|
|
5803
5935
|
logger.warn(
|