Skip to content

feat(cli): add crash reporting for automatic error capture#674

Merged
toiroakr merged 42 commits intomainfrom
feat/cli-crash-reporting
Mar 18, 2026
Merged

feat(cli): add crash reporting for automatic error capture#674
toiroakr merged 42 commits intomainfrom
feat/cli-crash-reporting

Conversation

@dqn
Copy link
Contributor

@dqn dqn commented Mar 7, 2026

Add crash reporting system that captures unexpected CLI errors and allows users to send reports to aid debugging.

Usage

CLI output on unexpected crash:

An unexpected error occurred. A crash report has been saved to:
  ~/.config/tailor-platform/crash-reports/2026-03-07T12-34-56-789Z-a1b2c3d4.crash.log

To submit this report:
  tailor-sdk crash-report send --file ~/.config/tailor-platform/crash-reports/2026-03-07T12-34-56-789Z-a1b2c3d4.crash.log

New commands:

tailor-sdk crash-report list          # List saved crash reports
tailor-sdk crash-report send --file   # Submit a crash log file via GraphQL mutation

Main Changes

  • Add crash report writer that saves sanitized error details, CLI arguments, and environment info to local files with JSON footer for reliable re-parsing
  • Add PII sanitization layer (paths, UUIDs, hex tokens, emails, argv flag values, request/response bodies)
  • Add crash-report list and crash-report send CLI commands
  • Add allowlist-based RemoteCrashReport for auto-send path with configurable endpoint
  • Restrict crash reports to unexpected errors only (not user errors or known failures)
  • Submit reports via GraphQL mutation (submitCrashReport) with 5s timeout and best-effort semantics
  • Local file rotation keeps max 10 crash files; both local and remote auto-disabled in CI

Privacy / Sanitization

Data type Treatment
Absolute paths (Unix/Windows) SDK-relative preserved; others -> <path>/filename or ~/<redacted>/filename
UUIDs -> <uuid>
Long hex tokens (32+ chars) -> <redacted>
Email addresses -> <email>
URL query strings -> ?<redacted>
CLI flag values All --flag value and --flag=value -> redacted
Positional args Paths/emails redacted; first 3 tokens kept as command name

Configuration

Env var Default Description
TAILOR_CRASH_REPORTS_LOCAL on Write crash logs locally (off to disable)
TAILOR_CRASH_REPORTS_REMOTE off Auto-send to remote (on to enable)
TAILOR_CRASH_REPORT_ENDPOINT production URL Override remote endpoint for testing

Notes

  • Remote endpoint is a placeholder until the server is deployed; override with TAILOR_CRASH_REPORT_ENDPOINT for testing
  • Server side built in a separate repository

Open with Devin

@changeset-bot
Copy link

changeset-bot bot commented Mar 7, 2026

🦋 Changeset detected

Latest commit: 2d685f8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tailor-platform/sdk Minor
@tailor-platform/create-sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 7, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@tailor-platform/create-sdk@674

commit: 2d685f8

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

dqn added 22 commits March 11, 2026 16:59
Add crash report infrastructure that writes local crash logs and
optionally sends reports to a remote endpoint. Includes privacy-
preserving sanitization of sensitive data (paths, UUIDs, emails,
tokens) and a `crash-report` subcommand for listing and submitting
reports.
- Remove config memoization so env-file settings are respected
- Add Windows path sanitization for stack traces, messages, and argv
- Fix multiline error message parsing in crash-report send command
- Document the crash report webhook endpoint domain
- Sanitize command field via sanitizeMessage to redact emails/UUIDs
- Serialize argv as JSON array to preserve argument boundaries
- Validate required fields (id, timestamp, crashType) before sending
- Support parsing both JSON and legacy space-separated argv formats
- Expand SENSITIVE_FLAGS to cover --value, -b, -H, --cookie, etc.
- Strip serialized request bodies from error messages before logging
- Suppress crash report banner for handled errors (log only)
- Print original error before crash reporting in global handlers
- Limit parseCommand to 2 tokens to avoid leaking positional user input
- Allow spaces in Unix path regex to match paths like '/My App/...'
- Decouple remoteEnabled from localEnabled so remote-only mode works
- Update reportCrash/initCrashReporting to support remote-only config
…ction

