@specific.dev/cli 0.1.81 → 0.1.83
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 +494 -323
- package/dist/docs/integrations/drizzle.md +5 -3
- package/dist/docs/postgres.md +7 -0
- package/dist/postinstall.js +1 -1
- package/package.json +1 -1
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_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.83",
|
|
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,9 @@ 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
|
|
186531
|
+
import { Readable as Readable2 } from "stream";
|
|
186532
|
+
import * as fs24 from "fs";
|
|
186533
|
+
import * as os11 from "os";
|
|
186388
186534
|
import * as path20 from "path";
|
|
186389
186535
|
|
|
186390
186536
|
// node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
@@ -188139,7 +188285,7 @@ var PortAllocator = class {
|
|
|
188139
188285
|
};
|
|
188140
188286
|
|
|
188141
188287
|
// src/lib/dev/stable-port-allocator.ts
|
|
188142
|
-
import * as
|
|
188288
|
+
import * as fs15 from "fs";
|
|
188143
188289
|
import * as path10 from "path";
|
|
188144
188290
|
var PORT_RANGE_START2 = 4e4;
|
|
188145
188291
|
var PORT_RANGE_END2 = 49999;
|
|
@@ -188154,11 +188300,11 @@ var StablePortAllocator = class {
|
|
|
188154
188300
|
this.loadPorts();
|
|
188155
188301
|
}
|
|
188156
188302
|
loadPorts() {
|
|
188157
|
-
if (!
|
|
188303
|
+
if (!fs15.existsSync(this.portsFilePath)) {
|
|
188158
188304
|
return;
|
|
188159
188305
|
}
|
|
188160
188306
|
try {
|
|
188161
|
-
const content =
|
|
188307
|
+
const content = fs15.readFileSync(this.portsFilePath, "utf-8");
|
|
188162
188308
|
const data = JSON.parse(content);
|
|
188163
188309
|
if (data.version === 1 && data.ports) {
|
|
188164
188310
|
this.savedPorts = data.ports;
|
|
@@ -188171,14 +188317,14 @@ var StablePortAllocator = class {
|
|
|
188171
188317
|
}
|
|
188172
188318
|
}
|
|
188173
188319
|
savePorts() {
|
|
188174
|
-
if (!
|
|
188175
|
-
|
|
188320
|
+
if (!fs15.existsSync(this.portsDir)) {
|
|
188321
|
+
fs15.mkdirSync(this.portsDir, { recursive: true });
|
|
188176
188322
|
}
|
|
188177
188323
|
const data = {
|
|
188178
188324
|
version: 1,
|
|
188179
188325
|
ports: this.savedPorts
|
|
188180
188326
|
};
|
|
188181
|
-
|
|
188327
|
+
fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
188182
188328
|
}
|
|
188183
188329
|
allocateRandom() {
|
|
188184
188330
|
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
@@ -188205,7 +188351,7 @@ var StablePortAllocator = class {
|
|
|
188205
188351
|
};
|
|
188206
188352
|
|
|
188207
188353
|
// src/lib/dev/database-manager.ts
|
|
188208
|
-
import * as
|
|
188354
|
+
import * as fs16 from "fs";
|
|
188209
188355
|
import * as path11 from "path";
|
|
188210
188356
|
import * as net from "net";
|
|
188211
188357
|
import { spawn } from "child_process";
|
|
@@ -188217,9 +188363,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
188217
188363
|
const password = "postgres";
|
|
188218
188364
|
const libraryEnv = getLibraryEnv(binary);
|
|
188219
188365
|
const env2 = { ...process.env, ...libraryEnv };
|
|
188220
|
-
const dataExists =
|
|
188366
|
+
const dataExists = fs16.existsSync(dbDataPath);
|
|
188221
188367
|
if (!dataExists) {
|
|
188222
|
-
|
|
188368
|
+
fs16.mkdirSync(dbDataPath, { recursive: true });
|
|
188223
188369
|
await runCommand(
|
|
188224
188370
|
binary.executables["initdb"],
|
|
188225
188371
|
["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
|
|
@@ -188296,8 +188442,8 @@ async function startRedis(redis, port, onProgress) {
|
|
|
188296
188442
|
async function startStorage(storage, port, dataDir) {
|
|
188297
188443
|
const S3rver = (await import("s3rver")).default;
|
|
188298
188444
|
const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
|
|
188299
|
-
if (!
|
|
188300
|
-
|
|
188445
|
+
if (!fs16.existsSync(storageDataPath)) {
|
|
188446
|
+
fs16.mkdirSync(storageDataPath, { recursive: true });
|
|
188301
188447
|
}
|
|
188302
188448
|
const host = "127.0.0.1";
|
|
188303
188449
|
const accessKey = "S3RVER";
|
|
@@ -188950,7 +189096,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
188950
189096
|
}
|
|
188951
189097
|
|
|
188952
189098
|
// src/lib/dev/instance-state.ts
|
|
188953
|
-
import * as
|
|
189099
|
+
import * as fs17 from "fs";
|
|
188954
189100
|
import * as path12 from "path";
|
|
188955
189101
|
var InstanceStateManager = class {
|
|
188956
189102
|
stateDir;
|
|
@@ -188968,8 +189114,8 @@ var InstanceStateManager = class {
|
|
|
188968
189114
|
return this.key;
|
|
188969
189115
|
}
|
|
188970
189116
|
ensureStateDir() {
|
|
188971
|
-
if (!
|
|
188972
|
-
|
|
189117
|
+
if (!fs17.existsSync(this.stateDir)) {
|
|
189118
|
+
fs17.mkdirSync(this.stateDir, { recursive: true });
|
|
188973
189119
|
}
|
|
188974
189120
|
}
|
|
188975
189121
|
isProcessRunning(pid) {
|
|
@@ -188986,15 +189132,15 @@ var InstanceStateManager = class {
|
|
|
188986
189132
|
const startTime = Date.now();
|
|
188987
189133
|
while (Date.now() - startTime < timeoutMs) {
|
|
188988
189134
|
try {
|
|
188989
|
-
const fd =
|
|
189135
|
+
const fd = fs17.openSync(
|
|
188990
189136
|
this.lockPath,
|
|
188991
|
-
|
|
189137
|
+
fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
|
|
188992
189138
|
);
|
|
188993
|
-
|
|
188994
|
-
|
|
189139
|
+
fs17.writeSync(fd, String(process.pid));
|
|
189140
|
+
fs17.closeSync(fd);
|
|
188995
189141
|
return () => {
|
|
188996
189142
|
try {
|
|
188997
|
-
|
|
189143
|
+
fs17.unlinkSync(this.lockPath);
|
|
188998
189144
|
} catch {
|
|
188999
189145
|
}
|
|
189000
189146
|
};
|
|
@@ -189003,16 +189149,16 @@ var InstanceStateManager = class {
|
|
|
189003
189149
|
if (err.code === "EEXIST") {
|
|
189004
189150
|
try {
|
|
189005
189151
|
const lockPid = parseInt(
|
|
189006
|
-
|
|
189152
|
+
fs17.readFileSync(this.lockPath, "utf-8").trim(),
|
|
189007
189153
|
10
|
|
189008
189154
|
);
|
|
189009
189155
|
if (!this.isProcessRunning(lockPid)) {
|
|
189010
|
-
|
|
189156
|
+
fs17.unlinkSync(this.lockPath);
|
|
189011
189157
|
continue;
|
|
189012
189158
|
}
|
|
189013
189159
|
} catch {
|
|
189014
189160
|
try {
|
|
189015
|
-
|
|
189161
|
+
fs17.unlinkSync(this.lockPath);
|
|
189016
189162
|
} catch {
|
|
189017
189163
|
}
|
|
189018
189164
|
continue;
|
|
@@ -189026,12 +189172,12 @@ var InstanceStateManager = class {
|
|
|
189026
189172
|
throw new Error("Failed to acquire state lock (timeout)");
|
|
189027
189173
|
}
|
|
189028
189174
|
async getExistingInstances() {
|
|
189029
|
-
if (!
|
|
189175
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
189030
189176
|
return null;
|
|
189031
189177
|
}
|
|
189032
189178
|
const releaseLock = await this.acquireLock();
|
|
189033
189179
|
try {
|
|
189034
|
-
const content =
|
|
189180
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189035
189181
|
const state = JSON.parse(content);
|
|
189036
189182
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
189037
189183
|
return null;
|
|
@@ -189044,21 +189190,21 @@ var InstanceStateManager = class {
|
|
|
189044
189190
|
}
|
|
189045
189191
|
}
|
|
189046
189192
|
async cleanStaleState() {
|
|
189047
|
-
if (!
|
|
189193
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
189048
189194
|
return false;
|
|
189049
189195
|
}
|
|
189050
189196
|
const releaseLock = await this.acquireLock();
|
|
189051
189197
|
try {
|
|
189052
|
-
const content =
|
|
189198
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189053
189199
|
const state = JSON.parse(content);
|
|
189054
189200
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
189055
|
-
|
|
189201
|
+
fs17.unlinkSync(this.statePath);
|
|
189056
189202
|
return true;
|
|
189057
189203
|
}
|
|
189058
189204
|
return false;
|
|
189059
189205
|
} catch {
|
|
189060
189206
|
try {
|
|
189061
|
-
|
|
189207
|
+
fs17.unlinkSync(this.statePath);
|
|
189062
189208
|
return true;
|
|
189063
189209
|
} catch {
|
|
189064
189210
|
}
|
|
@@ -189070,8 +189216,8 @@ var InstanceStateManager = class {
|
|
|
189070
189216
|
async claimOwnership(command) {
|
|
189071
189217
|
const releaseLock = await this.acquireLock();
|
|
189072
189218
|
try {
|
|
189073
|
-
if (
|
|
189074
|
-
const content =
|
|
189219
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189220
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189075
189221
|
const state2 = JSON.parse(content);
|
|
189076
189222
|
if (this.isProcessRunning(state2.owner.pid)) {
|
|
189077
189223
|
throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
|
|
@@ -189140,8 +189286,8 @@ var InstanceStateManager = class {
|
|
|
189140
189286
|
}
|
|
189141
189287
|
const releaseLock = await this.acquireLock();
|
|
189142
189288
|
try {
|
|
189143
|
-
if (
|
|
189144
|
-
|
|
189289
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189290
|
+
fs17.unlinkSync(this.statePath);
|
|
189145
189291
|
}
|
|
189146
189292
|
this.ownsInstances = false;
|
|
189147
189293
|
} finally {
|
|
@@ -189149,21 +189295,21 @@ var InstanceStateManager = class {
|
|
|
189149
189295
|
}
|
|
189150
189296
|
}
|
|
189151
189297
|
readState() {
|
|
189152
|
-
const content =
|
|
189298
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189153
189299
|
return JSON.parse(content);
|
|
189154
189300
|
}
|
|
189155
189301
|
writeStateAtomic(state) {
|
|
189156
189302
|
this.ensureStateDir();
|
|
189157
189303
|
const tmpPath = this.statePath + ".tmp";
|
|
189158
|
-
|
|
189159
|
-
|
|
189304
|
+
fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
189305
|
+
fs17.renameSync(tmpPath, this.statePath);
|
|
189160
189306
|
}
|
|
189161
189307
|
};
|
|
189162
189308
|
|
|
189163
189309
|
// src/lib/dev/http-proxy.ts
|
|
189164
189310
|
import * as http from "http";
|
|
189165
189311
|
import * as https from "https";
|
|
189166
|
-
import * as
|
|
189312
|
+
import * as fs18 from "fs";
|
|
189167
189313
|
import * as path13 from "path";
|
|
189168
189314
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
189169
189315
|
import httpProxy from "http-proxy";
|
|
@@ -189586,10 +189732,10 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189586
189732
|
res.end("<h1>Forbidden</h1>");
|
|
189587
189733
|
return;
|
|
189588
189734
|
}
|
|
189589
|
-
if (
|
|
189590
|
-
if (
|
|
189735
|
+
if (fs18.existsSync(resolvedPath)) {
|
|
189736
|
+
if (fs18.statSync(resolvedPath).isDirectory()) {
|
|
189591
189737
|
const indexPath2 = path13.join(resolvedPath, "index.html");
|
|
189592
|
-
if (
|
|
189738
|
+
if (fs18.existsSync(indexPath2)) {
|
|
189593
189739
|
return serveFile(res, indexPath2);
|
|
189594
189740
|
}
|
|
189595
189741
|
} else {
|
|
@@ -189597,15 +189743,15 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189597
189743
|
}
|
|
189598
189744
|
}
|
|
189599
189745
|
const htmlPath = resolvedPath + ".html";
|
|
189600
|
-
if (
|
|
189746
|
+
if (fs18.existsSync(htmlPath)) {
|
|
189601
189747
|
return serveFile(res, htmlPath);
|
|
189602
189748
|
}
|
|
189603
189749
|
const indexPath = path13.join(resolvedPath, "index.html");
|
|
189604
|
-
if (
|
|
189750
|
+
if (fs18.existsSync(indexPath)) {
|
|
189605
189751
|
return serveFile(res, indexPath);
|
|
189606
189752
|
}
|
|
189607
189753
|
const notFoundPath = path13.join(adminDir, "404.html");
|
|
189608
|
-
if (
|
|
189754
|
+
if (fs18.existsSync(notFoundPath)) {
|
|
189609
189755
|
return serveFileContent(res, notFoundPath, "text/html", 404);
|
|
189610
189756
|
}
|
|
189611
189757
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
@@ -189618,7 +189764,7 @@ function serveFile(res, filePath) {
|
|
|
189618
189764
|
}
|
|
189619
189765
|
function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
189620
189766
|
try {
|
|
189621
|
-
const content =
|
|
189767
|
+
const content = fs18.readFileSync(filePath);
|
|
189622
189768
|
res.writeHead(statusCode, { "Content-Type": contentType });
|
|
189623
189769
|
res.end(content);
|
|
189624
189770
|
} catch (err) {
|
|
@@ -189993,7 +190139,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
|
|
|
189993
190139
|
|
|
189994
190140
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
189995
190141
|
import * as net3 from "net";
|
|
189996
|
-
import * as
|
|
190142
|
+
import * as fs19 from "fs";
|
|
189997
190143
|
import * as path15 from "path";
|
|
189998
190144
|
import { spawn as spawn4 } from "child_process";
|
|
189999
190145
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -190027,12 +190173,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
|
|
|
190027
190173
|
);
|
|
190028
190174
|
const host = "127.0.0.1";
|
|
190029
190175
|
const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
|
|
190030
|
-
if (!
|
|
190031
|
-
|
|
190176
|
+
if (!fs19.existsSync(drizzleConfigDir)) {
|
|
190177
|
+
fs19.mkdirSync(drizzleConfigDir, { recursive: true });
|
|
190032
190178
|
}
|
|
190033
190179
|
const storeJson = generateStoreJson(postgresInstances);
|
|
190034
190180
|
const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
|
|
190035
|
-
|
|
190181
|
+
fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
|
|
190036
190182
|
writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
|
|
190037
190183
|
writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
|
|
190038
190184
|
writeLog("drizzle-gateway", `PORT: ${port}`);
|
|
@@ -190134,16 +190280,16 @@ function detectSyncDatabases(config) {
|
|
|
190134
190280
|
}
|
|
190135
190281
|
|
|
190136
190282
|
// src/lib/dev/reshape-watcher.ts
|
|
190137
|
-
import * as
|
|
190283
|
+
import * as fs20 from "fs";
|
|
190138
190284
|
import * as path16 from "path";
|
|
190139
190285
|
import { spawnSync } from "child_process";
|
|
190140
190286
|
function getMigrationFiles(dir, log) {
|
|
190141
190287
|
log(`Scanning migrations directory: ${dir}`);
|
|
190142
|
-
if (!
|
|
190288
|
+
if (!fs20.existsSync(dir)) {
|
|
190143
190289
|
log(`Migrations directory does not exist: ${dir}`);
|
|
190144
190290
|
return [];
|
|
190145
190291
|
}
|
|
190146
|
-
const files =
|
|
190292
|
+
const files = fs20.readdirSync(dir);
|
|
190147
190293
|
log(`Found ${files.length} files in directory`);
|
|
190148
190294
|
const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
|
|
190149
190295
|
log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
|
|
@@ -190193,7 +190339,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
|
|
|
190193
190339
|
}
|
|
190194
190340
|
function makeReadOnly(filePath, log) {
|
|
190195
190341
|
try {
|
|
190196
|
-
|
|
190342
|
+
fs20.chmodSync(filePath, 292);
|
|
190197
190343
|
log(`Set file permissions to read-only (444): ${filePath}`);
|
|
190198
190344
|
} catch (err) {
|
|
190199
190345
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -190279,9 +190425,9 @@ function createReshapeWatcher(options2) {
|
|
|
190279
190425
|
};
|
|
190280
190426
|
const startWatching = () => {
|
|
190281
190427
|
log(`Starting file watcher for migrations directory...`);
|
|
190282
|
-
if (!
|
|
190428
|
+
if (!fs20.existsSync(migrationsDir)) {
|
|
190283
190429
|
log(`Migrations directory does not exist, creating: ${migrationsDir}`);
|
|
190284
|
-
|
|
190430
|
+
fs20.mkdirSync(migrationsDir, { recursive: true });
|
|
190285
190431
|
}
|
|
190286
190432
|
log(`Watching directory: ${migrationsDir}`);
|
|
190287
190433
|
watcher = chokidar_default.watch(migrationsDir, {
|
|
@@ -190416,7 +190562,7 @@ function createReshapeWatcher(options2) {
|
|
|
190416
190562
|
}
|
|
190417
190563
|
|
|
190418
190564
|
// src/lib/dev/temporal-manager.ts
|
|
190419
|
-
import * as
|
|
190565
|
+
import * as fs21 from "fs";
|
|
190420
190566
|
import * as path17 from "path";
|
|
190421
190567
|
import * as net4 from "net";
|
|
190422
190568
|
import { spawn as spawn5 } from "child_process";
|
|
@@ -190424,8 +190570,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
|
|
|
190424
190570
|
const binary = await ensureBinary(temporalBinary, void 0, onProgress);
|
|
190425
190571
|
const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
|
|
190426
190572
|
const dbDir = path17.dirname(dbPath);
|
|
190427
|
-
if (!
|
|
190428
|
-
|
|
190573
|
+
if (!fs21.existsSync(dbDir)) {
|
|
190574
|
+
fs21.mkdirSync(dbDir, { recursive: true });
|
|
190429
190575
|
}
|
|
190430
190576
|
const host = "127.0.0.1";
|
|
190431
190577
|
const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
|
|
@@ -190754,7 +190900,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
190754
190900
|
}
|
|
190755
190901
|
|
|
190756
190902
|
// src/lib/dev/subdomain-generator.ts
|
|
190757
|
-
import * as
|
|
190903
|
+
import * as fs22 from "fs";
|
|
190758
190904
|
import * as path18 from "path";
|
|
190759
190905
|
import { generateSlug } from "random-word-slugs";
|
|
190760
190906
|
var StableSubdomainAllocator = class {
|
|
@@ -190767,11 +190913,11 @@ var StableSubdomainAllocator = class {
|
|
|
190767
190913
|
this.loadTunnels();
|
|
190768
190914
|
}
|
|
190769
190915
|
loadTunnels() {
|
|
190770
|
-
if (!
|
|
190916
|
+
if (!fs22.existsSync(this.tunnelsFilePath)) {
|
|
190771
190917
|
return;
|
|
190772
190918
|
}
|
|
190773
190919
|
try {
|
|
190774
|
-
const content =
|
|
190920
|
+
const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
|
|
190775
190921
|
const data = JSON.parse(content);
|
|
190776
190922
|
if (data.version === 1 && data.baseSlug) {
|
|
190777
190923
|
this.baseSlug = data.baseSlug;
|
|
@@ -190781,14 +190927,14 @@ var StableSubdomainAllocator = class {
|
|
|
190781
190927
|
}
|
|
190782
190928
|
}
|
|
190783
190929
|
saveTunnels() {
|
|
190784
|
-
if (!
|
|
190785
|
-
|
|
190930
|
+
if (!fs22.existsSync(this.tunnelsDir)) {
|
|
190931
|
+
fs22.mkdirSync(this.tunnelsDir, { recursive: true });
|
|
190786
190932
|
}
|
|
190787
190933
|
const data = {
|
|
190788
190934
|
version: 1,
|
|
190789
190935
|
baseSlug: this.baseSlug
|
|
190790
190936
|
};
|
|
190791
|
-
|
|
190937
|
+
fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
|
|
190792
190938
|
}
|
|
190793
190939
|
generateBaseSlug() {
|
|
190794
190940
|
return generateSlug(2, {
|
|
@@ -190944,9 +191090,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
190944
191090
|
}
|
|
190945
191091
|
|
|
190946
191092
|
// src/lib/dev/proxy-registry.ts
|
|
190947
|
-
import * as
|
|
191093
|
+
import * as fs23 from "fs";
|
|
190948
191094
|
import * as path19 from "path";
|
|
190949
|
-
import * as
|
|
191095
|
+
import * as os10 from "os";
|
|
190950
191096
|
import * as net6 from "net";
|
|
190951
191097
|
var ProxyRegistryManager = class {
|
|
190952
191098
|
proxyDir;
|
|
@@ -190956,14 +191102,14 @@ var ProxyRegistryManager = class {
|
|
|
190956
191102
|
isOwner = false;
|
|
190957
191103
|
registryWatcher = null;
|
|
190958
191104
|
constructor() {
|
|
190959
|
-
this.proxyDir = path19.join(
|
|
191105
|
+
this.proxyDir = path19.join(os10.homedir(), ".specific", "proxy");
|
|
190960
191106
|
this.ownerPath = path19.join(this.proxyDir, "owner.json");
|
|
190961
191107
|
this.registryPath = path19.join(this.proxyDir, "registry.json");
|
|
190962
191108
|
this.lockPath = path19.join(this.proxyDir, "registry.lock");
|
|
190963
191109
|
}
|
|
190964
191110
|
ensureProxyDir() {
|
|
190965
|
-
if (!
|
|
190966
|
-
|
|
191111
|
+
if (!fs23.existsSync(this.proxyDir)) {
|
|
191112
|
+
fs23.mkdirSync(this.proxyDir, { recursive: true });
|
|
190967
191113
|
}
|
|
190968
191114
|
}
|
|
190969
191115
|
isProcessRunning(pid) {
|
|
@@ -191020,15 +191166,15 @@ var ProxyRegistryManager = class {
|
|
|
191020
191166
|
const startTime = Date.now();
|
|
191021
191167
|
while (Date.now() - startTime < timeoutMs) {
|
|
191022
191168
|
try {
|
|
191023
|
-
const fd =
|
|
191169
|
+
const fd = fs23.openSync(
|
|
191024
191170
|
this.lockPath,
|
|
191025
|
-
|
|
191171
|
+
fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
|
|
191026
191172
|
);
|
|
191027
|
-
|
|
191028
|
-
|
|
191173
|
+
fs23.writeSync(fd, String(process.pid));
|
|
191174
|
+
fs23.closeSync(fd);
|
|
191029
191175
|
return () => {
|
|
191030
191176
|
try {
|
|
191031
|
-
|
|
191177
|
+
fs23.unlinkSync(this.lockPath);
|
|
191032
191178
|
} catch {
|
|
191033
191179
|
}
|
|
191034
191180
|
};
|
|
@@ -191037,16 +191183,16 @@ var ProxyRegistryManager = class {
|
|
|
191037
191183
|
if (err.code === "EEXIST") {
|
|
191038
191184
|
try {
|
|
191039
191185
|
const lockPid = parseInt(
|
|
191040
|
-
|
|
191186
|
+
fs23.readFileSync(this.lockPath, "utf-8").trim(),
|
|
191041
191187
|
10
|
|
191042
191188
|
);
|
|
191043
191189
|
if (!this.isProcessRunning(lockPid)) {
|
|
191044
|
-
|
|
191190
|
+
fs23.unlinkSync(this.lockPath);
|
|
191045
191191
|
continue;
|
|
191046
191192
|
}
|
|
191047
191193
|
} catch {
|
|
191048
191194
|
try {
|
|
191049
|
-
|
|
191195
|
+
fs23.unlinkSync(this.lockPath);
|
|
191050
191196
|
} catch {
|
|
191051
191197
|
}
|
|
191052
191198
|
continue;
|
|
@@ -191066,8 +191212,8 @@ var ProxyRegistryManager = class {
|
|
|
191066
191212
|
async claimProxyOwnership(key) {
|
|
191067
191213
|
const releaseLock = await this.acquireLock();
|
|
191068
191214
|
try {
|
|
191069
|
-
if (
|
|
191070
|
-
const content =
|
|
191215
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191216
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191071
191217
|
const ownerFile2 = JSON.parse(content);
|
|
191072
191218
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
191073
191219
|
return false;
|
|
@@ -191097,11 +191243,11 @@ var ProxyRegistryManager = class {
|
|
|
191097
191243
|
}
|
|
191098
191244
|
const releaseLock = await this.acquireLock();
|
|
191099
191245
|
try {
|
|
191100
|
-
if (
|
|
191101
|
-
const content =
|
|
191246
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191247
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191102
191248
|
const ownerFile = JSON.parse(content);
|
|
191103
191249
|
if (ownerFile.owner.pid === process.pid) {
|
|
191104
|
-
|
|
191250
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191105
191251
|
}
|
|
191106
191252
|
}
|
|
191107
191253
|
this.isOwner = false;
|
|
@@ -191113,12 +191259,12 @@ var ProxyRegistryManager = class {
|
|
|
191113
191259
|
* Get the current proxy owner.
|
|
191114
191260
|
*/
|
|
191115
191261
|
async getProxyOwner() {
|
|
191116
|
-
if (!
|
|
191262
|
+
if (!fs23.existsSync(this.ownerPath)) {
|
|
191117
191263
|
return null;
|
|
191118
191264
|
}
|
|
191119
191265
|
const releaseLock = await this.acquireLock();
|
|
191120
191266
|
try {
|
|
191121
|
-
const content =
|
|
191267
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191122
191268
|
const ownerFile = JSON.parse(content);
|
|
191123
191269
|
if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
|
|
191124
191270
|
return null;
|
|
@@ -191215,7 +191361,7 @@ var ProxyRegistryManager = class {
|
|
|
191215
191361
|
*/
|
|
191216
191362
|
watchRegistry(onChange) {
|
|
191217
191363
|
this.ensureProxyDir();
|
|
191218
|
-
if (!
|
|
191364
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191219
191365
|
const emptyRegistry = {
|
|
191220
191366
|
version: 1,
|
|
191221
191367
|
keys: {},
|
|
@@ -191258,13 +191404,13 @@ var ProxyRegistryManager = class {
|
|
|
191258
191404
|
async attemptElection(key) {
|
|
191259
191405
|
const releaseLock = await this.acquireLock();
|
|
191260
191406
|
try {
|
|
191261
|
-
if (
|
|
191262
|
-
const content =
|
|
191407
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191408
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191263
191409
|
const ownerFile2 = JSON.parse(content);
|
|
191264
191410
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
191265
191411
|
return false;
|
|
191266
191412
|
}
|
|
191267
|
-
|
|
191413
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191268
191414
|
}
|
|
191269
191415
|
const ownerFile = {
|
|
191270
191416
|
version: 1,
|
|
@@ -191282,7 +191428,7 @@ var ProxyRegistryManager = class {
|
|
|
191282
191428
|
}
|
|
191283
191429
|
}
|
|
191284
191430
|
readRegistry() {
|
|
191285
|
-
if (!
|
|
191431
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191286
191432
|
return {
|
|
191287
191433
|
version: 1,
|
|
191288
191434
|
keys: {},
|
|
@@ -191290,7 +191436,7 @@ var ProxyRegistryManager = class {
|
|
|
191290
191436
|
};
|
|
191291
191437
|
}
|
|
191292
191438
|
try {
|
|
191293
|
-
const content =
|
|
191439
|
+
const content = fs23.readFileSync(this.registryPath, "utf-8");
|
|
191294
191440
|
return JSON.parse(content);
|
|
191295
191441
|
} catch {
|
|
191296
191442
|
return {
|
|
@@ -191303,8 +191449,8 @@ var ProxyRegistryManager = class {
|
|
|
191303
191449
|
writeFileAtomic(filePath, data) {
|
|
191304
191450
|
this.ensureProxyDir();
|
|
191305
191451
|
const tmpPath = filePath + ".tmp";
|
|
191306
|
-
|
|
191307
|
-
|
|
191452
|
+
fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
191453
|
+
fs23.renameSync(tmpPath, filePath);
|
|
191308
191454
|
}
|
|
191309
191455
|
};
|
|
191310
191456
|
|
|
@@ -191411,9 +191557,17 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191411
191557
|
const { exit } = useApp2();
|
|
191412
191558
|
const [state, setState] = useState5(() => {
|
|
191413
191559
|
const setupDone = tunnelEnabled || !systemSetupNeeded();
|
|
191560
|
+
let setupStep;
|
|
191561
|
+
if (!setupDone) {
|
|
191562
|
+
if (os11.platform() === "darwin") {
|
|
191563
|
+
setupStep = dnsSetupNeeded() ? "setup-dns" : "setup-tls";
|
|
191564
|
+
} else {
|
|
191565
|
+
setupStep = "setup-combined";
|
|
191566
|
+
}
|
|
191567
|
+
}
|
|
191414
191568
|
return {
|
|
191415
|
-
status: setupDone ? "loading" : "
|
|
191416
|
-
...
|
|
191569
|
+
status: setupDone ? "loading" : "setup",
|
|
191570
|
+
...setupStep ? { setupStep, setupStepPhase: "prompt" } : {},
|
|
191417
191571
|
resources: /* @__PURE__ */ new Map(),
|
|
191418
191572
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
191419
191573
|
services: [],
|
|
@@ -191425,28 +191579,41 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191425
191579
|
};
|
|
191426
191580
|
});
|
|
191427
191581
|
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() {
|
|
191582
|
+
if (state.status !== "setup" || state.setupStepPhase !== "installing") return;
|
|
191438
191583
|
try {
|
|
191439
|
-
|
|
191440
|
-
|
|
191441
|
-
|
|
191584
|
+
if (state.setupStep === "setup-dns") {
|
|
191585
|
+
ensureCAGenerated();
|
|
191586
|
+
performDnsSetup();
|
|
191587
|
+
} else if (state.setupStep === "setup-tls") {
|
|
191588
|
+
ensureCAGenerated();
|
|
191589
|
+
performTlsTrust();
|
|
191590
|
+
} else if (state.setupStep === "setup-combined") {
|
|
191591
|
+
performSystemSetup();
|
|
191592
|
+
}
|
|
191593
|
+
const nextStep = state.setupStep === "setup-dns" && tlsSetupNeeded() ? "setup-tls" : void 0;
|
|
191594
|
+
if (nextStep) {
|
|
191595
|
+
setState((s) => ({
|
|
191596
|
+
...s,
|
|
191597
|
+
setupStep: nextStep,
|
|
191598
|
+
setupStepPhase: "prompt"
|
|
191599
|
+
}));
|
|
191600
|
+
} else {
|
|
191601
|
+
setState((s) => ({ ...s, status: "loading", setupStepPhase: "done" }));
|
|
191602
|
+
setReadyToStart(true);
|
|
191603
|
+
}
|
|
191442
191604
|
} catch (err) {
|
|
191443
191605
|
setState((s) => ({
|
|
191444
191606
|
...s,
|
|
191445
|
-
|
|
191446
|
-
|
|
191607
|
+
setupStepPhase: "error",
|
|
191608
|
+
setupError: err instanceof Error ? err.message : String(err)
|
|
191447
191609
|
}));
|
|
191448
191610
|
}
|
|
191449
|
-
}
|
|
191611
|
+
}, [state.status, state.setupStepPhase]);
|
|
191612
|
+
useInput4((_input, key) => {
|
|
191613
|
+
if (state.status === "setup" && state.setupStepPhase === "prompt" && key.return) {
|
|
191614
|
+
setState((s) => ({ ...s, setupStepPhase: "installing" }));
|
|
191615
|
+
}
|
|
191616
|
+
});
|
|
191450
191617
|
const shuttingDown = useRef(false);
|
|
191451
191618
|
const startTimeRef = useRef(null);
|
|
191452
191619
|
const stateManagerRef = useRef(null);
|
|
@@ -191627,7 +191794,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191627
191794
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191628
191795
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
191629
191796
|
try {
|
|
191630
|
-
const hcl =
|
|
191797
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191631
191798
|
parseConfig(hcl).then(() => {
|
|
191632
191799
|
triggerReload();
|
|
191633
191800
|
}).catch((err) => {
|
|
@@ -191753,7 +191920,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191753
191920
|
return;
|
|
191754
191921
|
}
|
|
191755
191922
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191756
|
-
if (!
|
|
191923
|
+
if (!fs24.existsSync(configPath)) {
|
|
191757
191924
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
191758
191925
|
setState((s) => ({
|
|
191759
191926
|
...s,
|
|
@@ -191771,7 +191938,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191771
191938
|
return;
|
|
191772
191939
|
}
|
|
191773
191940
|
let config2;
|
|
191774
|
-
const hcl =
|
|
191941
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191775
191942
|
try {
|
|
191776
191943
|
config2 = await parseConfig(hcl);
|
|
191777
191944
|
} catch (err) {
|
|
@@ -192098,7 +192265,7 @@ Add them to the config block in specific.local`);
|
|
|
192098
192265
|
if (service.volumes) {
|
|
192099
192266
|
for (const vol of service.volumes) {
|
|
192100
192267
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192101
|
-
|
|
192268
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192102
192269
|
volumePaths.set(vol.name, volumeDir);
|
|
192103
192270
|
}
|
|
192104
192271
|
}
|
|
@@ -192154,7 +192321,7 @@ Add them to the config block in specific.local`);
|
|
|
192154
192321
|
if (service.volumes) {
|
|
192155
192322
|
for (const vol of service.volumes) {
|
|
192156
192323
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192157
|
-
|
|
192324
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192158
192325
|
volumePaths.set(vol.name, volumeDir);
|
|
192159
192326
|
}
|
|
192160
192327
|
}
|
|
@@ -192447,15 +192614,17 @@ Add them to the config block in specific.local`);
|
|
|
192447
192614
|
});
|
|
192448
192615
|
};
|
|
192449
192616
|
}, [reloadTrigger, readyToStart, instanceKey]);
|
|
192450
|
-
if (state.status === "
|
|
192451
|
-
if (state.
|
|
192452
|
-
|
|
192617
|
+
if (state.status === "setup") {
|
|
192618
|
+
if (state.setupStepPhase === "prompt") {
|
|
192619
|
+
const isDns = state.setupStep === "setup-dns";
|
|
192620
|
+
const isTls = state.setupStep === "setup-tls";
|
|
192621
|
+
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
192622
|
}
|
|
192454
|
-
if (state.
|
|
192623
|
+
if (state.setupStepPhase === "installing") {
|
|
192455
192624
|
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
192625
|
}
|
|
192457
|
-
if (state.
|
|
192458
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.
|
|
192626
|
+
if (state.setupStepPhase === "error") {
|
|
192627
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.setupError));
|
|
192459
192628
|
}
|
|
192460
192629
|
}
|
|
192461
192630
|
if (state.status === "loading") {
|
|
@@ -192687,7 +192856,9 @@ Add them to the config block in specific.local`);
|
|
|
192687
192856
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React6.createElement(Box6, { key: item.key }, item.content)));
|
|
192688
192857
|
}
|
|
192689
192858
|
function devCommand(instanceKey, tunnelEnabled = false) {
|
|
192690
|
-
|
|
192859
|
+
const options2 = isInteractive() ? {} : { stdin: new Readable2({ read() {
|
|
192860
|
+
} }) };
|
|
192861
|
+
render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey, tunnelEnabled }), options2);
|
|
192691
192862
|
}
|
|
192692
192863
|
|
|
192693
192864
|
// src/lib/dev/git-worktree.ts
|
|
@@ -192734,12 +192905,12 @@ init_open();
|
|
|
192734
192905
|
import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
|
|
192735
192906
|
import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
|
|
192736
192907
|
import Spinner5 from "ink-spinner";
|
|
192737
|
-
import * as
|
|
192908
|
+
import * as fs26 from "fs";
|
|
192738
192909
|
import * as path23 from "path";
|
|
192739
192910
|
|
|
192740
192911
|
// src/lib/tarball/create.ts
|
|
192741
192912
|
import { execSync as execSync4 } from "child_process";
|
|
192742
|
-
import * as
|
|
192913
|
+
import * as fs25 from "fs";
|
|
192743
192914
|
import * as path22 from "path";
|
|
192744
192915
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
192745
192916
|
function isInsideGitRepository(dir) {
|
|
@@ -192797,7 +192968,7 @@ var EXCLUDED_DIRS = [
|
|
|
192797
192968
|
];
|
|
192798
192969
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
192799
192970
|
const results = [];
|
|
192800
|
-
const entries = await
|
|
192971
|
+
const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
|
|
192801
192972
|
for (const entry of entries) {
|
|
192802
192973
|
const fullPath = path22.join(currentDir, entry.name);
|
|
192803
192974
|
const relativePath = path22.relative(baseDir, fullPath);
|
|
@@ -192816,7 +192987,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
192816
192987
|
async function createTarArchive(projectDir) {
|
|
192817
192988
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
192818
192989
|
const configPath = path22.join(projectDir, "specific.hcl");
|
|
192819
|
-
if (!
|
|
192990
|
+
if (!fs25.existsSync(configPath)) {
|
|
192820
192991
|
throw new Error("specific.hcl not found in project directory");
|
|
192821
192992
|
}
|
|
192822
192993
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -193738,12 +193909,12 @@ function DeployUI({ environment, config }) {
|
|
|
193738
193909
|
}
|
|
193739
193910
|
async function deployCommand(environment) {
|
|
193740
193911
|
const configPath = path23.join(process.cwd(), "specific.hcl");
|
|
193741
|
-
if (!
|
|
193912
|
+
if (!fs26.existsSync(configPath)) {
|
|
193742
193913
|
console.error("Error: No specific.hcl found in current directory");
|
|
193743
193914
|
process.exit(1);
|
|
193744
193915
|
}
|
|
193745
193916
|
let config;
|
|
193746
|
-
const hcl =
|
|
193917
|
+
const hcl = fs26.readFileSync(configPath, "utf-8");
|
|
193747
193918
|
try {
|
|
193748
193919
|
config = await parseConfig(hcl);
|
|
193749
193920
|
} catch (err) {
|
|
@@ -193764,7 +193935,7 @@ async function deployCommand(environment) {
|
|
|
193764
193935
|
|
|
193765
193936
|
// src/commands/exec.tsx
|
|
193766
193937
|
import { spawn as spawn6 } from "child_process";
|
|
193767
|
-
import * as
|
|
193938
|
+
import * as fs27 from "fs";
|
|
193768
193939
|
import * as path24 from "path";
|
|
193769
193940
|
function startSpinner(text) {
|
|
193770
193941
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -193807,12 +193978,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
193807
193978
|
}
|
|
193808
193979
|
};
|
|
193809
193980
|
const configPath = path24.join(process.cwd(), "specific.hcl");
|
|
193810
|
-
if (!
|
|
193981
|
+
if (!fs27.existsSync(configPath)) {
|
|
193811
193982
|
console.error("Error: No specific.hcl found in current directory");
|
|
193812
193983
|
process.exit(1);
|
|
193813
193984
|
}
|
|
193814
193985
|
let config;
|
|
193815
|
-
const hcl =
|
|
193986
|
+
const hcl = fs27.readFileSync(configPath, "utf-8");
|
|
193816
193987
|
try {
|
|
193817
193988
|
config = await parseConfig(hcl);
|
|
193818
193989
|
} catch (err) {
|
|
@@ -193982,7 +194153,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
193982
194153
|
|
|
193983
194154
|
// src/commands/psql.tsx
|
|
193984
194155
|
import { spawn as spawn7 } from "child_process";
|
|
193985
|
-
import * as
|
|
194156
|
+
import * as fs28 from "fs";
|
|
193986
194157
|
import * as path25 from "path";
|
|
193987
194158
|
async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
|
|
193988
194159
|
let startedResources = [];
|
|
@@ -194001,12 +194172,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194001
194172
|
}
|
|
194002
194173
|
};
|
|
194003
194174
|
const configPath = path25.join(process.cwd(), "specific.hcl");
|
|
194004
|
-
if (!
|
|
194175
|
+
if (!fs28.existsSync(configPath)) {
|
|
194005
194176
|
console.error("Error: No specific.hcl found in current directory");
|
|
194006
194177
|
process.exit(1);
|
|
194007
194178
|
}
|
|
194008
194179
|
let config;
|
|
194009
|
-
const hcl =
|
|
194180
|
+
const hcl = fs28.readFileSync(configPath, "utf-8");
|
|
194010
194181
|
try {
|
|
194011
194182
|
config = await parseConfig(hcl);
|
|
194012
194183
|
} catch (err) {
|
|
@@ -194130,7 +194301,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194130
194301
|
|
|
194131
194302
|
// src/commands/reshape.tsx
|
|
194132
194303
|
import { spawn as spawn8 } from "child_process";
|
|
194133
|
-
import * as
|
|
194304
|
+
import * as fs29 from "fs";
|
|
194134
194305
|
import * as path26 from "path";
|
|
194135
194306
|
var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
|
|
194136
194307
|
var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
|
|
@@ -194148,8 +194319,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194148
194319
|
let migrationsDir = "migrations";
|
|
194149
194320
|
let targetDb;
|
|
194150
194321
|
try {
|
|
194151
|
-
if (
|
|
194152
|
-
const configContent =
|
|
194322
|
+
if (fs29.existsSync(configPath)) {
|
|
194323
|
+
const configContent = fs29.readFileSync(configPath, "utf-8");
|
|
194153
194324
|
config = await parseConfig(configContent);
|
|
194154
194325
|
if (databaseName) {
|
|
194155
194326
|
const postgresConfig = config.postgres.find((p) => p.name === databaseName);
|
|
@@ -194285,7 +194456,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194285
194456
|
const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
|
|
194286
194457
|
const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
|
|
194287
194458
|
if (action === "check" || action === "start") {
|
|
194288
|
-
if (
|
|
194459
|
+
if (fs29.existsSync(fullMigrationsPath)) {
|
|
194289
194460
|
reshapeArgs.push("--dirs", fullMigrationsPath);
|
|
194290
194461
|
} else if (action === "check") {
|
|
194291
194462
|
console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
|
|
@@ -194335,7 +194506,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194335
194506
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
194336
194507
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
194337
194508
|
import Spinner6 from "ink-spinner";
|
|
194338
|
-
import * as
|
|
194509
|
+
import * as fs30 from "fs";
|
|
194339
194510
|
import * as path27 from "path";
|
|
194340
194511
|
function CleanUI({ instanceKey }) {
|
|
194341
194512
|
const [state, setState] = useState7({ status: "checking" });
|
|
@@ -194343,13 +194514,13 @@ function CleanUI({ instanceKey }) {
|
|
|
194343
194514
|
async function clean() {
|
|
194344
194515
|
const projectRoot = process.cwd();
|
|
194345
194516
|
const specificDir = path27.join(projectRoot, ".specific");
|
|
194346
|
-
if (!
|
|
194517
|
+
if (!fs30.existsSync(specificDir)) {
|
|
194347
194518
|
setState({ status: "nothing" });
|
|
194348
194519
|
return;
|
|
194349
194520
|
}
|
|
194350
194521
|
if (instanceKey) {
|
|
194351
194522
|
const keyDir = path27.join(specificDir, "keys", instanceKey);
|
|
194352
|
-
if (!
|
|
194523
|
+
if (!fs30.existsSync(keyDir)) {
|
|
194353
194524
|
setState({ status: "nothing" });
|
|
194354
194525
|
return;
|
|
194355
194526
|
}
|
|
@@ -194365,7 +194536,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194365
194536
|
await stateManager.cleanStaleState();
|
|
194366
194537
|
setState({ status: "cleaning" });
|
|
194367
194538
|
try {
|
|
194368
|
-
|
|
194539
|
+
fs30.rmSync(keyDir, { recursive: true, force: true });
|
|
194369
194540
|
setState({ status: "success" });
|
|
194370
194541
|
} catch (err) {
|
|
194371
194542
|
setState({
|
|
@@ -194375,12 +194546,12 @@ function CleanUI({ instanceKey }) {
|
|
|
194375
194546
|
}
|
|
194376
194547
|
} else {
|
|
194377
194548
|
const keysDir = path27.join(specificDir, "keys");
|
|
194378
|
-
if (!
|
|
194549
|
+
if (!fs30.existsSync(keysDir)) {
|
|
194379
194550
|
setState({ status: "nothing" });
|
|
194380
194551
|
return;
|
|
194381
194552
|
}
|
|
194382
|
-
const keys =
|
|
194383
|
-
(f) =>
|
|
194553
|
+
const keys = fs30.readdirSync(keysDir).filter(
|
|
194554
|
+
(f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
|
|
194384
194555
|
);
|
|
194385
194556
|
for (const key of keys) {
|
|
194386
194557
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -194404,7 +194575,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194404
194575
|
}
|
|
194405
194576
|
setState({ status: "cleaning" });
|
|
194406
194577
|
try {
|
|
194407
|
-
|
|
194578
|
+
fs30.rmSync(keysDir, { recursive: true, force: true });
|
|
194408
194579
|
setState({ status: "success" });
|
|
194409
194580
|
} catch (err) {
|
|
194410
194581
|
setState({
|
|
@@ -194569,7 +194740,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
|
|
|
194569
194740
|
import Spinner7 from "ink-spinner";
|
|
194570
194741
|
|
|
194571
194742
|
// src/lib/update.ts
|
|
194572
|
-
import * as
|
|
194743
|
+
import * as fs31 from "fs";
|
|
194573
194744
|
import * as path28 from "path";
|
|
194574
194745
|
var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
|
|
194575
194746
|
function compareVersions(a, b) {
|
|
@@ -194584,7 +194755,7 @@ function compareVersions(a, b) {
|
|
|
194584
194755
|
return 0;
|
|
194585
194756
|
}
|
|
194586
194757
|
async function checkForUpdate() {
|
|
194587
|
-
const currentVersion = "0.1.
|
|
194758
|
+
const currentVersion = "0.1.83";
|
|
194588
194759
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
194589
194760
|
if (!response.ok) {
|
|
194590
194761
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -194600,7 +194771,7 @@ function isBinaryWritable() {
|
|
|
194600
194771
|
const binaryPath = getCurrentBinaryPath();
|
|
194601
194772
|
const dir = path28.dirname(binaryPath);
|
|
194602
194773
|
try {
|
|
194603
|
-
|
|
194774
|
+
fs31.accessSync(dir, fs31.constants.W_OK);
|
|
194604
194775
|
return true;
|
|
194605
194776
|
} catch {
|
|
194606
194777
|
return false;
|
|
@@ -194611,21 +194782,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194611
194782
|
const binaryDir = path28.dirname(binaryPath);
|
|
194612
194783
|
const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
|
|
194613
194784
|
try {
|
|
194614
|
-
const { platform:
|
|
194615
|
-
const url = `${BINARIES_BASE_URL}/${version}/specific-${
|
|
194785
|
+
const { platform: platform8, arch: arch3 } = getPlatformInfo();
|
|
194786
|
+
const url = `${BINARIES_BASE_URL}/${version}/specific-${platform8}-${arch3}`;
|
|
194616
194787
|
await downloadFile(url, tempPath, onProgress);
|
|
194617
|
-
const stat4 =
|
|
194788
|
+
const stat4 = fs31.statSync(tempPath);
|
|
194618
194789
|
if (stat4.size === 0) {
|
|
194619
194790
|
throw new Error("Downloaded binary is empty");
|
|
194620
194791
|
}
|
|
194621
|
-
|
|
194792
|
+
fs31.chmodSync(tempPath, 493);
|
|
194622
194793
|
onProgress?.({ phase: "finalizing" });
|
|
194623
|
-
|
|
194624
|
-
|
|
194794
|
+
fs31.unlinkSync(binaryPath);
|
|
194795
|
+
fs31.renameSync(tempPath, binaryPath);
|
|
194625
194796
|
} catch (error) {
|
|
194626
194797
|
try {
|
|
194627
|
-
if (
|
|
194628
|
-
|
|
194798
|
+
if (fs31.existsSync(tempPath)) {
|
|
194799
|
+
fs31.unlinkSync(tempPath);
|
|
194629
194800
|
}
|
|
194630
194801
|
} catch {
|
|
194631
194802
|
}
|
|
@@ -194635,21 +194806,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194635
194806
|
|
|
194636
194807
|
// src/lib/background-update.ts
|
|
194637
194808
|
import { spawn as spawn9 } from "child_process";
|
|
194638
|
-
import * as
|
|
194809
|
+
import * as fs32 from "fs";
|
|
194639
194810
|
import * as path29 from "path";
|
|
194640
|
-
import * as
|
|
194641
|
-
var SPECIFIC_DIR = path29.join(
|
|
194811
|
+
import * as os12 from "os";
|
|
194812
|
+
var SPECIFIC_DIR = path29.join(os12.homedir(), ".specific");
|
|
194642
194813
|
var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
|
|
194643
194814
|
var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
|
|
194644
194815
|
var RATE_LIMIT_MS = 60 * 60 * 1e3;
|
|
194645
194816
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
194646
194817
|
function writeCheckTimestamp() {
|
|
194647
|
-
|
|
194648
|
-
|
|
194818
|
+
fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
|
|
194819
|
+
fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
|
|
194649
194820
|
}
|
|
194650
194821
|
function isRateLimited() {
|
|
194651
194822
|
try {
|
|
194652
|
-
const content =
|
|
194823
|
+
const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
|
|
194653
194824
|
const lastCheck = parseInt(content, 10);
|
|
194654
194825
|
if (isNaN(lastCheck)) return false;
|
|
194655
194826
|
return Date.now() - lastCheck < RATE_LIMIT_MS;
|
|
@@ -194783,7 +194954,7 @@ function updateCommand() {
|
|
|
194783
194954
|
var program = new Command();
|
|
194784
194955
|
var env = "production";
|
|
194785
194956
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
194786
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
194957
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.83").enablePositionalOptions();
|
|
194787
194958
|
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
194959
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
194789
194960
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|