@specific.dev/cli 0.1.42 → 0.1.44

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 (58) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.__PAGE__.txt +4 -4
  4. package/dist/admin/__next._full.txt +11 -11
  5. package/dist/admin/__next._head.txt +4 -4
  6. package/dist/admin/__next._index.txt +4 -4
  7. package/dist/admin/__next._tree.txt +2 -2
  8. package/dist/admin/_next/static/chunks/{b71388016463cab2.js → 522cc1cbb935d4c6.js} +1 -1
  9. package/dist/admin/_next/static/chunks/62190944d690fc4e.js +4 -0
  10. package/dist/admin/_next/static/chunks/938d410f2031f3b1.css +3 -0
  11. package/dist/admin/_next/static/chunks/979e895ce202c4a3.js +1 -0
  12. package/dist/admin/_next/static/chunks/99f58b3b47071cc8.js +5 -0
  13. package/dist/admin/_next/static/chunks/9f53491ced2668ee.js +1 -0
  14. package/dist/admin/_next/static/chunks/a308451471d4cb39.js +1 -0
  15. package/dist/admin/_next/static/chunks/a4ff1b18f2f45e23.js +2 -0
  16. package/dist/admin/_next/static/chunks/a5c8191596f07db5.js +2 -0
  17. package/dist/admin/_next/static/chunks/a6dad97d9634a72d.js.map +1 -1
  18. package/dist/admin/_next/static/chunks/{ff1a16fafef87110.js → b2b4aada246f4749.js} +1 -1
  19. package/dist/admin/_next/static/chunks/bf65cbe8dc67cf90.js +5 -0
  20. package/dist/admin/_next/static/chunks/{turbopack-22b7312525502d51.js → turbopack-9e3df33047c5ecb2.js} +2 -2
  21. package/dist/admin/_not-found/__next._full.txt +9 -9
  22. package/dist/admin/_not-found/__next._head.txt +4 -4
  23. package/dist/admin/_not-found/__next._index.txt +4 -4
  24. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +2 -2
  25. package/dist/admin/_not-found/__next._not-found.txt +3 -3
  26. package/dist/admin/_not-found/__next._tree.txt +2 -2
  27. package/dist/admin/_not-found/index.html +1 -1
  28. package/dist/admin/_not-found/index.txt +9 -9
  29. package/dist/admin/databases/__next._full.txt +11 -11
  30. package/dist/admin/databases/__next._head.txt +4 -4
  31. package/dist/admin/databases/__next._index.txt +4 -4
  32. package/dist/admin/databases/__next._tree.txt +2 -2
  33. package/dist/admin/databases/__next.databases.__PAGE__.txt +4 -4
  34. package/dist/admin/databases/__next.databases.txt +3 -3
  35. package/dist/admin/databases/index.html +1 -1
  36. package/dist/admin/databases/index.txt +11 -11
  37. package/dist/admin/index.html +1 -1
  38. package/dist/admin/index.txt +11 -11
  39. package/dist/cli.js +1522 -1360
  40. package/dist/docs/index.md +1 -0
  41. package/dist/docs/integrations/nextjs.md +18 -0
  42. package/dist/docs/integrations/temporal.md +89 -0
  43. package/dist/docs/migrations/supabase.md +18 -0
  44. package/dist/docs/secrets-config.md +52 -8
  45. package/dist/docs/services.md +4 -2
  46. package/package.json +12 -4
  47. package/dist/admin/_next/static/chunks/1a608619ba3183f8.js +0 -5
  48. package/dist/admin/_next/static/chunks/237926899f121e8a.js +0 -2
  49. package/dist/admin/_next/static/chunks/2ca8ab35893ba132.css +0 -3
  50. package/dist/admin/_next/static/chunks/42730c0491633b9d.js +0 -5
  51. package/dist/admin/_next/static/chunks/465f799faf41e6df.js +0 -1
  52. package/dist/admin/_next/static/chunks/806bdb8e4a6a9b95.js +0 -4
  53. package/dist/admin/_next/static/chunks/9054c84ba21a4c14.js +0 -2
  54. package/dist/admin/_next/static/chunks/d2be314c3ece3fbe.js +0 -1
  55. package/dist/admin/_next/static/chunks/dde2c8e6322d1671.js +0 -1
  56. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → pbjYnqTudS-YVLwgwOgBz}/_buildManifest.js +0 -0
  57. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → pbjYnqTudS-YVLwgwOgBz}/_clientMiddlewareManifest.json +0 -0
  58. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → pbjYnqTudS-YVLwgwOgBz}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -11,9 +11,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
11
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
12
12
  throw Error('Dynamic require of "' + x + '" is not supported');
13
13
  });
14
+ var __esm = (fn, res) => function __init() {
15
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
+ };
14
17
  var __commonJS = (cb, mod) => function __require2() {
15
18
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
19
  };
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, { get: all[name], enumerable: true });
23
+ };
17
24
  var __copyProps = (to, from, except, desc) => {
18
25
  if (from && typeof from === "object" || typeof from === "function") {
19
26
  for (let key of __getOwnPropNames(from))
@@ -31,9 +38,715 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
38
  mod
32
39
  ));
33
40
 
