@specific.dev/cli 0.1.91 → 0.1.93
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/42de6b5004222d9f.js +1 -0
- package/dist/admin/_next/static/chunks/{091449dbc4d3eb97.js → df799a08b7ff15f9.js} +2 -2
- 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 +489 -2062
- package/dist/docs/postgres/reshape/index.md +6 -9
- package/dist/docs/services.md +49 -5
- package/package.json +1 -2
- package/dist/admin/_next/static/chunks/153355cea359ee0f.js +0 -1
- package/dist/postinstall.js +0 -182912
- /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{NRTSfx4J_pKuSA3c36hNC → bHtcCLWKGqkBj4NJ2_XcY}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -520,10 +520,10 @@ ${frame}`;
|
|
|
520
520
|
});
|
|
521
521
|
|
|
522
522
|
// node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
|
|
523
|
-
import
|
|
523
|
+
import fs13 from "node:fs";
|
|
524
524
|
function hasDockerEnv() {
|
|
525
525
|
try {
|
|
526
|
-
|
|
526
|
+
fs13.statSync("/.dockerenv");
|
|
527
527
|
return true;
|
|
528
528
|
} catch {
|
|
529
529
|
return false;
|
|
@@ -531,7 +531,7 @@ function hasDockerEnv() {
|
|
|
531
531
|
}
|
|
532
532
|
function hasDockerCGroup() {
|
|
533
533
|
try {
|
|
534
|
-
return
|
|
534
|
+
return fs13.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
535
535
|
} catch {
|
|
536
536
|
return false;
|
|
537
537
|
}
|
|
@@ -549,7 +549,7 @@ var init_is_docker = __esm({
|
|
|
549
549
|
});
|
|
550
550
|
|
|
551
551
|
// node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
|
|
552
|
-
import
|
|
552
|
+
import fs14 from "node:fs";
|
|
553
553
|
function isInsideContainer() {
|
|
554
554
|
if (cachedResult === void 0) {
|
|
555
555
|
cachedResult = hasContainerEnv() || isDocker();
|
|
@@ -562,7 +562,7 @@ var init_is_inside_container = __esm({
|
|
|
562
562
|
init_is_docker();
|
|
563
563
|
hasContainerEnv = () => {
|
|
564
564
|
try {
|
|
565
|
-
|
|
565
|
+
fs14.statSync("/run/.containerenv");
|
|
566
566
|
return true;
|
|
567
567
|
} catch {
|
|
568
568
|
return false;
|
|
@@ -573,8 +573,8 @@ var init_is_inside_container = __esm({
|
|
|
573
573
|
|
|
574
574
|
// node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js
|
|
575
575
|
import process2 from "node:process";
|
|
576
|
-
import
|
|
577
|
-
import
|
|
576
|
+
import os3 from "node:os";
|
|
577
|
+
import fs15 from "node:fs";
|
|
578
578
|
var isWsl, is_wsl_default;
|
|
579
579
|
var init_is_wsl = __esm({
|
|
580
580
|
"node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js"() {
|
|
@@ -583,14 +583,14 @@ var init_is_wsl = __esm({
|
|
|
583
583
|
if (process2.platform !== "linux") {
|
|
584
584
|
return false;
|
|
585
585
|
}
|
|
586
|
-
if (
|
|
586
|
+
if (os3.release().toLowerCase().includes("microsoft")) {
|
|
587
587
|
if (isInsideContainer()) {
|
|
588
588
|
return false;
|
|
589
589
|
}
|
|
590
590
|
return true;
|
|
591
591
|
}
|
|
592
592
|
try {
|
|
593
|
-
return
|
|
593
|
+
return fs15.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
594
594
|
} catch {
|
|
595
595
|
return false;
|
|
596
596
|
}
|
|
@@ -660,7 +660,7 @@ var init_utilities = __esm({
|
|
|
660
660
|
// node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
|
|
661
661
|
import { promisify as promisify2 } from "node:util";
|
|
662
662
|
import childProcess2 from "node:child_process";
|
|
663
|
-
import
|
|
663
|
+
import fs16, { constants as fsConstants } from "node:fs/promises";
|
|
664
664
|
var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
|
|
665
665
|
var init_wsl_utils = __esm({
|
|
666
666
|
"node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
|
|
@@ -679,14 +679,14 @@ var init_wsl_utils = __esm({
|
|
|
679
679
|
const configFilePath = "/etc/wsl.conf";
|
|
680
680
|
let isConfigFileExists = false;
|
|
681
681
|
try {
|
|
682
|
-
await
|
|
682
|
+
await fs16.access(configFilePath, fsConstants.F_OK);
|
|
683
683
|
isConfigFileExists = true;
|
|
684
684
|
} catch {
|
|
685
685
|
}
|
|
686
686
|
if (!isConfigFileExists) {
|
|
687
687
|
return defaultMountPoint;
|
|
688
688
|
}
|
|
689
|
-
const configContent = await
|
|
689
|
+
const configContent = await fs16.readFile(configFilePath, { encoding: "utf8" });
|
|
690
690
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
691
691
|
if (parsedMountPoint === void 0) {
|
|
692
692
|
return defaultMountPoint;
|
|
@@ -705,7 +705,7 @@ var init_wsl_utils = __esm({
|
|
|
705
705
|
canAccessPowerShellPromise ??= (async () => {
|
|
706
706
|
try {
|
|
707
707
|
const psPath = await powerShellPath2();
|
|
708
|
-
await
|
|
708
|
+
await fs16.access(psPath, fsConstants.X_OK);
|
|
709
709
|
return true;
|
|
710
710
|
} catch {
|
|
711
711
|
return false;
|
|
@@ -719,15 +719,15 @@ var init_wsl_utils = __esm({
|
|
|
719
719
|
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
720
720
|
return stdout.trim();
|
|
721
721
|
};
|
|
722
|
-
convertWslPathToWindows = async (
|
|
723
|
-
if (/^[a-z]+:\/\//i.test(
|
|
724
|
-
return
|
|
722
|
+
convertWslPathToWindows = async (path26) => {
|
|
723
|
+
if (/^[a-z]+:\/\//i.test(path26)) {
|
|
724
|
+
return path26;
|
|
725
725
|
}
|
|
726
726
|
try {
|
|
727
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
727
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path26], { encoding: "utf8" });
|
|
728
728
|
return stdout.trim();
|
|
729
729
|
} catch {
|
|
730
|
-
return
|
|
730
|
+
return path26;
|
|
731
731
|
}
|
|
732
732
|
};
|
|
733
733
|
}
|
|
@@ -913,10 +913,10 @@ __export(open_exports, {
|
|
|
913
913
|
openApp: () => openApp
|
|
914
914
|
});
|
|
915
915
|
import process8 from "node:process";
|
|
916
|
-
import
|
|
916
|
+
import path15 from "node:path";
|
|
917
917
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
918
918
|
import childProcess3 from "node:child_process";
|
|
919
|
-
import
|
|
919
|
+
import fs17, { constants as fsConstants2 } from "node:fs/promises";
|
|
920
920
|
function detectArchBinary(binary) {
|
|
921
921
|
if (typeof binary === "string" || Array.isArray(binary)) {
|
|
922
922
|
return binary;
|
|
@@ -927,16 +927,16 @@ function detectArchBinary(binary) {
|
|
|
927
927
|
}
|
|
928
928
|
return archBinary;
|
|
929
929
|
}
|
|
930
|
-
function detectPlatformBinary({ [
|
|
930
|
+
function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
|
|
931
931
|
if (wsl && is_wsl_default) {
|
|
932
932
|
return detectArchBinary(wsl);
|
|
933
933
|
}
|
|
934
934
|
if (!platformBinary) {
|
|
935
|
-
throw new Error(`${
|
|
935
|
+
throw new Error(`${platform2} is not supported`);
|
|
936
936
|
}
|
|
937
937
|
return detectArchBinary(platformBinary);
|
|
938
938
|
}
|
|
939
|
-
var fallbackAttemptSymbol, __dirname2, localXdgOpenPath,
|
|
939
|
+
var fallbackAttemptSymbol, __dirname2, localXdgOpenPath, platform2, arch2, tryEachApp, baseOpen, open2, openApp, apps, open_default;
|
|
940
940
|
var init_open = __esm({
|
|
941
941
|
"node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
|
|
942
942
|
init_wsl_utils();
|
|
@@ -946,9 +946,9 @@ var init_open = __esm({
|
|
|
946
946
|
init_is_inside_container();
|
|
947
947
|
init_is_in_ssh();
|
|
948
948
|
fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
949
|
-
__dirname2 = import.meta.url ?
|
|
950
|
-
localXdgOpenPath =
|
|
951
|
-
({ platform:
|
|
949
|
+
__dirname2 = import.meta.url ? path15.dirname(fileURLToPath2(import.meta.url)) : "";
|
|
950
|
+
localXdgOpenPath = path15.join(__dirname2, "xdg-open");
|
|
951
|
+
({ platform: platform2, arch: arch2 } = process8);
|
|
952
952
|
tryEachApp = async (apps2, opener) => {
|
|
953
953
|
if (apps2.length === 0) {
|
|
954
954
|
return;
|
|
@@ -1045,7 +1045,7 @@ var init_open = __esm({
|
|
|
1045
1045
|
if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
|
|
1046
1046
|
shouldUseWindowsInWsl = await canAccessPowerShell();
|
|
1047
1047
|
}
|
|
1048
|
-
if (
|
|
1048
|
+
if (platform2 === "darwin") {
|
|
1049
1049
|
command = "open";
|
|
1050
1050
|
if (options2.wait) {
|
|
1051
1051
|
cliArguments.push("--wait-apps");
|
|
@@ -1059,7 +1059,7 @@ var init_open = __esm({
|
|
|
1059
1059
|
if (app) {
|
|
1060
1060
|
cliArguments.push("-a", app);
|
|
1061
1061
|
}
|
|
1062
|
-
} else if (
|
|
1062
|
+
} else if (platform2 === "win32" || shouldUseWindowsInWsl) {
|
|
1063
1063
|
command = await powerShellPath2();
|
|
1064
1064
|
cliArguments.push(...executePowerShell.argumentsPrefix);
|
|
1065
1065
|
if (!is_wsl_default) {
|
|
@@ -1095,11 +1095,11 @@ var init_open = __esm({
|
|
|
1095
1095
|
const isBundled = !__dirname2 || __dirname2 === "/";
|
|
1096
1096
|
let exeLocalXdgOpen = false;
|
|
1097
1097
|
try {
|
|
1098
|
-
await
|
|
1098
|
+
await fs17.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
1099
1099
|
exeLocalXdgOpen = true;
|
|
1100
1100
|
} catch {
|
|
1101
1101
|
}
|
|
1102
|
-
const useSystemXdgOpen = process8.versions.electron ?? (
|
|
1102
|
+
const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
|
|
1103
1103
|
command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
|
|
1104
1104
|
}
|
|
1105
1105
|
if (appArguments.length > 0) {
|
|
@@ -1110,7 +1110,7 @@ var init_open = __esm({
|
|
|
1110
1110
|
childProcessOptions.detached = true;
|
|
1111
1111
|
}
|
|
1112
1112
|
}
|
|
1113
|
-
if (
|
|
1113
|
+
if (platform2 === "darwin" && appArguments.length > 0) {
|
|
1114
1114
|
cliArguments.push("--args", ...appArguments);
|
|
1115
1115
|
}
|
|
1116
1116
|
if (options2.target) {
|
|
@@ -1235,8 +1235,8 @@ var require_dist2 = __commonJS({
|
|
|
1235
1235
|
var $global, $module, $NaN = NaN;
|
|
1236
1236
|
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");
|
|
1237
1237
|
if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
|
|
1238
|
-
var
|
|
1239
|
-
"object" == typeof
|
|
1238
|
+
var fs28 = $global.require("fs");
|
|
1239
|
+
"object" == typeof fs28 && null !== fs28 && 0 !== Object.keys(fs28).length && ($global.fs = fs28);
|
|
1240
1240
|
} catch (e) {
|
|
1241
1241
|
}
|
|
1242
1242
|
if (!$global.fs) {
|
|
@@ -183429,10 +183429,8 @@ import { Command } from "commander";
|
|
|
183429
183429
|
// src/commands/init.tsx
|
|
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
|
-
import "
|
|
183433
|
-
import * as
|
|
183434
|
-
import * as path21 from "path";
|
|
183435
|
-
import * as os11 from "os";
|
|
183432
|
+
import * as fs19 from "fs";
|
|
183433
|
+
import * as path17 from "path";
|
|
183436
183434
|
|
|
183437
183435
|
// node_modules/.pnpm/@specific+sdk@file+..+sdk/node_modules/@specific/sdk/dist/index.js
|
|
183438
183436
|
import * as fs from "fs";
|
|
@@ -183442,9 +183440,8 @@ import { execSync } from "child_process";
|
|
|
183442
183440
|
import * as fs2 from "fs";
|
|
183443
183441
|
import * as path2 from "path";
|
|
183444
183442
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
183445
|
-
import * as
|
|
183446
|
-
import * as
|
|
183447
|
-
import * as path16 from "path";
|
|
183443
|
+
import * as fs12 from "fs";
|
|
183444
|
+
import * as path13 from "path";
|
|
183448
183445
|
|
|
183449
183446
|
// node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
183450
183447
|
import { EventEmitter } from "node:events";
|
|
@@ -183536,7 +183533,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
183536
183533
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
183537
183534
|
const statMethod = opts.lstat ? lstat : stat;
|
|
183538
183535
|
if (wantBigintFsStats) {
|
|
183539
|
-
this._stat = (
|
|
183536
|
+
this._stat = (path26) => statMethod(path26, { bigint: true });
|
|
183540
183537
|
} else {
|
|
183541
183538
|
this._stat = statMethod;
|
|
183542
183539
|
}
|
|
@@ -183561,8 +183558,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
183561
183558
|
const par = this.parent;
|
|
183562
183559
|
const fil = par && par.files;
|
|
183563
183560
|
if (fil && fil.length > 0) {
|
|
183564
|
-
const { path:
|
|
183565
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
183561
|
+
const { path: path26, depth } = par;
|
|
183562
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
|
|
183566
183563
|
const awaited = await Promise.all(slice);
|
|
183567
183564
|
for (const entry of awaited) {
|
|
183568
183565
|
if (!entry)
|
|
@@ -183602,20 +183599,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
183602
183599
|
this.reading = false;
|
|
183603
183600
|
}
|
|
183604
183601
|
}
|
|
183605
|
-
async _exploreDir(
|
|
183602
|
+
async _exploreDir(path26, depth) {
|
|
183606
183603
|
let files;
|
|
183607
183604
|
try {
|
|
183608
|
-
files = await readdir(
|
|
183605
|
+
files = await readdir(path26, this._rdOptions);
|
|
183609
183606
|
} catch (error) {
|
|
183610
183607
|
this._onError(error);
|
|
183611
183608
|
}
|
|
183612
|
-
return { files, depth, path:
|
|
183609
|
+
return { files, depth, path: path26 };
|
|
183613
183610
|
}
|
|
183614
|
-
async _formatEntry(dirent,
|
|
183611
|
+
async _formatEntry(dirent, path26) {
|
|
183615
183612
|
let entry;
|
|
183616
183613
|
const basename6 = this._isDirent ? dirent.name : dirent;
|
|
183617
183614
|
try {
|
|
183618
|
-
const fullPath = presolve(pjoin(
|
|
183615
|
+
const fullPath = presolve(pjoin(path26, basename6));
|
|
183619
183616
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
|
|
183620
183617
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
183621
183618
|
} catch (err) {
|
|
@@ -184015,16 +184012,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
184015
184012
|
};
|
|
184016
184013
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
184017
184014
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
184018
|
-
function createFsWatchInstance(
|
|
184015
|
+
function createFsWatchInstance(path26, options2, listener, errHandler, emitRaw) {
|
|
184019
184016
|
const handleEvent = (rawEvent, evPath) => {
|
|
184020
|
-
listener(
|
|
184021
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
184022
|
-
if (evPath &&
|
|
184023
|
-
fsWatchBroadcast(sp.resolve(
|
|
184017
|
+
listener(path26);
|
|
184018
|
+
emitRaw(rawEvent, evPath, { watchedPath: path26 });
|
|
184019
|
+
if (evPath && path26 !== evPath) {
|
|
184020
|
+
fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
|
|
184024
184021
|
}
|
|
184025
184022
|
};
|
|
184026
184023
|
try {
|
|
184027
|
-
return fs_watch(
|
|
184024
|
+
return fs_watch(path26, {
|
|
184028
184025
|
persistent: options2.persistent
|
|
184029
184026
|
}, handleEvent);
|
|
184030
184027
|
} catch (error) {
|
|
@@ -184040,12 +184037,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
184040
184037
|
listener(val1, val2, val3);
|
|
184041
184038
|
});
|
|
184042
184039
|
};
|
|
184043
|
-
var setFsWatchListener = (
|
|
184040
|
+
var setFsWatchListener = (path26, fullPath, options2, handlers) => {
|
|
184044
184041
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
184045
184042
|
let cont = FsWatchInstances.get(fullPath);
|
|
184046
184043
|
let watcher;
|
|
184047
184044
|
if (!options2.persistent) {
|
|
184048
|
-
watcher = createFsWatchInstance(
|
|
184045
|
+
watcher = createFsWatchInstance(path26, options2, listener, errHandler, rawEmitter);
|
|
184049
184046
|
if (!watcher)
|
|
184050
184047
|
return;
|
|
184051
184048
|
return watcher.close.bind(watcher);
|
|
@@ -184056,7 +184053,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
|
|
|
184056
184053
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
184057
184054
|
} else {
|
|
184058
184055
|
watcher = createFsWatchInstance(
|
|
184059
|
-
|
|
184056
|
+
path26,
|
|
184060
184057
|
options2,
|
|
184061
184058
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
184062
184059
|
errHandler,
|
|
@@ -184071,7 +184068,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
|
|
|
184071
184068
|
cont.watcherUnusable = true;
|
|
184072
184069
|
if (isWindows && error.code === "EPERM") {
|
|
184073
184070
|
try {
|
|
184074
|
-
const fd = await open(
|
|
184071
|
+
const fd = await open(path26, "r");
|
|
184075
184072
|
await fd.close();
|
|
184076
184073
|
broadcastErr(error);
|
|
184077
184074
|
} catch (err) {
|
|
@@ -184102,7 +184099,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
|
|
|
184102
184099
|
};
|
|
184103
184100
|
};
|
|
184104
184101
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
184105
|
-
var setFsWatchFileListener = (
|
|
184102
|
+
var setFsWatchFileListener = (path26, fullPath, options2, handlers) => {
|
|
184106
184103
|
const { listener, rawEmitter } = handlers;
|
|
184107
184104
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
184108
184105
|
const copts = cont && cont.options;
|
|
@@ -184124,7 +184121,7 @@ var setFsWatchFileListener = (path30, fullPath, options2, handlers) => {
|
|
|
184124
184121
|
});
|
|
184125
184122
|
const currmtime = curr.mtimeMs;
|
|
184126
184123
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
184127
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
184124
|
+
foreach(cont.listeners, (listener2) => listener2(path26, curr));
|
|
184128
184125
|
}
|
|
184129
184126
|
})
|
|
184130
184127
|
};
|
|
@@ -184154,13 +184151,13 @@ var NodeFsHandler = class {
|
|
|
184154
184151
|
* @param listener on fs change
|
|
184155
184152
|
* @returns closer for the watcher instance
|
|
184156
184153
|
*/
|
|
184157
|
-
_watchWithNodeFs(
|
|
184154
|
+
_watchWithNodeFs(path26, listener) {
|
|
184158
184155
|
const opts = this.fsw.options;
|
|
184159
|
-
const directory = sp.dirname(
|
|
184160
|
-
const basename6 = sp.basename(
|
|
184156
|
+
const directory = sp.dirname(path26);
|
|
184157
|
+
const basename6 = sp.basename(path26);
|
|
184161
184158
|
const parent = this.fsw._getWatchedDir(directory);
|
|
184162
184159
|
parent.add(basename6);
|
|
184163
|
-
const absolutePath = sp.resolve(
|
|
184160
|
+
const absolutePath = sp.resolve(path26);
|
|
184164
184161
|
const options2 = {
|
|
184165
184162
|
persistent: opts.persistent
|
|
184166
184163
|
};
|
|
@@ -184170,12 +184167,12 @@ var NodeFsHandler = class {
|
|
|
184170
184167
|
if (opts.usePolling) {
|
|
184171
184168
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
184172
184169
|
options2.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
|
|
184173
|
-
closer = setFsWatchFileListener(
|
|
184170
|
+
closer = setFsWatchFileListener(path26, absolutePath, options2, {
|
|
184174
184171
|
listener,
|
|
184175
184172
|
rawEmitter: this.fsw._emitRaw
|
|
184176
184173
|
});
|
|
184177
184174
|
} else {
|
|
184178
|
-
closer = setFsWatchListener(
|
|
184175
|
+
closer = setFsWatchListener(path26, absolutePath, options2, {
|
|
184179
184176
|
listener,
|
|
184180
184177
|
errHandler: this._boundHandleError,
|
|
184181
184178
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -184197,7 +184194,7 @@ var NodeFsHandler = class {
|
|
|
184197
184194
|
let prevStats = stats;
|
|
184198
184195
|
if (parent.has(basename6))
|
|
184199
184196
|
return;
|
|
184200
|
-
const listener = async (
|
|
184197
|
+
const listener = async (path26, newStats) => {
|
|
184201
184198
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
184202
184199
|
return;
|
|
184203
184200
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -184211,11 +184208,11 @@ var NodeFsHandler = class {
|
|
|
184211
184208
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
184212
184209
|
}
|
|
184213
184210
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
184214
|
-
this.fsw._closeFile(
|
|
184211
|
+
this.fsw._closeFile(path26);
|
|
184215
184212
|
prevStats = newStats2;
|
|
184216
184213
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
184217
184214
|
if (closer2)
|
|
184218
|
-
this.fsw._addPathCloser(
|
|
184215
|
+
this.fsw._addPathCloser(path26, closer2);
|
|
184219
184216
|
} else {
|
|
184220
184217
|
prevStats = newStats2;
|
|
184221
184218
|
}
|
|
@@ -184247,7 +184244,7 @@ var NodeFsHandler = class {
|
|
|
184247
184244
|
* @param item basename of this item
|
|
184248
184245
|
* @returns true if no more processing is needed for this entry.
|
|
184249
184246
|
*/
|
|
184250
|
-
async _handleSymlink(entry, directory,
|
|
184247
|
+
async _handleSymlink(entry, directory, path26, item) {
|
|
184251
184248
|
if (this.fsw.closed) {
|
|
184252
184249
|
return;
|
|
184253
184250
|
}
|
|
@@ -184257,7 +184254,7 @@ var NodeFsHandler = class {
|
|
|
184257
184254
|
this.fsw._incrReadyCount();
|
|
184258
184255
|
let linkPath;
|
|
184259
184256
|
try {
|
|
184260
|
-
linkPath = await fsrealpath(
|
|
184257
|
+
linkPath = await fsrealpath(path26);
|
|
184261
184258
|
} catch (e) {
|
|
184262
184259
|
this.fsw._emitReady();
|
|
184263
184260
|
return true;
|
|
@@ -184267,12 +184264,12 @@ var NodeFsHandler = class {
|
|
|
184267
184264
|
if (dir.has(item)) {
|
|
184268
184265
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
184269
184266
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184270
|
-
this.fsw._emit(EV.CHANGE,
|
|
184267
|
+
this.fsw._emit(EV.CHANGE, path26, entry.stats);
|
|
184271
184268
|
}
|
|
184272
184269
|
} else {
|
|
184273
184270
|
dir.add(item);
|
|
184274
184271
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184275
|
-
this.fsw._emit(EV.ADD,
|
|
184272
|
+
this.fsw._emit(EV.ADD, path26, entry.stats);
|
|
184276
184273
|
}
|
|
184277
184274
|
this.fsw._emitReady();
|
|
184278
184275
|
return true;
|
|
@@ -184302,9 +184299,9 @@ var NodeFsHandler = class {
|
|
|
184302
184299
|
return;
|
|
184303
184300
|
}
|
|
184304
184301
|
const item = entry.path;
|
|
184305
|
-
let
|
|
184302
|
+
let path26 = sp.join(directory, item);
|
|
184306
184303
|
current.add(item);
|
|
184307
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
184304
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
|
|
184308
184305
|
return;
|
|
184309
184306
|
}
|
|
184310
184307
|
if (this.fsw.closed) {
|
|
@@ -184313,8 +184310,8 @@ var NodeFsHandler = class {
|
|
|
184313
184310
|
}
|
|
184314
184311
|
if (item === target || !target && !previous.has(item)) {
|
|
184315
184312
|
this.fsw._incrReadyCount();
|
|
184316
|
-
|
|
184317
|
-
this._addToNodeFs(
|
|
184313
|
+
path26 = sp.join(dir, sp.relative(dir, path26));
|
|
184314
|
+
this._addToNodeFs(path26, initialAdd, wh, depth + 1);
|
|
184318
184315
|
}
|
|
184319
184316
|
}).on(EV.ERROR, this._boundHandleError);
|
|
184320
184317
|
return new Promise((resolve9, reject) => {
|
|
@@ -184383,13 +184380,13 @@ var NodeFsHandler = class {
|
|
|
184383
184380
|
* @param depth Child path actually targeted for watch
|
|
184384
184381
|
* @param target Child path actually targeted for watch
|
|
184385
184382
|
*/
|
|
184386
|
-
async _addToNodeFs(
|
|
184383
|
+
async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
|
|
184387
184384
|
const ready = this.fsw._emitReady;
|
|
184388
|
-
if (this.fsw._isIgnored(
|
|
184385
|
+
if (this.fsw._isIgnored(path26) || this.fsw.closed) {
|
|
184389
184386
|
ready();
|
|
184390
184387
|
return false;
|
|
184391
184388
|
}
|
|
184392
|
-
const wh = this.fsw._getWatchHelpers(
|
|
184389
|
+
const wh = this.fsw._getWatchHelpers(path26);
|
|
184393
184390
|
if (priorWh) {
|
|
184394
184391
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
184395
184392
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -184405,8 +184402,8 @@ var NodeFsHandler = class {
|
|
|
184405
184402
|
const follow = this.fsw.options.followSymlinks;
|
|
184406
184403
|
let closer;
|
|
184407
184404
|
if (stats.isDirectory()) {
|
|
184408
|
-
const absPath = sp.resolve(
|
|
184409
|
-
const targetPath = follow ? await fsrealpath(
|
|
184405
|
+
const absPath = sp.resolve(path26);
|
|
184406
|
+
const targetPath = follow ? await fsrealpath(path26) : path26;
|
|
184410
184407
|
if (this.fsw.closed)
|
|
184411
184408
|
return;
|
|
184412
184409
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -184416,29 +184413,29 @@ var NodeFsHandler = class {
|
|
|
184416
184413
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
184417
184414
|
}
|
|
184418
184415
|
} else if (stats.isSymbolicLink()) {
|
|
184419
|
-
const targetPath = follow ? await fsrealpath(
|
|
184416
|
+
const targetPath = follow ? await fsrealpath(path26) : path26;
|
|
184420
184417
|
if (this.fsw.closed)
|
|
184421
184418
|
return;
|
|
184422
184419
|
const parent = sp.dirname(wh.watchPath);
|
|
184423
184420
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
184424
184421
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
184425
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
184422
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
|
|
184426
184423
|
if (this.fsw.closed)
|
|
184427
184424
|
return;
|
|
184428
184425
|
if (targetPath !== void 0) {
|
|
184429
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
184426
|
+
this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
|
|
184430
184427
|
}
|
|
184431
184428
|
} else {
|
|
184432
184429
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
184433
184430
|
}
|
|
184434
184431
|
ready();
|
|
184435
184432
|
if (closer)
|
|
184436
|
-
this.fsw._addPathCloser(
|
|
184433
|
+
this.fsw._addPathCloser(path26, closer);
|
|
184437
184434
|
return false;
|
|
184438
184435
|
} catch (error) {
|
|
184439
184436
|
if (this.fsw._handleError(error)) {
|
|
184440
184437
|
ready();
|
|
184441
|
-
return
|
|
184438
|
+
return path26;
|
|
184442
184439
|
}
|
|
184443
184440
|
}
|
|
184444
184441
|
}
|
|
@@ -184481,24 +184478,24 @@ function createPattern(matcher) {
|
|
|
184481
184478
|
}
|
|
184482
184479
|
return () => false;
|
|
184483
184480
|
}
|
|
184484
|
-
function normalizePath(
|
|
184485
|
-
if (typeof
|
|
184481
|
+
function normalizePath(path26) {
|
|
184482
|
+
if (typeof path26 !== "string")
|
|
184486
184483
|
throw new Error("string expected");
|
|
184487
|
-
|
|
184488
|
-
|
|
184484
|
+
path26 = sp2.normalize(path26);
|
|
184485
|
+
path26 = path26.replace(/\\/g, "/");
|
|
184489
184486
|
let prepend = false;
|
|
184490
|
-
if (
|
|
184487
|
+
if (path26.startsWith("//"))
|
|
184491
184488
|
prepend = true;
|
|
184492
|
-
|
|
184489
|
+
path26 = path26.replace(DOUBLE_SLASH_RE, "/");
|
|
184493
184490
|
if (prepend)
|
|
184494
|
-
|
|
184495
|
-
return
|
|
184491
|
+
path26 = "/" + path26;
|
|
184492
|
+
return path26;
|
|
184496
184493
|
}
|
|
184497
184494
|
function matchPatterns(patterns, testString, stats) {
|
|
184498
|
-
const
|
|
184495
|
+
const path26 = normalizePath(testString);
|
|
184499
184496
|
for (let index = 0; index < patterns.length; index++) {
|
|
184500
184497
|
const pattern = patterns[index];
|
|
184501
|
-
if (pattern(
|
|
184498
|
+
if (pattern(path26, stats)) {
|
|
184502
184499
|
return true;
|
|
184503
184500
|
}
|
|
184504
184501
|
}
|
|
@@ -184536,19 +184533,19 @@ var toUnix = (string) => {
|
|
|
184536
184533
|
}
|
|
184537
184534
|
return str;
|
|
184538
184535
|
};
|
|
184539
|
-
var normalizePathToUnix = (
|
|
184540
|
-
var normalizeIgnored = (cwd = "") => (
|
|
184541
|
-
if (typeof
|
|
184542
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
184536
|
+
var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
|
|
184537
|
+
var normalizeIgnored = (cwd = "") => (path26) => {
|
|
184538
|
+
if (typeof path26 === "string") {
|
|
184539
|
+
return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
|
|
184543
184540
|
} else {
|
|
184544
|
-
return
|
|
184541
|
+
return path26;
|
|
184545
184542
|
}
|
|
184546
184543
|
};
|
|
184547
|
-
var getAbsolutePath = (
|
|
184548
|
-
if (sp2.isAbsolute(
|
|
184549
|
-
return
|
|
184544
|
+
var getAbsolutePath = (path26, cwd) => {
|
|
184545
|
+
if (sp2.isAbsolute(path26)) {
|
|
184546
|
+
return path26;
|
|
184550
184547
|
}
|
|
184551
|
-
return sp2.join(cwd,
|
|
184548
|
+
return sp2.join(cwd, path26);
|
|
184552
184549
|
};
|
|
184553
184550
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
184554
184551
|
var DirEntry = class {
|
|
@@ -184613,10 +184610,10 @@ var WatchHelper = class {
|
|
|
184613
184610
|
dirParts;
|
|
184614
184611
|
followSymlinks;
|
|
184615
184612
|
statMethod;
|
|
184616
|
-
constructor(
|
|
184613
|
+
constructor(path26, follow, fsw) {
|
|
184617
184614
|
this.fsw = fsw;
|
|
184618
|
-
const watchPath =
|
|
184619
|
-
this.path =
|
|
184615
|
+
const watchPath = path26;
|
|
184616
|
+
this.path = path26 = path26.replace(REPLACER_RE, "");
|
|
184620
184617
|
this.watchPath = watchPath;
|
|
184621
184618
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
184622
184619
|
this.dirParts = [];
|
|
@@ -184756,20 +184753,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184756
184753
|
this._closePromise = void 0;
|
|
184757
184754
|
let paths = unifyPaths(paths_);
|
|
184758
184755
|
if (cwd) {
|
|
184759
|
-
paths = paths.map((
|
|
184760
|
-
const absPath = getAbsolutePath(
|
|
184756
|
+
paths = paths.map((path26) => {
|
|
184757
|
+
const absPath = getAbsolutePath(path26, cwd);
|
|
184761
184758
|
return absPath;
|
|
184762
184759
|
});
|
|
184763
184760
|
}
|
|
184764
|
-
paths.forEach((
|
|
184765
|
-
this._removeIgnoredPath(
|
|
184761
|
+
paths.forEach((path26) => {
|
|
184762
|
+
this._removeIgnoredPath(path26);
|
|
184766
184763
|
});
|
|
184767
184764
|
this._userIgnored = void 0;
|
|
184768
184765
|
if (!this._readyCount)
|
|
184769
184766
|
this._readyCount = 0;
|
|
184770
184767
|
this._readyCount += paths.length;
|
|
184771
|
-
Promise.all(paths.map(async (
|
|
184772
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
184768
|
+
Promise.all(paths.map(async (path26) => {
|
|
184769
|
+
const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
|
|
184773
184770
|
if (res)
|
|
184774
184771
|
this._emitReady();
|
|
184775
184772
|
return res;
|
|
@@ -184791,17 +184788,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184791
184788
|
return this;
|
|
184792
184789
|
const paths = unifyPaths(paths_);
|
|
184793
184790
|
const { cwd } = this.options;
|
|
184794
|
-
paths.forEach((
|
|
184795
|
-
if (!sp2.isAbsolute(
|
|
184791
|
+
paths.forEach((path26) => {
|
|
184792
|
+
if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
|
|
184796
184793
|
if (cwd)
|
|
184797
|
-
|
|
184798
|
-
|
|
184794
|
+
path26 = sp2.join(cwd, path26);
|
|
184795
|
+
path26 = sp2.resolve(path26);
|
|
184799
184796
|
}
|
|
184800
|
-
this._closePath(
|
|
184801
|
-
this._addIgnoredPath(
|
|
184802
|
-
if (this._watched.has(
|
|
184797
|
+
this._closePath(path26);
|
|
184798
|
+
this._addIgnoredPath(path26);
|
|
184799
|
+
if (this._watched.has(path26)) {
|
|
184803
184800
|
this._addIgnoredPath({
|
|
184804
|
-
path:
|
|
184801
|
+
path: path26,
|
|
184805
184802
|
recursive: true
|
|
184806
184803
|
});
|
|
184807
184804
|
}
|
|
@@ -184865,38 +184862,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184865
184862
|
* @param stats arguments to be passed with event
|
|
184866
184863
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
184867
184864
|
*/
|
|
184868
|
-
async _emit(event,
|
|
184865
|
+
async _emit(event, path26, stats) {
|
|
184869
184866
|
if (this.closed)
|
|
184870
184867
|
return;
|
|
184871
184868
|
const opts = this.options;
|
|
184872
184869
|
if (isWindows)
|
|
184873
|
-
|
|
184870
|
+
path26 = sp2.normalize(path26);
|
|
184874
184871
|
if (opts.cwd)
|
|
184875
|
-
|
|
184876
|
-
const args = [
|
|
184872
|
+
path26 = sp2.relative(opts.cwd, path26);
|
|
184873
|
+
const args = [path26];
|
|
184877
184874
|
if (stats != null)
|
|
184878
184875
|
args.push(stats);
|
|
184879
184876
|
const awf = opts.awaitWriteFinish;
|
|
184880
184877
|
let pw;
|
|
184881
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
184878
|
+
if (awf && (pw = this._pendingWrites.get(path26))) {
|
|
184882
184879
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
184883
184880
|
return this;
|
|
184884
184881
|
}
|
|
184885
184882
|
if (opts.atomic) {
|
|
184886
184883
|
if (event === EVENTS.UNLINK) {
|
|
184887
|
-
this._pendingUnlinks.set(
|
|
184884
|
+
this._pendingUnlinks.set(path26, [event, ...args]);
|
|
184888
184885
|
setTimeout(() => {
|
|
184889
|
-
this._pendingUnlinks.forEach((entry,
|
|
184886
|
+
this._pendingUnlinks.forEach((entry, path27) => {
|
|
184890
184887
|
this.emit(...entry);
|
|
184891
184888
|
this.emit(EVENTS.ALL, ...entry);
|
|
184892
|
-
this._pendingUnlinks.delete(
|
|
184889
|
+
this._pendingUnlinks.delete(path27);
|
|
184893
184890
|
});
|
|
184894
184891
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
184895
184892
|
return this;
|
|
184896
184893
|
}
|
|
184897
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
184894
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
|
|
184898
184895
|
event = EVENTS.CHANGE;
|
|
184899
|
-
this._pendingUnlinks.delete(
|
|
184896
|
+
this._pendingUnlinks.delete(path26);
|
|
184900
184897
|
}
|
|
184901
184898
|
}
|
|
184902
184899
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -184914,16 +184911,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184914
184911
|
this.emitWithAll(event, args);
|
|
184915
184912
|
}
|
|
184916
184913
|
};
|
|
184917
|
-
this._awaitWriteFinish(
|
|
184914
|
+
this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
|
|
184918
184915
|
return this;
|
|
184919
184916
|
}
|
|
184920
184917
|
if (event === EVENTS.CHANGE) {
|
|
184921
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
184918
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
|
|
184922
184919
|
if (isThrottled)
|
|
184923
184920
|
return this;
|
|
184924
184921
|
}
|
|
184925
184922
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
184926
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
184923
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
|
|
184927
184924
|
let stats2;
|
|
184928
184925
|
try {
|
|
184929
184926
|
stats2 = await stat3(fullPath);
|
|
@@ -184954,23 +184951,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184954
184951
|
* @param timeout duration of time to suppress duplicate actions
|
|
184955
184952
|
* @returns tracking object or false if action should be suppressed
|
|
184956
184953
|
*/
|
|
184957
|
-
_throttle(actionType,
|
|
184954
|
+
_throttle(actionType, path26, timeout) {
|
|
184958
184955
|
if (!this._throttled.has(actionType)) {
|
|
184959
184956
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
184960
184957
|
}
|
|
184961
184958
|
const action = this._throttled.get(actionType);
|
|
184962
184959
|
if (!action)
|
|
184963
184960
|
throw new Error("invalid throttle");
|
|
184964
|
-
const actionPath = action.get(
|
|
184961
|
+
const actionPath = action.get(path26);
|
|
184965
184962
|
if (actionPath) {
|
|
184966
184963
|
actionPath.count++;
|
|
184967
184964
|
return false;
|
|
184968
184965
|
}
|
|
184969
184966
|
let timeoutObject;
|
|
184970
184967
|
const clear = () => {
|
|
184971
|
-
const item = action.get(
|
|
184968
|
+
const item = action.get(path26);
|
|
184972
184969
|
const count = item ? item.count : 0;
|
|
184973
|
-
action.delete(
|
|
184970
|
+
action.delete(path26);
|
|
184974
184971
|
clearTimeout(timeoutObject);
|
|
184975
184972
|
if (item)
|
|
184976
184973
|
clearTimeout(item.timeoutObject);
|
|
@@ -184978,7 +184975,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184978
184975
|
};
|
|
184979
184976
|
timeoutObject = setTimeout(clear, timeout);
|
|
184980
184977
|
const thr = { timeoutObject, clear, count: 0 };
|
|
184981
|
-
action.set(
|
|
184978
|
+
action.set(path26, thr);
|
|
184982
184979
|
return thr;
|
|
184983
184980
|
}
|
|
184984
184981
|
_incrReadyCount() {
|
|
@@ -184992,44 +184989,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184992
184989
|
* @param event
|
|
184993
184990
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
184994
184991
|
*/
|
|
184995
|
-
_awaitWriteFinish(
|
|
184992
|
+
_awaitWriteFinish(path26, threshold, event, awfEmit) {
|
|
184996
184993
|
const awf = this.options.awaitWriteFinish;
|
|
184997
184994
|
if (typeof awf !== "object")
|
|
184998
184995
|
return;
|
|
184999
184996
|
const pollInterval = awf.pollInterval;
|
|
185000
184997
|
let timeoutHandler;
|
|
185001
|
-
let fullPath =
|
|
185002
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
185003
|
-
fullPath = sp2.join(this.options.cwd,
|
|
184998
|
+
let fullPath = path26;
|
|
184999
|
+
if (this.options.cwd && !sp2.isAbsolute(path26)) {
|
|
185000
|
+
fullPath = sp2.join(this.options.cwd, path26);
|
|
185004
185001
|
}
|
|
185005
185002
|
const now = /* @__PURE__ */ new Date();
|
|
185006
185003
|
const writes = this._pendingWrites;
|
|
185007
185004
|
function awaitWriteFinishFn(prevStat) {
|
|
185008
185005
|
statcb(fullPath, (err, curStat) => {
|
|
185009
|
-
if (err || !writes.has(
|
|
185006
|
+
if (err || !writes.has(path26)) {
|
|
185010
185007
|
if (err && err.code !== "ENOENT")
|
|
185011
185008
|
awfEmit(err);
|
|
185012
185009
|
return;
|
|
185013
185010
|
}
|
|
185014
185011
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
185015
185012
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
185016
|
-
writes.get(
|
|
185013
|
+
writes.get(path26).lastChange = now2;
|
|
185017
185014
|
}
|
|
185018
|
-
const pw = writes.get(
|
|
185015
|
+
const pw = writes.get(path26);
|
|
185019
185016
|
const df = now2 - pw.lastChange;
|
|
185020
185017
|
if (df >= threshold) {
|
|
185021
|
-
writes.delete(
|
|
185018
|
+
writes.delete(path26);
|
|
185022
185019
|
awfEmit(void 0, curStat);
|
|
185023
185020
|
} else {
|
|
185024
185021
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
185025
185022
|
}
|
|
185026
185023
|
});
|
|
185027
185024
|
}
|
|
185028
|
-
if (!writes.has(
|
|
185029
|
-
writes.set(
|
|
185025
|
+
if (!writes.has(path26)) {
|
|
185026
|
+
writes.set(path26, {
|
|
185030
185027
|
lastChange: now,
|
|
185031
185028
|
cancelWait: () => {
|
|
185032
|
-
writes.delete(
|
|
185029
|
+
writes.delete(path26);
|
|
185033
185030
|
clearTimeout(timeoutHandler);
|
|
185034
185031
|
return event;
|
|
185035
185032
|
}
|
|
@@ -185040,8 +185037,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
185040
185037
|
/**
|
|
185041
185038
|
* Determines whether user has asked to ignore this path.
|
|
185042
185039
|
*/
|
|
185043
|
-
_isIgnored(
|
|
185044
|
-
if (this.options.atomic && DOT_RE.test(
|
|
185040
|
+
_isIgnored(path26, stats) {
|
|
185041
|
+
if (this.options.atomic && DOT_RE.test(path26))
|
|
185045
185042
|
return true;
|
|
185046
185043
|
if (!this._userIgnored) {
|
|
185047
185044
|
const { cwd } = this.options;
|
|
@@ -185051,17 +185048,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
185051
185048
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
185052
185049
|
this._userIgnored = anymatch(list, void 0);
|
|
185053
185050
|
}
|
|
185054
|
-
return this._userIgnored(
|
|
185051
|
+
return this._userIgnored(path26, stats);
|
|
185055
185052
|
}
|
|
185056
|
-
_isntIgnored(
|
|
185057
|
-
return !this._isIgnored(
|
|
185053
|
+
_isntIgnored(path26, stat4) {
|
|
185054
|
+
return !this._isIgnored(path26, stat4);
|
|
185058
185055
|
}
|
|
185059
185056
|
/**
|
|
185060
185057
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
185061
185058
|
* @param path file or directory pattern being watched
|
|
185062
185059
|
*/
|
|
185063
|
-
_getWatchHelpers(
|
|
185064
|
-
return new WatchHelper(
|
|
185060
|
+
_getWatchHelpers(path26) {
|
|
185061
|
+
return new WatchHelper(path26, this.options.followSymlinks, this);
|
|
185065
185062
|
}
|
|
185066
185063
|
// Directory helpers
|
|
185067
185064
|
// -----------------
|
|
@@ -185093,63 +185090,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
185093
185090
|
* @param item base path of item/directory
|
|
185094
185091
|
*/
|
|
185095
185092
|
_remove(directory, item, isDirectory) {
|
|
185096
|
-
const
|
|
185097
|
-
const fullPath = sp2.resolve(
|
|
185098
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
185099
|
-
if (!this._throttle("remove",
|
|
185093
|
+
const path26 = sp2.join(directory, item);
|
|
185094
|
+
const fullPath = sp2.resolve(path26);
|
|
185095
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
|
|
185096
|
+
if (!this._throttle("remove", path26, 100))
|
|
185100
185097
|
return;
|
|
185101
185098
|
if (!isDirectory && this._watched.size === 1) {
|
|
185102
185099
|
this.add(directory, item, true);
|
|
185103
185100
|
}
|
|
185104
|
-
const wp = this._getWatchedDir(
|
|
185101
|
+
const wp = this._getWatchedDir(path26);
|
|
185105
185102
|
const nestedDirectoryChildren = wp.getChildren();
|
|
185106
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
185103
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
|
|
185107
185104
|
const parent = this._getWatchedDir(directory);
|
|
185108
185105
|
const wasTracked = parent.has(item);
|
|
185109
185106
|
parent.remove(item);
|
|
185110
185107
|
if (this._symlinkPaths.has(fullPath)) {
|
|
185111
185108
|
this._symlinkPaths.delete(fullPath);
|
|
185112
185109
|
}
|
|
185113
|
-
let relPath =
|
|
185110
|
+
let relPath = path26;
|
|
185114
185111
|
if (this.options.cwd)
|
|
185115
|
-
relPath = sp2.relative(this.options.cwd,
|
|
185112
|
+
relPath = sp2.relative(this.options.cwd, path26);
|
|
185116
185113
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
185117
185114
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
185118
185115
|
if (event === EVENTS.ADD)
|
|
185119
185116
|
return;
|
|
185120
185117
|
}
|
|
185121
|
-
this._watched.delete(
|
|
185118
|
+
this._watched.delete(path26);
|
|
185122
185119
|
this._watched.delete(fullPath);
|
|
185123
185120
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
185124
|
-
if (wasTracked && !this._isIgnored(
|
|
185125
|
-
this._emit(eventName,
|
|
185126
|
-
this._closePath(
|
|
185121
|
+
if (wasTracked && !this._isIgnored(path26))
|
|
185122
|
+
this._emit(eventName, path26);
|
|
185123
|
+
this._closePath(path26);
|
|
185127
185124
|
}
|
|
185128
185125
|
/**
|
|
185129
185126
|
* Closes all watchers for a path
|
|
185130
185127
|
*/
|
|
185131
|
-
_closePath(
|
|
185132
|
-
this._closeFile(
|
|
185133
|
-
const dir = sp2.dirname(
|
|
185134
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
185128
|
+
_closePath(path26) {
|
|
185129
|
+
this._closeFile(path26);
|
|
185130
|
+
const dir = sp2.dirname(path26);
|
|
185131
|
+
this._getWatchedDir(dir).remove(sp2.basename(path26));
|
|
185135
185132
|
}
|
|
185136
185133
|
/**
|
|
185137
185134
|
* Closes only file-specific watchers
|
|
185138
185135
|
*/
|
|
185139
|
-
_closeFile(
|
|
185140
|
-
const closers = this._closers.get(
|
|
185136
|
+
_closeFile(path26) {
|
|
185137
|
+
const closers = this._closers.get(path26);
|
|
185141
185138
|
if (!closers)
|
|
185142
185139
|
return;
|
|
185143
185140
|
closers.forEach((closer) => closer());
|
|
185144
|
-
this._closers.delete(
|
|
185141
|
+
this._closers.delete(path26);
|
|
185145
185142
|
}
|
|
185146
|
-
_addPathCloser(
|
|
185143
|
+
_addPathCloser(path26, closer) {
|
|
185147
185144
|
if (!closer)
|
|
185148
185145
|
return;
|
|
185149
|
-
let list = this._closers.get(
|
|
185146
|
+
let list = this._closers.get(path26);
|
|
185150
185147
|
if (!list) {
|
|
185151
185148
|
list = [];
|
|
185152
|
-
this._closers.set(
|
|
185149
|
+
this._closers.set(path26, list);
|
|
185153
185150
|
}
|
|
185154
185151
|
list.push(closer);
|
|
185155
185152
|
}
|
|
@@ -185180,8 +185177,6 @@ var chokidar_default = { watch, FSWatcher };
|
|
|
185180
185177
|
|
|
185181
185178
|
// node_modules/.pnpm/@specific+sdk@file+..+sdk/node_modules/@specific/sdk/dist/index.js
|
|
185182
185179
|
var import_code_frame = __toESM(require_lib2(), 1);
|
|
185183
|
-
import * as fs3 from "fs";
|
|
185184
|
-
import * as path3 from "path";
|
|
185185
185180
|
import * as fs5 from "fs";
|
|
185186
185181
|
import * as path5 from "path";
|
|
185187
185182
|
import * as net from "net";
|
|
@@ -185193,17 +185188,14 @@ import { createReadStream } from "fs";
|
|
|
185193
185188
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185194
185189
|
import { spawn as spawn2 } from "child_process";
|
|
185195
185190
|
import { readFile, writeFile } from "fs/promises";
|
|
185196
|
-
import { existsSync as
|
|
185191
|
+
import { existsSync as existsSync5 } from "fs";
|
|
185197
185192
|
import * as fs6 from "fs";
|
|
185198
185193
|
import * as path6 from "path";
|
|
185199
185194
|
import * as http from "http";
|
|
185200
|
-
import * as https from "https";
|
|
185201
185195
|
import * as fs7 from "fs";
|
|
185202
185196
|
import * as path7 from "path";
|
|
185203
185197
|
import { fileURLToPath } from "url";
|
|
185204
|
-
import httpProxy from "http-proxy";
|
|
185205
185198
|
import * as net2 from "net";
|
|
185206
|
-
import * as net3 from "net";
|
|
185207
185199
|
import { spawn as spawn3 } from "child_process";
|
|
185208
185200
|
import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
|
|
185209
185201
|
import { existsSync as existsSync8 } from "fs";
|
|
@@ -185211,7 +185203,7 @@ import * as path8 from "path";
|
|
|
185211
185203
|
import * as crypto from "crypto";
|
|
185212
185204
|
import * as http2 from "http";
|
|
185213
185205
|
import * as crypto2 from "crypto";
|
|
185214
|
-
import * as
|
|
185206
|
+
import * as net3 from "net";
|
|
185215
185207
|
import * as fs8 from "fs";
|
|
185216
185208
|
import * as path9 from "path";
|
|
185217
185209
|
import { spawn as spawn4 } from "child_process";
|
|
@@ -185221,32 +185213,17 @@ import * as path10 from "path";
|
|
|
185221
185213
|
import { spawnSync } from "child_process";
|
|
185222
185214
|
import * as fs10 from "fs";
|
|
185223
185215
|
import * as path11 from "path";
|
|
185224
|
-
import * as
|
|
185216
|
+
import * as net4 from "net";
|
|
185225
185217
|
import { spawn as spawn5 } from "child_process";
|
|
185218
|
+
import * as net5 from "net";
|
|
185226
185219
|
import * as fs11 from "fs";
|
|
185227
185220
|
import * as path12 from "path";
|
|
185228
|
-
import * as os3 from "os";
|
|
185229
|
-
import forge from "node-forge";
|
|
185230
|
-
import { execSync as execSync2 } from "child_process";
|
|
185231
|
-
import DNS from "dns2";
|
|
185232
|
-
import { execSync as execSync3 } from "child_process";
|
|
185233
|
-
import * as fs13 from "fs";
|
|
185234
|
-
import * as os5 from "os";
|
|
185235
|
-
import * as path13 from "path";
|
|
185236
|
-
import * as fs12 from "fs";
|
|
185237
|
-
import * as os4 from "os";
|
|
185238
|
-
import * as fs14 from "fs";
|
|
185239
|
-
import * as path14 from "path";
|
|
185240
185221
|
import { generateSlug } from "random-word-slugs";
|
|
185241
185222
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
185242
185223
|
import * as net6 from "node:net";
|
|
185243
|
-
import * as fs15 from "fs";
|
|
185244
|
-
import * as path15 from "path";
|
|
185245
|
-
import * as os6 from "os";
|
|
185246
|
-
import * as net7 from "net";
|
|
185247
185224
|
import { EventEmitter as EventEmitter22 } from "events";
|
|
185248
|
-
import { execSync as
|
|
185249
|
-
import * as
|
|
185225
|
+
import { execSync as execSync2 } from "child_process";
|
|
185226
|
+
import * as path14 from "path";
|
|
185250
185227
|
var __create2 = Object.create;
|
|
185251
185228
|
var __defProp2 = Object.defineProperty;
|
|
185252
185229
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
@@ -185287,8 +185264,8 @@ var require_dist = __commonJS2({
|
|
|
185287
185264
|
var $global, $module, $NaN = NaN;
|
|
185288
185265
|
if ("undefined" != typeof window ? $global = window : "undefined" != typeof self ? $global = self : "undefined" != typeof global ? ($global = global).require = __require2 : $global = this, void 0 === $global || void 0 === $global.Array) throw new Error("no global object found");
|
|
185289
185266
|
if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
|
|
185290
|
-
var
|
|
185291
|
-
"object" == typeof
|
|
185267
|
+
var fs132 = $global.require("fs");
|
|
185268
|
+
"object" == typeof fs132 && null !== fs132 && 0 !== Object.keys(fs132).length && ($global.fs = fs132);
|
|
185292
185269
|
} catch (e) {
|
|
185293
185270
|
}
|
|
185294
185271
|
if (!$global.fs) {
|
|
@@ -368888,68 +368865,6 @@ var PortAllocator = class {
|
|
|
368888
368865
|
return port;
|
|
368889
368866
|
}
|
|
368890
368867
|
};
|
|
368891
|
-
var PORT_RANGE_START2 = 4e4;
|
|
368892
|
-
var PORT_RANGE_END2 = 49999;
|
|
368893
|
-
var StablePortAllocator = class {
|
|
368894
|
-
portsDir;
|
|
368895
|
-
portsFilePath;
|
|
368896
|
-
savedPorts = {};
|
|
368897
|
-
usedPorts = /* @__PURE__ */ new Set();
|
|
368898
|
-
constructor(projectRoot, key = "default") {
|
|
368899
|
-
this.portsDir = path3.join(projectRoot, ".specific", "keys", key);
|
|
368900
|
-
this.portsFilePath = path3.join(this.portsDir, "ports.json");
|
|
368901
|
-
this.loadPorts();
|
|
368902
|
-
}
|
|
368903
|
-
loadPorts() {
|
|
368904
|
-
if (!fs3.existsSync(this.portsFilePath)) {
|
|
368905
|
-
return;
|
|
368906
|
-
}
|
|
368907
|
-
try {
|
|
368908
|
-
const content = fs3.readFileSync(this.portsFilePath, "utf-8");
|
|
368909
|
-
const data = JSON.parse(content);
|
|
368910
|
-
if (data.version === 1 && data.ports) {
|
|
368911
|
-
this.savedPorts = data.ports;
|
|
368912
|
-
for (const port of Object.values(this.savedPorts)) {
|
|
368913
|
-
this.usedPorts.add(port);
|
|
368914
|
-
}
|
|
368915
|
-
}
|
|
368916
|
-
} catch {
|
|
368917
|
-
this.savedPorts = {};
|
|
368918
|
-
}
|
|
368919
|
-
}
|
|
368920
|
-
savePorts() {
|
|
368921
|
-
if (!fs3.existsSync(this.portsDir)) {
|
|
368922
|
-
fs3.mkdirSync(this.portsDir, { recursive: true });
|
|
368923
|
-
}
|
|
368924
|
-
const data = {
|
|
368925
|
-
version: 1,
|
|
368926
|
-
ports: this.savedPorts
|
|
368927
|
-
};
|
|
368928
|
-
fs3.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
368929
|
-
}
|
|
368930
|
-
allocateRandom() {
|
|
368931
|
-
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
368932
|
-
if (this.usedPorts.size >= rangeSize) {
|
|
368933
|
-
throw new Error("No available ports in range");
|
|
368934
|
-
}
|
|
368935
|
-
let port;
|
|
368936
|
-
do {
|
|
368937
|
-
port = PORT_RANGE_START2 + Math.floor(Math.random() * rangeSize);
|
|
368938
|
-
} while (this.usedPorts.has(port));
|
|
368939
|
-
this.usedPorts.add(port);
|
|
368940
|
-
return port;
|
|
368941
|
-
}
|
|
368942
|
-
allocate(key) {
|
|
368943
|
-
const savedPort = this.savedPorts[key];
|
|
368944
|
-
if (savedPort !== void 0) {
|
|
368945
|
-
return savedPort;
|
|
368946
|
-
}
|
|
368947
|
-
const port = this.allocateRandom();
|
|
368948
|
-
this.savedPorts[key] = port;
|
|
368949
|
-
this.savePorts();
|
|
368950
|
-
return port;
|
|
368951
|
-
}
|
|
368952
|
-
};
|
|
368953
368868
|
var BinaryNotFoundError = class extends Error {
|
|
368954
368869
|
constructor(binaryName, version, platformInfo) {
|
|
368955
368870
|
super(
|
|
@@ -368986,10 +368901,10 @@ function getLibraryEnv(binary) {
|
|
|
368986
368901
|
if (!binary.libraryPath) {
|
|
368987
368902
|
return {};
|
|
368988
368903
|
}
|
|
368989
|
-
const
|
|
368990
|
-
if (
|
|
368904
|
+
const platform22 = os2.platform();
|
|
368905
|
+
if (platform22 === "darwin") {
|
|
368991
368906
|
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
368992
|
-
} else if (
|
|
368907
|
+
} else if (platform22 === "linux") {
|
|
368993
368908
|
return { LD_LIBRARY_PATH: binary.libraryPath };
|
|
368994
368909
|
}
|
|
368995
368910
|
return {};
|
|
@@ -368998,11 +368913,11 @@ function getBinBaseDir() {
|
|
|
368998
368913
|
return path4.join(os2.homedir(), ".specific", "bin");
|
|
368999
368914
|
}
|
|
369000
368915
|
function getPlatformInfo() {
|
|
369001
|
-
const
|
|
368916
|
+
const platform22 = os2.platform();
|
|
369002
368917
|
const arch22 = os2.arch();
|
|
369003
|
-
if (
|
|
368918
|
+
if (platform22 !== "darwin" && platform22 !== "linux") {
|
|
369004
368919
|
throw new Error(
|
|
369005
|
-
`Unsupported platform: ${
|
|
368920
|
+
`Unsupported platform: ${platform22}. Only macOS and Linux are supported.`
|
|
369006
368921
|
);
|
|
369007
368922
|
}
|
|
369008
368923
|
const archStr = arch22;
|
|
@@ -369016,7 +368931,7 @@ function getPlatformInfo() {
|
|
|
369016
368931
|
`Unsupported architecture: ${arch22}. Only x64 and arm64 are supported.`
|
|
369017
368932
|
);
|
|
369018
368933
|
}
|
|
369019
|
-
return { platform:
|
|
368934
|
+
return { platform: platform22, arch: mappedArch };
|
|
369020
368935
|
}
|
|
369021
368936
|
function getBinaryDir(definition, version, platformInfo) {
|
|
369022
368937
|
return path4.join(
|
|
@@ -369547,7 +369462,7 @@ async function parseLocalFile(content) {
|
|
|
369547
369462
|
return { secrets, configs };
|
|
369548
369463
|
}
|
|
369549
369464
|
async function loadLocal() {
|
|
369550
|
-
if (!
|
|
369465
|
+
if (!existsSync5(LOCAL_FILE)) {
|
|
369551
369466
|
return { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
|
|
369552
369467
|
}
|
|
369553
369468
|
const content = await readFile(LOCAL_FILE, "utf-8");
|
|
@@ -369587,7 +369502,7 @@ ${newLine}
|
|
|
369587
369502
|
}
|
|
369588
369503
|
async function saveLocalSecret(name, value) {
|
|
369589
369504
|
let content = "";
|
|
369590
|
-
if (
|
|
369505
|
+
if (existsSync5(LOCAL_FILE)) {
|
|
369591
369506
|
content = await readFile(LOCAL_FILE, "utf-8");
|
|
369592
369507
|
} else {
|
|
369593
369508
|
content = HEADER_COMMENT;
|
|
@@ -369597,7 +369512,7 @@ async function saveLocalSecret(name, value) {
|
|
|
369597
369512
|
}
|
|
369598
369513
|
async function saveLocalConfig(name, value) {
|
|
369599
369514
|
let content = "";
|
|
369600
|
-
if (
|
|
369515
|
+
if (existsSync5(LOCAL_FILE)) {
|
|
369601
369516
|
content = await readFile(LOCAL_FILE, "utf-8");
|
|
369602
369517
|
} else {
|
|
369603
369518
|
content = HEADER_COMMENT;
|
|
@@ -370227,13 +370142,6 @@ var InstanceStateManager = class {
|
|
|
370227
370142
|
var __dirname = path7.dirname(fileURLToPath(import.meta.url));
|
|
370228
370143
|
var adminDir = path7.join(__dirname, "admin");
|
|
370229
370144
|
var _embeddedAdmin = null;
|
|
370230
|
-
var HTTP_PORT = 80;
|
|
370231
|
-
var HTTPS_PORT = 443;
|
|
370232
|
-
var FALLBACK_HTTPS_PORT = 3443;
|
|
370233
|
-
var DOMAIN_SUFFIX = ".spcf.localhost";
|
|
370234
|
-
var ADMIN_DOMAIN = "spcf.localhost";
|
|
370235
|
-
var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
|
|
370236
|
-
var TEMPORAL_UI_PREFIX = "__temporal";
|
|
370237
370145
|
var MIME_TYPES = {
|
|
370238
370146
|
".html": "text/html",
|
|
370239
370147
|
".css": "text/css",
|
|
@@ -370248,399 +370156,6 @@ var MIME_TYPES = {
|
|
|
370248
370156
|
".woff2": "font/woff2",
|
|
370249
370157
|
".txt": "text/plain"
|
|
370250
370158
|
};
|
|
370251
|
-
function isPortAvailable(port) {
|
|
370252
|
-
return new Promise((resolve52) => {
|
|
370253
|
-
const server = net2.createServer();
|
|
370254
|
-
server.once("error", () => resolve52(false));
|
|
370255
|
-
server.once("listening", () => {
|
|
370256
|
-
server.close(() => resolve52(true));
|
|
370257
|
-
});
|
|
370258
|
-
server.listen(port, "0.0.0.0");
|
|
370259
|
-
});
|
|
370260
|
-
}
|
|
370261
|
-
async function probeHttpsPort() {
|
|
370262
|
-
for (const port of [HTTPS_PORT, FALLBACK_HTTPS_PORT]) {
|
|
370263
|
-
if (await isPortAvailable(port)) return port;
|
|
370264
|
-
}
|
|
370265
|
-
return 0;
|
|
370266
|
-
}
|
|
370267
|
-
async function startHttpProxy(services, certificate, getState, instanceKey = "default", preferredHttpsPort) {
|
|
370268
|
-
const serviceMap = /* @__PURE__ */ new Map();
|
|
370269
|
-
const adminPortMap = /* @__PURE__ */ new Map();
|
|
370270
|
-
const drizzleGatewayPortMap = /* @__PURE__ */ new Map();
|
|
370271
|
-
const temporalUiPortMap = /* @__PURE__ */ new Map();
|
|
370272
|
-
const buildMapKey = (serviceName, key) => {
|
|
370273
|
-
return key === "default" ? serviceName : `${serviceName}.${key}`;
|
|
370274
|
-
};
|
|
370275
|
-
for (const svc of services) {
|
|
370276
|
-
serviceMap.set(buildMapKey(svc.name, instanceKey), svc.port);
|
|
370277
|
-
}
|
|
370278
|
-
const updateServices = (registryServices, keys) => {
|
|
370279
|
-
serviceMap.clear();
|
|
370280
|
-
adminPortMap.clear();
|
|
370281
|
-
drizzleGatewayPortMap.clear();
|
|
370282
|
-
temporalUiPortMap.clear();
|
|
370283
|
-
for (const svc of registryServices) {
|
|
370284
|
-
serviceMap.set(buildMapKey(svc.serviceName, svc.key), svc.port);
|
|
370285
|
-
}
|
|
370286
|
-
for (const [key, registration] of Object.entries(keys)) {
|
|
370287
|
-
adminPortMap.set(key, registration.adminPort);
|
|
370288
|
-
if (registration.drizzleGatewayPort !== void 0) {
|
|
370289
|
-
drizzleGatewayPortMap.set(key, registration.drizzleGatewayPort);
|
|
370290
|
-
}
|
|
370291
|
-
if (registration.temporalUiPort !== void 0) {
|
|
370292
|
-
temporalUiPortMap.set(key, registration.temporalUiPort);
|
|
370293
|
-
}
|
|
370294
|
-
}
|
|
370295
|
-
writeLog("proxy", `Updated service map: ${serviceMap.size} services, ${adminPortMap.size} admin instances, ${drizzleGatewayPortMap.size} drizzle gateways`);
|
|
370296
|
-
};
|
|
370297
|
-
const { key: defaultKey, cert: defaultCert } = certificate;
|
|
370298
|
-
const proxy = httpProxy.createProxyServer({});
|
|
370299
|
-
proxy.on("error", (err, req, res) => {
|
|
370300
|
-
const host = req.headers?.host || "unknown";
|
|
370301
|
-
writeLog("proxy:error", `Bad Gateway for ${host}: ${err.message}`);
|
|
370302
|
-
if (res instanceof http.ServerResponse && !res.headersSent) {
|
|
370303
|
-
res.writeHead(502, { "Content-Type": "text/html" });
|
|
370304
|
-
res.end(`<h1>Bad Gateway</h1><p>Service unavailable: ${err.message}</p>`);
|
|
370305
|
-
}
|
|
370306
|
-
});
|
|
370307
|
-
proxy.on("proxyRes", (proxyRes, req) => {
|
|
370308
|
-
const host = req.headers?.host || "";
|
|
370309
|
-
if (extractTemporalUiKey(host) !== null || extractDrizzleGatewayKey(host) !== null) {
|
|
370310
|
-
delete proxyRes.headers["x-frame-options"];
|
|
370311
|
-
const csp = proxyRes.headers["content-security-policy"];
|
|
370312
|
-
if (typeof csp === "string" && csp.includes("frame-ancestors")) {
|
|
370313
|
-
proxyRes.headers["content-security-policy"] = csp.split(";").filter((d) => !d.trim().startsWith("frame-ancestors")).join(";");
|
|
370314
|
-
}
|
|
370315
|
-
}
|
|
370316
|
-
});
|
|
370317
|
-
const handleRequest = (req, res) => {
|
|
370318
|
-
const host = req.headers.host || "";
|
|
370319
|
-
const hostname2 = host.split(":")[0];
|
|
370320
|
-
const temporalKey = extractTemporalUiKey(host);
|
|
370321
|
-
if (temporalKey !== null) {
|
|
370322
|
-
const temporalPort = temporalUiPortMap.get(temporalKey);
|
|
370323
|
-
if (temporalPort) {
|
|
370324
|
-
proxy.web(req, res, {
|
|
370325
|
-
target: `http://127.0.0.1:${temporalPort}`
|
|
370326
|
-
});
|
|
370327
|
-
return;
|
|
370328
|
-
}
|
|
370329
|
-
res.writeHead(503, { "Content-Type": "text/html" });
|
|
370330
|
-
res.end("<h1>Workflow Engine</h1><p>No workflow engine running for this instance.</p>");
|
|
370331
|
-
return;
|
|
370332
|
-
}
|
|
370333
|
-
const drizzleKey = extractDrizzleGatewayKey(host);
|
|
370334
|
-
if (drizzleKey !== null) {
|
|
370335
|
-
const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
|
|
370336
|
-
if (drizzlePort) {
|
|
370337
|
-
proxy.web(req, res, {
|
|
370338
|
-
target: `http://127.0.0.1:${drizzlePort}`
|
|
370339
|
-
});
|
|
370340
|
-
return;
|
|
370341
|
-
}
|
|
370342
|
-
res.writeHead(503, { "Content-Type": "text/html" });
|
|
370343
|
-
res.end("<h1>Database Viewer</h1><p>No database viewer running for this instance.</p>");
|
|
370344
|
-
return;
|
|
370345
|
-
}
|
|
370346
|
-
const adminKey = extractAdminKey(host);
|
|
370347
|
-
if (adminKey !== null) {
|
|
370348
|
-
const adminPort = adminPortMap.get(adminKey);
|
|
370349
|
-
if (adminPort) {
|
|
370350
|
-
const url = new URL(req.url || "/", "http://localhost");
|
|
370351
|
-
if (url.pathname.startsWith("/api/")) {
|
|
370352
|
-
proxy.web(req, res, { target: `http://127.0.0.1:${adminPort}` });
|
|
370353
|
-
return;
|
|
370354
|
-
}
|
|
370355
|
-
serveStaticFile(res, url.pathname);
|
|
370356
|
-
return;
|
|
370357
|
-
}
|
|
370358
|
-
if (adminKey === "default" && hostname2 === ADMIN_DOMAIN) {
|
|
370359
|
-
res.writeHead(503, { "Content-Type": "text/html" });
|
|
370360
|
-
res.end("<h1>Admin UI</h1><p>No dev instance running. Start <code>specific dev</code> first.</p>");
|
|
370361
|
-
return;
|
|
370362
|
-
}
|
|
370363
|
-
}
|
|
370364
|
-
const route = extractServiceAndKey(host);
|
|
370365
|
-
if (!route) {
|
|
370366
|
-
return sendNotFound(res, null, serviceMap);
|
|
370367
|
-
}
|
|
370368
|
-
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
370369
|
-
const targetPort2 = serviceMap.get(mapKey);
|
|
370370
|
-
if (!targetPort2) {
|
|
370371
|
-
return sendNotFound(res, route.serviceName, serviceMap);
|
|
370372
|
-
}
|
|
370373
|
-
proxy.web(req, res, { target: `http://127.0.0.1:${targetPort2}` });
|
|
370374
|
-
};
|
|
370375
|
-
const handleUpgrade = (req, socket, head) => {
|
|
370376
|
-
const host = req.headers.host || "";
|
|
370377
|
-
const temporalKey = extractTemporalUiKey(host);
|
|
370378
|
-
if (temporalKey !== null) {
|
|
370379
|
-
const temporalPort = temporalUiPortMap.get(temporalKey);
|
|
370380
|
-
if (temporalPort) {
|
|
370381
|
-
proxy.ws(req, socket, head, { target: `http://127.0.0.1:${temporalPort}` });
|
|
370382
|
-
return;
|
|
370383
|
-
}
|
|
370384
|
-
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
370385
|
-
return;
|
|
370386
|
-
}
|
|
370387
|
-
const drizzleKey = extractDrizzleGatewayKey(host);
|
|
370388
|
-
if (drizzleKey !== null) {
|
|
370389
|
-
const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
|
|
370390
|
-
if (drizzlePort) {
|
|
370391
|
-
proxy.ws(req, socket, head, { target: `http://127.0.0.1:${drizzlePort}` });
|
|
370392
|
-
return;
|
|
370393
|
-
}
|
|
370394
|
-
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
370395
|
-
return;
|
|
370396
|
-
}
|
|
370397
|
-
const adminKey = extractAdminKey(host);
|
|
370398
|
-
if (adminKey !== null) {
|
|
370399
|
-
if (adminPortMap.has(adminKey)) {
|
|
370400
|
-
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
370401
|
-
return;
|
|
370402
|
-
}
|
|
370403
|
-
}
|
|
370404
|
-
const route = extractServiceAndKey(host);
|
|
370405
|
-
if (!route) {
|
|
370406
|
-
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
370407
|
-
return;
|
|
370408
|
-
}
|
|
370409
|
-
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
370410
|
-
const targetPort2 = serviceMap.get(mapKey);
|
|
370411
|
-
if (!targetPort2) {
|
|
370412
|
-
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
370413
|
-
return;
|
|
370414
|
-
}
|
|
370415
|
-
proxy.ws(req, socket, head, { target: `http://127.0.0.1:${targetPort2}` });
|
|
370416
|
-
};
|
|
370417
|
-
const createHttpsServer = (key, cert) => {
|
|
370418
|
-
const server = https.createServer({ key, cert }, handleRequest);
|
|
370419
|
-
server.on("upgrade", handleUpgrade);
|
|
370420
|
-
return server;
|
|
370421
|
-
};
|
|
370422
|
-
const makeInstance = (httpsServer, actualHttpsPort, httpServer, actualHttpPort) => {
|
|
370423
|
-
let currentHttpsServer = httpsServer;
|
|
370424
|
-
return {
|
|
370425
|
-
httpPort: actualHttpPort ?? 0,
|
|
370426
|
-
httpsPort: actualHttpsPort,
|
|
370427
|
-
updateServices,
|
|
370428
|
-
updateCertificate(newCert) {
|
|
370429
|
-
if (typeof currentHttpsServer.setSecureContext === "function") {
|
|
370430
|
-
currentHttpsServer.setSecureContext({
|
|
370431
|
-
key: newCert.key,
|
|
370432
|
-
cert: newCert.cert
|
|
370433
|
-
});
|
|
370434
|
-
writeLog("proxy", "Certificate updated");
|
|
370435
|
-
} else {
|
|
370436
|
-
const oldServer = currentHttpsServer;
|
|
370437
|
-
currentHttpsServer = createHttpsServer(newCert.key, newCert.cert);
|
|
370438
|
-
currentHttpsServer.listen(actualHttpsPort, "0.0.0.0", () => {
|
|
370439
|
-
writeLog("proxy", "Certificate updated (server restarted)");
|
|
370440
|
-
});
|
|
370441
|
-
oldServer.closeAllConnections?.();
|
|
370442
|
-
oldServer.close();
|
|
370443
|
-
}
|
|
370444
|
-
},
|
|
370445
|
-
async stop() {
|
|
370446
|
-
return new Promise((resolve52) => {
|
|
370447
|
-
const serverCount = httpServer ? 2 : 1;
|
|
370448
|
-
let closed = 0;
|
|
370449
|
-
const onClose = () => {
|
|
370450
|
-
closed++;
|
|
370451
|
-
if (closed === serverCount) {
|
|
370452
|
-
clearTimeout(forceCloseTimeout);
|
|
370453
|
-
resolve52();
|
|
370454
|
-
}
|
|
370455
|
-
};
|
|
370456
|
-
currentHttpsServer.close(onClose);
|
|
370457
|
-
httpServer?.close(onClose);
|
|
370458
|
-
const forceCloseTimeout = setTimeout(() => {
|
|
370459
|
-
currentHttpsServer.closeAllConnections?.();
|
|
370460
|
-
httpServer?.closeAllConnections?.();
|
|
370461
|
-
resolve52();
|
|
370462
|
-
}, 2e3);
|
|
370463
|
-
});
|
|
370464
|
-
}
|
|
370465
|
-
};
|
|
370466
|
-
};
|
|
370467
|
-
const useStandardPorts = !preferredHttpsPort || preferredHttpsPort === HTTPS_PORT;
|
|
370468
|
-
if (useStandardPorts) {
|
|
370469
|
-
try {
|
|
370470
|
-
return await startStandardProxy();
|
|
370471
|
-
} catch (err) {
|
|
370472
|
-
const isPortError = err instanceof Error && (err.message.includes("already in use") || err.message.includes("Cannot bind to port"));
|
|
370473
|
-
if (!isPortError) throw err;
|
|
370474
|
-
writeLog("proxy", `Standard ports unavailable (${err instanceof Error ? err.message : err}), falling back to HTTPS-only`);
|
|
370475
|
-
}
|
|
370476
|
-
}
|
|
370477
|
-
const targetPort = preferredHttpsPort && preferredHttpsPort !== HTTPS_PORT ? preferredHttpsPort : FALLBACK_HTTPS_PORT;
|
|
370478
|
-
return startFallbackProxy(targetPort);
|
|
370479
|
-
function startStandardProxy() {
|
|
370480
|
-
const httpServer = http.createServer(handleRequest);
|
|
370481
|
-
httpServer.on("upgrade", handleUpgrade);
|
|
370482
|
-
const httpsServer = https.createServer(
|
|
370483
|
-
{ key: defaultKey, cert: defaultCert },
|
|
370484
|
-
handleRequest
|
|
370485
|
-
);
|
|
370486
|
-
httpsServer.on("upgrade", handleUpgrade);
|
|
370487
|
-
return new Promise((resolve52, reject) => {
|
|
370488
|
-
let httpStarted = false;
|
|
370489
|
-
let httpsStarted = false;
|
|
370490
|
-
let failed = false;
|
|
370491
|
-
const checkReady = () => {
|
|
370492
|
-
if (failed) return;
|
|
370493
|
-
if (httpStarted && httpsStarted) {
|
|
370494
|
-
writeLog("proxy", `HTTP/HTTPS proxy started on ports ${HTTP_PORT}/${HTTPS_PORT}`);
|
|
370495
|
-
resolve52(makeInstance(httpsServer, HTTPS_PORT, httpServer, HTTP_PORT));
|
|
370496
|
-
}
|
|
370497
|
-
};
|
|
370498
|
-
const handleError = (port) => (err) => {
|
|
370499
|
-
if (failed) return;
|
|
370500
|
-
failed = true;
|
|
370501
|
-
httpServer.close(() => {
|
|
370502
|
-
});
|
|
370503
|
-
httpsServer.close(() => {
|
|
370504
|
-
});
|
|
370505
|
-
if (err.code === "EACCES" || err.code === "EADDRINUSE") {
|
|
370506
|
-
reject(new Error(`Port ${port} is already in use`));
|
|
370507
|
-
} else {
|
|
370508
|
-
reject(err);
|
|
370509
|
-
}
|
|
370510
|
-
};
|
|
370511
|
-
httpServer.on("error", handleError(HTTP_PORT));
|
|
370512
|
-
httpsServer.on("error", handleError(HTTPS_PORT));
|
|
370513
|
-
httpServer.listen(HTTP_PORT, "0.0.0.0", () => {
|
|
370514
|
-
httpStarted = true;
|
|
370515
|
-
checkReady();
|
|
370516
|
-
});
|
|
370517
|
-
httpsServer.listen(HTTPS_PORT, "0.0.0.0", () => {
|
|
370518
|
-
httpsStarted = true;
|
|
370519
|
-
checkReady();
|
|
370520
|
-
});
|
|
370521
|
-
});
|
|
370522
|
-
}
|
|
370523
|
-
function startFallbackProxy(port) {
|
|
370524
|
-
const httpsServer = https.createServer(
|
|
370525
|
-
{ key: defaultKey, cert: defaultCert },
|
|
370526
|
-
handleRequest
|
|
370527
|
-
);
|
|
370528
|
-
httpsServer.on("upgrade", handleUpgrade);
|
|
370529
|
-
return new Promise((resolve52, reject) => {
|
|
370530
|
-
const tryPort = (p) => {
|
|
370531
|
-
httpsServer.removeAllListeners("error");
|
|
370532
|
-
httpsServer.on("error", (err) => {
|
|
370533
|
-
if ((err.code === "EADDRINUSE" || err.code === "EACCES") && p !== 0) {
|
|
370534
|
-
tryPort(0);
|
|
370535
|
-
} else {
|
|
370536
|
-
reject(err);
|
|
370537
|
-
}
|
|
370538
|
-
});
|
|
370539
|
-
httpsServer.listen(p, "0.0.0.0", () => {
|
|
370540
|
-
const addr = httpsServer.address();
|
|
370541
|
-
const actualPort = typeof addr === "object" && addr ? addr.port : p;
|
|
370542
|
-
writeLog("proxy", `HTTPS proxy started on port ${actualPort} (fallback mode)`);
|
|
370543
|
-
resolve52(makeInstance(httpsServer, actualPort));
|
|
370544
|
-
});
|
|
370545
|
-
};
|
|
370546
|
-
tryPort(port);
|
|
370547
|
-
});
|
|
370548
|
-
}
|
|
370549
|
-
}
|
|
370550
|
-
function extractServiceAndKey(host) {
|
|
370551
|
-
const hostname2 = host.split(":")[0];
|
|
370552
|
-
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
370553
|
-
return null;
|
|
370554
|
-
}
|
|
370555
|
-
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
370556
|
-
if (!prefix) {
|
|
370557
|
-
return null;
|
|
370558
|
-
}
|
|
370559
|
-
const parts = prefix.split(".");
|
|
370560
|
-
if (parts.length === 1 && parts[0]) {
|
|
370561
|
-
return { serviceName: parts[0], key: "default" };
|
|
370562
|
-
} else if (parts.length === 2 && parts[0] && parts[1]) {
|
|
370563
|
-
return { serviceName: parts[0], key: parts[1] };
|
|
370564
|
-
}
|
|
370565
|
-
return null;
|
|
370566
|
-
}
|
|
370567
|
-
function extractDrizzleGatewayKey(host) {
|
|
370568
|
-
const hostname2 = host.split(":")[0];
|
|
370569
|
-
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
370570
|
-
return null;
|
|
370571
|
-
}
|
|
370572
|
-
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
370573
|
-
if (!prefix) {
|
|
370574
|
-
return null;
|
|
370575
|
-
}
|
|
370576
|
-
const parts = prefix.split(".");
|
|
370577
|
-
if (parts.length === 1 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
|
|
370578
|
-
return "default";
|
|
370579
|
-
} else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX && parts[1]) {
|
|
370580
|
-
return parts[1];
|
|
370581
|
-
}
|
|
370582
|
-
return null;
|
|
370583
|
-
}
|
|
370584
|
-
function extractTemporalUiKey(host) {
|
|
370585
|
-
const hostname2 = host.split(":")[0];
|
|
370586
|
-
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
370587
|
-
return null;
|
|
370588
|
-
}
|
|
370589
|
-
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
370590
|
-
if (!prefix) {
|
|
370591
|
-
return null;
|
|
370592
|
-
}
|
|
370593
|
-
const parts = prefix.split(".");
|
|
370594
|
-
if (parts.length === 1 && parts[0] === TEMPORAL_UI_PREFIX) {
|
|
370595
|
-
return "default";
|
|
370596
|
-
} else if (parts.length === 2 && parts[0] === TEMPORAL_UI_PREFIX && parts[1]) {
|
|
370597
|
-
return parts[1];
|
|
370598
|
-
}
|
|
370599
|
-
return null;
|
|
370600
|
-
}
|
|
370601
|
-
function extractAdminKey(host) {
|
|
370602
|
-
const hostname2 = host.split(":")[0];
|
|
370603
|
-
if (!hostname2) {
|
|
370604
|
-
return null;
|
|
370605
|
-
}
|
|
370606
|
-
if (hostname2 === ADMIN_DOMAIN) {
|
|
370607
|
-
return "default";
|
|
370608
|
-
}
|
|
370609
|
-
if (!hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
370610
|
-
return null;
|
|
370611
|
-
}
|
|
370612
|
-
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
370613
|
-
if (!prefix) {
|
|
370614
|
-
return null;
|
|
370615
|
-
}
|
|
370616
|
-
const parts = prefix.split(".");
|
|
370617
|
-
if (parts.length === 1 && parts[0]) {
|
|
370618
|
-
return parts[0];
|
|
370619
|
-
}
|
|
370620
|
-
return null;
|
|
370621
|
-
}
|
|
370622
|
-
function sendNotFound(res, requestedService, serviceMap) {
|
|
370623
|
-
const serviceList = Array.from(serviceMap.keys()).map((key) => {
|
|
370624
|
-
const parts = key.split(".");
|
|
370625
|
-
if (parts.length === 1) {
|
|
370626
|
-
return `<li>${parts[0]}.spcf.localhost</li>`;
|
|
370627
|
-
} else {
|
|
370628
|
-
return `<li>${key}.spcf.localhost</li>`;
|
|
370629
|
-
}
|
|
370630
|
-
}).join("\n");
|
|
370631
|
-
const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
|
|
370632
|
-
res.writeHead(404, { "Content-Type": "text/html" });
|
|
370633
|
-
res.end(`<!DOCTYPE html>
|
|
370634
|
-
<html>
|
|
370635
|
-
<head><title>Service Not Found</title></head>
|
|
370636
|
-
<body>
|
|
370637
|
-
<h1>Service Not Found</h1>
|
|
370638
|
-
<p>${message}</p>
|
|
370639
|
-
${serviceMap.size > 0 ? `<p>Available services:</p>
|
|
370640
|
-
<ul>${serviceList}</ul>` : "<p>No services are currently exposed.</p>"}
|
|
370641
|
-
</body>
|
|
370642
|
-
</html>`);
|
|
370643
|
-
}
|
|
370644
370159
|
function serveStaticFile(res, pathname) {
|
|
370645
370160
|
if (_embeddedAdmin) {
|
|
370646
370161
|
return serveEmbeddedFile(res, pathname);
|
|
@@ -370740,7 +370255,7 @@ function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
|
370740
370255
|
res.end("<h1>Internal Server Error</h1>");
|
|
370741
370256
|
}
|
|
370742
370257
|
}
|
|
370743
|
-
async function startAdminServer(getState) {
|
|
370258
|
+
async function startAdminServer(getState, listenPort = 0) {
|
|
370744
370259
|
return new Promise((resolve52, reject) => {
|
|
370745
370260
|
const server = http.createServer((req, res) => {
|
|
370746
370261
|
const url = new URL(req.url || "/", "http://localhost");
|
|
@@ -370763,14 +370278,14 @@ async function startAdminServer(getState) {
|
|
|
370763
370278
|
serveStaticFile(res, pathname);
|
|
370764
370279
|
});
|
|
370765
370280
|
server.on("error", reject);
|
|
370766
|
-
server.listen(
|
|
370281
|
+
server.listen(listenPort, "127.0.0.1", () => {
|
|
370767
370282
|
const addr = server.address();
|
|
370768
370283
|
if (!addr || typeof addr === "string") {
|
|
370769
370284
|
reject(new Error("Failed to get server address"));
|
|
370770
370285
|
return;
|
|
370771
370286
|
}
|
|
370772
370287
|
const port = addr.port;
|
|
370773
|
-
writeLog("admin", `Admin
|
|
370288
|
+
writeLog("admin", `Admin server started on port ${port}`);
|
|
370774
370289
|
resolve52({
|
|
370775
370290
|
port,
|
|
370776
370291
|
stop: () => new Promise((res, rej) => {
|
|
@@ -370921,7 +370436,7 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
|
|
|
370921
370436
|
}
|
|
370922
370437
|
function checkTcpPort2(host, port) {
|
|
370923
370438
|
return new Promise((resolve52) => {
|
|
370924
|
-
const socket = new
|
|
370439
|
+
const socket = new net2.Socket();
|
|
370925
370440
|
socket.setTimeout(1e3);
|
|
370926
370441
|
socket.on("connect", () => {
|
|
370927
370442
|
socket.destroy();
|
|
@@ -371159,7 +370674,7 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
|
|
|
371159
370674
|
}
|
|
371160
370675
|
function checkTcpPort3(host, port) {
|
|
371161
370676
|
return new Promise((resolve52) => {
|
|
371162
|
-
const socket = new
|
|
370677
|
+
const socket = new net3.Socket();
|
|
371163
370678
|
socket.setTimeout(1e3);
|
|
371164
370679
|
socket.on("connect", () => {
|
|
371165
370680
|
socket.destroy();
|
|
@@ -371553,7 +371068,7 @@ async function waitForTcpPort4(host, port, timeoutMs = 3e4) {
|
|
|
371553
371068
|
}
|
|
371554
371069
|
function checkTcpPort4(host, port) {
|
|
371555
371070
|
return new Promise((resolve52) => {
|
|
371556
|
-
const socket = new
|
|
371071
|
+
const socket = new net4.Socket();
|
|
371557
371072
|
socket.setTimeout(1e3);
|
|
371558
371073
|
socket.on("connect", () => {
|
|
371559
371074
|
socket.destroy();
|
|
@@ -371821,418 +371336,86 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
371821
371336
|
}
|
|
371822
371337
|
};
|
|
371823
371338
|
}
|
|
371824
|
-
|
|
371825
|
-
|
|
371826
|
-
|
|
371827
|
-
var
|
|
371828
|
-
|
|
371829
|
-
|
|
371830
|
-
|
|
371831
|
-
|
|
371832
|
-
|
|
371833
|
-
|
|
371834
|
-
}
|
|
371835
|
-
try {
|
|
371836
|
-
const certPem = fs11.readFileSync(certPath, "utf-8");
|
|
371837
|
-
const cert = forge.pki.certificateFromPem(certPem);
|
|
371838
|
-
const nameConstraints = cert.getExtension("nameConstraints");
|
|
371839
|
-
if (!nameConstraints) {
|
|
371840
|
-
return false;
|
|
371841
|
-
}
|
|
371842
|
-
return nameConstraints.value.includes(EXPECTED_DOMAIN);
|
|
371843
|
-
} catch {
|
|
371844
|
-
return false;
|
|
371845
|
-
}
|
|
371846
|
-
}
|
|
371847
|
-
function caInstalledInTrustStore() {
|
|
371848
|
-
if (!caFilesExist()) {
|
|
371849
|
-
return false;
|
|
371339
|
+
var BLOCK_SIZE = 100;
|
|
371340
|
+
var FIRST_BLOCK_START = 3e3;
|
|
371341
|
+
var MAX_BLOCKS = 100;
|
|
371342
|
+
var BlockPortAllocator = class _BlockPortAllocator {
|
|
371343
|
+
basePort;
|
|
371344
|
+
nextServiceOffset = 1;
|
|
371345
|
+
nextResourceOffset = BLOCK_SIZE - 1;
|
|
371346
|
+
// starts at 99
|
|
371347
|
+
constructor(basePort) {
|
|
371348
|
+
this.basePort = basePort;
|
|
371850
371349
|
}
|
|
371851
|
-
|
|
371852
|
-
|
|
371853
|
-
|
|
371854
|
-
|
|
371855
|
-
|
|
371856
|
-
const
|
|
371857
|
-
|
|
371858
|
-
|
|
371859
|
-
).replace(/\r\n/g, "\n").trim();
|
|
371860
|
-
if (keychainCert !== diskCert) {
|
|
371861
|
-
return false;
|
|
371862
|
-
}
|
|
371863
|
-
const trustOutput = execSync2(
|
|
371864
|
-
`security dump-trust-settings 2>&1`,
|
|
371865
|
-
{ encoding: "utf-8" }
|
|
371866
|
-
);
|
|
371867
|
-
const caSection = trustOutput.split("Specific Local Development CA");
|
|
371868
|
-
if (caSection.length < 2) {
|
|
371869
|
-
return false;
|
|
371870
|
-
}
|
|
371871
|
-
const afterCA = caSection[1].split(/^Cert \d+:/m)[0];
|
|
371872
|
-
return afterCA.includes("Policy OID");
|
|
371873
|
-
} else if (platform62 === "linux") {
|
|
371874
|
-
const trustPaths = [
|
|
371875
|
-
"/usr/local/share/ca-certificates/specific-local-ca.crt",
|
|
371876
|
-
// Debian/Ubuntu
|
|
371877
|
-
"/etc/pki/ca-trust/source/anchors/specific-local-ca.crt",
|
|
371878
|
-
// RHEL/Fedora
|
|
371879
|
-
"/etc/ca-certificates/trust-source/anchors/specific-local-ca.crt"
|
|
371880
|
-
// Arch
|
|
371881
|
-
];
|
|
371882
|
-
for (const trustPath of trustPaths) {
|
|
371883
|
-
if (fs11.existsSync(trustPath)) {
|
|
371884
|
-
const trustedCert = fs11.readFileSync(trustPath, "utf-8").replace(/\r\n/g, "\n").trim();
|
|
371885
|
-
return trustedCert === diskCert;
|
|
371886
|
-
}
|
|
371350
|
+
/**
|
|
371351
|
+
* Create a BlockPortAllocator by probing for an available port block.
|
|
371352
|
+
*/
|
|
371353
|
+
static async create() {
|
|
371354
|
+
for (let i = 0; i < MAX_BLOCKS; i++) {
|
|
371355
|
+
const base = FIRST_BLOCK_START + i * BLOCK_SIZE;
|
|
371356
|
+
if (await isPortAvailable(base)) {
|
|
371357
|
+
return new _BlockPortAllocator(base);
|
|
371887
371358
|
}
|
|
371888
|
-
return false;
|
|
371889
371359
|
}
|
|
371890
|
-
|
|
371891
|
-
|
|
371892
|
-
}
|
|
371893
|
-
return false;
|
|
371894
|
-
}
|
|
371895
|
-
function createNameConstraintsExtension(permittedDNSNames) {
|
|
371896
|
-
const nameConstraintsOID = "2.5.29.30";
|
|
371897
|
-
const permittedSubtrees = permittedDNSNames.map((dnsName) => {
|
|
371898
|
-
return forge.asn1.create(
|
|
371899
|
-
forge.asn1.Class.UNIVERSAL,
|
|
371900
|
-
forge.asn1.Type.SEQUENCE,
|
|
371901
|
-
true,
|
|
371902
|
-
[
|
|
371903
|
-
// dNSName [2] IA5String
|
|
371904
|
-
forge.asn1.create(
|
|
371905
|
-
forge.asn1.Class.CONTEXT_SPECIFIC,
|
|
371906
|
-
2,
|
|
371907
|
-
false,
|
|
371908
|
-
dnsName
|
|
371909
|
-
)
|
|
371910
|
-
]
|
|
371360
|
+
throw new Error(
|
|
371361
|
+
`No available port block found (tried ${FIRST_BLOCK_START}\u2013${FIRST_BLOCK_START + MAX_BLOCKS * BLOCK_SIZE - 1})`
|
|
371911
371362
|
);
|
|
371912
|
-
});
|
|
371913
|
-
const nameConstraints = forge.asn1.create(
|
|
371914
|
-
forge.asn1.Class.UNIVERSAL,
|
|
371915
|
-
forge.asn1.Type.SEQUENCE,
|
|
371916
|
-
true,
|
|
371917
|
-
[
|
|
371918
|
-
// permittedSubtrees [0] IMPLICIT GeneralSubtrees
|
|
371919
|
-
forge.asn1.create(
|
|
371920
|
-
forge.asn1.Class.CONTEXT_SPECIFIC,
|
|
371921
|
-
0,
|
|
371922
|
-
true,
|
|
371923
|
-
permittedSubtrees
|
|
371924
|
-
)
|
|
371925
|
-
]
|
|
371926
|
-
);
|
|
371927
|
-
return {
|
|
371928
|
-
id: nameConstraintsOID,
|
|
371929
|
-
critical: true,
|
|
371930
|
-
value: forge.asn1.toDer(nameConstraints).getBytes()
|
|
371931
|
-
};
|
|
371932
|
-
}
|
|
371933
|
-
function generateRootCA() {
|
|
371934
|
-
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
371935
|
-
const cert = forge.pki.createCertificate();
|
|
371936
|
-
cert.publicKey = keys.publicKey;
|
|
371937
|
-
cert.serialNumber = "01";
|
|
371938
|
-
cert.validity.notBefore = /* @__PURE__ */ new Date();
|
|
371939
|
-
cert.validity.notAfter = /* @__PURE__ */ new Date();
|
|
371940
|
-
cert.validity.notAfter.setFullYear(
|
|
371941
|
-
cert.validity.notBefore.getFullYear() + 10
|
|
371942
|
-
);
|
|
371943
|
-
const attrs = [
|
|
371944
|
-
{ name: "commonName", value: "Specific Local Development CA" },
|
|
371945
|
-
{ name: "organizationName", value: "Specific" }
|
|
371946
|
-
];
|
|
371947
|
-
cert.setSubject(attrs);
|
|
371948
|
-
cert.setIssuer(attrs);
|
|
371949
|
-
const nameConstraintsExt = createNameConstraintsExtension([
|
|
371950
|
-
".spcf.localhost",
|
|
371951
|
-
"spcf.localhost"
|
|
371952
|
-
]);
|
|
371953
|
-
cert.setExtensions([
|
|
371954
|
-
{ name: "basicConstraints", cA: true, critical: true },
|
|
371955
|
-
{ name: "keyUsage", keyCertSign: true, cRLSign: true, critical: true },
|
|
371956
|
-
nameConstraintsExt
|
|
371957
|
-
]);
|
|
371958
|
-
cert.sign(keys.privateKey, forge.md.sha256.create());
|
|
371959
|
-
return {
|
|
371960
|
-
key: forge.pki.privateKeyToPem(keys.privateKey),
|
|
371961
|
-
cert: forge.pki.certificateToPem(cert)
|
|
371962
|
-
};
|
|
371963
|
-
}
|
|
371964
|
-
function saveCA(key, cert) {
|
|
371965
|
-
const caDir = getCADir();
|
|
371966
|
-
fs11.mkdirSync(caDir, { recursive: true, mode: 448 });
|
|
371967
|
-
const keyPath = path12.join(caDir, "ca.key");
|
|
371968
|
-
const certPath = path12.join(caDir, "ca.crt");
|
|
371969
|
-
fs11.writeFileSync(keyPath, key, { mode: 384 });
|
|
371970
|
-
fs11.writeFileSync(certPath, cert, { mode: 420 });
|
|
371971
|
-
return certPath;
|
|
371972
|
-
}
|
|
371973
|
-
function removeCA() {
|
|
371974
|
-
const caDir = getCADir();
|
|
371975
|
-
const keyPath = path12.join(caDir, "ca.key");
|
|
371976
|
-
const certPath = path12.join(caDir, "ca.crt");
|
|
371977
|
-
if (fs11.existsSync(keyPath)) {
|
|
371978
|
-
fs11.unlinkSync(keyPath);
|
|
371979
371363
|
}
|
|
371980
|
-
|
|
371981
|
-
|
|
371364
|
+
getBasePort() {
|
|
371365
|
+
return this.basePort;
|
|
371982
371366
|
}
|
|
371983
|
-
|
|
371984
|
-
|
|
371985
|
-
return path12.join(os3.homedir(), "Library", "Keychains", "login.keychain-db");
|
|
371986
|
-
}
|
|
371987
|
-
function getCADeleteCommand() {
|
|
371988
|
-
return `security delete-certificate -c "Specific Local Development CA" "${getLoginKeychainPath()}"`;
|
|
371989
|
-
}
|
|
371990
|
-
function getCATrustCommand(certPath) {
|
|
371991
|
-
return `security add-trusted-cert -r trustRoot -p ssl -p basic -k "${getLoginKeychainPath()}" "${certPath}"`;
|
|
371992
|
-
}
|
|
371993
|
-
function getCAInstallCommands(certPath) {
|
|
371994
|
-
const platform62 = os3.platform();
|
|
371995
|
-
if (platform62 === "darwin") {
|
|
371996
|
-
return [
|
|
371997
|
-
`${getCADeleteCommand()} 2>/dev/null || true`,
|
|
371998
|
-
getCATrustCommand(certPath)
|
|
371999
|
-
];
|
|
372000
|
-
} else if (platform62 === "linux") {
|
|
372001
|
-
if (fs11.existsSync("/usr/local/share/ca-certificates")) {
|
|
372002
|
-
return [
|
|
372003
|
-
`cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
|
|
372004
|
-
"update-ca-certificates"
|
|
372005
|
-
];
|
|
372006
|
-
} else if (fs11.existsSync("/etc/pki/ca-trust/source/anchors")) {
|
|
372007
|
-
return [
|
|
372008
|
-
`cp "${certPath}" /etc/pki/ca-trust/source/anchors/specific-local-ca.crt`,
|
|
372009
|
-
"update-ca-trust extract"
|
|
372010
|
-
];
|
|
372011
|
-
} else if (fs11.existsSync("/etc/ca-certificates/trust-source/anchors")) {
|
|
372012
|
-
return [
|
|
372013
|
-
`cp "${certPath}" /etc/ca-certificates/trust-source/anchors/specific-local-ca.crt`,
|
|
372014
|
-
"trust extract-compat"
|
|
372015
|
-
];
|
|
372016
|
-
}
|
|
372017
|
-
throw new Error("Could not detect Linux certificate trust mechanism");
|
|
371367
|
+
getAdminPort() {
|
|
371368
|
+
return this.basePort;
|
|
372018
371369
|
}
|
|
372019
|
-
|
|
372020
|
-
|
|
372021
|
-
|
|
372022
|
-
|
|
372023
|
-
|
|
372024
|
-
const caKeyPem = fs11.readFileSync(path12.join(caDir, "ca.key"), "utf-8");
|
|
372025
|
-
const caCert = forge.pki.certificateFromPem(caCertPem);
|
|
372026
|
-
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
|
372027
|
-
const certKeys = forge.pki.rsa.generateKeyPair(2048);
|
|
372028
|
-
const cert = forge.pki.createCertificate();
|
|
372029
|
-
cert.publicKey = certKeys.publicKey;
|
|
372030
|
-
cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16));
|
|
372031
|
-
cert.validity.notBefore = /* @__PURE__ */ new Date();
|
|
372032
|
-
cert.validity.notAfter = /* @__PURE__ */ new Date();
|
|
372033
|
-
cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + 7);
|
|
372034
|
-
cert.setSubject([{ name: "commonName", value: domain }]);
|
|
372035
|
-
cert.setIssuer(caCert.subject.attributes);
|
|
372036
|
-
const altNames = [
|
|
372037
|
-
{ type: 2, value: domain },
|
|
372038
|
-
{ type: 2, value: `*.${domain}` }
|
|
372039
|
-
];
|
|
372040
|
-
for (const key of keys) {
|
|
372041
|
-
if (key !== "default") {
|
|
372042
|
-
altNames.push({ type: 2, value: `*.${key}.${domain}` });
|
|
372043
|
-
}
|
|
372044
|
-
}
|
|
372045
|
-
cert.setExtensions([
|
|
372046
|
-
{ name: "basicConstraints", cA: false },
|
|
372047
|
-
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
372048
|
-
{ name: "extKeyUsage", serverAuth: true },
|
|
372049
|
-
{ name: "subjectAltName", altNames }
|
|
372050
|
-
]);
|
|
372051
|
-
cert.sign(caKey, forge.md.sha256.create());
|
|
372052
|
-
return {
|
|
372053
|
-
key: Buffer.from(forge.pki.privateKeyToPem(certKeys.privateKey)),
|
|
372054
|
-
cert: Buffer.from(forge.pki.certificateToPem(cert))
|
|
372055
|
-
};
|
|
372056
|
-
}
|
|
372057
|
-
var DNS_PORT = 15353;
|
|
372058
|
-
async function startDnsServer(port = DNS_PORT) {
|
|
372059
|
-
const server = DNS.createServer({
|
|
372060
|
-
udp: true,
|
|
372061
|
-
handle: (request, send) => {
|
|
372062
|
-
const response = DNS.Packet.createResponseFromRequest(request);
|
|
372063
|
-
for (const question of request.questions) {
|
|
372064
|
-
if (question.type === DNS.Packet.TYPE.A) {
|
|
372065
|
-
response.answers.push({
|
|
372066
|
-
name: question.name,
|
|
372067
|
-
type: DNS.Packet.TYPE.A,
|
|
372068
|
-
class: DNS.Packet.CLASS.IN,
|
|
372069
|
-
ttl: 300,
|
|
372070
|
-
address: "127.0.0.1"
|
|
372071
|
-
});
|
|
372072
|
-
}
|
|
372073
|
-
}
|
|
372074
|
-
send(response);
|
|
372075
|
-
}
|
|
372076
|
-
});
|
|
372077
|
-
await server.listen({
|
|
372078
|
-
udp: { port, address: "127.0.0.1" }
|
|
372079
|
-
});
|
|
372080
|
-
writeLog("dns", `DNS server started on 127.0.0.1:${port}`);
|
|
372081
|
-
return {
|
|
372082
|
-
port,
|
|
372083
|
-
async stop() {
|
|
372084
|
-
await server.close();
|
|
372085
|
-
writeLog("dns", "DNS server stopped");
|
|
372086
|
-
}
|
|
372087
|
-
};
|
|
372088
|
-
}
|
|
372089
|
-
var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
|
|
372090
|
-
var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
|
|
372091
|
-
var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
|
|
372092
|
-
function resolverConfigExists() {
|
|
372093
|
-
const platform62 = os4.platform();
|
|
372094
|
-
if (platform62 === "darwin") {
|
|
372095
|
-
return fs12.existsSync(RESOLVER_FILE_MACOS);
|
|
372096
|
-
} else if (platform62 === "linux") {
|
|
372097
|
-
return fs12.existsSync(RESOLVER_FILE_LINUX);
|
|
372098
|
-
}
|
|
372099
|
-
return false;
|
|
372100
|
-
}
|
|
372101
|
-
function getResolverInstallCommands(port) {
|
|
372102
|
-
const platform62 = os4.platform();
|
|
372103
|
-
if (platform62 === "darwin") {
|
|
372104
|
-
return [
|
|
372105
|
-
"mkdir -p /etc/resolver",
|
|
372106
|
-
`rm -f ${OLD_RESOLVER_FILE_MACOS}`,
|
|
372107
|
-
`printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
|
|
372108
|
-
];
|
|
372109
|
-
} else if (platform62 === "linux") {
|
|
372110
|
-
if (fs12.existsSync("/etc/systemd/resolved.conf.d") || fs12.existsSync("/etc/systemd")) {
|
|
372111
|
-
return [
|
|
372112
|
-
"mkdir -p /etc/systemd/resolved.conf.d",
|
|
372113
|
-
`printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~spcf.localhost\\n" > ${RESOLVER_FILE_LINUX}`,
|
|
372114
|
-
"systemctl restart systemd-resolved"
|
|
372115
|
-
];
|
|
371370
|
+
allocateServicePort() {
|
|
371371
|
+
const port = this.basePort + this.nextServiceOffset;
|
|
371372
|
+
this.nextServiceOffset++;
|
|
371373
|
+
if (this.nextServiceOffset >= this.nextResourceOffset) {
|
|
371374
|
+
throw new Error("Port block exhausted: too many services and resources");
|
|
372116
371375
|
}
|
|
372117
|
-
return
|
|
372118
|
-
}
|
|
372119
|
-
return [];
|
|
372120
|
-
}
|
|
372121
|
-
function systemSetupNeeded() {
|
|
372122
|
-
return dnsSetupNeeded() || tlsSetupNeeded();
|
|
372123
|
-
}
|
|
372124
|
-
function dnsSetupNeeded() {
|
|
372125
|
-
return !resolverConfigExists();
|
|
372126
|
-
}
|
|
372127
|
-
function tlsSetupNeeded() {
|
|
372128
|
-
return !caInstalledInTrustStore();
|
|
372129
|
-
}
|
|
372130
|
-
function ensureCAGenerated() {
|
|
372131
|
-
if (caFilesExist()) {
|
|
372132
|
-
return false;
|
|
372133
|
-
}
|
|
372134
|
-
const { key, cert } = generateRootCA();
|
|
372135
|
-
saveCA(key, cert);
|
|
372136
|
-
return true;
|
|
372137
|
-
}
|
|
372138
|
-
function performDnsSetup() {
|
|
372139
|
-
const commands = getResolverInstallCommands(DNS_PORT);
|
|
372140
|
-
if (commands.length === 0) return;
|
|
372141
|
-
if (os5.platform() === "darwin") {
|
|
372142
|
-
execPrivilegedMacOS(commands);
|
|
372143
|
-
} else {
|
|
372144
|
-
execSync3(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
|
|
372145
|
-
}
|
|
372146
|
-
}
|
|
372147
|
-
function performTlsTrust() {
|
|
372148
|
-
const wasGenerated = ensureCAGenerated();
|
|
372149
|
-
const certPath = path13.join(getCADir(), "ca.crt");
|
|
372150
|
-
try {
|
|
372151
|
-
if (os5.platform() === "darwin") {
|
|
372152
|
-
execSync3(`${getCADeleteCommand()} 2>/dev/null || true`, {
|
|
372153
|
-
stdio: "inherit"
|
|
372154
|
-
});
|
|
372155
|
-
execSync3(getCATrustCommand(certPath), { stdio: "inherit" });
|
|
372156
|
-
} else {
|
|
372157
|
-
execSync3(
|
|
372158
|
-
`sudo sh -c '${getCAInstallCommands(certPath).join(" && ")}'`,
|
|
372159
|
-
{ stdio: "inherit" }
|
|
372160
|
-
);
|
|
372161
|
-
}
|
|
372162
|
-
} catch (err) {
|
|
372163
|
-
if (wasGenerated) {
|
|
372164
|
-
removeCA();
|
|
372165
|
-
}
|
|
372166
|
-
throw err;
|
|
372167
|
-
}
|
|
372168
|
-
}
|
|
372169
|
-
function performSystemSetup() {
|
|
372170
|
-
const wasGenerated = ensureCAGenerated();
|
|
372171
|
-
const needsInstallCA = !caInstalledInTrustStore();
|
|
372172
|
-
const needsResolver = !resolverConfigExists();
|
|
372173
|
-
if (!needsInstallCA && !needsResolver) {
|
|
372174
|
-
return;
|
|
372175
|
-
}
|
|
372176
|
-
const certPath = path13.join(getCADir(), "ca.crt");
|
|
372177
|
-
const commands = [];
|
|
372178
|
-
if (needsInstallCA) {
|
|
372179
|
-
commands.push(...getCAInstallCommands(certPath));
|
|
372180
|
-
}
|
|
372181
|
-
if (needsResolver) {
|
|
372182
|
-
commands.push(...getResolverInstallCommands(DNS_PORT));
|
|
372183
|
-
}
|
|
372184
|
-
if (commands.length === 0) {
|
|
372185
|
-
return;
|
|
371376
|
+
return port;
|
|
372186
371377
|
}
|
|
372187
|
-
|
|
372188
|
-
|
|
372189
|
-
|
|
372190
|
-
if (
|
|
372191
|
-
|
|
371378
|
+
allocateResourcePort() {
|
|
371379
|
+
const port = this.basePort + this.nextResourceOffset;
|
|
371380
|
+
this.nextResourceOffset--;
|
|
371381
|
+
if (this.nextResourceOffset <= this.nextServiceOffset) {
|
|
371382
|
+
throw new Error("Port block exhausted: too many services and resources");
|
|
372192
371383
|
}
|
|
372193
|
-
|
|
371384
|
+
return port;
|
|
372194
371385
|
}
|
|
372195
|
-
|
|
372196
|
-
|
|
372197
|
-
|
|
372198
|
-
|
|
372199
|
-
|
|
372200
|
-
|
|
372201
|
-
fs13.writeFileSync(
|
|
372202
|
-
scriptPath,
|
|
372203
|
-
"#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
|
|
372204
|
-
{ mode: 448 }
|
|
372205
|
-
);
|
|
372206
|
-
execSync3(
|
|
372207
|
-
`osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
|
|
372208
|
-
);
|
|
372209
|
-
execSync3(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
|
|
372210
|
-
} finally {
|
|
372211
|
-
try {
|
|
372212
|
-
fs13.unlinkSync(scriptPath);
|
|
372213
|
-
} catch {
|
|
372214
|
-
}
|
|
372215
|
-
try {
|
|
372216
|
-
fs13.rmSync(appPath, { recursive: true });
|
|
372217
|
-
} catch {
|
|
372218
|
-
}
|
|
371386
|
+
/**
|
|
371387
|
+
* Implements PortAllocatorInterface for compatibility with resource-starter.
|
|
371388
|
+
* Delegates to allocateResourcePort().
|
|
371389
|
+
*/
|
|
371390
|
+
allocate(_key) {
|
|
371391
|
+
return this.allocateResourcePort();
|
|
372219
371392
|
}
|
|
371393
|
+
};
|
|
371394
|
+
function isPortAvailable(port) {
|
|
371395
|
+
return new Promise((resolve52) => {
|
|
371396
|
+
const server = net5.createServer();
|
|
371397
|
+
server.once("error", () => resolve52(false));
|
|
371398
|
+
server.once("listening", () => {
|
|
371399
|
+
server.close(() => resolve52(true));
|
|
371400
|
+
});
|
|
371401
|
+
server.listen(port, "127.0.0.1");
|
|
371402
|
+
});
|
|
372220
371403
|
}
|
|
372221
371404
|
var StableSubdomainAllocator = class {
|
|
372222
371405
|
tunnelsDir;
|
|
372223
371406
|
tunnelsFilePath;
|
|
372224
371407
|
baseSlug = null;
|
|
372225
371408
|
constructor(projectRoot, key = "default") {
|
|
372226
|
-
this.tunnelsDir =
|
|
372227
|
-
this.tunnelsFilePath =
|
|
371409
|
+
this.tunnelsDir = path12.join(projectRoot, ".specific", "keys", key);
|
|
371410
|
+
this.tunnelsFilePath = path12.join(this.tunnelsDir, "tunnels.json");
|
|
372228
371411
|
this.loadTunnels();
|
|
372229
371412
|
}
|
|
372230
371413
|
loadTunnels() {
|
|
372231
|
-
if (!
|
|
371414
|
+
if (!fs11.existsSync(this.tunnelsFilePath)) {
|
|
372232
371415
|
return;
|
|
372233
371416
|
}
|
|
372234
371417
|
try {
|
|
372235
|
-
const content =
|
|
371418
|
+
const content = fs11.readFileSync(this.tunnelsFilePath, "utf-8");
|
|
372236
371419
|
const data = JSON.parse(content);
|
|
372237
371420
|
if (data.version === 1 && data.baseSlug) {
|
|
372238
371421
|
this.baseSlug = data.baseSlug;
|
|
@@ -372242,14 +371425,14 @@ var StableSubdomainAllocator = class {
|
|
|
372242
371425
|
}
|
|
372243
371426
|
}
|
|
372244
371427
|
saveTunnels() {
|
|
372245
|
-
if (!
|
|
372246
|
-
|
|
371428
|
+
if (!fs11.existsSync(this.tunnelsDir)) {
|
|
371429
|
+
fs11.mkdirSync(this.tunnelsDir, { recursive: true });
|
|
372247
371430
|
}
|
|
372248
371431
|
const data = {
|
|
372249
371432
|
version: 1,
|
|
372250
371433
|
baseSlug: this.baseSlug
|
|
372251
371434
|
};
|
|
372252
|
-
|
|
371435
|
+
fs11.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
|
|
372253
371436
|
}
|
|
372254
371437
|
generateBaseSlug() {
|
|
372255
371438
|
return generateSlug(2, {
|
|
@@ -372397,382 +371580,6 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
372397
371580
|
stop: async () => tunnel.close()
|
|
372398
371581
|
};
|
|
372399
371582
|
}
|
|
372400
|
-
var ProxyRegistryManager = class {
|
|
372401
|
-
proxyDir;
|
|
372402
|
-
ownerPath;
|
|
372403
|
-
registryPath;
|
|
372404
|
-
lockPath;
|
|
372405
|
-
isOwner = false;
|
|
372406
|
-
registryWatcher = null;
|
|
372407
|
-
constructor() {
|
|
372408
|
-
this.proxyDir = path15.join(os6.homedir(), ".specific", "proxy");
|
|
372409
|
-
this.ownerPath = path15.join(this.proxyDir, "owner.json");
|
|
372410
|
-
this.registryPath = path15.join(this.proxyDir, "registry.json");
|
|
372411
|
-
this.lockPath = path15.join(this.proxyDir, "registry.lock");
|
|
372412
|
-
}
|
|
372413
|
-
ensureProxyDir() {
|
|
372414
|
-
if (!fs15.existsSync(this.proxyDir)) {
|
|
372415
|
-
fs15.mkdirSync(this.proxyDir, { recursive: true });
|
|
372416
|
-
}
|
|
372417
|
-
}
|
|
372418
|
-
isProcessRunning(pid) {
|
|
372419
|
-
try {
|
|
372420
|
-
process.kill(pid, 0);
|
|
372421
|
-
return true;
|
|
372422
|
-
} catch (e) {
|
|
372423
|
-
const err = e;
|
|
372424
|
-
return err.code !== "ESRCH";
|
|
372425
|
-
}
|
|
372426
|
-
}
|
|
372427
|
-
/**
|
|
372428
|
-
* Check if the proxy is actually listening on its expected port.
|
|
372429
|
-
* This catches cases where the owner process is alive but the proxy has crashed.
|
|
372430
|
-
*/
|
|
372431
|
-
isProxyListening(port, timeoutMs = 1e3) {
|
|
372432
|
-
return new Promise((resolve52) => {
|
|
372433
|
-
const socket = new net7.Socket();
|
|
372434
|
-
let resolved = false;
|
|
372435
|
-
const cleanup = () => {
|
|
372436
|
-
if (!resolved) {
|
|
372437
|
-
resolved = true;
|
|
372438
|
-
socket.destroy();
|
|
372439
|
-
}
|
|
372440
|
-
};
|
|
372441
|
-
socket.setTimeout(timeoutMs);
|
|
372442
|
-
socket.on("connect", () => {
|
|
372443
|
-
cleanup();
|
|
372444
|
-
resolve52(true);
|
|
372445
|
-
});
|
|
372446
|
-
socket.on("timeout", () => {
|
|
372447
|
-
cleanup();
|
|
372448
|
-
resolve52(false);
|
|
372449
|
-
});
|
|
372450
|
-
socket.on("error", () => {
|
|
372451
|
-
cleanup();
|
|
372452
|
-
resolve52(false);
|
|
372453
|
-
});
|
|
372454
|
-
socket.connect(port, "127.0.0.1");
|
|
372455
|
-
});
|
|
372456
|
-
}
|
|
372457
|
-
/**
|
|
372458
|
-
* Check if the proxy owner is healthy (process running AND proxy listening).
|
|
372459
|
-
*/
|
|
372460
|
-
async isProxyOwnerHealthy(owner) {
|
|
372461
|
-
if (!this.isProcessRunning(owner.pid)) {
|
|
372462
|
-
return false;
|
|
372463
|
-
}
|
|
372464
|
-
const port = owner.httpsPort ?? 443;
|
|
372465
|
-
const isListening = await this.isProxyListening(port);
|
|
372466
|
-
return isListening;
|
|
372467
|
-
}
|
|
372468
|
-
async acquireLock(timeoutMs = 5e3) {
|
|
372469
|
-
this.ensureProxyDir();
|
|
372470
|
-
const startTime = Date.now();
|
|
372471
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
372472
|
-
try {
|
|
372473
|
-
const fd = fs15.openSync(
|
|
372474
|
-
this.lockPath,
|
|
372475
|
-
fs15.constants.O_CREAT | fs15.constants.O_EXCL | fs15.constants.O_WRONLY
|
|
372476
|
-
);
|
|
372477
|
-
fs15.writeSync(fd, String(process.pid));
|
|
372478
|
-
fs15.closeSync(fd);
|
|
372479
|
-
return () => {
|
|
372480
|
-
try {
|
|
372481
|
-
fs15.unlinkSync(this.lockPath);
|
|
372482
|
-
} catch {
|
|
372483
|
-
}
|
|
372484
|
-
};
|
|
372485
|
-
} catch (e) {
|
|
372486
|
-
const err = e;
|
|
372487
|
-
if (err.code === "EEXIST") {
|
|
372488
|
-
try {
|
|
372489
|
-
const lockPid = parseInt(
|
|
372490
|
-
fs15.readFileSync(this.lockPath, "utf-8").trim(),
|
|
372491
|
-
10
|
|
372492
|
-
);
|
|
372493
|
-
if (!this.isProcessRunning(lockPid)) {
|
|
372494
|
-
fs15.unlinkSync(this.lockPath);
|
|
372495
|
-
continue;
|
|
372496
|
-
}
|
|
372497
|
-
} catch {
|
|
372498
|
-
try {
|
|
372499
|
-
fs15.unlinkSync(this.lockPath);
|
|
372500
|
-
} catch {
|
|
372501
|
-
}
|
|
372502
|
-
continue;
|
|
372503
|
-
}
|
|
372504
|
-
await new Promise((resolve52) => setTimeout(resolve52, 100));
|
|
372505
|
-
} else {
|
|
372506
|
-
throw e;
|
|
372507
|
-
}
|
|
372508
|
-
}
|
|
372509
|
-
}
|
|
372510
|
-
throw new Error("Failed to acquire proxy registry lock (timeout)");
|
|
372511
|
-
}
|
|
372512
|
-
/**
|
|
372513
|
-
* Try to claim ownership of the proxy.
|
|
372514
|
-
* Returns true if this process is now the owner.
|
|
372515
|
-
*/
|
|
372516
|
-
async claimProxyOwnership(key, httpsPort) {
|
|
372517
|
-
const releaseLock = await this.acquireLock();
|
|
372518
|
-
try {
|
|
372519
|
-
if (fs15.existsSync(this.ownerPath)) {
|
|
372520
|
-
const content = fs15.readFileSync(this.ownerPath, "utf-8");
|
|
372521
|
-
const ownerFile2 = JSON.parse(content);
|
|
372522
|
-
if (await this.isProxyOwnerHealthy(ownerFile2.owner)) {
|
|
372523
|
-
return false;
|
|
372524
|
-
}
|
|
372525
|
-
}
|
|
372526
|
-
const ownerFile = {
|
|
372527
|
-
version: 1,
|
|
372528
|
-
owner: {
|
|
372529
|
-
pid: process.pid,
|
|
372530
|
-
key,
|
|
372531
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
372532
|
-
httpsPort
|
|
372533
|
-
}
|
|
372534
|
-
};
|
|
372535
|
-
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
372536
|
-
this.isOwner = true;
|
|
372537
|
-
return true;
|
|
372538
|
-
} finally {
|
|
372539
|
-
releaseLock();
|
|
372540
|
-
}
|
|
372541
|
-
}
|
|
372542
|
-
/**
|
|
372543
|
-
* Release ownership of the proxy.
|
|
372544
|
-
*/
|
|
372545
|
-
async releaseProxyOwnership() {
|
|
372546
|
-
if (!this.isOwner) {
|
|
372547
|
-
return;
|
|
372548
|
-
}
|
|
372549
|
-
const releaseLock = await this.acquireLock();
|
|
372550
|
-
try {
|
|
372551
|
-
if (fs15.existsSync(this.ownerPath)) {
|
|
372552
|
-
const content = fs15.readFileSync(this.ownerPath, "utf-8");
|
|
372553
|
-
const ownerFile = JSON.parse(content);
|
|
372554
|
-
if (ownerFile.owner.pid === process.pid) {
|
|
372555
|
-
fs15.unlinkSync(this.ownerPath);
|
|
372556
|
-
}
|
|
372557
|
-
}
|
|
372558
|
-
this.isOwner = false;
|
|
372559
|
-
} finally {
|
|
372560
|
-
releaseLock();
|
|
372561
|
-
}
|
|
372562
|
-
}
|
|
372563
|
-
/**
|
|
372564
|
-
* Get the current proxy owner.
|
|
372565
|
-
*/
|
|
372566
|
-
async getProxyOwner() {
|
|
372567
|
-
if (!fs15.existsSync(this.ownerPath)) {
|
|
372568
|
-
return null;
|
|
372569
|
-
}
|
|
372570
|
-
const releaseLock = await this.acquireLock();
|
|
372571
|
-
try {
|
|
372572
|
-
const content = fs15.readFileSync(this.ownerPath, "utf-8");
|
|
372573
|
-
const ownerFile = JSON.parse(content);
|
|
372574
|
-
if (!await this.isProxyOwnerHealthy(ownerFile.owner)) {
|
|
372575
|
-
return null;
|
|
372576
|
-
}
|
|
372577
|
-
return ownerFile.owner;
|
|
372578
|
-
} catch {
|
|
372579
|
-
return null;
|
|
372580
|
-
} finally {
|
|
372581
|
-
releaseLock();
|
|
372582
|
-
}
|
|
372583
|
-
}
|
|
372584
|
-
/**
|
|
372585
|
-
* Check if this process is the proxy owner.
|
|
372586
|
-
*/
|
|
372587
|
-
isProxyOwner() {
|
|
372588
|
-
return this.isOwner;
|
|
372589
|
-
}
|
|
372590
|
-
/**
|
|
372591
|
-
* Get the HTTPS port used by the current proxy owner.
|
|
372592
|
-
* Returns 443 if unknown.
|
|
372593
|
-
*/
|
|
372594
|
-
getProxyHttpsPort() {
|
|
372595
|
-
try {
|
|
372596
|
-
if (!fs15.existsSync(this.ownerPath)) return 443;
|
|
372597
|
-
const content = fs15.readFileSync(this.ownerPath, "utf-8");
|
|
372598
|
-
const ownerFile = JSON.parse(content);
|
|
372599
|
-
return ownerFile.owner.httpsPort ?? 443;
|
|
372600
|
-
} catch {
|
|
372601
|
-
return 443;
|
|
372602
|
-
}
|
|
372603
|
-
}
|
|
372604
|
-
/**
|
|
372605
|
-
* Register services for a key.
|
|
372606
|
-
*/
|
|
372607
|
-
async registerServices(key, adminPort, services, drizzleGatewayPort, temporalUiPort) {
|
|
372608
|
-
const releaseLock = await this.acquireLock();
|
|
372609
|
-
try {
|
|
372610
|
-
const registry = this.readRegistry();
|
|
372611
|
-
const registration = {
|
|
372612
|
-
adminPort,
|
|
372613
|
-
services
|
|
372614
|
-
};
|
|
372615
|
-
if (drizzleGatewayPort !== void 0) {
|
|
372616
|
-
registration.drizzleGatewayPort = drizzleGatewayPort;
|
|
372617
|
-
}
|
|
372618
|
-
if (temporalUiPort !== void 0) {
|
|
372619
|
-
registration.temporalUiPort = temporalUiPort;
|
|
372620
|
-
}
|
|
372621
|
-
registry.keys[key] = registration;
|
|
372622
|
-
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
372623
|
-
this.writeFileAtomic(this.registryPath, registry);
|
|
372624
|
-
} finally {
|
|
372625
|
-
releaseLock();
|
|
372626
|
-
}
|
|
372627
|
-
}
|
|
372628
|
-
/**
|
|
372629
|
-
* Unregister all services for a key.
|
|
372630
|
-
*/
|
|
372631
|
-
async unregisterServices(key) {
|
|
372632
|
-
const releaseLock = await this.acquireLock();
|
|
372633
|
-
try {
|
|
372634
|
-
const registry = this.readRegistry();
|
|
372635
|
-
delete registry.keys[key];
|
|
372636
|
-
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
372637
|
-
this.writeFileAtomic(this.registryPath, registry);
|
|
372638
|
-
} finally {
|
|
372639
|
-
releaseLock();
|
|
372640
|
-
}
|
|
372641
|
-
}
|
|
372642
|
-
/**
|
|
372643
|
-
* Get all registered services (flattened view for proxy routing).
|
|
372644
|
-
*/
|
|
372645
|
-
async getAllServices() {
|
|
372646
|
-
const releaseLock = await this.acquireLock();
|
|
372647
|
-
try {
|
|
372648
|
-
const registry = this.readRegistry();
|
|
372649
|
-
const services = [];
|
|
372650
|
-
for (const [key, registration] of Object.entries(registry.keys)) {
|
|
372651
|
-
for (const svc of registration.services) {
|
|
372652
|
-
services.push({
|
|
372653
|
-
key,
|
|
372654
|
-
serviceName: svc.serviceName,
|
|
372655
|
-
port: svc.port
|
|
372656
|
-
});
|
|
372657
|
-
}
|
|
372658
|
-
}
|
|
372659
|
-
return services;
|
|
372660
|
-
} finally {
|
|
372661
|
-
releaseLock();
|
|
372662
|
-
}
|
|
372663
|
-
}
|
|
372664
|
-
/**
|
|
372665
|
-
* Get all registered keys and their admin ports.
|
|
372666
|
-
*/
|
|
372667
|
-
async getAllKeyRegistrations() {
|
|
372668
|
-
const releaseLock = await this.acquireLock();
|
|
372669
|
-
try {
|
|
372670
|
-
const registry = this.readRegistry();
|
|
372671
|
-
return { ...registry.keys };
|
|
372672
|
-
} finally {
|
|
372673
|
-
releaseLock();
|
|
372674
|
-
}
|
|
372675
|
-
}
|
|
372676
|
-
/**
|
|
372677
|
-
* Watch the registry for changes. Returns a cleanup function.
|
|
372678
|
-
* Callback receives both the flattened services list and all key registrations (for admin ports).
|
|
372679
|
-
* Uses chokidar with stat-polling for reliable detection of atomic file writes (rename) on macOS.
|
|
372680
|
-
*/
|
|
372681
|
-
watchRegistry(onChange) {
|
|
372682
|
-
this.ensureProxyDir();
|
|
372683
|
-
if (!fs15.existsSync(this.registryPath)) {
|
|
372684
|
-
const emptyRegistry = {
|
|
372685
|
-
version: 1,
|
|
372686
|
-
keys: {},
|
|
372687
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
372688
|
-
};
|
|
372689
|
-
this.writeFileAtomic(this.registryPath, emptyRegistry);
|
|
372690
|
-
}
|
|
372691
|
-
let debounceTimer = null;
|
|
372692
|
-
const triggerUpdate = () => {
|
|
372693
|
-
if (debounceTimer) {
|
|
372694
|
-
clearTimeout(debounceTimer);
|
|
372695
|
-
}
|
|
372696
|
-
debounceTimer = setTimeout(async () => {
|
|
372697
|
-
try {
|
|
372698
|
-
const services = await this.getAllServices();
|
|
372699
|
-
const keys = await this.getAllKeyRegistrations();
|
|
372700
|
-
onChange(services, keys);
|
|
372701
|
-
} catch {
|
|
372702
|
-
}
|
|
372703
|
-
}, 50);
|
|
372704
|
-
};
|
|
372705
|
-
this.registryWatcher = chokidar_default.watch(this.registryPath, {
|
|
372706
|
-
persistent: true,
|
|
372707
|
-
usePolling: true,
|
|
372708
|
-
interval: 500
|
|
372709
|
-
});
|
|
372710
|
-
this.registryWatcher.on("change", triggerUpdate);
|
|
372711
|
-
return () => {
|
|
372712
|
-
if (debounceTimer) {
|
|
372713
|
-
clearTimeout(debounceTimer);
|
|
372714
|
-
}
|
|
372715
|
-
this.registryWatcher?.close();
|
|
372716
|
-
this.registryWatcher = null;
|
|
372717
|
-
};
|
|
372718
|
-
}
|
|
372719
|
-
/**
|
|
372720
|
-
* Attempt to become the proxy owner (election).
|
|
372721
|
-
* This is called when the current owner is detected as dead.
|
|
372722
|
-
*/
|
|
372723
|
-
async attemptElection(key, httpsPort) {
|
|
372724
|
-
const releaseLock = await this.acquireLock();
|
|
372725
|
-
try {
|
|
372726
|
-
if (fs15.existsSync(this.ownerPath)) {
|
|
372727
|
-
const content = fs15.readFileSync(this.ownerPath, "utf-8");
|
|
372728
|
-
const ownerFile2 = JSON.parse(content);
|
|
372729
|
-
if (await this.isProxyOwnerHealthy(ownerFile2.owner)) {
|
|
372730
|
-
return false;
|
|
372731
|
-
}
|
|
372732
|
-
fs15.unlinkSync(this.ownerPath);
|
|
372733
|
-
}
|
|
372734
|
-
const ownerFile = {
|
|
372735
|
-
version: 1,
|
|
372736
|
-
owner: {
|
|
372737
|
-
pid: process.pid,
|
|
372738
|
-
key,
|
|
372739
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
372740
|
-
httpsPort
|
|
372741
|
-
}
|
|
372742
|
-
};
|
|
372743
|
-
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
372744
|
-
this.isOwner = true;
|
|
372745
|
-
return true;
|
|
372746
|
-
} finally {
|
|
372747
|
-
releaseLock();
|
|
372748
|
-
}
|
|
372749
|
-
}
|
|
372750
|
-
readRegistry() {
|
|
372751
|
-
if (!fs15.existsSync(this.registryPath)) {
|
|
372752
|
-
return {
|
|
372753
|
-
version: 1,
|
|
372754
|
-
keys: {},
|
|
372755
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
372756
|
-
};
|
|
372757
|
-
}
|
|
372758
|
-
try {
|
|
372759
|
-
const content = fs15.readFileSync(this.registryPath, "utf-8");
|
|
372760
|
-
return JSON.parse(content);
|
|
372761
|
-
} catch {
|
|
372762
|
-
return {
|
|
372763
|
-
version: 1,
|
|
372764
|
-
keys: {},
|
|
372765
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
372766
|
-
};
|
|
372767
|
-
}
|
|
372768
|
-
}
|
|
372769
|
-
writeFileAtomic(filePath, data) {
|
|
372770
|
-
this.ensureProxyDir();
|
|
372771
|
-
const tmpPath = filePath + ".tmp";
|
|
372772
|
-
fs15.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
372773
|
-
fs15.renameSync(tmpPath, filePath);
|
|
372774
|
-
}
|
|
372775
|
-
};
|
|
372776
371583
|
async function loadConfigs() {
|
|
372777
371584
|
const { configs } = await loadLocal();
|
|
372778
371585
|
return configs;
|
|
@@ -372872,19 +371679,14 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
372872
371679
|
startTime = null;
|
|
372873
371680
|
// Core managers
|
|
372874
371681
|
stateManager = null;
|
|
372875
|
-
|
|
372876
|
-
// Running instances (class fields replacing useRef)
|
|
372877
|
-
proxy = null;
|
|
371682
|
+
// Running instances
|
|
372878
371683
|
adminServer = null;
|
|
372879
371684
|
services = [];
|
|
372880
371685
|
resources = /* @__PURE__ */ new Map();
|
|
372881
371686
|
electricInstances = [];
|
|
372882
371687
|
drizzleGateway = null;
|
|
372883
371688
|
reshapeWatchers = [];
|
|
372884
|
-
registryWatcherCleanup = null;
|
|
372885
|
-
electionInterval = null;
|
|
372886
371689
|
tunnels = [];
|
|
372887
|
-
dnsServer = null;
|
|
372888
371690
|
mailServers = /* @__PURE__ */ new Map();
|
|
372889
371691
|
restartServices = null;
|
|
372890
371692
|
// Config file watcher
|
|
@@ -372940,31 +371742,6 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
372940
371742
|
this.started = true;
|
|
372941
371743
|
this.shuttingDown = false;
|
|
372942
371744
|
this.cancelled = false;
|
|
372943
|
-
const setupNeeded = !this.tunnelEnabled && systemSetupNeeded();
|
|
372944
|
-
if (setupNeeded) {
|
|
372945
|
-
this.setStatus("setup");
|
|
372946
|
-
this.systemLog("Setting up TLS certificates and DNS resolution");
|
|
372947
|
-
try {
|
|
372948
|
-
if (os7.platform() === "darwin") {
|
|
372949
|
-
if (dnsSetupNeeded()) {
|
|
372950
|
-
ensureCAGenerated();
|
|
372951
|
-
performDnsSetup();
|
|
372952
|
-
}
|
|
372953
|
-
if (tlsSetupNeeded()) {
|
|
372954
|
-
ensureCAGenerated();
|
|
372955
|
-
performTlsTrust();
|
|
372956
|
-
}
|
|
372957
|
-
} else {
|
|
372958
|
-
performSystemSetup();
|
|
372959
|
-
}
|
|
372960
|
-
this.systemLog("System setup complete");
|
|
372961
|
-
} catch (err) {
|
|
372962
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
372963
|
-
this.setStatus("error");
|
|
372964
|
-
this.emit("error", new Error(`System setup failed: ${msg}`));
|
|
372965
|
-
return;
|
|
372966
|
-
}
|
|
372967
|
-
}
|
|
372968
371745
|
return new Promise((resolve52) => {
|
|
372969
371746
|
this.startResolve = resolve52;
|
|
372970
371747
|
this.startInternal().then(() => {
|
|
@@ -372989,8 +371766,6 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
372989
371766
|
if (this.shuttingDown) {
|
|
372990
371767
|
this.systemLog("Force shutting down");
|
|
372991
371768
|
const allProcesses = [
|
|
372992
|
-
this.proxy,
|
|
372993
|
-
this.dnsServer,
|
|
372994
371769
|
...this.services,
|
|
372995
371770
|
...this.electricInstances,
|
|
372996
371771
|
this.drizzleGateway,
|
|
@@ -373022,17 +371797,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373022
371797
|
this.configWatcher.close();
|
|
373023
371798
|
this.configWatcher = null;
|
|
373024
371799
|
}
|
|
373025
|
-
if (this.electionInterval) {
|
|
373026
|
-
clearInterval(this.electionInterval);
|
|
373027
|
-
this.electionInterval = null;
|
|
373028
|
-
}
|
|
373029
|
-
if (this.registryWatcherCleanup) {
|
|
373030
|
-
this.registryWatcherCleanup();
|
|
373031
|
-
this.registryWatcherCleanup = null;
|
|
373032
|
-
}
|
|
373033
371800
|
await Promise.all([
|
|
373034
|
-
this.proxy?.stop(),
|
|
373035
|
-
this.dnsServer?.stop(),
|
|
373036
371801
|
this.adminServer?.stop(),
|
|
373037
371802
|
...this.services.map((service) => service.stop()),
|
|
373038
371803
|
...this.electricInstances.map((electric) => electric.stop()),
|
|
@@ -373045,16 +371810,10 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373045
371810
|
this.reshapeWatchers = [];
|
|
373046
371811
|
this.restartServices = null;
|
|
373047
371812
|
this.drizzleGateway = null;
|
|
373048
|
-
this.proxy = null;
|
|
373049
|
-
this.dnsServer = null;
|
|
373050
371813
|
this.adminServer = null;
|
|
373051
371814
|
this.services = [];
|
|
373052
371815
|
this.resources = /* @__PURE__ */ new Map();
|
|
373053
371816
|
this.tunnels = [];
|
|
373054
|
-
if (this.proxyRegistry) {
|
|
373055
|
-
await this.proxyRegistry.unregisterServices(this.instanceKey);
|
|
373056
|
-
await this.proxyRegistry.releaseProxyOwnership();
|
|
373057
|
-
}
|
|
373058
371817
|
if (this.stateManager) {
|
|
373059
371818
|
await this.stateManager.releaseOwnership();
|
|
373060
371819
|
}
|
|
@@ -373132,17 +371891,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373132
371891
|
await this.fileAppearanceWatcher.close();
|
|
373133
371892
|
this.fileAppearanceWatcher = null;
|
|
373134
371893
|
}
|
|
373135
|
-
if (this.electionInterval) {
|
|
373136
|
-
clearInterval(this.electionInterval);
|
|
373137
|
-
this.electionInterval = null;
|
|
373138
|
-
}
|
|
373139
|
-
if (this.registryWatcherCleanup) {
|
|
373140
|
-
this.registryWatcherCleanup();
|
|
373141
|
-
this.registryWatcherCleanup = null;
|
|
373142
|
-
}
|
|
373143
371894
|
await Promise.all([
|
|
373144
|
-
this.proxy?.stop(),
|
|
373145
|
-
this.dnsServer?.stop(),
|
|
373146
371895
|
this.adminServer?.stop(),
|
|
373147
371896
|
...this.services.map((service) => service.stop()),
|
|
373148
371897
|
...this.electricInstances.map((electric) => electric.stop()),
|
|
@@ -373153,10 +371902,6 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373153
371902
|
]);
|
|
373154
371903
|
this.reshapeWatchers = [];
|
|
373155
371904
|
this.restartServices = null;
|
|
373156
|
-
if (this.proxyRegistry) {
|
|
373157
|
-
await this.proxyRegistry.unregisterServices(this.instanceKey);
|
|
373158
|
-
await this.proxyRegistry.releaseProxyOwnership();
|
|
373159
|
-
}
|
|
373160
371905
|
if (this.stateManager) {
|
|
373161
371906
|
await this.stateManager.releaseOwnership();
|
|
373162
371907
|
}
|
|
@@ -373171,10 +371916,10 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373171
371916
|
}
|
|
373172
371917
|
// ── Private: config file watcher ───────────────────────────────────────────
|
|
373173
371918
|
startConfigWatcher() {
|
|
373174
|
-
const configPath =
|
|
371919
|
+
const configPath = path13.join(this.projectDir, "specific.hcl");
|
|
373175
371920
|
this.configWatcher = watchConfigFile(configPath, 1e3, () => {
|
|
373176
371921
|
try {
|
|
373177
|
-
const hcl =
|
|
371922
|
+
const hcl = fs12.readFileSync(configPath, "utf-8");
|
|
373178
371923
|
parseConfig(hcl).then(() => {
|
|
373179
371924
|
this.reload();
|
|
373180
371925
|
}).catch((err) => {
|
|
@@ -373195,8 +371940,6 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373195
371940
|
this.instanceKey
|
|
373196
371941
|
);
|
|
373197
371942
|
this.stateManager = stateManager;
|
|
373198
|
-
const proxyRegistry = new ProxyRegistryManager();
|
|
373199
|
-
this.proxyRegistry = proxyRegistry;
|
|
373200
371943
|
this.systemLog("Starting dev server");
|
|
373201
371944
|
this.setStatus("loading");
|
|
373202
371945
|
await stateManager.cleanStaleState();
|
|
@@ -373215,8 +371958,8 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373215
371958
|
this.emit("error", new Error(msg));
|
|
373216
371959
|
return;
|
|
373217
371960
|
}
|
|
373218
|
-
const configPath =
|
|
373219
|
-
if (!
|
|
371961
|
+
const configPath = path13.join(this.projectDir, "specific.hcl");
|
|
371962
|
+
if (!fs12.existsSync(configPath)) {
|
|
373220
371963
|
this.systemLog("Waiting for specific.hcl to appear");
|
|
373221
371964
|
this.setStatus("waiting");
|
|
373222
371965
|
const dirWatcher = chokidar_default.watch(configPath, {
|
|
@@ -373237,7 +371980,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373237
371980
|
return;
|
|
373238
371981
|
}
|
|
373239
371982
|
let config;
|
|
373240
|
-
const hcl =
|
|
371983
|
+
const hcl = fs12.readFileSync(configPath, "utf-8");
|
|
373241
371984
|
try {
|
|
373242
371985
|
config = await parseConfig(hcl);
|
|
373243
371986
|
} catch (err) {
|
|
@@ -373247,9 +371990,9 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373247
371990
|
return;
|
|
373248
371991
|
}
|
|
373249
371992
|
if (this.cancelled) return;
|
|
373250
|
-
const localFilePath =
|
|
373251
|
-
if (
|
|
373252
|
-
const localContent =
|
|
371993
|
+
const localFilePath = path13.join(this.projectDir, "specific.local");
|
|
371994
|
+
if (fs12.existsSync(localFilePath)) {
|
|
371995
|
+
const localContent = fs12.readFileSync(localFilePath, "utf-8");
|
|
373253
371996
|
try {
|
|
373254
371997
|
await parseLocalFile(localContent);
|
|
373255
371998
|
} catch (err) {
|
|
@@ -373296,10 +372039,8 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373296
372039
|
colorIndex++;
|
|
373297
372040
|
}
|
|
373298
372041
|
this.setStatus("starting");
|
|
373299
|
-
const portAllocator =
|
|
373300
|
-
|
|
373301
|
-
this.instanceKey
|
|
373302
|
-
);
|
|
372042
|
+
const portAllocator = await BlockPortAllocator.create();
|
|
372043
|
+
this.systemLog(`Using port block ${portAllocator.getBasePort()}\u2013${portAllocator.getBasePort() + 99}`);
|
|
373303
372044
|
const syncDatabases = detectSyncDatabases(config);
|
|
373304
372045
|
let resources;
|
|
373305
372046
|
const startedElectric = [];
|
|
@@ -373368,7 +372109,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373368
372109
|
const drizzleGateway = await startDrizzleGateway(
|
|
373369
372110
|
postgresResources,
|
|
373370
372111
|
drizzlePort,
|
|
373371
|
-
|
|
372112
|
+
path13.join(this.projectDir, ".specific", "keys", this.instanceKey)
|
|
373372
372113
|
);
|
|
373373
372114
|
this.drizzleGateway = drizzleGateway;
|
|
373374
372115
|
this.systemLog(`Database viewer ready at ${drizzleGateway.url}`);
|
|
@@ -373388,7 +372129,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
373388
372129
|
if (pg.reshape?.enabled) {
|
|
373389
372130
|
const resource = resources.get(pg.name);
|
|
373390
372131
|
if (!resource) continue;
|
|
373391
|
-
const migrationsDir =
|
|
372132
|
+
const migrationsDir = path13.resolve(
|
|
373392
372133
|
this.projectDir,
|
|
373393
372134
|
pg.reshape.migrations_dir ?? "migrations"
|
|
373394
372135
|
);
|
|
@@ -373536,8 +372277,7 @@ Add them to the config block in specific.local`
|
|
|
373536
372277
|
const endpointInfos = [];
|
|
373537
372278
|
if (service.endpoints.length > 0) {
|
|
373538
372279
|
for (const endpoint of service.endpoints) {
|
|
373539
|
-
const
|
|
373540
|
-
const port = portAllocator.allocate(portKey);
|
|
372280
|
+
const port = portAllocator.allocateServicePort();
|
|
373541
372281
|
endpointPorts.set(endpoint.name, port);
|
|
373542
372282
|
endpointInfos.push({
|
|
373543
372283
|
serviceName: service.name,
|
|
@@ -373546,7 +372286,7 @@ Add them to the config block in specific.local`
|
|
|
373546
372286
|
});
|
|
373547
372287
|
}
|
|
373548
372288
|
} else if (hasPortReference(service.env)) {
|
|
373549
|
-
const port = portAllocator.
|
|
372289
|
+
const port = portAllocator.allocateServicePort();
|
|
373550
372290
|
endpointPorts.set("default", port);
|
|
373551
372291
|
endpointInfos.push({
|
|
373552
372292
|
serviceName: service.name,
|
|
@@ -373557,7 +372297,6 @@ Add them to the config block in specific.local`
|
|
|
373557
372297
|
serviceEndpointPorts.set(service.name, endpointPorts);
|
|
373558
372298
|
serviceEndpoints.set(service.name, endpointInfos);
|
|
373559
372299
|
}
|
|
373560
|
-
const serviceInfos = [];
|
|
373561
372300
|
const exposedServices = [];
|
|
373562
372301
|
for (const service of config.services) {
|
|
373563
372302
|
const endpointInfos = serviceEndpoints.get(service.name) || [];
|
|
@@ -373568,16 +372307,9 @@ Add them to the config block in specific.local`
|
|
|
373568
372307
|
if (endpointConfig?.public) {
|
|
373569
372308
|
const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
|
|
373570
372309
|
exposedServices.push({ name: proxyName, port: info.port });
|
|
373571
|
-
serviceInfos.push({
|
|
373572
|
-
serviceName: proxyName,
|
|
373573
|
-
port: info.port
|
|
373574
|
-
});
|
|
373575
372310
|
}
|
|
373576
372311
|
}
|
|
373577
372312
|
}
|
|
373578
|
-
const existingOwner = await proxyRegistry.getProxyOwner();
|
|
373579
|
-
const expectedHttpsPort = existingOwner ? existingOwner.httpsPort ?? 443 : await probeHttpsPort();
|
|
373580
|
-
const portSuffix = expectedHttpsPort !== 443 && expectedHttpsPort !== 0 ? `:${expectedHttpsPort}` : "";
|
|
373581
372313
|
const publicUrls = /* @__PURE__ */ new Map();
|
|
373582
372314
|
let subdomainAllocator;
|
|
373583
372315
|
if (this.tunnelEnabled && exposedServices.length > 0) {
|
|
@@ -373594,21 +372326,20 @@ Add them to the config block in specific.local`
|
|
|
373594
372326
|
publicUrls.set(svc.name, `${subdomain}.tunnel.spcf.app`);
|
|
373595
372327
|
}
|
|
373596
372328
|
} else {
|
|
373597
|
-
const keySuffix = this.instanceKey === "default" ? "" : `.${this.instanceKey}`;
|
|
373598
372329
|
for (const svc of exposedServices) {
|
|
373599
|
-
publicUrls.set(svc.name,
|
|
372330
|
+
publicUrls.set(svc.name, `localhost:${svc.port}`);
|
|
373600
372331
|
}
|
|
373601
372332
|
}
|
|
373602
372333
|
const runningServices = [];
|
|
373603
372334
|
const resolveServiceCwd = (service) => {
|
|
373604
372335
|
if (service.root)
|
|
373605
|
-
return
|
|
372336
|
+
return path13.resolve(this.projectDir, service.root);
|
|
373606
372337
|
if (service.build) {
|
|
373607
372338
|
const build = config.builds.find(
|
|
373608
372339
|
(b) => b.name === service.build.name
|
|
373609
372340
|
);
|
|
373610
372341
|
if (build?.root)
|
|
373611
|
-
return
|
|
372342
|
+
return path13.resolve(this.projectDir, build.root);
|
|
373612
372343
|
}
|
|
373613
372344
|
return this.projectDir;
|
|
373614
372345
|
};
|
|
@@ -373806,175 +372537,13 @@ Add them to the config block in specific.local`
|
|
|
373806
372537
|
drizzleGatewayPort: this.drizzleGateway?.port,
|
|
373807
372538
|
temporalUiPort
|
|
373808
372539
|
});
|
|
373809
|
-
const
|
|
372540
|
+
const adminPort = portAllocator.getAdminPort();
|
|
372541
|
+
const adminServer = await startAdminServer(getState, adminPort);
|
|
373810
372542
|
this.adminServer = adminServer;
|
|
373811
372543
|
this.systemLog(
|
|
373812
|
-
`Admin
|
|
372544
|
+
`Admin server started on port ${adminServer.port}`
|
|
373813
372545
|
);
|
|
373814
|
-
|
|
373815
|
-
this.emit("adminReady", `http://localhost:${adminServer.port}`);
|
|
373816
|
-
} else {
|
|
373817
|
-
const adminHost = this.instanceKey === "default" ? "" : `${this.instanceKey}.`;
|
|
373818
|
-
this.emit("adminReady", `https://${adminHost}spcf.localhost${portSuffix}`);
|
|
373819
|
-
await proxyRegistry.registerServices(
|
|
373820
|
-
this.instanceKey,
|
|
373821
|
-
adminServer.port,
|
|
373822
|
-
serviceInfos,
|
|
373823
|
-
this.drizzleGateway?.port,
|
|
373824
|
-
temporalUiPort
|
|
373825
|
-
);
|
|
373826
|
-
this.systemLog(
|
|
373827
|
-
`Registered ${serviceInfos.length} services with proxy registry`
|
|
373828
|
-
);
|
|
373829
|
-
const becameProxyOwner = await proxyRegistry.claimProxyOwnership(
|
|
373830
|
-
this.instanceKey,
|
|
373831
|
-
expectedHttpsPort
|
|
373832
|
-
);
|
|
373833
|
-
if (becameProxyOwner) {
|
|
373834
|
-
this.systemLog(
|
|
373835
|
-
"Claimed proxy ownership, starting HTTP proxy and DNS server"
|
|
373836
|
-
);
|
|
373837
|
-
try {
|
|
373838
|
-
const dnsServer = await startDnsServer();
|
|
373839
|
-
this.dnsServer = dnsServer;
|
|
373840
|
-
const currentServices = await proxyRegistry.getAllServices();
|
|
373841
|
-
const registeredKeys = [
|
|
373842
|
-
...new Set(currentServices.map((s) => s.key))
|
|
373843
|
-
];
|
|
373844
|
-
const certificate = generateCertificate(
|
|
373845
|
-
"spcf.localhost",
|
|
373846
|
-
registeredKeys
|
|
373847
|
-
);
|
|
373848
|
-
const proxy = await startHttpProxy(
|
|
373849
|
-
exposedServices,
|
|
373850
|
-
certificate,
|
|
373851
|
-
getState,
|
|
373852
|
-
this.instanceKey,
|
|
373853
|
-
expectedHttpsPort
|
|
373854
|
-
);
|
|
373855
|
-
this.proxy = proxy;
|
|
373856
|
-
const knownKeys = new Set(registeredKeys);
|
|
373857
|
-
this.registryWatcherCleanup = proxyRegistry.watchRegistry(
|
|
373858
|
-
async (updatedServices, updatedKeys) => {
|
|
373859
|
-
this.systemLog(
|
|
373860
|
-
`Registry updated: ${updatedServices.length} services`
|
|
373861
|
-
);
|
|
373862
|
-
proxy.updateServices(updatedServices, updatedKeys);
|
|
373863
|
-
const newKeyNames = Object.keys(updatedKeys).filter(
|
|
373864
|
-
(k) => !knownKeys.has(k)
|
|
373865
|
-
);
|
|
373866
|
-
if (newKeyNames.length > 0) {
|
|
373867
|
-
this.systemLog(
|
|
373868
|
-
`New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`
|
|
373869
|
-
);
|
|
373870
|
-
for (const key of newKeyNames) {
|
|
373871
|
-
knownKeys.add(key);
|
|
373872
|
-
}
|
|
373873
|
-
const allKeys = [...knownKeys];
|
|
373874
|
-
const newCertificate = generateCertificate(
|
|
373875
|
-
"spcf.localhost",
|
|
373876
|
-
allKeys
|
|
373877
|
-
);
|
|
373878
|
-
proxy.updateCertificate(newCertificate);
|
|
373879
|
-
}
|
|
373880
|
-
}
|
|
373881
|
-
);
|
|
373882
|
-
const currentKeys = await proxyRegistry.getAllKeyRegistrations();
|
|
373883
|
-
proxy.updateServices(currentServices, currentKeys);
|
|
373884
|
-
this.systemLog(
|
|
373885
|
-
`Loaded ${currentServices.length} services from registry`
|
|
373886
|
-
);
|
|
373887
|
-
} catch (err) {
|
|
373888
|
-
const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
|
|
373889
|
-
writeLog("system:error", errorMsg);
|
|
373890
|
-
this.setStatus("error");
|
|
373891
|
-
this.emit("error", new Error(errorMsg));
|
|
373892
|
-
return;
|
|
373893
|
-
}
|
|
373894
|
-
} else {
|
|
373895
|
-
this.systemLog(
|
|
373896
|
-
"Another instance owns the proxy, starting election watcher"
|
|
373897
|
-
);
|
|
373898
|
-
const isProcessRunning = (pid) => {
|
|
373899
|
-
try {
|
|
373900
|
-
process.kill(pid, 0);
|
|
373901
|
-
return true;
|
|
373902
|
-
} catch {
|
|
373903
|
-
return false;
|
|
373904
|
-
}
|
|
373905
|
-
};
|
|
373906
|
-
this.electionInterval = setInterval(async () => {
|
|
373907
|
-
if (this.cancelled || this.shuttingDown) {
|
|
373908
|
-
if (this.electionInterval) {
|
|
373909
|
-
clearInterval(this.electionInterval);
|
|
373910
|
-
this.electionInterval = null;
|
|
373911
|
-
}
|
|
373912
|
-
return;
|
|
373913
|
-
}
|
|
373914
|
-
const owner = await proxyRegistry.getProxyOwner();
|
|
373915
|
-
if (!owner || !isProcessRunning(owner.pid)) {
|
|
373916
|
-
this.systemLog(
|
|
373917
|
-
"Proxy owner died, attempting election"
|
|
373918
|
-
);
|
|
373919
|
-
const previousPort = proxyRegistry.getProxyHttpsPort();
|
|
373920
|
-
const won = await proxyRegistry.attemptElection(
|
|
373921
|
-
this.instanceKey,
|
|
373922
|
-
previousPort
|
|
373923
|
-
);
|
|
373924
|
-
if (won) {
|
|
373925
|
-
this.systemLog(
|
|
373926
|
-
"Won election, starting HTTP proxy and DNS server"
|
|
373927
|
-
);
|
|
373928
|
-
if (this.electionInterval) {
|
|
373929
|
-
clearInterval(this.electionInterval);
|
|
373930
|
-
this.electionInterval = null;
|
|
373931
|
-
}
|
|
373932
|
-
try {
|
|
373933
|
-
const dnsServer = await startDnsServer();
|
|
373934
|
-
this.dnsServer = dnsServer;
|
|
373935
|
-
const electionServices = await proxyRegistry.getAllServices();
|
|
373936
|
-
const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
|
|
373937
|
-
const electionKeyNames = Object.keys(
|
|
373938
|
-
electionKeyRegistrations
|
|
373939
|
-
);
|
|
373940
|
-
const certificate = generateCertificate(
|
|
373941
|
-
"spcf.localhost",
|
|
373942
|
-
electionKeyNames
|
|
373943
|
-
);
|
|
373944
|
-
const proxy = await startHttpProxy(
|
|
373945
|
-
exposedServices,
|
|
373946
|
-
certificate,
|
|
373947
|
-
getState,
|
|
373948
|
-
this.instanceKey,
|
|
373949
|
-
previousPort
|
|
373950
|
-
);
|
|
373951
|
-
this.proxy = proxy;
|
|
373952
|
-
this.registryWatcherCleanup = proxyRegistry.watchRegistry(
|
|
373953
|
-
(updatedServices, updatedKeys) => {
|
|
373954
|
-
this.systemLog(
|
|
373955
|
-
`Registry updated: ${updatedServices.length} services`
|
|
373956
|
-
);
|
|
373957
|
-
proxy.updateServices(
|
|
373958
|
-
updatedServices,
|
|
373959
|
-
updatedKeys
|
|
373960
|
-
);
|
|
373961
|
-
}
|
|
373962
|
-
);
|
|
373963
|
-
proxy.updateServices(
|
|
373964
|
-
electionServices,
|
|
373965
|
-
electionKeyRegistrations
|
|
373966
|
-
);
|
|
373967
|
-
} catch (err) {
|
|
373968
|
-
writeLog(
|
|
373969
|
-
"system:error",
|
|
373970
|
-
`Failed to start proxy after election: ${err}`
|
|
373971
|
-
);
|
|
373972
|
-
}
|
|
373973
|
-
}
|
|
373974
|
-
}
|
|
373975
|
-
}, 1e3);
|
|
373976
|
-
}
|
|
373977
|
-
}
|
|
372546
|
+
this.emit("adminReady", `http://localhost:${adminServer.port}`);
|
|
373978
372547
|
if (this.cancelled) return;
|
|
373979
372548
|
this.systemLog("Dev server running");
|
|
373980
372549
|
this.setStatus("running");
|
|
@@ -373984,16 +372553,16 @@ Add them to the config block in specific.local`
|
|
|
373984
372553
|
};
|
|
373985
372554
|
function isInWorktree() {
|
|
373986
372555
|
try {
|
|
373987
|
-
const commonDir =
|
|
372556
|
+
const commonDir = execSync2("git rev-parse --git-common-dir", {
|
|
373988
372557
|
encoding: "utf-8",
|
|
373989
372558
|
stdio: ["pipe", "pipe", "pipe"]
|
|
373990
372559
|
}).trim();
|
|
373991
|
-
const gitDir =
|
|
372560
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
373992
372561
|
encoding: "utf-8",
|
|
373993
372562
|
stdio: ["pipe", "pipe", "pipe"]
|
|
373994
372563
|
}).trim();
|
|
373995
|
-
const resolvedCommonDir =
|
|
373996
|
-
const resolvedGitDir =
|
|
372564
|
+
const resolvedCommonDir = path14.resolve(commonDir);
|
|
372565
|
+
const resolvedGitDir = path14.resolve(gitDir);
|
|
373997
372566
|
return resolvedCommonDir !== resolvedGitDir;
|
|
373998
372567
|
} catch {
|
|
373999
372568
|
return false;
|
|
@@ -374004,11 +372573,11 @@ function getWorktreeName() {
|
|
|
374004
372573
|
return null;
|
|
374005
372574
|
}
|
|
374006
372575
|
try {
|
|
374007
|
-
const gitDir =
|
|
372576
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
374008
372577
|
encoding: "utf-8",
|
|
374009
372578
|
stdio: ["pipe", "pipe", "pipe"]
|
|
374010
372579
|
}).trim();
|
|
374011
|
-
return
|
|
372580
|
+
return path14.basename(gitDir);
|
|
374012
372581
|
} catch {
|
|
374013
372582
|
return null;
|
|
374014
372583
|
}
|
|
@@ -374020,12 +372589,12 @@ function getDefaultKey() {
|
|
|
374020
372589
|
|
|
374021
372590
|
// src/lib/analytics/index.ts
|
|
374022
372591
|
import { PostHog } from "posthog-node";
|
|
374023
|
-
import * as
|
|
372592
|
+
import * as os5 from "os";
|
|
374024
372593
|
import * as crypto3 from "crypto";
|
|
374025
372594
|
|
|
374026
372595
|
// src/lib/project/config.ts
|
|
374027
|
-
import * as
|
|
374028
|
-
import * as
|
|
372596
|
+
import * as fs3 from "fs";
|
|
372597
|
+
import * as path3 from "path";
|
|
374029
372598
|
var PROJECT_ID_FILE = ".specific/project_id";
|
|
374030
372599
|
var ProjectNotLinkedError = class extends Error {
|
|
374031
372600
|
constructor() {
|
|
@@ -374040,32 +372609,32 @@ Run: specific deploy`
|
|
|
374040
372609
|
}
|
|
374041
372610
|
};
|
|
374042
372611
|
function readProjectId(projectDir = process.cwd()) {
|
|
374043
|
-
const projectIdPath =
|
|
374044
|
-
if (!
|
|
372612
|
+
const projectIdPath = path3.join(projectDir, PROJECT_ID_FILE);
|
|
372613
|
+
if (!fs3.existsSync(projectIdPath)) {
|
|
374045
372614
|
throw new ProjectNotLinkedError();
|
|
374046
372615
|
}
|
|
374047
|
-
const projectId =
|
|
372616
|
+
const projectId = fs3.readFileSync(projectIdPath, "utf-8").trim();
|
|
374048
372617
|
if (!projectId) {
|
|
374049
372618
|
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
374050
372619
|
}
|
|
374051
372620
|
return projectId;
|
|
374052
372621
|
}
|
|
374053
372622
|
function hasProjectId(projectDir = process.cwd()) {
|
|
374054
|
-
const projectIdPath =
|
|
374055
|
-
return
|
|
372623
|
+
const projectIdPath = path3.join(projectDir, PROJECT_ID_FILE);
|
|
372624
|
+
return fs3.existsSync(projectIdPath);
|
|
374056
372625
|
}
|
|
374057
372626
|
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
374058
|
-
const specificDir =
|
|
374059
|
-
if (!
|
|
374060
|
-
|
|
372627
|
+
const specificDir = path3.join(projectDir, ".specific");
|
|
372628
|
+
if (!fs3.existsSync(specificDir)) {
|
|
372629
|
+
fs3.mkdirSync(specificDir, { recursive: true });
|
|
374061
372630
|
}
|
|
374062
|
-
|
|
372631
|
+
fs3.writeFileSync(path3.join(specificDir, "project_id"), projectId + "\n");
|
|
374063
372632
|
}
|
|
374064
372633
|
|
|
374065
372634
|
// src/lib/auth/credentials.ts
|
|
374066
|
-
import * as
|
|
374067
|
-
import * as
|
|
374068
|
-
import * as
|
|
372635
|
+
import * as fs18 from "fs";
|
|
372636
|
+
import * as path16 from "path";
|
|
372637
|
+
import * as os4 from "os";
|
|
374069
372638
|
|
|
374070
372639
|
// src/lib/auth/errors.ts
|
|
374071
372640
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -374307,18 +372876,18 @@ function performLogin(options2 = {}) {
|
|
|
374307
372876
|
|
|
374308
372877
|
// src/lib/auth/credentials.ts
|
|
374309
372878
|
function getUserCredentialsDir() {
|
|
374310
|
-
return
|
|
372879
|
+
return path16.join(os4.homedir(), ".specific");
|
|
374311
372880
|
}
|
|
374312
372881
|
function getCredentialsPath() {
|
|
374313
|
-
return
|
|
372882
|
+
return path16.join(getUserCredentialsDir(), "credentials.json");
|
|
374314
372883
|
}
|
|
374315
372884
|
function readUserCredentials() {
|
|
374316
372885
|
const credentialsPath = getCredentialsPath();
|
|
374317
|
-
if (!
|
|
372886
|
+
if (!fs18.existsSync(credentialsPath)) {
|
|
374318
372887
|
return null;
|
|
374319
372888
|
}
|
|
374320
372889
|
try {
|
|
374321
|
-
const content =
|
|
372890
|
+
const content = fs18.readFileSync(credentialsPath, "utf-8");
|
|
374322
372891
|
return JSON.parse(content);
|
|
374323
372892
|
} catch {
|
|
374324
372893
|
return null;
|
|
@@ -374326,18 +372895,18 @@ function readUserCredentials() {
|
|
|
374326
372895
|
}
|
|
374327
372896
|
function writeUserCredentials(credentials) {
|
|
374328
372897
|
const dir = getUserCredentialsDir();
|
|
374329
|
-
if (!
|
|
374330
|
-
|
|
372898
|
+
if (!fs18.existsSync(dir)) {
|
|
372899
|
+
fs18.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
374331
372900
|
}
|
|
374332
372901
|
const credentialsPath = getCredentialsPath();
|
|
374333
|
-
|
|
372902
|
+
fs18.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
374334
372903
|
mode: 384
|
|
374335
372904
|
});
|
|
374336
372905
|
}
|
|
374337
372906
|
function clearUserCredentials() {
|
|
374338
372907
|
const credentialsPath = getCredentialsPath();
|
|
374339
|
-
if (
|
|
374340
|
-
|
|
372908
|
+
if (fs18.existsSync(credentialsPath)) {
|
|
372909
|
+
fs18.unlinkSync(credentialsPath);
|
|
374341
372910
|
}
|
|
374342
372911
|
}
|
|
374343
372912
|
function isLoggedIn() {
|
|
@@ -374416,7 +372985,7 @@ function isEnabled() {
|
|
|
374416
372985
|
}
|
|
374417
372986
|
function getAnonymousId() {
|
|
374418
372987
|
if (anonymousId) return anonymousId;
|
|
374419
|
-
const machineId = `${
|
|
372988
|
+
const machineId = `${os5.hostname()}-${os5.userInfo().username}`;
|
|
374420
372989
|
anonymousId = crypto3.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
|
|
374421
372990
|
return anonymousId;
|
|
374422
372991
|
}
|
|
@@ -374463,7 +373032,7 @@ function trackEvent(event, properties) {
|
|
|
374463
373032
|
event,
|
|
374464
373033
|
properties: {
|
|
374465
373034
|
...properties,
|
|
374466
|
-
cli_version: "0.1.
|
|
373035
|
+
cli_version: "0.1.93",
|
|
374467
373036
|
platform: process.platform,
|
|
374468
373037
|
node_version: process.version,
|
|
374469
373038
|
project_id: getProjectId()
|
|
@@ -374494,67 +373063,67 @@ var options = [
|
|
|
374494
373063
|
{ id: "other", label: "Other / Manual" }
|
|
374495
373064
|
];
|
|
374496
373065
|
function isGitProject() {
|
|
374497
|
-
const gitPath =
|
|
374498
|
-
return
|
|
373066
|
+
const gitPath = path17.join(process.cwd(), ".git");
|
|
373067
|
+
return fs19.existsSync(gitPath);
|
|
374499
373068
|
}
|
|
374500
373069
|
function detectExistingAgents() {
|
|
374501
373070
|
const detected = {};
|
|
374502
|
-
const cursorDir =
|
|
374503
|
-
if (
|
|
373071
|
+
const cursorDir = path17.join(process.cwd(), ".cursor");
|
|
373072
|
+
if (fs19.existsSync(cursorDir)) {
|
|
374504
373073
|
detected["cursor"] = true;
|
|
374505
373074
|
}
|
|
374506
|
-
const claudeDir =
|
|
374507
|
-
const claudeMd =
|
|
374508
|
-
if (
|
|
373075
|
+
const claudeDir = path17.join(process.cwd(), ".claude");
|
|
373076
|
+
const claudeMd = path17.join(process.cwd(), "CLAUDE.md");
|
|
373077
|
+
if (fs19.existsSync(claudeDir) || fs19.existsSync(claudeMd)) {
|
|
374509
373078
|
detected["claude"] = true;
|
|
374510
373079
|
}
|
|
374511
|
-
const agentsMd =
|
|
374512
|
-
if (
|
|
373080
|
+
const agentsMd = path17.join(process.cwd(), "AGENTS.md");
|
|
373081
|
+
if (fs19.existsSync(agentsMd)) {
|
|
374513
373082
|
detected["codex"] = true;
|
|
374514
373083
|
}
|
|
374515
373084
|
return detected;
|
|
374516
373085
|
}
|
|
374517
373086
|
function appendOrCreateFile(filePath, content) {
|
|
374518
|
-
if (
|
|
374519
|
-
const existing =
|
|
373087
|
+
if (fs19.existsSync(filePath)) {
|
|
373088
|
+
const existing = fs19.readFileSync(filePath, "utf-8");
|
|
374520
373089
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
374521
373090
|
return "unchanged";
|
|
374522
373091
|
}
|
|
374523
373092
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
374524
|
-
|
|
373093
|
+
fs19.writeFileSync(filePath, existing + separator + content + "\n");
|
|
374525
373094
|
return "modified";
|
|
374526
373095
|
} else {
|
|
374527
|
-
|
|
373096
|
+
fs19.writeFileSync(filePath, content + "\n");
|
|
374528
373097
|
return "created";
|
|
374529
373098
|
}
|
|
374530
373099
|
}
|
|
374531
373100
|
function addToGitignore() {
|
|
374532
|
-
const gitignorePath =
|
|
373101
|
+
const gitignorePath = path17.join(process.cwd(), ".gitignore");
|
|
374533
373102
|
const entries = [".specific", "specific.local"];
|
|
374534
|
-
if (
|
|
374535
|
-
const existing =
|
|
373103
|
+
if (fs19.existsSync(gitignorePath)) {
|
|
373104
|
+
const existing = fs19.readFileSync(gitignorePath, "utf-8");
|
|
374536
373105
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
374537
373106
|
const missingEntries = entries.filter((entry) => !lines.includes(entry));
|
|
374538
373107
|
if (missingEntries.length === 0) {
|
|
374539
373108
|
return "unchanged";
|
|
374540
373109
|
}
|
|
374541
373110
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
374542
|
-
|
|
373111
|
+
fs19.writeFileSync(
|
|
374543
373112
|
gitignorePath,
|
|
374544
373113
|
existing + separator + missingEntries.join("\n") + "\n"
|
|
374545
373114
|
);
|
|
374546
373115
|
return "modified";
|
|
374547
373116
|
} else {
|
|
374548
|
-
|
|
373117
|
+
fs19.writeFileSync(gitignorePath, entries.join("\n") + "\n");
|
|
374549
373118
|
return "created";
|
|
374550
373119
|
}
|
|
374551
373120
|
}
|
|
374552
373121
|
function configureClaudeCodePermissions() {
|
|
374553
|
-
const claudeDir =
|
|
374554
|
-
const settingsPath =
|
|
373122
|
+
const claudeDir = path17.join(process.cwd(), ".claude");
|
|
373123
|
+
const settingsPath = path17.join(claudeDir, "settings.local.json");
|
|
374555
373124
|
const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
|
|
374556
|
-
if (
|
|
374557
|
-
const existing = JSON.parse(
|
|
373125
|
+
if (fs19.existsSync(settingsPath)) {
|
|
373126
|
+
const existing = JSON.parse(fs19.readFileSync(settingsPath, "utf-8"));
|
|
374558
373127
|
const allowList = existing?.permissions?.allow || [];
|
|
374559
373128
|
const missingPermissions = permissions.filter(
|
|
374560
373129
|
(p) => !allowList.includes(p)
|
|
@@ -374569,39 +373138,39 @@ function configureClaudeCodePermissions() {
|
|
|
374569
373138
|
existing.permissions.allow = [];
|
|
374570
373139
|
}
|
|
374571
373140
|
existing.permissions.allow.push(...missingPermissions);
|
|
374572
|
-
|
|
373141
|
+
fs19.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
|
|
374573
373142
|
return "modified";
|
|
374574
373143
|
}
|
|
374575
|
-
if (!
|
|
374576
|
-
|
|
373144
|
+
if (!fs19.existsSync(claudeDir)) {
|
|
373145
|
+
fs19.mkdirSync(claudeDir);
|
|
374577
373146
|
}
|
|
374578
373147
|
const settings = {
|
|
374579
373148
|
permissions: {
|
|
374580
373149
|
allow: permissions
|
|
374581
373150
|
}
|
|
374582
373151
|
};
|
|
374583
|
-
|
|
373152
|
+
fs19.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
374584
373153
|
return "created";
|
|
374585
373154
|
}
|
|
374586
373155
|
function createCursorRule() {
|
|
374587
|
-
const cursorDir =
|
|
374588
|
-
const rulesDir =
|
|
374589
|
-
const mdcPath =
|
|
374590
|
-
if (
|
|
374591
|
-
const existing =
|
|
373156
|
+
const cursorDir = path17.join(process.cwd(), ".cursor");
|
|
373157
|
+
const rulesDir = path17.join(cursorDir, "rules");
|
|
373158
|
+
const mdcPath = path17.join(rulesDir, "specific.mdc");
|
|
373159
|
+
if (fs19.existsSync(mdcPath)) {
|
|
373160
|
+
const existing = fs19.readFileSync(mdcPath, "utf-8");
|
|
374592
373161
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
374593
373162
|
return "unchanged";
|
|
374594
373163
|
}
|
|
374595
|
-
|
|
373164
|
+
fs19.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
374596
373165
|
return "modified";
|
|
374597
373166
|
}
|
|
374598
|
-
if (!
|
|
374599
|
-
|
|
373167
|
+
if (!fs19.existsSync(cursorDir)) {
|
|
373168
|
+
fs19.mkdirSync(cursorDir);
|
|
374600
373169
|
}
|
|
374601
|
-
if (!
|
|
374602
|
-
|
|
373170
|
+
if (!fs19.existsSync(rulesDir)) {
|
|
373171
|
+
fs19.mkdirSync(rulesDir);
|
|
374603
373172
|
}
|
|
374604
|
-
|
|
373173
|
+
fs19.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
374605
373174
|
return "created";
|
|
374606
373175
|
}
|
|
374607
373176
|
function configureAgents(checked) {
|
|
@@ -374617,7 +373186,7 @@ function configureAgents(checked) {
|
|
|
374617
373186
|
agents.filesModified.push(".cursor/rules/specific.mdc");
|
|
374618
373187
|
}
|
|
374619
373188
|
if (checked["claude"]) {
|
|
374620
|
-
const claudeMdPath =
|
|
373189
|
+
const claudeMdPath = path17.join(process.cwd(), "CLAUDE.md");
|
|
374621
373190
|
const status = appendOrCreateFile(claudeMdPath, SPECIFIC_INSTRUCTIONS);
|
|
374622
373191
|
if (status === "created") agents.filesCreated.push("CLAUDE.md");
|
|
374623
373192
|
else if (status === "modified") agents.filesModified.push("CLAUDE.md");
|
|
@@ -374628,7 +373197,7 @@ function configureAgents(checked) {
|
|
|
374628
373197
|
agents.filesModified.push(".claude/settings.local.json");
|
|
374629
373198
|
}
|
|
374630
373199
|
if (checked["codex"]) {
|
|
374631
|
-
const agentsMdPath =
|
|
373200
|
+
const agentsMdPath = path17.join(process.cwd(), "AGENTS.md");
|
|
374632
373201
|
const status = appendOrCreateFile(agentsMdPath, SPECIFIC_INSTRUCTIONS);
|
|
374633
373202
|
if (status === "created") agents.filesCreated.push("AGENTS.md");
|
|
374634
373203
|
else if (status === "modified") agents.filesModified.push("AGENTS.md");
|
|
@@ -374643,16 +373212,6 @@ function configureAgents(checked) {
|
|
|
374643
373212
|
return { agents, git, showManualInstructions: !!checked["other"] };
|
|
374644
373213
|
}
|
|
374645
373214
|
function getInitialSetupPhase() {
|
|
374646
|
-
if (!systemSetupNeeded()) return "agents";
|
|
374647
|
-
if (os11.platform() === "darwin") {
|
|
374648
|
-
if (dnsSetupNeeded()) return "setup-dns";
|
|
374649
|
-
if (tlsSetupNeeded()) return "setup-tls";
|
|
374650
|
-
return "agents";
|
|
374651
|
-
}
|
|
374652
|
-
return "setup-combined";
|
|
374653
|
-
}
|
|
374654
|
-
function getNextPhase(current) {
|
|
374655
|
-
if (current === "setup-dns" && tlsSetupNeeded()) return "setup-tls";
|
|
374656
373215
|
return "agents";
|
|
374657
373216
|
}
|
|
374658
373217
|
function InitUI() {
|
|
@@ -374666,91 +373225,13 @@ function InitUI() {
|
|
|
374666
373225
|
};
|
|
374667
373226
|
});
|
|
374668
373227
|
const [phase, setPhase] = useState(getInitialSetupPhase);
|
|
374669
|
-
const [stepPhase, setStepPhase] = useState(
|
|
374670
|
-
() => getInitialSetupPhase() === "agents" ? "done" : "prompt"
|
|
374671
|
-
);
|
|
374672
|
-
const [stepError, setStepError] = useState();
|
|
374673
373228
|
const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
|
|
374674
373229
|
const [checked, setChecked] = useState(
|
|
374675
373230
|
initialState.detected
|
|
374676
373231
|
);
|
|
374677
373232
|
const [result, setResult] = useState(null);
|
|
374678
|
-
const [setupResult, setSetupResult] = useState(
|
|
374679
|
-
() => !systemSetupNeeded() ? { dns: { status: "success" }, tls: { status: "success" } } : {}
|
|
374680
|
-
);
|
|
374681
|
-
useEffect(() => {
|
|
374682
|
-
if (stepPhase !== "installing") return;
|
|
374683
|
-
if (phase === "setup-dns") {
|
|
374684
|
-
runSetupStep(
|
|
374685
|
-
() => {
|
|
374686
|
-
ensureCAGenerated();
|
|
374687
|
-
performDnsSetup();
|
|
374688
|
-
},
|
|
374689
|
-
"dns"
|
|
374690
|
-
);
|
|
374691
|
-
} else if (phase === "setup-tls") {
|
|
374692
|
-
runSetupStep(
|
|
374693
|
-
() => {
|
|
374694
|
-
ensureCAGenerated();
|
|
374695
|
-
performTlsTrust();
|
|
374696
|
-
},
|
|
374697
|
-
"tls"
|
|
374698
|
-
);
|
|
374699
|
-
} else if (phase === "setup-combined") {
|
|
374700
|
-
runSetupStep(
|
|
374701
|
-
() => performSystemSetup(),
|
|
374702
|
-
"combined"
|
|
374703
|
-
);
|
|
374704
|
-
}
|
|
374705
|
-
}, [phase, stepPhase]);
|
|
374706
|
-
function runSetupStep(fn, type) {
|
|
374707
|
-
try {
|
|
374708
|
-
fn();
|
|
374709
|
-
if (type === "combined") {
|
|
374710
|
-
setSetupResult((r) => ({
|
|
374711
|
-
...r,
|
|
374712
|
-
dns: { status: "success" },
|
|
374713
|
-
tls: { status: "success" }
|
|
374714
|
-
}));
|
|
374715
|
-
} else {
|
|
374716
|
-
setSetupResult((r) => ({ ...r, [type]: { status: "success" } }));
|
|
374717
|
-
}
|
|
374718
|
-
setStepPhase("done");
|
|
374719
|
-
const next = getNextPhase(phase);
|
|
374720
|
-
setPhase(next);
|
|
374721
|
-
if (next !== "agents") {
|
|
374722
|
-
setStepPhase("prompt");
|
|
374723
|
-
}
|
|
374724
|
-
} catch (err) {
|
|
374725
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
374726
|
-
if (type === "combined") {
|
|
374727
|
-
setSetupResult((r) => ({
|
|
374728
|
-
...r,
|
|
374729
|
-
dns: { status: "error", error: errorMsg },
|
|
374730
|
-
tls: { status: "error", error: errorMsg }
|
|
374731
|
-
}));
|
|
374732
|
-
} else {
|
|
374733
|
-
setSetupResult((r) => ({
|
|
374734
|
-
...r,
|
|
374735
|
-
[type]: { status: "error", error: errorMsg }
|
|
374736
|
-
}));
|
|
374737
|
-
}
|
|
374738
|
-
setStepError(errorMsg);
|
|
374739
|
-
setStepPhase("error");
|
|
374740
|
-
const next = getNextPhase(phase);
|
|
374741
|
-
setPhase(next);
|
|
374742
|
-
if (next !== "agents") {
|
|
374743
|
-
setStepPhase("prompt");
|
|
374744
|
-
setStepError(void 0);
|
|
374745
|
-
}
|
|
374746
|
-
}
|
|
374747
|
-
}
|
|
374748
373233
|
const isSubmitFocused = focusedIndex === options.length;
|
|
374749
373234
|
useInput((input, key) => {
|
|
374750
|
-
if ((phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") && stepPhase === "prompt" && key.return) {
|
|
374751
|
-
setStepPhase("installing");
|
|
374752
|
-
return;
|
|
374753
|
-
}
|
|
374754
373235
|
if (phase !== "agents") return;
|
|
374755
373236
|
if (key.upArrow || input === "k") {
|
|
374756
373237
|
setFocusedIndex((prev) => Math.max(0, prev - 1));
|
|
@@ -374779,30 +373260,13 @@ function InitUI() {
|
|
|
374779
373260
|
return () => clearTimeout(timer);
|
|
374780
373261
|
}
|
|
374781
373262
|
}, [phase, exit]);
|
|
374782
|
-
if (phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") {
|
|
374783
|
-
if (stepPhase === "prompt") {
|
|
374784
|
-
const isDns = phase === "setup-dns";
|
|
374785
|
-
const isTls = phase === "setup-tls";
|
|
374786
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), (isDns || phase === "setup-combined") && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize.")), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to continue."));
|
|
374787
|
-
}
|
|
374788
|
-
if (stepPhase === "installing") {
|
|
374789
|
-
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"));
|
|
374790
|
-
}
|
|
374791
|
-
}
|
|
374792
373263
|
if (phase === "done") {
|
|
374793
373264
|
const selectedAgents = Object.entries(checked).filter(([, v]) => v).length;
|
|
374794
373265
|
const agentChanges = result && (result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0);
|
|
374795
373266
|
const gitChanges = result?.git && (result.git.filesCreated.length > 0 || result.git.filesModified.length > 0);
|
|
374796
|
-
|
|
374797
|
-
const setupError2 = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
|
|
374798
|
-
const setupErrorMsg2 = setupResult.dns?.error || setupResult.tls?.error;
|
|
374799
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupSuccess2 && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured"), setupError2 && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " Failed to set up TLS and DNS"), setupErrorMsg2 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", setupErrorMsg2)), result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
|
|
373267
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
|
|
374800
373268
|
}
|
|
374801
|
-
|
|
374802
|
-
const setupError = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
|
|
374803
|
-
const setupErrorMsg = setupResult.dns?.error || setupResult.tls?.error;
|
|
374804
|
-
const setupLine = setupSuccess ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured") : setupError ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS and DNS setup failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", setupErrorMsg, ")")) : null;
|
|
374805
|
-
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupLine, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
|
|
373269
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
|
|
374806
373270
|
const isFocused = focusedIndex === index;
|
|
374807
373271
|
const isChecked = checked[option.id];
|
|
374808
373272
|
return /* @__PURE__ */ React2.createElement(Box2, { key: option.id }, /* @__PURE__ */ React2.createElement(Text2, { ...isFocused && { color: "cyan" } }, isFocused ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", option.label));
|
|
@@ -374872,11 +373336,6 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
|
|
|
374872
373336
|
console.log(" No changes needed (.gitignore already configured)");
|
|
374873
373337
|
}
|
|
374874
373338
|
}
|
|
374875
|
-
if (systemSetupNeeded()) {
|
|
374876
|
-
console.log(
|
|
374877
|
-
"\u26A0 TLS and DNS not configured (run `specific init` in a terminal to set up)"
|
|
374878
|
-
);
|
|
374879
|
-
}
|
|
374880
373339
|
if (result.showManualInstructions) {
|
|
374881
373340
|
console.log("\n\u2713 Manual configuration selected");
|
|
374882
373341
|
console.log("\n Add this to your agent's system prompt:");
|
|
@@ -374891,8 +373350,8 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
|
|
|
374891
373350
|
}
|
|
374892
373351
|
|
|
374893
373352
|
// src/commands/docs.tsx
|
|
374894
|
-
import { readFileSync as
|
|
374895
|
-
import { join as
|
|
373353
|
+
import { readFileSync as readFileSync9, existsSync as existsSync17 } from "fs";
|
|
373354
|
+
import { join as join18, dirname as dirname8 } from "path";
|
|
374896
373355
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
374897
373356
|
|
|
374898
373357
|
// src/lib/beta/registry.ts
|
|
@@ -374908,16 +373367,16 @@ var BETA_REGISTRY = [
|
|
|
374908
373367
|
];
|
|
374909
373368
|
|
|
374910
373369
|
// src/lib/beta/storage.ts
|
|
374911
|
-
import { readFileSync as
|
|
374912
|
-
import { join as
|
|
373370
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync16, mkdirSync as mkdirSync12 } from "fs";
|
|
373371
|
+
import { join as join17 } from "path";
|
|
374913
373372
|
var BETAS_FILE = ".specific/betas.json";
|
|
374914
373373
|
function loadEnabledBetas(projectDir = process.cwd()) {
|
|
374915
|
-
const filePath =
|
|
374916
|
-
if (!
|
|
373374
|
+
const filePath = join17(projectDir, BETAS_FILE);
|
|
373375
|
+
if (!existsSync16(filePath)) {
|
|
374917
373376
|
return [];
|
|
374918
373377
|
}
|
|
374919
373378
|
try {
|
|
374920
|
-
const content =
|
|
373379
|
+
const content = readFileSync8(filePath, "utf-8");
|
|
374921
373380
|
const data = JSON.parse(content);
|
|
374922
373381
|
return data.enabled ?? [];
|
|
374923
373382
|
} catch {
|
|
@@ -374936,26 +373395,26 @@ function disableBeta(name, projectDir = process.cwd()) {
|
|
|
374936
373395
|
saveBetas(enabled, projectDir);
|
|
374937
373396
|
}
|
|
374938
373397
|
function saveBetas(enabled, projectDir) {
|
|
374939
|
-
const specificDir =
|
|
374940
|
-
if (!
|
|
374941
|
-
|
|
373398
|
+
const specificDir = join17(projectDir, ".specific");
|
|
373399
|
+
if (!existsSync16(specificDir)) {
|
|
373400
|
+
mkdirSync12(specificDir, { recursive: true });
|
|
374942
373401
|
}
|
|
374943
373402
|
const data = { enabled };
|
|
374944
|
-
|
|
374945
|
-
|
|
373403
|
+
writeFileSync7(
|
|
373404
|
+
join17(projectDir, BETAS_FILE),
|
|
374946
373405
|
JSON.stringify(data, null, 2) + "\n"
|
|
374947
373406
|
);
|
|
374948
373407
|
}
|
|
374949
373408
|
|
|
374950
373409
|
// src/commands/docs.tsx
|
|
374951
373410
|
var __dirname3 = dirname8(fileURLToPath3(import.meta.url));
|
|
374952
|
-
var docsDir =
|
|
373411
|
+
var docsDir = join18(__dirname3, "docs");
|
|
374953
373412
|
var _embeddedDocs = null;
|
|
374954
|
-
function docsCommand(
|
|
374955
|
-
const content = resolveDocContent(
|
|
373413
|
+
function docsCommand(path26) {
|
|
373414
|
+
const content = resolveDocContent(path26);
|
|
374956
373415
|
if (!content) {
|
|
374957
373416
|
console.error(
|
|
374958
|
-
`Documentation not found: ${
|
|
373417
|
+
`Documentation not found: ${path26 || "index"}
|
|
374959
373418
|
|
|
374960
373419
|
Run 'specific docs' to see available topics.`
|
|
374961
373420
|
);
|
|
@@ -374973,33 +373432,33 @@ function filterBetaTags(content, enabledBetas) {
|
|
|
374973
373432
|
}
|
|
374974
373433
|
);
|
|
374975
373434
|
}
|
|
374976
|
-
function resolveDocContent(
|
|
374977
|
-
const normalized =
|
|
373435
|
+
function resolveDocContent(path26) {
|
|
373436
|
+
const normalized = path26?.replace(/^\/+|\/+$/g, "") || void 0;
|
|
374978
373437
|
if (_embeddedDocs) {
|
|
374979
373438
|
return resolveEmbeddedDoc(normalized);
|
|
374980
373439
|
}
|
|
374981
373440
|
return resolveFilesystemDoc(normalized);
|
|
374982
373441
|
}
|
|
374983
|
-
function resolveEmbeddedDoc(
|
|
374984
|
-
if (!
|
|
373442
|
+
function resolveEmbeddedDoc(path26) {
|
|
373443
|
+
if (!path26) {
|
|
374985
373444
|
return _embeddedDocs.get("index.md") ?? null;
|
|
374986
373445
|
}
|
|
374987
|
-
const direct = _embeddedDocs.get(`${
|
|
373446
|
+
const direct = _embeddedDocs.get(`${path26}.md`);
|
|
374988
373447
|
if (direct) return direct;
|
|
374989
|
-
return _embeddedDocs.get(`${
|
|
373448
|
+
return _embeddedDocs.get(`${path26}/index.md`) ?? null;
|
|
374990
373449
|
}
|
|
374991
|
-
function resolveFilesystemDoc(
|
|
374992
|
-
if (!
|
|
374993
|
-
const indexPath2 =
|
|
374994
|
-
return
|
|
373450
|
+
function resolveFilesystemDoc(path26) {
|
|
373451
|
+
if (!path26) {
|
|
373452
|
+
const indexPath2 = join18(docsDir, "index.md");
|
|
373453
|
+
return existsSync17(indexPath2) ? readFileSync9(indexPath2, "utf-8") : null;
|
|
374995
373454
|
}
|
|
374996
|
-
const directPath =
|
|
374997
|
-
if (
|
|
374998
|
-
return
|
|
373455
|
+
const directPath = join18(docsDir, `${path26}.md`);
|
|
373456
|
+
if (existsSync17(directPath)) {
|
|
373457
|
+
return readFileSync9(directPath, "utf-8");
|
|
374999
373458
|
}
|
|
375000
|
-
const indexPath =
|
|
375001
|
-
if (
|
|
375002
|
-
return
|
|
373459
|
+
const indexPath = join18(docsDir, path26, "index.md");
|
|
373460
|
+
if (existsSync17(indexPath)) {
|
|
373461
|
+
return readFileSync9(indexPath, "utf-8");
|
|
375003
373462
|
}
|
|
375004
373463
|
return null;
|
|
375005
373464
|
}
|
|
@@ -375007,9 +373466,9 @@ function resolveFilesystemDoc(path30) {
|
|
|
375007
373466
|
// src/commands/check.tsx
|
|
375008
373467
|
import React3, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
375009
373468
|
import { render as render3, Text as Text3, Box as Box3 } from "ink";
|
|
375010
|
-
import
|
|
375011
|
-
import * as
|
|
375012
|
-
import * as
|
|
373469
|
+
import Spinner2 from "ink-spinner";
|
|
373470
|
+
import * as fs20 from "fs";
|
|
373471
|
+
import * as path18 from "path";
|
|
375013
373472
|
import { execFile as execFile7 } from "child_process";
|
|
375014
373473
|
|
|
375015
373474
|
// node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
|
|
@@ -375681,20 +374140,20 @@ function CheckUI() {
|
|
|
375681
374140
|
const [state, setState] = useState2({ status: "loading" });
|
|
375682
374141
|
useEffect2(() => {
|
|
375683
374142
|
async function load() {
|
|
375684
|
-
const configPath =
|
|
375685
|
-
if (!
|
|
374143
|
+
const configPath = path18.join(process.cwd(), "specific.hcl");
|
|
374144
|
+
if (!fs20.existsSync(configPath)) {
|
|
375686
374145
|
setState({
|
|
375687
374146
|
status: "error",
|
|
375688
374147
|
error: "No specific.hcl found in current directory"
|
|
375689
374148
|
});
|
|
375690
374149
|
return;
|
|
375691
374150
|
}
|
|
375692
|
-
const hcl =
|
|
374151
|
+
const hcl = fs20.readFileSync(configPath, "utf-8");
|
|
375693
374152
|
try {
|
|
375694
374153
|
const config2 = await parseConfig2(hcl);
|
|
375695
|
-
const localPath =
|
|
375696
|
-
if (
|
|
375697
|
-
const localContent =
|
|
374154
|
+
const localPath = path18.join(process.cwd(), LOCAL_FILE);
|
|
374155
|
+
if (fs20.existsSync(localPath)) {
|
|
374156
|
+
const localContent = fs20.readFileSync(localPath, "utf-8");
|
|
375698
374157
|
try {
|
|
375699
374158
|
await parseLocalFile(localContent);
|
|
375700
374159
|
} catch (localErr) {
|
|
@@ -375707,8 +374166,8 @@ function CheckUI() {
|
|
|
375707
374166
|
}
|
|
375708
374167
|
for (const build of config2.builds) {
|
|
375709
374168
|
if (build.dockerfile) {
|
|
375710
|
-
const dockerfilePath =
|
|
375711
|
-
if (!
|
|
374169
|
+
const dockerfilePath = path18.resolve(process.cwd(), build.dockerfile);
|
|
374170
|
+
if (!fs20.existsSync(dockerfilePath)) {
|
|
375712
374171
|
setState({
|
|
375713
374172
|
status: "error",
|
|
375714
374173
|
error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
|
|
@@ -375720,11 +374179,11 @@ function CheckUI() {
|
|
|
375720
374179
|
const reshapeChecks2 = [];
|
|
375721
374180
|
for (const pg of config2.postgres) {
|
|
375722
374181
|
if (pg.reshape?.enabled) {
|
|
375723
|
-
const migrationsDir =
|
|
374182
|
+
const migrationsDir = path18.resolve(
|
|
375724
374183
|
process.cwd(),
|
|
375725
374184
|
pg.reshape.migrations_dir ?? "migrations"
|
|
375726
374185
|
);
|
|
375727
|
-
if (!
|
|
374186
|
+
if (!fs20.existsSync(migrationsDir)) {
|
|
375728
374187
|
reshapeChecks2.push({
|
|
375729
374188
|
databaseName: pg.name,
|
|
375730
374189
|
migrationsDir: pg.reshape.migrations_dir ?? "migrations",
|
|
@@ -375755,7 +374214,7 @@ function CheckUI() {
|
|
|
375755
374214
|
load();
|
|
375756
374215
|
}, []);
|
|
375757
374216
|
if (state.status === "loading") {
|
|
375758
|
-
return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, /* @__PURE__ */ React3.createElement(
|
|
374217
|
+
return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, /* @__PURE__ */ React3.createElement(Spinner2, { type: "dots" })), /* @__PURE__ */ React3.createElement(Text3, null, " Checking configuration..."));
|
|
375759
374218
|
}
|
|
375760
374219
|
if (state.status === "error") {
|
|
375761
374220
|
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, state.error));
|
|
@@ -375776,7 +374235,7 @@ function checkCommand() {
|
|
|
375776
374235
|
// src/commands/dev.tsx
|
|
375777
374236
|
import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
|
|
375778
374237
|
import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
|
|
375779
|
-
import
|
|
374238
|
+
import Spinner3 from "ink-spinner";
|
|
375780
374239
|
import { Readable as Readable2 } from "stream";
|
|
375781
374240
|
|
|
375782
374241
|
// src/lib/ui/SecretInput.tsx
|
|
@@ -375873,13 +374332,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
375873
374332
|
devEnvRef.current = devEnv;
|
|
375874
374333
|
let currentColorMap = /* @__PURE__ */ new Map();
|
|
375875
374334
|
devEnv.on("status", (status) => {
|
|
375876
|
-
if (status === "
|
|
375877
|
-
setState((s) => ({
|
|
375878
|
-
...s,
|
|
375879
|
-
status: "installing-ca",
|
|
375880
|
-
caInstallPhase: "installing"
|
|
375881
|
-
}));
|
|
375882
|
-
} else if (status === "loading") {
|
|
374335
|
+
if (status === "loading") {
|
|
375883
374336
|
setState((s) => ({ ...s, status: "loading" }));
|
|
375884
374337
|
} else if (status === "starting") {
|
|
375885
374338
|
const config2 = devEnv.config;
|
|
@@ -375988,20 +374441,11 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
375988
374441
|
}
|
|
375989
374442
|
});
|
|
375990
374443
|
devEnv.on("error", (error) => {
|
|
375991
|
-
setState((s) => {
|
|
375992
|
-
|
|
375993
|
-
|
|
375994
|
-
|
|
375995
|
-
|
|
375996
|
-
caError: error.message
|
|
375997
|
-
};
|
|
375998
|
-
}
|
|
375999
|
-
return {
|
|
376000
|
-
...s,
|
|
376001
|
-
status: "error",
|
|
376002
|
-
error: error.message
|
|
376003
|
-
};
|
|
376004
|
-
});
|
|
374444
|
+
setState((s) => ({
|
|
374445
|
+
...s,
|
|
374446
|
+
status: "error",
|
|
374447
|
+
error: error.message
|
|
374448
|
+
}));
|
|
376005
374449
|
});
|
|
376006
374450
|
devEnv.on("configError", (errorStr) => {
|
|
376007
374451
|
setState((s) => ({ ...s, parseError: errorStr }));
|
|
@@ -376116,16 +374560,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
376116
374560
|
currentConfigIndex: void 0
|
|
376117
374561
|
}));
|
|
376118
374562
|
};
|
|
376119
|
-
if (state.status === "installing-ca") {
|
|
376120
|
-
if (state.caInstallPhase === "installing") {
|
|
376121
|
-
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"));
|
|
376122
|
-
}
|
|
376123
|
-
if (state.caInstallPhase === "error") {
|
|
376124
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
|
|
376125
|
-
}
|
|
376126
|
-
}
|
|
376127
374563
|
if (state.status === "loading") {
|
|
376128
|
-
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(
|
|
374564
|
+
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })));
|
|
376129
374565
|
}
|
|
376130
374566
|
if (state.status === "awaiting-secrets" && state.missingSecrets && state.currentSecretIndex !== void 0) {
|
|
376131
374567
|
const currentSecret = state.missingSecrets[state.currentSecretIndex];
|
|
@@ -376152,20 +374588,20 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
376152
374588
|
));
|
|
376153
374589
|
}
|
|
376154
374590
|
if (state.status === "waiting") {
|
|
376155
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(
|
|
374591
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " No specific.hcl in project yet. Go build something with your coding agent!")));
|
|
376156
374592
|
}
|
|
376157
374593
|
if (state.status === "error") {
|
|
376158
374594
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, state.error));
|
|
376159
374595
|
}
|
|
376160
374596
|
if (state.status === "stopping") {
|
|
376161
|
-
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(
|
|
374597
|
+
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Shutting down..."));
|
|
376162
374598
|
}
|
|
376163
374599
|
if (state.status === "reloading") {
|
|
376164
|
-
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(
|
|
374600
|
+
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Reloading (specific.hcl changed)...")));
|
|
376165
374601
|
}
|
|
376166
374602
|
const { config, resources, services, output } = state;
|
|
376167
374603
|
if (!config) {
|
|
376168
|
-
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(
|
|
374604
|
+
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })));
|
|
376169
374605
|
}
|
|
376170
374606
|
if (state.status === "starting") {
|
|
376171
374607
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)")), /* @__PURE__ */ React6.createElement(Text6, null, " "), (config.postgres.length > 0 || config.redis.length > 0 || config.storage.length > 0 || config.temporal.length > 0) && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Resources:"), config.postgres.map((pg) => {
|
|
@@ -376228,17 +374664,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
376228
374664
|
const defaultPort = running?.ports.get("default");
|
|
376229
374665
|
const portInfo = defaultPort ? ` \u2192 localhost:${defaultPort}` : "";
|
|
376230
374666
|
return /* @__PURE__ */ React6.createElement(Text6, { key: svc.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name, portInfo));
|
|
376231
|
-
}), /* @__PURE__ */ React6.createElement(Text6, null, " ")), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(
|
|
374667
|
+
}), /* @__PURE__ */ React6.createElement(Text6, null, " ")), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Starting...")));
|
|
376232
374668
|
}
|
|
376233
|
-
const portSuffix = (() => {
|
|
376234
|
-
if (!state.adminUrl || !state.adminUrl.startsWith("https://")) return "";
|
|
376235
|
-
try {
|
|
376236
|
-
const p = new URL(state.adminUrl).port;
|
|
376237
|
-
return p ? `:${p}` : "";
|
|
376238
|
-
} catch {
|
|
376239
|
-
return "";
|
|
376240
|
-
}
|
|
376241
|
-
})();
|
|
376242
374669
|
const staticItems = [
|
|
376243
374670
|
{
|
|
376244
374671
|
key: "title",
|
|
@@ -376287,7 +374714,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
376287
374714
|
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
376288
374715
|
return {
|
|
376289
374716
|
key: `svc-${svc.name}-${endpoint.name}`,
|
|
376290
|
-
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 }, "
|
|
374717
|
+
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 }, "http://localhost:", port)) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
|
|
376291
374718
|
};
|
|
376292
374719
|
});
|
|
376293
374720
|
}),
|
|
@@ -376362,9 +374789,9 @@ function devCommand(instanceKey, tunnelEnabled = false) {
|
|
|
376362
374789
|
init_open();
|
|
376363
374790
|
import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
|
|
376364
374791
|
import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput4 } from "ink";
|
|
376365
|
-
import
|
|
376366
|
-
import * as
|
|
376367
|
-
import * as
|
|
374792
|
+
import Spinner4 from "ink-spinner";
|
|
374793
|
+
import * as fs21 from "fs";
|
|
374794
|
+
import * as path19 from "path";
|
|
376368
374795
|
function formatBytes(bytes) {
|
|
376369
374796
|
if (bytes < 1024) return `${bytes} B`;
|
|
376370
374797
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -376400,7 +374827,7 @@ function PhaseIndicator({
|
|
|
376400
374827
|
}
|
|
376401
374828
|
if (phaseIndex === currentIndex) {
|
|
376402
374829
|
if (showSpinner) {
|
|
376403
|
-
return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(
|
|
374830
|
+
return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), " ", label);
|
|
376404
374831
|
}
|
|
376405
374832
|
return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, " ", "\u25CF"), " ", label);
|
|
376406
374833
|
}
|
|
@@ -377118,13 +375545,13 @@ function DeployUI({ environment, config }) {
|
|
|
377118
375545
|
verificationUri
|
|
377119
375546
|
} = state;
|
|
377120
375547
|
if (phase === "checking-auth") {
|
|
377121
|
-
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(
|
|
375548
|
+
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Checking authentication..."));
|
|
377122
375549
|
}
|
|
377123
375550
|
if (phase === "logging-in") {
|
|
377124
|
-
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(
|
|
375551
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Initiating login...")));
|
|
377125
375552
|
}
|
|
377126
375553
|
if (phase === "loading-projects") {
|
|
377127
|
-
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(
|
|
375554
|
+
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
|
|
377128
375555
|
}
|
|
377129
375556
|
if (phase === "selecting-project" && projects && state.organizations && selectedIndex !== void 0) {
|
|
377130
375557
|
const selectorItems = buildSelectorItems(projects, state.organizations);
|
|
@@ -377155,7 +375582,7 @@ function DeployUI({ environment, config }) {
|
|
|
377155
375582
|
return /* @__PURE__ */ React7.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
|
|
377156
375583
|
}
|
|
377157
375584
|
if (phase === "creating-project") {
|
|
377158
|
-
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(
|
|
375585
|
+
return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Creating project..."));
|
|
377159
375586
|
}
|
|
377160
375587
|
const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
|
|
377161
375588
|
const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
|
|
@@ -377236,13 +375663,13 @@ function DeployUI({ environment, config }) {
|
|
|
377236
375663
|
), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, deployment?.error ? /* @__PURE__ */ React7.createElement(StructuredError, { error: deployment.error }) : /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, error), buildOutput && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Build output:"), /* @__PURE__ */ React7.createElement(Text7, null, buildOutput))), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
|
|
377237
375664
|
}
|
|
377238
375665
|
async function deployCommand(environment) {
|
|
377239
|
-
const configPath =
|
|
377240
|
-
if (!
|
|
375666
|
+
const configPath = path19.join(process.cwd(), "specific.hcl");
|
|
375667
|
+
if (!fs21.existsSync(configPath)) {
|
|
377241
375668
|
console.error("Error: No specific.hcl found in current directory");
|
|
377242
375669
|
process.exit(1);
|
|
377243
375670
|
}
|
|
377244
375671
|
let config;
|
|
377245
|
-
const hcl =
|
|
375672
|
+
const hcl = fs21.readFileSync(configPath, "utf-8");
|
|
377246
375673
|
try {
|
|
377247
375674
|
config = await parseConfig2(hcl);
|
|
377248
375675
|
} catch (err) {
|
|
@@ -377263,8 +375690,8 @@ async function deployCommand(environment) {
|
|
|
377263
375690
|
|
|
377264
375691
|
// src/commands/exec.tsx
|
|
377265
375692
|
import { spawn as spawn6 } from "child_process";
|
|
377266
|
-
import * as
|
|
377267
|
-
import * as
|
|
375693
|
+
import * as fs22 from "fs";
|
|
375694
|
+
import * as path20 from "path";
|
|
377268
375695
|
function startSpinner(text) {
|
|
377269
375696
|
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
377270
375697
|
let i = 0;
|
|
@@ -377305,13 +375732,13 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
377305
375732
|
}
|
|
377306
375733
|
}
|
|
377307
375734
|
};
|
|
377308
|
-
const configPath =
|
|
377309
|
-
if (!
|
|
375735
|
+
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
375736
|
+
if (!fs22.existsSync(configPath)) {
|
|
377310
375737
|
console.error("Error: No specific.hcl found in current directory");
|
|
377311
375738
|
process.exit(1);
|
|
377312
375739
|
}
|
|
377313
375740
|
let config;
|
|
377314
|
-
const hcl =
|
|
375741
|
+
const hcl = fs22.readFileSync(configPath, "utf-8");
|
|
377315
375742
|
try {
|
|
377316
375743
|
config = await parseConfig2(hcl);
|
|
377317
375744
|
} catch (err) {
|
|
@@ -377448,11 +375875,11 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
377448
375875
|
process.on("SIGTERM", () => handleSignal("SIGTERM"));
|
|
377449
375876
|
let effectiveCwd = process.cwd();
|
|
377450
375877
|
if (service.root) {
|
|
377451
|
-
effectiveCwd =
|
|
375878
|
+
effectiveCwd = path20.resolve(process.cwd(), service.root);
|
|
377452
375879
|
} else if (service.build) {
|
|
377453
375880
|
const build = config.builds.find((b) => b.name === service.build.name);
|
|
377454
375881
|
if (build?.root) {
|
|
377455
|
-
effectiveCwd =
|
|
375882
|
+
effectiveCwd = path20.resolve(process.cwd(), build.root);
|
|
377456
375883
|
}
|
|
377457
375884
|
}
|
|
377458
375885
|
spinner?.stop();
|
|
@@ -377481,8 +375908,8 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
377481
375908
|
|
|
377482
375909
|
// src/commands/psql.tsx
|
|
377483
375910
|
import { spawn as spawn7 } from "child_process";
|
|
377484
|
-
import * as
|
|
377485
|
-
import * as
|
|
375911
|
+
import * as fs23 from "fs";
|
|
375912
|
+
import * as path21 from "path";
|
|
377486
375913
|
async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
|
|
377487
375914
|
let startedResources = [];
|
|
377488
375915
|
let ownsInstances = false;
|
|
@@ -377499,13 +375926,13 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
377499
375926
|
}
|
|
377500
375927
|
}
|
|
377501
375928
|
};
|
|
377502
|
-
const configPath =
|
|
377503
|
-
if (!
|
|
375929
|
+
const configPath = path21.join(process.cwd(), "specific.hcl");
|
|
375930
|
+
if (!fs23.existsSync(configPath)) {
|
|
377504
375931
|
console.error("Error: No specific.hcl found in current directory");
|
|
377505
375932
|
process.exit(1);
|
|
377506
375933
|
}
|
|
377507
375934
|
let config;
|
|
377508
|
-
const hcl =
|
|
375935
|
+
const hcl = fs23.readFileSync(configPath, "utf-8");
|
|
377509
375936
|
try {
|
|
377510
375937
|
config = await parseConfig2(hcl);
|
|
377511
375938
|
} catch (err) {
|
|
@@ -377629,8 +376056,8 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
|
|
|
377629
376056
|
|
|
377630
376057
|
// src/commands/reshape.tsx
|
|
377631
376058
|
import { spawn as spawn8 } from "child_process";
|
|
377632
|
-
import * as
|
|
377633
|
-
import * as
|
|
376059
|
+
import * as fs24 from "fs";
|
|
376060
|
+
import * as path22 from "path";
|
|
377634
376061
|
var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
|
|
377635
376062
|
var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
|
|
377636
376063
|
var OFFLINE_ACTIONS = ["check"];
|
|
@@ -377642,13 +376069,13 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
377642
376069
|
process.exit(1);
|
|
377643
376070
|
}
|
|
377644
376071
|
const isOfflineAction = OFFLINE_ACTIONS.includes(action);
|
|
377645
|
-
const configPath =
|
|
376072
|
+
const configPath = path22.join(process.cwd(), "specific.hcl");
|
|
377646
376073
|
let config;
|
|
377647
376074
|
let migrationsDir = "migrations";
|
|
377648
376075
|
let targetDb;
|
|
377649
376076
|
try {
|
|
377650
|
-
if (
|
|
377651
|
-
const configContent =
|
|
376077
|
+
if (fs24.existsSync(configPath)) {
|
|
376078
|
+
const configContent = fs24.readFileSync(configPath, "utf-8");
|
|
377652
376079
|
config = await parseConfig2(configContent);
|
|
377653
376080
|
if (databaseName) {
|
|
377654
376081
|
const postgresConfig = config.postgres.find((p) => p.name === databaseName);
|
|
@@ -377782,9 +376209,9 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
377782
376209
|
}
|
|
377783
376210
|
const isMigrationSubcommand = MIGRATION_SUBCOMMANDS.includes(action);
|
|
377784
376211
|
const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
|
|
377785
|
-
const fullMigrationsPath =
|
|
376212
|
+
const fullMigrationsPath = path22.join(process.cwd(), migrationsDir);
|
|
377786
376213
|
if (action === "check" || action === "start") {
|
|
377787
|
-
if (
|
|
376214
|
+
if (fs24.existsSync(fullMigrationsPath)) {
|
|
377788
376215
|
reshapeArgs.push("--dirs", fullMigrationsPath);
|
|
377789
376216
|
} else if (action === "check") {
|
|
377790
376217
|
console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
|
|
@@ -377833,22 +376260,22 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
|
|
|
377833
376260
|
// src/commands/clean.tsx
|
|
377834
376261
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
377835
376262
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
377836
|
-
import
|
|
377837
|
-
import * as
|
|
377838
|
-
import * as
|
|
376263
|
+
import Spinner5 from "ink-spinner";
|
|
376264
|
+
import * as fs25 from "fs";
|
|
376265
|
+
import * as path23 from "path";
|
|
377839
376266
|
function CleanUI({ instanceKey }) {
|
|
377840
376267
|
const [state, setState] = useState7({ status: "checking" });
|
|
377841
376268
|
useEffect5(() => {
|
|
377842
376269
|
async function clean() {
|
|
377843
376270
|
const projectRoot = process.cwd();
|
|
377844
|
-
const specificDir =
|
|
377845
|
-
if (!
|
|
376271
|
+
const specificDir = path23.join(projectRoot, ".specific");
|
|
376272
|
+
if (!fs25.existsSync(specificDir)) {
|
|
377846
376273
|
setState({ status: "nothing" });
|
|
377847
376274
|
return;
|
|
377848
376275
|
}
|
|
377849
376276
|
if (instanceKey) {
|
|
377850
|
-
const keyDir =
|
|
377851
|
-
if (!
|
|
376277
|
+
const keyDir = path23.join(specificDir, "keys", instanceKey);
|
|
376278
|
+
if (!fs25.existsSync(keyDir)) {
|
|
377852
376279
|
setState({ status: "nothing" });
|
|
377853
376280
|
return;
|
|
377854
376281
|
}
|
|
@@ -377864,7 +376291,7 @@ function CleanUI({ instanceKey }) {
|
|
|
377864
376291
|
await stateManager.cleanStaleState();
|
|
377865
376292
|
setState({ status: "cleaning" });
|
|
377866
376293
|
try {
|
|
377867
|
-
|
|
376294
|
+
fs25.rmSync(keyDir, { recursive: true, force: true });
|
|
377868
376295
|
setState({ status: "success" });
|
|
377869
376296
|
} catch (err) {
|
|
377870
376297
|
setState({
|
|
@@ -377873,13 +376300,13 @@ function CleanUI({ instanceKey }) {
|
|
|
377873
376300
|
});
|
|
377874
376301
|
}
|
|
377875
376302
|
} else {
|
|
377876
|
-
const keysDir =
|
|
377877
|
-
if (!
|
|
376303
|
+
const keysDir = path23.join(specificDir, "keys");
|
|
376304
|
+
if (!fs25.existsSync(keysDir)) {
|
|
377878
376305
|
setState({ status: "nothing" });
|
|
377879
376306
|
return;
|
|
377880
376307
|
}
|
|
377881
|
-
const keys =
|
|
377882
|
-
(f) =>
|
|
376308
|
+
const keys = fs25.readdirSync(keysDir).filter(
|
|
376309
|
+
(f) => fs25.statSync(path23.join(keysDir, f)).isDirectory()
|
|
377883
376310
|
);
|
|
377884
376311
|
for (const key of keys) {
|
|
377885
376312
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -377903,7 +376330,7 @@ function CleanUI({ instanceKey }) {
|
|
|
377903
376330
|
}
|
|
377904
376331
|
setState({ status: "cleaning" });
|
|
377905
376332
|
try {
|
|
377906
|
-
|
|
376333
|
+
fs25.rmSync(keysDir, { recursive: true, force: true });
|
|
377907
376334
|
setState({ status: "success" });
|
|
377908
376335
|
} catch (err) {
|
|
377909
376336
|
setState({
|
|
@@ -377916,10 +376343,10 @@ function CleanUI({ instanceKey }) {
|
|
|
377916
376343
|
clean();
|
|
377917
376344
|
}, [instanceKey]);
|
|
377918
376345
|
if (state.status === "checking") {
|
|
377919
|
-
return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(
|
|
376346
|
+
return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Checking for running instances..."));
|
|
377920
376347
|
}
|
|
377921
376348
|
if (state.status === "cleaning") {
|
|
377922
|
-
return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(
|
|
376349
|
+
return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Cleaning app data..."));
|
|
377923
376350
|
}
|
|
377924
376351
|
if (state.status === "error") {
|
|
377925
376352
|
return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", state.error);
|
|
@@ -378065,11 +376492,11 @@ function betaCommand() {
|
|
|
378065
376492
|
// src/commands/update.tsx
|
|
378066
376493
|
import React11, { useState as useState10, useEffect as useEffect8 } from "react";
|
|
378067
376494
|
import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } from "ink";
|
|
378068
|
-
import
|
|
376495
|
+
import Spinner6 from "ink-spinner";
|
|
378069
376496
|
|
|
378070
376497
|
// src/lib/update.ts
|
|
378071
|
-
import * as
|
|
378072
|
-
import * as
|
|
376498
|
+
import * as fs26 from "fs";
|
|
376499
|
+
import * as path24 from "path";
|
|
378073
376500
|
var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
|
|
378074
376501
|
function compareVersions(a, b) {
|
|
378075
376502
|
const partsA = a.split(".").map(Number);
|
|
@@ -378083,7 +376510,7 @@ function compareVersions(a, b) {
|
|
|
378083
376510
|
return 0;
|
|
378084
376511
|
}
|
|
378085
376512
|
async function checkForUpdate() {
|
|
378086
|
-
const currentVersion = "0.1.
|
|
376513
|
+
const currentVersion = "0.1.93";
|
|
378087
376514
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
378088
376515
|
if (!response.ok) {
|
|
378089
376516
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -378097,9 +376524,9 @@ function getCurrentBinaryPath() {
|
|
|
378097
376524
|
}
|
|
378098
376525
|
function isBinaryWritable() {
|
|
378099
376526
|
const binaryPath = getCurrentBinaryPath();
|
|
378100
|
-
const dir =
|
|
376527
|
+
const dir = path24.dirname(binaryPath);
|
|
378101
376528
|
try {
|
|
378102
|
-
|
|
376529
|
+
fs26.accessSync(dir, fs26.constants.W_OK);
|
|
378103
376530
|
return true;
|
|
378104
376531
|
} catch {
|
|
378105
376532
|
return false;
|
|
@@ -378107,24 +376534,24 @@ function isBinaryWritable() {
|
|
|
378107
376534
|
}
|
|
378108
376535
|
async function performUpdate(version, onProgress) {
|
|
378109
376536
|
const binaryPath = getCurrentBinaryPath();
|
|
378110
|
-
const binaryDir =
|
|
378111
|
-
const tempPath =
|
|
376537
|
+
const binaryDir = path24.dirname(binaryPath);
|
|
376538
|
+
const tempPath = path24.join(binaryDir, `.specific-update-${process.pid}`);
|
|
378112
376539
|
try {
|
|
378113
|
-
const { platform:
|
|
378114
|
-
const url = `${BINARIES_BASE_URL}/${version}/specific-${
|
|
376540
|
+
const { platform: platform3, arch: arch3 } = getPlatformInfo();
|
|
376541
|
+
const url = `${BINARIES_BASE_URL}/${version}/specific-${platform3}-${arch3}`;
|
|
378115
376542
|
await downloadFile(url, tempPath, onProgress);
|
|
378116
|
-
const stat4 =
|
|
376543
|
+
const stat4 = fs26.statSync(tempPath);
|
|
378117
376544
|
if (stat4.size === 0) {
|
|
378118
376545
|
throw new Error("Downloaded binary is empty");
|
|
378119
376546
|
}
|
|
378120
|
-
|
|
376547
|
+
fs26.chmodSync(tempPath, 493);
|
|
378121
376548
|
onProgress?.({ phase: "finalizing" });
|
|
378122
|
-
|
|
378123
|
-
|
|
376549
|
+
fs26.unlinkSync(binaryPath);
|
|
376550
|
+
fs26.renameSync(tempPath, binaryPath);
|
|
378124
376551
|
} catch (error) {
|
|
378125
376552
|
try {
|
|
378126
|
-
if (
|
|
378127
|
-
|
|
376553
|
+
if (fs26.existsSync(tempPath)) {
|
|
376554
|
+
fs26.unlinkSync(tempPath);
|
|
378128
376555
|
}
|
|
378129
376556
|
} catch {
|
|
378130
376557
|
}
|
|
@@ -378134,21 +376561,21 @@ async function performUpdate(version, onProgress) {
|
|
|
378134
376561
|
|
|
378135
376562
|
// src/lib/background-update.ts
|
|
378136
376563
|
import { spawn as spawn9 } from "child_process";
|
|
378137
|
-
import * as
|
|
378138
|
-
import * as
|
|
378139
|
-
import * as
|
|
378140
|
-
var SPECIFIC_DIR =
|
|
378141
|
-
var RATE_LIMIT_FILE =
|
|
378142
|
-
var LOCK_FILE =
|
|
376564
|
+
import * as fs27 from "fs";
|
|
376565
|
+
import * as path25 from "path";
|
|
376566
|
+
import * as os6 from "os";
|
|
376567
|
+
var SPECIFIC_DIR = path25.join(os6.homedir(), ".specific");
|
|
376568
|
+
var RATE_LIMIT_FILE = path25.join(SPECIFIC_DIR, "last-update-check");
|
|
376569
|
+
var LOCK_FILE = path25.join(SPECIFIC_DIR, "update.lock");
|
|
378143
376570
|
var RATE_LIMIT_MS = 60 * 60 * 1e3;
|
|
378144
376571
|
var STALE_LOCK_MS = 10 * 60 * 1e3;
|
|
378145
376572
|
function writeCheckTimestamp() {
|
|
378146
|
-
|
|
378147
|
-
|
|
376573
|
+
fs27.mkdirSync(SPECIFIC_DIR, { recursive: true });
|
|
376574
|
+
fs27.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
|
|
378148
376575
|
}
|
|
378149
376576
|
function isRateLimited() {
|
|
378150
376577
|
try {
|
|
378151
|
-
const content =
|
|
376578
|
+
const content = fs27.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
|
|
378152
376579
|
const lastCheck = parseInt(content, 10);
|
|
378153
376580
|
if (isNaN(lastCheck)) return false;
|
|
378154
376581
|
return Date.now() - lastCheck < RATE_LIMIT_MS;
|
|
@@ -378247,7 +376674,7 @@ function UpdateUI() {
|
|
|
378247
376674
|
}
|
|
378248
376675
|
}, [state.phase, exit]);
|
|
378249
376676
|
if (state.phase === "checking") {
|
|
378250
|
-
return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(
|
|
376677
|
+
return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " Checking for updates..."));
|
|
378251
376678
|
}
|
|
378252
376679
|
if (state.phase === "up-to-date") {
|
|
378253
376680
|
return /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "Already up to date (v", state.checkResult.currentVersion, ")");
|
|
@@ -378260,7 +376687,7 @@ function UpdateUI() {
|
|
|
378260
376687
|
const { currentVersion, latestVersion } = state.checkResult;
|
|
378261
376688
|
const { progress } = state;
|
|
378262
376689
|
const progressText = progress?.percent ? ` (${progress.percent}%)` : "";
|
|
378263
|
-
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Updating: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(
|
|
376690
|
+
return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Updating: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " ", progress?.phase === "finalizing" ? "Installing..." : `Downloading${progressText}`)));
|
|
378264
376691
|
}
|
|
378265
376692
|
if (state.phase === "success") {
|
|
378266
376693
|
const { currentVersion, latestVersion } = state.checkResult;
|
|
@@ -378282,7 +376709,7 @@ function updateCommand() {
|
|
|
378282
376709
|
var program = new Command();
|
|
378283
376710
|
var env = "production";
|
|
378284
376711
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
378285
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
376712
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.93").enablePositionalOptions();
|
|
378286
376713
|
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));
|
|
378287
376714
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
378288
376715
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|