agentic-canvas 0.7.0 → 0.9.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.
@@ -0,0 +1,130 @@
1
+ import {
2
+ FileStoreError
3
+ } from "./chunk-NWQED42L.js";
4
+
5
+ // src/store-file.ts
6
+ import { randomUUID } from "crypto";
7
+ import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "fs";
8
+ import { join } from "path";
9
+ import { createFileStore as createIdentityFileStore } from "agentic-identity/file";
10
+ import { StoreConflictError } from "agentic-identity";
11
+ var RETRY_FOR_MS = 1e4;
12
+ var LOCK_STALE_MS = 3e3;
13
+ var LOCK_WAIT_MS = 5e3;
14
+ var HELD = /locked by another process/;
15
+ var NOT_A_STORE_FILE = /is not an agentic-identity store file/;
16
+ async function retried(run) {
17
+ const deadline = Date.now() + RETRY_FOR_MS;
18
+ for (; ; ) {
19
+ try {
20
+ return await run();
21
+ } catch (error) {
22
+ const clash = error instanceof StoreConflictError && !HELD.test(error.message);
23
+ if (!clash || Date.now() > deadline) throw error;
24
+ await new Promise((resolve) => setTimeout(resolve, 5 + Math.random() * 20));
25
+ }
26
+ }
27
+ }
28
+ var isDoc = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
29
+ var sleep = (ms) => void Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
30
+ function legacyCollections(path) {
31
+ try {
32
+ const data = JSON.parse(readFileSync(path, "utf8"));
33
+ const current = isDoc(data) && data.format === 1 && isDoc(data.collections);
34
+ const old = isDoc(data) && !current && Object.values(data).every((docs) => isDoc(docs) && Object.values(docs).every(isDoc));
35
+ return old ? data : void 0;
36
+ } catch {
37
+ return void 0;
38
+ }
39
+ }
40
+ function withLock(path, fn) {
41
+ const lock = `${path}.lock`;
42
+ const mine = `${process.pid}:${randomUUID()}`;
43
+ for (const start = Date.now(); ; ) {
44
+ try {
45
+ writeFileSync(lock, mine, { flag: "wx", mode: 384 });
46
+ break;
47
+ } catch (error) {
48
+ if (error.code !== "EEXIST") return false;
49
+ try {
50
+ if (Date.now() - statSync(lock).mtimeMs > LOCK_STALE_MS) unlinkSync(lock);
51
+ } catch {
52
+ }
53
+ if (Date.now() - start > LOCK_WAIT_MS) return false;
54
+ sleep(10);
55
+ }
56
+ }
57
+ const owned = () => {
58
+ try {
59
+ return readFileSync(lock, "utf8") === mine;
60
+ } catch {
61
+ return false;
62
+ }
63
+ };
64
+ try {
65
+ fn(owned);
66
+ } finally {
67
+ if (owned()) rmSync(lock, { force: true });
68
+ }
69
+ return true;
70
+ }
71
+ function upgradeLegacyFile(path) {
72
+ if (legacyCollections(path) === void 0) return true;
73
+ try {
74
+ withLock(path, (owned) => {
75
+ const data = legacyCollections(path);
76
+ if (data === void 0) return;
77
+ const temp = `${path}.${process.pid}.upgrade`;
78
+ try {
79
+ const fd = openSync(temp, "w", 384);
80
+ try {
81
+ writeSync(fd, JSON.stringify({ format: 1, collections: data }));
82
+ fsyncSync(fd);
83
+ } finally {
84
+ closeSync(fd);
85
+ }
86
+ if (owned()) renameSync(temp, path);
87
+ } finally {
88
+ rmSync(temp, { force: true });
89
+ }
90
+ });
91
+ } catch {
92
+ }
93
+ return legacyCollections(path) === void 0;
94
+ }
95
+ function createFileStore(path, options = {}) {
96
+ upgradeLegacyFile(path);
97
+ const inner = createIdentityFileStore({ ...options, path });
98
+ const guard = async (run) => {
99
+ for (let upgraded = false; ; upgraded = true) {
100
+ try {
101
+ return await retried(run);
102
+ } catch (error) {
103
+ if (!(error instanceof Error)) throw error;
104
+ if (HELD.test(error.message)) throw new FileStoreError("LOCKED", "the database file is locked. If no agentic-canvas process runs, remove the .lock file next to it");
105
+ if (!NOT_A_STORE_FILE.test(error.message)) throw error;
106
+ if (legacyCollections(path) === void 0) throw new FileStoreError("DAMAGED", "the database file is not valid JSON. Repair or remove it");
107
+ if (upgraded || !upgradeLegacyFile(path)) throw new FileStoreError("DAMAGED", "the database file is in the older layout and could not be upgraded. Try again, and check that its folder is writable");
108
+ }
109
+ }
110
+ };
111
+ return {
112
+ region: inner.region,
113
+ get: (collection, id) => guard(() => inner.get(collection, id)),
114
+ query: (collection, query) => guard(() => inner.query(collection, query)),
115
+ put: (collection, id, doc) => guard(() => inner.put(collection, id, doc)),
116
+ delete: (collection, id) => guard(() => inner.delete(collection, id)),
117
+ transaction: (fn) => guard(() => inner.transaction(fn))
118
+ };
119
+ }
120
+ function openLocalStore(dir) {
121
+ mkdirSync(dir, { recursive: true });
122
+ return createFileStore(join(dir, STORE_FILE));
123
+ }
124
+ var STORE_FILE = "canvas.json";
125
+
126
+ export {
127
+ createFileStore,
128
+ openLocalStore,
129
+ STORE_FILE
130
+ };
package/dist/cli.js CHANGED
@@ -5,13 +5,13 @@ import {
5
5
  operatorActor,
6
6
  runMcp,
7
7
  svgToPng
8
- } from "./chunk-3QW4UF2R.js";
8
+ } from "./chunk-33ANMQEY.js";
9
9
  import {
10
10
  openLocalStore
11
- } from "./chunk-UKST5A57.js";
11
+ } from "./chunk-NRLF6MGR.js";
12
12
  import {
13
13
  CanvasService
14
- } from "./chunk-GPSOLW2Y.js";
14
+ } from "./chunk-M74ENUWR.js";
15
15
  import "./chunk-NWQED42L.js";
