@1agh/maude 1.0.11 → 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/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/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/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 +72 -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 -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
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import type { AssetPushProgress } from './asset-push.ts';
|
|
16
16
|
import type { SyncStatusSnapshot } from './connection-state.ts';
|
|
17
|
+
import type { SeedProgress } from './seed-progress.ts';
|
|
17
18
|
|
|
18
19
|
// `cold-start-hub-wins` stays in the union for OLD payload readers (additive
|
|
19
20
|
// evolution — the NoSyncablePayload discriminator pattern); new conflicts are
|
|
@@ -41,6 +42,11 @@ export interface SyncStatusPayload extends SyncStatusSnapshot {
|
|
|
41
42
|
canvases: number;
|
|
42
43
|
/** Recent conflict notifications (most-recent-last, capped). */
|
|
43
44
|
conflicts: SyncConflict[];
|
|
45
|
+
/**
|
|
46
|
+
* When the ConnectionMonitor last spoke, as distinct from when this payload
|
|
47
|
+
* was built (`updatedAt`). Additive — see the note in `payload()`.
|
|
48
|
+
*/
|
|
49
|
+
connectionUpdatedAt?: number;
|
|
44
50
|
/**
|
|
45
51
|
* Phase 9.2 (DDR-064) — true when the unified single-shared-doc model is
|
|
46
52
|
* active (MAUDE_SHARED_DOC). Lets `maude design status` + the browser banner
|
|
@@ -112,6 +118,25 @@ export interface FilePlaneStatus {
|
|
|
112
118
|
/** One sentence, in the user's terms, about what is waiting and why. */
|
|
113
119
|
detail: string;
|
|
114
120
|
}[];
|
|
121
|
+
/**
|
|
122
|
+
* Files this lane could NOT move on the last pass — issue #109.
|
|
123
|
+
*
|
|
124
|
+
* `held` covers the breakers, which are deliberate refusals this machine
|
|
125
|
+
* made. This covers the other half: a transfer the hub refused, or a write
|
|
126
|
+
* that failed. It had no field at all, so ~200 refused assets rendered as a
|
|
127
|
+
* status bar reading `synced` and a person seeing broken images with no
|
|
128
|
+
* cause anywhere but a terminal they never open. Same disease `held` was
|
|
129
|
+
* added to cure, one lane over.
|
|
130
|
+
*/
|
|
131
|
+
failed?: number;
|
|
132
|
+
/**
|
|
133
|
+
* The hub asked us to slow down and the WHOLE LANE is paused until `until`.
|
|
134
|
+
*
|
|
135
|
+
* Distinguished from `failed` because the answer is different: nothing is
|
|
136
|
+
* wrong, nothing is lost, and there is nothing to do but wait — which is
|
|
137
|
+
* exactly what a person cannot infer from a count of failures.
|
|
138
|
+
*/
|
|
139
|
+
rateLimited?: { until: number; waiting: number };
|
|
115
140
|
/**
|
|
116
141
|
* THE DORUČENKA (DDR-226 §7) — per-path delivery state.
|
|
117
142
|
*
|
|
@@ -126,6 +151,26 @@ export interface FilePlaneStatus {
|
|
|
126
151
|
* as the thing to disagree with.
|
|
127
152
|
*/
|
|
128
153
|
delivery?: Record<string, string>;
|
|
154
|
+
/**
|
|
155
|
+
* How many delivery rows were dropped from `delivery` by its cap.
|
|
156
|
+
*
|
|
157
|
+
* The map used to be unbounded — one entry per ledger row, so 2 961 keys for
|
|
158
|
+
* a real project, re-serialized into `_sync.json` AND broadcast over the
|
|
159
|
+
* WebSocket on EVERY status change, including 200 ms-throttled asset-progress
|
|
160
|
+
* emits. Same bounding shape `items`/`itemsTruncated` already uses.
|
|
161
|
+
*/
|
|
162
|
+
deliveryTruncated?: number;
|
|
163
|
+
/**
|
|
164
|
+
* THE DENOMINATOR (feature-large-project-seed).
|
|
165
|
+
*
|
|
166
|
+
* Derived from the LEDGER rather than from per-pass results — see
|
|
167
|
+
* `seed-progress.ts` for why the per-pass counters read zero for twenty
|
|
168
|
+
* minutes while 2 961 rows changed underneath them. The raw counters above
|
|
169
|
+
* stay beside it deliberately: a panel derived from the same source it
|
|
170
|
+
* displays cannot be cross-checked, and DDR-214's lesson is that a status
|
|
171
|
+
* surface has to be falsifiable.
|
|
172
|
+
*/
|
|
173
|
+
progress?: SeedProgress;
|
|
129
174
|
}
|
|
130
175
|
|
|
131
176
|
export interface SyncStatusStoreOptions {
|
|
@@ -139,6 +184,8 @@ export interface SyncStatusStoreOptions {
|
|
|
139
184
|
broadcast?: (payload: SyncStatusPayload) => void;
|
|
140
185
|
/** Max conflict notifications retained. Default 20. */
|
|
141
186
|
maxConflicts?: number;
|
|
187
|
+
/** Floor between two writes/broadcasts. 0 disables coalescing (tests). */
|
|
188
|
+
flushIntervalMs?: number;
|
|
142
189
|
now?: () => number;
|
|
143
190
|
}
|
|
144
191
|
|
|
@@ -164,9 +211,17 @@ export interface SyncStatusStore {
|
|
|
164
211
|
|
|
165
212
|
const DEFAULT_MAX_CONFLICTS = 20;
|
|
166
213
|
|
|
214
|
+
/** Floor between two status writes/broadcasts. Matches `asset-push.ts`'s own
|
|
215
|
+
* `PROGRESS_INTERVAL_MS` — the lane that emits fastest sets the pace. */
|
|
216
|
+
export const STATUS_FLUSH_MS = 200;
|
|
217
|
+
|
|
218
|
+
/** Delivery rows one payload carries, actionable-first. See `deliveryTruncated`. */
|
|
219
|
+
export const MAX_DELIVERY_ROWS = 300;
|
|
220
|
+
|
|
167
221
|
export function createSyncStatusStore(opts: SyncStatusStoreOptions): SyncStatusStore {
|
|
168
222
|
const now = opts.now ?? Date.now;
|
|
169
223
|
const maxConflicts = opts.maxConflicts ?? DEFAULT_MAX_CONFLICTS;
|
|
224
|
+
const flushIntervalMs = opts.flushIntervalMs ?? STATUS_FLUSH_MS;
|
|
170
225
|
const conflicts: SyncConflict[] = [];
|
|
171
226
|
|
|
172
227
|
let snapshot: SyncStatusSnapshot = {
|
|
@@ -188,6 +243,18 @@ export function createSyncStatusStore(opts: SyncStatusStoreOptions): SyncStatusS
|
|
|
188
243
|
function payload(): SyncStatusPayload {
|
|
189
244
|
return {
|
|
190
245
|
...snapshot,
|
|
246
|
+
// WHEN THIS PAYLOAD WAS BUILT, not when the connection monitor last
|
|
247
|
+
// spoke. `updatedAt` arrived here by spreading the monitor's snapshot,
|
|
248
|
+
// which only `update()` refreshes — so `updateFiles()` and
|
|
249
|
+
// `updateAssets()` flushed payloads carrying a stale timestamp ABOUT
|
|
250
|
+
// THEMSELVES. Measured at 130 s stale against a ledger that was current
|
|
251
|
+
// to within a second, on a file being rewritten continuously. A
|
|
252
|
+
// freshness stamp that lies is worse than none: it is what a person (and
|
|
253
|
+
// `maude design status`) uses to decide whether sync is alive at all.
|
|
254
|
+
updatedAt: now(),
|
|
255
|
+
// The monitor's own stamp, preserved under a name that says what it is,
|
|
256
|
+
// so nothing that depended on the old meaning silently changes meaning.
|
|
257
|
+
connectionUpdatedAt: snapshot.updatedAt,
|
|
191
258
|
url: opts.url,
|
|
192
259
|
canvases: opts.canvases,
|
|
193
260
|
conflicts: conflicts.slice(),
|
|
@@ -198,7 +265,23 @@ export function createSyncStatusStore(opts: SyncStatusStoreOptions): SyncStatusS
|
|
|
198
265
|
};
|
|
199
266
|
}
|
|
200
267
|
|
|
201
|
-
|
|
268
|
+
/**
|
|
269
|
+
* Write + broadcast, COALESCED.
|
|
270
|
+
*
|
|
271
|
+
* Every emit used to re-serialize the whole payload — including an unbounded
|
|
272
|
+
* per-path delivery map — to disk and to every open tab. During a seed the
|
|
273
|
+
* asset lane alone emits every 200 ms, and the file lane emits per pass, so
|
|
274
|
+
* the cost was paid continuously for a payload nobody could read that fast.
|
|
275
|
+
*
|
|
276
|
+
* `immediate` is not an optimisation escape hatch: conflicts, notices and
|
|
277
|
+
* terminal transitions are the events a person is waiting on, and delaying
|
|
278
|
+
* those by even a debounce window is the class of lie this file exists to
|
|
279
|
+
* avoid.
|
|
280
|
+
*/
|
|
281
|
+
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
282
|
+
let pendingFlush = false;
|
|
283
|
+
|
|
284
|
+
function emit(): void {
|
|
202
285
|
const p = payload();
|
|
203
286
|
try {
|
|
204
287
|
opts.write(p);
|
|
@@ -212,15 +295,47 @@ export function createSyncStatusStore(opts: SyncStatusStoreOptions): SyncStatusS
|
|
|
212
295
|
}
|
|
213
296
|
}
|
|
214
297
|
|
|
298
|
+
function flush(immediate = false): void {
|
|
299
|
+
if (flushIntervalMs <= 0) {
|
|
300
|
+
emit();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (!immediate && flushTimer !== null) {
|
|
304
|
+
pendingFlush = true;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
// An immediate flush jumps the queue but still OPENS the window — without
|
|
308
|
+
// that, every urgent write was followed by a free un-coalesced one, so a
|
|
309
|
+
// conflict during a seed re-admitted the whole burst it was jumping ahead of.
|
|
310
|
+
if (flushTimer !== null) {
|
|
311
|
+
clearTimeout(flushTimer);
|
|
312
|
+
flushTimer = null;
|
|
313
|
+
}
|
|
314
|
+
pendingFlush = false;
|
|
315
|
+
emit();
|
|
316
|
+
flushTimer = setTimeout(() => {
|
|
317
|
+
flushTimer = null;
|
|
318
|
+
// Only if something arrived while we were waiting — an idle store must
|
|
319
|
+
// not keep a timer alive forever.
|
|
320
|
+
if (pendingFlush) {
|
|
321
|
+
pendingFlush = false;
|
|
322
|
+
flush();
|
|
323
|
+
}
|
|
324
|
+
}, flushIntervalMs);
|
|
325
|
+
flushTimer.unref?.();
|
|
326
|
+
}
|
|
327
|
+
|
|
215
328
|
return {
|
|
216
329
|
update(next) {
|
|
330
|
+
const changed = snapshot.state !== next.state;
|
|
217
331
|
snapshot = next;
|
|
218
|
-
|
|
332
|
+
// A connection state CHANGE is the headline; a heartbeat is not.
|
|
333
|
+
flush(changed);
|
|
219
334
|
},
|
|
220
335
|
addConflict(conflict) {
|
|
221
336
|
conflicts.push({ ...conflict, at: now() });
|
|
222
337
|
if (conflicts.length > maxConflicts) conflicts.splice(0, conflicts.length - maxConflicts);
|
|
223
|
-
flush();
|
|
338
|
+
flush(true);
|
|
224
339
|
},
|
|
225
340
|
updateAssets(progress) {
|
|
226
341
|
assets = progress;
|
|
@@ -228,12 +343,14 @@ export function createSyncStatusStore(opts: SyncStatusStoreOptions): SyncStatusS
|
|
|
228
343
|
},
|
|
229
344
|
updateFiles(next) {
|
|
230
345
|
files = next;
|
|
231
|
-
|
|
346
|
+
// A finished or stalled seed is what a person is waiting to see; a
|
|
347
|
+
// mid-seed tick is not.
|
|
348
|
+
flush(next.progress?.phase === 'converged' || next.progress?.phase === 'blocked');
|
|
232
349
|
},
|
|
233
350
|
notice(next) {
|
|
234
351
|
if (notices.some((n) => n.id === next.id)) return;
|
|
235
352
|
notices.push({ ...next, at: now() });
|
|
236
|
-
flush();
|
|
353
|
+
flush(true);
|
|
237
354
|
},
|
|
238
355
|
get: payload,
|
|
239
356
|
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// What a THROWN fetch error means, in words a person can act on.
|
|
2
|
+
//
|
|
3
|
+
// THE BUG THIS EXISTS TO FIX. Every `catch` in `file-plane.ts` stored
|
|
4
|
+
// `(err as Error).message` directly into the per-path delivery reason a person
|
|
5
|
+
// reads in the Sync panel. Those messages come from Bun's runtime, not from us:
|
|
6
|
+
//
|
|
7
|
+
// $ bun -e "try{await fetch('http://127.0.0.1:1/x')}catch(e){console.log(e.message)}"
|
|
8
|
+
// Unable to connect. Is the computer able to access the url?
|
|
9
|
+
//
|
|
10
|
+
// On 2026-09-03, 484 of 803 undelivered files in an 8.8 GB project carried
|
|
11
|
+
// exactly those two Bun strings — because the cell they were being pushed to
|
|
12
|
+
// was restarting in a loop. So a person looking for "why is my project not
|
|
13
|
+
// syncing" was told, 135 times, to check their URL for a typo. It also cost a
|
|
14
|
+
// debugging session: the strings appear nowhere in this repo, so grepping for
|
|
15
|
+
// them found nothing and the reason class looked like a mystery.
|
|
16
|
+
//
|
|
17
|
+
// `failureReason()` in `retry-after.ts` is the sibling for RESPONSE refusals —
|
|
18
|
+
// a request that arrived and was declined. This module is for the requests that
|
|
19
|
+
// never arrived at all.
|
|
20
|
+
|
|
21
|
+
/** What kind of transport failure this was. */
|
|
22
|
+
export type TransportErrorClass = 'unreachable' | 'timeout' | 'aborted' | 'other';
|
|
23
|
+
|
|
24
|
+
export interface ClassifiedTransportError {
|
|
25
|
+
class: TransportErrorClass;
|
|
26
|
+
/** What a person reads. Ours, never the runtime's. */
|
|
27
|
+
text: string;
|
|
28
|
+
/** The runtime's own words — for the console and `detail` fields ONLY. */
|
|
29
|
+
raw: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** How much of a raw message we keep. Bounded: it reaches logs, and a hostile
|
|
33
|
+
* peer can influence some of what a fetch throws. */
|
|
34
|
+
const MAX_RAW_CHARS = 200;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Bun's connect-failure wording, as of Bun 1.x.
|
|
38
|
+
*
|
|
39
|
+
* MATCHED AS A FALLBACK, NOT AS THE PRIMARY SIGNAL. Error *names* are stable
|
|
40
|
+
* API; these sentences are not, and a Bun upgrade that rewords them must
|
|
41
|
+
* degrade this to `other` with a neutral sentence rather than start leaking the
|
|
42
|
+
* new wording. That is why `other` has a real message of its own instead of
|
|
43
|
+
* falling through to `raw`.
|
|
44
|
+
*/
|
|
45
|
+
const BUN_CONNECT_HINTS = [
|
|
46
|
+
'unable to connect',
|
|
47
|
+
'was there a typo in the url or port',
|
|
48
|
+
'connectionrefused',
|
|
49
|
+
'connection refused',
|
|
50
|
+
'failed to connect',
|
|
51
|
+
'econnrefused',
|
|
52
|
+
'enotfound',
|
|
53
|
+
'eai_again',
|
|
54
|
+
'dns',
|
|
55
|
+
'socket connection was closed unexpectedly',
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const BUN_TIMEOUT_HINTS = ['timeout', 'timed out', 'etimedout'];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Classify a caught `fetch` rejection.
|
|
62
|
+
*
|
|
63
|
+
* Total: every input produces a class and a sentence, including a non-Error
|
|
64
|
+
* throw. A classifier that can return "nothing" would just reintroduce the
|
|
65
|
+
* `err.message` fall-through it replaces.
|
|
66
|
+
*/
|
|
67
|
+
export function classifyTransportError(err: unknown): ClassifiedTransportError {
|
|
68
|
+
const raw = rawTextOf(err).slice(0, MAX_RAW_CHARS);
|
|
69
|
+
const name = typeof (err as { name?: unknown })?.name === 'string' ? (err as Error).name : '';
|
|
70
|
+
const haystack = `${name} ${raw}`.toLowerCase();
|
|
71
|
+
|
|
72
|
+
// Names first — they are the stable half of the contract.
|
|
73
|
+
if (name === 'TimeoutError' || (name === 'AbortError' && haystack.includes('timeout'))) {
|
|
74
|
+
return {
|
|
75
|
+
class: 'timeout',
|
|
76
|
+
text: 'The workspace took too long to answer',
|
|
77
|
+
raw,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (name === 'AbortError') {
|
|
81
|
+
return { class: 'aborted', text: 'Stopped before it finished', raw };
|
|
82
|
+
}
|
|
83
|
+
if (BUN_TIMEOUT_HINTS.some((h) => haystack.includes(h))) {
|
|
84
|
+
return { class: 'timeout', text: 'The workspace took too long to answer', raw };
|
|
85
|
+
}
|
|
86
|
+
if (BUN_CONNECT_HINTS.some((h) => haystack.includes(h))) {
|
|
87
|
+
return { class: 'unreachable', text: 'Could not reach the workspace', raw };
|
|
88
|
+
}
|
|
89
|
+
// NEUTRAL, never the raw string. An unrecognised failure is still a failure
|
|
90
|
+
// we own the wording of.
|
|
91
|
+
return { class: 'other', text: 'The transfer did not complete', raw };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The user-facing sentence alone — the common call site, since almost every
|
|
96
|
+
* `catch` wants exactly this and nothing else.
|
|
97
|
+
*/
|
|
98
|
+
export function transportErrorText(err: unknown): string {
|
|
99
|
+
return classifyTransportError(err).text;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function rawTextOf(err: unknown): string {
|
|
103
|
+
if (typeof err === 'string') return err;
|
|
104
|
+
const message = (err as { message?: unknown })?.message;
|
|
105
|
+
if (typeof message === 'string') return message;
|
|
106
|
+
try {
|
|
107
|
+
return String(err);
|
|
108
|
+
} catch {
|
|
109
|
+
return 'unknown error';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./whats-new.schema.json",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"id": "pdf-export-print-safe-text",
|
|
6
|
+
"version": "1.2.0",
|
|
7
|
+
"date": "2026-09-04",
|
|
8
|
+
"kind": "feature",
|
|
9
|
+
"title": "PDF exports a print shop will actually accept",
|
|
10
|
+
"summary": "A colour font or a faux-italic could come out of a PDF export in a form printers reject — the file looked perfect on screen, and you only found out when the print shop refused it. Maude now checks the fonts on every PDF export and warns you, by name, when one of them would fail preflight. Under Text you can also ask it to verify the fonts are properly embedded, or convert all text to curves so there is nothing left to break — bleed, crop marks and photo resolution all come through untouched.",
|
|
11
|
+
"surface": "design-ui"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "seed-progress-you-can-watch",
|
|
15
|
+
"version": "1.2.0",
|
|
16
|
+
"date": "2026-09-04",
|
|
17
|
+
"kind": "improvement",
|
|
18
|
+
"title": "You can watch a large project sync",
|
|
19
|
+
"summary": "Linking a big project used to be a black box: the Sync panel counted files it had moved but never how many there were, so \"1 412\" looked the same whether it was a quarter done or finished — and `maude design status` reported only canvases, so a project with hundreds of files still on their way could read as fully synced. Now every surface shows the same thing: how many of how many have arrived, what is waiting, and what is stuck and why. The panel has a progress bar, `maude design status` has a files line (and a `--watch` mode), and the desktop app puts the count in the window title and tells you once when it finishes. Files the workspace will never take — something over its size limit, or an hourly upload allowance that is used up — now say so by name instead of retrying forever.",
|
|
20
|
+
"surface": "design-ui"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "images-arrive-again",
|
|
24
|
+
"version": "1.2.0",
|
|
25
|
+
"date": "2026-09-04",
|
|
26
|
+
"kind": "fix",
|
|
27
|
+
"title": "Images arrive again on linked projects",
|
|
28
|
+
"summary": "On a project synced with a workspace, images and other project files could quietly never arrive — broken placeholders on the canvas while the Sync panel still said everything was synced. When a workspace asks Maude to slow down, it now waits as long as it was asked instead of retrying straight into the same wall, and the Sync panel says so in plain words: syncing is paused for a moment, nothing is lost, the rest arrives when the pause lifts.",
|
|
29
|
+
"surface": "design-ui"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "comments-stop-disappearing",
|
|
33
|
+
"version": "1.2.0",
|
|
34
|
+
"date": "2026-09-04",
|
|
35
|
+
"kind": "fix",
|
|
36
|
+
"title": "Comments stop disappearing",
|
|
37
|
+
"summary": "On a project linked to a hub, a comment you had just added could vanish a few seconds later, leaving only the first one on the canvas — with nothing to undo and no sign anything had gone wrong. A new comment now reaches the shared project before it is written to disk, and Maude will not overwrite a comment it has not seen yet, so what you write stays put.",
|
|
38
|
+
"surface": "design-ui"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "comments-stop-duplicating",
|
|
42
|
+
"version": "1.2.0",
|
|
43
|
+
"date": "2026-09-04",
|
|
44
|
+
"kind": "fix",
|
|
45
|
+
"title": "Comments stop duplicating, and resolve works",
|
|
46
|
+
"summary": "On a project linked to a hub, a comment could come back two, four, then eight times, and clicking resolve appeared to do nothing — every copy had to be dealt with separately, and new ones kept arriving. Maude now recognises the copies as one comment, so they collapse back to a single pin, and resolving or deleting one clears all of it. A project that already filled up with duplicates repairs itself the next time you open it.",
|
|
47
|
+
"surface": "design-ui"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "synced-canvases-stop-duplicating",
|
|
51
|
+
"version": "1.2.0",
|
|
52
|
+
"date": "2026-09-04",
|
|
53
|
+
"kind": "fix",
|
|
54
|
+
"title": "Synced canvases stop duplicating themselves",
|
|
55
|
+
"summary": "On a project linked to a hub, a canvas could come back with its whole contents pasted onto itself — twice, sometimes more — which broke the build and left the canvas unopenable. It happened when two people (or your machine and the hub) first picked up the same canvas at the same moment. Maude now recognises that exact shape and collapses it back to one copy, on the styles as well as the body. Moving a canvas into another folder also fixes up its imports instead of breaking them.",
|
|
56
|
+
"surface": "design-ui"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"date": "2026-09-04",
|
|
60
|
+
"id": "sync-recovers-after-the-workspace-sleeps",
|
|
61
|
+
"kind": "fix",
|
|
62
|
+
"summary": "If you left Maude alone for a while, your cloud workspace went to sleep and syncing quietly stopped — the indicator still said it was connected, and only pressing Resync got it moving again. Maude now notices when the connection has gone quiet, brings it back on its own, and only reports canvases as synced once they really are.",
|
|
63
|
+
"surface": "design-ui",
|
|
64
|
+
"title": "Sync comes back on its own",
|
|
65
|
+
"version": "1.2.0"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"date": "2026-09-04",
|
|
69
|
+
"id": "long-chats-no-longer-freeze",
|
|
70
|
+
"kind": "fix",
|
|
71
|
+
"summary": "In a project with a long chat history, Maude could slow to a crawl and stop responding for many minutes while using enormous amounts of memory — often needing a force-quit. Screenshots the agent took were being stored inside the chat history at full size, and every chat you opened re-read all of it. Chats now stay small and are read in fixed-size pieces, so a long history costs the same as a short one.",
|
|
72
|
+
"surface": "design-ui",
|
|
73
|
+
"title": "Long chats no longer freeze Maude",
|
|
74
|
+
"version": "1.2.0"
|
|
75
|
+
},
|
|
4
76
|
{
|
|
5
77
|
"id": "canvas-media-plays-on-linux",
|
|
6
78
|
"version": "1.0.10",
|
package/cli/bin/maude.mjs
CHANGED
|
@@ -14,6 +14,7 @@ const COMMANDS = {
|
|
|
14
14
|
init: () => import('../commands/init.mjs'),
|
|
15
15
|
config: () => import('../commands/config.mjs'),
|
|
16
16
|
cache: () => import('../commands/cache.mjs'),
|
|
17
|
+
codex: () => import('../commands/codex.mjs'),
|
|
17
18
|
preflight: () => import('../commands/preflight.mjs'),
|
|
18
19
|
design: () => import('../commands/design.mjs'),
|
|
19
20
|
'scenario-report': () => import('../commands/scenario-report.mjs'),
|
|
@@ -21,6 +22,7 @@ const COMMANDS = {
|
|
|
21
22
|
kg: () => import('../commands/kg.mjs'),
|
|
22
23
|
help: () => import('../commands/help.mjs'),
|
|
23
24
|
hub: () => import('../commands/hub.mjs'),
|
|
25
|
+
harness: () => import('../commands/harness.mjs'),
|
|
24
26
|
version: () => import('../commands/version.mjs'),
|
|
25
27
|
};
|
|
26
28
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { access, realpath } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { delimiter, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { parseArgs } from '../lib/argv.mjs';
|
|
7
|
+
import { assertCodexPermissionAuthority, syncCodexRuntime } from '../lib/harness/codex-runtime.mjs';
|
|
8
|
+
|
|
9
|
+
export async function run({ args, io = defaultIo() }) {
|
|
10
|
+
const syncOnly = args[0] === 'sync';
|
|
11
|
+
const { flags } = syncOnly ? parseArgs(args.slice(1), { booleans: ['json'] }) : { flags: {} };
|
|
12
|
+
const launchArgs = [...args];
|
|
13
|
+
if (!syncOnly) assertLocalCodexTarget(launchArgs);
|
|
14
|
+
let explicitExecutable = flags.real;
|
|
15
|
+
if (!syncOnly && launchArgs[0] === '--real') {
|
|
16
|
+
if (!launchArgs[1]) throw new Error('--real requires an executable path');
|
|
17
|
+
explicitExecutable = launchArgs[1];
|
|
18
|
+
launchArgs.splice(0, 2);
|
|
19
|
+
}
|
|
20
|
+
if (process.env.MAUDE_CODEX_BRIDGE_ACTIVE === '1') {
|
|
21
|
+
throw new Error(
|
|
22
|
+
'refusing recursive maude codex launch; set MAUDE_CODEX_REAL to the real binary'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
process.env.MAUDE_CODEX_BRIDGE_ACTIVE = '1';
|
|
26
|
+
const home = resolve(process.env.HOME || homedir());
|
|
27
|
+
const projectRoot = await realpath(
|
|
28
|
+
resolve(flags.project || codexProjectDirectory(launchArgs) || process.cwd())
|
|
29
|
+
);
|
|
30
|
+
const requestedCodexHome = resolve(process.env.CODEX_HOME || join(home, '.codex'));
|
|
31
|
+
if (isContained(requestedCodexHome, projectRoot)) {
|
|
32
|
+
throw new Error('CODEX_HOME must be outside the current project for maude codex');
|
|
33
|
+
}
|
|
34
|
+
const codexHome = await realpath(requestedCodexHome);
|
|
35
|
+
if (isContained(codexHome, projectRoot)) {
|
|
36
|
+
throw new Error('CODEX_HOME must resolve outside the current project for maude codex');
|
|
37
|
+
}
|
|
38
|
+
const executable = await resolveCodexExecutable(explicitExecutable);
|
|
39
|
+
const synced = await syncCodexRuntime({
|
|
40
|
+
codexExecutable: executable,
|
|
41
|
+
codexHome,
|
|
42
|
+
home,
|
|
43
|
+
projectRoot,
|
|
44
|
+
});
|
|
45
|
+
if (syncOnly) {
|
|
46
|
+
if (flags.json) io.stdout.write(`${JSON.stringify(synced.summary, null, 2)}\n`);
|
|
47
|
+
return synced.summary;
|
|
48
|
+
}
|
|
49
|
+
if (synced.permissionMode && hasCodexPermissionOverride(launchArgs)) {
|
|
50
|
+
throw new Error('refusing a Codex sandbox override that could disable projected Claude denies');
|
|
51
|
+
}
|
|
52
|
+
const canonicalLaunchArgs = canonicalizeCodexProjectArguments(launchArgs, projectRoot);
|
|
53
|
+
if (synced.permissionMode) {
|
|
54
|
+
await assertCodexPermissionAuthority({ codexHome, projectRoot });
|
|
55
|
+
}
|
|
56
|
+
const result = spawnSync(executable, [...synced.launchArguments, ...canonicalLaunchArgs], {
|
|
57
|
+
cwd: projectRoot,
|
|
58
|
+
env: {
|
|
59
|
+
...process.env,
|
|
60
|
+
...synced.launchEnvironment,
|
|
61
|
+
CODEX_HOME: codexHome,
|
|
62
|
+
MAUDE_CODEX_BRIDGE_ACTIVE: process.env.MAUDE_CODEX_BRIDGE_ACTIVE,
|
|
63
|
+
},
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
});
|
|
66
|
+
if (result.error) throw result.error;
|
|
67
|
+
process.exitCode = result.status ?? 1;
|
|
68
|
+
return { ...synced.summary, exitCode: process.exitCode };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function codexProjectDirectory(args) {
|
|
72
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
73
|
+
const argument = args[index];
|
|
74
|
+
if (argument === '--') return null;
|
|
75
|
+
if (argument === '-C' || argument === '--cd') return args[index + 1] ?? null;
|
|
76
|
+
if (argument.startsWith('-C')) return argument.slice(2) || null;
|
|
77
|
+
if (argument.startsWith('--cd=')) return argument.slice('--cd='.length) || null;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function hasCodexRemoteTarget(args) {
|
|
83
|
+
for (const argument of args) {
|
|
84
|
+
if (argument === '--') return false;
|
|
85
|
+
if (argument === '--remote' || argument.startsWith('--remote=')) return true;
|
|
86
|
+
if (argument === 'cloud') return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function assertLocalCodexTarget(args) {
|
|
92
|
+
if (hasCodexRemoteTarget(args)) {
|
|
93
|
+
throw new Error('refusing to synchronize local Claude state into a remote Codex session');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function canonicalizeCodexProjectArguments(args, projectRoot) {
|
|
98
|
+
const output = [];
|
|
99
|
+
let found = false;
|
|
100
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
101
|
+
const argument = args[index];
|
|
102
|
+
if (argument === '--')
|
|
103
|
+
return [...(found ? ['-C', projectRoot] : []), ...output, ...args.slice(index)];
|
|
104
|
+
if (argument === '-C' || argument === '--cd') {
|
|
105
|
+
if (found) throw new Error('multiple Codex working-directory overrides are not supported');
|
|
106
|
+
if (args[index + 1] === undefined) throw new Error(`${argument} requires a directory`);
|
|
107
|
+
found = true;
|
|
108
|
+
index += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (argument.startsWith('-C')) {
|
|
112
|
+
if (found) throw new Error('multiple Codex working-directory overrides are not supported');
|
|
113
|
+
if (!argument.slice(2)) throw new Error('-C requires a directory');
|
|
114
|
+
found = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (argument.startsWith('--cd=')) {
|
|
118
|
+
if (found) throw new Error('multiple Codex working-directory overrides are not supported');
|
|
119
|
+
if (!argument.slice('--cd='.length)) throw new Error('--cd requires a directory');
|
|
120
|
+
found = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
output.push(argument);
|
|
124
|
+
}
|
|
125
|
+
return [...(found ? ['-C', projectRoot] : []), ...output];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function hasCodexPermissionOverride(args) {
|
|
129
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
130
|
+
const argument = args[index];
|
|
131
|
+
if (argument === '--') return false;
|
|
132
|
+
if (
|
|
133
|
+
argument === '-s' ||
|
|
134
|
+
/^-s.+/.test(argument) ||
|
|
135
|
+
argument === '--sandbox' ||
|
|
136
|
+
argument.startsWith('--sandbox=') ||
|
|
137
|
+
argument === '--dangerously-bypass-approvals-and-sandbox' ||
|
|
138
|
+
argument === '--yolo' ||
|
|
139
|
+
argument === '--approve-for-me' ||
|
|
140
|
+
argument === '-p' ||
|
|
141
|
+
/^-p.+/.test(argument) ||
|
|
142
|
+
argument === '--profile' ||
|
|
143
|
+
argument.startsWith('--profile=') ||
|
|
144
|
+
argument === '-P' ||
|
|
145
|
+
/^-P.+/.test(argument) ||
|
|
146
|
+
argument === '--permission-profile' ||
|
|
147
|
+
argument.startsWith('--permission-profile=')
|
|
148
|
+
) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if (
|
|
152
|
+
argument === '-c' ||
|
|
153
|
+
/^-c.+/.test(argument) ||
|
|
154
|
+
argument === '--config' ||
|
|
155
|
+
argument.startsWith('--config=')
|
|
156
|
+
) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isContained(path, root) {
|
|
164
|
+
const rel = relative(root, path);
|
|
165
|
+
return rel === '' || (!rel.startsWith(`..${sep}`) && rel !== '..' && !isAbsolute(rel));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function resolveCodexExecutable(explicit) {
|
|
169
|
+
if (explicit) return await canonicalExecutable(explicit);
|
|
170
|
+
if (process.env.MAUDE_CODEX_REAL) return await canonicalExecutable(process.env.MAUDE_CODEX_REAL);
|
|
171
|
+
for (const root of String(process.env.PATH || '').split(delimiter)) {
|
|
172
|
+
if (!root) continue;
|
|
173
|
+
const candidate = join(root, 'codex');
|
|
174
|
+
try {
|
|
175
|
+
await access(candidate);
|
|
176
|
+
return await realpath(candidate);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (error.code !== 'ENOENT') throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
throw new Error('Codex executable not found; pass --real <path>');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function canonicalExecutable(path) {
|
|
185
|
+
await access(path);
|
|
186
|
+
return await realpath(path);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function defaultIo() {
|
|
190
|
+
return { stderr: process.stderr, stdout: process.stdout };
|
|
191
|
+
}
|