- Only report crashes for Error subclasses (TypeError, etc.), not plain Error
- Add --data/-d, --arg/-a, --email, --user/-u to SENSITIVE_FLAGS
- Redact email addresses in argv values
- Extract lastSegment helper to deduplicate path basename extraction
- Use AbortSignal.timeout() instead of manual AbortController
- Extract handleFatal to deduplicate global error handlers
- Avoid duplicate OS string split in crash log parser
Align the directory name with the actual command name "crash-report".
…crash reports

Replace hardcoded api.tailor.wiki endpoint with example.com placeholder.
Add TAILOR_CRASH_REPORT_ENDPOINT env var to override the endpoint for
testing and development.
Add RemoteCrashReport type with only provably PII-free fields and
toRemoteReport() converter. Auto-send to remote now uses the strict
allowlist (excludes argv, errorMessage, full stackTrace), while local
files and manual send retain full detail via existing sanitize.ts.
Replace the SENSITIVE_FLAGS denylist (25 entries) with a blanket rule
that redacts the value of every flag. This eliminates maintenance burden
when new flags are added and provides stronger defense-in-depth alongside
the allowlist-based RemoteCrashReport.
…chema

Rename crashType to errorType and remove RemoteCrashReport in favor of
sending the full sanitized CrashReport directly. This aligns the client
payload with the server's ErrorEventPayload interface, eliminating the
need for field mapping and the sdkStackTrace indirection.

- Rename CrashType to ErrorType, crashType field to errorType
- Remove RemoteCrashReport, toRemoteReport, extractSdkStackFrames
- Simplify sender to accept CrashReport only (no union type)
- Update writer format label from "Crash Type" to "Error Type"
- Update send command parser accordingly
Replace the `constructor !== Error` heuristic with explicit native error
type checks (TypeError, RangeError, SyntaxError, ReferenceError). This
prevents expected domain errors like ConnectError and CIPromptError from
being misclassified as crashes, while still capturing genuine programming
bugs.

Also show the crash-report banner for all saved reports, including those
caught in command handlers, so users know they can submit the report.
- Apply sanitizeMessage to the error message line of stack traces so
  secrets (tokens, emails, UUIDs) embedded in the message are redacted
  consistently with the errorMessage field.
- Fix sanitizeArgv to recognize consecutive flags: when a boolean flag
  like --verbose is followed by another flag, don't consume the second
  flag as a value. Previously `--verbose --workspace-id secret` would
  leak `secret`.
- Narrow crash reporting to TypeError and RangeError only. SyntaxError
  and ReferenceError at runtime typically originate from dynamically
  imported user config files, not SDK code defects.
Add tests for the crash-report list command covering sorted output,
empty directory, and non-existent directory cases.

Document two accepted design trade-offs as code comments:
- Error classification misses exhaustiveness checks thrown as plain Error
- initCrashReporting runs before env file loading by design
…sage

- Deduplicate crash reporting logic in withCommonArgs: consolidate two
  separate reportCrash call sites into a single shouldReport check
  after the logging branches.
- Replace global EMAIL_PATTERN regex with inline non-global regex in
  sanitizeArgv to avoid the .test() + global lastIndex state footgun.
- Extract EMAIL_TEST_PATTERN constant for non-global .test() usage
- Simplify stack trace first-line sanitization with regex replace
- Consolidate flag detection in sanitizeArgv under single startsWith check
Replace the complex text-based parseCrashLogFile (~45 lines of regex
parsing) with a simple JSON footer approach. formatCrashReport now
appends a `--- JSON ---` section with the full report as JSON, and
parseCrashLogFile reads it in 3 lines.

Also simplify URL_QUERY_PATTERN to use character class instead of
capture group, and remove redundant callback wrapper in stack trace
sanitization.
… footer

Read TAILOR_CRASH_REPORT_ENDPOINT inside sendCrashReport() instead of at
module scope so that env files loaded by withCommonArgs() are respected.

