mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 16:02:11 +00:00
* fix(ai-vault): make the merged scan stamp independent of leg order
The all-host merge picked its stamp with a strict `stampMs > latestMs` and
echoed the winning leg's verbatim string. Two legs reporting the same instant
in different legal ISO shapes ("...:05Z" vs "...:05.000Z") therefore resolved
by position in the results array, i.e. by host-enumeration order (local, then
SSH, then runtime). The prior lexicographic max was order-independent, so this
was a regression with no test covering it.
A merge has no single scan instant, so its stamp is derived data rather than
any one leg's string: return the canonical ISO form of the newest accepted
instant. That is order-independent and format-independent, and drops a
variable instead of adding a tie-break branch.
Also share one request resolver between main and the renderer so the
renderer's merged-scope predicate is equivalent to main's routing by
construction, rather than by a comment that overclaimed it.
* docs(ai-vault): scope the merged-predicate comment to the desktop IPC path
The replacement comment still asserted the result is always several hosts'
legs. The paired web transport drops executionHostScope and serves one host,
so 'all' there is a single scan. State that the predicate is deliberately
over-inclusive and why erring the other way would be unsafe.
* test(ai-vault): pin the merged-stamp Date range boundary
new Date(ms).toISOString() throws RangeError outside +/-8.64e15. That is
unreachable only because Date.parse applies TimeClip, so the NaN guard alone
constrains the argument. Nothing pinned that. Dropping the guard now fails
these two cases with the RangeError they exist to prevent.
* refactor(ai-vault): route session-title scope through the shared resolver
The last character-for-character copy of the request-scope default. Leaving
it would make the shared resolver the single source of truth for two of three
sites, which is the drift this change exists to remove. No behavior change.
123 lines
5.2 KiB
TypeScript
123 lines
5.2 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
ALL_EXECUTION_HOSTS_SCOPE,
|
|
LOCAL_EXECUTION_HOST_ID,
|
|
getLocalExecutionHostLabel,
|
|
getRepoExecutionHostId,
|
|
getSettingsFocusedExecutionHostId,
|
|
getWorktreeExecutionHostId,
|
|
normalizeExecutionHostOrder,
|
|
normalizeExecutionHostScope,
|
|
normalizeVisibleExecutionHostIds,
|
|
parseExecutionHostId,
|
|
requestedExecutionHostScope,
|
|
toRuntimeExecutionHostId,
|
|
toSshExecutionHostId
|
|
} from './execution-host'
|
|
|
|
describe('execution host identity', () => {
|
|
// Why: the navigator cases below replace globalThis.navigator; restore it after
|
|
// each test so the stub can't bleed into the rest of the suite.
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
it('normalizes local, SSH, and runtime host ids', () => {
|
|
expect(parseExecutionHostId('local')).toEqual({ kind: 'local', id: 'local' })
|
|
expect(parseExecutionHostId(toSshExecutionHostId('win vm'))).toEqual({
|
|
kind: 'ssh',
|
|
id: 'ssh:win%20vm',
|
|
targetId: 'win vm'
|
|
})
|
|
expect(parseExecutionHostId(toRuntimeExecutionHostId('prod/server'))).toEqual({
|
|
kind: 'runtime',
|
|
id: 'runtime:prod%2Fserver',
|
|
environmentId: 'prod/server'
|
|
})
|
|
})
|
|
|
|
it('labels the local host by platform and by navigator detection', () => {
|
|
expect(getLocalExecutionHostLabel('darwin')).toBe('Local Mac')
|
|
expect(getLocalExecutionHostLabel('win32')).toBe('Local Windows')
|
|
expect(getLocalExecutionHostLabel('linux')).toBe('Local Linux')
|
|
expect(getLocalExecutionHostLabel('freebsd')).toBe('This computer')
|
|
|
|
// With no explicit platform, the label is derived from navigator.userAgent
|
|
// (the path the live host-selector dialog uses).
|
|
vi.stubGlobal('navigator', { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' })
|
|
expect(getLocalExecutionHostLabel()).toBe('Local Windows')
|
|
|
|
vi.stubGlobal('navigator', { userAgent: 'Mozilla/5.0 (X11; Linux x86_64)' })
|
|
expect(getLocalExecutionHostLabel()).toBe('Local Linux')
|
|
|
|
vi.stubGlobal('navigator', { userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)' })
|
|
expect(getLocalExecutionHostLabel()).toBe('Local Mac')
|
|
|
|
// Non-matching userAgent falls through to process.platform; compare against the
|
|
// explicit-platform label so the assertion is deterministic on any CI OS.
|
|
vi.stubGlobal('navigator', { userAgent: 'totally-unknown-agent' })
|
|
expect(getLocalExecutionHostLabel()).toBe(getLocalExecutionHostLabel(process.platform))
|
|
})
|
|
|
|
it('falls back invalid scopes to all hosts', () => {
|
|
expect(normalizeExecutionHostScope(null)).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(normalizeExecutionHostScope('')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(normalizeExecutionHostScope('bogus')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(normalizeExecutionHostScope('ssh:')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(normalizeExecutionHostScope('all')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
})
|
|
|
|
it('defaults an omitted scope request to this host, not a fan-out', () => {
|
|
expect(requestedExecutionHostScope(undefined)).toBe(LOCAL_EXECUTION_HOST_ID)
|
|
expect(requestedExecutionHostScope(null)).toBe(LOCAL_EXECUTION_HOST_ID)
|
|
// An empty or unrecognized scope is a real value, so it still fans out.
|
|
expect(requestedExecutionHostScope('')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(requestedExecutionHostScope('bogus')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(requestedExecutionHostScope('all')).toBe(ALL_EXECUTION_HOSTS_SCOPE)
|
|
expect(requestedExecutionHostScope('ssh:dev%20box')).toBe('ssh:dev%20box')
|
|
})
|
|
|
|
it('normalizes visible host id arrays', () => {
|
|
expect(normalizeVisibleExecutionHostIds(null)).toBeNull()
|
|
expect(normalizeVisibleExecutionHostIds([])).toBeNull()
|
|
expect(normalizeVisibleExecutionHostIds(['local', 'bogus', 'ssh:win%20vm', 'local'])).toEqual([
|
|
'local',
|
|
'ssh:win%20vm'
|
|
])
|
|
})
|
|
|
|
it('normalizes host order arrays', () => {
|
|
expect(normalizeExecutionHostOrder(null)).toEqual([])
|
|
expect(normalizeExecutionHostOrder([])).toEqual([])
|
|
expect(normalizeExecutionHostOrder(['ssh:win%20vm', 'bogus', 'local', 'ssh:win%20vm'])).toEqual(
|
|
['ssh:win%20vm', 'local']
|
|
)
|
|
})
|
|
|
|
it('derives repo ownership from SSH connection ids', () => {
|
|
expect(getRepoExecutionHostId({ connectionId: null })).toBe(LOCAL_EXECUTION_HOST_ID)
|
|
expect(getRepoExecutionHostId({ connectionId: 'ssh-target-1' })).toBe('ssh:ssh-target-1')
|
|
})
|
|
|
|
it('prefers explicit worktree ownership before repo and focused-host fallbacks', () => {
|
|
expect(
|
|
getWorktreeExecutionHostId(
|
|
{ hostId: 'runtime:workspace-owner' },
|
|
{ connectionId: 'repo-owner' },
|
|
'runtime:focused-host'
|
|
)
|
|
).toBe('runtime:workspace-owner')
|
|
expect(
|
|
getWorktreeExecutionHostId({}, { connectionId: 'repo-owner' }, 'runtime:focused-host')
|
|
).toBe('ssh:repo-owner')
|
|
expect(getWorktreeExecutionHostId({}, {}, 'runtime:focused-host')).toBe('runtime:focused-host')
|
|
})
|
|
|
|
it('derives focused host compatibility from active runtime settings', () => {
|
|
expect(getSettingsFocusedExecutionHostId(null)).toBe(LOCAL_EXECUTION_HOST_ID)
|
|
expect(getSettingsFocusedExecutionHostId({ activeRuntimeEnvironmentId: 'runtime-1' })).toBe(
|
|
'runtime:runtime-1'
|
|
)
|
|
})
|
|
})
|