34
- // ../../node_modules/hcl2-json-parser/dist/index.js
41
+ // node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
42
+ import fs4 from "node:fs";
43
+ function hasDockerEnv() {
44
+ try {
45
+ fs4.statSync("/.dockerenv");
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+ function hasDockerCGroup() {
52
+ try {
53
+ return fs4.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+ function isDocker() {
59
+ if (isDockerCached === void 0) {
60
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
61
+ }
62
+ return isDockerCached;
63
+ }
64
+ var isDockerCached;
65
+ var init_is_docker = __esm({
66
+ "node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js"() {
67
+ }
68
+ });
69
+
70
+ // node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
71
+ import fs5 from "node:fs";
72
+ function isInsideContainer() {
73
+ if (cachedResult === void 0) {
74
+ cachedResult = hasContainerEnv() || isDocker();
75
+ }
76
+ return cachedResult;
77
+ }
78
+ var cachedResult, hasContainerEnv;
79
+ var init_is_inside_container = __esm({
80
+ "node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js"() {
81
+ init_is_docker();
82
+ hasContainerEnv = () => {
83
+ try {
84
+ fs5.statSync("/run/.containerenv");
85
+ return true;
86
+ } catch {
87
+ return false;
88
+ }
89
+ };
90
+ }
91
+ });
92
+
93
+ // node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js
94
+ import process2 from "node:process";
95
+ import os3 from "node:os";
96
+ import fs6 from "node:fs";
97
+ var isWsl, is_wsl_default;
98
+ var init_is_wsl = __esm({
99
+ "node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js"() {
100
+ init_is_inside_container();
101
+ isWsl = () => {
102
+ if (process2.platform !== "linux") {
103
+ return false;
104
+ }
105
+ if (os3.release().toLowerCase().includes("microsoft")) {
106
+ if (isInsideContainer()) {
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+ try {
112
+ return fs6.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
113
+ } catch {
114
+ return false;
115
+ }
116
+ };
117
+ is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
118
+ }
119
+ });
120
+
121
+ // node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
122
+ import process3 from "node:process";
123
+ import { Buffer as Buffer2 } from "node:buffer";
124
+ import { promisify } from "node:util";
125
+ import childProcess from "node:child_process";
126
+ var execFile, powerShellPath, executePowerShell;
127
+ var init_powershell_utils = __esm({
128
+ "node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js"() {
129
+ execFile = promisify(childProcess.execFile);
130
+ powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
131
+ executePowerShell = async (command, options2 = {}) => {
132
+ const {
133
+ powerShellPath: psPath,
134
+ ...execFileOptions
135
+ } = options2;
136
+ const encodedCommand = executePowerShell.encodeCommand(command);
137
+ return execFile(
138
+ psPath ?? powerShellPath(),
139
+ [
140
+ ...executePowerShell.argumentsPrefix,
141
+ encodedCommand
142
+ ],
143
+ {
144
+ encoding: "utf8",
145
+ ...execFileOptions
146
+ }
147
+ );
148
+ };
149
+ executePowerShell.argumentsPrefix = [
150
+ "-NoProfile",
151
+ "-NonInteractive",
152
+ "-ExecutionPolicy",
153
+ "Bypass",
154
+ "-EncodedCommand"
155
+ ];
156
+ executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
157
+ executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
158
+ }
159
+ });
160
+
161
+ // node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/utilities.js
162
+ function parseMountPointFromConfig(content) {
163
+ for (const line of content.split("\n")) {
164
+ if (/^\s*#/.test(line)) {
165
+ continue;
166
+ }
167
+ const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
168
+ if (!match) {
169
+ continue;
170
+ }
171
+ return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
172
+ }
173
+ }
174
+ var init_utilities = __esm({
175
+ "node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/utilities.js"() {
176
+ }
177
+ });
178
+
179
+ // node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
180
+ import { promisify as promisify2 } from "node:util";
181
+ import childProcess2 from "node:child_process";
182
+ import fs7, { constants as fsConstants } from "node:fs/promises";
183
+ var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
184
+ var init_wsl_utils = __esm({
185
+ "node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
186
+ init_is_wsl();
187
+ init_powershell_utils();
188
+ init_utilities();
189
+ init_is_wsl();
190
+ execFile2 = promisify2(childProcess2.execFile);
191
+ wslDrivesMountPoint = /* @__PURE__ */ (() => {
192
+ const defaultMountPoint = "/mnt/";
193
+ let mountPoint;
194
+ return async function() {
195
+ if (mountPoint) {
196
+ return mountPoint;
197
+ }
198
+ const configFilePath = "/etc/wsl.conf";
199
+ let isConfigFileExists = false;
200
+ try {
201
+ await fs7.access(configFilePath, fsConstants.F_OK);
202
+ isConfigFileExists = true;
203
+ } catch {
204
+ }
205
+ if (!isConfigFileExists) {
206
+ return defaultMountPoint;
207
+ }
208
+ const configContent = await fs7.readFile(configFilePath, { encoding: "utf8" });
209
+ const parsedMountPoint = parseMountPointFromConfig(configContent);
210
+ if (parsedMountPoint === void 0) {
211
+ return defaultMountPoint;
212
+ }
213
+ mountPoint = parsedMountPoint;
214
+ mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
215
+ return mountPoint;
216
+ };
217
+ })();
218
+ powerShellPathFromWsl = async () => {
219
+ const mountPoint = await wslDrivesMountPoint();
220
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
221
+ };
222
+ powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
223
+ canAccessPowerShell = async () => {
224
+ canAccessPowerShellPromise ??= (async () => {
225
+ try {
226
+ const psPath = await powerShellPath2();
227
+ await fs7.access(psPath, fsConstants.X_OK);
228
+ return true;
229
+ } catch {
230
+ return false;
231
+ }
232
+ })();
233
+ return canAccessPowerShellPromise;
234
+ };
235
+ wslDefaultBrowser = async () => {
236
+ const psPath = await powerShellPath2();
237
+ const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
238
+ const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
239
+ return stdout.trim();
240
+ };
241
+ convertWslPathToWindows = async (path22) => {
242
+ if (/^[a-z]+:\/\//i.test(path22)) {
243
+ return path22;
244
+ }
245
+ try {
246
+ const { stdout } = await execFile2("wslpath", ["-aw", path22], { encoding: "utf8" });
247
+ return stdout.trim();
248
+ } catch {
249
+ return path22;
250
+ }
251
+ };
252
+ }
253
+ });
254
+
255
+ // node_modules/.pnpm/define-lazy-prop@3.0.0/node_modules/define-lazy-prop/index.js
256
+ function defineLazyProperty(object, propertyName, valueGetter) {
257
+ const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
258
+ Object.defineProperty(object, propertyName, {
259
+ configurable: true,
260
+ enumerable: true,
261
+ get() {
262
+ const result = valueGetter();
263
+ define(result);
264
+ return result;
265
+ },
266
+ set(value) {
267
+ define(value);
268
+ }
269
+ });
270
+ return object;
271
+ }
272
+ var init_define_lazy_prop = __esm({
273
+ "node_modules/.pnpm/define-lazy-prop@3.0.0/node_modules/define-lazy-prop/index.js"() {
274
+ }
275
+ });
276
+
277
+ // node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js
278
+ import { promisify as promisify3 } from "node:util";
279
+ import process4 from "node:process";
280
+ import { execFile as execFile3 } from "node:child_process";
281
+ async function defaultBrowserId() {
282
+ if (process4.platform !== "darwin") {
283
+ throw new Error("macOS only");
284
+ }
285
+ const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
286
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
287
+ const browserId = match?.groups.id ?? "com.apple.Safari";
288
+ if (browserId === "com.apple.safari") {
289
+ return "com.apple.Safari";
290
+ }
291
+ return browserId;
292
+ }
293
+ var execFileAsync;
294
+ var init_default_browser_id = __esm({
295
+ "node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js"() {
296
+ execFileAsync = promisify3(execFile3);
297
+ }
298
+ });
299
+
300
+ // node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js
301
+ import process5 from "node:process";
302
+ import { promisify as promisify4 } from "node:util";
303
+ import { execFile as execFile4, execFileSync } from "node:child_process";
304
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
305
+ if (process5.platform !== "darwin") {
306
+ throw new Error("macOS only");
307
+ }
308
+ const outputArguments = humanReadableOutput ? [] : ["-ss"];
309
+ const execOptions = {};
310
+ if (signal) {
311
+ execOptions.signal = signal;
312
+ }
313
+ const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
314
+ return stdout.trim();
315
+ }
316
+ var execFileAsync2;
317
+ var init_run_applescript = __esm({
318
+ "node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js"() {
319
+ execFileAsync2 = promisify4(execFile4);
320
+ }
321
+ });
322
+
323
+ // node_modules/.pnpm/bundle-name@4.1.0/node_modules/bundle-name/index.js
324
+ async function bundleName(bundleId) {
325
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
326
+ tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
327
+ }
328
+ var init_bundle_name = __esm({
329
+ "node_modules/.pnpm/bundle-name@4.1.0/node_modules/bundle-name/index.js"() {
330
+ init_run_applescript();
331
+ }
332
+ });
333
+
334
+ // node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js
335
+ import { promisify as promisify5 } from "node:util";
336
+ import { execFile as execFile5 } from "node:child_process";
337
+ async function defaultBrowser(_execFileAsync = execFileAsync3) {
338
+ const { stdout } = await _execFileAsync("reg", [
339
+ "QUERY",
340
+ " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
341
+ "/v",
342
+ "ProgId"
343
+ ]);
344
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
345
+ if (!match) {
346
+ throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
347
+ }
348
+ const { id } = match.groups;
349
+ const browser = windowsBrowserProgIds[id];
350
+ if (!browser) {
351
+ throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
352
+ }
353
+ return browser;
354
+ }
355
+ var execFileAsync3, windowsBrowserProgIds, _windowsBrowserProgIdMap, UnknownBrowserError;
356
+ var init_windows = __esm({
357
+ "node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js"() {
358
+ execFileAsync3 = promisify5(execFile5);
359
+ windowsBrowserProgIds = {
360
+ MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
361
+ // The missing `L` is correct.
362
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
363
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
364
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
365
+ ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
366
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
367
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
368
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
369
+ BraveHTML: { name: "Brave", id: "com.brave.Browser" },
370
+ BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
371
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
372
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
373
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
374
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
375
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
376
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
377
+ };
378
+ _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
379
+ UnknownBrowserError = class extends Error {
380
+ };
381
+ }
382
+ });
383
+
384
+ // node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js
385
+ import { promisify as promisify6 } from "node:util";
386
+ import process6 from "node:process";
387
+ import { execFile as execFile6 } from "node:child_process";
388
+ async function defaultBrowser2() {
389
+ if (process6.platform === "darwin") {
390
+ const id = await defaultBrowserId();
391
+ const name = await bundleName(id);
392
+ return { name, id };
393
+ }
394
+ if (process6.platform === "linux") {
395
+ const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
396
+ const id = stdout.trim();
397
+ const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
398
+ return { name, id };
399
+ }
400
+ if (process6.platform === "win32") {
401
+ return defaultBrowser();
402
+ }
403
+ throw new Error("Only macOS, Linux, and Windows are supported");
404
+ }
405
+ var execFileAsync4, titleize;
406
+ var init_default_browser = __esm({
407
+ "node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js"() {
408
+ init_default_browser_id();
409
+ init_bundle_name();
410
+ init_windows();
411
+ init_windows();
412
+ execFileAsync4 = promisify6(execFile6);
413
+ titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
414
+ }
415
+ });
416
+
417
+ // node_modules/.pnpm/is-in-ssh@1.0.0/node_modules/is-in-ssh/index.js
418
+ import process7 from "node:process";
419
+ var isInSsh, is_in_ssh_default;
420
+ var init_is_in_ssh = __esm({
421
+ "node_modules/.pnpm/is-in-ssh@1.0.0/node_modules/is-in-ssh/index.js"() {
422
+ isInSsh = Boolean(process7.env.SSH_CONNECTION || process7.env.SSH_CLIENT || process7.env.SSH_TTY);
423
+ is_in_ssh_default = isInSsh;
424
+ }
425
+ });
426
+
427
+ // node_modules/.pnpm/open@11.0.0/node_modules/open/index.js
428
+ var open_exports = {};
429
+ __export(open_exports, {
430
+ apps: () => apps,
431
+ default: () => open_default,
432
+ openApp: () => openApp
433
+ });
434
+ import process8 from "node:process";
435
+ import path4 from "node:path";
436
+ import { fileURLToPath } from "node:url";
437
+ import childProcess3 from "node:child_process";
438
+ import fs8, { constants as fsConstants2 } from "node:fs/promises";
439
+ function detectArchBinary(binary) {
440
+ if (typeof binary === "string" || Array.isArray(binary)) {
441
+ return binary;
442
+ }
443
+ const { [arch]: archBinary } = binary;
444
+ if (!archBinary) {
445
+ throw new Error(`${arch} is not supported`);
446
+ }
447
+ return archBinary;
448
+ }
449
+ function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
450
+ if (wsl && is_wsl_default) {
451
+ return detectArchBinary(wsl);
452
+ }
453
+ if (!platformBinary) {
454
+ throw new Error(`${platform2} is not supported`);
455
+ }
456
+ return detectArchBinary(platformBinary);
457
+ }
458
+ var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform2, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
459
+ var init_open = __esm({
460
+ "node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
461
+ init_wsl_utils();
462
+ init_powershell_utils();
463
+ init_define_lazy_prop();
464
+ init_default_browser();
465
+ init_is_inside_container();
466
+ init_is_in_ssh();
467
+ fallbackAttemptSymbol = Symbol("fallbackAttempt");
468
+ __dirname = import.meta.url ? path4.dirname(fileURLToPath(import.meta.url)) : "";
469
+ localXdgOpenPath = path4.join(__dirname, "xdg-open");
470
+ ({ platform: platform2, arch } = process8);
471
+ tryEachApp = async (apps2, opener) => {
472
+ if (apps2.length === 0) {
473
+ return;
474
+ }
475
+ const errors = [];
476
+ for (const app of apps2) {
477
+ try {
478
+ return await opener(app);
479
+ } catch (error) {
480
+ errors.push(error);
481
+ }
482
+ }
483
+ throw new AggregateError(errors, "Failed to open in all supported apps");
484
+ };
485
+ baseOpen = async (options2) => {
486
+ options2 = {
487
+ wait: false,
488
+ background: false,
489
+ newInstance: false,
490
+ allowNonzeroExitCode: false,
491
+ ...options2
492
+ };
493
+ const isFallbackAttempt = options2[fallbackAttemptSymbol] === true;
494
+ delete options2[fallbackAttemptSymbol];
495
+ if (Array.isArray(options2.app)) {
496
+ return tryEachApp(options2.app, (singleApp) => baseOpen({
497
+ ...options2,
498
+ app: singleApp,
499
+ [fallbackAttemptSymbol]: true
500
+ }));
501
+ }
502
+ let { name: app, arguments: appArguments = [] } = options2.app ?? {};
503
+ appArguments = [...appArguments];
504
+ if (Array.isArray(app)) {
505
+ return tryEachApp(app, (appName) => baseOpen({
506
+ ...options2,
507
+ app: {
508
+ name: appName,
509
+ arguments: appArguments
510
+ },
511
+ [fallbackAttemptSymbol]: true
512
+ }));
513
+ }
514
+ if (app === "browser" || app === "browserPrivate") {
515
+ const ids = {
516
+ "com.google.chrome": "chrome",
517
+ "google-chrome.desktop": "chrome",
518
+ "com.brave.browser": "brave",
519
+ "org.mozilla.firefox": "firefox",
520
+ "firefox.desktop": "firefox",
521
+ "com.microsoft.msedge": "edge",
522
+ "com.microsoft.edge": "edge",
523
+ "com.microsoft.edgemac": "edge",
524
+ "microsoft-edge.desktop": "edge",
525
+ "com.apple.safari": "safari"
526
+ };
527
+ const flags = {
528
+ chrome: "--incognito",
529
+ brave: "--incognito",
530
+ firefox: "--private-window",
531
+ edge: "--inPrivate"
532
+ // Safari doesn't support private mode via command line
533
+ };
534
+ let browser;
535
+ if (is_wsl_default) {
536
+ const progId = await wslDefaultBrowser();
537
+ const browserInfo = _windowsBrowserProgIdMap.get(progId);
538
+ browser = browserInfo ?? {};
539
+ } else {
540
+ browser = await defaultBrowser2();
541
+ }
542
+ if (browser.id in ids) {
543
+ const browserName = ids[browser.id.toLowerCase()];
544
+ if (app === "browserPrivate") {
545
+ if (browserName === "safari") {
546
+ throw new Error("Safari doesn't support opening in private mode via command line");
547
+ }
548
+ appArguments.push(flags[browserName]);
549
+ }
550
+ return baseOpen({
551
+ ...options2,
552
+ app: {
553
+ name: apps[browserName],
554
+ arguments: appArguments
555
+ }
556
+ });
557
+ }
558
+ throw new Error(`${browser.name} is not supported as a default browser`);
559
+ }
560
+ let command;
561
+ const cliArguments = [];
562
+ const childProcessOptions = {};
563
+ let shouldUseWindowsInWsl = false;
564
+ if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
565
+ shouldUseWindowsInWsl = await canAccessPowerShell();
566
+ }
567
+ if (platform2 === "darwin") {
568
+ command = "open";
569
+ if (options2.wait) {
570
+ cliArguments.push("--wait-apps");
571
+ }
572
+ if (options2.background) {
573
+ cliArguments.push("--background");
574
+ }
575
+ if (options2.newInstance) {
576
+ cliArguments.push("--new");
577
+ }
578
+ if (app) {
579
+ cliArguments.push("-a", app);
580
+ }
581
+ } else if (platform2 === "win32" || shouldUseWindowsInWsl) {
582
+ command = await powerShellPath2();
583
+ cliArguments.push(...executePowerShell.argumentsPrefix);
584
+ if (!is_wsl_default) {
585
+ childProcessOptions.windowsVerbatimArguments = true;
586
+ }
587
+ if (is_wsl_default && options2.target) {
588
+ options2.target = await convertWslPathToWindows(options2.target);
589
+ }
590
+ const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
591
+ if (options2.wait) {
592
+ encodedArguments.push("-Wait");
593
+ }
594
+ if (app) {
595
+ encodedArguments.push(executePowerShell.escapeArgument(app));
596
+ if (options2.target) {
597
+ appArguments.push(options2.target);
598
+ }
599
+ } else if (options2.target) {
600
+ encodedArguments.push(executePowerShell.escapeArgument(options2.target));
601
+ }
602
+ if (appArguments.length > 0) {
603
+ appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
604
+ encodedArguments.push("-ArgumentList", appArguments.join(","));
605
+ }
606
+ options2.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
607
+ if (!options2.wait) {
608
+ childProcessOptions.stdio = "ignore";
609
+ }
610
+ } else {
611
+ if (app) {
612
+ command = app;
613
+ } else {
614
+ const isBundled = !__dirname || __dirname === "/";
615
+ let exeLocalXdgOpen = false;
616
+ try {
617
+ await fs8.access(localXdgOpenPath, fsConstants2.X_OK);
618
+ exeLocalXdgOpen = true;
619
+ } catch {
620
+ }
621
+ const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
622
+ command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
623
+ }
624
+ if (appArguments.length > 0) {
625
+ cliArguments.push(...appArguments);
626
+ }
627
+ if (!options2.wait) {
628
+ childProcessOptions.stdio = "ignore";
629
+ childProcessOptions.detached = true;
630
+ }
631
+ }
632
+ if (platform2 === "darwin" && appArguments.length > 0) {
633
+ cliArguments.push("--args", ...appArguments);
634
+ }
635
+ if (options2.target) {
636
+ cliArguments.push(options2.target);
637
+ }
638
+ const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
639
+ if (options2.wait) {
640
+ return new Promise((resolve7, reject) => {
641
+ subprocess.once("error", reject);
642
+ subprocess.once("close", (exitCode) => {
643
+ if (!options2.allowNonzeroExitCode && exitCode !== 0) {
644
+ reject(new Error(`Exited with code ${exitCode}`));
645
+ return;
646
+ }
647
+ resolve7(subprocess);
648
+ });
649
+ });
650
+ }
651
+ if (isFallbackAttempt) {
652
+ return new Promise((resolve7, reject) => {
653
+ subprocess.once("error", reject);
654
+ subprocess.once("spawn", () => {
655
+ subprocess.once("close", (exitCode) => {
656
+ subprocess.off("error", reject);
657
+ if (exitCode !== 0) {
658
+ reject(new Error(`Exited with code ${exitCode}`));
659
+ return;
660
+ }
661
+ subprocess.unref();
662
+ resolve7(subprocess);
663
+ });
664
+ });
665
+ });
666
+ }
667
+ subprocess.unref();
668
+ return new Promise((resolve7, reject) => {
669
+ subprocess.once("error", reject);
670
+ subprocess.once("spawn", () => {
671
+ subprocess.off("error", reject);
672
+ resolve7(subprocess);
673
+ });
674
+ });
675
+ };
676
+ open = (target, options2) => {
677
+ if (typeof target !== "string") {
678
+ throw new TypeError("Expected a `target`");
679
+ }
680
+ return baseOpen({
681
+ ...options2,
682
+ target
683
+ });
684
+ };
685
+ openApp = (name, options2) => {
686
+ if (typeof name !== "string" && !Array.isArray(name)) {
687
+ throw new TypeError("Expected a valid `name`");
688
+ }
689
+ const { arguments: appArguments = [] } = options2 ?? {};
690
+ if (appArguments !== void 0 && appArguments !== null && !Array.isArray(appArguments)) {
691
+ throw new TypeError("Expected `appArguments` as Array type");
692
+ }
693
+ return baseOpen({
694
+ ...options2,
695
+ app: {
696
+ name,
697
+ arguments: appArguments
698
+ }
699
+ });
700
+ };
701
+ apps = {
702
+ browser: "browser",
703
+ browserPrivate: "browserPrivate"
704
+ };
705
+ defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
706
+ darwin: "google chrome",
707
+ win32: "chrome",
708
+ // `chromium-browser` is the older deb package name used by Ubuntu/Debian before snap.
709
+ linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
710
+ }, {
711
+ wsl: {
712
+ ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
713
+ x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
714
+ }
715
+ }));
716
+ defineLazyProperty(apps, "brave", () => detectPlatformBinary({
717
+ darwin: "brave browser",
718
+ win32: "brave",
719
+ linux: ["brave-browser", "brave"]
720
+ }, {
721
+ wsl: {
722
+ ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
723
+ x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
724
+ }
725
+ }));
726
+ defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
727
+ darwin: "firefox",
728
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
729
+ linux: "firefox"
730
+ }, {
731
+ wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
732
+ }));
733
+ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
734
+ darwin: "microsoft edge",
735
+ win32: "msedge",
736
+ linux: ["microsoft-edge", "microsoft-edge-dev"]
737
+ }, {
738
+ wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
739
+ }));
740
+ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
741
+ darwin: "Safari"
742
+ }));
743
+ open_default = open;
744
+ }
745
+ });
746
+
747
+ // node_modules/.pnpm/hcl2-json-parser@1.0.1/node_modules/hcl2-json-parser/dist/index.js
35
748
  var require_dist = __commonJS({
36
- "../../node_modules/hcl2-json-parser/dist/index.js"(exports, module) {
749
+ "node_modules/.pnpm/hcl2-json-parser@1.0.1/node_modules/hcl2-json-parser/dist/index.js"(exports, module) {
37
750
  "use strict";
38
751
  (function() {
39
752
  var $goVersion = "go1.18.10";
@@ -41,8 +754,8 @@ var require_dist = __commonJS({
41
754
  var $global, $module, $NaN = NaN;
42
755
  if ("undefined" != typeof window ? $global = window : "undefined" != typeof self ? $global = self : "undefined" != typeof global ? ($global = global).require = __require : $global = this, void 0 === $global || void 0 === $global.Array) throw new Error("no global object found");
43
756
  if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
44
- var fs26 = $global.require("fs");
45
- "object" == typeof fs26 && null !== fs26 && 0 !== Object.keys(fs26).length && ($global.fs = fs26);
757
+ var fs24 = $global.require("fs");
758
+ "object" == typeof fs24 && null !== fs24 && 0 !== Object.keys(fs24).length && ($global.fs = fs24);
46
759
  } catch (e) {
47
760
  }
48
761
  if (!$global.fs) {
@@ -182233,8 +182946,8 @@ var require_dist = __commonJS({
182233
182946
  import { Command } from "commander";
182234
182947
 
182235
182948
  // src/commands/init.tsx
182236
- import React2, { useState as useState2, useEffect as useEffect2 } from "react";
182237
- import { render as render2, Text as Text2, Box as Box2, useInput, useApp as useApp2 } from "ink";
182949
+ import React2, { useState, useEffect } from "react";
182950
+ import { render as render2, Text as Text2, Box as Box2, useInput, useApp } from "ink";
182238
182951
  import "ink-spinner";
182239
182952
  import * as fs10 from "fs";
182240
182953
  import * as path6 from "path";
@@ -182407,7 +183120,7 @@ function generateCertificate(domain, keys = []) {
182407
183120
 
182408
183121
  // src/lib/analytics/index.ts
182409
183122
  import { PostHog } from "posthog-node";
182410
- import * as os4 from "os";
183123
+ import * as os5 from "os";
182411
183124
  import * as crypto from "crypto";
182412
183125
 
182413
183126
  // src/lib/project/config.ts
@@ -182452,7 +183165,7 @@ function writeProjectId(projectId, projectDir = process.cwd()) {
182452
183165
  // src/lib/auth/credentials.ts
182453
183166
  import * as fs9 from "fs";
182454
183167
  import * as path5 from "path";
182455
- import * as os3 from "os";
183168
+ import * as os4 from "os";
182456
183169
 
182457
183170
  // src/lib/auth/errors.ts
182458
183171
  var RefreshTokenExpiredError = class extends Error {
@@ -182525,642 +183238,22 @@ async function refreshAccessToken(refreshToken) {
182525
183238
  }
182526
183239
 
182527
183240
  // src/lib/auth/login.tsx
182528
- import React, { useState, useEffect } from "react";
182529
- import { render, Box, Text, useApp } from "ink";
183241
+ import React from "react";
183242
+ import { render, Box, Text } from "ink";
182530
183243
  import Spinner from "ink-spinner";
182531
183244
 
182532
- // ../../node_modules/open/index.js
182533
- import process8 from "node:process";
182534
- import path3 from "node:path";
182535
- import { fileURLToPath } from "node:url";
182536
- import childProcess3 from "node:child_process";
182537
- import fs7, { constants as fsConstants2 } from "node:fs/promises";
182538
-
182539
- // ../../node_modules/wsl-utils/index.js
182540
- import { promisify as promisify2 } from "node:util";
182541
- import childProcess2 from "node:child_process";
182542
- import fs6, { constants as fsConstants } from "node:fs/promises";
182543
-
182544
- // ../../node_modules/is-wsl/index.js
182545
- import process2 from "node:process";
182546
- import os2 from "node:os";
182547
- import fs5 from "node:fs";
182548
-
182549
- // ../../node_modules/is-inside-container/index.js
182550
- import fs4 from "node:fs";
182551
-
182552
- // ../../node_modules/is-docker/index.js
182553
- import fs3 from "node:fs";
182554
- var isDockerCached;
182555
- function hasDockerEnv() {
182556
- try {
182557
- fs3.statSync("/.dockerenv");
182558
- return true;
182559
- } catch {
182560
- return false;
182561
- }
182562
- }
182563
- function hasDockerCGroup() {
182564
- try {
182565
- return fs3.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
182566
- } catch {
182567
- return false;
182568
- }
182569
- }
182570
- function isDocker() {
182571
- if (isDockerCached === void 0) {
182572
- isDockerCached = hasDockerEnv() || hasDockerCGroup();
182573
- }
182574
- return isDockerCached;
182575
- }
182576
-
182577
- // ../../node_modules/is-inside-container/index.js
182578
- var cachedResult;
182579
- var hasContainerEnv = () => {
182580
- try {
182581
- fs4.statSync("/run/.containerenv");
182582
- return true;
182583
- } catch {
182584
- return false;
182585
- }
182586
- };
182587
- function isInsideContainer() {
182588
- if (cachedResult === void 0) {
182589
- cachedResult = hasContainerEnv() || isDocker();
182590
- }
182591
- return cachedResult;
182592
- }
182593
-
182594
- // ../../node_modules/is-wsl/index.js
182595
- var isWsl = () => {
182596
- if (process2.platform !== "linux") {
182597
- return false;
182598
- }
182599
- if (os2.release().toLowerCase().includes("microsoft")) {
182600
- if (isInsideContainer()) {
182601
- return false;
182602
- }
182603
- return true;
182604
- }
182605
- try {
182606
- return fs5.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
182607
- } catch {
182608
- return false;
182609
- }
182610
- };
182611
- var is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
182612
-
182613
- // ../../node_modules/powershell-utils/index.js
182614
- import process3 from "node:process";
182615
- import { Buffer as Buffer2 } from "node:buffer";
182616
- import { promisify } from "node:util";
182617
- import childProcess from "node:child_process";
182618
- var execFile = promisify(childProcess.execFile);
182619
- var powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
182620
- var executePowerShell = async (command, options2 = {}) => {
182621
- const {
182622
- powerShellPath: psPath,
182623
- ...execFileOptions
182624
- } = options2;
182625
- const encodedCommand = executePowerShell.encodeCommand(command);
182626
- return execFile(
182627
- psPath ?? powerShellPath(),
182628
- [
182629
- ...executePowerShell.argumentsPrefix,
182630
- encodedCommand
182631
- ],
182632
- {
182633
- encoding: "utf8",
182634
- ...execFileOptions
182635
- }
182636
- );
182637
- };
182638
- executePowerShell.argumentsPrefix = [
182639
- "-NoProfile",
182640
- "-NonInteractive",
182641
- "-ExecutionPolicy",
182642
- "Bypass",
182643
- "-EncodedCommand"
182644
- ];
182645
- executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
182646
- executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
182647
-
182648
- // ../../node_modules/wsl-utils/utilities.js
182649
- function parseMountPointFromConfig(content) {
182650
- for (const line of content.split("\n")) {
182651
- if (/^\s*#/.test(line)) {
182652
- continue;
182653
- }
182654
- const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
182655
- if (!match) {
182656
- continue;
182657
- }
182658
- return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
182659
- }
182660
- }
182661
-
182662
- // ../../node_modules/wsl-utils/index.js
182663
- var execFile2 = promisify2(childProcess2.execFile);
182664
- var wslDrivesMountPoint = /* @__PURE__ */ (() => {
182665
- const defaultMountPoint = "/mnt/";
182666
- let mountPoint;
182667
- return async function() {
182668
- if (mountPoint) {
182669
- return mountPoint;
182670
- }
182671
- const configFilePath = "/etc/wsl.conf";
182672
- let isConfigFileExists = false;
182673
- try {
182674
- await fs6.access(configFilePath, fsConstants.F_OK);
182675
- isConfigFileExists = true;
182676
- } catch {
182677
- }
182678
- if (!isConfigFileExists) {
182679
- return defaultMountPoint;
182680
- }
182681
- const configContent = await fs6.readFile(configFilePath, { encoding: "utf8" });
182682
- const parsedMountPoint = parseMountPointFromConfig(configContent);
182683
- if (parsedMountPoint === void 0) {
182684
- return defaultMountPoint;
182685
- }
182686
- mountPoint = parsedMountPoint;
182687
- mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
182688
- return mountPoint;
182689
- };
182690
- })();
182691
- var powerShellPathFromWsl = async () => {
182692
- const mountPoint = await wslDrivesMountPoint();
182693
- return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
182694
- };
182695
- var powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
182696
- var canAccessPowerShellPromise;
182697
- var canAccessPowerShell = async () => {
182698
- canAccessPowerShellPromise ??= (async () => {
182699
- try {
182700
- const psPath = await powerShellPath2();
182701
- await fs6.access(psPath, fsConstants.X_OK);
182702
- return true;
182703
- } catch {
182704
- return false;
182705
- }
182706
- })();
182707
- return canAccessPowerShellPromise;
182708
- };
182709
- var wslDefaultBrowser = async () => {
182710
- const psPath = await powerShellPath2();
182711
- const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
182712
- const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
182713
- return stdout.trim();
182714
- };
182715
- var convertWslPathToWindows = async (path22) => {
182716
- if (/^[a-z]+:\/\//i.test(path22)) {
182717
- return path22;
182718
- }
182719
- try {
182720
- const { stdout } = await execFile2("wslpath", ["-aw", path22], { encoding: "utf8" });
182721
- return stdout.trim();
182722
- } catch {
182723
- return path22;
182724
- }
182725
- };
182726
-
182727
- // ../../node_modules/define-lazy-prop/index.js
182728
- function defineLazyProperty(object, propertyName, valueGetter) {
182729
- const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
182730
- Object.defineProperty(object, propertyName, {
182731
- configurable: true,
182732
- enumerable: true,
182733
- get() {
182734
- const result = valueGetter();
182735
- define(result);
182736
- return result;
182737
- },
182738
- set(value) {
182739
- define(value);
182740
- }
182741
- });
182742
- return object;
182743
- }
182744
-
182745
- // ../../node_modules/default-browser/index.js
182746
- import { promisify as promisify6 } from "node:util";
182747
- import process6 from "node:process";
182748
- import { execFile as execFile6 } from "node:child_process";
182749
-
182750
- // ../../node_modules/default-browser-id/index.js
182751
- import { promisify as promisify3 } from "node:util";
182752
- import process4 from "node:process";
182753
- import { execFile as execFile3 } from "node:child_process";
182754
- var execFileAsync = promisify3(execFile3);
182755
- async function defaultBrowserId() {
182756
- if (process4.platform !== "darwin") {
182757
- throw new Error("macOS only");
182758
- }
182759
- const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
182760
- const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
182761
- const browserId = match?.groups.id ?? "com.apple.Safari";
182762
- if (browserId === "com.apple.safari") {
182763
- return "com.apple.Safari";
182764
- }
182765
- return browserId;
182766
- }
182767
-
182768
- // ../../node_modules/run-applescript/index.js
182769
- import process5 from "node:process";
182770
- import { promisify as promisify4 } from "node:util";
182771
- import { execFile as execFile4, execFileSync } from "node:child_process";
182772
- var execFileAsync2 = promisify4(execFile4);
182773
- async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
182774
- if (process5.platform !== "darwin") {
182775
- throw new Error("macOS only");
182776
- }
182777
- const outputArguments = humanReadableOutput ? [] : ["-ss"];
182778
- const execOptions = {};
182779
- if (signal) {
182780
- execOptions.signal = signal;
182781
- }
182782
- const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
182783
- return stdout.trim();
182784
- }
182785
-
182786
- // ../../node_modules/bundle-name/index.js
182787
- async function bundleName(bundleId) {
182788
- return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
182789
- tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
182790
- }
182791
-
182792
- // ../../node_modules/default-browser/windows.js
182793
- import { promisify as promisify5 } from "node:util";
182794
- import { execFile as execFile5 } from "node:child_process";
182795
- var execFileAsync3 = promisify5(execFile5);
182796
- var windowsBrowserProgIds = {
182797
- MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
182798
- // The missing `L` is correct.
182799
- MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
182800
- MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
182801
- AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
182802
- ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
182803
- ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
182804
- ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
182805
- ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
182806
- BraveHTML: { name: "Brave", id: "com.brave.Browser" },
182807
- BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
182808
- BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
182809
- BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
182810
- FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
182811
- OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
182812
- VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
182813
- "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
182814
- };
182815
- var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
182816
- var UnknownBrowserError = class extends Error {
182817
- };
182818
- async function defaultBrowser(_execFileAsync = execFileAsync3) {
182819
- const { stdout } = await _execFileAsync("reg", [
182820
- "QUERY",
182821
- " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
182822
- "/v",
182823
- "ProgId"
182824
- ]);
182825
- const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
182826
- if (!match) {
182827
- throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
182828
- }
182829
- const { id } = match.groups;
182830
- const browser = windowsBrowserProgIds[id];
182831
- if (!browser) {
182832
- throw new UnknownBrowserError(`Unknown browser ID: ${id}`);
182833
- }
182834
- return browser;
182835
- }
182836
-
182837
- // ../../node_modules/default-browser/index.js
182838
- var execFileAsync4 = promisify6(execFile6);
182839
- var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
182840
- async function defaultBrowser2() {
182841
- if (process6.platform === "darwin") {
182842
- const id = await defaultBrowserId();
182843
- const name = await bundleName(id);
182844
- return { name, id };
182845
- }
182846
- if (process6.platform === "linux") {
182847
- const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
182848
- const id = stdout.trim();
182849
- const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
182850
- return { name, id };
182851
- }
182852
- if (process6.platform === "win32") {
182853
- return defaultBrowser();
182854
- }
182855
- throw new Error("Only macOS, Linux, and Windows are supported");
182856
- }
182857
-
182858
- // ../../node_modules/is-in-ssh/index.js
182859
- import process7 from "node:process";
182860
- var isInSsh = Boolean(process7.env.SSH_CONNECTION || process7.env.SSH_CLIENT || process7.env.SSH_TTY);
182861
- var is_in_ssh_default = isInSsh;
182862
-
182863
- // ../../node_modules/open/index.js
182864
- var fallbackAttemptSymbol = Symbol("fallbackAttempt");
182865
- var __dirname = import.meta.url ? path3.dirname(fileURLToPath(import.meta.url)) : "";
182866
- var localXdgOpenPath = path3.join(__dirname, "xdg-open");
182867
- var { platform: platform2, arch } = process8;
182868
- var tryEachApp = async (apps2, opener) => {
182869
- if (apps2.length === 0) {
182870
- return;
182871
- }
182872
- const errors = [];
182873
- for (const app of apps2) {
182874
- try {
182875
- return await opener(app);
182876
- } catch (error) {
182877
- errors.push(error);
182878
- }
182879
- }
182880
- throw new AggregateError(errors, "Failed to open in all supported apps");
182881
- };
182882
- var baseOpen = async (options2) => {
182883
- options2 = {
182884
- wait: false,
182885
- background: false,
182886
- newInstance: false,
182887
- allowNonzeroExitCode: false,
182888
- ...options2
182889
- };
182890
- const isFallbackAttempt = options2[fallbackAttemptSymbol] === true;
182891
- delete options2[fallbackAttemptSymbol];
182892
- if (Array.isArray(options2.app)) {
182893
- return tryEachApp(options2.app, (singleApp) => baseOpen({
182894
- ...options2,
182895
- app: singleApp,
182896
- [fallbackAttemptSymbol]: true
182897
- }));
182898
- }
182899
- let { name: app, arguments: appArguments = [] } = options2.app ?? {};
182900
- appArguments = [...appArguments];
182901
- if (Array.isArray(app)) {
182902
- return tryEachApp(app, (appName) => baseOpen({
182903
- ...options2,
182904
- app: {
182905
- name: appName,
182906
- arguments: appArguments
182907
- },
182908
- [fallbackAttemptSymbol]: true
182909
- }));
182910
- }
182911
- if (app === "browser" || app === "browserPrivate") {
182912
- const ids = {
182913
- "com.google.chrome": "chrome",
182914
- "google-chrome.desktop": "chrome",
182915
- "com.brave.browser": "brave",
182916
- "org.mozilla.firefox": "firefox",
182917
- "firefox.desktop": "firefox",
182918
- "com.microsoft.msedge": "edge",
182919
- "com.microsoft.edge": "edge",
182920
- "com.microsoft.edgemac": "edge",
182921
- "microsoft-edge.desktop": "edge",
182922
- "com.apple.safari": "safari"
182923
- };
182924
- const flags = {
182925
- chrome: "--incognito",
182926
- brave: "--incognito",
182927
- firefox: "--private-window",
182928
- edge: "--inPrivate"
182929
- // Safari doesn't support private mode via command line
182930
- };
182931
- let browser;
182932
- if (is_wsl_default) {
182933
- const progId = await wslDefaultBrowser();
182934
- const browserInfo = _windowsBrowserProgIdMap.get(progId);
182935
- browser = browserInfo ?? {};
182936
- } else {
182937
- browser = await defaultBrowser2();
182938
- }
182939
- if (browser.id in ids) {
182940
- const browserName = ids[browser.id.toLowerCase()];
182941
- if (app === "browserPrivate") {
182942
- if (browserName === "safari") {
182943
- throw new Error("Safari doesn't support opening in private mode via command line");
182944
- }
182945
- appArguments.push(flags[browserName]);
182946
- }
182947
- return baseOpen({
182948
- ...options2,
182949
- app: {
182950
- name: apps[browserName],
182951
- arguments: appArguments
182952
- }
182953
- });
182954
- }
182955
- throw new Error(`${browser.name} is not supported as a default browser`);
182956
- }
182957
- let command;
182958
- const cliArguments = [];
182959
- const childProcessOptions = {};
182960
- let shouldUseWindowsInWsl = false;
182961
- if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
182962
- shouldUseWindowsInWsl = await canAccessPowerShell();
182963
- }
182964
- if (platform2 === "darwin") {
182965
- command = "open";
182966
- if (options2.wait) {
182967
- cliArguments.push("--wait-apps");
182968
- }
182969
- if (options2.background) {
182970
- cliArguments.push("--background");
182971
- }
182972
- if (options2.newInstance) {
182973
- cliArguments.push("--new");
182974
- }
182975
- if (app) {
182976
- cliArguments.push("-a", app);
182977
- }
182978
- } else if (platform2 === "win32" || shouldUseWindowsInWsl) {
182979
- command = await powerShellPath2();
182980
- cliArguments.push(...executePowerShell.argumentsPrefix);
182981
- if (!is_wsl_default) {
182982
- childProcessOptions.windowsVerbatimArguments = true;
182983
- }
182984
- if (is_wsl_default && options2.target) {
182985
- options2.target = await convertWslPathToWindows(options2.target);
182986
- }
182987
- const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
182988
- if (options2.wait) {
182989
- encodedArguments.push("-Wait");
182990
- }
182991
- if (app) {
182992
- encodedArguments.push(executePowerShell.escapeArgument(app));
182993
- if (options2.target) {
182994
- appArguments.push(options2.target);
182995
- }
182996
- } else if (options2.target) {
182997
- encodedArguments.push(executePowerShell.escapeArgument(options2.target));
182998
- }
182999
- if (appArguments.length > 0) {
183000
- appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
183001
- encodedArguments.push("-ArgumentList", appArguments.join(","));
183002
- }
183003
- options2.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
183004
- if (!options2.wait) {
183005
- childProcessOptions.stdio = "ignore";
183006
- }
183007
- } else {
183008
- if (app) {
183009
- command = app;
183010
- } else {
183011
- const isBundled = !__dirname || __dirname === "/";
183012
- let exeLocalXdgOpen = false;
183013
- try {
183014
- await fs7.access(localXdgOpenPath, fsConstants2.X_OK);
183015
- exeLocalXdgOpen = true;
183016
- } catch {
183017
- }
183018
- const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
183019
- command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
183020
- }
183021
- if (appArguments.length > 0) {
183022
- cliArguments.push(...appArguments);
183023
- }
183024
- if (!options2.wait) {
183025
- childProcessOptions.stdio = "ignore";
183026
- childProcessOptions.detached = true;
183027
- }
183028
- }
183029
- if (platform2 === "darwin" && appArguments.length > 0) {
183030
- cliArguments.push("--args", ...appArguments);
183031
- }
183032
- if (options2.target) {
183033
- cliArguments.push(options2.target);
183034
- }
183035
- const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
183036
- if (options2.wait) {
183037
- return new Promise((resolve7, reject) => {
183038
- subprocess.once("error", reject);
183039
- subprocess.once("close", (exitCode) => {
183040
- if (!options2.allowNonzeroExitCode && exitCode !== 0) {
183041
- reject(new Error(`Exited with code ${exitCode}`));
183042
- return;
183043
- }
183044
- resolve7(subprocess);
183045
- });
183046
- });
183047
- }
183048
- if (isFallbackAttempt) {
183049
- return new Promise((resolve7, reject) => {
183050
- subprocess.once("error", reject);
183051
- subprocess.once("spawn", () => {
183052
- subprocess.once("close", (exitCode) => {
183053
- subprocess.off("error", reject);
183054
- if (exitCode !== 0) {
183055
- reject(new Error(`Exited with code ${exitCode}`));
183056
- return;
183057
- }
183058
- subprocess.unref();
183059
- resolve7(subprocess);
183060
- });
183061
- });
183062
- });
183063
- }
183064
- subprocess.unref();
183065
- return new Promise((resolve7, reject) => {
183066
- subprocess.once("error", reject);
183067
- subprocess.once("spawn", () => {
183068
- subprocess.off("error", reject);
183069
- resolve7(subprocess);
183070
- });
183071
- });
183072
- };
183073
- var open = (target, options2) => {
183074
- if (typeof target !== "string") {
183075
- throw new TypeError("Expected a `target`");
183076
- }
183077
- return baseOpen({
183078
- ...options2,
183079
- target
183080
- });
183081
- };
183082
- function detectArchBinary(binary) {
183083
- if (typeof binary === "string" || Array.isArray(binary)) {
183084
- return binary;
183085
- }
183086
- const { [arch]: archBinary } = binary;
183087
- if (!archBinary) {
183088
- throw new Error(`${arch} is not supported`);
183089
- }
183090
- return archBinary;
183091
- }
183092
- function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
183093
- if (wsl && is_wsl_default) {
183094
- return detectArchBinary(wsl);
183095
- }
183096
- if (!platformBinary) {
183097
- throw new Error(`${platform2} is not supported`);
183098
- }
183099
- return detectArchBinary(platformBinary);
183100
- }
183101
- var apps = {
183102
- browser: "browser",
183103
- browserPrivate: "browserPrivate"
183104
- };
183105
- defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
183106
- darwin: "google chrome",
183107
- win32: "chrome",
183108
- // `chromium-browser` is the older deb package name used by Ubuntu/Debian before snap.
183109
- linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
183110
- }, {
183111
- wsl: {
183112
- ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
183113
- x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
183114
- }
183115
- }));
183116
- defineLazyProperty(apps, "brave", () => detectPlatformBinary({
183117
- darwin: "brave browser",
183118
- win32: "brave",
183119
- linux: ["brave-browser", "brave"]
183120
- }, {
183121
- wsl: {
183122
- ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
183123
- x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
183124
- }
183125
- }));
183126
- defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
183127
- darwin: "firefox",
183128
- win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
183129
- linux: "firefox"
183130
- }, {
183131
- wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
183132
- }));
183133
- defineLazyProperty(apps, "edge", () => detectPlatformBinary({
183134
- darwin: "microsoft edge",
183135
- win32: "msedge",
183136
- linux: ["microsoft-edge", "microsoft-edge-dev"]
183137
- }, {
183138
- wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
183139
- }));
183140
- defineLazyProperty(apps, "safari", () => detectPlatformBinary({
183141
- darwin: "Safari"
183142
- }));
183143
- var open_default = open;
183144
-
183145
183245
  // src/lib/dev/debug-logger.ts
183146
- import * as fs8 from "fs";
183147
- import * as path4 from "path";
183246
+ import * as fs3 from "fs";
183247
+ import * as os2 from "os";
183248
+ import * as path3 from "path";
183148
183249
  var DEBUG_LOG_PATH = ".specific/debug.log";
183149
183250
  var logStream = null;
183150
- function getLogStream() {
183151
- if (logStream) {
183152
- return logStream;
183153
- }
183154
- const logPath = path4.join(process.cwd(), DEBUG_LOG_PATH);
183155
- fs8.mkdirSync(path4.dirname(logPath), { recursive: true });
183156
- logStream = fs8.createWriteStream(logPath, { flags: "a" });
183157
- return logStream;
183158
- }
183159
183251
  function writeLog(source, message) {
183160
- const stream = getLogStream();
183252
+ const logPath = path3.join(os2.homedir(), DEBUG_LOG_PATH);
183253
+ fs3.mkdirSync(path3.dirname(logPath), { recursive: true });
183161
183254
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
183162
183255
  const line = message.endsWith("\n") ? message : message + "\n";
183163
- stream.write(`[${timestamp}] [${source}] ${line}`);
183256
+ fs3.appendFileSync(logPath, `[${timestamp}] [${source}] ${line}`);
183164
183257
  }
183165
183258
  function pipeProcess(source, proc) {
183166
183259
  proc.stdout?.on("data", (data) => {
@@ -183471,127 +183564,169 @@ var ApiClient = class {
183471
183564
  }
183472
183565
  };
183473
183566
 
183567
+ // src/lib/auth/login-flow.ts
183568
+ function runLoginFlow(options2) {
183569
+ const { setState, isReauthentication } = options2;
183570
+ let cancelled = false;
183571
+ async function run() {
183572
+ try {
183573
+ setState({ phase: "initiating" });
183574
+ writeLog("auth", "Starting login flow");
183575
+ if (isReauthentication) {
183576
+ clearUserCredentials();
183577
+ }
183578
+ writeLog("auth", "Initiating device authorization with WorkOS");
183579
+ const deviceAuth = await initiateDeviceAuthorization();
183580
+ writeLog(
183581
+ "auth",
183582
+ `Device authorization received: user_code=${deviceAuth.user_code}`
183583
+ );
183584
+ if (cancelled) return;
183585
+ setState({
183586
+ phase: "waiting-for-browser",
183587
+ userCode: deviceAuth.user_code,
183588
+ verificationUri: deviceAuth.verification_uri_complete
183589
+ });
183590
+ const { default: open3 } = await Promise.resolve().then(() => (init_open(), open_exports));
183591
+ await open3(deviceAuth.verification_uri_complete);
183592
+ const token = await pollUntilToken(deviceAuth, () => cancelled);
183593
+ if (cancelled || !token) return;
183594
+ writeLog("auth", "Fetching user info from platform API...");
183595
+ const client2 = new ApiClient(token.access_token);
183596
+ const user = await client2.getMe();
183597
+ writeLog("auth", `User info received: id=${user.id}`);
183598
+ if (cancelled) return;
183599
+ writeLog("auth", "Writing credentials to disk...");
183600
+ writeUserCredentials({
183601
+ accessToken: token.access_token,
183602
+ refreshToken: token.refresh_token,
183603
+ expiresAt: Date.now() + token.expires_in * 1e3,
183604
+ userId: user.id
183605
+ });
183606
+ writeLog("auth", "Credentials written successfully");
183607
+ setState({ phase: "success", email: token.user.email });
183608
+ writeLog("auth", "Login flow completed successfully");
183609
+ } catch (err) {
183610
+ if (cancelled) return;
183611
+ const message = err instanceof Error ? err.message : String(err);
183612
+ writeLog("auth", `Login error: ${message}`);
183613
+ setState({ phase: "error", message });
183614
+ }
183615
+ }
183616
+ run();
183617
+ return {
183618
+ cancel: () => {
183619
+ cancelled = true;
183620
+ }
183621
+ };
183622
+ }
183623
+ async function pollUntilToken(deviceAuth, isCancelled) {
183624
+ let interval = deviceAuth.interval * 1e3;
183625
+ const expiresAt = Date.now() + deviceAuth.expires_in * 1e3;
183626
+ while (!isCancelled() && Date.now() < expiresAt) {
183627
+ await sleep(interval);
183628
+ if (isCancelled()) return null;
183629
+ writeLog(
183630
+ "auth",
183631
+ `Polling for token (timeRemaining=${Math.round((expiresAt - Date.now()) / 1e3)}s)`
183632
+ );
183633
+ const response = await pollForToken(deviceAuth.device_code);
183634
+ writeLog(
183635
+ "auth",
183636
+ `Poll response: ${JSON.stringify(response).substring(0, 200)}`
183637
+ );
183638
+ if (!("error" in response)) {
183639
+ writeLog("auth", "Token received successfully from WorkOS");
183640
+ return response;
183641
+ }
183642
+ if (response.error === "slow_down") {
183643
+ interval += 1e3;
183644
+ writeLog("auth", `Slowing down, new interval: ${interval}ms`);
183645
+ } else if (response.error !== "authorization_pending") {
183646
+ throw new Error(`Authentication failed: ${response.error}`);
183647
+ }
183648
+ }
183649
+ if (!isCancelled()) {
183650
+ throw new Error("Authentication timed out. Please try again.");
183651
+ }
183652
+ return null;
183653
+ }
183654
+ function sleep(ms) {
183655
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
183656
+ }
183657
+
183474
183658
  // src/lib/auth/login.tsx
183659
+ function LoginUI({
183660
+ state,
183661
+ isReauthentication
183662
+ }) {
183663
+ if (state.phase === "error") {
183664
+ return /* @__PURE__ */ React.createElement(Text, { color: "red" }, "Error: ", state.message);
183665
+ }
183666
+ if (state.phase === "success") {
183667
+ return /* @__PURE__ */ React.createElement(Text, { color: "green" }, isReauthentication ? "Re-authenticated" : "Logged in", " as ", state.email);
183668
+ }
183669
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, isReauthentication && /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "Session expired. Please log in again."), /* @__PURE__ */ React.createElement(Text, { bold: true }, "Log in to Specific"), state.phase === "waiting-for-browser" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, "Your authentication code:", " ", /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, state.userCode))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "If the browser didn't open, visit: ", state.verificationUri)) : /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Initiating login...")));
183670
+ }
183475
183671
  function performLogin(options2 = {}) {
183476
183672
  return new Promise((resolve7) => {
183673
+ let currentState = { phase: "initiating" };
183674
+ let flowHandle;
183477
183675
  const instance = render(
183478
183676
  /* @__PURE__ */ React.createElement(
183479
- LoginFlow,
183677
+ LoginUI,
183480
183678
  {
183481
- isReauthentication: options2.isReauthentication,
183482
- onComplete: (result) => {
183483
- instance.unmount();
183484
- resolve7(result);
183485
- }
183679
+ state: currentState,
183680
+ isReauthentication: options2.isReauthentication
183486
183681
  }
183487
183682
  )
183488
183683
  );
183489
- });
183490
- }
183491
- function LoginFlow({ isReauthentication, onComplete }) {
183492
- const { exit } = useApp();
183493
- const [phase, setPhase] = useState("initiating");
183494
- const [userCode, setUserCode] = useState();
183495
- const [verificationUri, setVerificationUri] = useState();
183496
- const [userEmail, setUserEmail] = useState();
183497
- const [error, setError] = useState();
183498
- useEffect(() => {
183499
- if (phase !== "initiating") return;
183500
- let cancelled = false;
183501
- let pollTimeoutId;
183502
- async function startLogin() {
183503
- try {
183504
- if (isReauthentication) {
183505
- clearUserCredentials();
183506
- }
183507
- const deviceAuth = await initiateDeviceAuthorization();
183508
- if (cancelled) return;
183509
- setUserCode(deviceAuth.user_code);
183510
- setVerificationUri(deviceAuth.verification_uri_complete);
183511
- setPhase("waiting");
183512
- await open_default(deviceAuth.verification_uri_complete);
183513
- let pollInterval = deviceAuth.interval * 1e3;
183514
- const expiresAt = Date.now() + deviceAuth.expires_in * 1e3;
183515
- const poll = async () => {
183516
- if (cancelled || Date.now() > expiresAt) {
183517
- if (!cancelled && Date.now() > expiresAt) {
183518
- setError("Authentication timed out. Please try again.");
183519
- setPhase("error");
183520
- }
183521
- return;
183522
- }
183523
- try {
183524
- const response = await pollForToken(deviceAuth.device_code);
183525
- if (cancelled) return;
183526
- if ("error" in response) {
183527
- if (response.error === "authorization_pending") {
183528
- pollTimeoutId = setTimeout(poll, pollInterval);
183529
- } else if (response.error === "slow_down") {
183530
- pollInterval += 1e3;
183531
- pollTimeoutId = setTimeout(poll, pollInterval);
183532
- } else {
183533
- setError(`Authentication failed: ${response.error}`);
183534
- setPhase("error");
183535
- }
183536
- } else {
183537
- const successResponse = response;
183538
- const expiresAt2 = Date.now() + successResponse.expires_in * 1e3;
183539
- const client2 = new ApiClient(successResponse.access_token);
183540
- const user = await client2.getMe();
183541
- writeUserCredentials({
183542
- accessToken: successResponse.access_token,
183543
- refreshToken: successResponse.refresh_token,
183544
- expiresAt: expiresAt2,
183545
- userId: user.id
183546
- });
183547
- setUserEmail(successResponse.user.email);
183548
- setPhase("success");
183684
+ const cleanup = () => {
183685
+ flowHandle?.cancel();
183686
+ instance.unmount();
183687
+ };
183688
+ const handleExit = () => {
183689
+ cleanup();
183690
+ process.exit(0);
183691
+ };
183692
+ process.on("SIGINT", handleExit);
183693
+ process.on("SIGTERM", handleExit);
183694
+ flowHandle = runLoginFlow({
183695
+ isReauthentication: options2.isReauthentication,
183696
+ setState: (newState) => {
183697
+ currentState = newState;
183698
+ instance.rerender(
183699
+ /* @__PURE__ */ React.createElement(
183700
+ LoginUI,
183701
+ {
183702
+ state: currentState,
183703
+ isReauthentication: options2.isReauthentication
183549
183704
  }
183550
- } catch (err) {
183551
- if (cancelled) return;
183552
- setError(err instanceof Error ? err.message : String(err));
183553
- setPhase("error");
183554
- }
183555
- };
183556
- pollTimeoutId = setTimeout(poll, pollInterval);
183557
- } catch (err) {
183558
- if (cancelled) return;
183559
- setError(err instanceof Error ? err.message : String(err));
183560
- setPhase("error");
183705
+ )
183706
+ );
183707
+ if (newState.phase === "success") {
183708
+ setTimeout(() => {
183709
+ process.off("SIGINT", handleExit);
183710
+ process.off("SIGTERM", handleExit);
183711
+ instance.unmount();
183712
+ resolve7({ success: true, userEmail: newState.email });
183713
+ }, 100);
183714
+ } else if (newState.phase === "error") {
183715
+ setTimeout(() => {
183716
+ process.off("SIGINT", handleExit);
183717
+ process.off("SIGTERM", handleExit);
183718
+ instance.unmount();
183719
+ resolve7({ success: false, error: new Error(newState.message) });
183720
+ }, 100);
183721
+ }
183561
183722
  }
183562
- }
183563
- startLogin();
183564
- return () => {
183565
- cancelled = true;
183566
- if (pollTimeoutId) clearTimeout(pollTimeoutId);
183567
- };
183568
- }, [phase, isReauthentication]);
183569
- useEffect(() => {
183570
- if (phase === "success") {
183571
- const timer = setTimeout(() => {
183572
- onComplete({ success: true, userEmail });
183573
- }, 100);
183574
- return () => clearTimeout(timer);
183575
- }
183576
- if (phase === "error") {
183577
- const timer = setTimeout(() => {
183578
- onComplete({ success: false, error: new Error(error) });
183579
- }, 100);
183580
- return () => clearTimeout(timer);
183581
- }
183582
- }, [phase, userEmail, error, onComplete]);
183583
- if (phase === "error") {
183584
- return /* @__PURE__ */ React.createElement(Text, { color: "red" }, "Error: ", error);
183585
- }
183586
- if (phase === "success") {
183587
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: "green" }, isReauthentication ? "Re-authenticated" : "Logged in", " as ", userEmail), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "Credentials saved to ~/.specific/credentials.json"));
183588
- }
183589
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, isReauthentication && /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "Session expired. Please log in again."), /* @__PURE__ */ React.createElement(Text, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, "Your authentication code:", " ", /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, userCode))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Initiating login...")));
183723
+ });
183724
+ });
183590
183725
  }
