@obtoai/agent-bridge 0.1.0-beta.4 → 0.1.0-beta.6
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 +55 -31
- package/bin/obto-bridge.js +6 -5
- package/cli/init.js +107 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
# @obtoai/agent-bridge
|
|
2
2
|
|
|
3
|
-
A local daemon that lets
|
|
3
|
+
A local daemon that lets coding agents — [Claude Code](https://claude.ai/code), [OpenAI Codex](https://developers.openai.com/codex), or [opencode](https://opencode.ai) — running on your machine be driven from the [OBTO Agent Bridge](https://obto.co) web UI, even when you're away from the keyboard.
|
|
4
4
|
|
|
5
|
-
You post a message on a thread from your phone or laptop. The daemon (running on your machine, no port forwarding required) receives it over a long-lived HTTPS stream, spawns or resumes
|
|
5
|
+
You post a message on a thread from your phone or laptop. The daemon (running on your machine, no port forwarding required) receives it over a long-lived HTTPS stream, spawns or resumes a session for the agent that thread is bound to, and the response posts back to the bridge thread within seconds.
|
|
6
|
+
|
|
7
|
+
**Three commands, you're driving Claude/Codex/opencode on your phone:**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @obtoai/agent-bridge
|
|
11
|
+
obto-bridge init # creates a free account inline — no card, no invite
|
|
12
|
+
obto-bridge start # daemon connects, you're live
|
|
13
|
+
```
|
|
6
14
|
|
|
7
15
|
## Status
|
|
8
16
|
|
|
9
|
-
**
|
|
17
|
+
**Public beta.** Self-serve, no card. `obto-bridge init` creates a free account inline — no waiting on an invite, no support email loop. A Pro tier with longer Hindsight memory retention is coming; until then everyone gets the same daemon features on the free tier.
|
|
10
18
|
|
|
11
19
|
## What you'll need
|
|
12
20
|
|
|
13
21
|
- macOS, Linux, or Windows, **Node.js 18.17+**
|
|
14
|
-
-
|
|
15
|
-
- **Claude** — Claude Code / the Claude Agent SDK, billed to your Anthropic account
|
|
16
|
-
- **Codex** — the `codex` CLI (`npm i -g @openai/codex`), signed in to your OpenAI/ChatGPT account
|
|
17
|
-
-
|
|
22
|
+
- At least one coding agent installed on the machine (the daemon drives whichever ones it finds, with your own auth):
|
|
23
|
+
- **Claude** — Claude Code / the Claude Agent SDK, billed to your Anthropic account.
|
|
24
|
+
- **Codex** — the `codex` CLI (`npm i -g @openai/codex`), signed in to your OpenAI/ChatGPT account.
|
|
25
|
+
- **opencode** — `npm i -g opencode-ai` (the `opencode` CLI; the daemon bundles the `@opencode-ai/sdk`). Auth is your own provider key (Anthropic by default; override with env vars below).
|
|
18
26
|
|
|
19
27
|
## Install
|
|
20
28
|
|
|
@@ -34,18 +42,31 @@ npx @obtoai/agent-bridge <command>
|
|
|
34
42
|
obto-bridge init
|
|
35
43
|
```
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
`init` prompts for an email, a username (3-40 chars, lowercase + digits + `_`/`-`), and a password, then creates the account inline via `POST /api/bridge/register` and saves the returned token to `~/.obto-bridge/config.json` (mode 0600). It then asks for an agent name (so multiple machines on the same account don't collide), the project directory the daemon should work in, a *fallback* agent (`claude` / `codex` / `opencode` — used only for legacy events without an explicit agent), and whether to relay tool-permission requests via the bridge. Done — you can sign in to the web UI as `@username` with your password to start a thread.
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
Already have a token from an earlier invite or another machine? Skip the registration step:
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
```bash
|
|
50
|
+
obto-bridge init --token obto_xxxxxxxx --account acc_xxxxxxxx
|
|
51
|
+
```
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
The token is shown to you exactly once at registration time. **Save it.** If you lose it, rotate it from your account settings — it is not stored in readable form server-side. Safe to commit your `accountId`; **never commit the `apiToken`**. (Server URL is a built-in default; advanced / self-hosted users can override with the `BRIDGE_BASE_URL` env var.)
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
- **codex** — runs the task and delivers one final answer per turn. No mid-task updates and no per-tool relay (the Codex SDK exposes neither); it runs unattended inside a sandbox (`workspace-write` by default, override with `BRIDGE_CODEX_SANDBOX`).
|
|
55
|
+
### Agents (claude / codex / opencode)
|
|
47
56
|
|
|
48
|
-
|
|
57
|
+
v1.1 makes the daemon **agent-agnostic per event**: at startup it detects which of `claude`, `codex`, and `opencode` are installed on the machine, advertises that to the bridge, and routes each incoming reply to the right driver based on what the thread is bound to in the UI. You can switch a thread's agent live from the thread header; each engine keeps its own session for that thread, so flipping claude→codex→claude resumes each side's context.
|
|
58
|
+
|
|
59
|
+
How the three differ in how they report back:
|
|
60
|
+
|
|
61
|
+
- **claude** — the fullest integration. Posts status updates, mid-task questions, and final results as it works (via an in-process MCP tool), and supports the human-in-the-loop tool-permission relay.
|
|
62
|
+
- **codex** — runs the turn and delivers one final answer per turn. No mid-task updates and no per-tool relay (the Codex SDK exposes neither). Runs unattended inside a sandbox (`workspace-write` by default, override with `BRIDGE_CODEX_SANDBOX`).
|
|
63
|
+
- **opencode** — same capture-model shape as codex: one final answer per turn, no mid-task chatter. Defaults to provider `anthropic` and model `claude-sonnet-4-5`; override with `BRIDGE_OPENCODE_PROVIDER` and `BRIDGE_OPENCODE_MODEL`.
|
|
64
|
+
|
|
65
|
+
Picking a model is done in the bridge UI's **+ New thread** dialog and the thread-header switcher — not in the daemon config.
|
|
66
|
+
|
|
67
|
+
### Multi-daemon (running across more than one machine)
|
|
68
|
+
|
|
69
|
+
You can run the same account's daemon on more than one machine (e.g. a Mac and a Windows box). Each daemon advertises its `agentId` (machine name) + capabilities on connect; threads are atomically **first-touch claimed** by whichever daemon gets the event first, and every other daemon skips the event cleanly. No duplicate replies, no special configuration — just install + start the daemon on each machine.
|
|
49
70
|
|
|
50
71
|
## Run
|
|
51
72
|
|
|
@@ -56,14 +77,16 @@ obto-bridge start
|
|
|
56
77
|
You'll see two log lines and then the daemon waits silently:
|
|
57
78
|
|
|
58
79
|
```
|
|
59
|
-
{"msg":"starting daemon","data":{"accountId":"acc_...","agentId":"my-mac",...}}
|
|
80
|
+
{"msg":"starting daemon","data":{"accountId":"acc_...","agentId":"my-mac","capabilities":["claude","codex"],...}}
|
|
60
81
|
{"msg":"sse stream connected","data":{"status":200}}
|
|
61
82
|
```
|
|
62
83
|
|
|
84
|
+
`capabilities` is the list of agents this daemon will accept — the bridge UI offers exactly the union across your connected machines.
|
|
85
|
+
|
|
63
86
|
Now open the bridge UI in any browser, log in with the browser credentials from your invite, and either:
|
|
64
87
|
|
|
65
|
-
- Reply on an existing thread — daemon resumes the session bound to that thread
|
|
66
|
-
- Start a new thread via the **+ New thread** button — daemon spawns a fresh session in your project directory
|
|
88
|
+
- Reply on an existing thread — daemon resumes the session bound to that thread (and to whichever agent the thread currently uses).
|
|
89
|
+
- Start a new thread via the **+ New thread** button — pick Claude, Codex, or Opencode; the daemon spawns a fresh session in your project directory.
|
|
67
90
|
|
|
68
91
|
Within ~5–10 seconds you should see the agent's reply appear back on the thread.
|
|
69
92
|
|
|
@@ -72,40 +95,41 @@ Within ~5–10 seconds you should see the agent's reply appear back on the threa
|
|
|
72
95
|
| Command | What it does |
|
|
73
96
|
|---|---|
|
|
74
97
|
| `obto-bridge whoami` | Verify your token works + show your account info |
|
|
75
|
-
| `obto-bridge status` | List
|
|
98
|
+
| `obto-bridge status` | List bindings per (thread, agent) — one row per engine that's ever driven a thread |
|
|
76
99
|
| `obto-bridge logout` | Wipe `~/.obto-bridge/config.json` |
|
|
77
100
|
|
|
78
101
|
## How it actually works
|
|
79
102
|
|
|
80
103
|
```
|
|
81
|
-
Your phone OBTO server Your machine
|
|
82
|
-
───────── ───────────
|
|
104
|
+
Your phone OBTO server Your machine(s)
|
|
105
|
+
───────── ─────────── ───────────────
|
|
83
106
|
[reply form] ──► /api/reply ─► Mongo (durable)
|
|
84
|
-
└─► RabbitMQ (publish bridge.<acct>.reply.<thread
|
|
107
|
+
└─► RabbitMQ (publish bridge.<acct>.reply.<thread>,
|
|
108
|
+
payload carries agent + agentId)
|
|
85
109
|
◄── /api/bridge/stream (SSE, Bearer auth)
|
|
86
|
-
└─► daemon
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/api/message ◄──── bridge_post (in-process MCP tool
|
|
110
|
+
└─► daemon (dispatches per payload.agent)
|
|
111
|
+
├─► Claude Agent SDK → ~/.claude/projects/...
|
|
112
|
+
├─► @openai/codex-sdk → ~/.codex/sessions/...
|
|
113
|
+
└─► @opencode-ai/sdk → opencode server
|
|
114
|
+
/api/message ◄──── bridge_post (in-process MCP tool, Claude only)
|
|
91
115
|
[poll: /api/messages] ◄──── (4s loop)
|
|
92
116
|
```
|
|
93
117
|
|
|
94
118
|
Key bits:
|
|
95
119
|
|
|
96
120
|
- The daemon **never** holds RabbitMQ credentials; broker access stays server-side. Per-account routing key isolation enforced by `BridgeAuth`.
|
|
97
|
-
-
|
|
98
|
-
- Each bridge **thread** binds to its own
|
|
121
|
+
- For the **claude** driver, the spawned Claude session uses an **in-process MCP server** (`mcp__bridge__bridge_post`) — not the platform's hosted MCP, so the daemon's tools don't depend on a long-lived OBTO MCP proxy session. For **codex** and **opencode**, the SDKs can't auto-approve a write tool when run unattended, so the daemon captures the final response and posts it to the thread on the agent's behalf.
|
|
122
|
+
- Each bridge **thread** binds to its own session ID **per agent**. Subsequent messages on the same thread + same agent resume the same engine-specific session, so the agent keeps full context. Switching the thread's agent in the UI starts (or resumes) the other engine's session — each side's state stays intact. Your interactive sessions are unaffected — they live in separate session stores.
|
|
99
123
|
- Per-thread serialization means rapid bursts on the same thread are handled in order, never racing the same session.
|
|
100
|
-
-
|
|
124
|
+
- Multi-daemon races are killed by atomic first-touch claim against the thread record on the bridge.
|
|
101
125
|
|
|
102
126
|
## Agent costs
|
|
103
127
|
|
|
104
|
-
The daemon runs your chosen agent on your machine with **your** credentials — Anthropic for `claude` (whatever Claude Code uses: `ANTHROPIC_API_KEY` or your Claude.ai session)
|
|
128
|
+
The daemon runs your chosen agent on your machine with **your** credentials — Anthropic for `claude` (whatever Claude Code uses: `ANTHROPIC_API_KEY` or your Claude.ai session); your OpenAI/ChatGPT account for `codex`; whichever provider you've configured `opencode` to call (Anthropic by default for this daemon). Every bridge-driven turn is a normal API call billed to you. We don't proxy.
|
|
105
129
|
|
|
106
130
|
## Data handling
|
|
107
131
|
|
|
108
|
-
**Your model traffic never touches us.** The daemon runs on your machine and calls Anthropic or
|
|
132
|
+
**Your model traffic never touches us.** The daemon runs on your machine and calls Anthropic, OpenAI, or whichever provider opencode is configured for, with *your own* credentials. Your prompts, your code, and the model's responses pass directly between your machine and the model provider, under your own API account and its terms. OBTO does not proxy, route, or see that traffic.
|
|
109
133
|
|
|
110
134
|
**What the bridge stores.** For threads to work, the messages you and the agent post are saved in OBTO's database — that's what makes a thread durable and readable from your phone. Threads are strictly scoped to your account; one tenant can never see another's. Your daemon's API token is stored server-side only as a SHA-256 hash; the plaintext token never leaves your local config file.
|
|
111
135
|
|
package/bin/obto-bridge.js
CHANGED
|
@@ -14,7 +14,8 @@ const usage = () => {
|
|
|
14
14
|
console.error('Usage: obto-bridge <command>');
|
|
15
15
|
console.error('');
|
|
16
16
|
console.error('Commands:');
|
|
17
|
-
console.error(' init
|
|
17
|
+
console.error(' init Create a free account (or paste an existing token via --token/--account)');
|
|
18
|
+
console.error(' and write ~/.obto-bridge/config.json.');
|
|
18
19
|
console.error(' start Run the daemon (foreground).');
|
|
19
20
|
console.error(' status Print active thread/session bindings.');
|
|
20
21
|
console.error(' whoami Verify config and show your account info from the server.');
|
|
@@ -22,10 +23,10 @@ const usage = () => {
|
|
|
22
23
|
console.error(' logout Wipe local credentials at ~/.obto-bridge/config.json.');
|
|
23
24
|
console.error('');
|
|
24
25
|
console.error('Flags:');
|
|
25
|
-
console.error(' --version, -v
|
|
26
|
-
console.error(' --help, -h
|
|
27
|
-
console.error('');
|
|
28
|
-
console.error('
|
|
26
|
+
console.error(' --version, -v Print the installed package version.');
|
|
27
|
+
console.error(' --help, -h Show this help.');
|
|
28
|
+
console.error(' --token <obto_…> (init only) Skip self-serve register, use this token.');
|
|
29
|
+
console.error(' --account <acc_…> (init only) Pair with --token for paste-in mode.');
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
const cmd = process.argv[2];
|
package/cli/init.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// `obto-bridge init` — interactive setup wizard.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// `obto-bridge init` — interactive setup wizard.
|
|
4
|
+
//
|
|
5
|
+
// Default (>=0.1.0-beta.6): self-serve registration. Prompts for email,
|
|
6
|
+
// username, password and POSTs to /api/bridge/register, which returns a
|
|
7
|
+
// freshly-minted free-tier account + API token. No invite, no waiting.
|
|
8
|
+
//
|
|
9
|
+
// Legacy paste-in (for users with an existing token, e.g. from before
|
|
10
|
+
// self-serve, or migrating across machines): `obto-bridge init --token <obto_…>
|
|
11
|
+
// --account <acc_…>` skips the register step and uses the supplied creds.
|
|
12
|
+
//
|
|
13
|
+
// Either way the wizard writes ~/.obto-bridge/config.json with mode 0600 and
|
|
14
|
+
// then validates the token against the server via GET /api/bridge/whoami.
|
|
7
15
|
|
|
8
16
|
const fs = require('fs');
|
|
9
17
|
const path = require('path');
|
|
@@ -19,6 +27,15 @@ const DEFAULTS = {
|
|
|
19
27
|
relayPermissions: true,
|
|
20
28
|
};
|
|
21
29
|
|
|
30
|
+
// argv after `obto-bridge init` — for --token / --account paste-in mode.
|
|
31
|
+
const argv = process.argv.slice(3);
|
|
32
|
+
const flagValue = (name) => {
|
|
33
|
+
const i = argv.indexOf(name);
|
|
34
|
+
return i !== -1 && argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : null;
|
|
35
|
+
};
|
|
36
|
+
const cliToken = flagValue('--token');
|
|
37
|
+
const cliAccount = flagValue('--account');
|
|
38
|
+
|
|
22
39
|
const loadExisting = () => {
|
|
23
40
|
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); }
|
|
24
41
|
catch (_) { return {}; }
|
|
@@ -35,6 +52,32 @@ const ask = (rl, prompt, def) =>
|
|
|
35
52
|
});
|
|
36
53
|
});
|
|
37
54
|
|
|
55
|
+
// Password echoes (readline + raw mode is gnarly to do portably). Users doing
|
|
56
|
+
// scripted/headless deploys should set BRIDGE_API_TOKEN env directly instead.
|
|
57
|
+
const askPassword = (rl, prompt) =>
|
|
58
|
+
new Promise((resolve) => {
|
|
59
|
+
rl.question(prompt + ': ', (answer) => resolve(answer));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const registerSelfServe = async ({ baseUrl, originHost, email, username, password }) => {
|
|
63
|
+
const url = baseUrl.replace(/\/$/, '') + '/api/bridge/register';
|
|
64
|
+
const res = await fetch(url, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
Accept: 'application/json',
|
|
68
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
69
|
+
'OBTO-ORIGIN-HOST': originHost,
|
|
70
|
+
},
|
|
71
|
+
body: new URLSearchParams({ email, username, password }).toString(),
|
|
72
|
+
cache: 'no-store',
|
|
73
|
+
redirect: 'manual',
|
|
74
|
+
});
|
|
75
|
+
const text = await res.text();
|
|
76
|
+
let data;
|
|
77
|
+
try { data = JSON.parse(text); } catch (_) { data = { _rawBody: text }; }
|
|
78
|
+
return { status: res.status, ok: res.ok, data };
|
|
79
|
+
};
|
|
80
|
+
|
|
38
81
|
const validateAgainstServer = async (cfg) => {
|
|
39
82
|
const url = cfg.baseUrl.replace(/\/$/, '') + '/api/bridge/whoami';
|
|
40
83
|
const res = await fetch(url, {
|
|
@@ -55,25 +98,74 @@ const validateAgainstServer = async (cfg) => {
|
|
|
55
98
|
const main = async () => {
|
|
56
99
|
console.log('OBTO Agent Bridge — setup');
|
|
57
100
|
console.log('-------------------------');
|
|
58
|
-
console.log('Need credentials? Email support@obto.co for an invite.');
|
|
59
101
|
console.log('Config will be written to: ' + CONFIG_PATH);
|
|
60
102
|
console.log('');
|
|
61
103
|
|
|
62
104
|
const existing = loadExisting();
|
|
63
105
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
64
106
|
|
|
65
|
-
// Server base URL and OBTO-ORIGIN-HOST are constants of the platform, not
|
|
66
|
-
// per-user config — every daemon talks to the same bridge app. They are no
|
|
67
|
-
// longer prompted. Advanced / self-hosted users can still override them via
|
|
68
|
-
// the BRIDGE_BASE_URL / BRIDGE_ORIGIN_HOST env vars or by editing config.json.
|
|
69
107
|
const baseUrl = process.env.BRIDGE_BASE_URL || existing.baseUrl || DEFAULTS.baseUrl;
|
|
70
108
|
const originHost = process.env.BRIDGE_ORIGIN_HOST || existing.originHost || DEFAULTS.originHost;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
109
|
+
|
|
110
|
+
let accountId = existing.accountId || '';
|
|
111
|
+
let apiToken = existing.apiToken || '';
|
|
112
|
+
|
|
113
|
+
// Pick the credential path. Order of precedence:
|
|
114
|
+
// 1. --token + --account flags (machine-paste, scripted).
|
|
115
|
+
// 2. Existing config from a prior init (carry over).
|
|
116
|
+
// 3. Self-serve registration via /api/bridge/register (default for new users).
|
|
117
|
+
const haveCliPaste = !!(cliToken && cliAccount);
|
|
118
|
+
const haveExisting = !!(existing.accountId && existing.apiToken);
|
|
119
|
+
|
|
120
|
+
if (haveCliPaste) {
|
|
121
|
+
apiToken = cliToken;
|
|
122
|
+
accountId = cliAccount;
|
|
123
|
+
console.log('Using credentials from --token / --account flags.');
|
|
124
|
+
console.log('');
|
|
125
|
+
} else if (haveExisting) {
|
|
126
|
+
console.log('Existing config found:');
|
|
127
|
+
console.log(' Account: ' + existing.accountId);
|
|
128
|
+
console.log(' Token: ' + (existing.apiToken || '').slice(0, 10) + '…');
|
|
129
|
+
console.log('Reusing those credentials. To re-register from scratch, remove ' + CONFIG_PATH + ' first.');
|
|
130
|
+
console.log('');
|
|
131
|
+
} else {
|
|
132
|
+
console.log('Create a free account (no card needed):');
|
|
133
|
+
const email = await ask(rl, ' Email', '');
|
|
134
|
+
const username = await ask(rl, ' Username (3-40 chars: a-z, 0-9, _ or -)', '');
|
|
135
|
+
const password = await askPassword(rl, ' Password (min 8 chars)');
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log('Creating account at ' + baseUrl + ' ...');
|
|
138
|
+
let r;
|
|
139
|
+
try {
|
|
140
|
+
r = await registerSelfServe({ baseUrl, originHost, email, username, password });
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.error('error: registration request failed: ' + (e && e.message ? e.message : e));
|
|
143
|
+
console.error(' (network problem? you can also paste an existing token via:');
|
|
144
|
+
console.error(' `obto-bridge init --token <obto_…> --account <acc_…>`)');
|
|
145
|
+
rl.close();
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
if (!r.ok || !r.data || !r.data.ok) {
|
|
149
|
+
const msg = (r.data && (r.data.error || r.data._rawBody)) || ('HTTP ' + r.status);
|
|
150
|
+
console.error('error: registration rejected: ' + msg);
|
|
151
|
+
rl.close();
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
accountId = r.data.accountId;
|
|
155
|
+
apiToken = r.data.apiToken;
|
|
156
|
+
console.log(' ✓ Free account created.');
|
|
157
|
+
console.log(' Account: ' + accountId);
|
|
158
|
+
console.log(' User: @' + (r.data.basicAuthUser || username));
|
|
159
|
+
console.log(' Plan: ' + (r.data.plan || 'free'));
|
|
160
|
+
console.log(' Sign in at ' + baseUrl + ' as @' + (r.data.basicAuthUser || username) + ' with the password you just set.');
|
|
161
|
+
console.log('');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const agentId = await ask(rl, 'Agent name (e.g. my-mac)', existing.agentId || os.hostname().split('.')[0] || 'unnamed-agent');
|
|
74
165
|
const projectDir = await ask(rl, 'Project working dir', existing.projectDir || process.cwd());
|
|
75
|
-
const agentAns = await ask(rl, 'Coding agent — claude
|
|
76
|
-
const
|
|
166
|
+
const agentAns = await ask(rl, 'Coding agent fallback — claude / codex / opencode', existing.agent || 'claude');
|
|
167
|
+
const agentLow = String(agentAns).trim().toLowerCase();
|
|
168
|
+
const agent = ['claude', 'codex', 'opencode'].indexOf(agentLow) !== -1 ? agentLow : 'claude';
|
|
77
169
|
const relayAns = await ask(rl, 'Relay permission requests via bridge? (y/n)', existing.relayPermissions !== false ? 'y' : 'n');
|
|
78
170
|
const relayPermissions = String(relayAns).toLowerCase().startsWith('y');
|
|
79
171
|
|
|
@@ -138,7 +230,7 @@ const main = async () => {
|
|
|
138
230
|
}
|
|
139
231
|
|
|
140
232
|
if (result.status === 401) {
|
|
141
|
-
console.error(' ✗ Server rejected the API token (HTTP 401).
|
|
233
|
+
console.error(' ✗ Server rejected the API token (HTTP 401). Re-run `obto-bridge init` to reset.');
|
|
142
234
|
process.exit(2);
|
|
143
235
|
}
|
|
144
236
|
if (result.status === 403) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obtoai/agent-bridge",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.6",
|
|
4
4
|
"description": "Local consumer for the OBTO Agent Bridge. Receives bridge events over SSE and drives a coding agent (Claude Code or OpenAI Codex) on your machine.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "OBTO Inc.",
|