vellum-cli 0.3.0 → 0.3.1
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 +25 -0
- package/dist/index.js +152 -305
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -71,6 +71,31 @@ vellum push artifact.html --page-id pg_123 --json
|
|
|
71
71
|
On success it prints the new version number, the human view URL, and the raw
|
|
72
72
|
URL. With `--json` it prints the server's `CreateVersionResponse` verbatim.
|
|
73
73
|
|
|
74
|
+
### `vellum markup <page-id>`
|
|
75
|
+
|
|
76
|
+
Pin a comment to a passage of a published document. The `--quote` text must
|
|
77
|
+
appear verbatim in the document; the viewer highlights it and links your note to
|
|
78
|
+
the highlight. Without `--version`, the page's current version is used.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# anchor a note to a passage
|
|
82
|
+
vellum markup pg_123 --quote "Q3 dashboard" --body "tighten this headline"
|
|
83
|
+
|
|
84
|
+
# pipe the note in on stdin instead of --body
|
|
85
|
+
echo "tighten this headline" | vellum markup pg_123 --quote "Q3 dashboard"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The CLI checks the quote is anchorable before posting; pass `--force` to post
|
|
89
|
+
anyway, or `--json` for the raw response.
|
|
90
|
+
|
|
91
|
+
### `vellum whoami`
|
|
92
|
+
|
|
93
|
+
Show the identity the CLI is authenticated as for a server.
|
|
94
|
+
|
|
95
|
+
### `vellum logout`
|
|
96
|
+
|
|
97
|
+
Revoke this machine's stored token server-side and forget it locally.
|
|
98
|
+
|
|
74
99
|
## Development
|
|
75
100
|
|
|
76
101
|
From the repo root, Bun runs the TypeScript source directly (no build):
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ async function clearToken(baseUrl) {
|
|
|
61
61
|
return existing;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// src/commands/
|
|
64
|
+
// src/commands/archive.ts
|
|
65
65
|
import { parseArgs } from "node:util";
|
|
66
66
|
// ../core/src/client.ts
|
|
67
67
|
class VellumApiError extends Error {
|
|
@@ -119,6 +119,30 @@ class VellumClient {
|
|
|
119
119
|
await this.fail(res);
|
|
120
120
|
return await res.json();
|
|
121
121
|
}
|
|
122
|
+
async listPages(opts = {}) {
|
|
123
|
+
const url = new URL(`${this.baseUrl}/v1/pages`);
|
|
124
|
+
if (opts.archived)
|
|
125
|
+
url.searchParams.set("status", "archived");
|
|
126
|
+
else if (opts.all)
|
|
127
|
+
url.searchParams.set("include", "archived");
|
|
128
|
+
const res = await this.fetchImpl(url, { headers: this.authHeaders() });
|
|
129
|
+
if (!res.ok)
|
|
130
|
+
await this.fail(res);
|
|
131
|
+
return await res.json();
|
|
132
|
+
}
|
|
133
|
+
async archivePage(pageId) {
|
|
134
|
+
return this.setArchived(pageId, true);
|
|
135
|
+
}
|
|
136
|
+
async unarchivePage(pageId) {
|
|
137
|
+
return this.setArchived(pageId, false);
|
|
138
|
+
}
|
|
139
|
+
async setArchived(pageId, archived) {
|
|
140
|
+
const action = archived ? "archive" : "unarchive";
|
|
141
|
+
const res = await this.fetchImpl(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}/${action}`, { method: "POST", headers: this.authHeaders() });
|
|
142
|
+
if (!res.ok)
|
|
143
|
+
await this.fail(res);
|
|
144
|
+
return await res.json();
|
|
145
|
+
}
|
|
122
146
|
async createComment(pageId, opts) {
|
|
123
147
|
const res = await this.fetchImpl(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}/comments`, {
|
|
124
148
|
method: "POST",
|
|
@@ -134,21 +158,6 @@ class VellumClient {
|
|
|
134
158
|
await this.fail(res);
|
|
135
159
|
return await res.json();
|
|
136
160
|
}
|
|
137
|
-
async listComments(pageId, opts = {}) {
|
|
138
|
-
const url = new URL(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}/comments`);
|
|
139
|
-
if (opts.versionId)
|
|
140
|
-
url.searchParams.set("version_id", opts.versionId);
|
|
141
|
-
const res = await this.fetchImpl(url, { headers: this.authHeaders() });
|
|
142
|
-
if (!res.ok)
|
|
143
|
-
await this.fail(res);
|
|
144
|
-
return await res.json();
|
|
145
|
-
}
|
|
146
|
-
async resolveComment(commentId) {
|
|
147
|
-
const res = await this.fetchImpl(`${this.baseUrl}/v1/comments/${encodeURIComponent(commentId)}/resolve`, { method: "POST", headers: this.authHeaders() });
|
|
148
|
-
if (!res.ok)
|
|
149
|
-
await this.fail(res);
|
|
150
|
-
return await res.json();
|
|
151
|
-
}
|
|
152
161
|
async startCliAuth() {
|
|
153
162
|
const res = await this.fetchImpl(`${this.baseUrl}/v1/cli/auth/start`, {
|
|
154
163
|
method: "POST",
|
|
@@ -186,143 +195,159 @@ class VellumClient {
|
|
|
186
195
|
await this.fail(res);
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
|
-
// src/commands/
|
|
190
|
-
|
|
198
|
+
// src/commands/archive.ts
|
|
199
|
+
function help(verb) {
|
|
200
|
+
const what = verb === "archive" ? "hide a page from the gallery and make it read-only" : "restore an archived page to live";
|
|
201
|
+
return `vellum ${verb} — ${what}
|
|
191
202
|
|
|
192
203
|
Usage:
|
|
193
|
-
vellum
|
|
204
|
+
vellum ${verb} <page-id> [options]
|
|
194
205
|
|
|
195
|
-
|
|
196
|
-
are
|
|
206
|
+
${verb === "archive" ? `Archiving keeps the bytes, versions, comments, and URLs intact — new
|
|
207
|
+
versions and comments are paused until you unarchive. Owner/admin only.` : `Unarchiving restores the page to the gallery and re-opens it for new
|
|
208
|
+
versions and comments. Owner/admin only.`}
|
|
197
209
|
|
|
198
210
|
Options:
|
|
199
|
-
--open Show only unresolved threads
|
|
200
|
-
--version <id> Only comments anchored to this version id
|
|
201
211
|
--url <url> Server base URL (env: VELLUM_URL)
|
|
212
|
+
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
202
213
|
--json Print the raw JSON response
|
|
203
214
|
-h, --help Show this help`;
|
|
204
|
-
var truncate = (s, n) => s.length > n ? `${s.slice(0, n - 1)}…` : s;
|
|
205
|
-
var oneLine = (s) => s.replace(/\s+/g, " ").trim();
|
|
206
|
-
function printComment(c, indent) {
|
|
207
|
-
const who = c.author_email ?? c.author_name ?? "(unknown)";
|
|
208
|
-
const state = c.resolved ? "resolved" : "open";
|
|
209
|
-
console.log(`${indent}${c.id} [${state}] ${who}`);
|
|
210
|
-
if (c.anchor?.exact) {
|
|
211
|
-
console.log(`${indent} on “${truncate(oneLine(c.anchor.exact), 70)}”`);
|
|
212
|
-
}
|
|
213
|
-
console.log(`${indent} ${truncate(oneLine(c.body), 100)}`);
|
|
214
|
-
for (const reply of c.replies ?? []) {
|
|
215
|
-
printComment(reply, `${indent} `);
|
|
216
|
-
}
|
|
217
215
|
}
|
|
218
|
-
async function
|
|
216
|
+
async function run(verb, argv) {
|
|
219
217
|
const { values, positionals } = parseArgs({
|
|
220
218
|
args: argv,
|
|
221
219
|
allowPositionals: true,
|
|
222
220
|
options: {
|
|
223
|
-
open: { type: "boolean", default: false },
|
|
224
|
-
version: { type: "string" },
|
|
225
221
|
url: { type: "string" },
|
|
222
|
+
"api-key": { type: "string" },
|
|
226
223
|
json: { type: "boolean", default: false },
|
|
227
224
|
help: { type: "boolean", short: "h", default: false }
|
|
228
225
|
}
|
|
229
226
|
});
|
|
230
227
|
if (values.help) {
|
|
231
|
-
console.log(
|
|
228
|
+
console.log(help(verb));
|
|
232
229
|
return 0;
|
|
233
230
|
}
|
|
234
231
|
const pageId = positionals[0];
|
|
235
232
|
if (!pageId) {
|
|
236
233
|
console.error(`Error: missing <page-id>.
|
|
237
234
|
`);
|
|
238
|
-
console.error(
|
|
235
|
+
console.error(help(verb));
|
|
239
236
|
return 1;
|
|
240
237
|
}
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (comments.length === 0) {
|
|
253
|
-
console.log(values.open ? "No open comments." : "No comments.");
|
|
238
|
+
const flags = { url: values.url, apiKey: values["api-key"] };
|
|
239
|
+
const baseUrl = resolveBaseUrl(flags);
|
|
240
|
+
const credential = await resolveCredential(flags, baseUrl);
|
|
241
|
+
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
242
|
+
try {
|
|
243
|
+
const res = verb === "archive" ? await client2.archivePage(pageId) : await client2.unarchivePage(pageId);
|
|
244
|
+
if (values.json) {
|
|
245
|
+
console.log(JSON.stringify(res, null, 2));
|
|
246
|
+
} else {
|
|
247
|
+
console.log(`✓ ${verb}d ${res.id}`);
|
|
248
|
+
}
|
|
254
249
|
return 0;
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (err instanceof VellumApiError && err.status === 401) {
|
|
252
|
+
console.error(`Error: ${verb} requires the owner API key. Set VELLUM_API_KEY or pass --api-key.`);
|
|
253
|
+
return 1;
|
|
254
|
+
}
|
|
255
|
+
if (err instanceof VellumApiError && err.status === 404) {
|
|
256
|
+
console.error(`Error: no page found at ${pageId}.`);
|
|
257
|
+
return 1;
|
|
258
|
+
}
|
|
259
|
+
throw err;
|
|
255
260
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return
|
|
261
|
+
}
|
|
262
|
+
function archiveCommand(argv) {
|
|
263
|
+
return run("archive", argv);
|
|
264
|
+
}
|
|
265
|
+
function unarchiveCommand(argv) {
|
|
266
|
+
return run("unarchive", argv);
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
// src/commands/
|
|
269
|
+
// src/commands/list.ts
|
|
262
270
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
263
|
-
var
|
|
271
|
+
var HELP = `vellum list — list your artifacts (owner/admin only)
|
|
264
272
|
|
|
265
273
|
Usage:
|
|
266
|
-
vellum
|
|
274
|
+
vellum list [options]
|
|
267
275
|
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
By default only live pages are shown. Use --archived for archived-only, or
|
|
277
|
+
--all for both. Listing requires the owner API key (VELLUM_API_KEY / --api-key).
|
|
270
278
|
|
|
271
279
|
Options:
|
|
272
|
-
--
|
|
280
|
+
--archived Show only archived pages
|
|
281
|
+
--all Show both live and archived pages
|
|
273
282
|
--url <url> Server base URL (env: VELLUM_URL)
|
|
283
|
+
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
284
|
+
--json Print the raw JSON response
|
|
274
285
|
-h, --help Show this help`;
|
|
275
|
-
function
|
|
276
|
-
if (
|
|
277
|
-
return
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
286
|
+
function cell(value, width) {
|
|
287
|
+
if (value.length > width)
|
|
288
|
+
return `${value.slice(0, width - 1)}…`;
|
|
289
|
+
return value.padEnd(width);
|
|
290
|
+
}
|
|
291
|
+
function renderTable(pages) {
|
|
292
|
+
const lines = [
|
|
293
|
+
`${cell("PAGE", 26)} ${cell("LATEST", 7)} ${cell("VERS", 5)} ${cell("COMM", 5)} TITLE`
|
|
294
|
+
];
|
|
295
|
+
for (const p of pages) {
|
|
296
|
+
const id = p.archived_at ? `${p.id} *` : p.id;
|
|
297
|
+
const latest = p.latest_version != null ? `v${p.latest_version}` : "—";
|
|
298
|
+
lines.push(`${cell(id, 26)} ${cell(latest, 7)} ${cell(String(p.version_count), 5)} ` + `${cell(String(p.comment_count), 5)} ${p.title ?? ""}`.trimEnd());
|
|
299
|
+
}
|
|
300
|
+
return lines.join(`
|
|
301
|
+
`);
|
|
281
302
|
}
|
|
282
|
-
async function
|
|
283
|
-
const { values
|
|
303
|
+
async function listCommand(argv) {
|
|
304
|
+
const { values } = parseArgs2({
|
|
284
305
|
args: argv,
|
|
285
|
-
allowPositionals:
|
|
306
|
+
allowPositionals: false,
|
|
286
307
|
options: {
|
|
287
|
-
|
|
308
|
+
archived: { type: "boolean", default: false },
|
|
309
|
+
all: { type: "boolean", default: false },
|
|
288
310
|
url: { type: "string" },
|
|
311
|
+
"api-key": { type: "string" },
|
|
312
|
+
json: { type: "boolean", default: false },
|
|
289
313
|
help: { type: "boolean", short: "h", default: false }
|
|
290
314
|
}
|
|
291
315
|
});
|
|
292
316
|
if (values.help) {
|
|
293
|
-
console.log(
|
|
317
|
+
console.log(HELP);
|
|
294
318
|
return 0;
|
|
295
319
|
}
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
320
|
+
const flags = { url: values.url, apiKey: values["api-key"] };
|
|
321
|
+
const baseUrl = resolveBaseUrl(flags);
|
|
322
|
+
const credential = await resolveCredential(flags, baseUrl);
|
|
323
|
+
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
324
|
+
try {
|
|
325
|
+
const { pages } = await client2.listPages({
|
|
326
|
+
archived: values.archived,
|
|
327
|
+
all: values.all
|
|
328
|
+
});
|
|
329
|
+
if (values.json) {
|
|
330
|
+
console.log(JSON.stringify(pages, null, 2));
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
if (pages.length === 0) {
|
|
334
|
+
const scope = values.archived ? "archived " : "";
|
|
335
|
+
console.log(`No ${scope}pages.`);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
console.log(renderTable(pages));
|
|
339
|
+
const archivedCount = pages.filter((p) => p.archived_at).length;
|
|
340
|
+
const suffix = archivedCount ? ` (${archivedCount} archived, marked *)` : "";
|
|
341
|
+
console.log(`
|
|
342
|
+
${pages.length} page${pages.length === 1 ? "" : "s"}${suffix}`);
|
|
343
|
+
return 0;
|
|
344
|
+
} catch (err) {
|
|
345
|
+
if (err instanceof VellumApiError && err.status === 401) {
|
|
346
|
+
console.error("Error: list requires the owner API key. Set VELLUM_API_KEY or pass --api-key.");
|
|
308
347
|
return 1;
|
|
309
348
|
}
|
|
349
|
+
throw err;
|
|
310
350
|
}
|
|
311
|
-
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
312
|
-
const client2 = new VellumClient({ baseUrl });
|
|
313
|
-
const page = await client2.getPage(pageId);
|
|
314
|
-
const target = resolveVersion(page, version);
|
|
315
|
-
if (!target) {
|
|
316
|
-
console.error(version != null ? `Error: page has no version ${version}.` : "Error: page has no versions.");
|
|
317
|
-
return 1;
|
|
318
|
-
}
|
|
319
|
-
const res = await fetch(target.raw_url);
|
|
320
|
-
if (!res.ok) {
|
|
321
|
-
console.error(`Error: could not fetch raw HTML (${res.status}).`);
|
|
322
|
-
return 1;
|
|
323
|
-
}
|
|
324
|
-
process.stdout.write(await res.text());
|
|
325
|
-
return 0;
|
|
326
351
|
}
|
|
327
352
|
|
|
328
353
|
// src/commands/login.ts
|
|
@@ -344,7 +369,7 @@ function openBrowser(url) {
|
|
|
344
369
|
}
|
|
345
370
|
|
|
346
371
|
// src/commands/login.ts
|
|
347
|
-
var
|
|
372
|
+
var HELP2 = `vellum login — authenticate this machine to a Vellum server
|
|
348
373
|
|
|
349
374
|
Opens your browser to approve a CLI login, then stores a token under
|
|
350
375
|
~/.config/vellum/config.json so future commands authenticate as you.
|
|
@@ -367,7 +392,7 @@ async function loginCommand(argv) {
|
|
|
367
392
|
}
|
|
368
393
|
});
|
|
369
394
|
if (values.help) {
|
|
370
|
-
console.log(
|
|
395
|
+
console.log(HELP2);
|
|
371
396
|
return 0;
|
|
372
397
|
}
|
|
373
398
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -408,7 +433,7 @@ To authorize this CLI, visit:
|
|
|
408
433
|
|
|
409
434
|
// src/commands/logout.ts
|
|
410
435
|
import { parseArgs as parseArgs4 } from "node:util";
|
|
411
|
-
var
|
|
436
|
+
var HELP3 = `vellum logout — remove this machine's stored CLI token
|
|
412
437
|
|
|
413
438
|
Usage:
|
|
414
439
|
vellum logout [options]
|
|
@@ -425,7 +450,7 @@ async function logoutCommand(argv) {
|
|
|
425
450
|
}
|
|
426
451
|
});
|
|
427
452
|
if (values.help) {
|
|
428
|
-
console.log(
|
|
453
|
+
console.log(HELP3);
|
|
429
454
|
return 0;
|
|
430
455
|
}
|
|
431
456
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -443,7 +468,7 @@ async function logoutCommand(argv) {
|
|
|
443
468
|
|
|
444
469
|
// src/commands/markup.ts
|
|
445
470
|
import { parseArgs as parseArgs5 } from "node:util";
|
|
446
|
-
var
|
|
471
|
+
var HELP4 = `vellum markup — pin a comment to a passage of a document
|
|
447
472
|
|
|
448
473
|
Usage:
|
|
449
474
|
vellum markup <page-id> --quote "<text>" --body "<note>" [options]
|
|
@@ -468,7 +493,7 @@ async function readStdin() {
|
|
|
468
493
|
chunks.push(chunk);
|
|
469
494
|
return Buffer.concat(chunks).toString("utf8");
|
|
470
495
|
}
|
|
471
|
-
function
|
|
496
|
+
function resolveVersion(page, requested) {
|
|
472
497
|
if (requested != null) {
|
|
473
498
|
return page.versions.find((v) => v.version_number === requested) ?? null;
|
|
474
499
|
}
|
|
@@ -499,14 +524,14 @@ async function markupCommand(argv) {
|
|
|
499
524
|
}
|
|
500
525
|
});
|
|
501
526
|
if (values.help) {
|
|
502
|
-
console.log(
|
|
527
|
+
console.log(HELP4);
|
|
503
528
|
return 0;
|
|
504
529
|
}
|
|
505
530
|
const pageId = positionals[0];
|
|
506
531
|
if (!pageId) {
|
|
507
532
|
console.error(`Error: missing <page-id>.
|
|
508
533
|
`);
|
|
509
|
-
console.error(
|
|
534
|
+
console.error(HELP4);
|
|
510
535
|
return 1;
|
|
511
536
|
}
|
|
512
537
|
const quote = (values.quote ?? "").trim();
|
|
@@ -532,7 +557,7 @@ async function markupCommand(argv) {
|
|
|
532
557
|
const credential = await resolveCredential(flags, baseUrl);
|
|
533
558
|
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
534
559
|
const page = await client2.getPage(pageId);
|
|
535
|
-
const target =
|
|
560
|
+
const target = resolveVersion(page, version);
|
|
536
561
|
if (!target) {
|
|
537
562
|
console.error(version != null ? `Error: page has no version ${version}.` : "Error: page has no versions to mark up.");
|
|
538
563
|
return 1;
|
|
@@ -561,50 +586,10 @@ async function markupCommand(argv) {
|
|
|
561
586
|
return 0;
|
|
562
587
|
}
|
|
563
588
|
|
|
564
|
-
// src/commands/open.ts
|
|
565
|
-
import { parseArgs as parseArgs6 } from "node:util";
|
|
566
|
-
var HELP6 = `vellum open — print (and open) a document's view URL
|
|
567
|
-
|
|
568
|
-
Usage:
|
|
569
|
-
vellum open <page-id> [options]
|
|
570
|
-
|
|
571
|
-
Options:
|
|
572
|
-
--no-browser Just print the URL; don't open the browser
|
|
573
|
-
--url <url> Server base URL (env: VELLUM_URL)
|
|
574
|
-
-h, --help Show this help`;
|
|
575
|
-
async function openCommand(argv) {
|
|
576
|
-
const { values, positionals } = parseArgs6({
|
|
577
|
-
args: argv,
|
|
578
|
-
allowPositionals: true,
|
|
579
|
-
options: {
|
|
580
|
-
"no-browser": { type: "boolean", default: false },
|
|
581
|
-
url: { type: "string" },
|
|
582
|
-
help: { type: "boolean", short: "h", default: false }
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
if (values.help) {
|
|
586
|
-
console.log(HELP6);
|
|
587
|
-
return 0;
|
|
588
|
-
}
|
|
589
|
-
const pageId = positionals[0];
|
|
590
|
-
if (!pageId) {
|
|
591
|
-
console.error(`Error: missing <page-id>.
|
|
592
|
-
`);
|
|
593
|
-
console.error(HELP6);
|
|
594
|
-
return 1;
|
|
595
|
-
}
|
|
596
|
-
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
597
|
-
const page = await new VellumClient({ baseUrl }).getPage(pageId);
|
|
598
|
-
console.log(page.view_url);
|
|
599
|
-
if (!values["no-browser"])
|
|
600
|
-
openBrowser(page.view_url);
|
|
601
|
-
return 0;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
589
|
// src/commands/push.ts
|
|
605
590
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
606
|
-
import { parseArgs as
|
|
607
|
-
var
|
|
591
|
+
import { parseArgs as parseArgs6 } from "node:util";
|
|
592
|
+
var HELP5 = `vellum push — create a Vellum artifact from HTML
|
|
608
593
|
|
|
609
594
|
Usage:
|
|
610
595
|
vellum push <file.html> [options]
|
|
@@ -623,7 +608,7 @@ async function readStdin2() {
|
|
|
623
608
|
return Buffer.concat(chunks).toString("utf8");
|
|
624
609
|
}
|
|
625
610
|
async function pushCommand(argv) {
|
|
626
|
-
const { values, positionals } =
|
|
611
|
+
const { values, positionals } = parseArgs6({
|
|
627
612
|
args: argv,
|
|
628
613
|
allowPositionals: true,
|
|
629
614
|
options: {
|
|
@@ -635,7 +620,7 @@ async function pushCommand(argv) {
|
|
|
635
620
|
}
|
|
636
621
|
});
|
|
637
622
|
if (values.help) {
|
|
638
|
-
console.log(
|
|
623
|
+
console.log(HELP5);
|
|
639
624
|
return 0;
|
|
640
625
|
}
|
|
641
626
|
const file = positionals[0];
|
|
@@ -659,143 +644,9 @@ async function pushCommand(argv) {
|
|
|
659
644
|
return 0;
|
|
660
645
|
}
|
|
661
646
|
|
|
662
|
-
// src/commands/reply.ts
|
|
663
|
-
import { parseArgs as parseArgs8 } from "node:util";
|
|
664
|
-
var HELP8 = `vellum reply — reply to a comment thread
|
|
665
|
-
|
|
666
|
-
Usage:
|
|
667
|
-
vellum reply <page-id> <comment-id> --body "<note>"
|
|
668
|
-
echo "<note>" | vellum reply <page-id> <comment-id>
|
|
669
|
-
|
|
670
|
-
The reply is threaded under <comment-id> and inherits the parent's version.
|
|
671
|
-
Run \`vellum comments <page-id>\` to find the page and comment ids.
|
|
672
|
-
|
|
673
|
-
Options:
|
|
674
|
-
--body <note> Your reply (or pipe it via stdin)
|
|
675
|
-
--url <url> Server base URL (env: VELLUM_URL)
|
|
676
|
-
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
677
|
-
--json Print the raw JSON response
|
|
678
|
-
-h, --help Show this help`;
|
|
679
|
-
async function readStdin3() {
|
|
680
|
-
const chunks = [];
|
|
681
|
-
for await (const chunk of process.stdin)
|
|
682
|
-
chunks.push(chunk);
|
|
683
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
684
|
-
}
|
|
685
|
-
function flatten(comments) {
|
|
686
|
-
const out = [];
|
|
687
|
-
const walk = (c) => {
|
|
688
|
-
out.push(c);
|
|
689
|
-
for (const reply of c.replies ?? [])
|
|
690
|
-
walk(reply);
|
|
691
|
-
};
|
|
692
|
-
for (const c of comments)
|
|
693
|
-
walk(c);
|
|
694
|
-
return out;
|
|
695
|
-
}
|
|
696
|
-
async function replyCommand(argv) {
|
|
697
|
-
const { values, positionals } = parseArgs8({
|
|
698
|
-
args: argv,
|
|
699
|
-
allowPositionals: true,
|
|
700
|
-
options: {
|
|
701
|
-
body: { type: "string" },
|
|
702
|
-
url: { type: "string" },
|
|
703
|
-
"api-key": { type: "string" },
|
|
704
|
-
json: { type: "boolean", default: false },
|
|
705
|
-
help: { type: "boolean", short: "h", default: false }
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
if (values.help) {
|
|
709
|
-
console.log(HELP8);
|
|
710
|
-
return 0;
|
|
711
|
-
}
|
|
712
|
-
const [pageId, commentId] = positionals;
|
|
713
|
-
if (!pageId || !commentId) {
|
|
714
|
-
console.error("Error: usage is `vellum reply <page-id> <comment-id>`.\n");
|
|
715
|
-
console.error(HELP8);
|
|
716
|
-
return 1;
|
|
717
|
-
}
|
|
718
|
-
const body = (values.body ?? await readStdin3()).trim();
|
|
719
|
-
if (!body) {
|
|
720
|
-
console.error("Error: no note provided (pass --body or pipe it via stdin).");
|
|
721
|
-
return 1;
|
|
722
|
-
}
|
|
723
|
-
const flags = { url: values.url, apiKey: values["api-key"] };
|
|
724
|
-
const baseUrl = resolveBaseUrl(flags);
|
|
725
|
-
const credential = await resolveCredential(flags, baseUrl);
|
|
726
|
-
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
727
|
-
const parent = flatten(await client2.listComments(pageId)).find((c) => c.id === commentId);
|
|
728
|
-
if (!parent) {
|
|
729
|
-
console.error(`Error: no comment ${commentId} on page ${pageId}.`);
|
|
730
|
-
return 1;
|
|
731
|
-
}
|
|
732
|
-
if (!parent.version_id) {
|
|
733
|
-
console.error("Error: parent comment has no version to reply on.");
|
|
734
|
-
return 1;
|
|
735
|
-
}
|
|
736
|
-
const reply = await client2.createComment(pageId, {
|
|
737
|
-
versionId: parent.version_id,
|
|
738
|
-
body,
|
|
739
|
-
parentId: commentId
|
|
740
|
-
});
|
|
741
|
-
if (values.json) {
|
|
742
|
-
console.log(JSON.stringify(reply, null, 2));
|
|
743
|
-
} else {
|
|
744
|
-
console.log(`✓ replied to ${commentId}`);
|
|
745
|
-
}
|
|
746
|
-
return 0;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// src/commands/resolve.ts
|
|
750
|
-
import { parseArgs as parseArgs9 } from "node:util";
|
|
751
|
-
var HELP9 = `vellum resolve — mark a comment as resolved
|
|
752
|
-
|
|
753
|
-
Usage:
|
|
754
|
-
vellum resolve <comment-id> [options]
|
|
755
|
-
|
|
756
|
-
Options:
|
|
757
|
-
--url <url> Server base URL (env: VELLUM_URL)
|
|
758
|
-
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
759
|
-
--json Print the raw JSON response
|
|
760
|
-
-h, --help Show this help`;
|
|
761
|
-
async function resolveCommand(argv) {
|
|
762
|
-
const { values, positionals } = parseArgs9({
|
|
763
|
-
args: argv,
|
|
764
|
-
allowPositionals: true,
|
|
765
|
-
options: {
|
|
766
|
-
url: { type: "string" },
|
|
767
|
-
"api-key": { type: "string" },
|
|
768
|
-
json: { type: "boolean", default: false },
|
|
769
|
-
help: { type: "boolean", short: "h", default: false }
|
|
770
|
-
}
|
|
771
|
-
});
|
|
772
|
-
if (values.help) {
|
|
773
|
-
console.log(HELP9);
|
|
774
|
-
return 0;
|
|
775
|
-
}
|
|
776
|
-
const commentId = positionals[0];
|
|
777
|
-
if (!commentId) {
|
|
778
|
-
console.error(`Error: missing <comment-id>.
|
|
779
|
-
`);
|
|
780
|
-
console.error(HELP9);
|
|
781
|
-
return 1;
|
|
782
|
-
}
|
|
783
|
-
const flags = { url: values.url, apiKey: values["api-key"] };
|
|
784
|
-
const baseUrl = resolveBaseUrl(flags);
|
|
785
|
-
const credential = await resolveCredential(flags, baseUrl);
|
|
786
|
-
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
787
|
-
const comment = await client2.resolveComment(commentId);
|
|
788
|
-
if (values.json) {
|
|
789
|
-
console.log(JSON.stringify(comment, null, 2));
|
|
790
|
-
} else {
|
|
791
|
-
console.log(`✓ resolved ${comment.id}`);
|
|
792
|
-
}
|
|
793
|
-
return 0;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
647
|
// src/commands/whoami.ts
|
|
797
|
-
import { parseArgs as
|
|
798
|
-
var
|
|
648
|
+
import { parseArgs as parseArgs7 } from "node:util";
|
|
649
|
+
var HELP6 = `vellum whoami — show the identity the CLI is authenticated as
|
|
799
650
|
|
|
800
651
|
Usage:
|
|
801
652
|
vellum whoami [options]
|
|
@@ -805,7 +656,7 @@ Options:
|
|
|
805
656
|
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
806
657
|
-h, --help Show this help`;
|
|
807
658
|
async function whoamiCommand(argv) {
|
|
808
|
-
const { values } =
|
|
659
|
+
const { values } = parseArgs7({
|
|
809
660
|
args: argv,
|
|
810
661
|
options: {
|
|
811
662
|
url: { type: "string" },
|
|
@@ -814,7 +665,7 @@ async function whoamiCommand(argv) {
|
|
|
814
665
|
}
|
|
815
666
|
});
|
|
816
667
|
if (values.help) {
|
|
817
|
-
console.log(
|
|
668
|
+
console.log(HELP6);
|
|
818
669
|
return 0;
|
|
819
670
|
}
|
|
820
671
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -830,7 +681,7 @@ async function whoamiCommand(argv) {
|
|
|
830
681
|
}
|
|
831
682
|
|
|
832
683
|
// src/index.ts
|
|
833
|
-
var
|
|
684
|
+
var HELP7 = `vellum — CLI for the Vellum artifact store
|
|
834
685
|
|
|
835
686
|
Usage:
|
|
836
687
|
vellum <command> [options]
|
|
@@ -840,12 +691,10 @@ Commands:
|
|
|
840
691
|
logout Remove this machine's stored CLI token
|
|
841
692
|
whoami Show the identity the CLI is authenticated as
|
|
842
693
|
push Create an artifact (page) from HTML (file or stdin)
|
|
843
|
-
|
|
844
|
-
open Print (and open) a document's view URL
|
|
694
|
+
list List your artifacts (owner/admin only)
|
|
845
695
|
markup Pin a comment to a passage of a document
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
resolve Mark a comment as resolved
|
|
696
|
+
archive Archive a page (read-only, hidden from the gallery)
|
|
697
|
+
unarchive Restore an archived page to live
|
|
849
698
|
|
|
850
699
|
Run 'vellum <command> --help' for command-specific options.`;
|
|
851
700
|
var commands = {
|
|
@@ -853,24 +702,22 @@ var commands = {
|
|
|
853
702
|
logout: logoutCommand,
|
|
854
703
|
whoami: whoamiCommand,
|
|
855
704
|
push: pushCommand,
|
|
856
|
-
|
|
857
|
-
open: openCommand,
|
|
705
|
+
list: listCommand,
|
|
858
706
|
markup: markupCommand,
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
resolve: resolveCommand
|
|
707
|
+
archive: archiveCommand,
|
|
708
|
+
unarchive: unarchiveCommand
|
|
862
709
|
};
|
|
863
710
|
async function main() {
|
|
864
711
|
const [, , cmd, ...rest] = process.argv;
|
|
865
712
|
if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
|
|
866
|
-
console.log(
|
|
713
|
+
console.log(HELP7);
|
|
867
714
|
return cmd ? 0 : 1;
|
|
868
715
|
}
|
|
869
716
|
const handler = commands[cmd];
|
|
870
717
|
if (!handler) {
|
|
871
718
|
console.error(`Unknown command: ${cmd}
|
|
872
719
|
`);
|
|
873
|
-
console.error(
|
|
720
|
+
console.error(HELP7);
|
|
874
721
|
return 1;
|
|
875
722
|
}
|
|
876
723
|
return handler(rest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vellum-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "The vellum CLI — publish agent-authored HTML artifacts to a Vellum server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,9 +28,10 @@
|
|
|
28
28
|
"start": "bun run src/index.ts",
|
|
29
29
|
"build": "bun build src/index.ts --target=node --format=esm --outfile=dist/index.js",
|
|
30
30
|
"prepack": "bun run build",
|
|
31
|
+
"release": "bun publish --access public",
|
|
31
32
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@vellum/core": "
|
|
35
|
+
"@vellum/core": "0.1.0"
|
|
35
36
|
}
|
|
36
37
|
}
|