sparkecoder 0.1.114 → 0.1.115

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 (107) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/cli.js +156 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/db/index.d.ts +2 -2
  5. package/dist/{index-Bi8Ek02A.d.ts → index-Biy5JTop.d.ts} +104 -104
  6. package/dist/index.d.ts +5 -5
  7. package/dist/{schema-ecQSnCMz.d.ts → schema-CYSKJZ3m.d.ts} +3 -3
  8. package/dist/{search-DOzC4ojH.d.ts → search-CVVfuBPZ.d.ts} +4 -4
  9. package/dist/skills/default/computer-use.md +20 -13
  10. package/dist/skills/default/recording.md +66 -42
  11. package/dist/tools/index.d.ts +3 -3
  12. package/package.json +1 -1
  13. package/src/skills/default/computer-use.md +20 -13
  14. package/src/skills/default/recording.md +66 -42
  15. package/web/.next/BUILD_ID +1 -1
  16. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  17. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  18. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  19. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  20. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  21. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  22. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  23. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  35. package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  79. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  87. package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  94. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  96. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  97. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  98. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  99. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_buildManifest.js +0 -0
  100. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_clientMiddlewareManifest.json +0 -0
  101. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_ssgManifest.js +0 -0
  102. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_buildManifest.js +0 -0
  103. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_clientMiddlewareManifest.json +0 -0
  104. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_ssgManifest.js +0 -0
  105. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_buildManifest.js +0 -0
  106. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_clientMiddlewareManifest.json +0 -0
  107. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → pSGgtxBjN5_o0YHZgQzXm}/_ssgManifest.js +0 -0
@@ -22,6 +22,42 @@ Skip recording when:
22
22
  - The task is purely textual (reading/writing files, code analysis). The chat transcript already has everything.
23
23
  - Recording would capture sensitive content (passwords on screen, private DMs, customer data). Ask first.
24
24
 
25
+ ## Screen recording — use `sparkecoder record` (preferred)
26
+
27
+ The built-in helper handles start/stop reliably so the recording covers the WHOLE task, not a fixed time window. It tracks PIDs in state, works on macOS + Linux with the same interface, and returns JSON you can parse.
28
+
29
+ ```bash
30
+ # Start: returns {id, path, pid}
31
+ REC=$(sparkecoder record start --name "calculator-demo")
32
+ REC_ID=$(echo "$REC" | jq -r .id)
33
+ REC_PATH=$(echo "$REC" | jq -r .path)
34
+
35
+ # ... do work ...
36
+
37
+ # Stop: returns {id, path, durationSec, sizeMb, ok}
38
+ sparkecoder record stop "$REC_ID"
39
+ ```
40
+
41
+ Other operations:
42
+
43
+ ```bash
44
+ sparkecoder record list # list active recordings (auto-prunes dead pids)
45
+ sparkecoder record stop-all # stop everything (cleanup)
46
+ sparkecoder record start --dir /custom/path --name my-task
47
+ ```
48
+
49
+ Storage: defaults to `~/recordings/`. Files are named `rec-<id>[-<name>].<mov|mp4>`.
50
+
51
+ ### Why not `screencapture -V N` or raw `ffmpeg` backgrounded?
52
+
53
+ You can — but you have to:
54
+ - guess the right `-V` timeout (too short = partial, too long = wasted disk),
55
+ - track the PID yourself for `kill -INT`,
56
+ - pick the right tool per OS,
57
+ - and clean up zombie processes if you crash mid-task.
58
+
59
+ `sparkecoder record` does all of that. Reach for raw `screencapture` / `ffmpeg` only if you need specific flags the helper doesn't expose (custom display, rectangle crop, audio mux).
60
+
25
61
  ## Terminal recording — asciinema
26
62
 
27
63
  `asciinema` records the **content of a terminal session** as a tiny `.cast` text file. It's a fraction of the size of video, plays in any browser via [asciinema-player](https://github.com/asciinema/asciinema-player), and the output is also greppable.
@@ -85,49 +121,33 @@ curl -fsS -X POST "${SPARKECODER_REMOTE_URL}/storage/upload" \
85
121
  -F "file=@${RECORDING}" | jq .url
