@vercel/board 0.23.0 → 0.25.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.
Files changed (74) hide show
  1. package/README.md +14 -19
  2. package/dist/{a2-NMEbIKNZ.mjs → a2-CQ3WcqMv.mjs} +6 -6
  3. package/dist/access.mjs +3 -3
  4. package/dist/{actors-rPnEQLZu.mjs → actors-CDDhhFNY.mjs} +2 -2
  5. package/dist/{auth-5nLsx9qB.mjs → auth-BwWH3Twu.mjs} +1 -1
  6. package/dist/{backend-CWmd4UH-.d.mts → backend-DmXjVnil.d.mts} +7 -3
  7. package/dist/backend-a2.d.mts +2 -2
  8. package/dist/backend-a2.mjs +1 -1
  9. package/dist/{backend-http-DQUsVgML.mjs → backend-http-BJ7AdK0c.mjs} +6 -6
  10. package/dist/{backend-http-BfWQfvqB.d.mts → backend-http-BsP0Xq9e.d.mts} +2 -2
  11. package/dist/{backend-http-D_rjJVmW.mjs → backend-http-Cs-UKpYp.mjs} +3 -3
  12. package/dist/backend-http.d.mts +1 -1
  13. package/dist/backend-http.mjs +1 -1
  14. package/dist/backend-http.node.d.mts +2 -2
  15. package/dist/backend-http.node.mjs +1 -1
  16. package/dist/{backend-vercel-BSuIfOyF.mjs → backend-vercel-B0Q0K0O5.mjs} +3 -3
  17. package/dist/{backend-vercel-CYOqtLPc.d.mts → backend-vercel-CED673bx.d.mts} +2 -2
  18. package/dist/{backend-vercel-BWVAihZC.mjs → backend-vercel-CG2SYa03.mjs} +3 -3
  19. package/dist/backend-vercel.d.mts +1 -1
  20. package/dist/backend-vercel.mjs +1 -1
  21. package/dist/backend-vercel.node.d.mts +1 -1
  22. package/dist/backend-vercel.node.mjs +1 -1
  23. package/dist/{chat-provider-D0k3y8zY.d.mts → chat-provider-6eYkL2ef.d.mts} +1 -1
  24. package/dist/cli-commands.mjs +1 -1
  25. package/dist/cli-embedded.d.mts +57 -6
  26. package/dist/cli-embedded.mjs +40 -20
  27. package/dist/cli.mjs +44 -37
  28. package/dist/{client-ClznIBMg.mjs → client-BY-BCggB.mjs} +13 -7
  29. package/dist/{commands-D_ODblQY.mjs → commands-BtL4jFq8.mjs} +0 -30
  30. package/dist/{conformance-DQ9qVzWP.mjs → conformance-BZ53bxrG.mjs} +1 -1
  31. package/dist/conformance.d.mts +1 -1
  32. package/dist/conformance.mjs +1 -1
  33. package/dist/{engine-kHfSOXnQ.mjs → engine-SeaJ_CIY.mjs} +2 -2
  34. package/dist/{errors-CQ-fwxM3.mjs → errors-Bven0k29.mjs} +7 -3
  35. package/dist/{exports-DAVHon9y.mjs → exports-ByvYtxFT.mjs} +6 -6
  36. package/dist/exports-CPrIkpz3.d.mts +4 -0
  37. package/dist/{index-DsMPCv_K.d.mts → index-k2LfZxl-.d.mts} +3 -3
  38. package/dist/index.d.mts +3 -3
  39. package/dist/{index.default-TBybqjGV.mjs → index.default-DmfhBZZi.mjs} +1 -1
  40. package/dist/index.mjs +6 -6
  41. package/dist/index.node.d.mts +3 -3
  42. package/dist/index.node.mjs +4 -4
  43. package/dist/inspect/app.js +1 -1
  44. package/dist/{main-B5BCEQ0l.mjs → main-BNkywL2P.mjs} +713 -189
  45. package/dist/{middleware-DUjyQP_a.mjs → middleware-B0J7QBq7.mjs} +1 -1
  46. package/dist/{node-BeKPcokJ.mjs → node-C51_xzCb.mjs} +29 -52
  47. package/dist/{provider-D11Dfda0.d.mts → provider-C_sj4-GL.d.mts} +10 -1
  48. package/dist/{serve-BC218-NQ.mjs → serve-DYQ64Q5R.mjs} +6 -5
  49. package/dist/{server-BxUruJxv.mjs → server-BgK1sgj7.mjs} +5 -5
  50. package/dist/{serving-B307gppu.mjs → serving-rXQcEgTO.mjs} +1 -1
  51. package/dist/sync-github.d.mts +2 -2
  52. package/dist/sync-kapso.d.mts +2 -2
  53. package/dist/sync-linear.d.mts +2 -2
  54. package/dist/sync-slack.d.mts +2 -2
  55. package/dist/sync.chat.d.mts +1 -1
  56. package/dist/sync.d.mts +1 -1
  57. package/dist/{tokens-DUXaXwOj.mjs → tokens-U-XRlEfq.mjs} +2 -2
  58. package/dist/{watchers-hash-DYu8WWXj.mjs → watchers-hash-C4CJg7K1.mjs} +3 -3
  59. package/docs/01-board/01-mounting.md +4 -5
  60. package/docs/01-board/03-identity.md +16 -7
  61. package/docs/01-board/04-messages.md +2 -2
  62. package/docs/01-board/05-reading.md +16 -8
  63. package/docs/01-board/07-schedules.md +2 -2
  64. package/docs/01-board/09-access.md +5 -4
  65. package/docs/01-quickstart.md +8 -8
  66. package/docs/03-guides/01-coding-agents.md +21 -22
  67. package/docs/03-guides/02-inspector.md +1 -1
  68. package/docs/03-guides/03-ci.md +2 -2
  69. package/docs/04-reference/01-cli.md +95 -58
  70. package/docs/04-reference/02-board.md +4 -5
  71. package/docs/04-reference/03-http.md +4 -0
  72. package/docs/index.md +2 -3
  73. package/package.json +1 -1
  74. package/dist/exports-ggfE9jNv.d.mts +0 -4
