@take-out/cli 0.4.3 → 0.4.5

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 (69) hide show
  1. package/dist/cjs/cli.cjs +53 -35
  2. package/dist/cjs/commands/changed.cjs +133 -95
  3. package/dist/cjs/commands/docs.cjs +276 -203
  4. package/dist/cjs/commands/env-setup.cjs +45 -33
  5. package/dist/cjs/commands/onboard.cjs +668 -224
  6. package/dist/cjs/commands/run-all.cjs +54 -45
  7. package/dist/cjs/commands/run.cjs +80 -65
  8. package/dist/cjs/commands/script.cjs +263 -187
  9. package/dist/cjs/commands/skills.cjs +174 -118
  10. package/dist/cjs/commands/sync.cjs +193 -93
  11. package/dist/cjs/constants/ascii.cjs +14 -12
  12. package/dist/cjs/index.cjs +12 -10
  13. package/dist/cjs/types.cjs +7 -5
  14. package/dist/cjs/utils/env-categories.cjs +53 -48
  15. package/dist/cjs/utils/env-setup.cjs +214 -106
  16. package/dist/cjs/utils/env.cjs +65 -44
  17. package/dist/cjs/utils/files.cjs +186 -113
  18. package/dist/cjs/utils/parallel-runner.cjs +125 -75
  19. package/dist/cjs/utils/ports.cjs +21 -21
  20. package/dist/cjs/utils/prerequisites.cjs +42 -34
  21. package/dist/cjs/utils/prompts.cjs +66 -41
  22. package/dist/cjs/utils/script-listing.cjs +130 -60
  23. package/dist/cjs/utils/script-utils.cjs +29 -19
  24. package/dist/cjs/utils/sync.cjs +67 -45
  25. package/dist/esm/cli.mjs +40 -24
  26. package/dist/esm/cli.mjs.map +1 -1
  27. package/dist/esm/commands/changed.mjs +104 -68
  28. package/dist/esm/commands/changed.mjs.map +1 -1
  29. package/dist/esm/commands/docs.mjs +245 -174
  30. package/dist/esm/commands/docs.mjs.map +1 -1
  31. package/dist/esm/commands/env-setup.mjs +18 -8
  32. package/dist/esm/commands/env-setup.mjs.map +1 -1
  33. package/dist/esm/commands/onboard.mjs +631 -189
  34. package/dist/esm/commands/onboard.mjs.map +1 -1
  35. package/dist/esm/commands/run-all.mjs +26 -19
  36. package/dist/esm/commands/run-all.mjs.map +1 -1
  37. package/dist/esm/commands/run.mjs +52 -39
  38. package/dist/esm/commands/run.mjs.map +1 -1
  39. package/dist/esm/commands/script.mjs +230 -156
  40. package/dist/esm/commands/script.mjs.map +1 -1
  41. package/dist/esm/commands/skills.mjs +143 -89
  42. package/dist/esm/commands/skills.mjs.map +1 -1
  43. package/dist/esm/commands/sync.mjs +160 -62
  44. package/dist/esm/commands/sync.mjs.map +1 -1
  45. package/dist/esm/constants/ascii.mjs +2 -2
  46. package/dist/esm/constants/ascii.mjs.map +1 -1
  47. package/dist/esm/utils/env-categories.mjs +29 -26
  48. package/dist/esm/utils/env-categories.mjs.map +1 -1
  49. package/dist/esm/utils/env-setup.mjs +184 -78
  50. package/dist/esm/utils/env-setup.mjs.map +1 -1
  51. package/dist/esm/utils/env.mjs +50 -31
  52. package/dist/esm/utils/env.mjs.map +1 -1
  53. package/dist/esm/utils/files.mjs +171 -100
  54. package/dist/esm/utils/files.mjs.map +1 -1
  55. package/dist/esm/utils/parallel-runner.mjs +111 -63
  56. package/dist/esm/utils/parallel-runner.mjs.map +1 -1
  57. package/dist/esm/utils/ports.mjs +9 -11
  58. package/dist/esm/utils/ports.mjs.map +1 -1
  59. package/dist/esm/utils/prerequisites.mjs +30 -24
  60. package/dist/esm/utils/prerequisites.mjs.map +1 -1
  61. package/dist/esm/utils/prompts.mjs +40 -17
  62. package/dist/esm/utils/prompts.mjs.map +1 -1
  63. package/dist/esm/utils/script-listing.mjs +101 -33
  64. package/dist/esm/utils/script-listing.mjs.map +1 -1
  65. package/dist/esm/utils/script-utils.mjs +14 -6
  66. package/dist/esm/utils/script-utils.mjs.map +1 -1
  67. package/dist/esm/utils/sync.mjs +38 -18
  68. package/dist/esm/utils/sync.mjs.map +1 -1
  69. package/package.json +5 -5
@@ -2,51 +2,53 @@ var __create = Object.create;
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf,
6
- __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: !0
11
- });
12
- },
13
- __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from == "object" || typeof from == "function") for (let key of __getOwnPropNames(from)) !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
15
16
  get: () => from[key],
16
17
  enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
18
  });
18
- return to;
19
- };
19
+ }
20
+ return to;
21
+ };
20
22
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
26
- value: mod,
27
- enumerable: !0
28
- }) : target, mod)),
29
- __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
30
- value: !0
31
- }), mod);
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
32
+ value: true
33
+ }), mod);
32
34
  var onboard_exports = {};
33
35
  __export(onboard_exports, {
34
36
  onboardCommand: () => onboardCommand
35
37
  });
36
38
  module.exports = __toCommonJS(onboard_exports);
