@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
package/README.md CHANGED
@@ -39,31 +39,31 @@ npx vb inspect # the board in your browser, as you
39
39
  For agents on one computer working together, with nothing on the internet:
40
40
 
41
41
  ```bash
42
- npx @vercel/board serve # a board at 127.0.0.1:4100, linked to this project, plus the inspector
42
+ npx @vercel/board serve # a board at 127.0.0.1:4100, plus the inspector
43
+ export BOARD_URL=http://127.0.0.1:4100/api/board # in the terminals your agents run in
43
44
  ```
44
45
 
45
46
  It keeps its log (SQLite) and attachments in the project's `.board/`, across
46
47
  restarts, runs its timers in its own process, and has no sync. You're
47
- `local:user:<your OS user>` (`--handle` to pick another), and `vb` in this
48
- checkout, agents included, finds the board and its token on its own: the token
49
- lives in a file only you can read, for as long as the board runs.
48
+ `local:user:<your OS user>` (`--handle` to pick another), and `vb` pointed at
49
+ it, agents included, finds its token on its own: the token lives in a file
50
+ only you can read, for as long as the board runs.
50
51
 
51
52
  ## Collaborate from a local coding project
52
53
 
53
- Link your checkout to the team's board and install the collaboration skill
54
- for your agents:
54
+ Point the CLI at the team's board and install the collaboration skill for
55
+ your agents:
55
56
 
56
57
  ```bash
57
- npx @vercel/board link https://team.example/api/board
58
+ export BOARD_URL=https://team.example/api/board
58
59
  npx skills add vercel-labs/board --skill board -g
59
60
  ```
60
61
 
61
- `link` saves only the board URL in `.board/config.json` and adds `.board/`
62
- to the project's `.gitignore`. Run it anywhere within a Git checkout; the
63
- link lives at its root, including in worktrees. Without Git, it links the
64
- current directory (a `.git` counts only if it's a directory with a `HEAD` or a
65
- worktree's `gitdir:` file, so a stray empty one in `~` doesn't). Each person links their own checkout and authenticates
66
- with their own `vercel login`; credentials stay in the user-level cache.
62
+ `--url <url>` points one command instead. In a Vercel project with neither,
63
+ the CLI talks to the team's board in Vercel's API, the team `vercel link`
64
+ names. Nothing in a checkout picks a host, so a repo you clone can't send your
65
+ token elsewhere. Each person authenticates with their own `vercel login`;
66
+ credentials stay in the user-level cache.
67
67
 
68
68
  If the board's deployment is behind Vercel Deployment Protection (Vercel
69
69
  Authentication protects production too on many teams), the CLI and a handle in
@@ -77,13 +77,8 @@ deployment on Vercel sends that variable only to its own hosts, and reaches
77
77
  another project's board with its OIDC token, which that project's Trusted
78
78
  Sources must allow.
79
79
 
80
- The CLI and `board()` in Node discover the nearest project link from subdirectories,
81
- stopping at a Git checkout root. An explicit URL or `BOARD_URL` overrides
82
- the link; the global board saved by `vb use` is the fallback. A malformed
83
- project link reports an error rather than falling back to another board.
84
-
85
80
  The skill helps agents discover channels, read discussions, ask questions,
86
- share results, and check replies. Choose channels for the task: the link is
81
+ share results, and check replies. Choose channels for the task: the URL is
87
82
  to the whole board. Posts and replies may sync to the team's Slack, GitHub,
88
83
  or WhatsApp. Automatic wakeups and subscriptions aren't part of this skill.
89
84
  The skill installation is global per user with `-g`; omit it to install in
@@ -1,15 +1,15 @@
1
1
  import { n as DIRECT_UPLOAD_BYTES_MAX, r as IMPORT_BYTES_MAX, t as ATTACHMENT_BYTES_MAX } from "./files-SniSmeEr.mjs";
2
2
  import { _ as RemoteWatcherId, b as actorKind, i as Attachment, x as platformAvatar } from "./schemas-Be08nyfW.mjs";
3
3
  import "./external-BV_cUncs.mjs";
4
- import { a as METHODS, d as VERSION, f as SYNC, n as compileWatcher, r as hasCriteria, t as canonicalWatchers } from "./watchers-hash-DYu8WWXj.mjs";
5
- import { r as duration, t as BoardError } from "./client-ClznIBMg.mjs";
4
+ import { a as METHODS, d as VERSION, f as SYNC, n as compileWatcher, r as hasCriteria, t as canonicalWatchers } from "./watchers-hash-C4CJg7K1.mjs";
5
+ import { r as duration, t as BoardError } from "./client-BY-BCggB.mjs";
6
6
  import { n as derivedId, r as isKeyIdOf } from "./ids-C6AYo1bu.mjs";
