@specific.dev/cli 0.1.107 → 0.1.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/fullscreen/__next._full.txt +1 -1
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +1 -1
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +1 -1
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
- package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/mail/__next._full.txt +1 -1
- package/dist/admin/mail/__next._head.txt +1 -1
- package/dist/admin/mail/__next._index.txt +1 -1
- package/dist/admin/mail/__next._tree.txt +1 -1
- package/dist/admin/mail/index.html +1 -1
- package/dist/admin/mail/index.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
- package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
- package/dist/admin/workflows/__next._full.txt +1 -1
- package/dist/admin/workflows/__next._head.txt +1 -1
- package/dist/admin/workflows/__next._index.txt +1 -1
- package/dist/admin/workflows/__next._tree.txt +1 -1
- package/dist/admin/workflows/index.html +1 -1
- package/dist/admin/workflows/index.txt +1 -1
- package/dist/cli.js +151 -35
- package/package.json +5 -2
- /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -602,12 +602,12 @@ var init_is_wsl = __esm({
|
|
|
602
602
|
// node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
|
|
603
603
|
import process3 from "node:process";
|
|
604
604
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
605
|
-
import { promisify } from "node:util";
|
|
605
|
+
import { promisify as promisify2 } from "node:util";
|
|
606
606
|
import childProcess from "node:child_process";
|
|
607
|
-
var
|
|
607
|
+
var execFile2, powerShellPath, executePowerShell;
|
|
608
608
|
var init_powershell_utils = __esm({
|
|
609
609
|
"node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js"() {
|
|
610
|
-
|
|
610
|
+
execFile2 = promisify2(childProcess.execFile);
|
|
611
611
|
powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
|
612
612
|
executePowerShell = async (command, options2 = {}) => {
|
|
613
613
|
const {
|
|
@@ -615,7 +615,7 @@ var init_powershell_utils = __esm({
|
|
|
615
615
|
...execFileOptions
|
|
616
616
|
} = options2;
|
|
617
617
|
const encodedCommand = executePowerShell.encodeCommand(command);
|
|
618
|
-
return
|
|
618
|
+
return execFile2(
|
|
619
619
|
psPath ?? powerShellPath(),
|
|
620
620
|
[
|
|
621
621
|
...executePowerShell.argumentsPrefix,
|
|
@@ -658,17 +658,17 @@ var init_utilities = __esm({
|
|
|
658
658
|
});
|
|
659
659
|
|
|
660
660
|
// node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
|
|
661
|
-
import { promisify as
|
|
661
|
+
import { promisify as promisify3 } from "node:util";
|
|
662
662
|
import childProcess2 from "node:child_process";
|
|
663
663
|
import fs17, { constants as fsConstants } from "node:fs/promises";
|
|
664
|
-
var
|
|
664
|
+
var execFile3, 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"() {
|
|
667
667
|
init_is_wsl();
|
|
668
668
|
init_powershell_utils();
|
|
669
669
|
init_utilities();
|
|
670
670
|
init_is_wsl();
|
|
671
|
-
|
|
671
|
+
execFile3 = promisify3(childProcess2.execFile);
|
|
672
672
|
wslDrivesMountPoint = /* @__PURE__ */ (() => {
|
|
673
673
|
const defaultMountPoint = "/mnt/";
|
|
674
674
|
let mountPoint;
|
|
@@ -724,7 +724,7 @@ var init_wsl_utils = __esm({
|
|
|
724
724
|
return path27;
|
|
725
725
|
}
|
|
726
726
|
try {
|
|
727
|
-
const { stdout } = await
|
|
727
|
+
const { stdout } = await execFile3("wslpath", ["-aw", path27], { encoding: "utf8" });
|
|
728
728
|
return stdout.trim();
|
|
729
729
|
} catch {
|
|
730
730
|
return path27;
|
|
@@ -756,14 +756,14 @@ var init_define_lazy_prop = __esm({
|
|
|
756
756
|
});
|
|
757
757
|
|
|
758
758
|
// node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js
|
|
759
|
-
import { promisify as
|
|
759
|
+
import { promisify as promisify4 } from "node:util";
|
|
760
760
|
import process4 from "node:process";
|
|
761
|
-
import { execFile as
|
|
761
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
762
762
|
async function defaultBrowserId() {
|
|
763
763
|
if (process4.platform !== "darwin") {
|
|
764
764
|
throw new Error("macOS only");
|
|
765
765
|
}
|
|
766
|
-
const { stdout } = await
|
|
766
|
+
const { stdout } = await execFileAsync2("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
|
|
767
767
|
const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
|
|
768
768
|
const browserId = match?.groups.id ?? "com.apple.Safari";
|
|
769
769
|
if (browserId === "com.apple.safari") {
|
|
@@ -771,17 +771,17 @@ async function defaultBrowserId() {
|
|
|
771
771
|
}
|
|
772
772
|
return browserId;
|
|
773
773
|
}
|
|
774
|
-
var
|
|
774
|
+
var execFileAsync2;
|
|
775
775
|
var init_default_browser_id = __esm({
|
|
776
776
|
"node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js"() {
|
|
777
|
-
|
|
777
|
+
execFileAsync2 = promisify4(execFile4);
|
|
778
778
|
}
|
|
779
779
|
});
|
|
780
780
|
|
|
781
781
|
// node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js
|
|
782
782
|
import process5 from "node:process";
|
|
783
|
-
import { promisify as
|
|
784
|
-
import { execFile as
|
|
783
|
+
import { promisify as promisify5 } from "node:util";
|
|
784
|
+
import { execFile as execFile5, execFileSync } from "node:child_process";
|
|
785
785
|
async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
|
|
786
786
|
if (process5.platform !== "darwin") {
|
|
787
787
|
throw new Error("macOS only");
|
|
@@ -791,13 +791,13 @@ async function runAppleScript(script, { humanReadableOutput = true, signal } = {
|
|
|
791
791
|
if (signal) {
|
|
792
792
|
execOptions.signal = signal;
|
|
793
793
|
}
|
|
794
|
-
const { stdout } = await
|
|
794
|
+
const { stdout } = await execFileAsync3("osascript", ["-e", script, outputArguments], execOptions);
|
|
795
795
|
return stdout.trim();
|
|
796
796
|
}
|
|
797
|
-
var
|
|
797
|
+
var execFileAsync3;
|
|
798
798
|
var init_run_applescript = __esm({
|
|
799
799
|
"node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js"() {
|
|
800
|
-
|
|
800
|
+
execFileAsync3 = promisify5(execFile5);
|
|
801
801
|
}
|
|
802
802
|
});
|
|
803
803
|
|
|
@@ -813,9 +813,9 @@ var init_bundle_name = __esm({
|
|
|
813
813
|
});
|
|
814
814
|
|
|
815
815
|
// node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js
|
|
816
|
-
import { promisify as
|
|
817
|
-
import { execFile as
|
|
818
|
-
async function defaultBrowser(_execFileAsync =
|
|
816
|
+
import { promisify as promisify6 } from "node:util";
|
|
817
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
818
|
+
async function defaultBrowser(_execFileAsync = execFileAsync4) {
|
|
819
819
|
const { stdout } = await _execFileAsync("reg", [
|
|
820
820
|
"QUERY",
|
|
821
821
|
" HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
|
|
@@ -833,10 +833,10 @@ async function defaultBrowser(_execFileAsync = execFileAsync3) {
|
|
|
833
833
|
}
|
|
834
834
|
return browser;
|
|
835
835
|
}
|
|
836
|
-
var
|
|
836
|
+
var execFileAsync4, windowsBrowserProgIds, _windowsBrowserProgIdMap, UnknownBrowserError;
|
|
837
837
|
var init_windows = __esm({
|
|
838
838
|
"node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js"() {
|
|
839
|
-
|
|
839
|
+
execFileAsync4 = promisify6(execFile6);
|
|
840
840
|
windowsBrowserProgIds = {
|
|
841
841
|
MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
|
|
842
842
|
// The missing `L` is correct.
|
|
@@ -863,9 +863,9 @@ var init_windows = __esm({
|
|
|
863
863
|
});
|
|
864
864
|
|
|
865
865
|
// node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js
|
|
866
|
-
import { promisify as
|
|
866
|
+
import { promisify as promisify7 } from "node:util";
|
|
867
867
|
import process6 from "node:process";
|
|
868
|
-
import { execFile as
|
|
868
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
869
869
|
async function defaultBrowser2() {
|
|
870
870
|
if (process6.platform === "darwin") {
|
|
871
871
|
const id = await defaultBrowserId();
|
|
@@ -873,7 +873,7 @@ async function defaultBrowser2() {
|
|
|
873
873
|
return { name, id };
|
|
874
874
|
}
|
|
875
875
|
if (process6.platform === "linux") {
|
|
876
|
-
const { stdout } = await
|
|
876
|
+
const { stdout } = await execFileAsync5("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
|
|
877
877
|
const id = stdout.trim();
|
|
878
878
|
const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
|
|
879
879
|
return { name, id };
|
|
@@ -883,14 +883,14 @@ async function defaultBrowser2() {
|
|
|
883
883
|
}
|
|
884
884
|
throw new Error("Only macOS, Linux, and Windows are supported");
|
|
885
885
|
}
|
|
886
|
-
var
|
|
886
|
+
var execFileAsync5, titleize;
|
|
887
887
|
var init_default_browser = __esm({
|
|
888
888
|
"node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js"() {
|
|
889
889
|
init_default_browser_id();
|
|
890
890
|
init_bundle_name();
|
|
891
891
|
init_windows();
|
|
892
892
|
init_windows();
|
|
893
|
-
|
|
893
|
+
execFileAsync5 = promisify7(execFile7);
|
|
894
894
|
titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
|
|
895
895
|
}
|
|
896
896
|
});
|
|
@@ -185191,6 +185191,8 @@ import { readFile, writeFile } from "fs/promises";
|
|
|
185191
185191
|
import { existsSync as existsSync5 } from "fs";
|
|
185192
185192
|
import * as fs6 from "fs";
|
|
185193
185193
|
import * as path6 from "path";
|
|
185194
|
+
import { execFile } from "child_process";
|
|
185195
|
+
import { promisify } from "util";
|
|
185194
185196
|
import * as http from "http";
|
|
185195
185197
|
import * as fs7 from "fs";
|
|
185196
185198
|
import * as path7 from "path";
|
|
@@ -369253,7 +369255,18 @@ async function startPostgres(pg, port, dataDir, onProgress) {
|
|
|
369253
369255
|
}
|
|
369254
369256
|
);
|
|
369255
369257
|
pipeProcess("postgres", postgres);
|
|
369256
|
-
await
|
|
369258
|
+
await Promise.race([
|
|
369259
|
+
waitForTcpPort(host, port),
|
|
369260
|
+
new Promise((_, reject) => {
|
|
369261
|
+
postgres.once("exit", (code, signal) => {
|
|
369262
|
+
reject(
|
|
369263
|
+
new Error(
|
|
369264
|
+
`PostgreSQL "${pg.name}" exited during startup (code ${code}, signal ${signal}). Port ${port} may already be in use by another process.`
|
|
369265
|
+
)
|
|
369266
|
+
);
|
|
369267
|
+
});
|
|
369268
|
+
})
|
|
369269
|
+
]);
|
|
369257
369270
|
return {
|
|
369258
369271
|
name: pg.name,
|
|
369259
369272
|
type: "postgres",
|
|
@@ -369936,6 +369949,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
369936
369949
|
}
|
|
369937
369950
|
};
|
|
369938
369951
|
}
|
|
369952
|
+
var execFileAsync = promisify(execFile);
|
|
369939
369953
|
var InstanceStateManager = class {
|
|
369940
369954
|
stateDir;
|
|
369941
369955
|
statePath;
|
|
@@ -370036,6 +370050,7 @@ var InstanceStateManager = class {
|
|
|
370036
370050
|
const content = fs6.readFileSync(this.statePath, "utf-8");
|
|
370037
370051
|
const state = JSON.parse(content);
|
|
370038
370052
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
370053
|
+
await this.killOrphanedProcesses(state);
|
|
370039
370054
|
fs6.unlinkSync(this.statePath);
|
|
370040
370055
|
return true;
|
|
370041
370056
|
}
|
|
@@ -370051,6 +370066,35 @@ var InstanceStateManager = class {
|
|
|
370051
370066
|
releaseLock();
|
|
370052
370067
|
}
|
|
370053
370068
|
}
|
|
370069
|
+
/**
|
|
370070
|
+
* Kill orphaned child processes left behind by a dead owner.
|
|
370071
|
+
* Best-effort: failures are silently ignored.
|
|
370072
|
+
*/
|
|
370073
|
+
async killOrphanedProcesses(state) {
|
|
370074
|
+
for (const service of Object.values(state.services)) {
|
|
370075
|
+
if (service.pid && this.isProcessRunning(service.pid)) {
|
|
370076
|
+
try {
|
|
370077
|
+
process.kill(-service.pid, "SIGKILL");
|
|
370078
|
+
} catch {
|
|
370079
|
+
}
|
|
370080
|
+
}
|
|
370081
|
+
}
|
|
370082
|
+
for (const db of Object.values(state.databases)) {
|
|
370083
|
+
if (db.syncUrl) {
|
|
370084
|
+
try {
|
|
370085
|
+
const url = new URL(db.syncUrl);
|
|
370086
|
+
const port = parseInt(url.port, 10);
|
|
370087
|
+
if (!isNaN(port)) {
|
|
370088
|
+
const pid = await findPidOnPort(port);
|
|
370089
|
+
if (pid !== void 0) {
|
|
370090
|
+
process.kill(pid, "SIGKILL");
|
|
370091
|
+
}
|
|
370092
|
+
}
|
|
370093
|
+
} catch {
|
|
370094
|
+
}
|
|
370095
|
+
}
|
|
370096
|
+
}
|
|
370097
|
+
}
|
|
370054
370098
|
async claimOwnership(command) {
|
|
370055
370099
|
const releaseLock = await this.acquireLock();
|
|
370056
370100
|
try {
|
|
@@ -370143,6 +370187,19 @@ var InstanceStateManager = class {
|
|
|
370143
370187
|
fs6.renameSync(tmpPath, this.statePath);
|
|
370144
370188
|
}
|
|
370145
370189
|
};
|
|
370190
|
+
async function findPidOnPort(port) {
|
|
370191
|
+
try {
|
|
370192
|
+
const { stdout } = await execFileAsync(
|
|
370193
|
+
"lsof",
|
|
370194
|
+
["-i", `:${port}`, "-t", "-sTCP:LISTEN"],
|
|
370195
|
+
{ timeout: 5e3 }
|
|
370196
|
+
);
|
|
370197
|
+
const pid = parseInt(stdout.trim().split("\n")[0], 10);
|
|
370198
|
+
return isNaN(pid) ? void 0 : pid;
|
|
370199
|
+
} catch {
|
|
370200
|
+
return void 0;
|
|
370201
|
+
}
|
|
370202
|
+
}
|
|
370146
370203
|
var __dirname = path7.dirname(fileURLToPath(import.meta.url));
|
|
370147
370204
|
var adminDir = path7.join(__dirname, "admin");
|
|
370148
370205
|
var _embeddedAdmin = null;
|
|
@@ -370399,6 +370456,22 @@ async function startElectric(postgres, port, dataDir, options2) {
|
|
|
370399
370456
|
);
|
|
370400
370457
|
const secret = generateRandomString(32);
|
|
370401
370458
|
const host = "127.0.0.1";
|
|
370459
|
+
if (await checkTcpPort2(host, port)) {
|
|
370460
|
+
let freed = false;
|
|
370461
|
+
for (let i = 0; i < 30; i++) {
|
|
370462
|
+
await sleep2(100);
|
|
370463
|
+
if (!await checkTcpPort2(host, port)) {
|
|
370464
|
+
freed = true;
|
|
370465
|
+
break;
|
|
370466
|
+
}
|
|
370467
|
+
}
|
|
370468
|
+
if (!freed) {
|
|
370469
|
+
throw new Error(
|
|
370470
|
+
`Electric port ${port} is already in use. This may be an orphaned process from a previous session \u2014 find it with \`lsof -i :${port}\` and kill it, then retry.`
|
|
370471
|
+
);
|
|
370472
|
+
}
|
|
370473
|
+
writeLog("electric", `Port ${port} was occupied but is now free`);
|
|
370474
|
+
}
|
|
370402
370475
|
const storageDir = path9.join(process.cwd(), dataDir, `electric-${postgres.name}`);
|
|
370403
370476
|
fs8.rmSync(storageDir, { recursive: true, force: true });
|
|
370404
370477
|
writeLog("electric", `Starting Electric for database "${postgres.name}"`);
|
|
@@ -370423,6 +370496,7 @@ async function startElectric(postgres, port, dataDir, options2) {
|
|
|
370423
370496
|
await waitForTcpPort2(host, port);
|
|
370424
370497
|
return {
|
|
370425
370498
|
databaseName: postgres.name,
|
|
370499
|
+
pid: electric.pid,
|
|
370426
370500
|
port,
|
|
370427
370501
|
url: `http://${host}:${port}`,
|
|
370428
370502
|
secret,
|
|
@@ -371370,7 +371444,9 @@ var BlockPortAllocator = class _BlockPortAllocator {
|
|
|
371370
371444
|
static async create() {
|
|
371371
371445
|
for (let i = 0; i < MAX_BLOCKS; i++) {
|
|
371372
371446
|
const base = FIRST_BLOCK_START + i * BLOCK_SIZE;
|
|
371373
|
-
|
|
371447
|
+
const ports = Array.from({ length: BLOCK_SIZE }, (_, j) => base + j);
|
|
371448
|
+
const results = await Promise.all(ports.map(isPortAvailable));
|
|
371449
|
+
if (results.every(Boolean)) {
|
|
371374
371450
|
return new _BlockPortAllocator(base);
|
|
371375
371451
|
}
|
|
371376
371452
|
}
|
|
@@ -371720,6 +371796,9 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
371720
371796
|
collectedSecrets = {};
|
|
371721
371797
|
collectedConfigs = {};
|
|
371722
371798
|
inputResolve = null;
|
|
371799
|
+
// Last-resort exit handler to kill child processes when the process exits
|
|
371800
|
+
// unexpectedly (e.g. SIGKILL from an agent). Runs synchronously.
|
|
371801
|
+
exitHandler = null;
|
|
371723
371802
|
constructor(options2) {
|
|
371724
371803
|
super();
|
|
371725
371804
|
this.projectDir = options2?.projectDir ?? process.cwd();
|
|
@@ -371922,6 +372001,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
371922
372001
|
if (this.stateManager) {
|
|
371923
372002
|
await this.stateManager.releaseOwnership();
|
|
371924
372003
|
}
|
|
372004
|
+
this.removeExitHandler();
|
|
371925
372005
|
this.systemLog("Shutdown complete");
|
|
371926
372006
|
closeDebugLog();
|
|
371927
372007
|
this.setStatus("idle");
|
|
@@ -371931,6 +372011,41 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
371931
372011
|
this.startResolve = null;
|
|
371932
372012
|
}
|
|
371933
372013
|
}
|
|
372014
|
+
// ── Private: exit handler ─────────────────────────────────────────────────
|
|
372015
|
+
/**
|
|
372016
|
+
* Register a synchronous process 'exit' handler that kills all known child
|
|
372017
|
+
* PIDs. This is a last resort for when the process exits without going
|
|
372018
|
+
* through shutdownInternal (e.g. parent sends SIGKILL).
|
|
372019
|
+
*/
|
|
372020
|
+
registerExitHandler() {
|
|
372021
|
+
this.removeExitHandler();
|
|
372022
|
+
this.exitHandler = () => {
|
|
372023
|
+
for (const electric of this.electricInstances) {
|
|
372024
|
+
if (electric.pid) {
|
|
372025
|
+
try {
|
|
372026
|
+
process.kill(electric.pid, "SIGKILL");
|
|
372027
|
+
} catch {
|
|
372028
|
+
}
|
|
372029
|
+
}
|
|
372030
|
+
}
|
|
372031
|
+
for (const service of this.services) {
|
|
372032
|
+
const pid = service.process.pid;
|
|
372033
|
+
if (pid) {
|
|
372034
|
+
try {
|
|
372035
|
+
process.kill(-pid, "SIGKILL");
|
|
372036
|
+
} catch {
|
|
372037
|
+
}
|
|
372038
|
+
}
|
|
372039
|
+
}
|
|
372040
|
+
};
|
|
372041
|
+
process.on("exit", this.exitHandler);
|
|
372042
|
+
}
|
|
372043
|
+
removeExitHandler() {
|
|
372044
|
+
if (this.exitHandler) {
|
|
372045
|
+
process.removeListener("exit", this.exitHandler);
|
|
372046
|
+
this.exitHandler = null;
|
|
372047
|
+
}
|
|
372048
|
+
}
|
|
371934
372049
|
// ── Private: config file watcher ───────────────────────────────────────────
|
|
371935
372050
|
startConfigWatcher() {
|
|
371936
372051
|
const configPath = path14.join(this.projectDir, "specific.hcl");
|
|
@@ -372108,6 +372223,7 @@ var DevEnvironment = class extends TypedEventEmitter {
|
|
|
372108
372223
|
if (result.cancelled) return;
|
|
372109
372224
|
resources = result.resources;
|
|
372110
372225
|
this.mailServers = result.mail;
|
|
372226
|
+
this.registerExitHandler();
|
|
372111
372227
|
} catch (err) {
|
|
372112
372228
|
const errorMsg = `Failed to start resources: ${err instanceof Error ? err.message : String(err)}`;
|
|
372113
372229
|
writeLog("system:error", errorMsg);
|
|
@@ -373084,7 +373200,7 @@ function trackEvent(event, properties) {
|
|
|
373084
373200
|
event,
|
|
373085
373201
|
properties: {
|
|
373086
373202
|
...properties,
|
|
373087
|
-
cli_version: "0.1.
|
|
373203
|
+
cli_version: "0.1.109",
|
|
373088
373204
|
platform: process.platform,
|
|
373089
373205
|
node_version: process.version,
|
|
373090
373206
|
project_id: getProjectId()
|
|
@@ -373521,7 +373637,7 @@ import { render as render3, Text as Text3, Box as Box3 } from "ink";
|
|
|
373521
373637
|
import Spinner2 from "ink-spinner";
|
|
373522
373638
|
import * as fs21 from "fs";
|
|
373523
373639
|
import * as path19 from "path";
|
|
373524
|
-
import { execFile as
|
|
373640
|
+
import { execFile as execFile8 } from "child_process";
|
|
373525
373641
|
|
|
373526
373642
|
// node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
|
|
373527
373643
|
var import_hcl2_json_parser3 = __toESM(require_dist2(), 1);
|
|
@@ -374175,7 +374291,7 @@ async function runReshapeCheck(migrationsDir) {
|
|
|
374175
374291
|
const binary = await ensureBinary(reshapeBinary);
|
|
374176
374292
|
const reshapePath = binary.executables["reshape"];
|
|
374177
374293
|
return new Promise((resolve9) => {
|
|
374178
|
-
|
|
374294
|
+
execFile8(reshapePath, ["check", "--dirs", migrationsDir], (err, _stdout, stderr) => {
|
|
374179
374295
|
if (err) {
|
|
374180
374296
|
const errorMsg = stderr.trim() || err.message;
|
|
374181
374297
|
resolve9({ success: false, error: errorMsg });
|
|
@@ -376763,7 +376879,7 @@ function compareVersions(a, b) {
|
|
|
376763
376879
|
return 0;
|
|
376764
376880
|
}
|
|
376765
376881
|
async function checkForUpdate() {
|
|
376766
|
-
const currentVersion = "0.1.
|
|
376882
|
+
const currentVersion = "0.1.109";
|
|
376767
376883
|
const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
|
|
376768
376884
|
if (!response.ok) {
|
|
376769
376885
|
throw new Error(`Failed to check for updates: HTTP ${response.status}`);
|
|
@@ -377031,7 +377147,7 @@ async function projectListCommand() {
|
|
|
377031
377147
|
var program = new Command();
|
|
377032
377148
|
var env = "production";
|
|
377033
377149
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
377034
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
377150
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.109").enablePositionalOptions();
|
|
377035
377151
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
|
|
377036
377152
|
Examples:
|
|
377037
377153
|
$ specific init
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specific.dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.109",
|
|
4
4
|
"description": "CLI for Specific infrastructure-as-code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"link:dev": "npm run build:dev && npm link",
|
|
18
18
|
"link:staging": "npm run build:staging && npm link",
|
|
19
19
|
"link:prod": "npm run build && npm link",
|
|
20
|
-
"build:binary": "npx tsx scripts/build-binary.ts"
|
|
20
|
+
"build:binary": "npx tsx scripts/build-binary.ts",
|
|
21
|
+
"link:binary:dev": "NODE_ENV=development npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific",
|
|
22
|
+
"link:binary:staging": "NODE_ENV=staging npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific",
|
|
23
|
+
"link:binary:prod": "npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific"
|
|
21
24
|
},
|
|
22
25
|
"keywords": [
|
|
23
26
|
"infrastructure",
|
/package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_ssgManifest.js
RENAMED
|
File without changes
|