tandem-editor 0.6.3 → 0.7.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.
@@ -17925,6 +17925,25 @@ function resolveTandemUrl(override) {
17925
17925
  const raw = override ?? process.env.TANDEM_URL ?? `http://localhost:${DEFAULT_MCP_PORT}`;
17926
17926
  return raw.replace(/\/$/, "");
17927
17927
  }
17928
+ var VALID_TOKEN_RE = /^[A-Za-z0-9_\-]{32,}$/;
17929
+ var _warnedInvalidToken = false;
17930
+ async function authFetch(url, init) {
17931
+ const token = process.env.TANDEM_AUTH_TOKEN;
17932
+ if (token !== void 0 && token.trim() !== "") {
17933
+ if (VALID_TOKEN_RE.test(token.trim())) {
17934
+ const headers = new Headers(init?.headers);
17935
+ headers.set("Authorization", `Bearer ${token.trim()}`);
17936
+ return fetch(url, { ...init, headers });
17937
+ }
17938
+ if (!_warnedInvalidToken) {
17939
+ _warnedInvalidToken = true;
17940
+ console.error(
17941
+ "[tandem] authFetch: TANDEM_AUTH_TOKEN is set but invalid (must be 32+ alphanumeric chars [A-Za-z0-9_-]); sending without Authorization header"
17942
+ );
17943
+ }
17944
+ }
17945
+ return fetch(url, init);
17946
+ }
17928
17947
 
17929
17948
  // src/server/events/types.ts
17930
17949
  var VALID_EVENT_TYPES = /* @__PURE__ */ new Set([
@@ -18042,7 +18061,7 @@ async function startEventBridge(mcp, tandemUrl) {
18042
18061
  if (retries >= CHANNEL_MAX_RETRIES) {
18043
18062
  console.error("[Channel] SSE connection exhausted, reporting error and exiting");
18044
18063
  try {
18045
- await fetch(`${tandemUrl}/api/channel-error`, {
18064
+ await authFetch(`${tandemUrl}/api/channel-error`, {
18046
18065
  method: "POST",
18047
18066
  headers: { "Content-Type": "application/json" },
18048
18067
  body: JSON.stringify({
@@ -18065,7 +18084,7 @@ async function startEventBridge(mcp, tandemUrl) {
18065
18084
  async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
18066
18085
  const headers = { Accept: "text/event-stream" };
18067
18086
  if (lastEventId) headers["Last-Event-ID"] = lastEventId;
18068
- const res = await fetch(`${tandemUrl}/api/events`, { headers });
18087
+ const res = await authFetch(`${tandemUrl}/api/events`, { headers });
18069
18088
  if (!res.ok) throw new Error(`SSE endpoint returned ${res.status}`);
18070
18089
  if (!res.body) throw new Error("SSE endpoint returned no body");
18071
18090
  const reader = res.body.getReader();
@@ -18076,7 +18095,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
18076
18095
  let pendingAwareness = null;
18077
18096
  const AWARENESS_CLEAR_MS = 3e3;
18078
18097
  function clearAwareness(documentId) {
18079
- fetch(`${tandemUrl}/api/channel-awareness`, {
18098
+ authFetch(`${tandemUrl}/api/channel-awareness`, {
18080
18099
  method: "POST",
18081
18100
  headers: { "Content-Type": "application/json" },
18082
18101
  body: JSON.stringify({
@@ -18091,7 +18110,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
18091
18110
  if (!pendingAwareness) return;
18092
18111
  const event = pendingAwareness;
18093
18112
  pendingAwareness = null;
18094
- fetch(`${tandemUrl}/api/channel-awareness`, {
18113
+ authFetch(`${tandemUrl}/api/channel-awareness`, {
18095
18114
  method: "POST",
18096
18115
  headers: { "Content-Type": "application/json" },
18097
18116
  body: JSON.stringify({
@@ -18168,7 +18187,7 @@ async function getCachedMode(tandemUrl) {
18168
18187
  const now = Date.now();
18169
18188
  if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;
18170
18189
  try {
18171
- const res = await fetch(`${tandemUrl}/api/mode`);
18190
+ const res = await authFetch(`${tandemUrl}/api/mode`);
18172
18191
  if (res.ok) {
18173
18192
  const { mode } = await res.json();
18174
18193
  cachedMode = mode;
@@ -18240,7 +18259,7 @@ async function runChannel(opts = {}) {
18240
18259
  if (req.params.name === "tandem_reply") {
18241
18260
  const args = req.params.arguments;
18242
18261
  try {
18243
- const res = await fetch(`${tandemUrl}/api/channel-reply`, {
18262
+ const res = await authFetch(`${tandemUrl}/api/channel-reply`, {
18244
18263
  method: "POST",
18245
18264
  headers: { "Content-Type": "application/json" },
18246
18265
  body: JSON.stringify(args)
@@ -18288,7 +18307,7 @@ async function runChannel(opts = {}) {
18288
18307
  });
18289
18308
  mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
18290
18309
  try {
18291
- const res = await fetch(`${tandemUrl}/api/channel-permission`, {
18310
+ const res = await authFetch(`${tandemUrl}/api/channel-permission`, {
18292
18311
  method: "POST",
18293
18312
  headers: { "Content-Type": "application/json" },
18294
18313
  body: JSON.stringify({
@@ -18353,6 +18372,21 @@ async function checkServerReachable(url, timeoutMs = 2e3) {
18353
18372
  }
18354
18373
 
18355
18374
  // src/channel/index.ts
18375
+ process.once("uncaughtException", (err) => {
18376
+ const msg = err instanceof Error ? err.stack ?? err.message : String(err);
18377
+ try {
18378
+ process.stderr.write(`[tandem channel] uncaughtException: ${msg}
18379
+ `);
18380
+ } catch {
18381
+ }
18382
+ process.exit(1);
18383
+ });
18384
+ process.once("unhandledRejection", (reason) => {
18385
+ const detail = reason instanceof Error ? reason.message : String(reason);
18386
+ process.stderr.write(`[tandem channel] unhandledRejection: ${detail}
18387
+ `);
18388
+ process.exit(1);
18389
+ });
18356
18390
  runChannel().catch((err) => {
18357
18391
  console.error("[Channel] Fatal error:", err);
18358
18392
  process.exit(1);