pi-sync-cli 0.1.0__tar.gz → 0.3.0__tar.gz
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.
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/.gitignore +3 -0
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/PKG-INFO +71 -1
- pi_sync_cli-0.3.0/README.md +145 -0
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/pyproject.toml +1 -1
- pi_sync_cli-0.3.0/pyrightconfig.json +5 -0
- pi_sync_cli-0.3.0/src/pi_sync/cli.py +480 -0
- pi_sync_cli-0.3.0/tests/test_cli.py +632 -0
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/uv.lock +1 -1
- pi_sync_cli-0.1.0/README.md +0 -75
- pi_sync_cli-0.1.0/src/pi_sync/cli.py +0 -268
- pi_sync_cli-0.1.0/tests/test_cli.py +0 -270
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/LICENSE +0 -0
- {pi_sync_cli-0.1.0 → pi_sync_cli-0.3.0}/src/pi_sync/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pi-sync-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Sync pi agent config and extensions between hosts over rsync
|
|
5
5
|
Project-URL: Repository, https://github.com/say4n/pi-sync
|
|
6
6
|
Project-URL: Issues, https://github.com/say4n/pi-sync/issues
|
|
@@ -62,6 +62,8 @@ Host-local state is deliberately never touched: `sessions/`, `npm/`,
|
|
|
62
62
|
| `--delete` | mirror `extensions/` exactly (deletes extras on the destination) |
|
|
63
63
|
| `--dry-run` | report changes, copy nothing |
|
|
64
64
|
| `-x, --exclude PATTERN` | skip matching files (repeatable) |
|
|
65
|
+
| `--install` | install pi on hosts that lack it, without prompting |
|
|
66
|
+
| `--uninstall` | remove pi from the host instead of syncing (config is kept) |
|
|
65
67
|
| `--local-dir` | default `$PI_CODING_AGENT_DIR` or `~/.pi/agent` |
|
|
66
68
|
| `--remote-dir` | default `~/.pi/agent` |
|
|
67
69
|
| `-v` / `--verbose` | print each rsync command and its output |
|
|
@@ -69,6 +71,74 @@ Host-local state is deliberately never touched: `sessions/`, `npm/`,
|
|
|
69
71
|
Multiple hosts are accepted: `pi-sync a b c`. Exits non-zero if any host is
|
|
70
72
|
unreachable or any transfer fails.
|
|
71
73
|
|
|
74
|
+
## Host preflight
|
|
75
|
+
|
|
76
|
+
Each host gets one ssh probe that reports reachability and pi's location in the
|
|
77
|
+
same round trip.
|
|
78
|
+
|
|
79
|
+
If pi is missing, pi-sync hands the terminal to pi's own installer
|
|
80
|
+
(`curl -fsSL https://pi.dev/install.sh | sh`), which keeps full control: its
|
|
81
|
+
prompts (its install/uninstall/do-nothing menu, a Node.js install, a sudo
|
|
82
|
+
password) work normally, and the sync continues once it exits. pi-sync does not
|
|
83
|
+
add a confirmation of its own, because the installer already asks. Without a
|
|
84
|
+
terminal it runs unattended under `--install`, stays quiet otherwise, and
|
|
85
|
+
`--dry-run` never installs anything.
|
|
86
|
+
|
|
87
|
+
The installer's exit status is not treated as proof: its "do nothing" choice
|
|
88
|
+
exits 0, so the host is re-probed afterwards and reported honestly.
|
|
89
|
+
|
|
90
|
+
**A host without pi is skipped** — copying into a host that has never run pi is
|
|
91
|
+
not useful and usually fails anyway, since there is no agent directory to copy
|
|
92
|
+
into. That covers an installer run that installed nothing, a failed install, and
|
|
93
|
+
a non-interactive run without `--install`; a skipped host makes the run exit
|
|
94
|
+
non-zero. After a successful install pi-sync creates the agent directory,
|
|
95
|
+
because rsync will not create intermediate directories on its own.
|
|
96
|
+
|
|
97
|
+
The probe checks `command -v pi` plus the usual install locations
|
|
98
|
+
(`~/.local/bin`, `~/.pi/bin`, `~/.pi/agent/bin`, linuxbrew, homebrew,
|
|
99
|
+
`/usr/local/bin`), because a non-interactive ssh session does not source the
|
|
100
|
+
host's shell init — on a linuxbrew host `command -v pi` alone misses it.
|
|
101
|
+
|
|
102
|
+
## Uninstalling
|
|
103
|
+
|
|
104
|
+
`--uninstall` removes pi from the host instead of syncing. It runs
|
|
105
|
+
`npm uninstall -g --prefix <prefix> @earendil-works/pi-coding-agent`, deriving the
|
|
106
|
+
prefix from where pi actually lives (as the official installer does), then
|
|
107
|
+
re-probes to confirm the binary is really gone — npm can exit 0 having removed
|
|
108
|
+
nothing. `~/.pi/agent` is deliberately left untouched, since that is your config
|
|
109
|
+
rather than the CLI.
|
|
110
|
+
|
|
111
|
+
This exists because the official installer can only uninstall through its
|
|
112
|
+
interactive menu: its unattended mode always installs or reinstalls. If the
|
|
113
|
+
uninstall fails, that is usually a managed install
|
|
114
|
+
(`PI_EXPERIMENTAL=1`, under `<agent dir>/install`) — run
|
|
115
|
+
`curl -fsSL https://pi.dev/install.sh | sh` on the host and choose `u`.
|
|
116
|
+
|
|
117
|
+
## Shell completions
|
|
118
|
+
|
|
119
|
+
Host arguments complete from `~/.ssh/config`, following `Include` directives and
|
|
120
|
+
skipping wildcard entries:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# bash
|
|
124
|
+
_PI_SYNC_COMPLETE=bash_source pi-sync > ~/.pi-sync-complete.bash
|
|
125
|
+
echo 'source ~/.pi-sync-complete.bash' >> ~/.bashrc
|
|
126
|
+
|
|
127
|
+
# zsh
|
|
128
|
+
_PI_SYNC_COMPLETE=zsh_source pi-sync > ~/.pi-sync-complete.zsh
|
|
129
|
+
echo 'source ~/.pi-sync-complete.zsh' >> ~/.zshrc
|
|
130
|
+
|
|
131
|
+
# fish (config.fish)
|
|
132
|
+
_PI_SYNC_COMPLETE=fish_source pi-sync | source
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Writing the script out (rather than `eval "$(_PI_SYNC_COMPLETE=bash_source pi-sync)"`
|
|
136
|
+
on every shell start) keeps shell startup fast, since the eval form runs the
|
|
137
|
+
binary each time. PowerShell works the same way via `powershell_source`.
|
|
138
|
+
|
|
139
|
+
Completions are read from the config at completion time, so new hosts appear
|
|
140
|
+
without regenerating anything.
|
|
141
|
+
|
|
72
142
|
## Caveats
|
|
73
143
|
|
|
74
144
|
- `settings.json` is machine-written by pi (`lastChangelogVersion` bumps, UI
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# pi-sync
|
|
2
|
+
|
|
3
|
+
Sync pi agent config between hosts over rsync, using your existing ssh config
|
|
4
|
+
for routing (so `~/.ssh/config` aliases just work).
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pi-sync tinfoil # push config + extensions
|
|
8
|
+
pi-sync --config laptop # only models.json and settings.json
|
|
9
|
+
pi-sync --pull --all tinfoil # fetch the host's config back
|
|
10
|
+
pi-sync --dry-run --all a b # preview against two hosts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# the PyPI package is pi-sync-cli; it installs the `pi-sync` command
|
|
17
|
+
pipx install pi-sync-cli
|
|
18
|
+
pipx install git+ssh://git@github.com/say4n/pi-sync # from source (needs access)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.10+. `uv tool install` works in place of `pipx install`.
|
|
22
|
+
|
|
23
|
+
## What syncs
|
|
24
|
+
|
|
25
|
+
Only the declarative parts of the agent dir:
|
|
26
|
+
|
|
27
|
+
| Group | Files |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `--config` | `models.json`, `settings.json` |
|
|
30
|
+
| `--extensions` | `extensions/` |
|
|
31
|
+
| `--auth` | `auth.json` — secrets, opt-in, warns on push |
|
|
32
|
+
|
|
33
|
+
`--all` is `--config` + `--extensions` (also the default when no flag is given).
|
|
34
|
+
|
|
35
|
+
Host-local state is deliberately never touched: `sessions/`, `npm/`,
|
|
36
|
+
`models-store.json` (regenerated from the pi.dev catalog), `ayu/`, `bin/`,
|
|
37
|
+
`trust.json`.
|
|
38
|
+
|
|
39
|
+
## Flags
|
|
40
|
+
|
|
41
|
+
| Flag | Effect |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `--all` / `--config` / `--extensions` / `--auth` | what to sync |
|
|
44
|
+
| `--pull` | host → local instead of local → host |
|
|
45
|
+
| `--delete` | mirror `extensions/` exactly (deletes extras on the destination) |
|
|
46
|
+
| `--dry-run` | report changes, copy nothing |
|
|
47
|
+
| `-x, --exclude PATTERN` | skip matching files (repeatable) |
|
|
48
|
+
| `--install` | install pi on hosts that lack it, without prompting |
|
|
49
|
+
| `--uninstall` | remove pi from the host instead of syncing (config is kept) |
|
|
50
|
+
| `--local-dir` | default `$PI_CODING_AGENT_DIR` or `~/.pi/agent` |
|
|
51
|
+
| `--remote-dir` | default `~/.pi/agent` |
|
|
52
|
+
| `-v` / `--verbose` | print each rsync command and its output |
|
|
53
|
+
|
|
54
|
+
Multiple hosts are accepted: `pi-sync a b c`. Exits non-zero if any host is
|
|
55
|
+
unreachable or any transfer fails.
|
|
56
|
+
|
|
57
|
+
## Host preflight
|
|
58
|
+
|
|
59
|
+
Each host gets one ssh probe that reports reachability and pi's location in the
|
|
60
|
+
same round trip.
|
|
61
|
+
|
|
62
|
+
If pi is missing, pi-sync hands the terminal to pi's own installer
|
|
63
|
+
(`curl -fsSL https://pi.dev/install.sh | sh`), which keeps full control: its
|
|
64
|
+
prompts (its install/uninstall/do-nothing menu, a Node.js install, a sudo
|
|
65
|
+
password) work normally, and the sync continues once it exits. pi-sync does not
|
|
66
|
+
add a confirmation of its own, because the installer already asks. Without a
|
|
67
|
+
terminal it runs unattended under `--install`, stays quiet otherwise, and
|
|
68
|
+
`--dry-run` never installs anything.
|
|
69
|
+
|
|
70
|
+
The installer's exit status is not treated as proof: its "do nothing" choice
|
|
71
|
+
exits 0, so the host is re-probed afterwards and reported honestly.
|
|
72
|
+
|
|
73
|
+
**A host without pi is skipped** — copying into a host that has never run pi is
|
|
74
|
+
not useful and usually fails anyway, since there is no agent directory to copy
|
|
75
|
+
into. That covers an installer run that installed nothing, a failed install, and
|
|
76
|
+
a non-interactive run without `--install`; a skipped host makes the run exit
|
|
77
|
+
non-zero. After a successful install pi-sync creates the agent directory,
|
|
78
|
+
because rsync will not create intermediate directories on its own.
|
|
79
|
+
|
|
80
|
+
The probe checks `command -v pi` plus the usual install locations
|
|
81
|
+
(`~/.local/bin`, `~/.pi/bin`, `~/.pi/agent/bin`, linuxbrew, homebrew,
|
|
82
|
+
`/usr/local/bin`), because a non-interactive ssh session does not source the
|
|
83
|
+
host's shell init — on a linuxbrew host `command -v pi` alone misses it.
|
|
84
|
+
|
|
85
|
+
## Uninstalling
|
|
86
|
+
|
|
87
|
+
`--uninstall` removes pi from the host instead of syncing. It runs
|
|
88
|
+
`npm uninstall -g --prefix <prefix> @earendil-works/pi-coding-agent`, deriving the
|
|
89
|
+
prefix from where pi actually lives (as the official installer does), then
|
|
90
|
+
re-probes to confirm the binary is really gone — npm can exit 0 having removed
|
|
91
|
+
nothing. `~/.pi/agent` is deliberately left untouched, since that is your config
|
|
92
|
+
rather than the CLI.
|
|
93
|
+
|
|
94
|
+
This exists because the official installer can only uninstall through its
|
|
95
|
+
interactive menu: its unattended mode always installs or reinstalls. If the
|
|
96
|
+
uninstall fails, that is usually a managed install
|
|
97
|
+
(`PI_EXPERIMENTAL=1`, under `<agent dir>/install`) — run
|
|
98
|
+
`curl -fsSL https://pi.dev/install.sh | sh` on the host and choose `u`.
|
|
99
|
+
|
|
100
|
+
## Shell completions
|
|
101
|
+
|
|
102
|
+
Host arguments complete from `~/.ssh/config`, following `Include` directives and
|
|
103
|
+
skipping wildcard entries:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# bash
|
|
107
|
+
_PI_SYNC_COMPLETE=bash_source pi-sync > ~/.pi-sync-complete.bash
|
|
108
|
+
echo 'source ~/.pi-sync-complete.bash' >> ~/.bashrc
|
|
109
|
+
|
|
110
|
+
# zsh
|
|
111
|
+
_PI_SYNC_COMPLETE=zsh_source pi-sync > ~/.pi-sync-complete.zsh
|
|
112
|
+
echo 'source ~/.pi-sync-complete.zsh' >> ~/.zshrc
|
|
113
|
+
|
|
114
|
+
# fish (config.fish)
|
|
115
|
+
_PI_SYNC_COMPLETE=fish_source pi-sync | source
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Writing the script out (rather than `eval "$(_PI_SYNC_COMPLETE=bash_source pi-sync)"`
|
|
119
|
+
on every shell start) keeps shell startup fast, since the eval form runs the
|
|
120
|
+
binary each time. PowerShell works the same way via `powershell_source`.
|
|
121
|
+
|
|
122
|
+
Completions are read from the config at completion time, so new hosts appear
|
|
123
|
+
without regenerating anything.
|
|
124
|
+
|
|
125
|
+
## Caveats
|
|
126
|
+
|
|
127
|
+
- `settings.json` is machine-written by pi (`lastChangelogVersion` bumps, UI
|
|
128
|
+
toggles), so two hosts pushing it will overwrite each other's local
|
|
129
|
+
preferences. Sync it when you change `packages`, not reflexively.
|
|
130
|
+
- Extensions that write runtime files inside their own directory (logs,
|
|
131
|
+
checkpoints) get those files synced too, and each host's copy is overwritten by
|
|
132
|
+
whichever side pushed last — exclude them with `-x '*/logs/*'`.
|
|
133
|
+
- Extension versions are whatever each host has installed; pin them in
|
|
134
|
+
`settings.json` (`npm:pi-lens@1.2.3`) if you need hosts identical.
|
|
135
|
+
- `--auth` copies API keys in the clear. Prefer `OPENCODE_API_KEY` (and friends)
|
|
136
|
+
in the environment where you can.
|
|
137
|
+
- Remote paths go through the host's shell, so `~` expands there as usual.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv sync
|
|
143
|
+
uv run pytest
|
|
144
|
+
uv run pi-sync --help
|
|
145
|
+
```
|