@stacksjs/buddy 0.70.257 → 0.70.259

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.
Files changed (113) hide show
  1. package/dist/cli.d.ts +11 -1
  2. package/dist/cli.js +5 -164
  3. package/dist/commands/about.js +1 -35
  4. package/dist/commands/add.js +1 -25
  5. package/dist/commands/ai-context.js +4 -27
  6. package/dist/commands/auth.js +1 -68
  7. package/dist/commands/build.js +1 -221
  8. package/dist/commands/cd.js +8 -39
  9. package/dist/commands/changelog.js +1 -32
  10. package/dist/commands/clean.js +1 -44
  11. package/dist/commands/cloud.js +2 -519
  12. package/dist/commands/commit.js +1 -16
  13. package/dist/commands/completion.js +6 -42
  14. package/dist/commands/config-migrate.js +6 -94
  15. package/dist/commands/configure.js +2 -45
  16. package/dist/commands/create.js +1 -162
  17. package/dist/commands/deploy.d.ts +10 -4
  18. package/dist/commands/deploy.js +13 -1664
  19. package/dist/commands/desktop-apple.js +12 -399
  20. package/dist/commands/dev.js +6 -1083
  21. package/dist/commands/dns.js +3 -103
  22. package/dist/commands/docs/buddy-commands.js +10 -132
  23. package/dist/commands/docs/buddy-commands.test.js +1 -38
  24. package/dist/commands/docs/framework-repo.js +1 -14
  25. package/dist/commands/docs/framework-repo.test.js +1 -68
  26. package/dist/commands/docs/generated-artifacts.js +3 -70
  27. package/dist/commands/docs/generated-artifacts.test.js +1 -19
  28. package/dist/commands/docs/links.js +2 -85
  29. package/dist/commands/docs/links.test.js +4 -66
  30. package/dist/commands/docs.js +1 -24
  31. package/dist/commands/doctor.js +2 -478
  32. package/dist/commands/domains.js +2 -152
  33. package/dist/commands/email.js +31 -503
  34. package/dist/commands/env.js +1 -312
  35. package/dist/commands/extension.js +1 -239
  36. package/dist/commands/features.js +9 -345
  37. package/dist/commands/fresh.js +1 -46
  38. package/dist/commands/generate.js +2 -152
  39. package/dist/commands/http.js +1 -33
  40. package/dist/commands/index.js +1 -57
  41. package/dist/commands/install.js +1 -22
  42. package/dist/commands/key.js +1 -23
  43. package/dist/commands/link.js +2 -147
  44. package/dist/commands/lint.js +2 -46
  45. package/dist/commands/list.js +1 -140
  46. package/dist/commands/mail.js +15 -1477
  47. package/dist/commands/maintenance.js +1 -141
  48. package/dist/commands/make.js +1 -392
  49. package/dist/commands/migrate-project.js +1 -49
  50. package/dist/commands/migrate.js +27 -625
  51. package/dist/commands/outdated.js +1 -19
  52. package/dist/commands/package.js +1 -17
  53. package/dist/commands/phone.js +17 -181
  54. package/dist/commands/ports.js +2 -118
  55. package/dist/commands/prepublish.js +1 -17
  56. package/dist/commands/projects.js +1 -29
  57. package/dist/commands/protocol/craft-evidence.js +4 -98
  58. package/dist/commands/protocol/craft-evidence.test.js +1 -20
  59. package/dist/commands/protocol/desktop-lifecycle-report.js +3 -57
  60. package/dist/commands/protocol/desktop-lifecycle-report.test.js +1 -32
  61. package/dist/commands/protocol/desktop-support.js +2 -32
  62. package/dist/commands/protocol/desktop-workflow.test.js +3 -35
  63. package/dist/commands/protocol/driver-registry.js +2 -71
  64. package/dist/commands/protocol/pantry-evidence.js +4 -144
  65. package/dist/commands/protocol/pantry-evidence.test.js +1 -29
  66. package/dist/commands/protocol/release-manifest.js +3 -102
  67. package/dist/commands/protocol/release-manifest.test.js +2 -34
  68. package/dist/commands/protocol/run-conformance.js +4 -346
  69. package/dist/commands/protocol/run-conformance.test.js +1 -52
  70. package/dist/commands/protocol/run-driver-contracts.js +4 -83
  71. package/dist/commands/protocol/run-driver-contracts.test.js +1 -18
  72. package/dist/commands/protocol/source-manifest.js +3 -113
  73. package/dist/commands/protocol/sync-suite.js +3 -136
  74. package/dist/commands/protocol/sync-suite.test.js +1 -44
  75. package/dist/commands/protocol.js +1 -60
  76. package/dist/commands/publish.js +8 -500
  77. package/dist/commands/queue.js +1 -248
  78. package/dist/commands/release.js +1 -42
  79. package/dist/commands/route.js +1 -21
  80. package/dist/commands/run-tool.js +1 -9
  81. package/dist/commands/saas.js +1 -25
  82. package/dist/commands/schedule.js +1 -61
  83. package/dist/commands/search.js +1 -84
  84. package/dist/commands/seed.js +1 -47
  85. package/dist/commands/serve.js +1 -55
  86. package/dist/commands/setup-ai.js +2 -121
  87. package/dist/commands/setup.js +2 -305
  88. package/dist/commands/share.js +1 -214
  89. package/dist/commands/sms.js +32 -319
  90. package/dist/commands/stacks.js +1 -72
  91. package/dist/commands/telemetry.js +1 -74
  92. package/dist/commands/test.js +1 -130
  93. package/dist/commands/tinker.js +1 -37
  94. package/dist/commands/types.js +1 -18
  95. package/dist/commands/upgrade.js +1 -98
  96. package/dist/commands/user.js +3 -84
  97. package/dist/commands/version.js +1 -14
  98. package/dist/config.js +1 -231
  99. package/dist/custom-cli.js +1 -23
  100. package/dist/database-preflight.js +1 -88
  101. package/dist/global-options.js +1 -4
  102. package/dist/index.js +1 -1
  103. package/dist/lazy-commands.js +1 -241
  104. package/dist/migrators/index.js +2 -62
  105. package/dist/migrators/laravel/index.js +1 -148
  106. package/dist/migrators/laravel/migrations.js +4 -231
  107. package/dist/migrators/laravel/models.js +8 -111
  108. package/dist/migrators/rails/index.js +1 -11
  109. package/dist/production-server.js +1 -257
  110. package/dist/project-setup.js +1 -36
  111. package/dist/serve-entry.js +1 -5
  112. package/dist/version-info.js +1 -5
  113. package/package.json +54 -47
