conductor-remote 0.0.0-development

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eivind Hyldmo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # conductor-remote
2
+
3
+ A phone control panel for your **local** Conductor agents. Monitor every
4
+ workspace, read live transcripts, view diffs, and send prompts — from your
5
+ phone, over your Tailnet, no cloud, no App Store.
6
+
7
+ Installable as a PWA (Add to Home Screen). Reads ride Conductor's on-disk SQLite
8
+ + git worktrees (zero coupling); prompts ride Conductor's **own** dispatch path.
9
+
10
+ > Built after reconnaissance forced a design change from the original
11
+ > injected-IPC-bridge idea. Read **[FINDINGS.md](./FINDINGS.md)** for why — the
12
+ > short version is that reads ride SQLite + git and the write path is isolated
13
+ > behind a swappable `Actuator` interface (AppleScript by default, or Conductor's
14
+ > sidecar socket for precise per-session targeting).
15
+
16
+ ## Install
17
+
18
+ Needs [Conductor](https://conductor.build) running on the same Mac, Node ≥ 24, and
19
+ [Tailscale](https://tailscale.com) on the Mac + phone. Then:
20
+
21
+ ```bash
22
+ npm i -g conductor-remote
23
+ conductor-remote service install # run on login; prints your phone URL
24
+ ```
25
+
26
+ Or the one-liner (checks Node, offers `brew install node` if missing, installs,
27
+ registers the service):
28
+
29
+ ```bash
30
+ curl -fsSL https://raw.githubusercontent.com/hyldmo/conductor-remote/main/install.sh | bash
31
+ ```
32
+
33
+ The published package is **dependency-free** — no build step and no native addons
34
+ on your machine, so `npm i -g` finishes in about a second. `service install`
35
+ prints a phone URL with an embedded token; open it and **Add to Home Screen**.
36
+ Manage the service with `conductor-remote service status|restart|uninstall`.
37
+
38
+ ## Architecture
39
+
40
+ ```
41
+ Phone PWA ──HTTP/poll──► Relay (Node, on the Mac, bound to Tailscale)
42
+ (React, installable) │
43
+ ├─ reads: node:sqlite ⟶ conductor.db (workspaces, sessions, transcripts)
44
+ ├─ reads: git ⟶ each worktree (diffs vs target branch)
45
+ └─ writes: Actuator ⟶ Conductor
46
+ ├─ applescript (default): osascript ⟶ focused session
47
+ └─ sidecar (opt-in): unix socket ⟶ exact session
48
+ ```
49
+
50
+ - **Two halves.** A **Node relay** on the Mac (reads Conductor's state, drives
51
+ the write path) and a **React PWA** the phone installs. Vite builds the PWA to
52
+ `dist/`, which the relay serves.
53
+ - **Reads are durable.** No Conductor API, no injection — the SQLite schema and
54
+ git worktrees outlive UI changes.
55
+ - **Writes are the one fragile nerve** and are isolated behind a swappable
56
+ `Actuator` (`src/writes.ts`).
57
+
58
+ ## Stack
59
+
60
+ React 19 · React Router 7 · Vite 7 · Tailwind v4 · `vite-plugin-pwa` · TanStack
61
+ Query · Zustand · Biome · lucide. The relay is dependency-free Node 24
62
+ (`node:sqlite` + native TS type-stripping).
63
+
64
+ ## Requirements
65
+
66
+ - Node ≥ 24. ([Yarn](https://yarnpkg.com) — Berry, the repo pins `yarn@4` — is
67
+ only needed to run from source; the published package installs with plain npm.)
68
+ - Conductor running on the same Mac.
69
+ - [Tailscale](https://tailscale.com) on the Mac and the phone (or any shared
70
+ private network) to reach the relay.
71
+ - For the **write** path only: macOS **Accessibility** permission for whatever
72
+ runs the relay (Terminal/node) — System Settings ▸ Privacy & Security ▸
73
+ Accessibility. Reads work without it.
74
+
75
+ ## Run from source
76
+
77
+ For contributing or running an unpublished checkout (the `npm i -g` path above
78
+ needs neither a clone nor a build):
79
+
80
+ ```bash
81
+ git clone https://github.com/hyldmo/conductor-remote.git
82
+ cd conductor-remote
83
+ yarn install
84
+ yarn build # builds the PWA into dist/
85
+ yarn start # serves dist/ + the API (node bin/cli.js)
86
+ ```
87
+
88
+ Or in one step: `yarn preview` (build + start). For live development with HMR,
89
+ `yarn dev` runs Vite (`:5173`, proxying `/api`) alongside the relay.
90
+
91
+ The relay prints a phone URL with an embedded token:
92
+
93
+ ```
94
+ Open on your phone (same Tailnet):
95
+ http://100.x.y.z:8787/#token=<generated>
96
+ ```
97
+
98
+ Open that on the phone and **Add to Home Screen**. The token is stored in
99
+ `localStorage`; every `/api/*` call carries it, so other devices on the LAN
100
+ can't read your sessions.
101
+
102
+ ### Deploy (run on login as a background service)
103
+
104
+ The relay must run on the Mac that runs Conductor — it reads the local state DB
105
+ and git worktrees and drives the local sidecar/AX write path, so there is
106
+ nothing to host in the cloud. "Deployment" here means installing it as a macOS
107
+ **LaunchAgent** that starts on login and restarts if it dies:
108
+
109
+ ```bash
110
+ yarn deploy # build dist/ + install & start the LaunchAgent, prints the phone URL
111
+ yarn service status # state + pid + phone URL
112
+ yarn service restart # e.g. after Tailscale comes up late, or a code change
113
+ yarn service uninstall # tear it down
114
+ ```
115
+
116
+ From a global install the same commands are `conductor-remote service
117
+ <status|restart|uninstall>` (and `conductor-remote service install` in place of
118
+ `yarn deploy`, since `dist/` ships prebuilt in the package).
119
+
120
+ The token is now **persisted** (`~/Library/Application Support/conductor-remote/token`),
121
+ so the home-screen URL stays valid across restarts and reboots. Logs land in
122
+ `~/Library/Logs/conductor-remote/`. Two caveats: the agent bakes the absolute
123
+ `node` path at install time — re-run `yarn deploy` after an nvm node upgrade —
124
+ and the AppleScript write path needs Accessibility permission granted to that
125
+ `node` binary (System Settings ▸ Privacy & Security ▸ Accessibility).
126
+
127
+ ### Config (env)
128
+
129
+ | Var | Default | Purpose |
130
+ | --- | --- | --- |
131
+ | `RELAY_PORT` | `8787` | Listen port |
132
+ | `RELAY_HOST` | auto (Tailscale `100.x`, else `127.0.0.1`) | Bind address |
133
+ | `RELAY_TOKEN` | persisted (auto-generated, reused across restarts) | Override the shared secret |
134
+ | `WRITE_STRATEGY` | `applescript` | `applescript` (focused session) or `sidecar` (precise per-session — see below) |
135
+ | `CONDUCTOR_DB` | `~/Library/Application Support/com.conductor.app/conductor.db` | State DB |
136
+ | `CONDUCTOR_WORKSPACES` | `~/conductor/workspaces` | Worktree root |
137
+
138
+ The token is auto-generated once and persisted, so the home-screen icon keeps
139
+ working across restarts without any env var. To pin a specific secret (e.g. to
140
+ share a known URL), set `RELAY_TOKEN`:
141
+
142
+ ```bash
143
+ RELAY_TOKEN=$(openssl rand -hex 16) yarn start
144
+ ```
145
+
146
+ ## What works today
147
+
148
+ - ✅ Live list of active workspaces with agent status (working / idle / done),
149
+ repo, branch, model, context %, unread badge.
150
+ - ✅ Live transcript per session (assistant text, tool calls, queued messages),
151
+ incremental polling.
152
+ - ✅ Diff vs the workspace's target branch (file list + colorized patch,
153
+ including untracked files).
154
+ - ✅ **Send prompt** — two strategies:
155
+ - **`applescript`** (default): drives Conductor's real UI send, landing in the
156
+ *focused* session. Uses the session's own model/permission mode (zero risk of
157
+ altering the agent). Bring the target workspace to front first.
158
+ - **`sidecar`** (opt-in, `WRITE_STRATEGY=sidecar`): delivers straight to the
159
+ target `sessionId` over Conductor's dispatch socket — precise per-workspace
160
+ targeting, no focus needed. Speaks a private, versioned IPC (see FINDINGS ▸
161
+ Writes), so it's the more fragile of the two.
162
+
163
+ ## Layout
164
+
165
+ ```
166
+ src/ the Node relay (no build step)
167
+ server.ts HTTP router: /api/* (token-gated) + static PWA
168
+ config.ts paths, port, Tailscale bind, token, write strategy
169
+ db.ts read-only node:sqlite handle to conductor.db
170
+ reads.ts workspaces / sessions / messages + worktree resolution
171
+ transcript.ts Claude Code SDK stream JSON → phone-renderable entries
172
+ git.ts workspace diff vs target branch (incl. untracked)
173
+ sidecar.ts Conductor sidecar IPC client (precise write path)
174
+ writes.ts Actuator interface + AppleScript (default) + Sidecar (opt-in)
175
+ web/ the React PWA (Vite)
176
+ index.html, src/ app shell, components, hooks, api client
177
+ public/ icon.svg (PNGs generated by `yarn gen:icons`)
178
+ scripts/
179
+ dev.ts runs Vite + relay together
180
+ gen-icons.ts rasterizes icon.svg → PWA PNGs (sharp)
181
+ devtools-recon.js invoke-logger — only usable if a future build ships devtools
182
+ dist/ built PWA (gitignored) — what the relay serves
183
+ ```
184
+
185
+ ## Security notes
186
+
187
+ - The relay binds to your Tailnet IP (or loopback), not `0.0.0.0`.
188
+ - All data endpoints require the shared token; static shell assets are public
189
+ but carry no secrets. The service worker never caches `/api/*`.
190
+ - The write path can drive your real agents — keep the token private and the
191
+ bind address off untrusted networks.
192
+ - The relay reads Conductor's SQLite DB **read-only** and never writes to it.
193
+ Your data stays on your machine — nothing is sent anywhere.
194
+
195
+ ## Disclaimer
196
+
197
+ Unofficial and not affiliated with, endorsed by, or supported by Conductor. It
198
+ reads your own local Conductor data and automates your own machine. It depends
199
+ on Conductor's on-disk layout (a SQLite DB + git worktrees) and, for the sidecar
200
+ write strategy, a private IPC — neither is a public API and both may change
201
+ between Conductor releases. See [FINDINGS.md](./FINDINGS.md) for how it's
202
+ structured to keep breakage rare and cheap to repair. macOS only.
203
+
204
+ ## License
205
+
206
+ [MIT](./LICENSE)
package/bin/cli.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ // conductor-remote CLI entrypoint.
3
+ //
4
+ // Runs on plain Node ≥24 with zero flags: the relay is stdlib-only and the two
5
+ // param-property constructors that once needed --experimental-transform-types
6
+ // are gone, so default type-stripping handles the .ts sources on import.
7
+ //
8
+ // The one remaining wrinkle is node:sqlite, still flagged experimental in 24.
9
+ // We silence *only* that warning here (instead of re-execing node with
10
+ // --disable-warning) so the entrypoint stays a shebang + import.
11
+ const emitWarning = process.emitWarning.bind(process)
12
+ process.emitWarning = (warning, ...rest) => {
13
+ const type = typeof rest[0] === 'string' ? rest[0] : rest[0]?.type
14
+ if (type === 'ExperimentalWarning') return
15
+ return emitWarning(warning, ...rest)
16
+ }
17
+
18
+ const REQUIRED_MAJOR = 24
19
+ const major = Number(process.versions.node.split('.')[0])
20
+ if (major < REQUIRED_MAJOR) {
21
+ console.error(
22
+ `conductor-remote needs Node ${REQUIRED_MAJOR}+ (found ${process.versions.node}).\n` +
23
+ 'It relies on node:sqlite and default TypeScript type-stripping. Upgrade node and retry.'
24
+ )
25
+ process.exit(1)
26
+ }
27
+
28
+ const [cmd, ...rest] = process.argv.slice(2)
29
+
30
+ function usage() {
31
+ console.info(
32
+ [
33
+ 'conductor-remote — phone control panel for local Conductor agents',
34
+ '',
35
+ 'Usage:',
36
+ ' conductor-remote [start] run the relay (default)',
37
+ ' conductor-remote service <subcommand> manage the login LaunchAgent',
38
+ ' install | uninstall | restart | status',
39
+ '',
40
+ 'Env: RELAY_HOST, RELAY_PORT, RELAY_TOKEN, WRITE_STRATEGY, CONDUCTOR_DB, CONDUCTOR_WORKSPACES'
41
+ ].join('\n')
42
+ )
43
+ }
44
+
45
+ switch (cmd) {
46
+ case undefined:
47
+ case 'start':
48
+ await import('../src/server.ts')
49
+ break
50
+ case 'service':
51
+ // service.ts reads its subcommand from argv[2]; re-shape argv so `service install` → `install`.
52
+ process.argv = [process.argv[0], process.argv[1], ...rest]
53
+ await import('../scripts/service.ts')
54
+ break
55
+ case '-h':
56
+ case '--help':
57
+ case 'help':
58
+ usage()
59
+ break
60
+ default:
61
+ console.error(`unknown command: ${cmd}\n`)
62
+ usage()
63
+ process.exit(1)
64
+ }
Binary file
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#0a0b0e;--color-surface:#14161b;--color-surface-2:#1b1e26;--color-border:#262a33;--color-border-soft:#1e222a;--color-text:#e7e9ee;--color-muted:#8b909d;--color-faint:#5c616d;--color-accent:#8b7dff;--color-accent-soft:#2a2650;--color-working:#f5a623;--color-idle:#3ecf8e;--color-done:#5b9dff;--color-add:#3ecf8e;--color-del:#ff6b6b}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{-webkit-tap-highlight-color:transparent}html,body,#root{height:100%}body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overscroll-behavior-y:none;margin:0;overflow-x:hidden}button{font:inherit;color:inherit;cursor:pointer}::-webkit-scrollbar{width:0;height:0}}@layer components{.card{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3.5);text-align:left;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.card:active{background-color:var(--color-surface-2);scale:.985}.pill{padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-muted);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px}.pill-active{background-color:var(--color-surface-2);color:var(--color-text)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);border-radius:3.40282e38px;flex-shrink:0}.dot-working{background:var(--color-working);box-shadow:0 0 #f5a62399}@supports (color:color-mix(in lab,red,red)){.dot-working{box-shadow:0 0 color-mix(in srgb,var(--color-working) 60%,transparent)}}.dot-working{animation:1.6s ease-out infinite pulse}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.9em 0 .4em;font-weight:600;line-height:1.3}.md h1{font-size:1.25em}.md h2{font-size:1.15em}.md h3{font-size:1.05em}.md ul,.md ol{margin:.5em 0;padding-left:1.4em}.md ul{list-style:outside}.md ol{list-style:decimal}.md li,.md li>ul,.md li>ol{margin:.2em 0}.md code{background:#1b1e26cc;border-radius:.3rem}@supports (color:color-mix(in lab,red,red)){.md code{background:color-mix(in srgb,var(--color-surface-2) 80%,transparent)}}.md code{font-family:var(--font-mono);padding:.1em .35em;font-size:.85em}.md pre{border:1px solid var(--color-border-soft);background:var(--color-bg);border-radius:.6rem;margin:.5em 0;padding:.6em .75em;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:12px;line-height:1.5}.md a{color:var(--color-accent);text-underline-offset:2px;text-decoration:underline}.md blockquote{border-left:2px solid var(--color-border);color:var(--color-muted);margin:.5em 0;padding-left:.75em}.md hr{border:0;border-top:1px solid var(--color-border-soft);margin:.75em 0}.md table{border-collapse:collapse;max-width:100%;margin:.5em 0;font-size:.9em;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--color-border);text-align:left;padding:.3em .6em}.md th{background:var(--color-surface-2);font-weight:600}.md strong{font-weight:600}.md input[type=checkbox]{margin-right:.4em}}@layer utilities{.fixed{position:fixed}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{inset-block:0}.top-0{top:0}.left-0{left:0}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.size-1\.5\!{width:calc(var(--spacing) * 1.5)!important;height:calc(var(--spacing) * 1.5)!important}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-20{width:calc(var(--spacing) * 20);height:calc(var(--spacing) * 20)}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.w-1{width:var(--spacing)}.w-\[85\%\]{width:85%}.w-full{width:100%}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[85\%\]{max-width:85%}.max-w-\[92\%\]{max-width:92%}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-5{gap:calc(var(--spacing) * 5)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-accent\/50{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-border{border-color:var(--color-border)}.border-border-soft{border-color:var(--color-border-soft)}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-bg{background-color:var(--color-bg)}.bg-bg\/80{background-color:#0a0b0ecc}@supports (color:color-mix(in lab,red,red)){.bg-bg\/80{background-color:color-mix(in oklab,var(--color-bg) 80%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-del\/10{background-color:#ff6b6b1a}@supports (color:color-mix(in lab,red,red)){.bg-del\/10{background-color:color-mix(in oklab,var(--color-del) 10%,transparent)}}.bg-idle\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-idle\/10{background-color:color-mix(in oklab,var(--color-idle) 10%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface\/60{background-color:#14161b99}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-transparent{background-color:#0000}.bg-working\/10{background-color:#f5a6231a}@supports (color:color-mix(in lab,red,red)){.bg-working\/10{background-color:color-mix(in oklab,var(--color-working) 10%,transparent)}}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-add{color:var(--color-add)}.text-del{color:var(--color-del)}.text-faint{color:var(--color-faint)}.text-idle{color:var(--color-idle)}.text-muted{color:var(--color-muted)}.text-text{color:var(--color-text)}.text-white{color:var(--color-white)}.text-working{color:var(--color-working)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.opacity-60{opacity:.6}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-faint::placeholder{color:var(--color-faint)}.focus-within\:border-accent\/60:focus-within{border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.focus-within\:border-accent\/60:focus-within{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}.active\:scale-90:active{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:bg-surface-2:active,.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:static{position:static}.md\:z-auto{z-index:auto}.md\:block{display:block}.md\:hidden{display:none}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:transition-none{transition-property:none}}@media(min-width:64rem){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:w-80{width:calc(var(--spacing) * 80)}.lg\:w-\[380px\]{width:380px}.lg\:shrink-0{flex-shrink:0}.lg\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.lg\:border-border-soft{border-color:var(--color-border-soft)}}@media(min-width:80rem){.xl\:w-\[460px\]{width:460px}}}@keyframes pulse{50%{opacity:.5}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}