@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.
- package/README.md +3 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- 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 +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- 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/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -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 +892 -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 +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- 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 -545
- 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/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- 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/noisy-claude-auth.mjs +0 -27
- 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/fixtures/wedged-claude-auth.sh +0 -11
- 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 -337
- 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,251 +0,0 @@
|
|
|
1
|
-
// Pure unit tests for client/panels/acp-elicitation.js — the schema parser +
|
|
2
|
-
// content builder ElicitationPrompt.jsx renders from. Fixture shapes mirror
|
|
3
|
-
// the EXACT wire output of `@agentclientprotocol/claude-agent-acp`'s
|
|
4
|
-
// `askUserQuestionsToCreateRequest` (confirmed on disk — see the file's own
|
|
5
|
-
// header comment), not a guessed/simplified schema.
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from 'bun:test';
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
buildElicitationContent,
|
|
11
|
-
looksLikeSecretRequest,
|
|
12
|
-
parseElicitationSchema,
|
|
13
|
-
} from '../client/panels/acp-elicitation.js';
|
|
14
|
-
|
|
15
|
-
// A real single-question AskUserQuestion-shaped schema (single() branch of
|
|
16
|
-
// askUserQuestionsToCreateRequest — message carries the question, the field
|
|
17
|
-
// itself has no description).
|
|
18
|
-
const SINGLE_SELECT_SCHEMA = {
|
|
19
|
-
type: 'object',
|
|
20
|
-
properties: {
|
|
21
|
-
question_0: {
|
|
22
|
-
type: 'string',
|
|
23
|
-
title: 'Which color?',
|
|
24
|
-
oneOf: [
|
|
25
|
-
{ const: 'Red', title: 'Red', description: 'Warm' },
|
|
26
|
-
{ const: 'Blue', title: 'Blue' },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
question_0_custom: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
title: 'Other',
|
|
32
|
-
description: 'Type your own answer instead of choosing an option above (optional).',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// A real multi-question schema — index 1 is multi-select (array + items.anyOf).
|
|
38
|
-
const MULTI_QUESTION_SCHEMA = {
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
question_0: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
description: 'Pick one',
|
|
44
|
-
oneOf: [
|
|
45
|
-
{ const: 'A', title: 'A' },
|
|
46
|
-
{ const: 'B', title: 'B' },
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
question_0_custom: { type: 'string', title: 'Other' },
|
|
50
|
-
question_1: {
|
|
51
|
-
type: 'array',
|
|
52
|
-
description: 'Pick many',
|
|
53
|
-
items: {
|
|
54
|
-
anyOf: [
|
|
55
|
-
{ const: 'X', title: 'X' },
|
|
56
|
-
{ const: 'Y', title: 'Y' },
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
question_1_custom: { type: 'string', title: 'Other' },
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
describe('parseElicitationSchema', () => {
|
|
65
|
-
test('single-select (oneOf): one question, custom field paired not top-level', () => {
|
|
66
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
67
|
-
expect(qs.length).toBe(1);
|
|
68
|
-
expect(qs[0]).toMatchObject({
|
|
69
|
-
id: 'question_0',
|
|
70
|
-
kind: 'single',
|
|
71
|
-
customFieldId: 'question_0_custom',
|
|
72
|
-
required: false,
|
|
73
|
-
});
|
|
74
|
-
expect(qs[0].options).toEqual([
|
|
75
|
-
{ value: 'Red', label: 'Red', description: 'Warm', preview: null },
|
|
76
|
-
{ value: 'Blue', label: 'Blue', description: null, preview: null },
|
|
77
|
-
]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('multi-select (array+items.anyOf) alongside a single-select sibling', () => {
|
|
81
|
-
const qs = parseElicitationSchema(MULTI_QUESTION_SCHEMA);
|
|
82
|
-
expect(qs.map((q) => q.id)).toEqual(['question_0', 'question_1']);
|
|
83
|
-
expect(qs[0].kind).toBe('single');
|
|
84
|
-
expect(qs[1].kind).toBe('multi');
|
|
85
|
-
expect(qs[1].options).toEqual([
|
|
86
|
-
{ value: 'X', label: 'X', description: null, preview: null },
|
|
87
|
-
{ value: 'Y', label: 'Y', description: null, preview: null },
|
|
88
|
-
]);
|
|
89
|
-
expect(qs[1].customFieldId).toBe('question_1_custom');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('a bare enum (untitled strings) renders with value===label', () => {
|
|
93
|
-
const qs = parseElicitationSchema({
|
|
94
|
-
type: 'object',
|
|
95
|
-
properties: { color: { type: 'string', enum: ['red', 'blue'] } },
|
|
96
|
-
});
|
|
97
|
-
expect(qs[0].options).toEqual([
|
|
98
|
-
{ value: 'red', label: 'red', description: null, preview: null },
|
|
99
|
-
{ value: 'blue', label: 'blue', description: null, preview: null },
|
|
100
|
-
]);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('an enum option carrying the AskUserQuestion preview _meta convention surfaces it as `preview`', () => {
|
|
104
|
-
const qs = parseElicitationSchema({
|
|
105
|
-
type: 'object',
|
|
106
|
-
properties: {
|
|
107
|
-
question_0: {
|
|
108
|
-
type: 'string',
|
|
109
|
-
oneOf: [
|
|
110
|
-
{
|
|
111
|
-
const: 'Option A',
|
|
112
|
-
title: 'Option A',
|
|
113
|
-
_meta: { '_claude/askUserQuestionOption': { preview: 'const x = 1' } },
|
|
114
|
-
},
|
|
115
|
-
{ const: 'Option B', title: 'Option B' },
|
|
116
|
-
],
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
expect(qs[0].options[0].preview).toBe('const x = 1');
|
|
121
|
-
expect(qs[0].options[1].preview).toBeNull();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test('a plain string/number/boolean property with no enum falls back to a text question', () => {
|
|
125
|
-
const qs = parseElicitationSchema({
|
|
126
|
-
type: 'object',
|
|
127
|
-
properties: { note: { type: 'string', title: 'Note' }, age: { type: 'number' } },
|
|
128
|
-
});
|
|
129
|
-
expect(qs.map((q) => q.kind)).toEqual(['text', 'text']);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('schema.required marks a question required', () => {
|
|
133
|
-
const qs = parseElicitationSchema({
|
|
134
|
-
type: 'object',
|
|
135
|
-
properties: { note: { type: 'string' } },
|
|
136
|
-
required: ['note'],
|
|
137
|
-
});
|
|
138
|
-
expect(qs[0].required).toBe(true);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('a "_custom"-suffixed field with NO base sibling renders as its own question', () => {
|
|
142
|
-
const qs = parseElicitationSchema({
|
|
143
|
-
type: 'object',
|
|
144
|
-
properties: { foo_custom: { type: 'string' } },
|
|
145
|
-
});
|
|
146
|
-
expect(qs.map((q) => q.id)).toEqual(['foo_custom']);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('malformed/missing schema tolerance — never throws, returns []', () => {
|
|
150
|
-
expect(parseElicitationSchema(null)).toEqual([]);
|
|
151
|
-
expect(parseElicitationSchema(undefined)).toEqual([]);
|
|
152
|
-
expect(parseElicitationSchema({})).toEqual([]);
|
|
153
|
-
expect(parseElicitationSchema({ properties: null })).toEqual([]);
|
|
154
|
-
expect(parseElicitationSchema({ properties: 'not-an-object' })).toEqual([]);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe('buildElicitationContent', () => {
|
|
159
|
-
test('a selected single-select option folds into content[id]', () => {
|
|
160
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
161
|
-
const content = buildElicitationContent(qs, { question_0: 'Red' });
|
|
162
|
-
expect(content).toEqual({ question_0: 'Red' });
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('a non-empty custom answer overrides the selection (matches applyAskElicitationResponse read order)', () => {
|
|
166
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
167
|
-
const content = buildElicitationContent(qs, {
|
|
168
|
-
question_0: 'Red',
|
|
169
|
-
question_0_custom: ' Something else ',
|
|
170
|
-
});
|
|
171
|
-
expect(content).toEqual({ question_0_custom: 'Something else' });
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('a blank/whitespace-only custom answer does NOT override the selection', () => {
|
|
175
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
176
|
-
const content = buildElicitationContent(qs, { question_0: 'Blue', question_0_custom: ' ' });
|
|
177
|
-
expect(content).toEqual({ question_0: 'Blue' });
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test('multi-select folds a non-empty array; an empty selection is omitted', () => {
|
|
181
|
-
const qs = parseElicitationSchema(MULTI_QUESTION_SCHEMA);
|
|
182
|
-
const content = buildElicitationContent(qs, { question_1: ['X', 'Y'] });
|
|
183
|
-
expect(content).toEqual({ question_1: ['X', 'Y'] });
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test('multi-select: a custom answer is ADDED to the picks (via the base field), not routed through customFieldId', () => {
|
|
187
|
-
const qs = parseElicitationSchema(MULTI_QUESTION_SCHEMA);
|
|
188
|
-
const content = buildElicitationContent(qs, {
|
|
189
|
-
question_1: ['X', 'Y'],
|
|
190
|
-
question_1_custom: ' Z ',
|
|
191
|
-
});
|
|
192
|
-
// Combined into the BASE field's array — applyAskElicitationResponse only
|
|
193
|
-
// overrides when content[customFieldId] itself is non-empty, so leaving
|
|
194
|
-
// that key absent and appending to content[question_1] instead means the
|
|
195
|
-
// adapter's own join() sees the pick AND the custom text.
|
|
196
|
-
expect(content).toEqual({ question_1: ['X', 'Y', 'Z'] });
|
|
197
|
-
expect(content.question_1_custom).toBeUndefined();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test('multi-select: a custom answer with NO picks still sends as a single-entry array', () => {
|
|
201
|
-
const qs = parseElicitationSchema(MULTI_QUESTION_SCHEMA);
|
|
202
|
-
const content = buildElicitationContent(qs, { question_1_custom: 'Just this' });
|
|
203
|
-
expect(content).toEqual({ question_1: ['Just this'] });
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
test('an unanswered, non-required question is omitted entirely — never fabricates a value', () => {
|
|
207
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
208
|
-
expect(buildElicitationContent(qs, {})).toEqual({});
|
|
209
|
-
expect(buildElicitationContent([], { anything: 'x' })).toEqual({});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
test('tolerates a missing/malformed answers object', () => {
|
|
213
|
-
const qs = parseElicitationSchema(SINGLE_SELECT_SCHEMA);
|
|
214
|
-
expect(buildElicitationContent(qs, null)).toEqual({});
|
|
215
|
-
expect(buildElicitationContent(qs, undefined)).toEqual({});
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe('looksLikeSecretRequest', () => {
|
|
220
|
-
test('matches common credential-shaped phrasing, case-insensitively', () => {
|
|
221
|
-
expect(looksLikeSecretRequest('Enter your API key')).toBe(true);
|
|
222
|
-
expect(looksLikeSecretRequest('what is your PASSWORD')).toBe(true);
|
|
223
|
-
expect(looksLikeSecretRequest('Paste your auth token here')).toBe(true);
|
|
224
|
-
expect(looksLikeSecretRequest('Provide your OAuth token')).toBe(true);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test('checks all provided texts, not just the first', () => {
|
|
228
|
-
expect(looksLikeSecretRequest('Which color do you like?', 'a secret preference')).toBe(true);
|
|
229
|
-
expect(looksLikeSecretRequest(null, 'Enter your secret key', undefined)).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test('an ordinary question does not trip the heuristic', () => {
|
|
233
|
-
expect(looksLikeSecretRequest('Which color do you like?')).toBe(false);
|
|
234
|
-
expect(looksLikeSecretRequest('What layout do you prefer?')).toBe(false);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('tolerates no/malformed input', () => {
|
|
238
|
-
expect(looksLikeSecretRequest()).toBe(false);
|
|
239
|
-
expect(looksLikeSecretRequest(null, undefined, 42)).toBe(false);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test("parseElicitationSchema tags each question's own secretShaped flag from its title/description", () => {
|
|
243
|
-
const qs = parseElicitationSchema({
|
|
244
|
-
type: 'object',
|
|
245
|
-
properties: {
|
|
246
|
-
question_0: { type: 'string', description: 'Enter your password to continue' },
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
expect(qs[0].secretShaped).toBe(true);
|
|
250
|
-
});
|
|
251
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// DDR-123 guardrail #1 — the single load-bearing detail that keeps the chat
|
|
2
|
-
// panel on the user's Pro/Max subscription instead of metered API billing.
|
|
3
|
-
|
|
4
|
-
import { describe, expect, test } from 'bun:test';
|
|
5
|
-
|
|
6
|
-
import { SUBSCRIPTION_SCRUBBED_ENV_KEYS, scrubAgentEnv } from '../acp/env.ts';
|
|
7
|
-
|
|
8
|
-
describe('scrubAgentEnv — subscription guardrail', () => {
|
|
9
|
-
test('strips the billing-switching keys, keeps everything else', () => {
|
|
10
|
-
const out = scrubAgentEnv({
|
|
11
|
-
PATH: '/usr/bin',
|
|
12
|
-
HOME: '/Users/x',
|
|
13
|
-
ANTHROPIC_API_KEY: 'sk-live-xxx',
|
|
14
|
-
ANTHROPIC_AUTH_TOKEN: 'tok-xxx',
|
|
15
|
-
UNDEFINED_VAR: undefined,
|
|
16
|
-
});
|
|
17
|
-
expect(out.PATH).toBe('/usr/bin');
|
|
18
|
-
expect(out.HOME).toBe('/Users/x');
|
|
19
|
-
expect('ANTHROPIC_API_KEY' in out).toBe(false);
|
|
20
|
-
expect('ANTHROPIC_AUTH_TOKEN' in out).toBe(false);
|
|
21
|
-
// undefined values are dropped (Bun.spawn env wants Record<string,string>)
|
|
22
|
-
expect('UNDEFINED_VAR' in out).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('pins exactly the two precedence-relevant keys', () => {
|
|
26
|
-
expect([...SUBSCRIPTION_SCRUBBED_ENV_KEYS].sort()).toEqual([
|
|
27
|
-
'ANTHROPIC_API_KEY',
|
|
28
|
-
'ANTHROPIC_AUTH_TOKEN',
|
|
29
|
-
]);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('scrubs the whole provider/billing namespace, not just the two keys (F1)', () => {
|
|
33
|
-
const out = scrubAgentEnv({
|
|
34
|
-
PATH: '/usr/bin',
|
|
35
|
-
ANTHROPIC_BASE_URL: 'https://evil.example/v1',
|
|
36
|
-
ANTHROPIC_MODEL: 'attacker-pinned',
|
|
37
|
-
ANTHROPIC_BEDROCK_BASE_URL: 'https://evil',
|
|
38
|
-
CLAUDE_CODE_USE_BEDROCK: '1',
|
|
39
|
-
CLAUDE_CODE_USE_VERTEX: '1',
|
|
40
|
-
AWS_BEARER_TOKEN_BEDROCK: 'tok',
|
|
41
|
-
claude_session: 'keep-me', // not a provider redirect → kept
|
|
42
|
-
});
|
|
43
|
-
expect(out.PATH).toBe('/usr/bin');
|
|
44
|
-
expect('ANTHROPIC_BASE_URL' in out).toBe(false);
|
|
45
|
-
expect('ANTHROPIC_MODEL' in out).toBe(false); // re-added by the bridge from a validated value
|
|
46
|
-
expect('ANTHROPIC_BEDROCK_BASE_URL' in out).toBe(false);
|
|
47
|
-
expect('CLAUDE_CODE_USE_BEDROCK' in out).toBe(false);
|
|
48
|
-
expect('CLAUDE_CODE_USE_VERTEX' in out).toBe(false);
|
|
49
|
-
expect('AWS_BEARER_TOKEN_BEDROCK' in out).toBe(false);
|
|
50
|
-
expect(out.claude_session).toBe('keep-me');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('never mutates the source (process.env stays intact for the parent)', () => {
|
|
54
|
-
const src: Record<string, string | undefined> = { ANTHROPIC_API_KEY: 'sk', PATH: '/bin' };
|
|
55
|
-
const out = scrubAgentEnv(src);
|
|
56
|
-
expect(src.ANTHROPIC_API_KEY).toBe('sk');
|
|
57
|
-
expect(out).not.toBe(src);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('defaults to process.env when called with no argument', () => {
|
|
61
|
-
// Smoke: doesn't throw, returns a string map without the scrubbed keys.
|
|
62
|
-
const out = scrubAgentEnv();
|
|
63
|
-
expect('ANTHROPIC_API_KEY' in out).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// feature-ai-media-generation Phase 1 (Task 1.4) + DDR-164 F3 — the ACP chat
|
|
67
|
-
// panel can TRIGGER generation because the BYOK provider key is resolved
|
|
68
|
-
// SERVER-SIDE (the dev-server, a separate process, reads the 0600 file /
|
|
69
|
-
// keychain via keys.ts). The ACP `claude` subprocess never needs a generation
|
|
70
|
-
// key-custody env var, so we SCRUB them: the Phase-5.1 keychain-bridge endpoint
|
|
71
|
-
// + key (`MAUDE_GEN_KEY_*` — F3, brought forward so the tripwire is armed before
|
|
72
|
-
// the bridge is wired) AND a custom key-file path (`MAUDE_GEN_KEYS_PATH`). This
|
|
73
|
-
// does NOT make the key unreachable to a compromised same-UID agent (it can
|
|
74
|
-
// still read the default keys.json off disk — the pre-existing full-tool-agent
|
|
75
|
-
// trifecta, documented, not closable by an env-scrub); it only ensures the child
|
|
76
|
-
// is never HANDED a pointer/credential it doesn't need. Meanwhile the `maude`
|
|
77
|
-
// CLI on PATH still resolves, so generation keeps working through the scrub.
|
|
78
|
-
test('scrubs the generation key-custody env vars, keeps CLI PATH (Task 1.4 / DDR-164 F3)', () => {
|
|
79
|
-
const out = scrubAgentEnv({
|
|
80
|
-
PATH: '/usr/local/bin:/usr/bin',
|
|
81
|
-
MAUDE_GEN_KEYS_PATH: '/Users/x/.config/maude/keys.json',
|
|
82
|
-
MAUDE_GEN_KEY_ENDPOINT: 'http://127.0.0.1:9/keychain', // Phase-5.1 bridge — MUST be scrubbed (F3)
|
|
83
|
-
MAUDE_GEN_KEY_KEY: 'per-launch-bridge-secret',
|
|
84
|
-
ANTHROPIC_API_KEY: 'sk-should-be-scrubbed',
|
|
85
|
-
});
|
|
86
|
-
// The generate CLI stays reachable (the sidecar, not this child, holds the key).
|
|
87
|
-
expect(out.PATH).toBe('/usr/local/bin:/usr/bin');
|
|
88
|
-
// Every generation key-custody var is stripped from the agent env.
|
|
89
|
-
expect('MAUDE_GEN_KEYS_PATH' in out).toBe(false);
|
|
90
|
-
expect('MAUDE_GEN_KEY_ENDPOINT' in out).toBe(false);
|
|
91
|
-
expect('MAUDE_GEN_KEY_KEY' in out).toBe(false);
|
|
92
|
-
// The subscription guardrail still fires.
|
|
93
|
-
expect('ANTHROPIC_API_KEY' in out).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// DDR-184 #3 — the predicate behind ChatPanel's loud "you can't edit" ModeBanner.
|
|
2
|
-
// Pure unit test (the banner itself is a thin render of this). Keyed off the mode
|
|
3
|
-
// ID, never a label, so it stays truthful as the adapter's roster evolves.
|
|
4
|
-
|
|
5
|
-
import { describe, expect, test } from 'bun:test';
|
|
6
|
-
|
|
7
|
-
import { modeBlocksEdits, NO_EDIT_MODE_IDS } from '../client/panels/acp-capabilities.js';
|
|
8
|
-
|
|
9
|
-
const rosterWith = (currentModeId: string) => ({
|
|
10
|
-
currentModeId,
|
|
11
|
-
availableModes: [
|
|
12
|
-
{ id: 'default', name: 'Manual' },
|
|
13
|
-
{ id: 'acceptEdits', name: 'Accept Edits' },
|
|
14
|
-
{ id: 'plan', name: 'Plan Mode' },
|
|
15
|
-
{ id: 'dontAsk', name: "Don't Ask" },
|
|
16
|
-
{ id: 'bypassPermissions', name: 'Bypass Permissions' },
|
|
17
|
-
],
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('modeBlocksEdits', () => {
|
|
21
|
-
test('blocks in Plan Mode (no tool execution)', () => {
|
|
22
|
-
expect(modeBlocksEdits(rosterWith('plan'))).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("blocks in Don't-Ask (denies anything without a standing approval)", () => {
|
|
26
|
-
expect(modeBlocksEdits(rosterWith('dontAsk'))).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('does NOT block in the editing modes (Manual / Accept-Edits / Bypass)', () => {
|
|
30
|
-
expect(modeBlocksEdits(rosterWith('default'))).toBe(false);
|
|
31
|
-
expect(modeBlocksEdits(rosterWith('acceptEdits'))).toBe(false);
|
|
32
|
-
expect(modeBlocksEdits(rosterWith('bypassPermissions'))).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('false when the roster is empty / not yet loaded (no premature alarm)', () => {
|
|
36
|
-
expect(modeBlocksEdits(null)).toBe(false);
|
|
37
|
-
expect(modeBlocksEdits(undefined)).toBe(false);
|
|
38
|
-
expect(modeBlocksEdits({ availableModes: [] })).toBe(false);
|
|
39
|
-
expect(modeBlocksEdits({ currentModeId: '' })).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('NO_EDIT_MODE_IDS is exactly {plan, dontAsk}', () => {
|
|
43
|
-
expect([...NO_EDIT_MODE_IDS].sort()).toEqual(['dontAsk', 'plan']);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
// ACP bridge origin gate (DDR-123). The agent bridge spawns the user's `claude`
|
|
2
|
-
// and can drive file edits, so it lives ONLY on the main (privileged) origin and
|
|
3
|
-
// only over loopback. This proves:
|
|
4
|
-
// (1) GET /_api/acp/status serves on the main origin;
|
|
5
|
-
// (2) it is 403'd on the untrusted canvas origin (off CANVAS_SAFE_API +
|
|
6
|
-
// startCanvasServer routes — the dual-allowlist rule);
|
|
7
|
-
// (3) /_ws/acp is wired on the main origin (upgrade-without-headers → 400);
|
|
8
|
-
// (4) the loopback guard that fronts /_ws/acp rejects non-loopback hosts.
|
|
9
|
-
|
|
10
|
-
import { describe, expect, test } from 'bun:test';
|
|
11
|
-
import { readFileSync } from 'node:fs';
|
|
12
|
-
import { join } from 'node:path';
|
|
13
|
-
|
|
14
|
-
import { isLoopbackHost, isSameOriginWs } from '../ws.ts';
|
|
15
|
-
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
16
|
-
|
|
17
|
-
// A minimal Request stand-in — `new Request()` strips forbidden headers like
|
|
18
|
-
// `Host`, so drive the gate through a header-getter shim instead.
|
|
19
|
-
function wsReq(host: string | null, origin: string | null): Request {
|
|
20
|
-
return {
|
|
21
|
-
headers: {
|
|
22
|
-
get: (n: string) =>
|
|
23
|
-
n.toLowerCase() === 'host' ? host : n.toLowerCase() === 'origin' ? origin : null,
|
|
24
|
-
},
|
|
25
|
-
} as unknown as Request;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function readCanvasOrigin(designRoot: string): Promise<string> {
|
|
29
|
-
for (let i = 0; i < 40; i++) {
|
|
30
|
-
try {
|
|
31
|
-
const info = JSON.parse(readFileSync(join(designRoot, '_server.json'), 'utf8'));
|
|
32
|
-
if (info.canvasOrigin) return info.canvasOrigin as string;
|
|
33
|
-
} catch {
|
|
34
|
-
/* not written yet */
|
|
35
|
-
}
|
|
36
|
-
await Bun.sleep(50);
|
|
37
|
-
}
|
|
38
|
-
throw new Error('canvasOrigin never appeared in _server.json');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe('ACP bridge origin gate', () => {
|
|
42
|
-
test('status is main-origin only; /_ws/acp wired on main', async () => {
|
|
43
|
-
const { root, designRoot } = makeSandbox();
|
|
44
|
-
const port = nextPort();
|
|
45
|
-
const proc = await bootServer(root, port, { MAUDE_CANVAS_ORIGIN_SPLIT: '1' });
|
|
46
|
-
try {
|
|
47
|
-
const main = `http://localhost:${port}`;
|
|
48
|
-
const canvas = await readCanvasOrigin(designRoot);
|
|
49
|
-
const status = async (base: string, path: string) =>
|
|
50
|
-
(await fetch(base + path, { signal: AbortSignal.timeout(2000) })).status;
|
|
51
|
-
|
|
52
|
-
// (1) main origin exposes the readiness probe
|
|
53
|
-
const probe = await fetch(`${main}/_api/acp/status`, { signal: AbortSignal.timeout(2000) });
|
|
54
|
-
expect(probe.status).toBe(200);
|
|
55
|
-
expect(typeof (await probe.json()).available).toBe('boolean');
|
|
56
|
-
|
|
57
|
-
// (2) the untrusted canvas origin must NOT reach the probe
|
|
58
|
-
expect(await status(canvas, '/_api/acp/status')).toBe(403);
|
|
59
|
-
|
|
60
|
-
// (3) /_ws/acp is wired on the main origin — a plain GET (no Upgrade
|
|
61
|
-
// headers) passes the loopback guard then fails the WS upgrade → 400,
|
|
62
|
-
// which proves the route branch exists (a 404 would mean it's unwired).
|
|
63
|
-
expect(await status(main, '/_ws/acp')).toBe(400);
|
|
64
|
-
|
|
65
|
-
// (3b) CSWSH gate — a cross-origin drive-by CANNOT open /_ws/acp even over
|
|
66
|
-
// loopback: an Origin that isn't this same loopback server is 403'd BEFORE
|
|
67
|
-
// the upgrade. WS handshakes bypass SOP, and the bridge spawns the user's
|
|
68
|
-
// `claude` + drives edits, so Host-loopback alone is not enough.
|
|
69
|
-
const driveBy = await fetch(`${main}/_ws/acp`, {
|
|
70
|
-
headers: { Origin: 'https://evil.example' },
|
|
71
|
-
signal: AbortSignal.timeout(2000),
|
|
72
|
-
});
|
|
73
|
-
expect(driveBy.status).toBe(403);
|
|
74
|
-
|
|
75
|
-
// (4) /_api/acp/focus (the /design:chat hook) — POST-only on the main
|
|
76
|
-
// origin, 403 on the canvas origin.
|
|
77
|
-
const focusPost = await fetch(`${main}/_api/acp/focus`, {
|
|
78
|
-
method: 'POST',
|
|
79
|
-
signal: AbortSignal.timeout(2000),
|
|
80
|
-
});
|
|
81
|
-
expect(focusPost.status).toBe(200);
|
|
82
|
-
expect((await focusPost.json()).ok).toBe(true);
|
|
83
|
-
expect(await status(main, '/_api/acp/focus')).toBe(405); // GET not allowed
|
|
84
|
-
expect(await status(canvas, '/_api/acp/focus')).toBe(403); // off the canvas origin
|
|
85
|
-
|
|
86
|
-
// (5) DDR-128 — /_api/preflight readiness probe: main-origin 200 with the
|
|
87
|
-
// {ready, items[]} shape; 403 on the untrusted canvas origin (dual-allowlist).
|
|
88
|
-
const pre = await fetch(`${main}/_api/preflight`, { signal: AbortSignal.timeout(2000) });
|
|
89
|
-
expect(pre.status).toBe(200);
|
|
90
|
-
const preJson = await pre.json();
|
|
91
|
-
expect(typeof preJson.ready).toBe('boolean');
|
|
92
|
-
expect(Array.isArray(preJson.items)).toBe(true);
|
|
93
|
-
expect(await status(canvas, '/_api/preflight')).toBe(403);
|
|
94
|
-
// A cross-origin drive-by GET is Origin-rejected before the probe can shell
|
|
95
|
-
// out — DDR-128 DoS hardening (ethical-hacker F1). Same-origin/loopback (no
|
|
96
|
-
// Origin header, asserted above) stays 200.
|
|
97
|
-
const crossOrigin = await fetch(`${main}/_api/preflight`, {
|
|
98
|
-
headers: { Origin: 'https://evil.example' },
|
|
99
|
-
signal: AbortSignal.timeout(2000),
|
|
100
|
-
});
|
|
101
|
-
expect(crossOrigin.status).toBe(403);
|
|
102
|
-
|
|
103
|
-
// (6) Phase 31 follow-up — /_api/acp/attachment (clipboard-image paste):
|
|
104
|
-
// main-origin only. A valid PNG → 201 with an absolute path under the
|
|
105
|
-
// runtime _chat/attachments/; GET (the thumbnail serve branch) 404s
|
|
106
|
-
// without a valid content-addressed name — full serve coverage lives in
|
|
107
|
-
// acp-attachment-serve.test.ts; the canvas origin + a cross-origin
|
|
108
|
-
// drive-by POST are 403.
|
|
109
|
-
const png = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0]);
|
|
110
|
-
const up = await fetch(`${main}/_api/acp/attachment`, {
|
|
111
|
-
method: 'POST',
|
|
112
|
-
headers: { 'Content-Type': 'image/png' },
|
|
113
|
-
body: png,
|
|
114
|
-
signal: AbortSignal.timeout(2000),
|
|
115
|
-
});
|
|
116
|
-
expect(up.status).toBe(201);
|
|
117
|
-
const upJson = await up.json();
|
|
118
|
-
expect(typeof upJson.path).toBe('string');
|
|
119
|
-
expect(upJson.path).toContain('_chat/attachments/');
|
|
120
|
-
expect(await status(main, '/_api/acp/attachment')).toBe(404); // GET without a name → 404
|
|
121
|
-
expect(await status(canvas, '/_api/acp/attachment')).toBe(403); // off the canvas origin
|
|
122
|
-
const attDriveBy = await fetch(`${main}/_api/acp/attachment`, {
|
|
123
|
-
method: 'POST',
|
|
124
|
-
headers: { Origin: 'https://evil.example', 'Content-Type': 'image/png' },
|
|
125
|
-
body: png,
|
|
126
|
-
signal: AbortSignal.timeout(2000),
|
|
127
|
-
});
|
|
128
|
-
expect(attDriveBy.status).toBe(403);
|
|
129
|
-
} finally {
|
|
130
|
-
await killProc(proc);
|
|
131
|
-
}
|
|
132
|
-
}, 15000);
|
|
133
|
-
|
|
134
|
-
test('the loopback guard fronting /_ws/acp rejects non-loopback hosts', () => {
|
|
135
|
-
// Same guard server.ts applies before upgrading the acp socket.
|
|
136
|
-
expect(isLoopbackHost('127.0.0.1:4399')).toBe(true);
|
|
137
|
-
expect(isLoopbackHost('localhost:4399')).toBe(true);
|
|
138
|
-
expect(isLoopbackHost('[::1]:4399')).toBe(true);
|
|
139
|
-
expect(isLoopbackHost('evil.example.com')).toBe(false);
|
|
140
|
-
expect(isLoopbackHost('10.0.0.5:4399')).toBe(false);
|
|
141
|
-
expect(isLoopbackHost(null)).toBe(false);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('isSameOriginWs — CSWSH gate: only same-loopback-port Origins (or none) pass', () => {
|
|
145
|
-
// No Origin (CLI / tests / non-browser) → allowed.
|
|
146
|
-
expect(isSameOriginWs(wsReq('localhost:4399', null))).toBe(true);
|
|
147
|
-
// Same loopback host + port (native panel + same-origin `maude design serve`
|
|
148
|
-
// tab both navigate to http://localhost:<serverport>) → allowed.
|
|
149
|
-
expect(isSameOriginWs(wsReq('localhost:4399', 'http://localhost:4399'))).toBe(true);
|
|
150
|
-
expect(isSameOriginWs(wsReq('127.0.0.1:4399', 'http://127.0.0.1:4399'))).toBe(true);
|
|
151
|
-
// localhost ≡ 127.0.0.1 on the same port → allowed (same server).
|
|
152
|
-
expect(isSameOriginWs(wsReq('localhost:4399', 'http://127.0.0.1:4399'))).toBe(true);
|
|
153
|
-
// Cross-origin drive-by (remote host) → rejected — the CSWSH close.
|
|
154
|
-
expect(isSameOriginWs(wsReq('localhost:4399', 'https://evil.example'))).toBe(false);
|
|
155
|
-
// Same host, DIFFERENT local port (attacker's own dev server) → rejected.
|
|
156
|
-
expect(isSameOriginWs(wsReq('localhost:4399', 'http://localhost:3000'))).toBe(false);
|
|
157
|
-
// Opaque/null origin (sandboxed iframe, file://) → rejected.
|
|
158
|
-
expect(isSameOriginWs(wsReq('localhost:4399', 'null'))).toBe(false);
|
|
159
|
-
});
|
|
160
|
-
});
|