183591
183726
 
183592
183727
  // src/lib/auth/credentials.ts
183593
183728
  function getUserCredentialsDir() {
183594
- return path5.join(os3.homedir(), ".specific");
183729
+ return path5.join(os4.homedir(), ".specific");
183595
183730
  }
183596
183731
  function getCredentialsPath() {
183597
183732
  return path5.join(getUserCredentialsDir(), "credentials.json");
@@ -183675,7 +183810,7 @@ function isEnabled() {
183675
183810
  }
183676
183811
  function getAnonymousId() {
183677
183812
  if (anonymousId) return anonymousId;
183678
- const machineId = `${os4.hostname()}-${os4.userInfo().username}`;
183813
+ const machineId = `${os5.hostname()}-${os5.userInfo().username}`;
183679
183814
  anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
183680
183815
  return anonymousId;
183681
183816
  }
@@ -183708,7 +183843,7 @@ function trackEvent(event, properties) {
183708
183843
  event,
183709
183844
  properties: {
183710
183845
  ...properties,
183711
- cli_version: "0.1.42",
183846
+ cli_version: "0.1.44",
183712
183847
  platform: process.platform,
183713
183848
  node_version: process.version,
183714
183849
  project_id: getProjectId(),
@@ -183776,7 +183911,7 @@ function appendOrCreateFile(filePath, content) {
183776
183911
  }
183777
183912
  function addToGitignore() {
183778
183913
  const gitignorePath = path6.join(process.cwd(), ".gitignore");
183779
- const entries = [".specific", "specific.secrets"];
183914
+ const entries = [".specific", "specific.local"];
183780
183915
  if (fs10.existsSync(gitignorePath)) {
183781
183916
  const existing = fs10.readFileSync(gitignorePath, "utf-8");
183782
183917
  const lines = existing.split("\n").map((l) => l.trim());
@@ -183889,8 +184024,8 @@ function configureAgents(checked) {
183889
184024
  return { agents, git, showManualInstructions: !!checked["other"] };
183890
184025
  }
183891
184026
  function InitUI() {
183892
- const { exit } = useApp2();
183893
- const [initialState] = useState2(() => {
184027
+ const { exit } = useApp();
184028
+ const [initialState] = useState(() => {
183894
184029
  const detected = detectExistingAgents();
183895
184030
  const hasDetected = Object.keys(detected).length > 0;
183896
184031
  return {
@@ -183899,19 +184034,19 @@ function InitUI() {
183899
184034
  // Focus submit if any detected
183900
184035
  };
183901
184036
  });
183902
- const [phase, setPhase] = useState2(
184037
+ const [phase, setPhase] = useState(
183903
184038
  () => caFilesExist() ? "agents" : "installing-ca"
183904
184039
  );
183905
- const [caInstallPhase, setCaInstallPhase] = useState2(() => caFilesExist() ? "done" : "installing");
183906
- const [focusedIndex, setFocusedIndex] = useState2(initialState.focusedIndex);
183907
- const [checked, setChecked] = useState2(
184040
+ const [caInstallPhase, setCaInstallPhase] = useState(() => caFilesExist() ? "done" : "installing");
184041
+ const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
184042
+ const [checked, setChecked] = useState(
183908
184043
  initialState.detected
183909
184044
  );
183910
- const [result, setResult] = useState2(null);
183911
- const [tlsResult, setTlsResult] = useState2(
184045
+ const [result, setResult] = useState(null);
184046
+ const [tlsResult, setTlsResult] = useState(
183912
184047
  caFilesExist() ? { status: "success" } : null
183913
184048
  );
183914
- useEffect2(() => {
184049
+ useEffect(() => {
183915
184050
  if (phase === "installing-ca" && caInstallPhase === "installing") {
183916
184051
  installCA();
183917
184052
  }
@@ -183956,7 +184091,7 @@ function InitUI() {
183956
184091
  setPhase("done");
183957
184092
  }
183958
184093
  });
183959
- useEffect2(() => {
184094
+ useEffect(() => {
183960
184095
  if (phase === "done") {
183961
184096
  const timer = setTimeout(() => {
183962
184097
  exit();
@@ -184031,13 +184166,13 @@ function resolveDocPath(path22) {
184031
184166
  }
184032
184167
 
184033
184168
  // src/commands/check.tsx
184034
- import React3, { useState as useState3, useEffect as useEffect3 } from "react";
184169
+ import React3, { useState as useState2, useEffect as useEffect2 } from "react";
184035
184170
  import { render as render3, Text as Text3, Box as Box3 } from "ink";
184036
184171
  import Spinner3 from "ink-spinner";
184037
184172
  import * as fs11 from "fs";
184038
184173
  import * as path7 from "path";
184039
184174
 
184040
- // ../config/dist/parser.js
184175
+ // node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
184041
184176
  var import_hcl2_json_parser = __toESM(require_dist(), 1);
184042
184177
  var { parseToObject } = import_hcl2_json_parser.default;
184043
184178
  function parseReference(value) {
@@ -184369,6 +184504,22 @@ function parseStorage(data) {
184369
184504
  }
184370
184505
  return result;
184371
184506
  }
184507
+ function parseConfigDevBlock(dev) {
184508
+ if (!dev) {
184509
+ return void 0;
184510
+ }
184511
+ const devArray = Array.isArray(dev) ? dev : [dev];
184512
+ const devObj = devArray[0];
184513
+ if (!devObj || typeof devObj !== "object" || Array.isArray(devObj)) {
184514
+ return void 0;
184515
+ }
184516
+ const fieldObj = devObj;
184517
+ const result = {};
184518
+ if (fieldObj.default !== void 0) {
184519
+ result.default = String(fieldObj.default);
184520
+ }
184521
+ return Object.keys(result).length > 0 ? result : void 0;
184522
+ }
184372
184523
  function parseConfigs(configData) {
184373
184524
  if (!configData || typeof configData !== "object" || Array.isArray(configData)) {
184374
184525
  return [];
@@ -184377,6 +184528,7 @@ function parseConfigs(configData) {
184377
184528
  for (const [name, fieldsArray] of Object.entries(configData)) {
184378
184529
  const fields = Array.isArray(fieldsArray) ? fieldsArray[0] : fieldsArray;
184379
184530
  if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
184531
+ configs.push({ name });
184380
184532
  continue;
184381
184533
  }
184382
184534
  const fieldObj = fields;
@@ -184384,10 +184536,30 @@ function parseConfigs(configData) {
184384
184536
  if (fieldObj.default) {
184385
184537
  config.default = String(fieldObj.default);
184386
184538
  }
184539
+ const dev = parseConfigDevBlock(fieldObj.dev);
184540
+ if (dev) {
184541
+ config.dev = dev;
184542
+ }
184387
184543
  configs.push(config);
184388
184544
  }
184389
184545
  return configs;
184390
184546
  }
184547
+ function parseSecretDevBlock(dev) {
184548
+ if (!dev) {
184549
+ return void 0;
184550
+ }
184551
+ const devArray = Array.isArray(dev) ? dev : [dev];
184552
+ const devObj = devArray[0];
184553
+ if (!devObj || typeof devObj !== "object" || Array.isArray(devObj)) {
184554
+ return void 0;
184555
+ }
184556
+ const fieldObj = devObj;
184557
+ const result = {};
184558
+ if (fieldObj.required !== void 0) {
184559
+ result.required = fieldObj.required === true;
184560
+ }
184561
+ return Object.keys(result).length > 0 ? result : void 0;
184562
+ }
184391
184563
  function parseSecrets(secretData) {
184392
184564
  if (!secretData || typeof secretData !== "object" || Array.isArray(secretData)) {
184393
184565
  return [];
@@ -184404,6 +184576,10 @@ function parseSecrets(secretData) {
184404
184576
  if (typeof fieldObj.length === "number") {
184405
184577
  secret.length = fieldObj.length;
184406
184578
  }
184579
+ const dev = parseSecretDevBlock(fieldObj.dev);
184580
+ if (dev) {
184581
+ secret.dev = dev;
184582
+ }
184407
184583
  }
184408
184584
  secrets.push(secret);
184409
184585
  }
@@ -184517,8 +184693,8 @@ function validateEndpointReferences(config) {
184517
184693
 
184518
184694
  // src/commands/check.tsx
184519
184695
  function CheckUI() {
184520
- const [state, setState] = useState3({ status: "loading" });
184521
- useEffect3(() => {
184696
+ const [state, setState] = useState2({ status: "loading" });
184697
+ useEffect2(() => {
184522
184698
  async function load() {
184523
184699
  const configPath = path7.join(process.cwd(), "specific.hcl");
184524
184700
  if (!fs11.existsSync(configPath)) {
@@ -184558,19 +184734,19 @@ function checkCommand() {
184558
184734
  }
184559
184735
 
184560
184736
  // src/commands/dev.tsx
184561
- import React4, { useState as useState4, useEffect as useEffect4, useRef } from "react";
184562
- import { render as render4, Text as Text4, Box as Box4, useApp as useApp3, Static } from "ink";
184737
+ import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
184738
+ import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
184563
184739
  import Spinner4 from "ink-spinner";
184564
184740
  import * as fs19 from "fs";
184565
184741
  import * as path16 from "path";
184566
184742
 
184567
- // node_modules/chokidar/index.js
184743
+ // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
184568
184744
  import { EventEmitter } from "node:events";
184569
184745
  import { stat as statcb, Stats } from "node:fs";
184570
184746
  import { readdir as readdir2, stat as stat3 } from "node:fs/promises";
184571
184747
  import * as sp2 from "node:path";
184572
184748
 
184573
- // node_modules/readdirp/index.js
184749
+ // node_modules/.pnpm/readdirp@5.0.0/node_modules/readdirp/index.js
184574
184750
  import { lstat, readdir, realpath, stat } from "node:fs/promises";
184575
184751
  import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "node:path";
184576
184752
  import { Readable } from "node:stream";
@@ -184803,7 +184979,7 @@ function readdirp(root, options2 = {}) {
184803
184979
  return new ReaddirpStream(options2);
184804
184980
  }
184805
184981
 
184806
- // node_modules/chokidar/handler.js
184982
+ // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/handler.js
184807
184983
  import { watch as fs_watch, unwatchFile, watchFile } from "node:fs";
184808
184984
  import { realpath as fsrealpath, lstat as lstat2, open as open2, stat as stat2 } from "node:fs/promises";
184809
184985
  import { type as osType } from "node:os";
@@ -185562,7 +185738,7 @@ var NodeFsHandler = class {
185562
185738
  }
185563
185739
  };
185564
185740
 
185565
- // node_modules/chokidar/index.js
185741
+ // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
185566
185742
  var SLASH = "/";
185567
185743
  var SLASH_SLASH = "//";
185568
185744
  var ONE_DOT = ".";
@@ -186384,7 +186560,7 @@ var StablePortAllocator = class {
186384
186560
  import * as fs14 from "fs";
186385
186561
  import * as path10 from "path";
186386
186562
  import * as net from "net";
186387
- import * as os6 from "os";
186563
+ import * as os7 from "os";
186388
186564
  import { spawn } from "child_process";
186389
186565
 
186390
186566
  // src/lib/bin/types.ts
@@ -186424,15 +186600,15 @@ var ExtractionError = class extends Error {
186424
186600
  // src/lib/bin/manager.ts
186425
186601
  import * as fs13 from "fs";
186426
186602
  import * as path9 from "path";
186427
- import * as os5 from "os";
186603
+ import * as os6 from "os";
186428
186604
  import { createReadStream } from "fs";
186429
186605
  import { createTarExtractor, extractTo } from "tar-vern";
186430
186606
  function getBinBaseDir() {
186431
- return path9.join(os5.homedir(), ".specific", "bin");
186607
+ return path9.join(os6.homedir(), ".specific", "bin");
186432
186608
  }
186433
186609
  function getPlatformInfo() {
186434
- const platform5 = os5.platform();
186435
- const arch3 = os5.arch();
186610
+ const platform5 = os6.platform();
186611
+ const arch3 = os6.arch();
186436
186612
  if (platform5 !== "darwin" && platform5 !== "linux") {
186437
186613
  throw new Error(
186438
186614
  `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
@@ -186700,7 +186876,7 @@ function getLibraryEnv(binary) {
186700
186876
  if (!binary.libraryPath) {
186701
186877
  return {};
186702
186878
  }
186703
- const platform5 = os6.platform();
186879
+ const platform5 = os7.platform();
186704
186880
  if (platform5 === "darwin") {
186705
186881
  return { DYLD_LIBRARY_PATH: binary.libraryPath };
186706
186882
  } else if (platform5 === "linux") {
@@ -186879,7 +187055,7 @@ async function waitForTcpPort(host, port, timeoutMs = 3e4) {
186879
187055
  if (isOpen) {
186880
187056
  return;
186881
187057
  }
186882
- await sleep(100);
187058
+ await sleep2(100);
186883
187059
  }
186884
187060
  throw new Error(`Port ${port} did not become available within timeout`);
186885
187061
  }
@@ -186920,140 +187096,114 @@ async function stopProcess(proc) {
186920
187096
  }, 2e3);
186921
187097
  });
186922
187098
  }
186923
- function sleep(ms) {
187099
+ function sleep2(ms) {
186924
187100
  return new Promise((resolve7) => setTimeout(resolve7, ms));
186925
187101
  }
186926
187102
 
186927
187103
  // src/lib/dev/service-runner.ts
186928
187104
  import { spawn as spawn2 } from "child_process";
186929
187105
 
186930
- // src/lib/secrets/parser.ts
187106
+ // src/lib/local/parser.ts
186931
187107
  var import_hcl2_json_parser2 = __toESM(require_dist(), 1);
186932
- import { readFile, writeFile, mkdir } from "fs/promises";
187108
+ import { readFile, writeFile } from "fs/promises";
186933
187109
  import { existsSync as existsSync10 } from "fs";
186934
- import * as path11 from "path";
186935
- import * as crypto2 from "crypto";
186936
187110
  var { parseToObject: parseToObject2 } = import_hcl2_json_parser2.default;
186937
- var SECRETS_FILE = "specific.secrets";
186938
- var GENERATED_SECRETS_FILE = ".specific/generated-secrets.json";
186939
- async function parseSecretsFile(content) {
187111
+ var LOCAL_FILE = "specific.local";
187112
+ var HEADER_COMMENT = `# Local secrets and configuration
187113
+ # Do not commit this file
187114
+
187115
+ `;
187116
+ async function parseLocalFile(content) {
186940
187117
  const secrets = /* @__PURE__ */ new Map();
187118
+ const configs = /* @__PURE__ */ new Map();
186941
187119
  if (!content.trim()) {
186942
- return secrets;
187120
+ return { secrets, configs };
186943
187121
  }
186944
187122
  const parsed = await parseToObject2(content);
186945
- for (const [key, value] of Object.entries(parsed)) {
186946
- if (typeof value === "string") {
186947
- secrets.set(key, value);
187123
+ if (parsed.secrets) {
187124
+ const secretsBlocks = Array.isArray(parsed.secrets) ? parsed.secrets : [parsed.secrets];
187125
+ for (const block of secretsBlocks) {
187126
+ if (block && typeof block === "object") {
187127
+ for (const [key, value] of Object.entries(block)) {
187128
+ if (typeof value === "string") {
187129
+ secrets.set(key, value);
187130
+ }
187131
+ }
187132
+ }
186948
187133
  }
186949
187134
  }
186950
- return secrets;
186951
- }
186952
- async function loadSecrets() {
186953
- if (!existsSync10(SECRETS_FILE)) {
186954
- return /* @__PURE__ */ new Map();
186955
- }
186956
- const content = await readFile(SECRETS_FILE, "utf-8");
186957
- return await parseSecretsFile(content);
186958
- }
186959
- function generateRandomString(length = 64) {
186960
- const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
186961
- const bytes = crypto2.randomBytes(length);
186962
- let result = "";
186963
- for (let i = 0; i < length; i++) {
186964
- result += chars[bytes[i] % chars.length];
187135
+ if (parsed.config) {
187136
+ const configBlocks = Array.isArray(parsed.config) ? parsed.config : [parsed.config];
187137
+ for (const block of configBlocks) {
187138
+ if (block && typeof block === "object") {
187139
+ for (const [key, value] of Object.entries(block)) {
187140
+ if (typeof value === "string") {
187141
+ configs.set(key, value);
187142
+ }
187143
+ }
187144
+ }
187145
+ }
186965
187146
  }
186966
- return result;
187147
+ return { secrets, configs };
186967
187148
  }
186968
- async function loadGeneratedSecrets() {
186969
- if (!existsSync10(GENERATED_SECRETS_FILE)) {
186970
- return /* @__PURE__ */ new Map();
187149
+ async function loadLocal() {
187150
+ if (!existsSync10(LOCAL_FILE)) {
187151
+ return { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
186971
187152
  }
186972
- const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
186973
- const parsed = JSON.parse(content);
186974
- return new Map(Object.entries(parsed));
187153
+ const content = await readFile(LOCAL_FILE, "utf-8");
187154
+ return await parseLocalFile(content);
186975
187155
  }
186976
- async function saveGeneratedSecret(name, value) {
186977
- let secrets = {};
186978
- if (existsSync10(GENERATED_SECRETS_FILE)) {
186979
- const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
186980
- secrets = JSON.parse(content);
186981
- }
186982
- secrets[name] = value;
186983
- const dir = path11.dirname(GENERATED_SECRETS_FILE);
186984
- if (!existsSync10(dir)) {
186985
- await mkdir(dir, { recursive: true });
186986
- }
186987
- await writeFile(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
187156
+ function escapeHclValue(value) {
187157
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
186988
187158
  }
186989
- async function prepareSecrets(secretsConfig) {
186990
- const userSecrets = await loadSecrets();
186991
- const generatedSecrets = await loadGeneratedSecrets();
186992
- const finalSecrets = /* @__PURE__ */ new Map();
186993
- for (const secretDef of secretsConfig) {
186994
- const userValue = userSecrets.get(secretDef.name);
186995
- if (userValue !== void 0) {
186996
- finalSecrets.set(secretDef.name, userValue);
186997
- continue;
186998
- }
186999
- if (secretDef.generated) {
187000
- let generatedValue = generatedSecrets.get(secretDef.name);
187001
- if (generatedValue === void 0) {
187002
- generatedValue = generateRandomString(secretDef.length ?? 64);
187003
- await saveGeneratedSecret(secretDef.name, generatedValue);
187004
- }
187005
- finalSecrets.set(secretDef.name, generatedValue);
187159
+ function updateBlockValue(content, blockName, key, value) {
187160
+ const escapedValue = escapeHclValue(value);
187161
+ const newLine = ` ${key} = "${escapedValue}"`;
187162
+ const blockRegex = new RegExp(`(${blockName}\\s*\\{)([^}]*)(\\})`, "s");
187163
+ const blockMatch = content.match(blockRegex);
187164
+ if (blockMatch) {
187165
+ const blockContent = blockMatch[2];
187166
+ const keyRegex = new RegExp(`^(\\s*)${key}\\s*=\\s*"[^"]*"\\s*$`, "m");
187167
+ const keyMatch = blockContent.match(keyRegex);
187168
+ if (keyMatch) {
187169
+ const updatedBlockContent = blockContent.replace(keyRegex, newLine);
187170
+ return content.replace(blockRegex, `$1${updatedBlockContent}$3`);
187171
+ } else {
187172
+ const trimmedContent = blockContent.trimEnd();
187173
+ const newBlockContent = trimmedContent ? `${trimmedContent}
187174
+ ${newLine}
187175
+ ` : `
187176
+ ${newLine}
187177
+ `;
187178
+ return content.replace(blockRegex, `$1${newBlockContent}$3`);
187006
187179
  }
187007
- }
187008
- return finalSecrets;
187180
+ } else {
187181
+ const newBlock = `${blockName} {
187182
+ ${newLine}
187009
187183
  }
187010
-
187011
- // src/lib/config/parser.ts
187012
- var import_hcl2_json_parser3 = __toESM(require_dist(), 1);
187013
- import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
187014
- import { existsSync as existsSync11 } from "fs";
187015
- var { parseToObject: parseToObject3 } = import_hcl2_json_parser3.default;
187016
- var CONFIG_FILE = "specific.config";
187017
- async function parseConfigFile(content) {
187018
- const configs = /* @__PURE__ */ new Map();
187019
- if (!content.trim()) {
187020
- return configs;
187021
- }
187022
- const parsed = await parseToObject3(content);
187023
- for (const [key, value] of Object.entries(parsed)) {
187024
- if (typeof value === "string") {
187025
- configs.set(key, value);
187026
- }
187184
+ `;
187185
+ return content.trimEnd() + "\n\n" + newBlock;
187027
187186
  }
187028
- return configs;
187029
187187
  }
187030
- async function loadConfigs() {
187031
- if (!existsSync11(CONFIG_FILE)) {
187032
- return /* @__PURE__ */ new Map();
187188
+ async function saveLocalSecret(name, value) {
187189
+ let content = "";
187190
+ if (existsSync10(LOCAL_FILE)) {
187191
+ content = await readFile(LOCAL_FILE, "utf-8");
187192
+ } else {
187193
+ content = HEADER_COMMENT;
187033
187194
  }
187034
- const content = await readFile2(CONFIG_FILE, "utf-8");
187035
- return await parseConfigFile(content);
187195
+ content = updateBlockValue(content, "secrets", name, value);
187196
+ await writeFile(LOCAL_FILE, content);
187036
187197
  }
187037
- async function prepareConfigs(configsDef, environmentOverrides) {
187038
- const userConfigs = await loadConfigs();
187039
- const finalConfigs = /* @__PURE__ */ new Map();
187040
- for (const configDef of configsDef) {
187041
- const userValue = userConfigs.get(configDef.name);
187042
- if (userValue !== void 0) {
187043
- finalConfigs.set(configDef.name, userValue);
187044
- continue;
187045
- }
187046
- const envOverride = environmentOverrides?.[configDef.name];
187047
- if (envOverride !== void 0) {
187048
- finalConfigs.set(configDef.name, envOverride);
187049
- continue;
187050
- }
187051
- if (configDef.default !== void 0) {
187052
- finalConfigs.set(configDef.name, configDef.default);
187053
- continue;
187054
- }
187198
+ async function saveLocalConfig(name, value) {
187199
+ let content = "";
187200
+ if (existsSync10(LOCAL_FILE)) {
187201
+ content = await readFile(LOCAL_FILE, "utf-8");
187202
+ } else {
187203
+ content = HEADER_COMMENT;
187055
187204
  }
187056
- return finalConfigs;
187205
+ content = updateBlockValue(content, "config", name, value);
187206
+ await writeFile(LOCAL_FILE, content);
187057
187207
  }
187058
187208
 
187059
187209
  // src/lib/dev/env-resolver.ts
@@ -187062,9 +187212,12 @@ var MissingSecretError = class extends Error {
187062
187212
  super(
187063
187213
  `Missing secret '${secretName}'
187064
187214
 
187065
- This secret is declared in specific.hcl but has no value in ${SECRETS_FILE}.
187215
+ This secret is declared in specific.hcl but has no value in ${LOCAL_FILE}.
187066
187216
 
187067
- Run: specific secrets set ${secretName}`
187217
+ Add it to the secrets block in ${LOCAL_FILE}:
187218
+ secrets {
187219
+ ${secretName} = "your-value"
187220
+ }`
187068
187221
  );
187069
187222
  this.secretName = secretName;
187070
187223
  this.name = "MissingSecretError";
@@ -187075,9 +187228,12 @@ var MissingConfigError = class extends Error {
187075
187228
  super(
187076
187229
  `Missing config '${configName}'
187077
187230
 
187078
- This config is declared in specific.hcl but has no default value, environment override, or value in ${CONFIG_FILE}.
187231
+ This config is declared in specific.hcl but has no default value, environment override, or value in ${LOCAL_FILE}.
187079
187232
 
187080
- Run: specific config set ${configName} <value>`
187233
+ Add it to the config block in ${LOCAL_FILE}:
187234
+ config {
187235
+ ${configName} = "your-value"
187236
+ }`
187081
187237
  );
187082
187238
  this.configName = configName;
187083
187239
  this.name = "MissingConfigError";
@@ -187124,7 +187280,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187124
187280
  }
187125
187281
  switch (serviceRef.attribute) {
187126
187282
  case "url":
187127
- return `http://localhost:${endpoint.port}`;
187283
+ return `localhost:${endpoint.port}`;
187128
187284
  case "host":
187129
187285
  return "localhost";
187130
187286
  case "port":
@@ -187341,7 +187497,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
187341
187497
 
187342
187498
  // src/lib/dev/instance-state.ts
187343
187499
  import * as fs15 from "fs";
187344
- import * as path12 from "path";
187500
+ import * as path11 from "path";
187345
187501
  var InstanceStateManager = class {
187346
187502
  stateDir;
187347
187503
  statePath;
@@ -187350,9 +187506,9 @@ var InstanceStateManager = class {
187350
187506
  key;
187351
187507
  constructor(projectRoot, key = "default") {
187352
187508
  this.key = key;
187353
- this.stateDir = path12.join(projectRoot, ".specific", "keys", key);
187354
- this.statePath = path12.join(this.stateDir, "state.json");
187355
- this.lockPath = path12.join(this.stateDir, "state.lock");
187509
+ this.stateDir = path11.join(projectRoot, ".specific", "keys", key);
187510
+ this.statePath = path11.join(this.stateDir, "state.json");
187511
+ this.lockPath = path11.join(this.stateDir, "state.lock");
187356
187512
  }
187357
187513
  getKey() {
187358
187514
  return this.key;
@@ -187554,11 +187710,11 @@ var InstanceStateManager = class {
187554
187710
  import * as http from "http";
187555
187711
  import * as https from "https";
187556
187712
  import * as fs16 from "fs";
187557
- import * as path13 from "path";
187713
+ import * as path12 from "path";
187558
187714
  import { fileURLToPath as fileURLToPath3 } from "url";
187559
187715
  import httpProxy from "http-proxy";
187560
- var __dirname3 = path13.dirname(fileURLToPath3(import.meta.url));
187561
- var adminDir = path13.join(__dirname3, "admin");
187716
+ var __dirname3 = path12.dirname(fileURLToPath3(import.meta.url));
187717
+ var adminDir = path12.join(__dirname3, "admin");
187562
187718
  var HTTP_PORT = 80;
187563
187719
  var HTTPS_PORT = 443;
187564
187720
  var DOMAIN_SUFFIX = ".local.spcf.app";
@@ -187844,9 +188000,9 @@ function serveStaticFile(res, pathname) {
187844
188000
  filePath = filePath + "index.html";
187845
188001
  }
187846
188002
  const relativePath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
187847
- const fullPath = path13.join(adminDir, relativePath);
187848
- const resolvedPath = path13.resolve(fullPath);
187849
- const resolvedAdminDir = path13.resolve(adminDir);
188003
+ const fullPath = path12.join(adminDir, relativePath);
188004
+ const resolvedPath = path12.resolve(fullPath);
188005
+ const resolvedAdminDir = path12.resolve(adminDir);
187850
188006
  if (!resolvedPath.startsWith(resolvedAdminDir)) {
187851
188007
  res.writeHead(403, { "Content-Type": "text/html" });
187852
188008
  res.end("<h1>Forbidden</h1>");
@@ -187857,11 +188013,11 @@ function serveStaticFile(res, pathname) {
187857
188013
  if (fs16.existsSync(htmlPath)) {
187858
188014
  return serveFile(res, htmlPath);
187859
188015
  }
187860
- const indexPath = path13.join(resolvedPath, "index.html");
188016
+ const indexPath = path12.join(resolvedPath, "index.html");
187861
188017
  if (fs16.existsSync(indexPath)) {
187862
188018
  return serveFile(res, indexPath);
187863
188019
  }
187864
- const notFoundPath = path13.join(adminDir, "404.html");
188020
+ const notFoundPath = path12.join(adminDir, "404.html");
187865
188021
  if (fs16.existsSync(notFoundPath)) {
187866
188022
  return serveFileContent(res, notFoundPath, "text/html", 404);
187867
188023
  }
@@ -187872,7 +188028,7 @@ function serveStaticFile(res, pathname) {
187872
188028
  serveFile(res, resolvedPath);
187873
188029
  }
187874
188030
  function serveFile(res, filePath) {
187875
- const ext = path13.extname(filePath).toLowerCase();
188031
+ const ext = path12.extname(filePath).toLowerCase();
187876
188032
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
187877
188033
  serveFileContent(res, filePath, contentType, 200);
187878
188034
  }
@@ -187935,6 +188091,101 @@ async function startAdminServer(getState) {
187935
188091
  // src/lib/dev/electric-manager.ts
187936
188092
  import * as net2 from "net";
187937
188093
  import { spawn as spawn3 } from "child_process";
188094
+
188095
+ // src/lib/secrets/parser.ts
188096
+ import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
188097
+ import { existsSync as existsSync13 } from "fs";
188098
+ import * as path13 from "path";
188099
+ import * as crypto2 from "crypto";
188100
+ var GENERATED_SECRETS_FILE = ".specific/generated-secrets.json";
188101
+ async function loadSecrets() {
188102
+ const { secrets } = await loadLocal();
188103
+ return secrets;
188104
+ }
188105
+ function generateRandomString(length = 64) {
188106
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
188107
+ const bytes = crypto2.randomBytes(length);
188108
+ let result = "";
188109
+ for (let i = 0; i < length; i++) {
188110
+ result += chars[bytes[i] % chars.length];
188111
+ }
188112
+ return result;
188113
+ }
188114
+ async function loadGeneratedSecrets() {
188115
+ if (!existsSync13(GENERATED_SECRETS_FILE)) {
188116
+ return /* @__PURE__ */ new Map();
188117
+ }
188118
+ const content = await readFile2(GENERATED_SECRETS_FILE, "utf-8");
188119
+ const parsed = JSON.parse(content);
188120
+ return new Map(Object.entries(parsed));
188121
+ }
188122
+ async function saveGeneratedSecret(name, value) {
188123
+ let secrets = {};
188124
+ if (existsSync13(GENERATED_SECRETS_FILE)) {
188125
+ const content = await readFile2(GENERATED_SECRETS_FILE, "utf-8");
188126
+ secrets = JSON.parse(content);
188127
+ }
188128
+ secrets[name] = value;
188129
+ const dir = path13.dirname(GENERATED_SECRETS_FILE);
188130
+ if (!existsSync13(dir)) {
188131
+ await mkdir(dir, { recursive: true });
188132
+ }
188133
+ await writeFile2(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
188134
+ }
188135
+ async function prepareSecrets(secretsConfig, isDevMode = false) {
188136
+ const userSecrets = await loadSecrets();
188137
+ const generatedSecrets = await loadGeneratedSecrets();
188138
+ const finalSecrets = /* @__PURE__ */ new Map();
188139
+ for (const secretDef of secretsConfig) {
188140
+ const userValue = userSecrets.get(secretDef.name);
188141
+ if (userValue !== void 0) {
188142
+ finalSecrets.set(secretDef.name, userValue);
188143
+ continue;
188144
+ }
188145
+ if (secretDef.generated) {
188146
+ let generatedValue = generatedSecrets.get(secretDef.name);
188147
+ if (generatedValue === void 0) {
188148
+ generatedValue = generateRandomString(secretDef.length ?? 64);
188149
+ await saveGeneratedSecret(secretDef.name, generatedValue);
188150
+ }
188151
+ finalSecrets.set(secretDef.name, generatedValue);
188152
+ continue;
188153
+ }
188154
+ if (isDevMode && secretDef.dev?.required === false) {
188155
+ finalSecrets.set(secretDef.name, "");
188156
+ continue;
188157
+ }
188158
+ }
188159
+ return finalSecrets;
188160
+ }
188161
+ async function saveSecret(name, value) {
188162
+ await saveLocalSecret(name, value);
188163
+ }
188164
+ function findUsedSecrets(services, isDevMode) {
188165
+ const used = /* @__PURE__ */ new Set();
188166
+ for (const service of services) {
188167
+ const mergedEnv = isDevMode ? { ...service.env, ...service.dev?.env } : service.env;
188168
+ for (const value of Object.values(mergedEnv ?? {})) {
188169
+ if (typeof value === "object" && value.type === "secret") {
188170
+ used.add(value.name);
188171
+ }
188172
+ }
188173
+ }
188174
+ return used;
188175
+ }
188176
+ function findMissingSecrets(secretsDef, preparedSecrets, usedSecrets) {
188177
+ const missing = [];
188178
+ for (const secret of secretsDef) {
188179
+ if (secret.generated) continue;
188180
+ if (usedSecrets && !usedSecrets.has(secret.name)) continue;
188181
+ if (!preparedSecrets.has(secret.name)) {
188182
+ missing.push(secret.name);
188183
+ }
188184
+ }
188185
+ return missing;
188186
+ }
188187
+
188188
+ // src/lib/dev/electric-manager.ts
187938
188189
  async function startElectric(postgres, port, options2) {
187939
188190
  if (postgres.type !== "postgres") {
187940
188191
  throw new Error(
@@ -187983,7 +188234,7 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
187983
188234
  if (isOpen) {
187984
188235
  return;
187985
188236
  }
187986
- await sleep2(100);
188237
+ await sleep3(100);
187987
188238
  }
187988
188239
  throw new Error(`Electric port ${port} did not become available within timeout`);
187989
188240
  }
@@ -188024,7 +188275,7 @@ async function stopProcess2(proc) {
188024
188275
  }, 2e3);
188025
188276
  });
188026
188277
  }
188027
- function sleep2(ms) {
188278
+ function sleep3(ms) {
188028
188279
  return new Promise((resolve7) => setTimeout(resolve7, ms));
188029
188280
  }
188030
188281
 
@@ -188102,7 +188353,7 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
188102
188353
  if (isOpen) {
188103
188354
  return;
188104
188355
  }
188105
- await sleep3(100);
188356
+ await sleep4(100);
188106
188357
  }
188107
188358
  throw new Error(
188108
188359
  `Drizzle Gateway port ${port} did not become available within timeout`
@@ -188145,7 +188396,7 @@ async function stopProcess3(proc) {
188145
188396
  }, 2e3);
188146
188397
  });
188147
188398
  }
188148
- function sleep3(ms) {
188399
+ function sleep4(ms) {
188149
188400
  return new Promise((resolve7) => setTimeout(resolve7, ms));
188150
188401
  }
188151
188402
 
@@ -188340,7 +188591,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
188340
188591
  // src/lib/dev/proxy-registry.ts
188341
188592
  import * as fs18 from "fs";
188342
188593
  import * as path15 from "path";
188343
- import * as os7 from "os";
188594
+ import * as os8 from "os";
188344
188595
  var ProxyRegistryManager = class {
188345
188596
  proxyDir;
188346
188597
  ownerPath;
@@ -188349,7 +188600,7 @@ var ProxyRegistryManager = class {
188349
188600
  isOwner = false;
188350
188601
  registryWatcher = null;
188351
188602
  constructor() {
188352
- this.proxyDir = path15.join(os7.homedir(), ".specific", "proxy");
188603
+ this.proxyDir = path15.join(os8.homedir(), ".specific", "proxy");
188353
188604
  this.ownerPath = path15.join(this.proxyDir, "owner.json");
188354
188605
  this.registryPath = path15.join(this.proxyDir, "registry.json");
188355
188606
  this.lockPath = path15.join(this.proxyDir, "registry.lock");
@@ -188659,11 +188910,111 @@ var ProxyRegistryManager = class {
188659
188910
  }
188660
188911
  };
188661
188912
 
188913
+ // src/lib/config/parser.ts
188914
+ async function loadConfigs() {
188915
+ const { configs } = await loadLocal();
188916
+ return configs;
188917
+ }
188918
+ async function saveConfig(name, value) {
188919
+ await saveLocalConfig(name, value);
188920
+ }
188921
+ async function prepareConfigs(configsDef, environmentOverrides, isDevMode = false) {
188922
+ const userConfigs = await loadConfigs();
188923
+ const finalConfigs = /* @__PURE__ */ new Map();
188924
+ for (const configDef of configsDef) {
188925
+ const userValue = userConfigs.get(configDef.name);
188926
+ if (userValue !== void 0) {
188927
+ finalConfigs.set(configDef.name, userValue);
188928
+ continue;
188929
+ }
188930
+ const envOverride = environmentOverrides?.[configDef.name];
188931
+ if (envOverride !== void 0) {
188932
+ finalConfigs.set(configDef.name, envOverride);
188933
+ continue;
188934
+ }
188935
+ const effectiveDefault = isDevMode && configDef.dev?.default !== void 0 ? configDef.dev.default : configDef.default;
188936
+ if (effectiveDefault !== void 0) {
188937
+ finalConfigs.set(configDef.name, effectiveDefault);
188938
+ continue;
188939
+ }
188940
+ }
188941
+ return finalConfigs;
188942
+ }
188943
+ function findUsedConfigs(services, isDevMode) {
188944
+ const used = /* @__PURE__ */ new Set();
188945
+ for (const service of services) {
188946
+ const mergedEnv = isDevMode ? { ...service.env, ...service.dev?.env } : service.env;
188947
+ for (const value of Object.values(mergedEnv ?? {})) {
188948
+ if (typeof value === "object" && value.type === "config") {
188949
+ used.add(value.name);
188950
+ }
188951
+ }
188952
+ }
188953
+ return used;
188954
+ }
188955
+ function findMissingConfigs(configsDef, preparedConfigs, usedConfigs) {
188956
+ const missing = [];
188957
+ for (const config of configsDef) {
188958
+ if (usedConfigs && !usedConfigs.has(config.name)) continue;
188959
+ if (!preparedConfigs.has(config.name)) {
188960
+ missing.push(config.name);
188961
+ }
188962
+ }
188963
+ return missing;
188964
+ }
188965
+
188966
+ // src/lib/ui/SecretInput.tsx
188967
+ import React4, { useState as useState3 } from "react";
188968
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
188969
+ function SecretInput({ secretName, onSubmit, onCancel }) {
188970
+ const [value, setValue] = useState3("");
188971
+ useInput2((input, key) => {
188972
+ if (key.return) {
188973
+ if (value.trim() !== "") {
188974
+ onSubmit(value);
188975
+ }
188976
+ } else if (key.escape) {
188977
+ onCancel();
188978
+ } else if (key.backspace || key.delete) {
188979
+ setValue((prev) => prev.slice(0, -1));
188980
+ } else if (!key.ctrl && !key.meta && input) {
188981
+ setValue((prev) => prev + input);
188982
+ }
188983
+ });
188984
+ return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, "Enter value for secret ", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, secretName), ":"), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(Text4, null, value.length > 0 ? "*".repeat(value.length) : ""), /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "|")), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
188985
+ }
188986
+
188987
+ // src/lib/ui/ConfigInput.tsx
188988
+ import React5, { useState as useState4 } from "react";
188989
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
188990
+ function ConfigInput({ configName, onSubmit, onCancel }) {
188991
+ const [value, setValue] = useState4("");
188992
+ useInput3((input, key) => {
188993
+ if (key.return) {
188994
+ if (value.trim() !== "") {
188995
+ onSubmit(value);
188996
+ }
188997
+ } else if (key.escape) {
188998
+ onCancel();
188999
+ } else if (key.backspace || key.delete) {
189000
+ setValue((prev) => prev.slice(0, -1));
189001
+ } else if (!key.ctrl && !key.meta && input) {
189002
+ setValue((prev) => prev + input);
189003
+ }
189004
+ });
189005
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
189006
+ }
189007
+
189008
+ // src/lib/ui/interactive.ts
189009
+ function isInteractive() {
189010
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
189011
+ }
189012
+
188662
189013
  // src/commands/dev.tsx
188663
189014
  var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
188664
189015
  function DevUI({ instanceKey }) {
188665
- const { exit } = useApp3();
188666
- const [state, setState] = useState4(() => {
189016
+ const { exit } = useApp2();
189017
+ const [state, setState] = useState5(() => {
188667
189018
  const caExists = caFilesExist();
188668
189019
  return {
188669
189020
  status: caExists ? "loading" : "installing-ca",
@@ -188676,7 +189027,7 @@ function DevUI({ instanceKey }) {
188676
189027
  isProxyOwner: false
188677
189028
  };
188678
189029
  });
188679
- useEffect4(() => {
189030
+ useEffect3(() => {
188680
189031
  if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
188681
189032
  installCA();
188682
189033
  }
@@ -188709,8 +189060,8 @@ function DevUI({ instanceKey }) {
188709
189060
  const adminServerRef = useRef(null);
188710
189061
  const servicesRef = useRef([]);
188711
189062
  const resourcesRef = useRef(/* @__PURE__ */ new Map());
188712
- const [reloadTrigger, setReloadTrigger] = useState4(0);
188713
- const [readyToStart, setReadyToStart] = useState4(() => caFilesExist());
189063
+ const [reloadTrigger, setReloadTrigger] = useState5(0);
189064
+ const [readyToStart, setReadyToStart] = useState5(() => caFilesExist());
188714
189065
  const shutdown2 = async () => {
188715
189066
  if (shuttingDown.current) return;
188716
189067
  shuttingDown.current = true;
@@ -188802,7 +189153,7 @@ function DevUI({ instanceKey }) {
188802
189153
  }));
188803
189154
  setReloadTrigger((t) => t + 1);
188804
189155
  };
188805
- useEffect4(() => {
189156
+ useEffect3(() => {
188806
189157
  const handleSignal = () => {
188807
189158
  if (shuttingDown.current) {
188808
189159
  writeLog("system", "Force shutting down");
@@ -188831,13 +189182,13 @@ function DevUI({ instanceKey }) {
188831
189182
  process.off("SIGTERM", handleSignal);
188832
189183
  };
188833
189184
  }, []);
188834
- useEffect4(() => {
189185
+ useEffect3(() => {
188835
189186
  if (state.status === "running" && !startTimeRef.current) {
188836
189187
  startTimeRef.current = Date.now();
188837
189188
  trackEvent("dev_started");
188838
189189
  }
188839
189190
  }, [state.status]);
188840
- useEffect4(() => {
189191
+ useEffect3(() => {
188841
189192
  if (state.status !== "running") return;
188842
189193
  const configPath = path16.join(process.cwd(), "specific.hcl");
188843
189194
  const watcher = watchConfigFile(configPath, 1e3, () => {
@@ -188872,7 +189223,66 @@ function DevUI({ instanceKey }) {
188872
189223
  ]
188873
189224
  }));
188874
189225
  };
188875
- useEffect4(() => {
189226
+ const handleSecretSubmit = async (value) => {
189227
+ const currentSecret = state.missingSecrets?.[state.currentSecretIndex ?? 0];
189228
+ if (!currentSecret) return;
189229
+ await saveSecret(currentSecret, value);
189230
+ const nextIndex = (state.currentSecretIndex ?? 0) + 1;
189231
+ if (nextIndex < (state.missingSecrets?.length ?? 0)) {
189232
+ setState((s) => ({
189233
+ ...s,
189234
+ currentSecretIndex: nextIndex
189235
+ }));
189236
+ } else if (state.missingConfigs && state.missingConfigs.length > 0) {
189237
+ setState((s) => ({
189238
+ ...s,
189239
+ status: "awaiting-configs",
189240
+ missingSecrets: void 0,
189241
+ currentSecretIndex: void 0,
189242
+ currentConfigIndex: 0
189243
+ }));
189244
+ } else {
189245
+ setState((s) => ({
189246
+ ...s,
189247
+ status: "loading",
189248
+ missingSecrets: void 0,
189249
+ currentSecretIndex: void 0
189250
+ }));
189251
+ setReloadTrigger((t) => t + 1);
189252
+ }
189253
+ };
189254
+ const handleConfigSubmit = async (value) => {
189255
+ const currentConfig = state.missingConfigs?.[state.currentConfigIndex ?? 0];
189256
+ if (!currentConfig) return;
189257
+ await saveConfig(currentConfig, value);
189258
+ const nextIndex = (state.currentConfigIndex ?? 0) + 1;
189259
+ if (nextIndex < (state.missingConfigs?.length ?? 0)) {
189260
+ setState((s) => ({
189261
+ ...s,
189262
+ currentConfigIndex: nextIndex
189263
+ }));
189264
+ } else {
189265
+ setState((s) => ({
189266
+ ...s,
189267
+ status: "loading",
189268
+ missingConfigs: void 0,
189269
+ currentConfigIndex: void 0
189270
+ }));
189271
+ setReloadTrigger((t) => t + 1);
189272
+ }
189273
+ };
189274
+ const handleInputCancel = () => {
189275
+ setState((s) => ({
189276
+ ...s,
189277
+ status: "error",
189278
+ error: "Startup cancelled - required values not provided",
189279
+ missingSecrets: void 0,
189280
+ currentSecretIndex: void 0,
189281
+ missingConfigs: void 0,
189282
+ currentConfigIndex: void 0
189283
+ }));
189284
+ };
189285
+ useEffect3(() => {
188876
189286
  if (!readyToStart) {
188877
189287
  return;
188878
189288
  }
@@ -189047,8 +189457,50 @@ function DevUI({ instanceKey }) {
189047
189457
  }
189048
189458
  }
189049
189459
  if (cancelled) return;
189050
- const secrets = await prepareSecrets(config2.secrets);
189051
- const configs = await prepareConfigs(config2.configs);
189460
+ const secrets = await prepareSecrets(config2.secrets, true);
189461
+ const configs = await prepareConfigs(config2.configs, void 0, true);
189462
+ const usedSecrets = findUsedSecrets(config2.services, true);
189463
+ const usedConfigs = findUsedConfigs(config2.services, true);
189464
+ const missingSecrets = findMissingSecrets(config2.secrets, secrets, usedSecrets);
189465
+ const missingConfigs = findMissingConfigs(config2.configs, configs, usedConfigs);
189466
+ if (missingSecrets.length > 0 || missingConfigs.length > 0) {
189467
+ if (isInteractive()) {
189468
+ if (missingSecrets.length > 0) {
189469
+ setState((s) => ({
189470
+ ...s,
189471
+ status: "awaiting-secrets",
189472
+ missingSecrets,
189473
+ currentSecretIndex: 0,
189474
+ missingConfigs: missingConfigs.length > 0 ? missingConfigs : void 0
189475
+ }));
189476
+ return;
189477
+ } else {
189478
+ setState((s) => ({
189479
+ ...s,
189480
+ status: "awaiting-configs",
189481
+ missingConfigs,
189482
+ currentConfigIndex: 0
189483
+ }));
189484
+ return;
189485
+ }
189486
+ } else {
189487
+ const errorParts = [];
189488
+ if (missingSecrets.length > 0) {
189489
+ errorParts.push(`Missing secrets: ${missingSecrets.join(", ")}
189490
+ Add them to the secrets block in specific.local`);
189491
+ }
189492
+ if (missingConfigs.length > 0) {
189493
+ errorParts.push(`Missing configs: ${missingConfigs.join(", ")}
189494
+ Add them to the config block in specific.local`);
189495
+ }
189496
+ setState((s) => ({
189497
+ ...s,
189498
+ status: "error",
189499
+ error: errorParts.join("\n\n")
189500
+ }));
189501
+ return;
189502
+ }
189503
+ }
189052
189504
  const validationErrors = validateEndpointReferences(config2);
189053
189505
  if (validationErrors.length > 0) {
189054
189506
  for (const error of validationErrors) {
@@ -189334,33 +189786,57 @@ function DevUI({ instanceKey }) {
189334
189786
  }, [reloadTrigger, readyToStart, instanceKey]);
189335
189787
  if (state.status === "installing-ca") {
189336
189788
  if (state.caInstallPhase === "installing") {
189337
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "TLS Certificate Setup"), /* @__PURE__ */ React4.createElement(Text4, null, " "), /* @__PURE__ */ React4.createElement(Text4, null, "Installing a local certificate authority (CA) to enable HTTPS"), /* @__PURE__ */ React4.createElement(Text4, null, "for local development. The CA is limited to Specific projects."), /* @__PURE__ */ React4.createElement(Text4, null, " "), /* @__PURE__ */ React4.createElement(Text4, null, "Your password is required to add the CA to your system's trust store."), /* @__PURE__ */ React4.createElement(Text4, null, " "));
189789
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "TLS Certificate Setup"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Installing a local certificate authority (CA) to enable HTTPS"), /* @__PURE__ */ React6.createElement(Text6, null, "for local development. The CA is limited to Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Your password is required to add the CA to your system's trust store."), /* @__PURE__ */ React6.createElement(Text6, null, " "));
189338
189790
  }
189339
189791
  if (state.caInstallPhase === "error") {
189340
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Failed to install CA: ", state.caError));
189792
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Failed to install CA: ", state.caError));
189341
189793
  }
189342
189794
  }
189343
189795
  if (state.status === "loading") {
189344
- return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })));
189796
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
189797
+ }
189798
+ if (state.status === "awaiting-secrets" && state.missingSecrets && state.currentSecretIndex !== void 0) {
189799
+ const currentSecret = state.missingSecrets[state.currentSecretIndex];
189800
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Missing secrets (", state.currentSecretIndex + 1, "/", state.missingSecrets.length, "):"), /* @__PURE__ */ React6.createElement(
189801
+ SecretInput,
189802
+ {
189803
+ key: currentSecret,
189804
+ secretName: currentSecret,
189805
+ onSubmit: handleSecretSubmit,
189806
+ onCancel: handleInputCancel
189807
+ }
189808
+ ));
189809
+ }
189810
+ if (state.status === "awaiting-configs" && state.missingConfigs && state.currentConfigIndex !== void 0) {
189811
+ const currentConfig = state.missingConfigs[state.currentConfigIndex];
189812
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Missing configs (", state.currentConfigIndex + 1, "/", state.missingConfigs.length, "):"), /* @__PURE__ */ React6.createElement(
189813
+ ConfigInput,
189814
+ {
189815
+ key: currentConfig,
189816
+ configName: currentConfig,
189817
+ onSubmit: handleConfigSubmit,
189818
+ onCancel: handleInputCancel
189819
+ }
189820
+ ));
189345
189821
  }
189346
189822
  if (state.status === "waiting") {
189347
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React4.createElement(Text4, null, " No specific.hcl in project yet. Go build something with your coding agent!")));
189823
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " No specific.hcl in project yet. Go build something with your coding agent!")));
189348
189824
  }
189349
189825
  if (state.status === "error") {
189350
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Error: ", state.error));
189826
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error));
189351
189827
  }
189352
189828
  if (state.status === "stopping") {
189353
- return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React4.createElement(Text4, null, " Shutting down..."));
189829
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Shutting down..."));
189354
189830
  }
189355
189831
  if (state.status === "reloading") {
189356
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React4.createElement(Text4, null, " "), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React4.createElement(Text4, null, " Reloading (specific.hcl changed)...")));
189832
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Reloading (specific.hcl changed)...")));
189357
189833
  }
189358
189834
  const { config, resources, services, proxy, output } = state;
189359
189835
  if (!config) {
189360
- return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })));
189836
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
189361
189837
  }
189362
189838
  if (state.status === "starting") {
189363
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "Specific dev server"), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (Ctrl+C to stop)")), /* @__PURE__ */ React4.createElement(Text4, null, " "), (config.postgres.length > 0 || config.redis.length > 0 || config.storage.length > 0) && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Resources:"), config.postgres.map((pg) => {
189839
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)")), /* @__PURE__ */ React6.createElement(Text6, null, " "), (config.postgres.length > 0 || config.redis.length > 0 || config.storage.length > 0) && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Resources:"), config.postgres.map((pg) => {
189364
189840
  const instance = resources.get(pg.name);
189365
189841
  const resStatus = state.resourceStatus.get(pg.name);
189366
189842
  const isReady = !!instance;
@@ -189375,7 +189851,7 @@ function DevUI({ instanceKey }) {
189375
189851
  } else if (resStatus?.status === "starting") {
189376
189852
  statusText = " starting...";
189377
189853
  }
189378
- return /* @__PURE__ */ React4.createElement(Text4, { key: pg.name }, /* @__PURE__ */ React4.createElement(Text4, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, pg.name, " (postgres)"), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, statusText));
189854
+ return /* @__PURE__ */ React6.createElement(Text6, { key: pg.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, pg.name, " (postgres)"), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, statusText));
189379
189855
  }), config.redis.map((redis) => {
189380
189856
  const instance = resources.get(redis.name);
189381
189857
  const resStatus = state.resourceStatus.get(redis.name);
@@ -189391,35 +189867,35 @@ function DevUI({ instanceKey }) {
189391
189867
  } else if (resStatus?.status === "starting") {
189392
189868
  statusText = " starting...";
189393
189869
  }
189394
- return /* @__PURE__ */ React4.createElement(Text4, { key: redis.name }, /* @__PURE__ */ React4.createElement(Text4, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, redis.name, " (redis)"), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, statusText));
189870
+ return /* @__PURE__ */ React6.createElement(Text6, { key: redis.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, redis.name, " (redis)"), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, statusText));
189395
189871
  }), config.storage.map((storage) => {
189396
189872
  const instance = resources.get(storage.name);
189397
189873
  const resStatus = state.resourceStatus.get(storage.name);
189398
189874
  const isReady = !!instance;
189399
189875
  const statusText = resStatus?.status === "starting" ? " starting..." : "";
189400
- return /* @__PURE__ */ React4.createElement(Text4, { key: storage.name }, /* @__PURE__ */ React4.createElement(Text4, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, storage.name, " (storage)"), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, statusText));
189401
- }), /* @__PURE__ */ React4.createElement(Text4, null, " ")), config.services.length > 0 && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Services:"), config.services.map((svc) => {
189876
+ return /* @__PURE__ */ React6.createElement(Text6, { key: storage.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, storage.name, " (storage)"), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 localhost:", instance.port), !isReady && statusText && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, statusText));
189877
+ }), /* @__PURE__ */ React6.createElement(Text6, null, " ")), config.services.length > 0 && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Services:"), config.services.map((svc) => {
189402
189878
  const running = services.find((s) => s.name === svc.name);
189403
189879
  const isReady = !!running;
189404
189880
  const defaultPort = running?.ports.get("default");
189405
189881
  const portInfo = defaultPort ? ` \u2192 localhost:${defaultPort}` : "";
189406
- return /* @__PURE__ */ React4.createElement(Text4, { key: svc.name }, /* @__PURE__ */ React4.createElement(Text4, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, svc.name, portInfo));
189407
- }), /* @__PURE__ */ React4.createElement(Text4, null, " ")), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React4.createElement(Text4, null, " Starting...")));
189882
+ return /* @__PURE__ */ React6.createElement(Text6, { key: svc.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name, portInfo));
189883
+ }), /* @__PURE__ */ React6.createElement(Text6, null, " ")), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Starting...")));
189408
189884
  }
189409
189885
  const staticItems = [
189410
189886
  {
189411
189887
  key: "title",
189412
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "Specific dev server"), instanceKey !== "default" && /* @__PURE__ */ React4.createElement(Text4, { color: "yellow" }, " [", instanceKey, "]"), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (Ctrl+C to stop)"))
189888
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), instanceKey !== "default" && /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " [", instanceKey, "]"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)"))
189413
189889
  },
189414
- { key: "space1", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") },
189890
+ { key: "space1", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") },
189415
189891
  // Show admin UI URL
189416
189892
  {
189417
189893
  key: "admin",
189418
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Admin:"), /* @__PURE__ */ React4.createElement(Text4, null, " "), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "local.spcf.app"))
189894
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "local.spcf.app"))
189419
189895
  },
189420
- { key: "admin-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") },
189896
+ { key: "admin-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") },
189421
189897
  ...services.length > 0 ? [
189422
- { key: "svc-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Services:") },
189898
+ { key: "svc-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Services:") },
189423
189899
  ...services.flatMap((svc) => {
189424
189900
  const serviceConfig = config.services.find((s) => s.name === svc.name);
189425
189901
  const endpoints = serviceConfig?.endpoints || [];
@@ -189427,7 +189903,7 @@ function DevUI({ instanceKey }) {
189427
189903
  const defaultPort = svc.ports.get("default");
189428
189904
  return [{
189429
189905
  key: `svc-${svc.name}`,
189430
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, svc.name), defaultPort && /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (localhost:", defaultPort, ")"))
189906
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name), defaultPort && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", defaultPort, ")"))
189431
189907
  }];
189432
189908
  }
189433
189909
  return endpoints.map((endpoint) => {
@@ -189436,61 +189912,61 @@ function DevUI({ instanceKey }) {
189436
189912
  const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
189437
189913
  return {
189438
189914
  key: `svc-${svc.name}-${endpoint.name}`,
189439
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 "), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".local.spcf.app"), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, " (localhost:", port, ")") : null)
189915
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".local.spcf.app"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
189440
189916
  };
189441
189917
  });
189442
189918
  }),
189443
- { key: "space2", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189919
+ { key: "space2", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189444
189920
  ] : [],
189445
189921
  ...config.postgres.length > 0 ? [
189446
- { key: "pg-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Postgres:") },
189922
+ { key: "pg-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Postgres:") },
189447
189923
  ...config.postgres.map((pg) => {
189448
189924
  const instance = resources.get(pg.name);
189449
189925
  return {
189450
189926
  key: `pg-${pg.name}`,
189451
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, pg.name), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 ", instance.url))
189927
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, pg.name), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 ", instance.url))
189452
189928
  };
189453
189929
  }),
189454
- { key: "pg-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189930
+ { key: "pg-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189455
189931
  ] : [],
189456
189932
  ...config.redis.length > 0 ? [
189457
- { key: "redis-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Redis:") },
189933
+ { key: "redis-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Redis:") },
189458
189934
  ...config.redis.map((redis) => {
189459
189935
  const instance = resources.get(redis.name);
189460
189936
  return {
189461
189937
  key: `redis-${redis.name}`,
189462
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, redis.name), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 localhost:", instance.port))
189938
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, redis.name), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 localhost:", instance.port))
189463
189939
  };
189464
189940
  }),
189465
- { key: "redis-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189941
+ { key: "redis-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189466
189942
  ] : [],
189467
189943
  ...config.storage.length > 0 ? [
189468
- { key: "storage-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Storage:") },
189944
+ { key: "storage-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Storage:") },
189469
189945
  ...config.storage.map((storage) => {
189470
189946
  const instance = resources.get(storage.name);
189471
189947
  return {
189472
189948
  key: `storage-${storage.name}`,
189473
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "green" }, " \u25CF "), /* @__PURE__ */ React4.createElement(Text4, null, storage.name), instance && /* @__PURE__ */ React4.createElement(Text4, null, " \u2192 localhost:", instance.port))
189949
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, storage.name), instance && /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 localhost:", instance.port))
189474
189950
  };
