@sailorbridge/client 0.1.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/LICENSE +201 -0
- package/README.md +411 -0
- package/dist/src/cli.js +16276 -0
- package/dist/src/cli.js.map +90 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# SailorBridge Client
|
|
2
|
+
|
|
3
|
+
Standalone Node/TypeScript client for SailorBridge: a relay HTTP client, a debug
|
|
4
|
+
CLI, a worker host runtime, and a tmux supervisor that owns one local Claude or
|
|
5
|
+
Codex agent process and reports monitor state to the relay. Host pairing login
|
|
6
|
+
stores a host-scoped relay key for subsequent local host commands.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @sailorbridge/client
|
|
12
|
+
sailorbridge --help
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
When installing globally on Linux or macOS, make sure your npm global prefix is
|
|
16
|
+
writable and its `bin` directory is on `PATH`. Prefer a user-owned Node install
|
|
17
|
+
from tools such as nvm/asdf over `sudo npm install -g`. Linux and macOS are the
|
|
18
|
+
supported targets; Windows packaging support is deferred.
|
|
19
|
+
|
|
20
|
+
For a managed host daemon, use the compiled single-binary path instead of a
|
|
21
|
+
Node-based global install. Build or download the native `sailorbridge` binary,
|
|
22
|
+
then log in once — `login` installs and starts the user service itself:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bun run build:binary
|
|
26
|
+
./bin/sailorbridge login
|
|
27
|
+
./bin/sailorbridge host service status
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The service runs `sailorbridge host run` from the binary and reads the
|
|
31
|
+
host-scoped credential file. The unit/plist never embeds bearer tokens. After
|
|
32
|
+
the binary is built or installed, the managed host runtime does not require
|
|
33
|
+
Node, Bun, or Python on `PATH`.
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
Configure relay credentials:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export SAILORBRIDGE_RELAY_URL=http://127.0.0.1:8765
|
|
41
|
+
export SAILORBRIDGE_TOKEN=...
|
|
42
|
+
export SAILORBRIDGE_AGENT_NAME=session_agent
|
|
43
|
+
export SAILORBRIDGE_CAPTAIN_ID=100
|
|
44
|
+
export SAILORBRIDGE_SESSION=session
|
|
45
|
+
export SAILORBRIDGE_HOST_ID="$(hostname)"
|
|
46
|
+
export SAILORBRIDGE_HOST_DISPLAY_NAME="$(hostname)"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or pair this machine through the product API:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
sailorbridge login
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`login` connects to `https://www.sailorbridge.com`. The command prints a short
|
|
56
|
+
host pairing code, waits for console/mobile approval, writes a 0600 credentials
|
|
57
|
+
file at `~/.sailorbridge/credentials.json`, and then installs and starts the
|
|
58
|
+
host service so the console can start workers and agents on this machine without
|
|
59
|
+
a second command.
|
|
60
|
+
|
|
61
|
+
Common commands:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sailorbridge --help
|
|
65
|
+
sailorbridge login
|
|
66
|
+
sailorbridge workflow
|
|
67
|
+
sailorbridge mailbox peek "$SAILORBRIDGE_AGENT_NAME"
|
|
68
|
+
sailorbridge mailbox ack MESSAGE_ID --lease-token LEASE_TOKEN
|
|
69
|
+
printf '%s\n' 'hello $USER `date` $(pwd)' | sailorbridge mailbox send --to-agents ab_hand --tag BTW
|
|
70
|
+
sailorbridge mailbox send --to-roles reviewer --session ab --tag PLAN-REVIEW --body-file ./plan.txt
|
|
71
|
+
sailorbridge enrollment --session ab --agent ab_hand --roles operator
|
|
72
|
+
sailorbridge host register
|
|
73
|
+
sailorbridge host consume --once --dry-run-start
|
|
74
|
+
sailorbridge host service install --binary /opt/sailorbridge/bin/sailorbridge
|
|
75
|
+
sailorbridge host service status
|
|
76
|
+
sailorbridge worker serve --worker dev-1 --workdir "$PWD"
|
|
77
|
+
sailorbridge supervise --agent ab_hand --cli codex --cwd "$PWD" --i-own-this-agent
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The package ships only the built CLI/runtime files under `dist/src/` plus this
|
|
81
|
+
README. It deliberately does not publish `src/`, `tests/`, or `dist/tests/`.
|
|
82
|
+
Create a release tarball with `npm run pack:staged`; it stages the current build without
|
|
83
|
+
mutating the live `dist/` symlink.
|
|
84
|
+
Publish that staged tarball with `npm publish sailorbridge-client-<version>.tgz`;
|
|
85
|
+
do not run `npm publish` directly from this package.
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bun install
|
|
91
|
+
bun run check
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Building requires Perl with `Fcntl::flock` (available on supported Linux and macOS hosts).
|
|
95
|
+
Builds retain immutable releases for at least one hour so in-flight readers remain
|
|
96
|
+
loadable; older non-current releases are reclaimed.
|
|
97
|
+
|
|
98
|
+
The package installs a `sailorbridge` bin after build:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
bun run build
|
|
102
|
+
node dist/src/cli.js workflow
|
|
103
|
+
node dist/src/cli.js mailbox peek "$SAILORBRIDGE_AGENT_NAME"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Build a native standalone binary for the current host:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bun run build:binary
|
|
110
|
+
./bin/sailorbridge --help
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`build:binary` defaults to the native Bun compile target. Release automation can
|
|
114
|
+
pass `--target` through `scripts/build-binary.mjs` for the supported matrix:
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
linux-x64 -> bun-linux-x64
|
|
118
|
+
linux-arm64 -> bun-linux-arm64
|
|
119
|
+
darwin-x64 -> bun-darwin-x64
|
|
120
|
+
darwin-arm64 -> bun-darwin-arm64
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
Preferred environment:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
SAILORBRIDGE_RELAY_URL=http://127.0.0.1:8765
|
|
129
|
+
SAILORBRIDGE_TOKEN=...
|
|
130
|
+
SAILORBRIDGE_AGENT_NAME=session_agent
|
|
131
|
+
SAILORBRIDGE_CAPTAIN_ID=100
|
|
132
|
+
SAILORBRIDGE_SESSION=session
|
|
133
|
+
SAILORBRIDGE_HOST_ID=mac
|
|
134
|
+
SAILORBRIDGE_HOST_DISPLAY_NAME="Mac"
|
|
135
|
+
SAILORBRIDGE_HOST_WORKSPACES=~/project-a:~/project-b
|
|
136
|
+
SAILORBRIDGE_API_URL=https://www.sailorbridge.com
|
|
137
|
+
SAILORBRIDGE_CREDENTIALS_FILE=~/.sailorbridge/credentials.json
|
|
138
|
+
SAILORBRIDGE_WORKER_NAME=dev-1
|
|
139
|
+
SAILORBRIDGE_WORKDIR=~/sailorbridge-workdir
|
|
140
|
+
SAILORBRIDGE_MAX_CONCURRENCY=1
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`SAILORBRIDGE_CAPTAIN_ID` is optional for current descriptors but, when present,
|
|
144
|
+
must be a numeric string. It is diagnostic identity metadata; routing still uses
|
|
145
|
+
the short `SAILORBRIDGE_AGENT_NAME`.
|
|
146
|
+
|
|
147
|
+
`SAILORBRIDGE_HOST_ID` and `SAILORBRIDGE_HOST_DISPLAY_NAME` identify the
|
|
148
|
+
captain-scoped local host for Tier 6 console start-request polling. When omitted,
|
|
149
|
+
host commands default both values from the OS hostname.
|
|
150
|
+
`SAILORBRIDGE_HOST_WORKSPACES` is an explicit opt-in inventory list for host
|
|
151
|
+
daemon reporting, split by the platform path delimiter (`:` on Linux/macOS). The
|
|
152
|
+
daemon reports only those declared git workspaces, including `remote.origin.url`
|
|
153
|
+
and the current branch, so the console can queue an isolated clone from that
|
|
154
|
+
origin. It does not scan home directories.
|
|
155
|
+
|
|
156
|
+
Every 6 hours the host daemon also runs a scratch sweep over each declared
|
|
157
|
+
workspace, reaping `<root>/.agent/tmp/` (a git-ignored scratch directory): it
|
|
158
|
+
deletes git-untracked files older than 7 days, then evicts oldest-first until the
|
|
159
|
+
directory total is under 512 MiB. It only ever touches untracked files inside that
|
|
160
|
+
directory, never follows symlinks, and skips (with a log) any workspace whose
|
|
161
|
+
`.agent/tmp/` resolves out of bounds or is not a git repository. Set
|
|
162
|
+
`SAILORBRIDGE_SCRATCH_SWEEP_ENABLED=0` (or `false`/`no`/`off`) to disable it;
|
|
163
|
+
it is on by default.
|
|
164
|
+
|
|
165
|
+
`sailorbridge worker serve` runs the TypeScript host worker against the relay
|
|
166
|
+
without importing the Python worker package. It registers the configured worker
|
|
167
|
+
name, long-polls `/pull`, executes jobs inside `SAILORBRIDGE_WORKDIR`, posts
|
|
168
|
+
results with the relay lease token, and streams stdout/stderr for stream jobs.
|
|
169
|
+
Use `--worker`, `--workdir`, and `--max-concurrency` or the matching
|
|
170
|
+
`SAILORBRIDGE_WORKER_NAME`, `SAILORBRIDGE_WORKDIR`, and
|
|
171
|
+
`SAILORBRIDGE_MAX_CONCURRENCY` environment variables. `SAILORBRIDGE_MODE=readonly`
|
|
172
|
+
blocks mutating job kinds locally.
|
|
173
|
+
|
|
174
|
+
`sailorbridge login` creates a host pairing against
|
|
175
|
+
`https://www.sailorbridge.com`. The command displays the one-time code and
|
|
176
|
+
polls until approval. The stored credential contains a host-scoped relay key
|
|
177
|
+
(`key_scope: "host"` plus `host_id`) and is used as a fallback when
|
|
178
|
+
`SAILORBRIDGE_RELAY_URL` or `SAILORBRIDGE_TOKEN` are not set. Raw host keys are
|
|
179
|
+
written to the credentials file only. Data commands default to
|
|
180
|
+
`--format plain`; login's plain success output prints the host id, credential
|
|
181
|
+
path, and service status. Explicit `--format json` prints key id and host
|
|
182
|
+
metadata, not the token.
|
|
183
|
+
|
|
184
|
+
`sailorbridge login` installs this service automatically; it exits 3 when
|
|
185
|
+
pairing succeeded but the install did not, and states that the host remains
|
|
186
|
+
offline and cannot receive console tasks before naming the command to run by hand.
|
|
187
|
+
Pass `--no-service` (or set `SAILORBRIDGE_NO_SERVICE=1`) to pair only — the
|
|
188
|
+
opt-out for containers, WSL, and CI images without a service manager.
|
|
189
|
+
Re-running `login` refreshes the definition and restarts the daemon so it always
|
|
190
|
+
runs with the arguments on disk. A markerless legacy definition is taken over
|
|
191
|
+
only when its parsed executable and arguments identify `sailorbridge host run`:
|
|
192
|
+
the old daemon is stopped/unloaded and the definition is renamed to a
|
|
193
|
+
`.sb-legacy-<UTC timestamp>` archive before reinstall. Foreign definitions,
|
|
194
|
+
symlinks, and definitions serving another host remain rejected; move a foreign
|
|
195
|
+
file aside or pass `--service-name` to install a scoped
|
|
196
|
+
`sailorbridge-host-<suffix>` unit.
|
|
197
|
+
`sailorbridge host service install|uninstall|status` manages the long-running
|
|
198
|
+
host daemon as a per-user service afterwards. Linux uses user systemd with
|
|
199
|
+
`Restart=on-failure`; macOS uses a user LaunchAgent with `KeepAlive`. The service
|
|
200
|
+
definition points at a credentials file and a binary path; it does not inline
|
|
201
|
+
tokens. Linux `status` detects whether linger is enabled. If boot persistence is
|
|
202
|
+
not active, `install` and `status` print the exact
|
|
203
|
+
`sudo loginctl enable-linger <user>` command for the user to run, but never run
|
|
204
|
+
sudo automatically. macOS LaunchAgents have no linger equivalent.
|
|
205
|
+
|
|
206
|
+
## Relay Contract
|
|
207
|
+
|
|
208
|
+
This client reimplements the relay HTTP contract in TypeScript. It does not
|
|
209
|
+
import the Python SailorBridge engine, SailorBridge Site, or SailorAgent code. Key creation uses
|
|
210
|
+
the observed relay contract `roles: string[]`; the legacy singular `role` field
|
|
211
|
+
is deprecated and intentionally not sent.
|
|
212
|
+
|
|
213
|
+
Client host commands implement the P5a relay contract from `engine/PROTOCOL.md`:
|
|
214
|
+
registering or heartbeating a host, listing captain-scoped hosts, and claiming a
|
|
215
|
+
single `agent_start_request`. Host-paired credentials are limited to their bound
|
|
216
|
+
host id and cannot list sibling hosts. In P5a, `sailorbridge host consume` requires
|
|
217
|
+
`--once --dry-run-start`; it validates and transitions a claimed request but does
|
|
218
|
+
not launch an engine process. `sailorbridge host run` is the real long-running
|
|
219
|
+
host daemon: it claims console start requests, redeems an agent credential,
|
|
220
|
+
prepares the workspace, and starts the supervised agent.
|
|
221
|
+
On daemon restart it asks the product API for every still-active member owned by
|
|
222
|
+
the host, rotates the agent credential, and rebuilds the exact relay-reported
|
|
223
|
+
tmux target before it resumes polling new start requests. Workers started by the
|
|
224
|
+
same daemon are restored from the same durable ledger. Console **Relaunch** also
|
|
225
|
+
queues a new host start, so it does not depend on the old supervisor still polling.
|
|
226
|
+
Host-started Claude and Codex agents get terse-output defaults and Claude output
|
|
227
|
+
hygiene hooks to reduce routine token spend without lowering reasoning effort or
|
|
228
|
+
verification requirements.
|
|
229
|
+
Before launching an agent, the host must already be logged in to that engine:
|
|
230
|
+
Codex uses `$CODEX_HOME/auth.json` when set, otherwise `~/.codex/auth.json`;
|
|
231
|
+
Claude uses `$CLAUDE_CONFIG_DIR/.credentials.json` when set, otherwise
|
|
232
|
+
`~/.claude/.credentials.json`.
|
|
233
|
+
The per-agent sandbox links to that single host auth file; missing engine auth
|
|
234
|
+
fails fast instead of starting a CLI that will prompt for credentials.
|
|
235
|
+
|
|
236
|
+
Worker commands implement the relay worker contract from `engine/PROTOCOL.md`:
|
|
237
|
+
register, heartbeat, pull, stream upload, and result posting. Worker HTTP uses
|
|
238
|
+
bounded connect/read-idle timeouts for liveness and leaves result uploads without
|
|
239
|
+
a total timeout cap.
|
|
240
|
+
|
|
241
|
+
Mailbox claim is a destructive operation on the relay; `mailbox peek` is the
|
|
242
|
+
non-destructive command. Add `--lease` to a claim only to include rows already
|
|
243
|
+
leased by this client. A supervisor peek leaves workflow-obligation messages
|
|
244
|
+
queued and claimable after it surfaces the body-free doorbell. The bound agent's
|
|
245
|
+
first `mailbox claim` leases them, then the agent completes the required action.
|
|
246
|
+
Direct workflow messages are acknowledged with the lease token returned by the
|
|
247
|
+
claim. Durable task sources are acknowledged by their successful task
|
|
248
|
+
transition, while task terminal replies and `UNPARKED` are auto-acked after
|
|
249
|
+
delivery. Ordinary and control messages keep their existing acknowledgement
|
|
250
|
+
behavior.
|
|
251
|
+
|
|
252
|
+
Mailbox send is footgun-free by construction: message bodies are read only from
|
|
253
|
+
piped stdin or `--body-file`, never from positional argv. Every `--body-file` and
|
|
254
|
+
`--description-file` flag treats `-` as stdin; `task create --description -` is the
|
|
255
|
+
same stdin shorthand, as is positional `reply` body `-`. Non-empty piped input
|
|
256
|
+
that another selected source would discard is rejected instead of silently
|
|
257
|
+
ignored; commands without a stdin contract reject it before any relay request.
|
|
258
|
+
This prevents the local
|
|
259
|
+
shell from expanding `$VARS`, backticks, or `$()` before the client starts. A
|
|
260
|
+
single `--to-agents A` sends directly to that agent even when `--session` is also
|
|
261
|
+
present. Multiple explicit agents use the session mailbox endpoint. `--to-agents`
|
|
262
|
+
is mutually exclusive with `--to-roles` and `--to-host`; combine roles and host
|
|
263
|
+
when filtering registered agents. An empty pipe sends an empty string body; an
|
|
264
|
+
interactive TTY with no pipe fails fast instead of waiting for typed input.
|
|
265
|
+
|
|
266
|
+
Plaintext bearer tokens are never printed by default. Enrollment descriptors
|
|
267
|
+
contain a plaintext token and are printed only when the caller explicitly runs
|
|
268
|
+
the enrollment command.
|
|
269
|
+
|
|
270
|
+
## Driver Contract
|
|
271
|
+
|
|
272
|
+
Phase 1 exposes only the tmux transport for Claude and Codex agents. SDK,
|
|
273
|
+
headless, and PTY driver code remains in the package for a later release, but
|
|
274
|
+
the public CLI and host-start surfaces fail fast if callers request those
|
|
275
|
+
transports or request postponed engines such as opencode, copilot, or a custom
|
|
276
|
+
CLI id. There is no silent downgrade to tmux or PTY.
|
|
277
|
+
|
|
278
|
+
The client provides a shared `AgentDriver` interface. The public Phase 1 path is
|
|
279
|
+
the tmux backend; all drivers share the same transport-level contract: `start`,
|
|
280
|
+
`send`, `on`, `resume`, `isAlive`, and `stop`.
|
|
281
|
+
|
|
282
|
+
The tmux driver creates a dedicated tmux session by default and only destroys
|
|
283
|
+
sessions it created. Existing-session mode can send or interrupt, but it does
|
|
284
|
+
not launch a process because environment secrets must not be typed into an
|
|
285
|
+
interactive pane. Host-started agents use readable session names such as
|
|
286
|
+
`sb-<team>-<role>` and append the captain number only when that default already
|
|
287
|
+
exists on the host.
|
|
288
|
+
|
|
289
|
+
Tmux launch environment is passed through tmux process mechanisms (`-e` and the
|
|
290
|
+
child process environment), never as `KEY=value command` text typed into the
|
|
291
|
+
pane. That still has the normal short-lived process-list exposure of command
|
|
292
|
+
arguments; cleaner secret delivery belongs to a later transport release.
|
|
293
|
+
|
|
294
|
+
SDK drivers are optional implementation code and are release-gated from the
|
|
295
|
+
public Phase 1 CLI. A base install or tmux-only caller does not need to
|
|
296
|
+
instantiate them. The pinned SDK versions are:
|
|
297
|
+
|
|
298
|
+
- `@anthropic-ai/claude-agent-sdk@0.3.168`
|
|
299
|
+
- `@openai/codex-sdk@0.137.0`
|
|
300
|
+
|
|
301
|
+
Anthropic's public Claude Code SDK docs still reference
|
|
302
|
+
`@anthropic-ai/claude-code` in some places; the implementation is grounded in
|
|
303
|
+
the `@anthropic-ai/claude-agent-sdk@0.3.168` npm package declarations. Unit
|
|
304
|
+
tests use injected fake SDK facades to verify our event mapping and option
|
|
305
|
+
assembly; they do not prove live provider behavior. Real SDK smoke tests exist
|
|
306
|
+
but are skipped by default. Set `SAILORBRIDGE_REAL_SDK_SMOKE=1` to run them.
|
|
307
|
+
|
|
308
|
+
Productized supervisors default to unattended execution so a BYO agent does not
|
|
309
|
+
stop on local permission prompts: Claude uses `bypassPermissions` and Codex uses
|
|
310
|
+
`danger-full-access` plus `approvalPolicy: "never"` / `yolo`. Explicit
|
|
311
|
+
`--permission-mode`, `--sandbox-mode`, `--approval-policy`, or `--yolo false`
|
|
312
|
+
opt back into a more interactive or constrained launch; the tmux transport
|
|
313
|
+
translates those flags into first-party CLI argv. An explicit `-- COMMAND...`
|
|
314
|
+
is always used verbatim; combining it with safety flags fails fast because those
|
|
315
|
+
flags would not be applied to the verbatim argv. `--yolo true` is also mutually
|
|
316
|
+
exclusive with explicit Codex sandbox or approval-policy constraints.
|
|
317
|
+
|
|
318
|
+
SDK capabilities, PTY support, opencode/copilot adapters, and custom CLI ids are
|
|
319
|
+
deferred to the next release. Their implementation files may exist in this
|
|
320
|
+
package, but Phase 1 help, docs, and validation do not expose them as supported
|
|
321
|
+
product surface.
|
|
322
|
+
|
|
323
|
+
Mailbox consumption, `CLEAR`, and `WORKFLOW-REFRESH` handling are supervisor
|
|
324
|
+
responsibilities (see Supervisor Contract). The driver only manages transport
|
|
325
|
+
lifecycle, liveness inspection, and text delivery.
|
|
326
|
+
|
|
327
|
+
## Supervisor Contract
|
|
328
|
+
|
|
329
|
+
The TypeScript supervisor owns one local agent process,
|
|
330
|
+
chooses a transport, leases that agent's mailbox, and reports only real
|
|
331
|
+
session/profile changes back to the relay.
|
|
332
|
+
|
|
333
|
+
Ownership is exclusive: do not run this supervisor and the Python SailorBridge
|
|
334
|
+
listener against the same `agent_name`. A single agent inbox and runtime control
|
|
335
|
+
channel must have one owner, otherwise mailbox leases can be stolen. Use
|
|
336
|
+
`sailorbridge supervise` only for agents not already driven by another
|
|
337
|
+
listener/supervisor.
|
|
338
|
+
|
|
339
|
+
Default transport selection:
|
|
340
|
+
|
|
341
|
+
| CLI | Default transport | Default command |
|
|
342
|
+
| --- | --- | --- |
|
|
343
|
+
| `claude` | `tmux` | `claude` |
|
|
344
|
+
| `codex` | `tmux` | `codex` |
|
|
345
|
+
|
|
346
|
+
`claude`/`codex` default to `tmux` so the agent runs the real first-party CLI
|
|
347
|
+
(covered by the captain's Claude/Codex subscription and attachable). When `tmux`
|
|
348
|
+
is the resolved transport and no `--tmux-session` is given, host-started agents
|
|
349
|
+
use readable names such as `sb-<team>-<role>`; same-host collisions try the
|
|
350
|
+
default name, then the captain number, then the agent name, followed by numeric
|
|
351
|
+
suffixes `-1` through `-100`. If every candidate is occupied, startup fails
|
|
352
|
+
explicitly. Direct `sailorbridge supervise` has no team/role context, so it
|
|
353
|
+
falls back to a readable `sb-<agent>` name.
|
|
354
|
+
`--tmux-session` overrides all derived names.
|
|
355
|
+
|
|
356
|
+
When `tmux` is the resolved transport but no `tmux` binary is found, the client
|
|
357
|
+
fails fast or asks before installing it:
|
|
358
|
+
|
|
359
|
+
- **default path** (no `--transport`) → resolves to tmux and fails
|
|
360
|
+
fast with a platform install hint.
|
|
361
|
+
- **explicit `--transport tmux`** on an interactive TTY → prompts: `[1]` install
|
|
362
|
+
tmux (macOS `brew` / Linux `apt-get`/`dnf`/`pacman`, asking for confirmation
|
|
363
|
+
and showing the exact command — including `sudo` — before running it),
|
|
364
|
+
or `[2]` cancel.
|
|
365
|
+
- **explicit `--transport tmux`** without a TTY (e.g. CI) → fails fast with the
|
|
366
|
+
platform install hint. It never installs silently or downgrades the transport.
|
|
367
|
+
|
|
368
|
+
`--transport tmux` launches through the tmux driver, using an explicit
|
|
369
|
+
`--tmux-session` when given or otherwise deriving the readable default described
|
|
370
|
+
above; pass `--tmux-session` for direct-supervise collision control. It is the
|
|
371
|
+
attach/mirror option because tmux reports `attach: true`. Explicit
|
|
372
|
+
`--transport auto` is rejected so the public surface stays tmux-only; `sdk`,
|
|
373
|
+
`headless`, and `pty` fail fast as next-release transports.
|
|
374
|
+
|
|
375
|
+
Supervisor relaunch decisions are based only on local process liveness:
|
|
376
|
+
`driver.on("exit")` or `isAlive().processAlive === false`. Reply liveness is
|
|
377
|
+
not reported as relay presence and never triggers relaunch by itself.
|
|
378
|
+
|
|
379
|
+
`CLEAR` and `WORKFLOW-REFRESH` are monitor-handled control messages. They are
|
|
380
|
+
claimed and acknowledged by the supervisor after successful control cue delivery;
|
|
381
|
+
they are not normal work prompts. For the current tmux transport, `CLEAR` sends
|
|
382
|
+
`/clear`. Future SDK transports must expose a real session reset or document
|
|
383
|
+
their best-effort semantics before they become public.
|
|
384
|
+
|
|
385
|
+
The supervisor owns mailbox leases for its agent and long-polls with
|
|
386
|
+
`wait=true` and `peek=false`. Delivery is serial and control-first. If driver
|
|
387
|
+
delivery fails, the message is not acknowledged so the relay lease can expire and
|
|
388
|
+
requeue it. Surface telemetry is observability-only;
|
|
389
|
+
acknowledgement and driver delivery failures remain real errors. Shutdown
|
|
390
|
+
aborts the in-flight long poll and never acknowledges a partially delivered
|
|
391
|
+
message. Persistent relay errors back off instead of spinning.
|
|
392
|
+
|
|
393
|
+
The CLI entrypoint is explicit about ownership:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
sailorbridge supervise --agent A --cli codex --cwd /repo --i-own-this-agent
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`--i-own-this-agent` is required as a runtime collision guard. For tmux launches,
|
|
400
|
+
the built-in Claude/Codex default commands include the matching no-prompt flags
|
|
401
|
+
unless explicit safety flags request a constrained command. Explicit command argv
|
|
402
|
+
is used verbatim and cannot be combined with safety flags.
|
|
403
|
+
The runtime fetches the relay monitor record before start/relaunch and uses
|
|
404
|
+
`session_handle` only when `should_resume` is true and the selected driver
|
|
405
|
+
reports `resume: true`. After every successful start/rebuild it patches the
|
|
406
|
+
driver's live handle back to the relay; for tmux this is the final
|
|
407
|
+
collision-resolved session name consumed by roster `tmux_session_name`.
|
|
408
|
+
|
|
409
|
+
The liveness loop no longer patches relay presence. Relaunch is attempted only from `driver.on("exit")` or
|
|
410
|
+
`isAlive().processAlive === false`, with capped exponential backoff and a
|
|
411
|
+
give-up state after repeated failures. `replyAlive` never causes a relaunch by itself.
|