Skip to content

feat(cli): add and validate nemoclaw update command#2883

Merged
ericksoa merged 9 commits into
NVIDIA:mainfrom
Iamkewl:feat/prompt-d-integration
May 9, 2026
Merged

feat(cli): add and validate nemoclaw update command#2883
ericksoa merged 9 commits into
NVIDIA:mainfrom
Iamkewl:feat/prompt-d-integration

Conversation

@Iamkewl

@Iamkewl Iamkewl commented May 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the new nemoclaw update feature end-to-end and integrates all work from Prompt A through Prompt F.
This includes core update orchestration, CLI wiring, docs/tests, integration hardening, reviewer/auditor validation.

Changes

  • Implemented core update engine with:
    • npm registry version lookup with retry
    • semantic version normalization/comparison
    • detached self-update execution flow
    • optional upgrade-sandboxes --auto follow-up
    • Docker reachability gate before sandbox sync
    • OpenShell minimum-version warning (non-fatal)
    • credential permission preservation behavior
  • Added CLI integration:
    • registered nemoclaw update under Getting Started
    • exposed --check and --auto flags
    • wired top-level dispatch and usage/error flow
  • Added/updated tests:
    • update helper/engine tests
    • command registry invariants
    • CLI command/help/flag coverage
    • stabilization updates for related flaky paths discovered during integration
  • Updated command docs for nemoclaw update.
  • Added hadolint configuration for stable lint gating.
  • Final compliance pass:
    • DCO sign-off applied
    • new update test migrated from JS to TS per updated CONTRIBUTING guidance

Type of Change

  • Code change with doc updates

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Additional Linux-authoritative validation (fresh clone):

  • Bootstrap:
    • npm install --ignore-scripts
    • cd nemoclaw && npm install --ignore-scripts && npm run build && cd ..
    • hadolint availability ensured
  • Gates:
    • npm run build:cli -> BUILD_EXIT=0
    • npm test -> TEST_EXIT=0
    • make check -> CHECK_EXIT=0
  • Test summary:
    • Test Files 143 passed | 2 skipped (145)
    • Tests 2761 passed | 12 skipped (2773)

AI Disclosure

  • AI-assisted — tool: GitHub Copilot

Signed-off-by: Suryaansh Suryaansh.aa@gmail.com

Summary by CodeRabbit

  • New Features

    • Added a global nemoclaw update command with --check (dry-run) and --auto (non-interactive) to upgrade the CLI and optionally synchronize sandboxes.
  • Improvements

    • Update runs in a detached worker, records update logs, preserves credential file permissions when possible, and emits non-fatal warnings (e.g., Docker unreachable, OpenShell).
  • Documentation

    • CLI docs updated with nemoclaw update usage and flags.
  • Tests

    • New and updated tests covering update behavior, parsing, CLI registration, and runtime scenarios.
  • Dependencies

    • Added semver packages for version handling.
  • Chores

    • Lint config tightened: hadolint now fails on error-level findings.

@copy-pr-bot

copy-pr-bot Bot commented May 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new global nemoclaw update CLI command with full implementation, tests, registry/help/docs entries, and semver dependency; integrates a detached-worker install flow with sandbox sync and credential-permission handling. Also tightens hadolint by setting failure-threshold: error.

Changes

nemoclaw update Implementation & Integration

Layer / File(s) Summary
Data Shape / Types
package.json, src/lib/update.ts
Added dependency semver and dev types; introduced many exported interfaces and types for parsed args, detached payloads, worker results, and DI deps in src/lib/update.ts.
Core Helpers
src/lib/update.ts (lines 155–333)
Implemented semver normalization, parseUpdateArgs, printUpdateUsage, isUpdateAvailable, getLatestNemoClawVersion (with retry), getNpmGlobalPrefix, shouldUseSudoForPrefix, resolveSandboxSyncCommand, and OpenShell min-version helpers.
Credentials & Logging
src/lib/update.ts (lines 412–466, 467–557)
Added credential file snapshotting and safe wrapper; built update log file resolution and timestamped/truncated logging helpers with fallback to ~/.nemoclaw.
Detached Worker & Install Flow
src/lib/update.ts (lines 559–662)
Implemented runDetachedUpdateWorker, sandbox sync invocation, install invocation builder (npm ± sudo -n), warning collection, and credential-permission restore attempts.
Orchestration / CLI Command
src/lib/update.ts (lines 709–875), src/nemoclaw.ts
Implemented runUpdateCommand (help/check/auto/no-update/start paths), host preflight gating (Docker/OpenShell), payload creation, startDetachedUpdateWorker, and integrated error handling; wired update dispatch in CLI entrypoint.
Command Registry & Help / Docs
src/lib/command-registry.ts, docs/reference/commands.md
Registered visible global nemoclaw update in "Getting Started" with flags "[--check] [--auto]"; added docs describing detached install, --check, and --auto behaviors including Docker reachability and sandbox sync notes.
Tests
src/lib/update.test.ts, test/update.test.ts, src/lib/command-registry.test.ts, test/cli.test.ts, src/lib/nim.test.ts
Added comprehensive unit/integration tests for helpers, orchestration, detached worker behavior, permission restoration, sandbox sync handling, CLI parsing, help output, and registry counts; adjusted waitForNimHealth timeout and unknown-option tests.

