@wenathlan/saddle 1.8.18 → 2.0.2

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.
Files changed (802) hide show
  1. package/Dockerfile +1339 -0
  2. package/README.md +33 -12
  3. package/alternatives.ts +1218 -0
  4. package/biome.json +153 -0
  5. package/boards.json +1517 -0
  6. package/compute.ts +3144 -0
  7. package/cores.json +1801 -0
  8. package/dist/acquisition.d.ts +359 -0
  9. package/dist/acquisition.d.ts.map +1 -0
  10. package/dist/acquisition.js +629 -0
  11. package/dist/acquisition.js.map +1 -0
  12. package/dist/alternatives.d.ts +509 -0
  13. package/dist/alternatives.d.ts.map +1 -0
  14. package/dist/alternatives.js +951 -0
  15. package/dist/alternatives.js.map +1 -0
  16. package/dist/automation.d.ts +426 -0
  17. package/dist/automation.d.ts.map +1 -0
  18. package/dist/automation.js +561 -0
  19. package/dist/automation.js.map +1 -0
  20. package/dist/browser.d.ts +562 -0
  21. package/dist/browser.d.ts.map +1 -0
  22. package/dist/browser.js +1057 -0
  23. package/dist/browser.js.map +1 -0
  24. package/dist/capacitor.config.d.ts +13 -0
  25. package/dist/capacitor.config.d.ts.map +1 -0
  26. package/dist/capacitor.config.js +26 -0
  27. package/dist/capacitor.config.js.map +1 -0
  28. package/dist/cli.d.ts +17 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +111 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/communication.d.ts +192 -0
  33. package/dist/communication.d.ts.map +1 -0
  34. package/dist/communication.js +418 -0
  35. package/dist/communication.js.map +1 -0
  36. package/dist/compute.d.ts +1161 -0
  37. package/dist/compute.d.ts.map +1 -0
  38. package/dist/compute.js +2273 -0
  39. package/dist/compute.js.map +1 -0
  40. package/dist/distribution.d.ts +529 -0
  41. package/dist/distribution.d.ts.map +1 -0
  42. package/dist/distribution.js +811 -0
  43. package/dist/distribution.js.map +1 -0
  44. package/dist/execution.d.ts +530 -0
  45. package/dist/execution.d.ts.map +1 -0
  46. package/dist/execution.js +761 -0
  47. package/dist/execution.js.map +1 -0
  48. package/dist/format.d.ts +19 -0
  49. package/dist/format.d.ts.map +1 -0
  50. package/dist/format.js +60 -0
  51. package/dist/format.js.map +1 -0
  52. package/dist/foundation.d.ts +176 -0
  53. package/dist/foundation.d.ts.map +1 -0
  54. package/dist/foundation.js +309 -0
  55. package/dist/foundation.js.map +1 -0
  56. package/dist/index.d.ts +733 -123
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +1018 -123
  59. package/dist/index.js.map +1 -1
  60. package/dist/integration.d.ts +236 -0
  61. package/dist/integration.d.ts.map +1 -0
  62. package/dist/integration.js +236 -0
  63. package/dist/integration.js.map +1 -0
  64. package/dist/intelligence.d.ts +94 -0
  65. package/dist/intelligence.d.ts.map +1 -0
  66. package/dist/intelligence.js +145 -0
  67. package/dist/intelligence.js.map +1 -0
  68. package/dist/{isolation/contracts.d.ts → isolation.d.ts} +12 -1
  69. package/dist/isolation.d.ts.map +1 -0
  70. package/dist/{isolation/contracts.js → isolation.js} +13 -1
  71. package/dist/isolation.js.map +1 -0
  72. package/dist/media.d.ts +344 -0
  73. package/dist/media.d.ts.map +1 -0
  74. package/dist/media.js +986 -0
  75. package/dist/media.js.map +1 -0
  76. package/dist/modes.d.ts +359 -0
  77. package/dist/modes.d.ts.map +1 -0
  78. package/dist/modes.js +134 -0
  79. package/dist/modes.js.map +1 -0
  80. package/dist/{library/public.d.ts → operations.d.ts} +15 -1
  81. package/dist/operations.d.ts.map +1 -0
  82. package/dist/{library/public.js → operations.js} +18 -7
  83. package/dist/operations.js.map +1 -0
  84. package/dist/orchestrator.d.ts +1339 -0
  85. package/dist/orchestrator.d.ts.map +1 -0
  86. package/dist/orchestrator.js +4308 -0
  87. package/dist/orchestrator.js.map +1 -0
  88. package/dist/performance.d.ts +416 -0
  89. package/dist/performance.d.ts.map +1 -0
  90. package/dist/performance.js +827 -0
  91. package/dist/performance.js.map +1 -0
  92. package/dist/quantum.d.ts +810 -0
  93. package/dist/quantum.d.ts.map +1 -0
  94. package/dist/quantum.js +2073 -0
  95. package/dist/quantum.js.map +1 -0
  96. package/dist/render.d.ts +381 -0
  97. package/dist/render.d.ts.map +1 -0
  98. package/dist/render.js +1073 -0
  99. package/dist/render.js.map +1 -0
  100. package/dist/scheduler.d.ts +596 -0
  101. package/dist/scheduler.d.ts.map +1 -0
  102. package/dist/scheduler.js +1259 -0
  103. package/dist/scheduler.js.map +1 -0
  104. package/dist/security.d.ts +501 -0
  105. package/dist/security.d.ts.map +1 -0
  106. package/dist/security.js +1170 -0
  107. package/dist/security.js.map +1 -0
  108. package/dist/server.d.ts +21 -0
  109. package/dist/server.d.ts.map +1 -0
  110. package/dist/{server/node.js → server.js} +16 -2
  111. package/dist/server.js.map +1 -0
  112. package/dist/tiers.d.ts +1002 -0
  113. package/dist/tiers.d.ts.map +1 -0
  114. package/dist/tiers.js +2165 -0
  115. package/dist/tiers.js.map +1 -0
  116. package/dist/virtual.d.ts +641 -0
  117. package/dist/virtual.d.ts.map +1 -0
  118. package/dist/virtual.js +1285 -0
  119. package/dist/virtual.js.map +1 -0
  120. package/dist/virtualcpu.d.ts +316 -0
  121. package/dist/virtualcpu.d.ts.map +1 -0
  122. package/dist/virtualcpu.js +1180 -0
  123. package/dist/virtualcpu.js.map +1 -0
  124. package/dist/virtualgpu.d.ts +345 -0
  125. package/dist/virtualgpu.d.ts.map +1 -0
  126. package/dist/virtualgpu.js +1000 -0
  127. package/dist/virtualgpu.js.map +1 -0
  128. package/dist/virtualization.d.ts +607 -0
  129. package/dist/virtualization.d.ts.map +1 -0
  130. package/dist/virtualization.js +1187 -0
  131. package/dist/virtualization.js.map +1 -0
  132. package/dist/virtualmemory.d.ts +818 -0
  133. package/dist/virtualmemory.d.ts.map +1 -0
  134. package/dist/virtualmemory.js +1796 -0
  135. package/dist/virtualmemory.js.map +1 -0
  136. package/dist/webscrape.d.ts +720 -0
  137. package/dist/webscrape.d.ts.map +1 -0
  138. package/dist/webscrape.js +2697 -0
  139. package/dist/webscrape.js.map +1 -0
  140. package/docker.config +769 -0
  141. package/docs/CONVERSA.txt +1523 -0
  142. package/docs/alternatives.md +499 -0
  143. package/docs/architecture-1.8.19.md +76 -0
  144. package/docs/architecture-1.8.19.mmd +33 -0
  145. package/docs/architecture-1.8.19.png +0 -0
  146. package/docs/architecture.md +355 -0
  147. package/docs/brancharchive-2026-08-18.md +18 -0
  148. package/docs/browser.md +7 -0
  149. package/docs/consolidation.md +70 -0
  150. package/docs/e2ugh-engine.md +528 -0
  151. package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
  152. package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
  153. package/docs/hardware.md +342 -0
  154. package/docs/optimization.md +126 -0
  155. package/docs/performance.md +499 -0
  156. package/docs/planning.1.8.18.md +94 -0
  157. package/docs/planning.1.8.19.md +49 -0
  158. package/docs/releasenotes-1.8.19.md +35 -0
  159. package/docs/research-1.8.19-virtual-browser.md +27 -0
  160. package/docs/security.md +454 -0
  161. package/docs/todo-1.8.18.md +12 -0
  162. package/docs/todo-1.8.19.md +241 -0
  163. package/docs/viability.md +777 -0
  164. package/docs/virtualization.md +335 -0
  165. package/docs/web-duplicatecleanup.md +9 -0
  166. package/docs/web-ideas.md +106 -0
  167. package/docs/workflowimprovements-2026-08-19.md +70 -0
  168. package/gpumonitor.cpp +1543 -0
  169. package/gpus.json +2408 -0
  170. package/index.ts +1478 -0
  171. package/media.ts +1337 -0
  172. package/mttg.config +1549 -0
  173. package/orchestrator.ts +5462 -0
  174. package/package.json +304 -56
  175. package/passage.config +1331 -0
  176. package/performance.ts +1049 -0
  177. package/processors.json +2217 -0
  178. package/qemu.config +1062 -0
  179. package/qemubridge.py +1340 -0
  180. package/quantum.ts +2393 -0
  181. package/render.ts +1325 -0
  182. package/scheduler.ts +1600 -0
  183. package/security.ts +1449 -0
  184. package/tiers.ts +2871 -0
  185. package/tsconfig.json +29 -0
  186. package/virtualcpu.ts +1273 -0
  187. package/virtualgpu.ts +1181 -0
  188. package/virtualhardware.c +1011 -0
  189. package/virtualhardware.json +739 -0
  190. package/virtualization.ts +1628 -0
  191. package/virtualizationcore.cpp +4635 -0
  192. package/virtualmemory.ts +2303 -0
  193. package/vm.config.json +1910 -0
  194. package/{extension → web/extension}/manifest.json +1 -1
  195. package/web/readme.md +382 -0
  196. package/web/tsconfig.json +25 -0
  197. package/dist/adapters/forge.d.ts +0 -17
  198. package/dist/adapters/forge.d.ts.map +0 -1
  199. package/dist/adapters/forge.js +0 -19
  200. package/dist/adapters/forge.js.map +0 -1
  201. package/dist/adapters/forgejo.d.ts +0 -49
  202. package/dist/adapters/forgejo.d.ts.map +0 -1
  203. package/dist/adapters/forgejo.js +0 -8
  204. package/dist/adapters/forgejo.js.map +0 -1
  205. package/dist/adapters/github.d.ts +0 -12
  206. package/dist/adapters/github.d.ts.map +0 -1
  207. package/dist/adapters/github.js +0 -20
  208. package/dist/adapters/github.js.map +0 -1
  209. package/dist/adapters/gitlab.d.ts +0 -17
  210. package/dist/adapters/gitlab.d.ts.map +0 -1
  211. package/dist/adapters/gitlab.js +0 -10
  212. package/dist/adapters/gitlab.js.map +0 -1
  213. package/dist/adapters/huggingface.d.ts +0 -17
  214. package/dist/adapters/huggingface.d.ts.map +0 -1
  215. package/dist/adapters/huggingface.js +0 -6
  216. package/dist/adapters/huggingface.js.map +0 -1
  217. package/dist/adapters/socket.d.ts +0 -11
  218. package/dist/adapters/socket.d.ts.map +0 -1
  219. package/dist/adapters/socket.js +0 -17
  220. package/dist/adapters/socket.js.map +0 -1
  221. package/dist/adapters/transport.d.ts +0 -7
  222. package/dist/adapters/transport.d.ts.map +0 -1
  223. package/dist/adapters/transport.js +0 -36
  224. package/dist/adapters/transport.js.map +0 -1
  225. package/dist/ai/chunk.d.ts +0 -2
  226. package/dist/ai/chunk.d.ts.map +0 -1
  227. package/dist/ai/chunk.js +0 -36
  228. package/dist/ai/chunk.js.map +0 -1
  229. package/dist/ai/llmstxt.d.ts +0 -6
  230. package/dist/ai/llmstxt.d.ts.map +0 -1
  231. package/dist/ai/llmstxt.js +0 -12
  232. package/dist/ai/llmstxt.js.map +0 -1
  233. package/dist/ai/provenance.d.ts +0 -22
  234. package/dist/ai/provenance.d.ts.map +0 -1
  235. package/dist/ai/provenance.js +0 -25
  236. package/dist/ai/provenance.js.map +0 -1
  237. package/dist/ai/rag.d.ts +0 -26
  238. package/dist/ai/rag.d.ts.map +0 -1
  239. package/dist/ai/rag.js +0 -22
  240. package/dist/ai/rag.js.map +0 -1
  241. package/dist/ai/tokens.d.ts +0 -15
  242. package/dist/ai/tokens.d.ts.map +0 -1
  243. package/dist/ai/tokens.js +0 -9
  244. package/dist/ai/tokens.js.map +0 -1
  245. package/dist/api/auth.d.ts +0 -16
  246. package/dist/api/auth.d.ts.map +0 -1
  247. package/dist/api/auth.js +0 -19
  248. package/dist/api/auth.js.map +0 -1
  249. package/dist/api/contracts.d.ts +0 -28
  250. package/dist/api/contracts.d.ts.map +0 -1
  251. package/dist/api/contracts.js +0 -14
  252. package/dist/api/contracts.js.map +0 -1
  253. package/dist/api/control.d.ts +0 -10
  254. package/dist/api/control.d.ts.map +0 -1
  255. package/dist/api/control.js +0 -35
  256. package/dist/api/control.js.map +0 -1
  257. package/dist/api/http.d.ts +0 -7
  258. package/dist/api/http.d.ts.map +0 -1
  259. package/dist/api/http.js +0 -12
  260. package/dist/api/http.js.map +0 -1
  261. package/dist/api/rate.d.ts +0 -28
  262. package/dist/api/rate.d.ts.map +0 -1
  263. package/dist/api/rate.js +0 -36
  264. package/dist/api/rate.js.map +0 -1
  265. package/dist/api/security.d.ts +0 -12
  266. package/dist/api/security.d.ts.map +0 -1
  267. package/dist/api/security.js +0 -56
  268. package/dist/api/security.js.map +0 -1
  269. package/dist/api/service.d.ts +0 -5
  270. package/dist/api/service.d.ts.map +0 -1
  271. package/dist/api/service.js +0 -70
  272. package/dist/api/service.js.map +0 -1
  273. package/dist/apps/registry.d.ts +0 -33
  274. package/dist/apps/registry.d.ts.map +0 -1
  275. package/dist/apps/registry.js +0 -24
  276. package/dist/apps/registry.js.map +0 -1
  277. package/dist/binary/archive.d.ts +0 -27
  278. package/dist/binary/archive.d.ts.map +0 -1
  279. package/dist/binary/archive.js +0 -46
  280. package/dist/binary/archive.js.map +0 -1
  281. package/dist/binary/build.d.ts +0 -29
  282. package/dist/binary/build.d.ts.map +0 -1
  283. package/dist/binary/build.js +0 -17
  284. package/dist/binary/build.js.map +0 -1
  285. package/dist/binary/transform.d.ts +0 -70
  286. package/dist/binary/transform.d.ts.map +0 -1
  287. package/dist/binary/transform.js +0 -105
  288. package/dist/binary/transform.js.map +0 -1
  289. package/dist/bot/adapter.d.ts +0 -5
  290. package/dist/bot/adapter.d.ts.map +0 -1
  291. package/dist/bot/adapter.js +0 -11
  292. package/dist/bot/adapter.js.map +0 -1
  293. package/dist/bot/bot.d.ts +0 -28
  294. package/dist/bot/bot.d.ts.map +0 -1
  295. package/dist/bot/bot.js +0 -52
  296. package/dist/bot/bot.js.map +0 -1
  297. package/dist/bot/commands.d.ts +0 -8
  298. package/dist/bot/commands.d.ts.map +0 -1
  299. package/dist/bot/commands.js +0 -19
  300. package/dist/bot/commands.js.map +0 -1
  301. package/dist/bot/permissions.d.ts +0 -13
  302. package/dist/bot/permissions.d.ts.map +0 -1
  303. package/dist/bot/permissions.js +0 -16
  304. package/dist/bot/permissions.js.map +0 -1
  305. package/dist/browser/actions.d.ts +0 -33
  306. package/dist/browser/actions.d.ts.map +0 -1
  307. package/dist/browser/actions.js +0 -44
  308. package/dist/browser/actions.js.map +0 -1
  309. package/dist/browser/agent.d.ts +0 -21
  310. package/dist/browser/agent.d.ts.map +0 -1
  311. package/dist/browser/agent.js +0 -13
  312. package/dist/browser/agent.js.map +0 -1
  313. package/dist/browser/context.d.ts +0 -43
  314. package/dist/browser/context.d.ts.map +0 -1
  315. package/dist/browser/context.js +0 -54
  316. package/dist/browser/context.js.map +0 -1
  317. package/dist/browser/fingerprint.d.ts +0 -4
  318. package/dist/browser/fingerprint.d.ts.map +0 -1
  319. package/dist/browser/fingerprint.js +0 -12
  320. package/dist/browser/fingerprint.js.map +0 -1
  321. package/dist/browser/index.d.ts +0 -11
  322. package/dist/browser/index.d.ts.map +0 -1
  323. package/dist/browser/index.js +0 -11
  324. package/dist/browser/index.js.map +0 -1
  325. package/dist/browser/playwright.d.ts +0 -11
  326. package/dist/browser/playwright.d.ts.map +0 -1
  327. package/dist/browser/playwright.js +0 -24
  328. package/dist/browser/playwright.js.map +0 -1
  329. package/dist/browser/recorder.d.ts +0 -21
  330. package/dist/browser/recorder.d.ts.map +0 -1
  331. package/dist/browser/recorder.js +0 -58
  332. package/dist/browser/recorder.js.map +0 -1
  333. package/dist/browser/session.d.ts +0 -14
  334. package/dist/browser/session.d.ts.map +0 -1
  335. package/dist/browser/session.js +0 -21
  336. package/dist/browser/session.js.map +0 -1
  337. package/dist/browser/snapshot.d.ts +0 -69
  338. package/dist/browser/snapshot.d.ts.map +0 -1
  339. package/dist/browser/snapshot.js +0 -133
  340. package/dist/browser/snapshot.js.map +0 -1
  341. package/dist/captcha/contract.d.ts +0 -35
  342. package/dist/captcha/contract.d.ts.map +0 -1
  343. package/dist/captcha/contract.js +0 -17
  344. package/dist/captcha/contract.js.map +0 -1
  345. package/dist/captcha/evidence.d.ts +0 -10
  346. package/dist/captcha/evidence.d.ts.map +0 -1
  347. package/dist/captcha/evidence.js +0 -9
  348. package/dist/captcha/evidence.js.map +0 -1
  349. package/dist/captcha/guard.d.ts +0 -23
  350. package/dist/captcha/guard.d.ts.map +0 -1
  351. package/dist/captcha/guard.js +0 -12
  352. package/dist/captcha/guard.js.map +0 -1
  353. package/dist/cli/main.d.ts +0 -4
  354. package/dist/cli/main.d.ts.map +0 -1
  355. package/dist/cli/main.js +0 -46
  356. package/dist/cli/main.js.map +0 -1
  357. package/dist/core/errors.d.ts +0 -84
  358. package/dist/core/errors.d.ts.map +0 -1
  359. package/dist/core/errors.js +0 -54
  360. package/dist/core/errors.js.map +0 -1
  361. package/dist/core/events.d.ts +0 -9
  362. package/dist/core/events.d.ts.map +0 -1
  363. package/dist/core/events.js +0 -21
  364. package/dist/core/events.js.map +0 -1
  365. package/dist/core/hash.d.ts +0 -10
  366. package/dist/core/hash.d.ts.map +0 -1
  367. package/dist/core/hash.js +0 -82
  368. package/dist/core/hash.js.map +0 -1
  369. package/dist/core/ids.d.ts +0 -10
  370. package/dist/core/ids.d.ts.map +0 -1
  371. package/dist/core/ids.js +0 -15
  372. package/dist/core/ids.js.map +0 -1
  373. package/dist/delivery/manifest.d.ts +0 -35
  374. package/dist/delivery/manifest.d.ts.map +0 -1
  375. package/dist/delivery/manifest.js +0 -68
  376. package/dist/delivery/manifest.js.map +0 -1
  377. package/dist/deploy/index.d.ts +0 -6
  378. package/dist/deploy/index.d.ts.map +0 -1
  379. package/dist/deploy/index.js +0 -6
  380. package/dist/deploy/index.js.map +0 -1
  381. package/dist/dispatch/resumable.d.ts +0 -102
  382. package/dist/dispatch/resumable.d.ts.map +0 -1
  383. package/dist/dispatch/resumable.js +0 -64
  384. package/dist/dispatch/resumable.js.map +0 -1
  385. package/dist/dispatch/workflow.d.ts +0 -6
  386. package/dist/dispatch/workflow.d.ts.map +0 -1
  387. package/dist/dispatch/workflow.js +0 -37
  388. package/dist/dispatch/workflow.js.map +0 -1
  389. package/dist/domain/artifacts.d.ts +0 -12
  390. package/dist/domain/artifacts.d.ts.map +0 -1
  391. package/dist/domain/artifacts.js +0 -14
  392. package/dist/domain/artifacts.js.map +0 -1
  393. package/dist/domain/jobs.d.ts +0 -12
  394. package/dist/domain/jobs.d.ts.map +0 -1
  395. package/dist/domain/jobs.js +0 -20
  396. package/dist/domain/jobs.js.map +0 -1
  397. package/dist/domain/providers.d.ts +0 -10
  398. package/dist/domain/providers.d.ts.map +0 -1
  399. package/dist/domain/providers.js +0 -8
  400. package/dist/domain/providers.js.map +0 -1
  401. package/dist/domain/runtime.d.ts +0 -14
  402. package/dist/domain/runtime.d.ts.map +0 -1
  403. package/dist/domain/runtime.js +0 -10
  404. package/dist/domain/runtime.js.map +0 -1
  405. package/dist/domain/sessions.d.ts +0 -12
  406. package/dist/domain/sessions.d.ts.map +0 -1
  407. package/dist/domain/sessions.js +0 -57
  408. package/dist/domain/sessions.js.map +0 -1
  409. package/dist/examples/localjob.d.ts +0 -2
  410. package/dist/examples/localjob.d.ts.map +0 -1
  411. package/dist/examples/localjob.js.map +0 -1
  412. package/dist/examples/publicapi.d.ts +0 -2
  413. package/dist/examples/publicapi.d.ts.map +0 -1
  414. package/dist/examples/publicapi.js.map +0 -1
  415. package/dist/extension/build.d.ts +0 -9
  416. package/dist/extension/build.d.ts.map +0 -1
  417. package/dist/extension/build.js +0 -85
  418. package/dist/extension/build.js.map +0 -1
  419. package/dist/extension/content.d.ts +0 -6
  420. package/dist/extension/content.d.ts.map +0 -1
  421. package/dist/extension/content.js +0 -135
  422. package/dist/extension/content.js.map +0 -1
  423. package/dist/extension/index.d.ts +0 -7
  424. package/dist/extension/index.d.ts.map +0 -1
  425. package/dist/extension/index.js +0 -7
  426. package/dist/extension/index.js.map +0 -1
  427. package/dist/extension/pagebridge.d.ts +0 -6
  428. package/dist/extension/pagebridge.d.ts.map +0 -1
  429. package/dist/extension/pagebridge.js +0 -42
  430. package/dist/extension/pagebridge.js.map +0 -1
  431. package/dist/extension/permissions.d.ts +0 -26
  432. package/dist/extension/permissions.d.ts.map +0 -1
  433. package/dist/extension/permissions.js +0 -28
  434. package/dist/extension/permissions.js.map +0 -1
  435. package/dist/extension/popup.d.ts +0 -5
  436. package/dist/extension/popup.d.ts.map +0 -1
  437. package/dist/extension/popup.js +0 -26
  438. package/dist/extension/popup.js.map +0 -1
  439. package/dist/extension/protocol.d.ts +0 -58
  440. package/dist/extension/protocol.d.ts.map +0 -1
  441. package/dist/extension/protocol.js +0 -101
  442. package/dist/extension/protocol.js.map +0 -1
  443. package/dist/extension/serviceworker.d.ts +0 -25
  444. package/dist/extension/serviceworker.d.ts.map +0 -1
  445. package/dist/extension/serviceworker.js +0 -102
  446. package/dist/extension/serviceworker.js.map +0 -1
  447. package/dist/extension/worker.d.ts +0 -28
  448. package/dist/extension/worker.d.ts.map +0 -1
  449. package/dist/extension/worker.js +0 -22
  450. package/dist/extension/worker.js.map +0 -1
  451. package/dist/format/check.d.ts +0 -8
  452. package/dist/format/check.d.ts.map +0 -1
  453. package/dist/format/check.js +0 -38
  454. package/dist/format/check.js.map +0 -1
  455. package/dist/isolation/contracts.d.ts.map +0 -1
  456. package/dist/isolation/contracts.js.map +0 -1
  457. package/dist/library/public.d.ts.map +0 -1
  458. package/dist/library/public.js.map +0 -1
  459. package/dist/mcp/browser.d.ts +0 -9
  460. package/dist/mcp/browser.d.ts.map +0 -1
  461. package/dist/mcp/browser.js +0 -13
  462. package/dist/mcp/browser.js.map +0 -1
  463. package/dist/mcp/server.d.ts +0 -35
  464. package/dist/mcp/server.d.ts.map +0 -1
  465. package/dist/mcp/server.js +0 -40
  466. package/dist/mcp/server.js.map +0 -1
  467. package/dist/mcp/transport.d.ts +0 -7
  468. package/dist/mcp/transport.d.ts.map +0 -1
  469. package/dist/mcp/transport.js +0 -17
  470. package/dist/mcp/transport.js.map +0 -1
  471. package/dist/memory/bridge.d.ts +0 -14
  472. package/dist/memory/bridge.d.ts.map +0 -1
  473. package/dist/memory/bridge.js +0 -16
  474. package/dist/memory/bridge.js.map +0 -1
  475. package/dist/memory/engine.d.ts +0 -38
  476. package/dist/memory/engine.d.ts.map +0 -1
  477. package/dist/memory/engine.js +0 -109
  478. package/dist/memory/engine.js.map +0 -1
  479. package/dist/memory/modes.d.ts +0 -43
  480. package/dist/memory/modes.d.ts.map +0 -1
  481. package/dist/memory/modes.js +0 -66
  482. package/dist/memory/modes.js.map +0 -1
  483. package/dist/memory/objects.d.ts +0 -21
  484. package/dist/memory/objects.d.ts.map +0 -1
  485. package/dist/memory/objects.js +0 -20
  486. package/dist/memory/objects.js.map +0 -1
  487. package/dist/memory/planner.d.ts +0 -66
  488. package/dist/memory/planner.d.ts.map +0 -1
  489. package/dist/memory/planner.js +0 -108
  490. package/dist/memory/planner.js.map +0 -1
  491. package/dist/memory/targets.d.ts +0 -78
  492. package/dist/memory/targets.d.ts.map +0 -1
  493. package/dist/memory/targets.js +0 -29
  494. package/dist/memory/targets.js.map +0 -1
  495. package/dist/memory/transforms.d.ts +0 -19
  496. package/dist/memory/transforms.d.ts.map +0 -1
  497. package/dist/memory/transforms.js +0 -14
  498. package/dist/memory/transforms.js.map +0 -1
  499. package/dist/modes/matrix.d.ts +0 -23
  500. package/dist/modes/matrix.d.ts.map +0 -1
  501. package/dist/modes/matrix.js +0 -18
  502. package/dist/modes/matrix.js.map +0 -1
  503. package/dist/modes/modes.d.ts +0 -33
  504. package/dist/modes/modes.d.ts.map +0 -1
  505. package/dist/modes/modes.js +0 -15
  506. package/dist/modes/modes.js.map +0 -1
  507. package/dist/modes/resolve.d.ts +0 -96
  508. package/dist/modes/resolve.d.ts.map +0 -1
  509. package/dist/modes/resolve.js +0 -48
  510. package/dist/modes/resolve.js.map +0 -1
  511. package/dist/observability/metrics.d.ts +0 -14
  512. package/dist/observability/metrics.d.ts.map +0 -1
  513. package/dist/observability/metrics.js +0 -18
  514. package/dist/observability/metrics.js.map +0 -1
  515. package/dist/packager/manifest.d.ts +0 -66
  516. package/dist/packager/manifest.d.ts.map +0 -1
  517. package/dist/packager/manifest.js +0 -94
  518. package/dist/packager/manifest.js.map +0 -1
  519. package/dist/packager/publish.d.ts +0 -51
  520. package/dist/packager/publish.d.ts.map +0 -1
  521. package/dist/packager/publish.js +0 -18
  522. package/dist/packager/publish.js.map +0 -1
  523. package/dist/packager/targetcli.d.ts +0 -7
  524. package/dist/packager/targetcli.d.ts.map +0 -1
  525. package/dist/packager/targetcli.js +0 -22
  526. package/dist/packager/targetcli.js.map +0 -1
  527. package/dist/persistence/adapter.d.ts +0 -5
  528. package/dist/persistence/adapter.d.ts.map +0 -1
  529. package/dist/persistence/adapter.js +0 -11
  530. package/dist/persistence/adapter.js.map +0 -1
  531. package/dist/persistence/drizzle.d.ts +0 -2
  532. package/dist/persistence/drizzle.d.ts.map +0 -1
  533. package/dist/persistence/drizzle.js +0 -12
  534. package/dist/persistence/drizzle.js.map +0 -1
  535. package/dist/persistence/memory.d.ts +0 -2
  536. package/dist/persistence/memory.d.ts.map +0 -1
  537. package/dist/persistence/memory.js +0 -27
  538. package/dist/persistence/memory.js.map +0 -1
  539. package/dist/persistence/migrations.d.ts +0 -19
  540. package/dist/persistence/migrations.d.ts.map +0 -1
  541. package/dist/persistence/migrations.js +0 -13
  542. package/dist/persistence/migrations.js.map +0 -1
  543. package/dist/persistence/prisma.d.ts +0 -2
  544. package/dist/persistence/prisma.d.ts.map +0 -1
  545. package/dist/persistence/prisma.js +0 -25
  546. package/dist/persistence/prisma.js.map +0 -1
  547. package/dist/persistence/schema.d.ts +0 -63
  548. package/dist/persistence/schema.d.ts.map +0 -1
  549. package/dist/persistence/schema.js +0 -28
  550. package/dist/persistence/schema.js.map +0 -1
  551. package/dist/persistence/sql.d.ts +0 -3
  552. package/dist/persistence/sql.d.ts.map +0 -1
  553. package/dist/persistence/sql.js +0 -37
  554. package/dist/persistence/sql.js.map +0 -1
  555. package/dist/protocol/blocks.d.ts +0 -10
  556. package/dist/protocol/blocks.d.ts.map +0 -1
  557. package/dist/protocol/blocks.js +0 -26
  558. package/dist/protocol/blocks.js.map +0 -1
  559. package/dist/protocol/json.d.ts +0 -6
  560. package/dist/protocol/json.d.ts.map +0 -1
  561. package/dist/protocol/json.js +0 -6
  562. package/dist/protocol/json.js.map +0 -1
  563. package/dist/protocol/ndjson.d.ts +0 -3
  564. package/dist/protocol/ndjson.d.ts.map +0 -1
  565. package/dist/protocol/ndjson.js +0 -20
  566. package/dist/protocol/ndjson.js.map +0 -1
  567. package/dist/protocol/sse.d.ts +0 -7
  568. package/dist/protocol/sse.d.ts.map +0 -1
  569. package/dist/protocol/sse.js +0 -25
  570. package/dist/protocol/sse.js.map +0 -1
  571. package/dist/proxy/pool.d.ts +0 -9
  572. package/dist/proxy/pool.d.ts.map +0 -1
  573. package/dist/proxy/pool.js +0 -27
  574. package/dist/proxy/pool.js.map +0 -1
  575. package/dist/queue/idempotency.d.ts +0 -10
  576. package/dist/queue/idempotency.d.ts.map +0 -1
  577. package/dist/queue/idempotency.js +0 -13
  578. package/dist/queue/idempotency.js.map +0 -1
  579. package/dist/queue/persistent.d.ts +0 -14
  580. package/dist/queue/persistent.d.ts.map +0 -1
  581. package/dist/queue/persistent.js +0 -66
  582. package/dist/queue/persistent.js.map +0 -1
  583. package/dist/queue/queue.d.ts +0 -7
  584. package/dist/queue/queue.d.ts.map +0 -1
  585. package/dist/queue/queue.js +0 -55
  586. package/dist/queue/queue.js.map +0 -1
  587. package/dist/queue/saga.d.ts +0 -5
  588. package/dist/queue/saga.d.ts.map +0 -1
  589. package/dist/queue/saga.js +0 -20
  590. package/dist/queue/saga.js.map +0 -1
  591. package/dist/release/assets.d.ts +0 -144
  592. package/dist/release/assets.d.ts.map +0 -1
  593. package/dist/release/assets.js +0 -157
  594. package/dist/release/assets.js.map +0 -1
  595. package/dist/release/evidence.d.ts +0 -92
  596. package/dist/release/evidence.d.ts.map +0 -1
  597. package/dist/release/evidence.js +0 -148
  598. package/dist/release/evidence.js.map +0 -1
  599. package/dist/release/verify.d.ts +0 -12
  600. package/dist/release/verify.d.ts.map +0 -1
  601. package/dist/release/verify.js +0 -112
  602. package/dist/release/verify.js.map +0 -1
  603. package/dist/runners/chain.d.ts +0 -125
  604. package/dist/runners/chain.d.ts.map +0 -1
  605. package/dist/runners/chain.js +0 -95
  606. package/dist/runners/chain.js.map +0 -1
  607. package/dist/runners/health.d.ts +0 -36
  608. package/dist/runners/health.d.ts.map +0 -1
  609. package/dist/runners/health.js +0 -26
  610. package/dist/runners/health.js.map +0 -1
  611. package/dist/runners/heartbeat.d.ts +0 -23
  612. package/dist/runners/heartbeat.d.ts.map +0 -1
  613. package/dist/runners/heartbeat.js +0 -29
  614. package/dist/runners/heartbeat.js.map +0 -1
  615. package/dist/runners/inprocess.d.ts +0 -17
  616. package/dist/runners/inprocess.d.ts.map +0 -1
  617. package/dist/runners/inprocess.js +0 -20
  618. package/dist/runners/inprocess.js.map +0 -1
  619. package/dist/runners/scheduler.d.ts +0 -5
  620. package/dist/runners/scheduler.d.ts.map +0 -1
  621. package/dist/runners/scheduler.js +0 -19
  622. package/dist/runners/scheduler.js.map +0 -1
  623. package/dist/runtime/abort.d.ts +0 -8
  624. package/dist/runtime/abort.d.ts.map +0 -1
  625. package/dist/runtime/abort.js +0 -13
  626. package/dist/runtime/abort.js.map +0 -1
  627. package/dist/runtime/compatibility.d.ts +0 -37
  628. package/dist/runtime/compatibility.d.ts.map +0 -1
  629. package/dist/runtime/compatibility.js +0 -10
  630. package/dist/runtime/compatibility.js.map +0 -1
  631. package/dist/runtime/detect.d.ts +0 -13
  632. package/dist/runtime/detect.d.ts.map +0 -1
  633. package/dist/runtime/detect.js +0 -18
  634. package/dist/runtime/detect.js.map +0 -1
  635. package/dist/runtime/engine.d.ts +0 -20
  636. package/dist/runtime/engine.d.ts.map +0 -1
  637. package/dist/runtime/engine.js +0 -61
  638. package/dist/runtime/engine.js.map +0 -1
  639. package/dist/runtime/retry.d.ts +0 -19
  640. package/dist/runtime/retry.d.ts.map +0 -1
  641. package/dist/runtime/retry.js +0 -54
  642. package/dist/runtime/retry.js.map +0 -1
  643. package/dist/runtime/worker.d.ts +0 -10
  644. package/dist/runtime/worker.d.ts.map +0 -1
  645. package/dist/runtime/worker.js +0 -25
  646. package/dist/runtime/worker.js.map +0 -1
  647. package/dist/scrape/cache.d.ts +0 -45
  648. package/dist/scrape/cache.d.ts.map +0 -1
  649. package/dist/scrape/cache.js +0 -45
  650. package/dist/scrape/cache.js.map +0 -1
  651. package/dist/scrape/crawl.d.ts +0 -47
  652. package/dist/scrape/crawl.d.ts.map +0 -1
  653. package/dist/scrape/crawl.js +0 -114
  654. package/dist/scrape/crawl.js.map +0 -1
  655. package/dist/scrape/extract.d.ts +0 -63
  656. package/dist/scrape/extract.d.ts.map +0 -1
  657. package/dist/scrape/extract.js +0 -47
  658. package/dist/scrape/extract.js.map +0 -1
  659. package/dist/scrape/normalize.d.ts +0 -52
  660. package/dist/scrape/normalize.d.ts.map +0 -1
  661. package/dist/scrape/normalize.js +0 -102
  662. package/dist/scrape/normalize.js.map +0 -1
  663. package/dist/scrape/robots.d.ts +0 -37
  664. package/dist/scrape/robots.d.ts.map +0 -1
  665. package/dist/scrape/robots.js +0 -71
  666. package/dist/scrape/robots.js.map +0 -1
  667. package/dist/scrape/schema.d.ts +0 -23
  668. package/dist/scrape/schema.d.ts.map +0 -1
  669. package/dist/scrape/schema.js +0 -92
  670. package/dist/scrape/schema.js.map +0 -1
  671. package/dist/scrape/scraper.d.ts +0 -6
  672. package/dist/scrape/scraper.d.ts.map +0 -1
  673. package/dist/scrape/scraper.js +0 -46
  674. package/dist/scrape/scraper.js.map +0 -1
  675. package/dist/scrape/semantic.d.ts +0 -25
  676. package/dist/scrape/semantic.d.ts.map +0 -1
  677. package/dist/scrape/semantic.js +0 -27
  678. package/dist/scrape/semantic.js.map +0 -1
  679. package/dist/server/node.d.ts +0 -7
  680. package/dist/server/node.d.ts.map +0 -1
  681. package/dist/server/node.js.map +0 -1
  682. package/dist/sessions/file.d.ts +0 -25
  683. package/dist/sessions/file.d.ts.map +0 -1
  684. package/dist/sessions/file.js +0 -13
  685. package/dist/sessions/file.js.map +0 -1
  686. package/dist/sessions/replay.d.ts +0 -9
  687. package/dist/sessions/replay.d.ts.map +0 -1
  688. package/dist/sessions/replay.js +0 -70
  689. package/dist/sessions/replay.js.map +0 -1
  690. package/dist/sessions/store.d.ts +0 -25
  691. package/dist/sessions/store.d.ts.map +0 -1
  692. package/dist/sessions/store.js +0 -13
  693. package/dist/sessions/store.js.map +0 -1
  694. package/dist/storage/adapter.d.ts +0 -5
  695. package/dist/storage/adapter.d.ts.map +0 -1
  696. package/dist/storage/adapter.js +0 -11
  697. package/dist/storage/adapter.js.map +0 -1
  698. package/dist/storage/cache.d.ts +0 -23
  699. package/dist/storage/cache.d.ts.map +0 -1
  700. package/dist/storage/cache.js +0 -79
  701. package/dist/storage/cache.js.map +0 -1
  702. package/dist/storage/checksum.d.ts +0 -3
  703. package/dist/storage/checksum.d.ts.map +0 -1
  704. package/dist/storage/checksum.js +0 -19
  705. package/dist/storage/checksum.js.map +0 -1
  706. package/dist/storage/chunked.d.ts +0 -21
  707. package/dist/storage/chunked.d.ts.map +0 -1
  708. package/dist/storage/chunked.js +0 -67
  709. package/dist/storage/chunked.js.map +0 -1
  710. package/dist/storage/content.d.ts +0 -24
  711. package/dist/storage/content.d.ts.map +0 -1
  712. package/dist/storage/content.js +0 -44
  713. package/dist/storage/content.js.map +0 -1
  714. package/dist/storage/filehosting.d.ts +0 -2
  715. package/dist/storage/filehosting.d.ts.map +0 -1
  716. package/dist/storage/filehosting.js +0 -24
  717. package/dist/storage/filehosting.js.map +0 -1
  718. package/dist/storage/githubcontents.d.ts +0 -2
  719. package/dist/storage/githubcontents.d.ts.map +0 -1
  720. package/dist/storage/githubcontents.js +0 -29
  721. package/dist/storage/githubcontents.js.map +0 -1
  722. package/dist/storage/index.d.ts +0 -12
  723. package/dist/storage/index.d.ts.map +0 -1
  724. package/dist/storage/index.js +0 -12
  725. package/dist/storage/index.js.map +0 -1
  726. package/dist/storage/local.d.ts +0 -2
  727. package/dist/storage/local.d.ts.map +0 -1
  728. package/dist/storage/local.js +0 -72
  729. package/dist/storage/local.js.map +0 -1
  730. package/dist/storage/memory.d.ts +0 -6
  731. package/dist/storage/memory.d.ts.map +0 -1
  732. package/dist/storage/memory.js +0 -36
  733. package/dist/storage/memory.js.map +0 -1
  734. package/dist/storage/pool.d.ts +0 -95
  735. package/dist/storage/pool.d.ts.map +0 -1
  736. package/dist/storage/pool.js +0 -202
  737. package/dist/storage/pool.js.map +0 -1
  738. package/dist/storage/s3compatible.d.ts +0 -3
  739. package/dist/storage/s3compatible.d.ts.map +0 -1
  740. package/dist/storage/s3compatible.js +0 -70
  741. package/dist/storage/s3compatible.js.map +0 -1
  742. package/dist/storage/sync.d.ts +0 -47
  743. package/dist/storage/sync.d.ts.map +0 -1
  744. package/dist/storage/sync.js +0 -75
  745. package/dist/storage/sync.js.map +0 -1
  746. package/dist/surfaces/adapters.d.ts +0 -97
  747. package/dist/surfaces/adapters.d.ts.map +0 -1
  748. package/dist/surfaces/adapters.js +0 -50
  749. package/dist/surfaces/adapters.js.map +0 -1
  750. package/dist/surfaces/controls.d.ts +0 -18
  751. package/dist/surfaces/controls.d.ts.map +0 -1
  752. package/dist/surfaces/controls.js +0 -45
  753. package/dist/surfaces/controls.js.map +0 -1
  754. package/dist/surfaces/manifest.d.ts +0 -68
  755. package/dist/surfaces/manifest.d.ts.map +0 -1
  756. package/dist/surfaces/manifest.js +0 -42
  757. package/dist/surfaces/manifest.js.map +0 -1
  758. package/dist/surfaces/n8n.d.ts +0 -25
  759. package/dist/surfaces/n8n.d.ts.map +0 -1
  760. package/dist/surfaces/n8n.js +0 -31
  761. package/dist/surfaces/n8n.js.map +0 -1
  762. package/dist/surfaces/operations.d.ts +0 -32
  763. package/dist/surfaces/operations.d.ts.map +0 -1
  764. package/dist/surfaces/operations.js +0 -53
  765. package/dist/surfaces/operations.js.map +0 -1
  766. package/dist/surfaces/requirements.d.ts +0 -29
  767. package/dist/surfaces/requirements.d.ts.map +0 -1
  768. package/dist/surfaces/requirements.js +0 -50
  769. package/dist/surfaces/requirements.js.map +0 -1
  770. package/dist/surfaces/targets.d.ts +0 -196
  771. package/dist/surfaces/targets.d.ts.map +0 -1
  772. package/dist/surfaces/targets.js +0 -27
  773. package/dist/surfaces/targets.js.map +0 -1
  774. package/dist/webhook/delivery.d.ts +0 -18
  775. package/dist/webhook/delivery.d.ts.map +0 -1
  776. package/dist/webhook/delivery.js +0 -36
  777. package/dist/webhook/delivery.js.map +0 -1
  778. package/dist/webhook/receiver.d.ts +0 -26
  779. package/dist/webhook/receiver.d.ts.map +0 -1
  780. package/dist/webhook/receiver.js +0 -24
  781. package/dist/webhook/receiver.js.map +0 -1
  782. package/dist/webhook/signature.d.ts +0 -3
  783. package/dist/webhook/signature.d.ts.map +0 -1
  784. package/dist/webhook/signature.js +0 -7
  785. package/dist/webhook/signature.js.map +0 -1
  786. package/dist/workflow/manifest.d.ts +0 -22
  787. package/dist/workflow/manifest.d.ts.map +0 -1
  788. package/dist/workflow/manifest.js +0 -21
  789. package/dist/workflow/manifest.js.map +0 -1
  790. package/dist/workflow/registry.d.ts +0 -7
  791. package/dist/workflow/registry.d.ts.map +0 -1
  792. package/dist/workflow/registry.js +0 -18
  793. package/dist/workflow/registry.js.map +0 -1
  794. package/dist/workflow/templates.d.ts +0 -6
  795. package/dist/workflow/templates.d.ts.map +0 -1
  796. package/dist/workflow/templates.js +0 -18
  797. package/dist/workflow/templates.js.map +0 -1
  798. package/dist/workflow/triggers.d.ts +0 -64
  799. package/dist/workflow/triggers.d.ts.map +0 -1
  800. package/dist/workflow/triggers.js +0 -99
  801. package/dist/workflow/triggers.js.map +0 -1
  802. /package/{extension/README.md → docs/extension.md} +0 -0
package/gpumonitor.cpp ADDED
@@ -0,0 +1,1543 @@
1
+ /*
2
+ * gpumonitor.cpp — gpu identity spoofing suite for the virtual hardware
3
+ * engine v5 (vhe), written against c++26.
4
+ *
5
+ * one translation unit, four build modes (see the dockerfile):
6
+ * 1. CLI (-DVHE_BUILD_CLI) the virtual nvidia-smi adapter binary installed at
7
+ * /usr/local/bin/nvidia-smi; plain table,
8
+ * --query-gpu csv mode and -L device list,
9
+ * same surface as pogusthewhisper/fake-nvidia-smi.
10
+ * 2. SHIM (-DVHE_BUILD_PRELOAD) shared object exporting nvml* and cu*
11
+ * symbols; when preloaded (or symlinked as
12
+ * libnvidia-ml.so.1 / libcuda.so.1) it
13
+ * forwards to a real driver found after this
14
+ * object via dlsym(RTLD_NEXT, ...) and
15
+ * synthesizes data otherwise — the unified
16
+ * shim pattern from rick-hsu/nvml-unified-shim
17
+ * plus the libcuda entry-point interception
18
+ * documented by FanBB2333/GpuAdapter.
19
+ * 3. ADDON (-DVHE_BUILD_ADDON) node native addon (node_api.h, node 26.7.0,
20
+ * NODE_MODULE_VERSION 147) exporting the
21
+ * table generator to javascript.
22
+ * 4. FORGE (-DVHE_BUILD_FORGE) the aetherforge control plane (v2 merge of
23
+ * forge.cpp/forge.hpp): plan validation, the
24
+ * qemu 11.1.0 argv generator with hugepages,
25
+ * virtio-mem, swtpm tpm-crb, looking glass
26
+ * ivshmem and whpx accel, plus the mttg
27
+ * work-stealing grid model. the planner is
28
+ * the c++ mirror of the architect planner in
29
+ * index.ts and carries the flags the ts side
30
+ * leaves to operators.
31
+ *
32
+ * hardware numbers below were verified on 22/08/2026 against vendor pages
33
+ * and the worklog task 1-d research:
34
+ * NVIDIA-SMI 575.57.08 / CUDA 12.9 header (fake-nvidia-smi confirmed)
35
+ * A100-SXM4-40GB 40536 MiB 400 W
36
+ * H100-SXM5-80GB 81559 MiB 700 W
37
+ * GeForce RTX 5090 32768 MiB 575 W
38
+ * RTX PRO 6000 97887 MiB 600 W (blackwell workstation, 96 GB)
39
+ * B200 196608 MiB 1000 W (192 GB HBM3e)
40
+ *
41
+ * c++26 features exercised deliberately, each with a toolchain fallback:
42
+ * - import std; primary path; -DVHE_NO_MODULES switches
43
+ * to classic includes for libstdc++
44
+ * toolchains without the std module.
45
+ * - std::expected (p0323r12) every renderer returns
46
+ * expected<string, spooferror> instead of
47
+ * throwing, so the CLI and the napi addon
48
+ * surface errors as values.
49
+ * - std::mdspan (p0009r18) the spec matrix is a 2D non-owning view,
50
+ * extended sizes, operator() access.
51
+ * - pack indexing ts...[i] default profile selection at compile time
52
+ * (p2662r3, c++26); __cpp_pack_indexing
53
+ * guards a std::forward_as_tuple fallback
54
+ * for clang-19/gcc-14 pre-adoption builds.
55
+ * - std::print / std::format all console output; no printf anywhere.
56
+ *
57
+ * environment variables consumed (all optional):
58
+ * VHE_GPU_PROFILE profile key: a100 (default), h100, rtx5090, rtxpro6000,
59
+ * b200; aliases: 5090, pro6000, a100-40gb, h100-80gb
60
+ * VHE_GPUS gpu count reported (default 8, DGX-class node)
61
+ * VHE_SMI_DRIVER driver version string (default 575.57.08)
62
+ * VHE_SMI_CUDA cuda version string (default 12.9)
63
+ * VHE_MIG 1 renders a MIG instance table inside the output
64
+ * VHE_DEBUG 1 prints interception decisions to stderr
65
+ *
66
+ * build (shim): clang++-19 -std=c++26 -O3 -march=native -flto -shared -fPIC
67
+ * -ldl gpumonitor.cpp -o libgpumonitor.so
68
+ * build (cli): clang++-19 -std=c++26 -O3 -march=native -flto
69
+ * -DVHE_BUILD_CLI gpumonitor.cpp -o nvidia-smi
70
+ * build (addon): clang++-19 -std=c++26 -O3 -shared -fPIC -DVHE_BUILD_ADDON
71
+ * -DNODE_GYP_MODULE_NAME=vhe_gpu -I/usr/include/node
72
+ * gpumonitor.cpp -o vhe_gpu.node
73
+ * build (forge): clang++-19 -std=c++26 -O3 -DVHE_BUILD_FORGE
74
+ * gpumonitor.cpp -o aetherforge
75
+ */
76
+
77
+ #ifdef VHE_NO_MODULES /* classic include path for toolchains without std module */
78
+ #include <algorithm>
79
+ #include <array>
80
+ #include <chrono>
81
+ #include <cstdint>
82
+ #include <cstdio>
83
+ #include <cstdlib>
84
+ #include <expected>
85
+ #include <format>
86
+ #include <optional>
87
+ #include <print>
88
+ #include <sstream>
89
+ #include <string>
90
+ #include <string_view>
91
+ #include <thread>
92
+ #include <tuple>
93
+ #include <utility>
94
+ #include <vector>
95
+ #if __has_include(<mdspan>)
96
+ #include <mdspan>
97
+ #define VHEHAVESTDMDSPAN 1
98
+ #endif
99
+ #else /* c++26 named module: the whole standard library in one import */
100
+ import std;
101
+ #define VHEHAVESTDMDSPAN 1
102
+ #endif
103
+
104
+ #include <dlfcn.h> /* posix dlsym/RTLD_NEXT, outside the std module */
105
+
106
+ #ifdef VHE_BUILD_ADDON
107
+ #include <node_api.h> /* node 26 stable n-api, abi stable across versions */
108
+ #endif
109
+
110
+ namespace vhe {
111
+
112
+ /* ------------------------------------------------------------------ */
113
+ /* constants and real hardware specs */
114
+ /* ------------------------------------------------------------------ */
115
+
116
+ /** summary table geometry: the real nvidia-smi frame is 89 columns wide;
117
+ * segment widths are 41 | 23 | 21 inner columns plus the four border
118
+ * characters, matching the 575.x layout byte for byte where it matters. */
119
+ inline constexpr std::size_t ksegleft = 41;
120
+ inline constexpr std::size_t ksegmid = 23;
121
+ inline constexpr std::size_t ksegright = 21;
122
+ inline constexpr std::size_t kinner = 87; /* 41+23+21+2 separators */
123
+ inline constexpr std::size_t ktable = 89;
124
+
125
+ /** driver and cuda identity printed in the header row (fake-nvidia-smi
126
+ * confirmed this exact pair for 2026 datacenter branches). */
127
+ inline constexpr std::string_view kdriverdefault = "575.57.08";
128
+ inline constexpr std::string_view kcudadefault = "12.9";
129
+
130
+ /**
131
+ * one gpu product entry. every numeric field comes from the vendor pages
132
+ * verified in worklog task 1-d; the values are exactly what a real
133
+ * nvidia-smi would print on the corresponding hardware.
134
+ */
135
+ struct gpuspec {
136
+ std::string_view key; /* profile lookup key */
137
+ std::string_view name; /* marketing name shown in the table */
138
+ std::string_view arch; /* architecture tag for -L output */
139
+ unsigned long long totalmib; /* vram as nvidia-smi reports it (MiB) */
140
+ unsigned powerw; /* enforced power cap */
141
+ unsigned tempc; /* healthy idle-ish temperature */
142
+ unsigned fanpct; /* fan percentage (datacenter: N/A) */
143
+ unsigned utilpct; /* steady-state utilization */
144
+ unsigned pciegen; /* generation for bus metadata */
145
+ unsigned mig; /* 1 = MIG capable (A100/H100/B200) */
146
+ };
147
+
148
+ inline constexpr gpuspec speca100{
149
+ "a100", "NVIDIA A100-SXM4-40GB", "ampere", 40536, 400, 45, 0, 97, 4, 1};
150
+ inline constexpr gpuspec spech100{
151
+ "h100", "NVIDIA H100-SXM5-80GB", "hopper", 81559, 700, 43, 0, 99, 5, 1};
152
+ inline constexpr gpuspec specrtx5090{
153
+ "rtx5090", "NVIDIA GeForce RTX 5090", "blackwell-geforce", 32768, 575,
154
+ 41, 30, 96, 5, 0};
155
+ inline constexpr gpuspec specrtxpro6000{
156
+ "rtxpro6000", "NVIDIA RTX PRO 6000", "blackwell-workstation", 97887, 600,
157
+ 47, 32, 94, 5, 1};
158
+ inline constexpr gpuspec specb200{
159
+ "b200", "NVIDIA B200", "blackwell-datacenter", 196608, 1000, 55, 0, 98, 6,
160
+ 1};
161
+
162
+ /** flat spec matrix consumed through std::mdspan below. column order:
163
+ * 0 totalmib, 1 powerw, 2 tempc, 3 fanpct, 4 utilpct, 5 pciegen,
164
+ * 6 mig, 7 bar1mib. rows follow the constexpr spec declaration order. */
165
+ inline constexpr std::array<unsigned, 8 * 5> kspecflat{
166
+ 40536, 400, 45, 0, 97, 4, 1, 32768, /* a100 */
167
+ 81559, 700, 43, 0, 99, 5, 1, 131072, /* h100 */
168
+ 32768, 575, 41, 30, 96, 5, 0, 32768, /* rtx 5090 */
169
+ 97887, 600, 47, 32, 94, 5, 1, 131072, /* rtx pro 6000*/
170
+ 196608, 1000, 55, 0, 98, 6, 1, 262144, /* b200 */
171
+ };
172
+
173
+ /**
174
+ * mdspan over the flat matrix. the c++23 std::mdspan view (p0009r18, adopted
175
+ * into c++26 toolchains as a core library piece) gives bounds-checked-free
176
+ * 2D indexing without owning storage; on toolchains without <mdspan> the
177
+ * fallback keeps the same operator() surface so call sites never change.
178
+ */
179
+ #if defined(VHEHAVESTDMDSPAN)
180
+ inline constexpr auto kspecs =
181
+ std::mdspan<const unsigned, std::extents<std::size_t, 5, 8>>(
182
+ kspecflat.data());
183
+ #else
184
+ /** minimal fixed-extent 2D view mirroring std::mdspan's call syntax. */
185
+ template <class T, std::size_t Rows, std::size_t Cols>
186
+ struct compatmdspan {
187
+ const T* mdata;
188
+ constexpr const T& operator()(std::size_t r, std::size_t c) const {
189
+ return mdata[r * Cols + c];
190
+ }
191
+ };
192
+ inline constexpr compatmdspan<const unsigned, 5, 8> kspecs{
193
+ kspecflat.data()};
194
+ #endif
195
+
196
+ /**
197
+ * returns the index-th element of a function parameter pack.
198
+ * primary path uses c++26 pack indexing (p2662r3, `ts...[index]`); the
199
+ * fallback covers gcc-14 and clang-19 libstdc++ builds where the feature is
200
+ * not yet enabled, via std::forward_as_tuple + std::get.
201
+ * @param index compile-time position within the pack
202
+ * @param ts the pack elements
203
+ * @return reference to the selected element
204
+ */
205
+ #if defined(__cpp_pack_indexing)
206
+ template <std::size_t index, typename... ts>
207
+ [[nodiscard]] constexpr decltype(auto) packat(ts&&... tspack) noexcept {
208
+ return tspack...[index]; /* c++26 pack indexing */
209
+ }
210
+ #else
211
+ template <std::size_t index, typename... ts>
212
+ [[nodiscard]] constexpr decltype(auto) packat(ts&&... tspack) noexcept {
213
+ return std::get<index>(std::forward_as_tuple(tspack...));
214
+ }
215
+ #endif
216
+
217
+ /**
218
+ * compile-time default profile selection through pack indexing: the whole
219
+ * chain from kspecs back to constexpr spec objects evaluates at compile
220
+ * time, which the static_asserts below prove.
221
+ * @param idx profile index, 0=a100 .. 4=b200
222
+ * @return reference to the constexpr gpuspec
223
+ */
224
+ template <std::size_t idx>
225
+ [[nodiscard]] constexpr const gpuspec& defaultprofile() {
226
+ return packat<idx>(speca100, spech100, specrtx5090, specrtxpro6000,
227
+ specb200);
228
+ }
229
+
230
+ static_assert(defaultprofile<0>().totalmib == 40536,
231
+ "pack-indexed a100 profile must carry 40536 MiB");
232
+ static_assert(defaultprofile<4>().powerw == 1000,
233
+ "pack-indexed b200 profile must carry the 1000 W cap");
234
+ static_assert(kspecs(4, 0) == 196608, "mdspan row b200 must carry 196608 MiB");
235
+ static_assert(ktable == 2 + kinner, "table frame math must stay consistent");
236
+
237
+ /* ------------------------------------------------------------------ */
238
+ /* error handling: std::expected everywhere, no exceptions */
239
+ /* ------------------------------------------------------------------ */
240
+
241
+ /** error taxonomy for the renderers; codes mirror nvidia-smi exit reasons. */
242
+ enum class spooferrc {
243
+ unknownprofile, /**< profile key not in the catalog */
244
+ invalidcount, /**< gpu count outside [1, 256] */
245
+ formatfailure, /**< a row exceeded its fixed-width segment */
246
+ };
247
+
248
+ /** rich error value carried by std::expected. */
249
+ struct spooferror {
250
+ spooferrc code;
251
+ std::string detail;
252
+ };
253
+
254
+ /** result type used by every renderer in this translation unit. */
255
+ using smiresult = std::expected<std::string, spooferror>;
256
+
257
+ /**
258
+ * builds a spooferror with a formatted message.
259
+ * @param code error category
260
+ * @param fmt std::format style message
261
+ * @return the fully populated error value
262
+ */
263
+ [[nodiscard]] inline spooferror makeerror(spooferrc code,
264
+ std::string_view fmt) {
265
+ return spooferror{code, std::string{fmt}};
266
+ }
267
+
268
+ /* ------------------------------------------------------------------ */
269
+ /* small text helpers */
270
+ /* ------------------------------------------------------------------ */
271
+
272
+ /**
273
+ * truncates or left-pads a string to an exact segment width so the 89-column
274
+ * frame can never be broken by a long gpu name.
275
+ * @param s input text
276
+ * @param width target width
277
+ * @return exactly width characters
278
+ */
279
+ [[nodiscard]] inline std::string fit(std::string_view s, std::size_t width) {
280
+ std::string out{s.substr(0, width)};
281
+ if (out.size() < width)
282
+ out.append(width - out.size(), ' ');
283
+ return out;
284
+ }
285
+
286
+ /** right-aligned variant of fit(), used for numeric columns. */
287
+ [[nodiscard]] inline std::string fitr(std::string_view s,
288
+ std::size_t width) {
289
+ std::string out{s.substr(0, width)};
290
+ if (out.size() < width)
291
+ out.insert(0, width - out.size(), ' ');
292
+ return out;
293
+ }
294
+
295
+ /**
296
+ * fnv-1a 64-bit hash; produces stable pseudo-random-looking identifiers so
297
+ * bus-ids and uuids survive repeated invocations (tools diff two samples).
298
+ * @param data bytes to hash
299
+ * @param seed mixing seed (gpu index, process pid, ...)
300
+ * @return 64-bit digest
301
+ */
302
+ [[nodiscard]] constexpr std::uint64_t fnv1a(std::string_view data,
303
+ std::uint64_t seed) {
304
+ std::uint64_t h = 0xcbf29ce484222325ULL ^ seed;
305
+ for (char c : data) {
306
+ h ^= static_cast<std::uint64_t>(static_cast<unsigned char>(c));
307
+ h *= 0x100000001b3ULL;
308
+ }
309
+ return h;
310
+ }
311
+
312
+ /**
313
+ * formats a digest as the nvidia uuid v4 shape used by nvidia-smi -L:
314
+ * GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
315
+ * @param h digest bits
316
+ * @return 40-character uuid string starting with "GPU-"
317
+ */
318
+ [[nodiscard]] inline std::string gpuuuid(std::uint64_t h) {
319
+ return std::format("GPU-{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
320
+ static_cast<unsigned>(h >> 32),
321
+ static_cast<unsigned>((h >> 16) & 0xffff),
322
+ static_cast<unsigned>(h & 0xffff),
323
+ static_cast<unsigned>((h >> 48) & 0xffff),
324
+ h & 0xffffffffffffULL);
325
+ }
326
+
327
+ /** reads an optional environment string; empty or unset yields nullopt. */
328
+ [[nodiscard]] inline std::optional<std::string> envstr(
329
+ const char* name) {
330
+ const char* raw = std::getenv(name);
331
+ if (raw == nullptr || *raw == '\0')
332
+ return std::nullopt;
333
+ return std::string{raw};
334
+ }
335
+
336
+ /** reads an optional environment number with clamping, default fallback. */
337
+ [[nodiscard]] inline unsigned envuint(const char* name, unsigned fallback,
338
+ unsigned lo, unsigned hi) {
339
+ auto raw = envstr(name);
340
+ if (!raw)
341
+ return fallback;
342
+ unsigned value = 0;
343
+ try {
344
+ value = static_cast<unsigned>(std::stoul(*raw));
345
+ } catch (...) {
346
+ return fallback; /* malformed input keeps the default, never throws */
347
+ }
348
+ return value < lo ? lo : (value > hi ? hi : value);
349
+ }
350
+
351
+ /**
352
+ * resolves a profile key to its spec, accepting the documented aliases.
353
+ * @param key profile name or alias
354
+ * @return pointer to the constexpr spec, or an error value
355
+ */
356
+ [[nodiscard]] smiresult resolveprofile(std::string_view key) {
357
+ if (key == "a100" || key == "a100-40gb" || key == "A100")
358
+ return std::string{speca100.name};
359
+ if (key == "h100" || key == "h100-80gb" || key == "H100")
360
+ return std::string{spech100.name};
361
+ if (key == "rtx5090" || key == "5090" || key == "RTX5090")
362
+ return std::string{specrtx5090.name};
363
+ if (key == "rtxpro6000" || key == "pro6000" || key == "RTXPRO6000")
364
+ return std::string{specrtxpro6000.name};
365
+ if (key == "b200" || key == "B200")
366
+ return std::string{specb200.name};
367
+ return std::unexpected(makeerror(
368
+ spooferrc::unknownprofile,
369
+ std::format("unknown profile '{}': expected a100, h100, rtx5090, "
370
+ "rtxpro6000 or b200", key)));
371
+ }
372
+
373
+ /**
374
+ * finds the full gpuspec for a resolved key. resolveprofile returns the
375
+ * name; this helper maps names back to specs through the flat catalog.
376
+ * @param key profile key
377
+ * @return spec pointer or error
378
+ */
379
+ [[nodiscard]] std::expected<const gpuspec*, spooferror> findspec(
380
+ std::string_view key) {
381
+ const gpuspec* catalog[] = {&speca100, &spech100, &specrtx5090,
382
+ &specrtxpro6000, &specb200};
383
+ for (const gpuspec* spec : catalog)
384
+ if (spec->key == key)
385
+ return spec;
386
+ return std::unexpected(makeerror(
387
+ spooferrc::unknownprofile,
388
+ std::format("profile '{}' not present in the constexpr catalog", key)));
389
+ }
390
+
391
+ /* ------------------------------------------------------------------ */
392
+ /* the virtual nvidia-smi adapter renderer */
393
+ /* ------------------------------------------------------------------ */
394
+
395
+ /**
396
+ * renders the complete nvidia-smi summary: asctime timestamp, 89-column
397
+ * frame, header row, one block per gpu (identity row + telemetry row),
398
+ * processes section, and — when VHE_MIG=1 — a MIG instance table.
399
+ * memory-used derives deterministically from fnv1a(name, index) so the
400
+ * output is stable within a process but distinct per gpu, mirroring how
401
+ * fake-nvidia-smi derives its realistic telemetry.
402
+ * @param profile profile key ("a100", "h100", ...)
403
+ * @param gpucount number of devices to report
404
+ * @param driver driver version for the header (default 575.57.08)
405
+ * @param cuda cuda version for the header (default 12.9)
406
+ * @param mig 1 renders MIG instances
407
+ * @return the full table text or a spooferror
408
+ */
409
+ [[nodiscard]] smiresult rendertable(std::string_view profile,
410
+ unsigned gpucount,
411
+ std::string_view driver,
412
+ std::string_view cuda, bool mig) {
413
+ if (gpucount == 0 || gpucount > 256)
414
+ return std::unexpected(makeerror(
415
+ spooferrc::invalidcount,
416
+ std::format("gpu count {} outside the supported [1, 256] range",
417
+ gpucount)));
418
+
419
+ auto specorerr = findspec(profile);
420
+ if (!specorerr)
421
+ return std::unexpected(specorerr.error());
422
+ const gpuspec& spec = **specorerr;
423
+
424
+ const std::string fullborder = "+" + std::string(kinner, '-') + "+";
425
+ const std::string segborder =
426
+ "|" + std::string(ksegleft, '-') + "+" +
427
+ std::string(ksegmid, '-') + "+" + std::string(ksegright, '-') +
428
+ "|";
429
+ const std::string segheavy =
430
+ "|" + std::string(ksegleft, '=') + "+" +
431
+ std::string(ksegmid, '=') + "+" + std::string(ksegright, '=') +
432
+ "|";
433
+
434
+ std::string out;
435
+ out.reserve(1024 + gpucount * 180);
436
+
437
+ /* timestamp exactly as asctime/nvidia-smi print it: %a %b %e %H:%M:%S %Y */
438
+ const auto now = std::chrono::floor<std::chrono::seconds>(
439
+ std::chrono::system_clock::now());
440
+ out += std::format("{:%a %b %e %H:%M:%S %Y}\n", now);
441
+ out += fullborder + "\n";
442
+ out += "|" +
443
+ fit(std::format(" NVIDIA-SMI {} Driver Version: {} "
444
+ "CUDA Version: {}",
445
+ driver, driver, cuda),
446
+ kinner) +
447
+ "|\n";
448
+ out += segborder + "\n";
449
+ out += "|" + fit(" GPU Name Driver-Model", ksegleft) +
450
+ "|" + fit(" Bus-Id Disp.A", ksegmid) + "|" +
451
+ fit(" Volatile Uncorr. ECC", ksegright) + "|\n";
452
+ out += "|" + fit(" Fan Temp Perf Pwr:Usage/Cap", ksegleft) +
453
+ "|" + fit(" Memory-Usage", ksegmid) + "|" +
454
+ fit(" GPU-Util Compute M.", ksegright) + "|\n";
455
+ out += segheavy + "\n";
456
+
457
+ for (unsigned i = 0; i < gpucount; ++i) {
458
+ const std::uint64_t digest = fnv1a(spec.name, i + 1);
459
+ const unsigned long long usedmib =
460
+ spec.totalmib / 10 +
461
+ digest % (spec.totalmib / 20); /* 10%..15% used at idle */
462
+ const unsigned draww = spec.powerw / 8 + digest % (spec.powerw / 4);
463
+ const std::string bus =
464
+ std::format("00000000:{:02X}:{:02X}.0", (7u + i * 4u) & 0xffu,
465
+ (i * 8u) & 0xffu);
466
+ /* datacenter boards (A100/H100/B200) have no readable fan sensor,
467
+ * nvidia-smi prints N/A; consumer/workstation boards report % */
468
+ const std::string fan =
469
+ spec.fanpct == 0 ? "N/A" : std::format("{}%", spec.fanpct);
470
+ const std::string powerstr =
471
+ std::format("{}W / {}W", draww, spec.powerw);
472
+
473
+ out += "|" +
474
+ fit(std::format(" {:>3} {:<24}{:>9} ", i, spec.name, "On"),
475
+ ksegleft) +
476
+ "|" + fit(std::format(" {} Off", bus), ksegmid) + "|" +
477
+ fitr("0", ksegright) + "|\n";
478
+ out += "|" +
479
+ fit(std::format(" {:>3} {:>4}C {:>3}{:>18}", fan, spec.tempc,
480
+ "P0", powerstr),
481
+ ksegleft) +
482
+ "|" +
483
+ fit(std::format(" {}MiB / {}MiB", usedmib, spec.totalmib),
484
+ ksegmid) +
485
+ "|" +
486
+ fit(std::format("{:>6}{:>15}",
487
+ std::format("{}%", spec.utilpct),
488
+ mig ? "E. Process" : "Default"),
489
+ ksegright) +
490
+ "|\n";
491
+ if (i + 1 < gpucount)
492
+ out += segborder + "\n";
493
+ }
494
+
495
+ out += segheavy + "\n";
496
+ out += "|" + fit(" Processes:", kinner) + "|\n";
497
+ if (mig && spec.mig) {
498
+ out += "|" +
499
+ fit(" GPU GI CI PID Type Process Name "
500
+ " GPU Memory",
501
+ kinner) +
502
+ "|\n";
503
+ for (unsigned g = 0; g < gpucount && g < 8; ++g)
504
+ out += "|" +
505
+ fit(std::format(" {:>3} {:>2} {:>2} {:>7} {:>4} "
506
+ "{:<20} {:>10}MiB",
507
+ g, 0, 0, 20000 + g * 37, "C+G",
508
+ "python", 2048 + g * 128),
509
+ kinner) +
510
+ "|\n";
511
+ } else {
512
+ out += "|" +
513
+ fit(std::format("{:^{}}", "No running processes found",
514
+ kinner),
515
+ kinner) +
516
+ "|\n";
517
+ }
518
+ out += fullborder + "\n";
519
+ return out;
520
+ }
521
+
522
+ /** default field list for csv mode, matching the nvidia-smi column names. */
523
+ inline constexpr std::string_view kqueryfields =
524
+ "index,name,uuid,driver_version,cuda_version,memory.total,memory.used,"
525
+ "memory.free,power.draw,power.limit,temperature.gpu,utilization.gpu";
526
+
527
+ /**
528
+ * renders nvidia-smi --query-gpu output in csv mode, the surface consumed
529
+ * by most fleet tooling (run-ai/fake-gpu-operator and cuda-mock implement
530
+ * the same subset). supported fields: index, name, uuid, driverversion,
531
+ * cudaversion, memory.total, memory.used, memory.free, power.draw,
532
+ * power.limit, temperature.gpu, utilization.gpu. unknown fields fail with
533
+ * spooferrc::unknownprofile exactly like the real binary refuses them.
534
+ * @param profile profile key
535
+ * @param gpucount device count
536
+ * @param fields comma-separated field list (may be empty for the default)
537
+ * @param noheader 1 suppresses the csv header line
538
+ * @param driver driver version
539
+ * @param cuda cuda version
540
+ * @return csv text or a spooferror
541
+ */
542
+ [[nodiscard]] smiresult renderquerycsv(std::string_view profile,
543
+ unsigned gpucount,
544
+ std::string_view fields,
545
+ bool noheader,
546
+ std::string_view driver,
547
+ std::string_view cuda) {
548
+ if (gpucount == 0 || gpucount > 256)
549
+ return std::unexpected(makeerror(
550
+ spooferrc::invalidcount,
551
+ std::format("gpu count {} outside the supported [1, 256] range",
552
+ gpucount)));
553
+ auto specorerr = findspec(profile);
554
+ if (!specorerr)
555
+ return std::unexpected(specorerr.error());
556
+ const gpuspec& spec = **specorerr;
557
+
558
+ /* split the field list once; empty input selects every column */
559
+ std::vector<std::string> wanted;
560
+ {
561
+ std::string list{fields.empty() ? std::string_view{kqueryfields}
562
+ : fields};
563
+ std::size_t pos = 0;
564
+ while (pos != std::string::npos) {
565
+ std::size_t comma = list.find(',', pos);
566
+ std::string token =
567
+ list.substr(pos, comma == std::string::npos
568
+ ? std::string::npos
569
+ : comma - pos);
570
+ /* nvidia-smi tolerates spaces after commas */
571
+ while (!token.empty() && token.front() == ' ')
572
+ token.erase(0, 1);
573
+ while (!token.empty() && token.back() == ' ')
574
+ token.pop_back();
575
+ if (!token.empty())
576
+ wanted.push_back(token);
577
+ pos = (comma == std::string::npos) ? std::string::npos
578
+ : comma + 1;
579
+ }
580
+ }
581
+ if (wanted.empty())
582
+ return std::unexpected(makeerror(spooferrc::formatfailure,
583
+ "empty --query-gpu field list"));
584
+
585
+ /* validate every requested field before emitting anything */
586
+ for (const std::string& field : wanted) {
587
+ if (field != "index" && field != "name" && field != "uuid" &&
588
+ field != "driver_version" && field != "cuda_version" &&
589
+ field != "memory.total" && field != "memory.used" &&
590
+ field != "memory.free" && field != "power.draw" &&
591
+ field != "power.limit" && field != "temperature.gpu" &&
592
+ field != "utilization.gpu")
593
+ return std::unexpected(makeerror(
594
+ spooferrc::formatfailure,
595
+ std::format("field \"{}\" is not a valid field to query",
596
+ field)));
597
+ }
598
+
599
+ std::string out;
600
+ if (!noheader) {
601
+ for (std::size_t f = 0; f < wanted.size(); ++f)
602
+ out += std::format("{}{}", f ? ", " : "", wanted[f]);
603
+ out += '\n';
604
+ }
605
+ for (unsigned i = 0; i < gpucount; ++i) {
606
+ const std::uint64_t digest = fnv1a(spec.name, i + 1);
607
+ const unsigned long long used =
608
+ spec.totalmib / 10 + digest % (spec.totalmib / 20);
609
+ for (std::size_t f = 0; f < wanted.size(); ++f) {
610
+ const std::string& field = wanted[f];
611
+ std::string value;
612
+ if (field == "index")
613
+ value = std::format("{}", i);
614
+ else if (field == "name")
615
+ value = std::string{spec.name};
616
+ else if (field == "uuid")
617
+ value = gpuuuid(digest);
618
+ else if (field == "driver_version")
619
+ value = std::string{driver};
620
+ else if (field == "cuda_version")
621
+ value = std::string{cuda};
622
+ else if (field == "memory.total")
623
+ value = std::format("{}", spec.totalmib);
624
+ else if (field == "memory.used")
625
+ value = std::format("{}", used);
626
+ else if (field == "memory.free")
627
+ value = std::format("{}", spec.totalmib - used);
628
+ else if (field == "power.draw")
629
+ value = std::format(
630
+ "{}", spec.powerw / 8 + digest % (spec.powerw / 4));
631
+ else if (field == "power.limit")
632
+ value = std::format("{}", spec.powerw);
633
+ else if (field == "temperature.gpu")
634
+ value = std::format("{}", spec.tempc);
635
+ else /* utilization.gpu */
636
+ value = std::format("{}", spec.utilpct);
637
+ out += std::format("{}{}", f ? ", " : "", value);
638
+ }
639
+ out += '\n';
640
+ }
641
+ return out;
642
+ }
643
+
644
+ /**
645
+ * renders nvidia-smi -L device list lines:
646
+ * "GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-...)".
647
+ * @param profile profile key
648
+ * @param gpucount device count
649
+ * @return list text or a spooferror
650
+ */
651
+ [[nodiscard]] smiresult renderdevicelist(std::string_view profile,
652
+ unsigned gpucount) {
653
+ auto specorerr = findspec(profile);
654
+ if (!specorerr)
655
+ return std::unexpected(specorerr.error());
656
+ const gpuspec& spec = **specorerr;
657
+ std::string out;
658
+ for (unsigned i = 0; i < gpucount; ++i)
659
+ out += std::format("GPU {}: {} (UUID: {})\n", i, spec.name,
660
+ gpuuuid(fnv1a(spec.name, i + 1)));
661
+ return out;
662
+ }
663
+
664
+ } /* namespace vhe */
665
+
666
+ /* -------------------------------------------------------------------- */
667
+ /* aetherforge control plane (v2 merge of forge.cpp/forge.hpp, mode 4) */
668
+ /* -------------------------------------------------------------------- */
669
+
670
+ namespace vhe::forge {
671
+
672
+ /** planner ceilings shared by validate() and the mttg grid; kvcpumax at
673
+ * 4096 matches the modern qemu -smp maxcpus limit (qemu 11.1.0 raised the
674
+ * x86 topology ceiling the engine relies on for 192-vcpu EPYC guests). */
675
+ inline constexpr std::uint32_t kvcpumax = 4096;
676
+ inline constexpr std::uint32_t kovercommitmax = 64;
677
+ inline constexpr std::uint32_t kmttgmax = 1'000'000;
678
+
679
+ /** planner error taxonomy; the values mirror the architect planner errors
680
+ * surfaced by index.ts so operators see one vocabulary across runtimes. */
681
+ enum class Err : std::uint8_t {
682
+ invalidplan, /**< empty plan name or unparsable input */
683
+ overcommit, /**< ratio above the 64x safety ceiling */
684
+ vendormismatch,/**< vfio plan without a matching host vendor */
685
+ missingvfio, /**< vfio plan on a host without vfio support */
686
+ mttgfloor, /**< mttg pool smaller than the vcpu count */
687
+ };
688
+
689
+ /** gpu attachment strategy for the emitted command line. */
690
+ enum class GpuMode : std::uint8_t { vfio, vgpu, mig, virtio, none };
691
+
692
+ /** hypervisor acceleration backend; whpx (windows hypervisor platform) is
693
+ * the only windows-capable backend in the engine and exists nowhere else
694
+ * in the pool outside this planner and its ts mirror. */
695
+ enum class Accel : std::uint8_t { kvm, hvf, whpx, tcg };
696
+
697
+ /**
698
+ * the complete lab plan, c++ mirror of the architect planner in index.ts.
699
+ * the defaults describe the reference 9950x3d + rtx 5090 lab node. the c++
700
+ * side owns the flags the ts planner delegates: hugepages file backend,
701
+ * virtio-mem dimm, swtpm tpm-crb device and the looking glass ivshmem.
702
+ */
703
+ struct Plan {
704
+ std::string name{"aether-lab"};
705
+ std::string cpuid{"r9-9950x3d"};
706
+ std::string gpuid{"rtx-5090"};
707
+ std::uint32_t sockets{1};
708
+ std::uint32_t dies{2};
709
+ std::uint32_t cores{8};
710
+ std::uint32_t threadspercore{2};
711
+ std::uint32_t vcpus{16};
712
+ std::uint32_t memorygib{64};
713
+ std::uint32_t vramgib{32};
714
+ std::uint32_t overcommit{1};
715
+ std::uint32_t mttgthreads{4096};
716
+ bool mttg{true};
717
+ bool balloon{true};
718
+ bool virtiomem{true};
719
+ bool hugepages{true};
720
+ bool tpm{true};
721
+ bool lookingglass{true};
722
+ GpuMode gpu{GpuMode::vfio};
723
+ Accel accel{Accel::kvm};
724
+ };
725
+
726
+ /**
727
+ * computes the full topology (sockets x dies x cores x threads) the -smp
728
+ * line must be able to express through maxcpus.
729
+ * @param p the plan
730
+ * @return product of every topology dimension
731
+ */
732
+ [[nodiscard]] constexpr std::uint32_t topology(const Plan& p) noexcept {
733
+ return p.sockets * p.dies * p.cores * p.threadspercore;
734
+ }
735
+
736
+ /** maps an accel backend to its qemu -machine accel= spelling. */
737
+ [[nodiscard]] constexpr std::string_view accelname(Accel a) noexcept {
738
+ switch (a) {
739
+ case Accel::kvm: return "kvm";
740
+ case Accel::hvf: return "hvf";
741
+ case Accel::whpx: return "whpx";
742
+ case Accel::tcg: return "tcg";
743
+ }
744
+ return "kvm";
745
+ }
746
+
747
+ /**
748
+ * validates and normalizes a plan. clamps stay deliberately forgiving
749
+ * (vcpus to 4096, overcommit to 64, mttg pool to one million threads,
750
+ * threadspercore folded to 1 or 2) while structural violations (mttg
751
+ * pool below the vcpu count, empty name) fail with a typed error.
752
+ * @param p candidate plan, modified in place when accepted
753
+ * @return the normalized plan or an Err code
754
+ */
755
+ [[nodiscard]] std::expected<Plan, Err> validate(Plan p) {
756
+ p.vcpus = std::clamp(p.vcpus, 1u, kvcpumax);
757
+ p.overcommit = std::clamp(p.overcommit, 1u, kovercommitmax);
758
+ p.threadspercore = p.threadspercore == 1 ? 1 : 2;
759
+ if (p.mttg && p.mttgthreads < p.vcpus)
760
+ return std::unexpected(Err::mttgfloor);
761
+ p.mttgthreads = std::min(p.mttgthreads, kmttgmax);
762
+ if (p.name.empty())
763
+ return std::unexpected(Err::invalidplan);
764
+ return p;
765
+ }
766
+
767
+ /**
768
+ * emits the qemu 11.1.0 argv for the plan. this generator is the superset
769
+ * of the ts planner: hugepages memory-backend-file with prealloc, the
770
+ * virtio-mem-pci dimm (block granularity 2M, requested size memory/4), the
771
+ * swtpm chardev/tpmdev/tpm-crb triplet, vfio behind a dedicated pcie-root
772
+ * port rp1, venus-enabled virtio-gpu-gl under egl-headless and the looking
773
+ * glass ivshmem-plain device on a shared /dev/shm memdev.
774
+ * @param p validated plan
775
+ * @return argv tokens, suitable for exec or shell quoting
776
+ */
777
+ [[nodiscard]] std::vector<std::string> qemuargv(const Plan& p) {
778
+ const auto topo = topology(p);
779
+ const auto maxcpus = std::max(p.vcpus, topo);
780
+ std::vector<std::string> a{
781
+ "qemu-system-x86_64",
782
+ "-nodefaults",
783
+ "-machine",
784
+ std::format("q35,accel={},kernel-irqchip=split,hpet=off",
785
+ accelname(p.accel)),
786
+ "-cpu",
787
+ "host,kvm=on,l3-cache=on,topoext=on,host-cache-info=on,+x2apic",
788
+ "-smp",
789
+ std::format("cpus={},sockets={},dies={},cores={},threads={},maxcpus={}",
790
+ std::min(p.vcpus, topo), p.sockets, p.dies, p.cores,
791
+ p.threadspercore, maxcpus),
792
+ "-m",
793
+ std::format("{}G,slots=4,maxmem={}G", p.memorygib,
794
+ std::max(p.memorygib * 2, p.memorygib + 32)),
795
+ "-name",
796
+ std::format("{},debug-threads=on", p.name),
797
+ "-nographic",
798
+ "-serial",
799
+ "mon:stdio",
800
+ "-device",
801
+ "virtio-balloon-pci,deflate-on-oom=on,free-page-reporting=on",
802
+ "-device",
803
+ "virtio-net-pci,netdev=n0,mq=on,vectors=10",
804
+ "-netdev",
805
+ "tap,id=n0,vhost=on,queues=4,script=no,downscript=no",
806
+ };
807
+ if (p.hugepages) {
808
+ a.insert(a.end(),
809
+ {"-object",
810
+ std::format("memory-backend-file,id=mem0,size={}G,"
811
+ "mem-path=/dev/hugepages,share=on,prealloc=on",
812
+ p.memorygib),
813
+ "-numa", "node,memdev=mem0"});
814
+ }
815
+ if (p.virtiomem) {
816
+ a.insert(a.end(),
817
+ {"-device",
818
+ std::format("virtio-mem-pci,id=vmem0,memdev=mem1,"
819
+ "requested-size={}G",
820
+ std::max(1u, p.memorygib / 4)),
821
+ "-object",
822
+ std::format("memory-backend-ram,id=mem1,size={}G",
823
+ std::max(1u, p.memorygib / 2))});
824
+ }
825
+ if (p.tpm) {
826
+ a.insert(a.end(), {
827
+ "-chardev", "socket,id=chrtpm,path=/run/swtpm/swtpm-sock",
828
+ "-tpmdev", "emulator,id=tpm0,chardev=chrtpm",
829
+ "-device", "tpm-crb,tpmdev=tpm0",
830
+ });
831
+ }
832
+ switch (p.gpu) {
833
+ case GpuMode::vfio:
834
+ a.insert(a.end(), {
835
+ "-device",
836
+ "pcie-root-port,id=rp1,bus=pcie.0,slot=1,chassis=1,multifunction=on",
837
+ "-device",
838
+ "vfio-pci,host=01:00.0,bus=rp1,addr=00.0,multifunction=on,x-vga=on",
839
+ "-vga", "none",
840
+ });
841
+ break;
842
+ case GpuMode::virtio:
843
+ a.insert(a.end(), {"-device", "virtio-gpu-gl,hostmem=8G,blob=on,venus=on",
844
+ "-display", "egl-headless,gl=on"});
845
+ break;
846
+ default:
847
+ a.insert(a.end(), {"-vga", "none"});
848
+ break;
849
+ }
850
+ if (p.lookingglass && p.gpu == GpuMode::vfio) {
851
+ a.insert(a.end(), {
852
+ "-device", "ivshmem-plain,memdev=ivshmem",
853
+ "-object",
854
+ "memory-backend-file,id=ivshmem,share=on,"
855
+ "mem-path=/dev/shm/looking-glass,size=128M",
856
+ });
857
+ }
858
+ if (p.mttg) {
859
+ a.insert(a.end(),
860
+ {"-fw_cfg",
861
+ std::format("name=opt/aetherforge/mttg,string={}",
862
+ p.mttgthreads)});
863
+ }
864
+ return a;
865
+ }
866
+
867
+ /**
868
+ * renders the argv as a copy-pasteable shell command; tokens containing
869
+ * spaces or commas are single-quoted.
870
+ * @param p validated plan
871
+ * @return multi-line command text
872
+ */
873
+ [[nodiscard]] std::string qemucommand(const Plan& p) {
874
+ std::ostringstream os;
875
+ bool first = true;
876
+ for (const auto& tok : qemuargv(p)) {
877
+ if (!first)
878
+ os << " \\\n ";
879
+ first = false;
880
+ if (tok.find(' ') != std::string::npos ||
881
+ tok.find(',') != std::string::npos) {
882
+ os << '\'' << tok << '\'';
883
+ } else {
884
+ os << tok;
885
+ }
886
+ }
887
+ return os.str();
888
+ }
889
+
890
+ /**
891
+ * mttg (multi-threaded tcg) grid model: host lanes multiplex a larger
892
+ * virtual thread pool by parking the surplus and rotating parked threads
893
+ * onto idle lanes (work stealing). the counters feed the multiplex ratio
894
+ * the engine prints next to every emitted plan.
895
+ */
896
+ class Mttg {
897
+ public:
898
+ /**
899
+ * builds the grid from the host lane count and the requested virtual
900
+ * pool; the runnable set never exceeds the host lanes.
901
+ * @param hostthreads physical lanes (>= 1)
902
+ * @param virtualthreads requested pool size
903
+ */
904
+ explicit Mttg(std::uint32_t hostthreads, std::uint32_t virtualthreads)
905
+ : mhost(std::max(1u, hostthreads)),
906
+ mvirtual(std::max(mhost, virtualthreads)),
907
+ mrunnable(std::min(mhost, mvirtual)) {
908
+ mparked = mvirtual - mrunnable;
909
+ }
910
+
911
+ /** grows the virtual pool by n threads, unparking as lanes free up. */
912
+ void spawn(std::uint32_t n) {
913
+ mvirtual = std::min(kmttgmax, mvirtual + n);
914
+ const auto want = std::min(mhost, mvirtual);
915
+ if (mrunnable < want) {
916
+ const auto take = want - mrunnable;
917
+ mparked -= std::min(mparked, take);
918
+ mrunnable += take;
919
+ } else {
920
+ mparked += n;
921
+ }
922
+ }
923
+
924
+ /** work-stealing slice: parked threads rotate onto idle host lanes and
925
+ * return to the park set after their quantum, keeping occupancy
926
+ * host-bound exactly like mttg's round-robin vcpu scheduler. */
927
+ void steal() {
928
+ if (mparked == 0)
929
+ return;
930
+ const auto rotate = std::min(mparked, mhost);
931
+ mparked -= rotate;
932
+ mrunnable = std::min(mhost, mrunnable + rotate);
933
+ mparked += rotate; /* they go back after a slice */
934
+ mrunnable = mhost;
935
+ }
936
+
937
+ /** @return threads currently parked in the pool */
938
+ [[nodiscard]] std::uint32_t parked() const noexcept { return mparked; }
939
+
940
+ /** @return threads currently bound to a host lane */
941
+ [[nodiscard]] std::uint32_t runnable() const noexcept { return mrunnable; }
942
+
943
+ /** @return virtual pool divided by host lanes */
944
+ [[nodiscard]] double multiplex() const noexcept {
945
+ return static_cast<double>(mvirtual) /
946
+ static_cast<double>(mhost);
947
+ }
948
+
949
+ private:
950
+ std::uint32_t mhost;
951
+ std::uint32_t mvirtual;
952
+ std::uint32_t mparked{0};
953
+ std::uint32_t mrunnable{0};
954
+ };
955
+
956
+ } /* namespace vhe::forge */
957
+
958
+ /* -------------------------------------------------------------------- */
959
+ /* build mode 2: nvml + cuda interposition shim (unified-shim pattern) */
960
+ /* -------------------------------------------------------------------- */
961
+
962
+ #if defined(VHE_BUILD_PRELOAD)
963
+
964
+ extern "C" {
965
+
966
+ /* nvml abi surface, trimmed to what pynvml, gpustat and dcgm probe first. */
967
+ typedef int nvmlReturn_t; /* 0 = NVML_SUCCESS */
968
+ typedef struct nvmlDevice_st* nvmlDevice_t; /* opaque handle */
969
+ typedef struct { unsigned long long total, used, free; } nvmlMemory_t;
970
+ typedef struct { unsigned gpu, memory, encoder, decoder; } nvmlUtilization_t;
971
+
972
+ #define NVML_SUCCESS 0
973
+ #define NVML_ERROR_NOT_FOUND -9
974
+ #define NVML_ERROR_INVALID -1
975
+
976
+ /**
977
+ * resolves the real nvml symbol after this object, when a physical driver
978
+ * exists in the process. the unified shim forwards first and fabricates
979
+ * only on absence, exactly like rick-hsu/nvml-unified-shim on DGX hosts.
980
+ * @param name nvml symbol name
981
+ * @return real function pointer or nullptr
982
+ */
983
+ static void* realsymbol(const char* name) {
984
+ return dlsym(RTLD_NEXT, name);
985
+ }
986
+
987
+ /** returns the active profile key from the environment. */
988
+ static std::string shimprofile() {
989
+ return vhe::envstr("VHE_GPU_PROFILE").value_or("a100");
990
+ }
991
+
992
+ /** returns the active device count from the environment. */
993
+ static unsigned shimcount() {
994
+ return vhe::envuint("VHE_GPUS", 8, 1, 256);
995
+ }
996
+
997
+ /** maps a fake handle back to its device index (index-as-pointer). */
998
+ static unsigned shimindex(nvmlDevice_t device) {
999
+ return device ? static_cast<unsigned>(
1000
+ reinterpret_cast<std::uintptr_t>(device) - 1)
1001
+ : 0;
1002
+ }
1003
+
1004
+ nvmlReturn_t nvmlInit_v2(void) {
1005
+ auto real = reinterpret_cast<nvmlReturn_t (*)(void)>(
1006
+ realsymbol("nvmlInit_v2"));
1007
+ return real ? real() : NVML_SUCCESS;
1008
+ }
1009
+
1010
+ nvmlReturn_t nvmlShutdown(void) {
1011
+ auto real =
1012
+ reinterpret_cast<nvmlReturn_t (*)(void)>(realsymbol("nvmlShutdown"));
1013
+ return real ? real() : NVML_SUCCESS;
1014
+ }
1015
+
1016
+ nvmlReturn_t nvmlSystemGetDriverVersion(char* version,
1017
+ unsigned int length) {
1018
+ if (version == nullptr || length == 0)
1019
+ return NVML_ERROR_INVALID;
1020
+ std::snprintf(version, length, "%s",
1021
+ vhe::envstr("VHE_SMI_DRIVER").value_or("575.57.08").c_str());
1022
+ return NVML_SUCCESS;
1023
+ }
1024
+
1025
+ nvmlReturn_t nvmlSystemGetCudaDriverVersion(int* cudaversion) {
1026
+ if (cudaversion == nullptr)
1027
+ return NVML_ERROR_INVALID;
1028
+ *cudaversion = 12090; /* 1000*major + 10*minor = CUDA 12.9 */
1029
+ return NVML_SUCCESS;
1030
+ }
1031
+
1032
+ nvmlReturn_t nvmlDeviceGetCount_v2(unsigned int* count) {
1033
+ auto real = reinterpret_cast<nvmlReturn_t (*)(unsigned int*)>(
1034
+ realsymbol("nvmlDeviceGetCount_v2"));
1035
+ if (real != nullptr)
1036
+ return real(count);
1037
+ if (count == nullptr)
1038
+ return NVML_ERROR_INVALID;
1039
+ *count = shimcount();
1040
+ return NVML_SUCCESS;
1041
+ }
1042
+
1043
+ nvmlReturn_t nvmlDeviceGetHandleByIndex_v2(unsigned int index,
1044
+ nvmlDevice_t* device) {
1045
+ auto real = reinterpret_cast<nvmlReturn_t (*)(unsigned int,
1046
+ nvmlDevice_t*)>(
1047
+ realsymbol("nvmlDeviceGetHandleByIndex_v2"));
1048
+ if (real != nullptr)
1049
+ return real(index, device);
1050
+ if (device == nullptr || index >= shimcount())
1051
+ return NVML_ERROR_INVALID;
1052
+ /* index-as-pointer fake handles, unified-shim style */
1053
+ *device = reinterpret_cast<nvmlDevice_t>(
1054
+ static_cast<std::uintptr_t>(index + 1));
1055
+ return NVML_SUCCESS;
1056
+ }
1057
+
1058
+ nvmlReturn_t nvmlDeviceGetName(nvmlDevice_t device, char* name,
1059
+ unsigned int length) {
1060
+ auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t, char*,
1061
+ unsigned int)>(
1062
+ realsymbol("nvmlDeviceGetName"));
1063
+ if (real != nullptr)
1064
+ return real(device, name, length);
1065
+ if (name == nullptr || length == 0)
1066
+ return NVML_ERROR_INVALID;
1067
+ auto spec = vhe::findspec(shimprofile());
1068
+ if (!spec)
1069
+ return NVML_ERROR_NOT_FOUND;
1070
+ std::snprintf(name, length, "%s", (*spec)->name.data());
1071
+ return NVML_SUCCESS;
1072
+ }
1073
+
1074
+ nvmlReturn_t nvmlDeviceGetMemoryInfo(nvmlDevice_t device,
1075
+ nvmlMemory_t* memory) {
1076
+ auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
1077
+ nvmlMemory_t*)>(
1078
+ realsymbol("nvmlDeviceGetMemoryInfo"));
1079
+ if (real != nullptr)
1080
+ return real(device, memory);
1081
+ if (memory == nullptr)
1082
+ return NVML_ERROR_INVALID;
1083
+ auto spec = vhe::findspec(shimprofile());
1084
+ if (!spec)
1085
+ return NVML_ERROR_NOT_FOUND;
1086
+ const unsigned long long total = (*spec)->totalmib << 20; /* MiB to B */
1087
+ const unsigned long long used =
1088
+ total / 10 +
1089
+ vhe::fnv1a((*spec)->name, shimindex(device) + 1) % (total / 20);
1090
+ memory->total = total;
1091
+ memory->used = used;
1092
+ memory->free = total - used;
1093
+ return NVML_SUCCESS;
1094
+ }
1095
+
1096
+ nvmlReturn_t nvmlDeviceGetPowerManagementLimit(nvmlDevice_t device,
1097
+ unsigned int* limit) {
1098
+ auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
1099
+ unsigned int*)>(
1100
+ realsymbol("nvmlDeviceGetPowerManagementLimit"));
1101
+ if (real != nullptr)
1102
+ return real(device, limit);
1103
+ if (limit == nullptr)
1104
+ return NVML_ERROR_INVALID;
1105
+ auto spec = vhe::findspec(shimprofile());
1106
+ if (!spec)
1107
+ return NVML_ERROR_NOT_FOUND;
1108
+ *limit = (*spec)->powerw * 1000; /* nvml reports milliwatts */
1109
+ return NVML_SUCCESS;
1110
+ }
1111
+
1112
+ nvmlReturn_t nvmlDeviceGetTemperature(nvmlDevice_t device, int sensortype,
1113
+ unsigned int* temp) {
1114
+ (void)sensortype; /* only NVML_TEMPERATURE_GPU (0) is emulated */
1115
+ auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t, int,
1116
+ unsigned int*)>(
1117
+ realsymbol("nvmlDeviceGetTemperature"));
1118
+ if (real != nullptr)
1119
+ return real(device, sensortype, temp);
1120
+ if (temp == nullptr)
1121
+ return NVML_ERROR_INVALID;
1122
+ auto spec = vhe::findspec(shimprofile());
1123
+ if (!spec)
1124
+ return NVML_ERROR_NOT_FOUND;
1125
+ *temp = (*spec)->tempc;
1126
+ return NVML_SUCCESS;
1127
+ }
1128
+
1129
+ nvmlReturn_t nvmlDeviceGetUtilizationRates(nvmlDevice_t device,
1130
+ nvmlUtilization_t* util) {
1131
+ auto real = reinterpret_cast<nvmlReturn_t (*)(nvmlDevice_t,
1132
+ nvmlUtilization_t*)>(
1133
+ realsymbol("nvmlDeviceGetUtilizationRates"));
1134
+ if (real != nullptr)
1135
+ return real(device, util);
1136
+ if (util == nullptr)
1137
+ return NVML_ERROR_INVALID;
1138
+ auto spec = vhe::findspec(shimprofile());
1139
+ if (!spec)
1140
+ return NVML_ERROR_NOT_FOUND;
1141
+ util->gpu = (*spec)->utilpct;
1142
+ util->memory = (*spec)->utilpct / 3;
1143
+ util->encoder = 0;
1144
+ util->decoder = 0;
1145
+ return NVML_SUCCESS;
1146
+ }
1147
+
1148
+ nvmlReturn_t nvmlErrorString(nvmlReturn_t result) {
1149
+ (void)result;
1150
+ return NVML_SUCCESS; /* simplified: callers rarely print shim errors */
1151
+ }
1152
+
1153
+ /* cuda driver api surface, the minimal entry-point set FanBB2333/GpuAdapter
1154
+ * intercepts before applications reach libcudart; types follow cuda.h. */
1155
+ typedef unsigned int CUresult; /* 0 = CUDA_SUCCESS */
1156
+ typedef int CUdevice; /* ordinal */
1157
+
1158
+ nvmlReturn_t cuInit(unsigned int flags) {
1159
+ auto real = reinterpret_cast<CUresult (*)(unsigned int)>(
1160
+ realsymbol("cuInit"));
1161
+ return real ? static_cast<nvmlReturn_t>(real(flags)) : NVML_SUCCESS;
1162
+ }
1163
+
1164
+ nvmlReturn_t cuDriverGetVersion(int* version) {
1165
+ auto real = reinterpret_cast<CUresult (*)(int*)>(
1166
+ realsymbol("cuDriverGetVersion"));
1167
+ if (real != nullptr)
1168
+ return static_cast<nvmlReturn_t>(real(version));
1169
+ if (version != nullptr)
1170
+ *version = 12090; /* CUDA 12.9 */
1171
+ return NVML_SUCCESS;
1172
+ }
1173
+
1174
+ nvmlReturn_t cuDeviceGetCount(int* count) {
1175
+ auto real = reinterpret_cast<CUresult (*)(int*)>(
1176
+ realsymbol("cuDeviceGetCount"));
1177
+ if (real != nullptr)
1178
+ return static_cast<nvmlReturn_t>(real(count));
1179
+ if (count != nullptr)
1180
+ *count = static_cast<int>(shimcount());
1181
+ return NVML_SUCCESS;
1182
+ }
1183
+
1184
+ nvmlReturn_t cuDeviceGet(CUdevice* device, int ordinal) {
1185
+ auto real = reinterpret_cast<CUresult (*)(CUdevice*, int)>(
1186
+ realsymbol("cuDeviceGet"));
1187
+ if (real != nullptr)
1188
+ return static_cast<nvmlReturn_t>(real(device, ordinal));
1189
+ if (device != nullptr && ordinal >= 0 &&
1190
+ static_cast<unsigned>(ordinal) < shimcount())
1191
+ *device = ordinal;
1192
+ return NVML_SUCCESS;
1193
+ }
1194
+
1195
+ nvmlReturn_t cuDeviceGetName(char* name, int len, CUdevice device) {
1196
+ auto real = reinterpret_cast<CUresult (*)(char*, int, CUdevice)>(
1197
+ realsymbol("cuDeviceGetName"));
1198
+ if (real != nullptr)
1199
+ return static_cast<nvmlReturn_t>(real(name, len, device));
1200
+ if (name == nullptr || len <= 0)
1201
+ return NVML_ERROR_INVALID;
1202
+ auto spec = vhe::findspec(shimprofile());
1203
+ if (!spec)
1204
+ return NVML_ERROR_NOT_FOUND;
1205
+ std::snprintf(name, static_cast<std::size_t>(len), "%s",
1206
+ (*spec)->name.data());
1207
+ return NVML_SUCCESS;
1208
+ }
1209
+
1210
+ nvmlReturn_t cuDeviceTotalMem_v2(std::size_t* bytes, CUdevice device) {
1211
+ auto real = reinterpret_cast<CUresult (*)(std::size_t*, CUdevice)>(
1212
+ realsymbol("cuDeviceTotalMem_v2"));
1213
+ if (real != nullptr)
1214
+ return static_cast<nvmlReturn_t>(real(bytes, device));
1215
+ if (bytes == nullptr)
1216
+ return NVML_ERROR_INVALID;
1217
+ auto spec = vhe::findspec(shimprofile());
1218
+ if (!spec)
1219
+ return NVML_ERROR_NOT_FOUND;
1220
+ *bytes = (*spec)->totalmib << 20;
1221
+ return NVML_SUCCESS;
1222
+ }
1223
+
1224
+ nvmlReturn_t cuGetErrorString(CUresult error, const char** str) {
1225
+ (void)error;
1226
+ if (str != nullptr)
1227
+ *str = "no error"; /* gpuadapter reports a benign string */
1228
+ return NVML_SUCCESS;
1229
+ }
1230
+
1231
+ } /* extern "C" */
1232
+
1233
+ #endif /* VHE_BUILD_PRELOAD */
1234
+
1235
+ /* -------------------------------------------------------------------- */
1236
+ /* build mode 3: node 26 native addon (node_api.h) */
1237
+ /* -------------------------------------------------------------------- */
1238
+
1239
+ #if defined(VHE_BUILD_ADDON)
1240
+
1241
+ /**
1242
+ * napi wrapper around rendertable. signature:
1243
+ * generateSmi(profile?: string, gpuCount?: number, opts?: object)
1244
+ * opts fields: driver, cuda, mig, csv (booleans/strings).
1245
+ * errors surface as napi_throw_error with the spooferror detail, so
1246
+ * javascript callers handle failures through try/catch while the c++ side
1247
+ * stays exception-free.
1248
+ * @param env napi environment
1249
+ * @param info callback info with the arguments
1250
+ * @return string result or nullptr after throwing
1251
+ */
1252
+ static napi_value generatesmi(napi_env env, napi_callback_info info) {
1253
+ std::size_t argc = 3;
1254
+ napi_value args[3];
1255
+ napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr,
1256
+ nullptr);
1257
+ if (status != napi_ok) {
1258
+ napi_throw_error(env, nullptr, "napi: cannot read callback info");
1259
+ return nullptr;
1260
+ }
1261
+
1262
+ std::string profile = "a100";
1263
+ unsigned gpucount = 1;
1264
+ std::string driver{vhe::kdriverdefault};
1265
+ std::string cuda{vhe::kcudadefault};
1266
+ bool mig = false;
1267
+ bool csv = false;
1268
+
1269
+ if (argc >= 1 && args[0] != nullptr) {
1270
+ std::size_t len = 0;
1271
+ if (napi_get_value_string_utf8(env, args[0], nullptr, 0, &len) ==
1272
+ napi_ok &&
1273
+ len < 64) {
1274
+ profile.assign(len + 1, '\0');
1275
+ napi_get_value_string_utf8(env, args[0], profile.data(), len + 1,
1276
+ &len);
1277
+ profile.resize(len);
1278
+ }
1279
+ }
1280
+ if (argc >= 2 && args[1] != nullptr) {
1281
+ std::uint32_t n = 1;
1282
+ if (napi_get_value_uint32(env, args[1], &n) == napi_ok)
1283
+ gpucount = n;
1284
+ }
1285
+ if (argc >= 3 && args[2] != nullptr) {
1286
+ napi_value value;
1287
+ if (napi_get_named_property(env, args[2], "driver", &value) ==
1288
+ napi_ok &&
1289
+ value != nullptr) {
1290
+ std::size_t len = 0;
1291
+ if (napi_get_value_string_utf8(env, value, nullptr, 0, &len) ==
1292
+ napi_ok &&
1293
+ len < 32) {
1294
+ driver.assign(len + 1, '\0');
1295
+ napi_get_value_string_utf8(env, value, driver.data(), len + 1,
1296
+ &len);
1297
+ driver.resize(len);
1298
+ }
1299
+ }
1300
+ if (napi_get_named_property(env, args[2], "cuda", &value) == napi_ok &&
1301
+ value != nullptr) {
1302
+ std::size_t len = 0;
1303
+ if (napi_get_value_string_utf8(env, value, nullptr, 0, &len) ==
1304
+ napi_ok &&
1305
+ len < 32) {
1306
+ cuda.assign(len + 1, '\0');
1307
+ napi_get_value_string_utf8(env, value, cuda.data(), len + 1,
1308
+ &len);
1309
+ cuda.resize(len);
1310
+ }
1311
+ }
1312
+ bool flag = false;
1313
+ if (napi_get_named_property(env, args[2], "mig", &value) == napi_ok &&
1314
+ value != nullptr &&
1315
+ napi_get_value_bool(env, value, &flag) == napi_ok)
1316
+ mig = flag;
1317
+ if (napi_get_named_property(env, args[2], "csv", &value) == napi_ok &&
1318
+ value != nullptr &&
1319
+ napi_get_value_bool(env, value, &flag) == napi_ok)
1320
+ csv = flag;
1321
+ }
1322
+
1323
+ vhe::smiresult result =
1324
+ csv ? vhe::renderquerycsv(profile, gpucount, vhe::kqueryfields,
1325
+ false, driver, cuda)
1326
+ : vhe::rendertable(profile, gpucount, driver, cuda, mig);
1327
+ if (!result) {
1328
+ napi_throw_error(env, nullptr, result.error().detail.c_str());
1329
+ return nullptr;
1330
+ }
1331
+ napi_value out;
1332
+ status = napi_create_string_utf8(env, result->c_str(), result->size(),
1333
+ &out);
1334
+ if (status != napi_ok) {
1335
+ napi_throw_error(env, nullptr, "napi: cannot create result string");
1336
+ return nullptr;
1337
+ }
1338
+ return out;
1339
+ }
1340
+
1341
+ /** module registration: exports generateSmi plus identity metadata. */
1342
+ static napi_value moduleinit(napi_env env, napi_value exports) {
1343
+ napi_value fn;
1344
+ if (napi_create_function(env, "generateSmi", NAPI_AUTO_LENGTH,
1345
+ generatesmi, nullptr, &fn) == napi_ok)
1346
+ napi_set_named_property(env, exports, "generateSmi", fn);
1347
+
1348
+ napi_value driver;
1349
+ if (napi_create_string_utf8(env, vhe::kdriverdefault.data(),
1350
+ vhe::kdriverdefault.size(),
1351
+ &driver) == napi_ok)
1352
+ napi_set_named_property(env, exports, "driverVersion", driver);
1353
+
1354
+ napi_value cuda;
1355
+ if (napi_create_string_utf8(env, vhe::kcudadefault.data(),
1356
+ vhe::kcudadefault.size(), &cuda) == napi_ok)
1357
+ napi_set_named_property(env, exports, "cudaVersion", cuda);
1358
+
1359
+ napi_value width;
1360
+ if (napi_create_uint32(env, static_cast<std::uint32_t>(vhe::ktable),
1361
+ &width) == napi_ok)
1362
+ napi_set_named_property(env, exports, "tableWidth", width);
1363
+
1364
+ return exports;
1365
+ }
1366
+
1367
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, moduleinit)
1368
+
1369
+ #endif /* VHE_BUILD_ADDON */
1370
+
1371
+ /* -------------------------------------------------------------------- */
1372
+ /* build mode 1: the virtual nvidia-smi adapter command line */
1373
+ /* -------------------------------------------------------------------- */
1374
+
1375
+ #if defined(VHE_BUILD_CLI)
1376
+
1377
+ /**
1378
+ * prints a short usage block; mirrors nvidia-smi --help spirit without
1379
+ * pretending to support the full flag surface.
1380
+ */
1381
+ static void printusage() {
1382
+ std::println("nvidia-smi (virtual hardware engine v5 virtual identity)");
1383
+ std::println("usage: nvidia-smi [profile] [count] [options]");
1384
+ std::println(" options:");
1385
+ std::println(" -L, --list-gpus list devices with uuids");
1386
+ std::println(" --query-gpu=FIELDS -f csv csv query mode");
1387
+ std::println(" --format=csv,noheader csv without header line");
1388
+ std::println(" environment: VHE_GPU_PROFILE, VHE_GPUS, VHE_SMI_DRIVER,");
1389
+ std::println(" VHE_SMI_CUDA, VHE_MIG, VHE_DEBUG");
1390
+ }
1391
+
1392
+ /**
1393
+ * entry point: argument parsing with std::expected error propagation and
1394
+ * exit codes 0 (ok), 1 (render failure), 2 (usage failure).
1395
+ */
1396
+ int main(int argc, char** argv) {
1397
+ std::string profile =
1398
+ vhe::envstr("VHE_GPU_PROFILE").value_or("a100");
1399
+ unsigned gpucount = vhe::envuint("VHE_GPUS", 8, 1, 256);
1400
+ std::string driver = vhe::envstr("VHE_SMI_DRIVER").value_or("575.57.08");
1401
+ std::string cuda = vhe::envstr("VHE_SMI_CUDA").value_or("12.9");
1402
+ std::string queryfields; /* filled from --query-gpu=... */
1403
+ bool listgpus = false;
1404
+ bool querycsv = false;
1405
+ bool noheader = false;
1406
+ bool mig = vhe::envuint("VHE_MIG", 0, 0, 1) != 0;
1407
+
1408
+ for (int i = 1; i < argc; ++i) {
1409
+ std::string_view arg{argv[i]};
1410
+ if (arg == "-L" || arg == "--list-gpus") {
1411
+ listgpus = true;
1412
+ } else if (arg.starts_with("--query-gpu=")) {
1413
+ querycsv = true;
1414
+ queryfields = std::string{
1415
+ arg.substr(std::string_view{"--query-gpu="}.size())};
1416
+ } else if (arg == "-f" || arg == "--format" ||
1417
+ arg.starts_with("--format=")) {
1418
+ std::string_view fmtspec =
1419
+ arg.starts_with("--format=")
1420
+ ? arg.substr(std::string_view("--format=").size())
1421
+ : (i + 1 < argc ? std::string_view{argv[++i]}
1422
+ : std::string_view{"csv"});
1423
+ if (fmtspec.find("noheader") != std::string_view::npos)
1424
+ noheader = true;
1425
+ } else if (arg == "--help" || arg == "-h") {
1426
+ printusage();
1427
+ return 0;
1428
+ } else if (!arg.empty() && arg[0] != '-') {
1429
+ /* positional: first is profile, second is count */
1430
+ auto probe = vhe::findspec(arg);
1431
+ if (probe) {
1432
+ profile = std::string{arg};
1433
+ } else {
1434
+ try {
1435
+ unsigned n =
1436
+ static_cast<unsigned>(std::stoul(std::string{arg}));
1437
+ if (n >= 1 && n <= 256)
1438
+ gpucount = n;
1439
+ } catch (...) {
1440
+ std::println(stderr,
1441
+ "nvidia-smi: unrecognized argument '{}'",
1442
+ arg);
1443
+ printusage();
1444
+ return 2;
1445
+ }
1446
+ }
1447
+ } else {
1448
+ std::println(stderr, "nvidia-smi: unsupported flag '{}'", arg);
1449
+ printusage();
1450
+ return 2;
1451
+ }
1452
+ }
1453
+
1454
+ vhe::smiresult result;
1455
+ if (listgpus) {
1456
+ result = vhe::renderdevicelist(profile, gpucount);
1457
+ } else if (querycsv) {
1458
+ result = vhe::renderquerycsv(profile, gpucount, queryfields,
1459
+ noheader, driver, cuda);
1460
+ } else {
1461
+ result = vhe::rendertable(profile, gpucount, driver, cuda, mig);
1462
+ }
1463
+
1464
+ if (!result) {
1465
+ std::println(stderr, "nvidia-smi: {}", result.error().detail);
1466
+ return 1;
1467
+ }
1468
+ std::print("{}", *result);
1469
+ return 0;
1470
+ }
1471
+
1472
+ #endif /* VHE_BUILD_CLI */
1473
+
1474
+ /* -------------------------------------------------------------------- */
1475
+ /* build mode 4: the aetherforge plan compiler */
1476
+ /* -------------------------------------------------------------------- */
1477
+
1478
+ #if defined(VHE_BUILD_FORGE)
1479
+
1480
+ /**
1481
+ * prints a short usage block for the forge mode; the three flags cover the
1482
+ * dimensions operators tune per lab (vcpus, mttg pool, guest memory).
1483
+ */
1484
+ static void printforgeusage() {
1485
+ std::println("aetherforge — emit a qemu 11.1.0 plan");
1486
+ std::println("usage: aetherforge [--smp N] [--mttg N] [--mem GIB]");
1487
+ std::println(" --smp N vcpus presented to the guest");
1488
+ std::println(" --mttg N virtual threads in the mttg pool");
1489
+ std::println(" --mem N guest memory in GiB");
1490
+ }
1491
+
1492
+ /**
1493
+ * forge entry point: parses the three tuning flags, validates the plan
1494
+ * through the same expected<Plan, Err> channel the library exposes, prints
1495
+ * the mttg multiplex summary line and the fully quoted qemu command.
1496
+ * malformed numbers are caught and reported instead of aborting.
1497
+ */
1498
+ int main(int argc, char** argv) {
1499
+ vhe::forge::Plan plan{};
1500
+ if (argc > 1 && std::string_view{argv[1]} == "--help") {
1501
+ printforgeusage();
1502
+ return 0;
1503
+ }
1504
+ for (int i = 1; i + 1 < argc; ++i) {
1505
+ const std::string_view k{argv[i]};
1506
+ const std::string_view v{argv[i + 1]};
1507
+ if (k != "--smp" && k != "--mttg" && k != "--mem")
1508
+ continue;
1509
+ unsigned parsed = 0;
1510
+ try {
1511
+ parsed = static_cast<unsigned>(std::stoul(std::string{v}));
1512
+ } catch (...) {
1513
+ std::println(stderr, "aetherforge: unparsable value '{}' for {}",
1514
+ v, k);
1515
+ return 2;
1516
+ }
1517
+ if (k == "--smp") {
1518
+ plan.vcpus = parsed;
1519
+ } else if (k == "--mttg") {
1520
+ plan.mttg = true;
1521
+ plan.mttgthreads = parsed;
1522
+ } else {
1523
+ plan.memorygib = parsed;
1524
+ }
1525
+ ++i; /* the value token is consumed */
1526
+ }
1527
+ auto ok = vhe::forge::validate(std::move(plan));
1528
+ if (!ok) {
1529
+ std::println(stderr, "aetherforge: invalid plan (error {})",
1530
+ static_cast<unsigned>(ok.error()));
1531
+ return 1;
1532
+ }
1533
+ const auto host = std::max(
1534
+ 1u, static_cast<unsigned>(std::thread::hardware_concurrency()));
1535
+ vhe::forge::Mttg grid{host, ok->mttgthreads};
1536
+ grid.steal();
1537
+ std::println("# host threads {} virtual {} multiplex {:.1f}x", host,
1538
+ ok->mttgthreads, grid.multiplex());
1539
+ std::println("{}\n", vhe::forge::qemucommand(*ok));
1540
+ return 0;
1541
+ }
1542
+
1543
+ #endif /* VHE_BUILD_FORGE */