Use lastIndexOf to find the final --- JSON --- marker in crash log files,
preventing misparse when the marker string appears in error messages.
The send command requires --file but the post-crash hint printed
the path as a positional argument, making the suggested command fail.
Wrap the crash log path in double quotes so the suggested command
works correctly when the path contains spaces.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@dqn
Copy link
Contributor Author

dqn commented Mar 17, 2026

I verified crash report submission locally -- the report is generated correctly and the mutation returns success: true. However, I don't have read permission on the errorEvents query, so I could not confirm that the record was actually persisted on the server. Could someone with access verify that the records are being stored?

@github-actions

This comment has been minimized.

@dqn dqn marked this pull request as ready for review March 17, 2026 08:11
@dqn dqn requested review from remiposo and toiroakr as code owners March 17, 2026 08:11
@claude
Copy link

claude bot commented Mar 17, 2026

📖 Docs Consistency Check

⚠️ Inconsistencies Found

File Issue Suggested Fix
packages/sdk/docs/cli-reference.md New crash-report commands not documented in Commands section Add a new section under Commands listing crash-report list and crash-report send
packages/sdk/docs/cli/crash-report.md File missing entirely Create new file following the pattern of other CLI command docs (e.g., secret.md, workflow.md)
packages/sdk/docs/cli-reference.md TAILOR_CRASH_REPORT_ENDPOINT environment variable not documented Add row to Environment Variables table documenting this override capability

Details

1. Missing crash-report commands in cli-reference.md

The PR adds two new CLI commands (crash-report list and crash-report send) which are:

  • ✅ Implemented in packages/sdk/src/cli/commands/crash-report/list.ts and send.ts
  • ✅ Registered in packages/sdk/src/cli/index.ts:54 as "crash-report": crashReportCommand
  • ❌ NOT listed in the Commands section of packages/sdk/docs/cli-reference.md

All other command groups (Application, TailorDB, User & Auth, Workspace, Auth Resource, Workflow, Function, Executor, Secret, Static Website, Completion) have their own section in cli-reference.md with links to detailed docs. The crash-report commands should follow this pattern.

Expected addition to cli-reference.md around line 207:

### [Crash Report Commands](./cli/crash-report.md)

Commands for managing crash reports.

