@specific.dev/cli 0.1.81 → 0.1.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/fullscreen/__next._full.txt +1 -1
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +1 -1
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +1 -1
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/mail/__next._full.txt +1 -1
- package/dist/admin/mail/__next._head.txt +1 -1
- package/dist/admin/mail/__next._index.txt +1 -1
- package/dist/admin/mail/__next._tree.txt +1 -1
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +1 -1
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +1 -1
- package/dist/admin/workflows/__next._tree.txt +1 -1
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +1 -1
- package/dist/cli.js +490 -322
- package/dist/docs/postgres.md +6 -0
- package/dist/postinstall.js +1 -1
- package/package.json +1 -1
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -39,10 +39,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
));
|
|
40
40
|
|
|
41
41
|
// node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
|
|
42
|
-
import
|
|
42
|
+
import fs6 from "node:fs";
|
|
43
43
|
function hasDockerEnv() {
|
|
44
44
|
try {
|
|
45
|
-
|
|
45
|
+
fs6.statSync("/.dockerenv");
|
|
46
46
|
return true;
|
|
47
47
|
} catch {
|
|
48
48
|
return false;
|
|
@@ -50,7 +50,7 @@ function hasDockerEnv() {
|
|
|
50
50
|
}
|
|
51
51
|
function hasDockerCGroup() {
|
|
52
52
|
try {
|
|
53
|
-
return
|
|
53
|
+
return fs6.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
54
54
|
} catch {
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
@@ -68,7 +68,7 @@ var init_is_docker = __esm({
|
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
// node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
|
|
71
|
-
import
|
|
71
|
+
import fs7 from "node:fs";
|
|
72
72
|
function isInsideContainer() {
|
|
73
73
|
if (cachedResult === void 0) {
|
|
74
74
|
cachedResult = hasContainerEnv() || isDocker();
|
|
@@ -81,7 +81,7 @@ var init_is_inside_container = __esm({
|
|
|
81
81
|
init_is_docker();
|
|
82
82
|
hasContainerEnv = () => {
|
|
83
83
|
try {
|
|
84
|
-
|
|
84
|
+
fs7.statSync("/run/.containerenv");
|
|
85
85
|
return true;
|
|
86
86
|
} catch {
|
|
87
87
|
return false;
|
|
@@ -92,8 +92,8 @@ var init_is_inside_container = __esm({
|
|
|
92
92
|
|
|
93
93
|
// node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js
|
|
94
94
|
import process2 from "node:process";
|
|
95
|
-
import
|
|
96
|
-
import
|
|
95
|
+
import os5 from "node:os";
|
|
96
|
+
import fs8 from "node:fs";
|
|
97
97
|
var isWsl, is_wsl_default;
|
|
98
98
|
var init_is_wsl = __esm({
|
|
99
99
|
"node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js"() {
|
|
@@ -102,14 +102,14 @@ var init_is_wsl = __esm({
|
|
|
102
102
|
if (process2.platform !== "linux") {
|
|
103
103
|
return false;
|
|
104
104
|
}
|
|
105
|
-
if (
|
|
105
|
+
if (os5.release().toLowerCase().includes("microsoft")) {
|
|
106
106
|
if (isInsideContainer()) {
|
|
107
107
|
return false;
|
|
108
108
|
}
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
111
|
try {
|
|
112
|
-
return
|
|
112
|
+
return fs8.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
113
113
|
} catch {
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
@@ -179,7 +179,7 @@ var init_utilities = __esm({
|
|
|
179
179
|
// node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
|
|
180
180
|
import { promisify as promisify2 } from "node:util";
|
|
181
181
|
import childProcess2 from "node:child_process";
|
|
182
|
-
import
|
|
182
|
+
import fs9, { constants as fsConstants } from "node:fs/promises";
|
|
183
183
|
var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
|
|
184
184
|
var init_wsl_utils = __esm({
|
|
185
185
|
"node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
|
|
@@ -198,14 +198,14 @@ var init_wsl_utils = __esm({
|
|
|
198
198
|
const configFilePath = "/etc/wsl.conf";
|
|
199
199
|
let isConfigFileExists = false;
|
|
200
200
|
try {
|
|
201
|
-
await
|
|
201
|
+
await fs9.access(configFilePath, fsConstants.F_OK);
|
|
202
202
|
isConfigFileExists = true;
|
|
203
203
|
} catch {
|
|
204
204
|
}
|
|
205
205
|
if (!isConfigFileExists) {
|
|
206
206
|
return defaultMountPoint;
|
|
207
207
|
}
|
|
208
|
-
const configContent = await
|
|
208
|
+
const configContent = await fs9.readFile(configFilePath, { encoding: "utf8" });
|
|
209
209
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
210
210
|
if (parsedMountPoint === void 0) {
|
|
211
211
|
return defaultMountPoint;
|
|
@@ -224,7 +224,7 @@ var init_wsl_utils = __esm({
|
|
|
224
224
|
canAccessPowerShellPromise ??= (async () => {
|
|
225
225
|
try {
|
|
226
226
|
const psPath = await powerShellPath2();
|
|
227
|
-
await
|
|
227
|
+
await fs9.access(psPath, fsConstants.X_OK);
|
|
228
228
|
return true;
|
|
229
229
|
} catch {
|
|
230
230
|
return false;
|
|
@@ -435,7 +435,7 @@ import process8 from "node:process";
|
|
|
435
435
|
import path5 from "node:path";
|
|
436
436
|
import { fileURLToPath } from "node:url";
|
|
437
437
|
import childProcess3 from "node:child_process";
|
|
438
|
-
import
|
|
438
|
+
import fs10, { constants as fsConstants2 } from "node:fs/promises";
|
|
439
439
|
function detectArchBinary(binary) {
|
|
440
440
|
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
441
441
|
return binary;
|
|
@@ -446,16 +446,16 @@ function detectArchBinary(binary) {
|
|
|
446
446
|
}
|
|
447
447
|
return archBinary;
|
|
448
448
|
}
|
|
449
|
-
function detectPlatformBinary({ [
|
|
449
|
+
function detectPlatformBinary({ [platform4]: platformBinary }, { wsl } = {}) {
|
|
450
450
|
if (wsl && is_wsl_default) {
|
|
451
451
|
return detectArchBinary(wsl);
|
|
452
452
|
}
|
|
453
453
|
if (!platformBinary) {
|
|
454
|
-
throw new Error(`${
|
|
454
|
+
throw new Error(`${platform4} is not supported`);
|
|
455
455
|
}
|
|
456
456
|
return detectArchBinary(platformBinary);
|
|
457
457
|
}
|
|
458
|
-
var fallbackAttemptSymbol, __dirname, localXdgOpenPath,
|
|
458
|
+
var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform4, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
|
|
459
459
|
var init_open = __esm({
|
|
460
460
|
"node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
|
|
461
461
|
init_wsl_utils();
|
|
@@ -467,7 +467,7 @@ var init_open = __esm({
|
|
|
467
467
|
fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
468
468
|
__dirname = import.meta.url ? path5.dirname(fileURLToPath(import.meta.url)) : "";
|
|
469
469
|
localXdgOpenPath = path5.join(__dirname, "xdg-open");
|
|
470
|
-
({ platform:
|
|
470
|
+
({ platform: platform4, arch } = process8);
|
|
471
471
|
tryEachApp = async (apps2, opener) => {
|
|
472
472
|
if (apps2.length === 0) {
|
|
473
473
|
return;
|
|
@@ -564,7 +564,7 @@ var init_open = __esm({
|
|
|
564
564
|
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
565
565
|
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
566
566
|
}
|
|
567
|
-
if (
|
|
567
|
+
if (platform4 === "darwin") {
|
|
568
568
|
command = "open";
|
|
569
569
|
if (options2.wait) {
|
|
570
570
|
cliArguments.push("--wait-apps");
|
|
@@ -578,7 +578,7 @@ var init_open = __esm({
|
|
|
578
578
|
if (app) {
|
|
579
579
|
cliArguments.push("-a", app);
|
|
580
580
|
}
|
|
581
|
-
} else if (
|
|
581
|
+
} else if (platform4 === "win32" || shouldUseWindowsInWsl) {
|
|
582
582
|
command = await powerShellPath2();
|
|
583
583
|
cliArguments.push(...executePowerShell.argumentsPrefix);
|
|
584
584
|
if (!is_wsl_default) {
|
|
@@ -614,11 +614,11 @@ var init_open = __esm({
|
|
|
614
614
|
const isBundled = !__dirname || __dirname === "/";
|
|
615
615
|
let exeLocalXdgOpen = false;
|
|
616
616
|
try {
|
|
617
|
-
await
|
|
617
|
+
await fs10.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
618
618
|
exeLocalXdgOpen = true;
|
|
619
619
|
} catch {
|
|
620
620
|
}
|
|
621
|
-
const useSystemXdgOpen = process8.versions.electron ?? (
|
|
621
|
+
const useSystemXdgOpen = process8.versions.electron ?? (platform4 === "android" || isBundled || !exeLocalXdgOpen);
|
|
622
622
|
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
623
623
|
}
|
|
624
624
|
if (appArguments.length > 0) {
|
|
@@ -629,7 +629,7 @@ var init_open = __esm({
|
|
|
629
629
|
childProcessOptions.detached = true;
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
|
-
if (
|
|
632
|
+
if (platform4 === "darwin" && appArguments.length > 0) {
|
|
633
633
|
cliArguments.push("--args", ...appArguments);
|
|
634
634
|
}
|
|
635
635
|
if (options2.target) {
|
|
@@ -754,8 +754,8 @@ var require_dist = __commonJS({
|
|
|
754
754
|
var $global, $module, $NaN = NaN;
|
|
755
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");
|
|
756
756
|
if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
|
|
757
|
-
var
|
|
758
|
-
"object" == typeof
|
|
757
|
+
var fs33 = $global.require("fs");
|
|
758
|
+
"object" == typeof fs33 && null !== fs33 && 0 !== Object.keys(fs33).length && ($global.fs = fs33);
|
|
759
759
|
} catch (e) {
|
|
760
760
|
}
|
|
761
761
|
if (!$global.fs) {
|
|
@@ -183430,11 +183430,14 @@ import { Command } from "commander";
|
|
|
183430
183430
|
import React2, { useState, useEffect } from "react";
|
|
183431
183431
|
import { render as render2, Text as Text2, Box as Box2, useInput, useApp } from "ink";
|
|
183432
183432
|
import "ink-spinner";
|
|
183433
|
-
import * as
|
|
183433
|
+
import * as fs12 from "fs";
|
|
183434
183434
|
import * as path7 from "path";
|
|
183435
|
+
import * as os8 from "os";
|
|
183435
183436
|
|
|
183436
183437
|
// src/lib/dev/system-setup.ts
|
|
183437
183438
|
import { execSync as execSync2 } from "child_process";
|
|
183439
|
+
import * as fs4 from "fs";
|
|
183440
|
+
import * as os4 from "os";
|
|
183438
183441
|
import * as path3 from "path";
|
|
183439
183442
|
|
|
183440
183443
|
// src/lib/dev/local-ca.ts
|
|
@@ -183470,17 +183473,17 @@ function caInstalledInTrustStore() {
|
|
|
183470
183473
|
if (!caFilesExist()) {
|
|
183471
183474
|
return false;
|
|
183472
183475
|
}
|
|
183473
|
-
const
|
|
183476
|
+
const platform8 = os.platform();
|
|
183474
183477
|
const certPath = path.join(getCADir(), "ca.crt");
|
|
183475
183478
|
const diskCert = fs.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
|
|
183476
183479
|
try {
|
|
183477
|
-
if (
|
|
183480
|
+
if (platform8 === "darwin") {
|
|
183478
183481
|
const keychainCert = execSync(
|
|
183479
|
-
|
|
183482
|
+
`security find-certificate -c "Specific Local Development CA" -p "${getLoginKeychainPath()}"`,
|
|
183480
183483
|
{ encoding: "utf-8" }
|
|
183481
183484
|
).replace(/\r\n/g, "\n").trim();
|
|
183482
183485
|
return keychainCert === diskCert;
|
|
183483
|
-
} else if (
|
|
183486
|
+
} else if (platform8 === "linux") {
|
|
183484
183487
|
const trustPaths = [
|
|
183485
183488
|
"/usr/local/share/ca-certificates/specific-local-ca.crt",
|
|
183486
183489
|
// Debian/Ubuntu
|
|
@@ -183591,16 +183594,23 @@ function removeCA() {
|
|
|
183591
183594
|
fs.unlinkSync(certPath);
|
|
183592
183595
|
}
|
|
183593
183596
|
}
|
|
183597
|
+
function getLoginKeychainPath() {
|
|
183598
|
+
return path.join(os.homedir(), "Library", "Keychains", "login.keychain-db");
|
|
183599
|
+
}
|
|
183600
|
+
function getCADeleteCommand() {
|
|
183601
|
+
return `security delete-certificate -c "Specific Local Development CA" "${getLoginKeychainPath()}"`;
|
|
183602
|
+
}
|
|
183603
|
+
function getCATrustCommand(certPath) {
|
|
183604
|
+
return `security add-trusted-cert -d -r trustRoot -k "${getLoginKeychainPath()}" "${certPath}"`;
|
|
183605
|
+
}
|
|
183594
183606
|
function getCAInstallCommands(certPath) {
|
|
183595
|
-
const
|
|
183596
|
-
if (
|
|
183607
|
+
const platform8 = os.platform();
|
|
183608
|
+
if (platform8 === "darwin") {
|
|
183597
183609
|
return [
|
|
183598
|
-
|
|
183599
|
-
|
|
183600
|
-
'security delete-certificate -c "Specific Local Development CA" /Library/Keychains/System.keychain 2>/dev/null || true',
|
|
183601
|
-
`security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`
|
|
183610
|
+
`${getCADeleteCommand()} 2>/dev/null || true`,
|
|
183611
|
+
getCATrustCommand(certPath)
|
|
183602
183612
|
];
|
|
183603
|
-
} else if (
|
|
183613
|
+
} else if (platform8 === "linux") {
|
|
183604
183614
|
if (fs.existsSync("/usr/local/share/ca-certificates")) {
|
|
183605
183615
|
return [
|
|
183606
183616
|
`cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
|
|
@@ -183619,7 +183629,7 @@ function getCAInstallCommands(certPath) {
|
|
|
183619
183629
|
}
|
|
183620
183630
|
throw new Error("Could not detect Linux certificate trust mechanism");
|
|
183621
183631
|
}
|
|
183622
|
-
throw new Error(`Unsupported platform: ${
|
|
183632
|
+
throw new Error(`Unsupported platform: ${platform8}`);
|
|
183623
183633
|
}
|
|
183624
183634
|
function generateCertificate(domain, keys = []) {
|
|
183625
183635
|
const caDir = getCADir();
|
|
@@ -183665,23 +183675,23 @@ var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
|
|
|
183665
183675
|
var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
|
|
183666
183676
|
var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
|
|
183667
183677
|
function resolverConfigExists() {
|
|
183668
|
-
const
|
|
183669
|
-
if (
|
|
183678
|
+
const platform8 = os2.platform();
|
|
183679
|
+
if (platform8 === "darwin") {
|
|
183670
183680
|
return fs2.existsSync(RESOLVER_FILE_MACOS);
|
|
183671
|
-
} else if (
|
|
183681
|
+
} else if (platform8 === "linux") {
|
|
183672
183682
|
return fs2.existsSync(RESOLVER_FILE_LINUX);
|
|
183673
183683
|
}
|
|
183674
183684
|
return false;
|
|
183675
183685
|
}
|
|
183676
183686
|
function getResolverInstallCommands(port) {
|
|
183677
|
-
const
|
|
183678
|
-
if (
|
|
183687
|
+
const platform8 = os2.platform();
|
|
183688
|
+
if (platform8 === "darwin") {
|
|
183679
183689
|
return [
|
|
183680
183690
|
"mkdir -p /etc/resolver",
|
|
183681
183691
|
`rm -f ${OLD_RESOLVER_FILE_MACOS}`,
|
|
183682
183692
|
`printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
|
|
183683
183693
|
];
|
|
183684
|
-
} else if (
|
|
183694
|
+
} else if (platform8 === "linux") {
|
|
183685
183695
|
if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
|
|
183686
183696
|
return [
|
|
183687
183697
|
"mkdir -p /etc/systemd/resolved.conf.d",
|
|
@@ -183775,19 +183785,60 @@ async function startDnsServer(port = DNS_PORT) {
|
|
|
183775
183785
|
|
|
183776
183786
|
// src/lib/dev/system-setup.ts
|
|
183777
183787
|
function systemSetupNeeded() {
|
|
183778
|
-
return
|
|
183788
|
+
return dnsSetupNeeded() || tlsSetupNeeded();
|
|
183789
|
+
}
|
|
183790
|
+
function dnsSetupNeeded() {
|
|
183791
|
+
return !resolverConfigExists();
|
|
183792
|
+
}
|
|
183793
|
+
function tlsSetupNeeded() {
|
|
183794
|
+
return !caInstalledInTrustStore();
|
|
183795
|
+
}
|
|
183796
|
+
function ensureCAGenerated() {
|
|
183797
|
+
if (caFilesExist()) {
|
|
183798
|
+
return false;
|
|
183799
|
+
}
|
|
183800
|
+
const { key, cert } = generateRootCA();
|
|
183801
|
+
saveCA(key, cert);
|
|
183802
|
+
return true;
|
|
183803
|
+
}
|
|
183804
|
+
function performDnsSetup() {
|
|
183805
|
+
const commands = getResolverInstallCommands(DNS_PORT);
|
|
183806
|
+
if (commands.length === 0) return;
|
|
183807
|
+
if (os4.platform() === "darwin") {
|
|
183808
|
+
execPrivilegedMacOS(commands);
|
|
183809
|
+
} else {
|
|
183810
|
+
execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
|
|
183811
|
+
}
|
|
183812
|
+
}
|
|
183813
|
+
function performTlsTrust() {
|
|
183814
|
+
const wasGenerated = ensureCAGenerated();
|
|
183815
|
+
const certPath = path3.join(getCADir(), "ca.crt");
|
|
183816
|
+
try {
|
|
183817
|
+
if (os4.platform() === "darwin") {
|
|
183818
|
+
execSync2(`${getCADeleteCommand()} 2>/dev/null || true`, {
|
|
183819
|
+
stdio: "inherit"
|
|
183820
|
+
});
|
|
183821
|
+
execSync2(getCATrustCommand(certPath), { stdio: "inherit" });
|
|
183822
|
+
} else {
|
|
183823
|
+
execSync2(
|
|
183824
|
+
`sudo sh -c '${getCAInstallCommands(certPath).join(" && ")}'`,
|
|
183825
|
+
{ stdio: "inherit" }
|
|
183826
|
+
);
|
|
183827
|
+
}
|
|
183828
|
+
} catch (err) {
|
|
183829
|
+
if (wasGenerated) {
|
|
183830
|
+
removeCA();
|
|
183831
|
+
}
|
|
183832
|
+
throw err;
|
|
183833
|
+
}
|
|
183779
183834
|
}
|
|
183780
183835
|
function performSystemSetup() {
|
|
183781
|
-
const
|
|
183836
|
+
const wasGenerated = ensureCAGenerated();
|
|
183782
183837
|
const needsInstallCA = !caInstalledInTrustStore();
|
|
183783
183838
|
const needsResolver = !resolverConfigExists();
|
|
183784
183839
|
if (!needsInstallCA && !needsResolver) {
|
|
183785
183840
|
return;
|
|
183786
183841
|
}
|
|
183787
|
-
if (needsGenerate) {
|
|
183788
|
-
const { key, cert } = generateRootCA();
|
|
183789
|
-
saveCA(key, cert);
|
|
183790
|
-
}
|
|
183791
183842
|
const certPath = path3.join(getCADir(), "ca.crt");
|
|
183792
183843
|
const commands = [];
|
|
183793
183844
|
if (needsInstallCA) {
|
|
@@ -183802,20 +183853,45 @@ function performSystemSetup() {
|
|
|
183802
183853
|
try {
|
|
183803
183854
|
execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
|
|
183804
183855
|
} catch (err) {
|
|
183805
|
-
if (
|
|
183856
|
+
if (wasGenerated) {
|
|
183806
183857
|
removeCA();
|
|
183807
183858
|
}
|
|
183808
183859
|
throw err;
|
|
183809
183860
|
}
|
|
183810
183861
|
}
|
|
183862
|
+
function execPrivilegedMacOS(commands) {
|
|
183863
|
+
if (commands.length === 0) return;
|
|
183864
|
+
const scriptPath = path3.join(os4.tmpdir(), "specific-setup.sh");
|
|
183865
|
+
const appPath = path3.join(os4.tmpdir(), "Specific.app");
|
|
183866
|
+
try {
|
|
183867
|
+
fs4.writeFileSync(
|
|
183868
|
+
scriptPath,
|
|
183869
|
+
"#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
|
|
183870
|
+
{ mode: 448 }
|
|
183871
|
+
);
|
|
183872
|
+
execSync2(
|
|
183873
|
+
`osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
|
|
183874
|
+
);
|
|
183875
|
+
execSync2(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
|
|
183876
|
+
} finally {
|
|
183877
|
+
try {
|
|
183878
|
+
fs4.unlinkSync(scriptPath);
|
|
183879
|
+
} catch {
|
|
183880
|
+
}
|
|
183881
|
+
try {
|
|
183882
|
+
fs4.rmSync(appPath, { recursive: true });
|
|
183883
|
+
} catch {
|
|
183884
|
+
}
|
|
183885
|
+
}
|
|
183886
|
+
}
|
|
183811
183887
|
|
|
183812
183888
|
// src/lib/analytics/index.ts
|
|
183813
183889
|
import { PostHog } from "posthog-node";
|
|
183814
|
-
import * as
|
|
183890
|
+
import * as os7 from "os";
|
|
183815
183891
|
import * as crypto from "crypto";
|
|
183816
183892
|
|
|
183817
183893
|
// src/lib/project/config.ts
|
|
183818
|
-
import * as
|
|
183894
|
+
import * as fs5 from "fs";
|
|
183819
183895
|
import * as path4 from "path";
|
|
183820
183896
|
var PROJECT_ID_FILE = ".specific/project_id";
|
|
183821
183897
|
var ProjectNotLinkedError = class extends Error {
|
|
@@ -183832,10 +183908,10 @@ Run: specific deploy`
|
|
|
183832
183908
|
};
|
|
183833
183909
|
function readProjectId(projectDir = process.cwd()) {
|
|
183834
183910
|
const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
|
|
183835
|
-
if (!
|
|
183911
|
+
if (!fs5.existsSync(projectIdPath)) {
|
|
183836
183912
|
throw new ProjectNotLinkedError();
|
|
183837
183913
|
}
|
|
183838
|
-
const projectId =
|
|
183914
|
+
const projectId = fs5.readFileSync(projectIdPath, "utf-8").trim();
|
|
183839
183915
|
if (!projectId) {
|
|
183840
183916
|
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
183841
183917
|
}
|
|
@@ -183843,20 +183919,20 @@ function readProjectId(projectDir = process.cwd()) {
|
|
|
183843
183919
|
}
|
|
183844
183920
|
function hasProjectId(projectDir = process.cwd()) {
|
|
183845
183921
|
const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
|
|
183846
|
-
return
|
|
183922
|
+
return fs5.existsSync(projectIdPath);
|
|
183847
183923
|
}
|
|
183848
183924
|
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
183849
183925
|
const specificDir = path4.join(projectDir, ".specific");
|
|
183850
|
-
if (!
|
|
183851
|
-
|
|
183926
|
+
if (!fs5.existsSync(specificDir)) {
|
|
183927
|
+
fs5.mkdirSync(specificDir, { recursive: true });
|
|
183852
183928
|
}
|
|
183853
|
-
|
|
183929
|
+
fs5.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
|
|
183854
183930
|
}
|
|
183855
183931
|
|
|
183856
183932
|
// src/lib/auth/credentials.ts
|
|
183857
|
-
import * as
|
|
183933
|
+
import * as fs11 from "fs";
|
|
183858
183934
|
import * as path6 from "path";
|
|
183859
|
-
import * as
|
|
183935
|
+
import * as os6 from "os";
|
|
183860
183936
|
|
|
183861
183937
|
// src/lib/auth/errors.ts
|
|
183862
183938
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -184437,18 +184513,18 @@ var ApiClient = class {
|
|
|
184437
184513
|
|
|
184438
184514
|
// src/lib/auth/credentials.ts
|
|
184439
184515
|
function getUserCredentialsDir() {
|
|
184440
|
-
return path6.join(
|
|
184516
|
+
return path6.join(os6.homedir(), ".specific");
|
|
184441
184517
|
}
|
|
184442
184518
|
function getCredentialsPath() {
|
|
184443
184519
|
return path6.join(getUserCredentialsDir(), "credentials.json");
|
|
184444
184520
|
}
|
|
184445
184521
|
function readUserCredentials() {
|
|
184446
184522
|
const credentialsPath = getCredentialsPath();
|
|
184447
|
-
if (!
|
|
184523
|
+
if (!fs11.existsSync(credentialsPath)) {
|
|
184448
184524
|
return null;
|
|
184449
184525
|
}
|
|
184450
184526
|
try {
|
|
184451
|
-
const content =
|
|
184527
|
+
const content = fs11.readFileSync(credentialsPath, "utf-8");
|
|
184452
184528
|
return JSON.parse(content);
|
|
184453
184529
|
} catch {
|
|
184454
184530
|
return null;
|
|
@@ -184456,18 +184532,18 @@ function readUserCredentials() {
|
|
|
184456
184532
|
}
|
|
184457
184533
|
function writeUserCredentials(credentials) {
|
|
184458
184534
|
const dir = getUserCredentialsDir();
|
|
184459
|
-
if (!
|
|
184460
|
-
|
|
184535
|
+
if (!fs11.existsSync(dir)) {
|
|
184536
|
+
fs11.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
184461
184537
|
}
|
|
184462
184538
|
const credentialsPath = getCredentialsPath();
|
|
184463
|
-
|
|
184539
|
+
fs11.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
184464
184540
|
mode: 384
|
|
184465
184541
|
});
|
|
184466
184542
|
}
|
|
184467
184543
|
function clearUserCredentials() {
|
|
184468
184544
|
const credentialsPath = getCredentialsPath();
|
|
184469
|
-
if (
|
|
184470
|
-
|
|
184545
|
+
if (fs11.existsSync(credentialsPath)) {
|
|
184546
|
+
fs11.unlinkSync(credentialsPath);
|
|
184471
184547
|
}
|
|
184472
184548
|
}
|
|
184473
184549
|
function isLoggedIn() {
|
|
@@ -184546,7 +184622,7 @@ function isEnabled() {
|
|
|
184546
184622
|
}
|
|
184547
184623
|
function getAnonymousId() {
|
|
184548
184624
|
if (anonymousId) return anonymousId;
|
|
184549
|
-
const machineId = `${
|
|
184625
|
+
const machineId = `${os7.hostname()}-${os7.userInfo().username}`;
|
|
184550
184626
|
anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
|
|
184551
184627
|
return anonymousId;
|
|
184552
184628
|
}
|
|
@@ -184593,7 +184669,7 @@ function trackEvent(event, properties) {
|
|
|
184593
184669
|
event,
|
|
184594
184670
|
properties: {
|
|
184595
184671
|
...properties,
|
|
184596
|
-
cli_version: "0.1.
|
|
184672
|
+
cli_version: "0.1.82",
|
|
184597
184673
|
platform: process.platform,
|
|
184598
184674
|
node_version: process.version,
|
|
184599
184675
|
project_id: getProjectId()
|
|
@@ -184630,57 +184706,57 @@ var options = [
|
|
|
184630
184706
|
];
|
|
184631
184707
|
function isGitProject() {
|
|
184632
184708
|
const gitPath = path7.join(process.cwd(), ".git");
|
|
184633
|
-
return
|
|
184709
|
+
return fs12.existsSync(gitPath);
|
|
184634
184710
|
}
|
|
184635
184711
|
function detectExistingAgents() {
|
|
184636
184712
|
const detected = {};
|
|
184637
184713
|
const cursorDir = path7.join(process.cwd(), ".cursor");
|
|
184638
|
-
if (
|
|
184714
|
+
if (fs12.existsSync(cursorDir)) {
|
|
184639
184715
|
detected["cursor"] = true;
|
|
184640
184716
|
}
|
|
184641
184717
|
const claudeDir = path7.join(process.cwd(), ".claude");
|
|
184642
184718
|
const claudeMd = path7.join(process.cwd(), "CLAUDE.md");
|
|
184643
|
-
if (
|
|
184719
|
+
if (fs12.existsSync(claudeDir) || fs12.existsSync(claudeMd)) {
|
|
184644
184720
|
detected["claude"] = true;
|
|
184645
184721
|
}
|
|
184646
184722
|
const agentsMd = path7.join(process.cwd(), "AGENTS.md");
|
|
184647
|
-
if (
|
|
184723
|
+
if (fs12.existsSync(agentsMd)) {
|
|
184648
184724
|
detected["codex"] = true;
|
|
184649
184725
|
}
|
|
184650
184726
|
return detected;
|
|
184651
184727
|
}
|
|
184652
184728
|
function appendOrCreateFile(filePath, content) {
|
|
184653
|
-
if (
|
|
184654
|
-
const existing =
|
|
184729
|
+
if (fs12.existsSync(filePath)) {
|
|
184730
|
+
const existing = fs12.readFileSync(filePath, "utf-8");
|
|
184655
184731
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
184656
184732
|
return "unchanged";
|
|
184657
184733
|
}
|
|
184658
184734
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
184659
|
-
|
|
184735
|
+
fs12.writeFileSync(filePath, existing + separator + content + "\n");
|
|
184660
184736
|
return "modified";
|
|
184661
184737
|
} else {
|
|
184662
|
-
|
|
184738
|
+
fs12.writeFileSync(filePath, content + "\n");
|
|
184663
184739
|
return "created";
|
|
184664
184740
|
}
|
|
184665
184741
|
}
|
|
184666
184742
|
function addToGitignore() {
|
|
184667
184743
|
const gitignorePath = path7.join(process.cwd(), ".gitignore");
|
|
184668
184744
|
const entries = [".specific", "specific.local"];
|
|
184669
|
-
if (
|
|
184670
|
-
const existing =
|
|
184745
|
+
if (fs12.existsSync(gitignorePath)) {
|
|
184746
|
+
const existing = fs12.readFileSync(gitignorePath, "utf-8");
|
|
184671
184747
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
184672
184748
|
const missingEntries = entries.filter((entry) => !lines.includes(entry));
|
|
184673
184749
|
if (missingEntries.length === 0) {
|
|
184674
184750
|
return "unchanged";
|
|
184675
184751
|
}
|
|
184676
184752
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
184677
|
-
|
|
184753
|
+
fs12.writeFileSync(
|
|
184678
184754
|
gitignorePath,
|
|
184679
184755
|
existing + separator + missingEntries.join("\n") + "\n"
|
|
184680
184756
|
);
|
|
184681
184757
|
return "modified";
|
|
184682
184758
|
} else {
|
|
184683
|
-
|
|
184759
|
+
fs12.writeFileSync(gitignorePath, entries.join("\n") + "\n");
|
|
184684
184760
|
return "created";
|
|
184685
184761
|
}
|
|
184686
184762
|
}
|
|
@@ -184688,8 +184764,8 @@ function configureClaudeCodePermissions() {
|
|
|
184688
184764
|
const claudeDir = path7.join(process.cwd(), ".claude");
|
|
184689
184765
|
const settingsPath = path7.join(claudeDir, "settings.local.json");
|
|
184690
184766
|
const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
|
|
184691
|
-
if (
|
|
184692
|
-
const existing = JSON.parse(
|
|
184767
|
+
if (fs12.existsSync(settingsPath)) {
|
|
184768
|
+
const existing = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
|
|
184693
184769
|
const allowList = existing?.permissions?.allow || [];
|
|
184694
184770
|
const missingPermissions = permissions.filter(
|
|
184695
184771
|
(p) => !allowList.includes(p)
|
|
@@ -184704,39 +184780,39 @@ function configureClaudeCodePermissions() {
|
|
|
184704
184780
|
existing.permissions.allow = [];
|
|
184705
184781
|
}
|
|
184706
184782
|
existing.permissions.allow.push(...missingPermissions);
|
|
184707
|
-
|
|
184783
|
+
fs12.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
|
|
184708
184784
|
return "modified";
|
|
184709
184785
|
}
|
|
184710
|
-
if (!
|
|
184711
|
-
|
|
184786
|
+
if (!fs12.existsSync(claudeDir)) {
|
|
184787
|
+
fs12.mkdirSync(claudeDir);
|
|
184712
184788
|
}
|
|
184713
184789
|
const settings = {
|
|
184714
184790
|
permissions: {
|
|
184715
184791
|
allow: permissions
|
|
184716
184792
|
}
|
|
184717
184793
|
};
|
|
184718
|
-
|
|
184794
|
+
fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
184719
184795
|
return "created";
|
|
184720
184796
|
}
|
|
184721
184797
|
function createCursorRule() {
|
|
184722
184798
|
const cursorDir = path7.join(process.cwd(), ".cursor");
|
|
184723
184799
|
const rulesDir = path7.join(cursorDir, "rules");
|
|
184724
184800
|
const mdcPath = path7.join(rulesDir, "specific.mdc");
|
|
184725
|
-
if (
|
|
184726
|
-
const existing =
|
|
184801
|
+
if (fs12.existsSync(mdcPath)) {
|
|
184802
|
+
const existing = fs12.readFileSync(mdcPath, "utf-8");
|
|
184727
184803
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
184728
184804
|
return "unchanged";
|
|
184729
184805
|
}
|
|
184730
|
-
|
|
184806
|
+
fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
184731
184807
|
return "modified";
|
|
184732
184808
|
}
|
|
184733
|
-
if (!
|
|
184734
|
-
|
|
184809
|
+
if (!fs12.existsSync(cursorDir)) {
|
|
184810
|
+
fs12.mkdirSync(cursorDir);
|
|
184735
184811
|
}
|
|
184736
|
-
if (!
|
|
184737
|
-
|
|
184812
|
+
if (!fs12.existsSync(rulesDir)) {
|
|
184813
|
+
fs12.mkdirSync(rulesDir);
|
|
184738
184814
|
}
|
|
184739
|
-
|
|
184815
|
+
fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
184740
184816
|
return "created";
|
|
184741
184817
|
}
|
|
184742
184818
|
function configureAgents(checked) {
|
|
@@ -184777,6 +184853,19 @@ function configureAgents(checked) {
|
|
|
184777
184853
|
}
|
|
184778
184854
|
return { agents, git, showManualInstructions: !!checked["other"] };
|
|
184779
184855
|
}
|
|
184856
|
+
function getInitialSetupPhase() {
|
|
184857
|
+
if (!systemSetupNeeded()) return "agents";
|
|
184858
|
+
if (os8.platform() === "darwin") {
|
|
184859
|
+
if (dnsSetupNeeded()) return "setup-dns";
|
|
184860
|
+
if (tlsSetupNeeded()) return "setup-tls";
|
|
184861
|
+
return "agents";
|
|
184862
|
+
}
|
|
184863
|
+
return "setup-combined";
|
|
184864
|
+
}
|
|
184865
|
+
function getNextPhase(current) {
|
|
184866
|
+
if (current === "setup-dns" && tlsSetupNeeded()) return "setup-tls";
|
|
184867
|
+
return "agents";
|
|
184868
|
+
}
|
|
184780
184869
|
function InitUI() {
|
|
184781
184870
|
const { exit } = useApp();
|
|
184782
184871
|
const [initialState] = useState(() => {
|
|
@@ -184785,45 +184874,92 @@ function InitUI() {
|
|
|
184785
184874
|
return {
|
|
184786
184875
|
detected,
|
|
184787
184876
|
focusedIndex: hasDetected ? options.length : 0
|
|
184788
|
-
// Focus submit if any detected
|
|
184789
184877
|
};
|
|
184790
184878
|
});
|
|
184791
|
-
const [phase, setPhase] = useState(
|
|
184792
|
-
|
|
184879
|
+
const [phase, setPhase] = useState(getInitialSetupPhase);
|
|
184880
|
+
const [stepPhase, setStepPhase] = useState(
|
|
184881
|
+
() => getInitialSetupPhase() === "agents" ? "done" : "prompt"
|
|
184793
184882
|
);
|
|
184794
|
-
const [
|
|
184883
|
+
const [stepError, setStepError] = useState();
|
|
184795
184884
|
const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
|
|
184796
184885
|
const [checked, setChecked] = useState(
|
|
184797
184886
|
initialState.detected
|
|
184798
184887
|
);
|
|
184799
184888
|
const [result, setResult] = useState(null);
|
|
184800
|
-
const [
|
|
184801
|
-
!systemSetupNeeded() ? { status: "success" } :
|
|
184889
|
+
const [setupResult, setSetupResult] = useState(
|
|
184890
|
+
() => !systemSetupNeeded() ? { dns: { status: "success" }, tls: { status: "success" } } : {}
|
|
184802
184891
|
);
|
|
184803
184892
|
useEffect(() => {
|
|
184804
|
-
if (
|
|
184805
|
-
|
|
184893
|
+
if (stepPhase !== "installing") return;
|
|
184894
|
+
if (phase === "setup-dns") {
|
|
184895
|
+
runSetupStep(
|
|
184896
|
+
() => {
|
|
184897
|
+
ensureCAGenerated();
|
|
184898
|
+
performDnsSetup();
|
|
184899
|
+
},
|
|
184900
|
+
"dns"
|
|
184901
|
+
);
|
|
184902
|
+
} else if (phase === "setup-tls") {
|
|
184903
|
+
runSetupStep(
|
|
184904
|
+
() => {
|
|
184905
|
+
ensureCAGenerated();
|
|
184906
|
+
performTlsTrust();
|
|
184907
|
+
},
|
|
184908
|
+
"tls"
|
|
184909
|
+
);
|
|
184910
|
+
} else if (phase === "setup-combined") {
|
|
184911
|
+
runSetupStep(
|
|
184912
|
+
() => performSystemSetup(),
|
|
184913
|
+
"combined"
|
|
184914
|
+
);
|
|
184806
184915
|
}
|
|
184807
|
-
}, [phase,
|
|
184808
|
-
|
|
184916
|
+
}, [phase, stepPhase]);
|
|
184917
|
+
function runSetupStep(fn, type) {
|
|
184809
184918
|
try {
|
|
184810
|
-
|
|
184811
|
-
|
|
184812
|
-
|
|
184813
|
-
|
|
184919
|
+
fn();
|
|
184920
|
+
if (type === "combined") {
|
|
184921
|
+
setSetupResult((r) => ({
|
|
184922
|
+
...r,
|
|
184923
|
+
dns: { status: "success" },
|
|
184924
|
+
tls: { status: "success" }
|
|
184925
|
+
}));
|
|
184926
|
+
} else {
|
|
184927
|
+
setSetupResult((r) => ({ ...r, [type]: { status: "success" } }));
|
|
184928
|
+
}
|
|
184929
|
+
setStepPhase("done");
|
|
184930
|
+
const next = getNextPhase(phase);
|
|
184931
|
+
setPhase(next);
|
|
184932
|
+
if (next !== "agents") {
|
|
184933
|
+
setStepPhase("prompt");
|
|
184934
|
+
}
|
|
184814
184935
|
} catch (err) {
|
|
184815
|
-
|
|
184816
|
-
|
|
184817
|
-
|
|
184818
|
-
|
|
184819
|
-
|
|
184820
|
-
|
|
184936
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
184937
|
+
if (type === "combined") {
|
|
184938
|
+
setSetupResult((r) => ({
|
|
184939
|
+
...r,
|
|
184940
|
+
dns: { status: "error", error: errorMsg },
|
|
184941
|
+
tls: { status: "error", error: errorMsg }
|
|
184942
|
+
}));
|
|
184943
|
+
} else {
|
|
184944
|
+
setSetupResult((r) => ({
|
|
184945
|
+
...r,
|
|
184946
|
+
[type]: { status: "error", error: errorMsg }
|
|
184947
|
+
}));
|
|
184948
|
+
}
|
|
184949
|
+
setStepError(errorMsg);
|
|
184950
|
+
setStepPhase("error");
|
|
184951
|
+
const next = getNextPhase(phase);
|
|
184952
|
+
setPhase(next);
|
|
184953
|
+
if (next !== "agents") {
|
|
184954
|
+
setStepPhase("prompt");
|
|
184955
|
+
setStepError(void 0);
|
|
184956
|
+
}
|
|
184821
184957
|
}
|
|
184822
184958
|
}
|
|
184823
184959
|
const isSubmitFocused = focusedIndex === options.length;
|
|
184824
184960
|
useInput((input, key) => {
|
|
184825
|
-
if (phase === "
|
|
184826
|
-
|
|
184961
|
+
if ((phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") && stepPhase === "prompt" && key.return) {
|
|
184962
|
+
setStepPhase("installing");
|
|
184827
184963
|
return;
|
|
184828
184964
|
}
|
|
184829
184965
|
if (phase !== "agents") return;
|
|
@@ -184854,11 +184990,13 @@ function InitUI() {
|
|
|
184854
184990
|
return () => clearTimeout(timer);
|
|
184855
184991
|
}
|
|
184856
184992
|
}, [phase, exit]);
|
|
184857
|
-
if (phase === "
|
|
184858
|
-
if (
|
|
184859
|
-
|
|
184993
|
+
if (phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") {
|
|
184994
|
+
if (stepPhase === "prompt") {
|
|
184995
|
+
const isDns = phase === "setup-dns";
|
|
184996
|
+
const isTls = phase === "setup-tls";
|
|
184997
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), (isDns || phase === "setup-combined") && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize.")), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to continue."));
|
|
184860
184998
|
}
|
|
184861
|
-
if (
|
|
184999
|
+
if (stepPhase === "installing") {
|
|
184862
185000
|
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Awaiting authorization\u2026"));
|
|
184863
185001
|
}
|
|
184864
185002
|
}
|
|
@@ -184866,10 +185004,16 @@ function InitUI() {
|
|
|
184866
185004
|
const selectedAgents = Object.entries(checked).filter(([, v]) => v).length;
|
|
184867
185005
|
const agentChanges = result && (result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0);
|
|
184868
185006
|
const gitChanges = result?.git && (result.git.filesCreated.length > 0 || result.git.filesModified.length > 0);
|
|
184869
|
-
|
|
185007
|
+
const setupSuccess2 = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
|
|
185008
|
+
const setupError2 = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
|
|
185009
|
+
const setupErrorMsg2 = setupResult.dns?.error || setupResult.tls?.error;
|
|
185010
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupSuccess2 && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured"), setupError2 && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " Failed to set up TLS and DNS"), setupErrorMsg2 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", setupErrorMsg2)), result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
|
|
184870
185011
|
}
|
|
184871
|
-
const
|
|
184872
|
-
|
|
185012
|
+
const setupSuccess = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
|
|
185013
|
+
const setupError = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
|
|
185014
|
+
const setupErrorMsg = setupResult.dns?.error || setupResult.tls?.error;
|
|
185015
|
+
const setupLine = setupSuccess ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured") : setupError ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS and DNS setup failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", setupErrorMsg, ")")) : null;
|
|
185016
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupLine, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
|
|
184873
185017
|
const isFocused = focusedIndex === index;
|
|
184874
185018
|
const isChecked = checked[option.id];
|
|
184875
185019
|
return /* @__PURE__ */ React2.createElement(Box2, { key: option.id }, /* @__PURE__ */ React2.createElement(Text2, { ...isFocused && { color: "cyan" } }, isFocused ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", option.label));
|
|
@@ -184979,7 +185123,7 @@ var BETA_REGISTRY = [
|
|
|
184979
185123
|
];
|
|
184980
185124
|
|
|
184981
185125
|
// src/lib/beta/storage.ts
|
|
184982
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
185126
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
184983
185127
|
import { join as join7 } from "path";
|
|
184984
185128
|
var BETAS_FILE = ".specific/betas.json";
|
|
184985
185129
|
function loadEnabledBetas(projectDir = process.cwd()) {
|
|
@@ -185012,7 +185156,7 @@ function saveBetas(enabled, projectDir) {
|
|
|
185012
185156
|
mkdirSync6(specificDir, { recursive: true });
|
|
185013
185157
|
}
|
|
185014
185158
|
const data = { enabled };
|
|
185015
|
-
|
|
185159
|
+
writeFileSync6(
|
|
185016
185160
|
join7(projectDir, BETAS_FILE),
|
|
185017
185161
|
JSON.stringify(data, null, 2) + "\n"
|
|
185018
185162
|
);
|
|
@@ -185079,7 +185223,7 @@ function resolveFilesystemDoc(path30) {
|
|
|
185079
185223
|
import React3, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
185080
185224
|
import { render as render3, Text as Text3, Box as Box3 } from "ink";
|
|
185081
185225
|
import Spinner3 from "ink-spinner";
|
|
185082
|
-
import * as
|
|
185226
|
+
import * as fs14 from "fs";
|
|
185083
185227
|
import * as path9 from "path";
|
|
185084
185228
|
import { execFile as execFile7 } from "child_process";
|
|
185085
185229
|
|
|
@@ -185951,32 +186095,32 @@ var ExtractionError = class extends Error {
|
|
|
185951
186095
|
};
|
|
185952
186096
|
|
|
185953
186097
|
// src/lib/bin/manager.ts
|
|
185954
|
-
import * as
|
|
186098
|
+
import * as fs13 from "fs";
|
|
185955
186099
|
import * as path8 from "path";
|
|
185956
|
-
import * as
|
|
186100
|
+
import * as os9 from "os";
|
|
185957
186101
|
import { createReadStream } from "fs";
|
|
185958
186102
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185959
186103
|
function getLibraryEnv(binary) {
|
|
185960
186104
|
if (!binary.libraryPath) {
|
|
185961
186105
|
return {};
|
|
185962
186106
|
}
|
|
185963
|
-
const
|
|
185964
|
-
if (
|
|
186107
|
+
const platform8 = os9.platform();
|
|
186108
|
+
if (platform8 === "darwin") {
|
|
185965
186109
|
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
185966
|
-
} else if (
|
|
186110
|
+
} else if (platform8 === "linux") {
|
|
185967
186111
|
return { LD_LIBRARY_PATH: binary.libraryPath };
|
|
185968
186112
|
}
|
|
185969
186113
|
return {};
|
|
185970
186114
|
}
|
|
185971
186115
|
function getBinBaseDir() {
|
|
185972
|
-
return path8.join(
|
|
186116
|
+
return path8.join(os9.homedir(), ".specific", "bin");
|
|
185973
186117
|
}
|
|
185974
186118
|
function getPlatformInfo() {
|
|
185975
|
-
const
|
|
185976
|
-
const arch3 =
|
|
185977
|
-
if (
|
|
186119
|
+
const platform8 = os9.platform();
|
|
186120
|
+
const arch3 = os9.arch();
|
|
186121
|
+
if (platform8 !== "darwin" && platform8 !== "linux") {
|
|
185978
186122
|
throw new Error(
|
|
185979
|
-
`Unsupported platform: ${
|
|
186123
|
+
`Unsupported platform: ${platform8}. Only macOS and Linux are supported.`
|
|
185980
186124
|
);
|
|
185981
186125
|
}
|
|
185982
186126
|
const archStr = arch3;
|
|
@@ -185990,7 +186134,7 @@ function getPlatformInfo() {
|
|
|
185990
186134
|
`Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
|
|
185991
186135
|
);
|
|
185992
186136
|
}
|
|
185993
|
-
return { platform:
|
|
186137
|
+
return { platform: platform8, arch: mappedArch };
|
|
185994
186138
|
}
|
|
185995
186139
|
function getBinaryDir(definition, version, platformInfo) {
|
|
185996
186140
|
return path8.join(
|
|
@@ -186004,7 +186148,7 @@ function isBinaryInstalled(definition, version, platformInfo) {
|
|
|
186004
186148
|
const binDir = getBinaryDir(definition, version, platformInfo);
|
|
186005
186149
|
for (const execPath of definition.executables) {
|
|
186006
186150
|
const fullPath = path8.join(binDir, execPath);
|
|
186007
|
-
if (!
|
|
186151
|
+
if (!fs13.existsSync(fullPath)) {
|
|
186008
186152
|
return false;
|
|
186009
186153
|
}
|
|
186010
186154
|
}
|
|
@@ -186033,11 +186177,11 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
186033
186177
|
);
|
|
186034
186178
|
let bytesDownloaded = 0;
|
|
186035
186179
|
const parentDir = path8.dirname(destPath);
|
|
186036
|
-
if (!
|
|
186037
|
-
|
|
186180
|
+
if (!fs13.existsSync(parentDir)) {
|
|
186181
|
+
fs13.mkdirSync(parentDir, { recursive: true });
|
|
186038
186182
|
}
|
|
186039
186183
|
const partPath = destPath + ".part";
|
|
186040
|
-
const fileStream =
|
|
186184
|
+
const fileStream = fs13.createWriteStream(partPath);
|
|
186041
186185
|
try {
|
|
186042
186186
|
const reader = response.body.getReader();
|
|
186043
186187
|
while (true) {
|
|
@@ -186060,12 +186204,12 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
186060
186204
|
else resolve9();
|
|
186061
186205
|
});
|
|
186062
186206
|
});
|
|
186063
|
-
|
|
186207
|
+
fs13.renameSync(partPath, destPath);
|
|
186064
186208
|
} catch (error) {
|
|
186065
186209
|
try {
|
|
186066
186210
|
fileStream.close();
|
|
186067
|
-
if (
|
|
186068
|
-
|
|
186211
|
+
if (fs13.existsSync(partPath)) {
|
|
186212
|
+
fs13.unlinkSync(partPath);
|
|
186069
186213
|
}
|
|
186070
186214
|
} catch {
|
|
186071
186215
|
}
|
|
@@ -186074,8 +186218,8 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
186074
186218
|
}
|
|
186075
186219
|
async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
186076
186220
|
onProgress?.({ phase: "extracting" });
|
|
186077
|
-
if (!
|
|
186078
|
-
|
|
186221
|
+
if (!fs13.existsSync(destDir)) {
|
|
186222
|
+
fs13.mkdirSync(destDir, { recursive: true });
|
|
186079
186223
|
}
|
|
186080
186224
|
try {
|
|
186081
186225
|
const fileStream = createReadStream(archivePath);
|
|
@@ -186084,8 +186228,8 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
|
186084
186228
|
onProgress?.({ phase: "finalizing" });
|
|
186085
186229
|
for (const execPath of definition.executables) {
|
|
186086
186230
|
const fullPath = path8.join(destDir, execPath);
|
|
186087
|
-
if (
|
|
186088
|
-
|
|
186231
|
+
if (fs13.existsSync(fullPath)) {
|
|
186232
|
+
fs13.chmodSync(fullPath, 493);
|
|
186089
186233
|
}
|
|
186090
186234
|
}
|
|
186091
186235
|
} catch (error) {
|
|
@@ -186109,12 +186253,12 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
186109
186253
|
`Binary type definitions must have exactly one executable, got ${definition.executables.length}`
|
|
186110
186254
|
);
|
|
186111
186255
|
}
|
|
186112
|
-
if (!
|
|
186113
|
-
|
|
186256
|
+
if (!fs13.existsSync(binDir)) {
|
|
186257
|
+
fs13.mkdirSync(binDir, { recursive: true });
|
|
186114
186258
|
}
|
|
186115
186259
|
const execPath = path8.join(binDir, definition.executables[0]);
|
|
186116
186260
|
await downloadFile(url, execPath, onProgress);
|
|
186117
|
-
|
|
186261
|
+
fs13.chmodSync(execPath, 493);
|
|
186118
186262
|
onProgress?.({ phase: "finalizing" });
|
|
186119
186263
|
} else {
|
|
186120
186264
|
const downloadDir = path8.join(getBinBaseDir(), "downloads");
|
|
@@ -186125,8 +186269,8 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
186125
186269
|
await extractTarball(archivePath, binDir, definition, onProgress);
|
|
186126
186270
|
} finally {
|
|
186127
186271
|
try {
|
|
186128
|
-
if (
|
|
186129
|
-
|
|
186272
|
+
if (fs13.existsSync(archivePath)) {
|
|
186273
|
+
fs13.unlinkSync(archivePath);
|
|
186130
186274
|
}
|
|
186131
186275
|
} catch {
|
|
186132
186276
|
}
|
|
@@ -186302,20 +186446,20 @@ function CheckUI() {
|
|
|
186302
186446
|
useEffect2(() => {
|
|
186303
186447
|
async function load() {
|
|
186304
186448
|
const configPath = path9.join(process.cwd(), "specific.hcl");
|
|
186305
|
-
if (!
|
|
186449
|
+
if (!fs14.existsSync(configPath)) {
|
|
186306
186450
|
setState({
|
|
186307
186451
|
status: "error",
|
|
186308
186452
|
error: "No specific.hcl found in current directory"
|
|
186309
186453
|
});
|
|
186310
186454
|
return;
|
|
186311
186455
|
}
|
|
186312
|
-
const hcl =
|
|
186456
|
+
const hcl = fs14.readFileSync(configPath, "utf-8");
|
|
186313
186457
|
try {
|
|
186314
186458
|
const config2 = await parseConfig(hcl);
|
|
186315
186459
|
for (const build of config2.builds) {
|
|
186316
186460
|
if (build.dockerfile) {
|
|
186317
186461
|
const dockerfilePath = path9.resolve(process.cwd(), build.dockerfile);
|
|
186318
|
-
if (!
|
|
186462
|
+
if (!fs14.existsSync(dockerfilePath)) {
|
|
186319
186463
|
setState({
|
|
186320
186464
|
status: "error",
|
|
186321
186465
|
error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
|
|
@@ -186331,7 +186475,7 @@ function CheckUI() {
|
|
|
186331
186475
|
process.cwd(),
|
|
186332
186476
|
pg.reshape.migrations_dir ?? "migrations"
|
|
186333
186477
|
);
|
|
186334
|
-
if (!
|
|
186478
|
+
if (!fs14.existsSync(migrationsDir)) {
|
|
186335
186479
|
reshapeChecks2.push({
|
|
186336
186480
|
databaseName: pg.name,
|
|
186337
186481
|
migrationsDir: pg.reshape.migrations_dir ?? "migrations",
|
|
@@ -186384,7 +186528,8 @@ function checkCommand() {
|
|
|
186384
186528
|
import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
|
|
186385
186529
|
import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static, useInput as useInput4 } from "ink";
|
|
186386
186530
|
import Spinner4 from "ink-spinner";
|
|
186387
|
-
import * as
|
|
186531
|
+
import * as fs24 from "fs";
|
|
186532
|
+
import * as os11 from "os";
|
|
186388
186533
|
import * as path20 from "path";
|
|
186389
186534
|
|
|
186390
186535
|
// node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
@@ -188139,7 +188284,7 @@ var PortAllocator = class {
|
|
|
188139
188284
|
};
|
|
188140
188285
|
|
|
188141
188286
|
// src/lib/dev/stable-port-allocator.ts
|
|
188142
|
-
import * as
|
|
188287
|
+
import * as fs15 from "fs";
|
|
188143
188288
|
import * as path10 from "path";
|
|
188144
188289
|
var PORT_RANGE_START2 = 4e4;
|
|
188145
188290
|
var PORT_RANGE_END2 = 49999;
|
|
@@ -188154,11 +188299,11 @@ var StablePortAllocator = class {
|
|
|
188154
188299
|
this.loadPorts();
|
|
188155
188300
|
}
|
|
188156
188301
|
loadPorts() {
|
|
188157
|
-
if (!
|
|
188302
|
+
if (!fs15.existsSync(this.portsFilePath)) {
|
|
188158
188303
|
return;
|
|
188159
188304
|
}
|
|
188160
188305
|
try {
|
|
188161
|
-
const content =
|
|
188306
|
+
const content = fs15.readFileSync(this.portsFilePath, "utf-8");
|
|
188162
188307
|
const data = JSON.parse(content);
|
|
188163
188308
|
if (data.version === 1 && data.ports) {
|
|
188164
188309
|
this.savedPorts = data.ports;
|
|
@@ -188171,14 +188316,14 @@ var StablePortAllocator = class {
|
|
|
188171
188316
|
}
|
|
188172
188317
|
}
|
|
188173
188318
|
savePorts() {
|
|
188174
|
-
if (!
|
|
188175
|
-
|
|
188319
|
+
if (!fs15.existsSync(this.portsDir)) {
|
|
188320
|
+
fs15.mkdirSync(this.portsDir, { recursive: true });
|
|
188176
188321
|
}
|
|
188177
188322
|
const data = {
|
|
188178
188323
|
version: 1,
|
|
188179
188324
|
ports: this.savedPorts
|
|
188180
188325
|
};
|
|
188181
|
-
|
|
188326
|
+
fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
188182
188327
|
}
|
|
188183
188328
|
allocateRandom() {
|
|
188184
188329
|
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
@@ -188205,7 +188350,7 @@ var StablePortAllocator = class {
|
|
|
188205
188350
|
};
|
|
188206
188351
|
|
|
188207
188352
|
// src/lib/dev/database-manager.ts
|
|
188208
|
-
import * as
|
|
188353
|
+
import * as fs16 from "fs";
|
|
188209
188354
|
import * as path11 from "path";
|
|
188210
188355
|
import * as net from "net";
|
|
188211
188356
|
import { spawn } from "child_process";
|
|
@@ -188217,9 +188362,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
188217
188362
|
const password = "postgres";
|
|
188218
188363
|
const libraryEnv = getLibraryEnv(binary);
|
|
188219
188364
|
const env2 = { ...process.env, ...libraryEnv };
|
|
188220
|
-
const dataExists =
|
|
188365
|
+
const dataExists = fs16.existsSync(dbDataPath);
|
|
188221
188366
|
if (!dataExists) {
|
|
188222
|
-
|
|
188367
|
+
fs16.mkdirSync(dbDataPath, { recursive: true });
|
|
188223
188368
|
await runCommand(
|
|
188224
188369
|
binary.executables["initdb"],
|
|
188225
188370
|
["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
|
|
@@ -188296,8 +188441,8 @@ async function startRedis(redis, port, onProgress) {
|
|
|
188296
188441
|
async function startStorage(storage, port, dataDir) {
|
|
188297
188442
|
const S3rver = (await import("s3rver")).default;
|
|
188298
188443
|
const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
|
|
188299
|
-
if (!
|
|
188300
|
-
|
|
188444
|
+
if (!fs16.existsSync(storageDataPath)) {
|
|
188445
|
+
fs16.mkdirSync(storageDataPath, { recursive: true });
|
|
188301
188446
|
}
|
|
188302
188447
|
const host = "127.0.0.1";
|
|
188303
188448
|
const accessKey = "S3RVER";
|
|
@@ -188950,7 +189095,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
188950
189095
|
}
|
|
188951
189096
|
|
|
188952
189097
|
// src/lib/dev/instance-state.ts
|
|
188953
|
-
import * as
|
|
189098
|
+
import * as fs17 from "fs";
|
|
188954
189099
|
import * as path12 from "path";
|
|
188955
189100
|
var InstanceStateManager = class {
|
|
188956
189101
|
stateDir;
|
|
@@ -188968,8 +189113,8 @@ var InstanceStateManager = class {
|
|
|
188968
189113
|
return this.key;
|
|
188969
189114
|
}
|
|
188970
189115
|
ensureStateDir() {
|
|
188971
|
-
if (!
|
|
188972
|
-
|
|
189116
|
+
if (!fs17.existsSync(this.stateDir)) {
|
|
189117
|
+
fs17.mkdirSync(this.stateDir, { recursive: true });
|
|
188973
189118
|
}
|
|
188974
189119
|
}
|
|
188975
189120
|
isProcessRunning(pid) {
|
|
@@ -188986,15 +189131,15 @@ var InstanceStateManager = class {
|
|
|
188986
189131
|
const startTime = Date.now();
|
|
188987
189132
|
while (Date.now() - startTime < timeoutMs) {
|
|
188988
189133
|
try {
|
|
188989
|
-
const fd =
|
|
189134
|
+
const fd = fs17.openSync(
|
|
188990
189135
|
this.lockPath,
|
|
188991
|
-
|
|
189136
|
+
fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
|
|
188992
189137
|
);
|
|
188993
|
-
|
|
188994
|
-
|
|
189138
|
+
fs17.writeSync(fd, String(process.pid));
|
|
189139
|
+
fs17.closeSync(fd);
|
|
188995
189140
|
return () => {
|
|
188996
189141
|
try {
|
|
188997
|
-
|
|
189142
|
+
fs17.unlinkSync(this.lockPath);
|
|
188998
189143
|
} catch {
|
|
188999
189144
|
}
|
|
189000
189145
|
};
|
|
@@ -189003,16 +189148,16 @@ var InstanceStateManager = class {
|
|
|
189003
189148
|
if (err.code === "EEXIST") {
|
|
189004
189149
|
try {
|
|
189005
189150
|
const lockPid = parseInt(
|
|
189006
|
-
|
|
189151
|
+
fs17.readFileSync(this.lockPath, "utf-8").trim(),
|
|
189007
189152
|
10
|
|
189008
189153
|
);
|
|
189009
189154
|
if (!this.isProcessRunning(lockPid)) {
|
|
189010
|
-
|
|
189155
|
+
fs17.unlinkSync(this.lockPath);
|
|
189011
189156
|
continue;
|
|
189012
189157
|
}
|
|
189013
189158
|
} catch {
|
|
189014
189159
|
try {
|
|
189015
|
-
|
|
189160
|
+
fs17.unlinkSync(this.lockPath);
|
|
189016
189161
|
} catch {
|
|
189017
189162
|
}
|
|
189018
189163
|
continue;
|
|
@@ -189026,12 +189171,12 @@ var InstanceStateManager = class {
|
|
|
189026
189171
|
throw new Error("Failed to acquire state lock (timeout)");
|
|
189027
189172
|
}
|
|
189028
189173
|
async getExistingInstances() {
|
|
189029
|
-
if (!
|
|
189174
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
189030
189175
|
return null;
|
|
189031
189176
|
}
|
|
189032
189177
|
const releaseLock = await this.acquireLock();
|
|
189033
189178
|
try {
|
|
189034
|
-
const content =
|
|
189179
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189035
189180
|
const state = JSON.parse(content);
|
|
189036
189181
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
189037
189182
|
return null;
|
|
@@ -189044,21 +189189,21 @@ var InstanceStateManager = class {
|
|
|
189044
189189
|
}
|
|
189045
189190
|
}
|
|
189046
189191
|
async cleanStaleState() {
|
|
189047
|
-
if (!
|
|
189192
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
189048
189193
|
return false;
|
|
189049
189194
|
}
|
|
189050
189195
|
const releaseLock = await this.acquireLock();
|
|
189051
189196
|
try {
|
|
189052
|
-
const content =
|
|
189197
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189053
189198
|
const state = JSON.parse(content);
|
|
189054
189199
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
189055
|
-
|
|
189200
|
+
fs17.unlinkSync(this.statePath);
|
|
189056
189201
|
return true;
|
|
189057
189202
|
}
|
|
189058
189203
|
return false;
|
|
189059
189204
|
} catch {
|
|
189060
189205
|
try {
|
|
189061
|
-
|
|
189206
|
+
fs17.unlinkSync(this.statePath);
|
|
189062
189207
|
return true;
|
|
189063
189208
|
} catch {
|
|
189064
189209
|
}
|
|
@@ -189070,8 +189215,8 @@ var InstanceStateManager = class {
|
|
|
189070
189215
|
async claimOwnership(command) {
|
|
189071
189216
|
const releaseLock = await this.acquireLock();
|
|
189072
189217
|
try {
|
|
189073
|
-
if (
|
|
189074
|
-
const content =
|
|
189218
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189219
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189075
189220
|
const state2 = JSON.parse(content);
|
|
189076
189221
|
if (this.isProcessRunning(state2.owner.pid)) {
|
|
189077
189222
|
throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
|
|
@@ -189140,8 +189285,8 @@ var InstanceStateManager = class {
|
|
|
189140
189285
|
}
|
|
189141
189286
|
const releaseLock = await this.acquireLock();
|
|
189142
189287
|
try {
|
|
189143
|
-
if (
|
|
189144
|
-
|
|
189288
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189289
|
+
fs17.unlinkSync(this.statePath);
|
|
189145
189290
|
}
|
|
189146
189291
|
this.ownsInstances = false;
|
|
189147
189292
|
} finally {
|
|
@@ -189149,21 +189294,21 @@ var InstanceStateManager = class {
|
|
|
189149
189294
|
}
|
|
189150
189295
|
}
|
|
189151
189296
|
readState() {
|
|
189152
|
-
const content =
|
|
189297
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189153
189298
|
return JSON.parse(content);
|
|
189154
189299
|
}
|
|
189155
189300
|
writeStateAtomic(state) {
|
|
189156
189301
|
this.ensureStateDir();
|
|
189157
189302
|
const tmpPath = this.statePath + ".tmp";
|
|
189158
|
-
|
|
189159
|
-
|
|
189303
|
+
fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
189304
|
+
fs17.renameSync(tmpPath, this.statePath);
|
|
189160
189305
|
}
|
|
189161
189306
|
};
|
|
189162
189307
|
|
|
189163
189308
|
// src/lib/dev/http-proxy.ts
|
|
189164
189309
|
import * as http from "http";
|
|
189165
189310
|
import * as https from "https";
|
|
189166
|
-
import * as
|
|
189311
|
+
import * as fs18 from "fs";
|
|
189167
189312
|
import * as path13 from "path";
|
|
189168
189313
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
189169
189314
|
import httpProxy from "http-proxy";
|
|
@@ -189586,10 +189731,10 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189586
189731
|
res.end("<h1>Forbidden</h1>");
|
|
189587
189732
|
return;
|
|
189588
189733
|
}
|
|
189589
|
-
if (
|
|
189590
|
-
if (
|
|
189734
|
+
if (fs18.existsSync(resolvedPath)) {
|
|
189735
|
+
if (fs18.statSync(resolvedPath).isDirectory()) {
|
|
189591
189736
|
const indexPath2 = path13.join(resolvedPath, "index.html");
|
|
189592
|
-
if (
|
|
189737
|
+
if (fs18.existsSync(indexPath2)) {
|
|
189593
189738
|
return serveFile(res, indexPath2);
|
|
189594
189739
|
}
|
|
189595
189740
|
} else {
|
|
@@ -189597,15 +189742,15 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189597
189742
|
}
|
|
189598
189743
|
}
|
|
189599
189744
|
const htmlPath = resolvedPath + ".html";
|
|
189600
|
-
if (
|
|
189745
|
+
if (fs18.existsSync(htmlPath)) {
|
|
189601
189746
|
return serveFile(res, htmlPath);
|
|
189602
189747
|
}
|
|
189603
189748
|
const indexPath = path13.join(resolvedPath, "index.html");
|
|
189604
|
-
if (
|
|
189749
|
+
if (fs18.existsSync(indexPath)) {
|
|
189605
189750
|
return serveFile(res, indexPath);
|
|
189606
189751
|
}
|
|
189607
189752
|
const notFoundPath = path13.join(adminDir, "404.html");
|
|
189608
|
-
if (
|
|
189753
|
+
if (fs18.existsSync(notFoundPath)) {
|
|
189609
189754
|
return serveFileContent(res, notFoundPath, "text/html", 404);
|
|
189610
189755
|
}
|
|
189611
189756
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
@@ -189618,7 +189763,7 @@ function serveFile(res, filePath) {
|
|
|
189618
189763
|
}
|
|
189619
189764
|
function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
189620
189765
|
try {
|
|
189621
|
-
const content =
|
|
189766
|
+
const content = fs18.readFileSync(filePath);
|
|
189622
189767
|
res.writeHead(statusCode, { "Content-Type": contentType });
|
|
189623
189768
|
res.end(content);
|
|
189624
189769
|
} catch (err) {
|
|
@@ -189993,7 +190138,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
|
|
|
189993
190138
|
|
|
189994
190139
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
189995
190140
|
import * as net3 from "net";
|
|
189996
|
-
import * as
|
|
190141
|
+
import * as fs19 from "fs";
|
|
189997
190142
|
import * as path15 from "path";
|
|
189998
190143
|
import { spawn as spawn4 } from "child_process";
|
|
189999
190144
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -190027,12 +190172,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
|
|
|
190027
190172
|
);
|
|
190028
190173
|
const host = "127.0.0.1";
|
|
190029
190174
|
const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
|
|
190030
|
-
if (!
|
|
190031
|
-
|
|
190175
|
+
if (!fs19.existsSync(drizzleConfigDir)) {
|
|
190176
|
+
fs19.mkdirSync(drizzleConfigDir, { recursive: true });
|
|
190032
190177
|
}
|
|
190033
190178
|
const storeJson = generateStoreJson(postgresInstances);
|
|
190034
190179
|
const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
|
|
190035
|
-
|
|
190180
|
+
fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
|
|
190036
190181
|
writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
|
|
190037
190182
|
writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
|
|
190038
190183
|
writeLog("drizzle-gateway", `PORT: ${port}`);
|
|
@@ -190134,16 +190279,16 @@ function detectSyncDatabases(config) {
|
|
|
190134
190279
|
}
|
|
190135
190280
|
|
|
190136
190281
|
// src/lib/dev/reshape-watcher.ts
|
|
190137
|
-
import * as
|
|
190282
|
+
import * as fs20 from "fs";
|
|
190138
190283
|
import * as path16 from "path";
|
|
190139
190284
|
import { spawnSync } from "child_process";
|
|
190140
190285
|
function getMigrationFiles(dir, log) {
|
|
190141
190286
|
log(`Scanning migrations directory: ${dir}`);
|
|
190142
|
-
if (!
|
|
190287
|
+
if (!fs20.existsSync(dir)) {
|
|
190143
190288
|
log(`Migrations directory does not exist: ${dir}`);
|
|
190144
190289
|
return [];
|
|
190145
190290
|
}
|
|
190146
|
-
const files =
|
|
190291
|
+
const files = fs20.readdirSync(dir);
|
|
190147
190292
|
log(`Found ${files.length} files in directory`);
|
|
190148
190293
|
const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
|
|
190149
190294
|
log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
|
|
@@ -190193,7 +190338,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
|
|
|
190193
190338
|
}
|
|
190194
190339
|
function makeReadOnly(filePath, log) {
|
|
190195
190340
|
try {
|
|
190196
|
-
|
|
190341
|
+
fs20.chmodSync(filePath, 292);
|
|
190197
190342
|
log(`Set file permissions to read-only (444): ${filePath}`);
|
|
190198
190343
|
} catch (err) {
|
|
190199
190344
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -190279,9 +190424,9 @@ function createReshapeWatcher(options2) {
|
|
|
190279
190424
|
};
|
|
190280
190425
|
const startWatching = () => {
|
|
190281
190426
|
log(`Starting file watcher for migrations directory...`);
|
|
190282
|
-
if (!
|
|
190427
|
+
if (!fs20.existsSync(migrationsDir)) {
|
|
190283
190428
|
log(`Migrations directory does not exist, creating: ${migrationsDir}`);
|
|
190284
|
-
|
|
190429
|
+
fs20.mkdirSync(migrationsDir, { recursive: true });
|
|
190285
190430
|
}
|
|
190286
190431
|
log(`Watching directory: ${migrationsDir}`);
|
|
190287
190432
|
watcher = chokidar_default.watch(migrationsDir, {
|
|
@@ -190416,7 +190561,7 @@ function createReshapeWatcher(options2) {
|
|
|
190416
190561
|
}
|
|
190417
190562
|
|
|
190418
190563
|
// src/lib/dev/temporal-manager.ts
|
|
190419
|
-
import * as
|
|
190564
|
+
import * as fs21 from "fs";
|
|
190420
190565
|
import * as path17 from "path";
|
|
190421
190566
|
import * as net4 from "net";
|
|
190422
190567
|
import { spawn as spawn5 } from "child_process";
|
|
@@ -190424,8 +190569,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
|
|
|
190424
190569
|
const binary = await ensureBinary(temporalBinary, void 0, onProgress);
|
|
190425
190570
|
const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
|
|
190426
190571
|
const dbDir = path17.dirname(dbPath);
|
|
190427
|
-
if (!
|
|
190428
|
-
|
|
190572
|
+
if (!fs21.existsSync(dbDir)) {
|
|
190573
|
+
fs21.mkdirSync(dbDir, { recursive: true });
|
|
190429
190574
|
}
|
|
190430
190575
|
const host = "127.0.0.1";
|
|
190431
190576
|
const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
|
|
@@ -190754,7 +190899,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
190754
190899
|
}
|
|
190755
190900
|
|
|
190756
190901
|
// src/lib/dev/subdomain-generator.ts
|
|
190757
|
-
import * as
|
|
190902
|
+
import * as fs22 from "fs";
|
|
190758
190903
|
import * as path18 from "path";
|
|
190759
190904
|
import { generateSlug } from "random-word-slugs";
|
|
190760
190905
|
var StableSubdomainAllocator = class {
|
|
@@ -190767,11 +190912,11 @@ var StableSubdomainAllocator = class {
|
|
|
190767
190912
|
this.loadTunnels();
|
|
190768
190913
|
}
|
|
190769
190914
|
loadTunnels() {
|
|
190770
|
-
if (!
|
|
190915
|
+
if (!fs22.existsSync(this.tunnelsFilePath)) {
|
|
190771
190916
|
return;
|
|
190772
190917
|
}
|
|
190773
190918
|
try {
|
|
190774
|
-
const content =
|
|
190919
|
+
const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
|
|
190775
190920
|
const data = JSON.parse(content);
|
|
190776
190921
|
if (data.version === 1 && data.baseSlug) {
|
|
190777
190922
|
this.baseSlug = data.baseSlug;
|
|
@@ -190781,14 +190926,14 @@ var StableSubdomainAllocator = class {
|
|
|
190781
190926
|
}
|
|
190782
190927
|
}
|
|
190783
190928
|
saveTunnels() {
|
|
190784
|
-
if (!
|
|
190785
|
-
|
|
190929
|
+
if (!fs22.existsSync(this.tunnelsDir)) {
|
|
190930
|
+
fs22.mkdirSync(this.tunnelsDir, { recursive: true });
|
|
190786
190931
|
}
|
|
190787
190932
|
const data = {
|
|
190788
190933
|
version: 1,
|
|
190789
190934
|
baseSlug: this.baseSlug
|
|
190790
190935
|
};
|
|
190791
|
-
|
|
190936
|
+
fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
|
|
190792
190937
|
}
|
|
190793
190938
|
generateBaseSlug() {
|
|
190794
190939
|
return generateSlug(2, {
|
|
@@ -190944,9 +191089,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
190944
191089
|
}
|
|
190945
191090
|
|
|
190946
191091
|
// src/lib/dev/proxy-registry.ts
|
|
190947
|
-
import * as
|
|
191092
|
+
import * as fs23 from "fs";
|
|
190948
191093
|
import * as path19 from "path";
|
|
190949
|
-
import * as
|
|
191094
|
+
import * as os10 from "os";
|
|
190950
191095
|
import * as net6 from "net";
|
|
190951
191096
|
var ProxyRegistryManager = class {
|
|
190952
191097
|
proxyDir;
|
|
@@ -190956,14 +191101,14 @@ var ProxyRegistryManager = class {
|
|
|
190956
191101
|
isOwner = false;
|
|
190957
191102
|
registryWatcher = null;
|
|
190958
191103
|
constructor() {
|
|
190959
|
-
this.proxyDir = path19.join(
|
|
191104
|
+
this.proxyDir = path19.join(os10.homedir(), ".specific", "proxy");
|
|
190960
191105
|
this.ownerPath = path19.join(this.proxyDir, "owner.json");
|
|
190961
191106
|
this.registryPath = path19.join(this.proxyDir, "registry.json");
|
|
190962
191107
|
this.lockPath = path19.join(this.proxyDir, "registry.lock");
|
|
190963
191108
|
}
|
|
190964
191109
|
ensureProxyDir() {
|
|
190965
|
-
if (!
|
|
190966
|
-
|
|
191110
|
+
if (!fs23.existsSync(this.proxyDir)) {
|
|
191111
|
+
fs23.mkdirSync(this.proxyDir, { recursive: true });
|
|
190967
191112
|
}
|
|
190968
191113
|
}
|
|
190969
191114
|
isProcessRunning(pid) {
|
|
@@ -191020,15 +191165,15 @@ var ProxyRegistryManager = class {
|
|
|
191020
191165
|
const startTime = Date.now();
|
|
191021
191166
|
while (Date.now() - startTime < timeoutMs) {
|
|
191022
191167
|
try {
|
|
191023
|
-
const fd =
|
|
191168
|
+
const fd = fs23.openSync(
|
|
191024
191169
|
this.lockPath,
|
|
191025
|
-
|
|
191170
|
+
fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
|
|
191026
191171
|
);
|
|
191027
|
-
|
|
191028
|
-
|
|
191172
|
+
fs23.writeSync(fd, String(process.pid));
|
|
191173
|
+
fs23.closeSync(fd);
|
|
191029
191174
|
return () => {
|
|
191030
191175
|
try {
|
|
191031
|
-
|
|
191176
|
+
fs23.unlinkSync(this.lockPath);
|
|
191032
191177
|
} catch {
|
|
191033
191178
|
}
|
|
191034
191179
|
};
|
|
@@ -191037,16 +191182,16 @@ var ProxyRegistryManager = class {
|
|
|
191037
191182
|
if (err.code === "EEXIST") {
|
|
191038
191183
|
try {
|
|
191039
191184
|
const lockPid = parseInt(
|
|
191040
|
-
|
|
191185
|
+
fs23.readFileSync(this.lockPath, "utf-8").trim(),
|
|
191041
191186
|
10
|
|
191042
191187
|
);
|
|
191043
191188
|
if (!this.isProcessRunning(lockPid)) {
|
|
191044
|
-
|
|
191189
|
+
fs23.unlinkSync(this.lockPath);
|
|
191045
191190
|
continue;
|
|
191046
191191
|
}
|
|
191047
191192
|
} catch {
|
|
191048
191193
|
try {
|
|
191049
|
-
|
|
191194
|
+
fs23.unlinkSync(this.lockPath);
|
|
191050
191195
|
} catch {
|
|
191051
191196
|
}
|
|
191052
191197
|
continue;
|
|
@@ -191066,8 +191211,8 @@ var ProxyRegistryManager = class {
|
|
|
191066
191211
|
async claimProxyOwnership(key) {
|
|
191067
191212
|
const releaseLock = await this.acquireLock();
|
|
191068
191213
|
try {
|
|
191069
|
-
if (
|
|
191070
|
-
const content =
|
|
191214
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191215
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191071
191216
|
const ownerFile2 = JSON.parse(content);
|
|
191072
191217
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
191073
191218
|
return false;
|
|
@@ -191097,11 +191242,11 @@ var ProxyRegistryManager = class {
|
|
|
191097
191242
|
}
|
|
191098
191243
|
const releaseLock = await this.acquireLock();
|
|
191099
191244
|
try {
|
|
191100
|
-
if (
|
|
191101
|
-
const content =
|
|
191245
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191246
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191102
191247
|
const ownerFile = JSON.parse(content);
|
|
191103
191248
|
if (ownerFile.owner.pid === process.pid) {
|
|
191104
|
-
|
|
191249
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191105
191250
|
}
|
|
191106
191251
|
}
|
|
191107
191252
|
this.isOwner = false;
|
|
@@ -191113,12 +191258,12 @@ var ProxyRegistryManager = class {
|
|
|
191113
191258
|
* Get the current proxy owner.
|
|
191114
191259
|
*/
|
|
191115
191260
|
async getProxyOwner() {
|
|
191116
|
-
if (!
|
|
191261
|
+
if (!fs23.existsSync(this.ownerPath)) {
|
|
191117
191262
|
return null;
|
|
191118
191263
|
}
|
|
191119
191264
|
const releaseLock = await this.acquireLock();
|
|
191120
191265
|
try {
|
|
191121
|
-
const content =
|
|
191266
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191122
191267
|
const ownerFile = JSON.parse(content);
|
|
191123
191268
|
if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
|
|
191124
191269
|
return null;
|
|
@@ -191215,7 +191360,7 @@ var ProxyRegistryManager = class {
|
|
|
191215
191360
|
*/
|
|
191216
191361
|
watchRegistry(onChange) {
|
|
191217
191362
|
this.ensureProxyDir();
|
|
191218
|
-
if (!
|
|
191363
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191219
191364
|
const emptyRegistry = {
|
|
191220
191365
|
version: 1,
|
|
191221
191366
|
keys: {},
|
|
@@ -191258,13 +191403,13 @@ var ProxyRegistryManager = class {
|
|
|
191258
191403
|
async attemptElection(key) {
|
|
191259
191404
|
const releaseLock = await this.acquireLock();
|
|
191260
191405
|
try {
|
|
191261
|
-
if (
|
|
191262
|
-
const content =
|
|
191406
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191407
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191263
191408
|
const ownerFile2 = JSON.parse(content);
|
|
191264
191409
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
191265
191410
|
return false;
|
|
191266
191411
|
}
|
|
191267
|
-
|
|
191412
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191268
191413
|
}
|
|
191269
191414
|
const ownerFile = {
|
|
191270
191415
|
version: 1,
|
|
@@ -191282,7 +191427,7 @@ var ProxyRegistryManager = class {
|
|
|
191282
191427
|
}
|
|
191283
191428
|
}
|
|
191284
191429
|
readRegistry() {
|
|
191285
|
-
if (!
|
|
191430
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191286
191431
|
return {
|
|
191287
191432
|
version: 1,
|
|
191288
191433
|
keys: {},
|
|
@@ -191290,7 +191435,7 @@ var ProxyRegistryManager = class {
|
|
|
191290
191435
|
};
|
|
191291
191436
|
}
|
|
191292
191437
|
try {
|
|
191293
|
-
const content =
|
|
191438
|
+
const content = fs23.readFileSync(this.registryPath, "utf-8");
|
|
191294
191439
|
return JSON.parse(content);
|
|
191295
191440
|
} catch {
|
|
191296
191441
|
return {
|
|
@@ -191303,8 +191448,8 @@ var ProxyRegistryManager = class {
|
|
|
191303
191448
|
writeFileAtomic(filePath, data) {
|
|
191304
191449
|
this.ensureProxyDir();
|
|
191305
191450
|
const tmpPath = filePath + ".tmp";
|
|
191306
|
-
|
|
191307
|
-
|
|
191451
|
+
fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
191452
|
+
fs23.renameSync(tmpPath, filePath);
|
|
191308
191453
|
}
|
|
191309
191454
|
};
|
|
191310
191455
|
|
|
@@ -191411,9 +191556,17 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191411
191556
|
const { exit } = useApp2();
|
|
191412
191557
|
const [state, setState] = useState5(() => {
|
|
191413
191558
|
const setupDone = tunnelEnabled || !systemSetupNeeded();
|
|
191559
|
+
let setupStep;
|
|
191560
|
+
if (!setupDone) {
|
|
191561
|
+
if (os11.platform() === "darwin") {
|
|
191562
|
+
setupStep = dnsSetupNeeded() ? "setup-dns" : "setup-tls";
|
|
191563
|
+
} else {
|
|
191564
|
+
setupStep = "setup-combined";
|
|
191565
|
+
}
|
|
191566
|
+
}
|
|
191414
191567
|
return {
|
|
191415
|
-
status: setupDone ? "loading" : "
|
|
191416
|
-
...
|
|
191568
|
+
status: setupDone ? "loading" : "setup",
|
|
191569
|
+
...setupStep ? { setupStep, setupStepPhase: "prompt" } : {},
|
|
191417
191570
|
resources: /* @__PURE__ */ new Map(),
|
|
191418
191571
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
191419
191572
|
services: [],
|
|
@@ -191425,28 +191578,41 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191425
191578
|
};
|
|
191426
191579
|
});
|
|
191427
191580
|
useEffect3(() => {
|
|
191428
|
-
if (state.status
|
|
191429
|
-
installSystemConfig();
|
|
191430
|
-
}
|
|
191431
|
-
}, [state.status, state.caInstallPhase]);
|
|
191432
|
-
useInput4((_input, key) => {
|
|
191433
|
-
if (key.return) {
|
|
191434
|
-
setState((s) => ({ ...s, caInstallPhase: "installing" }));
|
|
191435
|
-
}
|
|
191436
|
-
}, { isActive: state.status === "installing-ca" && state.caInstallPhase === "prompt" });
|
|
191437
|
-
async function installSystemConfig() {
|
|
191581
|
+
if (state.status !== "setup" || state.setupStepPhase !== "installing") return;
|
|
191438
191582
|
try {
|
|
191439
|
-
|
|
191440
|
-
|
|
191441
|
-
|
|
191583
|
+
if (state.setupStep === "setup-dns") {
|
|
191584
|
+
ensureCAGenerated();
|
|
191585
|
+
performDnsSetup();
|
|
191586
|
+
} else if (state.setupStep === "setup-tls") {
|
|
191587
|
+
ensureCAGenerated();
|
|
191588
|
+
performTlsTrust();
|
|
191589
|
+
} else if (state.setupStep === "setup-combined") {
|
|
191590
|
+
performSystemSetup();
|
|
191591
|
+
}
|
|
191592
|
+
const nextStep = state.setupStep === "setup-dns" && tlsSetupNeeded() ? "setup-tls" : void 0;
|
|
191593
|
+
if (nextStep) {
|
|
191594
|
+
setState((s) => ({
|
|
191595
|
+
...s,
|
|
191596
|
+
setupStep: nextStep,
|
|
191597
|
+
setupStepPhase: "prompt"
|
|
191598
|
+
}));
|
|
191599
|
+
} else {
|
|
191600
|
+
setState((s) => ({ ...s, status: "loading", setupStepPhase: "done" }));
|
|
191601
|
+
setReadyToStart(true);
|
|
191602
|
+
}
|
|
191442
191603
|
} catch (err) {
|
|
191443
191604
|
setState((s) => ({
|
|
191444
191605
|
...s,
|
|
191445
|
-
|
|
191446
|
-
|
|
191606
|
+
setupStepPhase: "error",
|
|
191607
|
+
setupError: err instanceof Error ? err.message : String(err)
|
|
191447
191608
|
}));
|
|
191448
191609
|
}
|
|
191449
|
-
}
|
|
191610
|
+
}, [state.status, state.setupStepPhase]);
|
|
191611
|
+
useInput4((_input, key) => {
|
|
191612
|
+
if (state.status === "setup" && state.setupStepPhase === "prompt" && key.return) {
|
|
191613
|
+
setState((s) => ({ ...s, setupStepPhase: "installing" }));
|
|
191614
|
+
}
|
|
191615
|
+
});
|
|
191450
191616
|
const shuttingDown = useRef(false);
|
|
191451
191617
|
const startTimeRef = useRef(null);
|
|
191452
191618
|
const stateManagerRef = useRef(null);
|
|
@@ -191627,7 +191793,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191627
191793
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191628
191794
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
191629
191795
|
try {
|
|
191630
|
-
const hcl =
|
|
191796
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191631
191797
|
parseConfig(hcl).then(() => {
|
|
191632
191798
|
triggerReload();
|
|
191633
191799
|
}).catch((err) => {
|
|
@@ -191753,7 +191919,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191753
191919
|
return;
|
|
191754
191920
|
}
|
|
191755
191921
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191756
|
-
if (!
|
|
191922
|
+
if (!fs24.existsSync(configPath)) {
|
|
191757
191923
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
191758
191924
|
setState((s) => ({
|
|
191759
191925
|
...s,
|
|
@@ -191771,7 +191937,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191771
191937
|
return;
|
|
191772
191938
|
}
|
|
191773
191939
|
let config2;
|
|
191774
|
-
const hcl =
|
|
191940
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191775
191941
|
try {
|
|
191776
191942
|
config2 = await parseConfig(hcl);
|
|
191777
191943
|
} catch (err) {
|
|
@@ -192098,7 +192264,7 @@ Add them to the config block in specific.local`);
|
|
|
192098
192264
|
if (service.volumes) {
|
|
192099
192265
|
for (const vol of service.volumes) {
|
|
192100
192266
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192101
|
-
|
|
192267
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192102
192268
|
volumePaths.set(vol.name, volumeDir);
|
|
192103
192269
|
}
|
|
192104
192270
|
}
|
|
@@ -192154,7 +192320,7 @@ Add them to the config block in specific.local`);
|
|
|
192154
192320
|
if (service.volumes) {
|
|
192155
192321
|
for (const vol of service.volumes) {
|
|
192156
192322
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192157
|
-
|
|
192323
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192158
192324
|
volumePaths.set(vol.name, volumeDir);
|
|
192159
192325
|
}
|
|
192160
192326
|
}
|
|
@@ -192447,15 +192613,17 @@ Add them to the config block in specific.local`);
|
|
|
192447
192613
|
});
|
|
192448
192614
|
};
|
|
192449
192615
|
}, [reloadTrigger, readyToStart, instanceKey]);
|
|
192450
|
-
if (state.status === "
|
|
192451
|
-
if (state.
|
|
192452
|
-
|
|
192616
|
+
if (state.status === "setup") {
|
|
192617
|
+
if (state.setupStepPhase === "prompt") {
|
|
192618
|
+
const isDns = state.setupStep === "setup-dns";
|
|
192619
|
+
const isTls = state.setupStep === "setup-tls";
|
|
192620
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), (isDns || state.setupStep === "setup-combined") && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize.")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Press ", /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Enter"), " to continue."));
|
|
192453
192621
|
}
|
|
192454
|
-
if (state.
|
|
192622
|
+
if (state.setupStepPhase === "installing") {
|
|
192455
192623
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Awaiting authorization\u2026"));
|
|
192456
192624
|
}
|
|
192457
|
-
if (state.
|
|
192458
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.
|
|
192625
|
+
if (state.setupStepPhase === "error") {
|
|
192626
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.setupError));
|
|
192459
192627
|
}
|
|
192460
192628
|
}
|
|
192461
192629
|
if (state.status === "loading") {
|
|
@@ -192734,12 +192902,12 @@ init_open();
|
|
|
192734
192902
|
import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
|
|
192735
192903
|
import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
|
|
192736
192904
|
import Spinner5 from "ink-spinner";
|
|
192737
|
-
import * as
|
|
192905
|
+
import * as fs26 from "fs";
|
|
192738
192906
|
import * as path23 from "path";
|
|
192739
192907
|
|
|
192740
192908
|
// src/lib/tarball/create.ts
|
|
192741
192909
|
import { execSync as execSync4 } from "child_process";
|
|
192742
|
-
import * as
|
|
192910
|
+
import * as fs25 from "fs";
|
|
192743
192911
|
import * as path22 from "path";
|
|
192744
192912
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
192745
192913
|
function isInsideGitRepository(dir) {
|
|
@@ -192797,7 +192965,7 @@ var EXCLUDED_DIRS = [
|
|
|
192797
192965
|
];
|
|
192798
192966
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
192799
192967
|
const results = [];
|
|
192800
|
-
const entries = await
|
|
192968
|
+
const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
|
|
192801
192969
|
for (const entry of entries) {
|
|
192802
192970
|
const fullPath = path22.join(currentDir, entry.name);
|
|
192803
192971
|
const relativePath = path22.relative(baseDir, fullPath);
|
|
@@ -192816,7 +192984,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
192816
192984
|
async function createTarArchive(projectDir) {
|
|
192817
192985
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
192818
192986
|
const configPath = path22.join(projectDir, "specific.hcl");
|
|
192819
|
-
if (!
|
|
192987
|
+
if (!fs25.existsSync(configPath)) {
|
|
192820
192988
|
throw new Error("specific.hcl not found in project directory");
|
|
192821
192989
|
}
|
|
192822
192990
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -193738,12 +193906,12 @@ function DeployUI({ environment, config }) {
|
|
|
193738
193906
|
}
|
|
193739
193907
|
async function deployCommand(environment) {
|
|
193740
193908
|
const configPath = path23.join(process.cwd(), "specific.hcl");
|
|
193741
|
-
if (!
|
|
193909
|
+
if (!fs26.existsSync(configPath)) {
|
|
193742
193910
|
console.error("Error: No specific.hcl found in current directory");
|
|
193743
193911
|
process.exit(1);
|
|
193744
193912
|
}
|
|
193745
193913
|
let config;
|
|
193746
|
-
const hcl =
|
|
193914
|
+
const hcl = fs26.readFileSync(configPath, "utf-8");
|
|
193747
193915
|
try {
|
|
193748
193916
|
config = await parseConfig(hcl);
|
|
193749
193917
|
} catch (err) {
|
|
@@ -193764,7 +193932,7 @@ async function deployCommand(environment) {
|
|
|
193764
193932
|
|
|
193765
193933
|
// src/commands/exec.tsx
|
|
193766
193934
|
import { spawn as spawn6 } from "child_process";
|
|
193767
|
-
import * as
|
|
193935
|
+
import * as fs27 from "fs";
|
|
193768
193936
|
import * as path24 from "path";
|
|
193769
193937
|
function startSpinner(text) {
|
|
193770
193938
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -193807,12 +193975,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
193807
193975
|
}
|
|
193808
193976
|
};
|
|
193809
193977
|
const configPath = path24.join(process.cwd(), "specific.hcl");
|
|
193810
|
-
if (!
|
|
193978
|
+
if (!fs27.existsSync(configPath)) {
|
|
193811
193979
|
console.error("Error: No specific.hcl found in current directory");
|
|
193812
193980
|
process.exit(1);
|
|
193813
193981
|
}
|
|
193814
193982
|
let config;
|
|
193815
|
-
const hcl =
|
|
193983
|
+
const hcl = fs27.readFileSync(configPath, "utf-8");
|
|
193816
193984
|
try {
|
|
193817
193985
|
config = await parseConfig(hcl);
|
|
193818
193986
|
} catch (err) {
|
|
@@ -193982,7 +194150,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
193982
194150
|
|
|
193983
194151
|
// src/commands/psql.tsx
|
|
193984
194152
|
import { spawn as spawn7 } from "child_process";
|
|
193985
|
-
import * as
|
|
194153
|
+
import * as fs28 from "fs";
|
|
193986
194154
|
import * as path25 from "path";
|
|
193987
194155
|
async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
|
|
193988
194156
|
let startedResources = [];
|
|
@@ -194001,12 +194169,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194001
194169
|
}
|
|
194002
194170
|
};
|
|
194003
194171
|
const configPath = path25.join(process.cwd(), "specific.hcl");
|
|
194004
|
-
if (!
|
|
194172
|
+
if (!fs28.existsSync(configPath)) {
|
|
194005
194173
|
console.error("Error: No specific.hcl found in current directory");
|
|
194006
194174
|
process.exit(1);
|
|
194007
194175
|
}
|
|
194008
194176
|
let config;
|
|
194009
|
-
const hcl =
|
|
194177
|
+
const hcl = fs28.readFileSync(configPath, "utf-8");
|
|
194010
194178
|
try {
|
|
194011
194179
|
config = await parseConfig(hcl);
|
|
194012
194180
|
} catch (err) {
|
|
@@ -194130,7 +194298,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194130
194298
|
|
|
194131
194299
|
// src/commands/reshape.tsx
|
|
194132
194300
|
import { spawn as spawn8 } from "child_process";
|
|
194133
|
-
import * as
|
|
194301
|
+
import * as fs29 from "fs";
|
|
194134
194302
|
import * as path26 from "path";
|
|
194135
194303
|
var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
|
|
194136
194304
|
var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
|
|
@@ -194148,8 +194316,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194148
194316
|
let migrationsDir = "migrations";
|
|
194149
194317
|
let targetDb;
|
|
194150
194318
|
try {
|
|
194151
|
-
if (
|
|
194152
|
-
const configContent =
|
|
194319
|
+
if (fs29.existsSync(configPath)) {
|
|
194320
|
+
const configContent = fs29.readFileSync(configPath, "utf-8");
|
|
194153
194321
|
config = await parseConfig(configContent);
|
|
194154
194322
|
if (databaseName) {
|
|
194155
194323
|
const postgresConfig = config.postgres.find((p) => p.name === databaseName);
|
|
@@ -194285,7 +194453,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194285
194453
|
const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
|
|
194286
194454
|
const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
|
|
194287
194455
|
if (action === "check" || action === "start") {
|
|
194288
|
-
if (
|
|
194456
|
+
if (fs29.existsSync(fullMigrationsPath)) {
|
|
194289
194457
|
reshapeArgs.push("--dirs", fullMigrationsPath);
|
|
194290
194458
|
} else if (action === "check") {
|
|
194291
194459
|
console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
|
|
@@ -194335,7 +194503,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194335
194503
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
194336
194504
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
194337
194505
|
import Spinner6 from "ink-spinner";
|
|
194338
|
-
import * as
|
|
194506
|
+
import * as fs30 from "fs";
|
|
194339
194507
|
import * as path27 from "path";
|
|
194340
194508
|
function CleanUI({ instanceKey }) {
|
|
194341
194509
|
const [state, setState] = useState7({ status: "checking" });
|
|
@@ -194343,13 +194511,13 @@ function CleanUI({ instanceKey }) {
|
|
|
194343
194511
|
async function clean() {
|
|
194344
194512
|
const projectRoot = process.cwd();
|
|
194345
194513
|
const specificDir = path27.join(projectRoot, ".specific");
|
|
194346
|
-
if (!
|
|
194514
|
+
if (!fs30.existsSync(specificDir)) {
|
|
194347
194515
|
setState({ status: "nothing" });
|
|
194348
194516
|
return;
|
|
194349
194517
|
}
|
|
194350
194518
|
if (instanceKey) {
|
|
194351
194519
|
const keyDir = path27.join(specificDir, "keys", instanceKey);
|
|
194352
|
-
if (!
|
|
194520
|
+
if (!fs30.existsSync(keyDir)) {
|
|
194353
194521
|
setState({ status: "nothing" });
|
|
194354
194522
|
return;
|
|
194355
194523
|
}
|
|
@@ -194365,7 +194533,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194365
194533
|
await stateManager.cleanStaleState();
|
|
194366
194534
|
setState({ status: "cleaning" });
|
|
194367
194535
|
try {
|
|
194368
|
-
|
|
194536
|
+
fs30.rmSync(keyDir, { recursive: true, force: true });
|
|
194369
194537
|
setState({ status: "success" });
|
|
194370
194538
|
} catch (err) {
|
|
194371
194539
|
setState({
|
|
@@ -194375,12 +194543,12 @@ function CleanUI({ instanceKey }) {
|
|
|
194375
194543
|
}
|
|
194376
194544
|
} else {
|
|
194377
194545
|
const keysDir = path27.join(specificDir, "keys");
|
|
194378
|
-
if (!
|
|
194546
|
+
if (!fs30.existsSync(keysDir)) {
|
|
194379
194547
|
setState({ status: "nothing" });
|
|
194380
194548
|
return;
|
|
194381
194549
|
}
|
|
194382
|
-
const keys =
|
|
194383
|
-
(f) =>
|
|
194550
|
+
const keys = fs30.readdirSync(keysDir).filter(
|
|
194551
|
+
(f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
|
|
194384
194552
|
);
|
|
194385
194553
|
for (const key of keys) {
|
|
194386
194554
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -194404,7 +194572,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194404
194572
|
}
|
|
194405
194573
|
setState({ status: "cleaning" });
|
|
194406
194574
|
try {
|
|
194407
|
-
|
|
194575
|
+
fs30.rmSync(keysDir, { recursive: true, force: true });
|
|
194408
194576
|
setState({ status: "success" });
|
|
194409
194577
|
} catch (err) {
|
|
194410
194578
|
setState({
|
|
@@ -194569,7 +194737,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
|
|
|
194569
194737
|
import Spinner7 from "ink-spinner";
|
|
194570
194738
|
|
|
194571
194739
|
// src/lib/update.ts
|
|
194572
|
-
import * as
|
|
194740
|
+
import * as fs31 from "fs";
|
|
194573
194741
|
import * as path28 from "path";
|
|
194574
194742
|
var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
|
|
194575
194743
|
function compareVersions(a, b) {
|
|
@@ -194584,7 +194752,7 @@ function compareVersions(a, b) {
|
|
|
194584
194752
|
return 0;
|
|
194585
194753
|
}
|
|
194586
194754
|
async function checkForUpdate() {
|
|
194587
|
-
const currentVersion = "0.1.
|
|
194755
|
+
const currentVersion = "0.1.82";
|
|
194588
194756
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
194589
194757
|
if (!response.ok) {
|
|
194590
194758
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -194600,7 +194768,7 @@ function isBinaryWritable() {
|
|
|
194600
194768
|
const binaryPath = getCurrentBinaryPath();
|
|
194601
194769
|
const dir = path28.dirname(binaryPath);
|
|
194602
194770
|
try {
|
|
194603
|
-
|
|
194771
|
+
fs31.accessSync(dir, fs31.constants.W_OK);
|
|
194604
194772
|
return true;
|
|
194605
194773
|
} catch {
|
|
194606
194774
|
return false;
|
|
@@ -194611,21 +194779,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194611
194779
|
const binaryDir = path28.dirname(binaryPath);
|
|
194612
194780
|
const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
|
|
194613
194781
|
try {
|
|
194614
|
-
const { platform:
|
|
194615
|
-
const url = `${BINARIES_BASE_URL}/${version}/specific-${
|
|
194782
|
+
const { platform: platform8, arch: arch3 } = getPlatformInfo();
|
|
194783
|
+
const url = `${BINARIES_BASE_URL}/${version}/specific-${platform8}-${arch3}`;
|
|
194616
194784
|
await downloadFile(url, tempPath, onProgress);
|
|
194617
|
-
const stat4 =
|
|
194785
|
+
const stat4 = fs31.statSync(tempPath);
|
|
194618
194786
|
if (stat4.size === 0) {
|
|
194619
194787
|
throw new Error("Downloaded binary is empty");
|
|
194620
194788
|
}
|
|
194621
|
-
|
|
194789
|
+
fs31.chmodSync(tempPath, 493);
|
|
194622
194790
|
onProgress?.({ phase: "finalizing" });
|
|
194623
|
-
|
|
194624
|
-
|
|
194791
|
+
fs31.unlinkSync(binaryPath);
|
|
194792
|
+
fs31.renameSync(tempPath, binaryPath);
|
|
194625
194793
|
} catch (error) {
|
|
194626
194794
|
try {
|
|
194627
|
-
if (
|
|
194628
|
-
|
|
194795
|
+
if (fs31.existsSync(tempPath)) {
|
|
194796
|
+
fs31.unlinkSync(tempPath);
|
|
194629
194797
|
}
|
|
194630
194798
|
} catch {
|
|
194631
194799
|
}
|
|
@@ -194635,21 +194803,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194635
194803
|
|
|
194636
194804
|
// src/lib/background-update.ts
|
|
194637
194805
|
import { spawn as spawn9 } from "child_process";
|
|
194638
|
-
import * as
|
|
194806
|
+
import * as fs32 from "fs";
|
|
194639
194807
|
import * as path29 from "path";
|
|
194640
|
-
import * as
|
|
194641
|
-
var SPECIFIC_DIR = path29.join(
|
|
194808
|
+
import * as os12 from "os";
|
|
194809
|
+
var SPECIFIC_DIR = path29.join(os12.homedir(), ".specific");
|
|
194642
194810
|
var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
|
|
194643
194811
|
var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
|
|
194644
194812
|
var RATE_LIMIT_MS = 60 * 60 * 1e3;
|
|
194645
194813
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
194646
194814
|
function writeCheckTimestamp() {
|
|
194647
|
-
|
|
194648
|
-
|
|
194815
|
+
fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
|
|
194816
|
+
fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
|
|
194649
194817
|
}
|
|
194650
194818
|
function isRateLimited() {
|
|
194651
194819
|
try {
|
|
194652
|
-
const content =
|
|
194820
|
+
const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
|
|
194653
194821
|
const lastCheck = parseInt(content, 10);
|
|
194654
194822
|
if (isNaN(lastCheck)) return false;
|
|
194655
194823
|
return Date.now() - lastCheck < RATE_LIMIT_MS;
|
|
@@ -194783,7 +194951,7 @@ function updateCommand() {
|
|
|
194783
194951
|
var program = new Command();
|
|
194784
194952
|
var env = "production";
|
|
194785
194953
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
194786
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
194954
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.82").enablePositionalOptions();
|
|
194787
194955
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").action((options2) => initCommand(options2));
|
|
194788
194956
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
194789
194957
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|