Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/reference/troubleshoot-mcp-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,15 @@ The redacted `error_body` value contains at most 2,048 UTF-8 bytes before JSON e
It redacts session identifiers, bearer tokens, structured credentials such as `access_token`, `refresh_token`, and `client_secret`, and known token prefixes.
The diagnostic does not include a JSON-RPC operation because this boundary sees only the endpoint.

To find the matching sandbox audit record, read the audit log for the same endpoint around the failure time:
To find the matching sandbox audit record, read the sandbox log for the same endpoint around the failure time.
The `logs` command reads both OpenClaw gateway output and OpenShell audit events, so it needs no separate audit flag.

```bash
$$nemoclaw <sandbox> logs --audit --tail 200
$$nemoclaw <sandbox> logs --tail 200
```

Match on `target` and the timestamp.
If NemoClaw cannot enable OpenShell audit logs, it prints a warning and the policy denial events can be missing from the output.
`diagnostic_id` is a local identifier created for one line group.
It is not a distributed trace identifier and does not appear in OpenShell audit events.
Correlate with OpenShell by endpoint and time until OpenShell records a shared identifier.
Expand Down
80 changes: 80 additions & 0 deletions test/cli/logs-documented-invocations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import fs from "node:fs";
import path from "node:path";

import { describe, expect, test as it } from "../helpers/owned-test-resources";

import { createLogsTestSetup } from "./helpers";

const REPO_ROOT = path.join(import.meta.dirname, "..", "..");
const DOCS_ROOT = path.join(REPO_ROOT, "docs");
const SANDBOX_NAME = "alpha";
const LOGS_INVOCATION = /^\$\$nemoclaw\s+\S+\s+logs\b(?<rest>.*)$/;
const PLACEHOLDER_OR_SHELL_SYNTAX = /[[\]|><]/;

type DocumentedInvocation = {
args: string;
reference: string;
};

function walkMdxFiles(dir: string): string[] {
return fs
.readdirSync(dir, { withFileTypes: true })
.sort((left, right) => left.name.localeCompare(right.name))
.flatMap((entry) => {
const absolute = path.join(dir, entry.name);
return entry.name === "_build"
? []
: entry.isDirectory()
? walkMdxFiles(absolute)
: entry.isFile() && entry.name.endsWith(".mdx")
? [absolute]
: [];
});
}

function extractInvocation(line: string, index: number, file: string): DocumentedInvocation | null {
const rest = LOGS_INVOCATION.exec(line.trim())?.groups?.rest.trim();
return rest !== undefined && !PLACEHOLDER_OR_SHELL_SYNTAX.test(rest)
? {
args: [SANDBOX_NAME, "logs", rest].filter(Boolean).join(" "),
reference: `${path.relative(REPO_ROOT, file)}:${index + 1}`,
}
: null;
}

function isDocumentedInvocation(
invocation: DocumentedInvocation | null,
): invocation is DocumentedInvocation {
return invocation !== null;
}

function documentedLogsInvocations(): DocumentedInvocation[] {
return walkMdxFiles(DOCS_ROOT).flatMap((file) =>
fs
.readFileSync(file, "utf8")
.split(/\r?\n/)
.map((line, index) => extractInvocation(line, index, file))
.filter(isDocumentedInvocation),
);
}

describe("documented sandbox logs invocations", () => {
const invocations = documentedLogsInvocations();

it("collects runnable logs invocations from the published pages", () => {
expect(invocations.length).toBeGreaterThanOrEqual(5);
});

for (const { args, reference } of invocations) {
it(`runs the invocation documented at ${reference}`, ({ resources }) => {
const setup = createLogsTestSetup(resources, "nemoclaw-cli-logs-documented-");
const result = setup.runLogs(`${args} 2>&1`);

expect(result.out).not.toContain("Nonexistent flag");
expect(result.code).toBe(0);
});
}
});
Loading