@algolia/wizard 0.71.0 → 0.73.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 +567 -315
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -177,7 +177,7 @@ function stateDir(cwd = pinnedRoot ?? process.cwd()) {
|
|
|
177
177
|
// src/lib/logger.ts
|
|
178
178
|
var STDERR_FD = 2;
|
|
179
179
|
function resolveDest() {
|
|
180
|
-
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG
|
|
180
|
+
const target = process.env.VITEST ? devNull : process.env.WIZARD_LOG || join2(stateDir(), "wizard.log");
|
|
181
181
|
try {
|
|
182
182
|
mkdirSync(dirname(target), { recursive: true });
|
|
183
183
|
closeSync(openSync(target, "a"));
|
|
@@ -263,7 +263,9 @@ var useWizard = create((set, get) => ({
|
|
|
263
263
|
inputReq: null,
|
|
264
264
|
_resolve: null,
|
|
265
265
|
settingUpAppId: null,
|
|
266
|
+
loadingMessage: null,
|
|
266
267
|
setSettingUpApp: (appId) => set({ settingUpAppId: appId }),
|
|
268
|
+
setLoadingMessage: (message) => set({ loadingMessage: message }),
|
|
267
269
|
// `endAuth` lands on 'preflight', not 'idle': sign-in happens after the
|
|
268
270
|
// welcome screen, so going back would gate the run a second time.
|
|
269
271
|
beginAuth: () => set({ phase: "authenticating", cliOutput: [] }),
|
|
@@ -359,6 +361,11 @@ var useWizard = create((set, get) => ({
|
|
|
359
361
|
setTargetIndex: (index) => set({ targetIndex: index }),
|
|
360
362
|
recordWrittenFile: (path) => set((s) => ({ writtenFiles: [...s.writtenFiles, path] })),
|
|
361
363
|
clearWrittenFiles: () => set({ writtenFiles: [] }),
|
|
364
|
+
resetRunState: () => set({
|
|
365
|
+
targetIndex: null,
|
|
366
|
+
writtenFiles: [],
|
|
367
|
+
approvedCommands: /* @__PURE__ */ new Set()
|
|
368
|
+
}),
|
|
362
369
|
isCommandApproved: (command, cwd) => get().approvedCommands.has(commandApprovalKey(command, cwd)),
|
|
363
370
|
approveCommand: (command, cwd) => set((s) => ({
|
|
364
371
|
approvedCommands: new Set(s.approvedCommands).add(
|
|
@@ -389,7 +396,8 @@ var useWizard = create((set, get) => ({
|
|
|
389
396
|
set({
|
|
390
397
|
phase: "awaitingInput",
|
|
391
398
|
inputReq: req,
|
|
392
|
-
_resolve: resolve5
|
|
399
|
+
_resolve: resolve5,
|
|
400
|
+
loadingMessage: null
|
|
393
401
|
});
|
|
394
402
|
}),
|
|
395
403
|
submitInput: async (value) => {
|
|
@@ -402,8 +410,8 @@ var useWizard = create((set, get) => ({
|
|
|
402
410
|
);
|
|
403
411
|
get().logEnd(id, "success");
|
|
404
412
|
},
|
|
405
|
-
setDone: () => set({ phase: "done" }),
|
|
406
|
-
setError: (message) => set({ phase: "error", error: message }),
|
|
413
|
+
setDone: () => set({ phase: "done", loadingMessage: null }),
|
|
414
|
+
setError: (message) => set({ phase: "error", error: message, loadingMessage: null }),
|
|
407
415
|
reset: () => {
|
|
408
416
|
get()._clearNoticeQueue();
|
|
409
417
|
get()._resolve?.(false);
|
|
@@ -424,7 +432,8 @@ var useWizard = create((set, get) => ({
|
|
|
424
432
|
error: null,
|
|
425
433
|
inputReq: null,
|
|
426
434
|
_resolve: null,
|
|
427
|
-
settingUpAppId: null
|
|
435
|
+
settingUpAppId: null,
|
|
436
|
+
loadingMessage: null
|
|
428
437
|
});
|
|
429
438
|
}
|
|
430
439
|
}));
|
|
@@ -1770,6 +1779,37 @@ function trackWorkflowStart(ctx) {
|
|
|
1770
1779
|
);
|
|
1771
1780
|
sendMetric("wizard.workflow.invocation", 1, 1, metricTags(ctx.workflowId));
|
|
1772
1781
|
}
|
|
1782
|
+
function trackWorkflowAbandoned(ctx) {
|
|
1783
|
+
const attributes = {
|
|
1784
|
+
event: "wizard.workflow.abandoned",
|
|
1785
|
+
workflow_id: ctx.workflowId,
|
|
1786
|
+
app_id: ctx.appId,
|
|
1787
|
+
reason: ctx.reason
|
|
1788
|
+
};
|
|
1789
|
+
emitTelemetryLog(
|
|
1790
|
+
"info",
|
|
1791
|
+
"wizard workflow abandoned",
|
|
1792
|
+
attributes,
|
|
1793
|
+
logTags(ctx.workflowId, void 0, ctx.appId)
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
function trackActionAbandoned(ctx) {
|
|
1797
|
+
const attributes = {
|
|
1798
|
+
event: "wizard.action.abandoned",
|
|
1799
|
+
workflow_id: ctx.workflowId,
|
|
1800
|
+
action_id: ctx.actionId,
|
|
1801
|
+
action_title: ctx.actionTitle,
|
|
1802
|
+
duration_ms: ctx.durationMs,
|
|
1803
|
+
app_id: ctx.appId,
|
|
1804
|
+
reason: ctx.reason
|
|
1805
|
+
};
|
|
1806
|
+
emitTelemetryLog(
|
|
1807
|
+
"info",
|
|
1808
|
+
"wizard action abandoned",
|
|
1809
|
+
attributes,
|
|
1810
|
+
logTags(ctx.workflowId, ctx.actionId, ctx.appId)
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1773
1813
|
function trackActionStart(ctx) {
|
|
1774
1814
|
const attributes = {
|
|
1775
1815
|
event: "wizard.action.start",
|
|
@@ -1966,10 +2006,261 @@ function identify(traits) {
|
|
|
1966
2006
|
postAnalytics("/identify", { userId, traits }, "identify");
|
|
1967
2007
|
}
|
|
1968
2008
|
|
|
2009
|
+
// src/lib/algoliaApp.ts
|
|
2010
|
+
import { z as z3 } from "zod";
|
|
2011
|
+
var applicationSchema = z3.object({
|
|
2012
|
+
id: z3.string().min(1),
|
|
2013
|
+
name: z3.string().default(""),
|
|
2014
|
+
plan: z3.string().optional()
|
|
2015
|
+
});
|
|
2016
|
+
var listSchema = z3.array(
|
|
2017
|
+
z3.object({
|
|
2018
|
+
id: z3.string().min(1),
|
|
2019
|
+
name: z3.string().default(""),
|
|
2020
|
+
plan_label: z3.string().optional(),
|
|
2021
|
+
status: z3.string().optional(),
|
|
2022
|
+
acl: z3.array(z3.string()).optional()
|
|
2023
|
+
}).transform(({ id, name, plan_label, status, acl }) => ({
|
|
2024
|
+
id,
|
|
2025
|
+
name,
|
|
2026
|
+
plan: plan_label,
|
|
2027
|
+
status,
|
|
2028
|
+
acl
|
|
2029
|
+
}))
|
|
2030
|
+
);
|
|
2031
|
+
function canSelectApplication(app) {
|
|
2032
|
+
return app.status === "active" && (app.acl ?? []).includes("keys");
|
|
2033
|
+
}
|
|
2034
|
+
async function currentApplication() {
|
|
2035
|
+
let raw;
|
|
2036
|
+
try {
|
|
2037
|
+
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
2038
|
+
} catch {
|
|
2039
|
+
return null;
|
|
2040
|
+
}
|
|
2041
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2042
|
+
return parsed.success ? parsed.data : null;
|
|
2043
|
+
}
|
|
2044
|
+
async function requireApplication(expectedAppId) {
|
|
2045
|
+
const app = await currentApplication();
|
|
2046
|
+
if (!app) {
|
|
2047
|
+
throw new Error(
|
|
2048
|
+
"No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
if (expectedAppId && app.id !== expectedAppId) {
|
|
2052
|
+
throw new Error(
|
|
2053
|
+
`The wizard is running in application ${expectedAppId}, but the Algolia CLI is pointed at ${app.id}. Select ${expectedAppId} with the Algolia CLI and restart the wizard: npx @algolia/cli@latest application select --app-id "${expectedAppId}"`
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
return app;
|
|
2057
|
+
}
|
|
2058
|
+
async function listApplications() {
|
|
2059
|
+
const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
|
|
2060
|
+
const parsed = listSchema.safeParse(parseJson(raw));
|
|
2061
|
+
if (!parsed.success) {
|
|
2062
|
+
throw new Error("Could not read the list of Algolia applications.");
|
|
2063
|
+
}
|
|
2064
|
+
return parsed.data;
|
|
2065
|
+
}
|
|
2066
|
+
async function selectApplication(id) {
|
|
2067
|
+
const raw = await runAlgoliaCli(
|
|
2068
|
+
["application", "select", "--non-interactive", "--app-id", id],
|
|
2069
|
+
{ onOutput: stderrSink }
|
|
2070
|
+
);
|
|
2071
|
+
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2072
|
+
if (!parsed.success) {
|
|
2073
|
+
throw new Error(
|
|
2074
|
+
`Selected application ${id}, but the Algolia CLI returned an unreadable result.`
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
return parsed.data;
|
|
2078
|
+
}
|
|
2079
|
+
function parseJson(text) {
|
|
2080
|
+
try {
|
|
2081
|
+
return JSON.parse(text);
|
|
2082
|
+
} catch {
|
|
2083
|
+
return void 0;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// src/lib/envAppId.ts
|
|
2088
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2089
|
+
import { join as join5 } from "node:path";
|
|
2090
|
+
var ENV_FILES = [".env", ".env.local"];
|
|
2091
|
+
var APP_ID_LINE = /^[ \t]*(?:export[ \t]+)?([A-Z0-9_]*ALGOLIA_APP(?:LICATION)?_ID)[ \t]*=[ \t]*(.*)$/gm;
|
|
2092
|
+
async function findEnvApplicationId(root = process.cwd()) {
|
|
2093
|
+
for (const file of ENV_FILES) {
|
|
2094
|
+
let content;
|
|
2095
|
+
try {
|
|
2096
|
+
content = await readFile3(join5(root, file), "utf8");
|
|
2097
|
+
} catch (err) {
|
|
2098
|
+
if (err.code !== "ENOENT") {
|
|
2099
|
+
logger.warn(
|
|
2100
|
+
{ file, err },
|
|
2101
|
+
"could not read env file for an application id"
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
for (const [, name, raw] of content.matchAll(APP_ID_LINE)) {
|
|
2107
|
+
const id = readValue(raw);
|
|
2108
|
+
if (id) {
|
|
2109
|
+
logger.info({ file, name, app: id }, "found an application id in env");
|
|
2110
|
+
return { id, name, file };
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
return null;
|
|
2115
|
+
}
|
|
2116
|
+
function readValue(raw) {
|
|
2117
|
+
const trimmed = raw.trim();
|
|
2118
|
+
const quoted = trimmed.match(/^(['"])(.*)\1/);
|
|
2119
|
+
const value = quoted ? quoted[2].trim() : trimmed.replace(/\s+#.*$/, "").trim();
|
|
2120
|
+
return value.length > 0 && !value.startsWith("<") ? value : null;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
// src/lib/algoliaAppPicker.ts
|
|
2124
|
+
function blockReasonFor(app) {
|
|
2125
|
+
return app.status !== "active" ? "Paused" : "Missing permissions";
|
|
2126
|
+
}
|
|
2127
|
+
function secondaryFor(app, marks) {
|
|
2128
|
+
if (!canSelectApplication(app)) {
|
|
2129
|
+
return { kind: "text", value: blockReasonFor(app) };
|
|
2130
|
+
}
|
|
2131
|
+
if (marks?.currentId && app.id === marks.currentId) {
|
|
2132
|
+
return { kind: "badge", value: "[CURRENT]" };
|
|
2133
|
+
}
|
|
2134
|
+
if (marks?.detectedId && app.id === marks.detectedId) {
|
|
2135
|
+
return { kind: "badge", value: "[DETECTED]" };
|
|
2136
|
+
}
|
|
2137
|
+
return app.plan ? { kind: "badge", value: app.plan } : void 0;
|
|
2138
|
+
}
|
|
2139
|
+
function labelFor(app) {
|
|
2140
|
+
return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
|
|
2141
|
+
}
|
|
2142
|
+
async function selectAndReport(id) {
|
|
2143
|
+
const store = useWizard.getState();
|
|
2144
|
+
store.setSettingUpApp(id);
|
|
2145
|
+
try {
|
|
2146
|
+
return await selectApplication(id);
|
|
2147
|
+
} finally {
|
|
2148
|
+
store.setSettingUpApp(null);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
async function loadSelectableApps() {
|
|
2152
|
+
const unordered = await listApplications();
|
|
2153
|
+
if (unordered.length === 0) {
|
|
2154
|
+
throw new Error(
|
|
2155
|
+
"This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
if (!unordered.some(canSelectApplication)) {
|
|
2159
|
+
throw new Error(
|
|
2160
|
+
"None of the applications on this Algolia account are usable \u2014 each is either paused or missing key-management access. Activate one, or grant it the `keys` ACL, then restart the wizard."
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
return [
|
|
2164
|
+
...unordered.filter(canSelectApplication),
|
|
2165
|
+
...unordered.filter((app) => !canSelectApplication(app))
|
|
2166
|
+
];
|
|
2167
|
+
}
|
|
2168
|
+
async function pickApplication(opts) {
|
|
2169
|
+
const store = useWizard.getState();
|
|
2170
|
+
const apps = await loadSelectableApps();
|
|
2171
|
+
const envHighlight = opts.highlight;
|
|
2172
|
+
const envApp = envHighlight ? apps.find((app) => app.id === envHighlight.id) : void 0;
|
|
2173
|
+
const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
|
|
2174
|
+
const marks = {
|
|
2175
|
+
detectedId: envHighlight?.id,
|
|
2176
|
+
currentId: opts.currentId
|
|
2177
|
+
};
|
|
2178
|
+
const messages = [];
|
|
2179
|
+
if (envHighlight && highlightIndex < 0) {
|
|
2180
|
+
messages.push(
|
|
2181
|
+
envApp ? `Could not use ${envHighlight.id} from ${envHighlight.file} \u2014 it\u2019s ${blockReasonFor(envApp).toLowerCase()}. Pick another below.` : `Could not use ${envHighlight.id} from ${envHighlight.file} \u2014 it may have been removed, or this account may not have access to it.`
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
const extras = opts.extraOptions ?? [];
|
|
2185
|
+
const options = [...extras, ...apps.map(labelFor)];
|
|
2186
|
+
const secondary = [
|
|
2187
|
+
...extras.map(() => void 0),
|
|
2188
|
+
...apps.map((app) => secondaryFor(app, marks))
|
|
2189
|
+
];
|
|
2190
|
+
const disabled = [
|
|
2191
|
+
...extras.map(() => false),
|
|
2192
|
+
...apps.map(
|
|
2193
|
+
(app) => !canSelectApplication(app) || app.id === opts.currentId
|
|
2194
|
+
)
|
|
2195
|
+
];
|
|
2196
|
+
const defaultSelectedIndex = extras.length > 0 ? 0 : highlightIndex >= 0 ? highlightIndex : void 0;
|
|
2197
|
+
for (; ; ) {
|
|
2198
|
+
const choice = await store.requestUserInput({
|
|
2199
|
+
prompt: "Which Algolia application should the wizard work in?",
|
|
2200
|
+
promptType: "multipleChoice",
|
|
2201
|
+
options,
|
|
2202
|
+
secondary,
|
|
2203
|
+
disabled,
|
|
2204
|
+
...defaultSelectedIndex !== void 0 ? { defaultSelectedIndex } : {},
|
|
2205
|
+
messages,
|
|
2206
|
+
...opts.helpText ? { helpText: opts.helpText } : {}
|
|
2207
|
+
});
|
|
2208
|
+
if (typeof choice === "string" && extras.includes(choice)) {
|
|
2209
|
+
return { type: "option", value: choice };
|
|
2210
|
+
}
|
|
2211
|
+
const chosen = apps.find((app) => labelFor(app) === choice);
|
|
2212
|
+
if (!chosen || !canSelectApplication(chosen)) {
|
|
2213
|
+
throw new Error("Application picker received an unexpected selection");
|
|
2214
|
+
}
|
|
2215
|
+
try {
|
|
2216
|
+
return { type: "application", app: await selectAndReport(chosen.id) };
|
|
2217
|
+
} catch (err) {
|
|
2218
|
+
logger.warn(
|
|
2219
|
+
{ app: chosen.id, err: err.message },
|
|
2220
|
+
"application select failed; re-prompting"
|
|
2221
|
+
);
|
|
2222
|
+
messages.push(
|
|
2223
|
+
`Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
async function promptForApplication(highlight) {
|
|
2229
|
+
const result = await pickApplication({ highlight });
|
|
2230
|
+
if (result.type !== "application") {
|
|
2231
|
+
throw new Error("Application picker received an unexpected selection");
|
|
2232
|
+
}
|
|
2233
|
+
return result.app;
|
|
2234
|
+
}
|
|
2235
|
+
async function promptToChangeApplication(currentId) {
|
|
2236
|
+
const store = useWizard.getState();
|
|
2237
|
+
store.setLoadingMessage("Loading applications\u2026");
|
|
2238
|
+
try {
|
|
2239
|
+
const env = await findEnvApplicationId();
|
|
2240
|
+
const highlight = env ? { id: env.id, file: env.file } : void 0;
|
|
2241
|
+
const result = await pickApplication({
|
|
2242
|
+
currentId,
|
|
2243
|
+
highlight,
|
|
2244
|
+
extraOptions: ["Keep current application"],
|
|
2245
|
+
helpText: "Choosing another application restarts this wizard run. Progress so far is discarded."
|
|
2246
|
+
});
|
|
2247
|
+
if (result.type === "option") return null;
|
|
2248
|
+
return result.app;
|
|
2249
|
+
} finally {
|
|
2250
|
+
store.setLoadingMessage(null);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
async function ensureApplication(resumeAppId) {
|
|
2254
|
+
if (resumeAppId) return selectAndReport(resumeAppId);
|
|
2255
|
+
const env = await findEnvApplicationId();
|
|
2256
|
+
if (!env) return promptForApplication();
|
|
2257
|
+
return promptForApplication({ id: env.id, file: env.file });
|
|
2258
|
+
}
|
|
2259
|
+
|
|
1969
2260
|
// package.json
|
|
1970
2261
|
var package_default = {
|
|
1971
2262
|
name: "@algolia/wizard",
|
|
1972
|
-
version: "0.
|
|
2263
|
+
version: "0.73.0",
|
|
1973
2264
|
description: "Magically implement Algolia functionality in your codebase",
|
|
1974
2265
|
type: "module",
|
|
1975
2266
|
engines: {
|
|
@@ -2053,6 +2344,14 @@ var ReturnToStepSignal = class extends Error {
|
|
|
2053
2344
|
targetIndex;
|
|
2054
2345
|
stepId;
|
|
2055
2346
|
};
|
|
2347
|
+
var RestartWorkflowSignal = class extends Error {
|
|
2348
|
+
constructor(appId) {
|
|
2349
|
+
super(`restartWorkflow: ${appId}`);
|
|
2350
|
+
this.appId = appId;
|
|
2351
|
+
this.name = "RestartWorkflowSignal";
|
|
2352
|
+
}
|
|
2353
|
+
appId;
|
|
2354
|
+
};
|
|
2056
2355
|
function ensureExecutedStepCount(state) {
|
|
2057
2356
|
if (state.executedStepCount == null) {
|
|
2058
2357
|
state.executedStepCount = state.steps.filter(
|
|
@@ -2189,6 +2488,12 @@ async function makeContext(state) {
|
|
|
2189
2488
|
}
|
|
2190
2489
|
throw new ReturnToStepSignal(targetIndex, stepId);
|
|
2191
2490
|
},
|
|
2491
|
+
changeApplication: async () => {
|
|
2492
|
+
const currentAppId = runAppId(state);
|
|
2493
|
+
const next = await promptToChangeApplication(currentAppId);
|
|
2494
|
+
if (next?.id) throw new RestartWorkflowSignal(next.id);
|
|
2495
|
+
},
|
|
2496
|
+
appId: runAppId(state),
|
|
2192
2497
|
config: await loadConfig()
|
|
2193
2498
|
};
|
|
2194
2499
|
}
|
|
@@ -2201,13 +2506,16 @@ async function rewindTo(state, targetIndex, fromIndex) {
|
|
|
2201
2506
|
}
|
|
2202
2507
|
state.currentStepIndex = targetIndex;
|
|
2203
2508
|
state.updatedAt = nowIso();
|
|
2204
|
-
useWizard.getState()
|
|
2509
|
+
const store = useWizard.getState();
|
|
2510
|
+
store.setActiveStep(targetIndex);
|
|
2511
|
+
store.syncSteps([...state.steps], targetIndex);
|
|
2205
2512
|
await saveWorkflowState(state);
|
|
2206
2513
|
}
|
|
2207
|
-
async function runStep(state, index, step
|
|
2514
|
+
async function runStep(state, index, step) {
|
|
2208
2515
|
const store = useWizard.getState();
|
|
2209
2516
|
const record = state.steps[index];
|
|
2210
2517
|
const startedAt = Date.now();
|
|
2518
|
+
const appId = runAppId(state);
|
|
2211
2519
|
trackActionStart({
|
|
2212
2520
|
workflowId: state.workflowId,
|
|
2213
2521
|
appId,
|
|
@@ -2235,6 +2543,16 @@ async function runStep(state, index, step, appId) {
|
|
|
2235
2543
|
await rewindTo(state, err.targetIndex, index);
|
|
2236
2544
|
return err.targetIndex;
|
|
2237
2545
|
}
|
|
2546
|
+
if (err instanceof RestartWorkflowSignal) {
|
|
2547
|
+
trackActionAbandoned({
|
|
2548
|
+
workflowId: state.workflowId,
|
|
2549
|
+
appId,
|
|
2550
|
+
actionId: step.id,
|
|
2551
|
+
actionTitle: step.title,
|
|
2552
|
+
durationMs: Date.now() - startedAt,
|
|
2553
|
+
reason: "application_changed"
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2238
2556
|
throw err;
|
|
2239
2557
|
}
|
|
2240
2558
|
record.status = "done";
|
|
@@ -2253,57 +2571,106 @@ async function runStep(state, index, step, appId) {
|
|
|
2253
2571
|
}
|
|
2254
2572
|
var RESUME_OPTION = "Resume";
|
|
2255
2573
|
var START_NEW_OPTION = "Start a new one";
|
|
2574
|
+
async function startFresh(workflow, appId) {
|
|
2575
|
+
await clearWorkflowState(workflow.id);
|
|
2576
|
+
return initWorkflowState(workflow, nowIso(), appId);
|
|
2577
|
+
}
|
|
2256
2578
|
async function resolveWorkflowState(workflow, appId, options) {
|
|
2257
2579
|
const resumed = options?.resumableState !== void 0 ? options.resumableState : await loadResumableState(workflow);
|
|
2258
2580
|
if (!resumed) {
|
|
2259
2581
|
return initWorkflowState(workflow, nowIso(), appId);
|
|
2260
2582
|
}
|
|
2583
|
+
const savedAppId = runAppId(resumed);
|
|
2261
2584
|
if (options?.skipResumePrompt) {
|
|
2585
|
+
if (appId && savedAppId === void 0) {
|
|
2586
|
+
resumed.algoliaState.appId = appId;
|
|
2587
|
+
}
|
|
2262
2588
|
return resumed;
|
|
2263
2589
|
}
|
|
2590
|
+
if (appId && savedAppId !== appId) {
|
|
2591
|
+
logger.warn(
|
|
2592
|
+
{ workflowId: workflow.id, savedAppId, appId },
|
|
2593
|
+
"resolveWorkflowState: persisted run is not pinned to the selected application; starting from scratch"
|
|
2594
|
+
);
|
|
2595
|
+
return startFresh(workflow, appId);
|
|
2596
|
+
}
|
|
2264
2597
|
const answer = await useWizard.getState().requestUserInput({
|
|
2265
2598
|
prompt: "You have a previous session. Do you want to resume, or start a new one?",
|
|
2266
2599
|
promptType: "multipleChoice",
|
|
2267
2600
|
options: [RESUME_OPTION, START_NEW_OPTION]
|
|
2268
2601
|
});
|
|
2269
2602
|
if (answer === START_NEW_OPTION) {
|
|
2270
|
-
|
|
2271
|
-
return initWorkflowState(workflow, nowIso(), appId);
|
|
2603
|
+
return startFresh(workflow, appId);
|
|
2272
2604
|
}
|
|
2273
2605
|
return resumed;
|
|
2274
2606
|
}
|
|
2275
|
-
async function
|
|
2607
|
+
async function beginRun(workflow, state) {
|
|
2608
|
+
ensureExecutedStepCount(state);
|
|
2609
|
+
ensureAgentRunId(state);
|
|
2610
|
+
setAnalyticsContext({ appId: runAppId(state), agentRunId: state.agentRunId });
|
|
2611
|
+
useWizard.getState().startWorkflow(
|
|
2612
|
+
{
|
|
2613
|
+
id: workflow.id,
|
|
2614
|
+
title: workflow.title,
|
|
2615
|
+
description: workflow.description
|
|
2616
|
+
},
|
|
2617
|
+
[...state.steps]
|
|
2618
|
+
);
|
|
2619
|
+
await ensureConsent();
|
|
2620
|
+
trackWorkflowStart({ workflowId: workflow.id, appId: runAppId(state) });
|
|
2621
|
+
}
|
|
2622
|
+
async function handleRestartWorkflow(workflow, previous, newAppId) {
|
|
2623
|
+
trackWorkflowAbandoned({
|
|
2624
|
+
workflowId: workflow.id,
|
|
2625
|
+
appId: runAppId(previous),
|
|
2626
|
+
reason: "application_changed"
|
|
2627
|
+
});
|
|
2628
|
+
await clearWorkflowState(workflow.id);
|
|
2629
|
+
const next = initWorkflowState(workflow, nowIso(), newAppId);
|
|
2630
|
+
useWizard.getState().resetRunState();
|
|
2631
|
+
useWizard.getState().setLoadingMessage("Restarting wizard\u2026");
|
|
2632
|
+
try {
|
|
2633
|
+
await beginRun(workflow, next);
|
|
2634
|
+
} finally {
|
|
2635
|
+
useWizard.getState().setLoadingMessage(null);
|
|
2636
|
+
}
|
|
2637
|
+
return next;
|
|
2638
|
+
}
|
|
2639
|
+
async function runWorkflow(workflow, startWorkflowAppId, options) {
|
|
2276
2640
|
const store = useWizard.getState();
|
|
2277
2641
|
try {
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
store.startWorkflow(
|
|
2283
|
-
{
|
|
2284
|
-
id: workflow.id,
|
|
2285
|
-
title: workflow.title,
|
|
2286
|
-
description: workflow.description
|
|
2287
|
-
},
|
|
2288
|
-
[...state.steps]
|
|
2642
|
+
let state = await resolveWorkflowState(
|
|
2643
|
+
workflow,
|
|
2644
|
+
startWorkflowAppId,
|
|
2645
|
+
options
|
|
2289
2646
|
);
|
|
2290
|
-
await
|
|
2291
|
-
trackWorkflowStart({ workflowId: workflow.id, appId });
|
|
2647
|
+
await beginRun(workflow, state);
|
|
2292
2648
|
track("AI Wizard Started", {
|
|
2293
2649
|
version: package_default.version,
|
|
2294
2650
|
wizard_version: package_default.version,
|
|
2295
2651
|
os: process.platform
|
|
2296
2652
|
});
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2653
|
+
for (; ; ) {
|
|
2654
|
+
try {
|
|
2655
|
+
let i = state.currentStepIndex;
|
|
2656
|
+
while (i < workflow.steps.length) {
|
|
2657
|
+
const rewoundTo = await runStep(state, i, workflow.steps[i]);
|
|
2658
|
+
i = rewoundTo ?? i + 1;
|
|
2659
|
+
}
|
|
2660
|
+
break;
|
|
2661
|
+
} catch (err) {
|
|
2662
|
+
if (err instanceof RestartWorkflowSignal) {
|
|
2663
|
+
state = await handleRestartWorkflow(workflow, state, err.appId);
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
throw err;
|
|
2667
|
+
}
|
|
2301
2668
|
}
|
|
2302
2669
|
const totalDurationMs = Date.now() - Date.parse(state.startedAt);
|
|
2303
2670
|
const stepCount = state.executedStepCount ?? state.steps.filter((s) => s.status === "done" && isStepVisible(s)).length;
|
|
2304
2671
|
trackWizardComplete({
|
|
2305
2672
|
workflowId: workflow.id,
|
|
2306
|
-
appId,
|
|
2673
|
+
appId: runAppId(state),
|
|
2307
2674
|
total_duration: Math.round(totalDurationMs / 1e3),
|
|
2308
2675
|
total_steps: stepCount
|
|
2309
2676
|
});
|
|
@@ -2317,19 +2684,20 @@ async function runWorkflow(workflow, appId, options) {
|
|
|
2317
2684
|
store.setDone();
|
|
2318
2685
|
} catch (err) {
|
|
2319
2686
|
const message = err instanceof Error ? err.message : String(err);
|
|
2320
|
-
const
|
|
2687
|
+
const failed = await loadWorkflowState(workflow.id);
|
|
2688
|
+
const failedAppId = failed ? runAppId(failed) : startWorkflowAppId;
|
|
2321
2689
|
let step = "unknown";
|
|
2322
2690
|
let failedActionId;
|
|
2323
2691
|
let failedActionTitle;
|
|
2324
|
-
if (
|
|
2325
|
-
const record =
|
|
2692
|
+
if (failed) {
|
|
2693
|
+
const record = failed.steps[failed.currentStepIndex];
|
|
2326
2694
|
if (record) {
|
|
2327
2695
|
step = record.title;
|
|
2328
2696
|
if (record.status === "running") {
|
|
2329
2697
|
record.status = "error";
|
|
2330
2698
|
record.error = message;
|
|
2331
|
-
await saveWorkflowState(
|
|
2332
|
-
store.syncSteps([...
|
|
2699
|
+
await saveWorkflowState(failed);
|
|
2700
|
+
store.syncSteps([...failed.steps], failed.currentStepIndex);
|
|
2333
2701
|
}
|
|
2334
2702
|
failedActionId = record.id;
|
|
2335
2703
|
failedActionTitle = record.title;
|
|
@@ -2338,7 +2706,7 @@ async function runWorkflow(workflow, appId, options) {
|
|
|
2338
2706
|
if (failedActionId && failedActionTitle) {
|
|
2339
2707
|
trackActionError({
|
|
2340
2708
|
workflowId: workflow.id,
|
|
2341
|
-
appId,
|
|
2709
|
+
appId: failedAppId,
|
|
2342
2710
|
actionId: failedActionId,
|
|
2343
2711
|
actionTitle: failedActionTitle,
|
|
2344
2712
|
error: message,
|
|
@@ -2347,7 +2715,7 @@ async function runWorkflow(workflow, appId, options) {
|
|
|
2347
2715
|
}
|
|
2348
2716
|
trackWorkflowError({
|
|
2349
2717
|
workflowId: workflow.id,
|
|
2350
|
-
appId,
|
|
2718
|
+
appId: failedAppId,
|
|
2351
2719
|
error: message,
|
|
2352
2720
|
actionId: failedActionId,
|
|
2353
2721
|
cause: err
|
|
@@ -2367,12 +2735,12 @@ async function runWorkflow(workflow, appId, options) {
|
|
|
2367
2735
|
}
|
|
2368
2736
|
|
|
2369
2737
|
// src/actions/listIndices.ts
|
|
2370
|
-
import { z as
|
|
2371
|
-
var indicesListSchema =
|
|
2372
|
-
items:
|
|
2373
|
-
|
|
2374
|
-
name:
|
|
2375
|
-
entries:
|
|
2738
|
+
import { z as z5 } from "zod";
|
|
2739
|
+
var indicesListSchema = z5.object({
|
|
2740
|
+
items: z5.array(
|
|
2741
|
+
z5.object({
|
|
2742
|
+
name: z5.string(),
|
|
2743
|
+
entries: z5.number().default(0)
|
|
2376
2744
|
})
|
|
2377
2745
|
)
|
|
2378
2746
|
});
|
|
@@ -2382,83 +2750,10 @@ async function listIndices() {
|
|
|
2382
2750
|
return items.map((i) => ({ name: i.name, entries: i.entries })).sort((a, b) => a.name.localeCompare(b.name));
|
|
2383
2751
|
}
|
|
2384
2752
|
|
|
2385
|
-
// src/lib/algoliaApp.ts
|
|
2386
|
-
import { z as z5 } from "zod";
|
|
2387
|
-
var applicationSchema = z5.object({
|
|
2388
|
-
id: z5.string().min(1),
|
|
2389
|
-
name: z5.string().default(""),
|
|
2390
|
-
plan: z5.string().optional()
|
|
2391
|
-
});
|
|
2392
|
-
var listSchema = z5.array(
|
|
2393
|
-
z5.object({
|
|
2394
|
-
id: z5.string().min(1),
|
|
2395
|
-
name: z5.string().default(""),
|
|
2396
|
-
plan_label: z5.string().optional(),
|
|
2397
|
-
status: z5.string().optional(),
|
|
2398
|
-
acl: z5.array(z5.string()).optional()
|
|
2399
|
-
}).transform(({ id, name, plan_label, status, acl }) => ({
|
|
2400
|
-
id,
|
|
2401
|
-
name,
|
|
2402
|
-
plan: plan_label,
|
|
2403
|
-
status,
|
|
2404
|
-
acl
|
|
2405
|
-
}))
|
|
2406
|
-
);
|
|
2407
|
-
function canSelectApplication(app) {
|
|
2408
|
-
return app.status === "active" && (app.acl ?? []).includes("keys");
|
|
2409
|
-
}
|
|
2410
|
-
async function currentApplication() {
|
|
2411
|
-
let raw;
|
|
2412
|
-
try {
|
|
2413
|
-
raw = await runAlgoliaCli(["application", "current", "-o", "json"]);
|
|
2414
|
-
} catch {
|
|
2415
|
-
return null;
|
|
2416
|
-
}
|
|
2417
|
-
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2418
|
-
return parsed.success ? parsed.data : null;
|
|
2419
|
-
}
|
|
2420
|
-
async function requireApplication() {
|
|
2421
|
-
const app = await currentApplication();
|
|
2422
|
-
if (!app) {
|
|
2423
|
-
throw new Error(
|
|
2424
|
-
"No Algolia application is selected. Run `npx @algolia/cli@latest application select` and restart the wizard."
|
|
2425
|
-
);
|
|
2426
|
-
}
|
|
2427
|
-
return app;
|
|
2428
|
-
}
|
|
2429
|
-
async function listApplications() {
|
|
2430
|
-
const raw = await runAlgoliaCli(["application", "list", "-o", "json"]);
|
|
2431
|
-
const parsed = listSchema.safeParse(parseJson(raw));
|
|
2432
|
-
if (!parsed.success) {
|
|
2433
|
-
throw new Error("Could not read the list of Algolia applications.");
|
|
2434
|
-
}
|
|
2435
|
-
return parsed.data;
|
|
2436
|
-
}
|
|
2437
|
-
async function selectApplication(id) {
|
|
2438
|
-
const raw = await runAlgoliaCli(
|
|
2439
|
-
["application", "select", "--non-interactive", "--app-id", id],
|
|
2440
|
-
{ onOutput: stderrSink }
|
|
2441
|
-
);
|
|
2442
|
-
const parsed = applicationSchema.safeParse(parseJson(raw));
|
|
2443
|
-
if (!parsed.success) {
|
|
2444
|
-
throw new Error(
|
|
2445
|
-
`Selected application ${id}, but the Algolia CLI returned an unreadable result.`
|
|
2446
|
-
);
|
|
2447
|
-
}
|
|
2448
|
-
return parsed.data;
|
|
2449
|
-
}
|
|
2450
|
-
function parseJson(text) {
|
|
2451
|
-
try {
|
|
2452
|
-
return JSON.parse(text);
|
|
2453
|
-
} catch {
|
|
2454
|
-
return void 0;
|
|
2455
|
-
}
|
|
2456
|
-
}
|
|
2457
|
-
|
|
2458
2753
|
// src/actions/selectIndex.ts
|
|
2459
2754
|
var CREATE_NEW_INDEX = "Create a new index\u2026";
|
|
2460
2755
|
var selectIndexStep = async (ctx) => {
|
|
2461
|
-
await requireApplication();
|
|
2756
|
+
await requireApplication(ctx.appId);
|
|
2462
2757
|
const indices = await listIndices();
|
|
2463
2758
|
const names = indices.map((i) => i.name);
|
|
2464
2759
|
const hasIndices = names.length > 0;
|
|
@@ -2532,7 +2827,7 @@ import { readdir } from "node:fs/promises";
|
|
|
2532
2827
|
// src/lib/tools/path.ts
|
|
2533
2828
|
import { constants } from "node:fs";
|
|
2534
2829
|
import { lstat, mkdir as mkdir3, open } from "node:fs/promises";
|
|
2535
|
-
import { resolve as resolve2, relative, isAbsolute, dirname as dirname2, join as
|
|
2830
|
+
import { resolve as resolve2, relative, isAbsolute, dirname as dirname2, join as join6, sep } from "node:path";
|
|
2536
2831
|
var DEFAULT_FILE_MODE = 438;
|
|
2537
2832
|
function errorCode(err) {
|
|
2538
2833
|
if (typeof err === "object" && err !== null && "code" in err && typeof err.code === "string") {
|
|
@@ -2555,7 +2850,7 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
2555
2850
|
let current = ctx.root;
|
|
2556
2851
|
const parts = relative(ctx.root, dirname2(target)).split(sep).filter(Boolean);
|
|
2557
2852
|
for (const part of parts) {
|
|
2558
|
-
current =
|
|
2853
|
+
current = join6(current, part);
|
|
2559
2854
|
try {
|
|
2560
2855
|
if ((await lstat(current)).isSymbolicLink()) return true;
|
|
2561
2856
|
} catch (err) {
|
|
@@ -2673,7 +2968,7 @@ function reportStatusTool(output) {
|
|
|
2673
2968
|
// src/lib/tools/readFile.ts
|
|
2674
2969
|
import { tool as tool4 } from "ai";
|
|
2675
2970
|
import z9 from "zod";
|
|
2676
|
-
import { readFile as
|
|
2971
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
2677
2972
|
|
|
2678
2973
|
// src/lib/tools/env.ts
|
|
2679
2974
|
import { basename } from "node:path";
|
|
@@ -2761,7 +3056,7 @@ function readFileTool(ctx) {
|
|
|
2761
3056
|
const resolved2 = resolveInRoot(ctx, filePath);
|
|
2762
3057
|
if (resolved2.ok === false) return resolved2.error;
|
|
2763
3058
|
try {
|
|
2764
|
-
const content = await
|
|
3059
|
+
const content = await readFile4(resolved2.target, "utf8");
|
|
2765
3060
|
const safeContent = isEnvFile(resolved2.target) ? redactEnvValues(content) : content;
|
|
2766
3061
|
return readLines ? readLineRange(safeContent, [readLines[0], readLines[1]]) : sampleFile(safeContent);
|
|
2767
3062
|
} catch (err) {
|
|
@@ -2803,7 +3098,7 @@ function writeFileTool(ctx) {
|
|
|
2803
3098
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
2804
3099
|
import { tool as tool6 } from "ai";
|
|
2805
3100
|
import z13 from "zod";
|
|
2806
|
-
import { readFile as
|
|
3101
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
2807
3102
|
import { relative as relative3 } from "node:path";
|
|
2808
3103
|
|
|
2809
3104
|
// src/lib/algoliaApiKey.ts
|
|
@@ -3007,8 +3302,8 @@ async function resolveSearchOnlyKey(index, appId) {
|
|
|
3007
3302
|
|
|
3008
3303
|
// src/lib/gitignore.ts
|
|
3009
3304
|
import { execFile } from "node:child_process";
|
|
3010
|
-
import { lstat as lstat2, readFile as
|
|
3011
|
-
import { join as
|
|
3305
|
+
import { lstat as lstat2, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
|
|
3306
|
+
import { join as join7, relative as relative2 } from "node:path";
|
|
3012
3307
|
var GIT_ENV_OVERRIDES = [
|
|
3013
3308
|
"GIT_DIR",
|
|
3014
3309
|
"GIT_WORK_TREE",
|
|
@@ -3060,7 +3355,7 @@ async function ensureGitIgnored(root, target) {
|
|
|
3060
3355
|
if (ignoredByRule === void 0) return "unknown";
|
|
3061
3356
|
if (ignoredByRule) return tracked ? "tracked" : "covered";
|
|
3062
3357
|
const pattern = relative2(root, target);
|
|
3063
|
-
const gitIgnore =
|
|
3358
|
+
const gitIgnore = join7(root, ".gitignore");
|
|
3064
3359
|
try {
|
|
3065
3360
|
const link = await lstat2(gitIgnore).catch(() => null);
|
|
3066
3361
|
if (link?.isSymbolicLink()) {
|
|
@@ -3070,7 +3365,7 @@ async function ensureGitIgnored(root, target) {
|
|
|
3070
3365
|
);
|
|
3071
3366
|
return "unknown";
|
|
3072
3367
|
}
|
|
3073
|
-
const existing = link ? await
|
|
3368
|
+
const existing = link ? await readFile5(gitIgnore, "utf8") : "";
|
|
3074
3369
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
3075
3370
|
await writeFile3(gitIgnore, `${existing}${prefix}${pattern}
|
|
3076
3371
|
`, "utf8");
|
|
@@ -3138,7 +3433,7 @@ function writeCredentialsTool(ctx) {
|
|
|
3138
3433
|
const targetError = await validateSafeWriteTarget(ctx, resolved2.target);
|
|
3139
3434
|
if (targetError) return targetError;
|
|
3140
3435
|
try {
|
|
3141
|
-
existing = await
|
|
3436
|
+
existing = await readFile6(resolved2.target, "utf8");
|
|
3142
3437
|
} catch (err) {
|
|
3143
3438
|
if (err.code !== "ENOENT") throw err;
|
|
3144
3439
|
}
|
|
@@ -3155,7 +3450,7 @@ function writeCredentialsTool(ctx) {
|
|
|
3155
3450
|
const fileIndex = readEnv(existing, INDEX_NAME_VAR);
|
|
3156
3451
|
if (fileAppId === null || fileKey === null) {
|
|
3157
3452
|
try {
|
|
3158
|
-
const selected = (await requireApplication()).id;
|
|
3453
|
+
const selected = (await requireApplication(ctx.appId)).id;
|
|
3159
3454
|
if (fileAppId === null) credentials.push([APP_ID_VAR, selected]);
|
|
3160
3455
|
if (fileKey === null) {
|
|
3161
3456
|
if (fileAppId !== null && fileAppId !== selected) {
|
|
@@ -3174,7 +3469,7 @@ function writeCredentialsTool(ctx) {
|
|
|
3174
3469
|
);
|
|
3175
3470
|
}
|
|
3176
3471
|
} catch (err) {
|
|
3177
|
-
return `Error: could not resolve Algolia credentials (${err.message})
|
|
3472
|
+
return `Error: could not resolve Algolia credentials (${err.message})`;
|
|
3178
3473
|
}
|
|
3179
3474
|
}
|
|
3180
3475
|
let wrote;
|
|
@@ -3228,8 +3523,8 @@ async function gitIgnoreOutcome(ctx, target) {
|
|
|
3228
3523
|
// src/lib/tools/searchFiles.ts
|
|
3229
3524
|
import { tool as tool7 } from "ai";
|
|
3230
3525
|
import z14 from "zod";
|
|
3231
|
-
import { readdir as readdir2, readFile as
|
|
3232
|
-
import { join as
|
|
3526
|
+
import { readdir as readdir2, readFile as readFile7 } from "node:fs/promises";
|
|
3527
|
+
import { join as join8 } from "node:path";
|
|
3233
3528
|
var MAX_QUERY_LENGTH = 1e3;
|
|
3234
3529
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
3235
3530
|
"node_modules",
|
|
@@ -3244,7 +3539,7 @@ async function walkFiles(dir) {
|
|
|
3244
3539
|
const out = [];
|
|
3245
3540
|
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
3246
3541
|
if (e.name.startsWith(".") || SKIP_DIRS.has(e.name)) continue;
|
|
3247
|
-
const full =
|
|
3542
|
+
const full = join8(dir, e.name);
|
|
3248
3543
|
if (e.isDirectory()) out.push(...await walkFiles(full));
|
|
3249
3544
|
else if (e.isFile()) out.push(full);
|
|
3250
3545
|
}
|
|
@@ -3277,7 +3572,7 @@ function searchFilesTool(ctx) {
|
|
|
3277
3572
|
for (const file of await walkFiles(resolved2.target)) {
|
|
3278
3573
|
let content;
|
|
3279
3574
|
try {
|
|
3280
|
-
content = await
|
|
3575
|
+
content = await readFile7(file, "utf8");
|
|
3281
3576
|
} catch {
|
|
3282
3577
|
continue;
|
|
3283
3578
|
}
|
|
@@ -3307,7 +3602,7 @@ import { relative as relative5 } from "node:path";
|
|
|
3307
3602
|
import { tool as tool8 } from "ai";
|
|
3308
3603
|
import z15 from "zod";
|
|
3309
3604
|
import { createHash as createHash2 } from "node:crypto";
|
|
3310
|
-
import { readFile as
|
|
3605
|
+
import { readFile as readFile8, stat as stat2 } from "node:fs/promises";
|
|
3311
3606
|
import { relative as relative4 } from "node:path";
|
|
3312
3607
|
|
|
3313
3608
|
// src/lib/tools/utils/prompt.ts
|
|
@@ -3322,7 +3617,7 @@ function serializePrompt(work) {
|
|
|
3322
3617
|
// src/lib/tools/reviewScript.ts
|
|
3323
3618
|
var reviewedCommandApprovals = /* @__PURE__ */ new WeakMap();
|
|
3324
3619
|
async function fileDigest(filePath) {
|
|
3325
|
-
const content = await
|
|
3620
|
+
const content = await readFile8(filePath);
|
|
3326
3621
|
return createHash2("sha256").update(content).digest("hex");
|
|
3327
3622
|
}
|
|
3328
3623
|
function grantReviewedCommandApproval(ctx, approval) {
|
|
@@ -4913,13 +5208,13 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
4913
5208
|
|
|
4914
5209
|
// src/actions/implement.ts
|
|
4915
5210
|
import z28 from "zod";
|
|
4916
|
-
import { access, mkdir as mkdir5, readFile as
|
|
4917
|
-
import { dirname as dirname5, isAbsolute as isAbsolute3, join as
|
|
5211
|
+
import { access, mkdir as mkdir5, readFile as readFile9 } from "node:fs/promises";
|
|
5212
|
+
import { dirname as dirname5, isAbsolute as isAbsolute3, join as join11, relative as relative6, resolve as resolve4, sep as sep2 } from "node:path";
|
|
4918
5213
|
|
|
4919
5214
|
// src/lib/git.ts
|
|
4920
5215
|
import { execFile as execFile2 } from "node:child_process";
|
|
4921
5216
|
import { copyFile, mkdir as mkdir4, stat as stat3 } from "node:fs/promises";
|
|
4922
|
-
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
5217
|
+
import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join9, resolve as resolve3 } from "node:path";
|
|
4923
5218
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
4924
5219
|
function git(args) {
|
|
4925
5220
|
return new Promise((resolve5, reject) => {
|
|
@@ -4956,8 +5251,8 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
|
|
|
4956
5251
|
} catch {
|
|
4957
5252
|
return { ok: false, reason: `"${sourcePath}" does not exist` };
|
|
4958
5253
|
}
|
|
4959
|
-
const relPath =
|
|
4960
|
-
const dest =
|
|
5254
|
+
const relPath = join9(ingestDir, basename2(source));
|
|
5255
|
+
const dest = join9(repoRoot, relPath);
|
|
4961
5256
|
if (resolve3(source) === resolve3(dest)) {
|
|
4962
5257
|
return { ok: true, relPath };
|
|
4963
5258
|
}
|
|
@@ -4996,13 +5291,13 @@ function toRootRelative(p) {
|
|
|
4996
5291
|
|
|
4997
5292
|
// src/lib/algoliaDocs.ts
|
|
4998
5293
|
import { readFileSync, readdirSync, existsSync } from "node:fs";
|
|
4999
|
-
import { dirname as dirname4, join as
|
|
5294
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
5000
5295
|
import { fileURLToPath } from "node:url";
|
|
5001
|
-
var DOCS_SUBPATH =
|
|
5296
|
+
var DOCS_SUBPATH = join10("docs", "algolia-sdk");
|
|
5002
5297
|
function findDocsDir() {
|
|
5003
5298
|
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
5004
5299
|
for (; ; ) {
|
|
5005
|
-
const candidate =
|
|
5300
|
+
const candidate = join10(dir, DOCS_SUBPATH);
|
|
5006
5301
|
if (existsSync(candidate)) return candidate;
|
|
5007
5302
|
const parent = dirname4(dir);
|
|
5008
5303
|
if (parent === dir) return void 0;
|
|
@@ -5025,7 +5320,7 @@ function loadAlgoliaDoc(language) {
|
|
|
5025
5320
|
);
|
|
5026
5321
|
return "";
|
|
5027
5322
|
}
|
|
5028
|
-
return readFileSync(
|
|
5323
|
+
return readFileSync(join10(docsDir, files[0]), "utf8").trim();
|
|
5029
5324
|
}
|
|
5030
5325
|
function getNamedDoc(name, language) {
|
|
5031
5326
|
const docsDir = findDocsDir();
|
|
@@ -5033,7 +5328,7 @@ function getNamedDoc(name, language) {
|
|
|
5033
5328
|
logger.warn("docs/algolia-sdk not found");
|
|
5034
5329
|
return "";
|
|
5035
5330
|
}
|
|
5036
|
-
const file =
|
|
5331
|
+
const file = join10(docsDir, `${name}-${language}.md`);
|
|
5037
5332
|
if (!existsSync(file)) {
|
|
5038
5333
|
logger.warn({ name, language }, "named SDK reference not found");
|
|
5039
5334
|
return "";
|
|
@@ -5363,7 +5658,7 @@ function jsTypeName(value) {
|
|
|
5363
5658
|
}
|
|
5364
5659
|
async function firstRecordIn(filePath) {
|
|
5365
5660
|
try {
|
|
5366
|
-
const parsed = JSON.parse(await
|
|
5661
|
+
const parsed = JSON.parse(await readFile9(filePath, "utf8"));
|
|
5367
5662
|
const first = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
5368
5663
|
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
5369
5664
|
return first;
|
|
@@ -5377,10 +5672,10 @@ async function ingestedRecordSample(repoRoot, entityName, uploadFilePath) {
|
|
|
5377
5672
|
const candidates = [];
|
|
5378
5673
|
if (entityName) {
|
|
5379
5674
|
const slug = entityName.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
5380
|
-
candidates.push(
|
|
5675
|
+
candidates.push(join11(repoRoot, INGEST_DIR, "data", `${slug}.json`));
|
|
5381
5676
|
}
|
|
5382
5677
|
if (uploadFilePath?.toLowerCase().endsWith(".json")) {
|
|
5383
|
-
candidates.push(
|
|
5678
|
+
candidates.push(join11(repoRoot, uploadFilePath));
|
|
5384
5679
|
}
|
|
5385
5680
|
for (const candidate of candidates) {
|
|
5386
5681
|
const sample = await firstRecordIn(candidate);
|
|
@@ -5410,12 +5705,15 @@ function ingestFailure(attempt, executions) {
|
|
|
5410
5705
|
}
|
|
5411
5706
|
return { reason: `it failed (exit ${attempt.exitCode ?? "unknown"})`, detail };
|
|
5412
5707
|
}
|
|
5413
|
-
function makeToolContext(root, env = async () => ({})) {
|
|
5414
|
-
return
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5708
|
+
function makeToolContext(root, appId, env = async () => ({})) {
|
|
5709
|
+
return {
|
|
5710
|
+
...createToolContext(
|
|
5711
|
+
DEFAULT_TOOL_LIMITS,
|
|
5712
|
+
root,
|
|
5713
|
+
createShellContext({ env, approve: storeApproval(root) })
|
|
5714
|
+
),
|
|
5715
|
+
appId
|
|
5716
|
+
};
|
|
5419
5717
|
}
|
|
5420
5718
|
function validationRetryInstructions(validation) {
|
|
5421
5719
|
return [
|
|
@@ -5439,7 +5737,7 @@ async function frontendHasPackageJson(repoRoot, searchLocation) {
|
|
|
5439
5737
|
if (!outsideRoot) directory = candidate;
|
|
5440
5738
|
}
|
|
5441
5739
|
for (; ; ) {
|
|
5442
|
-
if (await pathExists(
|
|
5740
|
+
if (await pathExists(join11(directory, "package.json"))) return true;
|
|
5443
5741
|
if (directory === repoRoot) return false;
|
|
5444
5742
|
directory = dirname5(directory);
|
|
5445
5743
|
}
|
|
@@ -5448,7 +5746,7 @@ async function resolveBuildCheckPlan(ctx, repoRoot, input) {
|
|
|
5448
5746
|
if (!input.findings.verification?.length) {
|
|
5449
5747
|
return { run: false, installDependencies: false };
|
|
5450
5748
|
}
|
|
5451
|
-
const installDependencies = input.frontendHasPackageJson && isJsProject(input.language) && !await pathExists(
|
|
5749
|
+
const installDependencies = input.frontendHasPackageJson && isJsProject(input.language) && !await pathExists(join11(repoRoot, "node_modules"));
|
|
5452
5750
|
const accepted = Boolean(
|
|
5453
5751
|
await ctx.requestUserInput({
|
|
5454
5752
|
prompt: installDependencies ? MISSING_DEPENDENCIES_PROMPT : BUILD_CHECK_PROMPT,
|
|
@@ -5526,7 +5824,7 @@ async function resolveIngestionSource(ctx, repoRoot) {
|
|
|
5526
5824
|
messages: []
|
|
5527
5825
|
});
|
|
5528
5826
|
const uploadSourcePath = typeof answer === "string" ? answer : "";
|
|
5529
|
-
await mkdir5(
|
|
5827
|
+
await mkdir5(join11(repoRoot, INGEST_DIR), { recursive: true });
|
|
5530
5828
|
const copied = await copyUploadIntoProject(
|
|
5531
5829
|
repoRoot,
|
|
5532
5830
|
INGEST_DIR,
|
|
@@ -5536,10 +5834,7 @@ async function resolveIngestionSource(ctx, repoRoot) {
|
|
|
5536
5834
|
return { ingestionSource: "fileUpload", uploadFilePath: copied.relPath };
|
|
5537
5835
|
}
|
|
5538
5836
|
error = `Could not use the uploaded file (${copied.reason}).`;
|
|
5539
|
-
logger.warn(
|
|
5540
|
-
{ reason: copied.reason },
|
|
5541
|
-
"implement: file upload unavailable"
|
|
5542
|
-
);
|
|
5837
|
+
logger.warn({ reason: copied.reason }, "implement: file upload unavailable");
|
|
5543
5838
|
}
|
|
5544
5839
|
}
|
|
5545
5840
|
async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
@@ -5562,6 +5857,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5562
5857
|
let ingestionSource = "generated";
|
|
5563
5858
|
let uploadFilePath = void 0;
|
|
5564
5859
|
if (useCases.includes("ingestion")) {
|
|
5860
|
+
;
|
|
5565
5861
|
({ ingestionSource, uploadFilePath } = await resolveIngestionSource(
|
|
5566
5862
|
ctx,
|
|
5567
5863
|
repoRoot
|
|
@@ -5570,7 +5866,7 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5570
5866
|
const targetIndex = selected?.selection;
|
|
5571
5867
|
useWizard.getState().setTargetIndex(targetIndex ?? null);
|
|
5572
5868
|
if (useCases.includes("ingestion")) {
|
|
5573
|
-
await mkdir5(
|
|
5869
|
+
await mkdir5(join11(repoRoot, INGEST_DIR), { recursive: true });
|
|
5574
5870
|
}
|
|
5575
5871
|
const findings = normalizeFindingPaths({
|
|
5576
5872
|
ingestionAnalysis: entities?.ingestionAnalysis ?? scan.ingestionAnalysis,
|
|
@@ -5583,10 +5879,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5583
5879
|
let appId;
|
|
5584
5880
|
let ingestAppId;
|
|
5585
5881
|
if (useCases.includes("search")) {
|
|
5586
|
-
appId = (await requireApplication()).id;
|
|
5882
|
+
appId = (await requireApplication(ctx.appId)).id;
|
|
5587
5883
|
}
|
|
5588
5884
|
if (useCases.includes("ingestion")) {
|
|
5589
|
-
ingestAppId = appId ?? (await requireApplication()).id;
|
|
5885
|
+
ingestAppId = appId ?? (await requireApplication(ctx.appId)).id;
|
|
5590
5886
|
}
|
|
5591
5887
|
let ingestWriteKey;
|
|
5592
5888
|
if (useCases.includes("ingestion") && ingestAppId) {
|
|
@@ -5638,11 +5934,11 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5638
5934
|
let ingestDurationMs;
|
|
5639
5935
|
let ingestOutcomeMessage;
|
|
5640
5936
|
const ingestKeyAppId = ingestAppId;
|
|
5641
|
-
const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, async () => ({
|
|
5937
|
+
const ingestionTools = ingestKeyAppId && ingestWriteKey ? makeToolContext(repoRoot, ctx.appId, async () => ({
|
|
5642
5938
|
[APP_ID_VAR]: ingestKeyAppId,
|
|
5643
5939
|
[API_KEY_VAR]: ingestWriteKey,
|
|
5644
5940
|
[INDEX_NAME_VAR]: targetIndex
|
|
5645
|
-
})) : makeToolContext(repoRoot);
|
|
5941
|
+
})) : makeToolContext(repoRoot, ctx.appId);
|
|
5646
5942
|
async function runImplementationUseCase(currentUseCase, extraInstructions = [], isRetry = false) {
|
|
5647
5943
|
if (agentRuns > 0) ctx.recordStepExecution();
|
|
5648
5944
|
agentRuns += 1;
|
|
@@ -5667,14 +5963,10 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5667
5963
|
);
|
|
5668
5964
|
return runAgent({
|
|
5669
5965
|
operation: "search-validation",
|
|
5670
|
-
instructions: buildAgentInstructions(
|
|
5671
|
-
"
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
writtenFiles.length ? `Files written during search implementation: ${JSON.stringify(writtenFiles)}. Inspect these first, then trace the actual import and render site.` : "The search agent reported no written files. Inspect the current diff and set sufficient=false if no complete mounted search implementation exists.",
|
|
5675
|
-
...extraInstructions
|
|
5676
|
-
]
|
|
5677
|
-
),
|
|
5966
|
+
instructions: buildAgentInstructions("validation", input, [
|
|
5967
|
+
writtenFiles.length ? `Files written during search implementation: ${JSON.stringify(writtenFiles)}. Inspect these first, then trace the actual import and render site.` : "The search agent reported no written files. Inspect the current diff and set sufficient=false if no complete mounted search implementation exists.",
|
|
5968
|
+
...extraInstructions
|
|
5969
|
+
]),
|
|
5678
5970
|
tools: toolsForUseCase("validation"),
|
|
5679
5971
|
outputSchema: validationOutputSchema,
|
|
5680
5972
|
modelProfile: "validation" /* validation */,
|
|
@@ -5884,7 +6176,7 @@ ${detail}` : ""}`
|
|
|
5884
6176
|
if (searchConfigFile) {
|
|
5885
6177
|
const ignoreStatus = await gitIgnoreStatus(
|
|
5886
6178
|
repoRoot,
|
|
5887
|
-
|
|
6179
|
+
join11(repoRoot, searchConfigFile)
|
|
5888
6180
|
);
|
|
5889
6181
|
if (ignoreStatus === "covered") {
|
|
5890
6182
|
summaries.push(
|
|
@@ -6028,13 +6320,21 @@ import { z as z32 } from "zod";
|
|
|
6028
6320
|
import "zod";
|
|
6029
6321
|
var DASHBOARD_API_BASE_URL = `${PROXY_BASE_URL}/dashboard`;
|
|
6030
6322
|
var DashboardApiError = class extends Error {
|
|
6031
|
-
constructor(status,
|
|
6032
|
-
super(
|
|
6323
|
+
constructor(status, body) {
|
|
6324
|
+
super("Couldn't complete the request.");
|
|
6033
6325
|
this.status = status;
|
|
6326
|
+
this.body = body;
|
|
6034
6327
|
this.name = "DashboardApiError";
|
|
6035
6328
|
}
|
|
6036
6329
|
status;
|
|
6330
|
+
body;
|
|
6331
|
+
errorBody() {
|
|
6332
|
+
return isRecord(this.body) ? this.body : void 0;
|
|
6333
|
+
}
|
|
6037
6334
|
};
|
|
6335
|
+
function isRecord(value) {
|
|
6336
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6337
|
+
}
|
|
6038
6338
|
async function dashboardRequest(path, schema, init) {
|
|
6039
6339
|
const token = getAuthToken();
|
|
6040
6340
|
if (!token) {
|
|
@@ -6049,17 +6349,24 @@ async function dashboardRequest(path, schema, init) {
|
|
|
6049
6349
|
}
|
|
6050
6350
|
});
|
|
6051
6351
|
if (!res.ok) {
|
|
6052
|
-
throw
|
|
6053
|
-
res.status,
|
|
6054
|
-
`Dashboard API responded ${res.status} for ${path}`
|
|
6055
|
-
);
|
|
6352
|
+
throw await dashboardError(res);
|
|
6056
6353
|
}
|
|
6057
6354
|
const body = await res.json();
|
|
6058
6355
|
return { status: res.status, data: schema.parse(body) };
|
|
6059
6356
|
}
|
|
6357
|
+
async function dashboardError(res) {
|
|
6358
|
+
try {
|
|
6359
|
+
return new DashboardApiError(res.status, await res.json());
|
|
6360
|
+
} catch {
|
|
6361
|
+
return new DashboardApiError(res.status, void 0);
|
|
6362
|
+
}
|
|
6363
|
+
}
|
|
6060
6364
|
|
|
6061
6365
|
// src/lib/dashboardApi/schemas.ts
|
|
6062
6366
|
import { z as z31 } from "zod";
|
|
6367
|
+
var messageResponseSchema = z31.object({
|
|
6368
|
+
message: z31.string()
|
|
6369
|
+
});
|
|
6063
6370
|
var playbookSchema = z31.object({
|
|
6064
6371
|
slug: z31.string(),
|
|
6065
6372
|
category: z31.string().optional(),
|
|
@@ -6067,6 +6374,57 @@ var playbookSchema = z31.object({
|
|
|
6067
6374
|
description: z31.string().optional()
|
|
6068
6375
|
});
|
|
6069
6376
|
var playbooksListSchema = z31.array(playbookSchema);
|
|
6377
|
+
var playbookExecutionStepSchema = z31.enum([
|
|
6378
|
+
"assessment",
|
|
6379
|
+
"optimization",
|
|
6380
|
+
"implement"
|
|
6381
|
+
]);
|
|
6382
|
+
var playbookExecutionStepStatusSchema = z31.enum([
|
|
6383
|
+
"not_started",
|
|
6384
|
+
"in_progress",
|
|
6385
|
+
"done",
|
|
6386
|
+
"error"
|
|
6387
|
+
]);
|
|
6388
|
+
var schemaComparisonEntrySchema = z31.object({
|
|
6389
|
+
canonical_attr: z31.string(),
|
|
6390
|
+
canonical_type: z31.array(z31.string()),
|
|
6391
|
+
canonical_roles: z31.array(z31.string()),
|
|
6392
|
+
canonical_required: z31.boolean(),
|
|
6393
|
+
index_attr: z31.string().nullable(),
|
|
6394
|
+
index_type: z31.array(z31.string()).nullable(),
|
|
6395
|
+
index_required: z31.boolean().nullable(),
|
|
6396
|
+
status: z31.string(),
|
|
6397
|
+
mapping_confidence: z31.number()
|
|
6398
|
+
});
|
|
6399
|
+
var settingsRecommendationSchema = z31.record(
|
|
6400
|
+
z31.string(),
|
|
6401
|
+
z31.object({
|
|
6402
|
+
current: z31.any(),
|
|
6403
|
+
recommended: z31.any()
|
|
6404
|
+
})
|
|
6405
|
+
);
|
|
6406
|
+
var assessmentResultSchema = z31.object({
|
|
6407
|
+
ready: z31.boolean(),
|
|
6408
|
+
schema_comparison: z31.array(schemaComparisonEntrySchema),
|
|
6409
|
+
settings_recommendation: settingsRecommendationSchema
|
|
6410
|
+
});
|
|
6411
|
+
var playbookExecutionSchema = z31.object({
|
|
6412
|
+
uuid: z31.string(),
|
|
6413
|
+
playbook_slug: z31.string(),
|
|
6414
|
+
application_id: z31.string(),
|
|
6415
|
+
source_index_name: z31.string(),
|
|
6416
|
+
current_step: playbookExecutionStepSchema,
|
|
6417
|
+
step_status: playbookExecutionStepStatusSchema,
|
|
6418
|
+
status_reason: z31.string().nullable(),
|
|
6419
|
+
completed: z31.boolean(),
|
|
6420
|
+
assessment_result: assessmentResultSchema.nullable(),
|
|
6421
|
+
settings_snapshot: z31.json().nullable(),
|
|
6422
|
+
created_at: z31.iso.datetime()
|
|
6423
|
+
});
|
|
6424
|
+
var playbookExecutionCreateResponseSchema = playbookExecutionSchema.extend({
|
|
6425
|
+
resumed: z31.boolean(),
|
|
6426
|
+
sample_data: z31.boolean()
|
|
6427
|
+
});
|
|
6070
6428
|
|
|
6071
6429
|
// src/lib/dashboardApi/api.ts
|
|
6072
6430
|
async function getPlaybooks() {
|
|
@@ -6115,20 +6473,28 @@ import { z as z33 } from "zod";
|
|
|
6115
6473
|
var confirmPlaybookSchema = z33.void();
|
|
6116
6474
|
var CONTINUE_OPTION = "Looks good, continue";
|
|
6117
6475
|
var GO_BACK_OPTION = "Go back";
|
|
6476
|
+
var SELECT_APP_OPTION = "Select app";
|
|
6118
6477
|
async function confirmPlaybookStep(ctx) {
|
|
6119
6478
|
const { slug } = ctx.getStepOutput("select-playbook");
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6479
|
+
for (; ; ) {
|
|
6480
|
+
const selection = await ctx.requestUserInput({
|
|
6481
|
+
prompt: "Ready to continue?",
|
|
6482
|
+
promptType: "multipleChoice",
|
|
6483
|
+
options: [CONTINUE_OPTION, GO_BACK_OPTION, SELECT_APP_OPTION],
|
|
6484
|
+
messages: [`Playbook: ${slug}`, `Application: ${ctx.appId}`],
|
|
6485
|
+
helpText: "Going back re-opens the playbook catalogue. Select app opens the application catalogue \u2014 keep the current app to stay here, or pick another to restart the wizard."
|
|
6486
|
+
});
|
|
6487
|
+
if (typeof selection !== "string") {
|
|
6488
|
+
throw new Error("confirm-playbook received an unexpected result");
|
|
6489
|
+
}
|
|
6490
|
+
if (selection === SELECT_APP_OPTION) {
|
|
6491
|
+
await ctx.changeApplication();
|
|
6492
|
+
continue;
|
|
6493
|
+
}
|
|
6494
|
+
if (selection === GO_BACK_OPTION) {
|
|
6495
|
+
return ctx.returnToStep("select-playbook");
|
|
6496
|
+
}
|
|
6497
|
+
return;
|
|
6132
6498
|
}
|
|
6133
6499
|
}
|
|
6134
6500
|
|
|
@@ -6219,7 +6585,7 @@ function getWorkflow(id) {
|
|
|
6219
6585
|
}
|
|
6220
6586
|
|
|
6221
6587
|
// src/ui/Welcome.tsx
|
|
6222
|
-
import { dirname as dirname6, join as
|
|
6588
|
+
import { dirname as dirname6, join as join12 } from "node:path";
|
|
6223
6589
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6224
6590
|
import { useState as useState10 } from "react";
|
|
6225
6591
|
import { Box as Box11, Spacer, Text as Text12, useInput as useInput6, useWindowSize as useWindowSize5 } from "ink";
|
|
@@ -6251,7 +6617,7 @@ var sidebarItems = [
|
|
|
6251
6617
|
// src/ui/Welcome.tsx
|
|
6252
6618
|
import Image, { TerminalInfoContext, defaultTerminalInfo } from "ink-picture";
|
|
6253
6619
|
import { jsx as jsx10, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
6254
|
-
var IMAGE_PATH =
|
|
6620
|
+
var IMAGE_PATH = join12(dirname6(fileURLToPath2(import.meta.url)), "algolia.png");
|
|
6255
6621
|
var TERMINAL_INFO = {
|
|
6256
6622
|
...defaultTerminalInfo,
|
|
6257
6623
|
supportsUnicode: true,
|
|
@@ -7084,6 +7450,7 @@ function App() {
|
|
|
7084
7450
|
user,
|
|
7085
7451
|
workflow,
|
|
7086
7452
|
settingUpAppId,
|
|
7453
|
+
loadingMessage,
|
|
7087
7454
|
review: review2
|
|
7088
7455
|
} = useWizard();
|
|
7089
7456
|
const { exit } = useApp();
|
|
@@ -7190,6 +7557,11 @@ function App() {
|
|
|
7190
7557
|
" ",
|
|
7191
7558
|
settingUpAppId
|
|
7192
7559
|
] }) }),
|
|
7560
|
+
phase === "running" && !settingUpAppId && loadingMessage && /* @__PURE__ */ jsx19(Box20, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs19(Text21, { color: COLORS.strong, bold: true, children: [
|
|
7561
|
+
/* @__PURE__ */ jsx19(Spinner3, { type: "dots" }),
|
|
7562
|
+
" ",
|
|
7563
|
+
loadingMessage
|
|
7564
|
+
] }) }),
|
|
7193
7565
|
/* @__PURE__ */ jsx19(CliOutput, {}),
|
|
7194
7566
|
showTips && currentStep && /* @__PURE__ */ jsx19(
|
|
7195
7567
|
Tips,
|
|
@@ -7223,126 +7595,6 @@ function App() {
|
|
|
7223
7595
|
);
|
|
7224
7596
|
}
|
|
7225
7597
|
|
|
7226
|
-
// src/lib/envAppId.ts
|
|
7227
|
-
import { readFile as readFile9 } from "node:fs/promises";
|
|
7228
|
-
import { join as join12 } from "node:path";
|
|
7229
|
-
var ENV_FILES = [".env", ".env.local"];
|
|
7230
|
-
var APP_ID_LINE = /^[ \t]*(?:export[ \t]+)?([A-Z0-9_]*ALGOLIA_APP(?:LICATION)?_ID)[ \t]*=[ \t]*(.*)$/gm;
|
|
7231
|
-
async function findEnvApplicationId(root = process.cwd()) {
|
|
7232
|
-
for (const file of ENV_FILES) {
|
|
7233
|
-
let content;
|
|
7234
|
-
try {
|
|
7235
|
-
content = await readFile9(join12(root, file), "utf8");
|
|
7236
|
-
} catch (err) {
|
|
7237
|
-
if (err.code !== "ENOENT") {
|
|
7238
|
-
logger.warn(
|
|
7239
|
-
{ file, err },
|
|
7240
|
-
"could not read env file for an application id"
|
|
7241
|
-
);
|
|
7242
|
-
}
|
|
7243
|
-
continue;
|
|
7244
|
-
}
|
|
7245
|
-
for (const [, name, raw] of content.matchAll(APP_ID_LINE)) {
|
|
7246
|
-
const id = readValue(raw);
|
|
7247
|
-
if (id) {
|
|
7248
|
-
logger.info({ file, name, app: id }, "found an application id in env");
|
|
7249
|
-
return { id, name, file };
|
|
7250
|
-
}
|
|
7251
|
-
}
|
|
7252
|
-
}
|
|
7253
|
-
return null;
|
|
7254
|
-
}
|
|
7255
|
-
function readValue(raw) {
|
|
7256
|
-
const trimmed = raw.trim();
|
|
7257
|
-
const quoted = trimmed.match(/^(['"])(.*)\1/);
|
|
7258
|
-
const value = quoted ? quoted[2].trim() : trimmed.replace(/\s+#.*$/, "").trim();
|
|
7259
|
-
return value.length > 0 && !value.startsWith("<") ? value : null;
|
|
7260
|
-
}
|
|
7261
|
-
|
|
7262
|
-
// src/lib/algoliaAppPicker.ts
|
|
7263
|
-
function blockReasonFor(app) {
|
|
7264
|
-
return app.status !== "active" ? "Paused" : "Missing permissions";
|
|
7265
|
-
}
|
|
7266
|
-
function secondaryFor(app, highlightId) {
|
|
7267
|
-
if (!canSelectApplication(app)) {
|
|
7268
|
-
return { kind: "text", value: blockReasonFor(app) };
|
|
7269
|
-
}
|
|
7270
|
-
if (app.id === highlightId) {
|
|
7271
|
-
return { kind: "badge", value: "[DETECTED]" };
|
|
7272
|
-
}
|
|
7273
|
-
return app.plan ? { kind: "badge", value: app.plan } : void 0;
|
|
7274
|
-
}
|
|
7275
|
-
function labelFor(app) {
|
|
7276
|
-
return app.name.trim() ? `${app.name} \u2014 ${app.id}` : app.id;
|
|
7277
|
-
}
|
|
7278
|
-
async function selectAndReport(id) {
|
|
7279
|
-
const store = useWizard.getState();
|
|
7280
|
-
store.setSettingUpApp(id);
|
|
7281
|
-
try {
|
|
7282
|
-
return await selectApplication(id);
|
|
7283
|
-
} finally {
|
|
7284
|
-
store.setSettingUpApp(null);
|
|
7285
|
-
}
|
|
7286
|
-
}
|
|
7287
|
-
async function promptForApplication(highlight) {
|
|
7288
|
-
const store = useWizard.getState();
|
|
7289
|
-
const unordered = await listApplications();
|
|
7290
|
-
if (unordered.length === 0) {
|
|
7291
|
-
throw new Error(
|
|
7292
|
-
"This Algolia account has no applications. Create one in the Algolia dashboard, or with `npx @algolia/cli@latest application create`, then restart the wizard."
|
|
7293
|
-
);
|
|
7294
|
-
}
|
|
7295
|
-
if (!unordered.some(canSelectApplication)) {
|
|
7296
|
-
throw new Error(
|
|
7297
|
-
"None of the applications on this Algolia account are usable \u2014 each is either paused or missing key-management access. Activate one, or grant it the `keys` ACL, then restart the wizard."
|
|
7298
|
-
);
|
|
7299
|
-
}
|
|
7300
|
-
const apps = [
|
|
7301
|
-
...unordered.filter(canSelectApplication),
|
|
7302
|
-
...unordered.filter((app) => !canSelectApplication(app))
|
|
7303
|
-
];
|
|
7304
|
-
const envApp = highlight ? unordered.find((app) => app.id === highlight.id) : void 0;
|
|
7305
|
-
const highlightIndex = envApp && canSelectApplication(envApp) ? apps.indexOf(envApp) : -1;
|
|
7306
|
-
const messages = [];
|
|
7307
|
-
if (highlight && highlightIndex < 0) {
|
|
7308
|
-
messages.push(
|
|
7309
|
-
envApp ? `Could not use ${highlight.id} from ${highlight.file} \u2014 it\u2019s ${blockReasonFor(envApp).toLowerCase()}. Pick another below.` : `Could not use ${highlight.id} from ${highlight.file} \u2014 it may have been removed, or this account may not have access to it.`
|
|
7310
|
-
);
|
|
7311
|
-
}
|
|
7312
|
-
for (; ; ) {
|
|
7313
|
-
const choice = await store.requestUserInput({
|
|
7314
|
-
prompt: "Which Algolia application should the wizard work in?",
|
|
7315
|
-
promptType: "multipleChoice",
|
|
7316
|
-
options: apps.map(labelFor),
|
|
7317
|
-
secondary: apps.map((app) => secondaryFor(app, highlight?.id)),
|
|
7318
|
-
disabled: apps.map((app) => !canSelectApplication(app)),
|
|
7319
|
-
...highlightIndex >= 0 ? { defaultSelectedIndex: highlightIndex } : {},
|
|
7320
|
-
messages
|
|
7321
|
-
});
|
|
7322
|
-
const chosen = apps.find((app) => labelFor(app) === choice);
|
|
7323
|
-
if (!chosen || !canSelectApplication(chosen)) {
|
|
7324
|
-
throw new Error("Application picker received an unexpected selection");
|
|
7325
|
-
}
|
|
7326
|
-
try {
|
|
7327
|
-
return await selectAndReport(chosen.id);
|
|
7328
|
-
} catch (err) {
|
|
7329
|
-
logger.warn(
|
|
7330
|
-
{ app: chosen.id, err: err.message },
|
|
7331
|
-
"application select failed; re-prompting"
|
|
7332
|
-
);
|
|
7333
|
-
messages.push(
|
|
7334
|
-
`Could not select \u201C${labelFor(chosen)}\u201D. It may have been removed \u2014 pick another.`
|
|
7335
|
-
);
|
|
7336
|
-
}
|
|
7337
|
-
}
|
|
7338
|
-
}
|
|
7339
|
-
async function ensureApplication(resumeAppId) {
|
|
7340
|
-
if (resumeAppId) return selectAndReport(resumeAppId);
|
|
7341
|
-
const env = await findEnvApplicationId();
|
|
7342
|
-
if (!env) return promptForApplication();
|
|
7343
|
-
return promptForApplication({ id: env.id, file: env.file });
|
|
7344
|
-
}
|
|
7345
|
-
|
|
7346
7598
|
// src/lib/seed.ts
|
|
7347
7599
|
var projectScan2 = {
|
|
7348
7600
|
languages: [{ name: "TypeScript", version: "5.7.2" }],
|
|
@@ -7589,7 +7841,7 @@ function delay(ms) {
|
|
|
7589
7841
|
// package.json with { type: 'json' }
|
|
7590
7842
|
var package_default2 = {
|
|
7591
7843
|
name: "@algolia/wizard",
|
|
7592
|
-
version: "0.
|
|
7844
|
+
version: "0.73.0",
|
|
7593
7845
|
description: "Magically implement Algolia functionality in your codebase",
|
|
7594
7846
|
type: "module",
|
|
7595
7847
|
engines: {
|