Add Apple Silicon GPU monitoring and publish as System Vitals - #1
Merged
Conversation
Adds GPU utilization and GPU memory as two independently toggleable status bar resources. systeminformation cannot supply this: si.graphics() shells out to system_profiler, costs ~230ms, and reports no utilization field at all. Instead, statistics are read from the IOKit registry with `ioreg -r -d 1 -w 0 -c IOAccelerator`, which takes ~20ms and needs no elevated privileges, unlike powermetrics. Availability is decided by whether a machine actually reports usable statistics rather than by checking the architecture, so this also covers Intel Macs and stays hidden anywhere the data is missing. The sampler caches for 100ms so that a single update tick costs one ioreg invocation across both resources, and resolves to null rather than rejecting on any failure. That matters because ResMon.update() collects resources with Promise.all and has no error handling, so one rejection would break the setTimeout chain and freeze the whole status bar. GPU memory is reported as in-use over allocated, not used out of VRAM: Apple Silicon uses unified memory with no fixed GPU partition, so the second figure is what the driver has currently claimed from system memory rather than a fixed capacity. Also documents the existing behaviour that resources capture their configuration once at activation, so show.* and unit settings need a window reload to take effect. New settings: resmon.show.gpu, resmon.show.gpumem, resmon.gpu.unit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
systeminformation was listed in both dependencies and devDependencies,
and the lockfile recorded it with "dev": true. As a result vsce pruned it
as a dev dependency and produced a VSIX containing no node_modules at
all, so an installed build failed at activation on
require('systeminformation'). Running from source with F5 was unaffected,
which is why this went unnoticed.
Drops the duplicate devDependencies entry and clears the stale dev flag
in the lockfile. Verified with `npm ci --omit=dev`, which now resolves
the dependency, and by repackaging: the VSIX went from 13 files with no
node_modules to 34 files including systeminformation.
Also excludes .claude and .github from the package, neither of which
belongs in a published extension.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Prepares the fork for publishing under its own marketplace identity rather than the upstream author's. Identity: publisher sachinsachdeva, id system-vitals, version reset to 1.0.0 for a fresh listing. Repository, bugs and homepage now point at this fork. The icon is replaced, as the previous one was the upstream author's artwork. Adds LICENSE.md recording that upstream declares no license, so the redistribution terms for this project are unresolved, and that only the changes made in this fork are ours to license. This documents the situation rather than asserting rights over code we do not own. Switches activation from "*" to onStartupFinished so the extension no longer loads during editor startup. That API needs VS Code 1.53, which forced the rest: @types/vscode 1.53 replaces the deprecated vscode module and its postinstall hook, and since those typings use `unknown`, TypeScript moves from 2.6 to 5.9. The only source change that required was dropping a meaningless `async` from an abstract method declaration, which TypeScript 4+ rejects. vsce now packages with no warnings and no override flags, where it previously needed --allow-star-activation and --allow-missing-repository. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With System Vitals published as its own extension, sharing the resmon.* configuration namespace with the upstream project meant that installing both would give you two status bars driven by one set of keys, with no way to configure them independently. Renames all 17 contributed settings and both getConfiguration() call sites. Resource subclasses are unaffected, as they address keys relative to the namespace. The repository URL keeps the resmon name, since the GitHub repository is still called that; only the configuration namespace moves. Settings do not migrate: anyone coming from resmon has to set their preferences again, which the changelog now states. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Settings now apply live. Each Resource held the WorkspaceConfiguration it was handed at construction, and because that object is an immutable snapshot, the refreshed one ResMon fetched every tick never reached it. Every show.* and unit setting was therefore fixed at activation and needed a window reload. Adds setConfig(), called on each resource per tick. That exposed a second problem: _maxWidth only ever grows, so a setting that shortens a display, such as switching memory from B to GB, would leave the status bar padded at the old width for the rest of the session. A configuration listener now resets the high water mark when settings actually change. Memory ignored its unit setting: it read the key 'memunit' while the contributed key is 'mem.unit', so it always rendered GB. convertBytesToLargestUnit labelled its output from the Units enum, which yields "G" and "None" rather than "GB" and "B". Adds an explicit label lookup. Its unused precision parameter is gone; it already deferred to getPrecision(). The base isShown() hardcoded false instead of using _isShownByDefault, making that constructor argument dead. Now used, and the two values that disagreed with the manifest (cputemp, battery) are aligned with it. This is a no-op for contributed keys, since VS Code returns the contributed default, but it removes a trap for any key added later. Removes the unfinished Network resource. It logged every interface to the console on activation and its getDisplay() returned "". With isShown() honouring _isShownByDefault it would have started rendering an empty column, since show.net is not a contributed key and it was constructed with a default of true. Recoverable from 8720281 if picked back up. Corrects the show.precision description, which was copied from updatefrequencyms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two screenshots carried over from upstream were the original author's captures of the original extension: a Windows status bar showing a C: drive, with no GPU metrics. Same provenance question as the icon, and no longer an accurate picture of what this extension displays. Replaces them with a capture of System Vitals running on Apple Silicon, showing the default set of CPU usage, CPU frequency, GPU utilization and memory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI could not have passed. It targeted Node 10 and 12, both long EOL and below the 14.17 that TypeScript 5 requires, and invoked a bare `tsc`, which is not on PATH in a run step. It also used checkout@v2 and setup-node@v1. Rebuilds it on Node 20 and 22 with current actions, and adds lint, test and package steps. Packaging in CI is the point: the systeminformation bug shipped a broken extension that compiled and linted cleanly, and only inspecting the built artifact catches that class of problem, so the workflow now asserts the runtime dependency is present in the VSIX and uploads it for download from the pull request. Adds unit tests over the logic that can run outside the editor, using the Node test runner so nothing new is added to the dependency tree. They cover the ioreg parser, including the "In use system memory (driver)" sibling key that a looser pattern would mistake for the real value, and its behaviour on empty, malformed and truncated input; the sampler's guarantee that it resolves rather than rejecting, since a rejection would freeze the status bar; and that every unit and disk format offered in the manifest has a mapping behind it. Tests live in src/test and compile to out/test, which .vscodeignore already excludes, so they do not ship. Verified: the packaged VSIX contains no test files. Drops @types/mocha, which was unused, and whose global describe/it declarations would have shadowed the test runner's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
checkout, setup-node and upload-artifact at v4 target the Node 20 action runtime, which GitHub has deprecated and is already force-running on Node 24. v5 targets Node 24 directly, which clears the warning on every run before it becomes a failure. The build matrix is unaffected: it selects the Node used to compile and test, not the runtime the actions themselves execute on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Node 20 is in maintenance and its action runtime is already deprecated on GitHub runners. Testing against the current LTS and the current release matches where users and the runners actually are. The VSIX upload stays pinned to the 22.x job, which is still in the matrix, so exactly one artifact is produced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds GPU monitoring on macOS and prepares the fork to be published under its own marketplace identity.
GPU monitoring
systeminformationcannot supply GPU utilization on macOS —si.graphics()shells out tosystem_profiler, costs ~230ms, and has no utilization field. Statistics instead come from the IOKit registry viaioreg -r -d 1 -w 0 -c IOAccelerator, which takes ~20ms and needs no elevated privileges, unlikepowermetrics.Two independently toggleable resources: GPU utilization and GPU memory. Availability is gated on whether a machine actually reports usable statistics rather than on architecture, so this covers Intel Macs too and stays hidden everywhere else.
GPU memory reads as in use / allocated, not used-of-VRAM — Apple Silicon has unified memory with no fixed GPU partition, so the denominator is what the driver has currently claimed.
The sampler caches for 100ms so one tick costs a single
ioregcall across both resources, and resolves tonullrather than rejecting on failure. That last part matters:ResMon.update()gathers resources withPromise.alland has no error handling, so one rejection would break thesetTimeoutchain and freeze the entire status bar.Packaging fix
systeminformationwas listed in bothdependenciesanddevDependencies, and the lockfile recorded it"dev": true. vsce pruned it and produced a VSIX with nonode_modulesat all, so an installed build died on activation atrequire('systeminformation'). Running from source with F5 was unaffected, which is why it went unnoticed.Rebrand
Published as
sachinsachdeva.system-vitals, version reset to 1.0.0. New icon, since the previous one was the upstream author's artwork.LICENSE.mdrecords that upstream declares no license, so redistribution terms are unresolved, and that only the changes made here are ours to license.Settings move from
resmon.*tosystemvitals.*so the two extensions stay independent when both are installed. Existing settings do not migrate.Activation moves from
*toonStartupFinished, which requires VS Code 1.53. That forced@types/vscodein place of the deprecatedvscodemodule, and TypeScript 2.6 to 5.9. One line of source changed: a meaninglessasyncon an abstract declaration, which TypeScript 4+ rejects.Bug fixes
Resourceheld theWorkspaceConfigurationsnapshot it was constructed with, soshow.*and unit changes needed a window reload. Now pushed to each resource per tick._maxWidthonly grows, so a setting that shortens a display left the bar padded at the old width. A configuration listener now resets it.Memoryread'memunit'instead of the contributed'mem.unit', so it always rendered GB.convertBytesToLargestUnitlabelled units from the enum, yielding "G" and "None" instead of "GB" and "B".isShown()hardcodedfalseinstead of using_isShownByDefault, making that argument dead.Networkresource, which logged every interface to the console on activation and returned"". WithisShown()honouring_isShownByDefaultit would have started rendering an empty column.Verification
grepexactly on identical capturedioregtext across three snapshots; empty, garbage, truncated and missing-key inputs all returnnullwithout throwing; the"In use system memory (driver)"sibling key is not mistaken for the real one.ioregunreachable resolvesnullwithout rejecting, and non-darwin returnsnullwith zero spawns.vscodeAPI across six configurations — defaults, GPU memory on,gpu.unit: MB,precision: 0, both toggles off, and forced non-macOS.--allow-star-activationand--allow-missing-repository.Not yet verified: installing the VSIX and exercising the
onStartupFinishedpath inside a real editor.