86
122
  ```
87
123
 
88
- ## Screen recording macOS native
124
+ ## Advanced: raw `screencapture` / `ffmpeg` (use only when the helper isn't enough)
89
125
 
90
- macOS ships with `screencapture`, no extra install needed. It writes `.mov` (H.264) which any modern app/browser plays.
126
+ Prefer `sparkecoder record` (above) for almost everything. Reach for raw tools only when you need flags the helper doesn't expose: rectangle crop, specific display, audio mux, custom frame rate.
91
127
 
92
128
  ### Requirements
93
129
 
94
- - **Screen Recording permission** for the agent's parent process (Terminal, iTerm, IDE, or `node`).
95
- - `sparkecoder check-permissions` verifies; `sparkecoder request-permissions` opens the right System Settings pane.
130
+ - macOS: **Screen Recording permission** for the agent's parent process. `sparkecoder check-permissions` verifies; `sparkecoder request-permissions` opens the right System Settings pane.
131
+ - Linux: `ffmpeg` installed (`sudo apt-get install -y ffmpeg`), and a running X server with the right `DISPLAY` env var.
96
132
 
97
- ### Record
133
+ ### macOS `screencapture` flags
98
134
 
99
135
  ```bash
100
- # Record the entire primary display until Ctrl-C (or the agent sends SIGINT to the process):
101
- screencapture -v ~/recordings/screen-$(date +%s).mov &
102
- REC_PID=$!
136
+ # Crop to a rectangle (x,y,w,h):
137
+ screencapture -v -R 100,100,800,600 -C ~/recordings/clip.mov
103
138
 
104
- # ... do work via computer-use, bash, etc. ...
139
+ # Pick a specific display (run `system_profiler SPDisplaysDataType` to list ids):
140
+ screencapture -v -G <displayID> -C ~/recordings/clip.mov
105
141
 
106
- # Stop cleanly:
107
- kill -INT $REC_PID
108
- wait $REC_PID 2>/dev/null
142
+ # Auto-stop after N seconds (avoid this for task-bounded recordings; use the
143
+ # helper instead so the recording covers the whole task):
144
+ screencapture -v -V 30 -C ~/recordings/clip.mov
109
145
  ```
110
146
 
111
- Useful flags:
112
-
113
- - `-V <seconds>` — stop automatically after this many seconds (no need for kill).
114
- - `-R x,y,w,h` — record just a rectangle.
115
- - `-C` — also capture mouse cursor (off by default).
116
- - `-G <displayID>` — pick a specific display (run `system_profiler SPDisplaysDataType` to list).
117
- - `-T <seconds>` — pre-roll countdown before starting.
118
-
119
- Example: 30-second auto-stop, full screen, with cursor:
147
+ ### Linux `ffmpeg` x11grab
120
148
 
121
149
  ```bash
122
- screencapture -v -V 30 -C ~/recordings/full-$(date +%s).mov
123
- ```
124
-
125
- ## Screen recording — Linux (`ffmpeg`)
126
-
127
- ```bash
128
- sudo apt-get install -y ffmpeg
129
-
130
- # Record the X display. Adjust :0.0 / resolution for your setup.
150
+ # Capture the X display (size matches the screen):
131
151
  ffmpeg -y -f x11grab -framerate 25 -video_size 1920x1080 -i :0.0 \
132
152
  -c:v libx264 -preset veryfast -pix_fmt yuv420p \
133
153
  ~/recordings/screen-$(date +%s).mp4
@@ -141,32 +161,36 @@ On a headless server with no X, screen recording isn't meaningful — record the
141
161
 
142
162
  | You want to … | Use |
143
163
  |---|---|
164
+ | Show what a computer-use task did on the desktop | `sparkecoder record start` → work → `record stop <id>` |
144
165
  | Capture a CLI demo, share a link | `asciinema rec → upload` |
145
- | Show what a computer-use task did on the desktop | `screencapture -v -V <seconds>` (macOS) |
146
- | Debug a flaky GUI test on Linux CI | `ffmpeg -f x11grab` around the test run |
147
166
  | Capture a single command's output verbatim | `asciinema rec --command "cmd" out.cast` |
148
- | Both terminal AND screen at once | start `asciinema rec` in a sub-shell AND `screencapture -v` in parallel |
167
+ | Debug a flaky GUI test (any OS) | `sparkecoder record start` around the test run |
168
+ | Both terminal AND screen at once | `sparkecoder record start` + `asciinema rec` in parallel |
149
169
 
150
170
  ## Best practices
151
171
 
152
172
  1. **Always announce.** Before starting a recording, post a short message to the user: "Starting screen recording for the Calculator demo." So they can intervene if it'd capture something sensitive.
