@creator-notes/cnotes 0.33.1 → 0.35.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.
@@ -1 +1 @@
1
- {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAkG5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAm3E7D"}
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AA8G5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4hF7D"}
@@ -11,6 +11,17 @@ import { readCanvasMarkdown, renderCanvasMarkdown } from "../lib/canvas-read.js"
11
11
  import { resolveAgentSession } from "../lib/claude-session.js";
12
12
  import { WARN, dim } from "../lib/style.js";
13
13
  import { canvasUrl, canvasLinkForRef } from "../lib/urls.js";
14
+ /**
15
+ * Path for a `/api/canvas/<id>` action call. ALWAYS carries `?workspaceId=`:
16
+ * CLI Bearer auth needs it to resolve a CANVAS-<n> display id, and the
17
+ * placeLayout/arrangeLayout actions reject the request outright without it
18
+ * (server's resolveCanvasForAuth). Build every canvas action URL through this
19
+ * helper — a call site that hand-rolls the path silently loses display-id
20
+ * support at best and 400s at worst (the add-section bug).
21
+ */
22
+ function canvasActionPath(canvasId, workspaceId) {
23
+ return `/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(workspaceId)}`;
24
+ }
14
25
  /**
15
26
  * A list item is a *collection of member notes* with a short markdown description
16
27
  * that frames them — not a place to dump prose. When a description carries several
@@ -250,7 +261,7 @@ Examples:
250
261
  const cfg = resolveConfig(program.opts());
251
262
  const api = new ApiClient(cfg.serverUrl, cfg.token);
252
263
  try {
253
- const data = await api.get(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`);
264
+ const data = await api.get(canvasActionPath(canvasId, cfg.workspaceId));
254
265
  if (cfg.json) {
255
266
  outputJson(data);
256
267
  return;
@@ -540,7 +551,7 @@ Examples:
540
551
  const cfg = resolveConfig(program.opts());
541
552
  const api = new ApiClient(cfg.serverUrl, cfg.token);
542
553
  try {
543
- const data = await api.delete(`/api/canvas/${canvasId}`);
554
+ const data = await api.delete(canvasActionPath(canvasId, cfg.workspaceId));
544
555
  if (cfg.json) {
545
556
  outputJson(data);
546
557
  }
@@ -560,7 +571,7 @@ Examples:
560
571
  const cfg = resolveConfig(program.opts());
561
572
  const api = new ApiClient(cfg.serverUrl, cfg.token);
562
573
  try {
563
- const data = await api.post(`/api/canvas/${canvasId}`, {
574
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
564
575
  action: "setAsHome",
565
576
  });
566
577
  if (cfg.json) {
@@ -582,7 +593,7 @@ Examples:
582
593
  const cfg = resolveConfig(program.opts());
583
594
  const api = new ApiClient(cfg.serverUrl, cfg.token);
584
595
  try {
585
- const data = await api.post(`/api/canvas/${canvasId}`, {
596
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
586
597
  action: "archive",
587
598
  });
588
599
  if (cfg.json) {
@@ -604,7 +615,7 @@ Examples:
604
615
  const cfg = resolveConfig(program.opts());
605
616
  const api = new ApiClient(cfg.serverUrl, cfg.token);
606
617
  try {
607
- const data = await api.post(`/api/canvas/${canvasId}`, {
618
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
608
619
  action: "unarchive",
609
620
  });
610
621
  if (cfg.json) {
@@ -633,7 +644,7 @@ Examples:
633
644
  const cfg = resolveConfig(program.opts());
634
645
  const api = new ApiClient(cfg.serverUrl, cfg.token);
635
646
  try {
636
- const data = await api.post(`/api/canvas/${canvasId}`, {
647
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
637
648
  action: "addNode",
638
649
  noteId: opts.note,
639
650
  positionX: parseInt(opts.x, 10),
@@ -704,7 +715,7 @@ Examples:
704
715
  colorHex = opts.color.startsWith("#") ? opts.color : `#${opts.color}`;
705
716
  }
706
717
  try {
707
- const data = await api.post(`/api/canvas/${canvasId}`, {
718
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
708
719
  action: "addRichtextNode",
709
720
  content,
710
721
  size: opts.size,
@@ -768,7 +779,7 @@ Examples:
768
779
  // and lands the whole block collision-aware. Explicit --x/--y seed
769
780
  // the origin; omitting them places below existing content.
770
781
  const explicitOrigin = cmd.getOptionValueSource("x") === "cli" || cmd.getOptionValueSource("y") === "cli";
771
- const data = await api.post(`/api/canvas/${canvasId}`, {
782
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
772
783
  action: "placeLayout",
773
784
  doc: {
774
785
  ...(explicitOrigin
@@ -803,7 +814,7 @@ Examples:
803
814
  }
804
815
  return;
805
816
  }
806
- const data = await api.post(`/api/canvas/${canvasId}`, {
817
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
807
818
  action: "addSectionNode",
808
819
  name: opts.name,
809
820
  positionX: parseInt(opts.x, 10),
@@ -927,7 +938,7 @@ Examples:
927
938
  }
928
939
  const noteIds = opts.notes ? opts.notes.split(",").map((id) => id.trim()) : [];
929
940
  warnListItemMisuse(opts.description, noteIds.length);
930
- const data = await api.post(`/api/canvas/${canvasId}`, {
941
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
931
942
  action: "addListNode",
932
943
  description: opts.description,
933
944
  noteIds: noteIds.length > 0 ? noteIds : undefined,
@@ -958,7 +969,7 @@ Examples:
958
969
  const cfg = resolveConfig(program.opts());
959
970
  const api = new ApiClient(cfg.serverUrl, cfg.token);
960
971
  try {
961
- const data = await api.post(`/api/canvas/${canvasId}`, {
972
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
962
973
  action: "addCanvasLink",
963
974
  linkedCanvasId: opts.target,
964
975
  positionX: parseInt(opts.x, 10),
@@ -1011,7 +1022,7 @@ Examples:
1011
1022
  positionY,
1012
1023
  };
1013
1024
  });
1014
- const data = await api.post(`/api/canvas/${canvasId}`, {
1025
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1015
1026
  action: "bulkAddNodes",
1016
1027
  notes,
1017
1028
  });
@@ -1124,7 +1135,7 @@ Placement (relative to nodes already on the canvas):
1124
1135
  doc.placement = opts.placement;
1125
1136
  }
1126
1137
  warnLayoutListMisuse(doc);
1127
- const data = await api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`, {
1138
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1128
1139
  action: "placeLayout",
1129
1140
  doc,
1130
1141
  ...(opts.dryRun ? { dryRun: true } : {}),
@@ -1332,7 +1343,7 @@ Examples:
1332
1343
  // Name is required by the section container schema; the server
1333
1344
  // updates the existing frame in place and keeps SECTION-<n>.
1334
1345
  // Fetch the current name so we don't invent a placeholder title.
1335
- const canvas = await api.get(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`);
1346
+ const canvas = await api.get(canvasActionPath(canvasId, cfg.workspaceId));
1336
1347
  const want = String(opts.section);
1337
1348
  const wantUpper = want.toUpperCase();
1338
1349
  const section = (canvas.sectionNodes ?? []).find((s) => s.id === want ||
@@ -1354,7 +1365,7 @@ Examples:
1354
1365
  }
1355
1366
  }
1356
1367
  const autoResize = opts.autoResize !== false;
1357
- const data = await api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`, {
1368
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1358
1369
  action: "arrangeLayout",
1359
1370
  doc,
1360
1371
  ...(opts.section ? { targetSectionId: opts.section } : {}),
@@ -1439,7 +1450,7 @@ Examples:
1439
1450
  positionY,
1440
1451
  };
1441
1452
  });
1442
- const data = await api.post(`/api/canvas/${canvasId}`, {
1453
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1443
1454
  action: "bulkMoveNodes",
1444
1455
  moves,
1445
1456
  });
@@ -1469,7 +1480,7 @@ Examples:
1469
1480
  throw new ValidationError("--nodes must be a non-empty JSON array of strings");
1470
1481
  }
1471
1482
  nodeIds = nodeIds.map(String);
1472
- const data = await api.post(`/api/canvas/${canvasId}`, {
1483
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1473
1484
  action: "bulkRemoveNodes",
1474
1485
  nodeIds,
1475
1486
  });
@@ -1509,7 +1520,7 @@ Examples:
1509
1520
  });
1510
1521
  canvas
1511
1522
  .command("add-edge")
1512
- .description("Connect two nodes on a canvas (any node type: note, text, list, richtext, canvas-link)")
1523
+ .description("Connect two nodes on a canvas (any node type: note, text, list, richtext, canvas-link, section)")
1513
1524
  .argument("<canvasId>", "Canvas ID")
1514
1525
  .requiredOption("--source <nodeId>", "Source node ID (any node type on the canvas)")
1515
1526
  .requiredOption("--target <nodeId>", "Target node ID (any node type on the canvas)")
@@ -1522,7 +1533,7 @@ Examples:
1522
1533
  try {
1523
1534
  const { arrowStart, arrowEnd } = parseArrowMode(opts.arrow);
1524
1535
  const lineStyle = parseLineStyle(opts.lineStyle);
1525
- const data = await api.post(`/api/canvas/${canvasId}`, {
1536
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1526
1537
  action: "addEdge",
1527
1538
  sourceNodeId: opts.source,
1528
1539
  targetNodeId: opts.target,
@@ -1594,7 +1605,7 @@ Examples:
1594
1605
  }
1595
1606
  if (opts.reverse)
1596
1607
  body.reverse = true;
1597
- const data = await api.post(`/api/canvas/${canvasId}`, body);
1608
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), body);
1598
1609
  if (cfg.json) {
1599
1610
  outputJson(data);
1600
1611
  }
@@ -1661,7 +1672,7 @@ Examples:
1661
1672
  body.colorHex = opts.color;
1662
1673
  // workspaceId is what lets the server resolve a CANVAS-<n> handle (the
1663
1674
  // form every example here uses) rather than demanding a raw Convex id.
1664
- const data = await api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`, body);
1675
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), body);
1665
1676
  if (cfg.json) {
1666
1677
  outputJson(data);
1667
1678
  }
@@ -1688,7 +1699,7 @@ Examples:
1688
1699
  try {
1689
1700
  const positionX = Number(opts.x);
1690
1701
  const positionY = Number(opts.y);
1691
- await api.post(`/api/canvas/${canvasId}`, {
1702
+ await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1692
1703
  action: "moveNode",
1693
1704
  nodeId: opts.node,
1694
1705
  nodeType: opts.type,
@@ -1715,7 +1726,7 @@ Examples:
1715
1726
  const cfg = resolveConfig(program.opts());
1716
1727
  const api = new ApiClient(cfg.serverUrl, cfg.token);
1717
1728
  try {
1718
- await api.post(`/api/canvas/${canvasId}`, {
1729
+ await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1719
1730
  action: "removeNode",
1720
1731
  nodeId: opts.node,
1721
1732
  });
@@ -1739,7 +1750,7 @@ Examples:
1739
1750
  const cfg = resolveConfig(program.opts());
1740
1751
  const api = new ApiClient(cfg.serverUrl, cfg.token);
1741
1752
  try {
1742
- await api.post(`/api/canvas/${canvasId}`, {
1753
+ await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
1743
1754
  action: "removeEdge",
1744
1755
  edgeId: opts.edge,
1745
1756
  });
@@ -2165,5 +2176,164 @@ Examples:
2165
2176
  handleError(error, wantsJson);
2166
2177
  }
2167
2178
  });
2179
+ // ---- comments: private anchored comments (the review queue) ----
2180
+ // Visible only to the caller and agents acting as them — a shared canvas
2181
+ // never shows one member's comments to another. The human drops comments in
2182
+ // a review sweep (web UI or `comments add`); the agent runs `comments list`
2183
+ // to pull the queue, addresses each item, and `comments resolve`s it. The
2184
+ // active operation (from `operations begin` / `agent-run begin`) attaches to
2185
+ // add/resolve automatically via the X-Agent-Run-Id header, so every resolved
2186
+ // comment records which run addressed it.
2187
+ const comments = canvas
2188
+ .command("comments")
2189
+ .description("Private comments anchored to canvas nodes — the review queue (visible only to you and your agents)");
2190
+ comments
2191
+ .command("list")
2192
+ .description("List your comments on a canvas (open ones by default)")
2193
+ .argument("<canvasId>", "Canvas ID")
2194
+ .option("--status <status>", "Filter: open (default) | resolved | all", "open")
2195
+ .addHelpText("after", `
2196
+ Examples:
2197
+ $ cnotes canvas comments list <canvasId> # open review queue
2198
+ $ cnotes canvas comments list <canvasId> --status all --json
2199
+ `)
2200
+ .action(async (canvasId, opts) => {
2201
+ const cfg = resolveConfig(program.opts());
2202
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
2203
+ try {
2204
+ const status = String(opts.status);
2205
+ if (!["open", "resolved", "all"].includes(status)) {
2206
+ throw new ValidationError("--status must be one of: open, resolved, all");
2207
+ }
2208
+ const data = await api.get(`${canvasActionPath(canvasId, cfg.workspaceId)}&view=comments&status=${status}`);
2209
+ const rows = data.comments ?? [];
2210
+ if (cfg.quiet) {
2211
+ outputQuiet(rows.map((r) => String(r._id)).join("\n"));
2212
+ return;
2213
+ }
2214
+ if (cfg.json) {
2215
+ outputJson(data);
2216
+ return;
2217
+ }
2218
+ if (rows.length === 0) {
2219
+ console.log(status === "open" ? "No open comments on this canvas." : "No comments found.");
2220
+ return;
2221
+ }
2222
+ for (const row of rows) {
2223
+ const target = row.targetLabel ?? `${row.targetNodeType} node ${row.targetNodeId}`;
2224
+ const stale = row.isStale ? " — target has changed since this was written" : "";
2225
+ const missing = row.targetMissing ? " — node no longer on the canvas" : "";
2226
+ const kind = row.authorKind === "agent" ? "agent note" : "comment";
2227
+ console.log(`[${row.status}] ${row._id}`);
2228
+ console.log(` on ${target} (${kind})${stale}${missing}`);
2229
+ console.log(` ${truncate(String(row.content), 200)}`);
2230
+ }
2231
+ if (data.truncated) {
2232
+ console.log(`(showing the first ${rows.length} — resolve some or narrow --status to see the rest)`);
2233
+ }
2234
+ }
2235
+ catch (error) {
2236
+ handleError(error, cfg.json);
2237
+ }
2238
+ });
2239
+ comments
2240
+ .command("add")
2241
+ .description("Leave a comment anchored to a canvas node")
2242
+ .argument("<canvasId>", "Canvas ID")
2243
+ .requiredOption("--node <nodeRef>", "Target node: raw node id, NOTE display id, or SECTION display id")
2244
+ .requiredOption("--text <content>", "Comment text (max 4000 chars)")
2245
+ .option("--author <kind>", "Author kind: agent (CLI default) | human")
2246
+ .addHelpText("after", `
2247
+ Examples:
2248
+ $ cnotes canvas comments add <canvasId> --node NOTE-12 --text "Tighten this to one claim"
2249
+ $ cnotes canvas comments add <canvasId> --node SECTION-7 --text "Do not reflow — filmstrip depends on this order"
2250
+ `)
2251
+ .action(async (canvasId, opts) => {
2252
+ const cfg = resolveConfig(program.opts());
2253
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
2254
+ try {
2255
+ if (opts.author && !["human", "agent"].includes(opts.author)) {
2256
+ throw new ValidationError("--author must be human or agent");
2257
+ }
2258
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
2259
+ action: "addComment",
2260
+ targetNodeId: opts.node,
2261
+ content: opts.text,
2262
+ authorKind: opts.author,
2263
+ });
2264
+ if (cfg.quiet) {
2265
+ outputQuiet(String(data.annotationId));
2266
+ return;
2267
+ }
2268
+ if (cfg.json) {
2269
+ outputJson(data);
2270
+ return;
2271
+ }
2272
+ console.log(`Comment ${data.annotationId} added on ${data.targetNodeType} node (${opts.node}).`);
2273
+ }
2274
+ catch (error) {
2275
+ handleError(error, cfg.json);
2276
+ }
2277
+ });
2278
+ comments
2279
+ .command("resolve")
2280
+ .description("Resolve a comment — the active operation (if any) is recorded as what addressed it")
2281
+ .argument("<canvasId>", "Canvas ID")
2282
+ .requiredOption("--id <annotationId>", "Comment ID (from comments list)")
2283
+ .action(async (canvasId, opts) => {
2284
+ const cfg = resolveConfig(program.opts());
2285
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
2286
+ try {
2287
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
2288
+ action: "resolveComment",
2289
+ annotationId: opts.id,
2290
+ });
2291
+ if (cfg.quiet) {
2292
+ outputQuiet(String(data.annotationId));
2293
+ return;
2294
+ }
2295
+ if (cfg.json) {
2296
+ outputJson(data);
2297
+ return;
2298
+ }
2299
+ if (data.alreadyResolved) {
2300
+ console.log(`Comment ${opts.id} was already resolved.`);
2301
+ }
2302
+ else {
2303
+ const provenance = data.resolvedByOperationId ? " (resolving operation recorded)" : "";
2304
+ console.log(`Resolved comment ${opts.id}.${provenance}`);
2305
+ }
2306
+ }
2307
+ catch (error) {
2308
+ handleError(error, cfg.json);
2309
+ }
2310
+ });
2311
+ comments
2312
+ .command("rm")
2313
+ .description("Delete a comment")
2314
+ .argument("<canvasId>", "Canvas ID")
2315
+ .requiredOption("--id <annotationId>", "Comment ID (from comments list)")
2316
+ .action(async (canvasId, opts) => {
2317
+ const cfg = resolveConfig(program.opts());
2318
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
2319
+ try {
2320
+ const data = await api.post(canvasActionPath(canvasId, cfg.workspaceId), {
2321
+ action: "removeComment",
2322
+ annotationId: opts.id,
2323
+ });
2324
+ if (cfg.quiet) {
2325
+ outputQuiet(String(data.annotationId));
2326
+ return;
2327
+ }
2328
+ if (cfg.json) {
2329
+ outputJson(data);
2330
+ return;
2331
+ }
2332
+ console.log(`Deleted comment ${opts.id}.`);
2333
+ }
2334
+ catch (error) {
2335
+ handleError(error, cfg.json);
2336
+ }
2337
+ });
2168
2338
  }
2169
2339
  //# sourceMappingURL=canvas.js.map