tandem-editor 0.2.11 → 0.3.0

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/README.md CHANGED
@@ -1,201 +1,201 @@
1
- <p align="center">
2
- <img src="docs/assets/banner.png" alt="Tandem — Collaborative AI-Human Document Editor" width="800">
3
- </p>
4
-
5
- An AI document reviewer — open a progress report, RFP, or compliance filing and Claude reviews it alongside you in real time. Highlights, comments, suggestions, and questions appear as first-class annotations you accept, dismiss, or discuss. The original file is never modified unless you save.
6
-
7
- ![Tandem editor showing a document with annotations, side panel, and Claude's presence](docs/screenshots/01-editor-overview.png)
8
-
9
- ## Quick Start
10
-
11
- ### Prerequisites
12
-
13
- - **Node.js 22+** ([download](https://nodejs.org))
14
- - **Claude Code** (`irm https://claude.ai/install.ps1 | iex`)
15
-
16
- ### Install and Run
17
-
18
- ```bash
19
- npm install -g tandem-editor
20
- tandem setup # registers MCP tools + installs Claude Code skill
21
- tandem # starts server + opens browser
22
- ```
23
-
24
- `tandem setup` auto-detects Claude Code and Claude Desktop, writes MCP configuration, and installs a skill (`~/.claude/skills/tandem/SKILL.md`) that teaches Claude how to use Tandem's tools effectively. Re-run after upgrading (`npm update -g tandem-editor && tandem setup`).
25
-
26
- ### Connect Claude Code
27
-
28
- Start Claude Code with channel push for real-time notifications:
29
-
30
- ```bash
31
- claude --dangerously-load-development-channels server:tandem-channel
32
- ```
33
-
34
- Then try:
35
-
36
- ```
37
- "Review the welcome document with me"
38
- ```
39
-
40
- Claude calls `tandem_open`, the document appears in the browser, and annotations start flowing. Chat messages, annotation actions, and text selections push to Claude instantly.
41
-
42
- **Without channels:** Use the `/loop` skill in Claude Code to poll:
43
-
44
- ```
45
- /loop 30s check tandem inbox and respond to any new messages
46
- ```
47
-
48
- ### Verify
49
-
50
- ```bash
51
- npm run doctor # checks Node.js, MCP config, server health, ports
52
- ```
53
-
54
- Or check the raw health endpoint:
55
-
56
- ```bash
57
- curl http://localhost:3479/health
58
- # → {"status":"ok","version":"0.1.2","transport":"http","hasSession":false}
59
- ```
60
-
61
- `hasSession` becomes `true` once Claude Code connects.
62
-
63
- <details>
64
- <summary><strong>Development Setup</strong> (contributing / building from source)</summary>
65
-
66
- ```bash
67
- git clone https://github.com/bloknayrb/tandem.git
68
- cd tandem
69
- npm install
70
- npm run dev:standalone # starts server (:3478/:3479) + browser client (:5173)
71
- ```
72
-
73
- Open http://localhost:5173 — you'll see `sample/welcome.md` loaded automatically on first run. The `.mcp.json` in the repo configures Claude Code automatically when run from this directory.
74
-
75
- </details>
76
-
77
- ## Features
78
-
79
- ### Annotations
80
-
81
- ![Side panel showing annotation cards with filtering, bulk actions, and text previews](docs/screenshots/03-side-panel.png)
82
-
83
- Claude adds highlights, comments, suggestions, and flags directly in the document. The side panel lists all annotations with filtering by type, author, and status. Accept, dismiss, or edit each one individually — or use bulk actions to process them in batches.
84
-
85
- ### Chat
86
-
87
- ![Chat sidebar showing messages, typing indicator, and panel toggle](docs/screenshots/02-chat-sidebar.png)
88
-
89
- Send freeform messages to Claude alongside annotation review. Select text before sending to attach it as a clickable anchor — clicking it later scrolls back to that passage.
90
-
91
- ### Review Mode
92
-
93
- ![Review mode with dimmed editor and active annotation highlighted](docs/screenshots/05-review-mode.png)
94
-
95
- Press **Ctrl+Shift+R** to enter keyboard review mode. Navigate with **Tab**, accept with **Y**, dismiss with **N**, examine with **E**. A 10-second undo window lets you reverse accidental accepts. The side panel tracks your position.
96
-
97
- ### More
98
-
99
- - **Multi-document tabs** — open `.md`, `.txt`, `.docx` files side by side; drag to reorder
100
- - **.docx review-only mode** — open Word documents for annotation; imported Word comments appear alongside Claude's
101
- - **Session persistence** — documents and annotations survive server restarts
102
- - **Real-time channel push** — annotation actions, chat, and selections push to Claude instantly
103
- - **Keyboard shortcuts** — press `?` for the full reference
104
- - **Unsaved-changes indicator** — dot on tab title when a document has pending edits
105
- - **Configurable display name** — set your name so Claude knows who's reviewing
106
- - **Atomic file saves** — write to temp, then rename, preventing partial writes
107
- - **E2E tested** — Playwright tests cover the annotation lifecycle end-to-end
108
-
109
- ## Documentation
110
-
111
- - **[User Guide](docs/user-guide.md)** — How to use Tandem: browser UI, annotations, chat, review mode, keyboard shortcuts
112
- - [MCP Tool Reference](docs/mcp-tools.md) — 30 MCP tools + channel API endpoints
113
- - [Architecture](docs/architecture.md) — System design, data flows, coordinate systems, channel push
114
- - [Workflows](docs/workflows.md) — Claude Code usage patterns: document review, cross-referencing, multi-model
115
- - [Roadmap](docs/roadmap.md) — Phase 2+ roadmap, known issues, future extensions
116
- - [Design Decisions](docs/decisions.md) — ADR-001 through ADR-021
117
- - [Lessons Learned](docs/lessons-learned.md) — 31 implementation lessons
118
-
119
- ## CLI Commands
120
-
121
- | Command | What it does |
122
- |---------|-------------|
123
- | `tandem` | Start server and open browser (global install) |
124
- | `tandem setup` | Register MCP tools with Claude Code / Claude Desktop |
125
- | `tandem setup --force` | Register to default paths regardless of auto-detection |
126
- | `tandem --version` | Show installed version |
127
- | `tandem --help` | Show usage |
128
-
129
- ## MCP Configuration
130
-
131
- Tandem uses two MCP connections: **HTTP** for document tools (28 tools including annotation editing), and a **channel shim** for real-time push notifications.
132
-
133
- **Global install** (`tandem setup`): Automatically writes both entries to `~/.claude/mcp_settings.json` (Claude Code) and/or `claude_desktop_config.json` (Claude Desktop) with absolute paths. No manual configuration needed.
134
-
135
- **Development setup** (`.mcp.json`): The repo includes a `.mcp.json` that configures both entries automatically when Claude Code runs from the repo directory:
136
-
137
- ```json
138
- {
139
- "mcpServers": {
140
- "tandem": {
141
- "type": "http",
142
- "url": "http://localhost:3479/mcp"
143
- },
144
- "tandem-channel": {
145
- "command": "npx",
146
- "args": ["tsx", "src/channel/index.ts"],
147
- "env": { "TANDEM_URL": "http://localhost:3479" }
148
- }
149
- }
150
- }
151
- ```
152
-
153
- Both entries are cross-platform — no platform-specific configuration needed.
154
-
155
- ## Environment Variables
156
-
157
- All optional — defaults work out of the box.
158
-
159
- | Variable | Default | Description |
160
- |----------|---------|-------------|
161
- | `TANDEM_PORT` | `3478` | Hocuspocus WebSocket port |
162
- | `TANDEM_MCP_PORT` | `3479` | MCP HTTP + REST API port |
163
- | `TANDEM_URL` | `http://localhost:3479` | Channel shim server URL |
164
- | `TANDEM_TRANSPORT` | `http` | Transport mode (`http` or `stdio`) |
165
- | `TANDEM_NO_SAMPLE` | unset | Set to `1` to skip auto-opening `sample/welcome.md` |
166
- | `TANDEM_CLAUDE_CMD` | `claude` | Claude Code executable name (for `tandem setup` auto-detection) |
167
-
168
- See `.env.example` for a copy-paste template.
169
-
170
- ## Troubleshooting
171
-
172
- Run `npm run doctor` for a quick diagnostic of your setup. It checks Node.js version, `.mcp.json` config, server health, and port status.
173
-
174
- **Claude Code says "MCP failed to connect"**
175
- Start the server first (`tandem` for global install, or `npm run dev:standalone` for dev setup), then open Claude Code. The server must be running before Claude Code probes the MCP URL. If you restart the server, run `/mcp` in Claude Code to reconnect.
176
-
177
- **Port already in use**
178
- Tandem kills stale processes on :3478/:3479 at startup. If another app uses those ports, set `TANDEM_PORT` / `TANDEM_MCP_PORT` to different values and update `TANDEM_URL` to match.
179
-
180
- **Channel shim fails to start**
181
- The `tandem-channel` entry spawns a subprocess. For global installs, `tandem setup` writes absolute paths to the bundled `dist/channel/index.js` — re-run `tandem setup` after upgrading. For dev setup, if you see `MODULE_NOT_FOUND` with a production config (`node dist/channel/index.js`), run `npm run build`. The default dev config uses `npx tsx` and doesn't require a build step.
182
-
183
- **Browser shows "Cannot reach the Tandem server"**
184
- The browser connects to the server via WebSocket. For global installs, run `tandem` to start the server. For dev setup, use `npm run dev:standalone` (or `npm run dev:server`). The message appears after 3 seconds of failed connection.
185
-
186
- **Empty browser with no document**
187
- On first run, `sample/welcome.md` auto-opens. If you've cleared sessions or deleted the sample file, click the **+** button in the tab bar or drop a file onto the editor.
188
-
189
- ## Development
190
-
191
- | Command | What it does |
192
- |---------|-------------|
193
- | `npm run dev:standalone` | **Recommended** — both frontend + backend (via concurrently) |
194
- | `npm run dev:server` | Backend only: Hocuspocus (:3478) + MCP HTTP (:3479) |
195
- | `npm run dev:client` | Frontend only: Vite dev server (:5173) |
196
- | `npm run build` | Production build (`dist/server/` + `dist/channel/` + `dist/cli/` + `dist/client/`) |
197
- | `npm test` | Run vitest (unit tests) |
198
- | `npm run test:e2e` | Run Playwright E2E tests |
199
- | `npm run test:e2e:ui` | Playwright UI mode |
200
-
201
- **Tech Stack:** React 19, Tiptap, Vite, TypeScript | Node.js, Hocuspocus (Yjs WebSocket), MCP SDK, Express | Yjs (CRDT), y-prosemirror | mammoth.js (.docx), unified/remark (.md)
1
+ <p align="center">
2
+ <img src="docs/assets/banner.png" alt="Tandem — Collaborative AI-Human Document Editor" width="800">
3
+ </p>
4
+
5
+ An AI document reviewer — open a progress report, RFP, or compliance filing and Claude reviews it alongside you in real time. Highlights, comments, suggestions, and questions appear as first-class annotations you accept, dismiss, or discuss. The original file is never modified unless you save.
6
+
7
+ ![Tandem editor showing a document with annotations, side panel, and Claude's presence](docs/screenshots/01-editor-overview.png)
8
+
9
+ ## Quick Start
10
+
11
+ ### Prerequisites
12
+
13
+ - **Node.js 22+** ([download](https://nodejs.org))
14
+ - **Claude Code** (`irm https://claude.ai/install.ps1 | iex`)
15
+
16
+ ### Install and Run
17
+
18
+ ```bash
19
+ npm install -g tandem-editor
20
+ tandem setup # registers MCP tools + installs Claude Code skill
21
+ tandem # starts server + opens browser
22
+ ```
23
+
24
+ `tandem setup` auto-detects Claude Code and Claude Desktop, writes MCP configuration, and installs a skill (`~/.claude/skills/tandem/SKILL.md`) that teaches Claude how to use Tandem's tools effectively. Re-run after upgrading (`npm update -g tandem-editor && tandem setup`).
25
+
26
+ ### Connect Claude Code
27
+
28
+ Start Claude Code with channel push for real-time notifications:
29
+
30
+ ```bash
31
+ claude --dangerously-load-development-channels server:tandem-channel
32
+ ```
33
+
34
+ Then try:
35
+
36
+ ```
37
+ "Review the welcome document with me"
38
+ ```
39
+
40
+ Claude calls `tandem_open`, the document appears in the browser, and annotations start flowing. Chat messages, annotation actions, and text selections push to Claude instantly.
41
+
42
+ **Without channels:** Use the `/loop` skill in Claude Code to poll:
43
+
44
+ ```
45
+ /loop 30s check tandem inbox and respond to any new messages
46
+ ```
47
+
48
+ ### Verify
49
+
50
+ ```bash
51
+ npm run doctor # checks Node.js, MCP config, server health, ports
52
+ ```
53
+
54
+ Or check the raw health endpoint:
55
+
56
+ ```bash
57
+ curl http://localhost:3479/health
58
+ # → {"status":"ok","version":"0.1.2","transport":"http","hasSession":false}
59
+ ```
60
+
61
+ `hasSession` becomes `true` once Claude Code connects.
62
+
63
+ <details>
64
+ <summary><strong>Development Setup</strong> (contributing / building from source)</summary>
65
+
66
+ ```bash
67
+ git clone https://github.com/bloknayrb/tandem.git
68
+ cd tandem
69
+ npm install
70
+ npm run dev:standalone # starts server (:3478/:3479) + browser client (:5173)
71
+ ```
72
+
73
+ Open http://localhost:5173 — you'll see `sample/welcome.md` loaded automatically on first run. The `.mcp.json` in the repo configures Claude Code automatically when run from this directory.
74
+
75
+ </details>
76
+
77
+ ## Features
78
+
79
+ ### Annotations
80
+
81
+ ![Side panel showing annotation cards with filtering, bulk actions, and text previews](docs/screenshots/03-side-panel.png)
82
+
83
+ Claude adds highlights, comments, suggestions, and flags directly in the document. Suggestion cards show a visual diff — original text in red strikethrough, replacement in green. The side panel lists all annotations with filtering by type, author, and status. Accept, dismiss, or edit each one individually — or use bulk actions to process them in batches.
84
+
85
+ ### Chat
86
+
87
+ ![Chat sidebar showing messages, typing indicator, and panel toggle](docs/screenshots/02-chat-sidebar.png)
88
+
89
+ Send freeform messages to Claude alongside annotation review. Select text before sending to attach it as a clickable anchor — clicking it later scrolls back to that passage.
90
+
91
+ ### Review Mode
92
+
93
+ ![Review mode with dimmed editor and active annotation highlighted](docs/screenshots/05-review-mode.png)
94
+
95
+ Press **Ctrl+Shift+R** to enter keyboard review mode. Navigate with **Tab**, accept with **Y**, dismiss with **N**, examine with **E**. A 10-second undo window with a visual countdown lets you reverse accidental accepts. Shortcut hints appear below the Review button.
96
+
97
+ ### More
98
+
99
+ - **Multi-document tabs** — open `.md`, `.txt`, `.docx` files side by side; drag to reorder
100
+ - **.docx review-only mode** — open Word documents for annotation; imported Word comments appear alongside Claude's
101
+ - **Session persistence** — documents and annotations survive server restarts
102
+ - **Real-time channel push** — annotation actions, chat, and selections push to Claude instantly
103
+ - **Keyboard shortcuts** — press `?` for the full reference
104
+ - **Unsaved-changes indicator** — dot on tab title when a document has pending edits
105
+ - **Configurable display name** — set your name so Claude knows who's reviewing
106
+ - **Atomic file saves** — write to temp, then rename, preventing partial writes
107
+ - **E2E tested** — Playwright tests cover the annotation lifecycle end-to-end
108
+
109
+ ## Documentation
110
+
111
+ - **[User Guide](docs/user-guide.md)** — How to use Tandem: browser UI, annotations, chat, review mode, keyboard shortcuts
112
+ - [MCP Tool Reference](docs/mcp-tools.md) — 30 MCP tools + channel API endpoints
113
+ - [Architecture](docs/architecture.md) — System design, data flows, coordinate systems, channel push
114
+ - [Workflows](docs/workflows.md) — Claude Code usage patterns: document review, cross-referencing, multi-model
115
+ - [Roadmap](docs/roadmap.md) — Phase 2+ roadmap, known issues, future extensions
116
+ - [Design Decisions](docs/decisions.md) — ADR-001 through ADR-021
117
+ - [Lessons Learned](docs/lessons-learned.md) — 31 implementation lessons
118
+
119
+ ## CLI Commands
120
+
121
+ | Command | What it does |
122
+ |---------|-------------|
123
+ | `tandem` | Start server and open browser (global install) |
124
+ | `tandem setup` | Register MCP tools with Claude Code / Claude Desktop |
125
+ | `tandem setup --force` | Register to default paths regardless of auto-detection |
126
+ | `tandem --version` | Show installed version |
127
+ | `tandem --help` | Show usage |
128
+
129
+ ## MCP Configuration
130
+
131
+ Tandem uses two MCP connections: **HTTP** for document tools (30 tools including annotation editing), and a **channel shim** for real-time push notifications.
132
+
133
+ **Global install** (`tandem setup`): Automatically writes both entries to `~/.claude/mcp_settings.json` (Claude Code) and/or `claude_desktop_config.json` (Claude Desktop) with absolute paths. No manual configuration needed.
134
+
135
+ **Development setup** (`.mcp.json`): The repo includes a `.mcp.json` that configures both entries automatically when Claude Code runs from the repo directory:
136
+
137
+ ```json
138
+ {
139
+ "mcpServers": {
140
+ "tandem": {
141
+ "type": "http",
142
+ "url": "http://localhost:3479/mcp"
143
+ },
144
+ "tandem-channel": {
145
+ "command": "npx",
146
+ "args": ["tsx", "src/channel/index.ts"],
147
+ "env": { "TANDEM_URL": "http://localhost:3479" }
148
+ }
149
+ }
150
+ }
151
+ ```
152
+
153
+ Both entries are cross-platform — no platform-specific configuration needed.
154
+
155
+ ## Environment Variables
156
+
157
+ All optional — defaults work out of the box.
158
+
159
+ | Variable | Default | Description |
160
+ |----------|---------|-------------|
161
+ | `TANDEM_PORT` | `3478` | Hocuspocus WebSocket port |
162
+ | `TANDEM_MCP_PORT` | `3479` | MCP HTTP + REST API port |
163
+ | `TANDEM_URL` | `http://localhost:3479` | Channel shim server URL |
164
+ | `TANDEM_TRANSPORT` | `http` | Transport mode (`http` or `stdio`) |
165
+ | `TANDEM_NO_SAMPLE` | unset | Set to `1` to skip auto-opening `sample/welcome.md` |
166
+ | `TANDEM_CLAUDE_CMD` | `claude` | Claude Code executable name (for `tandem setup` auto-detection) |
167
+
168
+ See `.env.example` for a copy-paste template.
169
+
170
+ ## Troubleshooting
171
+
172
+ Run `npm run doctor` for a quick diagnostic of your setup. It checks Node.js version, `.mcp.json` config, server health, and port status.
173
+
174
+ **Claude Code says "MCP failed to connect"**
175
+ Start the server first (`tandem` for global install, or `npm run dev:standalone` for dev setup), then open Claude Code. The server must be running before Claude Code probes the MCP URL. If you restart the server, run `/mcp` in Claude Code to reconnect.
176
+
177
+ **Port already in use**
178
+ Tandem kills stale processes on :3478/:3479 at startup. If another app uses those ports, set `TANDEM_PORT` / `TANDEM_MCP_PORT` to different values and update `TANDEM_URL` to match.
179
+
180
+ **Channel shim fails to start**
181
+ The `tandem-channel` entry spawns a subprocess. For global installs, `tandem setup` writes absolute paths to the bundled `dist/channel/index.js` — re-run `tandem setup` after upgrading. For dev setup, if you see `MODULE_NOT_FOUND` with a production config (`node dist/channel/index.js`), run `npm run build`. The default dev config uses `npx tsx` and doesn't require a build step.
182
+
183
+ **Browser shows "Cannot reach the Tandem server"**
184
+ The browser connects to the server via WebSocket. For global installs, run `tandem` to start the server. For dev setup, use `npm run dev:standalone` (or `npm run dev:server`). The message appears after 3 seconds of failed connection.
185
+
186
+ **Empty browser with no document**
187
+ On first run, `sample/welcome.md` auto-opens. If you've cleared sessions or deleted the sample file, click the **+** button in the tab bar or drop a file onto the editor.
188
+
189
+ ## Development
190
+
191
+ | Command | What it does |
192
+ |---------|-------------|
193
+ | `npm run dev:standalone` | **Recommended** — both frontend + backend (via concurrently) |
194
+ | `npm run dev:server` | Backend only: Hocuspocus (:3478) + MCP HTTP (:3479) |
195
+ | `npm run dev:client` | Frontend only: Vite dev server (:5173) |
196
+ | `npm run build` | Production build (`dist/server/` + `dist/channel/` + `dist/cli/` + `dist/client/`) |
197
+ | `npm test` | Run vitest (unit tests) |
198
+ | `npm run test:e2e` | Run Playwright E2E tests |
199
+ | `npm run test:e2e:ui` | Playwright UI mode |
200
+
201
+ **Tech Stack:** React 19, Tiptap, Vite, TypeScript | Node.js, Hocuspocus (Yjs WebSocket), MCP SDK, Express | Yjs (CRDT), y-prosemirror | mammoth.js (.docx), unified/remark (.md)
@@ -57,7 +57,7 @@ function formatEventContent(event) {
57
57
  case "selection:changed": {
58
58
  const { from, to, selectedText } = event.payload;
59
59
  if (!selectedText) return `User cleared selection${doc}`;
60
- return `User selected text (${from}-${to}): "${selectedText}"${doc}`;
60
+ return `User is pointing at text (${from}-${to}): "${selectedText}"${doc} \u2014 respond via tandem_reply`;
61
61
  }
62
62
  case "document:opened": {
63
63
  const { fileName, format } = event.payload;
@@ -91,13 +91,25 @@ function formatEventMeta(event) {
91
91
  case "chat:message":
92
92
  meta.message_id = event.payload.messageId;
93
93
  break;
94
+ case "selection:changed":
95
+ meta.respond_via = "tandem_reply";
96
+ break;
97
+ case "document:opened":
98
+ case "document:closed":
99
+ case "document:switched":
100
+ break;
101
+ default: {
102
+ const _exhaustive = event;
103
+ break;
104
+ }
94
105
  }
95
106
  return meta;
96
107
  }
97
108
 
98
109
  // src/channel/event-bridge.ts
99
110
  var AWARENESS_DEBOUNCE_MS = 500;
100
- var SELECTION_DEBOUNCE_MS = 1500;
111
+ var SELECTION_DEBOUNCE_MS = 300;
112
+ var MODE_CACHE_TTL_MS = 2e3;
101
113
  async function startEventBridge(mcp2, tandemUrl) {
102
114
  let retries = 0;
103
115
  let lastEventId;
@@ -239,6 +251,14 @@ async function connectAndStream(mcp2, tandemUrl, lastEventId, onEventId) {
239
251
  console.error("[Channel] Received invalid SSE event, skipping");
240
252
  continue;
241
253
  }
254
+ if (event.type !== "chat:message") {
255
+ const mode = await getCachedMode(tandemUrl);
256
+ if (mode === "solo") {
257
+ console.error(`[Channel] Solo mode: suppressed ${event.type} event`);
258
+ if (eventId) onEventId(eventId);
259
+ continue;
260
+ }
261
+ }
242
262
  if (event.type === "selection:changed") {
243
263
  if (eventId) onEventId(eventId);
244
264
  if (isSelectionCleared(event)) continue;
@@ -264,6 +284,29 @@ async function connectAndStream(mcp2, tandemUrl, lastEventId, onEventId) {
264
284
  }
265
285
  }
266
286
  }
287
+ var cachedMode = "tandem";
288
+ var cachedModeAt = 0;
289
+ async function getCachedMode(tandemUrl) {
290
+ const now = Date.now();
291
+ if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;
292
+ try {
293
+ const res = await fetch(`${tandemUrl}/api/mode`);
294
+ if (res.ok) {
295
+ const { mode } = await res.json();
296
+ cachedMode = mode;
297
+ } else {
298
+ console.error(`[Channel] Mode check returned ${res.status}, using cached: "${cachedMode}"`);
299
+ }
300
+ cachedModeAt = now;
301
+ } catch (err) {
302
+ console.error(
303
+ "[Channel] Mode check failed, delivering event (fail-open):",
304
+ err instanceof Error ? err.message : err
305
+ );
306
+ cachedModeAt = now;
307
+ }
308
+ return cachedMode;
309
+ }
267
310
 
268
311
  // src/channel/index.ts
269
312
  console.log = console.error;