189475
189951
  }),
189476
- { key: "storage-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189952
+ { key: "storage-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189477
189953
  ] : [],
189478
- { key: "separator", content: /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "\u2500".repeat(50)) },
189954
+ { key: "separator", content: /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2500".repeat(50)) },
189479
189955
  ...state.parseError ? [
189480
189956
  {
189481
189957
  key: "parse-error",
189482
- content: /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Warning: ", state.parseError)
189958
+ content: /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Warning: ", state.parseError)
189483
189959
  }
189484
189960
  ] : [],
189485
189961
  ...output.map((line, i) => ({
189486
189962
  key: `log-${i}`,
189487
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: line.color }, "[", line.service, "]"), " ", line.text)
189963
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: line.color }, "[", line.service, "]"), " ", line.text)
189488
189964
  }))
189489
189965
  ];
189490
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React4.createElement(Box4, { key: item.key }, item.content)));
189966
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React6.createElement(Box6, { key: item.key }, item.content)));
189491
189967
  }
189492
189968
  function devCommand(instanceKey) {
189493
- render4(/* @__PURE__ */ React4.createElement(DevUI, { instanceKey }));
189969
+ render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey }));
189494
189970
  }
189495
189971
 
189496
189972
  // src/lib/dev/git-worktree.ts
