agent-canvas 0.1.43 → 0.1.45

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 (110) hide show
  1. package/bin/lib/commands/daemon.ts +3 -6
  2. package/bin/lib/commands/push.ts +1 -1
  3. package/bin/lib/daemon-lifecycle.ts +65 -72
  4. package/bin/lib/helpers.test.ts +1 -0
  5. package/bin/lib/helpers.ts +30 -74
  6. package/daemon/build.ts +16 -13
  7. package/daemon/client/App.tsx +38 -62
  8. package/daemon/client/CompareView.tsx +4 -4
  9. package/daemon/client/FileBrowser.tsx +4 -4
  10. package/daemon/client/FileViewer.tsx +4 -4
  11. package/daemon/client/OverviewView.tsx +2 -2
  12. package/daemon/client/RevisionSelect.tsx +1 -1
  13. package/daemon/client/clientApi.ts +18 -0
  14. package/daemon/client/components/index.ts +3 -16
  15. package/daemon/client/styles.css +4 -457
  16. package/daemon/dist/client.css +1 -1
  17. package/daemon/dist/client.js +76 -76
  18. package/daemon/dist/components.js +29 -29
  19. package/daemon/dist/runtime.js +1 -1
  20. package/daemon/src/components.ts +13 -0
  21. package/daemon/src/handlers/api.ts +14 -9
  22. package/daemon/src/handlers/files.ts +1 -3
  23. package/daemon/src/handlers/static.ts +1 -1
  24. package/daemon/src/handlers/uploads.ts +1 -3
  25. package/daemon/src/paths.ts +10 -24
  26. package/daemon/src/remote-feedback.test.ts +1 -1
  27. package/daemon/src/remote-feedback.ts +1 -1
  28. package/daemon/src/secrets-api.test.ts +1 -2
  29. package/daemon/src/server.ts +13 -10
  30. package/daemon/src/share-e2e.test.ts +1 -1
  31. package/daemon/src/share.test.ts +1 -1
  32. package/daemon/src/share.ts +1 -1
  33. package/daemon/tsconfig.json +1 -3
  34. package/package.json +10 -2
  35. package/.github/dark.png +0 -0
  36. package/.github/light.png +0 -0
  37. package/.github/workflows/ci.yml +0 -48
  38. package/.github/workflows/deploy-worker-prod.yml +0 -19
  39. package/.github/workflows/deploy-worker-stage.yml +0 -21
  40. package/.github/workflows/deploy-worker.yml +0 -72
  41. package/.github/workflows/release.yml +0 -36
  42. package/CLAUDE.md +0 -86
  43. package/bun.lock +0 -377
  44. package/daemon/client/AnnotationEditor.tsx +0 -315
  45. package/daemon/client/AnnotationProvider.tsx +0 -191
  46. package/daemon/client/AnnotationSidebar.tsx +0 -627
  47. package/daemon/client/FileIcon.tsx +0 -76
  48. package/daemon/client/PlanRenderer.tsx +0 -614
  49. package/daemon/client/Popover.tsx +0 -350
  50. package/daemon/client/RenderErrorContext.ts +0 -11
  51. package/daemon/client/ResponsePreview.tsx +0 -186
  52. package/daemon/client/SessionContext.tsx +0 -3
  53. package/daemon/client/annotationContext.ts +0 -114
  54. package/daemon/client/annotationDom.ts +0 -104
  55. package/daemon/client/annotationDraft.test.ts +0 -149
  56. package/daemon/client/annotationDraft.ts +0 -110
  57. package/daemon/client/compat/preact-all.ts +0 -4
  58. package/daemon/client/components/Callout.tsx +0 -26
  59. package/daemon/client/components/Checklist.tsx +0 -37
  60. package/daemon/client/components/Choice.tsx +0 -176
  61. package/daemon/client/components/CodeBlock.tsx +0 -35
  62. package/daemon/client/components/Diff.tsx +0 -65
  63. package/daemon/client/components/FilePreview.tsx +0 -88
  64. package/daemon/client/components/Image.tsx +0 -45
  65. package/daemon/client/components/Markdown.tsx +0 -44
  66. package/daemon/client/components/Mermaid.tsx +0 -565
  67. package/daemon/client/components/Note.tsx +0 -14
  68. package/daemon/client/components/Priority.tsx +0 -19
  69. package/daemon/client/components/ResponseNote.tsx +0 -19
  70. package/daemon/client/components/Section.tsx +0 -33
  71. package/daemon/client/components/Table.tsx +0 -35
  72. package/daemon/client/components/Task.tsx +0 -76
  73. package/daemon/client/components/UserInput.tsx +0 -174
  74. package/daemon/client/components/renderMarkdownChildren.tsx +0 -97
  75. package/daemon/client/generateMarkdown.ts +0 -273
  76. package/daemon/client/highlightRange.ts +0 -224
  77. package/daemon/client/popoverPosition.ts +0 -31
  78. package/daemon/client/runtime.ts +0 -123
  79. package/daemon/client/theme.css +0 -66
  80. package/daemon/client/useTextAnnotation.tsx +0 -188
  81. package/daemon/client/utils.ts +0 -13
  82. package/daemon/src/compiler.test.ts +0 -57
  83. package/daemon/src/compiler.ts +0 -360
  84. package/daemon/src/handlers/utils.ts +0 -14
  85. package/daemon/src/router.ts +0 -16
  86. package/daemon/src/session-secrets.test.ts +0 -83
  87. package/daemon/src/session.ts +0 -612
  88. package/daemon/src/watcher.ts +0 -71
  89. package/daemon/src/websocket.test.ts +0 -99
  90. package/daemon/src/websocket.ts +0 -211
  91. package/tsconfig.json +0 -14
  92. package/workers/canvas-share/README.md +0 -159
  93. package/workers/canvas-share/oblaka.ts +0 -56
  94. package/workers/canvas-share/package.json +0 -25
  95. package/workers/canvas-share/public/assets/.gitkeep +0 -0
  96. package/workers/canvas-share/public/assets/client.css +0 -2
  97. package/workers/canvas-share/public/assets/client.js +0 -91
  98. package/workers/canvas-share/public/assets/components.js +0 -65
  99. package/workers/canvas-share/public/assets/jsx-runtime.js +0 -3
  100. package/workers/canvas-share/public/assets/preact-compat.js +0 -1
  101. package/workers/canvas-share/public/assets/runtime.js +0 -1
  102. package/workers/canvas-share/public/index.html +0 -385
  103. package/workers/canvas-share/public/screenshot-dark.png +0 -0
  104. package/workers/canvas-share/public/screenshot-light.png +0 -0
  105. package/workers/canvas-share/src/index.test.ts +0 -209
  106. package/workers/canvas-share/src/index.ts +0 -518
  107. package/workers/canvas-share/src/types.ts +0 -119
  108. package/workers/canvas-share/src/util.ts +0 -156
  109. package/workers/canvas-share/src/validation.ts +0 -160
  110. package/workers/canvas-share/tsconfig.json +0 -16
