feat: add persistent desktop assistant workspace
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
698a15ad14
commit
6ef1795b81
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
mkdtemp,
|
||||
readFile,
|
||||
rm,
|
||||
writeFile
|
||||
} from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { parse } from 'yaml'
|
||||
import {
|
||||
addContinuePermanentPermission,
|
||||
createContinuePermissionRule
|
||||
} from './continue-permissions'
|
||||
|
||||
const temporaryDirectories: string[] = []
|
||||
|
||||
async function createTemporaryDirectory(): Promise<string> {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'goodbuddy-permissions-'))
|
||||
temporaryDirectories.push(directory)
|
||||
return directory
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
temporaryDirectories.splice(0).map((directory) =>
|
||||
rm(directory, { recursive: true, force: true })
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
describe('Continue permissions', () => {
|
||||
it('generates an exact narrow rule for command and file tools', () => {
|
||||
expect(
|
||||
createContinuePermissionRule('Bash', {
|
||||
command: 'git status --short'
|
||||
})
|
||||
).toBe('Bash(git status --short)')
|
||||
expect(
|
||||
createContinuePermissionRule('MultiEdit', {
|
||||
file_path: 'D:\\workspace\\report.md'
|
||||
})
|
||||
).toBe('MultiEdit(D:\\workspace\\report.md)')
|
||||
expect(() =>
|
||||
createContinuePermissionRule('Write', {
|
||||
filepath: 'D:\\workspace\\report.md'
|
||||
})
|
||||
).toThrow('足够窄化')
|
||||
})
|
||||
|
||||
it('atomically adds an allow rule and preserves restrictive policies', async () => {
|
||||
const directory = await createTemporaryDirectory()
|
||||
const filePath = join(directory, 'permissions.yaml')
|
||||
await writeFile(
|
||||
filePath,
|
||||
'exclude:\n - Bash(rm *)\nask: []\nallow:\n - Read\n',
|
||||
'utf8'
|
||||
)
|
||||
|
||||
await addContinuePermanentPermission(
|
||||
'Bash(git status --short)',
|
||||
filePath
|
||||
)
|
||||
|
||||
const value = parse(await readFile(filePath, 'utf8')) as {
|
||||
allow: string[]
|
||||
ask: string[]
|
||||
exclude: string[]
|
||||
}
|
||||
expect(value.allow).toEqual(['Read', 'Bash(git status --short)'])
|
||||
expect(value.ask).toEqual([])
|
||||
expect(value.exclude).toEqual(['Bash(rm *)'])
|
||||
})
|
||||
|
||||
it('refuses to weaken a higher-priority ask rule', async () => {
|
||||
const directory = await createTemporaryDirectory()
|
||||
const filePath = join(directory, 'permissions.yaml')
|
||||
const contents = 'ask:\n - Bash(git *)\nallow: []\n'
|
||||
await writeFile(filePath, contents, 'utf8')
|
||||
|
||||
await expect(
|
||||
addContinuePermanentPermission(
|
||||
'Bash(git status --short)',
|
||||
filePath
|
||||
)
|
||||
).rejects.toThrow('ask 规则优先级')
|
||||
await expect(readFile(filePath, 'utf8')).resolves.toBe(contents)
|
||||
})
|
||||
|
||||
it('fails closed when an existing policy file is malformed', async () => {
|
||||
const directory = await createTemporaryDirectory()
|
||||
const filePath = join(directory, 'permissions.yaml')
|
||||
await writeFile(filePath, 'allow: not-an-array\n', 'utf8')
|
||||
|
||||
await expect(
|
||||
addContinuePermanentPermission('Read', filePath)
|
||||
).rejects.toThrow('无法安全解析')
|
||||
await expect(readFile(filePath, 'utf8')).resolves.toBe(
|
||||
'allow: not-an-array\n'
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user