@kodrunhq/claudefy 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/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/backup-manager/backup-manager.d.ts +6 -0
- package/dist/backup-manager/backup-manager.js +27 -0
- package/dist/backup-manager/backup-manager.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +296 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +56 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/doctor.d.ts +14 -0
- package/dist/commands/doctor.js +64 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/hooks.d.ts +7 -0
- package/dist/commands/hooks.js +18 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +66 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/join.d.ts +12 -0
- package/dist/commands/join.js +51 -0
- package/dist/commands/join.js.map +1 -0
- package/dist/commands/link.d.ts +12 -0
- package/dist/commands/link.js +34 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/machines.d.ts +6 -0
- package/dist/commands/machines.js +25 -0
- package/dist/commands/machines.js.map +1 -0
- package/dist/commands/override.d.ts +12 -0
- package/dist/commands/override.js +44 -0
- package/dist/commands/override.js.map +1 -0
- package/dist/commands/pull.d.ts +17 -0
- package/dist/commands/pull.js +220 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +14 -0
- package/dist/commands/push.js +175 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/status.d.ts +14 -0
- package/dist/commands/status.js +50 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/config-manager.d.ts +22 -0
- package/dist/config/config-manager.js +118 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/defaults.d.ts +7 -0
- package/dist/config/defaults.js +33 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/types.d.ts +25 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/encryptor/encryptor.d.ts +10 -0
- package/dist/encryptor/encryptor.js +68 -0
- package/dist/encryptor/encryptor.js.map +1 -0
- package/dist/encryptor/passphrase.d.ts +7 -0
- package/dist/encryptor/passphrase.js +34 -0
- package/dist/encryptor/passphrase.js.map +1 -0
- package/dist/git-adapter/git-adapter.d.ts +19 -0
- package/dist/git-adapter/git-adapter.js +104 -0
- package/dist/git-adapter/git-adapter.js.map +1 -0
- package/dist/git-adapter/types.d.ts +14 -0
- package/dist/git-adapter/types.js +2 -0
- package/dist/git-adapter/types.js.map +1 -0
- package/dist/hook-manager/hook-manager.d.ts +11 -0
- package/dist/hook-manager/hook-manager.js +110 -0
- package/dist/hook-manager/hook-manager.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/machine-registry/machine-registry.d.ts +16 -0
- package/dist/machine-registry/machine-registry.js +65 -0
- package/dist/machine-registry/machine-registry.js.map +1 -0
- package/dist/merger/merger.d.ts +10 -0
- package/dist/merger/merger.js +15 -0
- package/dist/merger/merger.js.map +1 -0
- package/dist/output.d.ts +8 -0
- package/dist/output.js +10 -0
- package/dist/output.js.map +1 -0
- package/dist/path-mapper/git-identity.d.ts +8 -0
- package/dist/path-mapper/git-identity.js +35 -0
- package/dist/path-mapper/git-identity.js.map +1 -0
- package/dist/path-mapper/path-mapper.d.ts +19 -0
- package/dist/path-mapper/path-mapper.js +101 -0
- package/dist/path-mapper/path-mapper.js.map +1 -0
- package/dist/repo-creator/repo-creator.d.ts +6 -0
- package/dist/repo-creator/repo-creator.js +52 -0
- package/dist/repo-creator/repo-creator.js.map +1 -0
- package/dist/secret-scanner/scanner.d.ts +10 -0
- package/dist/secret-scanner/scanner.js +48 -0
- package/dist/secret-scanner/scanner.js.map +1 -0
- package/dist/sync-filter/sync-filter.d.ts +8 -0
- package/dist/sync-filter/sync-filter.js +37 -0
- package/dist/sync-filter/sync-filter.js.map +1 -0
- package/dist/sync-filter/types.d.ts +13 -0
- package/dist/sync-filter/types.js +2 -0
- package/dist/sync-filter/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { cp, mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { ConfigManager } from "../config/config-manager.js";
|
|
5
|
+
import { GitAdapter } from "../git-adapter/git-adapter.js";
|
|
6
|
+
import { PathMapper } from "../path-mapper/path-mapper.js";
|
|
7
|
+
import { MachineRegistry } from "../machine-registry/machine-registry.js";
|
|
8
|
+
import { Encryptor } from "../encryptor/encryptor.js";
|
|
9
|
+
import { Merger } from "../merger/merger.js";
|
|
10
|
+
import { BackupManager } from "../backup-manager/backup-manager.js";
|
|
11
|
+
import { output } from "../output.js";
|
|
12
|
+
export class PullCommand {
|
|
13
|
+
homeDir;
|
|
14
|
+
claudeDir;
|
|
15
|
+
configManager;
|
|
16
|
+
constructor(homeDir) {
|
|
17
|
+
this.homeDir = homeDir;
|
|
18
|
+
this.claudeDir = join(homeDir, ".claude");
|
|
19
|
+
this.configManager = new ConfigManager(homeDir);
|
|
20
|
+
}
|
|
21
|
+
async execute(options) {
|
|
22
|
+
const config = await this.configManager.load();
|
|
23
|
+
const result = { overrideDetected: false, filesUpdated: 0 };
|
|
24
|
+
// 1. Initialize git adapter and pull
|
|
25
|
+
const claudefyDir = join(this.homeDir, ".claudefy");
|
|
26
|
+
const gitAdapter = new GitAdapter(claudefyDir);
|
|
27
|
+
await gitAdapter.initStore(config.backend.url);
|
|
28
|
+
try {
|
|
29
|
+
await gitAdapter.pull();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Fresh store with no remote history yet
|
|
33
|
+
}
|
|
34
|
+
const storePath = gitAdapter.getStorePath();
|
|
35
|
+
const configDir = join(storePath, "config");
|
|
36
|
+
const unknownDir = join(storePath, "unknown");
|
|
37
|
+
const resolvedClaudeDir = resolve(this.claudeDir);
|
|
38
|
+
// 2. Check for override marker
|
|
39
|
+
const override = await gitAdapter.checkOverrideMarker();
|
|
40
|
+
if (override) {
|
|
41
|
+
result.overrideDetected = true;
|
|
42
|
+
if (!options.quiet) {
|
|
43
|
+
output.warn(`Override detected from machine: ${override.machine} at ${override.timestamp}`);
|
|
44
|
+
}
|
|
45
|
+
// Create backup before applying override (skip if .claude doesn't exist yet)
|
|
46
|
+
if (existsSync(this.claudeDir)) {
|
|
47
|
+
const backupManager = new BackupManager(claudefyDir);
|
|
48
|
+
result.backupPath = await backupManager.createBackup(this.claudeDir, "pre-override");
|
|
49
|
+
}
|
|
50
|
+
if (!options.quiet && result.backupPath) {
|
|
51
|
+
output.info(`Backup created at: ${result.backupPath}`);
|
|
52
|
+
}
|
|
53
|
+
// Remove override marker
|
|
54
|
+
await gitAdapter.removeOverrideMarker();
|
|
55
|
+
await gitAdapter.commitAndPush("pull: acknowledge override");
|
|
56
|
+
}
|
|
57
|
+
// 3. Decrypt .age files if encryption is enabled
|
|
58
|
+
if (config.encryption.enabled && !options.skipEncryption) {
|
|
59
|
+
if (!options.passphrase) {
|
|
60
|
+
throw new Error("Encryption is enabled but CLAUDEFY_PASSPHRASE env var is not set.");
|
|
61
|
+
}
|
|
62
|
+
const encryptor = new Encryptor(options.passphrase);
|
|
63
|
+
// Decrypt sensitive config files
|
|
64
|
+
const filesToDecrypt = ["settings.json.age", "history.jsonl.age"];
|
|
65
|
+
for (const fileName of filesToDecrypt) {
|
|
66
|
+
const filePath = join(configDir, fileName);
|
|
67
|
+
if (existsSync(filePath)) {
|
|
68
|
+
const outputPath = filePath.replace(/\.age$/, "");
|
|
69
|
+
await encryptor.decryptFile(filePath, outputPath);
|
|
70
|
+
await rm(filePath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Decrypt all .age files in unknown/
|
|
74
|
+
if (existsSync(unknownDir)) {
|
|
75
|
+
await encryptor.decryptDirectory(unknownDir);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 4. Remap paths (canonical → local)
|
|
79
|
+
const links = await this.configManager.getLinks();
|
|
80
|
+
const pathMapper = new PathMapper(links);
|
|
81
|
+
// settings.json
|
|
82
|
+
const remoteSettingsPath = join(configDir, "settings.json");
|
|
83
|
+
if (existsSync(remoteSettingsPath)) {
|
|
84
|
+
const settings = JSON.parse(await readFile(remoteSettingsPath, "utf-8"));
|
|
85
|
+
const remapped = pathMapper.remapSettingsPaths(settings, this.claudeDir);
|
|
86
|
+
await writeFile(remoteSettingsPath, JSON.stringify(remapped, null, 2));
|
|
87
|
+
}
|
|
88
|
+
// installed_plugins.json
|
|
89
|
+
const pluginsJsonPath = join(configDir, "plugins", "installed_plugins.json");
|
|
90
|
+
if (existsSync(pluginsJsonPath)) {
|
|
91
|
+
const plugins = JSON.parse(await readFile(pluginsJsonPath, "utf-8"));
|
|
92
|
+
const remapped = pathMapper.remapPluginPaths(plugins, this.claudeDir);
|
|
93
|
+
await writeFile(pluginsJsonPath, JSON.stringify(remapped, null, 2));
|
|
94
|
+
}
|
|
95
|
+
// known_marketplaces.json
|
|
96
|
+
const marketplacesPath = join(configDir, "plugins", "known_marketplaces.json");
|
|
97
|
+
if (existsSync(marketplacesPath)) {
|
|
98
|
+
const mp = JSON.parse(await readFile(marketplacesPath, "utf-8"));
|
|
99
|
+
const remapped = pathMapper.remapPluginPaths(mp, this.claudeDir);
|
|
100
|
+
await writeFile(marketplacesPath, JSON.stringify(remapped, null, 2));
|
|
101
|
+
}
|
|
102
|
+
// history.jsonl
|
|
103
|
+
const historyPath = join(configDir, "history.jsonl");
|
|
104
|
+
if (existsSync(historyPath)) {
|
|
105
|
+
const content = await readFile(historyPath, "utf-8");
|
|
106
|
+
const remapped = content
|
|
107
|
+
.split("\n")
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.map((line) => pathMapper.remapJsonlLine(line))
|
|
110
|
+
.join("\n") + "\n";
|
|
111
|
+
await writeFile(historyPath, remapped);
|
|
112
|
+
}
|
|
113
|
+
// projects/ directory renaming (canonical → local)
|
|
114
|
+
const projectsDir = join(configDir, "projects");
|
|
115
|
+
if (existsSync(projectsDir)) {
|
|
116
|
+
const projectDirs = await readdir(projectsDir);
|
|
117
|
+
for (const dirName of projectDirs) {
|
|
118
|
+
const localName = pathMapper.remapDirName(dirName);
|
|
119
|
+
if (localName) {
|
|
120
|
+
const destPath = resolve(join(projectsDir, localName));
|
|
121
|
+
// Path containment: ensure renamed dir stays within projects/
|
|
122
|
+
if (!destPath.startsWith(resolve(projectsDir) + "/")) {
|
|
123
|
+
console.warn(`Skipping directory rename "${dirName}" → "${localName}": path escapes projects directory`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
await rename(join(projectsDir, dirName), destPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 5. Merge and copy to ~/.claude
|
|
131
|
+
const merger = new Merger();
|
|
132
|
+
await mkdir(this.claudeDir, { recursive: true });
|
|
133
|
+
// 5a. Deep merge settings.json
|
|
134
|
+
if (existsSync(remoteSettingsPath)) {
|
|
135
|
+
const localSettingsPath = join(this.claudeDir, "settings.json");
|
|
136
|
+
const remoteSettings = JSON.parse(await readFile(remoteSettingsPath, "utf-8"));
|
|
137
|
+
// Security: strip hooks from remote settings to prevent remote hook injection.
|
|
138
|
+
// Local hooks should never be overwritten by remote data, as an attacker with
|
|
139
|
+
// store access could inject arbitrary shell commands via SessionStart/SessionEnd hooks.
|
|
140
|
+
delete remoteSettings.hooks;
|
|
141
|
+
if (existsSync(localSettingsPath) && !result.overrideDetected) {
|
|
142
|
+
const localSettings = JSON.parse(await readFile(localSettingsPath, "utf-8"));
|
|
143
|
+
const merged = merger.deepMergeJson(localSettings, remoteSettings);
|
|
144
|
+
await writeFile(localSettingsPath, JSON.stringify(merged, null, 2));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
await writeFile(localSettingsPath, JSON.stringify(remoteSettings, null, 2));
|
|
148
|
+
}
|
|
149
|
+
result.filesUpdated++;
|
|
150
|
+
}
|
|
151
|
+
// 5b. Copy remaining config items (remote overwrites local)
|
|
152
|
+
if (existsSync(configDir)) {
|
|
153
|
+
const entries = await readdir(configDir, { withFileTypes: true });
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
if (entry.name === "settings.json")
|
|
156
|
+
continue; // Already handled
|
|
157
|
+
// Security: skip symlinks to prevent path traversal attacks
|
|
158
|
+
if (entry.isSymbolicLink()) {
|
|
159
|
+
console.warn(`Skipping symlink in config store: ${entry.name}`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const src = join(configDir, entry.name);
|
|
163
|
+
const dest = resolve(join(this.claudeDir, entry.name));
|
|
164
|
+
// Path containment: ensure destination stays within ~/.claude/
|
|
165
|
+
if (!dest.startsWith(resolvedClaudeDir + "/") && dest !== resolvedClaudeDir) {
|
|
166
|
+
console.warn(`Skipping "${entry.name}": resolved path escapes ~/.claude/`);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
await cp(src, dest, { recursive: true, force: true });
|
|
170
|
+
result.filesUpdated++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// 5c. Copy unknown items back
|
|
174
|
+
if (existsSync(unknownDir)) {
|
|
175
|
+
const entries = await readdir(unknownDir, { withFileTypes: true });
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
// Security: skip symlinks to prevent path traversal attacks
|
|
178
|
+
if (entry.isSymbolicLink()) {
|
|
179
|
+
console.warn(`Skipping symlink in unknown store: ${entry.name}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const src = join(unknownDir, entry.name);
|
|
183
|
+
const dest = resolve(join(this.claudeDir, entry.name));
|
|
184
|
+
// Path containment: ensure destination stays within ~/.claude/
|
|
185
|
+
if (!dest.startsWith(resolvedClaudeDir + "/") && dest !== resolvedClaudeDir) {
|
|
186
|
+
console.warn(`Skipping "${entry.name}": resolved path escapes ~/.claude/`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
await cp(src, dest, { recursive: true, force: true });
|
|
190
|
+
result.filesUpdated++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// 6. Re-encrypt decrypted files in the store before committing,
|
|
194
|
+
// so plaintext is never committed to git history.
|
|
195
|
+
if (config.encryption.enabled && !options.skipEncryption) {
|
|
196
|
+
const encryptor = new Encryptor(options.passphrase);
|
|
197
|
+
const filesToReencrypt = ["settings.json", "history.jsonl"];
|
|
198
|
+
for (const fileName of filesToReencrypt) {
|
|
199
|
+
const filePath = join(configDir, fileName);
|
|
200
|
+
if (existsSync(filePath)) {
|
|
201
|
+
await encryptor.encryptFile(filePath, filePath + ".age");
|
|
202
|
+
await rm(filePath);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Re-encrypt all plaintext files in unknown/
|
|
206
|
+
if (existsSync(unknownDir)) {
|
|
207
|
+
await encryptor.encryptDirectory(unknownDir);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// 7. Update machine registry last sync time and commit
|
|
211
|
+
const registry = new MachineRegistry(join(storePath, "manifest.json"));
|
|
212
|
+
await registry.updateLastSync(config.machineId);
|
|
213
|
+
await gitAdapter.commitAndPush(`sync: pull on ${config.machineId}`);
|
|
214
|
+
if (!options.quiet) {
|
|
215
|
+
output.success(`Pull complete. ${result.filesUpdated} items updated.`);
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PushOptions {
|
|
2
|
+
quiet: boolean;
|
|
3
|
+
skipEncryption?: boolean;
|
|
4
|
+
skipSecretScan?: boolean;
|
|
5
|
+
passphrase?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class PushCommand {
|
|
8
|
+
private homeDir;
|
|
9
|
+
private claudeDir;
|
|
10
|
+
private configManager;
|
|
11
|
+
constructor(homeDir: string);
|
|
12
|
+
execute(options: PushOptions): Promise<void>;
|
|
13
|
+
private collectFiles;
|
|
14
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { cp, mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { hostname, platform } from "node:os";
|
|
5
|
+
import { ConfigManager } from "../config/config-manager.js";
|
|
6
|
+
import { SyncFilter } from "../sync-filter/sync-filter.js";
|
|
7
|
+
import { GitAdapter } from "../git-adapter/git-adapter.js";
|
|
8
|
+
import { PathMapper } from "../path-mapper/path-mapper.js";
|
|
9
|
+
import { MachineRegistry } from "../machine-registry/machine-registry.js";
|
|
10
|
+
import { Encryptor } from "../encryptor/encryptor.js";
|
|
11
|
+
import { SecretScanner } from "../secret-scanner/scanner.js";
|
|
12
|
+
import { output } from "../output.js";
|
|
13
|
+
export class PushCommand {
|
|
14
|
+
homeDir;
|
|
15
|
+
claudeDir;
|
|
16
|
+
configManager;
|
|
17
|
+
constructor(homeDir) {
|
|
18
|
+
this.homeDir = homeDir;
|
|
19
|
+
this.claudeDir = join(homeDir, ".claude");
|
|
20
|
+
this.configManager = new ConfigManager(homeDir);
|
|
21
|
+
}
|
|
22
|
+
async execute(options) {
|
|
23
|
+
const config = await this.configManager.load();
|
|
24
|
+
const syncFilterConfig = await this.configManager.getSyncFilter();
|
|
25
|
+
const syncFilter = new SyncFilter(syncFilterConfig);
|
|
26
|
+
// 1. Classify ~/.claude contents
|
|
27
|
+
if (!existsSync(this.claudeDir)) {
|
|
28
|
+
throw new Error(`No ${this.claudeDir} directory found. Nothing to push.`);
|
|
29
|
+
}
|
|
30
|
+
const classification = await syncFilter.classify(this.claudeDir);
|
|
31
|
+
const willEncrypt = config.encryption.enabled && !options.skipEncryption;
|
|
32
|
+
if (!options.quiet) {
|
|
33
|
+
const unknownLabel = willEncrypt ? "unknown (encrypted)" : "unknown";
|
|
34
|
+
output.info(`Syncing: ${classification.allowlist.length} allowed, ` +
|
|
35
|
+
`${classification.unknown.length} ${unknownLabel}, ` +
|
|
36
|
+
`${classification.denylist.length} denied`);
|
|
37
|
+
}
|
|
38
|
+
// 2. Initialize git adapter
|
|
39
|
+
const gitAdapter = new GitAdapter(join(this.homeDir, ".claudefy"));
|
|
40
|
+
await gitAdapter.initStore(config.backend.url);
|
|
41
|
+
try {
|
|
42
|
+
await gitAdapter.pull();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Fresh store with no remote history yet — safe to continue
|
|
46
|
+
}
|
|
47
|
+
const storePath = gitAdapter.getStorePath();
|
|
48
|
+
const configDir = join(storePath, "config");
|
|
49
|
+
const unknownDir = join(storePath, "unknown");
|
|
50
|
+
// 3. Clean existing config and unknown dirs in store
|
|
51
|
+
if (existsSync(configDir)) {
|
|
52
|
+
await rm(configDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
if (existsSync(unknownDir)) {
|
|
55
|
+
await rm(unknownDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
await mkdir(configDir, { recursive: true });
|
|
58
|
+
await mkdir(unknownDir, { recursive: true });
|
|
59
|
+
// 4. Copy allowlisted items
|
|
60
|
+
for (const item of classification.allowlist) {
|
|
61
|
+
const src = join(this.claudeDir, item.name);
|
|
62
|
+
const dest = join(configDir, item.name);
|
|
63
|
+
await cp(src, dest, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
// 5. Copy unknown items
|
|
66
|
+
for (const item of classification.unknown) {
|
|
67
|
+
const src = join(this.claudeDir, item.name);
|
|
68
|
+
const dest = join(unknownDir, item.name);
|
|
69
|
+
await cp(src, dest, { recursive: true });
|
|
70
|
+
}
|
|
71
|
+
// 6. Normalize paths in known files
|
|
72
|
+
const links = await this.configManager.getLinks();
|
|
73
|
+
const pathMapper = new PathMapper(links);
|
|
74
|
+
// settings.json
|
|
75
|
+
const settingsPath = join(configDir, "settings.json");
|
|
76
|
+
if (existsSync(settingsPath)) {
|
|
77
|
+
const settings = JSON.parse(await readFile(settingsPath, "utf-8"));
|
|
78
|
+
const normalized = pathMapper.normalizeSettingsPaths(settings, this.claudeDir);
|
|
79
|
+
await writeFile(settingsPath, JSON.stringify(normalized, null, 2));
|
|
80
|
+
}
|
|
81
|
+
// installed_plugins.json
|
|
82
|
+
const pluginsJsonPath = join(configDir, "plugins", "installed_plugins.json");
|
|
83
|
+
if (existsSync(pluginsJsonPath)) {
|
|
84
|
+
const plugins = JSON.parse(await readFile(pluginsJsonPath, "utf-8"));
|
|
85
|
+
const normalized = pathMapper.normalizePluginPaths(plugins, this.claudeDir);
|
|
86
|
+
await writeFile(pluginsJsonPath, JSON.stringify(normalized, null, 2));
|
|
87
|
+
}
|
|
88
|
+
// known_marketplaces.json
|
|
89
|
+
const marketplacesPath = join(configDir, "plugins", "known_marketplaces.json");
|
|
90
|
+
if (existsSync(marketplacesPath)) {
|
|
91
|
+
const mp = JSON.parse(await readFile(marketplacesPath, "utf-8"));
|
|
92
|
+
const normalized = pathMapper.normalizePluginPaths(mp, this.claudeDir);
|
|
93
|
+
await writeFile(marketplacesPath, JSON.stringify(normalized, null, 2));
|
|
94
|
+
}
|
|
95
|
+
// history.jsonl
|
|
96
|
+
const historyPath = join(configDir, "history.jsonl");
|
|
97
|
+
if (existsSync(historyPath)) {
|
|
98
|
+
const content = await readFile(historyPath, "utf-8");
|
|
99
|
+
const normalized = content
|
|
100
|
+
.split("\n")
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.map((line) => pathMapper.normalizeJsonlLine(line))
|
|
103
|
+
.join("\n") + "\n";
|
|
104
|
+
await writeFile(historyPath, normalized);
|
|
105
|
+
}
|
|
106
|
+
// projects/ directory renaming
|
|
107
|
+
const projectsDir = join(configDir, "projects");
|
|
108
|
+
if (existsSync(projectsDir)) {
|
|
109
|
+
const projectDirs = await readdir(projectsDir);
|
|
110
|
+
for (const dirName of projectDirs) {
|
|
111
|
+
const canonicalId = pathMapper.normalizeDirName(dirName);
|
|
112
|
+
if (canonicalId) {
|
|
113
|
+
await rename(join(projectsDir, dirName), join(projectsDir, canonicalId));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 7. Scan for secrets before committing
|
|
118
|
+
if (!options.skipSecretScan) {
|
|
119
|
+
const scanner = new SecretScanner();
|
|
120
|
+
const filesToScan = await this.collectFiles(configDir);
|
|
121
|
+
const unknownFiles = await this.collectFiles(unknownDir);
|
|
122
|
+
filesToScan.push(...unknownFiles);
|
|
123
|
+
const findings = await scanner.scanFiles(filesToScan);
|
|
124
|
+
if (findings.length > 0) {
|
|
125
|
+
const details = findings.map((f) => ` ${f.file}:${f.line} [${f.pattern}]`).join("\n");
|
|
126
|
+
throw new Error(`Secret scan detected ${findings.length} potential secret(s):\n${details}\n\nAborting push. Use --skip-secret-scan to bypass scanning.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// 8. Encrypt files if encryption is enabled
|
|
130
|
+
if (config.encryption.enabled && !options.skipEncryption) {
|
|
131
|
+
if (!options.passphrase) {
|
|
132
|
+
throw new Error("Encryption is enabled but CLAUDEFY_PASSPHRASE env var is not set.");
|
|
133
|
+
}
|
|
134
|
+
const encryptor = new Encryptor(options.passphrase);
|
|
135
|
+
// Encrypt sensitive config files
|
|
136
|
+
const filesToEncrypt = ["settings.json", "history.jsonl"];
|
|
137
|
+
for (const fileName of filesToEncrypt) {
|
|
138
|
+
const filePath = join(configDir, fileName);
|
|
139
|
+
if (existsSync(filePath)) {
|
|
140
|
+
await encryptor.encryptFile(filePath, filePath + ".age");
|
|
141
|
+
await rm(filePath);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Encrypt all files in unknown/
|
|
145
|
+
if (existsSync(unknownDir)) {
|
|
146
|
+
await encryptor.encryptDirectory(unknownDir);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// 9. Update machine registry
|
|
150
|
+
const registry = new MachineRegistry(join(storePath, "manifest.json"));
|
|
151
|
+
await registry.register(config.machineId, hostname(), platform());
|
|
152
|
+
// 10. Commit and push
|
|
153
|
+
await gitAdapter.commitAndPush(`sync: push from ${config.machineId}`);
|
|
154
|
+
if (!options.quiet) {
|
|
155
|
+
output.success("Push complete.");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async collectFiles(dirPath) {
|
|
159
|
+
if (!existsSync(dirPath))
|
|
160
|
+
return [];
|
|
161
|
+
const results = [];
|
|
162
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const fullPath = join(dirPath, entry.name);
|
|
165
|
+
if (entry.isDirectory()) {
|
|
166
|
+
results.push(...(await this.collectFiles(fullPath)));
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
results.push(fullPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return results;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/commands/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface StatusResult {
|
|
2
|
+
initialized: boolean;
|
|
3
|
+
machineId?: string;
|
|
4
|
+
backendUrl?: string;
|
|
5
|
+
localFiles: string[];
|
|
6
|
+
syncedFiles: string[];
|
|
7
|
+
deniedFiles: string[];
|
|
8
|
+
unknownFiles: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class StatusCommand {
|
|
11
|
+
private homeDir;
|
|
12
|
+
constructor(homeDir: string);
|
|
13
|
+
execute(): Promise<StatusResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { ConfigManager } from "../config/config-manager.js";
|
|
5
|
+
import { SyncFilter } from "../sync-filter/sync-filter.js";
|
|
6
|
+
export class StatusCommand {
|
|
7
|
+
homeDir;
|
|
8
|
+
constructor(homeDir) {
|
|
9
|
+
this.homeDir = homeDir;
|
|
10
|
+
}
|
|
11
|
+
async execute() {
|
|
12
|
+
const configManager = new ConfigManager(this.homeDir);
|
|
13
|
+
if (!configManager.isInitialized()) {
|
|
14
|
+
return {
|
|
15
|
+
initialized: false,
|
|
16
|
+
localFiles: [],
|
|
17
|
+
syncedFiles: [],
|
|
18
|
+
deniedFiles: [],
|
|
19
|
+
unknownFiles: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const config = await configManager.load();
|
|
23
|
+
const claudeDir = join(this.homeDir, ".claude");
|
|
24
|
+
if (!existsSync(claudeDir)) {
|
|
25
|
+
return {
|
|
26
|
+
initialized: true,
|
|
27
|
+
machineId: config.machineId,
|
|
28
|
+
backendUrl: config.backend.url,
|
|
29
|
+
localFiles: [],
|
|
30
|
+
syncedFiles: [],
|
|
31
|
+
deniedFiles: [],
|
|
32
|
+
unknownFiles: [],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const syncFilterConfig = await configManager.getSyncFilter();
|
|
36
|
+
const syncFilter = new SyncFilter(syncFilterConfig);
|
|
37
|
+
const localFiles = await readdir(claudeDir);
|
|
38
|
+
const classification = await syncFilter.classify(claudeDir);
|
|
39
|
+
return {
|
|
40
|
+
initialized: true,
|
|
41
|
+
machineId: config.machineId,
|
|
42
|
+
backendUrl: config.backend.url,
|
|
43
|
+
localFiles,
|
|
44
|
+
syncedFiles: classification.allowlist.map((i) => i.name),
|
|
45
|
+
deniedFiles: classification.denylist.map((i) => i.name),
|
|
46
|
+
unknownFiles: classification.unknown.map((i) => i.name),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAY3D,MAAM,OAAO,aAAa;IAChB,OAAO,CAAS;IAExB,YAAY,OAAe;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEtD,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,CAAC;YACnC,OAAO;gBACL,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,EAAE;gBACd,WAAW,EAAE,EAAE;gBACf,WAAW,EAAE,EAAE;gBACf,YAAY,EAAE,EAAE;aACjB,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAEhD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG;gBAC9B,UAAU,EAAE,EAAE;gBACd,WAAW,EAAE,EAAE;gBACf,WAAW,EAAE,EAAE;gBACf,YAAY,EAAE,EAAE;aACjB,CAAC;QACJ,CAAC;QAED,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAAC,aAAa,EAAE,CAAC;QAC7D,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;QACpD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE5D,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG;YAC9B,UAAU;YACV,WAAW,EAAE,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACxD,WAAW,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,YAAY,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SACxD,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ClaudefyConfig, LinksConfig, SyncFilterConfig } from "./types.js";
|
|
2
|
+
export declare class ConfigManager {
|
|
3
|
+
private baseDir;
|
|
4
|
+
private configDir;
|
|
5
|
+
constructor(homeDir: string);
|
|
6
|
+
initialize(backendUrl: string): Promise<ClaudefyConfig>;
|
|
7
|
+
load(): Promise<ClaudefyConfig>;
|
|
8
|
+
set(key: string, value: unknown): Promise<void>;
|
|
9
|
+
addLink(alias: string, localPath: string, meta: {
|
|
10
|
+
canonicalId: string;
|
|
11
|
+
gitRemote: string | null;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
removeLink(alias: string): Promise<void>;
|
|
14
|
+
getLinks(): Promise<LinksConfig>;
|
|
15
|
+
getSyncFilter(): Promise<SyncFilterConfig>;
|
|
16
|
+
setFilterOverride(name: string, tier: "allow" | "deny"): Promise<void>;
|
|
17
|
+
isInitialized(): boolean;
|
|
18
|
+
getConfigDir(): string;
|
|
19
|
+
private saveConfig;
|
|
20
|
+
private saveLinks;
|
|
21
|
+
private saveSyncFilter;
|
|
22
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { hostname } from "node:os";
|
|
6
|
+
import { CLAUDEFY_DIR, CONFIG_FILE, LINKS_FILE, SYNC_FILTER_FILE, MACHINE_ID_FILE, DEFAULT_SYNC_FILTER, } from "./defaults.js";
|
|
7
|
+
export class ConfigManager {
|
|
8
|
+
baseDir;
|
|
9
|
+
configDir;
|
|
10
|
+
constructor(homeDir) {
|
|
11
|
+
this.baseDir = homeDir;
|
|
12
|
+
this.configDir = join(homeDir, CLAUDEFY_DIR);
|
|
13
|
+
}
|
|
14
|
+
async initialize(backendUrl) {
|
|
15
|
+
if (this.isInitialized()) {
|
|
16
|
+
throw new Error("claudefy is already initialized. Use 'load()' to read existing config.");
|
|
17
|
+
}
|
|
18
|
+
await mkdir(this.configDir, { recursive: true });
|
|
19
|
+
await mkdir(join(this.configDir, "backups"), { recursive: true });
|
|
20
|
+
const machineId = `${hostname()}-${randomUUID().slice(0, 8)}`.toLowerCase();
|
|
21
|
+
await writeFile(join(this.configDir, MACHINE_ID_FILE), machineId);
|
|
22
|
+
const config = {
|
|
23
|
+
version: 1,
|
|
24
|
+
backend: { type: "git", url: backendUrl },
|
|
25
|
+
encryption: {
|
|
26
|
+
enabled: true,
|
|
27
|
+
useKeychain: false,
|
|
28
|
+
cacheDuration: "0",
|
|
29
|
+
},
|
|
30
|
+
machineId,
|
|
31
|
+
};
|
|
32
|
+
await this.saveConfig(config);
|
|
33
|
+
await this.saveLinks({});
|
|
34
|
+
await this.saveSyncFilter({ ...DEFAULT_SYNC_FILTER });
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
async load() {
|
|
38
|
+
const raw = await readFile(join(this.configDir, CONFIG_FILE), "utf-8");
|
|
39
|
+
return JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
async set(key, value) {
|
|
42
|
+
const FORBIDDEN_KEYS = ["__proto__", "prototype", "constructor"];
|
|
43
|
+
const parts = key.split(".");
|
|
44
|
+
for (const part of parts) {
|
|
45
|
+
if (FORBIDDEN_KEYS.includes(part)) {
|
|
46
|
+
throw new Error(`Forbidden config key segment: "${part}"`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const config = await this.load();
|
|
50
|
+
let obj = config;
|
|
51
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
52
|
+
const next = obj[parts[i]];
|
|
53
|
+
if (next === undefined || next === null || typeof next !== "object") {
|
|
54
|
+
throw new Error(`Invalid config key: "${key}" — "${parts[i]}" is not an object`);
|
|
55
|
+
}
|
|
56
|
+
obj = next;
|
|
57
|
+
}
|
|
58
|
+
obj[parts[parts.length - 1]] = value;
|
|
59
|
+
await this.saveConfig(config);
|
|
60
|
+
}
|
|
61
|
+
async addLink(alias, localPath, meta) {
|
|
62
|
+
const links = await this.getLinks();
|
|
63
|
+
links[alias] = {
|
|
64
|
+
localPath,
|
|
65
|
+
canonicalId: meta.canonicalId,
|
|
66
|
+
gitRemote: meta.gitRemote,
|
|
67
|
+
detectedAt: new Date().toISOString(),
|
|
68
|
+
};
|
|
69
|
+
await this.saveLinks(links);
|
|
70
|
+
}
|
|
71
|
+
async removeLink(alias) {
|
|
72
|
+
const links = await this.getLinks();
|
|
73
|
+
delete links[alias];
|
|
74
|
+
await this.saveLinks(links);
|
|
75
|
+
}
|
|
76
|
+
async getLinks() {
|
|
77
|
+
const path = join(this.configDir, LINKS_FILE);
|
|
78
|
+
if (!existsSync(path))
|
|
79
|
+
return {};
|
|
80
|
+
const raw = await readFile(path, "utf-8");
|
|
81
|
+
return JSON.parse(raw);
|
|
82
|
+
}
|
|
83
|
+
async getSyncFilter() {
|
|
84
|
+
const path = join(this.configDir, SYNC_FILTER_FILE);
|
|
85
|
+
if (!existsSync(path))
|
|
86
|
+
return { ...DEFAULT_SYNC_FILTER };
|
|
87
|
+
const raw = await readFile(path, "utf-8");
|
|
88
|
+
return JSON.parse(raw);
|
|
89
|
+
}
|
|
90
|
+
async setFilterOverride(name, tier) {
|
|
91
|
+
const filter = await this.getSyncFilter();
|
|
92
|
+
filter.denylist = filter.denylist.filter((d) => d !== name);
|
|
93
|
+
filter.allowlist = filter.allowlist.filter((a) => a !== name);
|
|
94
|
+
if (tier === "allow") {
|
|
95
|
+
filter.allowlist.push(name);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
filter.denylist.push(name);
|
|
99
|
+
}
|
|
100
|
+
await this.saveSyncFilter(filter);
|
|
101
|
+
}
|
|
102
|
+
isInitialized() {
|
|
103
|
+
return existsSync(join(this.configDir, CONFIG_FILE));
|
|
104
|
+
}
|
|
105
|
+
getConfigDir() {
|
|
106
|
+
return this.configDir;
|
|
107
|
+
}
|
|
108
|
+
async saveConfig(config) {
|
|
109
|
+
await writeFile(join(this.configDir, CONFIG_FILE), JSON.stringify(config, null, 2));
|
|
110
|
+
}
|
|
111
|
+
async saveLinks(links) {
|
|
112
|
+
await writeFile(join(this.configDir, LINKS_FILE), JSON.stringify(links, null, 2));
|
|
113
|
+
}
|
|
114
|
+
async saveSyncFilter(filter) {
|
|
115
|
+
await writeFile(join(this.configDir, SYNC_FILTER_FILE), JSON.stringify(filter, null, 2));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=config-manager.js.map
|