@@ -189533,8 +190009,9 @@ function getDefaultKey() {
189533
190009
  }
189534
190010
 
189535
190011
  // src/commands/deploy.tsx
189536
- import React5, { useState as useState5, useEffect as useEffect5, useCallback } from "react";
189537
- import { render as render5, Text as Text5, Box as Box5, useApp as useApp4, useInput as useInput3 } from "ink";
190012
+ init_open();
190013
+ import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
190014
+ import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
189538
190015
  import Spinner5 from "ink-spinner";
189539
190016
  import * as fs21 from "fs";
189540
190017
  import * as path19 from "path";
@@ -189542,7 +190019,7 @@ import * as path19 from "path";
189542
190019
  // src/lib/deploy/build-tester.ts
189543
190020
  import { spawn as spawn5 } from "child_process";
189544
190021
  import { existsSync as existsSync17 } from "fs";
189545
- import { join as join18, resolve as resolve5 } from "path";
190022
+ import { join as join18 } from "path";
189546
190023
  function getDependencyInstallCommand(build, projectDir) {
189547
190024
  switch (build.base) {
189548
190025
  case "node":
@@ -189631,12 +190108,12 @@ function runCommand2(command, projectDir, buildName) {
189631
190108
  async function testBuild(build, projectDir) {
189632
190109
  const startTime = Date.now();
189633
190110
  const outputs = [];
189634
- const contextDir = resolve5(projectDir, build.context || ".");
189635
- writeLog("build-test", `Starting test for build "${build.name}" (base: ${build.base}, context: ${contextDir})`);
189636
- const depsCommand = getDependencyInstallCommand(build, contextDir);
190111
+ const workDir = projectDir;
190112
+ writeLog("build-test", `Starting test for build "${build.name}" (base: ${build.base}, workDir: ${workDir})`);
190113
+ const depsCommand = getDependencyInstallCommand(build, workDir);
189637
190114
  if (depsCommand) {
189638
190115
  writeLog("build-test", `[${build.name}] Installing dependencies...`);
189639
- const depsResult = await runCommand2(depsCommand, contextDir, build.name);
190116
+ const depsResult = await runCommand2(depsCommand, workDir, build.name);
189640
190117
  outputs.push(`[${depsCommand}]
189641
190118
  ${depsResult.output}`);
189642
190119
  if (!depsResult.success) {
@@ -189655,7 +190132,7 @@ ${depsResult.output}`);
189655
190132
  }
189656
190133
  if (build.command) {
189657
190134
  writeLog("build-test", `[${build.name}] Running build command...`);
189658
- const buildResult = await runCommand2(build.command, contextDir, build.name);
190135
+ const buildResult = await runCommand2(build.command, workDir, build.name);
189659
190136
  outputs.push(`[${build.command}]
189660
190137
  ${buildResult.output}`);
189661
190138
  if (!buildResult.success) {
@@ -189857,57 +190334,23 @@ function PhaseIndicator({
189857
190334
  const currentIndex = phases.indexOf(currentPhase);
189858
190335
  if (currentPhase === "error") {
189859
190336
  if (phaseIndex < currentIndex) {
189860
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, " ", "\u2713"), " ", label);
190337
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, " ", "\u2713"), " ", label);
189861
190338
  }
189862
190339
  if (phaseIndex === currentIndex) {
189863
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, " ", "\u2717"), " ", label);
190340
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, " ", "\u2717"), " ", label);
189864
190341
  }
189865
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, label));
190342
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, label));
189866
190343
  }
189867
190344
  if (phaseIndex < currentIndex) {
189868
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, " ", "\u2713"), " ", label);
190345
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, " ", "\u2713"), " ", label);
189869
190346
  }
189870
190347
  if (phaseIndex === currentIndex) {
189871
190348
  if (showSpinner) {
189872
- return /* @__PURE__ */ React5.createElement(Text5, null, " ", /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), " ", label);
190349
+ return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), " ", label);
189873
190350
  }
189874
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, " ", "\u25CF"), " ", label);
190351
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, " ", "\u25CF"), " ", label);
189875
190352
  }
189876
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, label));
189877
- }
189878
- function SecretInput({ secretName, onSubmit, onCancel }) {
189879
- const [value, setValue] = useState5("");
189880
- useInput3((input, key) => {
189881
- if (key.return) {
189882
- if (value.trim() !== "") {
189883
- onSubmit(value);
189884
- }
189885
- } else if (key.escape) {
189886
- onCancel();
189887
- } else if (key.backspace || key.delete) {
189888
- setValue((prev) => prev.slice(0, -1));
189889
- } else if (!key.ctrl && !key.meta && input) {
189890
- setValue((prev) => prev + input);
189891
- }
189892
- });
189893
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, secretName), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value.length > 0 ? "*".repeat(value.length) : ""), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Press Enter to save, Esc to cancel deployment)"));
189894
- }
189895
- function ConfigInput({ configName, onSubmit, onCancel }) {
189896
- const [value, setValue] = useState5("");
189897
- useInput3((input, key) => {
189898
- if (key.return) {
189899
- if (value.trim() !== "") {
189900
- onSubmit(value);
189901
- }
189902
- } else if (key.escape) {
189903
- onCancel();
189904
- } else if (key.backspace || key.delete) {
189905
- setValue((prev) => prev.slice(0, -1));
189906
- } else if (!key.ctrl && !key.meta && input) {
189907
- setValue((prev) => prev + input);
189908
- }
189909
- });
189910
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Press Enter to save, Esc to cancel deployment)"));
190353
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, label));
189911
190354
  }
189912
190355
  function ProjectSelector({
189913
190356
  projects,
@@ -189916,7 +190359,7 @@ function ProjectSelector({
189916
190359
  onUp,
189917
190360
  onDown
189918
190361
  }) {
189919
- useInput3((input, key) => {
190362
+ useInput5((input, key) => {
189920
190363
  if (key.return) {
189921
190364
  if (selectedIndex === 0) {
189922
190365
  onSelect("new");
@@ -189933,11 +190376,11 @@ function ProjectSelector({
189933
190376
  { id: "new", name: "Create new project", isNew: true },
189934
190377
  ...projects.map((p) => ({ ...p, isNew: false }))
189935
190378
  ];
189936
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Select a project to deploy:"), /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React5.createElement(Text5, { key: item.id }, index === selectedIndex ? /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> ") : /* @__PURE__ */ React5.createElement(Text5, null, " "), item.isNew ? /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, item.name) : /* @__PURE__ */ React5.createElement(Text5, null, item.name, " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(", item.id, ")"))))), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Use arrow keys to navigate, Enter to select"));
190379
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Select a project to deploy:"), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, items.map((item, index) => /* @__PURE__ */ React7.createElement(Text7, { key: item.id }, index === selectedIndex ? /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> ") : /* @__PURE__ */ React7.createElement(Text7, null, " "), item.isNew ? /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, item.name) : /* @__PURE__ */ React7.createElement(Text7, null, item.name, " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(", item.id, ")"))))), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Use arrow keys to navigate, Enter to select"));
189937
190380
  }
189938
190381
  function NameInput({ onSubmit, onCancel }) {
189939
- const [value, setValue] = useState5("");
189940
- useInput3((input, key) => {
190382
+ const [value, setValue] = useState6("");
190383
+ useInput5((input, key) => {
189941
190384
  if (key.return) {
189942
190385
  if (value.trim() !== "") {
189943
190386
  onSubmit(value.trim());
@@ -189950,13 +190393,13 @@ function NameInput({ onSubmit, onCancel }) {
189950
190393
  setValue((prev) => prev + input);
189951
190394
  }
189952
190395
  });
189953
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Create new project"), /* @__PURE__ */ React5.createElement(Text5, null, "Enter project name:"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Press Enter to create, Esc to go back"));
190396
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Create new project"), /* @__PURE__ */ React7.createElement(Text7, null, "Enter project name:"), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> "), /* @__PURE__ */ React7.createElement(Text7, null, value), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "|")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Press Enter to create, Esc to go back"));
189954
190397
  }
189955
190398
  function DeployUI({ environment, config, skipBuildTest }) {
189956
- const { exit } = useApp4();
189957
- const [state, setState] = useState5({ phase: "checking-auth" });
189958
- const clientRef = React5.useRef(null);
189959
- useEffect5(() => {
190399
+ const { exit } = useApp3();
190400
+ const [state, setState] = useState6({ phase: "checking-auth" });
190401
+ const clientRef = React7.useRef(null);
190402
+ useEffect4(() => {
189960
190403
  if (state.phase !== "checking-auth") return;
189961
190404
  const projectDir = process.cwd();
189962
190405
  if (hasProjectId(projectDir)) {
@@ -189977,7 +190420,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
189977
190420
  }
189978
190421
  setState({ phase: "logging-in" });
189979
190422
  }, [state.phase]);
189980
- useEffect5(() => {
190423
+ useEffect4(() => {
189981
190424
  if (state.phase !== "logging-in") return;
189982
190425
  let cancelled = false;
189983
190426
  async function startLogin() {
@@ -190042,7 +190485,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190042
190485
  cancelled = true;
190043
190486
  };
190044
190487
  }, [state.phase]);
190045
- useEffect5(() => {
190488
+ useEffect4(() => {
190046
190489
  if (state.phase !== "loading-projects") return;
190047
190490
  let cancelled = false;
190048
190491
  async function loadProjects() {
@@ -190089,7 +190532,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190089
190532
  const handleNameCancel = useCallback(() => {
190090
190533
  setState((s) => ({ ...s, phase: "selecting-project" }));
190091
190534
  }, []);
190092
- useEffect5(() => {
190535
+ useEffect4(() => {
190093
190536
  if (state.phase !== "creating-project" || !state.newProjectName) return;
190094
190537
  let cancelled = false;
190095
190538
  async function createProject() {
@@ -190174,7 +190617,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190174
190617
  error: "Deployment cancelled - configs not provided"
190175
190618
  }));
190176
190619
  }, []);
190177
- useEffect5(() => {
190620
+ useEffect4(() => {
190178
190621
  const {
190179
190622
  phase: phase2,
190180
190623
  deployment: deployment2,
@@ -190216,7 +190659,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190216
190659
  }
190217
190660
  })();
190218
190661
  }, [state]);
190219
- useEffect5(() => {
190662
+ useEffect4(() => {
190220
190663
  const {
190221
190664
  phase: phase2,
190222
190665
  deployment: deployment2,
@@ -190258,7 +190701,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190258
190701
  }
190259
190702
  })();
190260
190703
  }, [state]);
190261
- useEffect5(() => {
190704
+ useEffect4(() => {
190262
190705
  if (state.phase !== "testing-builds" || !state.projectId) return;
190263
190706
  let cancelled = false;
190264
190707
  let pollInterval;
@@ -190448,7 +190891,7 @@ ${errorMsg}`
190448
190891
  if (pollInterval) clearInterval(pollInterval);
190449
190892
  };
190450
190893
  }, [state.projectId, environment, config.builds, skipBuildTest]);
190451
- useEffect5(() => {
190894
+ useEffect4(() => {
190452
190895
  let pollInterval;
190453
190896
  if ((state.phase === "building" || state.phase === "deploying") && state.deployment && state.missingSecrets === void 0 && state.secretValues === void 0 && state.missingConfigs === void 0 && state.configValues === void 0) {
190454
190897
  const client2 = clientRef.current;
@@ -190471,6 +190914,22 @@ ${errorMsg}`
190471
190914
  if (pollInterval) clearInterval(pollInterval);
190472
190915
  return;
190473
190916
  }
190917
+ if (status.state === "awaiting_configs") {
190918
+ if (pollInterval) clearInterval(pollInterval);
190919
+ pollInterval = void 0;
190920
+ const missingConfigs2 = status.missingConfigs || [];
190921
+ writeLog("deploy", `Awaiting configs: ${missingConfigs2.join(", ")}`);
190922
+ setState((s) => ({
190923
+ ...s,
190924
+ phase: "awaiting-configs",
190925
+ deployment: status,
190926
+ missingConfigs: missingConfigs2,
190927
+ configValues: {},
190928
+ currentConfigIndex: 0,
190929
+ currentConfigInput: ""
190930
+ }));
190931
+ return;
190932
+ }
190474
190933
  if (status.state === "deploying") {
190475
190934
  setState((s) => ({ ...s, phase: "deploying", deployment: status }));
190476
190935
  } else if (status.state === "building") {
@@ -190486,12 +190945,12 @@ ${errorMsg}`
190486
190945
  };
190487
190946
  }
190488
190947
  }, [state.phase, state.missingSecrets, state.secretValues, state.missingConfigs, state.configValues]);
190489
- useEffect5(() => {
190948
+ useEffect4(() => {
190490
190949
  if (state.phase === "testing-builds") {
190491
190950
  trackEvent("deploy_started", { environment });
190492
190951
  }
190493
190952
  }, [state.phase, environment]);
190494
- useEffect5(() => {
190953
+ useEffect4(() => {
190495
190954
  if (state.phase === "success") {
190496
190955
  trackEvent("deploy_succeeded", { environment });
190497
190956
  closeDebugLog();
@@ -190520,16 +190979,16 @@ ${errorMsg}`
190520
190979
  verificationUri
190521
190980
  } = state;
190522
190981
  if (phase === "checking-auth") {
190523
- return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " Checking authentication..."));
190982
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Checking authentication..."));
190524
190983
  }
190525
190984
  if (phase === "logging-in") {
190526
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Text5, null, "Your authentication code:", " ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " Initiating login...")));
190985
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Initiating login...")));
190527
190986
  }
190528
190987
  if (phase === "loading-projects") {
190529
- return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " Loading projects..."));
190988
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
190530
190989
  }
190531
190990
  if (phase === "selecting-project" && projects && selectedIndex !== void 0) {
190532
- return /* @__PURE__ */ React5.createElement(
190991
+ return /* @__PURE__ */ React7.createElement(
190533
190992
  ProjectSelector,
190534
190993
  {
190535
190994
  projects,
@@ -190550,57 +191009,57 @@ ${errorMsg}`
190550
191009
  );
190551
191010
  }
190552
191011
  if (phase === "entering-name") {
190553
- return /* @__PURE__ */ React5.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
191012
+ return /* @__PURE__ */ React7.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
190554
191013
  }
190555
191014
  if (phase === "creating-project") {
190556
- return /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " Creating project..."));
191015
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Creating project..."));
190557
191016
  }
190558
191017
  const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
190559
191018
  const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
190560
191019
  const displayPhase = phase === "awaiting-secrets" || phase === "awaiting-configs" ? "building" : phase;
190561
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "cyan" }, "Deploying to ", environment), deployment && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " (", deployment.id, ")")), /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(
191020
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "Deploying to ", environment), deployment && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " (", deployment.id, ")")), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
190562
191021
  PhaseIndicator,
190563
191022
  {
190564
191023
  phase: "testing-builds",
190565
191024
  currentPhase: displayPhase,
190566
191025
  label: "Testing builds locally"
190567
191026
  }
190568
- ), /* @__PURE__ */ React5.createElement(
191027
+ ), /* @__PURE__ */ React7.createElement(
190569
191028
  PhaseIndicator,
190570
191029
  {
190571
191030
  phase: "creating-tarball",
190572
191031
  currentPhase: displayPhase,
190573
191032
  label: `Creating archive${tarballSize ? ` (${formatBytes(tarballSize)})` : ""}`
190574
191033
  }
190575
- ), /* @__PURE__ */ React5.createElement(
191034
+ ), /* @__PURE__ */ React7.createElement(
190576
191035
  PhaseIndicator,
190577
191036
  {
190578
191037
  phase: "creating-deployment",
190579
191038
  currentPhase: displayPhase,
190580
191039
  label: "Creating deployment"
190581
191040
  }
190582
- ), /* @__PURE__ */ React5.createElement(
191041
+ ), /* @__PURE__ */ React7.createElement(
190583
191042
  PhaseIndicator,
190584
191043
  {
190585
191044
  phase: "uploading",
190586
191045
  currentPhase: displayPhase,
190587
191046
  label: "Uploading"
190588
191047
  }
190589
- ), /* @__PURE__ */ React5.createElement(
191048
+ ), /* @__PURE__ */ React7.createElement(
190590
191049
  PhaseIndicator,
190591
191050
  {
190592
191051
  phase: "building",
190593
191052
  currentPhase: displayPhase,
190594
191053
  label: "Building"
190595
191054
  }
190596
- ), /* @__PURE__ */ React5.createElement(
191055
+ ), /* @__PURE__ */ React7.createElement(
190597
191056
  PhaseIndicator,
190598
191057
  {
190599
191058
  phase: "deploying",
190600
191059
  currentPhase: displayPhase,
190601
191060
  label: "Deploying"
190602
191061
  }
190603
- )), phase === "awaiting-secrets" && currentSecret && /* @__PURE__ */ React5.createElement(
191062
+ )), phase === "awaiting-secrets" && currentSecret && /* @__PURE__ */ React7.createElement(
190604
191063
  SecretInput,
190605
191064
  {
190606
191065
  key: currentSecret,
@@ -190608,7 +191067,7 @@ ${errorMsg}`
190608
191067
  onSubmit: handleSecretSubmit,
190609
191068
  onCancel: handleSecretCancel
190610
191069
  }
190611
- ), phase === "awaiting-configs" && currentConfig && /* @__PURE__ */ React5.createElement(
191070
+ ), phase === "awaiting-configs" && currentConfig && /* @__PURE__ */ React7.createElement(
190612
191071
  ConfigInput,
190613
191072
  {
190614
191073
  key: currentConfig,
@@ -190616,7 +191075,7 @@ ${errorMsg}`
190616
191075
  onSubmit: handleConfigSubmit,
190617
191076
  onCancel: handleConfigCancel
190618
191077
  }
190619
- ), phase === "error" && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, "Error: ", error)), phase === "success" && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React5.createElement(Text5, { key: name }, " ", name, ": ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, url))))));
191078
+ ), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error)), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
190620
191079
  }
