vellum-cli 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +31 -12
  2. package/dist/index.js +26 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,14 +8,16 @@ Vellum server.
8
8
  ```bash
9
9
  # one-off, no install
10
10
  npx vellum-cli push --help
11
+ bunx vellum-cli push --help # or with Bun
11
12
 
12
13
  # or install the `vellum` command globally
13
- npm i -g vellum-cli
14
+ npm i -g vellum-cli # npm
15
+ bun add -g vellum-cli # Bun
14
16
  vellum push --help
15
17
  ```
16
18
 
17
- Requires Node 18+. The package is self-contained (`@vellum/core` is bundled in),
18
- so it has no runtime dependencies.
19
+ Requires Node 18+ (or Bun). The package is self-contained (`@vellum/core` is
20
+ bundled in), so it has no runtime dependencies.
19
21
 
20
22
  ## Authentication
21
23
 
@@ -39,11 +41,9 @@ env vars:
39
41
  | Flag | Env var | Description |
40
42
  | ------------ | ---------------- | -------------------------------------------- |
41
43
  | `--url` | `VELLUM_URL` | Server base URL (default `https://vellumai.app`) |
42
- | `--api-key` | `VELLUM_API_KEY` | Shared owner/agent key (`X-Api-Key`) |
43
44
 
44
- Credential precedence for a write: an explicit `--api-key`/`VELLUM_API_KEY`
45
- (the shared owner key, e.g. for agents/CI) wins; otherwise the stored
46
- `vellum login` token is used.
45
+ The CLI always uses the stored `vellum login` token. Commands are scoped to
46
+ the logged-in page owner; the CLI does not accept the shared server API key.
47
47
 
48
48
  ## Commands
49
49
 
@@ -55,7 +55,7 @@ then stores the token. `--no-browser` just prints the URL to open manually.
55
55
  ### `vellum push [file]`
56
56
 
57
57
  Create an artifact from HTML. Reads from `<file>` if given, otherwise from
58
- stdin. Pass `--page-id <id>` to append a new version to an existing page.
58
+ stdin. Pass `--page-id <PAGE_ID>` to append a new version to an existing page.
59
59
 
60
60
  ```bash
61
61
  # new page from a file
@@ -65,11 +65,19 @@ vellum push artifact.html
65
65
  echo '<!doctype html><h1>Hello</h1>' | vellum push
66
66
 
67
67
  # add a version to an existing page, print raw JSON
68
- vellum push artifact.html --page-id pg_123 --json
68
+ vellum push artifact.html --page-id <PAGE_ID> --json
69
69
  ```
70
70
 
71
71
  On success it prints the new version number, the human view URL, and the raw
72
- URL. With `--json` it prints the server's `CreateVersionResponse` verbatim.
72
+ URL. With `--json` it prints the server's `CreateVersionResponse` verbatim;
73
+ that response's `id` is the version id. The page id is the `pg_...` value in
74
+ the view URL (`/p/<PAGE_ID>`) or from `vellum list`.
75
+
76
+ ### `vellum list`
77
+
78
+ List the artifacts owned by your logged-in identity. By default only live pages
79
+ are shown; pass `--archived` for archived-only or `--all` for both (archived
80
+ pages are marked `*`). `--json` prints the raw response.
73
81
 
74
82
  ### `vellum markup <page-id>`
75
83
 
@@ -79,15 +87,22 @@ the highlight. Without `--version`, the page's current version is used.
79
87
 
80
88
  ```bash
81
89
  # anchor a note to a passage
82
- vellum markup pg_123 --quote "Q3 dashboard" --body "tighten this headline"
90
+ vellum markup <PAGE_ID> --quote "Q3 dashboard" --body "tighten this headline"
83
91
 
84
92
  # pipe the note in on stdin instead of --body
85
- echo "tighten this headline" | vellum markup pg_123 --quote "Q3 dashboard"
93
+ echo "tighten this headline" | vellum markup <PAGE_ID> --quote "Q3 dashboard"
86
94
  ```
87
95
 
88
96
  The CLI checks the quote is anchorable before posting; pass `--force` to post
89
97
  anyway, or `--json` for the raw response.
90
98
 
99
+ ### `vellum archive <page-id>` / `vellum unarchive <page-id>`
100
+
101
+ Archive hides a page from the gallery and makes it read-only; the bytes,
102
+ versions, comments, and URLs stay intact, but new versions and comments are
103
+ paused until you unarchive. Unarchive restores it to live. Page owner only.
104
+ `--json` prints the raw response.
105
+
91
106
  ### `vellum whoami`
92
107
 
93
108
  Show the identity the CLI is authenticated as for a server.
@@ -96,6 +111,10 @@ Show the identity the CLI is authenticated as for a server.
96
111
 
97
112
  Revoke this machine's stored token server-side and forget it locally.
98
113
 
114
+ ### `vellum version`
115
+
116
+ Print the installed CLI version.
117
+
99
118
  ## Development
100
119
 
101
120
  From the repo root, Bun runs the TypeScript source directly (no build):
package/dist/index.js CHANGED
@@ -15,13 +15,10 @@ function resolveBaseUrl(flags) {
15
15
  return normalizeUrl(flags.url ?? process.env.VELLUM_URL ?? DEFAULT_URL);
16
16
  }
17
17
  async function resolveCredential(flags, baseUrl) {
18
- const apiKey = flags.apiKey ?? process.env.VELLUM_API_KEY;
19
- if (apiKey)
20
- return { apiKey };
21
18
  const token = await loadToken(baseUrl);
22
19
  if (token)
23
20
  return { token };
24
- throw new ConfigError(`Not logged in to ${baseUrl}. Run \`vellum login\` (or set VELLUM_API_KEY / pass --api-key).`);
21
+ throw new ConfigError(`Not logged in to ${baseUrl}. Run \`vellum login\`.`);
25
22
  }
26
23
  function storePath() {
27
24
  const xdg = process.env.XDG_CONFIG_HOME;
@@ -111,7 +108,8 @@ class VellumClient {
111
108
  });
112
109
  if (!res.ok)
113
110
  await this.fail(res);
114
- return await res.json();
111
+ const body = await res.json();
112
+ return { ...body, created: res.status === 201 };
115
113
  }
116
114
  async getPage(pageId) {
117
115
  const res = await this.fetchImpl(`${this.baseUrl}/v1/pages/${encodeURIComponent(pageId)}`, { headers: this.authHeaders() });
@@ -204,12 +202,11 @@ Usage:
204
202
  vellum ${verb} <page-id> [options]
205
203
 
206
204
  ${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.`}
205
+ versions and comments are paused until you unarchive. Page owner only.` : `Unarchiving restores the page to the gallery and re-opens it for new
206
+ versions and comments. Page owner only.`}
209
207
 
210
208
  Options:
211
209
  --url <url> Server base URL (env: VELLUM_URL)
212
- --api-key <key> Shared API key (env: VELLUM_API_KEY)
213
210
  --json Print the raw JSON response
214
211
  -h, --help Show this help`;
215
212
  }
@@ -219,7 +216,6 @@ async function run(verb, argv) {
219
216
  allowPositionals: true,
220
217
  options: {
221
218
  url: { type: "string" },
222
- "api-key": { type: "string" },
223
219
  json: { type: "boolean", default: false },
224
220
  help: { type: "boolean", short: "h", default: false }
225
221
  }
@@ -235,7 +231,7 @@ async function run(verb, argv) {
235
231
  console.error(help(verb));
236
232
  return 1;
237
233
  }
238
- const flags = { url: values.url, apiKey: values["api-key"] };
234
+ const flags = { url: values.url };
239
235
  const baseUrl = resolveBaseUrl(flags);
240
236
  const credential = await resolveCredential(flags, baseUrl);
241
237
  const client2 = new VellumClient({ baseUrl, ...credential });
@@ -249,7 +245,11 @@ async function run(verb, argv) {
249
245
  return 0;
250
246
  } catch (err) {
251
247
  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.`);
248
+ console.error(`Error: not logged in. Run \`vellum login\` as the page owner.`);
249
+ return 1;
250
+ }
251
+ if (err instanceof VellumApiError && err.status === 403) {
252
+ console.error(`Error: you don't own ${pageId}, so you can't ${verb} it.`);
253
253
  return 1;
254
254
  }
