@narumitw/pi-sync 0.49.11 → 0.49.13
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 +112 -91
- package/dist/chunks/chunk-3NDD5QPM.ts +5 -5
- package/dist/chunks/chunk-4F3W4CSC.ts +2 -2
- package/dist/chunks/{chunk-D3S54OXI.ts → chunk-5WJNWFTG.ts} +11 -9
- package/dist/chunks/{chunk-D3S54OXI.ts.map → chunk-5WJNWFTG.ts.map} +2 -2
- package/dist/chunks/chunk-APZVBSM6.ts +2 -2
- package/dist/chunks/chunk-BHJUISB4.ts +3 -3
- package/dist/chunks/chunk-EWX3TPLQ.ts +2 -2
- package/dist/chunks/chunk-FR6CVXAL.ts +6 -6
- package/dist/chunks/chunk-HGV24P4T.ts +1 -1
- package/dist/chunks/chunk-JNOOSPEW.ts +3 -3
- package/dist/chunks/chunk-LJDSHNS3.ts +2 -2
- package/dist/chunks/{chunk-3GRWMWUZ.ts → chunk-LONPFFNP.ts} +4 -4
- package/dist/chunks/chunk-RGJ6SOYR.ts +2 -2
- package/dist/chunks/chunk-W64NEHXT.ts +3 -3
- package/dist/chunks/file-selection-4UABOP44.ts +4 -4
- package/dist/chunks/git-backend-E3UOUUOM.ts +3 -3
- package/dist/chunks/manager-result-dispatcher-RNRQA3PW.ts +18 -0
- package/dist/chunks/{manager-ui-7E25VWUP.ts → manager-ui-QO2P4V3Z.ts} +25 -22
- package/dist/chunks/{manager-ui-7E25VWUP.ts.map → manager-ui-QO2P4V3Z.ts.map} +2 -2
- package/dist/chunks/remote-selection-ui-LHSAGKGA.ts +17 -0
- package/dist/chunks/s3-backend-3QZGO2EF.ts +5 -5
- package/dist/chunks/setup-switch-X3W3FALZ.ts +8 -8
- package/dist/chunks/snapshot-WBSV4MQG.ts +4 -4
- package/dist/chunks/sync-operations-MPAFVVHA.ts +11 -11
- package/dist/chunks/sync-state-36H7ULG2.ts +5 -5
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts +3 -3
- package/dist/index.ts +18 -18
- package/package.json +7 -7
- package/src/cancellable-operation.ts +5 -4
- package/src/secret-input.ts +8 -5
- package/dist/chunks/manager-result-dispatcher-YKBLZ7KL.ts +0 -18
- package/dist/chunks/remote-selection-ui-J6OW5DKZ.ts +0 -17
- /package/dist/chunks/{chunk-3GRWMWUZ.ts.map → chunk-LONPFFNP.ts.map} +0 -0
- /package/dist/chunks/{manager-result-dispatcher-YKBLZ7KL.ts.map → manager-result-dispatcher-RNRQA3PW.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-J6OW5DKZ.ts.map → remote-selection-ui-LHSAGKGA.ts.map} +0 -0
package/README.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
# ☁️ pi-sync —
|
|
1
|
+
# ☁️ pi-sync — Sync Pi Settings Across Machines
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@narumitw/pi-sync) [](https://pi.dev) [](./LICENSE)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Sync selected Pi settings and content across machines through Git, WebDAV, Cloudflare R2, or another S3-compatible store.
|
|
6
|
+
|
|
7
|
+
Reusable storage connections hold credentials, while named sync setups define the exact remote path, included content, and automatic-sync behavior.
|
|
6
8
|
|
|
7
9
|
## ✨ Features
|
|
8
10
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- Atomic private settings writes that preserve unknown fields and reject stale concurrent edits.
|
|
17
|
-
- Fail-closed validation for missing references, mixed backend fields, duplicate remote locations, unsafe or oversized selection policies, malformed credentials, and credential-bearing Git URLs.
|
|
11
|
+
- Manages Git, WebDAV, Cloudflare R2, and general S3-compatible backends through `/sync`.
|
|
12
|
+
- Separates reusable storage connections from named sync setups and exact reviewed remote paths.
|
|
13
|
+
- Selects Pi roots, safe agent-relative paths, and the privacy-sensitive sessions root through one ordered include list.
|
|
14
|
+
- Protects changes with immutable snapshots, secret scanning, locks, conflict checks, pull backups, transactional apply, and recovery journals.
|
|
15
|
+
- Keeps snapshot selection portable and credential-free across environments.
|
|
16
|
+
- Writes settings atomically, preserves unknown fields, rejects stale edits, and fails closed on unsafe configuration.
|
|
17
|
+
- Loads a generated split runtime while preserving lazy UI and backend chunks.
|
|
18
18
|
|
|
19
19
|
## 📦 Install
|
|
20
20
|
|
|
@@ -41,13 +41,11 @@ An unbuilt checkout still declares `dist/index.ts`, but the generated entrypoint
|
|
|
41
41
|
|
|
42
42
|
## 🚀 Quick start
|
|
43
43
|
|
|
44
|
-
Run
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/sync
|
|
48
|
-
```
|
|
44
|
+
Run `/sync`, choose **Set up sync**, and review the storage connection, exact remote path, included content, automatic-sync choice, and masked credentials before saving.
|
|
45
|
+
Buckets and remote repositories must already exist.
|
|
46
|
+
Git uses existing non-interactive SSH or credential-helper configuration and never stores Git credentials.
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
## 🧭 Manager, conflicts, and recovery
|
|
51
49
|
|
|
52
50
|
The manager shows local state without contacting remote storage:
|
|
53
51
|
|
|
@@ -59,34 +57,26 @@ Automatic sync: On
|
|
|
59
57
|
Remote status: Not checked
|
|
60
58
|
```
|
|
61
59
|
|
|
62
|
-
Primary actions include **Sync now**, **Switch sync setup**, **Status & changes**, **Settings**, and
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
and
|
|
68
|
-
Destructive, credential-bearing, and externally visible operations retain exact specialized previews
|
|
69
|
-
and confirmations.
|
|
60
|
+
Primary actions include **Sync now**, **Switch sync setup**, **Status & changes**, **Settings**, and **More…**.
|
|
61
|
+
When pi-sync has already detected a synced-content-list mismatch, the manager instead shows **Sync status: Review needed**, puts **Review synced content (recommended)** first, and keeps **Sync now** visibly unavailable until the choice is reviewed.
|
|
62
|
+
Opening the manager still performs no new remote request.
|
|
63
|
+
The standard manager also owns setup and connection lists/details plus history and recovery.
|
|
64
|
+
Secondary screens provide **Back**; Escape goes back and Ctrl+C closes the flow.
|
|
65
|
+
Specialized operation and masked-credential prompts show the effective cancellation bindings and retain Ctrl+C as a hard-cancel input when Back is remapped.
|
|
66
|
+
Destructive, credential-bearing, and externally visible operations retain exact specialized previews and confirmations.
|
|
70
67
|
|
|
71
68
|
### Restore sync access
|
|
72
69
|
|
|
73
|
-
When an operation is currently running, the manager shows its command and process ID, keeps sync and
|
|
74
|
-
|
|
75
|
-
When lock metadata is still protected by an active guard, the manager explains that another pi-sync
|
|
76
|
-
process may be starting or finishing and asks the user to wait before refreshing.
|
|
70
|
+
When an operation is currently running, the manager shows its command and process ID, keeps sync and settings changes unavailable, and puts **Refresh operation status** first.
|
|
71
|
+
When lock metadata is still protected by an active guard, the manager explains that another pi-sync process may be starting or finishing and asks the user to wait before refreshing.
|
|
77
72
|
It never offers lock removal while an owner or guard may still be active.
|
|
78
73
|
|
|
79
|
-
When a previous operation appears to have stopped without releasing its local lock, the manager shows
|
|
80
|
-
**Sync paused** and puts **Restore sync access… (recommended)** first instead of hiding recovery
|
|
81
|
-
inside **History & recovery…**.
|
|
74
|
+
When a previous operation appears to have stopped without releasing its local lock, the manager shows **Sync paused** and puts **Restore sync access… (recommended)** first instead of hiding recovery inside **History & recovery…**.
|
|
82
75
|
Unreadable metadata receives a stronger warning because pi-sync cannot verify who owns it.
|
|
83
|
-
Close every other Pi session that may still be syncing, review the local-lock-only confirmation, then
|
|
84
|
-
|
|
85
|
-
The guarded recovery path rechecks metadata and ownership before removal, changes no settings, local
|
|
86
|
-
files, sync state, or remote data, and returns directly to the normal manager when access is restored.
|
|
76
|
+
Close every other Pi session that may still be syncing, review the local-lock-only confirmation, then choose **Remove local lock and continue**.
|
|
77
|
+
The guarded recovery path rechecks metadata and ownership before removal, changes no settings, local files, sync state, or remote data, and returns directly to the normal manager when access is restored.
|
|
87
78
|
Cancellation leaves the lock unchanged.
|
|
88
|
-
If ownership changes or a guard is still expiring, pi-sync refuses removal and keeps actionable status
|
|
89
|
-
available for refresh or retry.
|
|
79
|
+
If ownership changes or a guard is still expiring, pi-sync refuses removal and keeps actionable status available for refresh or retry.
|
|
90
80
|
The deterministic fallback remains `/sync unlock --stale` after the same no-other-sync verification.
|
|
91
81
|
|
|
92
82
|
### Resolve conflicts in the manager
|
|
@@ -106,36 +96,30 @@ Then choose one action:
|
|
|
106
96
|
- **Keep this device's content list and update remote…** opens the existing `push --force` preparation and exact confirmation without `--yes`.
|
|
107
97
|
Cancelling preparation or confirmation returns to the content-list choice without changing remote data.
|
|
108
98
|
- **Cancel** returns to the manager without changing settings, files, remote data, or sync state.
|
|
109
|
-
- **Later** appears when automatic startup sync detected the mismatch and returns immediately to the
|
|
110
|
-
Pi editor without changing anything.
|
|
99
|
+
- **Later** appears when automatic startup sync detected the mismatch and returns immediately to the Pi editor without changing anything.
|
|
111
100
|
|
|
112
101
|
Choose **Review differences (recommended)** in an ordinary file-direction conflict to inspect the exact affected paths without changing local files, remote data, or sync state.
|
|
113
102
|
Then choose one reviewed direction:
|
|
114
103
|
|
|
115
|
-
- **Keep local content and replace remote…** uses the existing forced-push path.
|
|
116
|
-
local files for secrets, shows the exact remote publication effect, re-reads a changed remote head,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
empty remote offers **Push local content…** only.
|
|
104
|
+
- **Keep local content and replace remote…** uses the existing forced-push path.
|
|
105
|
+
It scans managed local files for secrets, shows the exact remote publication effect, re-reads a changed remote head, and asks again if the reviewed plan changed.
|
|
106
|
+
- **Use remote content and replace local…** uses the existing forced-pull path.
|
|
107
|
+
It shows exact local writes and deletions, protects the live session, and creates a local backup before applying.
|
|
108
|
+
- First sync uses **Use local as initial source…** and **Use remote as initial source…** labels.
|
|
109
|
+
An empty remote offers **Push local content…** only.
|
|
122
110
|
|
|
123
111
|
Cancelling a preparation or confirmation returns to conflict resolution with no side effects.
|
|
124
112
|
Back returns to the sync manager, and Ctrl+C closes the complete flow.
|
|
125
113
|
|
|
126
|
-
When automatic startup sync detects an explicit content-list mismatch in TUI mode, it opens
|
|
127
|
-
**
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
file contents, and is revalidated before any settings or remote mutation. It clears after verified
|
|
131
|
-
resolution, invalidation, session replacement, or shutdown.
|
|
114
|
+
When automatic startup sync detects an explicit content-list mismatch in TUI mode, it opens **Synced content differs** once for that session instead of ending at a warning.
|
|
115
|
+
Choosing **Later** or closing the flow leaves a compact **Pi Sync needs review** widget above the editor and a **review needed** footer status.
|
|
116
|
+
The attention state is in memory only, contains no credentials or file contents, and is revalidated before any settings or remote mutation.
|
|
117
|
+
It clears after verified resolution, invalidation, session replacement, or shutdown.
|
|
132
118
|
|
|
133
|
-
Interactive TUI `/sync sync`, `/sync pull`, and `/sync push` routes without `--yes` open the same
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
RPC startup and direct routes remain read-only and notification-based, while print and JSON modes
|
|
138
|
-
remain unsupported for `/sync` because their UI output is not observable.
|
|
119
|
+
Interactive TUI `/sync sync`, `/sync pull`, and `/sync push` routes without `--yes` open the same review flow when they detect the mismatch.
|
|
120
|
+
Explicit `--yes` routes remain non-interactive and report exact remote-only, device-only, or order-only guidance while leaving visible attention for later review.
|
|
121
|
+
Shutdown automatic sync never opens a dialog because Pi is exiting.
|
|
122
|
+
RPC startup and direct routes remain read-only and notification-based, while print and JSON modes remain unsupported for `/sync` because their UI output is not observable.
|
|
139
123
|
|
|
140
124
|
## ⚙️ Settings
|
|
141
125
|
|
|
@@ -145,9 +129,15 @@ The canonical private user file is:
|
|
|
145
129
|
~/.pi/agent/pi-sync.json
|
|
146
130
|
```
|
|
147
131
|
|
|
148
|
-
Pi's configured agent directory replaces `~/.pi/agent` when applicable.
|
|
132
|
+
Pi's configured agent directory replaces `~/.pi/agent` when applicable.
|
|
133
|
+
Missing settings load as unconfigured without creating an agent directory, file, temporary, or lock.
|
|
134
|
+
An explicit setup creates the file atomically.
|
|
135
|
+
On POSIX, pi-sync creates and replaces it with mode `0600`.
|
|
136
|
+
Pi-sync processes coordinate settings access through `pi-sync.json.mutation-lock`; lock-unaware editors are outside that serialization boundary and should not save the file while a pi-sync settings operation is running.
|
|
137
|
+
Credentials stay in this canonical private file and are never shown in menus, reviews, status, notifications, errors, logs, or completion metadata.
|
|
149
138
|
|
|
150
|
-
An existing private `pi-sync.local.json` containing a valid version 3 document is copied byte-for-byte to `pi-sync.json`; the old file remains as a recovery copy.
|
|
139
|
+
An existing private `pi-sync.local.json` containing a valid version 3 document is copied byte-for-byte to `pi-sync.json`; the old file remains as a recovery copy.
|
|
140
|
+
If both paths exist, `pi-sync.json` wins and the other path remains untouched.
|
|
151
141
|
|
|
152
142
|
### Complete version 3 example
|
|
153
143
|
|
|
@@ -216,7 +206,8 @@ An existing private `pi-sync.local.json` containing a valid version 3 document i
|
|
|
216
206
|
}
|
|
217
207
|
```
|
|
218
208
|
|
|
219
|
-
Cloudflare R2 is persisted as `"type": "s3"`; R2 is a setup preset, not another schema type.
|
|
209
|
+
Cloudflare R2 is persisted as `"type": "s3"`; R2 is a setup preset, not another schema type.
|
|
210
|
+
Temporary S3 credentials may additionally include `credentials.sessionToken`.
|
|
220
211
|
|
|
221
212
|
### Required backend shapes
|
|
222
213
|
|
|
@@ -227,7 +218,11 @@ Cloudflare R2 is persisted as `"type": "s3"`; R2 is a setup preset, not another
|
|
|
227
218
|
- **WebDAV connection:** `type`, HTTPS `url`, and `credentials.username` / `credentials.password`.
|
|
228
219
|
- **WebDAV setup storage:** `connection` and complete relative `path`; `bucket` and `branch` are rejected.
|
|
229
220
|
|
|
230
|
-
Every setup requires `sync.include` and explicit `sync.automatic`.
|
|
221
|
+
Every setup requires `sync.include` and explicit `sync.automatic`.
|
|
222
|
+
`activeSyncSetup` must reference an own-property setup when any setups exist and must be absent when the setup catalog is empty.
|
|
223
|
+
A referenced connection cannot be removed.
|
|
224
|
+
The current setup must be switched before removal.
|
|
225
|
+
Two setups cannot resolve to the same normalized backend location.
|
|
231
226
|
|
|
232
227
|
`onSwitch` accepts:
|
|
233
228
|
|
|
@@ -237,24 +232,26 @@ Every setup requires `sync.include` and explicit `sync.automatic`. `activeSyncSe
|
|
|
237
232
|
|
|
238
233
|
### Included content
|
|
239
234
|
|
|
240
|
-
`sync.include` is ordered and duplicate-free.
|
|
235
|
+
`sync.include` is ordered and duplicate-free.
|
|
236
|
+
Supported Pi roots are:
|
|
241
237
|
|
|
242
238
|
```text
|
|
243
239
|
settings.json, keybindings.json, models.json, AGENTS.md, APPEND_SYSTEM.md,
|
|
244
240
|
skills, prompts, themes, extensions, sessions
|
|
245
241
|
```
|
|
246
242
|
|
|
247
|
-
Safe agent-relative custom files or directories may also be included.
|
|
243
|
+
Safe agent-relative custom files or directories may also be included.
|
|
244
|
+
Absolute paths, `..`, backslashes, controls, denied secret/settings paths, duplicate case variants, and ambiguous nested paths under reserved roots are rejected.
|
|
248
245
|
|
|
249
|
-
An empty array is valid.
|
|
246
|
+
An empty array is valid.
|
|
247
|
+
It means no useful transfer is selected: **Sync now** reports the condition and does not claim that the setup is up to date.
|
|
248
|
+
Unselected content remains unmanaged locally and is preserved when republishing existing remote snapshots.
|
|
250
249
|
|
|
251
|
-
The Included Content editor is a standard bounded multi-select backed by one in-memory draft.
|
|
252
|
-
never write settings.
|
|
253
|
-
it does not exist locally yet, allowing a new environment to select content that exists only in the
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
the draft and Discard/cancellation preserves the settings bytes. RPC remains a read-only summary with
|
|
257
|
-
the manual `sync.include` path.
|
|
250
|
+
The Included Content editor is a standard bounded multi-select backed by one in-memory draft.
|
|
251
|
+
Toggles never write settings.
|
|
252
|
+
**Add custom path…** accepts a safe agent-relative file or directory even when it does not exist locally yet, allowing a new environment to select content that exists only in the remote snapshot.
|
|
253
|
+
Leaving the editor opens an exact Include/Exclude review with **Save changes**, **Discard changes**, and **Continue editing**; only reviewed Save publishes, while Continue preserves the draft and Discard/cancellation preserves the settings bytes.
|
|
254
|
+
RPC remains a read-only summary with the manual `sync.include` path.
|
|
258
255
|
|
|
259
256
|
Every new snapshot stores the normalized included-content selection separately from the files that happened to exist.
|
|
260
257
|
This preserves selected-but-missing paths without syncing `pi-sync.json`, storage credentials, automatic-sync preferences, or setup names.
|
|
@@ -266,15 +263,18 @@ Keeping this device's list opens the reviewed force-push path and explicitly rep
|
|
|
266
263
|
|
|
267
264
|
Automatic sync and pull, including forced pull, pause on an explicit remote-policy difference rather than silently expanding local scope.
|
|
268
265
|
Status reports matches and exact local-only/remote-only paths.
|
|
269
|
-
Old snapshots remain readable.
|
|
270
|
-
clearly labeled read-only partial discovery from safe remote file roots; selected-but-missing and
|
|
271
|
-
|
|
266
|
+
Old snapshots remain readable.
|
|
267
|
+
Because they have no authoritative selection, pi-sync offers only a clearly labeled read-only partial discovery from safe remote file roots; selected-but-missing and preserved-unmanaged intent cannot be reconstructed.
|
|
268
|
+
Use **Add custom path…** for any needed path.
|
|
272
269
|
|
|
273
|
-
Adding `sessions` requires a privacy acknowledgement in interactive flows.
|
|
270
|
+
Adding `sessions` requires a privacy acknowledgement in interactive flows.
|
|
271
|
+
Session JSONL can contain prompts, tool output, file paths, images, and secrets.
|
|
272
|
+
Automatic apply protects the currently open session file; restart Pi or resume a pulled session to use newly synchronized conversations.
|
|
274
273
|
|
|
275
274
|
### Unsupported old settings and recovery
|
|
276
275
|
|
|
277
|
-
Version 1, version 2, and non-empty unversioned documents are intentionally unsupported by this breaking schema reset. pi-sync does **not** migrate, partially interpret, downgrade, or overwrite them.
|
|
276
|
+
Version 1, version 2, and non-empty unversioned documents are intentionally unsupported by this breaking schema reset. pi-sync does **not** migrate, partially interpret, downgrade, or overwrite them.
|
|
277
|
+
Automatic sync pauses and reports an actionable version 3 error without displaying secrets.
|
|
278
278
|
|
|
279
279
|
Recovery:
|
|
280
280
|
|
|
@@ -284,7 +284,8 @@ Recovery:
|
|
|
284
284
|
4. run `/sync doctor`, inspect the exact storage path, and review the first pull or push;
|
|
285
285
|
5. restore the retained file and a compatible older package only if rolling back.
|
|
286
286
|
|
|
287
|
-
Malformed, invalid, unsupported, symlinked, or concurrently changed documents remain untouched.
|
|
287
|
+
Malformed, invalid, unsupported, symlinked, or concurrently changed documents remain untouched.
|
|
288
|
+
Failed UI saves keep the previous file and displayed/effective state.
|
|
288
289
|
|
|
289
290
|
## 💬 Commands
|
|
290
291
|
|
|
@@ -313,11 +314,13 @@ The menu is preferred, while deterministic routes remain available:
|
|
|
313
314
|
- `--force` accepts a reviewed content conflict but never disables backend concurrency protection.
|
|
314
315
|
- `--stale` requests guarded stale-lock recovery.
|
|
315
316
|
|
|
316
|
-
The former version 2 setup-addressing flag is rejected.
|
|
317
|
+
The former version 2 setup-addressing flag is rejected.
|
|
318
|
+
Unknown flags, unknown commands, trailing values, and missing setup/snapshot values are rejected.
|
|
319
|
+
Completion includes known setup names and preserves preceding command tokens.
|
|
317
320
|
|
|
318
|
-
TUI mode uses standard manager, settings, resource, and included-content screens plus specialized
|
|
319
|
-
|
|
320
|
-
|
|
321
|
+
TUI mode uses standard manager, settings, resource, and included-content screens plus specialized secret, wizard, confirmation, and commit-aware loader components.
|
|
322
|
+
RPC uses Pi's supported dialog/notification protocol and does not gain settings mutation.
|
|
323
|
+
Print and JSON modes never enter TUI-only screens; unsupported interactive routes reject or remain protocol-safe rather than relying on no-op output.
|
|
321
324
|
|
|
322
325
|
## 🔄 Backend and recovery model
|
|
323
326
|
|
|
@@ -327,29 +330,47 @@ dialog/notification protocol and does not gain settings mutation. Print and JSON
|
|
|
327
330
|
| WebDAV | Verified strong conditional requests | Private settings username/app password | `<url>/<storage.path>` |
|
|
328
331
|
| R2/S3 | Read-check-write-verify | Private settings credentials | `<bucket>/<storage.path>` |
|
|
329
332
|
|
|
330
|
-
Git requires Git 2.30 or newer and a SHA-1-format remote repository.
|
|
333
|
+
Git requires Git 2.30 or newer and a SHA-1-format remote repository.
|
|
334
|
+
HTTPS userinfo, URL passwords, local paths, `file`, `git`, `ext`, and remote-helper transports are rejected.
|
|
335
|
+
When editing a Git setup, changing its storage path also requires a new owned branch so the existing branch remains readable at its reviewed path.
|
|
336
|
+
The private bare cache under `<agent-dir>/pi-sync/git/` is rebuildable.
|
|
331
337
|
|
|
332
|
-
WebDAV requires HTTPS except loopback tests.
|
|
338
|
+
WebDAV requires HTTPS except loopback tests.
|
|
339
|
+
URL credentials, query strings, fragments, unsafe redirects, weak/missing ETags, and ignored conditional headers fail closed.
|
|
340
|
+
`/sync doctor` verifies collection and conditional-write behavior with an isolated probe.
|
|
333
341
|
|
|
334
|
-
S3/R2 stages immutable bundles, rechecks the visible head before publication, and verifies afterward.
|
|
342
|
+
S3/R2 stages immutable bundles, rechecks the visible head before publication, and verifies afterward.
|
|
343
|
+
Unlike Git/WebDAV, generic S3 does not provide an atomic compare-and-swap for `latest.json`; status review remains important for simultaneous writers.
|
|
335
344
|
|
|
336
|
-
Before pull or rollback, pi-sync writes a backup under `<agent-dir>/pi-sync/backups/`.
|
|
345
|
+
Before pull or rollback, pi-sync writes a backup under `<agent-dir>/pi-sync/backups/`.
|
|
346
|
+
Apply preflights paths and checksums, journals all mutations, restores the prior state after failures, and recovers interrupted journals on startup.
|
|
347
|
+
Removing a local setup or connection never deletes remote data.
|
|
337
348
|
|
|
338
|
-
The operational state root is `<agent-dir>/pi-sync/`.
|
|
349
|
+
The operational state root is `<agent-dir>/pi-sync/`.
|
|
350
|
+
An existing installation continues using `<agent-dir>/.pisync/` and shows migration guidance on startup; it is never moved merely because no sync is active.
|
|
351
|
+
Close every other Pi process, then run `/sync migrate-state` and confirm the review (or pass `--yes` for an already reviewed RPC workflow).
|
|
352
|
+
The command serializes against every upgraded state/cache user and atomically renames `.pisync/` only when no legacy sync lock or guard is active.
|
|
353
|
+
Close Pi instances running older pi-sync versions during the migration because they do not understand the migration guard.
|
|
354
|
+
If both roots exist, or either root is a symlink or non-directory, pi-sync refuses stateful work instead of merging, following, or deleting data.
|
|
355
|
+
Preserve both roots before manual recovery; with every Pi process closed and no destination conflict, rollback is an atomic rename from `pi-sync/` back to `.pisync/`.
|
|
339
356
|
|
|
340
|
-
##
|
|
357
|
+
## 🔒 Security and privacy
|
|
341
358
|
|
|
342
359
|
- Canonical, legacy, temporary, and recovery settings paths are denied from snapshots; both `pi-sync/` and `.pisync/` state roots are permanently denied.
|
|
343
360
|
- Push scans managed local content for common secret patterns.
|
|
344
361
|
- Remote snapshot references, checksums, paths, manifests, response sizes, and publication revisions are validated.
|
|
345
362
|
- Symlink parents, path escapes, duplicate paths, and unsafe file/directory replacement fail before local mutation.
|
|
346
363
|
- Live locks block mutation; stale recovery rechecks process and guard ownership.
|
|
347
|
-
- Cancellation aborts preparation and dialogs.
|
|
364
|
+
- Cancellation aborts preparation and dialogs.
|
|
365
|
+
Publication/apply commit boundaries finish with bounded signals and report ambiguous outcomes explicitly.
|
|
348
366
|
- Terminal-bound names, paths, metadata, and errors are control-character sanitized.
|
|
349
367
|
|
|
350
368
|
## ♿ Conditional terminal accessibility smoke
|
|
351
369
|
|
|
352
|
-
Pi exposes terminal components rather than a semantic/ARIA tree.
|
|
370
|
+
Pi exposes terminal components rather than a semantic/ARIA tree.
|
|
371
|
+
Release validation checks textual state, keyboard operation, Escape/Back, control escaping, and narrow rendering.
|
|
372
|
+
Critical meaning appears in text such as `(current)`, `Review needed`, `Later`, `Warning`, `Invalid`, `Saved`, `Cancelled`, and `Applied`; color is supplementary.
|
|
373
|
+
The attention widget is informational and does not pretend to be a clickable control.
|
|
353
374
|
|
|
354
375
|
## 🗂️ Package layout
|
|
355
376
|
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
3
|
import {
|
|
4
4
|
withLock
|
|
5
|
-
} from "./chunk-LJDSHNS3.
|
|
5
|
+
} from "./chunk-LJDSHNS3.ts";
|
|
6
6
|
import {
|
|
7
7
|
sessionStorageRoot
|
|
8
|
-
} from "./chunk-EWX3TPLQ.
|
|
8
|
+
} from "./chunk-EWX3TPLQ.ts";
|
|
9
9
|
import {
|
|
10
10
|
agentDir
|
|
11
|
-
} from "./chunk-APZVBSM6.
|
|
11
|
+
} from "./chunk-APZVBSM6.ts";
|
|
12
12
|
import {
|
|
13
13
|
stateDir
|
|
14
|
-
} from "./chunk-F5QL2GPY.
|
|
14
|
+
} from "./chunk-F5QL2GPY.ts";
|
|
15
15
|
import {
|
|
16
16
|
assertWithinRoot,
|
|
17
17
|
isPathInside
|
|
18
|
-
} from "./chunk-YQ6UW7IF.
|
|
18
|
+
} from "./chunk-YQ6UW7IF.ts";
|
|
19
19
|
|
|
20
20
|
// src/snapshot-transaction.ts
|
|
21
21
|
import { randomUUID } from "node:crypto";
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
syncSetupStorageReview,
|
|
7
7
|
updateLocalConfig,
|
|
8
8
|
validateConfigName
|
|
9
|
-
} from "./chunk-APZVBSM6.
|
|
9
|
+
} from "./chunk-APZVBSM6.ts";
|
|
10
10
|
import {
|
|
11
11
|
normalizeSyncInclude
|
|
12
|
-
} from "./chunk-YQ6UW7IF.
|
|
12
|
+
} from "./chunk-YQ6UW7IF.ts";
|
|
13
13
|
|
|
14
14
|
// src/settings-management.ts
|
|
15
15
|
var SyncSetupReviewChangedError = class extends Error {
|
|
@@ -3,25 +3,25 @@
|
|
|
3
3
|
import {
|
|
4
4
|
SyncSetupReviewChangedError,
|
|
5
5
|
updateSyncSetup
|
|
6
|
-
} from "./chunk-4F3W4CSC.
|
|
6
|
+
} from "./chunk-4F3W4CSC.ts";
|
|
7
7
|
import {
|
|
8
8
|
createSyncBackend,
|
|
9
9
|
readSnapshotForHead
|
|
10
|
-
} from "./chunk-FR6CVXAL.
|
|
10
|
+
} from "./chunk-FR6CVXAL.ts";
|
|
11
11
|
import {
|
|
12
12
|
errorMessage,
|
|
13
13
|
safeTerminalText
|
|
14
|
-
} from "./chunk-W64NEHXT.
|
|
14
|
+
} from "./chunk-W64NEHXT.ts";
|
|
15
15
|
import {
|
|
16
16
|
loadConfig,
|
|
17
17
|
loadPartialConfig,
|
|
18
18
|
syncConfigReviewFingerprint
|
|
19
|
-
} from "./chunk-APZVBSM6.
|
|
19
|
+
} from "./chunk-APZVBSM6.ts";
|
|
20
20
|
import {
|
|
21
21
|
compareSyncInclude,
|
|
22
22
|
inspectRemoteSelection,
|
|
23
23
|
sameSyncInclude
|
|
24
|
-
} from "./chunk-YQ6UW7IF.
|
|
24
|
+
} from "./chunk-YQ6UW7IF.ts";
|
|
25
25
|
|
|
26
26
|
// src/remote-selection-ui.ts
|
|
27
27
|
import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
@@ -30,7 +30,7 @@ import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
|
30
30
|
import {
|
|
31
31
|
BorderedLoader
|
|
32
32
|
} from "@earendil-works/pi-coding-agent";
|
|
33
|
-
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
33
|
+
import { Key, matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
|
|
34
34
|
import { runCustomInteraction } from "@narumitw/pi-tui-kit";
|
|
35
35
|
|
|
36
36
|
// src/manager-helpers.ts
|
|
@@ -110,7 +110,9 @@ async function runCancellableOperation(ctx, message, route, runRoute, options =
|
|
|
110
110
|
},
|
|
111
111
|
invalidate: () => loader.invalidate(),
|
|
112
112
|
handleInput(data) {
|
|
113
|
-
if (!keybindings.matches(data, "tui.select.cancel"))
|
|
113
|
+
if (!matchesKey(data, Key.ctrl("c")) && !keybindings.matches(data, "tui.select.cancel")) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
114
116
|
if (commitStarted) {
|
|
115
117
|
ctx.ui.notify(
|
|
116
118
|
"Applying or publishing has started and cannot be cancelled safely.",
|
|
@@ -135,7 +137,7 @@ async function runCancellableOperation(ctx, message, route, runRoute, options =
|
|
|
135
137
|
return routeResult ?? { kind: "failed" };
|
|
136
138
|
}
|
|
137
139
|
function keybindingText(keybindings, binding, fallback) {
|
|
138
|
-
const keys = keybindings.getKeys(binding).map(String).map((key) => {
|
|
140
|
+
const keys = [.../* @__PURE__ */ new Set([...keybindings.getKeys(binding), "ctrl+c"])].map(String).map((key) => {
|
|
139
141
|
if (key === "return") return "enter";
|
|
140
142
|
if (key === "escape") return "esc";
|
|
141
143
|
return safeTerminalText2(key);
|
|
@@ -650,4 +652,4 @@ export {
|
|
|
650
652
|
runCancellableOperation,
|
|
651
653
|
showRemoteSelectionReview
|
|
652
654
|
};
|
|
653
|
-
//# sourceMappingURL=chunk-
|
|
655
|
+
//# sourceMappingURL=chunk-5WJNWFTG.ts.map
|