@specific.dev/cli 0.1.37 → 0.1.39
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 +1527 -719
- package/package.json +3 -2
- 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 → cNL40bpv360RCjyOls7vD}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{0zkv3YeV6IWOWVWE1S1A1 → cNL40bpv360RCjyOls7vD}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{0zkv3YeV6IWOWVWE1S1A1 → cNL40bpv360RCjyOls7vD}/_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) {
|
|
@@ -182236,8 +182236,8 @@ import { Command } from "commander";
|
|
|
182236
182236
|
import React, { useState, useEffect } from "react";
|
|
182237
182237
|
import { render, Text, Box, useInput, useApp } from "ink";
|
|
182238
182238
|
import "ink-spinner";
|
|
182239
|
-
import * as
|
|
182240
|
-
import * as
|
|
182239
|
+
import * as fs3 from "fs";
|
|
182240
|
+
import * as path3 from "path";
|
|
182241
182241
|
|
|
182242
182242
|
// src/lib/dev/local-ca.ts
|
|
182243
182243
|
import * as fs from "fs";
|
|
@@ -182368,40 +182368,143 @@ 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
|
}
|
|
182404
182407
|
|
|
182408
|
+
// src/lib/analytics/index.ts
|
|
182409
|
+
import { PostHog } from "posthog-node";
|
|
182410
|
+
import * as os2 from "os";
|
|
182411
|
+
import * as crypto from "crypto";
|
|
182412
|
+
|
|
182413
|
+
// src/lib/project/config.ts
|
|
182414
|
+
import * as fs2 from "fs";
|
|
182415
|
+
import * as path2 from "path";
|
|
182416
|
+
var PROJECT_ID_FILE = ".specific/project_id";
|
|
182417
|
+
var ProjectNotLinkedError = class extends Error {
|
|
182418
|
+
constructor() {
|
|
182419
|
+
super(
|
|
182420
|
+
`Project not linked to Specific.
|
|
182421
|
+
|
|
182422
|
+
The file ${PROJECT_ID_FILE} does not exist.
|
|
182423
|
+
|
|
182424
|
+
Run: specific deploy`
|
|
182425
|
+
);
|
|
182426
|
+
this.name = "ProjectNotLinkedError";
|
|
182427
|
+
}
|
|
182428
|
+
};
|
|
182429
|
+
function readProjectId(projectDir = process.cwd()) {
|
|
182430
|
+
const projectIdPath = path2.join(projectDir, PROJECT_ID_FILE);
|
|
182431
|
+
if (!fs2.existsSync(projectIdPath)) {
|
|
182432
|
+
throw new ProjectNotLinkedError();
|
|
182433
|
+
}
|
|
182434
|
+
const projectId = fs2.readFileSync(projectIdPath, "utf-8").trim();
|
|
182435
|
+
if (!projectId) {
|
|
182436
|
+
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
182437
|
+
}
|
|
182438
|
+
return projectId;
|
|
182439
|
+
}
|
|
182440
|
+
function hasProjectId(projectDir = process.cwd()) {
|
|
182441
|
+
const projectIdPath = path2.join(projectDir, PROJECT_ID_FILE);
|
|
182442
|
+
return fs2.existsSync(projectIdPath);
|
|
182443
|
+
}
|
|
182444
|
+
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
182445
|
+
const specificDir = path2.join(projectDir, ".specific");
|
|
182446
|
+
if (!fs2.existsSync(specificDir)) {
|
|
182447
|
+
fs2.mkdirSync(specificDir, { recursive: true });
|
|
182448
|
+
}
|
|
182449
|
+
fs2.writeFileSync(path2.join(specificDir, "project_id"), projectId + "\n");
|
|
182450
|
+
}
|
|
182451
|
+
|
|
182452
|
+
// src/lib/analytics/index.ts
|
|
182453
|
+
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
182454
|
+
var client = null;
|
|
182455
|
+
var anonymousId = null;
|
|
182456
|
+
function isEnabled() {
|
|
182457
|
+
return true;
|
|
182458
|
+
}
|
|
182459
|
+
function getAnonymousId() {
|
|
182460
|
+
if (anonymousId) return anonymousId;
|
|
182461
|
+
const machineId = `${os2.hostname()}-${os2.userInfo().username}`;
|
|
182462
|
+
anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
|
|
182463
|
+
return anonymousId;
|
|
182464
|
+
}
|
|
182465
|
+
function getProjectId() {
|
|
182466
|
+
try {
|
|
182467
|
+
if (hasProjectId()) {
|
|
182468
|
+
return readProjectId();
|
|
182469
|
+
}
|
|
182470
|
+
} catch {
|
|
182471
|
+
}
|
|
182472
|
+
return void 0;
|
|
182473
|
+
}
|
|
182474
|
+
function getClient() {
|
|
182475
|
+
if (!isEnabled()) return null;
|
|
182476
|
+
if (!client) {
|
|
182477
|
+
client = new PostHog("phc_qNQCEUXh6ErdciQRRmeG2xlVvwFjkcW6A5bnOFJ8vXZ", {
|
|
182478
|
+
host: POSTHOG_HOST,
|
|
182479
|
+
flushAt: 1,
|
|
182480
|
+
// Flush immediately for CLI
|
|
182481
|
+
flushInterval: 0
|
|
182482
|
+
});
|
|
182483
|
+
}
|
|
182484
|
+
return client;
|
|
182485
|
+
}
|
|
182486
|
+
function trackEvent(event, properties) {
|
|
182487
|
+
const ph = getClient();
|
|
182488
|
+
if (!ph) return;
|
|
182489
|
+
ph.capture({
|
|
182490
|
+
distinctId: getAnonymousId(),
|
|
182491
|
+
event,
|
|
182492
|
+
properties: {
|
|
182493
|
+
...properties,
|
|
182494
|
+
cli_version: "0.1.39",
|
|
182495
|
+
platform: process.platform,
|
|
182496
|
+
node_version: process.version,
|
|
182497
|
+
project_id: getProjectId()
|
|
182498
|
+
}
|
|
182499
|
+
});
|
|
182500
|
+
}
|
|
182501
|
+
async function shutdown() {
|
|
182502
|
+
if (client) {
|
|
182503
|
+
await client.shutdown();
|
|
182504
|
+
client = null;
|
|
182505
|
+
}
|
|
182506
|
+
}
|
|
182507
|
+
|
|
182405
182508
|
// src/commands/init.tsx
|
|
182406
182509
|
var SPECIFIC_INSTRUCTIONS = `Use Specific to handle all infrastructure and development environments: like deployments, databases, real-time sync, services, secrets, configuration and environment variables. Run \`specific docs\` to learn how to use Specific. ALWAYS run \`specific check\` after making changes to the Specific configuration.`;
|
|
182407
182510
|
var CURSOR_MDC_CONTENT = `---
|
|
@@ -182419,67 +182522,67 @@ var options = [
|
|
|
182419
182522
|
{ id: "other", label: "Other / Manual" }
|
|
182420
182523
|
];
|
|
182421
182524
|
function isGitProject() {
|
|
182422
|
-
const gitPath =
|
|
182423
|
-
return
|
|
182525
|
+
const gitPath = path3.join(process.cwd(), ".git");
|
|
182526
|
+
return fs3.existsSync(gitPath);
|
|
182424
182527
|
}
|
|
182425
182528
|
function detectExistingAgents() {
|
|
182426
182529
|
const detected = {};
|
|
182427
|
-
const cursorDir =
|
|
182428
|
-
if (
|
|
182530
|
+
const cursorDir = path3.join(process.cwd(), ".cursor");
|
|
182531
|
+
if (fs3.existsSync(cursorDir)) {
|
|
182429
182532
|
detected["cursor"] = true;
|
|
182430
182533
|
}
|
|
182431
|
-
const claudeDir =
|
|
182432
|
-
const claudeMd =
|
|
182433
|
-
if (
|
|
182534
|
+
const claudeDir = path3.join(process.cwd(), ".claude");
|
|
182535
|
+
const claudeMd = path3.join(process.cwd(), "CLAUDE.md");
|
|
182536
|
+
if (fs3.existsSync(claudeDir) || fs3.existsSync(claudeMd)) {
|
|
182434
182537
|
detected["claude"] = true;
|
|
182435
182538
|
}
|
|
182436
|
-
const agentsMd =
|
|
182437
|
-
if (
|
|
182539
|
+
const agentsMd = path3.join(process.cwd(), "AGENTS.md");
|
|
182540
|
+
if (fs3.existsSync(agentsMd)) {
|
|
182438
182541
|
detected["codex"] = true;
|
|
182439
182542
|
}
|
|
182440
182543
|
return detected;
|
|
182441
182544
|
}
|
|
182442
182545
|
function appendOrCreateFile(filePath, content) {
|
|
182443
|
-
if (
|
|
182444
|
-
const existing =
|
|
182546
|
+
if (fs3.existsSync(filePath)) {
|
|
182547
|
+
const existing = fs3.readFileSync(filePath, "utf-8");
|
|
182445
182548
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
182446
182549
|
return "unchanged";
|
|
182447
182550
|
}
|
|
182448
182551
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
182449
|
-
|
|
182552
|
+
fs3.writeFileSync(filePath, existing + separator + content + "\n");
|
|
182450
182553
|
return "modified";
|
|
182451
182554
|
} else {
|
|
182452
|
-
|
|
182555
|
+
fs3.writeFileSync(filePath, content + "\n");
|
|
182453
182556
|
return "created";
|
|
182454
182557
|
}
|
|
182455
182558
|
}
|
|
182456
182559
|
function addToGitignore() {
|
|
182457
|
-
const gitignorePath =
|
|
182560
|
+
const gitignorePath = path3.join(process.cwd(), ".gitignore");
|
|
182458
182561
|
const entries = [".specific", "specific.secrets"];
|
|
182459
|
-
if (
|
|
182460
|
-
const existing =
|
|
182562
|
+
if (fs3.existsSync(gitignorePath)) {
|
|
182563
|
+
const existing = fs3.readFileSync(gitignorePath, "utf-8");
|
|
182461
182564
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
182462
182565
|
const missingEntries = entries.filter((entry) => !lines.includes(entry));
|
|
182463
182566
|
if (missingEntries.length === 0) {
|
|
182464
182567
|
return "unchanged";
|
|
182465
182568
|
}
|
|
182466
182569
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
182467
|
-
|
|
182570
|
+
fs3.writeFileSync(
|
|
182468
182571
|
gitignorePath,
|
|
182469
182572
|
existing + separator + missingEntries.join("\n") + "\n"
|
|
182470
182573
|
);
|
|
182471
182574
|
return "modified";
|
|
182472
182575
|
} else {
|
|
182473
|
-
|
|
182576
|
+
fs3.writeFileSync(gitignorePath, entries.join("\n") + "\n");
|
|
182474
182577
|
return "created";
|
|
182475
182578
|
}
|
|
182476
182579
|
}
|
|
182477
182580
|
function configureClaudeCodePermissions() {
|
|
182478
|
-
const claudeDir =
|
|
182479
|
-
const settingsPath =
|
|
182581
|
+
const claudeDir = path3.join(process.cwd(), ".claude");
|
|
182582
|
+
const settingsPath = path3.join(claudeDir, "settings.local.json");
|
|
182480
182583
|
const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
|
|
182481
|
-
if (
|
|
182482
|
-
const existing = JSON.parse(
|
|
182584
|
+
if (fs3.existsSync(settingsPath)) {
|
|
182585
|
+
const existing = JSON.parse(fs3.readFileSync(settingsPath, "utf-8"));
|
|
182483
182586
|
const allowList = existing?.permissions?.allow || [];
|
|
182484
182587
|
const missingPermissions = permissions.filter(
|
|
182485
182588
|
(p) => !allowList.includes(p)
|
|
@@ -182494,39 +182597,39 @@ function configureClaudeCodePermissions() {
|
|
|
182494
182597
|
existing.permissions.allow = [];
|
|
182495
182598
|
}
|
|
182496
182599
|
existing.permissions.allow.push(...missingPermissions);
|
|
182497
|
-
|
|
182600
|
+
fs3.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
|
|
182498
182601
|
return "modified";
|
|
182499
182602
|
}
|
|
182500
|
-
if (!
|
|
182501
|
-
|
|
182603
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
182604
|
+
fs3.mkdirSync(claudeDir);
|
|
182502
182605
|
}
|
|
182503
182606
|
const settings = {
|
|
182504
182607
|
permissions: {
|
|
182505
182608
|
allow: permissions
|
|
182506
182609
|
}
|
|
182507
182610
|
};
|
|
182508
|
-
|
|
182611
|
+
fs3.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
182509
182612
|
return "created";
|
|
182510
182613
|
}
|
|
182511
182614
|
function createCursorRule() {
|
|
182512
|
-
const cursorDir =
|
|
182513
|
-
const rulesDir =
|
|
182514
|
-
const mdcPath =
|
|
182515
|
-
if (
|
|
182516
|
-
const existing =
|
|
182615
|
+
const cursorDir = path3.join(process.cwd(), ".cursor");
|
|
182616
|
+
const rulesDir = path3.join(cursorDir, "rules");
|
|
182617
|
+
const mdcPath = path3.join(rulesDir, "specific.mdc");
|
|
182618
|
+
if (fs3.existsSync(mdcPath)) {
|
|
182619
|
+
const existing = fs3.readFileSync(mdcPath, "utf-8");
|
|
182517
182620
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
182518
182621
|
return "unchanged";
|
|
182519
182622
|
}
|
|
182520
|
-
|
|
182623
|
+
fs3.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
182521
182624
|
return "modified";
|
|
182522
182625
|
}
|
|
182523
|
-
if (!
|
|
182524
|
-
|
|
182626
|
+
if (!fs3.existsSync(cursorDir)) {
|
|
182627
|
+
fs3.mkdirSync(cursorDir);
|
|
182525
182628
|
}
|
|
182526
|
-
if (!
|
|
182527
|
-
|
|
182629
|
+
if (!fs3.existsSync(rulesDir)) {
|
|
182630
|
+
fs3.mkdirSync(rulesDir);
|
|
182528
182631
|
}
|
|
182529
|
-
|
|
182632
|
+
fs3.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
182530
182633
|
return "created";
|
|
182531
182634
|
}
|
|
182532
182635
|
function configureAgents(checked) {
|
|
@@ -182542,7 +182645,7 @@ function configureAgents(checked) {
|
|
|
182542
182645
|
agents.filesModified.push(".cursor/rules/specific.mdc");
|
|
182543
182646
|
}
|
|
182544
182647
|
if (checked["claude"]) {
|
|
182545
|
-
const claudeMdPath =
|
|
182648
|
+
const claudeMdPath = path3.join(process.cwd(), "CLAUDE.md");
|
|
182546
182649
|
const status = appendOrCreateFile(claudeMdPath, SPECIFIC_INSTRUCTIONS);
|
|
182547
182650
|
if (status === "created") agents.filesCreated.push("CLAUDE.md");
|
|
182548
182651
|
else if (status === "modified") agents.filesModified.push("CLAUDE.md");
|
|
@@ -182553,7 +182656,7 @@ function configureAgents(checked) {
|
|
|
182553
182656
|
agents.filesModified.push(".claude/settings.local.json");
|
|
182554
182657
|
}
|
|
182555
182658
|
if (checked["codex"]) {
|
|
182556
|
-
const agentsMdPath =
|
|
182659
|
+
const agentsMdPath = path3.join(process.cwd(), "AGENTS.md");
|
|
182557
182660
|
const status = appendOrCreateFile(agentsMdPath, SPECIFIC_INSTRUCTIONS);
|
|
182558
182661
|
if (status === "created") agents.filesCreated.push("AGENTS.md");
|
|
182559
182662
|
else if (status === "modified") agents.filesModified.push("AGENTS.md");
|
|
@@ -182629,6 +182732,9 @@ function InitUI() {
|
|
|
182629
182732
|
} else if ((key.return || input === " ") && isSubmitFocused) {
|
|
182630
182733
|
const configResult = configureAgents(checked);
|
|
182631
182734
|
setResult(configResult);
|
|
182735
|
+
trackEvent("project_initialized", {
|
|
182736
|
+
agents: Object.keys(checked).filter((k) => checked[k])
|
|
182737
|
+
});
|
|
182632
182738
|
setPhase("done");
|
|
182633
182739
|
}
|
|
182634
182740
|
});
|
|
@@ -182672,35 +182778,35 @@ function initCommand() {
|
|
|
182672
182778
|
}
|
|
182673
182779
|
|
|
182674
182780
|
// src/commands/docs.tsx
|
|
182675
|
-
import { readFileSync as
|
|
182676
|
-
import { join as
|
|
182781
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
182782
|
+
import { join as join4, dirname } from "path";
|
|
182677
182783
|
import { fileURLToPath } from "url";
|
|
182678
182784
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
182679
|
-
var docsDir =
|
|
182680
|
-
function docsCommand(
|
|
182681
|
-
const docPath = resolveDocPath(
|
|
182785
|
+
var docsDir = join4(__dirname, "docs");
|
|
182786
|
+
function docsCommand(path22) {
|
|
182787
|
+
const docPath = resolveDocPath(path22);
|
|
182682
182788
|
if (!docPath) {
|
|
182683
182789
|
console.error(
|
|
182684
|
-
`Documentation not found: ${
|
|
182790
|
+
`Documentation not found: ${path22 || "index"}
|
|
182685
182791
|
|
|
182686
182792
|
Run 'specific docs' to see available topics.`
|
|
182687
182793
|
);
|
|
182688
182794
|
process.exit(1);
|
|
182689
182795
|
}
|
|
182690
|
-
const content =
|
|
182796
|
+
const content = readFileSync4(docPath, "utf-8");
|
|
182691
182797
|
console.log(content);
|
|
182692
182798
|
}
|
|
182693
|
-
function resolveDocPath(
|
|
182694
|
-
if (!
|
|
182695
|
-
const indexPath2 =
|
|
182696
|
-
return
|
|
182799
|
+
function resolveDocPath(path22) {
|
|
182800
|
+
if (!path22) {
|
|
182801
|
+
const indexPath2 = join4(docsDir, "index.md");
|
|
182802
|
+
return existsSync4(indexPath2) ? indexPath2 : null;
|
|
182697
182803
|
}
|
|
182698
|
-
const directPath =
|
|
182699
|
-
if (
|
|
182804
|
+
const directPath = join4(docsDir, `${path22}.md`);
|
|
182805
|
+
if (existsSync4(directPath)) {
|
|
182700
182806
|
return directPath;
|
|
182701
182807
|
}
|
|
182702
|
-
const indexPath =
|
|
182703
|
-
if (
|
|
182808
|
+
const indexPath = join4(docsDir, path22, "index.md");
|
|
182809
|
+
if (existsSync4(indexPath)) {
|
|
182704
182810
|
return indexPath;
|
|
182705
182811
|
}
|
|
182706
182812
|
return null;
|
|
@@ -182710,8 +182816,8 @@ function resolveDocPath(path20) {
|
|
|
182710
182816
|
import React2, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
182711
182817
|
import { render as render2, Text as Text2, Box as Box2 } from "ink";
|
|
182712
182818
|
import Spinner2 from "ink-spinner";
|
|
182713
|
-
import * as
|
|
182714
|
-
import * as
|
|
182819
|
+
import * as fs4 from "fs";
|
|
182820
|
+
import * as path4 from "path";
|
|
182715
182821
|
|
|
182716
182822
|
// ../config/dist/parser.js
|
|
182717
182823
|
var import_hcl2_json_parser = __toESM(require_dist(), 1);
|
|
@@ -183193,8 +183299,8 @@ function CheckUI() {
|
|
|
183193
183299
|
const [state, setState] = useState2({ status: "loading" });
|
|
183194
183300
|
useEffect2(() => {
|
|
183195
183301
|
async function load() {
|
|
183196
|
-
const configPath =
|
|
183197
|
-
if (!
|
|
183302
|
+
const configPath = path4.join(process.cwd(), "specific.hcl");
|
|
183303
|
+
if (!fs4.existsSync(configPath)) {
|
|
183198
183304
|
setState({
|
|
183199
183305
|
status: "error",
|
|
183200
183306
|
error: "No specific.hcl found in current directory"
|
|
@@ -183202,7 +183308,7 @@ function CheckUI() {
|
|
|
183202
183308
|
return;
|
|
183203
183309
|
}
|
|
183204
183310
|
try {
|
|
183205
|
-
const hcl =
|
|
183311
|
+
const hcl = fs4.readFileSync(configPath, "utf-8");
|
|
183206
183312
|
const config2 = await parseConfig(hcl);
|
|
183207
183313
|
setState({ status: "success", config: config2 });
|
|
183208
183314
|
} catch (err) {
|
|
@@ -183234,8 +183340,8 @@ function checkCommand() {
|
|
|
183234
183340
|
import React3, { useState as useState3, useEffect as useEffect3, useRef } from "react";
|
|
183235
183341
|
import { render as render3, Text as Text3, Box as Box3, useApp as useApp2, Static } from "ink";
|
|
183236
183342
|
import Spinner3 from "ink-spinner";
|
|
183237
|
-
import * as
|
|
183238
|
-
import * as
|
|
183343
|
+
import * as fs13 from "fs";
|
|
183344
|
+
import * as path14 from "path";
|
|
183239
183345
|
|
|
183240
183346
|
// node_modules/chokidar/index.js
|
|
183241
183347
|
import { EventEmitter } from "node:events";
|
|
@@ -183327,7 +183433,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
183327
183433
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
183328
183434
|
const statMethod = opts.lstat ? lstat : stat;
|
|
183329
183435
|
if (wantBigintFsStats) {
|
|
183330
|
-
this._stat = (
|
|
183436
|
+
this._stat = (path22) => statMethod(path22, { bigint: true });
|
|
183331
183437
|
} else {
|
|
183332
183438
|
this._stat = statMethod;
|
|
183333
183439
|
}
|
|
@@ -183352,8 +183458,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
183352
183458
|
const par = this.parent;
|
|
183353
183459
|
const fil = par && par.files;
|
|
183354
183460
|
if (fil && fil.length > 0) {
|
|
183355
|
-
const { path:
|
|
183356
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
183461
|
+
const { path: path22, depth } = par;
|
|
183462
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path22));
|
|
183357
183463
|
const awaited = await Promise.all(slice);
|
|
183358
183464
|
for (const entry of awaited) {
|
|
183359
183465
|
if (!entry)
|
|
@@ -183393,21 +183499,21 @@ var ReaddirpStream = class extends Readable {
|
|
|
183393
183499
|
this.reading = false;
|
|
183394
183500
|
}
|
|
183395
183501
|
}
|
|
183396
|
-
async _exploreDir(
|
|
183502
|
+
async _exploreDir(path22, depth) {
|
|
183397
183503
|
let files;
|
|
183398
183504
|
try {
|
|
183399
|
-
files = await readdir(
|
|
183505
|
+
files = await readdir(path22, this._rdOptions);
|
|
183400
183506
|
} catch (error) {
|
|
183401
183507
|
this._onError(error);
|
|
183402
183508
|
}
|
|
183403
|
-
return { files, depth, path:
|
|
183509
|
+
return { files, depth, path: path22 };
|
|
183404
183510
|
}
|
|
183405
|
-
async _formatEntry(dirent,
|
|
183511
|
+
async _formatEntry(dirent, path22) {
|
|
183406
183512
|
let entry;
|
|
183407
|
-
const
|
|
183513
|
+
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
183408
183514
|
try {
|
|
183409
|
-
const fullPath = presolve(pjoin(
|
|
183410
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
183515
|
+
const fullPath = presolve(pjoin(path22, basename5));
|
|
183516
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
183411
183517
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
183412
183518
|
} catch (err) {
|
|
183413
183519
|
this._onError(err);
|
|
@@ -183806,16 +183912,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
183806
183912
|
};
|
|
183807
183913
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
183808
183914
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
183809
|
-
function createFsWatchInstance(
|
|
183915
|
+
function createFsWatchInstance(path22, options2, listener, errHandler, emitRaw) {
|
|
183810
183916
|
const handleEvent = (rawEvent, evPath) => {
|
|
183811
|
-
listener(
|
|
183812
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
183813
|
-
if (evPath &&
|
|
183814
|
-
fsWatchBroadcast(sp.resolve(
|
|
183917
|
+
listener(path22);
|
|
183918
|
+
emitRaw(rawEvent, evPath, { watchedPath: path22 });
|
|
183919
|
+
if (evPath && path22 !== evPath) {
|
|
183920
|
+
fsWatchBroadcast(sp.resolve(path22, evPath), KEY_LISTENERS, sp.join(path22, evPath));
|
|
183815
183921
|
}
|
|
183816
183922
|
};
|
|
183817
183923
|
try {
|
|
183818
|
-
return fs_watch(
|
|
183924
|
+
return fs_watch(path22, {
|
|
183819
183925
|
persistent: options2.persistent
|
|
183820
183926
|
}, handleEvent);
|
|
183821
183927
|
} catch (error) {
|
|
@@ -183831,12 +183937,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
183831
183937
|
listener(val1, val2, val3);
|
|
183832
183938
|
});
|
|
183833
183939
|
};
|
|
183834
|
-
var setFsWatchListener = (
|
|
183940
|
+
var setFsWatchListener = (path22, fullPath, options2, handlers) => {
|
|
183835
183941
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
183836
183942
|
let cont = FsWatchInstances.get(fullPath);
|
|
183837
183943
|
let watcher;
|
|
183838
183944
|
if (!options2.persistent) {
|
|
183839
|
-
watcher = createFsWatchInstance(
|
|
183945
|
+
watcher = createFsWatchInstance(path22, options2, listener, errHandler, rawEmitter);
|
|
183840
183946
|
if (!watcher)
|
|
183841
183947
|
return;
|
|
183842
183948
|
return watcher.close.bind(watcher);
|
|
@@ -183847,7 +183953,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183847
183953
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
183848
183954
|
} else {
|
|
183849
183955
|
watcher = createFsWatchInstance(
|
|
183850
|
-
|
|
183956
|
+
path22,
|
|
183851
183957
|
options2,
|
|
183852
183958
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
183853
183959
|
errHandler,
|
|
@@ -183862,7 +183968,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183862
183968
|
cont.watcherUnusable = true;
|
|
183863
183969
|
if (isWindows && error.code === "EPERM") {
|
|
183864
183970
|
try {
|
|
183865
|
-
const fd = await open(
|
|
183971
|
+
const fd = await open(path22, "r");
|
|
183866
183972
|
await fd.close();
|
|
183867
183973
|
broadcastErr(error);
|
|
183868
183974
|
} catch (err) {
|
|
@@ -183893,7 +183999,7 @@ var setFsWatchListener = (path20, fullPath, options2, handlers) => {
|
|
|
183893
183999
|
};
|
|
183894
184000
|
};
|
|
183895
184001
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
183896
|
-
var setFsWatchFileListener = (
|
|
184002
|
+
var setFsWatchFileListener = (path22, fullPath, options2, handlers) => {
|
|
183897
184003
|
const { listener, rawEmitter } = handlers;
|
|
183898
184004
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
183899
184005
|
const copts = cont && cont.options;
|
|
@@ -183915,7 +184021,7 @@ var setFsWatchFileListener = (path20, fullPath, options2, handlers) => {
|
|
|
183915
184021
|
});
|
|
183916
184022
|
const currmtime = curr.mtimeMs;
|
|
183917
184023
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
183918
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
184024
|
+
foreach(cont.listeners, (listener2) => listener2(path22, curr));
|
|
183919
184025
|
}
|
|
183920
184026
|
})
|
|
183921
184027
|
};
|
|
@@ -183945,13 +184051,13 @@ var NodeFsHandler = class {
|
|
|
183945
184051
|
* @param listener on fs change
|
|
183946
184052
|
* @returns closer for the watcher instance
|
|
183947
184053
|
*/
|
|
183948
|
-
_watchWithNodeFs(
|
|
184054
|
+
_watchWithNodeFs(path22, listener) {
|
|
183949
184055
|
const opts = this.fsw.options;
|
|
183950
|
-
const directory = sp.dirname(
|
|
183951
|
-
const
|
|
184056
|
+
const directory = sp.dirname(path22);
|
|
184057
|
+
const basename5 = sp.basename(path22);
|
|
183952
184058
|
const parent = this.fsw._getWatchedDir(directory);
|
|
183953
|
-
parent.add(
|
|
183954
|
-
const absolutePath = sp.resolve(
|
|
184059
|
+
parent.add(basename5);
|
|
184060
|
+
const absolutePath = sp.resolve(path22);
|
|
183955
184061
|
const options2 = {
|
|
183956
184062
|
persistent: opts.persistent
|
|
183957
184063
|
};
|
|
@@ -183960,13 +184066,13 @@ var NodeFsHandler = class {
|
|
|
183960
184066
|
let closer;
|
|
183961
184067
|
if (opts.usePolling) {
|
|
183962
184068
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
183963
|
-
options2.interval = enableBin && isBinaryPath(
|
|
183964
|
-
closer = setFsWatchFileListener(
|
|
184069
|
+
options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
184070
|
+
closer = setFsWatchFileListener(path22, absolutePath, options2, {
|
|
183965
184071
|
listener,
|
|
183966
184072
|
rawEmitter: this.fsw._emitRaw
|
|
183967
184073
|
});
|
|
183968
184074
|
} else {
|
|
183969
|
-
closer = setFsWatchListener(
|
|
184075
|
+
closer = setFsWatchListener(path22, absolutePath, options2, {
|
|
183970
184076
|
listener,
|
|
183971
184077
|
errHandler: this._boundHandleError,
|
|
183972
184078
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -183983,12 +184089,12 @@ var NodeFsHandler = class {
|
|
|
183983
184089
|
return;
|
|
183984
184090
|
}
|
|
183985
184091
|
const dirname8 = sp.dirname(file);
|
|
183986
|
-
const
|
|
184092
|
+
const basename5 = sp.basename(file);
|
|
183987
184093
|
const parent = this.fsw._getWatchedDir(dirname8);
|
|
183988
184094
|
let prevStats = stats;
|
|
183989
|
-
if (parent.has(
|
|
184095
|
+
if (parent.has(basename5))
|
|
183990
184096
|
return;
|
|
183991
|
-
const listener = async (
|
|
184097
|
+
const listener = async (path22, newStats) => {
|
|
183992
184098
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
183993
184099
|
return;
|
|
183994
184100
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -184002,18 +184108,18 @@ var NodeFsHandler = class {
|
|
|
184002
184108
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
184003
184109
|
}
|
|
184004
184110
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
184005
|
-
this.fsw._closeFile(
|
|
184111
|
+
this.fsw._closeFile(path22);
|
|
184006
184112
|
prevStats = newStats2;
|
|
184007
184113
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
184008
184114
|
if (closer2)
|
|
184009
|
-
this.fsw._addPathCloser(
|
|
184115
|
+
this.fsw._addPathCloser(path22, closer2);
|
|
184010
184116
|
} else {
|
|
184011
184117
|
prevStats = newStats2;
|
|
184012
184118
|
}
|
|
184013
184119
|
} catch (error) {
|
|
184014
|
-
this.fsw._remove(dirname8,
|
|
184120
|
+
this.fsw._remove(dirname8, basename5);
|
|
184015
184121
|
}
|
|
184016
|
-
} else if (parent.has(
|
|
184122
|
+
} else if (parent.has(basename5)) {
|
|
184017
184123
|
const at = newStats.atimeMs;
|
|
184018
184124
|
const mt = newStats.mtimeMs;
|
|
184019
184125
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -184038,7 +184144,7 @@ var NodeFsHandler = class {
|
|
|
184038
184144
|
* @param item basename of this item
|
|
184039
184145
|
* @returns true if no more processing is needed for this entry.
|
|
184040
184146
|
*/
|
|
184041
|
-
async _handleSymlink(entry, directory,
|
|
184147
|
+
async _handleSymlink(entry, directory, path22, item) {
|
|
184042
184148
|
if (this.fsw.closed) {
|
|
184043
184149
|
return;
|
|
184044
184150
|
}
|
|
@@ -184048,7 +184154,7 @@ var NodeFsHandler = class {
|
|
|
184048
184154
|
this.fsw._incrReadyCount();
|
|
184049
184155
|
let linkPath;
|
|
184050
184156
|
try {
|
|
184051
|
-
linkPath = await fsrealpath(
|
|
184157
|
+
linkPath = await fsrealpath(path22);
|
|
184052
184158
|
} catch (e) {
|
|
184053
184159
|
this.fsw._emitReady();
|
|
184054
184160
|
return true;
|
|
@@ -184058,12 +184164,12 @@ var NodeFsHandler = class {
|
|
|
184058
184164
|
if (dir.has(item)) {
|
|
184059
184165
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
184060
184166
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184061
|
-
this.fsw._emit(EV.CHANGE,
|
|
184167
|
+
this.fsw._emit(EV.CHANGE, path22, entry.stats);
|
|
184062
184168
|
}
|
|
184063
184169
|
} else {
|
|
184064
184170
|
dir.add(item);
|
|
184065
184171
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
184066
|
-
this.fsw._emit(EV.ADD,
|
|
184172
|
+
this.fsw._emit(EV.ADD, path22, entry.stats);
|
|
184067
184173
|
}
|
|
184068
184174
|
this.fsw._emitReady();
|
|
184069
184175
|
return true;
|
|
@@ -184093,9 +184199,9 @@ var NodeFsHandler = class {
|
|
|
184093
184199
|
return;
|
|
184094
184200
|
}
|
|
184095
184201
|
const item = entry.path;
|
|
184096
|
-
let
|
|
184202
|
+
let path22 = sp.join(directory, item);
|
|
184097
184203
|
current.add(item);
|
|
184098
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
184204
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path22, item)) {
|
|
184099
184205
|
return;
|
|
184100
184206
|
}
|
|
184101
184207
|
if (this.fsw.closed) {
|
|
@@ -184104,11 +184210,11 @@ var NodeFsHandler = class {
|
|
|
184104
184210
|
}
|
|
184105
184211
|
if (item === target || !target && !previous.has(item)) {
|
|
184106
184212
|
this.fsw._incrReadyCount();
|
|
184107
|
-
|
|
184108
|
-
this._addToNodeFs(
|
|
184213
|
+
path22 = sp.join(dir, sp.relative(dir, path22));
|
|
184214
|
+
this._addToNodeFs(path22, initialAdd, wh, depth + 1);
|
|
184109
184215
|
}
|
|
184110
184216
|
}).on(EV.ERROR, this._boundHandleError);
|
|
184111
|
-
return new Promise((
|
|
184217
|
+
return new Promise((resolve5, reject) => {
|
|
184112
184218
|
if (!stream)
|
|
184113
184219
|
return reject();
|
|
184114
184220
|
stream.once(STR_END, () => {
|
|
@@ -184117,7 +184223,7 @@ var NodeFsHandler = class {
|
|
|
184117
184223
|
return;
|
|
184118
184224
|
}
|
|
184119
184225
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
184120
|
-
|
|
184226
|
+
resolve5(void 0);
|
|
184121
184227
|
previous.getChildren().filter((item) => {
|
|
184122
184228
|
return item !== directory && !current.has(item);
|
|
184123
184229
|
}).forEach((item) => {
|
|
@@ -184174,13 +184280,13 @@ var NodeFsHandler = class {
|
|
|
184174
184280
|
* @param depth Child path actually targeted for watch
|
|
184175
184281
|
* @param target Child path actually targeted for watch
|
|
184176
184282
|
*/
|
|
184177
|
-
async _addToNodeFs(
|
|
184283
|
+
async _addToNodeFs(path22, initialAdd, priorWh, depth, target) {
|
|
184178
184284
|
const ready = this.fsw._emitReady;
|
|
184179
|
-
if (this.fsw._isIgnored(
|
|
184285
|
+
if (this.fsw._isIgnored(path22) || this.fsw.closed) {
|
|
184180
184286
|
ready();
|
|
184181
184287
|
return false;
|
|
184182
184288
|
}
|
|
184183
|
-
const wh = this.fsw._getWatchHelpers(
|
|
184289
|
+
const wh = this.fsw._getWatchHelpers(path22);
|
|
184184
184290
|
if (priorWh) {
|
|
184185
184291
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
184186
184292
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -184196,8 +184302,8 @@ var NodeFsHandler = class {
|
|
|
184196
184302
|
const follow = this.fsw.options.followSymlinks;
|
|
184197
184303
|
let closer;
|
|
184198
184304
|
if (stats.isDirectory()) {
|
|
184199
|
-
const absPath = sp.resolve(
|
|
184200
|
-
const targetPath = follow ? await fsrealpath(
|
|
184305
|
+
const absPath = sp.resolve(path22);
|
|
184306
|
+
const targetPath = follow ? await fsrealpath(path22) : path22;
|
|
184201
184307
|
if (this.fsw.closed)
|
|
184202
184308
|
return;
|
|
184203
184309
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -184207,29 +184313,29 @@ var NodeFsHandler = class {
|
|
|
184207
184313
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
184208
184314
|
}
|
|
184209
184315
|
} else if (stats.isSymbolicLink()) {
|
|
184210
|
-
const targetPath = follow ? await fsrealpath(
|
|
184316
|
+
const targetPath = follow ? await fsrealpath(path22) : path22;
|
|
184211
184317
|
if (this.fsw.closed)
|
|
184212
184318
|
return;
|
|
184213
184319
|
const parent = sp.dirname(wh.watchPath);
|
|
184214
184320
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
184215
184321
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
184216
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
184322
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path22, wh, targetPath);
|
|
184217
184323
|
if (this.fsw.closed)
|
|
184218
184324
|
return;
|
|
184219
184325
|
if (targetPath !== void 0) {
|
|
184220
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
184326
|
+
this.fsw._symlinkPaths.set(sp.resolve(path22), targetPath);
|
|
184221
184327
|
}
|
|
184222
184328
|
} else {
|
|
184223
184329
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
184224
184330
|
}
|
|
184225
184331
|
ready();
|
|
184226
184332
|
if (closer)
|
|
184227
|
-
this.fsw._addPathCloser(
|
|
184333
|
+
this.fsw._addPathCloser(path22, closer);
|
|
184228
184334
|
return false;
|
|
184229
184335
|
} catch (error) {
|
|
184230
184336
|
if (this.fsw._handleError(error)) {
|
|
184231
184337
|
ready();
|
|
184232
|
-
return
|
|
184338
|
+
return path22;
|
|
184233
184339
|
}
|
|
184234
184340
|
}
|
|
184235
184341
|
}
|
|
@@ -184272,24 +184378,24 @@ function createPattern(matcher) {
|
|
|
184272
184378
|
}
|
|
184273
184379
|
return () => false;
|
|
184274
184380
|
}
|
|
184275
|
-
function normalizePath(
|
|
184276
|
-
if (typeof
|
|
184381
|
+
function normalizePath(path22) {
|
|
184382
|
+
if (typeof path22 !== "string")
|
|
184277
184383
|
throw new Error("string expected");
|
|
184278
|
-
|
|
184279
|
-
|
|
184384
|
+
path22 = sp2.normalize(path22);
|
|
184385
|
+
path22 = path22.replace(/\\/g, "/");
|
|
184280
184386
|
let prepend = false;
|
|
184281
|
-
if (
|
|
184387
|
+
if (path22.startsWith("//"))
|
|
184282
184388
|
prepend = true;
|
|
184283
|
-
|
|
184389
|
+
path22 = path22.replace(DOUBLE_SLASH_RE, "/");
|
|
184284
184390
|
if (prepend)
|
|
184285
|
-
|
|
184286
|
-
return
|
|
184391
|
+
path22 = "/" + path22;
|
|
184392
|
+
return path22;
|
|
184287
184393
|
}
|
|
184288
184394
|
function matchPatterns(patterns, testString, stats) {
|
|
184289
|
-
const
|
|
184395
|
+
const path22 = normalizePath(testString);
|
|
184290
184396
|
for (let index = 0; index < patterns.length; index++) {
|
|
184291
184397
|
const pattern = patterns[index];
|
|
184292
|
-
if (pattern(
|
|
184398
|
+
if (pattern(path22, stats)) {
|
|
184293
184399
|
return true;
|
|
184294
184400
|
}
|
|
184295
184401
|
}
|
|
@@ -184327,19 +184433,19 @@ var toUnix = (string) => {
|
|
|
184327
184433
|
}
|
|
184328
184434
|
return str;
|
|
184329
184435
|
};
|
|
184330
|
-
var normalizePathToUnix = (
|
|
184331
|
-
var normalizeIgnored = (cwd = "") => (
|
|
184332
|
-
if (typeof
|
|
184333
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
184436
|
+
var normalizePathToUnix = (path22) => toUnix(sp2.normalize(toUnix(path22)));
|
|
184437
|
+
var normalizeIgnored = (cwd = "") => (path22) => {
|
|
184438
|
+
if (typeof path22 === "string") {
|
|
184439
|
+
return normalizePathToUnix(sp2.isAbsolute(path22) ? path22 : sp2.join(cwd, path22));
|
|
184334
184440
|
} else {
|
|
184335
|
-
return
|
|
184441
|
+
return path22;
|
|
184336
184442
|
}
|
|
184337
184443
|
};
|
|
184338
|
-
var getAbsolutePath = (
|
|
184339
|
-
if (sp2.isAbsolute(
|
|
184340
|
-
return
|
|
184444
|
+
var getAbsolutePath = (path22, cwd) => {
|
|
184445
|
+
if (sp2.isAbsolute(path22)) {
|
|
184446
|
+
return path22;
|
|
184341
184447
|
}
|
|
184342
|
-
return sp2.join(cwd,
|
|
184448
|
+
return sp2.join(cwd, path22);
|
|
184343
184449
|
};
|
|
184344
184450
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
184345
184451
|
var DirEntry = class {
|
|
@@ -184404,10 +184510,10 @@ var WatchHelper = class {
|
|
|
184404
184510
|
dirParts;
|
|
184405
184511
|
followSymlinks;
|
|
184406
184512
|
statMethod;
|
|
184407
|
-
constructor(
|
|
184513
|
+
constructor(path22, follow, fsw) {
|
|
184408
184514
|
this.fsw = fsw;
|
|
184409
|
-
const watchPath =
|
|
184410
|
-
this.path =
|
|
184515
|
+
const watchPath = path22;
|
|
184516
|
+
this.path = path22 = path22.replace(REPLACER_RE, "");
|
|
184411
184517
|
this.watchPath = watchPath;
|
|
184412
184518
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
184413
184519
|
this.dirParts = [];
|
|
@@ -184547,20 +184653,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184547
184653
|
this._closePromise = void 0;
|
|
184548
184654
|
let paths = unifyPaths(paths_);
|
|
184549
184655
|
if (cwd) {
|
|
184550
|
-
paths = paths.map((
|
|
184551
|
-
const absPath = getAbsolutePath(
|
|
184656
|
+
paths = paths.map((path22) => {
|
|
184657
|
+
const absPath = getAbsolutePath(path22, cwd);
|
|
184552
184658
|
return absPath;
|
|
184553
184659
|
});
|
|
184554
184660
|
}
|
|
184555
|
-
paths.forEach((
|
|
184556
|
-
this._removeIgnoredPath(
|
|
184661
|
+
paths.forEach((path22) => {
|
|
184662
|
+
this._removeIgnoredPath(path22);
|
|
184557
184663
|
});
|
|
184558
184664
|
this._userIgnored = void 0;
|
|
184559
184665
|
if (!this._readyCount)
|
|
184560
184666
|
this._readyCount = 0;
|
|
184561
184667
|
this._readyCount += paths.length;
|
|
184562
|
-
Promise.all(paths.map(async (
|
|
184563
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
184668
|
+
Promise.all(paths.map(async (path22) => {
|
|
184669
|
+
const res = await this._nodeFsHandler._addToNodeFs(path22, !_internal, void 0, 0, _origAdd);
|
|
184564
184670
|
if (res)
|
|
184565
184671
|
this._emitReady();
|
|
184566
184672
|
return res;
|
|
@@ -184582,17 +184688,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184582
184688
|
return this;
|
|
184583
184689
|
const paths = unifyPaths(paths_);
|
|
184584
184690
|
const { cwd } = this.options;
|
|
184585
|
-
paths.forEach((
|
|
184586
|
-
if (!sp2.isAbsolute(
|
|
184691
|
+
paths.forEach((path22) => {
|
|
184692
|
+
if (!sp2.isAbsolute(path22) && !this._closers.has(path22)) {
|
|
184587
184693
|
if (cwd)
|
|
184588
|
-
|
|
184589
|
-
|
|
184694
|
+
path22 = sp2.join(cwd, path22);
|
|
184695
|
+
path22 = sp2.resolve(path22);
|
|
184590
184696
|
}
|
|
184591
|
-
this._closePath(
|
|
184592
|
-
this._addIgnoredPath(
|
|
184593
|
-
if (this._watched.has(
|
|
184697
|
+
this._closePath(path22);
|
|
184698
|
+
this._addIgnoredPath(path22);
|
|
184699
|
+
if (this._watched.has(path22)) {
|
|
184594
184700
|
this._addIgnoredPath({
|
|
184595
|
-
path:
|
|
184701
|
+
path: path22,
|
|
184596
184702
|
recursive: true
|
|
184597
184703
|
});
|
|
184598
184704
|
}
|
|
@@ -184656,38 +184762,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184656
184762
|
* @param stats arguments to be passed with event
|
|
184657
184763
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
184658
184764
|
*/
|
|
184659
|
-
async _emit(event,
|
|
184765
|
+
async _emit(event, path22, stats) {
|
|
184660
184766
|
if (this.closed)
|
|
184661
184767
|
return;
|
|
184662
184768
|
const opts = this.options;
|
|
184663
184769
|
if (isWindows)
|
|
184664
|
-
|
|
184770
|
+
path22 = sp2.normalize(path22);
|
|
184665
184771
|
if (opts.cwd)
|
|
184666
|
-
|
|
184667
|
-
const args = [
|
|
184772
|
+
path22 = sp2.relative(opts.cwd, path22);
|
|
184773
|
+
const args = [path22];
|
|
184668
184774
|
if (stats != null)
|
|
184669
184775
|
args.push(stats);
|
|
184670
184776
|
const awf = opts.awaitWriteFinish;
|
|
184671
184777
|
let pw;
|
|
184672
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
184778
|
+
if (awf && (pw = this._pendingWrites.get(path22))) {
|
|
184673
184779
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
184674
184780
|
return this;
|
|
184675
184781
|
}
|
|
184676
184782
|
if (opts.atomic) {
|
|
184677
184783
|
if (event === EVENTS.UNLINK) {
|
|
184678
|
-
this._pendingUnlinks.set(
|
|
184784
|
+
this._pendingUnlinks.set(path22, [event, ...args]);
|
|
184679
184785
|
setTimeout(() => {
|
|
184680
|
-
this._pendingUnlinks.forEach((entry,
|
|
184786
|
+
this._pendingUnlinks.forEach((entry, path23) => {
|
|
184681
184787
|
this.emit(...entry);
|
|
184682
184788
|
this.emit(EVENTS.ALL, ...entry);
|
|
184683
|
-
this._pendingUnlinks.delete(
|
|
184789
|
+
this._pendingUnlinks.delete(path23);
|
|
184684
184790
|
});
|
|
184685
184791
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
184686
184792
|
return this;
|
|
184687
184793
|
}
|
|
184688
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
184794
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path22)) {
|
|
184689
184795
|
event = EVENTS.CHANGE;
|
|
184690
|
-
this._pendingUnlinks.delete(
|
|
184796
|
+
this._pendingUnlinks.delete(path22);
|
|
184691
184797
|
}
|
|
184692
184798
|
}
|
|
184693
184799
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -184705,16 +184811,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184705
184811
|
this.emitWithAll(event, args);
|
|
184706
184812
|
}
|
|
184707
184813
|
};
|
|
184708
|
-
this._awaitWriteFinish(
|
|
184814
|
+
this._awaitWriteFinish(path22, awf.stabilityThreshold, event, awfEmit);
|
|
184709
184815
|
return this;
|
|
184710
184816
|
}
|
|
184711
184817
|
if (event === EVENTS.CHANGE) {
|
|
184712
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
184818
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path22, 50);
|
|
184713
184819
|
if (isThrottled)
|
|
184714
184820
|
return this;
|
|
184715
184821
|
}
|
|
184716
184822
|
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,
|
|
184823
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path22) : path22;
|
|
184718
184824
|
let stats2;
|
|
184719
184825
|
try {
|
|
184720
184826
|
stats2 = await stat3(fullPath);
|
|
@@ -184745,23 +184851,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184745
184851
|
* @param timeout duration of time to suppress duplicate actions
|
|
184746
184852
|
* @returns tracking object or false if action should be suppressed
|
|
184747
184853
|
*/
|
|
184748
|
-
_throttle(actionType,
|
|
184854
|
+
_throttle(actionType, path22, timeout) {
|
|
184749
184855
|
if (!this._throttled.has(actionType)) {
|
|
184750
184856
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
184751
184857
|
}
|
|
184752
184858
|
const action = this._throttled.get(actionType);
|
|
184753
184859
|
if (!action)
|
|
184754
184860
|
throw new Error("invalid throttle");
|
|
184755
|
-
const actionPath = action.get(
|
|
184861
|
+
const actionPath = action.get(path22);
|
|
184756
184862
|
if (actionPath) {
|
|
184757
184863
|
actionPath.count++;
|
|
184758
184864
|
return false;
|
|
184759
184865
|
}
|
|
184760
184866
|
let timeoutObject;
|
|
184761
184867
|
const clear = () => {
|
|
184762
|
-
const item = action.get(
|
|
184868
|
+
const item = action.get(path22);
|
|
184763
184869
|
const count = item ? item.count : 0;
|
|
184764
|
-
action.delete(
|
|
184870
|
+
action.delete(path22);
|
|
184765
184871
|
clearTimeout(timeoutObject);
|
|
184766
184872
|
if (item)
|
|
184767
184873
|
clearTimeout(item.timeoutObject);
|
|
@@ -184769,7 +184875,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184769
184875
|
};
|
|
184770
184876
|
timeoutObject = setTimeout(clear, timeout);
|
|
184771
184877
|
const thr = { timeoutObject, clear, count: 0 };
|
|
184772
|
-
action.set(
|
|
184878
|
+
action.set(path22, thr);
|
|
184773
184879
|
return thr;
|
|
184774
184880
|
}
|
|
184775
184881
|
_incrReadyCount() {
|
|
@@ -184783,44 +184889,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184783
184889
|
* @param event
|
|
184784
184890
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
184785
184891
|
*/
|
|
184786
|
-
_awaitWriteFinish(
|
|
184892
|
+
_awaitWriteFinish(path22, threshold, event, awfEmit) {
|
|
184787
184893
|
const awf = this.options.awaitWriteFinish;
|
|
184788
184894
|
if (typeof awf !== "object")
|
|
184789
184895
|
return;
|
|
184790
184896
|
const pollInterval = awf.pollInterval;
|
|
184791
184897
|
let timeoutHandler;
|
|
184792
|
-
let fullPath =
|
|
184793
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
184794
|
-
fullPath = sp2.join(this.options.cwd,
|
|
184898
|
+
let fullPath = path22;
|
|
184899
|
+
if (this.options.cwd && !sp2.isAbsolute(path22)) {
|
|
184900
|
+
fullPath = sp2.join(this.options.cwd, path22);
|
|
184795
184901
|
}
|
|
184796
184902
|
const now = /* @__PURE__ */ new Date();
|
|
184797
184903
|
const writes = this._pendingWrites;
|
|
184798
184904
|
function awaitWriteFinishFn(prevStat) {
|
|
184799
184905
|
statcb(fullPath, (err, curStat) => {
|
|
184800
|
-
if (err || !writes.has(
|
|
184906
|
+
if (err || !writes.has(path22)) {
|
|
184801
184907
|
if (err && err.code !== "ENOENT")
|
|
184802
184908
|
awfEmit(err);
|
|
184803
184909
|
return;
|
|
184804
184910
|
}
|
|
184805
184911
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
184806
184912
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
184807
|
-
writes.get(
|
|
184913
|
+
writes.get(path22).lastChange = now2;
|
|
184808
184914
|
}
|
|
184809
|
-
const pw = writes.get(
|
|
184915
|
+
const pw = writes.get(path22);
|
|
184810
184916
|
const df = now2 - pw.lastChange;
|
|
184811
184917
|
if (df >= threshold) {
|
|
184812
|
-
writes.delete(
|
|
184918
|
+
writes.delete(path22);
|
|
184813
184919
|
awfEmit(void 0, curStat);
|
|
184814
184920
|
} else {
|
|
184815
184921
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
184816
184922
|
}
|
|
184817
184923
|
});
|
|
184818
184924
|
}
|
|
184819
|
-
if (!writes.has(
|
|
184820
|
-
writes.set(
|
|
184925
|
+
if (!writes.has(path22)) {
|
|
184926
|
+
writes.set(path22, {
|
|
184821
184927
|
lastChange: now,
|
|
184822
184928
|
cancelWait: () => {
|
|
184823
|
-
writes.delete(
|
|
184929
|
+
writes.delete(path22);
|
|
184824
184930
|
clearTimeout(timeoutHandler);
|
|
184825
184931
|
return event;
|
|
184826
184932
|
}
|
|
@@ -184831,8 +184937,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184831
184937
|
/**
|
|
184832
184938
|
* Determines whether user has asked to ignore this path.
|
|
184833
184939
|
*/
|
|
184834
|
-
_isIgnored(
|
|
184835
|
-
if (this.options.atomic && DOT_RE.test(
|
|
184940
|
+
_isIgnored(path22, stats) {
|
|
184941
|
+
if (this.options.atomic && DOT_RE.test(path22))
|
|
184836
184942
|
return true;
|
|
184837
184943
|
if (!this._userIgnored) {
|
|
184838
184944
|
const { cwd } = this.options;
|
|
@@ -184842,17 +184948,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184842
184948
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
184843
184949
|
this._userIgnored = anymatch(list, void 0);
|
|
184844
184950
|
}
|
|
184845
|
-
return this._userIgnored(
|
|
184951
|
+
return this._userIgnored(path22, stats);
|
|
184846
184952
|
}
|
|
184847
|
-
_isntIgnored(
|
|
184848
|
-
return !this._isIgnored(
|
|
184953
|
+
_isntIgnored(path22, stat4) {
|
|
184954
|
+
return !this._isIgnored(path22, stat4);
|
|
184849
184955
|
}
|
|
184850
184956
|
/**
|
|
184851
184957
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
184852
184958
|
* @param path file or directory pattern being watched
|
|
184853
184959
|
*/
|
|
184854
|
-
_getWatchHelpers(
|
|
184855
|
-
return new WatchHelper(
|
|
184960
|
+
_getWatchHelpers(path22) {
|
|
184961
|
+
return new WatchHelper(path22, this.options.followSymlinks, this);
|
|
184856
184962
|
}
|
|
184857
184963
|
// Directory helpers
|
|
184858
184964
|
// -----------------
|
|
@@ -184884,63 +184990,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
184884
184990
|
* @param item base path of item/directory
|
|
184885
184991
|
*/
|
|
184886
184992
|
_remove(directory, item, isDirectory) {
|
|
184887
|
-
const
|
|
184888
|
-
const fullPath = sp2.resolve(
|
|
184889
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
184890
|
-
if (!this._throttle("remove",
|
|
184993
|
+
const path22 = sp2.join(directory, item);
|
|
184994
|
+
const fullPath = sp2.resolve(path22);
|
|
184995
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path22) || this._watched.has(fullPath);
|
|
184996
|
+
if (!this._throttle("remove", path22, 100))
|
|
184891
184997
|
return;
|
|
184892
184998
|
if (!isDirectory && this._watched.size === 1) {
|
|
184893
184999
|
this.add(directory, item, true);
|
|
184894
185000
|
}
|
|
184895
|
-
const wp = this._getWatchedDir(
|
|
185001
|
+
const wp = this._getWatchedDir(path22);
|
|
184896
185002
|
const nestedDirectoryChildren = wp.getChildren();
|
|
184897
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
185003
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path22, nested));
|
|
184898
185004
|
const parent = this._getWatchedDir(directory);
|
|
184899
185005
|
const wasTracked = parent.has(item);
|
|
184900
185006
|
parent.remove(item);
|
|
184901
185007
|
if (this._symlinkPaths.has(fullPath)) {
|
|
184902
185008
|
this._symlinkPaths.delete(fullPath);
|
|
184903
185009
|
}
|
|
184904
|
-
let relPath =
|
|
185010
|
+
let relPath = path22;
|
|
184905
185011
|
if (this.options.cwd)
|
|
184906
|
-
relPath = sp2.relative(this.options.cwd,
|
|
185012
|
+
relPath = sp2.relative(this.options.cwd, path22);
|
|
184907
185013
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
184908
185014
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
184909
185015
|
if (event === EVENTS.ADD)
|
|
184910
185016
|
return;
|
|
184911
185017
|
}
|
|
184912
|
-
this._watched.delete(
|
|
185018
|
+
this._watched.delete(path22);
|
|
184913
185019
|
this._watched.delete(fullPath);
|
|
184914
185020
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
184915
|
-
if (wasTracked && !this._isIgnored(
|
|
184916
|
-
this._emit(eventName,
|
|
184917
|
-
this._closePath(
|
|
185021
|
+
if (wasTracked && !this._isIgnored(path22))
|
|
185022
|
+
this._emit(eventName, path22);
|
|
185023
|
+
this._closePath(path22);
|
|
184918
185024
|
}
|
|
184919
185025
|
/**
|
|
184920
185026
|
* Closes all watchers for a path
|
|
184921
185027
|
*/
|
|
184922
|
-
_closePath(
|
|
184923
|
-
this._closeFile(
|
|
184924
|
-
const dir = sp2.dirname(
|
|
184925
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
185028
|
+
_closePath(path22) {
|
|
185029
|
+
this._closeFile(path22);
|
|
185030
|
+
const dir = sp2.dirname(path22);
|
|
185031
|
+
this._getWatchedDir(dir).remove(sp2.basename(path22));
|
|
184926
185032
|
}
|
|
184927
185033
|
/**
|
|
184928
185034
|
* Closes only file-specific watchers
|
|
184929
185035
|
*/
|
|
184930
|
-
_closeFile(
|
|
184931
|
-
const closers = this._closers.get(
|
|
185036
|
+
_closeFile(path22) {
|
|
185037
|
+
const closers = this._closers.get(path22);
|
|
184932
185038
|
if (!closers)
|
|
184933
185039
|
return;
|
|
184934
185040
|
closers.forEach((closer) => closer());
|
|
184935
|
-
this._closers.delete(
|
|
185041
|
+
this._closers.delete(path22);
|
|
184936
185042
|
}
|
|
184937
|
-
_addPathCloser(
|
|
185043
|
+
_addPathCloser(path22, closer) {
|
|
184938
185044
|
if (!closer)
|
|
184939
185045
|
return;
|
|
184940
|
-
let list = this._closers.get(
|
|
185046
|
+
let list = this._closers.get(path22);
|
|
184941
185047
|
if (!list) {
|
|
184942
185048
|
list = [];
|
|
184943
|
-
this._closers.set(
|
|
185049
|
+
this._closers.set(path22, list);
|
|
184944
185050
|
}
|
|
184945
185051
|
list.push(closer);
|
|
184946
185052
|
}
|
|
@@ -184989,8 +185095,8 @@ var PortAllocator = class {
|
|
|
184989
185095
|
};
|
|
184990
185096
|
|
|
184991
185097
|
// src/lib/dev/stable-port-allocator.ts
|
|
184992
|
-
import * as
|
|
184993
|
-
import * as
|
|
185098
|
+
import * as fs5 from "fs";
|
|
185099
|
+
import * as path5 from "path";
|
|
184994
185100
|
var PORT_RANGE_START2 = 4e4;
|
|
184995
185101
|
var PORT_RANGE_END2 = 49999;
|
|
184996
185102
|
var StablePortAllocator = class {
|
|
@@ -184998,17 +185104,17 @@ var StablePortAllocator = class {
|
|
|
184998
185104
|
portsFilePath;
|
|
184999
185105
|
savedPorts = {};
|
|
185000
185106
|
usedPorts = /* @__PURE__ */ new Set();
|
|
185001
|
-
constructor(projectRoot) {
|
|
185002
|
-
this.portsDir =
|
|
185003
|
-
this.portsFilePath =
|
|
185107
|
+
constructor(projectRoot, key = "default") {
|
|
185108
|
+
this.portsDir = path5.join(projectRoot, ".specific", "keys", key);
|
|
185109
|
+
this.portsFilePath = path5.join(this.portsDir, "ports.json");
|
|
185004
185110
|
this.loadPorts();
|
|
185005
185111
|
}
|
|
185006
185112
|
loadPorts() {
|
|
185007
|
-
if (!
|
|
185113
|
+
if (!fs5.existsSync(this.portsFilePath)) {
|
|
185008
185114
|
return;
|
|
185009
185115
|
}
|
|
185010
185116
|
try {
|
|
185011
|
-
const content =
|
|
185117
|
+
const content = fs5.readFileSync(this.portsFilePath, "utf-8");
|
|
185012
185118
|
const data = JSON.parse(content);
|
|
185013
185119
|
if (data.version === 1 && data.ports) {
|
|
185014
185120
|
this.savedPorts = data.ports;
|
|
@@ -185021,14 +185127,14 @@ var StablePortAllocator = class {
|
|
|
185021
185127
|
}
|
|
185022
185128
|
}
|
|
185023
185129
|
savePorts() {
|
|
185024
|
-
if (!
|
|
185025
|
-
|
|
185130
|
+
if (!fs5.existsSync(this.portsDir)) {
|
|
185131
|
+
fs5.mkdirSync(this.portsDir, { recursive: true });
|
|
185026
185132
|
}
|
|
185027
185133
|
const data = {
|
|
185028
185134
|
version: 1,
|
|
185029
185135
|
ports: this.savedPorts
|
|
185030
185136
|
};
|
|
185031
|
-
|
|
185137
|
+
fs5.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
185032
185138
|
}
|
|
185033
185139
|
allocateRandom() {
|
|
185034
185140
|
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
@@ -185054,10 +185160,10 @@ var StablePortAllocator = class {
|
|
|
185054
185160
|
};
|
|
185055
185161
|
|
|
185056
185162
|
// src/lib/dev/database-manager.ts
|
|
185057
|
-
import * as
|
|
185058
|
-
import * as
|
|
185163
|
+
import * as fs8 from "fs";
|
|
185164
|
+
import * as path8 from "path";
|
|
185059
185165
|
import * as net from "net";
|
|
185060
|
-
import * as
|
|
185166
|
+
import * as os4 from "os";
|
|
185061
185167
|
import { spawn } from "child_process";
|
|
185062
185168
|
|
|
185063
185169
|
// src/lib/bin/types.ts
|
|
@@ -185095,17 +185201,17 @@ var ExtractionError = class extends Error {
|
|
|
185095
185201
|
};
|
|
185096
185202
|
|
|
185097
185203
|
// src/lib/bin/manager.ts
|
|
185098
|
-
import * as
|
|
185099
|
-
import * as
|
|
185100
|
-
import * as
|
|
185204
|
+
import * as fs6 from "fs";
|
|
185205
|
+
import * as path6 from "path";
|
|
185206
|
+
import * as os3 from "os";
|
|
185101
185207
|
import { createReadStream } from "fs";
|
|
185102
185208
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185103
185209
|
function getBinBaseDir() {
|
|
185104
|
-
return
|
|
185210
|
+
return path6.join(os3.homedir(), ".specific", "bin");
|
|
185105
185211
|
}
|
|
185106
185212
|
function getPlatformInfo() {
|
|
185107
|
-
const platform5 =
|
|
185108
|
-
const arch3 =
|
|
185213
|
+
const platform5 = os3.platform();
|
|
185214
|
+
const arch3 = os3.arch();
|
|
185109
185215
|
if (platform5 !== "darwin" && platform5 !== "linux") {
|
|
185110
185216
|
throw new Error(
|
|
185111
185217
|
`Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
|
|
@@ -185124,7 +185230,7 @@ function getPlatformInfo() {
|
|
|
185124
185230
|
return { platform: platform5, arch: mappedArch };
|
|
185125
185231
|
}
|
|
185126
185232
|
function getBinaryDir(definition, version, platformInfo) {
|
|
185127
|
-
return
|
|
185233
|
+
return path6.join(
|
|
185128
185234
|
getBinBaseDir(),
|
|
185129
185235
|
definition.name,
|
|
185130
185236
|
version,
|
|
@@ -185134,8 +185240,8 @@ function getBinaryDir(definition, version, platformInfo) {
|
|
|
185134
185240
|
function isBinaryInstalled(definition, version, platformInfo) {
|
|
185135
185241
|
const binDir = getBinaryDir(definition, version, platformInfo);
|
|
185136
185242
|
for (const execPath of definition.executables) {
|
|
185137
|
-
const fullPath =
|
|
185138
|
-
if (!
|
|
185243
|
+
const fullPath = path6.join(binDir, execPath);
|
|
185244
|
+
if (!fs6.existsSync(fullPath)) {
|
|
185139
185245
|
return false;
|
|
185140
185246
|
}
|
|
185141
185247
|
}
|
|
@@ -185163,12 +185269,12 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185163
185269
|
10
|
|
185164
185270
|
);
|
|
185165
185271
|
let bytesDownloaded = 0;
|
|
185166
|
-
const parentDir =
|
|
185167
|
-
if (!
|
|
185168
|
-
|
|
185272
|
+
const parentDir = path6.dirname(destPath);
|
|
185273
|
+
if (!fs6.existsSync(parentDir)) {
|
|
185274
|
+
fs6.mkdirSync(parentDir, { recursive: true });
|
|
185169
185275
|
}
|
|
185170
185276
|
const partPath = destPath + ".part";
|
|
185171
|
-
const fileStream =
|
|
185277
|
+
const fileStream = fs6.createWriteStream(partPath);
|
|
185172
185278
|
try {
|
|
185173
185279
|
const reader = response.body.getReader();
|
|
185174
185280
|
while (true) {
|
|
@@ -185185,18 +185291,18 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185185
185291
|
});
|
|
185186
185292
|
}
|
|
185187
185293
|
}
|
|
185188
|
-
await new Promise((
|
|
185294
|
+
await new Promise((resolve5, reject) => {
|
|
185189
185295
|
fileStream.end((err) => {
|
|
185190
185296
|
if (err) reject(err);
|
|
185191
|
-
else
|
|
185297
|
+
else resolve5();
|
|
185192
185298
|
});
|
|
185193
185299
|
});
|
|
185194
|
-
|
|
185300
|
+
fs6.renameSync(partPath, destPath);
|
|
185195
185301
|
} catch (error) {
|
|
185196
185302
|
try {
|
|
185197
185303
|
fileStream.close();
|
|
185198
|
-
if (
|
|
185199
|
-
|
|
185304
|
+
if (fs6.existsSync(partPath)) {
|
|
185305
|
+
fs6.unlinkSync(partPath);
|
|
185200
185306
|
}
|
|
185201
185307
|
} catch {
|
|
185202
185308
|
}
|
|
@@ -185205,8 +185311,8 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185205
185311
|
}
|
|
185206
185312
|
async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
185207
185313
|
onProgress?.({ phase: "extracting" });
|
|
185208
|
-
if (!
|
|
185209
|
-
|
|
185314
|
+
if (!fs6.existsSync(destDir)) {
|
|
185315
|
+
fs6.mkdirSync(destDir, { recursive: true });
|
|
185210
185316
|
}
|
|
185211
185317
|
try {
|
|
185212
185318
|
const fileStream = createReadStream(archivePath);
|
|
@@ -185214,9 +185320,9 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
|
185214
185320
|
await extractTo(entries, destDir);
|
|
185215
185321
|
onProgress?.({ phase: "finalizing" });
|
|
185216
185322
|
for (const execPath of definition.executables) {
|
|
185217
|
-
const fullPath =
|
|
185218
|
-
if (
|
|
185219
|
-
|
|
185323
|
+
const fullPath = path6.join(destDir, execPath);
|
|
185324
|
+
if (fs6.existsSync(fullPath)) {
|
|
185325
|
+
fs6.chmodSync(fullPath, 493);
|
|
185220
185326
|
}
|
|
185221
185327
|
}
|
|
185222
185328
|
} catch (error) {
|
|
@@ -185240,24 +185346,24 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
185240
185346
|
`Binary type definitions must have exactly one executable, got ${definition.executables.length}`
|
|
185241
185347
|
);
|
|
185242
185348
|
}
|
|
185243
|
-
if (!
|
|
185244
|
-
|
|
185349
|
+
if (!fs6.existsSync(binDir)) {
|
|
185350
|
+
fs6.mkdirSync(binDir, { recursive: true });
|
|
185245
185351
|
}
|
|
185246
|
-
const execPath =
|
|
185352
|
+
const execPath = path6.join(binDir, definition.executables[0]);
|
|
185247
185353
|
await downloadFile(url, execPath, onProgress);
|
|
185248
|
-
|
|
185354
|
+
fs6.chmodSync(execPath, 493);
|
|
185249
185355
|
onProgress?.({ phase: "finalizing" });
|
|
185250
185356
|
} else {
|
|
185251
|
-
const downloadDir =
|
|
185357
|
+
const downloadDir = path6.join(getBinBaseDir(), "downloads");
|
|
185252
185358
|
const archiveName = `${definition.name}-${resolvedVersion}-${platformInfo.platform}-${platformInfo.arch}.tar.gz`;
|
|
185253
|
-
const archivePath =
|
|
185359
|
+
const archivePath = path6.join(downloadDir, archiveName);
|
|
185254
185360
|
try {
|
|
185255
185361
|
await downloadFile(url, archivePath, onProgress);
|
|
185256
185362
|
await extractTarball(archivePath, binDir, definition, onProgress);
|
|
185257
185363
|
} finally {
|
|
185258
185364
|
try {
|
|
185259
|
-
if (
|
|
185260
|
-
|
|
185365
|
+
if (fs6.existsSync(archivePath)) {
|
|
185366
|
+
fs6.unlinkSync(archivePath);
|
|
185261
185367
|
}
|
|
185262
185368
|
} catch {
|
|
185263
185369
|
}
|
|
@@ -185266,10 +185372,10 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
185266
185372
|
}
|
|
185267
185373
|
const executables = {};
|
|
185268
185374
|
for (const execPath of definition.executables) {
|
|
185269
|
-
const name =
|
|
185270
|
-
executables[name] =
|
|
185375
|
+
const name = path6.basename(execPath);
|
|
185376
|
+
executables[name] = path6.join(binDir, execPath);
|
|
185271
185377
|
}
|
|
185272
|
-
const libraryPath = definition.libraryDir ?
|
|
185378
|
+
const libraryPath = definition.libraryDir ? path6.join(binDir, definition.libraryDir) : void 0;
|
|
185273
185379
|
return {
|
|
185274
185380
|
rootDir: binDir,
|
|
185275
185381
|
version: resolvedVersion,
|
|
@@ -185369,17 +185475,17 @@ var drizzleGatewayBinary = {
|
|
|
185369
185475
|
};
|
|
185370
185476
|
|
|
185371
185477
|
// src/lib/dev/debug-logger.ts
|
|
185372
|
-
import * as
|
|
185373
|
-
import * as
|
|
185478
|
+
import * as fs7 from "fs";
|
|
185479
|
+
import * as path7 from "path";
|
|
185374
185480
|
var DEBUG_LOG_PATH = ".specific/debug.log";
|
|
185375
185481
|
var logStream = null;
|
|
185376
185482
|
function getLogStream() {
|
|
185377
185483
|
if (logStream) {
|
|
185378
185484
|
return logStream;
|
|
185379
185485
|
}
|
|
185380
|
-
const logPath =
|
|
185381
|
-
|
|
185382
|
-
logStream =
|
|
185486
|
+
const logPath = path7.join(process.cwd(), DEBUG_LOG_PATH);
|
|
185487
|
+
fs7.mkdirSync(path7.dirname(logPath), { recursive: true });
|
|
185488
|
+
logStream = fs7.createWriteStream(logPath, { flags: "a" });
|
|
185383
185489
|
return logStream;
|
|
185384
185490
|
}
|
|
185385
185491
|
function writeLog(source, message) {
|
|
@@ -185422,7 +185528,7 @@ function getLibraryEnv(binary) {
|
|
|
185422
185528
|
if (!binary.libraryPath) {
|
|
185423
185529
|
return {};
|
|
185424
185530
|
}
|
|
185425
|
-
const platform5 =
|
|
185531
|
+
const platform5 = os4.platform();
|
|
185426
185532
|
if (platform5 === "darwin") {
|
|
185427
185533
|
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
185428
185534
|
} else if (platform5 === "linux") {
|
|
@@ -185432,15 +185538,15 @@ function getLibraryEnv(binary) {
|
|
|
185432
185538
|
}
|
|
185433
185539
|
async function startPostgres(pg, port, dataDir, onProgress) {
|
|
185434
185540
|
const binary = await ensureBinary(postgresBinary, void 0, onProgress);
|
|
185435
|
-
const dbDataPath =
|
|
185541
|
+
const dbDataPath = path8.join(process.cwd(), dataDir, pg.name);
|
|
185436
185542
|
const host = "127.0.0.1";
|
|
185437
185543
|
const user = "postgres";
|
|
185438
185544
|
const password = "postgres";
|
|
185439
185545
|
const libraryEnv = getLibraryEnv(binary);
|
|
185440
185546
|
const env2 = { ...process.env, ...libraryEnv };
|
|
185441
|
-
const dataExists =
|
|
185547
|
+
const dataExists = fs8.existsSync(dbDataPath);
|
|
185442
185548
|
if (!dataExists) {
|
|
185443
|
-
|
|
185549
|
+
fs8.mkdirSync(dbDataPath, { recursive: true });
|
|
185444
185550
|
await runCommand(
|
|
185445
185551
|
binary.executables["initdb"],
|
|
185446
185552
|
["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
|
|
@@ -185516,9 +185622,9 @@ async function startRedis(redis, port, onProgress) {
|
|
|
185516
185622
|
}
|
|
185517
185623
|
async function startStorage(storage, port, dataDir) {
|
|
185518
185624
|
const S3rver = (await import("s3rver")).default;
|
|
185519
|
-
const storageDataPath =
|
|
185520
|
-
if (!
|
|
185521
|
-
|
|
185625
|
+
const storageDataPath = path8.join(process.cwd(), dataDir, storage.name);
|
|
185626
|
+
if (!fs8.existsSync(storageDataPath)) {
|
|
185627
|
+
fs8.mkdirSync(storageDataPath, { recursive: true });
|
|
185522
185628
|
}
|
|
185523
185629
|
const host = "127.0.0.1";
|
|
185524
185630
|
const accessKey = "S3RVER";
|
|
@@ -185552,7 +185658,7 @@ async function startStorage(storage, port, dataDir) {
|
|
|
185552
185658
|
};
|
|
185553
185659
|
}
|
|
185554
185660
|
async function runCommand(command, args, env2) {
|
|
185555
|
-
return new Promise((
|
|
185661
|
+
return new Promise((resolve5, reject) => {
|
|
185556
185662
|
const proc = spawn(command, args, {
|
|
185557
185663
|
stdio: ["ignore", "pipe", "pipe"],
|
|
185558
185664
|
env: env2
|
|
@@ -185563,7 +185669,7 @@ async function runCommand(command, args, env2) {
|
|
|
185563
185669
|
});
|
|
185564
185670
|
proc.on("close", (code) => {
|
|
185565
185671
|
if (code === 0) {
|
|
185566
|
-
|
|
185672
|
+
resolve5();
|
|
185567
185673
|
} else {
|
|
185568
185674
|
reject(new Error(`Command failed with code ${code}: ${stderr}`));
|
|
185569
185675
|
}
|
|
@@ -185572,7 +185678,7 @@ async function runCommand(command, args, env2) {
|
|
|
185572
185678
|
});
|
|
185573
185679
|
}
|
|
185574
185680
|
async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
185575
|
-
return new Promise((
|
|
185681
|
+
return new Promise((resolve5, reject) => {
|
|
185576
185682
|
const proc = spawn(
|
|
185577
185683
|
postgresPath,
|
|
185578
185684
|
["--single", "-D", dataDir, "postgres"],
|
|
@@ -185586,7 +185692,7 @@ async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
|
|
|
185586
185692
|
stderr += data.toString();
|
|
185587
185693
|
});
|
|
185588
185694
|
proc.on("close", (code) => {
|
|
185589
|
-
|
|
185695
|
+
resolve5();
|
|
185590
185696
|
});
|
|
185591
185697
|
proc.on("error", reject);
|
|
185592
185698
|
proc.stdin?.write(`CREATE DATABASE "${dbName}";
|
|
@@ -185606,33 +185712,33 @@ async function waitForTcpPort(host, port, timeoutMs = 3e4) {
|
|
|
185606
185712
|
throw new Error(`Port ${port} did not become available within timeout`);
|
|
185607
185713
|
}
|
|
185608
185714
|
function checkTcpPort(host, port) {
|
|
185609
|
-
return new Promise((
|
|
185715
|
+
return new Promise((resolve5) => {
|
|
185610
185716
|
const socket = new net.Socket();
|
|
185611
185717
|
socket.setTimeout(1e3);
|
|
185612
185718
|
socket.on("connect", () => {
|
|
185613
185719
|
socket.destroy();
|
|
185614
|
-
|
|
185720
|
+
resolve5(true);
|
|
185615
185721
|
});
|
|
185616
185722
|
socket.on("timeout", () => {
|
|
185617
185723
|
socket.destroy();
|
|
185618
|
-
|
|
185724
|
+
resolve5(false);
|
|
185619
185725
|
});
|
|
185620
185726
|
socket.on("error", () => {
|
|
185621
185727
|
socket.destroy();
|
|
185622
|
-
|
|
185728
|
+
resolve5(false);
|
|
185623
185729
|
});
|
|
185624
185730
|
socket.connect(port, host);
|
|
185625
185731
|
});
|
|
185626
185732
|
}
|
|
185627
185733
|
async function stopProcess(proc) {
|
|
185628
|
-
return new Promise((
|
|
185734
|
+
return new Promise((resolve5) => {
|
|
185629
185735
|
if (proc.killed || proc.exitCode !== null) {
|
|
185630
|
-
|
|
185736
|
+
resolve5();
|
|
185631
185737
|
return;
|
|
185632
185738
|
}
|
|
185633
185739
|
proc.once("exit", () => {
|
|
185634
185740
|
clearTimeout(forceKillTimeout);
|
|
185635
|
-
|
|
185741
|
+
resolve5();
|
|
185636
185742
|
});
|
|
185637
185743
|
proc.kill("SIGTERM");
|
|
185638
185744
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -185643,7 +185749,7 @@ async function stopProcess(proc) {
|
|
|
185643
185749
|
});
|
|
185644
185750
|
}
|
|
185645
185751
|
function sleep(ms) {
|
|
185646
|
-
return new Promise((
|
|
185752
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
185647
185753
|
}
|
|
185648
185754
|
|
|
185649
185755
|
// src/lib/dev/service-runner.ts
|
|
@@ -185652,9 +185758,9 @@ import { spawn as spawn2 } from "child_process";
|
|
|
185652
185758
|
// src/lib/secrets/parser.ts
|
|
185653
185759
|
var import_hcl2_json_parser2 = __toESM(require_dist(), 1);
|
|
185654
185760
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
185655
|
-
import { existsSync as
|
|
185656
|
-
import * as
|
|
185657
|
-
import * as
|
|
185761
|
+
import { existsSync as existsSync9 } from "fs";
|
|
185762
|
+
import * as path9 from "path";
|
|
185763
|
+
import * as crypto2 from "crypto";
|
|
185658
185764
|
var { parseToObject: parseToObject2 } = import_hcl2_json_parser2.default;
|
|
185659
185765
|
var SECRETS_FILE = "specific.secrets";
|
|
185660
185766
|
var GENERATED_SECRETS_FILE = ".specific/generated-secrets.json";
|
|
@@ -185672,7 +185778,7 @@ async function parseSecretsFile(content) {
|
|
|
185672
185778
|
return secrets;
|
|
185673
185779
|
}
|
|
185674
185780
|
async function loadSecrets() {
|
|
185675
|
-
if (!
|
|
185781
|
+
if (!existsSync9(SECRETS_FILE)) {
|
|
185676
185782
|
return /* @__PURE__ */ new Map();
|
|
185677
185783
|
}
|
|
185678
185784
|
const content = await readFile(SECRETS_FILE, "utf-8");
|
|
@@ -185680,7 +185786,7 @@ async function loadSecrets() {
|
|
|
185680
185786
|
}
|
|
185681
185787
|
function generateRandomString(length = 64) {
|
|
185682
185788
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
185683
|
-
const bytes =
|
|
185789
|
+
const bytes = crypto2.randomBytes(length);
|
|
185684
185790
|
let result = "";
|
|
185685
185791
|
for (let i = 0; i < length; i++) {
|
|
185686
185792
|
result += chars[bytes[i] % chars.length];
|
|
@@ -185688,7 +185794,7 @@ function generateRandomString(length = 64) {
|
|
|
185688
185794
|
return result;
|
|
185689
185795
|
}
|
|
185690
185796
|
async function loadGeneratedSecrets() {
|
|
185691
|
-
if (!
|
|
185797
|
+
if (!existsSync9(GENERATED_SECRETS_FILE)) {
|
|
185692
185798
|
return /* @__PURE__ */ new Map();
|
|
185693
185799
|
}
|
|
185694
185800
|
const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
|
|
@@ -185697,13 +185803,13 @@ async function loadGeneratedSecrets() {
|
|
|
185697
185803
|
}
|
|
185698
185804
|
async function saveGeneratedSecret(name, value) {
|
|
185699
185805
|
let secrets = {};
|
|
185700
|
-
if (
|
|
185806
|
+
if (existsSync9(GENERATED_SECRETS_FILE)) {
|
|
185701
185807
|
const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
|
|
185702
185808
|
secrets = JSON.parse(content);
|
|
185703
185809
|
}
|
|
185704
185810
|
secrets[name] = value;
|
|
185705
|
-
const dir =
|
|
185706
|
-
if (!
|
|
185811
|
+
const dir = path9.dirname(GENERATED_SECRETS_FILE);
|
|
185812
|
+
if (!existsSync9(dir)) {
|
|
185707
185813
|
await mkdir(dir, { recursive: true });
|
|
185708
185814
|
}
|
|
185709
185815
|
await writeFile(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
|
|
@@ -185733,7 +185839,7 @@ async function prepareSecrets(secretsConfig) {
|
|
|
185733
185839
|
// src/lib/config/parser.ts
|
|
185734
185840
|
var import_hcl2_json_parser3 = __toESM(require_dist(), 1);
|
|
185735
185841
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
185736
|
-
import { existsSync as
|
|
185842
|
+
import { existsSync as existsSync10 } from "fs";
|
|
185737
185843
|
var { parseToObject: parseToObject3 } = import_hcl2_json_parser3.default;
|
|
185738
185844
|
var CONFIG_FILE = "specific.config";
|
|
185739
185845
|
async function parseConfigFile(content) {
|
|
@@ -185750,7 +185856,7 @@ async function parseConfigFile(content) {
|
|
|
185750
185856
|
return configs;
|
|
185751
185857
|
}
|
|
185752
185858
|
async function loadConfigs() {
|
|
185753
|
-
if (!
|
|
185859
|
+
if (!existsSync10(CONFIG_FILE)) {
|
|
185754
185860
|
return /* @__PURE__ */ new Map();
|
|
185755
185861
|
}
|
|
185756
185862
|
const content = await readFile2(CONFIG_FILE, "utf-8");
|
|
@@ -186041,14 +186147,14 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
186041
186147
|
ports: endpointPorts,
|
|
186042
186148
|
process: child,
|
|
186043
186149
|
async stop() {
|
|
186044
|
-
return new Promise((
|
|
186150
|
+
return new Promise((resolve5) => {
|
|
186045
186151
|
if (child.killed || child.exitCode !== null) {
|
|
186046
|
-
|
|
186152
|
+
resolve5();
|
|
186047
186153
|
return;
|
|
186048
186154
|
}
|
|
186049
186155
|
child.once("exit", () => {
|
|
186050
186156
|
clearTimeout(forceKillTimeout);
|
|
186051
|
-
|
|
186157
|
+
resolve5();
|
|
186052
186158
|
});
|
|
186053
186159
|
child.kill("SIGTERM");
|
|
186054
186160
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186062,21 +186168,26 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
186062
186168
|
}
|
|
186063
186169
|
|
|
186064
186170
|
// src/lib/dev/instance-state.ts
|
|
186065
|
-
import * as
|
|
186066
|
-
import * as
|
|
186171
|
+
import * as fs9 from "fs";
|
|
186172
|
+
import * as path10 from "path";
|
|
186067
186173
|
var InstanceStateManager = class {
|
|
186068
186174
|
stateDir;
|
|
186069
186175
|
statePath;
|
|
186070
186176
|
lockPath;
|
|
186071
186177
|
ownsInstances = false;
|
|
186072
|
-
|
|
186073
|
-
|
|
186074
|
-
this.
|
|
186075
|
-
this.
|
|
186178
|
+
key;
|
|
186179
|
+
constructor(projectRoot, key = "default") {
|
|
186180
|
+
this.key = key;
|
|
186181
|
+
this.stateDir = path10.join(projectRoot, ".specific", "keys", key);
|
|
186182
|
+
this.statePath = path10.join(this.stateDir, "state.json");
|
|
186183
|
+
this.lockPath = path10.join(this.stateDir, "state.lock");
|
|
186184
|
+
}
|
|
186185
|
+
getKey() {
|
|
186186
|
+
return this.key;
|
|
186076
186187
|
}
|
|
186077
186188
|
ensureStateDir() {
|
|
186078
|
-
if (!
|
|
186079
|
-
|
|
186189
|
+
if (!fs9.existsSync(this.stateDir)) {
|
|
186190
|
+
fs9.mkdirSync(this.stateDir, { recursive: true });
|
|
186080
186191
|
}
|
|
186081
186192
|
}
|
|
186082
186193
|
isProcessRunning(pid) {
|
|
@@ -186093,15 +186204,15 @@ var InstanceStateManager = class {
|
|
|
186093
186204
|
const startTime = Date.now();
|
|
186094
186205
|
while (Date.now() - startTime < timeoutMs) {
|
|
186095
186206
|
try {
|
|
186096
|
-
const fd =
|
|
186207
|
+
const fd = fs9.openSync(
|
|
186097
186208
|
this.lockPath,
|
|
186098
|
-
|
|
186209
|
+
fs9.constants.O_CREAT | fs9.constants.O_EXCL | fs9.constants.O_WRONLY
|
|
186099
186210
|
);
|
|
186100
|
-
|
|
186101
|
-
|
|
186211
|
+
fs9.writeSync(fd, String(process.pid));
|
|
186212
|
+
fs9.closeSync(fd);
|
|
186102
186213
|
return () => {
|
|
186103
186214
|
try {
|
|
186104
|
-
|
|
186215
|
+
fs9.unlinkSync(this.lockPath);
|
|
186105
186216
|
} catch {
|
|
186106
186217
|
}
|
|
186107
186218
|
};
|
|
@@ -186110,21 +186221,21 @@ var InstanceStateManager = class {
|
|
|
186110
186221
|
if (err.code === "EEXIST") {
|
|
186111
186222
|
try {
|
|
186112
186223
|
const lockPid = parseInt(
|
|
186113
|
-
|
|
186224
|
+
fs9.readFileSync(this.lockPath, "utf-8").trim(),
|
|
186114
186225
|
10
|
|
186115
186226
|
);
|
|
186116
186227
|
if (!this.isProcessRunning(lockPid)) {
|
|
186117
|
-
|
|
186228
|
+
fs9.unlinkSync(this.lockPath);
|
|
186118
186229
|
continue;
|
|
186119
186230
|
}
|
|
186120
186231
|
} catch {
|
|
186121
186232
|
try {
|
|
186122
|
-
|
|
186233
|
+
fs9.unlinkSync(this.lockPath);
|
|
186123
186234
|
} catch {
|
|
186124
186235
|
}
|
|
186125
186236
|
continue;
|
|
186126
186237
|
}
|
|
186127
|
-
await new Promise((
|
|
186238
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
186128
186239
|
} else {
|
|
186129
186240
|
throw e;
|
|
186130
186241
|
}
|
|
@@ -186133,12 +186244,12 @@ var InstanceStateManager = class {
|
|
|
186133
186244
|
throw new Error("Failed to acquire state lock (timeout)");
|
|
186134
186245
|
}
|
|
186135
186246
|
async getExistingInstances() {
|
|
186136
|
-
if (!
|
|
186247
|
+
if (!fs9.existsSync(this.statePath)) {
|
|
186137
186248
|
return null;
|
|
186138
186249
|
}
|
|
186139
186250
|
const releaseLock = await this.acquireLock();
|
|
186140
186251
|
try {
|
|
186141
|
-
const content =
|
|
186252
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186142
186253
|
const state = JSON.parse(content);
|
|
186143
186254
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
186144
186255
|
return null;
|
|
@@ -186151,21 +186262,21 @@ var InstanceStateManager = class {
|
|
|
186151
186262
|
}
|
|
186152
186263
|
}
|
|
186153
186264
|
async cleanStaleState() {
|
|
186154
|
-
if (!
|
|
186265
|
+
if (!fs9.existsSync(this.statePath)) {
|
|
186155
186266
|
return false;
|
|
186156
186267
|
}
|
|
186157
186268
|
const releaseLock = await this.acquireLock();
|
|
186158
186269
|
try {
|
|
186159
|
-
const content =
|
|
186270
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186160
186271
|
const state = JSON.parse(content);
|
|
186161
186272
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
186162
|
-
|
|
186273
|
+
fs9.unlinkSync(this.statePath);
|
|
186163
186274
|
return true;
|
|
186164
186275
|
}
|
|
186165
186276
|
return false;
|
|
186166
186277
|
} catch {
|
|
186167
186278
|
try {
|
|
186168
|
-
|
|
186279
|
+
fs9.unlinkSync(this.statePath);
|
|
186169
186280
|
return true;
|
|
186170
186281
|
} catch {
|
|
186171
186282
|
}
|
|
@@ -186177,8 +186288,8 @@ var InstanceStateManager = class {
|
|
|
186177
186288
|
async claimOwnership(command) {
|
|
186178
186289
|
const releaseLock = await this.acquireLock();
|
|
186179
186290
|
try {
|
|
186180
|
-
if (
|
|
186181
|
-
const content =
|
|
186291
|
+
if (fs9.existsSync(this.statePath)) {
|
|
186292
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186182
186293
|
const state2 = JSON.parse(content);
|
|
186183
186294
|
if (this.isProcessRunning(state2.owner.pid)) {
|
|
186184
186295
|
throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
|
|
@@ -186247,8 +186358,8 @@ var InstanceStateManager = class {
|
|
|
186247
186358
|
}
|
|
186248
186359
|
const releaseLock = await this.acquireLock();
|
|
186249
186360
|
try {
|
|
186250
|
-
if (
|
|
186251
|
-
|
|
186361
|
+
if (fs9.existsSync(this.statePath)) {
|
|
186362
|
+
fs9.unlinkSync(this.statePath);
|
|
186252
186363
|
}
|
|
186253
186364
|
this.ownsInstances = false;
|
|
186254
186365
|
} finally {
|
|
@@ -186256,31 +186367,31 @@ var InstanceStateManager = class {
|
|
|
186256
186367
|
}
|
|
186257
186368
|
}
|
|
186258
186369
|
readState() {
|
|
186259
|
-
const content =
|
|
186370
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186260
186371
|
return JSON.parse(content);
|
|
186261
186372
|
}
|
|
186262
186373
|
writeStateAtomic(state) {
|
|
186263
186374
|
this.ensureStateDir();
|
|
186264
186375
|
const tmpPath = this.statePath + ".tmp";
|
|
186265
|
-
|
|
186266
|
-
|
|
186376
|
+
fs9.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
186377
|
+
fs9.renameSync(tmpPath, this.statePath);
|
|
186267
186378
|
}
|
|
186268
186379
|
};
|
|
186269
186380
|
|
|
186270
186381
|
// src/lib/dev/http-proxy.ts
|
|
186271
186382
|
import * as http from "http";
|
|
186272
186383
|
import * as https from "https";
|
|
186273
|
-
import * as
|
|
186274
|
-
import * as
|
|
186384
|
+
import * as fs10 from "fs";
|
|
186385
|
+
import * as path11 from "path";
|
|
186275
186386
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
186276
186387
|
import httpProxy from "http-proxy";
|
|
186277
|
-
var __dirname2 =
|
|
186278
|
-
var adminDir =
|
|
186388
|
+
var __dirname2 = path11.dirname(fileURLToPath2(import.meta.url));
|
|
186389
|
+
var adminDir = path11.join(__dirname2, "admin");
|
|
186279
186390
|
var HTTP_PORT = 80;
|
|
186280
186391
|
var HTTPS_PORT = 443;
|
|
186281
186392
|
var DOMAIN_SUFFIX = ".local.spcf.app";
|
|
186282
186393
|
var ADMIN_DOMAIN = "local.spcf.app";
|
|
186283
|
-
var
|
|
186394
|
+
var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
|
|
186284
186395
|
var MIME_TYPES = {
|
|
186285
186396
|
".html": "text/html",
|
|
186286
186397
|
".css": "text/css",
|
|
@@ -186295,11 +186406,31 @@ var MIME_TYPES = {
|
|
|
186295
186406
|
".woff2": "font/woff2",
|
|
186296
186407
|
".txt": "text/plain"
|
|
186297
186408
|
};
|
|
186298
|
-
async function startHttpProxy(services, certificate, getState,
|
|
186409
|
+
async function startHttpProxy(services, certificate, getState, instanceKey = "default") {
|
|
186299
186410
|
const serviceMap = /* @__PURE__ */ new Map();
|
|
186411
|
+
const adminPortMap = /* @__PURE__ */ new Map();
|
|
186412
|
+
const drizzleGatewayPortMap = /* @__PURE__ */ new Map();
|
|
186413
|
+
const buildMapKey = (serviceName, key) => {
|
|
186414
|
+
return key === "default" ? serviceName : `${serviceName}.${key}`;
|
|
186415
|
+
};
|
|
186300
186416
|
for (const svc of services) {
|
|
186301
|
-
serviceMap.set(svc.name, svc.port);
|
|
186417
|
+
serviceMap.set(buildMapKey(svc.name, instanceKey), svc.port);
|
|
186302
186418
|
}
|
|
186419
|
+
const updateServices = (registryServices, keys) => {
|
|
186420
|
+
serviceMap.clear();
|
|
186421
|
+
adminPortMap.clear();
|
|
186422
|
+
drizzleGatewayPortMap.clear();
|
|
186423
|
+
for (const svc of registryServices) {
|
|
186424
|
+
serviceMap.set(buildMapKey(svc.serviceName, svc.key), svc.port);
|
|
186425
|
+
}
|
|
186426
|
+
for (const [key, registration] of Object.entries(keys)) {
|
|
186427
|
+
adminPortMap.set(key, registration.adminPort);
|
|
186428
|
+
if (registration.drizzleGatewayPort !== void 0) {
|
|
186429
|
+
drizzleGatewayPortMap.set(key, registration.drizzleGatewayPort);
|
|
186430
|
+
}
|
|
186431
|
+
}
|
|
186432
|
+
writeLog("proxy", `Updated service map: ${serviceMap.size} services, ${adminPortMap.size} admin instances, ${drizzleGatewayPortMap.size} drizzle gateways`);
|
|
186433
|
+
};
|
|
186303
186434
|
const { key: defaultKey, cert: defaultCert } = certificate;
|
|
186304
186435
|
const proxy = httpProxy.createProxyServer({});
|
|
186305
186436
|
proxy.on("error", (err, req, res) => {
|
|
@@ -186312,39 +186443,65 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186312
186443
|
});
|
|
186313
186444
|
const handleRequest = (req, res) => {
|
|
186314
186445
|
const host = req.headers.host || "";
|
|
186315
|
-
const
|
|
186316
|
-
|
|
186317
|
-
|
|
186318
|
-
|
|
186319
|
-
|
|
186320
|
-
|
|
186321
|
-
|
|
186322
|
-
|
|
186446
|
+
const hostname2 = host.split(":")[0];
|
|
186447
|
+
const drizzleKey = extractDrizzleGatewayKey(host);
|
|
186448
|
+
if (drizzleKey !== null) {
|
|
186449
|
+
const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
|
|
186450
|
+
if (drizzlePort) {
|
|
186451
|
+
proxy.web(req, res, {
|
|
186452
|
+
target: `http://127.0.0.1:${drizzlePort}`
|
|
186453
|
+
});
|
|
186454
|
+
return;
|
|
186455
|
+
}
|
|
186456
|
+
res.writeHead(503, { "Content-Type": "text/html" });
|
|
186457
|
+
res.end("<h1>Database Viewer</h1><p>No database viewer running for this instance.</p>");
|
|
186323
186458
|
return;
|
|
186324
186459
|
}
|
|
186325
|
-
const
|
|
186326
|
-
if (
|
|
186327
|
-
|
|
186460
|
+
const adminKey = extractAdminKey(host);
|
|
186461
|
+
if (adminKey !== null) {
|
|
186462
|
+
const adminPort = adminPortMap.get(adminKey);
|
|
186463
|
+
if (adminPort) {
|
|
186464
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
186465
|
+
if (url.pathname.startsWith("/api/")) {
|
|
186466
|
+
proxy.web(req, res, { target: `http://127.0.0.1:${adminPort}` });
|
|
186467
|
+
return;
|
|
186468
|
+
}
|
|
186469
|
+
serveStaticFile(res, url.pathname);
|
|
186470
|
+
return;
|
|
186471
|
+
}
|
|
186472
|
+
if (adminKey === "default" && hostname2 === ADMIN_DOMAIN) {
|
|
186473
|
+
res.writeHead(503, { "Content-Type": "text/html" });
|
|
186474
|
+
res.end("<h1>Admin UI</h1><p>No dev instance running. Start <code>specific dev</code> first.</p>");
|
|
186475
|
+
return;
|
|
186476
|
+
}
|
|
186477
|
+
}
|
|
186478
|
+
const route = extractServiceAndKey(host);
|
|
186479
|
+
if (!route) {
|
|
186480
|
+
return sendNotFound(res, null, serviceMap);
|
|
186328
186481
|
}
|
|
186329
|
-
const
|
|
186482
|
+
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
186483
|
+
const targetPort = serviceMap.get(mapKey);
|
|
186330
186484
|
if (!targetPort) {
|
|
186331
|
-
return sendNotFound(res, serviceName,
|
|
186485
|
+
return sendNotFound(res, route.serviceName, serviceMap);
|
|
186332
186486
|
}
|
|
186333
186487
|
proxy.web(req, res, { target: `http://127.0.0.1:${targetPort}` });
|
|
186334
186488
|
};
|
|
186335
186489
|
const handleUpgrade = (req, socket, head) => {
|
|
186336
186490
|
const host = req.headers.host || "";
|
|
186337
|
-
const
|
|
186338
|
-
if (
|
|
186339
|
-
|
|
186340
|
-
|
|
186491
|
+
const adminKey = extractAdminKey(host);
|
|
186492
|
+
if (adminKey !== null) {
|
|
186493
|
+
if (adminPortMap.has(adminKey)) {
|
|
186494
|
+
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186495
|
+
return;
|
|
186496
|
+
}
|
|
186341
186497
|
}
|
|
186342
|
-
const
|
|
186343
|
-
if (!
|
|
186498
|
+
const route = extractServiceAndKey(host);
|
|
186499
|
+
if (!route) {
|
|
186344
186500
|
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186345
186501
|
return;
|
|
186346
186502
|
}
|
|
186347
|
-
const
|
|
186503
|
+
const mapKey = buildMapKey(route.serviceName, route.key);
|
|
186504
|
+
const targetPort = serviceMap.get(mapKey);
|
|
186348
186505
|
if (!targetPort) {
|
|
186349
186506
|
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
186350
186507
|
return;
|
|
@@ -186361,7 +186518,7 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186361
186518
|
handleRequest
|
|
186362
186519
|
);
|
|
186363
186520
|
httpsServer.on("upgrade", handleUpgrade);
|
|
186364
|
-
return new Promise((
|
|
186521
|
+
return new Promise((resolve5, reject) => {
|
|
186365
186522
|
let httpStarted = false;
|
|
186366
186523
|
let httpsStarted = false;
|
|
186367
186524
|
let failed = false;
|
|
@@ -186372,17 +186529,25 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186372
186529
|
"proxy",
|
|
186373
186530
|
`HTTP/HTTPS proxy started on ports ${HTTP_PORT}/${HTTPS_PORT}`
|
|
186374
186531
|
);
|
|
186375
|
-
|
|
186532
|
+
resolve5({
|
|
186376
186533
|
httpPort: HTTP_PORT,
|
|
186377
186534
|
httpsPort: HTTPS_PORT,
|
|
186535
|
+
updateServices,
|
|
186536
|
+
updateCertificate(newCert) {
|
|
186537
|
+
httpsServer.setSecureContext({
|
|
186538
|
+
key: newCert.key,
|
|
186539
|
+
cert: newCert.cert
|
|
186540
|
+
});
|
|
186541
|
+
writeLog("proxy", "Certificate updated");
|
|
186542
|
+
},
|
|
186378
186543
|
async stop() {
|
|
186379
|
-
return new Promise((
|
|
186544
|
+
return new Promise((resolve6) => {
|
|
186380
186545
|
let closed = 0;
|
|
186381
186546
|
const onClose = () => {
|
|
186382
186547
|
closed++;
|
|
186383
186548
|
if (closed === 2) {
|
|
186384
186549
|
clearTimeout(forceCloseTimeout);
|
|
186385
|
-
|
|
186550
|
+
resolve6();
|
|
186386
186551
|
}
|
|
186387
186552
|
};
|
|
186388
186553
|
httpServer.close(onClose);
|
|
@@ -186390,7 +186555,7 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186390
186555
|
const forceCloseTimeout = setTimeout(() => {
|
|
186391
186556
|
httpServer.closeAllConnections?.();
|
|
186392
186557
|
httpsServer.closeAllConnections?.();
|
|
186393
|
-
|
|
186558
|
+
resolve6();
|
|
186394
186559
|
}, 2e3);
|
|
186395
186560
|
});
|
|
186396
186561
|
}
|
|
@@ -186424,15 +186589,70 @@ async function startHttpProxy(services, certificate, getState, drizzleGatewayPor
|
|
|
186424
186589
|
});
|
|
186425
186590
|
});
|
|
186426
186591
|
}
|
|
186427
|
-
function
|
|
186428
|
-
const
|
|
186429
|
-
if (!
|
|
186592
|
+
function extractServiceAndKey(host) {
|
|
186593
|
+
const hostname2 = host.split(":")[0];
|
|
186594
|
+
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186430
186595
|
return null;
|
|
186431
186596
|
}
|
|
186432
|
-
|
|
186597
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186598
|
+
if (!prefix) {
|
|
186599
|
+
return null;
|
|
186600
|
+
}
|
|
186601
|
+
const parts = prefix.split(".");
|
|
186602
|
+
if (parts.length === 1) {
|
|
186603
|
+
return { serviceName: parts[0], key: "default" };
|
|
186604
|
+
} else if (parts.length === 2) {
|
|
186605
|
+
return { serviceName: parts[0], key: parts[1] };
|
|
186606
|
+
}
|
|
186607
|
+
return null;
|
|
186433
186608
|
}
|
|
186434
|
-
function
|
|
186435
|
-
const
|
|
186609
|
+
function extractDrizzleGatewayKey(host) {
|
|
186610
|
+
const hostname2 = host.split(":")[0];
|
|
186611
|
+
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186612
|
+
return null;
|
|
186613
|
+
}
|
|
186614
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186615
|
+
if (!prefix) {
|
|
186616
|
+
return null;
|
|
186617
|
+
}
|
|
186618
|
+
const parts = prefix.split(".");
|
|
186619
|
+
if (parts.length === 1 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
|
|
186620
|
+
return "default";
|
|
186621
|
+
} else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
|
|
186622
|
+
return parts[1];
|
|
186623
|
+
}
|
|
186624
|
+
return null;
|
|
186625
|
+
}
|
|
186626
|
+
function extractAdminKey(host) {
|
|
186627
|
+
const hostname2 = host.split(":")[0];
|
|
186628
|
+
if (!hostname2) {
|
|
186629
|
+
return null;
|
|
186630
|
+
}
|
|
186631
|
+
if (hostname2 === ADMIN_DOMAIN) {
|
|
186632
|
+
return "default";
|
|
186633
|
+
}
|
|
186634
|
+
if (!hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186635
|
+
return null;
|
|
186636
|
+
}
|
|
186637
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186638
|
+
if (!prefix) {
|
|
186639
|
+
return null;
|
|
186640
|
+
}
|
|
186641
|
+
const parts = prefix.split(".");
|
|
186642
|
+
if (parts.length === 1) {
|
|
186643
|
+
return parts[0];
|
|
186644
|
+
}
|
|
186645
|
+
return null;
|
|
186646
|
+
}
|
|
186647
|
+
function sendNotFound(res, requestedService, serviceMap) {
|
|
186648
|
+
const serviceList = Array.from(serviceMap.keys()).map((key) => {
|
|
186649
|
+
const parts = key.split(".");
|
|
186650
|
+
if (parts.length === 1) {
|
|
186651
|
+
return `<li>${parts[0]}.local.spcf.app</li>`;
|
|
186652
|
+
} else {
|
|
186653
|
+
return `<li>${key}.local.spcf.app</li>`;
|
|
186654
|
+
}
|
|
186655
|
+
}).join("\n");
|
|
186436
186656
|
const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
|
|
186437
186657
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
186438
186658
|
res.end(`<!DOCTYPE html>
|
|
@@ -186441,61 +186661,36 @@ function sendNotFound(res, requestedService, availableServices) {
|
|
|
186441
186661
|
<body>
|
|
186442
186662
|
<h1>Service Not Found</h1>
|
|
186443
186663
|
<p>${message}</p>
|
|
186444
|
-
${
|
|
186664
|
+
${serviceMap.size > 0 ? `<p>Available services:</p>
|
|
186445
186665
|
<ul>${serviceList}</ul>` : "<p>No services are currently exposed.</p>"}
|
|
186446
186666
|
</body>
|
|
186447
186667
|
</html>`);
|
|
186448
186668
|
}
|
|
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
186669
|
function serveStaticFile(res, pathname) {
|
|
186475
186670
|
let filePath = pathname;
|
|
186476
186671
|
if (filePath === "/" || filePath.endsWith("/")) {
|
|
186477
186672
|
filePath = filePath + "index.html";
|
|
186478
186673
|
}
|
|
186479
186674
|
const relativePath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
186480
|
-
const fullPath =
|
|
186481
|
-
const resolvedPath =
|
|
186482
|
-
const resolvedAdminDir =
|
|
186675
|
+
const fullPath = path11.join(adminDir, relativePath);
|
|
186676
|
+
const resolvedPath = path11.resolve(fullPath);
|
|
186677
|
+
const resolvedAdminDir = path11.resolve(adminDir);
|
|
186483
186678
|
if (!resolvedPath.startsWith(resolvedAdminDir)) {
|
|
186484
186679
|
res.writeHead(403, { "Content-Type": "text/html" });
|
|
186485
186680
|
res.end("<h1>Forbidden</h1>");
|
|
186486
186681
|
return;
|
|
186487
186682
|
}
|
|
186488
|
-
if (!
|
|
186683
|
+
if (!fs10.existsSync(resolvedPath)) {
|
|
186489
186684
|
const htmlPath = resolvedPath + ".html";
|
|
186490
|
-
if (
|
|
186685
|
+
if (fs10.existsSync(htmlPath)) {
|
|
186491
186686
|
return serveFile(res, htmlPath);
|
|
186492
186687
|
}
|
|
186493
|
-
const indexPath =
|
|
186494
|
-
if (
|
|
186688
|
+
const indexPath = path11.join(resolvedPath, "index.html");
|
|
186689
|
+
if (fs10.existsSync(indexPath)) {
|
|
186495
186690
|
return serveFile(res, indexPath);
|
|
186496
186691
|
}
|
|
186497
|
-
const notFoundPath =
|
|
186498
|
-
if (
|
|
186692
|
+
const notFoundPath = path11.join(adminDir, "404.html");
|
|
186693
|
+
if (fs10.existsSync(notFoundPath)) {
|
|
186499
186694
|
return serveFileContent(res, notFoundPath, "text/html", 404);
|
|
186500
186695
|
}
|
|
186501
186696
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
@@ -186505,13 +186700,13 @@ function serveStaticFile(res, pathname) {
|
|
|
186505
186700
|
serveFile(res, resolvedPath);
|
|
186506
186701
|
}
|
|
186507
186702
|
function serveFile(res, filePath) {
|
|
186508
|
-
const ext =
|
|
186703
|
+
const ext = path11.extname(filePath).toLowerCase();
|
|
186509
186704
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
186510
186705
|
serveFileContent(res, filePath, contentType, 200);
|
|
186511
186706
|
}
|
|
186512
186707
|
function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
186513
186708
|
try {
|
|
186514
|
-
const content =
|
|
186709
|
+
const content = fs10.readFileSync(filePath);
|
|
186515
186710
|
res.writeHead(statusCode, { "Content-Type": contentType });
|
|
186516
186711
|
res.end(content);
|
|
186517
186712
|
} catch (err) {
|
|
@@ -186522,6 +186717,48 @@ function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
|
186522
186717
|
res.end("<h1>Internal Server Error</h1>");
|
|
186523
186718
|
}
|
|
186524
186719
|
}
|
|
186720
|
+
async function startAdminServer(getState) {
|
|
186721
|
+
return new Promise((resolve5, reject) => {
|
|
186722
|
+
const server = http.createServer((req, res) => {
|
|
186723
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
186724
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
186725
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
186726
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
186727
|
+
if (req.method === "OPTIONS") {
|
|
186728
|
+
res.writeHead(204);
|
|
186729
|
+
res.end();
|
|
186730
|
+
return;
|
|
186731
|
+
}
|
|
186732
|
+
const pathname = url.pathname.endsWith("/") && url.pathname.length > 1 ? url.pathname.slice(0, -1) : url.pathname;
|
|
186733
|
+
if (pathname === "/api/state") {
|
|
186734
|
+
res.setHeader("Content-Type", "application/json");
|
|
186735
|
+
const state = getState();
|
|
186736
|
+
res.writeHead(200);
|
|
186737
|
+
res.end(JSON.stringify(state));
|
|
186738
|
+
return;
|
|
186739
|
+
}
|
|
186740
|
+
res.setHeader("Content-Type", "application/json");
|
|
186741
|
+
res.writeHead(404);
|
|
186742
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
186743
|
+
});
|
|
186744
|
+
server.on("error", reject);
|
|
186745
|
+
server.listen(0, "127.0.0.1", () => {
|
|
186746
|
+
const addr = server.address();
|
|
186747
|
+
if (!addr || typeof addr === "string") {
|
|
186748
|
+
reject(new Error("Failed to get server address"));
|
|
186749
|
+
return;
|
|
186750
|
+
}
|
|
186751
|
+
const port = addr.port;
|
|
186752
|
+
writeLog("admin", `Admin API server started on port ${port}`);
|
|
186753
|
+
resolve5({
|
|
186754
|
+
port,
|
|
186755
|
+
stop: () => new Promise((res, rej) => {
|
|
186756
|
+
server.close((err) => err ? rej(err) : res());
|
|
186757
|
+
})
|
|
186758
|
+
});
|
|
186759
|
+
});
|
|
186760
|
+
});
|
|
186761
|
+
}
|
|
186525
186762
|
|
|
186526
186763
|
// src/lib/dev/electric-manager.ts
|
|
186527
186764
|
import * as net2 from "net";
|
|
@@ -186579,33 +186816,33 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
|
|
|
186579
186816
|
throw new Error(`Electric port ${port} did not become available within timeout`);
|
|
186580
186817
|
}
|
|
186581
186818
|
function checkTcpPort2(host, port) {
|
|
186582
|
-
return new Promise((
|
|
186819
|
+
return new Promise((resolve5) => {
|
|
186583
186820
|
const socket = new net2.Socket();
|
|
186584
186821
|
socket.setTimeout(1e3);
|
|
186585
186822
|
socket.on("connect", () => {
|
|
186586
186823
|
socket.destroy();
|
|
186587
|
-
|
|
186824
|
+
resolve5(true);
|
|
186588
186825
|
});
|
|
186589
186826
|
socket.on("timeout", () => {
|
|
186590
186827
|
socket.destroy();
|
|
186591
|
-
|
|
186828
|
+
resolve5(false);
|
|
186592
186829
|
});
|
|
186593
186830
|
socket.on("error", () => {
|
|
186594
186831
|
socket.destroy();
|
|
186595
|
-
|
|
186832
|
+
resolve5(false);
|
|
186596
186833
|
});
|
|
186597
186834
|
socket.connect(port, host);
|
|
186598
186835
|
});
|
|
186599
186836
|
}
|
|
186600
186837
|
async function stopProcess2(proc) {
|
|
186601
|
-
return new Promise((
|
|
186838
|
+
return new Promise((resolve5) => {
|
|
186602
186839
|
if (proc.killed || proc.exitCode !== null) {
|
|
186603
|
-
|
|
186840
|
+
resolve5();
|
|
186604
186841
|
return;
|
|
186605
186842
|
}
|
|
186606
186843
|
proc.once("exit", () => {
|
|
186607
186844
|
clearTimeout(forceKillTimeout);
|
|
186608
|
-
|
|
186845
|
+
resolve5();
|
|
186609
186846
|
});
|
|
186610
186847
|
proc.kill("SIGTERM");
|
|
186611
186848
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186616,13 +186853,13 @@ async function stopProcess2(proc) {
|
|
|
186616
186853
|
});
|
|
186617
186854
|
}
|
|
186618
186855
|
function sleep2(ms) {
|
|
186619
|
-
return new Promise((
|
|
186856
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
186620
186857
|
}
|
|
186621
186858
|
|
|
186622
186859
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
186623
186860
|
import * as net3 from "net";
|
|
186624
|
-
import * as
|
|
186625
|
-
import * as
|
|
186861
|
+
import * as fs11 from "fs";
|
|
186862
|
+
import * as path12 from "path";
|
|
186626
186863
|
import { spawn as spawn4 } from "child_process";
|
|
186627
186864
|
import { randomUUID } from "crypto";
|
|
186628
186865
|
function generateStoreJson(postgresInstances) {
|
|
@@ -186654,13 +186891,13 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
|
|
|
186654
186891
|
options2?.onProgress
|
|
186655
186892
|
);
|
|
186656
186893
|
const host = "127.0.0.1";
|
|
186657
|
-
const drizzleConfigDir =
|
|
186658
|
-
if (!
|
|
186659
|
-
|
|
186894
|
+
const drizzleConfigDir = path12.join(configDir, "drizzle-gateway");
|
|
186895
|
+
if (!fs11.existsSync(drizzleConfigDir)) {
|
|
186896
|
+
fs11.mkdirSync(drizzleConfigDir, { recursive: true });
|
|
186660
186897
|
}
|
|
186661
186898
|
const storeJson = generateStoreJson(postgresInstances);
|
|
186662
|
-
const storeJsonPath =
|
|
186663
|
-
|
|
186899
|
+
const storeJsonPath = path12.join(drizzleConfigDir, "store.json");
|
|
186900
|
+
fs11.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
|
|
186664
186901
|
writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
|
|
186665
186902
|
writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
|
|
186666
186903
|
writeLog("drizzle-gateway", `PORT: ${port}`);
|
|
@@ -186700,33 +186937,33 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
|
|
|
186700
186937
|
);
|
|
186701
186938
|
}
|
|
186702
186939
|
function checkTcpPort3(host, port) {
|
|
186703
|
-
return new Promise((
|
|
186940
|
+
return new Promise((resolve5) => {
|
|
186704
186941
|
const socket = new net3.Socket();
|
|
186705
186942
|
socket.setTimeout(1e3);
|
|
186706
186943
|
socket.on("connect", () => {
|
|
186707
186944
|
socket.destroy();
|
|
186708
|
-
|
|
186945
|
+
resolve5(true);
|
|
186709
186946
|
});
|
|
186710
186947
|
socket.on("timeout", () => {
|
|
186711
186948
|
socket.destroy();
|
|
186712
|
-
|
|
186949
|
+
resolve5(false);
|
|
186713
186950
|
});
|
|
186714
186951
|
socket.on("error", () => {
|
|
186715
186952
|
socket.destroy();
|
|
186716
|
-
|
|
186953
|
+
resolve5(false);
|
|
186717
186954
|
});
|
|
186718
186955
|
socket.connect(port, host);
|
|
186719
186956
|
});
|
|
186720
186957
|
}
|
|
186721
186958
|
async function stopProcess3(proc) {
|
|
186722
|
-
return new Promise((
|
|
186959
|
+
return new Promise((resolve5) => {
|
|
186723
186960
|
if (proc.killed || proc.exitCode !== null) {
|
|
186724
|
-
|
|
186961
|
+
resolve5();
|
|
186725
186962
|
return;
|
|
186726
186963
|
}
|
|
186727
186964
|
proc.once("exit", () => {
|
|
186728
186965
|
clearTimeout(forceKillTimeout);
|
|
186729
|
-
|
|
186966
|
+
resolve5();
|
|
186730
186967
|
});
|
|
186731
186968
|
proc.kill("SIGTERM");
|
|
186732
186969
|
const forceKillTimeout = setTimeout(() => {
|
|
@@ -186737,7 +186974,7 @@ async function stopProcess3(proc) {
|
|
|
186737
186974
|
});
|
|
186738
186975
|
}
|
|
186739
186976
|
function sleep3(ms) {
|
|
186740
|
-
return new Promise((
|
|
186977
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
186741
186978
|
}
|
|
186742
186979
|
|
|
186743
186980
|
// src/lib/dev/sync-detector.ts
|
|
@@ -186928,19 +187165,345 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
186928
187165
|
};
|
|
186929
187166
|
}
|
|
186930
187167
|
|
|
187168
|
+
// src/lib/dev/proxy-registry.ts
|
|
187169
|
+
import * as fs12 from "fs";
|
|
187170
|
+
import * as path13 from "path";
|
|
187171
|
+
import * as os5 from "os";
|
|
187172
|
+
var ProxyRegistryManager = class {
|
|
187173
|
+
proxyDir;
|
|
187174
|
+
ownerPath;
|
|
187175
|
+
registryPath;
|
|
187176
|
+
lockPath;
|
|
187177
|
+
isOwner = false;
|
|
187178
|
+
registryWatcher = null;
|
|
187179
|
+
constructor() {
|
|
187180
|
+
this.proxyDir = path13.join(os5.homedir(), ".specific", "proxy");
|
|
187181
|
+
this.ownerPath = path13.join(this.proxyDir, "owner.json");
|
|
187182
|
+
this.registryPath = path13.join(this.proxyDir, "registry.json");
|
|
187183
|
+
this.lockPath = path13.join(this.proxyDir, "registry.lock");
|
|
187184
|
+
}
|
|
187185
|
+
ensureProxyDir() {
|
|
187186
|
+
if (!fs12.existsSync(this.proxyDir)) {
|
|
187187
|
+
fs12.mkdirSync(this.proxyDir, { recursive: true });
|
|
187188
|
+
}
|
|
187189
|
+
}
|
|
187190
|
+
isProcessRunning(pid) {
|
|
187191
|
+
try {
|
|
187192
|
+
process.kill(pid, 0);
|
|
187193
|
+
return true;
|
|
187194
|
+
} catch (e) {
|
|
187195
|
+
const err = e;
|
|
187196
|
+
return err.code !== "ESRCH";
|
|
187197
|
+
}
|
|
187198
|
+
}
|
|
187199
|
+
async acquireLock(timeoutMs = 5e3) {
|
|
187200
|
+
this.ensureProxyDir();
|
|
187201
|
+
const startTime = Date.now();
|
|
187202
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
187203
|
+
try {
|
|
187204
|
+
const fd = fs12.openSync(
|
|
187205
|
+
this.lockPath,
|
|
187206
|
+
fs12.constants.O_CREAT | fs12.constants.O_EXCL | fs12.constants.O_WRONLY
|
|
187207
|
+
);
|
|
187208
|
+
fs12.writeSync(fd, String(process.pid));
|
|
187209
|
+
fs12.closeSync(fd);
|
|
187210
|
+
return () => {
|
|
187211
|
+
try {
|
|
187212
|
+
fs12.unlinkSync(this.lockPath);
|
|
187213
|
+
} catch {
|
|
187214
|
+
}
|
|
187215
|
+
};
|
|
187216
|
+
} catch (e) {
|
|
187217
|
+
const err = e;
|
|
187218
|
+
if (err.code === "EEXIST") {
|
|
187219
|
+
try {
|
|
187220
|
+
const lockPid = parseInt(
|
|
187221
|
+
fs12.readFileSync(this.lockPath, "utf-8").trim(),
|
|
187222
|
+
10
|
|
187223
|
+
);
|
|
187224
|
+
if (!this.isProcessRunning(lockPid)) {
|
|
187225
|
+
fs12.unlinkSync(this.lockPath);
|
|
187226
|
+
continue;
|
|
187227
|
+
}
|
|
187228
|
+
} catch {
|
|
187229
|
+
try {
|
|
187230
|
+
fs12.unlinkSync(this.lockPath);
|
|
187231
|
+
} catch {
|
|
187232
|
+
}
|
|
187233
|
+
continue;
|
|
187234
|
+
}
|
|
187235
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
187236
|
+
} else {
|
|
187237
|
+
throw e;
|
|
187238
|
+
}
|
|
187239
|
+
}
|
|
187240
|
+
}
|
|
187241
|
+
throw new Error("Failed to acquire proxy registry lock (timeout)");
|
|
187242
|
+
}
|
|
187243
|
+
/**
|
|
187244
|
+
* Try to claim ownership of the proxy.
|
|
187245
|
+
* Returns true if this process is now the owner.
|
|
187246
|
+
*/
|
|
187247
|
+
async claimProxyOwnership(key) {
|
|
187248
|
+
const releaseLock = await this.acquireLock();
|
|
187249
|
+
try {
|
|
187250
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187251
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187252
|
+
const ownerFile2 = JSON.parse(content);
|
|
187253
|
+
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187254
|
+
return false;
|
|
187255
|
+
}
|
|
187256
|
+
}
|
|
187257
|
+
const ownerFile = {
|
|
187258
|
+
version: 1,
|
|
187259
|
+
owner: {
|
|
187260
|
+
pid: process.pid,
|
|
187261
|
+
key,
|
|
187262
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187263
|
+
}
|
|
187264
|
+
};
|
|
187265
|
+
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
187266
|
+
this.isOwner = true;
|
|
187267
|
+
return true;
|
|
187268
|
+
} finally {
|
|
187269
|
+
releaseLock();
|
|
187270
|
+
}
|
|
187271
|
+
}
|
|
187272
|
+
/**
|
|
187273
|
+
* Release ownership of the proxy.
|
|
187274
|
+
*/
|
|
187275
|
+
async releaseProxyOwnership() {
|
|
187276
|
+
if (!this.isOwner) {
|
|
187277
|
+
return;
|
|
187278
|
+
}
|
|
187279
|
+
const releaseLock = await this.acquireLock();
|
|
187280
|
+
try {
|
|
187281
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187282
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187283
|
+
const ownerFile = JSON.parse(content);
|
|
187284
|
+
if (ownerFile.owner.pid === process.pid) {
|
|
187285
|
+
fs12.unlinkSync(this.ownerPath);
|
|
187286
|
+
}
|
|
187287
|
+
}
|
|
187288
|
+
this.isOwner = false;
|
|
187289
|
+
} finally {
|
|
187290
|
+
releaseLock();
|
|
187291
|
+
}
|
|
187292
|
+
}
|
|
187293
|
+
/**
|
|
187294
|
+
* Get the current proxy owner.
|
|
187295
|
+
*/
|
|
187296
|
+
async getProxyOwner() {
|
|
187297
|
+
if (!fs12.existsSync(this.ownerPath)) {
|
|
187298
|
+
return null;
|
|
187299
|
+
}
|
|
187300
|
+
const releaseLock = await this.acquireLock();
|
|
187301
|
+
try {
|
|
187302
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187303
|
+
const ownerFile = JSON.parse(content);
|
|
187304
|
+
if (!this.isProcessRunning(ownerFile.owner.pid)) {
|
|
187305
|
+
return null;
|
|
187306
|
+
}
|
|
187307
|
+
return ownerFile.owner;
|
|
187308
|
+
} catch {
|
|
187309
|
+
return null;
|
|
187310
|
+
} finally {
|
|
187311
|
+
releaseLock();
|
|
187312
|
+
}
|
|
187313
|
+
}
|
|
187314
|
+
/**
|
|
187315
|
+
* Check if this process is the proxy owner.
|
|
187316
|
+
*/
|
|
187317
|
+
isProxyOwner() {
|
|
187318
|
+
return this.isOwner;
|
|
187319
|
+
}
|
|
187320
|
+
/**
|
|
187321
|
+
* Register services for a key.
|
|
187322
|
+
*/
|
|
187323
|
+
async registerServices(key, adminPort, services, drizzleGatewayPort) {
|
|
187324
|
+
const releaseLock = await this.acquireLock();
|
|
187325
|
+
try {
|
|
187326
|
+
const registry = this.readRegistry();
|
|
187327
|
+
const registration = {
|
|
187328
|
+
adminPort,
|
|
187329
|
+
services
|
|
187330
|
+
};
|
|
187331
|
+
if (drizzleGatewayPort !== void 0) {
|
|
187332
|
+
registration.drizzleGatewayPort = drizzleGatewayPort;
|
|
187333
|
+
}
|
|
187334
|
+
registry.keys[key] = registration;
|
|
187335
|
+
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
187336
|
+
this.writeFileAtomic(this.registryPath, registry);
|
|
187337
|
+
} finally {
|
|
187338
|
+
releaseLock();
|
|
187339
|
+
}
|
|
187340
|
+
}
|
|
187341
|
+
/**
|
|
187342
|
+
* Unregister all services for a key.
|
|
187343
|
+
*/
|
|
187344
|
+
async unregisterServices(key) {
|
|
187345
|
+
const releaseLock = await this.acquireLock();
|
|
187346
|
+
try {
|
|
187347
|
+
const registry = this.readRegistry();
|
|
187348
|
+
delete registry.keys[key];
|
|
187349
|
+
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
187350
|
+
this.writeFileAtomic(this.registryPath, registry);
|
|
187351
|
+
} finally {
|
|
187352
|
+
releaseLock();
|
|
187353
|
+
}
|
|
187354
|
+
}
|
|
187355
|
+
/**
|
|
187356
|
+
* Get all registered services (flattened view for proxy routing).
|
|
187357
|
+
*/
|
|
187358
|
+
async getAllServices() {
|
|
187359
|
+
const releaseLock = await this.acquireLock();
|
|
187360
|
+
try {
|
|
187361
|
+
const registry = this.readRegistry();
|
|
187362
|
+
const services = [];
|
|
187363
|
+
for (const [key, registration] of Object.entries(registry.keys)) {
|
|
187364
|
+
for (const svc of registration.services) {
|
|
187365
|
+
services.push({
|
|
187366
|
+
key,
|
|
187367
|
+
serviceName: svc.serviceName,
|
|
187368
|
+
port: svc.port
|
|
187369
|
+
});
|
|
187370
|
+
}
|
|
187371
|
+
}
|
|
187372
|
+
return services;
|
|
187373
|
+
} finally {
|
|
187374
|
+
releaseLock();
|
|
187375
|
+
}
|
|
187376
|
+
}
|
|
187377
|
+
/**
|
|
187378
|
+
* Get all registered keys and their admin ports.
|
|
187379
|
+
*/
|
|
187380
|
+
async getAllKeyRegistrations() {
|
|
187381
|
+
const releaseLock = await this.acquireLock();
|
|
187382
|
+
try {
|
|
187383
|
+
const registry = this.readRegistry();
|
|
187384
|
+
return { ...registry.keys };
|
|
187385
|
+
} finally {
|
|
187386
|
+
releaseLock();
|
|
187387
|
+
}
|
|
187388
|
+
}
|
|
187389
|
+
/**
|
|
187390
|
+
* Watch the registry for changes. Returns a cleanup function.
|
|
187391
|
+
* Callback receives both the flattened services list and all key registrations (for admin ports).
|
|
187392
|
+
* Uses chokidar instead of fs.watch for reliable detection of atomic file writes on macOS.
|
|
187393
|
+
*/
|
|
187394
|
+
watchRegistry(onChange) {
|
|
187395
|
+
this.ensureProxyDir();
|
|
187396
|
+
if (!fs12.existsSync(this.registryPath)) {
|
|
187397
|
+
const emptyRegistry = {
|
|
187398
|
+
version: 1,
|
|
187399
|
+
keys: {},
|
|
187400
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187401
|
+
};
|
|
187402
|
+
this.writeFileAtomic(this.registryPath, emptyRegistry);
|
|
187403
|
+
}
|
|
187404
|
+
let debounceTimer = null;
|
|
187405
|
+
this.registryWatcher = chokidar_default.watch(this.registryPath, {
|
|
187406
|
+
persistent: true,
|
|
187407
|
+
awaitWriteFinish: {
|
|
187408
|
+
stabilityThreshold: 100,
|
|
187409
|
+
pollInterval: 50
|
|
187410
|
+
}
|
|
187411
|
+
});
|
|
187412
|
+
this.registryWatcher.on("change", async () => {
|
|
187413
|
+
if (debounceTimer) {
|
|
187414
|
+
clearTimeout(debounceTimer);
|
|
187415
|
+
}
|
|
187416
|
+
debounceTimer = setTimeout(async () => {
|
|
187417
|
+
try {
|
|
187418
|
+
const services = await this.getAllServices();
|
|
187419
|
+
const keys = await this.getAllKeyRegistrations();
|
|
187420
|
+
onChange(services, keys);
|
|
187421
|
+
} catch {
|
|
187422
|
+
}
|
|
187423
|
+
}, 50);
|
|
187424
|
+
});
|
|
187425
|
+
return () => {
|
|
187426
|
+
if (debounceTimer) {
|
|
187427
|
+
clearTimeout(debounceTimer);
|
|
187428
|
+
}
|
|
187429
|
+
this.registryWatcher?.close();
|
|
187430
|
+
this.registryWatcher = null;
|
|
187431
|
+
};
|
|
187432
|
+
}
|
|
187433
|
+
/**
|
|
187434
|
+
* Attempt to become the proxy owner (election).
|
|
187435
|
+
* This is called when the current owner is detected as dead.
|
|
187436
|
+
*/
|
|
187437
|
+
async attemptElection(key) {
|
|
187438
|
+
const releaseLock = await this.acquireLock();
|
|
187439
|
+
try {
|
|
187440
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187441
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187442
|
+
const ownerFile2 = JSON.parse(content);
|
|
187443
|
+
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187444
|
+
return false;
|
|
187445
|
+
}
|
|
187446
|
+
fs12.unlinkSync(this.ownerPath);
|
|
187447
|
+
}
|
|
187448
|
+
const ownerFile = {
|
|
187449
|
+
version: 1,
|
|
187450
|
+
owner: {
|
|
187451
|
+
pid: process.pid,
|
|
187452
|
+
key,
|
|
187453
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187454
|
+
}
|
|
187455
|
+
};
|
|
187456
|
+
this.writeFileAtomic(this.ownerPath, ownerFile);
|
|
187457
|
+
this.isOwner = true;
|
|
187458
|
+
return true;
|
|
187459
|
+
} finally {
|
|
187460
|
+
releaseLock();
|
|
187461
|
+
}
|
|
187462
|
+
}
|
|
187463
|
+
readRegistry() {
|
|
187464
|
+
if (!fs12.existsSync(this.registryPath)) {
|
|
187465
|
+
return {
|
|
187466
|
+
version: 1,
|
|
187467
|
+
keys: {},
|
|
187468
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187469
|
+
};
|
|
187470
|
+
}
|
|
187471
|
+
try {
|
|
187472
|
+
const content = fs12.readFileSync(this.registryPath, "utf-8");
|
|
187473
|
+
return JSON.parse(content);
|
|
187474
|
+
} catch {
|
|
187475
|
+
return {
|
|
187476
|
+
version: 1,
|
|
187477
|
+
keys: {},
|
|
187478
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
187479
|
+
};
|
|
187480
|
+
}
|
|
187481
|
+
}
|
|
187482
|
+
writeFileAtomic(filePath, data) {
|
|
187483
|
+
this.ensureProxyDir();
|
|
187484
|
+
const tmpPath = filePath + ".tmp";
|
|
187485
|
+
fs12.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
187486
|
+
fs12.renameSync(tmpPath, filePath);
|
|
187487
|
+
}
|
|
187488
|
+
};
|
|
187489
|
+
|
|
186931
187490
|
// src/commands/dev.tsx
|
|
186932
187491
|
var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
|
|
186933
|
-
function DevUI() {
|
|
187492
|
+
function DevUI({ instanceKey }) {
|
|
186934
187493
|
const { exit } = useApp2();
|
|
186935
|
-
const [state, setState] = useState3(() =>
|
|
186936
|
-
|
|
186937
|
-
|
|
186938
|
-
|
|
186939
|
-
|
|
186940
|
-
|
|
186941
|
-
|
|
186942
|
-
|
|
186943
|
-
|
|
187494
|
+
const [state, setState] = useState3(() => {
|
|
187495
|
+
const caExists = caFilesExist();
|
|
187496
|
+
return {
|
|
187497
|
+
status: caExists ? "loading" : "installing-ca",
|
|
187498
|
+
...caExists ? {} : { caInstallPhase: "installing" },
|
|
187499
|
+
resources: /* @__PURE__ */ new Map(),
|
|
187500
|
+
resourceStatus: /* @__PURE__ */ new Map(),
|
|
187501
|
+
services: [],
|
|
187502
|
+
output: [],
|
|
187503
|
+
colorMap: /* @__PURE__ */ new Map(),
|
|
187504
|
+
isProxyOwner: false
|
|
187505
|
+
};
|
|
187506
|
+
});
|
|
186944
187507
|
useEffect3(() => {
|
|
186945
187508
|
if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
|
|
186946
187509
|
installCA();
|
|
@@ -186963,22 +187526,39 @@ function DevUI() {
|
|
|
186963
187526
|
}
|
|
186964
187527
|
}
|
|
186965
187528
|
const shuttingDown = useRef(false);
|
|
187529
|
+
const startTimeRef = useRef(null);
|
|
186966
187530
|
const stateManagerRef = useRef(null);
|
|
187531
|
+
const proxyRegistryRef = useRef(null);
|
|
186967
187532
|
const electricInstancesRef = useRef([]);
|
|
186968
187533
|
const drizzleGatewayRef = useRef(null);
|
|
187534
|
+
const registryWatcherCleanupRef = useRef(null);
|
|
187535
|
+
const electionIntervalRef = useRef(null);
|
|
186969
187536
|
const proxyRef = useRef(null);
|
|
187537
|
+
const adminServerRef = useRef(null);
|
|
186970
187538
|
const servicesRef = useRef([]);
|
|
186971
187539
|
const resourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
186972
187540
|
const [reloadTrigger, setReloadTrigger] = useState3(0);
|
|
186973
187541
|
const [readyToStart, setReadyToStart] = useState3(() => caFilesExist());
|
|
186974
|
-
const
|
|
187542
|
+
const shutdown2 = async () => {
|
|
186975
187543
|
if (shuttingDown.current) return;
|
|
186976
187544
|
shuttingDown.current = true;
|
|
187545
|
+
const duration = startTimeRef.current ? Math.round((Date.now() - startTimeRef.current) / 1e3) : void 0;
|
|
187546
|
+
trackEvent("dev_stopped", { duration_seconds: duration });
|
|
186977
187547
|
writeLog("system", "Shutting down");
|
|
186978
187548
|
setState((s) => ({ ...s, status: "stopping" }));
|
|
187549
|
+
if (electionIntervalRef.current) {
|
|
187550
|
+
clearInterval(electionIntervalRef.current);
|
|
187551
|
+
electionIntervalRef.current = null;
|
|
187552
|
+
}
|
|
187553
|
+
if (registryWatcherCleanupRef.current) {
|
|
187554
|
+
registryWatcherCleanupRef.current();
|
|
187555
|
+
registryWatcherCleanupRef.current = null;
|
|
187556
|
+
}
|
|
186979
187557
|
await Promise.all([
|
|
186980
187558
|
// Stop proxy
|
|
186981
187559
|
proxyRef.current?.stop(),
|
|
187560
|
+
// Stop admin server
|
|
187561
|
+
adminServerRef.current?.stop(),
|
|
186982
187562
|
// Stop all services
|
|
186983
187563
|
...servicesRef.current.map((service) => service.stop()),
|
|
186984
187564
|
// Stop all Electric instances
|
|
@@ -186988,6 +187568,10 @@ function DevUI() {
|
|
|
186988
187568
|
// Stop all resources
|
|
186989
187569
|
...[...resourcesRef.current.values()].map((resource) => resource.stop())
|
|
186990
187570
|
]);
|
|
187571
|
+
if (proxyRegistryRef.current) {
|
|
187572
|
+
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
187573
|
+
await proxyRegistryRef.current.releaseProxyOwnership();
|
|
187574
|
+
}
|
|
186991
187575
|
if (stateManagerRef.current) {
|
|
186992
187576
|
await stateManagerRef.current.releaseOwnership();
|
|
186993
187577
|
}
|
|
@@ -186999,9 +187583,19 @@ function DevUI() {
|
|
|
186999
187583
|
if (shuttingDown.current || state.status === "reloading") return;
|
|
187000
187584
|
writeLog("system", "Reloading (config changed)");
|
|
187001
187585
|
setState((s) => ({ ...s, status: "reloading", parseError: void 0 }));
|
|
187586
|
+
if (electionIntervalRef.current) {
|
|
187587
|
+
clearInterval(electionIntervalRef.current);
|
|
187588
|
+
electionIntervalRef.current = null;
|
|
187589
|
+
}
|
|
187590
|
+
if (registryWatcherCleanupRef.current) {
|
|
187591
|
+
registryWatcherCleanupRef.current();
|
|
187592
|
+
registryWatcherCleanupRef.current = null;
|
|
187593
|
+
}
|
|
187002
187594
|
await Promise.all([
|
|
187003
187595
|
// Stop proxy
|
|
187004
187596
|
proxyRef.current?.stop(),
|
|
187597
|
+
// Stop admin server
|
|
187598
|
+
adminServerRef.current?.stop(),
|
|
187005
187599
|
// Stop all services
|
|
187006
187600
|
...servicesRef.current.map((service) => service.stop()),
|
|
187007
187601
|
// Stop all Electric instances
|
|
@@ -187014,8 +187608,13 @@ function DevUI() {
|
|
|
187014
187608
|
electricInstancesRef.current = [];
|
|
187015
187609
|
drizzleGatewayRef.current = null;
|
|
187016
187610
|
proxyRef.current = null;
|
|
187611
|
+
adminServerRef.current = null;
|
|
187017
187612
|
servicesRef.current = [];
|
|
187018
187613
|
resourcesRef.current = /* @__PURE__ */ new Map();
|
|
187614
|
+
if (proxyRegistryRef.current) {
|
|
187615
|
+
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
187616
|
+
await proxyRegistryRef.current.releaseProxyOwnership();
|
|
187617
|
+
}
|
|
187019
187618
|
if (stateManagerRef.current) {
|
|
187020
187619
|
await stateManagerRef.current.releaseOwnership();
|
|
187021
187620
|
}
|
|
@@ -187026,7 +187625,8 @@ function DevUI() {
|
|
|
187026
187625
|
resources: /* @__PURE__ */ new Map(),
|
|
187027
187626
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
187028
187627
|
services: [],
|
|
187029
|
-
proxy: void 0
|
|
187628
|
+
proxy: void 0,
|
|
187629
|
+
isProxyOwner: false
|
|
187030
187630
|
}));
|
|
187031
187631
|
setReloadTrigger((t) => t + 1);
|
|
187032
187632
|
};
|
|
@@ -187050,7 +187650,7 @@ function DevUI() {
|
|
|
187050
187650
|
}
|
|
187051
187651
|
process.exit(1);
|
|
187052
187652
|
}
|
|
187053
|
-
|
|
187653
|
+
shutdown2();
|
|
187054
187654
|
};
|
|
187055
187655
|
process.on("SIGINT", handleSignal);
|
|
187056
187656
|
process.on("SIGTERM", handleSignal);
|
|
@@ -187059,12 +187659,18 @@ function DevUI() {
|
|
|
187059
187659
|
process.off("SIGTERM", handleSignal);
|
|
187060
187660
|
};
|
|
187061
187661
|
}, []);
|
|
187662
|
+
useEffect3(() => {
|
|
187663
|
+
if (state.status === "running" && !startTimeRef.current) {
|
|
187664
|
+
startTimeRef.current = Date.now();
|
|
187665
|
+
trackEvent("dev_started");
|
|
187666
|
+
}
|
|
187667
|
+
}, [state.status]);
|
|
187062
187668
|
useEffect3(() => {
|
|
187063
187669
|
if (state.status !== "running") return;
|
|
187064
|
-
const configPath =
|
|
187670
|
+
const configPath = path14.join(process.cwd(), "specific.hcl");
|
|
187065
187671
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
187066
187672
|
try {
|
|
187067
|
-
const hcl =
|
|
187673
|
+
const hcl = fs13.readFileSync(configPath, "utf-8");
|
|
187068
187674
|
parseConfig(hcl).then(() => {
|
|
187069
187675
|
triggerReload();
|
|
187070
187676
|
}).catch((err) => {
|
|
@@ -187104,8 +187710,10 @@ function DevUI() {
|
|
|
187104
187710
|
let startedDrizzleGateway = null;
|
|
187105
187711
|
const startedServices = [];
|
|
187106
187712
|
let startedProxy = null;
|
|
187107
|
-
const stateManager = new InstanceStateManager(process.cwd());
|
|
187713
|
+
const stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
187108
187714
|
stateManagerRef.current = stateManager;
|
|
187715
|
+
const proxyRegistry = new ProxyRegistryManager();
|
|
187716
|
+
proxyRegistryRef.current = proxyRegistry;
|
|
187109
187717
|
async function start() {
|
|
187110
187718
|
writeLog("system", "Starting dev server");
|
|
187111
187719
|
await stateManager.cleanStaleState();
|
|
@@ -187128,8 +187736,8 @@ function DevUI() {
|
|
|
187128
187736
|
}));
|
|
187129
187737
|
return;
|
|
187130
187738
|
}
|
|
187131
|
-
const configPath =
|
|
187132
|
-
if (!
|
|
187739
|
+
const configPath = path14.join(process.cwd(), "specific.hcl");
|
|
187740
|
+
if (!fs13.existsSync(configPath)) {
|
|
187133
187741
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
187134
187742
|
setState((s) => ({
|
|
187135
187743
|
...s,
|
|
@@ -187148,7 +187756,7 @@ function DevUI() {
|
|
|
187148
187756
|
}
|
|
187149
187757
|
let config2;
|
|
187150
187758
|
try {
|
|
187151
|
-
const hcl =
|
|
187759
|
+
const hcl = fs13.readFileSync(configPath, "utf-8");
|
|
187152
187760
|
config2 = await parseConfig(hcl);
|
|
187153
187761
|
} catch (err) {
|
|
187154
187762
|
setState((s) => ({
|
|
@@ -187181,7 +187789,7 @@ function DevUI() {
|
|
|
187181
187789
|
colorIndex++;
|
|
187182
187790
|
}
|
|
187183
187791
|
setState((s) => ({ ...s, status: "starting", config: config2, colorMap }));
|
|
187184
|
-
const portAllocator = new StablePortAllocator(process.cwd());
|
|
187792
|
+
const portAllocator = new StablePortAllocator(process.cwd(), instanceKey);
|
|
187185
187793
|
const resourceStatus = /* @__PURE__ */ new Map();
|
|
187186
187794
|
const syncDatabases = detectSyncDatabases(config2);
|
|
187187
187795
|
let resources2;
|
|
@@ -187190,7 +187798,7 @@ function DevUI() {
|
|
|
187190
187798
|
config: config2,
|
|
187191
187799
|
selection: { mode: "all" },
|
|
187192
187800
|
stateManager,
|
|
187193
|
-
dataDir:
|
|
187801
|
+
dataDir: `.specific/keys/${instanceKey}/data`,
|
|
187194
187802
|
portAllocator,
|
|
187195
187803
|
callbacks: {
|
|
187196
187804
|
onResourceStarting: (name, type) => {
|
|
@@ -187254,7 +187862,7 @@ function DevUI() {
|
|
|
187254
187862
|
const drizzleGateway = await startDrizzleGateway(
|
|
187255
187863
|
postgresResources,
|
|
187256
187864
|
drizzlePort,
|
|
187257
|
-
|
|
187865
|
+
path14.join(process.cwd(), ".specific", "keys", instanceKey)
|
|
187258
187866
|
);
|
|
187259
187867
|
startedDrizzleGateway = drizzleGateway;
|
|
187260
187868
|
drizzleGatewayRef.current = drizzleGateway;
|
|
@@ -187362,6 +187970,7 @@ function DevUI() {
|
|
|
187362
187970
|
}
|
|
187363
187971
|
}
|
|
187364
187972
|
if (cancelled) return;
|
|
187973
|
+
const serviceInfos = [];
|
|
187365
187974
|
const exposedServices = [];
|
|
187366
187975
|
for (const service of config2.services) {
|
|
187367
187976
|
const endpointInfos = serviceEndpoints.get(service.name) || [];
|
|
@@ -187372,6 +187981,10 @@ function DevUI() {
|
|
|
187372
187981
|
if (endpointConfig?.public) {
|
|
187373
187982
|
const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
|
|
187374
187983
|
exposedServices.push({ name: proxyName, port: info.port });
|
|
187984
|
+
serviceInfos.push({
|
|
187985
|
+
serviceName: proxyName,
|
|
187986
|
+
port: info.port
|
|
187987
|
+
});
|
|
187375
187988
|
}
|
|
187376
187989
|
}
|
|
187377
187990
|
}
|
|
@@ -187395,26 +188008,114 @@ function DevUI() {
|
|
|
187395
188008
|
syncEnabled: r.type === "postgres" && syncDatabases.has(name)
|
|
187396
188009
|
}))
|
|
187397
188010
|
});
|
|
187398
|
-
|
|
187399
|
-
|
|
187400
|
-
|
|
187401
|
-
|
|
187402
|
-
|
|
187403
|
-
|
|
187404
|
-
|
|
187405
|
-
|
|
187406
|
-
|
|
187407
|
-
|
|
187408
|
-
|
|
187409
|
-
|
|
187410
|
-
|
|
187411
|
-
|
|
187412
|
-
|
|
187413
|
-
...s
|
|
187414
|
-
|
|
187415
|
-
|
|
187416
|
-
|
|
187417
|
-
|
|
188011
|
+
const adminServer = await startAdminServer(getState);
|
|
188012
|
+
adminServerRef.current = adminServer;
|
|
188013
|
+
writeLog("system", `Admin API server started on port ${adminServer.port}`);
|
|
188014
|
+
await proxyRegistry.registerServices(
|
|
188015
|
+
instanceKey,
|
|
188016
|
+
adminServer.port,
|
|
188017
|
+
serviceInfos,
|
|
188018
|
+
startedDrizzleGateway?.port
|
|
188019
|
+
);
|
|
188020
|
+
writeLog("system", `Registered ${serviceInfos.length} services with proxy registry`);
|
|
188021
|
+
const becameProxyOwner = await proxyRegistry.claimProxyOwnership(instanceKey);
|
|
188022
|
+
if (becameProxyOwner) {
|
|
188023
|
+
writeLog("system", "Claimed proxy ownership, starting HTTP proxy");
|
|
188024
|
+
try {
|
|
188025
|
+
const currentServices = await proxyRegistry.getAllServices();
|
|
188026
|
+
const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
|
|
188027
|
+
const certificate = generateCertificate("local.spcf.app", registeredKeys);
|
|
188028
|
+
const proxy2 = await startHttpProxy(
|
|
188029
|
+
exposedServices,
|
|
188030
|
+
certificate,
|
|
188031
|
+
getState,
|
|
188032
|
+
instanceKey
|
|
188033
|
+
);
|
|
188034
|
+
startedProxy = proxy2;
|
|
188035
|
+
proxyRef.current = proxy2;
|
|
188036
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
188037
|
+
const knownKeys = new Set(registeredKeys);
|
|
188038
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry(async (updatedServices, updatedKeys) => {
|
|
188039
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
188040
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
188041
|
+
const newKeyNames = Object.keys(updatedKeys).filter((k) => !knownKeys.has(k));
|
|
188042
|
+
if (newKeyNames.length > 0) {
|
|
188043
|
+
writeLog("system", `New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`);
|
|
188044
|
+
for (const key of newKeyNames) {
|
|
188045
|
+
knownKeys.add(key);
|
|
188046
|
+
}
|
|
188047
|
+
const allKeys = [...knownKeys];
|
|
188048
|
+
const newCertificate = generateCertificate("local.spcf.app", allKeys);
|
|
188049
|
+
proxy2.updateCertificate(newCertificate);
|
|
188050
|
+
}
|
|
188051
|
+
});
|
|
188052
|
+
const currentKeys = await proxyRegistry.getAllKeyRegistrations();
|
|
188053
|
+
proxy2.updateServices(currentServices, currentKeys);
|
|
188054
|
+
writeLog("system", `Loaded ${currentServices.length} services from registry`);
|
|
188055
|
+
} catch (err) {
|
|
188056
|
+
const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
|
|
188057
|
+
writeLog("system:error", errorMsg);
|
|
188058
|
+
setState((s) => ({
|
|
188059
|
+
...s,
|
|
188060
|
+
status: "error",
|
|
188061
|
+
error: errorMsg
|
|
188062
|
+
}));
|
|
188063
|
+
return;
|
|
188064
|
+
}
|
|
188065
|
+
} else {
|
|
188066
|
+
writeLog("system", "Another instance owns the proxy, starting election watcher");
|
|
188067
|
+
setState((s) => ({ ...s, isProxyOwner: false }));
|
|
188068
|
+
const isProcessRunning = (pid) => {
|
|
188069
|
+
try {
|
|
188070
|
+
process.kill(pid, 0);
|
|
188071
|
+
return true;
|
|
188072
|
+
} catch {
|
|
188073
|
+
return false;
|
|
188074
|
+
}
|
|
188075
|
+
};
|
|
188076
|
+
electionIntervalRef.current = setInterval(async () => {
|
|
188077
|
+
if (cancelled || shuttingDown.current) {
|
|
188078
|
+
if (electionIntervalRef.current) {
|
|
188079
|
+
clearInterval(electionIntervalRef.current);
|
|
188080
|
+
electionIntervalRef.current = null;
|
|
188081
|
+
}
|
|
188082
|
+
return;
|
|
188083
|
+
}
|
|
188084
|
+
const owner = await proxyRegistry.getProxyOwner();
|
|
188085
|
+
if (!owner || !isProcessRunning(owner.pid)) {
|
|
188086
|
+
writeLog("system", "Proxy owner died, attempting election");
|
|
188087
|
+
const won = await proxyRegistry.attemptElection(instanceKey);
|
|
188088
|
+
if (won) {
|
|
188089
|
+
writeLog("system", "Won election, starting HTTP proxy");
|
|
188090
|
+
if (electionIntervalRef.current) {
|
|
188091
|
+
clearInterval(electionIntervalRef.current);
|
|
188092
|
+
electionIntervalRef.current = null;
|
|
188093
|
+
}
|
|
188094
|
+
try {
|
|
188095
|
+
const electionServices = await proxyRegistry.getAllServices();
|
|
188096
|
+
const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
|
|
188097
|
+
const electionKeyNames = Object.keys(electionKeyRegistrations);
|
|
188098
|
+
const certificate = generateCertificate("local.spcf.app", electionKeyNames);
|
|
188099
|
+
const proxy2 = await startHttpProxy(
|
|
188100
|
+
exposedServices,
|
|
188101
|
+
certificate,
|
|
188102
|
+
getState,
|
|
188103
|
+
instanceKey
|
|
188104
|
+
);
|
|
188105
|
+
startedProxy = proxy2;
|
|
188106
|
+
proxyRef.current = proxy2;
|
|
188107
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
188108
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry((updatedServices, updatedKeys) => {
|
|
188109
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
188110
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
188111
|
+
});
|
|
188112
|
+
proxy2.updateServices(electionServices, electionKeyRegistrations);
|
|
188113
|
+
} catch (err) {
|
|
188114
|
+
writeLog("system:error", `Failed to start proxy after election: ${err}`);
|
|
188115
|
+
}
|
|
188116
|
+
}
|
|
188117
|
+
}
|
|
188118
|
+
}, 1e3);
|
|
187418
188119
|
}
|
|
187419
188120
|
if (cancelled) return;
|
|
187420
188121
|
writeLog("system", "Dev server running");
|
|
@@ -187423,6 +188124,14 @@ function DevUI() {
|
|
|
187423
188124
|
start();
|
|
187424
188125
|
return () => {
|
|
187425
188126
|
cancelled = true;
|
|
188127
|
+
if (electionIntervalRef.current) {
|
|
188128
|
+
clearInterval(electionIntervalRef.current);
|
|
188129
|
+
electionIntervalRef.current = null;
|
|
188130
|
+
}
|
|
188131
|
+
if (registryWatcherCleanupRef.current) {
|
|
188132
|
+
registryWatcherCleanupRef.current();
|
|
188133
|
+
registryWatcherCleanupRef.current = null;
|
|
188134
|
+
}
|
|
187426
188135
|
if (startedProxy) {
|
|
187427
188136
|
startedProxy.stop().catch(() => {
|
|
187428
188137
|
});
|
|
@@ -187443,10 +188152,14 @@ function DevUI() {
|
|
|
187443
188152
|
resource.stop().catch(() => {
|
|
187444
188153
|
});
|
|
187445
188154
|
}
|
|
188155
|
+
proxyRegistry.unregisterServices(instanceKey).catch(() => {
|
|
188156
|
+
});
|
|
188157
|
+
proxyRegistry.releaseProxyOwnership().catch(() => {
|
|
188158
|
+
});
|
|
187446
188159
|
stateManager.releaseOwnership().catch(() => {
|
|
187447
188160
|
});
|
|
187448
188161
|
};
|
|
187449
|
-
}, [reloadTrigger, readyToStart]);
|
|
188162
|
+
}, [reloadTrigger, readyToStart, instanceKey]);
|
|
187450
188163
|
if (state.status === "installing-ca") {
|
|
187451
188164
|
if (state.caInstallPhase === "installing") {
|
|
187452
188165
|
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 +188237,15 @@ function DevUI() {
|
|
|
187524
188237
|
const staticItems = [
|
|
187525
188238
|
{
|
|
187526
188239
|
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)"))
|
|
188240
|
+
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
188241
|
},
|
|
187529
188242
|
{ 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
|
-
] : [],
|
|
188243
|
+
// Show admin UI URL
|
|
188244
|
+
{
|
|
188245
|
+
key: "admin",
|
|
188246
|
+
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"))
|
|
188247
|
+
},
|
|
188248
|
+
{ key: "admin-space", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") },
|
|
187538
188249
|
...services.length > 0 ? [
|
|
187539
188250
|
{ key: "svc-header", content: /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Services:") },
|
|
187540
188251
|
...services.flatMap((svc) => {
|
|
@@ -187553,17 +188264,14 @@ function DevUI() {
|
|
|
187553
188264
|
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
187554
188265
|
return {
|
|
187555
188266
|
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
|
|
188267
|
+
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
188268
|
};
|
|
187558
188269
|
});
|
|
187559
188270
|
}),
|
|
187560
188271
|
{ key: "space2", content: /* @__PURE__ */ React3.createElement(Text3, null, " ") }
|
|
187561
188272
|
] : [],
|
|
187562
188273
|
...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
|
-
},
|
|
188274
|
+
{ key: "pg-header", content: /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Postgres:") },
|
|
187567
188275
|
...config.postgres.map((pg) => {
|
|
187568
188276
|
const instance = resources.get(pg.name);
|
|
187569
188277
|
return {
|
|
@@ -187609,8 +188317,47 @@ function DevUI() {
|
|
|
187609
188317
|
];
|
|
187610
188318
|
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React3.createElement(Box3, { key: item.key }, item.content)));
|
|
187611
188319
|
}
|
|
187612
|
-
function devCommand() {
|
|
187613
|
-
render3(/* @__PURE__ */ React3.createElement(DevUI,
|
|
188320
|
+
function devCommand(instanceKey) {
|
|
188321
|
+
render3(/* @__PURE__ */ React3.createElement(DevUI, { instanceKey }));
|
|
188322
|
+
}
|
|
188323
|
+
|
|
188324
|
+
// src/lib/dev/git-worktree.ts
|
|
188325
|
+
import { execSync as execSync2 } from "child_process";
|
|
188326
|
+
import * as path15 from "path";
|
|
188327
|
+
function isInWorktree() {
|
|
188328
|
+
try {
|
|
188329
|
+
const commonDir = execSync2("git rev-parse --git-common-dir", {
|
|
188330
|
+
encoding: "utf-8",
|
|
188331
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188332
|
+
}).trim();
|
|
188333
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
188334
|
+
encoding: "utf-8",
|
|
188335
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188336
|
+
}).trim();
|
|
188337
|
+
const resolvedCommonDir = path15.resolve(commonDir);
|
|
188338
|
+
const resolvedGitDir = path15.resolve(gitDir);
|
|
188339
|
+
return resolvedCommonDir !== resolvedGitDir;
|
|
188340
|
+
} catch {
|
|
188341
|
+
return false;
|
|
188342
|
+
}
|
|
188343
|
+
}
|
|
188344
|
+
function getWorktreeName() {
|
|
188345
|
+
if (!isInWorktree()) {
|
|
188346
|
+
return null;
|
|
188347
|
+
}
|
|
188348
|
+
try {
|
|
188349
|
+
const gitDir = execSync2("git rev-parse --git-dir", {
|
|
188350
|
+
encoding: "utf-8",
|
|
188351
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
188352
|
+
}).trim();
|
|
188353
|
+
return path15.basename(gitDir);
|
|
188354
|
+
} catch {
|
|
188355
|
+
return null;
|
|
188356
|
+
}
|
|
188357
|
+
}
|
|
188358
|
+
function getDefaultKey() {
|
|
188359
|
+
const worktreeName = getWorktreeName();
|
|
188360
|
+
return worktreeName ?? "default";
|
|
187614
188361
|
}
|
|
187615
188362
|
|
|
187616
188363
|
// src/commands/deploy.tsx
|
|
@@ -187620,30 +188367,30 @@ import Spinner5 from "ink-spinner";
|
|
|
187620
188367
|
|
|
187621
188368
|
// ../../node_modules/open/index.js
|
|
187622
188369
|
import process8 from "node:process";
|
|
187623
|
-
import
|
|
188370
|
+
import path16 from "node:path";
|
|
187624
188371
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
187625
188372
|
import childProcess3 from "node:child_process";
|
|
187626
|
-
import
|
|
188373
|
+
import fs18, { constants as fsConstants2 } from "node:fs/promises";
|
|
187627
188374
|
|
|
187628
188375
|
// ../../node_modules/wsl-utils/index.js
|
|
187629
188376
|
import { promisify as promisify2 } from "node:util";
|
|
187630
188377
|
import childProcess2 from "node:child_process";
|
|
187631
|
-
import
|
|
188378
|
+
import fs17, { constants as fsConstants } from "node:fs/promises";
|
|
187632
188379
|
|
|
187633
188380
|
// ../../node_modules/is-wsl/index.js
|
|
187634
188381
|
import process2 from "node:process";
|
|
187635
|
-
import
|
|
187636
|
-
import
|
|
188382
|
+
import os6 from "node:os";
|
|
188383
|
+
import fs16 from "node:fs";
|
|
187637
188384
|
|
|
187638
188385
|
// ../../node_modules/is-inside-container/index.js
|
|
187639
|
-
import
|
|
188386
|
+
import fs15 from "node:fs";
|
|
187640
188387
|
|
|
187641
188388
|
// ../../node_modules/is-docker/index.js
|
|
187642
|
-
import
|
|
188389
|
+
import fs14 from "node:fs";
|
|
187643
188390
|
var isDockerCached;
|
|
187644
188391
|
function hasDockerEnv() {
|
|
187645
188392
|
try {
|
|
187646
|
-
|
|
188393
|
+
fs14.statSync("/.dockerenv");
|
|
187647
188394
|
return true;
|
|
187648
188395
|
} catch {
|
|
187649
188396
|
return false;
|
|
@@ -187651,7 +188398,7 @@ function hasDockerEnv() {
|
|
|
187651
188398
|
}
|
|
187652
188399
|
function hasDockerCGroup() {
|
|
187653
188400
|
try {
|
|
187654
|
-
return
|
|
188401
|
+
return fs14.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
187655
188402
|
} catch {
|
|
187656
188403
|
return false;
|
|
187657
188404
|
}
|
|
@@ -187667,7 +188414,7 @@ function isDocker() {
|
|
|
187667
188414
|
var cachedResult;
|
|
187668
188415
|
var hasContainerEnv = () => {
|
|
187669
188416
|
try {
|
|
187670
|
-
|
|
188417
|
+
fs15.statSync("/run/.containerenv");
|
|
187671
188418
|
return true;
|
|
187672
188419
|
} catch {
|
|
187673
188420
|
return false;
|
|
@@ -187685,14 +188432,14 @@ var isWsl = () => {
|
|
|
187685
188432
|
if (process2.platform !== "linux") {
|
|
187686
188433
|
return false;
|
|
187687
188434
|
}
|
|
187688
|
-
if (
|
|
188435
|
+
if (os6.release().toLowerCase().includes("microsoft")) {
|
|
187689
188436
|
if (isInsideContainer()) {
|
|
187690
188437
|
return false;
|
|
187691
188438
|
}
|
|
187692
188439
|
return true;
|
|
187693
188440
|
}
|
|
187694
188441
|
try {
|
|
187695
|
-
return
|
|
188442
|
+
return fs16.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
187696
188443
|
} catch {
|
|
187697
188444
|
return false;
|
|
187698
188445
|
}
|
|
@@ -187760,14 +188507,14 @@ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
|
|
|
187760
188507
|
const configFilePath = "/etc/wsl.conf";
|
|
187761
188508
|
let isConfigFileExists = false;
|
|
187762
188509
|
try {
|
|
187763
|
-
await
|
|
188510
|
+
await fs17.access(configFilePath, fsConstants.F_OK);
|
|
187764
188511
|
isConfigFileExists = true;
|
|
187765
188512
|
} catch {
|
|
187766
188513
|
}
|
|
187767
188514
|
if (!isConfigFileExists) {
|
|
187768
188515
|
return defaultMountPoint;
|
|
187769
188516
|
}
|
|
187770
|
-
const configContent = await
|
|
188517
|
+
const configContent = await fs17.readFile(configFilePath, { encoding: "utf8" });
|
|
187771
188518
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
187772
188519
|
if (parsedMountPoint === void 0) {
|
|
187773
188520
|
return defaultMountPoint;
|
|
@@ -187787,7 +188534,7 @@ var canAccessPowerShell = async () => {
|
|
|
187787
188534
|
canAccessPowerShellPromise ??= (async () => {
|
|
187788
188535
|
try {
|
|
187789
188536
|
const psPath = await powerShellPath2();
|
|
187790
|
-
await
|
|
188537
|
+
await fs17.access(psPath, fsConstants.X_OK);
|
|
187791
188538
|
return true;
|
|
187792
188539
|
} catch {
|
|
187793
188540
|
return false;
|
|
@@ -187801,15 +188548,15 @@ var wslDefaultBrowser = async () => {
|
|
|
187801
188548
|
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
187802
188549
|
return stdout.trim();
|
|
187803
188550
|
};
|
|
187804
|
-
var convertWslPathToWindows = async (
|
|
187805
|
-
if (/^[a-z]+:\/\//i.test(
|
|
187806
|
-
return
|
|
188551
|
+
var convertWslPathToWindows = async (path22) => {
|
|
188552
|
+
if (/^[a-z]+:\/\//i.test(path22)) {
|
|
188553
|
+
return path22;
|
|
187807
188554
|
}
|
|
187808
188555
|
try {
|
|
187809
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
188556
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path22], { encoding: "utf8" });
|
|
187810
188557
|
return stdout.trim();
|
|
187811
188558
|
} catch {
|
|
187812
|
-
return
|
|
188559
|
+
return path22;
|
|
187813
188560
|
}
|
|
187814
188561
|
};
|
|
187815
188562
|
|
|
@@ -187951,8 +188698,8 @@ var is_in_ssh_default = isInSsh;
|
|
|
187951
188698
|
|
|
187952
188699
|
// ../../node_modules/open/index.js
|
|
187953
188700
|
var fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
187954
|
-
var __dirname3 = import.meta.url ?
|
|
187955
|
-
var localXdgOpenPath =
|
|
188701
|
+
var __dirname3 = import.meta.url ? path16.dirname(fileURLToPath3(import.meta.url)) : "";
|
|
188702
|
+
var localXdgOpenPath = path16.join(__dirname3, "xdg-open");
|
|
187956
188703
|
var { platform: platform4, arch: arch2 } = process8;
|
|
187957
188704
|
var tryEachApp = async (apps2, opener) => {
|
|
187958
188705
|
if (apps2.length === 0) {
|
|
@@ -188100,7 +188847,7 @@ var baseOpen = async (options2) => {
|
|
|
188100
188847
|
const isBundled = !__dirname3 || __dirname3 === "/";
|
|
188101
188848
|
let exeLocalXdgOpen = false;
|
|
188102
188849
|
try {
|
|
188103
|
-
await
|
|
188850
|
+
await fs18.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
188104
188851
|
exeLocalXdgOpen = true;
|
|
188105
188852
|
} catch {
|
|
188106
188853
|
}
|
|
@@ -188123,19 +188870,19 @@ var baseOpen = async (options2) => {
|
|
|
188123
188870
|
}
|
|
188124
188871
|
const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
|
|
188125
188872
|
if (options2.wait) {
|
|
188126
|
-
return new Promise((
|
|
188873
|
+
return new Promise((resolve5, reject) => {
|
|
188127
188874
|
subprocess.once("error", reject);
|
|
188128
188875
|
subprocess.once("close", (exitCode) => {
|
|
188129
188876
|
if (!options2.allowNonzeroExitCode && exitCode !== 0) {
|
|
188130
188877
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
188131
188878
|
return;
|
|
188132
188879
|
}
|
|
188133
|
-
|
|
188880
|
+
resolve5(subprocess);
|
|
188134
188881
|
});
|
|
188135
188882
|
});
|
|
188136
188883
|
}
|
|
188137
188884
|
if (isFallbackAttempt) {
|
|
188138
|
-
return new Promise((
|
|
188885
|
+
return new Promise((resolve5, reject) => {
|
|
188139
188886
|
subprocess.once("error", reject);
|
|
188140
188887
|
subprocess.once("spawn", () => {
|
|
188141
188888
|
subprocess.once("close", (exitCode) => {
|
|
@@ -188145,17 +188892,17 @@ var baseOpen = async (options2) => {
|
|
|
188145
188892
|
return;
|
|
188146
188893
|
}
|
|
188147
188894
|
subprocess.unref();
|
|
188148
|
-
|
|
188895
|
+
resolve5(subprocess);
|
|
188149
188896
|
});
|
|
188150
188897
|
});
|
|
188151
188898
|
});
|
|
188152
188899
|
}
|
|
188153
188900
|
subprocess.unref();
|
|
188154
|
-
return new Promise((
|
|
188901
|
+
return new Promise((resolve5, reject) => {
|
|
188155
188902
|
subprocess.once("error", reject);
|
|
188156
188903
|
subprocess.once("spawn", () => {
|
|
188157
188904
|
subprocess.off("error", reject);
|
|
188158
|
-
|
|
188905
|
+
resolve5(subprocess);
|
|
188159
188906
|
});
|
|
188160
188907
|
});
|
|
188161
188908
|
};
|
|
@@ -188232,35 +188979,35 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
|
|
|
188232
188979
|
var open_default = open2;
|
|
188233
188980
|
|
|
188234
188981
|
// src/commands/deploy.tsx
|
|
188235
|
-
import * as
|
|
188236
|
-
import * as
|
|
188982
|
+
import * as fs21 from "fs";
|
|
188983
|
+
import * as path19 from "path";
|
|
188237
188984
|
|
|
188238
188985
|
// src/lib/deploy/build-tester.ts
|
|
188239
188986
|
import { spawn as spawn5 } from "child_process";
|
|
188240
|
-
import { existsSync as
|
|
188241
|
-
import { join as
|
|
188987
|
+
import { existsSync as existsSync16 } from "fs";
|
|
188988
|
+
import { join as join17 } from "path";
|
|
188242
188989
|
function getDependencyInstallCommand(build, projectDir) {
|
|
188243
188990
|
switch (build.base) {
|
|
188244
188991
|
case "node":
|
|
188245
|
-
if (
|
|
188992
|
+
if (existsSync16(join17(projectDir, "pnpm-lock.yaml"))) {
|
|
188246
188993
|
return "pnpm install --frozen-lockfile";
|
|
188247
|
-
} else if (
|
|
188994
|
+
} else if (existsSync16(join17(projectDir, "yarn.lock"))) {
|
|
188248
188995
|
return "yarn install --frozen-lockfile";
|
|
188249
|
-
} else if (
|
|
188996
|
+
} else if (existsSync16(join17(projectDir, "package-lock.json"))) {
|
|
188250
188997
|
return "npm ci";
|
|
188251
188998
|
} else {
|
|
188252
188999
|
return "npm install";
|
|
188253
189000
|
}
|
|
188254
189001
|
case "python":
|
|
188255
|
-
if (
|
|
189002
|
+
if (existsSync16(join17(projectDir, "poetry.lock"))) {
|
|
188256
189003
|
return "poetry install --no-interaction";
|
|
188257
|
-
} else if (
|
|
189004
|
+
} else if (existsSync16(join17(projectDir, "Pipfile.lock"))) {
|
|
188258
189005
|
return "pipenv install --deploy";
|
|
188259
|
-
} else if (
|
|
189006
|
+
} else if (existsSync16(join17(projectDir, "Pipfile"))) {
|
|
188260
189007
|
return "pipenv install";
|
|
188261
|
-
} else if (
|
|
189008
|
+
} else if (existsSync16(join17(projectDir, "pyproject.toml"))) {
|
|
188262
189009
|
return "pip install .";
|
|
188263
|
-
} else if (
|
|
189010
|
+
} else if (existsSync16(join17(projectDir, "requirements.txt"))) {
|
|
188264
189011
|
return "pip install -r requirements.txt";
|
|
188265
189012
|
}
|
|
188266
189013
|
return null;
|
|
@@ -188274,7 +189021,7 @@ function getDependencyInstallCommand(build, projectDir) {
|
|
|
188274
189021
|
}
|
|
188275
189022
|
}
|
|
188276
189023
|
function runCommand2(command, projectDir, buildName) {
|
|
188277
|
-
return new Promise((
|
|
189024
|
+
return new Promise((resolve5) => {
|
|
188278
189025
|
const stdout = [];
|
|
188279
189026
|
const stderr = [];
|
|
188280
189027
|
writeLog("build-test", `[${buildName}] Running: ${command}`);
|
|
@@ -188304,7 +189051,7 @@ function runCommand2(command, projectDir, buildName) {
|
|
|
188304
189051
|
});
|
|
188305
189052
|
child.on("error", (err) => {
|
|
188306
189053
|
writeLog("build-test:error", `[${buildName}] Failed to start: ${err.message}`);
|
|
188307
|
-
|
|
189054
|
+
resolve5({
|
|
188308
189055
|
success: false,
|
|
188309
189056
|
output: `Failed to start command: ${err.message}`
|
|
188310
189057
|
});
|
|
@@ -188313,10 +189060,10 @@ function runCommand2(command, projectDir, buildName) {
|
|
|
188313
189060
|
const output = [...stdout, ...stderr].join("");
|
|
188314
189061
|
if (code === 0) {
|
|
188315
189062
|
writeLog("build-test", `[${buildName}] Command succeeded (exit code 0)`);
|
|
188316
|
-
|
|
189063
|
+
resolve5({ success: true, output });
|
|
188317
189064
|
} else {
|
|
188318
189065
|
writeLog("build-test:error", `[${buildName}] Command failed with exit code ${code}`);
|
|
188319
|
-
|
|
189066
|
+
resolve5({
|
|
188320
189067
|
success: false,
|
|
188321
189068
|
output: output || `Exit code: ${code}`
|
|
188322
189069
|
});
|
|
@@ -188652,16 +189399,44 @@ var ApiClient = class {
|
|
|
188652
189399
|
}
|
|
188653
189400
|
return response.json();
|
|
188654
189401
|
}
|
|
189402
|
+
async getMe(signal) {
|
|
189403
|
+
const url = `${this.baseUrl}/users/me`;
|
|
189404
|
+
writeLog("api", `GET ${url}`);
|
|
189405
|
+
const response = await fetch(url, {
|
|
189406
|
+
headers: this.authHeaders(),
|
|
189407
|
+
signal
|
|
189408
|
+
});
|
|
189409
|
+
writeLog("api", `Response: ${response.status} ${response.statusText}`);
|
|
189410
|
+
if (!response.ok) {
|
|
189411
|
+
let errorBody;
|
|
189412
|
+
try {
|
|
189413
|
+
const error = await response.json();
|
|
189414
|
+
errorBody = JSON.stringify(error);
|
|
189415
|
+
writeLog("api:error", `API error: ${error.error} (${error.code})`);
|
|
189416
|
+
writeLog("api:error", `Request was: GET ${url}`);
|
|
189417
|
+
writeLog("api:error", `Response body: ${errorBody}`);
|
|
189418
|
+
throw new Error(`Failed to get user: ${error.error} (${error.code})`);
|
|
189419
|
+
} catch (e) {
|
|
189420
|
+
if (e instanceof Error && e.message.startsWith("Failed to get user")) {
|
|
189421
|
+
throw e;
|
|
189422
|
+
}
|
|
189423
|
+
errorBody = await response.text();
|
|
189424
|
+
writeLog("api:error", `Failed to parse error response: ${errorBody}`);
|
|
189425
|
+
throw new Error(`Failed to get user: ${response.statusText}`);
|
|
189426
|
+
}
|
|
189427
|
+
}
|
|
189428
|
+
return response.json();
|
|
189429
|
+
}
|
|
188655
189430
|
};
|
|
188656
189431
|
|
|
188657
189432
|
// src/lib/tarball/create.ts
|
|
188658
|
-
import { execSync as
|
|
188659
|
-
import * as
|
|
188660
|
-
import * as
|
|
189433
|
+
import { execSync as execSync3 } from "child_process";
|
|
189434
|
+
import * as fs19 from "fs";
|
|
189435
|
+
import * as path17 from "path";
|
|
188661
189436
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
188662
189437
|
function isInsideGitRepository(dir) {
|
|
188663
189438
|
try {
|
|
188664
|
-
const result =
|
|
189439
|
+
const result = execSync3("git rev-parse --is-inside-work-tree", {
|
|
188665
189440
|
cwd: dir,
|
|
188666
189441
|
encoding: "utf-8",
|
|
188667
189442
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -188671,50 +189446,42 @@ function isInsideGitRepository(dir) {
|
|
|
188671
189446
|
return false;
|
|
188672
189447
|
}
|
|
188673
189448
|
}
|
|
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"`,
|
|
189449
|
+
async function createGitArchive(projectDir) {
|
|
189450
|
+
writeLog("tarball", "Creating tarball using git ls-files");
|
|
189451
|
+
const filesOutput = execSync3(
|
|
189452
|
+
"git ls-files --cached --others --exclude-standard",
|
|
188702
189453
|
{
|
|
188703
|
-
cwd:
|
|
189454
|
+
cwd: projectDir,
|
|
189455
|
+
encoding: "utf-8",
|
|
188704
189456
|
maxBuffer: 100 * 1024 * 1024
|
|
188705
189457
|
// 100MB max
|
|
188706
189458
|
}
|
|
188707
189459
|
);
|
|
188708
|
-
|
|
189460
|
+
const allFiles = filesOutput.trim().split("\n").filter(Boolean);
|
|
189461
|
+
const filteredFiles = allFiles.filter(
|
|
189462
|
+
(file) => !file.startsWith(".specific/") && file !== ".specific"
|
|
189463
|
+
);
|
|
189464
|
+
writeLog(
|
|
189465
|
+
"tarball",
|
|
189466
|
+
`Found ${allFiles.length} files, ${filteredFiles.length} after filtering .specific`
|
|
189467
|
+
);
|
|
189468
|
+
const entryGenerator = createEntryItemGenerator(projectDir, filteredFiles);
|
|
189469
|
+
const tarStream = createTarPacker(entryGenerator, "gzip");
|
|
189470
|
+
const chunks = [];
|
|
189471
|
+
for await (const chunk of tarStream) {
|
|
189472
|
+
chunks.push(Buffer.from(chunk));
|
|
189473
|
+
}
|
|
189474
|
+
const tarball = Buffer.concat(chunks);
|
|
189475
|
+
writeLog("tarball", `Tarball created: ${tarball.length} bytes`);
|
|
188709
189476
|
return tarball;
|
|
188710
189477
|
}
|
|
188711
189478
|
var EXCLUDED_DIRS = [".specific"];
|
|
188712
189479
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
188713
189480
|
const results = [];
|
|
188714
|
-
const entries = await
|
|
189481
|
+
const entries = await fs19.promises.readdir(currentDir, { withFileTypes: true });
|
|
188715
189482
|
for (const entry of entries) {
|
|
188716
|
-
const fullPath =
|
|
188717
|
-
const relativePath =
|
|
189483
|
+
const fullPath = path17.join(currentDir, entry.name);
|
|
189484
|
+
const relativePath = path17.relative(baseDir, fullPath);
|
|
188718
189485
|
if (entry.isDirectory()) {
|
|
188719
189486
|
if (!exclude.includes(entry.name)) {
|
|
188720
189487
|
results.push(relativePath);
|
|
@@ -188729,8 +189496,8 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
188729
189496
|
}
|
|
188730
189497
|
async function createTarArchive(projectDir) {
|
|
188731
189498
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
188732
|
-
const configPath =
|
|
188733
|
-
if (!
|
|
189499
|
+
const configPath = path17.join(projectDir, "specific.hcl");
|
|
189500
|
+
if (!fs19.existsSync(configPath)) {
|
|
188734
189501
|
throw new Error("specific.hcl not found in project directory");
|
|
188735
189502
|
}
|
|
188736
189503
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -188752,49 +189519,10 @@ async function createProjectTarball(projectDir) {
|
|
|
188752
189519
|
return createTarArchive(projectDir);
|
|
188753
189520
|
}
|
|
188754
189521
|
|
|
188755
|
-
// src/lib/project/config.ts
|
|
188756
|
-
import * as fs18 from "fs";
|
|
188757
|
-
import * as path15 from "path";
|
|
188758
|
-
var PROJECT_ID_FILE = ".specific/project_id";
|
|
188759
|
-
var ProjectNotLinkedError = class extends Error {
|
|
188760
|
-
constructor() {
|
|
188761
|
-
super(
|
|
188762
|
-
`Project not linked to Specific.
|
|
188763
|
-
|
|
188764
|
-
The file ${PROJECT_ID_FILE} does not exist.
|
|
188765
|
-
|
|
188766
|
-
Run: specific deploy`
|
|
188767
|
-
);
|
|
188768
|
-
this.name = "ProjectNotLinkedError";
|
|
188769
|
-
}
|
|
188770
|
-
};
|
|
188771
|
-
function readProjectId(projectDir = process.cwd()) {
|
|
188772
|
-
const projectIdPath = path15.join(projectDir, PROJECT_ID_FILE);
|
|
188773
|
-
if (!fs18.existsSync(projectIdPath)) {
|
|
188774
|
-
throw new ProjectNotLinkedError();
|
|
188775
|
-
}
|
|
188776
|
-
const projectId = fs18.readFileSync(projectIdPath, "utf-8").trim();
|
|
188777
|
-
if (!projectId) {
|
|
188778
|
-
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
188779
|
-
}
|
|
188780
|
-
return projectId;
|
|
188781
|
-
}
|
|
188782
|
-
function hasProjectId(projectDir = process.cwd()) {
|
|
188783
|
-
const projectIdPath = path15.join(projectDir, PROJECT_ID_FILE);
|
|
188784
|
-
return fs18.existsSync(projectIdPath);
|
|
188785
|
-
}
|
|
188786
|
-
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
188787
|
-
const specificDir = path15.join(projectDir, ".specific");
|
|
188788
|
-
if (!fs18.existsSync(specificDir)) {
|
|
188789
|
-
fs18.mkdirSync(specificDir, { recursive: true });
|
|
188790
|
-
}
|
|
188791
|
-
fs18.writeFileSync(path15.join(specificDir, "project_id"), projectId + "\n");
|
|
188792
|
-
}
|
|
188793
|
-
|
|
188794
189522
|
// src/lib/auth/credentials.ts
|
|
188795
|
-
import * as
|
|
188796
|
-
import * as
|
|
188797
|
-
import * as
|
|
189523
|
+
import * as fs20 from "fs";
|
|
189524
|
+
import * as path18 from "path";
|
|
189525
|
+
import * as os7 from "os";
|
|
188798
189526
|
|
|
188799
189527
|
// src/lib/auth/errors.ts
|
|
188800
189528
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -188871,7 +189599,7 @@ import React4, { useState as useState4, useEffect as useEffect4 } from "react";
|
|
|
188871
189599
|
import { render as render4, Box as Box4, Text as Text4, useApp as useApp3 } from "ink";
|
|
188872
189600
|
import Spinner4 from "ink-spinner";
|
|
188873
189601
|
function performLogin(options2 = {}) {
|
|
188874
|
-
return new Promise((
|
|
189602
|
+
return new Promise((resolve5) => {
|
|
188875
189603
|
const instance = render4(
|
|
188876
189604
|
/* @__PURE__ */ React4.createElement(
|
|
188877
189605
|
LoginFlow,
|
|
@@ -188879,7 +189607,7 @@ function performLogin(options2 = {}) {
|
|
|
188879
189607
|
isReauthentication: options2.isReauthentication,
|
|
188880
189608
|
onComplete: (result) => {
|
|
188881
189609
|
instance.unmount();
|
|
188882
|
-
|
|
189610
|
+
resolve5(result);
|
|
188883
189611
|
}
|
|
188884
189612
|
}
|
|
188885
189613
|
)
|
|
@@ -188933,10 +189661,14 @@ function LoginFlow({ isReauthentication, onComplete }) {
|
|
|
188933
189661
|
}
|
|
188934
189662
|
} else {
|
|
188935
189663
|
const successResponse = response;
|
|
189664
|
+
const expiresAt2 = Date.now() + successResponse.expires_in * 1e3;
|
|
189665
|
+
const client2 = new ApiClient(successResponse.access_token);
|
|
189666
|
+
const user = await client2.getMe();
|
|
188936
189667
|
writeUserCredentials({
|
|
188937
189668
|
accessToken: successResponse.access_token,
|
|
188938
189669
|
refreshToken: successResponse.refresh_token,
|
|
188939
|
-
expiresAt:
|
|
189670
|
+
expiresAt: expiresAt2,
|
|
189671
|
+
userId: user.id
|
|
188940
189672
|
});
|
|
188941
189673
|
setUserEmail(successResponse.user.email);
|
|
188942
189674
|
setPhase("success");
|
|
@@ -188985,18 +189717,18 @@ function LoginFlow({ isReauthentication, onComplete }) {
|
|
|
188985
189717
|
|
|
188986
189718
|
// src/lib/auth/credentials.ts
|
|
188987
189719
|
function getUserCredentialsDir() {
|
|
188988
|
-
return
|
|
189720
|
+
return path18.join(os7.homedir(), ".specific");
|
|
188989
189721
|
}
|
|
188990
189722
|
function getCredentialsPath() {
|
|
188991
|
-
return
|
|
189723
|
+
return path18.join(getUserCredentialsDir(), "credentials.json");
|
|
188992
189724
|
}
|
|
188993
189725
|
function readUserCredentials() {
|
|
188994
189726
|
const credentialsPath = getCredentialsPath();
|
|
188995
|
-
if (!
|
|
189727
|
+
if (!fs20.existsSync(credentialsPath)) {
|
|
188996
189728
|
return null;
|
|
188997
189729
|
}
|
|
188998
189730
|
try {
|
|
188999
|
-
const content =
|
|
189731
|
+
const content = fs20.readFileSync(credentialsPath, "utf-8");
|
|
189000
189732
|
return JSON.parse(content);
|
|
189001
189733
|
} catch {
|
|
189002
189734
|
return null;
|
|
@@ -189004,18 +189736,18 @@ function readUserCredentials() {
|
|
|
189004
189736
|
}
|
|
189005
189737
|
function writeUserCredentials(credentials) {
|
|
189006
189738
|
const dir = getUserCredentialsDir();
|
|
189007
|
-
if (!
|
|
189008
|
-
|
|
189739
|
+
if (!fs20.existsSync(dir)) {
|
|
189740
|
+
fs20.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
189009
189741
|
}
|
|
189010
189742
|
const credentialsPath = getCredentialsPath();
|
|
189011
|
-
|
|
189743
|
+
fs20.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
|
|
189012
189744
|
mode: 384
|
|
189013
189745
|
});
|
|
189014
189746
|
}
|
|
189015
189747
|
function clearUserCredentials() {
|
|
189016
189748
|
const credentialsPath = getCredentialsPath();
|
|
189017
|
-
if (
|
|
189018
|
-
|
|
189749
|
+
if (fs20.existsSync(credentialsPath)) {
|
|
189750
|
+
fs20.unlinkSync(credentialsPath);
|
|
189019
189751
|
}
|
|
189020
189752
|
}
|
|
189021
189753
|
function isLoggedIn() {
|
|
@@ -189272,8 +190004,8 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189272
190004
|
async function loadProjects() {
|
|
189273
190005
|
try {
|
|
189274
190006
|
const token = await getValidAccessToken();
|
|
189275
|
-
const
|
|
189276
|
-
const { projects: projects2 } = await
|
|
190007
|
+
const client2 = new ApiClient(token);
|
|
190008
|
+
const { projects: projects2 } = await client2.listProjects();
|
|
189277
190009
|
if (cancelled) return;
|
|
189278
190010
|
setState({
|
|
189279
190011
|
phase: "selecting-project",
|
|
@@ -189319,8 +190051,8 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189319
190051
|
async function createProject() {
|
|
189320
190052
|
try {
|
|
189321
190053
|
const token = await getValidAccessToken();
|
|
189322
|
-
const
|
|
189323
|
-
const project = await
|
|
190054
|
+
const client2 = new ApiClient(token);
|
|
190055
|
+
const project = await client2.createProject(state.newProjectName);
|
|
189324
190056
|
if (cancelled) return;
|
|
189325
190057
|
writeProjectId(project.id);
|
|
189326
190058
|
setState({
|
|
@@ -189413,15 +190145,15 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189413
190145
|
(secret) => secretValues[secret]
|
|
189414
190146
|
);
|
|
189415
190147
|
if (!allSecretsCollected) return;
|
|
189416
|
-
const
|
|
189417
|
-
if (!
|
|
190148
|
+
const client2 = clientRef.current;
|
|
190149
|
+
if (!client2) return;
|
|
189418
190150
|
(async () => {
|
|
189419
190151
|
try {
|
|
189420
190152
|
writeLog(
|
|
189421
190153
|
"deploy",
|
|
189422
190154
|
`Submitting secrets: ${Object.keys(secretValues).join(", ")}`
|
|
189423
190155
|
);
|
|
189424
|
-
await
|
|
190156
|
+
await client2.submitSecrets(deployment2.id, secretValues);
|
|
189425
190157
|
writeLog("deploy", "Secrets submitted successfully");
|
|
189426
190158
|
setState((s) => ({
|
|
189427
190159
|
...s,
|
|
@@ -189455,15 +190187,15 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189455
190187
|
(config2) => configValues[config2]
|
|
189456
190188
|
);
|
|
189457
190189
|
if (!allConfigsCollected) return;
|
|
189458
|
-
const
|
|
189459
|
-
if (!
|
|
190190
|
+
const client2 = clientRef.current;
|
|
190191
|
+
if (!client2) return;
|
|
189460
190192
|
(async () => {
|
|
189461
190193
|
try {
|
|
189462
190194
|
writeLog(
|
|
189463
190195
|
"deploy",
|
|
189464
190196
|
`Submitting configs: ${Object.keys(configValues).join(", ")}`
|
|
189465
190197
|
);
|
|
189466
|
-
await
|
|
190198
|
+
await client2.submitConfigs(deployment2.id, configValues);
|
|
189467
190199
|
writeLog("deploy", "Configs submitted successfully");
|
|
189468
190200
|
setState((s) => ({
|
|
189469
190201
|
...s,
|
|
@@ -189535,8 +190267,8 @@ ${errorMsg}`
|
|
|
189535
190267
|
writeLog("deploy", `Starting deployment to "${environment}"`);
|
|
189536
190268
|
writeLog("deploy", `Project directory: ${projectDir}`);
|
|
189537
190269
|
const authToken = await getValidAccessToken();
|
|
189538
|
-
const
|
|
189539
|
-
clientRef.current =
|
|
190270
|
+
const client2 = new ApiClient(authToken);
|
|
190271
|
+
clientRef.current = client2;
|
|
189540
190272
|
if (cancelled) return;
|
|
189541
190273
|
let tarball;
|
|
189542
190274
|
try {
|
|
@@ -189561,7 +190293,7 @@ ${errorMsg}`
|
|
|
189561
190293
|
let deployment2;
|
|
189562
190294
|
try {
|
|
189563
190295
|
writeLog("deploy", `Creating deployment for project ${state.projectId}`);
|
|
189564
|
-
deployment2 = await
|
|
190296
|
+
deployment2 = await client2.createDeployment(state.projectId, environment);
|
|
189565
190297
|
writeLog("deploy", `Deployment created: ${deployment2.id}`);
|
|
189566
190298
|
} catch (err) {
|
|
189567
190299
|
const errorMsg = `Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -189576,7 +190308,7 @@ ${errorMsg}`
|
|
|
189576
190308
|
setState((s) => ({ ...s, phase: "uploading", deployment: deployment2 }));
|
|
189577
190309
|
try {
|
|
189578
190310
|
writeLog("deploy", `Uploading tarball (${tarball.length} bytes)`);
|
|
189579
|
-
deployment2 = await
|
|
190311
|
+
deployment2 = await client2.uploadTarball(deployment2.id, tarball);
|
|
189580
190312
|
writeLog("deploy", "Tarball uploaded successfully");
|
|
189581
190313
|
} catch (err) {
|
|
189582
190314
|
const errorMsg = `Failed to upload tarball: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -189595,7 +190327,7 @@ ${errorMsg}`
|
|
|
189595
190327
|
let lastState;
|
|
189596
190328
|
const pollForCompletion = async () => {
|
|
189597
190329
|
try {
|
|
189598
|
-
const status = await
|
|
190330
|
+
const status = await client2.getDeployment(deployment2.id);
|
|
189599
190331
|
if (cancelled) return;
|
|
189600
190332
|
if (status.state !== lastState) {
|
|
189601
190333
|
writeLog(
|
|
@@ -189681,11 +190413,11 @@ ${errorMsg}`
|
|
|
189681
190413
|
useEffect5(() => {
|
|
189682
190414
|
let pollInterval;
|
|
189683
190415
|
if ((state.phase === "building" || state.phase === "deploying") && state.deployment && state.missingSecrets === void 0 && state.secretValues === void 0 && state.missingConfigs === void 0 && state.configValues === void 0) {
|
|
189684
|
-
const
|
|
189685
|
-
if (!
|
|
190416
|
+
const client2 = clientRef.current;
|
|
190417
|
+
if (!client2) return;
|
|
189686
190418
|
const pollForCompletion = async () => {
|
|
189687
190419
|
try {
|
|
189688
|
-
const status = await
|
|
190420
|
+
const status = await client2.getDeployment(state.deployment.id);
|
|
189689
190421
|
if (status.state === "failed") {
|
|
189690
190422
|
setState((s) => ({
|
|
189691
190423
|
...s,
|
|
@@ -189717,12 +190449,24 @@ ${errorMsg}`
|
|
|
189717
190449
|
}
|
|
189718
190450
|
}, [state.phase, state.missingSecrets, state.secretValues, state.missingConfigs, state.configValues]);
|
|
189719
190451
|
useEffect5(() => {
|
|
189720
|
-
if (state.phase === "
|
|
190452
|
+
if (state.phase === "testing-builds") {
|
|
190453
|
+
trackEvent("deploy_started", { environment });
|
|
190454
|
+
}
|
|
190455
|
+
}, [state.phase, environment]);
|
|
190456
|
+
useEffect5(() => {
|
|
190457
|
+
if (state.phase === "success") {
|
|
190458
|
+
trackEvent("deploy_succeeded", { environment });
|
|
189721
190459
|
closeDebugLog();
|
|
189722
190460
|
const timer = setTimeout(() => exit(), 100);
|
|
189723
190461
|
return () => clearTimeout(timer);
|
|
189724
190462
|
}
|
|
189725
|
-
|
|
190463
|
+
if (state.phase === "error") {
|
|
190464
|
+
trackEvent("deploy_failed", { environment });
|
|
190465
|
+
closeDebugLog();
|
|
190466
|
+
const timer = setTimeout(() => exit(), 100);
|
|
190467
|
+
return () => clearTimeout(timer);
|
|
190468
|
+
}
|
|
190469
|
+
}, [state.phase, exit, environment]);
|
|
189726
190470
|
const {
|
|
189727
190471
|
phase,
|
|
189728
190472
|
deployment,
|
|
@@ -189837,14 +190581,14 @@ ${errorMsg}`
|
|
|
189837
190581
|
), 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
190582
|
}
|
|
189839
190583
|
async function deployCommand(environment, options2) {
|
|
189840
|
-
const configPath =
|
|
189841
|
-
if (!
|
|
190584
|
+
const configPath = path19.join(process.cwd(), "specific.hcl");
|
|
190585
|
+
if (!fs21.existsSync(configPath)) {
|
|
189842
190586
|
console.error("Error: No specific.hcl found in current directory");
|
|
189843
190587
|
process.exit(1);
|
|
189844
190588
|
}
|
|
189845
190589
|
let config;
|
|
189846
190590
|
try {
|
|
189847
|
-
const hcl =
|
|
190591
|
+
const hcl = fs21.readFileSync(configPath, "utf-8");
|
|
189848
190592
|
config = await parseConfig(hcl);
|
|
189849
190593
|
} catch (err) {
|
|
189850
190594
|
console.error(
|
|
@@ -189868,9 +190612,9 @@ async function deployCommand(environment, options2) {
|
|
|
189868
190612
|
|
|
189869
190613
|
// src/commands/exec.tsx
|
|
189870
190614
|
import { spawn as spawn6 } from "child_process";
|
|
189871
|
-
import * as
|
|
189872
|
-
import * as
|
|
189873
|
-
async function execCommand(serviceName, command) {
|
|
190615
|
+
import * as fs22 from "fs";
|
|
190616
|
+
import * as path20 from "path";
|
|
190617
|
+
async function execCommand(serviceName, command, instanceKey = "default") {
|
|
189874
190618
|
if (command.length === 0) {
|
|
189875
190619
|
console.error(
|
|
189876
190620
|
"Error: No command provided. Usage: specific exec <service> -- <command>"
|
|
@@ -189897,14 +190641,14 @@ async function execCommand(serviceName, command) {
|
|
|
189897
190641
|
}
|
|
189898
190642
|
}
|
|
189899
190643
|
};
|
|
189900
|
-
const configPath =
|
|
189901
|
-
if (!
|
|
190644
|
+
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
190645
|
+
if (!fs22.existsSync(configPath)) {
|
|
189902
190646
|
console.error("Error: No specific.hcl found in current directory");
|
|
189903
190647
|
process.exit(1);
|
|
189904
190648
|
}
|
|
189905
190649
|
let config;
|
|
189906
190650
|
try {
|
|
189907
|
-
const hcl =
|
|
190651
|
+
const hcl = fs22.readFileSync(configPath, "utf-8");
|
|
189908
190652
|
config = await parseConfig(hcl);
|
|
189909
190653
|
} catch (err) {
|
|
189910
190654
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -189921,7 +190665,7 @@ async function execCommand(serviceName, command) {
|
|
|
189921
190665
|
const required = findRequiredResources(service);
|
|
189922
190666
|
let resources = /* @__PURE__ */ new Map();
|
|
189923
190667
|
const hasRequiredResources = required.postgres.length > 0 || required.redis.length > 0 || required.storage.length > 0;
|
|
189924
|
-
stateManager = new InstanceStateManager(process.cwd());
|
|
190668
|
+
stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
189925
190669
|
await stateManager.cleanStaleState();
|
|
189926
190670
|
const existingInstances = await stateManager.getExistingInstances();
|
|
189927
190671
|
if (hasRequiredResources) {
|
|
@@ -189976,7 +190720,7 @@ async function execCommand(serviceName, command) {
|
|
|
189976
190720
|
storage: required.storage
|
|
189977
190721
|
},
|
|
189978
190722
|
stateManager,
|
|
189979
|
-
dataDir:
|
|
190723
|
+
dataDir: `.specific/keys/${instanceKey}/data`,
|
|
189980
190724
|
portAllocator: new PortAllocator(),
|
|
189981
190725
|
callbacks: {
|
|
189982
190726
|
log: (msg) => console.error(msg)
|
|
@@ -190041,8 +190785,8 @@ async function execCommand(serviceName, command) {
|
|
|
190041
190785
|
|
|
190042
190786
|
// src/commands/psql.tsx
|
|
190043
190787
|
import { spawn as spawn7 } from "child_process";
|
|
190044
|
-
async function psqlCommand(databaseName) {
|
|
190045
|
-
const stateManager = new InstanceStateManager(process.cwd());
|
|
190788
|
+
async function psqlCommand(databaseName, instanceKey = "default") {
|
|
190789
|
+
const stateManager = new InstanceStateManager(process.cwd(), instanceKey);
|
|
190046
190790
|
await stateManager.cleanStaleState();
|
|
190047
190791
|
const existingInstances = await stateManager.getExistingInstances();
|
|
190048
190792
|
if (!existingInstances) {
|
|
@@ -190098,41 +190842,85 @@ async function psqlCommand(databaseName) {
|
|
|
190098
190842
|
import React6, { useState as useState6, useEffect as useEffect6 } from "react";
|
|
190099
190843
|
import { render as render6, Text as Text6, Box as Box6 } from "ink";
|
|
190100
190844
|
import Spinner6 from "ink-spinner";
|
|
190101
|
-
import * as
|
|
190102
|
-
import * as
|
|
190103
|
-
function CleanUI() {
|
|
190845
|
+
import * as fs23 from "fs";
|
|
190846
|
+
import * as path21 from "path";
|
|
190847
|
+
function CleanUI({ instanceKey }) {
|
|
190104
190848
|
const [state, setState] = useState6({ status: "checking" });
|
|
190105
190849
|
useEffect6(() => {
|
|
190106
190850
|
async function clean() {
|
|
190107
190851
|
const projectRoot = process.cwd();
|
|
190108
|
-
const specificDir =
|
|
190109
|
-
if (!
|
|
190852
|
+
const specificDir = path21.join(projectRoot, ".specific");
|
|
190853
|
+
if (!fs23.existsSync(specificDir)) {
|
|
190110
190854
|
setState({ status: "nothing" });
|
|
190111
190855
|
return;
|
|
190112
190856
|
}
|
|
190113
|
-
|
|
190114
|
-
|
|
190115
|
-
|
|
190116
|
-
|
|
190117
|
-
|
|
190118
|
-
|
|
190119
|
-
|
|
190120
|
-
|
|
190121
|
-
|
|
190122
|
-
|
|
190123
|
-
|
|
190124
|
-
|
|
190125
|
-
|
|
190126
|
-
|
|
190127
|
-
|
|
190128
|
-
|
|
190129
|
-
|
|
190130
|
-
|
|
190131
|
-
|
|
190857
|
+
if (instanceKey) {
|
|
190858
|
+
const keyDir = path21.join(specificDir, "keys", instanceKey);
|
|
190859
|
+
if (!fs23.existsSync(keyDir)) {
|
|
190860
|
+
setState({ status: "nothing" });
|
|
190861
|
+
return;
|
|
190862
|
+
}
|
|
190863
|
+
const stateManager = new InstanceStateManager(projectRoot, instanceKey);
|
|
190864
|
+
const existingInstances = await stateManager.getExistingInstances();
|
|
190865
|
+
if (existingInstances) {
|
|
190866
|
+
setState({
|
|
190867
|
+
status: "error",
|
|
190868
|
+
error: `Cannot clean while 'specific dev' is running for key "${instanceKey}" (PID ${existingInstances.owner.pid}). Please stop it first.`
|
|
190869
|
+
});
|
|
190870
|
+
return;
|
|
190871
|
+
}
|
|
190872
|
+
await stateManager.cleanStaleState();
|
|
190873
|
+
setState({ status: "cleaning" });
|
|
190874
|
+
try {
|
|
190875
|
+
fs23.rmSync(keyDir, { recursive: true, force: true });
|
|
190876
|
+
setState({ status: "success" });
|
|
190877
|
+
} catch (err) {
|
|
190878
|
+
setState({
|
|
190879
|
+
status: "error",
|
|
190880
|
+
error: err instanceof Error ? err.message : String(err)
|
|
190881
|
+
});
|
|
190882
|
+
}
|
|
190883
|
+
} else {
|
|
190884
|
+
const keysDir = path21.join(specificDir, "keys");
|
|
190885
|
+
if (fs23.existsSync(keysDir)) {
|
|
190886
|
+
const keys = fs23.readdirSync(keysDir).filter(
|
|
190887
|
+
(f) => fs23.statSync(path21.join(keysDir, f)).isDirectory()
|
|
190888
|
+
);
|
|
190889
|
+
for (const key of keys) {
|
|
190890
|
+
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
190891
|
+
const existingInstances2 = await stateManager2.getExistingInstances();
|
|
190892
|
+
if (existingInstances2) {
|
|
190893
|
+
setState({
|
|
190894
|
+
status: "error",
|
|
190895
|
+
error: `Cannot clean while 'specific dev' is running for key "${key}" (PID ${existingInstances2.owner.pid}). Please stop it first.`
|
|
190896
|
+
});
|
|
190897
|
+
return;
|
|
190898
|
+
}
|
|
190899
|
+
}
|
|
190900
|
+
}
|
|
190901
|
+
const stateManager = new InstanceStateManager(projectRoot);
|
|
190902
|
+
const existingInstances = await stateManager.getExistingInstances();
|
|
190903
|
+
if (existingInstances) {
|
|
190904
|
+
setState({
|
|
190905
|
+
status: "error",
|
|
190906
|
+
error: `Cannot clean while 'specific dev' is running (PID ${existingInstances.owner.pid}). Please stop it first.`
|
|
190907
|
+
});
|
|
190908
|
+
return;
|
|
190909
|
+
}
|
|
190910
|
+
setState({ status: "cleaning" });
|
|
190911
|
+
try {
|
|
190912
|
+
fs23.rmSync(specificDir, { recursive: true, force: true });
|
|
190913
|
+
setState({ status: "success" });
|
|
190914
|
+
} catch (err) {
|
|
190915
|
+
setState({
|
|
190916
|
+
status: "error",
|
|
190917
|
+
error: err instanceof Error ? err.message : String(err)
|
|
190918
|
+
});
|
|
190919
|
+
}
|
|
190132
190920
|
}
|
|
190133
190921
|
}
|
|
190134
190922
|
clean();
|
|
190135
|
-
}, []);
|
|
190923
|
+
}, [instanceKey]);
|
|
190136
190924
|
if (state.status === "checking") {
|
|
190137
190925
|
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
190926
|
}
|
|
@@ -190143,18 +190931,24 @@ function CleanUI() {
|
|
|
190143
190931
|
return /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Error: ", state.error);
|
|
190144
190932
|
}
|
|
190145
190933
|
if (state.status === "nothing") {
|
|
190146
|
-
|
|
190934
|
+
const target2 = instanceKey ? `key "${instanceKey}"` : ".specific directory";
|
|
190935
|
+
return /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "Nothing to clean (", target2, " does not exist)");
|
|
190147
190936
|
}
|
|
190148
|
-
|
|
190937
|
+
const target = instanceKey ? `key "${instanceKey}"` : ".specific directory";
|
|
190938
|
+
return /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "Cleaned ", target);
|
|
190149
190939
|
}
|
|
190150
|
-
function cleanCommand() {
|
|
190151
|
-
|
|
190940
|
+
function cleanCommand(instanceKey) {
|
|
190941
|
+
if (instanceKey) {
|
|
190942
|
+
render6(/* @__PURE__ */ React6.createElement(CleanUI, { instanceKey }));
|
|
190943
|
+
} else {
|
|
190944
|
+
render6(/* @__PURE__ */ React6.createElement(CleanUI, null));
|
|
190945
|
+
}
|
|
190152
190946
|
}
|
|
190153
190947
|
|
|
190154
190948
|
// src/commands/secrets.tsx
|
|
190155
190949
|
import React7, { useState as useState7, useEffect as useEffect7 } from "react";
|
|
190156
190950
|
import { render as render7, Text as Text7, Box as Box7, useInput as useInput4, useApp as useApp5 } from "ink";
|
|
190157
|
-
import * as
|
|
190951
|
+
import * as fs24 from "fs";
|
|
190158
190952
|
var HEADER_COMMENT = "# Do not commit this file - it contains secrets\n";
|
|
190159
190953
|
function SetSecretUI({ secretName }) {
|
|
190160
190954
|
const { exit } = useApp5();
|
|
@@ -190173,8 +190967,8 @@ function SetSecretUI({ secretName }) {
|
|
|
190173
190967
|
const hclLine = `${secretName} = "${escapedValue}"`;
|
|
190174
190968
|
let content = "";
|
|
190175
190969
|
let hasHeader = false;
|
|
190176
|
-
if (
|
|
190177
|
-
content =
|
|
190970
|
+
if (fs24.existsSync(SECRETS_FILE)) {
|
|
190971
|
+
content = fs24.readFileSync(SECRETS_FILE, "utf-8");
|
|
190178
190972
|
hasHeader = content.startsWith("#");
|
|
190179
190973
|
const lines = content.split("\n");
|
|
190180
190974
|
const newLines = [];
|
|
@@ -190189,7 +190983,7 @@ function SetSecretUI({ secretName }) {
|
|
|
190189
190983
|
}
|
|
190190
190984
|
}
|
|
190191
190985
|
if (found) {
|
|
190192
|
-
|
|
190986
|
+
fs24.writeFileSync(SECRETS_FILE, newLines.join("\n"));
|
|
190193
190987
|
setDone(true);
|
|
190194
190988
|
return;
|
|
190195
190989
|
}
|
|
@@ -190202,7 +190996,7 @@ function SetSecretUI({ secretName }) {
|
|
|
190202
190996
|
}
|
|
190203
190997
|
newContent += `${hclLine}
|
|
190204
190998
|
`;
|
|
190205
|
-
|
|
190999
|
+
fs24.writeFileSync(SECRETS_FILE, newContent);
|
|
190206
191000
|
setDone(true);
|
|
190207
191001
|
} catch (err) {
|
|
190208
191002
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -190262,7 +191056,7 @@ async function secretsCommand(action, secretName) {
|
|
|
190262
191056
|
// src/commands/config.tsx
|
|
190263
191057
|
import React8, { useState as useState8, useEffect as useEffect8 } from "react";
|
|
190264
191058
|
import { render as render8, Text as Text8, Box as Box8, useInput as useInput5, useApp as useApp6 } from "ink";
|
|
190265
|
-
import * as
|
|
191059
|
+
import * as fs25 from "fs";
|
|
190266
191060
|
var HEADER_COMMENT2 = "# Configuration values for this project\n# These values override defaults defined in specific.hcl\n";
|
|
190267
191061
|
function SetConfigUI({ configName, initialValue }) {
|
|
190268
191062
|
const { exit } = useApp6();
|
|
@@ -190281,8 +191075,8 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190281
191075
|
const hclLine = `${configName} = "${escapedValue}"`;
|
|
190282
191076
|
let content = "";
|
|
190283
191077
|
let hasHeader = false;
|
|
190284
|
-
if (
|
|
190285
|
-
content =
|
|
191078
|
+
if (fs25.existsSync(CONFIG_FILE)) {
|
|
191079
|
+
content = fs25.readFileSync(CONFIG_FILE, "utf-8");
|
|
190286
191080
|
hasHeader = content.startsWith("#");
|
|
190287
191081
|
const lines = content.split("\n");
|
|
190288
191082
|
const newLines = [];
|
|
@@ -190297,7 +191091,7 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190297
191091
|
}
|
|
190298
191092
|
}
|
|
190299
191093
|
if (found) {
|
|
190300
|
-
|
|
191094
|
+
fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
|
|
190301
191095
|
setDone(true);
|
|
190302
191096
|
return;
|
|
190303
191097
|
}
|
|
@@ -190310,7 +191104,7 @@ function SetConfigUI({ configName, initialValue }) {
|
|
|
190310
191104
|
}
|
|
190311
191105
|
newContent += `${hclLine}
|
|
190312
191106
|
`;
|
|
190313
|
-
|
|
191107
|
+
fs25.writeFileSync(CONFIG_FILE, newContent);
|
|
190314
191108
|
setDone(true);
|
|
190315
191109
|
} catch (err) {
|
|
190316
191110
|
setError(err instanceof Error ? err.message : String(err));
|
|
@@ -190349,8 +191143,8 @@ async function configSetCommand(configName, configValue) {
|
|
|
190349
191143
|
const hclLine = `${configName} = "${escapedValue}"`;
|
|
190350
191144
|
let content = "";
|
|
190351
191145
|
let hasHeader = false;
|
|
190352
|
-
if (
|
|
190353
|
-
content =
|
|
191146
|
+
if (fs25.existsSync(CONFIG_FILE)) {
|
|
191147
|
+
content = fs25.readFileSync(CONFIG_FILE, "utf-8");
|
|
190354
191148
|
hasHeader = content.startsWith("#");
|
|
190355
191149
|
const lines = content.split("\n");
|
|
190356
191150
|
const newLines = [];
|
|
@@ -190365,7 +191159,7 @@ async function configSetCommand(configName, configValue) {
|
|
|
190365
191159
|
}
|
|
190366
191160
|
}
|
|
190367
191161
|
if (found) {
|
|
190368
|
-
|
|
191162
|
+
fs25.writeFileSync(CONFIG_FILE, newLines.join("\n"));
|
|
190369
191163
|
console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
|
|
190370
191164
|
return;
|
|
190371
191165
|
}
|
|
@@ -190378,7 +191172,7 @@ async function configSetCommand(configName, configValue) {
|
|
|
190378
191172
|
}
|
|
190379
191173
|
newContent += `${hclLine}
|
|
190380
191174
|
`;
|
|
190381
|
-
|
|
191175
|
+
fs25.writeFileSync(CONFIG_FILE, newContent);
|
|
190382
191176
|
console.log(`Config '${configName}' saved to ${CONFIG_FILE}`);
|
|
190383
191177
|
} catch (err) {
|
|
190384
191178
|
console.error("Error:", err instanceof Error ? err.message : String(err));
|
|
@@ -190497,24 +191291,38 @@ function logoutCommand() {
|
|
|
190497
191291
|
var program = new Command();
|
|
190498
191292
|
var env = "production";
|
|
190499
191293
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
190500
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
191294
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.39").enablePositionalOptions();
|
|
190501
191295
|
program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
|
|
190502
191296
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
190503
191297
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
|
190504
|
-
program.command("dev").description("Start local development environment").action(
|
|
191298
|
+
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) => {
|
|
191299
|
+
const key = options2.key ?? getDefaultKey();
|
|
191300
|
+
devCommand(key);
|
|
191301
|
+
});
|
|
190505
191302
|
program.command("deploy [environment]").description("Deploy to Specific infrastructure").option("--skip-build-test", "Skip local build testing before deploy").action((environment, options2) => {
|
|
190506
191303
|
deployCommand(environment, options2);
|
|
190507
191304
|
});
|
|
190508
|
-
program.command("exec <service> [args...]").description("Run a one-off command with service environment").passThroughOptions().action(async (service, args) => {
|
|
191305
|
+
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
191306
|
const filteredArgs = args[0] === "--" ? args.slice(1) : args;
|
|
190510
|
-
|
|
191307
|
+
const key = options2.key ?? getDefaultKey();
|
|
191308
|
+
await execCommand(service, filteredArgs, key);
|
|
191309
|
+
});
|
|
191310
|
+
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) => {
|
|
191311
|
+
const key = options2.key ?? getDefaultKey();
|
|
191312
|
+
psqlCommand(database, key);
|
|
191313
|
+
});
|
|
191314
|
+
program.command("clean").description("Remove .specific directory for a clean slate").option("-k, --key <key>", "Clean only the specified dev environment key").action((options2) => {
|
|
191315
|
+
cleanCommand(options2.key);
|
|
190511
191316
|
});
|
|
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
191317
|
program.command("secrets [action] [name]").description("Manage secrets").action(secretsCommand);
|
|
190515
191318
|
program.command("config [action] [name] [value]").description("Manage configuration values").action(configCommand);
|
|
190516
191319
|
program.command("login").description("Log in to Specific").action(loginCommand);
|
|
190517
191320
|
program.command("logout").description("Log out of Specific").action(logoutCommand);
|
|
191321
|
+
var commandName = process.argv[2] || "help";
|
|
191322
|
+
trackEvent("cli_command_invoked", { command: commandName });
|
|
191323
|
+
process.on("beforeExit", async () => {
|
|
191324
|
+
await shutdown();
|
|
191325
|
+
});
|
|
190518
191326
|
program.parse();
|
|
190519
191327
|
/*! Bundled license information:
|
|
190520
191328
|
|