mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
478 lines
18 KiB
JavaScript
478 lines
18 KiB
JavaScript
import {
|
|
copyFileSync,
|
|
existsSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
rmSync,
|
|
utimesSync,
|
|
writeFileSync
|
|
} from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join, resolve } from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
import { runProcessSync } from '@orca/process-host'
|
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
|
|
import {
|
|
shouldReuseCompiledWindowsRustExecutable,
|
|
windowsRustExecutableFileVersion,
|
|
windowsRustExecutableTarget,
|
|
windowsRustExecutableFingerprint
|
|
} from './windows-rust-executable-build.mjs'
|
|
import { findDynamicVcRuntimeImports, readPeImportedDllNames } from './windows-pe-imports.mjs'
|
|
|
|
const itCrossHost = process.platform === 'win32' ? it.skip : it
|
|
const projectRoot = resolve(import.meta.dirname, '../..')
|
|
const WINDOWS_LOCK_CODES = ['EBUSY', 'ENOTEMPTY', 'EPERM']
|
|
|
|
// Windows image/AV locks can outlast exit; retry cleanup then retain temp tree rather than fail launcher assertions.
|
|
function removeFixtureTree(path) {
|
|
try {
|
|
rmSync(path, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })
|
|
} catch (error) {
|
|
if (process.platform !== 'win32' || !WINDOWS_LOCK_CODES.includes(error?.code)) {
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
// Cold Cargo compile/link exceeds unit timeout on hosted Windows; subsequent cases reuse target output.
|
|
function itWindows(name, test) {
|
|
const runner = process.platform === 'win32' ? it : it.skip
|
|
runner(name, { timeout: 300_000 }, test)
|
|
}
|
|
|
|
let sharedLauncherRoot = null
|
|
let sharedLauncherPath = null
|
|
let sharedLauncherBuild = null
|
|
|
|
// Compile once; each launch case gets a private copy so it can own its fixture tree.
|
|
beforeAll(() => {
|
|
if (process.platform !== 'win32') {
|
|
return
|
|
}
|
|
sharedLauncherRoot = mkdtempSync(join(tmpdir(), 'orca cli launcher build '))
|
|
sharedLauncherPath = join(sharedLauncherRoot, 'orca.exe')
|
|
const result = runProcessSync({
|
|
program: process.execPath,
|
|
args: ['config/scripts/build-windows-cli-launcher.mjs'],
|
|
cwd: projectRoot,
|
|
timeoutMs: 300_000
|
|
})
|
|
sharedLauncherBuild = { status: result.code, stdout: result.stdout, stderr: result.stderr }
|
|
if (result.code !== 0) {
|
|
throw new Error(`${result.stdout}\n${result.stderr}`)
|
|
}
|
|
copyFileSync(
|
|
join(projectRoot, 'native', 'windows-cli-launcher', '.build', 'orca.exe'),
|
|
sharedLauncherPath
|
|
)
|
|
}, 300_000)
|
|
|
|
afterAll(() => {
|
|
if (sharedLauncherRoot) {
|
|
removeFixtureTree(sharedLauncherRoot)
|
|
}
|
|
})
|
|
|
|
function copySharedLauncher(target) {
|
|
if (!sharedLauncherPath) {
|
|
throw new Error('shared Windows CLI launcher was not built')
|
|
}
|
|
mkdirSync(dirname(target), { recursive: true })
|
|
copyFileSync(sharedLauncherPath, target)
|
|
}
|
|
|
|
describe('Windows CLI launcher', () => {
|
|
itWindows('reuses the hook host target while invalidating changed inputs', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca-hook-target-'))
|
|
try {
|
|
const hookScript = join('config', 'scripts', 'build-windows-agent-hook.mjs')
|
|
const crate = join('native', 'windows-agent-hook')
|
|
const outputFile = join(crate, '.build', 'orca-agent-hook.exe')
|
|
const outputPath = join(root, outputFile)
|
|
const fingerprintPath = `${outputPath}.sha256`
|
|
// Why the default output: it reuses a warm canonical prebuild; Cargo runs only when that is missing or stale.
|
|
const first = runProcessSync({
|
|
program: process.execPath,
|
|
args: [hookScript],
|
|
cwd: projectRoot,
|
|
timeoutMs: 300_000
|
|
})
|
|
expect(first.code, `${first.stdout}\n${first.stderr}`).toBe(0)
|
|
for (const file of [
|
|
outputFile,
|
|
`${outputFile}.sha256`,
|
|
'package.json',
|
|
hookScript,
|
|
join('config', 'scripts', 'windows-rust-executable-build.mjs'),
|
|
join('config', 'scripts', 'windows-pe-imports.mjs'),
|
|
join('resources', 'build', 'icon.ico'),
|
|
...[
|
|
'src/main.rs',
|
|
'build.rs',
|
|
'Cargo.toml',
|
|
'Cargo.lock',
|
|
'app.manifest',
|
|
'.cargo/config.toml'
|
|
].map((file) => join(crate, ...file.split('/')))
|
|
]) {
|
|
mkdirSync(dirname(join(root, file)), { recursive: true })
|
|
copyFileSync(join(projectRoot, file), join(root, file))
|
|
}
|
|
const fingerprint = readFileSync(fingerprintPath, 'utf8')
|
|
const binary = readFileSync(outputPath)
|
|
// A reuse hit must succeed even when Cargo is unavailable.
|
|
const environment = Object.fromEntries(
|
|
Object.entries(process.env).filter(([key]) => key.toLowerCase() !== 'path')
|
|
)
|
|
environment.PATH = ''
|
|
const invoke = (args = []) =>
|
|
runProcessSync({
|
|
program: process.execPath,
|
|
args: [hookScript, ...args],
|
|
cwd: root,
|
|
env: environment,
|
|
timeoutMs: 10_000
|
|
})
|
|
for (const args of [[], ['--arch', process.arch]]) {
|
|
const reused = invoke(args)
|
|
expect(reused.code, `${reused.stdout}\n${reused.stderr}`).toBe(0)
|
|
expect(reused.stdout).toContain('reusing Windows agent hook')
|
|
expect(readFileSync(fingerprintPath, 'utf8')).toBe(fingerprint)
|
|
expect(readFileSync(outputPath)).toEqual(binary)
|
|
}
|
|
const otherArchitecture = process.arch === 'x64' ? 'arm64' : 'x64'
|
|
const crossTarget = invoke(['--arch', otherArchitecture])
|
|
expect(crossTarget.code).not.toBe(0)
|
|
expect(crossTarget.stderr).toContain('Unable to run cargo')
|
|
const unsupported = invoke(['--arch', 'ia32'])
|
|
expect(unsupported.code).not.toBe(0)
|
|
expect(unsupported.stderr).toContain('Unsupported Windows executable architecture: ia32')
|
|
for (const file of [join(crate, 'src', 'main.rs'), 'package.json']) {
|
|
const path = join(root, file)
|
|
const original = readFileSync(path, 'utf8')
|
|
writeFileSync(fingerprintPath, fingerprint)
|
|
writeFileSync(
|
|
path,
|
|
file === 'package.json'
|
|
? JSON.stringify({ ...JSON.parse(original), version: '99.0.0' })
|
|
: `${original}\n// changed fixture source\n`
|
|
)
|
|
const changed = invoke()
|
|
expect(changed.code).not.toBe(0)
|
|
expect(changed.stderr).toContain('Unable to run cargo')
|
|
writeFileSync(path, original)
|
|
}
|
|
writeFileSync(fingerprintPath, fingerprint)
|
|
expect(invoke().code).toBe(0)
|
|
} finally {
|
|
removeFixtureTree(root)
|
|
}
|
|
})
|
|
|
|
it('reuses restored builds only while all embedded inputs and the release version match', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca-cli-launcher-reuse-'))
|
|
try {
|
|
const inputs = ['main.rs', 'build.rs', 'Cargo.toml', 'app.manifest', 'icon.ico'].map(
|
|
(name) => {
|
|
const path = join(root, name)
|
|
writeFileSync(path, name)
|
|
return path
|
|
}
|
|
)
|
|
const outputPath = join(root, 'orca.exe')
|
|
const fingerprint = windowsRustExecutableFingerprint(inputs, '1.4.214')
|
|
expect(shouldReuseCompiledWindowsRustExecutable(outputPath, fingerprint)).toBe(false)
|
|
writeFileSync(outputPath, 'binary')
|
|
expect(shouldReuseCompiledWindowsRustExecutable(outputPath, fingerprint)).toBe(false)
|
|
writeFileSync(`${outputPath}.sha256`, fingerprint)
|
|
for (const input of inputs) {
|
|
utimesSync(input, new Date(), new Date())
|
|
}
|
|
expect(
|
|
shouldReuseCompiledWindowsRustExecutable(
|
|
outputPath,
|
|
windowsRustExecutableFingerprint(inputs, '1.4.214')
|
|
)
|
|
).toBe(true)
|
|
expect(
|
|
shouldReuseCompiledWindowsRustExecutable(
|
|
outputPath,
|
|
windowsRustExecutableFingerprint(inputs, '1.4.215')
|
|
)
|
|
).toBe(false)
|
|
for (const input of inputs) {
|
|
const original = readFileSync(input)
|
|
writeFileSync(input, 'changed')
|
|
expect(
|
|
shouldReuseCompiledWindowsRustExecutable(
|
|
outputPath,
|
|
windowsRustExecutableFingerprint(inputs, '1.4.214')
|
|
)
|
|
).toBe(false)
|
|
writeFileSync(input, original)
|
|
}
|
|
} finally {
|
|
removeFixtureTree(root)
|
|
}
|
|
})
|
|
|
|
it('reduces a prerelease to the numeric version Windows can record', () => {
|
|
expect(windowsRustExecutableFileVersion('1.4.214-daily.202609281300')).toBe('1.4.214.0')
|
|
expect(windowsRustExecutableFileVersion('1.4.214')).toBe('1.4.214.0')
|
|
for (const version of ['1.4.65535', '1.4', '1.4.214"', undefined]) {
|
|
expect(() => windowsRustExecutableFileVersion(version)).toThrow('Invalid Windows')
|
|
}
|
|
})
|
|
|
|
itWindows(
|
|
'embeds publisher, release version, icon and an unelevated application manifest',
|
|
() => {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca launcher metadata '))
|
|
try {
|
|
const launcherPath = join(root, 'orca.exe')
|
|
expect(sharedLauncherBuild).not.toBeNull()
|
|
expect(
|
|
sharedLauncherBuild.status,
|
|
`${sharedLauncherBuild.stdout}\n${sharedLauncherBuild.stderr}`
|
|
).toBe(0)
|
|
copySharedLauncher(launcherPath)
|
|
const inspect = spawnSync(
|
|
'powershell.exe',
|
|
[
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-Command',
|
|
'[Diagnostics.FileVersionInfo]::GetVersionInfo($env:ORCA_TEST_LAUNCHER) | ConvertTo-Json -Compress'
|
|
],
|
|
{ encoding: 'utf8', env: { ...process.env, ORCA_TEST_LAUNCHER: launcherPath } }
|
|
)
|
|
expect(inspect.status, inspect.stderr).toBe(0)
|
|
const info = JSON.parse(inspect.stdout)
|
|
const { version } = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'))
|
|
expect(info.CompanyName).toBe('Stably AI')
|
|
expect(info.ProductName).toBe('Orca')
|
|
expect(info.FileDescription).toBe('Orca CLI Launcher')
|
|
expect(info.FileVersion).toBe(`${version.split(/[+-]/)[0]}.0`)
|
|
expect(info.ProductVersion).toBe(version)
|
|
const binary = readFileSync(launcherPath)
|
|
expect(binary.includes(Buffer.from('requestedExecutionLevel level="asInvoker"'))).toBe(true)
|
|
const imports = readPeImportedDllNames(binary)
|
|
expect(imports.map((name) => name.toLowerCase())).toContain('kernel32.dll')
|
|
expect(findDynamicVcRuntimeImports(imports)).toEqual([])
|
|
const icon = readFileSync(join(projectRoot, 'resources', 'build', 'icon.ico'))
|
|
const imageSize = icon.readUInt32LE(14)
|
|
const imageOffset = icon.readUInt32LE(18)
|
|
expect(binary.includes(icon.subarray(imageOffset, imageOffset + imageSize))).toBe(true)
|
|
} finally {
|
|
removeFixtureTree(root)
|
|
}
|
|
}
|
|
)
|
|
|
|
itCrossHost('fails closed when the Windows launcher cannot be compiled on this host', () => {
|
|
const outputRoot = mkdtempSync(join(tmpdir(), 'orca cross-host launcher '))
|
|
try {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
['config/scripts/build-windows-cli-launcher.mjs', '--output', join(outputRoot, 'orca.exe')],
|
|
{ cwd: projectRoot, encoding: 'utf8' }
|
|
)
|
|
|
|
expect(result.status).not.toBe(0)
|
|
expect(result.stderr).toContain('Windows CLI launcher')
|
|
expect(result.stderr).toContain('Windows host')
|
|
} finally {
|
|
removeFixtureTree(outputRoot)
|
|
}
|
|
})
|
|
|
|
itWindows('preserves a multiline argument from PowerShell through the native launcher', () => {
|
|
const appRoot = mkdtempSync(join(tmpdir(), 'orca cli launcher '))
|
|
try {
|
|
const resourcesPath = join(appRoot, 'resources')
|
|
const launcherPath = join(resourcesPath, 'bin', 'orca.exe')
|
|
const cliPath = join(resourcesPath, 'app.asar.unpacked', 'out', 'cli', 'index.js')
|
|
mkdirSync(join(resourcesPath, 'bin'), { recursive: true })
|
|
mkdirSync(dirname(cliPath), { recursive: true })
|
|
copyFileSync(process.execPath, join(appRoot, 'Orca.exe'))
|
|
writeFileSync(
|
|
cliPath,
|
|
`process.stdout.write(JSON.stringify({
|
|
argv: process.argv.slice(2),
|
|
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE,
|
|
nodeOptions: process.env.NODE_OPTIONS ?? null,
|
|
orcaNodeOptions: process.env.ORCA_NODE_OPTIONS ?? null
|
|
}))\n`,
|
|
'utf8'
|
|
)
|
|
|
|
copySharedLauncher(launcherPath)
|
|
|
|
const body = 'paragraph one line one\nparagraph one line two\n\nparagraph two'
|
|
const powershell = spawnSync(
|
|
'powershell.exe',
|
|
[
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-Command',
|
|
'& $env:ORCA_TEST_LAUNCHER orchestration send --body $env:ORCA_TEST_BODY --json'
|
|
],
|
|
{
|
|
encoding: 'utf8',
|
|
env: {
|
|
...process.env,
|
|
NODE_OPTIONS: '--no-warnings',
|
|
ORCA_TEST_BODY: body,
|
|
ORCA_TEST_LAUNCHER: launcherPath
|
|
}
|
|
}
|
|
)
|
|
|
|
expect(powershell.status, powershell.stderr).toBe(0)
|
|
expect(JSON.parse(powershell.stdout)).toEqual({
|
|
argv: ['orchestration', 'send', '--body', body, '--json'],
|
|
electronRunAsNode: '1',
|
|
nodeOptions: null,
|
|
orcaNodeOptions: '--no-warnings'
|
|
})
|
|
} finally {
|
|
removeFixtureTree(appRoot)
|
|
}
|
|
})
|
|
|
|
it('cross-compiles only the architectures the orcad template ships', () => {
|
|
expect(windowsRustExecutableTarget(undefined)).toBeNull()
|
|
expect(windowsRustExecutableTarget('x64')).toBe('x86_64-pc-windows-msvc')
|
|
expect(windowsRustExecutableTarget('arm64')).toBe('aarch64-pc-windows-msvc')
|
|
expect(() => windowsRustExecutableTarget('ia32')).toThrow('Unsupported')
|
|
})
|
|
|
|
itWindows('runs a server profile CLI on pinned Node with argv and profile intact', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca server launcher '))
|
|
try {
|
|
const profile = join(root, 'server profile')
|
|
const launcherPath = join(profile, 'cli', 'bin', 'orca.exe')
|
|
const cliPath = join(root, 'server slot', 'out', 'cli', 'index.js')
|
|
mkdirSync(dirname(cliPath), { recursive: true })
|
|
writeFileSync(
|
|
cliPath,
|
|
`process.stdout.write(JSON.stringify({
|
|
argv: process.argv.slice(2),
|
|
profile: process.env.ORCA_USER_DATA_PATH,
|
|
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE ?? null,
|
|
nodeOptions: process.env.NODE_OPTIONS ?? null,
|
|
desktopMarker: process.env.ORCA_WINDOWS_PACKAGED_CLI_LAUNCHER ?? null
|
|
}))\n`,
|
|
'utf8'
|
|
)
|
|
copySharedLauncher(launcherPath)
|
|
writeFileSync(
|
|
join(dirname(launcherPath), 'orca-launcher.cfg'),
|
|
`node=${process.execPath}\ncli=${cliPath}\nuser-data=${profile}\n`
|
|
)
|
|
|
|
const inherited = {
|
|
...process.env,
|
|
ELECTRON_RUN_AS_NODE: '1',
|
|
NODE_OPTIONS: '--require C:\\missing\\preload.js',
|
|
ORCA_USER_DATA_PATH: 'C:\\another\\owner',
|
|
ORCA_WINDOWS_PACKAGED_CLI_LAUNCHER: '1',
|
|
ORCA_TEST_LAUNCHER: launcherPath
|
|
}
|
|
const expected = (body) => ({
|
|
argv: ['orchestration', 'send', '--body', body, '--json'],
|
|
profile,
|
|
electronRunAsNode: null,
|
|
nodeOptions: null,
|
|
desktopMarker: null
|
|
})
|
|
// Windows PowerShell 5.1 itself drops embedded double quotes, so its body has none.
|
|
const shellBody = 'line one & %PATH% ^caret\n\nline three'
|
|
const powershell = spawnSync(
|
|
'powershell.exe',
|
|
[
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-Command',
|
|
'& $env:ORCA_TEST_LAUNCHER orchestration send --body $env:ORCA_TEST_BODY --json'
|
|
],
|
|
{ encoding: 'utf8', env: { ...inherited, ORCA_TEST_BODY: shellBody } }
|
|
)
|
|
expect(powershell.status, powershell.stderr).toBe(0)
|
|
expect(JSON.parse(powershell.stdout)).toEqual(expected(shellBody))
|
|
|
|
const body = 'line one "quoted" \\"escaped\\" & %PATH% ^caret\\\n\nline three\\'
|
|
const direct = spawnSync(launcherPath, expected(body).argv, {
|
|
encoding: 'utf8',
|
|
env: inherited
|
|
})
|
|
expect(direct.status, direct.stderr).toBe(0)
|
|
expect(JSON.parse(direct.stdout)).toEqual(expected(body))
|
|
} finally {
|
|
removeFixtureTree(root)
|
|
}
|
|
})
|
|
|
|
itWindows('survives an inherited environment block containing PATH and Path', () => {
|
|
const appRoot = mkdtempSync(join(tmpdir(), 'orca duplicate path launcher '))
|
|
try {
|
|
const resourcesPath = join(appRoot, 'resources')
|
|
const launcherPath = join(resourcesPath, 'bin', 'orca.exe')
|
|
const cliPath = join(resourcesPath, 'app.asar.unpacked', 'out', 'cli', 'index.js')
|
|
const outputPath = join(appRoot, 'child-result.json')
|
|
const harnessSourcePath = join(
|
|
projectRoot,
|
|
'config',
|
|
'scripts',
|
|
'fixtures',
|
|
'DuplicatePathProcessLauncher.cs'
|
|
)
|
|
const harnessPath = join(appRoot, 'DuplicatePathLauncher.exe')
|
|
mkdirSync(dirname(launcherPath), { recursive: true })
|
|
mkdirSync(dirname(cliPath), { recursive: true })
|
|
copyFileSync(process.execPath, join(appRoot, 'Orca.exe'))
|
|
writeFileSync(
|
|
cliPath,
|
|
`require('node:fs').writeFileSync(process.env.ORCA_TEST_OUTPUT, JSON.stringify({
|
|
electronRunAsNode: process.env.ELECTRON_RUN_AS_NODE,
|
|
pathKeys: Object.keys(process.env).filter((key) => key.toLowerCase() === 'path')
|
|
}))\n`,
|
|
'utf8'
|
|
)
|
|
copySharedLauncher(launcherPath)
|
|
|
|
const compiler = findFrameworkCompiler()
|
|
expect(compiler).not.toBeNull()
|
|
const compileHarness = spawnSync(
|
|
compiler,
|
|
['/nologo', '/target:exe', `/out:${harnessPath}`, harnessSourcePath],
|
|
{ encoding: 'utf8' }
|
|
)
|
|
expect(compileHarness.status, `${compileHarness.stdout}\n${compileHarness.stderr}`).toBe(0)
|
|
|
|
const launch = spawnSync(harnessPath, [launcherPath, outputPath], { encoding: 'utf8' })
|
|
expect(launch.status, `${launch.stdout}\n${launch.stderr}`).toBe(0)
|
|
expect(JSON.parse(readFileSync(outputPath, 'utf8'))).toEqual({
|
|
electronRunAsNode: '1',
|
|
pathKeys: ['PATH', 'Path']
|
|
})
|
|
} finally {
|
|
removeFixtureTree(appRoot)
|
|
}
|
|
})
|
|
})
|
|
|
|
function findFrameworkCompiler() {
|
|
const windowsDirectory = process.env.WINDIR ?? process.env.SystemRoot
|
|
if (!windowsDirectory) {
|
|
return null
|
|
}
|
|
return (
|
|
[
|
|
join(windowsDirectory, 'Microsoft.NET', 'Framework64', 'v4.0.30319', 'csc.exe'),
|
|
join(windowsDirectory, 'Microsoft.NET', 'Framework', 'v4.0.30319', 'csc.exe')
|
|
].find((candidate) => existsSync(candidate)) ?? null
|
|
)
|
|
}
|