mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 08:03:12 +00:00
The user report F0BDXH978GJ surfaced 'Error invoking remote method fs:readDir' — Electron's opaque message when the main-process handler throws. On Windows this is typically a WSL \wsl$ / \wsl.localhostUNC path or network drive failing realpath/readdir after the distro or share goes away, or a dropped SSH provider. Nothing recorded which throw site fired or what kind of path was involved, so the crash report carried no actionable cause. Wrap the fs:readDir handler to record an 'fs_readdir_error' breadcrumb on throw, tagging the throw site (ssh-provider | authorize | readdir), the error name/code, and a REDACTED path shape (isUNC, isWsl, driveLetter, hasConnectionId) — never the raw path. The error is re-thrown unchanged, so renderer behavior is identical; only crash reports gain structured context. Pure classification lives in readdir-error-diagnostics.ts (8 unit tests covering WSL UNC, \wsl$, network share, drive letter, SSH, and no-path-leak); 4 handler tests assert the breadcrumb fires per throw site and not on success. Co-authored-by: Neil <neil@stably.ai>
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { buildReadDirErrorBreadcrumb, describeReadDirPathShape } from './readdir-error-diagnostics'
|
|
|
|
describe('describeReadDirPathShape', () => {
|
|
it('classifies a WSL UNC path without leaking it', () => {
|
|
const shape = describeReadDirPathShape('\\\\wsl.localhost\\Ubuntu\\home\\u\\repo', undefined)
|
|
expect(shape).toEqual({ hasConnectionId: false, isUNC: true, isWsl: true })
|
|
})
|
|
|
|
it('classifies the legacy \\\\wsl$ root as WSL', () => {
|
|
expect(describeReadDirPathShape('\\\\wsl$\\Ubuntu\\home', undefined).isWsl).toBe(true)
|
|
})
|
|
|
|
it('classifies a plain network UNC share as UNC but not WSL', () => {
|
|
const shape = describeReadDirPathShape('\\\\fileserver\\share\\dir', undefined)
|
|
expect(shape).toMatchObject({ isUNC: true, isWsl: false })
|
|
expect(shape.driveLetter).toBeUndefined()
|
|
})
|
|
|
|
it('extracts an uppercased drive letter for mapped drives', () => {
|
|
expect(describeReadDirPathShape('z:\\projects\\repo', undefined)).toEqual({
|
|
hasConnectionId: false,
|
|
isUNC: false,
|
|
isWsl: false,
|
|
driveLetter: 'Z'
|
|
})
|
|
})
|
|
|
|
it('flags the SSH connection without recording it', () => {
|
|
const shape = describeReadDirPathShape('/remote/repo', 'ssh-1')
|
|
expect(shape).toEqual({ hasConnectionId: true, isUNC: false, isWsl: false })
|
|
})
|
|
|
|
it('never includes the raw path in the shape', () => {
|
|
const shape = describeReadDirPathShape('\\\\wsl.localhost\\Ubuntu\\secret\\path', 'ssh-9')
|
|
expect(JSON.stringify(shape)).not.toContain('secret')
|
|
})
|
|
})
|
|
|
|
describe('buildReadDirErrorBreadcrumb', () => {
|
|
it('captures throw site, error code/name, and path shape', () => {
|
|
const breadcrumb = buildReadDirErrorBreadcrumb({
|
|
dirPath: '\\\\wsl.localhost\\Ubuntu\\home\\u\\repo',
|
|
connectionId: undefined,
|
|
throwSite: 'readdir',
|
|
error: Object.assign(new Error('EIO: i/o error'), { code: 'EIO' })
|
|
})
|
|
expect(breadcrumb).toEqual({
|
|
throwSite: 'readdir',
|
|
errorName: 'Error',
|
|
errorCode: 'EIO',
|
|
hasConnectionId: false,
|
|
isUNC: true,
|
|
isWsl: true
|
|
})
|
|
})
|
|
|
|
it('omits errorCode when the error has none', () => {
|
|
const breadcrumb = buildReadDirErrorBreadcrumb({
|
|
dirPath: '/remote/repo',
|
|
connectionId: 'ssh-1',
|
|
throwSite: 'ssh-provider',
|
|
error: new Error('Remote connection dropped.')
|
|
})
|
|
expect(breadcrumb).toMatchObject({ throwSite: 'ssh-provider', errorName: 'Error' })
|
|
expect(breadcrumb.errorCode).toBeUndefined()
|
|
expect(breadcrumb.hasConnectionId).toBe(true)
|
|
})
|
|
})
|