@jbaehova/onthego 0.3.1 → 0.4.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.
package/README.md
CHANGED
|
@@ -1,117 +1,176 @@
|
|
|
1
1
|
# ONTHEGO
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Move your unfinished work and your coding-agent conversations between your desktop, laptop, and VPS with one command.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```sh
|
|
6
|
+
onthego sync laptop
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
ONTHEGO carries a Git project exactly as you left it: staged, unstaged, and untracked work, plus the project's Codex CLI and Claude Code sessions, so you can `codex resume` or `claude --resume` on the other device and keep going. Each device keeps its own Git settings, hooks, `node_modules`, caches, and agent logins.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
It is a small open-source CLI (MIT). There is no account, no hosted service, and no AI feature. Your devices talk to each other directly over SSH, optionally through [Tailscale](#anywhere-with-tailscale).
|
|
8
12
|
|
|
9
13
|
## Install
|
|
10
14
|
|
|
11
15
|
```sh
|
|
12
|
-
npm install -g @jbaehova/onthego
|
|
16
|
+
npm install -g @jbaehova/onthego
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The npm package ships native binaries for macOS and Linux (arm64 and amd64). Git and OpenSSH are required; Node.js is only needed for npm itself.
|
|
20
|
+
|
|
21
|
+
On a server without Node.js:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
curl -fsSL https://raw.githubusercontent.com/peerproblem/onthego/main/install.sh | sh
|
|
13
25
|
```
|
|
14
26
|
|
|
15
|
-
The
|
|
27
|
+
The script downloads the release binary for the machine, verifies its SHA-256 checksum, and installs it to `~/.local/bin`. Pass `-s -- --version 0.4.0` for a specific version.
|
|
28
|
+
|
|
29
|
+
You usually do not need to install it on the other devices by hand: `onthego device add <name> --install` copies the exact same version over SSH.
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
<details>
|
|
32
|
+
<summary>Build from source</summary>
|
|
18
33
|
|
|
19
34
|
```sh
|
|
20
35
|
git clone https://github.com/peerproblem/onthego.git
|
|
21
36
|
cd onthego
|
|
22
|
-
make build
|
|
23
|
-
mkdir -p ~/.local/bin
|
|
37
|
+
make build # Go 1.26 or newer
|
|
24
38
|
install -m 755 dist/onthego ~/.local/bin/onthego
|
|
25
|
-
# Or: go install github.com/peerproblem/onthego/cmd/onthego@latest
|
|
26
39
|
```
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
</details>
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
## Quick start
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
On your desktop, inside a Git project:
|
|
33
46
|
|
|
34
47
|
```sh
|
|
35
|
-
onthego init
|
|
36
|
-
onthego device add laptop --host me@
|
|
37
|
-
onthego
|
|
38
|
-
onthego doctor laptop
|
|
39
|
-
onthego push laptop --dry-run
|
|
40
|
-
|
|
41
|
-
# Stop agents and other processes that write project files, then:
|
|
42
|
-
onthego push laptop --verbose
|
|
48
|
+
onthego init # once per project; commit .onthego.yaml
|
|
49
|
+
onthego device add laptop --host me@laptop.local --install
|
|
50
|
+
onthego sync laptop
|
|
43
51
|
```
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
Then on the laptop:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
cd ~/projects/my-app # the same home-relative path by default
|
|
57
|
+
codex resume <session-id> # or: claude --resume <session-id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The handoff prints the exact `cd` and resume commands. When you are back at the desktop, run `onthego sync laptop` again: the desktop knows it handed the work off, so it pulls the laptop's changes back.
|
|
61
|
+
|
|
62
|
+
`sync` picks the direction for you:
|
|
63
|
+
|
|
64
|
+
| Situation | What `sync` does |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| The project is not on the other device yet | push |
|
|
67
|
+
| Only this device changed since the last handoff | push |
|
|
68
|
+
| Only the other device changed | pull |
|
|
69
|
+
| You handed the work off and have not touched it since | pull (take it back) |
|
|
70
|
+
| Nothing changed | nothing |
|
|
71
|
+
| Both sides changed | stops, and tells you to choose `push --replace` or `pull --replace` |
|
|
72
|
+
|
|
73
|
+
Nothing is ever merged silently. Whenever a handoff replaces work, the replaced state is kept as a Git backup that `onthego undo` restores. Use `onthego sync laptop --dry-run` to see the decision first, or `push` and `pull` to choose the direction yourself.
|
|
74
|
+
|
|
75
|
+
## Connecting your devices
|
|
76
|
+
|
|
77
|
+
ONTHEGO uses your normal SSH setup (`~/.ssh/config`, keys, `known_hosts`). The only requirement is that one device can `ssh` into the other without a password prompt. **Run every command from the device that can reach the other one.** A laptop that can reach a VPS can both push to it and pull from it, so the VPS never needs to reach the laptop.
|
|
78
|
+
|
|
79
|
+
`onthego doctor laptop` checks the whole chain: SSH, the remote onthego version, and the pinned identity.
|
|
80
|
+
|
|
81
|
+
### Same network
|
|
82
|
+
|
|
83
|
+
1. Enable SSH on the receiving device.
|
|
84
|
+
- macOS: System Settings > General > Sharing > Remote Login.
|
|
85
|
+
- Linux: `sudo systemctl enable --now ssh`
|
|
86
|
+
2. From the device you type on, install your key and accept the host key once:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
ssh-copy-id me@laptop.local
|
|
90
|
+
ssh me@laptop.local true
|
|
91
|
+
```
|
|
46
92
|
|
|
47
|
-
|
|
93
|
+
3. Pair it: `onthego device add laptop --host me@laptop.local --install`
|
|
48
94
|
|
|
49
|
-
|
|
95
|
+
An alias from `~/.ssh/config` works as the host too (`--host laptop`). `--port` and `--identity-file` are available; private keys are never copied.
|
|
96
|
+
|
|
97
|
+
### Anywhere with Tailscale
|
|
98
|
+
|
|
99
|
+
Use Tailscale when your devices are not on the same network, for example a laptop on the road and a desktop at home. Tailscale gives every device a stable name; ONTHEGO looks it up for you.
|
|
100
|
+
|
|
101
|
+
1. Install Tailscale on each device and sign in with the same account ([download](https://tailscale.com/download); on Linux: `curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up`).
|
|
102
|
+
2. Enable SSH on each device you pair to, as above. On Linux you can instead turn on Tailscale SSH with `sudo tailscale set --ssh`.
|
|
103
|
+
3. List your tailnet and pair by name:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
onthego tailscale
|
|
107
|
+
onthego device add laptop --tailscale me@laptop --install
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`onthego tailscale` shows which devices are online, which are already paired, and what is missing. ONTHEGO only reads `tailscale status`; it never changes Tailscale settings. See the [Tailscale guide](docs/tailscale.md) for MagicDNS, Tailscale SSH "check" mode, and troubleshooting.
|
|
111
|
+
|
|
112
|
+
### A VPS
|
|
50
113
|
|
|
51
114
|
```sh
|
|
52
|
-
|
|
53
|
-
onthego
|
|
54
|
-
codex resume <session-id>
|
|
55
|
-
# Or: claude --resume <session-id>
|
|
115
|
+
onthego device add vps --host me@vps.example.com --install
|
|
116
|
+
onthego sync vps
|
|
56
117
|
```
|
|
57
118
|
|
|
58
|
-
|
|
119
|
+
`--install` detects the server's OS and CPU and copies the matching binary to `~/.local/bin/onthego` there. No Node.js or internet access is needed on the server. After you upgrade onthego locally, run `onthego device install vps` to upgrade the server to the same version.
|
|
59
120
|
|
|
60
|
-
|
|
121
|
+
Work on the VPS over SSH or in a terminal multiplexer as usual. When you come back, run `onthego sync vps` from your laptop or desktop.
|
|
122
|
+
|
|
123
|
+
### Where the project lives on each device
|
|
124
|
+
|
|
125
|
+
By default the project goes to the same path relative to the home directory (`~/projects/my-app` on macOS becomes `/home/me/projects/my-app` on Linux). Agent session paths are remapped to match. To choose another location:
|
|
61
126
|
|
|
62
127
|
```sh
|
|
63
|
-
onthego
|
|
128
|
+
onthego device path laptop /Users/me/code/my-app
|
|
129
|
+
onthego device list # shows each device and this project's path there
|
|
64
130
|
```
|
|
65
131
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
After a successful push, `status` warns that the project and sessions were handed to the other device. Pull before editing or resuming them on the original device.
|
|
132
|
+
For the first transfer, the destination directory should not exist yet. To adopt an existing copy, use `push --replace` once (a Git backup of the destination is kept).
|
|
69
133
|
|
|
70
134
|
## What moves
|
|
71
135
|
|
|
72
136
|
| Data | Behavior |
|
|
73
137
|
| --- | --- |
|
|
74
|
-
| Git history |
|
|
75
|
-
| Staged and unstaged work |
|
|
76
|
-
| Untracked
|
|
77
|
-
| Ignored files |
|
|
78
|
-
| Secret candidates such as `.env` | Excluded and reported
|
|
79
|
-
| Codex and Claude
|
|
80
|
-
| Destination Git
|
|
81
|
-
|
|
|
82
|
-
| Agent logins, global databases,
|
|
138
|
+
| Git history | Only missing objects travel, in an incremental bundle. No Git hosting needed. |
|
|
139
|
+
| Staged and unstaged work | Preserved separately, including binary files and executable bits. |
|
|
140
|
+
| Untracked files | Included, subject to `exclude` rules and the secret policy. |
|
|
141
|
+
| Ignored files | Only when you add them with `--include` or project policy. |
|
|
142
|
+
| Secret candidates such as `.env`, `.npmrc` | Excluded and reported. `--include-secret` carries a file encrypted, outside Git objects. |
|
|
143
|
+
| Codex and Claude sessions | Sessions for this project active in the last 7 days. Paths are remapped; conversation content is not rewritten. |
|
|
144
|
+
| Destination Git config, hooks, remotes, stashes | Left as they are. |
|
|
145
|
+
| `node_modules`, caches, build output | Left as they are on each device. |
|
|
146
|
+
| Agent logins, global databases, running processes | Never transferred. Sign in to the agent on each device. |
|
|
83
147
|
|
|
84
|
-
|
|
148
|
+
Everything travels in one package that is encrypted for the receiving device (age) and signed by the sender (Ed25519). Pairing pins both identities, so a device that changes keys is rejected.
|
|
85
149
|
|
|
86
|
-
|
|
150
|
+
Stop agents and other processes that write to the project before a handoff, so they cannot race with it. Agent transcripts can contain sensitive prompts or tool output; they are encrypted in transit and stored as the agent would store them. Anything already committed to Git travels as recorded, including secrets in history.
|
|
87
151
|
|
|
88
|
-
|
|
152
|
+
## Day to day
|
|
89
153
|
|
|
90
154
|
```sh
|
|
91
|
-
onthego
|
|
155
|
+
onthego status # did anything change since the last handoff? who has the work?
|
|
156
|
+
onthego status laptop # the same, for the laptop's copy
|
|
157
|
+
onthego push laptop --dry-run # preview files and sessions without sending
|
|
92
158
|
onthego push laptop --session-days 2
|
|
93
|
-
onthego push laptop --all-sessions
|
|
94
|
-
onthego
|
|
95
|
-
onthego pull laptop --session-conflict skip
|
|
159
|
+
onthego push laptop --session <id> # or --all-sessions, --no-sessions
|
|
160
|
+
onthego push laptop --include scratch/notes.txt --include-secret .env
|
|
96
161
|
```
|
|
97
162
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
Identical sessions are skipped. Incoming extensions update an older destination, while a newer destination is kept. Divergent sessions keep the destination and save the incoming history in private `~/.onthego/conflicts/` storage by default. `--session-conflict keep-both`, `--session-conflict theirs`, and `--session-conflict skip` make the policy explicit. Conflict copies remain until you review and remove them. Plaintext staging copies are removed after import. If an adapter cannot write its destination, the workspace still arrives and its authenticated inbound histories are retained in a private `session-recovery-*` directory under ONTHEGO state. The warning prints its location. `status` reports pending recovery. Retry the same package or run `onthego sessions recover --session-conflict keep-both` to import only the saved sessions while preserving subsequent workspace edits. JSON reports `sessions.status: "recovery_needed"` until recovery succeeds. The deprecated `--sessions` alias still works and warns; it only chooses a conflict policy.
|
|
101
|
-
|
|
102
|
-
Concurrent workspace edits require a deliberate decision. ONTHEGO checks Git handoff ancestry and the destination's captured workspace state. `--replace` applies incoming managed files after retaining a Git backup; a different project ID is always rejected.
|
|
163
|
+
After a push, `status` on the sending device reminds you that the work was handed to the other device. Pull (or `sync`) before editing there again.
|
|
103
164
|
|
|
104
|
-
|
|
105
|
-
onthego backups list
|
|
106
|
-
onthego undo
|
|
107
|
-
onthego backups prune --keep 3
|
|
108
|
-
```
|
|
165
|
+
### Conflicts and recovery
|
|
109
166
|
|
|
110
|
-
|
|
167
|
+
- **Workspace.** When both devices changed, `sync`, `push`, and `pull` stop with exit code 6. `--replace` overwrites the other side after saving a Git backup there. `onthego backups list` shows backups, `onthego undo` restores the latest one, and `onthego backups prune --keep 3` trims them. The newest five are kept automatically.
|
|
168
|
+
- **Agent sessions.** Identical sessions are skipped and a longer incoming history updates an older one. A diverged session keeps the destination's copy and saves the incoming one under `~/.onthego/conflicts/`. Change this with `--session-conflict keep-both|theirs|skip`.
|
|
169
|
+
- **Partial delivery.** If the workspace arrives but an agent home cannot be written, the sessions are kept privately and `status` says so. Run `onthego sessions recover` once the problem is fixed; later workspace edits are preserved.
|
|
111
170
|
|
|
112
171
|
## Configuration
|
|
113
172
|
|
|
114
|
-
|
|
173
|
+
`onthego init` writes `.onthego.yaml`. Commit it so every device uses the same project ID:
|
|
115
174
|
|
|
116
175
|
```yaml
|
|
117
176
|
schema_version: 2
|
|
@@ -127,92 +186,74 @@ exclude:
|
|
|
127
186
|
- build
|
|
128
187
|
- .cache
|
|
129
188
|
- __pycache__
|
|
130
|
-
agent: auto
|
|
189
|
+
agent: auto # auto, codex, claude, or none (workspace only)
|
|
131
190
|
```
|
|
132
191
|
|
|
133
|
-
|
|
192
|
+
Paths passed with `--include` or `--include-secret` stay managed in later handoffs, including deletions. Adding a managed file to `exclude` stops sending it and leaves the receiving copy in place with a warning.
|
|
193
|
+
|
|
194
|
+
Private state lives in `~/.onthego` (or `$ONTHEGO_HOME`), with `0700` directories and `0600` files: paired devices in `config.yaml`, keys in `identity/`, and per-project paths under `projects/`. ONTHEGO never loads `.env` files.
|
|
134
195
|
|
|
135
|
-
|
|
196
|
+
Shell completion, including device names:
|
|
136
197
|
|
|
137
198
|
```sh
|
|
138
|
-
onthego
|
|
139
|
-
onthego device list
|
|
140
|
-
onthego identity
|
|
141
|
-
onthego push laptop --include scratch/notes.txt
|
|
142
|
-
onthego push laptop --include-secret .env
|
|
199
|
+
onthego completion zsh > "${fpath[1]}/_onthego" # also: bash, fish
|
|
143
200
|
```
|
|
144
201
|
|
|
145
|
-
|
|
202
|
+
## Offline handoff
|
|
146
203
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
## Portable snapshots
|
|
150
|
-
|
|
151
|
-
For removable storage or another delivery channel, create an encrypted `.otg` file:
|
|
204
|
+
Without a network path, hand off through an encrypted file on a USB drive or any other channel:
|
|
152
205
|
|
|
153
206
|
```sh
|
|
154
|
-
onthego
|
|
207
|
+
onthego identity # on the receiving device: prints its public keys
|
|
208
|
+
onthego snapshot pack --output /tmp/my-app.otg --recipient age1...
|
|
155
209
|
|
|
156
210
|
# On the receiving device:
|
|
157
|
-
onthego snapshot restore /tmp/
|
|
158
|
-
--output /path/to/project \
|
|
159
|
-
--signing-public <trusted-source-public-key>
|
|
211
|
+
onthego snapshot restore /tmp/my-app.otg --output ~/projects/my-app --signing-public <sender-signing-key>
|
|
160
212
|
```
|
|
161
213
|
|
|
162
|
-
Use the recipient and signing public keys printed by `onthego identity`. Without `--recipient`, packing targets the local device. SSH handoffs resolve both identities from pairing; offline restores require you to authenticate the source signing key.
|
|
163
|
-
|
|
164
|
-
Offline exports retain pending receipts for up to 30 days, bounded to 64 per workspace. A returning descendant can acknowledge this lineage without `--replace`. Retrying an already applied snapshot succeeds without overwriting later local edits.
|
|
165
|
-
|
|
166
214
|
## Commands
|
|
167
215
|
|
|
168
216
|
| Command | Purpose |
|
|
169
217
|
| --- | --- |
|
|
170
|
-
| `
|
|
171
|
-
| `device
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
| `sync <device>` | Push or pull, whichever brings both devices up to date. |
|
|
219
|
+
| `push <device>`, `pull <device>` | Hand off in an explicit direction. |
|
|
220
|
+
| `status [device]` | Show changes since the last handoff and who has the work. |
|
|
221
|
+
| `inspect` | Preview selected files, excluded secrets, and sessions. |
|
|
222
|
+
| `init` | Set up the current Git project. |
|
|
223
|
+
| `device add/list/path/install/remove` | Pair devices, map project paths, install or upgrade onthego on them. |
|
|
224
|
+
| `tailscale` | Check Tailscale and list tailnet devices to pair. |
|
|
225
|
+
| `doctor [device]` | Check Git, SSH, Tailscale, agents, and a device connection. |
|
|
226
|
+
| `undo`, `backups list/prune` | Restore or manage workspace backups. |
|
|
227
|
+
| `sessions recover` | Retry an agent session import after a partial handoff. |
|
|
228
|
+
| `snapshot pack/restore` | Offline handoff through an encrypted file. |
|
|
229
|
+
| `identity`, `config path`, `completion`, `version` | Keys, settings location, shell completion, build info. |
|
|
230
|
+
|
|
231
|
+
`--json` prints structured results and errors, `--verbose` prints transfer phases on stderr.
|
|
232
|
+
|
|
233
|
+
| Exit status | Meaning |
|
|
185
234
|
| --- | --- |
|
|
186
|
-
| 0 | Success
|
|
235
|
+
| 0 | Success (including a delivered workspace with pending session recovery) |
|
|
187
236
|
| 1 | `E_INTERNAL` |
|
|
188
237
|
| 2 | `E_INPUT` |
|
|
189
238
|
| 3 | `E_PRECONDITION`, `E_UNSUPPORTED_GIT`, `E_SECRET_POLICY`, `E_SESSION_FORMAT` |
|
|
190
|
-
| 4 | `E_TARGET_AUTH`, `E_TARGET_UNAVAILABLE` |
|
|
239
|
+
| 4 | `E_TARGET_AUTH`, `E_TARGET_UNAVAILABLE` (SSH login, host key, or reachability) |
|
|
191
240
|
| 5 | `E_PACKAGE_INVALID`, `E_RESTORE_FAILED` |
|
|
192
241
|
| 6 | `E_GENERATION_CONFLICT`, `E_SOURCE_CHANGED` |
|
|
193
242
|
| 130 | `E_CANCELED` |
|
|
194
243
|
|
|
195
|
-
## Upgrading
|
|
196
|
-
|
|
197
|
-
Upgrade both devices together. Snapshot schema 3 adds signed `retained_paths` and `payload_sizes`; 0.3.1 reads schema 2 and 3, while older receivers reject schema 3. Peer transfers require the `snapshot-v3` and `session-scope` capabilities. Project policy and handoff receipt schemas remain 2; session recovery uses a separate state file.
|
|
198
|
-
|
|
199
|
-
If you transferred an untracked credential file such as `.npmrc` with 0.3.0 or earlier, rotate its tokens. Those bytes can remain in the source and receiving repositories' Git objects through retained WIP history. This update prevents new automatic capture but does not remove existing objects; Git garbage collection alone does not remove referenced history. See the [0.3.1 release notes](docs/releases/0.3.1.md).
|
|
244
|
+
## Upgrading
|
|
200
245
|
|
|
201
|
-
|
|
246
|
+
0.4 keeps the 0.3.1 wire protocol and file formats, so a 0.4 device can hand off with a 0.3.1 device. Keeping every device on the same version is still the simplest: after `npm update -g @jbaehova/onthego`, run `onthego device install <name>` for each SSH device. `onthego remote set` still works but is replaced by `onthego device path`. See the [release notes](docs/releases/0.4.0.md) and [migration notes](docs/migration.md).
|
|
202
247
|
|
|
203
|
-
|
|
204
|
-
make check
|
|
205
|
-
go test -race ./...
|
|
206
|
-
make build
|
|
207
|
-
./scripts/demo.sh
|
|
208
|
-
./scripts/ssh-e2e.sh
|
|
209
|
-
make package
|
|
210
|
-
```
|
|
248
|
+
## More
|
|
211
249
|
|
|
212
|
-
|
|
250
|
+
- [Tailscale guide](docs/tailscale.md)
|
|
251
|
+
- [Agent compatibility](docs/agent-compatibility.md): tested Codex and Claude Code versions and how resume is verified.
|
|
252
|
+
- [Architecture](docs/architecture.md): how capture, encryption, in-place application, and receipts work.
|
|
253
|
+
- [Development](docs/development.md): tests, demos, end-to-end checks, and benchmarks.
|
|
213
254
|
|
|
214
|
-
|
|
255
|
+
Continuous background synchronization is intentionally out of scope: handoffs are explicit, so you always know which device has the work.
|
|
215
256
|
|
|
216
257
|
## License
|
|
217
258
|
|
|
218
|
-
[MIT](LICENSE)
|
|
259
|
+
[MIT](LICENSE)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|