@specific.dev/cli 0.1.42 → 0.1.43

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 (37) 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 +2 -2
  4. package/dist/admin/__next._full.txt +3 -3
  5. package/dist/admin/__next._head.txt +1 -1
  6. package/dist/admin/__next._index.txt +2 -2
  7. package/dist/admin/__next._tree.txt +2 -2
  8. package/dist/admin/_next/static/chunks/1da818a0086b4acf.css +3 -0
  9. package/dist/admin/_next/static/chunks/cba5c081fc9dc612.js +2 -0
  10. package/dist/admin/_not-found/__next._full.txt +2 -2
  11. package/dist/admin/_not-found/__next._head.txt +1 -1
  12. package/dist/admin/_not-found/__next._index.txt +2 -2
  13. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  14. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  15. package/dist/admin/_not-found/__next._tree.txt +2 -2
  16. package/dist/admin/_not-found/index.html +1 -1
  17. package/dist/admin/_not-found/index.txt +2 -2
  18. package/dist/admin/databases/__next._full.txt +2 -2
  19. package/dist/admin/databases/__next._head.txt +1 -1
  20. package/dist/admin/databases/__next._index.txt +2 -2
  21. package/dist/admin/databases/__next._tree.txt +2 -2
  22. package/dist/admin/databases/__next.databases.__PAGE__.txt +1 -1
  23. package/dist/admin/databases/__next.databases.txt +1 -1
  24. package/dist/admin/databases/index.html +1 -1
  25. package/dist/admin/databases/index.txt +2 -2
  26. package/dist/admin/index.html +1 -1
  27. package/dist/admin/index.txt +3 -3
  28. package/dist/cli.js +1391 -1038
  29. package/dist/docs/index.md +1 -0
  30. package/dist/docs/integrations/temporal.md +89 -0
  31. package/dist/docs/services.md +4 -2
  32. package/package.json +1 -1
  33. package/dist/admin/_next/static/chunks/237926899f121e8a.js +0 -2
  34. package/dist/admin/_next/static/chunks/2ca8ab35893ba132.css +0 -3
  35. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → 3dGrdrrH7RVk5ixe6lgRQ}/_buildManifest.js +0 -0
  36. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → 3dGrdrrH7RVk5ixe6lgRQ}/_clientMiddlewareManifest.json +0 -0
  37. /package/dist/admin/_next/static/{w-7TGbUFVs5LhUxmBNTTr → 3dGrdrrH7RVk5ixe6lgRQ}/_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,6 +38,712 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
38
  mod
32
39
  ));
33
40
 
41
+ // ../../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/is-docker/index.js"() {
67
+ }
68
+ });
69
+
70
+ // ../../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/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/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/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/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/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/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/wsl-utils/utilities.js"() {
176
+ }
177
+ });
178
+
179
+ // ../../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/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/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/define-lazy-prop/index.js"() {
274
+ }
275
+ });
276
+
277
+ // ../../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/default-browser-id/index.js"() {
296
+ execFileAsync = promisify3(execFile3);
297
+ }
298
+ });
299
+
300
+ // ../../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/run-applescript/index.js"() {
319
+ execFileAsync2 = promisify4(execFile4);
320
+ }
321
+ });
322
+
323
+ // ../../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/bundle-name/index.js"() {
330
+ init_run_applescript();
331
+ }
332
+ });
333
+
334
+ // ../../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/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/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/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/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/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/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/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
+
34
747
  // ../../node_modules/hcl2-json-parser/dist/index.js
