@specific.dev/cli 0.1.71 → 0.1.73
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 +2 -2
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/__next._full.txt +8 -8
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +3 -3
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_next/static/chunks/4bd160f783f6ccf1.js +1 -0
- package/dist/admin/_not-found/__next._full.txt +3 -3
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +3 -3
- 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 +3 -3
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/databases/__next._full.txt +8 -8
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +3 -3
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +8 -8
- package/dist/admin/fullscreen/__next._full.txt +4 -4
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +3 -3
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +4 -4
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +3 -3
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
- 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 +4 -4
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +4 -4
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +8 -8
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +2 -2
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/mail/__next._full.txt +8 -8
- package/dist/admin/mail/__next._head.txt +1 -1
- package/dist/admin/mail/__next._index.txt +3 -3
- package/dist/admin/mail/__next._tree.txt +1 -1
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +8 -8
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +8 -8
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +3 -3
- package/dist/admin/workflows/__next._tree.txt +1 -1
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +8 -8
- package/dist/cli.js +343 -279
- package/dist/postinstall.js +1 -1
- package/package.json +1 -1
- package/dist/admin/_next/static/chunks/47a5dab862795de7.js +0 -1
- /package/dist/admin/_next/static/{3nqvPNOX7a-_9HydleH6d → 2GhRJUspcGN_TKmFF6ihm}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{3nqvPNOX7a-_9HydleH6d → 2GhRJUspcGN_TKmFF6ihm}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{3nqvPNOX7a-_9HydleH6d → 2GhRJUspcGN_TKmFF6ihm}/_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
183435
|
|
|
183436
183436
|
// src/lib/dev/system-setup.ts
|
|
183437
183437
|
import { execSync as execSync2 } from "child_process";
|
|
183438
|
+
import * as fs4 from "fs";
|
|
183439
|
+
import * as os4 from "os";
|
|
183440
|
+
import * as path3 from "path";
|
|
183438
183441
|
|
|
183439
183442
|
// src/lib/dev/local-ca.ts
|
|
183440
183443
|
import * as fs from "fs";
|
|
@@ -183445,25 +183448,41 @@ import { execSync } from "child_process";
|
|
|
183445
183448
|
function getCADir() {
|
|
183446
183449
|
return path.join(os.homedir(), ".specific", "root_ca");
|
|
183447
183450
|
}
|
|
183451
|
+
var EXPECTED_DOMAIN = "spcf.localhost";
|
|
183448
183452
|
function caFilesExist() {
|
|
183449
183453
|
const caDir = getCADir();
|
|
183450
|
-
|
|
183454
|
+
const keyPath = path.join(caDir, "ca.key");
|
|
183455
|
+
const certPath = path.join(caDir, "ca.crt");
|
|
183456
|
+
if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
|
|
183457
|
+
return false;
|
|
183458
|
+
}
|
|
183459
|
+
try {
|
|
183460
|
+
const certPem = fs.readFileSync(certPath, "utf-8");
|
|
183461
|
+
const cert = forge.pki.certificateFromPem(certPem);
|
|
183462
|
+
const nameConstraints = cert.getExtension("nameConstraints");
|
|
183463
|
+
if (!nameConstraints) {
|
|
183464
|
+
return false;
|
|
183465
|
+
}
|
|
183466
|
+
return nameConstraints.value.includes(EXPECTED_DOMAIN);
|
|
183467
|
+
} catch {
|
|
183468
|
+
return false;
|
|
183469
|
+
}
|
|
183451
183470
|
}
|
|
183452
183471
|
function caInstalledInTrustStore() {
|
|
183453
183472
|
if (!caFilesExist()) {
|
|
183454
183473
|
return false;
|
|
183455
183474
|
}
|
|
183456
|
-
const
|
|
183475
|
+
const platform6 = os.platform();
|
|
183457
183476
|
const certPath = path.join(getCADir(), "ca.crt");
|
|
183458
183477
|
const diskCert = fs.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
|
|
183459
183478
|
try {
|
|
183460
|
-
if (
|
|
183479
|
+
if (platform6 === "darwin") {
|
|
183461
183480
|
const keychainCert = execSync(
|
|
183462
183481
|
'security find-certificate -c "Specific Local Development CA" -p /Library/Keychains/System.keychain',
|
|
183463
183482
|
{ encoding: "utf-8" }
|
|
183464
183483
|
).replace(/\r\n/g, "\n").trim();
|
|
183465
183484
|
return keychainCert === diskCert;
|
|
183466
|
-
} else if (
|
|
183485
|
+
} else if (platform6 === "linux") {
|
|
183467
183486
|
const trustPaths = [
|
|
183468
183487
|
"/usr/local/share/ca-certificates/specific-local-ca.crt",
|
|
183469
183488
|
"/etc/pki/ca-trust/source/anchors/specific-local-ca.crt"
|
|
@@ -183536,8 +183555,8 @@ function generateRootCA() {
|
|
|
183536
183555
|
cert.setSubject(attrs);
|
|
183537
183556
|
cert.setIssuer(attrs);
|
|
183538
183557
|
const nameConstraintsExt = createNameConstraintsExtension([
|
|
183539
|
-
".
|
|
183540
|
-
"
|
|
183558
|
+
".spcf.localhost",
|
|
183559
|
+
"spcf.localhost"
|
|
183541
183560
|
]);
|
|
183542
183561
|
cert.setExtensions([
|
|
183543
183562
|
{ name: "basicConstraints", cA: true, critical: true },
|
|
@@ -183571,15 +183590,15 @@ function removeCA() {
|
|
|
183571
183590
|
}
|
|
183572
183591
|
}
|
|
183573
183592
|
function getCAInstallCommands(certPath) {
|
|
183574
|
-
const
|
|
183575
|
-
if (
|
|
183593
|
+
const platform6 = os.platform();
|
|
183594
|
+
if (platform6 === "darwin") {
|
|
183576
183595
|
return [
|
|
183577
183596
|
// Remove any existing cert with the same CN first — add-trusted-cert
|
|
183578
183597
|
// silently does nothing if a cert with the same subject already exists.
|
|
183579
183598
|
'security delete-certificate -c "Specific Local Development CA" /Library/Keychains/System.keychain 2>/dev/null || true',
|
|
183580
183599
|
`security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`
|
|
183581
183600
|
];
|
|
183582
|
-
} else if (
|
|
183601
|
+
} else if (platform6 === "linux") {
|
|
183583
183602
|
if (fs.existsSync("/usr/local/share/ca-certificates")) {
|
|
183584
183603
|
return [
|
|
183585
183604
|
`cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
|
|
@@ -183593,7 +183612,7 @@ function getCAInstallCommands(certPath) {
|
|
|
183593
183612
|
}
|
|
183594
183613
|
throw new Error("Could not detect Linux certificate trust mechanism");
|
|
183595
183614
|
}
|
|
183596
|
-
throw new Error(`Unsupported platform: ${
|
|
183615
|
+
throw new Error(`Unsupported platform: ${platform6}`);
|
|
183597
183616
|
}
|
|
183598
183617
|
function generateCertificate(domain, keys = []) {
|
|
183599
183618
|
const caDir = getCADir();
|
|
@@ -183635,29 +183654,31 @@ function generateCertificate(domain, keys = []) {
|
|
|
183635
183654
|
// src/lib/dev/resolver-config.ts
|
|
183636
183655
|
import * as fs2 from "fs";
|
|
183637
183656
|
import * as os2 from "os";
|
|
183638
|
-
var RESOLVER_FILE_MACOS = "/etc/resolver/
|
|
183657
|
+
var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
|
|
183658
|
+
var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
|
|
183639
183659
|
var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
|
|
183640
183660
|
function resolverConfigExists() {
|
|
183641
|
-
const
|
|
183642
|
-
if (
|
|
183661
|
+
const platform6 = os2.platform();
|
|
183662
|
+
if (platform6 === "darwin") {
|
|
183643
183663
|
return fs2.existsSync(RESOLVER_FILE_MACOS);
|
|
183644
|
-
} else if (
|
|
183664
|
+
} else if (platform6 === "linux") {
|
|
183645
183665
|
return fs2.existsSync(RESOLVER_FILE_LINUX);
|
|
183646
183666
|
}
|
|
183647
183667
|
return false;
|
|
183648
183668
|
}
|
|
183649
183669
|
function getResolverInstallCommands(port) {
|
|
183650
|
-
const
|
|
183651
|
-
if (
|
|
183670
|
+
const platform6 = os2.platform();
|
|
183671
|
+
if (platform6 === "darwin") {
|
|
183652
183672
|
return [
|
|
183653
183673
|
"mkdir -p /etc/resolver",
|
|
183674
|
+
`rm -f ${OLD_RESOLVER_FILE_MACOS}`,
|
|
183654
183675
|
`printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
|
|
183655
183676
|
];
|
|
183656
|
-
} else if (
|
|
183677
|
+
} else if (platform6 === "linux") {
|
|
183657
183678
|
if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
|
|
183658
183679
|
return [
|
|
183659
183680
|
"mkdir -p /etc/systemd/resolved.conf.d",
|
|
183660
|
-
`printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~
|
|
183681
|
+
`printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~spcf.localhost\\n" > ${RESOLVER_FILE_LINUX}`,
|
|
183661
183682
|
"systemctl restart systemd-resolved"
|
|
183662
183683
|
];
|
|
183663
183684
|
}
|
|
@@ -183746,7 +183767,6 @@ async function startDnsServer(port = DNS_PORT) {
|
|
|
183746
183767
|
}
|
|
183747
183768
|
|
|
183748
183769
|
// src/lib/dev/system-setup.ts
|
|
183749
|
-
import * as path3 from "path";
|
|
183750
183770
|
function systemSetupNeeded() {
|
|
183751
183771
|
return !caInstalledInTrustStore() || !resolverConfigExists();
|
|
183752
183772
|
}
|
|
@@ -183773,7 +183793,7 @@ function performSystemSetup() {
|
|
|
183773
183793
|
return;
|
|
183774
183794
|
}
|
|
183775
183795
|
try {
|
|
183776
|
-
|
|
183796
|
+
execPrivileged(commands);
|
|
183777
183797
|
} catch (err) {
|
|
183778
183798
|
if (needsGenerate) {
|
|
183779
183799
|
removeCA();
|
|
@@ -183781,14 +183801,43 @@ function performSystemSetup() {
|
|
|
183781
183801
|
throw err;
|
|
183782
183802
|
}
|
|
183783
183803
|
}
|
|
183804
|
+
function execPrivileged(commands) {
|
|
183805
|
+
if (commands.length === 0) return;
|
|
183806
|
+
if (os4.platform() === "darwin") {
|
|
183807
|
+
const scriptPath = path3.join(os4.tmpdir(), "specific-setup.sh");
|
|
183808
|
+
const appPath = path3.join(os4.tmpdir(), "Specific.app");
|
|
183809
|
+
try {
|
|
183810
|
+
fs4.writeFileSync(
|
|
183811
|
+
scriptPath,
|
|
183812
|
+
"#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
|
|
183813
|
+
{ mode: 448 }
|
|
183814
|
+
);
|
|
183815
|
+
execSync2(
|
|
183816
|
+
`osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
|
|
183817
|
+
);
|
|
183818
|
+
execSync2(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
|
|
183819
|
+
} finally {
|
|
183820
|
+
try {
|
|
183821
|
+
fs4.unlinkSync(scriptPath);
|
|
183822
|
+
} catch {
|
|
183823
|
+
}
|
|
183824
|
+
try {
|
|
183825
|
+
fs4.rmSync(appPath, { recursive: true });
|
|
183826
|
+
} catch {
|
|
183827
|
+
}
|
|
183828
|
+
}
|
|
183829
|
+
} else {
|
|
183830
|
+
execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
|
|
183831
|
+
}
|
|
183832
|
+
}
|
|
183784
183833
|
|
|
183785
183834
|
// src/lib/analytics/index.ts
|
|
183786
183835
|
import { PostHog } from "posthog-node";
|
|
183787
|
-
import * as
|
|
183836
|
+
import * as os7 from "os";
|
|
183788
183837
|
import * as crypto from "crypto";
|
|
183789
183838
|
|
|
183790
183839
|
// src/lib/project/config.ts
|
|
183791
|
-
import * as
|
|
183840
|
+
import * as fs5 from "fs";
|
|
183792
183841
|
import * as path4 from "path";
|
|
183793
183842
|
var PROJECT_ID_FILE = ".specific/project_id";
|
|
183794
183843
|
var ProjectNotLinkedError = class extends Error {
|
|
@@ -183805,10 +183854,10 @@ Run: specific deploy`
|
|
|
183805
183854
|
};
|
|
183806
183855
|
function readProjectId(projectDir = process.cwd()) {
|
|
183807
183856
|
const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
|
|
183808
|
-
if (!
|
|
183857
|
+
if (!fs5.existsSync(projectIdPath)) {
|
|
183809
183858
|
throw new ProjectNotLinkedError();
|
|
183810
183859
|
}
|
|
183811
|
-
const projectId =
|
|
183860
|
+
const projectId = fs5.readFileSync(projectIdPath, "utf-8").trim();
|
|
183812
183861
|
if (!projectId) {
|
|
183813
183862
|
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
183814
183863
|
}
|
|
@@ -183816,20 +183865,20 @@ function readProjectId(projectDir = process.cwd()) {
|
|
|
183816
183865
|
}
|
|
183817
183866
|
function hasProjectId(projectDir = process.cwd()) {
|
|
183818
183867
|
const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
|
|
183819
|
-
return
|
|
183868
|
+
return fs5.existsSync(projectIdPath);
|
|
183820
183869
|
}
|
|
183821
183870
|
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
183822
183871
|
const specificDir = path4.join(projectDir, ".specific");
|
|
183823
|
-
if (!
|
|
183824
|
-
|
|
183872
|
+
if (!fs5.existsSync(specificDir)) {
|
|
183873
|
+
fs5.mkdirSync(specificDir, { recursive: true });
|
|
183825
183874
|
}
|
|
183826
|
-
|
|
183875
|
+
fs5.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
|
|
183827
183876
|
}
|
|
183828
183877
|
|
|
183829
183878
|
// src/lib/auth/credentials.ts
|
|
183830
|
-
import * as
|
|
183879
|
+
import * as fs11 from "fs";
|
|
183831
183880
|
import * as path6 from "path";
|
|
183832
|
-
import * as
|
|
183881
|
+
import * as os6 from "os";
|
|
183833
183882
|
|
|
183834
183883
|
// src/lib/auth/errors.ts
|
|
183835
183884
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -184382,18 +184431,18 @@ var ApiClient = class {
|
|
|
184382
184431
|
|
|
184383
184432
|
// src/lib/auth/credentials.ts
|
|
184384
184433
|
function getUserCredentialsDir() {
|
|
184385
|
-
return path6.join(
|
|
184434
|
+
return path6.join(os6.homedir(), ".specific");
|
|
184386
184435
|
}
|
|
184387
184436
|
function getCredentialsPath() {
|
|
184388
184437
|
return path6.join(getUserCredentialsDir(), "credentials.json");
|
|
184389
184438
|
}
|
|
184390
184439
|
function readUserCredentials() {
|
|
184391
184440
|
const credentialsPath = getCredentialsPath();
|
|
184392
|
-
if (!
|
|
184441
|
+
if (!fs11.existsSync(credentialsPath)) {
|
|
184393
184442
|
return null;
|
|
184394
184443
|
}
|
|
184395
184444
|
try {
|
|
184396
|
-
const content =
|
|
184445
|
+
const content = fs11.readFileSync(credentialsPath, "utf-8");
|
|
184397
184446
|
return JSON.parse(content);
|
|
184398
184447
|
} catch {
|
|
184399
184448
|
return null;
|
|
@@ -184401,18 +184450,18 @@ function readUserCredentials() {
|
|
|
184401
184450
|
}
|
|
184402
184451
|
function writeUserCredentials(credentials) {
|
|
184403
184452
|
const dir = getUserCredentialsDir();
|
|
184404
|
-
if (!
|
|
184405
|
-
|
|
184453
|
+
if (!fs11.existsSync(dir)) {
|
|
184454
|
+
fs11.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
184406
184455
|
}
|
|
184407
184456
|
const credentialsPath = getCredentialsPath();
|
|
184408
|
-
|
|
184457
|
+
fs11.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
184409
184458
|
mode: 384
|
|
184410
184459
|
});
|
|
184411
184460
|
}
|
|
184412
184461
|
function clearUserCredentials() {
|
|
184413
184462
|
const credentialsPath = getCredentialsPath();
|
|
184414
|
-
if (
|
|
184415
|
-
|
|
184463
|
+
if (fs11.existsSync(credentialsPath)) {
|
|
184464
|
+
fs11.unlinkSync(credentialsPath);
|
|
184416
184465
|
}
|
|
184417
184466
|
}
|
|
184418
184467
|
function isLoggedIn() {
|
|
@@ -184491,7 +184540,7 @@ function isEnabled() {
|
|
|
184491
184540
|
}
|
|
184492
184541
|
function getAnonymousId() {
|
|
184493
184542
|
if (anonymousId) return anonymousId;
|
|
184494
|
-
const machineId = `${
|
|
184543
|
+
const machineId = `${os7.hostname()}-${os7.userInfo().username}`;
|
|
184495
184544
|
anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
|
|
184496
184545
|
return anonymousId;
|
|
184497
184546
|
}
|
|
@@ -184538,7 +184587,7 @@ function trackEvent(event, properties) {
|
|
|
184538
184587
|
event,
|
|
184539
184588
|
properties: {
|
|
184540
184589
|
...properties,
|
|
184541
|
-
cli_version: "0.1.
|
|
184590
|
+
cli_version: "0.1.73",
|
|
184542
184591
|
platform: process.platform,
|
|
184543
184592
|
node_version: process.version,
|
|
184544
184593
|
project_id: getProjectId()
|
|
@@ -184575,57 +184624,57 @@ var options = [
|
|
|
184575
184624
|
];
|
|
184576
184625
|
function isGitProject() {
|
|
184577
184626
|
const gitPath = path7.join(process.cwd(), ".git");
|
|
184578
|
-
return
|
|
184627
|
+
return fs12.existsSync(gitPath);
|
|
184579
184628
|
}
|
|
184580
184629
|
function detectExistingAgents() {
|
|
184581
184630
|
const detected = {};
|
|
184582
184631
|
const cursorDir = path7.join(process.cwd(), ".cursor");
|
|
184583
|
-
if (
|
|
184632
|
+
if (fs12.existsSync(cursorDir)) {
|
|
184584
184633
|
detected["cursor"] = true;
|
|
184585
184634
|
}
|
|
184586
184635
|
const claudeDir = path7.join(process.cwd(), ".claude");
|
|
184587
184636
|
const claudeMd = path7.join(process.cwd(), "CLAUDE.md");
|
|
184588
|
-
if (
|
|
184637
|
+
if (fs12.existsSync(claudeDir) || fs12.existsSync(claudeMd)) {
|
|
184589
184638
|
detected["claude"] = true;
|
|
184590
184639
|
}
|
|
184591
184640
|
const agentsMd = path7.join(process.cwd(), "AGENTS.md");
|
|
184592
|
-
if (
|
|
184641
|
+
if (fs12.existsSync(agentsMd)) {
|
|
184593
184642
|
detected["codex"] = true;
|
|
184594
184643
|
}
|
|
184595
184644
|
return detected;
|
|
184596
184645
|
}
|
|
184597
184646
|
function appendOrCreateFile(filePath, content) {
|
|
184598
|
-
if (
|
|
184599
|
-
const existing =
|
|
184647
|
+
if (fs12.existsSync(filePath)) {
|
|
184648
|
+
const existing = fs12.readFileSync(filePath, "utf-8");
|
|
184600
184649
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
184601
184650
|
return "unchanged";
|
|
184602
184651
|
}
|
|
184603
184652
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
184604
|
-
|
|
184653
|
+
fs12.writeFileSync(filePath, existing + separator + content + "\n");
|
|
184605
184654
|
return "modified";
|
|
184606
184655
|
} else {
|
|
184607
|
-
|
|
184656
|
+
fs12.writeFileSync(filePath, content + "\n");
|
|
184608
184657
|
return "created";
|
|
184609
184658
|
}
|
|
184610
184659
|
}
|
|
184611
184660
|
function addToGitignore() {
|
|
184612
184661
|
const gitignorePath = path7.join(process.cwd(), ".gitignore");
|
|
184613
184662
|
const entries = [".specific", "specific.local"];
|
|
184614
|
-
if (
|
|
184615
|
-
const existing =
|
|
184663
|
+
if (fs12.existsSync(gitignorePath)) {
|
|
184664
|
+
const existing = fs12.readFileSync(gitignorePath, "utf-8");
|
|
184616
184665
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
184617
184666
|
const missingEntries = entries.filter((entry) => !lines.includes(entry));
|
|
184618
184667
|
if (missingEntries.length === 0) {
|
|
184619
184668
|
return "unchanged";
|
|
184620
184669
|
}
|
|
184621
184670
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
184622
|
-
|
|
184671
|
+
fs12.writeFileSync(
|
|
184623
184672
|
gitignorePath,
|
|
184624
184673
|
existing + separator + missingEntries.join("\n") + "\n"
|
|
184625
184674
|
);
|
|
184626
184675
|
return "modified";
|
|
184627
184676
|
} else {
|
|
184628
|
-
|
|
184677
|
+
fs12.writeFileSync(gitignorePath, entries.join("\n") + "\n");
|
|
184629
184678
|
return "created";
|
|
184630
184679
|
}
|
|
184631
184680
|
}
|
|
@@ -184633,8 +184682,8 @@ function configureClaudeCodePermissions() {
|
|
|
184633
184682
|
const claudeDir = path7.join(process.cwd(), ".claude");
|
|
184634
184683
|
const settingsPath = path7.join(claudeDir, "settings.local.json");
|
|
184635
184684
|
const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
|
|
184636
|
-
if (
|
|
184637
|
-
const existing = JSON.parse(
|
|
184685
|
+
if (fs12.existsSync(settingsPath)) {
|
|
184686
|
+
const existing = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
|
|
184638
184687
|
const allowList = existing?.permissions?.allow || [];
|
|
184639
184688
|
const missingPermissions = permissions.filter(
|
|
184640
184689
|
(p) => !allowList.includes(p)
|
|
@@ -184649,39 +184698,39 @@ function configureClaudeCodePermissions() {
|
|
|
184649
184698
|
existing.permissions.allow = [];
|
|
184650
184699
|
}
|
|
184651
184700
|
existing.permissions.allow.push(...missingPermissions);
|
|
184652
|
-
|
|
184701
|
+
fs12.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
|
|
184653
184702
|
return "modified";
|
|
184654
184703
|
}
|
|
184655
|
-
if (!
|
|
184656
|
-
|
|
184704
|
+
if (!fs12.existsSync(claudeDir)) {
|
|
184705
|
+
fs12.mkdirSync(claudeDir);
|
|
184657
184706
|
}
|
|
184658
184707
|
const settings = {
|
|
184659
184708
|
permissions: {
|
|
184660
184709
|
allow: permissions
|
|
184661
184710
|
}
|
|
184662
184711
|
};
|
|
184663
|
-
|
|
184712
|
+
fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
184664
184713
|
return "created";
|
|
184665
184714
|
}
|
|
184666
184715
|
function createCursorRule() {
|
|
184667
184716
|
const cursorDir = path7.join(process.cwd(), ".cursor");
|
|
184668
184717
|
const rulesDir = path7.join(cursorDir, "rules");
|
|
184669
184718
|
const mdcPath = path7.join(rulesDir, "specific.mdc");
|
|
184670
|
-
if (
|
|
184671
|
-
const existing =
|
|
184719
|
+
if (fs12.existsSync(mdcPath)) {
|
|
184720
|
+
const existing = fs12.readFileSync(mdcPath, "utf-8");
|
|
184672
184721
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
184673
184722
|
return "unchanged";
|
|
184674
184723
|
}
|
|
184675
|
-
|
|
184724
|
+
fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
184676
184725
|
return "modified";
|
|
184677
184726
|
}
|
|
184678
|
-
if (!
|
|
184679
|
-
|
|
184727
|
+
if (!fs12.existsSync(cursorDir)) {
|
|
184728
|
+
fs12.mkdirSync(cursorDir);
|
|
184680
184729
|
}
|
|
184681
|
-
if (!
|
|
184682
|
-
|
|
184730
|
+
if (!fs12.existsSync(rulesDir)) {
|
|
184731
|
+
fs12.mkdirSync(rulesDir);
|
|
184683
184732
|
}
|
|
184684
|
-
|
|
184733
|
+
fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
184685
184734
|
return "created";
|
|
184686
184735
|
}
|
|
184687
184736
|
function configureAgents(checked) {
|
|
@@ -184736,7 +184785,7 @@ function InitUI() {
|
|
|
184736
184785
|
const [phase, setPhase] = useState(
|
|
184737
184786
|
() => !systemSetupNeeded() ? "agents" : "installing-ca"
|
|
184738
184787
|
);
|
|
184739
|
-
const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "
|
|
184788
|
+
const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "prompt");
|
|
184740
184789
|
const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
|
|
184741
184790
|
const [checked, setChecked] = useState(
|
|
184742
184791
|
initialState.detected
|
|
@@ -184767,6 +184816,10 @@ function InitUI() {
|
|
|
184767
184816
|
}
|
|
184768
184817
|
const isSubmitFocused = focusedIndex === options.length;
|
|
184769
184818
|
useInput((input, key) => {
|
|
184819
|
+
if (phase === "installing-ca" && caInstallPhase === "prompt" && key.return) {
|
|
184820
|
+
setCaInstallPhase("installing");
|
|
184821
|
+
return;
|
|
184822
|
+
}
|
|
184770
184823
|
if (phase !== "agents") return;
|
|
184771
184824
|
if (key.upArrow || input === "k") {
|
|
184772
184825
|
setFocusedIndex((prev) => Math.max(0, prev - 1));
|
|
@@ -184796,8 +184849,11 @@ function InitUI() {
|
|
|
184796
184849
|
}
|
|
184797
184850
|
}, [phase, exit]);
|
|
184798
184851
|
if (phase === "installing-ca") {
|
|
184852
|
+
if (caInstallPhase === "prompt") {
|
|
184853
|
+
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, "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."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
|
|
184854
|
+
}
|
|
184799
184855
|
if (caInstallPhase === "installing") {
|
|
184800
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "
|
|
184856
|
+
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"));
|
|
184801
184857
|
}
|
|
184802
184858
|
}
|
|
184803
184859
|
if (phase === "done") {
|
|
@@ -184917,7 +184973,7 @@ var BETA_REGISTRY = [
|
|
|
184917
184973
|
];
|
|
184918
184974
|
|
|
184919
184975
|
// src/lib/beta/storage.ts
|
|
184920
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
184976
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
184921
184977
|
import { join as join7 } from "path";
|
|
184922
184978
|
var BETAS_FILE = ".specific/betas.json";
|
|
184923
184979
|
function loadEnabledBetas(projectDir = process.cwd()) {
|
|
@@ -184950,7 +185006,7 @@ function saveBetas(enabled, projectDir) {
|
|
|
184950
185006
|
mkdirSync6(specificDir, { recursive: true });
|
|
184951
185007
|
}
|
|
184952
185008
|
const data = { enabled };
|
|
184953
|
-
|
|
185009
|
+
writeFileSync6(
|
|
184954
185010
|
join7(projectDir, BETAS_FILE),
|
|
184955
185011
|
JSON.stringify(data, null, 2) + "\n"
|
|
184956
185012
|
);
|
|
@@ -185017,7 +185073,7 @@ function resolveFilesystemDoc(path30) {
|
|
|
185017
185073
|
import React3, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
185018
185074
|
import { render as render3, Text as Text3, Box as Box3 } from "ink";
|
|
185019
185075
|
import Spinner3 from "ink-spinner";
|
|
185020
|
-
import * as
|
|
185076
|
+
import * as fs14 from "fs";
|
|
185021
185077
|
import * as path9 from "path";
|
|
185022
185078
|
import { execFile as execFile7 } from "child_process";
|
|
185023
185079
|
|
|
@@ -185889,32 +185945,32 @@ var ExtractionError = class extends Error {
|
|
|
185889
185945
|
};
|
|
185890
185946
|
|
|
185891
185947
|
// src/lib/bin/manager.ts
|
|
185892
|
-
import * as
|
|
185948
|
+
import * as fs13 from "fs";
|
|
185893
185949
|
import * as path8 from "path";
|
|
185894
|
-
import * as
|
|
185950
|
+
import * as os8 from "os";
|
|
185895
185951
|
import { createReadStream } from "fs";
|
|
185896
185952
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185897
185953
|
function getLibraryEnv(binary) {
|
|
185898
185954
|
if (!binary.libraryPath) {
|
|
185899
185955
|
return {};
|
|
185900
185956
|
}
|
|
185901
|
-
const
|
|
185902
|
-
if (
|
|
185957
|
+
const platform6 = os8.platform();
|
|
185958
|
+
if (platform6 === "darwin") {
|
|
185903
185959
|
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
185904
|
-
} else if (
|
|
185960
|
+
} else if (platform6 === "linux") {
|
|
185905
185961
|
return { LD_LIBRARY_PATH: binary.libraryPath };
|
|
185906
185962
|
}
|
|
185907
185963
|
return {};
|
|
185908
185964
|
}
|
|
185909
185965
|
function getBinBaseDir() {
|
|
185910
|
-
return path8.join(
|
|
185966
|
+
return path8.join(os8.homedir(), ".specific", "bin");
|
|
185911
185967
|
}
|
|
185912
185968
|
function getPlatformInfo() {
|
|
185913
|
-
const
|
|
185914
|
-
const arch3 =
|
|
185915
|
-
if (
|
|
185969
|
+
const platform6 = os8.platform();
|
|
185970
|
+
const arch3 = os8.arch();
|
|
185971
|
+
if (platform6 !== "darwin" && platform6 !== "linux") {
|
|
185916
185972
|
throw new Error(
|
|
185917
|
-
`Unsupported platform: ${
|
|
185973
|
+
`Unsupported platform: ${platform6}. Only macOS and Linux are supported.`
|
|
185918
185974
|
);
|
|
185919
185975
|
}
|
|
185920
185976
|
const archStr = arch3;
|
|
@@ -185928,7 +185984,7 @@ function getPlatformInfo() {
|
|
|
185928
185984
|
`Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
|
|
185929
185985
|
);
|
|
185930
185986
|
}
|
|
185931
|
-
return { platform:
|
|
185987
|
+
return { platform: platform6, arch: mappedArch };
|
|
185932
185988
|
}
|
|
185933
185989
|
function getBinaryDir(definition, version, platformInfo) {
|
|
185934
185990
|
return path8.join(
|
|
@@ -185942,7 +185998,7 @@ function isBinaryInstalled(definition, version, platformInfo) {
|
|
|
185942
185998
|
const binDir = getBinaryDir(definition, version, platformInfo);
|
|
185943
185999
|
for (const execPath of definition.executables) {
|
|
185944
186000
|
const fullPath = path8.join(binDir, execPath);
|
|
185945
|
-
if (!
|
|
186001
|
+
if (!fs13.existsSync(fullPath)) {
|
|
185946
186002
|
return false;
|
|
185947
186003
|
}
|
|
185948
186004
|
}
|
|
@@ -185971,11 +186027,11 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185971
186027
|
);
|
|
185972
186028
|
let bytesDownloaded = 0;
|
|
185973
186029
|
const parentDir = path8.dirname(destPath);
|
|
185974
|
-
if (!
|
|
185975
|
-
|
|
186030
|
+
if (!fs13.existsSync(parentDir)) {
|
|
186031
|
+
fs13.mkdirSync(parentDir, { recursive: true });
|
|
185976
186032
|
}
|
|
185977
186033
|
const partPath = destPath + ".part";
|
|
185978
|
-
const fileStream =
|
|
186034
|
+
const fileStream = fs13.createWriteStream(partPath);
|
|
185979
186035
|
try {
|
|
185980
186036
|
const reader = response.body.getReader();
|
|
185981
186037
|
while (true) {
|
|
@@ -185998,12 +186054,12 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185998
186054
|
else resolve10();
|
|
185999
186055
|
});
|
|
186000
186056
|
});
|
|
186001
|
-
|
|
186057
|
+
fs13.renameSync(partPath, destPath);
|
|
186002
186058
|
} catch (error) {
|
|
186003
186059
|
try {
|
|
186004
186060
|
fileStream.close();
|
|
186005
|
-
if (
|
|
186006
|
-
|
|
186061
|
+
if (fs13.existsSync(partPath)) {
|
|
186062
|
+
fs13.unlinkSync(partPath);
|
|
186007
186063
|
}
|
|
186008
186064
|
} catch {
|
|
186009
186065
|
}
|
|
@@ -186012,8 +186068,8 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
186012
186068
|
}
|
|
186013
186069
|
async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
186014
186070
|
onProgress?.({ phase: "extracting" });
|
|
186015
|
-
if (!
|
|
186016
|
-
|
|
186071
|
+
if (!fs13.existsSync(destDir)) {
|
|
186072
|
+
fs13.mkdirSync(destDir, { recursive: true });
|
|
186017
186073
|
}
|
|
186018
186074
|
try {
|
|
186019
186075
|
const fileStream = createReadStream(archivePath);
|
|
@@ -186022,8 +186078,8 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
|
186022
186078
|
onProgress?.({ phase: "finalizing" });
|
|
186023
186079
|
for (const execPath of definition.executables) {
|
|
186024
186080
|
const fullPath = path8.join(destDir, execPath);
|
|
186025
|
-
if (
|
|
186026
|
-
|
|
186081
|
+
if (fs13.existsSync(fullPath)) {
|
|
186082
|
+
fs13.chmodSync(fullPath, 493);
|
|
186027
186083
|
}
|
|
186028
186084
|
}
|
|
186029
186085
|
} catch (error) {
|
|
@@ -186047,12 +186103,12 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
186047
186103
|
`Binary type definitions must have exactly one executable, got ${definition.executables.length}`
|
|
186048
186104
|
);
|
|
186049
186105
|
}
|
|
186050
|
-
if (!
|
|
186051
|
-
|
|
186106
|
+
if (!fs13.existsSync(binDir)) {
|
|
186107
|
+
fs13.mkdirSync(binDir, { recursive: true });
|
|
186052
186108
|
}
|
|
186053
186109
|
const execPath = path8.join(binDir, definition.executables[0]);
|
|
186054
186110
|
await downloadFile(url, execPath, onProgress);
|
|
186055
|
-
|
|
186111
|
+
fs13.chmodSync(execPath, 493);
|
|
186056
186112
|
onProgress?.({ phase: "finalizing" });
|
|
186057
186113
|
} else {
|
|
186058
186114
|
const downloadDir = path8.join(getBinBaseDir(), "downloads");
|
|
@@ -186063,8 +186119,8 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
186063
186119
|
await extractTarball(archivePath, binDir, definition, onProgress);
|
|
186064
186120
|
} finally {
|
|
186065
186121
|
try {
|
|
186066
|
-
if (
|
|
186067
|
-
|
|
186122
|
+
if (fs13.existsSync(archivePath)) {
|
|
186123
|
+
fs13.unlinkSync(archivePath);
|
|
186068
186124
|
}
|
|
186069
186125
|
} catch {
|
|
186070
186126
|
}
|
|
@@ -186240,20 +186296,20 @@ function CheckUI() {
|
|
|
186240
186296
|
useEffect2(() => {
|
|
186241
186297
|
async function load() {
|
|
186242
186298
|
const configPath = path9.join(process.cwd(), "specific.hcl");
|
|
186243
|
-
if (!
|
|
186299
|
+
if (!fs14.existsSync(configPath)) {
|
|
186244
186300
|
setState({
|
|
186245
186301
|
status: "error",
|
|
186246
186302
|
error: "No specific.hcl found in current directory"
|
|
186247
186303
|
});
|
|
186248
186304
|
return;
|
|
186249
186305
|
}
|
|
186250
|
-
const hcl =
|
|
186306
|
+
const hcl = fs14.readFileSync(configPath, "utf-8");
|
|
186251
186307
|
try {
|
|
186252
186308
|
const config2 = await parseConfig(hcl);
|
|
186253
186309
|
for (const build of config2.builds) {
|
|
186254
186310
|
if (build.dockerfile) {
|
|
186255
186311
|
const dockerfilePath = path9.resolve(process.cwd(), build.dockerfile);
|
|
186256
|
-
if (!
|
|
186312
|
+
if (!fs14.existsSync(dockerfilePath)) {
|
|
186257
186313
|
setState({
|
|
186258
186314
|
status: "error",
|
|
186259
186315
|
error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
|
|
@@ -186269,7 +186325,7 @@ function CheckUI() {
|
|
|
186269
186325
|
process.cwd(),
|
|
186270
186326
|
pg.reshape.migrations_dir ?? "migrations"
|
|
186271
186327
|
);
|
|
186272
|
-
if (!
|
|
186328
|
+
if (!fs14.existsSync(migrationsDir)) {
|
|
186273
186329
|
reshapeChecks2.push({
|
|
186274
186330
|
databaseName: pg.name,
|
|
186275
186331
|
migrationsDir: pg.reshape.migrations_dir ?? "migrations",
|
|
@@ -186320,9 +186376,9 @@ function checkCommand() {
|
|
|
186320
186376
|
|
|
186321
186377
|
// src/commands/dev.tsx
|
|
186322
186378
|
import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
|
|
186323
|
-
import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
|
|
186379
|
+
import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static, useInput as useInput4 } from "ink";
|
|
186324
186380
|
import Spinner4 from "ink-spinner";
|
|
186325
|
-
import * as
|
|
186381
|
+
import * as fs24 from "fs";
|
|
186326
186382
|
import * as path20 from "path";
|
|
186327
186383
|
|
|
186328
186384
|
// node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
@@ -188077,7 +188133,7 @@ var PortAllocator = class {
|
|
|
188077
188133
|
};
|
|
188078
188134
|
|
|
188079
188135
|
// src/lib/dev/stable-port-allocator.ts
|
|
188080
|
-
import * as
|
|
188136
|
+
import * as fs15 from "fs";
|
|
188081
188137
|
import * as path10 from "path";
|
|
188082
188138
|
var PORT_RANGE_START2 = 4e4;
|
|
188083
188139
|
var PORT_RANGE_END2 = 49999;
|
|
@@ -188092,11 +188148,11 @@ var StablePortAllocator = class {
|
|
|
188092
188148
|
this.loadPorts();
|
|
188093
188149
|
}
|
|
188094
188150
|
loadPorts() {
|
|
188095
|
-
if (!
|
|
188151
|
+
if (!fs15.existsSync(this.portsFilePath)) {
|
|
188096
188152
|
return;
|
|
188097
188153
|
}
|
|
188098
188154
|
try {
|
|
188099
|
-
const content =
|
|
188155
|
+
const content = fs15.readFileSync(this.portsFilePath, "utf-8");
|
|
188100
188156
|
const data = JSON.parse(content);
|
|
188101
188157
|
if (data.version === 1 && data.ports) {
|
|
188102
188158
|
this.savedPorts = data.ports;
|
|
@@ -188109,14 +188165,14 @@ var StablePortAllocator = class {
|
|
|
188109
188165
|
}
|
|
188110
188166
|
}
|
|
188111
188167
|
savePorts() {
|
|
188112
|
-
if (!
|
|
188113
|
-
|
|
188168
|
+
if (!fs15.existsSync(this.portsDir)) {
|
|
188169
|
+
fs15.mkdirSync(this.portsDir, { recursive: true });
|
|
188114
188170
|
}
|
|
188115
188171
|
const data = {
|
|
188116
188172
|
version: 1,
|
|
188117
188173
|
ports: this.savedPorts
|
|
188118
188174
|
};
|
|
188119
|
-
|
|
188175
|
+
fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
188120
188176
|
}
|
|
188121
188177
|
allocateRandom() {
|
|
188122
188178
|
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
@@ -188143,7 +188199,7 @@ var StablePortAllocator = class {
|
|
|
188143
188199
|
};
|
|
188144
188200
|
|
|
188145
188201
|
// src/lib/dev/database-manager.ts
|
|
188146
|
-
import * as
|
|
188202
|
+
import * as fs16 from "fs";
|
|
188147
188203
|
import * as path11 from "path";
|
|
188148
188204
|
import * as net from "net";
|
|
188149
188205
|
import { spawn } from "child_process";
|
|
@@ -188155,9 +188211,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
188155
188211
|
const password = "postgres";
|
|
188156
188212
|
const libraryEnv = getLibraryEnv(binary);
|
|
188157
188213
|
const env2 = { ...process.env, ...libraryEnv };
|
|
188158
|
-
const dataExists =
|
|
188214
|
+
const dataExists = fs16.existsSync(dbDataPath);
|
|
188159
188215
|
if (!dataExists) {
|
|
188160
|
-
|
|
188216
|
+
fs16.mkdirSync(dbDataPath, { recursive: true });
|
|
188161
188217
|
await runCommand(
|
|
188162
188218
|
binary.executables["initdb"],
|
|
188163
188219
|
["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
|
|
@@ -188234,8 +188290,8 @@ async function startRedis(redis, port, onProgress) {
|
|
|
188234
188290
|
async function startStorage(storage, port, dataDir) {
|
|
188235
188291
|
const S3rver = (await import("s3rver")).default;
|
|
188236
188292
|
const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
|
|
188237
|
-
if (!
|
|
188238
|
-
|
|
188293
|
+
if (!fs16.existsSync(storageDataPath)) {
|
|
188294
|
+
fs16.mkdirSync(storageDataPath, { recursive: true });
|
|
188239
188295
|
}
|
|
188240
188296
|
const host = "127.0.0.1";
|
|
188241
188297
|
const accessKey = "S3RVER";
|
|
@@ -188878,7 +188934,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
188878
188934
|
}
|
|
188879
188935
|
|
|
188880
188936
|
// src/lib/dev/instance-state.ts
|
|
188881
|
-
import * as
|
|
188937
|
+
import * as fs17 from "fs";
|
|
188882
188938
|
import * as path12 from "path";
|
|
188883
188939
|
var InstanceStateManager = class {
|
|
188884
188940
|
stateDir;
|
|
@@ -188896,8 +188952,8 @@ var InstanceStateManager = class {
|
|
|
188896
188952
|
return this.key;
|
|
188897
188953
|
}
|
|
188898
188954
|
ensureStateDir() {
|
|
188899
|
-
if (!
|
|
188900
|
-
|
|
188955
|
+
if (!fs17.existsSync(this.stateDir)) {
|
|
188956
|
+
fs17.mkdirSync(this.stateDir, { recursive: true });
|
|
188901
188957
|
}
|
|
188902
188958
|
}
|
|
188903
188959
|
isProcessRunning(pid) {
|
|
@@ -188914,15 +188970,15 @@ var InstanceStateManager = class {
|
|
|
188914
188970
|
const startTime = Date.now();
|
|
188915
188971
|
while (Date.now() - startTime < timeoutMs) {
|
|
188916
188972
|
try {
|
|
188917
|
-
const fd =
|
|
188973
|
+
const fd = fs17.openSync(
|
|
188918
188974
|
this.lockPath,
|
|
188919
|
-
|
|
188975
|
+
fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
|
|
188920
188976
|
);
|
|
188921
|
-
|
|
188922
|
-
|
|
188977
|
+
fs17.writeSync(fd, String(process.pid));
|
|
188978
|
+
fs17.closeSync(fd);
|
|
188923
188979
|
return () => {
|
|
188924
188980
|
try {
|
|
188925
|
-
|
|
188981
|
+
fs17.unlinkSync(this.lockPath);
|
|
188926
188982
|
} catch {
|
|
188927
188983
|
}
|
|
188928
188984
|
};
|
|
@@ -188931,16 +188987,16 @@ var InstanceStateManager = class {
|
|
|
188931
188987
|
if (err.code === "EEXIST") {
|
|
188932
188988
|
try {
|
|
188933
188989
|
const lockPid = parseInt(
|
|
188934
|
-
|
|
188990
|
+
fs17.readFileSync(this.lockPath, "utf-8").trim(),
|
|
188935
188991
|
10
|
|
188936
188992
|
);
|
|
188937
188993
|
if (!this.isProcessRunning(lockPid)) {
|
|
188938
|
-
|
|
188994
|
+
fs17.unlinkSync(this.lockPath);
|
|
188939
188995
|
continue;
|
|
188940
188996
|
}
|
|
188941
188997
|
} catch {
|
|
188942
188998
|
try {
|
|
188943
|
-
|
|
188999
|
+
fs17.unlinkSync(this.lockPath);
|
|
188944
189000
|
} catch {
|
|
188945
189001
|
}
|
|
188946
189002
|
continue;
|
|
@@ -188954,12 +189010,12 @@ var InstanceStateManager = class {
|
|
|
188954
189010
|
throw new Error("Failed to acquire state lock (timeout)");
|
|
188955
189011
|
}
|
|
188956
189012
|
async getExistingInstances() {
|
|
188957
|
-
if (!
|
|
189013
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
188958
189014
|
return null;
|
|
188959
189015
|
}
|
|
188960
189016
|
const releaseLock = await this.acquireLock();
|
|
188961
189017
|
try {
|
|
188962
|
-
const content =
|
|
189018
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
188963
189019
|
const state = JSON.parse(content);
|
|
188964
189020
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
188965
189021
|
return null;
|
|
@@ -188972,21 +189028,21 @@ var InstanceStateManager = class {
|
|
|
188972
189028
|
}
|
|
188973
189029
|
}
|
|
188974
189030
|
async cleanStaleState() {
|
|
188975
|
-
if (!
|
|
189031
|
+
if (!fs17.existsSync(this.statePath)) {
|
|
188976
189032
|
return false;
|
|
188977
189033
|
}
|
|
188978
189034
|
const releaseLock = await this.acquireLock();
|
|
188979
189035
|
try {
|
|
188980
|
-
const content =
|
|
189036
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
188981
189037
|
const state = JSON.parse(content);
|
|
188982
189038
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
188983
|
-
|
|
189039
|
+
fs17.unlinkSync(this.statePath);
|
|
188984
189040
|
return true;
|
|
188985
189041
|
}
|
|
188986
189042
|
return false;
|
|
188987
189043
|
} catch {
|
|
188988
189044
|
try {
|
|
188989
|
-
|
|
189045
|
+
fs17.unlinkSync(this.statePath);
|
|
188990
189046
|
return true;
|
|
188991
189047
|
} catch {
|
|
188992
189048
|
}
|
|
@@ -188998,8 +189054,8 @@ var InstanceStateManager = class {
|
|
|
188998
189054
|
async claimOwnership(command) {
|
|
188999
189055
|
const releaseLock = await this.acquireLock();
|
|
189000
189056
|
try {
|
|
189001
|
-
if (
|
|
189002
|
-
const content =
|
|
189057
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189058
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189003
189059
|
const state2 = JSON.parse(content);
|
|
189004
189060
|
if (this.isProcessRunning(state2.owner.pid)) {
|
|
189005
189061
|
throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
|
|
@@ -189068,8 +189124,8 @@ var InstanceStateManager = class {
|
|
|
189068
189124
|
}
|
|
189069
189125
|
const releaseLock = await this.acquireLock();
|
|
189070
189126
|
try {
|
|
189071
|
-
if (
|
|
189072
|
-
|
|
189127
|
+
if (fs17.existsSync(this.statePath)) {
|
|
189128
|
+
fs17.unlinkSync(this.statePath);
|
|
189073
189129
|
}
|
|
189074
189130
|
this.ownsInstances = false;
|
|
189075
189131
|
} finally {
|
|
@@ -189077,21 +189133,21 @@ var InstanceStateManager = class {
|
|
|
189077
189133
|
}
|
|
189078
189134
|
}
|
|
189079
189135
|
readState() {
|
|
189080
|
-
const content =
|
|
189136
|
+
const content = fs17.readFileSync(this.statePath, "utf-8");
|
|
189081
189137
|
return JSON.parse(content);
|
|
189082
189138
|
}
|
|
189083
189139
|
writeStateAtomic(state) {
|
|
189084
189140
|
this.ensureStateDir();
|
|
189085
189141
|
const tmpPath = this.statePath + ".tmp";
|
|
189086
|
-
|
|
189087
|
-
|
|
189142
|
+
fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
189143
|
+
fs17.renameSync(tmpPath, this.statePath);
|
|
189088
189144
|
}
|
|
189089
189145
|
};
|
|
189090
189146
|
|
|
189091
189147
|
// src/lib/dev/http-proxy.ts
|
|
189092
189148
|
import * as http from "http";
|
|
189093
189149
|
import * as https from "https";
|
|
189094
|
-
import * as
|
|
189150
|
+
import * as fs18 from "fs";
|
|
189095
189151
|
import * as path13 from "path";
|
|
189096
189152
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
189097
189153
|
import httpProxy from "http-proxy";
|
|
@@ -189100,8 +189156,8 @@ var adminDir = path13.join(__dirname3, "admin");
|
|
|
189100
189156
|
var _embeddedAdmin = null;
|
|
189101
189157
|
var HTTP_PORT = 80;
|
|
189102
189158
|
var HTTPS_PORT = 443;
|
|
189103
|
-
var DOMAIN_SUFFIX = ".
|
|
189104
|
-
var ADMIN_DOMAIN = "
|
|
189159
|
+
var DOMAIN_SUFFIX = ".spcf.localhost";
|
|
189160
|
+
var ADMIN_DOMAIN = "spcf.localhost";
|
|
189105
189161
|
var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
|
|
189106
189162
|
var TEMPORAL_UI_PREFIX = "__temporal";
|
|
189107
189163
|
var MIME_TYPES = {
|
|
@@ -189425,9 +189481,9 @@ function sendNotFound(res, requestedService, serviceMap) {
|
|
|
189425
189481
|
const serviceList = Array.from(serviceMap.keys()).map((key) => {
|
|
189426
189482
|
const parts = key.split(".");
|
|
189427
189483
|
if (parts.length === 1) {
|
|
189428
|
-
return `<li>${parts[0]}.
|
|
189484
|
+
return `<li>${parts[0]}.spcf.localhost</li>`;
|
|
189429
189485
|
} else {
|
|
189430
|
-
return `<li>${key}.
|
|
189486
|
+
return `<li>${key}.spcf.localhost</li>`;
|
|
189431
189487
|
}
|
|
189432
189488
|
}).join("\n");
|
|
189433
189489
|
const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
|
|
@@ -189499,10 +189555,10 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189499
189555
|
res.end("<h1>Forbidden</h1>");
|
|
189500
189556
|
return;
|
|
189501
189557
|
}
|
|
189502
|
-
if (
|
|
189503
|
-
if (
|
|
189558
|
+
if (fs18.existsSync(resolvedPath)) {
|
|
189559
|
+
if (fs18.statSync(resolvedPath).isDirectory()) {
|
|
189504
189560
|
const indexPath2 = path13.join(resolvedPath, "index.html");
|
|
189505
|
-
if (
|
|
189561
|
+
if (fs18.existsSync(indexPath2)) {
|
|
189506
189562
|
return serveFile(res, indexPath2);
|
|
189507
189563
|
}
|
|
189508
189564
|
} else {
|
|
@@ -189510,15 +189566,15 @@ function serveFilesystemFile(res, pathname) {
|
|
|
189510
189566
|
}
|
|
189511
189567
|
}
|
|
189512
189568
|
const htmlPath = resolvedPath + ".html";
|
|
189513
|
-
if (
|
|
189569
|
+
if (fs18.existsSync(htmlPath)) {
|
|
189514
189570
|
return serveFile(res, htmlPath);
|
|
189515
189571
|
}
|
|
189516
189572
|
const indexPath = path13.join(resolvedPath, "index.html");
|
|
189517
|
-
if (
|
|
189573
|
+
if (fs18.existsSync(indexPath)) {
|
|
189518
189574
|
return serveFile(res, indexPath);
|
|
189519
189575
|
}
|
|
189520
189576
|
const notFoundPath = path13.join(adminDir, "404.html");
|
|
189521
|
-
if (
|
|
189577
|
+
if (fs18.existsSync(notFoundPath)) {
|
|
189522
189578
|
return serveFileContent(res, notFoundPath, "text/html", 404);
|
|
189523
189579
|
}
|
|
189524
189580
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
@@ -189531,7 +189587,7 @@ function serveFile(res, filePath) {
|
|
|
189531
189587
|
}
|
|
189532
189588
|
function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
189533
189589
|
try {
|
|
189534
|
-
const content =
|
|
189590
|
+
const content = fs18.readFileSync(filePath);
|
|
189535
189591
|
res.writeHead(statusCode, { "Content-Type": contentType });
|
|
189536
189592
|
res.end(content);
|
|
189537
189593
|
} catch (err) {
|
|
@@ -189906,7 +189962,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
|
|
|
189906
189962
|
|
|
189907
189963
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
189908
189964
|
import * as net3 from "net";
|
|
189909
|
-
import * as
|
|
189965
|
+
import * as fs19 from "fs";
|
|
189910
189966
|
import * as path15 from "path";
|
|
189911
189967
|
import { spawn as spawn4 } from "child_process";
|
|
189912
189968
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -189940,12 +189996,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
|
|
|
189940
189996
|
);
|
|
189941
189997
|
const host = "127.0.0.1";
|
|
189942
189998
|
const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
|
|
189943
|
-
if (!
|
|
189944
|
-
|
|
189999
|
+
if (!fs19.existsSync(drizzleConfigDir)) {
|
|
190000
|
+
fs19.mkdirSync(drizzleConfigDir, { recursive: true });
|
|
189945
190001
|
}
|
|
189946
190002
|
const storeJson = generateStoreJson(postgresInstances);
|
|
189947
190003
|
const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
|
|
189948
|
-
|
|
190004
|
+
fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
|
|
189949
190005
|
writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
|
|
189950
190006
|
writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
|
|
189951
190007
|
writeLog("drizzle-gateway", `PORT: ${port}`);
|
|
@@ -190047,16 +190103,16 @@ function detectSyncDatabases(config) {
|
|
|
190047
190103
|
}
|
|
190048
190104
|
|
|
190049
190105
|
// src/lib/dev/reshape-watcher.ts
|
|
190050
|
-
import * as
|
|
190106
|
+
import * as fs20 from "fs";
|
|
190051
190107
|
import * as path16 from "path";
|
|
190052
190108
|
import { spawnSync } from "child_process";
|
|
190053
190109
|
function getMigrationFiles(dir, log) {
|
|
190054
190110
|
log(`Scanning migrations directory: ${dir}`);
|
|
190055
|
-
if (!
|
|
190111
|
+
if (!fs20.existsSync(dir)) {
|
|
190056
190112
|
log(`Migrations directory does not exist: ${dir}`);
|
|
190057
190113
|
return [];
|
|
190058
190114
|
}
|
|
190059
|
-
const files =
|
|
190115
|
+
const files = fs20.readdirSync(dir);
|
|
190060
190116
|
log(`Found ${files.length} files in directory`);
|
|
190061
190117
|
const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
|
|
190062
190118
|
log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
|
|
@@ -190106,7 +190162,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
|
|
|
190106
190162
|
}
|
|
190107
190163
|
function makeReadOnly(filePath, log) {
|
|
190108
190164
|
try {
|
|
190109
|
-
|
|
190165
|
+
fs20.chmodSync(filePath, 292);
|
|
190110
190166
|
log(`Set file permissions to read-only (444): ${filePath}`);
|
|
190111
190167
|
} catch (err) {
|
|
190112
190168
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -190192,9 +190248,9 @@ function createReshapeWatcher(options2) {
|
|
|
190192
190248
|
};
|
|
190193
190249
|
const startWatching = () => {
|
|
190194
190250
|
log(`Starting file watcher for migrations directory...`);
|
|
190195
|
-
if (!
|
|
190251
|
+
if (!fs20.existsSync(migrationsDir)) {
|
|
190196
190252
|
log(`Migrations directory does not exist, creating: ${migrationsDir}`);
|
|
190197
|
-
|
|
190253
|
+
fs20.mkdirSync(migrationsDir, { recursive: true });
|
|
190198
190254
|
}
|
|
190199
190255
|
log(`Watching directory: ${migrationsDir}`);
|
|
190200
190256
|
watcher = chokidar_default.watch(migrationsDir, {
|
|
@@ -190329,7 +190385,7 @@ function createReshapeWatcher(options2) {
|
|
|
190329
190385
|
}
|
|
190330
190386
|
|
|
190331
190387
|
// src/lib/dev/temporal-manager.ts
|
|
190332
|
-
import * as
|
|
190388
|
+
import * as fs21 from "fs";
|
|
190333
190389
|
import * as path17 from "path";
|
|
190334
190390
|
import * as net4 from "net";
|
|
190335
190391
|
import { spawn as spawn5 } from "child_process";
|
|
@@ -190337,8 +190393,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
|
|
|
190337
190393
|
const binary = await ensureBinary(temporalBinary, void 0, onProgress);
|
|
190338
190394
|
const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
|
|
190339
190395
|
const dbDir = path17.dirname(dbPath);
|
|
190340
|
-
if (!
|
|
190341
|
-
|
|
190396
|
+
if (!fs21.existsSync(dbDir)) {
|
|
190397
|
+
fs21.mkdirSync(dbDir, { recursive: true });
|
|
190342
190398
|
}
|
|
190343
190399
|
const host = "127.0.0.1";
|
|
190344
190400
|
const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
|
|
@@ -190667,7 +190723,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
190667
190723
|
}
|
|
190668
190724
|
|
|
190669
190725
|
// src/lib/dev/subdomain-generator.ts
|
|
190670
|
-
import * as
|
|
190726
|
+
import * as fs22 from "fs";
|
|
190671
190727
|
import * as path18 from "path";
|
|
190672
190728
|
import { generateSlug } from "random-word-slugs";
|
|
190673
190729
|
var StableSubdomainAllocator = class {
|
|
@@ -190680,11 +190736,11 @@ var StableSubdomainAllocator = class {
|
|
|
190680
190736
|
this.loadTunnels();
|
|
190681
190737
|
}
|
|
190682
190738
|
loadTunnels() {
|
|
190683
|
-
if (!
|
|
190739
|
+
if (!fs22.existsSync(this.tunnelsFilePath)) {
|
|
190684
190740
|
return;
|
|
190685
190741
|
}
|
|
190686
190742
|
try {
|
|
190687
|
-
const content =
|
|
190743
|
+
const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
|
|
190688
190744
|
const data = JSON.parse(content);
|
|
190689
190745
|
if (data.version === 1 && data.baseSlug) {
|
|
190690
190746
|
this.baseSlug = data.baseSlug;
|
|
@@ -190694,14 +190750,14 @@ var StableSubdomainAllocator = class {
|
|
|
190694
190750
|
}
|
|
190695
190751
|
}
|
|
190696
190752
|
saveTunnels() {
|
|
190697
|
-
if (!
|
|
190698
|
-
|
|
190753
|
+
if (!fs22.existsSync(this.tunnelsDir)) {
|
|
190754
|
+
fs22.mkdirSync(this.tunnelsDir, { recursive: true });
|
|
190699
190755
|
}
|
|
190700
190756
|
const data = {
|
|
190701
190757
|
version: 1,
|
|
190702
190758
|
baseSlug: this.baseSlug
|
|
190703
190759
|
};
|
|
190704
|
-
|
|
190760
|
+
fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
|
|
190705
190761
|
}
|
|
190706
190762
|
generateBaseSlug() {
|
|
190707
190763
|
return generateSlug(2, {
|
|
@@ -190857,9 +190913,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
190857
190913
|
}
|
|
190858
190914
|
|
|
190859
190915
|
// src/lib/dev/proxy-registry.ts
|
|
190860
|
-
import * as
|
|
190916
|
+
import * as fs23 from "fs";
|
|
190861
190917
|
import * as path19 from "path";
|
|
190862
|
-
import * as
|
|
190918
|
+
import * as os9 from "os";
|
|
190863
190919
|
import * as net6 from "net";
|
|
190864
190920
|
var ProxyRegistryManager = class {
|
|
190865
190921
|
proxyDir;
|
|
@@ -190869,14 +190925,14 @@ var ProxyRegistryManager = class {
|
|
|
190869
190925
|
isOwner = false;
|
|
190870
190926
|
registryWatcher = null;
|
|
190871
190927
|
constructor() {
|
|
190872
|
-
this.proxyDir = path19.join(
|
|
190928
|
+
this.proxyDir = path19.join(os9.homedir(), ".specific", "proxy");
|
|
190873
190929
|
this.ownerPath = path19.join(this.proxyDir, "owner.json");
|
|
190874
190930
|
this.registryPath = path19.join(this.proxyDir, "registry.json");
|
|
190875
190931
|
this.lockPath = path19.join(this.proxyDir, "registry.lock");
|
|
190876
190932
|
}
|
|
190877
190933
|
ensureProxyDir() {
|
|
190878
|
-
if (!
|
|
190879
|
-
|
|
190934
|
+
if (!fs23.existsSync(this.proxyDir)) {
|
|
190935
|
+
fs23.mkdirSync(this.proxyDir, { recursive: true });
|
|
190880
190936
|
}
|
|
190881
190937
|
}
|
|
190882
190938
|
isProcessRunning(pid) {
|
|
@@ -190933,15 +190989,15 @@ var ProxyRegistryManager = class {
|
|
|
190933
190989
|
const startTime = Date.now();
|
|
190934
190990
|
while (Date.now() - startTime < timeoutMs) {
|
|
190935
190991
|
try {
|
|
190936
|
-
const fd =
|
|
190992
|
+
const fd = fs23.openSync(
|
|
190937
190993
|
this.lockPath,
|
|
190938
|
-
|
|
190994
|
+
fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
|
|
190939
190995
|
);
|
|
190940
|
-
|
|
190941
|
-
|
|
190996
|
+
fs23.writeSync(fd, String(process.pid));
|
|
190997
|
+
fs23.closeSync(fd);
|
|
190942
190998
|
return () => {
|
|
190943
190999
|
try {
|
|
190944
|
-
|
|
191000
|
+
fs23.unlinkSync(this.lockPath);
|
|
190945
191001
|
} catch {
|
|
190946
191002
|
}
|
|
190947
191003
|
};
|
|
@@ -190950,16 +191006,16 @@ var ProxyRegistryManager = class {
|
|
|
190950
191006
|
if (err.code === "EEXIST") {
|
|
190951
191007
|
try {
|
|
190952
191008
|
const lockPid = parseInt(
|
|
190953
|
-
|
|
191009
|
+
fs23.readFileSync(this.lockPath, "utf-8").trim(),
|
|
190954
191010
|
10
|
|
190955
191011
|
);
|
|
190956
191012
|
if (!this.isProcessRunning(lockPid)) {
|
|
190957
|
-
|
|
191013
|
+
fs23.unlinkSync(this.lockPath);
|
|
190958
191014
|
continue;
|
|
190959
191015
|
}
|
|
190960
191016
|
} catch {
|
|
190961
191017
|
try {
|
|
190962
|
-
|
|
191018
|
+
fs23.unlinkSync(this.lockPath);
|
|
190963
191019
|
} catch {
|
|
190964
191020
|
}
|
|
190965
191021
|
continue;
|
|
@@ -190979,8 +191035,8 @@ var ProxyRegistryManager = class {
|
|
|
190979
191035
|
async claimProxyOwnership(key) {
|
|
190980
191036
|
const releaseLock = await this.acquireLock();
|
|
190981
191037
|
try {
|
|
190982
|
-
if (
|
|
190983
|
-
const content =
|
|
191038
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191039
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
190984
191040
|
const ownerFile2 = JSON.parse(content);
|
|
190985
191041
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
190986
191042
|
return false;
|
|
@@ -191010,11 +191066,11 @@ var ProxyRegistryManager = class {
|
|
|
191010
191066
|
}
|
|
191011
191067
|
const releaseLock = await this.acquireLock();
|
|
191012
191068
|
try {
|
|
191013
|
-
if (
|
|
191014
|
-
const content =
|
|
191069
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191070
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191015
191071
|
const ownerFile = JSON.parse(content);
|
|
191016
191072
|
if (ownerFile.owner.pid === process.pid) {
|
|
191017
|
-
|
|
191073
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191018
191074
|
}
|
|
191019
191075
|
}
|
|
191020
191076
|
this.isOwner = false;
|
|
@@ -191026,12 +191082,12 @@ var ProxyRegistryManager = class {
|
|
|
191026
191082
|
* Get the current proxy owner.
|
|
191027
191083
|
*/
|
|
191028
191084
|
async getProxyOwner() {
|
|
191029
|
-
if (!
|
|
191085
|
+
if (!fs23.existsSync(this.ownerPath)) {
|
|
191030
191086
|
return null;
|
|
191031
191087
|
}
|
|
191032
191088
|
const releaseLock = await this.acquireLock();
|
|
191033
191089
|
try {
|
|
191034
|
-
const content =
|
|
191090
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191035
191091
|
const ownerFile = JSON.parse(content);
|
|
191036
191092
|
if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
|
|
191037
191093
|
return null;
|
|
@@ -191128,7 +191184,7 @@ var ProxyRegistryManager = class {
|
|
|
191128
191184
|
*/
|
|
191129
191185
|
watchRegistry(onChange) {
|
|
191130
191186
|
this.ensureProxyDir();
|
|
191131
|
-
if (!
|
|
191187
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191132
191188
|
const emptyRegistry = {
|
|
191133
191189
|
version: 1,
|
|
191134
191190
|
keys: {},
|
|
@@ -191171,13 +191227,13 @@ var ProxyRegistryManager = class {
|
|
|
191171
191227
|
async attemptElection(key) {
|
|
191172
191228
|
const releaseLock = await this.acquireLock();
|
|
191173
191229
|
try {
|
|
191174
|
-
if (
|
|
191175
|
-
const content =
|
|
191230
|
+
if (fs23.existsSync(this.ownerPath)) {
|
|
191231
|
+
const content = fs23.readFileSync(this.ownerPath, "utf-8");
|
|
191176
191232
|
const ownerFile2 = JSON.parse(content);
|
|
191177
191233
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
191178
191234
|
return false;
|
|
191179
191235
|
}
|
|
191180
|
-
|
|
191236
|
+
fs23.unlinkSync(this.ownerPath);
|
|
191181
191237
|
}
|
|
191182
191238
|
const ownerFile = {
|
|
191183
191239
|
version: 1,
|
|
@@ -191195,7 +191251,7 @@ var ProxyRegistryManager = class {
|
|
|
191195
191251
|
}
|
|
191196
191252
|
}
|
|
191197
191253
|
readRegistry() {
|
|
191198
|
-
if (!
|
|
191254
|
+
if (!fs23.existsSync(this.registryPath)) {
|
|
191199
191255
|
return {
|
|
191200
191256
|
version: 1,
|
|
191201
191257
|
keys: {},
|
|
@@ -191203,7 +191259,7 @@ var ProxyRegistryManager = class {
|
|
|
191203
191259
|
};
|
|
191204
191260
|
}
|
|
191205
191261
|
try {
|
|
191206
|
-
const content =
|
|
191262
|
+
const content = fs23.readFileSync(this.registryPath, "utf-8");
|
|
191207
191263
|
return JSON.parse(content);
|
|
191208
191264
|
} catch {
|
|
191209
191265
|
return {
|
|
@@ -191216,8 +191272,8 @@ var ProxyRegistryManager = class {
|
|
|
191216
191272
|
writeFileAtomic(filePath, data) {
|
|
191217
191273
|
this.ensureProxyDir();
|
|
191218
191274
|
const tmpPath = filePath + ".tmp";
|
|
191219
|
-
|
|
191220
|
-
|
|
191275
|
+
fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
191276
|
+
fs23.renameSync(tmpPath, filePath);
|
|
191221
191277
|
}
|
|
191222
191278
|
};
|
|
191223
191279
|
|
|
@@ -191326,7 +191382,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191326
191382
|
const setupDone = tunnelEnabled || !systemSetupNeeded();
|
|
191327
191383
|
return {
|
|
191328
191384
|
status: setupDone ? "loading" : "installing-ca",
|
|
191329
|
-
...setupDone ? {} : { caInstallPhase: "
|
|
191385
|
+
...setupDone ? {} : { caInstallPhase: "prompt" },
|
|
191330
191386
|
resources: /* @__PURE__ */ new Map(),
|
|
191331
191387
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
191332
191388
|
services: [],
|
|
@@ -191342,6 +191398,11 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191342
191398
|
installSystemConfig();
|
|
191343
191399
|
}
|
|
191344
191400
|
}, [state.status, state.caInstallPhase]);
|
|
191401
|
+
useInput4((_input, key) => {
|
|
191402
|
+
if (state.status === "installing-ca" && state.caInstallPhase === "prompt" && key.return) {
|
|
191403
|
+
setState((s) => ({ ...s, caInstallPhase: "installing" }));
|
|
191404
|
+
}
|
|
191405
|
+
});
|
|
191345
191406
|
async function installSystemConfig() {
|
|
191346
191407
|
try {
|
|
191347
191408
|
performSystemSetup();
|
|
@@ -191535,7 +191596,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191535
191596
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191536
191597
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
191537
191598
|
try {
|
|
191538
|
-
const hcl =
|
|
191599
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191539
191600
|
parseConfig(hcl).then(() => {
|
|
191540
191601
|
triggerReload();
|
|
191541
191602
|
}).catch((err) => {
|
|
@@ -191661,7 +191722,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191661
191722
|
return;
|
|
191662
191723
|
}
|
|
191663
191724
|
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191664
|
-
if (!
|
|
191725
|
+
if (!fs24.existsSync(configPath)) {
|
|
191665
191726
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
191666
191727
|
setState((s) => ({
|
|
191667
191728
|
...s,
|
|
@@ -191679,7 +191740,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
191679
191740
|
return;
|
|
191680
191741
|
}
|
|
191681
191742
|
let config2;
|
|
191682
|
-
const hcl =
|
|
191743
|
+
const hcl = fs24.readFileSync(configPath, "utf-8");
|
|
191683
191744
|
try {
|
|
191684
191745
|
config2 = await parseConfig(hcl);
|
|
191685
191746
|
} catch (err) {
|
|
@@ -191975,7 +192036,7 @@ Add them to the config block in specific.local`);
|
|
|
191975
192036
|
} else {
|
|
191976
192037
|
const keySuffix = instanceKey === "default" ? "" : `.${instanceKey}`;
|
|
191977
192038
|
for (const svc of exposedServices) {
|
|
191978
|
-
publicUrls.set(svc.name, `${svc.name}${keySuffix}.
|
|
192039
|
+
publicUrls.set(svc.name, `${svc.name}${keySuffix}.spcf.localhost`);
|
|
191979
192040
|
}
|
|
191980
192041
|
}
|
|
191981
192042
|
const services2 = [];
|
|
@@ -192006,7 +192067,7 @@ Add them to the config block in specific.local`);
|
|
|
192006
192067
|
if (service.volumes) {
|
|
192007
192068
|
for (const vol of service.volumes) {
|
|
192008
192069
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192009
|
-
|
|
192070
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192010
192071
|
volumePaths.set(vol.name, volumeDir);
|
|
192011
192072
|
}
|
|
192012
192073
|
}
|
|
@@ -192062,7 +192123,7 @@ Add them to the config block in specific.local`);
|
|
|
192062
192123
|
if (service.volumes) {
|
|
192063
192124
|
for (const vol of service.volumes) {
|
|
192064
192125
|
const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
|
|
192065
|
-
|
|
192126
|
+
fs24.mkdirSync(volumeDir, { recursive: true });
|
|
192066
192127
|
volumePaths.set(vol.name, volumeDir);
|
|
192067
192128
|
}
|
|
192068
192129
|
}
|
|
@@ -192208,7 +192269,7 @@ Add them to the config block in specific.local`);
|
|
|
192208
192269
|
dnsServerRef.current = dnsServer;
|
|
192209
192270
|
const currentServices = await proxyRegistry.getAllServices();
|
|
192210
192271
|
const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
|
|
192211
|
-
const certificate = generateCertificate("
|
|
192272
|
+
const certificate = generateCertificate("spcf.localhost", registeredKeys);
|
|
192212
192273
|
const proxy2 = await startHttpProxy(
|
|
192213
192274
|
exposedServices,
|
|
192214
192275
|
certificate,
|
|
@@ -192229,7 +192290,7 @@ Add them to the config block in specific.local`);
|
|
|
192229
192290
|
knownKeys.add(key);
|
|
192230
192291
|
}
|
|
192231
192292
|
const allKeys = [...knownKeys];
|
|
192232
|
-
const newCertificate = generateCertificate("
|
|
192293
|
+
const newCertificate = generateCertificate("spcf.localhost", allKeys);
|
|
192233
192294
|
proxy2.updateCertificate(newCertificate);
|
|
192234
192295
|
}
|
|
192235
192296
|
});
|
|
@@ -192281,7 +192342,7 @@ Add them to the config block in specific.local`);
|
|
|
192281
192342
|
const electionServices = await proxyRegistry.getAllServices();
|
|
192282
192343
|
const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
|
|
192283
192344
|
const electionKeyNames = Object.keys(electionKeyRegistrations);
|
|
192284
|
-
const certificate = generateCertificate("
|
|
192345
|
+
const certificate = generateCertificate("spcf.localhost", electionKeyNames);
|
|
192285
192346
|
const proxy2 = await startHttpProxy(
|
|
192286
192347
|
exposedServices,
|
|
192287
192348
|
certificate,
|
|
@@ -192356,8 +192417,11 @@ Add them to the config block in specific.local`);
|
|
|
192356
192417
|
};
|
|
192357
192418
|
}, [reloadTrigger, readyToStart, instanceKey]);
|
|
192358
192419
|
if (state.status === "installing-ca") {
|
|
192420
|
+
if (state.caInstallPhase === "prompt") {
|
|
192421
|
+
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, "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."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Press ", /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
|
|
192422
|
+
}
|
|
192359
192423
|
if (state.caInstallPhase === "installing") {
|
|
192360
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "
|
|
192424
|
+
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"));
|
|
192361
192425
|
}
|
|
192362
192426
|
if (state.caInstallPhase === "error") {
|
|
192363
192427
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
|
|
@@ -192479,7 +192543,7 @@ Add them to the config block in specific.local`);
|
|
|
192479
192543
|
...!tunnelEnabled ? [
|
|
192480
192544
|
{
|
|
192481
192545
|
key: "admin",
|
|
192482
|
-
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "
|
|
192546
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "spcf.localhost"))
|
|
192483
192547
|
},
|
|
192484
192548
|
{ key: "admin-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
|
|
192485
192549
|
] : [],
|
|
@@ -192517,7 +192581,7 @@ Add them to the config block in specific.local`);
|
|
|
192517
192581
|
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
192518
192582
|
return {
|
|
192519
192583
|
key: `svc-${svc.name}-${endpoint.name}`,
|
|
192520
|
-
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".
|
|
192584
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
|
|
192521
192585
|
};
|
|
192522
192586
|
});
|
|
192523
192587
|
}),
|
|
@@ -192630,7 +192694,7 @@ init_open();
|
|
|
192630
192694
|
import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
|
|
192631
192695
|
import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
|
|
192632
192696
|
import Spinner5 from "ink-spinner";
|
|
192633
|
-
import * as
|
|
192697
|
+
import * as fs26 from "fs";
|
|
192634
192698
|
import * as path23 from "path";
|
|
192635
192699
|
|
|
192636
192700
|
// src/lib/deploy/build-tester.ts
|
|
@@ -192815,7 +192879,7 @@ async function testAllBuilds(builds, projectDir) {
|
|
|
192815
192879
|
|
|
192816
192880
|
// src/lib/tarball/create.ts
|
|
192817
192881
|
import { execSync as execSync4 } from "child_process";
|
|
192818
|
-
import * as
|
|
192882
|
+
import * as fs25 from "fs";
|
|
192819
192883
|
import * as path22 from "path";
|
|
192820
192884
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
192821
192885
|
function isInsideGitRepository(dir) {
|
|
@@ -192873,7 +192937,7 @@ var EXCLUDED_DIRS = [
|
|
|
192873
192937
|
];
|
|
192874
192938
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
192875
192939
|
const results = [];
|
|
192876
|
-
const entries = await
|
|
192940
|
+
const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
|
|
192877
192941
|
for (const entry of entries) {
|
|
192878
192942
|
const fullPath = path22.join(currentDir, entry.name);
|
|
192879
192943
|
const relativePath = path22.relative(baseDir, fullPath);
|
|
@@ -192892,7 +192956,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
192892
192956
|
async function createTarArchive(projectDir) {
|
|
192893
192957
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
192894
192958
|
const configPath = path22.join(projectDir, "specific.hcl");
|
|
192895
|
-
if (!
|
|
192959
|
+
if (!fs25.existsSync(configPath)) {
|
|
192896
192960
|
throw new Error("specific.hcl not found in project directory");
|
|
192897
192961
|
}
|
|
192898
192962
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -193825,12 +193889,12 @@ ${errorMsg}`
|
|
|
193825
193889
|
}
|
|
193826
193890
|
async function deployCommand(environment, options2) {
|
|
193827
193891
|
const configPath = path23.join(process.cwd(), "specific.hcl");
|
|
193828
|
-
if (!
|
|
193892
|
+
if (!fs26.existsSync(configPath)) {
|
|
193829
193893
|
console.error("Error: No specific.hcl found in current directory");
|
|
193830
193894
|
process.exit(1);
|
|
193831
193895
|
}
|
|
193832
193896
|
let config;
|
|
193833
|
-
const hcl =
|
|
193897
|
+
const hcl = fs26.readFileSync(configPath, "utf-8");
|
|
193834
193898
|
try {
|
|
193835
193899
|
config = await parseConfig(hcl);
|
|
193836
193900
|
} catch (err) {
|
|
@@ -193853,7 +193917,7 @@ async function deployCommand(environment, options2) {
|
|
|
193853
193917
|
|
|
193854
193918
|
// src/commands/exec.tsx
|
|
193855
193919
|
import { spawn as spawn7 } from "child_process";
|
|
193856
|
-
import * as
|
|
193920
|
+
import * as fs27 from "fs";
|
|
193857
193921
|
import * as path24 from "path";
|
|
193858
193922
|
async function execCommand(serviceName, command, instanceKey = "default") {
|
|
193859
193923
|
if (command.length === 0) {
|
|
@@ -193883,12 +193947,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
193883
193947
|
}
|
|
193884
193948
|
};
|
|
193885
193949
|
const configPath = path24.join(process.cwd(), "specific.hcl");
|
|
193886
|
-
if (!
|
|
193950
|
+
if (!fs27.existsSync(configPath)) {
|
|
193887
193951
|
console.error("Error: No specific.hcl found in current directory");
|
|
193888
193952
|
process.exit(1);
|
|
193889
193953
|
}
|
|
193890
193954
|
let config;
|
|
193891
|
-
const hcl =
|
|
193955
|
+
const hcl = fs27.readFileSync(configPath, "utf-8");
|
|
193892
193956
|
try {
|
|
193893
193957
|
config = await parseConfig(hcl);
|
|
193894
193958
|
} catch (err) {
|
|
@@ -194038,7 +194102,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
194038
194102
|
|
|
194039
194103
|
// src/commands/psql.tsx
|
|
194040
194104
|
import { spawn as spawn8 } from "child_process";
|
|
194041
|
-
import * as
|
|
194105
|
+
import * as fs28 from "fs";
|
|
194042
194106
|
import * as path25 from "path";
|
|
194043
194107
|
async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
|
|
194044
194108
|
let startedResources = [];
|
|
@@ -194057,12 +194121,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194057
194121
|
}
|
|
194058
194122
|
};
|
|
194059
194123
|
const configPath = path25.join(process.cwd(), "specific.hcl");
|
|
194060
|
-
if (!
|
|
194124
|
+
if (!fs28.existsSync(configPath)) {
|
|
194061
194125
|
console.error("Error: No specific.hcl found in current directory");
|
|
194062
194126
|
process.exit(1);
|
|
194063
194127
|
}
|
|
194064
194128
|
let config;
|
|
194065
|
-
const hcl =
|
|
194129
|
+
const hcl = fs28.readFileSync(configPath, "utf-8");
|
|
194066
194130
|
try {
|
|
194067
194131
|
config = await parseConfig(hcl);
|
|
194068
194132
|
} catch (err) {
|
|
@@ -194186,7 +194250,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
194186
194250
|
|
|
194187
194251
|
// src/commands/reshape.tsx
|
|
194188
194252
|
import { spawn as spawn9 } from "child_process";
|
|
194189
|
-
import * as
|
|
194253
|
+
import * as fs29 from "fs";
|
|
194190
194254
|
import * as path26 from "path";
|
|
194191
194255
|
var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
|
|
194192
194256
|
var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
|
|
@@ -194204,8 +194268,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194204
194268
|
let migrationsDir = "migrations";
|
|
194205
194269
|
let targetDb;
|
|
194206
194270
|
try {
|
|
194207
|
-
if (
|
|
194208
|
-
const configContent =
|
|
194271
|
+
if (fs29.existsSync(configPath)) {
|
|
194272
|
+
const configContent = fs29.readFileSync(configPath, "utf-8");
|
|
194209
194273
|
config = await parseConfig(configContent);
|
|
194210
194274
|
if (databaseName) {
|
|
194211
194275
|
const postgresConfig = config.postgres.find((p) => p.name === databaseName);
|
|
@@ -194341,7 +194405,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194341
194405
|
const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
|
|
194342
194406
|
const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
|
|
194343
194407
|
if (action === "check" || action === "start") {
|
|
194344
|
-
if (
|
|
194408
|
+
if (fs29.existsSync(fullMigrationsPath)) {
|
|
194345
194409
|
reshapeArgs.push("--dirs", fullMigrationsPath);
|
|
194346
194410
|
} else if (action === "check") {
|
|
194347
194411
|
console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
|
|
@@ -194391,7 +194455,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
194391
194455
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
194392
194456
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
194393
194457
|
import Spinner6 from "ink-spinner";
|
|
194394
|
-
import * as
|
|
194458
|
+
import * as fs30 from "fs";
|
|
194395
194459
|
import * as path27 from "path";
|
|
194396
194460
|
function CleanUI({ instanceKey }) {
|
|
194397
194461
|
const [state, setState] = useState7({ status: "checking" });
|
|
@@ -194399,13 +194463,13 @@ function CleanUI({ instanceKey }) {
|
|
|
194399
194463
|
async function clean() {
|
|
194400
194464
|
const projectRoot = process.cwd();
|
|
194401
194465
|
const specificDir = path27.join(projectRoot, ".specific");
|
|
194402
|
-
if (!
|
|
194466
|
+
if (!fs30.existsSync(specificDir)) {
|
|
194403
194467
|
setState({ status: "nothing" });
|
|
194404
194468
|
return;
|
|
194405
194469
|
}
|
|
194406
194470
|
if (instanceKey) {
|
|
194407
194471
|
const keyDir = path27.join(specificDir, "keys", instanceKey);
|
|
194408
|
-
if (!
|
|
194472
|
+
if (!fs30.existsSync(keyDir)) {
|
|
194409
194473
|
setState({ status: "nothing" });
|
|
194410
194474
|
return;
|
|
194411
194475
|
}
|
|
@@ -194421,7 +194485,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194421
194485
|
await stateManager.cleanStaleState();
|
|
194422
194486
|
setState({ status: "cleaning" });
|
|
194423
194487
|
try {
|
|
194424
|
-
|
|
194488
|
+
fs30.rmSync(keyDir, { recursive: true, force: true });
|
|
194425
194489
|
setState({ status: "success" });
|
|
194426
194490
|
} catch (err) {
|
|
194427
194491
|
setState({
|
|
@@ -194431,12 +194495,12 @@ function CleanUI({ instanceKey }) {
|
|
|
194431
194495
|
}
|
|
194432
194496
|
} else {
|
|
194433
194497
|
const keysDir = path27.join(specificDir, "keys");
|
|
194434
|
-
if (!
|
|
194498
|
+
if (!fs30.existsSync(keysDir)) {
|
|
194435
194499
|
setState({ status: "nothing" });
|
|
194436
194500
|
return;
|
|
194437
194501
|
}
|
|
194438
|
-
const keys =
|
|
194439
|
-
(f) =>
|
|
194502
|
+
const keys = fs30.readdirSync(keysDir).filter(
|
|
194503
|
+
(f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
|
|
194440
194504
|
);
|
|
194441
194505
|
for (const key of keys) {
|
|
194442
194506
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -194460,7 +194524,7 @@ function CleanUI({ instanceKey }) {
|
|
|
194460
194524
|
}
|
|
194461
194525
|
setState({ status: "cleaning" });
|
|
194462
194526
|
try {
|
|
194463
|
-
|
|
194527
|
+
fs30.rmSync(keysDir, { recursive: true, force: true });
|
|
194464
194528
|
setState({ status: "success" });
|
|
194465
194529
|
} catch (err) {
|
|
194466
194530
|
setState({
|
|
@@ -194625,7 +194689,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
|
|
|
194625
194689
|
import Spinner7 from "ink-spinner";
|
|
194626
194690
|
|
|
194627
194691
|
// src/lib/update.ts
|
|
194628
|
-
import * as
|
|
194692
|
+
import * as fs31 from "fs";
|
|
194629
194693
|
import * as path28 from "path";
|
|
194630
194694
|
var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
|
|
194631
194695
|
function compareVersions(a, b) {
|
|
@@ -194640,7 +194704,7 @@ function compareVersions(a, b) {
|
|
|
194640
194704
|
return 0;
|
|
194641
194705
|
}
|
|
194642
194706
|
async function checkForUpdate() {
|
|
194643
|
-
const currentVersion = "0.1.
|
|
194707
|
+
const currentVersion = "0.1.73";
|
|
194644
194708
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
194645
194709
|
if (!response.ok) {
|
|
194646
194710
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -194656,7 +194720,7 @@ function isBinaryWritable() {
|
|
|
194656
194720
|
const binaryPath = getCurrentBinaryPath();
|
|
194657
194721
|
const dir = path28.dirname(binaryPath);
|
|
194658
194722
|
try {
|
|
194659
|
-
|
|
194723
|
+
fs31.accessSync(dir, fs31.constants.W_OK);
|
|
194660
194724
|
return true;
|
|
194661
194725
|
} catch {
|
|
194662
194726
|
return false;
|
|
@@ -194667,21 +194731,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194667
194731
|
const binaryDir = path28.dirname(binaryPath);
|
|
194668
194732
|
const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
|
|
194669
194733
|
try {
|
|
194670
|
-
const { platform:
|
|
194671
|
-
const url = `${BINARIES_BASE_URL}/${version}/specific-${
|
|
194734
|
+
const { platform: platform6, arch: arch3 } = getPlatformInfo();
|
|
194735
|
+
const url = `${BINARIES_BASE_URL}/${version}/specific-${platform6}-${arch3}`;
|
|
194672
194736
|
await downloadFile(url, tempPath, onProgress);
|
|
194673
|
-
const stat4 =
|
|
194737
|
+
const stat4 = fs31.statSync(tempPath);
|
|
194674
194738
|
if (stat4.size === 0) {
|
|
194675
194739
|
throw new Error("Downloaded binary is empty");
|
|
194676
194740
|
}
|
|
194677
|
-
|
|
194741
|
+
fs31.chmodSync(tempPath, 493);
|
|
194678
194742
|
onProgress?.({ phase: "finalizing" });
|
|
194679
|
-
|
|
194680
|
-
|
|
194743
|
+
fs31.unlinkSync(binaryPath);
|
|
194744
|
+
fs31.renameSync(tempPath, binaryPath);
|
|
194681
194745
|
} catch (error) {
|
|
194682
194746
|
try {
|
|
194683
|
-
if (
|
|
194684
|
-
|
|
194747
|
+
if (fs31.existsSync(tempPath)) {
|
|
194748
|
+
fs31.unlinkSync(tempPath);
|
|
194685
194749
|
}
|
|
194686
194750
|
} catch {
|
|
194687
194751
|
}
|
|
@@ -194691,21 +194755,21 @@ async function performUpdate(version, onProgress) {
|
|
|
194691
194755
|
|
|
194692
194756
|
// src/lib/background-update.ts
|
|
194693
194757
|
import { spawn as spawn10 } from "child_process";
|
|
194694
|
-
import * as
|
|
194758
|
+
import * as fs32 from "fs";
|
|
194695
194759
|
import * as path29 from "path";
|
|
194696
|
-
import * as
|
|
194697
|
-
var SPECIFIC_DIR = path29.join(
|
|
194760
|
+
import * as os10 from "os";
|
|
194761
|
+
var SPECIFIC_DIR = path29.join(os10.homedir(), ".specific");
|
|
194698
194762
|
var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
|
|
194699
194763
|
var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
|
|
194700
194764
|
var RATE_LIMIT_MS = 60 * 60 * 1e3;
|
|
194701
194765
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
194702
194766
|
function writeCheckTimestamp() {
|
|
194703
|
-
|
|
194704
|
-
|
|
194767
|
+
fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
|
|
194768
|
+
fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
|
|
194705
194769
|
}
|
|
194706
194770
|
function isRateLimited() {
|
|
194707
194771
|
try {
|
|
194708
|
-
const content =
|
|
194772
|
+
const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
|
|
194709
194773
|
const lastCheck = parseInt(content, 10);
|
|
194710
194774
|
if (isNaN(lastCheck)) return false;
|
|
194711
194775
|
return Date.now() - lastCheck < RATE_LIMIT_MS;
|
|
@@ -194839,7 +194903,7 @@ function updateCommand() {
|
|
|
194839
194903
|
var program = new Command();
|
|
194840
194904
|
var env = "production";
|
|
194841
194905
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
194842
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
194906
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.73").enablePositionalOptions();
|
|
194843
194907
|
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));
|
|
194844
194908
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
194845
194909
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|