mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
refactor(agent-hooks): separate Pi and OMP home fix
This commit is contained in:
+30
-82
@@ -18,8 +18,8 @@ import { PtyWriteUnavailableError } from '../providers/pty-write-unavailable-err
|
||||
const isWindowsHost = process.platform === 'win32'
|
||||
const posixOnlyIt = isWindowsHost ? it.skip : it
|
||||
const expectedOmpStatusExtension = posix.join(
|
||||
'/tmp/orca-user-data',
|
||||
'omp-managed-status-extension',
|
||||
'/tmp/default-omp-agent',
|
||||
'extensions',
|
||||
'orca-agent-status.ts'
|
||||
)
|
||||
function expectedAttributionShimDir(): string {
|
||||
@@ -413,31 +413,15 @@ describe('registerPtyHandlers', () => {
|
||||
ORCA_AGENT_HOOK_TOKEN: 'agent-token'
|
||||
})
|
||||
piBuildPtyEnvMock.mockImplementation(
|
||||
(
|
||||
_ptyId: string,
|
||||
existingAgentDir?: string,
|
||||
kind?: string,
|
||||
options?: { materializeDefaultHome?: boolean }
|
||||
) => {
|
||||
if (kind === 'omp') {
|
||||
if (!existingAgentDir && options?.materializeDefaultHome === false) {
|
||||
return {
|
||||
ORCA_OMP_STATUS_EXTENSION:
|
||||
'/tmp/orca-user-data/omp-managed-status-extension/orca-agent-status.ts'
|
||||
(_ptyId: string, existingAgentDir?: string, kind?: string) =>
|
||||
kind === 'omp'
|
||||
? {
|
||||
ORCA_OMP_SOURCE_AGENT_DIR: existingAgentDir ?? '/tmp/default-omp-agent',
|
||||
ORCA_OMP_STATUS_EXTENSION: `${existingAgentDir ?? '/tmp/default-omp-agent'}/extensions/orca-agent-status.ts`
|
||||
}
|
||||
: {
|
||||
ORCA_PI_SOURCE_AGENT_DIR: existingAgentDir ?? '/tmp/default-pi-agent'
|
||||
}
|
||||
}
|
||||
return {
|
||||
ORCA_OMP_SOURCE_AGENT_DIR: existingAgentDir ?? '/tmp/default-omp-agent',
|
||||
ORCA_OMP_STATUS_EXTENSION: `${existingAgentDir ?? '/tmp/default-omp-agent'}/extensions/orca-agent-status.ts`
|
||||
}
|
||||
}
|
||||
if (!existingAgentDir && options?.materializeDefaultHome === false) {
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
ORCA_PI_SOURCE_AGENT_DIR: existingAgentDir ?? '/tmp/default-pi-agent'
|
||||
}
|
||||
}
|
||||
)
|
||||
isPwshAvailableMock.mockReturnValue(false)
|
||||
spawnMock.mockReturnValue({
|
||||
@@ -2440,21 +2424,15 @@ describe('registerPtyHandlers', () => {
|
||||
|
||||
it('installs Pi managed extensions without redirecting Orca terminal PTY homes', async () => {
|
||||
const env = await spawnAndGetEnv(undefined, { PI_CODING_AGENT_DIR: '/tmp/user-pi-agent' })
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/tmp/user-pi-agent',
|
||||
'pi',
|
||||
{ materializeDefaultHome: false }
|
||||
)
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), '/tmp/user-pi-agent', 'pi')
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp')
|
||||
expect(env.PI_CODING_AGENT_DIR).toBe('/tmp/user-pi-agent')
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/tmp/user-pi-agent')
|
||||
expect(env.ORCA_OMP_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_OMP_STATUS_EXTENSION).toBe(expectedOmpStatusExtension)
|
||||
expect(env.ORCA_OMP_SOURCE_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_OMP_STATUS_EXTENSION).toBe(
|
||||
'/tmp/default-omp-agent/extensions/orca-agent-status.ts'
|
||||
)
|
||||
})
|
||||
|
||||
it('threads command: "omp" through to piBuildPtyEnv and emits OMP status metadata', async () => {
|
||||
@@ -2469,8 +2447,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/tmp/user-omp-agent',
|
||||
'omp',
|
||||
{ materializeDefaultHome: true }
|
||||
'omp'
|
||||
)
|
||||
expect(env.PI_CODING_AGENT_DIR).toBe('/tmp/user-omp-agent')
|
||||
expect(env.ORCA_OMP_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -2498,8 +2475,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/tmp/user-omp-agent',
|
||||
'omp',
|
||||
{ materializeDefaultHome: true }
|
||||
'omp'
|
||||
)
|
||||
expect(env.ORCA_OMP_STATUS_EXTENSION).toBe(
|
||||
'/tmp/user-omp-agent/extensions/orca-agent-status.ts'
|
||||
@@ -2512,12 +2488,7 @@ describe('registerPtyHandlers', () => {
|
||||
PI_CODING_AGENT_DIR: '/tmp/parent-orca-pi-overlay',
|
||||
ORCA_PI_SOURCE_AGENT_DIR: '/tmp/user-pi-agent'
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/tmp/user-pi-agent',
|
||||
'pi',
|
||||
{ materializeDefaultHome: false }
|
||||
)
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), '/tmp/user-pi-agent', 'pi')
|
||||
expect(env.PI_CODING_AGENT_DIR).toBe('/tmp/parent-orca-pi-overlay')
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/tmp/user-pi-agent')
|
||||
@@ -2536,9 +2507,7 @@ describe('registerPtyHandlers', () => {
|
||||
'omp'
|
||||
)
|
||||
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp', {
|
||||
materializeDefaultHome: true
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp')
|
||||
expect(env.ORCA_OMP_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_OMP_SOURCE_AGENT_DIR).toBe('/tmp/default-omp-agent')
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -2558,9 +2527,7 @@ describe('registerPtyHandlers', () => {
|
||||
'pi'
|
||||
)
|
||||
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'pi', {
|
||||
materializeDefaultHome: true
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'pi')
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/tmp/default-pi-agent')
|
||||
expect(env.ORCA_OMP_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -2602,8 +2569,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/home/tester/.config/pi-agent',
|
||||
'pi',
|
||||
{ materializeDefaultHome: false }
|
||||
'pi'
|
||||
)
|
||||
expect(env.PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -3005,15 +2971,8 @@ describe('registerPtyHandlers', () => {
|
||||
|
||||
it('installs Pi managed extensions without redirecting homes on the daemon path', async () => {
|
||||
const env = await daemonSpawnAndGetEnv({ PI_CODING_AGENT_DIR: '/user/.pi/agent' })
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/user/.pi/agent',
|
||||
'pi',
|
||||
{ materializeDefaultHome: false }
|
||||
)
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), '/user/.pi/agent', 'pi')
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(expect.any(String), undefined, 'omp')
|
||||
expect(env.PI_CODING_AGENT_DIR).toBe('/user/.pi/agent')
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_SOURCE_AGENT_DIR).toBe('/user/.pi/agent')
|
||||
@@ -3033,8 +2992,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/user/.omp/agent',
|
||||
'omp',
|
||||
{ materializeDefaultHome: true }
|
||||
'omp'
|
||||
)
|
||||
expect(env.PI_CODING_AGENT_DIR).toBe('/user/.omp/agent')
|
||||
expect(env.ORCA_OMP_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -3061,8 +3019,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/user/.omp/agent',
|
||||
'omp',
|
||||
{ materializeDefaultHome: true }
|
||||
'omp'
|
||||
)
|
||||
expect(env.ORCA_OMP_STATUS_EXTENSION).toBe(
|
||||
'/user/.omp/agent/extensions/orca-agent-status.ts'
|
||||
@@ -4003,9 +3960,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(sessionId).toEqual(expect.any(String))
|
||||
expect((sessionId ?? '').length).toBeGreaterThan(0)
|
||||
expect(spawnOpts.isNewSession).toBe(true)
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(sessionId, undefined, 'pi', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(sessionId, undefined, 'pi')
|
||||
})
|
||||
|
||||
it('respects a caller-provided sessionId instead of minting a new one', async () => {
|
||||
@@ -4020,9 +3975,7 @@ describe('registerPtyHandlers', () => {
|
||||
})
|
||||
expect(daemonSpawn.mock.calls.at(-1)![0].sessionId).toBe('user-session-42')
|
||||
expect(daemonSpawn.mock.calls.at(-1)![0].isNewSession).toBeUndefined()
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith('user-session-42', undefined, 'pi', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith('user-session-42', undefined, 'pi')
|
||||
})
|
||||
|
||||
it('prefixes a minted sessionId with the worktreeId when provided', async () => {
|
||||
@@ -4038,9 +3991,7 @@ describe('registerPtyHandlers', () => {
|
||||
})
|
||||
const sessionId = daemonSpawn.mock.calls.at(-1)![0].sessionId ?? ''
|
||||
expect(sessionId).toMatch(/^wt-alpha@@[0-9a-f]{8}$/)
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(sessionId, undefined, 'pi', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(sessionId, undefined, 'pi')
|
||||
})
|
||||
|
||||
it('reuses one attach-style daemon session for fresh-agent operation retries', async () => {
|
||||
@@ -4119,8 +4070,7 @@ describe('registerPtyHandlers', () => {
|
||||
expect(piBuildPtyEnvMock).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
'/ambient/pi/agent',
|
||||
'pi',
|
||||
{ materializeDefaultHome: false }
|
||||
'pi'
|
||||
)
|
||||
expect(env.PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
expect(env.ORCA_PI_CODING_AGENT_DIR).toBeUndefined()
|
||||
@@ -8906,13 +8856,11 @@ describe('registerPtyHandlers', () => {
|
||||
'ORCA_CLI_COMMAND/u',
|
||||
'ORCA_AGENT_HOOK_PORT/u',
|
||||
'ORCA_AGENT_HOOK_TOKEN/u',
|
||||
'ORCA_OMP_SOURCE_AGENT_DIR/p',
|
||||
'ORCA_OMP_STATUS_EXTENSION/p',
|
||||
'POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD'
|
||||
])
|
||||
)
|
||||
expect(env.WSLENV?.split(':')).not.toEqual(
|
||||
expect.arrayContaining(['ORCA_OMP_SOURCE_AGENT_DIR/p'])
|
||||
)
|
||||
})
|
||||
|
||||
it('forces managed ORCA_USER_DATA_PATH for WSL spawns even when the caller provides a stale root', async () => {
|
||||
|
||||
+2
-6
@@ -1166,17 +1166,13 @@ export function buildPtyHostEnv(
|
||||
clearPiAgentShadowEnv(baseEnv, 'pi')
|
||||
clearPiAgentShadowEnv(baseEnv, 'omp')
|
||||
if (piAgentKind === 'pi') {
|
||||
const piEnv = piTitlebarExtensionService.buildPtyEnv(id, preexistingPiAgentDir, 'pi', {
|
||||
materializeDefaultHome: hasLaunchCommand
|
||||
})
|
||||
const piEnv = piTitlebarExtensionService.buildPtyEnv(id, preexistingPiAgentDir, 'pi')
|
||||
Object.assign(baseEnv, piEnv)
|
||||
exposePiManagedExtensionEnv(baseEnv, 'pi', piEnv)
|
||||
}
|
||||
|
||||
if (shouldPrepareOmpShadow) {
|
||||
const ompEnv = piTitlebarExtensionService.buildPtyEnv(id, preexistingOmpAgentDir, 'omp', {
|
||||
materializeDefaultHome: piAgentKind === 'omp'
|
||||
})
|
||||
const ompEnv = piTitlebarExtensionService.buildPtyEnv(id, preexistingOmpAgentDir, 'omp')
|
||||
Object.assign(baseEnv, ompEnv)
|
||||
exposePiManagedExtensionEnv(baseEnv, 'omp', ompEnv)
|
||||
}
|
||||
|
||||
@@ -643,35 +643,6 @@ describe('PiTitlebarExtensionService', () => {
|
||||
rmSync(fakeHome, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('bare-shell prep does not create missing ~/.pi or ~/.omp homes (#10196)', () => {
|
||||
const fakeHome = mkdtempSync(join(tmpdir(), 'orca-no-eager-agent-home-'))
|
||||
expect(existsSync(join(fakeHome, '.pi'))).toBe(false)
|
||||
expect(existsSync(join(fakeHome, '.omp'))).toBe(false)
|
||||
|
||||
homedirOverride.current = fakeHome
|
||||
try {
|
||||
const svc = new PiTitlebarExtensionService()
|
||||
const piEnv = svc.buildPtyEnv('pty-bare-pi', undefined, 'pi', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
const ompEnv = svc.buildPtyEnv('pty-bare-omp', undefined, 'omp', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
|
||||
expect(piEnv).toEqual({})
|
||||
expect(existsSync(join(fakeHome, '.pi'))).toBe(false)
|
||||
expect(existsSync(join(fakeHome, '.omp'))).toBe(false)
|
||||
expect(ompEnv.ORCA_OMP_SOURCE_AGENT_DIR).toBeUndefined()
|
||||
expect(ompEnv.ORCA_OMP_STATUS_EXTENSION).toEqual(
|
||||
expect.stringContaining('omp-managed-status-extension')
|
||||
)
|
||||
expect(existsSync(ompEnv.ORCA_OMP_STATUS_EXTENSION!)).toBe(true)
|
||||
} finally {
|
||||
homedirOverride.current = ''
|
||||
rmSync(fakeHome, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('isSafeDescendCandidate (Windows junction regression guard)', () => {
|
||||
|
||||
@@ -169,8 +169,7 @@ export class PiTitlebarExtensionService {
|
||||
buildPtyEnv(
|
||||
ptyId: string,
|
||||
existingAgentDir: string | undefined,
|
||||
kind: PiAgentKind,
|
||||
options?: { materializeDefaultHome?: boolean }
|
||||
kind: PiAgentKind
|
||||
): Record<string, string> {
|
||||
const sourceAgentDir = existingAgentDir || getDefaultPiAgentDir(kind)
|
||||
try {
|
||||
@@ -181,15 +180,6 @@ export class PiTitlebarExtensionService {
|
||||
// directory should not prevent the terminal from starting.
|
||||
}
|
||||
|
||||
if (!existsSync(sourceAgentDir) && options?.materializeDefaultHome === false) {
|
||||
if (kind === 'omp') {
|
||||
const statusSource = withOrcaManagedExtensionMarker(getPiAgentStatusExtensionSource(kind))
|
||||
const statusExtensionPath = this.writeOmpFallbackStatusExtension(statusSource)
|
||||
return statusExtensionPath ? { ORCA_OMP_STATUS_EXTENSION: statusExtensionPath } : {}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
if (kind === 'omp') {
|
||||
migrateLegacyOmpOverlayState(sourceAgentDir, this.getSourceOverlayDir(sourceAgentDir, 'omp'))
|
||||
}
|
||||
|
||||
@@ -72,10 +72,9 @@ describe('PluginOverlayManager', () => {
|
||||
|
||||
it('installs Pi extension into the real agent extensions dir', () => {
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-2:0')
|
||||
expect(result?.sourceAgentDir).toBeDefined()
|
||||
const file = join(result!.sourceAgentDir!, 'extensions', 'orca-agent-status.ts')
|
||||
expect(result?.statusExtensionPath).toBe(file)
|
||||
const dir = manager.materializePi('tab-2:0')
|
||||
expect(dir).not.toBeNull()
|
||||
const file = join(dir!, 'extensions', 'orca-agent-status.ts')
|
||||
expect(existsSync(file)).toBe(true)
|
||||
expect(readFileSync(file, 'utf8')).toContain('@orca-managed-pi-extension')
|
||||
})
|
||||
@@ -97,17 +96,17 @@ describe('PluginOverlayManager', () => {
|
||||
ompExtensionSource: '// omp extension'
|
||||
})
|
||||
|
||||
const piResult = manager.materializePi('tab-kind-pi:0', undefined, 'pi')
|
||||
const ompResult = manager.materializePi('tab-kind-omp:0', undefined, 'omp')
|
||||
const piDir = manager.materializePi('tab-kind-pi:0', undefined, 'pi')
|
||||
const ompDir = manager.materializePi('tab-kind-omp:0', undefined, 'omp')
|
||||
|
||||
expect(piResult?.sourceAgentDir).toBeDefined()
|
||||
expect(ompResult?.sourceAgentDir).toBeDefined()
|
||||
expect(
|
||||
readFileSync(join(piResult!.sourceAgentDir!, 'extensions', 'orca-agent-status.ts'), 'utf8')
|
||||
).toContain('// pi extension')
|
||||
expect(
|
||||
readFileSync(join(ompResult!.sourceAgentDir!, 'extensions', 'orca-agent-status.ts'), 'utf8')
|
||||
).toContain('// omp extension')
|
||||
expect(piDir).not.toBeNull()
|
||||
expect(ompDir).not.toBeNull()
|
||||
expect(readFileSync(join(piDir!, 'extensions', 'orca-agent-status.ts'), 'utf8')).toContain(
|
||||
'// pi extension'
|
||||
)
|
||||
expect(readFileSync(join(ompDir!, 'extensions', 'orca-agent-status.ts'), 'utf8')).toContain(
|
||||
'// omp extension'
|
||||
)
|
||||
})
|
||||
|
||||
it('installs Orca status extension into the remote default Pi agent dir', () => {
|
||||
@@ -130,10 +129,9 @@ describe('PluginOverlayManager', () => {
|
||||
)
|
||||
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-pi:0')
|
||||
const dir = result?.sourceAgentDir
|
||||
const dir = manager.materializePi('tab-pi:0')
|
||||
|
||||
expect(dir).toBeDefined()
|
||||
expect(dir).not.toBeNull()
|
||||
expect(readFileSync(join(dir!, 'auth.json'), 'utf8')).toBe('secret token')
|
||||
expect(readFileSync(join(dir!, 'skills', 'my-skill', 'SKILL.md'), 'utf8')).toBe(
|
||||
'critical user skill'
|
||||
@@ -173,10 +171,9 @@ describe('PluginOverlayManager', () => {
|
||||
writeFileSync(join(customAgentDir, 'extensions', 'custom.ts'), 'custom extension')
|
||||
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-custom-pi:0', customAgentDir)
|
||||
const dir = result?.sourceAgentDir
|
||||
const dir = manager.materializePi('tab-custom-pi:0', customAgentDir)
|
||||
|
||||
expect(dir).toBeDefined()
|
||||
expect(dir).not.toBeNull()
|
||||
expect(readFileSync(join(dir!, 'auth.json'), 'utf8')).toBe('custom token')
|
||||
expect(readFileSync(join(dir!, 'extensions', 'custom.ts'), 'utf8')).toBe('custom extension')
|
||||
expect(readFileSync(join(dir!, 'extensions', 'orca-agent-status.ts'), 'utf8')).toContain(
|
||||
@@ -187,9 +184,10 @@ describe('PluginOverlayManager', () => {
|
||||
it('leaves lazy OMP agent.db in the real remote home on the relay', () => {
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const sourceDir = join(homeDir, '.omp', 'agent')
|
||||
const first = manager.materializePi('tab-relay-omp-sqlite:0', undefined, 'omp')
|
||||
const firstDir = manager.materializePi('tab-relay-omp-sqlite:0', undefined, 'omp')
|
||||
|
||||
expect(first?.sourceAgentDir).toBe(sourceDir)
|
||||
expect(firstDir).not.toBeNull()
|
||||
expect(firstDir).toBe(sourceDir)
|
||||
const sourcePath = join(sourceDir, 'agent.db')
|
||||
const content = 'agent.db relay credentials'
|
||||
|
||||
@@ -200,10 +198,10 @@ describe('PluginOverlayManager', () => {
|
||||
|
||||
expect(readFileSync(sourcePath, 'utf8')).toBe(content)
|
||||
|
||||
const second = manager.materializePi('tab-relay-omp-sqlite:0', undefined, 'omp')
|
||||
const secondDir = manager.materializePi('tab-relay-omp-sqlite:0', undefined, 'omp')
|
||||
|
||||
expect(second?.sourceAgentDir).toBe(first?.sourceAgentDir)
|
||||
expect(readFileSync(join(second!.sourceAgentDir!, 'agent.db'), 'utf8')).toBe(content)
|
||||
expect(secondDir).toBe(firstDir)
|
||||
expect(readFileSync(join(secondDir!, 'agent.db'), 'utf8')).toBe(content)
|
||||
})
|
||||
|
||||
// Why: per-agent source dir. The renderer picks Pi or OMP per
|
||||
@@ -224,9 +222,9 @@ describe('PluginOverlayManager', () => {
|
||||
seedAgentDir('.omp', 'omp')
|
||||
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-relay-pi-both:0', undefined, 'pi')
|
||||
const dir = result?.sourceAgentDir
|
||||
const dir = manager.materializePi('tab-relay-pi-both:0', undefined, 'pi')
|
||||
|
||||
expect(dir).not.toBeNull()
|
||||
expect(dir).toBe(join(homeDir, '.pi', 'agent'))
|
||||
expect(readFileSync(join(dir!, 'auth.json'), 'utf8')).toBe('pi token')
|
||||
const extensions = readdirSync(join(dir!, 'extensions')).sort()
|
||||
@@ -240,9 +238,9 @@ describe('PluginOverlayManager', () => {
|
||||
seedAgentDir('.omp', 'omp')
|
||||
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-relay-omp-both:0', undefined, 'omp')
|
||||
const dir = result?.sourceAgentDir
|
||||
const dir = manager.materializePi('tab-relay-omp-both:0', undefined, 'omp')
|
||||
|
||||
expect(dir).not.toBeNull()
|
||||
expect(dir).toBe(join(homeDir, '.omp', 'agent'))
|
||||
// Even though ~/.pi/agent exists, the OMP launch MUST mirror OMP's
|
||||
// source dir. Cross-agent fallback would silently shadow the user's
|
||||
@@ -261,41 +259,15 @@ describe('PluginOverlayManager', () => {
|
||||
expect(existsSync(join(homeDir, '.omp'))).toBe(false)
|
||||
|
||||
manager.setSources({ piExtensionSource: '// pi extension' })
|
||||
const result = manager.materializePi('tab-relay-omp-empty:0', undefined, 'omp')
|
||||
const dir = result?.sourceAgentDir
|
||||
const dir = manager.materializePi('tab-relay-omp-empty:0', undefined, 'omp')
|
||||
|
||||
expect(dir).not.toBeNull()
|
||||
expect(dir).toBe(join(homeDir, '.omp', 'agent'))
|
||||
// Pi-only home must NOT leak into the OMP home.
|
||||
expect(existsSync(join(dir!, 'auth.json'))).toBe(false)
|
||||
const extensions = readdirSync(join(dir!, 'extensions')).sort()
|
||||
expect(extensions).toEqual(['orca-agent-status.ts'])
|
||||
})
|
||||
|
||||
it('bare-shell prep does not create missing remote agent homes (#10196)', () => {
|
||||
expect(existsSync(join(homeDir, '.pi'))).toBe(false)
|
||||
expect(existsSync(join(homeDir, '.omp'))).toBe(false)
|
||||
manager.setSources({
|
||||
piExtensionSource: '// pi extension',
|
||||
ompExtensionSource: '// omp extension'
|
||||
})
|
||||
|
||||
expect(
|
||||
manager.materializePi('tab-bare-pi:0', undefined, 'pi', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
).toBeNull()
|
||||
const bareOmp = manager.materializePi('tab-bare-omp:0', undefined, 'omp', {
|
||||
materializeDefaultHome: false
|
||||
})
|
||||
expect(bareOmp?.sourceAgentDir).toBeUndefined()
|
||||
expect(bareOmp?.statusExtensionPath).toEqual(
|
||||
expect.stringContaining(join('.orca-relay', 'omp-managed-status-extension'))
|
||||
)
|
||||
expect(existsSync(bareOmp!.statusExtensionPath!)).toBe(true)
|
||||
expect(readFileSync(bareOmp!.statusExtensionPath!, 'utf8')).toContain('// omp extension')
|
||||
expect(existsSync(join(homeDir, '.pi'))).toBe(false)
|
||||
expect(existsSync(join(homeDir, '.omp'))).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not override a missing preexisting Pi agent dir', () => {
|
||||
@@ -311,8 +283,8 @@ describe('PluginOverlayManager', () => {
|
||||
ompExtensionSource: 'omp'
|
||||
})
|
||||
const opencodeDir = manager.materializeOpenCode('tab-3:0')!
|
||||
const piDir = manager.materializePi('tab-3:0', undefined, 'pi')!.sourceAgentDir!
|
||||
const ompDir = manager.materializePi('tab-3:0', undefined, 'omp')!.sourceAgentDir!
|
||||
const piDir = manager.materializePi('tab-3:0', undefined, 'pi')!
|
||||
const ompDir = manager.materializePi('tab-3:0', undefined, 'omp')!
|
||||
expect(piDir).not.toBe(ompDir)
|
||||
expect(existsSync(opencodeDir)).toBe(true)
|
||||
expect(existsSync(piDir)).toBe(true)
|
||||
|
||||
@@ -41,7 +41,6 @@ const PI_OVERLAY_SUBDIR_BY_KIND: Record<PiAgentKind, string> = {
|
||||
const OPENCODE_PLUGIN_FILE = 'orca-opencode-status.js'
|
||||
const PI_EXTENSION_FILE = 'orca-agent-status.ts'
|
||||
const PI_AGENT_SUBDIR = 'agent'
|
||||
const OMP_MANAGED_STATUS_EXTENSION_DIR = 'omp-managed-status-extension'
|
||||
const ORCA_MANAGED_EXTENSION_MARKER = '@orca-managed-pi-extension'
|
||||
|
||||
function withOrcaManagedPiExtensionMarker(source: string): string {
|
||||
@@ -80,9 +79,8 @@ export type PluginSources = {
|
||||
ompExtensionSource?: string
|
||||
}
|
||||
|
||||
export type MaterializePiResult = {
|
||||
sourceAgentDir?: string
|
||||
statusExtensionPath?: string
|
||||
export function getRelayPiStatusExtensionPath(agentDir: string): string {
|
||||
return join(agentDir, 'extensions', PI_EXTENSION_FILE)
|
||||
}
|
||||
|
||||
/** Presence of this file is what makes an overlay usable — a rebuild that failed
|
||||
@@ -238,30 +236,10 @@ export class PluginOverlayManager {
|
||||
}
|
||||
}
|
||||
|
||||
private writeOmpManagedStatusExtension(extensionSource: string): string | null {
|
||||
const fallbackDir = join(this.homeDir, RELAY_HOOKS_DIR, OMP_MANAGED_STATUS_EXTENSION_DIR)
|
||||
try {
|
||||
mkdirSync(fallbackDir, { recursive: true })
|
||||
const fallbackPath = join(fallbackDir, PI_EXTENSION_FILE)
|
||||
if (!this.canOverwritePiExtension(fallbackPath)) {
|
||||
return null
|
||||
}
|
||||
writeFileSync(fallbackPath, extensionSource)
|
||||
return fallbackPath
|
||||
} catch (err) {
|
||||
process.stderr.write(
|
||||
`[plugin-overlay] failed to write OMP managed status extension: ${err instanceof Error ? err.message : String(err)}\n`
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
materializePi(
|
||||
id: string,
|
||||
existingAgentDir?: string,
|
||||
kind: PiAgentKind = 'pi',
|
||||
options?: { materializeDefaultHome?: boolean }
|
||||
): MaterializePiResult | null {
|
||||
/** Install the Pi/OMP status extension into the remote real agent dir and
|
||||
* return that directory. `kind` selects which Pi-compatible agent's default
|
||||
* dir to use when `existingAgentDir` is not supplied. */
|
||||
materializePi(id: string, existingAgentDir?: string, kind: PiAgentKind = 'pi'): string | null {
|
||||
const extensionSource = this.getPiExtensionSource(kind)
|
||||
if (!extensionSource || !isUsableId(id)) {
|
||||
return null
|
||||
@@ -271,17 +249,6 @@ export class PluginOverlayManager {
|
||||
if (existingAgentDir && !existsSync(existingAgentDir)) {
|
||||
return null
|
||||
}
|
||||
if (
|
||||
!existingAgentDir &&
|
||||
!existsSync(sourceAgentDir) &&
|
||||
options?.materializeDefaultHome === false
|
||||
) {
|
||||
if (kind === 'omp') {
|
||||
const statusExtensionPath = this.writeOmpManagedStatusExtension(extensionSource)
|
||||
return statusExtensionPath ? { statusExtensionPath } : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
const extensionsDir = join(sourceAgentDir, 'extensions')
|
||||
mkdirSync(extensionsDir, { recursive: true })
|
||||
const extensionPath = join(extensionsDir, PI_EXTENSION_FILE)
|
||||
@@ -289,10 +256,7 @@ export class PluginOverlayManager {
|
||||
return null
|
||||
}
|
||||
writeFileSync(extensionPath, extensionSource)
|
||||
return {
|
||||
sourceAgentDir,
|
||||
statusExtensionPath: extensionPath
|
||||
}
|
||||
return sourceAgentDir
|
||||
} catch (err) {
|
||||
process.stderr.write(
|
||||
`[plugin-overlay] failed to install ${kind} extension: ${err instanceof Error ? err.message : String(err)}\n`
|
||||
|
||||
+8
-14
@@ -33,7 +33,7 @@ import { PortScanHandler } from './port-scan-handler'
|
||||
import { AgentExecHandler } from './agent-exec-handler'
|
||||
import { WorkspaceSessionHandler } from './workspace-session-handler'
|
||||
import { endpointDirForRelaySocket, RelayAgentHookServer } from './agent-hook-server'
|
||||
import { PluginOverlayManager } from './plugin-overlay'
|
||||
import { PluginOverlayManager, getRelayPiStatusExtensionPath } from './plugin-overlay'
|
||||
import {
|
||||
AGENT_HOOK_INSTALL_PLUGINS_METHOD,
|
||||
AGENT_HOOK_NOTIFICATION_METHOD,
|
||||
@@ -585,11 +585,9 @@ async function main(): Promise<void> {
|
||||
const shouldPrepareOmpShadow = kind === 'omp' || !hasLaunchCommand
|
||||
if (kind === 'pi') {
|
||||
const sourceDir = resolvePiSourceAgentDir(ctx.env, ctx.shell, 'pi')
|
||||
const result = pluginOverlay.materializePi(overlayId, sourceDir, 'pi', {
|
||||
materializeDefaultHome: hasLaunchCommand
|
||||
})
|
||||
if (result?.sourceAgentDir) {
|
||||
env.ORCA_PI_SOURCE_AGENT_DIR = result.sourceAgentDir
|
||||
const dir = pluginOverlay.materializePi(overlayId, sourceDir, 'pi')
|
||||
if (dir) {
|
||||
env.ORCA_PI_SOURCE_AGENT_DIR = dir
|
||||
}
|
||||
}
|
||||
if (shouldPrepareOmpShadow) {
|
||||
@@ -598,14 +596,10 @@ async function main(): Promise<void> {
|
||||
kind === 'omp'
|
||||
? resolvePiSourceAgentDir(ctx.env, ctx.shell, 'omp')
|
||||
: ctx.env.ORCA_OMP_SOURCE_AGENT_DIR
|
||||
const result = pluginOverlay.materializePi(overlayId, sourceDir, 'omp', {
|
||||
materializeDefaultHome: kind === 'omp'
|
||||
})
|
||||
if (result?.statusExtensionPath) {
|
||||
env.ORCA_OMP_STATUS_EXTENSION = result.statusExtensionPath
|
||||
}
|
||||
if (result?.sourceAgentDir) {
|
||||
env.ORCA_OMP_SOURCE_AGENT_DIR = result.sourceAgentDir
|
||||
const dir = pluginOverlay.materializePi(overlayId, sourceDir, 'omp')
|
||||
if (dir) {
|
||||
env.ORCA_OMP_STATUS_EXTENSION = getRelayPiStatusExtensionPath(dir)
|
||||
env.ORCA_OMP_SOURCE_AGENT_DIR = dir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user