35
748
  var require_dist = __commonJS({
36
749
  "../../node_modules/hcl2-json-parser/dist/index.js"(exports, module) {
@@ -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 fs25 = $global.require("fs");
758
+ "object" == typeof fs25 && null !== fs25 && 0 !== Object.keys(fs25).length && ($global.fs = fs25);
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.43",
183712
183847
  platform: process.platform,
183713
183848
  node_version: process.version,
183714
183849
  project_id: getProjectId(),
@@ -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,7 +184166,7 @@ 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";
@@ -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,8 +184734,8 @@ 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";
@@ -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,7 +187096,7 @@ 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
 
@@ -186986,7 +187162,7 @@ async function saveGeneratedSecret(name, value) {
186986
187162
  }
186987
187163
  await writeFile(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
186988
187164
  }
186989
- async function prepareSecrets(secretsConfig) {
187165
+ async function prepareSecrets(secretsConfig, isDevMode = false) {
186990
187166
  const userSecrets = await loadSecrets();
186991
187167
  const generatedSecrets = await loadGeneratedSecrets();
186992
187168
  const finalSecrets = /* @__PURE__ */ new Map();
@@ -187003,10 +187179,57 @@ async function prepareSecrets(secretsConfig) {
187003
187179
  await saveGeneratedSecret(secretDef.name, generatedValue);
187004
187180
  }
187005
187181
  finalSecrets.set(secretDef.name, generatedValue);
187182
+ continue;
187183
+ }
187184
+ if (isDevMode && secretDef.dev?.required === false) {
187185
+ finalSecrets.set(secretDef.name, "");
187186
+ continue;
187006
187187
  }
187007
187188
  }
187008
187189
  return finalSecrets;
187009
187190
  }
187191
+ var HEADER_COMMENT = "# Do not commit this file - it contains secrets\n\n";
187192
+ async function saveSecret(name, value) {
187193
+ let content = "";
187194
+ if (existsSync10(SECRETS_FILE)) {
187195
+ content = await readFile(SECRETS_FILE, "utf-8");
187196
+ } else {
187197
+ content = HEADER_COMMENT;
187198
+ }
187199
+ const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
187200
+ const secretPattern = new RegExp(`^${name}\\s*=\\s*"[^"]*"\\s*$`, "m");
187201
+ if (secretPattern.test(content)) {
187202
+ content = content.replace(secretPattern, `${name} = "${escapedValue}"`);
187203
+ } else {
187204
+ content = content.trimEnd() + `
187205
+ ${name} = "${escapedValue}"
187206
+ `;
187207
+ }
187208
+ await writeFile(SECRETS_FILE, content);
187209
+ }
187210
+ function findUsedSecrets(services, isDevMode) {
187211
+ const used = /* @__PURE__ */ new Set();
187212
+ for (const service of services) {
187213
+ const mergedEnv = isDevMode ? { ...service.env, ...service.dev?.env } : service.env;
187214
+ for (const value of Object.values(mergedEnv ?? {})) {
187215
+ if (typeof value === "object" && value.type === "secret") {
187216
+ used.add(value.name);
187217
+ }
187218
+ }
187219
+ }
187220
+ return used;
187221
+ }
187222
+ function findMissingSecrets(secretsDef, preparedSecrets, usedSecrets) {
187223
+ const missing = [];
187224
+ for (const secret of secretsDef) {
187225
+ if (secret.generated) continue;
187226
+ if (usedSecrets && !usedSecrets.has(secret.name)) continue;
187227
+ if (!preparedSecrets.has(secret.name)) {
187228
+ missing.push(secret.name);
187229
+ }
187230
+ }
187231
+ return missing;
187232
+ }
187010
187233
 
187011
187234
  // src/lib/config/parser.ts
187012
187235
  var import_hcl2_json_parser3 = __toESM(require_dist(), 1);
@@ -187034,7 +187257,28 @@ async function loadConfigs() {
187034
187257
  const content = await readFile2(CONFIG_FILE, "utf-8");
187035
187258
  return await parseConfigFile(content);
187036
187259
  }
187037
- async function prepareConfigs(configsDef, environmentOverrides) {
187260
+ async function saveConfig(name, value) {
187261
+ let content = "";
187262
+ if (existsSync11(CONFIG_FILE)) {
187263
+ content = await readFile2(CONFIG_FILE, "utf-8");
187264
+ } else {
187265
+ content = `# Configuration values for this project
187266
+ # These values override defaults defined in specific.hcl
187267
+
187268
+ `;
187269
+ }
187270
+ const escapedValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
187271
+ const configPattern = new RegExp(`^${name}\\s*=\\s*"[^"]*"\\s*$`, "m");
187272
+ if (configPattern.test(content)) {
187273
+ content = content.replace(configPattern, `${name} = "${escapedValue}"`);
187274
+ } else {
187275
+ content = content.trimEnd() + `
187276
+ ${name} = "${escapedValue}"
187277
+ `;
187278
+ }
187279
+ await writeFile2(CONFIG_FILE, content);
187280
+ }
187281
+ async function prepareConfigs(configsDef, environmentOverrides, isDevMode = false) {
187038
187282
  const userConfigs = await loadConfigs();
187039
187283
  const finalConfigs = /* @__PURE__ */ new Map();
187040
187284
  for (const configDef of configsDef) {
@@ -187048,13 +187292,36 @@ async function prepareConfigs(configsDef, environmentOverrides) {
187048
187292
  finalConfigs.set(configDef.name, envOverride);
187049
187293
  continue;
187050
187294
  }
187051
- if (configDef.default !== void 0) {
187052
- finalConfigs.set(configDef.name, configDef.default);
187295
+ const effectiveDefault = isDevMode && configDef.dev?.default !== void 0 ? configDef.dev.default : configDef.default;
187296
+ if (effectiveDefault !== void 0) {
187297
+ finalConfigs.set(configDef.name, effectiveDefault);
187053
187298
  continue;
187054
187299
  }
187055
187300
  }
187056
187301
  return finalConfigs;
187057
187302
  }
187303
+ function findUsedConfigs(services, isDevMode) {
187304
+ const used = /* @__PURE__ */ new Set();
187305
+ for (const service of services) {
187306
+ const mergedEnv = isDevMode ? { ...service.env, ...service.dev?.env } : service.env;
187307
+ for (const value of Object.values(mergedEnv ?? {})) {
187308
+ if (typeof value === "object" && value.type === "config") {
187309
+ used.add(value.name);
187310
+ }
187311
+ }
187312
+ }
187313
+ return used;
187314
+ }
187315
+ function findMissingConfigs(configsDef, preparedConfigs, usedConfigs) {
187316
+ const missing = [];
187317
+ for (const config of configsDef) {
187318
+ if (usedConfigs && !usedConfigs.has(config.name)) continue;
187319
+ if (!preparedConfigs.has(config.name)) {
187320
+ missing.push(config.name);
187321
+ }
187322
+ }
187323
+ return missing;
187324
+ }
187058
187325
 
187059
187326
  // src/lib/dev/env-resolver.ts
187060
187327
  var MissingSecretError = class extends Error {
@@ -187124,7 +187391,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187124
187391
  }
187125
187392
  switch (serviceRef.attribute) {
187126
187393
  case "url":
187127
- return `http://localhost:${endpoint.port}`;
187394
+ return `localhost:${endpoint.port}`;
187128
187395
  case "host":
187129
187396
  return "localhost";
187130
187397
  case "port":
@@ -187983,7 +188250,7 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
187983
188250
  if (isOpen) {
187984
188251
  return;
187985
188252
  }
187986
- await sleep2(100);
188253
+ await sleep3(100);
187987
188254
  }
187988
188255
  throw new Error(`Electric port ${port} did not become available within timeout`);
187989
188256
  }
@@ -188024,7 +188291,7 @@ async function stopProcess2(proc) {
188024
188291
  }, 2e3);
188025
188292
  });
188026
188293
  }
188027
- function sleep2(ms) {
188294
+ function sleep3(ms) {
188028
188295
  return new Promise((resolve7) => setTimeout(resolve7, ms));
188029
188296
  }
188030
188297
 
@@ -188102,7 +188369,7 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
188102
188369
  if (isOpen) {
188103
188370
  return;
188104
188371
  }
188105
- await sleep3(100);
188372
+ await sleep4(100);
188106
188373
  }
188107
188374
  throw new Error(
188108
188375
  `Drizzle Gateway port ${port} did not become available within timeout`
@@ -188145,7 +188412,7 @@ async function stopProcess3(proc) {
188145
188412
  }, 2e3);
188146
188413
  });
188147
188414
  }
