tandem-editor 0.6.2 → 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.
- package/CHANGELOG.md +407 -28
- package/dist/channel/index.js +41 -7
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +477 -58
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/index-B1Cd5UGT.js +349 -0
- package/dist/client/index.html +48 -1
- package/dist/monitor/index.js +24 -2
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +61109 -59854
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/client/assets/index-mo5ZOPfU.js +0 -349
package/dist/channel/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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);
|