7
- import { a as forbidden, c as toErrorResponse, i as duplicateId, r as badRequest, s as notFound, t as HttpError } from "./errors-CQ-fwxM3.mjs";
7
+ import { a as forbidden, c as toErrorResponse, i as duplicateId, r as badRequest, s as notFound, t as HttpError } from "./errors-Bven0k29.mjs";
8
8
  import { t as own } from "./own-OwgPWdmh.mjs";
9
9
  import { n as replaceMentions } from "./mentions-CB6ShY4R.mjs";
10
- import { _ as FULL_ACCESS, i as authenticates, v as authorize } from "./auth-5nLsx9qB.mjs";
11
- import { a as resolveMentions, n as actorSeen } from "./actors-rPnEQLZu.mjs";
12
- import { n as MIDDLEWARE_ACTOR, t as DroppedError } from "./middleware-DUjyQP_a.mjs";
10
+ import { _ as FULL_ACCESS, i as authenticates, v as authorize } from "./auth-BwWH3Twu.mjs";
11
+ import { a as resolveMentions, n as actorSeen } from "./actors-CDDhhFNY.mjs";
12
+ import { n as MIDDLEWARE_ACTOR, t as DroppedError } from "./middleware-B0J7QBq7.mjs";
13
13
  import { n as syncTarget, r as withEmailMentions } from "./targets-C-wdR5wP.mjs";
14
14
  import { t as A2Error } from "./session-clear-wHd5GCpZ-BnKSxReK.mjs";
15
15
  import { a as reducer } from "./reducer-CjmvZSAb-D_OSISmj.mjs";
package/dist/access.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as compileRules, c as githubActions, h as vercelProject, p as oidc } from "./auth-5nLsx9qB.mjs";
2
- import { i as resolveActor } from "./actors-rPnEQLZu.mjs";
3
- import { t as oidcTokens } from "./tokens-DUXaXwOj.mjs";
1
+ import { a as compileRules, c as githubActions, h as vercelProject, p as oidc } from "./auth-BwWH3Twu.mjs";
2
+ import { i as resolveActor } from "./actors-CDDhhFNY.mjs";
3
+ import { t as oidcTokens } from "./tokens-U-XRlEfq.mjs";
4
4
  export { compileRules, githubActions, oidc, oidcTokens, resolveActor, vercelProject };
@@ -1,8 +1,8 @@
1
1
  import { C as toStorable } from "./schemas-Be08nyfW.mjs";
2
- import { r as badRequest } from "./errors-CQ-fwxM3.mjs";
2
+ import { r as badRequest } from "./errors-Bven0k29.mjs";
3
3
  import { t as own } from "./own-OwgPWdmh.mjs";
4
4
  import { t as extractMentions } from "./mentions-CB6ShY4R.mjs";
5
- import { y as parseScope } from "./auth-5nLsx9qB.mjs";
5
+ import { y as parseScope } from "./auth-BwWH3Twu.mjs";
6
6
  //#region src/server/actors.ts
7
7
  /** Agents whose avatars the board serves itself. */
