@fizzyflow/wdoublesync_cli 1.0.2 → 1.0.5

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 CHANGED
@@ -1,11 +1,14 @@
1
1
  # wdoublesync cli
2
2
 
3
- CLI tool to sync local folders to [Walrus](https://walrus.xyz) decentralised storage on the Sui network.
3
+ CLI tool to sync local folders to Sui and [Walrus](https://walrus.xyz) decentralised storage on the Sui network.
4
4
 
5
5
  Built on top of the [`wdoublesync`](https://github.com/fizzyFlow/wdoublesync) library.
6
6
 
7
- Each `push` stores a versioned, gzip-compressed snapshot (or diff) inside an [EndlessVector](https://github.com/fizzyFlow/endless_vector) on-chain object. Any past version can be restored at any time with `pull`. Folders can optionally be encrypted with [Seal](https://github.com/MystenLabs/seal).
7
+ Each `push` stores a versioned, gzip-compressed diff or shapshot inside an [EndlessVector](https://github.com/fizzyFlow/endless_vector) on-chain object. Any past version can be restored at any time with `pull`. Folders can optionally be encrypted with [Seal](https://github.com/MystenLabs/seal).
8
8
 
9
+ ## AI Agent Skill
10
+
11
+ wdoublesync ships with a [SKILL.md](https://github.com/FizzyFlow/wdoublesync_cli/blob/main/SKILL.md) that teaches AI coding agents how to use it autonomously. Once loaded, an agent can push folders to on-chain storage, pull specific versions for inspection or rollback, watch a directory for bi-directional sync, and run rebate to compact a vector's archive — all without hand-holding on the commands. This is useful when you want an agent to persist its own working state, archive project snapshots, or coordinate versioned data across sessions. The skill also covers how to combine wdoublesync with [MemWal](https://github.com/MystenLabs/MemWal) for workflows.
9
12
 
10
13
  ## Installation
11
14
 
@@ -18,12 +21,15 @@ pnpm link --global
18
21
  ## Usage
19
22
 
20
23
  ```
21
- wdoublesync push [vector-id] [options] Sync current folder to a vector (creates one if no id given)
22
- wdoublesync pull <vector-id> [options] Restore vector contents to the current folder
23
- wdoublesync info <vector-id> [options] Show vector metadata and local sync status
24
- wdoublesync watch <vector-id> [options] Watch folder and auto-push on changes, auto-pull on remote updates
24
+ wdoublesync push [vector-id] [path] [options] Sync folder to a vector (creates one if no id given)
25
+ wdoublesync pull <vector-id> [path] [options] Restore vector contents to a folder
26
+ wdoublesync info [vector-id] [path] [options] Show chain/wallet info, or full vector metadata
27
+ wdoublesync watch <vector-id> [path] [options] Watch folder: auto-push on changes, auto-pull on remote updates
28
+ wdoublesync rebate <vector-id> [path] [options] Burn archive patches and push folder as a fresh snapshot
25
29
  ```
26
30
 
31
+ The `[path]` argument is optional — if omitted, the current working directory is used.
32
+
27
33
  ### Options
28
34
 
29
35
  | Flag | Description |
@@ -54,11 +60,15 @@ Supply a signing key in one of three ways (checked in order):
54
60
 
55
61
  ## Examples
56
62
 
57
- ### Push current folder (first time)
63
+ ### Push a folder (first time)
58
64
 
59
65
  ```bash
60
- cd my-project
66
+ # from current directory
61
67
  wdoublesync push --chain testnet --key suiprivkey1...
68
+
69
+ # or pass path explicitly
70
+ wdoublesync push ~/my-project --chain testnet --key suiprivkey1...
71
+
62
72
  # prints the new vector id, e.g.:
63
73
  # created: 0xabc123...
64
74
  # version 1 pushed (full snapshot, gzip compressed)
@@ -68,26 +78,32 @@ wdoublesync push --chain testnet --key suiprivkey1...
68
78
 
69
79
  ```bash
70
80
  wdoublesync push 0xabc123... --chain testnet --key suiprivkey1...
71
- # version 2 pushed (diff, gzip compressed)
81
+
82
+ # or with explicit path
83
+ wdoublesync push 0xabc123... ~/my-project --chain testnet --key suiprivkey1...
72
84
  ```
73
85
 
74
- ### Pull the latest version into the current folder
86
+ ### Pull the latest version
75
87
 
76
88
  ```bash
77
- mkdir restored && cd restored
78
- wdoublesync pull 0xabc123... --chain testnet
89
+ wdoublesync pull 0xabc123... ~/restored --chain testnet
79
90
  ```
80
91
 
81
92
  ### Pull a specific version
82
93
 
83
94
  ```bash
84
- wdoublesync pull 0xabc123... --chain testnet --version 1
95
+ wdoublesync pull 0xabc123... ~/restored --chain testnet --version 1
85
96
  ```
86
97
 
87
- ### Inspect a vector without touching the local folder
98
+ ### Inspect a vector
88
99
 
89
100
  ```bash
101
+ # check your wallet + chain (no vector ID needed)
102
+ wdoublesync info
103
+
104
+ # full vector metadata + local sync status
90
105
  wdoublesync info 0xabc123... --chain testnet
106
+ wdoublesync info 0xabc123... ~/my-project --chain testnet
91
107
  ```
92
108
 
93
109
  ### Fast incremental pushes with a manifest
@@ -101,6 +117,9 @@ wdoublesync push 0xabc123... --chain testnet --key suiprivkey1... --manifest
101
117
 
102
118
  ```bash
103
119
  wdoublesync watch 0xabc123... --chain testnet --key suiprivkey1...
120
+
121
+ # or with explicit path
122
+ wdoublesync watch 0xabc123... ~/my-project --chain testnet --key suiprivkey1...
104
123
  # pushes local changes 1 s after the last edit
105
124
  # pulls remote updates every 2 s
106
125
  # Ctrl-C to stop
@@ -119,6 +138,17 @@ wdoublesync watch 0xabc123... --push-only # no auto-pull
119
138
  wdoublesync watch 0xabc123... --pull-only # no auto-push (read-only mirror)
120
139
  ```
121
140
 
141
+ ### Rebate (compact archive)
142
+
143
+ After many incremental syncs, burn old archive patches and push the current folder as a fresh single snapshot. Reduces storage cost.
144
+
145
+ ```bash
146
+ wdoublesync rebate 0xabc123... --chain testnet --key suiprivkey1...
147
+
148
+ # or with explicit path
149
+ wdoublesync rebate 0xabc123... ~/my-project --chain testnet --key suiprivkey1...
150
+ ```
151
+
122
152
  ### Repair a corrupt vector with a force snapshot
123
153
 
124
154
  If a diff patch was pushed against a stale base (e.g. a race condition in `watch`), subsequent pulls will fail. Fix it by pushing a new full snapshot:
@@ -135,6 +165,7 @@ wdoublesync push 0xabc123... --chain testnet --key suiprivkey1... --force-snapsh
135
165
  2. **pull** — reads the requested version from the EndlessVector, decrypts it if Seal-encrypted, and writes only changed files to disk. Files absent from the stored version are deleted.
136
166
  3. **info** — reads EndlessVector metadata from the chain (version count, binary size, history) and checks whether the local folder matches any stored version.
137
167
  4. **watch** — combines push and pull in a loop. A filesystem watcher triggers a debounced push on local changes. A poll interval checks the remote vector for new versions and pulls them if found. Push and pull never run concurrently.
168
+ 5. **rebate** — burns all existing archive patches on-chain and pushes the current folder as a single fresh full snapshot, reducing Walrus storage costs after many incremental syncs.
138
169
 
139
170
  ### Default excludes
140
171
 
package/SKILL.md ADDED
@@ -0,0 +1,297 @@
1
+ ---
2
+ name: wdoublesync
3
+ version: 0.1.7
4
+ description: Claude agent skill for decentralized file storage, versioning, and sync on Sui + Walrus + Seal. Enables agents to push local folders as on-chain vectors, pull versions, manage Walrus storage, and watch for changes.
5
+ keywords: [walrus, sui, storage, versioning, sync, seal-encryption, blockchain, defi]
6
+ ---
7
+
8
+ # wdoublesync
9
+
10
+ Decentralized file storage, versioning, and sync on **Sui + Walrus + Seal**.
11
+
12
+ An abstract virtual filesystem that synchronizes folder state on-chain via Sui + Walrus using Content-Defined Chunking (CDC) to compute minimal binary diffs. Files can optionally be encrypted with Seal so only authorized addresses can decrypt them.
13
+
14
+ ## When to Use
15
+
16
+ - **Archive and restore folder snapshots** on-chain with full version history
17
+ - **Efficient incremental sync** — only changed chunks uploaded, not entire files
18
+ - **End-to-end encrypted storage** — data encrypted with Seal before Walrus upload
19
+ - **Verify data integrity** — pull any version and reconstruct exact folder state
20
+ - **Extend storage** before Walrus epochs expire without re-uploading data
21
+ - **Clean archive** with rebate workflow (burn old archive, push as fresh snapshot)
22
+
23
+ ## When NOT to Use
24
+
25
+ - For **semantic/vector search over text** — if you need to index and search text by meaning, use [MemWal](https://raw.githubusercontent.com/MystenLabs/MemWal/refs/heads/dev/SKILL.md) instead (vector database with embeddings)
26
+
27
+ ## MemWal + WDoubleSync
28
+
29
+ Feel free to combine MemWal and WDoubleSync in your agent workflows. For example, you can use MemWal to store and search text embeddings, and WDoubleSync to store the original files or folders that correspond to those embeddings. This allows you to have both semantic search capabilities and versioned file storage in one system.
30
+
31
+ ## Installation
32
+
33
+ Check if `wdoublesync` is already installed:
34
+
35
+ ```bash
36
+ wdoublesync --help
37
+ ```
38
+
39
+ if not, install via npm:
40
+
41
+ **Global npm install**
42
+ ```bash
43
+ npm install -g @fizzyflow/wdoublesync_cli
44
+ # Now use: wdoublesync <command> (installed from npm)
45
+ ```
46
+
47
+ After you have access to `wdoublesync`, you can run `wdoublesync --help` to see the available commands and options. Use `wdoublesync info` to check your currently connected wallet (`your wallet` field), if there's no - wallet, you can set one with `--key` or `--phrase` flags, or by exporting `WDOUBLESYNC_KEY` environment variable. Feel free to ask user to set up a wallet if they haven't done so yet, as it's required for push operations and pull encrypted vectors.
48
+
49
+ ### Set up your key (required for push and pull of encrypted vectors)
50
+
51
+ ```bash
52
+ export WDOUBLESYNC_KEY=suiprivkey1...
53
+ ```
54
+
55
+ For **read-only public vectors**, the key is not required.
56
+
57
+ ### Push a folder (creates new vector if no ID provided)
58
+
59
+ Be sure to run this command from the folder you want to sync:
60
+
61
+ ```bash
62
+ cd ~/my-folder
63
+ wdoublesync push
64
+ # Creates new vector + syncs current folder
65
+ # Output: created: 0x1234... / syncing ./ → 0x1234...
66
+ ```
67
+
68
+ This creates a new EndlessVector on-chain and pushes the current folder as a snapshot encrypted by Seal. The output will show the vector ID (e.g., `0x1234...`) which you can use for future pulls or pushes.
69
+
70
+ ### Pull a vector to restore it
71
+
72
+ ```bash
73
+ mkdir ~/restored-folder && cd ~/restored-folder
74
+ wdoublesync pull 0x1234...
75
+ # Restores vector to current directory
76
+ ```
77
+
78
+ ### View vector metadata
79
+
80
+ ```bash
81
+ wdoublesync info 0x1234...
82
+ # Output: ID, owner, version count, total size, encryption status
83
+ ```
84
+
85
+ ### Watch for changes (bi-directional sync)
86
+
87
+ ```bash
88
+ cd ~/my-data
89
+ wdoublesync watch 0x1234... --poll-interval 5
90
+ # Monitors local folder, auto-pushes changes
91
+ # Checks remote every 5 seconds, auto-pulls updates
92
+ ```
93
+
94
+ ## API Surface
95
+
96
+ All commands operate on the **current working directory** — `cd` into your folder first.
97
+
98
+ | Command | Arguments | Use Case |
99
+ |---------|-----------|----------|
100
+ | **push** | `[vectorId]` `[options]` | Sync current folder to vector; creates new vector if no ID given |
101
+ | **pull** | `vectorId` `[options]` | Restore vector (default: latest version) into current directory |
102
+ | **info** | `[vectorId]` | No ID: show chain + wallet. With ID: full vector details |
103
+ | **watch** | `vectorId` `[options]` | Bi-directional sync: auto-push local changes, auto-pull remote updates |
104
+ | **rebate** | `vectorId` | Burn archive patches, push current folder as fresh single snapshot |
105
+
106
+ ### Command Examples
107
+
108
+ #### push (create new vector)
109
+
110
+ ```bash
111
+ cd ~/my-data
112
+ wdoublesync push
113
+ # Syncs current folder as new vector
114
+ # Output: creating new EndlessVector on testnet...
115
+ # created: 0xabc123...
116
+ # syncing ./ → 0xabc123...
117
+ ```
118
+
119
+ #### push (with manifest for faster change detection)
120
+
121
+ ```bash
122
+ cd ~/my-data
123
+ wdoublesync push 0x1234... --manifest
124
+ # Writes .wdoublesync manifest; faster change detection on future pushes
125
+ ```
126
+
127
+ #### push (full snapshot, not incremental)
128
+
129
+ ```bash
130
+ cd ~/my-data
131
+ wdoublesync push 0x1234... --force-snapshot
132
+ # Uploads entire snapshot, useful if history is corrupted
133
+ ```
134
+
135
+ #### pull (latest version)
136
+
137
+ ```bash
138
+ mkdir ~/restored && cd ~/restored
139
+ wdoublesync pull 0x1234...
140
+ # Restores latest version to current directory
141
+ ```
142
+
143
+ #### pull (specific version)
144
+
145
+ ```bash
146
+ cd ~/restored
147
+ wdoublesync pull 0x1234... --version 10
148
+ # Restores version 10 instead of latest
149
+ ```
150
+
151
+ #### watch (bi-directional)
152
+
153
+ ```bash
154
+ cd ~/my-data
155
+ wdoublesync watch 0x1234... --poll-interval 5
156
+ # Monitors current folder, auto-pushes on change (debounce: 1000ms default)
157
+ # Checks remote every 5 seconds, auto-pulls new versions
158
+ # Runs indefinitely; stop with Ctrl+C
159
+ ```
160
+
161
+ #### watch (push-only)
162
+
163
+ ```bash
164
+ cd ~/my-data
165
+ wdoublesync watch 0x1234... --push-only
166
+ # Only auto-push on local changes, don't pull remote updates
167
+ ```
168
+
169
+ #### watch (pull-only)
170
+
171
+ ```bash
172
+ cd ~/my-data
173
+ wdoublesync watch 0x1234... --pull-only
174
+ # Only pull remote updates, don't auto-push local changes
175
+ ```
176
+
177
+ #### rebate
178
+
179
+ When user asks to rebate, you can explain that this command is useful after many incremental syncs to reclaim on-chain storage. It burns the archive history and pushes the current folder as a fresh single snapshot. Make sure user understands that after rebate, only the latest snapshot is available, and old patches will be burned.
180
+
181
+ ```bash
182
+ cd ~/my-data
183
+ wdoublesync rebate 0x1234...
184
+ # Burns archive history, pushes current folder as fresh single snapshot
185
+ # Useful after many incremental syncs to reclaim on-chain storage
186
+ ```
187
+
188
+
189
+ ## Configuration
190
+
191
+ ### Command-line flags
192
+
193
+ | Flag | Arguments | Default | Use |
194
+ |------|-----------|---------|-----|
195
+ | `--chain` | `mainnet` \| `testnet` \| `devnet` \| `localnet` | `testnet` | Which Sui network to use |
196
+ | `--key` | Sui private key (`suiprivkey1...`) | — | Sign transactions (or use env vars) |
197
+ | `--phrase` | Mnemonic phrase | — | Alternative: derive key from mnemonic |
198
+ | `--no-compress` | — | — | Push: disable gzip compression |
199
+ | `--manifest` | — | — | Push: write/use `.wdoublesync` file for faster change detection |
200
+ | `--force-snapshot` | — | — | Push: full snapshot (repairs corrupt vectors) |
201
+ | `--version` | Number | `latest` | Pull: restore specific version instead of latest |
202
+ | `--exclude` | Patterns (comma-separated) | — | Extra file patterns to exclude from sync |
203
+ | `--poll-interval` | Seconds | `2` | Watch: check for remote updates every N seconds |
204
+ | `--debounce` | Milliseconds | `1000` | Watch: quiet period before pushing after local change |
205
+ | `--push-only` | — | — | Watch: disable auto-pull |
206
+ | `--pull-only` | — | — | Watch: disable auto-push |
207
+
208
+ ### Environment variables
209
+
210
+ | Variable | Type | Use |
211
+ |----------|------|-----|
212
+ | `WDOUBLESYNC_KEY` | Sui private key (`suiprivkey1...`) | Sign transactions (alternative to `--key` flag) |
213
+
214
+ ## Example Workflows
215
+
216
+ ### Workflow 1: Create and verify
217
+
218
+ ```bash
219
+ # 1. Push a folder (creates new vector on first push)
220
+ cd ~/important-data
221
+ wdoublesync push
222
+ # Output: creating new EndlessVector on testnet...
223
+ # created: 0xabc123...
224
+
225
+ # 2. Verify by pulling to a fresh folder
226
+ mkdir ~/verify-tmp && cd ~/verify-tmp
227
+ wdoublesync pull 0xabc123...
228
+
229
+ # 3. Check file counts match
230
+ ls ~/important-data | wc -l
231
+ ls ~/verify-tmp | wc -l
232
+ ```
233
+
234
+ ### Workflow 2: Multi-version history and rollback
235
+
236
+ ```bash
237
+ cd ~/data
238
+ # Push version 1
239
+ wdoublesync push 0xabc123...
240
+
241
+ # Make changes, push version 2
242
+ echo "new file" > new.txt
243
+ wdoublesync push 0xabc123...
244
+
245
+ # List all versions
246
+ wdoublesync info 0xabc123...
247
+
248
+ # Rollback to version 1
249
+ mkdir ~/data-v1 && cd ~/data-v1
250
+ wdoublesync pull 0xabc123... --version 1
251
+ # Folder now matches state from version 1
252
+ ```
253
+
254
+ ### Workflow 3: Rebate to compact archive
255
+
256
+ After many incremental syncs, archive grows with lots of patches. Rebate burns old data and rebuilds as single snapshot:
257
+
258
+ ```bash
259
+ # Before rebate: many small patches
260
+ wdoublesync info 0xabc123...
261
+ # Output: version 87, total size 45 MB (many patches)
262
+
263
+ # Run rebate from current directory
264
+ cd ~/data
265
+ wdoublesync rebate 0xabc123...
266
+ # Output: Burned 86 items, pushed version 88 (3.2 MB)
267
+
268
+ # After rebate: single snapshot instead of 87 patches
269
+ # Much more efficient for Walrus storage
270
+ ```
271
+
272
+ ### Workflow 4: Watch for external changes
273
+
274
+ ```bash
275
+ # Another user pushes to the same vector
276
+ # You want to auto-sync your local copy
277
+
278
+ cd ~/data
279
+ wdoublesync watch 0xabc123... --poll-interval 5
280
+ # Monitors local folder for changes, auto-pushes them
281
+ # Also checks remote every 5 seconds for updates, auto-pulls
282
+ # Press Ctrl+C to stop
283
+ ```
284
+
285
+ ## Troubleshooting
286
+
287
+ | Issue | Cause | Solution |
288
+ |-------|-------|----------|
289
+ | **"No signing key"** | Trying to push/pull without credentials | Export `WDOUBLESYNC_KEY=suiprivkey1...` or use `--key` / `--phrase` flag |
290
+ | **"vector-id is required"** | Command needs a vector ID but none provided | Pass the vector ID as the first argument: `wdoublesync pull 0x...` |
291
+ | **"This vector is Seal-encrypted"** | Vector is encrypted but no key provided | Provide `--key` or `--phrase`, or export `WDOUBLESYNC_KEY` |
292
+ | **"at() is out of range, this part of archive has been burned"** | Trying to restore a version whose archive was burned by rebate | Only the latest snapshot is available after rebate; pull without `--version` |
293
+ | **Connection errors** | RPC endpoint unreachable or wrong chain | You may need to try again little later |
294
+
295
+ ## Links
296
+
297
+ - **Repository**: https://github.com/FizzyFlow/wdoublesync_cli
@@ -1,6 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { push, pull, info, watch, rebate } from '../lib/commands.js';
3
+ // Intercept process.exit() calls from dependencies so they don't interrupt
4
+ // the top-level await before it settles. Converts them to thrown errors that
5
+ // our try/catch handles, then sets process.exitCode without calling exit().
6
+ const _realExit = process.exit.bind(process);
7
+ process.exit = (code) => {
8
+ const err = new Error(`process.exit(${code ?? 0})`);
9
+ err._isProcessExit = true;
10
+ err._exitCode = code ?? 0;
11
+ throw err;
12
+ };
13
+
14
+ import { push, pull, info, watch, rebate } from '../lib/commands/index.js';
4
15
 
5
16
  const USAGE = `wdoublesync — sync local folders to EndlessVector on Sui+Walrus
6
17
 
@@ -31,6 +42,7 @@ function parseArgs(argv) {
31
42
  const args = {
32
43
  command: null,
33
44
  vectorId: null,
45
+ path: process.cwd(),
34
46
  chain: 'testnet',
35
47
  key: null,
36
48
  phrase: null,
@@ -49,18 +61,30 @@ function parseArgs(argv) {
49
61
 
50
62
  if (raw.length === 0 || raw.includes('--help')) {
51
63
  console.log(USAGE);
52
- process.exit(0);
64
+ _realExit(0);
53
65
  }
54
66
 
55
67
  args.command = raw[0];
56
68
 
57
69
  let i = 1;
58
70
 
59
- // Next positional arg is vector-id if it looks like one (starts with 0x)
60
- if (i < raw.length && !raw[i].startsWith('--')) {
61
- args.vectorId = raw[i];
71
+ // Collect up to two positional args: [vectorId] [path]
72
+ // vectorId starts with 0x, path starts with /, ~, or .
73
+ const positionals = [];
74
+ while (i < raw.length && !raw[i].startsWith('--')) {
75
+ positionals.push(raw[i]);
62
76
  i++;
63
77
  }
78
+ if (positionals.length === 2) {
79
+ args.vectorId = positionals[0];
80
+ args.path = positionals[1];
81
+ } else if (positionals.length === 1) {
82
+ if (positionals[0].startsWith('0x')) {
83
+ args.vectorId = positionals[0];
84
+ } else {
85
+ args.path = positionals[0];
86
+ }
87
+ }
64
88
 
65
89
  while (i < raw.length) {
66
90
  const flag = raw[i];
@@ -97,6 +121,12 @@ function parseArgs(argv) {
97
121
 
98
122
  const args = parseArgs(process.argv);
99
123
 
124
+ // The Sui/Walrus SDKs use undici (Node.js fetch) which unref's its HTTP connections.
125
+ // With only unref'd I/O in flight, the event loop drains and Node.js exits with code 13
126
+ // ("Detected unsettled top-level await") before network responses arrive.
127
+ // A ref'd interval keeps the event loop alive for the duration of the command.
128
+ const keepAlive = setInterval(() => {}, 60_000);
129
+
100
130
  try {
101
131
  if (args.command === 'push') {
102
132
  await push(args);
@@ -111,11 +141,16 @@ try {
111
141
  } else {
112
142
  console.error('Unknown command:', args.command);
113
143
  console.log(USAGE);
114
- process.exit(1);
144
+ process.exitCode = 1;
115
145
  }
116
- process.exit(0);
117
146
  } catch (err) {
118
- console.error('Error:', err.message);
119
- if (err.stack) console.error(err.stack);
120
- process.exit(1);
147
+ if (err._isProcessExit) {
148
+ process.exitCode = err._exitCode;
149
+ } else {
150
+ console.error('Error:', err.message);
151
+ if (err.stack) console.error(err.stack);
152
+ process.exitCode = 1;
153
+ }
154
+ } finally {
155
+ clearInterval(keepAlive);
121
156
  }