sparkecoder 0.1.115 → 0.1.117

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 (162) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +62 -0
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +387 -108
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-Biy5JTop.d.ts → index-Bi8Ek02A.d.ts} +104 -104
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +313 -64
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-CYSKJZ3m.d.ts → schema-ecQSnCMz.d.ts} +3 -3
  12. package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
  13. package/dist/server/index.js +313 -64
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/computer-use.md +7 -0
  16. package/dist/skills/default/recording.md +5 -0
  17. package/dist/tools/index.d.ts +3 -3
  18. package/package.json +1 -1
  19. package/src/skills/default/computer-use.md +7 -0
  20. package/src/skills/default/recording.md +5 -0
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  24. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  25. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  27. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  28. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  29. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
  30. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  32. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  47. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  55. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  62. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  63. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  73. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  78. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  81. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  83. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  84. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  88. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  89. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  92. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  93. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  94. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  96. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  97. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  100. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  102. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  103. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  104. package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
  105. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
  106. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  107. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  108. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  109. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  110. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  111. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +3 -0
  113. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f3e6443f._.js +15 -0
  114. package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +3 -0
  115. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +1 -1
  116. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  117. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  118. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  119. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  120. package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
  121. package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
  122. package/web/.next/standalone/web/.next/static/chunks/44c575e006ddb992.js +13 -0
  123. package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
  124. package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
  125. package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +1 -0
  126. package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +7 -0
  127. package/web/.next/standalone/web/.next/static/static/chunks/44c575e006ddb992.js +13 -0
  128. package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +1 -0
  129. package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +1 -0
  130. package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +255 -3
  131. package/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
  132. package/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
  133. package/web/.next/static/chunks/44c575e006ddb992.js +13 -0
  134. package/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
  135. package/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
  136. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_50c2f239._.js +0 -3
  137. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__6097da17._.js +0 -15
  138. package/web/.next/standalone/web/.next/server/chunks/ssr/web_3b9a2423._.js +0 -3
  139. package/web/.next/standalone/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
  140. package/web/.next/standalone/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
  141. package/web/.next/standalone/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
  142. package/web/.next/standalone/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
  143. package/web/.next/standalone/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  144. package/web/.next/standalone/web/.next/static/static/chunks/60359bdd369c0c72.js +0 -1
  145. package/web/.next/standalone/web/.next/static/static/chunks/a189cacf6d83cf0b.js +0 -13
  146. package/web/.next/standalone/web/.next/static/static/chunks/bef6931fdd8428c8.js +0 -1
  147. package/web/.next/standalone/web/.next/static/static/chunks/c1f73b3fa4353c31.css +0 -1
  148. package/web/.next/standalone/web/.next/static/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  149. package/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
  150. package/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
  151. package/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
  152. package/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
  153. package/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  154. /package/web/.next/standalone/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → static/vLqK4jK7EKdLCpQ-D6-qL}/_buildManifest.js +0 -0
  155. /package/web/.next/standalone/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → static/vLqK4jK7EKdLCpQ-D6-qL}/_clientMiddlewareManifest.json +0 -0
  156. /package/web/.next/standalone/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → static/vLqK4jK7EKdLCpQ-D6-qL}/_ssgManifest.js +0 -0
  157. /package/web/.next/standalone/web/.next/static/{static/pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_buildManifest.js +0 -0
  158. /package/web/.next/standalone/web/.next/static/{static/pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_clientMiddlewareManifest.json +0 -0
  159. /package/web/.next/standalone/web/.next/static/{static/pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_ssgManifest.js +0 -0
  160. /package/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_buildManifest.js +0 -0
  161. /package/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_clientMiddlewareManifest.json +0 -0
  162. /package/web/.next/static/{pSGgtxBjN5_o0YHZgQzXm → vLqK4jK7EKdLCpQ-D6-qL}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -5386,7 +5386,7 @@ function isPathExcluded(relativePath, exclude) {
5386
5386
  }
5387
5387
  async function walkDirectory(dir, include, exclude, baseDir) {
5388
5388
  const { readdirSync: readdirSync4 } = await import("fs");
5389
- const { join: join17, relative: relative10 } = await import("path");
5389
+ const { join: join18, relative: relative10 } = await import("path");
5390
5390
  const files = [];
5391
5391
  function walk(currentDir) {
5392
5392
  let entries;
@@ -5396,7 +5396,7 @@ async function walkDirectory(dir, include, exclude, baseDir) {
5396
5396
  return;
5397
5397
  }
5398
5398
  for (const entry2 of entries) {
5399
- const fullPath = join17(currentDir, entry2.name);
5399
+ const fullPath = join18(currentDir, entry2.name);
5400
5400
  const relativePath = relative10(baseDir, fullPath);
5401
5401
  if (isPathExcluded(relativePath, exclude)) {
5402
5402
  continue;
@@ -8409,6 +8409,68 @@ Headless workers never interfere with desktop workers (they don't touch the scre
8409
8409
  When you spawn a **desktop worker**, include a one-liner in the goal asking it to **record the session by default** (\`screencapture -v -V <seconds> -C\` on macOS) and return the recording path in its result, *unless* the task is long-running / boring / contains sensitive content. The recording lets you (and the user) replay what actually happened on screen. When the worker reports back, mention the recording path in your reply via the original channel.` : ""}
8410
8410
 
8411
8411
  Default bias: **when in doubt, decompose**. Two workers running in parallel and reporting independently is almost always better UX than one worker doing things sequentially.
8412
+
8413
+ ### How to TALK to the user (versus how you reason internally)
8414
+
8415
+ All of the rules below \u2014 decomposition, parallel spawning, "don't invent commands", load-the-skill, etc. \u2014 are **your internal operating procedure**. They are how you decide what to do. They are NOT something to recite back to the user.
8416
+
8417
+ When replying to the user (Slack, web, or any channel), be a normal helpful assistant:
8418
+
8419
+ - Tell them *what you're doing*, not *how you're doing it internally*.
8420
+ - Don't quote your own prompt language ("just the goal", "read the skill", "no step-by-step from me", "decomposing into parallel workers"). The user doesn't care about your delegation pattern.
8421
+ - Don't narrate "handing off to a worker now" unless it's actually relevant (e.g. they asked how you work, or the work is going to take a noticeably long time).
8422
+ - Skip ceremony. A short acknowledgement + a brief description of the actual task + (optionally) a heads-up if there's anything they need to do or avoid is plenty.
8423
+
8424
+ | What you might be tempted to say | What you should say instead |
8425
+ |---|---|
8426
+ | *"Got it \u2014 handing it off with just the goal + 'read the skills and figure it out.' No step-by-step from me."* | *"On it. Recording a screen capture of the Weather app showing Anchorage. Don't touch the keyboard while it's running \u2014 I'll post the video when it's done."* |
8427
+ | *"Decomposing into two parallel workers: one headless, one desktop."* | *"Running the tests and pulling the diff at the same time. Back in ~30s."* |
8428
+ | *"Spawning worker \`screenshot-calc\` with goal: \u2026"* | *"Taking a screenshot of Calculator now."* |
8429
+ | *"I'll relay the user's instructions to the worker verbatim."* | *(say nothing \u2014 just do it)* |
8430
+
8431
+ If the user explicitly asks how you work, *then* you can explain the orchestrator/worker split. Otherwise: less is more.
8432
+
8433
+ ### How to write a worker goal (and what NOT to put in it)
8434
+
8435
+ You delegate; the worker executes. Stay at the **what** level, not the **how**.
8436
+
8437
+ **DO** put in the goal:
8438
+
8439
+ - The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
8440
+ - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill computer-use\`).
8441
+ - Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
8442
+ - Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
8443
+
8444
+ **DO NOT** put in the goal (when you're inventing commands from memory):
8445
+
8446
+ - Literal shell commands (\`screencapture -v -V 45 -C /tmp/...\`, \`osascript -e ...\`, \`cliclick kp:cmd+f\`).
8447
+ - Pre-written step-by-step bash that you're drafting from training-data recall.
8448
+ - The names of tools you can't see in your current toolset.
8449
+
8450
+ If you have to reach for memory to write a command, **you're guessing** \u2014 your training data has lots of plausible-looking commands that are subtly wrong or stale. The worker will dutifully follow your wrong instructions instead of consulting the source of truth.
8451
+
8452
+ **Exceptions \u2014 concrete steps ARE appropriate ONLY when relayed verbatim:**
8453
+
8454
+ You CAN include literal step-by-step commands when one of these is true:
8455
+
8456
+ 1. **The user gave the steps to you.** Their message says "first run X, then Y, then commit" \u2192 forward those verbatim. That's the user's intent, not your guess.
8457
+ 2. **A previous turn already established them.** An earlier worker reported "the deploy script lives at \`./scripts/deploy.sh --env prod\`" \u2192 you can hand that command to the next worker.
8458
+ 3. **The conversation history contains them.** A documented \`bash\` invocation from earlier in the chat, an error message that revealed the right path, a CI log the user shared \u2014 anything already concrete in context.
8459
+
8460
+ In all three cases, **label the source** in the goal: *"Per your instructions earlier: 1. run X, 2. run Y..."* or *"Reusing the command worker-foo found last turn: \`./scripts/deploy.sh\`"*.
8461
+
8462
+ What you do NOT do:
8463
+
8464
+ - **Don't paraphrase or "improve" a skill into the goal.** If the recording skill describes \`sparkecoder record start/stop\`, the goal just says *"per the recording skill, bracket the work with start/stop"* \u2014 never quote the exact command. The worker loads the skill and reads the canonical version itself. You quoting it just adds a way for the quote to drift behind the source of truth.
8465
+ - **Don't reason your way to a command.** "I think macOS uses \`open -a Weather\` to launch apps" is exactly the kind of training-data recall that produces stale or wrong commands. Don't include it.
8466
+
8467
+ The rule is: **relay, never invent \u2014 and never quote a skill.** Your job is to (a) tell the worker which skills to load, (b) state the objective and acceptance criteria, (c) relay any concrete steps the user or prior context already established. Workers do the looking-up; you do the dispatching.
8468
+
8469
+ Bad goal (don't do this):
8470
+ > "Start a screen recording with \`screencapture -v -V 45 -C /tmp/weather.mov &\`, then open Weather with \`open -a Weather\`, then click the search bar with cliclick, type 'Anchorage'..."
8471
+
8472
+ Good goal (do this):
8473
+ > "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill computer-use\` first; use the canonical commands from those skills. Verify the Anchorage temperature is visible in a final screenshot before completing. Return the recording path + a one-line summary of the forecast."
8412
8474
  `;
8413
8475
  }
8414
8476
  function createSummaryPrompt(conversationHistory) {
@@ -11325,11 +11387,11 @@ ${p.text}` : p.text;
11325
11387
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
11326
11388
  if (!isRemoteConfigured2()) return [];
11327
11389
  const { readFile: readFile12 } = await import("fs/promises");
11328
- const { join: join17, basename: basename6 } = await import("path");
11390
+ const { join: join18, basename: basename6 } = await import("path");
11329
11391
  const urls = [];
11330
11392
  for (const filePath of filePaths) {
11331
11393
  try {
11332
- const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
11394
+ const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
11333
11395
  const fileName = basename6(fullPath);
11334
11396
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
11335
11397
  const mimeMap = {
@@ -11533,6 +11595,121 @@ var init_session_lock = __esm({
11533
11595
  }
11534
11596
  });
11535
11597
 
11598
+ // src/orchestrator/webhook-events.ts
11599
+ import { existsSync as existsSync18, readFileSync as readFileSync9, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
11600
+ import { dirname as dirname6, join as join12 } from "path";
11601
+ import { nanoid as nanoid10 } from "nanoid";
11602
+ function logFilePath() {
11603
+ return join12(getAppDataDirectory(), "webhook-events.jsonl");
11604
+ }
11605
+ function ensureLoaded() {
11606
+ if (cache !== null) return cache;
11607
+ cache = [];
11608
+ try {
11609
+ const p = logFilePath();
11610
+ if (!existsSync18(p)) return cache;
11611
+ const lines = readFileSync9(p, "utf-8").split("\n").filter(Boolean);
11612
+ for (const line of lines) {
11613
+ try {
11614
+ cache.push(JSON.parse(line));
11615
+ } catch {
11616
+ }
11617
+ }
11618
+ if (cache.length > MAX_EVENTS) {
11619
+ cache = cache.slice(-MAX_EVENTS);
11620
+ try {
11621
+ writeFileSync3(p, cache.map((e) => JSON.stringify(e)).join("\n") + "\n");
11622
+ } catch {
11623
+ }
11624
+ }
11625
+ } catch {
11626
+ }
11627
+ return cache;
11628
+ }
11629
+ function appendEvent(ev) {
11630
+ const list = ensureLoaded();
11631
+ list.push(ev);
11632
+ if (list.length > MAX_EVENTS) list.shift();
11633
+ try {
11634
+ const p = logFilePath();
11635
+ mkdirSync7(dirname6(p), { recursive: true });
11636
+ appendFileSync3(p, JSON.stringify(ev) + "\n");
11637
+ } catch {
11638
+ }
11639
+ }
11640
+ function recordEvent(ev) {
11641
+ const full = {
11642
+ id: ev.id ?? nanoid10(),
11643
+ ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
11644
+ source: ev.source,
11645
+ status: ev.status,
11646
+ subtype: ev.subtype,
11647
+ channel: ev.channel,
11648
+ user: ev.user,
11649
+ textSnippet: ev.textSnippet?.slice(0, 200),
11650
+ dropReason: ev.dropReason,
11651
+ error: ev.error,
11652
+ sessionId: ev.sessionId,
11653
+ durationMs: ev.durationMs,
11654
+ meta: ev.meta
11655
+ };
11656
+ appendEvent(full);
11657
+ return full.id;
11658
+ }
11659
+ function updateEvent(id, patch) {
11660
+ const list = ensureLoaded();
11661
+ const i = list.findIndex((e) => e.id === id);
11662
+ if (i < 0) return;
11663
+ list[i] = { ...list[i], ...patch };
11664
+ try {
11665
+ const p = logFilePath();
11666
+ mkdirSync7(dirname6(p), { recursive: true });
11667
+ writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
11668
+ } catch {
11669
+ }
11670
+ }
11671
+ function listEvents(filter = {}) {
11672
+ const list = ensureLoaded();
11673
+ const q = filter.q?.toLowerCase();
11674
+ const sinceTs = filter.since ? Date.parse(filter.since) : -Infinity;
11675
+ const beforeTs = filter.before ? Date.parse(filter.before) : Infinity;
11676
+ const matched = list.filter((e) => {
11677
+ if (filter.source && e.source !== filter.source) return false;
11678
+ if (filter.status && e.status !== filter.status) return false;
11679
+ const t = Date.parse(e.ts);
11680
+ if (t < sinceTs) return false;
11681
+ if (t >= beforeTs) return false;
11682
+ if (q) {
11683
+ const hay = `${e.channel ?? ""} ${e.user ?? ""} ${e.textSnippet ?? ""} ${e.dropReason ?? ""} ${e.error ?? ""} ${e.subtype ?? ""}`.toLowerCase();
11684
+ if (!hay.includes(q)) return false;
11685
+ }
11686
+ return true;
11687
+ });
11688
+ matched.reverse();
11689
+ const offset = Math.max(0, filter.offset ?? 0);
11690
+ const limit = Math.min(500, Math.max(1, filter.limit ?? 50));
11691
+ return {
11692
+ events: matched.slice(offset, offset + limit),
11693
+ total: matched.length
11694
+ };
11695
+ }
11696
+ function clearAllEvents() {
11697
+ cache = [];
11698
+ try {
11699
+ writeFileSync3(logFilePath(), "");
11700
+ } catch {
11701
+ }
11702
+ }
11703
+ var MAX_EVENTS, cache;
11704
+ var init_webhook_events = __esm({
11705
+ "src/orchestrator/webhook-events.ts"() {
11706
+ "use strict";
11707
+ init_config();
11708
+ MAX_EVENTS = 1e3;
11709
+ cache = null;
11710
+ }
11711
+ });
11712
+
11536
11713
  // src/orchestrator/daemon.ts
11537
11714
  var daemon_exports = {};
11538
11715
  __export(daemon_exports, {
@@ -11594,6 +11771,17 @@ async function runDaemonTurn(sessionId, events) {
11594
11771
  });
11595
11772
  const finishedAt = /* @__PURE__ */ new Date();
11596
11773
  const trimmed = text.trim();
11774
+ recordEvent({
11775
+ source: "daemon",
11776
+ status: error ? "failed" : "completed",
11777
+ channel: events[0]?.ref?.channel ?? void 0,
11778
+ user: events[0]?.ref?.user,
11779
+ textSnippet: trimmed.slice(0, 200),
11780
+ error,
11781
+ sessionId,
11782
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
11783
+ meta: { triggeredBy: events.map((e) => e.content?.slice(0, 80)) }
11784
+ });
11597
11785
  broadcast({ sessionId, text: trimmed, triggeredBy: events, startedAt, finishedAt, error });
11598
11786
  }
11599
11787
  var listeners;
@@ -11604,6 +11792,7 @@ var init_daemon = __esm({
11604
11792
  init_session_lock();
11605
11793
  init_db();
11606
11794
  init_inbox();
11795
+ init_webhook_events();
11607
11796
  listeners = /* @__PURE__ */ new Map();
11608
11797
  }
11609
11798
  });
@@ -11855,7 +12044,7 @@ import chalk from "chalk";
11855
12044
  import ora from "ora";
11856
12045
  import "dotenv/config";
11857
12046
  import { createInterface } from "readline";
11858
- import { dirname as dirname8 } from "path";
12047
+ import { dirname as dirname9 } from "path";
11859
12048
  import { fileURLToPath as fileURLToPath5 } from "url";
11860
12049
 
11861
12050
  // src/server/index.ts
@@ -11864,8 +12053,8 @@ import { Hono as Hono9 } from "hono";
11864
12053
  import { serve } from "@hono/node-server";
11865
12054
  import { cors } from "hono/cors";
11866
12055
  import { logger } from "hono/logger";
11867
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
11868
- import { resolve as resolve11, dirname as dirname7, join as join15 } from "path";
12056
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
12057
+ import { resolve as resolve11, dirname as dirname8, join as join16 } from "path";
11869
12058
  import { spawn as spawn2 } from "child_process";
11870
12059
  import { createServer as createNetServer } from "net";
11871
12060
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -11879,10 +12068,10 @@ init_checkpoints();
11879
12068
  import { Hono } from "hono";
11880
12069
  import { zValidator } from "@hono/zod-validator";
11881
12070
  import { z as z17 } from "zod";
11882
- import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
12071
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
11883
12072
  import { readdir as readdir6 } from "fs/promises";
11884
- import { join as join12, basename as basename5, extname as extname8, relative as relative9 } from "path";
11885
- import { nanoid as nanoid10 } from "nanoid";
12073
+ import { join as join13, basename as basename5, extname as extname8, relative as relative9 } from "path";
12074
+ import { nanoid as nanoid11 } from "nanoid";
11886
12075
 
11887
12076
  // src/tasks/agent-status.ts
11888
12077
  init_questions();
@@ -12523,12 +12712,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
12523
12712
  });
12524
12713
  function getAttachmentsDir(sessionId) {
12525
12714
  const appDataDir = getAppDataDirectory();
12526
- return join12(appDataDir, "attachments", sessionId);
12715
+ return join13(appDataDir, "attachments", sessionId);
12527
12716
  }
12528
12717
  function ensureAttachmentsDir(sessionId) {
12529
12718
  const dir = getAttachmentsDir(sessionId);
12530
- if (!existsSync18(dir)) {
12531
- mkdirSync7(dir, { recursive: true });
12719
+ if (!existsSync19(dir)) {
12720
+ mkdirSync8(dir, { recursive: true });
12532
12721
  }
12533
12722
  return dir;
12534
12723
  }
@@ -12539,12 +12728,12 @@ sessions2.get("/:id/attachments", async (c) => {
12539
12728
  return c.json({ error: "Session not found" }, 404);
12540
12729
  }
12541
12730
  const dir = getAttachmentsDir(sessionId);
12542
- if (!existsSync18(dir)) {
12731
+ if (!existsSync19(dir)) {
12543
12732
  return c.json({ sessionId, attachments: [], count: 0 });
12544
12733
  }
12545
12734
  const files = readdirSync3(dir);
12546
12735
  const attachments = files.map((filename) => {
12547
- const filePath = join12(dir, filename);
12736
+ const filePath = join13(dir, filename);
12548
12737
  const stats = statSync2(filePath);
12549
12738
  return {
12550
12739
  id: filename.split("_")[0],
@@ -12576,12 +12765,12 @@ sessions2.post("/:id/attachments", async (c) => {
12576
12765
  return c.json({ error: "No file provided" }, 400);
12577
12766
  }
12578
12767
  const dir = ensureAttachmentsDir(sessionId);
12579
- const id = nanoid10(10);
12768
+ const id = nanoid11(10);
12580
12769
  const ext = extname8(file.name) || "";
12581
12770
  const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
12582
- const filePath = join12(dir, safeFilename);
12771
+ const filePath = join13(dir, safeFilename);
12583
12772
  const arrayBuffer = await file.arrayBuffer();
12584
- writeFileSync3(filePath, Buffer.from(arrayBuffer));
12773
+ writeFileSync4(filePath, Buffer.from(arrayBuffer));
12585
12774
  return c.json({
12586
12775
  id,
12587
12776
  filename: file.name,
@@ -12602,16 +12791,16 @@ sessions2.post("/:id/attachments", async (c) => {
12602
12791
  return c.json({ error: "Missing filename or data" }, 400);
12603
12792
  }
12604
12793
  const dir = ensureAttachmentsDir(sessionId);
12605
- const id = nanoid10(10);
12794
+ const id = nanoid11(10);
12606
12795
  const ext = extname8(body.filename) || "";
12607
12796
  const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
12608
- const filePath = join12(dir, safeFilename);
12797
+ const filePath = join13(dir, safeFilename);
12609
12798
  let base64Data = body.data;
12610
12799
  if (base64Data.includes(",")) {
12611
12800
  base64Data = base64Data.split(",")[1];
12612
12801
  }
12613
12802
  const buffer = Buffer.from(base64Data, "base64");
12614
- writeFileSync3(filePath, buffer);
12803
+ writeFileSync4(filePath, buffer);
12615
12804
  return c.json({
12616
12805
  id,
12617
12806
  filename: body.filename,
@@ -12634,7 +12823,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12634
12823
  return c.json({ error: "Session not found" }, 404);
12635
12824
  }
12636
12825
  const dir = getAttachmentsDir(sessionId);
12637
- if (!existsSync18(dir)) {
12826
+ if (!existsSync19(dir)) {
12638
12827
  return c.json({ error: "Attachment not found" }, 404);
12639
12828
  }
12640
12829
  const files = readdirSync3(dir);
@@ -12642,7 +12831,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12642
12831
  if (!file) {
12643
12832
  return c.json({ error: "Attachment not found" }, 404);
12644
12833
  }
12645
- const filePath = join12(dir, file);
12834
+ const filePath = join13(dir, file);
12646
12835
  unlinkSync3(filePath);
12647
12836
  return c.json({ success: true, id: attachmentId });
12648
12837
  });
@@ -12725,7 +12914,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
12725
12914
  const entries = await readdir6(currentDir, { withFileTypes: true });
12726
12915
  for (const entry2 of entries) {
12727
12916
  if (results.length >= limit * 2) break;
12728
- const fullPath = join12(currentDir, entry2.name);
12917
+ const fullPath = join13(currentDir, entry2.name);
12729
12918
  const relativePath = relative9(baseDir, fullPath);
12730
12919
  if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
12731
12920
  continue;
@@ -12773,7 +12962,7 @@ sessions2.get(
12773
12962
  return c.json({ error: "Session not found" }, 404);
12774
12963
  }
12775
12964
  const workingDirectory = session.workingDirectory;
12776
- if (!existsSync18(workingDirectory)) {
12965
+ if (!existsSync19(workingDirectory)) {
12777
12966
  return c.json({
12778
12967
  sessionId,
12779
12968
  workingDirectory,
@@ -12887,8 +13076,8 @@ init_config();
12887
13076
  import { Hono as Hono2 } from "hono";
12888
13077
  import { zValidator as zValidator2 } from "@hono/zod-validator";
12889
13078
  import { z as z18 } from "zod";
12890
- import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4 } from "fs";
12891
- import { join as join13 } from "path";
13079
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
13080
+ import { join as join14 } from "path";
12892
13081
 
12893
13082
  // src/server/resumable-stream.ts
12894
13083
  import { createResumableStreamContext } from "resumable-stream/generic";
@@ -12975,7 +13164,7 @@ var streamContext = createResumableStreamContext({
12975
13164
 
12976
13165
  // src/server/routes/agents.ts
12977
13166
  init_checkpoints();
12978
- import { nanoid as nanoid11 } from "nanoid";
13167
+ import { nanoid as nanoid12 } from "nanoid";
12979
13168
  init_stream_proxy();
12980
13169
  init_recorder();
12981
13170
  init_remote();
@@ -13095,12 +13284,12 @@ var rejectSchema = z18.object({
13095
13284
  var streamAbortControllers = /* @__PURE__ */ new Map();
13096
13285
  function getAttachmentsDirectory(sessionId) {
13097
13286
  const appDataDir = getAppDataDirectory();
13098
- return join13(appDataDir, "attachments", sessionId);
13287
+ return join14(appDataDir, "attachments", sessionId);
13099
13288
  }
13100
13289
  async function saveAttachmentToDisk(sessionId, attachment, index) {
13101
13290
  const attachmentsDir = getAttachmentsDirectory(sessionId);
13102
- if (!existsSync19(attachmentsDir)) {
13103
- mkdirSync8(attachmentsDir, { recursive: true });
13291
+ if (!existsSync20(attachmentsDir)) {
13292
+ mkdirSync9(attachmentsDir, { recursive: true });
13104
13293
  }
13105
13294
  let filename = attachment.filename;
13106
13295
  if (!filename) {
@@ -13118,8 +13307,8 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
13118
13307
  attachment.mediaType = resized.mediaType;
13119
13308
  attachment.data = buffer.toString("base64");
13120
13309
  }
13121
- const filePath = join13(attachmentsDir, filename);
13122
- writeFileSync4(filePath, buffer);
13310
+ const filePath = join14(attachmentsDir, filename);
13311
+ writeFileSync5(filePath, buffer);
13123
13312
  return filePath;
13124
13313
  }
13125
13314
  function stripDataUrlPrefix2(data) {
@@ -13555,7 +13744,7 @@ ${prompt}` });
13555
13744
  });
13556
13745
  } catch {
13557
13746
  }
13558
- const streamId = `stream_${id}_${nanoid11(10)}`;
13747
+ const streamId = `stream_${id}_${nanoid12(10)}`;
13559
13748
  console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
13560
13749
  await activeStreamQueries.create(id, streamId);
13561
13750
  const stream = await streamContext.resumableStream(
@@ -13760,7 +13949,7 @@ agents.post(
13760
13949
  });
13761
13950
  const session = agent.getSession();
13762
13951
  const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
13763
- const streamId = `stream_${session.id}_${nanoid11(10)}`;
13952
+ const streamId = `stream_${session.id}_${nanoid12(10)}`;
13764
13953
  await createCheckpoint(session.id, session.workingDirectory, 0);
13765
13954
  await activeStreamQueries.create(session.id, streamId);
13766
13955
  const createQuickStreamProducer = () => {
@@ -14079,26 +14268,26 @@ init_config();
14079
14268
  import { Hono as Hono3 } from "hono";
14080
14269
  import { zValidator as zValidator3 } from "@hono/zod-validator";
14081
14270
  import { z as z19 } from "zod";
14082
- import { readFileSync as readFileSync9 } from "fs";
14271
+ import { readFileSync as readFileSync10 } from "fs";
14083
14272
  import { fileURLToPath as fileURLToPath3 } from "url";
14084
- import { dirname as dirname6, join as join14 } from "path";
14273
+ import { dirname as dirname7, join as join15 } from "path";
14085
14274
  var __filename = fileURLToPath3(import.meta.url);
14086
- var __dirname = dirname6(__filename);
14275
+ var __dirname = dirname7(__filename);
14087
14276
  var possiblePaths = [
14088
- join14(__dirname, "../package.json"),
14277
+ join15(__dirname, "../package.json"),
14089
14278
  // From dist/server -> dist/../package.json
14090
- join14(__dirname, "../../package.json"),
14279
+ join15(__dirname, "../../package.json"),
14091
14280
  // From dist/server (if nested differently)
14092
- join14(__dirname, "../../../package.json"),
14281
+ join15(__dirname, "../../../package.json"),
14093
14282
  // From src/server/routes (development)
14094
- join14(process.cwd(), "package.json")
14283
+ join15(process.cwd(), "package.json")
14095
14284
  // From current working directory
14096
14285
  ];
14097
14286
  var currentVersion = "0.0.0";
14098
14287
  var packageName = "sparkecoder";
14099
14288
  for (const packageJsonPath of possiblePaths) {
14100
14289
  try {
14101
- const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
14290
+ const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
14102
14291
  if (packageJson.name === "sparkecoder") {
14103
14292
  currentVersion = packageJson.version || "0.0.0";
14104
14293
  packageName = packageJson.name || "sparkecoder";
@@ -14548,7 +14737,7 @@ init_config();
14548
14737
  import { Hono as Hono5 } from "hono";
14549
14738
  import { zValidator as zValidator5 } from "@hono/zod-validator";
14550
14739
  import { z as z21 } from "zod";
14551
- import { nanoid as nanoid12 } from "nanoid";
14740
+ import { nanoid as nanoid13 } from "nanoid";
14552
14741
  init_questions();
14553
14742
  var tasks = new Hono5();
14554
14743
  var taskAbortControllers = /* @__PURE__ */ new Map();
@@ -14629,7 +14818,7 @@ tasks.post(
14629
14818
  const taskId = agent.sessionId;
14630
14819
  const abortController = new AbortController();
14631
14820
  taskAbortControllers.set(taskId, abortController);
14632
- const streamId = `stream_${taskId}_${nanoid12(10)}`;
14821
+ const streamId = `stream_${taskId}_${nanoid13(10)}`;
14633
14822
  await activeStreamQueries.create(taskId, streamId);
14634
14823
  const taskStreamProducer = () => {
14635
14824
  const { readable, writable } = new TransformStream();
@@ -14855,6 +15044,7 @@ function verifySlackSignature(opts) {
14855
15044
  // src/server/routes/slack.ts
14856
15045
  init_client3();
14857
15046
  init_slack();
15047
+ init_webhook_events();
14858
15048
  init_inbox();
14859
15049
  var recentlyHandled = /* @__PURE__ */ new Map();
14860
15050
  var MAX_RECENT = 1e3;
@@ -14895,7 +15085,17 @@ slack.post("/events", async (c) => {
14895
15085
  }
14896
15086
  if (payload?.type === "event_callback" && payload?.event) {
14897
15087
  const ev = payload.event;
15088
+ const auditId = recordEvent({
15089
+ source: "slack",
15090
+ status: "received",
15091
+ subtype: ev.type === "message" ? `message.${ev.channel_type ?? "channels"}` : ev.type,
15092
+ channel: ev.channel,
15093
+ user: ev.user,
15094
+ textSnippet: typeof ev.text === "string" ? ev.text : void 0,
15095
+ meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
15096
+ });
14898
15097
  if (alreadyHandled(ev.channel, ev.ts)) {
15098
+ updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
14899
15099
  return c.json({ ok: true });
14900
15100
  }
14901
15101
  const { event: inbound, dropReason } = slackEventToInboundResult(ev);
@@ -14905,6 +15105,7 @@ slack.post("/events", async (c) => {
14905
15105
  const ours = isThreadOwned(ev.channel, ev.thread_ts) || await threadBelongsToUs(ev.channel, ev.thread_ts);
14906
15106
  if (!ours) {
14907
15107
  console.log(`[slack] dropping thread reply in unknown thread: channel=${ev.channel} thread=${ev.thread_ts}`);
15108
+ updateEvent(auditId, { status: "dropped", dropReason: "thread_not_owned" });
14908
15109
  return c.json({ ok: true });
14909
15110
  }
14910
15111
  }
@@ -14917,8 +15118,12 @@ slack.post("/events", async (c) => {
14917
15118
  const orchestratorId = await findOrCreateOrchestratorId();
14918
15119
  if (orchestratorId) {
14919
15120
  pushToInbox(orchestratorId, inbound);
15121
+ updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
15122
+ } else {
15123
+ updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
14920
15124
  }
14921
15125
  } else if (dropReason) {
15126
+ updateEvent(auditId, { status: "dropped", dropReason });
14922
15127
  const userFacingDrops = ["user_not_allowed", "channel_not_allowed", "dm_blocked"];
14923
15128
  if (userFacingDrops.includes(dropReason)) {
14924
15129
  console.log(`[slack] dropped event from user=${payload.event.user} channel=${payload.event.channel}: ${dropReason}`);
@@ -14981,13 +15186,30 @@ async function sendDeniedReply(event) {
14981
15186
  init_webhooks_store();
14982
15187
  init_webhook();
14983
15188
  init_inbox();
15189
+ init_webhook_events();
14984
15190
  import { Hono as Hono7 } from "hono";
14985
15191
  var inbox = new Hono7();
14986
15192
  inbox.post("/:token", async (c) => {
14987
15193
  const token = c.req.param("token");
14988
- if (!token || token.length < 16) return c.json({ error: "invalid token" }, 401);
15194
+ if (!token || token.length < 16) {
15195
+ recordEvent({
15196
+ source: "inbox",
15197
+ status: "dropped",
15198
+ dropReason: "invalid_token_format",
15199
+ meta: { tokenLen: token?.length ?? 0 }
15200
+ });
15201
+ return c.json({ error: "invalid token" }, 401);
15202
+ }
14989
15203
  const lookup = await findByToken(token);
14990
- if (!lookup) return c.json({ error: "unknown token" }, 404);
15204
+ if (!lookup) {
15205
+ recordEvent({
15206
+ source: "inbox",
15207
+ status: "dropped",
15208
+ dropReason: "unknown_token",
15209
+ meta: { tokenPrefix: token.slice(0, 8) + "\u2026" }
15210
+ });
15211
+ return c.json({ error: "unknown token" }, 404);
15212
+ }
14991
15213
  let body;
14992
15214
  const contentType = c.req.header("content-type") || "";
14993
15215
  try {
@@ -15009,6 +15231,14 @@ inbox.post("/:token", async (c) => {
15009
15231
  pushToInbox(lookup.orchestratorSessionId, event);
15010
15232
  void recordHit(lookup.orchestratorSessionId, lookup.webhook.id).catch(() => {
15011
15233
  });
15234
+ recordEvent({
15235
+ source: "inbox",
15236
+ status: "routed",
15237
+ channel: lookup.webhook.name,
15238
+ textSnippet: typeof body === "string" ? body : JSON.stringify(body).slice(0, 200),
15239
+ sessionId: lookup.orchestratorSessionId,
15240
+ meta: { wake: lookup.webhook.wake, webhookId: lookup.webhook.id }
15241
+ });
15012
15242
  return c.json({ ok: true, queued: true, wake: lookup.webhook.wake });
15013
15243
  });
15014
15244
 
@@ -15020,6 +15250,7 @@ init_webhooks_store();
15020
15250
  init_messenger();
15021
15251
  init_store();
15022
15252
  init_pool();
15253
+ init_webhook_events();
15023
15254
  import { Hono as Hono8 } from "hono";
15024
15255
  import { zValidator as zValidator6 } from "@hono/zod-validator";
15025
15256
  import { z as z22 } from "zod";
@@ -15078,6 +15309,24 @@ function webhookPrefix() {
15078
15309
  const token = cfg?.webhooks?.token;
15079
15310
  return token ? `/w/${token}` : "/api";
15080
15311
  }
15312
+ integrations.get("/events", async (c) => {
15313
+ const q = c.req.query();
15314
+ const filter = {
15315
+ source: q.source,
15316
+ status: q.status,
15317
+ q: q.q,
15318
+ since: q.since,
15319
+ before: q.before,
15320
+ limit: q.limit ? Math.min(500, Math.max(1, parseInt(q.limit, 10))) : 50,
15321
+ offset: q.offset ? Math.max(0, parseInt(q.offset, 10)) : 0
15322
+ };
15323
+ const { events, total } = listEvents(filter);
15324
+ return c.json({ events, total, limit: filter.limit, offset: filter.offset });
15325
+ });
15326
+ integrations.delete("/events", async (c) => {
15327
+ clearAllEvents();
15328
+ return c.json({ ok: true });
15329
+ });
15081
15330
  integrations.get("/", async (c) => {
15082
15331
  const cfg = getConfig();
15083
15332
  return c.json({
@@ -15536,13 +15785,13 @@ var DEFAULT_WEB_PORT = 6969;
15536
15785
  var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
15537
15786
  function getWebDirectory() {
15538
15787
  try {
15539
- const currentDir = dirname7(fileURLToPath4(import.meta.url));
15788
+ const currentDir = dirname8(fileURLToPath4(import.meta.url));
15540
15789
  const webDir = resolve11(currentDir, "..", "web");
15541
- if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
15790
+ if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
15542
15791
  return webDir;
15543
15792
  }
15544
15793
  const altWebDir = resolve11(currentDir, "..", "..", "web");
15545
- if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
15794
+ if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
15546
15795
  return altWebDir;
15547
15796
  }
15548
15797
  return null;
@@ -15600,23 +15849,23 @@ async function findWebPort(preferredPort) {
15600
15849
  return { port: preferredPort, alreadyRunning: false };
15601
15850
  }
15602
15851
  function hasProductionBuild(webDir) {
15603
- const buildIdPath = join15(webDir, ".next", "BUILD_ID");
15604
- return existsSync20(buildIdPath);
15852
+ const buildIdPath = join16(webDir, ".next", "BUILD_ID");
15853
+ return existsSync21(buildIdPath);
15605
15854
  }
15606
15855
  function hasSourceFiles(webDir) {
15607
- const appDir = join15(webDir, "src", "app");
15608
- const pagesDir = join15(webDir, "src", "pages");
15609
- const rootAppDir = join15(webDir, "app");
15610
- const rootPagesDir = join15(webDir, "pages");
15611
- return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
15856
+ const appDir = join16(webDir, "src", "app");
15857
+ const pagesDir = join16(webDir, "src", "pages");
15858
+ const rootAppDir = join16(webDir, "app");
15859
+ const rootPagesDir = join16(webDir, "pages");
15860
+ return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
15612
15861
  }
15613
15862
  function getStandaloneServerPath(webDir) {
15614
15863
  const possiblePaths2 = [
15615
- join15(webDir, ".next", "standalone", "server.js"),
15616
- join15(webDir, ".next", "standalone", "web", "server.js")
15864
+ join16(webDir, ".next", "standalone", "server.js"),
15865
+ join16(webDir, ".next", "standalone", "web", "server.js")
15617
15866
  ];
15618
15867
  for (const serverPath of possiblePaths2) {
15619
- if (existsSync20(serverPath)) {
15868
+ if (existsSync21(serverPath)) {
15620
15869
  return serverPath;
15621
15870
  }
15622
15871
  }
@@ -15656,15 +15905,15 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15656
15905
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
15657
15906
  return { process: null, port: actualPort };
15658
15907
  }
15659
- const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
15660
- const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
15908
+ const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
15909
+ const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
15661
15910
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
15662
15911
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
15663
15912
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
15664
15913
  const runtimeConfig = { apiBaseUrl: apiUrl };
15665
- const runtimeConfigPath = join15(webDir, "runtime-config.json");
15914
+ const runtimeConfigPath = join16(webDir, "runtime-config.json");
15666
15915
  try {
15667
- writeFileSync5(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15916
+ writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15668
15917
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
15669
15918
  } catch (err) {
15670
15919
  if (!quiet) console.warn(` \u26A0 Could not write runtime config: ${err}`);
@@ -15684,7 +15933,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15684
15933
  if (standaloneServerPath) {
15685
15934
  command = "node";
15686
15935
  args = ["server.js"];
15687
- cwd = dirname7(standaloneServerPath);
15936
+ cwd = dirname8(standaloneServerPath);
15688
15937
  webEnv.PORT = String(actualPort);
15689
15938
  webEnv.HOSTNAME = "0.0.0.0";
15690
15939
  if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
@@ -15877,8 +16126,8 @@ async function startServer(options = {}) {
15877
16126
  if (options.workingDirectory) {
15878
16127
  config.resolvedWorkingDirectory = options.workingDirectory;
15879
16128
  }
15880
- if (!existsSync20(config.resolvedWorkingDirectory)) {
15881
- mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
16129
+ if (!existsSync21(config.resolvedWorkingDirectory)) {
16130
+ mkdirSync10(config.resolvedWorkingDirectory, { recursive: true });
15882
16131
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
15883
16132
  }
15884
16133
  if (!config.resolvedRemoteServer.url) {
@@ -16422,18 +16671,18 @@ function generateOpenAPISpec() {
16422
16671
  init_config();
16423
16672
  init_semantic();
16424
16673
  init_db();
16425
- import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync6, readFileSync as readFileSync10, existsSync as existsSync21, statSync as statSync3 } from "fs";
16426
- import { resolve as resolve12, join as join16 } from "path";
16674
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync7, readFileSync as readFileSync11, existsSync as existsSync22, statSync as statSync3 } from "fs";
16675
+ import { resolve as resolve12, join as join17 } from "path";
16427
16676
  function getCliVersion() {
16428
- const here = dirname8(fileURLToPath5(import.meta.url));
16677
+ const here = dirname9(fileURLToPath5(import.meta.url));
16429
16678
  const candidates = [
16430
- join16(here, "..", "package.json"),
16431
- join16(here, "..", "..", "package.json"),
16432
- join16(process.cwd(), "package.json")
16679
+ join17(here, "..", "package.json"),
16680
+ join17(here, "..", "..", "package.json"),
16681
+ join17(process.cwd(), "package.json")
16433
16682
  ];
16434
16683
  for (const p of candidates) {
16435
16684
  try {
16436
- const pkg = JSON.parse(readFileSync10(p, "utf8"));
16685
+ const pkg = JSON.parse(readFileSync11(p, "utf8"));
16437
16686
  if (pkg.name === "sparkecoder" && pkg.version) return pkg.version;
16438
16687
  } catch {
16439
16688
  }
@@ -17096,8 +17345,8 @@ program.command("task").description("Run an autonomous task that completes witho
17096
17345
  let outputSchema;
17097
17346
  try {
17098
17347
  const schemaStr = options.schema;
17099
- if (existsSync21(schemaStr)) {
17100
- outputSchema = JSON.parse(readFileSync10(schemaStr, "utf-8"));
17348
+ if (existsSync22(schemaStr)) {
17349
+ outputSchema = JSON.parse(readFileSync11(schemaStr, "utf-8"));
17101
17350
  } else {
17102
17351
  outputSchema = JSON.parse(schemaStr);
17103
17352
  }
@@ -17164,19 +17413,19 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
17164
17413
  let configLocation;
17165
17414
  if (options.global) {
17166
17415
  const appDataDir = ensureAppDataDirectory();
17167
- configPath = join16(appDataDir, "sparkecoder.config.json");
17416
+ configPath = join17(appDataDir, "sparkecoder.config.json");
17168
17417
  configLocation = "global";
17169
17418
  } else {
17170
17419
  configPath = resolve12(process.cwd(), "sparkecoder.config.json");
17171
17420
  configLocation = "local";
17172
17421
  }
17173
- if (existsSync21(configPath) && !options.force) {
17422
+ if (existsSync22(configPath) && !options.force) {
17174
17423
  console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
17175
17424
  console.log(chalk.dim(` ${configPath}`));
17176
17425
  return;
17177
17426
  }
17178
17427
  const config = createDefaultConfig();
17179
- writeFileSync6(configPath, JSON.stringify(config, null, 2));
17428
+ writeFileSync7(configPath, JSON.stringify(config, null, 2));
17180
17429
  console.log(chalk.green(`\u2713 Created ${configLocation} config`));
17181
17430
  console.log(chalk.dim(` ${configPath}`));
17182
17431
  console.log(chalk.dim("Set AI_GATEWAY_API_KEY and run sparkecoder to start"));
@@ -17195,11 +17444,11 @@ program.command("slack-setup").description("Interactively configure Slack integr
17195
17444
  console.error(chalk.red("Both bot token and signing secret are required."));
17196
17445
  process.exit(1);
17197
17446
  }
17198
- const configPath = options.global ? join16(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
17447
+ const configPath = options.global ? join17(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
17199
17448
  let existing = {};
17200
- if (existsSync21(configPath)) {
17449
+ if (existsSync22(configPath)) {
17201
17450
  try {
17202
- existing = JSON.parse(readFileSync10(configPath, "utf-8"));
17451
+ existing = JSON.parse(readFileSync11(configPath, "utf-8"));
17203
17452
  } catch {
17204
17453
  }
17205
17454
  } else {
@@ -17211,7 +17460,7 @@ program.command("slack-setup").description("Interactively configure Slack integr
17211
17460
  signingSecret,
17212
17461
  defaultOrchestratorName: existing.slack?.defaultOrchestratorName ?? "orchestrator"
17213
17462
  };
17214
- writeFileSync6(configPath, JSON.stringify(existing, null, 2));
17463
+ writeFileSync7(configPath, JSON.stringify(existing, null, 2));
17215
17464
  console.log(chalk.green(`
17216
17465
  \u2713 Slack configured`));
17217
17466
  console.log(chalk.dim(` ${configPath}`));
@@ -17463,9 +17712,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17463
17712
  }
17464
17713
  const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
17465
17714
  console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
17466
- const cfDir = join16(homedir2(), ".cloudflared");
17467
- const certPath = join16(cfDir, "cert.pem");
17468
- if (!existsSync21(certPath)) {
17715
+ const cfDir = join17(homedir2(), ".cloudflared");
17716
+ const certPath = join17(cfDir, "cert.pem");
17717
+ if (!existsSync22(certPath)) {
17469
17718
  console.log(chalk.yellow("No Cloudflare login cert found."));
17470
17719
  if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
17471
17720
  run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
@@ -17509,8 +17758,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17509
17758
  return;
17510
17759
  }
17511
17760
  }
17512
- const credsFile = tunnel.credentials_file || join16(cfDir, `${tunnel.id}.json`);
17513
- if (!existsSync21(credsFile)) {
17761
+ const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
17762
+ if (!existsSync22(credsFile)) {
17514
17763
  console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
17515
17764
  }
17516
17765
  let hostname = options.hostname;
@@ -17533,7 +17782,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17533
17782
  console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
17534
17783
  }
17535
17784
  }
17536
- const configPath = join16(cfDir, "config.yml");
17785
+ const configPath = join17(cfDir, "config.yml");
17537
17786
  const configBody = `tunnel: ${tunnel.id}
17538
17787
  credentials-file: ${credsFile}
17539
17788
  ingress:
@@ -17544,14 +17793,14 @@ ingress:
17544
17793
  - service: http_status:404
17545
17794
  `;
17546
17795
  let wroteConfig = false;
17547
- if (existsSync21(configPath)) {
17548
- const existing = readFileSync10(configPath, "utf8");
17796
+ if (existsSync22(configPath)) {
17797
+ const existing = readFileSync11(configPath, "utf8");
17549
17798
  if (existing.trim() === configBody.trim()) {
17550
17799
  console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
17551
17800
  wroteConfig = true;
17552
17801
  } else if (await confirm(`A different ${configPath} exists. Overwrite (a backup will be saved)?`, false)) {
17553
17802
  copyFileSync(configPath, `${configPath}.bak.${Date.now()}`);
17554
- writeFileSync6(configPath, configBody);
17803
+ writeFileSync7(configPath, configBody);
17555
17804
  console.log(chalk.green("\u2713"), `wrote ${configPath} (previous saved as .bak.*)`);
17556
17805
  wroteConfig = true;
17557
17806
  } else {
@@ -17559,7 +17808,7 @@ ingress:
17559
17808
  console.log(chalk.cyan(configBody));
17560
17809
  }
17561
17810
  } else {
17562
- writeFileSync6(configPath, configBody);
17811
+ writeFileSync7(configPath, configBody);
17563
17812
  console.log(chalk.green("\u2713"), `wrote ${configPath}`);
17564
17813
  wroteConfig = true;
17565
17814
  }
@@ -18028,17 +18277,17 @@ program.command("request-permissions").description("Request macOS permissions fo
18028
18277
  });
18029
18278
  {
18030
18279
  let stateFilePath = function() {
18031
- return join16(ensureAppDataDirectory(), "recordings.json");
18280
+ return join17(ensureAppDataDirectory(), "recordings.json");
18032
18281
  }, readState = function() {
18033
18282
  const p = stateFilePath();
18034
- if (!existsSync21(p)) return [];
18283
+ if (!existsSync22(p)) return [];
18035
18284
  try {
18036
- return JSON.parse(readFileSync10(p, "utf-8"));
18285
+ return JSON.parse(readFileSync11(p, "utf-8"));
18037
18286
  } catch {
18038
18287
  return [];
18039
18288
  }
18040
18289
  }, writeState = function(rows) {
18041
- writeFileSync6(stateFilePath(), JSON.stringify(rows, null, 2), { mode: 384 });
18290
+ writeFileSync7(stateFilePath(), JSON.stringify(rows, null, 2), { mode: 384 });
18042
18291
  }, isAlive = function(pid) {
18043
18292
  try {
18044
18293
  process.kill(pid, 0);
@@ -18052,18 +18301,17 @@ program.command("request-permissions").description("Request macOS permissions fo
18052
18301
  stateFilePath2 = stateFilePath, readState2 = readState, writeState2 = writeState, isAlive2 = isAlive, pruneDead2 = pruneDead;
18053
18302
  const record = program.command("record").description("Start/stop screen recordings");
18054
18303
  record.command("start").description("Start a screen recording (returns id, path, pid as JSON)").option("--name <slug>", "Optional human-readable label for the recording").option("--dir <path>", "Output directory (default: ~/recordings)").action(async (opts) => {
18055
- const { spawn: spawn3 } = await import("child_process");
18056
18304
  const { homedir: homedir2, platform: osPlatform } = await import("os");
18057
- const outDir = opts.dir ? resolve12(opts.dir.replace(/^~/, homedir2())) : join16(homedir2(), "recordings");
18058
- mkdirSync10(outDir, { recursive: true });
18305
+ const outDir = opts.dir ? resolve12(opts.dir.replace(/^~/, homedir2())) : join17(homedir2(), "recordings");
18306
+ mkdirSync11(outDir, { recursive: true });
18059
18307
  const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
18060
18308
  const ext = osPlatform() === "darwin" ? "mov" : "mp4";
18061
18309
  const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
18062
- const path = join16(outDir, filename);
18310
+ const path = join17(outDir, filename);
18063
18311
  let cmd;
18064
18312
  let args;
18065
18313
  if (osPlatform() === "darwin") {
18066
- cmd = "screencapture";
18314
+ cmd = existsSync22("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
18067
18315
  args = ["-v", "-C", "-k", path];
18068
18316
  } else if (osPlatform() === "linux") {
18069
18317
  const display = process.env.DISPLAY || ":0.0";
@@ -18091,12 +18339,43 @@ program.command("request-permissions").description("Request macOS permissions fo
18091
18339
  console.error(JSON.stringify({ error: `Unsupported platform: ${osPlatform()}` }));
18092
18340
  process.exit(1);
18093
18341
  }
18094
- const child = spawn3(cmd, args, { detached: true, stdio: "ignore" });
18095
- child.unref();
18096
- if (!child.pid) {
18097
- console.error(JSON.stringify({ error: `Failed to spawn ${cmd}` }));
18342
+ const { spawn: spawnRec } = await import("child_process");
18343
+ const child = spawnRec(cmd, args, { detached: true, stdio: ["ignore", "ignore", "pipe"] });
18344
+ let stderrTail = "";
18345
+ child.stderr?.on("data", (b) => {
18346
+ stderrTail = (stderrTail + b.toString()).slice(-2e3);
18347
+ });
18348
+ let spawnError = null;
18349
+ child.on("error", (err) => {
18350
+ spawnError = err?.message ?? String(err);
18351
+ });
18352
+ let exited = false;
18353
+ let exitCode = null;
18354
+ child.on("exit", (code) => {
18355
+ exited = true;
18356
+ exitCode = code;
18357
+ });
18358
+ await new Promise((r) => setTimeout(r, 600));
18359
+ if (spawnError || exited || !child.pid) {
18360
+ const diagnosis = [
18361
+ spawnError && `spawn error: ${spawnError}`,
18362
+ exited && `recorder exited code=${exitCode} within 600ms`,
18363
+ stderrTail.trim() && `stderr: ${stderrTail.trim()}`,
18364
+ osPlatform() === "darwin" && `tried: ${cmd} ${args.join(" ")}`,
18365
+ osPlatform() === "darwin" && `if missing Screen Recording permission, run: sparkecoder request-permissions`,
18366
+ osPlatform() === "linux" && `display=${process.env.DISPLAY || ":0.0"} \u2014 ensure X server is running and ffmpeg is installed`
18367
+ ].filter(Boolean).join(". ");
18368
+ console.error(JSON.stringify({
18369
+ error: `Failed to start ${cmd}`,
18370
+ detail: diagnosis || "(no diagnostic output)",
18371
+ cmd,
18372
+ args,
18373
+ pid: child.pid,
18374
+ exitCode
18375
+ }));
18098
18376
  process.exit(1);
18099
18377
  }
18378
+ child.unref();
18100
18379
  const row = {
18101
18380
  id,
18102
18381
  name: opts.name,
@@ -18135,7 +18414,7 @@ program.command("request-permissions").description("Request macOS permissions fo
18135
18414
  }
18136
18415
  }
18137
18416
  writeState(rows.filter((r) => r.id !== id));
18138
- const fileExists = existsSync21(row.path);
18417
+ const fileExists = existsSync22(row.path);
18139
18418
  const sizeMb = fileExists ? Math.round(statSync3(row.path).size / (1024 * 1024) * 10) / 10 : 0;
18140
18419
  console.log(JSON.stringify({
18141
18420
  id,
@@ -18170,7 +18449,7 @@ program.command("request-permissions").description("Request macOS permissions fo
18170
18449
  }
18171
18450
  }
18172
18451
  }
18173
- stopped.push({ id: r.id, path: r.path, ok: existsSync21(r.path) });
18452
+ stopped.push({ id: r.id, path: r.path, ok: existsSync22(r.path) });
18174
18453
  }
18175
18454
  writeState([]);
18176
18455
  console.log(JSON.stringify({ stopped }));