asyq 8.2.9 → 8.2.10
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/asyq.js +1 -2
- package/dist/asyq.js.map +1 -1
- package/package.json +1 -1
package/dist/asyq.js
CHANGED
|
@@ -435,8 +435,7 @@ program.command("init").description("Scan project and generate .env.example").op
|
|
|
435
435
|
"--workspaces <list>",
|
|
436
436
|
"In monorepo mode: comma-separated workspace list to generate for"
|
|
437
437
|
).option("--no-root", "In monorepo mode: skip generating for repo root").action(async (opts) => {
|
|
438
|
-
p.intro(pc.cyan(`
|
|
439
|
-
Asyq CLI v${getPackageVersion()} Created by @thev1ndu`));
|
|
438
|
+
p.intro(pc.cyan(`Asyq CLI v${getPackageVersion()} Created by @thev1ndu`));
|
|
440
439
|
const rootAbs = path2.resolve(process.cwd(), opts.root);
|
|
441
440
|
const outName = String(opts.out || ".env.example");
|
|
442
441
|
const mode = await pickMode();
|
package/dist/asyq.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/asyq.ts","../src/scan.ts","../src/ai.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\nconst MODELS = [\n \"gpt-5\",\n \"gpt-5-mini\",\n \"gpt-5-nano\",\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n] as const;\n\ntype ModelName = (typeof MODELS)[number];\n\nfunction getPackageVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const pkgPath = path.resolve(__dirname, \"../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction fail(message: string): never {\n p.outro(pc.red(message));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n const mode = await p.select({\n message: \"How would you like to generate .env.example?\",\n options: [\n { label: \"Default (fast)\", value: \"default\" },\n { label: \"AI-assisted (adds descriptions)\", value: \"ai\" },\n ],\n });\n\n if (p.isCancel(mode)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return mode as \"default\" | \"ai\";\n}\n\nasync function pickModel(): Promise<ModelName> {\n const model = await p.select({\n message: \"Select an AI model\",\n initialValue: \"gpt-4.1-mini\",\n options: MODELS.map((m) => ({ label: m, value: m })),\n });\n\n if (p.isCancel(model)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return model as ModelName;\n}\n\nasync function getApiKey(): Promise<string> {\n const envKey = process.env.OPENAI_API_KEY?.trim();\n if (envKey) return envKey;\n\n const key = await p.password({\n message: \"Enter OpenAI API key (not saved)\",\n validate: (v) =>\n v.trim().length > 0 ? undefined : \"API key cannot be empty\",\n });\n\n if (p.isCancel(key)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return key.trim();\n}\n\n/* ----------------------------- Monorepo support ---------------------------- */\n\nfunction readWorkspaceGlobs(rootAbs: string): string[] {\n const globs: string[] = [];\n\n // pnpm-workspace.yaml\n const pnpmWs = path.join(rootAbs, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmWs)) {\n const txt = fs.readFileSync(pnpmWs, \"utf8\");\n for (const line of txt.split(/\\r?\\n/)) {\n const m = line.match(/^\\s*-\\s*[\"']?([^\"']+)[\"']?\\s*$/);\n if (m) globs.push(m[1].trim());\n }\n }\n\n // package.json workspaces\n const pkgPath = path.join(rootAbs, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const ws = pkg?.workspaces;\n if (Array.isArray(ws)) globs.push(...ws);\n if (ws && Array.isArray(ws.packages)) globs.push(...ws.packages);\n } catch {\n // ignore\n }\n }\n\n return [...new Set(globs)].filter(Boolean);\n}\n\nfunction expandSimpleGlob(rootAbs: string, pattern: string): string[] {\n const norm = pattern.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n\n if (!norm.includes(\"*\")) {\n const abs = path.join(rootAbs, norm);\n return fs.existsSync(abs) && fs.statSync(abs).isDirectory() ? [norm] : [];\n }\n\n const m = norm.match(/^([^*]+)\\/\\*$/);\n if (!m) return [];\n\n const baseRel = m[1].replace(/\\/+$/, \"\");\n const baseAbs = path.join(rootAbs, baseRel);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory()) return [];\n\n const out: string[] = [];\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${baseRel}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) out.push(rel);\n }\n return out;\n}\n\nfunction detectWorkspaces(rootAbs: string): string[] {\n const globs = readWorkspaceGlobs(rootAbs);\n const found = new Set<string>();\n\n for (const g of globs) {\n for (const rel of expandSimpleGlob(rootAbs, g)) found.add(rel);\n }\n\n // Turbo-style fallback if no globs detected\n if (found.size === 0) {\n for (const base of [\"apps\", \"packages\"]) {\n const baseAbs = path.join(rootAbs, base);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory())\n continue;\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${base}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) found.add(rel);\n }\n }\n }\n\n return [...found].sort((a, b) => a.localeCompare(b));\n}\n\nasync function pickWorkspaces(workspaces: string[]): Promise<string[]> {\n const picked = await p.multiselect({\n message: \"Select workspaces to generate .env.example for\",\n options: workspaces.map((w) => ({ label: w, value: w })),\n required: false,\n });\n\n if (p.isCancel(picked)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return (picked as string[]) ?? [];\n}\n\n/* -------------------------------------------------------------------------- */\n\nconst program = new Command();\n\nprogram\n .name(\"asyq\")\n .description(\"Generate .env.example by scanning your project for env usage\")\n .version(`v${getPackageVersion()}`);\n\nprogram\n .command(\"init\")\n .description(\"Scan project and generate .env.example\")\n .option(\"--root <dir>\", \"Project root to scan\", \".\")\n .option(\"--out <file>\", \"Output file name\", \".env.example\")\n .option(\"--force\", \"Overwrite output if it exists\")\n .option(\n \"--include-lowercase\",\n \"Include lowercase/mixed-case keys (not recommended)\"\n )\n .option(\"--debug\", \"Print scan diagnostics\")\n .option(\"--monorepo\", \"Generate .env.example for root + each workspace\")\n .option(\n \"--select\",\n \"In monorepo mode: interactively choose which workspaces to generate for\"\n )\n .option(\n \"--workspaces <list>\",\n \"In monorepo mode: comma-separated workspace list to generate for\"\n )\n .option(\"--no-root\", \"In monorepo mode: skip generating for repo root\")\n .action(async (opts) => {\n p.intro(pc.cyan(`\\nAsyq CLI v${getPackageVersion()} Created by @thev1ndu`));\n\n const rootAbs = path.resolve(process.cwd(), opts.root);\n const outName = String(opts.out || \".env.example\");\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const targets: { label: string; dirAbs: string }[] = [];\n\n // Root target (default on, unless --no-root)\n if (opts.root !== false) {\n targets.push({ label: \"root\", dirAbs: rootAbs });\n }\n\n // Monorepo targets\n if (opts.monorepo) {\n const workspaces = detectWorkspaces(rootAbs);\n\n if (workspaces.length === 0) {\n p.note(\n \"No workspaces detected (pnpm-workspace.yaml / package.json workspaces / apps/* / packages/*).\",\n \"Monorepo\"\n );\n } else {\n let selected = workspaces;\n\n if (opts.workspaces) {\n const allow = new Set(\n String(opts.workspaces)\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n );\n selected = workspaces.filter((w) => allow.has(w));\n const missing = [...allow].filter((x) => !workspaces.includes(x));\n if (missing.length) {\n p.note(missing.join(\"\\n\"), \"Unknown workspaces ignored\");\n }\n } else if (opts.select) {\n selected = await pickWorkspaces(workspaces);\n }\n\n for (const rel of selected) {\n targets.push({ label: rel, dirAbs: path.join(rootAbs, rel) });\n }\n }\n }\n\n if (targets.length === 0) {\n fail(\n \"No targets selected. Tip: remove --no-root or select at least one workspace.\"\n );\n }\n\n // API key once for all targets (AI mode)\n let apiKey = \"\";\n if (mode === \"ai\" && model) {\n apiKey = await getApiKey();\n if (!apiKey) fail(\"OpenAI API key is required for AI-assisted mode.\");\n }\n\n const results: Array<{\n target: string;\n outRel: string;\n keys: number;\n files: number;\n wrote: boolean;\n }> = [];\n\n for (const t of targets) {\n const outFileAbs = path.join(t.dirAbs, outName);\n const outRelFromRoot =\n path.relative(rootAbs, outFileAbs).replace(/\\\\/g, \"/\") || outName;\n\n if (fs.existsSync(outFileAbs) && !opts.force) {\n fail(\n `Output already exists: ${outRelFromRoot}. Use --force to overwrite.`\n );\n }\n\n const s = p.spinner();\n s.start(`Scanning ${t.label} for environment variables`);\n\n const res = scanProjectForEnvKeys({\n rootDir: t.dirAbs,\n includeLowercase: !!opts.includeLowercase,\n });\n\n s.stop(\n `Scan complete: ${t.label} (${res.filesScanned} files, ${res.keys.size} keys)`\n );\n\n if (opts.debug) {\n p.note(\n [\n `dir: ${t.dirAbs}`,\n `files scanned: ${res.filesScanned}`,\n `keys found: ${res.keys.size}`,\n ].join(\"\\n\"),\n `${t.label} diagnostics`\n );\n }\n\n // If no keys, skip writing (current behavior), but show a clear note.\n if (res.keys.size === 0) {\n p.note(\n `No env vars detected in ${t.label}. Skipping ${outRelFromRoot}`,\n \"Nothing to write\"\n );\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: 0,\n files: res.filesScanned,\n wrote: false,\n });\n continue;\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const aiSpinner = p.spinner();\n aiSpinner.start(`Writing .env.example documentation for ${t.label}`);\n\n try {\n const docs = await generateEnvDocsWithOpenAI({\n apiKey,\n model,\n projectHint:\n \"Write practical guidance for developers setting env vars.\",\n contexts: res.contexts,\n keys,\n });\n\n aiSpinner.stop(\n `Documented ${keys.length} env variables for ${t.label}`\n );\n\n const byKey = new Map(docs.map((d) => [d.key, d]));\n\n content =\n keys\n .map((k) => {\n const d = byKey.get(k);\n if (!d) return `${k}=\\n`;\n\n const secretNote = d.is_secret\n ? \"Secret value. Do not commit.\"\n : \"Non-secret value (verify before committing).\";\n\n return [\n `# ${d.key}`,\n `# ${d.description}`,\n `# Where to get it: ${d.where_to_get}`,\n `# ${secretNote}`,\n `${d.key}=${d.example_value || \"\"}`,\n \"\",\n ].join(\"\\n\");\n })\n .join(\"\\n\")\n .trimEnd() + \"\\n\";\n } catch (e: any) {\n aiSpinner.stop(`Failed to write documentation for ${t.label}`);\n fail(e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFileAbs, content, \"utf8\");\n\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: keys.length,\n files: res.filesScanned,\n wrote: true,\n });\n }\n\n // Summary table\n const summary = results\n .map((r) => {\n const status = r.wrote ? pc.green(\"wrote\") : pc.yellow(\"skipped\");\n return `${pc.cyan(r.target.padEnd(20))} ${pc.green(\n String(r.keys).padStart(3)\n )} keys → ${pc.dim(r.outRel)} ${pc.dim(`(${status})`)}`;\n })\n .join(\"\\n\");\n\n p.note(summary, \"Generated\");\n p.outro(pc.green(\"✓ All done!\"));\n });\n\nprogram.parse(process.argv);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport type ScanOptions = {\n rootDir: string;\n includeLowercase?: boolean;\n maxContextPerKey?: number;\n};\n\nexport type KeyContext = { file: string; line: number; snippet: string };\n\nexport type ScanResult = {\n keys: Set<string>;\n filesScanned: number;\n contexts: Record<string, KeyContext[]>;\n};\n\nconst IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \".turbo\",\n \".cache\",\n \".vercel\",\n \".netlify\",\n]);\n\n// ENV VARS MUST BE EXPLICIT + UPPERCASE\nconst ENV_KEY_RE_STRICT = /^[A-Z][A-Z0-9_]*$/;\nconst ENV_KEY_RE_LOOSE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport function scanProjectForEnvKeys(opts: ScanOptions): ScanResult {\n const root = opts.rootDir;\n const maxCtx = opts.maxContextPerKey ?? 2;\n\n const keyOk = (k: string) =>\n (opts.includeLowercase ? ENV_KEY_RE_LOOSE : ENV_KEY_RE_STRICT).test(k);\n\n const exts = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".toml\",\n ]);\n\n const keys = new Set<string>();\n const contexts: Record<string, KeyContext[]> = {};\n let filesScanned = 0;\n const seenInCode = new Set<string>();\n\n walk(root);\n\n // Only keep keys that were found in actual code, not just .env files\n // Normalize to uppercase for comparison\n const finalKeys = new Set<string>();\n const seenInCodeUpper = new Set([...seenInCode].map((k) => k.toUpperCase()));\n\n for (const key of keys) {\n if (seenInCodeUpper.has(key.toUpperCase())) {\n finalKeys.add(key);\n }\n }\n\n // Add any code-only keys that weren't in .env files\n for (const codeKey of seenInCode) {\n const upper = codeKey.toUpperCase();\n let found = false;\n for (const key of finalKeys) {\n if (key.toUpperCase() === upper) {\n found = true;\n break;\n }\n }\n if (!found && keyOk(codeKey)) {\n finalKeys.add(codeKey);\n }\n }\n\n return { keys: finalKeys, filesScanned, contexts };\n\n function addCtx(key: string, relFile: string, line: number, snippet: string) {\n if (!contexts[key]) contexts[key] = [];\n if (contexts[key].length >= maxCtx) return;\n contexts[key].push({\n file: relFile,\n line,\n snippet: snippet.trim().slice(0, 220),\n });\n }\n\n function walk(dir: string) {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!IGNORE_DIRS.has(entry.name)) walk(full);\n continue;\n }\n\n const isEnvFile = entry.name === \".env\" || entry.name.startsWith(\".env.\");\n const ext = path.extname(entry.name);\n\n if (!isEnvFile && !exts.has(ext)) continue;\n\n const content = safeRead(full);\n if (!content) continue;\n\n filesScanned++;\n const rel = path.relative(root, full).replace(/\\\\/g, \"/\");\n\n if (isEnvFile) {\n extractFromEnvFile(content, rel, keys, addCtx, keyOk);\n } else {\n extractFromCode(content, rel, keys, seenInCode, addCtx, keyOk);\n }\n }\n }\n}\n\nfunction extractFromEnvFile(\n text: string,\n relFile: string,\n keys: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments and empty lines\n if (!ln.trim() || ln.trim().startsWith(\"#\")) continue;\n\n // Match variable assignments\n const m = ln.match(/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (!m) continue;\n\n const k = m[1];\n if (!keyOk(k)) continue;\n\n keys.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n}\n\nfunction extractFromCode(\n text: string,\n relFile: string,\n keys: Set<string>,\n seenInCode: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n\n // JavaScript/TypeScript environment variable patterns\n const patterns: RegExp[] = [\n // process.env.KEY or process?.env?.KEY\n /\\bprocess(?:\\?\\.|\\.)env(?:\\?\\.|\\.)([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // process.env[\"KEY\"] or process?.env?.[\"KEY\"]\n /\\bprocess(?:\\?\\.|\\.)env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/gi,\n // import.meta.env.KEY (Vite, etc.)\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // Deno.env.get(\"KEY\")\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/gi,\n // Bun.env.KEY\n /\\bBun\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n ];\n\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments\n if (ln.trim().startsWith(\"//\") || ln.trim().startsWith(\"#\")) continue;\n\n for (const re of patterns) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(ln))) {\n const k = match[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n seenInCode.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n }\n }\n}\n\nfunction safeRead(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n","export type AIEnvDoc = {\n key: string;\n description: string;\n where_to_get: string;\n example_value: string;\n is_secret: boolean;\n};\n\nexport type AIGenerateOptions = {\n apiKey: string;\n model: string;\n projectHint?: string;\n contexts: Record<string, { file: string; line: number; snippet: string }[]>;\n keys: string[];\n};\n\nconst JSON_SCHEMA = {\n name: \"env_docs\",\n strict: true,\n schema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: { type: \"string\" },\n description: { type: \"string\" },\n where_to_get: { type: \"string\" },\n example_value: { type: \"string\" },\n is_secret: { type: \"boolean\" },\n },\n required: [\n \"key\",\n \"description\",\n \"where_to_get\",\n \"example_value\",\n \"is_secret\",\n ],\n },\n },\n },\n required: [\"items\"],\n },\n} as const;\n\nfunction buildInput(opts: AIGenerateOptions) {\n const lines = opts.keys.map((k) => {\n const ctx = opts.contexts[k]?.[0];\n const seenAt = ctx ? `${ctx.file}:${ctx.line}` : \"unknown\";\n const snippet = ctx ? ctx.snippet : \"\";\n return `- ${k}\\n seen_at: ${seenAt}\\n snippet: ${snippet}`;\n });\n\n const system = [\n \"You generate documentation for environment variables.\",\n \"Return ONLY JSON that matches the provided JSON Schema.\",\n \"Do not include markdown or extra text.\",\n \"Never output real secrets. Use safe placeholders.\",\n \"Keep descriptions short and practical.\",\n \"where_to_get must be actionable (dashboard, secret manager, CI, local service, etc.).\",\n ].join(\" \");\n\n const user = [\n opts.projectHint ? `Project hint: ${opts.projectHint}` : \"\",\n \"Variables:\",\n ...lines,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: user },\n ];\n}\n\nfunction extractTextFromResponses(data: any): string {\n if (typeof data?.output_text === \"string\" && data.output_text.trim())\n return data.output_text;\n\n const out = data?.output;\n if (Array.isArray(out)) {\n for (const item of out) {\n const content = item?.content;\n if (!Array.isArray(content)) continue;\n for (const c of content) {\n if (typeof c?.text === \"string\" && c.text.trim()) return c.text;\n }\n }\n }\n return \"\";\n}\n\nfunction tryParseJsonLoose(raw: string): any | null {\n // First try direct parse\n try {\n return JSON.parse(raw);\n } catch {\n // Try to extract the first {...} block\n const m = raw.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]);\n } catch {\n return null;\n }\n }\n}\n\nexport async function generateEnvDocsWithOpenAI(\n opts: AIGenerateOptions\n): Promise<AIEnvDoc[]> {\n const input = buildInput(opts);\n\n const res = await fetch(\"https://api.openai.com/v1/responses\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: opts.model,\n input,\n text: {\n format: {\n type: \"json_schema\",\n ...JSON_SCHEMA,\n },\n },\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`OpenAI request failed (${res.status}): ${text}`);\n }\n\n const data: any = await res.json();\n const raw = extractTextFromResponses(data).trim();\n\n const parsed = tryParseJsonLoose(raw);\n if (!parsed) {\n throw new Error(\n \"AI output was not valid JSON. Try again, or use a different model.\"\n );\n }\n\n const items = Array.isArray(parsed?.items) ? parsed.items : [];\n return items\n .map((x: any) => ({\n key: String(x.key ?? \"\"),\n description: String(x.description ?? \"\"),\n where_to_get: String(x.where_to_get ?? \"\"),\n example_value: String(x.example_value ?? \"\"),\n is_secret: Boolean(x.is_secret),\n }))\n .filter((x: AIEnvDoc) => x.key.length > 0);\n}\n"],"mappings":"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","names":["fs","path","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/asyq.ts","../src/scan.ts","../src/ai.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\nimport { scanProjectForEnvKeys } from \"./scan.js\";\nimport { generateEnvDocsWithOpenAI } from \"./ai.js\";\n\nconst MODELS = [\n \"gpt-5\",\n \"gpt-5-mini\",\n \"gpt-5-nano\",\n \"gpt-4.1\",\n \"gpt-4.1-mini\",\n \"gpt-4.1-nano\",\n] as const;\n\ntype ModelName = (typeof MODELS)[number];\n\nfunction getPackageVersion(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const pkgPath = path.resolve(__dirname, \"../package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction fail(message: string): never {\n p.outro(pc.red(message));\n process.exit(1);\n}\n\nasync function pickMode(): Promise<\"default\" | \"ai\"> {\n const mode = await p.select({\n message: \"How would you like to generate .env.example?\",\n options: [\n { label: \"Default (fast)\", value: \"default\" },\n { label: \"AI-assisted (adds descriptions)\", value: \"ai\" },\n ],\n });\n\n if (p.isCancel(mode)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return mode as \"default\" | \"ai\";\n}\n\nasync function pickModel(): Promise<ModelName> {\n const model = await p.select({\n message: \"Select an AI model\",\n initialValue: \"gpt-4.1-mini\",\n options: MODELS.map((m) => ({ label: m, value: m })),\n });\n\n if (p.isCancel(model)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return model as ModelName;\n}\n\nasync function getApiKey(): Promise<string> {\n const envKey = process.env.OPENAI_API_KEY?.trim();\n if (envKey) return envKey;\n\n const key = await p.password({\n message: \"Enter OpenAI API key (not saved)\",\n validate: (v) =>\n v.trim().length > 0 ? undefined : \"API key cannot be empty\",\n });\n\n if (p.isCancel(key)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return key.trim();\n}\n\n/* ----------------------------- Monorepo support ---------------------------- */\n\nfunction readWorkspaceGlobs(rootAbs: string): string[] {\n const globs: string[] = [];\n\n // pnpm-workspace.yaml\n const pnpmWs = path.join(rootAbs, \"pnpm-workspace.yaml\");\n if (fs.existsSync(pnpmWs)) {\n const txt = fs.readFileSync(pnpmWs, \"utf8\");\n for (const line of txt.split(/\\r?\\n/)) {\n const m = line.match(/^\\s*-\\s*[\"']?([^\"']+)[\"']?\\s*$/);\n if (m) globs.push(m[1].trim());\n }\n }\n\n // package.json workspaces\n const pkgPath = path.join(rootAbs, \"package.json\");\n if (fs.existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n const ws = pkg?.workspaces;\n if (Array.isArray(ws)) globs.push(...ws);\n if (ws && Array.isArray(ws.packages)) globs.push(...ws.packages);\n } catch {\n // ignore\n }\n }\n\n return [...new Set(globs)].filter(Boolean);\n}\n\nfunction expandSimpleGlob(rootAbs: string, pattern: string): string[] {\n const norm = pattern.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n\n if (!norm.includes(\"*\")) {\n const abs = path.join(rootAbs, norm);\n return fs.existsSync(abs) && fs.statSync(abs).isDirectory() ? [norm] : [];\n }\n\n const m = norm.match(/^([^*]+)\\/\\*$/);\n if (!m) return [];\n\n const baseRel = m[1].replace(/\\/+$/, \"\");\n const baseAbs = path.join(rootAbs, baseRel);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory()) return [];\n\n const out: string[] = [];\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${baseRel}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) out.push(rel);\n }\n return out;\n}\n\nfunction detectWorkspaces(rootAbs: string): string[] {\n const globs = readWorkspaceGlobs(rootAbs);\n const found = new Set<string>();\n\n for (const g of globs) {\n for (const rel of expandSimpleGlob(rootAbs, g)) found.add(rel);\n }\n\n // Turbo-style fallback if no globs detected\n if (found.size === 0) {\n for (const base of [\"apps\", \"packages\"]) {\n const baseAbs = path.join(rootAbs, base);\n if (!fs.existsSync(baseAbs) || !fs.statSync(baseAbs).isDirectory())\n continue;\n for (const name of fs.readdirSync(baseAbs)) {\n const rel = `${base}/${name}`;\n const abs = path.join(rootAbs, rel);\n if (!fs.statSync(abs).isDirectory()) continue;\n if (fs.existsSync(path.join(abs, \"package.json\"))) found.add(rel);\n }\n }\n }\n\n return [...found].sort((a, b) => a.localeCompare(b));\n}\n\nasync function pickWorkspaces(workspaces: string[]): Promise<string[]> {\n const picked = await p.multiselect({\n message: \"Select workspaces to generate .env.example for\",\n options: workspaces.map((w) => ({ label: w, value: w })),\n required: false,\n });\n\n if (p.isCancel(picked)) {\n p.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n\n return (picked as string[]) ?? [];\n}\n\n/* -------------------------------------------------------------------------- */\n\nconst program = new Command();\n\nprogram\n .name(\"asyq\")\n .description(\"Generate .env.example by scanning your project for env usage\")\n .version(`v${getPackageVersion()}`);\n\nprogram\n .command(\"init\")\n .description(\"Scan project and generate .env.example\")\n .option(\"--root <dir>\", \"Project root to scan\", \".\")\n .option(\"--out <file>\", \"Output file name\", \".env.example\")\n .option(\"--force\", \"Overwrite output if it exists\")\n .option(\n \"--include-lowercase\",\n \"Include lowercase/mixed-case keys (not recommended)\"\n )\n .option(\"--debug\", \"Print scan diagnostics\")\n .option(\"--monorepo\", \"Generate .env.example for root + each workspace\")\n .option(\n \"--select\",\n \"In monorepo mode: interactively choose which workspaces to generate for\"\n )\n .option(\n \"--workspaces <list>\",\n \"In monorepo mode: comma-separated workspace list to generate for\"\n )\n .option(\"--no-root\", \"In monorepo mode: skip generating for repo root\")\n .action(async (opts) => {\n p.intro(pc.cyan(`Asyq CLI v${getPackageVersion()} Created by @thev1ndu`));\n\n const rootAbs = path.resolve(process.cwd(), opts.root);\n const outName = String(opts.out || \".env.example\");\n\n const mode = await pickMode();\n const model: ModelName | null = mode === \"ai\" ? await pickModel() : null;\n\n const targets: { label: string; dirAbs: string }[] = [];\n\n // Root target (default on, unless --no-root)\n if (opts.root !== false) {\n targets.push({ label: \"root\", dirAbs: rootAbs });\n }\n\n // Monorepo targets\n if (opts.monorepo) {\n const workspaces = detectWorkspaces(rootAbs);\n\n if (workspaces.length === 0) {\n p.note(\n \"No workspaces detected (pnpm-workspace.yaml / package.json workspaces / apps/* / packages/*).\",\n \"Monorepo\"\n );\n } else {\n let selected = workspaces;\n\n if (opts.workspaces) {\n const allow = new Set(\n String(opts.workspaces)\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n );\n selected = workspaces.filter((w) => allow.has(w));\n const missing = [...allow].filter((x) => !workspaces.includes(x));\n if (missing.length) {\n p.note(missing.join(\"\\n\"), \"Unknown workspaces ignored\");\n }\n } else if (opts.select) {\n selected = await pickWorkspaces(workspaces);\n }\n\n for (const rel of selected) {\n targets.push({ label: rel, dirAbs: path.join(rootAbs, rel) });\n }\n }\n }\n\n if (targets.length === 0) {\n fail(\n \"No targets selected. Tip: remove --no-root or select at least one workspace.\"\n );\n }\n\n // API key once for all targets (AI mode)\n let apiKey = \"\";\n if (mode === \"ai\" && model) {\n apiKey = await getApiKey();\n if (!apiKey) fail(\"OpenAI API key is required for AI-assisted mode.\");\n }\n\n const results: Array<{\n target: string;\n outRel: string;\n keys: number;\n files: number;\n wrote: boolean;\n }> = [];\n\n for (const t of targets) {\n const outFileAbs = path.join(t.dirAbs, outName);\n const outRelFromRoot =\n path.relative(rootAbs, outFileAbs).replace(/\\\\/g, \"/\") || outName;\n\n if (fs.existsSync(outFileAbs) && !opts.force) {\n fail(\n `Output already exists: ${outRelFromRoot}. Use --force to overwrite.`\n );\n }\n\n const s = p.spinner();\n s.start(`Scanning ${t.label} for environment variables`);\n\n const res = scanProjectForEnvKeys({\n rootDir: t.dirAbs,\n includeLowercase: !!opts.includeLowercase,\n });\n\n s.stop(\n `Scan complete: ${t.label} (${res.filesScanned} files, ${res.keys.size} keys)`\n );\n\n if (opts.debug) {\n p.note(\n [\n `dir: ${t.dirAbs}`,\n `files scanned: ${res.filesScanned}`,\n `keys found: ${res.keys.size}`,\n ].join(\"\\n\"),\n `${t.label} diagnostics`\n );\n }\n\n // If no keys, skip writing (current behavior), but show a clear note.\n if (res.keys.size === 0) {\n p.note(\n `No env vars detected in ${t.label}. Skipping ${outRelFromRoot}`,\n \"Nothing to write\"\n );\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: 0,\n files: res.filesScanned,\n wrote: false,\n });\n continue;\n }\n\n const keys = [...res.keys].sort((a, b) => a.localeCompare(b));\n let content = keys.map((k) => `${k}=`).join(\"\\n\") + \"\\n\";\n\n if (mode === \"ai\" && model) {\n const aiSpinner = p.spinner();\n aiSpinner.start(`Writing .env.example documentation for ${t.label}`);\n\n try {\n const docs = await generateEnvDocsWithOpenAI({\n apiKey,\n model,\n projectHint:\n \"Write practical guidance for developers setting env vars.\",\n contexts: res.contexts,\n keys,\n });\n\n aiSpinner.stop(\n `Documented ${keys.length} env variables for ${t.label}`\n );\n\n const byKey = new Map(docs.map((d) => [d.key, d]));\n\n content =\n keys\n .map((k) => {\n const d = byKey.get(k);\n if (!d) return `${k}=\\n`;\n\n const secretNote = d.is_secret\n ? \"Secret value. Do not commit.\"\n : \"Non-secret value (verify before committing).\";\n\n return [\n `# ${d.key}`,\n `# ${d.description}`,\n `# Where to get it: ${d.where_to_get}`,\n `# ${secretNote}`,\n `${d.key}=${d.example_value || \"\"}`,\n \"\",\n ].join(\"\\n\");\n })\n .join(\"\\n\")\n .trimEnd() + \"\\n\";\n } catch (e: any) {\n aiSpinner.stop(`Failed to write documentation for ${t.label}`);\n fail(e?.message ?? String(e));\n }\n }\n\n fs.writeFileSync(outFileAbs, content, \"utf8\");\n\n results.push({\n target: t.label,\n outRel: outRelFromRoot,\n keys: keys.length,\n files: res.filesScanned,\n wrote: true,\n });\n }\n\n // Summary table\n const summary = results\n .map((r) => {\n const status = r.wrote ? pc.green(\"wrote\") : pc.yellow(\"skipped\");\n return `${pc.cyan(r.target.padEnd(20))} ${pc.green(\n String(r.keys).padStart(3)\n )} keys → ${pc.dim(r.outRel)} ${pc.dim(`(${status})`)}`;\n })\n .join(\"\\n\");\n\n p.note(summary, \"Generated\");\n p.outro(pc.green(\"✓ All done!\"));\n });\n\nprogram.parse(process.argv);\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nexport type ScanOptions = {\n rootDir: string;\n includeLowercase?: boolean;\n maxContextPerKey?: number;\n};\n\nexport type KeyContext = { file: string; line: number; snippet: string };\n\nexport type ScanResult = {\n keys: Set<string>;\n filesScanned: number;\n contexts: Record<string, KeyContext[]>;\n};\n\nconst IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \".turbo\",\n \".cache\",\n \".vercel\",\n \".netlify\",\n]);\n\n// ENV VARS MUST BE EXPLICIT + UPPERCASE\nconst ENV_KEY_RE_STRICT = /^[A-Z][A-Z0-9_]*$/;\nconst ENV_KEY_RE_LOOSE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport function scanProjectForEnvKeys(opts: ScanOptions): ScanResult {\n const root = opts.rootDir;\n const maxCtx = opts.maxContextPerKey ?? 2;\n\n const keyOk = (k: string) =>\n (opts.includeLowercase ? ENV_KEY_RE_LOOSE : ENV_KEY_RE_STRICT).test(k);\n\n const exts = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".toml\",\n ]);\n\n const keys = new Set<string>();\n const contexts: Record<string, KeyContext[]> = {};\n let filesScanned = 0;\n const seenInCode = new Set<string>();\n\n walk(root);\n\n // Only keep keys that were found in actual code, not just .env files\n // Normalize to uppercase for comparison\n const finalKeys = new Set<string>();\n const seenInCodeUpper = new Set([...seenInCode].map((k) => k.toUpperCase()));\n\n for (const key of keys) {\n if (seenInCodeUpper.has(key.toUpperCase())) {\n finalKeys.add(key);\n }\n }\n\n // Add any code-only keys that weren't in .env files\n for (const codeKey of seenInCode) {\n const upper = codeKey.toUpperCase();\n let found = false;\n for (const key of finalKeys) {\n if (key.toUpperCase() === upper) {\n found = true;\n break;\n }\n }\n if (!found && keyOk(codeKey)) {\n finalKeys.add(codeKey);\n }\n }\n\n return { keys: finalKeys, filesScanned, contexts };\n\n function addCtx(key: string, relFile: string, line: number, snippet: string) {\n if (!contexts[key]) contexts[key] = [];\n if (contexts[key].length >= maxCtx) return;\n contexts[key].push({\n file: relFile,\n line,\n snippet: snippet.trim().slice(0, 220),\n });\n }\n\n function walk(dir: string) {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!IGNORE_DIRS.has(entry.name)) walk(full);\n continue;\n }\n\n const isEnvFile = entry.name === \".env\" || entry.name.startsWith(\".env.\");\n const ext = path.extname(entry.name);\n\n if (!isEnvFile && !exts.has(ext)) continue;\n\n const content = safeRead(full);\n if (!content) continue;\n\n filesScanned++;\n const rel = path.relative(root, full).replace(/\\\\/g, \"/\");\n\n if (isEnvFile) {\n extractFromEnvFile(content, rel, keys, addCtx, keyOk);\n } else {\n extractFromCode(content, rel, keys, seenInCode, addCtx, keyOk);\n }\n }\n }\n}\n\nfunction extractFromEnvFile(\n text: string,\n relFile: string,\n keys: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments and empty lines\n if (!ln.trim() || ln.trim().startsWith(\"#\")) continue;\n\n // Match variable assignments\n const m = ln.match(/^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/);\n if (!m) continue;\n\n const k = m[1];\n if (!keyOk(k)) continue;\n\n keys.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n}\n\nfunction extractFromCode(\n text: string,\n relFile: string,\n keys: Set<string>,\n seenInCode: Set<string>,\n addCtx: (key: string, relFile: string, line: number, snippet: string) => void,\n keyOk: (k: string) => boolean\n) {\n const lines = text.split(/\\r?\\n/);\n\n // JavaScript/TypeScript environment variable patterns\n const patterns: RegExp[] = [\n // process.env.KEY or process?.env?.KEY\n /\\bprocess(?:\\?\\.|\\.)env(?:\\?\\.|\\.)([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // process.env[\"KEY\"] or process?.env?.[\"KEY\"]\n /\\bprocess(?:\\?\\.|\\.)env\\[\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\]/gi,\n // import.meta.env.KEY (Vite, etc.)\n /\\bimport\\.meta\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n // Deno.env.get(\"KEY\")\n /\\bDeno\\.env\\.get\\(\\s*[\"']([A-Za-z_][A-Za-z0-9_]*)[\"']\\s*\\)/gi,\n // Bun.env.KEY\n /\\bBun\\.env\\.([A-Za-z_][A-Za-z0-9_]*)\\b/gi,\n ];\n\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n\n // Skip comments\n if (ln.trim().startsWith(\"//\") || ln.trim().startsWith(\"#\")) continue;\n\n for (const re of patterns) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(ln))) {\n const k = match[1];\n if (!keyOk(k)) continue;\n keys.add(k);\n seenInCode.add(k);\n addCtx(k, relFile, i + 1, ln);\n }\n }\n }\n}\n\nfunction safeRead(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n","export type AIEnvDoc = {\n key: string;\n description: string;\n where_to_get: string;\n example_value: string;\n is_secret: boolean;\n};\n\nexport type AIGenerateOptions = {\n apiKey: string;\n model: string;\n projectHint?: string;\n contexts: Record<string, { file: string; line: number; snippet: string }[]>;\n keys: string[];\n};\n\nconst JSON_SCHEMA = {\n name: \"env_docs\",\n strict: true,\n schema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n key: { type: \"string\" },\n description: { type: \"string\" },\n where_to_get: { type: \"string\" },\n example_value: { type: \"string\" },\n is_secret: { type: \"boolean\" },\n },\n required: [\n \"key\",\n \"description\",\n \"where_to_get\",\n \"example_value\",\n \"is_secret\",\n ],\n },\n },\n },\n required: [\"items\"],\n },\n} as const;\n\nfunction buildInput(opts: AIGenerateOptions) {\n const lines = opts.keys.map((k) => {\n const ctx = opts.contexts[k]?.[0];\n const seenAt = ctx ? `${ctx.file}:${ctx.line}` : \"unknown\";\n const snippet = ctx ? ctx.snippet : \"\";\n return `- ${k}\\n seen_at: ${seenAt}\\n snippet: ${snippet}`;\n });\n\n const system = [\n \"You generate documentation for environment variables.\",\n \"Return ONLY JSON that matches the provided JSON Schema.\",\n \"Do not include markdown or extra text.\",\n \"Never output real secrets. Use safe placeholders.\",\n \"Keep descriptions short and practical.\",\n \"where_to_get must be actionable (dashboard, secret manager, CI, local service, etc.).\",\n ].join(\" \");\n\n const user = [\n opts.projectHint ? `Project hint: ${opts.projectHint}` : \"\",\n \"Variables:\",\n ...lines,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n return [\n { role: \"system\", content: system },\n { role: \"user\", content: user },\n ];\n}\n\nfunction extractTextFromResponses(data: any): string {\n if (typeof data?.output_text === \"string\" && data.output_text.trim())\n return data.output_text;\n\n const out = data?.output;\n if (Array.isArray(out)) {\n for (const item of out) {\n const content = item?.content;\n if (!Array.isArray(content)) continue;\n for (const c of content) {\n if (typeof c?.text === \"string\" && c.text.trim()) return c.text;\n }\n }\n }\n return \"\";\n}\n\nfunction tryParseJsonLoose(raw: string): any | null {\n // First try direct parse\n try {\n return JSON.parse(raw);\n } catch {\n // Try to extract the first {...} block\n const m = raw.match(/\\{[\\s\\S]*\\}/);\n if (!m) return null;\n try {\n return JSON.parse(m[0]);\n } catch {\n return null;\n }\n }\n}\n\nexport async function generateEnvDocsWithOpenAI(\n opts: AIGenerateOptions\n): Promise<AIEnvDoc[]> {\n const input = buildInput(opts);\n\n const res = await fetch(\"https://api.openai.com/v1/responses\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: opts.model,\n input,\n text: {\n format: {\n type: \"json_schema\",\n ...JSON_SCHEMA,\n },\n },\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`OpenAI request failed (${res.status}): ${text}`);\n }\n\n const data: any = await res.json();\n const raw = extractTextFromResponses(data).trim();\n\n const parsed = tryParseJsonLoose(raw);\n if (!parsed) {\n throw new Error(\n \"AI output was not valid JSON. Try again, or use a different model.\"\n );\n }\n\n const items = Array.isArray(parsed?.items) ? parsed.items : [];\n return items\n .map((x: any) => ({\n key: String(x.key ?? \"\"),\n description: String(x.description ?? \"\"),\n where_to_get: String(x.where_to_get ?? \"\"),\n example_value: String(x.example_value ?? \"\"),\n is_secret: Boolean(x.is_secret),\n }))\n .filter((x: AIEnvDoc) => x.key.length > 0);\n}\n"],"mappings":"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","names":["fs","path","path","fs"]}
|