@askalf/claude-sync 0.0.1
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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +338 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/format.d.ts +51 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +72 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/project.d.ts +76 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +138 -0
- package/dist/project.js.map +1 -0
- package/dist/session.d.ts +50 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +101 -0
- package/dist/session.js.map +1 -0
- package/dist/transport.d.ts +39 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +90 -0
- package/dist/transport.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem transport — the only transport in v0.0.1.
|
|
3
|
+
*
|
|
4
|
+
* Layout under `<syncDir>`:
|
|
5
|
+
*
|
|
6
|
+
* <syncDir>/
|
|
7
|
+
* <encoded-project-key>/
|
|
8
|
+
* <sessionId>-<machineName>.ccsync
|
|
9
|
+
*
|
|
10
|
+
* Project key is encoded the same way CC encodes cwds — `[/\\:.\s]+`
|
|
11
|
+
* collapsed to `-` — so `git:https://github.com/owner/repo.git` becomes
|
|
12
|
+
* `git-https---github-com-owner-repo-git`. Reversible enough for human
|
|
13
|
+
* inspection.
|
|
14
|
+
*
|
|
15
|
+
* Multiple machines can push into the same project subdir without
|
|
16
|
+
* collision: each writes a file named with its own machineName, and
|
|
17
|
+
* `pull` picks the newest non-self file per session id.
|
|
18
|
+
*/
|
|
19
|
+
import type { CcsyncFile } from './format.js';
|
|
20
|
+
export declare function projectSubdir(syncDir: string, projectKey: string): string;
|
|
21
|
+
/** Write a .ccsync file into the project's transport subdir. The
|
|
22
|
+
* filename includes the machine name so the receiver can tell who
|
|
23
|
+
* pushed it (and so two machines pushing the same session don't
|
|
24
|
+
* collide). */
|
|
25
|
+
export declare function pushToTransport(syncDir: string, file: CcsyncFile): string;
|
|
26
|
+
export interface TransportEntry {
|
|
27
|
+
path: string;
|
|
28
|
+
file: CcsyncFile;
|
|
29
|
+
mtime: number;
|
|
30
|
+
}
|
|
31
|
+
/** List all .ccsync files for a project key in the transport. Sorted
|
|
32
|
+
* by mtime descending. Files from the local machine (matching
|
|
33
|
+
* `selfMachineName`) are omitted — pull is for receiving OTHER
|
|
34
|
+
* machines' work, not echoing your own. */
|
|
35
|
+
export declare function listTransport(syncDir: string, projectKey: string, selfMachineName: string): TransportEntry[];
|
|
36
|
+
/** List all project keys present in the transport. Used by the CLI's
|
|
37
|
+
* `pull` (no args) form to fan out across every known project. */
|
|
38
|
+
export declare function listProjectKeys(syncDir: string): string[];
|
|
39
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;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;mEACmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAWzD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem transport — the only transport in v0.0.1.
|
|
3
|
+
*
|
|
4
|
+
* Layout under `<syncDir>`:
|
|
5
|
+
*
|
|
6
|
+
* <syncDir>/
|
|
7
|
+
* <encoded-project-key>/
|
|
8
|
+
* <sessionId>-<machineName>.ccsync
|
|
9
|
+
*
|
|
10
|
+
* Project key is encoded the same way CC encodes cwds — `[/\\:.\s]+`
|
|
11
|
+
* collapsed to `-` — so `git:https://github.com/owner/repo.git` becomes
|
|
12
|
+
* `git-https---github-com-owner-repo-git`. Reversible enough for human
|
|
13
|
+
* inspection.
|
|
14
|
+
*
|
|
15
|
+
* Multiple machines can push into the same project subdir without
|
|
16
|
+
* collision: each writes a file named with its own machineName, and
|
|
17
|
+
* `pull` picks the newest non-self file per session id.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync, readFileSync, renameSync, } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { encodeProjectDir } from './project.js';
|
|
22
|
+
import { parseCcsync, serializeCcsync } from './format.js';
|
|
23
|
+
export function projectSubdir(syncDir, projectKey) {
|
|
24
|
+
return join(syncDir, encodeProjectDir(projectKey));
|
|
25
|
+
}
|
|
26
|
+
/** Write a .ccsync file into the project's transport subdir. The
|
|
27
|
+
* filename includes the machine name so the receiver can tell who
|
|
28
|
+
* pushed it (and so two machines pushing the same session don't
|
|
29
|
+
* collide). */
|
|
30
|
+
export function pushToTransport(syncDir, file) {
|
|
31
|
+
const dir = projectSubdir(syncDir, file.projectKey);
|
|
32
|
+
if (!existsSync(dir))
|
|
33
|
+
mkdirSync(dir, { recursive: true });
|
|
34
|
+
const filename = `${file.sessionId}-${file.machineName}.ccsync`;
|
|
35
|
+
const path = join(dir, filename);
|
|
36
|
+
// Atomic write: tmp + rename. If the sync provider mid-snapshots the
|
|
37
|
+
// dir (Dropbox does this), at least it sees a complete file.
|
|
38
|
+
const tmp = `${path}.tmp`;
|
|
39
|
+
writeFileSync(tmp, serializeCcsync(file), { mode: 0o600 });
|
|
40
|
+
renameSync(tmp, path);
|
|
41
|
+
return path;
|
|
42
|
+
}
|
|
43
|
+
/** List all .ccsync files for a project key in the transport. Sorted
|
|
44
|
+
* by mtime descending. Files from the local machine (matching
|
|
45
|
+
* `selfMachineName`) are omitted — pull is for receiving OTHER
|
|
46
|
+
* machines' work, not echoing your own. */
|
|
47
|
+
export function listTransport(syncDir, projectKey, selfMachineName) {
|
|
48
|
+
const dir = projectSubdir(syncDir, projectKey);
|
|
49
|
+
if (!existsSync(dir))
|
|
50
|
+
return [];
|
|
51
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
52
|
+
const result = [];
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (!entry.isFile() || !entry.name.endsWith('.ccsync'))
|
|
55
|
+
continue;
|
|
56
|
+
const path = join(dir, entry.name);
|
|
57
|
+
let file;
|
|
58
|
+
try {
|
|
59
|
+
file = parseCcsync(readFileSync(path, 'utf-8'));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Skip malformed files quietly. The transport directory is shared
|
|
63
|
+
// across machines and might contain in-progress writes from
|
|
64
|
+
// other tools or stale tmp files.
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (file.machineName === selfMachineName)
|
|
68
|
+
continue;
|
|
69
|
+
const mtime = statSync(path).mtimeMs;
|
|
70
|
+
result.push({ path, file, mtime });
|
|
71
|
+
}
|
|
72
|
+
result.sort((a, b) => b.mtime - a.mtime);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/** List all project keys present in the transport. Used by the CLI's
|
|
76
|
+
* `pull` (no args) form to fan out across every known project. */
|
|
77
|
+
export function listProjectKeys(syncDir) {
|
|
78
|
+
if (!existsSync(syncDir))
|
|
79
|
+
return [];
|
|
80
|
+
const entries = readdirSync(syncDir, { withFileTypes: true });
|
|
81
|
+
return entries
|
|
82
|
+
.filter((e) => e.isDirectory())
|
|
83
|
+
.map((e) => e.name)
|
|
84
|
+
// We could try to reverse-decode the encoded key, but the lossy
|
|
85
|
+
// encoding means we can't reliably get the original `git:...`
|
|
86
|
+
// form back. Return the encoded form; callers that need to match
|
|
87
|
+
// by raw key should encode their key first too.
|
|
88
|
+
.sort();
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@askalf/claude-sync",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Sync Claude Code sessions across machines. Pack a local CC session into a portable .ccsync file, ship it via Dropbox / iCloud / Syncthing / a USB stick, unpack on the other side. Path-hash mismatches solved via git-remote-url as canonical project key.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claude-sync": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"test": "node --test test/project.test.mjs test/format.test.mjs test/session.test.mjs test/cli.test.mjs",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"audit": "npm audit --production --audit-level=high",
|
|
27
|
+
"prepublishOnly": "npm run build",
|
|
28
|
+
"start": "node dist/cli.js",
|
|
29
|
+
"dev": "tsx src/cli.ts"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"claude-code",
|
|
33
|
+
"claude",
|
|
34
|
+
"session",
|
|
35
|
+
"sync",
|
|
36
|
+
"developer-tools",
|
|
37
|
+
"cli"
|
|
38
|
+
],
|
|
39
|
+
"author": "askalf (https://github.com/askalf)",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/askalf/claude-sync.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/askalf/claude-sync",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/askalf/claude-sync/issues"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^25.6.0",
|
|
54
|
+
"tsx": "^4.19.0",
|
|
55
|
+
"typescript": "^6.0.3"
|
|
56
|
+
}
|
|
57
|
+
}
|