refactor(tool-fs): consolidate read rendering; drop the fs/observed try-catch

Two cohesion cleanups on the filesystem tool package:

- Fold window.ts + types.ts + formatReadOutput into one cordis-free
  read-render.ts. Line windowing, the FileReadOutcome shape, and output
  formatting are one concern (the read tool's rendering); splitting them across
  three files added no value. read.ts is now just the tool (schema + I/O).

- Drop observe.ts and emit fs/observed with a plain ctx.emit in read/write/edit.
  The event is contractually a synchronous, side-effect-only recorder
  (file-context's listener is a WeakMap.set), so the per-call try/catch guarded
  against a contract violation that cannot happen under the shipped listener —
  defensive code for an impossible case. The event contract (dsh-fs JSDoc,
  README, RFC) is updated to state the fire-and-forget semantics plainly.
This commit is contained in:
Dudu-0223
2026-06-29 10:34:08 +08:00
parent 4a1177093a
commit f8e99b8740
15 changed files with 100 additions and 159 deletions

View File

@@ -223,19 +223,6 @@ describe('default deployment (with dsh-file-context)', () => {
statSpy.mockRestore()
})
})
describe('contained fs/observed recording', () => {
it('a synchronously throwing fs/observed listener does not fail the completed write', async () => {
ctx.on('fs/observed', () => { throw new Error('listener boom') })
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => {})
const result = await call('write', { file_path: 'a.txt', content: 'hi' })
// The write succeeded on disk; the listener throw was logged and swallowed.
expect(result.isError).toBe(false)
expect(await readFile(join(dir, 'a.txt'), 'utf8')).toBe('hi')
expect(warn).toHaveBeenCalled()
warn.mockRestore()
})
})
})
// --------------------------------------------------------------------------