Linting Config Update (independent)

Layer / File(s) Summary
Lint Configuration
.hadolint.yaml
Added failure-threshold: error, making hadolint error findings fail the lint run.

Sequence Diagram

sequenceDiagram
    autonumber
    actor User
    participant CLI as "nemoclaw (CLI)"
    participant Parser as "parseUpdateArgs"
    participant NPM as "npm registry"
    participant Preflight as "Host Preflight (Docker/OpenShell)"
    participant Worker as "Detached Update Worker"
    participant Install as "npm install -g"
    participant Sandbox as "upgrade-sandboxes --auto"
    participant Log as "Update Log File"

    User->>CLI: nemoclaw update [--check] [--auto]
    CLI->>Parser: parseUpdateArgs
    Parser-->>CLI: { check, auto }

    CLI->>NPM: getLatestNemoClawVersion (with retry)
    NPM-->>CLI: latest version

    CLI->>CLI: isUpdateAvailable / compute sudo & prefix / plan sandbox sync

    alt --check
        CLI-->>User: summary (no install)
    else --auto
        CLI->>Preflight: assessHost (Docker reachable?, OpenShell)
        Preflight-->>CLI: preflight results / warnings

        CLI->>Worker: startDetachedUpdateWorker(payload)
        Worker->>Install: npm install -g nemoclaw@latest (maybe sudo -n)
        Install-->>Worker: stdout/stderr/exit

        opt Docker reachable
            Worker->>Sandbox: upgrade-sandboxes --auto
            Sandbox-->>Worker: sync result
        end

        Worker->>Log: append timestamped output, warnings, status
        Worker-->>CLI: detached PID / status
        CLI-->>User: detached worker started (PID) / plan info
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

"I nibbled at versions, semver and log,
Sudo checked, sandbox paths agog,
Detached hops, credentials kept tight,
Docker peeked and blinked in the night,
NemoClaw updated—hop, hops of delight." 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: adding and validating a new nemoclaw update command feature, which aligns with the comprehensive implementation across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
test/cli.test.ts (1)

193-196: ⚡ Quick win

Keep asserting the parser-specific failure here.