37
- var import_node_child_process = require("node:child_process"),
38
- import_node_crypto = require("node:crypto"),
39
- import_node_fs = require("node:fs"),
40
- import_node_os = require("node:os"),
41
- import_node_path = require("node:path"),
42
- import_citty = require("citty"),
43
- import_picocolors = __toESM(require("picocolors"), 1),
44
- import_env = require("../utils/env.cjs"),
45
- import_env_setup = require("../utils/env-setup.cjs"),
46
- import_files = require("../utils/files.cjs"),
47
- import_ports = require("../utils/ports.cjs"),
48
- import_prerequisites = require("../utils/prerequisites.cjs"),
49
- import_prompts = require("../utils/prompts.cjs");
39
+ var import_node_child_process = require("node:child_process");
40
+ var import_node_crypto = require("node:crypto");
41
+ var import_node_fs = require("node:fs");
42
+ var import_node_os = require("node:os");
43
+ var import_node_path = require("node:path");
44
+ var import_citty = require("citty");
45
+ var import_picocolors = __toESM(require("picocolors"), 1);
46
+ var import_env = require("../utils/env.cjs");
47
+ var import_env_setup = require("../utils/env-setup.cjs");
48
+ var import_files = require("../utils/files.cjs");
49
+ var import_ports = require("../utils/ports.cjs");
50
+ var import_prerequisites = require("../utils/prerequisites.cjs");
51
+ var import_prompts = require("../utils/prompts.cjs");
50
52
  const onboardCommand = (0, import_citty.defineCommand)({
51
53
  meta: {
52
54
  name: "onboard",
@@ -56,12 +58,12 @@ const onboardCommand = (0, import_citty.defineCommand)({
56
58
  skip: {
57
59
  type: "boolean",
58
60
  description: "Skip onboarding entirely",
59
- default: !1
61
+ default: false
60
62
  },
61
63
  defaults: {
62
64
  type: "boolean",
63
65
  description: "Run with defaults (non-interactive)",
64
- default: !1
66
+ default: false
65
67
  }
66
68
  },
67
69
  async run({
@@ -73,147 +75,348 @@ const onboardCommand = (0, import_citty.defineCommand)({
73
75
  return;
74
76
  }
75
77
  if (args.defaults) {
76
- if ((0, import_prompts.showInfo)("Running onboarding with defaults (--defaults flag)"), console.info(), (0, import_env.envFileExists)(cwd, ".env")) (0, import_prompts.showInfo)(".env already exists, skipping");else {
78
+ (0, import_prompts.showInfo)("Running onboarding with defaults (--defaults flag)");
79
+ console.info();
80
+ const hasEnv = (0, import_env.envFileExists)(cwd, ".env");
81
+ if (!hasEnv) {
77
82
  const copyResult = (0, import_env.copyEnvFile)(cwd, ".env.development", ".env");
78
- if (copyResult.success) (0, import_prompts.showSuccess)("Created .env from .env.development");else {
83
+ if (copyResult.success) {
84
+ (0, import_prompts.showSuccess)("Created .env from .env.development");
85
+ } else {
79
86
  (0, import_prompts.showError)(`Failed to create .env: ${copyResult.error}`);
80
87
  return;
81
88
  }
82
- (0, import_env.createEnvLocal)(cwd), (0, import_prompts.showSuccess)("Created .env.local for personal overrides");
89
+ (0, import_env.createEnvLocal)(cwd);
90
+ (0, import_prompts.showSuccess)("Created .env.local for personal overrides");
91
+ } else {
92
+ (0, import_prompts.showInfo)(".env already exists, skipping");
83
93
  }
84
- (0, import_files.markOnboarded)(cwd), (0, import_prompts.showSuccess)("Onboarding complete with defaults"), console.info(), (0, import_prompts.showInfo)("Next steps:"), console.info(" bun backend # start docker services"), console.info(" bun dev # start web dev server");
94
+ (0, import_files.markOnboarded)(cwd);
95
+ (0, import_prompts.showSuccess)("Onboarding complete with defaults");
96
+ console.info();
97
+ (0, import_prompts.showInfo)("Next steps:");
98
+ console.info(" bun backend # start docker services");
99
+ console.info(" bun dev # start web dev server");
85
100
  return;
86
101
  }
87
102
  (0, import_prompts.displayWelcome)();
88
103
  const savedState = loadOnboardState(cwd);
89
104
  let startStep;
90
- if (savedState ? (console.info(), (0, import_prompts.showInfo)(`Found incomplete setup from previous session (${savedState.step})`), (await (0, import_prompts.confirmContinue)("Resume from where you left off?", !0)) ? startStep = savedState.step : (clearOnboardState(cwd), startStep = await (0, import_prompts.promptStartStep)())) : startStep = await (0, import_prompts.promptStartStep)(), startStep === "cancel") {
105
+ if (savedState) {
106
+ console.info();
107
+ (0, import_prompts.showInfo)(`Found incomplete setup from previous session (${savedState.step})`);
108
+ const shouldResume = await (0, import_prompts.confirmContinue)("Resume from where you left off?", true);
109
+ if (shouldResume) {
110
+ startStep = savedState.step;
111
+ } else {
112
+ clearOnboardState(cwd);
113
+ startStep = await (0, import_prompts.promptStartStep)();
114
+ }
115
+ } else {
116
+ startStep = await (0, import_prompts.promptStartStep)();
117
+ }
118
+ if (startStep === "cancel") {
91
119
  (0, import_prompts.displayOutro)("Setup cancelled");
92
120
  return;
93
121
  }
94
122
  if (startStep === "eject") {
95
- if ((0, import_prompts.showStep)("Monorepo Ejection"), console.info(), console.info(import_picocolors.default.gray("We've included a variety of packages we found useful building apps with this stack:")), console.info(import_picocolors.default.gray(" \u2022 @take-out/cli - CLI tools and onboarding")), console.info(import_picocolors.default.gray(" \u2022 @take-out/helpers - Utility functions")), console.info(import_picocolors.default.gray(" \u2022 @take-out/hooks - React hooks")), console.info(import_picocolors.default.gray(" \u2022 @take-out/database - Database utilities")), console.info(import_picocolors.default.gray(" \u2022 @take-out/scripts - Build and dev scripts")), console.info(import_picocolors.default.gray(" \u2022 @take-out/better-auth-utils - Auth helpers")), console.info(), await (0, import_prompts.confirmContinue)("Eject from monorepo setup? (removes ./packages, uses published versions)", !0)) {
123
+ (0, import_prompts.showStep)("Monorepo Ejection");
124
+ console.info();
125
+ console.info(import_picocolors.default.gray("We've included a variety of packages we found useful building apps with this stack:"));
126
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/cli - CLI tools and onboarding"));
127
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/helpers - Utility functions"));
128
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/hooks - React hooks"));
129
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/database - Database utilities"));
130
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/scripts - Build and dev scripts"));
131
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/better-auth-utils - Auth helpers"));
132
+ console.info();
133
+ const shouldEject = await (0, import_prompts.confirmContinue)("Eject from monorepo setup? (removes ./packages, uses published versions)", true);
134
+ if (shouldEject) {
96
135
  const dryResult = await (0, import_files.ejectFromMonorepo)(cwd, {
97
- dryRun: !0
136
+ dryRun: true
98
137
  });
99
138
  if (!dryResult.success) {
100
- (0, import_prompts.showError)(`Cannot eject: ${dryResult.error}`), clearOnboardState(cwd);
139
+ (0, import_prompts.showError)(`Cannot eject: ${dryResult.error}`);
140
+ clearOnboardState(cwd);
101
141
  return;
102
142
  }
103
- console.info(), (0, import_prompts.showInfo)(`Found ${dryResult.packages?.length} packages to convert:`);
104
- for (const pkg of dryResult.packages || []) console.info(import_picocolors.default.gray(` \u2022 ${pkg.name}@${pkg.version}`));
105
- if (dryResult.warnings?.length) for (const warn of dryResult.warnings) (0, import_prompts.showWarning)(warn);
106
- if (console.info(), !(await (0, import_prompts.confirmContinue)("Proceed with ejection?", !0))) {
107
- (0, import_prompts.showInfo)("Eject cancelled"), clearOnboardState(cwd), (0, import_prompts.displayOutro)("Done!");
143
+ console.info();
144
+ (0, import_prompts.showInfo)(`Found ${dryResult.packages?.length} packages to convert:`);
145
+ for (const pkg of dryResult.packages || []) {
146
+ console.info(import_picocolors.default.gray(` \u2022 ${pkg.name}@${pkg.version}`));
147
+ }
148
+ if (dryResult.warnings?.length) {
149
+ for (const warn of dryResult.warnings) {
150
+ (0, import_prompts.showWarning)(warn);
151
+ }
152
+ }
153
+ console.info();
154
+ const confirmEject = await (0, import_prompts.confirmContinue)("Proceed with ejection?", true);
155
+ if (!confirmEject) {
156
+ (0, import_prompts.showInfo)("Eject cancelled");
157
+ clearOnboardState(cwd);
158
+ (0, import_prompts.displayOutro)("Done!");
108
159
  return;
109
160
  }
110
- const spinner = (0, import_prompts.showSpinner)("Ejecting from monorepo..."),
111
- result = await (0, import_files.ejectFromMonorepo)(cwd);
161
+ const spinner = (0, import_prompts.showSpinner)("Ejecting from monorepo...");
162
+ const result = await (0, import_files.ejectFromMonorepo)(cwd);
112
163
  if (result.success) {
113
- spinner.stop("Ejected from monorepo"), (0, import_prompts.showSuccess)("\u2713 Removed ./packages directory"), (0, import_prompts.showSuccess)("\u2713 Updated package.json to use published versions"), (0, import_prompts.showSuccess)("\u2713 Installed published packages");
164
+ spinner.stop("Ejected from monorepo");
165
+ (0, import_prompts.showSuccess)("\u2713 Removed ./packages directory");
166
+ (0, import_prompts.showSuccess)("\u2713 Updated package.json to use published versions");
167
+ (0, import_prompts.showSuccess)("\u2713 Installed published packages");
114
168
  try {
115
169
  (0, import_node_child_process.execSync)('git add -A && git commit -m "ejected from monorepo"', {
116
170
  cwd,
117
171
  stdio: "ignore"
118
- }), (0, import_prompts.showSuccess)("\u2713 Committed eject changes");
172
+ });
173
+ (0, import_prompts.showSuccess)("\u2713 Committed eject changes");
119
174
  } catch {
120
175
  (0, import_prompts.showWarning)("Git commit skipped (not a git repo or no changes)");
121
176
  }
122
- console.info(), (0, import_prompts.showInfo)("You can now upgrade packages with: bun up takeout");
123
- } else spinner.stop("Ejection failed"), (0, import_prompts.showError)(`Failed to eject: ${result.error}`), result.error?.includes("install failed") && ((0, import_prompts.showInfo)('You may be able to fix this by running "bun install" manually'), (0, import_prompts.showInfo)('Or restore the repo from git with "git checkout ."'));
124
- } else (0, import_prompts.showInfo)("Eject cancelled");
125
- clearOnboardState(cwd), (0, import_prompts.displayOutro)("Done!");
177
+ console.info();
178
+ (0, import_prompts.showInfo)("You can now upgrade packages with: bun up takeout");
179
+ } else {
180
+ spinner.stop("Ejection failed");
181
+ (0, import_prompts.showError)(`Failed to eject: ${result.error}`);
182
+ if (result.error?.includes("install failed")) {
183
+ (0, import_prompts.showInfo)('You may be able to fix this by running "bun install" manually');
184
+ (0, import_prompts.showInfo)('Or restore the repo from git with "git checkout ."');
185
+ }
186
+ }
187
+ } else {
188
+ (0, import_prompts.showInfo)("Eject cancelled");
189
+ }
190
+ clearOnboardState(cwd);
191
+ (0, import_prompts.displayOutro)("Done!");
126
192
  return;
127
193
  }
128
194
  if (startStep === "prerequisites" || startStep === "full") {
129
195
  saveOnboardState(cwd, {
130
196
  step: "prerequisites",
131
197
  timestamp: Date.now()
132
- }), (0, import_prompts.showStep)("Checking prerequisites..."), console.info();
198
+ });
199
+ (0, import_prompts.showStep)("Checking prerequisites...");
200
+ console.info();
133
201
  const checks = (0, import_prerequisites.checkAllPrerequisites)();
134
- if ((0, import_prompts.displayPrerequisites)(checks), !(0, import_prerequisites.hasRequiredPrerequisites)(checks) && ((0, import_prompts.showWarning)("Some required prerequisites are missing. You can continue, but setup may fail."), !(await (0, import_prompts.confirmContinue)("Continue anyway?", !1)))) {
135
- (0, import_prompts.displayOutro)("Setup cancelled. Install prerequisites and try again.");
136
- return;
202
+ (0, import_prompts.displayPrerequisites)(checks);
203
+ const hasRequired = (0, import_prerequisites.hasRequiredPrerequisites)(checks);
204
+ if (!hasRequired) {
205
+ (0, import_prompts.showWarning)("Some required prerequisites are missing. You can continue, but setup may fail.");
206
+ const shouldContinue = await (0, import_prompts.confirmContinue)("Continue anyway?", false);
207
+ if (!shouldContinue) {
208
+ (0, import_prompts.displayOutro)("Setup cancelled. Install prerequisites and try again.");
209
+ return;
210
+ }
137
211
  }
138
212
  console.info();
139
213
  }
140
- if ((startStep === "prerequisites" || startStep === "identity" || startStep === "full") && (saveOnboardState(cwd, {
141
- step: "identity",
142
- timestamp: Date.now()
143
- }), (0, import_prompts.showStep)("Configuring project identity..."), console.info(), (await (0, import_prompts.confirmContinue)("Customize project name and bundle identifier?", !1)) ? await customizeProject(cwd) : (0, import_prompts.showInfo)("Keeping default project configuration"), console.info()), startStep === "prerequisites" || startStep === "identity" || startStep === "ports" || startStep === "full") {
144
- if (saveOnboardState(cwd, {
214
+ if (startStep === "prerequisites" || startStep === "identity" || startStep === "full") {
215
+ saveOnboardState(cwd, {
216
+ step: "identity",
217
+ timestamp: Date.now()
218
+ });
219
+ (0, import_prompts.showStep)("Configuring project identity...");
220
+ console.info();
221
+ const shouldCustomize = await (0, import_prompts.confirmContinue)("Customize project name and bundle identifier?", false);
222
+ if (shouldCustomize) {
223
+ await customizeProject(cwd);
224
+ } else {
225
+ (0, import_prompts.showInfo)("Keeping default project configuration");
226
+ }
227
+ console.info();
228
+ }
229
+ if (startStep === "prerequisites" || startStep === "identity" || startStep === "ports" || startStep === "full") {
230
+ saveOnboardState(cwd, {
145
231
  step: "ports",
146
232
  timestamp: Date.now()
147
- }), (0, import_prompts.showStep)("Configuring web server port..."), console.info(), (0, import_prompts.showInfo)("Default web port: 8081 (TAMA in T9)"), await (0, import_prompts.confirmContinue)("Customize web server port?", !1)) {
233
+ });
234
+ (0, import_prompts.showStep)("Configuring web server port...");
235
+ console.info();
236
+ (0, import_prompts.showInfo)("Default web port: 8081 (TAMA in T9)");
237
+ const shouldCustomizePort = await (0, import_prompts.confirmContinue)("Customize web server port?", false);
238
+ if (shouldCustomizePort) {
148
239
  const newPort = await (0, import_prompts.promptText)("Web server port:", "8081", "3000, 8080, 8081, etc.");
149
- newPort && newPort !== "8081" && (await replacePortInProject(cwd, "8081", newPort), (0, import_prompts.showSuccess)(`\u2713 Updated web server port to ${newPort}`));
150
- } else (0, import_prompts.showInfo)("Keeping default port 8081");
151
- console.info(), (0, import_prompts.showStep)("Checking service ports..."), console.info();
152
- const portChecks = (0, import_ports.checkAllPorts)(),
153
- conflicts = (0, import_ports.getConflictingPorts)(portChecks);
154
- conflicts.length > 0 && ((0, import_prompts.displayPortConflicts)(conflicts), (0, import_prompts.showWarning)("Some ports are already in use. You may need to stop other services.")), console.info();
240
+ if (newPort && newPort !== "8081") {
241
+ await replacePortInProject(cwd, "8081", newPort);
242
+ (0, import_prompts.showSuccess)(`\u2713 Updated web server port to ${newPort}`);
243
+ }
244
+ } else {
245
+ (0, import_prompts.showInfo)("Keeping default port 8081");
246
+ }
247
+ console.info();
248
+ (0, import_prompts.showStep)("Checking service ports...");
249
+ console.info();
250
+ const portChecks = (0, import_ports.checkAllPorts)();
251
+ const conflicts = (0, import_ports.getConflictingPorts)(portChecks);
252
+ if (conflicts.length > 0) {
253
+ (0, import_prompts.displayPortConflicts)(conflicts);
254
+ (0, import_prompts.showWarning)("Some ports are already in use. You may need to stop other services.");
255
+ }
256
+ console.info();
257
+ }
258
+ if (startStep === "prerequisites" || startStep === "identity" || startStep === "ports" || startStep === "full") {
259
+ saveOnboardState(cwd, {
260
+ step: "ci-runners",
261
+ timestamp: Date.now()
262
+ });
263
+ (0, import_prompts.showStep)("Configuring CI/CD runners...");
264
+ console.info();
265
+ const shouldConfigureCI = await (0, import_prompts.confirmContinue)("Configure GitHub Actions CI runners?", true);
266
+ if (shouldConfigureCI) {
267
+ await configureCIRunners(cwd);
268
+ } else {
269
+ (0, import_prompts.showInfo)("Skipping CI runner configuration");
270
+ (0, import_prompts.showWarning)("Default CI uses ARM64 Docker builds. If not using ARM runners, update scripts/web/build-docker.ts");
271
+ }
272
+ console.info();
273
+ }
274
+ if (startStep !== "production") {
275
+ (0, import_prompts.showStep)("Development setup complete!");
276
+ console.info();
277
+ (0, import_prompts.showSuccess)("\u2713 Environment configured");
278
+ (0, import_prompts.showSuccess)("\u2713 Project ready for development");
279
+ (0, import_files.markOnboarded)(cwd);
280
+ console.info();
281
+ (0, import_prompts.showInfo)("Next steps (run in separate terminals):");
282
+ console.info();
283
+ console.info(" bun backend # start docker services first");
284
+ console.info(" bun dev # start web dev server");
285
+ console.info();
286
+ console.info(" bun ios # build iOS dev app");
287
+ console.info(" bun android # build Android dev app");
288
+ console.info(" bun tko docs list # view Takeout docs");
289
+ console.info();
155
290
  }
156
- if ((startStep === "prerequisites" || startStep === "identity" || startStep === "ports" || startStep === "full") && (saveOnboardState(cwd, {
157
- step: "ci-runners",
158
- timestamp: Date.now()
159
- }), (0, import_prompts.showStep)("Configuring CI/CD runners..."), console.info(), (await (0, import_prompts.confirmContinue)("Configure GitHub Actions CI runners?", !0)) ? await configureCIRunners(cwd) : ((0, import_prompts.showInfo)("Skipping CI runner configuration"), (0, import_prompts.showWarning)("Default CI uses ARM64 Docker builds. If not using ARM runners, update scripts/web/build-docker.ts")), console.info()), startStep !== "production" && ((0, import_prompts.showStep)("Development setup complete!"), console.info(), (0, import_prompts.showSuccess)("\u2713 Environment configured"), (0, import_prompts.showSuccess)("\u2713 Project ready for development"), (0, import_files.markOnboarded)(cwd), console.info(), (0, import_prompts.showInfo)("Next steps (run in separate terminals):"), console.info(), console.info(" bun backend # start docker services first"), console.info(" bun dev # start web dev server"), console.info(), console.info(" bun ios # build iOS dev app"), console.info(" bun android # build Android dev app"), console.info(" bun tko docs list # view Takeout docs"), console.info()), startStep !== "production") {
160
- if (console.info(), (0, import_prompts.showStep)("Monorepo Setup"), console.info(), console.info(import_picocolors.default.gray("We've included a variety of packages we found useful building apps with this stack:")), console.info(import_picocolors.default.gray(" \u2022 @take-out/cli - CLI tools and onboarding")), console.info(import_picocolors.default.gray(" \u2022 @take-out/helpers - Utility functions")), console.info(import_picocolors.default.gray(" \u2022 @take-out/hooks - React hooks")), console.info(import_picocolors.default.gray(" \u2022 @take-out/database - Database utilities")), console.info(import_picocolors.default.gray(" \u2022 @take-out/scripts - Build and dev scripts")), console.info(import_picocolors.default.gray(" \u2022 @take-out/better-auth-utils - Auth helpers")), console.info(), console.info(import_picocolors.default.gray("These packages are included locally for two reasons:")), console.info(import_picocolors.default.gray(" 1. You can see their source and decide if you want to keep them")), console.info(import_picocolors.default.gray(" 2. Anyone can easily submit fixes or improvements back to our repo")), console.info(), console.info(import_picocolors.default.gray("Over time we'll publish new versions. You can sync with 'bun tko sync' (monorepo)")), console.info(import_picocolors.default.gray("or if you eject, use 'bun up takeout' for package updates.")), console.info(), await (0, import_prompts.confirmContinue)("Eject from monorepo setup? (removes ./packages, uses published versions)", !1)) {
291
+ if (startStep !== "production") {
292
+ console.info();
293
+ (0, import_prompts.showStep)("Monorepo Setup");
294
+ console.info();
295
+ console.info(import_picocolors.default.gray("We've included a variety of packages we found useful building apps with this stack:"));
296
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/cli - CLI tools and onboarding"));
297
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/helpers - Utility functions"));
298
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/hooks - React hooks"));
299
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/database - Database utilities"));
300
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/scripts - Build and dev scripts"));
301
+ console.info(import_picocolors.default.gray(" \u2022 @take-out/better-auth-utils - Auth helpers"));
302
+ console.info();
303
+ console.info(import_picocolors.default.gray("These packages are included locally for two reasons:"));
304
+ console.info(import_picocolors.default.gray(" 1. You can see their source and decide if you want to keep them"));
305
+ console.info(import_picocolors.default.gray(" 2. Anyone can easily submit fixes or improvements back to our repo"));
306
+ console.info();
307
+ console.info(import_picocolors.default.gray("Over time we'll publish new versions. You can sync with 'bun tko sync' (monorepo)"));
308
+ console.info(import_picocolors.default.gray("or if you eject, use 'bun up takeout' for package updates."));
309
+ console.info();
310
+ const shouldEject = await (0, import_prompts.confirmContinue)("Eject from monorepo setup? (removes ./packages, uses published versions)", false);
311
+ if (shouldEject) {
161
312
  const dryResult = await (0, import_files.ejectFromMonorepo)(cwd, {
162
- dryRun: !0
313
+ dryRun: true
163
314
  });
164
- if (!dryResult.success) (0, import_prompts.showError)(`Cannot eject: ${dryResult.error}`);else {
165
- console.info(), (0, import_prompts.showInfo)(`Found ${dryResult.packages?.length} packages to convert:`);
166
- for (const pkg of dryResult.packages || []) console.info(import_picocolors.default.gray(` \u2022 ${pkg.name}@${pkg.version}`));
167
- if (dryResult.warnings?.length) for (const warn of dryResult.warnings) (0, import_prompts.showWarning)(warn);
168
- if (console.info(), await (0, import_prompts.confirmContinue)("Proceed with ejection?", !0)) {
169
- const spinner = (0, import_prompts.showSpinner)("Ejecting from monorepo..."),
170
- result = await (0, import_files.ejectFromMonorepo)(cwd);
315
+ if (!dryResult.success) {
316
+ (0, import_prompts.showError)(`Cannot eject: ${dryResult.error}`);
317
+ } else {
318
+ console.info();
319
+ (0, import_prompts.showInfo)(`Found ${dryResult.packages?.length} packages to convert:`);
320
+ for (const pkg of dryResult.packages || []) {
321
+ console.info(import_picocolors.default.gray(` \u2022 ${pkg.name}@${pkg.version}`));
322
+ }
323
+ if (dryResult.warnings?.length) {
324
+ for (const warn of dryResult.warnings) {
325
+ (0, import_prompts.showWarning)(warn);
326
+ }
327
+ }
328
+ console.info();
329
+ const confirmEject = await (0, import_prompts.confirmContinue)("Proceed with ejection?", true);
330
+ if (confirmEject) {
331
+ const spinner = (0, import_prompts.showSpinner)("Ejecting from monorepo...");
332
+ const result = await (0, import_files.ejectFromMonorepo)(cwd);
171
333
  if (result.success) {
172
- spinner.stop("Ejected from monorepo"), (0, import_prompts.showSuccess)("\u2713 Removed ./packages directory"), (0, import_prompts.showSuccess)("\u2713 Updated package.json to use published versions"), (0, import_prompts.showSuccess)("\u2713 Installed published packages");
334
+ spinner.stop("Ejected from monorepo");
335
+ (0, import_prompts.showSuccess)("\u2713 Removed ./packages directory");
336
+ (0, import_prompts.showSuccess)("\u2713 Updated package.json to use published versions");
337
+ (0, import_prompts.showSuccess)("\u2713 Installed published packages");
173
338
  try {
174
339
  (0, import_node_child_process.execSync)('git add -A && git commit -m "ejected from monorepo"', {
175
340
  cwd,
176
341
  stdio: "ignore"
177
- }), (0, import_prompts.showSuccess)("\u2713 Committed eject changes");
342
+ });
343
+ (0, import_prompts.showSuccess)("\u2713 Committed eject changes");
178
344
  } catch {
179
345
  (0, import_prompts.showWarning)("Git commit skipped (not a git repo or no changes)");
180
346
  }
181
- console.info(), (0, import_prompts.showInfo)("You can now upgrade packages with: bun up takeout");
182
- } else spinner.stop("Ejection failed"), (0, import_prompts.showError)(`Failed to eject: ${result.error}`), result.error?.includes("install failed") && ((0, import_prompts.showInfo)('You may be able to fix this by running "bun install" manually'), (0, import_prompts.showInfo)('Or restore the repo from git with "git checkout ."'));
183
- } else (0, import_prompts.showInfo)("Keeping monorepo setup - you can customize packages locally"), (0, import_prompts.showInfo)('Run "bun tko sync" to sync with upstream Takeout updates later');
347
+ console.info();
348
+ (0, import_prompts.showInfo)("You can now upgrade packages with: bun up takeout");
349
+ } else {
350
+ spinner.stop("Ejection failed");
351
+ (0, import_prompts.showError)(`Failed to eject: ${result.error}`);
352
+ if (result.error?.includes("install failed")) {
353
+ (0, import_prompts.showInfo)('You may be able to fix this by running "bun install" manually');
354
+ (0, import_prompts.showInfo)('Or restore the repo from git with "git checkout ."');
355
+ }
356
+ }
357
+ } else {
358
+ (0, import_prompts.showInfo)("Keeping monorepo setup - you can customize packages locally");
359
+ (0, import_prompts.showInfo)('Run "bun tko sync" to sync with upstream Takeout updates later');
360
+ }
184
361
  }
185
- } else (0, import_prompts.showInfo)("Keeping monorepo setup - you can customize packages locally"), (0, import_prompts.showInfo)('Run "bun tko sync" to sync with upstream Takeout updates later');
362
+ } else {
363
+ (0, import_prompts.showInfo)("Keeping monorepo setup - you can customize packages locally");
364
+ (0, import_prompts.showInfo)('Run "bun tko sync" to sync with upstream Takeout updates later');
365
+ }
186
366
  console.info();
187
367
  }
188
368
  if (startStep === "full" || startStep === "production") {
189
369
  saveOnboardState(cwd, {
190
370
  step: "production",
191
371
  timestamp: Date.now()
192
- }), console.info(), (0, import_prompts.showStep)("Production deployment setup"), console.info();
372
+ });
373
+ console.info();
374
+ (0, import_prompts.showStep)("Production deployment setup");
375
+ console.info();
193
376
  let setupProd = startStep === "production";
194
- startStep === "full" && (setupProd = await (0, import_prompts.confirmContinue)("Set up production deployment?", !1)), setupProd ? await setupProductionDeployment(cwd) : ((0, import_prompts.showInfo)("Skipping production setup"), (0, import_prompts.showInfo)("You can set up production later with: bun tko onboard --production"));
377
+ if (startStep === "full") {
378
+ setupProd = await (0, import_prompts.confirmContinue)("Set up production deployment?", false);
379
+ }
380
+ if (setupProd) {
381
+ await setupProductionDeployment(cwd);
382
+ } else {
383
+ (0, import_prompts.showInfo)("Skipping production setup");
384
+ (0, import_prompts.showInfo)("You can set up production later with: bun tko onboard --production");
385
+ }
195
386
  }
196
- clearOnboardState(cwd), console.info(), (0, import_prompts.displayOutro)("Happy coding! \u{1F680}");
387
+ clearOnboardState(cwd);
388
+ console.info();
389
+ (0, import_prompts.displayOutro)("Happy coding! \u{1F680}");
197
390
  }
198
391
  });
199
392
  async function customizeProject(cwd) {
200
- const projectName = await (0, import_prompts.promptText)("Project name:", "takeout", "my-awesome-app"),
201
- slug = await (0, import_prompts.promptText)("Project slug (URL-friendly):", projectName.toLowerCase().replace(/\s+/g, "-"), "my-awesome-app"),
202
- bundleId = await (0, import_prompts.promptText)("Bundle identifier:", `com.${slug}.app`, "com.example.app"),
203
- domain = await (0, import_prompts.promptText)("Development domain:", "localhost:8081", "localhost:8081"),
204
- pkgResult = (0, import_files.updatePackageJson)(cwd, {
205
- name: projectName,
206
- description: `${projectName} - Built with Takeout starter kit`
207
- });
208
- pkgResult.success ? (0, import_prompts.showSuccess)("Updated package.json") : (0, import_prompts.showError)(`Failed to update package.json: ${pkgResult.error}`);
393
+ const projectName = await (0, import_prompts.promptText)("Project name:", "takeout", "my-awesome-app");
394
+ const slug = await (0, import_prompts.promptText)("Project slug (URL-friendly):", projectName.toLowerCase().replace(/\s+/g, "-"), "my-awesome-app");
395
+ const bundleId = await (0, import_prompts.promptText)("Bundle identifier:", `com.${slug}.app`, "com.example.app");
396
+ const domain = await (0, import_prompts.promptText)("Development domain:", "localhost:8081", "localhost:8081");
397
+ const pkgResult = (0, import_files.updatePackageJson)(cwd, {
398
+ name: projectName,
399
+ description: `${projectName} - Built with Takeout starter kit`
400
+ });
401
+ if (pkgResult.success) {
402
+ (0, import_prompts.showSuccess)("Updated package.json");
403
+ } else {
404
+ (0, import_prompts.showError)(`Failed to update package.json: ${pkgResult.error}`);
405
+ }
209
406
  const configResult = (0, import_files.updateAppConfig)(cwd, {
210
407
  name: projectName,
211
408
  slug,
212
409
  bundleId
213
410
  });
214
- configResult.success ? (0, import_prompts.showSuccess)("Updated app.config.ts") : (0, import_prompts.showError)(`Failed to update app.config.ts: ${configResult.error}`);
411
+ if (configResult.success) {
412
+ (0, import_prompts.showSuccess)("Updated app.config.ts");
413
+ } else {
414
+ (0, import_prompts.showError)(`Failed to update app.config.ts: ${configResult.error}`);
415
+ }
215
416
  const serverUrl = `http://${domain}`;
216
- (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", serverUrl), (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", serverUrl), (0, import_prompts.showSuccess)("Updated environment URLs");
417
+ (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", serverUrl);
418
+ (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", serverUrl);
419
+ (0, import_prompts.showSuccess)("Updated environment URLs");
217
420
  }
218
421
  async function setupProductionDeployment(cwd) {
219
422
  const platform = await (0, import_prompts.promptSelect)("Choose deployment platform:", [{
@@ -229,14 +432,22 @@ async function setupProductionDeployment(cwd) {
229
432
  (0, import_prompts.showInfo)("Skipping production setup");
230
433
  return;
231
434
  }
232
- console.info(), platform === "uncloud" ? await setupUncloudDeployment(cwd) : await setupSSTDeployment(cwd);
435
+ console.info();
436
+ if (platform === "uncloud") {
437
+ await setupUncloudDeployment(cwd);
438
+ } else {
439
+ await setupSSTDeployment(cwd);
440
+ }
233
441
  }
234
442
  async function setupUncloudDeployment(cwd) {
235
- (0, import_prompts.showInfo)("Setting up Uncloud deployment"), console.info(), console.info(import_picocolors.default.gray(`Uncloud provides:
236
- \u2022 Managed PostgreSQL with logical replication
237
- \u2022 Free subdomain (your-app.uncld.dev)
238
- \u2022 Automatic SSL certificates
239
- \u2022 Easy scaling`)), console.info(), console.info(), (0, import_prompts.showInfo)("Server Architecture"), console.info(import_picocolors.default.gray("Docker images must match your server CPU architecture")), console.info();
443
+ (0, import_prompts.showInfo)("Setting up Uncloud deployment");
444
+ console.info();
445
+ console.info(import_picocolors.default.gray("Uncloud provides:\n \u2022 Managed PostgreSQL with logical replication\n \u2022 Free subdomain (your-app.uncld.dev)\n \u2022 Automatic SSL certificates\n \u2022 Easy scaling"));
446
+ console.info();
447
+ console.info();
448
+ (0, import_prompts.showInfo)("Server Architecture");
449
+ console.info(import_picocolors.default.gray("Docker images must match your server CPU architecture"));
450
+ console.info();
240
451
  const architecture = await (0, import_prompts.promptSelect)("What CPU architecture is your deployment server?", [{
241
452
  value: "amd64",
242
453
  label: "AMD64/x86_64 (Intel/AMD)",
@@ -251,93 +462,249 @@ async function setupUncloudDeployment(cwd) {
251
462
  return;
252
463
  }
253
464
  const deploymentArch = architecture === "arm64" ? "linux/arm64" : "linux/amd64";
254
- console.info(), (0, import_prompts.showInfo)(`Will build Docker images for: ${deploymentArch}`), console.info();
465
+ console.info();
466
+ (0, import_prompts.showInfo)(`Will build Docker images for: ${deploymentArch}`);
467
+ console.info();
255
468
  const appConstantsPath = (0, import_node_path.resolve)(cwd, "src/constants/app.ts");
256
469
  let defaultAppName = "my-app";
257
470
  try {
258
- const appNameMatch = (0, import_node_fs.readFileSync)(appConstantsPath, "utf-8").match(/APP_NAME_LOWERCASE\s*=\s*['"](.+?)['"]/);
259
- appNameMatch?.[1] && (defaultAppName = appNameMatch[1]);
471
+ const appConstants = (0, import_node_fs.readFileSync)(appConstantsPath, "utf-8");
472
+ const appNameMatch = appConstants.match(/APP_NAME_LOWERCASE\s*=\s*['"](.+?)['"]/);
473
+ if (appNameMatch?.[1]) {
474
+ defaultAppName = appNameMatch[1];
475
+ }
260
476
  } catch {}
261
- const useFreeSubdomain = await (0, import_prompts.confirmContinue)("Use free Uncloud subdomain?", !0);
262
- let domain, zeroUrl;
477
+ const useFreeSubdomain = await (0, import_prompts.confirmContinue)("Use free Uncloud subdomain?", true);
478
+ let domain;
479
+ let zeroUrl;
263
480
  if (useFreeSubdomain) {
264
481
  const appName = await (0, import_prompts.promptText)("App name for subdomain:", defaultAppName, defaultAppName);
265
- domain = `https://${appName}.uncld.dev`, zeroUrl = `https://zero-${appName}.uncld.dev`;
266
- } else domain = await (0, import_prompts.promptText)("Enter your custom domain:", void 0, "https://yourapp.com"), zeroUrl = await (0, import_prompts.promptText)("Enter your Zero sync domain:", void 0, "https://zero.yourapp.com"), console.info(), (0, import_prompts.showWarning)("Custom domain setup requires DNS configuration after deployment"), console.info(import_picocolors.default.gray("See: https://uncloud.run/docs/domains"));
267
- console.info(), (0, import_prompts.showInfo)("Database Configuration"), console.info(), console.info(import_picocolors.default.gray("PostgreSQL database with logical replication enabled")), console.info(import_picocolors.default.gray("Zero sync requires 3 databases on the same host:")), console.info(import_picocolors.default.gray(" \u2022 Main database (your app data)")), console.info(import_picocolors.default.gray(" \u2022 Two Zero databases (for sync infrastructure)")), console.info(), console.info(import_picocolors.default.gray("Use a managed database (DigitalOcean, Render, Supabase, etc.)")), console.info(import_picocolors.default.gray("The deployment will automatically create the Zero databases")), console.info();
268
- const dbUser = await (0, import_prompts.promptText)("Database username:", "postgres", "postgres"),
269
- dbPassword = await (0, import_prompts.promptText)("Database password:", void 0, ""),
270
- dbHost = await (0, import_prompts.promptText)("Database host (e.g., db-xxx.ondigitalocean.com):", void 0, "localhost"),
271
- dbPort = await (0, import_prompts.promptText)("Database port:", "5432", "5432"),
272
- dbName = await (0, import_prompts.promptText)("Main database name (will derive Zero databases from this):", "postgres", "postgres"),
273
- dbUrl = `postgresql://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbName}`;
274
- console.info();
275
- const authSecret = (0, import_node_crypto.randomBytes)(32).toString("hex"),
276
- envFile = ".env.production";
482
+ domain = `https://${appName}.uncld.dev`;
483
+ zeroUrl = `https://zero-${appName}.uncld.dev`;
484
+ } else {
485
+ domain = await (0, import_prompts.promptText)("Enter your custom domain:", void 0, "https://yourapp.com");
486
+ zeroUrl = await (0, import_prompts.promptText)("Enter your Zero sync domain:", void 0, "https://zero.yourapp.com");
487
+ console.info();
488
+ (0, import_prompts.showWarning)("Custom domain setup requires DNS configuration after deployment");
489
+ console.info(import_picocolors.default.gray("See: https://uncloud.run/docs/domains"));
490
+ }
491
+ console.info();
492
+ (0, import_prompts.showInfo)("Database Configuration");
493
+ console.info();
494
+ console.info(import_picocolors.default.gray("PostgreSQL database with logical replication enabled"));
495
+ console.info(import_picocolors.default.gray("Zero sync requires 3 databases on the same host:"));
496
+ console.info(import_picocolors.default.gray(" \u2022 Main database (your app data)"));
497
+ console.info(import_picocolors.default.gray(" \u2022 Two Zero databases (for sync infrastructure)"));
498
+ console.info();
499
+ console.info(import_picocolors.default.gray("Use a managed database (DigitalOcean, Render, Supabase, etc.)"));
500
+ console.info(import_picocolors.default.gray("The deployment will automatically create the Zero databases"));
501
+ console.info();
502
+ const dbUser = await (0, import_prompts.promptText)("Database username:", "postgres", "postgres");
503
+ const dbPassword = await (0, import_prompts.promptText)("Database password:", void 0, "");
504
+ const dbHost = await (0, import_prompts.promptText)("Database host (e.g., db-xxx.ondigitalocean.com):", void 0, "localhost");
505
+ const dbPort = await (0, import_prompts.promptText)("Database port:", "5432", "5432");
506
+ const dbName = await (0, import_prompts.promptText)("Main database name (will derive Zero databases from this):", "postgres", "postgres");
507
+ const dbUrl = `postgresql://${dbUser}:${dbPassword}@${dbHost}:${dbPort}/${dbName}`;
508
+ console.info();
509
+ const authSecret = (0, import_node_crypto.randomBytes)(32).toString("hex");
510
+ const envFile = ".env.production";
277
511
  if (!(0, import_env.envFileExists)(cwd, envFile)) {
278
512
  const copyResult = (0, import_env.copyEnvFile)(cwd, ".env.production.example", envFile);
279
- if (copyResult.success) (0, import_prompts.showSuccess)(`Created ${envFile} from .env.production.example`);else {
513
+ if (copyResult.success) {
514
+ (0, import_prompts.showSuccess)(`Created ${envFile} from .env.production.example`);
515
+ } else {
280
516
  (0, import_prompts.showError)(`Failed to create ${envFile}: ${copyResult.error}`);
281
517
  return;
282
518
  }
283
519
  }
284
- (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_PLATFORM", "uncloud", envFile), (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_ARCH", deploymentArch, envFile), (0, import_env.updateEnvVariable)(cwd, "DEPLOY_DB", dbUrl, envFile), (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_SECRET", authSecret, envFile), (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", domain, envFile), (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", domain, envFile);
285
- const zeroHost = zeroUrl.replace(/^https?:\/\//, ""),
286
- webHost = domain.replace(/^https?:\/\//, "");
287
- (0, import_env.updateEnvVariable)(cwd, "VITE_ZERO_HOSTNAME", zeroHost, envFile), (0, import_env.updateEnvVariable)(cwd, "VITE_WEB_HOSTNAME", webHost, envFile);
288
- const dbBase = dbUrl.split("/").slice(0, -1).join("/"),
289
- zeroCvrDb = `${dbBase}/zero_cvr`,
290
- zeroChangeDb = `${dbBase}/zero_cdb`;
291
- (0, import_env.updateEnvVariable)(cwd, "ZERO_UPSTREAM_DB", dbUrl, envFile), (0, import_env.updateEnvVariable)(cwd, "ZERO_CVR_DB", zeroCvrDb, envFile), (0, import_env.updateEnvVariable)(cwd, "ZERO_CHANGE_DB", zeroChangeDb, envFile), (0, import_env.updateEnvVariable)(cwd, "ZERO_UPSTREAM_DB", dbUrl, ".env"), (0, import_env.updateEnvVariable)(cwd, "ZERO_CVR_DB", zeroCvrDb, ".env"), (0, import_env.updateEnvVariable)(cwd, "ZERO_CHANGE_DB", zeroChangeDb, ".env");
520
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_PLATFORM", "uncloud", envFile);
521
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_ARCH", deploymentArch, envFile);
522
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOY_DB", dbUrl, envFile);
523
+ (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_SECRET", authSecret, envFile);
524
+ (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", domain, envFile);
525
+ (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", domain, envFile);
526
+ const zeroHost = zeroUrl.replace(/^https?:\/\//, "");
527
+ const webHost = domain.replace(/^https?:\/\//, "");
528
+ (0, import_env.updateEnvVariable)(cwd, "VITE_ZERO_HOSTNAME", zeroHost, envFile);
529
+ (0, import_env.updateEnvVariable)(cwd, "VITE_WEB_HOSTNAME", webHost, envFile);
530
+ const dbBase = dbUrl.split("/").slice(0, -1).join("/");
531
+ const zeroCvrDb = `${dbBase}/zero_cvr`;
532
+ const zeroChangeDb = `${dbBase}/zero_cdb`;
533
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_UPSTREAM_DB", dbUrl, envFile);
534
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_CVR_DB", zeroCvrDb, envFile);
535
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_CHANGE_DB", zeroChangeDb, envFile);
536
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_UPSTREAM_DB", dbUrl, ".env");
537
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_CVR_DB", zeroCvrDb, ".env");
538
+ (0, import_env.updateEnvVariable)(cwd, "ZERO_CHANGE_DB", zeroChangeDb, ".env");
292
539
  const deployHost = new URL(domain).hostname;
293
- (0, import_env.updateEnvVariable)(cwd, "DEPLOY_HOST", deployHost, envFile), (0, import_env.updateEnvVariable)(cwd, "DEPLOY_USER", "root", envFile), console.info(), (0, import_prompts.showInfo)("SSH Key Configuration"), console.info(import_picocolors.default.gray("Deployment requires SSH access to your server")), console.info();
540
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOY_HOST", deployHost, envFile);
541
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOY_USER", "root", envFile);
542
+ console.info();
543
+ (0, import_prompts.showInfo)("SSH Key Configuration");
544
+ console.info(import_picocolors.default.gray("Deployment requires SSH access to your server"));
545
+ console.info();
294
546
  const sshKeyPath = await (0, import_prompts.promptText)("Path to SSH private key:", `${(0, import_node_os.homedir)()}/.ssh/id_rsa`, `${(0, import_node_os.homedir)()}/.ssh/id_rsa`);
295
- if (sshKeyPath && ((0, import_node_fs.existsSync)(sshKeyPath) ? ((0, import_env.updateEnvVariable)(cwd, "DEPLOY_SSH_KEY", sshKeyPath, envFile), (0, import_prompts.showSuccess)(`\u2713 SSH key configured: ${sshKeyPath}`), console.info(), (0, import_prompts.showInfo)(import_picocolors.default.gray("For CI/CD, you'll need to add the SSH private key content as a GitHub secret")), (0, import_prompts.showInfo)(import_picocolors.default.gray("The sync script will help with this"))) : ((0, import_prompts.showWarning)(`SSH key not found at: ${sshKeyPath}`), (0, import_prompts.showInfo)("You can add DEPLOY_SSH_KEY to .env.production manually later"))), console.info(), (0, import_prompts.showSuccess)(`\u2713 Saved configuration to ${envFile}`), console.info(), (0, import_prompts.showInfo)("Custom Domain Setup (Optional)"), console.info(import_picocolors.default.gray("By default, your app will use:")), console.info(import_picocolors.default.gray(` ${deployHost} (cluster subdomain from Uncloud DNS)`)), console.info(), console.info(import_picocolors.default.gray("You can optionally configure custom domains (e.g., app.yourdomain.com) by:")), console.info(import_picocolors.default.gray(" 1. Adding CNAME records in your DNS provider")), console.info(import_picocolors.default.gray(" 2. Pointing to your cluster subdomain")), console.info(import_picocolors.default.gray(" 3. Setting VITE_WEB_HOSTNAME and VITE_ZERO_HOSTNAME in .env.production")), console.info(), await (0, import_prompts.confirmContinue)("Configure custom domain now?", !1)) {
296
- console.info(), console.info(import_picocolors.default.gray("First, run: uc dns reserve")), console.info(import_picocolors.default.gray("This will give you a cluster subdomain like: abc123.uncld.dev")), console.info();
547
+ if (sshKeyPath) {
548
+ if ((0, import_node_fs.existsSync)(sshKeyPath)) {
549
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOY_SSH_KEY", sshKeyPath, envFile);
550
+ (0, import_prompts.showSuccess)(`\u2713 SSH key configured: ${sshKeyPath}`);
551
+ console.info();
552
+ (0, import_prompts.showInfo)(import_picocolors.default.gray("For CI/CD, you'll need to add the SSH private key content as a GitHub secret"));
553
+ (0, import_prompts.showInfo)(import_picocolors.default.gray("The sync script will help with this"));
554
+ } else {
555
+ (0, import_prompts.showWarning)(`SSH key not found at: ${sshKeyPath}`);
556
+ (0, import_prompts.showInfo)("You can add DEPLOY_SSH_KEY to .env.production manually later");
557
+ }
558
+ }
559
+ console.info();
560
+ (0, import_prompts.showSuccess)(`\u2713 Saved configuration to ${envFile}`);
561
+ console.info();
562
+ (0, import_prompts.showInfo)("Custom Domain Setup (Optional)");
563
+ console.info(import_picocolors.default.gray("By default, your app will use:"));
564
+ console.info(import_picocolors.default.gray(` ${deployHost} (cluster subdomain from Uncloud DNS)`));
565
+ console.info();
566
+ console.info(import_picocolors.default.gray("You can optionally configure custom domains (e.g., app.yourdomain.com) by:"));
567
+ console.info(import_picocolors.default.gray(" 1. Adding CNAME records in your DNS provider"));
568
+ console.info(import_picocolors.default.gray(` 2. Pointing to your cluster subdomain`));
569
+ console.info(import_picocolors.default.gray(" 3. Setting VITE_WEB_HOSTNAME and VITE_ZERO_HOSTNAME in .env.production"));
570
+ console.info();
571
+ const configureCustomDomain = await (0, import_prompts.confirmContinue)("Configure custom domain now?", false);
572
+ if (configureCustomDomain) {
573
+ console.info();
574
+ console.info(import_picocolors.default.gray("First, run: uc dns reserve"));
575
+ console.info(import_picocolors.default.gray("This will give you a cluster subdomain like: abc123.uncld.dev"));
576
+ console.info();
297
577
  const clusterSubdomain = await (0, import_prompts.promptText)("Enter your cluster subdomain from uc dns show:", "", "");
298
578
  if (clusterSubdomain) {
299
- console.info(), (0, import_prompts.showInfo)("DNS Setup Instructions:"), console.info(import_picocolors.default.gray("Add these CNAME records in your DNS provider (e.g., Cloudflare):")), console.info();
300
- const webDomain = await (0, import_prompts.promptText)("Custom domain for web app (e.g., app.yourdomain.com):", "", ""),
301
- zeroDomain = await (0, import_prompts.promptText)("Custom domain for zero sync (e.g., zero.yourdomain.com):", "", "");
579
+ console.info();
580
+ (0, import_prompts.showInfo)("DNS Setup Instructions:");
581
+ console.info(import_picocolors.default.gray("Add these CNAME records in your DNS provider (e.g., Cloudflare):"));
582
+ console.info();
583
+ const webDomain = await (0, import_prompts.promptText)("Custom domain for web app (e.g., app.yourdomain.com):", "", "");
584
+ const zeroDomain = await (0, import_prompts.promptText)("Custom domain for zero sync (e.g., zero.yourdomain.com):", "", "");
302
585
  if (webDomain) {
303
- console.info(), console.info(import_picocolors.default.cyan(` CNAME: ${webDomain} \u2192 ${clusterSubdomain}`)), zeroDomain && console.info(import_picocolors.default.cyan(` CNAME: ${zeroDomain} \u2192 ${clusterSubdomain}`)), console.info(), console.info(import_picocolors.default.yellow('\u26A0\uFE0F Set DNS to "DNS only" mode (gray cloud), not proxied')), console.info();
586
+ console.info();
587
+ console.info(import_picocolors.default.cyan(` CNAME: ${webDomain} \u2192 ${clusterSubdomain}`));
588
+ if (zeroDomain) {
589
+ console.info(import_picocolors.default.cyan(` CNAME: ${zeroDomain} \u2192 ${clusterSubdomain}`));
590
+ }
591
+ console.info();
592
+ console.info(import_picocolors.default.yellow('\u26A0\uFE0F Set DNS to "DNS only" mode (gray cloud), not proxied'));
593
+ console.info();
304
594
  const webUrl = `https://${webDomain}`;
305
- if ((0, import_env.updateEnvVariable)(cwd, "VITE_WEB_HOSTNAME", webDomain, envFile), (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", webUrl, envFile), (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", webUrl, envFile), zeroDomain && (0, import_env.updateEnvVariable)(cwd, "VITE_ZERO_HOSTNAME", zeroDomain, envFile), (0, import_prompts.showSuccess)("\u2713 Custom domains configured"), console.info(), (0, import_prompts.showInfo)(import_picocolors.default.gray("DNS propagation typically takes 5-30 minutes")), console.info(), (0, import_prompts.showInfo)("SSL Certificate Options"), console.info(import_picocolors.default.gray("By default, Caddy will use Let's Encrypt for SSL certificates.")), console.info(import_picocolors.default.gray("If using Cloudflare, you can use Origin CA instead to:")), console.info(import_picocolors.default.gray(" \u2022 Bypass Let's Encrypt rate limits")), console.info(import_picocolors.default.gray(" \u2022 Enable Cloudflare proxy (DDoS protection, caching)")), console.info(), await (0, import_prompts.confirmContinue)("Use Cloudflare Origin CA? (requires Cloudflare account)", !1)) {
306
- console.info(), (0, import_prompts.showInfo)("Cloudflare Origin CA Setup"), console.info(import_picocolors.default.gray("1. Go to Cloudflare Dashboard \u2192 SSL/TLS \u2192 Origin Server")), console.info(import_picocolors.default.gray('2. Click "Create Certificate"')), console.info(import_picocolors.default.gray(`3. Add hostnames: ${webDomain}${zeroDomain ? `, ${zeroDomain}` : ""}`)), console.info(import_picocolors.default.gray("4. Choose 15 year validity")), console.info(import_picocolors.default.gray("5. Save certificate to: certs/origin.pem")), console.info(import_picocolors.default.gray("6. Save private key to: certs/origin.key")), console.info();
307
- const certPath = await (0, import_prompts.promptText)("Path to Origin CA certificate:", "certs/origin.pem", "certs/origin.pem"),
308
- keyPath = await (0, import_prompts.promptText)("Path to Origin CA private key:", "certs/origin.key", "certs/origin.key");
309
- certPath && keyPath && ((0, import_env.updateEnvVariable)(cwd, "ORIGIN_CA_CERT", certPath, envFile), (0, import_env.updateEnvVariable)(cwd, "ORIGIN_CA_KEY", keyPath, envFile), (0, import_prompts.showSuccess)("\u2713 Origin CA configured"), console.info(import_picocolors.default.gray(" Caddyfile will be auto-generated during deploy")), console.info(), console.info(import_picocolors.default.yellow("Important: In Cloudflare Dashboard:")), console.info(import_picocolors.default.yellow(" 1. Enable proxy (orange cloud) for your domains")), console.info(import_picocolors.default.yellow(' 2. Set SSL mode to "Full (strict)"')));
595
+ (0, import_env.updateEnvVariable)(cwd, "VITE_WEB_HOSTNAME", webDomain, envFile);
596
+ (0, import_env.updateEnvVariable)(cwd, "BETTER_AUTH_URL", webUrl, envFile);
597
+ (0, import_env.updateEnvVariable)(cwd, "ONE_SERVER_URL", webUrl, envFile);
598
+ if (zeroDomain) {
599
+ (0, import_env.updateEnvVariable)(cwd, "VITE_ZERO_HOSTNAME", zeroDomain, envFile);
600
+ }
601
+ (0, import_prompts.showSuccess)("\u2713 Custom domains configured");
602
+ console.info();
603
+ (0, import_prompts.showInfo)(import_picocolors.default.gray("DNS propagation typically takes 5-30 minutes"));
604
+ console.info();
605
+ (0, import_prompts.showInfo)("SSL Certificate Options");
606
+ console.info(import_picocolors.default.gray("By default, Caddy will use Let's Encrypt for SSL certificates."));
607
+ console.info(import_picocolors.default.gray("If using Cloudflare, you can use Origin CA instead to:"));
608
+ console.info(import_picocolors.default.gray(" \u2022 Bypass Let's Encrypt rate limits"));
609
+ console.info(import_picocolors.default.gray(" \u2022 Enable Cloudflare proxy (DDoS protection, caching)"));
610
+ console.info();
611
+ const useOriginCA = await (0, import_prompts.confirmContinue)("Use Cloudflare Origin CA? (requires Cloudflare account)", false);
612
+ if (useOriginCA) {
613
+ console.info();
614
+ (0, import_prompts.showInfo)("Cloudflare Origin CA Setup");
615
+ console.info(import_picocolors.default.gray("1. Go to Cloudflare Dashboard \u2192 SSL/TLS \u2192 Origin Server"));
616
+ console.info(import_picocolors.default.gray('2. Click "Create Certificate"'));
617
+ console.info(import_picocolors.default.gray(`3. Add hostnames: ${webDomain}${zeroDomain ? `, ${zeroDomain}` : ""}`));
618
+ console.info(import_picocolors.default.gray("4. Choose 15 year validity"));
619
+ console.info(import_picocolors.default.gray("5. Save certificate to: certs/origin.pem"));
620
+ console.info(import_picocolors.default.gray("6. Save private key to: certs/origin.key"));
621
+ console.info();
622
+ const certPath = await (0, import_prompts.promptText)("Path to Origin CA certificate:", "certs/origin.pem", "certs/origin.pem");
623
+ const keyPath = await (0, import_prompts.promptText)("Path to Origin CA private key:", "certs/origin.key", "certs/origin.key");
624
+ if (certPath && keyPath) {
625
+ (0, import_env.updateEnvVariable)(cwd, "ORIGIN_CA_CERT", certPath, envFile);
626
+ (0, import_env.updateEnvVariable)(cwd, "ORIGIN_CA_KEY", keyPath, envFile);
627
+ (0, import_prompts.showSuccess)("\u2713 Origin CA configured");
628
+ console.info(import_picocolors.default.gray(" Caddyfile will be auto-generated during deploy"));
629
+ console.info();
630
+ console.info(import_picocolors.default.yellow("Important: In Cloudflare Dashboard:"));
631
+ console.info(import_picocolors.default.yellow(" 1. Enable proxy (orange cloud) for your domains"));
632
+ console.info(import_picocolors.default.yellow(' 2. Set SSL mode to "Full (strict)"'));
633
+ }
310
634
  }
311
635
  }
312
636
  }
313
637
  }
314
- console.info(), (0, import_prompts.showInfo)("Updating package.json env section for Uncloud deployment...");
638
+ console.info();
639
+ (0, import_prompts.showInfo)("Updating package.json env section for Uncloud deployment...");
315
640
  const envUpdateResult = (0, import_files.updatePackageJsonEnv)(cwd, "uncloud");
316
- envUpdateResult.success ? (0, import_prompts.showSuccess)("\u2713 Removed SST-specific environment variables from package.json") : (0, import_prompts.showWarning)(`Failed to update package.json env: ${envUpdateResult.error}`), console.info(), (0, import_prompts.showInfo)("Updating GitHub workflow with environment variables...");
641
+ if (envUpdateResult.success) {
642
+ (0, import_prompts.showSuccess)("\u2713 Removed SST-specific environment variables from package.json");
643
+ } else {
644
+ (0, import_prompts.showWarning)(`Failed to update package.json env: ${envUpdateResult.error}`);
645
+ }
646
+ console.info();
647
+ (0, import_prompts.showInfo)("Updating GitHub workflow with environment variables...");
317
648
  try {
318
649
  (0, import_node_child_process.execSync)("bun env:update", {
319
650
  cwd,
320
651
  stdio: "ignore"
321
- }), (0, import_prompts.showSuccess)("\u2713 GitHub workflow updated");
322
- } catch {
652
+ });
653
+ (0, import_prompts.showSuccess)("\u2713 GitHub workflow updated");
654
+ } catch (error) {
323
655
  (0, import_prompts.showWarning)("Failed to update GitHub workflow (non-critical)");
324
656
  }
325
- if (console.info(), await (0, import_prompts.confirmContinue)("Sync environment to GitHub secrets for CI/CD?", !0)) try {
326
- (0, import_node_child_process.execSync)("bun scripts/env/sync-to-github.ts --yes", {
327
- cwd,
328
- stdio: "inherit"
329
- });
330
- } catch {
331
- (0, import_prompts.showWarning)("Failed to sync to GitHub (you can do this later)"), (0, import_prompts.showInfo)("Run manually: bun scripts/env/sync-to-github.ts");
332
- } else (0, import_prompts.showInfo)("You can sync later with: bun scripts/env/sync-to-github.ts");
333
- console.info(), (0, import_prompts.showStep)("Ready to deploy!"), console.info(), console.info(import_picocolors.default.bold("Next steps:")), console.info(), console.info(import_picocolors.default.cyan("1. Install Uncloud CLI (if not already installed):")), console.info(import_picocolors.default.gray(" npm install -g uncloud-cli")), console.info(), console.info(import_picocolors.default.cyan("2. Login to Uncloud:")), console.info(import_picocolors.default.gray(" uncloud login")), console.info(), console.info(import_picocolors.default.cyan("3. Deploy your app:")), console.info(import_picocolors.default.gray(" bun tko uncloud deploy-prod")), console.info(), console.info(import_picocolors.default.cyan("Or push to main branch for automatic CI/CD deployment:")), console.info(import_picocolors.default.gray(" git push origin main")), console.info(), console.info(import_picocolors.default.bold("scaling to multiple machines:")), console.info(import_picocolors.default.gray(" uc machine add --name server-2 root@IP")), console.info(import_picocolors.default.gray(" uc scale web 3 # run 3 instances")), console.info(), console.info(import_picocolors.default.gray("view detailed guide: bun tko docs read deployment-uncloud")), console.info(import_picocolors.default.gray("or see: docs/deployment-uncloud.md"));
657
+ console.info();
658
+ const syncToGitHub = await (0, import_prompts.confirmContinue)("Sync environment to GitHub secrets for CI/CD?", true);
659
+ if (syncToGitHub) {
660
+ try {
661
+ (0, import_node_child_process.execSync)("bun scripts/env/sync-to-github.ts --yes", {
662
+ cwd,
663
+ stdio: "inherit"
664
+ });
665
+ } catch (error) {
666
+ (0, import_prompts.showWarning)("Failed to sync to GitHub (you can do this later)");
667
+ (0, import_prompts.showInfo)("Run manually: bun scripts/env/sync-to-github.ts");
668
+ }
669
+ } else {
670
+ (0, import_prompts.showInfo)("You can sync later with: bun scripts/env/sync-to-github.ts");
671
+ }
672
+ console.info();
673
+ (0, import_prompts.showStep)("Ready to deploy!");
674
+ console.info();
675
+ console.info(import_picocolors.default.bold("Next steps:"));
676
+ console.info();
677
+ console.info(import_picocolors.default.cyan("1. Install Uncloud CLI (if not already installed):"));
678
+ console.info(import_picocolors.default.gray(" npm install -g uncloud-cli"));
679
+ console.info();
680
+ console.info(import_picocolors.default.cyan("2. Login to Uncloud:"));
681
+ console.info(import_picocolors.default.gray(" uncloud login"));
682
+ console.info();
683
+ console.info(import_picocolors.default.cyan("3. Deploy your app:"));
684
+ console.info(import_picocolors.default.gray(" bun tko uncloud deploy-prod"));
685
+ console.info();
686
+ console.info(import_picocolors.default.cyan("Or push to main branch for automatic CI/CD deployment:"));
687
+ console.info(import_picocolors.default.gray(" git push origin main"));
688
+ console.info();
689
+ console.info(import_picocolors.default.bold("scaling to multiple machines:"));
690
+ console.info(import_picocolors.default.gray(" uc machine add --name server-2 root@IP"));
691
+ console.info(import_picocolors.default.gray(" uc scale web 3 # run 3 instances"));
692
+ console.info();
693
+ console.info(import_picocolors.default.gray("view detailed guide: bun tko docs read deployment-uncloud"));
694
+ console.info(import_picocolors.default.gray("or see: docs/deployment-uncloud.md"));
334
695
  }
335
696
  async function setupSSTDeployment(cwd) {
336
- (0, import_prompts.showInfo)("Setting up AWS SST deployment"), console.info(), (0, import_prompts.showWarning)("AWS setup takes approximately 30 minutes"), console.info(), console.info(import_picocolors.default.gray(`SST provides:
337
- \u2022 AWS infrastructure (ECS, Aurora, ALB)
338
- \u2022 Auto-scaling
339
- \u2022 Full control over resources
340
- \u2022 Higher setup complexity`)), console.info(), console.info(), (0, import_prompts.showInfo)("AWS ECS Architecture"), console.info(import_picocolors.default.gray("AWS Graviton (ARM64) is ~40% cheaper than x86_64")), console.info(import_picocolors.default.gray("Both have excellent performance, ARM recommended for cost savings")), console.info();
697
+ (0, import_prompts.showInfo)("Setting up AWS SST deployment");
698
+ console.info();
699
+ (0, import_prompts.showWarning)("AWS setup takes approximately 30 minutes");
700
+ console.info();
701
+ console.info(import_picocolors.default.gray("SST provides:\n \u2022 AWS infrastructure (ECS, Aurora, ALB)\n \u2022 Auto-scaling\n \u2022 Full control over resources\n \u2022 Higher setup complexity"));
702
+ console.info();
703
+ console.info();
704
+ (0, import_prompts.showInfo)("AWS ECS Architecture");
705
+ console.info(import_picocolors.default.gray("AWS Graviton (ARM64) is ~40% cheaper than x86_64"));
706
+ console.info(import_picocolors.default.gray("Both have excellent performance, ARM recommended for cost savings"));
707
+ console.info();
341
708
  const architecture = await (0, import_prompts.promptSelect)("What CPU architecture for AWS ECS?", [{
342
709
  value: "arm64",
343
710
  label: "ARM64 (Graviton) - Recommended",
@@ -352,49 +719,78 @@ async function setupSSTDeployment(cwd) {
352
719
  return;
353
720
  }
354
721
  const deploymentArch = architecture === "arm64" ? "linux/arm64" : "linux/amd64";
355
- if (console.info(), (0, import_prompts.showInfo)(`Will build Docker images for: ${deploymentArch}`), console.info(), !(await (0, import_prompts.confirmContinue)("Continue with AWS SST setup? (requires AWS account)", !1))) {
356
- (0, import_prompts.showInfo)("Skipping AWS setup"), (0, import_prompts.showInfo)("You can set up AWS later with: bun tko env:setup --category aws");
722
+ console.info();
723
+ (0, import_prompts.showInfo)(`Will build Docker images for: ${deploymentArch}`);
724
+ console.info();
725
+ const shouldContinue = await (0, import_prompts.confirmContinue)("Continue with AWS SST setup? (requires AWS account)", false);
726
+ if (!shouldContinue) {
727
+ (0, import_prompts.showInfo)("Skipping AWS setup");
728
+ (0, import_prompts.showInfo)("You can set up AWS later with: bun tko env:setup --category aws");
357
729
  return;
358
730
  }
359
731
  await (0, import_env_setup.setupProductionEnv)(cwd, {
360
732
  onlyCategory: "aws",
361
733
  envFile: ".env.production",
362
- interactive: !0
734
+ interactive: true
363
735
  });
364
736
  const envFile = ".env.production";
365
- (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_PLATFORM", "sst", envFile), (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_ARCH", deploymentArch, envFile), console.info(), (0, import_prompts.showInfo)("Updating sst.config.ts architecture...");
737
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_PLATFORM", "sst", envFile);
738
+ (0, import_env.updateEnvVariable)(cwd, "DEPLOYMENT_ARCH", deploymentArch, envFile);
739
+ console.info();
740
+ (0, import_prompts.showInfo)("Updating sst.config.ts architecture...");
366
741
  try {
367
742
  const sstConfigPath = (0, import_node_path.resolve)(cwd, "sst.config.ts");
368
743
  let sstConfig = (0, import_node_fs.readFileSync)(sstConfigPath, "utf-8");
369
744
  const archValue = architecture === "arm64" ? "arm64" : "x86_64";
370
- sstConfig = sstConfig.replace(/architecture:\s*['"]arm64['"]/g, `architecture: '${archValue}'`), sstConfig = sstConfig.replace(/architecture:\s*['"]x86_64['"]/g, `architecture: '${archValue}'`), (0, import_node_fs.writeFileSync)(sstConfigPath, sstConfig), (0, import_prompts.showSuccess)(`\u2713 Updated sst.config.ts to use ${archValue}`);
371
- } catch {
745
+ sstConfig = sstConfig.replace(/architecture:\s*['"]arm64['"]/g, `architecture: '${archValue}'`);
746
+ sstConfig = sstConfig.replace(/architecture:\s*['"]x86_64['"]/g, `architecture: '${archValue}'`);
747
+ (0, import_node_fs.writeFileSync)(sstConfigPath, sstConfig);
748
+ (0, import_prompts.showSuccess)(`\u2713 Updated sst.config.ts to use ${archValue}`);
749
+ } catch (error) {
372
750
  (0, import_prompts.showWarning)("Could not update sst.config.ts (you can update manually)");
373
751
  }
374
- console.info(), (0, import_prompts.showInfo)("Updating package.json env section for SST deployment...");
752
+ console.info();
753
+ (0, import_prompts.showInfo)("Updating package.json env section for SST deployment...");
375
754
  const envUpdateResult = (0, import_files.updatePackageJsonEnv)(cwd, "sst");
376
- envUpdateResult.success ? (0, import_prompts.showSuccess)("\u2713 Removed Uncloud-specific environment variables from package.json") : (0, import_prompts.showWarning)(`Failed to update package.json env: ${envUpdateResult.error}`), console.info(), (0, import_prompts.showInfo)("Updating GitHub workflow with environment variables...");
755
+ if (envUpdateResult.success) {
756
+ (0, import_prompts.showSuccess)("\u2713 Removed Uncloud-specific environment variables from package.json");
757
+ } else {
758
+ (0, import_prompts.showWarning)(`Failed to update package.json env: ${envUpdateResult.error}`);
759
+ }
760
+ console.info();
761
+ (0, import_prompts.showInfo)("Updating GitHub workflow with environment variables...");
377
762
  try {
378
763
  (0, import_node_child_process.execSync)("bun env:update", {
379
764
  cwd,
380
765
  stdio: "ignore"
381
- }), (0, import_prompts.showSuccess)("\u2713 GitHub workflow updated");
382
- } catch {
766
+ });
767
+ (0, import_prompts.showSuccess)("\u2713 GitHub workflow updated");
768
+ } catch (error) {
383
769
  (0, import_prompts.showWarning)("Failed to update GitHub workflow (non-critical)");
384
770
  }
385
- if (console.info(), await (0, import_prompts.confirmContinue)("Sync environment to GitHub secrets for CI/CD?", !0)) try {
386
- (0, import_node_child_process.execSync)("bun scripts/env/sync-to-github.ts --yes", {
387
- cwd,
388
- stdio: "inherit"
389
- });
390
- } catch {
391
- (0, import_prompts.showWarning)("Failed to sync to GitHub (you can do this later)"), (0, import_prompts.showInfo)("Run manually: bun scripts/env/sync-to-github.ts");
392
- } else (0, import_prompts.showInfo)("You can sync later with: bun scripts/env/sync-to-github.ts");
393
- console.info(), (0, import_prompts.showInfo)("For complete AWS deployment guide, see: https://docs.yourapp.com/deployment/sst");
771
+ console.info();
772
+ const syncToGitHub = await (0, import_prompts.confirmContinue)("Sync environment to GitHub secrets for CI/CD?", true);
773
+ if (syncToGitHub) {
774
+ try {
775
+ (0, import_node_child_process.execSync)("bun scripts/env/sync-to-github.ts --yes", {
776
+ cwd,
777
+ stdio: "inherit"
778
+ });
779
+ } catch (error) {
780
+ (0, import_prompts.showWarning)("Failed to sync to GitHub (you can do this later)");
781
+ (0, import_prompts.showInfo)("Run manually: bun scripts/env/sync-to-github.ts");
782
+ }
783
+ } else {
784
+ (0, import_prompts.showInfo)("You can sync later with: bun scripts/env/sync-to-github.ts");
785
+ }
786
+ console.info();
787
+ (0, import_prompts.showInfo)("For complete AWS deployment guide, see: https://docs.yourapp.com/deployment/sst");
394
788
  }
395
789
  async function configureCIRunners(cwd) {
396
- (0, import_prompts.showInfo)("GitHub Actions CI/CD Runner Configuration"), console.info(), console.info(import_picocolors.default.gray(`Your project uses ARM64 (Apple Silicon) architecture for Docker builds.
397
- GitHub Actions requires compatible runners for CI/CD to work properly.`)), console.info();
790
+ (0, import_prompts.showInfo)("GitHub Actions CI/CD Runner Configuration");
791
+ console.info();
792
+ console.info(import_picocolors.default.gray("Your project uses ARM64 (Apple Silicon) architecture for Docker builds.\nGitHub Actions requires compatible runners for CI/CD to work properly."));
793
+ console.info();
398
794
  const runnerChoice = await (0, import_prompts.promptSelect)("Choose your CI runner configuration:", [{
399
795
  value: "warp",
400
796
  label: "Warp Runners (Recommended)",
@@ -413,41 +809,79 @@ GitHub Actions requires compatible runners for CI/CD to work properly.`)), conso
413
809
  hint: "Skip for now (CI will fail until configured)"
414
810
  }]);
415
811
  if (runnerChoice === "cancel" || runnerChoice === "skip") {
416
- (0, import_prompts.showInfo)("Skipping CI runner configuration"), (0, import_prompts.showWarning)("CI/CD will fail until you configure runners. Update .github/workflows/ci.yml");
812
+ (0, import_prompts.showInfo)("Skipping CI runner configuration");
813
+ (0, import_prompts.showWarning)("CI/CD will fail until you configure runners. Update .github/workflows/ci.yml");
417
814
  return;
418
815
  }
419
- const ciConfigPath = (0, import_node_path.resolve)(cwd, ".github/workflows/ci.yml"),
420
- dockerBuildPath = (0, import_node_path.resolve)(cwd, "scripts/web/build-docker.ts");
816
+ const ciConfigPath = (0, import_node_path.resolve)(cwd, ".github/workflows/ci.yml");
817
+ const dockerBuildPath = (0, import_node_path.resolve)(cwd, "scripts/web/build-docker.ts");
421
818
  try {
422
- let ciContent = (0, import_node_fs.readFileSync)(ciConfigPath, "utf-8"),
423
- dockerContent = (0, import_node_fs.readFileSync)(dockerBuildPath, "utf-8");
424
- runnerChoice === "warp" ? (console.info(), (0, import_prompts.showStep)("Setting up Warp runners"), console.info(), console.info(import_picocolors.default.cyan("1. Sign up for Warp Build (if not already):")), console.info(import_picocolors.default.gray(" https://www.warpbuild.com")), console.info(), console.info(import_picocolors.default.cyan("2. Install Warp GitHub App:")), console.info(import_picocolors.default.gray(" https://github.com/apps/warp-build")), console.info(), console.info(import_picocolors.default.cyan("3. Grant access to your repository")), console.info(), (0, import_prompts.showSuccess)("\u2713 CI configuration already set for Warp runners"), (0, import_prompts.showInfo)("Warp uses ARM64 runners matching your local architecture")) : runnerChoice === "github-arm" ? (ciContent = ciContent.replace(/runs-on:.*warp-ubuntu-latest-arm64.*/, "runs-on: ubuntu-24.04-arm"), (0, import_node_fs.writeFileSync)(ciConfigPath, ciContent), console.info(), (0, import_prompts.showSuccess)("\u2713 Updated CI to use GitHub ARM runners"), (0, import_prompts.showWarning)("Note: GitHub ARM runners require Teams or Enterprise plan"), (0, import_prompts.showInfo)("Pricing: $0.16/minute for ARM runners")) : runnerChoice === "github-x64" && (ciContent = ciContent.replace(/runs-on:.*warp-ubuntu-latest-arm64.*/, "runs-on: ubuntu-latest"), (0, import_node_fs.writeFileSync)(ciConfigPath, ciContent), dockerContent = dockerContent.replace("linux/arm64", "linux/amd64"), (0, import_node_fs.writeFileSync)(dockerBuildPath, dockerContent), console.info(), (0, import_prompts.showSuccess)("\u2713 Updated CI to use free GitHub x64 runners"), (0, import_prompts.showSuccess)("\u2713 Updated Docker builds to x64 architecture"), (0, import_prompts.showWarning)("Note: Docker images built on x64 won't run on ARM64 machines without emulation")), console.info(), (0, import_prompts.showInfo)("CI runner configuration complete");
425
- } catch {
426
- (0, import_prompts.showError)("Failed to update CI configuration"), (0, import_prompts.showInfo)("Please manually update .github/workflows/ci.yml and scripts/web/build-docker.ts");
819
+ let ciContent = (0, import_node_fs.readFileSync)(ciConfigPath, "utf-8");
820
+ let dockerContent = (0, import_node_fs.readFileSync)(dockerBuildPath, "utf-8");
821
+ if (runnerChoice === "warp") {
822
+ console.info();
823
+ (0, import_prompts.showStep)("Setting up Warp runners");
824
+ console.info();
825
+ console.info(import_picocolors.default.cyan("1. Sign up for Warp Build (if not already):"));
826
+ console.info(import_picocolors.default.gray(" https://www.warpbuild.com"));
827
+ console.info();
828
+ console.info(import_picocolors.default.cyan("2. Install Warp GitHub App:"));
829
+ console.info(import_picocolors.default.gray(" https://github.com/apps/warp-build"));
830
+ console.info();
831
+ console.info(import_picocolors.default.cyan("3. Grant access to your repository"));
832
+ console.info();
833
+ (0, import_prompts.showSuccess)("\u2713 CI configuration already set for Warp runners");
834
+ (0, import_prompts.showInfo)("Warp uses ARM64 runners matching your local architecture");
835
+ } else if (runnerChoice === "github-arm") {
836
+ ciContent = ciContent.replace(/runs-on:.*warp-ubuntu-latest-arm64.*/, "runs-on: ubuntu-24.04-arm");
837
+ (0, import_node_fs.writeFileSync)(ciConfigPath, ciContent);
838
+ console.info();
839
+ (0, import_prompts.showSuccess)("\u2713 Updated CI to use GitHub ARM runners");
840
+ (0, import_prompts.showWarning)("Note: GitHub ARM runners require Teams or Enterprise plan");
841
+ (0, import_prompts.showInfo)("Pricing: $0.16/minute for ARM runners");
842
+ } else if (runnerChoice === "github-x64") {
843
+ ciContent = ciContent.replace(/runs-on:.*warp-ubuntu-latest-arm64.*/, "runs-on: ubuntu-latest");
844
+ (0, import_node_fs.writeFileSync)(ciConfigPath, ciContent);
845
+ dockerContent = dockerContent.replace("linux/arm64", "linux/amd64");
846
+ (0, import_node_fs.writeFileSync)(dockerBuildPath, dockerContent);
847
+ console.info();
848
+ (0, import_prompts.showSuccess)("\u2713 Updated CI to use free GitHub x64 runners");
849
+ (0, import_prompts.showSuccess)("\u2713 Updated Docker builds to x64 architecture");
850
+ (0, import_prompts.showWarning)("Note: Docker images built on x64 won't run on ARM64 machines without emulation");
851
+ }
852
+ console.info();
853
+ (0, import_prompts.showInfo)("CI runner configuration complete");
854
+ } catch (error) {
855
+ (0, import_prompts.showError)("Failed to update CI configuration");
856
+ (0, import_prompts.showInfo)("Please manually update .github/workflows/ci.yml and scripts/web/build-docker.ts");
427
857
  }
428
858
  }
429
859
  async function replacePortInProject(cwd, oldPort, newPort) {
430
- const spinner = (0, import_prompts.showSpinner)(`Replacing port ${oldPort} with ${newPort} throughout project...`),
431
- excludeDirs = /* @__PURE__ */new Set(["node_modules", ".git", "dist", "build", ".next", ".turbo", "types"]),
432
- includeExts = /* @__PURE__ */new Set([".ts", ".tsx", ".js", ".jsx", ".json", ".md", ".yml", ".yaml"]);
860
+ const spinner = (0, import_prompts.showSpinner)(`Replacing port ${oldPort} with ${newPort} throughout project...`);
861
+ const excludeDirs = /* @__PURE__ */new Set(["node_modules", ".git", "dist", "build", ".next", ".turbo", "types"]);
862
+ const includeExts = /* @__PURE__ */new Set([".ts", ".tsx", ".js", ".jsx", ".json", ".md", ".yml", ".yaml"]);
433
863
  async function processDir(dir) {
434
864
  const {
435
- readdirSync,
436
- statSync
437
- } = await import("node:fs"),
438
- {
439
- join,
440
- extname,
441
- basename
442
- } = await import("node:path"),
443
- entries = readdirSync(dir);
865
+ readdirSync,
866
+ statSync
867
+ } = await import("node:fs");
868
+ const {
869
+ join,
870
+ extname,
871
+ basename
872
+ } = await import("node:path");
873
+ const entries = readdirSync(dir);
444
874
  for (const entry of entries) {
445
875
  const fullPath = join(dir, entry);
446
876
  try {
447
877
  const stat = statSync(fullPath);
448
- if (stat.isDirectory()) excludeDirs.has(entry) || (await processDir(fullPath));else if (stat.isFile()) {
449
- const ext = extname(entry),
450
- name = basename(entry);
878
+ if (stat.isDirectory()) {
879
+ if (!excludeDirs.has(entry)) {
880
+ await processDir(fullPath);
881
+ }
882
+ } else if (stat.isFile()) {
883
+ const ext = extname(entry);
884
+ const name = basename(entry);
451
885
  if (includeExts.has(ext) || name.startsWith(".env")) {
452
886
  const content = (0, import_node_fs.readFileSync)(fullPath, "utf-8");
453
887
  if (content.includes(oldPort)) {
@@ -460,24 +894,32 @@ async function replacePortInProject(cwd, oldPort, newPort) {
460
894
  }
461
895
  }
462
896
  try {
463
- await processDir(cwd), spinner.stop(`Port updated from ${oldPort} to ${newPort}`);
897
+ await processDir(cwd);
898
+ spinner.stop(`Port updated from ${oldPort} to ${newPort}`);
464
899
  } catch (error) {
465
- throw spinner.stop("Port replacement failed"), (0, import_prompts.showError)("Failed to replace port. You may need to update manually."), error;
900
+ spinner.stop("Port replacement failed");
901
+ (0, import_prompts.showError)("Failed to replace port. You may need to update manually.");
902
+ throw error;
466
903
  }
467
904
  }
468
905
  function getStatePath(cwd) {
469
906
  return (0, import_node_path.resolve)(cwd, "node_modules/.takeout/onboard-state.json");
470
907
  }
471
908
  function saveOnboardState(cwd, state) {
472
- const statePath = getStatePath(cwd),
473
- stateDir = (0, import_node_path.resolve)(cwd, "node_modules/.takeout");
474
- (0, import_node_fs.existsSync)(stateDir) || (0, import_node_fs.mkdirSync)(stateDir, {
475
- recursive: !0
476
- }), (0, import_node_fs.writeFileSync)(statePath, JSON.stringify(state, null, 2));
909
+ const statePath = getStatePath(cwd);
910
+ const stateDir = (0, import_node_path.resolve)(cwd, "node_modules/.takeout");
911
+ if (!(0, import_node_fs.existsSync)(stateDir)) {
912
+ (0, import_node_fs.mkdirSync)(stateDir, {
913
+ recursive: true
914
+ });
915
+ }
916
+ (0, import_node_fs.writeFileSync)(statePath, JSON.stringify(state, null, 2));
477
917
  }
478
918
  function loadOnboardState(cwd) {
479
919
  const statePath = getStatePath(cwd);
480
- if (!(0, import_node_fs.existsSync)(statePath)) return null;
920
+ if (!(0, import_node_fs.existsSync)(statePath)) {
921
+ return null;
922
+ }
481
923
  try {
482
924
  const content = (0, import_node_fs.readFileSync)(statePath, "utf-8");
483
925
  return JSON.parse(content);
@@ -487,7 +929,9 @@ function loadOnboardState(cwd) {
487
929
  }
488
930
  function clearOnboardState(cwd) {
489
931
  const statePath = getStatePath(cwd);
490
- if ((0, import_node_fs.existsSync)(statePath)) try {
491
- (0, import_node_fs.unlinkSync)(statePath);
492
- } catch {}
932
+ if ((0, import_node_fs.existsSync)(statePath)) {
933
+ try {
934
+ (0, import_node_fs.unlinkSync)(statePath);
935
+ } catch {}
936
+ }
493
937
  }