@1agh/maude 1.0.11 → 1.3.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.
Files changed (567) hide show
  1. package/README.md +3 -1
  2. package/apps/studio/acp/bridge.ts +18 -13
  3. package/apps/studio/acp/index.ts +43 -7
  4. package/apps/studio/acp/transcript-io.ts +394 -0
  5. package/apps/studio/acp/transcript.ts +103 -38
  6. package/apps/studio/annotations-layer.tsx +191 -116
  7. package/apps/studio/annotations-model.ts +39 -0
  8. package/apps/studio/annotations-sync.ts +50 -0
  9. package/apps/studio/api.ts +821 -55
  10. package/apps/studio/bin/_import-figma.mjs +57 -11
  11. package/apps/studio/bin/_seed-fixture.mjs +163 -0
  12. package/apps/studio/bin/annotate.mjs +3 -1
  13. package/apps/studio/bin/server-up.sh +17 -2
  14. package/apps/studio/canvas-edit.ts +391 -7
  15. package/apps/studio/canvas-imports.ts +89 -0
  16. package/apps/studio/canvas-lib.tsx +82 -18
  17. package/apps/studio/canvas-notice-message.ts +16 -0
  18. package/apps/studio/canvas-notifications.tsx +19 -0
  19. package/apps/studio/canvas-shell.tsx +57 -6
  20. package/apps/studio/client/app.jsx +943 -248
  21. package/apps/studio/client/apply-edit-request.ts +55 -0
  22. package/apps/studio/client/export-center.jsx +125 -67
  23. package/apps/studio/client/github.js +38 -1
  24. package/apps/studio/client/index-loader.ts +76 -0
  25. package/apps/studio/client/panels/CloudBar.jsx +52 -6
  26. package/apps/studio/client/panels/GitPanel.jsx +103 -0
  27. package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
  28. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
  29. package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
  30. package/apps/studio/client/panels/SyncPanel.jsx +238 -4
  31. package/apps/studio/client/panels/TeamProjects.jsx +376 -0
  32. package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
  33. package/apps/studio/client/photo-knobs.jsx +3 -0
  34. package/apps/studio/client/report-bug.jsx +19 -1
  35. package/apps/studio/client/share-dialog.jsx +91 -0
  36. package/apps/studio/client/share-link.js +94 -0
  37. package/apps/studio/client/styles/3-shell-maude.css +69 -18
  38. package/apps/studio/client/styles/4-components.css +44 -1
  39. package/apps/studio/client/tree-row-menu.jsx +48 -3
  40. package/apps/studio/client/whats-new.jsx +26 -32
  41. package/apps/studio/cloud/endpoints.ts +132 -0
  42. package/apps/studio/collab/index.ts +4 -0
  43. package/apps/studio/collab/persistence.ts +137 -17
  44. package/apps/studio/collab/registry.ts +19 -21
  45. package/apps/studio/collab/room.ts +13 -2
  46. package/apps/studio/context.ts +59 -0
  47. package/apps/studio/debug-bundle.ts +13 -0
  48. package/apps/studio/dist/client.bundle.js +1911 -1879
  49. package/apps/studio/dist/runtime/.min-sizes.json +1 -0
  50. package/apps/studio/dist/runtime/sonner.js +1 -0
  51. package/apps/studio/dist/styles.css +1 -1
  52. package/apps/studio/export-dialog.tsx +54 -0
  53. package/apps/studio/exporters/degraded.ts +19 -3
  54. package/apps/studio/exporters/ghostscript.ts +194 -0
  55. package/apps/studio/exporters/pdf-fonts.ts +261 -0
  56. package/apps/studio/exporters/pdf.ts +152 -3
  57. package/apps/studio/exporters/remote.ts +36 -1
  58. package/apps/studio/fs-watch.ts +45 -1
  59. package/apps/studio/http.ts +296 -19
  60. package/apps/studio/inspect.ts +19 -0
  61. package/apps/studio/managed-projects.ts +150 -0
  62. package/apps/studio/mem.ts +45 -9
  63. package/apps/studio/notifications.tsx +292 -0
  64. package/apps/studio/runtime-bundle.ts +2 -0
  65. package/apps/studio/server.ts +69 -6
  66. package/apps/studio/sync/accepted-cold-start.ts +225 -0
  67. package/apps/studio/sync/accepted-link.ts +320 -0
  68. package/apps/studio/sync/action-stage.ts +343 -0
  69. package/apps/studio/sync/agent.ts +104 -45
  70. package/apps/studio/sync/asset-push.ts +1 -47
  71. package/apps/studio/sync/cell-file-events.ts +3 -0
  72. package/apps/studio/sync/codec.ts +192 -34
  73. package/apps/studio/sync/cold-start-apply.ts +7 -0
  74. package/apps/studio/sync/cold-start.ts +118 -22
  75. package/apps/studio/sync/comment-identity.ts +68 -0
  76. package/apps/studio/sync/connection-state.ts +15 -1
  77. package/apps/studio/sync/ctl-provider.ts +15 -2
  78. package/apps/studio/sync/document-discovery.ts +81 -0
  79. package/apps/studio/sync/file-ledger.ts +117 -0
  80. package/apps/studio/sync/file-membership.ts +10 -2
  81. package/apps/studio/sync/file-plane.ts +926 -47
  82. package/apps/studio/sync/index.ts +1786 -36
  83. package/apps/studio/sync/journal-client.ts +13 -1
  84. package/apps/studio/sync/migrate-seed.ts +150 -22
  85. package/apps/studio/sync/poke.ts +4 -2
  86. package/apps/studio/sync/presentation.ts +311 -5
  87. package/apps/studio/sync/projection.ts +820 -23
  88. package/apps/studio/sync/repeated-module.ts +73 -0
  89. package/apps/studio/sync/retry-after.ts +169 -0
  90. package/apps/studio/sync/revision-barrier.ts +129 -0
  91. package/apps/studio/sync/seed-progress.ts +218 -0
  92. package/apps/studio/sync/seed-repair.ts +46 -0
  93. package/apps/studio/sync/source-merge.ts +100 -0
  94. package/apps/studio/sync/source-ops.ts +289 -0
  95. package/apps/studio/sync/source-recovery.ts +70 -0
  96. package/apps/studio/sync/source-validation.ts +56 -0
  97. package/apps/studio/sync/status.ts +201 -6
  98. package/apps/studio/sync/transaction-client.ts +460 -0
  99. package/apps/studio/sync/transport-error.ts +111 -0
  100. package/apps/studio/sync/writer-registry.ts +236 -0
  101. package/apps/studio/text-caret.ts +35 -0
  102. package/apps/studio/undo-hud.tsx +9 -87
  103. package/apps/studio/use-canvas-media-drop.tsx +4 -39
  104. package/apps/studio/whats-new.json +173 -0
  105. package/cli/bin/maude.mjs +2 -0
  106. package/cli/commands/codex.mjs +191 -0
  107. package/cli/commands/harness.mjs +1051 -0
  108. package/cli/commands/help.mjs +13 -0
  109. package/cli/lib/design-link.mjs +190 -2
  110. package/cli/lib/harness/capabilities.mjs +302 -0
  111. package/cli/lib/harness/codex-runtime.mjs +892 -0
  112. package/cli/lib/harness/compatibility.mjs +27 -0
  113. package/cli/lib/harness/discover-claude.mjs +1393 -0
  114. package/cli/lib/harness/managed-state.mjs +1296 -0
  115. package/cli/lib/harness/model.mjs +86 -0
  116. package/cli/lib/harness/secrets.mjs +390 -0
  117. package/cli/lib/harness/targets/codex.mjs +1141 -0
  118. package/cli/lib/harness/targets/opencode.mjs +807 -0
  119. package/cli/lib/harness/transaction.mjs +1099 -0
  120. package/cli/lib/hubs-config.mjs +35 -8
  121. package/cli/templates/harness/opencode/maude-projector.ts +58 -0
  122. package/docs/harness-capability-matrix.md +125 -0
  123. package/docs/harness-environment-projection.md +95 -0
  124. package/package.json +33 -9
  125. package/plugins/design/dependencies.json +3 -3
  126. package/plugins/design/templates/_shell.html +47 -0
  127. package/plugins/flow/.claude-plugin/config.schema.json +8 -0
  128. package/plugins/flow/dependencies.json +3 -3
  129. package/apps/studio/.ai/cache/_stats.json +0 -7
  130. package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
  131. package/apps/studio/bin/_curl-local.test.mjs +0 -280
  132. package/apps/studio/bin/_pw-launch.test.mjs +0 -90
  133. package/apps/studio/bin/_smart-frames.test.mjs +0 -195
  134. package/apps/studio/bin/_transcribe.test.mjs +0 -80
  135. package/apps/studio/bun.lock +0 -928
  136. package/apps/studio/draw/test/animate.test.ts +0 -172
  137. package/apps/studio/draw/test/brush.test.ts +0 -213
  138. package/apps/studio/draw/test/composition.test.ts +0 -141
  139. package/apps/studio/draw/test/geometry.test.ts +0 -199
  140. package/apps/studio/draw/test/gradient.test.ts +0 -167
  141. package/apps/studio/draw/test/layout.test.ts +0 -120
  142. package/apps/studio/draw/test/morph.test.ts +0 -107
  143. package/apps/studio/draw/test/optimize.test.ts +0 -40
  144. package/apps/studio/draw/test/palette.test.ts +0 -123
  145. package/apps/studio/draw/test/primitives.test.ts +0 -129
  146. package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
  147. package/apps/studio/draw/test/serialize.test.ts +0 -105
  148. package/apps/studio/figma/assets.test.ts +0 -464
  149. package/apps/studio/figma/client.test.ts +0 -395
  150. package/apps/studio/figma/codegen-client.test.ts +0 -276
  151. package/apps/studio/figma/codegen-fonts.test.ts +0 -103
  152. package/apps/studio/figma/codegen-values.test.ts +0 -179
  153. package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
  154. package/apps/studio/figma/fig-decode.test.ts +0 -788
  155. package/apps/studio/figma/fig-differential.test.ts +0 -182
  156. package/apps/studio/figma/fig-translator.test.ts +0 -192
  157. package/apps/studio/figma/fig-vector.test.ts +0 -113
  158. package/apps/studio/figma/from-codegen.test.ts +0 -408
  159. package/apps/studio/figma/sanitize.test.ts +0 -325
  160. package/apps/studio/figma/tailwind-map.test.ts +0 -142
  161. package/apps/studio/figma/to-artboard.test.ts +0 -808
  162. package/apps/studio/figma/to-render.test.ts +0 -180
  163. package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
  164. package/apps/studio/figma/to-strokes.test.ts +0 -705
  165. package/apps/studio/figma/to-tokens.test.ts +0 -321
  166. package/apps/studio/figma/url.test.ts +0 -167
  167. package/apps/studio/footage/schema.test.ts +0 -287
  168. package/apps/studio/generation/adapters/groq.test.ts +0 -80
  169. package/apps/studio/generation/audio-library.test.ts +0 -95
  170. package/apps/studio/generation/captions.test.ts +0 -78
  171. package/apps/studio/generation/elevenlabs.test.ts +0 -223
  172. package/apps/studio/generation/gemini.test.ts +0 -350
  173. package/apps/studio/generation/jobs.test.ts +0 -128
  174. package/apps/studio/generation/keys.test.ts +0 -75
  175. package/apps/studio/generation/prefs.test.ts +0 -95
  176. package/apps/studio/generation/types.test.ts +0 -123
  177. package/apps/studio/generation/whisper-models.test.ts +0 -179
  178. package/apps/studio/test/_helpers.ts +0 -108
  179. package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
  180. package/apps/studio/test/acp-activity.test.ts +0 -154
  181. package/apps/studio/test/acp-ai-activity.test.ts +0 -182
  182. package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
  183. package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
  184. package/apps/studio/test/acp-branch-guard.test.ts +0 -123
  185. package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
  186. package/apps/studio/test/acp-bridge.test.ts +0 -545
  187. package/apps/studio/test/acp-capabilities.test.ts +0 -123
  188. package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
  189. package/apps/studio/test/acp-commands.test.ts +0 -119
  190. package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
  191. package/apps/studio/test/acp-elicitation.test.ts +0 -251
  192. package/apps/studio/test/acp-env.test.ts +0 -95
  193. package/apps/studio/test/acp-mode-banner.test.ts +0 -45
  194. package/apps/studio/test/acp-origin-gate.test.ts +0 -160
  195. package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
  196. package/apps/studio/test/acp-permission.test.ts +0 -278
  197. package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
  198. package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
  199. package/apps/studio/test/acp-session-plugins.test.ts +0 -209
  200. package/apps/studio/test/acp-toolgroup.test.ts +0 -76
  201. package/apps/studio/test/acp-transcript-view.test.ts +0 -71
  202. package/apps/studio/test/acp-transcript.test.ts +0 -238
  203. package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
  204. package/apps/studio/test/acp-usage.test.ts +0 -143
  205. package/apps/studio/test/acp-write-gate.test.ts +0 -323
  206. package/apps/studio/test/acp-write-scope.test.ts +0 -533
  207. package/apps/studio/test/active-state.test.ts +0 -186
  208. package/apps/studio/test/activity.test.ts +0 -189
  209. package/apps/studio/test/ai-activity.test.ts +0 -113
  210. package/apps/studio/test/annotate-write.test.ts +0 -1074
  211. package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
  212. package/apps/studio/test/annotations-align.test.ts +0 -88
  213. package/apps/studio/test/annotations-api.test.ts +0 -145
  214. package/apps/studio/test/annotations-bindings.test.ts +0 -262
  215. package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
  216. package/apps/studio/test/annotations-groups.test.ts +0 -231
  217. package/apps/studio/test/annotations-layer.test.ts +0 -1011
  218. package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
  219. package/apps/studio/test/annotations-snap.test.ts +0 -135
  220. package/apps/studio/test/annotations-text-format.test.ts +0 -396
  221. package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
  222. package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
  223. package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
  224. package/apps/studio/test/artboard-kinds.test.tsx +0 -83
  225. package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
  226. package/apps/studio/test/asset-api.test.ts +0 -149
  227. package/apps/studio/test/assets-s3.test.ts +0 -249
  228. package/apps/studio/test/binary-smoke.test.ts +0 -47
  229. package/apps/studio/test/boot-self-heal.test.ts +0 -87
  230. package/apps/studio/test/browse-posture.test.tsx +0 -107
  231. package/apps/studio/test/bundle-smoke.test.ts +0 -46
  232. package/apps/studio/test/camera-reveal.test.tsx +0 -173
  233. package/apps/studio/test/canvas-artifacts.test.ts +0 -125
  234. package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
  235. package/apps/studio/test/canvas-build.test.ts +0 -141
  236. package/apps/studio/test/canvas-create-api.test.ts +0 -676
  237. package/apps/studio/test/canvas-cursors.test.ts +0 -164
  238. package/apps/studio/test/canvas-edit.test.ts +0 -470
  239. package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
  240. package/apps/studio/test/canvas-header.test.ts +0 -127
  241. package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
  242. package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
  243. package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
  244. package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
  245. package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
  246. package/apps/studio/test/canvas-list-watch.test.ts +0 -388
  247. package/apps/studio/test/canvas-media-drop.test.ts +0 -187
  248. package/apps/studio/test/canvas-meta-api.test.ts +0 -707
  249. package/apps/studio/test/canvas-move-api.test.ts +0 -587
  250. package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
  251. package/apps/studio/test/canvas-pipeline.test.ts +0 -180
  252. package/apps/studio/test/canvas-rects.test.ts +0 -198
  253. package/apps/studio/test/canvas-reorder.test.ts +0 -211
  254. package/apps/studio/test/canvas-route.test.ts +0 -179
  255. package/apps/studio/test/canvas-shell-base.test.ts +0 -107
  256. package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
  257. package/apps/studio/test/canvas-url.test.ts +0 -138
  258. package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
  259. package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
  260. package/apps/studio/test/capture-core.test.ts +0 -32
  261. package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
  262. package/apps/studio/test/chat-attachments.test.ts +0 -117
  263. package/apps/studio/test/chat-context.test.ts +0 -129
  264. package/apps/studio/test/chat-markdown.test.tsx +0 -58
  265. package/apps/studio/test/clip-addressing.test.ts +0 -738
  266. package/apps/studio/test/clip-ops.test.ts +0 -922
  267. package/apps/studio/test/cloud-build.test.ts +0 -95
  268. package/apps/studio/test/cloud-connect-note.test.ts +0 -135
  269. package/apps/studio/test/cloud-endpoints.test.ts +0 -590
  270. package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
  271. package/apps/studio/test/cloud-history-posture.test.ts +0 -230
  272. package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
  273. package/apps/studio/test/cloud-renew.test.ts +0 -205
  274. package/apps/studio/test/cloud-session-role.test.ts +0 -161
  275. package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
  276. package/apps/studio/test/cold-start-apply.test.ts +0 -303
  277. package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
  278. package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
  279. package/apps/studio/test/collab-bridge.test.ts +0 -79
  280. package/apps/studio/test/collab-loopback.test.ts +0 -63
  281. package/apps/studio/test/collab-origin-gate.test.ts +0 -323
  282. package/apps/studio/test/collab-protocol.test.ts +0 -146
  283. package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
  284. package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
  285. package/apps/studio/test/collab-room.test.ts +0 -188
  286. package/apps/studio/test/collab-session-survival.test.tsx +0 -176
  287. package/apps/studio/test/collab-stress.test.ts +0 -129
  288. package/apps/studio/test/comment-mount.test.ts +0 -97
  289. package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
  290. package/apps/studio/test/comments-api.test.ts +0 -277
  291. package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
  292. package/apps/studio/test/comments-overlay.test.ts +0 -117
  293. package/apps/studio/test/compile-entry.test.ts +0 -133
  294. package/apps/studio/test/component-map.test.ts +0 -48
  295. package/apps/studio/test/config-projection.test.ts +0 -104
  296. package/apps/studio/test/config-reload.test.ts +0 -230
  297. package/apps/studio/test/config-version.test.ts +0 -88
  298. package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
  299. package/apps/studio/test/convert-to-absolute.test.ts +0 -333
  300. package/apps/studio/test/credential-grammar.test.ts +0 -107
  301. package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
  302. package/apps/studio/test/csrf-write-guard.test.ts +0 -141
  303. package/apps/studio/test/debug-bundle.test.ts +0 -105
  304. package/apps/studio/test/design-setup-readiness.test.ts +0 -155
  305. package/apps/studio/test/detach-component.test.ts +0 -94
  306. package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
  307. package/apps/studio/test/dom-selection.test.ts +0 -198
  308. package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
  309. package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
  310. package/apps/studio/test/edit-persistence.test.ts +0 -91
  311. package/apps/studio/test/edit-scope-api.test.ts +0 -119
  312. package/apps/studio/test/edit-source-command.test.ts +0 -142
  313. package/apps/studio/test/edit-suppress.test.ts +0 -170
  314. package/apps/studio/test/editing-presence.test.ts +0 -103
  315. package/apps/studio/test/element-resize.test.ts +0 -136
  316. package/apps/studio/test/element-structural-api.test.ts +0 -434
  317. package/apps/studio/test/element-structural-edit.test.ts +0 -750
  318. package/apps/studio/test/ensure-browser.test.ts +0 -92
  319. package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
  320. package/apps/studio/test/export-assemble.test.ts +0 -53
  321. package/apps/studio/test/export-browser-lane.test.ts +0 -126
  322. package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
  323. package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
  324. package/apps/studio/test/export-center.test.tsx +0 -287
  325. package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
  326. package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
  327. package/apps/studio/test/export-lane.test.ts +0 -245
  328. package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
  329. package/apps/studio/test/exporters/canva.test.ts +0 -63
  330. package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
  331. package/apps/studio/test/exporters/endpoint.test.ts +0 -121
  332. package/apps/studio/test/exporters/history.test.ts +0 -107
  333. package/apps/studio/test/exporters/html.test.ts +0 -26
  334. package/apps/studio/test/exporters/jobs.test.ts +0 -342
  335. package/apps/studio/test/exporters/pdf.test.ts +0 -85
  336. package/apps/studio/test/exporters/png.test.ts +0 -103
  337. package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
  338. package/apps/studio/test/exporters/pptx.test.ts +0 -31
  339. package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
  340. package/apps/studio/test/exporters/runtime.test.ts +0 -59
  341. package/apps/studio/test/exporters/scope.test.ts +0 -292
  342. package/apps/studio/test/exporters/svg.test.ts +0 -29
  343. package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
  344. package/apps/studio/test/exporters/zip.test.ts +0 -104
  345. package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
  346. package/apps/studio/test/figjam-v3-model.test.ts +0 -342
  347. package/apps/studio/test/figma-explode.test.ts +0 -438
  348. package/apps/studio/test/figma-provenance.test.ts +0 -108
  349. package/apps/studio/test/figma-routes.test.ts +0 -294
  350. package/apps/studio/test/file-lock.test.ts +0 -84
  351. package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
  352. package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
  353. package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
  354. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
  355. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
  356. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
  357. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
  358. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
  359. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
  360. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
  361. package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
  362. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
  363. package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
  364. package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
  365. package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
  366. package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
  367. package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
  368. package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
  369. package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
  370. package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
  371. package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
  372. package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
  373. package/apps/studio/test/footage-store.test.ts +0 -100
  374. package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
  375. package/apps/studio/test/fs-watch.test.ts +0 -41
  376. package/apps/studio/test/gemma-models.test.ts +0 -245
  377. package/apps/studio/test/generate-route.test.ts +0 -106
  378. package/apps/studio/test/git-api.test.ts +0 -0
  379. package/apps/studio/test/git-branches.test.ts +0 -341
  380. package/apps/studio/test/git-cloud-posture.test.ts +0 -59
  381. package/apps/studio/test/git-grouping.test.ts +0 -106
  382. package/apps/studio/test/git-lifecycle.test.ts +0 -100
  383. package/apps/studio/test/git-log-format.test.ts +0 -95
  384. package/apps/studio/test/git-safe-rel.test.ts +0 -132
  385. package/apps/studio/test/git-system-engine.test.ts +0 -135
  386. package/apps/studio/test/git-watch.test.ts +0 -97
  387. package/apps/studio/test/github-api.test.ts +0 -577
  388. package/apps/studio/test/grid-track-handles.test.ts +0 -160
  389. package/apps/studio/test/handoff-static-frames.test.ts +0 -215
  390. package/apps/studio/test/handoff.test.ts +0 -329
  391. package/apps/studio/test/history-rollback.test.ts +0 -88
  392. package/apps/studio/test/hmr-broadcast.test.ts +0 -236
  393. package/apps/studio/test/hmr-classify.test.ts +0 -87
  394. package/apps/studio/test/hub-link.test.ts +0 -80
  395. package/apps/studio/test/identity-cache.test.ts +0 -83
  396. package/apps/studio/test/import-asset-browser.test.ts +0 -64
  397. package/apps/studio/test/import-asset-route.test.ts +0 -71
  398. package/apps/studio/test/import-asset.test.ts +0 -427
  399. package/apps/studio/test/import-brand-browser.test.ts +0 -88
  400. package/apps/studio/test/import-brand-route.test.ts +0 -120
  401. package/apps/studio/test/import-brand.test.ts +0 -206
  402. package/apps/studio/test/import-figma.test.ts +0 -667
  403. package/apps/studio/test/import-tokens.test.ts +0 -722
  404. package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
  405. package/apps/studio/test/input-router.test.ts +0 -757
  406. package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
  407. package/apps/studio/test/inspect-selections.test.ts +0 -219
  408. package/apps/studio/test/knob-props-authored.test.ts +0 -87
  409. package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
  410. package/apps/studio/test/locator.test.ts +0 -214
  411. package/apps/studio/test/marquee-overlay.test.ts +0 -94
  412. package/apps/studio/test/media-commit-chain.test.ts +0 -210
  413. package/apps/studio/test/mode-toggle.test.tsx +0 -226
  414. package/apps/studio/test/motion-sample.test.ts +0 -35
  415. package/apps/studio/test/move-artboards-command.test.ts +0 -108
  416. package/apps/studio/test/participants-chrome.test.ts +0 -65
  417. package/apps/studio/test/paths.test.ts +0 -88
  418. package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
  419. package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
  420. package/apps/studio/test/perf-harness.ts +0 -193
  421. package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
  422. package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
  423. package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
  424. package/apps/studio/test/photo-edit-api.test.ts +0 -201
  425. package/apps/studio/test/photo-filters.test.ts +0 -149
  426. package/apps/studio/test/photo-pipeline.test.ts +0 -106
  427. package/apps/studio/test/photo-taxonomy.test.ts +0 -96
  428. package/apps/studio/test/print-marks.test.ts +0 -113
  429. package/apps/studio/test/print-units.test.ts +0 -173
  430. package/apps/studio/test/read-annotations.test.ts +0 -727
  431. package/apps/studio/test/read-only-gate.test.ts +0 -188
  432. package/apps/studio/test/read-only-role.test.ts +0 -95
  433. package/apps/studio/test/readiness.test.ts +0 -337
  434. package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
  435. package/apps/studio/test/reorder-api.test.ts +0 -210
  436. package/apps/studio/test/repo-concurrency.test.ts +0 -146
  437. package/apps/studio/test/repo-lock.test.ts +0 -350
  438. package/apps/studio/test/report-proxy.test.ts +0 -125
  439. package/apps/studio/test/ripple.test.ts +0 -197
  440. package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
  441. package/apps/studio/test/runtime-bundle.test.ts +0 -69
  442. package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
  443. package/apps/studio/test/scaffold-design.test.ts +0 -122
  444. package/apps/studio/test/server-lifecycle.test.ts +0 -38
  445. package/apps/studio/test/session-runtime-state.test.ts +0 -226
  446. package/apps/studio/test/session-scope.test.ts +0 -100
  447. package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
  448. package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
  449. package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
  450. package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
  451. package/apps/studio/test/shared-doc-projection.test.ts +0 -238
  452. package/apps/studio/test/shell-importmap.test.ts +0 -56
  453. package/apps/studio/test/sizing-mode.test.ts +0 -102
  454. package/apps/studio/test/slash-commands.test.ts +0 -117
  455. package/apps/studio/test/snap-distance-pill.test.ts +0 -68
  456. package/apps/studio/test/spacing-handles.test.ts +0 -138
  457. package/apps/studio/test/specimen-select.test.ts +0 -88
  458. package/apps/studio/test/sync-agent.test.ts +0 -660
  459. package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
  460. package/apps/studio/test/sync-asset-push.test.ts +0 -931
  461. package/apps/studio/test/sync-atomic-write.test.ts +0 -62
  462. package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
  463. package/apps/studio/test/sync-autocommit.test.ts +0 -537
  464. package/apps/studio/test/sync-canvas-path.test.ts +0 -200
  465. package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
  466. package/apps/studio/test/sync-codec.test.ts +0 -230
  467. package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
  468. package/apps/studio/test/sync-cold-start.test.ts +0 -304
  469. package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
  470. package/apps/studio/test/sync-connection-state.test.ts +0 -337
  471. package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
  472. package/apps/studio/test/sync-decide-file.test.ts +0 -420
  473. package/apps/studio/test/sync-doc-name.test.ts +0 -281
  474. package/apps/studio/test/sync-echo-guard.test.ts +0 -96
  475. package/apps/studio/test/sync-file-ledger.test.ts +0 -334
  476. package/apps/studio/test/sync-file-membership.test.ts +0 -347
  477. package/apps/studio/test/sync-file-plane.test.ts +0 -976
  478. package/apps/studio/test/sync-file-pull.test.ts +0 -333
  479. package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
  480. package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
  481. package/apps/studio/test/sync-hardening.test.ts +0 -586
  482. package/apps/studio/test/sync-hub-listing.test.ts +0 -46
  483. package/apps/studio/test/sync-hubs-config.test.ts +0 -133
  484. package/apps/studio/test/sync-incident-replay.test.ts +0 -211
  485. package/apps/studio/test/sync-journal.test.ts +0 -176
  486. package/apps/studio/test/sync-meta-codec.test.ts +0 -199
  487. package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
  488. package/apps/studio/test/sync-move-retirement.test.ts +0 -231
  489. package/apps/studio/test/sync-panel-surface.test.ts +0 -153
  490. package/apps/studio/test/sync-path-pull.test.ts +0 -531
  491. package/apps/studio/test/sync-presentation.test.ts +0 -262
  492. package/apps/studio/test/sync-pull-budget.test.ts +0 -169
  493. package/apps/studio/test/sync-remote-docs.test.ts +0 -333
  494. package/apps/studio/test/sync-resync-routes.test.ts +0 -168
  495. package/apps/studio/test/sync-runtime.test.ts +0 -1675
  496. package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
  497. package/apps/studio/test/sync-settings-routes.test.ts +0 -195
  498. package/apps/studio/test/sync-settings.test.ts +0 -151
  499. package/apps/studio/test/sync-status.test.ts +0 -261
  500. package/apps/studio/test/sync-supervisor.test.ts +0 -262
  501. package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
  502. package/apps/studio/test/sync-trash.test.ts +0 -132
  503. package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
  504. package/apps/studio/test/sync-untrusted.test.ts +0 -103
  505. package/apps/studio/test/system-endpoint.test.ts +0 -144
  506. package/apps/studio/test/text-editability-stamp.test.ts +0 -131
  507. package/apps/studio/test/timeline-comp-target.test.ts +0 -139
  508. package/apps/studio/test/timeline-media-cache.test.ts +0 -41
  509. package/apps/studio/test/timeline-parse.test.ts +0 -184
  510. package/apps/studio/test/timeline-scale.test.ts +0 -65
  511. package/apps/studio/test/timeline-snap.test.ts +0 -85
  512. package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
  513. package/apps/studio/test/tool-palette.test.tsx +0 -55
  514. package/apps/studio/test/tour-overlay.test.tsx +0 -90
  515. package/apps/studio/test/trusted-request-host.test.ts +0 -66
  516. package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
  517. package/apps/studio/test/undo-stack.test.ts +0 -244
  518. package/apps/studio/test/use-agent-presence.test.tsx +0 -117
  519. package/apps/studio/test/use-annotation-resize.test.ts +0 -402
  520. package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
  521. package/apps/studio/test/use-artboard-drag.test.ts +0 -324
  522. package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
  523. package/apps/studio/test/use-collab.test.ts +0 -0
  524. package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
  525. package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
  526. package/apps/studio/test/use-selection-set.test.tsx +0 -166
  527. package/apps/studio/test/use-snap-guides.test.ts +0 -271
  528. package/apps/studio/test/use-tool-mode.test.tsx +0 -186
  529. package/apps/studio/test/use-undo-stack.test.tsx +0 -223
  530. package/apps/studio/test/video-asset.test.ts +0 -163
  531. package/apps/studio/test/video-comp-fixture.test.ts +0 -50
  532. package/apps/studio/test/video-comp.test.ts +0 -270
  533. package/apps/studio/test/video-encode-lib.test.ts +0 -63
  534. package/apps/studio/test/video-render-bridge.test.ts +0 -171
  535. package/apps/studio/test/whats-new.test.ts +0 -113
  536. package/apps/studio/test/whisper-setup.test.ts +0 -97
  537. package/apps/studio/test/workspace-containment.test.ts +0 -382
  538. package/apps/studio/test/workspace-signin.test.ts +0 -292
  539. package/apps/studio/test/ws-handshake.test.ts +0 -33
  540. package/cli/commands/cache.test.mjs +0 -166
  541. package/cli/commands/design-link.test.mjs +0 -419
  542. package/cli/commands/design.test.mjs +0 -240
  543. package/cli/commands/doctor.test.mjs +0 -237
  544. package/cli/commands/hub-workspace.test.mjs +0 -275
  545. package/cli/commands/hub.test.mjs +0 -225
  546. package/cli/commands/kg.test.mjs +0 -142
  547. package/cli/commands/preflight.test.mjs +0 -46
  548. package/cli/lib/argv.test.mjs +0 -46
  549. package/cli/lib/cache.test.mjs +0 -303
  550. package/cli/lib/cell-plan.test.mjs +0 -225
  551. package/cli/lib/config-lint.test.mjs +0 -117
  552. package/cli/lib/ddr-to-kgai.test.mjs +0 -118
  553. package/cli/lib/design-ownership.test.mjs +0 -329
  554. package/cli/lib/fetch-asset.test.mjs +0 -213
  555. package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
  556. package/cli/lib/figma-import-controls.test.mjs +0 -70
  557. package/cli/lib/flow-design-integration.test.mjs +0 -165
  558. package/cli/lib/gitignore-block.test.mjs +0 -136
  559. package/cli/lib/gitignore-drift.test.mjs +0 -156
  560. package/cli/lib/hubs-config.test.mjs +0 -165
  561. package/cli/lib/pkg-root.test.mjs +0 -111
  562. package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
  563. package/cli/lib/plugin-name-namespace.test.mjs +0 -71
  564. package/cli/lib/reconstruct-toolset.test.mjs +0 -194
  565. package/cli/lib/stack-detect.test.mjs +0 -121
  566. package/cli/lib/update-check.test.mjs +0 -32
  567. package/cli/lib/workspace-plan.test.mjs +0 -699