16
16
  import {
17
17
  CanvasError
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { E as ErrorCode, a as BoardData, c as ShapeType, S as Shape, d as Endpoint, C as Connector } from './read-CeWoSAAy.js';
1
+ import { E as ErrorCode, a as BoardData, c as ShapeType, S as Shape, d as Endpoint, C as Connector } from './read--Jd3kE13.js';
2
2
  import { PrincipalKind } from 'agentic-identity';
3
3
  import 'yjs';
4
4
  import 'zod';
package/dist/embed.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { CanvasClient } from './client.js';
3
3
  import { PrincipalKind } from 'agentic-identity';
4
- import './read-CeWoSAAy.js';
4
+ import './read--Jd3kE13.js';
5
5
  import 'yjs';
6
6
  import 'zod';
7
7
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as Board, P as Principal } from './read-CeWoSAAy.js';
2
- export { a as BoardData, b as CanvasElement, e as CanvasError, C as Connector, f as EdgeData, d as Endpoint, E as ErrorCode, G as Group, M as MAX_ELEMENTS, g as MAX_TEXT, N as NodeData, h as SHAPE_TYPES, S as Shape, c as ShapeType, p as principalSchema, r as readBoard } from './read-CeWoSAAy.js';
3
- import { B as BoardAccess } from './service-D9WzMLH1.js';
4
- export { C as CanvasService, D as DrawOptions, F as FlowEdge, a as FlowGraph, b as FlowNode, M as MAX_FLOW_EDGES, c as MAX_FLOW_NODES, P as Placement, d as drawFlow, l as layoutFlow, p as parseFlow } from './service-D9WzMLH1.js';
1
+ import { B as Board, P as Principal } from './read--Jd3kE13.js';
2
+ export { a as BoardData, b as CanvasElement, e as CanvasError, C as Connector, f as EdgeData, d as Endpoint, E as ErrorCode, G as Group, M as MAX_ELEMENTS, g as MAX_TEXT, N as NodeData, h as SHAPE_TYPES, S as Shape, c as ShapeType, p as principalSchema, r as readBoard } from './read--Jd3kE13.js';
3
+ import { B as BoardAccess } from './service-OiTtNadu.js';
4
+ export { C as CanvasService, D as DrawOptions, F as FlowEdge, a as FlowGraph, b as FlowNode, M as MAX_FLOW_EDGES, c as MAX_FLOW_NODES, P as Placement, d as drawFlow, l as layoutFlow, p as parseFlow } from './service-OiTtNadu.js';
5
5
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6
6
  export { FileStoreOptions, createFileStore, openLocalStore } from './store-file.js';