255
255
  if (err instanceof VellumApiError && err.status === 404) {
@@ -273,15 +273,13 @@ var HELP = `vellum list — list your artifacts
273
273
  Usage:
274
274
  vellum list [options]
275
275
 
276
- Lists the pages you own (log in with \`vellum login\`). With the owner API key
277
- (VELLUM_API_KEY / --api-key) it lists every page. By default only live pages are
278
- shown; use --archived for archived-only, or --all for both.
276
+ Lists the pages you own (log in with \`vellum login\`). By default only live
277
+ pages are shown; use --archived for archived-only, or --all for both.
279
278
 
280
279
  Options:
281
280
  --archived Show only archived pages
282
281
  --all Show both live and archived pages
283
282
  --url <url> Server base URL (env: VELLUM_URL)
284
- --api-key <key> Shared API key (env: VELLUM_API_KEY)
285
283
  --json Print the raw JSON response
286
284
  -h, --help Show this help`;
287
285
  function cell(value, width) {
@@ -309,7 +307,6 @@ async function listCommand(argv) {
309
307
  archived: { type: "boolean", default: false },
310
308
  all: { type: "boolean", default: false },
311
309
  url: { type: "string" },
312
- "api-key": { type: "string" },
313
310
  json: { type: "boolean", default: false },
314
311
  help: { type: "boolean", short: "h", default: false }
315
312
  }
@@ -318,7 +315,7 @@ async function listCommand(argv) {
318
315
  console.log(HELP);
319
316
  return 0;
320
317
  }
321
- const flags = { url: values.url, apiKey: values["api-key"] };
318
+ const flags = { url: values.url };
322
319
  const baseUrl = resolveBaseUrl(flags);
323
320
  const credential = await resolveCredential(flags, baseUrl);
324
321
  const client2 = new VellumClient({ baseUrl, ...credential });
@@ -344,7 +341,7 @@ ${pages.length} page${pages.length === 1 ? "" : "s"}${suffix}`);
344
341
  return 0;
345
342
  } catch (err) {
346
343
  if (err instanceof VellumApiError && err.status === 401) {
347
- console.error("Error: not authenticated. Run `vellum login` (or set VELLUM_API_KEY / pass --api-key).");
344
+ console.error("Error: not authenticated. Run `vellum login` as the page owner.");
348
345
  return 1;
349
346
  }
350
347
  throw err;
@@ -485,7 +482,6 @@ Options:
485
482
  --version <n> Pin to a specific version number (default: current)
486
483
  --force Post even if the quote isn't found in the version
487
484
  --url <url> Server base URL (env: VELLUM_URL)
488
- --api-key <key> Shared API key (env: VELLUM_API_KEY)
489
485
  --json Print the raw JSON response
490
486
  -h, --help Show this help`;
491
487
  async function readStdin() {
@@ -519,7 +515,6 @@ async function markupCommand(argv) {
519
515
  version: { type: "string" },
520
516
  force: { type: "boolean", default: false },
521
517
  url: { type: "string" },
522
- "api-key": { type: "string" },
523
518
  json: { type: "boolean", default: false },
524
519
  help: { type: "boolean", short: "h", default: false }
525
520
  }
@@ -553,7 +548,7 @@ async function markupCommand(argv) {
553
548
  return 1;
554
549
  }
555
550
  }
556
- const flags = { url: values.url, apiKey: values["api-key"] };
551
+ const flags = { url: values.url };
557
552
  const baseUrl = resolveBaseUrl(flags);
558
553
  const credential = await resolveCredential(flags, baseUrl);
559
554
  const client2 = new VellumClient({ baseUrl, ...credential });
@@ -590,6 +585,10 @@ async function markupCommand(argv) {
590
585
  // src/commands/push.ts
591
586
  import { readFile as readFile2 } from "node:fs/promises";
592
587
  import { parseArgs as parseArgs6 } from "node:util";
588
+ function formatPush(res) {
589
+ const head = res.created ? `✓ v${res.version_number} created` : `↺ identical to v${res.version_number} — no new version`;
590
+ return [head, ` view: ${res.view_url}`, ` raw: ${res.raw_url}`];
591
+ }
593
592
  var HELP5 = `vellum push — create a Vellum artifact from HTML
594
593
 
595
594
  Usage:
@@ -599,7 +598,6 @@ Usage:
599
598
  Options:
600
599
  --page-id <id> Append a new version to an existing page
601
600
  --url <url> Server base URL (env: VELLUM_URL)
602
- --api-key <key> Shared API key (env: VELLUM_API_KEY)
603
601
  --json Print the raw JSON response
604
602
  -h, --help Show this help`;
605
603
  async function readStdin2() {
@@ -615,7 +613,6 @@ async function pushCommand(argv) {
615
613
  options: {
616
614
  "page-id": { type: "string" },
617
615
  url: { type: "string" },
618
- "api-key": { type: "string" },
619
616
  json: { type: "boolean", default: false },
620
617
  help: { type: "boolean", short: "h", default: false }
621
618
  }
@@ -630,7 +627,7 @@ async function pushCommand(argv) {
630
627
  console.error("Error: no HTML provided (empty file or stdin).");
631
628
  return 1;
632
629
  }
633
- const flags = { url: values.url, apiKey: values["api-key"] };
630
+ const flags = { url: values.url };
634
631
  const baseUrl = resolveBaseUrl(flags);
635
632
  const credential = await resolveCredential(flags, baseUrl);
636
633
  const client2 = new VellumClient({ baseUrl, ...credential });
@@ -638,16 +635,15 @@ async function pushCommand(argv) {
638
635
  if (values.json) {
639
636
  console.log(JSON.stringify(res, null, 2));
640
637
  } else {
641
- console.log(`✓ v${res.version_number} created`);
642
- console.log(` view: ${res.view_url}`);
643
- console.log(` raw: ${res.raw_url}`);
638
+ for (const line of formatPush(res))
639
+ console.log(line);
644
640
  }
645
641
  return 0;
646
642
  }
647
643
  // package.json
648
644
  var package_default = {
649
645
  name: "vellum-cli",
650
- version: "0.3.3",
646
+ version: "0.3.5",
651
647
  description: "The vellum CLI — publish agent-authored HTML artifacts to a Vellum server.",
652
648
  type: "module",
653
649
  license: "MIT",
@@ -701,14 +697,12 @@ Usage:
701
697
 
702
698
  Options:
703
699
  --url <url> Server base URL (env: VELLUM_URL)
704
- --api-key <key> Shared API key (env: VELLUM_API_KEY)
705
700
  -h, --help Show this help`;
706
701
  async function whoamiCommand(argv) {
707
702
  const { values } = parseArgs7({
708
703
  args: argv,
709
704
  options: {
710
705
  url: { type: "string" },
711
- "api-key": { type: "string" },
712
706
  help: { type: "boolean", short: "h", default: false }
713
707
  }
714
708
  });
@@ -717,11 +711,7 @@ async function whoamiCommand(argv) {
717
711
  return 0;
718
712
  }
719
713
  const baseUrl = resolveBaseUrl({ url: values.url });
720
- const credential = await resolveCredential({ url: values.url, apiKey: values["api-key"] }, baseUrl);
721
- if ("apiKey" in credential) {
722
- console.log(`Authenticated to ${baseUrl} with a shared API key (owner).`);
723
- return 0;
724
- }
714
+ const credential = await resolveCredential({ url: values.url }, baseUrl);
725
715
  const client2 = new VellumClient({ baseUrl, token: credential.token });
726
716
  const { email } = await client2.whoami();
727
717
  console.log(`${email ?? "(no email on record)"} — ${baseUrl}`);
@@ -739,7 +729,7 @@ Commands:
739
729
  logout Remove this machine's stored CLI token
740
730
  whoami Show the identity the CLI is authenticated as
741
731
  push Create an artifact (page) from HTML (file or stdin)
742
- list List your artifacts (owner/admin only)
732
+ list List artifacts owned by your logged-in identity
743
733
  markup Pin a comment to a passage of a document
744
734
  archive Archive a page (read-only, hidden from the gallery)
745
735
  unarchive Restore an archived page to live
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vellum-cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "The vellum CLI — publish agent-authored HTML artifacts to a Vellum server.",
5
5
  "type": "module",
6
6
  "license": "MIT",