This case now passes on any exit-1 path, so a regression in onboard option routing could slip through unnoticed. Please keep the "Unknown onboard option" assertion here as well, like the neighboring cases do.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cli.test.ts` around lines 193 - 196, The test "unknown onboard option
exits 1" currently only asserts exit code; update it to also assert the
parser-specific error message by checking that the output (from the run(...)
result object returned by run) contains the string "Unknown onboard option" in
addition to expect(r.code).toBe(1); locate the test case in test/cli.test.ts
(the it block with run("onboard --invalid-opt")) and add an assertion similar to
the neighboring cases that inspects r.stdout or r.stderr (whichever run returns
parser messages on) for the exact "Unknown onboard option" text.
test/update.test.ts (1)

20-23: ⚡ Quick win

Tighten help assertions to avoid false positives

These checks are very broad ("update", "--check", "--auto" anywhere in help output). They can pass even if the update command row is malformed. Prefer matching the specific update command/help row with a scoped regex.

Suggested fix
-      expect(output).toContain("update");
+      expect(output).toMatch(/^\s+update\b/m);

-      expect(output).toContain("--check");
+      expect(output).toMatch(/^\s+update\b.*--check/m);

-      expect(output).toContain("--auto");
+      expect(output).toMatch(/^\s+update\b.*--auto/m);

Also applies to: 44-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/update.test.ts` around lines 20 - 23, The test "update command appears
in help output" (and the similar assertion around lines 44-52) uses broad
substring checks; replace the loose expect(output).toContain(...) checks with
assertions that match the specific help row for the update command by using an
anchored, line-scoped regular expression that ensures a single help line begins
with the "update" command and includes the expected flags (e.g., --check and
--auto) and spacing; update the assertions that consume execSync(`node "${CLI}"
help`) to use this scoped regex match so the test fails when the update row is
malformed rather than when the tokens appear anywhere in help output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/update.ts`:
- Around line 572-585: The code currently restores credential permissions to a
hard-coded 0o600; instead use the original mode stored in
payload.credentialsMode so we truly preserve original permissions. In the block
that calls chmodSyncImpl(payload.credentialsFilePath, 0o600) replace the
hard-coded mode with payload.credentialsMode, and update the warning messages
that mention "Restored to 0600" or formatMode(currentMode) to use
formatMode(payload.credentialsMode) so the log reflects the actual restored
mode; keep the existing try/catch and error-string logic unchanged and continue
pushing messages into warnings.
- Around line 506-514: The resolveUpdateLogFilePath function currently returns a
path inside baseDir but doesn't ensure baseDir exists; modify
resolveUpdateLogFilePath to create the directory (e.g., using fs.mkdirSync or
fs.promises.mkdir with { recursive: true }) for baseDir before returning the
joined "update.log" path so subsequent appendWorkerLog() calls won't fail with
ENOENT; perform the mkdir in the same function (referencing credentialFilePath,
baseDir, and resolveHomeDirectory) and surface or log any unexpected errors as
appropriate.

In `@src/nemoclaw.ts`:
- Line 3776: The callback passed to printUpdateUsage currently forwards the
possibly-undefined line directly to console.error, which causes literal
"undefined" to appear; update the callback used where printUpdateUsage((line?:
string) => console.error(line)) is called so it normalizes undefined to an empty
string (or only logs when line is defined) before calling console.error (e.g.,
replace direct forwarding with a wrapper that does console.error(line ?? '') or
conditional logging) targeting the invocation that passes the anonymous callback
to printUpdateUsage.

---

Nitpick comments:
In `@test/cli.test.ts`:
- Around line 193-196: The test "unknown onboard option exits 1" currently only
asserts exit code; update it to also assert the parser-specific error message by
checking that the output (from the run(...) result object returned by run)
contains the string "Unknown onboard option" in addition to
expect(r.code).toBe(1); locate the test case in test/cli.test.ts (the it block
with run("onboard --invalid-opt")) and add an assertion similar to the
neighboring cases that inspects r.stdout or r.stderr (whichever run returns
parser messages on) for the exact "Unknown onboard option" text.

In `@test/update.test.ts`:
- Around line 20-23: The test "update command appears in help output" (and the
similar assertion around lines 44-52) uses broad substring checks; replace the
loose expect(output).toContain(...) checks with assertions that match the
specific help row for the update command by using an anchored, line-scoped
regular expression that ensures a single help line begins with the "update"
command and includes the expected flags (e.g., --check and --auto) and spacing;
update the assertions that consume execSync(`node "${CLI}" help`) to use this
scoped regex match so the test fails when the update row is malformed rather
than when the tokens appear anywhere in help output.
🪄 Autofix (Beta)

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: 5022d4f0-de0e-4969-98fb-01319a1d17ce

📥 Commits

Reviewing files that changed from the base of the PR and between 2653fb4 and 20b695d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .hadolint.yaml
  • docs/reference/commands.md
  • package.json
  • src/lib/command-registry.test.ts
  • src/lib/command-registry.ts
  • src/lib/nim.test.ts
  • src/lib/update.test.ts
  • src/lib/update.ts
  • src/nemoclaw.ts
  • test/cli.test.ts
  • test/onboard.test.ts
  • test/update.test.ts

Comment thread src/lib/update.ts Outdated
Comment thread src/lib/update.ts Outdated
Comment thread src/nemoclaw.ts Outdated
@Iamkewl Iamkewl force-pushed the feat/prompt-d-integration branch from 375d739 to 2acb6b5 Compare May 2, 2026 17:06

@Iamkewl Iamkewl left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased onto upstream/main and resolved conflicts locally; force-pushed branch to latest head 2acb6b5. Running targeted tests now passes for update and command-registry suites.

Suryaansh added 4 commits May 2, 2026 22:37
Signed-off-by: Suryaansh <Suryaansh.aa@gmail.com>
Signed-off-by: Suryaansh <Suryaansh.aa@gmail.com>
Signed-off-by: Suryaansh <Suryaansh.aa@gmail.com>
@Iamkewl Iamkewl force-pushed the feat/prompt-d-integration branch from 2acb6b5 to 3ad1500 Compare May 2, 2026 17:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/update.ts`:
- Around line 506-519: The resolveUpdateLogFilePath function currently calls
resolveHomeDirectory(env) which bypasses the HOME safety checks used by the
credentials code; replace that call with the validated home resolver used by the
credentials module (the same function that enforces HOME safety) so update log
directory creation uses the guarded HOME value, and keep the existing
mkdirSync/error handling around the resulting baseDir; update the import at the
top to bring in the validated resolver and use it instead of
resolveHomeDirectory(env) inside resolveUpdateLogFilePath.
- Around line 479-487: The helper describeWorkerResult currently hides spawn
failures because it only checks status and signal; update it to surface the
SpawnSyncReturns.error when present (check the result.error property on the
SpawnSyncReturns<string>), and return a descriptive string incorporating
error.name, error.code and/or error.message (and optionally error.stack) so
detached-update logs show why npm/sudo failed to start; keep the existing
branches (status -> "exit X", signal -> "signal Y") and only fall back to the
error-derived message before returning "unknown exit".
🪄 Autofix (Beta)

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: 0d65e3ab-59e0-427c-b967-e8144adf894b

