@specific.dev/cli 0.1.59 → 0.1.61

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.
Files changed (50) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +1 -1
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
  5. package/dist/admin/__next._full.txt +1 -1
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +1 -1
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_not-found/__next._full.txt +1 -1
  10. package/dist/admin/_not-found/__next._head.txt +1 -1
  11. package/dist/admin/_not-found/__next._index.txt +1 -1
  12. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  13. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  14. package/dist/admin/_not-found/__next._tree.txt +1 -1
  15. package/dist/admin/_not-found/index.html +1 -1
  16. package/dist/admin/_not-found/index.txt +1 -1
  17. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
  18. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
  20. package/dist/admin/databases/__next._full.txt +1 -1
  21. package/dist/admin/databases/__next._head.txt +1 -1
  22. package/dist/admin/databases/__next._index.txt +1 -1
  23. package/dist/admin/databases/__next._tree.txt +1 -1
  24. package/dist/admin/databases/index.html +1 -1
  25. package/dist/admin/databases/index.txt +1 -1
  26. package/dist/admin/fullscreen/__next._full.txt +1 -1
  27. package/dist/admin/fullscreen/__next._head.txt +1 -1
  28. package/dist/admin/fullscreen/__next._index.txt +1 -1
  29. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  30. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
  31. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  32. package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
  33. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
  35. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  39. package/dist/admin/fullscreen/databases/index.html +1 -1
  40. package/dist/admin/fullscreen/databases/index.txt +1 -1
  41. package/dist/admin/fullscreen/index.html +1 -1
  42. package/dist/admin/fullscreen/index.txt +1 -1
  43. package/dist/admin/index.html +1 -1
  44. package/dist/admin/index.txt +1 -1
  45. package/dist/cli.js +105 -36
  46. package/dist/postinstall.js +1 -1
  47. package/package.json +2 -3
  48. /package/dist/admin/_next/static/{3NwOLK8EcVipT46jmh2HP → ko53a3ptYl472sHJWxbir}/_buildManifest.js +0 -0
  49. /package/dist/admin/_next/static/{3NwOLK8EcVipT46jmh2HP → ko53a3ptYl472sHJWxbir}/_clientMiddlewareManifest.json +0 -0
  50. /package/dist/admin/_next/static/{3NwOLK8EcVipT46jmh2HP → ko53a3ptYl472sHJWxbir}/_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.59",
183876
+ cli_version: "0.1.61",
183877
183877
  platform: process.platform,
183878
183878
  node_version: process.version,
183879
183879
  project_id: getProjectId(),
@@ -189380,54 +189380,123 @@ var StableSubdomainAllocator = class {
189380
189380
  }
189381
189381
  };
189382
189382
 
