agentic-canvas 0.12.0 → 0.13.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.
- package/README.md +31 -1
- package/dist/account.d.ts +33 -0
- package/dist/account.js +213 -0
- package/dist/{chunk-5II2WZIL.js → chunk-4PY5SN2N.js} +8 -151
- package/dist/{chunk-A4YK2SWM.js → chunk-AI5NWPEW.js} +2 -2
- package/dist/{chunk-EDQNA2SU.js → chunk-BKG3FXX3.js} +1 -1
- package/dist/{chunk-Z6KLGAFP.js → chunk-GLNF43N4.js} +4 -2
- package/dist/chunk-LMSMUGBF.js +156 -0
- package/dist/{chunk-MVWUIBGV.js → chunk-ZP6BLCB7.js} +57 -0
- package/dist/cli.js +4 -4
- package/dist/{client-Dyd30StF.d.ts → client-DBb1jpxl.d.ts} +1 -1
- package/dist/embed.d.ts +3 -2
- package/dist/embed.js +2 -2
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -4
- package/dist/live-worker.d.ts +4 -3
- package/dist/live-worker.js +5 -4
- package/dist/live.d.ts +3 -2
- package/dist/live.js +2 -2
- package/dist/principal-blhsFbdS.d.ts +23 -0
- package/dist/{read-UIJlrkwx.d.ts → read-DeS2Zaz8.d.ts} +24 -22
- package/dist/{service-ojpxVNVl.d.ts → service-BNEH2Z12.d.ts} +2 -1
- package/dist/worker.d.ts +4 -3
- package/dist/worker.js +5 -4
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
A shared whiteboard for diagrams, drawn by people and agents.
|
|
9
9
|
|
|
10
|
-
> Status: 0.
|
|
10
|
+
> Status: 0.13.0. The board model, the agent operations, the exports, the CLI, the
|
|
11
11
|
> MCP server, a small Worker API, and live co-editing work. The CLI, the MCP server,
|
|
12
12
|
> and the SDK can join a live room. A remote client calls the Worker API. A small web UI, served by the Worker, is new in 0.4.0.
|
|
13
13
|
> The package exports `agentic-canvas/ui`, a board view you can mount in your own app, new in 0.5.0.
|
|
@@ -230,6 +230,36 @@ A live MCP server with an admin token can share boards: an agent that holds it c
|
|
|
230
230
|
|
|
231
231
|
The static dev fallback holds no board roles. Its tokens reach every board and cannot share.
|
|
232
232
|
|
|
233
|
+
## Account export and erase
|
|
234
|
+
|
|
235
|
+
A host that runs a person's data export and erase imports `canvasAccount` from `agentic-canvas/account`. It never reads the storage layout.
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { canvasAccount } from "agentic-canvas/account";
|
|
239
|
+
const data = await canvasAccount.exportAccount({ kind: "human", id: "ada" }, { store, saveRoom, eraseRoom });
|
|
240
|
+
const counts = await canvasAccount.eraseAccount({ kind: "human", id: "ada" }, { store, saveRoom, eraseRoom });
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The export is plain JSON in the collections `boards`, `grants`, `invites`, `authored_elements`, `edited_elements`, `titles`, and `audit`. It has no format or time. The erase returns a count for each name.
|
|
244
|
+
|
|
245
|
+
- A board that the subject alone owns is deleted with its grants, invites, links, and audit entries. The erase calls `eraseRoom(boardId)` first, as the Worker does.
|
|
246
|
+
- On any other board, the subject's grant and invites go. The elements it made are deleted. A connector of someone else that was bound to one of them goes too, and no count or export lists it. The members of a deleted group move to its parent. The editor of an element it only edited becomes `(erased)`, and a title it set is cleared.
|
|
247
|
+
- Its audit entries (it acted, it roots the chain, or a grant named it) stay with the id set to `(erased)`. The kinds stay, so an entry still says if a machine acted.
|
|
248
|
+
- A second erase counts zero. Stop writes first: a write between export and erase is erased and not exported.
|
|
249
|
+
- The erase reads every stored board to find the subject's elements, before it changes data. It stops with `INVALID` on a board that does not decode, on a damaged grant of the subject, on a damaged invite of the subject, on a board with child resources, on an owner grant that the sharing records do not accept, on a damaged co-owner grant, and on an audit entry that names the subject, as the actor, the root of the chain, or the target, with an unknown kind. The checks may add audit entries about the subject: the erase scrubs them.
|
|
250
|
+
- Live rooms: give `saveRoom(boardId)`, which saves the room to the Store as the Worker does before a read. Without it, edits that no request has saved yet are not seen, and the reset below drops them. Give `eraseRoom(boardId)` too: the erase calls it for every board that it changes or deletes, so the room opens again from the Store.
|
|
251
|
+
- `boards` holds each stored entry as it is, with full images and groups. It includes the elements that others made on the board.
|
|
252
|
+
- Run the canvas erase before the identity erase of the person: the identity erase removes the grants that this erase finds boards by.
|
|
253
|
+
- The subject is any principal kind, for example `agent_of_human`: call once for each principal. The id follows the strict identity rule: an old board that has an id from the older, looser rule cannot be named.
|
|
254
|
+
- Known gaps:
|
|
255
|
+
- Invites and audit entries name a principal by id with no kind, so `human:ada` and `agent:ada` share them. Do not give two principals one id.
|
|
256
|
+
- An audit entry where the subject is only a middle step of the chain is not found.
|
|
257
|
+
- An invite that someone else made for the subject's email or handle stays. The package cannot map a principal to an address.
|
|
258
|
+
- Image bytes in the image store have no uploader record, so they are not exported or erased. Only the `blob:` key in an element is.
|
|
259
|
+
- A deleted element leaves its id in the stored Yjs history. Its content is gone, and an id that the caller chose can stay in the stored bytes.
|
|
260
|
+
- A connector of someone else that the subject edited, and that goes with a deleted shape, is in `edited_elements` of the export and not in its erase count.
|
|
261
|
+
- The shape is provisional. It is the `AccountPackage` contract that a later `agentic-identity` release adds to `agentic-identity/account`. The `AccountApp` of 0.9.0 is a different contract, and this does not fit it.
|
|
262
|
+
|
|
233
263
|
## UI
|
|
234
264
|
|
|
235
265
|
The Worker serves a small web UI at its root. It uses the published `agentic-design` package for
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Store, Json } from 'agentic-identity';
|
|
2
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
interface CanvasAccountDeps {
|
|
6
|
+
/** The Store that holds the boards and the sharing records. */
|
|
7
|
+
store: Store;
|
|
8
|
+
/** Saves the live room of a board to the Store, as the Worker does before it reads a board. Without it, edits that no request has saved yet are not seen, and an erase that resets a room drops them. The call runs for every stored board, so it must be cheap for a board with no room. A board with no room needs no work. Omit it when no room runs. */
|
|
9
|
+
saveRoom?: (boardId: string) => Promise<void>;
|
|
10
|
+
/** Erases the live room of a board, so it opens again from the Store. The erase calls it for every board that it changes or deletes. Omit it when no room runs. */
|
|
11
|
+
eraseRoom?: (boardId: string) => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
type CanvasCollection = "boards" | "grants" | "invites" | "authored_elements" | "edited_elements" | "titles" | "audit";
|
|
14
|
+
interface CanvasAccount {
|
|
15
|
+
id: "canvas";
|
|
16
|
+
/**
|
|
17
|
+
* Collections: `boards` (each board the subject is the only owner of, with its elements), `grants` (every role the subject
|
|
18
|
+
* holds on a board), `invites` (made by the subject, with no email), `authored_elements` (made by the subject on a board of someone else, with
|
|
19
|
+
* its `boardId`), `edited_elements` (made by someone else, last edited by the subject: `boardId` and `elementId`), `titles` (boards
|
|
20
|
+
* of someone else whose title the subject set), and `audit` (entries on a board where the subject acted as the principal, as the root of the chain, or was the target of a grant, as stored). It holds no format, version, or time.
|
|
21
|
+
*/
|
|
22
|
+
exportAccount(subject: Principal, deps: CanvasAccountDeps): Promise<Record<CanvasCollection, Json[]>>;
|
|
23
|
+
/**
|
|
24
|
+
* Deletes the boards the subject is the only owner of, with their grants, invites, links, and audit entries. On every other
|
|
25
|
+
* board it removes the subject's grant and invites, deletes the elements the subject made, sets the editor of the
|
|
26
|
+
* elements it edited to `(erased)`, clears a title it set, and sets its audit entries to `(erased)`. Counts by the same collection
|
|
27
|
+
* names. A second run counts zero. Run it with writes stopped: a write after the export is erased and not exported.
|
|
28
|
+
*/
|
|
29
|
+
eraseAccount(subject: Principal, deps: CanvasAccountDeps): Promise<Record<CanvasCollection, number>>;
|
|
30
|
+
}
|
|
31
|
+
declare const canvasAccount: CanvasAccount;
|
|
32
|
+
|
|
33
|
+
export { type CanvasAccount, type CanvasAccountDeps, type CanvasCollection, canvasAccount };
|
package/dist/account.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RESOURCE_TYPE
|
|
3
|
+
} from "./chunk-LMSMUGBF.js";
|
|
4
|
+
import {
|
|
5
|
+
fromBase64,
|
|
6
|
+
isBoardId,
|
|
7
|
+
toBase64
|
|
8
|
+
} from "./chunk-GLNF43N4.js";
|
|
9
|
+
import "./chunk-NWQED42L.js";
|
|
10
|
+
import "./chunk-BKG3FXX3.js";
|
|
11
|
+
import {
|
|
12
|
+
Board,
|
|
13
|
+
CanvasError,
|
|
14
|
+
PRINCIPAL_KINDS,
|
|
15
|
+
principalSchema
|
|
16
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
17
|
+
|
|
18
|
+
// src/account.ts
|
|
19
|
+
import { createSharing, ROLES, SUBJECT_KINDS } from "agentic-identity";
|
|
20
|
+
var ERASED = "(erased)";
|
|
21
|
+
var PAGE = 100;
|
|
22
|
+
async function* pages(store, collection, where) {
|
|
23
|
+
let cursor;
|
|
24
|
+
do {
|
|
25
|
+
const page = await store.query(collection, { where, limit: PAGE, ...cursor === void 0 ? {} : { cursor } });
|
|
26
|
+
yield* page.items;
|
|
27
|
+
cursor = page.nextCursor;
|
|
28
|
+
} while (cursor !== void 0);
|
|
29
|
+
}
|
|
30
|
+
var rows = async (store, collection, where) => {
|
|
31
|
+
const out = [];
|
|
32
|
+
for await (const row of pages(store, collection, where)) out.push(row);
|
|
33
|
+
return out;
|
|
34
|
+
};
|
|
35
|
+
var plain = (value) => JSON.parse(JSON.stringify(value));
|
|
36
|
+
function check(subject, deps) {
|
|
37
|
+
if (typeof deps?.store?.query !== "function") throw new CanvasError("INVALID", "canvasAccount needs deps.store");
|
|
38
|
+
for (const hook of [deps.saveRoom, deps.eraseRoom]) if (hook !== void 0 && typeof hook !== "function") throw new CanvasError("INVALID", "deps.saveRoom and deps.eraseRoom must be functions");
|
|
39
|
+
if (!principalSchema.safeParse(subject).success) throw new CanvasError("INVALID", "subject needs an id and a known kind");
|
|
40
|
+
}
|
|
41
|
+
async function held(store, subject) {
|
|
42
|
+
const all = [];
|
|
43
|
+
const sole = /* @__PURE__ */ new Set();
|
|
44
|
+
for await (const { id: grantId, data } of pages(store, "grants", { subjectKind: subject.kind, subjectId: subject.id })) {
|
|
45
|
+
const { resourceId: id, role } = data;
|
|
46
|
+
if (typeof id !== "string" || typeof role !== "string" || !ROLES.includes(role)) throw new CanvasError("INVALID", `grant "${grantId}" of the subject is damaged`);
|
|
47
|
+
const resource = await store.get("resources", id);
|
|
48
|
+
if (resource === null ? !isBoardId(id) : resource.type !== RESOURCE_TYPE) continue;
|
|
49
|
+
all.push({ grantId, resourceId: id, role });
|
|
50
|
+
if (resource === null || role !== "owner") continue;
|
|
51
|
+
const owners = await rows(store, "grants", { resourceId: id, role: "owner" });
|
|
52
|
+
if (owners.some((o) => typeof o.data.subjectId !== "string" || !SUBJECT_KINDS.includes(o.data.subjectKind))) throw new CanvasError("INVALID", `an owner grant on board "${id}" is damaged`);
|
|
53
|
+
if (owners.every((o) => o.data.subjectKind === subject.kind && o.data.subjectId === subject.id)) sole.add(id);
|
|
54
|
+
}
|
|
55
|
+
return { all, sole };
|
|
56
|
+
}
|
|
57
|
+
async function* boards(store, saveRoom) {
|
|
58
|
+
let cursor;
|
|
59
|
+
do {
|
|
60
|
+
const page = await store.query("boards", { limit: PAGE, ...cursor === void 0 ? {} : { cursor } });
|
|
61
|
+
for (const item of page.items) {
|
|
62
|
+
const { id } = item;
|
|
63
|
+
let data = item.data;
|
|
64
|
+
if (saveRoom) {
|
|
65
|
+
await saveRoom(id);
|
|
66
|
+
data = await store.get("boards", id) ?? data;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
yield { id, board: Board.open(fromBase64(String(data.update))) };
|
|
70
|
+
} catch {
|
|
71
|
+
throw new CanvasError("INVALID", `stored board "${id}" is damaged`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
cursor = page.nextCursor;
|
|
75
|
+
} while (cursor !== void 0);
|
|
76
|
+
}
|
|
77
|
+
var isSubject = (value, subject) => typeof value === "object" && value !== null && value.kind === subject.kind && value.id === subject.id;
|
|
78
|
+
async function auditOf(store, subject) {
|
|
79
|
+
const found = /* @__PURE__ */ new Map();
|
|
80
|
+
for (const field of ["principalId", "rootId", "targetId"]) for (const row of await rows(store, "audit", { [field]: subject.id })) found.set(row.id, row);
|
|
81
|
+
const out = [];
|
|
82
|
+
const known = (kind, extra = []) => PRINCIPAL_KINDS.includes(kind) || extra.includes(kind);
|
|
83
|
+
for (const row of found.values()) {
|
|
84
|
+
const { principalId, principalKind, chain, rootId, resourceId, targetKind, targetId } = row.data;
|
|
85
|
+
if (typeof resourceId !== "string" || !isBoardId(resourceId)) continue;
|
|
86
|
+
const resource = await store.get("resources", resourceId);
|
|
87
|
+
if (resource !== null && resource.type !== RESOURCE_TYPE) continue;
|
|
88
|
+
const first = Array.isArray(chain) ? chain[0] : null;
|
|
89
|
+
const damaged = principalId === subject.id && !known(principalKind, ["anonymous"]) || targetId === subject.id && !known(targetKind, ["invite", "link", "person", "app", "key"]) || (first?.id === subject.id || first === null && rootId === subject.id) && !known(first?.kind);
|
|
90
|
+
if (damaged) throw new CanvasError("INVALID", `audit entry "${row.id}" is damaged`);
|
|
91
|
+
if (isSubject({ kind: principalKind, id: principalId }, subject) || isSubject(first, subject) || isSubject({ kind: targetKind, id: targetId }, subject)) out.push(row);
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
async function invitesOf(store, subject) {
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const row of await rows(store, "invites", { createdBy: subject.id })) {
|
|
98
|
+
const { resourceId } = row.data;
|
|
99
|
+
if (typeof resourceId !== "string") throw new CanvasError("INVALID", `invite "${row.id}" of the subject is damaged`);
|
|
100
|
+
if (!isBoardId(resourceId)) continue;
|
|
101
|
+
const resource = await store.get("resources", resourceId);
|
|
102
|
+
if (resource === null || resource.type === RESOURCE_TYPE) out.push(row);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function scrub(data, subject) {
|
|
107
|
+
const erase = (step) => isSubject(step, subject) ? { ...step, id: ERASED } : step;
|
|
108
|
+
return {
|
|
109
|
+
...data,
|
|
110
|
+
...isSubject({ kind: data.principalKind, id: data.principalId }, subject) ? { principalId: ERASED } : {},
|
|
111
|
+
...data.rootId === subject.id ? { rootId: ERASED } : {},
|
|
112
|
+
...isSubject({ kind: data.targetKind, id: data.targetId }, subject) ? { targetId: ERASED } : {},
|
|
113
|
+
...Array.isArray(data.chain) ? { chain: data.chain.map(erase) } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
var canvasAccount = {
|
|
117
|
+
id: "canvas",
|
|
118
|
+
async exportAccount(subject, deps) {
|
|
119
|
+
check(subject, deps);
|
|
120
|
+
const { store } = deps;
|
|
121
|
+
const out = { boards: [], grants: [], invites: [], authored_elements: [], edited_elements: [], titles: [], audit: [] };
|
|
122
|
+
const mine = await held(store, subject);
|
|
123
|
+
const owned = /* @__PURE__ */ new Map();
|
|
124
|
+
for await (const { id, board } of boards(store, deps.saveRoom)) {
|
|
125
|
+
if (mine.sole.has(id)) {
|
|
126
|
+
owned.set(id, plain([{ id, title: board.title, elements: board.rawElements() }])[0]);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const found = board.stampedBy(subject);
|
|
130
|
+
out.authored_elements.push(...plain(found.authored.map((element) => ({ boardId: id, element }))));
|
|
131
|
+
out.edited_elements.push(...plain(found.edited.map((elementId) => ({ boardId: id, elementId }))));
|
|
132
|
+
if (found.titleBy) out.titles.push(...plain([{ boardId: id, title: board.title }]));
|
|
133
|
+
}
|
|
134
|
+
for (const g of mine.all) {
|
|
135
|
+
out.grants.push(...plain([{ resourceId: g.resourceId, subjectKind: subject.kind, subjectId: subject.id, role: g.role }]));
|
|
136
|
+
}
|
|
137
|
+
for (const id of mine.sole) if (owned.has(id)) out.boards.push(owned.get(id));
|
|
138
|
+
for (const { id, data } of await invitesOf(store, subject)) {
|
|
139
|
+
const { resourceId, role, expiresAt, acceptedAt, revokedAt } = data;
|
|
140
|
+
out.invites.push(...plain([{ id, resourceId, role, expiresAt, acceptedAt, revokedAt }]));
|
|
141
|
+
}
|
|
142
|
+
out.audit.push(...plain((await auditOf(store, subject)).map((r) => r.data)));
|
|
143
|
+
return out;
|
|
144
|
+
},
|
|
145
|
+
async eraseAccount(subject, deps) {
|
|
146
|
+
check(subject, deps);
|
|
147
|
+
const { store } = deps;
|
|
148
|
+
const sharing = createSharing(store);
|
|
149
|
+
const total = { boards: 0, grants: 0, invites: 0, authored_elements: 0, edited_elements: 0, titles: 0, audit: 0 };
|
|
150
|
+
const ctx = { principalId: subject.id, kind: subject.kind, chain: [{ id: subject.id, kind: subject.kind }], scopes: [`${RESOURCE_TYPE}:admin`] };
|
|
151
|
+
const mine = await held(store, subject);
|
|
152
|
+
const stamped = [];
|
|
153
|
+
for await (const { id, board } of boards(store, deps.saveRoom)) {
|
|
154
|
+
if (mine.sole.has(id)) continue;
|
|
155
|
+
const found = board.stampedBy(subject);
|
|
156
|
+
if (found.authored.length > 0 || found.edited.length > 0 || found.titleBy) stamped.push(id);
|
|
157
|
+
}
|
|
158
|
+
for (const id of mine.sole) {
|
|
159
|
+
if ((await rows(store, "resources", { parentId: id })).length > 0) throw new CanvasError("INVALID", `board "${id}" has child resources: remove them first`);
|
|
160
|
+
if (!(await sharing.can(ctx, "admin", id)).allowed) throw new CanvasError("INVALID", `the owner grant on board "${id}" is not valid: an operator repairs it first`);
|
|
161
|
+
}
|
|
162
|
+
for (const { id, data } of await auditOf(store, subject)) {
|
|
163
|
+
await store.put("audit", id, scrub(data, subject));
|
|
164
|
+
total.audit += 1;
|
|
165
|
+
}
|
|
166
|
+
for (const { id } of await invitesOf(store, subject)) {
|
|
167
|
+
await store.delete("invites", id);
|
|
168
|
+
total.invites += 1;
|
|
169
|
+
}
|
|
170
|
+
for (const id of mine.sole) {
|
|
171
|
+
await deps.eraseRoom?.(id);
|
|
172
|
+
await store.delete("boards", id);
|
|
173
|
+
await deps.eraseRoom?.(id);
|
|
174
|
+
await sharing.deleteResource(ctx, id).catch((e) => {
|
|
175
|
+
if (e instanceof Error && (e.name === "EraseRefusedError" || e.message === "forbidden")) throw new CanvasError("INVALID", e.message);
|
|
176
|
+
throw e;
|
|
177
|
+
});
|
|
178
|
+
total.boards += 1;
|
|
179
|
+
}
|
|
180
|
+
for (const id of stamped) {
|
|
181
|
+
const done = await store.transaction(async (tx) => {
|
|
182
|
+
const doc = await tx.get("boards", id);
|
|
183
|
+
if (!doc) return null;
|
|
184
|
+
let board;
|
|
185
|
+
try {
|
|
186
|
+
board = Board.open(fromBase64(String(doc.update)));
|
|
187
|
+
} catch {
|
|
188
|
+
throw new CanvasError("INVALID", `stored board "${id}" is damaged`);
|
|
189
|
+
}
|
|
190
|
+
const counts = board.eraseStamps(subject, { kind: subject.kind, id: ERASED });
|
|
191
|
+
await tx.put("boards", id, { update: toBase64(board.encode()) });
|
|
192
|
+
return counts;
|
|
193
|
+
});
|
|
194
|
+
await deps.eraseRoom?.(id);
|
|
195
|
+
if (!done) continue;
|
|
196
|
+
total.authored_elements += done.authored;
|
|
197
|
+
total.edited_elements += done.edited;
|
|
198
|
+
total.titles += done.title ? 1 : 0;
|
|
199
|
+
}
|
|
200
|
+
for (const g of mine.all) {
|
|
201
|
+
if (mine.sole.has(g.resourceId)) {
|
|
202
|
+
total.grants += 1;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
await store.delete("grants", g.grantId);
|
|
206
|
+
total.grants += 1;
|
|
207
|
+
}
|
|
208
|
+
return total;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
export {
|
|
212
|
+
canvasAccount
|
|
213
|
+
};
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CanvasAccess
|
|
3
|
+
} from "./chunk-LMSMUGBF.js";
|
|
1
4
|
import {
|
|
2
5
|
CanvasService,
|
|
3
|
-
MAX_LIVE_BYTES
|
|
4
|
-
|
|
5
|
-
isStoreId
|
|
6
|
-
} from "./chunk-Z6KLGAFP.js";
|
|
6
|
+
MAX_LIVE_BYTES
|
|
7
|
+
} from "./chunk-GLNF43N4.js";
|
|
7
8
|
import {
|
|
8
9
|
CanvasError
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
10
11
|
|
|
11
12
|
// src/worker.ts
|
|
12
|
-
import { createStoreAuth, hasScope
|
|
13
|
+
import { createStoreAuth, hasScope, parsePrincipal, parseRegion } from "agentic-identity";
|
|
13
14
|
import { createD1Store, createR2Blobs, D1_SCHEMA } from "agentic-identity/cloudflare";
|
|
14
15
|
import { acceptWebSocketProtocol, principalFromWebSocketRequest } from "agentic-identity/http";
|
|
15
16
|
|
|
@@ -1790,150 +1791,6 @@ var Hono3 = class extends Hono {
|
|
|
1790
1791
|
}
|
|
1791
1792
|
};
|
|
1792
1793
|
|
|
1793
|
-
// src/access.ts
|
|
1794
|
-
import { createSharing, EraseRefusedError, hasScope, ROLES, SUBJECT_KINDS } from "agentic-identity";
|
|
1795
|
-
var RESOURCE_TYPE = "canvas";
|
|
1796
|
-
var SHARE_ROLES = ROLES.filter((r) => r !== "owner");
|
|
1797
|
-
var BAD_INPUT = /^(give an email or a handle|invalid handle|invalid email|invalid subject( kind "[^"]*")?|the last owner cannot be removed|expiresInMs must be .*|expiresAt must be .*|invalid role .*)$/;
|
|
1798
|
-
function refuse(error) {
|
|
1799
|
-
if (error instanceof EraseRefusedError) throw new CanvasError("INVALID", error.message);
|
|
1800
|
-
if (error instanceof Error && error.message === "forbidden") throw new CanvasError("FORBIDDEN", "you may not do this on this board");
|
|
1801
|
-
if (error instanceof Error && BAD_INPUT.test(error.message)) throw new CanvasError("INVALID", error.message);
|
|
1802
|
-
throw error;
|
|
1803
|
-
}
|
|
1804
|
-
var oneOf = (value, list, name) => {
|
|
1805
|
-
if (typeof value !== "string" || !list.includes(value)) throw new CanvasError("INVALID", `${name} must be one of: ${list.join(", ")}`);
|
|
1806
|
-
return value;
|
|
1807
|
-
};
|
|
1808
|
-
var storeId = (value, name) => {
|
|
1809
|
-
if (typeof value !== "string" || !isStoreId(value)) throw new CanvasError("INVALID", `${name} must be 1 to 128 of A-Z, a-z, 0-9, _ and -, and not like __x__`);
|
|
1810
|
-
return value;
|
|
1811
|
-
};
|
|
1812
|
-
var word = (value, name) => {
|
|
1813
|
-
if (typeof value !== "string" || value === "") throw new CanvasError("INVALID", `${name} must be a non-empty string`);
|
|
1814
|
-
return value;
|
|
1815
|
-
};
|
|
1816
|
-
var lifetime = (value, name) => {
|
|
1817
|
-
if (value === void 0) return void 0;
|
|
1818
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new CanvasError("INVALID", `${name} must be a whole number of milliseconds above 0`);
|
|
1819
|
-
return value;
|
|
1820
|
-
};
|
|
1821
|
-
var CanvasAccess = class {
|
|
1822
|
-
constructor(store, options = {}) {
|
|
1823
|
-
this.store = store;
|
|
1824
|
-
this.sharing = createSharing(store, options);
|
|
1825
|
-
this.now = options.now ?? Date.now;
|
|
1826
|
-
}
|
|
1827
|
-
store;
|
|
1828
|
-
sharing;
|
|
1829
|
-
now;
|
|
1830
|
-
/** 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. */
|
|
1831
|
-
known(id) {
|
|
1832
|
-
if (!isBoardId(id)) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
|
|
1833
|
-
}
|
|
1834
|
-
/** Make the actor the owner of a new board. */
|
|
1835
|
-
async register(ctx, id) {
|
|
1836
|
-
await this.sharing.createResource(ctx, { id, type: RESOURCE_TYPE }).catch((error) => {
|
|
1837
|
-
if (error instanceof Error && error.message === "resource exists") throw new CanvasError("DUPLICATE_ID", `board "${id}" already exists`);
|
|
1838
|
-
if (error instanceof Error && error.message === "resource id has old audit entries") throw new CanvasError("UNAVAILABLE", "this board id was used a moment ago. Try again, or use another id");
|
|
1839
|
-
return refuse(error);
|
|
1840
|
-
});
|
|
1841
|
-
}
|
|
1842
|
-
/** Remove the sharing records of a board. Needs an owner. */
|
|
1843
|
-
async unregister(ctx, id) {
|
|
1844
|
-
await this.sharing.deleteResource(ctx, id).catch(refuse);
|
|
1845
|
-
}
|
|
1846
|
-
/** True if the board has sharing records: it was made by an identity credential. */
|
|
1847
|
-
async registered(id) {
|
|
1848
|
-
if (!isBoardId(id)) return false;
|
|
1849
|
-
return (await this.store.get("resources", id))?.type === RESOURCE_TYPE;
|
|
1850
|
-
}
|
|
1851
|
-
/**
|
|
1852
|
-
* True if the caller may do `action` on the board. A link token adds the role of its link. It writes an audit entry.
|
|
1853
|
-
* An id that belongs to a resource of another type, in a Store shared with another app, is no board: it is denied.
|
|
1854
|
-
*/
|
|
1855
|
-
async allowed(ctx, action, id, linkToken) {
|
|
1856
|
-
if (!await this.registered(id)) return false;
|
|
1857
|
-
return (await this.sharing.can(ctx, action, id, linkToken === void 0 ? {} : { linkToken })).allowed;
|
|
1858
|
-
}
|
|
1859
|
-
/** Throw NOT_FOUND for a caller who cannot read the board, and FORBIDDEN for one who can read it but not do `action`. */
|
|
1860
|
-
async require(ctx, action, id, linkToken) {
|
|
1861
|
-
if (await this.allowed(ctx, action, id, linkToken)) return;
|
|
1862
|
-
if (action === "read" || !await this.allowed(ctx, "read", id, linkToken)) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
|
|
1863
|
-
throw new CanvasError("FORBIDDEN", `this needs the ${action === "admin" ? "owner" : "editor"} role on the board`);
|
|
1864
|
-
}
|
|
1865
|
-
/** The ids of the boards a person or agent holds a role on, the first 100. A credential without `canvas:read` gets none. */
|
|
1866
|
-
async readable(ctx) {
|
|
1867
|
-
const root = ctx.chain[0];
|
|
1868
|
-
if (!root || !SUBJECT_KINDS.includes(root.kind) || !hasScope(ctx.scopes, `${RESOURCE_TYPE}:read`)) return [];
|
|
1869
|
-
const ids = [];
|
|
1870
|
-
let cursor;
|
|
1871
|
-
for (let pages = 0; pages < 10 && ids.length < 100; pages++) {
|
|
1872
|
-
const page = await this.store.query("grants", { where: { subjectKind: root.kind, subjectId: root.id }, limit: 100, ...cursor === void 0 ? {} : { cursor } });
|
|
1873
|
-
for (const { data } of page.items) {
|
|
1874
|
-
if (ids.length >= 100 || typeof data.resourceId !== "string" || !ROLES.includes(data.role)) continue;
|
|
1875
|
-
if (await this.registered(data.resourceId)) ids.push(data.resourceId);
|
|
1876
|
-
}
|
|
1877
|
-
if ((cursor = page.nextCursor) === void 0) break;
|
|
1878
|
-
}
|
|
1879
|
-
return ids;
|
|
1880
|
-
}
|
|
1881
|
-
async grant(ctx, id, input) {
|
|
1882
|
-
this.known(id);
|
|
1883
|
-
const subject = { kind: oneOf(input.subjectKind, SUBJECT_KINDS, "subjectKind"), id: storeId(input.subjectId, "subjectId") };
|
|
1884
|
-
await this.sharing.grant(ctx, id, subject, oneOf(input.role, ROLES, "role")).catch(refuse);
|
|
1885
|
-
return { ok: true };
|
|
1886
|
-
}
|
|
1887
|
-
async revoke(ctx, id, input) {
|
|
1888
|
-
this.known(id);
|
|
1889
|
-
const subject = { kind: oneOf(input.subjectKind, SUBJECT_KINDS, "subjectKind"), id: storeId(input.subjectId, "subjectId") };
|
|
1890
|
-
await this.sharing.revokeGrant(ctx, id, subject).catch(refuse);
|
|
1891
|
-
return { ok: true };
|
|
1892
|
-
}
|
|
1893
|
-
/** The grants and the invites on a board. An invite never shows its email. Links are not listed: a token shows once. */
|
|
1894
|
-
async list(ctx, id) {
|
|
1895
|
-
this.known(id);
|
|
1896
|
-
const [grants, invites] = await Promise.all([this.sharing.listGrants(ctx, id, { limit: 200 }), this.sharing.listInvites(ctx, id, { limit: 200 })]).catch(refuse);
|
|
1897
|
-
return { grants: grants.grants, invites: invites.invites };
|
|
1898
|
-
}
|
|
1899
|
-
async invite(ctx, id, input) {
|
|
1900
|
-
this.known(id);
|
|
1901
|
-
const role = oneOf(input.role, SHARE_ROLES, "role");
|
|
1902
|
-
const expiresInMs = lifetime(input.expiresInMs, "expiresInMs");
|
|
1903
|
-
const who = input.email !== void 0 ? { email: word(input.email, "email") } : input.handle !== void 0 ? { handle: word(input.handle, "handle") } : {};
|
|
1904
|
-
if (input.email !== void 0 && input.handle !== void 0) throw new CanvasError("INVALID", "give an email or a handle, not both");
|
|
1905
|
-
return this.sharing.createInvite(ctx, id, { role, ...who, ...expiresInMs === void 0 ? {} : { expiresInMs } }).catch(refuse);
|
|
1906
|
-
}
|
|
1907
|
-
/** The email comes from a verified sign-in and the handle from the agent's own record, never from the request. */
|
|
1908
|
-
async accept(ctx, inviteId) {
|
|
1909
|
-
if (!isStoreId(inviteId)) throw new CanvasError("FORBIDDEN", "you may not do this on this board");
|
|
1910
|
-
const user = await this.store.get("users", ctx.principalId);
|
|
1911
|
-
const agent = await this.store.get("principals", ctx.principalId);
|
|
1912
|
-
const identity = {
|
|
1913
|
-
...user?.emailVerified === true && typeof user.email === "string" ? { email: user.email } : {},
|
|
1914
|
-
...typeof agent?.handle === "string" ? { handle: agent.handle } : {}
|
|
1915
|
-
};
|
|
1916
|
-
await this.sharing.acceptInvite(ctx, word(inviteId, "id"), identity).catch(refuse);
|
|
1917
|
-
return { ok: true };
|
|
1918
|
-
}
|
|
1919
|
-
/** The token shows once. Only its hash is stored. */
|
|
1920
|
-
async createLink(ctx, id, input) {
|
|
1921
|
-
this.known(id);
|
|
1922
|
-
const role = oneOf(input.role, SHARE_ROLES, "role");
|
|
1923
|
-
const expiresInMs = lifetime(input.expiresInMs, "expiresInMs");
|
|
1924
|
-
if (input.signedInOnly !== void 0 && typeof input.signedInOnly !== "boolean") throw new CanvasError("INVALID", "signedInOnly must be true or false");
|
|
1925
|
-
return this.sharing.createLink(ctx, id, { role, signedInOnly: input.signedInOnly === true, ...expiresInMs === void 0 ? {} : { expiresAt: this.now() + expiresInMs } }).catch(refuse);
|
|
1926
|
-
}
|
|
1927
|
-
/** Revoke a link of this board. A link of another board is NOT_FOUND here, even for an owner of both. */
|
|
1928
|
-
async revokeLink(ctx, id, linkId) {
|
|
1929
|
-
this.known(id);
|
|
1930
|
-
const link = isStoreId(linkId) ? await this.store.get("links", linkId) : null;
|
|
1931
|
-
if (!link || link.resourceId !== id) throw new CanvasError("NOT_FOUND", "no such link on this board");
|
|
1932
|
-
await this.sharing.revokeLink(ctx, linkId).catch(refuse);
|
|
1933
|
-
return { ok: true };
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1937
1794
|
// src/room-sync.ts
|
|
1938
1795
|
import * as Y from "yjs";
|
|
1939
1796
|
var SEEDER = { kind: "agent", id: "canvas-room" };
|
|
@@ -2171,7 +2028,7 @@ async function identify(request, env, store) {
|
|
|
2171
2028
|
const found = await principalFromWebSocketRequest(request, auth, { protocol: WS_PROTOCOL, scope: "canvas:read" });
|
|
2172
2029
|
if (failure() !== void 0) throw failure();
|
|
2173
2030
|
if (found instanceof Response) return null;
|
|
2174
|
-
return { actor: { kind: found.kind, id: found.principalId }, role:
|
|
2031
|
+
return { actor: { kind: found.kind, id: found.principalId }, role: hasScope(found.scopes, "canvas:write") ? "editor" : "viewer", principal: found, dev: dev() };
|
|
2175
2032
|
}
|
|
2176
2033
|
var svgResponse = (svg) => new Response(svg, { headers: { "content-type": "image/svg+xml", "x-content-type-options": "nosniff", "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; img-src data:" } });
|
|
2177
2034
|
function sniffImage(bytes) {
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
CanvasError,
|
|
3
3
|
PRINCIPAL_KINDS,
|
|
4
4
|
SHAPE_TYPES
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
6
6
|
|
|
7
7
|
// src/version.ts
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
var VERSION = true ? "0.
|
|
9
|
+
var VERSION = true ? "0.13.0" : readPackageVersion();
|
|
10
10
|
|
|
11
11
|
// src/brand.ts
|
|
12
12
|
var BRAND = {
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
drawFlow,
|
|
6
6
|
toSvg
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BKG3FXX3.js";
|
|
8
8
|
import {
|
|
9
9
|
Board,
|
|
10
10
|
CanvasError,
|
|
11
11
|
principalSchema,
|
|
12
12
|
readBoard
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
14
14
|
|
|
15
15
|
// src/service.ts
|
|
16
16
|
import * as Y from "yjs";
|
|
@@ -224,6 +224,8 @@ function decodeStored(doc, id, decode) {
|
|
|
224
224
|
|
|
225
225
|
export {
|
|
226
226
|
MAX_LIVE_BYTES,
|
|
227
|
+
toBase64,
|
|
228
|
+
fromBase64,
|
|
227
229
|
isStoreId,
|
|
228
230
|
isBoardId,
|
|
229
231
|
CanvasService
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBoardId,
|
|
3
|
+
isStoreId
|
|
4
|
+
} from "./chunk-GLNF43N4.js";
|
|
5
|
+
import {
|
|
6
|
+
CanvasError
|
|
7
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
8
|
+
|
|
9
|
+
// src/access.ts
|
|
10
|
+
import { createSharing, EraseRefusedError, hasScope, ROLES, SUBJECT_KINDS } from "agentic-identity";
|
|
11
|
+
var RESOURCE_TYPE = "canvas";
|
|
12
|
+
var SHARE_ROLES = ROLES.filter((r) => r !== "owner");
|
|
13
|
+
var BAD_INPUT = /^(give an email or a handle|invalid handle|invalid email|invalid subject( kind "[^"]*")?|the last owner cannot be removed|expiresInMs must be .*|expiresAt must be .*|invalid role .*)$/;
|
|
14
|
+
function refuse(error) {
|
|
15
|
+
if (error instanceof EraseRefusedError) throw new CanvasError("INVALID", error.message);
|
|
16
|
+
if (error instanceof Error && error.message === "forbidden") throw new CanvasError("FORBIDDEN", "you may not do this on this board");
|
|
17
|
+
if (error instanceof Error && BAD_INPUT.test(error.message)) throw new CanvasError("INVALID", error.message);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
var oneOf = (value, list, name) => {
|
|
21
|
+
if (typeof value !== "string" || !list.includes(value)) throw new CanvasError("INVALID", `${name} must be one of: ${list.join(", ")}`);
|
|
22
|
+
return value;
|
|
23
|
+
};
|
|
24
|
+
var storeId = (value, name) => {
|
|
25
|
+
if (typeof value !== "string" || !isStoreId(value)) throw new CanvasError("INVALID", `${name} must be 1 to 128 of A-Z, a-z, 0-9, _ and -, and not like __x__`);
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var word = (value, name) => {
|
|
29
|
+
if (typeof value !== "string" || value === "") throw new CanvasError("INVALID", `${name} must be a non-empty string`);
|
|
30
|
+
return value;
|
|
31
|
+
};
|
|
32
|
+
var lifetime = (value, name) => {
|
|
33
|
+
if (value === void 0) return void 0;
|
|
34
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) throw new CanvasError("INVALID", `${name} must be a whole number of milliseconds above 0`);
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
var CanvasAccess = class {
|
|
38
|
+
constructor(store, options = {}) {
|
|
39
|
+
this.store = store;
|
|
40
|
+
this.sharing = createSharing(store, options);
|
|
41
|
+
this.now = options.now ?? Date.now;
|
|
42
|
+
}
|
|
43
|
+
store;
|
|
44
|
+
sharing;
|
|
45
|
+
now;
|
|
46
|
+
/** 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. */
|
|
47
|
+
known(id) {
|
|
48
|
+
if (!isBoardId(id)) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
|
|
49
|
+
}
|
|
50
|
+
/** Make the actor the owner of a new board. */
|
|
51
|
+
async register(ctx, id) {
|
|
52
|
+
await this.sharing.createResource(ctx, { id, type: RESOURCE_TYPE }).catch((error) => {
|
|
53
|
+
if (error instanceof Error && error.message === "resource exists") throw new CanvasError("DUPLICATE_ID", `board "${id}" already exists`);
|
|
54
|
+
if (error instanceof Error && error.message === "resource id has old audit entries") throw new CanvasError("UNAVAILABLE", "this board id was used a moment ago. Try again, or use another id");
|
|
55
|
+
return refuse(error);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Remove the sharing records of a board. Needs an owner. */
|
|
59
|
+
async unregister(ctx, id) {
|
|
60
|
+
await this.sharing.deleteResource(ctx, id).catch(refuse);
|
|
61
|
+
}
|
|
62
|
+
/** True if the board has sharing records: it was made by an identity credential. */
|
|
63
|
+
async registered(id) {
|
|
64
|
+
if (!isBoardId(id)) return false;
|
|
65
|
+
return (await this.store.get("resources", id))?.type === RESOURCE_TYPE;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True if the caller may do `action` on the board. A link token adds the role of its link. It writes an audit entry.
|
|
69
|
+
* An id that belongs to a resource of another type, in a Store shared with another app, is no board: it is denied.
|
|
70
|
+
*/
|
|
71
|
+
async allowed(ctx, action, id, linkToken) {
|
|
72
|
+
if (!await this.registered(id)) return false;
|
|
73
|
+
return (await this.sharing.can(ctx, action, id, linkToken === void 0 ? {} : { linkToken })).allowed;
|
|
74
|
+
}
|
|
75
|
+
/** Throw NOT_FOUND for a caller who cannot read the board, and FORBIDDEN for one who can read it but not do `action`. */
|
|
76
|
+
async require(ctx, action, id, linkToken) {
|
|
77
|
+
if (await this.allowed(ctx, action, id, linkToken)) return;
|
|
78
|
+
if (action === "read" || !await this.allowed(ctx, "read", id, linkToken)) throw new CanvasError("NOT_FOUND", `no board "${id}"`);
|
|
79
|
+
throw new CanvasError("FORBIDDEN", `this needs the ${action === "admin" ? "owner" : "editor"} role on the board`);
|
|
80
|
+
}
|
|
81
|
+
/** The ids of the boards a person or agent holds a role on, the first 100. A credential without `canvas:read` gets none. */
|
|
82
|
+
async readable(ctx) {
|
|
83
|
+
const root = ctx.chain[0];
|
|
84
|
+
if (!root || !SUBJECT_KINDS.includes(root.kind) || !hasScope(ctx.scopes, `${RESOURCE_TYPE}:read`)) return [];
|
|
85
|
+
const ids = [];
|
|
86
|
+
let cursor;
|
|
87
|
+
for (let pages = 0; pages < 10 && ids.length < 100; pages++) {
|
|
88
|
+
const page = await this.store.query("grants", { where: { subjectKind: root.kind, subjectId: root.id }, limit: 100, ...cursor === void 0 ? {} : { cursor } });
|
|
89
|
+
for (const { data } of page.items) {
|
|
90
|
+
if (ids.length >= 100 || typeof data.resourceId !== "string" || !ROLES.includes(data.role)) continue;
|
|
91
|
+
if (await this.registered(data.resourceId)) ids.push(data.resourceId);
|
|
92
|
+
}
|
|
93
|
+
if ((cursor = page.nextCursor) === void 0) break;
|
|
94
|
+
}
|
|
95
|
+
return ids;
|
|
96
|
+
}
|
|
97
|
+
async grant(ctx, id, input) {
|
|
98
|
+
this.known(id);
|
|
99
|
+
const subject = { kind: oneOf(input.subjectKind, SUBJECT_KINDS, "subjectKind"), id: storeId(input.subjectId, "subjectId") };
|
|
100
|
+
await this.sharing.grant(ctx, id, subject, oneOf(input.role, ROLES, "role")).catch(refuse);
|
|
101
|
+
return { ok: true };
|
|
102
|
+
}
|
|
103
|
+
async revoke(ctx, id, input) {
|
|
104
|
+
this.known(id);
|
|
105
|
+
const subject = { kind: oneOf(input.subjectKind, SUBJECT_KINDS, "subjectKind"), id: storeId(input.subjectId, "subjectId") };
|
|
106
|
+
await this.sharing.revokeGrant(ctx, id, subject).catch(refuse);
|
|
107
|
+
return { ok: true };
|
|
108
|
+
}
|
|
109
|
+
/** The grants and the invites on a board. An invite never shows its email. Links are not listed: a token shows once. */
|
|
110
|
+
async list(ctx, id) {
|
|
111
|
+
this.known(id);
|
|
112
|
+
const [grants, invites] = await Promise.all([this.sharing.listGrants(ctx, id, { limit: 200 }), this.sharing.listInvites(ctx, id, { limit: 200 })]).catch(refuse);
|
|
113
|
+
return { grants: grants.grants, invites: invites.invites };
|
|
114
|
+
}
|
|
115
|
+
async invite(ctx, id, input) {
|
|
116
|
+
this.known(id);
|
|
117
|
+
const role = oneOf(input.role, SHARE_ROLES, "role");
|
|
118
|
+
const expiresInMs = lifetime(input.expiresInMs, "expiresInMs");
|
|
119
|
+
const who = input.email !== void 0 ? { email: word(input.email, "email") } : input.handle !== void 0 ? { handle: word(input.handle, "handle") } : {};
|
|
120
|
+
if (input.email !== void 0 && input.handle !== void 0) throw new CanvasError("INVALID", "give an email or a handle, not both");
|
|
121
|
+
return this.sharing.createInvite(ctx, id, { role, ...who, ...expiresInMs === void 0 ? {} : { expiresInMs } }).catch(refuse);
|
|
122
|
+
}
|
|
123
|
+
/** The email comes from a verified sign-in and the handle from the agent's own record, never from the request. */
|
|
124
|
+
async accept(ctx, inviteId) {
|
|
125
|
+
if (!isStoreId(inviteId)) throw new CanvasError("FORBIDDEN", "you may not do this on this board");
|
|
126
|
+
const user = await this.store.get("users", ctx.principalId);
|
|
127
|
+
const agent = await this.store.get("principals", ctx.principalId);
|
|
128
|
+
const identity = {
|
|
129
|
+
...user?.emailVerified === true && typeof user.email === "string" ? { email: user.email } : {},
|
|
130
|
+
...typeof agent?.handle === "string" ? { handle: agent.handle } : {}
|
|
131
|
+
};
|
|
132
|
+
await this.sharing.acceptInvite(ctx, word(inviteId, "id"), identity).catch(refuse);
|
|
133
|
+
return { ok: true };
|
|
134
|
+
}
|
|
135
|
+
/** The token shows once. Only its hash is stored. */
|
|
136
|
+
async createLink(ctx, id, input) {
|
|
137
|
+
this.known(id);
|
|
138
|
+
const role = oneOf(input.role, SHARE_ROLES, "role");
|
|
139
|
+
const expiresInMs = lifetime(input.expiresInMs, "expiresInMs");
|
|
140
|
+
if (input.signedInOnly !== void 0 && typeof input.signedInOnly !== "boolean") throw new CanvasError("INVALID", "signedInOnly must be true or false");
|
|
141
|
+
return this.sharing.createLink(ctx, id, { role, signedInOnly: input.signedInOnly === true, ...expiresInMs === void 0 ? {} : { expiresAt: this.now() + expiresInMs } }).catch(refuse);
|
|
142
|
+
}
|
|
143
|
+
/** Revoke a link of this board. A link of another board is NOT_FOUND here, even for an owner of both. */
|
|
144
|
+
async revokeLink(ctx, id, linkId) {
|
|
145
|
+
this.known(id);
|
|
146
|
+
const link = isStoreId(linkId) ? await this.store.get("links", linkId) : null;
|
|
147
|
+
if (!link || link.resourceId !== id) throw new CanvasError("NOT_FOUND", "no such link on this board");
|
|
148
|
+
await this.sharing.revokeLink(ctx, linkId).catch(refuse);
|
|
149
|
+
return { ok: true };
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
RESOURCE_TYPE,
|
|
155
|
+
CanvasAccess
|
|
156
|
+
};
|
|
@@ -94,6 +94,7 @@ var isBindable = (type) => typeof type === "string" && type !== "connector" && t
|
|
|
94
94
|
var isValidZ = (value) => typeof value === "number" && Number.isFinite(value) && Math.abs(value) <= MAX_Z;
|
|
95
95
|
var clampZ = (value) => Math.min(MAX_Z, Math.max(-MAX_Z, value));
|
|
96
96
|
var samePrincipal = (a, b) => a.kind === b.kind && a.id === b.id;
|
|
97
|
+
var isStamp = (value, who) => typeof value === "object" && value !== null && value.kind === who.kind && value.id === who.id;
|
|
97
98
|
var hasId = (e) => typeof e === "object" && e !== null && typeof e.id === "string";
|
|
98
99
|
var Board = class _Board {
|
|
99
100
|
doc;
|
|
@@ -348,6 +349,62 @@ var Board = class _Board {
|
|
|
348
349
|
this.write(plan, by);
|
|
349
350
|
return plan.map(([memberId]) => memberId);
|
|
350
351
|
}
|
|
352
|
+
/** Every stored entry as it is, valid or not, with its key as `id`. */
|
|
353
|
+
rawElements() {
|
|
354
|
+
return [...this.elements].map(([key, entry]) => ({ ...entry instanceof Y.Map ? entry.toJSON() : {}, id: key }));
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The raw stored elements that `who` made (`author`) and the ids of those that `who` last edited but did not make.
|
|
358
|
+
* It reads entries as they are, with no element checks, so a damaged entry still shows. Account export uses it.
|
|
359
|
+
*/
|
|
360
|
+
stampedBy(who) {
|
|
361
|
+
const authored = [];
|
|
362
|
+
const edited = [];
|
|
363
|
+
for (const [key, entry] of this.elements) {
|
|
364
|
+
if (!(entry instanceof Y.Map)) continue;
|
|
365
|
+
if (isStamp(entry.get("author"), who)) authored.push({ ...entry.toJSON(), id: key });
|
|
366
|
+
else if (isStamp(entry.get("editedBy"), who)) edited.push(key);
|
|
367
|
+
}
|
|
368
|
+
return { authored, edited, titleBy: isStamp(this.meta.get("titleBy"), who) };
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Erase `who` from the board. An element they made is deleted alone: its members go to its parent group, and a
|
|
372
|
+
* connector of someone else that was bound to it goes too, because it would be hidden. An element they only edited
|
|
373
|
+
* keeps its content, and its editor becomes `(erased)`. A title they set is cleared. Idempotent. Returns the counts.
|
|
374
|
+
*/
|
|
375
|
+
eraseStamps(who, erased) {
|
|
376
|
+
const mine = /* @__PURE__ */ new Set();
|
|
377
|
+
for (const [key, entry] of this.elements) if (entry instanceof Y.Map && isStamp(entry.get("author"), who)) mine.add(key);
|
|
378
|
+
const counts = { authored: mine.size, edited: 0, dangling: 0, title: isStamp(this.meta.get("titleBy"), who) };
|
|
379
|
+
this.doc.transact(() => {
|
|
380
|
+
for (const key of mine) {
|
|
381
|
+
const parent = this.elements.get(key).get("groupId");
|
|
382
|
+
for (const [, other] of this.elements) {
|
|
383
|
+
if (other instanceof Y.Map && other.get("groupId") === key) {
|
|
384
|
+
if (typeof parent === "string") other.set("groupId", parent);
|
|
385
|
+
else other.delete("groupId");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
mine.forEach((key) => this.elements.delete(key));
|
|
390
|
+
for (const [key, entry] of [...this.elements]) {
|
|
391
|
+
if (!(entry instanceof Y.Map)) continue;
|
|
392
|
+
const ends = [entry.get("from"), entry.get("to")];
|
|
393
|
+
if (entry.get("type") === "connector" && ends.some((e) => hasId(e) && mine.has(e.id))) {
|
|
394
|
+
this.elements.delete(key);
|
|
395
|
+
counts.dangling += 1;
|
|
396
|
+
} else if (isStamp(entry.get("editedBy"), who)) {
|
|
397
|
+
entry.set("editedBy", erased);
|
|
398
|
+
counts.edited += 1;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (counts.title) {
|
|
402
|
+
this.meta.delete("title");
|
|
403
|
+
this.meta.set("titleBy", erased);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
return counts;
|
|
407
|
+
}
|
|
351
408
|
/** Delete an element. A shape takes its connectors and a group takes its members. Returns the ids removed. */
|
|
352
409
|
remove(elementId) {
|
|
353
410
|
this.require(elementId);
|
package/dist/cli.js
CHANGED
|
@@ -5,18 +5,18 @@ import {
|
|
|
5
5
|
operatorActor,
|
|
6
6
|
runMcp,
|
|
7
7
|
svgToPng
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AI5NWPEW.js";
|
|
9
9
|
import {
|
|
10
10
|
openLocalStore
|
|
11
11
|
} from "./chunk-NRLF6MGR.js";
|
|
12
12
|
import {
|
|
13
13
|
CanvasService
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GLNF43N4.js";
|
|
15
15
|
import "./chunk-NWQED42L.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-BKG3FXX3.js";
|
|
17
17
|
import {
|
|
18
18
|
CanvasError
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
20
20
|
|
|
21
21
|
// src/cli.ts
|
|
22
22
|
import { readFileSync, writeFileSync } from "fs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as BoardData, c as ShapeType, S as Shape, E as Endpoint, C as Connector } from './read-
|
|
1
|
+
import { a as BoardData, c as ShapeType, S as Shape, E as Endpoint, C as Connector } from './read-DeS2Zaz8.js';
|
|
2
2
|
import { PrincipalKind } from 'agentic-identity';
|
|
3
3
|
|
|
4
4
|
/** Options for every call. */
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { C as CanvasClient } from './client-
|
|
2
|
+
import { C as CanvasClient } from './client-DBb1jpxl.js';
|
|
3
3
|
import { PrincipalKind } from 'agentic-identity';
|
|
4
4
|
import { SyncClient, Status } from 'agentic-realtime/client';
|
|
5
5
|
import * as Y from 'yjs';
|
|
6
|
-
import './read-
|
|
6
|
+
import './read-DeS2Zaz8.js';
|
|
7
|
+
import './principal-blhsFbdS.js';
|
|
7
8
|
import 'zod';
|
|
8
9
|
|
|
9
10
|
/** The presence of a room: who is here. A `SyncClient` of agentic-realtime has these. */
|
package/dist/embed.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
PRINCIPAL_KINDS,
|
|
4
4
|
principalSchema,
|
|
5
5
|
readBoard
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
7
7
|
|
|
8
8
|
// ui/embed.tsx
|
|
9
9
|
import { ToastProvider, TooltipProvider } from "agentic-design/react";
|
|
@@ -456,7 +456,7 @@ function BoardView({ id, client: remote, host, room: roomOptions, me, backHref }
|
|
|
456
456
|
const remove = (target) => {
|
|
457
457
|
setSelected(null);
|
|
458
458
|
write(() => client.remove(id, target), "Could not delete the item");
|
|
459
|
-
canvasBox.current?.querySelector("
|
|
459
|
+
canvasBox.current?.querySelector(".canvas")?.focus();
|
|
460
460
|
};
|
|
461
461
|
const add = (type) => {
|
|
462
462
|
const [w, h] = DEFAULT_SIZE[type];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { B as Board
|
|
2
|
-
export { a as BoardData, b as CanvasElement, d as CanvasError, C as Connector, e as EdgeData, E as Endpoint, f 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,
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
1
|
+
import { B as Board } from './read-DeS2Zaz8.js';
|
|
2
|
+
export { a as BoardData, b as CanvasElement, d as CanvasError, C as Connector, e as EdgeData, E as Endpoint, f 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, r as readBoard } from './read-DeS2Zaz8.js';
|
|
3
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
4
|
+
export { p as principalSchema } from './principal-blhsFbdS.js';
|
|
5
|
+
import { B as BoardAccess } from './service-BNEH2Z12.js';
|
|
6
|
+
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-BNEH2Z12.js';
|
|
5
7
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
-
import { B as BoardSharing } from './client-
|
|
8
|
+
import { B as BoardSharing } from './client-DBb1jpxl.js';
|
|
7
9
|
export { FileStoreOptions, createFileStore, openLocalStore } from './store-file.js';
|
|
8
10
|
export { PRINCIPAL_KINDS, PrincipalKind, isMachineMade } from 'agentic-identity';
|
|
9
11
|
import 'yjs';
|
package/dist/index.js
CHANGED
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
operatorActor,
|
|
7
7
|
runMcp,
|
|
8
8
|
svgToPng
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AI5NWPEW.js";
|
|
10
10
|
import {
|
|
11
11
|
createFileStore,
|
|
12
12
|
openLocalStore
|
|
13
13
|
} from "./chunk-NRLF6MGR.js";
|
|
14
14
|
import {
|
|
15
15
|
CanvasService
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GLNF43N4.js";
|
|
17
17
|
import "./chunk-NWQED42L.js";
|
|
18
18
|
import {
|
|
19
19
|
MAX_FLOW_EDGES,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
layoutFlow,
|
|
23
23
|
parseFlow,
|
|
24
24
|
toSvg
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-BKG3FXX3.js";
|
|
26
26
|
import {
|
|
27
27
|
Board,
|
|
28
28
|
CanvasError,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
isMachineMade,
|
|
34
34
|
principalSchema,
|
|
35
35
|
readBoard
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
37
37
|
export {
|
|
38
38
|
ActorConfigError,
|
|
39
39
|
BRAND,
|
package/dist/live-worker.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ import 'agentic-identity';
|
|
|
5
5
|
import 'agentic-realtime';
|
|
6
6
|
import 'agentic-identity/cloudflare';
|
|
7
7
|
import 'hono';
|
|
8
|
-
import './
|
|
9
|
-
import 'yjs';
|
|
8
|
+
import './principal-blhsFbdS.js';
|
|
10
9
|
import 'zod';
|
|
11
|
-
import './service-
|
|
10
|
+
import './service-BNEH2Z12.js';
|
|
11
|
+
import './read-DeS2Zaz8.js';
|
|
12
|
+
import 'yjs';
|
|
12
13
|
|
|
13
14
|
type Namespace = Parameters<typeof createDurableObjectRealtime>[0];
|
|
14
15
|
interface LiveEnv extends WorkerEnv {
|
package/dist/live-worker.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createApp
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-4PY5SN2N.js";
|
|
4
|
+
import "./chunk-LMSMUGBF.js";
|
|
5
|
+
import "./chunk-GLNF43N4.js";
|
|
5
6
|
import "./chunk-NWQED42L.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-BKG3FXX3.js";
|
|
7
8
|
import {
|
|
8
9
|
CanvasError
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
10
11
|
|
|
11
12
|
// src/live-worker.ts
|
|
12
13
|
import { parseRegion } from "agentic-identity";
|
package/dist/live.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { B as Board,
|
|
1
|
+
import { B as Board, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-DeS2Zaz8.js';
|
|
2
2
|
import { Peer } from 'agentic-realtime/client';
|
|
3
|
-
import { B as BoardAccess, D as DrawOptions } from './service-
|
|
3
|
+
import { B as BoardAccess, D as DrawOptions } from './service-BNEH2Z12.js';
|
|
4
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
4
5
|
import 'yjs';
|
|
5
6
|
import 'agentic-identity';
|
|
6
7
|
import 'zod';
|
package/dist/live.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
drawFlow,
|
|
3
3
|
toSvg
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BKG3FXX3.js";
|
|
5
5
|
import {
|
|
6
6
|
Board,
|
|
7
7
|
CanvasError,
|
|
8
8
|
readBoard
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZP6BLCB7.js";
|
|
10
10
|
|
|
11
11
|
// src/live.ts
|
|
12
12
|
import { parsePrincipal } from "agentic-identity";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { PrincipalKind } from 'agentic-identity';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The actor of a new write. The rule is the identity kernel's `parsePrincipal`: a known kind, and an id of 1 to 128
|
|
6
|
+
* characters from A-Z, a-z, 0-9, ".", "_" and "-". The Worker, the CLI and the SDK all use it.
|
|
7
|
+
*/
|
|
8
|
+
declare const principalSchema: z.ZodPipe<z.ZodObject<{
|
|
9
|
+
kind: z.ZodString;
|
|
10
|
+
id: z.ZodString;
|
|
11
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
12
|
+
kind: PrincipalKind;
|
|
13
|
+
id: string;
|
|
14
|
+
}, {
|
|
15
|
+
kind: string;
|
|
16
|
+
id: string;
|
|
17
|
+
}>>;
|
|
18
|
+
type Principal = {
|
|
19
|
+
kind: PrincipalKind;
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export { type Principal as P, principalSchema as p };
|
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
-
import {
|
|
3
|
-
import { z } from 'zod';
|
|
4
|
-
|
|
5
|
-
/**
|
|
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
|
-
* characters from A-Z, a-z, 0-9, ".", "_" and "-". The Worker, the CLI and the SDK all use it.
|
|
8
|
-
*/
|
|
9
|
-
declare const principalSchema: z.ZodPipe<z.ZodObject<{
|
|
10
|
-
kind: z.ZodString;
|
|
11
|
-
id: z.ZodString;
|
|
12
|
-
}, z.core.$strict>, z.ZodTransform<{
|
|
13
|
-
kind: PrincipalKind;
|
|
14
|
-
id: string;
|
|
15
|
-
}, {
|
|
16
|
-
kind: string;
|
|
17
|
-
id: string;
|
|
18
|
-
}>>;
|
|
19
|
-
type Principal = {
|
|
20
|
-
kind: PrincipalKind;
|
|
21
|
-
id: string;
|
|
22
|
-
};
|
|
2
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
23
3
|
|
|
24
4
|
type ErrorCode = "INVALID" | "DUPLICATE_ID" | "NOT_FOUND" | "BAD_REFERENCE" | "LIMIT" | "UNAVAILABLE" | "FORBIDDEN";
|
|
25
5
|
declare class CanvasError extends Error {
|
|
@@ -132,6 +112,28 @@ declare class Board {
|
|
|
132
112
|
update(elementId: string, patch: unknown, actor: Principal): CanvasElement;
|
|
133
113
|
/** Shift an element, or a whole group. Returns the ids that moved. Bound connector ends follow their shapes. */
|
|
134
114
|
move(elementId: string, dx: number, dy: number, actor: Principal): string[];
|
|
115
|
+
/** Every stored entry as it is, valid or not, with its key as `id`. */
|
|
116
|
+
rawElements(): Record<string, unknown>[];
|
|
117
|
+
/**
|
|
118
|
+
* The raw stored elements that `who` made (`author`) and the ids of those that `who` last edited but did not make.
|
|
119
|
+
* It reads entries as they are, with no element checks, so a damaged entry still shows. Account export uses it.
|
|
120
|
+
*/
|
|
121
|
+
stampedBy(who: Principal): {
|
|
122
|
+
authored: Record<string, unknown>[];
|
|
123
|
+
edited: string[];
|
|
124
|
+
titleBy: boolean;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Erase `who` from the board. An element they made is deleted alone: its members go to its parent group, and a
|
|
128
|
+
* connector of someone else that was bound to it goes too, because it would be hidden. An element they only edited
|
|
129
|
+
* keeps its content, and its editor becomes `(erased)`. A title they set is cleared. Idempotent. Returns the counts.
|
|
130
|
+
*/
|
|
131
|
+
eraseStamps(who: Principal, erased: Principal): {
|
|
132
|
+
authored: number;
|
|
133
|
+
edited: number;
|
|
134
|
+
dangling: number;
|
|
135
|
+
title: boolean;
|
|
136
|
+
};
|
|
135
137
|
/** Delete an element. A shape takes its connectors and a group takes its members. Returns the ids removed. */
|
|
136
138
|
remove(elementId: string): string[];
|
|
137
139
|
group(ids: string[], actor: Principal, groupId?: string): Group;
|
|
@@ -181,4 +183,4 @@ interface BoardData {
|
|
|
181
183
|
/** The board as plain data for an agent. Reads only. Embedded image bytes are left out. */
|
|
182
184
|
declare function readBoard(board: Board): BoardData;
|
|
183
185
|
|
|
184
|
-
export { Board as B, type Connector as C, type Endpoint as E, type Group as G, MAX_ELEMENTS as M, type NodeData as N, type
|
|
186
|
+
export { Board as B, type Connector as C, type Endpoint as E, type Group as G, MAX_ELEMENTS as M, type NodeData as N, type Shape as S, type BoardData as a, type CanvasElement as b, type ShapeType as c, CanvasError as d, type EdgeData as e, type ErrorCode as f, MAX_TEXT as g, SHAPE_TYPES as h, readBoard as r };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { c as ShapeType, B as Board,
|
|
1
|
+
import { c as ShapeType, B as Board, a as BoardData, S as Shape, C as Connector, b as CanvasElement } from './read-DeS2Zaz8.js';
|
|
2
2
|
import { Store } from 'agentic-identity';
|
|
3
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
3
4
|
|
|
4
5
|
declare const MAX_FLOW_NODES = 500;
|
|
5
6
|
declare const MAX_FLOW_EDGES = 2000;
|
package/dist/worker.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { Store, PrincipalContext, Action, Grant, InviteSummary, Blobs } from 'ag
|
|
|
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 './
|
|
6
|
-
import { C as CanvasService } from './service-
|
|
7
|
-
import 'yjs';
|
|
5
|
+
import { P as Principal } from './principal-blhsFbdS.js';
|
|
6
|
+
import { C as CanvasService } from './service-BNEH2Z12.js';
|
|
8
7
|
import 'zod';
|
|
8
|
+
import './read-DeS2Zaz8.js';
|
|
9
|
+
import 'yjs';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Who may do what on a board. A board is an identity resource of type `canvas`. The roles, the grants, the invites,
|
package/dist/worker.js
CHANGED
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
identify,
|
|
6
6
|
sniffImage,
|
|
7
7
|
worker_default
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-4PY5SN2N.js";
|
|
9
|
+
import "./chunk-LMSMUGBF.js";
|
|
10
|
+
import "./chunk-GLNF43N4.js";
|
|
10
11
|
import "./chunk-NWQED42L.js";
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-BKG3FXX3.js";
|
|
13
|
+
import "./chunk-ZP6BLCB7.js";
|
|
13
14
|
export {
|
|
14
15
|
WS_PROTOCOL,
|
|
15
16
|
authenticate,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-canvas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A shared whiteboard for diagrams, drawn by people and agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"whiteboard",
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./dist/worker.d.ts",
|
|
45
45
|
"import": "./dist/worker.js"
|
|
46
46
|
},
|
|
47
|
+
"./account": {
|
|
48
|
+
"types": "./dist/account.d.ts",
|
|
49
|
+
"import": "./dist/account.js"
|
|
50
|
+
},
|
|
47
51
|
"./live": {
|
|
48
52
|
"types": "./dist/live.d.ts",
|
|
49
53
|
"import": "./dist/live.js"
|
|
@@ -84,8 +88,8 @@
|
|
|
84
88
|
"@dagrejs/dagre": "^3.1.3",
|
|
85
89
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
86
90
|
"agentic-design": "^0.5.0",
|
|
87
|
-
"agentic-identity": "^0.
|
|
88
|
-
"agentic-realtime": "^0.4.
|
|
91
|
+
"agentic-identity": "^0.9.0",
|
|
92
|
+
"agentic-realtime": "^0.4.4",
|
|
89
93
|
"yjs": "^13.6.0",
|
|
90
94
|
"zod": "^4.6.7"
|
|
91
95
|
},
|