207 lines
18 KiB
JavaScript
207 lines
18 KiB
JavaScript
// Own isolated production preload/IPC -> actual OMP -> controlled Responses SSE.
|
|
// No original WorkBuddy observation and no real user model/registry access.
|
|
const fs = require('node:fs/promises')
|
|
const path = require('node:path')
|
|
const assert = require('node:assert/strict')
|
|
const { spawn } = require('node:child_process')
|
|
if (!process.versions.electron) {
|
|
;(async () => {
|
|
const directory = await fs.mkdtemp(path.resolve('.tmp/responses-integration-'))
|
|
await Promise.all(['data', 'sessions', 'home'].map(name => fs.mkdir(path.join(directory, name))))
|
|
const omp = process.env.YCODE_OMP_PATH || path.join(process.env.LOCALAPPDATA || '', 'Ygent', 'omp.exe')
|
|
assert.ok((await fs.stat(omp)).isFile(), 'Actual OMP executable required')
|
|
const env = { ...process.env, YCODE_USER_DATA_DIR: path.join(directory, 'data'), YCODE_OMP_HOME: path.join(directory, 'home'),
|
|
YCODE_QA_DIRECTORY: directory, YCODE_OMP_PATH: omp, YCODE_DISABLE_GPU: '1', YCODE_QA_OFFSCREEN: '1' }
|
|
delete env.ELECTRON_RUN_AS_NODE; delete env.ELECTRON_RENDERER_URL; delete env.YCODE_MODEL_API_KEY
|
|
const child = spawn(require('electron'), ['--no-sandbox', '--disable-gpu-sandbox', __filename], { env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
child.stdout.on('data', bytes => { if (bytes.toString().includes('RESPONSES_')) process.stdout.write(bytes) })
|
|
child.stderr.on('data', () => {}) // Consume arbitrary diagnostics without persisting credential-bearing text.
|
|
const timer = setTimeout(() => child.kill(), 120000)
|
|
child.once('exit', async code => {
|
|
clearTimeout(timer)
|
|
// Secret is generated only inside the child, and its process logs are sanitized there.
|
|
// Do not persist arbitrary OMP stderr here; diagnostics are recorded in the bounded child report.
|
|
console.log(JSON.stringify({ directory, exitCode: code }))
|
|
if (code) console.error('Responses integration failed; inspect the isolated report.json and phase.txt.')
|
|
process.exitCode = code ?? 1
|
|
})
|
|
})().catch(error => { console.error(error); process.exitCode = 1 })
|
|
} else {
|
|
const { app, BrowserWindow } = require('electron')
|
|
const { createServer } = require('node:http')
|
|
const { randomBytes, createHash } = require('node:crypto')
|
|
const { pathToFileURL } = require('node:url')
|
|
const directory = process.env.YCODE_QA_DIRECTORY, data = path.join(directory, 'data'), sessionData = path.join(directory, 'sessions'), home = path.join(directory, 'home')
|
|
const relative = path.relative(path.resolve('.tmp'), directory)
|
|
assert.ok(relative.startsWith('responses-integration-') && !relative.includes('..') && !path.isAbsolute(relative))
|
|
assert.equal(path.resolve(process.env.YCODE_OMP_HOME), home)
|
|
assert.equal(path.resolve(process.env.YCODE_USER_DATA_DIR), data)
|
|
app.disableHardwareAcceleration()
|
|
app.setPath('userData', data); app.setPath('sessionData', sessionData); app.setPath('home', home)
|
|
const secret = 'controlled-responses-' + randomBytes(24).toString('hex')
|
|
const prompts = ['RESPONSES_FIRST_REQUEST', 'RESPONSES_SECOND_REQUEST', 'RESPONSES_CANCEL_REQUEST']
|
|
const replies = ['RESPONSES_REAL_OMP_REPLY_ONE', 'RESPONSES_REAL_OMP_REPLY_TWO']
|
|
const checks = [], requests = [], sentEvents = [], serverErrors = [], sockets = new Set()
|
|
let main, server, phase = 'boot', session, info, connectionTest
|
|
const pause = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
const sanitize = value => value === undefined ? null : JSON.parse(JSON.stringify(value, (key, item) => {
|
|
if (/^(apiKey|authorization|secret|password|credential|cookie)$/i.test(key)) return '[redacted]'
|
|
return typeof item === 'string' ? item.split(secret).join('[redacted-controlled-secret]') : item
|
|
}))
|
|
const check = (label, actual, expected) => checks.push({ label, pass: JSON.stringify(actual) === JSON.stringify(expected), actual: sanitize(actual), expected: sanitize(expected) })
|
|
async function read(source) {
|
|
let timer
|
|
try { return await Promise.race([main.webContents.executeJavaScript(source), new Promise((_, reject) => { timer = setTimeout(() => reject(Error('Renderer timeout')), 15000) })]) }
|
|
finally { clearTimeout(timer) }
|
|
}
|
|
async function wait(readValue, label, timeout = 30000) {
|
|
const start = Date.now()
|
|
while (Date.now() - start < timeout) { const value = await readValue(); if (value) return value; await pause(30) }
|
|
throw Error('Timeout: ' + label)
|
|
}
|
|
async function step(label) { phase = label; await fs.writeFile(path.join(directory, 'phase.txt'), label) }
|
|
function flattenText(value) {
|
|
if (typeof value === 'string') return value
|
|
if (Array.isArray(value)) return value.map(flattenText).join('\n')
|
|
if (value && typeof value === 'object') return ['text', 'content', 'input'].filter(key => Object.hasOwn(value, key)).map(key => flattenText(value[key])).join('\n')
|
|
return ''
|
|
}
|
|
function responseObject(id, model, text, complete) {
|
|
return { id, object: 'response', created_at: 1700000000, status: complete ? 'completed' : 'in_progress', error: null, incomplete_details: null,
|
|
model, output: complete ? [{ id: id + '_message', type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', text, annotations: [] }] }] : [],
|
|
usage: complete ? { input_tokens: 20, output_tokens: 8, total_tokens: 28, input_tokens_details: { cached_tokens: 0 }, output_tokens_details: { reasoning_tokens: 0 } } : null,
|
|
parallel_tool_calls: false, tools: [], tool_choice: 'none', store: false }
|
|
}
|
|
function streamResponse(response, id, model, text, hold = false) {
|
|
response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive' })
|
|
let sequence = 0
|
|
const emit = (type, fields) => {
|
|
sentEvents.push({ responseId: id, type })
|
|
response.write(`event: ${type}\ndata: ${JSON.stringify({ type, sequence_number: sequence++, ...fields })}\n\n`)
|
|
}
|
|
emit('response.created', { response: responseObject(id, model, '', false) })
|
|
emit('response.in_progress', { response: responseObject(id, model, '', false) })
|
|
const item = { id: id + '_message', type: 'message', status: 'in_progress', role: 'assistant', content: [] }
|
|
emit('response.output_item.added', { output_index: 0, item })
|
|
emit('response.content_part.added', { item_id: item.id, output_index: 0, content_index: 0, part: { type: 'output_text', text: '', annotations: [] } })
|
|
emit('response.output_text.delta', { item_id: item.id, output_index: 0, content_index: 0, delta: text, logprobs: [] })
|
|
if (hold) return // Deliberately leave an actual SSE request open until chat.cancel.
|
|
emit('response.output_text.done', { item_id: item.id, output_index: 0, content_index: 0, text, logprobs: [] })
|
|
emit('response.content_part.done', { item_id: item.id, output_index: 0, content_index: 0, part: { type: 'output_text', text, annotations: [] } })
|
|
const completed = responseObject(id, model, text, true)
|
|
emit('response.output_item.done', { output_index: 0, item: completed.output[0] })
|
|
emit('response.completed', { response: completed })
|
|
response.end()
|
|
}
|
|
async function saveReport(extra = {}) {
|
|
const events = main && !main.isDestroyed() ? await read('window.responsesQaEvents||[]').catch(() => []) : []
|
|
const messages = session && main && !main.isDestroyed() ? await read(`window.ycode.session.messages(${JSON.stringify(session.id)})`).catch(() => []) : []
|
|
const mainBytes = await fs.readFile('out/main/index.mjs')
|
|
const report = sanitize({ result: checks.length && checks.every(item => item.pass) ? 'PASS' : 'FAIL', phase,
|
|
scope: 'Actual isolated production Electron App/preload/IPC -> actual OMP executable -> loopback standard Responses HTTP/SSE. Controlled fixture is not real user upstream acceptance.',
|
|
checks, requests, sentEvents, serverErrors, events, messages, connectionTest,
|
|
isolation: { directory, configDir: info?.configDir, userData: app.getPath('userData'), sessionData: app.getPath('sessionData'), ompHome: home },
|
|
session: session && { id: session.id, cwd: session.cwd }, buildSha256: createHash('sha256').update(mainBytes).digest('hex'), ...extra })
|
|
await fs.writeFile(path.join(directory, 'report.json'), JSON.stringify(report, null, 2))
|
|
}
|
|
async function cleanup() {
|
|
for (const socket of sockets) socket.destroy()
|
|
server?.close()
|
|
}
|
|
;(async () => {
|
|
// Import before the first await, preserving production readiness/GPU ordering.
|
|
const production = import(pathToFileURL(path.resolve('out/main/index.mjs')).href)
|
|
server = createServer(async (request, response) => {
|
|
try {
|
|
let raw = ''; for await (const bytes of request) { raw += bytes; if (raw.length > 2 * 1024 * 1024) throw Error('Fixture payload budget exceeded') }
|
|
const body = JSON.parse(raw), text = flattenText(body.input)
|
|
const marker = prompts.findLast(prompt => text.includes(prompt))
|
|
const kind = marker ? marker === prompts[2] ? 'cancel' : 'turn' : 'connection-test'
|
|
requests.push({ kind, method: request.method, path: request.url, model: body.model, stream: body.stream === true,
|
|
authorizationPresent: !!request.headers.authorization, authorizationMatchesControlledKey: request.headers.authorization === 'Bearer ' + secret,
|
|
toolsCount: Array.isArray(body.tools) ? body.tools.length : 0, toolChoice: body.tool_choice ?? null,
|
|
inputRoles: Array.isArray(body.input) ? body.input.map(item => item.role ?? item.type) : [],
|
|
history: { firstPrompt: text.includes(prompts[0]), firstReply: text.includes(replies[0]), secondPrompt: text.includes(prompts[1]), cancelPrompt: text.includes(prompts[2]) },
|
|
maxOutputTokens: body.max_output_tokens ?? null, previousResponseIdPresent: !!body.previous_response_id })
|
|
if (request.url !== '/v1/responses') { response.writeHead(404, { 'content-type': 'application/json' }); response.end(JSON.stringify({ error: { message: 'Unexpected controlled request path', type: 'invalid_request_error' } })); return }
|
|
if (request.headers.authorization !== 'Bearer ' + secret) { response.writeHead(401); response.end('{}'); return }
|
|
const reply = marker === prompts[1] ? replies[1] : marker === prompts[2] ? 'RESPONSES_CANCEL_PARTIAL' : marker === prompts[0] ? replies[0] : 'RESPONSES_CONNECTION_OK'
|
|
const id = 'resp_controlled_' + requests.length
|
|
if (!body.stream) { response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify(responseObject(id, body.model, reply, true))); return }
|
|
streamResponse(response, id, body.model, reply, kind === 'cancel')
|
|
} catch (error) { serverErrors.push(String(error)); response.writeHead(400); response.end('{}') }
|
|
})
|
|
server.on('connection', socket => { sockets.add(socket); socket.on('close', () => sockets.delete(socket)) })
|
|
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); await production; await app.whenReady()
|
|
await wait(async () => { main = BrowserWindow.getAllWindows().find(win => win.getTitle() === 'YCode'); return main && !main.webContents.isLoading() }, 'production main window')
|
|
await wait(() => read('!!window.ycode&&!!document.querySelector(".chat-pane")'), 'real App/preload')
|
|
info = await read('window.ycode.app.info()')
|
|
assert.equal(path.resolve(info.configDir), data, 'Must never write the live user registry')
|
|
assert.equal(info.omp.available, true, 'Actual OMP executable must be available')
|
|
check('isolated configDir', path.resolve(info.configDir), data)
|
|
check('isolated sessionData', app.getPath('sessionData'), sessionData)
|
|
const input = { id: 'responses-controlled', name: 'Responses Controlled', providerId: 'custom', model: 'responses-fixture',
|
|
baseUrl: `http://127.0.0.1:${server.address().port}/v1/responses`, apiKey: secret, authMode: 'api-key',
|
|
supportsToolCall: false, supportsImages: false, supportsReasoning: false, maxInputTokens: 8192, maxOutputTokens: 1234 }
|
|
await step('save-and-test-explicit-responses-model')
|
|
const registry = await read(`window.ycode.models.save(${JSON.stringify(input)})`), saved = registry.models.find(model => model.id === input.id)
|
|
check('preload model view omits existing plaintext key', Object.hasOwn(saved, 'apiKey'), false)
|
|
check('saved model reports key configured', saved.hasApiKey, true)
|
|
await read(`window.ycode.models.select(${JSON.stringify(input.id)})`)
|
|
connectionTest = await read(`window.ycode.models.test(${JSON.stringify({ ...input, apiKey: undefined })})`)
|
|
check('production models.test accepts explicit Responses endpoint', connectionTest.success, true)
|
|
const spaces = await read('window.ycode.workspace.generate("Responses隔离验收空间")'), workspace = spaces.workspaces[0]
|
|
assert.ok(path.resolve(workspace.root).startsWith(directory + path.sep))
|
|
session = await read(`window.ycode.session.create('Responses Integration',${JSON.stringify({ modelId: input.id, workspaceId: workspace.id })})`)
|
|
await read('window.responsesQaEvents=[];window.ycode.chat.onEvent(e=>window.responsesQaEvents.push(e));true')
|
|
for (let turn = 0; turn < 2; turn++) {
|
|
await step('actual-responses-omp-turn-' + (turn + 1))
|
|
const run = await read(`window.ycode.chat.send(${JSON.stringify({ sessionId: session.id, text: prompts[turn] })})`)
|
|
const message = await wait(() => read(`window.ycode.session.messages(${JSON.stringify(session.id)}).then(m=>m.find(x=>x.id===${JSON.stringify(run.assistantMessageId)}&&x.status!=='streaming'))`), 'actual Responses turn terminal', 40000)
|
|
check(`turn ${turn + 1} terminal done`, message.status, 'done')
|
|
check(`turn ${turn + 1} actual SSE reply`, message.content, replies[turn])
|
|
if (message.status !== 'done') throw Error('Actual OMP Responses terminal failure: ' + (message.error || message.status))
|
|
}
|
|
const turns = requests.filter(item => item.kind === 'turn')
|
|
check('two actual OMP streaming turn requests', turns.length, 2)
|
|
check('correct endpoint no suffix duplication', requests.map(item => item.path), requests.map(() => '/v1/responses'))
|
|
check('actual model IDs', requests.map(item => item.model), requests.map(() => input.model))
|
|
check('secret authentication matches controlled key', requests.map(item => item.authorizationMatchesControlledKey), requests.map(() => true))
|
|
check('actual OMP turns use streaming Responses', turns.map(item => item.stream), [true, true])
|
|
check('tool-disabled actual OMP requests contain zero tools', turns.map(item => item.toolsCount), [0, 0])
|
|
check('second turn receives persisted prior user/assistant history', turns[1]?.history, { firstPrompt: true, firstReply: true, secondPrompt: true, cancelPrompt: false })
|
|
check('actual OMP max output tokens', turns.map(item => item.maxOutputTokens), [1234, 1234])
|
|
await step('actual-responses-cancel-open-sse')
|
|
const run = await read(`window.ycode.chat.send(${JSON.stringify({ sessionId: session.id, text: prompts[2] })})`)
|
|
await wait(() => read(`window.ycode.session.messages(${JSON.stringify(session.id)}).then(m=>m.find(x=>x.id===${JSON.stringify(run.assistantMessageId)}&&x.content.includes('RESPONSES_CANCEL_PARTIAL')))`), 'actual open SSE partial text')
|
|
const cancellation = await read(`window.ycode.chat.cancel(${JSON.stringify(run.runId)})`)
|
|
check('production cancel accepted', cancellation.cancelled, true)
|
|
const cancelled = await wait(() => read(`window.ycode.session.messages(${JSON.stringify(session.id)}).then(m=>m.find(x=>x.id===${JSON.stringify(run.assistantMessageId)}&&x.status!=='streaming'))`), 'actual Responses cancellation terminal', 10000)
|
|
check('open SSE reaches cancelled rather than done/error', cancelled.status, 'cancelled')
|
|
check('cancel retains actually received partial text', cancelled.content, 'RESPONSES_CANCEL_PARTIAL')
|
|
await pause(200)
|
|
const terminalEvents = await read(`window.responsesQaEvents.filter(e=>e.runId===${JSON.stringify(run.runId)}&&['done','error','cancelled'].includes(e.type)).map(e=>e.type)`)
|
|
check('exactly one cancellation terminal event', terminalEvents, ['cancelled'])
|
|
check('cancel request endpoint and tools', requests.filter(item => item.kind === 'cancel').map(item => [item.path, item.toolsCount]), [['/v1/responses', 0]])
|
|
check('controlled fixture server errors', serverErrors, [])
|
|
const transcript = path.join(data, 'omp-sessions', `session-${encodeURIComponent(session.id)}.jsonl`)
|
|
check('real OMP transcript persisted', (await fs.stat(transcript)).size > 0, true)
|
|
const profiles = await fs.readdir(path.join(home, '.omp', 'profiles')), generated = []
|
|
for (const name of profiles) { const yaml = await fs.readFile(path.join(home, '.omp', 'profiles', name, 'agent', 'models.yml'), 'utf8'); if (yaml.includes('responses-fixture')) generated.push({ name, yaml }) }
|
|
check('one generated Responses profile', generated.length, 1)
|
|
check('actual profile selects openai-responses', /^ api: openai-responses$/m.test(generated[0]?.yaml || ''), true)
|
|
check('actual profile SDK base strips explicit endpoint suffix', generated[0]?.yaml.includes(JSON.stringify(`http://127.0.0.1:${server.address().port}/v1`)), true)
|
|
check('profile contains no controlled plaintext secret', generated[0]?.yaml.includes(secret), false)
|
|
await step('complete')
|
|
await saveReport({ transcript, generatedProfile: generated[0] })
|
|
const pass = checks.every(item => item.pass)
|
|
console.log('RESPONSES_INTEGRATION_' + (pass ? 'PASS ' : 'FAIL ') + directory)
|
|
for (const item of checks.filter(item => !item.pass)) console.log('RESPONSES_FAILURE ' + JSON.stringify(item))
|
|
await cleanup(); process.exitCode = pass ? 0 : 1; app.quit()
|
|
})().catch(async error => {
|
|
await saveReport({ result: 'FAIL', error: String(error), stack: error.stack }).catch(() => undefined)
|
|
console.error('RESPONSES_DIAGNOSTIC ' + sanitize(String(error)))
|
|
await cleanup(); process.exitCode = 1; app.quit()
|
|
})
|
|
}
|