feat: expand secure runtime and workspace UX

This commit is contained in:
lofyer
2026-08-04 00:57:08 +08:00
parent 09e9fbf5e2
commit 2910d315f7
56 changed files with 6164 additions and 882 deletions
@@ -1,10 +1,14 @@
import { execFile } from 'node:child_process'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { promisify } from 'node:util'
import { afterEach, describe, expect, it } from 'vitest'
import { getWorkspaceChanges } from './workspace-changes-service'
import {
getWorkspaceChanges,
listWorkspaceDirectory,
readWorkspaceFile
} from './workspace-changes-service'
const execute = promisify(execFile)
const temporaryDirectories: string[] = []
@@ -48,6 +52,12 @@ describe('getWorkspaceChanges', () => {
})
expect(changes.status).toContain('M tracked.txt')
expect(changes.status).toContain('?? new.txt')
expect(changes.files).toEqual(
expect.arrayContaining([
{ path: 'tracked.txt', status: ' M' },
{ path: 'new.txt', status: '??' }
])
)
expect(changes.patch).toContain('-before')
expect(changes.patch).toContain('+after')
})
@@ -59,6 +69,63 @@ describe('getWorkspaceChanges', () => {
const changes = await getWorkspaceChanges(directory)
expect(changes.available).toBe(false)
expect(changes.files).toEqual([])
expect(changes.error).toBeTruthy()
})
})
describe('workspace file browsing', () => {
it('lists directories and reads bounded Markdown previews', async () => {
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-files-'))
temporaryDirectories.push(directory)
await mkdir(join(directory, 'docs'))
await writeFile(join(directory, 'docs', 'guide.md'), '# 使用说明\n')
await writeFile(join(directory, 'notes.txt'), 'hello\n')
const root = await listWorkspaceDirectory(directory, '')
const docs = await listWorkspaceDirectory(directory, 'docs')
const preview = await readWorkspaceFile(directory, 'docs/guide.md')
expect(root.entries).toEqual([
{ name: 'docs', path: 'docs', type: 'directory' },
{ name: 'notes.txt', path: 'notes.txt', type: 'file' }
])
expect(docs.entries).toEqual([
{
name: 'guide.md',
path: 'docs/guide.md',
type: 'file'
}
])
expect(preview).toMatchObject({
path: 'docs/guide.md',
name: 'guide.md',
content: '# 使用说明\n',
mimeType: 'text/markdown'
})
})
it('rejects traversal, unsupported files, invalid UTF-8, and oversized files', async () => {
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-files-'))
temporaryDirectories.push(directory)
await writeFile(join(directory, 'image.bin'), Buffer.from([0, 1, 2]))
await writeFile(join(directory, 'invalid.txt'), Buffer.from([0xff]))
await writeFile(
join(directory, 'large.txt'),
Buffer.alloc(256 * 1024 + 1, 97)
)
await expect(
readWorkspaceFile(directory, '../outside.txt')
).rejects.toThrow('相对路径')
await expect(
readWorkspaceFile(directory, 'image.bin')
).rejects.toThrow('不支持安全预览')
await expect(
readWorkspaceFile(directory, 'invalid.txt')
).rejects.toThrow('有效 UTF-8')
await expect(
readWorkspaceFile(directory, 'large.txt')
).rejects.toThrow('超过 256KB')
})
})