@@ -1,55 +1 @@
1
- import { existsSync } from "node:fs";
2
- import { join } from "node:path";
3
- import process from "node:process";
4
- import { log } from "@stacksjs/logging";
5
- import { ExitCode } from "@stacksjs/types";
6
- import { startProductionServer } from "../production-server";
7
- export {
8
- loadStxPartialsDir,
9
- resolveUserPartialsPath,
10
- startProductionServer
11
- } from "../production-server";
12
- export function serve(buddy) {
13
- buddy.command("serve", "Start the production HTTP server (STX views + /api proxy + coming-soon/maintenance gate)").option("-p, --port <port>", "Port to listen on (defaults to PORT env or 3000)").option("--verbose", "Enable verbose output", { default: !1 }).action(startProductionServer);
14
- }
15
- export function serveApi(buddy) {
16
- buddy.command("serve:api", "Start the production API server (bun-router routes the frontend proxies /api to)").option("-p, --port <port>", "Port to listen on (defaults to PORT env or 3008)").action(async (options) => {
17
- if (options?.port)
18
- process.env.PORT = String(options.port);
19
- process.env.APP_ENV = process.env.APP_ENV || "production";
20
- await import(resolveApiEntry());
21
- });
22
- }
23
- function resolveApiEntry() {
24
- const vendored = join(process.cwd(), "storage/framework/core/actions/src/serve/api.ts");
25
- if (existsSync(vendored))
26
- return vendored;
27
- return "@stacksjs/actions/serve/api";
28
- }
29
- export function preview(buddy) {
30
- buddy.command("preview [dir]", "Serve a static build locally, the way a static host would").option("-p, --port <port>", "Port to listen on", { default: "3001" }).example("buddy preview").example("buddy preview dist --port 4000").action(async (dir, options) => {
31
- const root = dir || "dist", port = Number(options?.port) || 3001;
32
- if (!existsSync(root)) {
33
- log.error(`No \`${root}\` directory to preview. Run \`buddy build\` first, or pass the directory to serve.`);
34
- process.exit(ExitCode.FatalError);
35
- }
36
- Bun.serve({
37
- port,
38
- async fetch(req) {
39
- let pathname = new URL(req.url).pathname;
40
- if (pathname === "/" || pathname === "")
41
- pathname = "/index.html";
42
- else if (!pathname.includes("."))
43
- pathname = `${pathname.replace(/\/$/, "")}.html`;
44
- const file = Bun.file(join(root, pathname));
45
- if (await file.exists())
46
- return new Response(file);
47
- const notFound = Bun.file(join(root, "404.html"));
48
- if (await notFound.exists())
49
- return new Response(notFound, { status: 404 });
50
- return new Response("Not Found", { status: 404 });
51
- }
52
- });
53
- log.success(`Previewing ./${root} at http://localhost:${port}`);
54
- });
55
- }
1
+ import{existsSync}from"node:fs";import{join}from"node:path";import process from"node:process";import{log}from"@stacksjs/logging";import{ExitCode}from"@stacksjs/types";import{startProductionServer}from"../production-server";export{loadStxPartialsDir,resolveUserPartialsPath,startProductionServer}from"../production-server";export function serve(buddy){buddy.command("serve","Start the production HTTP server (STX views + /api proxy + coming-soon/maintenance gate)").option("-p, --port <port>","Port to listen on (defaults to PORT env or 3000)").option("--verbose","Enable verbose output",{default:!1}).action(startProductionServer)}export function serveApi(buddy){buddy.command("serve:api","Start the production API server (bun-router routes the frontend proxies /api to)").option("-p, --port <port>","Port to listen on (defaults to PORT env or 3008)").action(async(options)=>{if(options?.port)process.env.PORT=String(options.port);process.env.APP_ENV=process.env.APP_ENV||"production";await import(resolveApiEntry())})}function resolveApiEntry(){const vendored=join(process.cwd(),"storage/framework/core/actions/src/serve/api.ts");if(existsSync(vendored))return vendored;return"@stacksjs/actions/serve/api"}export function preview(buddy){buddy.command("preview [dir]","Serve a static build locally, the way a static host would").option("-p, --port <port>","Port to listen on",{default:"3001"}).example("buddy preview").example("buddy preview dist --port 4000").action(async(dir,options)=>{const root=dir||"dist",port=Number(options?.port)||3001;if(!existsSync(root)){log.error(`No \`${root}\` directory to preview. Run \`buddy build\` first, or pass the directory to serve.`);process.exit(ExitCode.FatalError)}Bun.serve({port,async fetch(req){let pathname=new URL(req.url).pathname;if(pathname==="/"||pathname==="")pathname="/index.html";else if(!pathname.includes("."))pathname=`${pathname.replace(/\/$/,"")}.html`;const file=Bun.file(join(root,pathname));if(await file.exists())return new Response(file);const notFound=Bun.file(join(root,"404.html"));if(await notFound.exists())return new Response(notFound,{status:404});return new Response("Not Found",{status:404})}});log.success(`Previewing ./${root} at http://localhost:${port}`)})}
@@ -1,121 +1,2 @@
1
- import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
2
- import process from "node:process";
3
- import { dim, green, log, yellow } from "@stacksjs/cli";
4
- import { frameworkPath, join, projectPath, relative } from "@stacksjs/path";
5
- import { listSkills, resolveSkillPath } from "@stacksjs/skills";
6
- export const AI_PROVIDERS = [
7
- { id: "claude", label: "Claude Code", reads: ["AGENTS.md", "CLAUDE.md", ".claude/skills", ".claude/launch.json"] },
8
- { id: "codex", label: "OpenAI Codex CLI", reads: ["AGENTS.md"] },
9
- { id: "cursor", label: "Cursor", reads: ["AGENTS.md", ".cursor/rules"] },
10
- { id: "copilot", label: "GitHub Copilot", reads: ["AGENTS.md", ".github/copilot-instructions.md"] },
11
- { id: "gemini", label: "Gemini CLI", reads: ["AGENTS.md", "GEMINI.md"] }
12
- ];
13
- export function isAiProvider(value) {
14
- return AI_PROVIDERS.some((provider) => provider.id === value);
15
- }
16
- function materialize(target, path, options) {
17
- const existing = lstatSync(path, { throwIfNoEntry: !1 });
18
- if (existing) {
19
- if (!existing.isSymbolicLink() && !options.force)
20
- return !1;
21
- rmSync(path, { recursive: !0, force: !0 });
22
- }
23
- mkdirSync(join(path, ".."), { recursive: !0 });
24
- if (options.copy)
25
- cpSync(target, path, { recursive: !0 });
26
- else
27
- symlinkSync(relative(join(path, ".."), target), path, process.platform === "win32" ? "junction" : "dir");
28
- return !0;
29
- }
30
- function materializeFile(source, path, options) {
31
- if (existsSync(path) && !options.force)
32
- return !1;
33
- mkdirSync(join(path, ".."), { recursive: !0 });
34
- cpSync(source, path, { recursive: !0, force: !0 });
35
- return !0;
36
- }
37
- function installSkills(destination, options) {
38
- const names = listSkills();
39
- let installed = 0, skipped = 0;
40
- mkdirSync(destination, { recursive: !0 });
41
- for (const entry of readdirSync(destination)) {
42
- const path = join(destination, entry);
43
- if (lstatSync(path, { throwIfNoEntry: !1 })?.isSymbolicLink() && !existsSync(path))
44
- rmSync(path, { force: !0 });
45
- }
46
- for (const name of names) {
47
- const source = resolveSkillPath(name);
48
- if (!source)
49
- continue;
50
- if (materialize(source, join(destination, name), options))
51
- installed++;
52
- else
53
- skipped++;
54
- }
55
- return { installed, skipped };
56
- }
57
- export function setupAiProvider(provider, options = {}) {
58
- const created = [], skipped = [], defaults = frameworkPath("defaults/ai"), rel = (path) => relative(projectPath(), path), record = (path, didCreate) => {
59
- (didCreate ? created : skipped).push(rel(path));
60
- }, agents = projectPath("AGENTS.md");
61
- record(agents, materializeFile(join(defaults, "AGENTS.md"), agents, { ...options, force: !1 }));
62
- switch (provider) {
63
- case "claude": {
64
- const claudeMd = projectPath("CLAUDE.md");
65
- record(claudeMd, materialize(agents, claudeMd, { ...options, copy: !1 }));
66
- const launch = projectPath(".claude/launch.json");
67
- record(launch, materializeFile(join(defaults, "claude/launch.json"), launch, options));
68
- const skillsDir = projectPath(".claude/skills"), { installed, skipped: untouched } = installSkills(skillsDir, options);
69
- if (installed > 0)
70
- created.push(`${rel(skillsDir)} (${installed} skills)`);
71
- if (untouched > 0)
72
- skipped.push(`${rel(skillsDir)} (${untouched} skills)`);
73
- break;
74
- }
75
- case "codex":
76
- break;
77
- case "cursor": {
78
- const rules = projectPath(".cursor/rules");
79
- record(rules, materialize(frameworkPath("defaults/ide/cursor/rules"), rules, options));
80
- break;
81
- }
82
- case "copilot": {
83
- const instructions = projectPath(".github/copilot-instructions.md");
84
- if (existsSync(instructions) && !options.force)
85
- skipped.push(rel(instructions));
86
- else {
87
- mkdirSync(projectPath(".github"), { recursive: !0 });
88
- writeFileSync(instructions, pointerFile("AGENTS.md"), "utf8");
89
- created.push(rel(instructions));
90
- }
91
- break;
92
- }
93
- case "gemini": {
94
- const geminiMd = projectPath("GEMINI.md");
95
- record(geminiMd, materialize(agents, geminiMd, { ...options, copy: !1 }));
96
- break;
97
- }
98
- }
99
- return { created, skipped };
100
- }
101
- function pointerFile(target) {
102
- return [
103
- "# Project instructions",
104
- "",
105
- `This project keeps its agent guidance in [\`${target}\`](../${target}), so every`,
106
- "agent works from the same rules. Read that file.",
107
- "",
108
- "Regenerate this pointer with `buddy setup:ai copilot --force`.",
109
- ""
110
- ].join(`
111
- `);
112
- }
113
- export function reportAiSetup(provider, result) {
114
- for (const path of result.created)
115
- log.info(` ${green("+")} ${path}`);
116
- for (const path of result.skipped)
117
- log.info(` ${yellow("\xB7")} ${path} ${dim("(already present, left alone)")}`);
118
- if (result.created.length === 0)
119
- log.info(dim(" Nothing to do - re-run with --force to overwrite."));
120
- log.success(`${provider.label} is set up. It reads: ${provider.reads.join(", ")}`);
121
- }
1
+ import{cpSync,existsSync,lstatSync,mkdirSync,readdirSync,rmSync,symlinkSync,writeFileSync}from"node:fs";import process from"node:process";import{dim,green,log,yellow}from"@stacksjs/cli";import{frameworkPath,join,projectPath,relative}from"@stacksjs/path";import{listSkills,resolveSkillPath}from"@stacksjs/skills";export const AI_PROVIDERS=[{id:"claude",label:"Claude Code",reads:["AGENTS.md","CLAUDE.md",".claude/skills",".claude/launch.json"]},{id:"codex",label:"OpenAI Codex CLI",reads:["AGENTS.md"]},{id:"cursor",label:"Cursor",reads:["AGENTS.md",".cursor/rules"]},{id:"copilot",label:"GitHub Copilot",reads:["AGENTS.md",".github/copilot-instructions.md"]},{id:"gemini",label:"Gemini CLI",reads:["AGENTS.md","GEMINI.md"]}];export function isAiProvider(value){return AI_PROVIDERS.some((provider)=>provider.id===value)}function materialize(target,path,options){const existing=lstatSync(path,{throwIfNoEntry:!1});if(existing){if(!existing.isSymbolicLink()&&!options.force)return!1;rmSync(path,{recursive:!0,force:!0})}mkdirSync(join(path,".."),{recursive:!0});if(options.copy)cpSync(target,path,{recursive:!0});else symlinkSync(relative(join(path,".."),target),path,process.platform==="win32"?"junction":"dir");return!0}function materializeFile(source,path,options){if(existsSync(path)&&!options.force)return!1;mkdirSync(join(path,".."),{recursive:!0});cpSync(source,path,{recursive:!0,force:!0});return!0}function installSkills(destination,options){const names=listSkills();let installed=0,skipped=0;mkdirSync(destination,{recursive:!0});for(const entry of readdirSync(destination)){const path=join(destination,entry);if(lstatSync(path,{throwIfNoEntry:!1})?.isSymbolicLink()&&!existsSync(path))rmSync(path,{force:!0})}for(const name of names){const source=resolveSkillPath(name);if(!source)continue;if(materialize(source,join(destination,name),options))installed++;else skipped++}return{installed,skipped}}export function setupAiProvider(provider,options={}){const created=[],skipped=[],defaults=frameworkPath("defaults/ai"),rel=(path)=>relative(projectPath(),path),record=(path,didCreate)=>{(didCreate?created:skipped).push(rel(path))},agents=projectPath("AGENTS.md");record(agents,materializeFile(join(defaults,"AGENTS.md"),agents,{...options,force:!1}));switch(provider){case"claude":{const claudeMd=projectPath("CLAUDE.md");record(claudeMd,materialize(agents,claudeMd,{...options,copy:!1}));const launch=projectPath(".claude/launch.json");record(launch,materializeFile(join(defaults,"claude/launch.json"),launch,options));const skillsDir=projectPath(".claude/skills"),{installed,skipped:untouched}=installSkills(skillsDir,options);if(installed>0)created.push(`${rel(skillsDir)} (${installed} skills)`);if(untouched>0)skipped.push(`${rel(skillsDir)} (${untouched} skills)`);break}case"codex":break;case"cursor":{const rules=projectPath(".cursor/rules");record(rules,materialize(frameworkPath("defaults/ide/cursor/rules"),rules,options));break}case"copilot":{const instructions=projectPath(".github/copilot-instructions.md");if(existsSync(instructions)&&!options.force)skipped.push(rel(instructions));else{mkdirSync(projectPath(".github"),{recursive:!0});writeFileSync(instructions,pointerFile("AGENTS.md"),"utf8");created.push(rel(instructions))}break}case"gemini":{const geminiMd=projectPath("GEMINI.md");record(geminiMd,materialize(agents,geminiMd,{...options,copy:!1}));break}}return{created,skipped}}function pointerFile(target){return["# Project instructions","",`This project keeps its agent guidance in [\`${target}\`](../${target}), so every`,"agent works from the same rules. Read that file.","","Regenerate this pointer with `buddy setup:ai copilot --force`.",""].join(`
2
+ `)}export function reportAiSetup(provider,result){for(const path of result.created)log.info(` ${green("+")} ${path}`);for(const path of result.skipped)log.info(` ${yellow("\xB7")} ${path} ${dim("(already present, left alone)")}`);if(result.created.length===0)log.info(dim(" Nothing to do - re-run with --force to overwrite."));log.success(`${provider.label} is set up. It reads: ${provider.reads.join(", ")}`)}
@@ -1,306 +1,3 @@
1
- import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import process from "node:process";
5
- import { runAction } from "@stacksjs/actions";
6
- import { log, onUnknownSubcommand, runCommand } from "@stacksjs/cli";
7
- import { Action } from "@stacksjs/enums";
8
- import { handleError } from "@stacksjs/error-handling";
9
- import { path as p } from "@stacksjs/path";
10
- import { copyFile, storage } from "@stacksjs/storage";
11
- import { ExitCode } from "@stacksjs/types";
12
- import { setupPrettyDevEnvironment } from "./dev";
13
- function getTimeoutMs(envVar, fallbackMs) {
14
- const value = Number(process.env[envVar]);
15
- if (Number.isFinite(value) && value > 0)
16
- return value;
17
- return fallbackMs;
18
- }
19
- const PANTRY_CHECK_TIMEOUT_MS = getTimeoutMs("PANTRY_CHECK_TIMEOUT_MS", 15000), PANTRY_INSTALL_TIMEOUT_MS = getTimeoutMs("PANTRY_INSTALL_TIMEOUT_MS", 600000), PANTRY_DEPENDENCIES_TIMEOUT_MS = getTimeoutMs("PANTRY_DEPENDENCIES_TIMEOUT_MS", 1200000), KEYGEN_TIMEOUT_MS = getTimeoutMs("KEYGEN_TIMEOUT_MS", 120000), AWS_CONFIG_TIMEOUT_MS = getTimeoutMs("AWS_CONFIG_TIMEOUT_MS", 900000);
20
- export function setup(buddy) {
21
- const descriptions = {
22
- setup: "This command ensures your project is setup correctly",
23
- ssl: "Setup SSL certificates and hosts file for HTTPS development",
24
- ai: "Set the project up for an AI coding agent (Claude Code, Codex, Cursor, Copilot, Gemini)",
25
- copy: "Copy the agent files instead of symlinking them, so they can be edited per project",
26
- force: "Overwrite files that already exist",
27
- ohMyZsh: "Enable Oh My Zsh",
28
- aws: "Ensures AWS is connected to the project",
29
- project: "Target a specific project",
30
- verbose: "Enable verbose output",
31
- domain: "Custom domain to setup (defaults to APP_URL)",
32
- skipHosts: "Skip adding domain to hosts file",
33
- skipTrust: "Skip trusting the certificate",
34
- skipAws: "Skip AWS configuration during setup",
35
- skipKeygen: "Skip generating an application key during setup"
36
- };
37
- buddy.command("setup", descriptions.setup).alias("ensure").option("-p, --project [project]", descriptions.project, { default: !1 }).option("--skip-aws", descriptions.skipAws, { default: !1 }).option("--skip-keygen", descriptions.skipKeygen, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
38
- log.debug("Running `buddy setup` ...", options);
39
- await ensurePantryInstalled();
40
- await optimizePantryDeps();
41
- await initializeProject(options);
42
- });
43
- buddy.command("setup:ssl", descriptions.ssl).alias("ssl:setup").option("-d, --domain [domain]", descriptions.domain).option("--skip-hosts", descriptions.skipHosts, { default: !1 }).option("--skip-trust", descriptions.skipTrust, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
44
- log.debug("Running `buddy setup:ssl` ...", options);
45
- if (!await setupPrettyDevEnvironment({
46
- domain: options.domain,
47
- skipHosts: options.skipHosts,
48
- skipTrust: options.skipTrust,
49
- verbose: options.verbose
50
- })) {
51
- log.warn("SSL setup completed with warnings");
52
- log.info("You may need to manually trust certificates or update hosts file");
53
- }
54
- });
55
- buddy.command("setup:ai [provider]", descriptions.ai).alias("ai:setup").option("--copy", descriptions.copy, { default: !1 }).option("--force", descriptions.force, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (provider, options) => {
56
- log.debug("Running `buddy setup:ai` ...", options);
57
- const { AI_PROVIDERS, isAiProvider, reportAiSetup, setupAiProvider } = await import("./setup-ai");
58
- let id = provider;
59
- if (!id) {
60
- const { select } = await import("@stacksjs/cli");
61
- id = await select({
62
- message: "Which AI coding agent do you use?",
63
- choices: AI_PROVIDERS.map((entry) => ({ value: entry.id, label: entry.label })),
64
- initial: 0
65
- });
66
- }
67
- if (!id || !isAiProvider(id)) {
68
- log.error(`Unknown AI provider: ${id}. Expected one of: ${AI_PROVIDERS.map((entry) => entry.id).join(", ")}`);
69
- process.exit(ExitCode.InvalidArgument);
70
- }
71
- const definition = AI_PROVIDERS.find((entry) => entry.id === id);
72
- reportAiSetup(definition, setupAiProvider(id, { copy: options.copy, force: options.force }));
73
- });
74
- buddy.command("setup:oh-my-zsh", descriptions.ohMyZsh).alias("upgrade:oh-my-zsh").option("--verbose", descriptions.verbose, { default: !1 }).action(async (_options) => {
75
- log.debug("Running `buddy setup:oh-my-zsh` ...", _options);
76
- const result = await runAction(Action.UpgradeShell);
77
- if (result.isErr) {
78
- log.error(result.error);
79
- process.exit(ExitCode.FatalError);
80
- }
81
- });
82
- onUnknownSubcommand(buddy, "setup");
83
- }
84
- async function isPantryInstalled() {
85
- try {
86
- return (await runCommand("pantry --version", {
87
- silent: !0,
88
- timeoutMs: PANTRY_CHECK_TIMEOUT_MS
89
- })).isOk;
90
- } catch {
91
- return !1;
92
- }
93
- }
94
- async function installPantry() {
95
- const bundledInstaller = p.frameworkPath("scripts/pantry-install"), command = existsSync(bundledInstaller) ? [bundledInstaller] : ["sh", "-c", "curl -fsSL https://pantry.dev | bash"], result = await runCommand(command, {
96
- timeoutMs: PANTRY_INSTALL_TIMEOUT_MS
97
- }), localBin = join(homedir(), ".local", "bin");
98
- if (!process.env.PATH?.split(":").includes(localBin))
99
- process.env.PATH = `${localBin}:${process.env.PATH || ""}`;
100
- if (result.isOk && await isPantryInstalled())
101
- return;
102
- if (result.isErr)
103
- handleError(result.error);
104
- else
105
- log.error("Pantry installed but is not available on PATH. Open a new shell and run `buddy setup` again.");
106
- process.exit(ExitCode.FatalError);
107
- }
108
- export async function ensurePantryInstalled() {
109
- if (await isPantryInstalled())
110
- return;
111
- log.info("Pantry is required. Installing it from https://pantry.dev...");
112
- await installPantry();
113
- }
114
- export async function ensurePantryDependencies(cwd) {
115
- await ensurePantryInstalled();
116
- log.info("Installing project dependencies with Pantry...");
117
- const result = await runCommand("pantry install", {
118
- cwd,
119
- timeoutMs: PANTRY_DEPENDENCIES_TIMEOUT_MS
120
- });
121
- if (result.isErr) {
122
- handleError(result.error);
123
- process.exit(ExitCode.FatalError);
124
- }
125
- if (existsSync(join(cwd, "package.json")) && !existsSync(join(cwd, "node_modules"))) {
126
- log.error("Pantry completed without installing the project JavaScript dependencies.");
127
- process.exit(ExitCode.FatalError);
128
- }
129
- log.success("Installed project dependencies with Pantry");
130
- }
131
- function hasAppKey(cwd) {
132
- const envPath = join(cwd, ".env");
133
- if (!existsSync(envPath))
134
- return !1;
135
- return /^APP_KEY=.+$/m.test(readFileSync(envPath, "utf-8"));
136
- }
137
- export async function ensureAppKey(cwd) {
138
- if (hasAppKey(cwd) || process.env.APP_KEY && process.env.APP_KEY.length > 0) {
139
- log.success("APP_KEY existed");
140
- return;
141
- }
142
- const keyResult = await runCommand("./buddy key:generate", {
143
- cwd,
144
- timeoutMs: KEYGEN_TIMEOUT_MS
145
- });
146
- if (keyResult.isErr) {
147
- handleError(keyResult.error);
148
- process.exit(ExitCode.FatalError);
149
- }
150
- log.success("Generated application key");
151
- }
152
- async function runInitialMigration(cwd) {
153
- const appEnv = (process.env.APP_ENV || process.env.NODE_ENV || "local").toLowerCase();
154
- if (!["local", "development", "dev", "test"].includes(appEnv)) {
155
- log.info(`Skipping initial migration in the ${appEnv} environment`);
156
- return;
157
- }
158
- log.info("Running initial database migration...");
159
- try {
160
- const result = await runAction(Action.Migrate, { cwd });
161
- if (result.isErr) {
162
- log.warn("Initial migration did not complete - you can run it later via ./buddy migrate");
163
- log.debug(result.error);
164
- return;
165
- }
166
- log.success("Database is migrated");
167
- } catch (error) {
168
- log.warn("Initial migration did not complete - you can run it later via ./buddy migrate");
169
- log.debug(error);
170
- }
171
- }
172
- async function initializeProject(options) {
173
- const cwd = options.cwd || p.projectPath();
174
- await ensurePantryDependencies(cwd);
175
- await ensureEnvIsSet(options);
176
- if (!options.skipKeygen)
177
- await ensureAppKey(cwd);
178
- await runInitialMigration(cwd);
179
- ensureIdeSettings(cwd);
180
- if (!options.skipAws) {
181
- log.info("Ensuring AWS is connected...");
182
- try {
183
- const awsResult = await runCommand("./buddy configure:aws", {
184
- cwd,
185
- timeoutMs: AWS_CONFIG_TIMEOUT_MS
186
- });
187
- if (awsResult.isErr) {
188
- log.warn("AWS not configured - you can do this later via ./buddy configure:aws");
189
- log.debug(awsResult.error);
190
- } else
191
- log.success("Configured AWS");
192
- } catch (error) {
193
- log.warn("AWS not configured - you can do this later via ./buddy configure:aws");
194
- log.debug(error);
195
- }
196
- }
197
- log.success("Project is setup");
198
- log.info("Run `./buddy doctor` anytime to check your setup. Happy coding! \uD83D\uDC99");
199
- }
200
- export function ensureIdeSettings(cwd) {
201
- const source = p.frameworkPath("defaults/ide/vscode/.vscode"), destination = join(cwd, ".vscode");
202
- if (existsSync(destination)) {
203
- log.debug(".vscode already exists; keeping the project settings");
204
- return;
205
- }
206
- if (!existsSync(source)) {
207
- log.debug("No bundled VS Code settings found; skipping IDE setup");
208
- return;
209
- }
210
- cpSync(source, destination, { recursive: !0 });
211
- log.success("Installed project VS Code settings");
212
- }
213
- const DB_CONNECTION_PACKAGES = {
214
- postgres: { name: "postgresql.org", version: "^17.10", service: "postgres" },
215
- mysql: { name: "mysql.com", version: "*", service: "mysql" },
216
- sqlite: { name: "sqlite.org", version: "^3.47.2" }
217
- };
218
- function databaseAliases(connection, pkg) {
219
- return [connection, pkg.name];
220
- }
221
- export function pantryDatabasePackage(connection) {
222
- return DB_CONNECTION_PACKAGES[connection];
223
- }
224
- function detectDbPackage(cwd) {
225
- const envPath = join(cwd, ".env"), envExamplePath = join(cwd, ".env.example"), filePath = existsSync(envPath) ? envPath : existsSync(envExamplePath) ? envExamplePath : void 0;
226
- if (!filePath)
227
- return;
228
- const match = readFileSync(filePath, "utf-8").match(/^DB_CONNECTION=(.+)$/m);
229
- if (!match)
230
- return;
231
- const value = match[1].trim().replace(/['"]/g, "");
232
- return pantryDatabasePackage(value);
233
- }
234
- export async function optimizePantryDeps() {
235
- const cwd = p.projectPath(), depsConfigPath = join(cwd, "config", "deps.ts");
236
- if (!existsSync(depsConfigPath)) {
237
- log.debug("No config/deps.ts found, skipping dependency optimization");
238
- return;
239
- }
240
- let configDeps = {}, configServices = [];
241
- try {
242
- const mod = await import(depsConfigPath), config = mod.config || mod.default;
243
- if (config?.dependencies)
244
- configDeps = { ...config.dependencies };
245
- if (Array.isArray(config?.services?.autoStart))
246
- configServices = config.services.autoStart.filter((name) => typeof name === "string");
247
- } catch (err) {
248
- log.debug("Could not load config/deps.ts, skipping dependency optimization");
249
- return;
250
- }
251
- const dbPackage = detectDbPackage(cwd), autoStart = [...configServices];
252
- if (dbPackage) {
253
- const selected = new Set(Object.entries(DB_CONNECTION_PACKAGES).filter(([, pkg]) => pkg.name === dbPackage.name).flatMap(([connection, pkg]) => databaseAliases(connection, pkg))), unused = new Set(Object.entries(DB_CONNECTION_PACKAGES).flatMap(([connection, pkg]) => databaseAliases(connection, pkg)).filter((alias) => !selected.has(alias)));
254
- for (const pkg of Object.keys(configDeps)) {
255
- const domain = pkg.split("/")[0];
256
- if (unused.has(domain)) {
257
- log.info(`DB_CONNECTION selects ${dbPackage.name}, dropping unused ${pkg}`);
258
- delete configDeps[pkg];
259
- }
260
- }
261
- if (!Object.keys(configDeps).some((key) => {
262
- const domain = key.split("/")[0];
263
- return selected.has(domain);
264
- })) {
265
- log.info(`Detected DB_CONNECTION requires ${dbPackage.name}, adding to dependencies`);
266
- configDeps[dbPackage.name] = dbPackage.version;
267
- }
268
- if (dbPackage.service && !autoStart.includes(dbPackage.service))
269
- autoStart.push(dbPackage.service);
270
- }
271
- const lines = [
272
- "# Auto-generated from config/deps.ts and .env sniffing.",
273
- "# This file is regenerated on each `buddy setup` run.",
274
- "#",
275
- "# To learn more, please visit:",
276
- "# https://stacksjs.com/docs/dependency-management",
277
- "",
278
- "dependencies:"
279
- ];
280
- for (const [pkg, version] of Object.entries(configDeps))
281
- lines.push(` ${pkg}: ${version}`);
282
- if (autoStart.length > 0) {
283
- lines.push("", "services:", " enabled: true", " autoStart:");
284
- for (const service of autoStart)
285
- lines.push(` - ${service}`);
286
- }
287
- const depsYamlPath = join(cwd, "deps.yaml");
288
- writeFileSync(depsYamlPath, `${lines.join(`
1
+ import{cpSync,existsSync,readFileSync,writeFileSync}from"node:fs";import{homedir}from"node:os";import{join}from"node:path";import process from"node:process";import{runAction}from"@stacksjs/actions";import{log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{handleError}from"@stacksjs/error-handling";import{path as p}from"@stacksjs/path";import{copyFile,storage}from"@stacksjs/storage";import{ExitCode}from"@stacksjs/types";import{setupPrettyDevEnvironment}from"./dev";function getTimeoutMs(envVar,fallbackMs){const value=Number(process.env[envVar]);if(Number.isFinite(value)&&value>0)return value;return fallbackMs}const PANTRY_CHECK_TIMEOUT_MS=getTimeoutMs("PANTRY_CHECK_TIMEOUT_MS",15000),PANTRY_INSTALL_TIMEOUT_MS=getTimeoutMs("PANTRY_INSTALL_TIMEOUT_MS",600000),PANTRY_DEPENDENCIES_TIMEOUT_MS=getTimeoutMs("PANTRY_DEPENDENCIES_TIMEOUT_MS",1200000),KEYGEN_TIMEOUT_MS=getTimeoutMs("KEYGEN_TIMEOUT_MS",120000),AWS_CONFIG_TIMEOUT_MS=getTimeoutMs("AWS_CONFIG_TIMEOUT_MS",900000);export function setup(buddy){const descriptions={setup:"This command ensures your project is setup correctly",ssl:"Setup SSL certificates and hosts file for HTTPS development",ai:"Set the project up for an AI coding agent (Claude Code, Codex, Cursor, Copilot, Gemini)",copy:"Copy the agent files instead of symlinking them, so they can be edited per project",force:"Overwrite files that already exist",ohMyZsh:"Enable Oh My Zsh",aws:"Ensures AWS is connected to the project",project:"Target a specific project",verbose:"Enable verbose output",domain:"Custom domain to setup (defaults to APP_URL)",skipHosts:"Skip adding domain to hosts file",skipTrust:"Skip trusting the certificate",skipAws:"Skip AWS configuration during setup",skipKeygen:"Skip generating an application key during setup"};buddy.command("setup",descriptions.setup).alias("ensure").option("-p, --project [project]",descriptions.project,{default:!1}).option("--skip-aws",descriptions.skipAws,{default:!1}).option("--skip-keygen",descriptions.skipKeygen,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy setup` ...",options);await ensurePantryInstalled();await optimizePantryDeps();await initializeProject(options)});buddy.command("setup:ssl",descriptions.ssl).alias("ssl:setup").option("-d, --domain [domain]",descriptions.domain).option("--skip-hosts",descriptions.skipHosts,{default:!1}).option("--skip-trust",descriptions.skipTrust,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy setup:ssl` ...",options);if(!await setupPrettyDevEnvironment({domain:options.domain,skipHosts:options.skipHosts,skipTrust:options.skipTrust,verbose:options.verbose})){log.warn("SSL setup completed with warnings");log.info("You may need to manually trust certificates or update hosts file")}});buddy.command("setup:ai [provider]",descriptions.ai).alias("ai:setup").option("--copy",descriptions.copy,{default:!1}).option("--force",descriptions.force,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(provider,options)=>{log.debug("Running `buddy setup:ai` ...",options);const{AI_PROVIDERS,isAiProvider,reportAiSetup,setupAiProvider}=await import("./setup-ai");let id=provider;if(!id){const{select}=await import("@stacksjs/cli");id=await select({message:"Which AI coding agent do you use?",choices:AI_PROVIDERS.map((entry)=>({value:entry.id,label:entry.label})),initial:0})}if(!id||!isAiProvider(id)){log.error(`Unknown AI provider: ${id}. Expected one of: ${AI_PROVIDERS.map((entry)=>entry.id).join(", ")}`);process.exit(ExitCode.InvalidArgument)}const definition=AI_PROVIDERS.find((entry)=>entry.id===id);reportAiSetup(definition,setupAiProvider(id,{copy:options.copy,force:options.force}))});buddy.command("setup:oh-my-zsh",descriptions.ohMyZsh).alias("upgrade:oh-my-zsh").option("--verbose",descriptions.verbose,{default:!1}).action(async(_options)=>{log.debug("Running `buddy setup:oh-my-zsh` ...",_options);const result=await runAction(Action.UpgradeShell);if(result.isErr){log.error(result.error);process.exit(ExitCode.FatalError)}});onUnknownSubcommand(buddy,"setup")}async function isPantryInstalled(){try{return(await runCommand("pantry --version",{silent:!0,timeoutMs:PANTRY_CHECK_TIMEOUT_MS})).isOk}catch{return!1}}async function installPantry(){const bundledInstaller=p.frameworkPath("scripts/pantry-install"),command=existsSync(bundledInstaller)?[bundledInstaller]:["sh","-c","curl -fsSL https://pantry.dev | bash"],result=await runCommand(command,{timeoutMs:PANTRY_INSTALL_TIMEOUT_MS}),localBin=join(homedir(),".local","bin");if(!process.env.PATH?.split(":").includes(localBin))process.env.PATH=`${localBin}:${process.env.PATH||""}`;if(result.isOk&&await isPantryInstalled())return;if(result.isErr)handleError(result.error);else log.error("Pantry installed but is not available on PATH. Open a new shell and run `buddy setup` again.");process.exit(ExitCode.FatalError)}export async function ensurePantryInstalled(){if(await isPantryInstalled())return;log.info("Pantry is required. Installing it from https://pantry.dev...");await installPantry()}export async function ensurePantryDependencies(cwd){await ensurePantryInstalled();log.info("Installing project dependencies with Pantry...");const result=await runCommand("pantry install",{cwd,timeoutMs:PANTRY_DEPENDENCIES_TIMEOUT_MS});if(result.isErr){handleError(result.error);process.exit(ExitCode.FatalError)}if(existsSync(join(cwd,"package.json"))&&!existsSync(join(cwd,"node_modules"))){log.error("Pantry completed without installing the project JavaScript dependencies.");process.exit(ExitCode.FatalError)}log.success("Installed project dependencies with Pantry")}function hasAppKey(cwd){const envPath=join(cwd,".env");if(!existsSync(envPath))return!1;return/^APP_KEY=.+$/m.test(readFileSync(envPath,"utf-8"))}export async function ensureAppKey(cwd){if(hasAppKey(cwd)||process.env.APP_KEY&&process.env.APP_KEY.length>0){log.success("APP_KEY existed");return}const keyResult=await runCommand("./buddy key:generate",{cwd,timeoutMs:KEYGEN_TIMEOUT_MS});if(keyResult.isErr){handleError(keyResult.error);process.exit(ExitCode.FatalError)}log.success("Generated application key")}async function runInitialMigration(cwd){const appEnv=(process.env.APP_ENV||process.env.NODE_ENV||"local").toLowerCase();if(!["local","development","dev","test"].includes(appEnv)){log.info(`Skipping initial migration in the ${appEnv} environment`);return}log.info("Running initial database migration...");try{const result=await runAction(Action.Migrate,{cwd});if(result.isErr){log.warn("Initial migration did not complete - you can run it later via ./buddy migrate");log.debug(result.error);return}log.success("Database is migrated")}catch(error){log.warn("Initial migration did not complete - you can run it later via ./buddy migrate");log.debug(error)}}async function initializeProject(options){const cwd=options.cwd||p.projectPath();await ensurePantryDependencies(cwd);await ensureEnvIsSet(options);if(!options.skipKeygen)await ensureAppKey(cwd);await runInitialMigration(cwd);ensureIdeSettings(cwd);if(!options.skipAws){log.info("Ensuring AWS is connected...");try{const awsResult=await runCommand("./buddy configure:aws",{cwd,timeoutMs:AWS_CONFIG_TIMEOUT_MS});if(awsResult.isErr){log.warn("AWS not configured - you can do this later via ./buddy configure:aws");log.debug(awsResult.error)}else log.success("Configured AWS")}catch(error){log.warn("AWS not configured - you can do this later via ./buddy configure:aws");log.debug(error)}}log.success("Project is setup");log.info("Run `./buddy doctor` anytime to check your setup. Happy coding! \uD83D\uDC99")}export function ensureIdeSettings(cwd){const source=p.frameworkPath("defaults/ide/vscode/.vscode"),destination=join(cwd,".vscode");if(existsSync(destination)){log.debug(".vscode already exists; keeping the project settings");return}if(!existsSync(source)){log.debug("No bundled VS Code settings found; skipping IDE setup");return}cpSync(source,destination,{recursive:!0});log.success("Installed project VS Code settings")}const DB_CONNECTION_PACKAGES={postgres:{name:"postgresql.org",version:"^17.10",service:"postgres"},mysql:{name:"mysql.com",version:"*",service:"mysql"},sqlite:{name:"sqlite.org",version:"^3.47.2"}};function databaseAliases(connection,pkg){return[connection,pkg.name]}export function pantryDatabasePackage(connection){return DB_CONNECTION_PACKAGES[connection]}function detectDbPackage(cwd){const envPath=join(cwd,".env"),envExamplePath=join(cwd,".env.example"),filePath=existsSync(envPath)?envPath:existsSync(envExamplePath)?envExamplePath:void 0;if(!filePath)return;const match=readFileSync(filePath,"utf-8").match(/^DB_CONNECTION=(.+)$/m);if(!match)return;const value=match[1].trim().replace(/['"]/g,"");return pantryDatabasePackage(value)}export async function optimizePantryDeps(){const cwd=p.projectPath(),depsConfigPath=join(cwd,"config","deps.ts");if(!existsSync(depsConfigPath)){log.debug("No config/deps.ts found, skipping dependency optimization");return}let configDeps={},configServices=[];try{const mod=await import(depsConfigPath),config=mod.config||mod.default;if(config?.dependencies)configDeps={...config.dependencies};if(Array.isArray(config?.services?.autoStart))configServices=config.services.autoStart.filter((name)=>typeof name==="string")}catch(err){log.debug("Could not load config/deps.ts, skipping dependency optimization");return}const dbPackage=detectDbPackage(cwd),autoStart=[...configServices];if(dbPackage){const selected=new Set(Object.entries(DB_CONNECTION_PACKAGES).filter(([,pkg])=>pkg.name===dbPackage.name).flatMap(([connection,pkg])=>databaseAliases(connection,pkg))),unused=new Set(Object.entries(DB_CONNECTION_PACKAGES).flatMap(([connection,pkg])=>databaseAliases(connection,pkg)).filter((alias)=>!selected.has(alias)));for(const pkg of Object.keys(configDeps)){const domain=pkg.split("/")[0];if(unused.has(domain)){log.info(`DB_CONNECTION selects ${dbPackage.name}, dropping unused ${pkg}`);delete configDeps[pkg]}}if(!Object.keys(configDeps).some((key)=>{const domain=key.split("/")[0];return selected.has(domain)})){log.info(`Detected DB_CONNECTION requires ${dbPackage.name}, adding to dependencies`);configDeps[dbPackage.name]=dbPackage.version}if(dbPackage.service&&!autoStart.includes(dbPackage.service))autoStart.push(dbPackage.service)}const lines=["# Auto-generated from config/deps.ts and .env sniffing.","# This file is regenerated on each `buddy setup` run.","#","# To learn more, please visit:","# https://stacksjs.com/docs/dependency-management","","dependencies:"];for(const[pkg,version]of Object.entries(configDeps))lines.push(` ${pkg}: ${version}`);if(autoStart.length>0){lines.push("","services:"," enabled: true"," autoStart:");for(const service of autoStart)lines.push(` - ${service}`)}const depsYamlPath=join(cwd,"deps.yaml");writeFileSync(depsYamlPath,`${lines.join(`
289
2
  `)}
290
- `);
291
- log.success("Generated deps.yaml from config/deps.ts");
292
- }
293
- export async function ensureEnvIsSet(options) {
294
- log.info("Ensuring .env exists...");
295
- const cwd = options.cwd || p.projectPath(), envPath = `${cwd}/.env`, envExamplePath = `${cwd}/.env.example`;
296
- if (storage.doesNotExist(envPath)) {
297
- try {
298
- copyFile(envExamplePath, envPath);
299
- } catch (error) {
300
- handleError(error);
301
- process.exit(ExitCode.FatalError);
302
- }
303
- log.success(".env created");
304
- } else
305
- log.success(".env existed");
306
- }
3
+ `);log.success("Generated deps.yaml from config/deps.ts")}export async function ensureEnvIsSet(options){log.info("Ensuring .env exists...");const cwd=options.cwd||p.projectPath(),envPath=`${cwd}/.env`,envExamplePath=`${cwd}/.env.example`;if(storage.doesNotExist(envPath)){try{copyFile(envExamplePath,envPath)}catch(error){handleError(error);process.exit(ExitCode.FatalError)}log.success(".env created")}else log.success(".env existed")}