130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Launch the desktop app with a mock inference provider — no real API
|
|
* keys needed. Starts a local OpenAI-compatible server that returns a
|
|
* canned reply, writes an isolated config.yaml + .env, and launches the
|
|
* built Electron app against them.
|
|
*
|
|
* This reuses the same mock-server core as the E2E fixtures
|
|
* (scripts/mock-inference-server.mjs, apps/desktop/e2e/mock-server.ts +
|
|
* fixtures.ts), so local dev, desktop E2E, and the installer/updater E2E
|
|
* all exercise the same mock inference chain.
|
|
*
|
|
* Prerequisite: `npm run build` must have been run so dist/ exists.
|
|
*
|
|
* Usage:
|
|
* node scripts/dev-mock.mjs
|
|
* npm run dev:mock
|
|
*
|
|
* The mock server listens on an ephemeral port and replies to every
|
|
* chat completion with:
|
|
* "Hello from the mock inference server! The full boot chain is working."
|
|
*/
|
|
|
|
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { spawn, spawnSync } from 'node:child_process'
|
|
|
|
import { startMockServer } from '../../../scripts/mock-inference-server.mjs'
|
|
|
|
const DESKTOP_ROOT = path.resolve(import.meta.dirname, '..')
|
|
const REPO_ROOT = path.resolve(DESKTOP_ROOT, '..', '..')
|
|
|
|
// ── Config + env writing (mirrors e2e/fixtures.ts) ─────────────────────
|
|
|
|
function createSandbox() {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), `hermes-dev-mock-${Date.now()}`))
|
|
const hermesHome = path.join(root, 'hermes-home')
|
|
const userDataDir = path.join(root, 'electron-user-data')
|
|
fs.mkdirSync(hermesHome, { recursive: true })
|
|
fs.mkdirSync(userDataDir, { recursive: true })
|
|
return { root, hermesHome, userDataDir, cleanup: () => fs.rmSync(root, { recursive: true, force: true }) }
|
|
}
|
|
|
|
function writeMockConfig(hermesHome, mockUrl) {
|
|
fs.writeFileSync(
|
|
path.join(hermesHome, 'config.yaml'),
|
|
`# Auto-generated by dev-mock.mjs
|
|
model:
|
|
default: mock-model
|
|
provider: mock
|
|
providers:
|
|
mock:
|
|
api: ${mockUrl}/v1
|
|
name: Mock
|
|
api_mode: chat_completions
|
|
key_env: MOCK_API_KEY
|
|
models:
|
|
mock-model: {}
|
|
context_length: 4096
|
|
`,
|
|
'utf8',
|
|
)
|
|
fs.writeFileSync(path.join(hermesHome, '.env'), 'MOCK_API_KEY=e2e-mock-key\n', 'utf8')
|
|
}
|
|
|
|
// ── Electron launch ────────────────────────────────────────────────────
|
|
|
|
function findElectron() {
|
|
const local = path.join(REPO_ROOT, 'node_modules', 'electron', 'dist', 'electron')
|
|
if (fs.existsSync(local)) return local
|
|
const r = spawnSync('which', ['electron'], { encoding: 'utf8' })
|
|
if (r.status === 0 && r.stdout.trim()) return r.stdout.trim()
|
|
throw new Error('Electron binary not found. Run "npm install" from the repo root.')
|
|
}
|
|
|
|
function assertDistBuilt() {
|
|
const electronMain = path.join(DESKTOP_ROOT, 'dist', 'electron-main.mjs')
|
|
const indexHtml = path.join(DESKTOP_ROOT, 'dist', 'index.html')
|
|
if (!fs.existsSync(electronMain) || !fs.existsSync(indexHtml)) {
|
|
throw new Error(
|
|
`Desktop dist not built. Run 'cd apps/desktop && npm run build' first.\n` +
|
|
`Missing: ${electronMain}`,
|
|
)
|
|
}
|
|
}
|
|
|
|
// ── Main ───────────────────────────────────────────────────────────────
|
|
|
|
async function main() {
|
|
assertDistBuilt()
|
|
|
|
console.log('Starting mock inference server...')
|
|
const mock = await startMockServer()
|
|
console.log(` Mock server: ${mock.url}`)
|
|
|
|
const sandbox = createSandbox()
|
|
writeMockConfig(sandbox.hermesHome, mock.url)
|
|
console.log(` HERMES_HOME: ${sandbox.hermesHome}`)
|
|
|
|
const electronBin = findElectron()
|
|
|
|
const env = {
|
|
...process.env,
|
|
HERMES_HOME: sandbox.hermesHome,
|
|
HERMES_DESKTOP_USER_DATA_DIR: sandbox.userDataDir,
|
|
HERMES_DESKTOP_IGNORE_EXISTING: '1',
|
|
HERMES_DESKTOP_HERMES_ROOT: REPO_ROOT,
|
|
HERMES_DESKTOP_APP_NAME: `HermesDevMock-${Date.now()}`,
|
|
}
|
|
|
|
console.log('Launching Electron...')
|
|
const child = spawn(electronBin, [DESKTOP_ROOT, '--disable-gpu', '--no-sandbox'], {
|
|
env,
|
|
cwd: DESKTOP_ROOT,
|
|
stdio: 'inherit',
|
|
})
|
|
|
|
child.on('exit', (code) => {
|
|
mock.close()
|
|
sandbox.cleanup()
|
|
process.exit(code ?? 0)
|
|
})
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|