import { describe, expect, it, vi } from 'vitest' import type { AgentConfig } from '../src/config/agentConfig.js' import { buildFrpcToml, isFrpRetired, spawnFrpc, type ChildLike, } from '../src/transport/frpScaffold.js' const CFG: AgentConfig = { relayUrl: 'wss://relay/agent', enrollUrl: 'https://x/enroll', stateDir: '/tmp/x', localTargetUrl: 'ws://127.0.0.1:3000', subdomain: 'host-42', hostId: null, } describe('frpScaffold (T6, v0.8 only)', () => { it('generates a loopback-only tls frpc.toml', () => { const toml = buildFrpcToml(CFG) expect(toml).toContain('local_ip = "127.0.0.1"') expect(toml).toContain('local_port = 3000') expect(toml).toContain('subdomain = "host-42"') expect(toml).toContain('tls_enable = true') expect(toml).not.toMatch(/local_ip = "(?!127\.0\.0\.1)/) }) it('refuses a non-loopback local target (anti-SSRF)', () => { expect(() => buildFrpcToml({ ...CFG, localTargetUrl: 'ws://10.0.0.5:3000' })).toThrow() }) it('propagates child exit to onExit', async () => { let exitHandler: ((code: number | null) => void) | null = null const child: ChildLike = { on: (_ev, cb) => { exitHandler = cb }, kill: vi.fn(), } const scaffold = spawnFrpc(CFG, '/usr/bin/frpc', () => child) const seen: number[] = [] scaffold.onExit((c) => seen.push(c)) await scaffold.start() exitHandler!(7) expect(seen).toEqual([7]) }) it('retirement guard: retired once ed25519', () => { expect(isFrpRetired('ed25519')).toBe(true) expect(isFrpRetired('token')).toBe(false) }) })