depfix-ai 0.2.11 → 0.2.12
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/lib/audit/npmAudit.d.ts +2 -3
- package/dist/lib/audit/npmAudit.d.ts.map +1 -1
- package/dist/lib/audit/npmAudit.js +7 -6
- package/dist/lib/env/ai.d.ts +15 -0
- package/dist/lib/env/ai.d.ts.map +1 -0
- package/dist/lib/env/ai.js +78 -0
- package/dist/lib/env/render.d.ts +5 -0
- package/dist/lib/env/render.d.ts.map +1 -1
- package/dist/lib/env/render.js +33 -2
- package/dist/lib/env/write.d.ts +3 -0
- package/dist/lib/env/write.d.ts.map +1 -1
- package/dist/lib/env/write.js +10 -3
- package/dist/ui/interactive.d.ts.map +1 -1
- package/dist/ui/interactive.js +48 -16
- package/package.json +1 -1
|
@@ -4,10 +4,9 @@ export interface NpmAuditResult {
|
|
|
4
4
|
exitCode: number | undefined;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Run an
|
|
7
|
+
* Run an audit in JSON mode.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* is detected we return early without running anything.
|
|
9
|
+
* Supports npm and pnpm. Both output JSON compatible with our summarize logic.
|
|
11
10
|
*/
|
|
12
11
|
export declare function runNpmAuditJson(cwd?: string): Promise<NpmAuditResult>;
|
|
13
12
|
//# sourceMappingURL=npmAudit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"npmAudit.d.ts","sourceRoot":"","sources":["../../../src/lib/audit/npmAudit.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,KAAK,GAAG,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED
|
|
1
|
+
{"version":3,"file":"npmAudit.d.ts","sourceRoot":"","sources":["../../../src/lib/audit/npmAudit.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,KAAK,GAAG,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBlF"}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { detectPackageManager } from "../pm/detect.js";
|
|
2
2
|
import { runPmCommand } from "../pm/run.js";
|
|
3
3
|
/**
|
|
4
|
-
* Run an
|
|
4
|
+
* Run an audit in JSON mode.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* is detected we return early without running anything.
|
|
6
|
+
* Supports npm and pnpm. Both output JSON compatible with our summarize logic.
|
|
8
7
|
*/
|
|
9
8
|
export async function runNpmAuditJson(cwd = process.cwd()) {
|
|
10
9
|
const pm = detectPackageManager(cwd);
|
|
11
|
-
if (pm !== "npm") {
|
|
10
|
+
if (pm !== "npm" && pm !== "pnpm") {
|
|
12
11
|
return { pm, rawJson: undefined, exitCode: undefined };
|
|
13
12
|
}
|
|
14
|
-
const { stdout, exitCode } = await runPmCommand(pm, ["audit", "--json"], {
|
|
13
|
+
const { stdout, stderr, exitCode } = await runPmCommand(pm, ["audit", "--json"], {
|
|
15
14
|
cwd,
|
|
16
15
|
});
|
|
16
|
+
// pnpm may send JSON to stderr when exitCode is non-zero
|
|
17
|
+
const rawJson = stdout?.trim() || stderr?.trim();
|
|
17
18
|
return {
|
|
18
19
|
pm,
|
|
19
|
-
rawJson:
|
|
20
|
+
rawJson: rawJson || undefined,
|
|
20
21
|
exitCode,
|
|
21
22
|
};
|
|
22
23
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface EnvVarEnriched {
|
|
2
|
+
key: string;
|
|
3
|
+
description: string;
|
|
4
|
+
example: string;
|
|
5
|
+
}
|
|
6
|
+
export interface EnvAiOptions {
|
|
7
|
+
provider: "openai" | "google";
|
|
8
|
+
model: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Use AI to generate descriptions and example values for env vars.
|
|
13
|
+
*/
|
|
14
|
+
export declare function enrichEnvVarsWithAi(keys: string[], options: EnvAiOptions): Promise<EnvVarEnriched[]>;
|
|
15
|
+
//# sourceMappingURL=ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../../src/lib/env/ai.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,cAAc,EAAE,CAAC,CAmB3B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use AI to generate descriptions and example values for env vars.
|
|
3
|
+
*/
|
|
4
|
+
export async function enrichEnvVarsWithAi(keys, options) {
|
|
5
|
+
if (keys.length === 0) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
const prompt = `You are helping generate a .env.example file. For each environment variable name below, provide:
|
|
9
|
+
1. A brief description (one line, what it's used for)
|
|
10
|
+
2. A realistic example value (placeholder, not real secrets)
|
|
11
|
+
|
|
12
|
+
Return a JSON array only, no markdown. Format: [{"key":"VAR_NAME","description":"...","example":"..."}]
|
|
13
|
+
Keep descriptions under 80 chars. Examples should be realistic placeholders like "localhost", "5432", "sk-xxx", "https://api.example.com".
|
|
14
|
+
|
|
15
|
+
Variables:
|
|
16
|
+
${keys.join("\n")}`;
|
|
17
|
+
if (options.provider === "openai") {
|
|
18
|
+
return callOpenAI(prompt, options.model, options.apiKey);
|
|
19
|
+
}
|
|
20
|
+
return callGoogle(prompt, options.model, options.apiKey);
|
|
21
|
+
}
|
|
22
|
+
async function callOpenAI(prompt, model, apiKey) {
|
|
23
|
+
const res = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
Authorization: `Bearer ${apiKey}`,
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
model,
|
|
31
|
+
messages: [{ role: "user", content: prompt }],
|
|
32
|
+
temperature: 0.3,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const err = await res.text();
|
|
37
|
+
throw new Error(`OpenAI API error ${res.status}: ${err}`);
|
|
38
|
+
}
|
|
39
|
+
const data = (await res.json());
|
|
40
|
+
const content = data.choices?.[0]?.message?.content;
|
|
41
|
+
if (!content)
|
|
42
|
+
throw new Error("No response from OpenAI");
|
|
43
|
+
return parseJsonResponse(content);
|
|
44
|
+
}
|
|
45
|
+
async function callGoogle(prompt, model, apiKey) {
|
|
46
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: { "Content-Type": "application/json" },
|
|
50
|
+
body: JSON.stringify({
|
|
51
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
52
|
+
generationConfig: { temperature: 0.3 },
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const err = await res.text();
|
|
57
|
+
throw new Error(`Google AI API error ${res.status}: ${err}`);
|
|
58
|
+
}
|
|
59
|
+
const data = (await res.json());
|
|
60
|
+
const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
61
|
+
if (!text)
|
|
62
|
+
throw new Error("No response from Google AI");
|
|
63
|
+
return parseJsonResponse(text);
|
|
64
|
+
}
|
|
65
|
+
function parseJsonResponse(content) {
|
|
66
|
+
const cleaned = content.replace(/```json\n?|\n?```/g, "").trim();
|
|
67
|
+
const parsed = JSON.parse(cleaned);
|
|
68
|
+
if (!Array.isArray(parsed))
|
|
69
|
+
throw new Error("AI did not return an array");
|
|
70
|
+
return parsed.map((item) => {
|
|
71
|
+
const obj = item;
|
|
72
|
+
return {
|
|
73
|
+
key: String(obj.key ?? ""),
|
|
74
|
+
description: String(obj.description ?? ""),
|
|
75
|
+
example: String(obj.example ?? ""),
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
package/dist/lib/env/render.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { EnvScanResult } from "./scan.js";
|
|
2
|
+
import type { EnvVarEnriched } from "./ai.js";
|
|
2
3
|
export declare function renderEnv(result: EnvScanResult): string;
|
|
4
|
+
/**
|
|
5
|
+
* Render .env.example with AI-generated descriptions and example values.
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderEnvAi(enriched: EnvVarEnriched[]): string;
|
|
3
8
|
//# sourceMappingURL=render.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/lib/env/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/lib/env/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAgB9C,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA6BvD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CA+B9D"}
|
package/dist/lib/env/render.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const GROUPS_PREFIX = [
|
|
2
2
|
{ prefix: "DB_", heading: "Database" },
|
|
3
3
|
{ prefix: "REDIS_", heading: "Redis" },
|
|
4
4
|
{ prefix: "AWS_", heading: "AWS" },
|
|
@@ -9,7 +9,7 @@ const GROUPS = [
|
|
|
9
9
|
export function renderEnv(result) {
|
|
10
10
|
const remaining = new Set(result.keys);
|
|
11
11
|
const groups = [];
|
|
12
|
-
for (const { prefix, heading } of
|
|
12
|
+
for (const { prefix, heading } of GROUPS_PREFIX) {
|
|
13
13
|
const keys = result.keys.filter((k) => k.startsWith(prefix));
|
|
14
14
|
if (keys.length === 0)
|
|
15
15
|
continue;
|
|
@@ -32,3 +32,34 @@ export function renderEnv(result) {
|
|
|
32
32
|
}
|
|
33
33
|
return lines.join("\n").trimEnd() + (lines.length ? "\n" : "");
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Render .env.example with AI-generated descriptions and example values.
|
|
37
|
+
*/
|
|
38
|
+
export function renderEnvAi(enriched) {
|
|
39
|
+
if (enriched.length === 0)
|
|
40
|
+
return "";
|
|
41
|
+
const byPrefix = new Map();
|
|
42
|
+
for (const { prefix, heading } of GROUPS_PREFIX) {
|
|
43
|
+
const keys = enriched.filter((e) => e.key.startsWith(prefix));
|
|
44
|
+
if (keys.length > 0) {
|
|
45
|
+
byPrefix.set(heading, keys);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const other = enriched.filter((e) => !GROUPS_PREFIX.some(({ prefix }) => e.key.startsWith(prefix)));
|
|
49
|
+
if (other.length > 0) {
|
|
50
|
+
byPrefix.set("Other", other.sort((a, b) => a.key.localeCompare(b.key)));
|
|
51
|
+
}
|
|
52
|
+
const headings = [...GROUPS_PREFIX.map((g) => g.heading), "Other"].filter((h) => byPrefix.has(h));
|
|
53
|
+
const lines = [];
|
|
54
|
+
for (const heading of headings) {
|
|
55
|
+
const items = byPrefix.get(heading);
|
|
56
|
+
lines.push(`# ${heading}`);
|
|
57
|
+
for (const { key, description, example } of items) {
|
|
58
|
+
if (description)
|
|
59
|
+
lines.push(`# ${description}`);
|
|
60
|
+
lines.push(`${key}=${example || ""}`);
|
|
61
|
+
lines.push("");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
65
|
+
}
|
package/dist/lib/env/write.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { type EnvAiOptions } from "./ai.js";
|
|
1
2
|
export interface EnvGenerateFlags {
|
|
2
3
|
out: string;
|
|
3
4
|
create: boolean;
|
|
4
5
|
force: boolean;
|
|
5
6
|
check: boolean;
|
|
7
|
+
/** AI options: adds descriptions and example values */
|
|
8
|
+
ai?: EnvAiOptions;
|
|
6
9
|
}
|
|
7
10
|
export declare function runEnvGenerate(opts?: Partial<EnvGenerateFlags>): Promise<void>;
|
|
8
11
|
//# sourceMappingURL=write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/lib/env/write.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/lib/env/write.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAGjE,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,uDAAuD;IACvD,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB;AAgCD,wBAAsB,cAAc,CAAC,IAAI,GAAE,OAAO,CAAC,gBAAgB,CAAM,iBAmDxE"}
|
package/dist/lib/env/write.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { scanEnv } from "./scan.js";
|
|
4
|
-
import { renderEnv } from "./render.js";
|
|
4
|
+
import { renderEnv, renderEnvAi } from "./render.js";
|
|
5
|
+
import { enrichEnvVarsWithAi } from "./ai.js";
|
|
5
6
|
import { logInfo, logError, logSuccess } from "../ui/log.js";
|
|
6
7
|
const defaultEnvFlags = {
|
|
7
8
|
out: ".env.example",
|
|
@@ -57,8 +58,14 @@ export async function runEnvGenerate(opts = {}) {
|
|
|
57
58
|
logSuccess(`${flags.out} contains all required environment variables.`);
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
let exampleContent;
|
|
62
|
+
if (flags.ai && scanResult.keys.length > 0) {
|
|
63
|
+
const enriched = await enrichEnvVarsWithAi(scanResult.keys, flags.ai);
|
|
64
|
+
exampleContent = renderEnvAi(enriched);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
exampleContent = renderEnv(scanResult);
|
|
68
|
+
}
|
|
62
69
|
await fs.writeFile(outPath, exampleContent, "utf8");
|
|
63
70
|
logSuccess(`Wrote environment template to ${outPath}`);
|
|
64
71
|
if (flags.create) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/ui/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/ui/interactive.ts"],"names":[],"mappings":"AA6NA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA6DpD"}
|
package/dist/ui/interactive.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
-
import { intro, select, confirm, password, isCancel, cancel } from "@clack/prompts";
|
|
5
|
+
import { intro, select, confirm, password, isCancel, cancel, spinner } from "@clack/prompts";
|
|
6
6
|
import pc from "picocolors";
|
|
7
7
|
import { execute } from "@oclif/core";
|
|
8
8
|
import { config as dotenvConfig } from "dotenv";
|
|
@@ -99,14 +99,15 @@ async function runEnvGenerateFlow() {
|
|
|
99
99
|
const mode = await select({
|
|
100
100
|
message: "📄 How would you like to generate .env.example?",
|
|
101
101
|
options: [
|
|
102
|
-
{ value: "default", label: "Default (fast)" },
|
|
103
|
-
{ value: "ai", label: "AI-assisted" },
|
|
102
|
+
{ value: "default", label: "Default (fast) – variable names only" },
|
|
103
|
+
{ value: "ai", label: "AI-assisted – descriptions + example values (requires API key)" },
|
|
104
104
|
],
|
|
105
105
|
});
|
|
106
106
|
if (isCancel(mode)) {
|
|
107
107
|
cancel("Cancelled.");
|
|
108
108
|
process.exit(0);
|
|
109
109
|
}
|
|
110
|
+
let aiOptions;
|
|
110
111
|
if (mode === "ai") {
|
|
111
112
|
const provider = await select({
|
|
112
113
|
message: "🤖 Select AI provider",
|
|
@@ -119,8 +120,11 @@ async function runEnvGenerateFlow() {
|
|
|
119
120
|
cancel("Cancelled.");
|
|
120
121
|
process.exit(0);
|
|
121
122
|
}
|
|
123
|
+
dotenvConfig({ path: join(getProjectCwd(), ".env") });
|
|
124
|
+
let model;
|
|
125
|
+
let apiKey;
|
|
122
126
|
if (provider === "openai") {
|
|
123
|
-
await select({
|
|
127
|
+
const modelChoice = await select({
|
|
124
128
|
message: "🤖 Select model",
|
|
125
129
|
options: [
|
|
126
130
|
{ value: "gpt-4o", label: "GPT-4o" },
|
|
@@ -128,20 +132,27 @@ async function runEnvGenerateFlow() {
|
|
|
128
132
|
{ value: "gpt-3.5-turbo", label: "GPT-3.5 Turbo" },
|
|
129
133
|
],
|
|
130
134
|
});
|
|
131
|
-
|
|
135
|
+
if (isCancel(modelChoice)) {
|
|
136
|
+
cancel("Cancelled.");
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
model = modelChoice;
|
|
140
|
+
const keyInput = await password({
|
|
132
141
|
message: "🔑 OpenAI API key (or leave blank to use OPENAI_API_KEY from env)",
|
|
133
142
|
validate: () => undefined,
|
|
134
143
|
});
|
|
135
|
-
if (isCancel(
|
|
144
|
+
if (isCancel(keyInput)) {
|
|
136
145
|
cancel("Cancelled.");
|
|
137
146
|
process.exit(0);
|
|
138
147
|
}
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
apiKey = (keyInput && typeof keyInput === "string" ? keyInput : process.env.OPENAI_API_KEY) ?? "";
|
|
149
|
+
if (!apiKey) {
|
|
150
|
+
logError("OpenAI API key is required for AI-assisted env generation.");
|
|
151
|
+
return;
|
|
141
152
|
}
|
|
142
153
|
}
|
|
143
|
-
else
|
|
144
|
-
await select({
|
|
154
|
+
else {
|
|
155
|
+
const modelChoice = await select({
|
|
145
156
|
message: "🤖 Select model",
|
|
146
157
|
options: [
|
|
147
158
|
{ value: "gemini-1.5-pro", label: "Gemini 1.5 Pro" },
|
|
@@ -149,21 +160,42 @@ async function runEnvGenerateFlow() {
|
|
|
149
160
|
{ value: "gemini-1.0-pro", label: "Gemini 1.0 Pro" },
|
|
150
161
|
],
|
|
151
162
|
});
|
|
152
|
-
|
|
163
|
+
if (isCancel(modelChoice)) {
|
|
164
|
+
cancel("Cancelled.");
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
model = modelChoice;
|
|
168
|
+
const keyInput = await password({
|
|
153
169
|
message: "🔑 Google AI API key (or leave blank to use GOOGLE_API_KEY from env)",
|
|
154
170
|
validate: () => undefined,
|
|
155
171
|
});
|
|
156
|
-
if (isCancel(
|
|
172
|
+
if (isCancel(keyInput)) {
|
|
157
173
|
cancel("Cancelled.");
|
|
158
174
|
process.exit(0);
|
|
159
175
|
}
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
apiKey = (keyInput && typeof keyInput === "string" ? keyInput : process.env.GOOGLE_API_KEY) ?? "";
|
|
177
|
+
if (!apiKey) {
|
|
178
|
+
logError("Google API key is required for AI-assisted env generation.");
|
|
179
|
+
return;
|
|
162
180
|
}
|
|
163
181
|
}
|
|
164
|
-
|
|
182
|
+
aiOptions = { provider: provider, model, apiKey };
|
|
183
|
+
}
|
|
184
|
+
if (aiOptions) {
|
|
185
|
+
const s = spinner();
|
|
186
|
+
s.start("Generating .env.example with AI (descriptions + examples)...");
|
|
187
|
+
try {
|
|
188
|
+
await runEnvGenerate({ out: ".env.example", ai: aiOptions });
|
|
189
|
+
s.stop("Done.");
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
s.stop("Failed.");
|
|
193
|
+
logError(String(err));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
await runEnvGenerate({ out: ".env.example" });
|
|
165
198
|
}
|
|
166
|
-
await runEnvGenerate({ out: ".env.example" });
|
|
167
199
|
}
|
|
168
200
|
export async function runInteractive() {
|
|
169
201
|
const cwd = getProjectCwd();
|