7
7
  export { PRINCIPAL_KINDS, PrincipalKind, isMachineMade } from 'agentic-identity';
package/dist/index.js CHANGED
@@ -6,14 +6,14 @@ import {
6
6
  operatorActor,
7
7
  runMcp,
8
8
  svgToPng
9
- } from "./chunk-3QW4UF2R.js";
9
+ } from "./chunk-33ANMQEY.js";
10
10
  import {
11
11
  createFileStore,
12
12
  openLocalStore
13
- } from "./chunk-UKST5A57.js";
13
+ } from "./chunk-NRLF6MGR.js";
14
14
  import {
15
15
  CanvasService
16
- } from "./chunk-GPSOLW2Y.js";
16
+ } from "./chunk-M74ENUWR.js";
17
17
  import "./chunk-NWQED42L.js";
18
18
  import {
19
19
  Board,
@@ -5,10 +5,10 @@ import 'agentic-identity';
5
5
  import 'agentic-realtime';
6
6
  import 'agentic-identity/cloudflare';
7
7
  import 'hono';
8
- import './read-CeWoSAAy.js';
8
+ import './read--Jd3kE13.js';
9
9
  import 'yjs';
10
10
  import 'zod';
11
- import './service-D9WzMLH1.js';
11
+ import './service-OiTtNadu.js';
12
12
 
13
13
  type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
14
14
  interface LiveEnv extends WorkerEnv {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createApp
3
- } from "./chunk-EH7WCMK6.js";
4
- import "./chunk-GPSOLW2Y.js";
3
+ } from "./chunk-JQCUVXXD.js";
4
+ import "./chunk-M74ENUWR.js";
5
5
  import "./chunk-NWQED42L.js";
