tandem-editor 0.11.0 → 0.11.2
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/CHANGELOG.md +67 -37
- package/README.md +4 -0
- package/dist/channel/index.js +2 -0
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +4 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/CoworkSettings-DK3jjdwK.js +3 -0
- package/dist/client/assets/index-CfT503n4.js +297 -0
- package/dist/client/assets/index-DeJe09pn.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/monitor/index.js +2 -0
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +24 -8
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/CoworkSettings-ChE5WuAe.js +0 -3
- package/dist/client/assets/index-BJKuWd_k.css +0 -1
- package/dist/client/assets/index-vu_QxvyU.js +0 -310
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/win-path-guard.ts","../../src/cli/uninstall-scrub.ts","../../src/shared/constants.ts","../../src/cli/skill-content.ts","../../src/cli/setup.ts","../../src/shared/cli-runtime.ts","../../src/cli/preflight.ts","../../src/cli/mcp-stdio.ts","../../src/shared/fetch-with-timeout.ts","../../src/shared/utils.ts","../../src/shared/events/types.ts","../../src/channel/event-bridge.ts","../../src/channel/run.ts","../../src/cli/channel.ts","../../src/shared/auth/token-file.ts","../../src/cli/rotate-token.ts","../../src/cli/start.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Windows workspace path guard — mirrors the Rust §3 invariant for TypeScript callers.\n *\n * Four steps (in order):\n * a. lstat each ancestor; reject any path whose chain contains a symlink.\n * b. fs.realpath() to canonicalize (safe: symlinks already rejected in (a)).\n * c. Reject UNC paths (\\\\server\\share or \\\\?\\UNC\\...; allow \\\\?\\C:\\...).\n * d. Component-wise containment check under realpath'd %LOCALAPPDATA%\n * (case-insensitive on Windows).\n *\n * Extracted into its own module so it can be unit-tested via vi.mock(\"node:fs\", ...).\n */\n\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\ntype Logger = { warn: (msg: string) => void };\n\n/**\n * Validate that `candidate` is a safe workspace path contained within `realLocalAppData`.\n *\n * @returns the realpath'd canonical path string on success, or null if rejected.\n * Callers are responsible for supplying a realpath'd `realLocalAppData`.\n */\nexport async function assertSafeWorkspacePath(\n candidate: string,\n realLocalAppData: string,\n logger?: Logger,\n): Promise<string | null> {\n const warn = (msg: string) => logger?.warn(`[path-guard] ${msg}`);\n\n // (a) lstat-walk: reject any component that is a symlink.\n if (await hasSymlinkInChain(candidate, warn)) {\n warn(`symlink/reparse point in chain: ${candidate}`);\n return null;\n }\n\n // (b) Canonicalize via realpath (safe now — symlinks already rejected).\n let real: string;\n try {\n real = await fs.realpath(candidate);\n } catch (err) {\n warn(`realpath failed for ${candidate}: ${(err as Error).message}`);\n return null;\n }\n\n // (c) Reject UNC paths.\n if (isUncPath(real)) {\n warn(`UNC path rejected: ${real}`);\n return null;\n }\n\n // (d) Component-wise containment under realLocalAppData (case-insensitive).\n if (!isComponentWiseChild(real, realLocalAppData)) {\n warn(`path outside %LOCALAPPDATA%: ${real}`);\n return null;\n }\n\n return real;\n}\n\n/** Returns true if any ancestor (inclusive) of `p` is a symbolic link. */\nasync function hasSymlinkInChain(p: string, warn: (m: string) => void): Promise<boolean> {\n // Walk from candidate up through all ancestors.\n let current = path.resolve(p);\n const visited = new Set<string>();\n\n while (true) {\n if (visited.has(current)) break;\n visited.add(current);\n\n try {\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) {\n return true;\n }\n } catch (err) {\n // lstat failed — fail closed for safety.\n warn(`lstat failed for ${current}: ${(err as Error).message}`);\n return true;\n }\n\n const parent = path.dirname(current);\n if (parent === current) break; // reached root\n current = parent;\n }\n\n return false;\n}\n\n/** Returns true if the path is a UNC path (\\\\server\\share or \\\\?\\UNC\\...). */\nfunction isUncPath(p: string): boolean {\n // Allow extended-length local paths (\\\\?\\C:\\...) but reject:\n // \\\\?\\UNC\\server\\share — extended UNC\n // \\\\server\\share — classic UNC\n if (p.startsWith(\"\\\\\\\\?\\\\UNC\\\\\") || p.startsWith(\"//?/UNC/\")) return true;\n if (\n (p.startsWith(\"\\\\\\\\\") && !p.startsWith(\"\\\\\\\\?\\\\\")) ||\n (p.startsWith(\"//\") && !p.startsWith(\"//?/\"))\n )\n return true;\n return false;\n}\n\n/**\n * Returns true if `child` is strictly within `root` on a component-wise basis\n * (case-insensitive on Windows).\n */\nfunction isComponentWiseChild(child: string, root: string): boolean {\n // Normalize separators and split on path.sep.\n const normalize = (p: string) => p.replace(/[\\\\/]+/g, path.sep).replace(/[/\\\\]$/, \"\");\n\n const rootNorm = normalize(root);\n const childNorm = normalize(child);\n\n const rootParts = rootNorm.split(path.sep);\n const childParts = childNorm.split(path.sep);\n\n if (childParts.length <= rootParts.length) return false;\n\n for (let i = 0; i < rootParts.length; i++) {\n // Case-insensitive on Windows.\n if (rootParts[i].toLowerCase() !== childParts[i].toLowerCase()) return false;\n }\n return true;\n}\n","/**\n * Tandem `--uninstall-scrub` subcommand.\n *\n * Invoked by the Tauri NSIS installer hook during uninstall. Walks every\n * Cowork workspace under `%LOCALAPPDATA%\\Packages\\Claude_*\\LocalCache\\\n * Roaming\\Claude\\local-agent-mode-sessions\\` and removes the Tandem plugin\n * entry from:\n * - `installed_plugins.json` (remove `mcpServers.tandem`)\n * - `known_marketplaces.json` (remove `marketplaces.tandem`)\n * - `cowork_settings.json` (remove `tandem@tandem` from `enabledPlugins`)\n *\n * Then removes the `Tandem Cowork*` Windows Firewall rules via `netsh`.\n *\n * **Security invariant §10 (ADR):** this runs INSIDE the already-signed\n * `tandem.exe` binary — NOT as a separate `uninstall_scrub.exe`. That\n * prevents binary-planting attacks during uninstall.\n *\n * **Failure policy:** logs every error, exits 0 on clean-or-not-installed,\n * non-zero only on unrecoverable I/O failures. NSIS logs the exit code but\n * does NOT block uninstall — Tandem must always uninstall even if a\n * workspace scrub partially fails.\n *\n * **Token safety:** this scrub READS JSON to find Tandem entries but the\n * removed-entry contents (including the auth token) are NEVER logged.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promises as fsPromises } from \"node:fs\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { assertSafeWorkspacePath } from \"./win-path-guard.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst TANDEM_PLUGIN_ID = \"tandem\";\nconst TANDEM_ENABLED_KEY = \"tandem@tandem\";\nconst FIREWALL_ALLOW_RULE = \"Tandem Cowork\";\nconst FIREWALL_DENY_RULE = \"Tandem Cowork \\u2014 Deny (elevation refused)\";\n\ntype ScrubLogger = {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n close: () => Promise<void>;\n};\n\nasync function openLogger(): Promise<ScrubLogger> {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) {\n // No log file — just use stderr.\n const write = (level: string, msg: string): void => {\n process.stderr.write(`[tandem uninstall-scrub ${level}] ${msg}\\n`);\n };\n return {\n info: (m) => write(\"info\", m),\n warn: (m) => write(\"warn\", m),\n error: (m) => write(\"error\", m),\n close: async () => {},\n };\n }\n\n const logDir = path.join(localAppData, \"tandem\", \"Logs\");\n await fsPromises.mkdir(logDir, { recursive: true }).catch(() => {});\n const logPath = path.join(logDir, \"uninstall.log\");\n const stream = await fsPromises.open(logPath, \"a\").catch(() => null);\n\n const write = (level: string, msg: string): void => {\n const line = `[${new Date().toISOString()}] [${level}] ${msg}\\n`;\n process.stderr.write(line);\n if (stream) {\n stream.write(line).catch(() => {});\n }\n };\n\n return {\n info: (m) => write(\"info\", m),\n warn: (m) => write(\"warn\", m),\n error: (m) => write(\"error\", m),\n close: async () => {\n if (stream) await stream.close().catch(() => {});\n },\n };\n}\n\n/**\n * Find all Cowork workspace directories.\n *\n * Matches `%LOCALAPPDATA%\\Packages\\Claude_*\\LocalCache\\Roaming\\Claude\\\n * local-agent-mode-sessions\\<ws-id>\\<vm-id>\\`.\n *\n * Each candidate vm-path is validated by the 4-step Windows path guard before\n * being included — callers receive only safe, realpath'd paths.\n *\n * Returns an empty array on any error (e.g. Cowork not installed).\n */\nexport async function findCoworkWorkspaces(logger: ScrubLogger): Promise<string[]> {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) {\n logger.info(\"%LOCALAPPDATA% not set — skipping workspace scan\");\n return [];\n }\n\n // Realpath %LOCALAPPDATA% once for use by the path guard.\n let realLad: string;\n try {\n realLad = await fsPromises.realpath(localAppData);\n } catch {\n realLad = localAppData; // best-effort fallback\n }\n\n const packagesDir = path.join(localAppData, \"Packages\");\n let packageEntries: string[];\n try {\n packageEntries = await fsPromises.readdir(packagesDir);\n } catch (err) {\n logger.info(`cannot read Packages dir: ${(err as Error).message}`);\n return [];\n }\n\n const claudePackages = packageEntries.filter((name) => name.startsWith(\"Claude_\"));\n if (claudePackages.length === 0) {\n logger.info(\"no Claude_* package directories found\");\n return [];\n }\n\n const workspaces: string[] = [];\n for (const pkg of claudePackages) {\n const sessionsRoot = path.join(\n packagesDir,\n pkg,\n \"LocalCache\",\n \"Roaming\",\n \"Claude\",\n \"local-agent-mode-sessions\",\n );\n\n let wsEntries: string[];\n try {\n wsEntries = await fsPromises.readdir(sessionsRoot);\n } catch (err) {\n logger.warn(`cannot read sessions root ${sessionsRoot}: ${(err as Error).message}`);\n continue;\n }\n\n for (const ws of wsEntries) {\n const wsPath = path.join(sessionsRoot, ws);\n let vmEntries: string[];\n try {\n vmEntries = await fsPromises.readdir(wsPath);\n } catch (err) {\n logger.warn(`cannot read workspace dir ${wsPath}: ${(err as Error).message}`);\n continue;\n }\n\n for (const vm of vmEntries) {\n const vmPath = path.join(wsPath, vm);\n try {\n const stat = await fsPromises.stat(vmPath);\n if (!stat.isDirectory()) continue;\n\n // 4-step path guard — only include validated, realpath'd paths.\n const safePath = await assertSafeWorkspacePath(vmPath, realLad, logger);\n if (safePath !== null) {\n workspaces.push(safePath);\n }\n } catch (err) {\n logger.warn(`cannot stat ${vmPath}: ${(err as Error).message}`);\n }\n }\n }\n }\n\n logger.info(`found ${workspaces.length} workspace(s)`);\n return workspaces;\n}\n\n/**\n * Atomically rewrite a JSON file, invoking `mutate` on the parsed object.\n *\n * Precondition: `filePath` has already been validated by the path guard.\n * This function trusts its callers (consistent with `with_locked_json` on the Rust side).\n *\n * Returns true if the mutation changed something and was written, false if\n * the file was absent or unchanged.\n */\nexport async function rewriteJson(\n filePath: string,\n mutate: (obj: Record<string, unknown>) => boolean,\n logger: ScrubLogger,\n): Promise<boolean> {\n let content: string;\n try {\n content = await fsPromises.readFile(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n logger.warn(`cannot read ${filePath}: ${(err as Error).message}`);\n return false;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch (err) {\n logger.warn(`invalid JSON in ${filePath}: ${(err as Error).message}`);\n return false;\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n logger.warn(`${filePath} is not a JSON object — skipping`);\n return false;\n }\n\n const changed = mutate(parsed as Record<string, unknown>);\n if (!changed) {\n return false;\n }\n\n const dir = path.dirname(filePath);\n const tmpName = `.tandem-scrub-tmp-${Math.random().toString(36).slice(2, 10)}`;\n const tmpPath = path.join(dir, tmpName);\n\n try {\n await fsPromises.writeFile(tmpPath, JSON.stringify(parsed, null, 2), \"utf8\");\n await fsPromises.rename(tmpPath, filePath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => {});\n throw err;\n }\n return true;\n}\n\n/**\n * Remove the Tandem entry from `installed_plugins.json`.\n */\nexport function removeInstalledPlugins(obj: Record<string, unknown>): boolean {\n let changed = false;\n for (const key of [\"mcpServers\", \"servers\"]) {\n const servers = obj[key];\n if (typeof servers === \"object\" && servers !== null && !Array.isArray(servers)) {\n const map = servers as Record<string, unknown>;\n if (TANDEM_PLUGIN_ID in map) {\n delete map[TANDEM_PLUGIN_ID];\n changed = true;\n }\n }\n }\n return changed;\n}\n\n/**\n * Remove the Tandem marketplace entry from `known_marketplaces.json`.\n */\nexport function removeKnownMarketplaces(obj: Record<string, unknown>): boolean {\n const mp = obj.marketplaces;\n if (typeof mp === \"object\" && mp !== null && !Array.isArray(mp)) {\n const map = mp as Record<string, unknown>;\n if (TANDEM_PLUGIN_ID in map) {\n delete map[TANDEM_PLUGIN_ID];\n return true;\n }\n }\n return false;\n}\n\n/**\n * Remove `tandem@tandem` from `enabledPlugins` in `cowork_settings.json`.\n */\nexport function removeCoworkSettings(obj: Record<string, unknown>): boolean {\n const enabled = obj.enabledPlugins;\n if (Array.isArray(enabled)) {\n const before = enabled.length;\n obj.enabledPlugins = enabled.filter((v) => v !== TANDEM_ENABLED_KEY);\n return (obj.enabledPlugins as unknown[]).length < before;\n }\n if (typeof enabled === \"object\" && enabled !== null) {\n const map = enabled as Record<string, unknown>;\n if (TANDEM_ENABLED_KEY in map) {\n delete map[TANDEM_ENABLED_KEY];\n return true;\n }\n }\n return false;\n}\n\n/**\n * Delete a Windows Firewall rule by name. Non-existent rules are not an error.\n */\nasync function deleteFirewallRule(name: string, logger: ScrubLogger): Promise<void> {\n try {\n await execFileAsync(\"netsh\", [\"advfirewall\", \"firewall\", \"delete\", \"rule\", `name=${name}`]);\n logger.info(`deleted firewall rule: ${name}`);\n } catch (err) {\n const e = err as NodeJS.ErrnoException & { stdout?: string; stderr?: string };\n // netsh returns exit code 1 when no rule matches — not fatal.\n const stdoutStr = e.stdout ?? \"\";\n if (stdoutStr.includes(\"No rules match\")) {\n logger.info(`no firewall rule to delete: ${name}`);\n return;\n }\n logger.warn(\n `failed to delete firewall rule ${name}: ${e.message ?? String(err)} ` +\n `(stdout: ${stdoutStr.trim().slice(0, 200)})`,\n );\n }\n}\n\n/**\n * Main entry — Windows-only.\n */\nexport async function runUninstallScrub(): Promise<number> {\n const logger = await openLogger();\n\n logger.info(\"Tandem uninstall scrub starting\");\n\n if (process.platform !== \"win32\") {\n logger.info(`platform ${process.platform} is not win32 — skipping Cowork scrub`);\n await logger.close();\n return 0;\n }\n\n let failures = 0;\n\n try {\n const workspaces = await findCoworkWorkspaces(logger);\n for (const ws of workspaces) {\n const pluginsDir = path.join(ws, \"cowork_plugins\");\n try {\n await rewriteJson(\n path.join(pluginsDir, \"installed_plugins.json\"),\n removeInstalledPlugins,\n logger,\n );\n await rewriteJson(\n path.join(pluginsDir, \"known_marketplaces.json\"),\n removeKnownMarketplaces,\n logger,\n );\n await rewriteJson(\n path.join(pluginsDir, \"cowork_settings.json\"),\n removeCoworkSettings,\n logger,\n );\n } catch (err) {\n logger.error(`scrub failed for ${ws}: ${(err as Error).message}`);\n failures++;\n }\n }\n\n await deleteFirewallRule(FIREWALL_ALLOW_RULE, logger);\n await deleteFirewallRule(FIREWALL_DENY_RULE, logger);\n\n logger.info(`scrub complete: ${workspaces.length} workspace(s), ${failures} failure(s)`);\n } catch (err) {\n logger.error(`scrub fatal error: ${(err as Error).message}`);\n failures++;\n }\n\n await logger.close();\n\n // Exit 0 on clean-or-not-installed. Non-zero only on unrecoverable failures —\n // and even then, NSIS does NOT block uninstall; it just records the code.\n return failures > 0 ? 1 : 0;\n}\n","export const DEFAULT_WS_PORT = 3478;\nexport const DEFAULT_MCP_PORT = 3479;\n\n/** File extensions the server accepts for opening. */\nexport const SUPPORTED_EXTENSIONS = new Set([\".md\", \".txt\", \".html\", \".htm\", \".docx\"]);\nexport const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB\nexport const MAX_WS_PAYLOAD = 10 * 1024 * 1024; // 10MB\nexport const MAX_WS_CONNECTIONS = 4;\nexport const IDLE_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nexport const SESSION_MAX_AGE = 30 * 24 * 60 * 60 * 1000; // 30 days\nexport const TYPING_DEBOUNCE = 3000; // 3 seconds\nexport const DISCONNECT_DEBOUNCE_MS = 3000; // 3 seconds before showing \"server not reachable\"\nexport const PROLONGED_DISCONNECT_MS = 30_000; // 30 seconds before showing App-level disconnect banner\nexport const OVERLAY_STALE_DEBOUNCE = 200; // 200ms\n\nimport type { HighlightColor } from \"./types.js\";\n\nexport const HIGHLIGHT_COLORS: Record<HighlightColor, string> = {\n yellow: \"rgba(255, 235, 59, 0.3)\",\n green: \"rgba(76, 175, 80, 0.3)\",\n blue: \"rgba(33, 150, 243, 0.3)\",\n pink: \"rgba(236, 72, 153, 0.3)\",\n};\n\nexport const HIGHLIGHT_COLOR_VARS: Record<HighlightColor, string> = {\n yellow: \"var(--tandem-highlight-yellow)\",\n green: \"var(--tandem-highlight-green)\",\n blue: \"var(--tandem-highlight-blue)\",\n pink: \"var(--tandem-highlight-pink)\",\n};\n\nexport function normalizeHighlightColor(color: string | null | undefined): HighlightColor {\n return color && color in HIGHLIGHT_COLORS ? (color as HighlightColor) : \"yellow\";\n}\n\nexport const TANDEM_MODE_DEFAULT = \"tandem\" as const;\nexport const TANDEM_MODE_KEY = \"tandem:mode\";\nexport const TANDEM_SETTINGS_KEY = \"tandem:settings\";\n// Panel-width localStorage keys.\n//\n// NOTE: these use legacy hyphen naming (vs the neighboring colon convention\n// `tandem:mode`/`tandem:settings`) because they predate the colon scheme and\n// changing the strings would invalidate every existing user's saved widths.\n// Do not \"fix\" the style — the key string is the persistence contract.\n//\n// Right-side panel width is shared between the tabbed layout and the\n// three-panel right panel. The left key only applies in three-panel mode.\nexport const PANEL_WIDTH_KEY = \"tandem-panel-width\";\nexport const LEFT_PANEL_WIDTH_KEY = \"tandem-left-panel-width\";\n\nexport type PanelSide = \"left\" | \"right\";\n\n/**\n * Maps a panel side to its localStorage key. Using a Record instead of two\n * bare constants makes the \"both handles write to the same key\" regression\n * (#228) structurally impossible — you can't accidentally map both sides to\n * the same value at a callsite.\n *\n * Uses `as const satisfies Record<PanelSide, string>` so the value type stays\n * as the literal strings rather than widening to `string` — this preserves\n * the persistence-key identity at every callsite while still enforcing\n * exhaustive coverage of `PanelSide`.\n */\nexport const PANEL_WIDTH_KEYS = {\n left: LEFT_PANEL_WIDTH_KEY,\n right: PANEL_WIDTH_KEY,\n} as const satisfies Record<PanelSide, string>;\nexport const SELECTION_DWELL_DEFAULT_MS = 1000;\nexport const SELECTION_DWELL_MIN_MS = 500;\nexport const SELECTION_DWELL_MAX_MS = 3000;\n\n// Large file thresholds\nexport const CHARS_PER_PAGE = 3_000;\nexport const LARGE_FILE_PAGE_THRESHOLD = 50;\nexport const VERY_LARGE_FILE_PAGE_THRESHOLD = 100;\n\nexport const CTRL_ROOM = \"__tandem_ctrl__\";\n\n/** Y.Map key constants — centralized to prevent silent bugs from string typos. */\nexport const Y_MAP_ANNOTATIONS = \"annotations\";\nexport const Y_MAP_AWARENESS = \"awareness\";\nexport const Y_MAP_USER_AWARENESS = \"userAwareness\";\nexport const Y_MAP_MODE = \"mode\";\nexport const Y_MAP_DWELL_MS = \"selectionDwellMs\";\nexport const Y_MAP_CHAT = \"chat\";\nexport const Y_MAP_DOCUMENT_META = \"documentMeta\";\nexport const Y_MAP_ANNOTATION_REPLIES = \"annotationReplies\";\nexport const Y_MAP_SAVED_AT_VERSION = \"savedAtVersion\";\nexport const Y_MAP_AUTHORSHIP = \"authorship\";\n// Y.Map sub-keys: userAwareness\nexport const Y_MAP_SELECTION = \"selection\";\nexport const Y_MAP_ACTIVITY = \"activity\";\n// Y.Map sub-keys: awareness (Claude focus)\nexport const Y_MAP_CLAUDE = \"claude\";\n// Y.Map sub-keys: documentMeta\nexport const Y_MAP_OPEN_DOCUMENTS = \"openDocuments\";\nexport const Y_MAP_ACTIVE_DOCUMENT_ID = \"activeDocumentId\";\nexport const Y_MAP_GENERATION_ID = \"generationId\";\nexport const Y_MAP_READ_ONLY = \"readOnly\";\nexport const Y_MAP_STORE_READ_ONLY = \"storeReadOnly\";\n\nexport const AUTHORSHIP_TOGGLE_KEY = \"tandem:showAuthorship\";\n\nexport const SERVER_INFO_DIR = \".tandem\";\nexport const SERVER_INFO_FILE = \".tandem/.server-info\";\n\nexport const RECENT_FILES_KEY = \"tandem:recentFiles\";\nexport const RECENT_FILES_CAP = 20;\n\nexport const USER_NAME_KEY = \"tandem:userName\";\nexport const USER_NAME_DEFAULT = \"You\";\nexport const USER_NAME_EVENT = \"tandem:user-name-changed\";\nexport const USER_NAME_MAX_LEN = 40;\n\n// Toast notifications\nexport const TOAST_DISMISS_MS = { error: 8000, warning: 6000, info: 4000 } as const;\nexport const MAX_VISIBLE_TOASTS = 5;\nexport const NOTIFICATION_BUFFER_SIZE = 50;\n\n// Onboarding tutorial\nexport const TUTORIAL_COMPLETED_KEY = \"tandem:tutorialCompleted\";\nexport const TUTORIAL_ANNOTATION_PREFIX = \"tutorial-\";\n/** Persists \"user skipped the Cowork onboarding step\" across sessions. */\nexport const COWORK_ONBOARDING_SKIPPED_KEY = \"tandem:coworkOnboardingSkipped\";\n/** Polling interval for `cowork_get_status` while the consumer is active. */\nexport const COWORK_STATUS_POLL_MS = 30_000;\n/** Debounce interval for the \"Re-scan workspaces\" button. */\nexport const COWORK_RESCAN_DEBOUNCE_MS = 2_000;\n\n// Editor layout\nexport const EDITOR_WIDTH_MODE_KEY = \"tandem:editorWidthMode\";\n\n// Channel / event queue\nexport const CHANNEL_EVENT_BUFFER_SIZE = 200;\nexport const CHANNEL_EVENT_BUFFER_AGE_MS = 60_000; // 60 seconds\nexport const CHANNEL_SSE_KEEPALIVE_MS = 15_000; // 15 seconds\nexport const CHANNEL_MAX_RETRIES = 5;\nexport const CHANNEL_RETRY_DELAY_MS = 2_000;\n\n// Channel shim per-request timeouts. Mirror the monitor pattern (#364) so a\n// half-open Tandem server can't wedge `tandem_reply`, the permission relay,\n// or the event-bridge SSE handshake / awareness / mode / error-report POSTs.\n// Intentionally separate constants per endpoint so a slow endpoint doesn't\n// hold up a faster one — and so log lines name a meaningful threshold.\nexport const CHANNEL_CONNECT_FETCH_TIMEOUT_MS = 10_000; // /api/events handshake\nexport const CHANNEL_SSE_INACTIVITY_TIMEOUT_MS = 60_000; // No-bytes watchdog on SSE body\nexport const CHANNEL_MODE_FETCH_TIMEOUT_MS = 2_000; // /api/mode cache refresh\nexport const CHANNEL_AWARENESS_FETCH_TIMEOUT_MS = 5_000; // /api/channel-awareness POST\nexport const CHANNEL_ERROR_REPORT_TIMEOUT_MS = 3_000; // /api/channel-error POST on exit\nexport const CHANNEL_REPLY_FETCH_TIMEOUT_MS = 5_000; // /api/channel-reply (tandem_reply)\nexport const CHANNEL_PERMISSION_FETCH_TIMEOUT_MS = 5_000; // /api/channel-permission relay\n// Bound the SSE buffer so a misbehaving server that never emits frame\n// boundaries can't wedge the bridge with unbounded string growth.\nexport const CHANNEL_MAX_SSE_BUFFER_BYTES = 1_000_000;\n\n/** Auth token filename inside the app-data directory. */\nexport const TOKEN_FILE_NAME = \"auth-token\";\n\n/** Default MCP bind host — loopback only by default. */\nexport const DEFAULT_BIND_HOST = \"127.0.0.1\";\n\n/** Env var name to opt in to unauthenticated LAN binding. */\nexport const TANDEM_ALLOW_UNAUTHENTICATED_LAN_ENV = \"TANDEM_ALLOW_UNAUTHENTICATED_LAN\";\n\n/** Tauri WebView origin hostname — must be accepted alongside localhost. */\nexport const TAURI_HOSTNAME = \"tauri.localhost\";\n\n// Zoom persistence (Tauri desktop)\nexport const ZOOM_STORAGE_KEY = \"tandem:zoomLevel\";\nexport const ZOOM_MIN = 0.5;\nexport const ZOOM_MAX = 2.0;\nexport const ZOOM_DEFAULT = 1.0;\n","/**\n * SKILL.md content installed to ~/.claude/skills/tandem/ by `tandem setup`.\n * Single source of truth lives at `skills/tandem/SKILL.md`. This module\n * reads that file at module load so the plugin install path and the\n * `tandem setup` install path always deliver byte-identical content.\n *\n * The file is shipped via package.json `files: [\"skills/\", ...]`, and the\n * CLI entry (dist/cli/index.js) is not self-contained — so at runtime the\n * relative path `../../skills/tandem/SKILL.md` resolves from either\n * dist/cli/ (tsx dev) or dist/cli/ (npm install) to the package-root\n * `skills/tandem/SKILL.md`.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst SKILL_PATH = resolve(__dirname, \"../../skills/tandem/SKILL.md\");\n\nexport const SKILL_CONTENT = readFileSync(SKILL_PATH, \"utf-8\");\n","import { randomUUID } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { copyFile, mkdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_MCP_PORT } from \"../shared/constants.js\";\nimport { SKILL_CONTENT } from \"./skill-content.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Paths are anchored to the package root (dist/cli/ resolves up two levels).\nconst PACKAGE_ROOT = resolve(__dirname, \"../..\");\nconst CHANNEL_DIST = resolve(PACKAGE_ROOT, \"dist/channel/index.js\");\n\nconst MCP_URL = `http://localhost:${DEFAULT_MCP_PORT}`;\n\nexport interface McpEntry {\n type?: \"http\";\n url?: string;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n headers?: Record<string, string>;\n}\n\nexport interface McpEntries {\n tandem: McpEntry;\n \"tandem-channel\"?: McpEntry;\n}\n\nexport interface BuildMcpEntriesOptions {\n /** Include the legacy stdio channel shim. Defaults to false — the plugin\n * monitor handles event push for modern installs. Users on older setups\n * can run `tandem setup --with-channel-shim` to preserve the shim. */\n withChannelShim?: boolean;\n nodeBinary?: string;\n /** Auth token to embed in HTTP entry headers and stdio shim env.\n * When omitted (first-run before token provisioned), headers/env are omitted\n * and backward compatibility is preserved. */\n token?: string;\n /** Target kind controls entry shape. Claude Code uses HTTP (direct);\n * Claude Desktop uses stdio (npx bridge) because Cowork sessions can\n * only surface stdio MCP servers. */\n targetKind?: TargetKind;\n}\n\nexport function buildMcpEntries(\n channelPath: string,\n opts: BuildMcpEntriesOptions = {},\n): McpEntries {\n const isDesktop = opts.targetKind === \"claude-desktop\";\n\n let tandemEntry: McpEntry;\n if (isDesktop) {\n const env: Record<string, string> = { TANDEM_URL: MCP_URL };\n if (opts.token) {\n env.TANDEM_AUTH_TOKEN = opts.token;\n }\n tandemEntry = {\n command: \"npx\",\n args: [\"-y\", \"tandem-editor\", \"mcp-stdio\"],\n env,\n };\n } else {\n tandemEntry = { type: \"http\", url: `${MCP_URL}/mcp` };\n if (opts.token) {\n tandemEntry.headers = { Authorization: `Bearer ${opts.token}` };\n }\n }\n const entries: McpEntries = { tandem: tandemEntry };\n\n if (opts.withChannelShim) {\n const shimEnv: Record<string, string> = { TANDEM_URL: MCP_URL };\n if (opts.token) {\n shimEnv.TANDEM_AUTH_TOKEN = opts.token;\n }\n entries[\"tandem-channel\"] = {\n command: opts.nodeBinary ?? \"node\",\n args: [channelPath],\n env: shimEnv,\n };\n }\n return entries;\n}\n\nexport type TargetKind = \"claude-code\" | \"claude-desktop\";\n\nexport interface DetectedTarget {\n label: string;\n configPath: string;\n kind: TargetKind;\n}\n\nexport interface DetectOptions {\n homeOverride?: string;\n localAppDataOverride?: string;\n force?: boolean;\n}\n\nexport function detectTargets(opts: DetectOptions = {}): DetectedTarget[] {\n const home = opts.homeOverride ?? homedir();\n const targets: DetectedTarget[] = [];\n\n // Claude Code — cross-platform.\n // MCP servers are configured in ~/.claude.json under the \"mcpServers\" key.\n // Detect if the file exists OR if ~/.claude directory exists (Claude Code is installed).\n // With --force, always include regardless.\n const claudeCodeConfig = join(home, \".claude.json\");\n const claudeCodeDir = join(home, \".claude\");\n if (opts.force || existsSync(claudeCodeConfig) || existsSync(claudeCodeDir)) {\n targets.push({ label: \"Claude Code\", configPath: claudeCodeConfig, kind: \"claude-code\" });\n }\n\n // Claude Desktop — platform-specific.\n // Only detect if the config file already exists (user has launched Desktop at least once).\n // With --force, always include.\n let desktopConfig: string | null = null;\n if (process.platform === \"win32\") {\n const appdata = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n desktopConfig = join(appdata, \"Claude\", \"claude_desktop_config.json\");\n } else if (process.platform === \"darwin\") {\n desktopConfig = join(\n home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n } else {\n desktopConfig = join(home, \".config\", \"claude\", \"claude_desktop_config.json\");\n }\n\n if (desktopConfig && (opts.force || existsSync(desktopConfig))) {\n targets.push({ label: \"Claude Desktop\", configPath: desktopConfig, kind: \"claude-desktop\" });\n }\n\n // Claude Desktop (MSIX) — Windows only.\n // MSIX-packaged installs (Microsoft Store) redirect %APPDATA% to a per-package\n // LocalCache dir. The config lives under %LOCALAPPDATA%\\Packages\\Claude_*\\\n // LocalCache\\Roaming\\Claude\\. Multiple package families may exist.\n if (process.platform === \"win32\") {\n const localAppData =\n opts.localAppDataOverride ?? process.env.LOCALAPPDATA ?? join(home, \"AppData\", \"Local\");\n const packagesDir = join(localAppData, \"Packages\");\n try {\n const entries = readdirSync(packagesDir);\n for (const pkg of entries.filter((n) => n.startsWith(\"Claude_\"))) {\n const msixConfig = join(\n packagesDir,\n pkg,\n \"LocalCache\",\n \"Roaming\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n if (opts.force || existsSync(msixConfig)) {\n const suffix =\n entries.filter((n) => n.startsWith(\"Claude_\")).length > 1\n ? ` (${pkg.slice(0, 12)}…)`\n : \"\";\n targets.push({\n label: `Claude Desktop MSIX${suffix}`,\n configPath: msixConfig,\n kind: \"claude-desktop\",\n });\n }\n }\n } catch {\n // %LOCALAPPDATA%\\Packages doesn't exist or isn't readable — not an MSIX install\n }\n }\n\n return targets;\n}\n\n/**\n * Atomic write: write to a temp file in the SAME directory as the destination,\n * then rename. Using the same directory avoids EXDEV errors on Windows when\n * %TEMP% and %APPDATA% are on different drives.\n */\nasync function atomicWrite(content: string, dest: string): Promise<void> {\n const tmp = join(dirname(dest), `.tandem-setup-${randomUUID()}.tmp`);\n await writeFile(tmp, content, \"utf-8\");\n try {\n await rename(tmp, dest);\n } catch (err) {\n // EXDEV: cross-device link — fall back to copy + delete\n if ((err as NodeJS.ErrnoException).code === \"EXDEV\") {\n await copyFile(tmp, dest);\n await unlink(tmp).catch((cleanupErr: Error) => {\n console.error(` Warning: could not remove temp file ${tmp}: ${cleanupErr.message}`);\n });\n } else {\n await unlink(tmp).catch((cleanupErr: Error) => {\n console.error(` Warning: could not remove temp file ${tmp}: ${cleanupErr.message}`);\n });\n throw err;\n }\n }\n}\n\nexport async function applyConfig(configPath: string, entries: McpEntries): Promise<void> {\n // Read existing config or start fresh — no existsSync guard needed.\n // ENOENT and malformed JSON start fresh; other errors (permissions, disk) propagate.\n let existing: { mcpServers?: Record<string, McpEntry> } = {};\n try {\n existing = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n // File doesn't exist yet — start fresh\n } else if (err instanceof SyntaxError) {\n // Don't silently wipe the user's other mcpServers. Copy the malformed\n // file to a .broken-<ts> sibling first so they can recover it. If the\n // backup itself fails, refuse to overwrite — runSetup's per-target\n // try/catch reports the partial failure.\n const backupPath = `${configPath}.broken-${Date.now()}`;\n try {\n await copyFile(configPath, backupPath);\n console.error(\n ` Warning: ${configPath} contains malformed JSON — backed up to ${basename(backupPath)}, replacing with fresh config`,\n );\n } catch (copyErr) {\n console.error(\n ` Warning: ${configPath} contains malformed JSON and backup failed (${\n copyErr instanceof Error ? copyErr.message : copyErr\n }) — refusing to overwrite. Fix the JSON manually and rerun 'tandem setup'.`,\n );\n throw copyErr;\n }\n } else {\n throw err; // Permission errors, disk errors, etc. should not be silently swallowed\n }\n }\n\n const merged = {\n ...(existing.mcpServers ?? {}),\n ...entries,\n };\n // Remove stale tandem-channel entry left by older Tauri installers.\n // The channel shim is Claude Code-only; Cowork can't use it.\n if (!entries[\"tandem-channel\"]) {\n if (merged[\"tandem-channel\"]) {\n console.error(\n ` Warning: removed stale tandem-channel entry from ${configPath} (legacy Tauri install artifact)`,\n );\n }\n delete merged[\"tandem-channel\"];\n }\n const updated = { ...existing, mcpServers: merged };\n\n await mkdir(dirname(configPath), { recursive: true });\n await atomicWrite(JSON.stringify(updated, null, 2) + \"\\n\", configPath);\n}\n\n/**\n * Install the Tandem skill to ~/.claude/skills/tandem/SKILL.md.\n * Claude Code auto-discovers skills in this directory and uses the description\n * field to trigger them when tandem_* tools are present.\n */\nexport async function installSkill(opts: { homeOverride?: string } = {}): Promise<void> {\n const home = opts.homeOverride ?? homedir();\n const skillPath = join(home, \".claude\", \"skills\", \"tandem\", \"SKILL.md\");\n await mkdir(dirname(skillPath), { recursive: true });\n await atomicWrite(SKILL_CONTENT, skillPath);\n}\n\n/**\n * Returns true if the channel-shim build artifact exists at the given path.\n * Exported so the prereq check can be tested without spawning runSetup.\n */\nexport function validateChannelShimPrereq(channelPath: string): boolean {\n return existsSync(channelPath);\n}\n\n/**\n * Write the given token into all detected Claude MCP config files.\n * Returns the number of configs successfully updated and any per-target errors.\n */\nexport async function applyConfigWithToken(\n token: string | null,\n opts: { force?: boolean; withChannelShim?: boolean } = {},\n): Promise<{ updated: number; errors: string[] }> {\n const targets = detectTargets({ force: opts.force });\n\n let updated = 0;\n const errors: string[] = [];\n for (const t of targets) {\n const entries = buildMcpEntries(CHANNEL_DIST, {\n withChannelShim: opts.withChannelShim,\n token: token ?? undefined,\n targetKind: t.kind,\n });\n try {\n await applyConfig(t.configPath, entries);\n updated++;\n } catch (err) {\n errors.push(`${t.label}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n return { updated, errors };\n}\n\n/** Run the setup command. Writes MCP config to all detected Claude installs. */\nexport async function runSetup(\n opts: { force?: boolean; withChannelShim?: boolean } = {},\n): Promise<void> {\n console.error(\"\\nTandem Setup\\n\");\n\n if (opts.withChannelShim && !validateChannelShimPrereq(CHANNEL_DIST)) {\n console.error(\n `Error: --with-channel-shim requires dist/channel/index.js at ${CHANNEL_DIST}\\n` +\n `Run 'npm run build' first, or drop --with-channel-shim to use the plugin monitor.`,\n );\n process.exit(1);\n }\n\n console.error(\"Detecting Claude installations...\");\n\n const targets = detectTargets({ force: opts.force });\n\n if (targets.length === 0) {\n console.error(\n \" No Claude installations detected.\\n\" +\n \" If Claude Code is installed, ensure ~/.claude exists.\\n\" +\n \" You can force configuration to default paths with: tandem setup --force\",\n );\n return;\n }\n\n for (const t of targets) {\n console.error(` Found: ${t.label} (${t.configPath})`);\n }\n\n console.error(\"\\nWriting MCP configuration...\");\n\n let failures = 0;\n for (const t of targets) {\n const entries = buildMcpEntries(CHANNEL_DIST, {\n withChannelShim: opts.withChannelShim,\n targetKind: t.kind,\n });\n try {\n await applyConfig(t.configPath, entries);\n console.error(` \\x1b[32m✓\\x1b[0m ${t.label}`);\n } catch (err) {\n failures++;\n console.error(\n ` \\x1b[31m✗\\x1b[0m ${t.label}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (failures === targets.length) {\n console.error(\"\\nSetup failed — could not write any configuration. Check file permissions.\");\n process.exit(1);\n } else if (failures > 0) {\n console.error(\n `\\nSetup partially complete (${failures} target(s) failed). Start Tandem with: tandem`,\n );\n } else {\n console.error(\"\\nSetup complete! Start Tandem with: tandem\");\n console.error(\"Then in Claude, your tandem_* tools will be available.\");\n }\n\n // Install Claude Code skill (best-effort — doesn't block MCP setup)\n console.error(\"\\nInstalling Claude Code skill...\");\n try {\n await installSkill();\n console.error(\" \\x1b[32m✓\\x1b[0m ~/.claude/skills/tandem/SKILL.md\");\n } catch (err) {\n console.error(\n ` \\x1b[33m⚠\\x1b[0m Could not install skill: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Plugin install instructions (shown on all successful setups)\n if (failures < targets.length) {\n const pluginManifest = join(PACKAGE_ROOT, \".claude-plugin\", \"plugin.json\");\n const devInstructions = existsSync(pluginManifest)\n ? ` Or for development, load directly from this package:\\n\\n` +\n ` claude --plugin-dir ${PACKAGE_ROOT}\\n\\n`\n : ` (Development plugin dir not found at ${pluginManifest}; skipping local-plugin instructions.)\\n\\n`;\n\n console.error(\n \"\\n\\x1b[1mReal-time push notifications (recommended):\\x1b[0m\\n\" +\n \" Install the Tandem plugin for instant events (one-time):\\n\\n\" +\n \" claude plugin marketplace add bloknayrb/tandem\\n\" +\n \" claude plugin install tandem@tandem-editor\\n\\n\" +\n devInstructions +\n \" Without the plugin, Claude still works but relies on tandem_checkInbox polling.\\n\",\n );\n }\n}\n","/**\n * Helpers shared by CLI stdio entry points (`mcp-stdio`, `channel`) and the\n * standalone server / monitor binaries that all speak MCP over stdout.\n */\n\nimport { DEFAULT_MCP_PORT } from \"./constants.js\";\n\n/**\n * In stdio MCP mode, stdout is the JSON-RPC wire — any stray library write\n * corrupts the protocol. Redirect `console.log/warn/info` to stderr so\n * incidental logging is safe. Callers that also run in-process tests (e.g.\n * `src/monitor/index.ts`) can gate this behind `!process.env.VITEST`.\n */\nexport function redirectConsoleToStderr(): void {\n console.log = console.error;\n console.warn = console.error;\n console.info = console.error;\n}\n\n/**\n * Resolve the Tandem HTTP base URL used by stdio subcommands. Precedence:\n * (1) explicit override (programmatic, e.g. from tests)\n * (2) CLAUDE_PLUGIN_OPTION_SERVER_URL — injected by plugin host from userConfig\n * (3) TANDEM_URL — explicit env override\n * (4) localhost default\n * Blank values are treated as absent so a blank plugin option does not mask an\n * explicit TANDEM_URL or the localhost default.\n * The returned string has no trailing slash so callers can concatenate\n * `/health`, `/mcp`, etc. without double-slash. One or more trailing slashes\n * are stripped, so both `http://x/` and `http://x//` resolve to `http://x`.\n */\nexport function resolveTandemUrl(override?: string): string {\n return resolveTandemUrlCandidate(override).replace(/\\/+$/, \"\");\n}\n\nfunction resolveTandemUrlCandidate(override?: string): string {\n const candidates = [\n override,\n process.env.CLAUDE_PLUGIN_OPTION_SERVER_URL,\n process.env.TANDEM_URL,\n ];\n for (const url of candidates) {\n if (url !== undefined && url.trim() !== \"\") return url.trim();\n }\n return `http://localhost:${DEFAULT_MCP_PORT}`;\n}\n\n/**\n * Resolve the Tandem auth token. Precedence:\n * (1) explicit override (programmatic, e.g. from tests)\n * (2) CLAUDE_PLUGIN_OPTION_AUTH_TOKEN — injected by plugin host from userConfig\n * (3) TANDEM_AUTH_TOKEN — explicit env override\n * Blank values are treated as absent so a blank plugin option does not mask an\n * explicit TANDEM_AUTH_TOKEN. Returns undefined when all absent (loopback mode,\n * no Authorization header sent).\n */\nexport function resolveAuthToken(override?: string): string | undefined {\n return resolveAuthTokenCandidate(override).token;\n}\n\nexport type AuthTokenSource =\n | \"explicit override\"\n | \"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\"\n | \"TANDEM_AUTH_TOKEN\";\n\nexport function resolveAuthTokenCandidate(\n override?: string,\n): { token: string; source: AuthTokenSource } | { token: undefined; source: undefined } {\n const candidates: Array<[AuthTokenSource, string | undefined]> = [\n [\"explicit override\", override],\n [\"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\", process.env.CLAUDE_PLUGIN_OPTION_AUTH_TOKEN],\n [\"TANDEM_AUTH_TOKEN\", process.env.TANDEM_AUTH_TOKEN],\n ];\n for (const [source, token] of candidates) {\n if (token !== undefined && token.trim() !== \"\") return { token, source };\n }\n return { token: undefined, source: undefined };\n}\n\n/** Regex for a valid Tandem auth token (32+ URL-safe alphanumeric chars). */\nconst VALID_TOKEN_RE = /^[A-Za-z0-9_\\-]{32,}$/;\n\n/** Guard so we only warn once per process (not on every SSE reconnect). */\nlet _warnedInvalidToken = false;\n\n/**\n * Fetch wrapper that automatically injects `Authorization: Bearer <token>`\n * when a resolved Tandem auth token is set and valid.\n *\n * This is the forgiving variant — used by monitor/channel which may run in\n * loopback-only mode without a token. Invalid or absent tokens are silently\n * ignored (no exit-1). The strict validation lives in mcp-stdio.ts only.\n * When the token is set but fails validation, a one-time warning is emitted\n * so operators know why auth headers are absent.\n */\nexport async function authFetch(url: string, init?: RequestInit): Promise<Response> {\n const { token, source } = resolveAuthTokenCandidate();\n if (token !== undefined) {\n const trimmed = token.trim();\n if (VALID_TOKEN_RE.test(trimmed)) {\n const headers = new Headers(init?.headers);\n headers.set(\"Authorization\", `Bearer ${trimmed}`);\n return fetch(url, { ...init, headers });\n }\n // Token is set but invalid — warn once so operators know why auth fails\n if (!_warnedInvalidToken) {\n _warnedInvalidToken = true;\n console.error(\n `[tandem] authFetch: ${source} is set but invalid (must be 32+ alphanumeric chars [A-Za-z0-9_-]); sending without Authorization header`,\n );\n }\n }\n return fetch(url, init);\n}\n","/**\n * Shared preflight check for stdio MCP subcommands.\n *\n * Both `tandem mcp-stdio` and `tandem channel` need a live Tandem server on\n * localhost before they can do anything useful. Two flavors:\n *\n * - `ensureTandemServer` — fail fast via stderr + exit(1) when the server\n * isn't reachable. Used by `tandem channel`, whose stdio transport can't\n * meaningfully respond on its own.\n * - `probeTandemServer` — returns a result without side effects. Used by\n * `tandem mcp-stdio`, which starts its stdio transport before preflight\n * so it can synthesize -32000 JSON-RPC errors for any in-flight request\n * before exiting (issue #336).\n */\n\nimport { resolveTandemUrl } from \"../shared/cli-runtime.js\";\n\nconst DEFAULT_TIMEOUT_MS = 2000;\n\nexport interface PreflightOptions {\n url?: string;\n timeoutMs?: number;\n}\n\n// Note: \"unreachable\" is a catch-all for any non-HTTP-status failure —\n// DNS, TLS, timeout, ECONNREFUSED, RST all land here. \"unhealthy\" is\n// strictly non-2xx responses from /health.\nexport type PreflightProbe =\n | { ok: true }\n | { ok: false; url: string; reason: string; kind: \"unreachable\" | \"unhealthy\" };\n\nexport async function probeTandemServer(opts: PreflightOptions = {}): Promise<PreflightProbe> {\n const url = resolveTandemUrl(opts.url);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const res = await fetch(`${url}/health`, { signal: controller.signal });\n if (!res.ok) {\n return {\n ok: false,\n url,\n reason: `health endpoint returned HTTP ${res.status}`,\n kind: \"unhealthy\",\n };\n }\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n url,\n reason: err instanceof Error ? err.message : String(err),\n kind: \"unreachable\",\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function ensureTandemServer(opts: PreflightOptions = {}): Promise<void> {\n const probe = await probeTandemServer(opts);\n if (!probe.ok) {\n const guidance =\n probe.kind === \"unreachable\"\n ? \"Start the Tauri app or run `tandem start` on the host, then retry.\"\n : \"The Tandem server is running but unhealthy — check the host logs.\";\n process.stderr.write(\n `[tandem] Tandem server preflight failed at ${probe.url} (${probe.reason}).\\n` +\n `[tandem] ${guidance}\\n`,\n );\n process.exit(1);\n }\n}\n","/**\n * Tandem mcp-stdio subcommand — stdio ↔ Streamable HTTP JSON-RPC proxy.\n *\n * Claude Desktop's plugin loader bridges stdio MCP servers into sandboxed\n * sessions but not HTTP MCP servers, so plugin-cached stdio entries that\n * forward to the local HTTP MCP endpoint are the only supported way to\n * surface tandem_* tools into those sessions.\n *\n * Raw message forwarding: no handler registrations, no per-method logic.\n * Any message the upstream emits (tool results, notifications, future\n * methods we haven't heard of) reaches the stdio client unchanged.\n *\n * Error surfacing (issue #336): the stdio transport is started before\n * preflight and http.start(), and early messages are buffered until the\n * upstream is ready. If the upstream never becomes ready — or dies mid-\n * session — every in-flight request ID is answered with a synthesized\n * `-32000` JSON-RPC error instead of a silent stdio close. Plugin hosts\n * surface `-32000` as actionable; a silent close is what produces \"tools\n * never appear in Cowork\" with nothing diagnosable in the logs.\n *\n * Intentional: no reconnection logic. If the upstream HTTP server dies\n * mid-session, we synthesize errors for pending requests and exit 1.\n * The plugin loader will respawn us on the next tool call and preflight\n * will re-run with a fresh, accurate error if the server is still down.\n */\n\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n redirectConsoleToStderr,\n resolveAuthTokenCandidate,\n resolveTandemUrl,\n} from \"../shared/cli-runtime.js\";\nimport { probeTandemServer } from \"./preflight.js\";\n\nredirectConsoleToStderr();\n\n// After preflight or http.start() fails we wait ~1.5s for any already-in-\n// flight `initialize` from the plugin loader to land on stdin and receive\n// a -32000 reply before tear-down. Sizing covers stdin-read lag between\n// preflight resolution and first message arrival — independent of\n// preflight's own fetch timeout.\nconst PREFLIGHT_GRACE_MS = 1500;\n\n// Per-request timeout. Node's setTimeout uses a 32-bit signed integer\n// internally — values above this constant are silently clamped to 1ms,\n// which would make every request immediately synthesize -32000.\nconst MAX_TIMEOUT_MS = 2_147_483_647; // 2^31 - 1\n\nexport function parseTimeoutMs(raw: string | undefined): number {\n if (raw !== undefined) {\n const parsed = parseInt(raw, 10);\n if (Number.isFinite(parsed) && parsed > 0 && parsed <= MAX_TIMEOUT_MS) {\n return parsed;\n }\n // Note: parseInt(\"3e4\", 10) returns 3 (stops at 'e'), which passes validation.\n // Scientific notation lands here only when the leading integer is invalid.\n process.stderr.write(\n `[tandem mcp-stdio] TANDEM_REQUEST_TIMEOUT_MS must be a positive integer ≤ ${MAX_TIMEOUT_MS}; ignoring \"${raw}\", using 30000ms default\\n`,\n );\n }\n return 30_000;\n}\n\nconst STDIO_REQUEST_TIMEOUT_MS = parseTimeoutMs(process.env.TANDEM_REQUEST_TIMEOUT_MS);\n\n// Last-gasp handlers for truly unexpected crashes: write one diagnostic to\n// stderr before exit. Installed at module load; process.once bounds each\n// handler to a single fire. No -32000 synthesis here because pendingRequests\n// lives inside runMcpStdio()'s closure.\nprocess.once(\"uncaughtException\", (err: Error) => {\n process.stderr.write(\n `[tandem mcp-stdio] uncaughtException: ${err.message}\\n${err.stack ?? \"\"}\\n`,\n );\n process.exit(1);\n});\nprocess.once(\"unhandledRejection\", (reason: unknown) => {\n const detail = reason instanceof Error ? reason.message : String(reason);\n process.stderr.write(`[tandem mcp-stdio] unhandledRejection: ${detail}\\n`);\n process.exit(1);\n});\n\n/** Regex for a valid Tandem auth token (32+ URL-safe alphanumeric chars). */\nconst VALID_TOKEN_RE = /^[A-Za-z0-9_\\-]{32,}$/;\n\n/**\n * Validate the configured auth token if present.\n * Rules (invariant 4):\n * - If not set at all, or if empty/whitespace-only after trim → return null (loopback-only mode).\n * - \"Bearer \" prefix → exit 1 with \"double-prefix\" message.\n * - Must match /^[A-Za-z0-9_-]{32,}$/ (no whitespace, no newlines, no Bearer prefix).\n */\nexport function readAndValidateAuthToken(): string | null {\n const { token, source } = resolveAuthTokenCandidate();\n // Token not set at all, or empty/whitespace-only → loopback-only mode, no auth header, no exit.\n if (token === undefined) return null;\n const trimmed = token.trim();\n if (trimmed === \"\") return null;\n\n if (trimmed.startsWith(\"Bearer \")) {\n process.stderr.write(\n `[tandem mcp-stdio] ${source} is invalid (double-prefix: do not include 'Bearer ' prefix — supply the raw token only)\\n`,\n );\n process.exit(1);\n }\n\n if (!VALID_TOKEN_RE.test(trimmed)) {\n process.stderr.write(\n `[tandem mcp-stdio] ${source} is malformed (must be 32+ URL-safe characters: [A-Za-z0-9_-])\\n`,\n );\n process.exit(1);\n }\n\n return trimmed;\n}\n\nexport async function runMcpStdio(): Promise<void> {\n const baseUrl = resolveTandemUrl();\n const authToken = readAndValidateAuthToken();\n\n const http = new StreamableHTTPClientTransport(new URL(`${baseUrl}/mcp`), {\n requestInit: authToken ? { headers: { Authorization: `Bearer ${authToken}` } } : undefined,\n });\n const stdio = new StdioServerTransport();\n\n // On upstream failure we synthesize -32000 for every entry before exit.\n // Value is the per-request timeout handle so we can cancel it on response.\n const pendingRequests = new Map<string | number, ReturnType<typeof setTimeout>>();\n // Messages arriving before httpReady flips; either drained and forwarded\n // on success, or each request answered with -32000 on preflight/http-start\n // failure.\n const preReadyBuffer: JSONRPCMessage[] = [];\n let shuttingDown = false;\n let httpReady = false;\n\n async function sendErrorResponse(\n id: string | number,\n message: string,\n detail?: string,\n ): Promise<void> {\n const errorResponse: JSONRPCMessage = {\n jsonrpc: \"2.0\",\n id,\n error: {\n // -32000 is the implementation-defined server error range per\n // JSON-RPC 2.0 §5.1 — upstream unavailability is an application-\n // level condition, not a generic Internal Error.\n code: -32000,\n message,\n ...(detail !== undefined ? { data: { detail } } : {}),\n },\n };\n try {\n await stdio.send(errorResponse);\n } catch (err) {\n // stdio already torn down; log so synth failures during shutdown\n // (e.g., http.onclose racing stdio.onclose) aren't silently dropped —\n // a silent drop here would recreate exactly the failure mode this\n // module exists to prevent.\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[tandem mcp-stdio] failed to send synthesized error for id ${id}: ${detail}\\n`,\n );\n }\n }\n\n function forwardToUpstream(msg: JSONRPCMessage): void {\n if (shuttingDown) return;\n const requestId = getRequestId(msg);\n if (requestId !== undefined) {\n // Clear any existing timer for this id (duplicate/retry scenario).\n const existing = pendingRequests.get(requestId);\n if (existing) clearTimeout(existing);\n\n const timeoutHandle = setTimeout(() => {\n // Atomic: delete returns false if synthesizePending already drained the map.\n if (!pendingRequests.delete(requestId)) return;\n void sendErrorResponse(\n requestId,\n \"Tandem HTTP upstream not responding (half-open)\",\n `No response after ${STDIO_REQUEST_TIMEOUT_MS}ms`,\n );\n }, STDIO_REQUEST_TIMEOUT_MS);\n pendingRequests.set(requestId, timeoutHandle);\n }\n http.send(msg).catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] upstream send failed: ${detail}\\n`);\n if (requestId !== undefined) {\n const handle = pendingRequests.get(requestId);\n if (handle !== undefined) {\n pendingRequests.delete(requestId);\n clearTimeout(handle);\n void sendErrorResponse(requestId, \"Tandem HTTP upstream unreachable\", detail);\n }\n }\n });\n }\n\n async function synthesizeBuffered(message: string, detail?: string): Promise<void> {\n const buffered = preReadyBuffer.splice(0);\n const ids = buffered\n .map((msg) => getRequestId(msg))\n .filter((id): id is string | number => id !== undefined);\n for (const id of ids) {\n await sendErrorResponse(id, message, detail);\n }\n }\n\n async function synthesizePending(message: string, detail?: string): Promise<void> {\n if (pendingRequests.size === 0) return;\n const ids = [...pendingRequests.keys()];\n // Synchronous before any await: clear all timers + drain map atomically.\n // Timer callbacks that are already queued observe empty map and return early.\n for (const handle of pendingRequests.values()) clearTimeout(handle);\n pendingRequests.clear();\n await Promise.all(ids.map((id) => sendErrorResponse(id, message, detail)));\n }\n\n const shutdown = async (\n code = 0,\n synth?: { message: string; detail?: string },\n ): Promise<never> => {\n if (!shuttingDown) {\n shuttingDown = true;\n // Hard deadline: if http.close() or stdio.close() hang (e.g., a half-\n // open upstream holding an SSE GET open), don't let cleanup block the\n // process exit indefinitely. .unref() means this timer doesn't itself\n // keep the event loop alive — fast paths resolve and call process.exit\n // below before the deadline fires; hung paths are forcibly terminated.\n setTimeout(() => process.exit(code), 2_000).unref();\n // Unconditionally drain all pending timers before any await — prevents\n // orphan timers from firing into the half-closed transport during\n // http.close() / stdio.close() awaits. synthesizePending will also\n // drain the map if synth is provided; the clearTimeout calls here are\n // defensive for the synth=undefined path (e.g., clean stdio.onclose).\n for (const handle of pendingRequests.values()) clearTimeout(handle);\n if (synth) {\n await synthesizeBuffered(synth.message, synth.detail);\n await synthesizePending(synth.message, synth.detail);\n }\n await http.close().catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] http.close failed: ${detail}\\n`);\n });\n await stdio.close().catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] stdio.close failed: ${detail}\\n`);\n });\n }\n process.exit(code);\n };\n\n // Plugin hosts typically send `initialize` immediately after spawn (MCP\n // lifecycle §initialization). Deferring shutdown by PREFLIGHT_GRACE_MS\n // lets that request land during the preflight/start window and receive\n // a -32000 reply rather than a silent stdio close. stdio.onclose\n // short-circuits this if the loader closes stdin first.\n function deferredShutdown(synth: { message: string; detail?: string }): void {\n setTimeout(() => void shutdown(1, synth), PREFLIGHT_GRACE_MS);\n }\n\n stdio.onmessage = (msg: JSONRPCMessage) => {\n if (!httpReady) {\n preReadyBuffer.push(msg);\n return;\n }\n forwardToUpstream(msg);\n };\n\n http.onmessage = (msg: JSONRPCMessage) => {\n if (shuttingDown) return;\n // Synchronous delete+clear FIRST — before stdio.send — to prevent the\n // per-request timer from firing in the window between response arrival\n // and stdio write completion (which would synthesize a false -32000 for\n // an id that already has a real response in flight).\n const responseId = getResponseId(msg);\n if (responseId !== undefined) {\n const handle = pendingRequests.get(responseId);\n if (handle !== undefined) {\n clearTimeout(handle);\n pendingRequests.delete(responseId);\n }\n }\n const sendHandler = (err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[tandem mcp-stdio] stdio write failed for id ${responseId ?? \"<notification>\"}: ${detail}\\n`,\n );\n // Map entry already deleted above. Synthesize directly for this id,\n // then tear down — stdio is broken so other pending requests can't\n // be delivered either.\n if (responseId !== undefined) {\n void sendErrorResponse(responseId, \"Tandem stdio write failed\", detail);\n }\n void shutdown(1, {\n message: \"Tandem stdio write failed\",\n detail,\n });\n };\n try {\n stdio.send(msg).catch(sendHandler);\n } catch (err) {\n sendHandler(err);\n }\n };\n\n stdio.onerror = (err) => {\n process.stderr.write(`[tandem mcp-stdio] stdio error: ${err.message}\\n${err.stack ?? \"\"}\\n`);\n };\n http.onerror = (err) => {\n const cause = (err as { cause?: unknown }).cause;\n process.stderr.write(\n `[tandem mcp-stdio] http error: ${err.message}\\n${err.stack ?? \"\"}${cause !== undefined ? `\\ncause: ${cause}` : \"\"}\\n`,\n );\n };\n\n stdio.onclose = () => {\n void shutdown(0);\n };\n http.onclose = () => {\n // We've observed the current @modelcontextprotocol/sdk (0.20.x) only\n // firing onclose from inside its own close() method — i.e., as a\n // consequence of *our* shutdown. The synth branch below is defensive\n // for future SDK versions that may propagate socket-death as onclose.\n // The `shuttingDown` guard prevents double-synth when shutdown() calls\n // http.close() itself.\n if (shuttingDown) return;\n void shutdown(1, {\n message: \"Tandem HTTP upstream closed unexpectedly\",\n detail: \"upstream connection dropped mid-session\",\n });\n };\n\n // Start stdio BEFORE preflight so any `initialize` that arrives during\n // the preflight window is captured and either forwarded once upstream is\n // ready, or answered with -32000 if upstream never comes up.\n await stdio.start();\n\n // The SDK's StdioServerTransport watches stdin for 'data' and 'error' only —\n // it does not call onclose when the plugin host closes stdin (EOF). Register\n // our own one-shot listener so that plugin-host close (stdin.end() / HUP)\n // triggers the same clean shutdown path as stdio.onclose does.\n process.stdin.once(\"end\", () => {\n void shutdown(0);\n });\n\n const probe = await probeTandemServer({ url: baseUrl });\n if (!probe.ok) {\n const guidance =\n probe.kind === \"unreachable\"\n ? \"Start the Tauri app or run `tandem start` on the host, then retry.\"\n : \"The Tandem server is running but unhealthy — check the host logs.\";\n process.stderr.write(\n `[tandem mcp-stdio] Tandem server preflight failed at ${probe.url} (${probe.reason}).\\n` +\n `[tandem mcp-stdio] ${guidance}\\n`,\n );\n const synthMessage =\n probe.kind === \"unreachable\"\n ? \"Tandem server not running. Start the Tauri app or run `tandem start`.\"\n : \"Tandem server unhealthy (check host logs).\";\n deferredShutdown({ message: synthMessage, detail: probe.reason });\n return;\n }\n\n // The current @modelcontextprotocol/sdk's StreamableHTTPClientTransport.start()\n // only creates an AbortController and returns synchronously — this catch is\n // defensive for future SDK versions that may perform real I/O during start().\n try {\n await http.start();\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] upstream http start failed: ${detail}\\n`);\n deferredShutdown({ message: \"Tandem HTTP upstream failed to start\", detail });\n return;\n }\n httpReady = true;\n\n // Held to preserve forwarding semantics — push through the normal path\n // now that upstream is ready. Note: forwardToUpstream does not await the\n // http.send, so buffered requests POST in parallel. Plugin hosts wait\n // for `initialize` to resolve before sending follow-ups per MCP spec, so\n // the buffer is usually ≤1 entry; we don't enforce serial ordering here.\n const buffered = preReadyBuffer.splice(0);\n for (const msg of buffered) forwardToUpstream(msg);\n}\n\nexport function getRequestId(msg: JSONRPCMessage): string | number | undefined {\n const m = msg as { id?: unknown; method?: unknown };\n if (typeof m.method !== \"string\") return undefined;\n if (typeof m.id === \"string\" || typeof m.id === \"number\") return m.id;\n return undefined;\n}\n\nexport function getResponseId(msg: JSONRPCMessage): string | number | undefined {\n const m = msg as { id?: unknown; method?: unknown };\n if (typeof m.method === \"string\") return undefined;\n if (typeof m.id === \"string\" || typeof m.id === \"number\") return m.id;\n return undefined;\n}\n","/**\n * Shared fetch-with-timeout helper.\n *\n * Used by `src/monitor/index.ts` and `src/channel/` (event-bridge + run) to\n * give every outbound HTTP call a bounded deadline. Without this, a half-open\n * upstream wedges the caller silently — see #336 (silent failures) and #364\n * (event-bridge transport timeout symmetry).\n *\n * Pure native `fetch` + `AbortSignal.timeout` so it can be imported from any\n * surface without dragging in server deps. Routes through `authFetch` so the\n * resolved Tandem auth token is forwarded automatically when set.\n *\n * `describeFetchError` formats timeout aborts as `<endpoint> timed out after\n * <ms>ms` so logs name the hung endpoint instead of the generic\n * \"operation was aborted\" string from AbortError/TimeoutError.\n */\n\nimport { authFetch } from \"./cli-runtime.js\";\n\n/**\n * Fetch with a per-request deadline.\n *\n * **Do not use for SSE handshake-then-stream patterns** — applying a fetch-level\n * timeout to a streaming response also aborts the body `ReadableStream` when\n * the timeout fires, killing the stream at `timeoutMs`. Use a local\n * `AbortController` cleared after the handshake settles for that case.\n */\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;\n return authFetch(url, { ...init, signal });\n}\n\n/**\n * Format a fetch error for logs. Recognizes `TimeoutError` / `AbortError`\n * (both names that `AbortSignal.timeout` and manual aborts produce) and tags\n * them with the endpoint + threshold so log lines name the hung request.\n */\nexport function describeFetchError(err: unknown, endpoint: string, timeoutMs: number): string {\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n return `${endpoint} timed out after ${timeoutMs}ms`;\n }\n return err instanceof Error ? err.message : String(err);\n}\n\n/** True iff `err` is an AbortError or TimeoutError (the names produced by\n * AbortSignal.timeout and manual aborts). Channel callers re-throw these\n * through broad catches so timeouts surface as structured errors instead of\n * being swallowed as \"non-JSON response\" fake-success. */\nexport function isAbortOrTimeoutError(err: unknown): boolean {\n return err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\");\n}\n","function generateId(prefix: string): string {\n return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function generateAnnotationId(): string {\n return generateId(\"ann\");\n}\n\nexport function generateMessageId(): string {\n return generateId(\"msg\");\n}\n\nexport function generateEventId(): string {\n return generateId(\"evt\");\n}\n\nexport function generateReplyId(): string {\n return generateId(\"rpl\");\n}\n\nexport function generateNotificationId(): string {\n return generateId(\"ntf\");\n}\n\nexport function generateAuthorshipId(author: \"user\" | \"claude\"): string {\n return generateId(author);\n}\n","/**\n * Event types for the Tandem → Claude Code channel.\n *\n * These events flow from browser-originated Y.Map changes through an SSE\n * endpoint to the channel shim, which pushes them into Claude Code as\n * `notifications/claude/channel` messages.\n *\n * This module lives in `src/shared/` so that `src/channel/` and\n * `src/monitor/` can import wire-protocol types without crossing the\n * server layer boundary.\n */\n\nimport type { AnnotationType, ReplyAuthor } from \"../types.js\";\n\n// --- Per-event payload interfaces ---\n\nexport interface AnnotationCreatedPayload {\n annotationId: string;\n annotationType: AnnotationType;\n content: string;\n textSnippet: string;\n hasSuggestedText?: boolean;\n}\n\nexport interface AnnotationAcceptedPayload {\n annotationId: string;\n textSnippet: string;\n}\n\nexport interface AnnotationDismissedPayload {\n annotationId: string;\n textSnippet: string;\n}\n\nexport interface ChatMessagePayload {\n messageId: string;\n text: string;\n replyTo: string | null;\n anchor: { from: number; to: number; textSnapshot: string } | null;\n /** Buffered selection context at the time the chat message was sent. */\n selection?: { from: number; to: number; selectedText: string } | { selectedText: string };\n}\n\nexport interface DocumentOpenedPayload {\n fileName: string;\n format: string;\n}\n\nexport interface DocumentClosedPayload {\n fileName: string;\n}\n\nexport interface AnnotationReplyPayload {\n annotationId: string;\n replyId: string;\n replyText: string;\n replyAuthor: ReplyAuthor;\n textSnippet: string;\n}\n\nexport interface DocumentSwitchedPayload {\n fileName: string;\n}\n\nexport interface AnnotationEditedPayload {\n annotationId: string;\n content: string;\n textSnippet: string;\n editedAt: number;\n}\n\n// --- Discriminated union ---\n\ninterface TandemEventBase {\n /** Timestamp-based unique ID for SSE `Last-Event-ID` reconnection. Format: `evt_<timestamp>_<rand>`. Roughly ordered but not strictly monotonic. */\n id: string;\n timestamp: number;\n /** Which document this event relates to (absent for global events). */\n documentId?: string;\n}\n\nexport type TandemEvent =\n | (TandemEventBase & { type: \"annotation:created\"; payload: AnnotationCreatedPayload })\n | (TandemEventBase & { type: \"annotation:accepted\"; payload: AnnotationAcceptedPayload })\n | (TandemEventBase & { type: \"annotation:dismissed\"; payload: AnnotationDismissedPayload })\n | (TandemEventBase & { type: \"annotation:reply\"; payload: AnnotationReplyPayload })\n | (TandemEventBase & { type: \"chat:message\"; payload: ChatMessagePayload })\n | (TandemEventBase & { type: \"document:opened\"; payload: DocumentOpenedPayload })\n | (TandemEventBase & { type: \"document:closed\"; payload: DocumentClosedPayload })\n | (TandemEventBase & { type: \"document:switched\"; payload: DocumentSwitchedPayload })\n | (TandemEventBase & { type: \"annotation:edited\"; payload: AnnotationEditedPayload });\n\n/** Union of all event type discriminants. */\nexport type TandemEventType = TandemEvent[\"type\"];\n\n// Re-export from shared utils (single ID generation pattern)\nexport { generateEventId } from \"../utils.js\";\n\n// --- Parse guard for SSE consumers ---\n\nconst VALID_EVENT_TYPES = new Set<TandemEventType>([\n \"annotation:created\",\n \"annotation:accepted\",\n \"annotation:dismissed\",\n \"annotation:edited\",\n \"annotation:reply\",\n \"chat:message\",\n \"document:opened\",\n \"document:closed\",\n \"document:switched\",\n]);\n\n/**\n * Validate a JSON-parsed value as a TandemEvent.\n * Used by the event-bridge to safely consume SSE data.\n */\nexport function parseTandemEvent(raw: unknown): TandemEvent | null {\n if (\n typeof raw !== \"object\" ||\n raw === null ||\n !(\"id\" in raw) ||\n typeof (raw as Record<string, unknown>).id !== \"string\" ||\n !(\"type\" in raw) ||\n !VALID_EVENT_TYPES.has((raw as Record<string, unknown>).type as TandemEventType) ||\n !(\"timestamp\" in raw) ||\n typeof (raw as Record<string, unknown>).timestamp !== \"number\" ||\n !(\"payload\" in raw) ||\n typeof (raw as Record<string, unknown>).payload !== \"object\"\n ) {\n return null;\n }\n return raw as TandemEvent;\n}\n\n/**\n * Convert a TandemEvent into a human-readable string for the channel `content` field.\n * Claude sees this text inside `<channel source=\"tandem-channel\">` tags.\n */\nexport function formatEventContent(event: TandemEvent): string {\n const doc = event.documentId ? ` [doc: ${event.documentId}]` : \"\";\n\n switch (event.type) {\n case \"annotation:created\": {\n const { annotationType, content, textSnippet, hasSuggestedText } = event.payload;\n const snippet = textSnippet ? ` on \"${textSnippet}\"` : \"\";\n const label = hasSuggestedText ? \"replacement\" : annotationType;\n return `User created ${label}${snippet}: ${content || \"(no content)\"}${doc}`;\n }\n case \"annotation:accepted\": {\n const { annotationId, textSnippet } = event.payload;\n return `User accepted annotation ${annotationId}${textSnippet ? ` (\"${textSnippet}\")` : \"\"}${doc}`;\n }\n case \"annotation:dismissed\": {\n const { annotationId, textSnippet } = event.payload;\n return `User dismissed annotation ${annotationId}${textSnippet ? ` (\"${textSnippet}\")` : \"\"}${doc}`;\n }\n case \"annotation:edited\": {\n const { content } = event.payload;\n return `User edited annotation: \"${content}\"${doc}`;\n }\n case \"annotation:reply\": {\n const { annotationId, replyAuthor, replyText, textSnippet } = event.payload;\n const who = replyAuthor === \"claude\" ? \"Claude\" : \"User\";\n const snippet = textSnippet ? ` (on \"${textSnippet}\")` : \"\";\n return `${who} replied to annotation ${annotationId}${snippet}: ${replyText}${doc}`;\n }\n case \"chat:message\": {\n const { text, replyTo, selection } = event.payload;\n const reply = replyTo ? ` (replying to ${replyTo})` : \"\";\n const sel =\n selection && selection.selectedText\n ? ` [selection: \"${selection.selectedText}\"${\"from\" in selection ? ` (${selection.from}-${selection.to})` : \"\"}]`\n : \"\";\n return `User says${reply}: ${text}${sel}${doc}`;\n }\n case \"document:opened\": {\n const { fileName, format } = event.payload;\n return `User opened document: ${fileName} (${format})${doc}`;\n }\n case \"document:closed\": {\n const { fileName } = event.payload;\n return `User closed document: ${fileName}${doc}`;\n }\n case \"document:switched\": {\n const { fileName } = event.payload;\n return `User switched to document: ${fileName}${doc}`;\n }\n default: {\n const _exhaustive: never = event;\n void _exhaustive;\n return `Unknown event${doc}`;\n }\n }\n}\n\n/**\n * Build the `meta` record for a channel notification.\n * Keys use underscores only (Channels API silently drops hyphenated keys).\n */\nexport function formatEventMeta(event: TandemEvent): Record<string, string> {\n const meta: Record<string, string> = {\n event_type: event.type,\n };\n if (event.documentId) meta.document_id = event.documentId;\n\n switch (event.type) {\n case \"annotation:created\":\n case \"annotation:accepted\":\n case \"annotation:dismissed\":\n meta.annotation_id = event.payload.annotationId;\n break;\n case \"annotation:edited\":\n meta.annotation_id = event.payload.annotationId;\n meta.edited_at = String(event.payload.editedAt);\n break;\n case \"annotation:reply\":\n meta.annotation_id = event.payload.annotationId;\n meta.reply_id = event.payload.replyId;\n break;\n case \"chat:message\":\n meta.message_id = event.payload.messageId;\n if (event.payload.selection?.selectedText) meta.has_selection = \"true\";\n break;\n case \"document:opened\":\n case \"document:closed\":\n case \"document:switched\":\n break;\n default: {\n const _exhaustive: never = event;\n void _exhaustive;\n break;\n }\n }\n\n return meta;\n}\n","/**\n * SSE event bridge: connects to Tandem server's /api/events endpoint\n * and pushes received events to Claude Code as channel notifications.\n *\n * Per-request timeouts (#364) mirror the monitor pattern: every outbound\n * fetch has a bounded deadline, the SSE body has an inactivity watchdog,\n * and the parse buffer is capped so a malformed upstream can't OOM us.\n * Without these, a half-open Tandem server wedges the bridge silently.\n */\n\nimport type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { authFetch } from \"../shared/cli-runtime.js\";\nimport {\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n CHANNEL_CONNECT_FETCH_TIMEOUT_MS,\n CHANNEL_ERROR_REPORT_TIMEOUT_MS,\n CHANNEL_MAX_RETRIES,\n CHANNEL_MAX_SSE_BUFFER_BYTES,\n CHANNEL_MODE_FETCH_TIMEOUT_MS,\n CHANNEL_RETRY_DELAY_MS,\n CHANNEL_SSE_INACTIVITY_TIMEOUT_MS,\n} from \"../shared/constants.js\";\nimport type { TandemEvent } from \"../shared/events/types.js\";\nimport { formatEventContent, formatEventMeta, parseTandemEvent } from \"../shared/events/types.js\";\nimport { describeFetchError, fetchWithTimeout } from \"../shared/fetch-with-timeout.js\";\n\nconst AWARENESS_DEBOUNCE_MS = 500;\nconst MODE_CACHE_TTL_MS = 2000;\n\n/**\n * Stdio-mode SSE bridge. New push-path work should target src/monitor/.\n * This path remains active for stdio-mode Claude Code connections.\n */\nexport async function startEventBridge(mcp: Server, tandemUrl: string): Promise<void> {\n let retries = 0;\n let lastEventId: string | undefined;\n\n while (retries < CHANNEL_MAX_RETRIES) {\n try {\n await connectAndStream(mcp, tandemUrl, lastEventId, (id) => {\n lastEventId = id;\n retries = 0;\n });\n } catch (err) {\n retries++;\n console.error(\n `[Channel] SSE connection failed (${retries}/${CHANNEL_MAX_RETRIES}):`,\n err instanceof Error ? err.message : err,\n );\n\n if (retries >= CHANNEL_MAX_RETRIES) {\n console.error(\"[Channel] SSE connection exhausted, reporting error and exiting\");\n try {\n await fetchWithTimeout(\n `${tandemUrl}/api/channel-error`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n error: \"CHANNEL_CONNECT_FAILED\",\n message: `Channel shim lost connection after ${CHANNEL_MAX_RETRIES} retries.`,\n }),\n },\n CHANNEL_ERROR_REPORT_TIMEOUT_MS,\n );\n } catch (reportErr) {\n console.error(\n \"[Channel] Could not report failure to server:\",\n describeFetchError(reportErr, \"/api/channel-error\", CHANNEL_ERROR_REPORT_TIMEOUT_MS),\n );\n }\n process.exit(1);\n }\n\n await new Promise((r) => setTimeout(r, CHANNEL_RETRY_DELAY_MS));\n }\n }\n}\n\nasync function connectAndStream(\n mcp: Server,\n tandemUrl: string,\n lastEventId: string | undefined,\n onEventId: (id: string) => void,\n): Promise<void> {\n const headers: Record<string, string> = { Accept: \"text/event-stream\" };\n if (lastEventId) headers[\"Last-Event-ID\"] = lastEventId;\n\n // Split handshake timeout from body lifetime. Using AbortSignal.timeout on\n // the fetch would kill the response body ReadableStream when the timeout\n // fires — every SSE stream would abort at CHANNEL_CONNECT_FETCH_TIMEOUT_MS.\n // A local AbortController cleared in `finally` after the handshake settles\n // means the body stream is no longer governed by it.\n const connectCtrl = new AbortController();\n const connectTimer = setTimeout(\n () => connectCtrl.abort(new Error(\"handshake timeout\")),\n CHANNEL_CONNECT_FETCH_TIMEOUT_MS,\n );\n let res: Response;\n try {\n res = await authFetch(`${tandemUrl}/api/events`, { headers, signal: connectCtrl.signal });\n } finally {\n clearTimeout(connectTimer);\n }\n if (!res.ok) throw new Error(`SSE endpoint returned ${res.status}`);\n if (!res.body) throw new Error(\"SSE endpoint returned no body\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n // Inactivity watchdog. A healthy stream emits keepalive comments\n // periodically; if no bytes arrive for CHANNEL_SSE_INACTIVITY_TIMEOUT_MS,\n // cancel the reader. reader.cancel() resolves a pending read() with\n // {done: true} (does not reject), so we surface the cause via a flag.\n let lastActivityAt = Date.now();\n let inactivityTimedOut = false;\n const watchdog = setInterval(() => {\n if (Date.now() - lastActivityAt > CHANNEL_SSE_INACTIVITY_TIMEOUT_MS) {\n inactivityTimedOut = true;\n reader.cancel(new Error(\"SSE inactivity timeout\")).catch(() => {});\n }\n }, CHANNEL_SSE_INACTIVITY_TIMEOUT_MS / 4);\n\n // Debounced awareness: only send the latest status after a quiet period\n let awarenessTimer: ReturnType<typeof setTimeout> | null = null;\n let clearAwarenessTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingAwareness: TandemEvent | null = null;\n const AWARENESS_CLEAR_MS = 3000; // Reset active state after 3s of no new events\n\n function clearAwareness(documentId?: string) {\n fetchWithTimeout(\n `${tandemUrl}/api/channel-awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n documentId: documentId ?? null,\n status: \"idle\",\n active: false,\n }),\n },\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ).catch((err) => {\n console.error(\n \"[Channel] clearAwareness failed (non-fatal):\",\n describeFetchError(err, \"/api/channel-awareness clear\", CHANNEL_AWARENESS_FETCH_TIMEOUT_MS),\n );\n });\n }\n\n function flushAwareness() {\n if (!pendingAwareness) return;\n const event = pendingAwareness;\n pendingAwareness = null;\n fetchWithTimeout(\n `${tandemUrl}/api/channel-awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n documentId: event.documentId,\n status: `processing: ${event.type}`,\n active: true,\n }),\n },\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ).catch((err) => {\n console.error(\n \"[Channel] Awareness update failed:\",\n describeFetchError(\n err,\n \"/api/channel-awareness update\",\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ),\n );\n });\n\n // Auto-clear after timeout so the indicator doesn't stick\n if (clearAwarenessTimer) clearTimeout(clearAwarenessTimer);\n clearAwarenessTimer = setTimeout(() => clearAwareness(event.documentId), AWARENESS_CLEAR_MS);\n }\n\n function scheduleAwareness(event: TandemEvent) {\n pendingAwareness = event;\n if (awarenessTimer) clearTimeout(awarenessTimer);\n awarenessTimer = setTimeout(flushAwareness, AWARENESS_DEBOUNCE_MS);\n }\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n if (inactivityTimedOut) throw new Error(\"SSE inactivity timeout\");\n throw new Error(\"SSE stream ended\");\n }\n lastActivityAt = Date.now();\n\n buffer += decoder.decode(value, { stream: true });\n\n if (buffer.length > CHANNEL_MAX_SSE_BUFFER_BYTES) {\n throw new Error(\n `SSE buffer exceeded ${CHANNEL_MAX_SSE_BUFFER_BYTES} bytes without a frame boundary`,\n );\n }\n\n let boundary: number;\n while ((boundary = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const frame = buffer.slice(0, boundary);\n buffer = buffer.slice(boundary + 2);\n\n if (frame.startsWith(\":\")) continue;\n\n let eventId: string | undefined;\n let data: string | undefined;\n\n for (const line of frame.split(\"\\n\")) {\n if (line.startsWith(\"id: \")) eventId = line.slice(4);\n else if (line.startsWith(\"data: \")) data = line.slice(6);\n }\n\n if (!data) continue;\n\n let event: TandemEvent | null;\n try {\n event = parseTandemEvent(JSON.parse(data));\n } catch {\n console.error(\n \"[Channel] Malformed SSE event data (skipping), eventId=%s:\",\n eventId,\n data.slice(0, 200),\n );\n // Permanently unparseable — advance past it to prevent infinite re-delivery on reconnect.\n if (eventId) onEventId(eventId);\n continue;\n }\n if (!event) {\n console.error(\n \"[Channel] Invalid SSE event structure (skipping), eventId=%s:\",\n eventId,\n data.slice(0, 200),\n );\n if (eventId) onEventId(eventId);\n continue;\n }\n\n // Solo mode: intentionally suppressed (not a delivery failure) — advance past it.\n if (event.type !== \"chat:message\") {\n const mode = await getCachedMode(tandemUrl);\n if (mode === \"solo\") {\n console.error(`[Channel] Solo mode: suppressed ${event.type} event`);\n if (eventId) onEventId(eventId);\n continue;\n }\n }\n\n try {\n await mcp.notification({\n method: \"notifications/claude/channel\",\n params: {\n content: formatEventContent(event),\n meta: formatEventMeta(event),\n },\n });\n } catch (err) {\n console.error(\"[Channel] MCP notification failed (transport broken?):\", err);\n throw err;\n }\n\n // Advance only after notification succeeds so a transport failure\n // doesn't silently skip events on reconnect.\n if (eventId) onEventId(eventId);\n\n scheduleAwareness(event);\n }\n }\n } finally {\n // Single source of truth for timer cleanup — every exit path (success,\n // throw, reader.cancel) runs through here so awareness/inactivity\n // timers can't leak across reconnects.\n clearInterval(watchdog);\n if (awarenessTimer) clearTimeout(awarenessTimer);\n if (clearAwarenessTimer) clearTimeout(clearAwarenessTimer);\n }\n}\n\n// Cached mode lookup — avoids an HTTP fetch per event\nlet cachedMode: string = \"tandem\";\nlet cachedModeAt = 0;\n\nasync function getCachedMode(tandemUrl: string): Promise<string> {\n const now = Date.now();\n if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;\n try {\n const res = await fetchWithTimeout(`${tandemUrl}/api/mode`, {}, CHANNEL_MODE_FETCH_TIMEOUT_MS);\n if (res.ok) {\n const { mode } = (await res.json()) as { mode: string };\n cachedMode = mode;\n } else {\n console.error(`[Channel] Mode check returned ${res.status}, using cached: \"${cachedMode}\"`);\n }\n cachedModeAt = now;\n } catch (err) {\n console.error(\n \"[Channel] Mode check failed, delivering event (fail-open):\",\n describeFetchError(err, \"/api/mode\", CHANNEL_MODE_FETCH_TIMEOUT_MS),\n );\n cachedModeAt = now;\n }\n return cachedMode;\n}\n","/**\n * Tandem Channel Shim — core runtime, shared by:\n * - src/channel/index.ts (standalone binary, used by the Desktop sidecar)\n * - src/cli/channel.ts (npm-delivered entry for the plugin `tandem-channel`)\n *\n * Bridges Tandem's SSE event stream → Claude Code channel notifications,\n * and exposes a `tandem_reply` tool for Claude to respond to chat messages.\n *\n * Uses the low-level MCP `Server` class (not `McpServer`) as required by\n * the Channels API spec.\n */\n\nimport { createConnection } from \"node:net\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { redirectConsoleToStderr, resolveTandemUrl } from \"../shared/cli-runtime.js\";\nimport {\n CHANNEL_PERMISSION_FETCH_TIMEOUT_MS,\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n DEFAULT_MCP_PORT,\n} from \"../shared/constants.js\";\nimport {\n describeFetchError,\n fetchWithTimeout,\n isAbortOrTimeoutError,\n} from \"../shared/fetch-with-timeout.js\";\nimport { startEventBridge } from \"./event-bridge.js\";\n\nexport interface RunChannelOptions {\n /** Skip the non-fatal reachability probe. The CLI wrapper runs a strict\n * preflight upstream and we don't want to double-log \"server not reachable\"\n * noise. Defaults to false. */\n skipReachabilityLog?: boolean;\n}\n\nexport async function runChannel(opts: RunChannelOptions = {}): Promise<void> {\n redirectConsoleToStderr();\n\n const tandemUrl = resolveTandemUrl();\n\n const mcp = new Server(\n { name: \"tandem-channel\", version: \"0.1.0\" },\n {\n capabilities: {\n experimental: {\n \"claude/channel\": {},\n \"claude/channel/permission\": {},\n },\n tools: {},\n },\n instructions: [\n 'Events from Tandem arrive as <channel source=\"tandem-channel\" event_type=\"...\" document_id=\"...\">.',\n \"These are real-time push notifications of user actions in the collaborative document editor.\",\n \"Event types: annotation:created, annotation:accepted, annotation:dismissed, annotation:reply,\",\n \"chat:message, document:opened, document:closed, document:switched.\",\n \"Chat messages may include a 'selection' field with buffered selection context.\",\n \"Use your tandem MCP tools (tandem_getTextContent, tandem_comment, tandem_edit, etc.) to act on them.\",\n \"Reply to chat messages using tandem_reply. Pass document_id from the tag attributes.\",\n \"Do not reply to non-chat events — just act on them using tools.\",\n \"If you haven't received channel notifications recently, call tandem_checkInbox as a fallback.\",\n ].join(\" \"),\n },\n );\n\n mcp.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: \"tandem_reply\",\n description: \"Reply to a chat message in Tandem\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n text: { type: \"string\", description: \"The reply message\" },\n documentId: {\n type: \"string\",\n description: \"Document ID from the channel event (optional)\",\n },\n replyTo: {\n type: \"string\",\n description: \"Message ID being replied to (optional)\",\n },\n },\n required: [\"text\"],\n },\n },\n ],\n }));\n\n mcp.setRequestHandler(CallToolRequestSchema, async (req) => {\n if (req.params.name === \"tandem_reply\") {\n const args = req.params.arguments as Record<string, unknown>;\n try {\n const res = await fetchWithTimeout(\n `${tandemUrl}/api/channel-reply`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(args),\n },\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n );\n let data: unknown;\n try {\n data = await res.json();\n } catch (parseErr) {\n // Re-throw timeout/abort errors so they surface as structured\n // failures to Claude. AbortSignal.timeout fires DURING `res.json()`\n // (headers landed but body hung); without this re-throw, the bare\n // catch swallows AbortError and reports a fake-success \"Non-JSON\n // response\" payload — exactly the silent-failure pattern #364\n // exists to prevent.\n if (isAbortOrTimeoutError(parseErr)) throw parseErr;\n data = { message: \"Non-JSON response\" };\n }\n if (!res.ok) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Reply failed (${res.status}): ${JSON.stringify(data)}`,\n },\n ],\n isError: true,\n };\n }\n return { content: [{ type: \"text\" as const, text: JSON.stringify(data) }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to send reply: ${describeFetchError(\n err,\n \"/api/channel-reply\",\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n )}`,\n },\n ],\n isError: true,\n };\n }\n }\n throw new Error(`Unknown tool: ${req.params.name}`);\n });\n\n const PermissionRequestSchema = z.object({\n method: z.literal(\"notifications/claude/channel/permission_request\"),\n params: z.object({\n request_id: z.string(),\n tool_name: z.string(),\n description: z.string(),\n input_preview: z.string(),\n }),\n });\n\n mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {\n try {\n const res = await fetchWithTimeout(\n `${tandemUrl}/api/channel-permission`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n requestId: params.request_id,\n toolName: params.tool_name,\n description: params.description,\n inputPreview: params.input_preview,\n }),\n },\n CHANNEL_PERMISSION_FETCH_TIMEOUT_MS,\n );\n if (!res.ok) {\n console.error(\n `[Channel] Permission relay got HTTP ${res.status} — browser may not see prompt`,\n );\n }\n } catch (err) {\n console.error(\n \"[Channel] Failed to forward permission request:\",\n describeFetchError(err, \"/api/channel-permission\", CHANNEL_PERMISSION_FETCH_TIMEOUT_MS),\n );\n }\n });\n\n console.error(`[Channel] Tandem channel shim starting (server: ${tandemUrl})`);\n\n if (!opts.skipReachabilityLog) {\n const reachable = await checkServerReachable(tandemUrl);\n if (!reachable) {\n console.error(`[Channel] Cannot reach Tandem server at ${tandemUrl}`);\n console.error(\"[Channel] Start it with: tandem start\");\n // Continue anyway — the event bridge will retry, and the server may start later\n }\n }\n\n const transport = new StdioServerTransport();\n await mcp.connect(transport);\n console.error(\"[Channel] Connected to Claude Code via stdio\");\n\n startEventBridge(mcp, tandemUrl).catch((err) => {\n console.error(\"[Channel] Event bridge failed unexpectedly:\", err);\n process.exit(1);\n });\n}\n\nasync function checkServerReachable(url: string, timeoutMs = 2000): Promise<boolean> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n console.error(\n `[Channel] Invalid TANDEM_URL: \"${url}\" — expected format: http://localhost:3479`,\n );\n return false;\n }\n const port = parseInt(parsed.port || String(DEFAULT_MCP_PORT), 10);\n return new Promise((resolve) => {\n const socket = createConnection({ port, host: parsed.hostname }, () => {\n socket.destroy();\n resolve(true);\n });\n socket.setTimeout(timeoutMs);\n socket.on(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.on(\"error\", (err) => {\n console.error(`[Channel] Server probe failed: ${err.message}`);\n socket.destroy();\n resolve(false);\n });\n });\n}\n","/**\n * Tandem channel subcommand — npm-delivered entry for the plugin\n * `tandem-channel` MCP server. Runs the unified preflight, then hands off to\n * the shared channel shim runtime in src/channel/run.ts.\n */\n\nimport { runChannel } from \"../channel/run.js\";\nimport { ensureTandemServer } from \"./preflight.js\";\n\nexport async function runChannelCli(): Promise<void> {\n await ensureTandemServer();\n await runChannel({ skipReachabilityLog: true });\n}\n","import envPaths from \"env-paths\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { TOKEN_FILE_NAME } from \"../constants.js\";\n\nexport function getTokenFilePath(): string {\n return path.join(envPaths(\"tandem\", { suffix: \"\" }).data, TOKEN_FILE_NAME);\n}\n\nexport async function readTokenFromFile(): Promise<string | null> {\n const filePath = getTokenFilePath();\n try {\n const content = await fs.promises.readFile(filePath, \"utf8\");\n // Remediate insecure permissions if a previous chmod failed (e.g., process crashed).\n if (process.platform !== \"win32\") {\n try {\n const stat = await fs.promises.stat(filePath);\n if ((stat.mode & 0o077) !== 0) {\n console.error(\"[tandem] auth token file has insecure permissions; attempting chmod 0600\");\n await fs.promises.chmod(filePath, 0o600);\n }\n } catch {\n // Non-fatal: stat/chmod failure doesn't invalidate the token we already read\n }\n }\n const trimmed = content.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { promises as fsPromises } from \"node:fs\";\nimport path from \"node:path\";\nimport { getTokenFilePath, readTokenFromFile } from \"../shared/auth/token-file.js\";\nimport { resolveAuthTokenCandidate, resolveTandemUrl } from \"../shared/cli-runtime.js\";\nimport { applyConfigWithToken } from \"./setup.js\";\n\n/** SHA-256 fingerprint — first 8 hex chars. Never logs the full token value. */\nfunction fingerprint(token: string): string {\n return createHash(\"sha256\").update(token, \"utf8\").digest(\"hex\").slice(0, 8);\n}\n\nfunction generateToken(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport async function rotateToken(): Promise<void> {\n console.error(\"\\n[tandem] Rotating auth token...\\n\");\n\n // Refuse to rotate when token comes from env — Tauri injects TANDEM_AUTH_TOKEN\n // before sidecar spawn, and Claude Code's plugin host injects\n // CLAUDE_PLUGIN_OPTION_AUTH_TOKEN from userConfig. In either case we have no\n // way to update the launcher; rotating the file would desync with what's\n // re-injected on the next launch.\n const { source: envAuthSource } = resolveAuthTokenCandidate();\n if (\n envAuthSource === \"TANDEM_AUTH_TOKEN\" ||\n envAuthSource === \"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\"\n ) {\n console.error(\n `[tandem] Error: ${envAuthSource} is set in the environment.\\n` +\n \" Token rotation is not supported in env-token mode (used by Tauri\\n\" +\n \" and Claude Code's plugin host). Unset the variable and let Tandem\\n\" +\n \" manage the token file, or rotate via the launcher's token management.\",\n );\n process.exit(1);\n }\n\n const oldToken = await readTokenFromFile();\n if (!oldToken) {\n console.error(\n \"[tandem] Error: no token file found. Run `tandem setup` first to initialize the token.\",\n );\n process.exit(1);\n }\n\n // writeTokenToFile uses O_EXCL; bypass it here — rotation is an intentional overwrite.\n // Use atomic write: write to a temp file first, then rename() into place.\n // rename() is atomic on the same filesystem — power-loss mid-write cannot leave an empty file.\n const newToken = generateToken();\n const tokenPath = getTokenFilePath();\n const dir = path.dirname(tokenPath);\n const tmpPath = path.join(dir, `.auth-token-tmp-${randomBytes(4).toString(\"hex\")}`);\n try {\n await fsPromises.writeFile(tmpPath, newToken, { encoding: \"utf8\", mode: 0o600 });\n await fsPromises.rename(tmpPath, tokenPath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => {});\n throw err;\n }\n\n const serverUrl = resolveTandemUrl();\n\n // Three distinct outcomes:\n // graceWindowActive = true → server accepted the rotation; grace window is live\n // serverRejected = true → server reachable but returned non-2xx\n // (neither) → fetch threw; server was not running\n let graceWindowActive = false;\n let serverRejected = false;\n let serverRejectedStatus = 0;\n try {\n const resp = await fetch(`${serverUrl}/api/rotate-token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${oldToken}`,\n },\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(5000),\n });\n if (resp.ok) {\n graceWindowActive = true;\n } else {\n serverRejected = true;\n serverRejectedStatus = resp.status;\n }\n } catch {\n console.error(\n \"[tandem] Warning: server is not reachable. The new token is written to disk.\\n\" +\n \" Restart the server to activate the grace window; reconnect Claude Code after.\",\n );\n }\n\n let updatedCount = 0;\n let configErrors: string[] = [];\n try {\n const result = await applyConfigWithToken(newToken);\n updatedCount = result.updated;\n configErrors = result.errors;\n } catch (err) {\n console.error(\n `[tandem] Warning: failed to update MCP configs: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // TODO(v0.8.1): After rotation, re-walk Cowork workspaces to rewrite\n // env.TANDEM_AUTH_TOKEN so post-rotation Cowork sessions don't 401\n // (security invariant §6 — silent-failure H1). The Tauri IPC dynamic import\n // approach is inert here: this CLI runs as a Node subprocess with no WebView,\n // so `@tauri-apps/api/core`'s `invoke()` has no bridge to Rust. The fix is\n // an HTTP bridge — add a POST /api/cowork-apply-token endpoint in the server\n // (guarded by the auth middleware) and call it from here after the server\n // accepts the rotation.\n\n if (serverRejected) {\n // Configs now reference the new token but the server still holds the old one.\n // Print a strong warning — do NOT print \"Rotated auth token\" as that implies success.\n console.error(\n `[tandem] WARNING: server rejected the rotation request (status: ${serverRejectedStatus}).`,\n );\n if (updatedCount > 0) {\n console.error(\n ` ${updatedCount} config file(s) updated to the new token, but the server still\\n` +\n \" holds the old token. Restart the server to complete rotation.\",\n );\n }\n console.error(` Old fingerprint: ${fingerprint(oldToken)}`);\n console.error(` New fingerprint: ${fingerprint(newToken)}`);\n for (const e of configErrors) {\n console.error(` Warning: could not update config — ${e}`);\n }\n console.error(\"\");\n return;\n }\n\n console.error(\"[tandem] Rotated auth token.\");\n console.error(` Old fingerprint: ${fingerprint(oldToken)}`);\n console.error(` New fingerprint: ${fingerprint(newToken)}`);\n console.error(` Updated ${updatedCount} config file(s).`);\n\n for (const e of configErrors) {\n console.error(` Warning: could not update config — ${e}`);\n }\n\n if (graceWindowActive) {\n console.error(\n \" Old token remains valid for 60 seconds; reconnect Claude Code within that window.\",\n );\n } else {\n console.error(\n \" Server was not running — start it with `tandem` and reconnect Claude Code with the new token.\",\n );\n }\n\n console.error(\"\");\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst SERVER_DIST = resolve(__dirname, \"../server/index.js\");\n\nexport function runStart(): void {\n if (!existsSync(SERVER_DIST)) {\n console.error(`[Tandem] Server not found at ${SERVER_DIST}`);\n console.error(\"[Tandem] The installation may be corrupted. Try: npm install -g tandem-editor\");\n process.exit(1);\n }\n\n console.error(\"[Tandem] Starting server...\");\n\n const proc = spawn(\"node\", [SERVER_DIST], {\n stdio: \"inherit\",\n env: { ...process.env, TANDEM_OPEN_BROWSER: \"1\" },\n });\n\n proc.on(\"error\", (err) => {\n console.error(`[Tandem] Failed to start server: ${err.message}`);\n process.exit(1);\n });\n\n proc.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n\n // Forward signals — proc.kill() with no argument uses SIGTERM on Unix\n // and TerminateProcess on Windows (correct cross-platform behavior).\n // On Windows SIGTERM is not emitted by the OS, but SIGINT (Ctrl+C) works.\n // Both are listed for Unix compatibility.\n for (const sig of [\"SIGINT\", \"SIGTERM\"] as const) {\n process.once(sig, () => proc.kill());\n }\n}\n","/**\n * Tandem CLI — entry point for the `tandem` global command.\n * Shebang is added by tsup banner at build time.\n *\n * Usage:\n * tandem Start the Tandem server and open the editor\n * tandem setup Register Tandem MCP tools with Claude Code / Claude Desktop\n * tandem setup --force Register even if no Claude install is auto-detected\n * tandem --help Show this help\n * tandem --version Show version\n */\n\nimport updateNotifier from \"update-notifier\";\n\nprocess.once(\"uncaughtException\", (err: unknown) => {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err);\n try {\n process.stderr.write(`[tandem cli] uncaughtException: ${msg}\\n`);\n } catch {\n /* EPIPE */\n }\n process.exit(1);\n});\nprocess.once(\"unhandledRejection\", (reason: unknown) => {\n const detail = reason instanceof Error ? reason.message : String(reason);\n process.stderr.write(`[tandem cli] unhandledRejection: ${detail}\\n`);\n process.exit(1);\n});\n\n// Injected at build time by tsup define; declared here for TypeScript\ndeclare const __TANDEM_VERSION__: string;\nconst version = typeof __TANDEM_VERSION__ !== \"undefined\" ? __TANDEM_VERSION__ : \"0.0.0-dev\";\n\nconst args = process.argv.slice(2);\n\n// Skip the update notifier for stdio subcommands — the output is machine-consumed\n// by Claude Desktop's plugin loader, and any incidental write risks corrupting\n// the MCP wire or producing log noise no human will ever read.\nconst isStdioMode = args[0] === \"mcp-stdio\" || args[0] === \"channel\";\nif (!isStdioMode) {\n updateNotifier({ pkg: { name: \"tandem-editor\", version } }).notify();\n}\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`tandem v${version}\n\nUsage:\n tandem Start Tandem server and open the editor\n tandem setup Register MCP tools with Claude Code / Claude Desktop\n tandem setup --force Register to default paths regardless of detection\n tandem setup --with-channel-shim Also register the stdio channel shim (legacy opt-in)\n tandem rotate-token Rotate the auth token with a 60-second grace window\n tandem mcp-stdio Run as a stdio MCP server proxying to local HTTP\n (used by the plugin's Cowork bridge; requires\n tandem server running on the host)\n tandem channel Run the Tandem channel shim (stdio MCP)\n (used by the plugin's tandem-channel entry)\n tandem --version\n tandem --help\n`);\n process.exit(0);\n}\n\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(version);\n process.exit(0);\n}\n\ntry {\n if (args[0] === \"--uninstall-scrub\") {\n // Hidden subcommand invoked by the Tauri NSIS uninstaller hook. Walks\n // Cowork workspaces and removes Tandem plugin entries + firewall rules.\n // Runs inside the already-signed tandem.exe (security invariant §10 —\n // prevents binary-planting during uninstall).\n const { runUninstallScrub } = await import(\"./uninstall-scrub.js\");\n const exitCode = await runUninstallScrub();\n process.exit(exitCode);\n } else if (args[0] === \"setup\") {\n const { runSetup } = await import(\"./setup.js\");\n await runSetup({\n force: args.includes(\"--force\"),\n withChannelShim: args.includes(\"--with-channel-shim\"),\n });\n } else if (args[0] === \"mcp-stdio\") {\n const { runMcpStdio } = await import(\"./mcp-stdio.js\");\n await runMcpStdio();\n } else if (args[0] === \"channel\") {\n const { runChannelCli } = await import(\"./channel.js\");\n await runChannelCli();\n } else if (args[0] === \"rotate-token\") {\n const { rotateToken } = await import(\"./rotate-token.js\");\n await rotateToken();\n } else if (!args[0] || args[0] === \"start\") {\n const { runStart } = await import(\"./start.js\");\n runStart();\n } else {\n console.error(`Unknown command: ${args[0]}`);\n console.error(\"Run 'tandem --help' for usage.\");\n process.exit(1);\n }\n} catch (err) {\n console.error(`\\n[Tandem] Fatal error: ${err instanceof Error ? err.message : String(err)}`);\n console.error(\"If this persists, try reinstalling: npm install -g tandem-editor\\n\");\n process.exit(1);\n}\n"],"mappings":"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","names":["path","write","readFileSync","dirname","resolve","fileURLToPath","__dirname","VALID_TOKEN_RE","detail","buffered","StdioServerTransport","args","resolve","fs","path","fsPromises","path","existsSync","dirname","resolve","fileURLToPath","__dirname","runUninstallScrub","runSetup","runMcpStdio","runChannelCli","rotateToken","runStart"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/win-path-guard.ts","../../src/cli/uninstall-scrub.ts","../../src/shared/constants.ts","../../src/cli/skill-content.ts","../../src/cli/setup.ts","../../src/shared/cli-runtime.ts","../../src/cli/preflight.ts","../../src/cli/mcp-stdio.ts","../../src/shared/fetch-with-timeout.ts","../../src/shared/utils.ts","../../src/shared/events/types.ts","../../src/channel/event-bridge.ts","../../src/channel/run.ts","../../src/cli/channel.ts","../../src/shared/auth/token-file.ts","../../src/cli/rotate-token.ts","../../src/cli/start.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Windows workspace path guard — mirrors the Rust §3 invariant for TypeScript callers.\n *\n * Four steps (in order):\n * a. lstat each ancestor; reject any path whose chain contains a symlink.\n * b. fs.realpath() to canonicalize (safe: symlinks already rejected in (a)).\n * c. Reject UNC paths (\\\\server\\share or \\\\?\\UNC\\...; allow \\\\?\\C:\\...).\n * d. Component-wise containment check under realpath'd %LOCALAPPDATA%\n * (case-insensitive on Windows).\n *\n * Extracted into its own module so it can be unit-tested via vi.mock(\"node:fs\", ...).\n */\n\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\ntype Logger = { warn: (msg: string) => void };\n\n/**\n * Validate that `candidate` is a safe workspace path contained within `realLocalAppData`.\n *\n * @returns the realpath'd canonical path string on success, or null if rejected.\n * Callers are responsible for supplying a realpath'd `realLocalAppData`.\n */\nexport async function assertSafeWorkspacePath(\n candidate: string,\n realLocalAppData: string,\n logger?: Logger,\n): Promise<string | null> {\n const warn = (msg: string) => logger?.warn(`[path-guard] ${msg}`);\n\n // (a) lstat-walk: reject any component that is a symlink.\n if (await hasSymlinkInChain(candidate, warn)) {\n warn(`symlink/reparse point in chain: ${candidate}`);\n return null;\n }\n\n // (b) Canonicalize via realpath (safe now — symlinks already rejected).\n let real: string;\n try {\n real = await fs.realpath(candidate);\n } catch (err) {\n warn(`realpath failed for ${candidate}: ${(err as Error).message}`);\n return null;\n }\n\n // (c) Reject UNC paths.\n if (isUncPath(real)) {\n warn(`UNC path rejected: ${real}`);\n return null;\n }\n\n // (d) Component-wise containment under realLocalAppData (case-insensitive).\n if (!isComponentWiseChild(real, realLocalAppData)) {\n warn(`path outside %LOCALAPPDATA%: ${real}`);\n return null;\n }\n\n return real;\n}\n\n/** Returns true if any ancestor (inclusive) of `p` is a symbolic link. */\nasync function hasSymlinkInChain(p: string, warn: (m: string) => void): Promise<boolean> {\n // Walk from candidate up through all ancestors.\n let current = path.resolve(p);\n const visited = new Set<string>();\n\n while (true) {\n if (visited.has(current)) break;\n visited.add(current);\n\n try {\n const stat = await fs.lstat(current);\n if (stat.isSymbolicLink()) {\n return true;\n }\n } catch (err) {\n // lstat failed — fail closed for safety.\n warn(`lstat failed for ${current}: ${(err as Error).message}`);\n return true;\n }\n\n const parent = path.dirname(current);\n if (parent === current) break; // reached root\n current = parent;\n }\n\n return false;\n}\n\n/** Returns true if the path is a UNC path (\\\\server\\share or \\\\?\\UNC\\...). */\nfunction isUncPath(p: string): boolean {\n // Allow extended-length local paths (\\\\?\\C:\\...) but reject:\n // \\\\?\\UNC\\server\\share — extended UNC\n // \\\\server\\share — classic UNC\n if (p.startsWith(\"\\\\\\\\?\\\\UNC\\\\\") || p.startsWith(\"//?/UNC/\")) return true;\n if (\n (p.startsWith(\"\\\\\\\\\") && !p.startsWith(\"\\\\\\\\?\\\\\")) ||\n (p.startsWith(\"//\") && !p.startsWith(\"//?/\"))\n )\n return true;\n return false;\n}\n\n/**\n * Returns true if `child` is strictly within `root` on a component-wise basis\n * (case-insensitive on Windows).\n */\nfunction isComponentWiseChild(child: string, root: string): boolean {\n // Normalize separators and split on path.sep.\n const normalize = (p: string) => p.replace(/[\\\\/]+/g, path.sep).replace(/[/\\\\]$/, \"\");\n\n const rootNorm = normalize(root);\n const childNorm = normalize(child);\n\n const rootParts = rootNorm.split(path.sep);\n const childParts = childNorm.split(path.sep);\n\n if (childParts.length <= rootParts.length) return false;\n\n for (let i = 0; i < rootParts.length; i++) {\n // Case-insensitive on Windows.\n if (rootParts[i].toLowerCase() !== childParts[i].toLowerCase()) return false;\n }\n return true;\n}\n","/**\n * Tandem `--uninstall-scrub` subcommand.\n *\n * Invoked by the Tauri NSIS installer hook during uninstall. Walks every\n * Cowork workspace under `%LOCALAPPDATA%\\Packages\\Claude_*\\LocalCache\\\n * Roaming\\Claude\\local-agent-mode-sessions\\` and removes the Tandem plugin\n * entry from:\n * - `installed_plugins.json` (remove `mcpServers.tandem`)\n * - `known_marketplaces.json` (remove `marketplaces.tandem`)\n * - `cowork_settings.json` (remove `tandem@tandem` from `enabledPlugins`)\n *\n * Then removes the `Tandem Cowork*` Windows Firewall rules via `netsh`.\n *\n * **Security invariant §10 (ADR):** this runs INSIDE the already-signed\n * `tandem.exe` binary — NOT as a separate `uninstall_scrub.exe`. That\n * prevents binary-planting attacks during uninstall.\n *\n * **Failure policy:** logs every error, exits 0 on clean-or-not-installed,\n * non-zero only on unrecoverable I/O failures. NSIS logs the exit code but\n * does NOT block uninstall — Tandem must always uninstall even if a\n * workspace scrub partially fails.\n *\n * **Token safety:** this scrub READS JSON to find Tandem entries but the\n * removed-entry contents (including the auth token) are NEVER logged.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { promises as fsPromises } from \"node:fs\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { assertSafeWorkspacePath } from \"./win-path-guard.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst TANDEM_PLUGIN_ID = \"tandem\";\nconst TANDEM_ENABLED_KEY = \"tandem@tandem\";\nconst FIREWALL_ALLOW_RULE = \"Tandem Cowork\";\nconst FIREWALL_DENY_RULE = \"Tandem Cowork \\u2014 Deny (elevation refused)\";\n\ntype ScrubLogger = {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n close: () => Promise<void>;\n};\n\nasync function openLogger(): Promise<ScrubLogger> {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) {\n // No log file — just use stderr.\n const write = (level: string, msg: string): void => {\n process.stderr.write(`[tandem uninstall-scrub ${level}] ${msg}\\n`);\n };\n return {\n info: (m) => write(\"info\", m),\n warn: (m) => write(\"warn\", m),\n error: (m) => write(\"error\", m),\n close: async () => {},\n };\n }\n\n const logDir = path.join(localAppData, \"tandem\", \"Logs\");\n await fsPromises.mkdir(logDir, { recursive: true }).catch(() => {});\n const logPath = path.join(logDir, \"uninstall.log\");\n const stream = await fsPromises.open(logPath, \"a\").catch(() => null);\n\n const write = (level: string, msg: string): void => {\n const line = `[${new Date().toISOString()}] [${level}] ${msg}\\n`;\n process.stderr.write(line);\n if (stream) {\n stream.write(line).catch(() => {});\n }\n };\n\n return {\n info: (m) => write(\"info\", m),\n warn: (m) => write(\"warn\", m),\n error: (m) => write(\"error\", m),\n close: async () => {\n if (stream) await stream.close().catch(() => {});\n },\n };\n}\n\n/**\n * Find all Cowork workspace directories.\n *\n * Matches `%LOCALAPPDATA%\\Packages\\Claude_*\\LocalCache\\Roaming\\Claude\\\n * local-agent-mode-sessions\\<ws-id>\\<vm-id>\\`.\n *\n * Each candidate vm-path is validated by the 4-step Windows path guard before\n * being included — callers receive only safe, realpath'd paths.\n *\n * Returns an empty array on any error (e.g. Cowork not installed).\n */\nexport async function findCoworkWorkspaces(logger: ScrubLogger): Promise<string[]> {\n const localAppData = process.env.LOCALAPPDATA;\n if (!localAppData) {\n logger.info(\"%LOCALAPPDATA% not set — skipping workspace scan\");\n return [];\n }\n\n // Realpath %LOCALAPPDATA% once for use by the path guard.\n let realLad: string;\n try {\n realLad = await fsPromises.realpath(localAppData);\n } catch {\n realLad = localAppData; // best-effort fallback\n }\n\n const packagesDir = path.join(localAppData, \"Packages\");\n let packageEntries: string[];\n try {\n packageEntries = await fsPromises.readdir(packagesDir);\n } catch (err) {\n logger.info(`cannot read Packages dir: ${(err as Error).message}`);\n return [];\n }\n\n const claudePackages = packageEntries.filter((name) => name.startsWith(\"Claude_\"));\n if (claudePackages.length === 0) {\n logger.info(\"no Claude_* package directories found\");\n return [];\n }\n\n const workspaces: string[] = [];\n for (const pkg of claudePackages) {\n const sessionsRoot = path.join(\n packagesDir,\n pkg,\n \"LocalCache\",\n \"Roaming\",\n \"Claude\",\n \"local-agent-mode-sessions\",\n );\n\n let wsEntries: string[];\n try {\n wsEntries = await fsPromises.readdir(sessionsRoot);\n } catch (err) {\n logger.warn(`cannot read sessions root ${sessionsRoot}: ${(err as Error).message}`);\n continue;\n }\n\n for (const ws of wsEntries) {\n const wsPath = path.join(sessionsRoot, ws);\n let vmEntries: string[];\n try {\n vmEntries = await fsPromises.readdir(wsPath);\n } catch (err) {\n logger.warn(`cannot read workspace dir ${wsPath}: ${(err as Error).message}`);\n continue;\n }\n\n for (const vm of vmEntries) {\n const vmPath = path.join(wsPath, vm);\n try {\n const stat = await fsPromises.stat(vmPath);\n if (!stat.isDirectory()) continue;\n\n // 4-step path guard — only include validated, realpath'd paths.\n const safePath = await assertSafeWorkspacePath(vmPath, realLad, logger);\n if (safePath !== null) {\n workspaces.push(safePath);\n }\n } catch (err) {\n logger.warn(`cannot stat ${vmPath}: ${(err as Error).message}`);\n }\n }\n }\n }\n\n logger.info(`found ${workspaces.length} workspace(s)`);\n return workspaces;\n}\n\n/**\n * Atomically rewrite a JSON file, invoking `mutate` on the parsed object.\n *\n * Precondition: `filePath` has already been validated by the path guard.\n * This function trusts its callers (consistent with `with_locked_json` on the Rust side).\n *\n * Returns true if the mutation changed something and was written, false if\n * the file was absent or unchanged.\n */\nexport async function rewriteJson(\n filePath: string,\n mutate: (obj: Record<string, unknown>) => boolean,\n logger: ScrubLogger,\n): Promise<boolean> {\n let content: string;\n try {\n content = await fsPromises.readFile(filePath, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n logger.warn(`cannot read ${filePath}: ${(err as Error).message}`);\n return false;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch (err) {\n logger.warn(`invalid JSON in ${filePath}: ${(err as Error).message}`);\n return false;\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n logger.warn(`${filePath} is not a JSON object — skipping`);\n return false;\n }\n\n const changed = mutate(parsed as Record<string, unknown>);\n if (!changed) {\n return false;\n }\n\n const dir = path.dirname(filePath);\n const tmpName = `.tandem-scrub-tmp-${Math.random().toString(36).slice(2, 10)}`;\n const tmpPath = path.join(dir, tmpName);\n\n try {\n await fsPromises.writeFile(tmpPath, JSON.stringify(parsed, null, 2), \"utf8\");\n await fsPromises.rename(tmpPath, filePath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => {});\n throw err;\n }\n return true;\n}\n\n/**\n * Remove the Tandem entry from `installed_plugins.json`.\n */\nexport function removeInstalledPlugins(obj: Record<string, unknown>): boolean {\n let changed = false;\n for (const key of [\"mcpServers\", \"servers\"]) {\n const servers = obj[key];\n if (typeof servers === \"object\" && servers !== null && !Array.isArray(servers)) {\n const map = servers as Record<string, unknown>;\n if (TANDEM_PLUGIN_ID in map) {\n delete map[TANDEM_PLUGIN_ID];\n changed = true;\n }\n }\n }\n return changed;\n}\n\n/**\n * Remove the Tandem marketplace entry from `known_marketplaces.json`.\n */\nexport function removeKnownMarketplaces(obj: Record<string, unknown>): boolean {\n const mp = obj.marketplaces;\n if (typeof mp === \"object\" && mp !== null && !Array.isArray(mp)) {\n const map = mp as Record<string, unknown>;\n if (TANDEM_PLUGIN_ID in map) {\n delete map[TANDEM_PLUGIN_ID];\n return true;\n }\n }\n return false;\n}\n\n/**\n * Remove `tandem@tandem` from `enabledPlugins` in `cowork_settings.json`.\n */\nexport function removeCoworkSettings(obj: Record<string, unknown>): boolean {\n const enabled = obj.enabledPlugins;\n if (Array.isArray(enabled)) {\n const before = enabled.length;\n obj.enabledPlugins = enabled.filter((v) => v !== TANDEM_ENABLED_KEY);\n return (obj.enabledPlugins as unknown[]).length < before;\n }\n if (typeof enabled === \"object\" && enabled !== null) {\n const map = enabled as Record<string, unknown>;\n if (TANDEM_ENABLED_KEY in map) {\n delete map[TANDEM_ENABLED_KEY];\n return true;\n }\n }\n return false;\n}\n\n/**\n * Delete a Windows Firewall rule by name. Non-existent rules are not an error.\n */\nasync function deleteFirewallRule(name: string, logger: ScrubLogger): Promise<void> {\n try {\n await execFileAsync(\"netsh\", [\"advfirewall\", \"firewall\", \"delete\", \"rule\", `name=${name}`]);\n logger.info(`deleted firewall rule: ${name}`);\n } catch (err) {\n const e = err as NodeJS.ErrnoException & { stdout?: string; stderr?: string };\n // netsh returns exit code 1 when no rule matches — not fatal.\n const stdoutStr = e.stdout ?? \"\";\n if (stdoutStr.includes(\"No rules match\")) {\n logger.info(`no firewall rule to delete: ${name}`);\n return;\n }\n logger.warn(\n `failed to delete firewall rule ${name}: ${e.message ?? String(err)} ` +\n `(stdout: ${stdoutStr.trim().slice(0, 200)})`,\n );\n }\n}\n\n/**\n * Main entry — Windows-only.\n */\nexport async function runUninstallScrub(): Promise<number> {\n const logger = await openLogger();\n\n logger.info(\"Tandem uninstall scrub starting\");\n\n if (process.platform !== \"win32\") {\n logger.info(`platform ${process.platform} is not win32 — skipping Cowork scrub`);\n await logger.close();\n return 0;\n }\n\n let failures = 0;\n\n try {\n const workspaces = await findCoworkWorkspaces(logger);\n for (const ws of workspaces) {\n const pluginsDir = path.join(ws, \"cowork_plugins\");\n try {\n await rewriteJson(\n path.join(pluginsDir, \"installed_plugins.json\"),\n removeInstalledPlugins,\n logger,\n );\n await rewriteJson(\n path.join(pluginsDir, \"known_marketplaces.json\"),\n removeKnownMarketplaces,\n logger,\n );\n await rewriteJson(\n path.join(pluginsDir, \"cowork_settings.json\"),\n removeCoworkSettings,\n logger,\n );\n } catch (err) {\n logger.error(`scrub failed for ${ws}: ${(err as Error).message}`);\n failures++;\n }\n }\n\n await deleteFirewallRule(FIREWALL_ALLOW_RULE, logger);\n await deleteFirewallRule(FIREWALL_DENY_RULE, logger);\n\n logger.info(`scrub complete: ${workspaces.length} workspace(s), ${failures} failure(s)`);\n } catch (err) {\n logger.error(`scrub fatal error: ${(err as Error).message}`);\n failures++;\n }\n\n await logger.close();\n\n // Exit 0 on clean-or-not-installed. Non-zero only on unrecoverable failures —\n // and even then, NSIS does NOT block uninstall; it just records the code.\n return failures > 0 ? 1 : 0;\n}\n","export const DEFAULT_WS_PORT = 3478;\nexport const DEFAULT_MCP_PORT = 3479;\n\nexport const TANDEM_REPO_URL = \"https://github.com/bloknayrb/tandem\";\nexport const TANDEM_ISSUES_NEW_URL = `${TANDEM_REPO_URL}/issues/new`;\n\n/** File extensions the server accepts for opening. */\nexport const SUPPORTED_EXTENSIONS = new Set([\".md\", \".txt\", \".html\", \".htm\", \".docx\"]);\nexport const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB\nexport const MAX_WS_PAYLOAD = 10 * 1024 * 1024; // 10MB\nexport const MAX_WS_CONNECTIONS = 4;\nexport const IDLE_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nexport const SESSION_MAX_AGE = 30 * 24 * 60 * 60 * 1000; // 30 days\nexport const TYPING_DEBOUNCE = 3000; // 3 seconds\nexport const DISCONNECT_DEBOUNCE_MS = 3000; // 3 seconds before showing \"server not reachable\"\nexport const PROLONGED_DISCONNECT_MS = 30_000; // 30 seconds before showing App-level disconnect banner\nexport const OVERLAY_STALE_DEBOUNCE = 200; // 200ms\n\nimport type { HighlightColor } from \"./types.js\";\n\nexport const HIGHLIGHT_COLORS: Record<HighlightColor, string> = {\n yellow: \"rgba(255, 235, 59, 0.3)\",\n green: \"rgba(76, 175, 80, 0.3)\",\n blue: \"rgba(33, 150, 243, 0.3)\",\n pink: \"rgba(236, 72, 153, 0.3)\",\n};\n\nexport const HIGHLIGHT_COLOR_VARS: Record<HighlightColor, string> = {\n yellow: \"var(--tandem-highlight-yellow)\",\n green: \"var(--tandem-highlight-green)\",\n blue: \"var(--tandem-highlight-blue)\",\n pink: \"var(--tandem-highlight-pink)\",\n};\n\nexport function normalizeHighlightColor(color: string | null | undefined): HighlightColor {\n return color && color in HIGHLIGHT_COLORS ? (color as HighlightColor) : \"yellow\";\n}\n\nexport const TANDEM_MODE_DEFAULT = \"tandem\" as const;\nexport const TANDEM_MODE_KEY = \"tandem:mode\";\nexport const TANDEM_SETTINGS_KEY = \"tandem:settings\";\n// Panel-width localStorage keys.\n//\n// NOTE: these use legacy hyphen naming (vs the neighboring colon convention\n// `tandem:mode`/`tandem:settings`) because they predate the colon scheme and\n// changing the strings would invalidate every existing user's saved widths.\n// Do not \"fix\" the style — the key string is the persistence contract.\n//\n// Right-side panel width is shared between the tabbed layout and the\n// three-panel right panel. The left key only applies in three-panel mode.\nexport const PANEL_WIDTH_KEY = \"tandem-panel-width\";\nexport const LEFT_PANEL_WIDTH_KEY = \"tandem-left-panel-width\";\n\nexport type PanelSide = \"left\" | \"right\";\n\n/**\n * Maps a panel side to its localStorage key. Using a Record instead of two\n * bare constants makes the \"both handles write to the same key\" regression\n * (#228) structurally impossible — you can't accidentally map both sides to\n * the same value at a callsite.\n *\n * Uses `as const satisfies Record<PanelSide, string>` so the value type stays\n * as the literal strings rather than widening to `string` — this preserves\n * the persistence-key identity at every callsite while still enforcing\n * exhaustive coverage of `PanelSide`.\n */\nexport const PANEL_WIDTH_KEYS = {\n left: LEFT_PANEL_WIDTH_KEY,\n right: PANEL_WIDTH_KEY,\n} as const satisfies Record<PanelSide, string>;\nexport const SELECTION_DWELL_DEFAULT_MS = 1000;\nexport const SELECTION_DWELL_MIN_MS = 500;\nexport const SELECTION_DWELL_MAX_MS = 3000;\n\n// Large file thresholds\nexport const CHARS_PER_PAGE = 3_000;\nexport const LARGE_FILE_PAGE_THRESHOLD = 50;\nexport const VERY_LARGE_FILE_PAGE_THRESHOLD = 100;\n\nexport const CTRL_ROOM = \"__tandem_ctrl__\";\n\n/** Y.Map key constants — centralized to prevent silent bugs from string typos. */\nexport const Y_MAP_ANNOTATIONS = \"annotations\";\nexport const Y_MAP_AWARENESS = \"awareness\";\nexport const Y_MAP_USER_AWARENESS = \"userAwareness\";\nexport const Y_MAP_MODE = \"mode\";\nexport const Y_MAP_DWELL_MS = \"selectionDwellMs\";\nexport const Y_MAP_CHAT = \"chat\";\nexport const Y_MAP_DOCUMENT_META = \"documentMeta\";\nexport const Y_MAP_ANNOTATION_REPLIES = \"annotationReplies\";\nexport const Y_MAP_SAVED_AT_VERSION = \"savedAtVersion\";\nexport const Y_MAP_AUTHORSHIP = \"authorship\";\n// Y.Map sub-keys: userAwareness\nexport const Y_MAP_SELECTION = \"selection\";\nexport const Y_MAP_ACTIVITY = \"activity\";\n// Y.Map sub-keys: awareness (Claude focus)\nexport const Y_MAP_CLAUDE = \"claude\";\n// Y.Map sub-keys: documentMeta\nexport const Y_MAP_OPEN_DOCUMENTS = \"openDocuments\";\nexport const Y_MAP_ACTIVE_DOCUMENT_ID = \"activeDocumentId\";\nexport const Y_MAP_GENERATION_ID = \"generationId\";\nexport const Y_MAP_READ_ONLY = \"readOnly\";\nexport const Y_MAP_STORE_READ_ONLY = \"storeReadOnly\";\n\nexport const AUTHORSHIP_TOGGLE_KEY = \"tandem:showAuthorship\";\n\nexport const SERVER_INFO_DIR = \".tandem\";\nexport const SERVER_INFO_FILE = \".tandem/.server-info\";\n\nexport const RECENT_FILES_KEY = \"tandem:recentFiles\";\nexport const RECENT_FILES_CAP = 20;\n\nexport const USER_NAME_KEY = \"tandem:userName\";\nexport const USER_NAME_DEFAULT = \"You\";\nexport const USER_NAME_EVENT = \"tandem:user-name-changed\";\nexport const USER_NAME_MAX_LEN = 40;\n\n// Toast notifications\nexport const TOAST_DISMISS_MS = { error: 8000, warning: 6000, info: 4000 } as const;\nexport const MAX_VISIBLE_TOASTS = 5;\nexport const NOTIFICATION_BUFFER_SIZE = 50;\n\n// Onboarding tutorial\nexport const TUTORIAL_COMPLETED_KEY = \"tandem:tutorialCompleted\";\nexport const TUTORIAL_ANNOTATION_PREFIX = \"tutorial-\";\n/** Persists \"user skipped the Cowork onboarding step\" across sessions. */\nexport const COWORK_ONBOARDING_SKIPPED_KEY = \"tandem:coworkOnboardingSkipped\";\n/** Polling interval for `cowork_get_status` while the consumer is active. */\nexport const COWORK_STATUS_POLL_MS = 30_000;\n/** Debounce interval for the \"Re-scan workspaces\" button. */\nexport const COWORK_RESCAN_DEBOUNCE_MS = 2_000;\n\n// Editor layout\nexport const EDITOR_WIDTH_MODE_KEY = \"tandem:editorWidthMode\";\n\n// Channel / event queue\nexport const CHANNEL_EVENT_BUFFER_SIZE = 200;\nexport const CHANNEL_EVENT_BUFFER_AGE_MS = 60_000; // 60 seconds\nexport const CHANNEL_SSE_KEEPALIVE_MS = 15_000; // 15 seconds\nexport const CHANNEL_MAX_RETRIES = 5;\nexport const CHANNEL_RETRY_DELAY_MS = 2_000;\n\n// Channel shim per-request timeouts. Mirror the monitor pattern (#364) so a\n// half-open Tandem server can't wedge `tandem_reply`, the permission relay,\n// or the event-bridge SSE handshake / awareness / mode / error-report POSTs.\n// Intentionally separate constants per endpoint so a slow endpoint doesn't\n// hold up a faster one — and so log lines name a meaningful threshold.\nexport const CHANNEL_CONNECT_FETCH_TIMEOUT_MS = 10_000; // /api/events handshake\nexport const CHANNEL_SSE_INACTIVITY_TIMEOUT_MS = 60_000; // No-bytes watchdog on SSE body\nexport const CHANNEL_MODE_FETCH_TIMEOUT_MS = 2_000; // /api/mode cache refresh\nexport const CHANNEL_AWARENESS_FETCH_TIMEOUT_MS = 5_000; // /api/channel-awareness POST\nexport const CHANNEL_ERROR_REPORT_TIMEOUT_MS = 3_000; // /api/channel-error POST on exit\nexport const CHANNEL_REPLY_FETCH_TIMEOUT_MS = 5_000; // /api/channel-reply (tandem_reply)\nexport const CHANNEL_PERMISSION_FETCH_TIMEOUT_MS = 5_000; // /api/channel-permission relay\n// Bound the SSE buffer so a misbehaving server that never emits frame\n// boundaries can't wedge the bridge with unbounded string growth.\nexport const CHANNEL_MAX_SSE_BUFFER_BYTES = 1_000_000;\n\n/** Auth token filename inside the app-data directory. */\nexport const TOKEN_FILE_NAME = \"auth-token\";\n\n/** Default MCP bind host — loopback only by default. */\nexport const DEFAULT_BIND_HOST = \"127.0.0.1\";\n\n/** Env var name to opt in to unauthenticated LAN binding. */\nexport const TANDEM_ALLOW_UNAUTHENTICATED_LAN_ENV = \"TANDEM_ALLOW_UNAUTHENTICATED_LAN\";\n\n/** Tauri WebView origin hostname — must be accepted alongside localhost. */\nexport const TAURI_HOSTNAME = \"tauri.localhost\";\n\n// Zoom persistence (Tauri desktop)\nexport const ZOOM_STORAGE_KEY = \"tandem:zoomLevel\";\nexport const ZOOM_MIN = 0.5;\nexport const ZOOM_MAX = 2.0;\nexport const ZOOM_DEFAULT = 1.0;\n","/**\n * SKILL.md content installed to ~/.claude/skills/tandem/ by `tandem setup`.\n * Single source of truth lives at `skills/tandem/SKILL.md`. This module\n * reads that file at module load so the plugin install path and the\n * `tandem setup` install path always deliver byte-identical content.\n *\n * The file is shipped via package.json `files: [\"skills/\", ...]`, and the\n * CLI entry (dist/cli/index.js) is not self-contained — so at runtime the\n * relative path `../../skills/tandem/SKILL.md` resolves from either\n * dist/cli/ (tsx dev) or dist/cli/ (npm install) to the package-root\n * `skills/tandem/SKILL.md`.\n */\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst SKILL_PATH = resolve(__dirname, \"../../skills/tandem/SKILL.md\");\n\nexport const SKILL_CONTENT = readFileSync(SKILL_PATH, \"utf-8\");\n","import { randomUUID } from \"node:crypto\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { copyFile, mkdir, rename, unlink, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { DEFAULT_MCP_PORT } from \"../shared/constants.js\";\nimport { SKILL_CONTENT } from \"./skill-content.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// Paths are anchored to the package root (dist/cli/ resolves up two levels).\nconst PACKAGE_ROOT = resolve(__dirname, \"../..\");\nconst CHANNEL_DIST = resolve(PACKAGE_ROOT, \"dist/channel/index.js\");\n\nconst MCP_URL = `http://localhost:${DEFAULT_MCP_PORT}`;\n\nexport interface McpEntry {\n type?: \"http\";\n url?: string;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n headers?: Record<string, string>;\n}\n\nexport interface McpEntries {\n tandem: McpEntry;\n \"tandem-channel\"?: McpEntry;\n}\n\nexport interface BuildMcpEntriesOptions {\n /** Include the legacy stdio channel shim. Defaults to false — the plugin\n * monitor handles event push for modern installs. Users on older setups\n * can run `tandem setup --with-channel-shim` to preserve the shim. */\n withChannelShim?: boolean;\n nodeBinary?: string;\n /** Auth token to embed in HTTP entry headers and stdio shim env.\n * When omitted (first-run before token provisioned), headers/env are omitted\n * and backward compatibility is preserved. */\n token?: string;\n /** Target kind controls entry shape. Claude Code uses HTTP (direct);\n * Claude Desktop uses stdio (npx bridge) because Cowork sessions can\n * only surface stdio MCP servers. */\n targetKind?: TargetKind;\n}\n\nexport function buildMcpEntries(\n channelPath: string,\n opts: BuildMcpEntriesOptions = {},\n): McpEntries {\n const isDesktop = opts.targetKind === \"claude-desktop\";\n\n let tandemEntry: McpEntry;\n if (isDesktop) {\n const env: Record<string, string> = { TANDEM_URL: MCP_URL };\n if (opts.token) {\n env.TANDEM_AUTH_TOKEN = opts.token;\n }\n tandemEntry = {\n command: \"npx\",\n args: [\"-y\", \"tandem-editor\", \"mcp-stdio\"],\n env,\n };\n } else {\n tandemEntry = { type: \"http\", url: `${MCP_URL}/mcp` };\n if (opts.token) {\n tandemEntry.headers = { Authorization: `Bearer ${opts.token}` };\n }\n }\n const entries: McpEntries = { tandem: tandemEntry };\n\n if (opts.withChannelShim) {\n const shimEnv: Record<string, string> = { TANDEM_URL: MCP_URL };\n if (opts.token) {\n shimEnv.TANDEM_AUTH_TOKEN = opts.token;\n }\n entries[\"tandem-channel\"] = {\n command: opts.nodeBinary ?? \"node\",\n args: [channelPath],\n env: shimEnv,\n };\n }\n return entries;\n}\n\nexport type TargetKind = \"claude-code\" | \"claude-desktop\";\n\nexport interface DetectedTarget {\n label: string;\n configPath: string;\n kind: TargetKind;\n}\n\nexport interface DetectOptions {\n homeOverride?: string;\n localAppDataOverride?: string;\n force?: boolean;\n}\n\nexport function detectTargets(opts: DetectOptions = {}): DetectedTarget[] {\n const home = opts.homeOverride ?? homedir();\n const targets: DetectedTarget[] = [];\n\n // Claude Code — cross-platform.\n // MCP servers are configured in ~/.claude.json under the \"mcpServers\" key.\n // Detect if the file exists OR if ~/.claude directory exists (Claude Code is installed).\n // With --force, always include regardless.\n const claudeCodeConfig = join(home, \".claude.json\");\n const claudeCodeDir = join(home, \".claude\");\n if (opts.force || existsSync(claudeCodeConfig) || existsSync(claudeCodeDir)) {\n targets.push({ label: \"Claude Code\", configPath: claudeCodeConfig, kind: \"claude-code\" });\n }\n\n // Claude Desktop — platform-specific.\n // Only detect if the config file already exists (user has launched Desktop at least once).\n // With --force, always include.\n let desktopConfig: string | null = null;\n if (process.platform === \"win32\") {\n const appdata = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n desktopConfig = join(appdata, \"Claude\", \"claude_desktop_config.json\");\n } else if (process.platform === \"darwin\") {\n desktopConfig = join(\n home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n } else {\n desktopConfig = join(home, \".config\", \"claude\", \"claude_desktop_config.json\");\n }\n\n if (desktopConfig && (opts.force || existsSync(desktopConfig))) {\n targets.push({ label: \"Claude Desktop\", configPath: desktopConfig, kind: \"claude-desktop\" });\n }\n\n // Claude Desktop (MSIX) — Windows only.\n // MSIX-packaged installs (Microsoft Store) redirect %APPDATA% to a per-package\n // LocalCache dir. The config lives under %LOCALAPPDATA%\\Packages\\Claude_*\\\n // LocalCache\\Roaming\\Claude\\. Multiple package families may exist.\n if (process.platform === \"win32\") {\n const localAppData =\n opts.localAppDataOverride ?? process.env.LOCALAPPDATA ?? join(home, \"AppData\", \"Local\");\n const packagesDir = join(localAppData, \"Packages\");\n try {\n const entries = readdirSync(packagesDir);\n for (const pkg of entries.filter((n) => n.startsWith(\"Claude_\"))) {\n const msixConfig = join(\n packagesDir,\n pkg,\n \"LocalCache\",\n \"Roaming\",\n \"Claude\",\n \"claude_desktop_config.json\",\n );\n if (opts.force || existsSync(msixConfig)) {\n const suffix =\n entries.filter((n) => n.startsWith(\"Claude_\")).length > 1\n ? ` (${pkg.slice(0, 12)}…)`\n : \"\";\n targets.push({\n label: `Claude Desktop MSIX${suffix}`,\n configPath: msixConfig,\n kind: \"claude-desktop\",\n });\n }\n }\n } catch {\n // %LOCALAPPDATA%\\Packages doesn't exist or isn't readable — not an MSIX install\n }\n }\n\n return targets;\n}\n\n/**\n * Atomic write: write to a temp file in the SAME directory as the destination,\n * then rename. Using the same directory avoids EXDEV errors on Windows when\n * %TEMP% and %APPDATA% are on different drives.\n */\nasync function atomicWrite(content: string, dest: string): Promise<void> {\n const tmp = join(dirname(dest), `.tandem-setup-${randomUUID()}.tmp`);\n await writeFile(tmp, content, \"utf-8\");\n try {\n await rename(tmp, dest);\n } catch (err) {\n // EXDEV: cross-device link — fall back to copy + delete\n if ((err as NodeJS.ErrnoException).code === \"EXDEV\") {\n await copyFile(tmp, dest);\n await unlink(tmp).catch((cleanupErr: Error) => {\n console.error(` Warning: could not remove temp file ${tmp}: ${cleanupErr.message}`);\n });\n } else {\n await unlink(tmp).catch((cleanupErr: Error) => {\n console.error(` Warning: could not remove temp file ${tmp}: ${cleanupErr.message}`);\n });\n throw err;\n }\n }\n}\n\nexport async function applyConfig(configPath: string, entries: McpEntries): Promise<void> {\n // Read existing config or start fresh — no existsSync guard needed.\n // ENOENT and malformed JSON start fresh; other errors (permissions, disk) propagate.\n let existing: { mcpServers?: Record<string, McpEntry> } = {};\n try {\n existing = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n // File doesn't exist yet — start fresh\n } else if (err instanceof SyntaxError) {\n // Don't silently wipe the user's other mcpServers. Copy the malformed\n // file to a .broken-<ts> sibling first so they can recover it. If the\n // backup itself fails, refuse to overwrite — runSetup's per-target\n // try/catch reports the partial failure.\n const backupPath = `${configPath}.broken-${Date.now()}`;\n try {\n await copyFile(configPath, backupPath);\n console.error(\n ` Warning: ${configPath} contains malformed JSON — backed up to ${basename(backupPath)}, replacing with fresh config`,\n );\n } catch (copyErr) {\n console.error(\n ` Warning: ${configPath} contains malformed JSON and backup failed (${\n copyErr instanceof Error ? copyErr.message : copyErr\n }) — refusing to overwrite. Fix the JSON manually and rerun 'tandem setup'.`,\n );\n throw copyErr;\n }\n } else {\n throw err; // Permission errors, disk errors, etc. should not be silently swallowed\n }\n }\n\n const merged = {\n ...(existing.mcpServers ?? {}),\n ...entries,\n };\n // Remove stale tandem-channel entry left by older Tauri installers.\n // The channel shim is Claude Code-only; Cowork can't use it.\n if (!entries[\"tandem-channel\"]) {\n if (merged[\"tandem-channel\"]) {\n console.error(\n ` Warning: removed stale tandem-channel entry from ${configPath} (legacy Tauri install artifact)`,\n );\n }\n delete merged[\"tandem-channel\"];\n }\n const updated = { ...existing, mcpServers: merged };\n\n await mkdir(dirname(configPath), { recursive: true });\n await atomicWrite(JSON.stringify(updated, null, 2) + \"\\n\", configPath);\n}\n\n/**\n * Install the Tandem skill to ~/.claude/skills/tandem/SKILL.md.\n * Claude Code auto-discovers skills in this directory and uses the description\n * field to trigger them when tandem_* tools are present.\n */\nexport async function installSkill(opts: { homeOverride?: string } = {}): Promise<void> {\n const home = opts.homeOverride ?? homedir();\n const skillPath = join(home, \".claude\", \"skills\", \"tandem\", \"SKILL.md\");\n await mkdir(dirname(skillPath), { recursive: true });\n await atomicWrite(SKILL_CONTENT, skillPath);\n}\n\n/**\n * Returns true if the channel-shim build artifact exists at the given path.\n * Exported so the prereq check can be tested without spawning runSetup.\n */\nexport function validateChannelShimPrereq(channelPath: string): boolean {\n return existsSync(channelPath);\n}\n\n/**\n * Write the given token into all detected Claude MCP config files.\n * Returns the number of configs successfully updated and any per-target errors.\n */\nexport async function applyConfigWithToken(\n token: string | null,\n opts: { force?: boolean; withChannelShim?: boolean } = {},\n): Promise<{ updated: number; errors: string[] }> {\n const targets = detectTargets({ force: opts.force });\n\n let updated = 0;\n const errors: string[] = [];\n for (const t of targets) {\n const entries = buildMcpEntries(CHANNEL_DIST, {\n withChannelShim: opts.withChannelShim,\n token: token ?? undefined,\n targetKind: t.kind,\n });\n try {\n await applyConfig(t.configPath, entries);\n updated++;\n } catch (err) {\n errors.push(`${t.label}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n return { updated, errors };\n}\n\n/** Run the setup command. Writes MCP config to all detected Claude installs. */\nexport async function runSetup(\n opts: { force?: boolean; withChannelShim?: boolean } = {},\n): Promise<void> {\n console.error(\"\\nTandem Setup\\n\");\n\n if (opts.withChannelShim && !validateChannelShimPrereq(CHANNEL_DIST)) {\n console.error(\n `Error: --with-channel-shim requires dist/channel/index.js at ${CHANNEL_DIST}\\n` +\n `Run 'npm run build' first, or drop --with-channel-shim to use the plugin monitor.`,\n );\n process.exit(1);\n }\n\n console.error(\"Detecting Claude installations...\");\n\n const targets = detectTargets({ force: opts.force });\n\n if (targets.length === 0) {\n console.error(\n \" No Claude installations detected.\\n\" +\n \" If Claude Code is installed, ensure ~/.claude exists.\\n\" +\n \" You can force configuration to default paths with: tandem setup --force\",\n );\n return;\n }\n\n for (const t of targets) {\n console.error(` Found: ${t.label} (${t.configPath})`);\n }\n\n console.error(\"\\nWriting MCP configuration...\");\n\n let failures = 0;\n for (const t of targets) {\n const entries = buildMcpEntries(CHANNEL_DIST, {\n withChannelShim: opts.withChannelShim,\n targetKind: t.kind,\n });\n try {\n await applyConfig(t.configPath, entries);\n console.error(` \\x1b[32m✓\\x1b[0m ${t.label}`);\n } catch (err) {\n failures++;\n console.error(\n ` \\x1b[31m✗\\x1b[0m ${t.label}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (failures === targets.length) {\n console.error(\"\\nSetup failed — could not write any configuration. Check file permissions.\");\n process.exit(1);\n } else if (failures > 0) {\n console.error(\n `\\nSetup partially complete (${failures} target(s) failed). Start Tandem with: tandem`,\n );\n } else {\n console.error(\"\\nSetup complete! Start Tandem with: tandem\");\n console.error(\"Then in Claude, your tandem_* tools will be available.\");\n }\n\n // Install Claude Code skill (best-effort — doesn't block MCP setup)\n console.error(\"\\nInstalling Claude Code skill...\");\n try {\n await installSkill();\n console.error(\" \\x1b[32m✓\\x1b[0m ~/.claude/skills/tandem/SKILL.md\");\n } catch (err) {\n console.error(\n ` \\x1b[33m⚠\\x1b[0m Could not install skill: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Plugin install instructions (shown on all successful setups)\n if (failures < targets.length) {\n const pluginManifest = join(PACKAGE_ROOT, \".claude-plugin\", \"plugin.json\");\n const devInstructions = existsSync(pluginManifest)\n ? ` Or for development, load directly from this package:\\n\\n` +\n ` claude --plugin-dir ${PACKAGE_ROOT}\\n\\n`\n : ` (Development plugin dir not found at ${pluginManifest}; skipping local-plugin instructions.)\\n\\n`;\n\n console.error(\n \"\\n\\x1b[1mReal-time push notifications (recommended):\\x1b[0m\\n\" +\n \" Install the Tandem plugin for instant events (one-time):\\n\\n\" +\n \" claude plugin marketplace add bloknayrb/tandem\\n\" +\n \" claude plugin install tandem@tandem-editor\\n\\n\" +\n devInstructions +\n \" Without the plugin, Claude still works but relies on tandem_checkInbox polling.\\n\",\n );\n }\n}\n","/**\n * Helpers shared by CLI stdio entry points (`mcp-stdio`, `channel`) and the\n * standalone server / monitor binaries that all speak MCP over stdout.\n */\n\nimport { DEFAULT_MCP_PORT } from \"./constants.js\";\n\n/**\n * In stdio MCP mode, stdout is the JSON-RPC wire — any stray library write\n * corrupts the protocol. Redirect `console.log/warn/info` to stderr so\n * incidental logging is safe. Callers that also run in-process tests (e.g.\n * `src/monitor/index.ts`) can gate this behind `!process.env.VITEST`.\n */\nexport function redirectConsoleToStderr(): void {\n console.log = console.error;\n console.warn = console.error;\n console.info = console.error;\n}\n\n/**\n * Resolve the Tandem HTTP base URL used by stdio subcommands. Precedence:\n * (1) explicit override (programmatic, e.g. from tests)\n * (2) CLAUDE_PLUGIN_OPTION_SERVER_URL — injected by plugin host from userConfig\n * (3) TANDEM_URL — explicit env override\n * (4) localhost default\n * Blank values are treated as absent so a blank plugin option does not mask an\n * explicit TANDEM_URL or the localhost default.\n * The returned string has no trailing slash so callers can concatenate\n * `/health`, `/mcp`, etc. without double-slash. One or more trailing slashes\n * are stripped, so both `http://x/` and `http://x//` resolve to `http://x`.\n */\nexport function resolveTandemUrl(override?: string): string {\n return resolveTandemUrlCandidate(override).replace(/\\/+$/, \"\");\n}\n\nfunction resolveTandemUrlCandidate(override?: string): string {\n const candidates = [\n override,\n process.env.CLAUDE_PLUGIN_OPTION_SERVER_URL,\n process.env.TANDEM_URL,\n ];\n for (const url of candidates) {\n if (url !== undefined && url.trim() !== \"\") return url.trim();\n }\n return `http://localhost:${DEFAULT_MCP_PORT}`;\n}\n\n/**\n * Resolve the Tandem auth token. Precedence:\n * (1) explicit override (programmatic, e.g. from tests)\n * (2) CLAUDE_PLUGIN_OPTION_AUTH_TOKEN — injected by plugin host from userConfig\n * (3) TANDEM_AUTH_TOKEN — explicit env override\n * Blank values are treated as absent so a blank plugin option does not mask an\n * explicit TANDEM_AUTH_TOKEN. Returns undefined when all absent (loopback mode,\n * no Authorization header sent).\n */\nexport function resolveAuthToken(override?: string): string | undefined {\n return resolveAuthTokenCandidate(override).token;\n}\n\nexport type AuthTokenSource =\n | \"explicit override\"\n | \"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\"\n | \"TANDEM_AUTH_TOKEN\";\n\nexport function resolveAuthTokenCandidate(\n override?: string,\n): { token: string; source: AuthTokenSource } | { token: undefined; source: undefined } {\n const candidates: Array<[AuthTokenSource, string | undefined]> = [\n [\"explicit override\", override],\n [\"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\", process.env.CLAUDE_PLUGIN_OPTION_AUTH_TOKEN],\n [\"TANDEM_AUTH_TOKEN\", process.env.TANDEM_AUTH_TOKEN],\n ];\n for (const [source, token] of candidates) {\n if (token !== undefined && token.trim() !== \"\") return { token, source };\n }\n return { token: undefined, source: undefined };\n}\n\n/** Regex for a valid Tandem auth token (32+ URL-safe alphanumeric chars). */\nconst VALID_TOKEN_RE = /^[A-Za-z0-9_\\-]{32,}$/;\n\n/** Guard so we only warn once per process (not on every SSE reconnect). */\nlet _warnedInvalidToken = false;\n\n/**\n * Fetch wrapper that automatically injects `Authorization: Bearer <token>`\n * when a resolved Tandem auth token is set and valid.\n *\n * This is the forgiving variant — used by monitor/channel which may run in\n * loopback-only mode without a token. Invalid or absent tokens are silently\n * ignored (no exit-1). The strict validation lives in mcp-stdio.ts only.\n * When the token is set but fails validation, a one-time warning is emitted\n * so operators know why auth headers are absent.\n */\nexport async function authFetch(url: string, init?: RequestInit): Promise<Response> {\n const { token, source } = resolveAuthTokenCandidate();\n if (token !== undefined) {\n const trimmed = token.trim();\n if (VALID_TOKEN_RE.test(trimmed)) {\n const headers = new Headers(init?.headers);\n headers.set(\"Authorization\", `Bearer ${trimmed}`);\n return fetch(url, { ...init, headers });\n }\n // Token is set but invalid — warn once so operators know why auth fails\n if (!_warnedInvalidToken) {\n _warnedInvalidToken = true;\n console.error(\n `[tandem] authFetch: ${source} is set but invalid (must be 32+ alphanumeric chars [A-Za-z0-9_-]); sending without Authorization header`,\n );\n }\n }\n return fetch(url, init);\n}\n","/**\n * Shared preflight check for stdio MCP subcommands.\n *\n * Both `tandem mcp-stdio` and `tandem channel` need a live Tandem server on\n * localhost before they can do anything useful. Two flavors:\n *\n * - `ensureTandemServer` — fail fast via stderr + exit(1) when the server\n * isn't reachable. Used by `tandem channel`, whose stdio transport can't\n * meaningfully respond on its own.\n * - `probeTandemServer` — returns a result without side effects. Used by\n * `tandem mcp-stdio`, which starts its stdio transport before preflight\n * so it can synthesize -32000 JSON-RPC errors for any in-flight request\n * before exiting (issue #336).\n */\n\nimport { resolveTandemUrl } from \"../shared/cli-runtime.js\";\n\nconst DEFAULT_TIMEOUT_MS = 2000;\n\nexport interface PreflightOptions {\n url?: string;\n timeoutMs?: number;\n}\n\n// Note: \"unreachable\" is a catch-all for any non-HTTP-status failure —\n// DNS, TLS, timeout, ECONNREFUSED, RST all land here. \"unhealthy\" is\n// strictly non-2xx responses from /health.\nexport type PreflightProbe =\n | { ok: true }\n | { ok: false; url: string; reason: string; kind: \"unreachable\" | \"unhealthy\" };\n\nexport async function probeTandemServer(opts: PreflightOptions = {}): Promise<PreflightProbe> {\n const url = resolveTandemUrl(opts.url);\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const res = await fetch(`${url}/health`, { signal: controller.signal });\n if (!res.ok) {\n return {\n ok: false,\n url,\n reason: `health endpoint returned HTTP ${res.status}`,\n kind: \"unhealthy\",\n };\n }\n return { ok: true };\n } catch (err) {\n return {\n ok: false,\n url,\n reason: err instanceof Error ? err.message : String(err),\n kind: \"unreachable\",\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function ensureTandemServer(opts: PreflightOptions = {}): Promise<void> {\n const probe = await probeTandemServer(opts);\n if (!probe.ok) {\n const guidance =\n probe.kind === \"unreachable\"\n ? \"Start the Tauri app or run `tandem start` on the host, then retry.\"\n : \"The Tandem server is running but unhealthy — check the host logs.\";\n process.stderr.write(\n `[tandem] Tandem server preflight failed at ${probe.url} (${probe.reason}).\\n` +\n `[tandem] ${guidance}\\n`,\n );\n process.exit(1);\n }\n}\n","/**\n * Tandem mcp-stdio subcommand — stdio ↔ Streamable HTTP JSON-RPC proxy.\n *\n * Claude Desktop's plugin loader bridges stdio MCP servers into sandboxed\n * sessions but not HTTP MCP servers, so plugin-cached stdio entries that\n * forward to the local HTTP MCP endpoint are the only supported way to\n * surface tandem_* tools into those sessions.\n *\n * Raw message forwarding: no handler registrations, no per-method logic.\n * Any message the upstream emits (tool results, notifications, future\n * methods we haven't heard of) reaches the stdio client unchanged.\n *\n * Error surfacing (issue #336): the stdio transport is started before\n * preflight and http.start(), and early messages are buffered until the\n * upstream is ready. If the upstream never becomes ready — or dies mid-\n * session — every in-flight request ID is answered with a synthesized\n * `-32000` JSON-RPC error instead of a silent stdio close. Plugin hosts\n * surface `-32000` as actionable; a silent close is what produces \"tools\n * never appear in Cowork\" with nothing diagnosable in the logs.\n *\n * Intentional: no reconnection logic. If the upstream HTTP server dies\n * mid-session, we synthesize errors for pending requests and exit 1.\n * The plugin loader will respawn us on the next tool call and preflight\n * will re-run with a fresh, accurate error if the server is still down.\n */\n\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { JSONRPCMessage } from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n redirectConsoleToStderr,\n resolveAuthTokenCandidate,\n resolveTandemUrl,\n} from \"../shared/cli-runtime.js\";\nimport { probeTandemServer } from \"./preflight.js\";\n\nredirectConsoleToStderr();\n\n// After preflight or http.start() fails we wait ~1.5s for any already-in-\n// flight `initialize` from the plugin loader to land on stdin and receive\n// a -32000 reply before tear-down. Sizing covers stdin-read lag between\n// preflight resolution and first message arrival — independent of\n// preflight's own fetch timeout.\nconst PREFLIGHT_GRACE_MS = 1500;\n\n// Per-request timeout. Node's setTimeout uses a 32-bit signed integer\n// internally — values above this constant are silently clamped to 1ms,\n// which would make every request immediately synthesize -32000.\nconst MAX_TIMEOUT_MS = 2_147_483_647; // 2^31 - 1\n\nexport function parseTimeoutMs(raw: string | undefined): number {\n if (raw !== undefined) {\n const parsed = parseInt(raw, 10);\n if (Number.isFinite(parsed) && parsed > 0 && parsed <= MAX_TIMEOUT_MS) {\n return parsed;\n }\n // Note: parseInt(\"3e4\", 10) returns 3 (stops at 'e'), which passes validation.\n // Scientific notation lands here only when the leading integer is invalid.\n process.stderr.write(\n `[tandem mcp-stdio] TANDEM_REQUEST_TIMEOUT_MS must be a positive integer ≤ ${MAX_TIMEOUT_MS}; ignoring \"${raw}\", using 30000ms default\\n`,\n );\n }\n return 30_000;\n}\n\nconst STDIO_REQUEST_TIMEOUT_MS = parseTimeoutMs(process.env.TANDEM_REQUEST_TIMEOUT_MS);\n\n// Last-gasp handlers for truly unexpected crashes: write one diagnostic to\n// stderr before exit. Installed at module load; process.once bounds each\n// handler to a single fire. No -32000 synthesis here because pendingRequests\n// lives inside runMcpStdio()'s closure.\nprocess.once(\"uncaughtException\", (err: Error) => {\n process.stderr.write(\n `[tandem mcp-stdio] uncaughtException: ${err.message}\\n${err.stack ?? \"\"}\\n`,\n );\n process.exit(1);\n});\nprocess.once(\"unhandledRejection\", (reason: unknown) => {\n const detail = reason instanceof Error ? reason.message : String(reason);\n process.stderr.write(`[tandem mcp-stdio] unhandledRejection: ${detail}\\n`);\n process.exit(1);\n});\n\n/** Regex for a valid Tandem auth token (32+ URL-safe alphanumeric chars). */\nconst VALID_TOKEN_RE = /^[A-Za-z0-9_\\-]{32,}$/;\n\n/**\n * Validate the configured auth token if present.\n * Rules (invariant 4):\n * - If not set at all, or if empty/whitespace-only after trim → return null (loopback-only mode).\n * - \"Bearer \" prefix → exit 1 with \"double-prefix\" message.\n * - Must match /^[A-Za-z0-9_-]{32,}$/ (no whitespace, no newlines, no Bearer prefix).\n */\nexport function readAndValidateAuthToken(): string | null {\n const { token, source } = resolveAuthTokenCandidate();\n // Token not set at all, or empty/whitespace-only → loopback-only mode, no auth header, no exit.\n if (token === undefined) return null;\n const trimmed = token.trim();\n if (trimmed === \"\") return null;\n\n if (trimmed.startsWith(\"Bearer \")) {\n process.stderr.write(\n `[tandem mcp-stdio] ${source} is invalid (double-prefix: do not include 'Bearer ' prefix — supply the raw token only)\\n`,\n );\n process.exit(1);\n }\n\n if (!VALID_TOKEN_RE.test(trimmed)) {\n process.stderr.write(\n `[tandem mcp-stdio] ${source} is malformed (must be 32+ URL-safe characters: [A-Za-z0-9_-])\\n`,\n );\n process.exit(1);\n }\n\n return trimmed;\n}\n\nexport async function runMcpStdio(): Promise<void> {\n const baseUrl = resolveTandemUrl();\n const authToken = readAndValidateAuthToken();\n\n const http = new StreamableHTTPClientTransport(new URL(`${baseUrl}/mcp`), {\n requestInit: authToken ? { headers: { Authorization: `Bearer ${authToken}` } } : undefined,\n });\n const stdio = new StdioServerTransport();\n\n // On upstream failure we synthesize -32000 for every entry before exit.\n // Value is the per-request timeout handle so we can cancel it on response.\n const pendingRequests = new Map<string | number, ReturnType<typeof setTimeout>>();\n // Messages arriving before httpReady flips; either drained and forwarded\n // on success, or each request answered with -32000 on preflight/http-start\n // failure.\n const preReadyBuffer: JSONRPCMessage[] = [];\n let shuttingDown = false;\n let httpReady = false;\n\n async function sendErrorResponse(\n id: string | number,\n message: string,\n detail?: string,\n ): Promise<void> {\n const errorResponse: JSONRPCMessage = {\n jsonrpc: \"2.0\",\n id,\n error: {\n // -32000 is the implementation-defined server error range per\n // JSON-RPC 2.0 §5.1 — upstream unavailability is an application-\n // level condition, not a generic Internal Error.\n code: -32000,\n message,\n ...(detail !== undefined ? { data: { detail } } : {}),\n },\n };\n try {\n await stdio.send(errorResponse);\n } catch (err) {\n // stdio already torn down; log so synth failures during shutdown\n // (e.g., http.onclose racing stdio.onclose) aren't silently dropped —\n // a silent drop here would recreate exactly the failure mode this\n // module exists to prevent.\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[tandem mcp-stdio] failed to send synthesized error for id ${id}: ${detail}\\n`,\n );\n }\n }\n\n function forwardToUpstream(msg: JSONRPCMessage): void {\n if (shuttingDown) return;\n const requestId = getRequestId(msg);\n if (requestId !== undefined) {\n // Clear any existing timer for this id (duplicate/retry scenario).\n const existing = pendingRequests.get(requestId);\n if (existing) clearTimeout(existing);\n\n const timeoutHandle = setTimeout(() => {\n // Atomic: delete returns false if synthesizePending already drained the map.\n if (!pendingRequests.delete(requestId)) return;\n void sendErrorResponse(\n requestId,\n \"Tandem HTTP upstream not responding (half-open)\",\n `No response after ${STDIO_REQUEST_TIMEOUT_MS}ms`,\n );\n }, STDIO_REQUEST_TIMEOUT_MS);\n pendingRequests.set(requestId, timeoutHandle);\n }\n http.send(msg).catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] upstream send failed: ${detail}\\n`);\n if (requestId !== undefined) {\n const handle = pendingRequests.get(requestId);\n if (handle !== undefined) {\n pendingRequests.delete(requestId);\n clearTimeout(handle);\n void sendErrorResponse(requestId, \"Tandem HTTP upstream unreachable\", detail);\n }\n }\n });\n }\n\n async function synthesizeBuffered(message: string, detail?: string): Promise<void> {\n const buffered = preReadyBuffer.splice(0);\n const ids = buffered\n .map((msg) => getRequestId(msg))\n .filter((id): id is string | number => id !== undefined);\n for (const id of ids) {\n await sendErrorResponse(id, message, detail);\n }\n }\n\n async function synthesizePending(message: string, detail?: string): Promise<void> {\n if (pendingRequests.size === 0) return;\n const ids = [...pendingRequests.keys()];\n // Synchronous before any await: clear all timers + drain map atomically.\n // Timer callbacks that are already queued observe empty map and return early.\n for (const handle of pendingRequests.values()) clearTimeout(handle);\n pendingRequests.clear();\n await Promise.all(ids.map((id) => sendErrorResponse(id, message, detail)));\n }\n\n const shutdown = async (\n code = 0,\n synth?: { message: string; detail?: string },\n ): Promise<never> => {\n if (!shuttingDown) {\n shuttingDown = true;\n // Hard deadline: if http.close() or stdio.close() hang (e.g., a half-\n // open upstream holding an SSE GET open), don't let cleanup block the\n // process exit indefinitely. .unref() means this timer doesn't itself\n // keep the event loop alive — fast paths resolve and call process.exit\n // below before the deadline fires; hung paths are forcibly terminated.\n setTimeout(() => process.exit(code), 2_000).unref();\n // Unconditionally drain all pending timers before any await — prevents\n // orphan timers from firing into the half-closed transport during\n // http.close() / stdio.close() awaits. synthesizePending will also\n // drain the map if synth is provided; the clearTimeout calls here are\n // defensive for the synth=undefined path (e.g., clean stdio.onclose).\n for (const handle of pendingRequests.values()) clearTimeout(handle);\n if (synth) {\n await synthesizeBuffered(synth.message, synth.detail);\n await synthesizePending(synth.message, synth.detail);\n }\n await http.close().catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] http.close failed: ${detail}\\n`);\n });\n await stdio.close().catch((err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] stdio.close failed: ${detail}\\n`);\n });\n }\n process.exit(code);\n };\n\n // Plugin hosts typically send `initialize` immediately after spawn (MCP\n // lifecycle §initialization). Deferring shutdown by PREFLIGHT_GRACE_MS\n // lets that request land during the preflight/start window and receive\n // a -32000 reply rather than a silent stdio close. stdio.onclose\n // short-circuits this if the loader closes stdin first.\n function deferredShutdown(synth: { message: string; detail?: string }): void {\n setTimeout(() => void shutdown(1, synth), PREFLIGHT_GRACE_MS);\n }\n\n stdio.onmessage = (msg: JSONRPCMessage) => {\n if (!httpReady) {\n preReadyBuffer.push(msg);\n return;\n }\n forwardToUpstream(msg);\n };\n\n http.onmessage = (msg: JSONRPCMessage) => {\n if (shuttingDown) return;\n // Synchronous delete+clear FIRST — before stdio.send — to prevent the\n // per-request timer from firing in the window between response arrival\n // and stdio write completion (which would synthesize a false -32000 for\n // an id that already has a real response in flight).\n const responseId = getResponseId(msg);\n if (responseId !== undefined) {\n const handle = pendingRequests.get(responseId);\n if (handle !== undefined) {\n clearTimeout(handle);\n pendingRequests.delete(responseId);\n }\n }\n const sendHandler = (err: unknown) => {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `[tandem mcp-stdio] stdio write failed for id ${responseId ?? \"<notification>\"}: ${detail}\\n`,\n );\n // Map entry already deleted above. Synthesize directly for this id,\n // then tear down — stdio is broken so other pending requests can't\n // be delivered either.\n if (responseId !== undefined) {\n void sendErrorResponse(responseId, \"Tandem stdio write failed\", detail);\n }\n void shutdown(1, {\n message: \"Tandem stdio write failed\",\n detail,\n });\n };\n try {\n stdio.send(msg).catch(sendHandler);\n } catch (err) {\n sendHandler(err);\n }\n };\n\n stdio.onerror = (err) => {\n process.stderr.write(`[tandem mcp-stdio] stdio error: ${err.message}\\n${err.stack ?? \"\"}\\n`);\n };\n http.onerror = (err) => {\n const cause = (err as { cause?: unknown }).cause;\n process.stderr.write(\n `[tandem mcp-stdio] http error: ${err.message}\\n${err.stack ?? \"\"}${cause !== undefined ? `\\ncause: ${cause}` : \"\"}\\n`,\n );\n };\n\n stdio.onclose = () => {\n void shutdown(0);\n };\n http.onclose = () => {\n // We've observed the current @modelcontextprotocol/sdk (0.20.x) only\n // firing onclose from inside its own close() method — i.e., as a\n // consequence of *our* shutdown. The synth branch below is defensive\n // for future SDK versions that may propagate socket-death as onclose.\n // The `shuttingDown` guard prevents double-synth when shutdown() calls\n // http.close() itself.\n if (shuttingDown) return;\n void shutdown(1, {\n message: \"Tandem HTTP upstream closed unexpectedly\",\n detail: \"upstream connection dropped mid-session\",\n });\n };\n\n // Start stdio BEFORE preflight so any `initialize` that arrives during\n // the preflight window is captured and either forwarded once upstream is\n // ready, or answered with -32000 if upstream never comes up.\n await stdio.start();\n\n // The SDK's StdioServerTransport watches stdin for 'data' and 'error' only —\n // it does not call onclose when the plugin host closes stdin (EOF). Register\n // our own one-shot listener so that plugin-host close (stdin.end() / HUP)\n // triggers the same clean shutdown path as stdio.onclose does.\n process.stdin.once(\"end\", () => {\n void shutdown(0);\n });\n\n const probe = await probeTandemServer({ url: baseUrl });\n if (!probe.ok) {\n const guidance =\n probe.kind === \"unreachable\"\n ? \"Start the Tauri app or run `tandem start` on the host, then retry.\"\n : \"The Tandem server is running but unhealthy — check the host logs.\";\n process.stderr.write(\n `[tandem mcp-stdio] Tandem server preflight failed at ${probe.url} (${probe.reason}).\\n` +\n `[tandem mcp-stdio] ${guidance}\\n`,\n );\n const synthMessage =\n probe.kind === \"unreachable\"\n ? \"Tandem server not running. Start the Tauri app or run `tandem start`.\"\n : \"Tandem server unhealthy (check host logs).\";\n deferredShutdown({ message: synthMessage, detail: probe.reason });\n return;\n }\n\n // The current @modelcontextprotocol/sdk's StreamableHTTPClientTransport.start()\n // only creates an AbortController and returns synchronously — this catch is\n // defensive for future SDK versions that may perform real I/O during start().\n try {\n await http.start();\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[tandem mcp-stdio] upstream http start failed: ${detail}\\n`);\n deferredShutdown({ message: \"Tandem HTTP upstream failed to start\", detail });\n return;\n }\n httpReady = true;\n\n // Held to preserve forwarding semantics — push through the normal path\n // now that upstream is ready. Note: forwardToUpstream does not await the\n // http.send, so buffered requests POST in parallel. Plugin hosts wait\n // for `initialize` to resolve before sending follow-ups per MCP spec, so\n // the buffer is usually ≤1 entry; we don't enforce serial ordering here.\n const buffered = preReadyBuffer.splice(0);\n for (const msg of buffered) forwardToUpstream(msg);\n}\n\nexport function getRequestId(msg: JSONRPCMessage): string | number | undefined {\n const m = msg as { id?: unknown; method?: unknown };\n if (typeof m.method !== \"string\") return undefined;\n if (typeof m.id === \"string\" || typeof m.id === \"number\") return m.id;\n return undefined;\n}\n\nexport function getResponseId(msg: JSONRPCMessage): string | number | undefined {\n const m = msg as { id?: unknown; method?: unknown };\n if (typeof m.method === \"string\") return undefined;\n if (typeof m.id === \"string\" || typeof m.id === \"number\") return m.id;\n return undefined;\n}\n","/**\n * Shared fetch-with-timeout helper.\n *\n * Used by `src/monitor/index.ts` and `src/channel/` (event-bridge + run) to\n * give every outbound HTTP call a bounded deadline. Without this, a half-open\n * upstream wedges the caller silently — see #336 (silent failures) and #364\n * (event-bridge transport timeout symmetry).\n *\n * Pure native `fetch` + `AbortSignal.timeout` so it can be imported from any\n * surface without dragging in server deps. Routes through `authFetch` so the\n * resolved Tandem auth token is forwarded automatically when set.\n *\n * `describeFetchError` formats timeout aborts as `<endpoint> timed out after\n * <ms>ms` so logs name the hung endpoint instead of the generic\n * \"operation was aborted\" string from AbortError/TimeoutError.\n */\n\nimport { authFetch } from \"./cli-runtime.js\";\n\n/**\n * Fetch with a per-request deadline.\n *\n * **Do not use for SSE handshake-then-stream patterns** — applying a fetch-level\n * timeout to a streaming response also aborts the body `ReadableStream` when\n * the timeout fires, killing the stream at `timeoutMs`. Use a local\n * `AbortController` cleared after the handshake settles for that case.\n */\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const timeoutSignal = AbortSignal.timeout(timeoutMs);\n const signal = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;\n return authFetch(url, { ...init, signal });\n}\n\n/**\n * Format a fetch error for logs. Recognizes `TimeoutError` / `AbortError`\n * (both names that `AbortSignal.timeout` and manual aborts produce) and tags\n * them with the endpoint + threshold so log lines name the hung request.\n */\nexport function describeFetchError(err: unknown, endpoint: string, timeoutMs: number): string {\n if (err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\")) {\n return `${endpoint} timed out after ${timeoutMs}ms`;\n }\n return err instanceof Error ? err.message : String(err);\n}\n\n/** True iff `err` is an AbortError or TimeoutError (the names produced by\n * AbortSignal.timeout and manual aborts). Channel callers re-throw these\n * through broad catches so timeouts surface as structured errors instead of\n * being swallowed as \"non-JSON response\" fake-success. */\nexport function isAbortOrTimeoutError(err: unknown): boolean {\n return err instanceof Error && (err.name === \"TimeoutError\" || err.name === \"AbortError\");\n}\n","function generateId(prefix: string): string {\n return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function generateAnnotationId(): string {\n return generateId(\"ann\");\n}\n\nexport function generateMessageId(): string {\n return generateId(\"msg\");\n}\n\nexport function generateEventId(): string {\n return generateId(\"evt\");\n}\n\nexport function generateReplyId(): string {\n return generateId(\"rpl\");\n}\n\nexport function generateNotificationId(): string {\n return generateId(\"ntf\");\n}\n\nexport function generateAuthorshipId(author: \"user\" | \"claude\"): string {\n return generateId(author);\n}\n","/**\n * Event types for the Tandem → Claude Code channel.\n *\n * These events flow from browser-originated Y.Map changes through an SSE\n * endpoint to the channel shim, which pushes them into Claude Code as\n * `notifications/claude/channel` messages.\n *\n * This module lives in `src/shared/` so that `src/channel/` and\n * `src/monitor/` can import wire-protocol types without crossing the\n * server layer boundary.\n */\n\nimport type { AnnotationType, ReplyAuthor } from \"../types.js\";\n\n// --- Per-event payload interfaces ---\n\nexport interface AnnotationCreatedPayload {\n annotationId: string;\n annotationType: AnnotationType;\n content: string;\n textSnippet: string;\n hasSuggestedText?: boolean;\n}\n\nexport interface AnnotationAcceptedPayload {\n annotationId: string;\n textSnippet: string;\n}\n\nexport interface AnnotationDismissedPayload {\n annotationId: string;\n textSnippet: string;\n}\n\nexport interface ChatMessagePayload {\n messageId: string;\n text: string;\n replyTo: string | null;\n anchor: { from: number; to: number; textSnapshot: string } | null;\n /** Buffered selection context at the time the chat message was sent. */\n selection?: { from: number; to: number; selectedText: string } | { selectedText: string };\n}\n\nexport interface DocumentOpenedPayload {\n fileName: string;\n format: string;\n}\n\nexport interface DocumentClosedPayload {\n fileName: string;\n}\n\nexport interface AnnotationReplyPayload {\n annotationId: string;\n replyId: string;\n replyText: string;\n replyAuthor: ReplyAuthor;\n textSnippet: string;\n}\n\nexport interface DocumentSwitchedPayload {\n fileName: string;\n}\n\nexport interface AnnotationEditedPayload {\n annotationId: string;\n content: string;\n textSnippet: string;\n editedAt: number;\n}\n\n// --- Discriminated union ---\n\ninterface TandemEventBase {\n /** Timestamp-based unique ID for SSE `Last-Event-ID` reconnection. Format: `evt_<timestamp>_<rand>`. Roughly ordered but not strictly monotonic. */\n id: string;\n timestamp: number;\n /** Which document this event relates to (absent for global events). */\n documentId?: string;\n}\n\nexport type TandemEvent =\n | (TandemEventBase & { type: \"annotation:created\"; payload: AnnotationCreatedPayload })\n | (TandemEventBase & { type: \"annotation:accepted\"; payload: AnnotationAcceptedPayload })\n | (TandemEventBase & { type: \"annotation:dismissed\"; payload: AnnotationDismissedPayload })\n | (TandemEventBase & { type: \"annotation:reply\"; payload: AnnotationReplyPayload })\n | (TandemEventBase & { type: \"chat:message\"; payload: ChatMessagePayload })\n | (TandemEventBase & { type: \"document:opened\"; payload: DocumentOpenedPayload })\n | (TandemEventBase & { type: \"document:closed\"; payload: DocumentClosedPayload })\n | (TandemEventBase & { type: \"document:switched\"; payload: DocumentSwitchedPayload })\n | (TandemEventBase & { type: \"annotation:edited\"; payload: AnnotationEditedPayload });\n\n/** Union of all event type discriminants. */\nexport type TandemEventType = TandemEvent[\"type\"];\n\n// Re-export from shared utils (single ID generation pattern)\nexport { generateEventId } from \"../utils.js\";\n\n// --- Parse guard for SSE consumers ---\n\nconst VALID_EVENT_TYPES = new Set<TandemEventType>([\n \"annotation:created\",\n \"annotation:accepted\",\n \"annotation:dismissed\",\n \"annotation:edited\",\n \"annotation:reply\",\n \"chat:message\",\n \"document:opened\",\n \"document:closed\",\n \"document:switched\",\n]);\n\n/**\n * Validate a JSON-parsed value as a TandemEvent.\n * Used by the event-bridge to safely consume SSE data.\n */\nexport function parseTandemEvent(raw: unknown): TandemEvent | null {\n if (\n typeof raw !== \"object\" ||\n raw === null ||\n !(\"id\" in raw) ||\n typeof (raw as Record<string, unknown>).id !== \"string\" ||\n !(\"type\" in raw) ||\n !VALID_EVENT_TYPES.has((raw as Record<string, unknown>).type as TandemEventType) ||\n !(\"timestamp\" in raw) ||\n typeof (raw as Record<string, unknown>).timestamp !== \"number\" ||\n !(\"payload\" in raw) ||\n typeof (raw as Record<string, unknown>).payload !== \"object\"\n ) {\n return null;\n }\n return raw as TandemEvent;\n}\n\n/**\n * Convert a TandemEvent into a human-readable string for the channel `content` field.\n * Claude sees this text inside `<channel source=\"tandem-channel\">` tags.\n */\nexport function formatEventContent(event: TandemEvent): string {\n const doc = event.documentId ? ` [doc: ${event.documentId}]` : \"\";\n\n switch (event.type) {\n case \"annotation:created\": {\n const { annotationType, content, textSnippet, hasSuggestedText } = event.payload;\n const snippet = textSnippet ? ` on \"${textSnippet}\"` : \"\";\n const label = hasSuggestedText ? \"replacement\" : annotationType;\n return `User created ${label}${snippet}: ${content || \"(no content)\"}${doc}`;\n }\n case \"annotation:accepted\": {\n const { annotationId, textSnippet } = event.payload;\n return `User accepted annotation ${annotationId}${textSnippet ? ` (\"${textSnippet}\")` : \"\"}${doc}`;\n }\n case \"annotation:dismissed\": {\n const { annotationId, textSnippet } = event.payload;\n return `User dismissed annotation ${annotationId}${textSnippet ? ` (\"${textSnippet}\")` : \"\"}${doc}`;\n }\n case \"annotation:edited\": {\n const { content } = event.payload;\n return `User edited annotation: \"${content}\"${doc}`;\n }\n case \"annotation:reply\": {\n const { annotationId, replyAuthor, replyText, textSnippet } = event.payload;\n const who = replyAuthor === \"claude\" ? \"Claude\" : \"User\";\n const snippet = textSnippet ? ` (on \"${textSnippet}\")` : \"\";\n return `${who} replied to annotation ${annotationId}${snippet}: ${replyText}${doc}`;\n }\n case \"chat:message\": {\n const { text, replyTo, selection } = event.payload;\n const reply = replyTo ? ` (replying to ${replyTo})` : \"\";\n const sel =\n selection && selection.selectedText\n ? ` [selection: \"${selection.selectedText}\"${\"from\" in selection ? ` (${selection.from}-${selection.to})` : \"\"}]`\n : \"\";\n return `User says${reply}: ${text}${sel}${doc}`;\n }\n case \"document:opened\": {\n const { fileName, format } = event.payload;\n return `User opened document: ${fileName} (${format})${doc}`;\n }\n case \"document:closed\": {\n const { fileName } = event.payload;\n return `User closed document: ${fileName}${doc}`;\n }\n case \"document:switched\": {\n const { fileName } = event.payload;\n return `User switched to document: ${fileName}${doc}`;\n }\n default: {\n const _exhaustive: never = event;\n void _exhaustive;\n return `Unknown event${doc}`;\n }\n }\n}\n\n/**\n * Build the `meta` record for a channel notification.\n * Keys use underscores only (Channels API silently drops hyphenated keys).\n */\nexport function formatEventMeta(event: TandemEvent): Record<string, string> {\n const meta: Record<string, string> = {\n event_type: event.type,\n };\n if (event.documentId) meta.document_id = event.documentId;\n\n switch (event.type) {\n case \"annotation:created\":\n case \"annotation:accepted\":\n case \"annotation:dismissed\":\n meta.annotation_id = event.payload.annotationId;\n break;\n case \"annotation:edited\":\n meta.annotation_id = event.payload.annotationId;\n meta.edited_at = String(event.payload.editedAt);\n break;\n case \"annotation:reply\":\n meta.annotation_id = event.payload.annotationId;\n meta.reply_id = event.payload.replyId;\n break;\n case \"chat:message\":\n meta.message_id = event.payload.messageId;\n if (event.payload.selection?.selectedText) meta.has_selection = \"true\";\n break;\n case \"document:opened\":\n case \"document:closed\":\n case \"document:switched\":\n break;\n default: {\n const _exhaustive: never = event;\n void _exhaustive;\n break;\n }\n }\n\n return meta;\n}\n","/**\n * SSE event bridge: connects to Tandem server's /api/events endpoint\n * and pushes received events to Claude Code as channel notifications.\n *\n * Per-request timeouts (#364) mirror the monitor pattern: every outbound\n * fetch has a bounded deadline, the SSE body has an inactivity watchdog,\n * and the parse buffer is capped so a malformed upstream can't OOM us.\n * Without these, a half-open Tandem server wedges the bridge silently.\n */\n\nimport type { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { authFetch } from \"../shared/cli-runtime.js\";\nimport {\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n CHANNEL_CONNECT_FETCH_TIMEOUT_MS,\n CHANNEL_ERROR_REPORT_TIMEOUT_MS,\n CHANNEL_MAX_RETRIES,\n CHANNEL_MAX_SSE_BUFFER_BYTES,\n CHANNEL_MODE_FETCH_TIMEOUT_MS,\n CHANNEL_RETRY_DELAY_MS,\n CHANNEL_SSE_INACTIVITY_TIMEOUT_MS,\n} from \"../shared/constants.js\";\nimport type { TandemEvent } from \"../shared/events/types.js\";\nimport { formatEventContent, formatEventMeta, parseTandemEvent } from \"../shared/events/types.js\";\nimport { describeFetchError, fetchWithTimeout } from \"../shared/fetch-with-timeout.js\";\n\nconst AWARENESS_DEBOUNCE_MS = 500;\nconst MODE_CACHE_TTL_MS = 2000;\n\n/**\n * Stdio-mode SSE bridge. New push-path work should target src/monitor/.\n * This path remains active for stdio-mode Claude Code connections.\n */\nexport async function startEventBridge(mcp: Server, tandemUrl: string): Promise<void> {\n let retries = 0;\n let lastEventId: string | undefined;\n\n while (retries < CHANNEL_MAX_RETRIES) {\n try {\n await connectAndStream(mcp, tandemUrl, lastEventId, (id) => {\n lastEventId = id;\n retries = 0;\n });\n } catch (err) {\n retries++;\n console.error(\n `[Channel] SSE connection failed (${retries}/${CHANNEL_MAX_RETRIES}):`,\n err instanceof Error ? err.message : err,\n );\n\n if (retries >= CHANNEL_MAX_RETRIES) {\n console.error(\"[Channel] SSE connection exhausted, reporting error and exiting\");\n try {\n await fetchWithTimeout(\n `${tandemUrl}/api/channel-error`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n error: \"CHANNEL_CONNECT_FAILED\",\n message: `Channel shim lost connection after ${CHANNEL_MAX_RETRIES} retries.`,\n }),\n },\n CHANNEL_ERROR_REPORT_TIMEOUT_MS,\n );\n } catch (reportErr) {\n console.error(\n \"[Channel] Could not report failure to server:\",\n describeFetchError(reportErr, \"/api/channel-error\", CHANNEL_ERROR_REPORT_TIMEOUT_MS),\n );\n }\n process.exit(1);\n }\n\n await new Promise((r) => setTimeout(r, CHANNEL_RETRY_DELAY_MS));\n }\n }\n}\n\nasync function connectAndStream(\n mcp: Server,\n tandemUrl: string,\n lastEventId: string | undefined,\n onEventId: (id: string) => void,\n): Promise<void> {\n const headers: Record<string, string> = { Accept: \"text/event-stream\" };\n if (lastEventId) headers[\"Last-Event-ID\"] = lastEventId;\n\n // Split handshake timeout from body lifetime. Using AbortSignal.timeout on\n // the fetch would kill the response body ReadableStream when the timeout\n // fires — every SSE stream would abort at CHANNEL_CONNECT_FETCH_TIMEOUT_MS.\n // A local AbortController cleared in `finally` after the handshake settles\n // means the body stream is no longer governed by it.\n const connectCtrl = new AbortController();\n const connectTimer = setTimeout(\n () => connectCtrl.abort(new Error(\"handshake timeout\")),\n CHANNEL_CONNECT_FETCH_TIMEOUT_MS,\n );\n let res: Response;\n try {\n res = await authFetch(`${tandemUrl}/api/events`, { headers, signal: connectCtrl.signal });\n } finally {\n clearTimeout(connectTimer);\n }\n if (!res.ok) throw new Error(`SSE endpoint returned ${res.status}`);\n if (!res.body) throw new Error(\"SSE endpoint returned no body\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n // Inactivity watchdog. A healthy stream emits keepalive comments\n // periodically; if no bytes arrive for CHANNEL_SSE_INACTIVITY_TIMEOUT_MS,\n // cancel the reader. reader.cancel() resolves a pending read() with\n // {done: true} (does not reject), so we surface the cause via a flag.\n let lastActivityAt = Date.now();\n let inactivityTimedOut = false;\n const watchdog = setInterval(() => {\n if (Date.now() - lastActivityAt > CHANNEL_SSE_INACTIVITY_TIMEOUT_MS) {\n inactivityTimedOut = true;\n reader.cancel(new Error(\"SSE inactivity timeout\")).catch(() => {});\n }\n }, CHANNEL_SSE_INACTIVITY_TIMEOUT_MS / 4);\n\n // Debounced awareness: only send the latest status after a quiet period\n let awarenessTimer: ReturnType<typeof setTimeout> | null = null;\n let clearAwarenessTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingAwareness: TandemEvent | null = null;\n const AWARENESS_CLEAR_MS = 3000; // Reset active state after 3s of no new events\n\n function clearAwareness(documentId?: string) {\n fetchWithTimeout(\n `${tandemUrl}/api/channel-awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n documentId: documentId ?? null,\n status: \"idle\",\n active: false,\n }),\n },\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ).catch((err) => {\n console.error(\n \"[Channel] clearAwareness failed (non-fatal):\",\n describeFetchError(err, \"/api/channel-awareness clear\", CHANNEL_AWARENESS_FETCH_TIMEOUT_MS),\n );\n });\n }\n\n function flushAwareness() {\n if (!pendingAwareness) return;\n const event = pendingAwareness;\n pendingAwareness = null;\n fetchWithTimeout(\n `${tandemUrl}/api/channel-awareness`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n documentId: event.documentId,\n status: `processing: ${event.type}`,\n active: true,\n }),\n },\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ).catch((err) => {\n console.error(\n \"[Channel] Awareness update failed:\",\n describeFetchError(\n err,\n \"/api/channel-awareness update\",\n CHANNEL_AWARENESS_FETCH_TIMEOUT_MS,\n ),\n );\n });\n\n // Auto-clear after timeout so the indicator doesn't stick\n if (clearAwarenessTimer) clearTimeout(clearAwarenessTimer);\n clearAwarenessTimer = setTimeout(() => clearAwareness(event.documentId), AWARENESS_CLEAR_MS);\n }\n\n function scheduleAwareness(event: TandemEvent) {\n pendingAwareness = event;\n if (awarenessTimer) clearTimeout(awarenessTimer);\n awarenessTimer = setTimeout(flushAwareness, AWARENESS_DEBOUNCE_MS);\n }\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n if (inactivityTimedOut) throw new Error(\"SSE inactivity timeout\");\n throw new Error(\"SSE stream ended\");\n }\n lastActivityAt = Date.now();\n\n buffer += decoder.decode(value, { stream: true });\n\n if (buffer.length > CHANNEL_MAX_SSE_BUFFER_BYTES) {\n throw new Error(\n `SSE buffer exceeded ${CHANNEL_MAX_SSE_BUFFER_BYTES} bytes without a frame boundary`,\n );\n }\n\n let boundary: number;\n while ((boundary = buffer.indexOf(\"\\n\\n\")) !== -1) {\n const frame = buffer.slice(0, boundary);\n buffer = buffer.slice(boundary + 2);\n\n if (frame.startsWith(\":\")) continue;\n\n let eventId: string | undefined;\n let data: string | undefined;\n\n for (const line of frame.split(\"\\n\")) {\n if (line.startsWith(\"id: \")) eventId = line.slice(4);\n else if (line.startsWith(\"data: \")) data = line.slice(6);\n }\n\n if (!data) continue;\n\n let event: TandemEvent | null;\n try {\n event = parseTandemEvent(JSON.parse(data));\n } catch {\n console.error(\n \"[Channel] Malformed SSE event data (skipping), eventId=%s:\",\n eventId,\n data.slice(0, 200),\n );\n // Permanently unparseable — advance past it to prevent infinite re-delivery on reconnect.\n if (eventId) onEventId(eventId);\n continue;\n }\n if (!event) {\n console.error(\n \"[Channel] Invalid SSE event structure (skipping), eventId=%s:\",\n eventId,\n data.slice(0, 200),\n );\n if (eventId) onEventId(eventId);\n continue;\n }\n\n // Solo mode: intentionally suppressed (not a delivery failure) — advance past it.\n if (event.type !== \"chat:message\") {\n const mode = await getCachedMode(tandemUrl);\n if (mode === \"solo\") {\n console.error(`[Channel] Solo mode: suppressed ${event.type} event`);\n if (eventId) onEventId(eventId);\n continue;\n }\n }\n\n try {\n await mcp.notification({\n method: \"notifications/claude/channel\",\n params: {\n content: formatEventContent(event),\n meta: formatEventMeta(event),\n },\n });\n } catch (err) {\n console.error(\"[Channel] MCP notification failed (transport broken?):\", err);\n throw err;\n }\n\n // Advance only after notification succeeds so a transport failure\n // doesn't silently skip events on reconnect.\n if (eventId) onEventId(eventId);\n\n scheduleAwareness(event);\n }\n }\n } finally {\n // Single source of truth for timer cleanup — every exit path (success,\n // throw, reader.cancel) runs through here so awareness/inactivity\n // timers can't leak across reconnects.\n clearInterval(watchdog);\n if (awarenessTimer) clearTimeout(awarenessTimer);\n if (clearAwarenessTimer) clearTimeout(clearAwarenessTimer);\n }\n}\n\n// Cached mode lookup — avoids an HTTP fetch per event\nlet cachedMode: string = \"tandem\";\nlet cachedModeAt = 0;\n\nasync function getCachedMode(tandemUrl: string): Promise<string> {\n const now = Date.now();\n if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;\n try {\n const res = await fetchWithTimeout(`${tandemUrl}/api/mode`, {}, CHANNEL_MODE_FETCH_TIMEOUT_MS);\n if (res.ok) {\n const { mode } = (await res.json()) as { mode: string };\n cachedMode = mode;\n } else {\n console.error(`[Channel] Mode check returned ${res.status}, using cached: \"${cachedMode}\"`);\n }\n cachedModeAt = now;\n } catch (err) {\n console.error(\n \"[Channel] Mode check failed, delivering event (fail-open):\",\n describeFetchError(err, \"/api/mode\", CHANNEL_MODE_FETCH_TIMEOUT_MS),\n );\n cachedModeAt = now;\n }\n return cachedMode;\n}\n","/**\n * Tandem Channel Shim — core runtime, shared by:\n * - src/channel/index.ts (standalone binary, used by the Desktop sidecar)\n * - src/cli/channel.ts (npm-delivered entry for the plugin `tandem-channel`)\n *\n * Bridges Tandem's SSE event stream → Claude Code channel notifications,\n * and exposes a `tandem_reply` tool for Claude to respond to chat messages.\n *\n * Uses the low-level MCP `Server` class (not `McpServer`) as required by\n * the Channels API spec.\n */\n\nimport { createConnection } from \"node:net\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { redirectConsoleToStderr, resolveTandemUrl } from \"../shared/cli-runtime.js\";\nimport {\n CHANNEL_PERMISSION_FETCH_TIMEOUT_MS,\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n DEFAULT_MCP_PORT,\n} from \"../shared/constants.js\";\nimport {\n describeFetchError,\n fetchWithTimeout,\n isAbortOrTimeoutError,\n} from \"../shared/fetch-with-timeout.js\";\nimport { startEventBridge } from \"./event-bridge.js\";\n\nexport interface RunChannelOptions {\n /** Skip the non-fatal reachability probe. The CLI wrapper runs a strict\n * preflight upstream and we don't want to double-log \"server not reachable\"\n * noise. Defaults to false. */\n skipReachabilityLog?: boolean;\n}\n\nexport async function runChannel(opts: RunChannelOptions = {}): Promise<void> {\n redirectConsoleToStderr();\n\n const tandemUrl = resolveTandemUrl();\n\n const mcp = new Server(\n { name: \"tandem-channel\", version: \"0.1.0\" },\n {\n capabilities: {\n experimental: {\n \"claude/channel\": {},\n \"claude/channel/permission\": {},\n },\n tools: {},\n },\n instructions: [\n 'Events from Tandem arrive as <channel source=\"tandem-channel\" event_type=\"...\" document_id=\"...\">.',\n \"These are real-time push notifications of user actions in the collaborative document editor.\",\n \"Event types: annotation:created, annotation:accepted, annotation:dismissed, annotation:reply,\",\n \"chat:message, document:opened, document:closed, document:switched.\",\n \"Chat messages may include a 'selection' field with buffered selection context.\",\n \"Use your tandem MCP tools (tandem_getTextContent, tandem_comment, tandem_edit, etc.) to act on them.\",\n \"Reply to chat messages using tandem_reply. Pass document_id from the tag attributes.\",\n \"Do not reply to non-chat events — just act on them using tools.\",\n \"If you haven't received channel notifications recently, call tandem_checkInbox as a fallback.\",\n ].join(\" \"),\n },\n );\n\n mcp.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: \"tandem_reply\",\n description: \"Reply to a chat message in Tandem\",\n inputSchema: {\n type: \"object\" as const,\n properties: {\n text: { type: \"string\", description: \"The reply message\" },\n documentId: {\n type: \"string\",\n description: \"Document ID from the channel event (optional)\",\n },\n replyTo: {\n type: \"string\",\n description: \"Message ID being replied to (optional)\",\n },\n },\n required: [\"text\"],\n },\n },\n ],\n }));\n\n mcp.setRequestHandler(CallToolRequestSchema, async (req) => {\n if (req.params.name === \"tandem_reply\") {\n const args = req.params.arguments as Record<string, unknown>;\n try {\n const res = await fetchWithTimeout(\n `${tandemUrl}/api/channel-reply`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(args),\n },\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n );\n let data: unknown;\n try {\n data = await res.json();\n } catch (parseErr) {\n // Re-throw timeout/abort errors so they surface as structured\n // failures to Claude. AbortSignal.timeout fires DURING `res.json()`\n // (headers landed but body hung); without this re-throw, the bare\n // catch swallows AbortError and reports a fake-success \"Non-JSON\n // response\" payload — exactly the silent-failure pattern #364\n // exists to prevent.\n if (isAbortOrTimeoutError(parseErr)) throw parseErr;\n data = { message: \"Non-JSON response\" };\n }\n if (!res.ok) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Reply failed (${res.status}): ${JSON.stringify(data)}`,\n },\n ],\n isError: true,\n };\n }\n return { content: [{ type: \"text\" as const, text: JSON.stringify(data) }] };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to send reply: ${describeFetchError(\n err,\n \"/api/channel-reply\",\n CHANNEL_REPLY_FETCH_TIMEOUT_MS,\n )}`,\n },\n ],\n isError: true,\n };\n }\n }\n throw new Error(`Unknown tool: ${req.params.name}`);\n });\n\n const PermissionRequestSchema = z.object({\n method: z.literal(\"notifications/claude/channel/permission_request\"),\n params: z.object({\n request_id: z.string(),\n tool_name: z.string(),\n description: z.string(),\n input_preview: z.string(),\n }),\n });\n\n mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {\n try {\n const res = await fetchWithTimeout(\n `${tandemUrl}/api/channel-permission`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n requestId: params.request_id,\n toolName: params.tool_name,\n description: params.description,\n inputPreview: params.input_preview,\n }),\n },\n CHANNEL_PERMISSION_FETCH_TIMEOUT_MS,\n );\n if (!res.ok) {\n console.error(\n `[Channel] Permission relay got HTTP ${res.status} — browser may not see prompt`,\n );\n }\n } catch (err) {\n console.error(\n \"[Channel] Failed to forward permission request:\",\n describeFetchError(err, \"/api/channel-permission\", CHANNEL_PERMISSION_FETCH_TIMEOUT_MS),\n );\n }\n });\n\n console.error(`[Channel] Tandem channel shim starting (server: ${tandemUrl})`);\n\n if (!opts.skipReachabilityLog) {\n const reachable = await checkServerReachable(tandemUrl);\n if (!reachable) {\n console.error(`[Channel] Cannot reach Tandem server at ${tandemUrl}`);\n console.error(\"[Channel] Start it with: tandem start\");\n // Continue anyway — the event bridge will retry, and the server may start later\n }\n }\n\n const transport = new StdioServerTransport();\n await mcp.connect(transport);\n console.error(\"[Channel] Connected to Claude Code via stdio\");\n\n startEventBridge(mcp, tandemUrl).catch((err) => {\n console.error(\"[Channel] Event bridge failed unexpectedly:\", err);\n process.exit(1);\n });\n}\n\nasync function checkServerReachable(url: string, timeoutMs = 2000): Promise<boolean> {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n console.error(\n `[Channel] Invalid TANDEM_URL: \"${url}\" — expected format: http://localhost:3479`,\n );\n return false;\n }\n const port = parseInt(parsed.port || String(DEFAULT_MCP_PORT), 10);\n return new Promise((resolve) => {\n const socket = createConnection({ port, host: parsed.hostname }, () => {\n socket.destroy();\n resolve(true);\n });\n socket.setTimeout(timeoutMs);\n socket.on(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.on(\"error\", (err) => {\n console.error(`[Channel] Server probe failed: ${err.message}`);\n socket.destroy();\n resolve(false);\n });\n });\n}\n","/**\n * Tandem channel subcommand — npm-delivered entry for the plugin\n * `tandem-channel` MCP server. Runs the unified preflight, then hands off to\n * the shared channel shim runtime in src/channel/run.ts.\n */\n\nimport { runChannel } from \"../channel/run.js\";\nimport { ensureTandemServer } from \"./preflight.js\";\n\nexport async function runChannelCli(): Promise<void> {\n await ensureTandemServer();\n await runChannel({ skipReachabilityLog: true });\n}\n","import envPaths from \"env-paths\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { TOKEN_FILE_NAME } from \"../constants.js\";\n\nexport function getTokenFilePath(): string {\n return path.join(envPaths(\"tandem\", { suffix: \"\" }).data, TOKEN_FILE_NAME);\n}\n\nexport async function readTokenFromFile(): Promise<string | null> {\n const filePath = getTokenFilePath();\n try {\n const content = await fs.promises.readFile(filePath, \"utf8\");\n // Remediate insecure permissions if a previous chmod failed (e.g., process crashed).\n if (process.platform !== \"win32\") {\n try {\n const stat = await fs.promises.stat(filePath);\n if ((stat.mode & 0o077) !== 0) {\n console.error(\"[tandem] auth token file has insecure permissions; attempting chmod 0600\");\n await fs.promises.chmod(filePath, 0o600);\n }\n } catch {\n // Non-fatal: stat/chmod failure doesn't invalidate the token we already read\n }\n }\n const trimmed = content.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { promises as fsPromises } from \"node:fs\";\nimport path from \"node:path\";\nimport { getTokenFilePath, readTokenFromFile } from \"../shared/auth/token-file.js\";\nimport { resolveAuthTokenCandidate, resolveTandemUrl } from \"../shared/cli-runtime.js\";\nimport { applyConfigWithToken } from \"./setup.js\";\n\n/** SHA-256 fingerprint — first 8 hex chars. Never logs the full token value. */\nfunction fingerprint(token: string): string {\n return createHash(\"sha256\").update(token, \"utf8\").digest(\"hex\").slice(0, 8);\n}\n\nfunction generateToken(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport async function rotateToken(): Promise<void> {\n console.error(\"\\n[tandem] Rotating auth token...\\n\");\n\n // Refuse to rotate when token comes from env — Tauri injects TANDEM_AUTH_TOKEN\n // before sidecar spawn, and Claude Code's plugin host injects\n // CLAUDE_PLUGIN_OPTION_AUTH_TOKEN from userConfig. In either case we have no\n // way to update the launcher; rotating the file would desync with what's\n // re-injected on the next launch.\n const { source: envAuthSource } = resolveAuthTokenCandidate();\n if (\n envAuthSource === \"TANDEM_AUTH_TOKEN\" ||\n envAuthSource === \"CLAUDE_PLUGIN_OPTION_AUTH_TOKEN\"\n ) {\n console.error(\n `[tandem] Error: ${envAuthSource} is set in the environment.\\n` +\n \" Token rotation is not supported in env-token mode (used by Tauri\\n\" +\n \" and Claude Code's plugin host). Unset the variable and let Tandem\\n\" +\n \" manage the token file, or rotate via the launcher's token management.\",\n );\n process.exit(1);\n }\n\n const oldToken = await readTokenFromFile();\n if (!oldToken) {\n console.error(\n \"[tandem] Error: no token file found. Run `tandem setup` first to initialize the token.\",\n );\n process.exit(1);\n }\n\n // writeTokenToFile uses O_EXCL; bypass it here — rotation is an intentional overwrite.\n // Use atomic write: write to a temp file first, then rename() into place.\n // rename() is atomic on the same filesystem — power-loss mid-write cannot leave an empty file.\n const newToken = generateToken();\n const tokenPath = getTokenFilePath();\n const dir = path.dirname(tokenPath);\n const tmpPath = path.join(dir, `.auth-token-tmp-${randomBytes(4).toString(\"hex\")}`);\n try {\n await fsPromises.writeFile(tmpPath, newToken, { encoding: \"utf8\", mode: 0o600 });\n await fsPromises.rename(tmpPath, tokenPath);\n } catch (err) {\n await fsPromises.unlink(tmpPath).catch(() => {});\n throw err;\n }\n\n const serverUrl = resolveTandemUrl();\n\n // Three distinct outcomes:\n // graceWindowActive = true → server accepted the rotation; grace window is live\n // serverRejected = true → server reachable but returned non-2xx\n // (neither) → fetch threw; server was not running\n let graceWindowActive = false;\n let serverRejected = false;\n let serverRejectedStatus = 0;\n try {\n const resp = await fetch(`${serverUrl}/api/rotate-token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${oldToken}`,\n },\n body: JSON.stringify({}),\n signal: AbortSignal.timeout(5000),\n });\n if (resp.ok) {\n graceWindowActive = true;\n } else {\n serverRejected = true;\n serverRejectedStatus = resp.status;\n }\n } catch {\n console.error(\n \"[tandem] Warning: server is not reachable. The new token is written to disk.\\n\" +\n \" Restart the server to activate the grace window; reconnect Claude Code after.\",\n );\n }\n\n let updatedCount = 0;\n let configErrors: string[] = [];\n try {\n const result = await applyConfigWithToken(newToken);\n updatedCount = result.updated;\n configErrors = result.errors;\n } catch (err) {\n console.error(\n `[tandem] Warning: failed to update MCP configs: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // TODO(v0.8.1): After rotation, re-walk Cowork workspaces to rewrite\n // env.TANDEM_AUTH_TOKEN so post-rotation Cowork sessions don't 401\n // (security invariant §6 — silent-failure H1). The Tauri IPC dynamic import\n // approach is inert here: this CLI runs as a Node subprocess with no WebView,\n // so `@tauri-apps/api/core`'s `invoke()` has no bridge to Rust. The fix is\n // an HTTP bridge — add a POST /api/cowork-apply-token endpoint in the server\n // (guarded by the auth middleware) and call it from here after the server\n // accepts the rotation.\n\n if (serverRejected) {\n // Configs now reference the new token but the server still holds the old one.\n // Print a strong warning — do NOT print \"Rotated auth token\" as that implies success.\n console.error(\n `[tandem] WARNING: server rejected the rotation request (status: ${serverRejectedStatus}).`,\n );\n if (updatedCount > 0) {\n console.error(\n ` ${updatedCount} config file(s) updated to the new token, but the server still\\n` +\n \" holds the old token. Restart the server to complete rotation.\",\n );\n }\n console.error(` Old fingerprint: ${fingerprint(oldToken)}`);\n console.error(` New fingerprint: ${fingerprint(newToken)}`);\n for (const e of configErrors) {\n console.error(` Warning: could not update config — ${e}`);\n }\n console.error(\"\");\n return;\n }\n\n console.error(\"[tandem] Rotated auth token.\");\n console.error(` Old fingerprint: ${fingerprint(oldToken)}`);\n console.error(` New fingerprint: ${fingerprint(newToken)}`);\n console.error(` Updated ${updatedCount} config file(s).`);\n\n for (const e of configErrors) {\n console.error(` Warning: could not update config — ${e}`);\n }\n\n if (graceWindowActive) {\n console.error(\n \" Old token remains valid for 60 seconds; reconnect Claude Code within that window.\",\n );\n } else {\n console.error(\n \" Server was not running — start it with `tandem` and reconnect Claude Code with the new token.\",\n );\n }\n\n console.error(\"\");\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst SERVER_DIST = resolve(__dirname, \"../server/index.js\");\n\nexport function runStart(): void {\n if (!existsSync(SERVER_DIST)) {\n console.error(`[Tandem] Server not found at ${SERVER_DIST}`);\n console.error(\"[Tandem] The installation may be corrupted. Try: npm install -g tandem-editor\");\n process.exit(1);\n }\n\n console.error(\"[Tandem] Starting server...\");\n\n const proc = spawn(\"node\", [SERVER_DIST], {\n stdio: \"inherit\",\n env: { ...process.env, TANDEM_OPEN_BROWSER: \"1\" },\n });\n\n proc.on(\"error\", (err) => {\n console.error(`[Tandem] Failed to start server: ${err.message}`);\n process.exit(1);\n });\n\n proc.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n\n // Forward signals — proc.kill() with no argument uses SIGTERM on Unix\n // and TerminateProcess on Windows (correct cross-platform behavior).\n // On Windows SIGTERM is not emitted by the OS, but SIGINT (Ctrl+C) works.\n // Both are listed for Unix compatibility.\n for (const sig of [\"SIGINT\", \"SIGTERM\"] as const) {\n process.once(sig, () => proc.kill());\n }\n}\n","/**\n * Tandem CLI — entry point for the `tandem` global command.\n * Shebang is added by tsup banner at build time.\n *\n * Usage:\n * tandem Start the Tandem server and open the editor\n * tandem setup Register Tandem MCP tools with Claude Code / Claude Desktop\n * tandem setup --force Register even if no Claude install is auto-detected\n * tandem --help Show this help\n * tandem --version Show version\n */\n\nimport updateNotifier from \"update-notifier\";\n\nprocess.once(\"uncaughtException\", (err: unknown) => {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err);\n try {\n process.stderr.write(`[tandem cli] uncaughtException: ${msg}\\n`);\n } catch {\n /* EPIPE */\n }\n process.exit(1);\n});\nprocess.once(\"unhandledRejection\", (reason: unknown) => {\n const detail = reason instanceof Error ? reason.message : String(reason);\n process.stderr.write(`[tandem cli] unhandledRejection: ${detail}\\n`);\n process.exit(1);\n});\n\n// Injected at build time by tsup define; declared here for TypeScript\ndeclare const __TANDEM_VERSION__: string;\nconst version = typeof __TANDEM_VERSION__ !== \"undefined\" ? __TANDEM_VERSION__ : \"0.0.0-dev\";\n\nconst args = process.argv.slice(2);\n\n// Skip the update notifier for stdio subcommands — the output is machine-consumed\n// by Claude Desktop's plugin loader, and any incidental write risks corrupting\n// the MCP wire or producing log noise no human will ever read.\nconst isStdioMode = args[0] === \"mcp-stdio\" || args[0] === \"channel\";\nif (!isStdioMode) {\n updateNotifier({ pkg: { name: \"tandem-editor\", version } }).notify();\n}\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`tandem v${version}\n\nUsage:\n tandem Start Tandem server and open the editor\n tandem setup Register MCP tools with Claude Code / Claude Desktop\n tandem setup --force Register to default paths regardless of detection\n tandem setup --with-channel-shim Also register the stdio channel shim (legacy opt-in)\n tandem rotate-token Rotate the auth token with a 60-second grace window\n tandem mcp-stdio Run as a stdio MCP server proxying to local HTTP\n (used by the plugin's Cowork bridge; requires\n tandem server running on the host)\n tandem channel Run the Tandem channel shim (stdio MCP)\n (used by the plugin's tandem-channel entry)\n tandem --version\n tandem --help\n`);\n process.exit(0);\n}\n\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(version);\n process.exit(0);\n}\n\ntry {\n if (args[0] === \"--uninstall-scrub\") {\n // Hidden subcommand invoked by the Tauri NSIS uninstaller hook. Walks\n // Cowork workspaces and removes Tandem plugin entries + firewall rules.\n // Runs inside the already-signed tandem.exe (security invariant §10 —\n // prevents binary-planting during uninstall).\n const { runUninstallScrub } = await import(\"./uninstall-scrub.js\");\n const exitCode = await runUninstallScrub();\n process.exit(exitCode);\n } else if (args[0] === \"setup\") {\n const { runSetup } = await import(\"./setup.js\");\n await runSetup({\n force: args.includes(\"--force\"),\n withChannelShim: args.includes(\"--with-channel-shim\"),\n });\n } else if (args[0] === \"mcp-stdio\") {\n const { runMcpStdio } = await import(\"./mcp-stdio.js\");\n await runMcpStdio();\n } else if (args[0] === \"channel\") {\n const { runChannelCli } = await import(\"./channel.js\");\n await runChannelCli();\n } else if (args[0] === \"rotate-token\") {\n const { rotateToken } = await import(\"./rotate-token.js\");\n await rotateToken();\n } else if (!args[0] || args[0] === \"start\") {\n const { runStart } = await import(\"./start.js\");\n runStart();\n } else {\n console.error(`Unknown command: ${args[0]}`);\n console.error(\"Run 'tandem --help' for usage.\");\n process.exit(1);\n }\n} catch (err) {\n console.error(`\\n[Tandem] Fatal error: ${err instanceof Error ? err.message : String(err)}`);\n console.error(\"If this persists, try reinstalling: npm install -g tandem-editor\\n\");\n process.exit(1);\n}\n"],"mappings":"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","names":["path","write","readFileSync","dirname","resolve","fileURLToPath","__dirname","VALID_TOKEN_RE","detail","buffered","StdioServerTransport","args","resolve","fs","path","fsPromises","path","existsSync","dirname","resolve","fileURLToPath","__dirname","runUninstallScrub","runSetup","runMcpStdio","runChannelCli","rotateToken","runStart"]}
|