153
- 2. **Auto-stop with a timer when you can.** `screencapture -V 60` is much safer than relying on the agent to remember to kill the process. Recordings that go forever fill disks.
154
- 3. **Use stable file names.** `~/recordings/<task-name>-<unix-ts>.<ext>`. The unix timestamp prevents overwrites and makes sorting easy.
155
- 4. **Clean up after yourself.** If the user only wanted to scrub through it once, delete the file when done recordings are often huge.
173
+ 2. **Use `sparkecoder record` for screen video.** It removes every footgun the raw tools have (no fixed timeout that cuts the task off mid-way, no PID juggling, no per-OS branching). Reach for raw `screencapture` / `ffmpeg` only when you need flags the helper doesn't expose (region crop, custom display, audio).
174
+ 3. **Bracket the WHOLE task.** `record start` *before* your first work command; `record stop <id>` *after* the very last one. Don't bracket each step individually — you'll lose context between clips.
175
+ 4. **Stop on exit, success OR failure.** Always run `record stop <id>` at the end of your task, even on errors. Otherwise the recorder keeps writing until `record stop-all` (or a reboot) catches it. The duration / size returned by `stop` is also useful in your final summary.
156
176
  5. **Don't record secrets.** Refuse to record if the screen contains visible passwords, 1Password vaults, customer PII, etc. Ask the user to close those windows first.
157
- 6. **For computer-use tasks: prefer recording the whole session, not per-action.** Starting `screencapture -v -V 120` once around your whole interaction is cheaper and produces a continuous video.
158
- 7. **Mention the file in your final message.** "Recording saved to `~/recordings/calc-1700000000.mov`" otherwise the user won't know where to look.
177
+ 6. **Clean up after yourself.** If a recording is no longer needed, `rm` it videos are big.
178
+ 7. **Always mention the file in your final message.** "Recording saved to `<path>`" or, better, upload via `/storage/upload` and post the `/f/<id>` URL so Slack/etc. unfurls it inline.
159
179
 
160
180
  ## Returning the recording in your final task summary
161
181
 
162
- For a **task worker** that captured a recording, include the path and a one-line description in your `result` so the orchestrator can route it back to the user:
182
+ For a **task worker** that captured a recording, include the structured fields below in your `result` so the orchestrator can route it back to the user:
163
183
 
164
184
  ```ts
165
185
  {
166
186
  "summary": "Opened Calculator, typed 2+2, hit Return. Got 4.",
167
- "screencapture": "~/recordings/calc-1700000000.mov",
168
- "screencapture_description": "60s screen recording of the entire Calculator interaction"
187
+ "recording": {
188
+ "path": "/Users/me/recordings/rec-abc123-calculator-demo.mov",
189
+ "durationSec": 42,
190
+ "sizeMb": 18.4,
191
+ "description": "Full screen recording of the Calculator interaction"
192
+ }
169
193
  }
170
194
  ```
171
195
 
172
- The orchestrator can then attach / post the file via Slack or whatever channel the original request came in on.
196
+ The orchestrator can then upload that path via `/storage/upload` and post the `/f/<id>` URL into Slack (or whatever channel the original request came in on) — Slack unfurls it inline as a video preview.
@@ -1,7 +1,7 @@
1
1
  import * as ai from 'ai';
2
2
  import { ToolSet } from 'ai';
3
- import { B as BashToolProgress, W as WriteFileProgress, S as SearchToolProgress } from '../search-DOzC4ojH.js';
4
- export { b as BashToolOptions, d as SearchToolOptions, e as WriteFileToolOptions, c as createBashTool, f as createSearchTool, a as createWriteFileTool } from '../search-DOzC4ojH.js';
3
+ import { B as BashToolProgress, W as WriteFileProgress, S as SearchToolProgress } from '../search-CVVfuBPZ.js';
4
+ export { b as BashToolOptions, d as SearchToolOptions, e as WriteFileToolOptions, c as createBashTool, f as createSearchTool, a as createWriteFileTool } from '../search-CVVfuBPZ.js';
5
5
 
