tandem-editor 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -356,7 +356,7 @@ var init_uninstall_scrub = __esm({
356
356
  });
357
357
 
358
358
  // src/shared/constants.ts
359
- var DEFAULT_MCP_PORT, TANDEM_REPO_URL, TANDEM_ISSUES_NEW_URL, MAX_FILE_SIZE, MAX_WS_PAYLOAD, IDLE_TIMEOUT, SESSION_MAX_AGE, CHANNEL_MAX_RETRIES, CHANNEL_RETRY_DELAY_MS, CHANNEL_CONNECT_FETCH_TIMEOUT_MS, CHANNEL_SSE_INACTIVITY_TIMEOUT_MS, CHANNEL_MODE_FETCH_TIMEOUT_MS, CHANNEL_AWARENESS_FETCH_TIMEOUT_MS, CHANNEL_ERROR_REPORT_TIMEOUT_MS, CHANNEL_REPLY_FETCH_TIMEOUT_MS, CHANNEL_PERMISSION_FETCH_TIMEOUT_MS, CHANNEL_MAX_SSE_BUFFER_BYTES, TOKEN_FILE_NAME;
359
+ var DEFAULT_MCP_PORT, TANDEM_REPO_URL, TANDEM_ISSUES_NEW_URL, MAX_FILE_SIZE, SESSION_MAX_AGE, CHANNEL_MAX_RETRIES, CHANNEL_RETRY_DELAY_MS, CHANNEL_CONNECT_FETCH_TIMEOUT_MS, CHANNEL_SSE_INACTIVITY_TIMEOUT_MS, CHANNEL_MODE_FETCH_TIMEOUT_MS, CHANNEL_AWARENESS_FETCH_TIMEOUT_MS, CHANNEL_ERROR_REPORT_TIMEOUT_MS, CHANNEL_REPLY_FETCH_TIMEOUT_MS, CHANNEL_PERMISSION_FETCH_TIMEOUT_MS, CHANNEL_MAX_SSE_BUFFER_BYTES, TOKEN_FILE_NAME;
360
360
  var init_constants = __esm({
361
361
  "src/shared/constants.ts"() {
362
362
  "use strict";
@@ -364,8 +364,6 @@ var init_constants = __esm({
364
364
  TANDEM_REPO_URL = "https://github.com/bloknayrb/tandem";
365
365
  TANDEM_ISSUES_NEW_URL = `${TANDEM_REPO_URL}/issues/new`;
366
366
  MAX_FILE_SIZE = 50 * 1024 * 1024;
367
- MAX_WS_PAYLOAD = 10 * 1024 * 1024;
368
- IDLE_TIMEOUT = 30 * 60 * 1e3;
369
367
  SESSION_MAX_AGE = 30 * 24 * 60 * 60 * 1e3;
370
368
  CHANNEL_MAX_RETRIES = 5;
371
369
  CHANNEL_RETRY_DELAY_MS = 2e3;
@@ -667,7 +665,7 @@ var init_setup = __esm({
667
665
  __dirname2 = dirname2(fileURLToPath2(import.meta.url));
668
666
  PACKAGE_ROOT = resolve2(__dirname2, "../..");
669
667
  CHANNEL_DIST = resolve2(PACKAGE_ROOT, "dist/channel/index.js");
670
- MCP_URL = `http://localhost:${DEFAULT_MCP_PORT}`;
668
+ MCP_URL = `http://127.0.0.1:${DEFAULT_MCP_PORT}`;
671
669
  }
672
670
  });
673
671
 
@@ -689,7 +687,7 @@ function resolveTandemUrlCandidate(override) {
689
687
  for (const url of candidates) {
690
688
  if (url !== void 0 && url.trim() !== "") return url.trim();
691
689
  }
692
- return `http://localhost:${DEFAULT_MCP_PORT}`;
690
+ return `http://127.0.0.1:${DEFAULT_MCP_PORT}`;
693
691
  }
694
692
  function resolveAuthTokenCandidate(override) {
695
693
  const candidates = [
@@ -1057,6 +1055,21 @@ ${err.stack ?? ""}
1057
1055
  }
1058
1056
  });
1059
1057
 
1058
+ // src/shared/api-paths.ts
1059
+ var API_EVENTS, API_CHANNEL_AWARENESS, API_CHANNEL_ERROR, API_CHANNEL_REPLY, API_CHANNEL_PERMISSION, API_MODE, API_ROTATE_TOKEN;
1060
+ var init_api_paths = __esm({
1061
+ "src/shared/api-paths.ts"() {
1062
+ "use strict";
1063
+ API_EVENTS = "/api/events";
1064
+ API_CHANNEL_AWARENESS = "/api/channel-awareness";
1065
+ API_CHANNEL_ERROR = "/api/channel-error";
1066
+ API_CHANNEL_REPLY = "/api/channel-reply";
1067
+ API_CHANNEL_PERMISSION = "/api/channel-permission";
1068
+ API_MODE = "/api/mode";
1069
+ API_ROTATE_TOKEN = "/api/rotate-token";
1070
+ }
1071
+ });
1072
+
1060
1073
  // src/shared/fetch-with-timeout.ts
1061
1074
  async function fetchWithTimeout(url, init, timeoutMs) {
1062
1075
  const timeoutSignal = AbortSignal.timeout(timeoutMs);
@@ -1199,6 +1212,48 @@ var init_types = __esm({
1199
1212
  }
1200
1213
  });
1201
1214
 
1215
+ // src/shared/positions/types.ts
1216
+ var init_types2 = __esm({
1217
+ "src/shared/positions/types.ts"() {
1218
+ "use strict";
1219
+ }
1220
+ });
1221
+
1222
+ // src/shared/types.ts
1223
+ import { z } from "zod";
1224
+ var AnnotationTypeSchema, AnnotationStatusSchema, HighlightColorSchema, SeveritySchema, TandemModeSchema, AuthorSchema, ReplyAuthorSchema, AnnotationActionSchema, ExportFormatSchema, DocumentFormatSchema, ToolErrorCodeSchema, ChannelErrorCodeSchema, CHANNEL_CONNECT_FAILED;
1225
+ var init_types3 = __esm({
1226
+ "src/shared/types.ts"() {
1227
+ "use strict";
1228
+ init_types2();
1229
+ AnnotationTypeSchema = z.enum(["highlight", "note", "comment"]);
1230
+ AnnotationStatusSchema = z.enum(["pending", "accepted", "dismissed"]);
1231
+ HighlightColorSchema = z.enum(["yellow", "green", "blue", "pink"]);
1232
+ SeveritySchema = z.enum(["info", "warning", "error", "success"]);
1233
+ TandemModeSchema = z.enum(["solo", "tandem"]);
1234
+ AuthorSchema = z.enum(["user", "claude", "import"]);
1235
+ ReplyAuthorSchema = z.enum(["user", "claude"]);
1236
+ AnnotationActionSchema = z.enum(["accept", "dismiss"]);
1237
+ ExportFormatSchema = z.enum(["markdown", "json"]);
1238
+ DocumentFormatSchema = z.enum(["md", "txt", "html", "docx"]);
1239
+ ToolErrorCodeSchema = z.enum([
1240
+ "RANGE_GONE",
1241
+ "RANGE_MOVED",
1242
+ "FILE_LOCKED",
1243
+ "FILE_NOT_FOUND",
1244
+ "NO_DOCUMENT",
1245
+ "INVALID_RANGE",
1246
+ "INVALID_ARGUMENT",
1247
+ "NOT_FOUND",
1248
+ "ANNOTATION_RESOLVED",
1249
+ "FORMAT_ERROR",
1250
+ "PERMISSION_DENIED"
1251
+ ]);
1252
+ ChannelErrorCodeSchema = z.enum(["CHANNEL_CONNECT_FAILED", "MONITOR_CONNECT_FAILED"]);
1253
+ CHANNEL_CONNECT_FAILED = "CHANNEL_CONNECT_FAILED";
1254
+ }
1255
+ });
1256
+
1202
1257
  // src/channel/event-bridge.ts
1203
1258
  async function startEventBridge(mcp, tandemUrl) {
1204
1259
  let retries = 0;
@@ -1219,12 +1274,12 @@ async function startEventBridge(mcp, tandemUrl) {
1219
1274
  console.error("[Channel] SSE connection exhausted, reporting error and exiting");
1220
1275
  try {
1221
1276
  await fetchWithTimeout(
1222
- `${tandemUrl}/api/channel-error`,
1277
+ `${tandemUrl}${API_CHANNEL_ERROR}`,
1223
1278
  {
1224
1279
  method: "POST",
1225
1280
  headers: { "Content-Type": "application/json" },
1226
1281
  body: JSON.stringify({
1227
- error: "CHANNEL_CONNECT_FAILED",
1282
+ error: CHANNEL_CONNECT_FAILED,
1228
1283
  message: `Channel shim lost connection after ${CHANNEL_MAX_RETRIES} retries.`
1229
1284
  })
1230
1285
  },
@@ -1233,7 +1288,7 @@ async function startEventBridge(mcp, tandemUrl) {
1233
1288
  } catch (reportErr) {
1234
1289
  console.error(
1235
1290
  "[Channel] Could not report failure to server:",
1236
- describeFetchError(reportErr, "/api/channel-error", CHANNEL_ERROR_REPORT_TIMEOUT_MS)
1291
+ describeFetchError(reportErr, API_CHANNEL_ERROR, CHANNEL_ERROR_REPORT_TIMEOUT_MS)
1237
1292
  );
1238
1293
  }
1239
1294
  process.exit(1);
@@ -1252,7 +1307,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
1252
1307
  );
1253
1308
  let res;
1254
1309
  try {
1255
- res = await authFetch(`${tandemUrl}/api/events`, { headers, signal: connectCtrl.signal });
1310
+ res = await authFetch(`${tandemUrl}${API_EVENTS}`, { headers, signal: connectCtrl.signal });
1256
1311
  } finally {
1257
1312
  clearTimeout(connectTimer);
1258
1313
  }
@@ -1276,7 +1331,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
1276
1331
  const AWARENESS_CLEAR_MS = 3e3;
1277
1332
  function clearAwareness(documentId) {
1278
1333
  fetchWithTimeout(
1279
- `${tandemUrl}/api/channel-awareness`,
1334
+ `${tandemUrl}${API_CHANNEL_AWARENESS}`,
1280
1335
  {
1281
1336
  method: "POST",
1282
1337
  headers: { "Content-Type": "application/json" },
@@ -1290,7 +1345,11 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
1290
1345
  ).catch((err) => {
1291
1346
  console.error(
1292
1347
  "[Channel] clearAwareness failed (non-fatal):",
1293
- describeFetchError(err, "/api/channel-awareness clear", CHANNEL_AWARENESS_FETCH_TIMEOUT_MS)
1348
+ describeFetchError(
1349
+ err,
1350
+ `${API_CHANNEL_AWARENESS} clear`,
1351
+ CHANNEL_AWARENESS_FETCH_TIMEOUT_MS
1352
+ )
1294
1353
  );
1295
1354
  });
1296
1355
  }
@@ -1299,7 +1358,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
1299
1358
  const event = pendingAwareness;
1300
1359
  pendingAwareness = null;
1301
1360
  fetchWithTimeout(
1302
- `${tandemUrl}/api/channel-awareness`,
1361
+ `${tandemUrl}${API_CHANNEL_AWARENESS}`,
1303
1362
  {
1304
1363
  method: "POST",
1305
1364
  headers: { "Content-Type": "application/json" },
@@ -1315,7 +1374,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
1315
1374
  "[Channel] Awareness update failed:",
1316
1375
  describeFetchError(
1317
1376
  err,
1318
- "/api/channel-awareness update",
1377
+ `${API_CHANNEL_AWARENESS} update`,
1319
1378
  CHANNEL_AWARENESS_FETCH_TIMEOUT_MS
1320
1379
  )
1321
1380
  );
@@ -1409,7 +1468,11 @@ async function getCachedMode(tandemUrl) {
1409
1468
  const now = Date.now();
1410
1469
  if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;
1411
1470
  try {
1412
- const res = await fetchWithTimeout(`${tandemUrl}/api/mode`, {}, CHANNEL_MODE_FETCH_TIMEOUT_MS);
1471
+ const res = await fetchWithTimeout(
1472
+ `${tandemUrl}${API_MODE}`,
1473
+ {},
1474
+ CHANNEL_MODE_FETCH_TIMEOUT_MS
1475
+ );
1413
1476
  if (res.ok) {
1414
1477
  const { mode } = await res.json();
1415
1478
  cachedMode = mode;
@@ -1420,7 +1483,7 @@ async function getCachedMode(tandemUrl) {
1420
1483
  } catch (err) {
1421
1484
  console.error(
1422
1485
  "[Channel] Mode check failed, delivering event (fail-open):",
1423
- describeFetchError(err, "/api/mode", CHANNEL_MODE_FETCH_TIMEOUT_MS)
1486
+ describeFetchError(err, API_MODE, CHANNEL_MODE_FETCH_TIMEOUT_MS)
1424
1487
  );
1425
1488
  cachedModeAt = now;
1426
1489
  }
@@ -1430,10 +1493,12 @@ var AWARENESS_DEBOUNCE_MS, MODE_CACHE_TTL_MS, cachedMode, cachedModeAt;
1430
1493
  var init_event_bridge = __esm({
1431
1494
  "src/channel/event-bridge.ts"() {
1432
1495
  "use strict";
1496
+ init_api_paths();
1433
1497
  init_cli_runtime();
1434
1498
  init_constants();
1435
1499
  init_types();
1436
1500
  init_fetch_with_timeout();
1501
+ init_types3();
1437
1502
  AWARENESS_DEBOUNCE_MS = 500;
1438
1503
  MODE_CACHE_TTL_MS = 2e3;
1439
1504
  cachedMode = "tandem";
@@ -1446,7 +1511,7 @@ import { createConnection } from "net";
1446
1511
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1447
1512
  import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
1448
1513
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
1449
- import { z } from "zod";
1514
+ import { z as z2 } from "zod";
1450
1515
  async function runChannel(opts = {}) {
1451
1516
  redirectConsoleToStderr();
1452
1517
  const tandemUrl = resolveTandemUrl();
@@ -1501,7 +1566,7 @@ async function runChannel(opts = {}) {
1501
1566
  const args2 = req.params.arguments;
1502
1567
  try {
1503
1568
  const res = await fetchWithTimeout(
1504
- `${tandemUrl}/api/channel-reply`,
1569
+ `${tandemUrl}${API_CHANNEL_REPLY}`,
1505
1570
  {
1506
1571
  method: "POST",
1507
1572
  headers: { "Content-Type": "application/json" },
@@ -1535,7 +1600,7 @@ async function runChannel(opts = {}) {
1535
1600
  type: "text",
1536
1601
  text: `Failed to send reply: ${describeFetchError(
1537
1602
  err,
1538
- "/api/channel-reply",
1603
+ API_CHANNEL_REPLY,
1539
1604
  CHANNEL_REPLY_FETCH_TIMEOUT_MS
1540
1605
  )}`
1541
1606
  }
@@ -1546,19 +1611,19 @@ async function runChannel(opts = {}) {
1546
1611
  }
1547
1612
  throw new Error(`Unknown tool: ${req.params.name}`);
1548
1613
  });
1549
- const PermissionRequestSchema = z.object({
1550
- method: z.literal("notifications/claude/channel/permission_request"),
1551
- params: z.object({
1552
- request_id: z.string(),
1553
- tool_name: z.string(),
1554
- description: z.string(),
1555
- input_preview: z.string()
1614
+ const PermissionRequestSchema = z2.object({
1615
+ method: z2.literal("notifications/claude/channel/permission_request"),
1616
+ params: z2.object({
1617
+ request_id: z2.string(),
1618
+ tool_name: z2.string(),
1619
+ description: z2.string(),
1620
+ input_preview: z2.string()
1556
1621
  })
1557
1622
  });
1558
1623
  mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
1559
1624
  try {
1560
1625
  const res = await fetchWithTimeout(
1561
- `${tandemUrl}/api/channel-permission`,
1626
+ `${tandemUrl}${API_CHANNEL_PERMISSION}`,
1562
1627
  {
1563
1628
  method: "POST",
1564
1629
  headers: { "Content-Type": "application/json" },
@@ -1579,7 +1644,7 @@ async function runChannel(opts = {}) {
1579
1644
  } catch (err) {
1580
1645
  console.error(
1581
1646
  "[Channel] Failed to forward permission request:",
1582
- describeFetchError(err, "/api/channel-permission", CHANNEL_PERMISSION_FETCH_TIMEOUT_MS)
1647
+ describeFetchError(err, API_CHANNEL_PERMISSION, CHANNEL_PERMISSION_FETCH_TIMEOUT_MS)
1583
1648
  );
1584
1649
  }
1585
1650
  });
@@ -1605,7 +1670,7 @@ async function checkServerReachable(url, timeoutMs = 2e3) {
1605
1670
  parsed = new URL(url);
1606
1671
  } catch {
1607
1672
  console.error(
1608
- `[Channel] Invalid TANDEM_URL: "${url}" \u2014 expected format: http://localhost:3479`
1673
+ `[Channel] Invalid TANDEM_URL: "${url}" \u2014 expected format: http://127.0.0.1:3479`
1609
1674
  );
1610
1675
  return false;
1611
1676
  }
@@ -1630,6 +1695,7 @@ async function checkServerReachable(url, timeoutMs = 2e3) {
1630
1695
  var init_run = __esm({
1631
1696
  "src/channel/run.ts"() {
1632
1697
  "use strict";
1698
+ init_api_paths();
1633
1699
  init_cli_runtime();
1634
1700
  init_constants();
1635
1701
  init_fetch_with_timeout();
@@ -1739,7 +1805,7 @@ async function rotateToken() {
1739
1805
  let serverRejected = false;
1740
1806
  let serverRejectedStatus = 0;
1741
1807
  try {
1742
- const resp = await fetch(`${serverUrl}/api/rotate-token`, {
1808
+ const resp = await fetch(`${serverUrl}${API_ROTATE_TOKEN}`, {
1743
1809
  method: "POST",
1744
1810
  headers: {
1745
1811
  "Content-Type": "application/json",
@@ -1809,6 +1875,7 @@ async function rotateToken() {
1809
1875
  var init_rotate_token = __esm({
1810
1876
  "src/cli/rotate-token.ts"() {
1811
1877
  "use strict";
1878
+ init_api_paths();
1812
1879
  init_token_file();
1813
1880
  init_cli_runtime();
1814
1881
  init_setup();
@@ -1830,10 +1897,14 @@ function runStart() {
1830
1897
  console.error("[Tandem] The installation may be corrupted. Try: npm install -g tandem-editor");
1831
1898
  process.exit(1);
1832
1899
  }
1900
+ console.error(
1901
+ "[Tandem] Browser distribution is deprecated; the Tauri desktop app is the primary form factor."
1902
+ );
1903
+ console.error("[Tandem] See https://github.com/bloknayrb/tandem/issues/477 for context.");
1833
1904
  console.error("[Tandem] Starting server...");
1834
1905
  const proc = spawn("node", [SERVER_DIST], {
1835
1906
  stdio: "inherit",
1836
- env: { ...process.env, TANDEM_OPEN_BROWSER: "1" }
1907
+ env: process.env
1837
1908
  });
1838
1909
  proc.on("error", (err) => {
1839
1910
  console.error(`[Tandem] Failed to start server: ${err.message}`);
@@ -1872,7 +1943,7 @@ process.once("unhandledRejection", (reason) => {
1872
1943
  `);
1873
1944
  process.exit(1);
1874
1945
  });
1875
- var version = true ? "0.11.2" : "0.0.0-dev";
1946
+ var version = true ? "0.12.0" : "0.0.0-dev";
1876
1947
  var args = process.argv.slice(2);
1877
1948
  var isStdioMode = args[0] === "mcp-stdio" || args[0] === "channel";
1878
1949
  if (!isStdioMode) {