190621
191080
  async function deployCommand(environment, options2) {
190622
191081
  const configPath = path19.join(process.cwd(), "specific.hcl");
@@ -190637,7 +191096,7 @@ async function deployCommand(environment, options2) {
190637
191096
  const env2 = environment || "prod";
190638
191097
  const skipBuildTest = options2?.skipBuildTest ?? false;
190639
191098
  render5(
190640
- /* @__PURE__ */ React5.createElement(
191099
+ /* @__PURE__ */ React7.createElement(
190641
191100
  DeployUI,
190642
191101
  {
190643
191102
  environment: env2,
@@ -190877,14 +191336,14 @@ async function psqlCommand(databaseName, instanceKey = "default") {
190877
191336
  }
190878
191337
 
190879
191338
  // src/commands/clean.tsx
190880
- import React6, { useState as useState6, useEffect as useEffect6 } from "react";
190881
- import { render as render6, Text as Text6, Box as Box6 } from "ink";
191339
+ import React8, { useState as useState7, useEffect as useEffect5 } from "react";
191340
+ import { render as render6, Text as Text8, Box as Box8 } from "ink";
190882
191341
  import Spinner6 from "ink-spinner";
190883
191342
  import * as fs23 from "fs";
190884
191343
  import * as path21 from "path";
190885
191344
  function CleanUI({ instanceKey }) {
190886
- const [state, setState] = useState6({ status: "checking" });
190887
- useEffect6(() => {
191345
+ const [state, setState] = useState7({ status: "checking" });
191346
+ useEffect5(() => {
190888
191347
  async function clean() {
190889
191348
  const projectRoot = process.cwd();
190890
191349
  const specificDir = path21.join(projectRoot, ".specific");
@@ -190960,345 +191419,50 @@ function CleanUI({ instanceKey }) {
190960
191419
  clean();
190961
191420
  }, [instanceKey]);
190962
191421
  if (state.status === "checking") {
190963
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Checking for running instances..."));
191422
+ return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Checking for running instances..."));
190964
191423
  }
190965
191424
  if (state.status === "cleaning") {
190966
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Removing .specific directory..."));
191425
+ return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Removing .specific directory..."));
190967
191426
  }
190968
191427
  if (state.status === "error") {
190969
- return /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error);
191428
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", state.error);
190970
191429
  }
190971
191430
  if (state.status === "nothing") {
190972
191431
  const target2 = instanceKey ? `key "${instanceKey}"` : ".specific directory";
190973
- return /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
191432
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
190974
191433
  }
190975
191434
  const target = instanceKey ? `key "${instanceKey}"` : ".specific directory";
190976
- return /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "Cleaned ", target);
191435
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, "Cleaned ", target);
190977
191436
  }
190978
191437
  function cleanCommand(instanceKey) {
190979
191438
  if (instanceKey) {
190980
- render6(/* @__PURE__ */ React6.createElement(CleanUI, { instanceKey }));
190981
- } else {
190982
- render6(/* @__PURE__ */ React6.createElement(CleanUI, null));
190983
- }
190984
- }
190985
-
190986
- // src/commands/secrets.tsx
190987
- import React7, { useState as useState7, useEffect as useEffect7 } from "react";
190988
- import { render as render7, Text as Text7, Box as Box7, useInput as useInput4, useApp as useApp5 } from "ink";
190989
- import * as fs24 from "fs";
190990
- var HEADER_COMMENT = "# Do not commit this file - it contains secrets\n";
190991
- function SetSecretUI({ secretName }) {
190992
- const { exit } = useApp5();
190993
- const [value, setValue] = useState7("");
190994
- const [done, setDone] = useState7(false);
190995
- const [error, setError] = useState7(null);
190996
- useInput4((input, key) => {
190997
- if (done) return;
190998
- if (key.return) {
190999
- if (value.trim() === "") {
191000
- setError("Secret value cannot be empty");
191001
- return;
191002
- }
191003
- try {
191004
- const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
191005
- const hclLine = `${secretName} = "${escapedValue}"`;
191006
- let content = "";
191007
- let hasHeader = false;
191008
- if (fs24.existsSync(SECRETS_FILE)) {
191009
- content = fs24.readFileSync(SECRETS_FILE, "utf-8");
191010
- hasHeader = content.startsWith("#");
191011
- const lines = content.split("\n");
191012
- const newLines = [];
191013
- let found = false;
191014
- const pattern = new RegExp(`^${secretName}\\s*=\\s*"`);
191015
- for (const line of lines) {
191016
- if (pattern.test(line.trim())) {
191017
- newLines.push(hclLine);
191018
- found = true;
191019
- } else {
191020
- newLines.push(line);
191021
- }
191022
- }
191023
- if (found) {
191024
- fs24.writeFileSync(SECRETS_FILE, newLines.join("\n"));
191025
- setDone(true);
191026
- return;
191027
- }
191028
- }
191029
- let newContent = "";
191030
- if (!hasHeader && !content) {
191031
- newContent = HEADER_COMMENT + "\n";
191032
- } else if (content) {
191033
- newContent = content.endsWith("\n") ? content : content + "\n";
191034
- }
191035
- newContent += `${hclLine}
191036
- `;
191037
- fs24.writeFileSync(SECRETS_FILE, newContent);
191038
- setDone(true);
191039
- } catch (err) {
191040
- setError(err instanceof Error ? err.message : String(err));
191041
- }
191042
- } else if (key.backspace || key.delete) {
191043
- setValue((prev) => prev.slice(0, -1));
191044
- } else if (key.escape) {
191045
- exit();
191046
- } else if (!key.ctrl && !key.meta && input) {
191047
- setValue((prev) => prev + input);
191048
- }
191049
- });
191050
- useEffect7(() => {
191051
- if (done) {
191052
- const timer = setTimeout(() => exit(), 50);
191053
- return () => clearTimeout(timer);
191054
- }
191055
- }, [done, exit]);
191056
- if (error) {
191057
- return /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error);
191058
- }
191059
- if (done) {
191060
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Secret '", secretName, "' saved to ", SECRETS_FILE));
191061
- }
191062
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, null, "Enter value for secret '", secretName, "':"), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> "), /* @__PURE__ */ React7.createElement(Text7, null, value.length > 0 ? "*".repeat(value.length) : ""), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "|")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
191063
- }
191064
- async function secretsSetCommand(secretName) {
191065
- if (!secretName) {
191066
- console.error("Error: Secret name required");
191067
- console.error("Usage: specific secrets set <name>");
191068
- process.exit(1);
191069
- }
191070
- render7(/* @__PURE__ */ React7.createElement(SetSecretUI, { secretName }));
191071
- }
191072
- async function secretsCommand(action, secretName) {
191073
- if (!action) {
191074
- console.error("Usage: specific secrets <command>");
191075
- console.error("");
191076
- console.error("Commands:");
191077
- console.error(" set <name> Set a secret value");
191078
- process.exit(1);
191079
- }
191080
- if (action === "set") {
191081
- if (!secretName) {
191082
- console.error("Error: Secret name required");
191083
- console.error("Usage: specific secrets set <name>");
191084
- process.exit(1);
191085
- }
191086
- await secretsSetCommand(secretName);
191439
+ render6(/* @__PURE__ */ React8.createElement(CleanUI, { instanceKey }));
191087
191440
  } else {
191088
- console.error(`Unknown command: ${action}`);
191089
- console.error("Usage: specific secrets set <name>");
191090
- process.exit(1);
191091
- }
191092
- }
191093
-
191094
- // src/commands/config.tsx
191095
- import React8, { useState as useState8, useEffect as useEffect8 } from "react";
191096
- import { render as render8, Text as Text8, Box as Box8, useInput as useInput5, useApp as useApp6 } from "ink";
191097
- import * as fs25 from "fs";
191098
- var HEADER_COMMENT2 = "# Configuration values for this project\n# These values override defaults defined in specific.hcl\n";
191099
- function SetConfigUI({ configName, initialValue }) {
191100
- const { exit } = useApp6();
191101
- const [value, setValue] = useState8(initialValue ?? "");
191102
- const [done, setDone] = useState8(false);
191103
- const [error, setError] = useState8(null);
191104
- useInput5((input, key) => {
191105
- if (done) return;
191106
- if (key.return) {
191107
- if (value.trim() === "") {
191108
- setError("Config value cannot be empty");
191109
- return;
191110
- }
191111
- try {
191112
- const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
191113
- const hclLine = `${configName} = "${escapedValue}"`;
191114
- let content = "";
191115
- let hasHeader = false;
191116
- if (fs25.existsSync(CONFIG_FILE)) {
191117
- content = fs25.readFileSync(CONFIG_FILE, "utf-8");
191118
- hasHeader = content.startsWith("#");
191119
- const lines = content.split("\n");
191120
- const newLines = [];
191121
- let found = false;
191122
- const pattern = new RegExp(`^${configName}\\s*=\\s*"`);
191123
- for (const line of lines) {
191124
- if (pattern.test(line.trim())) {
191125
- newLines.push(hclLine);
191126
- found = true;
191127
- } else {
191128
- newLines.push(line);
191129
- }
191130
- }
191131
- if (found) {
191132
- fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191133
- setDone(true);
191134
- return;
191135
- }
191136
- }
191137
- let newContent = "";
191138
- if (!hasHeader && !content) {
191139
- newContent = HEADER_COMMENT2 + "\n";
191140
- } else if (content) {
191141
- newContent = content.endsWith("\n") ? content : content + "\n";
191142
- }
191143
- newContent += `${hclLine}
191144
- `;
191145
- fs25.writeFileSync(CONFIG_FILE, newContent);
191146
- setDone(true);
191147
- } catch (err) {
191148
- setError(err instanceof Error ? err.message : String(err));
191149
- }
191150
- } else if (key.backspace || key.delete) {
191151
- setValue((prev) => prev.slice(0, -1));
191152
- } else if (key.escape) {
191153
- exit();
191154
- } else if (!key.ctrl && !key.meta && input) {
191155
- setValue((prev) => prev + input);
191156
- }
191157
- });
191158
- useEffect8(() => {
191159
- if (done) {
191160
- const timer = setTimeout(() => exit(), 50);
191161
- return () => clearTimeout(timer);
191162
- }
191163
- }, [done, exit]);
191164
- if (error) {
191165
- return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", error);
191166
- }
191167
- if (done) {
191168
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, "Config '", configName, "' saved to ", CONFIG_FILE));
191169
- }
191170
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, null, "Enter value for config '", configName, "':"), /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "cyan" }, "> "), /* @__PURE__ */ React8.createElement(Text8, null, value), /* @__PURE__ */ React8.createElement(Text8, { color: "gray" }, "|")), /* @__PURE__ */ React8.createElement(Text8, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
191171
- }
191172
- async function configSetCommand(configName, configValue) {
191173
- if (!configName) {
191174
- console.error("Error: Config name required");
191175
- console.error("Usage: specific config set <name> [value]");
191176
- process.exit(1);
191177
- }
191178
- if (configValue !== void 0) {
191179
- try {
191180
- const escapedValue = configValue.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
191181
- const hclLine = `${configName} = "${escapedValue}"`;
191182
- let content = "";
191183
- let hasHeader = false;
191184
- if (fs25.existsSync(CONFIG_FILE)) {
191185
- content = fs25.readFileSync(CONFIG_FILE, "utf-8");
191186
- hasHeader = content.startsWith("#");
191187
- const lines = content.split("\n");
191188
- const newLines = [];
191189
- let found = false;
191190
- const pattern = new RegExp(`^${configName}\\s*=\\s*"`);
191191
- for (const line of lines) {
191192
- if (pattern.test(line.trim())) {
191193
- newLines.push(hclLine);
191194
- found = true;
191195
- } else {
191196
- newLines.push(line);
191197
- }
191198
- }
191199
- if (found) {
191200
- fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191201
- console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
191202
- return;
191203
- }
191204
- }
191205
- let newContent = "";
191206
- if (!hasHeader && !content) {
191207
- newContent = HEADER_COMMENT2 + "\n";
191208
- } else if (content) {
191209
- newContent = content.endsWith("\n") ? content : content + "\n";
191210
- }
191211
- newContent += `${hclLine}
191212
- `;
191213
- fs25.writeFileSync(CONFIG_FILE, newContent);
191214
- console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
191215
- } catch (err) {
191216
- console.error("Error:", err instanceof Error ? err.message : String(err));
191217
- process.exit(1);
191218
- }
191219
- return;
191220
- }
191221
- render8(/* @__PURE__ */ React8.createElement(SetConfigUI, { configName }));
191222
- }
191223
- async function configCommand(action, configName, configValue) {
191224
- if (!action) {
191225
- console.error("Usage: specific config <command>");
191226
- console.error("");
191227
- console.error("Commands:");
191228
- console.error(" set <name> [value] Set a config value");
191229
- process.exit(1);
191230
- }
191231
- if (action === "set") {
191232
- if (!configName) {
191233
- console.error("Error: Config name required");
191234
- console.error("Usage: specific config set <name> [value]");
191235
- process.exit(1);
191236
- }
191237
- await configSetCommand(configName, configValue);
191238
- } else {
191239
- console.error(`Unknown command: ${action}`);
191240
- console.error("Usage: specific config set <name> [value]");
191241
- process.exit(1);
191441
+ render6(/* @__PURE__ */ React8.createElement(CleanUI, null));
191242
191442
  }
191243
191443
  }