189383
- // src/lib/dev/tunnel-manager.ts
189384
- import localtunnel from "localtunnel";
189385
- var TUNNEL_HOST = "https://tunnel.spcf.app";
189386
- async function startTunnel(serviceName, endpointName, port, subdomain, callbacks) {
189387
- let currentTunnel = null;
189388
- let stopped = false;
189389
- async function connect() {
189390
- const tunnel = await localtunnel({
189391
- port,
189392
- subdomain,
189393
- host: TUNNEL_HOST
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
+ var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 15e3];
189388
+ async function register(baseUrl, subdomain) {
189389
+ const res = await fetch(`${baseUrl}/${subdomain}`);
189390
+ if (!res.ok) {
189391
+ const body = await res.text();
189392
+ throw new Error(`Registration failed (${res.status}): ${body}`);
189393
+ }
189394
+ return await res.json();
189395
+ }
189396
+ var TunnelClientImpl = class extends EventEmitter2 {
189397
+ info;
189398
+ localPort;
189399
+ tunnelHost;
189400
+ baseUrl;
189401
+ url;
189402
+ subdomain;
189403
+ pool = /* @__PURE__ */ new Set();
189404
+ closed = false;
189405
+ constructor(info, localPort, tunnelHost, baseUrl) {
189406
+ super();
189407
+ this.info = info;
189408
+ this.localPort = localPort;
189409
+ this.tunnelHost = tunnelHost;
189410
+ this.baseUrl = baseUrl;
189411
+ this.url = info.url;
189412
+ this.subdomain = info.id;
189413
+ this.fillPool();
189414
+ }
189415
+ close() {
189416
+ this.closed = true;
189417
+ for (const c of this.pool)
189418
+ c.destroy();
189419
+ this.pool.clear();
189420
+ }
189421
+ fillPool() {
189422
+ while (!this.closed && this.pool.size < this.info.max_conn_count) {
189423
+ this.addPoolConnection();
189424
+ }
189425
+ }
189426
+ addPoolConnection() {
189427
+ const remote = net4.connect({ host: this.tunnelHost, port: this.info.port });
189428
+ remote.setKeepAlive(true, 3e4);
189429
+ this.pool.add(remote);
189430
+ remote.once("data", (firstChunk) => {
189431
+ this.pool.delete(remote);
189432
+ this.pipeToLocal(remote, firstChunk);
189394
189433
  });
189395
- tunnel.on("error", (err) => {
189396
- callbacks?.onError?.(serviceName, endpointName, err);
189434
+ let errored = false;
189435
+ remote.on("error", (err) => {
189436
+ errored = true;
189437
+ this.emit("error", err);
189397
189438
  });
189398
- tunnel.on("close", () => {
189399
- if (!stopped) {
189400
- callbacks?.onClose?.(serviceName, endpointName);
189401
- reconnect();
189402
- }
189439
+ remote.on("close", () => this.onIdleClose(remote, errored));
189440
+ }
189441
+ pipeToLocal(remote, firstChunk) {
189442
+ const local = net4.connect({ host: "127.0.0.1", port: this.localPort }, () => {
189443
+ local.write(firstChunk);
189444
+ remote.pipe(local);
189445
+ local.pipe(remote);
189446
+ });
189447
+ local.on("error", () => remote.destroy());
189448
+ remote.on("close", () => {
189449
+ if (!this.closed)
189450
+ this.fillPool();
189403
189451
  });
189404
- return tunnel;
189405
189452
  }
189406
- async function reconnect() {
189407
- const delays = [1e3, 2e3, 4e3, 8e3, 15e3];
189408
- for (let attempt = 0; !stopped; attempt++) {
189409
- const delay = delays[Math.min(attempt, delays.length - 1)];
189453
+ onIdleClose(remote, errored) {
189454
+ if (!this.pool.delete(remote) || this.closed)
189455
+ return;
189456
+ if (errored) {
189457
+ if (this.pool.size === 0)
189458
+ this.reconnect();
189459
+ } else {
189460
+ this.fillPool();
189461
+ }
189462
+ }
189463
+ async reconnect() {
189464
+ this.emit("close");
189465
+ for (let attempt = 0; !this.closed; attempt++) {
189466
+ const delay = RECONNECT_DELAYS[Math.min(attempt, RECONNECT_DELAYS.length - 1)];
189410
189467
  await new Promise((r) => setTimeout(r, delay));
189411
- if (stopped) return;
189468
+ if (this.closed)
189469
+ return;
189412
189470
  try {
189413
- currentTunnel = await connect();
189414
- callbacks?.onReconnect?.(serviceName, endpointName);
189471
+ this.info = await register(this.baseUrl, this.subdomain);
189472
+ this.fillPool();
189473
+ this.emit("reconnect");
189415
189474
  return;
189416
189475
  } catch {
189417
189476
  }
189418
189477
  }
189419
189478
  }
189420
- currentTunnel = await connect();
189479
+ };
189480
+ async function connect2(options2) {
189481
+ const host = options2.host ?? DEFAULT_HOST;
189482
+ const tunnelHost = new URL(host).hostname;
189483
+ const info = await register(host, options2.subdomain);
189484
+ return new TunnelClientImpl(info, options2.port, tunnelHost, host);
189485
+ }
189486
+
189487
+ // src/lib/dev/tunnel-manager.ts
189488
+ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks) {
189489
+ const tunnel = await connect2({ port, subdomain });
189490
+ tunnel.on("error", (err) => callbacks?.onError?.(serviceName, endpointName, err));
189491
+ tunnel.on("close", () => callbacks?.onClose?.(serviceName, endpointName));
189492
+ tunnel.on("reconnect", () => callbacks?.onReconnect?.(serviceName, endpointName));
189421
189493
  return {
189422
189494
  serviceName,
189423
189495
  endpointName,
189424
189496
  localPort: port,
189425
- url: currentTunnel.url,
189497
+ url: tunnel.url,
189426
189498
  subdomain,
189427
- stop: async () => {
189428
- stopped = true;
189429
- currentTunnel?.close();
189430
- }
189499
+ stop: async () => tunnel.close()
189431
189500
  };
189432
189501
  }
189433
189502
 
@@ -189435,7 +189504,7 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
189435
189504
  import * as fs20 from "fs";
189436
189505
  import * as path17 from "path";
189437
189506
  import * as os7 from "os";
189438
- import * as net4 from "net";
189507
+ import * as net5 from "net";
189439
189508
  var ProxyRegistryManager = class {
189440
189509
  proxyDir;
189441
189510
  ownerPath;
@@ -189469,7 +189538,7 @@ var ProxyRegistryManager = class {
189469
189538
  */
189470
189539
  isProxyListening(port, timeoutMs = 1e3) {
189471
189540
  return new Promise((resolve10) => {
189472
- const socket = new net4.Socket();
189541
+ const socket = new net5.Socket();
189473
189542
  let resolved = false;
189474
189543
  const cleanup = () => {
189475
189544
  if (!resolved) {
@@ -193103,7 +193172,7 @@ function betaCommand() {
193103
193172
  var program = new Command();
193104
193173
  var env = "production";
193105
193174
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
193106
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.59").enablePositionalOptions();
193175
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.61").enablePositionalOptions();
193107
193176
  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));
193108
193177
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
193109
193178
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
@@ -111,7 +111,7 @@ function trackEvent(event, properties) {
111
111
  event,
112
112
  properties: {
113
113
  ...properties,
114
- cli_version: "0.1.59",
114
+ cli_version: "0.1.61",
115
115
  platform: process.platform,
116
116
  node_version: process.version,
117
117
  project_id: getProjectId(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
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",