@askalf/claude-sync 0.0.2 → 0.1.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/README.md +76 -33
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +222 -25
- package/dist/cli.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/format.d.ts +2 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +16 -1
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/project.d.ts +39 -8
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +98 -15
- package/dist/project.js.map +1 -1
- package/dist/session.d.ts +21 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +59 -14
- package/dist/session.js.map +1 -1
- package/dist/transport.d.ts +12 -4
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +33 -4
- package/dist/transport.js.map +1 -1
- package/package.json +3 -3
package/dist/session.js
CHANGED
|
@@ -7,39 +7,70 @@
|
|
|
7
7
|
* format is opaque to us by design: CC's schema can evolve without
|
|
8
8
|
* needing claude-sync changes.
|
|
9
9
|
*/
|
|
10
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, statSync, openSync, readSync, closeSync, } from 'node:fs';
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, statSync, openSync, readSync, closeSync, renameSync, } from 'node:fs';
|
|
11
11
|
import { join, basename } from 'node:path';
|
|
12
12
|
import { claudeProjectsRoot, encodeProjectDir } from './project.js';
|
|
13
|
-
/**
|
|
14
|
-
* doesn't exist
|
|
15
|
-
*
|
|
16
|
-
|
|
13
|
+
/** Stat-only listing for a cwd — id, path, size, mtime, no line count.
|
|
14
|
+
* Returns [] if the project dir doesn't exist. Sorted by mtime
|
|
15
|
+
* descending. This is the cheap path: watch mode polls it every tick to
|
|
16
|
+
* spot changed sessions WITHOUT reading every file's contents to count
|
|
17
|
+
* lines (which `listSessions` does). */
|
|
18
|
+
export function listSessionStats(cwd) {
|
|
17
19
|
const projDir = join(claudeProjectsRoot(), encodeProjectDir(cwd));
|
|
18
20
|
if (!existsSync(projDir))
|
|
19
21
|
return [];
|
|
20
22
|
const entries = readdirSync(projDir, { withFileTypes: true });
|
|
21
|
-
const
|
|
23
|
+
const stats = [];
|
|
22
24
|
for (const entry of entries) {
|
|
23
25
|
if (!entry.isFile() || !entry.name.endsWith('.jsonl'))
|
|
24
26
|
continue;
|
|
25
27
|
const path = join(projDir, entry.name);
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
+
const st = statSync(path);
|
|
29
|
+
stats.push({
|
|
28
30
|
id: entry.name.replace(/\.jsonl$/, ''),
|
|
29
31
|
path,
|
|
30
|
-
size:
|
|
31
|
-
modifiedAt:
|
|
32
|
-
lineCount: countLines(path),
|
|
32
|
+
size: st.size,
|
|
33
|
+
modifiedAt: st.mtimeMs,
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
36
|
+
stats.sort((a, b) => b.modifiedAt - a.modifiedAt);
|
|
37
|
+
return stats;
|
|
38
|
+
}
|
|
39
|
+
/** List sessions for a given local cwd, including line counts. Returns []
|
|
40
|
+
* if the project dir doesn't exist (no sessions yet). Sorted by mtime
|
|
41
|
+
* descending so the caller can grab `[0]` for "the most recent session". */
|
|
42
|
+
export function listSessions(cwd) {
|
|
43
|
+
return listSessionStats(cwd).map((s) => ({ ...s, lineCount: countLines(s.path) }));
|
|
37
44
|
}
|
|
38
45
|
/** Read a session's JSONL content. Returned as the raw bytes (no parse)
|
|
39
46
|
* so we don't have to know about CC's evolving message schema. */
|
|
40
47
|
export function readSession(path) {
|
|
41
48
|
return readFileSync(path, 'utf-8');
|
|
42
49
|
}
|
|
50
|
+
/** A session id is used verbatim as a filename segment (`<id>.jsonl`).
|
|
51
|
+
* Since the id can originate from a synced, attacker-influenceable
|
|
52
|
+
* `.ccsync` file (see `installCcsync` in cli.ts), it MUST be a single,
|
|
53
|
+
* benign path segment — never a path that could escape the project dir
|
|
54
|
+
* via `..`, an absolute path, or an embedded separator. CC session ids
|
|
55
|
+
* are UUIDs in practice; `assignFreshId` may append `-copy` suffixes.
|
|
56
|
+
* Both fit comfortably inside this allowlist. */
|
|
57
|
+
const SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
|
|
58
|
+
/** Throw unless `sessionId` is a safe single path segment. Rejects
|
|
59
|
+
* empty ids, anything containing `/`, `\`, or `..`, and anything with
|
|
60
|
+
* characters outside the conservative allowlist. */
|
|
61
|
+
export function assertSafeSessionId(sessionId) {
|
|
62
|
+
if (!sessionId ||
|
|
63
|
+
sessionId === '.' ||
|
|
64
|
+
sessionId === '..' ||
|
|
65
|
+
sessionId.includes('/') ||
|
|
66
|
+
sessionId.includes('\\') ||
|
|
67
|
+
sessionId.includes('..') ||
|
|
68
|
+
!SAFE_SESSION_ID.test(sessionId)) {
|
|
69
|
+
throw new Error(`Refusing to write session: unsafe session id ${JSON.stringify(sessionId)}. ` +
|
|
70
|
+
`Session ids must be a single path segment matching ${SAFE_SESSION_ID} ` +
|
|
71
|
+
`(no \`/\`, \`\\\`, or \`..\`).`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
43
74
|
/** Write a session's JSONL into a target cwd's project dir. Creates the
|
|
44
75
|
* project dir if it doesn't exist; overwrites the session file if a
|
|
45
76
|
* file with the same id already exists.
|
|
@@ -49,6 +80,9 @@ export function readSession(path) {
|
|
|
49
80
|
* importing — typically by renaming the incoming session to a fresh id
|
|
50
81
|
* via `assignFreshId`. */
|
|
51
82
|
export function writeSession(targetCwd, sessionId, jsonl, opts = {}) {
|
|
83
|
+
// Validate BEFORE building any path: the id is interpolated into a
|
|
84
|
+
// filename and a malicious `../../x` would escape projDir otherwise.
|
|
85
|
+
assertSafeSessionId(sessionId);
|
|
52
86
|
const projDir = join(claudeProjectsRoot(), encodeProjectDir(targetCwd));
|
|
53
87
|
if (!existsSync(projDir))
|
|
54
88
|
mkdirSync(projDir, { recursive: true });
|
|
@@ -57,7 +91,12 @@ export function writeSession(targetCwd, sessionId, jsonl, opts = {}) {
|
|
|
57
91
|
throw new Error(`Session ${sessionId} already exists at ${targetPath}. ` +
|
|
58
92
|
`Pass { overwrite: true } to replace it, or assign a fresh id first.`);
|
|
59
93
|
}
|
|
60
|
-
|
|
94
|
+
// Atomic write: tmp + rename, with 0o600 perms (session JSONL holds
|
|
95
|
+
// conversation data). Mirrors transport.ts's pushToTransport so a
|
|
96
|
+
// mid-snapshot sync provider never sees a half-written file.
|
|
97
|
+
const tmp = `${targetPath}.tmp`;
|
|
98
|
+
writeFileSync(tmp, jsonl, { mode: 0o600 });
|
|
99
|
+
renameSync(tmp, targetPath);
|
|
61
100
|
return targetPath;
|
|
62
101
|
}
|
|
63
102
|
/** Generate a session id that doesn't collide with any existing session
|
|
@@ -76,6 +115,12 @@ export function assignFreshId(targetCwd, baseId) {
|
|
|
76
115
|
n++;
|
|
77
116
|
return `${baseId}-copy${n === 1 ? '' : `-${n}`}`;
|
|
78
117
|
}
|
|
118
|
+
/** Count newline-terminated records in a session file. Exported for watch
|
|
119
|
+
* mode, which counts lines only for the handful of sessions that changed
|
|
120
|
+
* since the last tick (the bulk listing stays on `listSessionStats`). */
|
|
121
|
+
export function countSessionLines(path) {
|
|
122
|
+
return countLines(path);
|
|
123
|
+
}
|
|
79
124
|
function countLines(path) {
|
|
80
125
|
// Fast newline count without buffering the whole file as a string —
|
|
81
126
|
// sessions can be large.
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EACzE,QAAQ,EAAE,QAAQ,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/transport.d.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* <encoded-project-key>/
|
|
8
8
|
* <sessionId>-<machineName>.ccsync
|
|
9
9
|
*
|
|
10
|
-
* Project key is encoded the same way CC encodes cwds —
|
|
11
|
-
*
|
|
12
|
-
* `git
|
|
13
|
-
*
|
|
10
|
+
* Project key is encoded the same way CC encodes cwds — each
|
|
11
|
+
* non-alphanumeric character becomes a single `-` (see
|
|
12
|
+
* `encodeProjectDir`) — so `git:https://github.com/owner/repo.git`
|
|
13
|
+
* becomes `git-https---github-com-owner-repo-git`. Reversible enough
|
|
14
|
+
* for human inspection.
|
|
14
15
|
*
|
|
15
16
|
* Multiple machines can push into the same project subdir without
|
|
16
17
|
* collision: each writes a file named with its own machineName, and
|
|
@@ -33,6 +34,13 @@ export interface TransportEntry {
|
|
|
33
34
|
* `selfMachineName`) are omitted — pull is for receiving OTHER
|
|
34
35
|
* machines' work, not echoing your own. */
|
|
35
36
|
export declare function listTransport(syncDir: string, projectKey: string, selfMachineName: string): TransportEntry[];
|
|
37
|
+
/** Tally `machineName` → file count across every `.ccsync` in the
|
|
38
|
+
* transport (all projects). Used by `doctor` to catch the
|
|
39
|
+
* duplicate-machineName footgun: `pull` self-filters files whose
|
|
40
|
+
* machineName matches this machine's, so if two machines share a name —
|
|
41
|
+
* e.g. both defaulted to the same hostname — pull silently imports
|
|
42
|
+
* nothing. Malformed files are skipped quietly. */
|
|
43
|
+
export declare function listTransportMachines(syncDir: string): Map<string, number>;
|
|
36
44
|
/** List all project keys present in the transport. Used by the CLI's
|
|
37
45
|
* `pull` (no args) form to fan out across every known project. */
|
|
38
46
|
export declare function listProjectKeys(syncDir: string): string[];
|
package/dist/transport.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;;gBAGgB;AAChB,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAWzE;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;4CAG4C;AAC5C,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB,cAAc,EAAE,CA0BlB;AAED;;;;;oDAKoD;AACpD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB1E;AAED;mEACmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAWzD"}
|
package/dist/transport.js
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* <encoded-project-key>/
|
|
8
8
|
* <sessionId>-<machineName>.ccsync
|
|
9
9
|
*
|
|
10
|
-
* Project key is encoded the same way CC encodes cwds —
|
|
11
|
-
*
|
|
12
|
-
* `git
|
|
13
|
-
*
|
|
10
|
+
* Project key is encoded the same way CC encodes cwds — each
|
|
11
|
+
* non-alphanumeric character becomes a single `-` (see
|
|
12
|
+
* `encodeProjectDir`) — so `git:https://github.com/owner/repo.git`
|
|
13
|
+
* becomes `git-https---github-com-owner-repo-git`. Reversible enough
|
|
14
|
+
* for human inspection.
|
|
14
15
|
*
|
|
15
16
|
* Multiple machines can push into the same project subdir without
|
|
16
17
|
* collision: each writes a file named with its own machineName, and
|
|
@@ -72,6 +73,34 @@ export function listTransport(syncDir, projectKey, selfMachineName) {
|
|
|
72
73
|
result.sort((a, b) => b.mtime - a.mtime);
|
|
73
74
|
return result;
|
|
74
75
|
}
|
|
76
|
+
/** Tally `machineName` → file count across every `.ccsync` in the
|
|
77
|
+
* transport (all projects). Used by `doctor` to catch the
|
|
78
|
+
* duplicate-machineName footgun: `pull` self-filters files whose
|
|
79
|
+
* machineName matches this machine's, so if two machines share a name —
|
|
80
|
+
* e.g. both defaulted to the same hostname — pull silently imports
|
|
81
|
+
* nothing. Malformed files are skipped quietly. */
|
|
82
|
+
export function listTransportMachines(syncDir) {
|
|
83
|
+
const counts = new Map();
|
|
84
|
+
if (!existsSync(syncDir))
|
|
85
|
+
return counts;
|
|
86
|
+
for (const sub of readdirSync(syncDir, { withFileTypes: true })) {
|
|
87
|
+
if (!sub.isDirectory())
|
|
88
|
+
continue;
|
|
89
|
+
const dir = join(syncDir, sub.name);
|
|
90
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
91
|
+
if (!entry.isFile() || !entry.name.endsWith('.ccsync'))
|
|
92
|
+
continue;
|
|
93
|
+
try {
|
|
94
|
+
const file = parseCcsync(readFileSync(join(dir, entry.name), 'utf-8'));
|
|
95
|
+
counts.set(file.machineName, (counts.get(file.machineName) ?? 0) + 1);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Shared dir may hold in-progress writes / stale tmp files.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return counts;
|
|
103
|
+
}
|
|
75
104
|
/** List all project keys present in the transport. Used by the CLI's
|
|
76
105
|
* `pull` (no args) form to fan out across every known project. */
|
|
77
106
|
export function listProjectKeys(syncDir) {
|
package/dist/transport.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/claude-sync",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "own your sessions — move Claude Code sessions across machines. Part of Own Your Stack.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claude-sync": "./dist/cli.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsc",
|
|
24
|
-
"test": "node --test test/project.test.mjs test/format.test.mjs test/session.test.mjs test/cli.test.mjs",
|
|
24
|
+
"test": "node --test test/project.test.mjs test/format.test.mjs test/session.test.mjs test/transport.test.mjs test/cli.test.mjs test/watch.test.mjs test/version.test.mjs",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
26
|
"audit": "npm audit --production --audit-level=high",
|
|
27
27
|
"prepublishOnly": "npm run build",
|