📥 Commits

Reviewing files that changed from the base of the PR and between 2acb6b5 and 3ad1500.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .hadolint.yaml
  • docs/reference/commands.md
  • package.json
  • src/lib/command-registry.test.ts
  • src/lib/command-registry.ts
  • src/lib/nim.test.ts
  • src/lib/update.test.ts
  • src/lib/update.ts
  • src/nemoclaw.ts
  • test/cli.test.ts
  • test/update.test.ts
✅ Files skipped from review due to trivial changes (9)
  • .hadolint.yaml
  • package.json
  • docs/reference/commands.md
  • test/update.test.ts
  • src/lib/command-registry.test.ts
  • src/nemoclaw.ts
  • src/lib/nim.test.ts
  • src/lib/update.test.ts
  • test/cli.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/command-registry.ts

Comment thread src/lib/update.ts Outdated
Comment thread src/lib/update.ts Outdated
@wscurran

wscurran commented May 4, 2026

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR that proposes a new nemoclaw update feature, which includes core update orchestration, CLI wiring, and integration hardening. This change improves the CLI's maintainability by adding a new command for updating NemoClaw.

@latenighthackathon

latenighthackathon commented May 4, 2026

Copy link
Copy Markdown
Contributor

Related PRs: #641, #644, #788

@ericksoa ericksoa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after adversarial review and follow-up hardening. The updated command preserves the maintained installer path, handles nemohermes branding/agent selection, avoids npm as release truth, uses pipefail, and keeps the sandbox upgrade boundary explicit.

@ericksoa ericksoa enabled auto-merge (squash) May 9, 2026 04:42
@ericksoa ericksoa disabled auto-merge May 9, 2026 04:50
@ericksoa ericksoa merged commit 7974b04 into NVIDIA:main May 9, 2026
14 checks passed
@miyoungc miyoungc mentioned this pull request May 12, 2026
12 tasks
miyoungc added a commit that referenced this pull request May 12, 2026
## Summary
Refreshes the release-prep docs for v0.0.39 based on changes merged
since the Friday 4pm doc refresh. Updates the source docs, bumps the
docs version metadata, and regenerates the NemoClaw user skills from the
refreshed docs.

## Changes
- #3314 -> `docs/get-started/prerequisites.md`,
`docs/get-started/quickstart.md`, `docs/reference/troubleshooting.md`:
Documents installer Docker setup, Docker group activation, and retry
guidance.
- #3317 -> `docs/get-started/quickstart.md`,
`docs/reference/commands.md`: Documents the DGX Spark and DGX Station
express install prompt and `NEMOCLAW_NO_EXPRESS`.
- #3328 and #3329 -> `docs/security/best-practices.md`,
`docs/deployment/sandbox-hardening.md`: Updates sandbox capability
hardening docs for the stricter bounding-set and `setpriv` step-down
behavior.
- #3330, #3335, and #3346 -> `docs/inference/use-local-inference.md`:
Documents Windows-host Ollama relaunch behavior, NIM key passthrough,
early health-fail diagnostics, and mixed-GPU preflight detail.
- #2406, #2883, #3001, #3244, #3267, #3318, #3320, and #3354 ->
`docs/about/release-notes.md`: Adds the v0.0.39 release-prep section
while keeping the v0.0.38 release notes intact.
- Advances the release-prep docs metadata from v0.0.38 to v0.0.39.
- Regenerates `.agents/skills/nemoclaw-user-*` from the updated source
docs.

## Type of Change
- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [x] Doc only (includes code sample changes)

## Verification
- [x] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [x] Docs updated for user-facing behavior changes
- [x] `make docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes v0.0.39

* **New Features**
  * Host alias management commands for easier configuration
  * Sandbox GPU control options during onboarding
  * Update command with check and confirmation modes

* **Documentation**
* Enhanced Linux installer guidance with Docker and group membership
handling
  * Expanded troubleshooting for permission and connectivity issues
  * Improved capability-dropping security documentation
  * Updated inference model switching commands
  * Brev environment-specific troubleshooting

* **Improvements**
  * DGX Spark/Station express install flow
  * Windows Ollama relay and health-check enhancements
  * NVIDIA NIM preflight GPU reporting

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/NVIDIA/NemoClaw/pull/3375)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output feature PR adds or expands user-visible functionality and removed NemoClaw CLI labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output feature PR adds or expands user-visible functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants