@1agh/maude 1.0.9 → 1.2.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.
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/probe.ts +268 -16
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/api.ts +139 -26
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/client/app.jsx +257 -7
- package/apps/studio/client/export-center.jsx +15 -7
- package/apps/studio/client/panels/ReadinessList.jsx +7 -1
- package/apps/studio/client/panels/SyncPanel.jsx +125 -4
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/styles/3-shell-maude.css +34 -0
- package/apps/studio/client/styles/4-components.css +26 -0
- package/apps/studio/collab/persistence.ts +132 -16
- package/apps/studio/collab/registry.ts +10 -18
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1128 -1128
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +7 -1
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/readiness.ts +42 -7
- package/apps/studio/server.ts +11 -4
- package/apps/studio/sync/agent.ts +136 -29
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/codec.ts +122 -31
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-plane.ts +623 -27
- package/apps/studio/sync/index.ts +711 -20
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +40 -3
- package/apps/studio/sync/presentation.ts +26 -3
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/status.ts +122 -5
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/whats-new.json +91 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +889 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +32 -9
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -301
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -13
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -226
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
// Silent hub-credential renewal (cloud/renew.ts) — the lane that keeps a
|
|
2
|
-
// cloud link alive past the 12 h cell-session cap without a person pressing
|
|
3
|
-
// Connect again. Every network hop is injected; nothing here talks to a real
|
|
4
|
-
// host.
|
|
5
|
-
|
|
6
|
-
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
7
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
|
|
11
|
-
import { renewHubCredential } from '../cloud/renew.ts';
|
|
12
|
-
|
|
13
|
-
const HUB_URL = 'https://alligators.cloud.test';
|
|
14
|
-
|
|
15
|
-
let dir: string;
|
|
16
|
-
let envBackup: Record<string, string | undefined>;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
dir = mkdtempSync(join(tmpdir(), 'maude-renew-'));
|
|
20
|
-
envBackup = {
|
|
21
|
-
MAUDE_CLOUD_CONFIG: process.env.MAUDE_CLOUD_CONFIG,
|
|
22
|
-
HUBS_CONFIG_PATH: process.env.HUBS_CONFIG_PATH,
|
|
23
|
-
MAUDE_CLOUD_URL: process.env.MAUDE_CLOUD_URL,
|
|
24
|
-
};
|
|
25
|
-
process.env.MAUDE_CLOUD_CONFIG = join(dir, 'cloud.json');
|
|
26
|
-
process.env.HUBS_CONFIG_PATH = join(dir, 'hubs.json');
|
|
27
|
-
process.env.MAUDE_CLOUD_URL = 'https://cloud.test';
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
for (const [k, v] of Object.entries(envBackup)) {
|
|
32
|
-
if (v === undefined) delete process.env[k];
|
|
33
|
-
else process.env[k] = v;
|
|
34
|
-
}
|
|
35
|
-
rmSync(dir, { recursive: true, force: true });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
function signIn(token = 'mpt_account'): void {
|
|
39
|
-
writeFileSync(
|
|
40
|
-
join(dir, 'cloud.json'),
|
|
41
|
-
JSON.stringify({ url: 'https://cloud.test', token, connectedAt: 1 })
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function writeHubRecord(record: Record<string, unknown>): void {
|
|
46
|
-
writeFileSync(join(dir, 'hubs.json'), JSON.stringify({ hubs: { [HUB_URL]: record } }));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function readHubRecord(): Record<string, unknown> | undefined {
|
|
50
|
-
try {
|
|
51
|
-
return JSON.parse(readFileSync(join(dir, 'hubs.json'), 'utf8')).hubs[HUB_URL];
|
|
52
|
-
} catch {
|
|
53
|
-
return undefined;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Route-keyed fetch stub; records every URL it was asked for. */
|
|
58
|
-
function fakeFetch(routes: Record<string, { status: number; body: unknown }>) {
|
|
59
|
-
const calls: string[] = [];
|
|
60
|
-
const doFetch = (async (input: string | URL | Request) => {
|
|
61
|
-
const url = String(input);
|
|
62
|
-
calls.push(url);
|
|
63
|
-
for (const [prefix, r] of Object.entries(routes)) {
|
|
64
|
-
if (url.startsWith(prefix)) {
|
|
65
|
-
return new Response(JSON.stringify(r.body), { status: r.status });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return new Response('{}', { status: 404 });
|
|
69
|
-
}) as typeof fetch;
|
|
70
|
-
return { doFetch, calls };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const HAPPY_ROUTES = {
|
|
74
|
-
'https://cloud.test/api/projects': {
|
|
75
|
-
status: 200,
|
|
76
|
-
body: { projects: [{ id: 'alligators', url: HUB_URL, role: 'owner' }] },
|
|
77
|
-
},
|
|
78
|
-
'https://cloud.test/projects/open': {
|
|
79
|
-
status: 200,
|
|
80
|
-
body: { token: 'proj_tok', url: HUB_URL, role: 'owner', expiresAt: 111 },
|
|
81
|
-
},
|
|
82
|
-
[`${HUB_URL}/auth/login`]: {
|
|
83
|
-
status: 200,
|
|
84
|
-
body: { token: 'mau_new', expiresAt: 12345, user: { email: 'm@x.cz', role: 'member' } },
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
describe('renewHubCredential', () => {
|
|
89
|
-
test('signed out → not-signed-in, and no network is touched', async () => {
|
|
90
|
-
const { doFetch, calls } = fakeFetch(HAPPY_ROUTES);
|
|
91
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
92
|
-
expect(r).toEqual({ ok: false, reason: 'not-signed-in' });
|
|
93
|
-
expect(calls).toHaveLength(0);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('happy path: projects → open → cell login → hubs.json updated, role preserved', async () => {
|
|
97
|
-
signIn();
|
|
98
|
-
writeHubRecord({ token: 'mau_stale', linkedAt: 1, role: 'member', expiresAt: 2 });
|
|
99
|
-
const { doFetch, calls } = fakeFetch(HAPPY_ROUTES);
|
|
100
|
-
|
|
101
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
102
|
-
expect(r).toEqual({ ok: true, token: 'mau_new', expiresAt: 12345 });
|
|
103
|
-
|
|
104
|
-
const stored = readHubRecord();
|
|
105
|
-
expect(stored?.token).toBe('mau_new');
|
|
106
|
-
expect(stored?.expiresAt).toBe(12345);
|
|
107
|
-
// The upsert replaces the record — the vouched role must survive renewal.
|
|
108
|
-
expect(stored?.role).toBe('member');
|
|
109
|
-
|
|
110
|
-
// The cell login went to the STORED hub address, not anything the cloud named.
|
|
111
|
-
expect(calls[2]).toBe(`${HUB_URL}/auth/login`);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test('F5: the FRESH role from the cell login wins over the stale stored one', async () => {
|
|
115
|
-
signIn();
|
|
116
|
-
writeHubRecord({ token: 'mau_stale', linkedAt: 1, role: 'member' });
|
|
117
|
-
const { doFetch } = fakeFetch({
|
|
118
|
-
...HAPPY_ROUTES,
|
|
119
|
-
[`${HUB_URL}/auth/login`]: {
|
|
120
|
-
status: 200,
|
|
121
|
-
body: { token: 'mau_new', expiresAt: 12345, user: { email: 'm@x.cz', role: 'owner' } },
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
125
|
-
expect(r.ok).toBe(true);
|
|
126
|
-
expect(readHubRecord()?.role).toBe('owner'); // not the stale 'member'
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test('F5: an older cell that omits the role falls back to the stored one', async () => {
|
|
130
|
-
signIn();
|
|
131
|
-
writeHubRecord({ token: 'mau_stale', linkedAt: 1, role: 'member' });
|
|
132
|
-
const { doFetch } = fakeFetch({
|
|
133
|
-
...HAPPY_ROUTES,
|
|
134
|
-
[`${HUB_URL}/auth/login`]: { status: 200, body: { token: 'mau_new', expiresAt: 12345 } },
|
|
135
|
-
});
|
|
136
|
-
await renewHubCredential(HUB_URL, doFetch);
|
|
137
|
-
expect(readHubRecord()?.role).toBe('member');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('claim-a: an embedded-credential hubUrl is refused, no network touched', async () => {
|
|
141
|
-
signIn();
|
|
142
|
-
const { doFetch, calls } = fakeFetch(HAPPY_ROUTES);
|
|
143
|
-
const r = await renewHubCredential('https://evil@alligators.cloud.test', doFetch);
|
|
144
|
-
expect(r).toEqual({ ok: false, reason: 'no-matching-project' });
|
|
145
|
-
expect(calls).toHaveLength(0);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test('account token revoked (401 on projects) → account-revoked, store untouched', async () => {
|
|
149
|
-
signIn();
|
|
150
|
-
writeHubRecord({ token: 'mau_stale', linkedAt: 1 });
|
|
151
|
-
const { doFetch } = fakeFetch({
|
|
152
|
-
'https://cloud.test/api/projects': { status: 401, body: {} },
|
|
153
|
-
});
|
|
154
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
155
|
-
expect(r).toEqual({ ok: false, reason: 'account-revoked' });
|
|
156
|
-
expect(readHubRecord()?.token).toBe('mau_stale');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test('hub not among the account projects → no-matching-project', async () => {
|
|
160
|
-
signIn();
|
|
161
|
-
const { doFetch } = fakeFetch({
|
|
162
|
-
'https://cloud.test/api/projects': {
|
|
163
|
-
status: 200,
|
|
164
|
-
body: { projects: [{ id: 'other', url: 'https://other.cloud.test', role: 'owner' }] },
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
168
|
-
expect(r).toEqual({ ok: false, reason: 'no-matching-project' });
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('a viewer role never renews an editor credential', async () => {
|
|
172
|
-
signIn();
|
|
173
|
-
const { doFetch } = fakeFetch({
|
|
174
|
-
'https://cloud.test/api/projects': {
|
|
175
|
-
status: 200,
|
|
176
|
-
body: { projects: [{ id: 'alligators', url: HUB_URL, role: 'viewer' }] },
|
|
177
|
-
},
|
|
178
|
-
});
|
|
179
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
180
|
-
expect(r).toEqual({ ok: false, reason: 'no-matching-project' });
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test('removed member: /projects/open refuses → open-refused (the right place to die)', async () => {
|
|
184
|
-
signIn();
|
|
185
|
-
const { doFetch } = fakeFetch({
|
|
186
|
-
'https://cloud.test/api/projects': HAPPY_ROUTES['https://cloud.test/api/projects'],
|
|
187
|
-
'https://cloud.test/projects/open': { status: 404, body: {} },
|
|
188
|
-
});
|
|
189
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
190
|
-
expect(r).toEqual({ ok: false, reason: 'open-refused' });
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test('cell refuses the exchange → cell-refused, store untouched', async () => {
|
|
194
|
-
signIn();
|
|
195
|
-
writeHubRecord({ token: 'mau_stale', linkedAt: 1 });
|
|
196
|
-
const { doFetch } = fakeFetch({
|
|
197
|
-
'https://cloud.test/api/projects': HAPPY_ROUTES['https://cloud.test/api/projects'],
|
|
198
|
-
'https://cloud.test/projects/open': HAPPY_ROUTES['https://cloud.test/projects/open'],
|
|
199
|
-
[`${HUB_URL}/auth/login`]: { status: 401, body: {} },
|
|
200
|
-
});
|
|
201
|
-
const r = await renewHubCredential(HUB_URL, doFetch);
|
|
202
|
-
expect(r).toEqual({ ok: false, reason: 'cell-refused' });
|
|
203
|
-
expect(readHubRecord()?.token).toBe('mau_stale');
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
// Cloud Phase 27 A3/A4 + C2/C4 (DDR-209) — the studio in a cell.
|
|
2
|
-
//
|
|
3
|
-
// The behaviour under test is the one that makes a cell multi-tenant-safe: in
|
|
4
|
-
// workspace mode this process serves an owner and a viewer at the SAME TIME, so
|
|
5
|
-
// "what may this request do" cannot come from a file on disk. It comes from the
|
|
6
|
-
// proxy in front, per request, and in the absence of proof it is `viewer`.
|
|
7
|
-
//
|
|
8
|
-
// The local gate this replaces fails OPEN by design — `isHubReadOnly()` returns
|
|
9
|
-
// false from its `catch`, and `projectReadOnly()` returns false when `linkedHub`
|
|
10
|
-
// is unset. That is correct for a tool on your own laptop and is the whole
|
|
11
|
-
// ballgame on the internet, which is why the no-header case below is the most
|
|
12
|
-
// important assertion in the file.
|
|
13
|
-
|
|
14
|
-
import { afterAll, describe, expect, test } from 'bun:test';
|
|
15
|
-
import type { Subprocess } from 'bun';
|
|
16
|
-
|
|
17
|
-
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
18
|
-
|
|
19
|
-
const procs: Subprocess[] = [];
|
|
20
|
-
afterAll(async () => {
|
|
21
|
-
for (const p of procs) await killProc(p);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
async function bootCell() {
|
|
25
|
-
const { root } = makeSandbox();
|
|
26
|
-
const port = nextPort();
|
|
27
|
-
const proc = await bootServer(root, port, {
|
|
28
|
-
MAUDE_WORKSPACE_MODE: '1',
|
|
29
|
-
// A dev checkout resolves Playwright (the E2E harness), which is a
|
|
30
|
-
// legitimate devDependency here and would otherwise fail the module half of
|
|
31
|
-
// the containment assert before workspace mode could be tested at all.
|
|
32
|
-
MAUDE_WORKSPACE_ALLOW_DEV_MODULES: '1',
|
|
33
|
-
HUB_DASHBOARD_URL: 'https://cloud.maude.sh',
|
|
34
|
-
MAUDE_PROJECT_NAME: 'Alligators',
|
|
35
|
-
});
|
|
36
|
-
procs.push(proc);
|
|
37
|
-
return { base: `http://localhost:${port}` };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe('a cell resolves the role per request, and defaults to read-only', () => {
|
|
41
|
-
test('no injected role at all ⇒ read-only, and writes are refused', async () => {
|
|
42
|
-
const { base } = await bootCell();
|
|
43
|
-
|
|
44
|
-
const cfg = (await (await fetch(`${base}/_config`)).json()) as { readOnly?: boolean };
|
|
45
|
-
expect(cfg.readOnly).toBe(true);
|
|
46
|
-
|
|
47
|
-
const res = await fetch(`${base}/_api/edit-text`, {
|
|
48
|
-
method: 'POST',
|
|
49
|
-
headers: { 'content-type': 'application/json' },
|
|
50
|
-
body: JSON.stringify({ file: 'ui/Sample.tsx', id: 'x', text: 'y' }),
|
|
51
|
-
});
|
|
52
|
-
expect(res.status).toBe(403);
|
|
53
|
-
expect(((await res.json()) as { reason?: string }).reason).toBe('read-only');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('a vouched editing role opens the same write', async () => {
|
|
57
|
-
const { base } = await bootCell();
|
|
58
|
-
|
|
59
|
-
const cfg = (await (
|
|
60
|
-
await fetch(`${base}/_config`, { headers: { 'x-maude-readonly': '0' } })
|
|
61
|
-
).json()) as { readOnly?: boolean };
|
|
62
|
-
expect(cfg.readOnly).toBe(false);
|
|
63
|
-
|
|
64
|
-
const res = await fetch(`${base}/_api/edit-text`, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: { 'content-type': 'application/json', 'x-maude-readonly': '0' },
|
|
67
|
-
body: JSON.stringify({ file: 'ui/Sample.tsx', id: 'x', text: 'y' }),
|
|
68
|
-
});
|
|
69
|
-
// Whatever the handler makes of the payload, the GATE let it through — the
|
|
70
|
-
// read-only refusal is the specific thing that must not happen.
|
|
71
|
-
expect(res.status).not.toBe(403);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('anything other than a positive `0` is read-only', async () => {
|
|
75
|
-
// The failure mode this rules out: a truthy-string check, where
|
|
76
|
-
// `x-maude-readonly: false` (a plausible thing for a future caller to send)
|
|
77
|
-
// reads as "not read-only".
|
|
78
|
-
const { base } = await bootCell();
|
|
79
|
-
for (const value of ['1', 'false', 'no', '', 'true', 'owner']) {
|
|
80
|
-
const cfg = (await (
|
|
81
|
-
await fetch(`${base}/_config`, { headers: { 'x-maude-readonly': value } })
|
|
82
|
-
).json()) as { readOnly?: boolean };
|
|
83
|
-
expect(cfg.readOnly).toBe(true);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('two requests to ONE process get two different answers', async () => {
|
|
88
|
-
// The actual multi-tenancy property, stated as a test: this is what a
|
|
89
|
-
// per-PROCESS config file cannot do, and it is why A3 exists.
|
|
90
|
-
const { base } = await bootCell();
|
|
91
|
-
const [viewer, member] = await Promise.all([
|
|
92
|
-
fetch(`${base}/_config`, { headers: { 'x-maude-readonly': '1' } }).then((r) => r.json()),
|
|
93
|
-
fetch(`${base}/_config`, { headers: { 'x-maude-readonly': '0' } }).then((r) => r.json()),
|
|
94
|
-
]);
|
|
95
|
-
expect((viewer as { readOnly: boolean }).readOnly).toBe(true);
|
|
96
|
-
expect((member as { readOnly: boolean }).readOnly).toBe(false);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('the cloud shell states what it is', () => {
|
|
101
|
-
test('/_config carries the cloud block C2 and C4 render from', async () => {
|
|
102
|
-
const { base } = await bootCell();
|
|
103
|
-
const cfg = (await (await fetch(`${base}/_config`)).json()) as {
|
|
104
|
-
cloud?: { dashboardUrl?: string; projectName?: string };
|
|
105
|
-
};
|
|
106
|
-
expect(cfg.cloud).toBeTruthy();
|
|
107
|
-
expect(cfg.cloud?.dashboardUrl).toBe('https://cloud.maude.sh');
|
|
108
|
-
expect(cfg.cloud?.projectName).toBe('Alligators');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// The shape every test above this line assumed away. A self-hosted
|
|
112
|
-
// `maude hub workspace` sets MAUDE_WORKSPACE_MODE but never
|
|
113
|
-
// HUB_DASHBOARD_URL (only cli/lib/cell-plan.mjs writes that), and the
|
|
114
|
-
// studio used to default it to the vendor's SaaS — so the ONE way out a
|
|
115
|
-
// browser tab offers walked a self-hoster off their own deployment onto
|
|
116
|
-
// cloud.maude.sh. Every cloud-block test booted WITH the variable set, so
|
|
117
|
-
// the whole suite was blind to the deployment the bug applies to.
|
|
118
|
-
test('a self-hosted hub gets the cloud block WITHOUT a dashboard link', async () => {
|
|
119
|
-
const { root } = makeSandbox();
|
|
120
|
-
const port = nextPort();
|
|
121
|
-
const proc = await bootServer(root, port, {
|
|
122
|
-
MAUDE_WORKSPACE_MODE: '1',
|
|
123
|
-
MAUDE_WORKSPACE_ALLOW_DEV_MODULES: '1',
|
|
124
|
-
MAUDE_PROJECT_NAME: 'Alligators',
|
|
125
|
-
// deliberately NO HUB_DASHBOARD_URL — this is what self-hosted looks like
|
|
126
|
-
});
|
|
127
|
-
procs.push(proc);
|
|
128
|
-
const cfg = (await (await fetch(`http://localhost:${port}/_config`)).json()) as {
|
|
129
|
-
cloud?: { dashboardUrl?: string; projectName?: string };
|
|
130
|
-
};
|
|
131
|
-
// The block still exists: "you are in a browser tab on somebody else's
|
|
132
|
-
// machine" is just as true self-hosted, and C2's stated agent absence,
|
|
133
|
-
// the account chip and the sign-out all hang off it.
|
|
134
|
-
expect(cfg.cloud).toBeTruthy();
|
|
135
|
-
expect(cfg.cloud?.projectName).toBe('Alligators');
|
|
136
|
-
// Only the platform-specific field is gone — absent, so the client's
|
|
137
|
-
// truthiness check is the single gate.
|
|
138
|
-
expect(cfg.cloud?.dashboardUrl).toBeUndefined();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('the canvas capability is echoed only when the proxy minted one', async () => {
|
|
142
|
-
const { base } = await bootCell();
|
|
143
|
-
const without = (await (await fetch(`${base}/_config`)).json()) as { canvasToken?: string };
|
|
144
|
-
expect(without.canvasToken).toBeUndefined();
|
|
145
|
-
|
|
146
|
-
const with_ = (await (
|
|
147
|
-
await fetch(`${base}/_config`, { headers: { 'x-maude-canvas-token': 'cap-123' } })
|
|
148
|
-
).json()) as { canvasToken?: string };
|
|
149
|
-
expect(with_.canvasToken).toBe('cap-123');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('a cell SERVES the canvas surfaces it used to 404 (DDR-209 A′1)', async () => {
|
|
153
|
-
const { base } = await bootCell();
|
|
154
|
-
// The shell is a static harness; the browser is what evaluates it. Before
|
|
155
|
-
// DDR-209 this was a hard 404 in workspace mode, which is precisely why
|
|
156
|
-
// Phase 25 had to hand-roll a second studio.
|
|
157
|
-
const shell = await fetch(`${base}/_canvas-shell.html`);
|
|
158
|
-
expect(shell.status).toBe(200);
|
|
159
|
-
expect(shell.headers.get('content-type')).toContain('text/html');
|
|
160
|
-
});
|
|
161
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
// Surfaces a cloud tab must not offer — Cloud Phase 27, found by using it.
|
|
2
|
-
//
|
|
3
|
-
// The phase's rule is C1's: role shapes what is OFFERED, never what is VISIBLE.
|
|
4
|
-
// That rule is about ROLES. A different question turned up the moment the real
|
|
5
|
-
// studio actually ran in a browser: what about a control whose feature cannot
|
|
6
|
-
// exist in THIS SHELL at all?
|
|
7
|
-
//
|
|
8
|
-
// Three answers, and the difference between them is the whole point:
|
|
9
|
-
//
|
|
10
|
-
// the agent chat ABSENT, WITH A NOTICE. It runs on your own Claude
|
|
11
|
-
// subscription on your own machine (DDR-123). The
|
|
12
|
-
// feature exists and you can have it — the menu row
|
|
13
|
-
// stays, disabled, saying where. (C2)
|
|
14
|
-
//
|
|
15
|
-
// "Sign in to Maude ABSENT, SILENTLY. It offers to do the thing you have
|
|
16
|
-
// Cloud" already done: you reached this tab THROUGH a Maude
|
|
17
|
-
// account. There is nothing to explain.
|
|
18
|
-
//
|
|
19
|
-
// AI generation / ABSENT, SILENTLY. These configure ffmpeg, mlx-vlm, a
|
|
20
|
-
// Subtitles / Video HuggingFace cache and your own provider keys — none
|
|
21
|
-
// of which a browser tab has, and whose routes a cell
|
|
22
|
-
// refuses by design (DDR-209 D1). Left in, they render
|
|
23
|
-
// as a wall of `HTTP 404`, which is what the owner saw.
|
|
24
|
-
//
|
|
25
|
-
// Source-level assertions, because the menubar's dropdowns do not open under a
|
|
26
|
-
// synthetic click and a test that cannot fail is worse than no test. What these
|
|
27
|
-
// pin is exactly what would regress: someone re-introducing `TABS.map` or
|
|
28
|
-
// dropping the gate on `<CloudBar />`.
|
|
29
|
-
|
|
30
|
-
import { describe, expect, test } from 'bun:test';
|
|
31
|
-
import { readFileSync } from 'node:fs';
|
|
32
|
-
import { join } from 'node:path';
|
|
33
|
-
|
|
34
|
-
const STUDIO = join(import.meta.dir, '..');
|
|
35
|
-
const APP = readFileSync(join(STUDIO, 'client', 'app.jsx'), 'utf8');
|
|
36
|
-
const SETTINGS = readFileSync(join(STUDIO, 'client', 'panels', 'SettingsPanel.jsx'), 'utf8');
|
|
37
|
-
|
|
38
|
-
describe('the cloud shell offers nothing it cannot honour', () => {
|
|
39
|
-
test('the local-machine settings tabs are flagged — and the list is exact', () => {
|
|
40
|
-
// An exact list, not a `toContain`: the failure this guards is a tab that
|
|
41
|
-
// configures machine-local state shipping to the cloud shell UNFLAGGED,
|
|
42
|
-
// where its routes are refused by design (DDR-209 D1) and it renders as a
|
|
43
|
-
// wall of 404s. A new tab has to be classified deliberately, which means
|
|
44
|
-
// this assertion is SUPPOSED to fail when one is added.
|
|
45
|
-
//
|
|
46
|
-
// `figma` (DDR-216) is local-machine for the same reason `ai-generation`
|
|
47
|
-
// is: the credential lives in `~/.config/maude/keys.json` on this machine,
|
|
48
|
-
// and the import writes into this machine's design root.
|
|
49
|
-
const flagged = [
|
|
50
|
-
...SETTINGS.matchAll(/\{ id: '([a-z-]+)', label: '[^']+', local: true \}/g),
|
|
51
|
-
].map((m) => m[1]);
|
|
52
|
-
expect(flagged.sort()).toEqual(['ai-generation', 'figma', 'subtitles', 'video']);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('the tab rail renders the FILTERED list, not the raw table', () => {
|
|
56
|
-
// `TABS.map` is what shipped the wall of 404s.
|
|
57
|
-
expect(SETTINGS).toContain('{tabs.map((t) => (');
|
|
58
|
-
expect(SETTINGS).not.toContain('{TABS.map(');
|
|
59
|
-
// …and every keyboard path must walk the same list, or End/Home select a
|
|
60
|
-
// tab that is not on screen.
|
|
61
|
-
expect(SETTINGS).not.toMatch(/setTab\(TABS\[/);
|
|
62
|
-
expect(SETTINGS).toContain('const tabs = useMemo(');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('a remembered tab this shell does not have cannot select nothing', () => {
|
|
66
|
-
// Open Video on the desktop, then open Settings in the cloud.
|
|
67
|
-
const init =
|
|
68
|
-
/const \[tab, setTab\] = useState\(\(\) => \{[\s\S]*?\}\);/.exec(SETTINGS)?.[0] ?? '';
|
|
69
|
-
expect(init).toContain('!(cloud && t.local)');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('the cloud sign-in is not offered inside the cloud', () => {
|
|
73
|
-
// `=== null`, not falsiness. `cloud` is undefined until `/_config` answers,
|
|
74
|
-
// and treating unknown as "not cloud" mounted this bar for one frame in
|
|
75
|
-
// every cloud tab — long enough to fire the one request the cell refuses.
|
|
76
|
-
// Matched on the GUARD rather than the whole element, so adding a prop to
|
|
77
|
-
// CloudBar (DDR-214 drilled the live sync payload in) does not read as a
|
|
78
|
-
// regression in a rule that is entirely about `=== null`.
|
|
79
|
-
expect(APP).toMatch(/\{cloud === null \? <CloudBar[^>]*\/> : null\}/);
|
|
80
|
-
expect(APP).not.toMatch(/\{cloud \? null : <CloudBar/);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('the local export queue does not hydrate before the shell is known', () => {
|
|
84
|
-
expect(APP).toContain('useExportCenter({ enabled: cfg.cloud === null })');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('every component that gates on the shell is actually GIVEN the flag', () => {
|
|
88
|
-
// The bug this exists for: three `cfg?.cloud` reads inside a component that
|
|
89
|
-
// never receives `cfg` — a ReferenceError at render, and a blank app.
|
|
90
|
-
for (const call of ['<Menubar', '<Sidebar', '<SettingsPanel']) {
|
|
91
|
-
const at = APP.indexOf(call);
|
|
92
|
-
expect(at).toBeGreaterThan(0);
|
|
93
|
-
const props = APP.slice(at, at + 900);
|
|
94
|
-
// Passed RAW — a `?? null` here would erase the "not known yet" state
|
|
95
|
-
// before any component could see it.
|
|
96
|
-
expect(props).toContain('cloud={cfg.cloud}');
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test('no component reaches for `cfg` where only the flag was passed', () => {
|
|
101
|
-
// `cfg` is a real variable at the top level, so a stray reference in a
|
|
102
|
-
// child component is a ReferenceError the bundler happily emits.
|
|
103
|
-
for (const fn of ['function Menubar({', 'function Sidebar({']) {
|
|
104
|
-
const start = APP.indexOf(fn);
|
|
105
|
-
expect(start).toBeGreaterThan(0);
|
|
106
|
-
// Bounded window: the component's own body, not the whole file.
|
|
107
|
-
const body = APP.slice(start, APP.indexOf('\nfunction ', start + 10));
|
|
108
|
-
expect(body).not.toMatch(/\bcfg\?\./);
|
|
109
|
-
expect(body).not.toMatch(/\bcfg\./);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('"not known yet" survives every layer between /_config and the DOM', () => {
|
|
115
|
-
// The three-line bug: the call sites were fixed to pass `cfg.cloud` raw, and
|
|
116
|
-
// the components still declared `cloud = null` as a default parameter. A
|
|
117
|
-
// default fires on `undefined` — so "we have not asked yet" turned back into
|
|
118
|
-
// "this is not the cloud" one layer further in, the sign-in bar mounted for a
|
|
119
|
-
// frame, and the boot 404 came back looking exactly like the one just fixed.
|
|
120
|
-
const APP_SRC = readFileSync(join(STUDIO, 'client', 'app.jsx'), 'utf8');
|
|
121
|
-
const SETTINGS_SRC = readFileSync(join(STUDIO, 'client', 'panels', 'SettingsPanel.jsx'), 'utf8');
|
|
122
|
-
|
|
123
|
-
test('no component defaults the shell flag', () => {
|
|
124
|
-
for (const [label, src] of [
|
|
125
|
-
['app.jsx', APP_SRC],
|
|
126
|
-
['SettingsPanel.jsx', SETTINGS_SRC],
|
|
127
|
-
] as const) {
|
|
128
|
-
expect(src, label).not.toMatch(/^\s*cloud = (null|false|\{\}),$/m);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('the way back out exists only where there is somewhere to go', () => {
|
|
134
|
-
// A self-hosted `maude hub workspace` is workspace mode too — the whole
|
|
135
|
-
// point of "one studio, three shells" — so it gets the `cloud` block, the
|
|
136
|
-
// stated agent absence, the account chip and the sign-out. What it does NOT
|
|
137
|
-
// have is a dashboard: one hub serves one project, and `HUB_DASHBOARD_URL`
|
|
138
|
-
// is written only by the managed fleet (cli/lib/cell-plan.mjs).
|
|
139
|
-
//
|
|
140
|
-
// Both sides had defaulted that URL to the vendor's SaaS, so the ONE way out
|
|
141
|
-
// a browser tab offers walked every self-hoster off their own deployment
|
|
142
|
-
// onto cloud.maude.sh — a dead end for an account most of them do not have.
|
|
143
|
-
// Fixing the server alone would not have fixed the button; the client
|
|
144
|
-
// carried its own copy of the same fallback.
|
|
145
|
-
test('the client never names the vendor as a fallback destination', () => {
|
|
146
|
-
// The literal is the bug. `dashboardUrl` is either configured or absent.
|
|
147
|
-
expect(APP).not.toContain("'https://cloud.maude.sh'");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test('the ← Dashboard anchor is gated on the URL, not on the shell', () => {
|
|
151
|
-
const at = APP.indexOf('data-testid="cloud-back"');
|
|
152
|
-
expect(at).toBeGreaterThan(0);
|
|
153
|
-
const block = APP.slice(at, at + 1200);
|
|
154
|
-
expect(block).toContain('{cloud.dashboardUrl ? (');
|
|
155
|
-
expect(block).toContain('href={cloud.dashboardUrl}');
|
|
156
|
-
// The project name is NOT gated with it: a tab must still say which
|
|
157
|
-
// project it is showing, dashboard or no dashboard.
|
|
158
|
-
expect(block).toContain('data-testid="cloud-project-name"');
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test('the server sends the field only when it is configured', () => {
|
|
162
|
-
const HTTP = readFileSync(join(STUDIO, 'http.ts'), 'utf8');
|
|
163
|
-
// Conditional spread — the same shape `canvasToken` uses two fields down,
|
|
164
|
-
// so an absent value is absent rather than a string nobody can act on.
|
|
165
|
-
expect(HTTP).toContain(
|
|
166
|
-
'...(process.env.HUB_DASHBOARD_URL\n ? { dashboardUrl: process.env.HUB_DASHBOARD_URL }\n : {}),'
|
|
167
|
-
);
|
|
168
|
-
expect(HTTP).not.toContain("HUB_DASHBOARD_URL ?? 'https://cloud.maude.sh'");
|
|
169
|
-
});
|
|
170
|
-
});
|