Files
rs-sdk/sdk/bug-report.ts
max bittkerandClaude Opus 5 11424fa84d feat(sdk): add a bug-report CLI and a write-only endpoint to collect them
Agents hit SDK papercuts constantly - a method returns success:true and
nothing happened, a dialog option isn't where the docs say it is - and
there was nowhere to put that. Filing one is now a single command:

  bun sdk/bug-report.ts "walkTo returns success at closed gates"

The SDK's git commit is stamped on every report, so a report points at the
code that produced it.

Server side, POST /bug-report appends JSONL to the mounted volume
(BUG_REPORTS_FILE in fly.toml, so reports survive deploys). The endpoint is
deliberately write-only: nothing is served back over HTTP, and reports are
read off the machine with

  fly ssh console -C "cat /opt/server/data/bug-reports.jsonl"

Reports always go to rs-sdk-demo.fly.dev, never to whatever SERVER a bot is
playing on - a bug found against a local server is still a bug in the SDK.
Telemetry is on by default; TELEMETRY=false in bot.env or the environment
turns it off.

Also renames claude.md -> CLAUDE.md and agents.md -> AGENTS.md. Those names
only resolved on macOS: the filesystem is case-insensitive here, so git had
recorded lowercase names that no tool would find on a case-sensitive
checkout. Verified on a case-sensitive volume - before the rename, lookups
for CLAUDE.md and AGENTS.md both missed; after, all three resolve to the one
real file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:43:49 -04:00

99 lines
3.1 KiB
TypeScript

#!/usr/bin/env bun
// Bug reports - file SDK papercuts to the drop box on the server.
//
// One text field, no auth, no setup. The git commit is stamped automatically so
// a report can be traced back to the SDK that produced it.
//
// bun sdk/bug-report.ts "walkTo lies about success at closed gates"
// bun sdk/bug-report.ts --version
//
// Submission is write-only: filed reports are read off the server directly
// (fly ssh console -C 'cat /opt/server/data/bug-reports.jsonl'), not over HTTP.
//
// Opt out with TELEMETRY=false (in bot.env or the environment).
import { execFileSync } from 'node:child_process';
/**
* Where reports go. Always the shared server, never whatever SERVER a bot happens
* to be pointed at - a bug found while testing against localhost is still a bug in
* the SDK, and a report that lands on a throwaway local server helps nobody.
*/
const ENDPOINT = 'https://rs-sdk-demo.fly.dev/bug-report';
/** The git commit this SDK is running from, or 'unknown' outside a git checkout. */
function sdkCommit(): string {
try {
return execFileSync('git', ['-C', import.meta.dir, 'rev-parse', '--short', 'HEAD'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore']
}).trim();
} catch {
return 'unknown';
}
}
/**
* Telemetry (i.e. bug reports leaving this machine) is on by default.
* Set TELEMETRY to false/0/no/off to disable it.
*/
function telemetryEnabled(): boolean {
const value = process.env.TELEMETRY?.trim().toLowerCase();
if (!value) return true;
return !['false', '0', 'no', 'off'].includes(value);
}
async function main() {
const argv = process.argv.slice(2);
if (!argv.length || argv[0] === '--help') {
console.log(`Usage:
bun sdk/bug-report.ts "<what went wrong>"
bun sdk/bug-report.ts --version
Reports always go to ${ENDPOINT} (no auth, write-only),
regardless of which server the bot plays on.
Telemetry is ${telemetryEnabled() ? 'on' : 'off'}; set TELEMETRY=false to disable it.`);
return;
}
if (argv[0] === '--version') {
console.log(sdkCommit());
return;
}
const text = argv.join(' ').trim();
if (!text) {
console.error('Nothing to report: describe what went wrong');
process.exit(1);
}
if (!telemetryEnabled()) {
console.log('Telemetry is off (TELEMETRY=false), report not sent');
return;
}
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, sdkVersion: sdkCommit() }),
signal: AbortSignal.timeout(15000)
});
const body = await response.text();
let parsed: any = null;
try { parsed = JSON.parse(body); } catch { /* non-JSON error page */ }
if (!response.ok || !parsed?.ok) {
console.error(`Bug report failed: ${parsed?.error ?? `${response.status} ${response.statusText}: ${body.slice(0, 200)}`}`);
process.exit(1);
}
console.log(`Filed ${parsed.id} (sdk ${sdkCommit()})`);
}
main().catch(err => {
console.error(`Bug report failed: ${err.message}`);
process.exit(1);
});