@volter/editor-core 0.5.57
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/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/** Byte-budget retention for editor-managed Gameplay Session artifacts. */
|
|
2
|
+
|
|
3
|
+
import { appendFile, lstat, readdir, rm, stat, unlink } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import {
|
|
6
|
+
GAMEPLAY_SESSION_CLIP_BUDGET_BYTES,
|
|
7
|
+
GAMEPLAY_SESSION_LOG_BUDGET_BYTES,
|
|
8
|
+
} from './support/play/session-record';
|
|
9
|
+
|
|
10
|
+
/** Structured logs are the durable Analytics source. At the current event
|
|
11
|
+
* density this keeps hundreds of substantial sessions, without allowing one
|
|
12
|
+
* project to grow forever. */
|
|
13
|
+
/** Automatically named session clips are working evidence, not keepsakes.
|
|
14
|
+
* Two GiB provides a useful history while making long recordings pay their
|
|
15
|
+
* actual disk cost. Explicitly named recordings are outside this budget. */
|
|
16
|
+
export {
|
|
17
|
+
GAMEPLAY_SESSION_CLIP_BUDGET_BYTES,
|
|
18
|
+
GAMEPLAY_SESSION_LOG_BUDGET_BYTES,
|
|
19
|
+
} from './support/play/session-record';
|
|
20
|
+
|
|
21
|
+
interface SizedFile {
|
|
22
|
+
readonly file: string;
|
|
23
|
+
readonly bytes: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function sizedFiles(directory: string, files: readonly string[]): Promise<SizedFile[]> {
|
|
27
|
+
const sized = await Promise.all(
|
|
28
|
+
files.map(async (file): Promise<SizedFile | null> => {
|
|
29
|
+
const info = await stat(join(directory, file)).catch(() => null);
|
|
30
|
+
return info?.isFile() ? { file, bytes: info.size } : null;
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
return sized.filter((value): value is SizedFile => value !== null);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function unlinkIfPresent(path: string): Promise<boolean> {
|
|
37
|
+
try {
|
|
38
|
+
await unlink(path);
|
|
39
|
+
return true;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return (error as NodeJS.ErrnoException).code === 'ENOENT';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function pathBytes(path: string): Promise<number> {
|
|
46
|
+
const info = await lstat(path).catch(() => null);
|
|
47
|
+
if (!info) return 0;
|
|
48
|
+
if (info.isFile()) return info.size;
|
|
49
|
+
if (!info.isDirectory()) return 0;
|
|
50
|
+
const children = await readdir(path).catch(() => []);
|
|
51
|
+
const sizes = await Promise.all(children.map((child) => pathBytes(join(path, child))));
|
|
52
|
+
return sizes.reduce((sum, bytes) => sum + bytes, 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function removeReplaySidecar(recordingsDir: string, stem: string): Promise<void> {
|
|
56
|
+
await rm(join(recordingsDir, `${stem}.replay`), { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Prune oldest durable records only when their combined bytes exceed the log
|
|
60
|
+
* allotment. The newest record is always retained, even if it alone is large. */
|
|
61
|
+
export async function pruneGameplaySessionLogs(
|
|
62
|
+
projectRoot: string,
|
|
63
|
+
budgetBytes = GAMEPLAY_SESSION_LOG_BUDGET_BYTES,
|
|
64
|
+
): Promise<string[]> {
|
|
65
|
+
const logsDir = join(projectRoot, 'logs');
|
|
66
|
+
const files = (await readdir(logsDir).catch(() => []))
|
|
67
|
+
.filter((file) => file.startsWith('play-') && file.endsWith('.jsonl'))
|
|
68
|
+
.sort();
|
|
69
|
+
const sized = await sizedFiles(logsDir, files);
|
|
70
|
+
let total = sized.reduce((sum, value) => sum + value.bytes, 0);
|
|
71
|
+
const removed: string[] = [];
|
|
72
|
+
for (const value of sized.slice(0, -1)) {
|
|
73
|
+
if (total <= budgetBytes) break;
|
|
74
|
+
const clipRemoved = await unlinkIfPresent(
|
|
75
|
+
join(projectRoot, '.vgai', 'recordings', `${value.file.slice(0, -'.jsonl'.length)}.webm`),
|
|
76
|
+
);
|
|
77
|
+
if (!clipRemoved) continue;
|
|
78
|
+
await removeReplaySidecar(
|
|
79
|
+
join(projectRoot, '.vgai', 'recordings'),
|
|
80
|
+
value.file.slice(0, -'.jsonl'.length),
|
|
81
|
+
);
|
|
82
|
+
const logRemoved = await unlinkIfPresent(join(logsDir, value.file));
|
|
83
|
+
if (!logRemoved) continue;
|
|
84
|
+
total -= value.bytes;
|
|
85
|
+
removed.push(value.file);
|
|
86
|
+
}
|
|
87
|
+
return removed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Prune only automatic clips paired by stem with a durable session log.
|
|
91
|
+
* Named recordings are caller-owned keepsakes and never enter this pool. The
|
|
92
|
+
* log remains when its optional clip is evicted, so Analytics history stays. */
|
|
93
|
+
export async function pruneGameplaySessionClips(
|
|
94
|
+
projectRoot: string,
|
|
95
|
+
budgetBytes = GAMEPLAY_SESSION_CLIP_BUDGET_BYTES,
|
|
96
|
+
): Promise<string[]> {
|
|
97
|
+
const logsDir = join(projectRoot, 'logs');
|
|
98
|
+
const recordingsDir = join(projectRoot, '.vgai', 'recordings');
|
|
99
|
+
const logStems = new Set(
|
|
100
|
+
(await readdir(logsDir).catch(() => []))
|
|
101
|
+
.filter((file) => file.startsWith('play-') && file.endsWith('.jsonl'))
|
|
102
|
+
.map((file) => file.slice(0, -'.jsonl'.length)),
|
|
103
|
+
);
|
|
104
|
+
const files = (await readdir(recordingsDir).catch(() => []))
|
|
105
|
+
.filter((file) => file.endsWith('.webm') && logStems.has(file.slice(0, -'.webm'.length)))
|
|
106
|
+
.sort();
|
|
107
|
+
const sized = await Promise.all(
|
|
108
|
+
(await sizedFiles(recordingsDir, files)).map(async (value) => ({
|
|
109
|
+
...value,
|
|
110
|
+
bytes:
|
|
111
|
+
value.bytes +
|
|
112
|
+
(await pathBytes(join(recordingsDir, `${value.file.slice(0, -'.webm'.length)}.replay`))),
|
|
113
|
+
})),
|
|
114
|
+
);
|
|
115
|
+
let total = sized.reduce((sum, value) => sum + value.bytes, 0);
|
|
116
|
+
const removed: string[] = [];
|
|
117
|
+
for (const value of sized.slice(0, -1)) {
|
|
118
|
+
if (total <= budgetBytes) break;
|
|
119
|
+
const logFile = join(logsDir, `${value.file.slice(0, -'.webm'.length)}.jsonl`);
|
|
120
|
+
const evictedAt = Date.now();
|
|
121
|
+
const recorded = await appendFile(
|
|
122
|
+
logFile,
|
|
123
|
+
`${JSON.stringify({
|
|
124
|
+
t: evictedAt,
|
|
125
|
+
level: 'info',
|
|
126
|
+
source: 'gameplay-recording',
|
|
127
|
+
msg: 'evicted',
|
|
128
|
+
meta: {
|
|
129
|
+
file: value.file,
|
|
130
|
+
bytes: value.bytes,
|
|
131
|
+
reason: 'managed-clip-byte-budget',
|
|
132
|
+
evictedAt,
|
|
133
|
+
},
|
|
134
|
+
})}\n`,
|
|
135
|
+
'utf-8',
|
|
136
|
+
)
|
|
137
|
+
.then(() => true)
|
|
138
|
+
.catch(() => false);
|
|
139
|
+
if (!recorded) continue;
|
|
140
|
+
const deleted = await unlink(join(recordingsDir, value.file))
|
|
141
|
+
.then(() => true)
|
|
142
|
+
.catch(() => false);
|
|
143
|
+
if (!deleted) continue;
|
|
144
|
+
await removeReplaySidecar(recordingsDir, value.file.slice(0, -'.webm'.length));
|
|
145
|
+
total -= value.bytes;
|
|
146
|
+
removed.push(value.file);
|
|
147
|
+
}
|
|
148
|
+
return removed;
|
|
149
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Gameplay Sessions, derived from the artifacts Play already owns.
|
|
3
|
+
*
|
|
4
|
+
* The JSONL log is the session record. Recording lifecycle entries inside that
|
|
5
|
+
* same record associate an optional WebM without a second manifest that can
|
|
6
|
+
* drift. A server restart reconstructs the complete catalog from disk.
|
|
7
|
+
*
|
|
8
|
+
* The PARSE RULES live in the browser-safe core
|
|
9
|
+
* (`@vgai/sdk`'s `play/session-record.ts`), and this module is the Node
|
|
10
|
+
* wiring: the stat-keyed cache, the directory walk, and the recording
|
|
11
|
+
* route/paths only a server has.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
15
|
+
import { basename, join } from 'node:path';
|
|
16
|
+
import {
|
|
17
|
+
type GameplaySessionRecord,
|
|
18
|
+
materializeGameplaySession,
|
|
19
|
+
type ParsedGameplaySession,
|
|
20
|
+
parsePlayLogText,
|
|
21
|
+
} from './support/play/session-record';
|
|
22
|
+
|
|
23
|
+
export type {
|
|
24
|
+
GameplaySessionRecord,
|
|
25
|
+
GameplaySessionRecording,
|
|
26
|
+
} from './support/play/session-record';
|
|
27
|
+
|
|
28
|
+
interface CachedGameplaySession {
|
|
29
|
+
readonly size: number;
|
|
30
|
+
readonly mtimeMs: number;
|
|
31
|
+
readonly parsed: ParsedGameplaySession | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sessionCache = new Map<string, CachedGameplaySession>();
|
|
35
|
+
|
|
36
|
+
async function parseGameplaySession(
|
|
37
|
+
projectRoot: string,
|
|
38
|
+
file: string,
|
|
39
|
+
): Promise<ParsedGameplaySession | null> {
|
|
40
|
+
const absolute = join(projectRoot, 'logs', file);
|
|
41
|
+
const fileStats = await stat(absolute).catch(() => null);
|
|
42
|
+
if (!fileStats) return null;
|
|
43
|
+
const cached = sessionCache.get(absolute);
|
|
44
|
+
if (cached && cached.size === fileStats.size && cached.mtimeMs === fileStats.mtimeMs) {
|
|
45
|
+
return cached.parsed;
|
|
46
|
+
}
|
|
47
|
+
let raw: string;
|
|
48
|
+
try {
|
|
49
|
+
raw = await readFile(absolute, 'utf-8');
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const parsed = parsePlayLogText(raw, {
|
|
54
|
+
file,
|
|
55
|
+
fallbackEndedAt: fileStats.mtimeMs,
|
|
56
|
+
recordingUrl: (sessionId) =>
|
|
57
|
+
`/__editor/gameplay-sessions/${encodeURIComponent(sessionId)}/recording`,
|
|
58
|
+
});
|
|
59
|
+
sessionCache.set(absolute, { size: fileStats.size, mtimeMs: fileStats.mtimeMs, parsed });
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function readGameplaySession(
|
|
64
|
+
projectRoot: string,
|
|
65
|
+
file: string,
|
|
66
|
+
activeLogFile: string | null,
|
|
67
|
+
): Promise<GameplaySessionRecord | null> {
|
|
68
|
+
const parsed = await parseGameplaySession(projectRoot, file);
|
|
69
|
+
return parsed
|
|
70
|
+
? materializeGameplaySession(parsed, {
|
|
71
|
+
activeLogBaseName: activeLogFile === null ? null : basename(activeLogFile),
|
|
72
|
+
now: Date.now(),
|
|
73
|
+
})
|
|
74
|
+
: null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function listGameplaySessions(
|
|
78
|
+
projectRoot: string,
|
|
79
|
+
activeLogFile: string | null,
|
|
80
|
+
): Promise<GameplaySessionRecord[]> {
|
|
81
|
+
const files = await readdir(join(projectRoot, 'logs')).catch(() => []);
|
|
82
|
+
const playFiles = files
|
|
83
|
+
.filter((file) => file.startsWith('play-') && file.endsWith('.jsonl'))
|
|
84
|
+
.sort()
|
|
85
|
+
.reverse();
|
|
86
|
+
const livePaths = new Set(playFiles.map((file) => join(projectRoot, 'logs', file)));
|
|
87
|
+
for (const cachedPath of sessionCache.keys()) {
|
|
88
|
+
if (!livePaths.has(cachedPath)) sessionCache.delete(cachedPath);
|
|
89
|
+
}
|
|
90
|
+
const sessions = await Promise.all(
|
|
91
|
+
playFiles.map((file) => readGameplaySession(projectRoot, file, activeLogFile)),
|
|
92
|
+
);
|
|
93
|
+
return sessions.filter((session): session is GameplaySessionRecord => session !== null);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Metadata-only catalog for the one-second rail poll. Entries are fetched
|
|
97
|
+
* only for the selected session, so twenty long runs never cross the wire on
|
|
98
|
+
* every live-edge update. */
|
|
99
|
+
export async function listGameplaySessionCatalog(
|
|
100
|
+
projectRoot: string,
|
|
101
|
+
activeLogFile: string | null,
|
|
102
|
+
): Promise<GameplaySessionRecord[]> {
|
|
103
|
+
const sessions = await listGameplaySessions(projectRoot, activeLogFile);
|
|
104
|
+
return sessions.map((session) => ({ ...session, entries: [] }));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function getGameplaySession(
|
|
108
|
+
projectRoot: string,
|
|
109
|
+
id: string,
|
|
110
|
+
activeLogFile: string | null,
|
|
111
|
+
): Promise<GameplaySessionRecord | null> {
|
|
112
|
+
if (basename(id) !== id || !id.startsWith('play-')) return null;
|
|
113
|
+
return readGameplaySession(projectRoot, `${id}.jsonl`, activeLogFile);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function gameplaySessionRecordingPath(
|
|
117
|
+
projectRoot: string,
|
|
118
|
+
session: GameplaySessionRecord,
|
|
119
|
+
): string | null {
|
|
120
|
+
const file = session.recording?.file;
|
|
121
|
+
if (!file || basename(file) !== file) return null;
|
|
122
|
+
return join(projectRoot, '.vgai', 'recordings', file);
|
|
123
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
type GenerationJob,
|
|
6
|
+
type GenerationJobDraft,
|
|
7
|
+
type GenerationJobsDocument,
|
|
8
|
+
GenerationJobsDocumentSchema,
|
|
9
|
+
type GenerationJobUpdate,
|
|
10
|
+
type GenerationToolContribution,
|
|
11
|
+
} from '@volter/editor-sdk/generations';
|
|
12
|
+
import { sanitizeRecordedValue } from './redact-secrets';
|
|
13
|
+
|
|
14
|
+
export const PROJECT_GENERATIONS_PATH = '.vgai/generations.json';
|
|
15
|
+
|
|
16
|
+
const projectQueues = new Map<string, Promise<unknown>>();
|
|
17
|
+
function emptyDocument(): GenerationJobsDocument {
|
|
18
|
+
return { version: 1, jobs: [] };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function readGenerationJobs(projectRoot: string): Promise<GenerationJobsDocument> {
|
|
22
|
+
try {
|
|
23
|
+
const value: unknown = JSON.parse(
|
|
24
|
+
await readFile(resolve(projectRoot, PROJECT_GENERATIONS_PATH), 'utf8'),
|
|
25
|
+
);
|
|
26
|
+
return GenerationJobsDocumentSchema.parse(value);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
29
|
+
return emptyDocument();
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function writeDocument(projectRoot: string, document: GenerationJobsDocument): Promise<void> {
|
|
36
|
+
const path = resolve(projectRoot, PROJECT_GENERATIONS_PATH);
|
|
37
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
38
|
+
await mkdir(dirname(path), { recursive: true });
|
|
39
|
+
await writeFile(
|
|
40
|
+
temporary,
|
|
41
|
+
`${JSON.stringify(GenerationJobsDocumentSchema.parse(document), null, 2)}\n`,
|
|
42
|
+
);
|
|
43
|
+
try {
|
|
44
|
+
await rename(temporary, path);
|
|
45
|
+
} finally {
|
|
46
|
+
await rm(temporary, { force: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function serialize<T>(projectRoot: string, work: () => Promise<T>): Promise<T> {
|
|
51
|
+
const prior = projectQueues.get(projectRoot) ?? Promise.resolve();
|
|
52
|
+
const next = prior.catch(() => undefined).then(work);
|
|
53
|
+
projectQueues.set(projectRoot, next);
|
|
54
|
+
const cleanup = () => {
|
|
55
|
+
if (projectQueues.get(projectRoot) === next) projectQueues.delete(projectRoot);
|
|
56
|
+
};
|
|
57
|
+
void next.then(cleanup, cleanup);
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function findJob(document: GenerationJobsDocument, update: GenerationJobUpdate): GenerationJob {
|
|
62
|
+
const job = document.jobs.find(
|
|
63
|
+
(candidate) =>
|
|
64
|
+
candidate.provider === update.provider && candidate.externalId === update.externalId,
|
|
65
|
+
);
|
|
66
|
+
if (!job) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`Generation provider ${JSON.stringify(update.provider)} updated unknown job ${JSON.stringify(update.externalId)}.`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return job;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function applyUpdate(job: GenerationJob, update: GenerationJobUpdate, now: string): GenerationJob {
|
|
75
|
+
const next = {
|
|
76
|
+
...job,
|
|
77
|
+
updatedAt: now,
|
|
78
|
+
...(update.status ? { status: update.status } : {}),
|
|
79
|
+
...(update.progress !== undefined ? { progress: update.progress } : {}),
|
|
80
|
+
...(update.providerStatus !== undefined ? { providerStatus: update.providerStatus } : {}),
|
|
81
|
+
...(update.queuePosition !== undefined ? { queuePosition: update.queuePosition } : {}),
|
|
82
|
+
...(update.message !== undefined ? { message: update.message } : {}),
|
|
83
|
+
...(update.billing ? { billing: update.billing } : {}),
|
|
84
|
+
...(update.cancel ? { cancel: update.cancel } : {}),
|
|
85
|
+
...(update.accept ? { accept: update.accept } : {}),
|
|
86
|
+
...(update.accepted
|
|
87
|
+
? {
|
|
88
|
+
acceptedAt: now,
|
|
89
|
+
provenanceOperationId: update.accepted.provenanceOperationId,
|
|
90
|
+
outputPaths: update.accepted.outputPaths,
|
|
91
|
+
}
|
|
92
|
+
: {}),
|
|
93
|
+
};
|
|
94
|
+
delete next.pollError;
|
|
95
|
+
delete next.pollErrorAt;
|
|
96
|
+
delete next.pollFailureCount;
|
|
97
|
+
delete next.nextPollAt;
|
|
98
|
+
if (update.accept === null) delete next.accept;
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function recordGenerationPollFailure(
|
|
103
|
+
projectRoot: string,
|
|
104
|
+
id: string,
|
|
105
|
+
failure: string | null,
|
|
106
|
+
): Promise<void> {
|
|
107
|
+
await serialize(projectRoot, async () => {
|
|
108
|
+
const document = await readGenerationJobs(projectRoot);
|
|
109
|
+
const job = document.jobs.find((candidate) => candidate.id === id);
|
|
110
|
+
if (!job) return;
|
|
111
|
+
const now = new Date().toISOString();
|
|
112
|
+
document.jobs = document.jobs.map((candidate) => {
|
|
113
|
+
if (candidate.id !== id) return candidate;
|
|
114
|
+
if (failure === null) {
|
|
115
|
+
const next = { ...candidate, updatedAt: now };
|
|
116
|
+
delete next.pollError;
|
|
117
|
+
delete next.pollErrorAt;
|
|
118
|
+
delete next.pollFailureCount;
|
|
119
|
+
delete next.nextPollAt;
|
|
120
|
+
return next;
|
|
121
|
+
}
|
|
122
|
+
const failureCount = (candidate.pollFailureCount ?? 0) + 1;
|
|
123
|
+
const backoffSeconds = Math.min(60, 5 * 2 ** Math.min(4, failureCount - 1));
|
|
124
|
+
return {
|
|
125
|
+
...candidate,
|
|
126
|
+
pollError: failure,
|
|
127
|
+
pollErrorAt: now,
|
|
128
|
+
pollFailureCount: failureCount,
|
|
129
|
+
nextPollAt: new Date(Date.now() + backoffSeconds * 1_000).toISOString(),
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
await writeDocument(projectRoot, document);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function applyGenerationContribution(
|
|
137
|
+
projectRoot: string,
|
|
138
|
+
contribution: GenerationToolContribution,
|
|
139
|
+
input: unknown,
|
|
140
|
+
result: unknown,
|
|
141
|
+
): Promise<GenerationJob> {
|
|
142
|
+
return serialize(projectRoot, async () => {
|
|
143
|
+
const document = await readGenerationJobs(projectRoot);
|
|
144
|
+
const now = new Date().toISOString();
|
|
145
|
+
let job: GenerationJob;
|
|
146
|
+
if (contribution.role === 'submit') {
|
|
147
|
+
const draft: GenerationJobDraft = contribution.toJob(input, result);
|
|
148
|
+
if (draft.provider !== contribution.provider) {
|
|
149
|
+
throw new Error('Generation contribution returned a different provider id.');
|
|
150
|
+
}
|
|
151
|
+
const existing = document.jobs.find(
|
|
152
|
+
(candidate) =>
|
|
153
|
+
candidate.provider === draft.provider && candidate.externalId === draft.externalId,
|
|
154
|
+
);
|
|
155
|
+
job = GenerationJobsDocumentSchema.shape.jobs.element.parse(
|
|
156
|
+
sanitizeRecordedValue({
|
|
157
|
+
...draft,
|
|
158
|
+
id: existing?.id ?? randomUUID(),
|
|
159
|
+
createdAt: existing?.createdAt ?? now,
|
|
160
|
+
updatedAt: now,
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
document.jobs = [job, ...document.jobs.filter((candidate) => candidate.id !== job.id)];
|
|
164
|
+
} else {
|
|
165
|
+
const update = contribution.toUpdate(input, result);
|
|
166
|
+
if (update.provider !== contribution.provider) {
|
|
167
|
+
throw new Error('Generation contribution returned a different provider id.');
|
|
168
|
+
}
|
|
169
|
+
job = applyUpdate(findJob(document, update), update, now);
|
|
170
|
+
document.jobs = document.jobs.map((candidate) => (candidate.id === job.id ? job : candidate));
|
|
171
|
+
}
|
|
172
|
+
await writeDocument(projectRoot, document);
|
|
173
|
+
return job;
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function forgetGenerationJob(projectRoot: string, id: string): Promise<boolean> {
|
|
178
|
+
return serialize(projectRoot, async () => {
|
|
179
|
+
const document = await readGenerationJobs(projectRoot);
|
|
180
|
+
const next = document.jobs.filter((job) => job.id !== id);
|
|
181
|
+
if (next.length === document.jobs.length) return false;
|
|
182
|
+
document.jobs = next;
|
|
183
|
+
await writeDocument(projectRoot, document);
|
|
184
|
+
return true;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function markGenerationJobRead(projectRoot: string, id: string): Promise<boolean> {
|
|
189
|
+
return serialize(projectRoot, async () => {
|
|
190
|
+
const document = await readGenerationJobs(projectRoot);
|
|
191
|
+
const job = document.jobs.find((candidate) => candidate.id === id);
|
|
192
|
+
if (!job || job.status === 'queued' || job.status === 'running') return false;
|
|
193
|
+
if (job.readAt && Date.parse(job.readAt) >= Date.parse(job.updatedAt)) return false;
|
|
194
|
+
const readAt = new Date().toISOString();
|
|
195
|
+
document.jobs = document.jobs.map((candidate) =>
|
|
196
|
+
candidate.id === id ? { ...candidate, readAt } : candidate,
|
|
197
|
+
);
|
|
198
|
+
await writeDocument(projectRoot, document);
|
|
199
|
+
return true;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { GenerationJob } from '@volter/editor-sdk/generations';
|
|
2
|
+
import type { EditorAccountService } from './account-service';
|
|
3
|
+
import { readGenerationJobs, recordGenerationPollFailure } from './generation-jobs';
|
|
4
|
+
import { reconcileAcceptedGenerationProvenance } from './project-output-writer';
|
|
5
|
+
import {
|
|
6
|
+
executeProjectTool,
|
|
7
|
+
type ProjectModuleLoader,
|
|
8
|
+
type ProjectToolExecution,
|
|
9
|
+
} from './project-tools';
|
|
10
|
+
|
|
11
|
+
export interface GenerationReconcileReport {
|
|
12
|
+
attempted: number;
|
|
13
|
+
succeeded: number;
|
|
14
|
+
failed: Array<{ id: string; message: string }>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function executionFailure(result: ProjectToolExecution): string | null {
|
|
18
|
+
return result.body.ok ? (result.body.generationWarning ?? null) : result.body.error.message;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function reconcileJob(
|
|
22
|
+
projectRoot: string,
|
|
23
|
+
loadModule: ProjectModuleLoader | undefined,
|
|
24
|
+
job: GenerationJob,
|
|
25
|
+
account?: EditorAccountService,
|
|
26
|
+
): Promise<string | null> {
|
|
27
|
+
try {
|
|
28
|
+
if (job.status === 'queued' || job.status === 'running') {
|
|
29
|
+
const result = await executeProjectTool({
|
|
30
|
+
account,
|
|
31
|
+
projectRoot,
|
|
32
|
+
loadModule,
|
|
33
|
+
name: job.poll.tool,
|
|
34
|
+
input: job.poll.input,
|
|
35
|
+
confirmed: false,
|
|
36
|
+
});
|
|
37
|
+
const failure = executionFailure(result);
|
|
38
|
+
if (failure) throw new Error(failure);
|
|
39
|
+
}
|
|
40
|
+
// Read again: polling or a concurrent explicit save may have completed it.
|
|
41
|
+
const current = (await readGenerationJobs(projectRoot)).jobs.find(
|
|
42
|
+
(entry) => entry.id === job.id,
|
|
43
|
+
);
|
|
44
|
+
if (current?.status === 'succeeded' && current.accept && !current.acceptedAt) {
|
|
45
|
+
const result = await executeProjectTool({
|
|
46
|
+
account,
|
|
47
|
+
projectRoot,
|
|
48
|
+
loadModule,
|
|
49
|
+
name: current.accept.tool,
|
|
50
|
+
input: current.accept.input,
|
|
51
|
+
confirmed: true,
|
|
52
|
+
});
|
|
53
|
+
const failure =
|
|
54
|
+
executionFailure(result) ??
|
|
55
|
+
(result.body.ok && !result.body.generation?.acceptedAt ? 'Output was not saved.' : null);
|
|
56
|
+
if (failure) throw new Error(`Saving output failed: ${failure}`);
|
|
57
|
+
}
|
|
58
|
+
await recordGenerationPollFailure(projectRoot, job.id, null);
|
|
59
|
+
return null;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
const failure = error instanceof Error ? error.message : String(error);
|
|
62
|
+
await recordGenerationPollFailure(projectRoot, job.id, failure);
|
|
63
|
+
return failure;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Server-owned, bounded reconciliation. Browser tabs observe this state and
|
|
68
|
+
* never multiply provider polls merely because several editors are open. */
|
|
69
|
+
async function reconcileProjectGenerationJobs(
|
|
70
|
+
projectRoot: string,
|
|
71
|
+
loadModule: ProjectModuleLoader | undefined,
|
|
72
|
+
concurrency = 3,
|
|
73
|
+
account?: EditorAccountService,
|
|
74
|
+
): Promise<GenerationReconcileReport> {
|
|
75
|
+
const allJobs = (await readGenerationJobs(projectRoot)).jobs;
|
|
76
|
+
const failed: GenerationReconcileReport['failed'] = [];
|
|
77
|
+
await Promise.all(
|
|
78
|
+
allJobs
|
|
79
|
+
.filter((job) => job.acceptedAt && job.provenanceOperationId)
|
|
80
|
+
.map(async (job) => {
|
|
81
|
+
try {
|
|
82
|
+
await reconcileAcceptedGenerationProvenance(projectRoot, job);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
failed.push({
|
|
85
|
+
id: job.id,
|
|
86
|
+
message: `Provenance settlement repair failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const jobs = allJobs.filter(
|
|
93
|
+
(job) =>
|
|
94
|
+
(job.status === 'queued' ||
|
|
95
|
+
job.status === 'running' ||
|
|
96
|
+
(job.status === 'succeeded' &&
|
|
97
|
+
job.accept &&
|
|
98
|
+
!job.acceptedAt &&
|
|
99
|
+
(job.pollFailureCount ?? 0) < 5)) &&
|
|
100
|
+
(!job.nextPollAt || Date.parse(job.nextPollAt) <= now),
|
|
101
|
+
);
|
|
102
|
+
let cursor = 0;
|
|
103
|
+
let succeeded = 0;
|
|
104
|
+
const worker = async () => {
|
|
105
|
+
while (cursor < jobs.length) {
|
|
106
|
+
const job = jobs[cursor++];
|
|
107
|
+
if (!job) continue;
|
|
108
|
+
const failure = await reconcileJob(projectRoot, loadModule, job, account);
|
|
109
|
+
if (failure) failed.push({ id: job.id, message: failure });
|
|
110
|
+
else succeeded++;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
await Promise.all(
|
|
114
|
+
Array.from({ length: Math.min(Math.max(1, concurrency), jobs.length) }, () => worker()),
|
|
115
|
+
);
|
|
116
|
+
return { attempted: jobs.length, succeeded, failed };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const inFlight = new Map<string, Promise<GenerationReconcileReport>>();
|
|
120
|
+
|
|
121
|
+
/** The timer and explicit refresh share the same poll/save pass. */
|
|
122
|
+
export function reconcileGenerationJobs(
|
|
123
|
+
projectRoot: string,
|
|
124
|
+
loadModule: ProjectModuleLoader | undefined,
|
|
125
|
+
concurrency = 3,
|
|
126
|
+
account?: EditorAccountService,
|
|
127
|
+
): Promise<GenerationReconcileReport> {
|
|
128
|
+
const pending = inFlight.get(projectRoot);
|
|
129
|
+
if (pending) return pending;
|
|
130
|
+
const work = reconcileProjectGenerationJobs(projectRoot, loadModule, concurrency, account);
|
|
131
|
+
inFlight.set(projectRoot, work);
|
|
132
|
+
const cleanup = () => {
|
|
133
|
+
if (inFlight.get(projectRoot) === work) inFlight.delete(projectRoot);
|
|
134
|
+
};
|
|
135
|
+
void work.then(cleanup, cleanup);
|
|
136
|
+
return work;
|
|
137
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import type { ProjectProviderExecution } from '@volter/editor-sdk/tools/types';
|
|
3
|
+
|
|
4
|
+
export const GENERATIVE_EXECUTION_RECORDER = Symbol.for('vgai.generative-execution-recorder.v1');
|
|
5
|
+
|
|
6
|
+
type ExecutionFacts = ProjectProviderExecution;
|
|
7
|
+
|
|
8
|
+
interface ExecutionCapture {
|
|
9
|
+
pending: ExecutionFacts[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const captures = new AsyncLocalStorage<ExecutionCapture>();
|
|
13
|
+
|
|
14
|
+
function sameNativeExecution(left: ExecutionFacts, right: ExecutionFacts): boolean {
|
|
15
|
+
if (left.mode !== right.mode || left.provider !== right.provider) return false;
|
|
16
|
+
if (left.requestId && right.requestId) return left.requestId === right.requestId;
|
|
17
|
+
if (left.taskId && right.taskId) return left.taskId === right.taskId;
|
|
18
|
+
if (left.managedJobId && right.managedJobId) return left.managedJobId === right.managedJobId;
|
|
19
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function validFacts(value: unknown): value is ExecutionFacts {
|
|
23
|
+
if (!value || typeof value !== 'object') return false;
|
|
24
|
+
const candidate = value as Partial<ExecutionFacts>;
|
|
25
|
+
return (
|
|
26
|
+
(candidate.mode === 'mock' || candidate.mode === 'direct' || candidate.mode === 'managed') &&
|
|
27
|
+
typeof candidate.provider === 'string' &&
|
|
28
|
+
candidate.provider.length > 0
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function installRecorder(): void {
|
|
33
|
+
const globals = globalThis as typeof globalThis & { [GENERATIVE_EXECUTION_RECORDER]?: unknown };
|
|
34
|
+
if (globals[GENERATIVE_EXECUTION_RECORDER] !== undefined) return;
|
|
35
|
+
globals[GENERATIVE_EXECUTION_RECORDER] = (facts: unknown) => {
|
|
36
|
+
if (!validFacts(facts)) return;
|
|
37
|
+
captures.getStore()?.pending.push({ ...facts });
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
installRecorder();
|
|
42
|
+
|
|
43
|
+
/** Runs one project tool with provider execution capture isolated across async work. */
|
|
44
|
+
export function runWithGenerativeExecutionCapture<T>(work: () => Promise<T>): Promise<T> {
|
|
45
|
+
return captures.run({ pending: [] }, work);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Consumes the ordered native provider execution chain associated with the next output commit. */
|
|
49
|
+
export function consumeGenerativeExecutions(): ExecutionFacts[] {
|
|
50
|
+
const capture = captures.getStore();
|
|
51
|
+
if (!capture || capture.pending.length === 0) return [];
|
|
52
|
+
const executions: ExecutionFacts[] = [];
|
|
53
|
+
for (const facts of capture.pending.splice(0)) {
|
|
54
|
+
const index = executions.findIndex((candidate) => sameNativeExecution(candidate, facts));
|
|
55
|
+
if (index === -1) executions.push(facts);
|
|
56
|
+
else executions[index] = { ...facts, ...executions[index] };
|
|
57
|
+
}
|
|
58
|
+
return executions;
|
|
59
|
+
}
|