feat(onboard): add hosted inference to portable profile - #8578
Conversation
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughPortable onboarding now resolves hosted inference descriptors from bootstrap credentials and S3. It validates credentials, endpoints, and models. Dashboard lifecycle management is separate from host dashboard forwarding. Recovery and deployment verification honor persisted forwarding state. ChangesPortable inference onboarding
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Onboarding
participant SandboxRegistry
participant Recovery
participant DeploymentVerification
Onboarding->>SandboxRegistry: persist dashboardForwardEnabled
Recovery->>SandboxRegistry: read dashboardForwardEnabled
alt Dashboard forwarding enabled
Recovery->>Recovery: recover dashboard/API forward
Recovery->>DeploymentVerification: verify dashboard forward
else Dashboard forwarding disabled
Recovery->>Recovery: skip dashboard/API forward recovery
Recovery->>DeploymentVerification: verify gateway and inference
end
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 3📝 Generate docstrings 💡
⚔️ Resolve merge conflicts 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/lib/onboard/command.test.ts (1)
532-540: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert removal of initially unset portable variables.
toMatchObjectpermits extra keys afterrunOnboardCommandreturns. The test does not detect leakedNEMOCLAW_OLLAMA_NO_AUTOSTART, policy variables, orNEMOCLAW_EXPERIMENTAL_PROFILE.Use
toEqualwith the initial environment object, or assert that each initially unset portable variable is absent after onboarding.Proposed test change
- expect(env).toMatchObject({ + expect(env).toEqual({ S3_BUCKET: "portable-inference", S3_KEY: "path/credential.b64", COMPATIBLE_API_KEY: "previous-compatible-key",The PR objective requires scoped environment restoration.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/command.test.ts` around lines 532 - 540, Update the environment assertion in the onboarding test to verify exact restoration rather than a subset: replace toMatchObject with toEqual against the initial environment object, or explicitly assert absence of every initially unset portable variable, including NEMOCLAW_OLLAMA_NO_AUTOSTART, policy variables, and NEMOCLAW_EXPERIMENTAL_PROFILE.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/onboard/experimental/portable-inference-source.test.ts`:
- Around line 35-39: Stub readActivatedDescriptor in every listed
source-selection test in
src/lib/onboard/experimental/portable-inference-source.test.ts (lines 35-39,
113-127, 129-152, 154-171, and 173-184) by passing a shared
noActivatedDescriptor fake returning null to resolvePortableInferenceSource,
ensuring tests do not depend on the external activation file.
---
Nitpick comments:
In `@src/lib/onboard/command.test.ts`:
- Around line 532-540: Update the environment assertion in the onboarding test
to verify exact restoration rather than a subset: replace toMatchObject with
toEqual against the initial environment object, or explicitly assert absence of
every initially unset portable variable, including NEMOCLAW_OLLAMA_NO_AUTOSTART,
policy variables, and NEMOCLAW_EXPERIMENTAL_PROFILE.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 4919853f-21ea-448c-9ba7-262c243ea2c1
📒 Files selected for processing (4)
src/lib/onboard/command.test.tssrc/lib/onboard/command.tssrc/lib/onboard/experimental/portable-inference-source.test.tssrc/lib/onboard/experimental/portable-inference-source.ts
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
PR Review Advisor — No blocking findings reportedAdvisor assessment: No blocking advisor findings reported Model lanes
Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests. 4 semantic terminology decisionsTerminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.
E2E guidanceAdvisory only. A maintainer can dispatch the default E2E suite against this exact revision. Recommended E2E: 1 optional E2E recommendation
2 warnings · 0 suggestionsWarningsWarnings do not block.
|
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/onboard/machine/handlers/finalization.ts (1)
246-272: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPass the dashboard-forward policy into deployment verification.
manageDashboardForwardonly controls forward creation and output.deps.verifyDeployment(sandboxName, verifyChain)still uses the default host-forward check. A portable run therefore verifies an intentionally absent host forward and pauses onboarding.
src/lib/onboard/machine/handlers/finalization.ts#L246-L272: Extenddeps.verifyDeploymentto accept verification options. Pass{ verifyDashboardForward: manageDashboardForward }.src/lib/onboard/machine/handlers/finalization.test.ts#L219-L236: Assert that portable finalization passesverifyDashboardForward: falseto the dependency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/machine/handlers/finalization.ts` around lines 246 - 272, Update src/lib/onboard/machine/handlers/finalization.ts lines 246-272 so verifyDeployment accepts and receives the options object { verifyDashboardForward: manageDashboardForward }, preserving the existing verification flow. Update src/lib/onboard/machine/handlers/finalization.test.ts lines 219-236 to assert that portable finalization passes verifyDashboardForward: false to the dependency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/onboard/sandbox-reuse.ts`:
- Line 108: Update the sandbox reuse flow around dashboardForwardEnabled so a
true-to-false manageDashboard transition stops any existing host forward before
persisting false. Only write the disabled state after the stop succeeds;
propagate or handle stop failures without recording false. Add a reuse test
covering this transition and verifying the persisted lifecycle state.
---
Outside diff comments:
In `@src/lib/onboard/machine/handlers/finalization.ts`:
- Around line 246-272: Update src/lib/onboard/machine/handlers/finalization.ts
lines 246-272 so verifyDeployment accepts and receives the options object {
verifyDashboardForward: manageDashboardForward }, preserving the existing
verification flow. Update src/lib/onboard/machine/handlers/finalization.test.ts
lines 219-236 to assert that portable finalization passes
verifyDashboardForward: false to the dependency.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 1bd38c9d-2a61-43df-848c-da57e81dfbd0
📒 Files selected for processing (17)
src/lib/actions/sandbox/doctor.tssrc/lib/actions/sandbox/forward-recovery.tssrc/lib/actions/sandbox/gateway-restart.tssrc/lib/actions/sandbox/process-recovery.tssrc/lib/onboard.tssrc/lib/onboard/agent-dashboard-forward.test.tssrc/lib/onboard/agent-dashboard-forward.tssrc/lib/onboard/dashboard-runtime.tssrc/lib/onboard/machine/handlers/finalization.test.tssrc/lib/onboard/machine/handlers/finalization.tssrc/lib/onboard/sandbox-registration.tssrc/lib/onboard/sandbox-reuse.test.tssrc/lib/onboard/sandbox-reuse.tssrc/lib/state/registry/types.tssrc/lib/verify-deployment.test.tssrc/lib/verify-deployment.tstest/recover-port-forward.test.ts
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
|
Security review on PRA-1’s portable-to-SSRF regression and a default AWS CLI subprocess-boundary test also remain outstanding. |
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/onboard/sandbox-reuse.ts (1)
125-125: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPersist the disabled-forward state before reused metadata.
Line 114 writes
dashboardPort: 0throughupdateReusedSandboxMetadata, but Line 125 writesdashboardForwardEnabled: falseonly afterward. If the process stops between these writes, the registry can retaindashboardForwardEnabled: truewithdashboardPort: 0. Recovery can then treat a stopped forward as active.Write
dashboardForwardEnabledanddashboardPorttogether before callingupdateReusedSandboxMetadata. Add an ordering test for the disabled path.Proposed fix
- input.updateReusedSandboxMetadata( - input.sandboxName, - input.agent, - input.model, - input.provider, - dashboardPort, - input.selectionVerified, - input.sandboxGpuConfig, - ); (input.updateSandbox ?? registry.updateSandbox)(input.sandboxName, { ...getHermesDashboardRegistryFields(hermesDashboardState), dashboardForwardEnabled: manageDashboard, + dashboardPort, gatewayName: input.gatewayName, gatewayPort: input.gatewayPort, }); + input.updateReusedSandboxMetadata( + input.sandboxName, + input.agent, + input.model, + input.provider, + dashboardPort, + input.selectionVerified, + input.sandboxGpuConfig, + );As per path instructions, persisted state must converge across resume and recovery paths.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/sandbox-reuse.ts` at line 125, Update the reused-sandbox metadata flow around updateReusedSandboxMetadata so the disabled path persists dashboardForwardEnabled: false and dashboardPort: 0 in the same write before any subsequent metadata update. Preserve the enabled path behavior, and add an ordering test that verifies the disabled state is written atomically before updateReusedSandboxMetadata completes.Source: Path instructions
🧹 Nitpick comments (2)
src/lib/onboard/experimental/portable-inference-source.ts (1)
137-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse overloads on
hmacto remove the type assertions.
hmacreturnsBuffer | string. Lines 161-165 then need fiveas Buffer/as stringassertions. Overloads make the return type follow theencodingargument, so the assertions disappear and a wrong usage becomes a compile error.♻️ Proposed refactor
-function hmac(key: Buffer | string, value: string, encoding?: "hex"): Buffer | string { - const digest = createHmac("sha256", key).update(value); - return encoding === "hex" ? digest.digest("hex") : digest.digest(); -} +function hmac(key: Buffer | string, value: string): Buffer; +function hmac(key: Buffer | string, value: string, encoding: "hex"): string; +function hmac(key: Buffer | string, value: string, encoding?: "hex"): Buffer | string { + const digest = createHmac("sha256", key).update(value); + return encoding === "hex" ? digest.digest("hex") : digest.digest(); +}Then drop the
as Bufferandas stringassertions at lines 161-165.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/experimental/portable-inference-source.ts` around lines 137 - 140, Update the hmac function with overloads that return Buffer when encoding is omitted and string when encoding is "hex", while retaining the existing implementation behavior. Then remove the related as Buffer and as string assertions from the hmac call sites around the derived key and digest values so TypeScript infers the correct types.src/lib/onboard/experimental/portable-inference-source.test.ts (1)
171-177: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAssert the HTTPS-only curl guard.
The test verifies the Authorization header, secret redaction, and the process limits. It does not verify
proto = "=https". If that line is removed from the curl config, this test stays green while the scheme restriction is gone. The scheme restriction is the control that keeps descriptor retrieval on HTTPS.Add an assertion on the config text.
💚 Proposed addition
expect(String(options.input)).toContain("Authorization: AWS4-HMAC-SHA256"); expect(String(options.input)).not.toContain(TEST_SECRET_ACCESS_KEY); + expect(String(options.input)).toContain('proto = "=https"'); + expect(String(options.input)).toContain("https://"); + expect(String(options.input)).not.toContain("http://"); expect(options).toMatchObject({As per path instructions, "Migration tests must prove the superseded path is unreachable" and tests should flag assertions that "make a test pass without exercising its claim."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard/experimental/portable-inference-source.test.ts` around lines 171 - 177, Extend the curl configuration assertions in the portable inference source test to require the HTTPS-only setting `proto = "=https"` in the config text. Keep the existing Authorization, secret-redaction, and process-limit assertions unchanged.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/onboard/experimental/portable-inference-source.test.ts`:
- Around line 184-204: Update the redaction test around
resolvePortableInferenceSource to use a captureError helper that throws if the
invocation returns normally, ensuring both branches always execute assertions.
For the bootstrap credential branch, assert the captured error contains the
generic message and excludes bootstrapSecret; for the credential descriptor
branch, retain the generic-message assertion and descriptorSecret exclusion.
- Around line 67-82: Make readPortableInferenceBootstrapFile accept an optional
runtime bootstrap path parameter, defaulting to the existing production path,
and use that parameter when selecting the runtime candidate. Update the test
around readPortableInferenceBootstrapFile to pass a non-existent temporary path
so it always exercises the Desktop branch without depending on host filesystem
state.
In `@src/lib/onboard/experimental/portable-inference-source.ts`:
- Around line 13-15: Document the accepted scope, owning team, object-content
rotation procedure, retirement/relocation plan, and compatibility, security, and
validation expectations for the fixed target constants
PORTABLE_INFERENCE_S3_REGION, PORTABLE_INFERENCE_S3_BUCKET, and
PORTABLE_INFERENCE_S3_KEY; include a link to the tracking GitHub issue and
ensure the lifecycle decision covers bucket or account changes.
- Around line 72-84: Update the bootstrap-reading function around the buffer
allocation and return path to clear the full buffer before throwing for empty or
oversized credentials, and return an independent copy of the bytes rather than
buffer.subarray(0, offset). Ensure resolvePortableInferenceSource’s existing
rawBootstrap.fill(0) clears all credential data, including any trailing byte
read past offset.
In `@src/lib/security/trusted-private-endpoint.ts`:
- Around line 275-284: Update the isOpenShellManagedHost() branch to return the
same rejection when options.allowExplicitLoopback is false, covering
inference.local and host.*.internal aliases while preserving existing acceptance
for non-portable flows. Add regression coverage for portable rejection of both
alias forms and allowed behavior when loopback access is enabled, keeping
deny-by-default and fail-closed handling intact.
---
Outside diff comments:
In `@src/lib/onboard/sandbox-reuse.ts`:
- Line 125: Update the reused-sandbox metadata flow around
updateReusedSandboxMetadata so the disabled path persists
dashboardForwardEnabled: false and dashboardPort: 0 in the same write before any
subsequent metadata update. Preserve the enabled path behavior, and add an
ordering test that verifies the disabled state is written atomically before
updateReusedSandboxMetadata completes.
---
Nitpick comments:
In `@src/lib/onboard/experimental/portable-inference-source.test.ts`:
- Around line 171-177: Extend the curl configuration assertions in the portable
inference source test to require the HTTPS-only setting `proto = "=https"` in
the config text. Keep the existing Authorization, secret-redaction, and
process-limit assertions unchanged.
In `@src/lib/onboard/experimental/portable-inference-source.ts`:
- Around line 137-140: Update the hmac function with overloads that return
Buffer when encoding is omitted and string when encoding is "hex", while
retaining the existing implementation behavior. Then remove the related as
Buffer and as string assertions from the hmac call sites around the derived key
and digest values so TypeScript infers the correct types.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: c8704d47-459d-410f-9065-82aa1ec6c1d1
📒 Files selected for processing (11)
src/lib/onboard.tssrc/lib/onboard/command.test.tssrc/lib/onboard/command.tssrc/lib/onboard/experimental/portable-inference-source.test.tssrc/lib/onboard/experimental/portable-inference-source.tssrc/lib/onboard/forward-cleanup.test.tssrc/lib/onboard/forward-cleanup.tssrc/lib/onboard/inference-selection-validation.tssrc/lib/onboard/sandbox-reuse.test.tssrc/lib/onboard/sandbox-reuse.tssrc/lib/security/trusted-private-endpoint.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/lib/onboard.ts
- src/lib/onboard/command.ts
- src/lib/onboard/command.test.ts
| const PORTABLE_INFERENCE_S3_REGION = "us-east-2"; | ||
| const PORTABLE_INFERENCE_S3_BUCKET = "gfn-ld-ai-poc-355178295565-us-east-2-an"; | ||
| const PORTABLE_INFERENCE_S3_KEY = "GFNClawV2/secrets/nvcf-llm.b64"; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Confirm ownership and lifecycle for the fixed object-storage target.
Lines 13-15 hardcode a region, a bucket name that embeds an AWS account ID, and an object key. This pins the product to one externally owned location with no rotation or relocation path. If the bucket is retired, renamed, or its account changes, every portable onboarding run fails with a generic descriptor error.
Record the accepted scope decision, the owning team, the rotation procedure for the object contents, and the retirement plan. Link the tracking issue in GitHub.
As per coding guidelines, "Before implementing or approving a new supported integration, recipe, custom image, third-party stack, or other product surface, confirm an accepted scope decision and defined ownership, lifecycle, compatibility, security, and validation expectations."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/onboard/experimental/portable-inference-source.ts` around lines 13 -
15, Document the accepted scope, owning team, object-content rotation procedure,
retirement/relocation plan, and compatibility, security, and validation
expectations for the fixed target constants PORTABLE_INFERENCE_S3_REGION,
PORTABLE_INFERENCE_S3_BUCKET, and PORTABLE_INFERENCE_S3_KEY; include a link to
the tracking GitHub issue and ensure the lifecycle decision covers bucket or
account changes.
Source: Coding guidelines
| // An explicit loopback host is valid only for flows that can select local inference. | ||
| if (isLoopbackHostname(hostname)) { | ||
| return options.allowExplicitLoopback !== false | ||
| ? { ok: true, addresses: [] } | ||
| : { | ||
| ok: false, | ||
| reason: `endpoint host "${hostname}" is a private/internal address`, | ||
| reasonCode: "rejected", | ||
| }; | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Apply the portable loopback policy to OpenShell-managed aliases.
This branch rejects localhost, 127.0.0.1, and ::1 when allowExplicitLoopback is false. The following isOpenShellManagedHost() branch still accepts inference.local and host.*.internal without checking that option. A portable hosted-only flow can therefore still select a local managed endpoint.
Reject OpenShell-managed aliases when allowExplicitLoopback is false. Add regression coverage for portable rejection of inference.local and host.*.internal, while preserving allowed loopback behavior for non-portable flows.
Proposed fix
- if (isOpenShellManagedHost(hostname)) return { ok: true, addresses: [] };
+ if (isOpenShellManagedHost(hostname)) {
+ return options.allowExplicitLoopback !== false
+ ? { ok: true, addresses: [] }
+ : {
+ ok: false,
+ reason: `endpoint host "${hostname}" is a private/internal address`,
+ reasonCode: "rejected",
+ };
+ }As per path instructions, preserve deny-by-default and fail-closed handling at this security boundary.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // An explicit loopback host is valid only for flows that can select local inference. | |
| if (isLoopbackHostname(hostname)) { | |
| return options.allowExplicitLoopback !== false | |
| ? { ok: true, addresses: [] } | |
| : { | |
| ok: false, | |
| reason: `endpoint host "${hostname}" is a private/internal address`, | |
| reasonCode: "rejected", | |
| }; | |
| } | |
| // An explicit loopback host is valid only for flows that can select local inference. | |
| if (isLoopbackHostname(hostname)) { | |
| return options.allowExplicitLoopback !== false | |
| ? { ok: true, addresses: [] } | |
| : { | |
| ok: false, | |
| reason: `endpoint host "${hostname}" is a private/internal address`, | |
| reasonCode: "rejected", | |
| }; | |
| } | |
| if (isOpenShellManagedHost(hostname)) { | |
| return options.allowExplicitLoopback !== false | |
| ? { ok: true, addresses: [] } | |
| : { | |
| ok: false, | |
| reason: `endpoint host "${hostname}" is a private/internal address`, | |
| reasonCode: "rejected", | |
| }; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/lib/security/trusted-private-endpoint.ts` around lines 275 - 284, Update
the isOpenShellManagedHost() branch to return the same rejection when
options.allowExplicitLoopback is false, covering inference.local and
host.*.internal aliases while preserving existing acceptance for non-portable
flows. Add regression coverage for portable rejection of both alias forms and
allowed behavior when loopback access is enabled, keeping deny-by-default and
fail-closed handling intact.
Source: Path instructions
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
This reverts commit b62b3be.
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Summary
The hidden portable profile now selects the existing OpenAI-compatible hosted inference path when a validated credential descriptor is available. It consumes an activated owner-only descriptor first, falls back to bounded object-storage retrieval, and stops before onboarding when neither hosted source is available.
Changes
Type of Change
Quality Gates
Documentation Writer Review
no-docs-neededDGX Station Hardware Evidence
Verification
Signed-off-by:line and every commit appears asVerifiedin GitHubpre-commit,commit-msg, andpre-pushhooks passed, ornpm run validate:prpassed after refreshingorigin/mainwhen hooks were skipped or unavailablenpm run typecheck:cliand repository checks passed.npm testfor broad runtime/test-harness changes;npm run checkfor repo-wide validation/coverage changes — command/result:npm run docsbuilds without warnings (doc changes only)Signed-off-by: Aaron Erickson aerickson@nvidia.com
Summary by CodeRabbit
New Features
Bug Fixes