nodal-agents 0.8.9 → 0.8.10

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 (203) hide show
  1. package/cli.js +1780 -501
  2. package/migrations/0107_agent_reflection_enabled.sql +13 -0
  3. package/migrations/meta/_journal.json +7 -0
  4. package/node_modules/brace-expansion/index.js +78 -22
  5. package/node_modules/brace-expansion/package.json +1 -1
  6. package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
  7. package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
  8. package/package.json +2 -2
  9. package/runner.js +40473 -21854
  10. package/web/.next/BUILD_ID +1 -1
  11. package/web/.next/app-path-routes-manifest.json +3 -3
  12. package/web/.next/build-manifest.json +2 -2
  13. package/web/.next/prerender-manifest.json +3 -3
  14. package/web/.next/server/app/(dashboard)/agents/[id]/channels/page.js +1 -1
  15. package/web/.next/server/app/(dashboard)/agents/[id]/channels/page_client-reference-manifest.js +1 -1
  16. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page.js +2 -2
  17. package/web/.next/server/app/(dashboard)/agents/[id]/edit/page_client-reference-manifest.js +1 -1
  18. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page.js +1 -1
  19. package/web/.next/server/app/(dashboard)/agents/[id]/telegram/page_client-reference-manifest.js +1 -1
  20. package/web/.next/server/app/(dashboard)/agents/page.js +1 -1
  21. package/web/.next/server/app/(dashboard)/agents/page_client-reference-manifest.js +1 -1
  22. package/web/.next/server/app/(dashboard)/approvals/page.js +1 -1
  23. package/web/.next/server/app/(dashboard)/approvals/page_client-reference-manifest.js +1 -1
  24. package/web/.next/server/app/(dashboard)/automations/page.js +1 -1
  25. package/web/.next/server/app/(dashboard)/automations/page_client-reference-manifest.js +1 -1
  26. package/web/.next/server/app/(dashboard)/chat/[id]/page.js +1 -1
  27. package/web/.next/server/app/(dashboard)/chat/[id]/page_client-reference-manifest.js +1 -1
  28. package/web/.next/server/app/(dashboard)/chat/page.js +1 -1
  29. package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  30. package/web/.next/server/app/(dashboard)/code/[id]/page.js +1 -1
  31. package/web/.next/server/app/(dashboard)/code/[id]/page_client-reference-manifest.js +1 -1
  32. package/web/.next/server/app/(dashboard)/code/page.js +1 -1
  33. package/web/.next/server/app/(dashboard)/code/page_client-reference-manifest.js +1 -1
  34. package/web/.next/server/app/(dashboard)/connectors/page.js +1 -1
  35. package/web/.next/server/app/(dashboard)/connectors/page_client-reference-manifest.js +1 -1
  36. package/web/.next/server/app/(dashboard)/credentials/page.js +1 -1
  37. package/web/.next/server/app/(dashboard)/credentials/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/jobs/[id]/page.js +1 -1
  39. package/web/.next/server/app/(dashboard)/jobs/[id]/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/jobs/page.js +1 -1
  41. package/web/.next/server/app/(dashboard)/jobs/page_client-reference-manifest.js +1 -1
  42. package/web/.next/server/app/(dashboard)/learned-skills/page.js +1 -1
  43. package/web/.next/server/app/(dashboard)/learned-skills/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/llm-providers/page.js +1 -1
  45. package/web/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/logs/page.js +1 -1
  47. package/web/.next/server/app/(dashboard)/logs/page_client-reference-manifest.js +1 -1
  48. package/web/.next/server/app/(dashboard)/mcp/page.js +1 -1
  49. package/web/.next/server/app/(dashboard)/mcp/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/memories/page.js +1 -1
  51. package/web/.next/server/app/(dashboard)/memories/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/page.js +1 -1
  53. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  54. package/web/.next/server/app/(dashboard)/scheduled/[id]/page.js +1 -1
  55. package/web/.next/server/app/(dashboard)/scheduled/[id]/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/scheduled/page.js +1 -1
  57. package/web/.next/server/app/(dashboard)/scheduled/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/settings/page.js +1 -1
  59. package/web/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/settings/root-context/page.js +1 -1
  61. package/web/.next/server/app/(dashboard)/settings/root-context/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page.js +1 -1
  63. package/web/.next/server/app/(dashboard)/skills/[id]/edit/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/(dashboard)/skills/new/page.js +1 -1
  65. package/web/.next/server/app/(dashboard)/skills/new/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/skills/page.js +1 -1
  67. package/web/.next/server/app/(dashboard)/skills/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/(dashboard)/spaces/[id]/files/page.js +1 -1
  69. package/web/.next/server/app/(dashboard)/spaces/[id]/files/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/(dashboard)/spaces/[id]/page.js +1 -1
  71. package/web/.next/server/app/(dashboard)/spaces/[id]/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/(dashboard)/spaces/page.js +1 -1
  73. package/web/.next/server/app/(dashboard)/spaces/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  75. package/web/.next/server/app/_global-error.html +1 -1
  76. package/web/.next/server/app/_global-error.rsc +2 -2
  77. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  78. package/web/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +2 -2
  79. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  80. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/_not-found.html +1 -1
  82. package/web/.next/server/app/_not-found.rsc +3 -3
  83. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  84. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  85. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  86. package/web/.next/server/app/api/auth/[...all]/route.js +1 -1
  87. package/web/.next/server/app/api/health/route.js +1 -1
  88. package/web/.next/server/app/api/oauth/[provider]/callback/route.js +1 -1
  89. package/web/.next/server/app/api/oauth/[provider]/start/route.js +1 -1
  90. package/web/.next/server/app/login/page.js +1 -1
  91. package/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  92. package/web/.next/server/app/onboarding/page.js +2 -2
  93. package/web/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  94. package/web/.next/server/app-paths-manifest.json +3 -3
  95. package/web/.next/server/chunks/1195.js +1 -1
  96. package/web/.next/server/chunks/12.js +1 -0
  97. package/web/.next/server/chunks/210.js +1 -1
  98. package/web/.next/server/chunks/231.js +7 -7
  99. package/web/.next/server/chunks/246.js +1 -0
  100. package/web/.next/server/chunks/3116.js +1 -0
  101. package/web/.next/server/chunks/353.js +1 -1
  102. package/web/.next/server/chunks/3598.js +1 -1
  103. package/web/.next/server/chunks/3660.js +1 -0
  104. package/web/.next/server/chunks/{5329.js → 3925.js} +1 -1
  105. package/web/.next/server/chunks/42.js +2 -2
  106. package/web/.next/server/chunks/4483.js +3 -3
  107. package/web/.next/server/chunks/4928.js +1 -0
  108. package/web/.next/server/chunks/5812.js +1 -0
  109. package/web/.next/server/chunks/6031.js +27 -10
  110. package/web/.next/server/chunks/6365.js +1 -0
  111. package/web/.next/server/chunks/6551.js +6 -6
  112. package/web/.next/server/chunks/7534.js +1 -1
  113. package/web/.next/server/chunks/{4659.js → 7927.js} +9 -9
  114. package/web/.next/server/chunks/9128.js +1 -0
  115. package/web/.next/server/chunks/9348.js +1786 -0
  116. package/web/.next/server/chunks/{2520.js → 990.js} +87 -87
  117. package/web/.next/server/chunks/9949.js +69 -34
  118. package/web/.next/server/middleware-build-manifest.js +1 -1
  119. package/web/.next/server/middleware.js +23 -23
  120. package/web/.next/server/pages/404.html +1 -1
  121. package/web/.next/server/pages/500.html +1 -1
  122. package/web/.next/server/server-reference-manifest.js +1 -1
  123. package/web/.next/server/server-reference-manifest.json +1 -1
  124. package/web/.next/static/chunks/1110-99defede02094d03.js +1 -0
  125. package/web/.next/static/chunks/{3024-0a4647e55c953355.js → 3024-750df22c9a0f5f85.js} +1 -1
  126. package/web/.next/static/chunks/5034-bffbb6ba9aa605de.js +1 -0
  127. package/web/.next/static/chunks/6646-3b01d0a39e57a4a3.js +1 -0
  128. package/web/.next/static/chunks/{7904-f76ecb7fb0ab1b3e.js → 7904-e8c495d3fd5c135e.js} +1 -1
  129. package/web/.next/static/chunks/{8551-0843f388c81ecc4b.js → 8551-66f5a96f8b5ffdd6.js} +38 -21
  130. package/web/.next/static/chunks/{8606-679d56e81c4e4550.js → 8606-3b4575644121850b.js} +1 -1
  131. package/web/.next/static/chunks/9558-ed5cb02a15adeea2.js +1 -0
  132. package/web/.next/static/chunks/9927-e98e963eb2b7f8ed.js +1 -0
  133. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-64aadeb968a4aa5e.js +6 -0
  134. package/web/.next/static/chunks/app/(dashboard)/agents/page-560e79db5926719a.js +1 -0
  135. package/web/.next/static/chunks/app/(dashboard)/approvals/page-eb63ecada43087fb.js +1 -0
  136. package/web/.next/static/chunks/app/(dashboard)/automations/page-8f27f571bb29513e.js +1 -0
  137. package/web/.next/static/chunks/app/(dashboard)/chat/[id]/page-80be9c2ebf0fcf09.js +1 -0
  138. package/web/.next/static/chunks/app/(dashboard)/chat/page-ee43b81b1aa4f36d.js +1 -0
  139. package/web/.next/static/chunks/app/(dashboard)/code/page-19f2af65e857b0ea.js +1 -0
  140. package/web/.next/static/chunks/app/(dashboard)/connectors/page-c350d2304c92e578.js +1 -0
  141. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-7a25f352889a5986.js +1 -0
  142. package/web/.next/static/chunks/app/(dashboard)/jobs/page-c0a871cd968cb52c.js +1 -0
  143. package/web/.next/static/chunks/app/(dashboard)/layout-3f68f447adcef6c5.js +1 -0
  144. package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-38f024a9ed713e0d.js +1 -0
  145. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-cac00d11a48f0521.js +1 -0
  146. package/web/.next/static/chunks/app/(dashboard)/logs/page-0c84de37cfc53542.js +1 -0
  147. package/web/.next/static/chunks/app/(dashboard)/mcp/{page-d38ced6b48036c96.js → page-5d72536a3ea874e5.js} +1 -1
  148. package/web/.next/static/chunks/app/(dashboard)/memories/page-095d01600502f349.js +1 -0
  149. package/web/.next/static/chunks/app/(dashboard)/page-9b219470f8e9a5cb.js +1 -0
  150. package/web/.next/static/chunks/app/(dashboard)/scheduled/[id]/page-9f77ef5800089d92.js +1 -0
  151. package/web/.next/static/chunks/app/(dashboard)/settings/page-fba1c42c418aea4e.js +1 -0
  152. package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-eecb34bf4c12d570.js +1 -0
  153. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-5b1dcfe6bae606ad.js +1 -0
  154. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-716cdb9f6925f3f0.js +1 -0
  155. package/web/.next/static/chunks/app/(dashboard)/skills/page-3c6ed7b04a409801.js +1 -0
  156. package/web/.next/static/chunks/app/(dashboard)/spaces/[id]/files/page-8cafe5c7f9e10cc5.js +1 -0
  157. package/web/.next/static/chunks/app/(dashboard)/spaces/[id]/page-e4f3d4e4fb1bf5b7.js +1 -0
  158. package/web/.next/static/chunks/app/(dashboard)/spaces/page-c4eb76ef7e0abcd8.js +1 -0
  159. package/web/.next/static/chunks/app/login/page-c0820c56e4376430.js +1 -0
  160. package/web/.next/static/chunks/app/onboarding/{page-79a89445432931ef.js → page-5d1b037a182b93b2.js} +2 -2
  161. package/web/.next/static/css/{537b435215d30466.css → 306e66bd437fbf32.css} +1 -1
  162. package/web/.next/server/chunks/2719.js +0 -1
  163. package/web/.next/server/chunks/3234.js +0 -1
  164. package/web/.next/server/chunks/3373.js +0 -1751
  165. package/web/.next/server/chunks/3483.js +0 -1
  166. package/web/.next/server/chunks/5020.js +0 -1
  167. package/web/.next/server/chunks/5042.js +0 -1
  168. package/web/.next/server/chunks/5767.js +0 -1
  169. package/web/.next/server/chunks/6143.js +0 -1
  170. package/web/.next/server/chunks/9717.js +0 -1
  171. package/web/.next/static/chunks/5034-7816e7ad05216095.js +0 -1
  172. package/web/.next/static/chunks/5386-f067c56f0ed0afd3.js +0 -1
  173. package/web/.next/static/chunks/6646-21e7105302193b0e.js +0 -1
  174. package/web/.next/static/chunks/704-0a3bf99b751ca9ab.js +0 -1
  175. package/web/.next/static/chunks/9927-c5951099612edcfb.js +0 -1
  176. package/web/.next/static/chunks/app/(dashboard)/agents/[id]/edit/page-0e0a349399606e8b.js +0 -6
  177. package/web/.next/static/chunks/app/(dashboard)/agents/page-6a8dc12dab1f2874.js +0 -1
  178. package/web/.next/static/chunks/app/(dashboard)/approvals/page-db286cc0a738971b.js +0 -1
  179. package/web/.next/static/chunks/app/(dashboard)/automations/page-c5b1e906f5cb0fe4.js +0 -1
  180. package/web/.next/static/chunks/app/(dashboard)/chat/[id]/page-ddb150b1540988ef.js +0 -1
  181. package/web/.next/static/chunks/app/(dashboard)/chat/page-22788334e4244f90.js +0 -1
  182. package/web/.next/static/chunks/app/(dashboard)/code/page-1571073f95522746.js +0 -1
  183. package/web/.next/static/chunks/app/(dashboard)/connectors/page-949c964856b89951.js +0 -1
  184. package/web/.next/static/chunks/app/(dashboard)/jobs/[id]/page-a431b6b1b9f6fea7.js +0 -1
  185. package/web/.next/static/chunks/app/(dashboard)/jobs/page-c2822831e76daf0c.js +0 -1
  186. package/web/.next/static/chunks/app/(dashboard)/layout-ef725b83f773ce1b.js +0 -1
  187. package/web/.next/static/chunks/app/(dashboard)/learned-skills/page-77a8fdd2d5e31e66.js +0 -1
  188. package/web/.next/static/chunks/app/(dashboard)/llm-providers/page-a5399e46d3a46a96.js +0 -1
  189. package/web/.next/static/chunks/app/(dashboard)/logs/page-9a887fc28ea3b341.js +0 -1
  190. package/web/.next/static/chunks/app/(dashboard)/memories/page-e8ae3c3c0d80b98f.js +0 -1
  191. package/web/.next/static/chunks/app/(dashboard)/page-c7fe8cb11a48378f.js +0 -1
  192. package/web/.next/static/chunks/app/(dashboard)/scheduled/[id]/page-da3e335c850aac91.js +0 -1
  193. package/web/.next/static/chunks/app/(dashboard)/settings/page-1d22f35fbd41c4c8.js +0 -1
  194. package/web/.next/static/chunks/app/(dashboard)/settings/root-context/page-d2e259fd4c3d09b0.js +0 -1
  195. package/web/.next/static/chunks/app/(dashboard)/skills/[id]/edit/page-6709a7b491ae41fd.js +0 -1
  196. package/web/.next/static/chunks/app/(dashboard)/skills/new/page-5d98e96847c72eb5.js +0 -1
  197. package/web/.next/static/chunks/app/(dashboard)/skills/page-c8b3805c9a058b07.js +0 -1
  198. package/web/.next/static/chunks/app/(dashboard)/spaces/[id]/files/page-2f444597fa0abf62.js +0 -1
  199. package/web/.next/static/chunks/app/(dashboard)/spaces/[id]/page-1c2b118f4ef10f7b.js +0 -1
  200. package/web/.next/static/chunks/app/(dashboard)/spaces/page-2d1554b9266429de.js +0 -1
  201. package/web/.next/static/chunks/app/login/page-d40c57350fe5366f.js +0 -1
  202. /package/web/.next/static/{vp639cRRGWy7XZlwOEsL8 → C1Q1ncZzMXdFifa-Gr7Wp}/_buildManifest.js +0 -0
  203. /package/web/.next/static/{vp639cRRGWy7XZlwOEsL8 → C1Q1ncZzMXdFifa-Gr7Wp}/_ssgManifest.js +0 -0
package/cli.js CHANGED
@@ -262,6 +262,214 @@ var init_config = __esm({
262
262
  }
263
263
  });
264
264
 
265
+ // src/lib/pg-logging.ts
266
+ import {
267
+ existsSync as existsSync3,
268
+ mkdirSync as mkdirSync2,
269
+ readFileSync as readFileSync2,
270
+ renameSync,
271
+ unlinkSync,
272
+ writeFileSync as writeFileSync2
273
+ } from "fs";
274
+ import { join as join3, resolve } from "path";
275
+ import { createHash } from "crypto";
276
+ function postgresLogDirFor(dataDir) {
277
+ const normalised = normaliseDataDir(dataDir);
278
+ const digest = createHash("sha256").update(normalised).digest("hex").slice(0, 8);
279
+ const leaf = normalised.split("/").filter(Boolean).pop() ?? "pg-data";
280
+ return join3(PG_LOG_DIR, `${leaf.replace(/[^A-Za-z0-9._-]/g, "_")}-${digest}`);
281
+ }
282
+ function normaliseDataDir(dataDir) {
283
+ const resolved = toPosixPath(resolve(dataDir)).replace(/\/+$/, "");
284
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
285
+ }
286
+ function toPosixPath(path) {
287
+ return path.split("\\").join("/");
288
+ }
289
+ function postgresLoggingSettings(logDirectory) {
290
+ return {
291
+ logging_collector: "on",
292
+ log_destination: "stderr",
293
+ log_directory: toPosixPath(logDirectory),
294
+ log_filename: "postgresql-%Y-%m-%d.log",
295
+ log_rotation_age: "1d",
296
+ log_rotation_size: `${PG_LOG_ROTATION_SIZE_KB}kB`,
297
+ log_truncate_on_rotation: "on",
298
+ // See the header: this KEEPS the crash lines, it does not drop them.
299
+ log_min_messages: "warning",
300
+ // %m = timestamp with milliseconds, %p = pid of the process writing the
301
+ // line. Both are load-bearing for #111: attributing a crash means matching
302
+ // a pid against what a process table said at a given instant.
303
+ log_line_prefix: "%m [%p] %q%u@%d ",
304
+ // A postmaster that reaps a crashed child names the signal only if it is
305
+ // still around to write the line. On by default; said out loud because the
306
+ // whole diagnosis depends on it.
307
+ restart_after_crash: "on"
308
+ };
309
+ }
310
+ function managedKeys() {
311
+ return new Set(Object.keys(postgresLoggingSettings("x")));
312
+ }
313
+ function quoteGuc(value) {
314
+ return `'${value.replace(/'/g, "''")}'`;
315
+ }
316
+ function mergeAutoConf(existing, settings) {
317
+ const managed = managedKeys();
318
+ const kept = [];
319
+ for (const line2 of existing.split(/\r?\n/)) {
320
+ const trimmed = line2.trim();
321
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
322
+ const eq2 = trimmed.indexOf("=");
323
+ if (eq2 < 0) continue;
324
+ const key = trimmed.slice(0, eq2).trim();
325
+ if (managed.has(key)) continue;
326
+ kept.push(trimmed);
327
+ }
328
+ const ours = Object.entries(settings).map(([k, v]) => `${k} = ${quoteGuc(v)}`);
329
+ return "# Do not edit this file manually!\n# It will be overwritten by the ALTER SYSTEM command,\n# and by nodal-agents for the logging_* settings (see apps/cli/src/lib/pg-logging.ts).\n" + [...kept, ...ours].join("\n") + "\n";
330
+ }
331
+ function applyPostgresLoggingConfig(dataDir, logDirectory) {
332
+ mkdirSync2(logDirectory, { recursive: true });
333
+ const target = join3(dataDir, "postgresql.auto.conf");
334
+ let existing = "";
335
+ if (existsSync3(target)) {
336
+ try {
337
+ existing = readFileSync2(target, "utf-8");
338
+ } catch {
339
+ existing = "";
340
+ }
341
+ }
342
+ writeAtomically(target, mergeAutoConf(existing, postgresLoggingSettings(logDirectory)));
343
+ return logDirectory;
344
+ }
345
+ function tempConfPath(target) {
346
+ return `${target}.nodalai.tmp`;
347
+ }
348
+ function writeAtomically(target, contents) {
349
+ const tmp = tempConfPath(target);
350
+ writeFileSync2(tmp, contents, "utf-8");
351
+ try {
352
+ renameSync(tmp, target);
353
+ } catch (err) {
354
+ try {
355
+ unlinkSync(tmp);
356
+ } catch {
357
+ }
358
+ throw err;
359
+ }
360
+ }
361
+ var PG_LOG_DIR, PG_LOG_ROTATION_SIZE_KB;
362
+ var init_pg_logging = __esm({
363
+ "src/lib/pg-logging.ts"() {
364
+ "use strict";
365
+ init_config();
366
+ PG_LOG_DIR = join3(LOG_DIR, "postgres");
367
+ PG_LOG_ROTATION_SIZE_KB = 20 * 1024;
368
+ }
369
+ });
370
+
371
+ // src/lib/orphans.ts
372
+ function parseProcessRows(stdout) {
373
+ const rows = [];
374
+ for (const line2 of stdout.split(/\r?\n/)) {
375
+ const first = line2.indexOf("|");
376
+ if (first < 0) continue;
377
+ const second = line2.indexOf("|", first + 1);
378
+ if (second < 0) continue;
379
+ const third = line2.indexOf("|", second + 1);
380
+ if (third < 0) continue;
381
+ const pid = Number.parseInt(line2.slice(0, first), 10);
382
+ if (!Number.isInteger(pid) || pid <= 0) continue;
383
+ const ppid = Number.parseInt(line2.slice(first + 1, second), 10);
384
+ const startedAt = Number.parseInt(line2.slice(second + 1, third), 10);
385
+ rows.push({
386
+ pid,
387
+ ppid: Number.isInteger(ppid) ? ppid : 0,
388
+ commandLine: line2.slice(third + 1),
389
+ ...Number.isInteger(startedAt) && startedAt > 0 ? { startedAt } : {}
390
+ });
391
+ }
392
+ return rows;
393
+ }
394
+ function isPlausibleParent(parent, child) {
395
+ if (parent.startedAt === void 0 || child.startedAt === void 0) return false;
396
+ return parent.startedAt <= child.startedAt;
397
+ }
398
+ function ownedPostgresPids(input) {
399
+ const owned = [];
400
+ const skipped = [];
401
+ const claim = input.claim;
402
+ if (claim === null) {
403
+ for (const row of input.rows) {
404
+ skipped.push({ pid: row.pid, reason: "no postmaster.pid claims this data dir" });
405
+ }
406
+ return { owned, skipped };
407
+ }
408
+ const byPid = /* @__PURE__ */ new Map();
409
+ for (const row of input.rows) byPid.set(row.pid, row);
410
+ const postmaster = byPid.get(claim.pid);
411
+ if (!input.tableRead) {
412
+ for (const row of input.rows) {
413
+ skipped.push({ pid: row.pid, reason: "the process table could not be read" });
414
+ }
415
+ return { owned, skipped };
416
+ }
417
+ {
418
+ if (postmaster === void 0) {
419
+ for (const row of input.rows) {
420
+ skipped.push({ pid: row.pid, reason: `lockfile pid ${claim.pid} is not a live postgres` });
421
+ }
422
+ return { owned, skipped };
423
+ }
424
+ if (!startMatchesClaim(postmaster, claim)) {
425
+ for (const row of input.rows) {
426
+ skipped.push({
427
+ pid: row.pid,
428
+ reason: `pid ${claim.pid} started at ${String(postmaster.startedAt)}, lockfile says ${String(claim.startedAtSeconds)}`
429
+ });
430
+ }
431
+ return { owned, skipped };
432
+ }
433
+ }
434
+ owned.push(claim.pid);
435
+ for (const row of input.rows) {
436
+ if (row.pid === claim.pid) continue;
437
+ if (descendsFrom(row, claim.pid, byPid)) owned.push(row.pid);
438
+ else skipped.push({ pid: row.pid, reason: `ancestry=${row.ppid} does not reach ${claim.pid}` });
439
+ }
440
+ return { owned, skipped };
441
+ }
442
+ function startMatchesClaim(process3, claim) {
443
+ if (process3.startedAt === void 0 || claim.startedAtSeconds === null) return false;
444
+ return Math.abs(process3.startedAt - claim.startedAtSeconds * 1e3) <= START_TIME_TOLERANCE_MS;
445
+ }
446
+ function descendsFrom(row, ancestorPid, byPid) {
447
+ const seen = /* @__PURE__ */ new Set([row.pid]);
448
+ let child = row;
449
+ let cursor = byPid.get(row.ppid);
450
+ while (cursor && !seen.has(cursor.pid)) {
451
+ if (!isPlausibleParent(cursor, child)) return false;
452
+ if (cursor.pid === ancestorPid) return true;
453
+ seen.add(cursor.pid);
454
+ child = cursor;
455
+ cursor = byPid.get(cursor.ppid);
456
+ }
457
+ return false;
458
+ }
459
+ function measuredPort(pid, listeners) {
460
+ return listeners.find((l) => l.pid === pid)?.port ?? null;
461
+ }
462
+ function formatForeignSkip(entry) {
463
+ return `ORPHAN_PROBE_FOREIGN_POSTGRES_SKIPPED pid=${entry.pid} reason=${entry.reason}`;
464
+ }
465
+ var START_TIME_TOLERANCE_MS;
466
+ var init_orphans = __esm({
467
+ "src/lib/orphans.ts"() {
468
+ "use strict";
469
+ START_TIME_TOLERANCE_MS = 2e3;
470
+ }
471
+ });
472
+
265
473
  // ../../node_modules/.pnpm/postgres@3.4.9/node_modules/postgres/src/query.js
266
474
  function cachedError(xs) {
267
475
  if (originCache.has(xs))
@@ -282,9 +490,9 @@ var init_query = __esm({
282
490
  CLOSE = {};
283
491
  Query = class extends Promise {
284
492
  constructor(strings, args, handler, canceller, options = {}) {
285
- let resolve3, reject;
493
+ let resolve4, reject;
286
494
  super((a, b2) => {
287
- resolve3 = a;
495
+ resolve4 = a;
288
496
  reject = b2;
289
497
  });
290
498
  this.tagged = Array.isArray(strings.raw);
@@ -295,7 +503,7 @@ var init_query = __esm({
295
503
  this.options = options;
296
504
  this.state = null;
297
505
  this.statement = null;
298
- this.resolve = (x) => (this.active = false, resolve3(x));
506
+ this.resolve = (x) => (this.active = false, resolve4(x));
299
507
  this.reject = (x) => (this.active = false, reject(x));
300
508
  this.active = false;
301
509
  this.cancelled = null;
@@ -343,12 +551,12 @@ var init_query = __esm({
343
551
  if (this.executed && !this.active)
344
552
  return { done: true };
345
553
  prev && prev();
346
- const promise = new Promise((resolve3, reject) => {
554
+ const promise = new Promise((resolve4, reject) => {
347
555
  this.cursorFn = (value) => {
348
- resolve3({ value, done: false });
556
+ resolve4({ value, done: false });
349
557
  return new Promise((r) => prev = r);
350
558
  };
351
- this.resolve = () => (this.active = false, resolve3({ done: true }));
559
+ this.resolve = () => (this.active = false, resolve4({ done: true }));
352
560
  this.reject = (x) => (this.active = false, reject(x));
353
561
  });
354
562
  this.execute();
@@ -946,12 +1154,12 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
946
1154
  x.on("drain", drain);
947
1155
  return x;
948
1156
  }
949
- async function cancel({ pid, secret }, resolve3, reject) {
1157
+ async function cancel({ pid, secret }, resolve4, reject) {
950
1158
  try {
951
1159
  cancelMessage = bytes_default().i32(16).i32(80877102).i32(pid).i32(secret).end(16);
952
1160
  await connect();
953
1161
  socket.once("error", reject);
954
- socket.once("close", resolve3);
1162
+ socket.once("close", resolve4);
955
1163
  } catch (error2) {
956
1164
  reject(error2);
957
1165
  }
@@ -1968,7 +2176,7 @@ var init_subscribe = __esm({
1968
2176
  // ../../node_modules/.pnpm/postgres@3.4.9/node_modules/postgres/src/large.js
1969
2177
  import Stream2 from "stream";
1970
2178
  function largeObject(sql2, oid, mode = 131072 | 262144) {
1971
- return new Promise(async (resolve3, reject) => {
2179
+ return new Promise(async (resolve4, reject) => {
1972
2180
  await sql2.begin(async (sql3) => {
1973
2181
  let finish;
1974
2182
  !oid && ([{ oid }] = await sql3`select lo_creat(-1) as oid`);
@@ -1994,7 +2202,7 @@ function largeObject(sql2, oid, mode = 131072 | 262144) {
1994
2202
  ) seek
1995
2203
  `
1996
2204
  };
1997
- resolve3(lo);
2205
+ resolve4(lo);
1998
2206
  return new Promise(async (r) => finish = r);
1999
2207
  async function readable({
2000
2208
  highWaterMark = 2048 * 8,
@@ -2155,8 +2363,8 @@ function Postgres(a, b2) {
2155
2363
  }
2156
2364
  async function reserve() {
2157
2365
  const queue = queue_default();
2158
- const c = open2.length ? open2.shift() : await new Promise((resolve3, reject) => {
2159
- const query = { reserve: resolve3, reject };
2366
+ const c = open2.length ? open2.shift() : await new Promise((resolve4, reject) => {
2367
+ const query = { reserve: resolve4, reject };
2160
2368
  queries.push(query);
2161
2369
  closed.length && connect(closed.shift(), query);
2162
2370
  });
@@ -2193,9 +2401,9 @@ function Postgres(a, b2) {
2193
2401
  let uncaughtError, result;
2194
2402
  name && await sql3`savepoint ${sql3(name)}`;
2195
2403
  try {
2196
- result = await new Promise((resolve3, reject) => {
2404
+ result = await new Promise((resolve4, reject) => {
2197
2405
  const x = fn2(sql3);
2198
- Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve3, reject);
2406
+ Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve4, reject);
2199
2407
  });
2200
2408
  if (uncaughtError)
2201
2409
  throw uncaughtError;
@@ -2252,8 +2460,8 @@ function Postgres(a, b2) {
2252
2460
  return c.execute(query) ? move(c, busy) : move(c, full);
2253
2461
  }
2254
2462
  function cancel(query) {
2255
- return new Promise((resolve3, reject) => {
2256
- query.state ? query.active ? connection_default(options).cancel(query.state, resolve3, reject) : query.cancelled = { resolve: resolve3, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve3());
2463
+ return new Promise((resolve4, reject) => {
2464
+ query.state ? query.active ? connection_default(options).cancel(query.state, resolve4, reject) : query.cancelled = { resolve: resolve4, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve4());
2257
2465
  });
2258
2466
  }
2259
2467
  async function end({ timeout = null } = {}) {
@@ -2272,11 +2480,11 @@ function Postgres(a, b2) {
2272
2480
  async function close() {
2273
2481
  await Promise.all(connections.map((c) => c.end()));
2274
2482
  }
2275
- async function destroy(resolve3) {
2483
+ async function destroy(resolve4) {
2276
2484
  await Promise.all(connections.map((c) => c.terminate()));
2277
2485
  while (queries.length)
2278
2486
  queries.shift().reject(Errors.connection("CONNECTION_DESTROYED", options));
2279
- resolve3();
2487
+ resolve4();
2280
2488
  }
2281
2489
  function connect(c, query) {
2282
2490
  move(c, connecting);
@@ -3714,7 +3922,7 @@ var init_sql = __esm({
3714
3922
  return new SQL([new StringChunk(str)]);
3715
3923
  }
3716
3924
  sql2.raw = raw;
3717
- function join12(chunks, separator) {
3925
+ function join14(chunks, separator) {
3718
3926
  const result = [];
3719
3927
  for (const [i, chunk] of chunks.entries()) {
3720
3928
  if (i > 0 && separator !== void 0) {
@@ -3724,7 +3932,7 @@ var init_sql = __esm({
3724
3932
  }
3725
3933
  return new SQL(result);
3726
3934
  }
3727
- sql2.join = join12;
3935
+ sql2.join = join14;
3728
3936
  function identifier(value) {
3729
3937
  return new Name(value);
3730
3938
  }
@@ -8047,7 +8255,7 @@ var init_select2 = __esm({
8047
8255
  const baseTableName = this.tableName;
8048
8256
  const tableName = getTableLikeName(table);
8049
8257
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
8050
- if (typeof tableName === "string" && this.config.joins?.some((join12) => join12.alias === tableName)) {
8258
+ if (typeof tableName === "string" && this.config.joins?.some((join14) => join14.alias === tableName)) {
8051
8259
  throw new Error(`Alias "${tableName}" is already used in this query`);
8052
8260
  }
8053
8261
  if (!this.isPartialSelect) {
@@ -9582,7 +9790,7 @@ var init_update = __esm({
9582
9790
  createJoin(joinType) {
9583
9791
  return (table, on) => {
9584
9792
  const tableName = getTableLikeName(table);
9585
- if (typeof tableName === "string" && this.config.joins.some((join12) => join12.alias === tableName)) {
9793
+ if (typeof tableName === "string" && this.config.joins.some((join14) => join14.alias === tableName)) {
9586
9794
  throw new Error(`Alias "${tableName}" is already used in this query`);
9587
9795
  }
9588
9796
  if (typeof on === "function") {
@@ -9678,10 +9886,10 @@ var init_update = __esm({
9678
9886
  const fromFields = this.getTableLikeFields(this.config.from);
9679
9887
  fields[tableName] = fromFields;
9680
9888
  }
9681
- for (const join12 of this.config.joins) {
9682
- const tableName2 = getTableLikeName(join12.table);
9683
- if (typeof tableName2 === "string" && !is(join12.table, SQL)) {
9684
- const fromFields = this.getTableLikeFields(join12.table);
9889
+ for (const join14 of this.config.joins) {
9890
+ const tableName2 = getTableLikeName(join14.table);
9891
+ if (typeof tableName2 === "string" && !is(join14.table, SQL)) {
9892
+ const fromFields = this.getTableLikeFields(join14.table);
9685
9893
  fields[tableName2] = fromFields;
9686
9894
  }
9687
9895
  }
@@ -10924,14 +11132,14 @@ __export(migrate_exports, {
10924
11132
  runMigrations: () => runMigrations
10925
11133
  });
10926
11134
  import { fileURLToPath as fileURLToPath2 } from "url";
10927
- import { join as join3, dirname as dirname2 } from "path";
11135
+ import { join as join4, dirname as dirname2 } from "path";
10928
11136
  import {
10929
- existsSync as existsSync3,
11137
+ existsSync as existsSync4,
10930
11138
  mkdtempSync,
10931
11139
  readdirSync,
10932
- readFileSync as readFileSync2,
10933
- writeFileSync as writeFileSync2,
10934
- mkdirSync as mkdirSync2
11140
+ readFileSync as readFileSync3,
11141
+ writeFileSync as writeFileSync3,
11142
+ mkdirSync as mkdirSync3
10935
11143
  } from "fs";
10936
11144
  import { tmpdir } from "os";
10937
11145
  async function runMigrations(connectionString, opts = {}) {
@@ -10944,26 +11152,26 @@ async function runMigrations(connectionString, opts = {}) {
10944
11152
  });
10945
11153
  const db = drizzle(sql2);
10946
11154
  const here = dirname2(fileURLToPath2(import.meta.url));
10947
- const sibling = join3(here, "migrations");
10948
- const sourceFolder = existsSync3(sibling) ? sibling : join3(here, "../migrations");
11155
+ const sibling = join4(here, "migrations");
11156
+ const sourceFolder = existsSync4(sibling) ? sibling : join4(here, "../migrations");
10949
11157
  const migrationsFolder = opts.patchVectorAsText ? patchMigrationsForNoVector(sourceFolder) : sourceFolder;
10950
11158
  await migrate(db, { migrationsFolder });
10951
11159
  await sql2.end();
10952
11160
  }
10953
11161
  function patchMigrationsForNoVector(sourceFolder) {
10954
- const tmpFolder = mkdtempSync(join3(tmpdir(), "nodalai-migrations-"));
10955
- const metaDir = join3(tmpFolder, "meta");
10956
- mkdirSync2(metaDir, { recursive: true });
11162
+ const tmpFolder = mkdtempSync(join4(tmpdir(), "nodalai-migrations-"));
11163
+ const metaDir = join4(tmpFolder, "meta");
11164
+ mkdirSync3(metaDir, { recursive: true });
10957
11165
  for (const entry of readdirSync(sourceFolder, { withFileTypes: true })) {
10958
11166
  if (entry.isDirectory() && entry.name === "meta") {
10959
- for (const metaFile of readdirSync(join3(sourceFolder, "meta"))) {
10960
- const content = readFileSync2(join3(sourceFolder, "meta", metaFile), "utf8");
10961
- writeFileSync2(join3(metaDir, metaFile), content);
11167
+ for (const metaFile of readdirSync(join4(sourceFolder, "meta"))) {
11168
+ const content = readFileSync3(join4(sourceFolder, "meta", metaFile), "utf8");
11169
+ writeFileSync3(join4(metaDir, metaFile), content);
10962
11170
  }
10963
11171
  } else if (entry.isFile() && entry.name.endsWith(".sql")) {
10964
- const content = readFileSync2(join3(sourceFolder, entry.name), "utf8");
11172
+ const content = readFileSync3(join4(sourceFolder, entry.name), "utf8");
10965
11173
  const patched = content.replace(/\bvector\(\d+\)/g, "text");
10966
- writeFileSync2(join3(tmpFolder, entry.name), patched);
11174
+ writeFileSync3(join4(tmpFolder, entry.name), patched);
10967
11175
  }
10968
11176
  }
10969
11177
  return tmpFolder;
@@ -10978,69 +11186,185 @@ var init_migrate = __esm({
10978
11186
  });
10979
11187
 
10980
11188
  // src/lib/postgres.ts
10981
- import { existsSync as existsSync4, readFileSync as readFileSync3, unlinkSync } from "fs";
10982
- import { join as join4 } from "path";
11189
+ var postgres_exports = {};
11190
+ __export(postgres_exports, {
11191
+ LEGACY_PG_PASSWORD: () => LEGACY_PG_PASSWORD,
11192
+ PG_DATABASE: () => PG_DATABASE,
11193
+ PG_USER: () => PG_USER,
11194
+ buildPgUrl: () => buildPgUrl,
11195
+ isPostgresAuthFailure: () => isPostgresAuthFailure,
11196
+ livePostmasterPid: () => livePostmasterPid,
11197
+ postgresProcessesForDataDir: () => postgresProcessesForDataDir,
11198
+ processStartedAtMs: () => processStartedAtMs,
11199
+ readPostmasterClaim: () => readPostmasterClaim,
11200
+ readPostmasterPid: () => readPostmasterPid,
11201
+ resolvePgCtl: () => resolvePgCtl,
11202
+ runMigrations: () => runMigrations2,
11203
+ startAndWaitUntilReady: () => startAndWaitUntilReady,
11204
+ startEmbeddedPostgres: () => startEmbeddedPostgres,
11205
+ stopOrphanPostgres: () => stopOrphanPostgres,
11206
+ unconfirmedReading: () => unconfirmedReading
11207
+ });
11208
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync4, realpathSync, statSync, unlinkSync as unlinkSync2 } from "fs";
11209
+ import { join as join5 } from "path";
10983
11210
  function readPostmasterPid(dataDir = PG_DATA_DIR) {
10984
- const pidFile = join4(dataDir, "postmaster.pid");
10985
- if (!existsSync4(pidFile)) return null;
11211
+ return readPostmasterClaim(dataDir)?.pid ?? null;
11212
+ }
11213
+ function readPostmasterClaim(dataDir = PG_DATA_DIR) {
11214
+ const pidFile = join5(dataDir, "postmaster.pid");
11215
+ if (!existsSync5(pidFile)) return null;
10986
11216
  try {
10987
- const firstLine = readFileSync3(pidFile, "utf-8").split("\n")[0]?.trim() ?? "";
10988
- const pid = Number.parseInt(firstLine, 10);
10989
- return Number.isInteger(pid) && pid > 0 ? pid : null;
11217
+ const lines = readFileSync4(pidFile, "utf-8").split("\n");
11218
+ const pid = Number.parseInt(lines[0]?.trim() ?? "", 10);
11219
+ if (!Number.isInteger(pid) || pid <= 0) return null;
11220
+ const recorded = lines[1]?.trim();
11221
+ if (recorded !== void 0 && recorded !== "" && !sameDirectory(recorded, dataDir)) {
11222
+ process.stderr.write(
11223
+ `POSTMASTER_PID_FOREIGN_DATA_DIR pid=${pid} recorded=${recorded} expected=${dataDir}
11224
+ `
11225
+ );
11226
+ return null;
11227
+ }
11228
+ const seconds = Number.parseInt(lines[2]?.trim() ?? "", 10);
11229
+ return { pid, startedAtSeconds: Number.isInteger(seconds) && seconds > 0 ? seconds : null };
10990
11230
  } catch {
10991
11231
  return null;
10992
11232
  }
10993
11233
  }
11234
+ function sameDirectory(a, b2) {
11235
+ const clean = (v) => process.platform === "win32" ? v.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() : (
11236
+ // A backslash is a legal character in a POSIX filename, so turning it
11237
+ // into a separator made `/srv/pg\data` and `/srv/pg/data` — two real,
11238
+ // different directories — compare equal (pass-9 finding R2). Only the
11239
+ // trailing separator is dropped.
11240
+ v.replace(/\/+$/, "")
11241
+ );
11242
+ return clean(a) === clean(b2);
11243
+ }
10994
11244
  function livePostmasterPid(dataDir = PG_DATA_DIR) {
10995
11245
  const pid = readPostmasterPid(dataDir);
10996
- if (pid === null) return null;
11246
+ return pid !== null && isPidRunning(pid) ? pid : null;
11247
+ }
11248
+ function isPidRunning(pid) {
10997
11249
  try {
10998
11250
  process.kill(pid, 0);
10999
- return pid;
11251
+ return true;
11000
11252
  } catch (err) {
11001
- return err.code === "ESRCH" ? null : pid;
11253
+ return err.code !== "ESRCH";
11002
11254
  }
11003
11255
  }
11004
- async function postgresPidsForDataDir(dataDir = PG_DATA_DIR) {
11005
- if (process.platform !== "win32") return [];
11256
+ async function postgresProcessesForDataDir(dataDir = PG_DATA_DIR) {
11257
+ if (process.platform !== "win32") return unconfirmedReading(dataDir);
11006
11258
  const { execa: execa4 } = await import("execa");
11007
11259
  try {
11008
- const { stdout } = await execa4(
11260
+ const { stdout, stderr, exitCode } = await execa4(
11009
11261
  "powershell",
11010
11262
  [
11011
11263
  "-NoProfile",
11012
11264
  "-NonInteractive",
11013
11265
  "-Command",
11014
- // CommandLine is where the -D <dataDir> argument lives. Matching on it
11015
- // is what ties a bare `postgres.exe` to OUR cluster rather than to some
11016
- // other Postgres the user runs.
11017
- `Get-CimInstance Win32_Process -Filter "Name='postgres.exe'" | Select-Object -ExpandProperty ProcessId,CommandLine | Out-Null; Get-CimInstance Win32_Process -Filter "Name='postgres.exe'" | ForEach-Object { "$($_.ProcessId)\`t$($_.CommandLine)" }`
11266
+ // `CreationDate` is the load-bearing field: it tells one generation of
11267
+ // a pid from the next, which is the only thing that catches a pid the
11268
+ // OS recycled onto a stranger. It goes BEFORE the command line, the one
11269
+ // field that may itself carry a `|`. The command line is kept for the
11270
+ // log line that says what was left alone — never to decide ownership.
11271
+ `Get-CimInstance Win32_Process -Filter "Name='postgres.exe'" | ForEach-Object { $ms = if ($_.CreationDate) { [int64]($_.CreationDate.ToUniversalTime() - (Get-Date '1970-01-01T00:00:00Z').ToUniversalTime()).TotalMilliseconds } else { 0 }; "$($_.ProcessId)|$($_.ParentProcessId)|$ms|$($_.CommandLine)" }`
11018
11272
  ],
11019
11273
  { reject: false, timeout: 1e4 }
11020
11274
  );
11021
- const dataNeedle = dataDir.toLowerCase().replace(/\\/g, "/");
11022
- const binNeedle = "@embedded-postgres";
11023
- const pids = [];
11024
- for (const line2 of stdout.split(/\r?\n/)) {
11025
- const tab = line2.indexOf(" ");
11026
- if (tab < 0) continue;
11027
- const pid = Number.parseInt(line2.slice(0, tab), 10);
11028
- if (!Number.isInteger(pid) || pid <= 0) continue;
11029
- const cmd = line2.slice(tab + 1).toLowerCase().replace(/\\/g, "/");
11030
- if (cmd.includes(dataNeedle) || cmd.includes(binNeedle)) pids.push(pid);
11031
- }
11032
- return pids;
11275
+ if (exitCode !== 0) {
11276
+ process.stderr.write(
11277
+ `ORPHAN_PROBE_UNREADABLE exit=${String(exitCode)} stderr=${stderr.trim().slice(0, 200)}
11278
+ `
11279
+ );
11280
+ return unconfirmedReading(dataDir);
11281
+ }
11282
+ const { owned, skipped } = ownedPostgresPids({
11283
+ rows: parseProcessRows(stdout),
11284
+ tableRead: true,
11285
+ // The data directory answers; this table only confirms.
11286
+ claim: readPostmasterClaim(dataDir)
11287
+ });
11288
+ for (const entry of skipped) process.stderr.write(`${formatForeignSkip(entry)}
11289
+ `);
11290
+ return { read: true, owned };
11291
+ } catch (err) {
11292
+ process.stderr.write(
11293
+ `ORPHAN_PROBE_UNREADABLE error=${err instanceof Error ? err.message : String(err)}
11294
+ `
11295
+ );
11296
+ return unconfirmedReading(dataDir);
11297
+ }
11298
+ }
11299
+ function unconfirmedReading(dataDir) {
11300
+ const claim = readPostmasterClaim(dataDir);
11301
+ if (claim === null) {
11302
+ process.stderr.write(`ORPHAN_PROBE_NO_CLAIM dataDir=${dataDir}
11303
+ `);
11304
+ return { read: false, owned: [] };
11305
+ }
11306
+ if (!isPidRunning(claim.pid)) {
11307
+ process.stderr.write(`ORPHAN_PROBE_CLAIMED_PID_NOT_ALIVE pid=${claim.pid}
11308
+ `);
11309
+ return { read: false, owned: [] };
11310
+ }
11311
+ const holdsDataDir = postmasterHoldsDataDir(claim.pid, dataDir);
11312
+ if (holdsDataDir !== true) {
11313
+ process.stderr.write(
11314
+ `ORPHAN_PROBE_NOT_IN_DATA_DIR pid=${claim.pid} holds=${String(holdsDataDir)}
11315
+ `
11316
+ );
11317
+ return { read: false, owned: [] };
11318
+ }
11319
+ const startedAt = processStartedAtMs(claim.pid);
11320
+ if (startedAt === null) {
11321
+ process.stderr.write(
11322
+ `ORPHAN_PROBE_NO_START_TIME pid=${claim.pid} platform=${process.platform}
11323
+ `
11324
+ );
11325
+ return { read: false, owned: [] };
11326
+ }
11327
+ const { owned, skipped } = ownedPostgresPids({
11328
+ rows: [{ pid: claim.pid, ppid: 0, commandLine: "", startedAt }],
11329
+ tableRead: true,
11330
+ claim
11331
+ });
11332
+ for (const entry of skipped) process.stderr.write(`${formatForeignSkip(entry)}
11333
+ `);
11334
+ return { read: false, owned };
11335
+ }
11336
+ function postmasterHoldsDataDir(pid, dataDir) {
11337
+ if (process.platform !== "linux") return null;
11338
+ try {
11339
+ return sameDirectory(realpathSync(`/proc/${pid}/cwd`), realpathSync(dataDir));
11033
11340
  } catch {
11034
- return [];
11341
+ return null;
11342
+ }
11343
+ }
11344
+ function processStartedAtMs(pid) {
11345
+ if (process.platform !== "linux") return null;
11346
+ try {
11347
+ const stat = readFileSync4(`/proc/${pid}/stat`, "utf-8");
11348
+ const after = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
11349
+ const ticksSinceBoot = Number.parseInt(after[19] ?? "", 10);
11350
+ if (!Number.isFinite(ticksSinceBoot)) return null;
11351
+ const uptimeSeconds = Number.parseFloat(
11352
+ readFileSync4("/proc/uptime", "utf-8").split(" ")[0] ?? ""
11353
+ );
11354
+ if (!Number.isFinite(uptimeSeconds)) return null;
11355
+ const elapsedSeconds = uptimeSeconds - ticksSinceBoot / 100;
11356
+ return Date.now() - elapsedSeconds * 1e3;
11357
+ } catch {
11358
+ return null;
11035
11359
  }
11036
11360
  }
11037
11361
  function clearStalePostmasterPid(dataDir) {
11038
- const pidFile = join4(dataDir, "postmaster.pid");
11039
- if (!existsSync4(pidFile)) return;
11362
+ const pidFile = join5(dataDir, "postmaster.pid");
11363
+ if (!existsSync5(pidFile)) return;
11040
11364
  if (readPostmasterPid(dataDir) === null) return;
11041
11365
  if (livePostmasterPid(dataDir) !== null) return;
11042
11366
  try {
11043
- unlinkSync(pidFile);
11367
+ unlinkSync2(pidFile);
11044
11368
  } catch {
11045
11369
  }
11046
11370
  }
@@ -11050,32 +11374,128 @@ function buildPgUrl(port, password) {
11050
11374
  function quotePgLiteral(value) {
11051
11375
  return `'${value.replace(/'/g, "''")}'`;
11052
11376
  }
11053
- async function stopOrphanPostgres(dataDir = PG_DATA_DIR) {
11054
- const pidFile = join4(dataDir, "postmaster.pid");
11055
- if (!existsSync4(pidFile)) return false;
11377
+ async function stopOrphanPostgres(decidedPid, dataDir = PG_DATA_DIR) {
11378
+ const claim = readPostmasterClaim(dataDir);
11379
+ if (claim === null || claim.pid !== decidedPid) {
11380
+ process.stderr.write(
11381
+ `PG_CTL_SKIPPED_LOCKFILE_MISMATCH decided=${decidedPid} lockfile=${String(claim?.pid ?? null)}
11382
+ `
11383
+ );
11384
+ return false;
11385
+ }
11386
+ const asRoot = process.platform !== "win32" && typeof process.getuid === "function" && process.getuid() === 0;
11387
+ if (asRoot) {
11388
+ process.stderr.write("PG_CTL_SKIPPED_ROOT \u2014 pg_ctl refuses to run as root\n");
11389
+ return false;
11390
+ }
11056
11391
  try {
11057
- const EmbeddedPostgres = (await import("embedded-postgres")).default;
11058
- const pg = new EmbeddedPostgres({
11059
- databaseDir: dataDir,
11060
- user: PG_USER,
11061
- // stop() only reads postmaster.pid and signals pg_ctl — it never
11062
- // authenticates, so the value here is irrelevant.
11063
- password: LEGACY_PG_PASSWORD,
11064
- port: 25432,
11065
- // unused for stop()
11066
- persistent: true,
11067
- onError: () => {
11068
- },
11069
- onLog: () => {
11070
- }
11071
- });
11072
- await pg.stop();
11392
+ const binary = await resolvePgCtl();
11393
+ if (binary === null) {
11394
+ process.stderr.write(`PG_CTL_NOT_FOUND dataDir=${dataDir}
11395
+ `);
11396
+ return false;
11397
+ }
11398
+ const { execa: execa4 } = await import("execa");
11399
+ const { exitCode, stderr } = await execa4(
11400
+ binary,
11401
+ ["stop", "-D", dataDir, "-m", "fast", "-w", "-t", "30"],
11402
+ { reject: false, timeout: 4e4 }
11403
+ );
11404
+ if (exitCode !== 0) {
11405
+ process.stderr.write(
11406
+ `PG_CTL_STOP_FAILED exit=${String(exitCode)} stderr=${stderr.trim().slice(0, 200)}
11407
+ `
11408
+ );
11409
+ return false;
11410
+ }
11073
11411
  return true;
11074
- } catch {
11412
+ } catch (err) {
11413
+ process.stderr.write(
11414
+ `PG_CTL_STOP_FAILED error=${err instanceof Error ? err.message : String(err)}
11415
+ `
11416
+ );
11075
11417
  return false;
11076
11418
  }
11077
11419
  }
11078
- async function startEmbeddedPostgres(dataDir = PG_DATA_DIR, port = 25432, password = LEGACY_PG_PASSWORD) {
11420
+ async function resolvePgCtl() {
11421
+ try {
11422
+ const { createRequire: createRequire2 } = await import("module");
11423
+ const { pathToFileURL } = await import("url");
11424
+ const { dirname: dirname5 } = await import("path");
11425
+ const require2 = createRequire2(import.meta.url);
11426
+ const entry = require2.resolve("embedded-postgres");
11427
+ const module = await import(pathToFileURL(join5(dirname5(entry), "binary.js")).href);
11428
+ const binaries = await module.default?.();
11429
+ const pgCtl = binaries?.pg_ctl;
11430
+ return pgCtl !== void 0 && existsSync5(pgCtl) ? pgCtl : null;
11431
+ } catch {
11432
+ return null;
11433
+ }
11434
+ }
11435
+ function isPostgresAuthFailure(err) {
11436
+ const code = err?.code;
11437
+ return typeof code === "string" && code.length === 5 && code.startsWith("28");
11438
+ }
11439
+ async function stopQuietly(pg) {
11440
+ try {
11441
+ await pg.stop?.();
11442
+ } catch {
11443
+ }
11444
+ }
11445
+ async function startAndWaitUntilReady(pg) {
11446
+ let exitedEarly = null;
11447
+ let resolvedItself = false;
11448
+ const started = pg.start().then(
11449
+ () => {
11450
+ resolvedItself = true;
11451
+ },
11452
+ (err) => {
11453
+ exitedEarly = err ?? new Error("the postmaster exited during startup");
11454
+ }
11455
+ );
11456
+ const deadline = Date.now() + READY_TIMEOUT_MS;
11457
+ for (; ; ) {
11458
+ if (exitedEarly !== null) throw exitedEarly;
11459
+ const probe = pg.getPgClient("postgres");
11460
+ try {
11461
+ await probe.connect();
11462
+ await probe.end();
11463
+ return;
11464
+ } catch (err) {
11465
+ await probe.end().catch(() => {
11466
+ });
11467
+ if (isPostgresAuthFailure(err)) {
11468
+ await stopQuietly(pg);
11469
+ const code = err.code;
11470
+ throw new Error(
11471
+ `Postgres refused the connection: authentication failed (SQLSTATE ${code}). The cluster started and was stopped again. The password the CLI holds does not match the one this data directory was initialised with.`
11472
+ );
11473
+ }
11474
+ }
11475
+ if (resolvedItself) {
11476
+ await started;
11477
+ return;
11478
+ }
11479
+ if (Date.now() > deadline) {
11480
+ await stopQuietly(pg);
11481
+ throw new Error(
11482
+ `Postgres did not accept a connection within ${READY_TIMEOUT_MS / 1e3}s of starting`
11483
+ );
11484
+ }
11485
+ await new Promise((r) => setTimeout(r, 500));
11486
+ }
11487
+ }
11488
+ function tailLogDirectory(logDirectory, lines = 40) {
11489
+ try {
11490
+ const files = readdirSync2(logDirectory).map((name) => join5(logDirectory, name)).map((path) => ({ path, mtime: statSync(path).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
11491
+ const newest = files[0];
11492
+ if (!newest) return [];
11493
+ return readFileSync4(newest.path, "utf-8").split(/\r?\n/).filter((l) => l.trim() !== "").slice(-lines);
11494
+ } catch {
11495
+ return [];
11496
+ }
11497
+ }
11498
+ async function startEmbeddedPostgres(dataDir = PG_DATA_DIR, port = 25432, password = LEGACY_PG_PASSWORD, logDirectory = postgresLogDirFor(dataDir)) {
11079
11499
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
11080
11500
  const capturedLogs = [];
11081
11501
  const capturedErrors = [];
@@ -11104,23 +11524,26 @@ async function startEmbeddedPostgres(dataDir = PG_DATA_DIR, port = 25432, passwo
11104
11524
  if (verboseLog) process.stdout.write("[pg] " + m + "\n");
11105
11525
  }
11106
11526
  });
11107
- const alreadyInitialised = existsSync4(join4(dataDir, "PG_VERSION"));
11527
+ const alreadyInitialised = existsSync5(join5(dataDir, "PG_VERSION"));
11108
11528
  if (alreadyInitialised) clearStalePostmasterPid(dataDir);
11109
11529
  try {
11110
11530
  if (!alreadyInitialised) {
11111
11531
  await pg.initialise();
11112
11532
  }
11113
- await pg.start();
11533
+ applyPostgresLoggingConfig(dataDir, logDirectory);
11534
+ await startAndWaitUntilReady(pg);
11114
11535
  } catch (err) {
11115
11536
  const errMsg = err instanceof Error && err.message ? err.message : String(err);
11116
- const fatalLines = capturedLogs.filter((l) => /\b(FATAL|PANIC|ERROR)\b/.test(l));
11537
+ const fatalLines = [...capturedLogs, ...tailLogDirectory(logDirectory)].filter(
11538
+ (l) => /\b(FATAL|PANIC|ERROR)\b/.test(l)
11539
+ );
11117
11540
  const detail = [
11118
11541
  capturedErrors.length ? `Captured Postgres errors:
11119
11542
  ${capturedErrors.join("\n ")}` : null,
11120
11543
  fatalLines.length ? `Postgres log:
11121
11544
  ${fatalLines.join("\n ")}` : null
11122
11545
  ].filter(Boolean).join("\n ");
11123
- const sharedMemHint = capturedLogs.some((l) => /pre-existing shared memory block/.test(l)) ? "\n \u2192 A previous Postgres crashed without releasing its Windows shared-memory block. Reboot the machine to clear the orphan kernel object, then retry. No data is lost; pg-data is preserved." : "";
11546
+ const sharedMemHint = fatalLines.some((l) => /pre-existing shared memory block/.test(l)) ? "\n \u2192 A previous Postgres crashed without releasing its Windows shared-memory block. Reboot the machine to clear the orphan kernel object, then retry. No data is lost; pg-data is preserved." : "";
11124
11547
  throw new Error(
11125
11548
  `Postgres ${alreadyInitialised ? "start" : "init+start"} failed: ${errMsg}` + (detail ? `
11126
11549
  ${detail}` : "") + sharedMemHint
@@ -11185,14 +11608,17 @@ async function runMigrations2(databaseUrl, opts = {}) {
11185
11608
  const { runMigrations: migrate2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
11186
11609
  await migrate2(databaseUrl, opts);
11187
11610
  }
11188
- var LEGACY_PG_PASSWORD, PG_USER, PG_DATABASE;
11611
+ var LEGACY_PG_PASSWORD, PG_USER, PG_DATABASE, READY_TIMEOUT_MS;
11189
11612
  var init_postgres = __esm({
11190
11613
  "src/lib/postgres.ts"() {
11191
11614
  "use strict";
11192
11615
  init_config();
11616
+ init_pg_logging();
11617
+ init_orphans();
11193
11618
  LEGACY_PG_PASSWORD = "nodalai";
11194
11619
  PG_USER = "nodalai";
11195
11620
  PG_DATABASE = "nodalai";
11621
+ READY_TIMEOUT_MS = 18e4;
11196
11622
  }
11197
11623
  });
11198
11624
 
@@ -14335,6 +14761,22 @@ var init_agents = __esm({
14335
14761
  // (Memory Sprint 2). Pure char budget — token estimation done at call site
14336
14762
  // (length/4). 1500 chars ≈ ~375 tokens, similar to Hermes' 2200+1375 split.
14337
14763
  memoryTokenBudget: integer("memory_token_budget").default(1500).notNull(),
14764
+ /**
14765
+ * La réflexion (boucle d'apprentissage de skills) pour CET agent.
14766
+ *
14767
+ * `null` = suivre le réglage du propriétaire (`entities.reflection_enabled`),
14768
+ * qui est le comportement historique et reste le défaut. `false` = jamais,
14769
+ * même quand le propriétaire l'a activée. `true` n'est pas un
14770
+ * contournement : la porte de l'entité s'applique toujours au-dessus.
14771
+ *
14772
+ * Existe parce que la réflexion était TOUT ou RIEN par propriétaire. Sur le
14773
+ * run 20b73ed1 (09/09), un relecteur qui avait rendu « approve » sans le
14774
+ * moindre constat a déclenché trois appels de réflexion à 0,041 $ — 12 % de
14775
+ * la facture du run, pour apprendre d'un travail qui n'avait rien à
14776
+ * apprendre. Un agent dont le métier est de juger n'accumule pas de savoir-
14777
+ * faire réutilisable ; c'est celui qui PRODUIT qui en accumule.
14778
+ */
14779
+ reflectionEnabled: boolean("reflection_enabled"),
14338
14780
  // Daily budget for coding-CLI runs (code_task, étape B of the
14339
14781
  // subscription-runtimes plan), in NOTIONAL USD — the cost the claude CLI
14340
14782
  // reports even under subscription (codex reports none; its runs count 0
@@ -14588,6 +15030,19 @@ var init_jobs = __esm({
14588
15030
  requestId: text("request_id"),
14589
15031
  parentJobId: uuid("parent_job_id"),
14590
15032
  parentRequestId: text("parent_request_id"),
15033
+ /**
15034
+ * Temps où le job a réellement TOURNÉ, cumulé sur tous ses segments —
15035
+ * suspensions exclues. Un job qui délègue ou attend une approbation sort de
15036
+ * `executeJob` et y rentre plus tard : chaque rentrée ajoute son segment,
15037
+ * elle ne remplace pas le total (apps/runner/src/job/execute.ts,
15038
+ * `dureeCumuleeMs`).
15039
+ *
15040
+ * Ce n'est PAS le temps écoulé de bout en bout. Celui-là se lit
15041
+ * `completed_at − created_at` et ne mérite pas une colonne. La distinction
15042
+ * compte : sur le run 20b73ed1 (09/09/2026), l'orchestrateur a tourné une
15043
+ * trentaine de secondes pour 34 minutes écoulées — le reste était de
15044
+ * l'attente, et les deux chiffres disent des choses différentes.
15045
+ */
14591
15046
  totalDurationMs: integer("total_duration_ms").default(0),
14592
15047
  inputTokens: integer("input_tokens").default(0),
14593
15048
  outputTokens: integer("output_tokens").default(0),
@@ -16530,10 +16985,10 @@ var init_transaction = __esm({
16530
16985
 
16531
16986
  // ../../packages/secrets/src/index.ts
16532
16987
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
16533
- import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
16988
+ import { chmodSync as chmodSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
16534
16989
  import { execFileSync as execFileSync2 } from "child_process";
16535
16990
  import { homedir as homedir2 } from "os";
16536
- import { dirname as dirname3, join as join5 } from "path";
16991
+ import { dirname as dirname3, join as join6 } from "path";
16537
16992
  function restrictFileToOwner2(path) {
16538
16993
  try {
16539
16994
  chmodSync2(path, 384);
@@ -16548,15 +17003,15 @@ function restrictFileToOwner2(path) {
16548
17003
  }
16549
17004
  }
16550
17005
  function masterKeyPath() {
16551
- return join5(homedir2(), ".nodalai", "secrets.key");
17006
+ return join6(homedir2(), ".nodalai", "secrets.key");
16552
17007
  }
16553
17008
  function masterKeyFileMissing(path = masterKeyPath()) {
16554
- return !existsSync5(path);
17009
+ return !existsSync6(path);
16555
17010
  }
16556
17011
  function loadOrCreateMasterKey(path = masterKeyPath()) {
16557
17012
  if (cachedMasterKey) return cachedMasterKey;
16558
- if (existsSync5(path)) {
16559
- const raw = readFileSync4(path, "utf-8").trim();
17013
+ if (existsSync6(path)) {
17014
+ const raw = readFileSync5(path, "utf-8").trim();
16560
17015
  const buf2 = Buffer.from(raw, "base64");
16561
17016
  if (buf2.length !== KEY_BYTES) {
16562
17017
  throw new Error(
@@ -16567,9 +17022,9 @@ function loadOrCreateMasterKey(path = masterKeyPath()) {
16567
17022
  return buf2;
16568
17023
  }
16569
17024
  const dir = dirname3(path);
16570
- if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
17025
+ if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
16571
17026
  const buf = randomBytes2(KEY_BYTES);
16572
- writeFileSync3(path, buf.toString("base64"), { mode: 384 });
17027
+ writeFileSync4(path, buf.toString("base64"), { mode: 384 });
16573
17028
  restrictFileToOwner2(path);
16574
17029
  cachedMasterKey = buf;
16575
17030
  return buf;
@@ -16833,7 +17288,7 @@ var init_master_key_guard = __esm({
16833
17288
  });
16834
17289
 
16835
17290
  // ../../packages/db/src/queries/channel-identity.ts
16836
- import { createHash } from "crypto";
17291
+ import { createHash as createHash2 } from "crypto";
16837
17292
  async function resolveOwnerConversation(db, agentId, channel) {
16838
17293
  if (channel === "telegram") {
16839
17294
  const [row2] = await db.select({ chatId: telegramAllowedChats.chatId }).from(telegramAllowedChats).where(
@@ -16909,7 +17364,7 @@ function credentialsFingerprint(creds) {
16909
17364
  const canonical = JSON.stringify(
16910
17365
  Object.keys(creds).sort().map((k) => [k, creds[k]])
16911
17366
  );
16912
- return createHash("sha256").update(canonical).digest("hex");
17367
+ return createHash2("sha256").update(canonical).digest("hex");
16913
17368
  }
16914
17369
  async function getBindingCredentials(db, agentId, channel) {
16915
17370
  if (channel === "telegram") {
@@ -17014,6 +17469,39 @@ var init_telegram_allowed = __esm({
17014
17469
  }
17015
17470
  });
17016
17471
 
17472
+ // ../../packages/db/src/queries/skill-approval-rules.ts
17473
+ async function dropApprovalRulesForDetachedSkill(db, params) {
17474
+ const [skillRetiree] = await db.select({ requiredBuiltins: agentSkills.requiredBuiltins }).from(agentSkills).where(and(eq(agentSkills.id, params.skillId), eq(agentSkills.entityId, params.entityId))).limit(1);
17475
+ const debloquesParLaSkill = skillRetiree?.requiredBuiltins ?? [];
17476
+ if (debloquesParLaSkill.length === 0) return [];
17477
+ const encoreDebloques = new Set(
17478
+ (await db.select({ requiredBuiltins: agentSkills.requiredBuiltins }).from(agentSkillAssignments).innerJoin(agentSkills, eq(agentSkills.id, agentSkillAssignments.skillId)).where(
17479
+ and(
17480
+ eq(agentSkillAssignments.agentId, params.agentId),
17481
+ eq(agentSkillAssignments.entityId, params.entityId)
17482
+ )
17483
+ )).flatMap((r) => r.requiredBuiltins ?? [])
17484
+ );
17485
+ const candidats = debloquesParLaSkill.filter((t) => !encoreDebloques.has(t));
17486
+ if (candidats.length === 0) return [];
17487
+ const retirees = await db.delete(approvalRules).where(
17488
+ and(
17489
+ eq(approvalRules.entityId, params.entityId),
17490
+ eq(approvalRules.agentId, params.agentId),
17491
+ eq(approvalRules.action, "auto_approve"),
17492
+ inArray(approvalRules.toolName, candidats)
17493
+ )
17494
+ ).returning({ toolName: approvalRules.toolName });
17495
+ return retirees.map((r) => r.toolName);
17496
+ }
17497
+ var init_skill_approval_rules = __esm({
17498
+ "../../packages/db/src/queries/skill-approval-rules.ts"() {
17499
+ "use strict";
17500
+ init_drizzle_orm();
17501
+ init_schema2();
17502
+ }
17503
+ });
17504
+
17017
17505
  // ../../packages/db/src/queries/mcp-approval-context.ts
17018
17506
  function slugToPrefix(slug) {
17019
17507
  return slug.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
@@ -17547,6 +18035,7 @@ __export(src_exports, {
17547
18035
  decryptChannelSecret: () => decryptChannelSecret,
17548
18036
  decryptCredentialForDisplay: () => decryptCredentialForDisplay,
17549
18037
  desc: () => desc,
18038
+ dropApprovalRulesForDetachedSkill: () => dropApprovalRulesForDetachedSkill,
17550
18039
  encryptChannelCredentials: () => encryptChannelCredentials,
17551
18040
  encryptChannelSecret: () => encryptChannelSecret,
17552
18041
  entities: () => entities,
@@ -17628,6 +18117,7 @@ var init_src4 = __esm({
17628
18117
  init_master_key_guard();
17629
18118
  init_telegram_owner();
17630
18119
  init_telegram_allowed();
18120
+ init_skill_approval_rules();
17631
18121
  init_mcp_approval_context();
17632
18122
  init_channel_identity();
17633
18123
  init_agents2();
@@ -17692,7 +18182,7 @@ var init_local_auth = __esm({
17692
18182
  });
17693
18183
 
17694
18184
  // ../../packages/auth/src/lib/constant-time.ts
17695
- import { createHash as createHash2, timingSafeEqual } from "crypto";
18185
+ import { createHash as createHash3, timingSafeEqual } from "crypto";
17696
18186
  var init_constant_time = __esm({
17697
18187
  "../../packages/auth/src/lib/constant-time.ts"() {
17698
18188
  "use strict";
@@ -17865,22 +18355,44 @@ var init_env = __esm({
17865
18355
  });
17866
18356
 
17867
18357
  // src/lib/ports.ts
18358
+ var ports_exports = {};
18359
+ __export(ports_exports, {
18360
+ DEFAULT_PORTS: () => DEFAULT_PORTS,
18361
+ findFreePort: () => findFreePort,
18362
+ formatPortRotation: () => formatPortRotation,
18363
+ isPortBindable: () => isPortBindable,
18364
+ pidListeningOnPort: () => pidListeningOnPort
18365
+ });
17868
18366
  import { createServer } from "net";
18367
+ async function pidListeningOnPort(port) {
18368
+ const { execa: execa4 } = await import("execa");
18369
+ try {
18370
+ const { stdout } = await execa4("netstat", ["-ano"], { reject: false });
18371
+ for (const line2 of stdout.split(/\r?\n/)) {
18372
+ const m = line2.match(/\s+TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/);
18373
+ if (m && m[1] && m[2] && Number.parseInt(m[1], 10) === port) {
18374
+ return Number.parseInt(m[2], 10);
18375
+ }
18376
+ }
18377
+ } catch {
18378
+ }
18379
+ return null;
18380
+ }
17869
18381
  function isPortBindable(port) {
17870
- return new Promise((resolve3) => {
18382
+ return new Promise((resolve4) => {
17871
18383
  const server = createServer();
17872
- server.once("error", () => resolve3(false));
18384
+ server.once("error", () => resolve4(false));
17873
18385
  try {
17874
18386
  server.listen(port, "127.0.0.1", () => {
17875
- server.close(() => resolve3(true));
18387
+ server.close(() => resolve4(true));
17876
18388
  });
17877
18389
  } catch {
17878
- resolve3(false);
18390
+ resolve4(false);
17879
18391
  }
17880
18392
  });
17881
18393
  }
17882
18394
  function findFreePort(preferred, maxAttempts = 20) {
17883
- return new Promise((resolve3, reject) => {
18395
+ return new Promise((resolve4, reject) => {
17884
18396
  let attempt = 0;
17885
18397
  const tryPort = (port) => {
17886
18398
  if (attempt >= maxAttempts) {
@@ -17896,7 +18408,7 @@ function findFreePort(preferred, maxAttempts = 20) {
17896
18408
  server.listen(port, "127.0.0.1", () => {
17897
18409
  const addr = server.address();
17898
18410
  const actualPort = typeof addr === "object" && addr ? addr.port : port;
17899
- server.close(() => resolve3(actualPort));
18411
+ server.close(() => resolve4(actualPort));
17900
18412
  });
17901
18413
  server.on("error", () => {
17902
18414
  tryPort(port + 1);
@@ -17905,59 +18417,309 @@ function findFreePort(preferred, maxAttempts = 20) {
17905
18417
  tryPort(preferred);
17906
18418
  });
17907
18419
  }
18420
+ function formatPortRotation(changes) {
18421
+ const lines = [];
18422
+ for (const c of changes) {
18423
+ lines.push(` - ${c.name}: ${c.from} \u2192 ${c.to}`);
18424
+ if (c.heldBy === null) continue;
18425
+ lines.push(
18426
+ ` :${c.from} is STILL HELD by pid ${c.heldBy}, which was not stopped: nothing here could confirm what it is.`,
18427
+ ` It is left running and the port is abandoned, not freed. Check that pid and stop it yourself if it is ours.`
18428
+ );
18429
+ }
18430
+ return lines;
18431
+ }
18432
+ var DEFAULT_PORTS;
17908
18433
  var init_ports = __esm({
17909
18434
  "src/lib/ports.ts"() {
17910
18435
  "use strict";
18436
+ DEFAULT_PORTS = {
18437
+ web: 3e3,
18438
+ runner: 3001,
18439
+ postgres: 25432
18440
+ };
17911
18441
  }
17912
18442
  });
17913
18443
 
17914
- // src/lib/log-rotation.ts
17915
- import { existsSync as existsSync6, renameSync, rmSync, statSync } from "fs";
17916
- function rotateLogIfNeeded(logFile, maxBytes = LOG_ROTATE_MAX_BYTES) {
18444
+ // src/lib/watchdog.ts
18445
+ var watchdog_exports = {};
18446
+ __export(watchdog_exports, {
18447
+ probeRunnerHealth: () => probeRunnerHealth,
18448
+ startHealthWatchdog: () => startHealthWatchdog
18449
+ });
18450
+ async function probeRunnerHealth(runnerUrl) {
17917
18451
  try {
17918
- if (!existsSync6(logFile)) return false;
17919
- if (statSync(logFile).size < maxBytes) return false;
17920
- const archived = `${logFile}.1`;
17921
- rmSync(archived, { force: true });
17922
- renameSync(logFile, archived);
17923
- return true;
18452
+ const controller = new AbortController();
18453
+ const t = setTimeout(() => controller.abort(), 3e3);
18454
+ const res = await fetch(`${runnerUrl}/api/health`, { signal: controller.signal });
18455
+ clearTimeout(t);
18456
+ if (res.status === 200) return { state: "healthy", detail: "" };
18457
+ const body = await res.json().catch(() => null);
18458
+ const detail = body?.db === "error" ? "database unreachable (runner reports db: error)" : `runner is unhealthy (HTTP ${res.status})`;
18459
+ return { state: "degraded", detail };
17924
18460
  } catch (err) {
17925
- console.warn(
17926
- `[nodalai] log rotation skipped for ${logFile}: ${err instanceof Error ? err.message : String(err)}`
17927
- );
17928
- return false;
17929
- }
17930
- }
17931
- var LOG_ROTATE_MAX_BYTES;
17932
- var init_log_rotation = __esm({
17933
- "src/lib/log-rotation.ts"() {
17934
- "use strict";
17935
- LOG_ROTATE_MAX_BYTES = 20 * 1024 * 1024;
18461
+ const aborted = err instanceof Error && err.name === "AbortError";
18462
+ return {
18463
+ state: "unreachable",
18464
+ detail: aborted ? "runner did not answer /api/health within 3s (busy or stalled)" : "runner is not answering /api/health"
18465
+ };
17936
18466
  }
17937
- });
17938
-
17939
- // src/lib/processes.ts
17940
- import { execa as execa2 } from "execa";
17941
- import { createWriteStream, openSync, writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
17942
- import { join as join6, dirname as dirname4, resolve } from "path";
17943
- import { fileURLToPath as fileURLToPath3 } from "url";
17944
- async function killProcessTree(child) {
17945
- const pid = child.pid;
17946
- if (!pid) return;
17947
- await killPidTree(pid);
17948
- }
17949
- async function descendantPidsWin(root, timeoutMs) {
17950
- const snapshot2 = await processSnapshotWin(timeoutMs);
17951
- return walkDescendants(snapshot2, root).map((p) => p.pid);
17952
18467
  }
17953
- async function processSnapshotWin(timeoutMs = SNAPSHOT_BUDGET_MS) {
17954
- if (process.platform !== "win32") return /* @__PURE__ */ new Map();
17955
- if (!inFlightSnapshot) {
17956
- inFlightSnapshot = readProcessTableWin(timeoutMs).finally(() => {
17957
- inFlightSnapshot = null;
17958
- });
17959
- }
17960
- return inFlightSnapshot;
18468
+ function startHealthWatchdog(opts) {
18469
+ const intervalMs = opts.intervalMs ?? 3e4;
18470
+ const failureThreshold = opts.failureThreshold ?? 2;
18471
+ let announced = "healthy";
18472
+ let consecutiveFailures = 0;
18473
+ let stopped = false;
18474
+ let inFlight = false;
18475
+ const tick = async () => {
18476
+ if (stopped || inFlight) return;
18477
+ inFlight = true;
18478
+ try {
18479
+ const result = await opts.probe();
18480
+ if (stopped) return;
18481
+ if (result.state === "healthy") {
18482
+ consecutiveFailures = 0;
18483
+ if (announced !== "healthy") {
18484
+ announced = "healthy";
18485
+ opts.onTransition("healthy", "services are answering again");
18486
+ }
18487
+ return;
18488
+ }
18489
+ consecutiveFailures += 1;
18490
+ if (consecutiveFailures < failureThreshold) return;
18491
+ if (announced !== result.state) {
18492
+ announced = result.state;
18493
+ opts.onTransition(result.state, result.detail);
18494
+ }
18495
+ } finally {
18496
+ inFlight = false;
18497
+ }
18498
+ };
18499
+ const timer2 = setInterval(() => void tick(), intervalMs);
18500
+ timer2.unref?.();
18501
+ return {
18502
+ stop: () => {
18503
+ stopped = true;
18504
+ clearInterval(timer2);
18505
+ }
18506
+ };
18507
+ }
18508
+ var init_watchdog = __esm({
18509
+ "src/lib/watchdog.ts"() {
18510
+ "use strict";
18511
+ }
18512
+ });
18513
+
18514
+ // src/lib/already-running.ts
18515
+ function decideStart(evidence) {
18516
+ if (!evidence.runnerHealthy || evidence.postmasterPid === null) return { proceed: true };
18517
+ return {
18518
+ proceed: false,
18519
+ reason: "ALREADY_RUNNING",
18520
+ message: formatAlreadyRunning(evidence)
18521
+ };
18522
+ }
18523
+ function formatAlreadyRunning(evidence) {
18524
+ const parts = [
18525
+ evidence.runnerPid === null ? null : `runner pid ${evidence.runnerPid}`,
18526
+ evidence.webPid === null ? null : `web pid ${evidence.webPid}`,
18527
+ `postgres pid ${String(evidence.postmasterPid)}`
18528
+ ].filter((p) => p !== null);
18529
+ const runnerRef = evidence.runnerPid === null ? "that port" : `pid ${evidence.runnerPid}`;
18530
+ return `Nodal-Agents looks already running (${parts.join(", ")}) \u2014 use \`nodal-agents down\` first.
18531
+ Nothing was stopped, and nothing was signalled.
18532
+ What was observed, and only that: something answered /api/health with 200 on the
18533
+ configured runner port, and postmaster.pid in our data directory names a live
18534
+ postmaster. That is usually this install, up and serving.
18535
+ If it is NOT yours: check ${runnerRef} to see whose server is on that port, free the
18536
+ port or change it in ~/.nodalai/config.json, then run \`up\` again.`;
18537
+ }
18538
+ async function decideStartFromProbes(opts) {
18539
+ const { livePostmasterPid: livePostmasterPid2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
18540
+ const postmasterPid = livePostmasterPid2(opts.dataDir);
18541
+ if (postmasterPid === null) return { proceed: true };
18542
+ const { probeRunnerHealth: probeRunnerHealth2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
18543
+ const health = await probeRunnerHealth2(`http://127.0.0.1:${opts.runnerPort}`);
18544
+ if (health.state !== "healthy") return { proceed: true };
18545
+ const { pidListeningOnPort: pidListeningOnPort2 } = await Promise.resolve().then(() => (init_ports(), ports_exports));
18546
+ return decideStart({
18547
+ runnerHealthy: true,
18548
+ postmasterPid,
18549
+ runnerPid: await pidListeningOnPort2(opts.runnerPort),
18550
+ webPid: await pidListeningOnPort2(opts.webPort)
18551
+ });
18552
+ }
18553
+ var AlreadyRunningError;
18554
+ var init_already_running = __esm({
18555
+ "src/lib/already-running.ts"() {
18556
+ "use strict";
18557
+ AlreadyRunningError = class extends Error {
18558
+ reason = "ALREADY_RUNNING";
18559
+ constructor(message) {
18560
+ super(message);
18561
+ this.name = "AlreadyRunningError";
18562
+ }
18563
+ };
18564
+ }
18565
+ });
18566
+
18567
+ // src/lib/pid-confirm.ts
18568
+ function confirmRecordedPid(input) {
18569
+ const { recorded, live, tableRead, ownedPostgresPids: ownedPostgresPids2 } = input;
18570
+ if (!tableRead) {
18571
+ return {
18572
+ killable: false,
18573
+ code: "TABLE_UNREADABLE",
18574
+ detail: `pid ${recorded.pid} could not be confirmed: the process table did not answer`
18575
+ };
18576
+ }
18577
+ if (live === void 0) {
18578
+ return {
18579
+ killable: false,
18580
+ code: "PID_GONE",
18581
+ detail: `pid ${recorded.pid} is no longer running`
18582
+ };
18583
+ }
18584
+ if (isPostgresExe(live.name) && !ownedPostgresPids2.has(live.pid)) {
18585
+ return {
18586
+ killable: false,
18587
+ code: "FOREIGN_POSTGRES",
18588
+ detail: `pid ${recorded.pid} is a ${live.name} that our data dir does not claim`
18589
+ };
18590
+ }
18591
+ if (recorded.name !== void 0 && !sameExe(recorded.name, live.name)) {
18592
+ return {
18593
+ killable: false,
18594
+ code: "BINARY_CHANGED",
18595
+ detail: `pid ${recorded.pid} was recorded as ${recorded.name} and now runs ${live.name}`
18596
+ };
18597
+ }
18598
+ if (recorded.startedAt !== void 0 && recorded.startedAt !== live.startedAt) {
18599
+ return {
18600
+ killable: false,
18601
+ code: "PID_RECYCLED",
18602
+ detail: `pid ${recorded.pid} started at ${live.startedAt}, recorded at ${recorded.startedAt}`
18603
+ };
18604
+ }
18605
+ if (recorded.startedAt === void 0 && recorded.name === void 0) {
18606
+ return {
18607
+ killable: false,
18608
+ code: "IDENTITY_NOT_RECORDED",
18609
+ detail: `pid ${recorded.pid} was recorded as a bare number: nothing identifies it now`
18610
+ };
18611
+ }
18612
+ return OK;
18613
+ }
18614
+ function sameExe(a, b2) {
18615
+ return a.toLowerCase() === b2.toLowerCase();
18616
+ }
18617
+ function isPostgresExe(name) {
18618
+ return sameExe(name, POSTGRES_EXE);
18619
+ }
18620
+ function confirmTree(root, members, ownedPostgresPids2) {
18621
+ const verdict = confirmRecordedPid(root);
18622
+ const foreign = [];
18623
+ const killable = [];
18624
+ for (const member of members) {
18625
+ if (isPostgresExe(member.name) && !ownedPostgresPids2.has(member.pid)) foreign.push(member);
18626
+ else killable.push(member);
18627
+ }
18628
+ return {
18629
+ root: verdict,
18630
+ treeKillAllowed: verdict.killable && foreign.length === 0,
18631
+ foreign,
18632
+ killable
18633
+ };
18634
+ }
18635
+ function formatRefusal(refusal) {
18636
+ return `KILL_REFUSED code=${refusal.code} ${refusal.detail}`;
18637
+ }
18638
+ function unconfirmedIdentityNotice(label, pid) {
18639
+ return `${label} (pid ${pid}) \u2014 no process table on this platform to confirm it with`;
18640
+ }
18641
+ var OK, POSTGRES_EXE;
18642
+ var init_pid_confirm = __esm({
18643
+ "src/lib/pid-confirm.ts"() {
18644
+ "use strict";
18645
+ OK = { killable: true };
18646
+ POSTGRES_EXE = "postgres.exe";
18647
+ }
18648
+ });
18649
+
18650
+ // src/lib/log-rotation.ts
18651
+ import { existsSync as existsSync7, renameSync as renameSync2, rmSync, statSync as statSync2 } from "fs";
18652
+ function rotateLogIfNeeded(logFile, maxBytes = LOG_ROTATE_MAX_BYTES) {
18653
+ try {
18654
+ if (!existsSync7(logFile)) return false;
18655
+ if (statSync2(logFile).size < maxBytes) return false;
18656
+ const archived = `${logFile}.1`;
18657
+ rmSync(archived, { force: true });
18658
+ renameSync2(logFile, archived);
18659
+ return true;
18660
+ } catch (err) {
18661
+ console.warn(
18662
+ `[nodalai] log rotation skipped for ${logFile}: ${err instanceof Error ? err.message : String(err)}`
18663
+ );
18664
+ return false;
18665
+ }
18666
+ }
18667
+ var LOG_ROTATE_MAX_BYTES;
18668
+ var init_log_rotation = __esm({
18669
+ "src/lib/log-rotation.ts"() {
18670
+ "use strict";
18671
+ LOG_ROTATE_MAX_BYTES = 20 * 1024 * 1024;
18672
+ }
18673
+ });
18674
+
18675
+ // src/lib/launcher-log.ts
18676
+ import { appendFileSync } from "fs";
18677
+ import { join as join7 } from "path";
18678
+ function formatLauncherLine(at, event, detail) {
18679
+ return `${at.toISOString()} ${event} ${detail.replace(/\s*\r?\n\s*/g, " ").trim()}
18680
+ `;
18681
+ }
18682
+ function logLauncherEvent(event, detail) {
18683
+ try {
18684
+ rotateLogIfNeeded(LAUNCHER_LOG);
18685
+ appendFileSync(LAUNCHER_LOG, formatLauncherLine(/* @__PURE__ */ new Date(), event, detail), "utf-8");
18686
+ } catch {
18687
+ }
18688
+ }
18689
+ var LAUNCHER_LOG;
18690
+ var init_launcher_log = __esm({
18691
+ "src/lib/launcher-log.ts"() {
18692
+ "use strict";
18693
+ init_config();
18694
+ init_log_rotation();
18695
+ LAUNCHER_LOG = join7(LOG_DIR, "launcher.log");
18696
+ }
18697
+ });
18698
+
18699
+ // src/lib/processes.ts
18700
+ import { execa as execa2 } from "execa";
18701
+ import { createWriteStream, openSync, writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync8 } from "fs";
18702
+ import { join as join8, dirname as dirname4, resolve as resolve2 } from "path";
18703
+ import { fileURLToPath as fileURLToPath3 } from "url";
18704
+ function asLiveRecord(rec) {
18705
+ return { pid: rec.pid, startedAt: rec.startedAt, name: rec.name ?? "" };
18706
+ }
18707
+ function asLive(rec) {
18708
+ return rec === void 0 ? void 0 : asLiveRecord(rec);
18709
+ }
18710
+ async function killProcessTree(child) {
18711
+ const pid = child.pid;
18712
+ if (!pid) return;
18713
+ await killPidTree(pid);
18714
+ }
18715
+ async function processSnapshotWin(timeoutMs = SNAPSHOT_BUDGET_MS) {
18716
+ if (process.platform !== "win32") return /* @__PURE__ */ new Map();
18717
+ if (!inFlightSnapshot) {
18718
+ inFlightSnapshot = readProcessTableWin(timeoutMs).finally(() => {
18719
+ inFlightSnapshot = null;
18720
+ });
18721
+ }
18722
+ return inFlightSnapshot;
17961
18723
  }
17962
18724
  async function readProcessTableWin(timeoutMs) {
17963
18725
  const out = /* @__PURE__ */ new Map();
@@ -17997,12 +18759,13 @@ async function readProcessTableWin(timeoutMs) {
17997
18759
  }
17998
18760
  for (const line2 of rows.split(/\r?\n/)) {
17999
18761
  const parts = line2.trim().split("|");
18000
- if (parts.length !== 3) continue;
18762
+ if (parts.length !== 4) continue;
18001
18763
  const pid = Number.parseInt(parts[0] ?? "", 10);
18002
18764
  const ppid = Number.parseInt(parts[1] ?? "", 10);
18003
18765
  const startedAt = (parts[2] ?? "").trim();
18766
+ const name = (parts[3] ?? "").trim();
18004
18767
  if (!Number.isInteger(pid) || !Number.isInteger(ppid) || startedAt === "") continue;
18005
- out.set(pid, { pid, ppid, startedAt });
18768
+ out.set(pid, { pid, ppid, startedAt, name });
18006
18769
  }
18007
18770
  if (out.size > 0) {
18008
18771
  lastSnapshotFailure = null;
@@ -18036,17 +18799,105 @@ function walkDescendants(snapshot2, root) {
18036
18799
  }
18037
18800
  return out;
18038
18801
  }
18039
- async function killPidTree(pid) {
18802
+ async function ownedPostgres() {
18803
+ const { postgresProcessesForDataDir: postgresProcessesForDataDir2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
18804
+ return new Set((await postgresProcessesForDataDir2()).owned);
18805
+ }
18806
+ async function ownedPostgresIfAnyInvolved(candidates) {
18807
+ const EMPTY = /* @__PURE__ */ new Set();
18808
+ const anyPostgres = candidates.some((c) => (c.name ?? "").toLowerCase() === "postgres.exe");
18809
+ return anyPostgres ? await ownedPostgres() : EMPTY;
18810
+ }
18811
+ function vetoesRoot(code, fromRecord) {
18812
+ if (ROOT_KILL_VETO.has(code)) return true;
18813
+ return fromRecord && code === "IDENTITY_NOT_RECORDED";
18814
+ }
18815
+ async function reconfirmMembers(members, rootPid, ownedPostgresPids2) {
18816
+ const fresh = await processSnapshotWin(SHUTDOWN_SNAPSHOT_BUDGET_MS);
18817
+ if (fresh.size === 0) {
18818
+ process.stderr.write(
18819
+ `KILL_UNCONFIRMED code=TABLE_UNREADABLE pid=${rootPid} the tree members are swept on the earlier reading: the table would not answer a second time
18820
+ `
18821
+ );
18822
+ return [...members];
18823
+ }
18824
+ const out = [];
18825
+ for (const member of members) {
18826
+ const verdict = confirmRecordedPid({
18827
+ // An empty name is a row WMI gave us without one; offering it as a
18828
+ // recorded name would read as "it changed" the moment the second reading
18829
+ // has one. One less proof, never a false refusal.
18830
+ recorded: {
18831
+ pid: member.pid,
18832
+ startedAt: member.startedAt,
18833
+ ...member.name === "" ? {} : { name: member.name }
18834
+ },
18835
+ live: asLive(fresh.get(member.pid)),
18836
+ tableRead: true,
18837
+ ownedPostgresPids: ownedPostgresPids2
18838
+ });
18839
+ if (verdict.killable) {
18840
+ out.push(member);
18841
+ continue;
18842
+ }
18843
+ if (verdict.code !== "PID_GONE") process.stderr.write(`${formatRefusal(verdict)}
18844
+ `);
18845
+ }
18846
+ return out;
18847
+ }
18848
+ async function killPidTree(pid, ownedPostgresPids2, recorded) {
18040
18849
  if (process.platform === "win32") {
18041
- const descendants = await descendantPidsWin(pid, SHUTDOWN_SNAPSHOT_BUDGET_MS);
18042
- try {
18043
- await execa2("taskkill", ["/T", "/F", "/PID", String(pid)], { reject: false });
18044
- } catch {
18850
+ const snapshot2 = await processSnapshotWin(SHUTDOWN_SNAPSHOT_BUDGET_MS);
18851
+ const descendants = walkDescendants(snapshot2, pid);
18852
+ const owned = ownedPostgresPids2 ?? await ownedPostgresIfAnyInvolved([...descendants, snapshot2.get(pid) ?? {}]);
18853
+ const fromRecord = recorded !== void 0;
18854
+ const { treeKillAllowed, root, foreign, killable } = confirmTree(
18855
+ {
18856
+ recorded: recorded ?? { pid },
18857
+ live: asLive(snapshot2.get(pid)),
18858
+ tableRead: snapshot2.size > 0,
18859
+ ownedPostgresPids: owned
18860
+ },
18861
+ descendants.map(asLiveRecord),
18862
+ owned
18863
+ );
18864
+ if (!root.killable && root.code === "TABLE_UNREADABLE") {
18865
+ if (fromRecord) {
18866
+ process.stderr.write(`${formatRefusal(root)}
18867
+ `);
18868
+ return;
18869
+ }
18870
+ process.stderr.write(
18871
+ `KILL_UNCONFIRMED code=TABLE_UNREADABLE pid=${pid} is a live handle, not a record: the tree kill proceeds with the identity unconfirmed
18872
+ `
18873
+ );
18045
18874
  }
18046
- for (const child of descendants) {
18047
- if (!isPidAlive(child)) continue;
18875
+ const rootVetoed = !root.killable && vetoesRoot(root.code, fromRecord);
18876
+ if (rootVetoed) process.stderr.write(`${formatRefusal(root)}
18877
+ `);
18878
+ const rootGone = !root.killable && root.code === "PID_GONE";
18879
+ for (const stranger of foreign) {
18880
+ process.stderr.write(
18881
+ `KILL_REFUSED code=FOREIGN_POSTGRES pid=${stranger.pid} is a ${stranger.name} in the tree of pid ${pid} that our data dir does not claim; the tree kill was done pid by pid around it
18882
+ `
18883
+ );
18884
+ }
18885
+ if (treeKillAllowed) {
18886
+ try {
18887
+ await execa2("taskkill", ["/T", "/F", "/PID", String(pid)], { reject: false });
18888
+ } catch {
18889
+ }
18890
+ } else if (!rootVetoed && !rootGone) {
18891
+ try {
18892
+ await execa2("taskkill", ["/F", "/PID", String(pid)], { reject: false });
18893
+ } catch {
18894
+ }
18895
+ }
18896
+ const vouched = killable.length > 0 ? await reconfirmMembers(killable, pid, owned) : [];
18897
+ for (const child of vouched) {
18898
+ if (!isPidAlive(child.pid)) continue;
18048
18899
  try {
18049
- await execa2("taskkill", ["/F", "/PID", String(child)], { reject: false });
18900
+ await execa2("taskkill", ["/F", "/PID", String(child.pid)], { reject: false });
18050
18901
  } catch {
18051
18902
  }
18052
18903
  }
@@ -18087,14 +18938,14 @@ function resolveAppDir(appName) {
18087
18938
  const fileDir = dirname4(here);
18088
18939
  let cursor = fileDir;
18089
18940
  for (let i = 0; i < 8; i++) {
18090
- if (existsSync7(join6(cursor, "pnpm-workspace.yaml"))) {
18091
- return join6(cursor, appName);
18941
+ if (existsSync8(join8(cursor, "pnpm-workspace.yaml"))) {
18942
+ return join8(cursor, appName);
18092
18943
  }
18093
18944
  const parent = dirname4(cursor);
18094
18945
  if (parent === cursor) break;
18095
18946
  cursor = parent;
18096
18947
  }
18097
- return resolve(fileDir, "..", "..", "..", "..", appName);
18948
+ return resolve2(fileDir, "..", "..", "..", "..", appName);
18098
18949
  }
18099
18950
  function spawnWiring(logFile, detach) {
18100
18951
  if (!detach) return { stdin: "ignore", stdout: "pipe", stderr: "pipe", detached: false };
@@ -18105,7 +18956,7 @@ function spawnWiring(logFile, detach) {
18105
18956
  };
18106
18957
  }
18107
18958
  function spawnRunner(env, opts = {}) {
18108
- const logFile = join6(LOG_DIR, "runner.log");
18959
+ const logFile = join8(LOG_DIR, "runner.log");
18109
18960
  rotateLogIfNeeded(logFile);
18110
18961
  const outStream = createWriteStream(logFile, { flags: "a" });
18111
18962
  let bin;
@@ -18113,7 +18964,7 @@ function spawnRunner(env, opts = {}) {
18113
18964
  let cwd;
18114
18965
  if (BUNDLED_ROOT) {
18115
18966
  bin = process.execPath;
18116
- args = [join6(BUNDLED_ROOT, "runner.js")];
18967
+ args = [join8(BUNDLED_ROOT, "runner.js")];
18117
18968
  cwd = BUNDLED_ROOT;
18118
18969
  } else {
18119
18970
  const runnerDir = resolveAppDir("apps/runner");
@@ -18151,7 +19002,7 @@ env keys: ${Object.keys(env).join(",")}
18151
19002
  return child;
18152
19003
  }
18153
19004
  function spawnWeb(env, opts = {}) {
18154
- const logFile = join6(LOG_DIR, "web.log");
19005
+ const logFile = join8(LOG_DIR, "web.log");
18155
19006
  rotateLogIfNeeded(logFile);
18156
19007
  const outStream = createWriteStream(logFile, { flags: "a" });
18157
19008
  let bin;
@@ -18160,8 +19011,8 @@ function spawnWeb(env, opts = {}) {
18160
19011
  let modeLabel;
18161
19012
  if (BUNDLED_ROOT) {
18162
19013
  bin = process.execPath;
18163
- args = [join6(BUNDLED_ROOT, "web", "server.js")];
18164
- cwd = join6(BUNDLED_ROOT, "web");
19014
+ args = [join8(BUNDLED_ROOT, "web", "server.js")];
19015
+ cwd = join8(BUNDLED_ROOT, "web");
18165
19016
  modeLabel = "bundled-standalone";
18166
19017
  } else {
18167
19018
  const webDir = resolveAppDir("apps/web");
@@ -18200,33 +19051,52 @@ env keys: ${Object.keys(env).join(",")}
18200
19051
  }
18201
19052
  function resolveLocalBin(packageDir, binName) {
18202
19053
  const ext = process.platform === "win32" ? ".CMD" : "";
18203
- return join6(packageDir, "node_modules", ".bin", `${binName}${ext}`);
19054
+ return join8(packageDir, "node_modules", ".bin", `${binName}${ext}`);
18204
19055
  }
18205
19056
  async function recordServiceTree(pids) {
18206
19057
  if (process.platform !== "win32") return;
18207
19058
  const snapshot2 = await processSnapshotWin();
18208
19059
  if (snapshot2.size === 0) return;
18209
19060
  const children = [];
19061
+ const roots = [];
18210
19062
  const seen = /* @__PURE__ */ new Set();
18211
19063
  for (const root of [pids.runner, pids.web]) {
18212
19064
  if (!root) continue;
18213
- for (const rec of walkDescendants(snapshot2, root)) {
18214
- if (seen.has(rec.pid)) continue;
18215
- seen.add(rec.pid);
18216
- children.push(rec);
19065
+ const rec = snapshot2.get(root);
19066
+ if (rec) roots.push(rec);
19067
+ for (const rec2 of walkDescendants(snapshot2, root)) {
19068
+ if (seen.has(rec2.pid)) continue;
19069
+ seen.add(rec2.pid);
19070
+ children.push(rec2);
18217
19071
  }
18218
19072
  }
18219
- writePids({ ...pids, children });
19073
+ writePids({ ...pids, children, roots });
19074
+ }
19075
+ function recordedRoot(pids, pid) {
19076
+ const found = pids?.roots?.find((r) => r.pid === pid);
19077
+ if (!found) return { pid };
19078
+ return { pid, startedAt: found.startedAt, ...found.name ? { name: found.name } : {} };
18220
19079
  }
18221
- async function sweepRecordedChildren(children) {
19080
+ async function sweepRecordedChildren(children, ownedPostgresPids2) {
18222
19081
  if (process.platform !== "win32" || children.length === 0) return [];
18223
19082
  const alive = children.filter((c) => isPidAlive(c.pid));
18224
19083
  if (alive.length === 0) return [];
18225
19084
  const snapshot2 = await processSnapshotWin();
19085
+ const tableRead = snapshot2.size > 0;
19086
+ const owned = ownedPostgresPids2 ?? await ownedPostgresIfAnyInvolved(alive.flatMap((rec) => [rec, snapshot2.get(rec.pid) ?? {}]));
18226
19087
  const killed = [];
18227
19088
  for (const rec of alive) {
18228
- const now = snapshot2.get(rec.pid);
18229
- if (!now || now.startedAt !== rec.startedAt) continue;
19089
+ const verdict = confirmRecordedPid({
19090
+ recorded: rec,
19091
+ live: asLive(snapshot2.get(rec.pid)),
19092
+ tableRead,
19093
+ ownedPostgresPids: owned
19094
+ });
19095
+ if (!verdict.killable) {
19096
+ if (verdict.code !== "PID_GONE") process.stderr.write(`${formatRefusal(verdict)}
19097
+ `);
19098
+ continue;
19099
+ }
18230
19100
  try {
18231
19101
  await execa2("taskkill", ["/F", "/PID", String(rec.pid)], { reject: false });
18232
19102
  killed.push(rec.pid);
@@ -18236,21 +19106,21 @@ async function sweepRecordedChildren(children) {
18236
19106
  return killed;
18237
19107
  }
18238
19108
  function writePids(pids) {
18239
- writeFileSync4(join6(PID_DIR, "processes.json"), JSON.stringify(pids, null, 2), "utf-8");
19109
+ writeFileSync5(join8(PID_DIR, "processes.json"), JSON.stringify(pids, null, 2), "utf-8");
18240
19110
  }
18241
19111
  function readPids() {
18242
- const pidFile = join6(PID_DIR, "processes.json");
18243
- if (!existsSync7(pidFile)) return null;
19112
+ const pidFile = join8(PID_DIR, "processes.json");
19113
+ if (!existsSync8(pidFile)) return null;
18244
19114
  try {
18245
- return JSON.parse(readFileSync5(pidFile, "utf-8"));
19115
+ return JSON.parse(readFileSync6(pidFile, "utf-8"));
18246
19116
  } catch {
18247
19117
  return null;
18248
19118
  }
18249
19119
  }
18250
19120
  function clearPids() {
18251
- const pidFile = join6(PID_DIR, "processes.json");
18252
- if (existsSync7(pidFile)) {
18253
- writeFileSync4(pidFile, JSON.stringify({}), "utf-8");
19121
+ const pidFile = join8(PID_DIR, "processes.json");
19122
+ if (existsSync8(pidFile)) {
19123
+ writeFileSync5(pidFile, JSON.stringify({}), "utf-8");
18254
19124
  }
18255
19125
  }
18256
19126
  async function waitForHealth(url, timeoutMs = 3e4) {
@@ -18290,12 +19160,13 @@ async function assertWebRenders(url, timeoutMs = 3e5) {
18290
19160
  function sleep(ms) {
18291
19161
  return new Promise((r) => setTimeout(r, ms));
18292
19162
  }
18293
- var SNAPSHOT_BUDGET_MS, SHUTDOWN_SNAPSHOT_BUDGET_MS, inFlightSnapshot, PROCESS_TABLE_QUERIES, lastSnapshotFailure, BUNDLED_ROOT;
19163
+ var SNAPSHOT_BUDGET_MS, SHUTDOWN_SNAPSHOT_BUDGET_MS, inFlightSnapshot, PROCESS_TABLE_QUERIES, lastSnapshotFailure, ROOT_KILL_VETO, BUNDLED_ROOT;
18294
19164
  var init_processes = __esm({
18295
19165
  "src/lib/processes.ts"() {
18296
19166
  "use strict";
18297
19167
  init_config();
18298
19168
  init_log_rotation();
19169
+ init_pid_confirm();
18299
19170
  SNAPSHOT_BUDGET_MS = 2e4;
18300
19171
  SHUTDOWN_SNAPSHOT_BUDGET_MS = 6e3;
18301
19172
  inFlightSnapshot = null;
@@ -18303,7 +19174,7 @@ var init_processes = __esm({
18303
19174
  {
18304
19175
  name: "Get-CimInstance",
18305
19176
  // -Property keeps WMI from materialising every column of every process.
18306
- command: 'Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,CreationDate | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate.Ticks)" }'
19177
+ command: 'Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,CreationDate,Name | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.CreationDate.Ticks)|$($_.Name)" }'
18307
19178
  },
18308
19179
  {
18309
19180
  name: "Get-WmiObject",
@@ -18321,82 +19192,22 @@ var init_processes = __esm({
18321
19192
  // (The obvious alternative, Get-Process | $_.Parent, was tried first and
18322
19193
  // does not exist before PowerShell 6. It returned empty parents for every
18323
19194
  // row, which the parser correctly discarded.)
18324
- command: 'Get-WmiObject Win32_Process | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.ConvertToDateTime($_.CreationDate).Ticks)" }'
19195
+ command: 'Get-WmiObject Win32_Process | ForEach-Object { "$($_.ProcessId)|$($_.ParentProcessId)|$($_.ConvertToDateTime($_.CreationDate).Ticks)|$($_.Name)" }'
18325
19196
  }
18326
19197
  ];
18327
19198
  lastSnapshotFailure = null;
19199
+ ROOT_KILL_VETO = /* @__PURE__ */ new Set([
19200
+ "FOREIGN_POSTGRES",
19201
+ "BINARY_CHANGED",
19202
+ "PID_RECYCLED"
19203
+ ]);
18328
19204
  BUNDLED_ROOT = (() => {
18329
19205
  const here = dirname4(fileURLToPath3(import.meta.url));
18330
- return existsSync7(join6(here, "runner.js")) ? here : null;
19206
+ return existsSync8(join8(here, "runner.js")) ? here : null;
18331
19207
  })();
18332
19208
  }
18333
19209
  });
18334
19210
 
18335
- // src/lib/watchdog.ts
18336
- async function probeRunnerHealth(runnerUrl) {
18337
- try {
18338
- const controller = new AbortController();
18339
- const t = setTimeout(() => controller.abort(), 3e3);
18340
- const res = await fetch(`${runnerUrl}/api/health`, { signal: controller.signal });
18341
- clearTimeout(t);
18342
- if (res.status === 200) return { state: "healthy", detail: "" };
18343
- const body = await res.json().catch(() => null);
18344
- const detail = body?.db === "error" ? "database unreachable (runner reports db: error)" : `runner is unhealthy (HTTP ${res.status})`;
18345
- return { state: "degraded", detail };
18346
- } catch (err) {
18347
- const aborted = err instanceof Error && err.name === "AbortError";
18348
- return {
18349
- state: "unreachable",
18350
- detail: aborted ? "runner did not answer /api/health within 3s (busy or stalled)" : "runner is not answering /api/health"
18351
- };
18352
- }
18353
- }
18354
- function startHealthWatchdog(opts) {
18355
- const intervalMs = opts.intervalMs ?? 3e4;
18356
- const failureThreshold = opts.failureThreshold ?? 2;
18357
- let announced = "healthy";
18358
- let consecutiveFailures = 0;
18359
- let stopped = false;
18360
- let inFlight = false;
18361
- const tick = async () => {
18362
- if (stopped || inFlight) return;
18363
- inFlight = true;
18364
- try {
18365
- const result = await opts.probe();
18366
- if (stopped) return;
18367
- if (result.state === "healthy") {
18368
- consecutiveFailures = 0;
18369
- if (announced !== "healthy") {
18370
- announced = "healthy";
18371
- opts.onTransition("healthy", "services are answering again");
18372
- }
18373
- return;
18374
- }
18375
- consecutiveFailures += 1;
18376
- if (consecutiveFailures < failureThreshold) return;
18377
- if (announced !== result.state) {
18378
- announced = result.state;
18379
- opts.onTransition(result.state, result.detail);
18380
- }
18381
- } finally {
18382
- inFlight = false;
18383
- }
18384
- };
18385
- const timer2 = setInterval(() => void tick(), intervalMs);
18386
- timer2.unref?.();
18387
- return {
18388
- stop: () => {
18389
- stopped = true;
18390
- clearInterval(timer2);
18391
- }
18392
- };
18393
- }
18394
- var init_watchdog = __esm({
18395
- "src/lib/watchdog.ts"() {
18396
- "use strict";
18397
- }
18398
- });
18399
-
18400
19211
  // src/commands/up.ts
18401
19212
  var up_exports = {};
18402
19213
  __export(up_exports, {
@@ -18406,20 +19217,20 @@ import chalk from "chalk";
18406
19217
  import ora from "ora";
18407
19218
  import open from "open";
18408
19219
  import { randomBytes as randomBytes4 } from "crypto";
18409
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
18410
- import { join as join7 } from "path";
19220
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
19221
+ import { join as join9 } from "path";
18411
19222
  async function killSilent(child) {
18412
19223
  await killProcessTree(child);
18413
19224
  }
18414
19225
  function printLogTail(service, lines) {
18415
- const logFile = join7(LOG_DIR, `${service}.log`);
19226
+ const logFile = join9(LOG_DIR, `${service}.log`);
18416
19227
  try {
18417
- if (!existsSync8(logFile)) {
19228
+ if (!existsSync9(logFile)) {
18418
19229
  console.error(chalk.gray(`
18419
19230
  ${service}: no log at ${logFile}`));
18420
19231
  return;
18421
19232
  }
18422
- const tail = readFileSync6(logFile, "utf-8").split(/\r?\n/).filter((l) => l.trim() !== "").slice(-lines);
19233
+ const tail = readFileSync7(logFile, "utf-8").split(/\r?\n/).filter((l) => l.trim() !== "").slice(-lines);
18423
19234
  if (tail.length === 0) {
18424
19235
  console.error(chalk.gray(`
18425
19236
  ${service}: log is empty (${logFile})`));
@@ -18434,20 +19245,6 @@ function printLogTail(service, lines) {
18434
19245
  ${service}: could not read ${logFile}`));
18435
19246
  }
18436
19247
  }
18437
- async function pidListeningOnPort(port) {
18438
- const { execa: execa4 } = await import("execa");
18439
- try {
18440
- const { stdout } = await execa4("netstat", ["-ano"], { reject: false });
18441
- for (const line2 of stdout.split(/\r?\n/)) {
18442
- const m = line2.match(/\s+TCP\s+\S+:(\d+)\s+\S+\s+LISTENING\s+(\d+)/);
18443
- if (m && m[1] && m[2] && Number.parseInt(m[1], 10) === port) {
18444
- return Number.parseInt(m[2], 10);
18445
- }
18446
- }
18447
- } catch {
18448
- }
18449
- return null;
18450
- }
18451
19248
  async function runUp(opts = {}) {
18452
19249
  let config = readConfig();
18453
19250
  if (!config) {
@@ -18469,8 +19266,19 @@ async function runUp(opts = {}) {
18469
19266
  config = defaultConfig;
18470
19267
  }
18471
19268
  resolveAuthMode(config);
19269
+ const startVerdict = await decideStartFromProbes({
19270
+ runnerPort: config.ports.runner,
19271
+ webPort: config.ports.web,
19272
+ dataDir: PG_DATA_DIR
19273
+ });
19274
+ if (!startVerdict.proceed) {
19275
+ logLauncherEvent("refused-kill", `already-running runner=${config.ports.runner}`);
19276
+ throw new AlreadyRunningError(startVerdict.message);
19277
+ }
18472
19278
  const known = readPids();
18473
- const leftovers = await sweepRecordedChildren(known?.children ?? []);
19279
+ const pgTable2 = await postgresProcessesForDataDir();
19280
+ const ownedPgPids = new Set(pgTable2.owned);
19281
+ const leftovers = await sweepRecordedChildren(known?.children ?? [], ownedPgPids);
18474
19282
  if (leftovers.length > 0) {
18475
19283
  console.log(
18476
19284
  chalk.gray(` Cleaned up ${leftovers.length} process(es) left by the previous session`)
@@ -18484,16 +19292,21 @@ async function runUp(opts = {}) {
18484
19292
  for (const root of [...ourPids]) {
18485
19293
  for (const rec of walkDescendants(snapshot2, root)) ourPids.add(rec.pid);
18486
19294
  }
18487
- const orphans = [];
18488
- const strangers = [];
19295
+ const listeners = [];
18489
19296
  for (const [name, port] of [
18490
19297
  ["web", config.ports.web],
18491
19298
  ["runner", config.ports.runner],
18492
19299
  ["postgres", config.ports.postgres]
18493
19300
  ]) {
18494
19301
  const pid = await pidListeningOnPort(port);
18495
- if (pid === null) continue;
18496
- if (ourPids.has(pid) || name === "postgres") orphans.push({ name, port, pid });
19302
+ if (pid !== null) listeners.push({ name, port, pid });
19303
+ }
19304
+ const isOurPostmasterPid = (pid) => ownedPgPids.has(pid);
19305
+ const orphans = [];
19306
+ const strangers = [];
19307
+ for (const { name, port, pid } of listeners) {
19308
+ const ours = name === "postgres" ? isOurPostmasterPid(pid) : ourPids.has(pid);
19309
+ if (ours) orphans.push({ name, port, pid });
18497
19310
  else strangers.push({ name, port, pid });
18498
19311
  }
18499
19312
  if (strangers.length > 0) {
@@ -18508,14 +19321,25 @@ ${lines}
18508
19321
  }
18509
19322
  if (!orphans.some((o) => o.name === "postgres")) {
18510
19323
  const pgPid = livePostmasterPid();
18511
- if (pgPid !== null) {
18512
- orphans.push({ name: "postgres", port: config.ports.postgres, pid: pgPid });
19324
+ if (pgPid !== null && isOurPostmasterPid(pgPid)) {
19325
+ orphans.push({ name: "postgres", port: measuredPort(pgPid, listeners), pid: pgPid });
19326
+ } else if (pgPid !== null) {
19327
+ console.log(
19328
+ chalk.yellow(
19329
+ ` - postmaster.pid in ${PG_DATA_DIR} names pid ${pgPid}, which has not been seen
19330
+ to exit. Its identity could NOT be confirmed: its start time differs from
19331
+ the one recorded, or it does not run out of that directory, or at least
19332
+ one of those two could not be read. Nothing was stopped. If Postgres then
19333
+ fails to start, check that pid yourself (its executable, its start time,
19334
+ which cluster it serves) and stop it through its owner rather than killing
19335
+ the number.`
19336
+ )
19337
+ );
18513
19338
  }
18514
19339
  }
18515
- if (!orphans.some((o) => o.name === "postgres")) {
18516
- for (const pid of await postgresPidsForDataDir()) {
18517
- orphans.push({ name: "postgres", port: config.ports.postgres, pid });
18518
- }
19340
+ for (const pid of ownedPgPids) {
19341
+ if (orphans.some((o) => o.pid === pid)) continue;
19342
+ orphans.push({ name: "postgres", port: measuredPort(pid, listeners), pid });
18519
19343
  }
18520
19344
  if (orphans.length > 0) {
18521
19345
  console.log(
@@ -18524,13 +19348,33 @@ ${lines}
18524
19348
  )
18525
19349
  );
18526
19350
  for (const o of orphans) {
18527
- console.log(chalk.gray(` - ${o.name} on :${o.port} (pid ${o.pid})`));
19351
+ console.log(
19352
+ chalk.gray(
19353
+ o.port === null ? ` - ${o.name} for our data dir (pid ${o.pid}, listening on none of our ports)` : ` - ${o.name} on :${o.port} (pid ${o.pid})`
19354
+ )
19355
+ );
18528
19356
  }
18529
19357
  console.log(chalk.yellow("Cleaning up before starting\u2026"));
18530
- const { execa: execa4 } = await import("execa");
18531
- const pgOrphan = orphans.find((o) => o.name === "postgres");
18532
- if (pgOrphan) {
18533
- await stopOrphanPostgres();
19358
+ const pgOrphans = orphans.filter((o) => o.name === "postgres");
19359
+ const postmasterPid = pgOrphans[0]?.pid;
19360
+ if (postmasterPid !== void 0) {
19361
+ const confirmedNow = new Set((await postgresProcessesForDataDir()).owned);
19362
+ if (confirmedNow.has(postmasterPid)) await stopOrphanPostgres(postmasterPid);
19363
+ }
19364
+ const leftAlone = [];
19365
+ const unconfirmed = [];
19366
+ for (const pgOrphan of pgOrphans) {
19367
+ const stillOurs = new Set((await postgresProcessesForDataDir()).owned);
19368
+ if (!stillOurs.has(pgOrphan.pid)) {
19369
+ if (!isPidAlive(pgOrphan.pid)) continue;
19370
+ leftAlone.push(pgOrphan.pid);
19371
+ console.log(
19372
+ chalk.yellow(
19373
+ ` - postgres pid ${pgOrphan.pid} is alive but can no longer be confirmed as ours; it was NOT stopped`
19374
+ )
19375
+ );
19376
+ continue;
19377
+ }
18534
19378
  if (isPidAlive(pgOrphan.pid)) {
18535
19379
  try {
18536
19380
  process.kill(pgOrphan.pid, "SIGKILL");
@@ -18546,12 +19390,31 @@ ${lines}
18546
19390
  );
18547
19391
  }
18548
19392
  }
19393
+ const tableAvailable = process.platform === "win32";
19394
+ const table = tableAvailable ? await processSnapshotWin() : /* @__PURE__ */ new Map();
18549
19395
  for (const o of orphans) {
18550
19396
  if (o.name === "postgres") continue;
19397
+ const live = table.get(o.pid);
19398
+ const verdict = !tableAvailable ? { killable: true } : confirmRecordedPid({
19399
+ recorded: recordedRoot(known, o.pid),
19400
+ live: live === void 0 ? void 0 : { pid: live.pid, startedAt: live.startedAt, name: live.name ?? "" },
19401
+ tableRead: table.size > 0,
19402
+ ownedPostgresPids: ownedPgPids
19403
+ });
19404
+ if (!verdict.killable) {
19405
+ if (verdict.code !== "PID_GONE") {
19406
+ unconfirmed.push(o.pid);
19407
+ console.log(chalk.yellow(` - ${o.name} pid ${o.pid} was NOT killed: ${verdict.detail}`));
19408
+ process.stderr.write(`${formatRefusal(verdict)}
19409
+ `);
19410
+ }
19411
+ continue;
19412
+ }
18551
19413
  try {
18552
19414
  if (process.platform === "win32") {
18553
- await execa4("taskkill", ["/T", "/F", "/PID", String(o.pid)], { reject: false });
19415
+ await killPidTree(o.pid, ownedPgPids, recordedRoot(known, o.pid));
18554
19416
  } else {
19417
+ console.log(chalk.gray(` - ${unconfirmedIdentityNotice(o.name, o.pid)}`));
18555
19418
  process.kill(o.pid, "SIGKILL");
18556
19419
  }
18557
19420
  } catch {
@@ -18562,6 +19425,7 @@ ${lines}
18562
19425
  while (Date.now() < deadline) {
18563
19426
  stillHeld = [];
18564
19427
  for (const o of orphans) {
19428
+ if (o.port === null) continue;
18565
19429
  const pid = await pidListeningOnPort(o.port);
18566
19430
  if (pid !== null) stillHeld.push({ ...o, pid });
18567
19431
  }
@@ -18571,7 +19435,30 @@ ${lines}
18571
19435
  if (stillHeld.length > 0) {
18572
19436
  const list = stillHeld.map((o) => `${o.name}:${o.port}=${o.pid}`).join(", ");
18573
19437
  console.log(chalk.yellow(`Some ports still held (likely Windows ghost sockets): ${list}.`));
19438
+ const alsoLeft = [...leftAlone, ...unconfirmed];
19439
+ if (alsoLeft.length > 0) {
19440
+ console.log(
19441
+ chalk.yellow(`Also left running, unconfirmed as ours: ${alsoLeft.join(", ")}.`)
19442
+ );
19443
+ }
18574
19444
  console.log(chalk.gray("Will rotate to free neighbours below.\n"));
19445
+ } else if (leftAlone.length > 0 || unconfirmed.length > 0) {
19446
+ if (leftAlone.length > 0) {
19447
+ console.log(
19448
+ chalk.yellow(
19449
+ `Ports are free, but ${leftAlone.length} postgres process(es) were left running (${leftAlone.join(", ")}): they could not be confirmed as ours.
19450
+ `
19451
+ )
19452
+ );
19453
+ }
19454
+ if (unconfirmed.length > 0) {
19455
+ console.log(
19456
+ chalk.yellow(
19457
+ `${unconfirmed.length} recorded process(es) were left running (${unconfirmed.join(", ")}): the pid no longer identifies what we started.
19458
+ `
19459
+ )
19460
+ );
19461
+ }
18575
19462
  } else {
18576
19463
  console.log(chalk.green("Orphans cleaned up.\n"));
18577
19464
  }
@@ -18581,13 +19468,18 @@ ${lines}
18581
19468
  const configured = config.ports[name];
18582
19469
  if (await isPortBindable(configured)) continue;
18583
19470
  const next = await findFreePort(configured + 1);
18584
- portChanges.push({ name, from: configured, to: next });
19471
+ portChanges.push({
19472
+ name,
19473
+ from: configured,
19474
+ to: next,
19475
+ heldBy: await pidListeningOnPort(configured)
19476
+ });
18585
19477
  config.ports[name] = next;
18586
19478
  }
18587
19479
  if (portChanges.length > 0) {
18588
19480
  console.log(chalk.yellow("Configured ports unavailable \u2014 rotating:"));
18589
- for (const c of portChanges) {
18590
- console.log(chalk.gray(` - ${c.name}: ${c.from} \u2192 ${c.to}`));
19481
+ for (const line2 of formatPortRotation(portChanges)) {
19482
+ console.log(line2.trimStart().startsWith("-") ? chalk.gray(line2) : chalk.yellow(line2));
18591
19483
  }
18592
19484
  writeConfig(config);
18593
19485
  console.log(chalk.gray(" Updated ~/.nodalai/config.json.\n"));
@@ -18610,9 +19502,10 @@ ${lines}
18610
19502
  runner: null,
18611
19503
  web: null
18612
19504
  };
18613
- const shutdown = async () => {
19505
+ const shutdown = async (cause = "signal") => {
18614
19506
  if (shuttingDown) return;
18615
19507
  shuttingDown = true;
19508
+ logLauncherEvent("shutdown", `cause=${cause}`);
18616
19509
  console.log("\n" + chalk.yellow(" Stopping Nodal-Agents\u2026"));
18617
19510
  const recorded = readPids()?.children ?? [];
18618
19511
  await Promise.allSettled(
@@ -18627,8 +19520,8 @@ ${lines}
18627
19520
  console.log(chalk.green(" Stopped. Goodbye!"));
18628
19521
  process.exit(0);
18629
19522
  };
18630
- process.on("SIGINT", () => void shutdown());
18631
- process.on("SIGTERM", () => void shutdown());
19523
+ process.on("SIGINT", () => void shutdown("SIGINT"));
19524
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
18632
19525
  let effectivePgPassword = pgPassword;
18633
19526
  if (pendingRotation) {
18634
19527
  writeConfig({ ...config, postgresPassword: pendingRotation });
@@ -18748,7 +19641,7 @@ ${lines}
18748
19641
  const installed = getInstalledVersion2();
18749
19642
  const latest = await Promise.race([
18750
19643
  getLatestVersion2(),
18751
- new Promise((resolve3) => setTimeout(() => resolve3(null), 3e3))
19644
+ new Promise((resolve4) => setTimeout(() => resolve4(null), 3e3))
18752
19645
  ]);
18753
19646
  if (latest !== null && isNewerVersion2(latest, installed)) {
18754
19647
  console.log(chalk.cyan(` \u2139 v${latest} available \u2014 run \`nodal-agents update\``));
@@ -18763,14 +19656,20 @@ ${lines}
18763
19656
  }
18764
19657
  }
18765
19658
  console.log("");
19659
+ logLauncherEvent(
19660
+ "started",
19661
+ `web=${webUrl} runner=${runnerUrl} mode=${opts.detach ? "detached" : "foreground"}`
19662
+ );
18766
19663
  console.log(chalk.bold.green(` Nodal-Agents ready at ${webUrl}`));
18767
19664
  if (config.bind === "lan") {
18768
19665
  console.log(chalk.cyan(` LAN mode \u2014 sign up at ${webUrl}/login`));
18769
19666
  }
18770
19667
  if (opts.detach) {
19668
+ logLauncherEvent("detached", `web=${webUrl} runner=${runnerUrl}`);
18771
19669
  console.log(chalk.gray(" Detached \u2014 the terminal is yours again."));
18772
19670
  console.log(chalk.gray(" nodal-agents logs runner follow a service"));
18773
19671
  console.log(chalk.gray(" nodal-agents down stop everything"));
19672
+ console.log(chalk.gray(` ${LAUNCHER_LOG} what the launcher saw`));
18774
19673
  console.log("");
18775
19674
  process.exit(0);
18776
19675
  }
@@ -18780,6 +19679,7 @@ ${lines}
18780
19679
  probe: () => probeRunnerHealth(runnerUrl),
18781
19680
  onTransition: (state, detail) => {
18782
19681
  const at = (/* @__PURE__ */ new Date()).toLocaleTimeString();
19682
+ logLauncherEvent(state === "healthy" ? "recovered" : "degraded", `state=${state} ${detail}`);
18783
19683
  if (state === "healthy") {
18784
19684
  console.log(chalk.green(`
18785
19685
  [${at}] Recovered \u2014 ${detail}.`));
@@ -18800,9 +19700,13 @@ ${lines}
18800
19700
  );
18801
19701
  }
18802
19702
  });
18803
- await Promise.race([runnerProcess, webProcess]);
19703
+ const exited = await Promise.race([
19704
+ runnerProcess.then(() => "runner").catch(() => "runner"),
19705
+ webProcess.then(() => "web").catch(() => "web")
19706
+ ]);
19707
+ logLauncherEvent("child-exited", `service=${exited}`);
18804
19708
  watchdog.stop();
18805
- await shutdown();
19709
+ await shutdown(`${exited} exited`);
18806
19710
  }
18807
19711
  var init_up = __esm({
18808
19712
  "src/commands/up.ts"() {
@@ -18812,6 +19716,10 @@ var init_up = __esm({
18812
19716
  init_seed();
18813
19717
  init_env();
18814
19718
  init_ports();
19719
+ init_orphans();
19720
+ init_already_running();
19721
+ init_pid_confirm();
19722
+ init_launcher_log();
18815
19723
  init_processes();
18816
19724
  init_src4();
18817
19725
  init_watchdog();
@@ -19099,17 +20007,49 @@ var init_init = __esm({
19099
20007
  // src/commands/down.ts
19100
20008
  var down_exports = {};
19101
20009
  __export(down_exports, {
19102
- runDown: () => runDown
20010
+ runDown: () => runDown,
20011
+ stopWithinBudget: () => stopWithinBudget
19103
20012
  });
19104
20013
  import chalk3 from "chalk";
19105
- import { existsSync as existsSync9 } from "fs";
19106
- import { join as join8 } from "path";
19107
- async function killPid(pid, label) {
20014
+ import { existsSync as existsSync10 } from "fs";
20015
+ import { join as join10 } from "path";
20016
+ function liveOf(snapshot2, pid) {
20017
+ const rec = snapshot2.get(pid);
20018
+ return rec === void 0 ? void 0 : { pid: rec.pid, startedAt: rec.startedAt, name: rec.name ?? "" };
20019
+ }
20020
+ async function killPid(pid, label, ctx) {
19108
20021
  if (!isPidAlive(pid)) {
19109
20022
  console.log(chalk3.gray(` ${label} (pid ${pid}) was already stopped`));
19110
20023
  return false;
19111
20024
  }
19112
- await killPidTree(pid);
20025
+ if (!ctx.tableAvailable) {
20026
+ console.log(chalk3.gray(` ${unconfirmedIdentityNotice(label, pid)}`));
20027
+ await killPidTree(pid, ctx.ownedPostgresPids);
20028
+ if (isPidAlive(pid)) {
20029
+ console.log(chalk3.red(` ${label} (pid ${pid}) is STILL RUNNING after SIGTERM then SIGKILL`));
20030
+ return false;
20031
+ }
20032
+ console.log(chalk3.green(` Stopped ${label} (pid ${pid})`));
20033
+ return true;
20034
+ }
20035
+ const verdict = confirmRecordedPid({
20036
+ recorded: recordedRoot(ctx.pids, pid),
20037
+ live: liveOf(ctx.snapshot, pid),
20038
+ tableRead: ctx.snapshot.size > 0,
20039
+ ownedPostgresPids: ctx.ownedPostgresPids
20040
+ });
20041
+ if (!verdict.killable) {
20042
+ console.log(
20043
+ chalk3.yellow(
20044
+ ` ${label} (pid ${pid}) was NOT stopped \u2014 ${verdict.detail}.
20045
+ Nodal-Agents does not kill a pid it cannot identify. Nothing was signalled.`
20046
+ )
20047
+ );
20048
+ process.stderr.write(`${formatRefusal(verdict)}
20049
+ `);
20050
+ return false;
20051
+ }
20052
+ await killPidTree(pid, ctx.ownedPostgresPids, recordedRoot(ctx.pids, pid));
19113
20053
  if (isPidAlive(pid)) {
19114
20054
  console.log(chalk3.red(` ${label} (pid ${pid}) is STILL RUNNING after SIGTERM then SIGKILL`));
19115
20055
  return false;
@@ -19117,10 +20057,30 @@ async function killPid(pid, label) {
19117
20057
  console.log(chalk3.green(` Stopped ${label} (pid ${pid})`));
19118
20058
  return true;
19119
20059
  }
19120
- async function stopPostgresGracefully() {
19121
- const pidFile = join8(PG_DATA_DIR, "postmaster.pid");
19122
- if (!existsSync9(pidFile)) return false;
20060
+ async function stopWithinBudget(pg, budgetMs) {
20061
+ return await Promise.race([
20062
+ pg.stop().then(() => true).catch(() => true),
20063
+ new Promise((resolve4) => setTimeout(() => resolve4(false), budgetMs))
20064
+ ]);
20065
+ }
20066
+ async function stopPostgresGracefully(ctx) {
20067
+ const pidFile = join10(PG_DATA_DIR, "postmaster.pid");
20068
+ if (!existsSync10(pidFile)) return false;
19123
20069
  const pgPid = readPostmasterPid(PG_DATA_DIR);
20070
+ const confirmed = pgPid !== null && ctx.ownedPostgresPids.has(pgPid);
20071
+ const refused = pgPid !== null && !confirmed && ctx.postgresReadingWorked;
20072
+ if (refused) {
20073
+ console.log(
20074
+ chalk3.yellow(
20075
+ ` postgres (pid ${pgPid}) was NOT stopped \u2014 postmaster.pid names it, but a fresh
20076
+ reading could not confirm it belongs to ${PG_DATA_DIR}.
20077
+ Nothing was signalled. Check that pid yourself before touching it.`
20078
+ )
20079
+ );
20080
+ process.stderr.write(`KILL_REFUSED code=FOREIGN_POSTGRES pid=${pgPid} lockfile=${pidFile}
20081
+ `);
20082
+ return false;
20083
+ }
19124
20084
  try {
19125
20085
  const EmbeddedPostgres = (await import("embedded-postgres")).default;
19126
20086
  const pg = new EmbeddedPostgres({
@@ -19136,7 +20096,15 @@ async function stopPostgresGracefully() {
19136
20096
  onLog: () => {
19137
20097
  }
19138
20098
  });
19139
- await pg.stop();
20099
+ if (!await stopWithinBudget(pg, GRACEFUL_STOP_BUDGET_MS)) {
20100
+ console.log(
20101
+ chalk3.yellow(
20102
+ ` postgres (pid ${pgPid ?? "unknown"}) did not finish a graceful stop within ${GRACEFUL_STOP_BUDGET_MS / 1e3}s.
20103
+ A postmaster that is recovering from a crashed backend can take longer than
20104
+ that. Nothing else was signalled; what it is doing now is reported below.`
20105
+ )
20106
+ );
20107
+ }
19140
20108
  const pgGone = pgPid === null || await waitForPidDead(pgPid, 15e3);
19141
20109
  if (!pgGone) {
19142
20110
  const killCmd = process.platform === "win32" ? `powershell Stop-Process -Id ${pgPid} -Force` : `kill -9 ${pgPid}`;
@@ -19144,7 +20112,9 @@ async function stopPostgresGracefully() {
19144
20112
  chalk3.red(
19145
20113
  ` postgres (pid ${pgPid}) is STILL RUNNING after a graceful stop
19146
20114
  It holds the shared-memory block for the data dir, so the next \`up\` will fail.
19147
- Fix: ${killCmd}`
20115
+ ` + (confirmed ? ` Fix: ${killCmd}` : ` Its identity could NOT be confirmed, so no kill command is suggested:
20116
+ check that pid yourself (its executable, its start time, which cluster
20117
+ it serves) and stop it through its owner.`)
19148
20118
  )
19149
20119
  );
19150
20120
  return false;
@@ -19158,15 +20128,23 @@ async function stopPostgresGracefully() {
19158
20128
  }
19159
20129
  async function runDown() {
19160
20130
  const pids = readPids();
20131
+ const pgReading = await postgresProcessesForDataDir();
20132
+ const ctx = {
20133
+ pids,
20134
+ tableAvailable: process.platform === "win32",
20135
+ snapshot: await processSnapshotWin(),
20136
+ ownedPostgresPids: new Set(pgReading.owned),
20137
+ postgresReadingWorked: pgReading.read
20138
+ };
19161
20139
  let stopped = 0;
19162
- if (pids?.runner) stopped += await killPid(pids.runner, "runner") ? 1 : 0;
19163
- if (pids?.web) stopped += await killPid(pids.web, "web") ? 1 : 0;
19164
- const swept = await sweepRecordedChildren(pids?.children ?? []);
20140
+ if (pids?.runner) stopped += await killPid(pids.runner, "runner", ctx) ? 1 : 0;
20141
+ if (pids?.web) stopped += await killPid(pids.web, "web", ctx) ? 1 : 0;
20142
+ const swept = await sweepRecordedChildren(pids?.children ?? [], ctx.ownedPostgresPids);
19165
20143
  if (swept.length > 0) {
19166
20144
  console.log(chalk3.green(` Stopped ${swept.length} background worker(s) left behind`));
19167
20145
  stopped += swept.length;
19168
20146
  }
19169
- if (await stopPostgresGracefully()) stopped++;
20147
+ if (await stopPostgresGracefully(ctx)) stopped++;
19170
20148
  clearPids();
19171
20149
  if (stopped === 0) {
19172
20150
  console.log(chalk3.yellow("No running Nodal-Agents processes found."));
@@ -19174,12 +20152,15 @@ async function runDown() {
19174
20152
  }
19175
20153
  console.log(chalk3.green("\n Nodal-Agents stopped."));
19176
20154
  }
20155
+ var GRACEFUL_STOP_BUDGET_MS;
19177
20156
  var init_down = __esm({
19178
20157
  "src/commands/down.ts"() {
19179
20158
  "use strict";
19180
20159
  init_processes();
19181
20160
  init_config();
19182
20161
  init_postgres();
20162
+ init_pid_confirm();
20163
+ GRACEFUL_STOP_BUDGET_MS = 6e4;
19183
20164
  }
19184
20165
  });
19185
20166
 
@@ -19189,8 +20170,8 @@ __export(logs_exports, {
19189
20170
  runLogs: () => runLogs
19190
20171
  });
19191
20172
  import chalk4 from "chalk";
19192
- import { existsSync as existsSync10, createReadStream, watch, statSync as statSync2 } from "fs";
19193
- import { join as join9 } from "path";
20173
+ import { existsSync as existsSync11, createReadStream, watch, statSync as statSync3 } from "fs";
20174
+ import { join as join11 } from "path";
19194
20175
  function isServiceName(s) {
19195
20176
  return VALID_SERVICES.includes(s);
19196
20177
  }
@@ -19202,34 +20183,34 @@ async function runLogs(service) {
19202
20183
  }
19203
20184
  const services = target === "all" ? VALID_SERVICES : [target];
19204
20185
  for (const svc of services) {
19205
- const logFile = join9(LOG_DIR, `${svc}.log`);
19206
- if (!existsSync10(logFile)) {
20186
+ const logFile = join11(LOG_DIR, `${svc}.log`);
20187
+ if (!existsSync11(logFile)) {
19207
20188
  console.log(chalk4.gray(` [${svc}] No log file yet at ${logFile}`));
19208
20189
  continue;
19209
20190
  }
19210
20191
  console.log(chalk4.bold(`
19211
20192
  --- ${svc} ---`));
19212
- await new Promise((resolve3, reject) => {
20193
+ await new Promise((resolve4, reject) => {
19213
20194
  const stream = createReadStream(logFile, { encoding: "utf-8" });
19214
20195
  stream.on("data", (chunk) => process.stdout.write(chunk));
19215
- stream.on("end", resolve3);
20196
+ stream.on("end", resolve4);
19216
20197
  stream.on("error", reject);
19217
20198
  });
19218
20199
  }
19219
20200
  if (target !== "all") {
19220
- const logFile = join9(LOG_DIR, `${target}.log`);
19221
- if (!existsSync10(logFile)) {
20201
+ const logFile = join11(LOG_DIR, `${target}.log`);
20202
+ if (!existsSync11(logFile)) {
19222
20203
  console.log(chalk4.yellow("Waiting for log file to be created\u2026"));
19223
20204
  }
19224
20205
  console.log(chalk4.gray("\nFollowing log (Ctrl+C to stop)...\n"));
19225
20206
  let fileSize = 0;
19226
20207
  try {
19227
- fileSize = statSync2(logFile).size;
20208
+ fileSize = statSync3(logFile).size;
19228
20209
  } catch {
19229
20210
  }
19230
20211
  const watcher = watch(logFile, () => {
19231
20212
  try {
19232
- const newSize = statSync2(logFile).size;
20213
+ const newSize = statSync3(logFile).size;
19233
20214
  if (newSize > fileSize) {
19234
20215
  const stream = createReadStream(logFile, { start: fileSize, encoding: "utf-8" });
19235
20216
  stream.on("data", (chunk) => process.stdout.write(chunk));
@@ -19254,10 +20235,10 @@ var init_logs = __esm({
19254
20235
 
19255
20236
  // ../../packages/checkpoints/src/checkpoints.ts
19256
20237
  import { execFile, spawn } from "child_process";
19257
- import { createHash as createHash3, randomBytes as randomBytes6 } from "crypto";
20238
+ import { createHash as createHash4, randomBytes as randomBytes6 } from "crypto";
19258
20239
  import { mkdir, rm, writeFile } from "fs/promises";
19259
- import { existsSync as existsSync11 } from "fs";
19260
- import { join as join10 } from "path";
20240
+ import { existsSync as existsSync12 } from "fs";
20241
+ import { join as join12 } from "path";
19261
20242
  import { promisify } from "util";
19262
20243
  function workspaceKey(workspace) {
19263
20244
  let norm = workspace;
@@ -19265,18 +20246,18 @@ function workspaceKey(workspace) {
19265
20246
  norm = norm.toLowerCase().replace(/\//g, "\\");
19266
20247
  }
19267
20248
  norm = norm.replace(/[\\/]+$/, "");
19268
- return createHash3("sha256").update(norm).digest("hex").slice(0, 16);
20249
+ return createHash4("sha256").update(norm).digest("hex").slice(0, 16);
19269
20250
  }
19270
20251
  function gitEnv(store, workspace, indexFile) {
19271
20252
  const key = workspaceKey(workspace);
19272
20253
  return {
19273
20254
  ...process.env,
19274
- GIT_DIR: join10(store, "store"),
20255
+ GIT_DIR: join12(store, "store"),
19275
20256
  GIT_WORK_TREE: workspace,
19276
20257
  // A per-workspace index: two workspaces snapshotting at once must not
19277
20258
  // stomp on each other's staging area. A READ (diffFile) brings its own
19278
20259
  // temporary index instead — see diffFile — so it never touches this one.
19279
- GIT_INDEX_FILE: indexFile ?? join10(store, "indexes", key),
20260
+ GIT_INDEX_FILE: indexFile ?? join12(store, "indexes", key),
19280
20261
  // A checkpoint is machinery, not authorship. Identity is fixed so it can
19281
20262
  // never depend on — or leak — the owner's git config.
19282
20263
  GIT_AUTHOR_NAME: "Nodal checkpoints",
@@ -19284,8 +20265,8 @@ function gitEnv(store, workspace, indexFile) {
19284
20265
  GIT_COMMITTER_NAME: "Nodal checkpoints",
19285
20266
  GIT_COMMITTER_EMAIL: "checkpoints@nodal.local",
19286
20267
  // The owner's global config must not change what we store or how.
19287
- GIT_CONFIG_GLOBAL: join10(store, "gitconfig"),
19288
- GIT_CONFIG_SYSTEM: join10(store, "gitconfig")
20268
+ GIT_CONFIG_GLOBAL: join12(store, "gitconfig"),
20269
+ GIT_CONFIG_SYSTEM: join12(store, "gitconfig")
19289
20270
  };
19290
20271
  }
19291
20272
  async function git(store, workspace, args, indexFile) {
@@ -19297,17 +20278,17 @@ async function git(store, workspace, args, indexFile) {
19297
20278
  return stdout.trim();
19298
20279
  }
19299
20280
  async function ensureStore(store) {
19300
- const gitDir = join10(store, "store");
19301
- await mkdir(join10(store, "indexes"), { recursive: true });
19302
- await writeFile(join10(store, "gitconfig"), "", { flag: "a" });
19303
- if (!existsSync11(join10(gitDir, "HEAD"))) {
20281
+ const gitDir = join12(store, "store");
20282
+ await mkdir(join12(store, "indexes"), { recursive: true });
20283
+ await writeFile(join12(store, "gitconfig"), "", { flag: "a" });
20284
+ if (!existsSync12(join12(gitDir, "HEAD"))) {
19304
20285
  await mkdir(gitDir, { recursive: true });
19305
20286
  await run("git", ["init", "--bare", "--quiet", gitDir], {
19306
20287
  timeout: GIT_TIMEOUT_MS,
19307
20288
  windowsHide: true
19308
20289
  });
19309
- await mkdir(join10(gitDir, "info"), { recursive: true });
19310
- await writeFile(join10(gitDir, "info", "exclude"), EXCLUDES.join("\n") + "\n", "utf-8");
20290
+ await mkdir(join12(gitDir, "info"), { recursive: true });
20291
+ await writeFile(join12(gitDir, "info", "exclude"), EXCLUDES.join("\n") + "\n", "utf-8");
19311
20292
  }
19312
20293
  }
19313
20294
  async function snapshot(store, workspace, label) {
@@ -19333,7 +20314,7 @@ async function snapshot(store, workspace, label) {
19333
20314
  return { sha, workspace, at, label };
19334
20315
  }
19335
20316
  async function listCheckpoints(store, workspace, limit = 20) {
19336
- if (!existsSync11(join10(store, "store", "HEAD"))) return [];
20317
+ if (!existsSync12(join12(store, "store", "HEAD"))) return [];
19337
20318
  const ref = `refs/nodal/${workspaceKey(workspace)}`;
19338
20319
  const out = await git(store, workspace, [
19339
20320
  "log",
@@ -19376,9 +20357,9 @@ var init_checkpoints = __esm({
19376
20357
 
19377
20358
  // ../../packages/checkpoints/src/root.ts
19378
20359
  import { homedir as homedir3 } from "os";
19379
- import { join as join11 } from "path";
20360
+ import { join as join13 } from "path";
19380
20361
  function checkpointsRoot() {
19381
- return process.env["NODALAI_CHECKPOINTS_ROOT"] ?? join11(homedir3(), ".nodalai", "checkpoints");
20362
+ return process.env["NODALAI_CHECKPOINTS_ROOT"] ?? join13(homedir3(), ".nodalai", "checkpoints");
19382
20363
  }
19383
20364
  var init_root = __esm({
19384
20365
  "../../packages/checkpoints/src/root.ts"() {
@@ -19402,7 +20383,7 @@ __export(checkpoints_exports, {
19402
20383
  runCheckpointsRestore: () => runCheckpointsRestore
19403
20384
  });
19404
20385
  import chalk5 from "chalk";
19405
- import { resolve as resolve2 } from "path";
20386
+ import { resolve as resolve3 } from "path";
19406
20387
  function ago(iso2) {
19407
20388
  const ms = Date.now() - new Date(iso2).getTime();
19408
20389
  const min2 = Math.round(ms / 6e4);
@@ -19413,7 +20394,7 @@ function ago(iso2) {
19413
20394
  return `${Math.round(h / 24)} d ago`;
19414
20395
  }
19415
20396
  async function runCheckpointsList(workspace) {
19416
- const ws = resolve2(workspace ?? process.cwd());
20397
+ const ws = resolve3(workspace ?? process.cwd());
19417
20398
  const list = await listCheckpoints(checkpointsRoot(), ws);
19418
20399
  if (list.length === 0) {
19419
20400
  console.log(chalk5.yellow(`No checkpoints for ${ws}`));
@@ -19438,7 +20419,7 @@ Checkpoints for ${ws}
19438
20419
  `));
19439
20420
  }
19440
20421
  async function runCheckpointsRestore(sha, workspace) {
19441
- const ws = resolve2(workspace ?? process.cwd());
20422
+ const ws = resolve3(workspace ?? process.cwd());
19442
20423
  const list = await listCheckpoints(checkpointsRoot(), ws, 200);
19443
20424
  const match = list.find((c) => c.sha.startsWith(sha));
19444
20425
  if (!match) {
@@ -23354,7 +24335,7 @@ var init_protocol = __esm({
23354
24335
  return;
23355
24336
  }
23356
24337
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
23357
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
24338
+ await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
23358
24339
  options?.signal?.throwIfAborted();
23359
24340
  }
23360
24341
  } catch (error) {
@@ -23371,7 +24352,7 @@ var init_protocol = __esm({
23371
24352
  */
23372
24353
  request(request, resultSchema, options) {
23373
24354
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
23374
- return new Promise((resolve3, reject) => {
24355
+ return new Promise((resolve4, reject) => {
23375
24356
  const earlyReject = (error) => {
23376
24357
  reject(error);
23377
24358
  };
@@ -23449,7 +24430,7 @@ var init_protocol = __esm({
23449
24430
  if (!parseResult.success) {
23450
24431
  reject(parseResult.error);
23451
24432
  } else {
23452
- resolve3(parseResult.data);
24433
+ resolve4(parseResult.data);
23453
24434
  }
23454
24435
  } catch (error) {
23455
24436
  reject(error);
@@ -23710,12 +24691,12 @@ var init_protocol = __esm({
23710
24691
  }
23711
24692
  } catch {
23712
24693
  }
23713
- return new Promise((resolve3, reject) => {
24694
+ return new Promise((resolve4, reject) => {
23714
24695
  if (signal.aborted) {
23715
24696
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
23716
24697
  return;
23717
24698
  }
23718
- const timeoutId = setTimeout(resolve3, interval2);
24699
+ const timeoutId = setTimeout(resolve4, interval2);
23719
24700
  signal.addEventListener("abort", () => {
23720
24701
  clearTimeout(timeoutId);
23721
24702
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -26742,7 +27723,7 @@ var require_compile = __commonJS({
26742
27723
  const schOrFunc = root.refs[ref];
26743
27724
  if (schOrFunc)
26744
27725
  return schOrFunc;
26745
- let _sch = resolve3.call(this, root, ref);
27726
+ let _sch = resolve4.call(this, root, ref);
26746
27727
  if (_sch === void 0) {
26747
27728
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
26748
27729
  const { schemaId } = this.opts;
@@ -26769,7 +27750,7 @@ var require_compile = __commonJS({
26769
27750
  function sameSchemaEnv(s1, s2) {
26770
27751
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
26771
27752
  }
26772
- function resolve3(root, ref) {
27753
+ function resolve4(root, ref) {
26773
27754
  let sch;
26774
27755
  while (typeof (sch = this.refs[ref]) == "string")
26775
27756
  ref = sch;
@@ -26861,15 +27842,33 @@ var require_data = __commonJS({
26861
27842
  }
26862
27843
  });
26863
27844
 
26864
- // ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js
27845
+ // ../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/utils.js
26865
27846
  var require_utils = __commonJS({
26866
- "../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js"(exports, module) {
27847
+ "../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/utils.js"(exports, module) {
26867
27848
  "use strict";
26868
27849
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
26869
27850
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
26870
27851
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
26871
27852
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
26872
- var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
27853
+ var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
27854
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
27855
+ var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
27856
+ var BYTE_HEX = new Array(256);
27857
+ {
27858
+ const HEX_DIGITS = "0123456789ABCDEF";
27859
+ for (let i = 0; i < 256; i++) {
27860
+ BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
27861
+ }
27862
+ }
27863
+ function percentEncodeNonAscii(cp) {
27864
+ if (cp < 2048) {
27865
+ return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
27866
+ }
27867
+ if (cp < 65536) {
27868
+ return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
27869
+ }
27870
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
27871
+ }
26873
27872
  function stringArrayToHexStripped(input) {
26874
27873
  let acc = "";
26875
27874
  let code = 0;
@@ -26894,91 +27893,105 @@ var require_utils = __commonJS({
26894
27893
  }
26895
27894
  return acc;
26896
27895
  }
27896
+ var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
27897
+ var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
27898
+ var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
26897
27899
  var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
26898
- function consumeIsZone(buffer2) {
26899
- buffer2.length = 0;
26900
- return true;
26901
- }
26902
- function consumeHextets(buffer2, address, output) {
26903
- if (buffer2.length) {
26904
- const hex = stringArrayToHexStripped(buffer2);
26905
- if (hex !== "") {
26906
- address.push(hex);
26907
- } else {
26908
- output.error = true;
26909
- return false;
27900
+ function isZoneIdentifier(zone) {
27901
+ if (zone.length === 0) return false;
27902
+ for (let i = 0; i < zone.length; i++) {
27903
+ if (isZoneCharacter(zone[i])) continue;
27904
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
27905
+ i += 2;
27906
+ continue;
26910
27907
  }
26911
- buffer2.length = 0;
27908
+ return false;
26912
27909
  }
26913
27910
  return true;
26914
27911
  }
26915
- function getIPV6(input) {
26916
- let tokenCount = 0;
26917
- const output = { error: false, address: "", zone: "" };
26918
- const address = [];
26919
- const buffer2 = [];
26920
- let endipv6Encountered = false;
26921
- let endIpv6 = false;
26922
- let consume = consumeHextets;
26923
- for (let i = 0; i < input.length; i++) {
26924
- const cursor = input[i];
26925
- if (cursor === "[" || cursor === "]") {
26926
- continue;
26927
- }
26928
- if (cursor === ":") {
26929
- if (endipv6Encountered === true) {
26930
- endIpv6 = true;
26931
- }
26932
- if (!consume(buffer2, address, output)) {
26933
- break;
26934
- }
26935
- if (++tokenCount > 7) {
26936
- output.error = true;
26937
- break;
26938
- }
26939
- if (i > 0 && input[i - 1] === ":") {
26940
- endipv6Encountered = true;
27912
+ function compressIPv6ZeroRun(hextets) {
27913
+ let bestStart = -1;
27914
+ let bestLength = 0;
27915
+ let runStart = -1;
27916
+ let runLength = 0;
27917
+ for (let i = 0; i < hextets.length; i++) {
27918
+ if (hextets[i] === "0") {
27919
+ if (runStart === -1) runStart = i;
27920
+ runLength++;
27921
+ if (runLength > bestLength) {
27922
+ bestLength = runLength;
27923
+ bestStart = runStart;
26941
27924
  }
26942
- address.push(":");
26943
- continue;
26944
- } else if (cursor === "%") {
26945
- if (!consume(buffer2, address, output)) {
26946
- break;
26947
- }
26948
- consume = consumeIsZone;
26949
27925
  } else {
26950
- buffer2.push(cursor);
27926
+ runStart = -1;
27927
+ runLength = 0;
27928
+ }
27929
+ }
27930
+ if (bestLength < 2) return hextets.join(":");
27931
+ const head = hextets.slice(0, bestStart).join(":");
27932
+ const tail = hextets.slice(bestStart + bestLength).join(":");
27933
+ return head + "::" + tail;
27934
+ }
27935
+ function normalizeIPv6Address(input) {
27936
+ const compression = input.indexOf("::");
27937
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
27938
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
27939
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
27940
+ if (compression !== -1) {
27941
+ if (left.length === 1 && left[0] === "") left.length = 0;
27942
+ if (right.length === 1 && right[0] === "") right.length = 0;
27943
+ }
27944
+ const parts = left.concat(right);
27945
+ let hextetCount = 0;
27946
+ for (let i = 0; i < parts.length; i++) {
27947
+ const part = parts[i];
27948
+ if (part === "") return void 0;
27949
+ if (part.indexOf(".") !== -1) {
27950
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
27951
+ hextetCount += 2;
26951
27952
  continue;
26952
27953
  }
27954
+ if (!isHextet(part)) return void 0;
27955
+ parts[i] = parseInt(part, 16).toString(16);
27956
+ hextetCount++;
26953
27957
  }
26954
- if (buffer2.length) {
26955
- if (consume === consumeIsZone) {
26956
- output.zone = buffer2.join("");
26957
- } else if (endIpv6) {
26958
- address.push(buffer2.join(""));
26959
- } else {
26960
- address.push(stringArrayToHexStripped(buffer2));
26961
- }
27958
+ if (compression === -1) {
27959
+ if (hextetCount !== 8) return void 0;
27960
+ return compressIPv6ZeroRun(parts);
26962
27961
  }
26963
- output.address = address.join("");
26964
- return output;
27962
+ if (hextetCount >= 8) return void 0;
27963
+ const expanded = parts.slice(0, left.length);
27964
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
27965
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
27966
+ return compressIPv6ZeroRun(expanded);
26965
27967
  }
26966
27968
  function normalizeIPv6(host) {
26967
- if (findToken(host, ":") < 2) {
26968
- return { host, isIPV6: false };
26969
- }
26970
- const ipv6 = getIPV6(host);
26971
- if (!ipv6.error) {
26972
- let newHost = ipv6.address;
26973
- let escapedHost = ipv6.address;
26974
- if (ipv6.zone) {
26975
- newHost += "%" + ipv6.zone;
26976
- escapedHost += "%25" + ipv6.zone;
26977
- }
26978
- return { host: newHost, isIPV6: true, escapedHost };
26979
- } else {
26980
- return { host, isIPV6: false };
26981
- }
27969
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
27970
+ const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
27971
+ if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
27972
+ let input = bracketed ? host.slice(1, -1) : host;
27973
+ if (bracketed && isIPvFuture(input)) {
27974
+ input = input.toLowerCase();
27975
+ return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
27976
+ }
27977
+ if (findToken(input, ":") < 2) {
27978
+ return { host, isIPV6: false, error: bracketed };
27979
+ }
27980
+ let zoneIdentifier = "";
27981
+ const zoneSeparator = input.indexOf("%");
27982
+ if (zoneSeparator !== -1) {
27983
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
27984
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
27985
+ if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
27986
+ input = input.slice(0, zoneSeparator);
27987
+ }
27988
+ const address = normalizeIPv6Address(input);
27989
+ if (address === void 0) return { host, isIPV6: false, error: true };
27990
+ return {
27991
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
27992
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
27993
+ isIPV6: true
27994
+ };
26982
27995
  }
26983
27996
  function findToken(str, token) {
26984
27997
  let ind = 0;
@@ -27097,7 +28110,8 @@ var require_utils = __commonJS({
27097
28110
  function normalizePathEncoding(input) {
27098
28111
  let output = "";
27099
28112
  for (let i = 0; i < input.length; i++) {
27100
- if (input[i] === "%" && i + 2 < input.length) {
28113
+ const ch = input[i];
28114
+ if (ch === "%" && i + 2 < input.length) {
27101
28115
  const hex = input.slice(i + 1, i + 3);
27102
28116
  if (isHexPair(hex)) {
27103
28117
  const normalizedHex = hex.toUpperCase();
@@ -27111,10 +28125,152 @@ var require_utils = __commonJS({
27111
28125
  continue;
27112
28126
  }
27113
28127
  }
27114
- if (isPathCharacter(input[i])) {
27115
- output += input[i];
28128
+ if (isPathCharacter(ch)) {
28129
+ output += ch;
28130
+ } else {
28131
+ const code = input.charCodeAt(i);
28132
+ if (code < 128) {
28133
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
28134
+ } else if (code < 55296 || code > 57343) {
28135
+ output += percentEncodeNonAscii(code);
28136
+ } else if (code <= 56319 && i + 1 < input.length) {
28137
+ const low = input.charCodeAt(i + 1);
28138
+ if (low >= 56320 && low <= 57343) {
28139
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
28140
+ i++;
28141
+ } else {
28142
+ output += percentEncodeNonAscii(65533);
28143
+ }
28144
+ } else {
28145
+ output += percentEncodeNonAscii(65533);
28146
+ }
28147
+ }
28148
+ }
28149
+ return output;
28150
+ }
28151
+ function serializePathEncoding(input, pathNoScheme = false) {
28152
+ let output = "";
28153
+ let firstSegment = pathNoScheme && input[0] !== "/";
28154
+ for (let i = 0; i < input.length; i++) {
28155
+ const ch = input[i];
28156
+ if (ch === "%" && i + 2 < input.length) {
28157
+ const hex = input.slice(i + 1, i + 3);
28158
+ if (isHexPair(hex)) {
28159
+ output += "%" + hex.toUpperCase();
28160
+ i += 2;
28161
+ continue;
28162
+ }
28163
+ }
28164
+ if (ch === "/") {
28165
+ firstSegment = false;
28166
+ }
28167
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
28168
+ output += ch;
28169
+ } else {
28170
+ const code = input.charCodeAt(i);
28171
+ if (code < 128) {
28172
+ output += BYTE_HEX[code];
28173
+ } else if (code < 55296 || code > 57343) {
28174
+ output += percentEncodeNonAscii(code);
28175
+ } else if (code <= 56319 && i + 1 < input.length) {
28176
+ const low = input.charCodeAt(i + 1);
28177
+ if (low >= 56320 && low <= 57343) {
28178
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
28179
+ i++;
28180
+ } else {
28181
+ output += percentEncodeNonAscii(65533);
28182
+ }
28183
+ } else {
28184
+ output += percentEncodeNonAscii(65533);
28185
+ }
28186
+ }
28187
+ }
28188
+ return output;
28189
+ }
28190
+ function encodeComponent(input, isAllowed) {
28191
+ let output = "";
28192
+ for (let i = 0; i < input.length; i++) {
28193
+ const ch = input[i];
28194
+ if (ch === "%" && i + 2 < input.length) {
28195
+ const hex = input.slice(i + 1, i + 3);
28196
+ if (isHexPair(hex)) {
28197
+ output += "%" + hex.toUpperCase();
28198
+ i += 2;
28199
+ continue;
28200
+ }
28201
+ }
28202
+ if (isAllowed(ch)) {
28203
+ output += ch;
28204
+ } else {
28205
+ const code = input.charCodeAt(i);
28206
+ if (code < 128) {
28207
+ output += BYTE_HEX[code];
28208
+ } else if (code < 55296 || code > 57343) {
28209
+ output += percentEncodeNonAscii(code);
28210
+ } else if (code <= 56319 && i + 1 < input.length) {
28211
+ const low = input.charCodeAt(i + 1);
28212
+ if (low >= 56320 && low <= 57343) {
28213
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
28214
+ i++;
28215
+ } else {
28216
+ output += percentEncodeNonAscii(65533);
28217
+ }
28218
+ } else {
28219
+ output += percentEncodeNonAscii(65533);
28220
+ }
28221
+ }
28222
+ }
28223
+ return output;
28224
+ }
28225
+ function encodeUserinfo(input) {
28226
+ return encodeComponent(input, isUserinfoCharacter);
28227
+ }
28228
+ function encodeQuery(input) {
28229
+ return encodeComponent(input, isQueryFragmentCharacter);
28230
+ }
28231
+ function encodeFragment(input) {
28232
+ return encodeComponent(input, isQueryFragmentCharacter);
28233
+ }
28234
+ function isEscapeSafe(cp) {
28235
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
28236
+ }
28237
+ function normalizeQueryFragmentEncoding(input) {
28238
+ let output = "";
28239
+ for (let i = 0; i < input.length; i++) {
28240
+ const ch = input[i];
28241
+ if (ch === "%" && i + 2 < input.length) {
28242
+ const hex = input.slice(i + 1, i + 3);
28243
+ if (isHexPair(hex)) {
28244
+ const normalizedHex = hex.toUpperCase();
28245
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
28246
+ if (isUnreserved(decoded)) {
28247
+ output += decoded;
28248
+ } else {
28249
+ output += "%" + normalizedHex;
28250
+ }
28251
+ i += 2;
28252
+ continue;
28253
+ }
28254
+ }
28255
+ if (isQueryFragmentCharacter(ch)) {
28256
+ output += ch;
27116
28257
  } else {
27117
- output += escape(input[i]);
28258
+ const code = input.charCodeAt(i);
28259
+ if (code < 128) {
28260
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
28261
+ } else if (code < 55296 || code > 57343) {
28262
+ output += percentEncodeNonAscii(code);
28263
+ } else if (code <= 56319 && i + 1 < input.length) {
28264
+ const low = input.charCodeAt(i + 1);
28265
+ if (low >= 56320 && low <= 57343) {
28266
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
28267
+ i++;
28268
+ } else {
28269
+ output += percentEncodeNonAscii(65533);
28270
+ }
28271
+ } else {
28272
+ output += percentEncodeNonAscii(65533);
28273
+ }
27118
28274
  }
27119
28275
  }
27120
28276
  return output;
@@ -27137,14 +28293,18 @@ var require_utils = __commonJS({
27137
28293
  function recomposeAuthority(component) {
27138
28294
  const uriTokens = [];
27139
28295
  if (component.userinfo !== void 0) {
27140
- uriTokens.push(component.userinfo);
28296
+ uriTokens.push(encodeUserinfo(component.userinfo));
27141
28297
  uriTokens.push("@");
27142
28298
  }
27143
28299
  if (component.host !== void 0) {
27144
- let host = unescape(component.host);
28300
+ let host = component.host;
27145
28301
  if (!isIPv4(host)) {
27146
- const ipV6res = normalizeIPv6(host);
27147
- if (ipV6res.isIPV6 === true) {
28302
+ let ipV6res = normalizeIPv6(host);
28303
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
28304
+ host = normalizePercentEncoding(host, true);
28305
+ ipV6res = normalizeIPv6(host);
28306
+ }
28307
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
27148
28308
  host = `[${ipV6res.escapedHost}]`;
27149
28309
  } else {
27150
28310
  host = reescapeHostDelimiters(host, false);
@@ -27164,6 +28324,11 @@ var require_utils = __commonJS({
27164
28324
  reescapeHostDelimiters,
27165
28325
  normalizePercentEncoding,
27166
28326
  normalizePathEncoding,
28327
+ serializePathEncoding,
28328
+ normalizeQueryFragmentEncoding,
28329
+ encodeUserinfo,
28330
+ encodeQuery,
28331
+ encodeFragment,
27167
28332
  escapePreservingEscapes,
27168
28333
  removeDotSegments,
27169
28334
  isIPv4,
@@ -27174,12 +28339,12 @@ var require_utils = __commonJS({
27174
28339
  }
27175
28340
  });
27176
28341
 
27177
- // ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js
28342
+ // ../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/schemes.js
27178
28343
  var require_schemes = __commonJS({
27179
- "../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js"(exports, module) {
28344
+ "../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/lib/schemes.js"(exports, module) {
27180
28345
  "use strict";
27181
28346
  var { isUUID } = require_utils();
27182
- var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
28347
+ var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
27183
28348
  var supportedSchemeNames = (
27184
28349
  /** @type {const} */
27185
28350
  [
@@ -27240,9 +28405,10 @@ var require_schemes = __commonJS({
27240
28405
  wsComponent.secure = void 0;
27241
28406
  }
27242
28407
  if (wsComponent.resourceName) {
27243
- const [path, query] = wsComponent.resourceName.split("?");
28408
+ const queryIndex = wsComponent.resourceName.indexOf("?");
28409
+ const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
27244
28410
  wsComponent.path = path && path !== "/" ? path : void 0;
27245
- wsComponent.query = query;
28411
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
27246
28412
  wsComponent.resourceName = void 0;
27247
28413
  }
27248
28414
  wsComponent.fragment = void 0;
@@ -27254,7 +28420,7 @@ var require_schemes = __commonJS({
27254
28420
  return urnComponent;
27255
28421
  }
27256
28422
  const matches = urnComponent.path.match(URN_REG);
27257
- if (matches) {
28423
+ if (matches && matches[0] === urnComponent.path) {
27258
28424
  const scheme = options.scheme || urnComponent.scheme || "urn";
27259
28425
  urnComponent.nid = matches[1].toLowerCase();
27260
28426
  urnComponent.nss = matches[2];
@@ -27384,12 +28550,21 @@ var require_schemes = __commonJS({
27384
28550
  }
27385
28551
  });
27386
28552
 
27387
- // ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js
28553
+ // ../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/index.js
27388
28554
  var require_fast_uri = __commonJS({
27389
- "../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js"(exports, module) {
28555
+ "../../node_modules/.pnpm/fast-uri@3.1.6/node_modules/fast-uri/index.js"(exports, module) {
27390
28556
  "use strict";
27391
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
28557
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
27392
28558
  var { SCHEMES, getSchemeHandler } = require_schemes();
28559
+ var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
28560
+ var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
28561
+ function decodeValidScheme(scheme) {
28562
+ const decodedScheme = unescape(String(scheme));
28563
+ if (!VALID_SCHEME.test(decodedScheme)) {
28564
+ throw new TypeError(MALFORMED_SCHEME_ERROR);
28565
+ }
28566
+ return decodedScheme;
28567
+ }
27393
28568
  function normalize(uri, options) {
27394
28569
  if (typeof uri === "string") {
27395
28570
  uri = /** @type {T} */
@@ -27400,9 +28575,36 @@ var require_fast_uri = __commonJS({
27400
28575
  }
27401
28576
  return uri;
27402
28577
  }
27403
- function resolve3(baseURI, relativeURI, options) {
28578
+ function resolve4(baseURI, relativeURI, options) {
27404
28579
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
27405
- const resolved = resolveComponent(parse2(baseURI, schemelessOptions), parse2(relativeURI, schemelessOptions), schemelessOptions, true);
28580
+ const {
28581
+ parsed: baseParsed,
28582
+ malformedAuthorityOrPort: baseMalformed,
28583
+ malformedPercentEncoding: baseMalformedPercentEncoding,
28584
+ malformedSchemeSpecific: baseMalformedSchemeSpecific,
28585
+ malformedHost: baseMalformedHost,
28586
+ malformedScheme: baseMalformedScheme
28587
+ } = parseWithStatus(baseURI, schemelessOptions);
28588
+ const {
28589
+ parsed: relativeParsed,
28590
+ malformedAuthorityOrPort: relativeMalformed,
28591
+ malformedPercentEncoding: relativeMalformedPercentEncoding,
28592
+ malformedSchemeSpecific: relativeMalformedSchemeSpecific,
28593
+ malformedHost: relativeMalformedHost,
28594
+ malformedScheme: relativeMalformedScheme
28595
+ } = parseWithStatus(relativeURI, schemelessOptions);
28596
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
28597
+ throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
28598
+ }
28599
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
28600
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
28601
+ const resolvedHost = resolved.host;
28602
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
28603
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
28604
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
28605
+ if (resolved.error && !encodedASCIIHost) {
28606
+ throw new Error(resolved.error);
28607
+ }
27406
28608
  schemelessOptions.skipEscape = true;
27407
28609
  return serialize(resolved, schemelessOptions);
27408
28610
  }
@@ -27462,7 +28664,7 @@ var require_fast_uri = __commonJS({
27462
28664
  function equal(uriA, uriB, options) {
27463
28665
  const normalizedA = normalizeComparableURI(uriA, options);
27464
28666
  const normalizedB = normalizeComparableURI(uriB, options);
27465
- return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
28667
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
27466
28668
  }
27467
28669
  function serialize(cmpts, opts) {
27468
28670
  const component = {
@@ -27483,19 +28685,22 @@ var require_fast_uri = __commonJS({
27483
28685
  };
27484
28686
  const options = Object.assign({}, opts);
27485
28687
  const uriTokens = [];
28688
+ if (component.scheme) {
28689
+ component.scheme = decodeValidScheme(component.scheme);
28690
+ }
27486
28691
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
27487
28692
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
28693
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
28694
+ const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
27488
28695
  if (component.path !== void 0) {
27489
28696
  if (!options.skipEscape) {
27490
- component.path = escapePreservingEscapes(component.path);
27491
- if (component.scheme !== void 0) {
27492
- component.path = component.path.split("%3A").join(":");
27493
- }
28697
+ component.path = serializePathEncoding(component.path, pathNoScheme);
27494
28698
  } else {
27495
28699
  component.path = normalizePercentEncoding(component.path);
27496
28700
  }
27497
28701
  }
27498
28702
  if (options.reference !== "suffix" && component.scheme) {
28703
+ component.scheme = decodeValidScheme(component.scheme);
27499
28704
  uriTokens.push(component.scheme, ":");
27500
28705
  }
27501
28706
  const authority = recomposeAuthority(component);
@@ -27513,20 +28718,25 @@ var require_fast_uri = __commonJS({
27513
28718
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
27514
28719
  s = removeDotSegments(s);
27515
28720
  }
28721
+ if (pathNoScheme) {
28722
+ s = serializePathEncoding(s, true);
28723
+ }
27516
28724
  if (authority === void 0 && s[0] === "/" && s[1] === "/") {
27517
28725
  s = "/%2F" + s.slice(2);
27518
28726
  }
27519
28727
  uriTokens.push(s);
27520
28728
  }
27521
28729
  if (component.query !== void 0) {
27522
- uriTokens.push("?", component.query);
28730
+ uriTokens.push("?", encodeQuery(component.query));
27523
28731
  }
27524
28732
  if (component.fragment !== void 0) {
27525
- uriTokens.push("#", component.fragment);
28733
+ uriTokens.push("#", encodeFragment(component.fragment));
27526
28734
  }
27527
28735
  return uriTokens.join("");
27528
28736
  }
27529
28737
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
28738
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
28739
+ var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
27530
28740
  function getParseError(parsed, matches) {
27531
28741
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
27532
28742
  return 'URI path must start with "/" when authority is present.';
@@ -27536,6 +28746,32 @@ var require_fast_uri = __commonJS({
27536
28746
  }
27537
28747
  return void 0;
27538
28748
  }
28749
+ function hasMalformedPercentEncoding(component) {
28750
+ if (component === void 0) return false;
28751
+ let percent = component.indexOf("%");
28752
+ while (percent !== -1) {
28753
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
28754
+ return true;
28755
+ }
28756
+ percent = component.indexOf("%", percent + 3);
28757
+ }
28758
+ return false;
28759
+ }
28760
+ function hasMalformedComponentPercentEncoding(matches) {
28761
+ const host = matches[4];
28762
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
28763
+ }
28764
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
28765
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
28766
+ try {
28767
+ parsed.host = new URL("http://" + parsed.host).hostname;
28768
+ } catch (e) {
28769
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
28770
+ return true;
28771
+ }
28772
+ }
28773
+ return false;
28774
+ }
27539
28775
  function parseWithStatus(uri, opts) {
27540
28776
  const options = Object.assign({}, opts);
27541
28777
  const parsed = {
@@ -27548,6 +28784,11 @@ var require_fast_uri = __commonJS({
27548
28784
  fragment: void 0
27549
28785
  };
27550
28786
  let malformedAuthorityOrPort = false;
28787
+ let malformedPercentEncoding = false;
28788
+ let malformedSchemeSpecific = false;
28789
+ let malformedHost = false;
28790
+ let malformedIPLiteral = false;
28791
+ let malformedScheme = false;
27551
28792
  let isIP = false;
27552
28793
  if (options.reference === "suffix") {
27553
28794
  if (options.scheme) {
@@ -27556,6 +28797,25 @@ var require_fast_uri = __commonJS({
27556
28797
  uri = "//" + uri;
27557
28798
  }
27558
28799
  }
28800
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
28801
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
28802
+ parsed.error = "URI authority must not contain a literal backslash.";
28803
+ malformedAuthorityOrPort = true;
28804
+ }
28805
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
28806
+ if (introducerMatch !== null) {
28807
+ const region = introducerMatch[1];
28808
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
28809
+ if (normalizedRegion.length >= 2) {
28810
+ if (normalizedRegion.slice(0, 2) !== "//") {
28811
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
28812
+ malformedAuthorityOrPort = true;
28813
+ } else if (region.length !== normalizedRegion.length) {
28814
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
28815
+ malformedAuthorityOrPort = true;
28816
+ }
28817
+ }
28818
+ }
27559
28819
  const matches = uri.match(URI_PARSE);
27560
28820
  if (matches) {
27561
28821
  parsed.scheme = matches[1];
@@ -27565,6 +28825,19 @@ var require_fast_uri = __commonJS({
27565
28825
  parsed.path = matches[6] || "";
27566
28826
  parsed.query = matches[7];
27567
28827
  parsed.fragment = matches[8];
28828
+ if (parsed.scheme !== void 0) {
28829
+ const decodedScheme = unescape(parsed.scheme);
28830
+ if (VALID_SCHEME.test(decodedScheme)) {
28831
+ parsed.scheme = decodedScheme.toLowerCase();
28832
+ } else {
28833
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
28834
+ malformedScheme = true;
28835
+ }
28836
+ }
28837
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
28838
+ if (malformedPercentEncoding) {
28839
+ parsed.error = parsed.error || "URI contains malformed percent-encoding.";
28840
+ }
27568
28841
  if (isNaN(parsed.port)) {
27569
28842
  parsed.port = matches[5];
27570
28843
  }
@@ -27576,9 +28849,15 @@ var require_fast_uri = __commonJS({
27576
28849
  if (parsed.host) {
27577
28850
  const ipv4result = isIPv4(parsed.host);
27578
28851
  if (ipv4result === false) {
28852
+ const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
27579
28853
  const ipv6result = normalizeIPv6(parsed.host);
27580
- parsed.host = ipv6result.host.toLowerCase();
27581
- isIP = ipv6result.isIPV6;
28854
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
28855
+ malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
28856
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
28857
+ if (malformedIPLiteral) {
28858
+ parsed.error = parsed.error || "URI host is malformed.";
28859
+ malformedAuthorityOrPort = true;
28860
+ }
27582
28861
  } else {
27583
28862
  isIP = true;
27584
28863
  }
@@ -27596,42 +28875,34 @@ var require_fast_uri = __commonJS({
27596
28875
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
27597
28876
  }
27598
28877
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
27599
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
27600
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
27601
- try {
27602
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
27603
- } catch (e) {
27604
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
27605
- }
27606
- }
27607
- }
28878
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
27608
28879
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
27609
28880
  if (uri.indexOf("%") !== -1) {
27610
- if (parsed.scheme !== void 0) {
27611
- parsed.scheme = unescape(parsed.scheme);
27612
- }
27613
- if (parsed.host !== void 0) {
27614
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
28881
+ if (parsed.host !== void 0 && !malformedIPLiteral) {
28882
+ const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
28883
+ parsed.host = reescapeHostDelimiters(host, isIP);
27615
28884
  }
27616
28885
  }
27617
28886
  if (parsed.path) {
27618
28887
  parsed.path = normalizePathEncoding(parsed.path);
27619
28888
  }
28889
+ if (parsed.query) {
28890
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
28891
+ }
27620
28892
  if (parsed.fragment) {
27621
- try {
27622
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
27623
- } catch {
27624
- parsed.error = parsed.error || "URI malformed";
27625
- }
28893
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
27626
28894
  }
27627
28895
  }
27628
28896
  if (schemeHandler && schemeHandler.parse) {
27629
28897
  schemeHandler.parse(parsed, options);
28898
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
28899
+ malformedSchemeSpecific = true;
28900
+ }
27630
28901
  }
27631
28902
  } else {
27632
28903
  parsed.error = parsed.error || "URI can not be parsed.";
27633
28904
  }
27634
- return { parsed, malformedAuthorityOrPort };
28905
+ return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
27635
28906
  }
27636
28907
  function parse2(uri, opts) {
27637
28908
  return parseWithStatus(uri, opts).parsed;
@@ -27640,25 +28911,33 @@ var require_fast_uri = __commonJS({
27640
28911
  return normalizeStringWithStatus(uri, opts).normalized;
27641
28912
  }
27642
28913
  function normalizeStringWithStatus(uri, opts) {
27643
- const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
28914
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
27644
28915
  return {
27645
- normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
27646
- malformedAuthorityOrPort
28916
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
28917
+ malformedAuthorityOrPort,
28918
+ malformedPercentEncoding,
28919
+ malformedSchemeSpecific,
28920
+ malformedHost,
28921
+ malformedScheme
27647
28922
  };
27648
28923
  }
27649
28924
  function normalizeComparableURI(uri, opts) {
27650
- if (typeof uri === "string") {
27651
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
27652
- return malformedAuthorityOrPort ? void 0 : normalized;
28925
+ if (typeof uri !== "string" && typeof uri !== "object") {
28926
+ return void 0;
27653
28927
  }
27654
- if (typeof uri === "object") {
27655
- return serialize(uri, opts);
28928
+ let value;
28929
+ try {
28930
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
28931
+ } catch {
28932
+ return void 0;
27656
28933
  }
28934
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
28935
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
27657
28936
  }
27658
28937
  var fastUri = {
27659
28938
  SCHEMES,
27660
28939
  normalize,
27661
- resolve: resolve3,
28940
+ resolve: resolve4,
27662
28941
  resolveComponent,
27663
28942
  equal,
27664
28943
  serialize,
@@ -31786,7 +33065,7 @@ var init_mcp2 = __esm({
31786
33065
  let task = createTaskResult.task;
31787
33066
  const pollInterval = task.pollInterval ?? 5e3;
31788
33067
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
31789
- await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
33068
+ await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
31790
33069
  const updatedTask = await extra.taskStore.getTask(taskId);
31791
33070
  if (!updatedTask) {
31792
33071
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -32382,12 +33661,12 @@ var init_stdio2 = __esm({
32382
33661
  this.onclose?.();
32383
33662
  }
32384
33663
  send(message) {
32385
- return new Promise((resolve3) => {
33664
+ return new Promise((resolve4) => {
32386
33665
  const json2 = serializeMessage(message);
32387
33666
  if (this._stdout.write(json2)) {
32388
- resolve3();
33667
+ resolve4();
32389
33668
  } else {
32390
- this._stdout.once("drain", resolve3);
33669
+ this._stdout.once("drain", resolve4);
32391
33670
  }
32392
33671
  });
32393
33672
  }
@@ -32619,10 +33898,10 @@ async function buildNodalMcpServer(opts) {
32619
33898
  }
32620
33899
  async function startNodalMcpServer(opts) {
32621
33900
  const server = await buildNodalMcpServer(opts);
32622
- const closed = new Promise((resolve3) => {
32623
- server.server.onclose = resolve3;
32624
- process.stdin.once("end", resolve3);
32625
- process.stdin.once("close", resolve3);
33901
+ const closed = new Promise((resolve4) => {
33902
+ server.server.onclose = resolve4;
33903
+ process.stdin.once("end", resolve4);
33904
+ process.stdin.once("close", resolve4);
32626
33905
  });
32627
33906
  await server.connect(new StdioServerTransport());
32628
33907
  await closed;
@@ -32723,7 +34002,7 @@ __export(reset_exports, {
32723
34002
  });
32724
34003
  import chalk6 from "chalk";
32725
34004
  import prompts2 from "prompts";
32726
- import { rmSync as rmSync2, existsSync as existsSync12 } from "fs";
34005
+ import { rmSync as rmSync2, existsSync as existsSync13 } from "fs";
32727
34006
  async function runReset(options = {}) {
32728
34007
  if (!options.yes) {
32729
34008
  const { confirm } = await prompts2({
@@ -32741,7 +34020,7 @@ async function runReset(options = {}) {
32741
34020
  }
32742
34021
  console.log(chalk6.yellow("Stopping any running processes\u2026"));
32743
34022
  await runDown();
32744
- if (existsSync12(CONFIG_DIR)) {
34023
+ if (existsSync13(CONFIG_DIR)) {
32745
34024
  rmSync2(CONFIG_DIR, { recursive: true, force: true });
32746
34025
  console.log(chalk6.green(` Removed ${CONFIG_DIR}`));
32747
34026
  } else {