| Command                                                   | Description                     |
| --------------------------------------------------------- | ------------------------------- |
| [crash-report list](./cli/crash-report.md#crash-report-list) | List local crash report files   |
| [crash-report send](./cli/crash-report.md#crash-report-send) | Submit a crash report to remote |

2. Missing packages/sdk/docs/cli/crash-report.md

Following the established pattern (see secret.md, workflow.md, etc.), each command group should have its own detailed documentation file under packages/sdk/docs/cli/. This file should include:

  • Command overview with description
  • Subcommand table of contents
  • Detailed documentation for each subcommand with usage, options, and examples
  • Auto-generated sections using <!-- politty:command:crash-report:*:start/end --> markers

Implementation details show:

  • list.ts:10 - Description: "List local crash report files."
  • send.ts:13 - Description: "Submit a crash report to help improve the SDK."
  • send.ts:16-20 - Has --file option (required): "Path to the crash report file"

3. Missing TAILOR_CRASH_REPORT_ENDPOINT environment variable

The implementation in packages/sdk/src/cli/crash-report/sender.ts:54 shows:

const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;

This environment variable is used to override the remote crash report endpoint (mentioned in the PR description for testing purposes), but it's not documented in the Environment Variables table in cli-reference.md.

Currently documented (lines 64-65):

  • TAILOR_CRASH_REPORTS_LOCAL
  • TAILOR_CRASH_REPORTS_REMOTE
  • TAILOR_CRASH_REPORT_ENDPOINT

Expected addition to cli-reference.md around line 66:

| `TAILOR_CRASH_REPORT_ENDPOINT`    | Override remote crash report endpoint (for testing)                         |

Recommended Actions

  1. Add crash-report commands section to packages/sdk/docs/cli-reference.md (between Static Website Commands and Completion sections)
  2. Create packages/sdk/docs/cli/crash-report.md with full command documentation following the existing pattern
  3. Add TAILOR_CRASH_REPORT_ENDPOINT to the Environment Variables table in cli-reference.md
  4. Run pnpm docs:update to auto-generate politty markers if needed

@claude
Copy link

claude bot commented Mar 17, 2026

📖 Docs Consistency Check

⚠️ Inconsistencies Found

File Issue Suggested Fix
packages/sdk/docs/cli-reference.md Missing Crash Report Commands section Add new section listing crash-report commands
packages/sdk/docs/cli-reference.md Missing TAILOR_CRASH_REPORT_ENDPOINT env var Add row in Environment Variables table
packages/sdk/docs/cli/crash-report.md File does not exist Create following pattern of other CLI docs
packages/sdk/src/cli/docs.test.ts Missing crash-report in docs test Add entry to files object

Details

1. Missing Commands Section in CLI Reference

Location: packages/sdk/docs/cli-reference.md lines 84-215

The main CLI reference lists all command categories but the new crash-report command group is missing.

Implementation:

  • Commands registered in packages/sdk/src/cli/index.ts line 54
  • Command definition: packages/sdk/src/cli/commands/crash-report/index.ts
  • Subcommands: list and send

Expected: Add section for Crash Report Commands linking to cli/crash-report.md

2. Missing Environment Variable

Location: packages/sdk/docs/cli-reference.md lines 52-65

The environment variables table documents TAILOR_CRASH_REPORTS_LOCAL and TAILOR_CRASH_REPORTS_REMOTE but misses TAILOR_CRASH_REPORT_ENDPOINT.

Implementation: packages/sdk/src/cli/crash-report/sender.ts line 54 uses this variable to override the production endpoint.

Expected: Add TAILOR_CRASH_REPORT_ENDPOINT to the environment variables table.

Note: Per the PR description this is mentioned for testing purposes.

3. Missing Detailed Command Documentation

Location: packages/sdk/docs/cli/crash-report.md (file does not exist)

Following the established pattern each command group has a dedicated documentation file in packages/sdk/docs/cli/.

Pattern reference: See packages/sdk/docs/cli/secret.md and workflow.md

4. Missing Docs Test Configuration

Location: packages/sdk/src/cli/docs.test.ts lines 35-80

The files object lists all command groups for documentation generation and validation. The crash-report commands are not included which means:

  • Running pnpm docs:check will not validate crash-report command docs
  • Running pnpm docs:update will not generate or update crash-report docs

Recommended Actions

  1. Update packages/sdk/src/cli/docs.test.ts - Add crash-report to the files configuration
  2. Run pnpm docs:update in packages/sdk/ to auto-generate the new docs file and update cli-reference.md
  3. Manually add TAILOR_CRASH_REPORT_ENDPOINT to the environment variables table in cli-reference.md
  4. Run pnpm docs:check to verify all documentation is consistent

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@dqn
Copy link
Contributor Author

dqn commented Mar 17, 2026

⚠️ Inconsistencies Found
| packages/sdk/docs/cli-reference.md | Missing Crash Report Commands section |
| packages/sdk/docs/cli-reference.md | Missing TAILOR_CRASH_REPORT_ENDPOINT env var |
| packages/sdk/docs/cli/crash-report.md | File does not exist |
| packages/sdk/src/cli/docs.test.ts | Missing crash-report in docs test |

Addressed in f0eeacbf:

  • Added docs/cli/crash-report.md with auto-generated command docs
  • Added Crash Report Commands section to cli-reference.md
  • Added crash-report entry to docs.test.ts

TAILOR_CRASH_REPORT_ENDPOINT is intentionally omitted as it's not user-facing.

@github-actions

This comment has been minimized.

Comment on lines +87 to +88
userId: currentUser,
userEmail: currentUser,
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, we only needed one of them...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently only email is stored in the platform config, but the /auth/platform/userinfo endpoint does return a sub field (UUID). If email immutability is guaranteed, a separate userId may not be needed. Otherwise, we could persist sub in the config at login time and use it here as a stable identifier.

Copy link
Contributor

Choose a reason for hiding this comment

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

we could persist sub in the config at login time and use it here as a stable identifier.

Since the sub field is guaranteed to be immutable, it would be better to use sub as the ID in the config in the first place, assuming that email addresses can change.

Copy link
Contributor

Choose a reason for hiding this comment

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

However, since the login-related parts are currently being refactored separately (#702, #746), it might be better to leave it as is for now and address it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave this as-is for now and revisit after the login refactoring lands.

const originalEnv = process.env;

beforeEach(() => {
process.env = { ...originalEnv };
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we use vi.stubEnv instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. Replaced with vi.stubEnv / vi.unstubAllEnvs.

@github-actions

This comment has been minimized.

Copy link
Contributor

@toiroakr toiroakr left a comment

Choose a reason for hiding this comment

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

Let's go with this for now.

Include both crash-report and setup command sections added independently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@github-actions
Copy link

Code Metrics Report (packages/sdk)

main (684d2aa) #674 (25f6630) +/-
Coverage 55.1% 55.5% +0.3%
Code to Test Ratio 1:0.3 1:0.4 +0.0
Details
  |                    | main (684d2aa) | #674 (25f6630) |  +/-  |
  |--------------------|----------------|----------------|-------|
+ | Coverage           |          55.1% |          55.5% | +0.3% |
  |   Files            |            312 |            321 |    +9 |
  |   Lines            |          10112 |          10310 |  +198 |
+ |   Covered          |           5576 |           5725 |  +149 |
+ | Code to Test Ratio |          1:0.3 |          1:0.4 |  +0.0 |
  |   Code             |          58877 |          60195 | +1318 |
+ |   Test             |          23393 |          24228 |  +835 |

Code coverage of files in pull request scope (29.4% → 71.6%)

Files Coverage +/- Status
packages/sdk/src/cli/commands/crash-report/index.ts 50.0% +50.0% added
packages/sdk/src/cli/commands/crash-report/list.ts 5.8% +5.8% added
packages/sdk/src/cli/commands/crash-report/send.ts 36.0% +36.0% added
packages/sdk/src/cli/crash-report/config.ts 100.0% +100.0% added
packages/sdk/src/cli/crash-report/index.ts 69.5% +69.5% added
packages/sdk/src/cli/crash-report/report.ts 76.1% +76.1% added
packages/sdk/src/cli/crash-report/sanitize.ts 100.0% +100.0% added
packages/sdk/src/cli/crash-report/sender.ts 100.0% +100.0% added
packages/sdk/src/cli/crash-report/writer.ts 100.0% +100.0% added
packages/sdk/src/cli/index.ts 27.2% -2.2% modified

SDK Configure Bundle Size

main (684d2aa) #674 (25f6630) +/-
configure-index-size 10.74KB 10.74KB 0KB
dependency-chunks-size 33.76KB 33.76KB 0KB
total-bundle-size 44.49KB 44.49KB 0KB

Runtime Performance

main (684d2aa) #674 (25f6630) +/-
Generate Median 2,590ms 2,551ms -39ms
Generate Max 2,606ms 2,585ms -21ms
Apply Build Median 2,629ms 2,596ms -33ms
Apply Build Max 2,654ms 2,623ms -31ms

Type Performance (instantiations)

main (684d2aa) #674 (25f6630) +/-
tailordb-basic 43,021 43,021 0
tailordb-optional 3,927 3,927 0
tailordb-relation 4,071 4,071 0
tailordb-validate 2,925 2,925 0
tailordb-hooks 5,790 5,790 0
tailordb-object 11,571 11,571 0
tailordb-enum 2,793 2,793 0
resolver-basic 9,236 9,236 0
resolver-nested 25,623 25,623 0
resolver-array 17,859 17,859 0
executor-schedule 4,244 4,244 0
executor-webhook 883 883 0
executor-record 4,847 4,847 0
executor-resolver 4,270 4,270 0
executor-operation-function 877 877 0
executor-operation-gql 879 879 0
executor-operation-webhook 898 898 0
executor-operation-workflow 2,290 2,290 0

Reported by octocov

@toiroakr toiroakr merged commit eb7cd8a into main Mar 18, 2026
35 checks passed
@toiroakr toiroakr deleted the feat/cli-crash-reporting branch March 18, 2026 09:20
@tailor-pr-trigger tailor-pr-trigger bot mentioned this pull request Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants