@wafflebase/cli 0.6.0 → 0.6.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.
Files changed (2) hide show
  1. package/dist/bin.js +475 -3
  2. package/package.json +3 -3
package/dist/bin.js CHANGED
@@ -263,6 +263,22 @@ var HttpClient = class {
263
263
  deck
264
264
  );
265
265
  }
266
+ // Notes content — same endpoint as docs/slides; the backend dispatches
267
+ // on the persisted document type, picking the note writer for `'note'`.
268
+ // A note's content is a single markdown string (`{ content }`).
269
+ getNoteContent(docId) {
270
+ return this.request(
271
+ "GET",
272
+ `/documents/${docId}/content`
273
+ );
274
+ }
275
+ putNoteContent(docId, note) {
276
+ return this.request(
277
+ "PUT",
278
+ `/documents/${docId}/content`,
279
+ note
280
+ );
281
+ }
266
282
  // Tabs
267
283
  listTabs(docId) {
268
284
  return this.request("GET", `/documents/${docId}/tabs`);
@@ -316,7 +332,7 @@ var HttpClient = class {
316
332
  };
317
333
 
318
334
  // package.json
319
- var version = "0.6.0";
335
+ var version = "0.6.1";
320
336
 
321
337
  // src/commands/root.ts
322
338
  function getGlobalOpts(cmd) {
@@ -99302,6 +99318,366 @@ function registerSlidesImportCommand(slides) {
99302
99318
  });
99303
99319
  }
99304
99320
 