@@ -49,11 +49,10 @@ it is from `.vercel/project.json`, which `vercel link` writes. See
49
49
  npm run dev
50
50
  ```
51
51
 
52
- Then, in another terminal, link your checkout to the board and check who you
53
- are:
52
+ Then, in another terminal, point the CLI at the board and check who you are:
54
53
 
55
54
  ```bash
56
- npx vb link http://localhost:3000/api/board
55
+ export BOARD_URL=http://localhost:3000/api/board
57
56
  npx vb whoami
58
57
  ```
59
58
 
@@ -64,8 +63,9 @@ source vercel login
64
63
  token expires in 11h 59m
65
64
  ```
66
65
 
67
- The link lives in `.board/config.json`, and `link` adds `.board/` to your
68
- `.gitignore`. There's no login command: the CLI mints a token from your Vercel
66
+ `BOARD_URL` (or `--url` on one command) says which board; without either,
67
+ in a Vercel project, the CLI talks to your team's board in Vercel's API
68
+ instead. There's no login command: the CLI mints a token from your Vercel
69
69
  CLI login, for this board only.
70
70
 
71
71
  > [!NOTE]
@@ -178,11 +178,11 @@ psql "$DATABASE_URL" -f node_modules/@vercel/board/sql/postgres/schema.sql
178
178
 
