@tengx5383/aitool-sync-cli 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/.claude/settings.local.json +8 -0
- package/README.md +203 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +87 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +2 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +24 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +89 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +105 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +22 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/pull.d.ts +6 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +52 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +6 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +60 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/remove.d.ts +2 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +20 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +46 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/config-file.d.ts +22 -0
- package/dist/config/config-file.d.ts.map +1 -0
- package/dist/config/config-file.js +86 -0
- package/dist/config/config-file.js.map +1 -0
- package/dist/config/schema.d.ts +101 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +24 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/backup.d.ts +8 -0
- package/dist/core/backup.d.ts.map +1 -0
- package/dist/core/backup.js +50 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/checksum.d.ts +14 -0
- package/dist/core/checksum.d.ts.map +1 -0
- package/dist/core/checksum.js +23 -0
- package/dist/core/checksum.js.map +1 -0
- package/dist/core/exclusions.d.ts +8 -0
- package/dist/core/exclusions.d.ts.map +1 -0
- package/dist/core/exclusions.js +29 -0
- package/dist/core/exclusions.js.map +1 -0
- package/dist/core/file-collector.d.ts +16 -0
- package/dist/core/file-collector.d.ts.map +1 -0
- package/dist/core/file-collector.js +104 -0
- package/dist/core/file-collector.js.map +1 -0
- package/dist/core/manifest.d.ts +18 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +42 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/path-expander.d.ts +6 -0
- package/dist/core/path-expander.d.ts.map +1 -0
- package/dist/core/path-expander.js +25 -0
- package/dist/core/path-expander.js.map +1 -0
- package/dist/core/sync-engine.d.ts +24 -0
- package/dist/core/sync-engine.d.ts.map +1 -0
- package/dist/core/sync-engine.js +206 -0
- package/dist/core/sync-engine.js.map +1 -0
- package/dist/core/sync-package.d.ts +12 -0
- package/dist/core/sync-package.d.ts.map +1 -0
- package/dist/core/sync-package.js +95 -0
- package/dist/core/sync-package.js.map +1 -0
- package/dist/crypto/encryptor.d.ts +15 -0
- package/dist/crypto/encryptor.d.ts.map +1 -0
- package/dist/crypto/encryptor.js +77 -0
- package/dist/crypto/encryptor.js.map +1 -0
- package/dist/crypto/key-resolver.d.ts +28 -0
- package/dist/crypto/key-resolver.d.ts.map +1 -0
- package/dist/crypto/key-resolver.js +62 -0
- package/dist/crypto/key-resolver.js.map +1 -0
- package/dist/gist/client.d.ts +18 -0
- package/dist/gist/client.d.ts.map +1 -0
- package/dist/gist/client.js +58 -0
- package/dist/gist/client.js.map +1 -0
- package/dist/gist/mock-client.d.ts +19 -0
- package/dist/gist/mock-client.d.ts.map +1 -0
- package/dist/gist/mock-client.js +61 -0
- package/dist/gist/mock-client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/presets/claude-code.d.ts +3 -0
- package/dist/presets/claude-code.d.ts.map +1 -0
- package/dist/presets/claude-code.js +27 -0
- package/dist/presets/claude-code.js.map +1 -0
- package/dist/presets/hermes.d.ts +3 -0
- package/dist/presets/hermes.d.ts.map +1 -0
- package/dist/presets/hermes.js +27 -0
- package/dist/presets/hermes.js.map +1 -0
- package/dist/presets/index.d.ts +15 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +42 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/openclaw.d.ts +3 -0
- package/dist/presets/openclaw.d.ts.map +1 -0
- package/dist/presets/openclaw.js +27 -0
- package/dist/presets/openclaw.js.map +1 -0
- package/dist/types/config.d.ts +45 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/gist.d.ts +25 -0
- package/dist/types/gist.d.ts.map +1 -0
- package/dist/types/gist.js +2 -0
- package/dist/types/gist.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/sync.d.ts +78 -0
- package/dist/types/sync.d.ts.map +1 -0
- package/dist/types/sync.js +2 -0
- package/dist/types/sync.js.map +1 -0
- package/dist/utils/diff.d.ts +7 -0
- package/dist/utils/diff.d.ts.map +1 -0
- package/dist/utils/diff.js +99 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/env.d.ts +13 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +29 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/table.d.ts +5 -0
- package/dist/utils/table.d.ts.map +1 -0
- package/dist/utils/table.js +59 -0
- package/dist/utils/table.js.map +1 -0
- package/package.json +41 -0
- package/tests/integration/encryption-flow.test.ts +142 -0
- package/tests/integration/init-flow.test.ts +69 -0
- package/tests/integration/push-pull-flow.test.ts +183 -0
- package/tests/integration/status-flow.test.ts +149 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface GistFile {
|
|
2
|
+
filename: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface GistDescriptor {
|
|
6
|
+
gistId: string;
|
|
7
|
+
description: string;
|
|
8
|
+
files: GistFile[];
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export interface GistClientInterface {
|
|
12
|
+
/** Create a new Gist */
|
|
13
|
+
createGist(description: string, files: Record<string, {
|
|
14
|
+
content: string;
|
|
15
|
+
}>): Promise<GistDescriptor>;
|
|
16
|
+
/** Get a Gist by ID */
|
|
17
|
+
getGist(gistId: string): Promise<GistDescriptor>;
|
|
18
|
+
/** Update an existing Gist */
|
|
19
|
+
updateGist(gistId: string, files: Record<string, {
|
|
20
|
+
content: string;
|
|
21
|
+
}>): Promise<GistDescriptor>;
|
|
22
|
+
/** Delete a Gist (for cleanup) */
|
|
23
|
+
deleteGist(gistId: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=gist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gist.d.ts","sourceRoot":"","sources":["../../src/types/gist.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrG,uBAAuB;IACvB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACjD,8BAA8B;IAC9B,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAChG,kCAAkC;IAClC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gist.js","sourceRoot":"","sources":["../../src/types/gist.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { AppConfig, SyncPathEntry, ExclusionRule, EncryptionRule, ToolPresetId, ToolPreset, } from './config.js';
|
|
2
|
+
export type { Manifest, ManifestEntry, SyncFile, SyncPackage, DiffEntry, ConflictEntry, SyncDiff, SyncResult, } from './sync.js';
|
|
3
|
+
export type { GistFile, GistDescriptor, GistClientInterface, } from './gist.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,EACb,QAAQ,EACR,UAAU,GACX,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,QAAQ,EACR,cAAc,EACd,mBAAmB,GACpB,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export interface ManifestEntry {
|
|
2
|
+
/** Relative path of the file within the sync root */
|
|
3
|
+
relativePath: string;
|
|
4
|
+
/** SHA-256 checksum of file contents */
|
|
5
|
+
checksum: string;
|
|
6
|
+
/** File size in bytes */
|
|
7
|
+
size: number;
|
|
8
|
+
/** Last modified timestamp (ISO 8601) */
|
|
9
|
+
modifiedAt: string;
|
|
10
|
+
/** Originating SyncPathEntry name */
|
|
11
|
+
sourceName: string;
|
|
12
|
+
/** Whether this file content is encrypted */
|
|
13
|
+
encrypted: boolean;
|
|
14
|
+
/** Encryption rule pattern that matched (null if not encrypted) */
|
|
15
|
+
encryptionPattern: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface Manifest {
|
|
18
|
+
/** Manifest format version */
|
|
19
|
+
version: 1;
|
|
20
|
+
/** Timestamp when manifest was generated */
|
|
21
|
+
generatedAt: string;
|
|
22
|
+
/** All file entries */
|
|
23
|
+
entries: ManifestEntry[];
|
|
24
|
+
}
|
|
25
|
+
export interface SyncFile {
|
|
26
|
+
/** Relative path of the file */
|
|
27
|
+
relativePath: string;
|
|
28
|
+
/** File content as Base64 string */
|
|
29
|
+
content: string;
|
|
30
|
+
/** Whether content is encrypted */
|
|
31
|
+
encrypted: boolean;
|
|
32
|
+
/** Entry metadata */
|
|
33
|
+
entry: ManifestEntry;
|
|
34
|
+
}
|
|
35
|
+
export interface SyncPackage {
|
|
36
|
+
/** Package format version */
|
|
37
|
+
version: 1;
|
|
38
|
+
/** Timestamp of package creation */
|
|
39
|
+
createdAt: string;
|
|
40
|
+
/** Manifest of all synced files */
|
|
41
|
+
manifest: Manifest;
|
|
42
|
+
/** File contents */
|
|
43
|
+
files: SyncFile[];
|
|
44
|
+
}
|
|
45
|
+
export interface DiffEntry {
|
|
46
|
+
relativePath: string;
|
|
47
|
+
status: 'added' | 'removed' | 'modified' | 'renamed';
|
|
48
|
+
localChecksum: string | null;
|
|
49
|
+
remoteChecksum: string | null;
|
|
50
|
+
}
|
|
51
|
+
export interface ConflictEntry {
|
|
52
|
+
relativePath: string;
|
|
53
|
+
localChecksum: string;
|
|
54
|
+
remoteChecksum: string;
|
|
55
|
+
lastKnownChecksum: string | null;
|
|
56
|
+
}
|
|
57
|
+
export interface SyncDiff {
|
|
58
|
+
/** Only in local manifest */
|
|
59
|
+
added: DiffEntry[];
|
|
60
|
+
/** Only in remote manifest */
|
|
61
|
+
removed: DiffEntry[];
|
|
62
|
+
/** Present in both, checksum differs, lastKnown matches one side */
|
|
63
|
+
modified: DiffEntry[];
|
|
64
|
+
/** Present in both, checksum differs, lastKnown matches neither (both sides changed) */
|
|
65
|
+
conflicts: ConflictEntry[];
|
|
66
|
+
/** Whether any conflicts exist */
|
|
67
|
+
hasConflicts: boolean;
|
|
68
|
+
/** Whether there are any differences at all */
|
|
69
|
+
hasChanges: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface SyncResult {
|
|
72
|
+
success: boolean;
|
|
73
|
+
action: 'push' | 'pull' | 'force-push' | 'force-pull';
|
|
74
|
+
filesUploaded: number;
|
|
75
|
+
filesDownloaded: number;
|
|
76
|
+
errors: string[];
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/types/sync.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACvB,8BAA8B;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,oBAAoB;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACvB,6BAA6B;IAC7B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,8BAA8B;IAC9B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,wFAAwF;IACxF,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,kCAAkC;IAClC,YAAY,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/types/sync.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Manifest, SyncDiff } from '../types/sync.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compare two manifests and produce a SyncDiff.
|
|
4
|
+
* Uses lastKnownManifest for three-way merge detection.
|
|
5
|
+
*/
|
|
6
|
+
export declare function diffManifests(local: Manifest, remote: Manifest, lastKnown: Manifest | null): SyncDiff;
|
|
7
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/utils/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAiB,QAAQ,EAA4B,MAAM,kBAAkB,CAAC;AAEpG;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ,EAChB,SAAS,EAAE,QAAQ,GAAG,IAAI,GACzB,QAAQ,CAqGV"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare two manifests and produce a SyncDiff.
|
|
3
|
+
* Uses lastKnownManifest for three-way merge detection.
|
|
4
|
+
*/
|
|
5
|
+
export function diffManifests(local, remote, lastKnown) {
|
|
6
|
+
const localMap = new Map();
|
|
7
|
+
const remoteMap = new Map();
|
|
8
|
+
const lastKnownMap = new Map();
|
|
9
|
+
for (const entry of local.entries) {
|
|
10
|
+
localMap.set(entry.relativePath, entry);
|
|
11
|
+
}
|
|
12
|
+
for (const entry of remote.entries) {
|
|
13
|
+
remoteMap.set(entry.relativePath, entry);
|
|
14
|
+
}
|
|
15
|
+
if (lastKnown) {
|
|
16
|
+
for (const entry of lastKnown.entries) {
|
|
17
|
+
lastKnownMap.set(entry.relativePath, entry);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const allPaths = new Set([
|
|
21
|
+
...localMap.keys(),
|
|
22
|
+
...remoteMap.keys(),
|
|
23
|
+
]);
|
|
24
|
+
const added = [];
|
|
25
|
+
const removed = [];
|
|
26
|
+
const modified = [];
|
|
27
|
+
const conflicts = [];
|
|
28
|
+
for (const path of allPaths) {
|
|
29
|
+
const localEntry = localMap.get(path);
|
|
30
|
+
const remoteEntry = remoteMap.get(path);
|
|
31
|
+
const lastKnownEntry = lastKnownMap.get(path);
|
|
32
|
+
// Only local
|
|
33
|
+
if (localEntry && !remoteEntry) {
|
|
34
|
+
added.push({
|
|
35
|
+
relativePath: path,
|
|
36
|
+
status: 'added',
|
|
37
|
+
localChecksum: localEntry.checksum,
|
|
38
|
+
remoteChecksum: null,
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
// Only remote
|
|
43
|
+
if (!localEntry && remoteEntry) {
|
|
44
|
+
removed.push({
|
|
45
|
+
relativePath: path,
|
|
46
|
+
status: 'removed',
|
|
47
|
+
localChecksum: null,
|
|
48
|
+
remoteChecksum: remoteEntry.checksum,
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// Both exist
|
|
53
|
+
if (localEntry && remoteEntry) {
|
|
54
|
+
if (localEntry.checksum === remoteEntry.checksum) {
|
|
55
|
+
// Same — no diff
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Different checksums — use three-way merge detection
|
|
59
|
+
const localChanged = !lastKnownEntry || localEntry.checksum !== lastKnownEntry.checksum;
|
|
60
|
+
const remoteChanged = !lastKnownEntry || remoteEntry.checksum !== lastKnownEntry.checksum;
|
|
61
|
+
if (localChanged && !remoteChanged) {
|
|
62
|
+
// Only local changed — safe to push
|
|
63
|
+
modified.push({
|
|
64
|
+
relativePath: path,
|
|
65
|
+
status: 'modified',
|
|
66
|
+
localChecksum: localEntry.checksum,
|
|
67
|
+
remoteChecksum: remoteEntry.checksum,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (!localChanged && remoteChanged) {
|
|
71
|
+
// Only remote changed — safe to pull
|
|
72
|
+
modified.push({
|
|
73
|
+
relativePath: path,
|
|
74
|
+
status: 'modified',
|
|
75
|
+
localChecksum: localEntry.checksum,
|
|
76
|
+
remoteChecksum: remoteEntry.checksum,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Both changed or no base — conflict
|
|
81
|
+
conflicts.push({
|
|
82
|
+
relativePath: path,
|
|
83
|
+
localChecksum: localEntry.checksum,
|
|
84
|
+
remoteChecksum: remoteEntry.checksum,
|
|
85
|
+
lastKnownChecksum: lastKnownEntry?.checksum || null,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
added,
|
|
92
|
+
removed,
|
|
93
|
+
modified,
|
|
94
|
+
conflicts,
|
|
95
|
+
hasConflicts: conflicts.length > 0,
|
|
96
|
+
hasChanges: added.length > 0 || removed.length > 0 || modified.length > 0 || conflicts.length > 0,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/utils/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppConfig } from '../types/config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Environment variable helpers.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getEnv(key: string, defaultValue?: string): string | undefined;
|
|
6
|
+
export declare function requireEnv(key: string): string;
|
|
7
|
+
export declare function getGithubToken(): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the GitHub token from config file first, then environment variable.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveGithubToken(config?: AppConfig | null): string | undefined;
|
|
12
|
+
export declare function getEncryptionPassword(): string | undefined;
|
|
13
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;GAEG;AAEH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAKhF;AAED,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAE1D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable helpers.
|
|
3
|
+
*/
|
|
4
|
+
export function getEnv(key, defaultValue) {
|
|
5
|
+
return process.env[key] || defaultValue;
|
|
6
|
+
}
|
|
7
|
+
export function requireEnv(key) {
|
|
8
|
+
const value = process.env[key];
|
|
9
|
+
if (!value) {
|
|
10
|
+
throw new Error(`Required environment variable ${key} is not set.`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
export function getGithubToken() {
|
|
15
|
+
return getEnv('GITHUB_TOKEN');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the GitHub token from config file first, then environment variable.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveGithubToken(config) {
|
|
21
|
+
if (config?.githubToken) {
|
|
22
|
+
return config.githubToken;
|
|
23
|
+
}
|
|
24
|
+
return getGithubToken();
|
|
25
|
+
}
|
|
26
|
+
export function getEncryptionPassword() {
|
|
27
|
+
return getEnv('AITOOLSYNC_PASSWORD');
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH,MAAM,UAAU,MAAM,CAAC,GAAW,EAAE,YAAqB;IACvD,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,cAAc,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,MAAM,CAAC,cAAc,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,IAAI,MAAM,EAAE,WAAW,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IACD,OAAO,cAAc,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare enum LogLevel {
|
|
2
|
+
DEBUG = 0,
|
|
3
|
+
INFO = 1,
|
|
4
|
+
WARN = 2,
|
|
5
|
+
ERROR = 3
|
|
6
|
+
}
|
|
7
|
+
export declare function setLogLevel(level: LogLevel): void;
|
|
8
|
+
export declare function debug(msg: string): void;
|
|
9
|
+
export declare function info(msg: string): void;
|
|
10
|
+
export declare function success(msg: string): void;
|
|
11
|
+
export declare function warn(msg: string): void;
|
|
12
|
+
export declare function error(msg: string): void;
|
|
13
|
+
export declare function plain(msg: string): void;
|
|
14
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAID,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAEjD;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIvC;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAItC;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEzC;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAItC;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIvC;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export var LogLevel;
|
|
3
|
+
(function (LogLevel) {
|
|
4
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
5
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
6
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
7
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
8
|
+
})(LogLevel || (LogLevel = {}));
|
|
9
|
+
let currentLevel = LogLevel.INFO;
|
|
10
|
+
export function setLogLevel(level) {
|
|
11
|
+
currentLevel = level;
|
|
12
|
+
}
|
|
13
|
+
export function debug(msg) {
|
|
14
|
+
if (currentLevel <= LogLevel.DEBUG) {
|
|
15
|
+
process.stderr.write(chalk.gray(`[debug] ${msg}\n`));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function info(msg) {
|
|
19
|
+
if (currentLevel <= LogLevel.INFO) {
|
|
20
|
+
process.stderr.write(chalk.blue(`[info] ${msg}\n`));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function success(msg) {
|
|
24
|
+
process.stderr.write(chalk.green(`[success] ${msg}\n`));
|
|
25
|
+
}
|
|
26
|
+
export function warn(msg) {
|
|
27
|
+
if (currentLevel <= LogLevel.WARN) {
|
|
28
|
+
process.stderr.write(chalk.yellow(`[warn] ${msg}\n`));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function error(msg) {
|
|
32
|
+
if (currentLevel <= LogLevel.ERROR) {
|
|
33
|
+
process.stderr.write(chalk.red(`[error] ${msg}\n`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function plain(msg) {
|
|
37
|
+
process.stdout.write(`${msg}\n`);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAN,IAAY,QAKX;AALD,WAAY,QAAQ;IAClB,yCAAS,CAAA;IACT,uCAAQ,CAAA;IACR,uCAAQ,CAAA;IACR,yCAAS,CAAA;AACX,CAAC,EALW,QAAQ,KAAR,QAAQ,QAKnB;AAED,IAAI,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;AAEjC,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,YAAY,GAAG,KAAK,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,YAAY,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,IAAI,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,IAAI,YAAY,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,YAAY,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SyncPathEntry } from '../types/config.js';
|
|
2
|
+
import type { DiffEntry, ConflictEntry } from '../types/sync.js';
|
|
3
|
+
export declare function renderPathList(entries: SyncPathEntry[]): void;
|
|
4
|
+
export declare function renderDiffTable(added: DiffEntry[], removed: DiffEntry[], modified: DiffEntry[], conflicts: ConflictEntry[]): void;
|
|
5
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/utils/table.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI,CAqB7D;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAAE,EAClB,OAAO,EAAE,SAAS,EAAE,EACpB,QAAQ,EAAE,SAAS,EAAE,EACrB,SAAS,EAAE,aAAa,EAAE,GACzB,IAAI,CA2CN"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Table from 'cli-table3';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
export function renderPathList(entries) {
|
|
4
|
+
if (entries.length === 0) {
|
|
5
|
+
console.log(chalk.gray(' No paths configured.'));
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const table = new Table({
|
|
9
|
+
head: [chalk.bold('Name'), chalk.bold('Path'), chalk.bold('Enabled')],
|
|
10
|
+
style: { head: [] },
|
|
11
|
+
colWidths: [20, 50, 10],
|
|
12
|
+
});
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
table.push([
|
|
15
|
+
entry.name,
|
|
16
|
+
entry.path,
|
|
17
|
+
entry.enabled ? chalk.green('yes') : chalk.gray('no'),
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
console.log(table.toString());
|
|
21
|
+
}
|
|
22
|
+
export function renderDiffTable(added, removed, modified, conflicts) {
|
|
23
|
+
if (added.length === 0 && removed.length === 0 && modified.length === 0 && conflicts.length === 0) {
|
|
24
|
+
console.log(chalk.green(' No differences found. Local and remote are in sync.'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const table = new Table({
|
|
28
|
+
head: [chalk.bold('File'), chalk.bold('Status'), chalk.bold('Local'), chalk.bold('Remote')],
|
|
29
|
+
style: { head: [] },
|
|
30
|
+
colWidths: [40, 12, 14, 14],
|
|
31
|
+
});
|
|
32
|
+
for (const entry of added) {
|
|
33
|
+
table.push([entry.relativePath, chalk.green('added'), entry.localChecksum?.slice(0, 8) || '-', '-']);
|
|
34
|
+
}
|
|
35
|
+
for (const entry of removed) {
|
|
36
|
+
table.push([entry.relativePath, chalk.red('removed'), '-', entry.remoteChecksum?.slice(0, 8) || '-']);
|
|
37
|
+
}
|
|
38
|
+
for (const entry of modified) {
|
|
39
|
+
table.push([
|
|
40
|
+
entry.relativePath,
|
|
41
|
+
chalk.yellow('modified'),
|
|
42
|
+
entry.localChecksum?.slice(0, 8) || '-',
|
|
43
|
+
entry.remoteChecksum?.slice(0, 8) || '-',
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
for (const entry of conflicts) {
|
|
47
|
+
table.push([
|
|
48
|
+
entry.relativePath,
|
|
49
|
+
chalk.red('CONFLICT'),
|
|
50
|
+
entry.localChecksum.slice(0, 8),
|
|
51
|
+
entry.remoteChecksum.slice(0, 8),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
console.log(table.toString());
|
|
55
|
+
if (conflicts.length > 0) {
|
|
56
|
+
console.log(chalk.red(`\n ${conflicts.length} conflict(s) detected. Use --force to override.`));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/utils/table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACnB,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;KACxB,CAAC,CAAC;IAEH,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC;YACT,KAAK,CAAC,IAAI;YACV,KAAK,CAAC,IAAI;YACV,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAkB,EAClB,OAAoB,EACpB,QAAqB,EACrB,SAA0B;IAE1B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC,CAAC;QAClF,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3F,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACnB,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;KAC5B,CAAC,CAAC;IAEH,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACvG,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACxG,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC;YACT,KAAK,CAAC,YAAY;YAClB,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG;YACvC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GAAG;SACzC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC;YACT,KAAK,CAAC,YAAY;YAClB,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;YACrB,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACjC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE9B,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,SAAS,CAAC,MAAM,iDAAiD,CAAC,CAAC,CAAC;IACnG,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tengx5383/aitool-sync-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for syncing AI agent tool configs across devices via GitHub Gist",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"aitool-sync": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"postbuild": "node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
13
|
+
"prepare": "npm run build",
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"prettier": "prettier --write ."
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["cli", "sync", "ai-tools", "claude-code", "config"],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@octokit/rest": "^21.0.0",
|
|
23
|
+
"chalk": "^5.3.0",
|
|
24
|
+
"cli-table3": "^0.6.5",
|
|
25
|
+
"commander": "^12.1.0",
|
|
26
|
+
"dayjs": "^1.11.13",
|
|
27
|
+
"inquirer": "^12.0.0",
|
|
28
|
+
"micromatch": "^4.0.8",
|
|
29
|
+
"ora": "^8.1.0",
|
|
30
|
+
"zod": "^3.23.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/inquirer": "^9.0.7",
|
|
34
|
+
"@types/micromatch": "^4.0.9",
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"prettier": "^3.3.0",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"typescript": "^5.5.0",
|
|
39
|
+
"vitest": "^2.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { encrypt, decrypt, isEncrypted } from '../../src/crypto/encryptor.js';
|
|
6
|
+
import { setPassword, clearPassword, resolvePassword, encryptWithPassword, decryptWithPassword, hasPassword } from '../../src/crypto/key-resolver.js';
|
|
7
|
+
import { createDefaultConfig, writeConfig } from '../../src/config/config-file.js';
|
|
8
|
+
import { buildSyncPackage, parseSyncPackage } from '../../src/core/sync-package.js';
|
|
9
|
+
import { createManifest, serializeManifest } from '../../src/core/manifest.js';
|
|
10
|
+
import { pushSync, pullSync, writeRemoteFiles } from '../../src/core/sync-engine.js';
|
|
11
|
+
import { MockGistClient } from '../../src/gist/mock-client.js';
|
|
12
|
+
import type { AppConfig } from '../../src/types/index.js';
|
|
13
|
+
|
|
14
|
+
const TEST_PASSWORD = 'test-secret-password-123';
|
|
15
|
+
|
|
16
|
+
function createTestDir(base: string): string {
|
|
17
|
+
const dir = path.join(base, 'encrypted-tools');
|
|
18
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify({ version: 1 }));
|
|
20
|
+
fs.writeFileSync(path.join(dir, 'token.json'), JSON.stringify({ apiKey: 'secret-token-123' }));
|
|
21
|
+
return dir;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('encryption flow', () => {
|
|
25
|
+
let tmpDir: string;
|
|
26
|
+
let mockClient: MockGistClient;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aitoolsync-crypto-'));
|
|
30
|
+
process.chdir(tmpDir);
|
|
31
|
+
mockClient = new MockGistClient();
|
|
32
|
+
setPassword(TEST_PASSWORD);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
37
|
+
clearPassword();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('encrypts and decrypts a string', () => {
|
|
41
|
+
const plaintext = 'Hello, World!';
|
|
42
|
+
const encrypted = encrypt(plaintext, TEST_PASSWORD);
|
|
43
|
+
expect(encrypted).not.toBe(plaintext);
|
|
44
|
+
|
|
45
|
+
const decrypted = decrypt(encrypted, TEST_PASSWORD);
|
|
46
|
+
expect(decrypted).toBe(plaintext);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('detects encrypted payloads', () => {
|
|
50
|
+
const encrypted = encrypt('test data', TEST_PASSWORD);
|
|
51
|
+
expect(isEncrypted(encrypted)).toBe(true);
|
|
52
|
+
expect(isEncrypted('plain text')).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('key resolver caches and resolves password', () => {
|
|
56
|
+
clearPassword();
|
|
57
|
+
expect(hasPassword()).toBe(false);
|
|
58
|
+
|
|
59
|
+
setPassword('my-password');
|
|
60
|
+
expect(hasPassword()).toBe(true);
|
|
61
|
+
expect(resolvePassword()).toBe('my-password');
|
|
62
|
+
|
|
63
|
+
clearPassword();
|
|
64
|
+
expect(hasPassword()).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('encrypt with password convenience function', () => {
|
|
68
|
+
const plaintext = 'sensitive data';
|
|
69
|
+
const encrypted = encryptWithPassword(plaintext);
|
|
70
|
+
expect(encrypted).not.toBe(plaintext);
|
|
71
|
+
|
|
72
|
+
const decrypted = decryptWithPassword(encrypted);
|
|
73
|
+
expect(decrypted).toBe(plaintext);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('push and pull with encrypted files', async () => {
|
|
77
|
+
const testDir = createTestDir(tmpDir);
|
|
78
|
+
const config = createDefaultConfig();
|
|
79
|
+
config.syncPaths = [{ name: 'enc-tools', path: testDir, enabled: true }];
|
|
80
|
+
config.encryption = [{ pattern: '**/token.json' }];
|
|
81
|
+
|
|
82
|
+
const gist = await mockClient.createGist('aitoolsync', {
|
|
83
|
+
'aitoolsync-package.json': { content: '{}' },
|
|
84
|
+
});
|
|
85
|
+
config.gistId = gist.gistId;
|
|
86
|
+
writeConfig(config);
|
|
87
|
+
|
|
88
|
+
// Push with encryption
|
|
89
|
+
const pushResult = await pushSync(config, mockClient, false, encryptWithPassword);
|
|
90
|
+
expect(pushResult.success).toBe(true);
|
|
91
|
+
|
|
92
|
+
// Verify Gist content is encrypted for token.json
|
|
93
|
+
const fetched = await mockClient.getGist(gist.gistId);
|
|
94
|
+
const pkgFile = fetched.files.find((f) => f.filename === 'aitoolsync-package.json');
|
|
95
|
+
const pkg = parseSyncPackage(pkgFile!.content);
|
|
96
|
+
const tokenFile = pkg!.files.find((f) => f.relativePath === 'token.json');
|
|
97
|
+
expect(tokenFile!.encrypted).toBe(true);
|
|
98
|
+
expect(tokenFile!.content).not.toContain('secret-token');
|
|
99
|
+
|
|
100
|
+
const configFile = pkg!.files.find((f) => f.relativePath === 'config.json');
|
|
101
|
+
expect(configFile!.encrypted).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('pull and decrypt files', async () => {
|
|
105
|
+
const testDir = createTestDir(tmpDir);
|
|
106
|
+
const config = createDefaultConfig();
|
|
107
|
+
config.syncPaths = [{ name: 'enc-tools', path: testDir, enabled: true }];
|
|
108
|
+
config.encryption = [{ pattern: '**/token.json' }];
|
|
109
|
+
|
|
110
|
+
const gist = await mockClient.createGist('aitoolsync', {
|
|
111
|
+
'aitoolsync-package.json': { content: '{}' },
|
|
112
|
+
});
|
|
113
|
+
config.gistId = gist.gistId;
|
|
114
|
+
writeConfig(config);
|
|
115
|
+
|
|
116
|
+
// Push with encryption first
|
|
117
|
+
await pushSync(config, mockClient, false, encryptWithPassword);
|
|
118
|
+
|
|
119
|
+
// Corrupt local files
|
|
120
|
+
fs.writeFileSync(path.join(testDir, 'token.json'), 'CORRUPTED');
|
|
121
|
+
|
|
122
|
+
// Pull and decrypt
|
|
123
|
+
const pullResult = await pullSync(config, mockClient, false, decryptWithPassword);
|
|
124
|
+
expect(pullResult.success).toBe(true);
|
|
125
|
+
|
|
126
|
+
// Verify file was restored correctly
|
|
127
|
+
const content = JSON.parse(fs.readFileSync(path.join(testDir, 'token.json'), 'utf-8'));
|
|
128
|
+
expect(content.apiKey).toBe('secret-token-123');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('fails to decrypt with wrong password', () => {
|
|
132
|
+
const encrypted = encrypt('secret', TEST_PASSWORD);
|
|
133
|
+
expect(() => decrypt(encrypted, 'wrong-password')).toThrow();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('encrypted content does not leak plaintext', () => {
|
|
137
|
+
const plaintext = 'super-secret-api-key-12345';
|
|
138
|
+
const encrypted = encrypt(plaintext, TEST_PASSWORD);
|
|
139
|
+
expect(encrypted).not.toContain('super-secret-api-key');
|
|
140
|
+
expect(encrypted).not.toContain('12345');
|
|
141
|
+
});
|
|
142
|
+
});
|