vellum-cli 0.2.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 +353 -22
- 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 {
|
|
@@ -113,6 +113,51 @@ class VellumClient {
|
|
|
113
113
|
await this.fail(res);
|
|
114
114
|
return await res.json();
|
|
115
115
|
}
|
|
116
|
+
async getPage(pageId) {
|
|
117
|
+
const res = await this.fetchImpl(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}`, { headers: this.authHeaders() });
|
|
118
|
+
if (!res.ok)
|
|
119
|
+
await this.fail(res);
|
|
120
|
+
return await res.json();
|
|
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
|
+
}
|
|
146
|
+
async createComment(pageId, opts) {
|
|
147
|
+
const res = await this.fetchImpl(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}/comments`, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json", ...this.authHeaders() },
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
version_id: opts.versionId,
|
|
152
|
+
body: opts.body,
|
|
153
|
+
anchor: opts.anchor,
|
|
154
|
+
parent_id: opts.parentId
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok)
|
|
158
|
+
await this.fail(res);
|
|
159
|
+
return await res.json();
|
|
160
|
+
}
|
|
116
161
|
async startCliAuth() {
|
|
117
162
|
const res = await this.fetchImpl(`${this.baseUrl}/v1/cli/auth/start`, {
|
|
118
163
|
method: "POST",
|
|
@@ -150,6 +195,164 @@ class VellumClient {
|
|
|
150
195
|
await this.fail(res);
|
|
151
196
|
}
|
|
152
197
|
}
|
|
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}
|
|
202
|
+
|
|
203
|
+
Usage:
|
|
204
|
+
vellum ${verb} <page-id> [options]
|
|
205
|
+
|
|
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.`}
|
|
209
|
+
|
|
210
|
+
Options:
|
|
211
|
+
--url <url> Server base URL (env: VELLUM_URL)
|
|
212
|
+
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
213
|
+
--json Print the raw JSON response
|
|
214
|
+
-h, --help Show this help`;
|
|
215
|
+
}
|
|
216
|
+
async function run(verb, argv) {
|
|
217
|
+
const { values, positionals } = parseArgs({
|
|
218
|
+
args: argv,
|
|
219
|
+
allowPositionals: true,
|
|
220
|
+
options: {
|
|
221
|
+
url: { type: "string" },
|
|
222
|
+
"api-key": { type: "string" },
|
|
223
|
+
json: { type: "boolean", default: false },
|
|
224
|
+
help: { type: "boolean", short: "h", default: false }
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
if (values.help) {
|
|
228
|
+
console.log(help(verb));
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
const pageId = positionals[0];
|
|
232
|
+
if (!pageId) {
|
|
233
|
+
console.error(`Error: missing <page-id>.
|
|
234
|
+
`);
|
|
235
|
+
console.error(help(verb));
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
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
|
+
}
|
|
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;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function archiveCommand(argv) {
|
|
263
|
+
return run("archive", argv);
|
|
264
|
+
}
|
|
265
|
+
function unarchiveCommand(argv) {
|
|
266
|
+
return run("unarchive", argv);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/commands/list.ts
|
|
270
|
+
import { parseArgs as parseArgs2 } from "node:util";
|
|
271
|
+
var HELP = `vellum list — list your artifacts (owner/admin only)
|
|
272
|
+
|
|
273
|
+
Usage:
|
|
274
|
+
vellum list [options]
|
|
275
|
+
|
|
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).
|
|
278
|
+
|
|
279
|
+
Options:
|
|
280
|
+
--archived Show only archived pages
|
|
281
|
+
--all Show both live and archived pages
|
|
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
|
|
285
|
+
-h, --help Show this help`;
|
|
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
|
+
`);
|
|
302
|
+
}
|
|
303
|
+
async function listCommand(argv) {
|
|
304
|
+
const { values } = parseArgs2({
|
|
305
|
+
args: argv,
|
|
306
|
+
allowPositionals: false,
|
|
307
|
+
options: {
|
|
308
|
+
archived: { type: "boolean", default: false },
|
|
309
|
+
all: { type: "boolean", default: false },
|
|
310
|
+
url: { type: "string" },
|
|
311
|
+
"api-key": { type: "string" },
|
|
312
|
+
json: { type: "boolean", default: false },
|
|
313
|
+
help: { type: "boolean", short: "h", default: false }
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
if (values.help) {
|
|
317
|
+
console.log(HELP);
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
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.");
|
|
347
|
+
return 1;
|
|
348
|
+
}
|
|
349
|
+
throw err;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/commands/login.ts
|
|
354
|
+
import { parseArgs as parseArgs3 } from "node:util";
|
|
355
|
+
|
|
153
356
|
// src/util/open.ts
|
|
154
357
|
import { spawn } from "node:child_process";
|
|
155
358
|
function openBrowser(url) {
|
|
@@ -166,7 +369,7 @@ function openBrowser(url) {
|
|
|
166
369
|
}
|
|
167
370
|
|
|
168
371
|
// src/commands/login.ts
|
|
169
|
-
var
|
|
372
|
+
var HELP2 = `vellum login — authenticate this machine to a Vellum server
|
|
170
373
|
|
|
171
374
|
Opens your browser to approve a CLI login, then stores a token under
|
|
172
375
|
~/.config/vellum/config.json so future commands authenticate as you.
|
|
@@ -180,7 +383,7 @@ Options:
|
|
|
180
383
|
-h, --help Show this help`;
|
|
181
384
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
182
385
|
async function loginCommand(argv) {
|
|
183
|
-
const { values } =
|
|
386
|
+
const { values } = parseArgs3({
|
|
184
387
|
args: argv,
|
|
185
388
|
options: {
|
|
186
389
|
url: { type: "string" },
|
|
@@ -189,7 +392,7 @@ async function loginCommand(argv) {
|
|
|
189
392
|
}
|
|
190
393
|
});
|
|
191
394
|
if (values.help) {
|
|
192
|
-
console.log(
|
|
395
|
+
console.log(HELP2);
|
|
193
396
|
return 0;
|
|
194
397
|
}
|
|
195
398
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -229,8 +432,8 @@ To authorize this CLI, visit:
|
|
|
229
432
|
}
|
|
230
433
|
|
|
231
434
|
// src/commands/logout.ts
|
|
232
|
-
import { parseArgs as
|
|
233
|
-
var
|
|
435
|
+
import { parseArgs as parseArgs4 } from "node:util";
|
|
436
|
+
var HELP3 = `vellum logout — remove this machine's stored CLI token
|
|
234
437
|
|
|
235
438
|
Usage:
|
|
236
439
|
vellum logout [options]
|
|
@@ -239,7 +442,7 @@ Options:
|
|
|
239
442
|
--url <url> Server base URL (env: VELLUM_URL)
|
|
240
443
|
-h, --help Show this help`;
|
|
241
444
|
async function logoutCommand(argv) {
|
|
242
|
-
const { values } =
|
|
445
|
+
const { values } = parseArgs4({
|
|
243
446
|
args: argv,
|
|
244
447
|
options: {
|
|
245
448
|
url: { type: "string" },
|
|
@@ -247,7 +450,7 @@ async function logoutCommand(argv) {
|
|
|
247
450
|
}
|
|
248
451
|
});
|
|
249
452
|
if (values.help) {
|
|
250
|
-
console.log(
|
|
453
|
+
console.log(HELP3);
|
|
251
454
|
return 0;
|
|
252
455
|
}
|
|
253
456
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -263,10 +466,130 @@ async function logoutCommand(argv) {
|
|
|
263
466
|
return 0;
|
|
264
467
|
}
|
|
265
468
|
|
|
469
|
+
// src/commands/markup.ts
|
|
470
|
+
import { parseArgs as parseArgs5 } from "node:util";
|
|
471
|
+
var HELP4 = `vellum markup — pin a comment to a passage of a document
|
|
472
|
+
|
|
473
|
+
Usage:
|
|
474
|
+
vellum markup <page-id> --quote "<text>" --body "<note>" [options]
|
|
475
|
+
echo "<note>" | vellum markup <page-id> --quote "<text>"
|
|
476
|
+
|
|
477
|
+
The --quote text must appear verbatim in the document; the viewer highlights it
|
|
478
|
+
and links the highlight to your note. Without --version, the page's current
|
|
479
|
+
version is used.
|
|
480
|
+
|
|
481
|
+
Options:
|
|
482
|
+
--quote <text> Passage to mark up (must match the document text)
|
|
483
|
+
--body <note> Your note (or pipe it via stdin)
|
|
484
|
+
--version <n> Pin to a specific version number (default: current)
|
|
485
|
+
--force Post even if the quote isn't found in the version
|
|
486
|
+
--url <url> Server base URL (env: VELLUM_URL)
|
|
487
|
+
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
488
|
+
--json Print the raw JSON response
|
|
489
|
+
-h, --help Show this help`;
|
|
490
|
+
async function readStdin() {
|
|
491
|
+
const chunks = [];
|
|
492
|
+
for await (const chunk of process.stdin)
|
|
493
|
+
chunks.push(chunk);
|
|
494
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
495
|
+
}
|
|
496
|
+
function resolveVersion(page, requested) {
|
|
497
|
+
if (requested != null) {
|
|
498
|
+
return page.versions.find((v) => v.version_number === requested) ?? null;
|
|
499
|
+
}
|
|
500
|
+
const current = page.current_version_id ? page.versions.find((v) => v.id === page.current_version_id) : undefined;
|
|
501
|
+
return current ?? page.versions[page.versions.length - 1] ?? null;
|
|
502
|
+
}
|
|
503
|
+
function extractText(html) {
|
|
504
|
+
return html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
505
|
+
}
|
|
506
|
+
var collapse = (s) => s.replace(/\s+/g, " ").trim();
|
|
507
|
+
function quoteIsAnchorable(html, quote) {
|
|
508
|
+
const text = extractText(html);
|
|
509
|
+
return text.includes(quote) || collapse(text).includes(collapse(quote));
|
|
510
|
+
}
|
|
511
|
+
async function markupCommand(argv) {
|
|
512
|
+
const { values, positionals } = parseArgs5({
|
|
513
|
+
args: argv,
|
|
514
|
+
allowPositionals: true,
|
|
515
|
+
options: {
|
|
516
|
+
quote: { type: "string" },
|
|
517
|
+
body: { type: "string" },
|
|
518
|
+
version: { type: "string" },
|
|
519
|
+
force: { type: "boolean", default: false },
|
|
520
|
+
url: { type: "string" },
|
|
521
|
+
"api-key": { type: "string" },
|
|
522
|
+
json: { type: "boolean", default: false },
|
|
523
|
+
help: { type: "boolean", short: "h", default: false }
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
if (values.help) {
|
|
527
|
+
console.log(HELP4);
|
|
528
|
+
return 0;
|
|
529
|
+
}
|
|
530
|
+
const pageId = positionals[0];
|
|
531
|
+
if (!pageId) {
|
|
532
|
+
console.error(`Error: missing <page-id>.
|
|
533
|
+
`);
|
|
534
|
+
console.error(HELP4);
|
|
535
|
+
return 1;
|
|
536
|
+
}
|
|
537
|
+
const quote = (values.quote ?? "").trim();
|
|
538
|
+
if (!quote) {
|
|
539
|
+
console.error("Error: --quote is required (the passage to mark up).");
|
|
540
|
+
return 1;
|
|
541
|
+
}
|
|
542
|
+
const body = (values.body ?? await readStdin()).trim();
|
|
543
|
+
if (!body) {
|
|
544
|
+
console.error("Error: no note provided (pass --body or pipe it via stdin).");
|
|
545
|
+
return 1;
|
|
546
|
+
}
|
|
547
|
+
let version;
|
|
548
|
+
if (values.version != null) {
|
|
549
|
+
version = Number.parseInt(values.version, 10);
|
|
550
|
+
if (!Number.isInteger(version) || version < 1) {
|
|
551
|
+
console.error("Error: --version must be a positive integer.");
|
|
552
|
+
return 1;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const flags = { url: values.url, apiKey: values["api-key"] };
|
|
556
|
+
const baseUrl = resolveBaseUrl(flags);
|
|
557
|
+
const credential = await resolveCredential(flags, baseUrl);
|
|
558
|
+
const client2 = new VellumClient({ baseUrl, ...credential });
|
|
559
|
+
const page = await client2.getPage(pageId);
|
|
560
|
+
const target = resolveVersion(page, version);
|
|
561
|
+
if (!target) {
|
|
562
|
+
console.error(version != null ? `Error: page has no version ${version}.` : "Error: page has no versions to mark up.");
|
|
563
|
+
return 1;
|
|
564
|
+
}
|
|
565
|
+
if (!values.force) {
|
|
566
|
+
const html = await fetch(target.raw_url).then((r) => r.ok ? r.text() : "");
|
|
567
|
+
if (!quoteIsAnchorable(html, quote)) {
|
|
568
|
+
console.error(`Error: --quote was not found in v${target.version_number}; the highlight
|
|
569
|
+
` + " would not appear. Check the wording, or pass --force to post anyway.");
|
|
570
|
+
return 1;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const anchor = { type: "text-quote", exact: quote };
|
|
574
|
+
const comment = await client2.createComment(pageId, {
|
|
575
|
+
versionId: target.id,
|
|
576
|
+
body,
|
|
577
|
+
anchor
|
|
578
|
+
});
|
|
579
|
+
if (values.json) {
|
|
580
|
+
console.log(JSON.stringify(comment, null, 2));
|
|
581
|
+
} else {
|
|
582
|
+
console.log("✓ markup added");
|
|
583
|
+
console.log(` on: “${quote.length > 80 ? `${quote.slice(0, 79)}…` : quote}”`);
|
|
584
|
+
console.log(` view: ${page.view_url}`);
|
|
585
|
+
}
|
|
586
|
+
return 0;
|
|
587
|
+
}
|
|
588
|
+
|
|
266
589
|
// src/commands/push.ts
|
|
267
590
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
268
|
-
import { parseArgs as
|
|
269
|
-
var
|
|
591
|
+
import { parseArgs as parseArgs6 } from "node:util";
|
|
592
|
+
var HELP5 = `vellum push — create a Vellum artifact from HTML
|
|
270
593
|
|
|
271
594
|
Usage:
|
|
272
595
|
vellum push <file.html> [options]
|
|
@@ -278,14 +601,14 @@ Options:
|
|
|
278
601
|
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
279
602
|
--json Print the raw JSON response
|
|
280
603
|
-h, --help Show this help`;
|
|
281
|
-
async function
|
|
604
|
+
async function readStdin2() {
|
|
282
605
|
const chunks = [];
|
|
283
606
|
for await (const chunk of process.stdin)
|
|
284
607
|
chunks.push(chunk);
|
|
285
608
|
return Buffer.concat(chunks).toString("utf8");
|
|
286
609
|
}
|
|
287
610
|
async function pushCommand(argv) {
|
|
288
|
-
const { values, positionals } =
|
|
611
|
+
const { values, positionals } = parseArgs6({
|
|
289
612
|
args: argv,
|
|
290
613
|
allowPositionals: true,
|
|
291
614
|
options: {
|
|
@@ -297,11 +620,11 @@ async function pushCommand(argv) {
|
|
|
297
620
|
}
|
|
298
621
|
});
|
|
299
622
|
if (values.help) {
|
|
300
|
-
console.log(
|
|
623
|
+
console.log(HELP5);
|
|
301
624
|
return 0;
|
|
302
625
|
}
|
|
303
626
|
const file = positionals[0];
|
|
304
|
-
const html = file ? await readFile2(file, "utf8") : await
|
|
627
|
+
const html = file ? await readFile2(file, "utf8") : await readStdin2();
|
|
305
628
|
if (!html.trim()) {
|
|
306
629
|
console.error("Error: no HTML provided (empty file or stdin).");
|
|
307
630
|
return 1;
|
|
@@ -322,8 +645,8 @@ async function pushCommand(argv) {
|
|
|
322
645
|
}
|
|
323
646
|
|
|
324
647
|
// src/commands/whoami.ts
|
|
325
|
-
import { parseArgs as
|
|
326
|
-
var
|
|
648
|
+
import { parseArgs as parseArgs7 } from "node:util";
|
|
649
|
+
var HELP6 = `vellum whoami — show the identity the CLI is authenticated as
|
|
327
650
|
|
|
328
651
|
Usage:
|
|
329
652
|
vellum whoami [options]
|
|
@@ -333,7 +656,7 @@ Options:
|
|
|
333
656
|
--api-key <key> Shared API key (env: VELLUM_API_KEY)
|
|
334
657
|
-h, --help Show this help`;
|
|
335
658
|
async function whoamiCommand(argv) {
|
|
336
|
-
const { values } =
|
|
659
|
+
const { values } = parseArgs7({
|
|
337
660
|
args: argv,
|
|
338
661
|
options: {
|
|
339
662
|
url: { type: "string" },
|
|
@@ -342,7 +665,7 @@ async function whoamiCommand(argv) {
|
|
|
342
665
|
}
|
|
343
666
|
});
|
|
344
667
|
if (values.help) {
|
|
345
|
-
console.log(
|
|
668
|
+
console.log(HELP6);
|
|
346
669
|
return 0;
|
|
347
670
|
}
|
|
348
671
|
const baseUrl = resolveBaseUrl({ url: values.url });
|
|
@@ -358,7 +681,7 @@ async function whoamiCommand(argv) {
|
|
|
358
681
|
}
|
|
359
682
|
|
|
360
683
|
// src/index.ts
|
|
361
|
-
var
|
|
684
|
+
var HELP7 = `vellum — CLI for the Vellum artifact store
|
|
362
685
|
|
|
363
686
|
Usage:
|
|
364
687
|
vellum <command> [options]
|
|
@@ -368,25 +691,33 @@ Commands:
|
|
|
368
691
|
logout Remove this machine's stored CLI token
|
|
369
692
|
whoami Show the identity the CLI is authenticated as
|
|
370
693
|
push Create an artifact (page) from HTML (file or stdin)
|
|
694
|
+
list List your artifacts (owner/admin only)
|
|
695
|
+
markup Pin a comment to a passage of a document
|
|
696
|
+
archive Archive a page (read-only, hidden from the gallery)
|
|
697
|
+
unarchive Restore an archived page to live
|
|
371
698
|
|
|
372
699
|
Run 'vellum <command> --help' for command-specific options.`;
|
|
373
700
|
var commands = {
|
|
374
701
|
login: loginCommand,
|
|
375
702
|
logout: logoutCommand,
|
|
376
703
|
whoami: whoamiCommand,
|
|
377
|
-
push: pushCommand
|
|
704
|
+
push: pushCommand,
|
|
705
|
+
list: listCommand,
|
|
706
|
+
markup: markupCommand,
|
|
707
|
+
archive: archiveCommand,
|
|
708
|
+
unarchive: unarchiveCommand
|
|
378
709
|
};
|
|
379
710
|
async function main() {
|
|
380
711
|
const [, , cmd, ...rest] = process.argv;
|
|
381
712
|
if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
|
|
382
|
-
console.log(
|
|
713
|
+
console.log(HELP7);
|
|
383
714
|
return cmd ? 0 : 1;
|
|
384
715
|
}
|
|
385
716
|
const handler = commands[cmd];
|
|
386
717
|
if (!handler) {
|
|
387
718
|
console.error(`Unknown command: ${cmd}
|
|
388
719
|
`);
|
|
389
|
-
console.error(
|
|
720
|
+
console.error(HELP7);
|
|
390
721
|
return 1;
|
|
391
722
|
}
|
|
392
723
|
return handler(rest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vellum-cli",
|
|
3
|
-
"version": "0.
|
|
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
|
}
|