99321
+ // src/commands/notes.ts
99322
+ import { extname as extname8 } from "path";
99323
+
99324
+ // src/notes/import.ts
99325
+ import { readFileSync as readFileSync6 } from "fs";
99326
+ import { basename as basename3, extname as extname7 } from "path";
99327
+ import { createInterface as createInterface3 } from "readline";
99328
+ var defaultNotesImportIO = {
99329
+ stdout: (line) => {
99330
+ process.stdout.write(line);
99331
+ if (!line.endsWith("\n")) process.stdout.write("\n");
99332
+ },
99333
+ stderr: (line) => {
99334
+ console.error(line);
99335
+ },
99336
+ readText: async (path) => {
99337
+ if (path === "-") {
99338
+ return new Promise((resolve, reject) => {
99339
+ const chunks = [];
99340
+ process.stdin.on("data", (c) => chunks.push(c));
99341
+ process.stdin.on(
99342
+ "end",
99343
+ () => resolve(Buffer.concat(chunks).toString("utf-8"))
99344
+ );
99345
+ process.stdin.on("error", reject);
99346
+ });
99347
+ }
99348
+ return readFileSync6(path, "utf-8");
99349
+ },
99350
+ confirm: (prompt) => {
99351
+ return new Promise((resolve) => {
99352
+ const rl3 = createInterface3({
99353
+ input: process.stdin,
99354
+ output: process.stderr
99355
+ });
99356
+ rl3.question(prompt, (answer) => {
99357
+ rl3.close();
99358
+ const a = answer.trim().toLowerCase();
99359
+ resolve(a === "y" || a === "yes");
99360
+ });
99361
+ });
99362
+ },
99363
+ isTTY: process.stdin.isTTY ?? false
99364
+ };
99365
+ async function runNotesImport(args, client, io2 = defaultNotesImportIO) {
99366
+ const { file, replace, yes = false, quiet = false, dryRun = false } = args;
99367
+ const inferredTitle = args.title ?? defaultTitleFor3(file);
99368
+ if (replace) {
99369
+ if (!yes) {
99370
+ if (!io2.isTTY) {
99371
+ io2.stderr(
99372
+ JSON.stringify(
99373
+ {
99374
+ error: {
99375
+ code: "CONFIRMATION_REQ",
99376
+ message: `Pass --yes to confirm replacing note "${replace}".`
99377
+ }
99378
+ },
99379
+ null,
99380
+ 2
99381
+ )
99382
+ );
99383
+ return { exitCode: 1 };
99384
+ }
99385
+ const ok3 = await io2.confirm(
99386
+ `This will replace content of ${replace}. Continue? [y/N] `
99387
+ );
99388
+ if (!ok3) {
99389
+ if (!quiet) io2.stderr("Aborted.");
99390
+ return { exitCode: 0 };
99391
+ }
99392
+ }
99393
+ const content2 = await io2.readText(file);
99394
+ if (dryRun) {
99395
+ io2.stdout(
99396
+ JSON.stringify(
99397
+ {
99398
+ method: "PUT",
99399
+ path: `/documents/${replace}/content`,
99400
+ body: { content: content2 }
99401
+ },
99402
+ null,
99403
+ 2
99404
+ )
99405
+ );
99406
+ return { exitCode: 0 };
99407
+ }
99408
+ const res = await client.putNoteContent(replace, { content: content2 });
99409
+ if (!res.ok) {
99410
+ io2.stderr(
99411
+ JSON.stringify(res.data ?? { error: { code: "HTTP_ERROR" } }, null, 2)
99412
+ );
99413
+ return { exitCode: 1 };
99414
+ }
99415
+ io2.stdout(JSON.stringify({ id: replace, replaced: true }, null, 2));
99416
+ return { exitCode: 0 };
99417
+ }
99418
+ const content = await io2.readText(file);
99419
+ if (dryRun) {
99420
+ io2.stdout(
99421
+ JSON.stringify(
99422
+ {
99423
+ method: "POST",
99424
+ path: "/documents",
99425
+ body: { title: inferredTitle, type: "note" },
99426
+ followUp: { method: "PUT", path: "/documents/<new-id>/content" }
99427
+ },
99428
+ null,
99429
+ 2
99430
+ )
99431
+ );
99432
+ return { exitCode: 0 };
99433
+ }
99434
+ const created = await client.createDocument(inferredTitle, "note");
99435
+ if (!created.ok) {
99436
+ io2.stderr(
99437
+ JSON.stringify(created.data ?? { error: { code: "HTTP_ERROR" } }, null, 2)
99438
+ );
99439
+ return { exitCode: 1 };
99440
+ }
99441
+ const newId = created.data?.id;
99442
+ if (!newId) {
99443
+ io2.stderr(
99444
+ JSON.stringify(
99445
+ {
99446
+ error: {
99447
+ code: "INVALID_RESPONSE",
99448
+ message: "Server did not return an id"
99449
+ }
99450
+ },
99451
+ null,
99452
+ 2
99453
+ )
99454
+ );
99455
+ return { exitCode: 1 };
99456
+ }
99457
+ const put = await client.putNoteContent(newId, { content });
99458
+ if (!put.ok) {
99459
+ io2.stderr(
99460
+ JSON.stringify(put.data ?? { error: { code: "HTTP_ERROR" } }, null, 2)
99461
+ );
99462
+ return { exitCode: 1 };
99463
+ }
99464
+ io2.stdout(JSON.stringify({ id: newId, title: inferredTitle }, null, 2));
99465
+ return { exitCode: 0 };
99466
+ }
99467
+ function defaultTitleFor3(file) {
99468
+ if (file === "-") return "Untitled";
99469
+ return basename3(file, extname7(file)) || "Untitled";
99470
+ }
99471
+
99472
+ // src/notes/content.ts
99473
+ import { writeFileSync as writeFileSync6, existsSync as existsSync5 } from "fs";
99474
+ var VALID_NOTES_CONTENT_FORMATS = ["json", "md", "text"];
99475
+ function parseNotesContentFormat(value) {
99476
+ if (!VALID_NOTES_CONTENT_FORMATS.includes(value)) {
99477
+ throw new Error(
99478
+ `Invalid --format "${value}". Use one of: ${VALID_NOTES_CONTENT_FORMATS.join(", ")}.`
99479
+ );
99480
+ }
99481
+ return value;
99482
+ }
99483
+ var defaultNotesContentIO = {
99484
+ stdout: (text) => {
99485
+ process.stdout.write(text);
99486
+ if (!text.endsWith("\n")) process.stdout.write("\n");
99487
+ },
99488
+ stderr: (line) => {
99489
+ console.error(line);
99490
+ },
99491
+ writeFile: (path, text, force) => {
99492
+ if (existsSync5(path) && !force) {
99493
+ throw new Error(
99494
+ `Refusing to overwrite "${path}". Pass --force to allow overwrite.`
99495
+ );
99496
+ }
99497
+ writeFileSync6(path, text.endsWith("\n") ? text : text + "\n", "utf-8");
99498
+ }
99499
+ };
99500
+ function runNotesContent(args, io2 = defaultNotesContentIO) {
99501
+ const { note, format: format2, out, force = false, quiet = false } = args;
99502
+ const safeNote = note ?? { content: "" };
99503
+ const text = format2 === "json" ? JSON.stringify(safeNote, null, 2) : safeNote.content ?? "";
99504
+ if (!out || out === "-") {
99505
+ io2.stdout(text);
99506
+ return;
99507
+ }
99508
+ io2.writeFile(out, text, force);
99509
+ if (!quiet) io2.stderr(`Wrote to ${out}`);
99510
+ }
99511
+
99512
+ // src/commands/notes.ts
99513
+ function registerNotesCommand(program2) {
99514
+ const notes = program2.command("notes").alias("note").description("Manage markdown notes");
99515
+ notes.command("list").description("List notes in workspace").action(async function() {
99516
+ const opts = getGlobalOpts(this);
99517
+ try {
99518
+ const res = await getClient(opts).listDocuments();
99519
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
99520
+ let data = res.data;
99521
+ if (Array.isArray(data)) {
99522
+ data = data.filter(
99523
+ (d) => d.type === "note"
99524
+ );
99525
+ }
99526
+ output(data, opts.format, opts.quiet);
99527
+ } catch (e) {
99528
+ outputError(e, opts.quiet);
99529
+ }
99530
+ });
99531
+ notes.command("create <title>").description("Create a new note").action(async function(title) {
99532
+ const opts = getGlobalOpts(this);
99533
+ try {
99534
+ if (opts.dryRun) {
99535
+ printDryRun(getConfig(opts), "POST", "/documents", {
99536
+ title,
99537
+ type: "note"
99538
+ });
99539
+ return;
99540
+ }
99541
+ const res = await getClient(opts).createDocument(title, "note");
99542
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
99543
+ output(res.data, opts.format, opts.quiet);
99544
+ } catch (e) {
99545
+ outputError(e, opts.quiet);
99546
+ }
99547
+ });
99548
+ notes.command("get <doc-id>").description("Show note metadata").action(async function(docId) {
99549
+ const opts = getGlobalOpts(this);
99550
+ try {
99551
+ const res = await getClient(opts).getDocument(docId);
99552
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
99553
+ output(res.data, opts.format, opts.quiet);
99554
+ } catch (e) {
99555
+ outputError(e, opts.quiet);
99556
+ }
99557
+ });
99558
+ notes.command("rename <doc-id> <title>").description("Rename a note").action(async function(docId, title) {
99559
+ const opts = getGlobalOpts(this);
99560
+ if (opts.dryRun) {
99561
+ printDryRun(getConfig(opts), "PATCH", `/documents/${docId}`, { title });
99562
+ return;
99563
+ }
99564
+ try {
99565
+ const res = await getClient(opts).updateDocument(docId, title);
99566
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
99567
+ output(res.data, opts.format, opts.quiet);
99568
+ } catch (e) {
99569
+ outputError(e, opts.quiet);
99570
+ }
99571
+ });
99572
+ notes.command("delete <doc-id>").description("Delete a note").action(async function(docId) {
99573
+ const opts = getGlobalOpts(this);
99574
+ if (opts.dryRun) {
99575
+ printDryRun(getConfig(opts), "DELETE", `/documents/${docId}`);
99576
+ return;
99577
+ }
99578
+ try {
99579
+ const res = await getClient(opts).deleteDocument(docId);
99580
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
99581
+ output(res.data, opts.format, opts.quiet);
99582
+ } catch (e) {
99583
+ outputError(e, opts.quiet);
99584
+ }
99585
+ });
99586
+ notes.command("content <doc-id>").description("Read note content as JSON or Markdown").option("--out <file>", "Output file (- for stdout)").option("--force", "Overwrite existing output file", false).action(async function(docId) {
99587
+ const opts = getGlobalOpts(this);
99588
+ const local = this.opts();
99589
+ try {
99590
+ const format2 = parseNotesContentFormat(opts.format);
99591
+ if (opts.dryRun) {
99592
+ printDryRun(getConfig(opts), "GET", `/documents/${docId}/content`);
99593
+ return;
99594
+ }
99595
+ const res = await getClient(opts).getNoteContent(docId);
99596
+ if (!res.ok) {
99597
+ const body = res.data;
99598
+ if (body?.error) {
99599
+ console.error(JSON.stringify(body, null, 2));
99600
+ process.exitCode = 1;
99601
+ return;
99602
+ }
99603
+ throw new Error(`HTTP ${res.status}`);
99604
+ }
99605
+ runNotesContent({
99606
+ note: res.data,
99607
+ format: format2,
99608
+ out: local.out,
99609
+ force: local.force,
99610
+ quiet: opts.quiet
99611
+ });
99612
+ } catch (e) {
99613
+ outputError(e, opts.quiet);
99614
+ }
99615
+ });
99616
+ notes.command("export <doc-id> <file>").description("Export a note to Markdown").option("--force", "Overwrite existing output file", false).action(async function(docId, file) {
99617
+ const opts = getGlobalOpts(this);
99618
+ const local = this.opts();
99619
+ try {
99620
+ const formatSource = this.getOptionValueSourceWithGlobals("format");
99621
+ const fmt = formatSource === "cli" ? opts.format : void 0;
99622
+ if (fmt && fmt !== "md" && fmt !== "markdown") {
99623
+ throw new Error(`Invalid --format "${fmt}". Only "md" is supported.`);
99624
+ }
99625
+ const ext = extname8(file).toLowerCase();
99626
+ if (!fmt && file !== "-" && ext !== ".md" && ext !== ".markdown") {
99627
+ throw new Error(
99628
+ `Cannot infer format from "${file}". Use a .md extension or --format md.`
99629
+ );
99630
+ }
99631
+ if (opts.dryRun) {
99632
+ printDryRun(getConfig(opts), "GET", `/documents/${docId}/content`);
99633
+ return;
99634
+ }
99635
+ const res = await getClient(opts).getNoteContent(docId);
99636
+ if (!res.ok) {
99637
+ const body = res.data;
99638
+ if (body?.error) {
99639
+ console.error(JSON.stringify(body, null, 2));
99640
+ process.exitCode = 1;
99641
+ return;
99642
+ }
99643
+ throw new Error(`HTTP ${res.status}`);
99644
+ }
99645
+ runNotesContent({
99646
+ note: res.data,
99647
+ format: "md",
99648
+ out: file,
99649
+ force: local.force,
99650
+ quiet: opts.quiet
99651
+ });
99652
+ } catch (e) {
99653
+ outputError(e, opts.quiet);
99654
+ }
99655
+ });
99656
+ registerNotesImportCommand(notes);
99657
+ }
99658
+ function registerNotesImportCommand(notes) {
99659
+ notes.command("import <file>").description("Import a Markdown file as a new (or replacement) note").option("--title <title>", "Note title (default: file basename)").option("--replace <doc-id>", "Replace content of an existing note").option("--yes", "Skip --replace confirmation prompt", false).action(async function(file) {
99660
+ const opts = getGlobalOpts(this);
99661
+ const local = this.opts();
99662
+ try {
99663
+ const result = await runNotesImport(
99664
+ {
99665
+ file,
99666
+ title: local.title,
99667
+ replace: local.replace,
99668
+ yes: local.yes,
99669
+ quiet: opts.quiet,
99670
+ dryRun: opts.dryRun
99671
+ },
99672
+ getClient(opts)
99673
+ );
99674
+ if (result.exitCode !== 0) process.exitCode = result.exitCode;
99675
+ } catch (e) {
99676
+ outputError(e, opts.quiet);
99677
+ }
99678
+ });
99679
+ }
99680
+
99305
99681
  // src/commands/api-keys.ts