6
6
  import {
7
7
  CanvasError
package/dist/live.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-CeWoSAAy.js';
1
+ import { B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read--Jd3kE13.js';
2
2
  import { Peer } from 'agentic-realtime/client';
3
- import { B as BoardAccess, D as DrawOptions } from './service-D9WzMLH1.js';
3
+ import { B as BoardAccess, D as DrawOptions } from './service-OiTtNadu.js';
4
4
  import 'yjs';
5
5
  import 'agentic-identity';
6
6
  import 'zod';
@@ -6,22 +6,16 @@ import { z } from 'zod';
6
6
  * The actor of a new write. The rule is the identity kernel's `parsePrincipal`: a known kind, and an id of 1 to 128
7
7
  * characters from A-Z, a-z, 0-9, ".", "_" and "-". The Worker, the CLI and the SDK all use it.
8
8
  */
9
- declare const principalSchema: z.ZodEffects<z.ZodObject<{
9
+ declare const principalSchema: z.ZodPipe<z.ZodObject<{
10
10
  kind: z.ZodString;
11
11
  id: z.ZodString;
12
- }, "strict", z.ZodTypeAny, {
13
- kind: string;
14
- id: string;
15
- }, {
16
- kind: string;
17
- id: string;
18
- }>, {
12
+ }, z.core.$strict>, z.ZodTransform<{
19
13
  kind: PrincipalKind;
20
14
  id: string;
21
15
  }, {
22
16
  kind: string;
23
17
  id: string;
24
- }>;
18
+ }>>;
25
19
  type Principal = {
26
20
  kind: PrincipalKind;
27
21
  id: string;
@@ -1,4 +1,4 @@
1
- import { c as ShapeType, B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-CeWoSAAy.js';
1
+ import { c as ShapeType, B as Board, P as Principal, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read--Jd3kE13.js';
2
2
  import { Store } from 'agentic-identity';
3
3
 
4
4
  declare const MAX_FLOW_NODES = 500;
@@ -68,6 +68,11 @@ declare class CanvasService {
68
68
  id: string;
69
69
  title: string;
70
70
  }[]>;
71
+ /** The listed boards that exist, with titles, in the order given. */
72
+ summaries(ids: string[]): Promise<{
73
+ id: string;
74
+ title: string;
75
+ }[]>;
71
76
  /** Delete a board and everything on it. Throws NOT_FOUND unless the board exists. The Worker erases the live room too. */
72
77
  delete(id: string): Promise<{
73
78
  deleted: string;
@@ -7,19 +7,8 @@ declare class FileStoreError extends Error {
7
7
  }
8
8
 
9
9
  interface FileStoreOptions extends AdapterOptions {
10
- /** How long a write waits for the lock before it fails. Defaults to 10000. */
11
- lockTimeoutMs?: number;
12
- /** A lock older than this is stale, if its owner is alive. Defaults to 30000. */
13
- staleAfterMs?: number;
14
10
  }
15
- /**
16
- * A Store kept in one JSON file. Node only. It is an adapter of the shared Store port and passes `runStoreContract`. Many processes may share the file, for example the CLI
17
- * and the MCP server. Each write takes a lock file, reads the file again, applies the change, and
18
- * replaces the file by an atomic rename. A read sees the last renamed file, so it takes no lock.
19
- * Each call reads the whole file and loads the docs it needs into the shared memory adapter, which runs the
20
- * queries and the transaction rules. Cost grows with the file. Use it for local work, not for scale.
21
- * The lock needs one machine: the process check and the clock do not work across hosts.
22
- */
11
+ /** A Store kept in one JSON file. Node only. It is the file adapter of agentic-identity, with a retry on a write clash. */
23
12
  declare function createFileStore(path: string, options?: FileStoreOptions): Store;
24
13
  /** The store the CLI and the MCP server share: one file in `dir`, which is made if it is missing. */
25
14
  declare function openLocalStore(dir: string): Store;
@@ -2,7 +2,7 @@ import {
2
2
  STORE_FILE,
3
3
  createFileStore,
4
4
  openLocalStore
5
- } from "./chunk-UKST5A57.js";
5
+ } from "./chunk-NRLF6MGR.js";
6
6
  import {
7
7
  FileStoreError
8
8
  } from "./chunk-NWQED42L.js";
package/dist/ui.css CHANGED
@@ -1,38 +1,38 @@
1
1
  /* Every rule is nested under .cv-root, so the styles do not reach the host page. */
2
2
  .cv-root {
3
- .row { display: flex; align-items: center; justify-content: space-between; gap: var(--ad-space-4); flex-wrap: wrap; }
4
- .failure { border: 1px solid var(--ad-color-danger); border-radius: var(--ad-radius-md); padding: var(--ad-space-4); color: var(--ad-color-danger-text); background: var(--ad-color-danger-subtle); display: grid; gap: var(--ad-space-3); justify-items: start; }
5
- .note { color: var(--ad-color-text-muted); }
3
+ .row { display: flex; align-items: center; justify-content: space-between; gap: var(--agd-space-4); flex-wrap: wrap; }
4
+ .failure { border: 1px solid var(--agd-color-danger); border-radius: var(--agd-radius-md); padding: var(--agd-space-4); color: var(--agd-color-danger-text); background: var(--agd-color-danger-subtle); display: grid; gap: var(--agd-space-3); justify-items: start; }
5
+ .note { color: var(--agd-color-text-muted); }
6
6
  .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
7
7
 
8
- .board { display: grid; gap: var(--ad-space-3); }
9
- .stage { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 16rem; gap: var(--ad-space-3); height: 65vh; min-height: 22rem; }
8
+ .board { display: grid; gap: var(--agd-space-3); }
9
+ .stage { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 16rem; gap: var(--agd-space-3); height: 65vh; min-height: 22rem; }
10
10
  @media (max-width: 48rem) { .stage { grid-template-columns: 1fr; height: auto; } .canvas { height: 55vh; } }
11
- .canvas { background: var(--ad-color-surface-sunken); border: 1px solid var(--ad-color-border); border-radius: var(--ad-radius-md); overflow: hidden; touch-action: none; min-height: 18rem; }
12
- .canvas:focus-visible { outline: 3px solid var(--ad-color-focus); outline-offset: 2px; }
13
- .canvas svg { display: block; cursor: grab; font-family: var(--ad-font-family-sans); font-size: 14px; }
11
+ .canvas { background: var(--agd-color-surface-sunken); border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-md); overflow: hidden; touch-action: none; min-height: 18rem; }
12
+ .canvas:focus-visible { outline: 3px solid var(--agd-color-focus); outline-offset: 2px; }
13
+ .canvas svg { display: block; cursor: grab; font-family: var(--agd-font-family-sans); font-size: 14px; }
14
14
  .item { outline: none; cursor: pointer; }
15
- .shape { fill: var(--ad-color-surface-raised); stroke: var(--ad-color-border-strong); stroke-width: 1.5; }
15
+ .shape { fill: var(--agd-color-surface-raised); stroke: var(--agd-color-border-strong); stroke-width: 1.5; }
16
16
  .shape.text { fill: transparent; stroke: none; }
17
17
  .shape.frame, .shape.image { fill: none; stroke-dasharray: 6 4; pointer-events: visiblePainted; }
18
- .shape.sticky { fill: var(--ad-color-warning-subtle); stroke: var(--ad-color-warning); }
19
- .label { fill: var(--ad-color-text); pointer-events: none; }
20
- .sticky-label { fill: var(--ad-color-warning-text); }
21
- .edge { stroke: var(--ad-color-text-muted); stroke-width: 2; fill: none; pointer-events: none; }
18
+ .shape.sticky { fill: var(--agd-color-warning-subtle); stroke: var(--agd-color-warning); }
19
+ .label { fill: var(--agd-color-text); pointer-events: none; }
20
+ .sticky-label { fill: var(--agd-color-warning-text); }
21
+ .edge { stroke: var(--agd-color-text-muted); stroke-width: 2; fill: none; pointer-events: none; }
22
22
  .hit { stroke: transparent; stroke-width: 14; }
23
- .arrowhead { fill: var(--ad-color-text-muted); }
24
- .edge-text { fill: var(--ad-color-text); paint-order: stroke; stroke: var(--ad-color-surface-sunken); stroke-width: 4px; pointer-events: none; }
23
+ .arrowhead { fill: var(--agd-color-text-muted); }
24
+ .edge-text { fill: var(--agd-color-text); paint-order: stroke; stroke: var(--agd-color-surface-sunken); stroke-width: 4px; pointer-events: none; }
25
25
  .ring, .ring-line { fill: none; stroke: none; stroke-width: 3; pointer-events: none; }
26
- .item.selected .ring, .item.selected .ring-line, .item:focus-visible .ring, .item:focus-visible .ring-line { stroke: var(--ad-color-focus); }
26
+ .item.selected .ring, .item.selected .ring-line, .item:focus-visible .ring, .item:focus-visible .ring-line { stroke: var(--agd-color-focus); }
27
27
  .item:focus-visible .ring, .item:focus-visible .ring-line { stroke-width: 4; }
28
- .handle { fill: var(--ad-color-focus); stroke: var(--ad-color-bg); stroke-width: 2; cursor: nwse-resize; }
29
- .agent-badge rect { fill: var(--ad-color-agent-subtle); stroke: var(--ad-color-agent); }
30
- .agent-badge text { fill: var(--ad-color-agent-text); font-size: 11px; font-weight: 600; pointer-events: none; }
28
+ .handle { fill: var(--agd-color-focus); stroke: var(--agd-color-bg); stroke-width: 2; cursor: nwse-resize; }
29
+ .agent-badge rect { fill: var(--agd-color-agent-subtle); stroke: var(--agd-color-agent); }
30
+ .agent-badge text { fill: var(--agd-color-agent-text); font-size: 11px; font-weight: 600; pointer-events: none; }
31
31
  .empty-overlay { position: absolute; inset: 0 16rem 0 0; display: grid; place-items: center; pointer-events: none; }
32
32
  @media (max-width: 48rem) { .empty-overlay { inset: 0; bottom: auto; height: 55vh; } }
33
- .inspector { border: 1px solid var(--ad-color-border); border-radius: var(--ad-radius-md); padding: var(--ad-space-3); background: var(--ad-color-surface); display: grid; gap: var(--ad-space-3); align-content: start; overflow: auto; }
34
- .inspector h2 { margin: 0; font-size: var(--ad-font-size-lg); }
35
- .inspector form { display: grid; gap: var(--ad-space-3); }
36
- .inspector .by { margin: 0; color: var(--ad-color-text-muted); font-size: var(--ad-font-size-sm); }
37
- .sizes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ad-space-2); }
33
+ .inspector { border: 1px solid var(--agd-color-border); border-radius: var(--agd-radius-md); padding: var(--agd-space-3); background: var(--agd-color-surface); display: grid; gap: var(--agd-space-3); align-content: start; overflow: auto; }
34
+ .inspector h2 { margin: 0; font-size: var(--agd-font-size-lg); }
35
+ .inspector form { display: grid; gap: var(--agd-space-3); }
36
+ .inspector .by { margin: 0; color: var(--agd-color-text-muted); font-size: var(--agd-font-size-sm); }
37
+ .sizes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--agd-space-2); }
38
38
  }
