pushwork 1.1.3 → 2.0.0-a.sub.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.
Files changed (116) hide show
  1. package/CLAUDE.md +9 -5
  2. package/dist/cli/commands.d.ts +71 -0
  3. package/dist/cli/commands.d.ts.map +1 -0
  4. package/dist/cli/commands.js +794 -0
  5. package/dist/cli/commands.js.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +19 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli.js +48 -55
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands.d.ts +5 -1
  13. package/dist/commands.d.ts.map +1 -1
  14. package/dist/commands.js +262 -263
  15. package/dist/commands.js.map +1 -1
  16. package/dist/config/index.d.ts +71 -0
  17. package/dist/config/index.d.ts.map +1 -0
  18. package/dist/config/index.js +314 -0
  19. package/dist/config/index.js.map +1 -0
  20. package/dist/core/change-detection.d.ts +2 -2
  21. package/dist/core/change-detection.d.ts.map +1 -1
  22. package/dist/core/change-detection.js +82 -109
  23. package/dist/core/change-detection.js.map +1 -1
  24. package/dist/core/config.d.ts +1 -1
  25. package/dist/core/config.d.ts.map +1 -1
  26. package/dist/core/config.js +14 -57
  27. package/dist/core/config.js.map +1 -1
  28. package/dist/core/index.d.ts +5 -5
  29. package/dist/core/index.d.ts.map +1 -1
  30. package/dist/core/index.js +5 -21
  31. package/dist/core/index.js.map +1 -1
  32. package/dist/core/move-detection.d.ts +2 -2
  33. package/dist/core/move-detection.d.ts.map +1 -1
  34. package/dist/core/move-detection.js +9 -13
  35. package/dist/core/move-detection.js.map +1 -1
  36. package/dist/core/snapshot.d.ts +1 -1
  37. package/dist/core/snapshot.d.ts.map +1 -1
  38. package/dist/core/snapshot.js +9 -46
  39. package/dist/core/snapshot.js.map +1 -1
  40. package/dist/core/sync-engine.d.ts +1 -1
  41. package/dist/core/sync-engine.d.ts.map +1 -1
  42. package/dist/core/sync-engine.js +126 -151
  43. package/dist/core/sync-engine.js.map +1 -1
  44. package/dist/index.d.ts +4 -4
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -20
  47. package/dist/index.js.map +1 -1
  48. package/dist/types/config.d.ts +7 -6
  49. package/dist/types/config.d.ts.map +1 -1
  50. package/dist/types/config.js +1 -5
  51. package/dist/types/config.js.map +1 -1
  52. package/dist/types/documents.js +4 -7
  53. package/dist/types/documents.js.map +1 -1
  54. package/dist/types/index.d.ts +3 -3
  55. package/dist/types/index.d.ts.map +1 -1
  56. package/dist/types/index.js +3 -19
  57. package/dist/types/index.js.map +1 -1
  58. package/dist/types/snapshot.js +1 -2
  59. package/dist/utils/content-similarity.d.ts +53 -0
  60. package/dist/utils/content-similarity.d.ts.map +1 -0
  61. package/dist/utils/content-similarity.js +155 -0
  62. package/dist/utils/content-similarity.js.map +1 -0
  63. package/dist/utils/content.js +4 -8
  64. package/dist/utils/content.js.map +1 -1
  65. package/dist/utils/directory.js +5 -9
  66. package/dist/utils/directory.js.map +1 -1
  67. package/dist/utils/fs.d.ts +1 -1
  68. package/dist/utils/fs.d.ts.map +1 -1
  69. package/dist/utils/fs.js +34 -84
  70. package/dist/utils/fs.js.map +1 -1
  71. package/dist/utils/index.d.ts +4 -4
  72. package/dist/utils/index.d.ts.map +1 -1
  73. package/dist/utils/index.js +4 -20
  74. package/dist/utils/index.js.map +1 -1
  75. package/dist/utils/mime-types.js +5 -43
  76. package/dist/utils/mime-types.js.map +1 -1
  77. package/dist/utils/network-sync.d.ts +13 -8
  78. package/dist/utils/network-sync.d.ts.map +1 -1
  79. package/dist/utils/network-sync.js +65 -137
  80. package/dist/utils/network-sync.js.map +1 -1
  81. package/dist/utils/node-polyfills.d.ts +9 -0
  82. package/dist/utils/node-polyfills.d.ts.map +1 -0
  83. package/dist/utils/node-polyfills.js +9 -0
  84. package/dist/utils/node-polyfills.js.map +1 -0
  85. package/dist/utils/output.js +32 -39
  86. package/dist/utils/output.js.map +1 -1
  87. package/dist/utils/repo-factory.d.ts +8 -2
  88. package/dist/utils/repo-factory.d.ts.map +1 -1
  89. package/dist/utils/repo-factory.js +38 -47
  90. package/dist/utils/repo-factory.js.map +1 -1
  91. package/dist/utils/string-similarity.js +1 -5
  92. package/dist/utils/string-similarity.js.map +1 -1
  93. package/dist/utils/text-diff.js +5 -43
  94. package/dist/utils/text-diff.js.map +1 -1
  95. package/dist/utils/trace.js +6 -11
  96. package/dist/utils/trace.js.map +1 -1
  97. package/package.json +7 -5
  98. package/src/cli.ts +25 -34
  99. package/src/commands.ts +75 -11
  100. package/src/core/change-detection.ts +25 -10
  101. package/src/core/config.ts +2 -12
  102. package/src/core/index.ts +5 -5
  103. package/src/core/move-detection.ts +4 -4
  104. package/src/core/snapshot.ts +3 -3
  105. package/src/core/sync-engine.ts +24 -17
  106. package/src/index.ts +4 -4
  107. package/src/types/config.ts +8 -8
  108. package/src/types/index.ts +3 -3
  109. package/src/utils/directory.ts +1 -1
  110. package/src/utils/fs.ts +6 -4
  111. package/src/utils/index.ts +4 -4
  112. package/src/utils/network-sync.ts +62 -115
  113. package/src/utils/node-polyfills.ts +8 -0
  114. package/src/utils/repo-factory.ts +55 -10
  115. package/src/utils/trace.ts +1 -1
  116. package/tsconfig.json +2 -1
