excalidrop 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/worker/src/mcp.js +17 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "excalidrop",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Remote GitHub-backed Excalidraw canvas + MCP server — no local server, one-command setup",
5
5
  "main": "dist/mcp.js",
6
6
  "type": "module",
package/worker/src/mcp.js CHANGED
@@ -13,6 +13,19 @@ const CANVAS_BRANCH = 'excalidrop';
13
13
  const GH_API = 'https://api.github.com';
14
14
  const TOOL_LIST_TTL_MS = 3600000;
15
15
 
16
+ // --- base64 helpers (Workers have no Node Buffer)
17
+ function b64encode(str) {
18
+ const bytes = new TextEncoder().encode(str);
19
+ let bin = '';
20
+ const CH = 0x8000;
21
+ for (let i = 0; i < bytes.length; i += CH) bin += String.fromCharCode(...bytes.subarray(i, i + CH));
22
+ return btoa(bin);
23
+ }
24
+ function b64decode(b64) {
25
+ const bin = atob(String(b64).replace(/\n/g, ''));
26
+ return new TextDecoder().decode(Uint8Array.from(bin, (c) => c.charCodeAt(0)));
27
+ }
28
+
16
29
  // --- best-effort quotas (per isolate; resets on evict — honest 429 either way)
17
30
  const DAY = () => new Date().toISOString().slice(0, 10);
18
31
  let dailyCount = 0;
@@ -90,10 +103,10 @@ async function getScene(repo, token) {
90
103
  b64 = (await blob.json()).content || null;
91
104
  }
92
105
  if (!b64) throw new Error('scene returned no content');
93
- return { doc: JSON.parse(Buffer.from(b64, 'base64').toString('utf8')), sha: j.sha };
106
+ return { doc: JSON.parse(b64decode(b64)), sha: j.sha };
94
107
  }
95
108
  async function putScene(repo, token, doc, message, sha) {
96
- const body = { message, content: Buffer.from(JSON.stringify(doc, null, 2)).toString('base64'), branch: CANVAS_BRANCH };
109
+ const body = { message, content: b64encode(JSON.stringify(doc, null, 2)), branch: CANVAS_BRANCH };
97
110
  if (sha) body.sha = sha;
98
111
  const r = await fetch(`${GH_API}/repos/${repo}/contents/${SCENE_PATH}`, { method: 'PUT', headers: ghHeaders(token), body: JSON.stringify(body) });
99
112
  if (r.status === 404) throw new Error(`branch ${CANVAS_BRANCH} missing — open the canvas viewer once to initialize it`);
@@ -483,7 +496,7 @@ async function callTool(env, name, args, token) {
483
496
  const elements = doc?.elements || [];
484
497
  const r = await fetch(`${GH_API}/repos/${repo}/contents/snapshots/${a.name}.json`, {
485
498
  method: 'PUT', headers: ghHeaders(token),
486
- body: JSON.stringify({ message: `excalidrop: snapshot ${a.name} (${elements.length} elements)`, content: Buffer.from(JSON.stringify({ name: a.name, elements, createdAt: new Date().toISOString() }, null, 2)).toString('base64'), branch: CANVAS_BRANCH }),
499
+ body: JSON.stringify({ message: `excalidrop: snapshot ${a.name} (${elements.length} elements)`, content: b64encode(JSON.stringify({ name: a.name, elements, createdAt: new Date().toISOString() }, null, 2)), branch: CANVAS_BRANCH }),
487
500
  });
488
501
  if (!r.ok) throw new Error(`snapshot write ${r.status}: ${(await r.text()).slice(0, 150)}`);
489
502
  return textResult(`Snapshot "${a.name}" saved (${elements.length} elements) on ${repo}.`);
@@ -494,7 +507,7 @@ async function callTool(env, name, args, token) {
494
507
  if (r.status === 404) throw new Error(`Snapshot "${a.name}" not found`);
495
508
  if (!r.ok) throw new Error(`snapshot read ${r.status}`);
496
509
  const j = await r.json();
497
- const snap = JSON.parse(Buffer.from(j.content, 'base64').toString('utf8'));
510
+ const snap = JSON.parse(b64decode(j.content));
498
511
  const els = (snap.elements || []).map((e) => ({ ...e, id: e.id || generateId() }));
499
512
  await mutateScene(repo, token, `excalidrop: restore ${a.name}`, (elements) => { elements.length = 0; for (const e of els) elements.push(e); });
500
513
  return textResult(`Snapshot "${a.name}" restored (${els.length} elements) on ${repo}.`);