@vercel/board 0.29.0 → 0.31.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.
- package/README.md +8 -17
- package/dist/{a2-HUbQJ0RD.mjs → a2-DngWY2Eb.mjs} +741 -415
- package/dist/access-CY5xVMIK.mjs +242 -0
- package/dist/{access-CV96SYgr.d.mts → access-DPcrKIH_.d.mts} +6 -1
- package/dist/access.d.mts +4 -4
- package/dist/access.mjs +5 -4
- package/dist/{actors-DcJhFbwJ.mjs → actors-Bm-ojWWT.mjs} +2 -2
- package/dist/{auth-JO22iIwr.mjs → auth-CyO2aw5n.mjs} +126 -236
- package/dist/{auth-CU56Q2Xh.d.mts → auth-DLYSt_-A.d.mts} +32 -4
- package/dist/{backend-CPC_vyOo.d.mts → backend-Bob-5CM7.d.mts} +93 -42
- package/dist/backend-a2/blob-vercel.d.mts +1 -1
- package/dist/backend-a2/contract.d.mts +38 -98
- package/dist/backend-a2/contract.mjs +1 -1
- package/dist/backend-a2/scheduler-local.d.mts +1 -1
- package/dist/backend-a2/scheduler-local.mjs +1 -1
- package/dist/backend-a2/scheduler-qstash.d.mts +3 -3
- package/dist/backend-a2/scheduler-qstash.mjs +8 -8
- package/dist/backend-a2/scheduler-vercel.d.mts +3 -3
- package/dist/backend-a2/scheduler-vercel.mjs +6 -6
- package/dist/backend-a2/store-memory.d.mts +2 -2
- package/dist/backend-a2/store-memory.mjs +3 -3
- package/dist/backend-a2/store-postgres.d.mts +1 -1
- package/dist/backend-a2/store-postgres.mjs +34 -15
- package/dist/backend-a2/store-redis-http.d.mts +1 -1
- package/dist/backend-a2/store-redis-http.mjs +6 -6
- package/dist/backend-a2/store-redis.d.mts +2 -2
- package/dist/backend-a2/store-redis.mjs +5 -5
- package/dist/backend-a2/store-sqlite.d.mts +1 -1
- package/dist/backend-a2/store-sqlite.mjs +3 -3
- package/dist/backend-a2.d.mts +143 -242
- package/dist/backend-a2.mjs +2 -2
- package/dist/{backend-http-CqfKhrnn.d.mts → backend-http-BRrRbjRx.d.mts} +59 -45
- package/dist/{backend-http-CzIJMm58.mjs → backend-http-CvUKOn-m.mjs} +11 -10
- package/dist/{backend-http-Bmp0IgD3.mjs → backend-http-D8mUzuKQ.mjs} +2 -2
- package/dist/backend-http.d.mts +1 -1
- package/dist/backend-http.mjs +1 -1
- package/dist/backend-http.node.d.mts +1 -1
- package/dist/backend-http.node.mjs +1 -1
- package/dist/{backend-vercel-DQc_TaHp.mjs → backend-vercel-Ba8JDtqW.mjs} +3 -3
- package/dist/{backend-vercel-4tvcPKqX.d.mts → backend-vercel-BaSEUdwg.d.mts} +62 -47
- package/dist/{backend-vercel-BNauAs7w.mjs → backend-vercel-CmY9yZ9m.mjs} +279 -42
- package/dist/backend-vercel.d.mts +1 -1
- package/dist/backend-vercel.mjs +1 -1
- package/dist/backend-vercel.node.d.mts +1 -1
- package/dist/backend-vercel.node.mjs +1 -1
- package/dist/{blob-DmiQjrYy.d.mts → blob-Bx8QPWEd.d.mts} +2 -2
- package/dist/{blob-DdbpJqN1.mjs → blob-WbB2DJKn.mjs} +30 -11
- package/dist/{chat-provider-BWHbChcQ.d.mts → chat-provider-BChYrrA7.d.mts} +32 -28
- package/dist/{chat-provider-COtsU5jB.mjs → chat-provider-DkFp2hrh.mjs} +105 -60
- package/dist/cli-commands.d.mts +1 -1
- package/dist/cli-commands.mjs +1 -1
- package/dist/cli-embedded.d.mts +2 -2
- package/dist/cli-embedded.mjs +1 -1
- package/dist/cli.mjs +4 -4
- package/dist/{client-BqAjk0Q4.d.mts → client-COfYrgfV.d.mts} +240 -227
- package/dist/{commands-D6gvrOh3.d.mts → commands-DK4W3HCJ.d.mts} +47 -10
- package/dist/{commands-C3R1AWgm.mjs → commands-L1JSGz2Z.mjs} +212 -54
- package/dist/conformance-DK43HiHf.mjs +1000 -0
- package/dist/conformance.d.mts +8 -2
- package/dist/conformance.mjs +1 -1
- package/dist/{engine-DQxXk7dX.mjs → engine-DurYIRt8.mjs} +29 -210
- package/dist/{errors-DDktVeUa.d.mts → errors-2vGs49Db.d.mts} +1 -1
- package/dist/{client-D0Px7KzF.mjs → errors-CJXhhzvT.mjs} +132 -27
- package/dist/{provider-BaVmi4Hi.mjs → events-lujPA3gQ.mjs} +80 -17
- package/dist/{exports-BG-0Wyn-.mjs → exports-CWIdN0Ub.mjs} +195 -103
- package/dist/exports-FBOQWT7O.d.mts +5 -0
- package/dist/{folds-BDMSxBUf.mjs → folds-Cu9Q9zyQ.mjs} +3 -22
- package/dist/{folds-ZkHM4M-v.d.mts → folds-DLUzfibJ.d.mts} +15 -40
- package/dist/{index-D3VvNfh_.d.mts → index-Ho-ayyQL.d.mts} +2 -2
- package/dist/index.d.mts +7 -7
- package/dist/{index.default-rB8sJ9RX.mjs → index.default-BUcwDaAe.mjs} +1 -1
- package/dist/index.mjs +5 -6
- package/dist/index.node.d.mts +7 -7
- package/dist/index.node.mjs +3 -4
- package/dist/inspect/app.js +62 -61
- package/dist/inspect/engine.js +3 -3
- package/dist/{internal-CvsrprnU-CRY89I-b.mjs → internal-CvsrprnU-zZMpDiRY.mjs} +1 -1
- package/dist/{local-DhkrAqPP.mjs → local-C76Fy5ad.mjs} +1 -1
- package/dist/{main-CVBqe3VO.mjs → main-Dz-8komG.mjs} +251 -127
- package/dist/{middleware-CXjEeef4.mjs → middleware-BavYSDUa.mjs} +1 -1
- package/dist/{node-Zd_C-gxP.mjs → node-BgeGcwX0.mjs} +7 -3
- package/dist/{platform-B4TnJtWu-CwcTyKg2.mjs → platform-B4TnJtWu-Do3agKr5.mjs} +1 -1
- package/dist/{project-CBXlLftU.mjs → project-CQ4VUibA.mjs} +1 -1
- package/dist/{provider-DmSTp9lI.d.mts → provider-gynDUoyW.d.mts} +13 -43
- package/dist/{redis-tables-BJQVUspu.mjs → redis-tables-DHf85ixI.mjs} +40 -42
- package/dist/{reducer-CjmvZSAb-D_OSISmj.mjs → reducer-Bswr11ar-P324o9vn.mjs} +2 -2
- package/dist/{reducer-CYPHsSmd-BQIfozGi.d.mts → reducer-CYPHsSmd-BXjGiptm.d.mts} +1 -1
- package/dist/{reducers-DJzVpXwM.mjs → reducers-DcENwtwZ.mjs} +74 -77
- package/dist/{retryable-lazy-DZWmHpii-Bye-W8q7.mjs → retryable-lazy-DZWmHpii-DpTc5PIP.mjs} +1 -1
- package/dist/{scheduler-task-X1SToDjP-BVoWuInI.mjs → scheduler-task-X1SToDjP-D2Il2rln.mjs} +1 -1
- package/dist/{serve-G9RuaDSr.mjs → serve-CTmow-YH.mjs} +7 -7
- package/dist/{server-qgez0Xny-D5mLfqFQ.d.mts → server-B9gu0qi1-DI1WvcVK.d.mts} +10 -4
- package/dist/{server-BixV_byP.mjs → server-DG8D6VV0.mjs} +4 -4
- package/dist/{server-DtFrWxi3-Boa6gdWO.mjs → server-DX7mM6nm-D7N_7bug.mjs} +32 -23
- package/dist/{serving-B75Y4BF-.mjs → serving-DBFRo6U_.mjs} +1 -1
- package/dist/{session-clear-wHd5GCpZ-BnKSxReK.mjs → session-clear-XJYpmUpg-2q8l_FJR.mjs} +14 -2
- package/dist/{sql-D4Z_efJX.mjs → sql-CT5xVuGr.mjs} +2 -8
- package/dist/{sqlite-db-Bz2JkfCU.mjs → sqlite-db-0WeSOEZN.mjs} +1 -1
- package/dist/{store-D0MhBvg6-4Z2L0Kqd.d.mts → store-DEzcHZOA-B-5vhxef.d.mts} +12 -2
- package/dist/store-N8PXxDAS-DWWgv-Vz.mjs +5 -0
- package/dist/{store-codec-DwrWlgQx-CYxGW0aD.mjs → store-codec-pAG2oPgQ-SqDXlPBR.mjs} +4 -4
- package/dist/store-memory-4IBl0Ztu.mjs +2 -0
- package/dist/{store-memory-Bas9Uq5C.mjs → store-memory-B0PkVSuQ.mjs} +14 -7
- package/dist/{store-polling-BZt2Rf3D-BiEM6_HB.mjs → store-polling-BZt2Rf3D-Div-bJ_D.mjs} +2 -2
- package/dist/{store-presence-polling-JuAy2Ck2-CYpuK75X.mjs → store-presence-polling-JuAy2Ck2-Dh-g_KYM.mjs} +3 -3
- package/dist/{store-sqlite-CxxPCcCT.mjs → store-sqlite-D85t3tE7.mjs} +14 -7
- package/dist/store-sqlite-iBTYYKBj.mjs +2 -0
- package/dist/{stream-buffer-DrTyiIuo-BKy-vI14.mjs → stream-buffer-DrTyiIuo-Be10yOBR.mjs} +2 -2
- package/dist/{sync-kapso.d.mts → sync-chat-sdk.d.mts} +33 -37
- package/dist/sync-chat-sdk.mjs +44 -0
- package/dist/sync-github.d.mts +5 -18
- package/dist/sync-github.mjs +4 -70
- package/dist/sync-linear.d.mts +3 -3
- package/dist/sync-linear.mjs +6 -5
- package/dist/sync-slack.d.mts +7 -7
- package/dist/sync-slack.mjs +33 -54
- package/dist/sync.d.mts +3 -3
- package/dist/sync.mjs +2 -4
- package/dist/{tokens-CC3izSNr.mjs → tokens-Dnyd_y1n.mjs} +15 -6
- package/dist/{types-No0WdhE5.d.mts → types-CmrFjpml.d.mts} +2 -4
- package/dist/{watchers-hash-BDhLVkV5.mjs → watchers-hash-BjwlnGD9.mjs} +124 -115
- package/docs/01-board/01-mounting.md +2 -2
- package/docs/01-board/03-identity.md +5 -6
- package/docs/01-board/04-messages.md +96 -2
- package/docs/01-board/05-reading.md +2 -1
- package/docs/01-board/06-watchers.md +8 -6
- package/docs/01-board/08-middleware.md +6 -3
- package/docs/01-board/09-access.md +145 -14
- package/docs/02-sync/01-overview.md +19 -26
- package/docs/02-sync/02-functions.md +8 -9
- package/docs/02-sync/03-slack.md +5 -5
- package/docs/02-sync/04-github.md +2 -2
- package/docs/02-sync/06-providers.md +91 -62
- package/docs/03-guides/01-coding-agents.md +1 -1
- package/docs/03-guides/03-ci.md +7 -4
- package/docs/04-reference/01-cli.md +109 -51
- package/docs/04-reference/02-board.md +76 -28
- package/docs/04-reference/03-http.md +76 -38
- package/docs/04-reference/04-log.md +8 -11
- package/docs/04-reference/05-conformance.md +47 -7
- package/docs/index.md +2 -2
- package/package.json +7 -10
- package/sql/postgres/board.sql +1 -1
- package/sql/postgres/schema.sql +1 -1
- package/dist/conformance-DlnfuzEB.mjs +0 -556
- package/dist/errors-CusN4x2l.mjs +0 -88
- package/dist/events-qf5XG5VQ.mjs +0 -75
- package/dist/exports-mhAYiU4i.d.mts +0 -5
- package/dist/store-N8PXxDAS-BWBgzBHp.mjs +0 -5
- package/dist/store-memory-ZLiZbH3R.mjs +0 -2
- package/dist/store-sqlite-BmwL3pUR.mjs +0 -2
- package/dist/sync-kapso.mjs +0 -89
- package/dist/sync.chat.d.mts +0 -2
- package/dist/sync.chat.mjs +0 -2
- package/dist/users-yryHKFMo.mjs +0 -42
- package/docs/02-sync/05-whatsapp.md +0 -152
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { i as fileSize, n as DIRECT_UPLOAD_BYTES_MAX } from "./files-SniSmeEr.mjs";
|
|
2
2
|
import { a as AttachmentId } from "./schemas-DBmUO6pr.mjs";
|
|
3
|
-
import { d as VERSION, i as withMentions } from "./watchers-hash-
|
|
3
|
+
import { d as VERSION, i as withMentions } from "./watchers-hash-BjwlnGD9.mjs";
|
|
4
4
|
import { n as displayEmoji, r as normalizeEmoji } from "./emoji-CTLpvH-A.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import { i as groups, n as filters$1, o as options, r as globalOptions, s as usage$1, t as commands } from "./commands-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { t as
|
|
10
|
-
import { t as
|
|
11
|
-
import {
|
|
5
|
+
import { u as BoardError } from "./errors-CJXhhzvT.mjs";
|
|
6
|
+
import { i as groups, n as filters$1, o as options, r as globalOptions, s as usage$1, t as commands } from "./commands-L1JSGz2Z.mjs";
|
|
7
|
+
import { i as parseScope } from "./access-CY5xVMIK.mjs";
|
|
8
|
+
import { a as handFetch, d as vercelProjectFile, n as boardUrl, o as handLogin, r as connect } from "./node-BgeGcwX0.mjs";
|
|
9
|
+
import { t as http } from "./backend-http-D8mUzuKQ.mjs";
|
|
10
|
+
import { t as board } from "./server-DG8D6VV0.mjs";
|
|
11
|
+
import { t as conformance } from "./conformance-DK43HiHf.mjs";
|
|
12
|
+
import { n as handTeam, r as vercel, t as credentialSource } from "./backend-vercel-Ba8JDtqW.mjs";
|
|
12
13
|
import { t as displayText } from "./bots-CGkO76qH.mjs";
|
|
13
14
|
import { spawn } from "node:child_process";
|
|
14
15
|
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
@@ -34,7 +35,6 @@ function authorName(message) {
|
|
|
34
35
|
if (!author) return message.by.actor;
|
|
35
36
|
const slash = author.handle.indexOf("/");
|
|
36
37
|
if (message.by.profile !== null && slash !== -1) return `${author.name} via ${author.handle.slice(0, slash)}`;
|
|
37
|
-
if (author.person && author.person.handle !== author.handle) return `${author.name} (${author.person.handle})`;
|
|
38
38
|
return author.name;
|
|
39
39
|
}
|
|
40
40
|
function ago(iso, now = Date.now()) {
|
|
@@ -83,7 +83,7 @@ function embed(link, pad) {
|
|
|
83
83
|
/** One message as a block: header line, indented text, reactions. */
|
|
84
84
|
function formatMessage(message, options = {}) {
|
|
85
85
|
const pad = options.reply ? " " : " ";
|
|
86
|
-
const head = `${options.reply ? " ↳ " : ""}#${message.id} ${authorName(message)} · ${ago(message.createdAt)}${message.editedAt ? " (edited)" : ""}${message.schedule ? ` · scheduled
|
|
86
|
+
const head = `${options.reply ? " ↳ " : ""}#${message.id} ${authorName(message)} · ${ago(message.createdAt)}${message.editedAt ? " (edited)" : ""}${message.schedule ? ` · scheduled ${message.schedule.id}` : ""}${labels(message)}`;
|
|
87
87
|
const body = message.deleted ? `${pad}(message removed)` : indent(displayText(message.text, message.by.actor), pad);
|
|
88
88
|
const files = (message.attachments ?? []).map((a) => `${pad}file ${a.name} · ${a.type} · ${fileSize(a.size)} · ${a.id}`);
|
|
89
89
|
const links = message.deleted ? [] : (message.links ?? []).flatMap((link) => embed(link, pad));
|
|
@@ -161,6 +161,36 @@ function formatAccess(access) {
|
|
|
161
161
|
return `${pattern.padEnd(width)} ${permissions.includes("manage") ? "all" : permissions.length === 0 ? "nothing" : permissions.join(" ")}`;
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
+
/** What a grant or token may do, as `--can` takes it: `dev/=read,post;ci/=read`, or `all`. */
|
|
165
|
+
function formatCan(can) {
|
|
166
|
+
const patterns = Object.keys(can);
|
|
167
|
+
if (patterns.length === 1 && patterns[0] === "*" && can["*"].includes("manage")) return "all";
|
|
168
|
+
if (patterns.length === 0) return "nothing";
|
|
169
|
+
return patterns.map((pattern) => `${pattern}=${can[pattern].includes("manage") ? "all" : can[pattern].join(",")}`).join(";");
|
|
170
|
+
}
|
|
171
|
+
/** Rows of cells, each column padded to its widest, two spaces apart. */
|
|
172
|
+
const table = (rows) => {
|
|
173
|
+
const widths = rows[0].map((_, column) => Math.max(...rows.map((row) => row[column].length)));
|
|
174
|
+
return rows.map((row) => row.map((cell, column) => column === row.length - 1 ? cell : cell.padEnd(widths[column])).join(" ").trimEnd()).join("\n");
|
|
175
|
+
};
|
|
176
|
+
/** Each grant on a row: its ID, its issuer, the claims it matches, and what it lets do. */
|
|
177
|
+
function formatGrants(grants) {
|
|
178
|
+
return table(grants.map((grant) => [
|
|
179
|
+
grant.id,
|
|
180
|
+
grant.issuer,
|
|
181
|
+
Object.entries(grant.claims).map(([name, value]) => `${name}=${typeof value === "string" ? value : value.join("|")}`).join(" "),
|
|
182
|
+
formatCan(grant.can)
|
|
183
|
+
]));
|
|
184
|
+
}
|
|
185
|
+
/** Each token on a row: its ID, its name, what it may do, and when it expires. */
|
|
186
|
+
function formatTokens(tokens, now = Date.now()) {
|
|
187
|
+
return table(tokens.map((token) => [
|
|
188
|
+
token.id,
|
|
189
|
+
token.name,
|
|
190
|
+
formatCan(token.can),
|
|
191
|
+
`expires ${until(token.expiresAt, now)}${token.profile !== null ? ` · as ${token.profile}` : ""}${token.parent !== null ? ` · minted with ${token.parent}` : ""}`
|
|
192
|
+
]));
|
|
193
|
+
}
|
|
164
194
|
/** `90m` → `1h30m`, `1d`: milliseconds as a duration flag would say them. */
|
|
165
195
|
function span(ms) {
|
|
166
196
|
const parts = [];
|
|
@@ -193,7 +223,7 @@ function formatSchedule(schedule, options = {}) {
|
|
|
193
223
|
const sent = schedule.sent > 0 ? ` · ${schedule.sent} sent` : "";
|
|
194
224
|
const files = schedule.attachments.length ? ` · ${schedule.attachments.length} ${schedule.attachments.length === 1 ? "file" : "files"}` : "";
|
|
195
225
|
const text = displayText(schedule.text, schedule.by.actor);
|
|
196
|
-
return [`${pad}
|
|
226
|
+
return [`${pad}${schedule.id} ${where} · ${repeat}${stop} · ${when}${sent}${files} · by ${authorName(schedule)}`, indent(text || "(no text)", `${pad} `)].join("\n");
|
|
197
227
|
}
|
|
198
228
|
/** One line per live event, for `tail` and `wait`. */
|
|
199
229
|
function formatEvent(event) {
|
|
@@ -448,9 +478,9 @@ function rows(entries, pad = padOf(entries)) {
|
|
|
448
478
|
/** An option as help writes it: `--limit <N>`, `-h, --help`, `--label <k[=v|w]>...`. */
|
|
449
479
|
const flag = (option) => `${option.short ? `-${option.short}, ` : ""}--${option.name}${option.value ? option.optionalValue ? ` [${option.value}]` : ` <${option.value}>` : ""}${option.multiple ? "..." : ""}`;
|
|
450
480
|
const shown = (list) => list.filter((option) => !option.hidden);
|
|
451
|
-
/** A command's rows in the list: itself, when it runs alone, and each of its subcommands. */
|
|
481
|
+
/** A command's rows in the list: itself, when it runs alone and no subcommand is what it runs, and each of its subcommands. */
|
|
452
482
|
function listed(command) {
|
|
453
|
-
return [...!command.subcommands || command.runsWithoutSubcommand ? [[usage$1([command]), command.description]] : [], ...(command.subcommands ?? []).filter((sub) => !sub.hidden).map((sub) => [usage$1([command, sub]), sub.description])];
|
|
483
|
+
return [...!command.subcommands || command.runsWithoutSubcommand && !command.subcommands.some((sub) => sub.default) ? [[usage$1([command]), command.description]] : [], ...(command.subcommands ?? []).filter((sub) => !sub.hidden).map((sub) => [usage$1([command, sub]), sub.description])];
|
|
454
484
|
}
|
|
455
485
|
/** Every command, by group, then what the app adds (`serve`, `inspect`), and the options they share. */
|
|
456
486
|
function rootHelp(app, version) {
|
|
@@ -497,7 +527,7 @@ function commandHelp(name, path) {
|
|
|
497
527
|
return lines.join("\n");
|
|
498
528
|
}
|
|
499
529
|
/**
|
|
500
|
-
* The command `words` name, and its subcommand when they name one (`
|
|
530
|
+
* The command `words` name, and its subcommand when they name one (`schedule cancel`, `sources slack
|
|
501
531
|
* read`): what `--help` shows the page of. Null when the first word names no command.
|
|
502
532
|
*/
|
|
503
533
|
function commandPath(words, all) {
|
|
@@ -735,7 +765,7 @@ let typed = [];
|
|
|
735
765
|
let warnings = [];
|
|
736
766
|
/** The board this run reached by its URL, for a failure to name. */
|
|
737
767
|
let reached = null;
|
|
738
|
-
/** The help `words` ask for: a command's page when they name one (`read`, `
|
|
768
|
+
/** The help `words` ask for: a command's page when they name one (`read`, `sources read`), or else every command. */
|
|
739
769
|
function help(words = []) {
|
|
740
770
|
const own = Object.values(app.commands).map((command) => command.spec);
|
|
741
771
|
const path = commandPath(words, [...commands, ...own]);
|
|
@@ -840,7 +870,7 @@ const result = (value) => io.write(`${JSON.stringify(warnings.length ? {
|
|
|
840
870
|
...value,
|
|
841
871
|
warnings
|
|
842
872
|
} : value, null, io.tty ? 2 : void 0)}\n`);
|
|
843
|
-
/** One line of a JSON stream (`tail
|
|
873
|
+
/** One line of a JSON stream (`tail`): compact, wherever it goes. */
|
|
844
874
|
const line = (value) => io.write(`${JSON.stringify(value)}\n`);
|
|
845
875
|
/** A message's ID as you'd write it: `#812` or `812`. It's passed to the board as it is. */
|
|
846
876
|
function messageId(value, what = "message ID") {
|
|
@@ -859,11 +889,18 @@ const tooLarge = (path) => new Failed({
|
|
|
859
889
|
const isMessageRef = (value) => /^#[^\s/]+$/.test(value) || /^\d+$/.test(value);
|
|
860
890
|
/** A message's ID as it's passed back: bare when it's a number, `#<id>` when it could be a channel's name. */
|
|
861
891
|
const messageRef = (id) => /^\d+$/.test(id) ? id : `#${id}`;
|
|
862
|
-
|
|
892
|
+
/** `--if-revision N`, as an edit or a delete takes it. */
|
|
893
|
+
function ifRevision() {
|
|
894
|
+
const given = flags["if-revision"];
|
|
895
|
+
if (given === void 0) return {};
|
|
896
|
+
if (!/^[1-9]\d{0,15}$/.test(given)) throw usage(`--if-revision takes the message's revision, a number, and ${given} isn't one.`);
|
|
897
|
+
return { revision: Number(given) };
|
|
898
|
+
}
|
|
899
|
+
function labelPairs(values, wrong = "Labels look like key=value, and {value} isn't one.") {
|
|
863
900
|
const pairs = {};
|
|
864
901
|
for (const value of values ?? []) {
|
|
865
902
|
const at = value.indexOf("=");
|
|
866
|
-
if (at <= 0) throw usage(
|
|
903
|
+
if (at <= 0) throw usage(wrong.replace("{value}", value));
|
|
867
904
|
pairs[value.slice(0, at)] = value.slice(at + 1);
|
|
868
905
|
}
|
|
869
906
|
return pairs;
|
|
@@ -895,12 +932,36 @@ async function attachments(board) {
|
|
|
895
932
|
}));
|
|
896
933
|
return uploaded;
|
|
897
934
|
}
|
|
898
|
-
/** `s12
|
|
935
|
+
/** A schedule's ID as the board gave it: `s12`, or `sch_…` on Vercel's board. */
|
|
899
936
|
function scheduleId(value) {
|
|
900
|
-
const id = value?.replace(/^s(?=\d)/i, "") ?? "";
|
|
901
937
|
if (value === void 0) throw usage("Missing the schedule ID, like s12.", { reason: "missing_arguments" });
|
|
902
|
-
if (!/^[^\s/]+$/.test(
|
|
903
|
-
return
|
|
938
|
+
if (!/^[^\s/]+$/.test(value)) throw usage(`${value} isn't a schedule ID like s12.`);
|
|
939
|
+
return value;
|
|
940
|
+
}
|
|
941
|
+
/** A grant's or token's ID as the board gave it: `g3`, `t12`, or another board's own. */
|
|
942
|
+
function trustId(what, value) {
|
|
943
|
+
const like = what === "grant" ? "g3" : "t12";
|
|
944
|
+
if (value === void 0) throw usage(`Missing the ${what} ID, like ${like}.`, { reason: "missing_arguments" });
|
|
945
|
+
if (!/^[^\s/]+$/.test(value)) throw usage(`${value} isn't a ${what} ID like ${like}.`);
|
|
946
|
+
return value;
|
|
947
|
+
}
|
|
948
|
+
/** `--can`, as `x-board-scope` writes it: `dev/=read,post;ci/=read`. */
|
|
949
|
+
function canFlag(value) {
|
|
950
|
+
try {
|
|
951
|
+
return parseScope(value);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
throw usage(`${error.message.replace(/x-board-scope/g, "--can")}.`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
/** `--claim k=v`, each again: the same name twice is any of its values. */
|
|
957
|
+
function claimPairs(values) {
|
|
958
|
+
const claims = {};
|
|
959
|
+
for (const value of values ?? []) {
|
|
960
|
+
const at = value.indexOf("=");
|
|
961
|
+
if (at <= 0 || at === value.length - 1) throw usage(`Claims look like name=value, and ${value} isn't one.`);
|
|
962
|
+
(claims[value.slice(0, at)] ??= []).push(value.slice(at + 1));
|
|
963
|
+
}
|
|
964
|
+
return Object.fromEntries(Object.entries(claims).map(([name, list]) => [name, list.length === 1 ? list[0] : list]));
|
|
904
965
|
}
|
|
905
966
|
/** A time flag: anything `Date` reads, as ISO. */
|
|
906
967
|
function instant(flag, value) {
|
|
@@ -970,11 +1031,12 @@ function doing() {
|
|
|
970
1031
|
case "typing": return `show typing in ${ref(a)}`;
|
|
971
1032
|
case "sync": return b === void 0 ? `list where ${ref(a)} is synced` : `sync ${ref(a)} with ${b}`;
|
|
972
1033
|
case "schedule": return a === "cancel" ? `cancel schedule ${b}` : `schedule a message in ${message ? ref(a) : a ?? "a channel"}`;
|
|
1034
|
+
case "channels": return a === "create" ? `create ${b ?? "the channel"}` : "list channels";
|
|
973
1035
|
case "schedules":
|
|
974
|
-
case "channels":
|
|
975
1036
|
case "actors": return `list ${command}`;
|
|
1037
|
+
case "grants": return a === "create" ? `grant ${b ?? "an issuer"}` : a === "revoke" ? `revoke grant ${b}` : "list grants";
|
|
1038
|
+
case "tokens": return a === "create" ? `mint token ${b ?? ""}`.trimEnd() : a === "revoke" ? `revoke token ${b}` : "list tokens";
|
|
976
1039
|
case "sources": return b === void 0 ? "list sources" : `${b} ${c ?? "it"} on ${a}`;
|
|
977
|
-
case "identity": return a === "link" ? `link your account on ${b}` : a === "unlink" ? `unlink ${b}` : "list your linked accounts";
|
|
978
1040
|
case "download": return `download ${a ?? "the attachment"}`;
|
|
979
1041
|
case "inbox": return `read ${a}'s inbox`;
|
|
980
1042
|
case "tail": return `follow ${a ?? "the board"}`;
|
|
@@ -1007,6 +1069,7 @@ function missing() {
|
|
|
1007
1069
|
"schedule"
|
|
1008
1070
|
].includes(command) && isMessageRef(a)) return `message ${ref(a)}`;
|
|
1009
1071
|
if (command === "schedule" && a === "cancel" && b !== void 0) return `schedule ${b}`;
|
|
1072
|
+
if ((command === "grants" || command === "tokens") && a === "revoke" && b !== void 0) return `${command === "grants" ? "grant" : "token"} ${b}`;
|
|
1010
1073
|
if (command === "download") return `attachment ${a}`;
|
|
1011
1074
|
}
|
|
1012
1075
|
/**
|
|
@@ -1109,7 +1172,7 @@ function failureOf(error) {
|
|
|
1109
1172
|
});
|
|
1110
1173
|
const specific = (fallback) => /^[a-z]+(_[a-z]+)+$/.test(code) && code !== "invalid_request" && code !== "remote_error" ? code : fallback;
|
|
1111
1174
|
if (status === 400) return theirs("usage", specific("invalid_arguments"), `Can't ${doing()}.`);
|
|
1112
|
-
if (status === 401) return theirs("auth", "login_required", "The board didn't accept your credentials.", [{
|
|
1175
|
+
if (status === 401) return theirs("auth", "login_required", "The board didn't accept your credentials.", process.env.BOARD_TOKEN ? [{ instruction: "Pass a token it accepts in BOARD_TOKEN, or unset it" }] : [{
|
|
1113
1176
|
instruction: "Log in again",
|
|
1114
1177
|
argv: ["vercel", "login"]
|
|
1115
1178
|
}]);
|
|
@@ -1121,7 +1184,37 @@ function failureOf(error) {
|
|
|
1121
1184
|
details: []
|
|
1122
1185
|
} : theirs("not_found", specific("not_found"), `Can't ${doing()}.`);
|
|
1123
1186
|
}
|
|
1124
|
-
if (status === 409)
|
|
1187
|
+
if (status === 409) {
|
|
1188
|
+
const { args } = current;
|
|
1189
|
+
const newer = code === "newer_message" && flags.after !== void 0 && args[0] !== void 0;
|
|
1190
|
+
const labeled = code === "label_conflict" && (flags.if !== void 0 || flags["if-unset"] !== void 0) && args[0] !== void 0;
|
|
1191
|
+
const changed = code === "revision_conflict" && flags["if-revision"] !== void 0 && args[0] !== void 0;
|
|
1192
|
+
return theirs("conflict", specific("conflict"), `Can't ${doing()}.`, newer ? [{
|
|
1193
|
+
instruction: `Read what came after ${ref(flags.after)}`,
|
|
1194
|
+
argv: [
|
|
1195
|
+
...cli(),
|
|
1196
|
+
"read",
|
|
1197
|
+
args[0].replace(/^#/, ""),
|
|
1198
|
+
"--after",
|
|
1199
|
+
flags.after.replace(/^#/, "")
|
|
1200
|
+
]
|
|
1201
|
+
}] : labeled ? [{
|
|
1202
|
+
instruction: "See its labels now",
|
|
1203
|
+
argv: [
|
|
1204
|
+
...cli(),
|
|
1205
|
+
"read",
|
|
1206
|
+
args[0].replace(/^#/, "")
|
|
1207
|
+
]
|
|
1208
|
+
}] : changed ? [{
|
|
1209
|
+
instruction: "Read it as it is now",
|
|
1210
|
+
argv: [
|
|
1211
|
+
...cli(),
|
|
1212
|
+
"read",
|
|
1213
|
+
args[0].replace(/^#/, ""),
|
|
1214
|
+
"--json"
|
|
1215
|
+
]
|
|
1216
|
+
}] : []);
|
|
1217
|
+
}
|
|
1125
1218
|
if (status === 413) return theirs("limit", "payload_too_large", `Can't ${doing()}. It's too large for the board.`);
|
|
1126
1219
|
if (status === 429) return theirs("rate_limit", "rate_limited", "Too many requests to the board. Try again in a minute.");
|
|
1127
1220
|
if (status === 501) return theirs("platform", specific("not_supported"), `Can't ${doing()}. This board doesn't support it.`);
|
|
@@ -1174,7 +1267,10 @@ function connection(url = flags.url, options = {}) {
|
|
|
1174
1267
|
const team = flags.scope ?? flags.team;
|
|
1175
1268
|
const configured = url ?? (team === void 0 ? configuredUrl() : null);
|
|
1176
1269
|
if (configured === null) {
|
|
1177
|
-
const board$1 = board({ backend: vercel(
|
|
1270
|
+
const board$1 = board({ backend: vercel({
|
|
1271
|
+
...team !== void 0 ? { team } : {},
|
|
1272
|
+
...sending(options.token)
|
|
1273
|
+
}) });
|
|
1178
1274
|
return {
|
|
1179
1275
|
url: null,
|
|
1180
1276
|
transport: { source: credentialSource },
|
|
@@ -1185,7 +1281,10 @@ function connection(url = flags.url, options = {}) {
|
|
|
1185
1281
|
reached = at;
|
|
1186
1282
|
const onVercel = vercelBoard(at);
|
|
1187
1283
|
if (onVercel) {
|
|
1188
|
-
const board$3 = board({ backend: vercel(
|
|
1284
|
+
const board$3 = board({ backend: vercel({
|
|
1285
|
+
...onVercel,
|
|
1286
|
+
...sending(options.token)
|
|
1287
|
+
}) });
|
|
1189
1288
|
return {
|
|
1190
1289
|
url: at,
|
|
1191
1290
|
transport: { source: credentialSource },
|
|
@@ -1204,6 +1303,15 @@ function connection(url = flags.url, options = {}) {
|
|
|
1204
1303
|
board: profile !== void 0 ? board$2.as(profile) : board$2
|
|
1205
1304
|
};
|
|
1206
1305
|
}
|
|
1306
|
+
/** For Vercel's API, `token` in place of your own credentials: a board token's secret. */
|
|
1307
|
+
function sending(token) {
|
|
1308
|
+
if (token === void 0) return {};
|
|
1309
|
+
return { fetch: (input, init) => {
|
|
1310
|
+
const request = new Request(input, init);
|
|
1311
|
+
request.headers.set("authorization", `Bearer ${token}`);
|
|
1312
|
+
return globalThis.fetch(request);
|
|
1313
|
+
} };
|
|
1314
|
+
}
|
|
1207
1315
|
/** A handle on the board this command talks to: `vb()`'s, as `--as` says, or the one `connection()` finds. */
|
|
1208
1316
|
function client() {
|
|
1209
1317
|
if (!io.board) return connection().board;
|
|
@@ -1287,94 +1395,6 @@ function portFlag() {
|
|
|
1287
1395
|
if (port !== void 0 && !(Number.isInteger(port) && port >= 0 && port <= 65535)) throw usage(`${flags.port} isn't a port.`, { reason: "invalid_port" });
|
|
1288
1396
|
return port;
|
|
1289
1397
|
}
|
|
1290
|
-
/** How long `identity link` waits for the sign-in or the code. */
|
|
1291
|
-
const LINK_WAIT_MS = 6e5;
|
|
1292
|
-
async function identity(args) {
|
|
1293
|
-
const board = client();
|
|
1294
|
-
const [sub, target] = args;
|
|
1295
|
-
if (sub === void 0) {
|
|
1296
|
-
const view = await board.identity();
|
|
1297
|
-
if (flags.json) return result(view), 0;
|
|
1298
|
-
if (view.links.length === 0) out("no linked accounts");
|
|
1299
|
-
for (const link of view.links) out(`${link.handle ? `@${link.handle} ` : ""}${link.actor} by ${link.method === "code" ? "code" : "sign-in"} · ${link.at}`);
|
|
1300
|
-
if (view.unlinked?.length) out(`not linked ${view.unlinked.join(", ")} (${named()} identity link <key>)`);
|
|
1301
|
-
return 0;
|
|
1302
|
-
}
|
|
1303
|
-
if (sub === "unlink") {
|
|
1304
|
-
if (!target) throw usage(`Missing the account to unlink: a handle or actor ID from \`${named()} identity\`.`, { reason: "missing_arguments" });
|
|
1305
|
-
if (args.length !== 2) throw usage("identity unlink takes one account.");
|
|
1306
|
-
const unlinked = await board.unlinkAccount(target);
|
|
1307
|
-
if (flags.json) result({
|
|
1308
|
-
...unlinked,
|
|
1309
|
-
outcome: "unlinked"
|
|
1310
|
-
});
|
|
1311
|
-
else out(`unlinked ${unlinked.actor}`);
|
|
1312
|
-
return 0;
|
|
1313
|
-
}
|
|
1314
|
-
if (sub !== "link") throw usage(`Unknown command ${sub} for ${named()} identity.`, { reason: "unknown_command" });
|
|
1315
|
-
if (!target) throw usage("Missing the sync entry to link your account on, such as slack.", { reason: "missing_arguments" });
|
|
1316
|
-
if (args.length !== 2) throw usage("identity link takes one sync entry.");
|
|
1317
|
-
const before = new Set((await board.identity()).links.map((link) => `${link.actor}|${link.at}`));
|
|
1318
|
-
const platform = (await board.discovery().catch(() => null))?.sync.find((entry) => entry.key === target)?.platform ?? target;
|
|
1319
|
-
const { code, expiresAt, signIn } = await board.syncCode(target);
|
|
1320
|
-
const consent = signIn !== void 0 && !flags.code;
|
|
1321
|
-
if (consent && browsable(signIn) === null) throw new Failed({
|
|
1322
|
-
class: "platform",
|
|
1323
|
-
reason: "invalid_sign_in",
|
|
1324
|
-
message: "The board's sign-in isn't an http or https URL.",
|
|
1325
|
-
details: [JSON.stringify(signIn)],
|
|
1326
|
-
data: { signIn }
|
|
1327
|
-
});
|
|
1328
|
-
if (flags.json) line({
|
|
1329
|
-
type: "code",
|
|
1330
|
-
sync: target,
|
|
1331
|
-
platform,
|
|
1332
|
-
code,
|
|
1333
|
-
expiresAt,
|
|
1334
|
-
...signIn !== void 0 ? { signIn } : {}
|
|
1335
|
-
});
|
|
1336
|
-
const until = Date.now() + LINK_WAIT_MS;
|
|
1337
|
-
const sleep = () => new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
1338
|
-
/** An account linked on that platform since this run began: where the code or the sign-in landed. */
|
|
1339
|
-
const linkedSince = async () => (await board.identity()).links.find((link) => link.actor.startsWith(`${platform}:user:`) && !before.has(`${link.actor}|${link.at}`));
|
|
1340
|
-
if (consent) {
|
|
1341
|
-
if (!flags.json) {
|
|
1342
|
-
out(`Sign in to ${platform} as yourself to link it (and to post as you there):\n\n ${signIn}\n`);
|
|
1343
|
-
out(`Only open it yourself: whoever completes it links their account to you.`);
|
|
1344
|
-
}
|
|
1345
|
-
if (!flags["no-open"] && !flags.json) openBrowser(signIn);
|
|
1346
|
-
} else if (!flags.json) {
|
|
1347
|
-
const minutes = Math.round((Date.parse(expiresAt) - Date.now()) / 6e4);
|
|
1348
|
-
out(`From your ${platform} account, post this as a message anywhere the board syncs, within ${minutes} minutes:\n\n ${code}\n`);
|
|
1349
|
-
}
|
|
1350
|
-
if (flags["no-wait"]) return 0;
|
|
1351
|
-
while (Date.now() < Math.min(until, consent ? Infinity : Date.parse(expiresAt))) {
|
|
1352
|
-
await sleep();
|
|
1353
|
-
const linked = await linkedSince();
|
|
1354
|
-
if (linked) {
|
|
1355
|
-
if (flags.json) line({
|
|
1356
|
-
type: "linked",
|
|
1357
|
-
account: linked
|
|
1358
|
-
});
|
|
1359
|
-
else out(`linked ${linked.handle ? `@${linked.handle} ` : ""}(${linked.actor})`);
|
|
1360
|
-
return 0;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
throw new Failed({
|
|
1364
|
-
class: "timeout",
|
|
1365
|
-
reason: "timeout",
|
|
1366
|
-
message: consent ? "Nothing linked an account within 10 minutes." : "The code expired before a message carried it.",
|
|
1367
|
-
next: [{
|
|
1368
|
-
instruction: "Try again",
|
|
1369
|
-
argv: [
|
|
1370
|
-
...cli(),
|
|
1371
|
-
"identity",
|
|
1372
|
-
"link",
|
|
1373
|
-
target
|
|
1374
|
-
]
|
|
1375
|
-
}]
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
1398
|
async function run(command, args) {
|
|
1379
1399
|
switch (command) {
|
|
1380
1400
|
case void 0:
|
|
@@ -1410,12 +1430,31 @@ async function run(command, args) {
|
|
|
1410
1430
|
}
|
|
1411
1431
|
case "conformance": {
|
|
1412
1432
|
if (args.length > 0) throw usage("conformance takes no arguments: pass its channel with --channel.");
|
|
1413
|
-
const
|
|
1433
|
+
const withToken = io.board ? {} : { withToken: (secret) => connection(flags.url, { token: secret }).board };
|
|
1434
|
+
const report = await conformance(client(), {
|
|
1435
|
+
...flags.channel ? { channel: flags.channel } : {},
|
|
1436
|
+
...withToken
|
|
1437
|
+
});
|
|
1414
1438
|
if (flags.json) result(report);
|
|
1415
1439
|
else out(formatConformance(report));
|
|
1416
1440
|
return report.ok ? 0 : 1;
|
|
1417
1441
|
}
|
|
1418
1442
|
case "channels": {
|
|
1443
|
+
const [sub, ...rest] = args;
|
|
1444
|
+
if (sub === "create") {
|
|
1445
|
+
const [path, extra] = rest;
|
|
1446
|
+
if (!path) throw usage("Missing the channel to create, such as dev/alerts.", { reason: "missing_arguments" });
|
|
1447
|
+
if (extra !== void 0) throw usage(`channels create takes one channel: ${extra} is one too many.`);
|
|
1448
|
+
const { channel, created } = await client().createChannel(path);
|
|
1449
|
+
if (flags.json) result({
|
|
1450
|
+
channel,
|
|
1451
|
+
outcome: created ? "created" : "unchanged"
|
|
1452
|
+
});
|
|
1453
|
+
else out(created ? `created ${channel.path}` : `${channel.path} already exists`);
|
|
1454
|
+
return 0;
|
|
1455
|
+
}
|
|
1456
|
+
if (sub !== void 0 && sub !== "list") throw usage(`Unknown command ${sub} for ${named()} channels.`, { reason: "unknown_command" });
|
|
1457
|
+
if (rest.length > 0) throw usage(`channels list takes no arguments: ${rest[0]} is one too many.`);
|
|
1419
1458
|
const channels = await client().channels();
|
|
1420
1459
|
if (flags.json) return result({ channels }), 0;
|
|
1421
1460
|
if (channels.length === 0) return out("no channels yet"), 0;
|
|
@@ -1427,17 +1466,18 @@ async function run(command, args) {
|
|
|
1427
1466
|
group = prefix;
|
|
1428
1467
|
}
|
|
1429
1468
|
const count = (n, one, many) => `${n} ${n === 1 ? one : many}`;
|
|
1430
|
-
|
|
1469
|
+
const { threads, messages, lastIndex } = channel;
|
|
1470
|
+
const counted = threads !== void 0 && messages !== void 0 ? ` ${count(threads, "thread", "threads")} · ${count(messages, "message", "messages")}${lastIndex ? ` · last #${lastIndex}` : ""}` : "";
|
|
1471
|
+
out(`${prefix ? " " : ""}${channel.path}${counted}`);
|
|
1431
1472
|
}
|
|
1432
1473
|
return 0;
|
|
1433
1474
|
}
|
|
1434
1475
|
case "actors": {
|
|
1435
1476
|
const actors = await client().actors();
|
|
1436
1477
|
if (flags.json) return result({ actors }), 0;
|
|
1437
|
-
for (const actor of actors) out(`@${actor.handle} ${actor.name} ${actor.id}
|
|
1478
|
+
for (const actor of actors) out(`@${actor.handle} ${actor.name} ${actor.id}`);
|
|
1438
1479
|
return 0;
|
|
1439
1480
|
}
|
|
1440
|
-
case "identity": return identity(args);
|
|
1441
1481
|
case "read": {
|
|
1442
1482
|
const target = args[0];
|
|
1443
1483
|
const board = client();
|
|
@@ -1506,6 +1546,7 @@ async function run(command, args) {
|
|
|
1506
1546
|
]
|
|
1507
1547
|
}] });
|
|
1508
1548
|
if (flags.sync?.length && flags["no-sync"]) throw usage("Can't use --sync with --no-sync.");
|
|
1549
|
+
if (re === void 0 && flags.after !== void 0) throw usage("Can't use --after on a new thread. It's for replies: the thread's newest message.");
|
|
1509
1550
|
const board = client();
|
|
1510
1551
|
const content = body === void 0 && flags.attach?.length ? "" : await text(body);
|
|
1511
1552
|
const files = await attachments(board);
|
|
@@ -1519,7 +1560,10 @@ async function run(command, args) {
|
|
|
1519
1560
|
const message = re === void 0 ? await board.post(target, {
|
|
1520
1561
|
...options,
|
|
1521
1562
|
...flags.sync?.length ? { sync: flags.sync } : {}
|
|
1522
|
-
}) : await board.reply(re,
|
|
1563
|
+
}) : await board.reply(re, {
|
|
1564
|
+
...options,
|
|
1565
|
+
...flags.after !== void 0 ? { after: messageId(flags.after) } : {}
|
|
1566
|
+
});
|
|
1523
1567
|
warnUnknownMentions(message.unknownMentions);
|
|
1524
1568
|
printMessage(message);
|
|
1525
1569
|
return 0;
|
|
@@ -1533,7 +1577,8 @@ async function run(command, args) {
|
|
|
1533
1577
|
text: content,
|
|
1534
1578
|
...files.length ? { attachments: files } : {},
|
|
1535
1579
|
...flags["idempotency-key"] ? { id: flags["idempotency-key"] } : {},
|
|
1536
|
-
...flags["no-sync"] ? { noSync: true } : {}
|
|
1580
|
+
...flags["no-sync"] ? { noSync: true } : {},
|
|
1581
|
+
...flags.after !== void 0 ? { after: messageId(flags.after) } : {}
|
|
1537
1582
|
});
|
|
1538
1583
|
warnUnknownMentions(message.unknownMentions);
|
|
1539
1584
|
printMessage(message);
|
|
@@ -1546,7 +1591,7 @@ async function run(command, args) {
|
|
|
1546
1591
|
schedule,
|
|
1547
1592
|
outcome: canceled ? "canceled" : "unchanged"
|
|
1548
1593
|
});
|
|
1549
|
-
else out(canceled ? `canceled
|
|
1594
|
+
else out(canceled ? `canceled ${schedule.id}` : `${schedule.id} is already ${schedule.state}`);
|
|
1550
1595
|
return 0;
|
|
1551
1596
|
}
|
|
1552
1597
|
const [target, body] = args;
|
|
@@ -1579,6 +1624,80 @@ async function run(command, args) {
|
|
|
1579
1624
|
else out(formatSchedule(schedule));
|
|
1580
1625
|
return 0;
|
|
1581
1626
|
}
|
|
1627
|
+
case "grants": {
|
|
1628
|
+
const [verb, ...rest] = args;
|
|
1629
|
+
if (verb === void 0 || verb === "list") {
|
|
1630
|
+
if (rest.length > 0) throw usage(`grants list takes no arguments: ${rest[0]} is one too many.`);
|
|
1631
|
+
const list = await client().grants();
|
|
1632
|
+
if (flags.json) return result({ grants: list }), 0;
|
|
1633
|
+
out(list.length === 0 ? "no grants" : formatGrants(list));
|
|
1634
|
+
return 0;
|
|
1635
|
+
}
|
|
1636
|
+
if (verb === "revoke") {
|
|
1637
|
+
const id = trustId("grant", rest[0]);
|
|
1638
|
+
const { revoked } = await client().revokeGrant(id);
|
|
1639
|
+
if (flags.json) result({
|
|
1640
|
+
grant: id,
|
|
1641
|
+
outcome: revoked ? "revoked" : "unchanged"
|
|
1642
|
+
});
|
|
1643
|
+
else out(revoked ? `revoked ${id}` : `${id} is already gone`);
|
|
1644
|
+
return 0;
|
|
1645
|
+
}
|
|
1646
|
+
if (verb !== "create") throw usage(`Unknown command ${verb} for ${named()} grants.`, { reason: "unknown_command" });
|
|
1647
|
+
const [issuer, extra] = rest;
|
|
1648
|
+
if (!issuer) throw usage("Missing the issuer to grant: vercel, github, or an https URL.", { reason: "missing_arguments" });
|
|
1649
|
+
if (extra !== void 0) throw usage(`grants create takes one issuer, and its claims as --claim name=value: ${extra} is one too many.`);
|
|
1650
|
+
const claims = claimPairs(flags.claim);
|
|
1651
|
+
if (Object.keys(claims).length === 0) throw usage(`Missing the claims ${issuer}'s tokens must carry: pass --claim name=value.`, { reason: "missing_arguments" });
|
|
1652
|
+
const { grant, created } = await client().createGrant({
|
|
1653
|
+
issuer,
|
|
1654
|
+
claims,
|
|
1655
|
+
...flags.can !== void 0 ? { can: canFlag(flags.can) } : {}
|
|
1656
|
+
});
|
|
1657
|
+
if (flags.json) result({
|
|
1658
|
+
grant,
|
|
1659
|
+
outcome: created ? "granted" : "updated"
|
|
1660
|
+
});
|
|
1661
|
+
else out(`${created ? "granted" : "updated"} ${grant.id}`);
|
|
1662
|
+
return 0;
|
|
1663
|
+
}
|
|
1664
|
+
case "tokens": {
|
|
1665
|
+
const [verb, ...rest] = args;
|
|
1666
|
+
if (verb === void 0 || verb === "list") {
|
|
1667
|
+
if (rest.length > 0) throw usage(`tokens list takes no arguments: ${rest[0]} is one too many.`);
|
|
1668
|
+
const list = await client().tokens();
|
|
1669
|
+
if (flags.json) return result({ tokens: list }), 0;
|
|
1670
|
+
out(list.length === 0 ? "no tokens" : formatTokens(list));
|
|
1671
|
+
return 0;
|
|
1672
|
+
}
|
|
1673
|
+
if (verb === "revoke") {
|
|
1674
|
+
const id = trustId("token", rest[0]);
|
|
1675
|
+
const { revoked } = await client().revokeToken(id);
|
|
1676
|
+
if (flags.json) result({
|
|
1677
|
+
token: id,
|
|
1678
|
+
outcome: revoked ? "revoked" : "unchanged"
|
|
1679
|
+
});
|
|
1680
|
+
else out(revoked ? `revoked ${id}` : `${id} isn't a live token of yours`);
|
|
1681
|
+
return 0;
|
|
1682
|
+
}
|
|
1683
|
+
if (verb !== "create") throw usage(`Unknown command ${verb} for ${named()} tokens.`, { reason: "unknown_command" });
|
|
1684
|
+
const [name, extra] = rest;
|
|
1685
|
+
if (!name) throw usage("Missing a name for the token, like feedback-widget.", { reason: "missing_arguments" });
|
|
1686
|
+
if (extra !== void 0) throw usage(`tokens create takes one name: ${extra} is one too many.`);
|
|
1687
|
+
const { token, secret } = await client().createToken({
|
|
1688
|
+
name,
|
|
1689
|
+
...flags.can !== void 0 ? { can: canFlag(flags.can) } : {},
|
|
1690
|
+
...flags.profile !== void 0 ? { profile: flags.profile } : {},
|
|
1691
|
+
...flags.session !== void 0 ? { session: flags.session } : {},
|
|
1692
|
+
...flags.expires !== void 0 ? { expires: flags.expires } : {}
|
|
1693
|
+
});
|
|
1694
|
+
if (flags.json) result({
|
|
1695
|
+
token,
|
|
1696
|
+
secret
|
|
1697
|
+
});
|
|
1698
|
+
else out(secret);
|
|
1699
|
+
return 0;
|
|
1700
|
+
}
|
|
1582
1701
|
case "schedules": {
|
|
1583
1702
|
const list = await client().schedules({
|
|
1584
1703
|
...args[0] ? { re: messageId(args[0]) } : {},
|
|
@@ -1630,14 +1749,14 @@ async function run(command, args) {
|
|
|
1630
1749
|
case "hook": return 0;
|
|
1631
1750
|
case "edit": {
|
|
1632
1751
|
const id = messageId(args[0]);
|
|
1633
|
-
const { unknownMentions } = await client().edit(id, await text(args[1]));
|
|
1752
|
+
const { unknownMentions } = await client().edit(id, await text(args[1]), ifRevision());
|
|
1634
1753
|
warnUnknownMentions(unknownMentions);
|
|
1635
1754
|
printDone(id, "edited");
|
|
1636
1755
|
return 0;
|
|
1637
1756
|
}
|
|
1638
1757
|
case "delete": {
|
|
1639
1758
|
const id = messageId(args[0]);
|
|
1640
|
-
await client().delete(id);
|
|
1759
|
+
await client().delete(id, ifRevision());
|
|
1641
1760
|
printDone(id, "deleted");
|
|
1642
1761
|
return 0;
|
|
1643
1762
|
}
|
|
@@ -1659,9 +1778,15 @@ async function run(command, args) {
|
|
|
1659
1778
|
const set = labelPairs(args.slice(1));
|
|
1660
1779
|
const unset = flags.unset ?? [];
|
|
1661
1780
|
if (Object.keys(set).length === 0 && unset.length === 0) throw usage("Missing the labels: k=v to set one, --unset k to remove one.", { reason: "missing_arguments" });
|
|
1781
|
+
const expected = labelPairs(flags.if, "--if takes key=value, and {value} isn't one. For a label that mustn't be set, --if-unset key.");
|
|
1782
|
+
for (const key of flags["if-unset"] ?? []) {
|
|
1783
|
+
if (Object.hasOwn(expected, key)) throw usage(`Can't use --if ${key}=… with --if-unset ${key}.`);
|
|
1784
|
+
expected[key] = null;
|
|
1785
|
+
}
|
|
1662
1786
|
await client().label(id, {
|
|
1663
1787
|
set,
|
|
1664
|
-
unset
|
|
1788
|
+
unset,
|
|
1789
|
+
...Object.keys(expected).length ? { if: expected } : {}
|
|
1665
1790
|
});
|
|
1666
1791
|
printDone(id, "labeled");
|
|
1667
1792
|
return 0;
|
|
@@ -1888,7 +2013,6 @@ async function runOnce(argv, options) {
|
|
|
1888
2013
|
/** The commands about the machine the CLI runs on, not the board: none of them run in-process. */
|
|
1889
2014
|
const MACHINE = /* @__PURE__ */ new Set([
|
|
1890
2015
|
"whoami",
|
|
1891
|
-
"identity",
|
|
1892
2016
|
"hooks",
|
|
1893
2017
|
"hook",
|
|
1894
2018
|
"serve",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { m as MESSAGE_TEXT_MAX, p as Labels } from "./schemas-DBmUO6pr.mjs";
|
|
2
|
-
import { t as HttpError } from "./errors-
|
|
2
|
+
import { t as HttpError } from "./errors-CJXhhzvT.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/server/middleware.ts
|
|
5
5
|
/** A write the middleware didn't let through: the API answers 422 with what it responded. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { u as BoardError } from "./errors-CJXhhzvT.mjs";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { mkdir, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -496,12 +496,16 @@ function guarded(url, doFetch, suppliedToken) {
|
|
|
496
496
|
envRejected = true;
|
|
497
497
|
}
|
|
498
498
|
if (process.env.VERCEL === "1") {
|
|
499
|
+
let edgeless = false;
|
|
499
500
|
if (!headers.has("x-vercel-trusted-oidc-idp-token")) {
|
|
500
501
|
const token = headers.get("authorization")?.match(/^Bearer (.+)$/i)?.[1] ?? (typeof suppliedToken === "function" ? await suppliedToken() : suppliedToken) ?? (process.env.BOARD_TOKEN || await hostedToken(url));
|
|
501
|
-
|
|
502
|
+
const edge = token.startsWith("board_") ? await hostedToken(url).catch(() => null) : token;
|
|
503
|
+
if (edge === null) edgeless = true;
|
|
504
|
+
else headers.set("x-vercel-trusted-oidc-idp-token", edge);
|
|
502
505
|
}
|
|
503
506
|
return through(doFetch, input, init, null).catch((error) => {
|
|
504
|
-
|
|
507
|
+
if (!(error instanceof Wall)) throw error;
|
|
508
|
+
throw edgeless ? new BoardError("protected", `${url} is behind Vercel Deployment Protection, and this deployment has no Vercel OIDC token to get through it.`, error.status, { hint: "Enable OIDC on its project, and add it to the board project's Deployment Protection Trusted Sources." }) : new BoardError("protected", `${url} is behind Vercel Deployment Protection, and it rejected this deployment's Vercel OIDC token.`, error.status, { hint: "Add this project and environment to the board project's Deployment Protection Trusted Sources." });
|
|
505
509
|
});
|
|
506
510
|
}
|
|
507
511
|
const used = await (secret ??= readBypass(host));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../node_modules/.pnpm/experimental-a2@0.
|
|
1
|
+
//#region ../../node_modules/.pnpm/experimental-a2@0.24.0_@upstash+qstash@2.12.0_@vercel+queue@0.7.0_ai@7.0.130_zod@4.6.5__b1e2c243a963f2620a5d202817417c26/node_modules/experimental-a2/dist/platform-B4TnJtWu.js
|
|
2
2
|
const SYMBOL_FOR_REQ_CONTEXT = Symbol.for("@vercel/request-context");
|
|
3
3
|
function requestContext() {
|
|
4
4
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as setOwn, t as own } from "./own-OwgPWdmh.mjs";
|
|
2
|
-
import { n as foldMessages, r as foldSync } from "./folds-
|
|
2
|
+
import { n as foldMessages, r as foldSync } from "./folds-Cu9Q9zyQ.mjs";
|
|
3
3
|
//#region src/store/project.ts
|
|
4
4
|
/** The message an event concerns, if any. */
|
|
5
5
|
function concernedMessageId(event) {
|