@narumitw/pi-sync 0.28.0 β 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -142
- package/package.json +1 -1
- package/src/command.ts +102 -7
- package/src/config.ts +363 -33
- package/src/file-selection.ts +158 -70
- package/src/lock.ts +22 -5
- package/src/manager-ui.ts +961 -0
- package/src/s3-client.ts +47 -13
- package/src/settings-management.ts +302 -0
- package/src/snapshot-apply.ts +6 -6
- package/src/snapshot-transaction.ts +210 -0
- package/src/sync-format.ts +168 -0
- package/src/sync.ts +217 -136
- package/src/types.ts +43 -0
package/README.md
CHANGED
|
@@ -2,26 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@narumitw/pi-sync) [](https://pi.dev) [](./LICENSE)
|
|
4
4
|
|
|
5
|
-
`@narumitw/pi-sync`
|
|
5
|
+
`@narumitw/pi-sync` syncs selected Pi configuration through Cloudflare R2 or other S3-compatible object storage. Named sync targets such as `home` and `work` can reuse storage profiles such as `r2` and `s3`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The extension uses immutable snapshot bundles, a `latest.json` publication pointer, local locking, secret scanning, pre-apply backups, and recoverable local apply transactions. Conversation/session syncing remains opt-in because session JSONL can contain prompts, tool output, paths, screenshots, and secrets.
|
|
8
8
|
|
|
9
9
|
## β¨ Features
|
|
10
10
|
|
|
11
|
-
- Opens
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- Opens a goal-oriented `/sync` manager showing the current target, storage, auto-sync, session scope, and relevant next actions.
|
|
12
|
+
- Supports multiple named **sync targets** and reusable R2/S3 **storage profiles**.
|
|
13
|
+
- Switches targets without syncing or modifying files; only the current target runs automatic startup/shutdown sync.
|
|
14
|
+
- Previews concrete local or remote file changes before push, pull, force resolution, or rollback.
|
|
15
|
+
- Uses transactional synced-content drafts with explicit Save, Discard, and Continue editing choices.
|
|
16
|
+
- Keeps direct `help`, `use`, `init`, `config`, `files`, `status`, `diff`, `doctor`, `push`, `pull`, `sync`, `history`, `rollback`, and `unlock` routes for compatibility and automation.
|
|
17
|
+
- Syncs allowlisted Pi configuration from the Pi agent directory:
|
|
14
18
|
- `settings.json`, `keybindings.json`, `models.json`, `AGENTS.md`, and `APPEND_SYSTEM.md`
|
|
15
19
|
- recursive `skills/`, `prompts/`, `themes/`, and `extensions/` groups
|
|
16
20
|
- safe top-level files selected through `extraFiles`
|
|
17
|
-
- optionally denylist-filtered `sessions/**/*.jsonl`
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Runs `/sync sync` automatically on Pi startup when R2/S3 config is present.
|
|
22
|
-
- Uses a local exclusive lock at `~/.pi/agent/.pisync/lock` for destructive sync operations and only treats locks as stale after checking process liveness.
|
|
23
|
-
- Refuses to push common secret patterns and denylisted paths such as `.env`, `.env.local`, token/secret files, `.pisync`, `.git`, and `node_modules`.
|
|
24
|
-
- Preflights snapshot apply operations before mutating local files, refuses symlink path escapes, and rejects writes over symlinks or directories.
|
|
21
|
+
- optionally denylist-filtered `sessions/**/*.jsonl`
|
|
22
|
+
- Preserves files outside the addressed target's selection locally and in remote uploads.
|
|
23
|
+
- Creates backups before pull/rollback and journals multi-file applies so failures roll back; interrupted transactions recover before the next sync.
|
|
24
|
+
- Refuses common secret patterns, unsafe paths, symlink escapes, live-lock removal, and unreviewed conflict overwrites.
|
|
25
25
|
|
|
26
26
|
## π¦ Install
|
|
27
27
|
|
|
@@ -35,212 +35,297 @@ Try without installing permanently:
|
|
|
35
35
|
pi -e npm:@narumitw/pi-sync
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Try this package
|
|
38
|
+
Try this package from a local checkout:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
pi -e ./extensions/pi-sync
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## π Quick start
|
|
45
45
|
|
|
46
|
-
Run:
|
|
46
|
+
Run the manager:
|
|
47
47
|
|
|
48
48
|
```text
|
|
49
|
-
/sync
|
|
49
|
+
/sync
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```text
|
|
55
|
-
~/.pi/agent/pi-sync.local.json
|
|
56
|
-
```
|
|
52
|
+
When pi-sync is not configured, choose **Set up sync**. The TUI guides you through:
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
1. Cloudflare R2 or another S3-compatible service
|
|
55
|
+
2. `Personal / Home`, `Work`, or a custom target purpose
|
|
56
|
+
3. an endpoint and, for S3, the existing bucket name
|
|
57
|
+
4. a recommended remote location or advanced customization
|
|
58
|
+
5. environment credentials or a private settings-file template
|
|
59
|
+
6. a synced-content preset
|
|
60
|
+
7. an exact setup preview and **Save setup** confirmation
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
For a first Cloudflare R2 target, the recommended location requires no raw path questions:
|
|
61
63
|
|
|
62
64
|
```json
|
|
63
65
|
{
|
|
64
|
-
"
|
|
66
|
+
"profile": "r2",
|
|
65
67
|
"bucket": "pi-sync",
|
|
66
|
-
"region": "auto",
|
|
67
|
-
"accessKeyId": "<access-key-id>",
|
|
68
|
-
"secretAccessKey": "<secret-access-key>",
|
|
69
|
-
"profile": "default",
|
|
70
68
|
"prefix": "pi-sync",
|
|
71
|
-
"
|
|
72
|
-
"syncFiles": [
|
|
73
|
-
"settings.json",
|
|
74
|
-
"keybindings.json",
|
|
75
|
-
"models.json",
|
|
76
|
-
"AGENTS.md",
|
|
77
|
-
"APPEND_SYSTEM.md",
|
|
78
|
-
"skills",
|
|
79
|
-
"prompts",
|
|
80
|
-
"themes",
|
|
81
|
-
"extensions"
|
|
82
|
-
],
|
|
83
|
-
"syncSessions": false,
|
|
84
|
-
"extraFiles": []
|
|
69
|
+
"namespace": "home"
|
|
85
70
|
}
|
|
86
71
|
```
|
|
87
72
|
|
|
88
|
-
|
|
73
|
+
The R2 bucket must already exist; pi-sync never creates buckets. Generic S3 setup asks for one existing, potentially globally unique bucket and derives storage profile `s3`, prefix `pi-sync`, and namespace `home` or `work`. **Customize remote location** retains direct control over profile name, bucket, prefix, and namespace.
|
|
89
74
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
Pi's extension input API does not provide masked secret entry, so pi-sync never asks for a secret in an unmasked dialog. Use existing environment credentials or add credentials manually to the private settings file. Secret values are never shown in menus, status, warnings, or errors.
|
|
76
|
+
|
|
77
|
+
A configured manager shows:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
Current target: home
|
|
81
|
+
Storage: Cloudflare R2 Β· personal-pi
|
|
82
|
+
Auto-sync: On Β· Sessions: Off
|
|
83
|
+
Last known sync: Remote not checked
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Its primary actions are:
|
|
87
|
+
|
|
88
|
+
- **Sync now** β conservatively decide whether to push, pull, or do nothing
|
|
89
|
+
- **Switch target** β preview and change the current target without syncing
|
|
90
|
+
- **Status & changes** β perform a cancellable read-only remote check
|
|
91
|
+
- **Settings** β control automatic sync and synced content for the current target
|
|
92
|
+
- **Manage targets & storage** β add, edit, or remove local target/profile definitions
|
|
93
|
+
- **History & recovery** β browse snapshots, preview rollback, diagnose setup, or recover a stale lock
|
|
94
|
+
- **Help**
|
|
95
|
+
|
|
96
|
+
Escape exits the main menu. Secondary menus provide Back; dirty synced-content drafts provide Save, Discard, and Continue editing. Cancellation before publication has no side effects.
|
|
97
|
+
|
|
98
|
+
## βοΈ Settings
|
|
99
|
+
|
|
100
|
+
The private user settings file is:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
${PI_CODING_AGENT_DIR:-~/.pi/agent}/pi-sync.local.json
|
|
101
104
|
```
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
A version 2 example:
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"version": 2,
|
|
111
|
+
"activeTarget": "home",
|
|
112
|
+
"profiles": {
|
|
113
|
+
"r2": {
|
|
114
|
+
"kind": "r2",
|
|
115
|
+
"endpoint": "https://<account-id>.r2.cloudflarestorage.com",
|
|
116
|
+
"region": "auto",
|
|
117
|
+
"accessKeyId": "<access-key-id>",
|
|
118
|
+
"secretAccessKey": "<secret-access-key>"
|
|
119
|
+
},
|
|
120
|
+
"s3": {
|
|
121
|
+
"kind": "s3-compatible",
|
|
122
|
+
"endpoint": "https://s3.example.com",
|
|
123
|
+
"region": "ap-northeast-1",
|
|
124
|
+
"accessKeyId": "<access-key-id>",
|
|
125
|
+
"secretAccessKey": "<secret-access-key>"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"targets": {
|
|
129
|
+
"home": {
|
|
130
|
+
"profile": "r2",
|
|
131
|
+
"bucket": "personal-pi",
|
|
132
|
+
"prefix": "pi-sync",
|
|
133
|
+
"namespace": "home",
|
|
134
|
+
"autoSync": true,
|
|
135
|
+
"syncFiles": ["settings.json", "skills", "prompts", "themes"],
|
|
136
|
+
"syncSessions": false,
|
|
137
|
+
"extraFiles": []
|
|
138
|
+
},
|
|
139
|
+
"work": {
|
|
140
|
+
"profile": "s3",
|
|
141
|
+
"bucket": "company-pi",
|
|
142
|
+
"prefix": "developers/narumi",
|
|
143
|
+
"namespace": "work",
|
|
144
|
+
"autoSync": true,
|
|
145
|
+
"syncFiles": ["AGENTS.md", "prompts"],
|
|
146
|
+
"syncSessions": false,
|
|
147
|
+
"extraFiles": []
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
106
152
|
|
|
107
|
-
|
|
153
|
+
### Terminology
|
|
108
154
|
|
|
109
|
-
|
|
155
|
+
- A **storage profile** owns connection/authentication fields: `kind`, `endpoint`, `region`, `accessKeyId`, `secretAccessKey`, and optional `sessionToken`.
|
|
156
|
+
- A **sync target** references a storage profile and owns `bucket`, `prefix`, `namespace`, `autoSync`, and its synced-content policy.
|
|
157
|
+
- `activeTarget` is used by bare commands and automatic sync.
|
|
158
|
+
- `namespace` is the old flat `profile` concept. The remote layout and snapshot wire field remain named `profiles`/`profile` for compatibility.
|
|
110
159
|
|
|
111
|
-
|
|
160
|
+
When adding another target with the same storage profile, pi-sync recommends reusing the current target's bucket and prefix while deriving a separate namespace from the new target name. For example, `home` and `work` may both use profile `r2`, bucket `pi-sync`, and prefix `pi-sync`, producing `pi-sync/profiles/home/` and `pi-sync/profiles/work/`.
|
|
112
161
|
|
|
113
|
-
|
|
162
|
+
Two targets may intentionally overlap local files, but only one is automatic. pi-sync rejects duplicate effective target remote identitiesβincluding deprecated bucket/prefix/namespace environment overridesβto prevent independent local states from controlling the same remote pointer. Removing a target/profile removes local configuration only; it never deletes buckets or snapshots. A referenced profile cannot be removed, and users must switch away before removing one of several current targets.
|
|
114
163
|
|
|
115
|
-
|
|
164
|
+
### Synced content
|
|
116
165
|
|
|
117
|
-
|
|
166
|
+
The **Settings β Choose synced content** screen edits a draft. Enter or Space toggles, typing searches, and Escape opens the Save/Discard/Continue decision. A single Save atomically updates the target while preserving unknown fields and POSIX `0600` permissions. Saving settings never starts network sync.
|
|
118
167
|
|
|
119
|
-
|
|
168
|
+
Omitting `syncFiles` preserves the legacy default of every built-in item. An empty array is valid and means the target manages nothing; **Sync now** reports that state instead of publishing an empty sync. Unknown built-in names fail validation.
|
|
120
169
|
|
|
121
|
-
|
|
170
|
+
Unselected items are unmanaged: pi-sync does not compare, pull, overwrite, or delete their local content, and pushes preserve their existing remote content.
|
|
122
171
|
|
|
123
|
-
|
|
172
|
+
### Environment precedence and deprecation
|
|
124
173
|
|
|
125
|
-
|
|
174
|
+
The existing `PI_SYNC_*` family still works in this release with its existing highest precedence, but it is deprecated and will be removed in a future major version. pi-sync shows variable namesβnot valuesβand migration guidance when any are active.
|
|
126
175
|
|
|
127
|
-
|
|
176
|
+
Move these fields into a storage profile:
|
|
128
177
|
|
|
129
|
-
|
|
178
|
+
- `PI_SYNC_ENDPOINT`
|
|
179
|
+
- `PI_SYNC_REGION`
|
|
180
|
+
- `PI_SYNC_ACCESS_KEY_ID`
|
|
181
|
+
- `PI_SYNC_SECRET_ACCESS_KEY`
|
|
182
|
+
- `PI_SYNC_SESSION_TOKEN`
|
|
130
183
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
184
|
+
Move these fields into a sync target:
|
|
185
|
+
|
|
186
|
+
- `PI_SYNC_BUCKET`
|
|
187
|
+
- `PI_SYNC_PROFILE` β `namespace`
|
|
188
|
+
- `PI_SYNC_PREFIX`
|
|
189
|
+
- `PI_SYNC_AUTO_SYNC`
|
|
190
|
+
- `PI_SYNC_SESSIONS`
|
|
191
|
+
|
|
192
|
+
`PI_SYNC_PROFILE` continues to mean only the remote namespace during the deprecation period; it never selects a storage profile or current target.
|
|
193
|
+
|
|
194
|
+
Standard compatibility aliases remain below `PI_SYNC_*` precedence:
|
|
195
|
+
|
|
196
|
+
- `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, `AWS_REGION`
|
|
197
|
+
- `R2_ENDPOINT`, `R2_BUCKET`
|
|
198
|
+
|
|
199
|
+
Environment-overridden fields are read-only in interactive settings. Cloudflare R2 static keys commonly reject `X-Amz-Security-Token`; pi-sync retains its one-time retry without the configured token for that R2 response.
|
|
200
|
+
|
|
201
|
+
### Legacy flat settings
|
|
202
|
+
|
|
203
|
+
Existing flat `pi-sync.local.json` files continue to work unchanged as a synthetic `default` target/profile. The old flat `profile` remains the remote namespace.
|
|
134
204
|
|
|
135
|
-
|
|
205
|
+
Adding a second target offers an explicit migration preview. On confirmation pi-sync:
|
|
206
|
+
|
|
207
|
+
1. takes an exact private backup of the original JSON bytes;
|
|
208
|
+
2. preserves unknown top-level fields;
|
|
209
|
+
3. maps existing connection and policy fields without changing the remote destination;
|
|
210
|
+
4. adopts the existing local sync state for the migrated target;
|
|
211
|
+
5. publishes the version 2 file atomically.
|
|
212
|
+
|
|
213
|
+
Malformed, invalid, symlinked, or concurrently changed settings are never overwritten automatically. Version 2 settings require the multi-target pi-sync release or newer. For a manual downgrade, restore the exact `.legacy-<timestamp>.bak` flat-config backup; no remote migration or rollback is needed.
|
|
214
|
+
|
|
215
|
+
## π¬ Commands
|
|
216
|
+
|
|
217
|
+
The menu is the preferred interactive workflow. Existing deterministic routes remain available:
|
|
136
218
|
|
|
137
219
|
```text
|
|
138
220
|
/sync help
|
|
221
|
+
/sync use <target>
|
|
139
222
|
/sync init
|
|
140
|
-
/sync config
|
|
141
|
-
/sync files
|
|
142
|
-
/sync status
|
|
143
|
-
/sync diff
|
|
144
|
-
/sync doctor
|
|
145
|
-
/sync push
|
|
146
|
-
/sync pull
|
|
147
|
-
/sync sync
|
|
148
|
-
/sync history
|
|
149
|
-
/sync rollback <snapshot-id>
|
|
223
|
+
/sync config [--target <name>]
|
|
224
|
+
/sync files [--target <name>]
|
|
225
|
+
/sync status [--target <name>]
|
|
226
|
+
/sync diff [--target <name>]
|
|
227
|
+
/sync doctor [--target <name>]
|
|
228
|
+
/sync push [--target <name>]
|
|
229
|
+
/sync pull [--target <name>]
|
|
230
|
+
/sync sync [--target <name>]
|
|
231
|
+
/sync history [--target <name>]
|
|
232
|
+
/sync rollback <snapshot-id> [--target <name>]
|
|
150
233
|
/sync unlock --stale
|
|
151
234
|
```
|
|
152
235
|
|
|
153
|
-
|
|
236
|
+
Flags:
|
|
154
237
|
|
|
155
|
-
|
|
238
|
+
- `--target <name>` addresses a target without switching it.
|
|
239
|
+
- `--yes` / `-y` skips an explicit direct command's confirmation.
|
|
240
|
+
- `--force` resolves a reviewed conflict for push/pull/sync.
|
|
241
|
+
- `--stale` requests guarded stale-lock recovery.
|
|
156
242
|
|
|
157
|
-
|
|
158
|
-
- `--force`: allow push or pull when both local and remote state changed.
|
|
159
|
-
- `--stale`: remove a stale local lock with `/sync unlock --stale`.
|
|
243
|
+
Unknown flags, unexpected values, and missing target/snapshot values are rejected. Argument completion includes configured target names after session start.
|
|
160
244
|
|
|
161
|
-
|
|
245
|
+
TUI mode provides the full manager and custom settings components. RPC uses Pi's dialog/notification protocol and does not call TUI-only components. Print/JSON modes cannot display extension UI; use explicit direct routes for compatible automation and do not expect notification-only status output.
|
|
162
246
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
- only local changed or remote is empty β push
|
|
166
|
-
- first sync with existing local settings and an identical remote snapshot β initialize local sync state without rewriting files
|
|
167
|
-
- first sync with existing local settings and a different remote snapshot β skip and show a warning so you manually choose `/sync pull` or `/sync push`
|
|
168
|
-
- only remote changed after an established sync β pull with a backup
|
|
169
|
-
- both local and remote changed after a previous sync β skip and show a warning
|
|
170
|
-
- no config present β do nothing
|
|
171
|
-
|
|
172
|
-
Disable startup sync with either:
|
|
173
|
-
|
|
174
|
-
```json
|
|
175
|
-
{
|
|
176
|
-
"autoSync": false
|
|
177
|
-
}
|
|
178
|
-
```
|
|
247
|
+
## π Sync and recovery model
|
|
179
248
|
|
|
180
|
-
|
|
249
|
+
For the current target, startup auto-sync uses conservative decisions:
|
|
181
250
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
251
|
+
- local changed or remote empty β preview/confirm manually, or push in the existing quiet automatic path
|
|
252
|
+
- identical first sync β initialize local state
|
|
253
|
+
- safe remote-only change β pull with backup
|
|
254
|
+
- first-sync mismatch or both changed β stop and require review
|
|
255
|
+
- no selected content β report/skip
|
|
185
256
|
|
|
186
|
-
|
|
257
|
+
Switching targets changes only `activeTarget`; it never starts sync. The next startup uses the new target's `autoSync`, or the user can choose **Sync now** immediately.
|
|
187
258
|
|
|
188
|
-
Remote layout:
|
|
259
|
+
Remote layout remains compatible:
|
|
189
260
|
|
|
190
|
-
```
|
|
191
|
-
|
|
261
|
+
```text
|
|
262
|
+
<prefix>/
|
|
192
263
|
βββ profiles/
|
|
193
|
-
βββ
|
|
264
|
+
βββ <namespace>/
|
|
194
265
|
βββ latest.json
|
|
195
266
|
βββ history.json
|
|
196
267
|
βββ snapshots/
|
|
197
|
-
βββ
|
|
268
|
+
βββ <snapshot-id>.json.gz
|
|
198
269
|
```
|
|
199
270
|
|
|
200
|
-
|
|
271
|
+
Snapshot upload is staging; `latest.json` is the active publication boundary. A failed history update after publication is reported as βsnapshot active, history needs repairβ instead of falsely claiming no remote change.
|
|
272
|
+
|
|
273
|
+
Before pull/rollback, pi-sync writes a backup under `.pisync/backups/`. It then preflights all paths, stages a private transaction journal, applies changes, and restores every affected path if a later mutation fails. An interrupted journal is recovered before the next session/snapshot apply. Filesystem-wide replacement cannot be one OS primitive, so the guarantee is a complete previous or complete new accepted state after rollback/recoveryβnot an unrecoverable partial accepted state.
|
|
274
|
+
|
|
275
|
+
R2/S3 does not provide the true cross-machine compare-and-swap used here. pi-sync re-reads `latest.json` immediately before publication and verifies afterward, but simultaneous writers can still race. Review status before important forced updates.
|
|
201
276
|
|
|
202
|
-
|
|
277
|
+
## π Session syncing
|
|
278
|
+
|
|
279
|
+
`syncSessions` defaults to `false`. Enabling it includes configured Pi session JSONL files and makes the privacy warning visible in settings, status, previews, and confirmations.
|
|
280
|
+
|
|
281
|
+
Only JSONL session files are included. Denylisted names and paths such as `.env*`, `.pisync`, `node_modules`, `token`, and `secret` are ignored. The currently open session file is protected during pull; restart Pi or resume a pulled session to use newly synced conversations.
|
|
282
|
+
|
|
283
|
+
Sessions can contain prompts, model output, tool results, file paths, images, and secrets. Use only storage you trust.
|
|
203
284
|
|
|
204
285
|
## π‘οΈ Safety notes
|
|
205
286
|
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
287
|
+
- Credentials stay local and are never included in snapshots.
|
|
288
|
+
- Push refuses common secret patterns in locally managed files.
|
|
289
|
+
- Pull/rollback reject unsafe paths, duplicate paths, checksum mismatches, symlink parents, and file/directory replacement hazards before mutation.
|
|
290
|
+
- Unmanaged local/remote files remain preserved.
|
|
291
|
+
- A live local lock disables destructive work. Stale/unreadable lock recovery retains process-liveness and guard checks.
|
|
292
|
+
- Force operations remain explicit direct/conflict-recovery actions and retain exact confirmations.
|
|
293
|
+
- Terminal-bound config, remote metadata, machine names, paths, and errors are control-character sanitized.
|
|
294
|
+
|
|
295
|
+
## βΏ Conditional terminal accessibility smoke
|
|
296
|
+
|
|
297
|
+
Pi exposes terminal components rather than a semantic/ARIA tree, so automated tests verify textual state, keyboard paths, control-character escaping, and 32/60/100-column fitting rather than claiming semantic announcements. When validating a release in a supported environment:
|
|
298
|
+
|
|
299
|
+
1. open `/sync` using only the keyboard and verify arrows, Enter, Space, search typing, paging where available, Back, and Escape;
|
|
300
|
+
2. enter a Unicode target/profile name with an IME and confirm focus returns to Pi after closing;
|
|
301
|
+
3. check the main, warning, preview, saved, cancelled, and invalid states with the terminal screen reader in use;
|
|
302
|
+
4. repeat under a light and dark Pi theme and at 32, 60, and 100 columns.
|
|
303
|
+
|
|
304
|
+
Critical meaning is always present in text such as `(current)`, `Warning`, `Invalid`, `Saved`, `Cancelled`, or `Applied`; color is supplementary.
|
|
213
305
|
|
|
214
306
|
## ποΈ Package layout
|
|
215
307
|
|
|
216
|
-
```
|
|
308
|
+
```text
|
|
217
309
|
extensions/pi-sync/
|
|
218
310
|
βββ src/
|
|
219
|
-
β βββ index.ts
|
|
220
|
-
β βββ sync.ts
|
|
221
|
-
β βββ
|
|
222
|
-
β βββ
|
|
223
|
-
β
|
|
311
|
+
β βββ index.ts # Thin Pi entrypoint
|
|
312
|
+
β βββ sync.ts # Command and lifecycle orchestration
|
|
313
|
+
β βββ manager-ui.ts # Goal-oriented menus and setup/management flows
|
|
314
|
+
β βββ file-selection.ts # Transactional synced-content editor
|
|
315
|
+
β βββ settings-management.ts # Profiles, targets, migration, and atomic saves
|
|
316
|
+
β βββ snapshot-transaction.ts # Local apply journal, rollback, and recovery
|
|
317
|
+
β βββ *.ts # Config, policy, snapshot, S3, lock, and format modules
|
|
318
|
+
βββ test/
|
|
224
319
|
βββ README.md
|
|
225
320
|
βββ LICENSE
|
|
226
321
|
βββ tsconfig.json
|
|
227
322
|
βββ package.json
|
|
228
323
|
```
|
|
229
324
|
|
|
230
|
-
`index.ts` is the Pi entrypoint and forwards to `sync.ts`; the other source modules are internal. The package exposes its Pi extension through `package.json`:
|
|
231
|
-
|
|
232
|
-
```json
|
|
233
|
-
{
|
|
234
|
-
"pi": {
|
|
235
|
-
"extensions": ["./src/index.ts"]
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
325
|
## π Keywords
|
|
241
326
|
|
|
242
|
-
Pi extension, Pi coding agent, settings sync, Cloudflare R2, S3-compatible storage, snapshot sync, dotfiles sync.
|
|
327
|
+
Pi extension, Pi coding agent, settings sync, Cloudflare R2, S3-compatible storage, multi-profile sync, sync targets, snapshot sync, dotfiles sync.
|
|
243
328
|
|
|
244
329
|
## π License
|
|
245
330
|
|
|
246
|
-
MIT
|
|
331
|
+
MIT
|