sparkecoder 0.1.114 → 0.1.116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +42 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +477 -93
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-Bi8Ek02A.d.ts → index-Biy5JTop.d.ts} +104 -104
- package/dist/index.d.ts +5 -5
- package/dist/index.js +293 -64
- package/dist/index.js.map +1 -1
- package/dist/{schema-ecQSnCMz.d.ts → schema-CYSKJZ3m.d.ts} +3 -3
- package/dist/{search-DOzC4ojH.d.ts → search-CVVfuBPZ.d.ts} +4 -4
- package/dist/server/index.js +293 -64
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/computer-use.md +27 -13
- package/dist/skills/default/recording.md +71 -42
- package/dist/tools/index.d.ts +3 -3
- package/package.json +1 -1
- package/src/skills/default/computer-use.md +27 -13
- package/src/skills/default/recording.md +71 -42
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f3e6443f._.js +15 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/44c575e006ddb992.js +13 -0
- package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/44c575e006ddb992.js +13 -0
- package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +1 -0
- package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +255 -3
- package/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
- package/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
- package/web/.next/static/chunks/44c575e006ddb992.js +13 -0
- package/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
- package/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_50c2f239._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__6097da17._.js +0 -15
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_3b9a2423._.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
- package/web/.next/standalone/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/60359bdd369c0c72.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/a189cacf6d83cf0b.js +0 -13
- package/web/.next/standalone/web/.next/static/static/chunks/bef6931fdd8428c8.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c1f73b3fa4353c31.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/fbdcbd65f9a38f4b.js +0 -7
- package/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
- package/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
- package/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
- package/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
- package/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
- /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_ssgManifest.js +0 -0
- /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
- /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_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:
|
|
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 =
|
|
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,48 @@ 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 write a worker goal (and what NOT to put in it)
|
|
8414
|
+
|
|
8415
|
+
You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
8416
|
+
|
|
8417
|
+
**DO** put in the goal:
|
|
8418
|
+
|
|
8419
|
+
- The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
|
|
8420
|
+
- Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill computer-use\`).
|
|
8421
|
+
- Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
|
|
8422
|
+
- Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
|
|
8423
|
+
|
|
8424
|
+
**DO NOT** put in the goal (when you're inventing commands from memory):
|
|
8425
|
+
|
|
8426
|
+
- Literal shell commands (\`screencapture -v -V 45 -C /tmp/...\`, \`osascript -e ...\`, \`cliclick kp:cmd+f\`).
|
|
8427
|
+
- Pre-written step-by-step bash that you're drafting from training-data recall.
|
|
8428
|
+
- The names of tools you can't see in your current toolset.
|
|
8429
|
+
|
|
8430
|
+
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.
|
|
8431
|
+
|
|
8432
|
+
**Exceptions \u2014 concrete steps ARE appropriate ONLY when relayed verbatim:**
|
|
8433
|
+
|
|
8434
|
+
You CAN include literal step-by-step commands when one of these is true:
|
|
8435
|
+
|
|
8436
|
+
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.
|
|
8437
|
+
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.
|
|
8438
|
+
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.
|
|
8439
|
+
|
|
8440
|
+
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\`"*.
|
|
8441
|
+
|
|
8442
|
+
What you do NOT do:
|
|
8443
|
+
|
|
8444
|
+
- **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.
|
|
8445
|
+
- **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.
|
|
8446
|
+
|
|
8447
|
+
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.
|
|
8448
|
+
|
|
8449
|
+
Bad goal (don't do this):
|
|
8450
|
+
> "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'..."
|
|
8451
|
+
|
|
8452
|
+
Good goal (do this):
|
|
8453
|
+
> "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
8454
|
`;
|
|
8413
8455
|
}
|
|
8414
8456
|
function createSummaryPrompt(conversationHistory) {
|
|
@@ -11325,11 +11367,11 @@ ${p.text}` : p.text;
|
|
|
11325
11367
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
11326
11368
|
if (!isRemoteConfigured2()) return [];
|
|
11327
11369
|
const { readFile: readFile12 } = await import("fs/promises");
|
|
11328
|
-
const { join:
|
|
11370
|
+
const { join: join18, basename: basename6 } = await import("path");
|
|
11329
11371
|
const urls = [];
|
|
11330
11372
|
for (const filePath of filePaths) {
|
|
11331
11373
|
try {
|
|
11332
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
11374
|
+
const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
|
|
11333
11375
|
const fileName = basename6(fullPath);
|
|
11334
11376
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
11335
11377
|
const mimeMap = {
|
|
@@ -11533,6 +11575,121 @@ var init_session_lock = __esm({
|
|
|
11533
11575
|
}
|
|
11534
11576
|
});
|
|
11535
11577
|
|
|
11578
|
+
// src/orchestrator/webhook-events.ts
|
|
11579
|
+
import { existsSync as existsSync18, readFileSync as readFileSync9, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
|
|
11580
|
+
import { dirname as dirname6, join as join12 } from "path";
|
|
11581
|
+
import { nanoid as nanoid10 } from "nanoid";
|
|
11582
|
+
function logFilePath() {
|
|
11583
|
+
return join12(getAppDataDirectory(), "webhook-events.jsonl");
|
|
11584
|
+
}
|
|
11585
|
+
function ensureLoaded() {
|
|
11586
|
+
if (cache !== null) return cache;
|
|
11587
|
+
cache = [];
|
|
11588
|
+
try {
|
|
11589
|
+
const p = logFilePath();
|
|
11590
|
+
if (!existsSync18(p)) return cache;
|
|
11591
|
+
const lines = readFileSync9(p, "utf-8").split("\n").filter(Boolean);
|
|
11592
|
+
for (const line of lines) {
|
|
11593
|
+
try {
|
|
11594
|
+
cache.push(JSON.parse(line));
|
|
11595
|
+
} catch {
|
|
11596
|
+
}
|
|
11597
|
+
}
|
|
11598
|
+
if (cache.length > MAX_EVENTS) {
|
|
11599
|
+
cache = cache.slice(-MAX_EVENTS);
|
|
11600
|
+
try {
|
|
11601
|
+
writeFileSync3(p, cache.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
11602
|
+
} catch {
|
|
11603
|
+
}
|
|
11604
|
+
}
|
|
11605
|
+
} catch {
|
|
11606
|
+
}
|
|
11607
|
+
return cache;
|
|
11608
|
+
}
|
|
11609
|
+
function appendEvent(ev) {
|
|
11610
|
+
const list = ensureLoaded();
|
|
11611
|
+
list.push(ev);
|
|
11612
|
+
if (list.length > MAX_EVENTS) list.shift();
|
|
11613
|
+
try {
|
|
11614
|
+
const p = logFilePath();
|
|
11615
|
+
mkdirSync7(dirname6(p), { recursive: true });
|
|
11616
|
+
appendFileSync3(p, JSON.stringify(ev) + "\n");
|
|
11617
|
+
} catch {
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
function recordEvent(ev) {
|
|
11621
|
+
const full = {
|
|
11622
|
+
id: ev.id ?? nanoid10(),
|
|
11623
|
+
ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
11624
|
+
source: ev.source,
|
|
11625
|
+
status: ev.status,
|
|
11626
|
+
subtype: ev.subtype,
|
|
11627
|
+
channel: ev.channel,
|
|
11628
|
+
user: ev.user,
|
|
11629
|
+
textSnippet: ev.textSnippet?.slice(0, 200),
|
|
11630
|
+
dropReason: ev.dropReason,
|
|
11631
|
+
error: ev.error,
|
|
11632
|
+
sessionId: ev.sessionId,
|
|
11633
|
+
durationMs: ev.durationMs,
|
|
11634
|
+
meta: ev.meta
|
|
11635
|
+
};
|
|
11636
|
+
appendEvent(full);
|
|
11637
|
+
return full.id;
|
|
11638
|
+
}
|
|
11639
|
+
function updateEvent(id, patch) {
|
|
11640
|
+
const list = ensureLoaded();
|
|
11641
|
+
const i = list.findIndex((e) => e.id === id);
|
|
11642
|
+
if (i < 0) return;
|
|
11643
|
+
list[i] = { ...list[i], ...patch };
|
|
11644
|
+
try {
|
|
11645
|
+
const p = logFilePath();
|
|
11646
|
+
mkdirSync7(dirname6(p), { recursive: true });
|
|
11647
|
+
writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
11648
|
+
} catch {
|
|
11649
|
+
}
|
|
11650
|
+
}
|
|
11651
|
+
function listEvents(filter = {}) {
|
|
11652
|
+
const list = ensureLoaded();
|
|
11653
|
+
const q = filter.q?.toLowerCase();
|
|
11654
|
+
const sinceTs = filter.since ? Date.parse(filter.since) : -Infinity;
|
|
11655
|
+
const beforeTs = filter.before ? Date.parse(filter.before) : Infinity;
|
|
11656
|
+
const matched = list.filter((e) => {
|
|
11657
|
+
if (filter.source && e.source !== filter.source) return false;
|
|
11658
|
+
if (filter.status && e.status !== filter.status) return false;
|
|
11659
|
+
const t = Date.parse(e.ts);
|
|
11660
|
+
if (t < sinceTs) return false;
|
|
11661
|
+
if (t >= beforeTs) return false;
|
|
11662
|
+
if (q) {
|
|
11663
|
+
const hay = `${e.channel ?? ""} ${e.user ?? ""} ${e.textSnippet ?? ""} ${e.dropReason ?? ""} ${e.error ?? ""} ${e.subtype ?? ""}`.toLowerCase();
|
|
11664
|
+
if (!hay.includes(q)) return false;
|
|
11665
|
+
}
|
|
11666
|
+
return true;
|
|
11667
|
+
});
|
|
11668
|
+
matched.reverse();
|
|
11669
|
+
const offset = Math.max(0, filter.offset ?? 0);
|
|
11670
|
+
const limit = Math.min(500, Math.max(1, filter.limit ?? 50));
|
|
11671
|
+
return {
|
|
11672
|
+
events: matched.slice(offset, offset + limit),
|
|
11673
|
+
total: matched.length
|
|
11674
|
+
};
|
|
11675
|
+
}
|
|
11676
|
+
function clearAllEvents() {
|
|
11677
|
+
cache = [];
|
|
11678
|
+
try {
|
|
11679
|
+
writeFileSync3(logFilePath(), "");
|
|
11680
|
+
} catch {
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
var MAX_EVENTS, cache;
|
|
11684
|
+
var init_webhook_events = __esm({
|
|
11685
|
+
"src/orchestrator/webhook-events.ts"() {
|
|
11686
|
+
"use strict";
|
|
11687
|
+
init_config();
|
|
11688
|
+
MAX_EVENTS = 1e3;
|
|
11689
|
+
cache = null;
|
|
11690
|
+
}
|
|
11691
|
+
});
|
|
11692
|
+
|
|
11536
11693
|
// src/orchestrator/daemon.ts
|
|
11537
11694
|
var daemon_exports = {};
|
|
11538
11695
|
__export(daemon_exports, {
|
|
@@ -11594,6 +11751,17 @@ async function runDaemonTurn(sessionId, events) {
|
|
|
11594
11751
|
});
|
|
11595
11752
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
11596
11753
|
const trimmed = text.trim();
|
|
11754
|
+
recordEvent({
|
|
11755
|
+
source: "daemon",
|
|
11756
|
+
status: error ? "failed" : "completed",
|
|
11757
|
+
channel: events[0]?.ref?.channel ?? void 0,
|
|
11758
|
+
user: events[0]?.ref?.user,
|
|
11759
|
+
textSnippet: trimmed.slice(0, 200),
|
|
11760
|
+
error,
|
|
11761
|
+
sessionId,
|
|
11762
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
11763
|
+
meta: { triggeredBy: events.map((e) => e.content?.slice(0, 80)) }
|
|
11764
|
+
});
|
|
11597
11765
|
broadcast({ sessionId, text: trimmed, triggeredBy: events, startedAt, finishedAt, error });
|
|
11598
11766
|
}
|
|
11599
11767
|
var listeners;
|
|
@@ -11604,6 +11772,7 @@ var init_daemon = __esm({
|
|
|
11604
11772
|
init_session_lock();
|
|
11605
11773
|
init_db();
|
|
11606
11774
|
init_inbox();
|
|
11775
|
+
init_webhook_events();
|
|
11607
11776
|
listeners = /* @__PURE__ */ new Map();
|
|
11608
11777
|
}
|
|
11609
11778
|
});
|
|
@@ -11855,7 +12024,7 @@ import chalk from "chalk";
|
|
|
11855
12024
|
import ora from "ora";
|
|
11856
12025
|
import "dotenv/config";
|
|
11857
12026
|
import { createInterface } from "readline";
|
|
11858
|
-
import { dirname as
|
|
12027
|
+
import { dirname as dirname9 } from "path";
|
|
11859
12028
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11860
12029
|
|
|
11861
12030
|
// src/server/index.ts
|
|
@@ -11864,8 +12033,8 @@ import { Hono as Hono9 } from "hono";
|
|
|
11864
12033
|
import { serve } from "@hono/node-server";
|
|
11865
12034
|
import { cors } from "hono/cors";
|
|
11866
12035
|
import { logger } from "hono/logger";
|
|
11867
|
-
import { existsSync as
|
|
11868
|
-
import { resolve as resolve11, dirname as
|
|
12036
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
12037
|
+
import { resolve as resolve11, dirname as dirname8, join as join16 } from "path";
|
|
11869
12038
|
import { spawn as spawn2 } from "child_process";
|
|
11870
12039
|
import { createServer as createNetServer } from "net";
|
|
11871
12040
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11879,10 +12048,10 @@ init_checkpoints();
|
|
|
11879
12048
|
import { Hono } from "hono";
|
|
11880
12049
|
import { zValidator } from "@hono/zod-validator";
|
|
11881
12050
|
import { z as z17 } from "zod";
|
|
11882
|
-
import { existsSync as
|
|
12051
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
11883
12052
|
import { readdir as readdir6 } from "fs/promises";
|
|
11884
|
-
import { join as
|
|
11885
|
-
import { nanoid as
|
|
12053
|
+
import { join as join13, basename as basename5, extname as extname8, relative as relative9 } from "path";
|
|
12054
|
+
import { nanoid as nanoid11 } from "nanoid";
|
|
11886
12055
|
|
|
11887
12056
|
// src/tasks/agent-status.ts
|
|
11888
12057
|
init_questions();
|
|
@@ -12523,12 +12692,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
12523
12692
|
});
|
|
12524
12693
|
function getAttachmentsDir(sessionId) {
|
|
12525
12694
|
const appDataDir = getAppDataDirectory();
|
|
12526
|
-
return
|
|
12695
|
+
return join13(appDataDir, "attachments", sessionId);
|
|
12527
12696
|
}
|
|
12528
12697
|
function ensureAttachmentsDir(sessionId) {
|
|
12529
12698
|
const dir = getAttachmentsDir(sessionId);
|
|
12530
|
-
if (!
|
|
12531
|
-
|
|
12699
|
+
if (!existsSync19(dir)) {
|
|
12700
|
+
mkdirSync8(dir, { recursive: true });
|
|
12532
12701
|
}
|
|
12533
12702
|
return dir;
|
|
12534
12703
|
}
|
|
@@ -12539,12 +12708,12 @@ sessions2.get("/:id/attachments", async (c) => {
|
|
|
12539
12708
|
return c.json({ error: "Session not found" }, 404);
|
|
12540
12709
|
}
|
|
12541
12710
|
const dir = getAttachmentsDir(sessionId);
|
|
12542
|
-
if (!
|
|
12711
|
+
if (!existsSync19(dir)) {
|
|
12543
12712
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
12544
12713
|
}
|
|
12545
12714
|
const files = readdirSync3(dir);
|
|
12546
12715
|
const attachments = files.map((filename) => {
|
|
12547
|
-
const filePath =
|
|
12716
|
+
const filePath = join13(dir, filename);
|
|
12548
12717
|
const stats = statSync2(filePath);
|
|
12549
12718
|
return {
|
|
12550
12719
|
id: filename.split("_")[0],
|
|
@@ -12576,12 +12745,12 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
12576
12745
|
return c.json({ error: "No file provided" }, 400);
|
|
12577
12746
|
}
|
|
12578
12747
|
const dir = ensureAttachmentsDir(sessionId);
|
|
12579
|
-
const id =
|
|
12748
|
+
const id = nanoid11(10);
|
|
12580
12749
|
const ext = extname8(file.name) || "";
|
|
12581
12750
|
const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
12582
|
-
const filePath =
|
|
12751
|
+
const filePath = join13(dir, safeFilename);
|
|
12583
12752
|
const arrayBuffer = await file.arrayBuffer();
|
|
12584
|
-
|
|
12753
|
+
writeFileSync4(filePath, Buffer.from(arrayBuffer));
|
|
12585
12754
|
return c.json({
|
|
12586
12755
|
id,
|
|
12587
12756
|
filename: file.name,
|
|
@@ -12602,16 +12771,16 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
12602
12771
|
return c.json({ error: "Missing filename or data" }, 400);
|
|
12603
12772
|
}
|
|
12604
12773
|
const dir = ensureAttachmentsDir(sessionId);
|
|
12605
|
-
const id =
|
|
12774
|
+
const id = nanoid11(10);
|
|
12606
12775
|
const ext = extname8(body.filename) || "";
|
|
12607
12776
|
const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
12608
|
-
const filePath =
|
|
12777
|
+
const filePath = join13(dir, safeFilename);
|
|
12609
12778
|
let base64Data = body.data;
|
|
12610
12779
|
if (base64Data.includes(",")) {
|
|
12611
12780
|
base64Data = base64Data.split(",")[1];
|
|
12612
12781
|
}
|
|
12613
12782
|
const buffer = Buffer.from(base64Data, "base64");
|
|
12614
|
-
|
|
12783
|
+
writeFileSync4(filePath, buffer);
|
|
12615
12784
|
return c.json({
|
|
12616
12785
|
id,
|
|
12617
12786
|
filename: body.filename,
|
|
@@ -12634,7 +12803,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
12634
12803
|
return c.json({ error: "Session not found" }, 404);
|
|
12635
12804
|
}
|
|
12636
12805
|
const dir = getAttachmentsDir(sessionId);
|
|
12637
|
-
if (!
|
|
12806
|
+
if (!existsSync19(dir)) {
|
|
12638
12807
|
return c.json({ error: "Attachment not found" }, 404);
|
|
12639
12808
|
}
|
|
12640
12809
|
const files = readdirSync3(dir);
|
|
@@ -12642,7 +12811,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
12642
12811
|
if (!file) {
|
|
12643
12812
|
return c.json({ error: "Attachment not found" }, 404);
|
|
12644
12813
|
}
|
|
12645
|
-
const filePath =
|
|
12814
|
+
const filePath = join13(dir, file);
|
|
12646
12815
|
unlinkSync3(filePath);
|
|
12647
12816
|
return c.json({ success: true, id: attachmentId });
|
|
12648
12817
|
});
|
|
@@ -12725,7 +12894,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
12725
12894
|
const entries = await readdir6(currentDir, { withFileTypes: true });
|
|
12726
12895
|
for (const entry2 of entries) {
|
|
12727
12896
|
if (results.length >= limit * 2) break;
|
|
12728
|
-
const fullPath =
|
|
12897
|
+
const fullPath = join13(currentDir, entry2.name);
|
|
12729
12898
|
const relativePath = relative9(baseDir, fullPath);
|
|
12730
12899
|
if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
|
|
12731
12900
|
continue;
|
|
@@ -12773,7 +12942,7 @@ sessions2.get(
|
|
|
12773
12942
|
return c.json({ error: "Session not found" }, 404);
|
|
12774
12943
|
}
|
|
12775
12944
|
const workingDirectory = session.workingDirectory;
|
|
12776
|
-
if (!
|
|
12945
|
+
if (!existsSync19(workingDirectory)) {
|
|
12777
12946
|
return c.json({
|
|
12778
12947
|
sessionId,
|
|
12779
12948
|
workingDirectory,
|
|
@@ -12887,8 +13056,8 @@ init_config();
|
|
|
12887
13056
|
import { Hono as Hono2 } from "hono";
|
|
12888
13057
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
12889
13058
|
import { z as z18 } from "zod";
|
|
12890
|
-
import { existsSync as
|
|
12891
|
-
import { join as
|
|
13059
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
13060
|
+
import { join as join14 } from "path";
|
|
12892
13061
|
|
|
12893
13062
|
// src/server/resumable-stream.ts
|
|
12894
13063
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -12975,7 +13144,7 @@ var streamContext = createResumableStreamContext({
|
|
|
12975
13144
|
|
|
12976
13145
|
// src/server/routes/agents.ts
|
|
12977
13146
|
init_checkpoints();
|
|
12978
|
-
import { nanoid as
|
|
13147
|
+
import { nanoid as nanoid12 } from "nanoid";
|
|
12979
13148
|
init_stream_proxy();
|
|
12980
13149
|
init_recorder();
|
|
12981
13150
|
init_remote();
|
|
@@ -13095,12 +13264,12 @@ var rejectSchema = z18.object({
|
|
|
13095
13264
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
13096
13265
|
function getAttachmentsDirectory(sessionId) {
|
|
13097
13266
|
const appDataDir = getAppDataDirectory();
|
|
13098
|
-
return
|
|
13267
|
+
return join14(appDataDir, "attachments", sessionId);
|
|
13099
13268
|
}
|
|
13100
13269
|
async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
13101
13270
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
13102
|
-
if (!
|
|
13103
|
-
|
|
13271
|
+
if (!existsSync20(attachmentsDir)) {
|
|
13272
|
+
mkdirSync9(attachmentsDir, { recursive: true });
|
|
13104
13273
|
}
|
|
13105
13274
|
let filename = attachment.filename;
|
|
13106
13275
|
if (!filename) {
|
|
@@ -13118,8 +13287,8 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
|
13118
13287
|
attachment.mediaType = resized.mediaType;
|
|
13119
13288
|
attachment.data = buffer.toString("base64");
|
|
13120
13289
|
}
|
|
13121
|
-
const filePath =
|
|
13122
|
-
|
|
13290
|
+
const filePath = join14(attachmentsDir, filename);
|
|
13291
|
+
writeFileSync5(filePath, buffer);
|
|
13123
13292
|
return filePath;
|
|
13124
13293
|
}
|
|
13125
13294
|
function stripDataUrlPrefix2(data) {
|
|
@@ -13555,7 +13724,7 @@ ${prompt}` });
|
|
|
13555
13724
|
});
|
|
13556
13725
|
} catch {
|
|
13557
13726
|
}
|
|
13558
|
-
const streamId = `stream_${id}_${
|
|
13727
|
+
const streamId = `stream_${id}_${nanoid12(10)}`;
|
|
13559
13728
|
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
13560
13729
|
await activeStreamQueries.create(id, streamId);
|
|
13561
13730
|
const stream = await streamContext.resumableStream(
|
|
@@ -13760,7 +13929,7 @@ agents.post(
|
|
|
13760
13929
|
});
|
|
13761
13930
|
const session = agent.getSession();
|
|
13762
13931
|
const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
|
|
13763
|
-
const streamId = `stream_${session.id}_${
|
|
13932
|
+
const streamId = `stream_${session.id}_${nanoid12(10)}`;
|
|
13764
13933
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
13765
13934
|
await activeStreamQueries.create(session.id, streamId);
|
|
13766
13935
|
const createQuickStreamProducer = () => {
|
|
@@ -14079,26 +14248,26 @@ init_config();
|
|
|
14079
14248
|
import { Hono as Hono3 } from "hono";
|
|
14080
14249
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
14081
14250
|
import { z as z19 } from "zod";
|
|
14082
|
-
import { readFileSync as
|
|
14251
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
14083
14252
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14084
|
-
import { dirname as
|
|
14253
|
+
import { dirname as dirname7, join as join15 } from "path";
|
|
14085
14254
|
var __filename = fileURLToPath3(import.meta.url);
|
|
14086
|
-
var __dirname =
|
|
14255
|
+
var __dirname = dirname7(__filename);
|
|
14087
14256
|
var possiblePaths = [
|
|
14088
|
-
|
|
14257
|
+
join15(__dirname, "../package.json"),
|
|
14089
14258
|
// From dist/server -> dist/../package.json
|
|
14090
|
-
|
|
14259
|
+
join15(__dirname, "../../package.json"),
|
|
14091
14260
|
// From dist/server (if nested differently)
|
|
14092
|
-
|
|
14261
|
+
join15(__dirname, "../../../package.json"),
|
|
14093
14262
|
// From src/server/routes (development)
|
|
14094
|
-
|
|
14263
|
+
join15(process.cwd(), "package.json")
|
|
14095
14264
|
// From current working directory
|
|
14096
14265
|
];
|
|
14097
14266
|
var currentVersion = "0.0.0";
|
|
14098
14267
|
var packageName = "sparkecoder";
|
|
14099
14268
|
for (const packageJsonPath of possiblePaths) {
|
|
14100
14269
|
try {
|
|
14101
|
-
const packageJson = JSON.parse(
|
|
14270
|
+
const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
|
|
14102
14271
|
if (packageJson.name === "sparkecoder") {
|
|
14103
14272
|
currentVersion = packageJson.version || "0.0.0";
|
|
14104
14273
|
packageName = packageJson.name || "sparkecoder";
|
|
@@ -14548,7 +14717,7 @@ init_config();
|
|
|
14548
14717
|
import { Hono as Hono5 } from "hono";
|
|
14549
14718
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
14550
14719
|
import { z as z21 } from "zod";
|
|
14551
|
-
import { nanoid as
|
|
14720
|
+
import { nanoid as nanoid13 } from "nanoid";
|
|
14552
14721
|
init_questions();
|
|
14553
14722
|
var tasks = new Hono5();
|
|
14554
14723
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
@@ -14629,7 +14798,7 @@ tasks.post(
|
|
|
14629
14798
|
const taskId = agent.sessionId;
|
|
14630
14799
|
const abortController = new AbortController();
|
|
14631
14800
|
taskAbortControllers.set(taskId, abortController);
|
|
14632
|
-
const streamId = `stream_${taskId}_${
|
|
14801
|
+
const streamId = `stream_${taskId}_${nanoid13(10)}`;
|
|
14633
14802
|
await activeStreamQueries.create(taskId, streamId);
|
|
14634
14803
|
const taskStreamProducer = () => {
|
|
14635
14804
|
const { readable, writable } = new TransformStream();
|
|
@@ -14855,6 +15024,7 @@ function verifySlackSignature(opts) {
|
|
|
14855
15024
|
// src/server/routes/slack.ts
|
|
14856
15025
|
init_client3();
|
|
14857
15026
|
init_slack();
|
|
15027
|
+
init_webhook_events();
|
|
14858
15028
|
init_inbox();
|
|
14859
15029
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
14860
15030
|
var MAX_RECENT = 1e3;
|
|
@@ -14895,7 +15065,17 @@ slack.post("/events", async (c) => {
|
|
|
14895
15065
|
}
|
|
14896
15066
|
if (payload?.type === "event_callback" && payload?.event) {
|
|
14897
15067
|
const ev = payload.event;
|
|
15068
|
+
const auditId = recordEvent({
|
|
15069
|
+
source: "slack",
|
|
15070
|
+
status: "received",
|
|
15071
|
+
subtype: ev.type === "message" ? `message.${ev.channel_type ?? "channels"}` : ev.type,
|
|
15072
|
+
channel: ev.channel,
|
|
15073
|
+
user: ev.user,
|
|
15074
|
+
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
15075
|
+
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
15076
|
+
});
|
|
14898
15077
|
if (alreadyHandled(ev.channel, ev.ts)) {
|
|
15078
|
+
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
14899
15079
|
return c.json({ ok: true });
|
|
14900
15080
|
}
|
|
14901
15081
|
const { event: inbound, dropReason } = slackEventToInboundResult(ev);
|
|
@@ -14905,6 +15085,7 @@ slack.post("/events", async (c) => {
|
|
|
14905
15085
|
const ours = isThreadOwned(ev.channel, ev.thread_ts) || await threadBelongsToUs(ev.channel, ev.thread_ts);
|
|
14906
15086
|
if (!ours) {
|
|
14907
15087
|
console.log(`[slack] dropping thread reply in unknown thread: channel=${ev.channel} thread=${ev.thread_ts}`);
|
|
15088
|
+
updateEvent(auditId, { status: "dropped", dropReason: "thread_not_owned" });
|
|
14908
15089
|
return c.json({ ok: true });
|
|
14909
15090
|
}
|
|
14910
15091
|
}
|
|
@@ -14917,8 +15098,12 @@ slack.post("/events", async (c) => {
|
|
|
14917
15098
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
14918
15099
|
if (orchestratorId) {
|
|
14919
15100
|
pushToInbox(orchestratorId, inbound);
|
|
15101
|
+
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
15102
|
+
} else {
|
|
15103
|
+
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
14920
15104
|
}
|
|
14921
15105
|
} else if (dropReason) {
|
|
15106
|
+
updateEvent(auditId, { status: "dropped", dropReason });
|
|
14922
15107
|
const userFacingDrops = ["user_not_allowed", "channel_not_allowed", "dm_blocked"];
|
|
14923
15108
|
if (userFacingDrops.includes(dropReason)) {
|
|
14924
15109
|
console.log(`[slack] dropped event from user=${payload.event.user} channel=${payload.event.channel}: ${dropReason}`);
|
|
@@ -14981,13 +15166,30 @@ async function sendDeniedReply(event) {
|
|
|
14981
15166
|
init_webhooks_store();
|
|
14982
15167
|
init_webhook();
|
|
14983
15168
|
init_inbox();
|
|
15169
|
+
init_webhook_events();
|
|
14984
15170
|
import { Hono as Hono7 } from "hono";
|
|
14985
15171
|
var inbox = new Hono7();
|
|
14986
15172
|
inbox.post("/:token", async (c) => {
|
|
14987
15173
|
const token = c.req.param("token");
|
|
14988
|
-
if (!token || token.length < 16)
|
|
15174
|
+
if (!token || token.length < 16) {
|
|
15175
|
+
recordEvent({
|
|
15176
|
+
source: "inbox",
|
|
15177
|
+
status: "dropped",
|
|
15178
|
+
dropReason: "invalid_token_format",
|
|
15179
|
+
meta: { tokenLen: token?.length ?? 0 }
|
|
15180
|
+
});
|
|
15181
|
+
return c.json({ error: "invalid token" }, 401);
|
|
15182
|
+
}
|
|
14989
15183
|
const lookup = await findByToken(token);
|
|
14990
|
-
if (!lookup)
|
|
15184
|
+
if (!lookup) {
|
|
15185
|
+
recordEvent({
|
|
15186
|
+
source: "inbox",
|
|
15187
|
+
status: "dropped",
|
|
15188
|
+
dropReason: "unknown_token",
|
|
15189
|
+
meta: { tokenPrefix: token.slice(0, 8) + "\u2026" }
|
|
15190
|
+
});
|
|
15191
|
+
return c.json({ error: "unknown token" }, 404);
|
|
15192
|
+
}
|
|
14991
15193
|
let body;
|
|
14992
15194
|
const contentType = c.req.header("content-type") || "";
|
|
14993
15195
|
try {
|
|
@@ -15009,6 +15211,14 @@ inbox.post("/:token", async (c) => {
|
|
|
15009
15211
|
pushToInbox(lookup.orchestratorSessionId, event);
|
|
15010
15212
|
void recordHit(lookup.orchestratorSessionId, lookup.webhook.id).catch(() => {
|
|
15011
15213
|
});
|
|
15214
|
+
recordEvent({
|
|
15215
|
+
source: "inbox",
|
|
15216
|
+
status: "routed",
|
|
15217
|
+
channel: lookup.webhook.name,
|
|
15218
|
+
textSnippet: typeof body === "string" ? body : JSON.stringify(body).slice(0, 200),
|
|
15219
|
+
sessionId: lookup.orchestratorSessionId,
|
|
15220
|
+
meta: { wake: lookup.webhook.wake, webhookId: lookup.webhook.id }
|
|
15221
|
+
});
|
|
15012
15222
|
return c.json({ ok: true, queued: true, wake: lookup.webhook.wake });
|
|
15013
15223
|
});
|
|
15014
15224
|
|
|
@@ -15020,6 +15230,7 @@ init_webhooks_store();
|
|
|
15020
15230
|
init_messenger();
|
|
15021
15231
|
init_store();
|
|
15022
15232
|
init_pool();
|
|
15233
|
+
init_webhook_events();
|
|
15023
15234
|
import { Hono as Hono8 } from "hono";
|
|
15024
15235
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
15025
15236
|
import { z as z22 } from "zod";
|
|
@@ -15078,6 +15289,24 @@ function webhookPrefix() {
|
|
|
15078
15289
|
const token = cfg?.webhooks?.token;
|
|
15079
15290
|
return token ? `/w/${token}` : "/api";
|
|
15080
15291
|
}
|
|
15292
|
+
integrations.get("/events", async (c) => {
|
|
15293
|
+
const q = c.req.query();
|
|
15294
|
+
const filter = {
|
|
15295
|
+
source: q.source,
|
|
15296
|
+
status: q.status,
|
|
15297
|
+
q: q.q,
|
|
15298
|
+
since: q.since,
|
|
15299
|
+
before: q.before,
|
|
15300
|
+
limit: q.limit ? Math.min(500, Math.max(1, parseInt(q.limit, 10))) : 50,
|
|
15301
|
+
offset: q.offset ? Math.max(0, parseInt(q.offset, 10)) : 0
|
|
15302
|
+
};
|
|
15303
|
+
const { events, total } = listEvents(filter);
|
|
15304
|
+
return c.json({ events, total, limit: filter.limit, offset: filter.offset });
|
|
15305
|
+
});
|
|
15306
|
+
integrations.delete("/events", async (c) => {
|
|
15307
|
+
clearAllEvents();
|
|
15308
|
+
return c.json({ ok: true });
|
|
15309
|
+
});
|
|
15081
15310
|
integrations.get("/", async (c) => {
|
|
15082
15311
|
const cfg = getConfig();
|
|
15083
15312
|
return c.json({
|
|
@@ -15536,13 +15765,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
15536
15765
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
15537
15766
|
function getWebDirectory() {
|
|
15538
15767
|
try {
|
|
15539
|
-
const currentDir =
|
|
15768
|
+
const currentDir = dirname8(fileURLToPath4(import.meta.url));
|
|
15540
15769
|
const webDir = resolve11(currentDir, "..", "web");
|
|
15541
|
-
if (
|
|
15770
|
+
if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
|
|
15542
15771
|
return webDir;
|
|
15543
15772
|
}
|
|
15544
15773
|
const altWebDir = resolve11(currentDir, "..", "..", "web");
|
|
15545
|
-
if (
|
|
15774
|
+
if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
|
|
15546
15775
|
return altWebDir;
|
|
15547
15776
|
}
|
|
15548
15777
|
return null;
|
|
@@ -15600,23 +15829,23 @@ async function findWebPort(preferredPort) {
|
|
|
15600
15829
|
return { port: preferredPort, alreadyRunning: false };
|
|
15601
15830
|
}
|
|
15602
15831
|
function hasProductionBuild(webDir) {
|
|
15603
|
-
const buildIdPath =
|
|
15604
|
-
return
|
|
15832
|
+
const buildIdPath = join16(webDir, ".next", "BUILD_ID");
|
|
15833
|
+
return existsSync21(buildIdPath);
|
|
15605
15834
|
}
|
|
15606
15835
|
function hasSourceFiles(webDir) {
|
|
15607
|
-
const appDir =
|
|
15608
|
-
const pagesDir =
|
|
15609
|
-
const rootAppDir =
|
|
15610
|
-
const rootPagesDir =
|
|
15611
|
-
return
|
|
15836
|
+
const appDir = join16(webDir, "src", "app");
|
|
15837
|
+
const pagesDir = join16(webDir, "src", "pages");
|
|
15838
|
+
const rootAppDir = join16(webDir, "app");
|
|
15839
|
+
const rootPagesDir = join16(webDir, "pages");
|
|
15840
|
+
return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
|
|
15612
15841
|
}
|
|
15613
15842
|
function getStandaloneServerPath(webDir) {
|
|
15614
15843
|
const possiblePaths2 = [
|
|
15615
|
-
|
|
15616
|
-
|
|
15844
|
+
join16(webDir, ".next", "standalone", "server.js"),
|
|
15845
|
+
join16(webDir, ".next", "standalone", "web", "server.js")
|
|
15617
15846
|
];
|
|
15618
15847
|
for (const serverPath of possiblePaths2) {
|
|
15619
|
-
if (
|
|
15848
|
+
if (existsSync21(serverPath)) {
|
|
15620
15849
|
return serverPath;
|
|
15621
15850
|
}
|
|
15622
15851
|
}
|
|
@@ -15656,15 +15885,15 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15656
15885
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
15657
15886
|
return { process: null, port: actualPort };
|
|
15658
15887
|
}
|
|
15659
|
-
const usePnpm =
|
|
15660
|
-
const useNpm = !usePnpm &&
|
|
15888
|
+
const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
|
|
15889
|
+
const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
|
|
15661
15890
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
15662
15891
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
15663
15892
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
15664
15893
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
15665
|
-
const runtimeConfigPath =
|
|
15894
|
+
const runtimeConfigPath = join16(webDir, "runtime-config.json");
|
|
15666
15895
|
try {
|
|
15667
|
-
|
|
15896
|
+
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
15668
15897
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
15669
15898
|
} catch (err) {
|
|
15670
15899
|
if (!quiet) console.warn(` \u26A0 Could not write runtime config: ${err}`);
|
|
@@ -15684,7 +15913,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15684
15913
|
if (standaloneServerPath) {
|
|
15685
15914
|
command = "node";
|
|
15686
15915
|
args = ["server.js"];
|
|
15687
|
-
cwd =
|
|
15916
|
+
cwd = dirname8(standaloneServerPath);
|
|
15688
15917
|
webEnv.PORT = String(actualPort);
|
|
15689
15918
|
webEnv.HOSTNAME = "0.0.0.0";
|
|
15690
15919
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
@@ -15877,8 +16106,8 @@ async function startServer(options = {}) {
|
|
|
15877
16106
|
if (options.workingDirectory) {
|
|
15878
16107
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
15879
16108
|
}
|
|
15880
|
-
if (!
|
|
15881
|
-
|
|
16109
|
+
if (!existsSync21(config.resolvedWorkingDirectory)) {
|
|
16110
|
+
mkdirSync10(config.resolvedWorkingDirectory, { recursive: true });
|
|
15882
16111
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
15883
16112
|
}
|
|
15884
16113
|
if (!config.resolvedRemoteServer.url) {
|
|
@@ -16422,18 +16651,18 @@ function generateOpenAPISpec() {
|
|
|
16422
16651
|
init_config();
|
|
16423
16652
|
init_semantic();
|
|
16424
16653
|
init_db();
|
|
16425
|
-
import { writeFileSync as
|
|
16426
|
-
import { resolve as resolve12, join as
|
|
16654
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync7, readFileSync as readFileSync11, existsSync as existsSync22, statSync as statSync3 } from "fs";
|
|
16655
|
+
import { resolve as resolve12, join as join17 } from "path";
|
|
16427
16656
|
function getCliVersion() {
|
|
16428
|
-
const here =
|
|
16657
|
+
const here = dirname9(fileURLToPath5(import.meta.url));
|
|
16429
16658
|
const candidates = [
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16659
|
+
join17(here, "..", "package.json"),
|
|
16660
|
+
join17(here, "..", "..", "package.json"),
|
|
16661
|
+
join17(process.cwd(), "package.json")
|
|
16433
16662
|
];
|
|
16434
16663
|
for (const p of candidates) {
|
|
16435
16664
|
try {
|
|
16436
|
-
const pkg = JSON.parse(
|
|
16665
|
+
const pkg = JSON.parse(readFileSync11(p, "utf8"));
|
|
16437
16666
|
if (pkg.name === "sparkecoder" && pkg.version) return pkg.version;
|
|
16438
16667
|
} catch {
|
|
16439
16668
|
}
|
|
@@ -17096,8 +17325,8 @@ program.command("task").description("Run an autonomous task that completes witho
|
|
|
17096
17325
|
let outputSchema;
|
|
17097
17326
|
try {
|
|
17098
17327
|
const schemaStr = options.schema;
|
|
17099
|
-
if (
|
|
17100
|
-
outputSchema = JSON.parse(
|
|
17328
|
+
if (existsSync22(schemaStr)) {
|
|
17329
|
+
outputSchema = JSON.parse(readFileSync11(schemaStr, "utf-8"));
|
|
17101
17330
|
} else {
|
|
17102
17331
|
outputSchema = JSON.parse(schemaStr);
|
|
17103
17332
|
}
|
|
@@ -17164,19 +17393,19 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
|
|
|
17164
17393
|
let configLocation;
|
|
17165
17394
|
if (options.global) {
|
|
17166
17395
|
const appDataDir = ensureAppDataDirectory();
|
|
17167
|
-
configPath =
|
|
17396
|
+
configPath = join17(appDataDir, "sparkecoder.config.json");
|
|
17168
17397
|
configLocation = "global";
|
|
17169
17398
|
} else {
|
|
17170
17399
|
configPath = resolve12(process.cwd(), "sparkecoder.config.json");
|
|
17171
17400
|
configLocation = "local";
|
|
17172
17401
|
}
|
|
17173
|
-
if (
|
|
17402
|
+
if (existsSync22(configPath) && !options.force) {
|
|
17174
17403
|
console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
|
|
17175
17404
|
console.log(chalk.dim(` ${configPath}`));
|
|
17176
17405
|
return;
|
|
17177
17406
|
}
|
|
17178
17407
|
const config = createDefaultConfig();
|
|
17179
|
-
|
|
17408
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2));
|
|
17180
17409
|
console.log(chalk.green(`\u2713 Created ${configLocation} config`));
|
|
17181
17410
|
console.log(chalk.dim(` ${configPath}`));
|
|
17182
17411
|
console.log(chalk.dim("Set AI_GATEWAY_API_KEY and run sparkecoder to start"));
|
|
@@ -17195,11 +17424,11 @@ program.command("slack-setup").description("Interactively configure Slack integr
|
|
|
17195
17424
|
console.error(chalk.red("Both bot token and signing secret are required."));
|
|
17196
17425
|
process.exit(1);
|
|
17197
17426
|
}
|
|
17198
|
-
const configPath = options.global ?
|
|
17427
|
+
const configPath = options.global ? join17(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
|
|
17199
17428
|
let existing = {};
|
|
17200
|
-
if (
|
|
17429
|
+
if (existsSync22(configPath)) {
|
|
17201
17430
|
try {
|
|
17202
|
-
existing = JSON.parse(
|
|
17431
|
+
existing = JSON.parse(readFileSync11(configPath, "utf-8"));
|
|
17203
17432
|
} catch {
|
|
17204
17433
|
}
|
|
17205
17434
|
} else {
|
|
@@ -17211,7 +17440,7 @@ program.command("slack-setup").description("Interactively configure Slack integr
|
|
|
17211
17440
|
signingSecret,
|
|
17212
17441
|
defaultOrchestratorName: existing.slack?.defaultOrchestratorName ?? "orchestrator"
|
|
17213
17442
|
};
|
|
17214
|
-
|
|
17443
|
+
writeFileSync7(configPath, JSON.stringify(existing, null, 2));
|
|
17215
17444
|
console.log(chalk.green(`
|
|
17216
17445
|
\u2713 Slack configured`));
|
|
17217
17446
|
console.log(chalk.dim(` ${configPath}`));
|
|
@@ -17463,9 +17692,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17463
17692
|
}
|
|
17464
17693
|
const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
|
|
17465
17694
|
console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
|
|
17466
|
-
const cfDir =
|
|
17467
|
-
const certPath =
|
|
17468
|
-
if (!
|
|
17695
|
+
const cfDir = join17(homedir2(), ".cloudflared");
|
|
17696
|
+
const certPath = join17(cfDir, "cert.pem");
|
|
17697
|
+
if (!existsSync22(certPath)) {
|
|
17469
17698
|
console.log(chalk.yellow("No Cloudflare login cert found."));
|
|
17470
17699
|
if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
|
|
17471
17700
|
run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
|
|
@@ -17509,8 +17738,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17509
17738
|
return;
|
|
17510
17739
|
}
|
|
17511
17740
|
}
|
|
17512
|
-
const credsFile = tunnel.credentials_file ||
|
|
17513
|
-
if (!
|
|
17741
|
+
const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
|
|
17742
|
+
if (!existsSync22(credsFile)) {
|
|
17514
17743
|
console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
|
|
17515
17744
|
}
|
|
17516
17745
|
let hostname = options.hostname;
|
|
@@ -17533,7 +17762,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17533
17762
|
console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
|
|
17534
17763
|
}
|
|
17535
17764
|
}
|
|
17536
|
-
const configPath =
|
|
17765
|
+
const configPath = join17(cfDir, "config.yml");
|
|
17537
17766
|
const configBody = `tunnel: ${tunnel.id}
|
|
17538
17767
|
credentials-file: ${credsFile}
|
|
17539
17768
|
ingress:
|
|
@@ -17544,14 +17773,14 @@ ingress:
|
|
|
17544
17773
|
- service: http_status:404
|
|
17545
17774
|
`;
|
|
17546
17775
|
let wroteConfig = false;
|
|
17547
|
-
if (
|
|
17548
|
-
const existing =
|
|
17776
|
+
if (existsSync22(configPath)) {
|
|
17777
|
+
const existing = readFileSync11(configPath, "utf8");
|
|
17549
17778
|
if (existing.trim() === configBody.trim()) {
|
|
17550
17779
|
console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
|
|
17551
17780
|
wroteConfig = true;
|
|
17552
17781
|
} else if (await confirm(`A different ${configPath} exists. Overwrite (a backup will be saved)?`, false)) {
|
|
17553
17782
|
copyFileSync(configPath, `${configPath}.bak.${Date.now()}`);
|
|
17554
|
-
|
|
17783
|
+
writeFileSync7(configPath, configBody);
|
|
17555
17784
|
console.log(chalk.green("\u2713"), `wrote ${configPath} (previous saved as .bak.*)`);
|
|
17556
17785
|
wroteConfig = true;
|
|
17557
17786
|
} else {
|
|
@@ -17559,7 +17788,7 @@ ingress:
|
|
|
17559
17788
|
console.log(chalk.cyan(configBody));
|
|
17560
17789
|
}
|
|
17561
17790
|
} else {
|
|
17562
|
-
|
|
17791
|
+
writeFileSync7(configPath, configBody);
|
|
17563
17792
|
console.log(chalk.green("\u2713"), `wrote ${configPath}`);
|
|
17564
17793
|
wroteConfig = true;
|
|
17565
17794
|
}
|
|
@@ -18026,5 +18255,160 @@ program.command("request-permissions").description("Request macOS permissions fo
|
|
|
18026
18255
|
}
|
|
18027
18256
|
console.log();
|
|
18028
18257
|
});
|
|
18258
|
+
{
|
|
18259
|
+
let stateFilePath = function() {
|
|
18260
|
+
return join17(ensureAppDataDirectory(), "recordings.json");
|
|
18261
|
+
}, readState = function() {
|
|
18262
|
+
const p = stateFilePath();
|
|
18263
|
+
if (!existsSync22(p)) return [];
|
|
18264
|
+
try {
|
|
18265
|
+
return JSON.parse(readFileSync11(p, "utf-8"));
|
|
18266
|
+
} catch {
|
|
18267
|
+
return [];
|
|
18268
|
+
}
|
|
18269
|
+
}, writeState = function(rows) {
|
|
18270
|
+
writeFileSync7(stateFilePath(), JSON.stringify(rows, null, 2), { mode: 384 });
|
|
18271
|
+
}, isAlive = function(pid) {
|
|
18272
|
+
try {
|
|
18273
|
+
process.kill(pid, 0);
|
|
18274
|
+
return true;
|
|
18275
|
+
} catch {
|
|
18276
|
+
return false;
|
|
18277
|
+
}
|
|
18278
|
+
}, pruneDead = function(rows) {
|
|
18279
|
+
return rows.filter((r) => isAlive(r.pid));
|
|
18280
|
+
};
|
|
18281
|
+
stateFilePath2 = stateFilePath, readState2 = readState, writeState2 = writeState, isAlive2 = isAlive, pruneDead2 = pruneDead;
|
|
18282
|
+
const record = program.command("record").description("Start/stop screen recordings");
|
|
18283
|
+
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) => {
|
|
18284
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
18285
|
+
const { homedir: homedir2, platform: osPlatform } = await import("os");
|
|
18286
|
+
const outDir = opts.dir ? resolve12(opts.dir.replace(/^~/, homedir2())) : join17(homedir2(), "recordings");
|
|
18287
|
+
mkdirSync11(outDir, { recursive: true });
|
|
18288
|
+
const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
18289
|
+
const ext = osPlatform() === "darwin" ? "mov" : "mp4";
|
|
18290
|
+
const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
|
|
18291
|
+
const path = join17(outDir, filename);
|
|
18292
|
+
let cmd;
|
|
18293
|
+
let args;
|
|
18294
|
+
if (osPlatform() === "darwin") {
|
|
18295
|
+
cmd = "screencapture";
|
|
18296
|
+
args = ["-v", "-C", "-k", path];
|
|
18297
|
+
} else if (osPlatform() === "linux") {
|
|
18298
|
+
const display = process.env.DISPLAY || ":0.0";
|
|
18299
|
+
const size = process.env.SPARKECODER_RECORD_SIZE || "1920x1080";
|
|
18300
|
+
cmd = "ffmpeg";
|
|
18301
|
+
args = [
|
|
18302
|
+
"-y",
|
|
18303
|
+
"-f",
|
|
18304
|
+
"x11grab",
|
|
18305
|
+
"-framerate",
|
|
18306
|
+
"25",
|
|
18307
|
+
"-video_size",
|
|
18308
|
+
size,
|
|
18309
|
+
"-i",
|
|
18310
|
+
display,
|
|
18311
|
+
"-c:v",
|
|
18312
|
+
"libx264",
|
|
18313
|
+
"-preset",
|
|
18314
|
+
"veryfast",
|
|
18315
|
+
"-pix_fmt",
|
|
18316
|
+
"yuv420p",
|
|
18317
|
+
path
|
|
18318
|
+
];
|
|
18319
|
+
} else {
|
|
18320
|
+
console.error(JSON.stringify({ error: `Unsupported platform: ${osPlatform()}` }));
|
|
18321
|
+
process.exit(1);
|
|
18322
|
+
}
|
|
18323
|
+
const child = spawn3(cmd, args, { detached: true, stdio: "ignore" });
|
|
18324
|
+
child.unref();
|
|
18325
|
+
if (!child.pid) {
|
|
18326
|
+
console.error(JSON.stringify({ error: `Failed to spawn ${cmd}` }));
|
|
18327
|
+
process.exit(1);
|
|
18328
|
+
}
|
|
18329
|
+
const row = {
|
|
18330
|
+
id,
|
|
18331
|
+
name: opts.name,
|
|
18332
|
+
path,
|
|
18333
|
+
pid: child.pid,
|
|
18334
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18335
|
+
platform: osPlatform()
|
|
18336
|
+
};
|
|
18337
|
+
const rows = pruneDead(readState());
|
|
18338
|
+
rows.push(row);
|
|
18339
|
+
writeState(rows);
|
|
18340
|
+
console.log(JSON.stringify({ id, path, pid: child.pid, platform: osPlatform() }));
|
|
18341
|
+
});
|
|
18342
|
+
record.command("stop <id>").description("Stop a recording started by `sparkecoder record start`").action(async (id) => {
|
|
18343
|
+
const rows = readState();
|
|
18344
|
+
const row = rows.find((r) => r.id === id);
|
|
18345
|
+
if (!row) {
|
|
18346
|
+
console.error(JSON.stringify({ error: `No recording found with id ${id}` }));
|
|
18347
|
+
process.exit(1);
|
|
18348
|
+
}
|
|
18349
|
+
const startedAt = new Date(row.startedAt).getTime();
|
|
18350
|
+
if (isAlive(row.pid)) {
|
|
18351
|
+
try {
|
|
18352
|
+
process.kill(row.pid, "SIGINT");
|
|
18353
|
+
} catch {
|
|
18354
|
+
}
|
|
18355
|
+
for (let i = 0; i < 40; i++) {
|
|
18356
|
+
if (!isAlive(row.pid)) break;
|
|
18357
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
18358
|
+
}
|
|
18359
|
+
if (isAlive(row.pid)) {
|
|
18360
|
+
try {
|
|
18361
|
+
process.kill(row.pid, "SIGTERM");
|
|
18362
|
+
} catch {
|
|
18363
|
+
}
|
|
18364
|
+
}
|
|
18365
|
+
}
|
|
18366
|
+
writeState(rows.filter((r) => r.id !== id));
|
|
18367
|
+
const fileExists = existsSync22(row.path);
|
|
18368
|
+
const sizeMb = fileExists ? Math.round(statSync3(row.path).size / (1024 * 1024) * 10) / 10 : 0;
|
|
18369
|
+
console.log(JSON.stringify({
|
|
18370
|
+
id,
|
|
18371
|
+
path: row.path,
|
|
18372
|
+
durationSec: Math.round((Date.now() - startedAt) / 1e3),
|
|
18373
|
+
sizeMb,
|
|
18374
|
+
ok: fileExists
|
|
18375
|
+
}));
|
|
18376
|
+
});
|
|
18377
|
+
record.command("list").description("List active recordings").action(() => {
|
|
18378
|
+
const rows = pruneDead(readState());
|
|
18379
|
+
writeState(rows);
|
|
18380
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
18381
|
+
});
|
|
18382
|
+
record.command("stop-all").description("Stop every active recording").action(async () => {
|
|
18383
|
+
const rows = readState();
|
|
18384
|
+
const stopped = [];
|
|
18385
|
+
for (const r of rows) {
|
|
18386
|
+
if (isAlive(r.pid)) {
|
|
18387
|
+
try {
|
|
18388
|
+
process.kill(r.pid, "SIGINT");
|
|
18389
|
+
} catch {
|
|
18390
|
+
}
|
|
18391
|
+
for (let i = 0; i < 30; i++) {
|
|
18392
|
+
if (!isAlive(r.pid)) break;
|
|
18393
|
+
await new Promise((res) => setTimeout(res, 200));
|
|
18394
|
+
}
|
|
18395
|
+
if (isAlive(r.pid)) {
|
|
18396
|
+
try {
|
|
18397
|
+
process.kill(r.pid, "SIGTERM");
|
|
18398
|
+
} catch {
|
|
18399
|
+
}
|
|
18400
|
+
}
|
|
18401
|
+
}
|
|
18402
|
+
stopped.push({ id: r.id, path: r.path, ok: existsSync22(r.path) });
|
|
18403
|
+
}
|
|
18404
|
+
writeState([]);
|
|
18405
|
+
console.log(JSON.stringify({ stopped }));
|
|
18406
|
+
});
|
|
18407
|
+
}
|
|
18408
|
+
var stateFilePath2;
|
|
18409
|
+
var readState2;
|
|
18410
|
+
var writeState2;
|
|
18411
|
+
var isAlive2;
|
|
18412
|
+
var pruneDead2;
|
|
18029
18413
|
program.parse();
|
|
18030
18414
|
//# sourceMappingURL=cli.js.map
|