99306
99682
  function registerApiKeysCommand(program2) {
99307
99683
  const apiKey = program2.command("api-keys").alias("api-key").description("Manage API keys");
@@ -99577,6 +99953,101 @@ var registry = [
99577
99953
  response: { type: "binary", description: "PPTX bytes" },
99578
99954
  aliases: ["slide.export", "deck.export", "decks.export"]
99579
99955
  },
99956
+ // Notes (markdown) namespace
99957
+ {
99958
+ name: "notes.list",
99959
+ description: "List notes in workspace",
99960
+ safety: "read-only",
99961
+ parameters: {},
99962
+ response: { type: "array", items: { id: "string", title: "string", type: "string", createdAt: "string" } },
99963
+ aliases: ["note.list"]
99964
+ },
99965
+ {
99966
+ name: "notes.create",
99967
+ description: "Create a new note",
99968
+ safety: "write",
99969
+ parameters: {
99970
+ title: { type: "string", required: true, description: "Note title" }
99971
+ },
99972
+ response: { id: "string", title: "string", type: "string" },
99973
+ aliases: ["note.create"]
99974
+ },
99975
+ {
99976
+ name: "notes.get",
99977
+ description: "Show note metadata",
99978
+ safety: "read-only",
99979
+ parameters: {
99980
+ "doc-id": { type: "string", required: true, description: "Document ID" }
99981
+ },
99982
+ response: { id: "string", title: "string", type: "string", createdAt: "string" },
99983
+ aliases: ["note.get"]
99984
+ },
99985
+ {
99986
+ name: "notes.rename",
99987
+ description: "Rename a note",
99988
+ safety: "write",
99989
+ parameters: {
99990
+ "doc-id": { type: "string", required: true, description: "Document ID" },
99991
+ title: { type: "string", required: true, description: "New title" }
99992
+ },
99993
+ response: { id: "string", title: "string" },
99994
+ aliases: ["note.rename"]
99995
+ },
99996
+ {
99997
+ name: "notes.delete",
99998
+ description: "Delete a note",
99999
+ safety: "destructive",
100000
+ parameters: {
100001
+ "doc-id": { type: "string", required: true, description: "Document ID" }
100002
+ },
100003
+ response: { id: "string" },
100004
+ aliases: ["note.delete"]
100005
+ },
100006
+ {
100007
+ name: "notes.content",
100008
+ description: "Read note content as JSON or Markdown",
100009
+ safety: "read-only",
100010
+ parameters: {
100011
+ "doc-id": { type: "string", required: true, description: "Document ID" },
100012
+ "--format": { type: "string", required: false, description: "Output format (json|md|text)", default: "json" },
100013
+ "--out": { type: "string", required: false, description: "Output file (- for stdout)" },
100014
+ "--force": { type: "boolean", required: false, description: "Overwrite existing output file", default: "false" }
100015
+ },
100016
+ response: { type: "object", description: "Note JSON ({content}) or raw Markdown per --format" },
100017
+ aliases: ["note.content"]
100018
+ },
100019
+ {
100020
+ name: "notes.import",
100021
+ description: "Import a Markdown file as a new (or replacement) note",
100022
+ // Default safety is `write` (create new note); `--replace` flips it to
100023
+ // destructive, mirroring `docs.import` / `slides.import`.
100024
+ safety: "write",
100025
+ parameters: {
100026
+ file: { type: "string", required: true, description: "Source .md path or - for stdin" },
100027
+ "--title": { type: "string", required: false, description: "Note title (default: file basename)" },
100028
+ "--replace": { type: "string", required: false, description: "Existing note ID to replace" },
100029
+ "--yes": { type: "boolean", required: false, description: "Skip --replace confirmation", default: "false" }
100030
+ },
100031
+ response: { id: "string", title: "string", replaced: "boolean" },
100032
+ variants: [
100033
+ { when: "default", safety: "write", creates: "new note" },
100034
+ { when: "--replace given", safety: "destructive", modifies: "existing note content" }
100035
+ ],
100036
+ aliases: ["note.import"]
100037
+ },
100038
+ {
100039
+ name: "notes.export",
100040
+ description: "Export a note to Markdown",
100041
+ safety: "read-only",
100042
+ parameters: {
100043
+ "doc-id": { type: "string", required: true, description: "Document ID" },
100044
+ file: { type: "string", required: true, description: "Output path or - for stdout" },
100045
+ "--format": { type: "string", required: false, description: "Output format (md); default from extension" },
100046
+ "--force": { type: "boolean", required: false, description: "Overwrite existing output file", default: "false" }
100047
+ },
100048
+ response: { type: "string", description: "Markdown text" },
100049
+ aliases: ["note.export"]
100050
+ },
99580
100051
  // Sheets namespace — canonical names live under sheets.*
99581
100052
  {
99582
100053
  name: "sheets.tabs.list",
@@ -99738,7 +100209,7 @@ function registerSchemaCommand(program2) {
99738
100209
 
99739
100210
  // src/commands/login.ts
99740
100211
  import { createServer } from "http";
99741
- import { createInterface as createInterface3 } from "readline";
100212
+ import { createInterface as createInterface4 } from "readline";
99742
100213
  function registerLoginCommand(program2) {
99743
100214
  program2.command("login").description("Log in via GitHub OAuth in the browser").action(async function() {
99744
100215
  const parentOpts = this.optsWithGlobals();
@@ -99832,7 +100303,7 @@ function registerLoginCommand(program2) {
99832
100303
  });
99833
100304
  }
99834
100305
  function ask(prompt) {
99835
- const rl3 = createInterface3({ input: process.stdin, output: process.stderr });
100306
+ const rl3 = createInterface4({ input: process.stdin, output: process.stderr });
99836
100307
  return new Promise((resolve) => {
99837
100308
  rl3.question(prompt, (answer) => {
99838
100309
  rl3.close();
@@ -100032,6 +100503,7 @@ registerCtxCommand(program);
100032
100503
  registerDocsCommand(program);
100033
100504
  registerSheetsCommand(program);
100034
100505
  registerSlidesCommand(program);
100506
+ registerNotesCommand(program);
100035
100507
  registerApiKeysCommand(program);
100036
100508
  registerSchemaCommand(program);
100037
100509
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wafflebase/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI for Wafflebase spreadsheet API",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -31,8 +31,8 @@
31
31
  "tsx": "^4.19.0",
32
32
  "typescript": "^5.9.3",
33
33
  "vitest": "^4.1.8",
34
- "@wafflebase/slides": "0.6.0",
35
- "@wafflebase/docs": "0.6.0"
34
+ "@wafflebase/docs": "0.6.1",
35
+ "@wafflebase/slides": "0.6.1"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup",