@wenathlan/saddle 1.8.18 → 2.0.3
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/Dockerfile +1346 -0
- package/README.md +33 -12
- package/alternatives.ts +1218 -0
- package/biome.json +153 -0
- package/boards.json +1517 -0
- package/compute.ts +3144 -0
- package/cores.json +1801 -0
- package/dist/acquisition.d.ts +359 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +629 -0
- package/dist/acquisition.js.map +1 -0
- package/dist/alternatives.d.ts +509 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +951 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/automation.d.ts +426 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +561 -0
- package/dist/automation.js.map +1 -0
- package/dist/browser.d.ts +562 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +1057 -0
- package/dist/browser.js.map +1 -0
- package/dist/capacitor.config.d.ts +13 -0
- package/dist/capacitor.config.d.ts.map +1 -0
- package/dist/capacitor.config.js +26 -0
- package/dist/capacitor.config.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/communication.d.ts +192 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +418 -0
- package/dist/communication.js.map +1 -0
- package/dist/compute.d.ts +1161 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +2273 -0
- package/dist/compute.js.map +1 -0
- package/dist/distribution.d.ts +529 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +811 -0
- package/dist/distribution.js.map +1 -0
- package/dist/execution.d.ts +530 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +761 -0
- package/dist/execution.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/foundation.d.ts +176 -0
- package/dist/foundation.d.ts.map +1 -0
- package/dist/foundation.js +309 -0
- package/dist/foundation.js.map +1 -0
- package/dist/index.d.ts +733 -123
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1018 -123
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +236 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +236 -0
- package/dist/integration.js.map +1 -0
- package/dist/intelligence.d.ts +94 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +145 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/{isolation/contracts.d.ts → isolation.d.ts} +12 -1
- package/dist/isolation.d.ts.map +1 -0
- package/dist/{isolation/contracts.js → isolation.js} +13 -1
- package/dist/isolation.js.map +1 -0
- package/dist/media.d.ts +344 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +986 -0
- package/dist/media.js.map +1 -0
- package/dist/modes.d.ts +359 -0
- package/dist/modes.d.ts.map +1 -0
- package/dist/modes.js +134 -0
- package/dist/modes.js.map +1 -0
- package/dist/{library/public.d.ts → operations.d.ts} +15 -1
- package/dist/operations.d.ts.map +1 -0
- package/dist/{library/public.js → operations.js} +18 -7
- package/dist/operations.js.map +1 -0
- package/dist/orchestrator.d.ts +1339 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +4308 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/packager/targetcli.js +4 -22
- package/dist/performance.d.ts +416 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +827 -0
- package/dist/performance.js.map +1 -0
- package/dist/quantum.d.ts +810 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +2073 -0
- package/dist/quantum.js.map +1 -0
- package/dist/render.d.ts +381 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1073 -0
- package/dist/render.js.map +1 -0
- package/dist/scheduler.d.ts +596 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +1259 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/security.d.ts +501 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1170 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{server/node.js → server.js} +16 -2
- package/dist/server.js.map +1 -0
- package/dist/tiers.d.ts +1002 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +2165 -0
- package/dist/tiers.js.map +1 -0
- package/dist/virtual.d.ts +641 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1285 -0
- package/dist/virtual.js.map +1 -0
- package/dist/virtualcpu.d.ts +316 -0
- package/dist/virtualcpu.d.ts.map +1 -0
- package/dist/virtualcpu.js +1180 -0
- package/dist/virtualcpu.js.map +1 -0
- package/dist/virtualgpu.d.ts +345 -0
- package/dist/virtualgpu.d.ts.map +1 -0
- package/dist/virtualgpu.js +1000 -0
- package/dist/virtualgpu.js.map +1 -0
- package/dist/virtualization.d.ts +607 -0
- package/dist/virtualization.d.ts.map +1 -0
- package/dist/virtualization.js +1187 -0
- package/dist/virtualization.js.map +1 -0
- package/dist/virtualmemory.d.ts +818 -0
- package/dist/virtualmemory.d.ts.map +1 -0
- package/dist/virtualmemory.js +1796 -0
- package/dist/virtualmemory.js.map +1 -0
- package/dist/webscrape.d.ts +720 -0
- package/dist/webscrape.d.ts.map +1 -0
- package/dist/webscrape.js +2697 -0
- package/dist/webscrape.js.map +1 -0
- package/docker.config +769 -0
- package/docs/CONVERSA.txt +1523 -0
- package/docs/alternatives.md +499 -0
- package/docs/architecture-1.8.19.md +76 -0
- package/docs/architecture-1.8.19.mmd +33 -0
- package/docs/architecture-1.8.19.png +0 -0
- package/docs/architecture.md +355 -0
- package/docs/brancharchive-2026-08-18.md +18 -0
- package/docs/browser.md +7 -0
- package/docs/consolidation.md +70 -0
- package/docs/e2ugh-engine.md +528 -0
- package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
- package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
- package/docs/hardware.md +342 -0
- package/docs/optimization.md +126 -0
- package/docs/performance.md +499 -0
- package/docs/planning.1.8.18.md +94 -0
- package/docs/planning.1.8.19.md +49 -0
- package/docs/releasenotes-1.8.19.md +35 -0
- package/docs/research-1.8.19-virtual-browser.md +27 -0
- package/docs/security.md +454 -0
- package/docs/todo-1.8.18.md +12 -0
- package/docs/todo-1.8.19.md +241 -0
- package/docs/viability.md +777 -0
- package/docs/virtualization.md +335 -0
- package/docs/web-duplicatecleanup.md +9 -0
- package/docs/web-ideas.md +106 -0
- package/docs/workflowimprovements-2026-08-19.md +70 -0
- package/gpumonitor.cpp +1543 -0
- package/gpus.json +2408 -0
- package/index.ts +1478 -0
- package/media.ts +1337 -0
- package/mttg.config +1549 -0
- package/orchestrator.ts +5462 -0
- package/package.json +304 -56
- package/passage.config +1331 -0
- package/performance.ts +1049 -0
- package/processors.json +2217 -0
- package/qemu.config +1062 -0
- package/qemubridge.py +1340 -0
- package/quantum.ts +2393 -0
- package/render.ts +1325 -0
- package/scheduler.ts +1600 -0
- package/security.ts +1449 -0
- package/tiers.ts +2871 -0
- package/tsconfig.json +29 -0
- package/virtualcpu.ts +1273 -0
- package/virtualgpu.ts +1181 -0
- package/virtualhardware.c +1011 -0
- package/virtualhardware.json +739 -0
- package/virtualization.ts +1628 -0
- package/virtualizationcore.cpp +4635 -0
- package/virtualmemory.ts +2303 -0
- package/vm.config.json +1910 -0
- package/{extension → web/extension}/manifest.json +1 -1
- package/web/readme.md +382 -0
- package/web/tsconfig.json +25 -0
- package/dist/adapters/forge.d.ts +0 -17
- package/dist/adapters/forge.d.ts.map +0 -1
- package/dist/adapters/forge.js +0 -19
- package/dist/adapters/forge.js.map +0 -1
- package/dist/adapters/forgejo.d.ts +0 -49
- package/dist/adapters/forgejo.d.ts.map +0 -1
- package/dist/adapters/forgejo.js +0 -8
- package/dist/adapters/forgejo.js.map +0 -1
- package/dist/adapters/github.d.ts +0 -12
- package/dist/adapters/github.d.ts.map +0 -1
- package/dist/adapters/github.js +0 -20
- package/dist/adapters/github.js.map +0 -1
- package/dist/adapters/gitlab.d.ts +0 -17
- package/dist/adapters/gitlab.d.ts.map +0 -1
- package/dist/adapters/gitlab.js +0 -10
- package/dist/adapters/gitlab.js.map +0 -1
- package/dist/adapters/huggingface.d.ts +0 -17
- package/dist/adapters/huggingface.d.ts.map +0 -1
- package/dist/adapters/huggingface.js +0 -6
- package/dist/adapters/huggingface.js.map +0 -1
- package/dist/adapters/socket.d.ts +0 -11
- package/dist/adapters/socket.d.ts.map +0 -1
- package/dist/adapters/socket.js +0 -17
- package/dist/adapters/socket.js.map +0 -1
- package/dist/adapters/transport.d.ts +0 -7
- package/dist/adapters/transport.d.ts.map +0 -1
- package/dist/adapters/transport.js +0 -36
- package/dist/adapters/transport.js.map +0 -1
- package/dist/ai/chunk.d.ts +0 -2
- package/dist/ai/chunk.d.ts.map +0 -1
- package/dist/ai/chunk.js +0 -36
- package/dist/ai/chunk.js.map +0 -1
- package/dist/ai/llmstxt.d.ts +0 -6
- package/dist/ai/llmstxt.d.ts.map +0 -1
- package/dist/ai/llmstxt.js +0 -12
- package/dist/ai/llmstxt.js.map +0 -1
- package/dist/ai/provenance.d.ts +0 -22
- package/dist/ai/provenance.d.ts.map +0 -1
- package/dist/ai/provenance.js +0 -25
- package/dist/ai/provenance.js.map +0 -1
- package/dist/ai/rag.d.ts +0 -26
- package/dist/ai/rag.d.ts.map +0 -1
- package/dist/ai/rag.js +0 -22
- package/dist/ai/rag.js.map +0 -1
- package/dist/ai/tokens.d.ts +0 -15
- package/dist/ai/tokens.d.ts.map +0 -1
- package/dist/ai/tokens.js +0 -9
- package/dist/ai/tokens.js.map +0 -1
- package/dist/api/auth.d.ts +0 -16
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -19
- package/dist/api/auth.js.map +0 -1
- package/dist/api/contracts.d.ts +0 -28
- package/dist/api/contracts.d.ts.map +0 -1
- package/dist/api/contracts.js +0 -14
- package/dist/api/contracts.js.map +0 -1
- package/dist/api/control.d.ts +0 -10
- package/dist/api/control.d.ts.map +0 -1
- package/dist/api/control.js +0 -35
- package/dist/api/control.js.map +0 -1
- package/dist/api/http.d.ts +0 -7
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js +0 -12
- package/dist/api/http.js.map +0 -1
- package/dist/api/rate.d.ts +0 -28
- package/dist/api/rate.d.ts.map +0 -1
- package/dist/api/rate.js +0 -36
- package/dist/api/rate.js.map +0 -1
- package/dist/api/security.d.ts +0 -12
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -56
- package/dist/api/security.js.map +0 -1
- package/dist/api/service.d.ts +0 -5
- package/dist/api/service.d.ts.map +0 -1
- package/dist/api/service.js +0 -70
- package/dist/api/service.js.map +0 -1
- package/dist/apps/registry.d.ts +0 -33
- package/dist/apps/registry.d.ts.map +0 -1
- package/dist/apps/registry.js +0 -24
- package/dist/apps/registry.js.map +0 -1
- package/dist/binary/archive.d.ts +0 -27
- package/dist/binary/archive.d.ts.map +0 -1
- package/dist/binary/archive.js +0 -46
- package/dist/binary/archive.js.map +0 -1
- package/dist/binary/build.d.ts +0 -29
- package/dist/binary/build.d.ts.map +0 -1
- package/dist/binary/build.js +0 -17
- package/dist/binary/build.js.map +0 -1
- package/dist/binary/transform.d.ts +0 -70
- package/dist/binary/transform.d.ts.map +0 -1
- package/dist/binary/transform.js +0 -105
- package/dist/binary/transform.js.map +0 -1
- package/dist/bot/adapter.d.ts +0 -5
- package/dist/bot/adapter.d.ts.map +0 -1
- package/dist/bot/adapter.js +0 -11
- package/dist/bot/adapter.js.map +0 -1
- package/dist/bot/bot.d.ts +0 -28
- package/dist/bot/bot.d.ts.map +0 -1
- package/dist/bot/bot.js +0 -52
- package/dist/bot/bot.js.map +0 -1
- package/dist/bot/commands.d.ts +0 -8
- package/dist/bot/commands.d.ts.map +0 -1
- package/dist/bot/commands.js +0 -19
- package/dist/bot/commands.js.map +0 -1
- package/dist/bot/permissions.d.ts +0 -13
- package/dist/bot/permissions.d.ts.map +0 -1
- package/dist/bot/permissions.js +0 -16
- package/dist/bot/permissions.js.map +0 -1
- package/dist/browser/actions.d.ts +0 -33
- package/dist/browser/actions.d.ts.map +0 -1
- package/dist/browser/actions.js +0 -44
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/agent.d.ts +0 -21
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js +0 -13
- package/dist/browser/agent.js.map +0 -1
- package/dist/browser/context.d.ts +0 -43
- package/dist/browser/context.d.ts.map +0 -1
- package/dist/browser/context.js +0 -54
- package/dist/browser/context.js.map +0 -1
- package/dist/browser/fingerprint.d.ts +0 -4
- package/dist/browser/fingerprint.d.ts.map +0 -1
- package/dist/browser/fingerprint.js +0 -12
- package/dist/browser/fingerprint.js.map +0 -1
- package/dist/browser/index.d.ts +0 -11
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -11
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/playwright.d.ts +0 -11
- package/dist/browser/playwright.d.ts.map +0 -1
- package/dist/browser/playwright.js +0 -24
- package/dist/browser/playwright.js.map +0 -1
- package/dist/browser/recorder.d.ts +0 -21
- package/dist/browser/recorder.d.ts.map +0 -1
- package/dist/browser/recorder.js +0 -58
- package/dist/browser/recorder.js.map +0 -1
- package/dist/browser/session.d.ts +0 -14
- package/dist/browser/session.d.ts.map +0 -1
- package/dist/browser/session.js +0 -21
- package/dist/browser/session.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -69
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -133
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/captcha/contract.d.ts +0 -35
- package/dist/captcha/contract.d.ts.map +0 -1
- package/dist/captcha/contract.js +0 -17
- package/dist/captcha/contract.js.map +0 -1
- package/dist/captcha/evidence.d.ts +0 -10
- package/dist/captcha/evidence.d.ts.map +0 -1
- package/dist/captcha/evidence.js +0 -9
- package/dist/captcha/evidence.js.map +0 -1
- package/dist/captcha/guard.d.ts +0 -23
- package/dist/captcha/guard.d.ts.map +0 -1
- package/dist/captcha/guard.js +0 -12
- package/dist/captcha/guard.js.map +0 -1
- package/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -46
- package/dist/cli/main.js.map +0 -1
- package/dist/core/errors.d.ts +0 -84
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -54
- package/dist/core/errors.js.map +0 -1
- package/dist/core/events.d.ts +0 -9
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/events.js.map +0 -1
- package/dist/core/hash.d.ts +0 -10
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -82
- package/dist/core/hash.js.map +0 -1
- package/dist/core/ids.d.ts +0 -10
- package/dist/core/ids.d.ts.map +0 -1
- package/dist/core/ids.js +0 -15
- package/dist/core/ids.js.map +0 -1
- package/dist/delivery/manifest.d.ts +0 -35
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/manifest.js +0 -68
- package/dist/delivery/manifest.js.map +0 -1
- package/dist/deploy/index.d.ts +0 -6
- package/dist/deploy/index.d.ts.map +0 -1
- package/dist/deploy/index.js +0 -6
- package/dist/deploy/index.js.map +0 -1
- package/dist/dispatch/resumable.d.ts +0 -102
- package/dist/dispatch/resumable.d.ts.map +0 -1
- package/dist/dispatch/resumable.js +0 -64
- package/dist/dispatch/resumable.js.map +0 -1
- package/dist/dispatch/workflow.d.ts +0 -6
- package/dist/dispatch/workflow.d.ts.map +0 -1
- package/dist/dispatch/workflow.js +0 -37
- package/dist/dispatch/workflow.js.map +0 -1
- package/dist/domain/artifacts.d.ts +0 -12
- package/dist/domain/artifacts.d.ts.map +0 -1
- package/dist/domain/artifacts.js +0 -14
- package/dist/domain/artifacts.js.map +0 -1
- package/dist/domain/jobs.d.ts +0 -12
- package/dist/domain/jobs.d.ts.map +0 -1
- package/dist/domain/jobs.js +0 -20
- package/dist/domain/jobs.js.map +0 -1
- package/dist/domain/providers.d.ts +0 -10
- package/dist/domain/providers.d.ts.map +0 -1
- package/dist/domain/providers.js +0 -8
- package/dist/domain/providers.js.map +0 -1
- package/dist/domain/runtime.d.ts +0 -14
- package/dist/domain/runtime.d.ts.map +0 -1
- package/dist/domain/runtime.js +0 -10
- package/dist/domain/runtime.js.map +0 -1
- package/dist/domain/sessions.d.ts +0 -12
- package/dist/domain/sessions.d.ts.map +0 -1
- package/dist/domain/sessions.js +0 -57
- package/dist/domain/sessions.js.map +0 -1
- package/dist/examples/localjob.d.ts +0 -2
- package/dist/examples/localjob.d.ts.map +0 -1
- package/dist/examples/localjob.js.map +0 -1
- package/dist/examples/publicapi.d.ts +0 -2
- package/dist/examples/publicapi.d.ts.map +0 -1
- package/dist/examples/publicapi.js.map +0 -1
- package/dist/extension/build.d.ts +0 -9
- package/dist/extension/build.d.ts.map +0 -1
- package/dist/extension/build.js +0 -85
- package/dist/extension/build.js.map +0 -1
- package/dist/extension/content.d.ts +0 -6
- package/dist/extension/content.d.ts.map +0 -1
- package/dist/extension/content.js +0 -135
- package/dist/extension/content.js.map +0 -1
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js +0 -7
- package/dist/extension/index.js.map +0 -1
- package/dist/extension/pagebridge.d.ts +0 -6
- package/dist/extension/pagebridge.d.ts.map +0 -1
- package/dist/extension/pagebridge.js +0 -42
- package/dist/extension/pagebridge.js.map +0 -1
- package/dist/extension/permissions.d.ts +0 -26
- package/dist/extension/permissions.d.ts.map +0 -1
- package/dist/extension/permissions.js +0 -28
- package/dist/extension/permissions.js.map +0 -1
- package/dist/extension/popup.d.ts +0 -5
- package/dist/extension/popup.d.ts.map +0 -1
- package/dist/extension/popup.js +0 -26
- package/dist/extension/popup.js.map +0 -1
- package/dist/extension/protocol.d.ts +0 -58
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js +0 -101
- package/dist/extension/protocol.js.map +0 -1
- package/dist/extension/serviceworker.d.ts +0 -25
- package/dist/extension/serviceworker.d.ts.map +0 -1
- package/dist/extension/serviceworker.js +0 -102
- package/dist/extension/serviceworker.js.map +0 -1
- package/dist/extension/worker.d.ts +0 -28
- package/dist/extension/worker.d.ts.map +0 -1
- package/dist/extension/worker.js +0 -22
- package/dist/extension/worker.js.map +0 -1
- package/dist/format/check.d.ts +0 -8
- package/dist/format/check.d.ts.map +0 -1
- package/dist/format/check.js +0 -38
- package/dist/format/check.js.map +0 -1
- package/dist/isolation/contracts.d.ts.map +0 -1
- package/dist/isolation/contracts.js.map +0 -1
- package/dist/library/public.d.ts.map +0 -1
- package/dist/library/public.js.map +0 -1
- package/dist/mcp/browser.d.ts +0 -9
- package/dist/mcp/browser.d.ts.map +0 -1
- package/dist/mcp/browser.js +0 -13
- package/dist/mcp/browser.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -35
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -40
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/transport.d.ts +0 -7
- package/dist/mcp/transport.d.ts.map +0 -1
- package/dist/mcp/transport.js +0 -17
- package/dist/mcp/transport.js.map +0 -1
- package/dist/memory/bridge.d.ts +0 -14
- package/dist/memory/bridge.d.ts.map +0 -1
- package/dist/memory/bridge.js +0 -16
- package/dist/memory/bridge.js.map +0 -1
- package/dist/memory/engine.d.ts +0 -38
- package/dist/memory/engine.d.ts.map +0 -1
- package/dist/memory/engine.js +0 -109
- package/dist/memory/engine.js.map +0 -1
- package/dist/memory/modes.d.ts +0 -43
- package/dist/memory/modes.d.ts.map +0 -1
- package/dist/memory/modes.js +0 -66
- package/dist/memory/modes.js.map +0 -1
- package/dist/memory/objects.d.ts +0 -21
- package/dist/memory/objects.d.ts.map +0 -1
- package/dist/memory/objects.js +0 -20
- package/dist/memory/objects.js.map +0 -1
- package/dist/memory/planner.d.ts +0 -66
- package/dist/memory/planner.d.ts.map +0 -1
- package/dist/memory/planner.js +0 -108
- package/dist/memory/planner.js.map +0 -1
- package/dist/memory/targets.d.ts +0 -78
- package/dist/memory/targets.d.ts.map +0 -1
- package/dist/memory/targets.js +0 -29
- package/dist/memory/targets.js.map +0 -1
- package/dist/memory/transforms.d.ts +0 -19
- package/dist/memory/transforms.d.ts.map +0 -1
- package/dist/memory/transforms.js +0 -14
- package/dist/memory/transforms.js.map +0 -1
- package/dist/modes/matrix.d.ts +0 -23
- package/dist/modes/matrix.d.ts.map +0 -1
- package/dist/modes/matrix.js +0 -18
- package/dist/modes/matrix.js.map +0 -1
- package/dist/modes/modes.d.ts +0 -33
- package/dist/modes/modes.d.ts.map +0 -1
- package/dist/modes/modes.js +0 -15
- package/dist/modes/modes.js.map +0 -1
- package/dist/modes/resolve.d.ts +0 -96
- package/dist/modes/resolve.d.ts.map +0 -1
- package/dist/modes/resolve.js +0 -48
- package/dist/modes/resolve.js.map +0 -1
- package/dist/observability/metrics.d.ts +0 -14
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js +0 -18
- package/dist/observability/metrics.js.map +0 -1
- package/dist/packager/manifest.d.ts +0 -66
- package/dist/packager/manifest.d.ts.map +0 -1
- package/dist/packager/manifest.js +0 -94
- package/dist/packager/manifest.js.map +0 -1
- package/dist/packager/publish.d.ts +0 -51
- package/dist/packager/publish.d.ts.map +0 -1
- package/dist/packager/publish.js +0 -18
- package/dist/packager/publish.js.map +0 -1
- package/dist/packager/targetcli.d.ts +0 -7
- package/dist/packager/targetcli.d.ts.map +0 -1
- package/dist/packager/targetcli.js.map +0 -1
- package/dist/persistence/adapter.d.ts +0 -5
- package/dist/persistence/adapter.d.ts.map +0 -1
- package/dist/persistence/adapter.js +0 -11
- package/dist/persistence/adapter.js.map +0 -1
- package/dist/persistence/drizzle.d.ts +0 -2
- package/dist/persistence/drizzle.d.ts.map +0 -1
- package/dist/persistence/drizzle.js +0 -12
- package/dist/persistence/drizzle.js.map +0 -1
- package/dist/persistence/memory.d.ts +0 -2
- package/dist/persistence/memory.d.ts.map +0 -1
- package/dist/persistence/memory.js +0 -27
- package/dist/persistence/memory.js.map +0 -1
- package/dist/persistence/migrations.d.ts +0 -19
- package/dist/persistence/migrations.d.ts.map +0 -1
- package/dist/persistence/migrations.js +0 -13
- package/dist/persistence/migrations.js.map +0 -1
- package/dist/persistence/prisma.d.ts +0 -2
- package/dist/persistence/prisma.d.ts.map +0 -1
- package/dist/persistence/prisma.js +0 -25
- package/dist/persistence/prisma.js.map +0 -1
- package/dist/persistence/schema.d.ts +0 -63
- package/dist/persistence/schema.d.ts.map +0 -1
- package/dist/persistence/schema.js +0 -28
- package/dist/persistence/schema.js.map +0 -1
- package/dist/persistence/sql.d.ts +0 -3
- package/dist/persistence/sql.d.ts.map +0 -1
- package/dist/persistence/sql.js +0 -37
- package/dist/persistence/sql.js.map +0 -1
- package/dist/protocol/blocks.d.ts +0 -10
- package/dist/protocol/blocks.d.ts.map +0 -1
- package/dist/protocol/blocks.js +0 -26
- package/dist/protocol/blocks.js.map +0 -1
- package/dist/protocol/json.d.ts +0 -6
- package/dist/protocol/json.d.ts.map +0 -1
- package/dist/protocol/json.js +0 -6
- package/dist/protocol/json.js.map +0 -1
- package/dist/protocol/ndjson.d.ts +0 -3
- package/dist/protocol/ndjson.d.ts.map +0 -1
- package/dist/protocol/ndjson.js +0 -20
- package/dist/protocol/ndjson.js.map +0 -1
- package/dist/protocol/sse.d.ts +0 -7
- package/dist/protocol/sse.d.ts.map +0 -1
- package/dist/protocol/sse.js +0 -25
- package/dist/protocol/sse.js.map +0 -1
- package/dist/proxy/pool.d.ts +0 -9
- package/dist/proxy/pool.d.ts.map +0 -1
- package/dist/proxy/pool.js +0 -27
- package/dist/proxy/pool.js.map +0 -1
- package/dist/queue/idempotency.d.ts +0 -10
- package/dist/queue/idempotency.d.ts.map +0 -1
- package/dist/queue/idempotency.js +0 -13
- package/dist/queue/idempotency.js.map +0 -1
- package/dist/queue/persistent.d.ts +0 -14
- package/dist/queue/persistent.d.ts.map +0 -1
- package/dist/queue/persistent.js +0 -66
- package/dist/queue/persistent.js.map +0 -1
- package/dist/queue/queue.d.ts +0 -7
- package/dist/queue/queue.d.ts.map +0 -1
- package/dist/queue/queue.js +0 -55
- package/dist/queue/queue.js.map +0 -1
- package/dist/queue/saga.d.ts +0 -5
- package/dist/queue/saga.d.ts.map +0 -1
- package/dist/queue/saga.js +0 -20
- package/dist/queue/saga.js.map +0 -1
- package/dist/release/assets.d.ts +0 -144
- package/dist/release/assets.d.ts.map +0 -1
- package/dist/release/assets.js +0 -157
- package/dist/release/assets.js.map +0 -1
- package/dist/release/evidence.d.ts +0 -92
- package/dist/release/evidence.d.ts.map +0 -1
- package/dist/release/evidence.js +0 -148
- package/dist/release/evidence.js.map +0 -1
- package/dist/release/verify.d.ts +0 -12
- package/dist/release/verify.d.ts.map +0 -1
- package/dist/release/verify.js +0 -112
- package/dist/release/verify.js.map +0 -1
- package/dist/runners/chain.d.ts +0 -125
- package/dist/runners/chain.d.ts.map +0 -1
- package/dist/runners/chain.js +0 -95
- package/dist/runners/chain.js.map +0 -1
- package/dist/runners/health.d.ts +0 -36
- package/dist/runners/health.d.ts.map +0 -1
- package/dist/runners/health.js +0 -26
- package/dist/runners/health.js.map +0 -1
- package/dist/runners/heartbeat.d.ts +0 -23
- package/dist/runners/heartbeat.d.ts.map +0 -1
- package/dist/runners/heartbeat.js +0 -29
- package/dist/runners/heartbeat.js.map +0 -1
- package/dist/runners/inprocess.d.ts +0 -17
- package/dist/runners/inprocess.d.ts.map +0 -1
- package/dist/runners/inprocess.js +0 -20
- package/dist/runners/inprocess.js.map +0 -1
- package/dist/runners/scheduler.d.ts +0 -5
- package/dist/runners/scheduler.d.ts.map +0 -1
- package/dist/runners/scheduler.js +0 -19
- package/dist/runners/scheduler.js.map +0 -1
- package/dist/runtime/abort.d.ts +0 -8
- package/dist/runtime/abort.d.ts.map +0 -1
- package/dist/runtime/abort.js +0 -13
- package/dist/runtime/abort.js.map +0 -1
- package/dist/runtime/compatibility.d.ts +0 -37
- package/dist/runtime/compatibility.d.ts.map +0 -1
- package/dist/runtime/compatibility.js +0 -10
- package/dist/runtime/compatibility.js.map +0 -1
- package/dist/runtime/detect.d.ts +0 -13
- package/dist/runtime/detect.d.ts.map +0 -1
- package/dist/runtime/detect.js +0 -18
- package/dist/runtime/detect.js.map +0 -1
- package/dist/runtime/engine.d.ts +0 -20
- package/dist/runtime/engine.d.ts.map +0 -1
- package/dist/runtime/engine.js +0 -61
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/retry.d.ts +0 -19
- package/dist/runtime/retry.d.ts.map +0 -1
- package/dist/runtime/retry.js +0 -54
- package/dist/runtime/retry.js.map +0 -1
- package/dist/runtime/worker.d.ts +0 -10
- package/dist/runtime/worker.d.ts.map +0 -1
- package/dist/runtime/worker.js +0 -25
- package/dist/runtime/worker.js.map +0 -1
- package/dist/scrape/cache.d.ts +0 -45
- package/dist/scrape/cache.d.ts.map +0 -1
- package/dist/scrape/cache.js +0 -45
- package/dist/scrape/cache.js.map +0 -1
- package/dist/scrape/crawl.d.ts +0 -47
- package/dist/scrape/crawl.d.ts.map +0 -1
- package/dist/scrape/crawl.js +0 -114
- package/dist/scrape/crawl.js.map +0 -1
- package/dist/scrape/extract.d.ts +0 -63
- package/dist/scrape/extract.d.ts.map +0 -1
- package/dist/scrape/extract.js +0 -47
- package/dist/scrape/extract.js.map +0 -1
- package/dist/scrape/normalize.d.ts +0 -52
- package/dist/scrape/normalize.d.ts.map +0 -1
- package/dist/scrape/normalize.js +0 -102
- package/dist/scrape/normalize.js.map +0 -1
- package/dist/scrape/robots.d.ts +0 -37
- package/dist/scrape/robots.d.ts.map +0 -1
- package/dist/scrape/robots.js +0 -71
- package/dist/scrape/robots.js.map +0 -1
- package/dist/scrape/schema.d.ts +0 -23
- package/dist/scrape/schema.d.ts.map +0 -1
- package/dist/scrape/schema.js +0 -92
- package/dist/scrape/schema.js.map +0 -1
- package/dist/scrape/scraper.d.ts +0 -6
- package/dist/scrape/scraper.d.ts.map +0 -1
- package/dist/scrape/scraper.js +0 -46
- package/dist/scrape/scraper.js.map +0 -1
- package/dist/scrape/semantic.d.ts +0 -25
- package/dist/scrape/semantic.d.ts.map +0 -1
- package/dist/scrape/semantic.js +0 -27
- package/dist/scrape/semantic.js.map +0 -1
- package/dist/server/node.d.ts +0 -7
- package/dist/server/node.d.ts.map +0 -1
- package/dist/server/node.js.map +0 -1
- package/dist/sessions/file.d.ts +0 -25
- package/dist/sessions/file.d.ts.map +0 -1
- package/dist/sessions/file.js +0 -13
- package/dist/sessions/file.js.map +0 -1
- package/dist/sessions/replay.d.ts +0 -9
- package/dist/sessions/replay.d.ts.map +0 -1
- package/dist/sessions/replay.js +0 -70
- package/dist/sessions/replay.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -25
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -13
- package/dist/sessions/store.js.map +0 -1
- package/dist/storage/adapter.d.ts +0 -5
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js +0 -11
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/cache.d.ts +0 -23
- package/dist/storage/cache.d.ts.map +0 -1
- package/dist/storage/cache.js +0 -79
- package/dist/storage/cache.js.map +0 -1
- package/dist/storage/checksum.d.ts +0 -3
- package/dist/storage/checksum.d.ts.map +0 -1
- package/dist/storage/checksum.js +0 -19
- package/dist/storage/checksum.js.map +0 -1
- package/dist/storage/chunked.d.ts +0 -21
- package/dist/storage/chunked.d.ts.map +0 -1
- package/dist/storage/chunked.js +0 -67
- package/dist/storage/chunked.js.map +0 -1
- package/dist/storage/content.d.ts +0 -24
- package/dist/storage/content.d.ts.map +0 -1
- package/dist/storage/content.js +0 -44
- package/dist/storage/content.js.map +0 -1
- package/dist/storage/filehosting.d.ts +0 -2
- package/dist/storage/filehosting.d.ts.map +0 -1
- package/dist/storage/filehosting.js +0 -24
- package/dist/storage/filehosting.js.map +0 -1
- package/dist/storage/githubcontents.d.ts +0 -2
- package/dist/storage/githubcontents.d.ts.map +0 -1
- package/dist/storage/githubcontents.js +0 -29
- package/dist/storage/githubcontents.js.map +0 -1
- package/dist/storage/index.d.ts +0 -12
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -12
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/local.d.ts +0 -2
- package/dist/storage/local.d.ts.map +0 -1
- package/dist/storage/local.js +0 -72
- package/dist/storage/local.js.map +0 -1
- package/dist/storage/memory.d.ts +0 -6
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js +0 -36
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/pool.d.ts +0 -95
- package/dist/storage/pool.d.ts.map +0 -1
- package/dist/storage/pool.js +0 -202
- package/dist/storage/pool.js.map +0 -1
- package/dist/storage/s3compatible.d.ts +0 -3
- package/dist/storage/s3compatible.d.ts.map +0 -1
- package/dist/storage/s3compatible.js +0 -70
- package/dist/storage/s3compatible.js.map +0 -1
- package/dist/storage/sync.d.ts +0 -47
- package/dist/storage/sync.d.ts.map +0 -1
- package/dist/storage/sync.js +0 -75
- package/dist/storage/sync.js.map +0 -1
- package/dist/surfaces/adapters.d.ts +0 -97
- package/dist/surfaces/adapters.d.ts.map +0 -1
- package/dist/surfaces/adapters.js +0 -50
- package/dist/surfaces/adapters.js.map +0 -1
- package/dist/surfaces/controls.d.ts +0 -18
- package/dist/surfaces/controls.d.ts.map +0 -1
- package/dist/surfaces/controls.js +0 -45
- package/dist/surfaces/controls.js.map +0 -1
- package/dist/surfaces/manifest.d.ts +0 -68
- package/dist/surfaces/manifest.d.ts.map +0 -1
- package/dist/surfaces/manifest.js +0 -42
- package/dist/surfaces/manifest.js.map +0 -1
- package/dist/surfaces/n8n.d.ts +0 -25
- package/dist/surfaces/n8n.d.ts.map +0 -1
- package/dist/surfaces/n8n.js +0 -31
- package/dist/surfaces/n8n.js.map +0 -1
- package/dist/surfaces/operations.d.ts +0 -32
- package/dist/surfaces/operations.d.ts.map +0 -1
- package/dist/surfaces/operations.js +0 -53
- package/dist/surfaces/operations.js.map +0 -1
- package/dist/surfaces/requirements.d.ts +0 -29
- package/dist/surfaces/requirements.d.ts.map +0 -1
- package/dist/surfaces/requirements.js +0 -50
- package/dist/surfaces/requirements.js.map +0 -1
- package/dist/surfaces/targets.d.ts +0 -196
- package/dist/surfaces/targets.d.ts.map +0 -1
- package/dist/surfaces/targets.js +0 -27
- package/dist/surfaces/targets.js.map +0 -1
- package/dist/webhook/delivery.d.ts +0 -18
- package/dist/webhook/delivery.d.ts.map +0 -1
- package/dist/webhook/delivery.js +0 -36
- package/dist/webhook/delivery.js.map +0 -1
- package/dist/webhook/receiver.d.ts +0 -26
- package/dist/webhook/receiver.d.ts.map +0 -1
- package/dist/webhook/receiver.js +0 -24
- package/dist/webhook/receiver.js.map +0 -1
- package/dist/webhook/signature.d.ts +0 -3
- package/dist/webhook/signature.d.ts.map +0 -1
- package/dist/webhook/signature.js +0 -7
- package/dist/webhook/signature.js.map +0 -1
- package/dist/workflow/manifest.d.ts +0 -22
- package/dist/workflow/manifest.d.ts.map +0 -1
- package/dist/workflow/manifest.js +0 -21
- package/dist/workflow/manifest.js.map +0 -1
- package/dist/workflow/registry.d.ts +0 -7
- package/dist/workflow/registry.d.ts.map +0 -1
- package/dist/workflow/registry.js +0 -18
- package/dist/workflow/registry.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -6
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -18
- package/dist/workflow/templates.js.map +0 -1
- package/dist/workflow/triggers.d.ts +0 -64
- package/dist/workflow/triggers.d.ts.map +0 -1
- package/dist/workflow/triggers.js +0 -99
- package/dist/workflow/triggers.js.map +0 -1
- /package/{extension/README.md → docs/extension.md} +0 -0
package/render.ts
ADDED
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render.ts — software rendering stack and gpu identity spoofing for the
|
|
3
|
+
* virtual hardware engine.
|
|
4
|
+
*
|
|
5
|
+
* the module pins the 100% software graphics pipeline verified against
|
|
6
|
+
* primary sources on 2026-08-22:
|
|
7
|
+
* - mesa 26.2.1 stable (released 2026-08-20) with the 26.3-devel mainline
|
|
8
|
+
* open since 2026-07-15; llvm 22.1.8 (2026-07-10) is the reference
|
|
9
|
+
* compiler backend and mesa 26.2 is already llvm-22 ready
|
|
10
|
+
* - llvmpipe: opengl 4.6 core profile 100% (161/161 extensions tracked by
|
|
11
|
+
* mesamatrix), opengl es 3.2 100%, avx-512 selected at runtime by gallivm
|
|
12
|
+
* (mesa mr !17813), rasterizer thread ceiling lp_max_threads = 32 (mr
|
|
13
|
+
* 31551 raised the previous 16; docs state "up to 32 at this time")
|
|
14
|
+
* - lavapipe: vulkan 1.4 surface since mesa 25.1, khronos-conformant on the
|
|
15
|
+
* vulkan 1.3 cts (submission 2022-07-19); icd manifest
|
|
16
|
+
* /usr/share/vulkan/icd.d/lvp_icd.x86_64.json selected through
|
|
17
|
+
* vk_driver_files (vk_icd_filenames is deprecated by the vulkan loader)
|
|
18
|
+
* - rusticl: opencl 3.1 with same-day spec support in mesa 26.2; sole mesa
|
|
19
|
+
* opencl frontend since clover was deleted in mesa 25.2; cl_khr_fp16 is
|
|
20
|
+
* default since mesa 25.2 so only fp64 still needs rusticl_features
|
|
21
|
+
* - xvfb headless glx recipe (man xvfb(1)):
|
|
22
|
+
* "xvfb :99 -ac -screen 0 1920x1080x24 -nolisten tcp +extension glx
|
|
23
|
+
* +render -noreset"
|
|
24
|
+
*
|
|
25
|
+
* contexts (25): versioncatalog, llvmpipefacts, cpucapsdomain, lpperfdomain,
|
|
26
|
+
* lavapipefacts, rusticlfacts, xvfbrecipe, gpuspoofdata, pciid, gpuregistry,
|
|
27
|
+
* gpuprofilebuilder, mesaenvbuilder, renderenv, threadadvisor, smiformat,
|
|
28
|
+
* smiadapter, rendertargets, rendererbase, llvmpiperenderer, lavapiperenderer,
|
|
29
|
+
* rusticlrenderer, xvfbdisplay, rendererfactory, rendererproxy, renderprobe
|
|
30
|
+
*
|
|
31
|
+
* patterns: builder (mesaenvbuilder, gpuprofilebuilder), registry
|
|
32
|
+
* (gpuregistry), factory (rendererfactory), proxy (rendererproxy).
|
|
33
|
+
* rules: lowercase identifiers, english jsdoc, third person, no emoji,
|
|
34
|
+
* try/catch catcher on every fallible path, node:* built-ins only, and no
|
|
35
|
+
* hardcoded localhost address anywhere.
|
|
36
|
+
*
|
|
37
|
+
* domain split (v2): this module keeps the software identity stack — mesa
|
|
38
|
+
* environment builders, llvmpipe/lavapipe/rusticl renderers, xvfb and the
|
|
39
|
+
* virtual nvidia-smi adapter renderer. the physical gpu side (vfio passthrough, vgpu
|
|
40
|
+
* slicing, MIG layouts, the verified gpu bank) lives in virtualgpu.ts, and
|
|
41
|
+
* the paravirtual virtio-gpu venus path (`-device virtio-gpu-pci,venus=true`
|
|
42
|
+
* with `hostmem=8g`, or `virtio-gpu-gl blob=on venus=on`) is emitted by the
|
|
43
|
+
* virtualization module that owns the QEMU command line.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import type { ChildProcess } from 'node:child_process';
|
|
47
|
+
import { spawn } from 'node:child_process';
|
|
48
|
+
import { randomInt } from 'node:crypto';
|
|
49
|
+
import { cpus } from 'node:os';
|
|
50
|
+
import process from 'node:process';
|
|
51
|
+
|
|
52
|
+
/* ------------------------------------------------------------------ */
|
|
53
|
+
/* context: version catalog (verified 2026-08-22) */
|
|
54
|
+
/* ------------------------------------------------------------------ */
|
|
55
|
+
|
|
56
|
+
/** immutable version contract for the software rendering stack. */
|
|
57
|
+
interface versioncatalog {
|
|
58
|
+
readonly mesa: string;
|
|
59
|
+
readonly mesadevel: string;
|
|
60
|
+
readonly llvm: string;
|
|
61
|
+
readonly gl: string;
|
|
62
|
+
readonly gles: string;
|
|
63
|
+
readonly vulkan: string;
|
|
64
|
+
readonly opencl: string;
|
|
65
|
+
readonly smidriver: string;
|
|
66
|
+
readonly cudaversion: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** stack pinned from primary sources (mesa3d.org, llvm releases, docs.nvidia.com). */
|
|
70
|
+
const mesastack = {
|
|
71
|
+
mesa: '26.2.1',
|
|
72
|
+
mesadevel: '26.3-devel',
|
|
73
|
+
llvm: '22.1.8',
|
|
74
|
+
gl: '4.6',
|
|
75
|
+
gles: '3.2',
|
|
76
|
+
vulkan: '1.4',
|
|
77
|
+
opencl: '3.1',
|
|
78
|
+
smidriver: '575.57.08',
|
|
79
|
+
cudaversion: '12.9',
|
|
80
|
+
} satisfies versioncatalog;
|
|
81
|
+
|
|
82
|
+
/* ------------------------------------------------------------------ */
|
|
83
|
+
/* context: llvmpipe capability facts */
|
|
84
|
+
/* ------------------------------------------------------------------ */
|
|
85
|
+
|
|
86
|
+
/** llvmpipe capability snapshot (docs.mesa3d.org/drivers/llvmpipe + mesamatrix). */
|
|
87
|
+
interface llvmpipefacts {
|
|
88
|
+
readonly glcoreprofile: string;
|
|
89
|
+
readonly glcoreextensions: string;
|
|
90
|
+
readonly glesprofile: string;
|
|
91
|
+
readonly glesextensions: string;
|
|
92
|
+
readonly avx512: string;
|
|
93
|
+
readonly threadceiling: number;
|
|
94
|
+
readonly threadnote: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const llvmpipe = {
|
|
98
|
+
glcoreprofile: '4.6 core',
|
|
99
|
+
glcoreextensions: '161/161 (100%)',
|
|
100
|
+
glesprofile: '3.2',
|
|
101
|
+
glesextensions: '100%',
|
|
102
|
+
avx512: 'gallivm enables avx-512 at runtime (mesa mr !17813)',
|
|
103
|
+
threadceiling: 32,
|
|
104
|
+
threadnote:
|
|
105
|
+
'lp_max_threads is a compile-time constant; mr 31551 raised 16 to 32; the docs state "up to 32 at this time"',
|
|
106
|
+
} satisfies llvmpipefacts;
|
|
107
|
+
|
|
108
|
+
/** valid values for gallium_override_cpu_caps (docs.mesa3d.org/envvars).
|
|
109
|
+
* no avx2/avx512 value exists: avx-512 is detected at runtime by gallivm. */
|
|
110
|
+
const cpucapsdomain = ['nosse', 'sse', 'sse2', 'sse3', 'ssse3', 'sse4.1', 'avx'] as const;
|
|
111
|
+
type cpucaps = (typeof cpucapsdomain)[number];
|
|
112
|
+
|
|
113
|
+
/** valid stages for lp_perf selective no-ops (docs: "see the source code"). */
|
|
114
|
+
const lpperfdomain = [
|
|
115
|
+
'no_blend',
|
|
116
|
+
'no_depth',
|
|
117
|
+
'no_alphatest',
|
|
118
|
+
'no_tex',
|
|
119
|
+
'no_linear',
|
|
120
|
+
'no_mipmaps',
|
|
121
|
+
'no_mip_linear',
|
|
122
|
+
'tex_mem',
|
|
123
|
+
] as const;
|
|
124
|
+
type lpperfstage = (typeof lpperfdomain)[number];
|
|
125
|
+
|
|
126
|
+
/* ------------------------------------------------------------------ */
|
|
127
|
+
/* context: lavapipe and rusticl facts */
|
|
128
|
+
/* ------------------------------------------------------------------ */
|
|
129
|
+
|
|
130
|
+
/** lavapipe (vulkan software) facts verified 2026-08-22. */
|
|
131
|
+
interface lavapipefacts {
|
|
132
|
+
readonly vulkan: string;
|
|
133
|
+
readonly conformance: string;
|
|
134
|
+
readonly icdmanifest: string;
|
|
135
|
+
readonly icdenv: string;
|
|
136
|
+
readonly deprecatedicdenv: string;
|
|
137
|
+
readonly extensions: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const lavapipe = {
|
|
141
|
+
vulkan: '1.4',
|
|
142
|
+
conformance: 'khronos-conformant on the vulkan 1.3 cts (submission 2022-07-19)',
|
|
143
|
+
icdmanifest: '/usr/share/vulkan/icd.d/lvp_icd.x86_64.json',
|
|
144
|
+
icdenv: 'VK_DRIVER_FILES',
|
|
145
|
+
deprecatedicdenv: 'VK_ICD_FILENAMES',
|
|
146
|
+
extensions: '204/303 extensions (67.3%) in mesa 26.2',
|
|
147
|
+
} satisfies lavapipefacts;
|
|
148
|
+
|
|
149
|
+
/** rusticl (opencl software) facts verified 2026-08-22. */
|
|
150
|
+
interface rusticlfacts {
|
|
151
|
+
readonly opencl: string;
|
|
152
|
+
readonly clovernote: string;
|
|
153
|
+
readonly fp16note: string;
|
|
154
|
+
readonly features: string;
|
|
155
|
+
readonly devicetype: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const rusticl = {
|
|
159
|
+
opencl: '3.1',
|
|
160
|
+
clovernote: 'clover was deleted in mesa 25.2; rusticl is the sole mesa opencl frontend',
|
|
161
|
+
fp16note: 'cl_khr_fp16 is default since mesa 25.2 (rusticl_features=fp16 is obsolete)',
|
|
162
|
+
features: 'fp64',
|
|
163
|
+
devicetype: 'gpu',
|
|
164
|
+
} satisfies rusticlfacts;
|
|
165
|
+
|
|
166
|
+
/* ------------------------------------------------------------------ */
|
|
167
|
+
/* context: xvfb headless recipe */
|
|
168
|
+
/* ------------------------------------------------------------------ */
|
|
169
|
+
|
|
170
|
+
/** default xvfb geometry documented in the project research. */
|
|
171
|
+
const defaultgeometry = '1920x1080x24';
|
|
172
|
+
|
|
173
|
+
/** builds the validated headless glx argv for xvfb (man xvfb(1)). */
|
|
174
|
+
function xvfbargs(display: string, geometry: string): readonly string[] {
|
|
175
|
+
return [
|
|
176
|
+
`:${display}`,
|
|
177
|
+
'-ac',
|
|
178
|
+
'-screen',
|
|
179
|
+
'0',
|
|
180
|
+
geometry,
|
|
181
|
+
'-nolisten',
|
|
182
|
+
'tcp',
|
|
183
|
+
'+extension',
|
|
184
|
+
'GLX',
|
|
185
|
+
'+render',
|
|
186
|
+
'-noreset',
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** picks a random x display number in the 90-99 range; the documented
|
|
191
|
+
* example uses :99, so the random pick avoids colliding with it. */
|
|
192
|
+
function randomdisplay(): string {
|
|
193
|
+
try {
|
|
194
|
+
return String(90 + randomInt(10));
|
|
195
|
+
} catch {
|
|
196
|
+
return '94';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* ------------------------------------------------------------------ */
|
|
201
|
+
/* context: gpu virtual identity data (verified specs, 2026-08-22) */
|
|
202
|
+
/* ------------------------------------------------------------------ */
|
|
203
|
+
|
|
204
|
+
/** gpu identity used for spoofing; every field comes from vendor pages,
|
|
205
|
+
* techpowerup and the pci id databases confirmed in the project research.
|
|
206
|
+
* physical passthrough identities live in virtualgpu.ts; this table only
|
|
207
|
+
* feeds the software identity stack (the smi adapter and friends). */
|
|
208
|
+
interface gpuspoof {
|
|
209
|
+
readonly name: string;
|
|
210
|
+
readonly vendor: 'nvidia' | 'amd';
|
|
211
|
+
readonly pcivendor: string;
|
|
212
|
+
readonly pcidevice: string;
|
|
213
|
+
readonly vrammib: number;
|
|
214
|
+
readonly memtype: string;
|
|
215
|
+
readonly bandwidthgbs: number;
|
|
216
|
+
readonly tdpw: number;
|
|
217
|
+
readonly arch: string;
|
|
218
|
+
readonly driver: string;
|
|
219
|
+
readonly cuda: string | null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const gpuspoofdata = {
|
|
223
|
+
rtx5090: {
|
|
224
|
+
name: 'NVIDIA RTX 5090',
|
|
225
|
+
vendor: 'nvidia',
|
|
226
|
+
pcivendor: '10DE',
|
|
227
|
+
pcidevice: '2B85',
|
|
228
|
+
vrammib: 32768,
|
|
229
|
+
memtype: 'GDDR7',
|
|
230
|
+
bandwidthgbs: 1792,
|
|
231
|
+
tdpw: 575,
|
|
232
|
+
arch: 'GB202 Blackwell sm_120',
|
|
233
|
+
driver: mesastack.smidriver,
|
|
234
|
+
cuda: mesastack.cudaversion,
|
|
235
|
+
},
|
|
236
|
+
rtxpro6000: {
|
|
237
|
+
name: 'NVIDIA RTX PRO 6000 Blackwell',
|
|
238
|
+
vendor: 'nvidia',
|
|
239
|
+
pcivendor: '10DE',
|
|
240
|
+
pcidevice: '26B5',
|
|
241
|
+
vrammib: 98304,
|
|
242
|
+
memtype: 'GDDR7 ECC',
|
|
243
|
+
bandwidthgbs: 1792,
|
|
244
|
+
tdpw: 600,
|
|
245
|
+
arch: 'GB202 Blackwell workstation',
|
|
246
|
+
driver: mesastack.smidriver,
|
|
247
|
+
cuda: mesastack.cudaversion,
|
|
248
|
+
},
|
|
249
|
+
b200: {
|
|
250
|
+
name: 'NVIDIA B200',
|
|
251
|
+
vendor: 'nvidia',
|
|
252
|
+
pcivendor: '10DE',
|
|
253
|
+
pcidevice: '2665',
|
|
254
|
+
vrammib: 196608,
|
|
255
|
+
memtype: 'HBM3e',
|
|
256
|
+
bandwidthgbs: 8000,
|
|
257
|
+
tdpw: 1000,
|
|
258
|
+
arch: 'GB100 Blackwell dual-die',
|
|
259
|
+
driver: mesastack.smidriver,
|
|
260
|
+
cuda: mesastack.cudaversion,
|
|
261
|
+
},
|
|
262
|
+
rx9070xt: {
|
|
263
|
+
name: 'AMD Radeon RX 9070 XT',
|
|
264
|
+
vendor: 'amd',
|
|
265
|
+
pcivendor: '1002',
|
|
266
|
+
pcidevice: '748E',
|
|
267
|
+
vrammib: 16384,
|
|
268
|
+
memtype: 'GDDR6',
|
|
269
|
+
bandwidthgbs: 640,
|
|
270
|
+
tdpw: 304,
|
|
271
|
+
arch: 'Navi 48 RDNA 4 gfx1201',
|
|
272
|
+
driver: 'adrenalin 26.x / mesa 26.2',
|
|
273
|
+
cuda: null,
|
|
274
|
+
},
|
|
275
|
+
mi350x: {
|
|
276
|
+
name: 'AMD Instinct MI350X',
|
|
277
|
+
vendor: 'amd',
|
|
278
|
+
pcivendor: '1002',
|
|
279
|
+
pcidevice: '75A0',
|
|
280
|
+
vrammib: 294912,
|
|
281
|
+
memtype: 'HBM3e',
|
|
282
|
+
bandwidthgbs: 8000,
|
|
283
|
+
tdpw: 1000,
|
|
284
|
+
arch: 'CDNA 4 gfx950',
|
|
285
|
+
driver: 'ROCm 7.x',
|
|
286
|
+
cuda: null,
|
|
287
|
+
},
|
|
288
|
+
} satisfies Record<string, gpuspoof>;
|
|
289
|
+
|
|
290
|
+
/** renders the canonical "vendor:device" pci id of a virtual identity profile. */
|
|
291
|
+
function pciid(profile: gpuspoof): string {
|
|
292
|
+
return `${profile.pcivendor}:${profile.pcidevice}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* ------------------------------------------------------------------ */
|
|
296
|
+
/* context: gpu profile registry (registry pattern) */
|
|
297
|
+
/* ------------------------------------------------------------------ */
|
|
298
|
+
|
|
299
|
+
/** registry of named gpu profiles; the whole engine resolves virtual identity
|
|
300
|
+
* identities exclusively through this registry. */
|
|
301
|
+
class gpuregistry {
|
|
302
|
+
#profiles = new Map<string, gpuspoof>();
|
|
303
|
+
|
|
304
|
+
/** registers a profile under a name; returns the registry for chaining. */
|
|
305
|
+
register(name: string, profile: gpuspoof): this {
|
|
306
|
+
try {
|
|
307
|
+
this.#profiles.set(name, profile);
|
|
308
|
+
return this;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
throw new Error(`gpu profile registration failed for ${name}: ${errormessage(error)}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** lists every registered profile name. */
|
|
315
|
+
names(): readonly string[] {
|
|
316
|
+
return [...this.#profiles.keys()];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** resolves a profile or undefined when absent. */
|
|
320
|
+
get(name: string): gpuspoof | undefined {
|
|
321
|
+
return this.#profiles.get(name);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** resolves a profile or throws with the list of known names. */
|
|
325
|
+
require(name: string): gpuspoof {
|
|
326
|
+
const found = this.#profiles.get(name);
|
|
327
|
+
if (found === undefined) {
|
|
328
|
+
throw new Error(`unknown gpu profile "${name}"; known profiles: ${this.names().join(', ')}`);
|
|
329
|
+
}
|
|
330
|
+
return found;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** default registry seeded with the five verified gpu identities. */
|
|
335
|
+
const defaultgpuregistry = new gpuregistry()
|
|
336
|
+
.register('rtx5090', gpuspoofdata.rtx5090)
|
|
337
|
+
.register('rtxpro6000', gpuspoofdata.rtxpro6000)
|
|
338
|
+
.register('b200', gpuspoofdata.b200)
|
|
339
|
+
.register('rx9070xt', gpuspoofdata.rx9070xt)
|
|
340
|
+
.register('mi350x', gpuspoofdata.mi350x);
|
|
341
|
+
|
|
342
|
+
/* ------------------------------------------------------------------ */
|
|
343
|
+
/* context: gpu profile builder (builder pattern) */
|
|
344
|
+
/* ------------------------------------------------------------------ */
|
|
345
|
+
|
|
346
|
+
/** fluent builder for custom virtual identity profiles; defaults mirror the rtx 5090. */
|
|
347
|
+
class gpuprofilebuilder {
|
|
348
|
+
#name = 'NVIDIA RTX 5090';
|
|
349
|
+
#vendor: gpuspoof['vendor'] = 'nvidia';
|
|
350
|
+
#pcivendor = '10DE';
|
|
351
|
+
#pcidevice = '2B85';
|
|
352
|
+
#vrammib = 32768;
|
|
353
|
+
#memtype = 'GDDR7';
|
|
354
|
+
#bandwidthgbs = 1792;
|
|
355
|
+
#tdpw = 575;
|
|
356
|
+
#arch = 'GB202 Blackwell sm_120';
|
|
357
|
+
#driver = mesastack.smidriver;
|
|
358
|
+
#cuda: string | null = mesastack.cudaversion;
|
|
359
|
+
|
|
360
|
+
withname(name: string): this {
|
|
361
|
+
this.#name = name;
|
|
362
|
+
return this;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
withvendor(vendor: gpuspoof['vendor']): this {
|
|
366
|
+
this.#vendor = vendor;
|
|
367
|
+
this.#pcivendor = vendor === 'nvidia' ? '10DE' : '1002';
|
|
368
|
+
return this;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
withpcidevice(device: string): this {
|
|
372
|
+
this.#pcidevice = device;
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
withvram(mib: number): this {
|
|
377
|
+
this.#vrammib = mib;
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
withmemtype(memtype: string): this {
|
|
382
|
+
this.#memtype = memtype;
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
withbandwidth(gbs: number): this {
|
|
387
|
+
this.#bandwidthgbs = gbs;
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
withtdp(w: number): this {
|
|
392
|
+
this.#tdpw = w;
|
|
393
|
+
return this;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
witharch(arch: string): this {
|
|
397
|
+
this.#arch = arch;
|
|
398
|
+
return this;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
withdriver(driver: string, cuda: string | null): this {
|
|
402
|
+
this.#driver = driver;
|
|
403
|
+
this.#cuda = cuda;
|
|
404
|
+
return this;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** validates the profile shape and freezes it into a gpuspoof. */
|
|
408
|
+
build(): gpuspoof {
|
|
409
|
+
if (this.#name.length === 0) {
|
|
410
|
+
throw new Error('gpu profile requires a name');
|
|
411
|
+
}
|
|
412
|
+
if (!/^[0-9A-Fa-f]{4}$/.test(this.#pcidevice)) {
|
|
413
|
+
throw new Error(`gpu profile pci device "${this.#pcidevice}" must be 4 hex digits`);
|
|
414
|
+
}
|
|
415
|
+
if (this.#vrammib <= 0 || this.#bandwidthgbs <= 0 || this.#tdpw <= 0) {
|
|
416
|
+
throw new Error('gpu profile requires positive vram, bandwidth and tdp values');
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
name: this.#name,
|
|
420
|
+
vendor: this.#vendor,
|
|
421
|
+
pcivendor: this.#pcivendor,
|
|
422
|
+
pcidevice: this.#pcidevice.toUpperCase(),
|
|
423
|
+
vrammib: this.#vrammib,
|
|
424
|
+
memtype: this.#memtype,
|
|
425
|
+
bandwidthgbs: this.#bandwidthgbs,
|
|
426
|
+
tdpw: this.#tdpw,
|
|
427
|
+
arch: this.#arch,
|
|
428
|
+
driver: this.#driver,
|
|
429
|
+
cuda: this.#cuda,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* ------------------------------------------------------------------ */
|
|
435
|
+
/* context: mesa environment builder (builder pattern) */
|
|
436
|
+
/* ------------------------------------------------------------------ */
|
|
437
|
+
|
|
438
|
+
/** fluent builder that assembles the complete validated mesa environment;
|
|
439
|
+
* every variable below is documented at docs.mesa3d.org/envvars.html. */
|
|
440
|
+
class mesaenvbuilder {
|
|
441
|
+
#vars = new Map<string, string>();
|
|
442
|
+
#perf = new Set<string>();
|
|
443
|
+
|
|
444
|
+
/** forces the software path: llvmpipe through the gallium loader. */
|
|
445
|
+
software(): this {
|
|
446
|
+
this.#vars.set('LIBGL_ALWAYS_SOFTWARE', 'true');
|
|
447
|
+
this.#vars.set('GALLIUM_DRIVER', 'llvmpipe');
|
|
448
|
+
return this;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** spoofs glgetstring(gl_version); 4.6 is the llvmpipe maximum. */
|
|
452
|
+
glversion(version: string = mesastack.gl): this {
|
|
453
|
+
this.#vars.set('MESA_GL_VERSION_OVERRIDE', version);
|
|
454
|
+
return this;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** spoofs gl_shading_language_version (460 matches gl 4.6). */
|
|
458
|
+
glslversion(version: string = '460'): this {
|
|
459
|
+
this.#vars.set('MESA_GLSL_VERSION_OVERRIDE', version);
|
|
460
|
+
return this;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** spoofs the gles version reported by the software driver. */
|
|
464
|
+
glesversion(version: string = mesastack.gles): this {
|
|
465
|
+
this.#vars.set('MESA_GLES_VERSION_OVERRIDE', version);
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** spoofs the vulkan apiversion; cannot exceed the driver instance version. */
|
|
470
|
+
vulkanversion(version: string = mesastack.vulkan): this {
|
|
471
|
+
this.#vars.set('MESA_VK_VERSION_OVERRIDE', version);
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** sets lp_num_threads; 0 lets llvmpipe use every core up to the
|
|
476
|
+
* lp_max_threads=32 ceiling, 1..32 pins an explicit thread count. */
|
|
477
|
+
threads(count: number = 0): this {
|
|
478
|
+
if (count < 0 || count > llvmpipe.threadceiling) {
|
|
479
|
+
throw new Error(`lp_num_threads must be 0..${llvmpipe.threadceiling}`);
|
|
480
|
+
}
|
|
481
|
+
this.#vars.set('LP_NUM_THREADS', String(count));
|
|
482
|
+
return this;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** sets lp_native_vector_width; 512 exercises the avx-512 paths even
|
|
486
|
+
* though gallivm picks avx-512 automatically at runtime. */
|
|
487
|
+
vectorwidth(width: 128 | 256 | 512): this {
|
|
488
|
+
this.#vars.set('LP_NATIVE_VECTOR_WIDTH', String(width));
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** overrides reported cpu caps; avx is the highest documented value
|
|
493
|
+
* (there is no avx2/avx512 entry in the envvars documentation). */
|
|
494
|
+
cpucaps(caps: cpucaps): this {
|
|
495
|
+
if (!(cpucapsdomain as readonly string[]).includes(caps)) {
|
|
496
|
+
throw new Error(`gallium_override_cpu_caps must be one of: ${cpucapsdomain.join('|')}`);
|
|
497
|
+
}
|
|
498
|
+
this.#vars.set('GALLIUM_OVERRIDE_CPU_CAPS', caps);
|
|
499
|
+
return this;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/** adds one lp_perf selective no-op stage (benchmarking aid). */
|
|
503
|
+
perf(stage: lpperfstage): this {
|
|
504
|
+
if (!(lpperfdomain as readonly string[]).includes(stage)) {
|
|
505
|
+
throw new Error(`lp_perf stage must be one of: ${lpperfdomain.join(',')}`);
|
|
506
|
+
}
|
|
507
|
+
this.#perf.add(stage);
|
|
508
|
+
return this;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** disables gl error checking (gl_khr_no_error) to cut cpu overhead. */
|
|
512
|
+
noerror(enabled: boolean = true): this {
|
|
513
|
+
this.#vars.set('MESA_NO_ERROR', enabled ? '1' : '0');
|
|
514
|
+
return this;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** pins lavapipe through vk_driver_files and enables the headless wsi
|
|
518
|
+
* swapchain; vk_icd_filenames is deprecated and never emitted. */
|
|
519
|
+
lavapipe(icd: string = lavapipe.icdmanifest): this {
|
|
520
|
+
this.#vars.set('VK_DRIVER_FILES', icd);
|
|
521
|
+
this.#vars.set('MESA_VK_WSI_HEADLESS_SWAPCHAIN', '1');
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** enables rusticl on llvmpipe with the gpu device type virtual identity, opencl
|
|
526
|
+
* 3.1 and the fp64 feature (fp16 is default since mesa 25.2). */
|
|
527
|
+
rusticl(): this {
|
|
528
|
+
this.#vars.set('RUSTICL_ENABLE', 'llvmpipe');
|
|
529
|
+
this.#vars.set('RUSTICL_DEVICE_TYPE', rusticl.devicetype);
|
|
530
|
+
this.#vars.set('RUSTICL_CL_VERSION', rusticl.opencl);
|
|
531
|
+
this.#vars.set('RUSTICL_FEATURES', rusticl.features);
|
|
532
|
+
return this;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** escape hatch for extra variables; caller keeps full choice. */
|
|
536
|
+
set(key: string, value: string): this {
|
|
537
|
+
this.#vars.set(key, value);
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** freezes the environment into a plain record (lp_perf joined last). */
|
|
542
|
+
build(): Record<string, string> {
|
|
543
|
+
const vars: Record<string, string> = {};
|
|
544
|
+
for (const [key, value] of this.#vars) {
|
|
545
|
+
vars[key] = value;
|
|
546
|
+
}
|
|
547
|
+
if (this.#perf.size > 0) {
|
|
548
|
+
vars.LP_PERF = [...this.#perf].join(',');
|
|
549
|
+
}
|
|
550
|
+
return vars;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/* ------------------------------------------------------------------ */
|
|
555
|
+
/* context: renderenv presets + thread advisor */
|
|
556
|
+
/* ------------------------------------------------------------------ */
|
|
557
|
+
|
|
558
|
+
/** render target selector used by the factory and the presets. */
|
|
559
|
+
type rendertarget = 'opengl' | 'gles' | 'vulkan' | 'opencl' | 'xvfb';
|
|
560
|
+
|
|
561
|
+
/** one-call environment assembly per render target. */
|
|
562
|
+
function renderenv(target: rendertarget): Record<string, string> {
|
|
563
|
+
const builder = new mesaenvbuilder();
|
|
564
|
+
switch (target) {
|
|
565
|
+
case 'opengl':
|
|
566
|
+
return builder
|
|
567
|
+
.software()
|
|
568
|
+
.glversion()
|
|
569
|
+
.glslversion()
|
|
570
|
+
.threads()
|
|
571
|
+
.vectorwidth(512)
|
|
572
|
+
.noerror()
|
|
573
|
+
.build();
|
|
574
|
+
case 'gles':
|
|
575
|
+
return builder.software().glesversion().threads().noerror().build();
|
|
576
|
+
case 'vulkan':
|
|
577
|
+
return builder.software().vulkanversion().lavapipe().build();
|
|
578
|
+
case 'opencl':
|
|
579
|
+
return builder.software().rusticl().build();
|
|
580
|
+
case 'xvfb':
|
|
581
|
+
return {};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/** advisory thread count: one rasterizer thread per host core bounded by
|
|
586
|
+
* the compile-time lp_max_threads=32 ceiling. */
|
|
587
|
+
function advisethreads(hostcores: number = cpus().length): number {
|
|
588
|
+
return Math.max(1, Math.min(hostcores, llvmpipe.threadceiling));
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* ------------------------------------------------------------------ */
|
|
592
|
+
/* context: smi format helpers (89-char summary table) */
|
|
593
|
+
/* ------------------------------------------------------------------ */
|
|
594
|
+
|
|
595
|
+
const smiwidth = 89;
|
|
596
|
+
const smicolumns = [40, 24, 21] as const;
|
|
597
|
+
const procwidth = 75;
|
|
598
|
+
|
|
599
|
+
/** right-pad helper that never exceeds the target width. */
|
|
600
|
+
function smipad(text: string, width: number): string {
|
|
601
|
+
return text.length >= width ? text.slice(0, width) : text + ' '.repeat(width - text.length);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** left-pad helper for right-aligned smi columns. */
|
|
605
|
+
function smipadstart(text: string, width: number): string {
|
|
606
|
+
return text.length >= width
|
|
607
|
+
? text.slice(text.length - width)
|
|
608
|
+
: ' '.repeat(width - text.length) + text;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/** full-width border line: "+" plus 87 fill characters plus "+". */
|
|
612
|
+
function smiborder(fill: string): string {
|
|
613
|
+
return `+${fill.repeat(smiwidth - 2)}+`;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** full-width content line padded to the 89-char table width. */
|
|
617
|
+
function smifull(content: string): string {
|
|
618
|
+
return `|${smipad(content, smiwidth - 2)}|`;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/** three-column separator: pipes at fixed offsets 1, 42 and 67. */
|
|
622
|
+
function smisep(fill: string): string {
|
|
623
|
+
return (
|
|
624
|
+
'|' +
|
|
625
|
+
fill.repeat(smicolumns[0]) +
|
|
626
|
+
'+' +
|
|
627
|
+
fill.repeat(smicolumns[1]) +
|
|
628
|
+
'+' +
|
|
629
|
+
fill.repeat(smicolumns[2]) +
|
|
630
|
+
'|'
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** three-column data row within the 89-char width. */
|
|
635
|
+
function smirow(left: string, mid: string, right: string): string {
|
|
636
|
+
return (
|
|
637
|
+
'|' +
|
|
638
|
+
smipad(left, smicolumns[0]) +
|
|
639
|
+
'|' +
|
|
640
|
+
smipad(mid, smicolumns[1]) +
|
|
641
|
+
'|' +
|
|
642
|
+
smipad(right, smicolumns[2]) +
|
|
643
|
+
'|'
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** process table content line within the 75-char width. */
|
|
648
|
+
function procline(content: string): string {
|
|
649
|
+
return `|${smipad(content, procwidth - 2)}|`;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/** process table bottom border. */
|
|
653
|
+
function procborder(): string {
|
|
654
|
+
return `+${'-'.repeat(procwidth - 2)}+`;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const smiweekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const;
|
|
658
|
+
const smimonths = [
|
|
659
|
+
'Jan',
|
|
660
|
+
'Feb',
|
|
661
|
+
'Mar',
|
|
662
|
+
'Apr',
|
|
663
|
+
'May',
|
|
664
|
+
'Jun',
|
|
665
|
+
'Jul',
|
|
666
|
+
'Aug',
|
|
667
|
+
'Sep',
|
|
668
|
+
'Oct',
|
|
669
|
+
'Nov',
|
|
670
|
+
'Dec',
|
|
671
|
+
] as const;
|
|
672
|
+
|
|
673
|
+
/** formats the nvidia-smi timestamp "day-of-week month day hh:mm:ss year". */
|
|
674
|
+
function formatstamp(date: Date): string {
|
|
675
|
+
const two = (value: number): string => String(value).padStart(2, '0');
|
|
676
|
+
return (
|
|
677
|
+
`${smiweekdays[date.getDay()]} ${smimonths[date.getMonth()]} ${two(date.getDate())} ` +
|
|
678
|
+
`${two(date.getHours())}:${two(date.getMinutes())}:${two(date.getSeconds())} ${date.getFullYear()}`
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/* ------------------------------------------------------------------ */
|
|
683
|
+
/* context: virtual nvidia-smi adapter generator */
|
|
684
|
+
/* ------------------------------------------------------------------ */
|
|
685
|
+
|
|
686
|
+
/** process row of the nvidia-smi processes table. */
|
|
687
|
+
interface smiprocess {
|
|
688
|
+
readonly gpu: number;
|
|
689
|
+
readonly gi: number;
|
|
690
|
+
readonly ci: number;
|
|
691
|
+
readonly pid: number;
|
|
692
|
+
readonly type: 'C' | 'G' | 'C+G';
|
|
693
|
+
readonly name: string;
|
|
694
|
+
readonly memorymib: number;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** builds a full virtual nvidia-smi adapter report; the header pins driver
|
|
698
|
+
* 575.57.08 and cuda 12.9 exactly as verified in the project research.
|
|
699
|
+
* notes: perf states span p0-p12, compute modes are default or exclusive
|
|
700
|
+
* process, mig is reported as n/a when disabled and fan speed may legally
|
|
701
|
+
* exceed 100% per the nvidia-smi documentation. */
|
|
702
|
+
class smiadapterbuilder {
|
|
703
|
+
readonly #profile: gpuspoof;
|
|
704
|
+
readonly #index: number;
|
|
705
|
+
#memusedmib = 0;
|
|
706
|
+
#util = 0;
|
|
707
|
+
#temperature = 41;
|
|
708
|
+
#fanspeed = 30;
|
|
709
|
+
#powerw: number;
|
|
710
|
+
#ecc = 'N/A';
|
|
711
|
+
#computemode = 'Default';
|
|
712
|
+
#processes: smiprocess[] = [];
|
|
713
|
+
|
|
714
|
+
constructor(profile: gpuspoof, index = 0) {
|
|
715
|
+
this.#profile = profile;
|
|
716
|
+
this.#index = index;
|
|
717
|
+
this.#powerw = Math.max(10, Math.round(profile.tdpw * 0.04));
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
withmemused(mib: number): this {
|
|
721
|
+
this.#memusedmib = mib;
|
|
722
|
+
return this;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
withutil(percent: number): this {
|
|
726
|
+
this.#util = percent;
|
|
727
|
+
return this;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
withtemperature(celsius: number): this {
|
|
731
|
+
this.#temperature = celsius;
|
|
732
|
+
return this;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
withfanspeed(percent: number): this {
|
|
736
|
+
this.#fanspeed = percent;
|
|
737
|
+
return this;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
withpower(watts: number): this {
|
|
741
|
+
this.#powerw = watts;
|
|
742
|
+
return this;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
withecc(text: string): this {
|
|
746
|
+
this.#ecc = text;
|
|
747
|
+
return this;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
withcomputemode(mode: 'Default' | 'Exclusive Process'): this {
|
|
751
|
+
this.#computemode = mode;
|
|
752
|
+
return this;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
withprocess(entry: smiprocess): this {
|
|
756
|
+
this.#processes.push(entry);
|
|
757
|
+
return this;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/** derives a realistic bus id from the gpu index. */
|
|
761
|
+
#busid(): string {
|
|
762
|
+
const bus = String(1 + this.#index).padStart(2, '0');
|
|
763
|
+
return `00000000:${bus}:00.0`;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/** renders the complete report; the summary table is exactly 89 chars. */
|
|
767
|
+
build(): string {
|
|
768
|
+
try {
|
|
769
|
+
const head =
|
|
770
|
+
' NVIDIA-SMI ' +
|
|
771
|
+
mesastack.smidriver +
|
|
772
|
+
' '.repeat(13) +
|
|
773
|
+
'Driver Version: ' +
|
|
774
|
+
mesastack.smidriver +
|
|
775
|
+
' '.repeat(6) +
|
|
776
|
+
'CUDA Version: ' +
|
|
777
|
+
mesastack.cudaversion;
|
|
778
|
+
const lines: string[] = [
|
|
779
|
+
formatstamp(new Date()),
|
|
780
|
+
smiborder('-'),
|
|
781
|
+
smifull(head),
|
|
782
|
+
smisep('-'),
|
|
783
|
+
smirow(
|
|
784
|
+
smipad(' GPU Name', 27) + smipad('Persistence-M', 13),
|
|
785
|
+
smipad(' Bus-Id Disp.A', 24),
|
|
786
|
+
` ${smipadstart('Volatile Uncorr. ECC', 20)}`,
|
|
787
|
+
),
|
|
788
|
+
smirow(
|
|
789
|
+
smipad(' Fan Temp Perf Pwr:Usage/Cap', 40),
|
|
790
|
+
smipad(' Memory-Usage', 24),
|
|
791
|
+
` ${smipadstart('GPU-Util Compute M.', 20)}`,
|
|
792
|
+
),
|
|
793
|
+
smisep('='),
|
|
794
|
+
smirow(
|
|
795
|
+
smipad(` ${this.#index} ${this.#profile.name}`, 36) + smipad('Off', 4),
|
|
796
|
+
smipad(` ${this.#busid()} Off`, 24),
|
|
797
|
+
` ${smipadstart(this.#ecc, 20)}`,
|
|
798
|
+
),
|
|
799
|
+
smirow(
|
|
800
|
+
smipad(
|
|
801
|
+
` ${this.#fanspeed}% ${this.#temperature}C P0 ${this.#powerw}W / ${this.#profile.tdpw}W`,
|
|
802
|
+
40,
|
|
803
|
+
),
|
|
804
|
+
smipadstart(`${this.#memusedmib}MiB / ${this.#profile.vrammib}MiB`, 24),
|
|
805
|
+
smipadstart(`${this.#util}%`, 10) + smipad(` ${this.#computemode}`, 11),
|
|
806
|
+
),
|
|
807
|
+
smirow('', '', ''),
|
|
808
|
+
smiborder('-'),
|
|
809
|
+
'',
|
|
810
|
+
'Processes:',
|
|
811
|
+
procline(
|
|
812
|
+
smipad(' GPU GI CI', 20) +
|
|
813
|
+
smipad('PID', 7) +
|
|
814
|
+
smipad('Type', 8) +
|
|
815
|
+
smipad('Process name', 27) +
|
|
816
|
+
smipad('GPU Memory', 11),
|
|
817
|
+
),
|
|
818
|
+
procline('='.repeat(procwidth - 2)),
|
|
819
|
+
];
|
|
820
|
+
if (this.#processes.length === 0) {
|
|
821
|
+
lines.push(procline(' No running processes found'));
|
|
822
|
+
} else {
|
|
823
|
+
for (const entry of this.#processes) {
|
|
824
|
+
lines.push(
|
|
825
|
+
procline(
|
|
826
|
+
smipad(` ${entry.gpu} ${entry.gi} ${entry.ci}`, 20) +
|
|
827
|
+
smipad(String(entry.pid), 7) +
|
|
828
|
+
smipad(entry.type, 8) +
|
|
829
|
+
smipad(entry.name, 27) +
|
|
830
|
+
smipad(`${entry.memorymib}MiB`, 11),
|
|
831
|
+
),
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
lines.push(procborder());
|
|
836
|
+
return lines.join('\n');
|
|
837
|
+
} catch (error) {
|
|
838
|
+
throw new Error(`virtual nvidia-smi adapter report failed: ${errormessage(error)}`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/* ------------------------------------------------------------------ */
|
|
844
|
+
/* context: run helper + error message catcher */
|
|
845
|
+
/* ------------------------------------------------------------------ */
|
|
846
|
+
|
|
847
|
+
/** result of a completed child process. */
|
|
848
|
+
interface runresult {
|
|
849
|
+
readonly code: number;
|
|
850
|
+
readonly stdout: string;
|
|
851
|
+
readonly stderr: string;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/** normalizes an unknown thrown value into a message string. */
|
|
855
|
+
function errormessage(error: unknown): string {
|
|
856
|
+
return error instanceof Error ? error.message : String(error);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/** runs a command to completion under a merged environment; every spawn
|
|
860
|
+
* path is wrapped in a try/catch catcher for later tracing. */
|
|
861
|
+
function run(
|
|
862
|
+
command: string,
|
|
863
|
+
args: readonly string[],
|
|
864
|
+
env: Record<string, string> = {},
|
|
865
|
+
): Promise<runresult> {
|
|
866
|
+
return new Promise<runresult>((resolve, reject) => {
|
|
867
|
+
try {
|
|
868
|
+
const child = spawn(command, args, { env: { ...process.env, ...env } });
|
|
869
|
+
let stdout = '';
|
|
870
|
+
let stderr = '';
|
|
871
|
+
child.stdout?.on('data', (chunk: Buffer) => {
|
|
872
|
+
stdout += chunk.toString('utf8');
|
|
873
|
+
});
|
|
874
|
+
child.stderr?.on('data', (chunk: Buffer) => {
|
|
875
|
+
stderr += chunk.toString('utf8');
|
|
876
|
+
});
|
|
877
|
+
child.once('error', (error: Error) => {
|
|
878
|
+
reject(new Error(`${command} failed to spawn: ${error.message}`));
|
|
879
|
+
});
|
|
880
|
+
child.once('close', (code: number | null) => {
|
|
881
|
+
resolve({ code: code ?? -1, stdout, stderr });
|
|
882
|
+
});
|
|
883
|
+
} catch (error) {
|
|
884
|
+
reject(new Error(`${command} failed: ${errormessage(error)}`));
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/** returns the first regex capture group of a text or null. */
|
|
890
|
+
function firstmatch(text: string, pattern: RegExp): string | null {
|
|
891
|
+
const found = pattern.exec(text);
|
|
892
|
+
return found === null ? null : (found[1] ?? '').trim() || null;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/* ------------------------------------------------------------------ */
|
|
896
|
+
/* context: renderer base class */
|
|
897
|
+
/* ------------------------------------------------------------------ */
|
|
898
|
+
|
|
899
|
+
/** shared renderer state: environment, optional child process and a
|
|
900
|
+
* trace buffer used as the embedded debugger for later tracing. */
|
|
901
|
+
abstract class rendererbase {
|
|
902
|
+
#child: ChildProcess | null = null;
|
|
903
|
+
readonly #label: string;
|
|
904
|
+
readonly #vars: Record<string, string>;
|
|
905
|
+
readonly #trace: string[] = [];
|
|
906
|
+
|
|
907
|
+
protected constructor(label: string, vars: Record<string, string>) {
|
|
908
|
+
this.#label = label;
|
|
909
|
+
this.#vars = { ...vars };
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
get label(): string {
|
|
913
|
+
return this.#label;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/** environment copy for the render target. */
|
|
917
|
+
get env(): Record<string, string> {
|
|
918
|
+
return { ...this.#vars };
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/** whether the owned helper process is alive. */
|
|
922
|
+
get running(): boolean {
|
|
923
|
+
return this.#child !== null && this.#child.exitCode === null && !this.#child.killed;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** accumulated trace entries (error catcher output). */
|
|
927
|
+
get traces(): readonly string[] {
|
|
928
|
+
return this.#trace;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/** records a trace entry; never throws. */
|
|
932
|
+
protected note(message: string): void {
|
|
933
|
+
this.#trace.push(`${new Date().toISOString()} ${message}`);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** spawns a long-lived helper under the renderer environment. */
|
|
937
|
+
protected launch(command: string, args: readonly string[]): ChildProcess {
|
|
938
|
+
const child = spawn(command, args, {
|
|
939
|
+
env: { ...process.env, ...this.#vars },
|
|
940
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
941
|
+
});
|
|
942
|
+
child.on('error', (error: Error) => {
|
|
943
|
+
this.note(`helper ${command} error: ${error.message}`);
|
|
944
|
+
});
|
|
945
|
+
this.#child = child;
|
|
946
|
+
return child;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/** verifies the virtualized context with the target-specific info tool. */
|
|
950
|
+
abstract verify(): Promise<string>;
|
|
951
|
+
|
|
952
|
+
/** terminates the helper; escalates to SIGKILL after a grace period. */
|
|
953
|
+
async stop(): Promise<void> {
|
|
954
|
+
const child = this.#child;
|
|
955
|
+
if (child === null || child.exitCode !== null) {
|
|
956
|
+
this.#child = null;
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
try {
|
|
960
|
+
child.kill('SIGTERM');
|
|
961
|
+
const exited = await Promise.race([
|
|
962
|
+
new Promise<void>((resolve) => {
|
|
963
|
+
child.once('exit', () => resolve());
|
|
964
|
+
}),
|
|
965
|
+
sleep(2000).then(() => false),
|
|
966
|
+
]);
|
|
967
|
+
if (exited === false && child.exitCode === null) {
|
|
968
|
+
child.kill('SIGKILL');
|
|
969
|
+
}
|
|
970
|
+
this.note(`helper for ${this.#label} stopped`);
|
|
971
|
+
} catch (error) {
|
|
972
|
+
this.note(`stop failed for ${this.#label}: ${errormessage(error)}`);
|
|
973
|
+
} finally {
|
|
974
|
+
this.#child = null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/** `using` disposal hook: stops the helper without awaiting. */
|
|
979
|
+
[Symbol.dispose](): void {
|
|
980
|
+
try {
|
|
981
|
+
void this.stop();
|
|
982
|
+
} catch (error) {
|
|
983
|
+
this.note(`dispose failed for ${this.#label}: ${errormessage(error)}`);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** promise-based sleep used by the stop escalation. */
|
|
989
|
+
function sleep(ms: number): Promise<void> {
|
|
990
|
+
return new Promise<void>((resolve) => {
|
|
991
|
+
setTimeout(resolve, ms);
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/* ------------------------------------------------------------------ */
|
|
996
|
+
/* context: llvmpipe / lavapipe / rusticl renderers */
|
|
997
|
+
/* ------------------------------------------------------------------ */
|
|
998
|
+
|
|
999
|
+
/** opengl/gles renderer backed by llvmpipe. */
|
|
1000
|
+
class llvmpiperenderer extends rendererbase {
|
|
1001
|
+
constructor(env?: Record<string, string>) {
|
|
1002
|
+
super('llvmpipe', env === undefined ? renderenv('opengl') : env);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/** probes glxinfo -b and returns the core profile version string. */
|
|
1006
|
+
override async verify(): Promise<string> {
|
|
1007
|
+
try {
|
|
1008
|
+
const result = await run('glxinfo', ['-B'], this.env);
|
|
1009
|
+
const version = firstmatch(result.stdout, /core profile version string:\s*(.+)/);
|
|
1010
|
+
this.note(`glxinfo exit ${result.code}: ${version ?? 'no version line'}`);
|
|
1011
|
+
return version ?? '';
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
throw new Error(`llvmpipe verify failed: ${errormessage(error)}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/** runs a workload command inside the llvmpipe environment. */
|
|
1018
|
+
async runworkload(command: string, args: readonly string[]): Promise<runresult> {
|
|
1019
|
+
return run(command, args, this.env);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/** vulkan renderer backed by lavapipe. */
|
|
1024
|
+
class lavapiperenderer extends rendererbase {
|
|
1025
|
+
constructor(env?: Record<string, string>) {
|
|
1026
|
+
super('lavapipe', env === undefined ? renderenv('vulkan') : env);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/** probes vulkaninfo --summary and returns the api version. */
|
|
1030
|
+
override async verify(): Promise<string> {
|
|
1031
|
+
try {
|
|
1032
|
+
const result = await run('vulkaninfo', ['--summary'], this.env);
|
|
1033
|
+
const version = firstmatch(result.stdout, /apiVersion\s*:\s*([\d.]+)/);
|
|
1034
|
+
this.note(`vulkaninfo exit ${result.code}: ${version ?? 'no version line'}`);
|
|
1035
|
+
return version ?? '';
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
throw new Error(`lavapipe verify failed: ${errormessage(error)}`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/** runs a workload command inside the lavapipe environment. */
|
|
1042
|
+
async runworkload(command: string, args: readonly string[]): Promise<runresult> {
|
|
1043
|
+
return run(command, args, this.env);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** opencl renderer backed by rusticl. */
|
|
1048
|
+
class rusticlrenderer extends rendererbase {
|
|
1049
|
+
constructor(env?: Record<string, string>) {
|
|
1050
|
+
super('rusticl', env === undefined ? renderenv('opencl') : env);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/** probes clinfo and returns the reported opencl version. */
|
|
1054
|
+
override async verify(): Promise<string> {
|
|
1055
|
+
try {
|
|
1056
|
+
const result = await run('clinfo', [], this.env);
|
|
1057
|
+
const version = firstmatch(result.stdout, /OpenCL (\d+\.\d+)/);
|
|
1058
|
+
this.note(`clinfo exit ${result.code}: ${version ?? 'no version line'}`);
|
|
1059
|
+
return version ?? '';
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
throw new Error(`rusticl verify failed: ${errormessage(error)}`);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/** runs a workload command inside the rusticl environment. */
|
|
1066
|
+
async runworkload(command: string, args: readonly string[]): Promise<runresult> {
|
|
1067
|
+
return run(command, args, this.env);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/* ------------------------------------------------------------------ */
|
|
1072
|
+
/* context: xvfb display server */
|
|
1073
|
+
/* ------------------------------------------------------------------ */
|
|
1074
|
+
|
|
1075
|
+
/** options for the xvfb headless display server. */
|
|
1076
|
+
interface xvfboptions {
|
|
1077
|
+
readonly display?: string;
|
|
1078
|
+
readonly geometry?: string;
|
|
1079
|
+
readonly env?: Record<string, string>;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/** headless glx display server; the child is terminated on dispose. */
|
|
1083
|
+
class xvfbdisplay extends rendererbase {
|
|
1084
|
+
readonly #displaynumber: string;
|
|
1085
|
+
readonly #geometry: string;
|
|
1086
|
+
|
|
1087
|
+
constructor(options: xvfboptions = {}) {
|
|
1088
|
+
const displaynumber = options.display ?? randomdisplay();
|
|
1089
|
+
super('xvfb', { DISPLAY: `:${displaynumber}`, ...options.env });
|
|
1090
|
+
this.#displaynumber = displaynumber;
|
|
1091
|
+
this.#geometry = options.geometry ?? defaultgeometry;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/** display number without the leading colon (for example "94"). */
|
|
1095
|
+
get displaynumber(): string {
|
|
1096
|
+
return this.#displaynumber;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/** geometry string in the widthxheightxdepth form. */
|
|
1100
|
+
get geometry(): string {
|
|
1101
|
+
return this.#geometry;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/** boots xvfb and waits for the socket grace period. */
|
|
1105
|
+
async start(): Promise<void> {
|
|
1106
|
+
try {
|
|
1107
|
+
const child = this.launch('Xvfb', xvfbargs(this.#displaynumber, this.#geometry));
|
|
1108
|
+
await new Promise<void>((resolve, reject) => {
|
|
1109
|
+
const timer = setTimeout(() => resolve(), 600);
|
|
1110
|
+
child.once('exit', (code: number | null) => {
|
|
1111
|
+
clearTimeout(timer);
|
|
1112
|
+
reject(new Error(`xvfb exited early with code ${code ?? 'signal'}`));
|
|
1113
|
+
});
|
|
1114
|
+
child.once('error', (error: Error) => {
|
|
1115
|
+
clearTimeout(timer);
|
|
1116
|
+
reject(error);
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
this.note(`xvfb serving display :${this.#displaynumber} at ${this.#geometry}`);
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
throw new Error(`xvfb start failed: ${errormessage(error)}`);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/** reports the served display. */
|
|
1126
|
+
override async verify(): Promise<string> {
|
|
1127
|
+
return `display :${this.#displaynumber} geometry ${this.#geometry}`;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/* ------------------------------------------------------------------ */
|
|
1132
|
+
/* context: renderer factory (factory pattern) */
|
|
1133
|
+
/* ------------------------------------------------------------------ */
|
|
1134
|
+
|
|
1135
|
+
/** creates concrete renderers per target; the preset environment can be
|
|
1136
|
+
* overridden per call (user choice), for example to merge an xvfb display. */
|
|
1137
|
+
class rendererfactory {
|
|
1138
|
+
readonly #builders = new Map<rendertarget, (env?: Record<string, string>) => rendererbase>();
|
|
1139
|
+
|
|
1140
|
+
constructor() {
|
|
1141
|
+
this.#builders.set('opengl', (env) => new llvmpiperenderer(env));
|
|
1142
|
+
this.#builders.set('gles', (env) => new llvmpiperenderer({ ...renderenv('gles'), ...env }));
|
|
1143
|
+
this.#builders.set('vulkan', (env) => new lavapiperenderer(env));
|
|
1144
|
+
this.#builders.set('opencl', (env) => new rusticlrenderer(env));
|
|
1145
|
+
this.#builders.set('xvfb', (env) => new xvfbdisplay({ env }));
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/** registers a custom builder for a target (extensibility hook). */
|
|
1149
|
+
register(target: rendertarget, builder: (env?: Record<string, string>) => rendererbase): this {
|
|
1150
|
+
this.#builders.set(target, builder);
|
|
1151
|
+
return this;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/** instantiates the renderer for a target. */
|
|
1155
|
+
create(target: rendertarget, env?: Record<string, string>): rendererbase {
|
|
1156
|
+
const build = this.#builders.get(target);
|
|
1157
|
+
if (build === undefined) {
|
|
1158
|
+
throw new Error(`unknown render target "${target}"`);
|
|
1159
|
+
}
|
|
1160
|
+
return build(env);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/* ------------------------------------------------------------------ */
|
|
1165
|
+
/* context: renderer proxy (proxy pattern) */
|
|
1166
|
+
/* ------------------------------------------------------------------ */
|
|
1167
|
+
|
|
1168
|
+
/** environment keys that define the virtual identity and can never be overridden. */
|
|
1169
|
+
const frozenenvkeys = [
|
|
1170
|
+
'LIBGL_ALWAYS_SOFTWARE',
|
|
1171
|
+
'GALLIUM_DRIVER',
|
|
1172
|
+
'VK_DRIVER_FILES',
|
|
1173
|
+
'RUSTICL_ENABLE',
|
|
1174
|
+
'LP_NUM_THREADS',
|
|
1175
|
+
] as const;
|
|
1176
|
+
|
|
1177
|
+
/** proxy that guards the renderer environment, keeps an audit trail and
|
|
1178
|
+
* delegates lifecycle calls to the wrapped renderer. */
|
|
1179
|
+
class rendererproxy {
|
|
1180
|
+
readonly #inner: rendererbase;
|
|
1181
|
+
readonly #frozen: ReadonlySet<string>;
|
|
1182
|
+
readonly #audit: string[] = [];
|
|
1183
|
+
|
|
1184
|
+
constructor(inner: rendererbase, frozen: readonly string[] = frozenenvkeys) {
|
|
1185
|
+
this.#inner = inner;
|
|
1186
|
+
this.#frozen = new Set(frozen);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
get label(): string {
|
|
1190
|
+
return this.#inner.label;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/** environment copy; reads are recorded in the audit trail. */
|
|
1194
|
+
get env(): Record<string, string> {
|
|
1195
|
+
this.#audit.push(`read env for ${this.#inner.label}`);
|
|
1196
|
+
return this.#inner.env;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/** accumulated audit entries. */
|
|
1200
|
+
get audit(): readonly string[] {
|
|
1201
|
+
return this.#audit;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/** merges overrides over the renderer environment while refusing to
|
|
1205
|
+
* clobber the frozen virtual identity keys; returns the merged copy. */
|
|
1206
|
+
patchenv(overrides: Record<string, string>): Record<string, string> {
|
|
1207
|
+
const merged: Record<string, string> = { ...this.#inner.env };
|
|
1208
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1209
|
+
if (this.#frozen.has(key)) {
|
|
1210
|
+
this.#audit.push(`refused override of ${key}`);
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
merged[key] = value;
|
|
1214
|
+
}
|
|
1215
|
+
return merged;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/** delegates verification. */
|
|
1219
|
+
async verify(): Promise<string> {
|
|
1220
|
+
return this.#inner.verify();
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/** delegates disposal. */
|
|
1224
|
+
[Symbol.dispose](): void {
|
|
1225
|
+
this.#inner[Symbol.dispose]();
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/* ------------------------------------------------------------------ */
|
|
1230
|
+
/* context: render probe */
|
|
1231
|
+
/* ------------------------------------------------------------------ */
|
|
1232
|
+
|
|
1233
|
+
/** probe report across the three software apis. */
|
|
1234
|
+
interface probereport {
|
|
1235
|
+
readonly opengl: string;
|
|
1236
|
+
readonly vulkan: string;
|
|
1237
|
+
readonly opencl: string;
|
|
1238
|
+
readonly notes: readonly string[];
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/** probes glxinfo, vulkaninfo and clinfo under the given environment and
|
|
1242
|
+
* parses the virtualized version strings; failures degrade into notes. */
|
|
1243
|
+
async function renderprobe(env: Record<string, string>): Promise<probereport> {
|
|
1244
|
+
const notes: string[] = [];
|
|
1245
|
+
let opengl = '';
|
|
1246
|
+
let vulkan = '';
|
|
1247
|
+
let opencl = '';
|
|
1248
|
+
try {
|
|
1249
|
+
const glx = await run('glxinfo', ['-B'], env);
|
|
1250
|
+
opengl = firstmatch(glx.stdout, /core profile version string:\s*(.+)/) ?? '';
|
|
1251
|
+
if (opengl === '') notes.push('glxinfo returned no core profile version line');
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
notes.push(`glxinfo failed: ${errormessage(error)}`);
|
|
1254
|
+
}
|
|
1255
|
+
try {
|
|
1256
|
+
const vk = await run('vulkaninfo', ['--summary'], env);
|
|
1257
|
+
vulkan = firstmatch(vk.stdout, /apiVersion\s*:\s*([\d.]+)/) ?? '';
|
|
1258
|
+
if (vulkan === '') notes.push('vulkaninfo returned no apiVersion line');
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
notes.push(`vulkaninfo failed: ${errormessage(error)}`);
|
|
1261
|
+
}
|
|
1262
|
+
try {
|
|
1263
|
+
const cl = await run('clinfo', [], env);
|
|
1264
|
+
opencl = firstmatch(cl.stdout, /OpenCL (\d+\.\d+)/) ?? '';
|
|
1265
|
+
if (opencl === '') notes.push('clinfo returned no opencl version line');
|
|
1266
|
+
} catch (error) {
|
|
1267
|
+
notes.push(`clinfo failed: ${errormessage(error)}`);
|
|
1268
|
+
}
|
|
1269
|
+
return { opengl, vulkan, opencl, notes };
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
/* ------------------------------------------------------------------ */
|
|
1273
|
+
/* context: end-to-end demo with `using` disposal */
|
|
1274
|
+
/* ------------------------------------------------------------------ */
|
|
1275
|
+
|
|
1276
|
+
/** boots xvfb, assembles the full software environment (opengl + vulkan +
|
|
1277
|
+
* opencl presets) and probes all three apis; every resource is released
|
|
1278
|
+
* through the `using` disposal at scope exit. */
|
|
1279
|
+
export async function renderdemo(profilename: string = 'rtx5090'): Promise<probereport> {
|
|
1280
|
+
const profile = defaultgpuregistry.require(profilename);
|
|
1281
|
+
using display = new xvfbdisplay({ env: { VHE_GPU_PROFILE: profile.name } });
|
|
1282
|
+
await display.start();
|
|
1283
|
+
const env: Record<string, string> = {
|
|
1284
|
+
...display.env,
|
|
1285
|
+
...renderenv('opengl'),
|
|
1286
|
+
...renderenv('vulkan'),
|
|
1287
|
+
...renderenv('opencl'),
|
|
1288
|
+
};
|
|
1289
|
+
return renderprobe(env);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
export type {
|
|
1293
|
+
cpucaps,
|
|
1294
|
+
gpuspoof,
|
|
1295
|
+
lpperfstage,
|
|
1296
|
+
probereport,
|
|
1297
|
+
rendertarget,
|
|
1298
|
+
runresult,
|
|
1299
|
+
smiprocess,
|
|
1300
|
+
xvfboptions,
|
|
1301
|
+
};
|
|
1302
|
+
export {
|
|
1303
|
+
advisethreads,
|
|
1304
|
+
cpucapsdomain,
|
|
1305
|
+
defaultgeometry,
|
|
1306
|
+
defaultgpuregistry,
|
|
1307
|
+
gpuprofilebuilder,
|
|
1308
|
+
gpuregistry,
|
|
1309
|
+
gpuspoofdata,
|
|
1310
|
+
lavapipe,
|
|
1311
|
+
llvmpipe,
|
|
1312
|
+
mesaenvbuilder,
|
|
1313
|
+
mesastack,
|
|
1314
|
+
pciid,
|
|
1315
|
+
randomdisplay,
|
|
1316
|
+
renderenv,
|
|
1317
|
+
rendererfactory,
|
|
1318
|
+
rendererproxy,
|
|
1319
|
+
renderprobe,
|
|
1320
|
+
run,
|
|
1321
|
+
rusticl,
|
|
1322
|
+
smiadapterbuilder,
|
|
1323
|
+
xvfbargs,
|
|
1324
|
+
xvfbdisplay,
|
|
1325
|
+
};
|