188148
- function sleep3(ms) {
188415
+ function sleep4(ms) {
188149
188416
  return new Promise((resolve7) => setTimeout(resolve7, ms));
188150
188417
  }
188151
188418
 
@@ -188340,7 +188607,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
188340
188607
  // src/lib/dev/proxy-registry.ts
188341
188608
  import * as fs18 from "fs";
188342
188609
  import * as path15 from "path";
188343
- import * as os7 from "os";
188610
+ import * as os8 from "os";
188344
188611
  var ProxyRegistryManager = class {
188345
188612
  proxyDir;
188346
188613
  ownerPath;
@@ -188349,7 +188616,7 @@ var ProxyRegistryManager = class {
188349
188616
  isOwner = false;
188350
188617
  registryWatcher = null;
188351
188618
  constructor() {
188352
- this.proxyDir = path15.join(os7.homedir(), ".specific", "proxy");
188619
+ this.proxyDir = path15.join(os8.homedir(), ".specific", "proxy");
188353
188620
  this.ownerPath = path15.join(this.proxyDir, "owner.json");
188354
188621
  this.registryPath = path15.join(this.proxyDir, "registry.json");
188355
188622
  this.lockPath = path15.join(this.proxyDir, "registry.lock");
@@ -188659,11 +188926,58 @@ var ProxyRegistryManager = class {
188659
188926
  }
188660
188927
  };
188661
188928
 
188929
+ // src/lib/ui/SecretInput.tsx
188930
+ import React4, { useState as useState3 } from "react";
188931
+ import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
188932
+ function SecretInput({ secretName, onSubmit, onCancel }) {
188933
+ const [value, setValue] = useState3("");
188934
+ useInput2((input, key) => {
188935
+ if (key.return) {
188936
+ if (value.trim() !== "") {
188937
+ onSubmit(value);
188938
+ }
188939
+ } else if (key.escape) {
188940
+ onCancel();
188941
+ } else if (key.backspace || key.delete) {
188942
+ setValue((prev) => prev.slice(0, -1));
188943
+ } else if (!key.ctrl && !key.meta && input) {
188944
+ setValue((prev) => prev + input);
188945
+ }
188946
+ });
188947
+ 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)"));
188948
+ }
188949
+
188950
+ // src/lib/ui/ConfigInput.tsx
188951
+ import React5, { useState as useState4 } from "react";
188952
+ import { Box as Box5, Text as Text5, useInput as useInput3 } from "ink";
188953
+ function ConfigInput({ configName, onSubmit, onCancel }) {
188954
+ const [value, setValue] = useState4("");
188955
+ useInput3((input, key) => {
188956
+ if (key.return) {
188957
+ if (value.trim() !== "") {
188958
+ onSubmit(value);
188959
+ }
188960
+ } else if (key.escape) {
188961
+ onCancel();
188962
+ } else if (key.backspace || key.delete) {
188963
+ setValue((prev) => prev.slice(0, -1));
188964
+ } else if (!key.ctrl && !key.meta && input) {
188965
+ setValue((prev) => prev + input);
188966
+ }
188967
+ });
188968
+ 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)"));
188969
+ }
188970
+
188971
+ // src/lib/ui/interactive.ts
188972
+ function isInteractive() {
188973
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
188974
+ }
188975
+
188662
188976
  // src/commands/dev.tsx
188663
188977
  var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
188664
188978
  function DevUI({ instanceKey }) {
188665
- const { exit } = useApp3();
188666
- const [state, setState] = useState4(() => {
188979
+ const { exit } = useApp2();
188980
+ const [state, setState] = useState5(() => {
188667
188981
  const caExists = caFilesExist();
188668
188982
  return {
188669
188983
  status: caExists ? "loading" : "installing-ca",
@@ -188676,7 +188990,7 @@ function DevUI({ instanceKey }) {
188676
188990
  isProxyOwner: false
188677
188991
  };
188678
188992
  });
188679
- useEffect4(() => {
188993
+ useEffect3(() => {
188680
188994
  if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
188681
188995
  installCA();
188682
188996
  }
@@ -188709,8 +189023,8 @@ function DevUI({ instanceKey }) {
188709
189023
  const adminServerRef = useRef(null);
188710
189024
  const servicesRef = useRef([]);
188711
189025
  const resourcesRef = useRef(/* @__PURE__ */ new Map());
188712
- const [reloadTrigger, setReloadTrigger] = useState4(0);
188713
- const [readyToStart, setReadyToStart] = useState4(() => caFilesExist());
189026
+ const [reloadTrigger, setReloadTrigger] = useState5(0);
189027
+ const [readyToStart, setReadyToStart] = useState5(() => caFilesExist());
188714
189028
  const shutdown2 = async () => {
188715
189029
  if (shuttingDown.current) return;
188716
189030
  shuttingDown.current = true;
@@ -188802,7 +189116,7 @@ function DevUI({ instanceKey }) {
188802
189116
  }));
188803
189117
  setReloadTrigger((t) => t + 1);
188804
189118
  };
188805
- useEffect4(() => {
189119
+ useEffect3(() => {
188806
189120
  const handleSignal = () => {
188807
189121
  if (shuttingDown.current) {
188808
189122
  writeLog("system", "Force shutting down");
@@ -188831,13 +189145,13 @@ function DevUI({ instanceKey }) {
188831
189145
  process.off("SIGTERM", handleSignal);
188832
189146
  };
188833
189147
  }, []);
188834
- useEffect4(() => {
189148
+ useEffect3(() => {
188835
189149
  if (state.status === "running" && !startTimeRef.current) {
188836
189150
  startTimeRef.current = Date.now();
188837
189151
  trackEvent("dev_started");
188838
189152
  }
188839
189153
  }, [state.status]);
188840
- useEffect4(() => {
189154
+ useEffect3(() => {
188841
189155
  if (state.status !== "running") return;
188842
189156
  const configPath = path16.join(process.cwd(), "specific.hcl");
188843
189157
  const watcher = watchConfigFile(configPath, 1e3, () => {
@@ -188872,7 +189186,66 @@ function DevUI({ instanceKey }) {
188872
189186
  ]
188873
189187
  }));
188874
189188
  };
188875
- useEffect4(() => {
189189
+ const handleSecretSubmit = async (value) => {
189190
+ const currentSecret = state.missingSecrets?.[state.currentSecretIndex ?? 0];
189191
+ if (!currentSecret) return;
189192
+ await saveSecret(currentSecret, value);
189193
+ const nextIndex = (state.currentSecretIndex ?? 0) + 1;
189194
+ if (nextIndex < (state.missingSecrets?.length ?? 0)) {
189195
+ setState((s) => ({
189196
+ ...s,
189197
+ currentSecretIndex: nextIndex
189198
+ }));
189199
+ } else if (state.missingConfigs && state.missingConfigs.length > 0) {
189200
+ setState((s) => ({
189201
+ ...s,
189202
+ status: "awaiting-configs",
189203
+ missingSecrets: void 0,
189204
+ currentSecretIndex: void 0,
189205
+ currentConfigIndex: 0
189206
+ }));
189207
+ } else {
189208
+ setState((s) => ({
189209
+ ...s,
189210
+ status: "loading",
189211
+ missingSecrets: void 0,
189212
+ currentSecretIndex: void 0
189213
+ }));
189214
+ setReloadTrigger((t) => t + 1);
189215
+ }
189216
+ };
189217
+ const handleConfigSubmit = async (value) => {
189218
+ const currentConfig = state.missingConfigs?.[state.currentConfigIndex ?? 0];
189219
+ if (!currentConfig) return;
189220
+ await saveConfig(currentConfig, value);
189221
+ const nextIndex = (state.currentConfigIndex ?? 0) + 1;
189222
+ if (nextIndex < (state.missingConfigs?.length ?? 0)) {
189223
+ setState((s) => ({
189224
+ ...s,
189225
+ currentConfigIndex: nextIndex
189226
+ }));
189227
+ } else {
189228
+ setState((s) => ({
189229
+ ...s,
189230
+ status: "loading",
189231
+ missingConfigs: void 0,
189232
+ currentConfigIndex: void 0
189233
+ }));
189234
+ setReloadTrigger((t) => t + 1);
189235
+ }
189236
+ };
189237
+ const handleInputCancel = () => {
189238
+ setState((s) => ({
189239
+ ...s,
189240
+ status: "error",
189241
+ error: "Startup cancelled - required values not provided",
189242
+ missingSecrets: void 0,
189243
+ currentSecretIndex: void 0,
189244
+ missingConfigs: void 0,
189245
+ currentConfigIndex: void 0
189246
+ }));
189247
+ };
189248
+ useEffect3(() => {
188876
189249
  if (!readyToStart) {
188877
189250
  return;
188878
189251
  }
@@ -189047,8 +189420,50 @@ function DevUI({ instanceKey }) {
189047
189420
  }
189048
189421
  }
189049
189422
  if (cancelled) return;
189050
- const secrets = await prepareSecrets(config2.secrets);
189051
- const configs = await prepareConfigs(config2.configs);
189423
+ const secrets = await prepareSecrets(config2.secrets, true);
189424
+ const configs = await prepareConfigs(config2.configs, void 0, true);
189425
+ const usedSecrets = findUsedSecrets(config2.services, true);
189426
+ const usedConfigs = findUsedConfigs(config2.services, true);
189427
+ const missingSecrets = findMissingSecrets(config2.secrets, secrets, usedSecrets);
189428
+ const missingConfigs = findMissingConfigs(config2.configs, configs, usedConfigs);
189429
+ if (missingSecrets.length > 0 || missingConfigs.length > 0) {
189430
+ if (isInteractive()) {
189431
+ if (missingSecrets.length > 0) {
189432
+ setState((s) => ({
189433
+ ...s,
189434
+ status: "awaiting-secrets",
189435
+ missingSecrets,
189436
+ currentSecretIndex: 0,
189437
+ missingConfigs: missingConfigs.length > 0 ? missingConfigs : void 0
189438
+ }));
189439
+ return;
189440
+ } else {
189441
+ setState((s) => ({
189442
+ ...s,
189443
+ status: "awaiting-configs",
189444
+ missingConfigs,
189445
+ currentConfigIndex: 0
189446
+ }));
189447
+ return;
189448
+ }
189449
+ } else {
189450
+ const errorParts = [];
189451
+ if (missingSecrets.length > 0) {
189452
+ errorParts.push(`Missing secrets: ${missingSecrets.join(", ")}
189453
+ Add them to specific.secrets`);
189454
+ }
189455
+ if (missingConfigs.length > 0) {
189456
+ errorParts.push(`Missing configs: ${missingConfigs.join(", ")}
189457
+ Add them to specific.config`);
189458
+ }
189459
+ setState((s) => ({
189460
+ ...s,
189461
+ status: "error",
189462
+ error: errorParts.join("\n\n")
189463
+ }));
189464
+ return;
189465
+ }
189466
+ }
189052
189467
  const validationErrors = validateEndpointReferences(config2);
189053
189468
  if (validationErrors.length > 0) {
189054
189469
  for (const error of validationErrors) {
@@ -189334,33 +189749,57 @@ function DevUI({ instanceKey }) {
189334
189749
  }, [reloadTrigger, readyToStart, instanceKey]);
189335
189750
  if (state.status === "installing-ca") {
189336
189751
  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, " "));
189752
+ 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
189753
  }
189339
189754
  if (state.caInstallPhase === "error") {
189340
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Failed to install CA: ", state.caError));
189755
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Failed to install CA: ", state.caError));
189341
189756
  }
189342
189757
  }
189343
189758
  if (state.status === "loading") {
189344
- return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })));
189759
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
189760
+ }
189761
+ if (state.status === "awaiting-secrets" && state.missingSecrets && state.currentSecretIndex !== void 0) {
189762
+ const currentSecret = state.missingSecrets[state.currentSecretIndex];
189763
+ 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(
189764
+ SecretInput,
189765
+ {
189766
+ key: currentSecret,
189767
+ secretName: currentSecret,
189768
+ onSubmit: handleSecretSubmit,
189769
+ onCancel: handleInputCancel
189770
+ }
189771
+ ));
189772
+ }
189773
+ if (state.status === "awaiting-configs" && state.missingConfigs && state.currentConfigIndex !== void 0) {
189774
+ const currentConfig = state.missingConfigs[state.currentConfigIndex];
189775
+ 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(
189776
+ ConfigInput,
189777
+ {
189778
+ key: currentConfig,
189779
+ configName: currentConfig,
189780
+ onSubmit: handleConfigSubmit,
189781
+ onCancel: handleInputCancel
189782
+ }
189783
+ ));
189345
189784
  }
189346
189785
  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!")));
189786
+ 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
189787
  }
189349
189788
  if (state.status === "error") {
189350
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Error: ", state.error));
189789
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error));
189351
189790
  }
189352
189791
  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..."));
189792
+ 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
189793
  }
189355
189794
  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)...")));
189795
+ 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
189796
  }
189358
189797
  const { config, resources, services, proxy, output } = state;
189359
189798
  if (!config) {
189360
- return /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "blue" }, /* @__PURE__ */ React4.createElement(Spinner4, { type: "dots" })));
189799
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
189361
189800
  }
189362
189801
  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) => {
189802
+ 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
189803
  const instance = resources.get(pg.name);
189365
189804
  const resStatus = state.resourceStatus.get(pg.name);
189366
189805
  const isReady = !!instance;
@@ -189375,7 +189814,7 @@ function DevUI({ instanceKey }) {
189375
189814
  } else if (resStatus?.status === "starting") {
189376
189815
  statusText = " starting...";
189377
189816
  }
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));
189817
+ 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
189818
  }), config.redis.map((redis) => {
189380
189819
  const instance = resources.get(redis.name);
189381
189820
  const resStatus = state.resourceStatus.get(redis.name);
@@ -189391,35 +189830,35 @@ function DevUI({ instanceKey }) {
189391
189830
  } else if (resStatus?.status === "starting") {
189392
189831
  statusText = " starting...";
189393
189832
  }
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));
189833
+ 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
189834
  }), config.storage.map((storage) => {
189396
189835
  const instance = resources.get(storage.name);
189397
189836
  const resStatus = state.resourceStatus.get(storage.name);
189398
189837
  const isReady = !!instance;
189399
189838
  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) => {
189839
+ 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));
189840
+ }), /* @__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
189841
  const running = services.find((s) => s.name === svc.name);
189403
189842
  const isReady = !!running;
189404
189843
  const defaultPort = running?.ports.get("default");
189405
189844
  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...")));
189845
+ 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));
189846
+ }), /* @__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
189847
  }
189409
189848
  const staticItems = [
189410
189849
  {
189411
189850
  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)"))
189851
+ 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
189852
  },
189414
- { key: "space1", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") },
189853
+ { key: "space1", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") },
189415
189854
  // Show admin UI URL
189416
189855
  {
189417
189856
  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"))
189857
+ 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
189858
  },
189420
- { key: "admin-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") },
189859
+ { key: "admin-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") },
189421
189860
  ...services.length > 0 ? [
189422
- { key: "svc-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Services:") },
189861
+ { key: "svc-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Services:") },
189423
189862
  ...services.flatMap((svc) => {
189424
189863
  const serviceConfig = config.services.find((s) => s.name === svc.name);
189425
189864
  const endpoints = serviceConfig?.endpoints || [];
@@ -189427,7 +189866,7 @@ function DevUI({ instanceKey }) {
189427
189866
  const defaultPort = svc.ports.get("default");
189428
189867
  return [{
189429
189868
  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, ")"))
189869
+ 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
189870
  }];
189432
189871
  }
189433
189872
  return endpoints.map((endpoint) => {
@@ -189436,61 +189875,61 @@ function DevUI({ instanceKey }) {
189436
189875
  const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
189437
189876
  return {
189438
189877
  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)
189878
+ 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
189879
  };
189441
189880
  });
189442
189881
  }),
189443
- { key: "space2", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189882
+ { key: "space2", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189444
189883
  ] : [],
189445
189884
  ...config.postgres.length > 0 ? [
189446
- { key: "pg-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Postgres:") },
189885
+ { key: "pg-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Postgres:") },
189447
189886
  ...config.postgres.map((pg) => {
189448
189887
  const instance = resources.get(pg.name);
189449
189888
  return {
189450
189889
  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))
189890
+ 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
189891
  };
189453
189892
  }),
189454
- { key: "pg-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189893
+ { key: "pg-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189455
189894
  ] : [],
189456
189895
  ...config.redis.length > 0 ? [
189457
- { key: "redis-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Redis:") },
189896
+ { key: "redis-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Redis:") },
189458
189897
  ...config.redis.map((redis) => {
189459
189898
  const instance = resources.get(redis.name);
189460
189899
  return {
189461
189900
  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))
189901
+ 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
189902
  };
189464
189903
  }),
189465
- { key: "redis-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189904
+ { key: "redis-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189466
189905
  ] : [],
189467
189906
  ...config.storage.length > 0 ? [
189468
- { key: "storage-header", content: /* @__PURE__ */ React4.createElement(Text4, { bold: true }, "Storage:") },
189907
+ { key: "storage-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Storage:") },
189469
189908
  ...config.storage.map((storage) => {
189470
189909
  const instance = resources.get(storage.name);
189471
189910
  return {
189472
189911
  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))
189912
+ 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
189913
  };