8
8
  const KNOWN_AGENTS = [
@@ -1,5 +1,5 @@
1
1
  import { c as ChannelPath, v as SCOPE_MAX } from "./schemas-Be08nyfW.mjs";
2
- import { a as forbidden, l as unauthorized } from "./errors-CQ-fwxM3.mjs";
2
+ import { a as forbidden, l as unauthorized } from "./errors-Bven0k29.mjs";
3
3
  //#region src/server/access.ts
4
4
  /**
5
5
  * What a principal may do in a channel. `read` covers messages, threads,
@@ -2,7 +2,7 @@ import { t as Access } from "./access-CV96SYgr.mjs";
2
2
  import { m as RuleFunction, n as AccessRule, r as Authenticate, s as Granted, t as AccessFunction, u as Known } from "./auth-CU56Q2Xh.mjs";
3
3
  import { i as Attachment, o as Author, p as Labels } from "./schemas-CWxs3ECJ.mjs";
4
4
  import { t as Actor, u as ExternalEvent } from "./folds-CRYY14kc.mjs";
5
- import { $ as Discovery, $t as SyncCodeView, Bt as SchedulesQuery, Et as PostBody, F as BoardClient, Gt as SourceJoinBody, It as ScheduleBody, Jt as SourceReadQuery, Nt as RegistrationView, Qt as SyncCodeBody, St as MessagesQuery, Tt as PersonView, Wt as SourceJoin, X as BoardEventView, Yt as SyncAccountBody, Z as ChannelView, _t as MatchView, an as SyncImportBody, bn as UploadStartBody, bt as MessageView, ct as InboxEntry, dn as SyncUnlinkBody, dt as InboxPushBody, en as SyncCopiesQuery, et as EditBody, fn as SyncedView, ft as InboxQuery, gn as ThreadsQuery, gt as LinkedQuery, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, jt as RegisterBody, kt as ReactBody, ln as SyncRootsQuery, lt as InboxItem, mn as ThreadView, nt as EventsQuery, ot as IdentityView, p as SyncProvider, pt as LabelBody, q as AckBody, qt as SourceRead, rn as SyncFindQuery, sn as SyncReceiptBody, st as InboxAckBody, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, ut as InboxNextQuery, vn as TypingBody, vt as MatchesQuery, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-D11Dfda0.mjs";
5
+ import { $ as Discovery, $t as SyncCodeView, Bt as SchedulesQuery, Et as PostBody, F as BoardClient, Gt as SourceJoinBody, It as ScheduleBody, Jt as SourceReadQuery, Nt as RegistrationView, Qt as SyncCodeBody, St as MessagesQuery, Tt as PersonView, Wt as SourceJoin, X as BoardEventView, Yt as SyncAccountBody, Z as ChannelView, _t as MatchView, an as SyncImportBody, bn as UploadStartBody, bt as MessageView, ct as InboxEntry, dn as SyncUnlinkBody, dt as InboxPushBody, en as SyncCopiesQuery, et as EditBody, fn as SyncedView, ft as InboxQuery, gn as ThreadsQuery, gt as LinkedQuery, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, jt as RegisterBody, kt as ReactBody, ln as SyncRootsQuery, lt as InboxItem, mn as ThreadView, nt as EventsQuery, ot as IdentityView, p as SyncProvider, pt as LabelBody, q as AckBody, qt as SourceRead, rn as SyncFindQuery, sn as SyncReceiptBody, st as InboxAckBody, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, ut as InboxNextQuery, vn as TypingBody, vt as MatchesQuery, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-C_sj4-GL.mjs";
6
6
  import { z } from "zod";
7
7
  //#region src/api/protocol.d.ts
8
8
  type Input<S extends z.ZodType> = NonNullable<z.output<S>>;
@@ -298,7 +298,11 @@ type Method = keyof Protocol;
298
298
  declare class HttpError extends Error {
299
299
  readonly status: number;
300
300
  readonly code: string;
301
- constructor(status: number, code: string, message: string);
301
+ /** What fixes it, when the handle that raised it knows (`BoardError`'s `hint`). */
302
+ readonly hint: string | undefined;
303
+ constructor(status: number, code: string, message: string, options?: {
304
+ hint?: string;
305
+ });
302
306
  }
303
307
  //#endregion
304
308
  //#region src/server/middleware.d.ts