6
6
  interface TaskCompletionSignal {
7
7
  status: 'completed' | 'failed';
@@ -110,7 +110,7 @@ interface TodoToolOptions {
110
110
  workingDirectory: string;
111
111
  }
112
112
  declare function createTodoTool(options: TodoToolOptions): ai.Tool<{
113
- action: "list" | "add" | "mark" | "clear" | "save_plan" | "list_plans" | "get_plan" | "delete_plan";
113
+ action: "clear" | "add" | "list" | "mark" | "save_plan" | "list_plans" | "get_plan" | "delete_plan";
114
114
  status?: "completed" | "pending" | "in_progress" | "cancelled" | undefined;
115
115
  items?: {
116
116
  content: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkecoder",
3
- "version": "0.1.114",
3
+ "version": "0.1.115",
4
4
  "description": "A powerful coding agent CLI with HTTP API for development environments",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -154,23 +154,30 @@ Coordinates are in the **logical points** of the primary display — same coordi
154
154
 
155
155
  ## Record what you're doing (default ON)
156
156
 
157
- Computer-use sessions are *visual* — the user can't see the screen you're driving, only your text summary. **Record almost every computer-use task** so the user can replay it. See the **Recording** skill for the details (`load_skill recording`); the short version:
157
+ Computer-use sessions are *visual* — the user can't see the screen you're driving, only your text summary. **Record almost every computer-use task** so the user can replay it. Use the built-in `sparkecoder record` helper which manages start/stop properly so the recording covers the **entire task**, not just a fixed-time window:
158
158
 
159
159
  ```bash
160
- # At the start of the task, before your first `computer` action:
161
- mkdir -p ~/recordings
162
- RECORDING=~/recordings/cu-$(date +%s).mov
163
- screencapture -v -V 180 -C "$RECORDING" &
164
- REC_PID=$!
165
-
166
- # ... do all your computer-use actions ...
167
-
168
- # When you're done (success OR failure), stop the recording:
169
- kill -INT $REC_PID 2>/dev/null
170
- wait $REC_PID 2>/dev/null
171
- echo "Recording: $RECORDING"
160
+ # 1. At the START of the task, BEFORE your first `computer` action:
161
+ REC=$(sparkecoder record start --name "calculator-demo")
162
+ # REC is JSON: {"id":"rec-abc123","path":"~/recordings/rec-abc123-calculator-demo.mov","pid":12345}
163
+ REC_ID=$(echo "$REC" | jq -r .id)
164
+ REC_PATH=$(echo "$REC" | jq -r .path)
165
+ echo "Recording started: $REC_PATH (id=$REC_ID)"
166
+
167
+ # 2. ... do all your computer-use actions (open apps, click, type, etc.) ...
168
+
169
+ # 3. At the END (success OR failure), stop the recording:
170
+ sparkecoder record stop "$REC_ID"
171
+ # → JSON: {"id":"...","path":"...","durationSec":42,"sizeMb":18.4,"ok":true}
172
172
  ```
173
173
 
174
+ Why use `sparkecoder record` instead of `screencapture -V 60` directly:
175
+
176
+ - `-V <seconds>` is a fixed timeout — if your task takes longer than the guess, the recording ends mid-task and you get a partial. The helper has no timeout; it records until you explicitly stop it.
177
+ - The helper tracks PIDs in state so `sparkecoder record stop-all` can clean up if something crashes.
178
+ - Works on both macOS (screencapture) and Linux (ffmpeg x11grab) with the same command.
179
+ - Returns the file path as JSON, so you can include it in your final result without guessing.
180
+
174
181
  Default behavior:
175
182
 
176
183
  - **Always record** short / visually interesting tasks (open an app, click around, drag/drop, fill a form, demos, "show me X working").
@@ -22,6 +22,42 @@ Skip recording when:
22
22
  - The task is purely textual (reading/writing files, code analysis). The chat transcript already has everything.
23
23
  - Recording would capture sensitive content (passwords on screen, private DMs, customer data). Ask first.
24
24
 
25
+ ## Screen recording — use `sparkecoder record` (preferred)
26
+
27
+ The built-in helper handles start/stop reliably so the recording covers the WHOLE task, not a fixed time window. It tracks PIDs in state, works on macOS + Linux with the same interface, and returns JSON you can parse.
28
+
29
+ ```bash
30
+ # Start: returns {id, path, pid}
31
+ REC=$(sparkecoder record start --name "calculator-demo")
32
+ REC_ID=$(echo "$REC" | jq -r .id)
33
+ REC_PATH=$(echo "$REC" | jq -r .path)
34
+
35
+ # ... do work ...
36
+
37
+ # Stop: returns {id, path, durationSec, sizeMb, ok}
38
+ sparkecoder record stop "$REC_ID"
39
+ ```
40
+
41
+ Other operations:
42
+
43
+ ```bash
44
+ sparkecoder record list # list active recordings (auto-prunes dead pids)
45
+ sparkecoder record stop-all # stop everything (cleanup)
46
+ sparkecoder record start --dir /custom/path --name my-task
47
+ ```
48
+
49
+ Storage: defaults to `~/recordings/`. Files are named `rec-<id>[-<name>].<mov|mp4>`.
50
+
51
+ ### Why not `screencapture -V N` or raw `ffmpeg` backgrounded?
52
+
53
+ You can — but you have to:
54
+ - guess the right `-V` timeout (too short = partial, too long = wasted disk),
55
+ - track the PID yourself for `kill -INT`,
56
+ - pick the right tool per OS,
57
+ - and clean up zombie processes if you crash mid-task.
58
+
59
+ `sparkecoder record` does all of that. Reach for raw `screencapture` / `ffmpeg` only if you need specific flags the helper doesn't expose (custom display, rectangle crop, audio mux).
60
+
25
61
  ## Terminal recording — asciinema
26
62
 
27
63
  `asciinema` records the **content of a terminal session** as a tiny `.cast` text file. It's a fraction of the size of video, plays in any browser via [asciinema-player](https://github.com/asciinema/asciinema-player), and the output is also greppable.
@@ -85,49 +121,33 @@ curl -fsS -X POST "${SPARKECODER_REMOTE_URL}/storage/upload" \
85
121
  -F "file=@${RECORDING}" | jq .url
86
122
  ```
87
123
 
88
- ## Screen recording macOS native
124
+ ## Advanced: raw `screencapture` / `ffmpeg` (use only when the helper isn't enough)
89
125
 
90
- macOS ships with `screencapture`, no extra install needed. It writes `.mov` (H.264) which any modern app/browser plays.
126
+ Prefer `sparkecoder record` (above) for almost everything. Reach for raw tools only when you need flags the helper doesn't expose: rectangle crop, specific display, audio mux, custom frame rate.
91
127
 
92
128
  ### Requirements
93
129
 
94
- - **Screen Recording permission** for the agent's parent process (Terminal, iTerm, IDE, or `node`).
95
- - `sparkecoder check-permissions` verifies; `sparkecoder request-permissions` opens the right System Settings pane.
130
+ - macOS: **Screen Recording permission** for the agent's parent process. `sparkecoder check-permissions` verifies; `sparkecoder request-permissions` opens the right System Settings pane.
131
+ - Linux: `ffmpeg` installed (`sudo apt-get install -y ffmpeg`), and a running X server with the right `DISPLAY` env var.
96
132
 
97
- ### Record
133
+ ### macOS `screencapture` flags
98
134
 
99
135
  ```bash
100
- # Record the entire primary display until Ctrl-C (or the agent sends SIGINT to the process):
101
- screencapture -v ~/recordings/screen-$(date +%s).mov &
102
- REC_PID=$!
136
+ # Crop to a rectangle (x,y,w,h):
137
+ screencapture -v -R 100,100,800,600 -C ~/recordings/clip.mov
103
138
 
104
- # ... do work via computer-use, bash, etc. ...
139
+ # Pick a specific display (run `system_profiler SPDisplaysDataType` to list ids):
140
+ screencapture -v -G <displayID> -C ~/recordings/clip.mov
105
141
 
106
- # Stop cleanly:
107
- kill -INT $REC_PID
108
- wait $REC_PID 2>/dev/null
142
+ # Auto-stop after N seconds (avoid this for task-bounded recordings; use the
143
+ # helper instead so the recording covers the whole task):
144
+ screencapture -v -V 30 -C ~/recordings/clip.mov
109
145
  ```
110
146
 
111
- Useful flags:
112
-
113
- - `-V <seconds>` — stop automatically after this many seconds (no need for kill).
114
- - `-R x,y,w,h` — record just a rectangle.
115
- - `-C` — also capture mouse cursor (off by default).
116
- - `-G <displayID>` — pick a specific display (run `system_profiler SPDisplaysDataType` to list).
117
- - `-T <seconds>` — pre-roll countdown before starting.
118
-
119
- Example: 30-second auto-stop, full screen, with cursor:
147
+ ### Linux `ffmpeg` x11grab
120
148
 
121
149
  ```bash
122
- screencapture -v -V 30 -C ~/recordings/full-$(date +%s).mov
123
- ```
124
-
125
- ## Screen recording — Linux (`ffmpeg`)
126
-
127
- ```bash
128
- sudo apt-get install -y ffmpeg
129
-
130
- # Record the X display. Adjust :0.0 / resolution for your setup.
150
+ # Capture the X display (size matches the screen):
131
151
  ffmpeg -y -f x11grab -framerate 25 -video_size 1920x1080 -i :0.0 \
132
152
  -c:v libx264 -preset veryfast -pix_fmt yuv420p \
133
153
  ~/recordings/screen-$(date +%s).mp4
@@ -141,32 +161,36 @@ On a headless server with no X, screen recording isn't meaningful — record the
141
161
 
142
162
  | You want to … | Use |
143
163
  |---|---|
164
+ | Show what a computer-use task did on the desktop | `sparkecoder record start` → work → `record stop <id>` |
144
165
  | Capture a CLI demo, share a link | `asciinema rec → upload` |
145
- | Show what a computer-use task did on the desktop | `screencapture -v -V <seconds>` (macOS) |
146
- | Debug a flaky GUI test on Linux CI | `ffmpeg -f x11grab` around the test run |
147
166
  | Capture a single command's output verbatim | `asciinema rec --command "cmd" out.cast` |
148
- | Both terminal AND screen at once | start `asciinema rec` in a sub-shell AND `screencapture -v` in parallel |
167
+ | Debug a flaky GUI test (any OS) | `sparkecoder record start` around the test run |
168
+ | Both terminal AND screen at once | `sparkecoder record start` + `asciinema rec` in parallel |
149
169
 
150
170
  ## Best practices
151
171
 
152
172
  1. **Always announce.** Before starting a recording, post a short message to the user: "Starting screen recording for the Calculator demo." So they can intervene if it'd capture something sensitive.
153
- 2. **Auto-stop with a timer when you can.** `screencapture -V 60` is much safer than relying on the agent to remember to kill the process. Recordings that go forever fill disks.
154
- 3. **Use stable file names.** `~/recordings/<task-name>-<unix-ts>.<ext>`. The unix timestamp prevents overwrites and makes sorting easy.
155
- 4. **Clean up after yourself.** If the user only wanted to scrub through it once, delete the file when done recordings are often huge.
173
+ 2. **Use `sparkecoder record` for screen video.** It removes every footgun the raw tools have (no fixed timeout that cuts the task off mid-way, no PID juggling, no per-OS branching). Reach for raw `screencapture` / `ffmpeg` only when you need flags the helper doesn't expose (region crop, custom display, audio).
174
+ 3. **Bracket the WHOLE task.** `record start` *before* your first work command; `record stop <id>` *after* the very last one. Don't bracket each step individually — you'll lose context between clips.
175
+ 4. **Stop on exit, success OR failure.** Always run `record stop <id>` at the end of your task, even on errors. Otherwise the recorder keeps writing until `record stop-all` (or a reboot) catches it. The duration / size returned by `stop` is also useful in your final summary.
156
176
  5. **Don't record secrets.** Refuse to record if the screen contains visible passwords, 1Password vaults, customer PII, etc. Ask the user to close those windows first.
157
- 6. **For computer-use tasks: prefer recording the whole session, not per-action.** Starting `screencapture -v -V 120` once around your whole interaction is cheaper and produces a continuous video.
158
- 7. **Mention the file in your final message.** "Recording saved to `~/recordings/calc-1700000000.mov`" otherwise the user won't know where to look.
177
+ 6. **Clean up after yourself.** If a recording is no longer needed, `rm` it videos are big.
178
+ 7. **Always mention the file in your final message.** "Recording saved to `<path>`" or, better, upload via `/storage/upload` and post the `/f/<id>` URL so Slack/etc. unfurls it inline.
159
179
 
160
180
  ## Returning the recording in your final task summary
161
181
 
162
- For a **task worker** that captured a recording, include the path and a one-line description in your `result` so the orchestrator can route it back to the user:
182
+ For a **task worker** that captured a recording, include the structured fields below in your `result` so the orchestrator can route it back to the user:
163
183
 
164
184
  ```ts
165
185
  {
166
186
  "summary": "Opened Calculator, typed 2+2, hit Return. Got 4.",
167
- "screencapture": "~/recordings/calc-1700000000.mov",
168
- "screencapture_description": "60s screen recording of the entire Calculator interaction"
187
+ "recording": {
188
+ "path": "/Users/me/recordings/rec-abc123-calculator-demo.mov",
189
+ "durationSec": 42,
190
+ "sizeMb": 18.4,
191
+ "description": "Full screen recording of the Calculator interaction"
192
+ }
169
193
  }
170
194
  ```
171
195
 
172
- The orchestrator can then attach / post the file via Slack or whatever channel the original request came in on.
196
+ The orchestrator can then upload that path via `/storage/upload` and post the `/f/<id>` URL into Slack (or whatever channel the original request came in on) — Slack unfurls it inline as a video preview.
@@ -1 +1 @@
1
- N09EiHl0JwEgsGMdCtwpB
1
+ pSGgtxBjN5_o0YHZgQzXm
@@ -1 +1 @@
1
- N09EiHl0JwEgsGMdCtwpB
1
+ pSGgtxBjN5_o0YHZgQzXm
@@ -7,8 +7,8 @@
7
7
  "static/chunks/a6dad97d9634a72d.js"
8
8
  ],
9
9
  "lowPriorityFiles": [
10
- "static/N09EiHl0JwEgsGMdCtwpB/_ssgManifest.js",
11
- "static/N09EiHl0JwEgsGMdCtwpB/_buildManifest.js"
10
+ "static/pSGgtxBjN5_o0YHZgQzXm/_ssgManifest.js",
11
+ "static/pSGgtxBjN5_o0YHZgQzXm/_buildManifest.js"
12
12
  ],
13
13
  "rootMainFiles": [
14
14
  "static/chunks/651e187cc15d66de.js",
@@ -367,8 +367,8 @@
367
367
  "dynamicRoutes": {},
368
368
  "notFoundRoutes": [],
369
369
  "preview": {
370
- "previewModeId": "d7e724670d207f21c0f5034d346976e5",
371
- "previewModeSigningKey": "9b405af8ca111a80edf9bbf89cd645933d0e2db6ad1e7409914619ea3fcf27e1",
372
- "previewModeEncryptionKey": "f4162302e7d425faa461fa6c9ce01feab11721742085a3ff6f5d758670e7e6af"
370
+ "previewModeId": "4d4df0b08ac51f255c1fba959eff551a",
371
+ "previewModeSigningKey": "61a87433a4783d0b199c61d12ecf05a6503595b50fb2863e5e86ea9db1544061",
372
+ "previewModeEncryptionKey": "b4d0ec3479d75253ad48871374201d5a399ead8edb8cc23a194a5b2375eddfa9"
373
373
  }
374
374
  }
@@ -1,2 +1,2 @@
1
- <!DOCTYPE html><!--N09EiHl0JwEgsGMdCtwpB--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/651e187cc15d66de.js"/><script src="/_next/static/chunks/735a2408c315b2f0.js" async=""></script><script src="/_next/static/chunks/186e0c1b3ff43c9c.js" async=""></script><script src="/_next/static/chunks/a14243261b055626.js" async=""></script><script src="/_next/static/chunks/862ced58ce21a270.js" async=""></script><script src="/_next/static/chunks/turbopack-2c0905c7bbebae3f.js" async=""></script><script src="/_next/static/chunks/9b5512fb633ef95d.js" async=""></script><script src="/_next/static/chunks/0f1cf11540868e42.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><link rel="icon" href="/favicon.ico?favicon.e3cbed1b.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/a6dad97d9634a72d.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
- @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/651e187cc15d66de.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[488287,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n3:I[174895,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n4:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"ViewportBoundary\"]\n9:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"MetadataBoundary\"]\nb:I[552576,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"N09EiHl0JwEgsGMdCtwpB\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",\"$undefined\"],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"c:I[349310,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"IconMark\"]\n6:null\na:[[\"$\",\"link\",\"0\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.e3cbed1b.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$Lc\",\"1\",{}]]\n"])</script></body></html>
1
+ <!DOCTYPE html><!--pSGgtxBjN5_o0YHZgQzXm--><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/651e187cc15d66de.js"/><script src="/_next/static/chunks/735a2408c315b2f0.js" async=""></script><script src="/_next/static/chunks/186e0c1b3ff43c9c.js" async=""></script><script src="/_next/static/chunks/a14243261b055626.js" async=""></script><script src="/_next/static/chunks/862ced58ce21a270.js" async=""></script><script src="/_next/static/chunks/turbopack-2c0905c7bbebae3f.js" async=""></script><script src="/_next/static/chunks/9b5512fb633ef95d.js" async=""></script><script src="/_next/static/chunks/0f1cf11540868e42.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: Internal Server Error.</title><link rel="icon" href="/favicon.ico?favicon.e3cbed1b.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/a6dad97d9634a72d.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}
2
+ @media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">500</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">Internal Server Error.</h2></div></div></div><!--$--><!--/$--><script src="/_next/static/chunks/651e187cc15d66de.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[488287,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n3:I[174895,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n4:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n7:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"ViewportBoundary\"]\n9:I[151858,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"MetadataBoundary\"]\nb:I[552576,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"default\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"pSGgtxBjN5_o0YHZgQzXm\",\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"__PAGE__\",{}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"title\",null,{\"children\":\"500: Internal Server Error.\"}]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"lineHeight\":\"48px\"},\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"paddingRight\":23,\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\"},\"children\":\"500\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"28px\"},\"children\":\"Internal Server Error.\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,false]},null,false,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L7\",null,{\"children\":\"$L8\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$La\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$b\",\"$undefined\"],\"S\":true}\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"c:I[349310,[\"/_next/static/chunks/9b5512fb633ef95d.js\",\"/_next/static/chunks/0f1cf11540868e42.js\"],\"IconMark\"]\n6:null\na:[[\"$\",\"link\",\"0\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.e3cbed1b.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$Lc\",\"1\",{}]]\n"])</script></body></html>
@@ -6,7 +6,7 @@
6
6
  7:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"ViewportBoundary"]
7
7
  9:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"MetadataBoundary"]
8
8
  b:I[552576,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"default"]
9
- 0:{"P":null,"b":"N09EiHl0JwEgsGMdCtwpB","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
9
+ 0:{"P":null,"b":"pSGgtxBjN5_o0YHZgQzXm","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
10
10
  8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
11
11
  c:I[349310,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"IconMark"]
12
12
  6:null
@@ -1,5 +1,5 @@
1
1
  1:"$Sreact.fragment"
2
2
  2:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"OutletBoundary"]
3
3
  3:"$Sreact.suspense"
4
- 0:{"buildId":"N09EiHl0JwEgsGMdCtwpB","rsc":["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
4
+ 0:{"buildId":"pSGgtxBjN5_o0YHZgQzXm","rsc":["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L2",null,{"children":["$","$3",null,{"name":"Next.MetadataOutlet","children":"$@4"}]}]]}],"loading":null,"isPartial":false}
5
5
  4:null
@@ -6,7 +6,7 @@
6
6
  7:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"ViewportBoundary"]
