diff --git a/.github/workflows/cli-ci.yaml b/.github/workflows/cli-ci.yaml
index 8747695..4e70869 100644
--- a/.github/workflows/cli-ci.yaml
+++ b/.github/workflows/cli-ci.yaml
@@ -41,7 +41,7 @@ jobs:
- name: Install
run: pnpm install --frozen-lockfile
- - name: Lint (TypeScript)
+ - name: Lint
run: |
pnpm --filter @autodraw/core run lint
pnpm --filter @maelstromlab/autodraw run lint
@@ -55,8 +55,5 @@ jobs:
- name: Test @maelstromlab/autodraw
run: pnpm --filter @maelstromlab/autodraw run test
- - name: Build @autodraw/core
- run: pnpm --filter @autodraw/core run build
-
- name: Build @maelstromlab/autodraw
run: pnpm --filter @maelstromlab/autodraw run build
diff --git a/.github/workflows/web-ci.yaml b/.github/workflows/web-ci.yaml
index 5c6d62c..7d62f95 100644
--- a/.github/workflows/web-ci.yaml
+++ b/.github/workflows/web-ci.yaml
@@ -45,6 +45,10 @@ jobs:
- name: Install
run: pnpm install --frozen-lockfile
+ # @autodraw/core resolves to dist/; astro check needs it (see AGENTS.md).
+ - name: Build @autodraw/core
+ run: pnpm --filter @autodraw/core run build
+
- name: Format
run: pnpm --filter web format
diff --git a/apps/cli/package.json b/apps/cli/package.json
index d78c0be..2599804 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -16,9 +16,9 @@
"prepublishOnly": "pnpm run build",
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup && oclif manifest",
"postpack": "oclif manifest",
- "lint": "tsc -p tsconfig.json --noEmit",
- "typecheck": "tsc -p tsconfig.json --noEmit",
- "test": "vitest run"
+ "lint": "pnpm --filter @autodraw/core run build && tsc -p tsconfig.json --noEmit",
+ "typecheck": "pnpm --filter @autodraw/core run build && tsc -p tsconfig.json --noEmit",
+ "test": "pnpm --filter @autodraw/core run build && vitest run"
},
"dependencies": {
"@oclif/core": "^4.2.10",
diff --git a/apps/editor/src/editor/onboarding/AgentGetStartedPanel.tsx b/apps/editor/src/editor/onboarding/AgentGetStartedPanel.tsx
index 44c1815..316c6a1 100644
--- a/apps/editor/src/editor/onboarding/AgentGetStartedPanel.tsx
+++ b/apps/editor/src/editor/onboarding/AgentGetStartedPanel.tsx
@@ -5,7 +5,7 @@
* Self-contained styling so it reads clearly inside the onboarding dialog and on any canvas theme.
*/
-import { Bot, Copy } from "lucide-react";
+import { Bot, Clipboard, ClipboardCheck } from "lucide-react";
import type { CSSProperties, ReactNode } from "react";
import { useCallback, useState } from "react";
import * as SimpleIcons from "simple-icons";
@@ -148,11 +148,9 @@ function CopyableRow({ row }: { row: AgentGetStartedCopyRow }) {
aria-label={done ? "Copied" : `Copy: ${row.id}`}
>
{done ? (
-
- Copied
-
+
) : (
-
+
)}
diff --git a/apps/editor/src/editor/onboarding/OnboardingDialog.tsx b/apps/editor/src/editor/onboarding/OnboardingDialog.tsx
index afb6ae3..e6204bf 100644
--- a/apps/editor/src/editor/onboarding/OnboardingDialog.tsx
+++ b/apps/editor/src/editor/onboarding/OnboardingDialog.tsx
@@ -21,7 +21,7 @@ export function OnboardingDialog() {
>
Connect your agent
diff --git a/apps/web/worker/diagramService.ts b/apps/web/worker/diagramService.ts
index 5daf125..94fa9bc 100644
--- a/apps/web/worker/diagramService.ts
+++ b/apps/web/worker/diagramService.ts
@@ -124,7 +124,11 @@ async function createDiagram(request: Request, env: AutodrawWorkerEnv): Promise<
const tokenHash = await sha256Hex(editToken);
const createdAt = new Date().toISOString();
- await env.DIAGRAMS!.put(r2Key(id), text, {
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return errorResponse("Diagram storage not configured", 503);
+ }
+ await bucket.put(r2Key(id), text, {
httpMetadata: { contentType: "application/json; charset=utf-8" },
customMetadata: { editTokenHash: tokenHash, createdAt },
});
@@ -139,7 +143,11 @@ async function createDiagram(request: Request, env: AutodrawWorkerEnv): Promise<
}
async function getDiagram(env: AutodrawWorkerEnv, id: string): Promise {
- const obj = await env.DIAGRAMS!.get(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return errorResponse("Diagram storage not configured", 503);
+ }
+ const obj = await bucket.get(r2Key(id));
if (!obj) {
return errorResponse("Not found", 404);
}
@@ -162,7 +170,11 @@ async function putDiagram(request: Request, env: AutodrawWorkerEnv, id: string):
);
}
- const head = await env.DIAGRAMS!.head(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return errorResponse("Diagram storage not configured", 503);
+ }
+ const head = await bucket.head(r2Key(id));
if (!head) {
return errorResponse("Not found", 404);
}
@@ -188,7 +200,7 @@ async function putDiagram(request: Request, env: AutodrawWorkerEnv, id: string):
}
const createdAt = head.customMetadata?.createdAt ?? new Date().toISOString();
- await env.DIAGRAMS!.put(r2Key(id), text, {
+ await bucket.put(r2Key(id), text, {
httpMetadata: { contentType: "application/json; charset=utf-8" },
customMetadata: { editTokenHash: tokenHash, createdAt },
});
@@ -205,7 +217,11 @@ async function deleteDiagram(
if (!token) {
return errorResponse("Missing edit token", 401);
}
- const head = await env.DIAGRAMS!.head(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return errorResponse("Diagram storage not configured", 503);
+ }
+ const head = await bucket.head(r2Key(id));
if (!head) {
return errorResponse("Not found", 404);
}
@@ -214,7 +230,7 @@ async function deleteDiagram(
if (!expected || tokenHash !== expected) {
return errorResponse("Invalid edit token", 403);
}
- await env.DIAGRAMS!.delete(r2Key(id));
+ await bucket.delete(r2Key(id));
return jsonResponse({ ok: true });
}
@@ -228,7 +244,11 @@ export async function loadDiagramJson(
if (!diagramStorageConfigured(env)) {
return { ok: false, error: "Diagram storage not configured", status: 503 };
}
- const head = await env.DIAGRAMS!.head(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return { ok: false, error: "Diagram storage not configured", status: 503 };
+ }
+ const head = await bucket.head(r2Key(id));
if (!head) {
return { ok: false, error: "Not found", status: 404 };
}
@@ -242,7 +262,7 @@ export async function loadDiagramJson(
return { ok: false, error: "Invalid edit token", status: 403 };
}
}
- const obj = await env.DIAGRAMS!.get(r2Key(id));
+ const obj = await bucket.get(r2Key(id));
if (!obj) {
return { ok: false, error: "Not found", status: 404 };
}
@@ -258,7 +278,11 @@ export async function saveDiagramJson(
if (!diagramStorageConfigured(env)) {
return { ok: false, error: "Diagram storage not configured", status: 503 };
}
- const head = await env.DIAGRAMS!.head(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return { ok: false, error: "Diagram storage not configured", status: 503 };
+ }
+ const head = await bucket.head(r2Key(id));
if (!head) {
return { ok: false, error: "Not found", status: 404 };
}
@@ -276,7 +300,7 @@ export async function saveDiagramJson(
}
const normalized = serializeDiagram(doc);
const createdAt = head.customMetadata?.createdAt ?? new Date().toISOString();
- await env.DIAGRAMS!.put(r2Key(id), normalized, {
+ await bucket.put(r2Key(id), normalized, {
httpMetadata: { contentType: "application/json; charset=utf-8" },
customMetadata: { editTokenHash: tokenHash, createdAt },
});
@@ -304,7 +328,11 @@ export async function createDiagramFromObject(
const editToken = newEditToken();
const tokenHash = await sha256Hex(editToken);
const createdAt = new Date().toISOString();
- await env.DIAGRAMS!.put(r2Key(id), text, {
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return { ok: false, error: "Diagram storage not configured", status: 503 };
+ }
+ await bucket.put(r2Key(id), text, {
httpMetadata: { contentType: "application/json; charset=utf-8" },
customMetadata: { editTokenHash: tokenHash, createdAt },
});
@@ -319,7 +347,11 @@ export async function deleteDiagramByToken(
if (!diagramStorageConfigured(env)) {
return { ok: false, error: "Diagram storage not configured", status: 503 };
}
- const head = await env.DIAGRAMS!.head(r2Key(id));
+ const bucket = env.DIAGRAMS;
+ if (!bucket) {
+ return { ok: false, error: "Diagram storage not configured", status: 503 };
+ }
+ const head = await bucket.head(r2Key(id));
if (!head) {
return { ok: false, error: "Not found", status: 404 };
}
@@ -328,6 +360,6 @@ export async function deleteDiagramByToken(
if (!expected || tokenHash !== expected) {
return { ok: false, error: "Invalid edit token", status: 403 };
}
- await env.DIAGRAMS!.delete(r2Key(id));
+ await bucket.delete(r2Key(id));
return { ok: true };
}
diff --git a/apps/web/worker/index.ts b/apps/web/worker/index.ts
index d4920ed..81908f4 100644
--- a/apps/web/worker/index.ts
+++ b/apps/web/worker/index.ts
@@ -1,4 +1,3 @@
-import { withCors } from "./cors.js";
import { handleDiagramApi } from "./diagramService.js";
import { handleMcpRequest } from "./mcpHandler.js";
import type { AutodrawWorkerEnv, ExecutionContext } from "./types.js";
diff --git a/apps/web/worker/mcpHandler.ts b/apps/web/worker/mcpHandler.ts
index 995e389..d00119d 100644
--- a/apps/web/worker/mcpHandler.ts
+++ b/apps/web/worker/mcpHandler.ts
@@ -13,7 +13,7 @@ import {
} from "@autodraw/core";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
-import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker.js";
+import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { corsOptionsResponse, withCors } from "./cors.js";
import {
diff --git a/apps/web/worker/types.ts b/apps/web/worker/types.ts
index f57e3bc..8409e6d 100644
--- a/apps/web/worker/types.ts
+++ b/apps/web/worker/types.ts
@@ -1,3 +1,8 @@
+/** Static assets binding (Workers + Assets). */
+export interface Fetcher {
+ fetch(input: Request | string, init?: RequestInit): Promise;
+}
+
/** Minimal R2 typing for the diagram Worker (no `@cloudflare/workers-types` required for `astro check`). */
export interface R2Object {
customMetadata?: Record;