tandem-editor 0.16.0 → 0.18.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/.claude-plugin/plugin.json +4 -4
- package/CHANGELOG.md +38 -1
- package/dist/cli/index.js +486 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/{CoworkSettings-B4LapjhG.js → CoworkSettings-Brg8F0cb.js} +1 -1
- package/dist/client/assets/{dist-CJtsfiU2.js → dist-CHppK3gQ.js} +1 -1
- package/dist/client/assets/index-Boow3rxN.css +1 -0
- package/dist/client/assets/index-BymYYAwj.js +271 -0
- package/dist/client/assets/{prod-p35ShryZ.js → prod-Pu_njNLv.js} +1 -1
- package/dist/client/index.html +21 -5
- package/dist/monitor/index.js +6 -3
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +324 -14
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/tandem/SKILL.md +2 -2
- package/dist/client/assets/index-BIEOrpU_.js +0 -270
- package/dist/client/assets/index-DdjGUhNd.css +0 -1
package/dist/server/index.js
CHANGED
|
@@ -93543,8 +93543,8 @@ function resolveChannelDist(env3 = process.env, exists = existsSync) {
|
|
|
93543
93543
|
return resolve3(PACKAGE_ROOT, "dist/channel/index.js");
|
|
93544
93544
|
}
|
|
93545
93545
|
function resolveCliVersion() {
|
|
93546
|
-
if (true) return "0.
|
|
93547
|
-
if (true) return "0.
|
|
93546
|
+
if (true) return "0.18.0";
|
|
93547
|
+
if (true) return "0.18.0";
|
|
93548
93548
|
for (const rel of ["../../package.json", "../../../package.json"]) {
|
|
93549
93549
|
try {
|
|
93550
93550
|
const pkg = JSON.parse(readFileSync2(resolve3(__dirname2, rel), "utf8"));
|
|
@@ -129433,6 +129433,10 @@ import { request } from "http";
|
|
|
129433
129433
|
import { createConnection } from "net";
|
|
129434
129434
|
import { homedir as homedir3, platform } from "os";
|
|
129435
129435
|
import { join as join5 } from "path";
|
|
129436
|
+
function errorClass(err) {
|
|
129437
|
+
if (err instanceof Error) return err.name;
|
|
129438
|
+
return typeof err;
|
|
129439
|
+
}
|
|
129436
129440
|
function checkNodeVersion(r) {
|
|
129437
129441
|
const version3 = process.version;
|
|
129438
129442
|
const major2 = Number.parseInt(version3.slice(1), 10);
|
|
@@ -129452,6 +129456,201 @@ function checkNodeModules(r) {
|
|
|
129452
129456
|
r.fail("node_modules/ not found", "npm install");
|
|
129453
129457
|
}
|
|
129454
129458
|
}
|
|
129459
|
+
function recordEvaluation(r, result2) {
|
|
129460
|
+
if (!result2) return;
|
|
129461
|
+
if (result2.status === "warn") {
|
|
129462
|
+
r.warn(result2.message, result2.fix, result2.data);
|
|
129463
|
+
return;
|
|
129464
|
+
}
|
|
129465
|
+
if (result2.status === "skip") {
|
|
129466
|
+
r.pass(`skipped \u2014 ${result2.message}`, result2.fix, { ...result2.data, skipped: true });
|
|
129467
|
+
return;
|
|
129468
|
+
}
|
|
129469
|
+
r.pass(result2.message, result2.fix, result2.data);
|
|
129470
|
+
}
|
|
129471
|
+
function probeTandemEditorRepo(dir) {
|
|
129472
|
+
const read = readJson2(join5(dir, "package.json"));
|
|
129473
|
+
if (read.kind === "absent") return "no";
|
|
129474
|
+
if (read.kind === "unreadable") return "unreadable";
|
|
129475
|
+
const parsed = read.value;
|
|
129476
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
129477
|
+
return "unreadable";
|
|
129478
|
+
}
|
|
129479
|
+
return parsed.name === "tandem-editor" ? "yes" : "no";
|
|
129480
|
+
}
|
|
129481
|
+
function readJson2(path33) {
|
|
129482
|
+
let raw;
|
|
129483
|
+
try {
|
|
129484
|
+
raw = readFileSync3(path33, "utf-8");
|
|
129485
|
+
} catch (err) {
|
|
129486
|
+
const code3 = err.code;
|
|
129487
|
+
if (code3 === "ENOENT") return { kind: "absent" };
|
|
129488
|
+
return { kind: "unreadable", reason: code3 ?? errorClass(err) };
|
|
129489
|
+
}
|
|
129490
|
+
try {
|
|
129491
|
+
return { kind: "ok", value: JSON.parse(raw) };
|
|
129492
|
+
} catch {
|
|
129493
|
+
return { kind: "unreadable", reason: "not valid JSON" };
|
|
129494
|
+
}
|
|
129495
|
+
}
|
|
129496
|
+
function parseLockfileEntry(value) {
|
|
129497
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
129498
|
+
const entry = value;
|
|
129499
|
+
if (entry.version !== void 0 && typeof entry.version !== "string") return null;
|
|
129500
|
+
if (entry.optional !== void 0 && typeof entry.optional !== "boolean") return null;
|
|
129501
|
+
return {
|
|
129502
|
+
version: entry.version,
|
|
129503
|
+
optional: entry.optional
|
|
129504
|
+
};
|
|
129505
|
+
}
|
|
129506
|
+
function parseLockfileJson(value) {
|
|
129507
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
129508
|
+
const obj2 = value;
|
|
129509
|
+
if (obj2.version !== void 0 && typeof obj2.version !== "string") return null;
|
|
129510
|
+
const version3 = obj2.version;
|
|
129511
|
+
if (obj2.packages === void 0) return { version: version3 };
|
|
129512
|
+
if (typeof obj2.packages !== "object" || obj2.packages === null || Array.isArray(obj2.packages)) {
|
|
129513
|
+
return null;
|
|
129514
|
+
}
|
|
129515
|
+
const packages = {};
|
|
129516
|
+
for (const [path33, raw] of Object.entries(obj2.packages)) {
|
|
129517
|
+
const entry = parseLockfileEntry(raw);
|
|
129518
|
+
if (!entry) return null;
|
|
129519
|
+
packages[path33] = entry;
|
|
129520
|
+
}
|
|
129521
|
+
return { version: version3, packages };
|
|
129522
|
+
}
|
|
129523
|
+
function evaluateNpmStaleness(pkgInput, lockInput, hiddenLockInput) {
|
|
129524
|
+
if (!pkgInput || !lockInput || !hiddenLockInput) return null;
|
|
129525
|
+
const lock = parseLockfileJson(lockInput);
|
|
129526
|
+
if (!lock) {
|
|
129527
|
+
return {
|
|
129528
|
+
status: "skip",
|
|
129529
|
+
message: "cannot compare (package-lock.json has an unexpected structure)",
|
|
129530
|
+
fix: "Restore it from git: git checkout package-lock.json",
|
|
129531
|
+
data: { reason: "malformed-lock" }
|
|
129532
|
+
};
|
|
129533
|
+
}
|
|
129534
|
+
const hiddenLock = parseLockfileJson(hiddenLockInput);
|
|
129535
|
+
if (!hiddenLock) {
|
|
129536
|
+
return {
|
|
129537
|
+
status: "skip",
|
|
129538
|
+
message: "cannot compare (node_modules/.package-lock.json has an unexpected structure)",
|
|
129539
|
+
fix: "npm install",
|
|
129540
|
+
data: { reason: "malformed-hidden-lock" }
|
|
129541
|
+
};
|
|
129542
|
+
}
|
|
129543
|
+
const pkg = typeof pkgInput === "object" && !Array.isArray(pkgInput) ? pkgInput : {};
|
|
129544
|
+
const pkgVersion = typeof pkg.version === "string" ? pkg.version : void 0;
|
|
129545
|
+
if (pkgVersion && lock.version && pkgVersion !== lock.version) {
|
|
129546
|
+
return {
|
|
129547
|
+
status: "warn",
|
|
129548
|
+
message: `package-lock.json (v${lock.version}) is out of date with package.json (v${pkgVersion})`,
|
|
129549
|
+
fix: "npm install",
|
|
129550
|
+
data: { packageVersion: pkgVersion, lockVersion: lock.version }
|
|
129551
|
+
};
|
|
129552
|
+
}
|
|
129553
|
+
if (lock.version && hiddenLock.version && lock.version !== hiddenLock.version) {
|
|
129554
|
+
return {
|
|
129555
|
+
status: "warn",
|
|
129556
|
+
message: `node_modules was installed from a different lockfile (v${hiddenLock.version} installed, v${lock.version} expected)`,
|
|
129557
|
+
fix: "npm install",
|
|
129558
|
+
data: { lockVersion: lock.version, installedVersion: hiddenLock.version }
|
|
129559
|
+
};
|
|
129560
|
+
}
|
|
129561
|
+
const wanted = lock.packages ?? {};
|
|
129562
|
+
const installed = hiddenLock.packages ?? {};
|
|
129563
|
+
const wantedCount = Object.keys(wanted).filter((path33) => path33 !== "").length;
|
|
129564
|
+
if (wantedCount === 0) {
|
|
129565
|
+
return {
|
|
129566
|
+
status: "skip",
|
|
129567
|
+
message: "cannot compare (package-lock.json lists no packages \u2014 a lockfileVersion 1 file, or one written by something other than npm)",
|
|
129568
|
+
fix: "npm install",
|
|
129569
|
+
// No inferred `lockfileVersion` here: we did not read that field, and
|
|
129570
|
+
// guessing it from the presence of `packages` would put a fabricated
|
|
129571
|
+
// value under a real npm field name.
|
|
129572
|
+
data: { reason: "no-comparable-packages" }
|
|
129573
|
+
};
|
|
129574
|
+
}
|
|
129575
|
+
const drifted = [];
|
|
129576
|
+
for (const [path33, entry] of Object.entries(wanted)) {
|
|
129577
|
+
if (path33 === "") continue;
|
|
129578
|
+
const got = installed[path33];
|
|
129579
|
+
if (!got) {
|
|
129580
|
+
if (!entry.optional) drifted.push(path33);
|
|
129581
|
+
} else if (entry.version !== got.version) {
|
|
129582
|
+
drifted.push(path33);
|
|
129583
|
+
}
|
|
129584
|
+
}
|
|
129585
|
+
for (const path33 of Object.keys(installed)) {
|
|
129586
|
+
if (path33 !== "" && !(path33 in wanted)) drifted.push(path33);
|
|
129587
|
+
}
|
|
129588
|
+
if (drifted.length > 0) {
|
|
129589
|
+
return {
|
|
129590
|
+
status: "warn",
|
|
129591
|
+
message: `node_modules is stale \u2014 ${drifted.length} package(s) differ from package-lock.json (e.g. after a pull or branch switch)`,
|
|
129592
|
+
fix: "npm install",
|
|
129593
|
+
data: { driftCount: drifted.length, sample: drifted.slice(0, 5) }
|
|
129594
|
+
};
|
|
129595
|
+
}
|
|
129596
|
+
const missingVersions = [];
|
|
129597
|
+
if (!pkgVersion) missingVersions.push("package.json");
|
|
129598
|
+
if (!lock.version) missingVersions.push("package-lock.json");
|
|
129599
|
+
if (!hiddenLock.version) missingVersions.push("node_modules/.package-lock.json");
|
|
129600
|
+
if (missingVersions.length > 0) {
|
|
129601
|
+
return {
|
|
129602
|
+
status: "skip",
|
|
129603
|
+
message: `node_modules matches package-lock.json, but the version check could not run \u2014 no "version" field in ${missingVersions.join(", ")}`,
|
|
129604
|
+
data: { reason: "no-comparable-version", missingVersions }
|
|
129605
|
+
};
|
|
129606
|
+
}
|
|
129607
|
+
return {
|
|
129608
|
+
status: "pass",
|
|
129609
|
+
message: "node_modules matches package-lock.json",
|
|
129610
|
+
data: { packageCount: Object.keys(installed).length }
|
|
129611
|
+
};
|
|
129612
|
+
}
|
|
129613
|
+
function readLockfileOrReport(r, path33, label) {
|
|
129614
|
+
const read = readJson2(path33);
|
|
129615
|
+
if (read.kind === "absent") {
|
|
129616
|
+
r.pass(`skipped \u2014 cannot compare (${label} not found)`, void 0, {
|
|
129617
|
+
skipped: true,
|
|
129618
|
+
reason: "absent",
|
|
129619
|
+
path: label
|
|
129620
|
+
});
|
|
129621
|
+
return { ok: false };
|
|
129622
|
+
}
|
|
129623
|
+
if (read.kind === "ok") {
|
|
129624
|
+
if (typeof read.value === "object" && read.value !== null && !Array.isArray(read.value)) {
|
|
129625
|
+
return { ok: true, value: read.value };
|
|
129626
|
+
}
|
|
129627
|
+
r.warn(
|
|
129628
|
+
`${label} is not a JSON object \u2014 npm install staleness cannot be checked`,
|
|
129629
|
+
"Check for a truncated or hand-edited file, then: npm install",
|
|
129630
|
+
{ reason: "not-an-object", path: label }
|
|
129631
|
+
);
|
|
129632
|
+
return { ok: false };
|
|
129633
|
+
}
|
|
129634
|
+
r.warn(
|
|
129635
|
+
`${label} could not be read (${read.reason}) \u2014 npm install staleness cannot be checked`,
|
|
129636
|
+
"Check for merge-conflict markers or a truncated file, then: npm install",
|
|
129637
|
+
{ reason: read.reason, path: label }
|
|
129638
|
+
);
|
|
129639
|
+
return { ok: false };
|
|
129640
|
+
}
|
|
129641
|
+
function checkNpmStaleness(r, repoDir) {
|
|
129642
|
+
const pkgRead = readJson2(join5(repoDir, "package.json"));
|
|
129643
|
+
const pkg = pkgRead.kind === "ok" ? pkgRead.value : null;
|
|
129644
|
+
const lock = readLockfileOrReport(r, join5(repoDir, "package-lock.json"), "package-lock.json");
|
|
129645
|
+
if (!lock.ok) return;
|
|
129646
|
+
const hiddenLock = readLockfileOrReport(
|
|
129647
|
+
r,
|
|
129648
|
+
join5(repoDir, "node_modules", ".package-lock.json"),
|
|
129649
|
+
"node_modules/.package-lock.json"
|
|
129650
|
+
);
|
|
129651
|
+
if (!hiddenLock.ok) return;
|
|
129652
|
+
recordEvaluation(r, evaluateNpmStaleness(pkg, lock.value, hiddenLock.value));
|
|
129653
|
+
}
|
|
129455
129654
|
function checkMcpJson(r) {
|
|
129456
129655
|
const mcpPath = join5(process.cwd(), ".mcp.json");
|
|
129457
129656
|
if (!existsSync2(mcpPath)) {
|
|
@@ -129577,14 +129776,64 @@ async function checkPorts(r, wsPort2, mcpPort2) {
|
|
|
129577
129776
|
}
|
|
129578
129777
|
return { ws, mcp };
|
|
129579
129778
|
}
|
|
129779
|
+
function evaluateOrphanedVite(input) {
|
|
129780
|
+
const { viteUp, viteConfirmed, wsUp, mcpUp, wsPort: wsPort2, mcpPort: mcpPort2, vitePort } = input;
|
|
129781
|
+
if (!viteUp) return null;
|
|
129782
|
+
if (!viteConfirmed) {
|
|
129783
|
+
return {
|
|
129784
|
+
status: "skip",
|
|
129785
|
+
message: `cannot identify the process on :${vitePort} \u2014 it is listening but did not answer /@vite/client, so it is probably not a Vite dev server`,
|
|
129786
|
+
data: { vite: false, ws: wsUp, mcp: mcpUp, reason: "unconfirmed-vite" }
|
|
129787
|
+
};
|
|
129788
|
+
}
|
|
129789
|
+
if (!wsUp && !mcpUp) {
|
|
129790
|
+
return {
|
|
129791
|
+
status: "warn",
|
|
129792
|
+
message: `Vite dev server on :${vitePort} is running but the backend (:${wsPort2} + :${mcpPort2}) is down \u2014 likely orphaned by a crashed dev session`,
|
|
129793
|
+
fix: `If you meant to run the client alone (npm run dev:client), this is expected. Otherwise kill the process on :${vitePort} and restart: npm run dev:standalone`,
|
|
129794
|
+
data: { vite: true, ws: wsUp, mcp: mcpUp }
|
|
129795
|
+
};
|
|
129796
|
+
}
|
|
129797
|
+
if (wsUp !== mcpUp) {
|
|
129798
|
+
return {
|
|
129799
|
+
status: "skip",
|
|
129800
|
+
message: `cannot tell whether the Vite dev server on :${vitePort} is orphaned \u2014 the backend is only partially up (:${wsPort2} ${wsUp ? "up" : "down"}, :${mcpPort2} ${mcpUp ? "up" : "down"}); see the ports check`,
|
|
129801
|
+
data: { vite: true, ws: wsUp, mcp: mcpUp, reason: "partial-backend" }
|
|
129802
|
+
};
|
|
129803
|
+
}
|
|
129804
|
+
return {
|
|
129805
|
+
status: "pass",
|
|
129806
|
+
message: `Vite dev server (:${vitePort}) running alongside the backend`,
|
|
129807
|
+
data: { vite: true, ws: wsUp, mcp: mcpUp }
|
|
129808
|
+
};
|
|
129809
|
+
}
|
|
129810
|
+
async function checkOrphanedVite(r, wsUp, mcpUp, wsPort2, mcpPort2, vitePort) {
|
|
129811
|
+
const viteUp = await probePort(vitePort);
|
|
129812
|
+
const viteConfirmed = viteUp ? await isViteDevServer(vitePort) : false;
|
|
129813
|
+
recordEvaluation(
|
|
129814
|
+
r,
|
|
129815
|
+
evaluateOrphanedVite({ viteUp, viteConfirmed, wsUp, mcpUp, wsPort: wsPort2, mcpPort: mcpPort2, vitePort })
|
|
129816
|
+
);
|
|
129817
|
+
}
|
|
129580
129818
|
function httpGet(url, timeoutMs = 3e3) {
|
|
129581
129819
|
return new Promise((resolve5) => {
|
|
129582
129820
|
const req = request(url, { timeout: timeoutMs }, (res) => {
|
|
129583
129821
|
let body = "";
|
|
129822
|
+
let bytes = 0;
|
|
129823
|
+
let truncated = false;
|
|
129584
129824
|
res.on("data", (chunk2) => {
|
|
129825
|
+
if (truncated) return;
|
|
129826
|
+
bytes += Buffer.byteLength(chunk2);
|
|
129827
|
+
if (bytes > HTTP_MAX_BYTES) {
|
|
129828
|
+
truncated = true;
|
|
129829
|
+
res.destroy();
|
|
129830
|
+
resolve5({ status: res.statusCode, data: null });
|
|
129831
|
+
return;
|
|
129832
|
+
}
|
|
129585
129833
|
body += chunk2;
|
|
129586
129834
|
});
|
|
129587
129835
|
res.on("end", () => {
|
|
129836
|
+
if (truncated) return;
|
|
129588
129837
|
try {
|
|
129589
129838
|
resolve5({ status: res.statusCode, data: JSON.parse(body) });
|
|
129590
129839
|
} catch {
|
|
@@ -129600,6 +129849,10 @@ function httpGet(url, timeoutMs = 3e3) {
|
|
|
129600
129849
|
req.end();
|
|
129601
129850
|
});
|
|
129602
129851
|
}
|
|
129852
|
+
async function isViteDevServer(port) {
|
|
129853
|
+
const result2 = await httpGet(`http://127.0.0.1:${port}/@vite/client`, 2e3);
|
|
129854
|
+
return result2?.status === 200;
|
|
129855
|
+
}
|
|
129603
129856
|
async function checkHealth(r, mcpPort2) {
|
|
129604
129857
|
const result2 = await httpGet(`http://127.0.0.1:${mcpPort2}/health`);
|
|
129605
129858
|
if (!result2) {
|
|
@@ -129839,7 +130092,7 @@ function evaluateStaleGlobal(bundled, globalVersion) {
|
|
|
129839
130092
|
};
|
|
129840
130093
|
}
|
|
129841
130094
|
async function checkStaleGlobal(r) {
|
|
129842
|
-
const bundled = true ? "0.
|
|
130095
|
+
const bundled = true ? "0.18.0" : null;
|
|
129843
130096
|
if (!bundled) return;
|
|
129844
130097
|
let globalVersion;
|
|
129845
130098
|
try {
|
|
@@ -129858,16 +130111,37 @@ async function checkStaleGlobal(r) {
|
|
|
129858
130111
|
async function runDoctor(opts = {}) {
|
|
129859
130112
|
const wsPort2 = opts.wsPort ?? DEFAULT_WS_PORT;
|
|
129860
130113
|
const mcpPort2 = opts.mcpPort ?? DEFAULT_MCP_PORT;
|
|
130114
|
+
const vitePort = opts.vitePort ?? VITE_DEV_PORT;
|
|
129861
130115
|
const r = new Recorder();
|
|
130116
|
+
const cwd = process.cwd();
|
|
130117
|
+
const repo = probeTandemEditorRepo(cwd);
|
|
130118
|
+
const devRepo = repo === "yes";
|
|
129862
130119
|
await r.check("node-version", () => checkNodeVersion(r));
|
|
129863
130120
|
await r.check("node-modules", () => checkNodeModules(r));
|
|
130121
|
+
if (repo === "unreadable") {
|
|
130122
|
+
await r.check(
|
|
130123
|
+
"dev-repo",
|
|
130124
|
+
() => r.warn(
|
|
130125
|
+
"package.json in the current directory could not be read \u2014 if this is the tandem-editor checkout, the npm-staleness and orphaned-Vite checks are being skipped",
|
|
130126
|
+
"Check for merge-conflict markers or a truncated file: git checkout package.json"
|
|
130127
|
+
)
|
|
130128
|
+
);
|
|
130129
|
+
}
|
|
130130
|
+
if (devRepo) {
|
|
130131
|
+
await r.check("npm-staleness", () => checkNpmStaleness(r, cwd));
|
|
130132
|
+
}
|
|
129864
130133
|
await r.check("mcp-json", () => checkMcpJson(r));
|
|
129865
130134
|
await r.check("user-mcp-config", () => checkUserMcpConfig(r));
|
|
129866
130135
|
await r.check("annotation-store", () => checkAnnotationStore(r));
|
|
129867
130136
|
await r.check("stale-global", () => checkStaleGlobal(r));
|
|
129868
|
-
const
|
|
130137
|
+
const ports = await r.check("ports", () => checkPorts(r, wsPort2, mcpPort2));
|
|
130138
|
+
const ws = ports?.ws ?? false;
|
|
130139
|
+
const mcp = ports?.mcp ?? false;
|
|
130140
|
+
if (devRepo) {
|
|
130141
|
+
await r.check("orphaned-vite", () => checkOrphanedVite(r, ws, mcp, wsPort2, mcpPort2, vitePort));
|
|
130142
|
+
}
|
|
129869
130143
|
if (mcp) {
|
|
129870
|
-
const healthy = await r.check("health", () => checkHealth(r, mcpPort2));
|
|
130144
|
+
const healthy = await r.check("health", () => checkHealth(r, mcpPort2)) ?? false;
|
|
129871
130145
|
if (healthy) {
|
|
129872
130146
|
await r.check("sse", () => checkSseEndpoint(r, mcpPort2));
|
|
129873
130147
|
}
|
|
@@ -129882,7 +130156,7 @@ async function runDoctor(opts = {}) {
|
|
|
129882
130156
|
results: r.results
|
|
129883
130157
|
};
|
|
129884
130158
|
}
|
|
129885
|
-
var Recorder;
|
|
130159
|
+
var Recorder, VITE_DEV_PORT, HTTP_MAX_BYTES;
|
|
129886
130160
|
var init_doctor = __esm({
|
|
129887
130161
|
"src/cli/doctor.ts"() {
|
|
129888
130162
|
"use strict";
|
|
@@ -129893,11 +130167,29 @@ var init_doctor = __esm({
|
|
|
129893
130167
|
warnings = 0;
|
|
129894
130168
|
results = [];
|
|
129895
130169
|
currentCheck = "";
|
|
130170
|
+
/**
|
|
130171
|
+
* Run one check under `name`. A throwing check is contained here and
|
|
130172
|
+
* recorded as a `fail` rather than taking the whole report down.
|
|
130173
|
+
*
|
|
130174
|
+
* Returns `T | undefined` — NOT `T`. The `undefined` is load-bearing and
|
|
130175
|
+
* must not be cast away: a crashed check produced no value, and the two
|
|
130176
|
+
* callers that consume one (`ports` destructures `{ws, mcp}`; `health`
|
|
130177
|
+
* gates the `sse` check) have to say what they want instead. Returning
|
|
130178
|
+
* `undefined as T` would satisfy the compiler and then throw a TypeError
|
|
130179
|
+
* on the destructure — converting a clear crash into a confusing one on
|
|
130180
|
+
* the exact path this containment exists to protect.
|
|
130181
|
+
*/
|
|
129896
130182
|
async check(name2, fn) {
|
|
129897
130183
|
const prev = this.currentCheck;
|
|
129898
130184
|
this.currentCheck = name2;
|
|
129899
130185
|
try {
|
|
129900
130186
|
return await fn();
|
|
130187
|
+
} catch (err) {
|
|
130188
|
+
this.fail(
|
|
130189
|
+
`${name2} check crashed (${errorClass(err)}) \u2014 the rest of the report is still valid`,
|
|
130190
|
+
"Please report this at https://github.com/bloknayrb/tandem/issues"
|
|
130191
|
+
);
|
|
130192
|
+
return void 0;
|
|
129901
130193
|
} finally {
|
|
129902
130194
|
this.currentCheck = prev;
|
|
129903
130195
|
}
|
|
@@ -129920,6 +130212,8 @@ var init_doctor = __esm({
|
|
|
129920
130212
|
this.record("fail", msg, fix, fields);
|
|
129921
130213
|
}
|
|
129922
130214
|
};
|
|
130215
|
+
VITE_DEV_PORT = 5173;
|
|
130216
|
+
HTTP_MAX_BYTES = 256 * 1024;
|
|
129923
130217
|
}
|
|
129924
130218
|
});
|
|
129925
130219
|
|
|
@@ -129974,7 +130268,13 @@ var init_diagnostics = __esm({
|
|
|
129974
130268
|
"use strict";
|
|
129975
130269
|
init_doctor();
|
|
129976
130270
|
init_middleware();
|
|
129977
|
-
DEV_REPO_CHECKS = /* @__PURE__ */ new Set([
|
|
130271
|
+
DEV_REPO_CHECKS = /* @__PURE__ */ new Set([
|
|
130272
|
+
"node-modules",
|
|
130273
|
+
"mcp-json",
|
|
130274
|
+
"npm-staleness",
|
|
130275
|
+
"orphaned-vite",
|
|
130276
|
+
"dev-repo"
|
|
130277
|
+
]);
|
|
129978
130278
|
}
|
|
129979
130279
|
});
|
|
129980
130280
|
|
|
@@ -174831,7 +175131,7 @@ function registerAwarenessTools(server) {
|
|
|
174831
175131
|
server.registerTool(
|
|
174832
175132
|
"tandem_checkInbox",
|
|
174833
175133
|
{
|
|
174834
|
-
description: "Check for user actions you haven't seen yet \u2014 new comments, chat messages, and responses to your annotations.
|
|
175134
|
+
description: "Check for user actions you haven't seen yet \u2014 new comments, chat messages, and responses to your annotations. You cannot tell whether real-time push is reaching you, so poll at a steady cadence: every 2-3 tool calls, after completing any task, between steps, and whenever you pause. Already-seen items are de-duplicated, so frequent calls are cheap and never double-report. Low token cost \u2014 when in doubt, call it.",
|
|
174835
175135
|
inputSchema: {
|
|
174836
175136
|
documentId: external_exports.string().optional().describe("Target document ID (defaults to active document)")
|
|
174837
175137
|
},
|
|
@@ -187922,6 +188222,10 @@ function sseHandler(req, res) {
|
|
|
187922
188222
|
}
|
|
187923
188223
|
res.write(": connected\n\n");
|
|
187924
188224
|
let keepalive;
|
|
188225
|
+
function cleanup() {
|
|
188226
|
+
clearInterval(keepalive);
|
|
188227
|
+
unsubscribe(onEvent);
|
|
188228
|
+
}
|
|
187925
188229
|
const onEvent = (event) => {
|
|
187926
188230
|
try {
|
|
187927
188231
|
writeEvent(res, event);
|
|
@@ -187930,17 +188234,23 @@ function sseHandler(req, res) {
|
|
|
187930
188234
|
"[SSE] Write failed, cleaning up subscriber:",
|
|
187931
188235
|
err instanceof Error ? err.message : err
|
|
187932
188236
|
);
|
|
187933
|
-
|
|
187934
|
-
unsubscribe(onEvent);
|
|
188237
|
+
cleanup();
|
|
187935
188238
|
}
|
|
187936
188239
|
};
|
|
187937
188240
|
subscribe2(onEvent);
|
|
187938
188241
|
keepalive = setInterval(() => {
|
|
187939
|
-
|
|
188242
|
+
try {
|
|
188243
|
+
if (!res.writableEnded) res.write(": keepalive\n\n");
|
|
188244
|
+
} catch (err) {
|
|
188245
|
+
console.error(
|
|
188246
|
+
"[SSE] Keepalive write failed, cleaning up subscriber:",
|
|
188247
|
+
err instanceof Error ? err.message : err
|
|
188248
|
+
);
|
|
188249
|
+
cleanup();
|
|
188250
|
+
}
|
|
187940
188251
|
}, CHANNEL_SSE_KEEPALIVE_MS);
|
|
187941
188252
|
req.on("close", () => {
|
|
187942
|
-
|
|
187943
|
-
unsubscribe(onEvent);
|
|
188253
|
+
cleanup();
|
|
187944
188254
|
console.error("[SSE] Client disconnected from /api/events");
|
|
187945
188255
|
});
|
|
187946
188256
|
console.error("[SSE] Client connected to /api/events");
|
|
@@ -188114,7 +188424,7 @@ init_document2();
|
|
|
188114
188424
|
init_document_service();
|
|
188115
188425
|
init_docx_apply2();
|
|
188116
188426
|
var esmRequire2 = createRequire2(import.meta.url);
|
|
188117
|
-
var APP_VERSION = true ? "0.
|
|
188427
|
+
var APP_VERSION = true ? "0.18.0" : _readVersionFromDisk();
|
|
188118
188428
|
var MCP_SDK_VERSION = true ? "1.27.1" : "0.0.0-unknown";
|
|
188119
188429
|
var __dirname3 = dirname3(fileURLToPath4(import.meta.url));
|
|
188120
188430
|
var CLIENT_DIST = join7(__dirname3, "../client");
|