189475
189914
  }),
189476
- { key: "storage-space", content: /* @__PURE__ */ React4.createElement(Text4, null, " ") }
189915
+ { key: "storage-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
189477
189916
  ] : [],
189478
- { key: "separator", content: /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "\u2500".repeat(50)) },
189917
+ { key: "separator", content: /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2500".repeat(50)) },
189479
189918
  ...state.parseError ? [
189480
189919
  {
189481
189920
  key: "parse-error",
189482
- content: /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "Warning: ", state.parseError)
189921
+ content: /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Warning: ", state.parseError)
189483
189922
  }
189484
189923
  ] : [],
189485
189924
  ...output.map((line, i) => ({
189486
189925
  key: `log-${i}`,
189487
- content: /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: line.color }, "[", line.service, "]"), " ", line.text)
189926
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: line.color }, "[", line.service, "]"), " ", line.text)
189488
189927
  }))
189489
189928
  ];
189490
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React4.createElement(Box4, { key: item.key }, item.content)));
189929
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React6.createElement(Box6, { key: item.key }, item.content)));
189491
189930
  }
189492
189931
  function devCommand(instanceKey) {
189493
- render4(/* @__PURE__ */ React4.createElement(DevUI, { instanceKey }));
189932
+ render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey }));
189494
189933
  }
