mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
Electron's sidecar runs outside app.asar, so H5 static files must be available as normal unpacked files. Point the sidecar at the unpacked renderer dist and keep a server fallback for stale app.asar-style paths. Constraint: Packaged Bun sidecars cannot read app.asar paths with ordinary fs stat calls. Rejected: Serve H5 from app.asar directly | the external sidecar is not Electron and does not get asar filesystem support. Confidence: high Scope-risk: narrow Directive: Keep package-smoke checking app.asar.unpacked/dist/index.html before changing asarUnpack or H5 dist paths. Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-policy.test.ts Tested: bun test desktop/electron/services/sidecarManager.test.ts scripts/quality-gate/package-smoke/index.test.ts Tested: bun run check:server Tested: cd desktop && bun run check:electron Tested: git diff --check Tested: SKIP_INSTALL=1 SIGN_BUILD=0 MAC_TARGETS=dmg desktop/scripts/build-macos-arm64.sh Tested: packaged sidecar curl /?serverUrl=...&h5Token=... returned HTTP 200 Not-tested: Gatekeeper notarization for the local ad-hoc DMG
92 lines
3.3 KiB
TypeScript
92 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import path from 'node:path'
|
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import {
|
|
buildSidecarEnv,
|
|
createAdapterPlan,
|
|
createServerPlan,
|
|
httpToWebSocketUrl,
|
|
pushStartupLog,
|
|
resolveHostTriple,
|
|
} from './sidecarManager'
|
|
|
|
describe('Electron sidecar manager', () => {
|
|
it('maps host platform to existing sidecar target triples', () => {
|
|
expect(resolveHostTriple('darwin', 'arm64')).toBe('aarch64-apple-darwin')
|
|
expect(resolveHostTriple('darwin', 'x64')).toBe('x86_64-apple-darwin')
|
|
expect(resolveHostTriple('win32', 'x64')).toBe('x86_64-pc-windows-msvc')
|
|
expect(resolveHostTriple('linux', 'arm64')).toBe('aarch64-unknown-linux-gnu')
|
|
})
|
|
|
|
it('builds server sidecar args without changing the REST/WebSocket boundary', () => {
|
|
const plan = createServerPlan({
|
|
desktopRoot: '/app/desktop',
|
|
appRoot: '/app',
|
|
port: 49321,
|
|
env: {},
|
|
})
|
|
|
|
expect(plan.args).toEqual([
|
|
'server',
|
|
'--app-root',
|
|
'/app',
|
|
'--host',
|
|
'0.0.0.0',
|
|
'--port',
|
|
'49321',
|
|
])
|
|
expect(plan.env.CLAUDE_H5_AUTO_PUBLIC_URL).toBe('1')
|
|
expect(plan.env.CLAUDE_H5_DIST_DIR).toBe(path.join('/app/desktop', 'dist'))
|
|
})
|
|
|
|
it('can keep sidecar binaries and H5 assets unpacked while pointing app-root at app.asar', () => {
|
|
const plan = createServerPlan({
|
|
desktopRoot: '/Applications/App.app/Contents/Resources/app.asar.unpacked',
|
|
appRoot: '/Applications/App.app/Contents/Resources/app.asar',
|
|
h5DistDir: '/Applications/App.app/Contents/Resources/app.asar.unpacked/dist',
|
|
port: 49321,
|
|
env: {},
|
|
})
|
|
|
|
expect(plan.command).toContain('/Applications/App.app/Contents/Resources/app.asar.unpacked/src-tauri/binaries/claude-sidecar-')
|
|
expect(plan.args).toContain('/Applications/App.app/Contents/Resources/app.asar')
|
|
expect(plan.env.CLAUDE_H5_DIST_DIR).toBe('/Applications/App.app/Contents/Resources/app.asar.unpacked/dist')
|
|
})
|
|
|
|
it('passes portable config and adapter server URL through the sidecar env', () => {
|
|
const configDir = mkdtempSync(path.join(tmpdir(), 'cc-haha-config-'))
|
|
try {
|
|
const env = buildSidecarEnv({ CLAUDE_CONFIG_DIR: configDir }, '/app/dist')
|
|
expect(env.CLAUDE_CONFIG_DIR).toBe(configDir)
|
|
expect(env.XDG_CACHE_HOME).toBe(path.join(configDir, 'Cache'))
|
|
|
|
const adapter = createAdapterPlan({
|
|
desktopRoot: '/app/desktop',
|
|
appRoot: '/app',
|
|
serverUrl: 'http://127.0.0.1:4567',
|
|
flag: '--telegram',
|
|
env: { CLAUDE_CONFIG_DIR: configDir },
|
|
})
|
|
expect(adapter.env.ADAPTER_SERVER_URL).toBe('ws://127.0.0.1:4567')
|
|
expect(adapter.args).toEqual(['adapters', '--app-root', '/app', '--telegram'])
|
|
} finally {
|
|
rmSync(configDir, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
it('keeps startup logs bounded', () => {
|
|
const logs: string[] = []
|
|
for (let index = 0; index < 85; index++) {
|
|
pushStartupLog(logs, `line ${index}`)
|
|
}
|
|
expect(logs).toHaveLength(80)
|
|
expect(logs[0]).toBe('line 5')
|
|
})
|
|
|
|
it('maps http urls to adapter websocket urls', () => {
|
|
expect(httpToWebSocketUrl('http://127.0.0.1:3456')).toBe('ws://127.0.0.1:3456')
|
|
expect(httpToWebSocketUrl('https://example.com')).toBe('wss://example.com')
|
|
})
|
|
})
|