@alfe.ai/openclaw-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/README.md +60 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +263 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/ignore.js +73 -0
- package/dist/ignore.js.map +1 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +469 -0
- package/dist/index.js.map +1 -0
- package/dist/sync-engine.js +672 -0
- package/dist/sync-engine.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# packages/openclaw-sync (`@alfe.ai/openclaw-sync`)
|
|
2
|
+
|
|
3
|
+
AlfeSync — agent workspace backup and sync skill for OpenClaw.
|
|
4
|
+
|
|
5
|
+
Syncs an agent's local workspace (config, conversations, memory) to/from S3 with hash-based change detection, conflict resolution, and flexible scheduling. Works as both an OpenClaw plugin and a standalone CLI.
|
|
6
|
+
|
|
7
|
+
## Key Files
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/
|
|
11
|
+
├── index.ts # Package exports
|
|
12
|
+
├── config.ts # .alfesync/config.json management
|
|
13
|
+
├── manifest.ts # Local/remote manifest + hash-based diff engine
|
|
14
|
+
├── ignore.ts # .alfesyncignore parsing (micromatch)
|
|
15
|
+
├── api-client.ts # Typed HTTP client (presigned URLs, manifest, stats)
|
|
16
|
+
├── uploader.ts # S3 upload via presigned URLs with retries
|
|
17
|
+
├── downloader.ts # S3 download via presigned URLs with retries
|
|
18
|
+
├── watcher.ts # Chokidar file watcher with debounce
|
|
19
|
+
├── sync-engine.ts # Orchestration (push/pull/fullSync)
|
|
20
|
+
├── plugin.ts # OpenClaw plugin lifecycle + gateway IPC
|
|
21
|
+
└── cli/
|
|
22
|
+
└── index.ts # Commander-based CLI (alfesync)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## OpenClaw Plugin
|
|
26
|
+
|
|
27
|
+
Follows the standard plugin pattern — exports `id`, `name`, `activate`, `deactivate`, `configure`. On activation:
|
|
28
|
+
|
|
29
|
+
- Initializes the sync engine for the workspace
|
|
30
|
+
- Starts a file watcher (realtime mode) or scheduled interval (hourly/daily/weekly)
|
|
31
|
+
- Connects to the Alfe daemon IPC for capability registration (`sync.push`, `sync.pull`, `sync.fullSync`)
|
|
32
|
+
- Registers `sync.now` and `sync.status` gateway RPC methods
|
|
33
|
+
- Gracefully degrades if the daemon is unavailable
|
|
34
|
+
|
|
35
|
+
Lifecycle hooks (`openclaw.plugin.json`):
|
|
36
|
+
|
|
37
|
+
- **onAgentStart** — `alfesync pull --quiet`
|
|
38
|
+
- **onAgentStop** — `alfesync push --quiet`
|
|
39
|
+
- **onCompaction** — `alfesync push --quiet --filter=context/`
|
|
40
|
+
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm install
|
|
45
|
+
pnpm --filter @alfe.ai/openclaw-sync build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Testing
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm --filter @alfe.ai/openclaw-sync test
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Dependencies
|
|
55
|
+
|
|
56
|
+
- **chokidar** — file watching
|
|
57
|
+
- **commander** — CLI framework
|
|
58
|
+
- **micromatch** — glob pattern matching
|
|
59
|
+
|
|
60
|
+
See [SKILL.md](./SKILL.md) for full CLI usage, storage classes, conflict resolution, and programmatic API docs.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { g as writeConfig, i as createApiClient, m as readConfig, o as diffManifests, p as isInitialized, s as readManifest, t as createSyncEngine } from "../sync-engine.js";
|
|
3
|
+
import { readdir } from "node:fs/promises";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
//#region src/cli/index.ts
|
|
7
|
+
/**
|
|
8
|
+
* AlfeSync CLI — alfesync command-line interface.
|
|
9
|
+
*
|
|
10
|
+
* Commands:
|
|
11
|
+
* init - Initialize AlfeSync in a workspace
|
|
12
|
+
* push - Push local changes to remote
|
|
13
|
+
* pull - Pull remote changes to local
|
|
14
|
+
* status - Show sync status and pending changes
|
|
15
|
+
* conflicts - List conflict files
|
|
16
|
+
* history - Show version history for a file
|
|
17
|
+
* restore - Restore agent workspace from remote
|
|
18
|
+
*/
|
|
19
|
+
const program = new Command();
|
|
20
|
+
program.name("alfesync").description("AlfeSync — agent workspace backup and sync").version("1.0.0");
|
|
21
|
+
program.command("init").description("Initialize AlfeSync in the current workspace").option("-t, --token <token>", "Alfe Connect bearer token").option("-u, --api-url <url>", "AlfeSync API URL", "https://api.alfe.ai").option("-a, --agent-id <id>", "Agent ID for this workspace").option("-n, --display-name <name>", "Display name for the agent").action(async (opts) => {
|
|
22
|
+
const workspacePath = resolve(".");
|
|
23
|
+
if (isInitialized(workspacePath)) {
|
|
24
|
+
console.log("AlfeSync is already initialized in this workspace.");
|
|
25
|
+
const existing = await readConfig(workspacePath);
|
|
26
|
+
if (existing) {
|
|
27
|
+
console.log(` Agent: ${existing.agentId}`);
|
|
28
|
+
console.log(` API: ${existing.apiUrl}`);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const token = opts.token ?? process.env.ALFESYNC_TOKEN;
|
|
33
|
+
const apiUrl = opts.apiUrl ?? process.env.ALFESYNC_API_URL ?? "https://api.alfe.ai";
|
|
34
|
+
const agentId = opts.agentId ?? process.env.ALFESYNC_AGENT_ID;
|
|
35
|
+
if (!token) {
|
|
36
|
+
console.error("Error: --token is required (or set ALFESYNC_TOKEN env var)");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (!agentId) {
|
|
40
|
+
console.error("Error: --agent-id is required (or set ALFESYNC_AGENT_ID env var)");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
console.log("Registering agent with AlfeSync...");
|
|
44
|
+
const client = createApiClient({
|
|
45
|
+
apiUrl,
|
|
46
|
+
token,
|
|
47
|
+
agentId
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
const orgId = (await client.registerAgent(opts.displayName ?? agentId)).agent.orgId;
|
|
51
|
+
await writeConfig({
|
|
52
|
+
agentId,
|
|
53
|
+
orgId,
|
|
54
|
+
token,
|
|
55
|
+
workspacePath,
|
|
56
|
+
apiUrl
|
|
57
|
+
});
|
|
58
|
+
console.log("✓ AlfeSync initialized!");
|
|
59
|
+
console.log(` Agent: ${agentId}`);
|
|
60
|
+
console.log(` Org: ${orgId}`);
|
|
61
|
+
console.log(` API: ${apiUrl}`);
|
|
62
|
+
console.log(` Workspace: ${workspacePath}`);
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log("Run 'alfesync push' to sync your workspace.");
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error("Failed to register agent:", err instanceof Error ? err.message : String(err));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
program.command("push").description("Push local changes to remote").option("-q, --quiet", "Suppress output").option("-f, --filter <prefix>", "Only push files matching this prefix").action(async (opts) => {
|
|
71
|
+
const workspacePath = resolve(".");
|
|
72
|
+
try {
|
|
73
|
+
await (await createSyncEngine(workspacePath)).push(void 0, {
|
|
74
|
+
quiet: opts.quiet,
|
|
75
|
+
filter: opts.filter
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
program.command("pull").description("Pull remote changes to local").option("-q, --quiet", "Suppress output").action(async (opts) => {
|
|
83
|
+
const workspacePath = resolve(".");
|
|
84
|
+
try {
|
|
85
|
+
await (await createSyncEngine(workspacePath)).pull({ quiet: opts.quiet });
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
program.command("status").description("Show sync status and pending changes").action(async () => {
|
|
92
|
+
const workspacePath = resolve(".");
|
|
93
|
+
try {
|
|
94
|
+
const config = await readConfig(workspacePath);
|
|
95
|
+
if (!config) {
|
|
96
|
+
console.log("AlfeSync not initialized. Run: alfesync init");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const client = createApiClient({
|
|
100
|
+
apiUrl: config.apiUrl,
|
|
101
|
+
token: config.token,
|
|
102
|
+
agentId: config.agentId
|
|
103
|
+
});
|
|
104
|
+
const [localManifest, remoteManifest, stats] = await Promise.all([
|
|
105
|
+
readManifest(workspacePath),
|
|
106
|
+
client.getManifest(),
|
|
107
|
+
client.getStats()
|
|
108
|
+
]);
|
|
109
|
+
const diff = diffManifests(localManifest, remoteManifest);
|
|
110
|
+
console.log("AlfeSync Status");
|
|
111
|
+
console.log("═══════════════");
|
|
112
|
+
console.log(`Agent: ${config.agentId}`);
|
|
113
|
+
console.log(`Workspace: ${workspacePath}`);
|
|
114
|
+
console.log(`API: ${config.apiUrl}`);
|
|
115
|
+
console.log("");
|
|
116
|
+
console.log("Storage:");
|
|
117
|
+
console.log(` Standard: ${formatBytes(stats.standardBytes)}`);
|
|
118
|
+
console.log(` Glacier IR: ${formatBytes(stats.glacierBytes)}`);
|
|
119
|
+
console.log(` Total files: ${String(stats.fileCount)}`);
|
|
120
|
+
console.log(` Last sync: ${stats.lastSyncAt ?? "never"}`);
|
|
121
|
+
console.log("");
|
|
122
|
+
console.log("Pending Changes:");
|
|
123
|
+
console.log(` To push: ${String(diff.toPush.length)}`);
|
|
124
|
+
console.log(` To pull: ${String(diff.toPull.length)}`);
|
|
125
|
+
console.log(` Conflicts: ${String(diff.conflicts.length)}`);
|
|
126
|
+
if (diff.toPush.length > 0) {
|
|
127
|
+
console.log("");
|
|
128
|
+
console.log("Files to push:");
|
|
129
|
+
for (const p of diff.toPush.slice(0, 20)) console.log(` ↑ ${p}`);
|
|
130
|
+
if (diff.toPush.length > 20) console.log(` ... and ${String(diff.toPush.length - 20)} more`);
|
|
131
|
+
}
|
|
132
|
+
if (diff.toPull.length > 0) {
|
|
133
|
+
console.log("");
|
|
134
|
+
console.log("Files to pull:");
|
|
135
|
+
for (const p of diff.toPull.slice(0, 20)) console.log(` ↓ ${p}`);
|
|
136
|
+
if (diff.toPull.length > 20) console.log(` ... and ${String(diff.toPull.length - 20)} more`);
|
|
137
|
+
}
|
|
138
|
+
if (diff.conflicts.length > 0) {
|
|
139
|
+
console.log("");
|
|
140
|
+
console.log("Conflicts:");
|
|
141
|
+
for (const p of diff.conflicts) console.log(` ⚡ ${p}`);
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
program.command("conflicts").description("List conflict files in the workspace").action(async () => {
|
|
149
|
+
const workspacePath = resolve(".");
|
|
150
|
+
try {
|
|
151
|
+
const conflicts = await findConflictFiles(workspacePath);
|
|
152
|
+
if (conflicts.length === 0) {
|
|
153
|
+
console.log("No conflict files found.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(`Found ${String(conflicts.length)} conflict file(s):`);
|
|
157
|
+
for (const f of conflicts) console.log(` ⚡ ${f}`);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
program.command("history <file>").description("Show version history for a file").action(async (file) => {
|
|
164
|
+
const workspacePath = resolve(".");
|
|
165
|
+
try {
|
|
166
|
+
const config = await readConfig(workspacePath);
|
|
167
|
+
if (!config) {
|
|
168
|
+
console.log("AlfeSync not initialized. Run: alfesync init");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const versions = await createApiClient({
|
|
172
|
+
apiUrl: config.apiUrl,
|
|
173
|
+
token: config.token,
|
|
174
|
+
agentId: config.agentId
|
|
175
|
+
}).getFileHistory(file);
|
|
176
|
+
if (versions.length === 0) {
|
|
177
|
+
console.log(`No history found for: ${file}`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
console.log(`Version history for: ${file}`);
|
|
181
|
+
console.log("─".repeat(60));
|
|
182
|
+
for (const v of versions) {
|
|
183
|
+
const marker = v.isLatest ? " (latest)" : "";
|
|
184
|
+
console.log(` ${v.lastModified} ${formatBytes(v.size)} ${v.versionId.slice(0, 12)}${marker}`);
|
|
185
|
+
}
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
program.command("restore").description("Restore agent workspace from remote backup").option("-m, --mode <mode>", "Restore mode: full, active, memory", "full").option("-q, --quiet", "Suppress output").action(async (opts) => {
|
|
192
|
+
const workspacePath = resolve(".");
|
|
193
|
+
try {
|
|
194
|
+
const config = await readConfig(workspacePath);
|
|
195
|
+
if (!config) {
|
|
196
|
+
console.log("AlfeSync not initialized. Run: alfesync init");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const mode = opts.mode;
|
|
200
|
+
if (![
|
|
201
|
+
"full",
|
|
202
|
+
"active",
|
|
203
|
+
"memory"
|
|
204
|
+
].includes(mode)) {
|
|
205
|
+
console.error("Error: mode must be full, active, or memory");
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
const client = createApiClient({
|
|
209
|
+
apiUrl: config.apiUrl,
|
|
210
|
+
token: config.token,
|
|
211
|
+
agentId: config.agentId
|
|
212
|
+
});
|
|
213
|
+
if (!opts.quiet) console.log(`Restoring workspace (mode: ${mode})...`);
|
|
214
|
+
const bundle = await client.reconstruct(mode);
|
|
215
|
+
if (!opts.quiet) console.log(`Downloading ${String(bundle.fileCount)} files (${formatBytes(bundle.totalSize)})...`);
|
|
216
|
+
const { writeFile, mkdir } = await import("node:fs/promises");
|
|
217
|
+
const { dirname } = await import("node:path");
|
|
218
|
+
let downloaded = 0;
|
|
219
|
+
let errors = 0;
|
|
220
|
+
for (const file of bundle.files) try {
|
|
221
|
+
const response = await fetch(file.url);
|
|
222
|
+
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
223
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
224
|
+
const absolutePath = join(workspacePath, file.path);
|
|
225
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
226
|
+
await writeFile(absolutePath, buffer);
|
|
227
|
+
downloaded++;
|
|
228
|
+
if (!opts.quiet) console.log(` ↓ ${file.path} (${formatBytes(file.size)})`);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
errors++;
|
|
231
|
+
if (!opts.quiet) console.error(` ✗ ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
232
|
+
}
|
|
233
|
+
if (!opts.quiet) console.log(`\nRestore complete: ${String(downloaded)} files downloaded, ${String(errors)} errors.`);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
async function findConflictFiles(dir, base) {
|
|
240
|
+
const results = [];
|
|
241
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".alfesync") continue;
|
|
244
|
+
const fullPath = join(dir, entry.name);
|
|
245
|
+
const relativePath = base ? join(base, entry.name) : entry.name;
|
|
246
|
+
if (entry.isDirectory()) {
|
|
247
|
+
const sub = await findConflictFiles(fullPath, relativePath);
|
|
248
|
+
results.push(...sub);
|
|
249
|
+
} else if (entry.name.includes(".conflict-")) results.push(relativePath);
|
|
250
|
+
}
|
|
251
|
+
return results;
|
|
252
|
+
}
|
|
253
|
+
function formatBytes(bytes) {
|
|
254
|
+
if (bytes < 1024) return `${String(bytes)} B`;
|
|
255
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
256
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
257
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
258
|
+
}
|
|
259
|
+
program.parse();
|
|
260
|
+
//#endregion
|
|
261
|
+
export {};
|
|
262
|
+
|
|
263
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * AlfeSync CLI — alfesync command-line interface.\n *\n * Commands:\n * init - Initialize AlfeSync in a workspace\n * push - Push local changes to remote\n * pull - Pull remote changes to local\n * status - Show sync status and pending changes\n * conflicts - List conflict files\n * history - Show version history for a file\n * restore - Restore agent workspace from remote\n */\n\nimport { Command } from \"commander\";\nimport { resolve, join } from \"node:path\";\nimport { readdir } from \"node:fs/promises\";\nimport {\n readConfig,\n writeConfig,\n isInitialized,\n} from \"../config.js\";\nimport { createApiClient } from \"../api-client.js\";\nimport { createSyncEngine } from \"../sync-engine.js\";\nimport { readManifest, diffManifests } from \"../manifest.js\";\n\n\nconst program = new Command();\n\nprogram\n .name(\"alfesync\")\n .description(\"AlfeSync — agent workspace backup and sync\")\n .version(\"1.0.0\");\n\n// ─── init ─────────────────────────────────────────────────\n\nprogram\n .command(\"init\")\n .description(\"Initialize AlfeSync in the current workspace\")\n .option(\"-t, --token <token>\", \"Alfe Connect bearer token\")\n .option(\"-u, --api-url <url>\", \"AlfeSync API URL\", \"https://api.alfe.ai\")\n .option(\"-a, --agent-id <id>\", \"Agent ID for this workspace\")\n .option(\"-n, --display-name <name>\", \"Display name for the agent\")\n .action(async (opts: { token?: string; apiUrl?: string; agentId?: string; displayName?: string }) => {\n const workspacePath = resolve(\".\");\n\n if (isInitialized(workspacePath)) {\n console.log(\"AlfeSync is already initialized in this workspace.\");\n const existing = await readConfig(workspacePath);\n if (existing) {\n console.log(` Agent: ${existing.agentId}`);\n console.log(` API: ${existing.apiUrl}`);\n }\n return;\n }\n\n const token = opts.token ?? process.env.ALFESYNC_TOKEN;\n const apiUrl = opts.apiUrl ?? process.env.ALFESYNC_API_URL ?? \"https://api.alfe.ai\";\n const agentId = opts.agentId ?? process.env.ALFESYNC_AGENT_ID;\n\n if (!token) {\n console.error(\n \"Error: --token is required (or set ALFESYNC_TOKEN env var)\",\n );\n process.exit(1);\n }\n if (!agentId) {\n console.error(\n \"Error: --agent-id is required (or set ALFESYNC_AGENT_ID env var)\",\n );\n process.exit(1);\n }\n\n // Register agent with the API\n console.log(\"Registering agent with AlfeSync...\");\n const client = createApiClient({ apiUrl, token, agentId });\n\n try {\n const result = await client.registerAgent(opts.displayName ?? agentId);\n const orgId = result.agent.orgId;\n\n await writeConfig({\n agentId,\n orgId,\n token,\n workspacePath,\n apiUrl,\n });\n\n console.log(\"✓ AlfeSync initialized!\");\n console.log(` Agent: ${agentId}`);\n console.log(` Org: ${orgId}`);\n console.log(` API: ${apiUrl}`);\n console.log(` Workspace: ${workspacePath}`);\n console.log(\"\");\n console.log(\"Run 'alfesync push' to sync your workspace.\");\n } catch (err) {\n console.error(\n \"Failed to register agent:\",\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── push ─────────────────────────────────────────────────\n\nprogram\n .command(\"push\")\n .description(\"Push local changes to remote\")\n .option(\"-q, --quiet\", \"Suppress output\")\n .option(\"-f, --filter <prefix>\", \"Only push files matching this prefix\")\n .action(async (opts: { quiet?: boolean; filter?: string }) => {\n const workspacePath = resolve(\".\");\n\n try {\n const engine = await createSyncEngine(workspacePath);\n await engine.push(undefined, {\n quiet: opts.quiet,\n filter: opts.filter,\n });\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── pull ─────────────────────────────────────────────────\n\nprogram\n .command(\"pull\")\n .description(\"Pull remote changes to local\")\n .option(\"-q, --quiet\", \"Suppress output\")\n .action(async (opts: { quiet?: boolean }) => {\n const workspacePath = resolve(\".\");\n\n try {\n const engine = await createSyncEngine(workspacePath);\n await engine.pull({ quiet: opts.quiet });\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── status ───────────────────────────────────────────────\n\nprogram\n .command(\"status\")\n .description(\"Show sync status and pending changes\")\n .action(async () => {\n const workspacePath = resolve(\".\");\n\n try {\n const config = await readConfig(workspacePath);\n if (!config) {\n console.log(\"AlfeSync not initialized. Run: alfesync init\");\n return;\n }\n\n const client = createApiClient({\n apiUrl: config.apiUrl,\n token: config.token,\n agentId: config.agentId,\n });\n\n const [localManifest, remoteManifest, stats] = await Promise.all([\n readManifest(workspacePath),\n client.getManifest(),\n client.getStats(),\n ]);\n\n const diff = diffManifests(localManifest, remoteManifest);\n\n console.log(\"AlfeSync Status\");\n console.log(\"═══════════════\");\n console.log(`Agent: ${config.agentId}`);\n console.log(`Workspace: ${workspacePath}`);\n console.log(`API: ${config.apiUrl}`);\n console.log(\"\");\n\n console.log(\"Storage:\");\n console.log(\n ` Standard: ${formatBytes(stats.standardBytes)}`,\n );\n console.log(\n ` Glacier IR: ${formatBytes(stats.glacierBytes)}`,\n );\n console.log(` Total files: ${String(stats.fileCount)}`);\n console.log(\n ` Last sync: ${stats.lastSyncAt ?? \"never\"}`,\n );\n console.log(\"\");\n\n console.log(\"Pending Changes:\");\n console.log(` To push: ${String(diff.toPush.length)}`);\n console.log(` To pull: ${String(diff.toPull.length)}`);\n console.log(` Conflicts: ${String(diff.conflicts.length)}`);\n\n if (diff.toPush.length > 0) {\n console.log(\"\");\n console.log(\"Files to push:\");\n for (const p of diff.toPush.slice(0, 20)) {\n console.log(` ↑ ${p}`);\n }\n if (diff.toPush.length > 20) {\n console.log(` ... and ${String(diff.toPush.length - 20)} more`);\n }\n }\n\n if (diff.toPull.length > 0) {\n console.log(\"\");\n console.log(\"Files to pull:\");\n for (const p of diff.toPull.slice(0, 20)) {\n console.log(` ↓ ${p}`);\n }\n if (diff.toPull.length > 20) {\n console.log(` ... and ${String(diff.toPull.length - 20)} more`);\n }\n }\n\n if (diff.conflicts.length > 0) {\n console.log(\"\");\n console.log(\"Conflicts:\");\n for (const p of diff.conflicts) {\n console.log(` ⚡ ${p}`);\n }\n }\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── conflicts ────────────────────────────────────────────\n\nprogram\n .command(\"conflicts\")\n .description(\"List conflict files in the workspace\")\n .action(async () => {\n const workspacePath = resolve(\".\");\n\n try {\n const conflicts = await findConflictFiles(workspacePath);\n if (conflicts.length === 0) {\n console.log(\"No conflict files found.\");\n return;\n }\n\n console.log(`Found ${String(conflicts.length)} conflict file(s):`);\n for (const f of conflicts) {\n console.log(` ⚡ ${f}`);\n }\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── history ──────────────────────────────────────────────\n\nprogram\n .command(\"history <file>\")\n .description(\"Show version history for a file\")\n .action(async (file: string) => {\n const workspacePath = resolve(\".\");\n\n try {\n const config = await readConfig(workspacePath);\n if (!config) {\n console.log(\"AlfeSync not initialized. Run: alfesync init\");\n return;\n }\n\n const client = createApiClient({\n apiUrl: config.apiUrl,\n token: config.token,\n agentId: config.agentId,\n });\n\n const versions = await client.getFileHistory(file);\n if (versions.length === 0) {\n console.log(`No history found for: ${file}`);\n return;\n }\n\n console.log(`Version history for: ${file}`);\n console.log(\"─\".repeat(60));\n for (const v of versions) {\n const marker = v.isLatest ? \" (latest)\" : \"\";\n console.log(\n ` ${v.lastModified} ${formatBytes(v.size)} ${v.versionId.slice(0, 12)}${marker}`,\n );\n }\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── restore ──────────────────────────────────────────────\n\nprogram\n .command(\"restore\")\n .description(\"Restore agent workspace from remote backup\")\n .option(\n \"-m, --mode <mode>\",\n \"Restore mode: full, active, memory\",\n \"full\",\n )\n .option(\"-q, --quiet\", \"Suppress output\")\n .action(async (opts: { mode?: string; quiet?: boolean }) => {\n const workspacePath = resolve(\".\");\n\n try {\n const config = await readConfig(workspacePath);\n if (!config) {\n console.log(\"AlfeSync not initialized. Run: alfesync init\");\n return;\n }\n\n const mode = opts.mode as \"full\" | \"active\" | \"memory\";\n if (![\"full\", \"active\", \"memory\"].includes(mode)) {\n console.error(\"Error: mode must be full, active, or memory\");\n process.exit(1);\n }\n\n const client = createApiClient({\n apiUrl: config.apiUrl,\n token: config.token,\n agentId: config.agentId,\n });\n\n if (!opts.quiet) {\n console.log(`Restoring workspace (mode: ${mode})...`);\n }\n\n const bundle = await client.reconstruct(mode);\n\n if (!opts.quiet) {\n console.log(\n `Downloading ${String(bundle.fileCount)} files (${formatBytes(bundle.totalSize)})...`,\n );\n }\n\n // Download each file from the bundle's presigned URLs\n const { writeFile, mkdir } = await import(\"node:fs/promises\");\n const { dirname } = await import(\"node:path\");\n\n let downloaded = 0;\n let errors = 0;\n\n for (const file of bundle.files) {\n try {\n const response = await fetch(file.url);\n if (!response.ok) {\n throw new Error(`HTTP ${String(response.status)}`);\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n const absolutePath = join(workspacePath, file.path);\n await mkdir(dirname(absolutePath), { recursive: true });\n await writeFile(absolutePath, buffer);\n\n downloaded++;\n if (!opts.quiet) {\n console.log(\n ` ↓ ${file.path} (${formatBytes(file.size)})`,\n );\n }\n } catch (err) {\n errors++;\n if (!opts.quiet) {\n console.error(\n ` ✗ ${file.path}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n if (!opts.quiet) {\n console.log(\n `\\nRestore complete: ${String(downloaded)} files downloaded, ${String(errors)} errors.`,\n );\n }\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : String(err),\n );\n process.exit(1);\n }\n });\n\n// ─── Helpers ──────────────────────────────────────────────\n\nasync function findConflictFiles(\n dir: string,\n base?: string,\n): Promise<string[]> {\n const results: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (\n entry.name === \"node_modules\" ||\n entry.name === \".git\" ||\n entry.name === \".alfesync\"\n ) {\n continue;\n }\n\n const fullPath = join(dir, entry.name);\n const relativePath = base\n ? join(base, entry.name)\n : entry.name;\n\n if (entry.isDirectory()) {\n const sub = await findConflictFiles(fullPath, relativePath);\n results.push(...sub);\n } else if (entry.name.includes(\".conflict-\")) {\n results.push(relativePath);\n }\n }\n\n return results;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${String(bytes)} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\n// ─── Run ──────────────────────────────────────────────────\n\nprogram.parse();\n"],"mappings":"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"}
|
package/dist/ignore.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import micromatch from "micromatch";
|
|
5
|
+
//#region \0rolldown/runtime.js
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __exportAll = (all, no_symbols) => {
|
|
8
|
+
let target = {};
|
|
9
|
+
for (var name in all) __defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true
|
|
12
|
+
});
|
|
13
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/ignore.ts
|
|
18
|
+
/**
|
|
19
|
+
* AlfeSync ignore — parse `.alfesyncignore` (gitignore-style glob matching).
|
|
20
|
+
*
|
|
21
|
+
* Uses micromatch for glob pattern matching, compatible with .gitignore syntax.
|
|
22
|
+
*/
|
|
23
|
+
var ignore_exports = /* @__PURE__ */ __exportAll({
|
|
24
|
+
filterIgnored: () => filterIgnored,
|
|
25
|
+
loadIgnorePatterns: () => loadIgnorePatterns,
|
|
26
|
+
shouldIgnore: () => shouldIgnore
|
|
27
|
+
});
|
|
28
|
+
/** Default ignore patterns — always applied */
|
|
29
|
+
const DEFAULT_IGNORES = [
|
|
30
|
+
".alfesync/**",
|
|
31
|
+
"node_modules/**",
|
|
32
|
+
"*.tmp",
|
|
33
|
+
".DS_Store",
|
|
34
|
+
".git/**",
|
|
35
|
+
".sst/**",
|
|
36
|
+
".build/**",
|
|
37
|
+
"dist/**"
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Load ignore patterns from `.alfesyncignore` file + defaults.
|
|
41
|
+
*/
|
|
42
|
+
async function loadIgnorePatterns(workspacePath) {
|
|
43
|
+
const ignoreFile = join(workspacePath, ".alfesyncignore");
|
|
44
|
+
const patterns = [...DEFAULT_IGNORES];
|
|
45
|
+
if (existsSync(ignoreFile)) try {
|
|
46
|
+
const lines = (await readFile(ignoreFile, "utf-8")).split("\n");
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
50
|
+
patterns.push(trimmed);
|
|
51
|
+
}
|
|
52
|
+
} catch {}
|
|
53
|
+
return patterns;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a relative path should be ignored.
|
|
57
|
+
*/
|
|
58
|
+
function shouldIgnore(relativePath, patterns) {
|
|
59
|
+
return micromatch.isMatch(relativePath, patterns, {
|
|
60
|
+
dot: true,
|
|
61
|
+
matchBase: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Filter a list of relative paths, removing ignored ones.
|
|
66
|
+
*/
|
|
67
|
+
function filterIgnored(paths, patterns) {
|
|
68
|
+
return paths.filter((p) => !shouldIgnore(p, patterns));
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { shouldIgnore as i, ignore_exports as n, loadIgnorePatterns as r, filterIgnored as t };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=ignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.js","names":[],"sources":["../src/ignore.ts"],"sourcesContent":["/**\n * AlfeSync ignore — parse `.alfesyncignore` (gitignore-style glob matching).\n *\n * Uses micromatch for glob pattern matching, compatible with .gitignore syntax.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport micromatch from \"micromatch\";\n\n/** Default ignore patterns — always applied */\nconst DEFAULT_IGNORES = [\n \".alfesync/**\",\n \"node_modules/**\",\n \"*.tmp\",\n \".DS_Store\",\n \".git/**\",\n \".sst/**\",\n \".build/**\",\n \"dist/**\",\n];\n\n/**\n * Load ignore patterns from `.alfesyncignore` file + defaults.\n */\nexport async function loadIgnorePatterns(\n workspacePath: string,\n): Promise<string[]> {\n const ignoreFile = join(workspacePath, \".alfesyncignore\");\n const patterns = [...DEFAULT_IGNORES];\n\n if (existsSync(ignoreFile)) {\n try {\n const content = await readFile(ignoreFile, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n patterns.push(trimmed);\n }\n } catch {\n // Ignore read errors — use defaults only\n }\n }\n\n return patterns;\n}\n\n/**\n * Check if a relative path should be ignored.\n */\nexport function shouldIgnore(\n relativePath: string,\n patterns: string[],\n): boolean {\n return micromatch.isMatch(relativePath, patterns, {\n dot: true,\n matchBase: true,\n });\n}\n\n/**\n * Filter a list of relative paths, removing ignored ones.\n */\nexport function filterIgnored(\n paths: string[],\n patterns: string[],\n): string[] {\n return paths.filter((p) => !shouldIgnore(p, patterns));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,MAAM,kBAAkB;CACtB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,eAAsB,mBACpB,eACmB;CACnB,MAAM,aAAa,KAAK,eAAe,kBAAkB;CACzD,MAAM,WAAW,CAAC,GAAG,gBAAgB;AAErC,KAAI,WAAW,WAAW,CACxB,KAAI;EAEF,MAAM,SADU,MAAM,SAAS,YAAY,QAAQ,EAC7B,MAAM,KAAK;AAEjC,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,MAAM;AAE3B,OAAI,CAAC,WAAW,QAAQ,WAAW,IAAI,CAAE;AACzC,YAAS,KAAK,QAAQ;;SAElB;AAKV,QAAO;;;;;AAMT,SAAgB,aACd,cACA,UACS;AACT,QAAO,WAAW,QAAQ,cAAc,UAAU;EAChD,KAAK;EACL,WAAW;EACZ,CAAC;;;;;AAMJ,SAAgB,cACd,OACA,UACU;AACV,QAAO,MAAM,QAAQ,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC"}
|