mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Computer Use setup could fail on machines where PATH discovery misses a valid Python installation, especially Windows or conda-style environments. Store an optional interpreter path, prefer it during environment checks and venv creation, and expose a desktop settings control for selecting or clearing it. Constraint: Python discovery is environment-specific and cannot always be inferred from PATH. Rejected: Continue falling back to PATH after an invalid custom path | hides a user-selected broken interpreter and makes diagnosis ambiguous. Confidence: high Scope-risk: moderate Directive: Preserve unknown Computer Use config fields and keep blank interpreter paths normalized to automatic detection. Tested: bun test src/utils/computerUse/preauthorizedConfig.test.ts src/server/__tests__/computer-use-python.test.ts src/server/__tests__/computer-use-api.test.ts Tested: cd desktop && bun run test src/pages/ComputerUseSettings.test.tsx Tested: cd desktop && bun run lint Tested: cd desktop && bun run build Tested: Computer Use browser smoke saved /opt/homebrew/bin/python3 and backend persisted then reset pythonPath to null Not-tested: Full bun run check:server; existing cron-scheduler-launcher test expects CLAUDE_CODE_ENTRYPOINT=sdk-cli but received undefined. Related: https://github.com/NanmiCoder/cc-haha/issues/331
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
|
|
import { handleComputerUseApi } from '../api/computer-use.js'
|
|
|
|
const originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR
|
|
let configDir: string | null = null
|
|
|
|
function makeRequest(method: string, body?: unknown): Request {
|
|
return new Request('http://localhost/api/computer-use/authorized-apps', {
|
|
method,
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
})
|
|
}
|
|
|
|
async function callAuthorizedApps(method: string, body?: unknown): Promise<Response> {
|
|
return handleComputerUseApi(
|
|
makeRequest(method, body),
|
|
new URL('http://localhost/api/computer-use/authorized-apps'),
|
|
['api', 'computer-use', 'authorized-apps'],
|
|
)
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
configDir = await mkdtemp(join(tmpdir(), 'cc-haha-computer-use-api-'))
|
|
process.env.CLAUDE_CONFIG_DIR = configDir
|
|
})
|
|
|
|
afterEach(async () => {
|
|
if (originalClaudeConfigDir === undefined) {
|
|
delete process.env.CLAUDE_CONFIG_DIR
|
|
} else {
|
|
process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir
|
|
}
|
|
|
|
if (configDir) {
|
|
await rm(configDir, { recursive: true, force: true })
|
|
configDir = null
|
|
}
|
|
})
|
|
|
|
describe('Computer Use API authorized app config', () => {
|
|
it('defaults Computer Use enabled for existing users without config', async () => {
|
|
const res = await callAuthorizedApps('GET')
|
|
|
|
expect(res.status).toBe(200)
|
|
expect(await res.json()).toMatchObject({
|
|
enabled: true,
|
|
authorizedApps: [],
|
|
})
|
|
})
|
|
|
|
it('persists the Computer Use enabled flag independently', async () => {
|
|
const putRes = await callAuthorizedApps('PUT', { enabled: false })
|
|
expect(putRes.status).toBe(200)
|
|
|
|
const getRes = await callAuthorizedApps('GET')
|
|
expect(await getRes.json()).toMatchObject({ enabled: false })
|
|
|
|
const raw = await readFile(
|
|
join(configDir!, 'cc-haha', 'computer-use-config.json'),
|
|
'utf8',
|
|
)
|
|
expect(JSON.parse(raw)).toMatchObject({ enabled: false })
|
|
})
|
|
|
|
it('persists and normalizes a custom Python interpreter path', async () => {
|
|
const pythonPath = ' C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe '
|
|
const putRes = await callAuthorizedApps('PUT', { pythonPath })
|
|
expect(putRes.status).toBe(200)
|
|
|
|
const getRes = await callAuthorizedApps('GET')
|
|
expect(await getRes.json()).toMatchObject({
|
|
pythonPath: 'C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe',
|
|
})
|
|
|
|
const resetRes = await callAuthorizedApps('PUT', { pythonPath: '' })
|
|
expect(resetRes.status).toBe(200)
|
|
|
|
const resetGetRes = await callAuthorizedApps('GET')
|
|
expect(await resetGetRes.json()).toMatchObject({ pythonPath: null })
|
|
})
|
|
})
|