package/CLAUDE.md CHANGED
@@ -103,20 +103,24 @@ Stored in `.pushwork/config.json` (local) and `~/.pushwork/config.json` (global)
103
103
 
104
104
  Key fields:
105
105
  - `sync_enabled: boolean` - Whether to do network sync
106
- - `sync_server: string` - WebSocket relay URL (default: `wss://sync3.automerge.org`)
107
- - `sync_server_storage_id: StorageId` - Server identity for sync verification
106
+ - `sync_server: string` - WebSocket relay URL (default: `wss://subduction.sync.inkandswitch.com`)
108
107
  - `exclude_patterns: string[]` - Gitignore-style patterns (default: `.git`, `node_modules`, `*.tmp`, `.pushwork`, `.DS_Store`)
109
108
  - `sync.move_detection_threshold: number` - Similarity threshold for move detection (0-1, default 0.7)
110
109
 
111
- ## Network sync details
110
+ ## Network sync details (Subduction)
112
111
 
113
- - Uses `waitForSync()` to verify documents reach the server by comparing local and remote heads
112
+ Pushwork uses Subduction for network sync. Subduction replaces the old `BrowserWebSocketClientAdapter` with a Subduction transport layer that handles sync automatically.
113
+
114
+ - **Repo setup** (`src/utils/repo-factory.ts`): Creates a `WebCryptoSigner`, wraps `NodeFSStorageAdapter` in `SubductionStorageBridge`, hydrates `Subduction`, and calls `connectDiscover()` to the sync server. The `Repo` receives `{ subduction, storage }` instead of `{ storage, network }`.
115
+ - **Sync verification**: Uses head-stability polling instead of `StorageId`-based `getSyncInfo()`. Both `waitForSync()` and `waitForBidirectionalSync()` poll document heads until they stabilize.
116
+ - Uses `waitForSync()` to verify documents reach the server by polling head stability
114
117
  - Uses `waitForBidirectionalSync()` to poll until document heads stabilize (no more incoming changes)