7
7
  9:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"MetadataBoundary"]
8
8
  b:I[552576,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"default"]
9
- 0:{"P":null,"b":"N09EiHl0JwEgsGMdCtwpB","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
9
+ 0:{"P":null,"b":"pSGgtxBjN5_o0YHZgQzXm","c":["","_global-error"],"q":"","i":false,"f":[[["",{"children":["__PAGE__",{}]}],[["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","html",null,{"id":"__next_error__","children":[["$","head",null,{"children":["$","title",null,{"children":"500: Internal Server Error."}]}],["$","body",null,{"children":["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"style":{"lineHeight":"48px"},"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}\n@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","paddingRight":23,"fontSize":24,"fontWeight":500,"verticalAlign":"top"},"children":"500"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"28px"},"children":"Internal Server Error."}]}]]}]}]}]]}],null,["$","$L4",null,{"children":["$","$5",null,{"name":"Next.MetadataOutlet","children":"$@6"}]}]]}],{},null,false,false]},null,false,false],["$","$1","h",{"children":[null,["$","$L7",null,{"children":"$L8"}],["$","div",null,{"hidden":true,"children":["$","$L9",null,{"children":["$","$5",null,{"name":"Next.Metadata","children":"$La"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$b","$undefined"],"S":true}
10
10
  8:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
11
11
  c:I[349310,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"IconMark"]
12
12
  6:null
@@ -3,4 +3,4 @@
3
3
  3:I[151858,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"MetadataBoundary"]
4
4
  4:"$Sreact.suspense"
5
5
  5:I[349310,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"IconMark"]
6
- 0:{"buildId":"N09EiHl0JwEgsGMdCtwpB","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","link","0",{"rel":"icon","href":"/favicon.ico?favicon.e3cbed1b.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","1",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
6
+ 0:{"buildId":"pSGgtxBjN5_o0YHZgQzXm","rsc":["$","$1","h",{"children":[null,["$","$L2",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]}],["$","div",null,{"hidden":true,"children":["$","$L3",null,{"children":["$","$4",null,{"name":"Next.Metadata","children":[["$","link","0",{"rel":"icon","href":"/favicon.ico?favicon.e3cbed1b.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L5","1",{}]]}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],"loading":null,"isPartial":false}
@@ -1,4 +1,4 @@
1
1
  1:"$Sreact.fragment"
2
2
  2:I[488287,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"default"]
3
3
  3:I[174895,["/_next/static/chunks/9b5512fb633ef95d.js","/_next/static/chunks/0f1cf11540868e42.js"],"default"]
4
- 0:{"buildId":"N09EiHl0JwEgsGMdCtwpB","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
4
+ 0:{"buildId":"pSGgtxBjN5_o0YHZgQzXm","rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"loading":null,"isPartial":false}
@@ -1 +1 @@
1
- 0:{"buildId":"N09EiHl0JwEgsGMdCtwpB","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false},"staleTime":300}
1
+ 0:{"buildId":"pSGgtxBjN5_o0YHZgQzXm","tree":{"name":"","paramType":null,"paramKey":"","hasRuntimePrefetch":false,"slots":{"children":{"name":"__PAGE__","paramType":null,"paramKey":"__PAGE__","hasRuntimePrefetch":false,"slots":null,"isRootLayout":false}},"isRootLayout":false},"staleTime":300}