mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
* local: add Codex Desktop session sync controls * feat(codex): import local Codex sessions into Hapi --------- Co-authored-by: Codex Local <codex-local@example.invalid>
150 lines
5.0 KiB
TypeScript
150 lines
5.0 KiB
TypeScript
import { afterEach, describe, expect, it } from 'bun:test'
|
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { Hono } from 'hono'
|
|
import { AGENT_MESSAGE_PAYLOAD_TYPE } from '@hapi/protocol'
|
|
import { Store } from '../../store'
|
|
import type { SyncEngine } from '../../sync/syncEngine'
|
|
import type { WebAppEnv } from '../middleware/auth'
|
|
import { createCodexDesktopRoutes, importSelectedCodexSessions } from './codexDesktop'
|
|
|
|
const originalCodexHome = process.env.CODEX_HOME
|
|
|
|
function createTranscript(codexHome: string, sessionId: string): void {
|
|
const sessionDir = join(codexHome, 'sessions', '2026', '06', '04')
|
|
mkdirSync(sessionDir, { recursive: true })
|
|
const transcriptPath = join(sessionDir, `rollout-${sessionId}.jsonl`)
|
|
const lines = [
|
|
{
|
|
type: 'session_meta',
|
|
payload: {
|
|
id: sessionId,
|
|
cwd: 'C:\\work\\project',
|
|
originator: 'codex_cli_rs',
|
|
cli_version: '0.0.0-test'
|
|
}
|
|
},
|
|
{
|
|
type: 'response_item',
|
|
payload: {
|
|
type: 'message',
|
|
role: 'user',
|
|
content: [{ type: 'input_text', text: 'normal user message' }]
|
|
}
|
|
},
|
|
{
|
|
type: 'response_item',
|
|
payload: {
|
|
type: 'message',
|
|
role: 'assistant',
|
|
content: [{ type: 'output_text', text: 'normal assistant message' }]
|
|
}
|
|
}
|
|
]
|
|
writeFileSync(transcriptPath, `${lines.map((line) => JSON.stringify(line)).join('\n')}\n`, 'utf-8')
|
|
}
|
|
|
|
function createRoutesApp(namespace: string): Hono<WebAppEnv> {
|
|
const app = new Hono<WebAppEnv>()
|
|
app.use('*', async (c, next) => {
|
|
c.set('namespace', namespace)
|
|
await next()
|
|
})
|
|
app.route('/api', createCodexDesktopRoutes({
|
|
store: new Store(':memory:'),
|
|
getSyncEngine: () => null
|
|
}))
|
|
return app
|
|
}
|
|
|
|
describe('Codex Desktop import routes', () => {
|
|
afterEach(() => {
|
|
if (originalCodexHome === undefined) {
|
|
delete process.env.CODEX_HOME
|
|
} else {
|
|
process.env.CODEX_HOME = originalCodexHome
|
|
}
|
|
})
|
|
|
|
it('imports normal response_item chat messages', async () => {
|
|
const codexHome = mkdtempSync(join(tmpdir(), 'hapi-codex-home-test-'))
|
|
const store = new Store(':memory:')
|
|
const codexSessionId = '11111111-1111-4111-8111-111111111111'
|
|
process.env.CODEX_HOME = codexHome
|
|
|
|
try {
|
|
createTranscript(codexHome, codexSessionId)
|
|
|
|
const result = await importSelectedCodexSessions({
|
|
codexSessionIds: [codexSessionId],
|
|
store,
|
|
namespace: 'default',
|
|
getSyncEngine: () => null
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
const session = store.sessions.getSessionsByNamespace('default')[0]
|
|
expect(session).toBeDefined()
|
|
const messages = store.messages.getAllMessages(session.id)
|
|
expect(messages).toHaveLength(2)
|
|
expect(messages[0].content).toEqual({
|
|
role: 'user',
|
|
content: {
|
|
type: 'text',
|
|
text: 'normal user message'
|
|
},
|
|
meta: {
|
|
sentFrom: 'cli'
|
|
}
|
|
})
|
|
expect(messages[1].content).toEqual({
|
|
role: 'agent',
|
|
content: {
|
|
type: AGENT_MESSAGE_PAYLOAD_TYPE,
|
|
data: {
|
|
type: 'message',
|
|
message: 'normal assistant message',
|
|
id: expect.any(String)
|
|
}
|
|
},
|
|
meta: {
|
|
sentFrom: 'cli'
|
|
}
|
|
})
|
|
} finally {
|
|
store.close()
|
|
rmSync(codexHome, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('rejects Codex transcript endpoints outside the default namespace', async () => {
|
|
const app = createRoutesApp('team-a')
|
|
const response = await app.request('/api/codex/sessions')
|
|
|
|
expect(response.status).toBe(403)
|
|
expect(await response.json()).toEqual({
|
|
success: false,
|
|
error: 'Codex transcript import is not available outside the default namespace'
|
|
})
|
|
})
|
|
|
|
it('allows Codex transcript endpoints in the default namespace', async () => {
|
|
const codexHome = mkdtempSync(join(tmpdir(), 'hapi-codex-home-route-test-'))
|
|
process.env.CODEX_HOME = codexHome
|
|
|
|
try {
|
|
const app = createRoutesApp('default')
|
|
const response = await app.request('/api/codex/sessions')
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(await response.json()).toEqual({
|
|
success: true,
|
|
sessions: []
|
|
})
|
|
} finally {
|
|
rmSync(codexHome, { recursive: true, force: true })
|
|
}
|
|
})
|
|
})
|