@@ -1 +1 @@
1
- import{createContext as q,useContext as B,useEffect as J}from"react";import{createContext as H}from"react";var I=H("");var K=q(null);function L(){return B(K)}function R(h,z,j){let{setFeedbackEntry:D,removeFeedbackEntry:G}=L();J(()=>{return D(h,{id:h,markdown:z,label:j?.label,required:j?.required}),()=>G(h)},[h,z,j?.label,j?.required])}var M=q("");function S(){return B(M)}var T=q({setActiveView:()=>{}});export{R as useFeedback,S as useCanvasFile,L as useAnnotations,I as SessionContext,M as CanvasFileCtx,K as AnnotationCtx,T as ActiveViewCtx};
1
+ import{createContext as z,useContext as B,useEffect as D}from"react";import{createContext as J}from"react";var K=J("");var L=z(null);function G(){return B(L)}function S(h){let{registerResponse:j}=G();D(()=>{j(h)},[h,j])}function T(h,j,q){let{setFeedbackEntry:H,removeFeedbackEntry:I}=G();D(()=>{return H(h,{id:h,markdown:j,label:q?.label,required:q?.required}),()=>I(h)},[h,j,q?.label,q?.required])}var M=z("");function U(){return B(M)}var W=z({setActiveView:()=>{}});export{S as useResponseRegistration,T as useFeedback,U as useCanvasFile,G as useAnnotations,K as SessionContext,M as CanvasFileCtx,L as AnnotationCtx,W as ActiveViewCtx};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Components agent-canvas adds on top of the kernel's set.
3
+ *
4
+ * Keep in sync with `daemon/client/components/index.ts` — the compiler injects
5
+ * these names into authored JSX, and the browser resolves them from that barrel
6
+ * through `#canvas/components`. A name here with no matching export renders as
7
+ * undefined.
8
+ */
9
+ import type { CanvasComponents } from "@fabrika/canvas-kernel/server";
10
+
11
+ export const HOST_COMPONENTS: CanvasComponents = {
12
+ SecretInput: { requiredProps: { id: "string", label: "string", env: "string" } },
13
+ };
@@ -1,10 +1,14 @@
1
1
  import { readFileSync, readdirSync, existsSync } from "fs";
