tandem-editor 0.2.7 → 0.2.9
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 +82 -0
- package/dist/cli/index.js +1 -1
- package/dist/server/index.js +51 -18
- package/dist/server/index.js.map +1 -1
- package/package.json +122 -121
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Tandem will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.9] - 2026-04-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Changelog tab no longer disappears after upgrade — version check and sample/welcome.md now open before servers start, preventing CRDT merge races with stale browser tabs
|
|
13
|
+
- Tutorial annotation injection errors now get their own log message instead of being misattributed as file-open failures
|
|
14
|
+
|
|
15
|
+
## [0.2.8] - 2026-04-05
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- CHANGELOG.md opens as the active tab on first startup after an npm update
|
|
20
|
+
- `checkVersionChange` helper tracks version transitions via `last-seen-version` file
|
|
21
|
+
- CHANGELOG.md now ships in the npm package
|
|
22
|
+
|
|
23
|
+
## [0.2.7] - 2026-04-05
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Force-reload (`tandem_open` with `force: true`) now clears Y.Doc in-place instead of destroying the Hocuspocus room — sidebar, observers, and connections survive
|
|
28
|
+
- TOCTOU fix: session deletion moved after successful reload transaction
|
|
29
|
+
- Observer ownership table corrected in architecture docs
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- 4 new tests for force-reload (annotation clearing, awareness clearing, .txt reload, metadata)
|
|
34
|
+
|
|
35
|
+
## [0.2.6] - 2026-04-05
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Demo script rewritten to be self-referential for recording
|
|
40
|
+
- Observer ownership documentation added to architecture.md
|
|
41
|
+
|
|
42
|
+
## [0.2.5] - 2026-04-05
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- `tandem setup` Claude Code MCP config path updated
|
|
47
|
+
|
|
48
|
+
## [0.2.4] - 2026-04-05
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Security audit findings (DNS rebinding, CORS, input validation)
|
|
53
|
+
|
|
54
|
+
## [0.2.3] - 2026-04-05
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- `tandem setup` now writes Claude Code MCP config to `~/.claude.json` instead of `~/.claude/mcp_settings.json`, which Claude Code no longer reads
|
|
59
|
+
|
|
60
|
+
## [0.2.2] - 2025-04-05
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- Silent failure review findings
|
|
65
|
+
|
|
66
|
+
## [0.2.1] - 2025-04-05
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Full security audit — 25 findings across 7 categories (#172)
|
|
71
|
+
|
|
72
|
+
## [0.2.0] - 2025-04-04
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- Initial public release on npm as `tandem-editor`
|
|
77
|
+
- 30 MCP tools for collaborative document editing
|
|
78
|
+
- Multi-document tabs with CRDT-anchored annotations
|
|
79
|
+
- Chat sidebar with real-time channel push
|
|
80
|
+
- Support for .md, .docx, .txt, .html files
|
|
81
|
+
- `tandem` CLI with `setup` and `start` commands
|
|
82
|
+
- Claude Code skill auto-installation
|
package/dist/cli/index.js
CHANGED
|
@@ -337,7 +337,7 @@ var init_start = __esm({
|
|
|
337
337
|
|
|
338
338
|
// src/cli/index.ts
|
|
339
339
|
import updateNotifier from "update-notifier";
|
|
340
|
-
var version = true ? "0.2.
|
|
340
|
+
var version = true ? "0.2.9" : "0.0.0-dev";
|
|
341
341
|
updateNotifier({ pkg: { name: "tandem-editor", version } }).notify();
|
|
342
342
|
var args = process.argv.slice(2);
|
|
343
343
|
if (args.includes("--help") || args.includes("-h")) {
|
package/dist/server/index.js
CHANGED
|
@@ -214,12 +214,13 @@ function freePortUnix(port) {
|
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
var paths, SESSION_DIR;
|
|
217
|
+
var paths, SESSION_DIR, LAST_SEEN_VERSION_FILE;
|
|
218
218
|
var init_platform = __esm({
|
|
219
219
|
"src/server/platform.ts"() {
|
|
220
220
|
"use strict";
|
|
221
221
|
paths = envPaths("tandem", { suffix: "" });
|
|
222
222
|
SESSION_DIR = path.join(paths.data, "sessions");
|
|
223
|
+
LAST_SEEN_VERSION_FILE = path.join(paths.data, "last-seen-version");
|
|
223
224
|
}
|
|
224
225
|
});
|
|
225
226
|
|
|
@@ -3577,7 +3578,7 @@ var init_launcher = __esm({
|
|
|
3577
3578
|
});
|
|
3578
3579
|
|
|
3579
3580
|
// src/server/index.ts
|
|
3580
|
-
import
|
|
3581
|
+
import path10 from "path";
|
|
3581
3582
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3582
3583
|
|
|
3583
3584
|
// src/server/mcp/server.ts
|
|
@@ -5678,6 +5679,26 @@ init_constants();
|
|
|
5678
5679
|
init_manager();
|
|
5679
5680
|
init_platform();
|
|
5680
5681
|
|
|
5682
|
+
// src/server/version-check.ts
|
|
5683
|
+
import fs6 from "fs/promises";
|
|
5684
|
+
import path9 from "path";
|
|
5685
|
+
async function checkVersionChange(currentVersion, versionFilePath) {
|
|
5686
|
+
let storedVersion = null;
|
|
5687
|
+
try {
|
|
5688
|
+
storedVersion = (await fs6.readFile(versionFilePath, "utf-8")).trim();
|
|
5689
|
+
} catch (err) {
|
|
5690
|
+
if (err.code !== "ENOENT") {
|
|
5691
|
+
console.error("[Tandem] Failed to read last-seen-version:", err);
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
const result = storedVersion === null ? "first-install" : storedVersion === currentVersion ? "current" : "upgraded";
|
|
5695
|
+
if (result !== "current") {
|
|
5696
|
+
await fs6.mkdir(path9.dirname(versionFilePath), { recursive: true });
|
|
5697
|
+
await fs6.writeFile(versionFilePath, currentVersion, "utf-8");
|
|
5698
|
+
}
|
|
5699
|
+
return result;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5681
5702
|
// src/server/error-filter.ts
|
|
5682
5703
|
function isKnownHocuspocusError(err) {
|
|
5683
5704
|
if (!(err instanceof Error)) return false;
|
|
@@ -5887,6 +5908,34 @@ async function main() {
|
|
|
5887
5908
|
} catch (err) {
|
|
5888
5909
|
console.error(`[Tandem] ${err instanceof Error ? err.message : err} \u2014 proceeding anyway`);
|
|
5889
5910
|
}
|
|
5911
|
+
const projectRoot = path10.resolve(path10.dirname(fileURLToPath2(import.meta.url)), "../..");
|
|
5912
|
+
try {
|
|
5913
|
+
const versionStatus = await checkVersionChange(APP_VERSION, LAST_SEEN_VERSION_FILE);
|
|
5914
|
+
if (versionStatus === "upgraded") {
|
|
5915
|
+
await openFileByPath(path10.join(projectRoot, "CHANGELOG.md"));
|
|
5916
|
+
console.error(`[Tandem] Opened CHANGELOG.md (upgraded to v${APP_VERSION})`);
|
|
5917
|
+
}
|
|
5918
|
+
} catch (err) {
|
|
5919
|
+
console.error("[Tandem] Version check / changelog open failed (non-fatal):", err);
|
|
5920
|
+
}
|
|
5921
|
+
if (docCount() === 0 && !process.env.TANDEM_NO_SAMPLE) {
|
|
5922
|
+
const samplePath = path10.join(projectRoot, "sample/welcome.md");
|
|
5923
|
+
try {
|
|
5924
|
+
await openFileByPath(samplePath);
|
|
5925
|
+
try {
|
|
5926
|
+
const doc = getOrCreateDocument(docIdFromPath(samplePath));
|
|
5927
|
+
injectTutorialAnnotations(doc);
|
|
5928
|
+
} catch (err) {
|
|
5929
|
+
console.error("[Tandem] Failed to inject tutorial annotations:", err);
|
|
5930
|
+
}
|
|
5931
|
+
} catch (err) {
|
|
5932
|
+
if (err.code === "ENOENT") {
|
|
5933
|
+
console.error("[Tandem] Sample file not found (skipping):", samplePath);
|
|
5934
|
+
} else {
|
|
5935
|
+
console.error("[Tandem] Failed to auto-open sample document:", err);
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5890
5939
|
const [srv] = await Promise.all([
|
|
5891
5940
|
startMcpServerHttp(mcpPort),
|
|
5892
5941
|
startHocuspocus(wsPort).then(() => {
|
|
@@ -5894,22 +5943,6 @@ async function main() {
|
|
|
5894
5943
|
})
|
|
5895
5944
|
]);
|
|
5896
5945
|
httpServer = srv;
|
|
5897
|
-
if (getOpenDocs().size === 0 && !process.env.TANDEM_NO_SAMPLE) {
|
|
5898
|
-
const samplePath = path9.resolve(
|
|
5899
|
-
path9.dirname(fileURLToPath2(import.meta.url)),
|
|
5900
|
-
"../../sample/welcome.md"
|
|
5901
|
-
);
|
|
5902
|
-
openFileByPath(samplePath).then(() => {
|
|
5903
|
-
const doc = getOrCreateDocument(docIdFromPath(samplePath));
|
|
5904
|
-
injectTutorialAnnotations(doc);
|
|
5905
|
-
}).catch((err) => {
|
|
5906
|
-
if (err.code === "ENOENT") {
|
|
5907
|
-
console.error("[Tandem] Sample file not found (skipping):", samplePath);
|
|
5908
|
-
} else {
|
|
5909
|
-
console.error("[Tandem] Failed to auto-open sample document:", err);
|
|
5910
|
-
}
|
|
5911
|
-
});
|
|
5912
|
-
}
|
|
5913
5946
|
console.error("");
|
|
5914
5947
|
console.error(` Tandem v${APP_VERSION}`);
|
|
5915
5948
|
console.error("");
|