package/dist/worker.d.ts CHANGED
@@ -1,12 +1,87 @@
1
- import { Store, Blobs } from 'agentic-identity';
1
+ import { Store, PrincipalContext, Action, Grant, InviteSummary, Blobs } from 'agentic-identity';
2
2
  import { Realtime } from 'agentic-realtime';
3
3
  import { D1Like, R2Like } from 'agentic-identity/cloudflare';
4
4
  import { Hono } from 'hono';
5
- import { P as Principal } from './read-CeWoSAAy.js';
6
- import { C as CanvasService } from './service-D9WzMLH1.js';
5
+ import { P as Principal } from './read--Jd3kE13.js';
6
+ import { C as CanvasService } from './service-OiTtNadu.js';
7
7
  import 'yjs';
8
8
  import 'zod';
9
9
 
10
+ /**
11
+ * Who may do what on a board. A board is an identity resource of type `canvas`. The roles, the grants, the invites,
12
+ * and the links are those of agentic-identity: this class checks input and maps its failures to `CanvasError`.
13
+ * A caller who cannot read a board gets NOT_FOUND, so a board id tells a stranger nothing. A caller who can read
14
+ * it and lacks the role for the action gets FORBIDDEN.
15
+ */
16
+ declare class CanvasAccess {
17
+ private readonly store;
18
+ private readonly sharing;
19
+ private readonly now;
20
+ constructor(store: Store, options?: {
21
+ now?: () => number;
22
+ });
23
+ /** A board id that the Store refuses names no board. The gate checks this for a role holder: a token of the dev list skips the gate. */
24
+ private known;
25
+ /** Make the actor the owner of a new board. */
26
+ register(ctx: PrincipalContext, id: string): Promise<void>;
27
+ /** Remove the sharing records of a board. Needs an owner. */
28
+ unregister(ctx: PrincipalContext, id: string): Promise<void>;
29
+ /** True if the board has sharing records: it was made by an identity credential. */
30
+ registered(id: string): Promise<boolean>;
31
+ /**
32
+ * True if the caller may do `action` on the board. A link token adds the role of its link. It writes an audit entry.
33
+ * An id that belongs to a resource of another type, in a Store shared with another app, is no board: it is denied.
34
+ */
35
+ allowed(ctx: PrincipalContext | null, action: Action, id: string, linkToken?: string): Promise<boolean>;
36
+ /** Throw NOT_FOUND for a caller who cannot read the board, and FORBIDDEN for one who can read it but not do `action`. */
37
+ require(ctx: PrincipalContext | null, action: Action, id: string, linkToken?: string): Promise<void>;
38
+ /** The ids of the boards a person or agent holds a role on, the first 100. A credential without `canvas:read` gets none. */
39
+ readable(ctx: PrincipalContext): Promise<string[]>;
40
+ grant(ctx: PrincipalContext, id: string, input: {
41
+ subjectKind?: unknown;
42
+ subjectId?: unknown;
43
+ role?: unknown;
44
+ }): Promise<{
45
+ ok: true;
46
+ }>;
47
+ revoke(ctx: PrincipalContext, id: string, input: {
48
+ subjectKind?: unknown;
49
+ subjectId?: unknown;
50
+ }): Promise<{
51
+ ok: true;
52
+ }>;
53
+ /** The grants and the invites on a board. An invite never shows its email. Links are not listed: a token shows once. */
54
+ list(ctx: PrincipalContext, id: string): Promise<{
55
+ grants: Grant[];
56
+ invites: InviteSummary[];
57
+ }>;
58
+ invite(ctx: PrincipalContext, id: string, input: {
59
+ role?: unknown;
60
+ email?: unknown;
61
+ handle?: unknown;
62
+ expiresInMs?: unknown;
63
+ }): Promise<{
64
+ id: string;
65
+ }>;
66
+ /** The email comes from a verified sign-in and the handle from the agent's own record, never from the request. */
67
+ accept(ctx: PrincipalContext, inviteId: string): Promise<{
68
+ ok: true;
69
+ }>;
70
+ /** The token shows once. Only its hash is stored. */
71
+ createLink(ctx: PrincipalContext, id: string, input: {
72
+ role?: unknown;
73
+ signedInOnly?: unknown;
74
+ expiresInMs?: unknown;
75
+ }): Promise<{
76
+ id: string;
77
+ token: string;
78
+ }>;
79
+ /** Revoke a link of this board. A link of another board is NOT_FOUND here, even for an owner of both. */
80
+ revokeLink(ctx: PrincipalContext, id: string, linkId: string): Promise<{
81
+ ok: true;
82
+ }>;
83
+ }
84
+
10
85
  interface WorkerEnv {
11
86
  DB: D1Like;
12
87
  IMAGES: R2Like;
@@ -33,13 +108,22 @@ interface WorkerEnv {
33
108
  CANVAS_MAX_IMAGE_BYTES?: string;
34
109
  }
35
110
  type Role = "editor" | "viewer";
111
+ /** The caller. `dev` is true for a token of the static list: it holds no board roles and reaches every board. */
112
+ interface Caller {
113
+ actor: Principal;
114
+ role: Role;
115
+ principal: PrincipalContext;
116
+ dev: boolean;
117
+ }
36
118
  type App = {
37
119
  Bindings: WorkerEnv;
38
120
  Variables: {
39
121
  service: CanvasService;
122
+ access: CanvasAccess;
40
123
  blobs: Blobs;
41
124
  actor: Principal;
42
125
  role: Role;
126
+ caller: Caller;
43
127
  };
44
128
  };
45
129
  interface AppOptions {
@@ -58,6 +142,8 @@ declare function authenticate(request: Request, env: WorkerEnv, store?: Store):
58
142
  actor: Principal;
59
143
  role: Role;
60
144
  } | null>;
145
+ /** Like `authenticate`, with the whole principal context that board roles are checked against. */
146
+ declare function identify(request: Request, env: WorkerEnv, store?: Store): Promise<Caller | null>;
61
147
  /** The image types we accept, found by their first bytes. The client's claim of a type is not trusted. */
62
148
  declare function sniffImage(bytes: Uint8Array): string | null;
63
149
  declare function createApp(options?: AppOptions): Hono<App>;
@@ -65,4 +151,4 @@ declare const _default: {
65
151
  fetch: (request: Request, env: WorkerEnv, ctx: unknown) => Response | Promise<Response>;
66
152
  };
67
153
 
68
- export { type AppOptions, type Role, WS_PROTOCOL, type WorkerEnv, authenticate, createApp, _default as default, sniffImage };
154
+ export { type AppOptions, type Caller, type Role, WS_PROTOCOL, type WorkerEnv, authenticate, createApp, _default as default, identify, sniffImage };
package/dist/worker.js CHANGED
@@ -2,10 +2,11 @@ import {
2
2
  WS_PROTOCOL,
3
3
  authenticate,
4
4
  createApp,
5
+ identify,
5
6
  sniffImage,
6
7
  worker_default
7
- } from "./chunk-EH7WCMK6.js";
8
- import "./chunk-GPSOLW2Y.js";
8
+ } from "./chunk-JQCUVXXD.js";
9
+ import "./chunk-M74ENUWR.js";
9
10
  import "./chunk-NWQED42L.js";
