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 +21 -0
- package/README.md +206 -0
- package/bin/cli.js +64 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/index-DiiwvzTm.css +1 -0
- package/dist/assets/index-DtY88CkY.js +40 -0
- package/dist/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/icon-maskable-512.png +0 -0
- package/dist/icon.svg +28 -0
- package/dist/index.html +20 -0
- package/dist/manifest.webmanifest +1 -0
- package/dist/sw.js +1 -0
- package/dist/workbox-9c191d2f.js +1 -0
- package/package.json +86 -0
- package/scripts/dev.ts +75 -0
- package/scripts/devtools-recon.js +42 -0
- package/scripts/gen-icons.ts +22 -0
- package/scripts/service.ts +308 -0
- package/src/config.ts +81 -0
- package/src/db.ts +38 -0
- package/src/git.ts +116 -0
- package/src/reads.ts +164 -0
- package/src/server.ts +149 -0
- package/src/sidecar.ts +194 -0
- package/src/transcript.ts +111 -0
- package/src/writes.ts +133 -0
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)}}
|