189495
189934
 
189496
189935
  // src/lib/dev/git-worktree.ts
@@ -189533,8 +189972,9 @@ function getDefaultKey() {
189533
189972
  }
189534
189973
 
189535
189974
  // 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";
189975
+ init_open();
189976
+ import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
189977
+ import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
189538
189978
  import Spinner5 from "ink-spinner";
189539
189979
  import * as fs21 from "fs";
189540
189980
  import * as path19 from "path";
@@ -189857,57 +190297,23 @@ function PhaseIndicator({
189857
190297
  const currentIndex = phases.indexOf(currentPhase);
189858
190298
  if (currentPhase === "error") {
189859
190299
  if (phaseIndex < currentIndex) {
189860
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, " ", "\u2713"), " ", label);
190300
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, " ", "\u2713"), " ", label);
189861
190301
  }
189862
190302
  if (phaseIndex === currentIndex) {
189863
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, " ", "\u2717"), " ", label);
190303
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, " ", "\u2717"), " ", label);
189864
190304
  }
189865
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, label));
190305
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, label));
189866
190306
  }
189867
190307
  if (phaseIndex < currentIndex) {
189868
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, " ", "\u2713"), " ", label);
190308
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, " ", "\u2713"), " ", label);
189869
190309
  }
189870
190310
  if (phaseIndex === currentIndex) {
189871
190311
  if (showSpinner) {
189872
- return /* @__PURE__ */ React5.createElement(Text5, null, " ", /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, /* @__PURE__ */ React5.createElement(Spinner5, { type: "dots" })), " ", label);
190312
+ return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), " ", label);
189873
190313
  }
189874
- return /* @__PURE__ */ React5.createElement(Text5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, " ", "\u25CF"), " ", label);
190314
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, " ", "\u25CF"), " ", label);
189875
190315
  }
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)"));
190316
+ return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " ", "\u25CB"), " ", /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, label));
189911
190317
  }
