@specific.dev/cli 0.1.48 → 0.1.49

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 (34) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.__PAGE__.txt +1 -1
  4. package/dist/admin/__next._full.txt +1 -1
  5. package/dist/admin/__next._head.txt +1 -1
  6. package/dist/admin/__next._index.txt +1 -1
  7. package/dist/admin/__next._tree.txt +1 -1
  8. package/dist/admin/_not-found/__next._full.txt +1 -1
  9. package/dist/admin/_not-found/__next._head.txt +1 -1
  10. package/dist/admin/_not-found/__next._index.txt +1 -1
  11. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  12. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  13. package/dist/admin/_not-found/__next._tree.txt +1 -1
  14. package/dist/admin/_not-found/index.html +1 -1
  15. package/dist/admin/_not-found/index.txt +1 -1
  16. package/dist/admin/databases/__next._full.txt +1 -1
  17. package/dist/admin/databases/__next._head.txt +1 -1
  18. package/dist/admin/databases/__next._index.txt +1 -1
  19. package/dist/admin/databases/__next._tree.txt +1 -1
  20. package/dist/admin/databases/__next.databases.__PAGE__.txt +1 -1
  21. package/dist/admin/databases/__next.databases.txt +1 -1
  22. package/dist/admin/databases/index.html +1 -1
  23. package/dist/admin/databases/index.txt +1 -1
  24. package/dist/admin/index.html +1 -1
  25. package/dist/admin/index.txt +1 -1
  26. package/dist/cli.js +474 -248
  27. package/dist/docs/builds.md +22 -0
  28. package/dist/docs/postgres.md +15 -0
  29. package/dist/docs/services.md +62 -8
  30. package/dist/postinstall.js +141 -0
  31. package/package.json +3 -2
  32. /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_buildManifest.js +0 -0
  33. /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_clientMiddlewareManifest.json +0 -0
  34. /package/dist/admin/_next/static/{dyH4SZNKyN31L1iV-yPZA → FMaKxl-Dpw5U-PgHqIMag}/_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 (path23) => {
242
- if (/^[a-z]+:\/\//i.test(path23)) {
243
- return path23;
241
+ convertWslPathToWindows = async (path24) => {
242
+ if (/^[a-z]+:\/\//i.test(path24)) {
243
+ return path24;
244
244
  }
245
245
  try {
246
- const { stdout } = await execFile2("wslpath", ["-aw", path23], { encoding: "utf8" });
246
+ const { stdout } = await execFile2("wslpath", ["-aw", path24], { encoding: "utf8" });
247
247
  return stdout.trim();
248
248
  } catch {
249
- return path23;
249
+ return path24;
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 fs25 = $global.require("fs");
758
- "object" == typeof fs25 && null !== fs25 && 0 !== Object.keys(fs25).length && ($global.fs = fs25);
757
+ var fs26 = $global.require("fs");
758
+ "object" == typeof fs26 && null !== fs26 && 0 !== Object.keys(fs26).length && ($global.fs = fs26);
759
759
  } catch (e) {
760
760
  }
761
761
  if (!$global.fs) {
@@ -183055,13 +183055,13 @@ function removeCA() {
183055
183055
  }
183056
183056
  }
183057
183057
  function installCAToTrustStore(certPath) {
183058
- const platform5 = os.platform();
183059
- if (platform5 === "darwin") {
183058
+ const platform4 = os.platform();
183059
+ if (platform4 === "darwin") {
183060
183060
  execSync(
183061
183061
  `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`,
183062
183062
  { stdio: "inherit" }
183063
183063
  );
183064
- } else if (platform5 === "linux") {
183064
+ } else if (platform4 === "linux") {
183065
183065
  if (fs.existsSync("/usr/local/share/ca-certificates")) {
183066
183066
  execSync(
183067
183067
  `sudo cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
@@ -183078,7 +183078,7 @@ function installCAToTrustStore(certPath) {
183078
183078
  throw new Error("Could not detect Linux certificate trust mechanism");
183079
183079
  }
183080
183080
  } else {
183081
- throw new Error(`Unsupported platform: ${platform5}`);
183081
+ throw new Error(`Unsupported platform: ${platform4}`);
183082
183082
  }
183083
183083
  }
183084
183084
  function generateCertificate(domain, keys = []) {
@@ -183873,7 +183873,7 @@ function trackEvent(event, properties) {
183873
183873
  event,
183874
183874
  properties: {
183875
183875
  ...properties,
183876
- cli_version: "0.1.48",
183876
+ cli_version: "0.1.49",
183877
183877
  platform: process.platform,
183878
183878
  node_version: process.version,
183879
183879
  project_id: getProjectId(),
@@ -183888,6 +183888,11 @@ async function shutdown() {
183888
183888
  }
183889
183889
  }
183890
183890
 
183891
+ // src/lib/ui/interactive.ts
183892
+ function isInteractive() {
183893
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
183894
+ }
183895
+
183891
183896
  // src/commands/init.tsx
183892
183897
  var SPECIFIC_INSTRUCTIONS = `Use Specific to handle all infrastructure and development environments: like deployments, databases, real-time sync, services, secrets, configuration and environment variables. Run \`specific docs\` to learn how to use Specific. ALWAYS run \`specific check\` after making changes to the Specific configuration.`;
183893
183898
  var CURSOR_MDC_CONTENT = `---
@@ -184156,8 +184161,77 @@ function InitUI() {
184156
184161
  " "
184157
184162
  ))));
184158
184163
  }
184159
- function initCommand() {
184160
- render2(/* @__PURE__ */ React2.createElement(InitUI, null));
184164
+ var VALID_AGENT_IDS = options.map((o) => o.id);
184165
+ function initCommand(opts) {
184166
+ if (isInteractive()) {
184167
+ render2(/* @__PURE__ */ React2.createElement(InitUI, null));
184168
+ return;
184169
+ }
184170
+ if (!opts.agent || opts.agent.length === 0) {
184171
+ console.error(
184172
+ `Error: --agent is required in non-interactive environments.
184173
+ Usage: specific init --agent cursor claude codex
184174
+ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
184175
+ );
184176
+ process.exit(1);
184177
+ }
184178
+ const invalid = opts.agent.filter((a) => !VALID_AGENT_IDS.includes(a));
184179
+ if (invalid.length > 0) {
184180
+ console.error(
184181
+ `Error: Unknown agent(s): ${invalid.join(", ")}
184182
+ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
184183
+ );
184184
+ process.exit(1);
184185
+ }
184186
+ const checked = {};
184187
+ for (const agent of opts.agent) {
184188
+ checked[agent] = true;
184189
+ }
184190
+ const result = configureAgents(checked);
184191
+ trackEvent("project_initialized", {
184192
+ agents: opts.agent
184193
+ });
184194
+ const selectedAgents = opts.agent.length;
184195
+ const agentChanges = result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0;
184196
+ console.log("\u2713 Coding agents configured");
184197
+ if (result.agents.filesCreated.length > 0) {
184198
+ console.log(` Created: ${result.agents.filesCreated.join(", ")}`);
184199
+ }
184200
+ if (result.agents.filesModified.length > 0) {
184201
+ console.log(` Modified: ${result.agents.filesModified.join(", ")}`);
184202
+ }
184203
+ if (!agentChanges && selectedAgents > 0) {
184204
+ console.log(" No changes needed (files already configured)");
184205
+ }
184206
+ if (result.git) {
184207
+ const gitChanges = result.git.filesCreated.length > 0 || result.git.filesModified.length > 0;
184208
+ console.log("\u2713 Git configured");
184209
+ if (result.git.filesCreated.length > 0) {
184210
+ console.log(` Created: ${result.git.filesCreated.join(", ")}`);
184211
+ }
184212
+ if (result.git.filesModified.length > 0) {
184213
+ console.log(` Modified: ${result.git.filesModified.join(", ")}`);
184214
+ }
184215
+ if (!gitChanges) {
184216
+ console.log(" No changes needed (.gitignore already configured)");
184217
+ }
184218
+ }
184219
+ if (!caFilesExist()) {
184220
+ console.log(
184221
+ "\u26A0 TLS certificates not installed (run `specific init` in a terminal to set up)"
184222
+ );
184223
+ }
184224
+ if (result.showManualInstructions) {
184225
+ console.log("\n\u2713 Manual configuration selected");
184226
+ console.log("\n Add this to your agent's system prompt:");
184227
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
184228
+ console.log(` ${SPECIFIC_INSTRUCTIONS}`);
184229
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
184230
+ console.log(
184231
+ "\n We also recommend allowing your agent to run `specific docs *`"
184232
+ );
184233
+ console.log(" and `specific check *` without confirmation.");
184234
+ }
184161
184235
  }
184162
184236
 
184163
184237
  // src/commands/docs.tsx
@@ -184166,11 +184240,11 @@ import { join as join6, dirname as dirname2 } from "path";
184166
184240
  import { fileURLToPath as fileURLToPath2 } from "url";
184167
184241
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
184168
184242
  var docsDir = join6(__dirname2, "docs");
184169
- function docsCommand(path23) {
184170
- const docPath = resolveDocPath(path23);
184243
+ function docsCommand(path24) {
184244
+ const docPath = resolveDocPath(path24);
184171
184245
  if (!docPath) {
184172
184246
  console.error(
184173
- `Documentation not found: ${path23 || "index"}
184247
+ `Documentation not found: ${path24 || "index"}
184174
184248
 
184175
184249
  Run 'specific docs' to see available topics.`
184176
184250
  );
@@ -184179,16 +184253,16 @@ Run 'specific docs' to see available topics.`
184179
184253
  const content = readFileSync5(docPath, "utf-8");
184180
184254
  console.log(content);
184181
184255
  }
184182
- function resolveDocPath(path23) {
184183
- if (!path23) {
184256
+ function resolveDocPath(path24) {
184257
+ if (!path24) {
184184
184258
  const indexPath2 = join6(docsDir, "index.md");
184185
184259
  return existsSync5(indexPath2) ? indexPath2 : null;
184186
184260
  }
184187
- const directPath = join6(docsDir, `${path23}.md`);
184261
+ const directPath = join6(docsDir, `${path24}.md`);
184188
184262
  if (existsSync5(directPath)) {
184189
184263
  return directPath;
184190
184264
  }
184191
- const indexPath = join6(docsDir, path23, "index.md");
184265
+ const indexPath = join6(docsDir, path24, "index.md");
184192
184266
  if (existsSync5(indexPath)) {
184193
184267
  return indexPath;
184194
184268
  }
@@ -184283,7 +184357,7 @@ function parseReferenceString(str) {
184283
184357
  attribute: "port"
184284
184358
  };
184285
184359
  }
184286
- const serviceEndpointMatch = str.match(/^service\.(\w+)\.endpoint\.(\w+)\.(url|host|port)$/);
184360
+ const serviceEndpointMatch = str.match(/^service\.(\w+)\.endpoint\.(\w+)\.(url|private_url|host|port|public_url)$/);
184287
184361
  if (serviceEndpointMatch && serviceEndpointMatch[1] && serviceEndpointMatch[2] && serviceEndpointMatch[3]) {
184288
184362
  return {
184289
184363
  type: "service",
@@ -184292,7 +184366,7 @@ function parseReferenceString(str) {
184292
184366
  attribute: serviceEndpointMatch[3]
184293
184367
  };
184294
184368
  }
184295
- const serviceMatch = str.match(/^service\.(\w+)\.(url|host|port)$/);
184369
+ const serviceMatch = str.match(/^service\.(\w+)\.(url|private_url|host|port|public_url)$/);
184296
184370
  if (serviceMatch && serviceMatch[1] && serviceMatch[2]) {
184297
184371
  return {
184298
184372
  type: "service",
@@ -184397,6 +184471,17 @@ function parseBuilds(buildData) {
184397
184471
  if (dev) {
184398
184472
  build.dev = dev;
184399
184473
  }
184474
+ const env2 = parseEnv(fieldObj.env);
184475
+ if (env2) {
184476
+ for (const [key, value] of Object.entries(env2)) {
184477
+ if (typeof value === "string")
184478
+ continue;
184479
+ if (value.type === "service" && value.attribute === "public_url")
184480
+ continue;
184481
+ throw new Error(`Build "${name}" env var "${key}" uses an unsupported reference type. Build env vars only support string literals and \${service.<name>.public_url} references.`);
184482
+ }
184483
+ build.env = env2;
184484
+ }
184400
184485
  builds.push(build);
184401
184486
  }
184402
184487
  return builds;
@@ -184704,6 +184789,26 @@ function validateEndpointReferences(config) {
184704
184789
  });
184705
184790
  }
184706
184791
  }
184792
+ if (serviceRef.attribute === "public_url") {
184793
+ const endpointName = serviceRef.endpointName;
184794
+ if (endpointName) {
184795
+ const targetEndpoint = targetEndpoints.find((e) => e.name === endpointName);
184796
+ if (targetEndpoint && !targetEndpoint.public) {
184797
+ errors.push({
184798
+ service: service.name,
184799
+ message: `Service "${service.name}" references public_url of endpoint "${endpointName}" on service "${serviceRef.serviceName}" in env var "${key}", but that endpoint is not public. Add \`public = true\` to the endpoint.`
184800
+ });
184801
+ }
184802
+ } else {
184803
+ const defaultEndpoint = targetEndpoints.length === 1 ? targetEndpoints[0] : targetEndpoints.find((e) => e.name === "default");
184804
+ if (defaultEndpoint && !defaultEndpoint.public) {
184805
+ errors.push({
184806
+ service: service.name,
184807
+ message: `Service "${service.name}" references public_url of service "${serviceRef.serviceName}" in env var "${key}", but its endpoint is not public. Add \`public = true\` to the endpoint.`
184808
+ });
184809
+ }
184810
+ }
184811
+ }
184707
184812
  }
184708
184813
  if (typeof value === "object" && value.type === "endpoint") {
184709
184814
  const endpointRef = value;
@@ -184860,7 +184965,7 @@ var ReaddirpStream = class extends Readable {
184860
184965
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
184861
184966
  const statMethod = opts.lstat ? lstat : stat;
184862
184967
  if (wantBigintFsStats) {
184863
- this._stat = (path23) => statMethod(path23, { bigint: true });
184968
+ this._stat = (path24) => statMethod(path24, { bigint: true });
184864
184969
  } else {
184865
184970
  this._stat = statMethod;
184866
184971
  }
@@ -184885,8 +184990,8 @@ var ReaddirpStream = class extends Readable {
184885
184990
  const par = this.parent;
184886
184991
  const fil = par && par.files;
184887
184992
  if (fil && fil.length > 0) {
184888
- const { path: path23, depth } = par;
184889
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path23));
184993
+ const { path: path24, depth } = par;
184994
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
184890
184995
  const awaited = await Promise.all(slice);
184891
184996
  for (const entry of awaited) {
184892
184997
  if (!entry)
@@ -184926,20 +185031,20 @@ var ReaddirpStream = class extends Readable {
184926
185031
  this.reading = false;
184927
185032
  }
184928
185033
  }
184929
- async _exploreDir(path23, depth) {
185034
+ async _exploreDir(path24, depth) {
184930
185035
  let files;
184931
185036
  try {
184932
- files = await readdir(path23, this._rdOptions);
185037
+ files = await readdir(path24, this._rdOptions);
184933
185038
  } catch (error) {
184934
185039
  this._onError(error);
184935
185040
  }
184936
- return { files, depth, path: path23 };
185041
+ return { files, depth, path: path24 };
184937
185042
  }
184938
- async _formatEntry(dirent, path23) {
185043
+ async _formatEntry(dirent, path24) {
184939
185044
  let entry;
184940
185045
  const basename5 = this._isDirent ? dirent.name : dirent;
184941
185046
  try {
184942
- const fullPath = presolve(pjoin(path23, basename5));
185047
+ const fullPath = presolve(pjoin(path24, basename5));
184943
185048
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
184944
185049
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
184945
185050
  } catch (err) {
@@ -185339,16 +185444,16 @@ var delFromSet = (main, prop, item) => {
185339
185444
  };
185340
185445
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
185341
185446
  var FsWatchInstances = /* @__PURE__ */ new Map();
185342
- function createFsWatchInstance(path23, options2, listener, errHandler, emitRaw) {
185447
+ function createFsWatchInstance(path24, options2, listener, errHandler, emitRaw) {
185343
185448
  const handleEvent = (rawEvent, evPath) => {
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));
185449
+ listener(path24);
185450
+ emitRaw(rawEvent, evPath, { watchedPath: path24 });
185451
+ if (evPath && path24 !== evPath) {
185452
+ fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
185348
185453
  }
185349
185454
  };
185350
185455
  try {
185351
- return fs_watch(path23, {
185456
+ return fs_watch(path24, {
185352
185457
  persistent: options2.persistent
185353
185458
  }, handleEvent);
185354
185459
  } catch (error) {
@@ -185364,12 +185469,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
185364
185469
  listener(val1, val2, val3);
185365
185470
  });
185366
185471
  };
185367
- var setFsWatchListener = (path23, fullPath, options2, handlers) => {
185472
+ var setFsWatchListener = (path24, fullPath, options2, handlers) => {
185368
185473
  const { listener, errHandler, rawEmitter } = handlers;
185369
185474
  let cont = FsWatchInstances.get(fullPath);
185370
185475
  let watcher;
185371
185476
  if (!options2.persistent) {
185372
- watcher = createFsWatchInstance(path23, options2, listener, errHandler, rawEmitter);
185477
+ watcher = createFsWatchInstance(path24, options2, listener, errHandler, rawEmitter);
185373
185478
  if (!watcher)
185374
185479
  return;
185375
185480
  return watcher.close.bind(watcher);
@@ -185380,7 +185485,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
185380
185485
  addAndConvert(cont, KEY_RAW, rawEmitter);
185381
185486
  } else {
185382
185487
  watcher = createFsWatchInstance(
185383
- path23,
185488
+ path24,
185384
185489
  options2,
185385
185490
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
185386
185491
  errHandler,
@@ -185395,7 +185500,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
185395
185500
  cont.watcherUnusable = true;
185396
185501
  if (isWindows && error.code === "EPERM") {
185397
185502
  try {
185398
- const fd = await open2(path23, "r");
185503
+ const fd = await open2(path24, "r");
185399
185504
  await fd.close();
185400
185505
  broadcastErr(error);
185401
185506
  } catch (err) {
@@ -185426,7 +185531,7 @@ var setFsWatchListener = (path23, fullPath, options2, handlers) => {
185426
185531
  };
185427
185532
  };
185428
185533
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
185429
- var setFsWatchFileListener = (path23, fullPath, options2, handlers) => {
185534
+ var setFsWatchFileListener = (path24, fullPath, options2, handlers) => {
185430
185535
  const { listener, rawEmitter } = handlers;
185431
185536
  let cont = FsWatchFileInstances.get(fullPath);
185432
185537
  const copts = cont && cont.options;
@@ -185448,7 +185553,7 @@ var setFsWatchFileListener = (path23, fullPath, options2, handlers) => {
185448
185553
  });
185449
185554
  const currmtime = curr.mtimeMs;
185450
185555
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
185451
- foreach(cont.listeners, (listener2) => listener2(path23, curr));
185556
+ foreach(cont.listeners, (listener2) => listener2(path24, curr));
185452
185557
  }
185453
185558
  })
185454
185559
  };
@@ -185478,13 +185583,13 @@ var NodeFsHandler = class {
185478
185583
  * @param listener on fs change
185479
185584
  * @returns closer for the watcher instance
185480
185585
  */
185481
- _watchWithNodeFs(path23, listener) {
185586
+ _watchWithNodeFs(path24, listener) {
185482
185587
  const opts = this.fsw.options;
185483
- const directory = sp.dirname(path23);
185484
- const basename5 = sp.basename(path23);
185588
+ const directory = sp.dirname(path24);
185589
+ const basename5 = sp.basename(path24);
185485
185590
  const parent = this.fsw._getWatchedDir(directory);
185486
185591
  parent.add(basename5);
185487
- const absolutePath = sp.resolve(path23);
185592
+ const absolutePath = sp.resolve(path24);
185488
185593
  const options2 = {
185489
185594
  persistent: opts.persistent
185490
185595
  };
@@ -185494,12 +185599,12 @@ var NodeFsHandler = class {
185494
185599
  if (opts.usePolling) {
185495
185600
  const enableBin = opts.interval !== opts.binaryInterval;
185496
185601
  options2.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
185497
- closer = setFsWatchFileListener(path23, absolutePath, options2, {
185602
+ closer = setFsWatchFileListener(path24, absolutePath, options2, {
185498
185603
  listener,
185499
185604
  rawEmitter: this.fsw._emitRaw
185500
185605
  });
185501
185606
  } else {
185502
- closer = setFsWatchListener(path23, absolutePath, options2, {
185607
+ closer = setFsWatchListener(path24, absolutePath, options2, {
185503
185608
  listener,
185504
185609
  errHandler: this._boundHandleError,
185505
185610
  rawEmitter: this.fsw._emitRaw
@@ -185521,7 +185626,7 @@ var NodeFsHandler = class {
185521
185626
  let prevStats = stats;
185522
185627
  if (parent.has(basename5))
185523
185628
  return;
185524
- const listener = async (path23, newStats) => {
185629
+ const listener = async (path24, newStats) => {
185525
185630
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
185526
185631
  return;
185527
185632
  if (!newStats || newStats.mtimeMs === 0) {
@@ -185535,11 +185640,11 @@ var NodeFsHandler = class {
185535
185640
  this.fsw._emit(EV.CHANGE, file, newStats2);
185536
185641
  }
185537
185642
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
185538
- this.fsw._closeFile(path23);
185643
+ this.fsw._closeFile(path24);
185539
185644
  prevStats = newStats2;
185540
185645
  const closer2 = this._watchWithNodeFs(file, listener);
185541
185646
  if (closer2)
185542
- this.fsw._addPathCloser(path23, closer2);
185647
+ this.fsw._addPathCloser(path24, closer2);
185543
185648
  } else {
185544
185649
  prevStats = newStats2;
185545
185650
  }
@@ -185571,7 +185676,7 @@ var NodeFsHandler = class {
185571
185676
  * @param item basename of this item
185572
185677
  * @returns true if no more processing is needed for this entry.
185573
185678
  */
185574
- async _handleSymlink(entry, directory, path23, item) {
185679
+ async _handleSymlink(entry, directory, path24, item) {
185575
185680
  if (this.fsw.closed) {
185576
185681
  return;
185577
185682
  }
@@ -185581,7 +185686,7 @@ var NodeFsHandler = class {
185581
185686
  this.fsw._incrReadyCount();
185582
185687
  let linkPath;
185583
185688
  try {
185584
- linkPath = await fsrealpath(path23);
185689
+ linkPath = await fsrealpath(path24);
185585
185690
  } catch (e) {
185586
185691
  this.fsw._emitReady();
185587
185692
  return true;
@@ -185591,12 +185696,12 @@ var NodeFsHandler = class {
185591
185696
  if (dir.has(item)) {
185592
185697
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
185593
185698
  this.fsw._symlinkPaths.set(full, linkPath);
185594
- this.fsw._emit(EV.CHANGE, path23, entry.stats);
185699
+ this.fsw._emit(EV.CHANGE, path24, entry.stats);
185595
185700
  }
185596
185701
  } else {
185597
185702
  dir.add(item);
185598
185703
  this.fsw._symlinkPaths.set(full, linkPath);
185599
- this.fsw._emit(EV.ADD, path23, entry.stats);
185704
+ this.fsw._emit(EV.ADD, path24, entry.stats);
185600
185705
  }
185601
185706
  this.fsw._emitReady();
185602
185707
  return true;
@@ -185626,9 +185731,9 @@ var NodeFsHandler = class {
185626
185731
  return;
185627
185732
  }
185628
185733
  const item = entry.path;
185629
- let path23 = sp.join(directory, item);
185734
+ let path24 = sp.join(directory, item);
185630
185735
  current.add(item);
185631
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path23, item)) {
185736
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
185632
185737
  return;
185633
185738
  }
185634
185739
  if (this.fsw.closed) {
@@ -185637,8 +185742,8 @@ var NodeFsHandler = class {
185637
185742
  }
185638
185743
  if (item === target || !target && !previous.has(item)) {
185639
185744
  this.fsw._incrReadyCount();
185640
- path23 = sp.join(dir, sp.relative(dir, path23));
185641
- this._addToNodeFs(path23, initialAdd, wh, depth + 1);
185745
+ path24 = sp.join(dir, sp.relative(dir, path24));
185746
+ this._addToNodeFs(path24, initialAdd, wh, depth + 1);
185642
185747
  }
185643
185748
  }).on(EV.ERROR, this._boundHandleError);
185644
185749
  return new Promise((resolve7, reject) => {
@@ -185707,13 +185812,13 @@ var NodeFsHandler = class {
185707
185812
  * @param depth Child path actually targeted for watch
185708
185813
  * @param target Child path actually targeted for watch
185709
185814
  */
185710
- async _addToNodeFs(path23, initialAdd, priorWh, depth, target) {
185815
+ async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
185711
185816
  const ready = this.fsw._emitReady;
185712
- if (this.fsw._isIgnored(path23) || this.fsw.closed) {
185817
+ if (this.fsw._isIgnored(path24) || this.fsw.closed) {
185713
185818
  ready();
185714
185819
  return false;
185715
185820
  }
185716
- const wh = this.fsw._getWatchHelpers(path23);
185821
+ const wh = this.fsw._getWatchHelpers(path24);
185717
185822
  if (priorWh) {
185718
185823
  wh.filterPath = (entry) => priorWh.filterPath(entry);
185719
185824
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -185729,8 +185834,8 @@ var NodeFsHandler = class {
185729
185834
  const follow = this.fsw.options.followSymlinks;
185730
185835
  let closer;
185731
185836
  if (stats.isDirectory()) {
185732
- const absPath = sp.resolve(path23);
185733
- const targetPath = follow ? await fsrealpath(path23) : path23;
185837
+ const absPath = sp.resolve(path24);
185838
+ const targetPath = follow ? await fsrealpath(path24) : path24;
185734
185839
  if (this.fsw.closed)
185735
185840
  return;
185736
185841
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -185740,29 +185845,29 @@ var NodeFsHandler = class {
185740
185845
  this.fsw._symlinkPaths.set(absPath, targetPath);
185741
185846
  }
185742
185847
  } else if (stats.isSymbolicLink()) {
185743
- const targetPath = follow ? await fsrealpath(path23) : path23;
185848
+ const targetPath = follow ? await fsrealpath(path24) : path24;
185744
185849
  if (this.fsw.closed)
185745
185850
  return;
185746
185851
  const parent = sp.dirname(wh.watchPath);
185747
185852
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
185748
185853
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
185749
- closer = await this._handleDir(parent, stats, initialAdd, depth, path23, wh, targetPath);
185854
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
185750
185855
  if (this.fsw.closed)
185751
185856
  return;
185752
185857
  if (targetPath !== void 0) {
185753
- this.fsw._symlinkPaths.set(sp.resolve(path23), targetPath);
185858
+ this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
185754
185859
  }
185755
185860
  } else {
185756
185861
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
185757
185862
  }
185758
185863
  ready();
185759
185864
  if (closer)
185760
- this.fsw._addPathCloser(path23, closer);
185865
+ this.fsw._addPathCloser(path24, closer);
185761
185866
  return false;
185762
185867
  } catch (error) {
185763
185868
  if (this.fsw._handleError(error)) {
185764
185869
  ready();
185765
- return path23;
185870
+ return path24;
185766
185871
  }
185767
185872
  }
185768
185873
  }
@@ -185805,24 +185910,24 @@ function createPattern(matcher) {
185805
185910
  }
185806
185911
  return () => false;
185807
185912
  }
185808
- function normalizePath(path23) {
185809
- if (typeof path23 !== "string")
185913
+ function normalizePath(path24) {
185914
+ if (typeof path24 !== "string")
185810
185915
  throw new Error("string expected");
185811
- path23 = sp2.normalize(path23);
185812
- path23 = path23.replace(/\\/g, "/");
185916
+ path24 = sp2.normalize(path24);
185917
+ path24 = path24.replace(/\\/g, "/");
185813
185918
  let prepend = false;
185814
- if (path23.startsWith("//"))
185919
+ if (path24.startsWith("//"))
185815
185920
  prepend = true;
185816
- path23 = path23.replace(DOUBLE_SLASH_RE, "/");
185921
+ path24 = path24.replace(DOUBLE_SLASH_RE, "/");
185817
185922
  if (prepend)
185818
- path23 = "/" + path23;
185819
- return path23;
185923
+ path24 = "/" + path24;
185924
+ return path24;
185820
185925
  }
185821
185926
  function matchPatterns(patterns, testString, stats) {
185822
- const path23 = normalizePath(testString);
185927
+ const path24 = normalizePath(testString);
185823
185928
  for (let index = 0; index < patterns.length; index++) {
185824
185929
  const pattern = patterns[index];
185825
- if (pattern(path23, stats)) {
185930
+ if (pattern(path24, stats)) {
185826
185931
  return true;
185827
185932
  }
185828
185933
  }
@@ -185860,19 +185965,19 @@ var toUnix = (string) => {
185860
185965
  }
185861
185966
  return str;
185862
185967
  };
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));
185968
+ var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
185969
+ var normalizeIgnored = (cwd = "") => (path24) => {
185970
+ if (typeof path24 === "string") {
185971
+ return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
185867
185972
  } else {
185868
- return path23;
185973
+ return path24;
185869
185974
  }
185870
185975
  };
185871
- var getAbsolutePath = (path23, cwd) => {
185872
- if (sp2.isAbsolute(path23)) {
185873
- return path23;
185976
+ var getAbsolutePath = (path24, cwd) => {
185977
+ if (sp2.isAbsolute(path24)) {
185978
+ return path24;
185874
185979
  }
185875
- return sp2.join(cwd, path23);
185980
+ return sp2.join(cwd, path24);
185876
185981
  };
185877
185982
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
185878
185983
  var DirEntry = class {
@@ -185937,10 +186042,10 @@ var WatchHelper = class {
185937
186042
  dirParts;
185938
186043
  followSymlinks;
185939
186044
  statMethod;
185940
- constructor(path23, follow, fsw) {
186045
+ constructor(path24, follow, fsw) {
185941
186046
  this.fsw = fsw;
185942
- const watchPath = path23;
185943
- this.path = path23 = path23.replace(REPLACER_RE, "");
186047
+ const watchPath = path24;
186048
+ this.path = path24 = path24.replace(REPLACER_RE, "");
185944
186049
  this.watchPath = watchPath;
185945
186050
  this.fullWatchPath = sp2.resolve(watchPath);
185946
186051
  this.dirParts = [];
@@ -186080,20 +186185,20 @@ var FSWatcher = class extends EventEmitter {
186080
186185
  this._closePromise = void 0;
186081
186186
  let paths = unifyPaths(paths_);
186082
186187
  if (cwd) {
186083
- paths = paths.map((path23) => {
186084
- const absPath = getAbsolutePath(path23, cwd);
186188
+ paths = paths.map((path24) => {
186189
+ const absPath = getAbsolutePath(path24, cwd);
186085
186190
  return absPath;
186086
186191
  });
186087
186192
  }
186088
- paths.forEach((path23) => {
186089
- this._removeIgnoredPath(path23);
186193
+ paths.forEach((path24) => {
186194
+ this._removeIgnoredPath(path24);
186090
186195
  });
186091
186196
  this._userIgnored = void 0;
186092
186197
  if (!this._readyCount)
186093
186198
  this._readyCount = 0;
186094
186199
  this._readyCount += paths.length;
186095
- Promise.all(paths.map(async (path23) => {
186096
- const res = await this._nodeFsHandler._addToNodeFs(path23, !_internal, void 0, 0, _origAdd);
186200
+ Promise.all(paths.map(async (path24) => {
186201
+ const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
186097
186202
  if (res)
186098
186203
  this._emitReady();
186099
186204
  return res;
@@ -186115,17 +186220,17 @@ var FSWatcher = class extends EventEmitter {
186115
186220
  return this;
186116
186221
  const paths = unifyPaths(paths_);
186117
186222
  const { cwd } = this.options;
186118
- paths.forEach((path23) => {
186119
- if (!sp2.isAbsolute(path23) && !this._closers.has(path23)) {
186223
+ paths.forEach((path24) => {
186224
+ if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
186120
186225
  if (cwd)
186121
- path23 = sp2.join(cwd, path23);
186122
- path23 = sp2.resolve(path23);
186226
+ path24 = sp2.join(cwd, path24);
186227
+ path24 = sp2.resolve(path24);
186123
186228
  }
186124
- this._closePath(path23);
186125
- this._addIgnoredPath(path23);
186126
- if (this._watched.has(path23)) {
186229
+ this._closePath(path24);
186230
+ this._addIgnoredPath(path24);
186231
+ if (this._watched.has(path24)) {
186127
186232
  this._addIgnoredPath({
186128
- path: path23,
186233
+ path: path24,
186129
186234
  recursive: true
186130
186235
  });
186131
186236
  }
@@ -186189,38 +186294,38 @@ var FSWatcher = class extends EventEmitter {
186189
186294
  * @param stats arguments to be passed with event
186190
186295
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
186191
186296
  */
186192
- async _emit(event, path23, stats) {
186297
+ async _emit(event, path24, stats) {
186193
186298
  if (this.closed)
186194
186299
  return;
186195
186300
  const opts = this.options;
186196
186301
  if (isWindows)
186197
- path23 = sp2.normalize(path23);
186302
+ path24 = sp2.normalize(path24);
186198
186303
  if (opts.cwd)
186199
- path23 = sp2.relative(opts.cwd, path23);
186200
- const args = [path23];
186304
+ path24 = sp2.relative(opts.cwd, path24);
186305
+ const args = [path24];
186201
186306
  if (stats != null)
186202
186307
  args.push(stats);
186203
186308
  const awf = opts.awaitWriteFinish;
186204
186309
  let pw;
186205
- if (awf && (pw = this._pendingWrites.get(path23))) {
186310
+ if (awf && (pw = this._pendingWrites.get(path24))) {
186206
186311
  pw.lastChange = /* @__PURE__ */ new Date();
186207
186312
  return this;
186208
186313
  }
186209
186314
  if (opts.atomic) {
186210
186315
  if (event === EVENTS.UNLINK) {
186211
- this._pendingUnlinks.set(path23, [event, ...args]);
186316
+ this._pendingUnlinks.set(path24, [event, ...args]);
186212
186317
  setTimeout(() => {
186213
- this._pendingUnlinks.forEach((entry, path24) => {
186318
+ this._pendingUnlinks.forEach((entry, path25) => {
186214
186319
  this.emit(...entry);
186215
186320
  this.emit(EVENTS.ALL, ...entry);
186216
- this._pendingUnlinks.delete(path24);
186321
+ this._pendingUnlinks.delete(path25);
186217
186322
  });
186218
186323
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
186219
186324
  return this;
186220
186325
  }
186221
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path23)) {
186326
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
186222
186327
  event = EVENTS.CHANGE;
186223
- this._pendingUnlinks.delete(path23);
186328
+ this._pendingUnlinks.delete(path24);
186224
186329
  }
186225
186330
  }
186226
186331
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -186238,16 +186343,16 @@ var FSWatcher = class extends EventEmitter {
186238
186343
  this.emitWithAll(event, args);
186239
186344
  }
186240
186345
  };
186241
- this._awaitWriteFinish(path23, awf.stabilityThreshold, event, awfEmit);
186346
+ this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
186242
186347
  return this;
186243
186348
  }
186244
186349
  if (event === EVENTS.CHANGE) {
186245
- const isThrottled = !this._throttle(EVENTS.CHANGE, path23, 50);
186350
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
186246
186351
  if (isThrottled)
186247
186352
  return this;
186248
186353
  }
186249
186354
  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, path23) : path23;
186355
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
186251
186356
  let stats2;
186252
186357
  try {
186253
186358
  stats2 = await stat3(fullPath);
@@ -186278,23 +186383,23 @@ var FSWatcher = class extends EventEmitter {
186278
186383
  * @param timeout duration of time to suppress duplicate actions
186279
186384
  * @returns tracking object or false if action should be suppressed
186280
186385
  */
186281
- _throttle(actionType, path23, timeout) {
186386
+ _throttle(actionType, path24, timeout) {
186282
186387
  if (!this._throttled.has(actionType)) {
186283
186388
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
186284
186389
  }
186285
186390
  const action = this._throttled.get(actionType);
186286
186391
  if (!action)
186287
186392
  throw new Error("invalid throttle");
186288
- const actionPath = action.get(path23);
186393
+ const actionPath = action.get(path24);
186289
186394
  if (actionPath) {
186290
186395
  actionPath.count++;
186291
186396
  return false;
186292
186397
  }
186293
186398
  let timeoutObject;
186294
186399
  const clear = () => {
186295
- const item = action.get(path23);
186400
+ const item = action.get(path24);
186296
186401
  const count = item ? item.count : 0;
186297
- action.delete(path23);
186402
+ action.delete(path24);
186298
186403
  clearTimeout(timeoutObject);
186299
186404
  if (item)
186300
186405
  clearTimeout(item.timeoutObject);
@@ -186302,7 +186407,7 @@ var FSWatcher = class extends EventEmitter {
186302
186407
  };
186303
186408
  timeoutObject = setTimeout(clear, timeout);
186304
186409
  const thr = { timeoutObject, clear, count: 0 };
186305
- action.set(path23, thr);
186410
+ action.set(path24, thr);
186306
186411
  return thr;
186307
186412
  }
186308
186413
  _incrReadyCount() {
@@ -186316,44 +186421,44 @@ var FSWatcher = class extends EventEmitter {
186316
186421
  * @param event
186317
186422
  * @param awfEmit Callback to be called when ready for event to be emitted.
186318
186423
  */
186319
- _awaitWriteFinish(path23, threshold, event, awfEmit) {
186424
+ _awaitWriteFinish(path24, threshold, event, awfEmit) {
186320
186425
  const awf = this.options.awaitWriteFinish;
186321
186426
  if (typeof awf !== "object")
186322
186427
  return;
186323
186428
  const pollInterval = awf.pollInterval;
186324
186429
  let timeoutHandler;
186325
- let fullPath = path23;
186326
- if (this.options.cwd && !sp2.isAbsolute(path23)) {
186327
- fullPath = sp2.join(this.options.cwd, path23);
186430
+ let fullPath = path24;
186431
+ if (this.options.cwd && !sp2.isAbsolute(path24)) {
186432
+ fullPath = sp2.join(this.options.cwd, path24);
186328
186433
  }
186329
186434
  const now = /* @__PURE__ */ new Date();
186330
186435
  const writes = this._pendingWrites;
186331
186436
  function awaitWriteFinishFn(prevStat) {
186332
186437
  statcb(fullPath, (err, curStat) => {
186333
- if (err || !writes.has(path23)) {
186438
+ if (err || !writes.has(path24)) {
186334
186439
  if (err && err.code !== "ENOENT")
186335
186440
  awfEmit(err);
186336
186441
  return;
186337
186442
  }
186338
186443
  const now2 = Number(/* @__PURE__ */ new Date());
186339
186444
  if (prevStat && curStat.size !== prevStat.size) {
186340
- writes.get(path23).lastChange = now2;
186445
+ writes.get(path24).lastChange = now2;
186341
186446
  }
186342
- const pw = writes.get(path23);
186447
+ const pw = writes.get(path24);
186343
186448
  const df = now2 - pw.lastChange;
186344
186449
  if (df >= threshold) {
186345
- writes.delete(path23);
186450
+ writes.delete(path24);
186346
186451
  awfEmit(void 0, curStat);
186347
186452
  } else {
186348
186453
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
186349
186454
  }
186350
186455
  });
186351
186456
  }
186352
- if (!writes.has(path23)) {
186353
- writes.set(path23, {
186457
+ if (!writes.has(path24)) {
186458
+ writes.set(path24, {
186354
186459
  lastChange: now,
186355
186460
  cancelWait: () => {
186356
- writes.delete(path23);
186461
+ writes.delete(path24);
186357
186462
  clearTimeout(timeoutHandler);
186358
186463
  return event;
186359
186464
  }
@@ -186364,8 +186469,8 @@ var FSWatcher = class extends EventEmitter {
186364
186469
  /**
186365
186470
  * Determines whether user has asked to ignore this path.
186366
186471
  */
186367
- _isIgnored(path23, stats) {
186368
- if (this.options.atomic && DOT_RE.test(path23))
186472
+ _isIgnored(path24, stats) {
186473
+ if (this.options.atomic && DOT_RE.test(path24))
186369
186474
  return true;
186370
186475
  if (!this._userIgnored) {
186371
186476
  const { cwd } = this.options;
@@ -186375,17 +186480,17 @@ var FSWatcher = class extends EventEmitter {
186375
186480
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
186376
186481
  this._userIgnored = anymatch(list, void 0);
186377
186482
  }
186378
- return this._userIgnored(path23, stats);
186483
+ return this._userIgnored(path24, stats);
186379
186484
  }
186380
- _isntIgnored(path23, stat4) {
186381
- return !this._isIgnored(path23, stat4);
186485
+ _isntIgnored(path24, stat4) {
186486
+ return !this._isIgnored(path24, stat4);
186382
186487
  }
186383
186488
  /**
186384
186489
  * Provides a set of common helpers and properties relating to symlink handling.
186385
186490
  * @param path file or directory pattern being watched
186386
186491
  */
186387
- _getWatchHelpers(path23) {
186388
- return new WatchHelper(path23, this.options.followSymlinks, this);
186492
+ _getWatchHelpers(path24) {
186493
+ return new WatchHelper(path24, this.options.followSymlinks, this);
186389
186494
  }
186390
186495
  // Directory helpers
186391
186496
  // -----------------
@@ -186417,63 +186522,63 @@ var FSWatcher = class extends EventEmitter {
186417
186522
  * @param item base path of item/directory
186418
186523
  */
186419
186524
  _remove(directory, item, isDirectory) {
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))
186525
+ const path24 = sp2.join(directory, item);
186526
+ const fullPath = sp2.resolve(path24);
186527
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
186528
+ if (!this._throttle("remove", path24, 100))
186424
186529
  return;
186425
186530
  if (!isDirectory && this._watched.size === 1) {
186426
186531
  this.add(directory, item, true);
186427
186532
  }
186428
- const wp = this._getWatchedDir(path23);
186533
+ const wp = this._getWatchedDir(path24);
186429
186534
  const nestedDirectoryChildren = wp.getChildren();
186430
- nestedDirectoryChildren.forEach((nested) => this._remove(path23, nested));
186535
+ nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
186431
186536
  const parent = this._getWatchedDir(directory);
186432
186537
  const wasTracked = parent.has(item);
186433
186538
  parent.remove(item);
186434
186539
  if (this._symlinkPaths.has(fullPath)) {
186435
186540
  this._symlinkPaths.delete(fullPath);
186436
186541
  }
186437
- let relPath = path23;
186542
+ let relPath = path24;
186438
186543
  if (this.options.cwd)
186439
- relPath = sp2.relative(this.options.cwd, path23);
186544
+ relPath = sp2.relative(this.options.cwd, path24);
186440
186545
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
186441
186546
  const event = this._pendingWrites.get(relPath).cancelWait();
186442
186547
  if (event === EVENTS.ADD)
186443
186548
  return;
186444
186549
  }
186445
- this._watched.delete(path23);
186550
+ this._watched.delete(path24);
186446
186551
  this._watched.delete(fullPath);
186447
186552
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
186448
- if (wasTracked && !this._isIgnored(path23))
186449
- this._emit(eventName, path23);
186450
- this._closePath(path23);
186553
+ if (wasTracked && !this._isIgnored(path24))
186554
+ this._emit(eventName, path24);
186555
+ this._closePath(path24);
186451
186556
  }
186452
186557
  /**
186453
186558
  * Closes all watchers for a path
186454
186559
  */
186455
- _closePath(path23) {
186456
- this._closeFile(path23);
186457
- const dir = sp2.dirname(path23);
186458
- this._getWatchedDir(dir).remove(sp2.basename(path23));
186560
+ _closePath(path24) {
186561
+ this._closeFile(path24);
186562
+ const dir = sp2.dirname(path24);
186563
+ this._getWatchedDir(dir).remove(sp2.basename(path24));
186459
186564
  }
186460
186565
  /**
186461
186566
  * Closes only file-specific watchers
186462
186567
  */
186463
- _closeFile(path23) {
186464
- const closers = this._closers.get(path23);
186568
+ _closeFile(path24) {
186569
+ const closers = this._closers.get(path24);
186465
186570
  if (!closers)
186466
186571
  return;
186467
186572
  closers.forEach((closer) => closer());
186468
- this._closers.delete(path23);
186573
+ this._closers.delete(path24);
186469
186574
  }
186470
- _addPathCloser(path23, closer) {
186575
+ _addPathCloser(path24, closer) {
186471
186576
  if (!closer)
186472
186577
  return;
186473
- let list = this._closers.get(path23);
186578
+ let list = this._closers.get(path24);
186474
186579
  if (!list) {
186475
186580
  list = [];
186476
- this._closers.set(path23, list);
186581
+ this._closers.set(path24, list);
186477
186582
  }
186478
186583
  list.push(closer);
186479
186584
  }
@@ -186591,7 +186696,6 @@ var StablePortAllocator = class {
186591
186696
  import * as fs14 from "fs";
186592
186697
  import * as path10 from "path";
186593
186698
  import * as net from "net";
186594
- import * as os7 from "os";
186595
186699
  import { spawn } from "child_process";
186596
186700
 
186597
186701
  // src/lib/bin/types.ts
@@ -186634,15 +186738,27 @@ import * as path9 from "path";
186634
186738
  import * as os6 from "os";
186635
186739
  import { createReadStream } from "fs";
186636
186740
  import { createTarExtractor, extractTo } from "tar-vern";
186741
+ function getLibraryEnv(binary) {
186742
+ if (!binary.libraryPath) {
186743
+ return {};
186744
+ }
186745
+ const platform4 = os6.platform();
186746
+ if (platform4 === "darwin") {
186747
+ return { DYLD_LIBRARY_PATH: binary.libraryPath };
186748
+ } else if (platform4 === "linux") {
186749
+ return { LD_LIBRARY_PATH: binary.libraryPath };
186750
+ }
186751
+ return {};
186752
+ }
186637
186753
  function getBinBaseDir() {
186638
186754
  return path9.join(os6.homedir(), ".specific", "bin");
186639
186755
  }
186640
186756
  function getPlatformInfo() {
186641
- const platform5 = os6.platform();
186757
+ const platform4 = os6.platform();
186642
186758
  const arch3 = os6.arch();
186643
- if (platform5 !== "darwin" && platform5 !== "linux") {
186759
+ if (platform4 !== "darwin" && platform4 !== "linux") {
186644
186760
  throw new Error(
186645
- `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
186761
+ `Unsupported platform: ${platform4}. Only macOS and Linux are supported.`
186646
186762
  );
186647
186763
  }
186648
186764
  const archStr = arch3;
@@ -186656,7 +186772,7 @@ function getPlatformInfo() {
186656
186772
  `Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
186657
186773
  );
186658
186774
  }
186659
- return { platform: platform5, arch: mappedArch };
186775
+ return { platform: platform4, arch: mappedArch };
186660
186776
  }
186661
186777
  function getBinaryDir(definition, version, platformInfo) {
186662
186778
  return path9.join(
@@ -186832,7 +186948,7 @@ var postgresBinary = {
186832
186948
  // Archive contains bin/ and lib/ directories at root
186833
186949
  stripComponents: 0,
186834
186950
  // Core PostgreSQL executables (in bin/ directory)
186835
- executables: ["bin/postgres", "bin/initdb"],
186951
+ executables: ["bin/postgres", "bin/initdb", "bin/psql"],
186836
186952
  // Library directory relative to root (for setting LD_LIBRARY_PATH)
186837
186953
  libraryDir: "lib"
186838
186954
  };
@@ -186904,18 +187020,6 @@ var drizzleGatewayBinary = {
186904
187020
  };
186905
187021
 
186906
187022
  // src/lib/dev/database-manager.ts
186907
- function getLibraryEnv(binary) {
186908
- if (!binary.libraryPath) {
186909
- return {};
186910
- }
186911
- const platform5 = os7.platform();
186912
- if (platform5 === "darwin") {
186913
- return { DYLD_LIBRARY_PATH: binary.libraryPath };
186914
- } else if (platform5 === "linux") {
186915
- return { LD_LIBRARY_PATH: binary.libraryPath };
186916
- }
186917
- return {};
186918
- }
186919
187023
  async function startPostgres(pg, port, dataDir, onProgress) {
186920
187024
  const binary = await ensureBinary(postgresBinary, void 0, onProgress);
186921
187025
  const dbDataPath = path10.join(process.cwd(), dataDir, pg.name);
@@ -187271,7 +187375,7 @@ config {
187271
187375
  this.name = "MissingConfigError";
187272
187376
  }
187273
187377
  };
187274
- function resolveEnvValue(value, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts) {
187378
+ function resolveEnvValue(value, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts, publicUrls) {
187275
187379
  if (typeof value === "string") {
187276
187380
  return value;
187277
187381
  }
@@ -187312,11 +187416,25 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187312
187416
  }
187313
187417
  switch (serviceRef.attribute) {
187314
187418
  case "url":
187419
+ case "private_url":
187315
187420
  return `localhost:${endpoint.port}`;
187316
187421
  case "host":
187317
187422
  return "localhost";
187318
187423
  case "port":
187319
187424
  return String(endpoint.port);
187425
+ case "public_url": {
187426
+ if (!publicUrls) {
187427
+ throw new Error("public_url reference used but no public URL map provided");
187428
+ }
187429
+ const k8sName = endpointName === "default" ? serviceRef.serviceName : `${serviceRef.serviceName}-${endpointName}`;
187430
+ const publicUrl = publicUrls.get(k8sName);
187431
+ if (!publicUrl) {
187432
+ throw new Error(
187433
+ `No public URL found for service "${serviceRef.serviceName}" endpoint "${endpointName}"`
187434
+ );
187435
+ }
187436
+ return publicUrl;
187437
+ }
187320
187438
  default:
187321
187439
  throw new Error(`Unknown service attribute: ${serviceRef.attribute}`);
187322
187440
  }
@@ -187421,7 +187539,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187421
187539
  throw new Error(`Unknown reference type`);
187422
187540
  }
187423
187541
  }
187424
- function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts) {
187542
+ function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpoints, currentServicePorts, publicUrls) {
187425
187543
  if (!env2) {
187426
187544
  return {};
187427
187545
  }
@@ -187434,7 +187552,8 @@ function resolveEnv(env2, resources, secrets, configs, servicePort, serviceEndpo
187434
187552
  configs,
187435
187553
  servicePort,
187436
187554
  serviceEndpoints,
187437
- currentServicePorts
187555
+ currentServicePorts,
187556
+ publicUrls
187438
187557
  );
187439
187558
  }
187440
187559
  return resolved;
@@ -187458,7 +187577,7 @@ function resolveEnvForExec(env2, resources, secrets, configs) {
187458
187577
  }
187459
187578
 
187460
187579
  // src/lib/dev/service-runner.ts
187461
- function startService(service, resources, secrets, configs, endpointPorts, serviceEndpoints, onLog) {
187580
+ function startService(service, resources, secrets, configs, endpointPorts, serviceEndpoints, onLog, publicUrls) {
187462
187581
  const command = service.dev?.command ?? service.command;
187463
187582
  if (!command) {
187464
187583
  throw new Error(`Service "${service.name}" has no command`);
@@ -187475,7 +187594,8 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
187475
187594
  configs,
187476
187595
  defaultPort,
187477
187596
  serviceEndpoints,
187478
- endpointPorts
187597
+ endpointPorts,
187598
+ publicUrls
187479
187599
  );
187480
187600
  const child = spawn2(command, {
187481
187601
  shell: true,
@@ -188720,7 +188840,7 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
188720
188840
  // src/lib/dev/proxy-registry.ts
188721
188841
  import * as fs19 from "fs";
188722
188842
  import * as path16 from "path";
188723
- import * as os8 from "os";
188843
+ import * as os7 from "os";
188724
188844
  import * as net4 from "net";
188725
188845
  var ProxyRegistryManager = class {
188726
188846
  proxyDir;
@@ -188730,7 +188850,7 @@ var ProxyRegistryManager = class {
188730
188850
  isOwner = false;
188731
188851
  registryWatcher = null;
188732
188852
  constructor() {
188733
- this.proxyDir = path16.join(os8.homedir(), ".specific", "proxy");
188853
+ this.proxyDir = path16.join(os7.homedir(), ".specific", "proxy");
188734
188854
  this.ownerPath = path16.join(this.proxyDir, "owner.json");
188735
188855
  this.registryPath = path16.join(this.proxyDir, "registry.json");
188736
188856
  this.lockPath = path16.join(this.proxyDir, "registry.lock");
@@ -189175,11 +189295,6 @@ function ConfigInput({ configName, onSubmit, onCancel }) {
189175
189295
  return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Enter value for config ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, configName), ":"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, "> "), /* @__PURE__ */ React5.createElement(Text5, null, value), /* @__PURE__ */ React5.createElement(Text5, { color: "gray" }, "|")), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Press Enter to save, Esc to cancel)"));
189176
189296
  }
189177
189297
 
189178
- // src/lib/ui/interactive.ts
189179
- function isInteractive() {
189180
- return process.stdin.isTTY === true && process.stdout.isTTY === true;
189181
- }
189182
-
189183
189298
  // src/commands/dev.tsx
189184
189299
  var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
189185
189300
  function DevUI({ instanceKey, tunnelEnabled }) {
@@ -189305,6 +189420,9 @@ function DevUI({ instanceKey, tunnelEnabled }) {
189305
189420
  // Stop all tunnels
189306
189421
  ...tunnelsRef.current.map((tunnel) => tunnel.stop())
189307
189422
  ]);
189423
+ if (tunnelsRef.current.length > 0) {
189424
+ await new Promise((resolve7) => setTimeout(resolve7, 1500));
189425
+ }
189308
189426
  electricInstancesRef.current = [];
189309
189427
  drizzleGatewayRef.current = null;
189310
189428
  proxyRef.current = null;
@@ -189724,6 +189842,39 @@ Add them to the config block in specific.local`);
189724
189842
  serviceEndpointPorts.set(service.name, endpointPorts);
189725
189843
  serviceEndpoints.set(service.name, endpointInfos);
189726
189844
  }
189845
+ const serviceInfos = [];
189846
+ const exposedServices = [];
189847
+ for (const service of config2.services) {
189848
+ const endpointInfos = serviceEndpoints.get(service.name) || [];
189849
+ for (const info of endpointInfos) {
189850
+ const endpointConfig = service.endpoints.find(
189851
+ (e) => e.name === info.endpointName
189852
+ );
189853
+ if (endpointConfig?.public) {
189854
+ const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
189855
+ exposedServices.push({ name: proxyName, port: info.port });
189856
+ serviceInfos.push({
189857
+ serviceName: proxyName,
189858
+ port: info.port
189859
+ });
189860
+ }
189861
+ }
189862
+ }
189863
+ const publicUrls = /* @__PURE__ */ new Map();
189864
+ let subdomainAllocator;
189865
+ if (tunnelEnabled && exposedServices.length > 0) {
189866
+ subdomainAllocator = new StableSubdomainAllocator(process.cwd(), instanceKey);
189867
+ const multipleServices = exposedServices.length > 1;
189868
+ for (const svc of exposedServices) {
189869
+ const subdomain = subdomainAllocator.allocate(svc.name, multipleServices);
189870
+ publicUrls.set(svc.name, `${subdomain}.tunnel.spcf.app`);
189871
+ }
189872
+ } else {
189873
+ const keySuffix = instanceKey === "default" ? "" : `.${instanceKey}`;
189874
+ for (const svc of exposedServices) {
189875
+ publicUrls.set(svc.name, `${svc.name}${keySuffix}.local.spcf.app`);
189876
+ }
189877
+ }
189727
189878
  const services2 = [];
189728
189879
  for (const service of config2.services) {
189729
189880
  if (cancelled) break;
@@ -189747,7 +189898,8 @@ Add them to the config block in specific.local`);
189747
189898
  configs,
189748
189899
  endpointPorts,
189749
189900
  serviceEndpoints,
189750
- (line) => addLog(line, colorMap)
189901
+ (line) => addLog(line, colorMap),
189902
+ publicUrls
189751
189903
  );
189752
189904
  services2.push(running);
189753
189905
  startedServices.push(running);
@@ -189775,30 +189927,11 @@ Add them to the config block in specific.local`);
189775
189927
  }
189776
189928
  }
189777
189929
  if (cancelled) return;
189778
- const serviceInfos = [];
189779
- const exposedServices = [];
189780
- for (const service of config2.services) {
189781
- const endpointInfos = serviceEndpoints.get(service.name) || [];
189782
- for (const info of endpointInfos) {
189783
- const endpointConfig = service.endpoints.find(
189784
- (e) => e.name === info.endpointName
189785
- );
189786
- if (endpointConfig?.public) {
189787
- const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
189788
- exposedServices.push({ name: proxyName, port: info.port });
189789
- serviceInfos.push({
189790
- serviceName: proxyName,
189791
- port: info.port
189792
- });
189793
- }
189794
- }
189795
- }
189796
189930
  if (tunnelEnabled) {
189797
189931
  writeLog("system", "Tunnel mode enabled, starting tunnels for public services");
189798
189932
  if (exposedServices.length === 0) {
189799
189933
  writeLog("system", "No public services to tunnel");
189800
189934
  } else {
189801
- const subdomainAllocator = new StableSubdomainAllocator(process.cwd(), instanceKey);
189802
189935
  const multipleServices = exposedServices.length > 1;
189803
189936
  const tunnelInstances = [];
189804
189937
  const tunnelStatusMap = /* @__PURE__ */ new Map();
@@ -191654,27 +191787,48 @@ async function execCommand(serviceName, command, instanceKey = "default") {
191654
191787
 
191655
191788
  // src/commands/psql.tsx
191656
191789
  import { spawn as spawn7 } from "child_process";
191657
- async function psqlCommand(databaseName, instanceKey = "default") {
191658
- const stateManager = new InstanceStateManager(process.cwd(), instanceKey);
191659
- await stateManager.cleanStaleState();
191660
- const existingInstances = await stateManager.getExistingInstances();
191661
- if (!existingInstances) {
191662
- console.error("Error: No running instances found. Run `specific dev` first.");
191790
+ import * as fs24 from "fs";
191791
+ import * as path22 from "path";
191792
+ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
191793
+ let startedResources = [];
191794
+ let ownsInstances = false;
191795
+ let stateManager = null;
191796
+ const cleanup = async () => {
191797
+ if (ownsInstances) {
191798
+ for (const resource of startedResources) {
191799
+ await resource.stop().catch(() => {
191800
+ });
191801
+ }
191802
+ if (stateManager) {
191803
+ await stateManager.releaseOwnership().catch(() => {
191804
+ });
191805
+ }
191806
+ }
191807
+ };
191808
+ const configPath = path22.join(process.cwd(), "specific.hcl");
191809
+ if (!fs24.existsSync(configPath)) {
191810
+ console.error("Error: No specific.hcl found in current directory");
191663
191811
  process.exit(1);
191664
191812
  }
191665
- const availableDatabases = Object.keys(existingInstances.databases);
191813
+ let config;
191814
+ try {
191815
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191816
+ config = await parseConfig(hcl);
191817
+ } catch (err) {
191818
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
191819
+ process.exit(1);
191820
+ }
191821
+ const availableDatabases = config.postgres.map((p) => p.name);
191666
191822
  if (availableDatabases.length === 0) {
191667
- console.error("Error: No databases found in running instances.");
191823
+ console.error("Error: No postgres databases defined in specific.hcl");
191668
191824
  process.exit(1);
191669
191825
  }
191670
191826
  let targetDb;
191827
+ if (databaseName && !availableDatabases.includes(databaseName)) {
191828
+ extraArgs = [databaseName, ...extraArgs];
191829
+ databaseName = void 0;
191830
+ }
191671
191831
  if (databaseName) {
191672
- if (!existingInstances.databases[databaseName]) {
191673
- console.error(
191674
- `Error: Database "${databaseName}" not found. Available: ${availableDatabases.join(", ")}`
191675
- );
191676
- process.exit(1);
191677
- }
191678
191832
  targetDb = databaseName;
191679
191833
  } else {
191680
191834
  if (availableDatabases.length > 1) {
@@ -191685,24 +191839,95 @@ async function psqlCommand(databaseName, instanceKey = "default") {
191685
191839
  }
191686
191840
  targetDb = availableDatabases[0];
191687
191841
  }
191688
- const dbState = existingInstances.databases[targetDb];
191689
- const child = spawn7("psql", ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName], {
191842
+ stateManager = new InstanceStateManager(process.cwd(), instanceKey);
191843
+ await stateManager.cleanStaleState();
191844
+ const existingInstances = await stateManager.getExistingInstances();
191845
+ let dbState;
191846
+ if (existingInstances && existingInstances.databases[targetDb]) {
191847
+ const db = existingInstances.databases[targetDb];
191848
+ dbState = { host: db.host, port: db.port, user: db.user, password: db.password, dbName: db.dbName };
191849
+ } else {
191850
+ console.error("Starting database...");
191851
+ try {
191852
+ await stateManager.claimOwnership("psql");
191853
+ ownsInstances = true;
191854
+ } catch (err) {
191855
+ console.error(
191856
+ `Error: ${err instanceof Error ? err.message : String(err)}`
191857
+ );
191858
+ process.exit(1);
191859
+ }
191860
+ try {
191861
+ const result = await startResources({
191862
+ config,
191863
+ selection: {
191864
+ mode: "required",
191865
+ postgres: [targetDb],
191866
+ redis: [],
191867
+ storage: []
191868
+ },
191869
+ stateManager,
191870
+ dataDir: `.specific/keys/${instanceKey}/data`,
191871
+ portAllocator: new PortAllocator(),
191872
+ callbacks: {
191873
+ log: (msg) => console.error(msg)
191874
+ },
191875
+ startElectric: false
191876
+ });
191877
+ startedResources = result.startedResources;
191878
+ const resource = result.resources.get(targetDb);
191879
+ if (!resource) {
191880
+ console.error(`Error: Failed to start database "${targetDb}"`);
191881
+ await cleanup();
191882
+ process.exit(1);
191883
+ }
191884
+ dbState = {
191885
+ host: resource.host,
191886
+ port: resource.port,
191887
+ user: resource.user,
191888
+ password: resource.password,
191889
+ dbName: resource.dbName
191890
+ };
191891
+ } catch (err) {
191892
+ console.error(
191893
+ `Error: Failed to start database: ${err instanceof Error ? err.message : String(err)}`
191894
+ );
191895
+ await cleanup();
191896
+ process.exit(1);
191897
+ }
191898
+ }
191899
+ const binary = await ensureBinary(postgresBinary);
191900
+ const psqlPath = binary.executables["psql"];
191901
+ const libraryEnv = getLibraryEnv(binary);
191902
+ let child = null;
191903
+ const handleSignal = async (signal) => {
191904
+ if (child && !child.killed) {
191905
+ child.kill(signal);
191906
+ }
191907
+ await cleanup();
191908
+ };
191909
+ process.on("SIGINT", () => handleSignal("SIGINT"));
191910
+ process.on("SIGTERM", () => handleSignal("SIGTERM"));
191911
+ child = spawn7(psqlPath, ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName, ...extraArgs], {
191690
191912
  cwd: process.cwd(),
191691
191913
  env: {
191692
191914
  ...process.env,
191915
+ ...libraryEnv,
191693
191916
  PGPASSWORD: dbState.password
191694
191917
  },
191695
191918
  stdio: "inherit"
191696
191919
  });
191697
- child.on("exit", (code, signal) => {
191920
+ child.on("exit", async (code, signal) => {
191921
+ await cleanup();
191698
191922
  if (signal) {
191699
191923
  process.kill(process.pid, signal);
191700
191924
  } else {
191701
191925
  process.exit(code ?? 0);
191702
191926
  }
191703
191927
  });
191704
- child.on("error", (err) => {
191928
+ child.on("error", async (err) => {
191705
191929
  console.error(`Error: Failed to start psql: ${err.message}`);
191930
+ await cleanup();
191706
191931
  process.exit(1);
191707
191932
  });
191708
191933
  }
@@ -191711,21 +191936,21 @@ async function psqlCommand(databaseName, instanceKey = "default") {
191711
191936
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
191712
191937
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
191713
191938
  import Spinner6 from "ink-spinner";
191714
- import * as fs24 from "fs";
191715
- import * as path22 from "path";
191939
+ import * as fs25 from "fs";
191940
+ import * as path23 from "path";
191716
191941
  function CleanUI({ instanceKey }) {
191717
191942
  const [state, setState] = useState7({ status: "checking" });
191718
191943
  useEffect5(() => {
191719
191944
  async function clean() {
191720
191945
  const projectRoot = process.cwd();
191721
- const specificDir = path22.join(projectRoot, ".specific");
191722
- if (!fs24.existsSync(specificDir)) {
191946
+ const specificDir = path23.join(projectRoot, ".specific");
191947
+ if (!fs25.existsSync(specificDir)) {
191723
191948
  setState({ status: "nothing" });
191724
191949
  return;
191725
191950
  }
191726
191951
  if (instanceKey) {
191727
- const keyDir = path22.join(specificDir, "keys", instanceKey);
191728
- if (!fs24.existsSync(keyDir)) {
191952
+ const keyDir = path23.join(specificDir, "keys", instanceKey);
191953
+ if (!fs25.existsSync(keyDir)) {
191729
191954
  setState({ status: "nothing" });
191730
191955
  return;
191731
191956
  }
@@ -191741,7 +191966,7 @@ function CleanUI({ instanceKey }) {
191741
191966
  await stateManager.cleanStaleState();
191742
191967
  setState({ status: "cleaning" });
191743
191968
  try {
191744
- fs24.rmSync(keyDir, { recursive: true, force: true });
191969
+ fs25.rmSync(keyDir, { recursive: true, force: true });
191745
191970
  setState({ status: "success" });
191746
191971
  } catch (err) {
191747
191972
  setState({
@@ -191750,10 +191975,10 @@ function CleanUI({ instanceKey }) {
191750
191975
  });
191751
191976
  }
191752
191977
  } else {
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()
191978
+ const keysDir = path23.join(specificDir, "keys");
191979
+ if (fs25.existsSync(keysDir)) {
191980
+ const keys = fs25.readdirSync(keysDir).filter(
191981
+ (f) => fs25.statSync(path23.join(keysDir, f)).isDirectory()
191757
191982
  );
191758
191983
  for (const key of keys) {
191759
191984
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -191778,7 +192003,7 @@ function CleanUI({ instanceKey }) {
191778
192003
  }
191779
192004
  setState({ status: "cleaning" });
191780
192005
  try {
191781
- fs24.rmSync(specificDir, { recursive: true, force: true });
192006
+ fs25.rmSync(specificDir, { recursive: true, force: true });
191782
192007
  setState({ status: "success" });
191783
192008
  } catch (err) {
191784
192009
  setState({
@@ -191865,8 +192090,8 @@ function logoutCommand() {
191865
192090
  var program = new Command();
191866
192091
  var env = "production";
191867
192092
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
191868
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.48").enablePositionalOptions();
191869
- program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
192093
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.49").enablePositionalOptions();
192094
+ 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));
191870
192095
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
191871
192096
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
191872
192097
  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) => {
@@ -191881,9 +192106,10 @@ program.command("exec <service> [args...]").description("Run a one-off command w
191881
192106
  const key = options2.key ?? getDefaultKey();
191882
192107
  await execCommand(service, filteredArgs, key);
191883
192108
  });
191884
- program.command("psql [database]").description("Connect to a running Postgres database").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").action((database, options2) => {
192109
+ program.command("psql [database] [args...]").description("Connect to a Postgres database").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").passThroughOptions().action((database, args, options2) => {
192110
+ const filteredArgs = args[0] === "--" ? args.slice(1) : args;
191885
192111
  const key = options2.key ?? getDefaultKey();
191886
- psqlCommand(database, key);
192112
+ psqlCommand(database, key, filteredArgs);
191887
192113
  });
191888
192114
  program.command("clean").description("Remove .specific directory for a clean slate").option("-k, --key <key>", "Clean only the specified dev environment key").action((options2) => {
191889
192115
  cleanCommand(options2.key);