115
118
  - Accepts optional `handles` param to check only specific handles instead of full tree traversal (used post-push in `sync()`)
116
119
  - Timeout scales dynamically: `max(timeoutMs, 5000 + docCount * 50)` so large trees don't prematurely time out
117
120
  - Tree traversal (`collectHeadsRecursive`) fetches siblings concurrently via `Promise.all`
118
121
  - Documents sync level-by-level, deepest first, so children are on the server before their parents
119
122
  - `handlesByPath` map tracks which documents changed and need syncing
123
+ - Default sync server: `wss://subduction.sync.inkandswitch.com`
120
124
 
121
125
  ## Leaf-first ordering
122
126
 
@@ -132,6 +136,6 @@ Used throughout sync-engine: if heads are available, calls `handle.changeAt(head
132
136
  - **Avoid diffing artifact files.** `diffChars()` is O(n*m) and pointless for artifact directories since they use RawString (immutable snapshots). Artifact files should always be replaced with a fresh document rather than diffed+spliced. This applies to `updateRemoteFile()`, `applyMoveToRemote()`, and change detection. `ChangeDetector` skips `getContentAtHead()` and `getCurrentRemoteContent()` for artifact paths — it uses a SHA-256 `contentHash` stored in the snapshot to detect local changes, and checks heads to detect remote changes. If neither changed, the artifact is skipped entirely. The `contentHash` field on `SnapshotFileEntry` is optional and only populated for artifact files.
133
137
  - **Sync timeout recovery.** `waitForSync()` returns `{ failed: DocHandle[] }` instead of throwing. When documents fail to sync (timeout or unavailable), `recreateFailedDocuments()` creates new Automerge docs with the same content, updates snapshot entries and parent directory references, then retries once. If documents still fail after recreation, it's reported as an error (not a warning) so the sync shows as "PARTIAL" rather than "SYNCED".
134
138
  - **Document availability during clone.** `repo.find()` rejects with "Document X is unavailable" if the sync server doesn't have the document yet. `DocHandle.doc()` is synchronous and throws if the handle isn't ready. For clone scenarios, `sync()` retries `repo.find()` for the root document with exponential backoff (up to 6 attempts). `ChangeDetector.findDocument()` wraps `repo.find()` + `doc()` with retry logic for all document fetches during change detection.
135
- - **Server load.** `enableRemoteHeadsGossiping` is disabled — pushwork syncs directly with the server so the gossip protocol is unnecessary overhead. `waitForSync` processes documents in batches of 10 (`SYNC_BATCH_SIZE`) to avoid flooding the server with concurrent sync messages. Without batching, syncing 100+ documents simultaneously can overwhelm the sync server (which is single-threaded with no backpressure).
139
+ - **Server load.** `waitForSync` processes documents in batches of 10 (`SYNC_BATCH_SIZE`) to avoid flooding the server with concurrent sync messages. Without batching, syncing 100+ documents simultaneously can overwhelm the sync server.
136
140
  - **`waitForBidirectionalSync` on large trees.** Full tree traversal (`getAllDocumentHeads`) is expensive because it `repo.find()`s every document. For post-push stabilization, pass the `handles` option to only check documents that actually changed. The initial pre-pull call still needs the full scan to discover remote changes. The dynamic timeout adds the first scan's duration on top of the base timeout, since the first scan is just establishing baseline — its duration shouldn't count against stability-wait time.
137
141
  - **Versioned URLs and `repo.find()`.** `repo.find(versionedUrl)` returns a view handle whose `.heads()` returns the VERSION heads, not the current document heads. Always use `getPlainUrl()` when you need the current/mutable state. The snapshot head update loop at the end of `sync()` must use `getPlainUrl(snapshotEntry.url)` — without this, artifact directories (which store versioned URLs) get stale heads written to the snapshot, causing `changeAt()` to fork from the wrong point on the next sync. This was the root cause of the artifact deletion resurrection bug: `batchUpdateDirectory` would `changeAt` from an empty directory state where the file entry didn't exist yet, so the splice found nothing to delete.
@@ -0,0 +1,71 @@
1
+ import { Repo } from "@automerge/automerge-repo";
2
+ import { CloneOptions, SyncOptions, DiffOptions, LogOptions, CheckoutOptions, DirectoryConfig } from "../types";
3
+ import { SyncEngine } from "../core";
4
+ /**
5
+ * Shared context that commands can use
6
+ */
7
+ export interface CommandContext {
8
+ repo: Repo;
9
+ syncEngine: SyncEngine;
10
+ config: DirectoryConfig;
11
+ workingDir: string;
12
+ }
13
+ /**
14
+ * Shared pre-action that ensures repository and sync engine are properly initialized
15
+ * This function always works, with or without network connectivity
16
+ */
17
+ export declare function setupCommandContext(workingDir?: string, customSyncServer?: string, customStorageId?: string, enableNetwork?: boolean): Promise<CommandContext>;
18
+ /**
19
+ * Safely shutdown a repository with proper error handling
20
+ */
21
+ export declare function safeRepoShutdown(repo: Repo, context?: string): Promise<void>;
22
+ /**
23
+ * Common progress message helpers
24
+ */
25
+ export declare const ProgressMessages: {
26
+ directoryFound: () => void;
27
+ configLoaded: () => void;
28
+ repoConnected: () => void;
29
+ syncServer: (server: string) => void;
30
+ storageId: (id: string) => void;
31
+ rootUrl: (url: string) => void;
32
+ changesWritten: () => void;
33
+ syncCompleted: (duration: number) => void;
34
+ directoryStructureCreated: () => void;
35
+ configSaved: () => void;
36
+ repoCreated: () => void;
37
+ };
38
+ /**
39
+ * Initialize sync in a directory
40
+ */
41
+ export declare function init(targetPath: string, syncServer?: string, syncServerStorageId?: string): Promise<void>;
42
+ /**
43
+ * Run bidirectional sync
44
+ */
45
+ export declare function sync(options: SyncOptions): Promise<void>;
46
+ /**
47
+ * Show differences between local and remote
48
+ */
49
+ export declare function diff(targetPath: string | undefined, options: DiffOptions): Promise<void>;
50
+ /**
51
+ * Show sync status
52
+ */
53
+ export declare function status(): Promise<void>;
54
+ /**
55
+ * Show sync history
56
+ */
57
+ export declare function log(targetPath: string | undefined, options: LogOptions): Promise<void>;
58
+ /**
59
+ * Checkout/restore from previous sync
60
+ */
61
+ export declare function checkout(syncId: string, targetPath: string | undefined, options: CheckoutOptions): Promise<void>;
62
+ /**
63
+ * Clone an existing synced directory from an AutomergeUrl
64
+ */
65
+ export declare function clone(rootUrl: string, targetPath: string, options: CloneOptions): Promise<void>;
66
+ /**
67
+ * Get the root URL for the current pushwork repository
68
+ */
69
+ export declare function url(targetPath?: string): Promise<void>;
70
+ export declare function commit(targetPath: string, dryRun?: boolean): Promise<void>;
71
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAA2B,MAAM,2BAA2B,CAAC;AAI1E,OAAO,EAEL,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,eAAe,EAEhB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,GAAE,MAAsB,EAClC,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,MAAM,EACxB,aAAa,GAAE,OAAc,GAC5B,OAAO,CAAC,cAAc,CAAC,CAqCzB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;yBAON,MAAM;oBAEX,MAAM;mBACP,MAAM;;8BAKK,MAAM;;;;CAOjC,CAAC;AA+DF;;GAEG;AACH,wBAAsB,IAAI,CACxB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Hf;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgM9D;AAED;;GAEG;AACH,wBAAsB,IAAI,CACxB,UAAU,oBAAM,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED;;GAEG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CA6H5C;AAED;;GAEG;AACH,wBAAsB,GAAG,CACvB,UAAU,oBAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,UAAU,oBAAM,EAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;GAEG;AACH,wBAAsB,KAAK,CACzB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CA6Hf;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,UAAU,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCzD;AAED,wBAAsB,MAAM,CAC1B,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAgEf"}