pushwork 1.0.4 → 1.0.7
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 +87 -328
- package/dist/.pushwork/automerge/3P/Dm3ekE2pmjGnWvDaG3vSR7ww98/snapshot/aa2349c94955ea561f698720142f9d884a6872d9f82dc332d578c216beb0df0e +0 -0
- package/dist/.pushwork/automerge/st/orage-adapter-id +1 -0
- package/dist/.pushwork/config.json +15 -0
- package/dist/.pushwork/snapshot.json +7 -0
- package/dist/cli.js +231 -170
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +51 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +799 -0
- package/dist/commands.js.map +1 -0
- package/dist/core/change-detection.d.ts +6 -19
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +101 -80
- package/dist/core/change-detection.js.map +1 -1
- package/dist/{config/index.d.ts → core/config.d.ts} +13 -3
- package/dist/core/config.d.ts.map +1 -0
- package/dist/{config/index.js → core/config.js} +55 -73
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/move-detection.d.ts +12 -50
- package/dist/core/move-detection.d.ts.map +1 -1
- package/dist/core/move-detection.js +58 -139
- package/dist/core/move-detection.js.map +1 -1
- package/dist/core/snapshot.d.ts +0 -4
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/snapshot.js +2 -11
- package/dist/core/snapshot.js.map +1 -1
- package/dist/core/sync-engine.d.ts +5 -11
- package/dist/core/sync-engine.d.ts.map +1 -1
- package/dist/core/sync-engine.js +220 -362
- package/dist/core/sync-engine.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/types/config.d.ts +43 -67
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/documents.d.ts +15 -3
- package/dist/types/documents.d.ts.map +1 -1
- package/dist/types/documents.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/snapshot.d.ts +3 -21
- package/dist/types/snapshot.d.ts.map +1 -1
- package/dist/types/snapshot.js +0 -14
- package/dist/types/snapshot.js.map +1 -1
- package/dist/utils/content.d.ts.map +1 -1
- package/dist/utils/content.js +2 -6
- package/dist/utils/content.js.map +1 -1
- package/dist/utils/directory.d.ts +10 -0
- package/dist/utils/directory.d.ts.map +1 -0
- package/dist/utils/directory.js +37 -0
- package/dist/utils/directory.js.map +1 -0
- package/dist/utils/fs.d.ts +15 -2
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +63 -53
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/mime-types.d.ts.map +1 -1
- package/dist/utils/mime-types.js +11 -4
- package/dist/utils/mime-types.js.map +1 -1
- package/dist/utils/network-sync.d.ts +0 -6
- package/dist/utils/network-sync.d.ts.map +1 -1
- package/dist/utils/network-sync.js +55 -99
- package/dist/utils/network-sync.js.map +1 -1
- package/dist/utils/output.d.ts +129 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +375 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/repo-factory.d.ts +2 -6
- package/dist/utils/repo-factory.d.ts.map +1 -1
- package/dist/utils/repo-factory.js +8 -22
- package/dist/utils/repo-factory.js.map +1 -1
- package/dist/utils/string-similarity.d.ts +14 -0
- package/dist/utils/string-similarity.d.ts.map +1 -0
- package/dist/utils/string-similarity.js +43 -0
- package/dist/utils/string-similarity.js.map +1 -0
- package/dist/utils/trace.d.ts +19 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +68 -0
- package/dist/utils/trace.js.map +1 -0
- package/package.json +17 -12
- package/src/cli.ts +326 -252
- package/src/commands.ts +988 -0
- package/src/core/change-detection.ts +199 -162
- package/src/{config/index.ts → core/config.ts} +65 -82
- package/src/core/index.ts +1 -1
- package/src/core/move-detection.ts +74 -180
- package/src/core/snapshot.ts +2 -12
- package/src/core/sync-engine.ts +248 -499
- package/src/index.ts +0 -10
- package/src/types/config.ts +50 -72
- package/src/types/documents.ts +16 -3
- package/src/types/index.ts +0 -5
- package/src/types/snapshot.ts +1 -23
- package/src/utils/content.ts +2 -6
- package/src/utils/directory.ts +50 -0
- package/src/utils/fs.ts +67 -56
- package/src/utils/index.ts +1 -6
- package/src/utils/mime-types.ts +12 -4
- package/src/utils/network-sync.ts +79 -137
- package/src/utils/output.ts +450 -0
- package/src/utils/repo-factory.ts +13 -31
- package/src/utils/string-similarity.ts +54 -0
- package/src/utils/trace.ts +70 -0
- package/test/integration/exclude-patterns.test.ts +6 -15
- package/test/integration/fuzzer.test.ts +308 -391
- package/test/integration/init-sync.test.ts +89 -0
- package/test/integration/sync-deletion.test.ts +2 -61
- package/test/integration/sync-flow.test.ts +4 -24
- package/test/jest.setup.ts +34 -0
- package/test/unit/deletion-behavior.test.ts +3 -14
- package/test/unit/enhanced-mime-detection.test.ts +0 -22
- package/test/unit/snapshot.test.ts +2 -29
- package/test/unit/sync-convergence.test.ts +3 -198
- package/test/unit/sync-timing.test.ts +0 -44
- package/test/unit/utils.test.ts +0 -2
- package/tsconfig.json +3 -3
- package/dist/browser/browser-sync-engine.d.ts +0 -64
- package/dist/browser/browser-sync-engine.d.ts.map +0 -1
- package/dist/browser/browser-sync-engine.js +0 -303
- package/dist/browser/browser-sync-engine.js.map +0 -1
- package/dist/browser/filesystem-adapter.d.ts +0 -84
- package/dist/browser/filesystem-adapter.d.ts.map +0 -1
- package/dist/browser/filesystem-adapter.js +0 -413
- package/dist/browser/filesystem-adapter.js.map +0 -1
- package/dist/browser/index.d.ts +0 -36
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -90
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/types.d.ts +0 -70
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/browser/types.js +0 -6
- package/dist/browser/types.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -77
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -904
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -19
- package/dist/cli/index.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/core/isomorphic-snapshot.d.ts +0 -58
- package/dist/core/isomorphic-snapshot.d.ts.map +0 -1
- package/dist/core/isomorphic-snapshot.js +0 -204
- package/dist/core/isomorphic-snapshot.js.map +0 -1
- package/dist/platform/browser-filesystem.d.ts +0 -26
- package/dist/platform/browser-filesystem.d.ts.map +0 -1
- package/dist/platform/browser-filesystem.js +0 -91
- package/dist/platform/browser-filesystem.js.map +0 -1
- package/dist/platform/filesystem.d.ts +0 -29
- package/dist/platform/filesystem.d.ts.map +0 -1
- package/dist/platform/filesystem.js +0 -65
- package/dist/platform/filesystem.js.map +0 -1
- package/dist/platform/node-filesystem.d.ts +0 -21
- package/dist/platform/node-filesystem.d.ts.map +0 -1
- package/dist/platform/node-filesystem.js +0 -93
- package/dist/platform/node-filesystem.js.map +0 -1
- package/dist/utils/content-similarity.d.ts +0 -53
- package/dist/utils/content-similarity.d.ts.map +0 -1
- package/dist/utils/content-similarity.js +0 -155
- package/dist/utils/content-similarity.js.map +0 -1
- package/dist/utils/fs-browser.d.ts +0 -57
- package/dist/utils/fs-browser.d.ts.map +0 -1
- package/dist/utils/fs-browser.js +0 -311
- package/dist/utils/fs-browser.js.map +0 -1
- package/dist/utils/fs-node.d.ts +0 -53
- package/dist/utils/fs-node.d.ts.map +0 -1
- package/dist/utils/fs-node.js +0 -220
- package/dist/utils/fs-node.js.map +0 -1
- package/dist/utils/isomorphic.d.ts +0 -29
- package/dist/utils/isomorphic.d.ts.map +0 -1
- package/dist/utils/isomorphic.js +0 -139
- package/dist/utils/isomorphic.js.map +0 -1
- package/dist/utils/pure.d.ts +0 -25
- package/dist/utils/pure.d.ts.map +0 -1
- package/dist/utils/pure.js +0 -112
- package/dist/utils/pure.js.map +0 -1
- package/src/cli/commands.ts +0 -1207
- package/src/cli/index.ts +0 -2
- package/src/utils/content-similarity.ts +0 -194
- package/test/README-TESTING-GAPS.md +0 -174
- package/test/unit/content-similarity.test.ts +0 -236
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { StorageId } from "@automerge/automerge-repo";
|
|
4
|
+
import { Command } from "@commander-js/extra-typings";
|
|
4
5
|
import chalk from "chalk";
|
|
5
6
|
import {
|
|
6
7
|
init,
|
|
@@ -12,319 +13,392 @@ import {
|
|
|
12
13
|
checkout,
|
|
13
14
|
commit,
|
|
14
15
|
url,
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
rm,
|
|
17
|
+
ls,
|
|
18
|
+
config,
|
|
19
|
+
watch,
|
|
20
|
+
} from "./commands";
|
|
17
21
|
|
|
18
|
-
/**
|
|
19
|
-
* Wrapper for command actions with consistent error handling
|
|
20
|
-
*/
|
|
21
|
-
function withErrorHandling<T extends any[], R>(
|
|
22
|
-
fn: (...args: T) => Promise<R>
|
|
23
|
-
): (...args: T) => Promise<void> {
|
|
24
|
-
return async (...args: T): Promise<void> => {
|
|
25
|
-
try {
|
|
26
|
-
await fn(...args);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
console.error(chalk.red(`Error: ${error}`));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const program = new Command();
|
|
35
|
-
|
|
36
|
-
// get the version from the package.json
|
|
37
22
|
const version = require("../package.json").version;
|
|
38
|
-
|
|
39
|
-
program
|
|
23
|
+
const program = new Command()
|
|
40
24
|
.name("pushwork")
|
|
41
25
|
.description("Bidirectional directory synchronization using Automerge CRDTs")
|
|
42
|
-
.version(version);
|
|
26
|
+
.version(version, "-V, --version", "output the version number");
|
|
43
27
|
|
|
44
28
|
// Init command
|
|
45
29
|
program
|
|
46
30
|
.command("init")
|
|
47
|
-
.
|
|
48
|
-
.argument(
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
31
|
+
.summary("Initialize sync in a directory")
|
|
32
|
+
.argument(
|
|
33
|
+
"[path]",
|
|
34
|
+
"Directory path to initialize (default: current directory)",
|
|
35
|
+
"."
|
|
52
36
|
)
|
|
53
37
|
.option(
|
|
54
|
-
"--sync-server
|
|
55
|
-
"Custom sync server storage ID
|
|
38
|
+
"--sync-server <url> <storage-id...>",
|
|
39
|
+
"Custom sync server URL and storage ID"
|
|
56
40
|
)
|
|
57
|
-
.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Note: Custom sync server options must always be used together.`
|
|
65
|
-
)
|
|
66
|
-
.action(
|
|
67
|
-
withErrorHandling(async (path: string, options) => {
|
|
68
|
-
// Validate that both sync server options are provided together
|
|
69
|
-
const hasSyncServer = !!options.syncServer;
|
|
70
|
-
const hasSyncServerStorageId = !!options.syncServerStorageId;
|
|
71
|
-
|
|
72
|
-
if (hasSyncServer && !hasSyncServerStorageId) {
|
|
73
|
-
console.error(
|
|
74
|
-
chalk.red("Error: --sync-server requires --sync-server-storage-id")
|
|
75
|
-
);
|
|
76
|
-
console.error(
|
|
77
|
-
chalk.yellow("Both arguments must be provided together.")
|
|
78
|
-
);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (hasSyncServerStorageId && !hasSyncServer) {
|
|
83
|
-
console.error(
|
|
84
|
-
chalk.red("Error: --sync-server-storage-id requires --sync-server")
|
|
85
|
-
);
|
|
86
|
-
console.error(
|
|
87
|
-
chalk.yellow("Both arguments must be provided together.")
|
|
88
|
-
);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
await init(path, options.syncServer, options.syncServerStorageId);
|
|
93
|
-
})
|
|
94
|
-
);
|
|
41
|
+
.action(async (path, opts) => {
|
|
42
|
+
const [syncServer, syncServerStorageId] = validateSyncServer(
|
|
43
|
+
opts.syncServer
|
|
44
|
+
);
|
|
45
|
+
await init(path, { syncServer, syncServerStorageId });
|
|
46
|
+
});
|
|
95
47
|
|
|
96
48
|
// Clone command
|
|
97
49
|
program
|
|
98
50
|
.command("clone")
|
|
99
|
-
.
|
|
100
|
-
.argument(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
.option(
|
|
104
|
-
"--sync-server <url>",
|
|
105
|
-
"Custom sync server URL (must be used with --sync-server-storage-id)"
|
|
51
|
+
.summary("Clone an existing synced directory")
|
|
52
|
+
.argument(
|
|
53
|
+
"<url>",
|
|
54
|
+
"AutomergeUrl of root directory to clone (format: automerge:XXXXX)"
|
|
106
55
|
)
|
|
56
|
+
.argument("<path>", "Target directory path")
|
|
57
|
+
.option("-f, --force", "Overwrite existing directory", false)
|
|
107
58
|
.option(
|
|
108
|
-
"--sync-server
|
|
109
|
-
"Custom sync server storage ID
|
|
59
|
+
"--sync-server <url> <storage-id...>",
|
|
60
|
+
"Custom sync server URL and storage ID"
|
|
110
61
|
)
|
|
111
|
-
.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Validate that both sync server options are provided together
|
|
124
|
-
const hasSyncServer = !!options.syncServer;
|
|
125
|
-
const hasSyncServerStorageId = !!options.syncServerStorageId;
|
|
126
|
-
|
|
127
|
-
if (hasSyncServer && !hasSyncServerStorageId) {
|
|
128
|
-
console.error(
|
|
129
|
-
chalk.red("Error: --sync-server requires --sync-server-storage-id")
|
|
130
|
-
);
|
|
131
|
-
console.error(
|
|
132
|
-
chalk.yellow("Both arguments must be provided together.")
|
|
133
|
-
);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (hasSyncServerStorageId && !hasSyncServer) {
|
|
138
|
-
console.error(
|
|
139
|
-
chalk.red("Error: --sync-server-storage-id requires --sync-server")
|
|
140
|
-
);
|
|
141
|
-
console.error(
|
|
142
|
-
chalk.yellow("Both arguments must be provided together.")
|
|
143
|
-
);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
await clone(url, path, {
|
|
148
|
-
force: options.force || false,
|
|
149
|
-
dryRun: false,
|
|
150
|
-
verbose: false,
|
|
151
|
-
syncServer: options.syncServer,
|
|
152
|
-
syncServerStorageId: options.syncServerStorageId,
|
|
153
|
-
});
|
|
154
|
-
})
|
|
155
|
-
);
|
|
62
|
+
.option("-v, --verbose", "Verbose output", false)
|
|
63
|
+
.action(async (url, path, opts) => {
|
|
64
|
+
const [syncServer, syncServerStorageId] = validateSyncServer(
|
|
65
|
+
opts.syncServer
|
|
66
|
+
);
|
|
67
|
+
await clone(url, path, {
|
|
68
|
+
force: opts.force,
|
|
69
|
+
verbose: opts.verbose,
|
|
70
|
+
syncServer,
|
|
71
|
+
syncServerStorageId,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
156
74
|
|
|
157
75
|
// Commit command
|
|
158
76
|
program
|
|
159
77
|
.command("commit")
|
|
160
|
-
.
|
|
161
|
-
.argument(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
78
|
+
.summary("Save local changes to Automerge documents")
|
|
79
|
+
.argument(
|
|
80
|
+
"[path]",
|
|
81
|
+
"Directory path to commit (default: current directory)",
|
|
82
|
+
"."
|
|
83
|
+
)
|
|
84
|
+
.action(async (path, _opts) => {
|
|
85
|
+
await commit(path);
|
|
86
|
+
});
|
|
168
87
|
|
|
169
88
|
// Sync command
|
|
170
89
|
program
|
|
171
90
|
.command("sync")
|
|
172
|
-
.
|
|
173
|
-
.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
)
|
|
91
|
+
.summary("Run full bidirectional synchronization")
|
|
92
|
+
.argument(
|
|
93
|
+
"[path]",
|
|
94
|
+
"Directory path to sync (default: current directory)",
|
|
95
|
+
"."
|
|
96
|
+
)
|
|
97
|
+
.option(
|
|
98
|
+
"--dry-run",
|
|
99
|
+
"Show what would be done without applying changes",
|
|
100
|
+
false
|
|
101
|
+
)
|
|
102
|
+
.option("-v, --verbose", "Verbose output", false)
|
|
103
|
+
.action(async (path, opts) => {
|
|
104
|
+
await sync(path, {
|
|
105
|
+
dryRun: opts.dryRun,
|
|
106
|
+
verbose: opts.verbose,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
183
109
|
|
|
184
110
|
// Diff command
|
|
185
111
|
program
|
|
186
112
|
.command("diff")
|
|
187
|
-
.
|
|
188
|
-
.argument(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
})
|
|
200
|
-
);
|
|
113
|
+
.summary("Show changes in working directory")
|
|
114
|
+
.argument(
|
|
115
|
+
"[path]",
|
|
116
|
+
"Limit diff to specific path (default: current directory)",
|
|
117
|
+
"."
|
|
118
|
+
)
|
|
119
|
+
.option("--name-only", "Show only changed file names", false)
|
|
120
|
+
.action(async (path, opts) => {
|
|
121
|
+
await diff(path, {
|
|
122
|
+
nameOnly: opts.nameOnly,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
201
125
|
|
|
202
126
|
// Status command
|
|
203
127
|
program
|
|
204
128
|
.command("status")
|
|
205
|
-
.
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
129
|
+
.summary("Show sync status summary")
|
|
130
|
+
.argument("[path]", "Directory path (default: current directory)", ".")
|
|
131
|
+
.option(
|
|
132
|
+
"-v, --verbose",
|
|
133
|
+
"Show detailed status including document info and all tracked files",
|
|
134
|
+
false
|
|
135
|
+
)
|
|
136
|
+
.action(async (path, opts) => {
|
|
137
|
+
await status(path, {
|
|
138
|
+
verbose: opts.verbose,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
211
141
|
|
|
212
142
|
// Log command
|
|
213
143
|
program
|
|
214
144
|
.command("log")
|
|
215
|
-
.
|
|
216
|
-
.argument(
|
|
217
|
-
|
|
145
|
+
.summary("Show sync history (experimental)")
|
|
146
|
+
.argument(
|
|
147
|
+
"[path]",
|
|
148
|
+
"Show history for specific file or directory (default: current directory)",
|
|
149
|
+
"."
|
|
150
|
+
)
|
|
151
|
+
.option("--oneline", "Compact one-line per sync format", false)
|
|
218
152
|
.option("--since <date>", "Show syncs since date")
|
|
219
153
|
.option("--limit <n>", "Limit number of syncs shown", "10")
|
|
220
|
-
.action(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
verbose: false,
|
|
228
|
-
});
|
|
229
|
-
})
|
|
230
|
-
);
|
|
154
|
+
.action(async (path, opts) => {
|
|
155
|
+
await log(path, {
|
|
156
|
+
oneline: opts.oneline,
|
|
157
|
+
since: opts.since,
|
|
158
|
+
limit: parseInt(opts.limit),
|
|
159
|
+
});
|
|
160
|
+
});
|
|
231
161
|
|
|
232
162
|
// Checkout command
|
|
233
163
|
program
|
|
234
164
|
.command("checkout")
|
|
235
|
-
.
|
|
165
|
+
.summary("Restore to previous sync (experimental)")
|
|
236
166
|
.argument("<sync-id>", "Sync ID to restore to")
|
|
237
|
-
.argument(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
)
|
|
167
|
+
.argument(
|
|
168
|
+
"[path]",
|
|
169
|
+
"Specific path to restore (default: current directory)",
|
|
170
|
+
"."
|
|
171
|
+
)
|
|
172
|
+
.option(
|
|
173
|
+
"-f, --force",
|
|
174
|
+
"Force checkout even if there are uncommitted changes",
|
|
175
|
+
false
|
|
176
|
+
)
|
|
177
|
+
.action(async (syncId, path, opts) => {
|
|
178
|
+
await checkout(syncId, path, {
|
|
179
|
+
force: opts.force,
|
|
180
|
+
});
|
|
181
|
+
});
|
|
248
182
|
|
|
249
183
|
// URL command
|
|
250
184
|
program
|
|
251
185
|
.command("url")
|
|
252
|
-
.
|
|
253
|
-
.argument("[path]", "Directory path", ".")
|
|
254
|
-
.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
186
|
+
.summary("Show the Automerge root URL")
|
|
187
|
+
.argument("[path]", "Directory path (default: current directory)", ".")
|
|
188
|
+
.action(async (path) => {
|
|
189
|
+
await url(path);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Remove command
|
|
193
|
+
program
|
|
194
|
+
.command("rm")
|
|
195
|
+
.summary("Remove local pushwork data")
|
|
196
|
+
.argument("[path]", "Directory path (default: current directory)", ".")
|
|
197
|
+
.action(async (path) => {
|
|
198
|
+
await rm(path);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// List command
|
|
202
|
+
program
|
|
203
|
+
.command("ls")
|
|
204
|
+
.summary("List tracked files")
|
|
205
|
+
.argument("[path]", "Directory path (default: current directory)", ".")
|
|
206
|
+
.option("-v, --verbose", "Show with Automerge URLs", false)
|
|
207
|
+
.action(async (path, opts) => {
|
|
208
|
+
await ls(path, {
|
|
209
|
+
verbose: opts.verbose,
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Config command
|
|
214
|
+
program
|
|
215
|
+
.command("config")
|
|
216
|
+
.summary("View or edit configuration")
|
|
217
|
+
.argument("[path]", "Directory path (default: current directory)", ".")
|
|
218
|
+
.option("--list", "Show full configuration", false)
|
|
219
|
+
.option(
|
|
220
|
+
"--get <key>",
|
|
221
|
+
"Get specific config value (dot notation, e.g., sync.move_detection_threshold)"
|
|
262
222
|
)
|
|
263
|
-
.action(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
223
|
+
.action(async (path, opts) => {
|
|
224
|
+
await config(path, {
|
|
225
|
+
list: opts.list,
|
|
226
|
+
get: opts.get,
|
|
227
|
+
});
|
|
228
|
+
});
|
|
268
229
|
|
|
269
|
-
//
|
|
230
|
+
// Watch command
|
|
270
231
|
program
|
|
271
|
-
.command("
|
|
272
|
-
.
|
|
273
|
-
.argument(
|
|
232
|
+
.command("watch")
|
|
233
|
+
.summary("Watch directory for changes, build, and sync")
|
|
234
|
+
.argument(
|
|
235
|
+
"[path]",
|
|
236
|
+
"Directory path to sync (default: current directory)",
|
|
237
|
+
"."
|
|
238
|
+
)
|
|
274
239
|
.option(
|
|
275
|
-
"
|
|
276
|
-
"
|
|
240
|
+
"--script <command>",
|
|
241
|
+
"Build script to run before syncing",
|
|
242
|
+
"pnpm build"
|
|
277
243
|
)
|
|
278
|
-
.
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
pushwork debug # Show debug info for current directory
|
|
283
|
-
pushwork debug --verbose # Show verbose debug info including full document contents
|
|
284
|
-
pushwork debug ./repo # Show debug info for specific directory
|
|
285
|
-
|
|
286
|
-
This command displays internal document state, including the lastSyncAt timestamp
|
|
287
|
-
that gets updated when sync operations make changes.`
|
|
244
|
+
.option(
|
|
245
|
+
"--dir <dir>",
|
|
246
|
+
"Directory to watch for changes (relative to working directory)",
|
|
247
|
+
"src"
|
|
288
248
|
)
|
|
289
|
-
.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
249
|
+
.option("-v, --verbose", "Show build script output", false)
|
|
250
|
+
.action(async (path, opts) => {
|
|
251
|
+
await watch(path, {
|
|
252
|
+
script: opts.script,
|
|
253
|
+
watchDir: opts.dir,
|
|
254
|
+
verbose: opts.verbose,
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Completion command (hidden from help)
|
|
259
|
+
program.command("completion", { hidden: true }).action(() => {
|
|
260
|
+
// Generate completion dynamically from registered commands
|
|
261
|
+
const commands = program.commands
|
|
262
|
+
.filter((cmd) => cmd.name() !== "completion") // Exclude self
|
|
263
|
+
.map((cmd) => {
|
|
264
|
+
const name = cmd.name();
|
|
265
|
+
const desc = (cmd.summary() || cmd.description() || "").replace(
|
|
266
|
+
/'/g,
|
|
267
|
+
"\\'"
|
|
268
|
+
);
|
|
269
|
+
return `'${name}:${desc}'`;
|
|
294
270
|
})
|
|
295
|
-
|
|
271
|
+
.join(" ");
|
|
296
272
|
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
273
|
+
// Generate option completions for each command
|
|
274
|
+
const commandCases = program.commands
|
|
275
|
+
.filter((cmd) => cmd.name() !== "completion")
|
|
276
|
+
.map((cmd) => {
|
|
277
|
+
const options = cmd.options
|
|
278
|
+
.filter((opt) => opt.flags !== "-h, --help") // Exclude help
|
|
279
|
+
.map((opt) => {
|
|
280
|
+
// Parse flags like "-v, --verbose" or "--dry-run"
|
|
281
|
+
const flags = opt.flags.split(",").map((f) => f.trim());
|
|
282
|
+
const desc = (opt.description || "")
|
|
283
|
+
.replace(/'/g, "\\'")
|
|
284
|
+
.replace(/\n/g, " ");
|
|
285
|
+
|
|
286
|
+
// For options with arguments like "--sync-server <url>"
|
|
287
|
+
// Extract just the flag part
|
|
288
|
+
const cleanFlags = flags.map((f) => f.split(/\s+/)[0]);
|
|
289
|
+
|
|
290
|
+
if (cleanFlags.length > 1) {
|
|
291
|
+
// Multiple flags (short and long): '(-v --verbose)'{-v,--verbose}'[description]'
|
|
292
|
+
const short = cleanFlags[0];
|
|
293
|
+
const long = cleanFlags[1];
|
|
294
|
+
return `'(${short} ${long})'{${short},${long}}'[${desc}]'`;
|
|
295
|
+
} else {
|
|
296
|
+
// Single flag: '--flag[description]'
|
|
297
|
+
return `'${cleanFlags[0]}[${desc}]'`;
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
.join(" \\\n ");
|
|
301
|
+
|
|
302
|
+
return options
|
|
303
|
+
? ` ${cmd.name()})
|
|
304
|
+
_arguments \\
|
|
305
|
+
${options}
|
|
306
|
+
;;`
|
|
307
|
+
: "";
|
|
308
|
+
})
|
|
309
|
+
.filter(Boolean)
|
|
310
|
+
.join("\n");
|
|
311
|
+
|
|
312
|
+
const completionScript = `
|
|
313
|
+
# pushwork completion for zsh
|
|
314
|
+
_pushwork() {
|
|
315
|
+
local -a commands
|
|
316
|
+
commands=(${commands})
|
|
317
|
+
|
|
318
|
+
_arguments -C \\
|
|
319
|
+
'1: :->command' \\
|
|
320
|
+
'*::arg:->args'
|
|
321
|
+
|
|
322
|
+
case $state in
|
|
323
|
+
command)
|
|
324
|
+
_describe 'command' commands
|
|
325
|
+
;;
|
|
326
|
+
args)
|
|
327
|
+
case $words[1] in
|
|
328
|
+
${commandCases}
|
|
329
|
+
esac
|
|
330
|
+
;;
|
|
331
|
+
esac
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
compdef _pushwork pushwork
|
|
335
|
+
`.trim();
|
|
336
|
+
|
|
337
|
+
console.log(completionScript);
|
|
306
338
|
});
|
|
307
339
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
errorMessage.includes("was closed")
|
|
315
|
-
) {
|
|
316
|
-
// Silently ignore WebSocket shutdown errors
|
|
317
|
-
return;
|
|
340
|
+
// Helper to validate and extract sync server options
|
|
341
|
+
function validateSyncServer(
|
|
342
|
+
syncServerOpt: string[] | undefined
|
|
343
|
+
): [string | undefined, StorageId | undefined] {
|
|
344
|
+
if (!syncServerOpt) {
|
|
345
|
+
return [undefined, undefined];
|
|
318
346
|
}
|
|
319
347
|
|
|
320
|
-
|
|
348
|
+
if (syncServerOpt.length < 2) {
|
|
349
|
+
console.error(
|
|
350
|
+
chalk.red("Error: --sync-server requires both URL and storage ID")
|
|
351
|
+
);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const [syncServer, syncServerStorageId] = syncServerOpt;
|
|
356
|
+
return [syncServer, syncServerStorageId as StorageId];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
process.on("unhandledRejection", (error) => {
|
|
360
|
+
console.log(chalk.bgRed.white(" ERROR "));
|
|
361
|
+
if (error instanceof Error && error.stack) {
|
|
362
|
+
console.log(chalk.red(error.stack));
|
|
363
|
+
} else {
|
|
364
|
+
console.error(chalk.red(error));
|
|
365
|
+
}
|
|
321
366
|
process.exit(1);
|
|
322
367
|
});
|
|
323
368
|
|
|
324
|
-
//
|
|
325
|
-
program
|
|
369
|
+
// Configure help colors using Commander v13's built-in color support
|
|
370
|
+
program
|
|
371
|
+
.configureHelp({
|
|
372
|
+
styleTitle: (str) => chalk.bold(str),
|
|
373
|
+
styleCommandText: (str) => chalk.white(str),
|
|
374
|
+
styleCommandDescription: (str) => chalk.dim(str),
|
|
375
|
+
styleOptionText: (str) => chalk.green(str),
|
|
376
|
+
styleArgumentText: (str) => chalk.cyan(str),
|
|
377
|
+
subcommandTerm: (cmd) => {
|
|
378
|
+
const opts = cmd.options
|
|
379
|
+
.filter((opt) => opt.flags !== "-h, --help")
|
|
380
|
+
.map((opt) => opt.short || opt.long)
|
|
381
|
+
.join(", ");
|
|
326
382
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
383
|
+
const name = chalk.white(cmd.name());
|
|
384
|
+
const args = cmd.registeredArguments
|
|
385
|
+
.map((arg) =>
|
|
386
|
+
arg.required
|
|
387
|
+
? chalk.cyan(`<${arg.name()}>`)
|
|
388
|
+
: chalk.dim(`[${arg.name()}]`)
|
|
389
|
+
)
|
|
390
|
+
.join(" ");
|
|
391
|
+
|
|
392
|
+
return [name, args, opts && chalk.dim(`[${opts}]`)]
|
|
393
|
+
.filter(Boolean)
|
|
394
|
+
.join(" ");
|
|
395
|
+
},
|
|
396
|
+
})
|
|
397
|
+
.addHelpText(
|
|
398
|
+
"after",
|
|
399
|
+
chalk.dim(
|
|
400
|
+
'\nEnable tab completion by adding this to your ~/.zshrc:\neval "$(pushwork completion)"'
|
|
401
|
+
)
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
program.parseAsync();
|