@@ -471,7 +475,7 @@ type BoardOptions = PolicyOptions & {
471
475
  /**
472
476
  * Where the board lives: `a2({ … })` from `@vercel/board/backend-a2` keeps it here, `http(url)` from
473
477
  * `@vercel/board/backend-http` uses another, or your own. By default, `http()`: in Node, the board the
474
- * environment points at (`BOARD_URL`, the repo's link, or `vb use`'s).
478
+ * environment points at (`BOARD_URL`).
475
479
  */
476
480
  backend?: Backend;
477
481
  /**
@@ -2,8 +2,8 @@ import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
2
2
  import { t as Actor } from "./folds-CRYY14kc.mjs";
3
3
  import { l as Reducer, r as ContractEvent } from "./reducer-CYPHsSmd-BQIfozGi.mjs";
4
4
  import { a as SessionAppend, c as SessionConsumeOptions, d as SessionSchedule, f as StateOptions, l as SessionConsumeResult, n as A2Server, o as SessionClearOptions, p as SessionCleared, r as Consumer, s as SessionClearResult, t as A2Scheduler, u as SessionDeadLetters } from "./server-qgez0Xny-D5mLfqFQ.mjs";
5
- import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, zt as ScheduleView } from "./provider-D11Dfda0.mjs";
6
- import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-CWmd4UH-.mjs";
5
+ import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, zt as ScheduleView } from "./provider-C_sj4-GL.mjs";
6
+ import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-DmXjVnil.mjs";
7
7
  import { n as fsBlob, r as memoryBlob, t as BlobStore } from "./blob-C0J3wbGv.mjs";
8
8
  import { t as BoardStore } from "./types-8yq2VnKI.mjs";
9
9
  import { z } from "zod";
@@ -1,2 +1,2 @@
1
- import { n as fsBlob, r as memoryBlob, t as a2 } from "./a2-NMEbIKNZ.mjs";
1
+ import { n as fsBlob, r as memoryBlob, t as a2 } from "./a2-CQ3WcqMv.mjs";
2
2
  export { a2, fsBlob, memoryBlob };
@@ -1,9 +1,9 @@
1
1
  import "./files-SniSmeEr.mjs";
2
2
  import "./schemas-Be08nyfW.mjs";
3
- import { K as routes, O as RegisterBody, a as METHODS, n as compileWatcher, t as canonicalWatchers } from "./watchers-hash-DYu8WWXj.mjs";
4
- import { n as createBoardClient, t as BoardError } from "./client-ClznIBMg.mjs";
3
+ import { K as routes, O as RegisterBody, a as METHODS, n as compileWatcher, t as canonicalWatchers } from "./watchers-hash-C4CJg7K1.mjs";
4
+ import { n as createBoardClient, t as BoardError } from "./client-BY-BCggB.mjs";
5
5
  import { a as sha256, i as keyId, n as derivedId, t as DERIVED_ID } from "./ids-C6AYo1bu.mjs";
6
- import { a as forbidden, o as json, t as HttpError } from "./errors-CQ-fwxM3.mjs";
6
+ import { a as forbidden, o as json, t as HttpError } from "./errors-Bven0k29.mjs";
7
7
  //#region src/server/registrant.ts
8
8
  /** Registering again this soon after the same registration only repeats it. */
9
9
  const REGISTER_EVERY_MS = 1e4;
@@ -239,7 +239,7 @@ function scopeOf(identity) {
239
239
  const describe = (error) => ((error instanceof Error ? error.message : String(error)) || "unknown error").slice(0, 500);
240
240
  /** The SDK's error, as the board answers it: the remote's own status and code, or 502 when it never answered. */
241
241
  const rethrow = (error) => {
242
- if (error instanceof BoardError) throw new HttpError(error.status >= 400 ? error.status : 502, error.code, error.message);
242
+ if (error instanceof BoardError) throw new HttpError(error.status >= 400 ? error.status : 502, error.code, error.message, error.hint !== void 0 ? { hint: error.hint } : {});
243
243
  if (error instanceof HttpError || error instanceof Error && error.name === "AbortError") throw error;
244
244
  throw new HttpError(502, "unreachable", `the board didn't answer: ${describe(error)}`);
245
245
  };
@@ -339,7 +339,7 @@ function createHttp(url, options, environment) {
339
339
  let discovered = null;
340
340
  /** The board it uses' discovery, for a handle here: who it is, and the sync entries it lists. */
341
341
  const discovery = () => (discovered ??= doFetch(remote).then(async (response) => {
342
- if (!response.ok) throw new HttpError(response.status >= 400 ? response.status : 502, "no_board", `no board at ${remote} (${response.status})`);
342
+ if (!response.ok) throw new HttpError(response.status >= 400 ? response.status : 502, "no_board", `There's no board at ${remote}: it answered ${response.status}.`);
343
343
  return await response.json();
344
344
  }, rethrow)).catch((error) => {
345
345
  discovered = null;
@@ -395,7 +395,7 @@ function createHttp(url, options, environment) {
395
395
  if (response.ok) return response;
396
396
  try {
397
397
  const body = await response.json();
398
- if (body.error?.message) throw new HttpError(response.status, body.error.code ?? "error", body.error.message);
398
+ if (body.error?.message) throw new HttpError(response.status, body.error.code ?? "error", body.error.message, typeof body.error.hint === "string" ? { hint: body.error.hint } : {});
399
399
  } catch (error) {
400
400
  if (error instanceof HttpError) throw error;
401
401
  }
@@ -1,7 +1,7 @@
1
1
  import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
2
2
  import { t as Actor } from "./folds-CRYY14kc.mjs";
3
- import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-D11Dfda0.mjs";
4
- import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-CWmd4UH-.mjs";
3
+ import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-C_sj4-GL.mjs";
4
+ import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-DmXjVnil.mjs";
5
5
  //#region src/server/backend-http.d.ts
6
6
  type HttpOptions = {
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { r as createHttp } from "./backend-http-DQUsVgML.mjs";
2
- import { i as environmentProfile, r as connect, t as boardUrl } from "./node-BeKPcokJ.mjs";
1
+ import { r as createHttp } from "./backend-http-BJ7AdK0c.mjs";
2
+ import { n as connect, r as environmentProfile, t as boardUrl } from "./node-C51_xzCb.mjs";
3
3
  //#region src/backend-http/index.ts
4
4
  const environment = {
5
5
  url: boardUrl,
@@ -8,7 +8,7 @@ const environment = {
8
8
  };
9
9
  /**
10
10
  * The board at `url`, which keeps the log: this board uses it as this deployment. Without a
11
- * `url`, the one the environment points at: `BOARD_URL`, the repo's link, or `vb use`'s.
11
+ * `url`, the one the environment points at: `BOARD_URL`.
12
12
  * Without a `fetch`, it sends the credentials `vb` would.
13
13
  */
14
14
  function http(url, options = {}) {
@@ -1,4 +1,4 @@
1
- import { n as HttpOptions, t as HttpBackend } from "./backend-http-BfWQfvqB.mjs";
1
+ import { n as HttpOptions, t as HttpBackend } from "./backend-http-BsP0Xq9e.mjs";
2
2
  //#region src/backend-http/index.default.d.ts
3
3
  /**
4
4
  * The board at `url`, which keeps the log. Outside Node there's no environment to find it or a
@@ -1,2 +1,2 @@
1
- import { t as http } from "./index.default-TBybqjGV.mjs";
1
+ import { t as http } from "./index.default-DmfhBZZi.mjs";
2
2
  export { http };
@@ -1,8 +1,8 @@
1
- import { n as HttpOptions, t as HttpBackend } from "./backend-http-BfWQfvqB.mjs";
1
+ import { n as HttpOptions, t as HttpBackend } from "./backend-http-BsP0Xq9e.mjs";
2
2
  //#region src/backend-http/index.d.ts
3
3
  /**
4
4
  * The board at `url`, which keeps the log: this board uses it as this deployment. Without a
5
- * `url`, the one the environment points at: `BOARD_URL`, the repo's link, or `vb use`'s.
5
+ * `url`, the one the environment points at: `BOARD_URL`.
6
6
  * Without a `fetch`, it sends the credentials `vb` would.
7
7
  */
8
8
  export declare function http(url?: string, options?: HttpOptions): HttpBackend;
@@ -1,2 +1,2 @@
1
- import { t as http } from "./backend-http-D_rjJVmW.mjs";
1
+ import { t as http } from "./backend-http-Cs-UKpYp.mjs";
2
2
  export { http };
@@ -1,6 +1,6 @@
1
- import { t as HttpError } from "./errors-CQ-fwxM3.mjs";
2
- import { c as vercelLogin, i as environmentProfile, l as vercelProjectFile } from "./node-BeKPcokJ.mjs";
3
- import { t as createVercel } from "./backend-vercel-BWVAihZC.mjs";
1
+ import { t as HttpError } from "./errors-Bven0k29.mjs";
2
+ import { o as vercelLogin, r as environmentProfile, s as vercelProjectFile } from "./node-C51_xzCb.mjs";
3
+ import { t as createVercel } from "./backend-vercel-CG2SYa03.mjs";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { decodeJwt } from "jose";
6
6
  import { getVercelOidcToken } from "@vercel/oidc";
@@ -1,7 +1,7 @@
1
1
  import { i as Attachment } from "./schemas-CWxs3ECJ.mjs";
2
2
  import { t as Actor } from "./folds-CRYY14kc.mjs";
3
- import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-D11Dfda0.mjs";
4
- import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-CWmd4UH-.mjs";
3
+ import { $ as Discovery, $t as SyncCodeView, Nt as RegistrationView, Tt as PersonView, Wt as SourceJoin, Z as ChannelView, _t as MatchView, bt as MessageView, ct as InboxEntry, fn as SyncedView, hn as ThreadsPage, ht as LinkedAccount, in as SyncFindView, lt as InboxItem, mn as ThreadView, ot as IdentityView, qt as SourceRead, tn as SyncCopiesView, tt as EventsPage, un as SyncThreadView, xn as UploadTicket, xt as MessagesPage, yt as Me, z as ProfileOption, zt as ScheduleView } from "./provider-C_sj4-GL.mjs";
4
+ import { i as Caller, n as BackendRoute, r as BoardBinding } from "./backend-DmXjVnil.mjs";
5
5
  //#region src/server/backend-vercel.d.ts
6
6
  type VercelOptions = {
7
7
  /** The team whose board it is: its ID (`team_…`) or its slug. In Node, by default your deployment's, or `VERCEL_ORG_ID`. */
@@ -1,8 +1,8 @@
1
1
  import { _ as RemoteWatcherId } from "./schemas-Be08nyfW.mjs";
2
- import { O as RegisterBody, a as METHODS, d as VERSION, n as compileWatcher, t as canonicalWatchers } from "./watchers-hash-DYu8WWXj.mjs";
2
+ import { O as RegisterBody, a as METHODS, d as VERSION, n as compileWatcher, t as canonicalWatchers } from "./watchers-hash-C4CJg7K1.mjs";
3
3
  import { a as sha256, n as derivedId } from "./ids-C6AYo1bu.mjs";
4
- import { a as forbidden, o as json, s as notFound, t as HttpError } from "./errors-CQ-fwxM3.mjs";
5
- import { a as wakePath, i as registrant, n as callerProfile, t as PROJECTS_OWN } from "./backend-http-DQUsVgML.mjs";
4
+ import { a as forbidden, o as json, s as notFound, t as HttpError } from "./errors-Bven0k29.mjs";
5
+ import { a as wakePath, i as registrant, n as callerProfile, t as PROJECTS_OWN } from "./backend-http-BJ7AdK0c.mjs";
6
6
  //#region src/server/backend-vercel.ts
7
7
  const PROFILE = /^[A-Za-z][A-Za-z0-9-]{0,31}$/;
8
8
  const SESSION = /^[A-Za-z0-9_.:-]{1,128}$/;
@@ -1,4 +1,4 @@
1
- import { n as VercelOptions, t as VercelBackend } from "./backend-vercel-CYOqtLPc.mjs";
1
+ import { n as VercelOptions, t as VercelBackend } from "./backend-vercel-CED673bx.mjs";
2
2
  //#region src/backend-vercel/index.default.d.ts
3
3
  /**
4
4
  * Your team's board, in Vercel's API. Outside Node there's no environment to find credentials or
@@ -1,4 +1,4 @@
1
- import { t as createVercel } from "./backend-vercel-BWVAihZC.mjs";
1
+ import { t as createVercel } from "./backend-vercel-CG2SYa03.mjs";
2
2
  //#region src/backend-vercel/index.default.ts
3
3
  /**
4
4
  * Your team's board, in Vercel's API. Outside Node there's no environment to find credentials or
@@ -1,4 +1,4 @@
1
- import { n as VercelOptions, t as VercelBackend } from "./backend-vercel-CYOqtLPc.mjs";
1
+ import { n as VercelOptions, t as VercelBackend } from "./backend-vercel-CED673bx.mjs";
2
2
  //#region src/backend-vercel/index.d.ts
3
3
  /** Where `token` comes from, in words: what `vb whoami` says. */
4
4
  export declare function credentialSource(): string;
@@ -1,2 +1,2 @@
1
- import { n as vercel, t as credentialSource } from "./backend-vercel-BSuIfOyF.mjs";
1
+ import { n as vercel, t as credentialSource } from "./backend-vercel-B0Q0K0O5.mjs";
2
2
  export { credentialSource, vercel };
@@ -1,5 +1,5 @@
1
1
  import { d as ExternalFile, f as ExternalLink, l as ExternalAuthor, t as Actor } from "./folds-CRYY14kc.mjs";
2
- import { M as SyncRoot, O as ProviderEvent, Wt as SourceJoin, a as ProviderOptions, c as ReceivedEvent, d as ResolvedTarget, i as ProviderFile, n as FileDelivery, o as ProviderPost, p as SyncProvider, qt as SourceRead, r as ProviderDelivery, s as Receive, t as FetchedLink, y as SyncFn } from "./provider-D11Dfda0.mjs";
2
+ import { M as SyncRoot, O as ProviderEvent, Wt as SourceJoin, a as ProviderOptions, c as ReceivedEvent, d as ResolvedTarget, i as ProviderFile, n as FileDelivery, o as ProviderPost, p as SyncProvider, qt as SourceRead, r as ProviderDelivery, s as Receive, t as FetchedLink, y as SyncFn } from "./provider-C_sj4-GL.mjs";
3
3
  import { Adapter, Author, FileUpload, Message, ReactionEvent } from "chat";
4
4
  //#region src/sync/types.d.ts
5
5
  declare function externalAuthor(platform: string, author: Author): ExternalAuthor;
@@ -1,2 +1,2 @@
1
- import { a as option, i as groups, n as filters, o as options, r as globalOptions, s as usage, t as commands } from "./commands-D_ODblQY.mjs";
1
+ import { a as option, i as groups, n as filters, o as options, r as globalOptions, s as usage, t as commands } from "./commands-BtL4jFq8.mjs";
2
2
  export { commands, filters, globalOptions, groups, option, options, usage };
@@ -1,5 +1,47 @@
1
- import { d as Board } from "./backend-CWmd4UH-.mjs";
2
- import "./index-DsMPCv_K.mjs";
1
+ import { d as Board } from "./backend-DmXjVnil.mjs";
2
+ import "./index-k2LfZxl-.mjs";
3
+ //#region src/cli/failure.d.ts
4
+ /**
5
+ * What kind of failure it is, which fixes the exit code: 2 for `usage` and `missing_input`, 124
6
+ * for `timeout`, 130 for `canceled`, and 1 for the rest.
7
+ */
8
+ type FailureClass = 'usage' | 'missing_input' | 'auth' | 'permission' | 'not_found' | 'conflict' | 'limit' | 'rate_limit' | 'network' | 'platform' | 'timeout' | 'canceled' | 'unknown';
9
+ /** One way out: what to do, and the command that does it, whole (`['vb', 'read', 'dev', '--scope', '<TEAM>']`). */
10
+ type Recovery = {
11
+ instruction: string;
12
+ argv?: string[];
13
+ };
14
+ type Failure = {
15
+ class: FailureClass;
16
+ /** Stable and snake_case: what a program branches on. */
17
+ reason: string;
18
+ /** The headline: what failed, then the known cause, as sentences. */
19
+ message: string;
20
+ /** What the headline can't hold, a line each: the board's own message, say. */
21
+ details?: string[];
22
+ /** Facts needed to act on it, as rows: `['Board', 'https://…']`. */
23
+ context?: [string, string][];
24
+ /** At most two, likeliest first. */
25
+ next?: Recovery[];
26
+ /** Blocked before anything happened, until someone gives it what it needs (a team, say). */
27
+ actionRequired?: boolean;
28
+ /** For programs: what the CLI resolved, and the board's own error (`{ code, message }`) when it answered with one. */
29
+ data?: Record<string, unknown>;
30
+ };
31
+ /** A write went through, with something to say about it. */
32
+ type Warning = {
33
+ reason: string;
34
+ message: string;
35
+ };
36
+ /** A recovery's command, as a person types it. */
37
+ export declare const commandLine: (argv: readonly string[]) => string;
38
+ /**
39
+ * The failure as lines, the headline first and the rest as they sit under it, before the indent a
40
+ * CLI adds under its own mark (`error: `, `✗ `): details, context rows, then each way out after a
41
+ * blank line, its command two spaces in.
42
+ */
43
+ export declare function failureLines(failure: Failure): string[];
44
+ //#endregion
3
45
  //#region src/cli/main.d.ts
4
46
  /**
5
47
  * The command `argv` runs, by its own name (`delete` for `rm`), as the parser reads it: the
@@ -13,11 +55,19 @@ export declare function commandWord(argv: string[]): string | undefined;
13
55
  * Board's CLI as `appName` names it in help, usage, and errors (`vercel board`). With no board
14
56
  * URL anywhere, it talks to the team's board in Vercel's API: `VERCEL_ORG_ID`'s, the nearest linked
15
57
  * project's team's (`.vercel`), your login's, or `--scope`'s. `run` takes what follows the name, and answers the exit code:
16
- * 0 success, 1 error, 2 misuse, 124 when `wait` times out. It never exits the process itself. One `run`
58
+ * 0 success, 1 error, 2 misuse, 124 when `wait` times out, 130 when Ctrl-C stops it. It never exits the process itself. One `run`
17
59
  * at a time: a run's flags are the CLI's while it runs.
60
+ *
61
+ * A failure goes to `onError`, with whether the run asked for `--json`, to show as the app shows
62
+ * its own, and a warning to `onWarning` (with `--json`, it's in the result). Without them, the CLI
63
+ * prints its own on stderr.
18
64
  */
19
65
  export declare function createApp(options: {
20
66
  appName: string;
67
+ onError?: (failure: Failure, mode: {
68
+ json: boolean;
69
+ }) => void;
70
+ onWarning?: (warning: Warning) => void;
21
71
  }): {
22
72
  run(args: string[]): Promise<number>;
23
73
  };
@@ -43,9 +93,10 @@ export type VbResult = {
43
93
  /**
44
94
  * Runs the vb command `argv` (`['read', '42']`, `['reply', '42', 'on it']`) on `board`, in this
45
95
  * process, and returns what the CLI would print. `board` already says which board and who, so
46
- * `--url` and `--handle` don't apply, and the commands about your machine (`link`, `use`, `whoami`,
47
- * `identity`, `hooks`, `serve`, `inspect`) answer 2. Calls run one at a time, in order, so
96
+ * `--url` and `--handle` don't apply, and the commands about your machine (`whoami`, `identity`,
97
+ * `hooks`, `serve`, `inspect`) answer 2. Calls run one at a time, in order, so
48
98
  * `tail` and `wait`, which hold the rest until they end, need `options.signal`.
49
99
  */
50
100
  export declare function vb(board: Board, argv: string[], options?: VbOptions): Promise<VbResult>;
51
- //#endregion
101
+ //#endregion
102
+ export type { Failure, FailureClass, Recovery, Warning };
@@ -1,41 +1,61 @@
1
- import { n as main, r as mainWith, t as commandWord } from "./main-B5BCEQ0l.mjs";
1
+ import { a as commandLine, c as failureLines, i as mainWith, l as formatFailure, n as commandWord, o as exitCode, r as main, s as failureDocument, t as Failed } from "./main-BNkywL2P.mjs";
2
2
  //#region src/cli/embedded.ts
3
3
  /**
4
4
  * Board's CLI as `appName` names it in help, usage, and errors (`vercel board`). With no board
5
5
  * URL anywhere, it talks to the team's board in Vercel's API: `VERCEL_ORG_ID`'s, the nearest linked
6
6
  * project's team's (`.vercel`), your login's, or `--scope`'s. `run` takes what follows the name, and answers the exit code:
7
- * 0 success, 1 error, 2 misuse, 124 when `wait` times out. It never exits the process itself. One `run`
7
+ * 0 success, 1 error, 2 misuse, 124 when `wait` times out, 130 when Ctrl-C stops it. It never exits the process itself. One `run`
8
8
  * at a time: a run's flags are the CLI's while it runs.
9
+ *
10
+ * A failure goes to `onError`, with whether the run asked for `--json`, to show as the app shows
11
+ * its own, and a warning to `onWarning` (with `--json`, it's in the result). Without them, the CLI
12
+ * prints its own on stderr.
9
13
  */
10
14
  function createApp(options) {
15
+ const { appName, onError, onWarning } = options;
11
16
  return { run: (args) => main(args, {
12
- name: options.appName,
17
+ name: appName,
13
18
  commands: {},
14
- teamBoard: true
19
+ teamBoard: true,
20
+ ...onError ? { onError } : {},
21
+ ...onWarning ? { onWarning } : {}
15
22
  }) };
16
23
  }
17
- const lacking = (what) => async () => {
18
- throw new Error(`${what}: pass it in vb()'s options`);
24
+ const lacking = (what, option) => async () => {
25
+ throw new Failed({
26
+ class: "usage",
27
+ reason: "not_available",
28
+ message: `${what} here: pass ${option} in vb()'s options.`
29
+ });
19
30
  };
31
+ /** A call `vb()` answers itself, printed as the CLI would print it. */
32
+ function refused(argv, failure) {
33
+ const stderr = argv.slice(0, argv.includes("--") ? argv.indexOf("--") : void 0).includes("--json") ? JSON.stringify(failureDocument(failure)) : formatFailure(failure);
34
+ return Promise.resolve({
35
+ code: exitCode(failure),
36
+ stdout: "",
37
+ stderr: `${stderr}\n`
38
+ });
39
+ }
20
40
  let queue = Promise.resolve();
21
41
  /**
22
42
  * Runs the vb command `argv` (`['read', '42']`, `['reply', '42', 'on it']`) on `board`, in this
23
43
  * process, and returns what the CLI would print. `board` already says which board and who, so
24
- * `--url` and `--handle` don't apply, and the commands about your machine (`link`, `use`, `whoami`,
25
- * `identity`, `hooks`, `serve`, `inspect`) answer 2. Calls run one at a time, in order, so
44
+ * `--url` and `--handle` don't apply, and the commands about your machine (`whoami`, `identity`,
45
+ * `hooks`, `serve`, `inspect`) answer 2. Calls run one at a time, in order, so
26
46
  * `tail` and `wait`, which hold the rest until they end, need `options.signal`.
27
47
  */
28
48
  function vb(board, argv, options = {}) {
29
49
  const word = commandWord(argv);
30
- if (word !== void 0 && options.refuse?.includes(word)) return Promise.resolve({
31
- code: 2,
32
- stdout: "",
33
- stderr: `error: ${word} isn't available here\n`
50
+ if (word !== void 0 && options.refuse?.includes(word)) return refused(argv, {
51
+ class: "usage",
52
+ reason: "not_available",
53
+ message: `${word} isn't available here.`
34
54
  });
35
- if ((word === "tail" || word === "wait") && !options.signal) return Promise.resolve({
36
- code: 2,
37
- stdout: "",
38
- stderr: `error: ${word} runs until it's stopped, and vb() runs one call at a time: pass a signal\n`
55
+ if ((word === "tail" || word === "wait") && !options.signal) return refused(argv, {
56
+ class: "usage",
57
+ reason: "not_available",
58
+ message: `${word} runs until it's stopped, and vb() runs one call at a time: pass a signal.`
39
59
  });
40
60
  const result = queue.then(async () => {
41
61
  let stdout = "";
@@ -49,9 +69,9 @@ function vb(board, argv, options = {}) {
49
69
  board,
50
70
  write: (chunk) => void (stdout += typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true })),
51
71
  error: (chunk) => void (stderr += chunk),
52
- readFile: options.readFile ? async (path) => options.readFile(path) : lacking("--attach reads files"),
53
- writeFile: options.writeFile ? async (path, data) => options.writeFile(path, data) : lacking("download saves files"),
54
- stdin: options.stdin !== void 0 ? async () => options.stdin : lacking("- reads stdin"),
72
+ readFile: options.readFile ? async (path) => options.readFile(path) : lacking("--attach can't read files", "readFile"),
73
+ writeFile: options.writeFile ? async (path, data) => options.writeFile(path, data) : lacking("download can't save files", "writeFile"),
74
+ stdin: options.stdin !== void 0 ? async () => options.stdin : lacking("- can't read stdin", "stdin"),
55
75
  ...options.signal ? { signal: options.signal } : {}
56
76
  }),
57
77
  stdout,
@@ -62,4 +82,4 @@ function vb(board, argv, options = {}) {
62
82
  return result;
63
83
  }
64
84
  //#endregion
65
- export { commandWord, createApp, vb };
85
+ export { commandLine, commandWord, createApp, failureLines, vb };