@obtoai/agent-bridge 0.1.0-beta.5 → 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 CHANGED
@@ -4,9 +4,17 @@ A local daemon that lets coding agents — [Claude Code](https://claude.ai/code)
4
4
 
5
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
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
+ ```
14
+
7
15
  ## Status
8
16
 
9
- **Closed beta.** Need an invite? Email **support@obto.co** with your name and a short note about what you'd use it for. We'll provision an account and mail you credentials.
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
 
@@ -15,7 +23,6 @@ You post a message on a thread from your phone or laptop. The daemon (running on
15
23
  - **Claude** — Claude Code / the Claude Agent SDK, billed to your Anthropic account.
16
24
  - **Codex** — the `codex` CLI (`npm i -g @openai/codex`), signed in to your OpenAI/ChatGPT account.
17
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
- - An invite from `support@obto.co` (gives you an `accountId`, browser username/password, and an API token).
19
26
 
20
27
  ## Install
21
28
 
@@ -35,9 +42,15 @@ npx @obtoai/agent-bridge <command>
35
42
  obto-bridge init
36
43
  ```
37
44
 
38
- Walks you through a few questions: your account ID, API token, an agent name (to distinguish multiple machines on one account), a *fallback* agent (`claude`, `codex`, or `opencode` — used only for legacy events without an explicit agent), the project directory to work in, and whether to relay tool-permission requests via the bridge. (The server URL is a built-in default; advanced / self-hosted users can override it with the `BRIDGE_BASE_URL` env var.)
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.
46
+
47
+ Already have a token from an earlier invite or another machine? Skip the registration step:
48
+
49
+ ```bash
50
+ obto-bridge init --token obto_xxxxxxxx --account acc_xxxxxxxx
51
+ ```
39
52
 
40
- Config lands at `~/.obto-bridge/config.json` (mode 0600). Safe to commit your account ID; **never commit the `apiToken`**.
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.)
41
54
 
42
55
  ### Agents (claude / codex / opencode)
43
56
 
@@ -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 One-time setup: paste credentials, choose agent name + project dir.');
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 Print the installed package version.');
26
- console.error(' --help, -h Show this help.');
27
- console.error('');
28
- console.error('Get an invite: support@obto.co');
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. Prompts for credentials,
4
- // writes ~/.obto-bridge/config.json with mode 0600, then validates the token
5
- // against the server via GET /api/bridge/whoami so the user knows immediately
6
- // if anything is wrong.
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
- const accountId = await ask(rl, 'Account ID (acc_…)', existing.accountId || '');
72
- const apiToken = await ask(rl, 'API token (obto_…)', existing.apiToken || '');
73
- const agentId = await ask(rl, 'Agent name (e.g. my-mac)', existing.agentId || os.hostname().split('.')[0] || 'unnamed-agent');
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 or codex', existing.agent || 'claude');
76
- const agent = String(agentAns).trim().toLowerCase() === 'codex' ? 'codex' : 'claude';
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). Double-check that you pasted the full token from your invite email.');
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.5",
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.",