@specific.dev/cli 0.1.37 → 0.1.38
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.__PAGE__.txt +2 -2
- package/dist/admin/__next._full.txt +3 -3
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +2 -2
- package/dist/admin/__next._tree.txt +2 -2
- package/dist/admin/_next/static/chunks/{de6af6d8adf8b50a.js → 0476153b08658d87.js} +2 -2
- package/dist/admin/_next/static/chunks/{4ab079bdcb131778.js → 1de437acc5206b1f.js} +2 -2
- package/dist/admin/_next/static/chunks/5ff94899b8b7a03a.css +3 -0
- package/dist/admin/_next/static/chunks/{895a6f91f0b479fb.js → 6aae09c9429b9f24.js} +2 -2
- package/dist/admin/_next/static/chunks/b71388016463cab2.js +1 -0
- package/dist/admin/_not-found/__next._full.txt +2 -2
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +2 -2
- 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 +2 -2
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +2 -2
- package/dist/admin/databases/__next._full.txt +3 -3
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +2 -2
- package/dist/admin/databases/__next._tree.txt +2 -2
- package/dist/admin/databases/__next.databases.__PAGE__.txt +2 -2
- package/dist/admin/databases/__next.databases.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +3 -3
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +3 -3
- package/dist/cli.js +1182 -496
- package/package.json +1 -1
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_buildManifest.js +0 -11
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/1o2O2QPOKhuZyxcNaCjq8/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_buildManifest.js +0 -11
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/2ZtqtFX2EkuM82mTM7NWT/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_buildManifest.js +0 -11
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/2vLF5bo4ZtUfhs8bdQBoF/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_buildManifest.js +0 -11
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/8_8s51m7RbLVvbIechG-b/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_buildManifest.js +0 -11
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/C4Y44KtLnP684m57gN3Ga/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_buildManifest.js +0 -11
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/DBeh36kAJnZBeBMJSPBW8/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_buildManifest.js +0 -11
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/REKEUu6DP2t99jKSAqNOu/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_buildManifest.js +0 -11
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/TDGT3mkKtTe0w_CxujLxI/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_buildManifest.js +0 -11
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/ZHMJ-g7mAlAMt_2uCXeEk/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_buildManifest.js +0 -11
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/a6JDAB-CdvWPvS4sBgJji/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_buildManifest.js +0 -11
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/abFUeBpymhlx-IxygVnM9/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/chunks/1584f10ea1cebcb2.js +0 -4
- package/dist/admin/_next/static/chunks/195bbec70cfcd241.js +0 -2
- package/dist/admin/_next/static/chunks/2583656ea9ac4ad6.js +0 -5
- package/dist/admin/_next/static/chunks/605800ff25160d05.js +0 -1
- package/dist/admin/_next/static/chunks/656b870f0567ed5f.js +0 -1
- package/dist/admin/_next/static/chunks/71098a6cd6181738.css +0 -3
- package/dist/admin/_next/static/chunks/9032f4a1aac1ca5d.css +0 -3
- package/dist/admin/_next/static/chunks/a28af2dc6f5fbaad.js +0 -1
- package/dist/admin/_next/static/chunks/b4205fc2f84bda68.css +0 -3
- package/dist/admin/_next/static/chunks/c1a750c25bc8d092.js +0 -1
- package/dist/admin/_next/static/chunks/c3d30f6f144dca51.js +0 -2
- package/dist/admin/_next/static/chunks/cbf55ce8731457ae.js +0 -2
- package/dist/admin/_next/static/chunks/f0c001244d275aab.js +0 -5
- package/dist/admin/_next/static/chunks/fde89fd76ad6a3d0.css +0 -3
- package/dist/admin/_next/static/chunks/turbopack-a3d691c83d4b1778.js +0 -4
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_buildManifest.js +0 -11
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/dcjrfD44GuB6g8bZ6BcFm/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_buildManifest.js +0 -11
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/fTEa8Scx922n-dMuqN3Vc/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_buildManifest.js +0 -11
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/kZNwyhHft01wPtJ_AvqQT/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_buildManifest.js +0 -11
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/kyVInC6N3DY6NWnjkLQ4J/_ssgManifest.js +0 -1
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_buildManifest.js +0 -11
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_clientMiddlewareManifest.json +0 -1
- package/dist/admin/_next/static/twedXBdhyxpjS68UQoNAc/_ssgManifest.js +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/check.d.ts +0 -2
- package/dist/commands/check.d.ts.map +0 -1
- package/dist/commands/check.js +0 -104
- package/dist/commands/check.js.map +0 -1
- package/dist/commands/clean.d.ts +0 -2
- package/dist/commands/clean.d.ts.map +0 -1
- package/dist/commands/clean.js +0 -70
- package/dist/commands/clean.js.map +0 -1
- package/dist/commands/deploy.d.ts +0 -2
- package/dist/commands/deploy.d.ts.map +0 -1
- package/dist/commands/deploy.js +0 -11
- package/dist/commands/deploy.js.map +0 -1
- package/dist/commands/dev.d.ts +0 -2
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -398
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/docs.d.ts +0 -2
- package/dist/commands/docs.d.ts.map +0 -1
- package/dist/commands/docs.js +0 -32
- package/dist/commands/docs.js.map +0 -1
- package/dist/commands/exec.d.ts +0 -2
- package/dist/commands/exec.d.ts.map +0 -1
- package/dist/commands/exec.js +0 -178
- package/dist/commands/exec.js.map +0 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -339
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/psql.d.ts +0 -2
- package/dist/commands/psql.d.ts.map +0 -1
- package/dist/commands/psql.js +0 -53
- package/dist/commands/psql.js.map +0 -1
- package/dist/commands/secrets.d.ts +0 -3
- package/dist/commands/secrets.d.ts.map +0 -1
- package/dist/commands/secrets.js +0 -136
- package/dist/commands/secrets.js.map +0 -1
- package/dist/lib/dev/database-manager.d.ts +0 -17
- package/dist/lib/dev/database-manager.d.ts.map +0 -1
- package/dist/lib/dev/database-manager.js +0 -114
- package/dist/lib/dev/database-manager.js.map +0 -1
- package/dist/lib/dev/env-resolver.d.ts +0 -14
- package/dist/lib/dev/env-resolver.d.ts.map +0 -1
- package/dist/lib/dev/env-resolver.js +0 -109
- package/dist/lib/dev/env-resolver.js.map +0 -1
- package/dist/lib/dev/http-proxy.d.ts +0 -11
- package/dist/lib/dev/http-proxy.d.ts.map +0 -1
- package/dist/lib/dev/http-proxy.js +0 -165
- package/dist/lib/dev/http-proxy.js.map +0 -1
- package/dist/lib/dev/index.d.ts +0 -11
- package/dist/lib/dev/index.d.ts.map +0 -1
- package/dist/lib/dev/index.js +0 -7
- package/dist/lib/dev/index.js.map +0 -1
- package/dist/lib/dev/instance-state.d.ts +0 -45
- package/dist/lib/dev/instance-state.d.ts.map +0 -1
- package/dist/lib/dev/instance-state.js +0 -213
- package/dist/lib/dev/instance-state.js.map +0 -1
- package/dist/lib/dev/port-allocator.d.ts +0 -5
- package/dist/lib/dev/port-allocator.d.ts.map +0 -1
- package/dist/lib/dev/port-allocator.js +0 -21
- package/dist/lib/dev/port-allocator.js.map +0 -1
- package/dist/lib/dev/service-runner.d.ts +0 -16
- package/dist/lib/dev/service-runner.d.ts.map +0 -1
- package/dist/lib/dev/service-runner.js +0 -61
- package/dist/lib/dev/service-runner.js.map +0 -1
- package/dist/lib/secrets/index.d.ts +0 -2
- package/dist/lib/secrets/index.d.ts.map +0 -1
- package/dist/lib/secrets/index.js +0 -2
- package/dist/lib/secrets/index.js.map +0 -1
- package/dist/lib/secrets/parser.d.ts +0 -37
- package/dist/lib/secrets/parser.d.ts.map +0 -1
- package/dist/lib/secrets/parser.js +0 -116
- package/dist/lib/secrets/parser.js.map +0 -1
- /package/dist/admin/_next/static/{0zkv3YeV6IWOWVWE1S1A1 → dtzWfchSIjRVrII5xoJl6}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{0zkv3YeV6IWOWVWE1S1A1 → dtzWfchSIjRVrII5xoJl6}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{0zkv3YeV6IWOWVWE1S1A1 → dtzWfchSIjRVrII5xoJl6}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -41,8 +41,8 @@ var require_dist = __commonJS({
|
|
|
41
41
|
var $global, $module, $NaN = NaN;
|
|
42
42
|
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");
|
|
43
43
|
if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
|
|
44
|
-
var
|
|
45
|
-
"object" == typeof
|
|
44
|
+
var fs26 = $global.require("fs");
|
|
45
|
+
"object" == typeof fs26 && null !== fs26 && 0 !== Object.keys(fs26).length && ($global.fs = fs26);
|
|
46
46
|
} catch (e) {
|
|
47
47
|
}
|
|
48
48
|
if (!$global.fs) {
|
|
@@ -182368,36 +182368,39 @@ function installCAToTrustStore(certPath) {
|
|
|
182368
182368
|
throw new Error(`Unsupported platform: ${platform5}`);
|
|
182369
182369
|
}
|
|
182370
182370
|
}
|
|
182371
|
-
function generateCertificate(domain) {
|
|
182371
|
+
function generateCertificate(domain, keys = []) {
|
|
182372
182372
|
const caDir = getCADir();
|
|
182373
182373
|
const caCertPem = fs.readFileSync(path.join(caDir, "ca.crt"), "utf-8");
|
|
182374
182374
|
const caKeyPem = fs.readFileSync(path.join(caDir, "ca.key"), "utf-8");
|
|
182375
182375
|
const caCert = forge.pki.certificateFromPem(caCertPem);
|
|
182376
182376
|
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
|
182377
|
-
const
|
|
182377
|
+
const certKeys = forge.pki.rsa.generateKeyPair(2048);
|
|
182378
182378
|
const cert = forge.pki.createCertificate();
|
|
182379
|
-
cert.publicKey =
|
|
182379
|
+
cert.publicKey = certKeys.publicKey;
|
|
182380
182380
|
cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16));
|
|
182381
182381
|
cert.validity.notBefore = /* @__PURE__ */ new Date();
|
|
182382
182382
|
cert.validity.notAfter = /* @__PURE__ */ new Date();
|
|
182383
182383
|
cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + 7);
|
|
182384
182384
|
cert.setSubject([{ name: "commonName", value: domain }]);
|
|
182385
182385
|
cert.setIssuer(caCert.subject.attributes);
|
|
182386
|
+
const altNames = [
|
|
182387
|
+
{ type: 2, value: domain },
|
|
182388
|
+
{ type: 2, value: `*.${domain}` }
|
|
182389
|
+
];
|
|
182390
|
+
for (const key of keys) {
|
|
182391
|
+
if (key !== "default") {
|
|
182392
|
+
altNames.push({ type: 2, value: `*.${key}.${domain}` });
|
|
182393
|
+
}
|
|
182394
|
+
}
|
|
182386
182395
|
cert.setExtensions([
|
|
182387
182396
|
{ name: "basicConstraints", cA: false },
|
|
182388
182397
|
{ name: "keyUsage", digitalSignature: true, keyEncipherment: true },
|
|
182389
182398
|
{ name: "extKeyUsage", serverAuth: true },
|
|
182390
|
-
{
|
|
182391
|
-
name: "subjectAltName",
|
|
182392
|
-
altNames: [
|
|
182393
|
-
{ type: 2, value: domain },
|
|
182394
|
-
{ type: 2, value: `*.${domain}` }
|
|
182395
|
-
]
|
|
182396
|
-
}
|
|
182399
|
+
{ name: "subjectAltName", altNames }
|
|
182397
182400
|
]);
|
|
182398
182401
|
cert.sign(caKey, forge.md.sha256.create());
|
|
182399
182402
|
return {
|
|
182400
|
-
key: Buffer.from(forge.pki.privateKeyToPem(
|
|
182403
|
+
key: Buffer.from(forge.pki.privateKeyToPem(certKeys.privateKey)),
|
|
182401
182404
|
cert: Buffer.from(forge.pki.certificateToPem(cert))
|
|
182402
182405
|
};
|
|
182403
182406
|
}
|
|
@@ -182677,11 +182680,11 @@ import { join as join3, dirname } from "path";
|
|
|
182677
182680
|
import { fileURLToPath } from "url";
|
|
182678
182681
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
182679
182682
|
var docsDir = join3(__dirname, "docs");
|
|
182680
|
-
function docsCommand(
|
|
182681
|
-
const docPath = resolveDocPath(
|
|
182683
|
+
function docsCommand(path22) {
|
|
182684
|
+
const docPath = resolveDocPath(path22);
|
|
182682
182685
|
if (!docPath) {
|
|
182683
182686
|
console.error(
|
|
182684
|
-
`Documentation not found: ${
|
|
182687
|
+
`Documentation not found: ${path22 || "index"}
|
|
182685
182688
|
|
|
182686
182689
|
Run 'specific docs' to see available topics.`
|
|
182687
182690
|
);
|
|
@@ -182690,16 +182693,16 @@ Run 'specific docs' to see available topics.`
|
|
|
182690
182693
|
const content = readFileSync3(docPath, "utf-8");
|
|
182691
182694
|
console.log(content);
|
|
182692
182695
|
}
|
|
182693
|
-
function resolveDocPath(
|
|
182694
|
-
if (!
|
|
182696
|
+
function resolveDocPath(path22) {
|
|
182697
|
+
if (!path22) {
|
|
182695
182698
|
const indexPath2 = join3(docsDir, "index.md");
|
|
182696
182699
|
return existsSync3(indexPath2) ? indexPath2 : null;
|
|
182697
182700
|
}
|
|
182698
|
-
const directPath = join3(docsDir, `${
|
|
182701
|
+
const directPath = join3(docsDir, `${path22}.md`);
|
|
182699
182702
|
if (existsSync3(directPath)) {
|
|
182700
182703
|
return directPath;
|
|
182701
182704
|
}
|
|
182702
|
-
const indexPath = join3(docsDir,
|
|
182705
|
+
const indexPath = join3(docsDir, path22, "index.md");
|
|
182703
182706
|
if (existsSync3(indexPath)) {
|
|
182704
182707
|
return indexPath;
|
|
182705
182708
|
}
|
|
@@ -183234,8 +183237,8 @@ function checkCommand() {
|
|
|
183234
183237
|
import React3, { useState as useState3, useEffect as useEffect3, useRef } from "react";
|
|
183235
183238
|
import { render as render3, Text as Text3, Box as Box3, useApp as useApp2, Static } from "ink";
|
|
183236
183239
|
import Spinner3 from "ink-spinner";
|
|
183237
|
-
import * as
|
|
183238
|
-
import * as
|
|
183240
|
+
import * as fs12 from "fs";
|
|
183241
|
+
import * as path13 from "path";
|
|
183239
183242
|
|
|
183240
183243
|
// node_modules/chokidar/index.js
|
|
183241
183244
|
import { EventEmitter } from "node:events";
|
|
@@ -183327,7 +183330,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
183327
183330
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
183328
183331
|
const statMethod = opts.lstat ? lstat : stat;
|
|
183329
183332
|
if (wantBigintFsStats) {
|
|
183330
|
-
this._stat = (
|
|
183333
|
+
this._stat = (path22) => statMethod(path22, { bigint: true });
|
|
183331
183334
|
} else {
|
|
183332
183335
|
this._stat = statMethod;
|
|
183333
183336
|
}
|
|
@@ -183352,8 +183355,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
183352
183355
|
const par = this.parent;
|
|
183353
183356
|
const fil = par && par.files;
|
|
183354
183357
|
if (fil && fil.length > 0) {
|
|
183355
|
-
const { path:
|
|
183356
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
183358
|
+
const { path: path22, depth } = par;
|
|
183359
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path22));
|
|
183357
183360
|
const awaited = await Promise.all(slice);
|
|
183358
183361
|
for (const entry of awaited) {
|
|
183359
183362
|
if (!entry)
|
|
@@ -183393,21 +183396,21 @@ var ReaddirpStream = class extends Readable {
|
|
|
183393
183396
|
this.reading = false;
|
|
183394
183397
|
}
|
|
183395
183398
|
}
|
|
183396
|
-
async _exploreDir(
|
|
183399
|
+
async _exploreDir(path22, depth) {
|
|
183397
183400
|
let files;
|
|
183398
183401
|
try {
|
|
183399
|
-
files = await readdir(
|
|
183402
|
+
files = await readdir(path22, this._rdOptions);
|
|
183400
183403
|
} catch (error) {
|
|
183401
183404
|
this._onError(error);
|
|
183402
183405
|
}
|
|
183403
|
-
return { files, depth, path:
|
|
183406
|
+
return { files, depth, path: path22 };
|
|
183404
183407
|
}
|
|
183405
|
-
async _formatEntry(dirent,
|
|
183408
|
+
async _formatEntry(dirent, path22) {
|
|
183406
183409
|
let entry;
|
|
183407
|
-
const
|
|
183410
|
+
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
183408
183411
|
try {
|
|
183409
|
-
const fullPath = presolve(pjoin(
|
|
183410
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
183412
|
+
const fullPath = presolve(pjoin(path22, basename5));
|
|
183413
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
183411
183414
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
183412
183415
|
} catch (err) {
|
|
183413
183416
|
this._onError(err);
|
|
@@ -183806,16 +183809,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
183806
183809
|
};
|
|
183807
183810
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
183808
183811
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
183809
|
-
function createFsWatchInstance(
|
|
183812
|
+
function createFsWatchInstance(path22, options2, listener, errHandler, emitRaw) {
|
|
183810
183813
|
const handleEvent = (rawEvent, evPath) => {
|
|
183811
|
-
listener(
|
|
183812
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
183813
|
-
if (evPath &&
|
|
183814
|
-
fsWatchBroadcast(sp.resolve(
|
|
183814
|
+
listener(path22);
|
|
183815
|
+
emitRaw(rawEvent, evPath, { watchedPath: path22 });
|
|
183816
|
+
if (evPath && path22 !== evPath) {
|
|
183817
|
+
fsWatchBroadcast(sp.resolve(path22, evPath), KEY_LISTENERS, sp.join(path22, evPath));
|
|
183815
183818
|
}
|
|
183816
183819
|
};
|
|
183817
183820
|
try {
|
|
183818
|
-
return fs_watch(
|
|
183821
|
+
return fs_watch(path22, {
|
|
183819
183822
|
persistent: options2.persistent
|
|
183820
183823
|
}, handleEvent);
|
|
183821
183824
|
} catch (error) {
|
|
@@ -183831,12 +183834,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
183831
183834
|
listener(val1, val2, val3);
|
|
183832
183835
|
});
|
|
183833
183836
|
};
|
|
183834
|
-
var setFsWatchListener = (
|
|
183837
|
+
var setFsWatchListener = (path22, fullPath, options2, handlers) => {
|
|
183835
183838
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
183836
183839
|
let cont = FsWatchInstances.get(fullPath);
|
|
183837
183840
|
let watcher;
|
|
183838
183841
|
if (!options2.persistent) {
|
|
183839
|
-
watcher = createFsWatchInstance(
|
|
183842
|
+
watcher = createFsWatchInstance(path22, options2, listener, errHandler, rawEmitter);
|
|
183840
183843
|
if (!watcher)
|
|
183841
183844
|
return;
|
|
183842
183845
|
return watcher.close.bind(watcher);
|
|
@@ -183847,7 +183850,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183847
183850
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
183848
183851
|
} else {
|
|
183849
183852
|
watcher = createFsWatchInstance(
|
|
183850
|
-
|
|
183853
|
+
path22,
|
|
183851
183854
|
options2,
|
|
183852
183855
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
183853
183856
|
errHandler,
|
|
@@ -183862,7 +183865,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183862
183865
|
cont.watcherUnusable = true;
|
|
183863
183866
|
if (isWindows && error.code === "EPERM") {
|
|
183864
183867
|
try {
|
|
183865
|
-
const fd = await open(
|
|
183868
|
+
const fd = await open(path22, "r");
|
|
183866
183869
|
await fd.close();
|
|
183867
183870
|
broadcastErr(error);
|
|
183868
183871
|
} catch (err) {
|
|
@@ -183893,7 +183896,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183893
183896
|
};
|
|
183894
183897
|
};
|
|
183895
183898
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
183896
|
-
var setFsWatchFileListener = (
|
|
183899
|
+
var setFsWatchFileListener = (path22, fullPath, options2, handlers) => {
|
|
183897
183900
|
const { listener, rawEmitter } = handlers;
|
|
183898
183901
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
183899
183902
|
const copts = cont && cont.options;
|
|
@@ -183915,7 +183918,7 @@ var setFsWatchFileListener = (path20, fullPath, options2, handlers) => {
|
|
|
183915
183918
|
});
|
|
183916
183919
|
const currmtime = curr.mtimeMs;
|
|
183917
183920
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
183918
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
183921
|
+
foreach(cont.listeners, (listener2) => listener2(path22, curr));
|
|
183919
183922
|
}
|
|
183920
183923
|
})
|
|
183921
183924
|
};
|
|
@@ -183945,13 +183948,13 @@ var NodeFsHandler = class {
|
|
|
183945
183948
|
* @param listener on fs change
|
|
183946
183949
|
* @returns closer for the watcher instance
|
|
183947
183950
|
*/
|
|
183948
|
-
_watchWithNodeFs(
|
|
183951
|
+
_watchWithNodeFs(path22, listener) {
|
|
183949
183952
|
const opts = this.fsw.options;
|
|
183950
|
-
const directory = sp.dirname(
|
|
183951
|
-
const
|
|
183953
|
+
const directory = sp.dirname(path22);
|
|
183954
|
+
const basename5 = sp.basename(path22);
|
|
183952
183955
|
const parent = this.fsw._getWatchedDir(directory);
|
|
183953
|
-
parent.add(
|
|
183954
|
-
const absolutePath = sp.resolve(
|
|
183956
|
+
parent.add(basename5);
|
|
183957
|
+
const absolutePath = sp.resolve(path22);
|
|
183955
183958
|
const options2 = {
|
|
183956
183959
|
persistent: opts.persistent
|
|
183957
183960
|
};
|
|
@@ -183960,13 +183963,13 @@ var NodeFsHandler = class {
|
|
|
183960
183963
|
let closer;
|
|
183961
183964
|
if (opts.usePolling) {
|
|
183962
183965
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
183963
|
-
options2.interval = enableBin && isBinaryPath(
|
|
183964
|
-
closer = setFsWatchFileListener(
|
|
183966
|
+
options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
183967
|
+
closer = setFsWatchFileListener(path22, absolutePath, options2, {
|
|
183965
183968
|
listener,
|
|
183966
183969
|
rawEmitter: this.fsw._emitRaw
|
|
183967
183970
|
});
|
|
183968
183971
|
} else {
|
|
183969
|
-
closer = setFsWatchListener(
|
|
183972
|
+
closer = setFsWatchListener(path22, absolutePath, options2, {
|
|
183970
183973
|
listener,
|
|
183971
183974
|
errHandler: this._boundHandleError,
|
|
183972
183975
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -183983,12 +183986,12 @@ var NodeFsHandler = class {
|
|
|
183983
183986
|
return;
|
|
183984
183987
|
}
|
|
183985
183988
|
const dirname8 = sp.dirname(file);
|
|
183986
|
-
const
|
|
183989
|
+
const basename5 = sp.basename(file);
|
|
183987
183990
|
const parent = this.fsw._getWatchedDir(dirname8);
|
|
183988
183991
|
let prevStats = stats;
|
|
183989
|
-
if (parent.has(
|
|
183992
|
+
if (parent.has(basename5))
|
|
183990
183993
|
return;
|
|
183991
|
-
const listener = async (
|
|
183994
|
+
const listener = async (path22, newStats) => {
|
|
183992
183995
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
183993
183996
|
return;
|
|
183994
183997
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -184002,18 +184005,18 @@ var NodeFsHandler = class {
|
|
|
184002
184005
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
184003
184006
|
}
|
|
184004
184007
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
184005
|
-
this.fsw._closeFile(
|
|
184008
|
+
this.fsw._closeFile(path22);
|
|
184006
184009
|
prevStats = newStats2;
|
|
184007
184010
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
184008
184011
|
if (closer2)
|
|
184009
|
-
this.fsw._addPathCloser(
|
|
184012
|
+
this.fsw._addPathCloser(path22, closer2);
|
|
184010
184013
|
} else {
|
|
184011
184014
|
prevStats = newStats2;
|
|
184012
184015
|
}
|
|
184013
184016
|
} catch (error) {
|
|
184014
|
-
this.fsw._remove(dirname8,
|
|
184017
|
+
this.fsw._remove(dirname8, basename5);
|
|
184015
184018
|
}
|
|
184016
|
-
} else if (parent.has(
|
|
184019
|
+
} else if (parent.has(basename5)) {
|
|
184017
184020
|
const at = newStats.atimeMs;
|
|
184018
184021
|
const mt = newStats.mtimeMs;
|
|
184019
184022
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -184038,7 +184041,7 @@ var NodeFsHandler = class {
|
|
|
184038
184041
|
* @param item basename of this item
|
|
184039
184042
|
* @returns true if no more processing is needed for this entry.
|
|
184040
184043
|
*/
|
|
184041
|
-
async _handleSymlink(entry, directory,
|
|
184044
|
+
async _handleSymlink(entry, directory, path22, item) {
|
|
184042
184045
|
if (this.fsw.closed) {
|
|
184043
184046
|
return;
|
|
184044
184047
|
}
|
|
@@ -184048,7 +184051,7 @@ var NodeFsHandler = class {
|
|
|
184048
184051
|
this.fsw._incrReadyCount();
|
|
184049
184052
|
let linkPath;
|
|
184050
184053
|
try {
|
|
184051
|
-
linkPath = await fsrealpath(
|
|
184054
|
+
linkPath = await fsrealpath(path22);
|
|
184052
184055
|
} catch (e) {
|
|
184053
184056
|
this.fsw._emitReady();
|
|
184054
184057
|
return true;
|
|
@@ -184058,12 +184061,12 @@ var NodeFsHandler = class {
|
|
|
184058
184061
|
if (dir.has(item)) {
|
|
184059
184062
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
184060
184063
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184061
|
-
this.fsw._emit(EV.CHANGE,
|
|
184064
|
+
this.fsw._emit(EV.CHANGE, path22, entry.stats);
|
|
184062
184065
|
}
|
|
184063
184066
|
} else {
|
|
184064
184067
|
dir.add(item);
|
|
184065
184068
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184066
|
-
this.fsw._emit(EV.ADD,
|
|
184069
|
+
this.fsw._emit(EV.ADD, path22, entry.stats);
|
|
184067
184070
|
}
|
|
184068
184071
|
this.fsw._emitReady();
|
|
184069
184072
|
return true;
|
|
@@ -184093,9 +184096,9 @@ var NodeFsHandler = class {
|
|
|
184093
184096
|
return;
|
|
184094
184097
|
}
|
|
184095
184098
|
const item = entry.path;
|
|
184096
|
-
let
|
|
184099
|
+
let path22 = sp.join(directory, item);
|
|
184097
184100
|
current.add(item);
|
|
184098
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
184101
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path22, item)) {
|
|
184099
184102
|
return;
|
|
184100
184103
|
}
|
|
184101
184104
|
if (this.fsw.closed) {
|
|
@@ -184104,11 +184107,11 @@ var NodeFsHandler = class {
|
|
|
184104
184107
|
}
|
|
184105
184108
|
if (item === target || !target && !previous.has(item)) {
|
|
184106
184109
|
this.fsw._incrReadyCount();
|
|
184107
|
-
|
|
184108
|
-
this._addToNodeFs(
|
|
184110
|
+
path22 = sp.join(dir, sp.relative(dir, path22));
|
|
184111
|
+
this._addToNodeFs(path22, initialAdd, wh, depth + 1);
|
|
184109
184112
|
}
|
|
184110
184113
|
}).on(EV.ERROR, this._boundHandleError);
|
|
184111
|
-
return new Promise((
|
|
184114
|
+
return new Promise((resolve5, reject) => {
|
|
184112
184115
|
if (!stream)
|
|
184113
184116
|
return reject();
|
|
184114
184117
|
stream.once(STR_END, () => {
|
|
@@ -184117,7 +184120,7 @@ var NodeFsHandler = class {
|
|
|
184117
184120
|
return;
|
|
184118
184121
|
}
|
|
184119
184122
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
184120
|
-
|
|
184123
|
+
resolve5(void 0);
|
|
184121
184124
|
previous.getChildren().filter((item) => {
|
|
184122
184125
|
return item !== directory && !current.has(item);
|
|
184123
184126
|
}).forEach((item) => {
|
|
@@ -184174,13 +184177,13 @@ var NodeFsHandler = class {
|
|
|
184174
184177
|
* @param depth Child path actually targeted for watch
|
|
184175
184178
|
* @param target Child path actually targeted for watch
|
|
184176
184179
|
*/
|
|
184177
|
-
async _addToNodeFs(
|
|
184180
|
+
async _addToNodeFs(path22, initialAdd, priorWh, depth, target) {
|
|
184178
184181
|
const ready = this.fsw._emitReady;
|
|
184179
|
-
if (this.fsw._isIgnored(
|
|
184182
|
+
if (this.fsw._isIgnored(path22) || this.fsw.closed) {
|
|
184180
184183
|
ready();
|
|
184181
184184
|
return false;
|
|
184182
184185
|
}
|
|
184183
|
-
const wh = this.fsw._getWatchHelpers(
|
|
184186
|
+
const wh = this.fsw._getWatchHelpers(path22);
|
|
184184
184187
|
if (priorWh) {
|
|
184185
184188
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
184186
184189
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -184196,8 +184199,8 @@ var NodeFsHandler = class {
|
|
|
184196
184199
|
const follow = this.fsw.options.followSymlinks;
|
|
184197
184200
|
let closer;
|
|
184198
184201
|
if (stats.isDirectory()) {
|
|
184199
|
-
const absPath = sp.resolve(
|
|
184200
|
-
const targetPath = follow ? await fsrealpath(
|
|
184202
|
+
const absPath = sp.resolve(path22);
|
|
184203
|
+
const targetPath = follow ? await fsrealpath(path22) : path22;
|
|
184201
184204
|
if (this.fsw.closed)
|
|
184202
184205
|
return;
|
|
184203
184206
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -184207,29 +184210,29 @@ var NodeFsHandler = class {
|
|
|
184207
184210
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
184208
184211
|
}
|
|
184209
184212
|
} else if (stats.isSymbolicLink()) {
|
|
184210
|
-
const targetPath = follow ? await fsrealpath(
|
|
184213
|
+
const targetPath = follow ? await fsrealpath(path22) : path22;
|
|
184211
184214
|
if (this.fsw.closed)
|
|
184212
184215
|
return;
|
|
184213
184216
|
const parent = sp.dirname(wh.watchPath);
|
|
184214
184217
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
184215
184218
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
184216
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
184219
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path22, wh, targetPath);
|
|
184217
184220
|
if (this.fsw.closed)
|
|
184218
184221
|
return;
|
|
184219
184222
|
if (targetPath !== void 0) {
|
|
184220
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
184223
|
+
this.fsw._symlinkPaths.set(sp.resolve(path22), targetPath);
|
|
184221
184224
|
}
|
|
184222
184225
|
} else {
|
|
184223
184226
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
184224
184227
|
}
|
|
184225
184228
|
ready();
|
|
184226
184229
|
if (closer)
|
|
184227
|
-
this.fsw._addPathCloser(
|
|
184230
|
+
this.fsw._addPathCloser(path22, closer);
|
|
184228
184231
|
return false;
|
|
184229
184232
|
} catch (error) {
|
|
184230
184233
|
if (this.fsw._handleError(error)) {
|
|
184231
184234
|
ready();
|
|
184232
|
-
return
|
|
184235
|
+
return path22;
|
|
184233
184236
|
}
|
|
184234
184237
|
}
|
|
184235
184238
|
}
|
|
@@ -184272,24 +184275,24 @@ function createPattern(matcher) {
|
|
|
184272
184275
|
}
|
|
184273
184276
|
return () => false;
|
|
184274
184277
|
}
|
|
184275
|
-
function normalizePath(
|
|
184276
|
-
if (typeof
|
|
184278
|
+
function normalizePath(path22) {
|
|
184279
|
+
if (typeof path22 !== "string")
|
|
184277
184280
|
throw new Error("string expected");
|
|
184278
|
-
|
|
184279
|
-
|
|
184281
|
+
path22 = sp2.normalize(path22);
|
|
184282
|
+
path22 = path22.replace(/\\/g, "/");
|
|
184280
184283
|
let prepend = false;
|
|
184281
|
-
if (
|
|
184284
|
+
if (path22.startsWith("//"))
|
|
184282
184285
|
prepend = true;
|
|
184283
|
-
|
|
184286
|
+
path22 = path22.replace(DOUBLE_SLASH_RE, "/");
|
|
184284
184287
|
if (prepend)
|
|
184285
|
-
|
|
184286
|
-
return
|
|
184288
|
+
path22 = "/" + path22;
|
|
184289
|
+
return path22;
|
|
184287
184290
|
}
|
|
184288
184291
|
function matchPatterns(patterns, testString, stats) {
|
|
184289
|
-
const
|
|
184292
|
+
const path22 = normalizePath(testString);
|
|
184290
184293
|
for (let index = 0; index < patterns.length; index++) {
|
|
184291
184294
|
const pattern = patterns[index];
|
|
184292
|
-
if (pattern(
|
|
184295
|
+
if (pattern(path22, stats)) {
|
|
184293
184296
|
return true;
|
|
184294
184297
|
}
|
|
184295
184298
|
}
|
|
@@ -184327,19 +184330,19 @@ var toUnix = (string) => {
|
|
|
184327
184330
|
}
|
|
184328
184331
|
return str;
|
|
184329
184332
|
};
|
|
184330
|
-
var normalizePathToUnix = (
|
|
184331
|
-
var normalizeIgnored = (cwd = "") => (
|
|
184332
|
-
if (typeof
|
|
184333
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
184333
|
+
var normalizePathToUnix = (path22) => toUnix(sp2.normalize(toUnix(path22)));
|
|
184334
|
+
var normalizeIgnored = (cwd = "") => (path22) => {
|
|
184335
|
+
if (typeof path22 === "string") {
|
|
184336
|
+
return normalizePathToUnix(sp2.isAbsolute(path22) ? path22 : sp2.join(cwd, path22));
|
|
184334
184337
|
} else {
|
|
184335
|
-
return
|
|
184338
|
+
return path22;
|
|
184336
184339
|
}
|
|
184337
184340
|
};
|
|
184338
|
-
var getAbsolutePath = (
|
|
184339
|
-
if (sp2.isAbsolute(
|
|
184340
|
-
return
|
|
184341
|
+
var getAbsolutePath = (path22, cwd) => {
|
|
184342
|
+
if (sp2.isAbsolute(path22)) {
|
|
184343
|
+
return path22;
|
|
184341
184344
|
}
|
|
184342
|
-
return sp2.join(cwd,
|
|
184345
|
+
return sp2.join(cwd, path22);
|
|
184343
184346
|
};
|
|
184344
184347
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
184345
184348
|
var DirEntry = class {
|
|
@@ -184404,10 +184407,10 @@ var WatchHelper = class {
|
|
|
184404
184407
|
dirParts;
|
|
184405
184408
|
followSymlinks;
|
|
184406
184409
|
statMethod;
|
|
184407
|
-
constructor(
|
|
184410
|
+
constructor(path22, follow, fsw) {
|
|
184408
184411
|
this.fsw = fsw;
|
|
184409
|
-
const watchPath =
|
|
184410
|
-
this.path =
|
|
184412
|
+
const watchPath = path22;
|
|
184413
|
+
this.path = path22 = path22.replace(REPLACER_RE, "");
|
|
184411
184414
|
this.watchPath = watchPath;
|
|
184412
184415
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
184413
184416
|
this.dirParts = [];
|
|
@@ -184547,20 +184550,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184547
184550
|
this._closePromise = void 0;
|
|
184548
184551
|
let paths = unifyPaths(paths_);
|
|
184549
184552
|
if (cwd) {
|
|
184550
|
-
paths = paths.map((
|
|
184551
|
-
const absPath = getAbsolutePath(
|
|
184553
|
+
paths = paths.map((path22) => {
|
|
184554
|
+
const absPath = getAbsolutePath(path22, cwd);
|
|
184552
184555
|
return absPath;
|
|
184553
184556
|
});
|
|
184554
184557
|
}
|
|
184555
|
-
paths.forEach((
|
|
184556
|
-
this._removeIgnoredPath(
|
|
184558
|
+
paths.forEach((path22) => {
|
|
184559
|
+
this._removeIgnoredPath(path22);
|
|
184557
184560
|
});
|
|
184558
184561
|
this._userIgnored = void 0;
|
|
184559
184562
|
if (!this._readyCount)
|
|
184560
184563
|
this._readyCount = 0;
|
|
184561
184564
|
this._readyCount += paths.length;
|
|
184562
|
-
Promise.all(paths.map(async (
|
|
184563
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
184565
|
+
Promise.all(paths.map(async (path22) => {
|
|
184566
|
+
const res = await this._nodeFsHandler._addToNodeFs(path22, !_internal, void 0, 0, _origAdd);
|
|
184564
184567
|
if (res)
|
|
184565
184568
|
this._emitReady();
|
|
184566
184569
|
return res;
|
|
@@ -184582,17 +184585,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184582
184585
|
return this;
|
|
184583
184586
|
const paths = unifyPaths(paths_);
|
|
184584
184587
|
const { cwd } = this.options;
|
|
184585
|
-
paths.forEach((
|
|
184586
|
-
if (!sp2.isAbsolute(
|
|
184588
|
+
paths.forEach((path22) => {
|
|
184589
|
+
if (!sp2.isAbsolute(path22) && !this._closers.has(path22)) {
|
|
184587
184590
|
if (cwd)
|
|
184588
|
-
|
|
184589
|
-
|
|
184591
|
+
path22 = sp2.join(cwd, path22);
|
|
184592
|
+
path22 = sp2.resolve(path22);
|
|
184590
184593
|
}
|
|
184591
|
-
this._closePath(
|
|
184592
|
-
this._addIgnoredPath(
|
|
184593
|
-
if (this._watched.has(
|
|
184594
|
+
this._closePath(path22);
|
|
184595
|
+
this._addIgnoredPath(path22);
|
|
184596
|
+
if (this._watched.has(path22)) {
|
|
184594
184597
|
this._addIgnoredPath({
|
|
184595
|
-
path:
|
|
184598
|
+
path: path22,
|
|
184596
184599
|
recursive: true
|
|
184597
184600
|
});
|
|
184598
184601
|
}
|
|
@@ -184656,38 +184659,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184656
184659
|
* @param stats arguments to be passed with event
|
|
184657
184660
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
184658
184661
|
*/
|
|
184659
|
-
async _emit(event,
|
|
184662
|
+
async _emit(event, path22, stats) {
|
|
184660
184663
|
if (this.closed)
|
|
184661
184664
|
return;
|
|
184662
184665
|
const opts = this.options;
|
|
184663
184666
|
if (isWindows)
|
|
184664
|
-
|
|
184667
|
+
path22 = sp2.normalize(path22);
|
|
184665
184668
|
if (opts.cwd)
|
|
184666
|
-
|
|
184667
|
-
const args = [
|
|
184669
|
+
path22 = sp2.relative(opts.cwd, path22);
|
|
184670
|
+
const args = [path22];
|
|
184668
184671
|
if (stats != null)
|
|
184669
184672
|
args.push(stats);
|
|
184670
184673
|
const awf = opts.awaitWriteFinish;
|
|
184671
184674
|
let pw;
|
|
184672
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
184675
|
+
if (awf && (pw = this._pendingWrites.get(path22))) {
|
|
184673
184676
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
184674
184677
|
return this;
|
|
184675
184678
|
}
|
|
184676
184679
|
if (opts.atomic) {
|
|
184677
184680
|
if (event === EVENTS.UNLINK) {
|
|
184678
|
-
this._pendingUnlinks.set(
|
|
184681
|
+
this._pendingUnlinks.set(path22, [event, ...args]);
|
|
184679
184682
|
setTimeout(() => {
|
|
184680
|
-
this._pendingUnlinks.forEach((entry,
|
|
184683
|
+
this._pendingUnlinks.forEach((entry, path23) => {
|
|
184681
184684
|
this.emit(...entry);
|
|
184682
184685
|
this.emit(EVENTS.ALL, ...entry);
|
|
184683
|
-
this._pendingUnlinks.delete(
|
|
184686
|
+
this._pendingUnlinks.delete(path23);
|
|
184684
184687
|
});
|
|
184685
184688
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
184686
184689
|
return this;
|
|
184687
184690
|
}
|
|
184688
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
184691
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path22)) {
|
|
184689
184692
|
event = EVENTS.CHANGE;
|
|
184690
|
-
this._pendingUnlinks.delete(
|
|
184693
|
+
this._pendingUnlinks.delete(path22);
|
|
184691
184694
|
}
|
|
184692
184695
|
}
|
|
184693
184696
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -184705,16 +184708,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184705
184708
|
this.emitWithAll(event, args);
|
|
184706
184709
|
}
|
|
184707
184710
|
};
|
|
184708
|
-
this._awaitWriteFinish(
|
|
184711
|
+
this._awaitWriteFinish(path22, awf.stabilityThreshold, event, awfEmit);
|
|
184709
184712
|
return this;
|
|
184710
184713
|
}
|
|
184711
184714
|
if (event === EVENTS.CHANGE) {
|
|
184712
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
184715
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path22, 50);
|
|
184713
184716
|
if (isThrottled)
|
|
184714
184717
|
return this;
|
|
184715
184718
|
}
|
|
184716
184719
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
184717
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
184720
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path22) : path22;
|
|
184718
184721
|
let stats2;
|
|
184719
184722
|
try {
|
|
184720
184723
|
stats2 = await stat3(fullPath);
|
|
@@ -184745,23 +184748,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184745
184748
|
* @param timeout duration of time to suppress duplicate actions
|
|
184746
184749
|
* @returns tracking object or false if action should be suppressed
|
|
184747
184750
|
*/
|
|
184748
|
-
_throttle(actionType,
|
|
184751
|
+
_throttle(actionType, path22, timeout) {
|
|
184749
184752
|
if (!this._throttled.has(actionType)) {
|
|
184750
184753
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
184751
184754
|
}
|
|
184752
184755
|
const action = this._throttled.get(actionType);
|
|
184753
184756
|
if (!action)
|
|
184754
184757
|
throw new Error("invalid throttle");
|
|
184755
|
-
const actionPath = action.get(
|
|
184758
|
+
const actionPath = action.get(path22);
|
|
184756
184759
|
if (actionPath) {
|
|
184757
184760
|
actionPath.count++;
|
|
184758
184761
|
return false;
|
|
184759
184762
|
}
|
|
184760
184763
|
let timeoutObject;
|
|
184761
184764
|
const clear = () => {
|
|
184762
|
-
const item = action.get(
|
|
184765
|
+
const item = action.get(path22);
|
|
184763
184766
|
const count = item ? item.count : 0;
|
|
184764
|
-
action.delete(
|
|
184767
|
+
action.delete(path22);
|
|
184765
184768
|
clearTimeout(timeoutObject);
|
|
184766
184769
|
if (item)
|
|
184767
184770
|
clearTimeout(item.timeoutObject);
|
|
@@ -184769,7 +184772,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184769
184772
|
};
|
|
184770
184773
|
timeoutObject = setTimeout(clear, timeout);
|
|
184771
184774
|
const thr = { timeoutObject, clear, count: 0 };
|
|
184772
|
-
action.set(
|
|
184775
|
+
action.set(path22, thr);
|
|
184773
184776
|
return thr;
|
|
184774
184777
|
}
|
|
184775
184778
|
_incrReadyCount() {
|
|
@@ -184783,44 +184786,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184783
184786
|
* @param event
|
|
184784
184787
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
184785
184788
|
*/
|
|
184786
|
-
_awaitWriteFinish(
|
|
184789
|
+
_awaitWriteFinish(path22, threshold, event, awfEmit) {
|
|
184787
184790
|
const awf = this.options.awaitWriteFinish;
|
|
184788
184791
|
if (typeof awf !== "object")
|
|
184789
184792
|
return;
|
|
184790
184793
|
const pollInterval = awf.pollInterval;
|
|
184791
184794
|
let timeoutHandler;
|
|
184792
|
-
let fullPath =
|
|
184793
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
184794
|
-
fullPath = sp2.join(this.options.cwd,
|
|
184795
|
+
let fullPath = path22;
|
|
184796
|
+
if (this.options.cwd && !sp2.isAbsolute(path22)) {
|
|
184797
|
+
fullPath = sp2.join(this.options.cwd, path22);
|
|
184795
184798
|
}
|
|
184796
184799
|
const now = /* @__PURE__ */ new Date();
|
|
184797
184800
|
const writes = this._pendingWrites;
|
|
184798
184801
|
function awaitWriteFinishFn(prevStat) {
|
|
184799
184802
|
statcb(fullPath, (err, curStat) => {
|
|
184800
|
-
if (err || !writes.has(
|
|
184803
|
+
if (err || !writes.has(path22)) {
|
|
184801
184804
|
if (err && err.code !== "ENOENT")
|
|
184802
184805
|
awfEmit(err);
|
|
184803
184806
|
return;
|
|
184804
184807
|
}
|
|
184805
184808
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
184806
184809
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
184807
|
-
writes.get(
|
|
184810
|
+
writes.get(path22).lastChange = now2;
|
|
184808
184811
|
}
|
|
184809
|
-
const pw = writes.get(
|
|
184812
|
+
const pw = writes.get(path22);
|
|
184810
184813
|
const df = now2 - pw.lastChange;
|
|
184811
184814
|
if (df >= threshold) {
|
|
184812
|
-
writes.delete(
|
|
184815
|
+
writes.delete(path22);
|
|
184813
184816
|
awfEmit(void 0, curStat);
|
|
184814
184817
|
} else {
|
|
184815
184818
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
184816
184819
|
}
|
|
184817
184820
|
});
|
|
184818
184821
|
}
|
|
184819
|
-
if (!writes.has(
|
|
184820
|
-
writes.set(
|
|
184822
|
+
if (!writes.has(path22)) {
|
|
184823
|
+
writes.set(path22, {
|
|
184821
184824
|
lastChange: now,
|
|
184822
184825
|
cancelWait: () => {
|
|
184823
|
-
writes.delete(
|
|
184826
|
+
writes.delete(path22);
|
|
184824
184827
|
clearTimeout(timeoutHandler);
|
|
184825
184828
|
return event;
|
|
184826
184829
|
}
|
|
@@ -184831,8 +184834,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184831
184834
|
/**
|
|
184832
184835
|
* Determines whether user has asked to ignore this path.
|
|
184833
184836
|
*/
|
|
184834
|
-
_isIgnored(
|
|
184835
|
-
if (this.options.atomic && DOT_RE.test(
|
|
184837
|
+
_isIgnored(path22, stats) {
|
|
184838
|
+
if (this.options.atomic && DOT_RE.test(path22))
|
|
184836
184839
|
return true;
|
|
184837
184840
|
if (!this._userIgnored) {
|
|
184838
184841
|
const { cwd } = this.options;
|
|
@@ -184842,17 +184845,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184842
184845
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
184843
184846
|
this._userIgnored = anymatch(list, void 0);
|
|
184844
184847
|
}
|
|
184845
|
-
return this._userIgnored(
|
|
184848
|
+
return this._userIgnored(path22, stats);
|
|
184846
184849
|
}
|
|
184847
|
-
_isntIgnored(
|
|
184848
|
-
return !this._isIgnored(
|
|
184850
|
+
_isntIgnored(path22, stat4) {
|
|
184851
|
+
return !this._isIgnored(path22, stat4);
|
|
184849
184852
|
}
|
|
184850
184853
|
/**
|
|
184851
184854
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
184852
184855
|
* @param path file or directory pattern being watched
|
|
184853
184856
|
*/
|
|
184854
|
-
_getWatchHelpers(
|
|
184855
|
-
return new WatchHelper(
|
|
184857
|
+
_getWatchHelpers(path22) {
|
|
184858
|
+
return new WatchHelper(path22, this.options.followSymlinks, this);
|
|
184856
184859
|
}
|
|
184857
184860
|
// Directory helpers
|
|
184858
184861
|
// -----------------
|
|
@@ -184884,63 +184887,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184884
184887
|
* @param item base path of item/directory
|
|
184885
184888
|
*/
|
|
184886
184889
|
_remove(directory, item, isDirectory) {
|
|
184887
|
-
const
|
|
184888
|
-
const fullPath = sp2.resolve(
|
|
184889
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
184890
|
-
if (!this._throttle("remove",
|
|
184890
|
+
const path22 = sp2.join(directory, item);
|
|
184891
|
+
const fullPath = sp2.resolve(path22);
|
|
184892
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path22) || this._watched.has(fullPath);
|
|
184893
|
+
if (!this._throttle("remove", path22, 100))
|
|
184891
184894
|
return;
|
|
184892
184895
|
if (!isDirectory && this._watched.size === 1) {
|
|
184893
184896
|
this.add(directory, item, true);
|
|
184894
184897
|
}
|
|
184895
|
-
const wp = this._getWatchedDir(
|
|
184898
|
+
const wp = this._getWatchedDir(path22);
|
|
184896
184899
|
const nestedDirectoryChildren = wp.getChildren();
|
|
184897
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
184900
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path22, nested));
|
|
184898
184901
|
const parent = this._getWatchedDir(directory);
|
|
184899
184902
|
const wasTracked = parent.has(item);
|
|
184900
184903
|
parent.remove(item);
|
|
184901
184904
|
if (this._symlinkPaths.has(fullPath)) {
|
|
184902
184905
|
this._symlinkPaths.delete(fullPath);
|
|
184903
184906
|
}
|
|
184904
|
-
let relPath =
|
|
184907
|
+
let relPath = path22;
|
|
184905
184908
|
if (this.options.cwd)
|
|
184906
|
-
relPath = sp2.relative(this.options.cwd,
|
|
184909
|
+
relPath = sp2.relative(this.options.cwd, path22);
|
|
184907
184910
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
184908
184911
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
184909
184912
|
if (event === EVENTS.ADD)
|
|
184910
184913
|
return;
|
|
184911
184914
|
}
|
|
184912
|
-
this._watched.delete(
|
|
184915
|
+
this._watched.delete(path22);
|
|
184913
184916
|
this._watched.delete(fullPath);
|
|
184914
184917
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
184915
|
-
if (wasTracked && !this._isIgnored(
|
|
184916
|
-
this._emit(eventName,
|
|
184917
|
-
this._closePath(
|
|
184918
|
+
if (wasTracked && !this._isIgnored(path22))
|
|
184919
|
+
this._emit(eventName, path22);
|
|
184920
|
+
this._closePath(path22);
|
|
184918
184921
|
}
|
|
184919
184922
|
/**
|
|
184920
184923
|
* Closes all watchers for a path
|
|
184921
184924
|
*/
|
|
184922
|
-
_closePath(
|
|
184923
|
-
this._closeFile(
|
|
184924
|
-
const dir = sp2.dirname(
|
|
184925
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
184925
|
+
_closePath(path22) {
|
|
184926
|
+
this._closeFile(path22);
|
|
184927
|
+
const dir = sp2.dirname(path22);
|
|
184928
|
+
this._getWatchedDir(dir).remove(sp2.basename(path22));
|
|
184926
184929
|
}
|
|
184927
184930
|
/**
|
|
184928
184931
|
* Closes only file-specific watchers
|
|
184929
184932
|
*/
|
|
184930
|
-
_closeFile(
|
|
184931
|
-
const closers = this._closers.get(
|
|
184933
|
+
_closeFile(path22) {
|
|
184934
|
+
const closers = this._closers.get(path22);
|
|
184932
184935
|
if (!closers)
|
|
184933
184936
|
return;
|
|
184934
184937
|
closers.forEach((closer) => closer());
|
|
184935
|
-
this._closers.delete(
|
|
184938
|
+
this._closers.delete(path22);
|
|
184936
184939
|
}
|
|
184937
|
-
_addPathCloser(
|
|
184940
|
+
_addPathCloser(path22, closer) {
|
|
184938
184941
|
if (!closer)
|
|
184939
184942
|
return;
|
|
184940
|
-
let list = this._closers.get(
|
|
184943
|
+
let list = this._closers.get(path22);
|
|
184941
184944
|
if (!list) {
|
|
184942
184945
|
list = [];
|
|
184943
|
-
this._closers.set(
|
|
184946
|
+
this._closers.set(path22, list);
|
|
184944
184947
|
}
|
|
184945
184948
|
list.push(closer);
|
|
184946
184949
|
}
|
|
@@ -184998,8 +185001,8 @@ var StablePortAllocator = class {
|
|
|
184998
185001
|
portsFilePath;
|
|
184999
185002
|
savedPorts = {};
|
|
185000
185003
|
usedPorts = /* @__PURE__ */ new Set();
|
|
185001
|
-
constructor(projectRoot) {
|
|
185002
|
-
this.portsDir = path4.join(projectRoot, ".specific");
|
|
185004
|
+
constructor(projectRoot, key = "default") {
|
|
185005
|
+
this.portsDir = path4.join(projectRoot, ".specific", "keys", key);
|
|
185003
185006
|
this.portsFilePath = path4.join(this.portsDir, "ports.json");
|
|
185004
185007
|
this.loadPorts();
|
|
185005
185008
|
}
|
|
@@ -185185,10 +185188,10 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185185
185188
|
});
|
|
185186
185189
|
}
|
|
185187
185190
|
}
|
|
185188
|
-
await new Promise((
|
|
185191
|
+
await new Promise((resolve5, reject) => {
|
|
185189
185192
|
fileStream.end((err) => {
|
|
185190
185193
|
if (err) reject(err);
|
|
185191
|
-
else
|
|
185194
|
+
else resolve5();
|
|
185192
185195
|
});
|
|
185193
185196
|
});
|
|
185194
185197
|
fs5.renameSync(partPath, destPath);
|
|
@@ -185552,7 +185555,7 @@ async function startStorage(storage, port, dataDir) {
|
|
|
185552
185555
|
};
|
|
185553
185556
|
}
|
|
185554
185557
|
async function runCommand(command, args, env2) {
|
|
185555
|
-
return new Promise((
|
|
185558
|
+
return new Promise((resolve5, reject) => {
|
|
185556
185559
|
const proc = spawn(command, args, {
|
|
185557
185560
|
stdio: ["ignore", "pipe", "pipe"],
|
|
185558
185561
|
env: env2
|
|
@@ -185563,7 +185566,7 @@ async function runCommand(command, args, env2) {
|
|
|
185563
185566
|
});
|
|
185564
185567
|
proc.on("close", (code) => {
|
|
185565
185568
|
if (code === 0) {
|
|
185566
|
-
|
|
185569
|
+
resolve5();
|
|
185567
185570
|
} else {
|
|
185568
185571
|
reject(new Error(`Command failed with code ${code}: ${stderr}`));
|
|
185569
185572
|
}
|
|
@@ -185572,7 +185575,7 @@ async function runCommand(command, args, env2) {
|
|
|
185572
185575
|
});
|
|
185573
185576
|
}
|
|
185574
185577
|
async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
185575
|
-
return new Promise((
|
|
185578
|
+
return new Promise((resolve5, reject) => {
|
|
185576
185579
|
const proc = spawn(
|
|
185577
185580
|
postgresPath,
|
|
185578
185581
|
["--single", "-D", dataDir, "postgres"],
|
|
@@ -185586,7 +185589,7 @@ async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
|
185586
185589
|
stderr += data.toString();
|
|
185587
185590
|
});
|
|
185588
185591
|
proc.on("close", (code) => {
|
|
185589
|
-
|
|
185592
|
+
resolve5();
|
|
185590
185593
|
});
|
|
185591
185594
|
proc.on("error", reject);
|
|
185592
185595
|
proc.stdin?.write(`CREATE DATABASE "${dbName}";
|
|
@@ -185606,33 +185609,33 @@ async function waitForTcpPort(host, port, timeoutMs = 3e4) {
|
|
|
185606
185609
|
throw new Error(`Port ${port} did not become available within timeout`);
|
|
185607
185610
|
}
|
|
185608
185611
|
function checkTcpPort(host, port) {
|
|
185609
|
-
return new Promise((
|
|
185612
|
+
return new Promise((resolve5) => {
|
|
185610
185613
|
const socket = new net.Socket();
|
|
185611
185614
|
socket.setTimeout(1e3);
|
|
185612
185615
|
socket.on("connect", () => {
|
|
185613
185616
|
socket.destroy();
|
|
185614
|
-
|
|
185617
|
+
resolve5(true);
|
|
185615
185618
|
});
|
|
185616
185619
|
socket.on("timeout", () => {
|
|
185617
185620
|
socket.destroy();
|
|
185618
|
-
|
|
185621
|
+
resolve5(false);
|
|
185619
185622
|
});
|
|
185620
185623
|
socket.on("error", () => {
|
|
185621
185624
|
socket.destroy();
|
|
185622
|
-
|
|
185625
|
+
resolve5(false);
|
|
185623
185626
|
});
|
|
185624
185627
|
socket.connect(port, host);
|
|
185625
185628
|
});
|
|
185626
185629
|
}
|
|
185627
185630
|
async function stopProcess(proc) {
|
|
185628
|
-
return new Promise((
|
|
185631
|
+
return new Promise((resolve5) => {
|
|
185629
185632
|
if (proc.killed || proc.exitCode !== null) {
|
|
185630
|
-
|
|
185633
|
+
resolve5();
|
|
185631
185634
|
return;
|
|
185632
185635
|
}
|
|
185633
185636
|
proc.once("exit", () => {
|
|
185634
185637
|
clearTimeout(forceKillTimeout);
|
|
185635
|
-
|
|
185638
|
+
resolve5();
|
|
185636
185639
|
});
|
|
185637
185640
|
proc.kill("SIGTERM");
|
|
185638
185641
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -185643,7 +185646,7 @@ async function stopProcess(proc) {
|
|
|
185643
185646
|
});
|
|
185644
185647
|
}
|
|
185645
185648
|
function sleep(ms) {
|
|
185646
|
-
return new Promise((
|
|
185649
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
185647
185650
|
}
|
|
185648
185651
|
|
|
185649
185652
|
// src/lib/dev/service-runner.ts
|
|
@@ -186041,14 +186044,14 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
186041
186044
|
ports: endpointPorts,
|
|
186042
186045
|
process: child,
|
|
186043
186046
|
async stop() {
|
|
186044
|
-
return new Promise((
|
|
186047
|
+
return new Promise((resolve5) => {
|
|
186045
186048
|
if (child.killed || child.exitCode !== null) {
|
|
186046
|
-
|
|
186049
|
+
resolve5();
|
|
186047
186050
|
return;
|
|
186048
186051
|
}
|
|
186049
186052
|
child.once("exit", () => {
|
|
186050
186053
|
clearTimeout(forceKillTimeout);
|
|
186051
|
-
|
|
186054
|
+
resolve5();
|
|
186052
186055
|
});
|
|
186053
186056
|
child.kill("SIGTERM");
|
|
186054
186057
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186069,11 +186072,16 @@ var InstanceStateManager = class {
|
|
|
186069
186072
|
statePath;
|
|
186070
186073
|
lockPath;
|
|
186071
186074
|
ownsInstances = false;
|
|
186072
|
-
|
|
186073
|
-
|
|
186075
|
+
key;
|
|
186076
|
+
constructor(projectRoot, key = "default") {
|
|
186077
|
+
this.key = key;
|
|
186078
|
+
this.stateDir = path9.join(projectRoot, ".specific", "keys", key);
|
|
186074
186079
|
this.statePath = path9.join(this.stateDir, "state.json");
|
|
186075
186080
|
this.lockPath = path9.join(this.stateDir, "state.lock");
|
|
186076
186081
|
}
|
|
186082
|
+
getKey() {
|
|
186083
|
+
return this.key;
|
|
186084
|
+
}
|
|
186077
186085
|
ensureStateDir() {
|
|
186078
186086
|
if (!fs8.existsSync(this.stateDir)) {
|
|
186079
186087
|
fs8.mkdirSync(this.stateDir, { recursive: true });
|
|
@@ -186124,7 +186132,7 @@ var InstanceStateManager = class {
|
|
|
186124
186132
|
}
|
|
186125
186133
|
continue;
|
|
186126
186134
|
}
|
|
186127
|
-
await new Promise((
|
|
186135
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
186128
186136
|
} else {
|
|
186129
186137
|
throw e;
|
|
186130
186138
|
}
|
|
@@ -186280,7 +186288,7 @@ var HTTP_PORT = 80;
|
|
|
186280
186288
|
var HTTPS_PORT = 443;
|
|
186281
186289
|
var DOMAIN_SUFFIX = ".local.spcf.app";
|
|
186282
186290
|
var ADMIN_DOMAIN = "local.spcf.app";
|
|
186283
|
-
var
|
|
186291
|
+
var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
|
|
186284
186292
|
var MIME_TYPES = {
|
|
186285
186293
|
".html": "text/html",
|
|
186286
186294
|
".css": "text/css",
|
|
@@ -186295,11 +186303,31 @@ var MIME_TYPES = {
|
|
|
186295
186303
|
".woff2": "font/woff2",
|
|
186296
186304
|
".txt": "text/plain"
|
|
186297
186305
|
};
|
|
186298
|
-
async function startHttpProxy(services, certificate, getState,
|
|
186306
|
+
async function startHttpProxy(services, certificate, getState, instanceKey = "default") {
|
|
186299
186307
|
const serviceMap = /* @__PURE__ */ new Map();
|
|
186308
|
+
const adminPortMap = /* @__PURE__ */ new Map();
|
|
186309
|
+
const drizzleGatewayPortMap = /* @__PURE__ */ new Map();
|
|
186310
|
+
const buildMapKey = (serviceName, key) => {
|
|
186311
|
+
return key === "default" ? serviceName : `${serviceName}.${key}`;
|
|
186312
|
+
};
|
|
186300
186313
|
for (const svc of services) {
|
|
186301
|
-
serviceMap.set(svc.name, svc.port);
|
|
186314
|
+
serviceMap.set(buildMapKey(svc.name, instanceKey), svc.port);
|
|
186302
186315
|
}
|
|
186316
|
+
const updateServices = (registryServices, keys) => {
|
|
186317
|
+
serviceMap.clear();
|
|
186318
|
+
adminPortMap.clear();
|
|
186319
|
+
drizzleGatewayPortMap.clear();
|
|
186320
|
+
for (const svc of registryServices) {
|
|
186321
|
+
serviceMap.set(buildMapKey(svc.serviceName, svc.key), svc.port);
|
|
186322
|
+
}
|
|
186323
|
+
for (const [key, registration] of Object.entries(keys)) {
|
|
186324
|
+
adminPortMap.set(key, registration.adminPort);
|
|
186325
|
+
if (registration.drizzleGatewayPort !== void 0) {
|
|
186326
|
+
drizzleGatewayPortMap.set(key, registration.drizzleGatewayPort);
|
|
186327
|
+
}
|
|
186328
|
+
}
|
|
186329
|
+
writeLog("proxy", `Updated service map: ${serviceMap.size} services, ${adminPortMap.size} admin instances, ${drizzleGatewayPortMap.size} drizzle gateways`);
|
|
186330
|
+
};
|
|
186303
186331
|
const { key: defaultKey, cert: defaultCert } = certificate;
|
|
186304
186332
|
const proxy = httpProxy.createProxyServer({});
|
|
186305
186333
|
proxy.on("error", (err, req, res) => {
|
|
@@ -186313,38 +186341,64 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186313
186341
|
const handleRequest = (req, res) => {
|
|
186314
186342
|
const host = req.headers.host || "";
|
|
186315
186343
|
const hostname = host.split(":")[0];
|
|
186316
|
-
|
|
186317
|
-
|
|
186318
|
-
|
|
186319
|
-
|
|
186320
|
-
|
|
186321
|
-
|
|
186322
|
-
|
|
186344
|
+
const drizzleKey = extractDrizzleGatewayKey(host);
|
|
186345
|
+
if (drizzleKey !== null) {
|
|
186346
|
+
const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
|
|
186347
|
+
if (drizzlePort) {
|
|
186348
|
+
proxy.web(req, res, {
|
|
186349
|
+
target: `http://127.0.0.1:${drizzlePort}`
|
|
186350
|
+
});
|
|
186351
|
+
return;
|
|
186352
|
+
}
|
|
186353
|
+
res.writeHead(503, { "Content-Type": "text/html" });
|
|
186354
|
+
res.end("<h1>Database Viewer</h1><p>No database viewer running for this instance.</p>");
|
|
186323
186355
|
return;
|
|
186324
186356
|
}
|
|
186325
|
-
const
|
|
186326
|
-
if (
|
|
186327
|
-
|
|
186357
|
+
const adminKey = extractAdminKey(host);
|
|
186358
|
+
if (adminKey !== null) {
|
|
186359
|
+
const adminPort = adminPortMap.get(adminKey);
|
|
186360
|
+
if (adminPort) {
|
|
186361
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
186362
|
+
if (url.pathname.startsWith("/api/")) {
|
|
186363
|
+
proxy.web(req, res, { target: `http://127.0.0.1:${adminPort}` });
|
|
186364
|
+
return;
|
|
186365
|
+
}
|
|
186366
|
+
serveStaticFile(res, url.pathname);
|
|
186367
|
+
return;
|
|
186368
|
+
}
|
|
186369
|
+
if (adminKey === "default" && hostname === ADMIN_DOMAIN) {
|
|
186370
|
+
res.writeHead(503, { "Content-Type": "text/html" });
|
|
186371
|
+
res.end("<h1>Admin UI</h1><p>No dev instance running. Start <code>specific dev</code> first.</p>");
|
|
186372
|
+
return;
|
|
186373
|
+
}
|
|
186374
|
+
}
|
|
186375
|
+
const route = extractServiceAndKey(host);
|
|
186376
|
+
if (!route) {
|
|
186377
|
+
return sendNotFound(res, null, serviceMap);
|
|
186328
186378
|
}
|
|
186329
|
-
const
|
|
186379
|
+
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
186380
|
+
const targetPort = serviceMap.get(mapKey);
|
|
186330
186381
|
if (!targetPort) {
|
|
186331
|
-
return sendNotFound(res, serviceName,
|
|
186382
|
+
return sendNotFound(res, route.serviceName, serviceMap);
|
|
186332
186383
|
}
|
|
186333
186384
|
proxy.web(req, res, { target: `http://127.0.0.1:${targetPort}` });
|
|
186334
186385
|
};
|
|
186335
186386
|
const handleUpgrade = (req, socket, head) => {
|
|
186336
186387
|
const host = req.headers.host || "";
|
|
186337
|
-
const
|
|
186338
|
-
if (
|
|
186339
|
-
|
|
186340
|
-
|
|
186388
|
+
const adminKey = extractAdminKey(host);
|
|
186389
|
+
if (adminKey !== null) {
|
|
186390
|
+
if (adminPortMap.has(adminKey)) {
|
|
186391
|
+
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186392
|
+
return;
|
|
186393
|
+
}
|
|
186341
186394
|
}
|
|
186342
|
-
const
|
|
186343
|
-
if (!
|
|
186395
|
+
const route = extractServiceAndKey(host);
|
|
186396
|
+
if (!route) {
|
|
186344
186397
|
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186345
186398
|
return;
|
|
186346
186399
|
}
|
|
186347
|
-
const
|
|
186400
|
+
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
186401
|
+
const targetPort = serviceMap.get(mapKey);
|
|
186348
186402
|
if (!targetPort) {
|
|
186349
186403
|
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186350
186404
|
return;
|
|
@@ -186361,7 +186415,7 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186361
186415
|
handleRequest
|
|
186362
186416
|
);
|
|
186363
186417
|
httpsServer.on("upgrade", handleUpgrade);
|
|
186364
|
-
return new Promise((
|
|
186418
|
+
return new Promise((resolve5, reject) => {
|
|
186365
186419
|
let httpStarted = false;
|
|
186366
186420
|
let httpsStarted = false;
|
|
186367
186421
|
let failed = false;
|
|
@@ -186372,17 +186426,25 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186372
186426
|
"proxy",
|
|
186373
186427
|
`HTTP/HTTPS proxy started on ports ${HTTP_PORT}/${HTTPS_PORT}`
|
|
186374
186428
|
);
|
|
186375
|
-
|
|
186429
|
+
resolve5({
|
|
186376
186430
|
httpPort: HTTP_PORT,
|
|
186377
186431
|
httpsPort: HTTPS_PORT,
|
|
186432
|
+
updateServices,
|
|
186433
|
+
updateCertificate(newCert) {
|
|
186434
|
+
httpsServer.setSecureContext({
|
|
186435
|
+
key: newCert.key,
|
|
186436
|
+
cert: newCert.cert
|
|
186437
|
+
});
|
|
186438
|
+
writeLog("proxy", "Certificate updated");
|
|
186439
|
+
},
|
|
186378
186440
|
async stop() {
|
|
186379
|
-
return new Promise((
|
|
186441
|
+
return new Promise((resolve6) => {
|
|
186380
186442
|
let closed = 0;
|
|
186381
186443
|
const onClose = () => {
|
|
186382
186444
|
closed++;
|
|
186383
186445
|
if (closed === 2) {
|
|
186384
186446
|
clearTimeout(forceCloseTimeout);
|
|
186385
|
-
|
|
186447
|
+
resolve6();
|
|
186386
186448
|
}
|
|
186387
186449
|
};
|
|
186388
186450
|
httpServer.close(onClose);
|
|
@@ -186390,7 +186452,7 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186390
186452
|
const forceCloseTimeout = setTimeout(() => {
|
|
186391
186453
|
httpServer.closeAllConnections?.();
|
|
186392
186454
|
httpsServer.closeAllConnections?.();
|
|
186393
|
-
|
|
186455
|
+
resolve6();
|
|
186394
186456
|
}, 2e3);
|
|
186395
186457
|
});
|
|
186396
186458
|
}
|
|
@@ -186424,15 +186486,70 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186424
186486
|
});
|
|
186425
186487
|
});
|
|
186426
186488
|
}
|
|
186427
|
-
function
|
|
186489
|
+
function extractServiceAndKey(host) {
|
|
186490
|
+
const hostname = host.split(":")[0];
|
|
186491
|
+
if (!hostname || !hostname.endsWith(DOMAIN_SUFFIX)) {
|
|
186492
|
+
return null;
|
|
186493
|
+
}
|
|
186494
|
+
const prefix = hostname.slice(0, -DOMAIN_SUFFIX.length);
|
|
186495
|
+
if (!prefix) {
|
|
186496
|
+
return null;
|
|
186497
|
+
}
|
|
186498
|
+
const parts = prefix.split(".");
|
|
186499
|
+
if (parts.length === 1) {
|
|
186500
|
+
return { serviceName: parts[0], key: "default" };
|
|
186501
|
+
} else if (parts.length === 2) {
|
|
186502
|
+
return { serviceName: parts[0], key: parts[1] };
|
|
186503
|
+
}
|
|
186504
|
+
return null;
|
|
186505
|
+
}
|
|
186506
|
+
function extractDrizzleGatewayKey(host) {
|
|
186428
186507
|
const hostname = host.split(":")[0];
|
|
186429
186508
|
if (!hostname || !hostname.endsWith(DOMAIN_SUFFIX)) {
|
|
186430
186509
|
return null;
|
|
186431
186510
|
}
|
|
186432
|
-
|
|
186511
|
+
const prefix = hostname.slice(0, -DOMAIN_SUFFIX.length);
|
|
186512
|
+
if (!prefix) {
|
|
186513
|
+
return null;
|
|
186514
|
+
}
|
|
186515
|
+
const parts = prefix.split(".");
|
|
186516
|
+
if (parts.length === 1 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
|
|
186517
|
+
return "default";
|
|
186518
|
+
} else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
|
|
186519
|
+
return parts[1];
|
|
186520
|
+
}
|
|
186521
|
+
return null;
|
|
186433
186522
|
}
|
|
186434
|
-
function
|
|
186435
|
-
const
|
|
186523
|
+
function extractAdminKey(host) {
|
|
186524
|
+
const hostname = host.split(":")[0];
|
|
186525
|
+
if (!hostname) {
|
|
186526
|
+
return null;
|
|
186527
|
+
}
|
|
186528
|
+
if (hostname === ADMIN_DOMAIN) {
|
|
186529
|
+
return "default";
|
|
186530
|
+
}
|
|
186531
|
+
if (!hostname.endsWith(DOMAIN_SUFFIX)) {
|
|
186532
|
+
return null;
|
|
186533
|
+
}
|
|
186534
|
+
const prefix = hostname.slice(0, -DOMAIN_SUFFIX.length);
|
|
186535
|
+
if (!prefix) {
|
|
186536
|
+
return null;
|
|
186537
|
+
}
|
|
186538
|
+
const parts = prefix.split(".");
|
|
186539
|
+
if (parts.length === 1) {
|
|
186540
|
+
return parts[0];
|
|
186541
|
+
}
|
|
186542
|
+
return null;
|
|
186543
|
+
}
|
|
186544
|
+
function sendNotFound(res, requestedService, serviceMap) {
|
|
186545
|
+
const serviceList = Array.from(serviceMap.keys()).map((key) => {
|
|
186546
|
+
const parts = key.split(".");
|
|
186547
|
+
if (parts.length === 1) {
|
|
186548
|
+
return `<li>${parts[0]}.local.spcf.app</li>`;
|
|
186549
|
+
} else {
|
|
186550
|
+
return `<li>${key}.local.spcf.app</li>`;
|
|
186551
|
+
}
|
|
186552
|
+
}).join("\n");
|
|
186436
186553
|
const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
|
|
186437
186554
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
186438
186555
|
res.end(`<!DOCTYPE html>
|
|
@@ -186441,36 +186558,11 @@ function sendNotFound(res, requestedService, availableServices) {
|
|
|
186441
186558
|
<body>
|
|
186442
186559
|
<h1>Service Not Found</h1>
|
|
186443
186560
|
<p>${message}</p>
|
|
186444
|
-
${
|
|
186561
|
+
${serviceMap.size > 0 ? `<p>Available services:</p>
|
|
186445
186562
|
<ul>${serviceList}</ul>` : "<p>No services are currently exposed.</p>"}
|
|
186446
186563
|
</body>
|
|
186447
186564
|
</html>`);
|
|
186448
186565
|
}
|
|
186449
|
-
function handleAdminRequest(req, res, proxy, getState) {
|
|
186450
|
-
const url = new URL(req.url || "/", "http://localhost");
|
|
186451
|
-
if (url.pathname.startsWith("/api/")) {
|
|
186452
|
-
return handleAdminApi(req, res, url, getState);
|
|
186453
|
-
}
|
|
186454
|
-
serveStaticFile(res, url.pathname);
|
|
186455
|
-
}
|
|
186456
|
-
function handleAdminApi(_req, res, url, getState) {
|
|
186457
|
-
res.setHeader("Content-Type", "application/json");
|
|
186458
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
186459
|
-
const pathname = url.pathname.endsWith("/") && url.pathname.length > 1 ? url.pathname.slice(0, -1) : url.pathname;
|
|
186460
|
-
if (pathname === "/api/state") {
|
|
186461
|
-
if (!getState) {
|
|
186462
|
-
res.writeHead(500);
|
|
186463
|
-
res.end(JSON.stringify({ error: "State not available" }));
|
|
186464
|
-
return;
|
|
186465
|
-
}
|
|
186466
|
-
const state = getState();
|
|
186467
|
-
res.writeHead(200);
|
|
186468
|
-
res.end(JSON.stringify(state));
|
|
186469
|
-
return;
|
|
186470
|
-
}
|
|
186471
|
-
res.writeHead(404);
|
|
186472
|
-
res.end(JSON.stringify({ error: "Not found" }));
|
|
186473
|
-
}
|
|
186474
186566
|
function serveStaticFile(res, pathname) {
|
|
186475
186567
|
let filePath = pathname;
|
|
186476
186568
|
if (filePath === "/" || filePath.endsWith("/")) {
|
|
@@ -186522,6 +186614,48 @@ function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
|
186522
186614
|
res.end("<h1>Internal Server Error</h1>");
|
|
186523
186615
|
}
|
|
186524
186616
|
}
|
|
186617
|
+
async function startAdminServer(getState) {
|
|
186618
|
+
return new Promise((resolve5, reject) => {
|
|
186619
|
+
const server = http.createServer((req, res) => {
|
|
186620
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
186621
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
186622
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
186623
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
186624
|
+
if (req.method === "OPTIONS") {
|
|
186625
|
+
res.writeHead(204);
|
|
186626
|
+
res.end();
|
|
186627
|
+
return;
|
|
186628
|
+
}
|
|
186629
|
+
const pathname = url.pathname.endsWith("/") && url.pathname.length > 1 ? url.pathname.slice(0, -1) : url.pathname;
|
|
186630
|
+
if (pathname === "/api/state") {
|
|
186631
|
+
res.setHeader("Content-Type", "application/json");
|
|
186632
|
+
const state = getState();
|
|
186633
|
+
res.writeHead(200);
|
|
186634
|
+
res.end(JSON.stringify(state));
|
|
186635
|
+
return;
|
|
186636
|
+
}
|
|
186637
|
+
res.setHeader("Content-Type", "application/json");
|
|
186638
|
+
res.writeHead(404);
|
|
186639
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
186640
|
+
});
|
|
186641
|
+
server.on("error", reject);
|
|
186642
|
+
server.listen(0, "127.0.0.1", () => {
|
|
186643
|
+
const addr = server.address();
|
|
186644
|
+
if (!addr || typeof addr === "string") {
|
|
186645
|
+
reject(new Error("Failed to get server address"));
|
|
186646
|
+
return;
|
|
186647
|
+
}
|
|
186648
|
+
const port = addr.port;
|
|
186649
|
+
writeLog("admin", `Admin API server started on port ${port}`);
|
|
186650
|
+
resolve5({
|
|
186651
|
+
port,
|
|
186652
|
+
stop: () => new Promise((res, rej) => {
|
|
186653
|
+
server.close((err) => err ? rej(err) : res());
|
|
186654
|
+
})
|
|
186655
|
+
});
|
|
186656
|
+
});
|
|
186657
|
+
});
|
|
186658
|
+
}
|
|
186525
186659
|
|
|
186526
186660
|
// src/lib/dev/electric-manager.ts
|
|
186527
186661
|
import * as net2 from "net";
|
|
@@ -186579,33 +186713,33 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
|
|
|
186579
186713
|
throw new Error(`Electric port ${port} did not become available within timeout`);
|
|
186580
186714
|
}
|
|
186581
186715
|
function checkTcpPort2(host, port) {
|
|
186582
|
-
return new Promise((
|
|
186716
|
+
return new Promise((resolve5) => {
|
|
186583
186717
|
const socket = new net2.Socket();
|
|
186584
186718
|
socket.setTimeout(1e3);
|
|
186585
186719
|
socket.on("connect", () => {
|
|
186586
186720
|
socket.destroy();
|
|
186587
|
-
|
|
186721
|
+
resolve5(true);
|
|
186588
186722
|
});
|
|
186589
186723
|
socket.on("timeout", () => {
|
|
186590
186724
|
socket.destroy();
|
|
186591
|
-
|
|
186725
|
+
resolve5(false);
|
|
186592
186726
|
});
|
|
186593
186727
|
socket.on("error", () => {
|
|
186594
186728
|
socket.destroy();
|
|
186595
|
-
|
|
186729
|
+
resolve5(false);
|
|
186596
186730
|
});
|
|
186597
186731
|
socket.connect(port, host);
|
|
186598
186732
|
});
|
|
186599
186733
|
}
|
|
186600
186734
|
async function stopProcess2(proc) {
|
|
186601
|
-
return new Promise((
|
|
186735
|
+
return new Promise((resolve5) => {
|
|
186602
186736
|
if (proc.killed || proc.exitCode !== null) {
|
|
186603
|
-
|
|
186737
|
+
resolve5();
|
|
186604
186738
|
return;
|
|
186605
186739
|
}
|
|
186606
186740
|
proc.once("exit", () => {
|
|
186607
186741
|
clearTimeout(forceKillTimeout);
|
|
186608
|
-
|
|
186742
|
+
resolve5();
|
|
186609
186743
|
});
|
|
186610
186744
|
proc.kill("SIGTERM");
|
|
186611
186745
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186616,7 +186750,7 @@ async function stopProcess2(proc) {
|
|
|
186616
186750
|
});
|
|
186617
186751
|
}
|
|
186618
186752
|
function sleep2(ms) {
|
|
186619
|
-
return new Promise((
|
|
186753
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
186620
186754
|
}
|
|
186621
186755
|
|
|
186622
186756
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
@@ -186700,33 +186834,33 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
|
|
|
186700
186834
|
);
|
|
186701
186835
|
}
|
|
186702
186836
|
function checkTcpPort3(host, port) {
|
|
186703
|
-
return new Promise((
|
|
186837
|
+
return new Promise((resolve5) => {
|
|
186704
186838
|
const socket = new net3.Socket();
|
|
186705
186839
|
socket.setTimeout(1e3);
|
|
186706
186840
|
socket.on("connect", () => {
|
|
186707
186841
|
socket.destroy();
|
|
186708
|
-
|
|
186842
|
+
resolve5(true);
|
|
186709
186843
|
});
|
|
186710
186844
|
socket.on("timeout", () => {
|
|
186711
186845
|
socket.destroy();
|
|
186712
|
-
|
|
186846
|
+
resolve5(false);
|
|
186713
186847
|
});
|
|
186714
186848
|
socket.on("error", () => {
|
|
186715
186849
|
socket.destroy();
|
|
186716
|
-
|
|
186850
|
+
resolve5(false);
|
|
186717
186851
|
});
|
|
186718
186852
|
socket.connect(port, host);
|
|
186719
186853
|
});
|
|
186720
186854
|
}
|
|
186721
186855
|
async function stopProcess3(proc) {
|
|
186722
|
-
return new Promise((
|
|
186856
|
+
return new Promise((resolve5) => {
|
|
186723
186857
|
if (proc.killed || proc.exitCode !== null) {
|
|
186724
|
-
|
|
186858
|
+
resolve5();
|
|
186725
186859
|
return;
|
|
186726
186860
|
}
|
|
186727
186861
|
proc.once("exit", () => {
|
|
186728
186862
|
clearTimeout(forceKillTimeout);
|
|
186729
|
-
|
|
186863
|
+
resolve5();
|
|
186730
186864
|
});
|
|
186731
186865
|
proc.kill("SIGTERM");
|
|
186732
186866
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186737,7 +186871,7 @@ async function stopProcess3(proc) {
|
|
|
186737
186871
|
});
|
|
186738
186872
|
}
|
|
186739
186873
|
function sleep3(ms) {
|
|
186740
|
-
return new Promise((
|
|
186874
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
186741
186875
|
}
|
|
186742
186876
|
|
|
186743
186877
|
// src/lib/dev/sync-detector.ts
|
|
@@ -186928,19 +187062,345 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
186928
187062
|
};
|
|
186929
187063
|
}
|
|
186930
187064
|
|
|
187065
|
+
// src/lib/dev/proxy-registry.ts
|
|
187066
|
+
import * as fs11 from "fs";
|
|
187067
|
+
import * as path12 from "path";
|
|
187068
|
+
import * as os4 from "os";
|
|
187069
|
+
var ProxyRegistryManager = class {
|
|
187070
|
+
proxyDir;
|
|
187071
|
+
ownerPath;
|
|
187072
|
+
registryPath;
|
|
187073
|
+
lockPath;
|
|
187074
|
+
isOwner = false;
|
|
187075
|
+
registryWatcher = null;
|
|
187076
|
+
constructor() {
|
|
187077
|
+
this.proxyDir = path12.join(os4.homedir(), ".specific", "proxy");
|
|
187078
|
+
this.ownerPath = path12.join(this.proxyDir, "owner.json");
|
|
187079
|
+
this.registryPath = path12.join(this.proxyDir, "registry.json");
|
|
187080
|
+
this.lockPath = path12.join(this.proxyDir, "registry.lock");
|
|
187081
|
+
}
|
|
187082
|
+
ensureProxyDir() {
|
|
187083
|
+
if (!fs11.existsSync(this.proxyDir)) {
|
|
187084
|
+
fs11.mkdirSync(this.proxyDir, { recursive: true });
|
|
187085
|
+
}
|
|
187086
|
+
}
|
|
187087
|
+
isProcessRunning(pid) {
|
|
187088
|
+
try {
|
|
187089
|
+
process.kill(pid, 0);
|
|
187090
|
+
return true;
|
|
187091
|
+
} catch (e) {
|
|
187092
|
+
const err = e;
|
|
187093
|
+
return err.code !== "ESRCH";
|
|
187094
|
+
}
|
|
187095
|
+
}
|
|
187096
|
+
async acquireLock(timeoutMs = 5e3) {
|
|
187097
|
+
this.ensureProxyDir();
|
|
187098
|
+
const startTime = Date.now();
|
|
187099
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
187100
|
+
try {
|
|
187101
|
+
const fd = fs11.openSync(
|
|
187102
|
+
this.lockPath,
|
|
187103
|
+
fs11.constants.O_CREAT | fs11.constants.O_EXCL | fs11.constants.O_WRONLY
|
|
187104
|
+
);
|
|
187105
|
+
fs11.writeSync(fd, String(process.pid));
|
|
187106
|
+
fs11.closeSync(fd);
|
|
187107
|
+
return () => {
|
|
187108
|
+
try {
|
|
187109
|
+
fs11.unlinkSync(this.lockPath);
|
|
187110
|
+
} catch {
|
|
187111
|
+
}
|
|
187112
|
+
};
|
|
187113
|
+
} catch (e) {
|
|
187114
|
+
const err = e;
|
|
187115
|
+
if (err.code === "EEXIST") {
|
|
187116
|
+
try {
|
|
187117
|
+
const lockPid = parseInt(
|
|
187118
|
+
fs11.readFileSync(this.lockPath, "utf-8").trim(),
|
|
187119
|
+
10
|
|
187120
|
+
);
|
|
187121
|
+
if (!this.isProcessRunning(lockPid)) {
|
|
187122
|
+
fs11.unlinkSync(this.lockPath);
|
|
187123
|
+
continue;
|
|
187124
|
+
}
|
|
187125
|
+
} catch {
|
|
187126
|
+
try {
|
|
187127
|
+
fs11.unlinkSync(this.lockPath);
|
|
187128
|
+
} catch {
|
|
187129
|
+
}
|
|
187130
|
+
continue;
|
|
187131
|
+
}
|
|
187132
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
187133
|
+
} else {
|
|
187134
|
+
throw e;
|
|
187135
|
+
}
|
|
187136
|
+
}
|
|
187137
|
+
}
|
|
187138
|
+
throw new Error("Failed to acquire proxy registry lock (timeout)");
|
|
187139
|
+
}
|
|
187140
|
+
/**
|
|
187141
|
+
* Try to claim ownership of the proxy.
|
|
187142
|
+
* Returns true if this process is now the owner.
|
|
187143
|
+
*/
|
|
187144
|
+
async claimProxyOwnership(key) {
|
|
187145
|
+
const releaseLock = await this.acquireLock();
|
|
187146
|
+
try {
|
|
187147
|
+
if (fs11.existsSync(this.ownerPath)) {
|
|
187148
|
+
const content = fs11.readFileSync(this.ownerPath, "utf-8");
|
|
187149
|
+
const ownerFile2 = JSON.parse(content);
|
|
187150
|
+
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187151
|
+
return false;
|
|
187152
|
+
}
|
|
187153
|
+
}
|
|
187154
|
+
const ownerFile = {
|
|
187155
|
+
version: 1,
|
|
187156
|
+
owner: {
|
|
187157
|
+
pid: process.pid,
|
|
187158
|
+
key,
|
|
187159
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187160
|
+
}
|
|
187161
|
+
};
|
|
187162
|
+
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
187163
|
+
this.isOwner = true;
|
|
187164
|
+
return true;
|
|
187165
|
+
} finally {
|
|
187166
|
+
releaseLock();
|
|
187167
|
+
}
|
|
187168
|
+
}
|
|
187169
|
+
/**
|
|
187170
|
+
* Release ownership of the proxy.
|
|
187171
|
+
*/
|
|
187172
|
+
async releaseProxyOwnership() {
|
|
187173
|
+
if (!this.isOwner) {
|
|
187174
|
+
return;
|
|
187175
|
+
}
|
|
187176
|
+
const releaseLock = await this.acquireLock();
|
|
187177
|
+
try {
|
|
187178
|
+
if (fs11.existsSync(this.ownerPath)) {
|
|
187179
|
+
const content = fs11.readFileSync(this.ownerPath, "utf-8");
|
|
187180
|
+
const ownerFile = JSON.parse(content);
|
|
187181
|
+
if (ownerFile.owner.pid === process.pid) {
|
|
187182
|
+
fs11.unlinkSync(this.ownerPath);
|
|
187183
|
+
}
|
|
187184
|
+
}
|
|
187185
|
+
this.isOwner = false;
|
|
187186
|
+
} finally {
|
|
187187
|
+
releaseLock();
|
|
187188
|
+
}
|
|
187189
|
+
}
|
|
187190
|
+
/**
|
|
187191
|
+
* Get the current proxy owner.
|
|
187192
|
+
*/
|
|
187193
|
+
async getProxyOwner() {
|
|
187194
|
+
if (!fs11.existsSync(this.ownerPath)) {
|
|
187195
|
+
return null;
|
|
187196
|
+
}
|
|
187197
|
+
const releaseLock = await this.acquireLock();
|
|
187198
|
+
try {
|
|
187199
|
+
const content = fs11.readFileSync(this.ownerPath, "utf-8");
|
|
187200
|
+
const ownerFile = JSON.parse(content);
|
|
187201
|
+
if (!this.isProcessRunning(ownerFile.owner.pid)) {
|
|
187202
|
+
return null;
|
|
187203
|
+
}
|
|
187204
|
+
return ownerFile.owner;
|
|
187205
|
+
} catch {
|
|
187206
|
+
return null;
|
|
187207
|
+
} finally {
|
|
187208
|
+
releaseLock();
|
|
187209
|
+
}
|
|
187210
|
+
}
|
|
187211
|
+
/**
|
|
187212
|
+
* Check if this process is the proxy owner.
|
|
187213
|
+
*/
|
|
187214
|
+
isProxyOwner() {
|
|
187215
|
+
return this.isOwner;
|
|
187216
|
+
}
|
|
187217
|
+
/**
|
|
187218
|
+
* Register services for a key.
|
|
187219
|
+
*/
|
|
187220
|
+
async registerServices(key, adminPort, services, drizzleGatewayPort) {
|
|
187221
|
+
const releaseLock = await this.acquireLock();
|
|
187222
|
+
try {
|
|
187223
|
+
const registry = this.readRegistry();
|
|
187224
|
+
const registration = {
|
|
187225
|
+
adminPort,
|
|
187226
|
+
services
|
|
187227
|
+
};
|
|
187228
|
+
if (drizzleGatewayPort !== void 0) {
|
|
187229
|
+
registration.drizzleGatewayPort = drizzleGatewayPort;
|
|
187230
|
+
}
|
|
187231
|
+
registry.keys[key] = registration;
|
|
187232
|
+
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
187233
|
+
this.writeFileAtomic(this.registryPath, registry);
|
|
187234
|
+
} finally {
|
|
187235
|
+
releaseLock();
|
|
187236
|
+
}
|
|
187237
|
+
}
|
|
187238
|
+
/**
|
|
187239
|
+
* Unregister all services for a key.
|
|
187240
|
+
*/
|
|
187241
|
+
async unregisterServices(key) {
|
|
187242
|
+
const releaseLock = await this.acquireLock();
|
|
187243
|
+
try {
|
|
187244
|
+
const registry = this.readRegistry();
|
|
187245
|
+
delete registry.keys[key];
|
|
187246
|
+
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
187247
|
+
this.writeFileAtomic(this.registryPath, registry);
|
|
187248
|
+
} finally {
|
|
187249
|
+
releaseLock();
|
|
187250
|
+
}
|
|
187251
|
+
}
|
|
187252
|
+
/**
|
|
187253
|
+
* Get all registered services (flattened view for proxy routing).
|
|
187254
|
+
*/
|
|
187255
|
+
async getAllServices() {
|
|
187256
|
+
const releaseLock = await this.acquireLock();
|
|
187257
|
+
try {
|
|
187258
|
+
const registry = this.readRegistry();
|
|
187259
|
+
const services = [];
|
|
187260
|
+
for (const [key, registration] of Object.entries(registry.keys)) {
|
|
187261
|
+
for (const svc of registration.services) {
|
|
187262
|
+
services.push({
|
|
187263
|
+
key,
|
|
187264
|
+
serviceName: svc.serviceName,
|
|
187265
|
+
port: svc.port
|
|
187266
|
+
});
|
|
187267
|
+
}
|
|
187268
|
+
}
|
|
187269
|
+
return services;
|
|
187270
|
+
} finally {
|
|
187271
|
+
releaseLock();
|
|
187272
|
+
}
|
|
187273
|
+
}
|
|
187274
|
+
/**
|
|
187275
|
+
* Get all registered keys and their admin ports.
|
|
187276
|
+
*/
|
|
187277
|
+
async getAllKeyRegistrations() {
|
|
187278
|
+
const releaseLock = await this.acquireLock();
|
|
187279
|
+
try {
|
|
187280
|
+
const registry = this.readRegistry();
|
|
187281
|
+
return { ...registry.keys };
|
|
187282
|
+
} finally {
|
|
187283
|
+
releaseLock();
|
|
187284
|
+
}
|
|
187285
|
+
}
|
|
187286
|
+
/**
|
|
187287
|
+
* Watch the registry for changes. Returns a cleanup function.
|
|
187288
|
+
* Callback receives both the flattened services list and all key registrations (for admin ports).
|
|
187289
|
+
* Uses chokidar instead of fs.watch for reliable detection of atomic file writes on macOS.
|
|
187290
|
+
*/
|
|
187291
|
+
watchRegistry(onChange) {
|
|
187292
|
+
this.ensureProxyDir();
|
|
187293
|
+
if (!fs11.existsSync(this.registryPath)) {
|
|
187294
|
+
const emptyRegistry = {
|
|
187295
|
+
version: 1,
|
|
187296
|
+
keys: {},
|
|
187297
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187298
|
+
};
|
|
187299
|
+
this.writeFileAtomic(this.registryPath, emptyRegistry);
|
|
187300
|
+
}
|
|
187301
|
+
let debounceTimer = null;
|
|
187302
|
+
this.registryWatcher = chokidar_default.watch(this.registryPath, {
|
|
187303
|
+
persistent: true,
|
|
187304
|
+
awaitWriteFinish: {
|
|
187305
|
+
stabilityThreshold: 100,
|
|
187306
|
+
pollInterval: 50
|
|
187307
|
+
}
|
|
187308
|
+
});
|
|
187309
|
+
this.registryWatcher.on("change", async () => {
|
|
187310
|
+
if (debounceTimer) {
|
|
187311
|
+
clearTimeout(debounceTimer);
|
|
187312
|
+
}
|
|
187313
|
+
debounceTimer = setTimeout(async () => {
|
|
187314
|
+
try {
|
|
187315
|
+
const services = await this.getAllServices();
|
|
187316
|
+
const keys = await this.getAllKeyRegistrations();
|
|
187317
|
+
onChange(services, keys);
|
|
187318
|
+
} catch {
|
|
187319
|
+
}
|
|
187320
|
+
}, 50);
|
|
187321
|
+
});
|
|
187322
|
+
return () => {
|
|
187323
|
+
if (debounceTimer) {
|
|
187324
|
+
clearTimeout(debounceTimer);
|
|
187325
|
+
}
|
|
187326
|
+
this.registryWatcher?.close();
|
|
187327
|
+
this.registryWatcher = null;
|
|
187328
|
+
};
|
|
187329
|
+
}
|
|
187330
|
+
/**
|
|
187331
|
+
* Attempt to become the proxy owner (election).
|
|
187332
|
+
* This is called when the current owner is detected as dead.
|
|
187333
|
+
*/
|
|
187334
|
+
async attemptElection(key) {
|
|
187335
|
+
const releaseLock = await this.acquireLock();
|
|
187336
|
+
try {
|
|
187337
|
+
if (fs11.existsSync(this.ownerPath)) {
|
|
187338
|
+
const content = fs11.readFileSync(this.ownerPath, "utf-8");
|
|
187339
|
+
const ownerFile2 = JSON.parse(content);
|
|
187340
|
+
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187341
|
+
return false;
|
|
187342
|
+
}
|
|
187343
|
+
fs11.unlinkSync(this.ownerPath);
|
|
187344
|
+
}
|
|
187345
|
+
const ownerFile = {
|
|
187346
|
+
version: 1,
|
|
187347
|
+
owner: {
|
|
187348
|
+
pid: process.pid,
|
|
187349
|
+
key,
|
|
187350
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187351
|
+
}
|
|
187352
|
+
};
|
|
187353
|
+
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
187354
|
+
this.isOwner = true;
|
|
187355
|
+
return true;
|
|
187356
|
+
} finally {
|
|
187357
|
+
releaseLock();
|
|
187358
|
+
}
|
|
187359
|
+
}
|
|
187360
|
+
readRegistry() {
|
|
187361
|
+
if (!fs11.existsSync(this.registryPath)) {
|
|
187362
|
+
return {
|
|
187363
|
+
version: 1,
|
|
187364
|
+
keys: {},
|
|
187365
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187366
|
+
};
|
|
187367
|
+
}
|
|
187368
|
+
try {
|
|
187369
|
+
const content = fs11.readFileSync(this.registryPath, "utf-8");
|
|
187370
|
+
return JSON.parse(content);
|
|
187371
|
+
} catch {
|
|
187372
|
+
return {
|
|
187373
|
+
version: 1,
|
|
187374
|
+
keys: {},
|
|
187375
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187376
|
+
};
|
|
187377
|
+
}
|
|
187378
|
+
}
|
|
187379
|
+
writeFileAtomic(filePath, data) {
|
|
187380
|
+
this.ensureProxyDir();
|
|
187381
|
+
const tmpPath = filePath + ".tmp";
|
|
187382
|
+
fs11.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
187383
|
+
fs11.renameSync(tmpPath, filePath);
|
|
187384
|
+
}
|
|
187385
|
+
};
|
|
187386
|
+
|
|
186931
187387
|
// src/commands/dev.tsx
|
|
186932
187388
|
var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
|
|
186933
|
-
function DevUI() {
|
|
187389
|
+
function DevUI({ instanceKey }) {
|
|
186934
187390
|
const { exit } = useApp2();
|
|
186935
|
-
const [state, setState] = useState3(() =>
|
|
186936
|
-
|
|
186937
|
-
|
|
186938
|
-
|
|
186939
|
-
|
|
186940
|
-
|
|
186941
|
-
|
|
186942
|
-
|
|
186943
|
-
|
|
187391
|
+
const [state, setState] = useState3(() => {
|
|
187392
|
+
const caExists = caFilesExist();
|
|
187393
|
+
return {
|
|
187394
|
+
status: caExists ? "loading" : "installing-ca",
|
|
187395
|
+
...caExists ? {} : { caInstallPhase: "installing" },
|
|
187396
|
+
resources: /* @__PURE__ */ new Map(),
|
|
187397
|
+
resourceStatus: /* @__PURE__ */ new Map(),
|
|
187398
|
+
services: [],
|
|
187399
|
+
output: [],
|
|
187400
|
+
colorMap: /* @__PURE__ */ new Map(),
|
|
187401
|
+
isProxyOwner: false
|
|
187402
|
+
};
|
|
187403
|
+
});
|
|
186944
187404
|
useEffect3(() => {
|
|
186945
187405
|
if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
|
|
186946
187406
|
installCA();
|
|
@@ -186964,9 +187424,13 @@ function DevUI() {
|
|
|
186964
187424
|
}
|
|
186965
187425
|
const shuttingDown = useRef(false);
|
|
186966
187426
|
const stateManagerRef = useRef(null);
|
|
187427
|
+
const proxyRegistryRef = useRef(null);
|
|
186967
187428
|
const electricInstancesRef = useRef([]);
|
|
186968
187429
|
const drizzleGatewayRef = useRef(null);
|
|
187430
|
+
const registryWatcherCleanupRef = useRef(null);
|
|
187431
|
+
const electionIntervalRef = useRef(null);
|
|
186969
187432
|
const proxyRef = useRef(null);
|
|
187433
|
+
const adminServerRef = useRef(null);
|
|
186970
187434
|
const servicesRef = useRef([]);
|
|
186971
187435
|
const resourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
186972
187436
|
const [reloadTrigger, setReloadTrigger] = useState3(0);
|
|
@@ -186976,9 +187440,19 @@ function DevUI() {
|
|
|
186976
187440
|
shuttingDown.current = true;
|
|
186977
187441
|
writeLog("system", "Shutting down");
|
|
186978
187442
|
setState((s) => ({ ...s, status: "stopping" }));
|
|
187443
|
+
if (electionIntervalRef.current) {
|
|
187444
|
+
clearInterval(electionIntervalRef.current);
|
|
187445
|
+
electionIntervalRef.current = null;
|
|
187446
|
+
}
|
|
187447
|
+
if (registryWatcherCleanupRef.current) {
|
|
187448
|
+
registryWatcherCleanupRef.current();
|
|
187449
|
+
registryWatcherCleanupRef.current = null;
|
|
187450
|
+
}
|
|
186979
187451
|
await Promise.all([
|
|
186980
187452
|
// Stop proxy
|
|
186981
187453
|
proxyRef.current?.stop(),
|
|
187454
|
+
// Stop admin server
|
|
187455
|
+
adminServerRef.current?.stop(),
|
|
186982
187456
|
// Stop all services
|
|
186983
187457
|
...servicesRef.current.map((service) => service.stop()),
|
|
186984
187458
|
// Stop all Electric instances
|
|
@@ -186988,6 +187462,10 @@ function DevUI() {
|
|
|
186988
187462
|
// Stop all resources
|
|
186989
187463
|
...[...resourcesRef.current.values()].map((resource) => resource.stop())
|
|
186990
187464
|
]);
|
|
187465
|
+
if (proxyRegistryRef.current) {
|
|
187466
|
+
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
187467
|
+
await proxyRegistryRef.current.releaseProxyOwnership();
|
|
187468
|
+
}
|
|
186991
187469
|
if (stateManagerRef.current) {
|
|
186992
187470
|
await stateManagerRef.current.releaseOwnership();
|
|
186993
187471
|
}
|
|
@@ -186999,9 +187477,19 @@ function DevUI() {
|
|
|
186999
187477
|
if (shuttingDown.current || state.status === "reloading") return;
|
|
187000
187478
|
writeLog("system", "Reloading (config changed)");
|
|
187001
187479
|
setState((s) => ({ ...s, status: "reloading", parseError: void 0 }));
|
|
187480
|
+
if (electionIntervalRef.current) {
|
|
187481
|
+
clearInterval(electionIntervalRef.current);
|
|
187482
|
+
electionIntervalRef.current = null;
|
|
187483
|
+
}
|
|
187484
|
+
if (registryWatcherCleanupRef.current) {
|
|
187485
|
+
registryWatcherCleanupRef.current();
|
|
187486
|
+
registryWatcherCleanupRef.current = null;
|
|
187487
|
+
}
|
|
187002
187488
|
await Promise.all([
|
|
187003
187489
|
// Stop proxy
|
|
187004
187490
|
proxyRef.current?.stop(),
|
|
187491
|
+
// Stop admin server
|
|
187492
|
+
adminServerRef.current?.stop(),
|
|
187005
187493
|
// Stop all services
|
|
187006
187494
|
...servicesRef.current.map((service) => service.stop()),
|
|
187007
187495
|
// Stop all Electric instances
|
|
@@ -187014,8 +187502,13 @@ function DevUI() {
|
|
|
187014
187502
|
electricInstancesRef.current = [];
|
|
187015
187503
|
drizzleGatewayRef.current = null;
|
|
187016
187504
|
proxyRef.current = null;
|
|
187505
|
+
adminServerRef.current = null;
|
|
187017
187506
|
servicesRef.current = [];
|
|
187018
187507
|
resourcesRef.current = /* @__PURE__ */ new Map();
|
|
187508
|
+
if (proxyRegistryRef.current) {
|
|
187509
|
+
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
187510
|
+
await proxyRegistryRef.current.releaseProxyOwnership();
|
|
187511
|
+
}
|
|
187019
187512
|
if (stateManagerRef.current) {
|
|
187020
187513
|
await stateManagerRef.current.releaseOwnership();
|
|
187021
187514
|
}
|
|
@@ -187026,7 +187519,8 @@ function DevUI() {
|
|
|
187026
187519
|
resources: /* @__PURE__ */ new Map(),
|
|
187027
187520
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
187028
187521
|
services: [],
|
|
187029
|
-
proxy: void 0
|
|
187522
|
+
proxy: void 0,
|
|
187523
|
+
isProxyOwner: false
|
|
187030
187524
|
}));
|
|
187031
187525
|
setReloadTrigger((t) => t + 1);
|
|
187032
187526
|
};
|
|
@@ -187061,10 +187555,10 @@ function DevUI() {
|
|
|
187061
187555
|
}, []);
|
|
187062
187556
|
useEffect3(() => {
|
|
187063
187557
|
if (state.status !== "running") return;
|
|
187064
|
-
const configPath =
|
|
187558
|
+
const configPath = path13.join(process.cwd(), "specific.hcl");
|
|
187065
187559
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
187066
187560
|
try {
|
|
187067
|
-
const hcl =
|
|
187561
|
+
const hcl = fs12.readFileSync(configPath, "utf-8");
|
|
187068
187562
|
parseConfig(hcl).then(() => {
|
|
187069
187563
|
triggerReload();
|
|
187070
187564
|
}).catch((err) => {
|
|
@@ -187104,8 +187598,10 @@ function DevUI() {
|
|
|
187104
187598
|
let startedDrizzleGateway = null;
|
|
187105
187599
|
const startedServices = [];
|
|
187106
187600
|
let startedProxy = null;
|
|
187107
|
-
const stateManager = new InstanceStateManager(process.cwd());
|
|
187601
|
+
const stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
187108
187602
|
stateManagerRef.current = stateManager;
|
|
187603
|
+
const proxyRegistry = new ProxyRegistryManager();
|
|
187604
|
+
proxyRegistryRef.current = proxyRegistry;
|
|
187109
187605
|
async function start() {
|
|
187110
187606
|
writeLog("system", "Starting dev server");
|
|
187111
187607
|
await stateManager.cleanStaleState();
|
|
@@ -187128,8 +187624,8 @@ function DevUI() {
|
|
|
187128
187624
|
}));
|
|
187129
187625
|
return;
|
|
187130
187626
|
}
|
|
187131
|
-
const configPath =
|
|
187132
|
-
if (!
|
|
187627
|
+
const configPath = path13.join(process.cwd(), "specific.hcl");
|
|
187628
|
+
if (!fs12.existsSync(configPath)) {
|
|
187133
187629
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
187134
187630
|
setState((s) => ({
|
|
187135
187631
|
...s,
|
|
@@ -187148,7 +187644,7 @@ function DevUI() {
|
|
|
187148
187644
|
}
|
|
187149
187645
|
let config2;
|
|
187150
187646
|
try {
|
|
187151
|
-
const hcl =
|
|
187647
|
+
const hcl = fs12.readFileSync(configPath, "utf-8");
|
|
187152
187648
|
config2 = await parseConfig(hcl);
|
|
187153
187649
|
} catch (err) {
|
|
187154
187650
|
setState((s) => ({
|
|
@@ -187181,7 +187677,7 @@ function DevUI() {
|
|
|
187181
187677
|
colorIndex++;
|
|
187182
187678
|
}
|
|
187183
187679
|
setState((s) => ({ ...s, status: "starting", config: config2, colorMap }));
|
|
187184
|
-
const portAllocator = new StablePortAllocator(process.cwd());
|
|
187680
|
+
const portAllocator = new StablePortAllocator(process.cwd(), instanceKey);
|
|
187185
187681
|
const resourceStatus = /* @__PURE__ */ new Map();
|
|
187186
187682
|
const syncDatabases = detectSyncDatabases(config2);
|
|
187187
187683
|
let resources2;
|
|
@@ -187190,7 +187686,7 @@ function DevUI() {
|
|
|
187190
187686
|
config: config2,
|
|
187191
187687
|
selection: { mode: "all" },
|
|
187192
187688
|
stateManager,
|
|
187193
|
-
dataDir:
|
|
187689
|
+
dataDir: `.specific/keys/${instanceKey}/data`,
|
|
187194
187690
|
portAllocator,
|
|
187195
187691
|
callbacks: {
|
|
187196
187692
|
onResourceStarting: (name, type) => {
|
|
@@ -187254,7 +187750,7 @@ function DevUI() {
|
|
|
187254
187750
|
const drizzleGateway = await startDrizzleGateway(
|
|
187255
187751
|
postgresResources,
|
|
187256
187752
|
drizzlePort,
|
|
187257
|
-
|
|
187753
|
+
path13.join(process.cwd(), ".specific", "keys", instanceKey)
|
|
187258
187754
|
);
|
|
187259
187755
|
startedDrizzleGateway = drizzleGateway;
|
|
187260
187756
|
drizzleGatewayRef.current = drizzleGateway;
|
|
@@ -187362,6 +187858,7 @@ function DevUI() {
|
|
|
187362
187858
|
}
|
|
187363
187859
|
}
|
|
187364
187860
|
if (cancelled) return;
|
|
187861
|
+
const serviceInfos = [];
|
|
187365
187862
|
const exposedServices = [];
|
|
187366
187863
|
for (const service of config2.services) {
|
|
187367
187864
|
const endpointInfos = serviceEndpoints.get(service.name) || [];
|
|
@@ -187372,6 +187869,10 @@ function DevUI() {
|
|
|
187372
187869
|
if (endpointConfig?.public) {
|
|
187373
187870
|
const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
|
|
187374
187871
|
exposedServices.push({ name: proxyName, port: info.port });
|
|
187872
|
+
serviceInfos.push({
|
|
187873
|
+
serviceName: proxyName,
|
|
187874
|
+
port: info.port
|
|
187875
|
+
});
|
|
187375
187876
|
}
|
|
187376
187877
|
}
|
|
187377
187878
|
}
|
|
@@ -187395,26 +187896,114 @@ function DevUI() {
|
|
|
187395
187896
|
syncEnabled: r.type === "postgres" && syncDatabases.has(name)
|
|
187396
187897
|
}))
|
|
187397
187898
|
});
|
|
187398
|
-
|
|
187399
|
-
|
|
187400
|
-
|
|
187401
|
-
|
|
187402
|
-
|
|
187403
|
-
|
|
187404
|
-
|
|
187405
|
-
|
|
187406
|
-
|
|
187407
|
-
|
|
187408
|
-
|
|
187409
|
-
|
|
187410
|
-
|
|
187411
|
-
|
|
187412
|
-
|
|
187413
|
-
...s
|
|
187414
|
-
|
|
187415
|
-
|
|
187416
|
-
|
|
187417
|
-
|
|
187899
|
+
const adminServer = await startAdminServer(getState);
|
|
187900
|
+
adminServerRef.current = adminServer;
|
|
187901
|
+
writeLog("system", `Admin API server started on port ${adminServer.port}`);
|
|
187902
|
+
await proxyRegistry.registerServices(
|
|
187903
|
+
instanceKey,
|
|
187904
|
+
adminServer.port,
|
|
187905
|
+
serviceInfos,
|
|
187906
|
+
startedDrizzleGateway?.port
|
|
187907
|
+
);
|
|
187908
|
+
writeLog("system", `Registered ${serviceInfos.length} services with proxy registry`);
|
|
187909
|
+
const becameProxyOwner = await proxyRegistry.claimProxyOwnership(instanceKey);
|
|
187910
|
+
if (becameProxyOwner) {
|
|
187911
|
+
writeLog("system", "Claimed proxy ownership, starting HTTP proxy");
|
|
187912
|
+
try {
|
|
187913
|
+
const currentServices = await proxyRegistry.getAllServices();
|
|
187914
|
+
const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
|
|
187915
|
+
const certificate = generateCertificate("local.spcf.app", registeredKeys);
|
|
187916
|
+
const proxy2 = await startHttpProxy(
|
|
187917
|
+
exposedServices,
|
|
187918
|
+
certificate,
|
|
187919
|
+
getState,
|
|
187920
|
+
instanceKey
|
|
187921
|
+
);
|
|
187922
|
+
startedProxy = proxy2;
|
|
187923
|
+
proxyRef.current = proxy2;
|
|
187924
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
187925
|
+
const knownKeys = new Set(registeredKeys);
|
|
187926
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry(async (updatedServices, updatedKeys) => {
|
|
187927
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
187928
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
187929
|
+
const newKeyNames = Object.keys(updatedKeys).filter((k) => !knownKeys.has(k));
|
|
187930
|
+
if (newKeyNames.length > 0) {
|
|
187931
|
+
writeLog("system", `New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`);
|
|
187932
|
+
for (const key of newKeyNames) {
|
|
187933
|
+
knownKeys.add(key);
|
|
187934
|
+
}
|
|
187935
|
+
const allKeys = [...knownKeys];
|
|
187936
|
+
const newCertificate = generateCertificate("local.spcf.app", allKeys);
|
|
187937
|
+
proxy2.updateCertificate(newCertificate);
|
|
187938
|
+
}
|
|
187939
|
+
});
|
|
187940
|
+
const currentKeys = await proxyRegistry.getAllKeyRegistrations();
|
|
187941
|
+
proxy2.updateServices(currentServices, currentKeys);
|
|
187942
|
+
writeLog("system", `Loaded ${currentServices.length} services from registry`);
|
|
187943
|
+
} catch (err) {
|
|
187944
|
+
const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
|
|
187945
|
+
writeLog("system:error", errorMsg);
|
|
187946
|
+
setState((s) => ({
|
|
187947
|
+
...s,
|
|
187948
|
+
status: "error",
|
|
187949
|
+
error: errorMsg
|
|
187950
|
+
}));
|
|
187951
|
+
return;
|
|
187952
|
+
}
|
|
187953
|
+
} else {
|
|
187954
|
+
writeLog("system", "Another instance owns the proxy, starting election watcher");
|
|
187955
|
+
setState((s) => ({ ...s, isProxyOwner: false }));
|
|
187956
|
+
const isProcessRunning = (pid) => {
|
|
187957
|
+
try {
|
|
187958
|
+
process.kill(pid, 0);
|
|
187959
|
+
return true;
|
|
187960
|
+
} catch {
|
|
187961
|
+
return false;
|
|
187962
|
+
}
|
|
187963
|
+
};
|
|
187964
|
+
electionIntervalRef.current = setInterval(async () => {
|
|
187965
|
+
if (cancelled || shuttingDown.current) {
|
|
187966
|
+
if (electionIntervalRef.current) {
|
|
187967
|
+
clearInterval(electionIntervalRef.current);
|
|
187968
|
+
electionIntervalRef.current = null;
|
|
187969
|
+
}
|
|
187970
|
+
return;
|
|
187971
|
+
}
|
|
187972
|
+
const owner = await proxyRegistry.getProxyOwner();
|
|
187973
|
+
if (!owner || !isProcessRunning(owner.pid)) {
|
|
187974
|
+
writeLog("system", "Proxy owner died, attempting election");
|
|
187975
|
+
const won = await proxyRegistry.attemptElection(instanceKey);
|
|
187976
|
+
if (won) {
|
|
187977
|
+
writeLog("system", "Won election, starting HTTP proxy");
|
|
187978
|
+
if (electionIntervalRef.current) {
|
|
187979
|
+
clearInterval(electionIntervalRef.current);
|
|
187980
|
+
electionIntervalRef.current = null;
|
|
187981
|
+
}
|
|
187982
|
+
try {
|
|
187983
|
+
const electionServices = await proxyRegistry.getAllServices();
|
|
187984
|
+
const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
|
|
187985
|
+
const electionKeyNames = Object.keys(electionKeyRegistrations);
|
|
187986
|
+
const certificate = generateCertificate("local.spcf.app", electionKeyNames);
|
|
187987
|
+
const proxy2 = await startHttpProxy(
|
|
187988
|
+
exposedServices,
|
|
187989
|
+
certificate,
|
|
187990
|
+
getState,
|
|
187991
|
+
instanceKey
|
|
187992
|
+
);
|
|
187993
|
+
startedProxy = proxy2;
|
|
187994
|
+
proxyRef.current = proxy2;
|
|
187995
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
187996
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry((updatedServices, updatedKeys) => {
|
|
187997
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
187998
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
187999
|
+
});
|
|
188000
|
+
proxy2.updateServices(electionServices, electionKeyRegistrations);
|
|
188001
|
+
} catch (err) {
|
|
188002
|
+
writeLog("system:error", `Failed to start proxy after election: ${err}`);
|
|
188003
|
+
}
|
|
188004
|
+
}
|
|
188005
|
+
}
|
|
188006
|
+
}, 1e3);
|
|
187418
188007
|
}
|
|
187419
188008
|
if (cancelled) return;
|
|
187420
188009
|
writeLog("system", "Dev server running");
|
|
@@ -187423,6 +188012,14 @@ function DevUI() {
|
|
|
187423
188012
|
start();
|
|
187424
188013
|
return () => {
|
|
187425
188014
|
cancelled = true;
|
|
188015
|
+
if (electionIntervalRef.current) {
|
|
188016
|
+
clearInterval(electionIntervalRef.current);
|
|
188017
|
+
electionIntervalRef.current = null;
|
|
188018
|
+
}
|
|
188019
|
+
if (registryWatcherCleanupRef.current) {
|
|
188020
|
+
registryWatcherCleanupRef.current();
|
|
188021
|
+
registryWatcherCleanupRef.current = null;
|
|
188022
|
+
}
|
|
187426
188023
|
if (startedProxy) {
|
|
187427
188024
|
startedProxy.stop().catch(() => {
|
|
187428
188025
|
});
|
|
@@ -187443,10 +188040,14 @@ function DevUI() {
|
|
|
187443
188040
|
resource.stop().catch(() => {
|
|
187444
188041
|
});
|
|
187445
188042
|
}
|
|
188043
|
+
proxyRegistry.unregisterServices(instanceKey).catch(() => {
|
|
188044
|
+
});
|
|
188045
|
+
proxyRegistry.releaseProxyOwnership().catch(() => {
|
|
188046
|
+
});
|
|
187446
188047
|
stateManager.releaseOwnership().catch(() => {
|
|
187447
188048
|
});
|
|
187448
188049
|
};
|
|
187449
|
-
}, [reloadTrigger, readyToStart]);
|
|
188050
|
+
}, [reloadTrigger, readyToStart, instanceKey]);
|
|
187450
188051
|
if (state.status === "installing-ca") {
|
|
187451
188052
|
if (state.caInstallPhase === "installing") {
|
|
187452
188053
|
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "TLS Certificate Setup"), /* @__PURE__ */ React3.createElement(Text3, null, " "), /* @__PURE__ */ React3.createElement(Text3, null, "Installing a local certificate authority (CA) to enable HTTPS"), /* @__PURE__ */ React3.createElement(Text3, null, "for local development. The CA is limited to Specific projects."), /* @__PURE__ */ React3.createElement(Text3, null, " "), /* @__PURE__ */ React3.createElement(Text3, null, "Your password is required to add the CA to your system's trust store."), /* @__PURE__ */ React3.createElement(Text3, null, " "));
|
|
@@ -187524,17 +188125,15 @@ function DevUI() {
|
|
|
187524
188125
|
const staticItems = [
|
|
187525
188126
|
{
|
|
187526
188127
|
key: "title",
|
|
187527
|
-
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "Specific dev server"), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " (Ctrl+C to stop)"))
|
|
188128
|
+
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "Specific dev server"), instanceKey !== "default" && /* @__PURE__ */ React3.createElement(Text3, { color: "yellow" }, " [", instanceKey, "]"), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " (Ctrl+C to stop)"))
|
|
187528
188129
|
},
|
|
187529
188130
|
{ key: "space1", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") },
|
|
187530
|
-
// Show admin UI URL
|
|
187531
|
-
|
|
187532
|
-
|
|
187533
|
-
|
|
187534
|
-
|
|
187535
|
-
|
|
187536
|
-
{ key: "admin-space", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") }
|
|
187537
|
-
] : [],
|
|
188131
|
+
// Show admin UI URL
|
|
188132
|
+
{
|
|
188133
|
+
key: "admin",
|
|
188134
|
+
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Admin:"), /* @__PURE__ */ React3.createElement(Text3, null, " "), /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "local.spcf.app"))
|
|
188135
|
+
},
|
|
188136
|
+
{ key: "admin-space", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") },
|
|
187538
188137
|
...services.length > 0 ? [
|
|
187539
188138
|
{ key: "svc-header", content: /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Services:") },
|
|
187540
188139
|
...services.flatMap((svc) => {
|
|
@@ -187553,17 +188152,14 @@ function DevUI() {
|
|
|
187553
188152
|
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
187554
188153
|
return {
|
|
187555
188154
|
key: `svc-${svc.name}-${endpoint.name}`,
|
|
187556
|
-
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, " \u25CF "), /* @__PURE__ */ React3.createElement(Text3, null, displayName), port ? endpoint.public
|
|
188155
|
+
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, " \u25CF "), /* @__PURE__ */ React3.createElement(Text3, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Text3, null, " \u2192 "), /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".local.spcf.app"), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " (localhost:", port, ")") : null)
|
|
187557
188156
|
};
|
|
187558
188157
|
});
|
|
187559
188158
|
}),
|
|
187560
188159
|
{ key: "space2", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") }
|
|
187561
188160
|
] : [],
|
|
187562
188161
|
...config.postgres.length > 0 ? [
|
|
187563
|
-
{
|
|
187564
|
-
key: "pg-header",
|
|
187565
|
-
content: /* @__PURE__ */ React3.createElement(Text3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Postgres:"), drizzleGatewayRef.current && /* @__PURE__ */ React3.createElement(Text3, null, " ", "(admin \u2192 ", /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "http://localhost:", drizzleGatewayRef.current.port), ")"))
|
|
187566
|
-
},
|
|
188162
|
+
{ key: "pg-header", content: /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Postgres:") },
|
|
187567
188163
|
...config.postgres.map((pg) => {
|
|
187568
188164
|
const instance = resources.get(pg.name);
|
|
187569
188165
|
return {
|
|
@@ -187609,8 +188205,47 @@ function DevUI() {
|
|
|
187609
188205
|
];
|
|
187610
188206
|
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React3.createElement(Box3, { key: item.key }, item.content)));
|
|
187611
188207
|
}
|
|
187612
|
-
function devCommand() {
|
|
187613
|
-
render3(/* @__PURE__ */ React3.createElement(DevUI,
|
|
188208
|
+
function devCommand(instanceKey) {
|
|
188209
|
+
render3(/* @__PURE__ */ React3.createElement(DevUI, { instanceKey }));
|
|
188210
|
+
}
|
|
188211
|
+
|
|
188212
|
+
// src/lib/dev/git-worktree.ts
|
|
188213
|
+
import { execSync as execSync2 } from "child_process";
|
|
188214
|
+
import * as path14 from "path";
|
|
188215
|
+
function isInWorktree() {
|
|
188216
|
+
try {
|
|
188217
|
+
const commonDir = execSync2("git rev-parse --git-common-dir", {
|
|
188218
|
+
encoding: "utf-8",
|
|
188219
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188220
|
+
}).trim();
|
|
188221
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
188222
|
+
encoding: "utf-8",
|
|
188223
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188224
|
+
}).trim();
|
|
188225
|
+
const resolvedCommonDir = path14.resolve(commonDir);
|
|
188226
|
+
const resolvedGitDir = path14.resolve(gitDir);
|
|
188227
|
+
return resolvedCommonDir !== resolvedGitDir;
|
|
188228
|
+
} catch {
|
|
188229
|
+
return false;
|
|
188230
|
+
}
|
|
188231
|
+
}
|
|
188232
|
+
function getWorktreeName() {
|
|
188233
|
+
if (!isInWorktree()) {
|
|
188234
|
+
return null;
|
|
188235
|
+
}
|
|
188236
|
+
try {
|
|
188237
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
188238
|
+
encoding: "utf-8",
|
|
188239
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188240
|
+
}).trim();
|
|
188241
|
+
return path14.basename(gitDir);
|
|
188242
|
+
} catch {
|
|
188243
|
+
return null;
|
|
188244
|
+
}
|
|
188245
|
+
}
|
|
188246
|
+
function getDefaultKey() {
|
|
188247
|
+
const worktreeName = getWorktreeName();
|
|
188248
|
+
return worktreeName ?? "default";
|
|
187614
188249
|
}
|
|
187615
188250
|
|
|
187616
188251
|
// src/commands/deploy.tsx
|
|
@@ -187620,30 +188255,30 @@ import Spinner5 from "ink-spinner";
|
|
|
187620
188255
|
|
|
187621
188256
|
// ../../node_modules/open/index.js
|
|
187622
188257
|
import process8 from "node:process";
|
|
187623
|
-
import
|
|
188258
|
+
import path15 from "node:path";
|
|
187624
188259
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
187625
188260
|
import childProcess3 from "node:child_process";
|
|
187626
|
-
import
|
|
188261
|
+
import fs17, { constants as fsConstants2 } from "node:fs/promises";
|
|
187627
188262
|
|
|
187628
188263
|
// ../../node_modules/wsl-utils/index.js
|
|
187629
188264
|
import { promisify as promisify2 } from "node:util";
|
|
187630
188265
|
import childProcess2 from "node:child_process";
|
|
187631
|
-
import
|
|
188266
|
+
import fs16, { constants as fsConstants } from "node:fs/promises";
|
|
187632
188267
|
|
|
187633
188268
|
// ../../node_modules/is-wsl/index.js
|
|
187634
188269
|
import process2 from "node:process";
|
|
187635
|
-
import
|
|
187636
|
-
import
|
|
188270
|
+
import os5 from "node:os";
|
|
188271
|
+
import fs15 from "node:fs";
|
|
187637
188272
|
|
|
187638
188273
|
// ../../node_modules/is-inside-container/index.js
|
|
187639
|
-
import
|
|
188274
|
+
import fs14 from "node:fs";
|
|
187640
188275
|
|
|
187641
188276
|
// ../../node_modules/is-docker/index.js
|
|
187642
|
-
import
|
|
188277
|
+
import fs13 from "node:fs";
|
|
187643
188278
|
var isDockerCached;
|
|
187644
188279
|
function hasDockerEnv() {
|
|
187645
188280
|
try {
|
|
187646
|
-
|
|
188281
|
+
fs13.statSync("/.dockerenv");
|
|
187647
188282
|
return true;
|
|
187648
188283
|
} catch {
|
|
187649
188284
|
return false;
|
|
@@ -187651,7 +188286,7 @@ function hasDockerEnv() {
|
|
|
187651
188286
|
}
|
|
187652
188287
|
function hasDockerCGroup() {
|
|
187653
188288
|
try {
|
|
187654
|
-
return
|
|
188289
|
+
return fs13.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
187655
188290
|
} catch {
|
|
187656
188291
|
return false;
|
|
187657
188292
|
}
|
|
@@ -187667,7 +188302,7 @@ function isDocker() {
|
|
|
187667
188302
|
var cachedResult;
|
|
187668
188303
|
var hasContainerEnv = () => {
|
|
187669
188304
|
try {
|
|
187670
|
-
|
|
188305
|
+
fs14.statSync("/run/.containerenv");
|
|
187671
188306
|
return true;
|
|
187672
188307
|
} catch {
|
|
187673
188308
|
return false;
|
|
@@ -187685,14 +188320,14 @@ var isWsl = () => {
|
|
|
187685
188320
|
if (process2.platform !== "linux") {
|
|
187686
188321
|
return false;
|
|
187687
188322
|
}
|
|
187688
|
-
if (
|
|
188323
|
+
if (os5.release().toLowerCase().includes("microsoft")) {
|
|
187689
188324
|
if (isInsideContainer()) {
|
|
187690
188325
|
return false;
|
|
187691
188326
|
}
|
|
187692
188327
|
return true;
|
|
187693
188328
|
}
|
|
187694
188329
|
try {
|
|
187695
|
-
return
|
|
188330
|
+
return fs15.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
187696
188331
|
} catch {
|
|
187697
188332
|
return false;
|
|
187698
188333
|
}
|
|
@@ -187760,14 +188395,14 @@ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
|
|
|
187760
188395
|
const configFilePath = "/etc/wsl.conf";
|
|
187761
188396
|
let isConfigFileExists = false;
|
|
187762
188397
|
try {
|
|
187763
|
-
await
|
|
188398
|
+
await fs16.access(configFilePath, fsConstants.F_OK);
|
|
187764
188399
|
isConfigFileExists = true;
|
|
187765
188400
|
} catch {
|
|
187766
188401
|
}
|
|
187767
188402
|
if (!isConfigFileExists) {
|
|
187768
188403
|
return defaultMountPoint;
|
|
187769
188404
|
}
|
|
187770
|
-
const configContent = await
|
|
188405
|
+
const configContent = await fs16.readFile(configFilePath, { encoding: "utf8" });
|
|
187771
188406
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
187772
188407
|
if (parsedMountPoint === void 0) {
|
|
187773
188408
|
return defaultMountPoint;
|
|
@@ -187787,7 +188422,7 @@ var canAccessPowerShell = async () => {
|
|
|
187787
188422
|
canAccessPowerShellPromise ??= (async () => {
|
|
187788
188423
|
try {
|
|
187789
188424
|
const psPath = await powerShellPath2();
|
|
187790
|
-
await
|
|
188425
|
+
await fs16.access(psPath, fsConstants.X_OK);
|
|
187791
188426
|
return true;
|
|
187792
188427
|
} catch {
|
|
187793
188428
|
return false;
|
|
@@ -187801,15 +188436,15 @@ var wslDefaultBrowser = async () => {
|
|
|
187801
188436
|
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
187802
188437
|
return stdout.trim();
|
|
187803
188438
|
};
|
|
187804
|
-
var convertWslPathToWindows = async (
|
|
187805
|
-
if (/^[a-z]+:\/\//i.test(
|
|
187806
|
-
return
|
|
188439
|
+
var convertWslPathToWindows = async (path22) => {
|
|
188440
|
+
if (/^[a-z]+:\/\//i.test(path22)) {
|
|
188441
|
+
return path22;
|
|
187807
188442
|
}
|
|
187808
188443
|
try {
|
|
187809
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
188444
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path22], { encoding: "utf8" });
|
|
187810
188445
|
return stdout.trim();
|
|
187811
188446
|
} catch {
|
|
187812
|
-
return
|
|
188447
|
+
return path22;
|
|
187813
188448
|
}
|
|
187814
188449
|
};
|
|
187815
188450
|
|
|
@@ -187951,8 +188586,8 @@ var is_in_ssh_default = isInSsh;
|
|
|
187951
188586
|
|
|
187952
188587
|
// ../../node_modules/open/index.js
|
|
187953
188588
|
var fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
187954
|
-
var __dirname3 = import.meta.url ?
|
|
187955
|
-
var localXdgOpenPath =
|
|
188589
|
+
var __dirname3 = import.meta.url ? path15.dirname(fileURLToPath3(import.meta.url)) : "";
|
|
188590
|
+
var localXdgOpenPath = path15.join(__dirname3, "xdg-open");
|
|
187956
188591
|
var { platform: platform4, arch: arch2 } = process8;
|
|
187957
188592
|
var tryEachApp = async (apps2, opener) => {
|
|
187958
188593
|
if (apps2.length === 0) {
|
|
@@ -188100,7 +188735,7 @@ var baseOpen = async (options2) => {
|
|
|
188100
188735
|
const isBundled = !__dirname3 || __dirname3 === "/";
|
|
188101
188736
|
let exeLocalXdgOpen = false;
|
|
188102
188737
|
try {
|
|
188103
|
-
await
|
|
188738
|
+
await fs17.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
188104
188739
|
exeLocalXdgOpen = true;
|
|
188105
188740
|
} catch {
|
|
188106
188741
|
}
|
|
@@ -188123,19 +188758,19 @@ var baseOpen = async (options2) => {
|
|
|
188123
188758
|
}
|
|
188124
188759
|
const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
|
|
188125
188760
|
if (options2.wait) {
|
|
188126
|
-
return new Promise((
|
|
188761
|
+
return new Promise((resolve5, reject) => {
|
|
188127
188762
|
subprocess.once("error", reject);
|
|
188128
188763
|
subprocess.once("close", (exitCode) => {
|
|
188129
188764
|
if (!options2.allowNonzeroExitCode && exitCode !== 0) {
|
|
188130
188765
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
188131
188766
|
return;
|
|
188132
188767
|
}
|
|
188133
|
-
|
|
188768
|
+
resolve5(subprocess);
|
|
188134
188769
|
});
|
|
188135
188770
|
});
|
|
188136
188771
|
}
|
|
188137
188772
|
if (isFallbackAttempt) {
|
|
188138
|
-
return new Promise((
|
|
188773
|
+
return new Promise((resolve5, reject) => {
|
|
188139
188774
|
subprocess.once("error", reject);
|
|
188140
188775
|
subprocess.once("spawn", () => {
|
|
188141
188776
|
subprocess.once("close", (exitCode) => {
|
|
@@ -188145,17 +188780,17 @@ var baseOpen = async (options2) => {
|
|
|
188145
188780
|
return;
|
|
188146
188781
|
}
|
|
188147
188782
|
subprocess.unref();
|
|
188148
|
-
|
|
188783
|
+
resolve5(subprocess);
|
|
188149
188784
|
});
|
|
188150
188785
|
});
|
|
188151
188786
|
});
|
|
188152
188787
|
}
|
|
188153
188788
|
subprocess.unref();
|
|
188154
|
-
return new Promise((
|
|
188789
|
+
return new Promise((resolve5, reject) => {
|
|
188155
188790
|
subprocess.once("error", reject);
|
|
188156
188791
|
subprocess.once("spawn", () => {
|
|
188157
188792
|
subprocess.off("error", reject);
|
|
188158
|
-
|
|
188793
|
+
resolve5(subprocess);
|
|
188159
188794
|
});
|
|
188160
188795
|
});
|
|
188161
188796
|
};
|
|
@@ -188232,35 +188867,35 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
|
188232
188867
|
var open_default = open2;
|
|
188233
188868
|
|
|
188234
188869
|
// src/commands/deploy.tsx
|
|
188235
|
-
import * as
|
|
188236
|
-
import * as
|
|
188870
|
+
import * as fs21 from "fs";
|
|
188871
|
+
import * as path19 from "path";
|
|
188237
188872
|
|
|
188238
188873
|
// src/lib/deploy/build-tester.ts
|
|
188239
188874
|
import { spawn as spawn5 } from "child_process";
|
|
188240
|
-
import { existsSync as
|
|
188241
|
-
import { join as
|
|
188875
|
+
import { existsSync as existsSync15 } from "fs";
|
|
188876
|
+
import { join as join16 } from "path";
|
|
188242
188877
|
function getDependencyInstallCommand(build, projectDir) {
|
|
188243
188878
|
switch (build.base) {
|
|
188244
188879
|
case "node":
|
|
188245
|
-
if (
|
|
188880
|
+
if (existsSync15(join16(projectDir, "pnpm-lock.yaml"))) {
|
|
188246
188881
|
return "pnpm install --frozen-lockfile";
|
|
188247
|
-
} else if (
|
|
188882
|
+
} else if (existsSync15(join16(projectDir, "yarn.lock"))) {
|
|
188248
188883
|
return "yarn install --frozen-lockfile";
|
|
188249
|
-
} else if (
|
|
188884
|
+
} else if (existsSync15(join16(projectDir, "package-lock.json"))) {
|
|
188250
188885
|
return "npm ci";
|
|
188251
188886
|
} else {
|
|
188252
188887
|
return "npm install";
|
|
188253
188888
|
}
|
|
188254
188889
|
case "python":
|
|
188255
|
-
if (
|
|
188890
|
+
if (existsSync15(join16(projectDir, "poetry.lock"))) {
|
|
188256
188891
|
return "poetry install --no-interaction";
|
|
188257
|
-
} else if (
|
|
188892
|
+
} else if (existsSync15(join16(projectDir, "Pipfile.lock"))) {
|
|
188258
188893
|
return "pipenv install --deploy";
|
|
188259
|
-
} else if (
|
|
188894
|
+
} else if (existsSync15(join16(projectDir, "Pipfile"))) {
|
|
188260
188895
|
return "pipenv install";
|
|
188261
|
-
} else if (
|
|
188896
|
+
} else if (existsSync15(join16(projectDir, "pyproject.toml"))) {
|
|
188262
188897
|
return "pip install .";
|
|
188263
|
-
} else if (
|
|
188898
|
+
} else if (existsSync15(join16(projectDir, "requirements.txt"))) {
|
|
188264
188899
|
return "pip install -r requirements.txt";
|
|
188265
188900
|
}
|
|
188266
188901
|
return null;
|
|
@@ -188274,7 +188909,7 @@ function getDependencyInstallCommand(build, projectDir) {
|
|
|
188274
188909
|
}
|
|
188275
188910
|
}
|
|
188276
188911
|
function runCommand2(command, projectDir, buildName) {
|
|
188277
|
-
return new Promise((
|
|
188912
|
+
return new Promise((resolve5) => {
|
|
188278
188913
|
const stdout = [];
|
|
188279
188914
|
const stderr = [];
|
|
188280
188915
|
writeLog("build-test", `[${buildName}] Running: ${command}`);
|
|
@@ -188304,7 +188939,7 @@ function runCommand2(command, projectDir, buildName) {
|
|
|
188304
188939
|
});
|
|
188305
188940
|
child.on("error", (err) => {
|
|
188306
188941
|
writeLog("build-test:error", `[${buildName}] Failed to start: ${err.message}`);
|
|
188307
|
-
|
|
188942
|
+
resolve5({
|
|
188308
188943
|
success: false,
|
|
188309
188944
|
output: `Failed to start command: ${err.message}`
|
|
188310
188945
|
});
|
|
@@ -188313,10 +188948,10 @@ function runCommand2(command, projectDir, buildName) {
|
|
|
188313
188948
|
const output = [...stdout, ...stderr].join("");
|
|
188314
188949
|
if (code === 0) {
|
|
188315
188950
|
writeLog("build-test", `[${buildName}] Command succeeded (exit code 0)`);
|
|
188316
|
-
|
|
188951
|
+
resolve5({ success: true, output });
|
|
188317
188952
|
} else {
|
|
188318
188953
|
writeLog("build-test:error", `[${buildName}] Command failed with exit code ${code}`);
|
|
188319
|
-
|
|
188954
|
+
resolve5({
|
|
188320
188955
|
success: false,
|
|
188321
188956
|
output: output || `Exit code: ${code}`
|
|
188322
188957
|
});
|
|
@@ -188655,13 +189290,13 @@ var ApiClient = class {
|
|
|
188655
189290
|
};
|
|
188656
189291
|
|
|
188657
189292
|
// src/lib/tarball/create.ts
|
|
188658
|
-
import { execSync as
|
|
188659
|
-
import * as
|
|
188660
|
-
import * as
|
|
189293
|
+
import { execSync as execSync3 } from "child_process";
|
|
189294
|
+
import * as fs18 from "fs";
|
|
189295
|
+
import * as path16 from "path";
|
|
188661
189296
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
188662
189297
|
function isInsideGitRepository(dir) {
|
|
188663
189298
|
try {
|
|
188664
|
-
const result =
|
|
189299
|
+
const result = execSync3("git rev-parse --is-inside-work-tree", {
|
|
188665
189300
|
cwd: dir,
|
|
188666
189301
|
encoding: "utf-8",
|
|
188667
189302
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -188671,50 +189306,42 @@ function isInsideGitRepository(dir) {
|
|
|
188671
189306
|
return false;
|
|
188672
189307
|
}
|
|
188673
189308
|
}
|
|
188674
|
-
function
|
|
188675
|
-
|
|
188676
|
-
|
|
188677
|
-
|
|
188678
|
-
}).trim();
|
|
188679
|
-
const relativePath = path14.relative(gitRoot, projectDir);
|
|
188680
|
-
return { gitRoot, relativePath: relativePath || "." };
|
|
188681
|
-
}
|
|
188682
|
-
function createGitArchive(projectDir) {
|
|
188683
|
-
writeLog("tarball", "Creating tarball using git archive");
|
|
188684
|
-
const { gitRoot, relativePath } = getGitInfo(projectDir);
|
|
188685
|
-
const isSubdirectory = relativePath !== ".";
|
|
188686
|
-
writeLog(
|
|
188687
|
-
"tarball",
|
|
188688
|
-
isSubdirectory ? `Project is in subdirectory: ${relativePath}` : "Project is at git root"
|
|
188689
|
-
);
|
|
188690
|
-
const stashResult = execSync2("git stash create --include-untracked", {
|
|
188691
|
-
cwd: gitRoot,
|
|
188692
|
-
encoding: "utf-8"
|
|
188693
|
-
}).trim();
|
|
188694
|
-
const treeish = stashResult || "HEAD";
|
|
188695
|
-
writeLog(
|
|
188696
|
-
"tarball",
|
|
188697
|
-
`Archiving from: ${treeish === "HEAD" ? "HEAD (no uncommitted changes)" : "stash (includes uncommitted changes)"}`
|
|
188698
|
-
);
|
|
188699
|
-
const archiveTarget = isSubdirectory ? `${treeish}:${relativePath}` : treeish;
|
|
188700
|
-
const tarball = execSync2(
|
|
188701
|
-
`git archive --format=tar.gz ${archiveTarget} -- . ":!.specific"`,
|
|
189309
|
+
async function createGitArchive(projectDir) {
|
|
189310
|
+
writeLog("tarball", "Creating tarball using git ls-files");
|
|
189311
|
+
const filesOutput = execSync3(
|
|
189312
|
+
"git ls-files --cached --others --exclude-standard",
|
|
188702
189313
|
{
|
|
188703
|
-
cwd:
|
|
189314
|
+
cwd: projectDir,
|
|
189315
|
+
encoding: "utf-8",
|
|
188704
189316
|
maxBuffer: 100 * 1024 * 1024
|
|
188705
189317
|
// 100MB max
|
|
188706
189318
|
}
|
|
188707
189319
|
);
|
|
188708
|
-
|
|
189320
|
+
const allFiles = filesOutput.trim().split("\n").filter(Boolean);
|
|
189321
|
+
const filteredFiles = allFiles.filter(
|
|
189322
|
+
(file) => !file.startsWith(".specific/") && file !== ".specific"
|
|
189323
|
+
);
|
|
189324
|
+
writeLog(
|
|
189325
|
+
"tarball",
|
|
189326
|
+
`Found ${allFiles.length} files, ${filteredFiles.length} after filtering .specific`
|
|
189327
|
+
);
|
|
189328
|
+
const entryGenerator = createEntryItemGenerator(projectDir, filteredFiles);
|
|
189329
|
+
const tarStream = createTarPacker(entryGenerator, "gzip");
|
|
189330
|
+
const chunks = [];
|
|
189331
|
+
for await (const chunk of tarStream) {
|
|
189332
|
+
chunks.push(Buffer.from(chunk));
|
|
189333
|
+
}
|
|
189334
|
+
const tarball = Buffer.concat(chunks);
|
|
189335
|
+
writeLog("tarball", `Tarball created: ${tarball.length} bytes`);
|
|
188709
189336
|
return tarball;
|
|
188710
189337
|
}
|
|
188711
189338
|
var EXCLUDED_DIRS = [".specific"];
|
|
188712
189339
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
188713
189340
|
const results = [];
|
|
188714
|
-
const entries = await
|
|
189341
|
+
const entries = await fs18.promises.readdir(currentDir, { withFileTypes: true });
|
|
188715
189342
|
for (const entry of entries) {
|
|
188716
|
-
const fullPath =
|
|
188717
|
-
const relativePath =
|
|
189343
|
+
const fullPath = path16.join(currentDir, entry.name);
|
|
189344
|
+
const relativePath = path16.relative(baseDir, fullPath);
|
|
188718
189345
|
if (entry.isDirectory()) {
|
|
188719
189346
|
if (!exclude.includes(entry.name)) {
|
|
188720
189347
|
results.push(relativePath);
|
|
@@ -188729,8 +189356,8 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
188729
189356
|
}
|
|
188730
189357
|
async function createTarArchive(projectDir) {
|
|
188731
189358
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
188732
|
-
const configPath =
|
|
188733
|
-
if (!
|
|
189359
|
+
const configPath = path16.join(projectDir, "specific.hcl");
|
|
189360
|
+
if (!fs18.existsSync(configPath)) {
|
|
188734
189361
|
throw new Error("specific.hcl not found in project directory");
|
|
188735
189362
|
}
|
|
188736
189363
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -188753,8 +189380,8 @@ async function createProjectTarball(projectDir) {
|
|
|
188753
189380
|
}
|
|
188754
189381
|
|
|
188755
189382
|
// src/lib/project/config.ts
|
|
188756
|
-
import * as
|
|
188757
|
-
import * as
|
|
189383
|
+
import * as fs19 from "fs";
|
|
189384
|
+
import * as path17 from "path";
|
|
188758
189385
|
var PROJECT_ID_FILE = ".specific/project_id";
|
|
188759
189386
|
var ProjectNotLinkedError = class extends Error {
|
|
188760
189387
|
constructor() {
|
|
@@ -188769,32 +189396,32 @@ Run: specific deploy`
|
|
|
188769
189396
|
}
|
|
188770
189397
|
};
|
|
188771
189398
|
function readProjectId(projectDir = process.cwd()) {
|
|
188772
|
-
const projectIdPath =
|
|
188773
|
-
if (!
|
|
189399
|
+
const projectIdPath = path17.join(projectDir, PROJECT_ID_FILE);
|
|
189400
|
+
if (!fs19.existsSync(projectIdPath)) {
|
|
188774
189401
|
throw new ProjectNotLinkedError();
|
|
188775
189402
|
}
|
|
188776
|
-
const projectId =
|
|
189403
|
+
const projectId = fs19.readFileSync(projectIdPath, "utf-8").trim();
|
|
188777
189404
|
if (!projectId) {
|
|
188778
189405
|
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
188779
189406
|
}
|
|
188780
189407
|
return projectId;
|
|
188781
189408
|
}
|
|
188782
189409
|
function hasProjectId(projectDir = process.cwd()) {
|
|
188783
|
-
const projectIdPath =
|
|
188784
|
-
return
|
|
189410
|
+
const projectIdPath = path17.join(projectDir, PROJECT_ID_FILE);
|
|
189411
|
+
return fs19.existsSync(projectIdPath);
|
|
188785
189412
|
}
|
|
188786
189413
|
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
188787
|
-
const specificDir =
|
|
188788
|
-
if (!
|
|
188789
|
-
|
|
189414
|
+
const specificDir = path17.join(projectDir, ".specific");
|
|
189415
|
+
if (!fs19.existsSync(specificDir)) {
|
|
189416
|
+
fs19.mkdirSync(specificDir, { recursive: true });
|
|
188790
189417
|
}
|
|
188791
|
-
|
|
189418
|
+
fs19.writeFileSync(path17.join(specificDir, "project_id"), projectId + "\n");
|
|
188792
189419
|
}
|
|
188793
189420
|
|
|
188794
189421
|
// src/lib/auth/credentials.ts
|
|
188795
|
-
import * as
|
|
188796
|
-
import * as
|
|
188797
|
-
import * as
|
|
189422
|
+
import * as fs20 from "fs";
|
|
189423
|
+
import * as path18 from "path";
|
|
189424
|
+
import * as os6 from "os";
|
|
188798
189425
|
|
|
188799
189426
|
// src/lib/auth/errors.ts
|
|
188800
189427
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -188871,7 +189498,7 @@ import React4, { useState as useState4, useEffect as useEffect4 } from "react";
|
|
|
188871
189498
|
import { render as render4, Box as Box4, Text as Text4, useApp as useApp3 } from "ink";
|
|
188872
189499
|
import Spinner4 from "ink-spinner";
|
|
188873
189500
|
function performLogin(options2 = {}) {
|
|
188874
|
-
return new Promise((
|
|
189501
|
+
return new Promise((resolve5) => {
|
|
188875
189502
|
const instance = render4(
|
|
188876
189503
|
/* @__PURE__ */ React4.createElement(
|
|
188877
189504
|
LoginFlow,
|
|
@@ -188879,7 +189506,7 @@ function performLogin(options2 = {}) {
|
|
|
188879
189506
|
isReauthentication: options2.isReauthentication,
|
|
188880
189507
|
onComplete: (result) => {
|
|
188881
189508
|
instance.unmount();
|
|
188882
|
-
|
|
189509
|
+
resolve5(result);
|
|
188883
189510
|
}
|
|
188884
189511
|
}
|
|
188885
189512
|
)
|
|
@@ -188985,18 +189612,18 @@ function LoginFlow({ isReauthentication, onComplete }) {
|
|
|
188985
189612
|
|
|
188986
189613
|
// src/lib/auth/credentials.ts
|
|
188987
189614
|
function getUserCredentialsDir() {
|
|
188988
|
-
return
|
|
189615
|
+
return path18.join(os6.homedir(), ".specific");
|
|
188989
189616
|
}
|
|
188990
189617
|
function getCredentialsPath() {
|
|
188991
|
-
return
|
|
189618
|
+
return path18.join(getUserCredentialsDir(), "credentials.json");
|
|
188992
189619
|
}
|
|
188993
189620
|
function readUserCredentials() {
|
|
188994
189621
|
const credentialsPath = getCredentialsPath();
|
|
188995
|
-
if (!
|
|
189622
|
+
if (!fs20.existsSync(credentialsPath)) {
|
|
188996
189623
|
return null;
|
|
188997
189624
|
}
|
|
188998
189625
|
try {
|
|
188999
|
-
const content =
|
|
189626
|
+
const content = fs20.readFileSync(credentialsPath, "utf-8");
|
|
189000
189627
|
return JSON.parse(content);
|
|
189001
189628
|
} catch {
|
|
189002
189629
|
return null;
|
|
@@ -189004,18 +189631,18 @@ function readUserCredentials() {
|
|
|
189004
189631
|
}
|
|
189005
189632
|
function writeUserCredentials(credentials) {
|
|
189006
189633
|
const dir = getUserCredentialsDir();
|
|
189007
|
-
if (!
|
|
189008
|
-
|
|
189634
|
+
if (!fs20.existsSync(dir)) {
|
|
189635
|
+
fs20.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
189009
189636
|
}
|
|
189010
189637
|
const credentialsPath = getCredentialsPath();
|
|
189011
|
-
|
|
189638
|
+
fs20.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
189012
189639
|
mode: 384
|
|
189013
189640
|
});
|
|
189014
189641
|
}
|
|
189015
189642
|
function clearUserCredentials() {
|
|
189016
189643
|
const credentialsPath = getCredentialsPath();
|
|
189017
|
-
if (
|
|
189018
|
-
|
|
189644
|
+
if (fs20.existsSync(credentialsPath)) {
|
|
189645
|
+
fs20.unlinkSync(credentialsPath);
|
|
189019
189646
|
}
|
|
189020
189647
|
}
|
|
189021
189648
|
function isLoggedIn() {
|
|
@@ -189837,14 +190464,14 @@ ${errorMsg}`
|
|
|
189837
190464
|
), phase === "error" && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, "Error: ", error)), phase === "success" && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Text5, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React5.createElement(Text5, { key: name }, " ", name, ": ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, url))))));
|
|
189838
190465
|
}
|
|
189839
190466
|
async function deployCommand(environment, options2) {
|
|
189840
|
-
const configPath =
|
|
189841
|
-
if (!
|
|
190467
|
+
const configPath = path19.join(process.cwd(), "specific.hcl");
|
|
190468
|
+
if (!fs21.existsSync(configPath)) {
|
|
189842
190469
|
console.error("Error: No specific.hcl found in current directory");
|
|
189843
190470
|
process.exit(1);
|
|
189844
190471
|
}
|
|
189845
190472
|
let config;
|
|
189846
190473
|
try {
|
|
189847
|
-
const hcl =
|
|
190474
|
+
const hcl = fs21.readFileSync(configPath, "utf-8");
|
|
189848
190475
|
config = await parseConfig(hcl);
|
|
189849
190476
|
} catch (err) {
|
|
189850
190477
|
console.error(
|
|
@@ -189868,9 +190495,9 @@ async function deployCommand(environment, options2) {
|
|
|
189868
190495
|
|
|
189869
190496
|
// src/commands/exec.tsx
|
|
189870
190497
|
import { spawn as spawn6 } from "child_process";
|
|
189871
|
-
import * as
|
|
189872
|
-
import * as
|
|
189873
|
-
async function execCommand(serviceName, command) {
|
|
190498
|
+
import * as fs22 from "fs";
|
|
190499
|
+
import * as path20 from "path";
|
|
190500
|
+
async function execCommand(serviceName, command, instanceKey = "default") {
|
|
189874
190501
|
if (command.length === 0) {
|
|
189875
190502
|
console.error(
|
|
189876
190503
|
"Error: No command provided. Usage: specific exec <service> -- <command>"
|
|
@@ -189897,14 +190524,14 @@ async function execCommand(serviceName, command) {
|
|
|
189897
190524
|
}
|
|
189898
190525
|
}
|
|
189899
190526
|
};
|
|
189900
|
-
const configPath =
|
|
189901
|
-
if (!
|
|
190527
|
+
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
190528
|
+
if (!fs22.existsSync(configPath)) {
|
|
189902
190529
|
console.error("Error: No specific.hcl found in current directory");
|
|
189903
190530
|
process.exit(1);
|
|
189904
190531
|
}
|
|
189905
190532
|
let config;
|
|
189906
190533
|
try {
|
|
189907
|
-
const hcl =
|
|
190534
|
+
const hcl = fs22.readFileSync(configPath, "utf-8");
|
|
189908
190535
|
config = await parseConfig(hcl);
|
|
189909
190536
|
} catch (err) {
|
|
189910
190537
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -189921,7 +190548,7 @@ async function execCommand(serviceName, command) {
|
|
|
189921
190548
|
const required = findRequiredResources(service);
|
|
189922
190549
|
let resources = /* @__PURE__ */ new Map();
|
|
189923
190550
|
const hasRequiredResources = required.postgres.length > 0 || required.redis.length > 0 || required.storage.length > 0;
|
|
189924
|
-
stateManager = new InstanceStateManager(process.cwd());
|
|
190551
|
+
stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
189925
190552
|
await stateManager.cleanStaleState();
|
|
189926
190553
|
const existingInstances = await stateManager.getExistingInstances();
|
|
189927
190554
|
if (hasRequiredResources) {
|
|
@@ -189976,7 +190603,7 @@ async function execCommand(serviceName, command) {
|
|
|
189976
190603
|
storage: required.storage
|
|
189977
190604
|
},
|
|
189978
190605
|
stateManager,
|
|
189979
|
-
dataDir:
|
|
190606
|
+
dataDir: `.specific/keys/${instanceKey}/data`,
|
|
189980
190607
|
portAllocator: new PortAllocator(),
|
|
189981
190608
|
callbacks: {
|
|
189982
190609
|
log: (msg) => console.error(msg)
|
|
@@ -190041,8 +190668,8 @@ async function execCommand(serviceName, command) {
|
|
|
190041
190668
|
|
|
190042
190669
|
// src/commands/psql.tsx
|
|
190043
190670
|
import { spawn as spawn7 } from "child_process";
|
|
190044
|
-
async function psqlCommand(databaseName) {
|
|
190045
|
-
const stateManager = new InstanceStateManager(process.cwd());
|
|
190671
|
+
async function psqlCommand(databaseName, instanceKey = "default") {
|
|
190672
|
+
const stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
190046
190673
|
await stateManager.cleanStaleState();
|
|
190047
190674
|
const existingInstances = await stateManager.getExistingInstances();
|
|
190048
190675
|
if (!existingInstances) {
|
|
@@ -190098,41 +190725,85 @@ async function psqlCommand(databaseName) {
|
|
|
190098
190725
|
import React6, { useState as useState6, useEffect as useEffect6 } from "react";
|
|
190099
190726
|
import { render as render6, Text as Text6, Box as Box6 } from "ink";
|
|
190100
190727
|
import Spinner6 from "ink-spinner";
|
|
190101
|
-
import * as
|
|
190102
|
-
import * as
|
|
190103
|
-
function CleanUI() {
|
|
190728
|
+
import * as fs23 from "fs";
|
|
190729
|
+
import * as path21 from "path";
|
|
190730
|
+
function CleanUI({ instanceKey }) {
|
|
190104
190731
|
const [state, setState] = useState6({ status: "checking" });
|
|
190105
190732
|
useEffect6(() => {
|
|
190106
190733
|
async function clean() {
|
|
190107
190734
|
const projectRoot = process.cwd();
|
|
190108
|
-
const specificDir =
|
|
190109
|
-
if (!
|
|
190735
|
+
const specificDir = path21.join(projectRoot, ".specific");
|
|
190736
|
+
if (!fs23.existsSync(specificDir)) {
|
|
190110
190737
|
setState({ status: "nothing" });
|
|
190111
190738
|
return;
|
|
190112
190739
|
}
|
|
190113
|
-
|
|
190114
|
-
|
|
190115
|
-
|
|
190116
|
-
|
|
190117
|
-
|
|
190118
|
-
|
|
190119
|
-
|
|
190120
|
-
|
|
190121
|
-
|
|
190122
|
-
|
|
190123
|
-
|
|
190124
|
-
|
|
190125
|
-
|
|
190126
|
-
|
|
190127
|
-
|
|
190128
|
-
|
|
190129
|
-
|
|
190130
|
-
|
|
190131
|
-
|
|
190740
|
+
if (instanceKey) {
|
|
190741
|
+
const keyDir = path21.join(specificDir, "keys", instanceKey);
|
|
190742
|
+
if (!fs23.existsSync(keyDir)) {
|
|
190743
|
+
setState({ status: "nothing" });
|
|
190744
|
+
return;
|
|
190745
|
+
}
|
|
190746
|
+
const stateManager = new InstanceStateManager(projectRoot, instanceKey);
|
|
190747
|
+
const existingInstances = await stateManager.getExistingInstances();
|
|
190748
|
+
if (existingInstances) {
|
|
190749
|
+
setState({
|
|
190750
|
+
status: "error",
|
|
190751
|
+
error: `Cannot clean while 'specific dev' is running for key "${instanceKey}" (PID ${existingInstances.owner.pid}). Please stop it first.`
|
|
190752
|
+
});
|
|
190753
|
+
return;
|
|
190754
|
+
}
|
|
190755
|
+
await stateManager.cleanStaleState();
|
|
190756
|
+
setState({ status: "cleaning" });
|
|
190757
|
+
try {
|
|
190758
|
+
fs23.rmSync(keyDir, { recursive: true, force: true });
|
|
190759
|
+
setState({ status: "success" });
|
|
190760
|
+
} catch (err) {
|
|
190761
|
+
setState({
|
|
190762
|
+
status: "error",
|
|
190763
|
+
error: err instanceof Error ? err.message : String(err)
|
|
190764
|
+
});
|
|
190765
|
+
}
|
|
190766
|
+
} else {
|
|
190767
|
+
const keysDir = path21.join(specificDir, "keys");
|
|
190768
|
+
if (fs23.existsSync(keysDir)) {
|
|
190769
|
+
const keys = fs23.readdirSync(keysDir).filter(
|
|
190770
|
+
(f) => fs23.statSync(path21.join(keysDir, f)).isDirectory()
|
|
190771
|
+
);
|
|
190772
|
+
for (const key of keys) {
|
|
190773
|
+
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
190774
|
+
const existingInstances2 = await stateManager2.getExistingInstances();
|
|
190775
|
+
if (existingInstances2) {
|
|
190776
|
+
setState({
|
|
190777
|
+
status: "error",
|
|
190778
|
+
error: `Cannot clean while 'specific dev' is running for key "${key}" (PID ${existingInstances2.owner.pid}). Please stop it first.`
|
|
190779
|
+
});
|
|
190780
|
+
return;
|
|
190781
|
+
}
|
|
190782
|
+
}
|
|
190783
|
+
}
|
|
190784
|
+
const stateManager = new InstanceStateManager(projectRoot);
|
|
190785
|
+
const existingInstances = await stateManager.getExistingInstances();
|
|
190786
|
+
if (existingInstances) {
|
|
190787
|
+
setState({
|
|
190788
|
+
status: "error",
|
|
190789
|
+
error: `Cannot clean while 'specific dev' is running (PID ${existingInstances.owner.pid}). Please stop it first.`
|
|
190790
|
+
});
|
|
190791
|
+
return;
|
|
190792
|
+
}
|
|
190793
|
+
setState({ status: "cleaning" });
|
|
190794
|
+
try {
|
|
190795
|
+
fs23.rmSync(specificDir, { recursive: true, force: true });
|
|
190796
|
+
setState({ status: "success" });
|
|
190797
|
+
} catch (err) {
|
|
190798
|
+
setState({
|
|
190799
|
+
status: "error",
|
|
190800
|
+
error: err instanceof Error ? err.message : String(err)
|
|
190801
|
+
});
|
|
190802
|
+
}
|
|
190132
190803
|
}
|
|
190133
190804
|
}
|
|
190134
190805
|
clean();
|
|
190135
|
-
}, []);
|
|
190806
|
+
}, [instanceKey]);
|
|
190136
190807
|
if (state.status === "checking") {
|
|
190137
190808
|
return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Checking for running instances..."));
|
|
190138
190809
|
}
|
|
@@ -190143,18 +190814,24 @@ function CleanUI() {
|
|
|
190143
190814
|
return /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error);
|
|
190144
190815
|
}
|
|
190145
190816
|
if (state.status === "nothing") {
|
|
190146
|
-
|
|
190817
|
+
const target2 = instanceKey ? `key "${instanceKey}"` : ".specific directory";
|
|
190818
|
+
return /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
|
|
190147
190819
|
}
|
|
190148
|
-
|
|
190820
|
+
const target = instanceKey ? `key "${instanceKey}"` : ".specific directory";
|
|
190821
|
+
return /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "Cleaned ", target);
|
|
190149
190822
|
}
|
|
190150
|
-
function cleanCommand() {
|
|
190151
|
-
|
|
190823
|
+
function cleanCommand(instanceKey) {
|
|
190824
|
+
if (instanceKey) {
|
|
190825
|
+
render6(/* @__PURE__ */ React6.createElement(CleanUI, { instanceKey }));
|
|
190826
|
+
} else {
|
|
190827
|
+
render6(/* @__PURE__ */ React6.createElement(CleanUI, null));
|
|
190828
|
+
}
|
|
190152
190829
|
}
|
|
190153
190830
|
|
|
190154
190831
|
// src/commands/secrets.tsx
|
|
190155
190832
|
import React7, { useState as useState7, useEffect as useEffect7 } from "react";
|
|
190156
190833
|
import { render as render7, Text as Text7, Box as Box7, useInput as useInput4, useApp as useApp5 } from "ink";
|
|
190157
|
-
import * as
|
|
190834
|
+
import * as fs24 from "fs";
|
|
190158
190835
|
var HEADER_COMMENT = "# Do not commit this file - it contains secrets\n";
|
|
190159
190836
|
function SetSecretUI({ secretName }) {
|
|
190160
190837
|
const { exit } = useApp5();
|
|
@@ -190173,8 +190850,8 @@ function SetSecretUI({ secretName }) {
|
|
|
190173
190850
|
const hclLine = `${secretName} = "${escapedValue}"`;
|
|
190174
190851
|
let content = "";
|
|
190175
190852
|
let hasHeader = false;
|
|
190176
|
-
if (
|
|
190177
|
-
content =
|
|
190853
|
+
if (fs24.existsSync(SECRETS_FILE)) {
|
|
190854
|
+
content = fs24.readFileSync(SECRETS_FILE, "utf-8");
|
|
190178
190855
|
hasHeader = content.startsWith("#");
|
|
190179
190856
|
const lines = content.split("\n");
|
|
190180
190857
|
const newLines = [];
|
|
@@ -190189,7 +190866,7 @@ function SetSecretUI({ secretName }) {
|
|
|
190189
190866
|
}
|
|
190190
190867
|
}
|
|
190191
190868
|
if (found) {
|
|
190192
|
-
|
|
190869
|
+
fs24.writeFileSync(SECRETS_FILE, newLines.join("\n"));
|
|
190193
190870
|
setDone(true);
|
|
190194
190871
|
return;
|
|
190195
190872
|
}
|
|
@@ -190202,7 +190879,7 @@ function SetSecretUI({ secretName }) {
|
|
|
190202
190879
|
}
|
|
190203
190880
|
newContent += `${hclLine}
|
|
190204
190881
|
`;
|
|
190205
|
-
|
|
190882
|
+
fs24.writeFileSync(SECRETS_FILE, newContent);
|
|
190206
190883
|
setDone(true);
|
|
190207
190884
|
} catch (err) {
|
|
190208
190885
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -190262,7 +190939,7 @@ async function secretsCommand(action, secretName) {
|
|
|
190262
190939
|
// src/commands/config.tsx
|
|
190263
190940
|
import React8, { useState as useState8, useEffect as useEffect8 } from "react";
|
|
190264
190941
|
import { render as render8, Text as Text8, Box as Box8, useInput as useInput5, useApp as useApp6 } from "ink";
|
|
190265
|
-
import * as
|
|
190942
|
+
import * as fs25 from "fs";
|
|
190266
190943
|
var HEADER_COMMENT2 = "# Configuration values for this project\n# These values override defaults defined in specific.hcl\n";
|
|
190267
190944
|
function SetConfigUI({ configName, initialValue }) {
|
|
190268
190945
|
const { exit } = useApp6();
|
|
@@ -190281,8 +190958,8 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190281
190958
|
const hclLine = `${configName} = "${escapedValue}"`;
|
|
190282
190959
|
let content = "";
|
|
190283
190960
|
let hasHeader = false;
|
|
190284
|
-
if (
|
|
190285
|
-
content =
|
|
190961
|
+
if (fs25.existsSync(CONFIG_FILE)) {
|
|
190962
|
+
content = fs25.readFileSync(CONFIG_FILE, "utf-8");
|
|
190286
190963
|
hasHeader = content.startsWith("#");
|
|
190287
190964
|
const lines = content.split("\n");
|
|
190288
190965
|
const newLines = [];
|
|
@@ -190297,7 +190974,7 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190297
190974
|
}
|
|
190298
190975
|
}
|
|
190299
190976
|
if (found) {
|
|
190300
|
-
|
|
190977
|
+
fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
|
|
190301
190978
|
setDone(true);
|
|
190302
190979
|
return;
|
|
190303
190980
|
}
|
|
@@ -190310,7 +190987,7 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190310
190987
|
}
|
|
190311
190988
|
newContent += `${hclLine}
|
|
190312
190989
|
`;
|
|
190313
|
-
|
|
190990
|
+
fs25.writeFileSync(CONFIG_FILE, newContent);
|
|
190314
190991
|
setDone(true);
|
|
190315
190992
|
} catch (err) {
|
|
190316
190993
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -190349,8 +191026,8 @@ async function configSetCommand(configName, configValue) {
|
|
|
190349
191026
|
const hclLine = `${configName} = "${escapedValue}"`;
|
|
190350
191027
|
let content = "";
|
|
190351
191028
|
let hasHeader = false;
|
|
190352
|
-
if (
|
|
190353
|
-
content =
|
|
191029
|
+
if (fs25.existsSync(CONFIG_FILE)) {
|
|
191030
|
+
content = fs25.readFileSync(CONFIG_FILE, "utf-8");
|
|
190354
191031
|
hasHeader = content.startsWith("#");
|
|
190355
191032
|
const lines = content.split("\n");
|
|
190356
191033
|
const newLines = [];
|
|
@@ -190365,7 +191042,7 @@ async function configSetCommand(configName, configValue) {
|
|
|
190365
191042
|
}
|
|
190366
191043
|
}
|
|
190367
191044
|
if (found) {
|
|
190368
|
-
|
|
191045
|
+
fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
|
|
190369
191046
|
console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
|
|
190370
191047
|
return;
|
|
190371
191048
|
}
|
|
@@ -190378,7 +191055,7 @@ async function configSetCommand(configName, configValue) {
|
|
|
190378
191055
|
}
|
|
190379
191056
|
newContent += `${hclLine}
|
|
190380
191057
|
`;
|
|
190381
|
-
|
|
191058
|
+
fs25.writeFileSync(CONFIG_FILE, newContent);
|
|
190382
191059
|
console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
|
|
190383
191060
|
} catch (err) {
|
|
190384
191061
|
console.error("Error:", err instanceof Error ? err.message : String(err));
|
|
@@ -190497,20 +191174,29 @@ function logoutCommand() {
|
|
|
190497
191174
|
var program = new Command();
|
|
190498
191175
|
var env = "production";
|
|
190499
191176
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
190500
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
191177
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.38").enablePositionalOptions();
|
|
190501
191178
|
program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
|
|
190502
191179
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
190503
191180
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
|
190504
|
-
program.command("dev").description("Start local development environment").action(
|
|
191181
|
+
program.command("dev").description("Start local development environment").option("-k, --key <key>", "Namespace for isolated dev environment (auto-detected from git worktree if not specified)").action((options2) => {
|
|
191182
|
+
const key = options2.key ?? getDefaultKey();
|
|
191183
|
+
devCommand(key);
|
|
191184
|
+
});
|
|
190505
191185
|
program.command("deploy [environment]").description("Deploy to Specific infrastructure").option("--skip-build-test", "Skip local build testing before deploy").action((environment, options2) => {
|
|
190506
191186
|
deployCommand(environment, options2);
|
|
190507
191187
|
});
|
|
190508
|
-
program.command("exec <service> [args...]").description("Run a one-off command with service environment").passThroughOptions().action(async (service, args) => {
|
|
191188
|
+
program.command("exec <service> [args...]").description("Run a one-off command with service environment").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").passThroughOptions().action(async (service, args, options2) => {
|
|
190509
191189
|
const filteredArgs = args[0] === "--" ? args.slice(1) : args;
|
|
190510
|
-
|
|
191190
|
+
const key = options2.key ?? getDefaultKey();
|
|
191191
|
+
await execCommand(service, filteredArgs, key);
|
|
191192
|
+
});
|
|
191193
|
+
program.command("psql [database]").description("Connect to a running Postgres database").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").action((database, options2) => {
|
|
191194
|
+
const key = options2.key ?? getDefaultKey();
|
|
191195
|
+
psqlCommand(database, key);
|
|
191196
|
+
});
|
|
191197
|
+
program.command("clean").description("Remove .specific directory for a clean slate").option("-k, --key <key>", "Clean only the specified dev environment key").action((options2) => {
|
|
191198
|
+
cleanCommand(options2.key);
|
|
190511
191199
|
});
|
|
190512
|
-
program.command("psql [database]").description("Connect to a running Postgres database").action(psqlCommand);
|
|
190513
|
-
program.command("clean").description("Remove .specific directory for a clean slate").action(cleanCommand);
|
|
190514
191200
|
program.command("secrets [action] [name]").description("Manage secrets").action(secretsCommand);
|
|
190515
191201
|
program.command("config [action] [name] [value]").description("Manage configuration values").action(configCommand);
|
|
190516
191202
|
program.command("login").description("Log in to Specific").action(loginCommand);
|