@@ -1,294 +0,0 @@
1
- // `/_api/figma/*` — the MAIN-origin half of DDR-216 D3.
2
- //
3
- // canvas-origin-gate.test.ts already asserts these routes are unreachable from
4
- // the segregated canvas origin. That proves the wrong property on its own: the
5
- // MAIN origin is reachable from any website the user visits while the dev
6
- // server is up. Round 1 of the DDR-216 security review found the first draft
7
- // named no CSRF / DNS-rebind guard at all, with a working PoC — a CORS-simple
8
- // POST that plants an attacker's PAT (`connect`) or spends the user's on an
9
- // attacker-chosen key (`probe`), opaque response, real side effect.
10
- //
11
- // So this file asserts the complementary half:
12
- // • cross-site → 403 (Sec-Fetch-Site, the `sameOriginWrite` guard)
13
- // • foreign Host header → 403 (the DNS-rebinding `isTrustedRequestHost` guard)
14
- // • the stored key is NEVER echoed on ANY response path
15
- // • `probe` is gated as a WRITE even though it stores nothing — it spends the
16
- // credential and reaches the network.
17
-
18
- import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
19
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
20
- import { tmpdir } from 'node:os';
21
- import { join } from 'node:path';
22
- import type { Subprocess } from 'bun';
23
-
24
- import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
25
-
26
- const TOKEN = 'figd_ROUTECANARY_0123456789abcdef';
27
-
28
- let proc: Subprocess;
29
- let port: number;
30
- let base: string;
31
- let keysPath: string;
32
- let keysDir: string;
33
-
34
- beforeAll(async () => {
35
- const { root } = makeSandbox();
36
- keysDir = mkdtempSync(join(tmpdir(), 'maude-figma-routes-keys-'));
37
- keysPath = join(keysDir, 'keys.json');
38
- writeFileSync(keysPath, JSON.stringify({ keys: {} }), { mode: 0o600 });
39
- port = nextPort();
40
- base = `http://localhost:${port}`;
41
- proc = await bootServer(root, port, { MAUDE_GEN_KEYS_PATH: keysPath });
42
- });
43
-
44
- afterAll(async () => {
45
- await killProc(proc);
46
- rmSync(keysDir, { recursive: true, force: true });
47
- });
48
-
49
- function storedToken(): string | undefined {
50
- try {
51
- return JSON.parse(readFileSync(keysPath, 'utf8')).keys?.figma;
52
- } catch {
53
- return undefined;
54
- }
55
- }
56
-
57
- // The WRITE guard in this codebase is `sameOriginWrite`, which keys on `Origin`
58
- // — the header a browser stamps unspoofably on every cross-origin POST/DELETE.
59
- // (`Sec-Fetch-Site` is what `sameOriginRead` uses, for the GET case below where
60
- // browsers do NOT reliably send Origin.) These tests therefore send a real
61
- // cross-origin `Origin`, which is what an attacker page actually produces.
62
- describe('cross-origin writes are refused (the CSRF guard)', () => {
63
- const EVIL = 'https://evil.example';
64
- const PLANTED = 'figd_ATTACKER_PLANTED_0123456789';
65
-
66
- test('POST /_api/figma/connect cross-origin is 403 and stores nothing', async () => {
67
- const res = await fetch(`${base}/_api/figma/connect`, {
68
- method: 'POST',
69
- headers: { 'Content-Type': 'application/json', Origin: EVIL },
70
- body: JSON.stringify({ token: PLANTED }),
71
- });
72
- expect(res.status).toBe(403);
73
- expect(storedToken()).toBeUndefined();
74
- });
75
-
76
- test('POST /_api/figma/probe cross-origin is 403 — probe spends the credential', async () => {
77
- const res = await fetch(`${base}/_api/figma/probe`, {
78
- method: 'POST',
79
- headers: { Origin: EVIL },
80
- });
81
- expect(res.status).toBe(403);
82
- });
83
-
84
- test('DELETE /_api/figma/connect cross-origin is 403', async () => {
85
- const res = await fetch(`${base}/_api/figma/connect`, {
86
- method: 'DELETE',
87
- headers: { Origin: EVIL },
88
- });
89
- expect(res.status).toBe(403);
90
- });
91
-
92
- test('a CORS-simple POST (no preflight, text/plain) is still refused', async () => {
93
- // The PoC shape from the DDR-216 review: a simple content type means no
94
- // preflight and the page cannot read the response — but the side effect
95
- // would land if the server allowed it. Origin is still stamped.
96
- const res = await fetch(`${base}/_api/figma/connect`, {
97
- method: 'POST',
98
- headers: { 'Content-Type': 'text/plain', Origin: EVIL },
99
- body: JSON.stringify({ token: PLANTED }),
100
- });
101
- expect(res.status).toBe(403);
102
- expect(storedToken()).toBeUndefined();
103
- });
104
- });
105
-
106
- describe('cross-site reads are refused (Sec-Fetch-Site, the GET case)', () => {
107
- test('GET /_api/figma/status with Sec-Fetch-Site: cross-site is 403', async () => {
108
- // A no-cors GET from an attacker page passes the loopback guard and may omit
109
- // Origin entirely, so `sameOriginWrite` cannot see it. Without the read
110
- // guard this leaks whether the user has Figma connected.
111
- const res = await fetch(`${base}/_api/figma/status`, {
112
- headers: { 'Sec-Fetch-Site': 'cross-site' },
113
- });
114
- expect(res.status).toBe(403);
115
- });
116
- });
117
-
118
- describe('a foreign Host header is refused (the DNS-rebinding guard)', () => {
119
- for (const [label, host] of [
120
- ['an attacker-controlled name', 'evil.example'],
121
- ['a rebinding service name', 'figma.127.0.0.1.nip.io'],
122
- ]) {
123
- test(`GET /_api/figma/status with Host: ${label} is 403`, async () => {
124
- const res = await fetch(`${base}/_api/figma/status`, { headers: { Host: host } });
125
- expect(res.status).toBe(403);
126
- });
127
- }
128
- });
129
-
130
- describe('method gating', () => {
131
- test('GET /_api/figma/connect is 405', async () => {
132
- expect((await fetch(`${base}/_api/figma/connect`)).status).toBe(405);
133
- });
134
-
135
- test('GET /_api/figma/probe is 405 — probe is a write', async () => {
136
- expect((await fetch(`${base}/_api/figma/probe`)).status).toBe(405);
137
- });
138
-
139
- test('POST /_api/figma/status is 405', async () => {
140
- expect((await fetch(`${base}/_api/figma/status`, { method: 'POST' })).status).toBe(405);
141
- });
142
- });
143
-
144
- describe('same-origin happy path — and the key never comes back out', () => {
145
- test('status reports presence only, plus the granular scope to ask for', async () => {
146
- const res = await fetch(`${base}/_api/figma/status`, {
147
- headers: { 'Sec-Fetch-Site': 'same-origin' },
148
- });
149
- expect(res.status).toBe(200);
150
- const body = (await res.json()) as Record<string, unknown>;
151
- expect(body.configured).toBe(false);
152
- // The blanket `files:read` scope is deprecated — the UI must never name it.
153
- expect(body.requiredScope).toBe('file_content:read');
154
- expect(JSON.stringify(body)).not.toContain('figd_');
155
- });
156
-
157
- test('connect stores the key and returns presence only — never the value', async () => {
158
- const res = await fetch(`${base}/_api/figma/connect`, {
159
- method: 'POST',
160
- headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
161
- body: JSON.stringify({ token: TOKEN }),
162
- });
163
- expect(res.status).toBe(200);
164
- const raw = await res.text();
165
- expect(JSON.parse(raw)).toEqual({ configured: true });
166
- expect(raw).not.toContain(TOKEN);
167
- expect(raw).not.toContain('ROUTECANARY');
168
- // It really did store it — the assertion above is about the RESPONSE, not
169
- // about the write silently failing.
170
- expect(storedToken()).toBe(TOKEN);
171
- });
172
-
173
- test('status still never echoes the value once one is configured', async () => {
174
- const res = await fetch(`${base}/_api/figma/status`, {
175
- headers: { 'Sec-Fetch-Site': 'same-origin' },
176
- });
177
- const raw = await res.text();
178
- expect(JSON.parse(raw).configured).toBe(true);
179
- expect(raw).not.toContain('ROUTECANARY');
180
- });
181
-
182
- test('a malformed token is refused with a message that does not echo it', async () => {
183
- const junk = 'not a token REFLECTION_CANARY';
184
- const res = await fetch(`${base}/_api/figma/connect`, {
185
- method: 'POST',
186
- headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
187
- body: JSON.stringify({ token: junk }),
188
- });
189
- expect(res.status).toBe(400);
190
- const raw = await res.text();
191
- expect(raw).not.toContain('REFLECTION_CANARY');
192
- // The previously-stored good token is untouched by a failed write.
193
- expect(storedToken()).toBe(TOKEN);
194
- });
195
-
196
- test('probe surfaces a typed failure without leaking the token', async () => {
197
- // No network stub here — the real api.figma.com will reject this canary
198
- // token (or the call fails to connect in CI). Either way the response must
199
- // be a typed reason with a code-owned message and no credential in it.
200
- const res = await fetch(`${base}/_api/figma/probe`, {
201
- method: 'POST',
202
- headers: { 'Sec-Fetch-Site': 'same-origin' },
203
- });
204
- const raw = await res.text();
205
- expect(raw).not.toContain('ROUTECANARY');
206
- expect(raw).not.toContain('figd_');
207
- const body = JSON.parse(raw) as { ok: boolean; reason?: string };
208
- if (!body.ok) {
209
- expect(['unauthorized', 'forbidden', 'network', 'bad_response', 'rate_limited']).toContain(
210
- body.reason
211
- );
212
- }
213
- });
214
-
215
- test('disconnect clears the key', async () => {
216
- const res = await fetch(`${base}/_api/figma/connect`, {
217
- method: 'DELETE',
218
- headers: { 'Sec-Fetch-Site': 'same-origin' },
219
- });
220
- expect(res.status).toBe(200);
221
- expect(await res.json()).toEqual({ configured: false });
222
- expect(storedToken()).toBeUndefined();
223
- });
224
- });
225
-
226
- describe('/_api/figma/import — same gate, and a validated body', () => {
227
- test('cross-origin is 403 — it spends the PAT and writes to disk', async () => {
228
- const res = await fetch(`${base}/_api/figma/import`, {
229
- method: 'POST',
230
- headers: { 'Content-Type': 'application/json', Origin: 'https://evil.example' },
231
- body: JSON.stringify({ mode: 'board', url: 'https://www.figma.com/board/AAAAAAAAAA/x' }),
232
- });
233
- expect(res.status).toBe(403);
234
- });
235
-
236
- test('a foreign Host is 403', async () => {
237
- const res = await fetch(`${base}/_api/figma/import`, {
238
- method: 'POST',
239
- headers: { Host: 'evil.example' },
240
- body: '{}',
241
- });
242
- expect(res.status).toBe(403);
243
- });
244
-
245
- test('GET is 405', async () => {
246
- expect((await fetch(`${base}/_api/figma/import`)).status).toBe(405);
247
- });
248
-
249
- test.each([
250
- ['no body', {}],
251
- ['unknown mode', { mode: 'everything', url: 'https://www.figma.com/board/AAAAAAAAAA/x' }],
252
- ['missing url', { mode: 'board' }],
253
- ['empty url', { mode: 'board', url: '' }],
254
- ])('a malformed request is 400 (%s)', async (_label, body) => {
255
- const res = await fetch(`${base}/_api/figma/import`, {
256
- method: 'POST',
257
- headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
258
- body: JSON.stringify(body),
259
- });
260
- expect(res.status).toBe(400);
261
- });
262
-
263
- test('a caller cannot choose an output path', async () => {
264
- // The body shape is fixed: {mode, url, dryRun}. A slug/path/into field is
265
- // simply not read — the same "the producer never picks its own target"
266
- // discipline DDR-174 applies to its authoring agent.
267
- const res = await fetch(`${base}/_api/figma/import`, {
268
- method: 'POST',
269
- headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
270
- body: JSON.stringify({
271
- mode: 'board',
272
- url: 'https://www.figma.com/board/AAAAAAAAAA/x',
273
- slug: '../../etc/passwd',
274
- into: '/etc/passwd',
275
- }),
276
- });
277
- // It gets past validation (the url is well-formed) and fails on the
278
- // credential instead — the point is that the path fields were ignored.
279
- const raw = await res.text();
280
- expect(raw).not.toContain('etc/passwd');
281
- expect([400, 502]).toContain(res.status);
282
- });
283
-
284
- test('a failure never echoes an upstream string', async () => {
285
- const res = await fetch(`${base}/_api/figma/import`, {
286
- method: 'POST',
287
- headers: { 'Content-Type': 'application/json', 'Sec-Fetch-Site': 'same-origin' },
288
- body: JSON.stringify({ mode: 'board', url: 'https://www.figma.com/board/AAAAAAAAAA/x' }),
289
- });
290
- const raw = await res.text();
291
- expect(raw).not.toContain('figd_');
292
- expect(raw).not.toContain('ROUTECANARY');
293
- });
294
- });
@@ -1,84 +0,0 @@
1
- // file-lock.test.ts — DDR-150 P2. The cross-process advisory lock that closes
2
- // the race the in-process mutex couldn't see: the HTTP server, the /design:edit
3
- // CLI, and the HMR watcher all editing the same comp file. Verifies mutual
4
- // exclusion across "processes" (concurrent acquirers), stale-lock stealing (a
5
- // crashed holder), and that an edit through the integrated withLock cleans up.
6
-
7
- import { describe, expect, test } from 'bun:test';
8
- import { utimes } from 'node:fs/promises';
9
-
10
- import { acquireFileLock, editAttribute, lockPathFor } from '../canvas-edit.ts';
11
- import { transpileCanvasSource } from '../canvas-pipeline.ts';
12
-
13
- const tmp = (tag: string) => `/tmp/maude-lock-${tag}-${Math.random().toString(36).slice(2)}.tsx`;
14
-
15
- describe('acquireFileLock', () => {
16
- test('acquires + releases; release is idempotent', async () => {
17
- const f = tmp('basic');
18
- const rel = await acquireFileLock(f);
19
- expect(await Bun.file(lockPathFor(f)).exists()).toBe(true);
20
- await rel();
21
- expect(await Bun.file(lockPathFor(f)).exists()).toBe(false);
22
- await rel(); // second release is a no-op, never throws
23
- });
24
-
25
- test('serialises a second (cross-process) contender until the first releases', async () => {
26
- const f = tmp('contend');
27
- const relA = await acquireFileLock(f);
28
- let bAcquired = false;
29
- const bPromise = acquireFileLock(f).then((rel) => {
30
- bAcquired = true;
31
- return rel;
32
- });
33
- // While A holds the lock, B must be blocked (it's polling the lockfile).
34
- await new Promise((r) => setTimeout(r, 80));
35
- expect(bAcquired).toBe(false);
36
- await relA();
37
- const relB = await bPromise; // B now wins the lock
38
- expect(bAcquired).toBe(true);
39
- await relB();
40
- expect(await Bun.file(lockPathFor(f)).exists()).toBe(false);
41
- });
42
-
43
- test('steals a STALE lock (holder crashed) instead of waiting forever', async () => {
44
- const f = tmp('stale');
45
- const lp = lockPathFor(f);
46
- await Bun.write(lp, '99999 0'); // a "held" lock from a dead pid
47
- const old = new Date(Date.now() - 60_000); // mtime 60s ago (> LOCK_STALE_MS)
48
- await utimes(lp, old, old);
49
- const start = Date.now();
50
- const rel = await acquireFileLock(f); // must steal it, not block
51
- expect(Date.now() - start).toBeLessThan(2000);
52
- await rel();
53
- });
54
- });
55
-
56
- describe('withLock integration', () => {
57
- test('an edit acquires + cleans up the cross-process lock', async () => {
58
- const f = tmp('edit');
59
- const src = 'function Demo() { return <div className="x">y</div>; }';
60
- await Bun.write(f, src);
61
- const { withIds } = transpileCanvasSource(f, src);
62
- const id = withIds.match(/data-cd-id="([0-9a-f]{8})"/)?.[1] as string;
63
- const r = await editAttribute(f, id, 'className', 'edited');
64
- expect(r.changed).toBe(true);
65
- expect(await Bun.file(f).text()).toContain('className="edited"');
66
- // The lockfile must be released after the edit — not left dangling.
67
- expect(await Bun.file(lockPathFor(f)).exists()).toBe(false);
68
- });
69
-
70
- test('concurrent edits to the same file both land (serialised, no lost write)', async () => {
71
- const f = tmp('concurrent');
72
- const src = 'function Demo() { return <div className="x" title="t">y</div>; }';
73
- await Bun.write(f, src);
74
- const { withIds } = transpileCanvasSource(f, src);
75
- const id = withIds.match(/data-cd-id="([0-9a-f]{8})"/)?.[1] as string;
76
- await Promise.all([
77
- editAttribute(f, id, 'className', 'cls-A'),
78
- editAttribute(f, id, 'title', 'title-B'),
79
- ]);
80
- const out = await Bun.file(f).text();
81
- expect(out).toContain('cls-A');
82
- expect(out).toContain('title-B'); // neither edit clobbered the other
83
- });
84
- });
@@ -1,12 +0,0 @@
1
- #!/bin/sh
2
- # Test fixture for issue #107 round-2 defender L6 — a `claude` stand-in that
3
- # prints a perfectly good status document and THEN leaves a descendant holding
4
- # fd 1 (a daemon, an update check, a corporate agent). The pipe never reaches
5
- # EOF, so the read hits its deadline — but the answer is already in the buffer
6
- # and must not be thrown away.
7
- if [ "$1" = "auth" ] && [ "$2" = "status" ]; then
8
- echo '{"loggedIn":true,"apiProvider":"firstParty","subscriptionType":"max"}'
9
- sleep 120 &
10
- exit 0
11
- fi
12
- exit 1
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env node
2
- // Test fixture standing in for the `claude` CLI — implements only the one
3
- // subcommand probeAcpAvailabilityAuthed()/getClaudeAuthStatus() shell out to.
4
- // FAKE_CLAUDE_LOGGED_IN=1 controls the reported sign-in state.
5
- //
6
- // FAKE_CLAUDE_STDOUT_NOISE=1 reproduces issue #107: a version-manager shim
7
- // (mise/asdf/volta) or corporate wrapper fronting the real binary and printing
8
- // a banner to STDOUT ahead of the JSON. The second noise line deliberately
9
- // carries braces of its own, pinning that a brace-bearing banner can't outrank
10
- // the real payload.
11
- const [, , cmd, sub] = process.argv;
12
- if (cmd === 'auth' && sub === 'status') {
13
- const loggedIn = process.env.FAKE_CLAUDE_LOGGED_IN === '1';
14
- if (process.env.FAKE_CLAUDE_STDOUT_NOISE === '1') {
15
- process.stdout.write('mise ~/.config/mise/config.toml tools: claude@2.1.246\n');
16
- process.stdout.write('resolving {claude} from {global} config\n');
17
- }
18
- process.stdout.write(
19
- JSON.stringify({ loggedIn, apiProvider: loggedIn ? 'firstParty' : undefined })
20
- );
21
- process.exit(0);
22
- }
23
- process.exit(1);
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" data-mdcc-annotations="1"><rect data-id="s_aaa1" data-tool="rect" stroke="#3b82f6" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" fill="#e0ebfd" x="100" y="100" width="160" height="100" data-group-ids="g_team1"/><g data-id="s_aaa2" data-tool="sticky" data-r="8" data-fs="14" fill="#fce8a6" data-group-ids="g_inner g_team1"><rect x="320" y="100" width="200" height="200" rx="8" ry="8"/><text data-sticky-body="1" x="332" y="112" font-size="14" fill="#1a1a1a" dominant-baseline="hanging">flow start</text></g><g data-id="s_aaa3" data-tool="arrow" stroke="#1f1f1f" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" fill="none" data-start-bind="s_aaa1 1 0.5" data-end-bind="s_aaa2 0 0.5 p"><line x1="260" y1="150" x2="320" y2="200"/><polyline points="307.1972004393032,196.78622909829892 320,200 314.5302123070536,187.9866148569984" fill="#1f1f1f"/></g><text data-id="s_aaa4" data-tool="text" x="100" y="260" data-font-size="14" fill="#1f1f1f" text-anchor="start" dominant-baseline="hanging" data-author="ai">AI note</text><ellipse data-id="s_aaa5" data-tool="ellipse" stroke="#30a46c" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" fill="none" cx="700" cy="150" rx="60" ry="40" data-rot="15" transform="rotate(15 700 150)"/></svg>
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env bun
2
- // Mock ACP agent exercising the session-capabilities channel (feature-acp-
3
- // panel-dynamic-claude-code-capabilities) — modes + configOptions returned
4
- // from session/new, live session/set_mode + session/set_config_option, and
5
- // the config_option_update / session_info_update notifications a real
6
- // claude-agent-acp session emits. No real `claude` needed.
7
- //
8
- // Mirrors the REAL adapter's observed behavior (read from the installed
9
- // @agentclientprotocol/claude-agent-acp source, not guessed):
10
- // - session/set_mode updates the mode AND mirrors it into configOptions'
11
- // "mode" entry, emitting ONLY a config_option_update notification (no
12
- // current_mode_update) — bridge.ts cross-derives lastModes from that.
13
- // - session/set_config_option('model', …) can change which OTHER options
14
- // are offered (here: picking "opus" adds a "fast" option) and returns
15
- // the FULL refreshed configOptions in its RPC response (no notification).
16
-
17
- import { Readable, Writable } from 'node:stream';
18
-
19
- import * as acp from '@agentclientprotocol/sdk';
20
-
21
- const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
22
-
23
- const MODEL_OPTIONS = [
24
- { value: 'sonnet', name: 'Sonnet' },
25
- { value: 'opus', name: 'Opus' },
26
- ];
27
- const EFFORT_OPTIONS = [
28
- { value: 'default', name: 'Default' },
29
- { value: 'high', name: 'High' },
30
- ];
31
- const MODE_OPTIONS = [
32
- { value: 'default', name: 'Manual' },
33
- { value: 'plan', name: 'Plan Mode' },
34
- ];
35
- const AVAILABLE_MODES = [
36
- {
37
- id: 'default',
38
- name: 'Manual',
39
- description: 'Standard behavior, prompts for dangerous operations',
40
- },
41
- { id: 'plan', name: 'Plan Mode', description: 'Planning mode, no actual tool execution' },
42
- ];
43
-
44
- // `session` carries every tracked field (model/effort/mode/fast) so a call
45
- // that rebuilds the FULL option set (e.g. session/set_mode, which is scoped
46
- // to just the mode) never regresses a field a PRIOR call already changed —
47
- // mirrors the real adapter's `session.configOptions` being one persistent
48
- // object, not independently recomputed per field.
49
- function baseConfigOptions(session) {
50
- const opts = [
51
- {
52
- id: 'model',
53
- name: 'Model',
54
- category: 'model',
55
- type: 'select',
56
- currentValue: session.model,
57
- options: MODEL_OPTIONS,
58
- },
59
- {
60
- id: 'effort',
61
- name: 'Effort',
62
- category: 'thought_level',
63
- type: 'select',
64
- currentValue: session.effort,
65
- options: EFFORT_OPTIONS,
66
- },
67
- {
68
- id: 'mode',
69
- name: 'Mode',
70
- category: 'mode',
71
- type: 'select',
72
- currentValue: session.mode,
73
- options: MODE_OPTIONS,
74
- },
75
- ];
76
- if (session.model === 'opus') {
77
- opts.push({
78
- id: 'fast',
79
- name: 'Fast mode',
80
- category: 'model_config',
81
- type: 'select',
82
- currentValue: session.fast,
83
- options: [
84
- { value: 'on', name: 'On' },
85
- { value: 'off', name: 'Off' },
86
- ],
87
- });
88
- }
89
- return opts;
90
- }
91
-
92
- let n = 0;
93
- const sessions = new Map(); // sessionId -> { model, effort, mode, fast }
94
-
95
- acp
96
- .agent({ name: 'mock-acp-agent-caps' })
97
- .onRequest('initialize', () => ({
98
- protocolVersion: acp.PROTOCOL_VERSION,
99
- agentCapabilities: { loadSession: false },
100
- }))
101
- .onRequest('session/new', async (ctx) => {
102
- const sessionId = `mock-caps-session-${++n}`;
103
- const session = { model: 'sonnet', effort: 'default', mode: 'default', fast: 'off' };
104
- sessions.set(sessionId, session);
105
- await ctx.client.notify('session/update', {
106
- sessionId,
107
- update: { sessionUpdate: 'session_info_update', title: 'New chat' },
108
- });
109
- return {
110
- sessionId,
111
- modes: { currentModeId: 'default', availableModes: AVAILABLE_MODES },
112
- configOptions: baseConfigOptions(session),
113
- };
114
- })
115
- .onRequest('session/prompt', async (ctx) => {
116
- await ctx.client.notify('session/update', {
117
- sessionId: ctx.params.sessionId,
118
- update: { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: 'ok' } },
119
- });
120
- return { stopReason: 'end_turn' };
121
- })
122
- .onRequest('session/set_mode', async (ctx) => {
123
- const session = sessions.get(ctx.params.sessionId);
124
- if (!session) throw new Error('session not found');
125
- if (!AVAILABLE_MODES.some((m) => m.id === ctx.params.modeId)) {
126
- throw new Error(`unknown mode: ${ctx.params.modeId}`);
127
- }
128
- session.mode = ctx.params.modeId;
129
- // Real adapter: setSessionMode emits ONLY config_option_update, never
130
- // current_mode_update — bridge.ts must cross-derive lastModes from it.
131
- await ctx.client.notify('session/update', {
132
- sessionId: ctx.params.sessionId,
133
- update: { sessionUpdate: 'config_option_update', configOptions: baseConfigOptions(session) },
134
- });
135
- return {};
136
- })
137
- .onRequest('session/set_config_option', async (ctx) => {
138
- const session = sessions.get(ctx.params.sessionId);
139
- if (!session) throw new Error('session not found');
140
- const { configId, value } = ctx.params;
141
- if (configId === 'model') {
142
- if (!MODEL_OPTIONS.some((o) => o.value === value)) throw new Error(`unknown model: ${value}`);
143
- session.model = value;
144
- } else if (configId === 'effort') {
145
- if (!EFFORT_OPTIONS.some((o) => o.value === value))
146
- throw new Error(`unknown effort: ${value}`);
147
- session.effort = value;
148
- } else if (configId === 'mode') {
149
- if (!AVAILABLE_MODES.some((m) => m.id === value)) throw new Error(`unknown mode: ${value}`);
150
- session.mode = value;
151
- } else if (configId === 'fast') {
152
- session.fast = value;
153
- } else {
154
- throw new Error(`unknown config option: ${configId}`);
155
- }
156
- return { configOptions: baseConfigOptions(session) };
157
- })
158
- .connect(stream);
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env bun
2
- // Mock ACP agent variant that publishes an `available_commands_update` the moment
3
- // a session is created (before the prompt) — stands in for Claude Code's command
4
- // catalogue so acp-commands.test.ts can exercise the warm-up + broadcast path
5
- // WITHOUT a real `claude`. Also answers a prompt so prompt-based paths still work.
6
-
7
- import { Readable, Writable } from 'node:stream';
8
-
9
- import * as acp from '@agentclientprotocol/sdk';
10
-
11
- const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
12
-
13
- let n = 0;
14
-
15
- acp
16
- .agent({ name: 'mock-acp-agent-commands' })
17
- .onRequest('initialize', () => ({
18
- protocolVersion: acp.PROTOCOL_VERSION,
19
- agentCapabilities: { loadSession: false },
20
- }))
21
- .onRequest('session/new', async (ctx) => {
22
- const sessionId = `mock-session-${++n}`;
23
- // Publish the command catalogue on session creation — the warm-up path relies
24
- // on this arriving without a prompt.
25
- await ctx.client.notify('session/update', {
26
- sessionId,
27
- update: {
28
- sessionUpdate: 'available_commands_update',
29
- availableCommands: [
30
- { name: 'design:edit', description: 'Iterate on the active canvas' },
31
- { name: 'flow:plan', description: 'Create a plan' },
32
- ],
33
- },
34
- });
35
- return { sessionId };
36
- })
37
- .onRequest('session/prompt', async (ctx) => {
38
- await ctx.client.notify('session/update', {
39
- sessionId: ctx.params.sessionId,
40
- update: { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: 'ok' } },
41
- });
42
- return { stopReason: 'end_turn' };
43
- })
44
- .connect(stream);
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env bun
2
- // Mock ACP agent proving the MAX_PENDING_ELICITATIONS cap (feature-acp-ask-
3
- // user-question, SECURITY / ethical-hacker finding — an unbounded queue lets
4
- // any connected MCP server flood the client with elicitation requests). Fires
5
- // MORE than the cap concurrently, without waiting for each one — mirroring a
6
- // hostile/compromised MCP server that never waits for an answer before
7
- // issuing the next request — and reports which ones actually round-tripped to
8
- // the client vs. were declined immediately by the bridge's own cap.
9
-
10
- import { Readable, Writable } from 'node:stream';
11
-
12
- import * as acp from '@agentclientprotocol/sdk';
13
-
14
- const stream = acp.ndJsonStream(Writable.toWeb(process.stdout), Readable.toWeb(process.stdin));
15
-
16
- const FLOOD_COUNT = 8; // > MAX_PENDING_ELICITATIONS (5) in acp/bridge.ts
17
-
18
- let n = 0;
19
-
20
- acp
21
- .agent({ name: 'mock-acp-agent-elicit-flood' })
22
- .onRequest('initialize', () => ({
23
- protocolVersion: acp.PROTOCOL_VERSION,
24
- agentCapabilities: { loadSession: false },
25
- }))
26
- .onRequest('session/new', () => ({ sessionId: `mock-elicit-flood-session-${++n}` }))
27
- .onRequest('session/prompt', async (ctx) => {
28
- const requests = Array.from({ length: FLOOD_COUNT }, (_, i) =>
29
- ctx.client.request(acp.methods.client.elicitation.create, {
30
- mode: 'form',
31
- sessionId: ctx.params.sessionId,
32
- message: `Flood question ${i}`,
33
- requestedSchema: { type: 'object', properties: { [`q${i}`]: { type: 'string' } } },
34
- })
35
- );
36
- const responses = await Promise.all(requests);
37
- await ctx.client.notify('session/update', {
38
- sessionId: ctx.params.sessionId,
39
- update: {
40
- sessionUpdate: 'agent_message_chunk',
41
- content: { type: 'text', text: `responses=${JSON.stringify(responses)}` },
42
- },
43
- });
44
- return { stopReason: 'end_turn' };
45
- })
46
- .connect(stream);