@specific.dev/cli 0.1.47 → 0.1.48
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 +586 -400
- package/package.json +5 -2
- /package/dist/admin/_next/static/{pcYHo7d7--ealoH3_ELSO → dyH4SZNKyN31L1iV-yPZA}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{pcYHo7d7--ealoH3_ELSO → dyH4SZNKyN31L1iV-yPZA}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{pcYHo7d7--ealoH3_ELSO → dyH4SZNKyN31L1iV-yPZA}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -238,15 +238,15 @@ var init_wsl_utils = __esm({
|
|
|
238
238
|
const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
|
|
239
239
|
return stdout.trim();
|
|
240
240
|
};
|
|
241
|
-
convertWslPathToWindows = async (
|
|
242
|
-
if (/^[a-z]+:\/\//i.test(
|
|
243
|
-
return
|
|
241
|
+
convertWslPathToWindows = async (path23) => {
|
|
242
|
+
if (/^[a-z]+:\/\//i.test(path23)) {
|
|
243
|
+
return path23;
|
|
244
244
|
}
|
|
245
245
|
try {
|
|
246
|
-
const { stdout } = await execFile2("wslpath", ["-aw",
|
|
246
|
+
const { stdout } = await execFile2("wslpath", ["-aw", path23], { encoding: "utf8" });
|
|
247
247
|
return stdout.trim();
|
|
248
248
|
} catch {
|
|
249
|
-
return
|
|
249
|
+
return path23;
|
|
250
250
|
}
|
|
251
251
|
};
|
|
252
252
|
}
|
|
@@ -754,8 +754,8 @@ var require_dist = __commonJS({
|
|
|
754
754
|
var $global, $module, $NaN = NaN;
|
|
755
755
|
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");
|
|
756
756
|
if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
|
|
757
|
-
var
|
|
758
|
-
"object" == typeof
|
|
757
|
+
var fs25 = $global.require("fs");
|
|
758
|
+
"object" == typeof fs25 && null !== fs25 && 0 !== Object.keys(fs25).length && ($global.fs = fs25);
|
|
759
759
|
} catch (e) {
|
|
760
760
|
}
|
|
761
761
|
if (!$global.fs) {
|
|
@@ -183873,7 +183873,7 @@ function trackEvent(event, properties) {
|
|
|
183873
183873
|
event,
|
|
183874
183874
|
properties: {
|
|
183875
183875
|
...properties,
|
|
183876
|
-
cli_version: "0.1.
|
|
183876
|
+
cli_version: "0.1.48",
|
|
183877
183877
|
platform: process.platform,
|
|
183878
183878
|
node_version: process.version,
|
|
183879
183879
|
project_id: getProjectId(),
|
|
@@ -184166,11 +184166,11 @@ import { join as join6, dirname as dirname2 } from "path";
|
|
|
184166
184166
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
184167
184167
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
184168
184168
|
var docsDir = join6(__dirname2, "docs");
|
|
184169
|
-
function docsCommand(
|
|
184170
|
-
const docPath = resolveDocPath(
|
|
184169
|
+
function docsCommand(path23) {
|
|
184170
|
+
const docPath = resolveDocPath(path23);
|
|
184171
184171
|
if (!docPath) {
|
|
184172
184172
|
console.error(
|
|
184173
|
-
`Documentation not found: ${
|
|
184173
|
+
`Documentation not found: ${path23 || "index"}
|
|
184174
184174
|
|
|
184175
184175
|
Run 'specific docs' to see available topics.`
|
|
184176
184176
|
);
|
|
@@ -184179,16 +184179,16 @@ Run 'specific docs' to see available topics.`
|
|
|
184179
184179
|
const content = readFileSync5(docPath, "utf-8");
|
|
184180
184180
|
console.log(content);
|
|
184181
184181
|
}
|
|
184182
|
-
function resolveDocPath(
|
|
184183
|
-
if (!
|
|
184182
|
+
function resolveDocPath(path23) {
|
|
184183
|
+
if (!path23) {
|
|
184184
184184
|
const indexPath2 = join6(docsDir, "index.md");
|
|
184185
184185
|
return existsSync5(indexPath2) ? indexPath2 : null;
|
|
184186
184186
|
}
|
|
184187
|
-
const directPath = join6(docsDir, `${
|
|
184187
|
+
const directPath = join6(docsDir, `${path23}.md`);
|
|
184188
184188
|
if (existsSync5(directPath)) {
|
|
184189
184189
|
return directPath;
|
|
184190
184190
|
}
|
|
184191
|
-
const indexPath = join6(docsDir,
|
|
184191
|
+
const indexPath = join6(docsDir, path23, "index.md");
|
|
184192
184192
|
if (existsSync5(indexPath)) {
|
|
184193
184193
|
return indexPath;
|
|
184194
184194
|
}
|
|
@@ -184767,8 +184767,8 @@ function checkCommand() {
|
|
|
184767
184767
|
import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
|
|
184768
184768
|
import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
|
|
184769
184769
|
import Spinner4 from "ink-spinner";
|
|
184770
|
-
import * as
|
|
184771
|
-
import * as
|
|
184770
|
+
import * as fs20 from "fs";
|
|
184771
|
+
import * as path17 from "path";
|
|
184772
184772
|
|
|
184773
184773
|
// node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
|
|
184774
184774
|
import { EventEmitter } from "node:events";
|
|
@@ -184860,7 +184860,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
184860
184860
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
184861
184861
|
const statMethod = opts.lstat ? lstat : stat;
|
|
184862
184862
|
if (wantBigintFsStats) {
|
|
184863
|
-
this._stat = (
|
|
184863
|
+
this._stat = (path23) => statMethod(path23, { bigint: true });
|
|
184864
184864
|
} else {
|
|
184865
184865
|
this._stat = statMethod;
|
|
184866
184866
|
}
|
|
@@ -184885,8 +184885,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
184885
184885
|
const par = this.parent;
|
|
184886
184886
|
const fil = par && par.files;
|
|
184887
184887
|
if (fil && fil.length > 0) {
|
|
184888
|
-
const { path:
|
|
184889
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
184888
|
+
const { path: path23, depth } = par;
|
|
184889
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path23));
|
|
184890
184890
|
const awaited = await Promise.all(slice);
|
|
184891
184891
|
for (const entry of awaited) {
|
|
184892
184892
|
if (!entry)
|
|
@@ -184926,20 +184926,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
184926
184926
|
this.reading = false;
|
|
184927
184927
|
}
|
|
184928
184928
|
}
|
|
184929
|
-
async _exploreDir(
|
|
184929
|
+
async _exploreDir(path23, depth) {
|
|
184930
184930
|
let files;
|
|
184931
184931
|
try {
|
|
184932
|
-
files = await readdir(
|
|
184932
|
+
files = await readdir(path23, this._rdOptions);
|
|
184933
184933
|
} catch (error) {
|
|
184934
184934
|
this._onError(error);
|
|
184935
184935
|
}
|
|
184936
|
-
return { files, depth, path:
|
|
184936
|
+
return { files, depth, path: path23 };
|
|
184937
184937
|
}
|
|
184938
|
-
async _formatEntry(dirent,
|
|
184938
|
+
async _formatEntry(dirent, path23) {
|
|
184939
184939
|
let entry;
|
|
184940
184940
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
184941
184941
|
try {
|
|
184942
|
-
const fullPath = presolve(pjoin(
|
|
184942
|
+
const fullPath = presolve(pjoin(path23, basename5));
|
|
184943
184943
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
184944
184944
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
184945
184945
|
} catch (err) {
|
|
@@ -185339,16 +185339,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
185339
185339
|
};
|
|
185340
185340
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
185341
185341
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
185342
|
-
function createFsWatchInstance(
|
|
185342
|
+
function createFsWatchInstance(path23, options2, listener, errHandler, emitRaw) {
|
|
185343
185343
|
const handleEvent = (rawEvent, evPath) => {
|
|
185344
|
-
listener(
|
|
185345
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
185346
|
-
if (evPath &&
|
|
185347
|
-
fsWatchBroadcast(sp.resolve(
|
|
185344
|
+
listener(path23);
|
|
185345
|
+
emitRaw(rawEvent, evPath, { watchedPath: path23 });
|
|
185346
|
+
if (evPath && path23 !== evPath) {
|
|
185347
|
+
fsWatchBroadcast(sp.resolve(path23, evPath), KEY_LISTENERS, sp.join(path23, evPath));
|
|
185348
185348
|
}
|
|
185349
185349
|
};
|
|
185350
185350
|
try {
|
|
185351
|
-
return fs_watch(
|
|
185351
|
+
return fs_watch(path23, {
|
|
185352
185352
|
persistent: options2.persistent
|
|
185353
185353
|
}, handleEvent);
|
|
185354
185354
|
} catch (error) {
|
|
@@ -185364,12 +185364,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
185364
185364
|
listener(val1, val2, val3);
|
|
185365
185365
|
});
|
|
185366
185366
|
};
|
|
185367
|
-
var setFsWatchListener = (
|
|
185367
|
+
var setFsWatchListener = (path23, fullPath, options2, handlers) => {
|
|
185368
185368
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
185369
185369
|
let cont = FsWatchInstances.get(fullPath);
|
|
185370
185370
|
let watcher;
|
|
185371
185371
|
if (!options2.persistent) {
|
|
185372
|
-
watcher = createFsWatchInstance(
|
|
185372
|
+
watcher = createFsWatchInstance(path23, options2, listener, errHandler, rawEmitter);
|
|
185373
185373
|
if (!watcher)
|
|
185374
185374
|
return;
|
|
185375
185375
|
return watcher.close.bind(watcher);
|
|
@@ -185380,7 +185380,7 @@ var setFsWatchListener = (path22, fullPath, options2, handlers) => {
|
|
|
185380
185380
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
185381
185381
|
} else {
|
|
185382
185382
|
watcher = createFsWatchInstance(
|
|
185383
|
-
|
|
185383
|
+
path23,
|
|
185384
185384
|
options2,
|
|
185385
185385
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
185386
185386
|
errHandler,
|
|
@@ -185395,7 +185395,7 @@ var setFsWatchListener = (path22, fullPath, options2, handlers) => {
|
|
|
185395
185395
|
cont.watcherUnusable = true;
|
|
185396
185396
|
if (isWindows && error.code === "EPERM") {
|
|
185397
185397
|
try {
|
|
185398
|
-
const fd = await open2(
|
|
185398
|
+
const fd = await open2(path23, "r");
|
|
185399
185399
|
await fd.close();
|
|
185400
185400
|
broadcastErr(error);
|
|
185401
185401
|
} catch (err) {
|
|
@@ -185426,7 +185426,7 @@ var setFsWatchListener = (path22, fullPath, options2, handlers) => {
|
|
|
185426
185426
|
};
|
|
185427
185427
|
};
|
|
185428
185428
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
185429
|
-
var setFsWatchFileListener = (
|
|
185429
|
+
var setFsWatchFileListener = (path23, fullPath, options2, handlers) => {
|
|
185430
185430
|
const { listener, rawEmitter } = handlers;
|
|
185431
185431
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
185432
185432
|
const copts = cont && cont.options;
|
|
@@ -185448,7 +185448,7 @@ var setFsWatchFileListener = (path22, fullPath, options2, handlers) => {
|
|
|
185448
185448
|
});
|
|
185449
185449
|
const currmtime = curr.mtimeMs;
|
|
185450
185450
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
185451
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
185451
|
+
foreach(cont.listeners, (listener2) => listener2(path23, curr));
|
|
185452
185452
|
}
|
|
185453
185453
|
})
|
|
185454
185454
|
};
|
|
@@ -185478,13 +185478,13 @@ var NodeFsHandler = class {
|
|
|
185478
185478
|
* @param listener on fs change
|
|
185479
185479
|
* @returns closer for the watcher instance
|
|
185480
185480
|
*/
|
|
185481
|
-
_watchWithNodeFs(
|
|
185481
|
+
_watchWithNodeFs(path23, listener) {
|
|
185482
185482
|
const opts = this.fsw.options;
|
|
185483
|
-
const directory = sp.dirname(
|
|
185484
|
-
const basename5 = sp.basename(
|
|
185483
|
+
const directory = sp.dirname(path23);
|
|
185484
|
+
const basename5 = sp.basename(path23);
|
|
185485
185485
|
const parent = this.fsw._getWatchedDir(directory);
|
|
185486
185486
|
parent.add(basename5);
|
|
185487
|
-
const absolutePath = sp.resolve(
|
|
185487
|
+
const absolutePath = sp.resolve(path23);
|
|
185488
185488
|
const options2 = {
|
|
185489
185489
|
persistent: opts.persistent
|
|
185490
185490
|
};
|
|
@@ -185494,12 +185494,12 @@ var NodeFsHandler = class {
|
|
|
185494
185494
|
if (opts.usePolling) {
|
|
185495
185495
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
185496
185496
|
options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
185497
|
-
closer = setFsWatchFileListener(
|
|
185497
|
+
closer = setFsWatchFileListener(path23, absolutePath, options2, {
|
|
185498
185498
|
listener,
|
|
185499
185499
|
rawEmitter: this.fsw._emitRaw
|
|
185500
185500
|
});
|
|
185501
185501
|
} else {
|
|
185502
|
-
closer = setFsWatchListener(
|
|
185502
|
+
closer = setFsWatchListener(path23, absolutePath, options2, {
|
|
185503
185503
|
listener,
|
|
185504
185504
|
errHandler: this._boundHandleError,
|
|
185505
185505
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -185521,7 +185521,7 @@ var NodeFsHandler = class {
|
|
|
185521
185521
|
let prevStats = stats;
|
|
185522
185522
|
if (parent.has(basename5))
|
|
185523
185523
|
return;
|
|
185524
|
-
const listener = async (
|
|
185524
|
+
const listener = async (path23, newStats) => {
|
|
185525
185525
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
185526
185526
|
return;
|
|
185527
185527
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -185535,11 +185535,11 @@ var NodeFsHandler = class {
|
|
|
185535
185535
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
185536
185536
|
}
|
|
185537
185537
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
185538
|
-
this.fsw._closeFile(
|
|
185538
|
+
this.fsw._closeFile(path23);
|
|
185539
185539
|
prevStats = newStats2;
|
|
185540
185540
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
185541
185541
|
if (closer2)
|
|
185542
|
-
this.fsw._addPathCloser(
|
|
185542
|
+
this.fsw._addPathCloser(path23, closer2);
|
|
185543
185543
|
} else {
|
|
185544
185544
|
prevStats = newStats2;
|
|
185545
185545
|
}
|
|
@@ -185571,7 +185571,7 @@ var NodeFsHandler = class {
|
|
|
185571
185571
|
* @param item basename of this item
|
|
185572
185572
|
* @returns true if no more processing is needed for this entry.
|
|
185573
185573
|
*/
|
|
185574
|
-
async _handleSymlink(entry, directory,
|
|
185574
|
+
async _handleSymlink(entry, directory, path23, item) {
|
|
185575
185575
|
if (this.fsw.closed) {
|
|
185576
185576
|
return;
|
|
185577
185577
|
}
|
|
@@ -185581,7 +185581,7 @@ var NodeFsHandler = class {
|
|
|
185581
185581
|
this.fsw._incrReadyCount();
|
|
185582
185582
|
let linkPath;
|
|
185583
185583
|
try {
|
|
185584
|
-
linkPath = await fsrealpath(
|
|
185584
|
+
linkPath = await fsrealpath(path23);
|
|
185585
185585
|
} catch (e) {
|
|
185586
185586
|
this.fsw._emitReady();
|
|
185587
185587
|
return true;
|
|
@@ -185591,12 +185591,12 @@ var NodeFsHandler = class {
|
|
|
185591
185591
|
if (dir.has(item)) {
|
|
185592
185592
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
185593
185593
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
185594
|
-
this.fsw._emit(EV.CHANGE,
|
|
185594
|
+
this.fsw._emit(EV.CHANGE, path23, entry.stats);
|
|
185595
185595
|
}
|
|
185596
185596
|
} else {
|
|
185597
185597
|
dir.add(item);
|
|
185598
185598
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
185599
|
-
this.fsw._emit(EV.ADD,
|
|
185599
|
+
this.fsw._emit(EV.ADD, path23, entry.stats);
|
|
185600
185600
|
}
|
|
185601
185601
|
this.fsw._emitReady();
|
|
185602
185602
|
return true;
|
|
@@ -185626,9 +185626,9 @@ var NodeFsHandler = class {
|
|
|
185626
185626
|
return;
|
|
185627
185627
|
}
|
|
185628
185628
|
const item = entry.path;
|
|
185629
|
-
let
|
|
185629
|
+
let path23 = sp.join(directory, item);
|
|
185630
185630
|
current.add(item);
|
|
185631
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
185631
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path23, item)) {
|
|
185632
185632
|
return;
|
|
185633
185633
|
}
|
|
185634
185634
|
if (this.fsw.closed) {
|
|
@@ -185637,8 +185637,8 @@ var NodeFsHandler = class {
|
|
|
185637
185637
|
}
|
|
185638
185638
|
if (item === target || !target && !previous.has(item)) {
|
|
185639
185639
|
this.fsw._incrReadyCount();
|
|
185640
|
-
|
|
185641
|
-
this._addToNodeFs(
|
|
185640
|
+
path23 = sp.join(dir, sp.relative(dir, path23));
|
|
185641
|
+
this._addToNodeFs(path23, initialAdd, wh, depth + 1);
|
|
185642
185642
|
}
|
|
185643
185643
|
}).on(EV.ERROR, this._boundHandleError);
|
|
185644
185644
|
return new Promise((resolve7, reject) => {
|
|
@@ -185707,13 +185707,13 @@ var NodeFsHandler = class {
|
|
|
185707
185707
|
* @param depth Child path actually targeted for watch
|
|
185708
185708
|
* @param target Child path actually targeted for watch
|
|
185709
185709
|
*/
|
|
185710
|
-
async _addToNodeFs(
|
|
185710
|
+
async _addToNodeFs(path23, initialAdd, priorWh, depth, target) {
|
|
185711
185711
|
const ready = this.fsw._emitReady;
|
|
185712
|
-
if (this.fsw._isIgnored(
|
|
185712
|
+
if (this.fsw._isIgnored(path23) || this.fsw.closed) {
|
|
185713
185713
|
ready();
|
|
185714
185714
|
return false;
|
|
185715
185715
|
}
|
|
185716
|
-
const wh = this.fsw._getWatchHelpers(
|
|
185716
|
+
const wh = this.fsw._getWatchHelpers(path23);
|
|
185717
185717
|
if (priorWh) {
|
|
185718
185718
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
185719
185719
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -185729,8 +185729,8 @@ var NodeFsHandler = class {
|
|
|
185729
185729
|
const follow = this.fsw.options.followSymlinks;
|
|
185730
185730
|
let closer;
|
|
185731
185731
|
if (stats.isDirectory()) {
|
|
185732
|
-
const absPath = sp.resolve(
|
|
185733
|
-
const targetPath = follow ? await fsrealpath(
|
|
185732
|
+
const absPath = sp.resolve(path23);
|
|
185733
|
+
const targetPath = follow ? await fsrealpath(path23) : path23;
|
|
185734
185734
|
if (this.fsw.closed)
|
|
185735
185735
|
return;
|
|
185736
185736
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -185740,29 +185740,29 @@ var NodeFsHandler = class {
|
|
|
185740
185740
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
185741
185741
|
}
|
|
185742
185742
|
} else if (stats.isSymbolicLink()) {
|
|
185743
|
-
const targetPath = follow ? await fsrealpath(
|
|
185743
|
+
const targetPath = follow ? await fsrealpath(path23) : path23;
|
|
185744
185744
|
if (this.fsw.closed)
|
|
185745
185745
|
return;
|
|
185746
185746
|
const parent = sp.dirname(wh.watchPath);
|
|
185747
185747
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
185748
185748
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
185749
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
185749
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path23, wh, targetPath);
|
|
185750
185750
|
if (this.fsw.closed)
|
|
185751
185751
|
return;
|
|
185752
185752
|
if (targetPath !== void 0) {
|
|
185753
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
185753
|
+
this.fsw._symlinkPaths.set(sp.resolve(path23), targetPath);
|
|
185754
185754
|
}
|
|
185755
185755
|
} else {
|
|
185756
185756
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
185757
185757
|
}
|
|
185758
185758
|
ready();
|
|
185759
185759
|
if (closer)
|
|
185760
|
-
this.fsw._addPathCloser(
|
|
185760
|
+
this.fsw._addPathCloser(path23, closer);
|
|
185761
185761
|
return false;
|
|
185762
185762
|
} catch (error) {
|
|
185763
185763
|
if (this.fsw._handleError(error)) {
|
|
185764
185764
|
ready();
|
|
185765
|
-
return
|
|
185765
|
+
return path23;
|
|
185766
185766
|
}
|
|
185767
185767
|
}
|
|
185768
185768
|
}
|
|
@@ -185805,24 +185805,24 @@ function createPattern(matcher) {
|
|
|
185805
185805
|
}
|
|
185806
185806
|
return () => false;
|
|
185807
185807
|
}
|
|
185808
|
-
function normalizePath(
|
|
185809
|
-
if (typeof
|
|
185808
|
+
function normalizePath(path23) {
|
|
185809
|
+
if (typeof path23 !== "string")
|
|
185810
185810
|
throw new Error("string expected");
|
|
185811
|
-
|
|
185812
|
-
|
|
185811
|
+
path23 = sp2.normalize(path23);
|
|
185812
|
+
path23 = path23.replace(/\\/g, "/");
|
|
185813
185813
|
let prepend = false;
|
|
185814
|
-
if (
|
|
185814
|
+
if (path23.startsWith("//"))
|
|
185815
185815
|
prepend = true;
|
|
185816
|
-
|
|
185816
|
+
path23 = path23.replace(DOUBLE_SLASH_RE, "/");
|
|
185817
185817
|
if (prepend)
|
|
185818
|
-
|
|
185819
|
-
return
|
|
185818
|
+
path23 = "/" + path23;
|
|
185819
|
+
return path23;
|
|
185820
185820
|
}
|
|
185821
185821
|
function matchPatterns(patterns, testString, stats) {
|
|
185822
|
-
const
|
|
185822
|
+
const path23 = normalizePath(testString);
|
|
185823
185823
|
for (let index = 0; index < patterns.length; index++) {
|
|
185824
185824
|
const pattern = patterns[index];
|
|
185825
|
-
if (pattern(
|
|
185825
|
+
if (pattern(path23, stats)) {
|
|
185826
185826
|
return true;
|
|
185827
185827
|
}
|
|
185828
185828
|
}
|
|
@@ -185860,19 +185860,19 @@ var toUnix = (string) => {
|
|
|
185860
185860
|
}
|
|
185861
185861
|
return str;
|
|
185862
185862
|
};
|
|
185863
|
-
var normalizePathToUnix = (
|
|
185864
|
-
var normalizeIgnored = (cwd = "") => (
|
|
185865
|
-
if (typeof
|
|
185866
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
185863
|
+
var normalizePathToUnix = (path23) => toUnix(sp2.normalize(toUnix(path23)));
|
|
185864
|
+
var normalizeIgnored = (cwd = "") => (path23) => {
|
|
185865
|
+
if (typeof path23 === "string") {
|
|
185866
|
+
return normalizePathToUnix(sp2.isAbsolute(path23) ? path23 : sp2.join(cwd, path23));
|
|
185867
185867
|
} else {
|
|
185868
|
-
return
|
|
185868
|
+
return path23;
|
|
185869
185869
|
}
|
|
185870
185870
|
};
|
|
185871
|
-
var getAbsolutePath = (
|
|
185872
|
-
if (sp2.isAbsolute(
|
|
185873
|
-
return
|
|
185871
|
+
var getAbsolutePath = (path23, cwd) => {
|
|
185872
|
+
if (sp2.isAbsolute(path23)) {
|
|
185873
|
+
return path23;
|
|
185874
185874
|
}
|
|
185875
|
-
return sp2.join(cwd,
|
|
185875
|
+
return sp2.join(cwd, path23);
|
|
185876
185876
|
};
|
|
185877
185877
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
185878
185878
|
var DirEntry = class {
|
|
@@ -185937,10 +185937,10 @@ var WatchHelper = class {
|
|
|
185937
185937
|
dirParts;
|
|
185938
185938
|
followSymlinks;
|
|
185939
185939
|
statMethod;
|
|
185940
|
-
constructor(
|
|
185940
|
+
constructor(path23, follow, fsw) {
|
|
185941
185941
|
this.fsw = fsw;
|
|
185942
|
-
const watchPath =
|
|
185943
|
-
this.path =
|
|
185942
|
+
const watchPath = path23;
|
|
185943
|
+
this.path = path23 = path23.replace(REPLACER_RE, "");
|
|
185944
185944
|
this.watchPath = watchPath;
|
|
185945
185945
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
185946
185946
|
this.dirParts = [];
|
|
@@ -186080,20 +186080,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186080
186080
|
this._closePromise = void 0;
|
|
186081
186081
|
let paths = unifyPaths(paths_);
|
|
186082
186082
|
if (cwd) {
|
|
186083
|
-
paths = paths.map((
|
|
186084
|
-
const absPath = getAbsolutePath(
|
|
186083
|
+
paths = paths.map((path23) => {
|
|
186084
|
+
const absPath = getAbsolutePath(path23, cwd);
|
|
186085
186085
|
return absPath;
|
|
186086
186086
|
});
|
|
186087
186087
|
}
|
|
186088
|
-
paths.forEach((
|
|
186089
|
-
this._removeIgnoredPath(
|
|
186088
|
+
paths.forEach((path23) => {
|
|
186089
|
+
this._removeIgnoredPath(path23);
|
|
186090
186090
|
});
|
|
186091
186091
|
this._userIgnored = void 0;
|
|
186092
186092
|
if (!this._readyCount)
|
|
186093
186093
|
this._readyCount = 0;
|
|
186094
186094
|
this._readyCount += paths.length;
|
|
186095
|
-
Promise.all(paths.map(async (
|
|
186096
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
186095
|
+
Promise.all(paths.map(async (path23) => {
|
|
186096
|
+
const res = await this._nodeFsHandler._addToNodeFs(path23, !_internal, void 0, 0, _origAdd);
|
|
186097
186097
|
if (res)
|
|
186098
186098
|
this._emitReady();
|
|
186099
186099
|
return res;
|
|
@@ -186115,17 +186115,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186115
186115
|
return this;
|
|
186116
186116
|
const paths = unifyPaths(paths_);
|
|
186117
186117
|
const { cwd } = this.options;
|
|
186118
|
-
paths.forEach((
|
|
186119
|
-
if (!sp2.isAbsolute(
|
|
186118
|
+
paths.forEach((path23) => {
|
|
186119
|
+
if (!sp2.isAbsolute(path23) && !this._closers.has(path23)) {
|
|
186120
186120
|
if (cwd)
|
|
186121
|
-
|
|
186122
|
-
|
|
186121
|
+
path23 = sp2.join(cwd, path23);
|
|
186122
|
+
path23 = sp2.resolve(path23);
|
|
186123
186123
|
}
|
|
186124
|
-
this._closePath(
|
|
186125
|
-
this._addIgnoredPath(
|
|
186126
|
-
if (this._watched.has(
|
|
186124
|
+
this._closePath(path23);
|
|
186125
|
+
this._addIgnoredPath(path23);
|
|
186126
|
+
if (this._watched.has(path23)) {
|
|
186127
186127
|
this._addIgnoredPath({
|
|
186128
|
-
path:
|
|
186128
|
+
path: path23,
|
|
186129
186129
|
recursive: true
|
|
186130
186130
|
});
|
|
186131
186131
|
}
|
|
@@ -186189,38 +186189,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186189
186189
|
* @param stats arguments to be passed with event
|
|
186190
186190
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
186191
186191
|
*/
|
|
186192
|
-
async _emit(event,
|
|
186192
|
+
async _emit(event, path23, stats) {
|
|
186193
186193
|
if (this.closed)
|
|
186194
186194
|
return;
|
|
186195
186195
|
const opts = this.options;
|
|
186196
186196
|
if (isWindows)
|
|
186197
|
-
|
|
186197
|
+
path23 = sp2.normalize(path23);
|
|
186198
186198
|
if (opts.cwd)
|
|
186199
|
-
|
|
186200
|
-
const args = [
|
|
186199
|
+
path23 = sp2.relative(opts.cwd, path23);
|
|
186200
|
+
const args = [path23];
|
|
186201
186201
|
if (stats != null)
|
|
186202
186202
|
args.push(stats);
|
|
186203
186203
|
const awf = opts.awaitWriteFinish;
|
|
186204
186204
|
let pw;
|
|
186205
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
186205
|
+
if (awf && (pw = this._pendingWrites.get(path23))) {
|
|
186206
186206
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
186207
186207
|
return this;
|
|
186208
186208
|
}
|
|
186209
186209
|
if (opts.atomic) {
|
|
186210
186210
|
if (event === EVENTS.UNLINK) {
|
|
186211
|
-
this._pendingUnlinks.set(
|
|
186211
|
+
this._pendingUnlinks.set(path23, [event, ...args]);
|
|
186212
186212
|
setTimeout(() => {
|
|
186213
|
-
this._pendingUnlinks.forEach((entry,
|
|
186213
|
+
this._pendingUnlinks.forEach((entry, path24) => {
|
|
186214
186214
|
this.emit(...entry);
|
|
186215
186215
|
this.emit(EVENTS.ALL, ...entry);
|
|
186216
|
-
this._pendingUnlinks.delete(
|
|
186216
|
+
this._pendingUnlinks.delete(path24);
|
|
186217
186217
|
});
|
|
186218
186218
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
186219
186219
|
return this;
|
|
186220
186220
|
}
|
|
186221
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
186221
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path23)) {
|
|
186222
186222
|
event = EVENTS.CHANGE;
|
|
186223
|
-
this._pendingUnlinks.delete(
|
|
186223
|
+
this._pendingUnlinks.delete(path23);
|
|
186224
186224
|
}
|
|
186225
186225
|
}
|
|
186226
186226
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -186238,16 +186238,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186238
186238
|
this.emitWithAll(event, args);
|
|
186239
186239
|
}
|
|
186240
186240
|
};
|
|
186241
|
-
this._awaitWriteFinish(
|
|
186241
|
+
this._awaitWriteFinish(path23, awf.stabilityThreshold, event, awfEmit);
|
|
186242
186242
|
return this;
|
|
186243
186243
|
}
|
|
186244
186244
|
if (event === EVENTS.CHANGE) {
|
|
186245
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
186245
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path23, 50);
|
|
186246
186246
|
if (isThrottled)
|
|
186247
186247
|
return this;
|
|
186248
186248
|
}
|
|
186249
186249
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
186250
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
186250
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path23) : path23;
|
|
186251
186251
|
let stats2;
|
|
186252
186252
|
try {
|
|
186253
186253
|
stats2 = await stat3(fullPath);
|
|
@@ -186278,23 +186278,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186278
186278
|
* @param timeout duration of time to suppress duplicate actions
|
|
186279
186279
|
* @returns tracking object or false if action should be suppressed
|
|
186280
186280
|
*/
|
|
186281
|
-
_throttle(actionType,
|
|
186281
|
+
_throttle(actionType, path23, timeout) {
|
|
186282
186282
|
if (!this._throttled.has(actionType)) {
|
|
186283
186283
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
186284
186284
|
}
|
|
186285
186285
|
const action = this._throttled.get(actionType);
|
|
186286
186286
|
if (!action)
|
|
186287
186287
|
throw new Error("invalid throttle");
|
|
186288
|
-
const actionPath = action.get(
|
|
186288
|
+
const actionPath = action.get(path23);
|
|
186289
186289
|
if (actionPath) {
|
|
186290
186290
|
actionPath.count++;
|
|
186291
186291
|
return false;
|
|
186292
186292
|
}
|
|
186293
186293
|
let timeoutObject;
|
|
186294
186294
|
const clear = () => {
|
|
186295
|
-
const item = action.get(
|
|
186295
|
+
const item = action.get(path23);
|
|
186296
186296
|
const count = item ? item.count : 0;
|
|
186297
|
-
action.delete(
|
|
186297
|
+
action.delete(path23);
|
|
186298
186298
|
clearTimeout(timeoutObject);
|
|
186299
186299
|
if (item)
|
|
186300
186300
|
clearTimeout(item.timeoutObject);
|
|
@@ -186302,7 +186302,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186302
186302
|
};
|
|
186303
186303
|
timeoutObject = setTimeout(clear, timeout);
|
|
186304
186304
|
const thr = { timeoutObject, clear, count: 0 };
|
|
186305
|
-
action.set(
|
|
186305
|
+
action.set(path23, thr);
|
|
186306
186306
|
return thr;
|
|
186307
186307
|
}
|
|
186308
186308
|
_incrReadyCount() {
|
|
@@ -186316,44 +186316,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186316
186316
|
* @param event
|
|
186317
186317
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
186318
186318
|
*/
|
|
186319
|
-
_awaitWriteFinish(
|
|
186319
|
+
_awaitWriteFinish(path23, threshold, event, awfEmit) {
|
|
186320
186320
|
const awf = this.options.awaitWriteFinish;
|
|
186321
186321
|
if (typeof awf !== "object")
|
|
186322
186322
|
return;
|
|
186323
186323
|
const pollInterval = awf.pollInterval;
|
|
186324
186324
|
let timeoutHandler;
|
|
186325
|
-
let fullPath =
|
|
186326
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
186327
|
-
fullPath = sp2.join(this.options.cwd,
|
|
186325
|
+
let fullPath = path23;
|
|
186326
|
+
if (this.options.cwd && !sp2.isAbsolute(path23)) {
|
|
186327
|
+
fullPath = sp2.join(this.options.cwd, path23);
|
|
186328
186328
|
}
|
|
186329
186329
|
const now = /* @__PURE__ */ new Date();
|
|
186330
186330
|
const writes = this._pendingWrites;
|
|
186331
186331
|
function awaitWriteFinishFn(prevStat) {
|
|
186332
186332
|
statcb(fullPath, (err, curStat) => {
|
|
186333
|
-
if (err || !writes.has(
|
|
186333
|
+
if (err || !writes.has(path23)) {
|
|
186334
186334
|
if (err && err.code !== "ENOENT")
|
|
186335
186335
|
awfEmit(err);
|
|
186336
186336
|
return;
|
|
186337
186337
|
}
|
|
186338
186338
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
186339
186339
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
186340
|
-
writes.get(
|
|
186340
|
+
writes.get(path23).lastChange = now2;
|
|
186341
186341
|
}
|
|
186342
|
-
const pw = writes.get(
|
|
186342
|
+
const pw = writes.get(path23);
|
|
186343
186343
|
const df = now2 - pw.lastChange;
|
|
186344
186344
|
if (df >= threshold) {
|
|
186345
|
-
writes.delete(
|
|
186345
|
+
writes.delete(path23);
|
|
186346
186346
|
awfEmit(void 0, curStat);
|
|
186347
186347
|
} else {
|
|
186348
186348
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
186349
186349
|
}
|
|
186350
186350
|
});
|
|
186351
186351
|
}
|
|
186352
|
-
if (!writes.has(
|
|
186353
|
-
writes.set(
|
|
186352
|
+
if (!writes.has(path23)) {
|
|
186353
|
+
writes.set(path23, {
|
|
186354
186354
|
lastChange: now,
|
|
186355
186355
|
cancelWait: () => {
|
|
186356
|
-
writes.delete(
|
|
186356
|
+
writes.delete(path23);
|
|
186357
186357
|
clearTimeout(timeoutHandler);
|
|
186358
186358
|
return event;
|
|
186359
186359
|
}
|
|
@@ -186364,8 +186364,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186364
186364
|
/**
|
|
186365
186365
|
* Determines whether user has asked to ignore this path.
|
|
186366
186366
|
*/
|
|
186367
|
-
_isIgnored(
|
|
186368
|
-
if (this.options.atomic && DOT_RE.test(
|
|
186367
|
+
_isIgnored(path23, stats) {
|
|
186368
|
+
if (this.options.atomic && DOT_RE.test(path23))
|
|
186369
186369
|
return true;
|
|
186370
186370
|
if (!this._userIgnored) {
|
|
186371
186371
|
const { cwd } = this.options;
|
|
@@ -186375,17 +186375,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186375
186375
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
186376
186376
|
this._userIgnored = anymatch(list, void 0);
|
|
186377
186377
|
}
|
|
186378
|
-
return this._userIgnored(
|
|
186378
|
+
return this._userIgnored(path23, stats);
|
|
186379
186379
|
}
|
|
186380
|
-
_isntIgnored(
|
|
186381
|
-
return !this._isIgnored(
|
|
186380
|
+
_isntIgnored(path23, stat4) {
|
|
186381
|
+
return !this._isIgnored(path23, stat4);
|
|
186382
186382
|
}
|
|
186383
186383
|
/**
|
|
186384
186384
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
186385
186385
|
* @param path file or directory pattern being watched
|
|
186386
186386
|
*/
|
|
186387
|
-
_getWatchHelpers(
|
|
186388
|
-
return new WatchHelper(
|
|
186387
|
+
_getWatchHelpers(path23) {
|
|
186388
|
+
return new WatchHelper(path23, this.options.followSymlinks, this);
|
|
186389
186389
|
}
|
|
186390
186390
|
// Directory helpers
|
|
186391
186391
|
// -----------------
|
|
@@ -186417,63 +186417,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
186417
186417
|
* @param item base path of item/directory
|
|
186418
186418
|
*/
|
|
186419
186419
|
_remove(directory, item, isDirectory) {
|
|
186420
|
-
const
|
|
186421
|
-
const fullPath = sp2.resolve(
|
|
186422
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
186423
|
-
if (!this._throttle("remove",
|
|
186420
|
+
const path23 = sp2.join(directory, item);
|
|
186421
|
+
const fullPath = sp2.resolve(path23);
|
|
186422
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path23) || this._watched.has(fullPath);
|
|
186423
|
+
if (!this._throttle("remove", path23, 100))
|
|
186424
186424
|
return;
|
|
186425
186425
|
if (!isDirectory && this._watched.size === 1) {
|
|
186426
186426
|
this.add(directory, item, true);
|
|
186427
186427
|
}
|
|
186428
|
-
const wp = this._getWatchedDir(
|
|
186428
|
+
const wp = this._getWatchedDir(path23);
|
|
186429
186429
|
const nestedDirectoryChildren = wp.getChildren();
|
|
186430
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
186430
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path23, nested));
|
|
186431
186431
|
const parent = this._getWatchedDir(directory);
|
|
186432
186432
|
const wasTracked = parent.has(item);
|
|
186433
186433
|
parent.remove(item);
|
|
186434
186434
|
if (this._symlinkPaths.has(fullPath)) {
|
|
186435
186435
|
this._symlinkPaths.delete(fullPath);
|
|
186436
186436
|
}
|
|
186437
|
-
let relPath =
|
|
186437
|
+
let relPath = path23;
|
|
186438
186438
|
if (this.options.cwd)
|
|
186439
|
-
relPath = sp2.relative(this.options.cwd,
|
|
186439
|
+
relPath = sp2.relative(this.options.cwd, path23);
|
|
186440
186440
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
186441
186441
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
186442
186442
|
if (event === EVENTS.ADD)
|
|
186443
186443
|
return;
|
|
186444
186444
|
}
|
|
186445
|
-
this._watched.delete(
|
|
186445
|
+
this._watched.delete(path23);
|
|
186446
186446
|
this._watched.delete(fullPath);
|
|
186447
186447
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
186448
|
-
if (wasTracked && !this._isIgnored(
|
|
186449
|
-
this._emit(eventName,
|
|
186450
|
-
this._closePath(
|
|
186448
|
+
if (wasTracked && !this._isIgnored(path23))
|
|
186449
|
+
this._emit(eventName, path23);
|
|
186450
|
+
this._closePath(path23);
|
|
186451
186451
|
}
|
|
186452
186452
|
/**
|
|
186453
186453
|
* Closes all watchers for a path
|
|
186454
186454
|
*/
|
|
186455
|
-
_closePath(
|
|
186456
|
-
this._closeFile(
|
|
186457
|
-
const dir = sp2.dirname(
|
|
186458
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
186455
|
+
_closePath(path23) {
|
|
186456
|
+
this._closeFile(path23);
|
|
186457
|
+
const dir = sp2.dirname(path23);
|
|
186458
|
+
this._getWatchedDir(dir).remove(sp2.basename(path23));
|
|
186459
186459
|
}
|
|
186460
186460
|
/**
|
|
186461
186461
|
* Closes only file-specific watchers
|
|
186462
186462
|
*/
|
|
186463
|
-
_closeFile(
|
|
186464
|
-
const closers = this._closers.get(
|
|
186463
|
+
_closeFile(path23) {
|
|
186464
|
+
const closers = this._closers.get(path23);
|
|
186465
186465
|
if (!closers)
|
|
186466
186466
|
return;
|
|
186467
186467
|
closers.forEach((closer) => closer());
|
|
186468
|
-
this._closers.delete(
|
|
186468
|
+
this._closers.delete(path23);
|
|
186469
186469
|
}
|
|
186470
|
-
_addPathCloser(
|
|
186470
|
+
_addPathCloser(path23, closer) {
|
|
186471
186471
|
if (!closer)
|
|
186472
186472
|
return;
|
|
186473
|
-
let list = this._closers.get(
|
|
186473
|
+
let list = this._closers.get(path23);
|
|
186474
186474
|
if (!list) {
|
|
186475
186475
|
list = [];
|
|
186476
|
-
this._closers.set(
|
|
186476
|
+
this._closers.set(path23, list);
|
|
186477
186477
|
}
|
|
186478
186478
|
list.push(closer);
|
|
186479
186479
|
}
|
|
@@ -188620,9 +188620,106 @@ function watchConfigFile(configPath, debounceMs, onChange) {
|
|
|
188620
188620
|
};
|
|
188621
188621
|
}
|
|
188622
188622
|
|
|
188623
|
-
// src/lib/dev/
|
|
188623
|
+
// src/lib/dev/subdomain-generator.ts
|
|
188624
188624
|
import * as fs18 from "fs";
|
|
188625
188625
|
import * as path15 from "path";
|
|
188626
|
+
import { generateSlug } from "random-word-slugs";
|
|
188627
|
+
var StableSubdomainAllocator = class {
|
|
188628
|
+
tunnelsDir;
|
|
188629
|
+
tunnelsFilePath;
|
|
188630
|
+
baseSlug = null;
|
|
188631
|
+
constructor(projectRoot, key = "default") {
|
|
188632
|
+
this.tunnelsDir = path15.join(projectRoot, ".specific", "keys", key);
|
|
188633
|
+
this.tunnelsFilePath = path15.join(this.tunnelsDir, "tunnels.json");
|
|
188634
|
+
this.loadTunnels();
|
|
188635
|
+
}
|
|
188636
|
+
loadTunnels() {
|
|
188637
|
+
if (!fs18.existsSync(this.tunnelsFilePath)) {
|
|
188638
|
+
return;
|
|
188639
|
+
}
|
|
188640
|
+
try {
|
|
188641
|
+
const content = fs18.readFileSync(this.tunnelsFilePath, "utf-8");
|
|
188642
|
+
const data = JSON.parse(content);
|
|
188643
|
+
if (data.version === 1 && data.baseSlug) {
|
|
188644
|
+
this.baseSlug = data.baseSlug;
|
|
188645
|
+
}
|
|
188646
|
+
} catch {
|
|
188647
|
+
this.baseSlug = null;
|
|
188648
|
+
}
|
|
188649
|
+
}
|
|
188650
|
+
saveTunnels() {
|
|
188651
|
+
if (!fs18.existsSync(this.tunnelsDir)) {
|
|
188652
|
+
fs18.mkdirSync(this.tunnelsDir, { recursive: true });
|
|
188653
|
+
}
|
|
188654
|
+
const data = {
|
|
188655
|
+
version: 1,
|
|
188656
|
+
baseSlug: this.baseSlug
|
|
188657
|
+
};
|
|
188658
|
+
fs18.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
|
|
188659
|
+
}
|
|
188660
|
+
generateBaseSlug() {
|
|
188661
|
+
return generateSlug(2, {
|
|
188662
|
+
format: "kebab",
|
|
188663
|
+
partsOfSpeech: ["adjective", "noun"],
|
|
188664
|
+
categories: {
|
|
188665
|
+
adjective: ["color", "appearance"],
|
|
188666
|
+
noun: ["animals"]
|
|
188667
|
+
}
|
|
188668
|
+
});
|
|
188669
|
+
}
|
|
188670
|
+
/**
|
|
188671
|
+
* Get the base slug, generating one if needed.
|
|
188672
|
+
*/
|
|
188673
|
+
getBaseSlug() {
|
|
188674
|
+
if (!this.baseSlug) {
|
|
188675
|
+
this.baseSlug = this.generateBaseSlug();
|
|
188676
|
+
this.saveTunnels();
|
|
188677
|
+
}
|
|
188678
|
+
return this.baseSlug;
|
|
188679
|
+
}
|
|
188680
|
+
/**
|
|
188681
|
+
* Allocate a subdomain for a service.
|
|
188682
|
+
* If multipleServices is true, appends the service name to the base slug.
|
|
188683
|
+
*/
|
|
188684
|
+
allocate(serviceName, multipleServices) {
|
|
188685
|
+
const baseSlug = this.getBaseSlug();
|
|
188686
|
+
if (multipleServices) {
|
|
188687
|
+
return `${baseSlug}-${serviceName}`;
|
|
188688
|
+
}
|
|
188689
|
+
return baseSlug;
|
|
188690
|
+
}
|
|
188691
|
+
};
|
|
188692
|
+
|
|
188693
|
+
// src/lib/dev/tunnel-manager.ts
|
|
188694
|
+
import localtunnel from "localtunnel";
|
|
188695
|
+
var TUNNEL_HOST = "https://tunnel.spcf.app";
|
|
188696
|
+
async function startTunnel(serviceName, endpointName, port, subdomain, callbacks) {
|
|
188697
|
+
const tunnel = await localtunnel({
|
|
188698
|
+
port,
|
|
188699
|
+
subdomain,
|
|
188700
|
+
host: TUNNEL_HOST
|
|
188701
|
+
});
|
|
188702
|
+
tunnel.on("error", (err) => {
|
|
188703
|
+
callbacks?.onError?.(serviceName, endpointName, err);
|
|
188704
|
+
});
|
|
188705
|
+
tunnel.on("close", () => {
|
|
188706
|
+
callbacks?.onClose?.(serviceName, endpointName);
|
|
188707
|
+
});
|
|
188708
|
+
return {
|
|
188709
|
+
serviceName,
|
|
188710
|
+
endpointName,
|
|
188711
|
+
localPort: port,
|
|
188712
|
+
url: tunnel.url,
|
|
188713
|
+
subdomain,
|
|
188714
|
+
stop: async () => {
|
|
188715
|
+
tunnel.close();
|
|
188716
|
+
}
|
|
188717
|
+
};
|
|
188718
|
+
}
|
|
188719
|
+
|
|
188720
|
+
// src/lib/dev/proxy-registry.ts
|
|
188721
|
+
import * as fs19 from "fs";
|
|
188722
|
+
import * as path16 from "path";
|
|
188626
188723
|
import * as os8 from "os";
|
|
188627
188724
|
import * as net4 from "net";
|
|
188628
188725
|
var ProxyRegistryManager = class {
|
|
@@ -188633,14 +188730,14 @@ var ProxyRegistryManager = class {
|
|
|
188633
188730
|
isOwner = false;
|
|
188634
188731
|
registryWatcher = null;
|
|
188635
188732
|
constructor() {
|
|
188636
|
-
this.proxyDir =
|
|
188637
|
-
this.ownerPath =
|
|
188638
|
-
this.registryPath =
|
|
188639
|
-
this.lockPath =
|
|
188733
|
+
this.proxyDir = path16.join(os8.homedir(), ".specific", "proxy");
|
|
188734
|
+
this.ownerPath = path16.join(this.proxyDir, "owner.json");
|
|
188735
|
+
this.registryPath = path16.join(this.proxyDir, "registry.json");
|
|
188736
|
+
this.lockPath = path16.join(this.proxyDir, "registry.lock");
|
|
188640
188737
|
}
|
|
188641
188738
|
ensureProxyDir() {
|
|
188642
|
-
if (!
|
|
188643
|
-
|
|
188739
|
+
if (!fs19.existsSync(this.proxyDir)) {
|
|
188740
|
+
fs19.mkdirSync(this.proxyDir, { recursive: true });
|
|
188644
188741
|
}
|
|
188645
188742
|
}
|
|
188646
188743
|
isProcessRunning(pid) {
|
|
@@ -188697,15 +188794,15 @@ var ProxyRegistryManager = class {
|
|
|
188697
188794
|
const startTime = Date.now();
|
|
188698
188795
|
while (Date.now() - startTime < timeoutMs) {
|
|
188699
188796
|
try {
|
|
188700
|
-
const fd =
|
|
188797
|
+
const fd = fs19.openSync(
|
|
188701
188798
|
this.lockPath,
|
|
188702
|
-
|
|
188799
|
+
fs19.constants.O_CREAT | fs19.constants.O_EXCL | fs19.constants.O_WRONLY
|
|
188703
188800
|
);
|
|
188704
|
-
|
|
188705
|
-
|
|
188801
|
+
fs19.writeSync(fd, String(process.pid));
|
|
188802
|
+
fs19.closeSync(fd);
|
|
188706
188803
|
return () => {
|
|
188707
188804
|
try {
|
|
188708
|
-
|
|
188805
|
+
fs19.unlinkSync(this.lockPath);
|
|
188709
188806
|
} catch {
|
|
188710
188807
|
}
|
|
188711
188808
|
};
|
|
@@ -188714,16 +188811,16 @@ var ProxyRegistryManager = class {
|
|
|
188714
188811
|
if (err.code === "EEXIST") {
|
|
188715
188812
|
try {
|
|
188716
188813
|
const lockPid = parseInt(
|
|
188717
|
-
|
|
188814
|
+
fs19.readFileSync(this.lockPath, "utf-8").trim(),
|
|
188718
188815
|
10
|
|
188719
188816
|
);
|
|
188720
188817
|
if (!this.isProcessRunning(lockPid)) {
|
|
188721
|
-
|
|
188818
|
+
fs19.unlinkSync(this.lockPath);
|
|
188722
188819
|
continue;
|
|
188723
188820
|
}
|
|
188724
188821
|
} catch {
|
|
188725
188822
|
try {
|
|
188726
|
-
|
|
188823
|
+
fs19.unlinkSync(this.lockPath);
|
|
188727
188824
|
} catch {
|
|
188728
188825
|
}
|
|
188729
188826
|
continue;
|
|
@@ -188743,8 +188840,8 @@ var ProxyRegistryManager = class {
|
|
|
188743
188840
|
async claimProxyOwnership(key) {
|
|
188744
188841
|
const releaseLock = await this.acquireLock();
|
|
188745
188842
|
try {
|
|
188746
|
-
if (
|
|
188747
|
-
const content =
|
|
188843
|
+
if (fs19.existsSync(this.ownerPath)) {
|
|
188844
|
+
const content = fs19.readFileSync(this.ownerPath, "utf-8");
|
|
188748
188845
|
const ownerFile2 = JSON.parse(content);
|
|
188749
188846
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
188750
188847
|
return false;
|
|
@@ -188774,11 +188871,11 @@ var ProxyRegistryManager = class {
|
|
|
188774
188871
|
}
|
|
188775
188872
|
const releaseLock = await this.acquireLock();
|
|
188776
188873
|
try {
|
|
188777
|
-
if (
|
|
188778
|
-
const content =
|
|
188874
|
+
if (fs19.existsSync(this.ownerPath)) {
|
|
188875
|
+
const content = fs19.readFileSync(this.ownerPath, "utf-8");
|
|
188779
188876
|
const ownerFile = JSON.parse(content);
|
|
188780
188877
|
if (ownerFile.owner.pid === process.pid) {
|
|
188781
|
-
|
|
188878
|
+
fs19.unlinkSync(this.ownerPath);
|
|
188782
188879
|
}
|
|
188783
188880
|
}
|
|
188784
188881
|
this.isOwner = false;
|
|
@@ -188790,12 +188887,12 @@ var ProxyRegistryManager = class {
|
|
|
188790
188887
|
* Get the current proxy owner.
|
|
188791
188888
|
*/
|
|
188792
188889
|
async getProxyOwner() {
|
|
188793
|
-
if (!
|
|
188890
|
+
if (!fs19.existsSync(this.ownerPath)) {
|
|
188794
188891
|
return null;
|
|
188795
188892
|
}
|
|
188796
188893
|
const releaseLock = await this.acquireLock();
|
|
188797
188894
|
try {
|
|
188798
|
-
const content =
|
|
188895
|
+
const content = fs19.readFileSync(this.ownerPath, "utf-8");
|
|
188799
188896
|
const ownerFile = JSON.parse(content);
|
|
188800
188897
|
if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
|
|
188801
188898
|
return null;
|
|
@@ -188889,7 +188986,7 @@ var ProxyRegistryManager = class {
|
|
|
188889
188986
|
*/
|
|
188890
188987
|
watchRegistry(onChange) {
|
|
188891
188988
|
this.ensureProxyDir();
|
|
188892
|
-
if (!
|
|
188989
|
+
if (!fs19.existsSync(this.registryPath)) {
|
|
188893
188990
|
const emptyRegistry = {
|
|
188894
188991
|
version: 1,
|
|
188895
188992
|
keys: {},
|
|
@@ -188933,13 +189030,13 @@ var ProxyRegistryManager = class {
|
|
|
188933
189030
|
async attemptElection(key) {
|
|
188934
189031
|
const releaseLock = await this.acquireLock();
|
|
188935
189032
|
try {
|
|
188936
|
-
if (
|
|
188937
|
-
const content =
|
|
189033
|
+
if (fs19.existsSync(this.ownerPath)) {
|
|
189034
|
+
const content = fs19.readFileSync(this.ownerPath, "utf-8");
|
|
188938
189035
|
const ownerFile2 = JSON.parse(content);
|
|
188939
189036
|
if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
|
|
188940
189037
|
return false;
|
|
188941
189038
|
}
|
|
188942
|
-
|
|
189039
|
+
fs19.unlinkSync(this.ownerPath);
|
|
188943
189040
|
}
|
|
188944
189041
|
const ownerFile = {
|
|
188945
189042
|
version: 1,
|
|
@@ -188957,7 +189054,7 @@ var ProxyRegistryManager = class {
|
|
|
188957
189054
|
}
|
|
188958
189055
|
}
|
|
188959
189056
|
readRegistry() {
|
|
188960
|
-
if (!
|
|
189057
|
+
if (!fs19.existsSync(this.registryPath)) {
|
|
188961
189058
|
return {
|
|
188962
189059
|
version: 1,
|
|
188963
189060
|
keys: {},
|
|
@@ -188965,7 +189062,7 @@ var ProxyRegistryManager = class {
|
|
|
188965
189062
|
};
|
|
188966
189063
|
}
|
|
188967
189064
|
try {
|
|
188968
|
-
const content =
|
|
189065
|
+
const content = fs19.readFileSync(this.registryPath, "utf-8");
|
|
188969
189066
|
return JSON.parse(content);
|
|
188970
189067
|
} catch {
|
|
188971
189068
|
return {
|
|
@@ -188978,8 +189075,8 @@ var ProxyRegistryManager = class {
|
|
|
188978
189075
|
writeFileAtomic(filePath, data) {
|
|
188979
189076
|
this.ensureProxyDir();
|
|
188980
189077
|
const tmpPath = filePath + ".tmp";
|
|
188981
|
-
|
|
188982
|
-
|
|
189078
|
+
fs19.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
189079
|
+
fs19.renameSync(tmpPath, filePath);
|
|
188983
189080
|
}
|
|
188984
189081
|
};
|
|
188985
189082
|
|
|
@@ -189085,10 +189182,10 @@ function isInteractive() {
|
|
|
189085
189182
|
|
|
189086
189183
|
// src/commands/dev.tsx
|
|
189087
189184
|
var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
|
|
189088
|
-
function DevUI({ instanceKey }) {
|
|
189185
|
+
function DevUI({ instanceKey, tunnelEnabled }) {
|
|
189089
189186
|
const { exit } = useApp2();
|
|
189090
189187
|
const [state, setState] = useState5(() => {
|
|
189091
|
-
const caExists = caFilesExist();
|
|
189188
|
+
const caExists = tunnelEnabled || caFilesExist();
|
|
189092
189189
|
return {
|
|
189093
189190
|
status: caExists ? "loading" : "installing-ca",
|
|
189094
189191
|
...caExists ? {} : { caInstallPhase: "installing" },
|
|
@@ -189097,7 +189194,9 @@ function DevUI({ instanceKey }) {
|
|
|
189097
189194
|
services: [],
|
|
189098
189195
|
output: [],
|
|
189099
189196
|
colorMap: /* @__PURE__ */ new Map(),
|
|
189100
|
-
isProxyOwner: false
|
|
189197
|
+
isProxyOwner: false,
|
|
189198
|
+
tunnels: /* @__PURE__ */ new Map(),
|
|
189199
|
+
tunnelStatus: /* @__PURE__ */ new Map()
|
|
189101
189200
|
};
|
|
189102
189201
|
});
|
|
189103
189202
|
useEffect3(() => {
|
|
@@ -189129,6 +189228,7 @@ function DevUI({ instanceKey }) {
|
|
|
189129
189228
|
const drizzleGatewayRef = useRef(null);
|
|
189130
189229
|
const registryWatcherCleanupRef = useRef(null);
|
|
189131
189230
|
const electionIntervalRef = useRef(null);
|
|
189231
|
+
const tunnelsRef = useRef([]);
|
|
189132
189232
|
const proxyRef = useRef(null);
|
|
189133
189233
|
const adminServerRef = useRef(null);
|
|
189134
189234
|
const servicesRef = useRef([]);
|
|
@@ -189162,7 +189262,9 @@ function DevUI({ instanceKey }) {
|
|
|
189162
189262
|
// Stop Drizzle Gateway
|
|
189163
189263
|
drizzleGatewayRef.current?.stop(),
|
|
189164
189264
|
// Stop all resources
|
|
189165
|
-
...[...resourcesRef.current.values()].map((resource) => resource.stop())
|
|
189265
|
+
...[...resourcesRef.current.values()].map((resource) => resource.stop()),
|
|
189266
|
+
// Stop all tunnels
|
|
189267
|
+
...tunnelsRef.current.map((tunnel) => tunnel.stop())
|
|
189166
189268
|
]);
|
|
189167
189269
|
if (proxyRegistryRef.current) {
|
|
189168
189270
|
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
@@ -189199,7 +189301,9 @@ function DevUI({ instanceKey }) {
|
|
|
189199
189301
|
// Stop Drizzle Gateway
|
|
189200
189302
|
drizzleGatewayRef.current?.stop(),
|
|
189201
189303
|
// Stop all resources
|
|
189202
|
-
...[...resourcesRef.current.values()].map((resource) => resource.stop())
|
|
189304
|
+
...[...resourcesRef.current.values()].map((resource) => resource.stop()),
|
|
189305
|
+
// Stop all tunnels
|
|
189306
|
+
...tunnelsRef.current.map((tunnel) => tunnel.stop())
|
|
189203
189307
|
]);
|
|
189204
189308
|
electricInstancesRef.current = [];
|
|
189205
189309
|
drizzleGatewayRef.current = null;
|
|
@@ -189207,6 +189311,7 @@ function DevUI({ instanceKey }) {
|
|
|
189207
189311
|
adminServerRef.current = null;
|
|
189208
189312
|
servicesRef.current = [];
|
|
189209
189313
|
resourcesRef.current = /* @__PURE__ */ new Map();
|
|
189314
|
+
tunnelsRef.current = [];
|
|
189210
189315
|
if (proxyRegistryRef.current) {
|
|
189211
189316
|
await proxyRegistryRef.current.unregisterServices(instanceKey);
|
|
189212
189317
|
await proxyRegistryRef.current.releaseProxyOwnership();
|
|
@@ -189222,7 +189327,9 @@ function DevUI({ instanceKey }) {
|
|
|
189222
189327
|
resourceStatus: /* @__PURE__ */ new Map(),
|
|
189223
189328
|
services: [],
|
|
189224
189329
|
proxy: void 0,
|
|
189225
|
-
isProxyOwner: false
|
|
189330
|
+
isProxyOwner: false,
|
|
189331
|
+
tunnels: /* @__PURE__ */ new Map(),
|
|
189332
|
+
tunnelStatus: /* @__PURE__ */ new Map()
|
|
189226
189333
|
}));
|
|
189227
189334
|
setReloadTrigger((t) => t + 1);
|
|
189228
189335
|
};
|
|
@@ -189263,10 +189370,10 @@ function DevUI({ instanceKey }) {
|
|
|
189263
189370
|
}, [state.status]);
|
|
189264
189371
|
useEffect3(() => {
|
|
189265
189372
|
if (state.status !== "running") return;
|
|
189266
|
-
const configPath =
|
|
189373
|
+
const configPath = path17.join(process.cwd(), "specific.hcl");
|
|
189267
189374
|
const watcher = watchConfigFile(configPath, 1e3, () => {
|
|
189268
189375
|
try {
|
|
189269
|
-
const hcl =
|
|
189376
|
+
const hcl = fs20.readFileSync(configPath, "utf-8");
|
|
189270
189377
|
parseConfig(hcl).then(() => {
|
|
189271
189378
|
triggerReload();
|
|
189272
189379
|
}).catch((err) => {
|
|
@@ -189391,8 +189498,8 @@ function DevUI({ instanceKey }) {
|
|
|
189391
189498
|
}));
|
|
189392
189499
|
return;
|
|
189393
189500
|
}
|
|
189394
|
-
const configPath =
|
|
189395
|
-
if (!
|
|
189501
|
+
const configPath = path17.join(process.cwd(), "specific.hcl");
|
|
189502
|
+
if (!fs20.existsSync(configPath)) {
|
|
189396
189503
|
writeLog("system", "Waiting for specific.hcl to appear");
|
|
189397
189504
|
setState((s) => ({
|
|
189398
189505
|
...s,
|
|
@@ -189411,7 +189518,7 @@ function DevUI({ instanceKey }) {
|
|
|
189411
189518
|
}
|
|
189412
189519
|
let config2;
|
|
189413
189520
|
try {
|
|
189414
|
-
const hcl =
|
|
189521
|
+
const hcl = fs20.readFileSync(configPath, "utf-8");
|
|
189415
189522
|
config2 = await parseConfig(hcl);
|
|
189416
189523
|
} catch (err) {
|
|
189417
189524
|
setState((s) => ({
|
|
@@ -189517,7 +189624,7 @@ function DevUI({ instanceKey }) {
|
|
|
189517
189624
|
const drizzleGateway = await startDrizzleGateway(
|
|
189518
189625
|
postgresResources,
|
|
189519
189626
|
drizzlePort,
|
|
189520
|
-
|
|
189627
|
+
path17.join(process.cwd(), ".specific", "keys", instanceKey)
|
|
189521
189628
|
);
|
|
189522
189629
|
startedDrizzleGateway = drizzleGateway;
|
|
189523
189630
|
drizzleGatewayRef.current = drizzleGateway;
|
|
@@ -189686,136 +189793,196 @@ Add them to the config block in specific.local`);
|
|
|
189686
189793
|
}
|
|
189687
189794
|
}
|
|
189688
189795
|
}
|
|
189689
|
-
|
|
189690
|
-
|
|
189691
|
-
|
|
189692
|
-
|
|
189693
|
-
|
|
189694
|
-
|
|
189695
|
-
|
|
189696
|
-
|
|
189697
|
-
|
|
189698
|
-
|
|
189699
|
-
|
|
189700
|
-
|
|
189701
|
-
|
|
189702
|
-
|
|
189703
|
-
|
|
189704
|
-
|
|
189705
|
-
|
|
189706
|
-
|
|
189707
|
-
|
|
189708
|
-
|
|
189709
|
-
|
|
189710
|
-
|
|
189711
|
-
|
|
189712
|
-
|
|
189713
|
-
|
|
189714
|
-
|
|
189715
|
-
|
|
189716
|
-
|
|
189717
|
-
|
|
189718
|
-
|
|
189719
|
-
|
|
189720
|
-
|
|
189721
|
-
|
|
189722
|
-
|
|
189723
|
-
|
|
189724
|
-
|
|
189725
|
-
|
|
189726
|
-
|
|
189727
|
-
|
|
189728
|
-
|
|
189729
|
-
|
|
189730
|
-
|
|
189731
|
-
|
|
189732
|
-
|
|
189733
|
-
|
|
189734
|
-
|
|
189735
|
-
|
|
189736
|
-
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
189737
|
-
const knownKeys = new Set(registeredKeys);
|
|
189738
|
-
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry(async (updatedServices, updatedKeys) => {
|
|
189739
|
-
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
189740
|
-
proxy2.updateServices(updatedServices, updatedKeys);
|
|
189741
|
-
const newKeyNames = Object.keys(updatedKeys).filter((k) => !knownKeys.has(k));
|
|
189742
|
-
if (newKeyNames.length > 0) {
|
|
189743
|
-
writeLog("system", `New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`);
|
|
189744
|
-
for (const key of newKeyNames) {
|
|
189745
|
-
knownKeys.add(key);
|
|
189796
|
+
if (tunnelEnabled) {
|
|
189797
|
+
writeLog("system", "Tunnel mode enabled, starting tunnels for public services");
|
|
189798
|
+
if (exposedServices.length === 0) {
|
|
189799
|
+
writeLog("system", "No public services to tunnel");
|
|
189800
|
+
} else {
|
|
189801
|
+
const subdomainAllocator = new StableSubdomainAllocator(process.cwd(), instanceKey);
|
|
189802
|
+
const multipleServices = exposedServices.length > 1;
|
|
189803
|
+
const tunnelInstances = [];
|
|
189804
|
+
const tunnelStatusMap = /* @__PURE__ */ new Map();
|
|
189805
|
+
for (const svc of exposedServices) {
|
|
189806
|
+
tunnelStatusMap.set(svc.name, "connecting");
|
|
189807
|
+
}
|
|
189808
|
+
setState((s) => ({ ...s, tunnelStatus: new Map(tunnelStatusMap) }));
|
|
189809
|
+
for (const svc of exposedServices) {
|
|
189810
|
+
if (cancelled) return;
|
|
189811
|
+
const subdomain = subdomainAllocator.allocate(svc.name, multipleServices);
|
|
189812
|
+
writeLog("system", `Starting tunnel for ${svc.name} on port ${svc.port} (subdomain: ${subdomain})`);
|
|
189813
|
+
try {
|
|
189814
|
+
const tunnel = await startTunnel(
|
|
189815
|
+
svc.name,
|
|
189816
|
+
"default",
|
|
189817
|
+
svc.port,
|
|
189818
|
+
subdomain,
|
|
189819
|
+
{
|
|
189820
|
+
onError: (serviceName, _endpointName, error) => {
|
|
189821
|
+
writeLog("system:error", `Tunnel error for ${serviceName}: ${error.message}`);
|
|
189822
|
+
},
|
|
189823
|
+
onClose: (serviceName) => {
|
|
189824
|
+
writeLog("system", `Tunnel closed for ${serviceName}`);
|
|
189825
|
+
}
|
|
189826
|
+
}
|
|
189827
|
+
);
|
|
189828
|
+
tunnelInstances.push(tunnel);
|
|
189829
|
+
tunnelsRef.current = [...tunnelInstances];
|
|
189830
|
+
tunnelStatusMap.set(svc.name, "connected");
|
|
189831
|
+
setState((s) => ({
|
|
189832
|
+
...s,
|
|
189833
|
+
tunnels: new Map([...s.tunnels, [svc.name, tunnel]]),
|
|
189834
|
+
tunnelStatus: new Map(tunnelStatusMap)
|
|
189835
|
+
}));
|
|
189836
|
+
writeLog("system", `Tunnel ready for ${svc.name}: ${tunnel.url}`);
|
|
189837
|
+
} catch (err) {
|
|
189838
|
+
const errorMsg = `Failed to start tunnel for ${svc.name}: ${err instanceof Error ? err.message : String(err)}`;
|
|
189839
|
+
writeLog("system:error", errorMsg);
|
|
189840
|
+
tunnelStatusMap.set(svc.name, "error");
|
|
189841
|
+
for (const t of tunnelInstances) {
|
|
189842
|
+
await t.stop();
|
|
189746
189843
|
}
|
|
189747
|
-
|
|
189748
|
-
|
|
189749
|
-
|
|
189844
|
+
setState((s) => ({
|
|
189845
|
+
...s,
|
|
189846
|
+
status: "error",
|
|
189847
|
+
error: errorMsg,
|
|
189848
|
+
tunnelStatus: new Map(tunnelStatusMap)
|
|
189849
|
+
}));
|
|
189850
|
+
return;
|
|
189750
189851
|
}
|
|
189751
|
-
}
|
|
189752
|
-
const currentKeys = await proxyRegistry.getAllKeyRegistrations();
|
|
189753
|
-
proxy2.updateServices(currentServices, currentKeys);
|
|
189754
|
-
writeLog("system", `Loaded ${currentServices.length} services from registry`);
|
|
189755
|
-
} catch (err) {
|
|
189756
|
-
const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
|
|
189757
|
-
writeLog("system:error", errorMsg);
|
|
189758
|
-
setState((s) => ({
|
|
189759
|
-
...s,
|
|
189760
|
-
status: "error",
|
|
189761
|
-
error: errorMsg
|
|
189762
|
-
}));
|
|
189763
|
-
return;
|
|
189852
|
+
}
|
|
189764
189853
|
}
|
|
189765
189854
|
} else {
|
|
189766
|
-
|
|
189767
|
-
|
|
189768
|
-
|
|
189855
|
+
const runningServicePorts = /* @__PURE__ */ new Map();
|
|
189856
|
+
for (const s of services2) {
|
|
189857
|
+
runningServicePorts.set(s.name, s.ports.get("default"));
|
|
189858
|
+
}
|
|
189859
|
+
const projectId = hasProjectId() ? readProjectId() : void 0;
|
|
189860
|
+
const getState = () => ({
|
|
189861
|
+
status: "running",
|
|
189862
|
+
services: config2.services.filter((svc) => runningServicePorts.has(svc.name) || svc.serve).map((svc) => ({
|
|
189863
|
+
name: svc.name,
|
|
189864
|
+
port: runningServicePorts.get(svc.name),
|
|
189865
|
+
exposed: !!svc.serve || svc.endpoints.some((e) => e.public),
|
|
189866
|
+
env: svc.env
|
|
189867
|
+
})),
|
|
189868
|
+
resources: [...resources2.entries()].map(([name, r]) => ({
|
|
189869
|
+
name,
|
|
189870
|
+
type: r.type,
|
|
189871
|
+
port: r.port,
|
|
189872
|
+
host: r.host,
|
|
189873
|
+
syncEnabled: r.type === "postgres" && syncDatabases.has(name)
|
|
189874
|
+
})),
|
|
189875
|
+
projectId
|
|
189876
|
+
});
|
|
189877
|
+
const adminServer = await startAdminServer(getState);
|
|
189878
|
+
adminServerRef.current = adminServer;
|
|
189879
|
+
writeLog("system", `Admin API server started on port ${adminServer.port}`);
|
|
189880
|
+
await proxyRegistry.registerServices(
|
|
189881
|
+
instanceKey,
|
|
189882
|
+
adminServer.port,
|
|
189883
|
+
serviceInfos,
|
|
189884
|
+
startedDrizzleGateway?.port
|
|
189885
|
+
);
|
|
189886
|
+
writeLog("system", `Registered ${serviceInfos.length} services with proxy registry`);
|
|
189887
|
+
const becameProxyOwner = await proxyRegistry.claimProxyOwnership(instanceKey);
|
|
189888
|
+
if (becameProxyOwner) {
|
|
189889
|
+
writeLog("system", "Claimed proxy ownership, starting HTTP proxy");
|
|
189769
189890
|
try {
|
|
189770
|
-
|
|
189771
|
-
|
|
189772
|
-
|
|
189773
|
-
|
|
189774
|
-
|
|
189775
|
-
|
|
189776
|
-
|
|
189777
|
-
|
|
189778
|
-
|
|
189779
|
-
|
|
189780
|
-
|
|
189781
|
-
}
|
|
189891
|
+
const currentServices = await proxyRegistry.getAllServices();
|
|
189892
|
+
const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
|
|
189893
|
+
const certificate = generateCertificate("local.spcf.app", registeredKeys);
|
|
189894
|
+
const proxy2 = await startHttpProxy(
|
|
189895
|
+
exposedServices,
|
|
189896
|
+
certificate,
|
|
189897
|
+
getState,
|
|
189898
|
+
instanceKey
|
|
189899
|
+
);
|
|
189900
|
+
startedProxy = proxy2;
|
|
189901
|
+
proxyRef.current = proxy2;
|
|
189902
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
189903
|
+
const knownKeys = new Set(registeredKeys);
|
|
189904
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry(async (updatedServices, updatedKeys) => {
|
|
189905
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
189906
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
189907
|
+
const newKeyNames = Object.keys(updatedKeys).filter((k) => !knownKeys.has(k));
|
|
189908
|
+
if (newKeyNames.length > 0) {
|
|
189909
|
+
writeLog("system", `New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`);
|
|
189910
|
+
for (const key of newKeyNames) {
|
|
189911
|
+
knownKeys.add(key);
|
|
189912
|
+
}
|
|
189913
|
+
const allKeys = [...knownKeys];
|
|
189914
|
+
const newCertificate = generateCertificate("local.spcf.app", allKeys);
|
|
189915
|
+
proxy2.updateCertificate(newCertificate);
|
|
189916
|
+
}
|
|
189917
|
+
});
|
|
189918
|
+
const currentKeys = await proxyRegistry.getAllKeyRegistrations();
|
|
189919
|
+
proxy2.updateServices(currentServices, currentKeys);
|
|
189920
|
+
writeLog("system", `Loaded ${currentServices.length} services from registry`);
|
|
189921
|
+
} catch (err) {
|
|
189922
|
+
const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
|
|
189923
|
+
writeLog("system:error", errorMsg);
|
|
189924
|
+
setState((s) => ({
|
|
189925
|
+
...s,
|
|
189926
|
+
status: "error",
|
|
189927
|
+
error: errorMsg
|
|
189928
|
+
}));
|
|
189782
189929
|
return;
|
|
189783
189930
|
}
|
|
189784
|
-
|
|
189785
|
-
|
|
189786
|
-
|
|
189787
|
-
|
|
189788
|
-
|
|
189789
|
-
|
|
189931
|
+
} else {
|
|
189932
|
+
writeLog("system", "Another instance owns the proxy, starting election watcher");
|
|
189933
|
+
setState((s) => ({ ...s, isProxyOwner: false }));
|
|
189934
|
+
const isProcessRunning = (pid) => {
|
|
189935
|
+
try {
|
|
189936
|
+
process.kill(pid, 0);
|
|
189937
|
+
return true;
|
|
189938
|
+
} catch {
|
|
189939
|
+
return false;
|
|
189940
|
+
}
|
|
189941
|
+
};
|
|
189942
|
+
electionIntervalRef.current = setInterval(async () => {
|
|
189943
|
+
if (cancelled || shuttingDown.current) {
|
|
189790
189944
|
if (electionIntervalRef.current) {
|
|
189791
189945
|
clearInterval(electionIntervalRef.current);
|
|
189792
189946
|
electionIntervalRef.current = null;
|
|
189793
189947
|
}
|
|
189794
|
-
|
|
189795
|
-
|
|
189796
|
-
|
|
189797
|
-
|
|
189798
|
-
|
|
189799
|
-
|
|
189800
|
-
|
|
189801
|
-
|
|
189802
|
-
|
|
189803
|
-
|
|
189804
|
-
|
|
189805
|
-
|
|
189806
|
-
|
|
189807
|
-
|
|
189808
|
-
|
|
189809
|
-
|
|
189810
|
-
|
|
189811
|
-
|
|
189812
|
-
|
|
189813
|
-
|
|
189814
|
-
|
|
189948
|
+
return;
|
|
189949
|
+
}
|
|
189950
|
+
const owner = await proxyRegistry.getProxyOwner();
|
|
189951
|
+
if (!owner || !isProcessRunning(owner.pid)) {
|
|
189952
|
+
writeLog("system", "Proxy owner died, attempting election");
|
|
189953
|
+
const won = await proxyRegistry.attemptElection(instanceKey);
|
|
189954
|
+
if (won) {
|
|
189955
|
+
writeLog("system", "Won election, starting HTTP proxy");
|
|
189956
|
+
if (electionIntervalRef.current) {
|
|
189957
|
+
clearInterval(electionIntervalRef.current);
|
|
189958
|
+
electionIntervalRef.current = null;
|
|
189959
|
+
}
|
|
189960
|
+
try {
|
|
189961
|
+
const electionServices = await proxyRegistry.getAllServices();
|
|
189962
|
+
const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
|
|
189963
|
+
const electionKeyNames = Object.keys(electionKeyRegistrations);
|
|
189964
|
+
const certificate = generateCertificate("local.spcf.app", electionKeyNames);
|
|
189965
|
+
const proxy2 = await startHttpProxy(
|
|
189966
|
+
exposedServices,
|
|
189967
|
+
certificate,
|
|
189968
|
+
getState,
|
|
189969
|
+
instanceKey
|
|
189970
|
+
);
|
|
189971
|
+
startedProxy = proxy2;
|
|
189972
|
+
proxyRef.current = proxy2;
|
|
189973
|
+
setState((s) => ({ ...s, proxy: proxy2, isProxyOwner: true }));
|
|
189974
|
+
registryWatcherCleanupRef.current = proxyRegistry.watchRegistry((updatedServices, updatedKeys) => {
|
|
189975
|
+
writeLog("system", `Registry updated: ${updatedServices.length} services`);
|
|
189976
|
+
proxy2.updateServices(updatedServices, updatedKeys);
|
|
189977
|
+
});
|
|
189978
|
+
proxy2.updateServices(electionServices, electionKeyRegistrations);
|
|
189979
|
+
} catch (err) {
|
|
189980
|
+
writeLog("system:error", `Failed to start proxy after election: ${err}`);
|
|
189981
|
+
}
|
|
189815
189982
|
}
|
|
189816
189983
|
}
|
|
189817
|
-
}
|
|
189818
|
-
}
|
|
189984
|
+
}, 1e3);
|
|
189985
|
+
}
|
|
189819
189986
|
}
|
|
189820
189987
|
if (cancelled) return;
|
|
189821
189988
|
writeLog("system", "Dev server running");
|
|
@@ -189961,39 +190128,58 @@ Add them to the config block in specific.local`);
|
|
|
189961
190128
|
const staticItems = [
|
|
189962
190129
|
{
|
|
189963
190130
|
key: "title",
|
|
189964
|
-
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), instanceKey !== "default" && /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " [", instanceKey, "]"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)"))
|
|
190131
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), tunnelEnabled && /* @__PURE__ */ React6.createElement(Text6, { color: "magenta" }, " [tunnel]"), instanceKey !== "default" && /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, " [", instanceKey, "]"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)"))
|
|
189965
190132
|
},
|
|
189966
190133
|
{ key: "space1", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") },
|
|
189967
|
-
// Show admin UI URL
|
|
189968
|
-
|
|
189969
|
-
|
|
189970
|
-
|
|
189971
|
-
|
|
189972
|
-
|
|
189973
|
-
|
|
189974
|
-
|
|
189975
|
-
|
|
189976
|
-
|
|
189977
|
-
|
|
189978
|
-
|
|
189979
|
-
|
|
189980
|
-
|
|
189981
|
-
|
|
189982
|
-
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name), defaultPort && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", defaultPort, ")"))
|
|
189983
|
-
}];
|
|
189984
|
-
}
|
|
189985
|
-
return endpoints.map((endpoint) => {
|
|
189986
|
-
const port = svc.ports.get(endpoint.name);
|
|
189987
|
-
const displayName = endpoint.name === "default" ? svc.name : `${svc.name}:${endpoint.name}`;
|
|
189988
|
-
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
190134
|
+
// Show admin UI URL (only in non-tunnel mode)
|
|
190135
|
+
...!tunnelEnabled ? [
|
|
190136
|
+
{
|
|
190137
|
+
key: "admin",
|
|
190138
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "local.spcf.app"))
|
|
190139
|
+
},
|
|
190140
|
+
{ key: "admin-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
|
|
190141
|
+
] : [],
|
|
190142
|
+
// Services section - different rendering for tunnel mode vs local mode
|
|
190143
|
+
...tunnelEnabled ? (
|
|
190144
|
+
// Tunnel mode: show tunnel URLs for public services
|
|
190145
|
+
state.tunnels.size > 0 || state.tunnelStatus.size > 0 ? [
|
|
190146
|
+
{ key: "svc-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Services:") },
|
|
190147
|
+
...[...state.tunnelStatus.entries()].map(([serviceName, status]) => {
|
|
190148
|
+
const tunnel = state.tunnels.get(serviceName);
|
|
189989
190149
|
return {
|
|
189990
|
-
key: `svc-${
|
|
189991
|
-
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null,
|
|
190150
|
+
key: `svc-${serviceName}`,
|
|
190151
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: status === "connected" ? "green" : status === "error" ? "red" : "yellow" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, serviceName), status === "connected" && tunnel ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, tunnel.url)) : status === "connecting" ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " connecting...") : /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, " error"))
|
|
189992
190152
|
};
|
|
189993
|
-
})
|
|
189994
|
-
|
|
189995
|
-
|
|
189996
|
-
|
|
190153
|
+
}),
|
|
190154
|
+
{ key: "space2", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
|
|
190155
|
+
] : []
|
|
190156
|
+
) : (
|
|
190157
|
+
// Local mode: show local URLs
|
|
190158
|
+
services.length > 0 ? [
|
|
190159
|
+
{ key: "svc-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Services:") },
|
|
190160
|
+
...services.flatMap((svc) => {
|
|
190161
|
+
const serviceConfig = config.services.find((s) => s.name === svc.name);
|
|
190162
|
+
const endpoints = serviceConfig?.endpoints || [];
|
|
190163
|
+
if (endpoints.length === 0 && svc.ports.size > 0) {
|
|
190164
|
+
const defaultPort = svc.ports.get("default");
|
|
190165
|
+
return [{
|
|
190166
|
+
key: `svc-${svc.name}`,
|
|
190167
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name), defaultPort && /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", defaultPort, ")"))
|
|
190168
|
+
}];
|
|
190169
|
+
}
|
|
190170
|
+
return endpoints.map((endpoint) => {
|
|
190171
|
+
const port = svc.ports.get(endpoint.name);
|
|
190172
|
+
const displayName = endpoint.name === "default" ? svc.name : `${svc.name}:${endpoint.name}`;
|
|
190173
|
+
const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
|
|
190174
|
+
return {
|
|
190175
|
+
key: `svc-${svc.name}-${endpoint.name}`,
|
|
190176
|
+
content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".local.spcf.app"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
|
|
190177
|
+
};
|
|
190178
|
+
});
|
|
190179
|
+
}),
|
|
190180
|
+
{ key: "space2", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
|
|
190181
|
+
] : []
|
|
190182
|
+
),
|
|
189997
190183
|
...config.postgres.length > 0 ? [
|
|
189998
190184
|
{ key: "pg-header", content: /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Postgres:") },
|
|
189999
190185
|
...config.postgres.map((pg) => {
|
|
@@ -190041,13 +190227,13 @@ Add them to the config block in specific.local`);
|
|
|
190041
190227
|
];
|
|
190042
190228
|
return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React6.createElement(Box6, { key: item.key }, item.content)));
|
|
190043
190229
|
}
|
|
190044
|
-
function devCommand(instanceKey) {
|
|
190045
|
-
render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey }));
|
|
190230
|
+
function devCommand(instanceKey, tunnelEnabled = false) {
|
|
190231
|
+
render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey, tunnelEnabled }));
|
|
190046
190232
|
}
|
|
190047
190233
|
|
|
190048
190234
|
// src/lib/dev/git-worktree.ts
|
|
190049
190235
|
import { execSync as execSync2 } from "child_process";
|
|
190050
|
-
import * as
|
|
190236
|
+
import * as path18 from "path";
|
|
190051
190237
|
function isInWorktree() {
|
|
190052
190238
|
try {
|
|
190053
190239
|
const commonDir = execSync2("git rev-parse --git-common-dir", {
|
|
@@ -190058,8 +190244,8 @@ function isInWorktree() {
|
|
|
190058
190244
|
encoding: "utf-8",
|
|
190059
190245
|
stdio: ["pipe", "pipe", "pipe"]
|
|
190060
190246
|
}).trim();
|
|
190061
|
-
const resolvedCommonDir =
|
|
190062
|
-
const resolvedGitDir =
|
|
190247
|
+
const resolvedCommonDir = path18.resolve(commonDir);
|
|
190248
|
+
const resolvedGitDir = path18.resolve(gitDir);
|
|
190063
190249
|
return resolvedCommonDir !== resolvedGitDir;
|
|
190064
190250
|
} catch {
|
|
190065
190251
|
return false;
|
|
@@ -190074,7 +190260,7 @@ function getWorktreeName() {
|
|
|
190074
190260
|
encoding: "utf-8",
|
|
190075
190261
|
stdio: ["pipe", "pipe", "pipe"]
|
|
190076
190262
|
}).trim();
|
|
190077
|
-
return
|
|
190263
|
+
return path18.basename(gitDir);
|
|
190078
190264
|
} catch {
|
|
190079
190265
|
return null;
|
|
190080
190266
|
}
|
|
@@ -190089,35 +190275,35 @@ init_open();
|
|
|
190089
190275
|
import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
|
|
190090
190276
|
import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
|
|
190091
190277
|
import Spinner5 from "ink-spinner";
|
|
190092
|
-
import * as
|
|
190093
|
-
import * as
|
|
190278
|
+
import * as fs22 from "fs";
|
|
190279
|
+
import * as path20 from "path";
|
|
190094
190280
|
|
|
190095
190281
|
// src/lib/deploy/build-tester.ts
|
|
190096
190282
|
import { spawn as spawn5 } from "child_process";
|
|
190097
|
-
import { existsSync as
|
|
190098
|
-
import { join as
|
|
190283
|
+
import { existsSync as existsSync18 } from "fs";
|
|
190284
|
+
import { join as join19 } from "path";
|
|
190099
190285
|
function getDependencyInstallCommand(build, projectDir) {
|
|
190100
190286
|
switch (build.base) {
|
|
190101
190287
|
case "node":
|
|
190102
|
-
if (
|
|
190288
|
+
if (existsSync18(join19(projectDir, "pnpm-lock.yaml"))) {
|
|
190103
190289
|
return "pnpm install --frozen-lockfile";
|
|
190104
|
-
} else if (
|
|
190290
|
+
} else if (existsSync18(join19(projectDir, "yarn.lock"))) {
|
|
190105
190291
|
return "yarn install --frozen-lockfile";
|
|
190106
|
-
} else if (
|
|
190292
|
+
} else if (existsSync18(join19(projectDir, "package-lock.json"))) {
|
|
190107
190293
|
return "npm ci";
|
|
190108
190294
|
} else {
|
|
190109
190295
|
return "npm install";
|
|
190110
190296
|
}
|
|
190111
190297
|
case "python":
|
|
190112
|
-
if (
|
|
190298
|
+
if (existsSync18(join19(projectDir, "poetry.lock"))) {
|
|
190113
190299
|
return "poetry install --no-interaction";
|
|
190114
|
-
} else if (
|
|
190300
|
+
} else if (existsSync18(join19(projectDir, "Pipfile.lock"))) {
|
|
190115
190301
|
return "pipenv install --deploy";
|
|
190116
|
-
} else if (
|
|
190302
|
+
} else if (existsSync18(join19(projectDir, "Pipfile"))) {
|
|
190117
190303
|
return "pipenv install";
|
|
190118
|
-
} else if (
|
|
190304
|
+
} else if (existsSync18(join19(projectDir, "pyproject.toml"))) {
|
|
190119
190305
|
return "pip install .";
|
|
190120
|
-
} else if (
|
|
190306
|
+
} else if (existsSync18(join19(projectDir, "requirements.txt"))) {
|
|
190121
190307
|
return "pip install -r requirements.txt";
|
|
190122
190308
|
}
|
|
190123
190309
|
return null;
|
|
@@ -190263,8 +190449,8 @@ async function testAllBuilds(builds, projectDir) {
|
|
|
190263
190449
|
|
|
190264
190450
|
// src/lib/tarball/create.ts
|
|
190265
190451
|
import { execSync as execSync3 } from "child_process";
|
|
190266
|
-
import * as
|
|
190267
|
-
import * as
|
|
190452
|
+
import * as fs21 from "fs";
|
|
190453
|
+
import * as path19 from "path";
|
|
190268
190454
|
import { createTarPacker, createEntryItemGenerator } from "tar-vern";
|
|
190269
190455
|
function isInsideGitRepository(dir) {
|
|
190270
190456
|
try {
|
|
@@ -190321,10 +190507,10 @@ var EXCLUDED_DIRS = [
|
|
|
190321
190507
|
];
|
|
190322
190508
|
async function collectPaths(baseDir, currentDir, exclude) {
|
|
190323
190509
|
const results = [];
|
|
190324
|
-
const entries = await
|
|
190510
|
+
const entries = await fs21.promises.readdir(currentDir, { withFileTypes: true });
|
|
190325
190511
|
for (const entry of entries) {
|
|
190326
|
-
const fullPath =
|
|
190327
|
-
const relativePath =
|
|
190512
|
+
const fullPath = path19.join(currentDir, entry.name);
|
|
190513
|
+
const relativePath = path19.relative(baseDir, fullPath);
|
|
190328
190514
|
if (entry.isDirectory()) {
|
|
190329
190515
|
if (!exclude.includes(entry.name)) {
|
|
190330
190516
|
results.push(relativePath);
|
|
@@ -190339,8 +190525,8 @@ async function collectPaths(baseDir, currentDir, exclude) {
|
|
|
190339
190525
|
}
|
|
190340
190526
|
async function createTarArchive(projectDir) {
|
|
190341
190527
|
writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
|
|
190342
|
-
const configPath =
|
|
190343
|
-
if (!
|
|
190528
|
+
const configPath = path19.join(projectDir, "specific.hcl");
|
|
190529
|
+
if (!fs21.existsSync(configPath)) {
|
|
190344
190530
|
throw new Error("specific.hcl not found in project directory");
|
|
190345
190531
|
}
|
|
190346
190532
|
const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
|
|
@@ -190357,8 +190543,8 @@ async function createTarArchive(projectDir) {
|
|
|
190357
190543
|
}
|
|
190358
190544
|
function findWidestContext(projectDir, contexts) {
|
|
190359
190545
|
if (contexts.length === 0) return ".";
|
|
190360
|
-
const absolute = contexts.map((c) =>
|
|
190361
|
-
const segments = absolute.map((p) => p.split(
|
|
190546
|
+
const absolute = contexts.map((c) => path19.resolve(projectDir, c));
|
|
190547
|
+
const segments = absolute.map((p) => p.split(path19.sep).filter(Boolean));
|
|
190362
190548
|
const firstSegments = segments[0];
|
|
190363
190549
|
if (!firstSegments) return ".";
|
|
190364
190550
|
const minLen = Math.min(...segments.map((s) => s.length));
|
|
@@ -190372,12 +190558,12 @@ function findWidestContext(projectDir, contexts) {
|
|
|
190372
190558
|
}
|
|
190373
190559
|
}
|
|
190374
190560
|
const ancestorSegments = firstSegments.slice(0, commonLength);
|
|
190375
|
-
const ancestor =
|
|
190376
|
-
return
|
|
190561
|
+
const ancestor = path19.sep + ancestorSegments.join(path19.sep);
|
|
190562
|
+
return path19.relative(projectDir, ancestor) || ".";
|
|
190377
190563
|
}
|
|
190378
190564
|
async function createProjectTarball(projectDir, context = ".") {
|
|
190379
|
-
const contextDir =
|
|
190380
|
-
const appPath =
|
|
190565
|
+
const contextDir = path19.resolve(projectDir, context);
|
|
190566
|
+
const appPath = path19.relative(contextDir, projectDir) || ".";
|
|
190381
190567
|
writeLog("tarball", `Context: ${contextDir}, appPath: ${appPath}`);
|
|
190382
190568
|
let tarball;
|
|
190383
190569
|
if (isInsideGitRepository(contextDir)) {
|
|
@@ -191264,14 +191450,14 @@ ${errorMsg}`
|
|
|
191264
191450
|
), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error)), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
|
|
191265
191451
|
}
|
|
191266
191452
|
async function deployCommand(environment, options2) {
|
|
191267
|
-
const configPath =
|
|
191268
|
-
if (!
|
|
191453
|
+
const configPath = path20.join(process.cwd(), "specific.hcl");
|
|
191454
|
+
if (!fs22.existsSync(configPath)) {
|
|
191269
191455
|
console.error("Error: No specific.hcl found in current directory");
|
|
191270
191456
|
process.exit(1);
|
|
191271
191457
|
}
|
|
191272
191458
|
let config;
|
|
191273
191459
|
try {
|
|
191274
|
-
const hcl =
|
|
191460
|
+
const hcl = fs22.readFileSync(configPath, "utf-8");
|
|
191275
191461
|
config = await parseConfig(hcl);
|
|
191276
191462
|
} catch (err) {
|
|
191277
191463
|
console.error(
|
|
@@ -191295,8 +191481,8 @@ async function deployCommand(environment, options2) {
|
|
|
191295
191481
|
|
|
191296
191482
|
// src/commands/exec.tsx
|
|
191297
191483
|
import { spawn as spawn6 } from "child_process";
|
|
191298
|
-
import * as
|
|
191299
|
-
import * as
|
|
191484
|
+
import * as fs23 from "fs";
|
|
191485
|
+
import * as path21 from "path";
|
|
191300
191486
|
async function execCommand(serviceName, command, instanceKey = "default") {
|
|
191301
191487
|
if (command.length === 0) {
|
|
191302
191488
|
console.error(
|
|
@@ -191324,14 +191510,14 @@ async function execCommand(serviceName, command, instanceKey = "default") {
|
|
|
191324
191510
|
}
|
|
191325
191511
|
}
|
|
191326
191512
|
};
|
|
191327
|
-
const configPath =
|
|
191328
|
-
if (!
|
|
191513
|
+
const configPath = path21.join(process.cwd(), "specific.hcl");
|
|
191514
|
+
if (!fs23.existsSync(configPath)) {
|
|
191329
191515
|
console.error("Error: No specific.hcl found in current directory");
|
|
191330
191516
|
process.exit(1);
|
|
191331
191517
|
}
|
|
191332
191518
|
let config;
|
|
191333
191519
|
try {
|
|
191334
|
-
const hcl =
|
|
191520
|
+
const hcl = fs23.readFileSync(configPath, "utf-8");
|
|
191335
191521
|
config = await parseConfig(hcl);
|
|
191336
191522
|
} catch (err) {
|
|
191337
191523
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -191525,21 +191711,21 @@ async function psqlCommand(databaseName, instanceKey = "default") {
|
|
|
191525
191711
|
import React8, { useState as useState7, useEffect as useEffect5 } from "react";
|
|
191526
191712
|
import { render as render6, Text as Text8, Box as Box8 } from "ink";
|
|
191527
191713
|
import Spinner6 from "ink-spinner";
|
|
191528
|
-
import * as
|
|
191529
|
-
import * as
|
|
191714
|
+
import * as fs24 from "fs";
|
|
191715
|
+
import * as path22 from "path";
|
|
191530
191716
|
function CleanUI({ instanceKey }) {
|
|
191531
191717
|
const [state, setState] = useState7({ status: "checking" });
|
|
191532
191718
|
useEffect5(() => {
|
|
191533
191719
|
async function clean() {
|
|
191534
191720
|
const projectRoot = process.cwd();
|
|
191535
|
-
const specificDir =
|
|
191536
|
-
if (!
|
|
191721
|
+
const specificDir = path22.join(projectRoot, ".specific");
|
|
191722
|
+
if (!fs24.existsSync(specificDir)) {
|
|
191537
191723
|
setState({ status: "nothing" });
|
|
191538
191724
|
return;
|
|
191539
191725
|
}
|
|
191540
191726
|
if (instanceKey) {
|
|
191541
|
-
const keyDir =
|
|
191542
|
-
if (!
|
|
191727
|
+
const keyDir = path22.join(specificDir, "keys", instanceKey);
|
|
191728
|
+
if (!fs24.existsSync(keyDir)) {
|
|
191543
191729
|
setState({ status: "nothing" });
|
|
191544
191730
|
return;
|
|
191545
191731
|
}
|
|
@@ -191555,7 +191741,7 @@ function CleanUI({ instanceKey }) {
|
|
|
191555
191741
|
await stateManager.cleanStaleState();
|
|
191556
191742
|
setState({ status: "cleaning" });
|
|
191557
191743
|
try {
|
|
191558
|
-
|
|
191744
|
+
fs24.rmSync(keyDir, { recursive: true, force: true });
|
|
191559
191745
|
setState({ status: "success" });
|
|
191560
191746
|
} catch (err) {
|
|
191561
191747
|
setState({
|
|
@@ -191564,10 +191750,10 @@ function CleanUI({ instanceKey }) {
|
|
|
191564
191750
|
});
|
|
191565
191751
|
}
|
|
191566
191752
|
} else {
|
|
191567
|
-
const keysDir =
|
|
191568
|
-
if (
|
|
191569
|
-
const keys =
|
|
191570
|
-
(f) =>
|
|
191753
|
+
const keysDir = path22.join(specificDir, "keys");
|
|
191754
|
+
if (fs24.existsSync(keysDir)) {
|
|
191755
|
+
const keys = fs24.readdirSync(keysDir).filter(
|
|
191756
|
+
(f) => fs24.statSync(path22.join(keysDir, f)).isDirectory()
|
|
191571
191757
|
);
|
|
191572
191758
|
for (const key of keys) {
|
|
191573
191759
|
const stateManager2 = new InstanceStateManager(projectRoot, key);
|
|
@@ -191592,7 +191778,7 @@ function CleanUI({ instanceKey }) {
|
|
|
191592
191778
|
}
|
|
191593
191779
|
setState({ status: "cleaning" });
|
|
191594
191780
|
try {
|
|
191595
|
-
|
|
191781
|
+
fs24.rmSync(specificDir, { recursive: true, force: true });
|
|
191596
191782
|
setState({ status: "success" });
|
|
191597
191783
|
} catch (err) {
|
|
191598
191784
|
setState({
|
|
@@ -191679,13 +191865,13 @@ function logoutCommand() {
|
|
|
191679
191865
|
var program = new Command();
|
|
191680
191866
|
var env = "production";
|
|
191681
191867
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
191682
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
191868
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.48").enablePositionalOptions();
|
|
191683
191869
|
program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
|
|
191684
191870
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
191685
191871
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
|
191686
|
-
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) => {
|
|
191872
|
+
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)").option("--tunnel", "Expose public services via localtunnel URLs").action((options2) => {
|
|
191687
191873
|
const key = options2.key ?? getDefaultKey();
|
|
191688
|
-
devCommand(key);
|
|
191874
|
+
devCommand(key, options2.tunnel ?? false);
|
|
191689
191875
|
});
|
|
191690
191876
|
program.command("deploy [environment]").description("Deploy to Specific infrastructure").option("--skip-build-test", "Skip local build testing before deploy").action((environment, options2) => {
|
|
191691
191877
|
deployCommand(environment, options2);
|