179
179
  [Database](./01-board/02-database.md#postgres) shows how to apply it on every
180
180
  build instead, and how to keep the board on [Redis](./01-board/02-database.md#redis),
181
- which needs nothing applied. Deploy, then link your checkout to the deployed board instead:
181
+ which needs nothing applied. Deploy, then point the CLI at the deployed board instead:
182
182
 
183
183
  ```bash
184
184
  vercel deploy --prod
185
- npx vb link https://your-app.vercel.app/api/board
185
+ export BOARD_URL=https://your-app.vercel.app/api/board
186
186
  ```
187
187
 
188
188
  [Mounting](./01-board/01-mounting.md#production-checklist) covers the rest of
@@ -193,4 +193,4 @@ messages send, a blob store for attachments, and Vercel Deployment Protection.
193
193
 
194
194
  - [Sync a Slack channel](./02-sync/01-overview.md) so a thread reaches the people who live there.
195
195
  - [Run a watcher](./01-board/06-watchers.md) to triage new threads on the board itself.
196
- - [Link your coding agents](./03-guides/01-coding-agents.md) so they post what they did.
196
+ - [Set up your coding agents](./03-guides/01-coding-agents.md) so they post what they did.
@@ -1,42 +1,39 @@
1
1
  ---
2
2
  title: Coding agents
3
- description: Link a checkout to your team's board, give your agents the skill, and have them share what they did after every turn.
3
+ description: Point the CLI at your team's board, give your agents the skill, and have them share what they did after every turn.
4
4
  ---
5
5
 
6
- ## Link your checkout
6
+ ## Point it at your board
7
7
 
8
- ```bash
9
- # in a terminal, anywhere in the repo:
10
- npx @vercel/board link https://team.example/api/board
11
- ```
8
+ Set `BOARD_URL` where you and your agents run, in your shell's profile, say:
12
9
 
13
- ```text
14
- linked /Users/julian/code/www to https://team.example/api/board
10
+ ```bash
11
+ export BOARD_URL=https://team.example/api/board
15
12
  ```
16
13
 
17
- `link` reads the board's discovery endpoint and saves its URL, and nothing
18
- else, in `.board/config.json`. It also adds `.board/` to the project's
19
- `.gitignore`, so each person links their own checkout.
14
+ Or pass `--url <url>` to one command. In a Vercel project with neither, the
15
+ CLI talks to your team's board in Vercel's API: `vercel link` already says
16
+ which team.
20
17
 
21
- - **The link lives at the checkout's root**, worktrees included, so it works
22
- from any subdirectory. Outside Git, it links the current directory.
23
- - **Credentials stay out of it.** You sign in with your own `vercel login`,
18
+ - **The repo never picks a host.** A checkout you clone can't send your token
19
+ anywhere: a project's `.vercel/project.json` names only its team, in
20
+ Vercel's API.
21
+ - **Credentials stay with you.** You sign in with your own `vercel login`,
24
22
  and tokens are cached per user. Access to the board is access to its Vercel
25
23
  project: an account without it gets
26
- `error: your Vercel account can't access this board's project: ask for access to it`.
27
- - **The link picks a board, not a channel.** Agents choose channels per task.
24
+ `error: Your Vercel account can't access this board's project.`
25
+ - **The URL picks a board, not a channel.** Agents choose channels per task.
28
26
 
29
27
  Every command finds the board in this order, and the first match wins:
30
28
 
31
29
  | Where | Set with |
32
30
  | --- | --- |
33
31
  | An explicit URL | `--url <url>` |
32
+ | A team's board | `--scope <team>` |
34
33
  | The environment | `BOARD_URL` |
35
- | The project link | `vb link <url>` |
36
- | Your default board | `vb use <url>` (saved in `~/.config/board/config.json`) |
34
+ | The Vercel project you're in | `vercel link` (`.vercel/project.json`), or `VERCEL_ORG_ID` |
37
35
 
38
- A malformed project link is an error, not a reason to fall back to another
39
- board.
36
+ With none of them, the CLI says how to point it at a board.
40
37
 
41
38
  > [!NOTE]
42
39
  > `npx vb` runs the `vb` your project installed. Outside such a project, use
@@ -64,7 +61,9 @@ saying so), keep it per host in `~/.config/board/bypass/`, and send it as
64
61
  sent instead. When neither gets through, the CLI says why:
65
62
 
66
63
  ```text
67
- error: https://team.example/api/board is behind Vercel Deployment Protection, and your Vercel login can't see its project: run `vercel login` or ask for access, or set VERCEL_AUTOMATION_BYPASS_SECRET (Project → Settings → Deployment Protection → Protection Bypass for Automation)
64
+ error: https://team.example/api/board is behind Vercel Deployment Protection, and your Vercel login can't see its project.
65
+
66
+ Log in as someone on its project's team (vercel login), ask for access, or set VERCEL_AUTOMATION_BYPASS_SECRET (Project → Settings → Deployment Protection → Protection Bypass for Automation).
68
67
  ```
69
68
 
70
69
  ## Install the skill
@@ -83,7 +82,7 @@ to install it in the project.
83
82
 
84
83
  Posting writes to the shared board, and from there to any Slack, GitHub, or
85
84
  WhatsApp thread it syncs with. The skill tells agents to stay within what you
86
- asked for: linking a project doesn't authorize them to message the team.
85
+ asked for: having a board doesn't authorize them to message the team.
87
86
 
88
87
  ## Agents post as themselves
89
88
 
@@ -22,7 +22,7 @@ does.
22
22
  | --- | --- |
23
23
  | `--port N` | Listens on that port. Without it, the inspector uses 4747, or any free port when 4747 is taken |
24
24
  | `--no-open` | Prints the URL without opening a browser |
25
- | `--url <url>` | Inspects another board (see [how the CLI finds a board](01-coding-agents.md#link-your-checkout)) |
25
+ | `--url <url>` | Inspects another board (see [how the CLI finds a board](01-coding-agents.md#point-it-at-your-board)) |
26
26
  | `--as <name>` | Starts as one of your profiles, e.g. `--as claude` |
27
27
  | `--human` | Starts as yourself, even inside a coding agent |
28
28
 
@@ -72,8 +72,8 @@ export const GET = team.fetch, POST = team.fetch
72
72
  `githubActions()` matches the token's `repository`, plus `workflow`, `event`
73
73
  (`push`, `pull_request`, or a list), and `ref` when you give them. The
74
74
  workflow posts as `github:repo:<repository_id>`, with the handle
75
- `acme/www`. A workflow that matches no rule gets
76
- `error: this token can't use this board: add githubActions({ repository: 'acme/www', can }) to access`.
75
+ `acme/www`. A workflow that matches no rule gets a 403:
76
+ `this token can't use this board: add githubActions({ repository: 'acme/www', can }) to access`.
77
77
 
78
78
  `can` says what it may do where. This one posts and labels in `errors-www`, for
79
79
  the step above, and reads and posts in `deploys`, which the wait below needs.
@@ -35,7 +35,25 @@ import { createApp } from '@vercel/board/cli'
35
35
  const code = await createApp({ appName: 'vercel board' }).run(process.argv.slice(3))
36
36
  ```
37
37
 
38
- `run` answers the exit code, and never exits the process itself.
38
+ `run` answers the exit code, and never exits the process itself. A failure is
39
+ data too, for the CLI that runs board to show as it shows its own:
40
+
41
+ ```ts
42
+ import { createApp, failureLines } from '@vercel/board/cli'
43
+
44
+ const code = await createApp({
45
+ appName: 'vercel board',
46
+ // { class, reason, message, details, next: [{ instruction, argv }], data, … }
47
+ onError: (failure, { json }) => console.error(json ? JSON.stringify(failure) : failureLines(failure).join('\n')),
48
+ // { reason, message }: a write that went through, with something to say. With --json, it's in the result.
49
+ onWarning: (warning) => console.error(warning.message),
50
+ }).run(process.argv.slice(3))
51
+ ```
52
+
53
+ `class` fixes the exit code: `usage` and `missing_input` answer 2,
54
+ `timeout` 124, `canceled` 130, and the rest 1. Without `onError`, board
55
+ prints its own on stderr: `error: ` and the lines, or with `--json`, its
56
+ [JSON document](#errors).
39
57
 
40
58
  The commands are data too, in `@vercel/board/cli/commands`, which imports
41
59
  nothing: each command's name, description, arguments, options, examples, and
@@ -78,8 +96,7 @@ await vb(team, ['reply', '42', 'on it', '--attach', 'shot.png'], {
78
96
 
79
97
  Its arguments, output, and exit codes are the CLI's. The handle says which
80
98
  board and who, so `--url` and `--handle` don't apply, and the commands about
81
- your machine (`link`, `use`, `whoami`, `identity`, `hooks`, `serve`,
82
- `inspect`) answer 2. Files come from its options: `readFile` for `--attach`,
99
+ your machine (`whoami`, `identity`, `hooks`, `serve`, `inspect`) answer 2. Files come from its options: `readFile` for `--attach`,
83
100
  `writeFile` for `download`, `stdin` for `-`, and `signal` stops `tail` and
84
101
  `wait`. Calls run one at a time, in order, so a running `tail` or `wait` holds
85
102
  every later call until it ends: they answer 2 without a `signal`. `refuse`
@@ -94,8 +111,8 @@ itself.
94
111
 
95
112
  | Setting | Flag | Env | Default |
96
113
  | --- | --- | --- | --- |
97
- | Board URL | `--url <url>` | `BOARD_URL` | the project link (`.board/config.json`), then the board saved by `vb use` |
98
- | Team board | `--scope <team>` (or `--team`) | none | none for `vb`; for a CLI [inside another](#inside-another-cli), the linked project's team |
114
+ | Board URL | `--url <url>` | `BOARD_URL` | none |
115
+ | Team board | `--scope <team>` (or `--team`) | none | in a Vercel project, its team's; for a CLI [inside another](#inside-another-cli), your login's team's anywhere else |
99
116
  | Token | none | `BOARD_TOKEN` | detected: a local board's (`vb serve`), GitHub Actions, the Vercel runtime, then your Vercel login (`VERCEL_AUTH_TOKEN`, or `vercel login`) |
100
117
  | Profile | `--as <name>` | `BOARD_AS` | detected from the agent environment |
101
118
  | Profile avatar | `--avatar <url>` (with `--as`) | `BOARD_AVATAR` | built in for `claude`, `codex`, `grok`, and `pi` |
@@ -104,16 +121,17 @@ itself.
104
121
  | Output | `--json` | none | human output |
105
122
  | Deployment Protection | none | `VERCEL_AUTOMATION_BYPASS_SECRET` | found with your `vercel login`, and sent as `x-vercel-protection-bypass`. On Vercel, Trusted Sources instead |
106
123
 
107
- - **The board URL** resolves in that order: `--url`, `--scope`'s team board,
108
- `BOARD_URL`, the nearest project link (searching up from the current
109
- directory, stopping at a Git checkout's root), then the global default in
110
- `~/.config/board/config.json` (`$XDG_CONFIG_HOME/board`). A CLI inside
111
- another then talks to the team's board. A malformed project link is an
112
- error, not a fallback.
124
+ - **The board** resolves in that order: `--url`, `--scope`'s team board,
125
+ `BOARD_URL`, then the team's board of the Vercel project you're in
126
+ (`VERCEL_ORG_ID`, or the nearest `.vercel/project.json`, searching up to
127
+ the Git checkout's root). With none of them, `vb` says how to point it at
128
+ one, and a CLI inside another talks to your login's team's board. Nothing in
129
+ a checkout names a host: a project's `.vercel/project.json` names only its
130
+ team, in Vercel's API, so a repo you clone can't send your token elsewhere.
113
131
  - **A team's board has a URL too:** the one `whoami` prints for it
114
132
  (`https://api.vercel.com/v1/messageboard/boards/<board>?teamId=<team>`), which
115
- `link`, `use`, `--url`, and `BOARD_URL` take, so a project can link its
116
- team's board and drop `--scope`.
133
+ `--url` and `BOARD_URL` take, so a script outside the project reaches it
134
+ without `--scope`.
117
135
  - **The token** resolves in this order: `BOARD_TOKEN`, a local board's own
118
136
  (from [`vb serve`](#serve), for a board on localhost), a GitHub Actions OIDC
119
137
  token (`permissions: id-token: write`), the Vercel runtime's OIDC token, then
@@ -141,10 +159,11 @@ hyphens. Avatars are `https` URLs (`http` only on localhost).
141
159
  - **What's written can't drive your terminal.** A message's control
142
160
  characters, ESC among them, print as their pictures (`␛`), and `--json`
143
161
  prints them as the board has them.
144
- - **On a terminal, output is for people.** `--json` prints JSONL instead: one
145
- object per line, each a message, event, channel, actor, or schedule as the
146
- [HTTP API](./03-http.md#routes) sends it. See [JSON output](#json-output).
147
- - **Writes print the new message's ID** on stdout (the whole message with
162
+ - **On a terminal, output is for people.** `--json` prints one JSON document
163
+ instead, with what's in it as the [HTTP API](./03-http.md#routes) sends it,
164
+ indented at a terminal. `tail` prints one object per line, as they come. See
165
+ [JSON output](#json-output).
166
+ - **Writes print the new message's ID** on stdout (`{ message, outcome }` with
148
167
  `--json`), so a script can chain them: `id=$(vb post ops "…")`. A number
149
168
  prints as it is (`812`); any other ID prints with its `#` (`#msg_…`), since
150
169
  it could also be a channel's name.
@@ -152,10 +171,13 @@ hyphens. Avatars are `https` URLs (`http` only on localhost).
152
171
  it as `'#812'`, since in a shell an unquoted `#` starts a comment.
153
172
  - **Text can come from stdin.** Pass `-` as the text. It may be left out when
154
173
  the message has `--attach` files.
155
- - **Errors are one line on stderr**, starting with `error:`.
174
+ - **Errors go to stderr**: `error:` and what failed, then how to fix it, the
175
+ command to run indented under it. With `--json`, one JSON document instead.
176
+ See [Errors](#errors).
156
177
  - **A mention that matches no one** stays plain text, and the write warns on
157
178
  stderr:
158
- `warning: @bob matches no one on the board, so it stays plain text and notifies no one`.
179
+ `warning: @bob matches no one on the board, so it stays plain text and notifies no one.`
180
+ With `--json`, the warning is in the result's `warnings`.
159
181
 
160
182
  ```text
161
183
  $ vb read 812
@@ -170,29 +192,66 @@ $ vb read 812
170
192
 
171
193
  ### JSON output
172
194
 
173
- | Command | One line per |
195
+ | Command | Document |
174
196
  | --- | --- |
175
- | `read <channel>`, `read [filters]` | thread: `{ message, replies, last }`, the root, its reply count, and its latest reply or `null` |
176
- | `read <id>`, `read <link>` | message: `{ id, channel, text, re, labels, reactions, by, author, createdAt, … }`, where `re` is the root's ID or `null` for a root |
177
- | `sync <id>` | target: `{ target, key, thread, message, url, undelivered }`, the message's copy there and the platform's link to it |
178
- | `tail`, `wait` | event: `{ cursor, type, createdAt, payload, message, actor }` |
179
- | `post`, `reply` | the new message |
180
-
181
- `read`'s lines carry no header and no cursor, so `jq` reads each as it is:
197
+ | `read <channel>`, `read [filters]` | `{ threads, cursor }`: each thread `{ message, replies, last }`, its root, its reply count, and its latest reply or `null`. `cursor` is where `tail` and `wait` read on from |
198
+ | `read <id>`, `read <link>` | `{ message, replies, cursor, schedules }`: the root and its replies, each `{ id, channel, text, re, labels, reactions, by, author, createdAt, … }` |
199
+ | `channels`, `actors`, `schedules`, `inbox`, `sources` | `{ channels }`, `{ actors }`, `{ schedules }`, `{ events }`, `{ sources }` |
200
+ | `sync <id>` | `{ targets }`: each `{ target, key, thread, message, url, undelivered }`, the message's copy there and the platform's link to it |
201
+ | `post`, `reply` | `{ message, outcome: "posted" }` |
202
+ | `edit`, `delete`, `react`, `label` | `{ message: { id }, outcome }`, `outcome` being `edited`, `deleted`, `reacted` or `unreacted`, or `labeled` |
203
+ | `schedule` | `{ schedule, outcome: "scheduled" }`; `schedule cancel`'s outcome is `canceled`, or `unchanged` when it already wasn't pending |
204
+ | `wait` | `{ event }`: `{ cursor, type, createdAt, payload, message, actor }` |
205
+ | `tail` | one event per line, as they come |
206
+ | `whoami` | `{ actor, name, handle, profile, session, expiresAt, can, board, source }` |
207
+
208
+ A write's warnings are in its document, as
209
+ `"warnings": [{ "reason": "unknown_mention", "message": "…" }]`.
182
210
 
183
211
  ```bash
184
- vb read prs --label state=open --json | jq -r '"#\(.message.id) \(.replies) \(.message.text)"'
185
- vb read 1204 --json | jq -r '.text' # every message in one thread
212
+ vb read prs --label state=open --json | jq -r '.threads[] | "#\(.message.id) \(.replies) \(.message.text)"'
213
+ vb read 1204 --json | jq -r '.message.text, .replies[].text' # every message in one thread
214
+ ```
215
+
216
+ ### Errors
217
+
218
+ An error says what failed and why, then each way out, likeliest first:
219
+
220
+ ```text
221
+ $ vb raed dev
222
+ error: Unknown command raed for vb. Did you mean read?
223
+
224
+ See all commands:
225
+ vb --help
226
+ ```
227
+
228
+ With `--json`, stdout stays empty and stderr gets one line, the Vercel CLI's
229
+ error document:
230
+
231
+ ```json
232
+ {"status":"error","reason":"unknown_command","message":"Unknown command raed for vb. Did you mean read?","next":[{"command":"vb --help","argv":["vb","--help"],"when":"See all commands"}]}
186
233
  ```
187
234
 
235
+ - **`reason` is what a program branches on:** `missing_scope`,
236
+ `login_required`, `permission_denied`, `deployment_protected`,
237
+ `board_not_found`, `message_not_found`, `channel_not_found`,
238
+ `unknown_command`, `unknown_option`, `invalid_arguments`, `timeout`, and
239
+ the board's own codes, such as `already_synced` or `no_scheduler`.
240
+ - **`next` is what to run**, each `{ command, argv, when }`, and `hint` what
241
+ to do when it isn't a command. `status` is `action_required` when the
242
+ command lacked something before it did anything: a team, say.
243
+ - **`data.error` is the board's own error**, `{ code, message, status }`, when
244
+ it answered with one.
245
+
188
246
  ## Exit codes
189
247
 
190
248
  | Code | Meaning |
191
249
  | --- | --- |
192
250
  | 0 | success |
193
- | 1 | error, e.g. `error: message not found: #9999` |
194
- | 2 | usage error: an unknown command or flag, a missing argument |
251
+ | 1 | it failed: `error: Couldn't find message #9999.` |
252
+ | 2 | misuse: an unknown command or option, a missing or malformed argument, or no board to use |
195
253
  | 124 | `wait` timed out (the GNU `timeout` convention), or `identity link` saw no link in time |
254
+ | 130 | Ctrl-C stopped `tail` or `wait` |
196
255
 
197
256
  ## Setup
198
257
 
@@ -231,27 +290,6 @@ can ci/ read post
231
290
 
232
291
  See [Access](../01-board/09-access.md).
233
292
 
234
- ### link
235
-
236
- ```text
237
- vb link <url>
238
- ```
239
-
240
- Links this project to a board. Reads the board's discovery document, saves
241
- `{ "url": "<board URL>" }` in `.board/config.json` at the Git checkout's root
242
- (or the nearest existing link), and adds `.board/` to `.gitignore`. Without
243
- Git, it links the current directory. See
244
- [Coding agents](../03-guides/01-coding-agents.md).
245
-
246
- ### use
247
-
248
- ```text
249
- vb use <url>
250
- ```
251
-
252
- Saves the global default board, used when there's no flag, `BOARD_URL`, or
253
- project link.
254
-
255
293
  ### serve
256
294
 
257
295
  ```text
@@ -259,21 +297,20 @@ vb serve [--port N] [--dir <dir>] [--no-inspect] [--no-open]
259
297
  ```
260
298
 
261
299
  Runs a board on this machine until Ctrl-C, at
262
- `http://127.0.0.1:4100/api/board` (`--port` picks another), and links this
263
- project to it. A project linked to another board stays linked, and `vb serve`
264
- prints the `BOARD_URL` that reaches this one instead. It starts the
300
+ `http://127.0.0.1:4100/api/board` (`--port` picks another). Other commands
301
+ reach it with `--url` or `BOARD_URL`, as it prints. It starts the
265
302
  [inspector](#inspect) and opens it; `--no-inspect` doesn't start it, and
266
303
  `--no-open` doesn't open it.
267
304
 
268
305
  ```text
269
306
  $ vb serve
270
- linked /Users/ada/src/www to http://127.0.0.1:4100/api/board
271
307
  serving a local board at http://127.0.0.1:4100/api/board as local:user:ada, its log in /Users/ada/src/www/.board (Ctrl-C to stop)
308
+ reach it with --url http://127.0.0.1:4100/api/board, or BOARD_URL=http://127.0.0.1:4100/api/board
272
309
  inspector at http://127.0.0.1:4747
273
310
  ```
274
311
 
275
- - **It keeps everything in `.board/`** at the Git checkout's root, or in
276
- `--dir`: the log in SQLite (`board.db`) and files in `attachments/`. It runs
312
+ - **It keeps everything in `.board/`** at the Git checkout's root (and adds
313
+ it to `.gitignore`), or in `--dir`: the log in SQLite (`board.db`) and files in `attachments/`. It runs
277
314
  a scheduler in the process, so schedules send while it runs, and passes every
278
315
  [conformance](#conformance) check. Its timers live in the process: a restart
279
316
  drops the pending ones, a scheduled message's included. It has no sync.
@@ -36,8 +36,7 @@ The short samples below act on that `team`, and on `msg`, the message
36
36
  | default | browsers, anything else | Finds none in the environment: pass `http(url)`, whose `fetch` sends the page's own session. The rest runs the same |
37
37
 
38
38
  In Node, `board()` with no backend is `http()` to the board the environment
39
- points at: `BOARD_URL`, then the project link (`.board/config.json`), then
40
- the board saved by `vb use`. Its default `fetch` sends a token whose audience
39
+ points at: `BOARD_URL`. Its default `fetch` sends a token whose audience
41
40
  is that board's URL, from `BOARD_TOKEN`, a local board's token file
42
41
  (`vb serve`), GitHub Actions, the Vercel runtime, then your `vercel login`,
43
42
  refreshed a minute before it expires, through Deployment Protection. The
@@ -517,7 +516,8 @@ which only the handle reaches. A handle on `http()` finds the board it uses in
517
516
  ## Errors
518
517
 
519
518
  A failed request throws a `BoardError` with the board's `code`, its
520
- `message`, and the HTTP `status`.
519
+ `message`, the HTTP `status`, and, when the handle or the board knows what
520
+ fixes it, a `hint` (`Run vercel login.`).
521
521
 
522
522
  ```ts
523
523
  // anywhere in Node:
@@ -534,7 +534,7 @@ try {
534
534
  ```
535
535
 
536
536
  The handle raises these itself, before the board answers. `board()` throws
537
- `no_board` and `invalid_config` as it's made, and `wait` throws
537
+ `no_board` as it's made, and `wait` throws
538
538
  `invalid_duration`, each with `status` 0. A call that never reaches the board
539
539
  fails with the status its URL answered, or 502.
540
540
 
@@ -550,7 +550,6 @@ fails with the status its URL answered, or 502.
550
550
  | `no_token` | GitHub Actions refused an OIDC token (`permissions: id-token: write`) |
551
551
  | `protected` | the board is behind Vercel Deployment Protection: set `VERCEL_AUTOMATION_BYPASS_SECRET` |
552
552
  | `ambiguous_agent` | the environment shows several coding agents at once: `as(null)` acts as yourself |
553
- | `invalid_config` | the project link isn't a valid board URL |
554
553
  | `invalid_duration` | a `timeout` the handle can't read |
555
554
  | `too_many_redirects` | the board's URL redirects more than 20 times |
556
555
 
@@ -436,6 +436,10 @@ Every failure is JSON with a matching status:
436
436
  { "error": { "code": "message_not_found", "message": "message not found: #9999" } }
437
437
  ```
438
438
 
439
+ When the board knows what fixes it, the error carries a `hint` too, as a board
440
+ on `http()` does when its own handle can't reach the board it uses:
441
+ `"hint": "Set VERCEL_AUTOMATION_BYPASS_SECRET (Project → Settings → …)."`.
442
+
439
443
  | Status | Code | When |
440
444
  | --- | --- | --- |
441
445
  | 400 | `invalid_request` | the body or query doesn't validate; the message names the field |
package/docs/index.md CHANGED
@@ -31,7 +31,7 @@ development needs no setup. On Vercel, pass Postgres or Redis: see
31
31
  ## Talk to it from anywhere
32
32
 
33
33
  ```bash
34
- vb link https://example.com/api/board # once per checkout
34
+ export BOARD_URL=https://example.com/api/board
35
35
  vb post errors-www "deploy failed on a1b2c3" # prints the new message's ID: 812
36
36
  vb reply 812 "rolled back to d4e5f6"
37
37
  vb read errors-www
@@ -40,8 +40,7 @@ vb wait --re 812 --timeout 30m # blocks until someone answers
40
40
 
41
41
  There are no accounts. People are who their `vercel login` says they are,
42
42
  and deployments and CI runs are who their platform's OIDC token says. Access
43
- to the board is access to its Vercel project. A CI job or a sandbox points at
44
- the board with `BOARD_URL` instead of a link. The CLI, your scripts, and the
43
+ to the board is access to its Vercel project. The CLI, your scripts, and the
45
44
  board's own watchers and sync all act through the same [handle](./04-reference/02-board.md).
46
45
 
47
46
  ## Sync Slack and GitHub
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/board",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "A message board you mount in one route: one durable log, verified authors, live tail, and two-way sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,4 +0,0 @@
1
- import "./auth-CU56Q2Xh.mjs";
2
- import "./schemas-CWxs3ECJ.mjs";
3
- import "./provider-D11Dfda0.mjs";
4
- import "./backend-CWmd4UH-.mjs";