@specific.dev/cli 0.1.38 → 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 +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/__next.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.databases.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/cli.js +452 -330
- package/package.json +3 -2
- /package/dist/admin/_next/static/{dtzWfchSIjRVrII5xoJl6 → cNL40bpv360RCjyOls7vD}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{dtzWfchSIjRVrII5xoJl6 → cNL40bpv360RCjyOls7vD}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{dtzWfchSIjRVrII5xoJl6 → cNL40bpv360RCjyOls7vD}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -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";
|
|
@@ -182405,6 +182405,106 @@ function generateCertificate(domain, keys = []) {
|
|
|
182405
182405
|
};
|
|
182406
182406
|
}
|
|
182407
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
|
+
|
|
182408
182508
|
// src/commands/init.tsx
|
|
182409
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.`;
|
|
182410
182510
|
var CURSOR_MDC_CONTENT = `---
|
|
@@ -182422,67 +182522,67 @@ var options = [
|
|
|
182422
182522
|
{ id: "other", label: "Other / Manual" }
|
|
182423
182523
|
];
|
|
182424
182524
|
function isGitProject() {
|
|
182425
|
-
const gitPath =
|
|
182426
|
-
return
|
|
182525
|
+
const gitPath = path3.join(process.cwd(), ".git");
|
|
182526
|
+
return fs3.existsSync(gitPath);
|
|
182427
182527
|
}
|
|
182428
182528
|
function detectExistingAgents() {
|
|
182429
182529
|
const detected = {};
|
|
182430
|
-
const cursorDir =
|
|
182431
|
-
if (
|
|
182530
|
+
const cursorDir = path3.join(process.cwd(), ".cursor");
|
|
182531
|
+
if (fs3.existsSync(cursorDir)) {
|
|
182432
182532
|
detected["cursor"] = true;
|
|
182433
182533
|
}
|
|
182434
|
-
const claudeDir =
|
|
182435
|
-
const claudeMd =
|
|
182436
|
-
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)) {
|
|
182437
182537
|
detected["claude"] = true;
|
|
182438
182538
|
}
|
|
182439
|
-
const agentsMd =
|
|
182440
|
-
if (
|
|
182539
|
+
const agentsMd = path3.join(process.cwd(), "AGENTS.md");
|
|
182540
|
+
if (fs3.existsSync(agentsMd)) {
|
|
182441
182541
|
detected["codex"] = true;
|
|
182442
182542
|
}
|
|
182443
182543
|
return detected;
|
|
182444
182544
|
}
|
|
182445
182545
|
function appendOrCreateFile(filePath, content) {
|
|
182446
|
-
if (
|
|
182447
|
-
const existing =
|
|
182546
|
+
if (fs3.existsSync(filePath)) {
|
|
182547
|
+
const existing = fs3.readFileSync(filePath, "utf-8");
|
|
182448
182548
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
182449
182549
|
return "unchanged";
|
|
182450
182550
|
}
|
|
182451
182551
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
182452
|
-
|
|
182552
|
+
fs3.writeFileSync(filePath, existing + separator + content + "\n");
|
|
182453
182553
|
return "modified";
|
|
182454
182554
|
} else {
|
|
182455
|
-
|
|
182555
|
+
fs3.writeFileSync(filePath, content + "\n");
|
|
182456
182556
|
return "created";
|
|
182457
182557
|
}
|
|
182458
182558
|
}
|
|
182459
182559
|
function addToGitignore() {
|
|
182460
|
-
const gitignorePath =
|
|
182560
|
+
const gitignorePath = path3.join(process.cwd(), ".gitignore");
|
|
182461
182561
|
const entries = [".specific", "specific.secrets"];
|
|
182462
|
-
if (
|
|
182463
|
-
const existing =
|
|
182562
|
+
if (fs3.existsSync(gitignorePath)) {
|
|
182563
|
+
const existing = fs3.readFileSync(gitignorePath, "utf-8");
|
|
182464
182564
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
182465
182565
|
const missingEntries = entries.filter((entry) => !lines.includes(entry));
|
|
182466
182566
|
if (missingEntries.length === 0) {
|
|
182467
182567
|
return "unchanged";
|
|
182468
182568
|
}
|
|
182469
182569
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
182470
|
-
|
|
182570
|
+
fs3.writeFileSync(
|
|
182471
182571
|
gitignorePath,
|
|
182472
182572
|
existing + separator + missingEntries.join("\n") + "\n"
|
|
182473
182573
|
);
|
|
182474
182574
|
return "modified";
|
|
182475
182575
|
} else {
|
|
182476
|
-
|
|
182576
|
+
fs3.writeFileSync(gitignorePath, entries.join("\n") + "\n");
|
|
182477
182577
|
return "created";
|
|
182478
182578
|
}
|
|
182479
182579
|
}
|
|
182480
182580
|
function configureClaudeCodePermissions() {
|
|
182481
|
-
const claudeDir =
|
|
182482
|
-
const settingsPath =
|
|
182581
|
+
const claudeDir = path3.join(process.cwd(), ".claude");
|
|
182582
|
+
const settingsPath = path3.join(claudeDir, "settings.local.json");
|
|
182483
182583
|
const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
|
|
182484
|
-
if (
|
|
182485
|
-
const existing = JSON.parse(
|
|
182584
|
+
if (fs3.existsSync(settingsPath)) {
|
|
182585
|
+
const existing = JSON.parse(fs3.readFileSync(settingsPath, "utf-8"));
|
|
182486
182586
|
const allowList = existing?.permissions?.allow || [];
|
|
182487
182587
|
const missingPermissions = permissions.filter(
|
|
182488
182588
|
(p) => !allowList.includes(p)
|
|
@@ -182497,39 +182597,39 @@ function configureClaudeCodePermissions() {
|
|
|
182497
182597
|
existing.permissions.allow = [];
|
|
182498
182598
|
}
|
|
182499
182599
|
existing.permissions.allow.push(...missingPermissions);
|
|
182500
|
-
|
|
182600
|
+
fs3.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
|
|
182501
182601
|
return "modified";
|
|
182502
182602
|
}
|
|
182503
|
-
if (!
|
|
182504
|
-
|
|
182603
|
+
if (!fs3.existsSync(claudeDir)) {
|
|
182604
|
+
fs3.mkdirSync(claudeDir);
|
|
182505
182605
|
}
|
|
182506
182606
|
const settings = {
|
|
182507
182607
|
permissions: {
|
|
182508
182608
|
allow: permissions
|
|
182509
182609
|
}
|
|
182510
182610
|
};
|
|
182511
|
-
|
|
182611
|
+
fs3.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
182512
182612
|
return "created";
|
|
182513
182613
|
}
|
|
182514
182614
|
function createCursorRule() {
|
|
182515
|
-
const cursorDir =
|
|
182516
|
-
const rulesDir =
|
|
182517
|
-
const mdcPath =
|
|
182518
|
-
if (
|
|
182519
|
-
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");
|
|
182520
182620
|
if (existing.includes("specific docs") || existing.includes("specific check")) {
|
|
182521
182621
|
return "unchanged";
|
|
182522
182622
|
}
|
|
182523
|
-
|
|
182623
|
+
fs3.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
182524
182624
|
return "modified";
|
|
182525
182625
|
}
|
|
182526
|
-
if (!
|
|
182527
|
-
|
|
182626
|
+
if (!fs3.existsSync(cursorDir)) {
|
|
182627
|
+
fs3.mkdirSync(cursorDir);
|
|
182528
182628
|
}
|
|
182529
|
-
if (!
|
|
182530
|
-
|
|
182629
|
+
if (!fs3.existsSync(rulesDir)) {
|
|
182630
|
+
fs3.mkdirSync(rulesDir);
|
|
182531
182631
|
}
|
|
182532
|
-
|
|
182632
|
+
fs3.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
|
|
182533
182633
|
return "created";
|
|
182534
182634
|
}
|
|
182535
182635
|
function configureAgents(checked) {
|
|
@@ -182545,7 +182645,7 @@ function configureAgents(checked) {
|
|
|
182545
182645
|
agents.filesModified.push(".cursor/rules/specific.mdc");
|
|
182546
182646
|
}
|
|
182547
182647
|
if (checked["claude"]) {
|
|
182548
|
-
const claudeMdPath =
|
|
182648
|
+
const claudeMdPath = path3.join(process.cwd(), "CLAUDE.md");
|
|
182549
182649
|
const status = appendOrCreateFile(claudeMdPath, SPECIFIC_INSTRUCTIONS);
|
|
182550
182650
|
if (status === "created") agents.filesCreated.push("CLAUDE.md");
|
|
182551
182651
|
else if (status === "modified") agents.filesModified.push("CLAUDE.md");
|
|
@@ -182556,7 +182656,7 @@ function configureAgents(checked) {
|
|
|
182556
182656
|
agents.filesModified.push(".claude/settings.local.json");
|
|
182557
182657
|
}
|
|
182558
182658
|
if (checked["codex"]) {
|
|
182559
|
-
const agentsMdPath =
|
|
182659
|
+
const agentsMdPath = path3.join(process.cwd(), "AGENTS.md");
|
|
182560
182660
|
const status = appendOrCreateFile(agentsMdPath, SPECIFIC_INSTRUCTIONS);
|
|
182561
182661
|
if (status === "created") agents.filesCreated.push("AGENTS.md");
|
|
182562
182662
|
else if (status === "modified") agents.filesModified.push("AGENTS.md");
|
|
@@ -182632,6 +182732,9 @@ function InitUI() {
|
|
|
182632
182732
|
} else if ((key.return || input === " ") && isSubmitFocused) {
|
|
182633
182733
|
const configResult = configureAgents(checked);
|
|
182634
182734
|
setResult(configResult);
|
|
182735
|
+
trackEvent("project_initialized", {
|
|
182736
|
+
agents: Object.keys(checked).filter((k) => checked[k])
|
|
182737
|
+
});
|
|
182635
182738
|
setPhase("done");
|
|
182636
182739
|
}
|
|
182637
182740
|
});
|
|
@@ -182675,11 +182778,11 @@ function initCommand() {
|
|
|
182675
182778
|
}
|
|
182676
182779
|
|
|
182677
182780
|
// src/commands/docs.tsx
|
|
182678
|
-
import { readFileSync as
|
|
182679
|
-
import { join as
|
|
182781
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
182782
|
+
import { join as join4, dirname } from "path";
|
|
182680
182783
|
import { fileURLToPath } from "url";
|
|
182681
182784
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
182682
|
-
var docsDir =
|
|
182785
|
+
var docsDir = join4(__dirname, "docs");
|
|
182683
182786
|
function docsCommand(path22) {
|
|
182684
182787
|
const docPath = resolveDocPath(path22);
|
|
182685
182788
|
if (!docPath) {
|
|
@@ -182690,20 +182793,20 @@ Run 'specific docs' to see available topics.`
|
|
|
182690
182793
|
);
|
|
182691
182794
|
process.exit(1);
|
|
182692
182795
|
}
|
|
182693
|
-
const content =
|
|
182796
|
+
const content = readFileSync4(docPath, "utf-8");
|
|
182694
182797
|
console.log(content);
|
|
182695
182798
|
}
|
|
182696
182799
|
function resolveDocPath(path22) {
|
|
182697
182800
|
if (!path22) {
|
|
182698
|
-
const indexPath2 =
|
|
182699
|
-
return
|
|
182801
|
+
const indexPath2 = join4(docsDir, "index.md");
|
|
182802
|
+
return existsSync4(indexPath2) ? indexPath2 : null;
|
|
182700
182803
|
}
|
|
182701
|
-
const directPath =
|
|
182702
|
-
if (
|
|
182804
|
+
const directPath = join4(docsDir, `${path22}.md`);
|
|
182805
|
+
if (existsSync4(directPath)) {
|
|
182703
182806
|
return directPath;
|
|
182704
182807
|
}
|
|
182705
|
-
const indexPath =
|
|
182706
|
-
if (
|
|
182808
|
+
const indexPath = join4(docsDir, path22, "index.md");
|
|
182809
|
+
if (existsSync4(indexPath)) {
|
|
182707
182810
|
return indexPath;
|
|
182708
182811
|
}
|
|
182709
182812
|
return null;
|
|
@@ -182713,8 +182816,8 @@ function resolveDocPath(path22) {
|
|
|
182713
182816
|
import React2, { useState as useState2, useEffect as useEffect2 } from "react";
|
|
182714
182817
|
import { render as render2, Text as Text2, Box as Box2 } from "ink";
|
|
182715
182818
|
import Spinner2 from "ink-spinner";
|
|
182716
|
-
import * as
|
|
182717
|
-
import * as
|
|
182819
|
+
import * as fs4 from "fs";
|
|
182820
|
+
import * as path4 from "path";
|
|
182718
182821
|
|
|
182719
182822
|
// ../config/dist/parser.js
|
|
182720
182823
|
var import_hcl2_json_parser = __toESM(require_dist(), 1);
|
|
@@ -183196,8 +183299,8 @@ function CheckUI() {
|
|
|
183196
183299
|
const [state, setState] = useState2({ status: "loading" });
|
|
183197
183300
|
useEffect2(() => {
|
|
183198
183301
|
async function load() {
|
|
183199
|
-
const configPath =
|
|
183200
|
-
if (!
|
|
183302
|
+
const configPath = path4.join(process.cwd(), "specific.hcl");
|
|
183303
|
+
if (!fs4.existsSync(configPath)) {
|
|
183201
183304
|
setState({
|
|
183202
183305
|
status: "error",
|
|
183203
183306
|
error: "No specific.hcl found in current directory"
|
|
@@ -183205,7 +183308,7 @@ function CheckUI() {
|
|
|
183205
183308
|
return;
|
|
183206
183309
|
}
|
|
183207
183310
|
try {
|
|
183208
|
-
const hcl =
|
|
183311
|
+
const hcl = fs4.readFileSync(configPath, "utf-8");
|
|
183209
183312
|
const config2 = await parseConfig(hcl);
|
|
183210
183313
|
setState({ status: "success", config: config2 });
|
|
183211
183314
|
} catch (err) {
|
|
@@ -183237,8 +183340,8 @@ function checkCommand() {
|
|
|
183237
183340
|
import React3, { useState as useState3, useEffect as useEffect3, useRef } from "react";
|
|
183238
183341
|
import { render as render3, Text as Text3, Box as Box3, useApp as useApp2, Static } from "ink";
|
|
183239
183342
|
import Spinner3 from "ink-spinner";
|
|
183240
|
-
import * as
|
|
183241
|
-
import * as
|
|
183343
|
+
import * as fs13 from "fs";
|
|
183344
|
+
import * as path14 from "path";
|
|
183242
183345
|
|
|
183243
183346
|
// node_modules/chokidar/index.js
|
|
183244
183347
|
import { EventEmitter } from "node:events";
|
|
@@ -184992,8 +185095,8 @@ var PortAllocator = class {
|
|
|
184992
185095
|
};
|
|
184993
185096
|
|
|
184994
185097
|
// src/lib/dev/stable-port-allocator.ts
|
|
184995
|
-
import * as
|
|
184996
|
-
import * as
|
|
185098
|
+
import * as fs5 from "fs";
|
|
185099
|
+
import * as path5 from "path";
|
|
184997
185100
|
var PORT_RANGE_START2 = 4e4;
|
|
184998
185101
|
var PORT_RANGE_END2 = 49999;
|
|
184999
185102
|
var StablePortAllocator = class {
|
|
@@ -185002,16 +185105,16 @@ var StablePortAllocator = class {
|
|
|
185002
185105
|
savedPorts = {};
|
|
185003
185106
|
usedPorts = /* @__PURE__ */ new Set();
|
|
185004
185107
|
constructor(projectRoot, key = "default") {
|
|
185005
|
-
this.portsDir =
|
|
185006
|
-
this.portsFilePath =
|
|
185108
|
+
this.portsDir = path5.join(projectRoot, ".specific", "keys", key);
|
|
185109
|
+
this.portsFilePath = path5.join(this.portsDir, "ports.json");
|
|
185007
185110
|
this.loadPorts();
|
|
185008
185111
|
}
|
|
185009
185112
|
loadPorts() {
|
|
185010
|
-
if (!
|
|
185113
|
+
if (!fs5.existsSync(this.portsFilePath)) {
|
|
185011
185114
|
return;
|
|
185012
185115
|
}
|
|
185013
185116
|
try {
|
|
185014
|
-
const content =
|
|
185117
|
+
const content = fs5.readFileSync(this.portsFilePath, "utf-8");
|
|
185015
185118
|
const data = JSON.parse(content);
|
|
185016
185119
|
if (data.version === 1 && data.ports) {
|
|
185017
185120
|
this.savedPorts = data.ports;
|
|
@@ -185024,14 +185127,14 @@ var StablePortAllocator = class {
|
|
|
185024
185127
|
}
|
|
185025
185128
|
}
|
|
185026
185129
|
savePorts() {
|
|
185027
|
-
if (!
|
|
185028
|
-
|
|
185130
|
+
if (!fs5.existsSync(this.portsDir)) {
|
|
185131
|
+
fs5.mkdirSync(this.portsDir, { recursive: true });
|
|
185029
185132
|
}
|
|
185030
185133
|
const data = {
|
|
185031
185134
|
version: 1,
|
|
185032
185135
|
ports: this.savedPorts
|
|
185033
185136
|
};
|
|
185034
|
-
|
|
185137
|
+
fs5.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
|
|
185035
185138
|
}
|
|
185036
185139
|
allocateRandom() {
|
|
185037
185140
|
const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
|
|
@@ -185057,10 +185160,10 @@ var StablePortAllocator = class {
|
|
|
185057
185160
|
};
|
|
185058
185161
|
|
|
185059
185162
|
// src/lib/dev/database-manager.ts
|
|
185060
|
-
import * as
|
|
185061
|
-
import * as
|
|
185163
|
+
import * as fs8 from "fs";
|
|
185164
|
+
import * as path8 from "path";
|
|
185062
185165
|
import * as net from "net";
|
|
185063
|
-
import * as
|
|
185166
|
+
import * as os4 from "os";
|
|
185064
185167
|
import { spawn } from "child_process";
|
|
185065
185168
|
|
|
185066
185169
|
// src/lib/bin/types.ts
|
|
@@ -185098,17 +185201,17 @@ var ExtractionError = class extends Error {
|
|
|
185098
185201
|
};
|
|
185099
185202
|
|
|
185100
185203
|
// src/lib/bin/manager.ts
|
|
185101
|
-
import * as
|
|
185102
|
-
import * as
|
|
185103
|
-
import * as
|
|
185204
|
+
import * as fs6 from "fs";
|
|
185205
|
+
import * as path6 from "path";
|
|
185206
|
+
import * as os3 from "os";
|
|
185104
185207
|
import { createReadStream } from "fs";
|
|
185105
185208
|
import { createTarExtractor, extractTo } from "tar-vern";
|
|
185106
185209
|
function getBinBaseDir() {
|
|
185107
|
-
return
|
|
185210
|
+
return path6.join(os3.homedir(), ".specific", "bin");
|
|
185108
185211
|
}
|
|
185109
185212
|
function getPlatformInfo() {
|
|
185110
|
-
const platform5 =
|
|
185111
|
-
const arch3 =
|
|
185213
|
+
const platform5 = os3.platform();
|
|
185214
|
+
const arch3 = os3.arch();
|
|
185112
185215
|
if (platform5 !== "darwin" && platform5 !== "linux") {
|
|
185113
185216
|
throw new Error(
|
|
185114
185217
|
`Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
|
|
@@ -185127,7 +185230,7 @@ function getPlatformInfo() {
|
|
|
185127
185230
|
return { platform: platform5, arch: mappedArch };
|
|
185128
185231
|
}
|
|
185129
185232
|
function getBinaryDir(definition, version, platformInfo) {
|
|
185130
|
-
return
|
|
185233
|
+
return path6.join(
|
|
185131
185234
|
getBinBaseDir(),
|
|
185132
185235
|
definition.name,
|
|
185133
185236
|
version,
|
|
@@ -185137,8 +185240,8 @@ function getBinaryDir(definition, version, platformInfo) {
|
|
|
185137
185240
|
function isBinaryInstalled(definition, version, platformInfo) {
|
|
185138
185241
|
const binDir = getBinaryDir(definition, version, platformInfo);
|
|
185139
185242
|
for (const execPath of definition.executables) {
|
|
185140
|
-
const fullPath =
|
|
185141
|
-
if (!
|
|
185243
|
+
const fullPath = path6.join(binDir, execPath);
|
|
185244
|
+
if (!fs6.existsSync(fullPath)) {
|
|
185142
185245
|
return false;
|
|
185143
185246
|
}
|
|
185144
185247
|
}
|
|
@@ -185166,12 +185269,12 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185166
185269
|
10
|
|
185167
185270
|
);
|
|
185168
185271
|
let bytesDownloaded = 0;
|
|
185169
|
-
const parentDir =
|
|
185170
|
-
if (!
|
|
185171
|
-
|
|
185272
|
+
const parentDir = path6.dirname(destPath);
|
|
185273
|
+
if (!fs6.existsSync(parentDir)) {
|
|
185274
|
+
fs6.mkdirSync(parentDir, { recursive: true });
|
|
185172
185275
|
}
|
|
185173
185276
|
const partPath = destPath + ".part";
|
|
185174
|
-
const fileStream =
|
|
185277
|
+
const fileStream = fs6.createWriteStream(partPath);
|
|
185175
185278
|
try {
|
|
185176
185279
|
const reader = response.body.getReader();
|
|
185177
185280
|
while (true) {
|
|
@@ -185194,12 +185297,12 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185194
185297
|
else resolve5();
|
|
185195
185298
|
});
|
|
185196
185299
|
});
|
|
185197
|
-
|
|
185300
|
+
fs6.renameSync(partPath, destPath);
|
|
185198
185301
|
} catch (error) {
|
|
185199
185302
|
try {
|
|
185200
185303
|
fileStream.close();
|
|
185201
|
-
if (
|
|
185202
|
-
|
|
185304
|
+
if (fs6.existsSync(partPath)) {
|
|
185305
|
+
fs6.unlinkSync(partPath);
|
|
185203
185306
|
}
|
|
185204
185307
|
} catch {
|
|
185205
185308
|
}
|
|
@@ -185208,8 +185311,8 @@ async function downloadFile(url, destPath, onProgress) {
|
|
|
185208
185311
|
}
|
|
185209
185312
|
async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
185210
185313
|
onProgress?.({ phase: "extracting" });
|
|
185211
|
-
if (!
|
|
185212
|
-
|
|
185314
|
+
if (!fs6.existsSync(destDir)) {
|
|
185315
|
+
fs6.mkdirSync(destDir, { recursive: true });
|
|
185213
185316
|
}
|
|
185214
185317
|
try {
|
|
185215
185318
|
const fileStream = createReadStream(archivePath);
|
|
@@ -185217,9 +185320,9 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
|
|
|
185217
185320
|
await extractTo(entries, destDir);
|
|
185218
185321
|
onProgress?.({ phase: "finalizing" });
|
|
185219
185322
|
for (const execPath of definition.executables) {
|
|
185220
|
-
const fullPath =
|
|
185221
|
-
if (
|
|
185222
|
-
|
|
185323
|
+
const fullPath = path6.join(destDir, execPath);
|
|
185324
|
+
if (fs6.existsSync(fullPath)) {
|
|
185325
|
+
fs6.chmodSync(fullPath, 493);
|
|
185223
185326
|
}
|
|
185224
185327
|
}
|
|
185225
185328
|
} catch (error) {
|
|
@@ -185243,24 +185346,24 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
185243
185346
|
`Binary type definitions must have exactly one executable, got ${definition.executables.length}`
|
|
185244
185347
|
);
|
|
185245
185348
|
}
|
|
185246
|
-
if (!
|
|
185247
|
-
|
|
185349
|
+
if (!fs6.existsSync(binDir)) {
|
|
185350
|
+
fs6.mkdirSync(binDir, { recursive: true });
|
|
185248
185351
|
}
|
|
185249
|
-
const execPath =
|
|
185352
|
+
const execPath = path6.join(binDir, definition.executables[0]);
|
|
185250
185353
|
await downloadFile(url, execPath, onProgress);
|
|
185251
|
-
|
|
185354
|
+
fs6.chmodSync(execPath, 493);
|
|
185252
185355
|
onProgress?.({ phase: "finalizing" });
|
|
185253
185356
|
} else {
|
|
185254
|
-
const downloadDir =
|
|
185357
|
+
const downloadDir = path6.join(getBinBaseDir(), "downloads");
|
|
185255
185358
|
const archiveName = `${definition.name}-${resolvedVersion}-${platformInfo.platform}-${platformInfo.arch}.tar.gz`;
|
|
185256
|
-
const archivePath =
|
|
185359
|
+
const archivePath = path6.join(downloadDir, archiveName);
|
|
185257
185360
|
try {
|
|
185258
185361
|
await downloadFile(url, archivePath, onProgress);
|
|
185259
185362
|
await extractTarball(archivePath, binDir, definition, onProgress);
|
|
185260
185363
|
} finally {
|
|
185261
185364
|
try {
|
|
185262
|
-
if (
|
|
185263
|
-
|
|
185365
|
+
if (fs6.existsSync(archivePath)) {
|
|
185366
|
+
fs6.unlinkSync(archivePath);
|
|
185264
185367
|
}
|
|
185265
185368
|
} catch {
|
|
185266
185369
|
}
|
|
@@ -185269,10 +185372,10 @@ async function ensureBinary(definition, version, onProgress) {
|
|
|
185269
185372
|
}
|
|
185270
185373
|
const executables = {};
|
|
185271
185374
|
for (const execPath of definition.executables) {
|
|
185272
|
-
const name =
|
|
185273
|
-
executables[name] =
|
|
185375
|
+
const name = path6.basename(execPath);
|
|
185376
|
+
executables[name] = path6.join(binDir, execPath);
|
|
185274
185377
|
}
|
|
185275
|
-
const libraryPath = definition.libraryDir ?
|
|
185378
|
+
const libraryPath = definition.libraryDir ? path6.join(binDir, definition.libraryDir) : void 0;
|
|
185276
185379
|
return {
|
|
185277
185380
|
rootDir: binDir,
|
|
185278
185381
|
version: resolvedVersion,
|
|
@@ -185372,17 +185475,17 @@ var drizzleGatewayBinary = {
|
|
|
185372
185475
|
};
|
|
185373
185476
|
|
|
185374
185477
|
// src/lib/dev/debug-logger.ts
|
|
185375
|
-
import * as
|
|
185376
|
-
import * as
|
|
185478
|
+
import * as fs7 from "fs";
|
|
185479
|
+
import * as path7 from "path";
|
|
185377
185480
|
var DEBUG_LOG_PATH = ".specific/debug.log";
|
|
185378
185481
|
var logStream = null;
|
|
185379
185482
|
function getLogStream() {
|
|
185380
185483
|
if (logStream) {
|
|
185381
185484
|
return logStream;
|
|
185382
185485
|
}
|
|
185383
|
-
const logPath =
|
|
185384
|
-
|
|
185385
|
-
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" });
|
|
185386
185489
|
return logStream;
|
|
185387
185490
|
}
|
|
185388
185491
|
function writeLog(source, message) {
|
|
@@ -185425,7 +185528,7 @@ function getLibraryEnv(binary) {
|
|
|
185425
185528
|
if (!binary.libraryPath) {
|
|
185426
185529
|
return {};
|
|
185427
185530
|
}
|
|
185428
|
-
const platform5 =
|
|
185531
|
+
const platform5 = os4.platform();
|
|
185429
185532
|
if (platform5 === "darwin") {
|
|
185430
185533
|
return { DYLD_LIBRARY_PATH: binary.libraryPath };
|
|
185431
185534
|
} else if (platform5 === "linux") {
|
|
@@ -185435,15 +185538,15 @@ function getLibraryEnv(binary) {
|
|
|
185435
185538
|
}
|
|
185436
185539
|
async function startPostgres(pg, port, dataDir, onProgress) {
|
|
185437
185540
|
const binary = await ensureBinary(postgresBinary, void 0, onProgress);
|
|
185438
|
-
const dbDataPath =
|
|
185541
|
+
const dbDataPath = path8.join(process.cwd(), dataDir, pg.name);
|
|
185439
185542
|
const host = "127.0.0.1";
|
|
185440
185543
|
const user = "postgres";
|
|
185441
185544
|
const password = "postgres";
|
|
185442
185545
|
const libraryEnv = getLibraryEnv(binary);
|
|
185443
185546
|
const env2 = { ...process.env, ...libraryEnv };
|
|
185444
|
-
const dataExists =
|
|
185547
|
+
const dataExists = fs8.existsSync(dbDataPath);
|
|
185445
185548
|
if (!dataExists) {
|
|
185446
|
-
|
|
185549
|
+
fs8.mkdirSync(dbDataPath, { recursive: true });
|
|
185447
185550
|
await runCommand(
|
|
185448
185551
|
binary.executables["initdb"],
|
|
185449
185552
|
["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
|
|
@@ -185519,9 +185622,9 @@ async function startRedis(redis, port, onProgress) {
|
|
|
185519
185622
|
}
|
|
185520
185623
|
async function startStorage(storage, port, dataDir) {
|
|
185521
185624
|
const S3rver = (await import("s3rver")).default;
|
|
185522
|
-
const storageDataPath =
|
|
185523
|
-
if (!
|
|
185524
|
-
|
|
185625
|
+
const storageDataPath = path8.join(process.cwd(), dataDir, storage.name);
|
|
185626
|
+
if (!fs8.existsSync(storageDataPath)) {
|
|
185627
|
+
fs8.mkdirSync(storageDataPath, { recursive: true });
|
|
185525
185628
|
}
|
|
185526
185629
|
const host = "127.0.0.1";
|
|
185527
185630
|
const accessKey = "S3RVER";
|
|
@@ -185655,9 +185758,9 @@ import { spawn as spawn2 } from "child_process";
|
|
|
185655
185758
|
// src/lib/secrets/parser.ts
|
|
185656
185759
|
var import_hcl2_json_parser2 = __toESM(require_dist(), 1);
|
|
185657
185760
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
185658
|
-
import { existsSync as
|
|
185659
|
-
import * as
|
|
185660
|
-
import * as
|
|
185761
|
+
import { existsSync as existsSync9 } from "fs";
|
|
185762
|
+
import * as path9 from "path";
|
|
185763
|
+
import * as crypto2 from "crypto";
|
|
185661
185764
|
var { parseToObject: parseToObject2 } = import_hcl2_json_parser2.default;
|
|
185662
185765
|
var SECRETS_FILE = "specific.secrets";
|
|
185663
185766
|
var GENERATED_SECRETS_FILE = ".specific/generated-secrets.json";
|
|
@@ -185675,7 +185778,7 @@ async function parseSecretsFile(content) {
|
|
|
185675
185778
|
return secrets;
|
|
185676
185779
|
}
|
|
185677
185780
|
async function loadSecrets() {
|
|
185678
|
-
if (!
|
|
185781
|
+
if (!existsSync9(SECRETS_FILE)) {
|
|
185679
185782
|
return /* @__PURE__ */ new Map();
|
|
185680
185783
|
}
|
|
185681
185784
|
const content = await readFile(SECRETS_FILE, "utf-8");
|
|
@@ -185683,7 +185786,7 @@ async function loadSecrets() {
|
|
|
185683
185786
|
}
|
|
185684
185787
|
function generateRandomString(length = 64) {
|
|
185685
185788
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
185686
|
-
const bytes =
|
|
185789
|
+
const bytes = crypto2.randomBytes(length);
|
|
185687
185790
|
let result = "";
|
|
185688
185791
|
for (let i = 0; i < length; i++) {
|
|
185689
185792
|
result += chars[bytes[i] % chars.length];
|
|
@@ -185691,7 +185794,7 @@ function generateRandomString(length = 64) {
|
|
|
185691
185794
|
return result;
|
|
185692
185795
|
}
|
|
185693
185796
|
async function loadGeneratedSecrets() {
|
|
185694
|
-
if (!
|
|
185797
|
+
if (!existsSync9(GENERATED_SECRETS_FILE)) {
|
|
185695
185798
|
return /* @__PURE__ */ new Map();
|
|
185696
185799
|
}
|
|
185697
185800
|
const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
|
|
@@ -185700,13 +185803,13 @@ async function loadGeneratedSecrets() {
|
|
|
185700
185803
|
}
|
|
185701
185804
|
async function saveGeneratedSecret(name, value) {
|
|
185702
185805
|
let secrets = {};
|
|
185703
|
-
if (
|
|
185806
|
+
if (existsSync9(GENERATED_SECRETS_FILE)) {
|
|
185704
185807
|
const content = await readFile(GENERATED_SECRETS_FILE, "utf-8");
|
|
185705
185808
|
secrets = JSON.parse(content);
|
|
185706
185809
|
}
|
|
185707
185810
|
secrets[name] = value;
|
|
185708
|
-
const dir =
|
|
185709
|
-
if (!
|
|
185811
|
+
const dir = path9.dirname(GENERATED_SECRETS_FILE);
|
|
185812
|
+
if (!existsSync9(dir)) {
|
|
185710
185813
|
await mkdir(dir, { recursive: true });
|
|
185711
185814
|
}
|
|
185712
185815
|
await writeFile(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
|
|
@@ -185736,7 +185839,7 @@ async function prepareSecrets(secretsConfig) {
|
|
|
185736
185839
|
// src/lib/config/parser.ts
|
|
185737
185840
|
var import_hcl2_json_parser3 = __toESM(require_dist(), 1);
|
|
185738
185841
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
185739
|
-
import { existsSync as
|
|
185842
|
+
import { existsSync as existsSync10 } from "fs";
|
|
185740
185843
|
var { parseToObject: parseToObject3 } = import_hcl2_json_parser3.default;
|
|
185741
185844
|
var CONFIG_FILE = "specific.config";
|
|
185742
185845
|
async function parseConfigFile(content) {
|
|
@@ -185753,7 +185856,7 @@ async function parseConfigFile(content) {
|
|
|
185753
185856
|
return configs;
|
|
185754
185857
|
}
|
|
185755
185858
|
async function loadConfigs() {
|
|
185756
|
-
if (!
|
|
185859
|
+
if (!existsSync10(CONFIG_FILE)) {
|
|
185757
185860
|
return /* @__PURE__ */ new Map();
|
|
185758
185861
|
}
|
|
185759
185862
|
const content = await readFile2(CONFIG_FILE, "utf-8");
|
|
@@ -186065,8 +186168,8 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
|
|
|
186065
186168
|
}
|
|
186066
186169
|
|
|
186067
186170
|
// src/lib/dev/instance-state.ts
|
|
186068
|
-
import * as
|
|
186069
|
-
import * as
|
|
186171
|
+
import * as fs9 from "fs";
|
|
186172
|
+
import * as path10 from "path";
|
|
186070
186173
|
var InstanceStateManager = class {
|
|
186071
186174
|
stateDir;
|
|
186072
186175
|
statePath;
|
|
@@ -186075,16 +186178,16 @@ var InstanceStateManager = class {
|
|
|
186075
186178
|
key;
|
|
186076
186179
|
constructor(projectRoot, key = "default") {
|
|
186077
186180
|
this.key = key;
|
|
186078
|
-
this.stateDir =
|
|
186079
|
-
this.statePath =
|
|
186080
|
-
this.lockPath =
|
|
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");
|
|
186081
186184
|
}
|
|
186082
186185
|
getKey() {
|
|
186083
186186
|
return this.key;
|
|
186084
186187
|
}
|
|
186085
186188
|
ensureStateDir() {
|
|
186086
|
-
if (!
|
|
186087
|
-
|
|
186189
|
+
if (!fs9.existsSync(this.stateDir)) {
|
|
186190
|
+
fs9.mkdirSync(this.stateDir, { recursive: true });
|
|
186088
186191
|
}
|
|
186089
186192
|
}
|
|
186090
186193
|
isProcessRunning(pid) {
|
|
@@ -186101,15 +186204,15 @@ var InstanceStateManager = class {
|
|
|
186101
186204
|
const startTime = Date.now();
|
|
186102
186205
|
while (Date.now() - startTime < timeoutMs) {
|
|
186103
186206
|
try {
|
|
186104
|
-
const fd =
|
|
186207
|
+
const fd = fs9.openSync(
|
|
186105
186208
|
this.lockPath,
|
|
186106
|
-
|
|
186209
|
+
fs9.constants.O_CREAT | fs9.constants.O_EXCL | fs9.constants.O_WRONLY
|
|
186107
186210
|
);
|
|
186108
|
-
|
|
186109
|
-
|
|
186211
|
+
fs9.writeSync(fd, String(process.pid));
|
|
186212
|
+
fs9.closeSync(fd);
|
|
186110
186213
|
return () => {
|
|
186111
186214
|
try {
|
|
186112
|
-
|
|
186215
|
+
fs9.unlinkSync(this.lockPath);
|
|
186113
186216
|
} catch {
|
|
186114
186217
|
}
|
|
186115
186218
|
};
|
|
@@ -186118,16 +186221,16 @@ var InstanceStateManager = class {
|
|
|
186118
186221
|
if (err.code === "EEXIST") {
|
|
186119
186222
|
try {
|
|
186120
186223
|
const lockPid = parseInt(
|
|
186121
|
-
|
|
186224
|
+
fs9.readFileSync(this.lockPath, "utf-8").trim(),
|
|
186122
186225
|
10
|
|
186123
186226
|
);
|
|
186124
186227
|
if (!this.isProcessRunning(lockPid)) {
|
|
186125
|
-
|
|
186228
|
+
fs9.unlinkSync(this.lockPath);
|
|
186126
186229
|
continue;
|
|
186127
186230
|
}
|
|
186128
186231
|
} catch {
|
|
186129
186232
|
try {
|
|
186130
|
-
|
|
186233
|
+
fs9.unlinkSync(this.lockPath);
|
|
186131
186234
|
} catch {
|
|
186132
186235
|
}
|
|
186133
186236
|
continue;
|
|
@@ -186141,12 +186244,12 @@ var InstanceStateManager = class {
|
|
|
186141
186244
|
throw new Error("Failed to acquire state lock (timeout)");
|
|
186142
186245
|
}
|
|
186143
186246
|
async getExistingInstances() {
|
|
186144
|
-
if (!
|
|
186247
|
+
if (!fs9.existsSync(this.statePath)) {
|
|
186145
186248
|
return null;
|
|
186146
186249
|
}
|
|
186147
186250
|
const releaseLock = await this.acquireLock();
|
|
186148
186251
|
try {
|
|
186149
|
-
const content =
|
|
186252
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186150
186253
|
const state = JSON.parse(content);
|
|
186151
186254
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
186152
186255
|
return null;
|
|
@@ -186159,21 +186262,21 @@ var InstanceStateManager = class {
|
|
|
186159
186262
|
}
|
|
186160
186263
|
}
|
|
186161
186264
|
async cleanStaleState() {
|
|
186162
|
-
if (!
|
|
186265
|
+
if (!fs9.existsSync(this.statePath)) {
|
|
186163
186266
|
return false;
|
|
186164
186267
|
}
|
|
186165
186268
|
const releaseLock = await this.acquireLock();
|
|
186166
186269
|
try {
|
|
186167
|
-
const content =
|
|
186270
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186168
186271
|
const state = JSON.parse(content);
|
|
186169
186272
|
if (!this.isProcessRunning(state.owner.pid)) {
|
|
186170
|
-
|
|
186273
|
+
fs9.unlinkSync(this.statePath);
|
|
186171
186274
|
return true;
|
|
186172
186275
|
}
|
|
186173
186276
|
return false;
|
|
186174
186277
|
} catch {
|
|
186175
186278
|
try {
|
|
186176
|
-
|
|
186279
|
+
fs9.unlinkSync(this.statePath);
|
|
186177
186280
|
return true;
|
|
186178
186281
|
} catch {
|
|
186179
186282
|
}
|
|
@@ -186185,8 +186288,8 @@ var InstanceStateManager = class {
|
|
|
186185
186288
|
async claimOwnership(command) {
|
|
186186
186289
|
const releaseLock = await this.acquireLock();
|
|
186187
186290
|
try {
|
|
186188
|
-
if (
|
|
186189
|
-
const content =
|
|
186291
|
+
if (fs9.existsSync(this.statePath)) {
|
|
186292
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186190
186293
|
const state2 = JSON.parse(content);
|
|
186191
186294
|
if (this.isProcessRunning(state2.owner.pid)) {
|
|
186192
186295
|
throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
|
|
@@ -186255,8 +186358,8 @@ var InstanceStateManager = class {
|
|
|
186255
186358
|
}
|
|
186256
186359
|
const releaseLock = await this.acquireLock();
|
|
186257
186360
|
try {
|
|
186258
|
-
if (
|
|
186259
|
-
|
|
186361
|
+
if (fs9.existsSync(this.statePath)) {
|
|
186362
|
+
fs9.unlinkSync(this.statePath);
|
|
186260
186363
|
}
|
|
186261
186364
|
this.ownsInstances = false;
|
|
186262
186365
|
} finally {
|
|
@@ -186264,26 +186367,26 @@ var InstanceStateManager = class {
|
|
|
186264
186367
|
}
|
|
186265
186368
|
}
|
|
186266
186369
|
readState() {
|
|
186267
|
-
const content =
|
|
186370
|
+
const content = fs9.readFileSync(this.statePath, "utf-8");
|
|
186268
186371
|
return JSON.parse(content);
|
|
186269
186372
|
}
|
|
186270
186373
|
writeStateAtomic(state) {
|
|
186271
186374
|
this.ensureStateDir();
|
|
186272
186375
|
const tmpPath = this.statePath + ".tmp";
|
|
186273
|
-
|
|
186274
|
-
|
|
186376
|
+
fs9.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
186377
|
+
fs9.renameSync(tmpPath, this.statePath);
|
|
186275
186378
|
}
|
|
186276
186379
|
};
|
|
186277
186380
|
|
|
186278
186381
|
// src/lib/dev/http-proxy.ts
|
|
186279
186382
|
import * as http from "http";
|
|
186280
186383
|
import * as https from "https";
|
|
186281
|
-
import * as
|
|
186282
|
-
import * as
|
|
186384
|
+
import * as fs10 from "fs";
|
|
186385
|
+
import * as path11 from "path";
|
|
186283
186386
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
186284
186387
|
import httpProxy from "http-proxy";
|
|
186285
|
-
var __dirname2 =
|
|
186286
|
-
var adminDir =
|
|
186388
|
+
var __dirname2 = path11.dirname(fileURLToPath2(import.meta.url));
|
|
186389
|
+
var adminDir = path11.join(__dirname2, "admin");
|
|
186287
186390
|
var HTTP_PORT = 80;
|
|
186288
186391
|
var HTTPS_PORT = 443;
|
|
186289
186392
|
var DOMAIN_SUFFIX = ".local.spcf.app";
|
|
@@ -186340,7 +186443,7 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
|
|
|
186340
186443
|
});
|
|
186341
186444
|
const handleRequest = (req, res) => {
|
|
186342
186445
|
const host = req.headers.host || "";
|
|
186343
|
-
const
|
|
186446
|
+
const hostname2 = host.split(":")[0];
|
|
186344
186447
|
const drizzleKey = extractDrizzleGatewayKey(host);
|
|
186345
186448
|
if (drizzleKey !== null) {
|
|
186346
186449
|
const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
|
|
@@ -186366,7 +186469,7 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
|
|
|
186366
186469
|
serveStaticFile(res, url.pathname);
|
|
186367
186470
|
return;
|
|
186368
186471
|
}
|
|
186369
|
-
if (adminKey === "default" &&
|
|
186472
|
+
if (adminKey === "default" && hostname2 === ADMIN_DOMAIN) {
|
|
186370
186473
|
res.writeHead(503, { "Content-Type": "text/html" });
|
|
186371
186474
|
res.end("<h1>Admin UI</h1><p>No dev instance running. Start <code>specific dev</code> first.</p>");
|
|
186372
186475
|
return;
|
|
@@ -186487,11 +186590,11 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
|
|
|
186487
186590
|
});
|
|
186488
186591
|
}
|
|
186489
186592
|
function extractServiceAndKey(host) {
|
|
186490
|
-
const
|
|
186491
|
-
if (!
|
|
186593
|
+
const hostname2 = host.split(":")[0];
|
|
186594
|
+
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186492
186595
|
return null;
|
|
186493
186596
|
}
|
|
186494
|
-
const prefix =
|
|
186597
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186495
186598
|
if (!prefix) {
|
|
186496
186599
|
return null;
|
|
186497
186600
|
}
|
|
@@ -186504,11 +186607,11 @@ function extractServiceAndKey(host) {
|
|
|
186504
186607
|
return null;
|
|
186505
186608
|
}
|
|
186506
186609
|
function extractDrizzleGatewayKey(host) {
|
|
186507
|
-
const
|
|
186508
|
-
if (!
|
|
186610
|
+
const hostname2 = host.split(":")[0];
|
|
186611
|
+
if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186509
186612
|
return null;
|
|
186510
186613
|
}
|
|
186511
|
-
const prefix =
|
|
186614
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186512
186615
|
if (!prefix) {
|
|
186513
186616
|
return null;
|
|
186514
186617
|
}
|
|
@@ -186521,17 +186624,17 @@ function extractDrizzleGatewayKey(host) {
|
|
|
186521
186624
|
return null;
|
|
186522
186625
|
}
|
|
186523
186626
|
function extractAdminKey(host) {
|
|
186524
|
-
const
|
|
186525
|
-
if (!
|
|
186627
|
+
const hostname2 = host.split(":")[0];
|
|
186628
|
+
if (!hostname2) {
|
|
186526
186629
|
return null;
|
|
186527
186630
|
}
|
|
186528
|
-
if (
|
|
186631
|
+
if (hostname2 === ADMIN_DOMAIN) {
|
|
186529
186632
|
return "default";
|
|
186530
186633
|
}
|
|
186531
|
-
if (!
|
|
186634
|
+
if (!hostname2.endsWith(DOMAIN_SUFFIX)) {
|
|
186532
186635
|
return null;
|
|
186533
186636
|
}
|
|
186534
|
-
const prefix =
|
|
186637
|
+
const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
|
|
186535
186638
|
if (!prefix) {
|
|
186536
186639
|
return null;
|
|
186537
186640
|
}
|
|
@@ -186569,25 +186672,25 @@ function serveStaticFile(res, pathname) {
|
|
|
186569
186672
|
filePath = filePath + "index.html";
|
|
186570
186673
|
}
|
|
186571
186674
|
const relativePath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
186572
|
-
const fullPath =
|
|
186573
|
-
const resolvedPath =
|
|
186574
|
-
const resolvedAdminDir =
|
|
186675
|
+
const fullPath = path11.join(adminDir, relativePath);
|
|
186676
|
+
const resolvedPath = path11.resolve(fullPath);
|
|
186677
|
+
const resolvedAdminDir = path11.resolve(adminDir);
|
|
186575
186678
|
if (!resolvedPath.startsWith(resolvedAdminDir)) {
|
|
186576
186679
|
res.writeHead(403, { "Content-Type": "text/html" });
|
|
186577
186680
|
res.end("<h1>Forbidden</h1>");
|
|
186578
186681
|
return;
|
|
186579
186682
|
}
|
|
186580
|
-
if (!
|
|
186683
|
+
if (!fs10.existsSync(resolvedPath)) {
|
|
186581
186684
|
const htmlPath = resolvedPath + ".html";
|
|
186582
|
-
if (
|
|
186685
|
+
if (fs10.existsSync(htmlPath)) {
|
|
186583
186686
|
return serveFile(res, htmlPath);
|
|
186584
186687
|
}
|
|
186585
|
-
const indexPath =
|
|
186586
|
-
if (
|
|
186688
|
+
const indexPath = path11.join(resolvedPath, "index.html");
|
|
186689
|
+
if (fs10.existsSync(indexPath)) {
|
|
186587
186690
|
return serveFile(res, indexPath);
|
|
186588
186691
|
}
|
|
186589
|
-
const notFoundPath =
|
|
186590
|
-
if (
|
|
186692
|
+
const notFoundPath = path11.join(adminDir, "404.html");
|
|
186693
|
+
if (fs10.existsSync(notFoundPath)) {
|
|
186591
186694
|
return serveFileContent(res, notFoundPath, "text/html", 404);
|
|
186592
186695
|
}
|
|
186593
186696
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
@@ -186597,13 +186700,13 @@ function serveStaticFile(res, pathname) {
|
|
|
186597
186700
|
serveFile(res, resolvedPath);
|
|
186598
186701
|
}
|
|
186599
186702
|
function serveFile(res, filePath) {
|
|
186600
|
-
const ext =
|
|
186703
|
+
const ext = path11.extname(filePath).toLowerCase();
|
|
186601
186704
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
186602
186705
|
serveFileContent(res, filePath, contentType, 200);
|
|
186603
186706
|
}
|
|
186604
186707
|
function serveFileContent(res, filePath, contentType, statusCode = 200) {
|
|
186605
186708
|
try {
|
|
186606
|
-
const content =
|
|
186709
|
+
const content = fs10.readFileSync(filePath);
|
|
186607
186710
|
res.writeHead(statusCode, { "Content-Type": contentType });
|
|
186608
186711
|
res.end(content);
|
|
186609
186712
|
} catch (err) {
|
|
@@ -186755,8 +186858,8 @@ function sleep2(ms) {
|
|
|
186755
186858
|
|
|
186756
186859
|
// src/lib/dev/drizzle-gateway-manager.ts
|
|
186757
186860
|
import * as net3 from "net";
|
|
186758
|
-
import * as
|
|
186759
|
-
import * as
|
|
186861
|
+
import * as fs11 from "fs";
|
|
186862
|
+
import * as path12 from "path";
|
|
186760
186863
|
import { spawn as spawn4 } from "child_process";
|
|
186761
186864
|
import { randomUUID } from "crypto";
|
|
186762
186865
|
function generateStoreJson(postgresInstances) {
|
|
@@ -186788,13 +186891,13 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
|
|
|
186788
186891
|
options2?.onProgress
|
|
186789
186892
|
);
|
|
186790
186893
|
const host = "127.0.0.1";
|
|
186791
|
-
const drizzleConfigDir =
|
|
186792
|
-
if (!
|
|
186793
|
-
|
|
186894
|
+
const drizzleConfigDir = path12.join(configDir, "drizzle-gateway");
|
|
186895
|
+
if (!fs11.existsSync(drizzleConfigDir)) {
|
|
186896
|
+
fs11.mkdirSync(drizzleConfigDir, { recursive: true });
|
|
186794
186897
|
}
|
|
186795
186898
|
const storeJson = generateStoreJson(postgresInstances);
|
|
186796
|
-
const storeJsonPath =
|
|
186797
|
-
|
|
186899
|
+
const storeJsonPath = path12.join(drizzleConfigDir, "store.json");
|
|
186900
|
+
fs11.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
|
|
186798
186901
|
writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
|
|
186799
186902
|
writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
|
|
186800
186903
|
writeLog("drizzle-gateway", `PORT: ${port}`);
|
|
@@ -187063,9 +187166,9 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
187063
187166
|
}
|
|
187064
187167
|
|
|
187065
187168
|
// src/lib/dev/proxy-registry.ts
|
|
187066
|
-
import * as
|
|
187067
|
-
import * as
|
|
187068
|
-
import * as
|
|
187169
|
+
import * as fs12 from "fs";
|
|
187170
|
+
import * as path13 from "path";
|
|
187171
|
+
import * as os5 from "os";
|
|
187069
187172
|
var ProxyRegistryManager = class {
|
|
187070
187173
|
proxyDir;
|
|
187071
187174
|
ownerPath;
|
|
@@ -187074,14 +187177,14 @@ var ProxyRegistryManager = class {
|
|
|
187074
187177
|
isOwner = false;
|
|
187075
187178
|
registryWatcher = null;
|
|
187076
187179
|
constructor() {
|
|
187077
|
-
this.proxyDir =
|
|
187078
|
-
this.ownerPath =
|
|
187079
|
-
this.registryPath =
|
|
187080
|
-
this.lockPath =
|
|
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");
|
|
187081
187184
|
}
|
|
187082
187185
|
ensureProxyDir() {
|
|
187083
|
-
if (!
|
|
187084
|
-
|
|
187186
|
+
if (!fs12.existsSync(this.proxyDir)) {
|
|
187187
|
+
fs12.mkdirSync(this.proxyDir, { recursive: true });
|
|
187085
187188
|
}
|
|
187086
187189
|
}
|
|
187087
187190
|
isProcessRunning(pid) {
|
|
@@ -187098,15 +187201,15 @@ var ProxyRegistryManager = class {
|
|
|
187098
187201
|
const startTime = Date.now();
|
|
187099
187202
|
while (Date.now() - startTime < timeoutMs) {
|
|
187100
187203
|
try {
|
|
187101
|
-
const fd =
|
|
187204
|
+
const fd = fs12.openSync(
|
|
187102
187205
|
this.lockPath,
|
|
187103
|
-
|
|
187206
|
+
fs12.constants.O_CREAT | fs12.constants.O_EXCL | fs12.constants.O_WRONLY
|
|
187104
187207
|
);
|
|
187105
|
-
|
|
187106
|
-
|
|
187208
|
+
fs12.writeSync(fd, String(process.pid));
|
|
187209
|
+
fs12.closeSync(fd);
|
|
187107
187210
|
return () => {
|
|
187108
187211
|
try {
|
|
187109
|
-
|
|
187212
|
+
fs12.unlinkSync(this.lockPath);
|
|
187110
187213
|
} catch {
|
|
187111
187214
|
}
|
|
187112
187215
|
};
|
|
@@ -187115,16 +187218,16 @@ var ProxyRegistryManager = class {
|
|
|
187115
187218
|
if (err.code === "EEXIST") {
|
|
187116
187219
|
try {
|
|
187117
187220
|
const lockPid = parseInt(
|
|
187118
|
-
|
|
187221
|
+
fs12.readFileSync(this.lockPath, "utf-8").trim(),
|
|
187119
187222
|
10
|
|
187120
187223
|
);
|
|
187121
187224
|
if (!this.isProcessRunning(lockPid)) {
|
|
187122
|
-
|
|
187225
|
+
fs12.unlinkSync(this.lockPath);
|
|
187123
187226
|
continue;
|
|
187124
187227
|
}
|
|
187125
187228
|
} catch {
|
|
187126
187229
|
try {
|
|
187127
|
-
|
|
187230
|
+
fs12.unlinkSync(this.lockPath);
|
|
187128
187231
|
} catch {
|
|
187129
187232
|
}
|
|
187130
187233
|
continue;
|
|
@@ -187144,8 +187247,8 @@ var ProxyRegistryManager = class {
|
|
|
187144
187247
|
async claimProxyOwnership(key) {
|
|
187145
187248
|
const releaseLock = await this.acquireLock();
|
|
187146
187249
|
try {
|
|
187147
|
-
if (
|
|
187148
|
-
const content =
|
|
187250
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187251
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187149
187252
|
const ownerFile2 = JSON.parse(content);
|
|
187150
187253
|
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187151
187254
|
return false;
|
|
@@ -187175,11 +187278,11 @@ var ProxyRegistryManager = class {
|
|
|
187175
187278
|
}
|
|
187176
187279
|
const releaseLock = await this.acquireLock();
|
|
187177
187280
|
try {
|
|
187178
|
-
if (
|
|
187179
|
-
const content =
|
|
187281
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187282
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187180
187283
|
const ownerFile = JSON.parse(content);
|
|
187181
187284
|
if (ownerFile.owner.pid === process.pid) {
|
|
187182
|
-
|
|
187285
|
+
fs12.unlinkSync(this.ownerPath);
|
|
187183
187286
|
}
|
|
187184
187287
|
}
|
|
187185
187288
|
this.isOwner = false;
|
|
@@ -187191,12 +187294,12 @@ var ProxyRegistryManager = class {
|
|
|
187191
187294
|
* Get the current proxy owner.
|
|
187192
187295
|
*/
|
|
187193
187296
|
async getProxyOwner() {
|
|
187194
|
-
if (!
|
|
187297
|
+
if (!fs12.existsSync(this.ownerPath)) {
|
|
187195
187298
|
return null;
|
|
187196
187299
|
}
|
|
187197
187300
|
const releaseLock = await this.acquireLock();
|
|
187198
187301
|
try {
|
|
187199
|
-
const content =
|
|
187302
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187200
187303
|
const ownerFile = JSON.parse(content);
|
|
187201
187304
|
if (!this.isProcessRunning(ownerFile.owner.pid)) {
|
|
187202
187305
|
return null;
|
|
@@ -187290,7 +187393,7 @@ var ProxyRegistryManager = class {
|
|
|
187290
187393
|
*/
|
|
187291
187394
|
watchRegistry(onChange) {
|
|
187292
187395
|
this.ensureProxyDir();
|
|
187293
|
-
if (!
|
|
187396
|
+
if (!fs12.existsSync(this.registryPath)) {
|
|
187294
187397
|
const emptyRegistry = {
|
|
187295
187398
|
version: 1,
|
|
187296
187399
|
keys: {},
|
|
@@ -187334,13 +187437,13 @@ var ProxyRegistryManager = class {
|
|
|
187334
187437
|
async attemptElection(key) {
|
|
187335
187438
|
const releaseLock = await this.acquireLock();
|
|
187336
187439
|
try {
|
|
187337
|
-
if (
|
|
187338
|
-
const content =
|
|
187440
|
+
if (fs12.existsSync(this.ownerPath)) {
|
|
187441
|
+
const content = fs12.readFileSync(this.ownerPath, "utf-8");
|
|
187339
187442
|
const ownerFile2 = JSON.parse(content);
|
|
187340
187443
|
if (this.isProcessRunning(ownerFile2.owner.pid)) {
|
|
187341
187444
|
return false;
|
|
187342
187445
|
}
|
|
187343
|
-
|
|
187446
|
+
fs12.unlinkSync(this.ownerPath);
|
|
187344
187447
|
}
|
|
187345
187448
|
const ownerFile = {
|
|
187346
187449
|
version: 1,
|
|
@@ -187358,7 +187461,7 @@ var ProxyRegistryManager = class {
|
|
|
187358
187461
|
}
|
|
187359
187462
|
}
|
|
187360
187463
|
readRegistry() {
|
|
187361
|
-
if (!
|
|
187464
|
+
if (!fs12.existsSync(this.registryPath)) {
|
|
187362
187465
|
return {
|
|
187363
187466
|
version: 1,
|
|
187364
187467
|
keys: {},
|
|
@@ -187366,7 +187469,7 @@ var ProxyRegistryManager = class {
|
|
|
187366
187469
|
};
|
|
187367
187470
|
}
|
|
187368
187471
|
try {
|
|
187369
|
-
const content =
|
|
187472
|
+
const content = fs12.readFileSync(this.registryPath, "utf-8");
|
|
187370
187473
|
return JSON.parse(content);
|
|
187371
187474
|
} catch {
|
|
187372
187475
|
return {
|
|
@@ -187379,8 +187482,8 @@ var ProxyRegistryManager = class {
|
|
|
187379
187482
|
writeFileAtomic(filePath, data) {
|
|
187380
187483
|
this.ensureProxyDir();
|
|
187381
187484
|
const tmpPath = filePath + ".tmp";
|
|
187382
|
-
|
|
187383
|
-
|
|
187485
|
+
fs12.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
187486
|
+
fs12.renameSync(tmpPath, filePath);
|
|
187384
187487
|
}
|
|
187385
187488
|
};
|
|
187386
187489
|
|
|
@@ -187423,6 +187526,7 @@ function DevUI({ instanceKey }) {
|
|
|
187423
187526
|
}
|
|
187424
187527
|
}
|
|
187425
187528
|
const shuttingDown = useRef(false);
|
|
187529
|
+
const startTimeRef = useRef(null);
|
|
187426
187530
|
const stateManagerRef = useRef(null);
|
|
187427
187531
|
const proxyRegistryRef = useRef(null);
|
|
187428
187532
|
const electricInstancesRef = useRef([]);
|
|
@@ -187435,9 +187539,11 @@ function DevUI({ instanceKey }) {
|
|
|
187435
187539
|
const resourcesRef = useRef(/* @__PURE__ */ new Map());
|
|
187436
187540
|
const [reloadTrigger, setReloadTrigger] = useState3(0);
|
|
187437
187541
|
const [readyToStart, setReadyToStart] = useState3(() => caFilesExist());
|
|
187438
|
-
const
|
|
187542
|
+
const shutdown2 = async () => {
|
|
187439
187543
|
if (shuttingDown.current) return;
|
|
187440
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 });
|
|
187441
187547
|
writeLog("system", "Shutting down");
|
|
187442
187548
|
setState((s) => ({ ...s, status: "stopping" }));
|
|
187443
187549
|
if (electionIntervalRef.current) {
|
|
@@ -187544,7 +187650,7 @@ function DevUI({ instanceKey }) {
|
|
|
187544
187650
|
}
|
|
187545
187651
|
process.exit(1);
|
|
187546
187652
|
}
|
|
187547
|
-
|
|
187653
|
+
shutdown2();
|
|
187548
187654
|
};
|
|
187549
187655
|
process.on("SIGINT", handleSignal);
|
|
187550
187656
|
process.on("SIGTERM", handleSignal);
|
|
@@ -187553,12 +187659,18 @@ function DevUI({ instanceKey }) {
|
|
|
187553
187659
|
process.off("SIGTERM", handleSignal);
|
|
187554
187660
|
};
|
|
187555
187661
|
}, []);
|
|
187662
|
+
useEffect3(() => {
|
|
187663
|
+
if (state.status === "running" && !startTimeRef.current) {
|
|
187664
|
+
startTimeRef.current = Date.now();
|
|
187665
|
+
trackEvent("dev_started");
|
|
187666
|
+
}
|
|
187667
|
+
}, [state.status]);
|
|
187556
187668
|
useEffect3(() => {
|
|
187557
187669
|
if (state.status !== "running") return;
|
|
187558
|
-
const configPath =
|
|
187670
|
+
const configPath = path14.join(process.cwd(), "specific.hcl");
|
|
187559
187671
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
187560
187672
|
try {
|
|
187561
|
-
const hcl =
|
|
187673
|
+
const hcl = fs13.readFileSync(configPath, "utf-8");
|
|
187562
187674
|
parseConfig(hcl).then(() => {
|
|
187563
187675
|
triggerReload();
|
|
187564
187676
|
}).catch((err) => {
|
|
@@ -187624,8 +187736,8 @@ function DevUI({ instanceKey }) {
|
|
|
187624
187736
|
}));
|
|
187625
187737
|
return;
|
|
187626
187738
|
}
|
|
187627
|
-
const configPath =
|
|
187628
|
-
if (!
|
|
187739
|
+
const configPath = path14.join(process.cwd(), "specific.hcl");
|
|
187740
|
+
if (!fs13.existsSync(configPath)) {
|
|
187629
187741
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
187630
187742
|
setState((s) => ({
|
|
187631
187743
|
...s,
|
|
@@ -187644,7 +187756,7 @@ function DevUI({ instanceKey }) {
|
|
|
187644
187756
|
}
|
|
187645
187757
|
let config2;
|
|
187646
187758
|
try {
|
|
187647
|
-
const hcl =
|
|
187759
|
+
const hcl = fs13.readFileSync(configPath, "utf-8");
|
|
187648
187760
|
config2 = await parseConfig(hcl);
|
|
187649
187761
|
} catch (err) {
|
|
187650
187762
|
setState((s) => ({
|
|
@@ -187750,7 +187862,7 @@ function DevUI({ instanceKey }) {
|
|
|
187750
187862
|
const drizzleGateway = await startDrizzleGateway(
|
|
187751
187863
|
postgresResources,
|
|
187752
187864
|
drizzlePort,
|
|
187753
|
-
|
|
187865
|
+
path14.join(process.cwd(), ".specific", "keys", instanceKey)
|
|
187754
187866
|
);
|
|
187755
187867
|
startedDrizzleGateway = drizzleGateway;
|
|
187756
187868
|
drizzleGatewayRef.current = drizzleGateway;
|
|
@@ -188211,7 +188323,7 @@ function devCommand(instanceKey) {
|
|
|
188211
188323
|
|
|
188212
188324
|
// src/lib/dev/git-worktree.ts
|
|
188213
188325
|
import { execSync as execSync2 } from "child_process";
|
|
188214
|
-
import * as
|
|
188326
|
+
import * as path15 from "path";
|
|
188215
188327
|
function isInWorktree() {
|
|
188216
188328
|
try {
|
|
188217
188329
|
const commonDir = execSync2("git rev-parse --git-common-dir", {
|
|
@@ -188222,8 +188334,8 @@ function isInWorktree() {
|
|
|
188222
188334
|
encoding: "utf-8",
|
|
188223
188335
|
stdio: ["pipe", "pipe", "pipe"]
|
|
188224
188336
|
}).trim();
|
|
188225
|
-
const resolvedCommonDir =
|
|
188226
|
-
const resolvedGitDir =
|
|
188337
|
+
const resolvedCommonDir = path15.resolve(commonDir);
|
|
188338
|
+
const resolvedGitDir = path15.resolve(gitDir);
|
|
188227
188339
|
return resolvedCommonDir !== resolvedGitDir;
|
|
188228
188340
|
} catch {
|
|
188229
188341
|
return false;
|
|
@@ -188238,7 +188350,7 @@ function getWorktreeName() {
|
|
|
188238
188350
|
encoding: "utf-8",
|
|
188239
188351
|
stdio: ["pipe", "pipe", "pipe"]
|
|
188240
188352
|
}).trim();
|
|
188241
|
-
return
|
|
188353
|
+
return path15.basename(gitDir);
|
|
188242
188354
|
} catch {
|
|
188243
188355
|
return null;
|
|
188244
188356
|
}
|
|
@@ -188255,30 +188367,30 @@ import Spinner5 from "ink-spinner";
|
|
|
188255
188367
|
|
|
188256
188368
|
// ../../node_modules/open/index.js
|
|
188257
188369
|
import process8 from "node:process";
|
|
188258
|
-
import
|
|
188370
|
+
import path16 from "node:path";
|
|
188259
188371
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
188260
188372
|
import childProcess3 from "node:child_process";
|
|
188261
|
-
import
|
|
188373
|
+
import fs18, { constants as fsConstants2 } from "node:fs/promises";
|
|
188262
188374
|
|
|
188263
188375
|
// ../../node_modules/wsl-utils/index.js
|
|
188264
188376
|
import { promisify as promisify2 } from "node:util";
|
|
188265
188377
|
import childProcess2 from "node:child_process";
|
|
188266
|
-
import
|
|
188378
|
+
import fs17, { constants as fsConstants } from "node:fs/promises";
|
|
188267
188379
|
|
|
188268
188380
|
// ../../node_modules/is-wsl/index.js
|
|
188269
188381
|
import process2 from "node:process";
|
|
188270
|
-
import
|
|
188271
|
-
import
|
|
188382
|
+
import os6 from "node:os";
|
|
188383
|
+
import fs16 from "node:fs";
|
|
188272
188384
|
|
|
188273
188385
|
// ../../node_modules/is-inside-container/index.js
|
|
188274
|
-
import
|
|
188386
|
+
import fs15 from "node:fs";
|
|
188275
188387
|
|
|
188276
188388
|
// ../../node_modules/is-docker/index.js
|
|
188277
|
-
import
|
|
188389
|
+
import fs14 from "node:fs";
|
|
188278
188390
|
var isDockerCached;
|
|
188279
188391
|
function hasDockerEnv() {
|
|
188280
188392
|
try {
|
|
188281
|
-
|
|
188393
|
+
fs14.statSync("/.dockerenv");
|
|
188282
188394
|
return true;
|
|
188283
188395
|
} catch {
|
|
188284
188396
|
return false;
|
|
@@ -188286,7 +188398,7 @@ function hasDockerEnv() {
|
|
|
188286
188398
|
}
|
|
188287
188399
|
function hasDockerCGroup() {
|
|
188288
188400
|
try {
|
|
188289
|
-
return
|
|
188401
|
+
return fs14.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
|
|
188290
188402
|
} catch {
|
|
188291
188403
|
return false;
|
|
188292
188404
|
}
|
|
@@ -188302,7 +188414,7 @@ function isDocker() {
|
|
|
188302
188414
|
var cachedResult;
|
|
188303
188415
|
var hasContainerEnv = () => {
|
|
188304
188416
|
try {
|
|
188305
|
-
|
|
188417
|
+
fs15.statSync("/run/.containerenv");
|
|
188306
188418
|
return true;
|
|
188307
188419
|
} catch {
|
|
188308
188420
|
return false;
|
|
@@ -188320,14 +188432,14 @@ var isWsl = () => {
|
|
|
188320
188432
|
if (process2.platform !== "linux") {
|
|
188321
188433
|
return false;
|
|
188322
188434
|
}
|
|
188323
|
-
if (
|
|
188435
|
+
if (os6.release().toLowerCase().includes("microsoft")) {
|
|
188324
188436
|
if (isInsideContainer()) {
|
|
188325
188437
|
return false;
|
|
188326
188438
|
}
|
|
188327
188439
|
return true;
|
|
188328
188440
|
}
|
|
188329
188441
|
try {
|
|
188330
|
-
return
|
|
188442
|
+
return fs16.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
|
|
188331
188443
|
} catch {
|
|
188332
188444
|
return false;
|
|
188333
188445
|
}
|
|
@@ -188395,14 +188507,14 @@ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
|
|
|
188395
188507
|
const configFilePath = "/etc/wsl.conf";
|
|
188396
188508
|
let isConfigFileExists = false;
|
|
188397
188509
|
try {
|
|
188398
|
-
await
|
|
188510
|
+
await fs17.access(configFilePath, fsConstants.F_OK);
|
|
188399
188511
|
isConfigFileExists = true;
|
|
188400
188512
|
} catch {
|
|
188401
188513
|
}
|
|
188402
188514
|
if (!isConfigFileExists) {
|
|
188403
188515
|
return defaultMountPoint;
|
|
188404
188516
|
}
|
|
188405
|
-
const configContent = await
|
|
188517
|
+
const configContent = await fs17.readFile(configFilePath, { encoding: "utf8" });
|
|
188406
188518
|
const parsedMountPoint = parseMountPointFromConfig(configContent);
|
|
188407
188519
|
if (parsedMountPoint === void 0) {
|
|
188408
188520
|
return defaultMountPoint;
|
|
@@ -188422,7 +188534,7 @@ var canAccessPowerShell = async () => {
|
|
|
188422
188534
|
canAccessPowerShellPromise ??= (async () => {
|
|
188423
188535
|
try {
|
|
188424
188536
|
const psPath = await powerShellPath2();
|
|
188425
|
-
await
|
|
188537
|
+
await fs17.access(psPath, fsConstants.X_OK);
|
|
188426
188538
|
return true;
|
|
188427
188539
|
} catch {
|
|
188428
188540
|
return false;
|
|
@@ -188586,8 +188698,8 @@ var is_in_ssh_default = isInSsh;
|
|
|
188586
188698
|
|
|
188587
188699
|
// ../../node_modules/open/index.js
|
|
188588
188700
|
var fallbackAttemptSymbol = Symbol("fallbackAttempt");
|
|
188589
|
-
var __dirname3 = import.meta.url ?
|
|
188590
|
-
var localXdgOpenPath =
|
|
188701
|
+
var __dirname3 = import.meta.url ? path16.dirname(fileURLToPath3(import.meta.url)) : "";
|
|
188702
|
+
var localXdgOpenPath = path16.join(__dirname3, "xdg-open");
|
|
188591
188703
|
var { platform: platform4, arch: arch2 } = process8;
|
|
188592
188704
|
var tryEachApp = async (apps2, opener) => {
|
|
188593
188705
|
if (apps2.length === 0) {
|
|
@@ -188735,7 +188847,7 @@ var baseOpen = async (options2) => {
|
|
|
188735
188847
|
const isBundled = !__dirname3 || __dirname3 === "/";
|
|
188736
188848
|
let exeLocalXdgOpen = false;
|
|
188737
188849
|
try {
|
|
188738
|
-
await
|
|
188850
|
+
await fs18.access(localXdgOpenPath, fsConstants2.X_OK);
|
|
188739
188851
|
exeLocalXdgOpen = true;
|
|
188740
188852
|
} catch {
|
|
188741
188853
|
}
|
|
@@ -188872,30 +188984,30 @@ import * as path19 from "path";
|
|
|
188872
188984
|
|
|
188873
188985
|
// src/lib/deploy/build-tester.ts
|
|
188874
188986
|
import { spawn as spawn5 } from "child_process";
|
|
188875
|
-
import { existsSync as
|
|
188876
|
-
import { join as
|
|
188987
|
+
import { existsSync as existsSync16 } from "fs";
|
|
188988
|
+
import { join as join17 } from "path";
|
|
188877
188989
|
function getDependencyInstallCommand(build, projectDir) {
|
|
188878
188990
|
switch (build.base) {
|
|
188879
188991
|
case "node":
|
|
188880
|
-
if (
|
|
188992
|
+
if (existsSync16(join17(projectDir, "pnpm-lock.yaml"))) {
|
|
188881
188993
|
return "pnpm install --frozen-lockfile";
|
|
188882
|
-
} else if (
|
|
188994
|
+
} else if (existsSync16(join17(projectDir, "yarn.lock"))) {
|
|
188883
188995
|
return "yarn install --frozen-lockfile";
|
|
188884
|
-
} else if (
|
|
188996
|
+
} else if (existsSync16(join17(projectDir, "package-lock.json"))) {
|
|
188885
188997
|
return "npm ci";
|
|
188886
188998
|
} else {
|
|
188887
188999
|
return "npm install";
|
|
188888
189000
|
}
|
|
188889
189001
|
case "python":
|
|
188890
|
-
if (
|
|
189002
|
+
if (existsSync16(join17(projectDir, "poetry.lock"))) {
|
|
188891
189003
|
return "poetry install --no-interaction";
|
|
188892
|
-
} else if (
|
|
189004
|
+
} else if (existsSync16(join17(projectDir, "Pipfile.lock"))) {
|
|
188893
189005
|
return "pipenv install --deploy";
|
|
188894
|
-
} else if (
|
|
189006
|
+
} else if (existsSync16(join17(projectDir, "Pipfile"))) {
|
|
188895
189007
|
return "pipenv install";
|
|
188896
|
-
} else if (
|
|
189008
|
+
} else if (existsSync16(join17(projectDir, "pyproject.toml"))) {
|
|
188897
189009
|
return "pip install .";
|
|
188898
|
-
} else if (
|
|
189010
|
+
} else if (existsSync16(join17(projectDir, "requirements.txt"))) {
|
|
188899
189011
|
return "pip install -r requirements.txt";
|
|
188900
189012
|
}
|
|
188901
189013
|
return null;
|
|
@@ -189287,12 +189399,40 @@ var ApiClient = class {
|
|
|
189287
189399
|
}
|
|
189288
189400
|
return response.json();
|
|
189289
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
|
+
}
|
|
189290
189430
|
};
|
|
189291
189431
|
|
|
189292
189432
|
// src/lib/tarball/create.ts
|
|
189293
189433
|
import { execSync as execSync3 } from "child_process";
|
|
189294
|
-
import * as
|
|
189295
|
-
import * as
|
|
189434
|
+
import * as fs19 from "fs";
|
|
189435
|
+
import * as path17 from "path";
|
|
189296
189436
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
189297
189437
|
function isInsideGitRepository(dir) {
|
|
189298
189438
|
try {
|
|
@@ -189338,10 +189478,10 @@ async function createGitArchive(projectDir) {
|
|
|
189338
189478
|
var EXCLUDED_DIRS = [".specific"];
|
|
189339
189479
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
189340
189480
|
const results = [];
|
|
189341
|
-
const entries = await
|
|
189481
|
+
const entries = await fs19.promises.readdir(currentDir, { withFileTypes: true });
|
|
189342
189482
|
for (const entry of entries) {
|
|
189343
|
-
const fullPath =
|
|
189344
|
-
const relativePath =
|
|
189483
|
+
const fullPath = path17.join(currentDir, entry.name);
|
|
189484
|
+
const relativePath = path17.relative(baseDir, fullPath);
|
|
189345
189485
|
if (entry.isDirectory()) {
|
|
189346
189486
|
if (!exclude.includes(entry.name)) {
|
|
189347
189487
|
results.push(relativePath);
|
|
@@ -189356,8 +189496,8 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
189356
189496
|
}
|
|
189357
189497
|
async function createTarArchive(projectDir) {
|
|
189358
189498
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
189359
|
-
const configPath =
|
|
189360
|
-
if (!
|
|
189499
|
+
const configPath = path17.join(projectDir, "specific.hcl");
|
|
189500
|
+
if (!fs19.existsSync(configPath)) {
|
|
189361
189501
|
throw new Error("specific.hcl not found in project directory");
|
|
189362
189502
|
}
|
|
189363
189503
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -189379,49 +189519,10 @@ async function createProjectTarball(projectDir) {
|
|
|
189379
189519
|
return createTarArchive(projectDir);
|
|
189380
189520
|
}
|
|
189381
189521
|
|
|
189382
|
-
// src/lib/project/config.ts
|
|
189383
|
-
import * as fs19 from "fs";
|
|
189384
|
-
import * as path17 from "path";
|
|
189385
|
-
var PROJECT_ID_FILE = ".specific/project_id";
|
|
189386
|
-
var ProjectNotLinkedError = class extends Error {
|
|
189387
|
-
constructor() {
|
|
189388
|
-
super(
|
|
189389
|
-
`Project not linked to Specific.
|
|
189390
|
-
|
|
189391
|
-
The file ${PROJECT_ID_FILE} does not exist.
|
|
189392
|
-
|
|
189393
|
-
Run: specific deploy`
|
|
189394
|
-
);
|
|
189395
|
-
this.name = "ProjectNotLinkedError";
|
|
189396
|
-
}
|
|
189397
|
-
};
|
|
189398
|
-
function readProjectId(projectDir = process.cwd()) {
|
|
189399
|
-
const projectIdPath = path17.join(projectDir, PROJECT_ID_FILE);
|
|
189400
|
-
if (!fs19.existsSync(projectIdPath)) {
|
|
189401
|
-
throw new ProjectNotLinkedError();
|
|
189402
|
-
}
|
|
189403
|
-
const projectId = fs19.readFileSync(projectIdPath, "utf-8").trim();
|
|
189404
|
-
if (!projectId) {
|
|
189405
|
-
throw new Error(`${PROJECT_ID_FILE} is empty`);
|
|
189406
|
-
}
|
|
189407
|
-
return projectId;
|
|
189408
|
-
}
|
|
189409
|
-
function hasProjectId(projectDir = process.cwd()) {
|
|
189410
|
-
const projectIdPath = path17.join(projectDir, PROJECT_ID_FILE);
|
|
189411
|
-
return fs19.existsSync(projectIdPath);
|
|
189412
|
-
}
|
|
189413
|
-
function writeProjectId(projectId, projectDir = process.cwd()) {
|
|
189414
|
-
const specificDir = path17.join(projectDir, ".specific");
|
|
189415
|
-
if (!fs19.existsSync(specificDir)) {
|
|
189416
|
-
fs19.mkdirSync(specificDir, { recursive: true });
|
|
189417
|
-
}
|
|
189418
|
-
fs19.writeFileSync(path17.join(specificDir, "project_id"), projectId + "\n");
|
|
189419
|
-
}
|
|
189420
|
-
|
|
189421
189522
|
// src/lib/auth/credentials.ts
|
|
189422
189523
|
import * as fs20 from "fs";
|
|
189423
189524
|
import * as path18 from "path";
|
|
189424
|
-
import * as
|
|
189525
|
+
import * as os7 from "os";
|
|
189425
189526
|
|
|
189426
189527
|
// src/lib/auth/errors.ts
|
|
189427
189528
|
var RefreshTokenExpiredError = class extends Error {
|
|
@@ -189560,10 +189661,14 @@ function LoginFlow({ isReauthentication, onComplete }) {
|
|
|
189560
189661
|
}
|
|
189561
189662
|
} else {
|
|
189562
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();
|
|
189563
189667
|
writeUserCredentials({
|
|
189564
189668
|
accessToken: successResponse.access_token,
|
|
189565
189669
|
refreshToken: successResponse.refresh_token,
|
|
189566
|
-
expiresAt:
|
|
189670
|
+
expiresAt: expiresAt2,
|
|
189671
|
+
userId: user.id
|
|
189567
189672
|
});
|
|
189568
189673
|
setUserEmail(successResponse.user.email);
|
|
189569
189674
|
setPhase("success");
|
|
@@ -189612,7 +189717,7 @@ function LoginFlow({ isReauthentication, onComplete }) {
|
|
|
189612
189717
|
|
|
189613
189718
|
// src/lib/auth/credentials.ts
|
|
189614
189719
|
function getUserCredentialsDir() {
|
|
189615
|
-
return path18.join(
|
|
189720
|
+
return path18.join(os7.homedir(), ".specific");
|
|
189616
189721
|
}
|
|
189617
189722
|
function getCredentialsPath() {
|
|
189618
189723
|
return path18.join(getUserCredentialsDir(), "credentials.json");
|
|
@@ -189899,8 +190004,8 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189899
190004
|
async function loadProjects() {
|
|
189900
190005
|
try {
|
|
189901
190006
|
const token = await getValidAccessToken();
|
|
189902
|
-
const
|
|
189903
|
-
const { projects: projects2 } = await
|
|
190007
|
+
const client2 = new ApiClient(token);
|
|
190008
|
+
const { projects: projects2 } = await client2.listProjects();
|
|
189904
190009
|
if (cancelled) return;
|
|
189905
190010
|
setState({
|
|
189906
190011
|
phase: "selecting-project",
|
|
@@ -189946,8 +190051,8 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
189946
190051
|
async function createProject() {
|
|
189947
190052
|
try {
|
|
189948
190053
|
const token = await getValidAccessToken();
|
|
189949
|
-
const
|
|
189950
|
-
const project = await
|
|
190054
|
+
const client2 = new ApiClient(token);
|
|
190055
|
+
const project = await client2.createProject(state.newProjectName);
|
|
189951
190056
|
if (cancelled) return;
|
|
189952
190057
|
writeProjectId(project.id);
|
|
189953
190058
|
setState({
|
|
@@ -190040,15 +190145,15 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
190040
190145
|
(secret) => secretValues[secret]
|
|
190041
190146
|
);
|
|
190042
190147
|
if (!allSecretsCollected) return;
|
|
190043
|
-
const
|
|
190044
|
-
if (!
|
|
190148
|
+
const client2 = clientRef.current;
|
|
190149
|
+
if (!client2) return;
|
|
190045
190150
|
(async () => {
|
|
190046
190151
|
try {
|
|
190047
190152
|
writeLog(
|
|
190048
190153
|
"deploy",
|
|
190049
190154
|
`Submitting secrets: ${Object.keys(secretValues).join(", ")}`
|
|
190050
190155
|
);
|
|
190051
|
-
await
|
|
190156
|
+
await client2.submitSecrets(deployment2.id, secretValues);
|
|
190052
190157
|
writeLog("deploy", "Secrets submitted successfully");
|
|
190053
190158
|
setState((s) => ({
|
|
190054
190159
|
...s,
|
|
@@ -190082,15 +190187,15 @@ function DeployUI({ environment, config, skipBuildTest }) {
|
|
|
190082
190187
|
(config2) => configValues[config2]
|
|
190083
190188
|
);
|
|
190084
190189
|
if (!allConfigsCollected) return;
|
|
190085
|
-
const
|
|
190086
|
-
if (!
|
|
190190
|
+
const client2 = clientRef.current;
|
|
190191
|
+
if (!client2) return;
|
|
190087
190192
|
(async () => {
|
|
190088
190193
|
try {
|
|
190089
190194
|
writeLog(
|
|
190090
190195
|
"deploy",
|
|
190091
190196
|
`Submitting configs: ${Object.keys(configValues).join(", ")}`
|
|
190092
190197
|
);
|
|
190093
|
-
await
|
|
190198
|
+
await client2.submitConfigs(deployment2.id, configValues);
|
|
190094
190199
|
writeLog("deploy", "Configs submitted successfully");
|
|
190095
190200
|
setState((s) => ({
|
|
190096
190201
|
...s,
|
|
@@ -190162,8 +190267,8 @@ ${errorMsg}`
|
|
|
190162
190267
|
writeLog("deploy", `Starting deployment to "${environment}"`);
|
|
190163
190268
|
writeLog("deploy", `Project directory: ${projectDir}`);
|
|
190164
190269
|
const authToken = await getValidAccessToken();
|
|
190165
|
-
const
|
|
190166
|
-
clientRef.current =
|
|
190270
|
+
const client2 = new ApiClient(authToken);
|
|
190271
|
+
clientRef.current = client2;
|
|
190167
190272
|
if (cancelled) return;
|
|
190168
190273
|
let tarball;
|
|
190169
190274
|
try {
|
|
@@ -190188,7 +190293,7 @@ ${errorMsg}`
|
|
|
190188
190293
|
let deployment2;
|
|
190189
190294
|
try {
|
|
190190
190295
|
writeLog("deploy", `Creating deployment for project ${state.projectId}`);
|
|
190191
|
-
deployment2 = await
|
|
190296
|
+
deployment2 = await client2.createDeployment(state.projectId, environment);
|
|
190192
190297
|
writeLog("deploy", `Deployment created: ${deployment2.id}`);
|
|
190193
190298
|
} catch (err) {
|
|
190194
190299
|
const errorMsg = `Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -190203,7 +190308,7 @@ ${errorMsg}`
|
|
|
190203
190308
|
setState((s) => ({ ...s, phase: "uploading", deployment: deployment2 }));
|
|
190204
190309
|
try {
|
|
190205
190310
|
writeLog("deploy", `Uploading tarball (${tarball.length} bytes)`);
|
|
190206
|
-
deployment2 = await
|
|
190311
|
+
deployment2 = await client2.uploadTarball(deployment2.id, tarball);
|
|
190207
190312
|
writeLog("deploy", "Tarball uploaded successfully");
|
|
190208
190313
|
} catch (err) {
|
|
190209
190314
|
const errorMsg = `Failed to upload tarball: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -190222,7 +190327,7 @@ ${errorMsg}`
|
|
|
190222
190327
|
let lastState;
|
|
190223
190328
|
const pollForCompletion = async () => {
|
|
190224
190329
|
try {
|
|
190225
|
-
const status = await
|
|
190330
|
+
const status = await client2.getDeployment(deployment2.id);
|
|
190226
190331
|
if (cancelled) return;
|
|
190227
190332
|
if (status.state !== lastState) {
|
|
190228
190333
|
writeLog(
|
|
@@ -190308,11 +190413,11 @@ ${errorMsg}`
|
|
|
190308
190413
|
useEffect5(() => {
|
|
190309
190414
|
let pollInterval;
|
|
190310
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) {
|
|
190311
|
-
const
|
|
190312
|
-
if (!
|
|
190416
|
+
const client2 = clientRef.current;
|
|
190417
|
+
if (!client2) return;
|
|
190313
190418
|
const pollForCompletion = async () => {
|
|
190314
190419
|
try {
|
|
190315
|
-
const status = await
|
|
190420
|
+
const status = await client2.getDeployment(state.deployment.id);
|
|
190316
190421
|
if (status.state === "failed") {
|
|
190317
190422
|
setState((s) => ({
|
|
190318
190423
|
...s,
|
|
@@ -190344,12 +190449,24 @@ ${errorMsg}`
|
|
|
190344
190449
|
}
|
|
190345
190450
|
}, [state.phase, state.missingSecrets, state.secretValues, state.missingConfigs, state.configValues]);
|
|
190346
190451
|
useEffect5(() => {
|
|
190347
|
-
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 });
|
|
190348
190459
|
closeDebugLog();
|
|
190349
190460
|
const timer = setTimeout(() => exit(), 100);
|
|
190350
190461
|
return () => clearTimeout(timer);
|
|
190351
190462
|
}
|
|
190352
|
-
|
|
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]);
|
|
190353
190470
|
const {
|
|
190354
190471
|
phase,
|
|
190355
190472
|
deployment,
|
|
@@ -191174,7 +191291,7 @@ function logoutCommand() {
|
|
|
191174
191291
|
var program = new Command();
|
|
191175
191292
|
var env = "production";
|
|
191176
191293
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
191177
|
-
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();
|
|
191178
191295
|
program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
|
|
191179
191296
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
191180
191297
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
|
@@ -191201,6 +191318,11 @@ program.command("secrets [action] [name]").description("Manage secrets").action(
|
|
|
191201
191318
|
program.command("config [action] [name] [value]").description("Manage configuration values").action(configCommand);
|
|
191202
191319
|
program.command("login").description("Log in to Specific").action(loginCommand);
|
|
191203
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
|
+
});
|
|
191204
191326
|
program.parse();
|
|
191205
191327
|
/*! Bundled license information:
|
|
191206
191328
|
|