2
- import type { SessionManager } from "../session";
3
- import { compilePlan } from "../compiler";
4
- import { watchSession } from "../watcher";
5
- import { jsonResponse } from "./utils";
2
+ import {
3
+ compileJsx,
4
+ jsonResponse,
5
+ watchSession,
6
+ type Route,
7
+ type SessionManager,
8
+ } from "@fabrika/canvas-kernel/server";
6
9
  import { loadShareConfig, shareRevision, revokeShare } from "../share";
7
- import type { Route } from "../router";
10
+ import { COMPILE_TEMP_DIR } from "../paths";
11
+ import { HOST_COMPONENTS } from "../components";
8
12
 
9
13
  const SECRET_FIELD_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
10
14
  const MAX_SECRET_BYTES = 64 * 1024;
@@ -83,7 +87,7 @@ export function createApiHandlers(ctx: ApiContext): Route[] {
83
87
  const errors: Record<string, string> = {};
84
88
  await Promise.all(
85
89
  [...canvasFiles.entries()].map(async ([filename, jsx]) => {
86
- const result = await compilePlan(jsx, projectRoot);
90
+ const result = await compileJsx(jsx, { projectRoot, tempDir: COMPILE_TEMP_DIR, components: HOST_COMPONENTS });
87
91
  if (result.ok) {
88
92
  compiled.set(filename, result.js);
89
93
  } else {
@@ -106,7 +110,7 @@ export function createApiHandlers(ctx: ApiContext): Route[] {
106
110
  }
107
111
 
108
112
  broadcastPlanUpdate(sessionId);
109
- watchSession(sessionId, sessionManager, broadcastPlanUpdate);
113
+ watchSession(sessionId, sessionManager, broadcastPlanUpdate, { tempDir: COMPILE_TEMP_DIR, components: HOST_COMPONENTS });
110
114
 
111
115
  const browserUrl = `http://localhost:${port}/s/${sessionId}`;
112
116
  return jsonResponse({
@@ -136,7 +140,7 @@ export function createApiHandlers(ctx: ApiContext): Route[] {
136
140
  if (session) {
137
141
  const jsx = sessionManager.readRevisionJsx(sessionId, rev, jsxFilename);
138
142
  if (jsx) {
139
- const result = await compilePlan(jsx, session.projectRoot);
143
+ const result = await compileJsx(jsx, { projectRoot: session.projectRoot, tempDir: COMPILE_TEMP_DIR, components: HOST_COMPONENTS });
140
144
  if (result.ok) {
141
145
  sessionManager.saveCompiled(sessionId, jsxFilename, result.js, rev);
142
146
  compiled = result.js;
@@ -160,7 +164,8 @@ export function createApiHandlers(ctx: ApiContext): Route[] {
160
164
 
161
165
  function handleHealth(): Response {
162
166
  const sessions = sessionManager.list().map((s) => s.id);
163
- return jsonResponse({ ok: true, sessions, version: ctx.version });
167
+ // The pid is what the CLI signals to stop us — a pid file it cannot corroborate is not.
168
+ return jsonResponse({ ok: true, sessions, version: ctx.version, pid: process.pid });
164
169
  }
165
170
 
166
171
  function handleMeta(_req: Request, _url: URL, match: URLPatternResult): Response {
@@ -1,9 +1,7 @@
1
1
  import { join, resolve } from "path";
2
2
  import { readdir } from "fs/promises";
3
- import type { SessionManager } from "../session";
3
+ import { jsonResponse, type Route, type SessionManager } from "@fabrika/canvas-kernel/server";
4
4
  import { LANG_MAP } from "../../langMap";
5
- import { jsonResponse } from "./utils";
6
- import type { Route } from "../router";
7
5
 
8
6
  const IMAGE_MIME: Record<string, string> = {
9
7
  png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
@@ -1,6 +1,6 @@
1
1
  import { join, dirname } from "path";
2
2
  import { DATA_DIR } from "../paths";
3
- import type { Route } from "../router";
3
+ import type { Route } from "@fabrika/canvas-kernel/server";
4
4
 
5
5
  const MIME_TYPES: Record<string, string> = {
6
6
  js: "application/javascript",
@@ -1,9 +1,7 @@
1
1
  import { writeFileSync } from "fs";
2
2
  import { join } from "path";
3
+ import { jsonResponse, type Route, type SessionManager } from "@fabrika/canvas-kernel/server";
3
4
  import { UPLOADS_DIR } from "../paths";
4
- import type { SessionManager } from "../session";
5
- import { jsonResponse } from "./utils";
6
- import type { Route } from "../router";
7
5
 
8
6
  const IMAGE_MIME: Record<string, string> = {
9
7
  png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
@@ -1,29 +1,15 @@
1
1
  import { join } from "path";
2
- import { homedir, tmpdir } from "os";
3
- import { mkdirSync } from "fs";
2
+ import { createCanvasPaths } from "@fabrika/canvas-kernel/server";
4
3
 
5
- /** Persistent data — survives reboots */
6
- export const DATA_DIR = join(homedir(), ".claude", "agent-canvas");
4
+ /** agent-canvas's on-disk layout. Kept in sync with bin/lib/config.ts. */
5
+ export const paths = createCanvasPaths({ appName: "agent-canvas" });
7
6
 
8
- /** Session storage */
9
- export const SESSIONS_DIR = join(DATA_DIR, "sessions");
10
-
11
- /** Ephemeral — can be lost on reboot */
12
- export const TEMP_DIR = join(tmpdir(), "agent-canvas");
13
-
14
- /** PID file for daemon process */
15
- export const PID_FILE = join(TEMP_DIR, "daemon.pid");
7
+ export const DATA_DIR = paths.dataDir;
8
+ export const SESSIONS_DIR = paths.sessionsDir;
9
+ export const TEMP_DIR = paths.tempDir;
10
+ export const PID_FILE = paths.pidFile;
11
+ export const COMPILE_TEMP_DIR = paths.compileTempDir;
12
+ export const UPLOADS_DIR = paths.uploadsDir;
16
13
 
17
14
  /** Capability used only by the CLI to resolve secret values */
18
- export const CLI_AUTH_FILE = join(TEMP_DIR, "daemon-auth-token");
19
-
20
- /** Temp dir for JSX compilation */
21
- export const COMPILE_TEMP_DIR = join(TEMP_DIR, "compile");
22
-
23
- /** Uploaded annotation images */
24
- export const UPLOADS_DIR = join(TEMP_DIR, "uploads");
25
-
26
- // Ensure dirs exist
27
- mkdirSync(SESSIONS_DIR, { recursive: true });
28
- mkdirSync(COMPILE_TEMP_DIR, { recursive: true });
29
- mkdirSync(UPLOADS_DIR, { recursive: true });
15
+ export const CLI_AUTH_FILE = join(paths.tempDir, "daemon-auth-token");
@@ -8,7 +8,7 @@ import { describe, expect, test, afterEach, beforeEach } from "bun:test";
8
8
  import { mkdtempSync, rmSync } from "fs";
9
9
  import { join } from "path";
10
10
  import { tmpdir } from "os";
11
- import { SessionManager } from "./session";
11
+ import { SessionManager } from "@fabrika/canvas-kernel/server";
12
12
  import { startRemoteFeedbackPoller } from "./remote-feedback";
13
13
 
14
14
  let testDir: string;
@@ -1,4 +1,4 @@
1
- import type { SessionManager, RemoteFeedbackEntry, RemoteAnnotation } from "./session";
1
+ import type { SessionManager, RemoteFeedbackEntry, RemoteAnnotation } from "@fabrika/canvas-kernel/server";
2
2
  import { loadShareConfig } from "./share";
3
3
  import { importShareKey, decryptString } from "../client/shareCrypto";
4
4
 
@@ -3,8 +3,7 @@ import { mkdtempSync, rmSync } from "fs";
3
3
  import { tmpdir } from "os";
4
4
  import { join } from "path";
5
5
  import { createApiHandlers } from "./handlers/api";
6
- import { dispatch, type Route } from "./router";
7
- import { SessionManager } from "./session";
6
+ import { dispatch, SessionManager, type Route } from "@fabrika/canvas-kernel/server";
8
7
 
9
8
  const testDirectories: string[] = [];
10
9
 
@@ -2,30 +2,33 @@ import { writeFileSync, mkdirSync, readFileSync, chmodSync } from "fs";
2
2
  import { join, dirname } from "path";
3
3
  import { tmpdir } from "os";
4
4
  import { randomBytes } from "crypto";
5
- import { SessionManager } from "./session";
6
- import { createWebSocketManager, type WSData } from "./websocket";
7
- import { dispatch } from "./router";
8
- import { jsonResponse, corsHeaders } from "./handlers/utils";
5
+ import {
6
+ corsHeaders,
7
+ createWebSocketManager,
8
+ dispatch,
9
+ jsonResponse,
10
+ SessionManager,
11
+ type WSData,
12
+ } from "@fabrika/canvas-kernel/server";
9
13
  import { createApiHandlers } from "./handlers/api";
10
14
  import { createFileHandlers } from "./handlers/files";
11
15
  import { createStaticHandlers } from "./handlers/static";
12
16
  import { createUploadHandlers } from "./handlers/uploads";
13
17
  import { startRemoteFeedbackPoller } from "./remote-feedback";
14
- import { CLI_AUTH_FILE } from "./paths";
18
+ import { CLI_AUTH_FILE, PID_FILE, SESSIONS_DIR, TEMP_DIR } from "./paths";
15
19
 
16
20
  const PORT = parseInt(process.env.CANVAS_PORT || "19400", 10);
17
21
  const VERSION = JSON.parse(readFileSync(join(dirname(import.meta.dir), "..", "package.json"), "utf-8")).version as string;
18
22
 
19
23
  // Write PID file so CLI can find and stop us
20
- const pidDir = join(tmpdir(), "agent-canvas");
21
- mkdirSync(pidDir, { recursive: true, mode: 0o700 });
22
- chmodSync(pidDir, 0o700);
23
- writeFileSync(join(pidDir, "daemon.pid"), String(process.pid));
24
+ mkdirSync(TEMP_DIR, { recursive: true, mode: 0o700 });
25
+ chmodSync(TEMP_DIR, 0o700);
26
+ writeFileSync(PID_FILE, String(process.pid));
24
27
  const cliAuthToken = randomBytes(32).toString("base64url");
25
28
  writeFileSync(CLI_AUTH_FILE, cliAuthToken, { mode: 0o600 });
26
29
  chmodSync(CLI_AUTH_FILE, 0o600);
27
30
 
28
- const sessionManager = new SessionManager();
31
+ const sessionManager = new SessionManager(SESSIONS_DIR);
29
32
  const wsManager = createWebSocketManager(sessionManager);
30
33
 
31
34
  const routes = [
@@ -16,7 +16,7 @@ import { tmpdir } from "os";
16
16
 
17
17
  import workerHandler from "../../workers/canvas-share/src/index";
18
18
  import type { Env } from "../../workers/canvas-share/src/types";
19
- import { SessionManager } from "./session";
19
+ import { SessionManager } from "@fabrika/canvas-kernel/server";
20
20
  import { buildSharePayload, encryptSharePayload } from "./share";
21
21
  import {
22
22
  generateShareKey,
@@ -8,7 +8,7 @@ import { describe, expect, test, beforeEach, afterEach } from "bun:test";
8
8
  import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "fs";
9
9
  import { join } from "path";
10
10
  import { tmpdir } from "os";
11
- import { SessionManager } from "./session";
11
+ import { SessionManager } from "@fabrika/canvas-kernel/server";
12
12
  import { buildSharePayload, pushShareToWorker, shareRevision, loadShareConfig, encryptSharePayload } from "./share";
13
13
  import { generateShareKey, importShareKey, decryptString } from "../client/shareCrypto";
14
14
 
@@ -1,4 +1,4 @@
1
- import type { SessionManager, ShareEntry } from "./session";
1
+ import type { SessionManager, ShareEntry } from "@fabrika/canvas-kernel/server";
2
2
  import {
3
3
  generateShareKey,
4
4
  exportShareKey,
@@ -8,16 +8,14 @@
8
8
  "esModuleInterop": true,
9
9
  "skipLibCheck": true,
10
10
  "noEmit": true,
11
- "rootDir": ".",
12
11
  "types": ["@types/bun"],
13
12
  "paths": {
14
- "#canvas/runtime": ["./client/runtime.ts"],
13
+ "#canvas/runtime": ["../packages/canvas-kernel/src/client/runtime.ts"],
15
14
  "#canvas/components": ["./client/components/index.ts"]
16
15
  }
17
16
  },
18
17
  "include": ["src/**/*", "client/**/*"],
19
18
  "exclude": [
20
- "client/compat/**/*",
21
19
  "src/share-e2e.test.ts"
22
20
  ]
23
21
  }
package/package.json CHANGED
@@ -1,17 +1,24 @@
1
1
  {
2
2
  "name": "agent-canvas",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "Interactive visual canvas for Claude Code — rich annotatable documents in the browser",
5
5
  "bin": {
6
6
  "agent-canvas": "bin/agent-canvas.js"
7
7
  },
8
+ "files": [
9
+ "bin",
10
+ "daemon",
11
+ "skills"
12
+ ],
8
13
  "scripts": {
9
14
  "build": "bun daemon/build.ts",
10
- "typecheck": "tsc --noEmit && tsc --noEmit -p daemon/tsconfig.json",
15
+ "check:versions": "bun scripts/check-release-versions.ts",
16
+ "typecheck": "tsc --noEmit && tsc --noEmit -p packages/canvas-kernel/tsconfig.json && tsc --noEmit -p daemon/tsconfig.json",
11
17
  "test": "bun test",
12
18
  "prepublishOnly": "bun daemon/build.ts"
13
19
  },
14
20
  "dependencies": {
21
+ "@fabrika/canvas-kernel": "0.1.45",
15
22
  "marked": "^17.0.4",
16
23
  "mermaid-parser-bundle": "^0.1.2",
17
24
  "preact": "^10.25.0",
@@ -30,6 +37,7 @@
30
37
  "url": "https://github.com/contember/agent-canvas"
31
38
  },
32
39
  "workspaces": [
40
+ "packages/*",
33
41
  "workers/*"
34
42
  ],
35
43
  "license": "MIT",
package/.github/dark.png DELETED
Binary file
package/.github/light.png DELETED
Binary file
@@ -1,48 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
-
8
- jobs:
9
- typecheck:
10
- runs-on: ubuntu-latest
11
- steps:
12
- - uses: actions/checkout@v4
13
-
14
- - uses: oven-sh/setup-bun@v2
15
-
16
- - name: Install dependencies
17
- run: bun install --frozen-lockfile
18
-
19
- - name: Typecheck
20
- run: bun run typecheck
21
-
22
- - name: Test
23
- run: bun test
24
-
25
- - name: Build
26
- run: bun run build
27
-
28
- worker:
29
- runs-on: ubuntu-latest
30
- steps:
31
- - uses: actions/checkout@v4
32
-
33
- - uses: oven-sh/setup-bun@v2
34
-
35
- - name: Install dependencies
36
- run: bun install --frozen-lockfile
37
-
38
- - name: Typecheck
39
- working-directory: workers/canvas-share
40
- run: bun run typecheck
41
-
42
- - name: Test
43
- working-directory: workers/canvas-share
44
- run: bun test
45
-
46
- - name: Generate wrangler config (local env)
47
- working-directory: workers/canvas-share
48
- run: bun run oblaka
@@ -1,19 +0,0 @@
1
- name: Deploy worker to prod
2
-
3
- on:
4
- # Triggered by version tags so the worker deploys alongside the npm
5
- # release. This keeps the runtime bundles served by the worker in
6
- # lockstep with the @agent-canvas package version users install.
7
- push:
8
- tags:
9
- - "v*"
10
- workflow_dispatch:
11
-
12
- jobs:
13
- deploy:
14
- uses: ./.github/workflows/deploy-worker.yml
15
- with:
16
- env: prod
17
- # inherit so the reusable workflow can resolve secrets; the env-scoped
18
- # values (set on the `prod` environment) win over repo-level ones.
19
- secrets: inherit
@@ -1,21 +0,0 @@
1
- name: Deploy worker to stage
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- paths:
7
- - "workers/canvas-share/**"
8
- - "daemon/client/**"
9
- - "daemon/build.ts"
10
- - ".github/workflows/deploy-worker.yml"
11
- - ".github/workflows/deploy-worker-stage.yml"
12
- workflow_dispatch:
13
-
14
- jobs:
15
- deploy:
16
- uses: ./.github/workflows/deploy-worker.yml
17
- with:
18
- env: stage
19
- # inherit so the reusable workflow can resolve secrets; the env-scoped
20
- # values (set on the `stage` environment) win over repo-level ones.
21
- secrets: inherit
@@ -1,72 +0,0 @@
1
- name: Reusable Worker Deploy
2
-
3
- on:
4
- workflow_call:
5
- inputs:
6
- env:
7
- description: "Target environment (stage or prod)"
8
- required: true
9
- type: string
10
- # No `secrets:` inputs on purpose. The deploy job below declares
11
- # `environment: ${{ inputs.env }}`, so referencing `secrets.X` directly
12
- # resolves the matching *environment* secret (falling back to a
13
- # repo-level secret of the same name). Callers pass `secrets: inherit`
14
- # so org/repo secrets remain reachable too. This is what makes the
15
- # CLOUDFLARE_* / SHARE_AUTH_TOKEN values environment-scoped — a prod
16
- # token can never be read by a stage deploy and vice versa.
17
-
18
- jobs:
19
- deploy:
20
- runs-on: ubuntu-latest
21
- environment: ${{ inputs.env }}
22
- steps:
23
- - uses: actions/checkout@v4
24
-
25
- - uses: oven-sh/setup-bun@v2
26
-
27
- - name: Install dependencies
28
- run: bun install --frozen-lockfile
29
-
30
- # Build the daemon bundles first. The root build script auto-mirrors
31
- # them into workers/canvas-share/public/assets/ so the worker's
32
- # `[assets]` binding always ships the exact runtime that was pushed
33
- # to npm for this version — no risk of drift between the client code
34
- # inside a share payload and the runtime bundles served by the worker.
35
- - name: Build runtime bundles
36
- run: bun run build
37
-
38
- # Ask oblaka to create/update the Cloudflare resources (R2 bucket,
39
- # KV namespace) for this environment and emit a fresh wrangler.jsonc
40
- # pointed at them. `--remote` is what triggers the actual CF API
41
- # calls; without it oblaka just regenerates the config file locally.
42
- - name: Provision Cloudflare resources
43
- working-directory: workers/canvas-share
44
- env:
45
- CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
46
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
47
- run: bunx oblaka oblaka.ts --env ${{ inputs.env }} --remote --state-namespace agent-state-kv
48
-
49
- # Push the bearer token as a Worker secret if it was provided. Using
50
- # `secret bulk` lets us set multiple secrets in one API call and
51
- # keeps the value out of the generated wrangler.jsonc. Skipped
52
- # silently when no token is configured (dev envs).
53
- - name: Set worker secrets
54
- working-directory: workers/canvas-share
55
- env:
56
- CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
57
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
58
- SHARE_AUTH_TOKEN: ${{ secrets.SHARE_AUTH_TOKEN }}
59
- run: |
60
- if [ -z "$SHARE_AUTH_TOKEN" ]; then
61
- echo "SHARE_AUTH_TOKEN not set — worker will run in open-share mode"
62
- exit 0
63
- fi
64
- printf '{"SHARE_AUTH_TOKEN":"%s"}\n' "$SHARE_AUTH_TOKEN" \
65
- | bunx wrangler secret bulk
66
-
67
- - name: Deploy worker
68
- working-directory: workers/canvas-share
69
- env:
70
- CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
71
- CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
72
- run: bunx wrangler deploy
@@ -1,36 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
-
8
- permissions: {}
9
-
10
- jobs:
11
- publish:
12
- runs-on: ubuntu-latest
13
- permissions:
14
- contents: read
15
- id-token: write
16
- steps:
17
- - uses: actions/checkout@v4
18
-
19
- - uses: oven-sh/setup-bun@v2
20
-
21
- - uses: actions/setup-node@v4
22
- with:
23
- node-version: "24.x"
24
- registry-url: "https://registry.npmjs.org"
25
-
26
- - name: Install dependencies
27
- run: bun install --frozen-lockfile
28
-
29
- - name: Typecheck
30
- run: bun run typecheck
31
-
32
- - name: Build
33
- run: bun run build
34
-
35
- - name: Publish to npm
36
- run: npm publish --provenance --access public
package/CLAUDE.md DELETED
@@ -1,86 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project
6
-
7
- Agent Canvas — an interactive browser-based visual canvas for Claude Code. Users author JSX documents that get compiled and rendered in the browser with annotation, feedback, and file browsing capabilities.
8
-
9
- ## Architecture
10
-
11
- Three components communicate via HTTP and WebSocket:
12
-
13
- - **CLI** (`bin/agent-canvas.ts`) — pushes JSX canvases to the daemon, waits for user feedback, manages daemon lifecycle
14
- - **Daemon** (`daemon/src/server.ts`) — Bun HTTP+WS server on port 19400 (`CANVAS_PORT`). Compiles JSX, manages sessions, serves the browser UI
15
- - **Browser UI** (`daemon/client/`) — React 18 app loaded via CDN UMD with ESM import maps. Handles annotations, revision history, feedback collection, file browsing
16
-
17
- **Data flow:** CLI pushes JSX → daemon compiles via `Bun.build()` → browser loads compiled JS module → user annotates/responds → feedback sent back to CLI via WebSocket
18
-
19
- **Session model:** Each canvas push creates/updates a session identified by `CANVAS_SESSION_ID` (set by hook). Sessions are stored on disk at `~/.claude/agent-canvas/sessions/{id}/` with a revision system (`revisions/{rev}/plan.jsx`, `plan.compiled.js`, `feedback.md`).
20
-
21
- ## Build
22
-
23
- ```bash
24
- bun install
25
- bun run build # builds client assets to daemon/dist/
26
- bun daemon/build.ts --watch # watch mode with debounced rebuild
27
- ```
28
-
29
- The build produces three bundles (runtime.js, components.js, client.js) plus Tailwind CSS. React is loaded from CDN, not bundled. Import maps route `#canvas/components` and `#canvas/runtime` to the local bundles.
30
-
31
- ## Typecheck
32
-
33
- ```bash
34
- bun run typecheck # runs tsc --noEmit on both root (bin/) and daemon (client/ + src/)
35
- ```
36
-
37
- Two separate tsconfig files: `tsconfig.json` (root, covers `bin/`) and `daemon/tsconfig.json` (covers `daemon/src/` and `daemon/client/`). CI runs typecheck on every push and PR.
38
-
39
- ## Key Technical Details
40
-
41
- - **Runtime is Bun only** — no Node.js compatibility needed
42
- - **JSX compilation** uses temp files because `Bun.build()` doesn't support `stdin`. See `daemon/src/compiler.ts`
43
- - **Component imports are injected** by the compiler — authored JSX can use `Section`, `Task`, `CodeBlock`, etc. without imports
44
- - **Adding a new component:** create in `daemon/client/components/`, export from `index.ts`, add to `COMPONENT_IMPORTS` in `daemon/src/compiler.ts`, rebuild
45
- - **Bun's `spawn` throws synchronously** on missing executables — always check with `which` before spawning
46
-
47
- ## Testing with Demo
48
-
49
- A demo JSX file lives at `example/plan.jsx` in the project root. To test UI changes:
50
-
51
- ```bash
52
- # 1. Start daemon (if not running)
53
- bun bin/agent-canvas.ts daemon start
54
-
55
- # 2. Build client assets (use --watch for live reload)
56
- bun daemon/build.ts --watch
57
-
58
- # 3. Push the demo plan
59
- CANVAS_SESSION_ID=planner-demo bun bin/agent-canvas.ts push example/plan.jsx --label "Demo"
60
-
61
- # 4. Open in browser
62
- # http://localhost:19400/s/planner-demo
63
- ```
64
-
65
- The CLI blocks waiting for feedback after push — press Ctrl+C to exit without submitting. Each push creates a new revision visible in the browser's revision selector.
66
-
67
- ## Publishing
68
-
69
- To release a new version:
70
-
71
- 1. Bump the version in `package.json`
72
- 2. Commit the change and create a git tag: `git tag v<version>`
73
- 3. Push both the commit and the tag: `git push && git push origin v<version>`
74
-
75
- The CI pipeline handles `npm publish` automatically when a new version tag is pushed. Do not run `npm publish` manually.
76
-
77
- ## Bundled Skill
78
-
79
- This package distributes a Claude Code skill in `skills/canvas/`. It teaches Claude Code how to use Agent Canvas — writing JSX canvases, pushing, waiting for feedback, iterating, and responding to user feedback. The skill is automatically available to users who install this package.
80
-
81
- ## Environment Variables
82
-
83
- - `CANVAS_SESSION_ID` — current session (set by SessionStart hook)
84
- - `CANVAS_PROJECT_ROOT` — project directory for file serving
85
- - `CANVAS_PORT` — daemon port (default: 19400)
86
- - `CANVAS_TIMEOUT` — CLI feedback wait timeout (default: 1 hour)