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;