191244
191444
 
191245
191445
  // src/commands/login.tsx
191246
- import React9, { useState as useState9, useEffect as useEffect9 } from "react";
191247
- import { render as render9, Text as Text9, Box as Box9, useApp as useApp7 } from "ink";
191248
- import Spinner7 from "ink-spinner";
191249
- function AlreadyLoggedInUI() {
191250
- const { exit } = useApp7();
191251
- useEffect9(() => {
191252
- const timer = setTimeout(() => exit(), 100);
191253
- return () => clearTimeout(timer);
191254
- }, [exit]);
191255
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, "Already logged in."), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "Credentials saved to ~/.specific/credentials.json"));
191256
- }
191257
- function CheckingUI() {
191258
- return /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { color: "blue" }, /* @__PURE__ */ React9.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React9.createElement(Text9, null, " Checking login status..."));
191259
- }
191260
- function LoginUI() {
191261
- const { exit } = useApp7();
191262
- const [checking, setChecking] = useState9(true);
191263
- const [alreadyLoggedIn, setAlreadyLoggedIn] = useState9(false);
191264
- useEffect9(() => {
191265
- if (isLoggedIn()) {
191266
- setAlreadyLoggedIn(true);
191267
- }
191268
- setChecking(false);
191269
- }, []);
191270
- useEffect9(() => {
191271
- if (checking || alreadyLoggedIn) return;
191272
- async function doLogin() {
191273
- const result = await performLogin();
191274
- if (!result.success) {
191275
- process.exitCode = 1;
191276
- }
191277
- exit();
191278
- }
191279
- doLogin();
191280
- }, [checking, alreadyLoggedIn, exit]);
191281
- if (checking) {
191282
- return /* @__PURE__ */ React9.createElement(CheckingUI, null);
191446
+ async function loginCommand() {
191447
+ if (isLoggedIn()) {
191448
+ console.log("Already logged in.");
191449
+ return;
191283
191450
  }
191284
- if (alreadyLoggedIn) {
191285
- return /* @__PURE__ */ React9.createElement(AlreadyLoggedInUI, null);
191451
+ const result = await performLogin();
191452
+ if (!result.success) {
191453
+ process.exitCode = 1;
191286
191454
  }
191287
- return null;
191288
- }
191289
- function loginCommand() {
191290
- render9(/* @__PURE__ */ React9.createElement(LoginUI, null));
191291
191455
  }
191292
191456
 
191293
191457
  // src/commands/logout.tsx
191294
- import React10, { useState as useState10, useEffect as useEffect10 } from "react";
191295
- import { render as render10, Text as Text10, useApp as useApp8 } from "ink";
191458
+ import React9, { useState as useState8, useEffect as useEffect6 } from "react";
191459
+ import { render as render7, Text as Text9, useApp as useApp4 } from "ink";
191296
191460
  function LogoutUI() {
191297
- const { exit } = useApp8();
191298
- const [state, setState] = useState10({
191461
+ const { exit } = useApp4();
191462
+ const [state, setState] = useState8({
191299
191463
  phase: "checking"
191300
191464
  });
191301
- useEffect10(() => {
191465
+ useEffect6(() => {
191302
191466
  if (state.phase !== "checking") return;
191303
191467
  if (!isLoggedIn()) {
191304
191468
  setState({ phase: "not-logged-in" });
@@ -191307,29 +191471,29 @@ function LogoutUI() {
191307
191471
  clearUserCredentials();
191308
191472
  setState({ phase: "done" });
191309
191473
  }, [state.phase]);
191310
- useEffect10(() => {
191474
+ useEffect6(() => {
191311
191475
  if (state.phase === "done" || state.phase === "not-logged-in") {
191312
191476
  const timer = setTimeout(() => exit(), 100);
191313
191477
  return () => clearTimeout(timer);
191314
191478
  }
191315
191479
  }, [state.phase, exit]);
191316
191480
  if (state.phase === "not-logged-in") {
191317
- return /* @__PURE__ */ React10.createElement(Text10, { dimColor: true }, "Not logged in.");
191481
+ return /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "Not logged in.");
191318
191482
  }
191319
191483
  if (state.phase === "done") {
191320
- return /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "Logged out successfully.");
191484
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, "Logged out successfully.");
191321
191485
  }
191322
- return /* @__PURE__ */ React10.createElement(Text10, null, "Logging out...");
191486
+ return /* @__PURE__ */ React9.createElement(Text9, null, "Logging out...");
191323
191487
  }
191324
191488
  function logoutCommand() {
191325
- render10(/* @__PURE__ */ React10.createElement(LogoutUI, null));
191489
+ render7(/* @__PURE__ */ React9.createElement(LogoutUI, null));
191326
191490
  }
191327
191491
 
191328
191492
  // src/cli.tsx
191329
191493
  var program = new Command();
191330
191494
  var env = "production";
191331
191495
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
191332
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.42").enablePositionalOptions();
191496
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.44").enablePositionalOptions();
191333
191497
  program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
191334
191498
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
191335
191499
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
@@ -191352,8 +191516,6 @@ program.command("psql [database]").description("Connect to a running Postgres da
191352
191516
  program.command("clean").description("Remove .specific directory for a clean slate").option("-k, --key <key>", "Clean only the specified dev environment key").action((options2) => {
191353
191517
  cleanCommand(options2.key);
191354
191518
  });
191355
- program.command("secrets [action] [name]").description("Manage secrets").action(secretsCommand);
191356
- program.command("config [action] [name] [value]").description("Manage configuration values").action(configCommand);
191357
191519
  program.command("login").description("Log in to Specific").action(loginCommand);
191358
191520
  program.command("logout").description("Log out of Specific").action(logoutCommand);
191359
191521
  var commandName = process.argv[2] || "help";