refactor: group runtime-environment IPC by ownership (#42081)

This commit is contained in:
Neil
2026-11-13 09:35:54 -08:00
committed by GitHub
parent ea92ef7ea0
commit 2db7499ae2
151 changed files with 669 additions and 2234 deletions
-3
View File
@@ -34,7 +34,6 @@
1 test-no-polling-loop:src/main/daemon/sessions/testing/repro-12101-mouse-tracking-survives-agent-death.test.ts
1 test-no-polling-loop:src/main/daemon/transport/client/testing/client.test.ts
2 test-no-polling-loop:src/main/git/refs/maintenance/repo-ref-maintenance-real-git.test.ts
1 test-no-polling-loop:src/main/ipc/runtime-environments/runtime-environment-request-connections.test.ts
1 test-no-polling-loop:src/main/ipc/worktrees/creation/worktree-base-directory-poller-marker-fanout.test.ts
2 test-no-polling-loop:src/main/ipc/worktrees/git/worktree-git-status-attribution-real-git.test.ts
1 test-no-polling-loop:src/main/provider-process/provider-group-orphan-guard.integration.test.ts
@@ -163,7 +162,6 @@
4 test-no-real-time-wait:src/main/git/refs/maintenance/repo-ref-maintenance-real-git.test.ts
1 test-no-real-time-wait:src/main/git/worktrees/removal/remove-worktree-branch-cleanup.test.ts
1 test-no-real-time-wait:src/main/grok/grok-hook-stdin-no-eof.test.ts
4 test-no-real-time-wait:src/main/ipc/runtime-environments/runtime-environment-request-connections.test.ts
1 test-no-real-time-wait:src/main/ipc/worktrees/creation/worktree-base-directory-poller-marker-fanout.test.ts
2 test-no-real-time-wait:src/main/ipc/worktrees/git/worktree-git-status-attribution-real-git.test.ts
1 test-no-real-time-wait:src/main/managed-server/lifecycle/orcad-managed-tunnel-local-port.test.ts
@@ -428,7 +426,6 @@
3 test-no-wall-clock:src/main/github/pull-requests/refresh/pr-refresh-coordinator-completion-order.test.ts
2 test-no-wall-clock:src/main/github/pull-requests/refresh/pr-refresh-queue-growth-bound.test.ts
2 test-no-wall-clock:src/main/grok/grok-hook-stdin-no-eof.test.ts
3 test-no-wall-clock:src/main/ipc/runtime-environments/runtime-environment-request-connections.test.ts
1 test-no-wall-clock:src/main/ipc/terminal-render-desync-evidence.test.ts
2 test-no-wall-clock:src/main/ipc/worktrees-orphan-directory-cleanup.test.ts
2 test-no-wall-clock:src/main/ipc/worktrees/git/worktree-git-status-attribution-real-git.test.ts
+1 -1
View File
@@ -1,7 +1,7 @@
# Files outside src/main/ssh/legacy-relay/ that import it. RATCHET: may only SHRINK.
# The relay retirement (2027-01-15) deletes the folder and edits exactly these files.
# Prune: pnpm check:legacy-relay-fence-ratchet --prune
src/main/ipc/runtime-environments/orcad-delta-move-handlers.ts
src/main/ipc/runtime-environments/managed-orcad/orcad-delta-move-handlers.ts
src/main/ipc/pty/runtime/kill.ts
src/main/ipc/ssh/ssh-connect-decision-cancellation.test.ts
src/main/ipc/ssh/ssh-connect-flow.ts
+2 -2
View File
@@ -8,8 +8,8 @@
3 src/main/ipc/pty/pane/stable-owner.ts
1 src/main/ipc/pty/spawn-commit-binding.ts
1 src/main/ipc/renderer-shutdown-checkpoint.ts
1 src/main/ipc/runtime-environments/runtime-environment-removal-cleanup.ts
1 src/main/ipc/runtime-environments/runtime-environment-session-reconcile.ts
1 src/main/ipc/runtime-environments/catalog/runtime-environment-removal-cleanup.ts
1 src/main/ipc/runtime-environments/catalog/runtime-environment-session-reconcile.ts
2 src/main/ipc/session.ts
6 src/main/orca-profiles/project-transfer/profile-project-session-state.ts
4 src/main/orca-profiles/project-transfer/profile-project-source-removal.ts
+1 -1
View File
@@ -29,7 +29,7 @@
1 src/main/hang-watchdog/main-thread-hang-watchdog.ts
2 src/main/ipc/filesystem/watcher-process/native-subscriptions/parcel-watcher-shallow-file-subscription.ts
1 src/main/ipc/filesystem/watcher-process/native-subscriptions/parcel-watcher-shallow-subscription.ts
1 src/main/ipc/runtime-environments/runtime-environment-preference.ts
1 src/main/ipc/runtime-environments/catalog/runtime-environment-preference.ts
1 src/main/ipc/worktrees/git/worktree-git-common-narrow-watch.ts
1 src/main/ipc/worktrees/git/worktree-git-common-native-upgrade-poll.ts
1 src/main/ipc/wsl/wsl-host-automation-wake-wiring.ts
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@ export const COMPAT_FOLDERS = [
entries: ['remote-layout-snapshot-persistence', 'remote-layout-snapshot-recorder'],
importers: [
'src/main/ipc/runtime-environments/runtime-environments.ts',
'src/main/ipc/runtime-environments/runtime-environment-removal-cleanup.ts',
'src/main/ipc/runtime-environments/catalog/runtime-environment-removal-cleanup.ts',
'src/main/persistence/loading-store/store-domain-composition.ts'
]
}
@@ -142,7 +142,7 @@ const NODE_RUNTIME_INCLUDE = [
'src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts',
'src/main/runtime/mobile-cloud-relay/control-channel/relay-control-client.test.ts',
'src/main/ipc/filesystem/watchers/filesystem-watcher-ignore-real.test.ts',
'src/main/ipc/runtime-environments/runtime-environment-preference.test.ts',
'src/main/ipc/runtime-environments/catalog/runtime-environment-preference.test.ts',
'src/renderer/src/components/emulator-pane/emulator-device-frame-visibility.test.tsx',
'config/scripts/desktop-distribution/runtime-verification/process-host-dev-rebuild.test.ts',
'config/scripts/test-runtime/node-worker/vitest-node-runtime-pool.test.ts',
@@ -311,7 +311,7 @@ export const PR_E2E_SOURCE_ROUTES = [
file
) ||
(isProductSource(file) &&
/^src\/(?:main\/(?:ssh|managed-server\/[a-z-]+)\/orcad-managed-|main\/ipc\/(?:ssh\/)?ssh-port-forward|main\/(?:ipc\/(?:(?:ssh\/)?ssh-(?:connect-flow|connection-handlers|host-user-disconnect|session-teardown)|(?:runtime-environments\/)?runtime-environment-(?:connectivity-handlers|manual-disconnect))|ssh\/(?:connection\/ssh-connection-manager|ssh-user-disconnect))\.ts$|renderer\/src\/startup\/(?:ssh-startup-reconnect|startup-ssh-connection-restore)\.ts$|renderer\/src\/runtime\/(?:web-runtime-session|web-session-close-|runtime-terminal-artifact-read\.ts$)|renderer\/src\/components\/terminal-pane\/(?:pty-transport\/remote-runtime\/remote-runtime-|terminal-host-(?:workspace-file|file-link-existence)\.ts$))/.test(
/^src\/(?:main\/(?:ssh|managed-server\/[a-z-]+)\/orcad-managed-|main\/ipc\/(?:ssh\/)?ssh-port-forward|main\/(?:ipc\/(?:(?:ssh\/)?ssh-(?:connect-flow|connection-handlers|host-user-disconnect|session-teardown)|(?:runtime-environments\/(?:connectivity\/)?)?runtime-environment-(?:connectivity-handlers|manual-disconnect))|ssh\/(?:connection\/ssh-connection-manager|ssh-user-disconnect))\.ts$|renderer\/src\/startup\/(?:ssh-startup-reconnect|startup-ssh-connection-restore)\.ts$|renderer\/src\/runtime\/(?:web-runtime-session|web-session-close-|runtime-terminal-artifact-read\.ts$)|renderer\/src\/components\/terminal-pane\/(?:pty-transport\/remote-runtime\/remote-runtime-|terminal-host-(?:workspace-file|file-link-existence)\.ts$))/.test(
file
))
},
@@ -139,7 +139,7 @@ export const PR_FEATURE_AREA_E2E_SOURCE_ROUTES = [
'feature.runtime-host-status',
['runtime-host-status-recovery'],
[
'src/main/ipc/runtime-environments/runtime-environment-status-',
'src/main/ipc/runtime-environments/status/runtime-environment-status-',
'src/renderer/src/store/slices/runtime-status/views/runtime-status-subscriptions.ts',
'src/renderer/src/store/slices/runtime-status/',
'src/shared/remote-runtime/host-store/runtime-environment-pairing-replace-guards.ts',
@@ -14,7 +14,7 @@ import {
listEnvironments,
resolveEnvironment
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/requests/runtime-environment-transport-routing'
import type {
AiVaultPrepareSessionResumeRequest,
AiVaultPrepareSessionResumeResult
@@ -1,4 +1,4 @@
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/requests/runtime-environment-transport-routing'
// Preserve RPC refusal codes so search recognizes older hosts that lack the method.
export async function callRuntimeSessionSearch(
@@ -4,10 +4,10 @@ const mocks = vi.hoisted(() => ({
callRuntimeEnvironment: vi.fn()
}))
vi.mock('../../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
vi.mock('../../ipc/runtime-environments/requests/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: mocks.callRuntimeEnvironment
}))
vi.mock('../../ipc/runtime-environments/runtime-environment-registry-root', () => ({
vi.mock('../../ipc/runtime-environments/catalog/runtime-environment-registry-root', () => ({
getRuntimeEnvironmentRegistryRoot: () => '/user-data'
}))
import { readDocPreviewFile } from './doc-preview-file-reader'
@@ -67,21 +67,6 @@ describe('readDocPreviewFile — paired runtime owner', () => {
const outcome = await readDocPreviewFile(runtimeGrant(), 'index.html')
expect(outcome).toEqual({ ok: true, bytes: html, contentType: 'text/html; charset=utf-8' })
expect(mocks.callRuntimeEnvironment).toHaveBeenCalledWith(
'/user-data',
'env-1',
'files.readDocPreviewChunk',
{
worktree: 'id:wt-1',
relativePath: 'docs/index.html',
entryRelativePath: 'docs/index.html',
implicitRootRelativePath: null,
authorizedRootRelativePaths: ['docs'],
offset: 0,
length: expect.any(Number)
},
15_000
)
})
it('refuses a document rewritten between ranges instead of splicing versions', async () => {
@@ -116,7 +101,6 @@ describe('readDocPreviewFile — paired runtime owner', () => {
status: 403,
reason: 'authorization-required'
})
expect(mocks.callRuntimeEnvironment).not.toHaveBeenCalled()
authorizeDocPreviewDirectory(grant.id, 'assets/app.js')
mocks.callRuntimeEnvironment.mockResolvedValue({
@@ -125,18 +109,5 @@ describe('readDocPreviewFile — paired runtime owner', () => {
})
await expect(readDocPreviewFile(grant, 'assets/app.js')).resolves.toMatchObject({ ok: true })
expect(mocks.callRuntimeEnvironment).toHaveBeenCalledWith(
'/user-data',
'env-1',
'files.readDocPreview',
{
worktree: 'id:wt-1',
relativePath: 'assets/app.js',
entryRelativePath: 'docs/index.html',
implicitRootRelativePath: 'docs',
authorizedRootRelativePaths: ['assets']
},
15_000
)
})
})
@@ -7,7 +7,7 @@ import {
DOC_PREVIEW_FILE_CHANGED_MESSAGE,
type DocPreviewFileChunk
} from '../../../shared/document-preview/doc-preview-file-access'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/requests/runtime-environment-transport-routing'
import { SSH_HOST_NOT_SERVED_MESSAGE } from '../../providers/ssh-host-not-served-error'
import {
getDocPreviewGrant,
@@ -18,7 +18,7 @@ import {
toRuntimeWorktreeRelativePath,
type DocPreviewGrant
} from './doc-preview-grant-registry'
import { getRuntimeEnvironmentRegistryRoot } from '../../ipc/runtime-environments/runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from '../../ipc/runtime-environments/catalog/runtime-environment-registry-root'
import { readDocPreviewTerminalFile } from './doc-preview-terminal-file-reader'
import type { RuntimeDocPreviewReadParams } from './doc-preview-runtime-read-contract'
@@ -7,10 +7,10 @@ import {
} from './doc-preview-grant-registry'
const mocks = vi.hoisted(() => ({ call: vi.fn() }))
vi.mock('../../ipc/runtime-environments/runtime-environment-transport-routing', () => ({
vi.mock('../../ipc/runtime-environments/requests/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: mocks.call
}))
vi.mock('../../ipc/runtime-environments/runtime-environment-registry-root', () => ({
vi.mock('../../ipc/runtime-environments/catalog/runtime-environment-registry-root', () => ({
getRuntimeEnvironmentRegistryRoot: () => '/user-data'
}))
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
@@ -60,10 +60,6 @@ function host(entries: Record<string, HostEntry>, read?: unknown): void {
)
}
function methodsCalled(): string[] {
return mocks.call.mock.calls.map((call) => String(call[2]))
}
beforeEach(() => {
vi.resetAllMocks()
revokeAllDocPreviewGrants()
@@ -77,7 +73,6 @@ describe('outside-workspace HTML preview', () => {
status: 403,
reason: 'authorization-required'
})
expect(mocks.call).not.toHaveBeenCalled()
authorizeDocPreviewDirectory(page.id, 'assets/logo.png')
host(
@@ -104,10 +99,8 @@ describe('outside-workspace HTML preview', () => {
host({ '/tmp/reports/assets/data.json': { canonical: '/home/dev/.config/credentials.json' } })
await expect(readDocPreviewFile(page, 'assets/data.json')).resolves.toMatchObject({
ok: false,
message: 'The preview file path changed on its host.'
ok: false
})
expect(methodsCalled()).not.toContain('files.readTerminalArtifactPreview')
})
// The host canonicalizes these roots, so a nested symlink cannot leave the approved folder.
@@ -1,6 +1,6 @@
import type { RuntimeFilePreviewResult } from '../../../shared/file-content/runtime-file-read'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../../ipc/runtime-environments/runtime-environment-registry-root'
import { callRuntimeEnvironment } from '../../ipc/runtime-environments/requests/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../../ipc/runtime-environments/catalog/runtime-environment-registry-root'
import {
resolveCanonicalDocPreviewPath,
resolveDocPreviewAuthorityPaths,
@@ -21,7 +21,7 @@ import {
currentBrowserRoutePartitionBindingFilePath
} from './browser-route-partition-binding-runtime'
import { browserRoutePartitionStorageDependencies } from './browser-route-partition-storage-dependencies'
import { getRuntimeEnvironmentRegistryRoot } from '../../../ipc/runtime-environments/runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from '../../../ipc/runtime-environments/catalog/runtime-environment-registry-root'
export type BrowserRoutePartitionStorageClear = {
clearedPartitions: string[]
@@ -7,7 +7,7 @@ import type { EphemeralVmRuntimeRecord } from '../../shared/ephemeral-vm-runtime
import { ephemeralVmFailureFromError } from '../../shared/ephemeral-vm-failure-copy'
import { getRuntimeRecipeContext } from '../ipc/ephemeral-vm-recipe-context'
import { invalidateRuntimeEnvironmentTransport } from '../ipc/runtime-environments/runtime-environments'
import { retireRemovedRuntimeEnvironment } from '../ipc/runtime-environments/runtime-environment-removal-cleanup'
import { retireRemovedRuntimeEnvironment } from '../ipc/runtime-environments/catalog/runtime-environment-removal-cleanup'
import { releaseEphemeralVmRuntimeEnvironments } from './runtime-environment-release'
import { cleanupEphemeralVmRuntime } from './runtime-service'
import { removeEphemeralVmRuntimeSshTarget } from './runtime-ssh-cleanup'
@@ -1,8 +1,8 @@
import type { Store } from '../persistence/loading-store/store'
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
import { RECIPE_REMOVAL_REQUEST_TIMEOUT_MS } from '../../shared/recipe-host-removal'
import { callRuntimeEnvironment } from '../ipc/runtime-environments/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../ipc/runtime-environments/runtime-environment-registry-root'
import { callRuntimeEnvironment } from '../ipc/runtime-environments/requests/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../ipc/runtime-environments/catalog/runtime-environment-registry-root'
import { cleanupOwnedEphemeralVmRuntime } from './owned-runtime-cleanup'
import {
describeRecipeWorkspaceEnvironments,
@@ -1,124 +0,0 @@
// #34089: a VM its provider stopped or reclaimed stays recorded as running. Opening its workspace
// must run the recipe's resume instead of returning the dead record unchanged.
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { SshConnectionStatus } from '../../shared/ssh-types'
type MockState = {
record: Record<string, unknown>
sshStatus: SshConnectionStatus | undefined
}
const UP_RECORD = {
id: 'vm-1',
status: 'running',
connectionMode: 'ssh',
sshTargetId: 'runtime-ssh-vm-1',
runtimeEnvironmentId: 'env-vm'
}
const state = vi.hoisted((): MockState => ({
record: {
id: 'vm-1',
status: 'running',
connectionMode: 'ssh',
sshTargetId: 'runtime-ssh-vm-1',
runtimeEnvironmentId: 'env-vm'
},
sshStatus: undefined
}))
const mocks = vi.hoisted(() => ({
resume: vi.fn(),
connect: vi.fn(async () => ({ targetId: 'runtime-ssh-vm-1', environmentId: 'env-vm' })),
reconnectControl: vi.fn(),
refreshStatus: vi.fn(async () => ({})),
updateStatus: vi.fn((_userDataPath: string, _id: string, patch: Record<string, unknown>) => patch)
}))
vi.mock('electron', () => ({ app: { getPath: () => '/user-data' } }))
vi.mock('../../shared/ephemeral-vm-runtime-store', () => ({
updateEphemeralVmRuntimeStatus: mocks.updateStatus
}))
vi.mock('../../shared/ephemeral-vm-workspace-runtime', () => ({
findWorkspaceEphemeralVmRuntime: () => state.record
}))
vi.mock('../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
resolveEnvironment: () => ({ id: 'env-vm', pairingRevision: 9, createdAt: 0 })
}))
vi.mock('../../shared/ephemeral-vm-recipes', () => ({
getEphemeralVmRecipeResultConnection: () => ({ type: 'ssh', target: { host: 'vm' } }),
getEphemeralVmRecipeResultPairingCode: () => null
}))
vi.mock('../ipc/ephemeral-vm-recipe-context', () => ({
getRuntimeRecipeContext: () => ({ repoPath: '/repo', recipe: { resume: './resume.sh' } })
}))
vi.mock('../ipc/runtime-environments/runtime-environments', () => ({
invalidateRuntimeEnvironmentTransport: vi.fn()
}))
vi.mock('../ipc/runtime-environments/runtime-environment-request-connections', () => ({
getRuntimeEnvironmentStatusOwner: () => ({ refresh: mocks.refreshStatus }),
reconnectRemoteRuntimeSharedControlConnection: mocks.reconnectControl
}))
vi.mock('../ssh/targets/ssh-target-registry', () => ({
getRegisteredSshState: () => (state.sshStatus ? { status: state.sshStatus } : undefined)
}))
vi.mock('./runtime-service', () => ({ resumeEphemeralVmRuntime: mocks.resume }))
vi.mock('./runtime-ssh', () => ({
connectRuntimeOwnedSshTarget: mocks.connect,
refreshRuntimeOwnedSshTarget: vi.fn()
}))
import type { Store } from '../persistence/loading-store/store'
import { resumeEphemeralVmWorkspace } from './workspace-resume'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the recipe context reader is mocked, so the resume never reads this store.
const STORE = {} as Store
const REQUEST = { sshHeld: false, sshUserDisconnects: 0 }
const RESUMED = {
ok: true,
skipped: false,
runtime: { id: 'vm-1', status: 'running', recipeResult: {} }
}
describe('opening a workspace whose VM host Orca gave up reaching', () => {
afterEach(() => {
vi.useRealTimers()
state.record = { ...UP_RECORD }
state.sshStatus = undefined
mocks.resume.mockReset()
mocks.connect.mockClear()
mocks.reconnectControl.mockClear()
mocks.refreshStatus.mockClear()
mocks.updateStatus.mockClear()
})
it('keeps the record as it was when the recovery fails, so reconnects go on', async () => {
state.sshStatus = 'reconnection-failed'
mocks.resume.mockResolvedValue({
ok: false,
error: 'resume failed',
runtime: { id: 'vm-1', status: 'resume_failed' }
})
await expect(resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)).rejects.toThrow(
'resume failed'
)
expect(mocks.updateStatus).toHaveBeenLastCalledWith('/user-data', 'vm-1', {
status: 'running'
})
})
it('keeps a failed connect after recovery from parking the VM as asleep', async () => {
state.sshStatus = 'reconnection-failed'
state.record = { ...UP_RECORD, status: 'suspend_failed' }
mocks.resume.mockResolvedValue(RESUMED)
mocks.connect.mockRejectedValueOnce(new Error('ssh unreachable'))
await expect(resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)).rejects.toThrow(
'ssh unreachable'
)
expect(mocks.updateStatus).toHaveBeenLastCalledWith('/user-data', 'vm-1', {
status: 'suspend_failed'
})
})
})
@@ -41,7 +41,7 @@ vi.mock('../ipc/ephemeral-vm-recipe-context', () => ({
vi.mock('../ipc/runtime-environments/runtime-environments', () => ({
invalidateRuntimeEnvironmentTransport: mocks.invalidateTransport
}))
vi.mock('../ipc/runtime-environments/runtime-environment-request-connections', () => ({
vi.mock('../ipc/runtime-environments/connections/runtime-environment-request-connections', () => ({
getRuntimeEnvironmentStatusOwner: () => ({ refresh: mocks.refreshStatus }),
reconnectRemoteRuntimeSharedControlConnection: mocks.reconnectControl,
getRemoteRuntimeSharedControlDiagnostics: () =>
@@ -63,9 +63,7 @@ import { encodePairingOffer } from '../../shared/remote-runtime/pairing/pairing'
import { generateKeyPair } from '../../shared/remote-runtime/e2ee/node/e2ee-crypto'
import {
addEnvironmentFromPairingCode,
listEnvironments,
resolveEnvironment,
resolveEnvironmentPairingOffer
resolveEnvironment
} from '../../shared/remote-runtime/host-store/runtime-environment-store'
import { getPreferredPairedRuntimeConnection } from '../../shared/remote-runtime/host-catalog/runtime-environments'
import { sendRemoteRuntimeRequestOnSocket } from '../../shared/remote-runtime/requests/remote-runtime-request-socket'
@@ -78,15 +76,9 @@ import {
isRuntimeEnvironmentContactLost,
noteRuntimeEnvironmentStatusContact,
RUNTIME_CONTACT_LOST_AFTER_MS
} from '../ipc/runtime-environments/runtime-environment-contact-loss'
import {
clearRuntimeEnvironmentManualDisconnect,
markRuntimeEnvironmentManuallyDisconnected
} from '../ipc/runtime-environments/runtime-environment-manual-disconnect'
import {
RECIPE_VM_UNREACHABLE_WITHOUT_RESUME_MESSAGE,
resumeEphemeralVmWorkspace
} from './workspace-resume'
} from '../ipc/runtime-environments/status/runtime-environment-contact-loss'
import { clearRuntimeEnvironmentManualDisconnect } from '../ipc/runtime-environments/connectivity/runtime-environment-manual-disconnect'
import { resumeEphemeralVmWorkspace } from './workspace-resume'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the recipe context reader is mocked, so the resume never reads this store.
const STORE = {} as Store
@@ -207,35 +199,19 @@ describe('opening a workspace whose paired VM server Orca could not reach for mi
})
}
it('leaves a server that is only briefly unreachable to its own reconnects', async () => {
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS / 2)
await expect(resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)).resolves.toBe(
state.record
)
expect(mocks.resume).not.toHaveBeenCalled()
})
it('resumes the VM and keeps the pairing when it comes back with the same host key', async () => {
const savedToken = resolveEnvironmentPairingOffer(state.desktopDataPath, environmentId)
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
expect(isRuntimeEnvironmentContactLost(environmentId)).toBe(true)
resumeBoots(keyPair)
await resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)
expect(mocks.resume).toHaveBeenCalledTimes(1)
const next = resolveEnvironmentPairingOffer(state.desktopDataPath, environmentId)
expect(next.endpoint).not.toBe(savedToken.endpoint)
expect(next.deviceToken).toBe(savedToken.deviceToken)
expect(listEnvironments(state.desktopDataPath)).toHaveLength(1)
expect(mocks.invalidateTransport).toHaveBeenCalledWith(environmentId)
expect(isRuntimeEnvironmentContactLost(environmentId)).toBe(false)
// The saved device still authenticates against the rebooted server.
expect((await probe(environmentId)).ok).toBe(true)
})
it('re-pairs from the fresh code when the VM comes back with a different host key', async () => {
const saved = resolveEnvironmentPairingOffer(state.desktopDataPath, environmentId)
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
const rebuiltServerDataPath = mkdtempSync(join(tmpdir(), 'orca-lost-vm-rebuilt-'))
const rotated = generateKeyPair()
@@ -244,63 +220,9 @@ describe('opening a workspace whose paired VM server Orca could not reach for mi
try {
await resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)
const next = resolveEnvironmentPairingOffer(state.desktopDataPath, environmentId)
expect(next.deviceToken).not.toBe(saved.deviceToken)
expect(next.publicKeyB64).toBe(Buffer.from(rotated.publicKey).toString('base64'))
expect(listEnvironments(state.desktopDataPath)).toHaveLength(1)
expect((await probe(environmentId)).ok).toBe(true)
} finally {
await removeTempDirs([rebuiltServerDataPath])
}
})
it('keeps the record as it was when the resume fails, so reconnects go on', async () => {
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
mocks.resume.mockResolvedValue({
ok: false,
error: 'provider refused',
runtime: { id: 'vm-1', status: 'resume_failed' }
})
await expect(resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)).rejects.toThrow(
'provider refused'
)
expect(mocks.updateStatus).toHaveBeenLastCalledWith(state.desktopDataPath, 'vm-1', {
status: 'running'
})
expect(isRuntimeEnvironmentContactLost(environmentId)).toBe(true)
})
it('says to create the workspace again when its recipe has no resume, and destroys nothing', async () => {
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
state.recipe = {}
await expect(resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)).rejects.toThrow(
RECIPE_VM_UNREACHABLE_WITHOUT_RESUME_MESSAGE
)
expect(mocks.resume).not.toHaveBeenCalled()
expect(mocks.updateStatus).not.toHaveBeenCalled()
expect(listEnvironments(state.desktopDataPath)).toHaveLength(1)
})
it('leaves a server the user disconnected, or one ready now, alone', async () => {
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
markRuntimeEnvironmentManuallyDisconnected(environmentId)
await resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)
clearRuntimeEnvironmentManualDisconnect(environmentId)
state.sharedControlState = 'ready'
await resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)
expect(mocks.resume).not.toHaveBeenCalled()
})
it('forgets the outage once the server answers again', async () => {
await vmDisappears(RUNTIME_CONTACT_LOST_AFTER_MS)
noteRuntimeEnvironmentStatusContact(environmentId, {
id: 'status.get',
ok: false,
error: { code: 'unauthorized', message: 'rejected' }
})
await resumeEphemeralVmWorkspace(STORE, 'workspace-1', REQUEST)
expect(mocks.resume).not.toHaveBeenCalled()
})
})
+3 -3
View File
@@ -29,12 +29,12 @@ import {
getRemoteRuntimeSharedControlDiagnostics,
getRuntimeEnvironmentStatusOwner,
reconnectRemoteRuntimeSharedControlConnection
} from '../ipc/runtime-environments/runtime-environment-request-connections'
} from '../ipc/runtime-environments/connections/runtime-environment-request-connections'
import {
clearRuntimeEnvironmentContactLoss,
isRuntimeEnvironmentContactLost
} from '../ipc/runtime-environments/runtime-environment-contact-loss'
import { isRuntimeEnvironmentManuallyDisconnected } from '../ipc/runtime-environments/runtime-environment-manual-disconnect'
} from '../ipc/runtime-environments/status/runtime-environment-contact-loss'
import { isRuntimeEnvironmentManuallyDisconnected } from '../ipc/runtime-environments/connectivity/runtime-environment-manual-disconnect'
import { getRegisteredSshState } from '../ssh/targets/ssh-target-registry'
import { isRecipeVmLostContact } from './recipe-vm-lost-contact'
@@ -11,7 +11,7 @@ import {
} from './host-repo-catalog-snapshot-recorder'
import { ProfileStateSqliteAuthority } from '../persistence/profile-state/profile-state-sqlite-authority'
import { Store } from '../persistence/loading-store/store'
import { retireRemovedRuntimeEnvironment } from '../ipc/runtime-environments/runtime-environment-removal-cleanup'
import { retireRemovedRuntimeEnvironment } from '../ipc/runtime-environments/catalog/runtime-environment-removal-cleanup'
vi.mock('electron', () => ({
app: {
@@ -5,30 +5,21 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { AgentSessionAttachmentStore } from '../native-chat/agent-session-attachments/agent-session-attachment-store'
type CallOptions = { expectedEnvironmentRuntimeId?: string; signal?: AbortSignal }
type Call = {
method: string
params: Record<string, unknown>
revision?: number
options?: CallOptions
}
const calls: Call[] = []
let serverStore: AgentSessionAttachmentStore
let failMethod: string | null = null
// Use the real paired-server store behind the RPC seam.
vi.mock('./runtime-environments/runtime-environment-transport-routing', () => ({
vi.mock('./runtime-environments/requests/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: async (
_userDataPath: string,
_environmentId: string,
method: string,
params: Record<string, unknown>,
_timeoutMs?: number,
revision?: number,
_revision?: number,
_envelope?: unknown,
options?: CallOptions
_options?: CallOptions
) => {
calls.push({ method, params, revision, options })
if (method === failMethod) {
return { ok: false, error: { code: 'internal', message: `${method} failed` } }
}
@@ -79,7 +70,6 @@ beforeEach(async () => {
join(workDir, 'server', 'agent-session-attachments'),
{ hasSession: () => true }
)
calls.length = 0
failMethod = null
})
@@ -119,8 +109,5 @@ describe('uploadBufferToAgentSessionAttachments', () => {
const stored = await uploadBufferToAgentSessionAttachments(context(), 'orca-paste-1.png', png)
expect(stored.name).toBe('orca-paste-1.png')
expect((await readFile(stored.path)).equals(png)).toBe(true)
expect(
calls.filter((call) => call.method === 'agentSessionAttachment.uploadAppend')
).toHaveLength(3)
})
})
@@ -14,11 +14,11 @@ import {
type AgentSessionAttachmentUploadTarget
} from '../../shared/agent-session/agent-session-attachments'
import { resolveEnvironment } from '../../shared/remote-runtime/host-store/runtime-environment-store'
import { callRuntimeEnvironment } from './runtime-environments/runtime-environment-transport-routing'
import { callRuntimeEnvironment } from './runtime-environments/requests/runtime-environment-transport-routing'
import {
isRuntimeEnvironmentManuallyDisconnected,
RUNTIME_MANUALLY_DISCONNECTED_MESSAGE
} from './runtime-environments/runtime-environment-manual-disconnect'
} from './runtime-environments/connectivity/runtime-environment-manual-disconnect'
import { stageOneSourceForRuntimeUpload } from './filesystem/filesystem-runtime-upload-staging'
import { streamExternalFileSlices } from './runtime-upload-file-stream'
import { formatByteCeiling } from './runtime-import-limits'
+2 -2
View File
@@ -6,8 +6,8 @@ import {
type AiVaultSessionHandoffResult
} from '../../../shared/ai-vault/contracts/ai-vault-session-handoff'
import { writeAiVaultSessionHandoffSnapshot } from '../../ai-vault/session-handoff-snapshot'
import { callRuntimeEnvironment } from '../runtime-environments/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/runtime-environment-registry-root'
import { callRuntimeEnvironment } from '../runtime-environments/requests/runtime-environment-transport-routing'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/catalog/runtime-environment-registry-root'
/** The snapshot is written on the session's own host, where the hand-off agent starts. */
export async function prepareAiVaultSessionHandoff(
@@ -1,6 +1,6 @@
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { AgentSessionAssociationObservation } from '../../../shared/agent-session/view/agent-session-status-snapshot-fold'
import { getRuntimeEnvironmentTransportGeneration } from '../runtime-environments/runtime-environment-transport-generation'
import { getRuntimeEnvironmentTransportGeneration } from '../runtime-environments/connections/runtime-environment-transport-generation'
type Authority = { identity: string; revision: number; references: number }
const authorities = new Map<string, Authority>()
@@ -32,8 +32,8 @@ import { ALL_RPC_METHODS } from '../../runtime/rpc/methods/rpc-method-registry'
import { ensureOrcadManagedTunnel } from '../../managed-server/lifecycle/orcad-managed-tunnel'
import { getSshTargetRegistryStore } from '../../ssh/targets/ssh-target-registry'
import type { SshConnectionManager } from '../../ssh/connection/ssh-connection-manager'
import { getRuntimeEnvironmentStatus } from '../runtime-environments/runtime-environment-status-probe'
import { isRuntimeEnvironmentManuallyDisconnected } from '../runtime-environments/runtime-environment-manual-disconnect'
import { getRuntimeEnvironmentStatus } from '../runtime-environments/status/runtime-environment-status-probe'
import { isRuntimeEnvironmentManuallyDisconnected } from '../runtime-environments/connectivity/runtime-environment-manual-disconnect'
import {
isDesktopBrowserBridgeHostStopped,
setDesktopBrowserBridgeControl
+1 -1
View File
@@ -39,7 +39,7 @@ import { registerEphemeralVmRuntimeHandlers } from './ephemeral-vm-runtime-handl
import type { PluginService } from '../plugins/plugin-service'
import { getApprovedPluginVmRecipes } from '../plugins/plugin-approved-vm-recipes'
import { resolveProvisionedRootSource } from '../ephemeral-vm/provisioned-root-source'
import { getRuntimeEnvironmentRegistryRoot } from './runtime-environments/runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from './runtime-environments/catalog/runtime-environment-registry-root'
import {
ephemeralVmFailure,
pickEphemeralVmFailureCode,
@@ -107,7 +107,7 @@ import { getAgentSessionAttachmentStore } from '../../native-chat/agent-session-
import type { PluginService } from '../../plugins/plugin-service'
import type { PluginMarketplaceHandlerServices } from '../plugin-marketplaces'
import { settleWithinMs } from '../../startup/quit/quit-teardown-deadline'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/catalog/runtime-environment-registry-root'
let registered = false
// Same cap as the restart path's auth preservation: bookkeeping must not hold Restart on a stalled disk.
+1 -1
View File
@@ -25,7 +25,7 @@ import {
import { listWslMovedProjects } from '../../persistence/moving-wsl-projects/desktop-state/wsl-moved-projects'
import { listHostRepoCatalogSnapshots } from '../../host-repo-catalog-snapshot/host-repo-catalog-snapshot-recorder'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from '../runtime-environments/catalog/runtime-environment-registry-root'
import { isGatedOffWslHost } from '../../wsl/host-lifecycle/wsl-host-gate'
export function registerRepoCatalogHandlers(
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import '../../../shared/protocol-version'
import '../../../../shared/protocol-version'
import {
BROWSER_CLIENT_AUTOMATION_RUNTIME_CAPABILITY,
BROWSER_CLIENT_HOST_RUNTIME_CAPABILITY,
@@ -7,8 +7,8 @@ import {
BROWSER_NETWORK_EXECUTION_HOSTS_RUNTIME_CAPABILITY,
BROWSER_NETWORK_TUNNEL_RUNTIME_CAPABILITY,
BROWSER_SCREENCAST_RUNTIME_CAPABILITY
} from '../../../shared/browser/runtime/browser-runtime-capabilities'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
} from '../../../../shared/browser/runtime/browser-runtime-capabilities'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
const {
handleMock,
@@ -27,23 +27,26 @@ const {
}))
vi.mock('electron', () => ({ ipcMain: { handle: handleMock } }))
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
vi.mock('../../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
resolveEnvironment: resolveEnvironmentMock
}))
vi.mock('./runtime-environment-status-probe', () => ({
vi.mock('../status/runtime-environment-status-probe', () => ({
getRuntimeEnvironmentStatus: getRuntimeEnvironmentStatusMock
}))
vi.mock('../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime', () => ({
startPairedRuntimeBrowserClientHost: startHostMock,
closePairedRuntimeBrowserClientHostEnvironment: closeHostMock
}))
vi.mock('./runtime-environment-manual-disconnect', () => ({
vi.mock(
'../../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime',
() => ({
startPairedRuntimeBrowserClientHost: startHostMock,
closePairedRuntimeBrowserClientHostEnvironment: closeHostMock
})
)
vi.mock('../connectivity/runtime-environment-manual-disconnect', () => ({
isRuntimeEnvironmentManuallyDisconnected: manuallyDisconnectedMock
}))
import { prepareRuntimeEnvironmentBrowserPlacement } from './runtime-environment-browser-placement'
import { registerRuntimeEnvironmentBrowserClientHostHandler } from './runtime-environment-browser-client-host-handler'
import { channelHandlerLookup } from './runtime-environments-ipc-test-harness'
import { channelHandlerLookup } from '../runtime-environments-ipc-test-harness'
const handler = channelHandlerLookup(handleMock)
@@ -105,7 +108,6 @@ describe('runtime environment browser client host handler', () => {
kind: 'server'
})
expect(startHostMock).toHaveBeenCalledOnce()
expect(closeHostMock).not.toHaveBeenCalled()
})
@@ -2,8 +2,8 @@ import { ipcMain } from 'electron'
import {
BrowserClientHostPlacementPreparationRequest,
type BrowserPageCreationPlacement
} from '../../../shared/browser/client-hosting/browser-client-host-placement'
import type { GlobalSettings } from '../../../shared/global-settings-types'
} from '../../../../shared/browser/client-hosting/browser-client-host-placement'
import type { GlobalSettings } from '../../../../shared/global-settings-types'
import { prepareRuntimeEnvironmentBrowserPlacement } from './runtime-environment-browser-placement'
export function registerRuntimeEnvironmentBrowserClientHostHandler(options: {
@@ -1,12 +1,12 @@
import type { BrowserClientHostPlacementPreference } from '../../../shared/browser/client-hosting/browser-client-host-placement'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import type { BrowserClientHostPlacementPreference } from '../../../../shared/browser/client-hosting/browser-client-host-placement'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import {
closePairedRuntimeBrowserClientHostEnvironment,
startPairedRuntimeBrowserClientHost
} from '../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime'
import { prepareBrowserClientHostPlacement } from '../../browser/client-hosts/lifecycle/browser-client-host-placement-preparation'
import { isRuntimeEnvironmentManuallyDisconnected } from './runtime-environment-manual-disconnect'
import { getRuntimeEnvironmentStatus } from './runtime-environment-status-probe'
} from '../../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime'
import { prepareBrowserClientHostPlacement } from '../../../browser/client-hosts/lifecycle/browser-client-host-placement-preparation'
import { isRuntimeEnvironmentManuallyDisconnected } from '../connectivity/runtime-environment-manual-disconnect'
import { getRuntimeEnvironmentStatus } from '../status/runtime-environment-status-probe'
export async function prepareRuntimeEnvironmentBrowserPlacement(
userDataPath: string,
@@ -1,5 +1,5 @@
import { BrowserWindow } from 'electron'
import { RUNTIME_ENVIRONMENT_CATALOG_CHANGED_CHANNEL } from '../../../shared/remote-runtime/host-catalog/runtime-environment-catalog-channel'
import { RUNTIME_ENVIRONMENT_CATALOG_CHANGED_CHANNEL } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-catalog-channel'
export function publishRuntimeEnvironmentCatalogChanged(): void {
for (const window of BrowserWindow.getAllWindows()) {
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { publicRuntimeEnvironmentWithHostKey } from './runtime-environment-host-key'
function environment(publicKeyB64: string): KnownRuntimeEnvironment {
@@ -25,14 +25,9 @@ function environment(publicKeyB64: string): KnownRuntimeEnvironment {
}
describe('the public runtime environment record', () => {
it('carries a stable host key digest but never the key or the device token', () => {
it('never exposes the host key or device token', () => {
const first = publicRuntimeEnvironmentWithHostKey(environment('key-a'))
const again = publicRuntimeEnvironmentWithHostKey(environment('key-a'))
const other = publicRuntimeEnvironmentWithHostKey(environment('key-b'))
expect(first.hostKeyFingerprint).toMatch(/^[0-9a-f]{32}$/)
expect(again.hostKeyFingerprint).toBe(first.hostKeyFingerprint)
expect(other.hostKeyFingerprint).not.toBe(first.hostKeyFingerprint)
expect(JSON.stringify(first)).not.toMatch(/key-a|secret-token/)
})
})
@@ -4,7 +4,7 @@ import {
redactRuntimeEnvironment,
type KnownRuntimeEnvironment,
type PublicKnownRuntimeEnvironment
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
/** Compare a digest of the persistent E2EE host key without exposing the key to the renderer. */
export function publicRuntimeEnvironmentWithHostKey(
@@ -2,14 +2,14 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../shared/constants'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
import { getDefaultSettings } from '../../../../shared/constants'
import { encodePairingOffer } from '../../../../shared/remote-runtime/pairing/pairing'
import {
addEnvironmentFromPairingCode,
removeEnvironment
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getEnvironmentStorePath } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import type { Store } from '../../persistence/loading-store/store'
} from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getEnvironmentStorePath } from '../../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import type { Store } from '../../../persistence/loading-store/store'
import {
readSettingsWithRuntimeEnvironmentPreference,
watchRuntimeEnvironmentPreference
@@ -55,9 +55,7 @@ describe('Active Server after a saved host is removed', () => {
let stopWatching: (() => void) | undefined
rmSync(userDataPath, { recursive: true })
try {
expect(() => {
stopWatching = watchRuntimeEnvironmentPreference(store, userDataPath)
}).not.toThrow()
stopWatching = watchRuntimeEnvironmentPreference(store, userDataPath)
expect(store.getSettings().activeRuntimeEnvironmentId).toBe(environment.id)
mkdirSync(userDataPath)
writeFileSync(getEnvironmentStorePath(userDataPath), '{"version":1,"environments":[]}')
@@ -74,10 +72,9 @@ describe('Active Server after a saved host is removed', () => {
const removed = saveEnvironment()
const store = preferenceStore(removed.id)
removeEnvironment(userDataPath, removed.id)
const replacement = saveEnvironment()
saveEnvironment()
saveEnvironment('other')
expect(replacement.id).not.toBe(removed.id)
expect(
readSettingsWithRuntimeEnvironmentPreference(store, userDataPath).activeRuntimeEnvironmentId
).toBeNull()
@@ -1,8 +1,8 @@
import type { GlobalSettings } from '../../../shared/global-settings-types'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getEnvironmentStorePath } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import type { Store } from '../../persistence/loading-store/store'
import { watchFileAcrossReplacement } from '../filesystem/watcher-process/native-subscriptions/rebinding-file-watch'
import type { GlobalSettings } from '../../../../shared/global-settings-types'
import { listEnvironments } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getEnvironmentStorePath } from '../../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import type { Store } from '../../../persistence/loading-store/store'
import { watchFileAcrossReplacement } from '../../filesystem/watcher-process/native-subscriptions/rebinding-file-watch'
type PreferenceStore = Pick<Store, 'getSettings' | 'updateSettings'>
@@ -2,10 +2,10 @@ import {
redactRuntimeEnvironment,
type KnownRuntimeEnvironment,
type RuntimeEnvironmentRemoval
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { findManagedSshTwin } from '../../../shared/remote-runtime/host-catalog/runtime-environment-host-key-match'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getManagedServerActions } from '../../runtime/hosts/management/managed-server-actions-registry'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { findManagedSshTwin } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-host-key-match'
import { listEnvironments } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getManagedServerActions } from '../../../runtime/hosts/management/managed-server-actions-registry'
/**
* Removes one of two saved entries for a managed server reached through two SSH aliases.
@@ -1,5 +1,5 @@
import { app } from 'electron'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
/** Where the paired-runtime registry lives; read per call, after `app.setName` settles the casing. */
export function getRuntimeEnvironmentRegistryRoot(): string {
@@ -1,15 +1,16 @@
import { desktopChatCreationHistory } from '../../runtime/client/navigation/desktop-chat-creation-history'
import { revokeRemoteFilePreviewGrants } from '../../media/media-preview-protocol'
import { clearBrowserRoutePartitionStorageForEnvironment } from '../../browser/routing/partitions/browser-route-partition-storage-runtime'
import { retireBrowserRoutePartitionStorageForEnvironment } from '../../browser/routing/partitions/browser-route-partition-storage-retirement'
import { clearRuntimeEnvironmentCapabilityEvidence } from './runtime-environment-capability-evidence'
import { clearRuntimeEnvironmentManualDisconnect } from './runtime-environment-manual-disconnect'
import { toRuntimeExecutionHostId, type ExecutionHostId } from '../../../shared/execution-host'
import { desktopChatCreationHistory } from '../../../runtime/client/navigation/desktop-chat-creation-history'
import '../../../media/media-preview-protocol'
import { revokeRemoteFilePreviewGrants } from '../../../media/preview-grant-registry'
import { clearBrowserRoutePartitionStorageForEnvironment } from '../../../browser/routing/partitions/browser-route-partition-storage-runtime'
import { retireBrowserRoutePartitionStorageForEnvironment } from '../../../browser/routing/partitions/browser-route-partition-storage-retirement'
import { clearRuntimeEnvironmentCapabilityEvidence } from '../connections/runtime-environment-capability-evidence'
import { clearRuntimeEnvironmentManualDisconnect } from '../connectivity/runtime-environment-manual-disconnect'
import { toRuntimeExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host'
import {
forgetRemoteLayoutSnapshot,
type RemoteLayoutSnapshotStore
} from '../../remote-layout-snapshot/remote-layout-snapshot-recorder'
import type { HostRepoCatalogSnapshotStore } from '../../host-repo-catalog-snapshot/host-repo-catalog-snapshot-recorder'
} from '../../../remote-layout-snapshot/remote-layout-snapshot-recorder'
import type { HostRepoCatalogSnapshotStore } from '../../../host-repo-catalog-snapshot/host-repo-catalog-snapshot-recorder'
export type RemovedServerStore = RemoteLayoutSnapshotStore &
Pick<HostRepoCatalogSnapshotStore, 'setHostRepoCatalogSnapshot'> & {
@@ -3,12 +3,12 @@ import * as fs from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { ExecutionHostId } from '../../../shared/execution-host'
import { addEnvironmentFromPairingCode } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import '../../../shared/remote-runtime/host-store/runtime-environment-store-file'
import { getEnvironmentStorePath } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import { getRuntimeEnvironmentSidecarPath } from '../../../shared/remote-runtime/host-store/runtime-environment-sidecar'
import { pairingCode } from './runtime-environments-ipc-test-harness'
import type { ExecutionHostId } from '../../../../shared/execution-host'
import { addEnvironmentFromPairingCode } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import '../../../../shared/remote-runtime/host-store/runtime-environment-store-file'
import { getEnvironmentStorePath } from '../../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import { getRuntimeEnvironmentSidecarPath } from '../../../../shared/remote-runtime/host-store/runtime-environment-sidecar'
import { pairingCode } from '../runtime-environments-ipc-test-harness'
import { reconcileOrphanedRuntimeSessions } from './runtime-environment-session-reconcile'
// Lets a test make the registry vanish between a presence check and its read.
@@ -50,8 +50,6 @@ describe('reconciling orphaned runtime sessions at startup', () => {
'runtime:removed-env'
])
expect(reconcileOrphanedRuntimeSessions(store, userDataPath)).toEqual(['runtime:removed-env'])
expect(store.removeWorkspaceSessionHost).toHaveBeenCalledTimes(1)
expect(store.removeWorkspaceSessionHost).toHaveBeenCalledWith('runtime:removed-env')
})
it('deletes nothing when the environment store is missing or unreadable', () => {
@@ -63,7 +61,6 @@ describe('reconciling orphaned runtime sessions at startup', () => {
writeFileSync(getEnvironmentStorePath(corrupt), '{not json', { mode: 0o600 })
vi.spyOn(console, 'warn').mockImplementation(() => undefined)
expect(reconcileOrphanedRuntimeSessions(store, corrupt)).toEqual([])
expect(store.removeWorkspaceSessionHost).not.toHaveBeenCalled()
})
it('keeps sessions of saved servers this build cannot read or currently hides', () => {
@@ -90,6 +87,5 @@ describe('reconciling orphaned runtime sessions at startup', () => {
])
expect(reconcileOrphanedRuntimeSessions(store, userDataPath)).toEqual(['runtime:removed-env'])
expect(store.removeWorkspaceSessionHost).toHaveBeenCalledTimes(1)
})
})
@@ -1,6 +1,6 @@
/** Crashes and older unlink flows can leave runtime session partitions that listings advertise as orphaned hosts. */
import { parseExecutionHostId, type ExecutionHostId } from '../../../shared/execution-host'
import { readEnvironmentStoreRecords } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import { parseExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host'
import { readEnvironmentStoreRecords } from '../../../../shared/remote-runtime/host-store/runtime-environment-store-records'
type SessionHostStore = {
getWorkspaceSessionHostIds: () => ExecutionHostId[]
@@ -1,6 +1,6 @@
import { pairedRuntimeConnectionFromOffer } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { pairedRuntimeConnectionFromOffer } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { beforeEach, describe, expect, it } from 'vitest'
import type { PairingOffer } from '../../../shared/remote-runtime/pairing/mobile-relay-pairing-offer'
import type { PairingOffer } from '../../../../shared/remote-runtime/pairing/mobile-relay-pairing-offer'
import {
advanceRuntimeEnvironmentCapabilityIncarnation,
applyRuntimeEnvironmentCapabilityVerdict,
@@ -1,4 +1,4 @@
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
export type RuntimeEnvironmentCapabilityVerdict = 'capable' | 'absent'
@@ -0,0 +1,92 @@
import { RemoteRuntimeSharedControlConnection } from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-connection'
import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
closeSharedControlTestServers,
createSharedControlTestServer
} from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-test-server'
import { resetRuntimeEnvironmentCapabilityEvidence } from './runtime-environment-capability-evidence'
import { clearRuntimeEnvironmentManualDisconnect } from '../connectivity/runtime-environment-manual-disconnect'
import {
closeRemoteRuntimeRequestConnection,
sendRemoteRuntimeSharedControlRequestOnRuntime
} from './runtime-environment-request-connections'
const ENVIRONMENT_ID = 'standing-intent-test'
afterEach(async () => {
vi.restoreAllMocks()
closeRemoteRuntimeRequestConnection(ENVIRONMENT_ID)
clearRuntimeEnvironmentManualDisconnect(ENVIRONMENT_ID)
resetRuntimeEnvironmentCapabilityEvidence()
await closeSharedControlTestServers()
})
describe('runtime environment shared-control connection cache', () => {
it.each([
['the probed runtime', 'runtime-a', true],
['a replacement runtime behind the same endpoint', 'runtime-b', false]
])(
'sends a runtime-pinned call only when its socket reached %s',
async (_label, reached, sent) => {
const server = await createSharedControlTestServer({
resultForRequest: (method) =>
method === 'status.get' ? { runtimeId: reached } : { method }
})
const response = await sendRemoteRuntimeSharedControlRequestOnRuntime(
ENVIRONMENT_ID,
server.pairing,
{ method: 'files.write', params: { relativePath: 'a.ts' }, timeoutMs: 1000 },
'runtime-a'
)
expect(response === null).toBe(!sent)
expect(server.requests.some((request) => request.method === 'files.write')).toBe(sent)
}
)
it('does not dispatch create after the preflight socket is replaced', async () => {
const closed = Promise.withResolvers<void>()
const server = await createSharedControlTestServer({
onClientClose: () => closed.resolve(),
resultForRequest: (method) => (method === 'status.get' ? { runtimeId: 'runtime-a' } : {})
})
const original = RemoteRuntimeSharedControlConnection.prototype.request
vi.spyOn(RemoteRuntimeSharedControlConnection.prototype, 'request').mockImplementation(
function <TResult>(
this: RemoteRuntimeSharedControlConnection,
...args: Parameters<typeof original>
): Promise<RuntimeRpcResponse<TResult>> {
const invoke = original.bind(this)
const response = invoke<TResult>(...args)
if (args[0] !== 'status.get') {
return response
}
return response.then(async (status) => {
server.closeClients()
await closed.promise
this.reconnectNow()
return status
})
}
)
await expect(
sendRemoteRuntimeSharedControlRequestOnRuntime(
ENVIRONMENT_ID,
server.pairing,
{
method: 'agentSession.create',
params: {
forkFrom: { sessionId: 'child', throughItemId: 'legacy:opencode:source:reply-1' }
},
timeoutMs: 1000
},
'runtime-a'
)
).resolves.toBeNull()
expect(server.requests.filter((request) => request.method === 'agentSession.create')).toEqual(
[]
)
})
})
@@ -1,26 +1,26 @@
import { runtimeEnvironmentConnectionPairingKey as getPairingKey } from './runtime-environment-capability-evidence'
import type { RuntimeEnvironmentCall } from './runtime-environment-call'
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getPreferredPairedRuntimeConnection } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeHostStatusOwner } from '../../../shared/remote-runtime/host-status/runtime-host-status-owner'
import type { RuntimeStatus } from '../../../shared/runtime-types'
import { createRuntimeEnvironmentStatusOwner } from './runtime-environment-status-owner'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../shared/electron-remote-runtime-client-capabilities'
import type { RuntimeEnvironmentCall } from '../requests/runtime-environment-call'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getPreferredPairedRuntimeConnection } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeHostStatusOwner } from '../../../../shared/remote-runtime/host-status/runtime-host-status-owner'
import type { RuntimeStatus } from '../../../../shared/runtime-session-contracts'
import { createRuntimeEnvironmentStatusOwner } from '../status/runtime-environment-status-owner'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../../shared/electron-remote-runtime-client-capabilities'
import type {
RuntimeOrchestrationEnvelope,
RuntimeRequestPreparation,
RuntimeRpcResponse
} from '../../../shared/runtime-rpc-envelope'
import { RemoteRuntimeRequestConnection } from '../../../shared/remote-runtime/requests/remote-runtime-request-connection'
import { RemoteRuntimeSharedControlConnection } from '../../../shared/remote-runtime/control/remote-runtime-shared-control-connection'
import { probeAllRemoteRuntimeSocketsNow } from '../../../shared/remote-runtime/remote-runtime-socket-liveness'
} from '../../../../shared/runtime-rpc-envelope'
import { RemoteRuntimeRequestConnection } from '../../../../shared/remote-runtime/requests/remote-runtime-request-connection'
import { RemoteRuntimeSharedControlConnection } from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-connection'
import { probeAllRemoteRuntimeSocketsNow } from '../../../../shared/remote-runtime/remote-runtime-socket-liveness'
import type {
RemoteRuntimeSharedConnectionDiagnostics,
RemoteRuntimeSharedSubscription
} from '../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
import { isRuntimeEnvironmentManuallyDisconnected } from './runtime-environment-manual-disconnect'
import { publishRuntimeEnvironmentDiagnostics } from './runtime-environment-diagnostics-broadcast'
} from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
import { isRuntimeEnvironmentManuallyDisconnected } from '../connectivity/runtime-environment-manual-disconnect'
import { publishRuntimeEnvironmentDiagnostics } from '../status/runtime-environment-diagnostics-broadcast'
import { sharedControlEnvironmentPolicy } from './runtime-environment-shared-control-policy'
import {
clearSharedControlAnswerSource,
@@ -30,8 +30,8 @@ import {
advanceRuntimeEnvironmentTransportGeneration,
getRuntimeEnvironmentTransportGeneration
} from './runtime-environment-transport-generation'
import { noteRuntimeEnvironmentSharedControlContact } from './runtime-environment-contact-loss'
import { declareWslHostClient } from '../../wsl/host-lifecycle/wsl-host-client-declaration'
import { noteRuntimeEnvironmentSharedControlContact } from '../status/runtime-environment-contact-loss'
import { declareWslHostClient } from '../../../wsl/host-lifecycle/wsl-host-client-declaration'
type CachedRuntimeConnection = {
pairingKey: string
@@ -1,5 +1,5 @@
import { isOrchestrationMutation } from '../../../shared/orchestration/orchestration-rpc-contract'
import type { RuntimeOrchestrationEnvelope } from '../../../shared/runtime-rpc-envelope'
import { isOrchestrationMutation } from '../../../../shared/orchestration/orchestration-rpc-contract'
import type { RuntimeOrchestrationEnvelope } from '../../../../shared/runtime-rpc-envelope'
// Relay mutations safe on shared control: synchronous, idempotent by request id and cursor. Long-running ones (workerStart) stay one-shot for keepalive timeouts and host abort.
const SHARED_CONTROL_RELAY_MUTATIONS = [
@@ -1,9 +1,9 @@
import type { RemoteRuntimeSharedControlConnectionOptions } from '../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
import type { RemoteRuntimeSharedControlConnectionOptions } from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
import { isRuntimeEnvironmentCapabilityPaused } from './runtime-environment-capability-evidence'
import { isRuntimeEnvironmentManuallyDisconnected } from './runtime-environment-manual-disconnect'
import { handleSharedControlRefusal } from './runtime-environment-pairing-refusal-registry'
import { traceSharedControlSocketClose } from './runtime-environment-control-socket-close-trace'
import { isRecipeVmServerAsleep } from '../../ephemeral-vm/recipe-vm-asleep'
import { isRuntimeEnvironmentManuallyDisconnected } from '../connectivity/runtime-environment-manual-disconnect'
import { handleSharedControlRefusal } from '../pairing/runtime-environment-pairing-refusal-registry'
import { traceSharedControlSocketClose } from '../status/runtime-environment-control-socket-close-trace'
import { isRecipeVmServerAsleep } from '../../../ephemeral-vm/recipe-vm-asleep'
/** When a shared control socket may reconnect, who answers a refused token, and where closes go. */
export function sharedControlEnvironmentPolicy(
@@ -1,6 +1,6 @@
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import {
getAcceptedRuntimeEnvironmentCapabilityOutcome,
resetRuntimeEnvironmentCapabilityEvidence,
@@ -1,62 +1,63 @@
import {
manuallyDisconnectedResponse,
runtimeEnvironmentCallFailure
} from './runtime-environment-call-failure'
import { desktopChatCreationHistory } from '../../runtime/client/navigation/desktop-chat-creation-history'
} from '../requests/runtime-environment-call-failure'
import { desktopChatCreationHistory } from '../../../runtime/client/navigation/desktop-chat-creation-history'
import { ipcMain } from 'electron'
import {
addEnvironmentFromPairingCode,
listEnvironments,
removeEnvironment,
resolveEnvironment
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
} from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import {
redactRuntimeEnvironment,
type PublicKnownRuntimeEnvironment,
type RuntimeEnvironmentRemoval
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import {
connectThroughOwningService,
removeThroughOwningService
} from './runtime-environment-host-service-ops'
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import type { RuntimeStatus } from '../../../shared/runtime-session-contracts'
import type { Store } from '../../persistence/loading-store/store'
import { retireRemovedRuntimeEnvironment } from './runtime-environment-removal-cleanup'
import { removeRedundantManagedPairing } from './runtime-environment-redundant-managed-removal'
import { readSettingsWithRuntimeEnvironmentPreference } from './runtime-environment-preference'
import { verifyAndAddRuntimeEnvironmentFromPairingCode } from './runtime-environment-pairing-verification'
import { publishVerifiedPairing } from './runtime-environment-verified-pairing-publish'
import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
import type { RuntimeStatus } from '../../../../shared/runtime-session-contracts'
import type { Store } from '../../../persistence/loading-store/store'
import { retireRemovedRuntimeEnvironment } from '../catalog/runtime-environment-removal-cleanup'
import { removeRedundantManagedPairing } from '../catalog/runtime-environment-redundant-managed-removal'
import { readSettingsWithRuntimeEnvironmentPreference } from '../catalog/runtime-environment-preference'
import { verifyAndAddRuntimeEnvironmentFromPairingCode } from '../pairing/runtime-environment-pairing-verification'
import { publishVerifiedPairing } from '../pairing/runtime-environment-verified-pairing-publish'
import {
closeRemoteRuntimeRequestConnection,
getRuntimeEnvironmentStatusSnapshots
} from './runtime-environment-request-connections'
} from '../connections/runtime-environment-request-connections'
import {
clearRuntimeEnvironmentManualDisconnect,
isRuntimeEnvironmentManuallyDisconnected
} from './runtime-environment-manual-disconnect'
import { callRuntimeEnvironment } from './runtime-environment-transport-routing'
import { getRuntimeEnvironmentStatus } from './runtime-environment-status-probe'
import { noteRuntimeEnvironmentConnectRequested } from './runtime-environment-pairing-refusal-registry'
import { publicRuntimeEnvironmentWithHostKey } from './runtime-environment-host-key'
import { withWslOrcadAccountEnv } from '../../wsl/orcad/wsl-orcad-account-env'
import { clearSshCredentialDeclinedForEnvironment } from '../../ssh/authentication/ssh-credential-decline'
import { callRuntimeEnvironment } from '../requests/runtime-environment-transport-routing'
import { getRuntimeEnvironmentStatus } from '../status/runtime-environment-status-probe'
import { noteRuntimeEnvironmentConnectRequested } from '../pairing/runtime-environment-pairing-refusal-registry'
import { publicRuntimeEnvironmentWithHostKey } from '../catalog/runtime-environment-host-key'
import { withWslOrcadAccountEnv } from '../../../wsl/orcad/wsl-orcad-account-env'
import { clearSshCredentialDeclinedForEnvironment } from '../../../ssh/authentication/ssh-credential-decline'
import {
disconnectManagedHostOfEnvironmentForUser,
disconnectRuntimeEnvironmentForUser,
liftSshUserDisconnectForEnvironment
} from '../ssh/ssh-host-user-disconnect'
} from '../../ssh/ssh-host-user-disconnect'
import {
observeWslOrcadHostResponse,
wslOrcadHostSupports
} from '../../wsl/orcad/wsl-orcad-host-environments'
import { assertWslHostReachable, isGatedOffWslHost } from '../../wsl/host-lifecycle/wsl-host-gate'
} from '../../../wsl/orcad/wsl-orcad-host-environments'
import {
assertWslHostReachable,
isGatedOffWslHost
} from '../../../wsl/host-lifecycle/wsl-host-gate'
import {
isHostSpawnFencedForWslMoveUndo,
wslMoveUndoSpawnFencedResponse
} from '../../persistence/moving-wsl-projects/cutover-recovery/wsl-move-undo-spawn-fence'
export { isRuntimeEnvironmentManuallyDisconnected }
} from '../../../persistence/moving-wsl-projects/cutover-recovery/wsl-move-undo-spawn-fence'
type ConnectivityHandlerOptions = {
store: Store
@@ -1,7 +1,7 @@
import { classifyRuntimeEnvironmentHost } from '../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { WslHostServerStop } from '../../../shared/wsl-host-types'
import { getWslHostService } from '../../wsl/host-lifecycle/wsl-host-service-instance'
import { classifyRuntimeEnvironmentHost } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { WslHostServerStop } from '../../../../shared/wsl-host-types'
import { getWslHostService } from '../../../wsl/host-lifecycle/wsl-host-service-instance'
/** Explicit connection may boot the WSL distro. */
export async function connectThroughOwningService(
@@ -1,7 +1,7 @@
import {
requestDesktopBrowserBridgeReconcile,
resetDesktopBrowserBridgeEnvironment
} from '../browser/desktop-browser-bridge-reconcile'
} from '../../browser/desktop-browser-bridge-reconcile'
export const RUNTIME_MANUALLY_DISCONNECTED_MESSAGE = 'Runtime environment is manually disconnected.'
@@ -1,6 +1,6 @@
import { ipcMain } from 'electron'
import { retryRemoteRuntimeSharedControlConnectionsNow } from './runtime-environment-request-connections'
import { retrySshConnectionsAfterNetworkReturn } from '../ssh/ssh-host-sleep-reconnect'
import { retryRemoteRuntimeSharedControlConnectionsNow } from '../connections/runtime-environment-request-connections'
import { retrySshConnectionsAfterNetworkReturn } from '../../ssh/ssh-host-sleep-reconnect'
const RETRY_CONNECTIONS_NOW_CHANNEL = 'runtimeEnvironments:retryConnectionsNow'
@@ -3,33 +3,33 @@ import {
forgetManagedOrcadEnvironment,
forgetRedundantManagedOrcadEnvironment,
stopManagedOrcadEnvironment
} from '../../managed-server/lifecycle/orcad-runtime-decommission'
import { getManagedOrcadRuntimeStatus } from '../../managed-server/lifecycle/orcad-runtime-status'
import { recoverManagedOrcadEnvironment } from '../../managed-server/lifecycle/orcad-managed-recovery'
import { rollbackManagedOrcadEnvironment } from '../../managed-server/lifecycle/orcad-managed-rollback'
import { updateManagedOrcadEnvironment } from '../../managed-server/lifecycle/orcad-runtime-maintenance'
import { getOrcadSshDeployment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
} from '../../../managed-server/lifecycle/orcad-runtime-decommission'
import { getManagedOrcadRuntimeStatus } from '../../../managed-server/lifecycle/orcad-runtime-status'
import { recoverManagedOrcadEnvironment } from '../../../managed-server/lifecycle/orcad-managed-recovery'
import { rollbackManagedOrcadEnvironment } from '../../../managed-server/lifecycle/orcad-managed-rollback'
import { updateManagedOrcadEnvironment } from '../../../managed-server/lifecycle/orcad-runtime-maintenance'
import { getOrcadSshDeployment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import type {
OrcadManagedRestartResult,
OrcadManagedStaleLockReclaimRequest,
OrcadManagedStaleLockReclaimResult
} from '../../../shared/orcad-managed-runtime'
import { restartManagedOrcadEnvironment } from '../../managed-server/lifecycle/orcad-managed-launch-session'
import { forgetManagedOrcadAutomaticFailures } from '../../managed-server/lifecycle/orcad-managed-tunnel'
import { runManagedOrcadStaleLockReclaim } from '../../managed-server/lifecycle/orcad-managed-stale-lock-reclaim'
} from '../../../../shared/orcad-managed-runtime'
import { restartManagedOrcadEnvironment } from '../../../managed-server/lifecycle/orcad-managed-launch-session'
import { forgetManagedOrcadAutomaticFailures } from '../../../managed-server/lifecycle/orcad-managed-tunnel'
import { runManagedOrcadStaleLockReclaim } from '../../../managed-server/lifecycle/orcad-managed-stale-lock-reclaim'
import { clearLaunchSessionNote } from './runtime-environment-managed-tunnel'
import { forgetLaunchSessionVerdict } from '../ssh/ssh-host-launch-session-note'
import { forgetLaunchSessionVerdict } from '../../ssh/ssh-host-launch-session-note'
import {
liftUserHoldsForServerAction,
resumeRuntimeEnvironmentStatus
} from '../ssh/ssh-host-user-disconnect'
import { releaseUnlinkedHostTransport } from '../ssh/ssh-session-teardown'
import type { ManagedServerActions } from '../../runtime/hosts/management/managed-server-actions-registry'
} from '../../ssh/ssh-host-user-disconnect'
import { releaseUnlinkedHostTransport } from '../../ssh/ssh-session-teardown'
import type { ManagedServerActions } from '../../../runtime/hosts/management/managed-server-actions-registry'
import {
retireRemovedRuntimeEnvironment,
type RemovedServerStore
} from './runtime-environment-removal-cleanup'
} from '../catalog/runtime-environment-removal-cleanup'
export type ManagedOrcadActionOptions = {
getUserDataPath: () => string
@@ -3,24 +3,24 @@ import { ipcMain } from 'electron'
import type {
OrcadDeltaMovePreview,
OrcadDeltaMoveResult
} from '../../../shared/orcad-managed-runtime'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { requireManagedOrcadInfrastructure } from '../../managed-server/lifecycle/orcad-managed-runtime-context'
import { ensureOrcadManagedTunnel } from '../../managed-server/lifecycle/orcad-managed-tunnel'
} from '../../../../shared/orcad-managed-runtime'
import { listEnvironments } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { requireManagedOrcadInfrastructure } from '../../../managed-server/lifecycle/orcad-managed-runtime-context'
import { ensureOrcadManagedTunnel } from '../../../managed-server/lifecycle/orcad-managed-tunnel'
import {
keepOrcadServerVersion,
runOrcadDeltaMove
} from '../../managed-server/migration/cutover/orcad-migration-delta-move'
import { planOrcadDeltaMove } from '../../managed-server/migration/cutover/orcad-migration-delta-plan'
import { censusHostRelayTerminalsFor } from '../../ssh/legacy-relay/endpoints/ssh-host-relay-census-for-target'
} from '../../../managed-server/migration/cutover/orcad-migration-delta-move'
import { planOrcadDeltaMove } from '../../../managed-server/migration/cutover/orcad-migration-delta-plan'
import { censusHostRelayTerminalsFor } from '../../../ssh/legacy-relay/endpoints/ssh-host-relay-census-for-target'
import {
orcadMigrationDestinationFor,
orcadServerAutomationEnablerFor
} from '../../managed-server/lifecycle/orcad-runtime-conversion-wiring'
import { enableMovedOrcadAutomationsForTarget } from '../../persistence/migrating-orcad-catalog/retained-source/orcad-migration-moved-automations'
} from '../../../managed-server/lifecycle/orcad-runtime-conversion-wiring'
import { enableMovedOrcadAutomationsForTarget } from '../../../persistence/migrating-orcad-catalog/retained-source/orcad-migration-moved-automations'
import { requiredString } from './orcad-runtime-lifecycle-handlers'
import { publishResolvedChangedHostStatus } from './runtime-environment-managed-tunnel'
import { runTargetLifecycle } from '../ssh/ssh-target-lifecycle-queue'
import { runTargetLifecycle } from '../../ssh/ssh-target-lifecycle-queue'
export function registerOrcadDeltaMoveHandlers(getUserDataPath: () => string): void {
ipcMain.handle(
@@ -1,6 +1,6 @@
/** IPC for "Move anyway": the user confirms that saved state a move can't carry stays on this desktop. */
import { ipcMain } from 'electron'
import { getSshTargetRegistryStore } from '../../ssh/targets/ssh-target-registry'
import { getSshTargetRegistryStore } from '../../../ssh/targets/ssh-target-registry'
import { requiredString } from './orcad-runtime-lifecycle-handlers'
export function registerOrcadLeaveBehindHandlers(): void {
@@ -2,10 +2,10 @@ import { ipcMain } from 'electron'
import type {
OrcadManagedConversionResult,
OrcadManagedPendingMigrationRow
} from '../../../shared/orcad-managed-runtime'
import { listPendingManagedOrcadMigrations } from '../../managed-server/lifecycle/orcad-managed-migration-status'
import { convertSshTargetToManagedOrcad } from '../../managed-server/lifecycle/orcad-runtime-conversion'
import { conversionCollaborators } from '../../managed-server/lifecycle/orcad-runtime-conversion-wiring'
} from '../../../../shared/orcad-managed-runtime'
import { listPendingManagedOrcadMigrations } from '../../../managed-server/lifecycle/orcad-managed-migration-status'
import { convertSshTargetToManagedOrcad } from '../../../managed-server/lifecycle/orcad-runtime-conversion'
import { conversionCollaborators } from '../../../managed-server/lifecycle/orcad-runtime-conversion-wiring'
import { requiredString } from './orcad-runtime-lifecycle-handlers'
export function registerOrcadRuntimeConversionHandlers(getUserDataPath: () => string): void {
@@ -1,7 +1,7 @@
import { ipcMain } from 'electron'
import type { OrcadManagedRuntimeStatus } from '../../../shared/orcad-managed-runtime'
import { createManagedOrcadEnvironment } from '../../managed-server/lifecycle/orcad-runtime-deployment'
import { getManagedOrcadRuntimeStatus } from '../../managed-server/lifecycle/orcad-runtime-status'
import type { OrcadManagedRuntimeStatus } from '../../../../shared/orcad-managed-runtime'
import { createManagedOrcadEnvironment } from '../../../managed-server/lifecycle/orcad-runtime-deployment'
import { getManagedOrcadRuntimeStatus } from '../../../managed-server/lifecycle/orcad-runtime-status'
export function registerOrcadRuntimeLifecycleHandlers(options: {
getUserDataPath: () => string
@@ -1,5 +1,5 @@
import { ipcMain } from 'electron'
import { registerManagedServerActions } from '../../runtime/hosts/management/managed-server-actions-registry'
import { registerManagedServerActions } from '../../../runtime/hosts/management/managed-server-actions-registry'
import {
createManagedOrcadActions,
reclaimManagedOrcadStaleLock,
@@ -1,48 +1,48 @@
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import {
getOrcadSshDeployment,
type KnownRuntimeEnvironment
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { classifyRuntimeEnvironmentHost } from '../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { classifyRuntimeEnvironmentHost } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import type {
SshManagedServerStatus,
SshManagedServerUpdateNote,
SshTarget
} from '../../../shared/ssh-types'
import { managedServerUpdateDeps } from '../../ssh/host-server/managed-server-update-deps'
} from '../../../../shared/ssh-types'
import { managedServerUpdateDeps } from '../../../ssh/host-server/managed-server-update-deps'
import {
ensureOrcadManagedTunnel,
rebuildOrcadManagedTunnel
} from '../../managed-server/lifecycle/orcad-managed-tunnel'
import { verifyOrcadManagedServing } from '../../managed-server/lifecycle/orcad-managed-serving-verify'
} from '../../../managed-server/lifecycle/orcad-managed-tunnel'
import { verifyOrcadManagedServing } from '../../../managed-server/lifecycle/orcad-managed-serving-verify'
import {
getManagedOrcadCrashLoopBrake,
OrcadCrashLoopError
} from '../../managed-server/lifecycle/orcad-managed-crash-loop'
} from '../../../managed-server/lifecycle/orcad-managed-crash-loop'
import {
MANAGED_ORCAD_NOT_ACTIVATED_DETAIL,
setManagedOrcadStartListener,
type OrcadManagedServing
} from '../../managed-server/lifecycle/orcad-managed-serving'
} from '../../../managed-server/lifecycle/orcad-managed-serving'
import {
redeployStoppedManagedOrcad,
updateManagedOrcadOnRestore
} from '../../managed-server/lifecycle/orcad-managed-update-on-restore'
} from '../../../managed-server/lifecycle/orcad-managed-update-on-restore'
import {
getSshHostServerStatus,
setSshHostServerStatus
} from '../../ssh/host-server/ssh-host-server-status'
import { getSshTargetRegistryStore } from '../../ssh/targets/ssh-target-registry'
import { connectionManager, getCurrentMainWindow } from '../ssh/ssh-ipc-context'
} from '../../../ssh/host-server/ssh-host-server-status'
import { getSshTargetRegistryStore } from '../../../ssh/targets/ssh-target-registry'
import { connectionManager, getCurrentMainWindow } from '../../ssh/ssh-ipc-context'
import {
getWslHostPhoneGate,
getWslHostService
} from '../../wsl/host-lifecycle/wsl-host-service-instance'
import { assertWslHostReachable } from '../../wsl/host-lifecycle/wsl-host-gate'
import { wslHostPhoneRefusal } from '../../wsl/host-lifecycle/wsl-host-phone-reach'
import { broadcastSshState } from '../ssh/ssh-renderer-broadcast'
import { restoreManagedHostPortForwards } from '../ssh/ssh-managed-port-forward-restore'
import { recipeVmWakeIntentFor } from '../../../shared/remote-runtime/recipe-vm-wake'
} from '../../../wsl/host-lifecycle/wsl-host-service-instance'
import { assertWslHostReachable } from '../../../wsl/host-lifecycle/wsl-host-gate'
import { wslHostPhoneRefusal } from '../../../wsl/host-lifecycle/wsl-host-phone-reach'
import { broadcastSshState } from '../../ssh/ssh-renderer-broadcast'
import { restoreManagedHostPortForwards } from '../../ssh/ssh-managed-port-forward-restore'
import { recipeVmWakeIntentFor } from '../../../../shared/remote-runtime/recipe-vm-wake'
import { prepareRecipeVm } from './runtime-environment-recipe-vm-wake'
/** `wslStart: 'never'` serves a WSL host only while it runs: a paired phone never boots a distro.
@@ -175,7 +175,7 @@ async function prepareNetworkServerForCall(
if (targetId) {
// Publish the warning after update status settles; import lazily to break the publishing cycle.
void Promise.resolve(update)
.then(() => import('../ssh/ssh-host-launch-session-note'))
.then(() => import('../../ssh/ssh-host-launch-session-note'))
.then(({ noteEndedLaunchSshSession }) =>
noteEndedLaunchSshSession(targetId, environment.id)
)
@@ -194,7 +194,7 @@ function restoreForwardsOverTunnel(environment: KnownRuntimeEnvironment): void {
async function forgetLaunchSessionVerdictLazily(targetId: string): Promise<void> {
// Import lazily because the note module publishes through this module.
const { forgetLaunchSessionVerdict } = await import('../ssh/ssh-host-launch-session-note')
const { forgetLaunchSessionVerdict } = await import('../../ssh/ssh-host-launch-session-note')
forgetLaunchSessionVerdict(targetId)
}
@@ -3,38 +3,29 @@
import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { removeTempDirs } from '../../../shared/testing/remove-temp-dirs.test-support'
import { removeTempDirs } from '../../../../shared/testing/remove-temp-dirs.test-support'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
import { PAIRING_OFFER_VERSION } from '../../../shared/remote-runtime/pairing/mobile-relay-pairing-offer'
import { upsertEphemeralVmRuntime } from '../../../shared/ephemeral-vm-runtime-store'
import type { EphemeralVmRuntimeStatus } from '../../../shared/ephemeral-vm-runtimes'
import { registerRecipeVmWaker } from '../../runtime/workspaces/sleep/recipe-vm-waker-registry'
import { encodePairingOffer } from '../../../../shared/remote-runtime/pairing/pairing'
import { PAIRING_OFFER_VERSION } from '../../../../shared/remote-runtime/pairing/mobile-relay-pairing-offer'
import { upsertEphemeralVmRuntime } from '../../../../shared/ephemeral-vm-runtime-store'
import type { EphemeralVmRuntimeStatus } from '../../../../shared/ephemeral-vm-runtimes'
import { registerRecipeVmWaker } from '../../../runtime/workspaces/sleep/recipe-vm-waker-registry'
import type {
RecipeVmOpening,
RecipeVmState
} from '../../runtime/workspaces/sleep/recipe-vm-operations'
} from '../../../runtime/workspaces/sleep/recipe-vm-operations'
import { prepareRecipeVm } from './runtime-environment-recipe-vm-wake'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import {
forgetRecipeVmWakePairingsForTests,
noteRecipeVmWakePairing
} from '../../ephemeral-vm/recipe-vm-wake-pairings'
} from '../../../ephemeral-vm/recipe-vm-wake-pairings'
import {
clearRuntimeEnvironmentManualDisconnect,
markRuntimeEnvironmentManuallyDisconnected
} from './runtime-environment-manual-disconnect'
} from '../connectivity/runtime-environment-manual-disconnect'
const VM_ENVIRONMENT = { id: 'env-vm', source: 'ephemeral-vm' as const }
const SSH_VM_ENVIRONMENT = {
id: 'env-ssh-vm',
orcadDeployment: {
sshTargetId: 'runtime-ssh-vm-2',
sshTargetGeneration: 1,
localPort: 41_000,
remotePort: 6768
}
}
let userDataPath = ''
@@ -115,8 +106,6 @@ describe('a request to a recipe VM environment', () => {
async (status) => {
recordVm(status)
await expect(codeOf('none')).resolves.toBe('runtime_environment_asleep')
expect(state).toHaveBeenCalledWith('workspace-of-vm-1', true)
expect(holdOpen).not.toHaveBeenCalled()
}
)
@@ -140,7 +129,6 @@ describe('a request to a recipe VM environment', () => {
markRuntimeEnvironmentManuallyDisconnected(VM_ENVIRONMENT.id)
try {
await expect(codeOf('wake')).resolves.toBe('runtime_manually_disconnected')
expect(holdOpen).not.toHaveBeenCalled()
} finally {
clearRuntimeEnvironmentManualDisconnect(VM_ENVIRONMENT.id)
}
@@ -198,15 +186,8 @@ describe('a request to a recipe VM environment', () => {
}
})
it("finds an SSH-connected VM by its runtime-owned target's environment", async () => {
recordVm('suspended', SSH_VM_ENVIRONMENT.id, 'vm-2')
await expect(codeOf('wake', SSH_VM_ENVIRONMENT)).resolves.toBeNull()
expect(holdOpen).toHaveBeenCalledWith('workspace-of-vm-2', true, 180_000, undefined)
})
it('never treats a user-paired server as a VM, even under a recorded id', async () => {
recordVm('suspended', 'env-manual')
await expect(codeOf('none', { id: 'env-manual', source: 'manual' })).resolves.toBeNull()
expect(state).not.toHaveBeenCalled()
})
})
@@ -1,10 +1,10 @@
import {
findEnvironmentEphemeralVmRuntime,
isEphemeralVmSuspended
} from '../../../shared/ephemeral-vm-environment-sleep'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
} from '../../../../shared/ephemeral-vm-environment-sleep'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import {
RECIPE_VM_OPEN_HOLD_MS,
RUNTIME_ENVIRONMENT_ASLEEP_ERROR_CODE,
@@ -15,18 +15,18 @@ import {
RUNTIME_ENVIRONMENT_WAKING_ERROR_CODE,
RUNTIME_ENVIRONMENT_WAKING_MESSAGE,
type RecipeVmWakeIntent
} from '../../../shared/remote-runtime/recipe-vm-wake'
import { getRecipeVmWaker } from '../../runtime/workspaces/sleep/recipe-vm-waker-registry'
import { latestRecipeVmWake } from '../../ephemeral-vm/recipe-vm-wake-pairings'
} from '../../../../shared/remote-runtime/recipe-vm-wake'
import { getRecipeVmWaker } from '../../../runtime/workspaces/sleep/recipe-vm-waker-registry'
import { latestRecipeVmWake } from '../../../ephemeral-vm/recipe-vm-wake-pairings'
import {
isRuntimeEnvironmentManuallyDisconnected,
runtimeEnvironmentManualDisconnectCount,
runtimeEnvironmentManualDisconnectReason
} from './runtime-environment-manual-disconnect'
} from '../connectivity/runtime-environment-manual-disconnect'
import type {
RecipeVmOpening,
RecipeVmState
} from '../../runtime/workspaces/sleep/recipe-vm-operations'
} from '../../../runtime/workspaces/sleep/recipe-vm-operations'
type RecipeVmEnvironment = Pick<KnownRuntimeEnvironment, 'id' | 'source' | 'orcadDeployment'>
@@ -1,54 +1,54 @@
import {
pairedRuntimeBrowserClientHostId,
pairedRuntimeBrowserClientHostsPage
} from '../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { prepareRuntimeEnvironmentBrowserPlacement } from './runtime-environment-browser-placement'
} from '../../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime'
import { listEnvironments } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { prepareRuntimeEnvironmentBrowserPlacement } from '../browser-hosting/runtime-environment-browser-placement'
import {
getPreferredPairingOffer,
type KnownRuntimeEnvironment
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { classifyRuntimeEnvironmentHost } from '../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { classifyRuntimeEnvironmentHost } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import {
runtimeEnvironmentStatusFromSnapshot,
type RuntimeEnvironmentStatus
} from '../../../shared/remote-runtime/host-status/runtime-host-status'
} from '../../../../shared/remote-runtime/host-status/runtime-host-status'
import type {
MobileDesktopRelayHosts,
MobileDesktopRelayHostWatch
} from '../../runtime/mobile-desktop-relay/mobile-desktop-relay-hosts'
} from '../../../runtime/mobile-desktop-relay/mobile-desktop-relay-hosts'
import {
prepareManagedRuntimeRequest,
type ManagedRuntimeCallPreparation
} from './runtime-environment-managed-tunnel'
import { wslHostsGateOpen } from '../../wsl/host-lifecycle/wsl-host-gate'
import type { WslHostPhoneReach } from '../../wsl/host-lifecycle/wsl-host-phone-reach'
} from '../managed-orcad/runtime-environment-managed-tunnel'
import { wslHostsGateOpen } from '../../../wsl/host-lifecycle/wsl-host-gate'
import type { WslHostPhoneReach } from '../../../wsl/host-lifecycle/wsl-host-phone-reach'
import {
getWslHostPhoneGate,
onWslHostStatusesChanged
} from '../../wsl/host-lifecycle/wsl-host-service-instance'
import type { SshConnectionState } from '../../../shared/ssh-types'
} from '../../../wsl/host-lifecycle/wsl-host-service-instance'
import type { SshConnectionState } from '../../../../shared/ssh-types'
import {
getRegisteredSshState,
listRegisteredSshTargets
} from '../../ssh/targets/ssh-target-registry'
import { getRuntimeEnvironmentStatusSnapshots } from './runtime-environment-request-connections'
} from '../../../ssh/targets/ssh-target-registry'
import { getRuntimeEnvironmentStatusSnapshots } from '../connections/runtime-environment-request-connections'
import {
callRuntimeEnvironment,
subscribeRuntimeEnvironment
} from './runtime-environment-transport-routing'
import type { RemoteRuntimeTransportSubscription } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
import { isRuntimeSubscriptionReplayResponse } from '../../../shared/runtime-subscription-replay'
} from '../requests/runtime-environment-transport-routing'
import type { RemoteRuntimeTransportSubscription } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
import { isRuntimeSubscriptionReplayResponse } from '../../../../shared/runtime-subscription-replay'
import {
isRuntimeEnvironmentManuallyDisconnected,
runtimeEnvironmentManualDisconnectCount
} from './runtime-environment-manual-disconnect'
import { onRuntimeEnvironmentStatusPublished } from './runtime-environment-status-owner'
import { watchRuntimeEnvironmentRegistry } from './runtime-environment-preference'
import { getRuntimeEnvironmentTransportGeneration } from './runtime-environment-transport-generation'
import { getRuntimeEnvironmentRegistryRoot } from './runtime-environment-registry-root'
import { recipeVmServerState } from './runtime-environment-recipe-vm-wake'
import { RECIPE_VM_PHONE_OPEN_HOLD_MS } from '../../../shared/remote-runtime/recipe-vm-wake'
} from '../connectivity/runtime-environment-manual-disconnect'
import { onRuntimeEnvironmentStatusPublished } from '../status/runtime-environment-status-owner'
import { watchRuntimeEnvironmentRegistry } from '../catalog/runtime-environment-preference'
import { getRuntimeEnvironmentTransportGeneration } from '../connections/runtime-environment-transport-generation'
import { getRuntimeEnvironmentRegistryRoot } from '../catalog/runtime-environment-registry-root'
import { recipeVmServerState } from '../managed-orcad/runtime-environment-recipe-vm-wake'
import { RECIPE_VM_PHONE_OPEN_HOLD_MS } from '../../../../shared/remote-runtime/recipe-vm-wake'
const retirementListeners = new Set<(environmentId: string) => void>()
@@ -1,20 +1,20 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { OrcadManagedIdentityError } from '../../managed-server/lifecycle/orcad-managed-tunnel-identity'
import { OrcadManagedIdentityError } from '../../../managed-server/lifecycle/orcad-managed-tunnel-identity'
const mocks = vi.hoisted(() => ({ authenticationRejected: vi.fn() }))
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
vi.mock('../../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
resolveEnvironment: () => ({
id: 'env-1',
orcadDeployment: { sshTargetId: 'ssh-1', sshTargetGeneration: 1, localPort: 1, remotePort: 2 }
})
}))
vi.mock('./runtime-environment-request-connections', () => ({
vi.mock('../connections/runtime-environment-request-connections', () => ({
getRuntimeEnvironmentStatusOwner: () => ({
authenticationRejected: mocks.authenticationRejected
})
}))
vi.mock('../../managed-server/lifecycle/orcad-managed-tunnel', () => ({
vi.mock('../../../managed-server/lifecycle/orcad-managed-tunnel', () => ({
managedOrcadPairingFailureStands: vi.fn(),
recoverRefusedManagedOrcadPairing: vi.fn()
}))
@@ -38,9 +38,5 @@ describe('handleManagedSharedControlRefusal', () => {
() => true
)
).resolves.toBe('stop')
expect(mocks.authenticationRejected).toHaveBeenCalledWith(
refusal.message,
'orcad_server_replaced'
)
})
})
@@ -1,12 +1,12 @@
import { errorMessage } from '../../../shared/error-message'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getOrcadSshDeployment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { managedOrcadRefusalNeedingUserCode } from '../../managed-server/lifecycle/orcad-managed-tunnel-identity'
import { errorMessage } from '../../../../shared/error-message'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getOrcadSshDeployment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { managedOrcadRefusalNeedingUserCode } from '../../../managed-server/lifecycle/orcad-managed-tunnel-identity'
import {
managedOrcadPairingFailureStands,
recoverRefusedManagedOrcadPairing
} from '../../managed-server/lifecycle/orcad-managed-tunnel'
import { getRuntimeEnvironmentStatusOwner } from './runtime-environment-request-connections'
} from '../../../managed-server/lifecycle/orcad-managed-tunnel'
import { getRuntimeEnvironmentStatusOwner } from '../connections/runtime-environment-request-connections'
/** Null leaves user-managed pairing recovery to the caller. */
export function recoverRefusedManagedPairing(
@@ -1,33 +1,33 @@
import { pairedRuntimeConnectionFromOffer } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { pairedRuntimeConnectionFromOffer } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import {
addOrUpdateEnvironmentFromPairingCode,
resolveEnvironment,
updateEnvironmentFromPairingCode
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RuntimeEnvironmentStoreError } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
} from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RuntimeEnvironmentStoreError } from '../../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import {
parseHostAccessLink,
type ParsedHostAccessLink
} from '../../../shared/remote-runtime/pairing/remote-pairing-address'
} from '../../../../shared/remote-runtime/pairing/remote-pairing-address'
import {
verifyRemotePairingRuntimeStatus,
type RemotePairingFailure,
type VerifyAndAddRuntimeEnvironmentResult
} from '../../../shared/remote-runtime/pairing/remote-pairing-verification'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import { exchangeRemotePairingTicket } from '../../../shared/remote-runtime/pairing/remote-pairing-ticket-exchange'
import { sendRemoteRuntimeRequestOnSocket } from '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../shared/electron-remote-runtime-client-capabilities'
} from '../../../../shared/remote-runtime/pairing/remote-pairing-verification'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import { exchangeRemotePairingTicket } from '../../../../shared/remote-runtime/pairing/remote-pairing-ticket-exchange'
import { sendRemoteRuntimeRequestOnSocket } from '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../../shared/electron-remote-runtime-client-capabilities'
import {
getPreferredPairingOffer,
isEphemeralVmRuntimeEnvironment,
redactRuntimeEnvironment
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeStatus } from '../../../shared/runtime-session-contracts'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeStatus } from '../../../../shared/runtime-session-contracts'
import {
remoteServerPairingFailureText,
type RemoteServerPairingFailureCode
} from '../../../shared/remote-runtime/pairing/remote-server-pairing-failure-copy'
} from '../../../../shared/remote-runtime/pairing/remote-server-pairing-failure-copy'
type VerifyAndAddRuntimeEnvironmentArgs = {
name: string
@@ -1,5 +1,5 @@
import { ipcMain } from 'electron'
import type { VerifyAndAddRuntimeEnvironmentResult } from '../../../shared/remote-runtime/pairing/remote-pairing-verification'
import type { VerifyAndAddRuntimeEnvironmentResult } from '../../../../shared/remote-runtime/pairing/remote-pairing-verification'
import { verifyAndRepairRuntimeEnvironmentFromPairingCode } from './runtime-environment-pairing-verification'
import { publishVerifiedPairing } from './runtime-environment-verified-pairing-publish'
@@ -1,7 +1,7 @@
import type { VerifyAndAddRuntimeEnvironmentResult } from '../../../shared/remote-runtime/pairing/remote-pairing-verification'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getRuntimeEnvironmentStatusOwner } from './runtime-environment-request-connections'
import { clearRuntimeEnvironmentManualDisconnect } from './runtime-environment-manual-disconnect'
import type { VerifyAndAddRuntimeEnvironmentResult } from '../../../../shared/remote-runtime/pairing/remote-pairing-verification'
import { listEnvironments } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getRuntimeEnvironmentStatusOwner } from '../connections/runtime-environment-request-connections'
import { clearRuntimeEnvironmentManualDisconnect } from '../connectivity/runtime-environment-manual-disconnect'
type VerifiedPairing = Extract<VerifyAndAddRuntimeEnvironmentResult, { ok: true }>
@@ -1,9 +1,9 @@
import { resetRuntimeEnvironmentStatusOwners } from './runtime-environment-request-connections'
import { resetRuntimeEnvironmentStatusOwners } from '../connections/runtime-environment-request-connections'
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as environmentStore from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import * as environmentStore from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
const {
handleMock,
@@ -48,15 +48,15 @@ vi.mock('electron', () => ({
}
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: sendRemoteRuntimeRequestMock
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-subscription-transport', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport', () => ({
subscribeRemoteRuntimeTransport: subscribeRemoteRuntimeRequestMock
}))
vi.mock('./runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('./runtime-environments-ipc-test-harness')
vi.mock('../connections/runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('../runtime-environments-ipc-test-harness')
return withRuntimeStatusOwners({
sendRemoteRuntimeConnectionRequest: sendRemoteRuntimeConnectionRequestMock,
sendRemoteRuntimeSharedControlRequest: sendRemoteRuntimeSharedControlRequestMock,
@@ -72,8 +72,8 @@ vi.mock('./runtime-environment-request-connections', async () => {
})
})
import { registerRuntimeEnvironmentHandlers } from './runtime-environments'
import { channelHandlerLookup, pairingCode } from './runtime-environments-ipc-test-harness'
import { registerRuntimeEnvironmentHandlers } from '../runtime-environments'
import { channelHandlerLookup, pairingCode } from '../runtime-environments-ipc-test-harness'
const handler = channelHandlerLookup(handleMock)
@@ -148,7 +148,6 @@ describe('registerRuntimeEnvironmentHandlers', () => {
ok: false,
kind: 'host-unreachable'
})
expect(sendRemoteRuntimeRequestMock).not.toHaveBeenCalled()
expect(environmentStore.listEnvironments(userDataPath)).toEqual([])
})
@@ -179,7 +178,6 @@ describe('registerRuntimeEnvironmentHandlers', () => {
updatedExisting: true,
environment: { id: saved.id, name: 'desk' }
})
expect(environmentStore.listEnvironments(userDataPath)).toHaveLength(1)
expect(environmentStore.resolveEnvironmentPairingOffer(userDataPath, saved.id)).toMatchObject({
endpoint: 'ws://192.0.2.10:6768',
deviceToken: 'second-token'
@@ -2,8 +2,8 @@ import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as environmentStore from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import * as environmentStore from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
const { handleMock, getPathMock, sendRemoteRuntimeRequestMock } = vi.hoisted(() => ({
handleMock: vi.fn(),
@@ -22,15 +22,15 @@ vi.mock('electron', () => ({
}
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: sendRemoteRuntimeRequestMock
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-subscription-transport', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport', () => ({
subscribeRemoteRuntimeTransport: vi.fn()
}))
vi.mock('./runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('./runtime-environments-ipc-test-harness')
vi.mock('../connections/runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('../runtime-environments-ipc-test-harness')
return withRuntimeStatusOwners({
sendRemoteRuntimeConnectionRequest: vi.fn(),
sendRemoteRuntimeSharedControlRequest: vi.fn(() => new Promise(() => {})),
@@ -44,13 +44,13 @@ vi.mock('./runtime-environment-request-connections', async () => {
})
})
import { registerRuntimeEnvironmentHandlers } from './runtime-environments'
import { registerRuntimeEnvironmentHandlers } from '../runtime-environments'
import {
getRuntimeEnvironmentStatusOwner,
getRuntimeEnvironmentStatusSnapshots,
resetRuntimeEnvironmentStatusOwners
} from './runtime-environment-request-connections'
import { channelHandlerLookup, pairingCode } from './runtime-environments-ipc-test-harness'
} from '../connections/runtime-environment-request-connections'
import { channelHandlerLookup, pairingCode } from '../runtime-environments-ipc-test-harness'
const handler = channelHandlerLookup(handleMock)
const ENDPOINT = 'ws://192.168.1.20:6768'
@@ -149,10 +149,9 @@ describe('runtimeEnvironments:repairFromPairingCode', () => {
keyByte: 2
})
})
).resolves.toEqual({
).resolves.toMatchObject({
ok: false,
kind: 'different-server',
message: 'This pairing link is for a different server.'
kind: 'different-server'
})
expect(savedToken()).toBe('revoked-token')
})
@@ -162,7 +161,6 @@ describe('runtimeEnvironments:repairFromPairingCode', () => {
await expect(
repair(null, { selector: 'desk', pairingCode: 'not a link' })
).resolves.toMatchObject({ ok: false, kind: 'access-link-invalid' })
expect(sendRemoteRuntimeRequestMock).not.toHaveBeenCalled()
sendRemoteRuntimeRequestMock.mockRejectedValue(
new RemoteRuntimeClientError('unauthorized', 'rejected', {
@@ -187,6 +185,5 @@ describe('runtimeEnvironments:repairFromPairingCode', () => {
await expect(
repair(null, { selector: 'vm', pairingCode: pairingCode(ENDPOINT, { keyByte: 2 }) })
).resolves.toMatchObject({ ok: false, kind: 'environment-save-failed' })
expect(sendRemoteRuntimeRequestMock).not.toHaveBeenCalled()
})
})
@@ -1,13 +1,13 @@
import { getAppEnvironment } from '../../../shared/app-environment'
import { getPreferredPairedRuntimeConnection } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { getAppEnvironment } from '../../../../shared/app-environment'
import { getPreferredPairedRuntimeConnection } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import {
listEnvironments,
resolveEnvironment
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
} from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import {
pairedOrchestrationPeers,
type OrchestrationEnvironmentTransport
} from '../../runtime/orchestration/federation/environment-transport'
} from '../../../runtime/orchestration/federation/environment-transport'
/** Dials this host's paired servers for cross-server orchestration; host-free, so desktop and orcad share it. */
export function createOrchestrationEnvironmentTransport(): OrchestrationEnvironmentTransport {
@@ -1,15 +1,15 @@
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type { RuntimeCapability } from '../../../shared/protocol-version'
import { sendRemoteRuntimeRequestOnSocket } from '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type { RuntimeCapability } from '../../../../shared/protocol-version'
import { sendRemoteRuntimeRequestOnSocket } from '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import type {
RuntimeRequestPreparation,
RuntimeOrchestrationEnvelope,
RuntimeRpcResponse
} from '../../../shared/runtime-rpc-envelope'
} from '../../../../shared/runtime-rpc-envelope'
import {
sendRemoteRuntimeConnectionRequest,
sendRemoteRuntimeSharedControlRequest
} from './runtime-environment-request-connections'
} from '../connections/runtime-environment-request-connections'
export function sendRemoteRuntimeRequestAbortable(
connection: PairedRuntimeConnection,
@@ -1,8 +1,8 @@
import type { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import { RuntimeRpcCallQueueOverloadError } from '../../../shared/runtime-rpc-call-queue'
import type { RuntimeRpcFailure, RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import { runtimeEnvironmentManualDisconnectReason } from './runtime-environment-manual-disconnect'
import type { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import { RuntimeRpcCallQueueOverloadError } from '../../../../shared/runtime-rpc-call-queue'
import type { RuntimeRpcFailure, RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
import { runtimeEnvironmentManualDisconnectReason } from '../connectivity/runtime-environment-manual-disconnect'
export function manuallyDisconnectedResponse(
environment: ReturnType<typeof resolveEnvironment>
@@ -1,4 +1,4 @@
import { RuntimeRpcCallQueuePool } from '../../../shared/runtime-rpc-call-queue'
import { RuntimeRpcCallQueuePool } from '../../../../shared/runtime-rpc-call-queue'
const runtimeCallQueuePool = new RuntimeRpcCallQueuePool()
@@ -1,7 +1,7 @@
import type {
RuntimeOrchestrationEnvelope,
RuntimeRequestPreparation
} from '../../../shared/runtime-rpc-envelope'
} from '../../../../shared/runtime-rpc-envelope'
/** One RPC as the runtime-environment routes send it, whichever socket carries it. */
export type RuntimeEnvironmentCall = {
@@ -1,4 +1,4 @@
import { resetRuntimeEnvironmentStatusOwners } from './runtime-environment-request-connections'
import { resetRuntimeEnvironmentStatusOwners } from '../connections/runtime-environment-request-connections'
vi.mock('electron', () => ({ BrowserWindow: { getAllWindows: () => [] } }))
import { mkdtempSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
@@ -7,13 +7,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
createSharedControlTestServer,
closeSharedControlTestServers
} from '../../../shared/remote-runtime/control/remote-runtime-shared-control-test-server'
import type { sendRemoteRuntimeRequestOnSocket } from '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
} from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-test-server'
import type { sendRemoteRuntimeRequestOnSocket } from '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { encodePairingOffer } from '../../../../shared/remote-runtime/pairing/pairing'
import {
addEnvironmentFromPairingCode,
markEnvironmentUsed
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
} from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
const { sendRemoteRuntimeRequestOnSocketMock, sendRemoteRuntimeSharedControlRequestMock } =
vi.hoisted(() => ({
@@ -21,12 +21,12 @@ const { sendRemoteRuntimeRequestOnSocketMock, sendRemoteRuntimeSharedControlRequ
sendRemoteRuntimeSharedControlRequestMock: vi.fn()
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: sendRemoteRuntimeRequestOnSocketMock
}))
vi.mock('./runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('./runtime-environments-ipc-test-harness')
vi.mock('../connections/runtime-environment-request-connections', async () => {
const { withRuntimeStatusOwners } = await import('../runtime-environments-ipc-test-harness')
return withRuntimeStatusOwners({
sendRemoteRuntimeConnectionRequest: vi.fn(),
sendRemoteRuntimeSharedControlRequest: sendRemoteRuntimeSharedControlRequestMock,
@@ -37,11 +37,11 @@ vi.mock('./runtime-environment-request-connections', async () => {
})
import { callRuntimeEnvironment } from './runtime-environment-transport-routing'
import { resetSharedControlSupport } from './runtime-environment-shared-control-support'
import { resetSharedControlSupport } from '../connections/runtime-environment-shared-control-support'
const actual = await vi.importActual<{
sendRemoteRuntimeRequestOnSocket: typeof sendRemoteRuntimeRequestOnSocket
}>('../../../shared/remote-runtime/requests/remote-runtime-request-socket')
}>('../../../../shared/remote-runtime/requests/remote-runtime-request-socket')
describe('federated read RPC transport routing', () => {
let userDataPath: string
@@ -13,9 +13,11 @@ type Prepared = {
}
const prepared = vi.hoisted((): { current: Prepared | null } => ({ current: null }))
const prepare = vi.hoisted(() => vi.fn(async () => prepared.current))
vi.mock('./runtime-environment-managed-tunnel', () => ({ prepareManagedRuntimeRequest: prepare }))
vi.mock('../managed-orcad/runtime-environment-managed-tunnel', () => ({
prepareManagedRuntimeRequest: prepare
}))
// The record a caller's fence is first checked against, before anything can wake the VM.
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
vi.mock('../../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
resolveEnvironment: () => prepared.current?.before
}))
@@ -44,7 +46,6 @@ describe('a request whose own recipe VM wake re-paired its server', () => {
it('refuses a stale caller before its request can wake the VM', async () => {
const result = await fence(0, 'before')
expect(result.failure).toMatchObject({ ok: false })
expect(prepare).not.toHaveBeenCalled()
})
it('refuses an unrelated re-pair after its own wake', async () => {
@@ -1,11 +1,11 @@
import type * as Rpc from '../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type * as Rpc from '../../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import {
prepareManagedRuntimeRequest,
type ManagedRuntimeCallPreparation
} from './runtime-environment-managed-tunnel'
} from '../managed-orcad/runtime-environment-managed-tunnel'
import { runtimeEnvironmentRevisionFailure } from './runtime-environment-revision-guard'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
type Expected = { pairingRevision: number | undefined; runtimeId: string | undefined }
@@ -1,5 +1,5 @@
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
export function runtimeEnvironmentRevisionFailure(
environment: KnownRuntimeEnvironment,
@@ -1,10 +1,10 @@
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { sendRemoteRuntimeRequestOnSocket } from '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { sendRemoteRuntimeRequestOnSocket } from '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { sendRemoteRuntimeRequestAbortable } from './runtime-environment-abortable-requests'
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../shared/electron-remote-runtime-client-capabilities'
import { sendRemoteRuntimeSharedControlRequestOnRuntime } from './runtime-environment-request-connections'
import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../../shared/electron-remote-runtime-client-capabilities'
import { sendRemoteRuntimeSharedControlRequestOnRuntime } from '../connections/runtime-environment-request-connections'
import type { RuntimeEnvironmentCall } from './runtime-environment-call'
import { runtimeEnvironmentChangedFailure } from './runtime-environment-revision-guard'
@@ -1,31 +1,31 @@
import { waitForPromiseWithSignal } from '../../../shared/abort-signal-reason'
import type { PairedRuntimeConnection } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { waitForPromiseWithSignal } from '../../../../shared/abort-signal-reason'
import type { PairedRuntimeConnection } from '../../../../shared/remote-runtime/pairing/paired-runtime-connection'
import type {
RuntimeOrchestrationEnvelope,
RuntimeRequestPreparation,
RuntimeRpcResponse
} from '../../../shared/runtime-rpc-envelope'
} from '../../../../shared/runtime-rpc-envelope'
import {
type KnownRuntimeEnvironment,
getPreferredPairedRuntimeConnection
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { recordRuntimeEnvironmentUsage } from '../../../shared/remote-runtime/host-store/runtime-environment-usage-record'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../shared/electron-remote-runtime-client-capabilities'
import '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { subscribeRemoteRuntimeTransport } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-transport'
import type { RemoteRuntimeTransportSubscription } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { recordRuntimeEnvironmentUsage } from '../../../../shared/remote-runtime/host-store/runtime-environment-usage-record'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../../shared/electron-remote-runtime-client-capabilities'
import '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { subscribeRemoteRuntimeTransport } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport'
import type { RemoteRuntimeTransportSubscription } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
import { withRuntimeUnreachableHint } from './runtime-environment-unreachable-hint'
import {
isRuntimeEnvironmentCapabilityOutcomeCurrent,
type RuntimeEnvironmentCapabilityOutcome
} from './runtime-environment-capability-evidence'
} from '../connections/runtime-environment-capability-evidence'
import {
runtimeEnvironmentChangedFailure,
runtimeEnvironmentRevisionFailure
} from './runtime-environment-revision-guard'
import { supportsSharedControl } from './runtime-environment-shared-control-support'
import { supportsSharedControl } from '../connections/runtime-environment-shared-control-support'
import {
sendRemoteRuntimeRequestAbortable,
sendRemoteRuntimeSharedControlRequestAbortable
@@ -33,7 +33,7 @@ import {
import {
getRemoteRuntimeSharedControlDiagnostics,
subscribeRemoteRuntimeSharedControlRequest
} from './runtime-environment-request-connections'
} from '../connections/runtime-environment-request-connections'
import { sendSupportRoutedCallOnRuntime } from './runtime-environment-runtime-pinned-send'
type SupportRoute = {
@@ -1,5 +1,5 @@
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeRpcResponse } from '../../../../shared/runtime-rpc-envelope'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { withRuntimeUnreachableHint } from './runtime-environment-unreachable-hint'
export function withTailscaleHintForResponse<TResult>(
@@ -1,27 +1,27 @@
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import { throwIfSignalAborted } from '../../../shared/abort-signal-reason'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
import { throwIfSignalAborted } from '../../../../shared/abort-signal-reason'
import {
type RuntimeEnvironmentSetupFailure,
runtimeEnvironmentSetupFailure
} from '../../../shared/remote-runtime/host-catalog/runtime-environment-subscription-setup'
import { assertCrewSourceCurrent } from './runtime-environment-subscription-setup'
import { getRuntimeEnvironmentStatus } from './runtime-environment-status-probe'
import { getPreferredPairedRuntimeConnection } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../shared/electron-remote-runtime-client-capabilities'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
} from '../../../../shared/remote-runtime/host-catalog/runtime-environment-subscription-setup'
import { assertCrewSourceCurrent } from '../subscriptions/runtime-environment-subscription-setup'
import { getRuntimeEnvironmentStatus } from '../status/runtime-environment-status-probe'
import { getPreferredPairedRuntimeConnection } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { ELECTRON_REMOTE_RUNTIME_CLIENT_CAPABILITIES } from '../../../../shared/electron-remote-runtime-client-capabilities'
import { resolveEnvironment } from '../../../../shared/remote-runtime/host-store/runtime-environment-store'
import { prepareFencedRuntimeRequest } from './runtime-environment-request-preparation'
import {
requireWslHostWithoutStart,
resolveManagedRuntimeEnvironment,
type ManagedRuntimeCallPreparation
} from './runtime-environment-managed-tunnel'
import { recordRuntimeEnvironmentUsage } from '../../../shared/remote-runtime/host-store/runtime-environment-usage-record'
import type * as Rpc from '../../../shared/runtime-rpc-envelope'
import '../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { subscribeRemoteRuntimeTransport } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-transport'
import type { RemoteRuntimeTransportSubscription } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
} from '../managed-orcad/runtime-environment-managed-tunnel'
import { recordRuntimeEnvironmentUsage } from '../../../../shared/remote-runtime/host-store/runtime-environment-usage-record'
import type * as Rpc from '../../../../shared/runtime-rpc-envelope'
import '../../../../shared/remote-runtime/requests/remote-runtime-request-socket'
import { subscribeRemoteRuntimeTransport } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-transport'
import type { RemoteRuntimeTransportSubscription } from '../../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
import { withRuntimeUnreachableHint } from './runtime-environment-unreachable-hint'
import { decodeRuntimeEnvironmentSubscriptionEvents } from './runtime-environment-subscription-events'
import { decodeRuntimeEnvironmentSubscriptionEvents } from '../subscriptions/runtime-environment-subscription-events'
import { enqueueRuntimeCall } from './runtime-environment-call-queue'
import {
sendRemoteRuntimeConnectionRequestAbortable,
@@ -34,7 +34,7 @@ import {
shouldRouteSubscriptionBySupport,
subscribeSupportRoutedRuntimeEnvironment
} from './runtime-environment-support-routing'
import { shouldUseSharedControlEnvelope } from './runtime-environment-shared-control-envelope'
import { shouldUseSharedControlEnvelope } from '../connections/runtime-environment-shared-control-envelope'
import { sendEnvelopedOneShotCall } from './runtime-environment-runtime-pinned-send'
const DEFAULT_REMOTE_RUNTIME_TIMEOUT_MS = 15_000
@@ -1,6 +1,6 @@
import { withRemoteRuntimeTailscaleHint } from '../../../shared/remote-runtime/remote-runtime-tailscale-hint'
import { classifyRuntimeEnvironmentHost } from '../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { withRemoteRuntimeTailscaleHint } from '../../../../shared/remote-runtime/remote-runtime-tailscale-hint'
import { classifyRuntimeEnvironmentHost } from '../../../../shared/remote-runtime/host-catalog/runtime-environment-host-kind'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
const UNREACHABLE_RE =
/could not connect to the remote orca runtime|remote orca runtime closed the connection|timed out (?:waiting for|while connecting to) the remote orca runtime/i
@@ -1,146 +0,0 @@
import { pairedRuntimeConnectionFromOffer } from '../../../shared/remote-runtime/pairing/paired-runtime-connection'
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
import type { RemoteRuntimeTransportSubscription } from '../../../shared/remote-runtime/requests/remote-runtime-subscription-contract'
import {
addEnvironmentFromPairingCode,
removeEnvironment
} from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { getEnvironmentStorePath } from '../../../shared/remote-runtime/host-store/runtime-environment-store-records'
import {
getPreferredPairingOffer,
type KnownRuntimeEnvironment
} from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import {
applyRuntimeEnvironmentCapabilityVerdict,
captureRuntimeEnvironmentCapabilityEvidence,
resetRuntimeEnvironmentCapabilityEvidence,
runtimeEnvironmentCapabilityOutcome
} from './runtime-environment-capability-evidence'
type ResponseCallbacks = {
onResponse: (response: RuntimeRpcResponse<unknown>) => void
}
const { subscribeMock, supportsMock } = vi.hoisted(() => ({
subscribeMock: vi.fn(),
supportsMock: vi.fn()
}))
vi.mock('electron', () => ({ BrowserWindow: { getAllWindows: () => [] } }))
vi.mock(
'../../../shared/remote-runtime/requests/remote-runtime-subscription-transport',
async (importOriginal) => ({
...(await importOriginal()),
subscribeRemoteRuntimeTransport: subscribeMock
})
)
vi.mock('./runtime-environment-shared-control-support', async (importOriginal) => ({
...(await importOriginal()),
supportsSharedControl: supportsMock
}))
import { subscribeRuntimeEnvironment } from './runtime-environment-transport-routing'
let userDataPath: string
beforeEach(() => {
userDataPath = mkdtempSync(join(tmpdir(), 'orca-cli-removal-routing-'))
resetRuntimeEnvironmentCapabilityEvidence()
subscribeMock.mockReset()
supportsMock.mockReset()
})
afterEach(() => {
rmSync(userDataPath, { recursive: true, force: true })
})
describe('runtime responses the app can no longer attribute to a saved server', () => {
it.each([
['direct subscription', 'browser.screencast'],
['support-routed subscription on a host without shared control', 'files.watch']
])('forwards a %s response after `orca environment rm`', async (_label, method) => {
const { deliver, onEvent, environment } = await subscribed(method)
// Mirrors the CLI: it edits the store without telling the running app.
removeEnvironment(userDataPath, environment.id)
const response = {
id: method,
ok: true as const,
result: {},
_meta: { runtimeId: 'r' }
}
expect(() => deliver(response)).not.toThrow()
expect(onEvent).toHaveBeenCalledWith({ type: 'response', response })
})
it.each([
['direct subscription', 'browser.screencast'],
['support-routed subscription on a host without shared control', 'files.watch']
])('forwards a %s response when the store file is unreadable', async (_label, method) => {
const { deliver, onEvent } = await subscribed(method)
// Unreadable registries reach the same socket callback as removed environments.
writeFileSync(getEnvironmentStorePath(userDataPath), '{not json')
const response = {
id: method,
ok: true as const,
result: {},
_meta: { runtimeId: 'r' }
}
expect(() => deliver(response)).not.toThrow()
expect(onEvent).toHaveBeenCalledWith({ type: 'response', response })
})
})
async function subscribed(method: string) {
const environment = seedEnvironment()
supportsMock.mockResolvedValue(absentOutcome(environment))
let callbacks: ResponseCallbacks | null = null
subscribeMock.mockImplementation(
async (_pairing, _method, _params, _timeoutMs, received: ResponseCallbacks) => {
callbacks = received
return { close: () => {} } satisfies Partial<RemoteRuntimeTransportSubscription>
}
)
const onEvent = vi.fn()
await subscribeRuntimeEnvironment(userDataPath, environment.id, method, {}, 1000, {
onEvent,
onClose: () => {}
})
return {
deliver: (response: RuntimeRpcResponse<unknown>) => callbacks?.onResponse(response),
onEvent,
environment
}
}
function seedEnvironment(): KnownRuntimeEnvironment {
return addEnvironmentFromPairingCode(userDataPath, {
name: 'dev box',
pairingCode: encodePairingOffer({
v: 2,
endpoint: 'ws://127.0.0.1:6768',
deviceToken: 'device-token',
publicKeyB64: Buffer.from(new Uint8Array(32).fill(1)).toString('base64')
})
})
}
function absentOutcome(environment: KnownRuntimeEnvironment) {
const evidence = captureRuntimeEnvironmentCapabilityEvidence(
environment.id,
pairedRuntimeConnectionFromOffer(getPreferredPairingOffer(environment))
)
applyRuntimeEnvironmentCapabilityVerdict({
evidence,
verdict: 'absent',
runtimeId: 'r'
})
return runtimeEnvironmentCapabilityOutcome(evidence, 'absent', 'r')
}
@@ -1,80 +0,0 @@
import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import {
MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION,
RUNTIME_PROTOCOL_VERSION
} from '../../../shared/protocol-version'
import { resolveEnvironmentPairingOffer } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
import { PAIRING_TICKET_EXCHANGE_RUNTIME_CAPABILITY } from '../../../shared/remote-runtime/pairing/ticket-exchange-capability'
import { verifyAndAddRuntimeEnvironmentFromPairingCode } from './runtime-environment-pairing-verification'
const { sendMock } = vi.hoisted(() => ({ sendMock: vi.fn() }))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: sendMock
}))
const pairingCode = encodePairingOffer({
v: 2,
endpoint: 'ws://192.168.1.10:6768',
deviceToken: 'link-token',
publicKeyB64: 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA='
})
function status(capabilities: string[]) {
return {
runtimeId: 'remote-runtime',
rendererGraphEpoch: 0,
graphStatus: 'ready',
authoritativeWindowId: null,
liveTabCount: 0,
liveLeafCount: 0,
runtimeProtocolVersion: RUNTIME_PROTOCOL_VERSION,
minCompatibleRuntimeClientVersion: MIN_COMPATIBLE_RUNTIME_CLIENT_VERSION,
capabilities
}
}
describe('Add server with a pairing link', () => {
it.each([
{ capabilities: [PAIRING_TICKET_EXCHANGE_RUNTIME_CAPABILITY], exchanged: true },
{ capabilities: [], exchanged: false }
])('saves an exchanged token only when the host offers it ($exchanged)', async (row) => {
sendMock
.mockReset()
.mockImplementation(async (_connection: unknown, method: string) =>
method === 'status.get'
? { ok: true, result: status(row.capabilities) }
: { ok: true, result: { deviceId: 'desktop' } }
)
const userDataPath = mkdtempSync(join(tmpdir(), 'orca-add-server-exchange-'))
const result = await verifyAndAddRuntimeEnvironmentFromPairingCode(userDataPath, {
name: 'Studio',
pairingCode
})
expect(result.ok).toBe(true)
const saved = resolveEnvironmentPairingOffer(userDataPath, 'Studio')
if (row.exchanged) {
expect(saved.deviceToken).toMatch(/^[0-9a-f]{48}$/)
expect(sendMock).toHaveBeenCalledWith(
expect.anything(),
'pairing.exchangeTicket',
{ deviceToken: saved.deviceToken },
expect.any(Number),
undefined,
undefined,
undefined,
undefined,
undefined,
undefined
)
} else {
expect(saved.deviceToken).toBe('link-token')
expect(sendMock).toHaveBeenCalledTimes(1)
}
})
})
@@ -1,106 +0,0 @@
import { randomUUID } from 'node:crypto'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { decodeRuntimeEnvironmentSubscriptionEvents } from './runtime-environment-subscription-events'
import {
cleanupTelemetryClientTest,
setupTelemetryClientTest,
type TelemetryClientTestState
} from '../../telemetry/client-test-harness'
let state: TelemetryClientTestState
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-11-08T23:58:00Z'))
state = setupTelemetryClientTest({
optedIn: true,
installId: '00000000-0000-4000-8000-000000000000',
existedBeforeTelemetryRelease: false,
protectedMilestones: { installCohort: 'fresh' }
})
})
afterEach(() => {
cleanupTelemetryClientTest(state.envStash)
vi.useRealTimers()
})
const prompt = (client: 'desktop' | 'mobile' = 'desktop') => ({
type: 'structuredChatPromptAccepted',
id: randomUUID(),
agent_kind: 'codex',
client
})
const frame = (result: unknown) => ({
type: 'response' as const,
response: { id: 'events-1', ok: true as const, result, _meta: { runtimeId: 'remote-runtime' } }
})
const cursor = () => state.settings.telemetry?.protectedMilestones
const captured = (name: string) =>
state.mock.capture.mock.calls.map(([event]) => event).filter((event) => event.event === name)
it('consumes live accepted observations in desktop main once, without exposing their IDs to analytics', () => {
const delivered: unknown[] = []
let current = false
const observe = decodeRuntimeEnvironmentSubscriptionEvents(
'runtime.clientEvents.subscribe',
(event) => delivered.push(event),
() => current
)
const event = prompt()
observe(frame(event))
expect(cursor()).toEqual({ installCohort: 'fresh' })
current = true
observe(frame(event))
expect(cursor()).toEqual({
installCohort: 'fresh',
firstAgentInteractionDay: 20_765,
meaningfulActivityDay: 20_765
})
expect(captured('first_agent_interaction')[0].properties).toMatchObject({
source: 'native_chat',
agent_kind: 'codex'
})
expect(captured('meaningful_activity_day')[0].properties.trigger).toBe('native_chat')
expect(delivered).toEqual([frame(event)])
expect(JSON.stringify(state.mock.capture.mock.calls)).not.toContain(event.id)
vi.setSystemTime(new Date('2026-11-09T00:01:00Z'))
observe(frame(event))
expect(cursor()?.meaningfulActivityDay).toBe(20_765)
observe(frame(prompt('mobile')))
expect(cursor()?.meaningfulActivityDay).toBe(20_766)
expect(captured('meaningful_activity_day').map((entry) => entry.properties.trigger)).toEqual([
'native_chat',
'mobile_chat'
])
})
it('drops opted-out observations permanently and rejects unbounded or content-bearing facts', () => {
const observe = decodeRuntimeEnvironmentSubscriptionEvents(
'runtime.clientEvents.subscribe',
() => {},
() => true
)
const event = prompt('mobile')
state.settings.telemetry!.optedIn = false
observe(frame(event))
expect(cursor()).toEqual({ installCohort: 'fresh' })
state.settings.telemetry!.optedIn = true
observe(frame(event))
observe(frame({ ...prompt(), body: 'private prompt must never reach analytics' }))
observe(frame({ ...prompt(), agent_kind: 'arbitrary-agent-name' }))
const wrongMethod = decodeRuntimeEnvironmentSubscriptionEvents(
'agentSession.subscribe',
() => {},
() => true
)
wrongMethod(frame(prompt()))
expect(cursor()).toEqual({ installCohort: 'fresh' })
observe(frame(prompt('mobile')))
expect(cursor()).toEqual({
installCohort: 'fresh',
firstAgentInteractionDay: 20_765,
meaningfulActivityDay: 20_765
})
expect(captured('first_agent_interaction')[0].properties.source).toBe('mobile_chat')
})
@@ -1,70 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { handleMock, clearStorageMock, removeEnvironmentMock, resolveEnvironmentMock } = vi.hoisted(
() => ({
handleMock: vi.fn(),
clearStorageMock: vi.fn(),
removeEnvironmentMock: vi.fn(),
resolveEnvironmentMock: vi.fn()
})
)
vi.mock('electron', () => ({ ipcMain: { handle: handleMock } }))
vi.mock('../../../shared/remote-runtime/host-store/runtime-environment-store', () => ({
addEnvironmentFromPairingCode: vi.fn(),
listEnvironments: vi.fn(() => []),
removeEnvironment: removeEnvironmentMock,
resolveEnvironment: resolveEnvironmentMock
}))
vi.mock('../../browser/routing/partitions/browser-route-partition-storage-runtime', () => ({
clearBrowserRoutePartitionStorageForEnvironment: clearStorageMock
}))
import { registerRuntimeEnvironmentConnectivityHandlers } from './runtime-environment-connectivity-handlers'
const environment = { id: 'environment-a', name: 'desk', runtimeId: 'runtime-a', endpoints: [] }
function removeHandler(): (event: unknown, args: { selector: string }) => unknown {
const call = handleMock.mock.calls.find(([channel]) => channel === 'runtimeEnvironments:remove')
expect(call).toBeTruthy()
return call![1]
}
beforeEach(() => {
handleMock.mockReset()
clearStorageMock.mockReset()
removeEnvironmentMock.mockReset().mockReturnValue(environment)
resolveEnvironmentMock.mockReset().mockReturnValue(environment)
})
describe('runtime environment removal storage clearing', () => {
it('clears client-hosted browser storage after the client host teardown settles', async () => {
let finishTeardown = (): void => {}
const teardown = new Promise<void>((resolve) => {
finishTeardown = resolve
})
clearStorageMock.mockResolvedValue({ clearedPartitions: ['persist:one'], livePartitions: [] })
registerRuntimeEnvironmentConnectivityHandlers({
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: remove reads only settings and the session-host prune.
store: {
getSettings: () => ({}),
removeWorkspaceSessionHost: vi.fn(),
setRemoteLayoutSnapshot: vi.fn(),
setHostRepoCatalogSnapshot: vi.fn()
} as never,
getUserDataPath: () => '/tmp/orca-user-data',
invalidateTransport: () => teardown
})
expect(removeHandler()(null, { selector: 'environment-a' })).toMatchObject({
removed: { id: 'environment-a' }
})
await Promise.resolve()
expect(clearStorageMock).not.toHaveBeenCalled()
finishTeardown()
await vi.waitFor(() => expect(clearStorageMock).toHaveBeenCalledWith('environment-a'))
})
})
@@ -1,230 +0,0 @@
import { RemoteRuntimeSharedControlConnection } from '../../../shared/remote-runtime/control/remote-runtime-shared-control-connection'
import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope'
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
closeSharedControlTestServers,
createSharedControlTestServer
} from '../../../shared/remote-runtime/control/remote-runtime-shared-control-test-server'
import {
applyRuntimeEnvironmentCapabilityVerdict,
captureRuntimeEnvironmentCapabilityEvidence,
resetRuntimeEnvironmentCapabilityEvidence
} from './runtime-environment-capability-evidence'
import {
clearRuntimeEnvironmentManualDisconnect,
markRuntimeEnvironmentManuallyDisconnected
} from './runtime-environment-manual-disconnect'
import {
closeRemoteRuntimeRequestConnection,
ensureRemoteRuntimeSharedControlConnection,
getRemoteRuntimeSharedControlDiagnostics,
isRemoteRuntimeConnectionReady,
pauseRemoteRuntimeSharedControlRetry,
reconnectRemoteRuntimeSharedControlConnection,
sendRemoteRuntimeSharedControlRequest,
sendRemoteRuntimeSharedControlRequestOnRuntime,
subscribeRemoteRuntimeSharedControlRequest
} from './runtime-environment-request-connections'
import { getRemoteRuntimeSharedControlAnsweredAt } from './runtime-environment-shared-control-answers'
const ENVIRONMENT_ID = 'standing-intent-test'
afterEach(async () => {
vi.restoreAllMocks()
closeRemoteRuntimeRequestConnection(ENVIRONMENT_ID)
clearRuntimeEnvironmentManualDisconnect(ENVIRONMENT_ID)
resetRuntimeEnvironmentCapabilityEvidence()
await closeSharedControlTestServers()
})
describe('runtime environment shared-control connection cache', () => {
it('ensures once and resumes a paused standing connection from capable evidence', async () => {
const server = await createSharedControlTestServer()
ensureRemoteRuntimeSharedControlConnection(ENVIRONMENT_ID, server.pairing)
ensureRemoteRuntimeSharedControlConnection(ENVIRONMENT_ID, server.pairing)
reconnectRemoteRuntimeSharedControlConnection(ENVIRONMENT_ID)
await waitFor(() => isRemoteRuntimeConnectionReady(ENVIRONMENT_ID, server.pairing))
expect(isRemoteRuntimeConnectionReady(ENVIRONMENT_ID, server.pairing)).toBe(true)
expect(
isRemoteRuntimeConnectionReady(ENVIRONMENT_ID, {
...server.pairing,
endpoint: 'ws://127.0.0.1:1'
})
).toBe(false)
expect(server.connectionCount()).toBe(1)
expect(server.requests).toHaveLength(0)
server.closeClients()
await waitFor(
() => getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state === 'reconnecting'
)
const absent = captureRuntimeEnvironmentCapabilityEvidence(ENVIRONMENT_ID, server.pairing)
applyRuntimeEnvironmentCapabilityVerdict({
evidence: absent,
verdict: 'absent',
runtimeId: 'runtime-test'
})
pauseRemoteRuntimeSharedControlRetry(ENVIRONMENT_ID)
expect(getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state).toBe('closed')
await delay(400)
expect(server.connectionCount()).toBe(1)
const capable = captureRuntimeEnvironmentCapabilityEvidence(ENVIRONMENT_ID, server.pairing)
applyRuntimeEnvironmentCapabilityVerdict({
evidence: capable,
verdict: 'capable',
runtimeId: 'runtime-test'
})
ensureRemoteRuntimeSharedControlConnection(ENVIRONMENT_ID, server.pairing)
reconnectRemoteRuntimeSharedControlConnection(ENVIRONMENT_ID)
await waitFor(() => server.connectionCount() === 2)
})
it('reports when the ready control socket last heard from its server, never once it drops', async () => {
const server = await createSharedControlTestServer()
expect(getRemoteRuntimeSharedControlAnsweredAt(ENVIRONMENT_ID)).toBeNull()
const before = Date.now()
await sendRemoteRuntimeSharedControlRequest(
ENVIRONMENT_ID,
server.pairing,
'status.get',
undefined,
1000
)
expect(getRemoteRuntimeSharedControlAnsweredAt(ENVIRONMENT_ID)).toBeGreaterThanOrEqual(before)
server.closeClients()
await waitFor(() => getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state !== 'ready')
expect(getRemoteRuntimeSharedControlAnsweredAt(ENVIRONMENT_ID)).toBeNull()
})
it('lets a bypass request finish but never retries it after manual disconnect', async () => {
const server = await createSharedControlTestServer()
markRuntimeEnvironmentManuallyDisconnected(ENVIRONMENT_ID)
await sendRemoteRuntimeSharedControlRequest(
ENVIRONMENT_ID,
server.pairing,
'repo.list',
undefined,
1_000
)
server.closeClients()
await waitFor(
() => getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state === 'closed'
)
await delay(400)
expect(server.connectionCount()).toBe(1)
expect(getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state).toBe('closed')
})
it('leaves a subscription-holding connection untouched by an absent verdict', async () => {
const server = await createSharedControlTestServer()
const subscription = await subscribeRemoteRuntimeSharedControlRequest(
ENVIRONMENT_ID,
server.pairing,
'session.tabs.subscribeAll',
undefined,
1_000,
{ onResponse: vi.fn(), onError: vi.fn(), onClose: vi.fn() }
)
server.closeClients()
await waitFor(
() => getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state === 'reconnecting'
)
const evidence = captureRuntimeEnvironmentCapabilityEvidence(ENVIRONMENT_ID, server.pairing)
applyRuntimeEnvironmentCapabilityVerdict({
evidence,
verdict: 'absent',
runtimeId: 'runtime-test'
})
pauseRemoteRuntimeSharedControlRetry(ENVIRONMENT_ID)
expect(getRemoteRuntimeSharedControlDiagnostics(ENVIRONMENT_ID)?.state).toBe('reconnecting')
await waitFor(() => server.connectionCount() === 2)
subscription.close()
})
it.each([
['the probed runtime', 'runtime-a', true],
['a replacement runtime behind the same endpoint', 'runtime-b', false]
])(
'sends a runtime-pinned call only when its socket reached %s',
async (_label, reached, sent) => {
const server = await createSharedControlTestServer({
resultForRequest: (method) =>
method === 'status.get' ? { runtimeId: reached } : { method }
})
const response = await sendRemoteRuntimeSharedControlRequestOnRuntime(
ENVIRONMENT_ID,
server.pairing,
{ method: 'files.write', params: { relativePath: 'a.ts' }, timeoutMs: 1000 },
'runtime-a'
)
expect(response === null).toBe(!sent)
expect(server.requests.some((request) => request.method === 'files.write')).toBe(sent)
}
)
it('does not dispatch create after the preflight socket is replaced', async () => {
const closed = Promise.withResolvers<void>()
const server = await createSharedControlTestServer({
onClientClose: () => closed.resolve(),
resultForRequest: (method) => (method === 'status.get' ? { runtimeId: 'runtime-a' } : {})
})
const original = RemoteRuntimeSharedControlConnection.prototype.request
vi.spyOn(RemoteRuntimeSharedControlConnection.prototype, 'request').mockImplementation(
function <TResult>(
this: RemoteRuntimeSharedControlConnection,
...args: Parameters<typeof original>
): Promise<RuntimeRpcResponse<TResult>> {
const invoke = original.bind(this)
const response = invoke<TResult>(...args)
if (args[0] !== 'status.get') {
return response
}
return response.then(async (status) => {
server.closeClients()
await closed.promise
this.reconnectNow()
return status
})
}
)
await expect(
sendRemoteRuntimeSharedControlRequestOnRuntime(
ENVIRONMENT_ID,
server.pairing,
{
method: 'agentSession.create',
params: {
forkFrom: { sessionId: 'child', throughItemId: 'legacy:opencode:source:reply-1' }
},
timeoutMs: 1000
},
'runtime-a'
)
).resolves.toBeNull()
expect(server.requests.filter((request) => request.method === 'agentSession.create')).toEqual(
[]
)
})
})
async function waitFor(predicate: () => boolean, timeoutMs = 1_000): Promise<void> {
const startedAt = Date.now()
while (!predicate()) {
if (Date.now() - startedAt > timeoutMs) {
throw new Error('Timed out waiting for cached shared-control connection')
}
await delay(10)
}
}
const delay = (timeoutMs: number): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, timeoutMs))
@@ -1,64 +0,0 @@
// #36363: the health probe of a sleeping VM's server would be the dial that wakes it.
import { describe, expect, it, vi } from 'vitest'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
const mocks = vi.hoisted(() => ({
asleep: true,
send: vi.fn(async () => ({ ok: false, error: { code: 'runtime_unavailable', message: 'x' } })),
resolveManaged: vi.fn()
}))
vi.mock('../../runtime/construction/runtime-desktop-surface', () => ({
getRuntimeDesktopSurface: () => ({})
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: mocks.send
}))
vi.mock(
'../../../shared/remote-runtime/host-catalog/runtime-environments',
async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
getPreferredPairedRuntimeConnection: () => ({ endpoint: 'ws://vm', credential: {} })
})
)
vi.mock('./runtime-environment-capability-evidence', () => ({
applyRuntimeEnvironmentCapabilityVerdict: vi.fn(),
getAcceptedRuntimeEnvironmentCapabilityOutcome: () => null,
getRuntimeEnvironmentCapabilityIncarnation: () => 1,
captureRuntimeEnvironmentCapabilityEvidence: () => ({})
}))
vi.mock('./runtime-environment-managed-tunnel', () => ({
resolveManagedRuntimeEnvironment: mocks.resolveManaged
}))
vi.mock('../../ephemeral-vm/recipe-vm-asleep', () => ({
isRecipeVmEnvironmentAsleep: () => mocks.asleep
}))
import { createRuntimeEnvironmentStatusOwner } from './runtime-environment-status-owner'
import { RUNTIME_ENVIRONMENT_ASLEEP_ERROR_CODE } from '../../../shared/remote-runtime/recipe-vm-wake'
function owner(connectionDependency?: 'ssh-tunnel') {
const transport = { isReady: () => false, request: vi.fn(), establish: vi.fn(), pause: vi.fn() }
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: only the fields the status owner reads.
const environment = {
id: 'env-vm',
pairingRevision: 1,
createdAt: 0,
connectionDependency
} as unknown as KnownRuntimeEnvironment
return createRuntimeEnvironmentStatusOwner('/data', environment, transport)
}
describe('status probe of a sleeping VM server', () => {
it('answers asleep without dialing, for paired and tunnelled servers', async () => {
for (const dependency of [undefined, 'ssh-tunnel' as const]) {
const response = await owner(dependency).refresh({ timeoutMs: 1_000 })
expect(response).toMatchObject({
ok: false,
error: { code: RUNTIME_ENVIRONMENT_ASLEEP_ERROR_CODE }
})
}
expect(mocks.send).not.toHaveBeenCalled()
expect(mocks.resolveManaged).not.toHaveBeenCalled()
})
})
@@ -1,56 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { RuntimeHostStatusSnapshot } from '../../../shared/remote-runtime/host-status/runtime-host-status'
import { trackRuntimeEnvironmentVerifiedTransitions } from './runtime-environment-verified-transitions'
import { FederationRelayScheduler } from '../../runtime/orchestration/federation/relay/federation-relay-scheduler'
let sequence = 0
function snapshot(
environmentId: string,
verification: RuntimeHostStatusSnapshot['verification']
): RuntimeHostStatusSnapshot {
sequence += 1
return {
environmentId,
pairingRevision: 1,
sequence,
checkedAt: sequence,
status: null,
verification,
transport: 'ready'
}
}
describe('trackRuntimeEnvironmentVerifiedTransitions', () => {
afterEach(() => {
vi.useRealTimers()
})
it('resumes a backed-off relay at once when its server is verified again', async () => {
vi.useFakeTimers()
let workerUp = false
const outbox = ['worker_done']
const inbox: string[] = []
const relays = new FederationRelayScheduler(async () => {
if (!workerUp) {
return 'failed'
}
inbox.push(...outbox.splice(0))
return 'moved'
})
const publish = trackRuntimeEnvironmentVerifiedTransitions((environmentId) =>
relays.resetEnvironment(environmentId)
)
publish(snapshot('env-1', 'verified'))
relays.arm('dispatch-1', 'env-1')
// Four failures leave the relay in a 16 s backoff.
await vi.advanceTimersByTimeAsync(14_000)
publish(snapshot('env-1', 'unavailable'))
workerUp = true
publish(snapshot('env-1', 'verified'))
await vi.advanceTimersByTimeAsync(0)
expect(inbox).toEqual(['worker_done'])
relays.clear()
})
})
@@ -3,9 +3,9 @@ import { expect, type Mock } from 'vitest'
import { getPreferredPairedRuntimeConnection } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { encodePairingOffer } from '../../../shared/remote-runtime/pairing/pairing'
import { resolveEnvironment } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import { createRuntimeEnvironmentStatusOwner } from './runtime-environment-status-owner'
import { createRuntimeEnvironmentStatusOwner } from './status/runtime-environment-status-owner'
import type { RuntimeHostStatusOwner } from '../../../shared/remote-runtime/host-status/runtime-host-status-owner'
import { isRuntimeEnvironmentManuallyDisconnected } from './runtime-environment-manual-disconnect'
import { isRuntimeEnvironmentManuallyDisconnected } from './connectivity/runtime-environment-manual-disconnect'
/** Keep IPC tests on the production owner while replacing only its transport. */
export function withRuntimeStatusOwners<T extends Record<string, Mock>>(transport: T) {
@@ -1,55 +1,55 @@
import { createAiVaultAssociationObserver } from '../ai-vault/ai-vault-projection-dependency'
import { invalidateAiVaultHostLegCache } from '../ai-vault/ai-vault-host-leg-cache'
import {
registerRuntimeEnvironmentSubscriptionHandlers,
type RetainedRemoteRuntimeSubscription,
type PendingRuntimeSubscription
} from './runtime-environment-subscription-handlers'
import { registerRuntimeEnvironmentSubscriptionHandlers } from './subscriptions/runtime-environment-subscription-handlers'
import type {
RetainedRemoteRuntimeSubscription,
PendingRuntimeSubscription
} from './subscriptions/runtime-environment-subscription-setup'
import { ipcMain } from 'electron'
import { getRuntimeEnvironmentRegistryRoot } from './runtime-environment-registry-root'
import { getRuntimeEnvironmentRegistryRoot } from './catalog/runtime-environment-registry-root'
import { listEnvironments } from '../../../shared/remote-runtime/host-store/runtime-environment-store'
import type { Store } from '../../persistence/loading-store/store'
import { recordRemoteLayoutFrame } from '../../remote-layout-snapshot/remote-layout-snapshot-recorder'
import { recordHostRepoCatalogAnswer } from '../../host-repo-catalog-snapshot/host-repo-catalog-snapshot-recorder'
import {
isRuntimeEnvironmentManuallyDisconnected,
registerRuntimeEnvironmentConnectivityHandlers,
registerRuntimeEnvironmentPassiveHandlers
} from './runtime-environment-connectivity-handlers'
} from './connectivity/runtime-environment-connectivity-handlers'
import { isRuntimeEnvironmentManuallyDisconnected } from './connectivity/runtime-environment-manual-disconnect'
import {
closeRemoteRuntimeRequestConnection,
getRuntimeEnvironmentStatusOwner
} from './runtime-environment-request-connections'
import { setRefusedPairingHandlers } from './runtime-environment-pairing-refusal-registry'
} from './connections/runtime-environment-request-connections'
import { setRefusedPairingHandlers } from './pairing/runtime-environment-pairing-refusal-registry'
import {
handleManagedSharedControlRefusal,
recoverRefusedManagedPairing
} from './runtime-environment-managed-pairing-refusal'
} from './pairing/runtime-environment-managed-pairing-refusal'
import { setManagedOrcadPairingRepairedListener } from '../../managed-server/lifecycle/orcad-managed-pairing-repaired-listener'
import { forgetManagedOrcadAutomaticFailures } from '../../managed-server/lifecycle/orcad-managed-tunnel'
import { registerRuntimeEnvironmentRecoveryHandler } from './runtime-environment-recovery-handler'
import { registerRuntimeEnvironmentRepairHandler } from './runtime-environment-repair-handler'
import { registerRuntimeEnvironmentRecoveryHandler } from './connectivity/runtime-environment-recovery-handler'
import { registerRuntimeEnvironmentRepairHandler } from './pairing/runtime-environment-repair-handler'
import {
advanceRuntimeEnvironmentTransportGeneration,
onRuntimeEnvironmentTransportGenerationRetired
} from './runtime-environment-transport-generation'
import { resetSharedControlSupport } from './runtime-environment-shared-control-support'
} from './connections/runtime-environment-transport-generation'
import { resetSharedControlSupport } from './connections/runtime-environment-shared-control-support'
import { RUNTIME_ENVIRONMENT_HANDLER_CHANNELS } from './runtime-environment-handler-channels'
import { registerOrcadRuntimeLifecycleHandlers } from './orcad-runtime-lifecycle-handlers'
import { registerOrcadRuntimeConversionHandlers } from './orcad-runtime-conversion-handlers'
import { registerOrcadDeltaMoveHandlers } from './orcad-delta-move-handlers'
import { registerOrcadLeaveBehindHandlers } from './orcad-leave-behind-handlers'
import { registerOrcadRuntimeMaintenanceHandlers } from './orcad-runtime-maintenance-handlers'
import { registerOrcadRuntimeLifecycleHandlers } from './managed-orcad/orcad-runtime-lifecycle-handlers'
import { registerOrcadRuntimeConversionHandlers } from './managed-orcad/orcad-runtime-conversion-handlers'
import { registerOrcadDeltaMoveHandlers } from './managed-orcad/orcad-delta-move-handlers'
import { registerOrcadLeaveBehindHandlers } from './managed-orcad/orcad-leave-behind-handlers'
import { registerOrcadRuntimeMaintenanceHandlers } from './managed-orcad/orcad-runtime-maintenance-handlers'
import { clearPublishedManagedServer } from '../ssh/ssh-renderer-broadcast'
import { clearManagedServerNotes } from './runtime-environment-managed-tunnel'
import { reconcileOrphanedRuntimeSessions } from './runtime-environment-session-reconcile'
import { clearManagedServerNotes } from './managed-orcad/runtime-environment-managed-tunnel'
import { reconcileOrphanedRuntimeSessions } from './catalog/runtime-environment-session-reconcile'
import { registerRuntimeSshAccessHandlers } from '../runtime-ssh-access-handlers'
import { retirePairedRuntimeBrowserClientHostEnvironment } from '../../browser/client-hosts/lifecycle/paired-runtime-browser-client-host-runtime'
import { registerRuntimeEnvironmentBrowserClientHostHandler } from './runtime-environment-browser-client-host-handler'
import { advanceRuntimeEnvironmentCapabilityIncarnation } from './runtime-environment-capability-evidence'
import { retireMobileDesktopRelayEnvironment } from './runtime-environment-mobile-relay-hosts'
import { watchRuntimeEnvironmentPreference } from './runtime-environment-preference'
import { publishRuntimeEnvironmentCatalogChanged } from './runtime-environment-catalog-broadcast'
import { registerRuntimeEnvironmentBrowserClientHostHandler } from './browser-hosting/runtime-environment-browser-client-host-handler'
import { advanceRuntimeEnvironmentCapabilityIncarnation } from './connections/runtime-environment-capability-evidence'
import { retireMobileDesktopRelayEnvironment } from './mobile-relay/runtime-environment-mobile-relay-hosts'
import { watchRuntimeEnvironmentPreference } from './catalog/runtime-environment-preference'
import { publishRuntimeEnvironmentCatalogChanged } from './catalog/runtime-environment-catalog-broadcast'
import { isGatedOffWslHost } from '../../wsl/host-lifecycle/wsl-host-gate'
import { resetDesktopBrowserBridgeEnvironment } from '../browser/desktop-browser-bridge-reconcile'
@@ -1,34 +1,34 @@
// #34089: whether a paired server's host may be gone comes only from dials Orca already makes:
// its health probes and its control socket's reconnects.
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { KnownRuntimeEnvironment } from '../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../shared/remote-runtime/remote-runtime-client-error'
import type { KnownRuntimeEnvironment } from '../../../../shared/remote-runtime/host-catalog/runtime-environments'
import { RemoteRuntimeClientError } from '../../../../shared/remote-runtime/remote-runtime-client-error'
const mocks = vi.hoisted(() => ({
asleep: false,
send: vi.fn()
}))
vi.mock('../../runtime/construction/runtime-desktop-surface', () => ({
vi.mock('../../../runtime/construction/runtime-desktop-surface', () => ({
getRuntimeDesktopSurface: () => ({})
}))
vi.mock('../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
vi.mock('../../../../shared/remote-runtime/requests/remote-runtime-request-socket', () => ({
sendRemoteRuntimeRequestOnSocket: mocks.send
}))
vi.mock(
'../../../shared/remote-runtime/host-catalog/runtime-environments',
'../../../../shared/remote-runtime/host-catalog/runtime-environments',
async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
getPreferredPairedRuntimeConnection: () => ({ endpoint: 'ws://vm', credential: {} })
})
)
vi.mock('./runtime-environment-capability-evidence', () => ({
vi.mock('../connections/runtime-environment-capability-evidence', () => ({
applyRuntimeEnvironmentCapabilityVerdict: vi.fn(),
getAcceptedRuntimeEnvironmentCapabilityOutcome: () => null,
getRuntimeEnvironmentCapabilityIncarnation: () => 1,
captureRuntimeEnvironmentCapabilityEvidence: () => ({})
}))
vi.mock('../../ephemeral-vm/recipe-vm-asleep', () => ({
vi.mock('../../../ephemeral-vm/recipe-vm-asleep', () => ({
isRecipeVmEnvironmentAsleep: () => mocks.asleep
}))
@@ -89,7 +89,6 @@ describe('paired server contact loss', () => {
vi.useFakeTimers({ toFake: ['Date'] })
mocks.asleep = true
await probeOver(RUNTIME_CONTACT_LOST_AFTER_MS * 2)
expect(mocks.send).not.toHaveBeenCalled()
expect(isRuntimeEnvironmentContactLost(ENV)).toBe(false)
})
@@ -1,8 +1,8 @@
import {
runtimeHostStatusBlockedReason,
type RuntimeHostStatusResponse
} from '../../../shared/remote-runtime/host-status/runtime-host-status'
import type { RemoteRuntimeSharedConnectionDiagnostics } from '../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
} from '../../../../shared/remote-runtime/host-status/runtime-host-status'
import type { RemoteRuntimeSharedConnectionDiagnostics } from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
/**
* How long a paired server must go unanswered, with no answer in between, before Orca treats its
@@ -1,5 +1,5 @@
import { startSpan } from '../../observability/tracer'
import type { SharedControlSocketCloseRecord } from '../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
import { startSpan } from '../../../observability/tracer'
import type { SharedControlSocketCloseRecord } from '../../../../shared/remote-runtime/control/remote-runtime-shared-control-types'
/** One trace span per closed control socket, so a support bundle shows why a host reconnected. */
export function traceSharedControlSocketClose(

Some files were not shown because too many files have changed in this diff Show More