@specific.dev/cli 0.1.58 → 0.1.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/404/index.html +1 -1
- package/dist/admin/404.html +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
- package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/__next._full.txt +1 -1
- package/dist/admin/__next._head.txt +1 -1
- package/dist/admin/__next._index.txt +1 -1
- package/dist/admin/__next._tree.txt +1 -1
- package/dist/admin/_not-found/__next._full.txt +1 -1
- package/dist/admin/_not-found/__next._head.txt +1 -1
- package/dist/admin/_not-found/__next._index.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/admin/_not-found/__next._not-found.txt +1 -1
- package/dist/admin/_not-found/__next._tree.txt +1 -1
- package/dist/admin/_not-found/index.html +1 -1
- package/dist/admin/_not-found/index.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
- package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
- package/dist/admin/databases/__next._full.txt +1 -1
- package/dist/admin/databases/__next._head.txt +1 -1
- package/dist/admin/databases/__next._index.txt +1 -1
- package/dist/admin/databases/__next._tree.txt +1 -1
- package/dist/admin/databases/index.html +1 -1
- package/dist/admin/databases/index.txt +1 -1
- package/dist/admin/fullscreen/__next._full.txt +1 -1
- package/dist/admin/fullscreen/__next._head.txt +1 -1
- package/dist/admin/fullscreen/__next._index.txt +1 -1
- package/dist/admin/fullscreen/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
- package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
- package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
- package/dist/admin/fullscreen/databases/index.html +1 -1
- package/dist/admin/fullscreen/databases/index.txt +1 -1
- package/dist/admin/fullscreen/index.html +1 -1
- package/dist/admin/fullscreen/index.txt +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/admin/index.txt +1 -1
- package/dist/cli.js +141 -23
- package/dist/postinstall.js +1 -1
- package/package.json +2 -3
- /package/dist/admin/_next/static/{vIye3POXFcvd-nt32Xq4j → tZ6oW5Gt46x7GjGGbdB6L}/_buildManifest.js +0 -0
- /package/dist/admin/_next/static/{vIye3POXFcvd-nt32Xq4j → tZ6oW5Gt46x7GjGGbdB6L}/_clientMiddlewareManifest.json +0 -0
- /package/dist/admin/_next/static/{vIye3POXFcvd-nt32Xq4j → tZ6oW5Gt46x7GjGGbdB6L}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -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.60",
|
|
183877
183877
|
platform: process.platform,
|
|
183878
183878
|
node_version: process.version,
|
|
183879
183879
|
project_id: getProjectId(),
|
|
@@ -189380,29 +189380,133 @@ var StableSubdomainAllocator = class {
|
|
|
189380
189380
|
}
|
|
189381
189381
|
};
|
|
189382
189382
|
|
|
189383
|
+
// node_modules/.pnpm/@specific+tunnel-client@file+..+tunnel+client/node_modules/@specific/tunnel-client/dist/index.js
|
|
189384
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
189385
|
+
import * as net4 from "node:net";
|
|
189386
|
+
var DEFAULT_HOST = "https://tunnel.spcf.app";
|
|
189387
|
+
async function register(baseUrl, subdomain) {
|
|
189388
|
+
const res = await fetch(`${baseUrl}/${subdomain}`);
|
|
189389
|
+
if (!res.ok) {
|
|
189390
|
+
const body = await res.text();
|
|
189391
|
+
throw new Error(`Registration failed (${res.status}): ${body}`);
|
|
189392
|
+
}
|
|
189393
|
+
return await res.json();
|
|
189394
|
+
}
|
|
189395
|
+
var TunnelClientImpl = class extends EventEmitter2 {
|
|
189396
|
+
info;
|
|
189397
|
+
localPort;
|
|
189398
|
+
tunnelHost;
|
|
189399
|
+
url;
|
|
189400
|
+
subdomain;
|
|
189401
|
+
pool = /* @__PURE__ */ new Set();
|
|
189402
|
+
closed = false;
|
|
189403
|
+
constructor(info, localPort, tunnelHost) {
|
|
189404
|
+
super();
|
|
189405
|
+
this.info = info;
|
|
189406
|
+
this.localPort = localPort;
|
|
189407
|
+
this.tunnelHost = tunnelHost;
|
|
189408
|
+
this.url = info.url;
|
|
189409
|
+
this.subdomain = info.id;
|
|
189410
|
+
this.fillPool();
|
|
189411
|
+
}
|
|
189412
|
+
close() {
|
|
189413
|
+
this.closed = true;
|
|
189414
|
+
for (const c of this.pool)
|
|
189415
|
+
c.destroy();
|
|
189416
|
+
this.pool.clear();
|
|
189417
|
+
}
|
|
189418
|
+
fillPool() {
|
|
189419
|
+
while (!this.closed && this.pool.size < this.info.max_conn_count) {
|
|
189420
|
+
this.addPoolConnection();
|
|
189421
|
+
}
|
|
189422
|
+
}
|
|
189423
|
+
addPoolConnection() {
|
|
189424
|
+
const remote = net4.connect({ host: this.tunnelHost, port: this.info.port });
|
|
189425
|
+
remote.setKeepAlive(true, 3e4);
|
|
189426
|
+
this.pool.add(remote);
|
|
189427
|
+
remote.once("data", (firstChunk) => {
|
|
189428
|
+
this.pool.delete(remote);
|
|
189429
|
+
this.pipeToLocal(remote, firstChunk);
|
|
189430
|
+
});
|
|
189431
|
+
let errored = false;
|
|
189432
|
+
remote.on("error", (err) => {
|
|
189433
|
+
errored = true;
|
|
189434
|
+
this.emit("error", err);
|
|
189435
|
+
});
|
|
189436
|
+
remote.on("close", () => this.onIdleClose(remote, errored));
|
|
189437
|
+
}
|
|
189438
|
+
pipeToLocal(remote, firstChunk) {
|
|
189439
|
+
const local = net4.connect({ host: "127.0.0.1", port: this.localPort }, () => {
|
|
189440
|
+
local.write(firstChunk);
|
|
189441
|
+
remote.pipe(local);
|
|
189442
|
+
local.pipe(remote);
|
|
189443
|
+
});
|
|
189444
|
+
local.on("error", () => remote.destroy());
|
|
189445
|
+
remote.on("close", () => {
|
|
189446
|
+
if (!this.closed)
|
|
189447
|
+
this.fillPool();
|
|
189448
|
+
});
|
|
189449
|
+
}
|
|
189450
|
+
onIdleClose(remote, errored) {
|
|
189451
|
+
if (!this.pool.delete(remote) || this.closed)
|
|
189452
|
+
return;
|
|
189453
|
+
if (errored) {
|
|
189454
|
+
if (this.pool.size === 0)
|
|
189455
|
+
this.emit("close");
|
|
189456
|
+
} else {
|
|
189457
|
+
this.fillPool();
|
|
189458
|
+
}
|
|
189459
|
+
}
|
|
189460
|
+
};
|
|
189461
|
+
async function connect2(options2) {
|
|
189462
|
+
const host = options2.host ?? DEFAULT_HOST;
|
|
189463
|
+
const tunnelHost = new URL(host).hostname;
|
|
189464
|
+
const info = await register(host, options2.subdomain);
|
|
189465
|
+
return new TunnelClientImpl(info, options2.port, tunnelHost);
|
|
189466
|
+
}
|
|
189467
|
+
|
|
189383
189468
|
// src/lib/dev/tunnel-manager.ts
|
|
189384
|
-
import localtunnel from "localtunnel";
|
|
189385
189469
|
var TUNNEL_HOST = "https://tunnel.spcf.app";
|
|
189386
189470
|
async function startTunnel(serviceName, endpointName, port, subdomain, callbacks) {
|
|
189387
|
-
|
|
189388
|
-
|
|
189389
|
-
|
|
189390
|
-
|
|
189391
|
-
|
|
189392
|
-
|
|
189393
|
-
|
|
189394
|
-
|
|
189395
|
-
|
|
189396
|
-
|
|
189397
|
-
|
|
189471
|
+
let currentTunnel = null;
|
|
189472
|
+
let stopped = false;
|
|
189473
|
+
function setupTunnel(tunnel) {
|
|
189474
|
+
tunnel.on("error", (err) => {
|
|
189475
|
+
callbacks?.onError?.(serviceName, endpointName, err);
|
|
189476
|
+
});
|
|
189477
|
+
tunnel.on("close", () => {
|
|
189478
|
+
if (!stopped) {
|
|
189479
|
+
callbacks?.onClose?.(serviceName, endpointName);
|
|
189480
|
+
reconnect();
|
|
189481
|
+
}
|
|
189482
|
+
});
|
|
189483
|
+
}
|
|
189484
|
+
async function reconnect() {
|
|
189485
|
+
const delays = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
189486
|
+
for (let attempt = 0; !stopped; attempt++) {
|
|
189487
|
+
const delay = delays[Math.min(attempt, delays.length - 1)];
|
|
189488
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
189489
|
+
if (stopped) return;
|
|
189490
|
+
try {
|
|
189491
|
+
currentTunnel = await connect2({ port, subdomain, host: TUNNEL_HOST });
|
|
189492
|
+
setupTunnel(currentTunnel);
|
|
189493
|
+
callbacks?.onReconnect?.(serviceName, endpointName);
|
|
189494
|
+
return;
|
|
189495
|
+
} catch {
|
|
189496
|
+
}
|
|
189497
|
+
}
|
|
189498
|
+
}
|
|
189499
|
+
currentTunnel = await connect2({ port, subdomain, host: TUNNEL_HOST });
|
|
189500
|
+
setupTunnel(currentTunnel);
|
|
189398
189501
|
return {
|
|
189399
189502
|
serviceName,
|
|
189400
189503
|
endpointName,
|
|
189401
189504
|
localPort: port,
|
|
189402
|
-
url:
|
|
189505
|
+
url: currentTunnel.url,
|
|
189403
189506
|
subdomain,
|
|
189404
189507
|
stop: async () => {
|
|
189405
|
-
|
|
189508
|
+
stopped = true;
|
|
189509
|
+
currentTunnel?.close();
|
|
189406
189510
|
}
|
|
189407
189511
|
};
|
|
189408
189512
|
}
|
|
@@ -189411,7 +189515,7 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
|
|
|
189411
189515
|
import * as fs20 from "fs";
|
|
189412
189516
|
import * as path17 from "path";
|
|
189413
189517
|
import * as os7 from "os";
|
|
189414
|
-
import * as
|
|
189518
|
+
import * as net5 from "net";
|
|
189415
189519
|
var ProxyRegistryManager = class {
|
|
189416
189520
|
proxyDir;
|
|
189417
189521
|
ownerPath;
|
|
@@ -189445,7 +189549,7 @@ var ProxyRegistryManager = class {
|
|
|
189445
189549
|
*/
|
|
189446
189550
|
isProxyListening(port, timeoutMs = 1e3) {
|
|
189447
189551
|
return new Promise((resolve10) => {
|
|
189448
|
-
const socket = new
|
|
189552
|
+
const socket = new net5.Socket();
|
|
189449
189553
|
let resolved = false;
|
|
189450
189554
|
const cleanup = () => {
|
|
189451
189555
|
if (!resolved) {
|
|
@@ -189997,9 +190101,6 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
189997
190101
|
// Stop all tunnels
|
|
189998
190102
|
...tunnelsRef.current.map((tunnel) => tunnel.stop())
|
|
189999
190103
|
]);
|
|
190000
|
-
if (tunnelsRef.current.length > 0) {
|
|
190001
|
-
await new Promise((resolve10) => setTimeout(resolve10, 1500));
|
|
190002
|
-
}
|
|
190003
190104
|
electricInstancesRef.current = [];
|
|
190004
190105
|
reshapeWatchersRef.current = [];
|
|
190005
190106
|
restartServicesRef.current = null;
|
|
@@ -190039,7 +190140,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
190039
190140
|
...servicesRef.current,
|
|
190040
190141
|
...electricInstancesRef.current,
|
|
190041
190142
|
drizzleGatewayRef.current,
|
|
190042
|
-
...[...resourcesRef.current.values()]
|
|
190143
|
+
...[...resourcesRef.current.values()],
|
|
190144
|
+
...tunnelsRef.current
|
|
190043
190145
|
].filter(Boolean);
|
|
190044
190146
|
for (const proc of allProcesses) {
|
|
190045
190147
|
try {
|
|
@@ -190052,11 +190154,20 @@ function DevUI({ instanceKey, tunnelEnabled }) {
|
|
|
190052
190154
|
}
|
|
190053
190155
|
shutdown2();
|
|
190054
190156
|
};
|
|
190157
|
+
const handleCrash = (err) => {
|
|
190158
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
190159
|
+
writeLog("system:error", `Uncaught error: ${msg}`);
|
|
190160
|
+
shutdown2();
|
|
190161
|
+
};
|
|
190055
190162
|
process.on("SIGINT", handleSignal);
|
|
190056
190163
|
process.on("SIGTERM", handleSignal);
|
|
190164
|
+
process.on("uncaughtException", handleCrash);
|
|
190165
|
+
process.on("unhandledRejection", handleCrash);
|
|
190057
190166
|
return () => {
|
|
190058
190167
|
process.off("SIGINT", handleSignal);
|
|
190059
190168
|
process.off("SIGTERM", handleSignal);
|
|
190169
|
+
process.off("uncaughtException", handleCrash);
|
|
190170
|
+
process.off("unhandledRejection", handleCrash);
|
|
190060
190171
|
};
|
|
190061
190172
|
}, []);
|
|
190062
190173
|
useEffect3(() => {
|
|
@@ -190631,7 +190742,14 @@ Add them to the config block in specific.local`);
|
|
|
190631
190742
|
writeLog("system:error", `Tunnel error for ${serviceName}: ${error.message}`);
|
|
190632
190743
|
},
|
|
190633
190744
|
onClose: (serviceName) => {
|
|
190634
|
-
writeLog("system", `Tunnel closed for ${serviceName}
|
|
190745
|
+
writeLog("system", `Tunnel closed for ${serviceName}, reconnecting...`);
|
|
190746
|
+
tunnelStatusMap.set(serviceName, "connecting");
|
|
190747
|
+
setState((s) => ({ ...s, tunnelStatus: new Map(tunnelStatusMap) }));
|
|
190748
|
+
},
|
|
190749
|
+
onReconnect: (serviceName) => {
|
|
190750
|
+
writeLog("system", `Tunnel reconnected for ${serviceName}`);
|
|
190751
|
+
tunnelStatusMap.set(serviceName, "connected");
|
|
190752
|
+
setState((s) => ({ ...s, tunnelStatus: new Map(tunnelStatusMap) }));
|
|
190635
190753
|
}
|
|
190636
190754
|
}
|
|
190637
190755
|
);
|
|
@@ -193065,7 +193183,7 @@ function betaCommand() {
|
|
|
193065
193183
|
var program = new Command();
|
|
193066
193184
|
var env = "production";
|
|
193067
193185
|
var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
|
|
193068
|
-
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.
|
|
193186
|
+
program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.60").enablePositionalOptions();
|
|
193069
193187
|
program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").action((options2) => initCommand(options2));
|
|
193070
193188
|
program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
|
|
193071
193189
|
program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
|
package/dist/postinstall.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specific.dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.60",
|
|
4
4
|
"description": "CLI for Specific infrastructure-as-code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
"http-proxy": "^1.18.1",
|
|
42
42
|
"ink": "^6.5.1",
|
|
43
43
|
"ink-spinner": "^5.0.0",
|
|
44
|
-
"localtunnel": "^2.0.2",
|
|
45
44
|
"node-forge": "^1.3.1",
|
|
46
45
|
"open": "^11.0.0",
|
|
47
46
|
"posthog-node": "^5.24.1",
|
|
@@ -52,8 +51,8 @@
|
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
54
53
|
"@specific/config": "file:../config",
|
|
54
|
+
"@specific/tunnel-client": "file:../tunnel/client",
|
|
55
55
|
"@types/http-proxy": "^1.17.17",
|
|
56
|
-
"@types/localtunnel": "^2.0.4",
|
|
57
56
|
"@types/node": "^25.0.1",
|
|
58
57
|
"@types/node-forge": "^1.3.11",
|
|
59
58
|
"@types/react": "^19.2.7",
|
/package/dist/admin/_next/static/{vIye3POXFcvd-nt32Xq4j → tZ6oW5Gt46x7GjGGbdB6L}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/dist/admin/_next/static/{vIye3POXFcvd-nt32Xq4j → tZ6oW5Gt46x7GjGGbdB6L}/_ssgManifest.js
RENAMED
|
File without changes
|