189912
190318
  function ProjectSelector({
189913
190319
  projects,
@@ -189916,7 +190322,7 @@ function ProjectSelector({
189916
190322
  onUp,
189917
190323
  onDown
189918
190324
  }) {
189919
- useInput3((input, key) => {
190325
+ useInput5((input, key) => {
189920
190326
  if (key.return) {
189921
190327
  if (selectedIndex === 0) {
189922
190328
  onSelect("new");
@@ -189933,11 +190339,11 @@ function ProjectSelector({
189933
190339
  { id: "new", name: "Create new project", isNew: true },
189934
190340
  ...projects.map((p) => ({ ...p, isNew: false }))
189935
190341
  ];
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"));
190342
+ 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
190343
  }
189938
190344
  function NameInput({ onSubmit, onCancel }) {
189939
- const [value, setValue] = useState5("");
189940
- useInput3((input, key) => {
190345
+ const [value, setValue] = useState6("");
190346
+ useInput5((input, key) => {
189941
190347
  if (key.return) {
189942
190348
  if (value.trim() !== "") {
189943
190349
  onSubmit(value.trim());
@@ -189950,13 +190356,13 @@ function NameInput({ onSubmit, onCancel }) {
189950
190356
  setValue((prev) => prev + input);
189951
190357
  }
189952
190358
  });
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"));
190359
+ 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
190360
  }
189955
190361
  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(() => {
190362
+ const { exit } = useApp3();
190363
+ const [state, setState] = useState6({ phase: "checking-auth" });
190364
+ const clientRef = React7.useRef(null);
190365
+ useEffect4(() => {
189960
190366
  if (state.phase !== "checking-auth") return;
189961
190367
  const projectDir = process.cwd();
189962
190368
  if (hasProjectId(projectDir)) {
@@ -189977,7 +190383,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
189977
190383
  }
189978
190384
  setState({ phase: "logging-in" });
189979
190385
  }, [state.phase]);
189980
- useEffect5(() => {
190386
+ useEffect4(() => {
189981
190387
  if (state.phase !== "logging-in") return;
189982
190388
  let cancelled = false;
189983
190389
  async function startLogin() {
@@ -190042,7 +190448,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190042
190448
  cancelled = true;
190043
190449
  };
190044
190450
  }, [state.phase]);
190045
- useEffect5(() => {
190451
+ useEffect4(() => {
190046
190452
  if (state.phase !== "loading-projects") return;
190047
190453
  let cancelled = false;
190048
190454
  async function loadProjects() {
@@ -190089,7 +190495,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190089
190495
  const handleNameCancel = useCallback(() => {
190090
190496
  setState((s) => ({ ...s, phase: "selecting-project" }));
190091
190497
  }, []);
190092
- useEffect5(() => {
190498
+ useEffect4(() => {
190093
190499
  if (state.phase !== "creating-project" || !state.newProjectName) return;
190094
190500
  let cancelled = false;
190095
190501
  async function createProject() {
@@ -190174,7 +190580,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190174
190580
  error: "Deployment cancelled - configs not provided"
190175
190581
  }));
190176
190582
  }, []);
190177
- useEffect5(() => {
190583
+ useEffect4(() => {
190178
190584
  const {
190179
190585
  phase: phase2,
190180
190586
  deployment: deployment2,
@@ -190216,7 +190622,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190216
190622
  }
190217
190623
  })();
190218
190624
  }, [state]);
190219
- useEffect5(() => {
190625
+ useEffect4(() => {
190220
190626
  const {
190221
190627
  phase: phase2,
190222
190628
  deployment: deployment2,
@@ -190258,7 +190664,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190258
190664
  }
190259
190665
  })();
190260
190666
  }, [state]);
190261
- useEffect5(() => {
190667
+ useEffect4(() => {
190262
190668
  if (state.phase !== "testing-builds" || !state.projectId) return;
190263
190669
  let cancelled = false;
190264
190670
  let pollInterval;
@@ -190448,7 +190854,7 @@ ${errorMsg}`
190448
190854
  if (pollInterval) clearInterval(pollInterval);
190449
190855
  };
190450
190856
  }, [state.projectId, environment, config.builds, skipBuildTest]);
190451
- useEffect5(() => {
190857
+ useEffect4(() => {
190452
190858
  let pollInterval;
190453
190859
  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
190860
  const client2 = clientRef.current;
@@ -190471,6 +190877,22 @@ ${errorMsg}`
190471
190877
  if (pollInterval) clearInterval(pollInterval);
190472
190878
  return;
190473
190879
  }
190880
+ if (status.state === "awaiting_configs") {
190881
+ if (pollInterval) clearInterval(pollInterval);
190882
+ pollInterval = void 0;
190883
+ const missingConfigs2 = status.missingConfigs || [];
190884
+ writeLog("deploy", `Awaiting configs: ${missingConfigs2.join(", ")}`);
190885
+ setState((s) => ({
190886
+ ...s,
190887
+ phase: "awaiting-configs",
190888
+ deployment: status,
190889
+ missingConfigs: missingConfigs2,
190890
+ configValues: {},
190891
+ currentConfigIndex: 0,
190892
+ currentConfigInput: ""
190893
+ }));
190894
+ return;
190895
+ }
190474
190896
  if (status.state === "deploying") {
190475
190897
  setState((s) => ({ ...s, phase: "deploying", deployment: status }));
190476
190898
  } else if (status.state === "building") {
@@ -190486,12 +190908,12 @@ ${errorMsg}`
190486
190908
  };
190487
190909
  }
190488
190910
  }, [state.phase, state.missingSecrets, state.secretValues, state.missingConfigs, state.configValues]);
190489
- useEffect5(() => {
190911
+ useEffect4(() => {
190490
190912
  if (state.phase === "testing-builds") {
190491
190913
  trackEvent("deploy_started", { environment });
190492
190914
  }
190493
190915
  }, [state.phase, environment]);
190494
- useEffect5(() => {
190916
+ useEffect4(() => {
190495
190917
  if (state.phase === "success") {
190496
190918
  trackEvent("deploy_succeeded", { environment });
190497
190919
  closeDebugLog();
@@ -190520,16 +190942,16 @@ ${errorMsg}`
190520
190942
  verificationUri
190521
190943
  } = state;
190522
190944
  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..."));
190945
+ 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
190946
  }
190525
190947
  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...")));
190948
+ 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
190949
  }
190528
190950
  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..."));
190951
+ 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
190952
  }
190531
190953
  if (phase === "selecting-project" && projects && selectedIndex !== void 0) {
190532
- return /* @__PURE__ */ React5.createElement(
190954
+ return /* @__PURE__ */ React7.createElement(
190533
190955
  ProjectSelector,
190534
190956
  {
190535
190957
  projects,
@@ -190550,57 +190972,57 @@ ${errorMsg}`
190550
190972
  );
190551
190973
  }
190552
190974
  if (phase === "entering-name") {
190553
- return /* @__PURE__ */ React5.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
190975
+ return /* @__PURE__ */ React7.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
190554
190976
  }
190555
190977
  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..."));
190978
+ 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
190979
  }
190558
190980
  const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
190559
190981
  const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
190560
190982
  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(
190983
+ 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
190984
  PhaseIndicator,
190563
190985
  {
190564
190986
  phase: "testing-builds",
190565
190987
  currentPhase: displayPhase,
190566
190988
  label: "Testing builds locally"
190567
190989
  }
190568
- ), /* @__PURE__ */ React5.createElement(
190990
+ ), /* @__PURE__ */ React7.createElement(
190569
190991
  PhaseIndicator,
190570
190992
  {
190571
190993
  phase: "creating-tarball",
190572
190994
  currentPhase: displayPhase,
190573
190995
  label: `Creating archive${tarballSize ? ` (${formatBytes(tarballSize)})` : ""}`
190574
190996
  }
190575
- ), /* @__PURE__ */ React5.createElement(
190997
+ ), /* @__PURE__ */ React7.createElement(
190576
190998
  PhaseIndicator,
190577
190999
  {
190578
191000
  phase: "creating-deployment",
190579
191001
  currentPhase: displayPhase,
190580
191002
  label: "Creating deployment"
190581
191003
  }
190582
- ), /* @__PURE__ */ React5.createElement(
191004
+ ), /* @__PURE__ */ React7.createElement(
190583
191005
  PhaseIndicator,
190584
191006
  {
190585
191007
  phase: "uploading",
190586
191008
  currentPhase: displayPhase,
190587
191009
  label: "Uploading"
190588
191010
  }
190589
- ), /* @__PURE__ */ React5.createElement(
191011
+ ), /* @__PURE__ */ React7.createElement(
190590
191012
  PhaseIndicator,
190591
191013
  {
190592
191014
  phase: "building",
190593
191015
  currentPhase: displayPhase,
190594
191016
  label: "Building"
190595
191017
  }
190596
- ), /* @__PURE__ */ React5.createElement(
191018
+ ), /* @__PURE__ */ React7.createElement(
190597
191019
  PhaseIndicator,
190598
191020
  {
190599
191021
  phase: "deploying",
190600
191022
  currentPhase: displayPhase,
190601
191023
  label: "Deploying"
190602
191024
  }
190603
- )), phase === "awaiting-secrets" && currentSecret && /* @__PURE__ */ React5.createElement(
191025
+ )), phase === "awaiting-secrets" && currentSecret && /* @__PURE__ */ React7.createElement(
190604
191026
  SecretInput,
190605
191027
  {
190606
191028
  key: currentSecret,
@@ -190608,7 +191030,7 @@ ${errorMsg}`
190608
191030
  onSubmit: handleSecretSubmit,
190609
191031
  onCancel: handleSecretCancel
190610
191032
  }
190611
- ), phase === "awaiting-configs" && currentConfig && /* @__PURE__ */ React5.createElement(
191033
+ ), phase === "awaiting-configs" && currentConfig && /* @__PURE__ */ React7.createElement(
190612
191034
  ConfigInput,
190613
191035
  {
190614
191036
  key: currentConfig,
@@ -190616,7 +191038,7 @@ ${errorMsg}`
190616
191038
  onSubmit: handleConfigSubmit,
190617
191039
  onCancel: handleConfigCancel
190618
191040
  }
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))))));
191041
+ ), 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
191042
  }
190621
191043
  async function deployCommand(environment, options2) {
190622
191044
  const configPath = path19.join(process.cwd(), "specific.hcl");
@@ -190637,7 +191059,7 @@ async function deployCommand(environment, options2) {
190637
191059
  const env2 = environment || "prod";
190638
191060
  const skipBuildTest = options2?.skipBuildTest ?? false;
190639
191061
  render5(
190640
- /* @__PURE__ */ React5.createElement(
191062
+ /* @__PURE__ */ React7.createElement(
190641
191063
  DeployUI,
190642
191064
  {
190643
191065
  environment: env2,
@@ -190877,14 +191299,14 @@ async function psqlCommand(databaseName, instanceKey = "default") {
190877
191299
  }
190878
191300
 
190879
191301
  // 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";
191302
+ import React8, { useState as useState7, useEffect as useEffect5 } from "react";
191303
+ import { render as render6, Text as Text8, Box as Box8 } from "ink";
190882
191304
  import Spinner6 from "ink-spinner";
190883
191305
  import * as fs23 from "fs";
190884
191306
  import * as path21 from "path";
190885
191307
  function CleanUI({ instanceKey }) {
190886
- const [state, setState] = useState6({ status: "checking" });
190887
- useEffect6(() => {
191308
+ const [state, setState] = useState7({ status: "checking" });
191309
+ useEffect5(() => {
190888
191310
  async function clean() {
190889
191311
  const projectRoot = process.cwd();
190890
191312
  const specificDir = path21.join(projectRoot, ".specific");
@@ -190960,85 +191382,52 @@ function CleanUI({ instanceKey }) {
190960
191382
  clean();
190961
191383
  }, [instanceKey]);
190962
191384
  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..."));
191385
+ 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
191386
  }
190965
191387
  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..."));
191388
+ 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
191389
  }
190968
191390
  if (state.status === "error") {
190969
- return /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error);
191391
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", state.error);
190970
191392
  }
190971
191393
  if (state.status === "nothing") {
190972
191394
  const target2 = instanceKey ? `key "${instanceKey}"` : ".specific directory";
190973
- return /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
191395
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
190974
191396
  }
190975
191397
  const target = instanceKey ? `key "${instanceKey}"` : ".specific directory";
190976
- return /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "Cleaned ", target);
191398
+ return /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, "Cleaned ", target);
190977
191399
  }
190978
191400
  function cleanCommand(instanceKey) {
190979
191401
  if (instanceKey) {
190980
- render6(/* @__PURE__ */ React6.createElement(CleanUI, { instanceKey }));
191402
+ render6(/* @__PURE__ */ React8.createElement(CleanUI, { instanceKey }));
190981
191403
  } else {
190982
- render6(/* @__PURE__ */ React6.createElement(CleanUI, null));
191404
+ render6(/* @__PURE__ */ React8.createElement(CleanUI, null));
190983
191405
  }
190984
191406
  }
190985
191407
 
190986
191408
  // 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";
191409
+ import React9, { useState as useState8, useEffect as useEffect6 } from "react";
191410
+ import { render as render7, Text as Text9, Box as Box9, useInput as useInput6, useApp as useApp4 } from "ink";
190991
191411
  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;
191412
+ const { exit } = useApp4();
191413
+ const [value, setValue] = useState8("");
191414
+ const [done, setDone] = useState8(false);
191415
+ const [saving, setSaving] = useState8(false);
191416
+ const [error, setError] = useState8(null);
191417
+ useInput6((input, key) => {
191418
+ if (done || saving) return;
190998
191419
  if (key.return) {
190999
191420
  if (value.trim() === "") {
191000
191421
  setError("Secret value cannot be empty");
191001
191422
  return;
191002
191423
  }
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);
191424
+ setSaving(true);
191425
+ saveSecret(secretName, value).then(() => {
191038
191426
  setDone(true);
191039
- } catch (err) {
191427
+ }).catch((err) => {
191040
191428
  setError(err instanceof Error ? err.message : String(err));
191041
- }
191429
+ setSaving(false);
191430
+ });
191042
191431
  } else if (key.backspace || key.delete) {
191043
191432
  setValue((prev) => prev.slice(0, -1));
191044
191433
  } else if (key.escape) {
@@ -191047,19 +191436,19 @@ function SetSecretUI({ secretName }) {
191047
191436
  setValue((prev) => prev + input);
191048
191437
  }
191049
191438
  });
191050
- useEffect7(() => {
191439
+ useEffect6(() => {
191051
191440
  if (done) {
191052
191441
  const timer = setTimeout(() => exit(), 50);
191053
191442
  return () => clearTimeout(timer);
191054
191443
  }
191055
191444
  }, [done, exit]);
191056
191445
  if (error) {
191057
- return /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error);
191446
+ return /* @__PURE__ */ React9.createElement(Text9, { color: "red" }, "Error: ", error);
191058
191447
  }
191059
191448
  if (done) {
191060
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Secret '", secretName, "' saved to ", SECRETS_FILE));
191449
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, "Secret '", secretName, "' saved to ", SECRETS_FILE));
191061
191450
  }
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)"));
191451
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text9, null, "Enter value for secret '", secretName, "':"), /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, "> "), /* @__PURE__ */ React9.createElement(Text9, null, value.length > 0 ? "*".repeat(value.length) : ""), /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, "|")), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
191063
191452
  }
191064
191453
  async function secretsSetCommand(secretName) {
191065
191454
  if (!secretName) {
@@ -191067,7 +191456,7 @@ async function secretsSetCommand(secretName) {
191067
191456
  console.error("Usage: specific secrets set <name>");
191068
191457
  process.exit(1);
191069
191458
  }
191070
- render7(/* @__PURE__ */ React7.createElement(SetSecretUI, { secretName }));
191459
+ render7(/* @__PURE__ */ React9.createElement(SetSecretUI, { secretName }));
191071
191460
  }
191072
191461
  async function secretsCommand(action, secretName) {
191073
191462
  if (!action) {
@@ -191092,16 +191481,16 @@ async function secretsCommand(action, secretName) {
191092
191481
  }
191093
191482
 
191094
191483
  // 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";
191484
+ import React10, { useState as useState9, useEffect as useEffect7 } from "react";
191485
+ import { render as render8, Text as Text10, Box as Box10, useInput as useInput7, useApp as useApp5 } from "ink";
191486
+ import * as fs24 from "fs";
191098
191487
  var HEADER_COMMENT2 = "# Configuration values for this project\n# These values override defaults defined in specific.hcl\n";
191099
191488
  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) => {
191489
+ const { exit } = useApp5();
191490
+ const [value, setValue] = useState9(initialValue ?? "");
191491
+ const [done, setDone] = useState9(false);
191492
+ const [error, setError] = useState9(null);
191493
+ useInput7((input, key) => {
191105
191494
  if (done) return;
191106
191495
  if (key.return) {
191107
191496
  if (value.trim() === "") {
@@ -191113,8 +191502,8 @@ function SetConfigUI({ configName, initialValue }) {
191113
191502
  const hclLine = `${configName} = "${escapedValue}"`;
191114
191503
  let content = "";
191115
191504
  let hasHeader = false;
191116
- if (fs25.existsSync(CONFIG_FILE)) {
191117
- content = fs25.readFileSync(CONFIG_FILE, "utf-8");
191505
+ if (fs24.existsSync(CONFIG_FILE)) {
191506
+ content = fs24.readFileSync(CONFIG_FILE, "utf-8");
191118
191507
  hasHeader = content.startsWith("#");
191119
191508
  const lines = content.split("\n");
191120
191509
  const newLines = [];
@@ -191129,7 +191518,7 @@ function SetConfigUI({ configName, initialValue }) {
191129
191518
  }
191130
191519
  }
191131
191520
  if (found) {
191132
- fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191521
+ fs24.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191133
191522
  setDone(true);
191134
191523
  return;
191135
191524
  }
@@ -191142,7 +191531,7 @@ function SetConfigUI({ configName, initialValue }) {
191142
191531
  }
191143
191532
  newContent += `${hclLine}
191144
191533
  `;
191145
- fs25.writeFileSync(CONFIG_FILE, newContent);
191534
+ fs24.writeFileSync(CONFIG_FILE, newContent);
191146
191535
  setDone(true);
191147
191536
  } catch (err) {
191148
191537
  setError(err instanceof Error ? err.message : String(err));
@@ -191155,19 +191544,19 @@ function SetConfigUI({ configName, initialValue }) {
191155
191544
  setValue((prev) => prev + input);
191156
191545
  }
191157
191546
  });
191158
- useEffect8(() => {
191547
+ useEffect7(() => {
191159
191548
  if (done) {
191160
191549
  const timer = setTimeout(() => exit(), 50);
191161
191550
  return () => clearTimeout(timer);
191162
191551
  }
191163
191552
  }, [done, exit]);
191164
191553
  if (error) {
191165
- return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", error);
191554
+ return /* @__PURE__ */ React10.createElement(Text10, { color: "red" }, "Error: ", error);
191166
191555
  }
191167
191556
  if (done) {
191168
- return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Text8, { color: "green" }, "Config '", configName, "' saved to ", CONFIG_FILE));
191557
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "Config '", configName, "' saved to ", CONFIG_FILE));
191169
191558
  }
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)"));
191559
+ return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text10, null, "Enter value for config '", configName, "':"), /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text10, { color: "cyan" }, "> "), /* @__PURE__ */ React10.createElement(Text10, null, value), /* @__PURE__ */ React10.createElement(Text10, { color: "gray" }, "|")), /* @__PURE__ */ React10.createElement(Text10, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
191171
191560
  }
191172
191561
  async function configSetCommand(configName, configValue) {
191173
191562
  if (!configName) {
@@ -191181,8 +191570,8 @@ async function configSetCommand(configName, configValue) {
191181
191570
  const hclLine = `${configName} = "${escapedValue}"`;
191182
191571
  let content = "";
191183
191572
  let hasHeader = false;
191184
- if (fs25.existsSync(CONFIG_FILE)) {
191185
- content = fs25.readFileSync(CONFIG_FILE, "utf-8");
191573
+ if (fs24.existsSync(CONFIG_FILE)) {
191574
+ content = fs24.readFileSync(CONFIG_FILE, "utf-8");
191186
191575
  hasHeader = content.startsWith("#");
191187
191576
  const lines = content.split("\n");
191188
191577
  const newLines = [];
@@ -191197,7 +191586,7 @@ async function configSetCommand(configName, configValue) {
191197
191586
  }
191198
191587
  }
191199
191588
  if (found) {
191200
- fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191589
+ fs24.writeFileSync(CONFIG_FILE, newLines.join("\n"));
191201
191590
  console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
191202
191591
  return;
191203
191592
  }
@@ -191210,7 +191599,7 @@ async function configSetCommand(configName, configValue) {
191210
191599
  }
191211
191600
  newContent += `${hclLine}
191212
191601
  `;
191213
- fs25.writeFileSync(CONFIG_FILE, newContent);
191602
+ fs24.writeFileSync(CONFIG_FILE, newContent);
191214
191603
  console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
191215
191604
  } catch (err) {
191216
191605
  console.error("Error:", err instanceof Error ? err.message : String(err));
@@ -191218,7 +191607,7 @@ async function configSetCommand(configName, configValue) {
191218
191607
  }
191219
191608
  return;
191220
191609
  }
191221
- render8(/* @__PURE__ */ React8.createElement(SetConfigUI, { configName }));
191610
+ render8(/* @__PURE__ */ React10.createElement(SetConfigUI, { configName }));
191222
191611
  }
191223
191612
  async function configCommand(action, configName, configValue) {
191224
191613
  if (!action) {
@@ -191243,62 +191632,26 @@ async function configCommand(action, configName, configValue) {
191243
191632
  }
191244
191633
 
191245
191634
  // 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);
191635
+ async function loginCommand() {
191636
+ if (isLoggedIn()) {
191637
+ console.log("Already logged in.");
191638
+ return;
191283
191639
  }
191284
- if (alreadyLoggedIn) {
191285
- return /* @__PURE__ */ React9.createElement(AlreadyLoggedInUI, null);
191640
+ const result = await performLogin();
191641
+ if (!result.success) {
191642
+ process.exitCode = 1;
191286
191643
  }
191287
- return null;
191288
- }
191289
- function loginCommand() {
191290
- render9(/* @__PURE__ */ React9.createElement(LoginUI, null));
191291
191644
  }
191292
191645
 
191293
191646
  // 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";
191647
+ import React11, { useState as useState10, useEffect as useEffect8 } from "react";
191648
+ import { render as render9, Text as Text11, useApp as useApp6 } from "ink";
191296
191649
  function LogoutUI() {
191297
- const { exit } = useApp8();
191650
+ const { exit } = useApp6();
191298
191651
  const [state, setState] = useState10({
191299
191652
  phase: "checking"
191300
191653
  });
191301
- useEffect10(() => {
191654
+ useEffect8(() => {
191302
191655
  if (state.phase !== "checking") return;
191303
191656
  if (!isLoggedIn()) {
191304
191657
  setState({ phase: "not-logged-in" });
@@ -191307,29 +191660,29 @@ function LogoutUI() {
191307
191660
  clearUserCredentials();
191308
191661
  setState({ phase: "done" });
191309
191662
  }, [state.phase]);
191310
- useEffect10(() => {
191663
+ useEffect8(() => {
191311
191664
  if (state.phase === "done" || state.phase === "not-logged-in") {
191312
191665
  const timer = setTimeout(() => exit(), 100);
191313
191666
  return () => clearTimeout(timer);
191314
191667
  }
191315
191668
  }, [state.phase, exit]);
191316
191669
  if (state.phase === "not-logged-in") {
191317
- return /* @__PURE__ */ React10.createElement(Text10, { dimColor: true }, "Not logged in.");
191670
+ return /* @__PURE__ */ React11.createElement(Text11, { dimColor: true }, "Not logged in.");
191318
191671
  }
191319
191672
  if (state.phase === "done") {
191320
- return /* @__PURE__ */ React10.createElement(Text10, { color: "green" }, "Logged out successfully.");
191673
+ return /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "Logged out successfully.");
191321
191674
  }
191322
- return /* @__PURE__ */ React10.createElement(Text10, null, "Logging out...");
191675
+ return /* @__PURE__ */ React11.createElement(Text11, null, "Logging out...");
191323
191676
  }
191324
191677
  function logoutCommand() {
191325
- render10(/* @__PURE__ */ React10.createElement(LogoutUI, null));
191678
+ render9(/* @__PURE__ */ React11.createElement(LogoutUI, null));
191326
191679
  }
191327
191680
 
191328
191681
  // src/cli.tsx
191329
191682
  var program = new Command();
191330
191683
  var env = "production";
191331
191684
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
191332
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.42").enablePositionalOptions();
191685
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.43").enablePositionalOptions();
191333
191686
  program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
191334
191687
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
191335
191688
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);