10
11
  import "./chunk-ZSPZMGHG.js";
11
12
  import "./chunk-JOBTIAFJ.js";
@@ -14,5 +15,6 @@ export {
14
15
  authenticate,
15
16
  createApp,
16
17
  worker_default as default,
18
+ identify,
17
19
  sniffImage
18
20
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-canvas",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "A shared whiteboard for diagrams, drawn by people and agents.",
5
5
  "keywords": [
6
6
  "whiteboard",
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "files": [
27
27
  "dist",
28
+ "LICENSE",
28
29
  "llms.txt",
29
30
  "SKILL.md"
30
31
  ],
@@ -80,11 +81,11 @@
80
81
  "dependencies": {
81
82
  "@dagrejs/dagre": "^3.1.3",
82
83
  "@modelcontextprotocol/sdk": "^1.12.0",
83
- "agentic-design": "^0.2.0",
84
- "agentic-identity": "^0.6.0",
84
+ "agentic-design": "^0.4.0",
85
+ "agentic-identity": "^0.7.0",
85
86
  "agentic-realtime": "^0.4.2",
86
87
  "yjs": "^13.6.0",
87
- "zod": "^3.25.0"
88
+ "zod": "^4.6.7"
88
89
  },
89
90
  "peerDependencies": {
90
91
  "playwright-core": ">=1.40.0",
@@ -121,5 +122,8 @@
121
122
  "vite": "^7.3.7",
122
123
  "vitest": "^3.0.0",
123
124
  "wrangler": "^4.155.0"
125
+ },
126
+ "overrides": {
127
+ "zod": "$zod"
124
128
  }
125
129
  }