mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 09:07:40 +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>
257 lines
10 KiB
TypeScript
257 lines
10 KiB
TypeScript
import { spawnSync } from 'node:child_process'
|
||
import {
|
||
chmodSync,
|
||
existsSync,
|
||
mkdirSync,
|
||
mkdtempSync,
|
||
rmSync,
|
||
statSync,
|
||
writeFileSync
|
||
} from 'node:fs'
|
||
import { tmpdir } from 'node:os'
|
||
import { join } from 'node:path'
|
||
import { afterEach, describe, expect, it } from 'vitest'
|
||
import {
|
||
getPosixClaudeShellFunction,
|
||
getFishClaudeShellFunction,
|
||
getPowerShellClaudeShellFunction
|
||
} from './claude-shell-function'
|
||
// Skip missing zsh/fish instead of failing with exit 127 on CI images.
|
||
const POSIX_SHELLS = ['/bin/bash', '/bin/zsh'].filter((shell) => existsSync(shell))
|
||
const FISH = ['/opt/homebrew/bin/fish', '/usr/local/bin/fish', '/usr/bin/fish'].find(existsSync)
|
||
const SHELLS = [...POSIX_SHELLS, ...(FISH ? [FISH] : [])]
|
||
const roots: string[] = []
|
||
afterEach(() => {
|
||
roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }))
|
||
})
|
||
function fixture() {
|
||
const root = mkdtempSync(join(tmpdir(), 'claude-shell-'))
|
||
roots.push(root)
|
||
const a = join(root, 'account a ü')
|
||
const b = join(root, 'account b')
|
||
const bin = join(root, 'bin')
|
||
for (const dir of [a, b, bin]) {
|
||
mkdirSync(dir)
|
||
}
|
||
const fake = join(bin, 'claude')
|
||
writeFileSync(
|
||
fake,
|
||
'#!/bin/sh\nprintf "HOME=%s KEY=%s TWIN=%s\\n" "${CLAUDE_CONFIG_DIR-default}" "${ANTHROPIC_API_KEY-none}" "${ORCA_CLAUDE_INJECTED_CONFIG_DIR-none}"\nexit 23\n'
|
||
)
|
||
chmodSync(fake, 0o700)
|
||
const pointer = join(root, 'selected')
|
||
const run = (shell: string, env: string[] = [], fakeKind: 'summary' | 'printenv' = 'summary') => {
|
||
if (fakeKind === 'printenv') {
|
||
writeFileSync(fake, '#!/bin/sh\nenv\n')
|
||
}
|
||
const fish = shell.endsWith('fish')
|
||
const fn = fish ? getFishClaudeShellFunction() : getPosixClaudeShellFunction()
|
||
// Abort if system config shadows the fake with a real claude binary.
|
||
const guard = fish
|
||
? `test (command -s claude) = '${fake}'; or exit 97`
|
||
: `[ "$(command -v claude)" = '${fake}' ] || exit 97`
|
||
const result = spawnSync(
|
||
'/usr/bin/env',
|
||
[
|
||
'-i',
|
||
`HOME=${root}`,
|
||
`PATH=${bin}:/usr/bin:/bin`,
|
||
`ORCA_CLAUDE_PROFILE_POINTER=${pointer}`,
|
||
'ANTHROPIC_API_KEY=fake',
|
||
...env,
|
||
shell,
|
||
...(fish ? ['--no-config'] : shell.endsWith('zsh') ? ['-f'] : ['--norc', '--noprofile']),
|
||
'-c',
|
||
`${guard}\n${fn}\nclaude`
|
||
],
|
||
{ encoding: 'utf8', cwd: root }
|
||
)
|
||
expect(result.status).not.toBe(97)
|
||
expect(result.stderr).toBe('')
|
||
return result
|
||
}
|
||
return { a, b, pointer, run }
|
||
}
|
||
const injected = (home: string) => [
|
||
`CLAUDE_CONFIG_DIR=${home}`,
|
||
`ORCA_CLAUDE_INJECTED_CONFIG_DIR=${home}`
|
||
]
|
||
|
||
describe.each(SHELLS)('the claude function in %s', (shell) => {
|
||
it("re-reads the selection on every launch and keeps the shell's auth for an account", () => {
|
||
const f = fixture()
|
||
writeFileSync(f.pointer, f.a)
|
||
expect(f.run(shell, injected(f.b)).stdout).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
writeFileSync(f.pointer, f.b)
|
||
const next = f.run(shell, injected(f.a))
|
||
expect(next.stdout).toBe(`HOME=${f.b} KEY=fake TWIN=${f.b}\n`)
|
||
expect(next.status).toBe(23)
|
||
})
|
||
|
||
it("passes a shell proxy's key and address through to an account", () => {
|
||
const f = fixture()
|
||
writeFileSync(f.pointer, f.a)
|
||
const env = [
|
||
'ANTHROPIC_API_KEY=proxy-key',
|
||
'ANTHROPIC_AUTH_TOKEN=proxy-token',
|
||
'ANTHROPIC_BASE_URL=https://proxy.example.test',
|
||
'ANTHROPIC_CUSTOM_HEADERS=Authorization: Bearer x'
|
||
]
|
||
const printed = f.run(shell, env, 'printenv').stdout
|
||
for (const line of [...env, `CLAUDE_CONFIG_DIR=${f.a}`]) {
|
||
expect(printed).toContain(`${line}\n`)
|
||
}
|
||
})
|
||
|
||
it('runs System default for an empty or missing selection, dropping only Orca’s own value', () => {
|
||
const f = fixture()
|
||
expect(f.run(shell).stdout).toBe('HOME=default KEY=fake TWIN=none\n')
|
||
expect(f.run(shell, injected(f.a)).stdout).toBe('HOME=default KEY=fake TWIN=none\n')
|
||
writeFileSync(f.pointer, '')
|
||
expect(f.run(shell, injected(f.a)).stdout).toBe('HOME=default KEY=fake TWIN=none\n')
|
||
})
|
||
|
||
it('restores the user’s own value Orca replaced when switched back to System default', () => {
|
||
const f = fixture()
|
||
const own = ['ORCA_CLAUDE_USER_CONFIG_DIR=/user/own']
|
||
writeFileSync(f.pointer, f.a)
|
||
const account = f.run(shell, [...injected(f.a), ...own])
|
||
expect(account.stdout).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
writeFileSync(f.pointer, '')
|
||
const restored = f.run(shell, [...injected(f.a), ...own])
|
||
expect(restored.stdout).toBe('HOME=/user/own KEY=fake TWIN=none\n')
|
||
const rc = ['CLAUDE_CONFIG_DIR=/rc/own', `ORCA_CLAUDE_INJECTED_CONFIG_DIR=${f.a}`, ...own]
|
||
expect(f.run(shell, rc).stdout).toBe(`HOME=/rc/own KEY=fake TWIN=${f.a}\n`)
|
||
const unset = f.run(shell, [`ORCA_CLAUDE_INJECTED_CONFIG_DIR=${f.a}`, ...own])
|
||
expect(unset.stdout).toBe('HOME=default KEY=fake TWIN=none\n')
|
||
})
|
||
|
||
it('reads a WSL pane’s home-relative pointer against $HOME', () => {
|
||
const f = fixture()
|
||
writeFileSync(f.pointer, f.a)
|
||
const relative = f.run(shell, ['ORCA_CLAUDE_PROFILE_POINTER=~/selected'])
|
||
expect(relative.stdout).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
})
|
||
|
||
it('creates a missing account folder and runs Claude in it, printing nothing', () => {
|
||
const f = fixture()
|
||
const fresh = join(f.a, 'claude-profiles', 'id', 'home')
|
||
writeFileSync(f.pointer, fresh)
|
||
const result = f.run(shell)
|
||
expect(result.stdout).toBe(`HOME=${fresh} KEY=fake TWIN=${fresh}\n`)
|
||
expect(result.status).toBe(23)
|
||
expect(existsSync(fresh)).toBe(true)
|
||
// Match private login-folder permissions.
|
||
expect(statSync(fresh).mode & 0o777).toBe(0o700)
|
||
})
|
||
|
||
it('still runs Claude only in the selected folder when that folder cannot be created', () => {
|
||
const f = fixture()
|
||
const blocked = join(f.a, 'file', 'home')
|
||
writeFileSync(join(f.a, 'file'), '')
|
||
writeFileSync(f.pointer, blocked)
|
||
expect(f.run(shell).stdout).toBe(`HOME=${blocked} KEY=fake TWIN=${blocked}\n`)
|
||
})
|
||
|
||
it('runs nothing for a pointer that names no absolute folder', () => {
|
||
const f = fixture()
|
||
writeFileSync(f.pointer, 'not-a-folder')
|
||
const result = f.run(shell)
|
||
expect(result.status).toBe(1)
|
||
expect(result.stdout).toBe('')
|
||
expect(existsSync(join(f.a, '..', 'not-a-folder'))).toBe(false)
|
||
})
|
||
|
||
it('follows the selection over the user’s own CLAUDE_CONFIG_DIR, inherited or exported by an rc', () => {
|
||
const f = fixture()
|
||
// Why the slash: Claude keys its login by the exact string, so System default keeps the spelling.
|
||
const own = ['CLAUDE_CONFIG_DIR=/user/own/', 'ORCA_CLAUDE_USER_CONFIG_DIR=/user/own/']
|
||
writeFileSync(f.pointer, f.a)
|
||
expect(f.run(shell, own).stdout).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
const rc = [...own, `ORCA_CLAUDE_INJECTED_CONFIG_DIR=${f.b}`]
|
||
expect(f.run(shell, rc).stdout).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
writeFileSync(f.pointer, '')
|
||
expect(f.run(shell, own).stdout).toBe('HOME=/user/own/ KEY=fake TWIN=none\n')
|
||
writeFileSync(f.pointer, f.a)
|
||
expect(
|
||
f.run(shell, ['CLAUDE_CONFIG_DIR=/user/own', 'ORCA_CLAUDE_USER_CONFIG_DIR=/user/own/']).stdout
|
||
).toBe(`HOME=${f.a} KEY=fake TWIN=${f.a}\n`)
|
||
const deliberate = [
|
||
'CLAUDE_CONFIG_DIR=/somewhere/else',
|
||
'ORCA_CLAUDE_USER_CONFIG_DIR=/user/own'
|
||
]
|
||
writeFileSync(f.pointer, f.a)
|
||
expect(f.run(shell, deliberate).stdout).toBe('HOME=/somewhere/else KEY=fake TWIN=none\n')
|
||
})
|
||
|
||
it('lets a CLAUDE_CONFIG_DIR win silently when Orca knows no user value', () => {
|
||
const f = fixture()
|
||
const own = f.run(shell, ['CLAUDE_CONFIG_DIR=/user/own'])
|
||
expect(own.stdout).toBe('HOME=/user/own KEY=fake TWIN=none\n')
|
||
expect(own.stderr).toBe('')
|
||
writeFileSync(f.pointer, f.a)
|
||
const overridden = f.run(shell, ['CLAUDE_CONFIG_DIR=/user/own'])
|
||
expect(overridden.stdout).toBe('HOME=/user/own KEY=fake TWIN=none\n')
|
||
expect(overridden.stderr).toBe('')
|
||
})
|
||
})
|
||
|
||
it("leaves the shell's Anthropic auth alone in the PowerShell function", () => {
|
||
const script = getPowerShellClaudeShellFunction()
|
||
expect(script).toContain("$names = @('CLAUDE_CONFIG_DIR', 'ORCA_CLAUDE_INJECTED_CONFIG_DIR')")
|
||
expect(script).not.toMatch(/ANTHROPIC|CLAUDE_CODE_OAUTH_TOKEN|AWS_BEARER_TOKEN_BEDROCK/)
|
||
})
|
||
|
||
it('restores PowerShell process env without creating empty variables', () => {
|
||
const script = getPowerShellClaudeShellFunction()
|
||
expect(script.startsWith('$orcaClaudeCommand = Get-Command claude')).toBe(true)
|
||
expect(script).toContain('finally {')
|
||
// .NET 9+ turns a $null/'' SetEnvironmentVariable into an empty variable, not a removal.
|
||
expect(script).not.toMatch(/SetEnvironmentVariable\([^)]*,\s*(\$null|''|"")\s*,/)
|
||
})
|
||
|
||
it('prints no Orca text from the PowerShell function and creates a missing account folder', () => {
|
||
const script = getPowerShellClaudeShellFunction()
|
||
expect(script).not.toMatch(/Write-(Host|Output|Warning)|\bthrow\b|Orca:/)
|
||
expect(script.match(/Write-Error/g)).toHaveLength(1)
|
||
expect(script).toContain(
|
||
'} catch { $global:LASTEXITCODE = 1; Write-Error $_ -ErrorAction Continue }'
|
||
)
|
||
expect(script).toContain(
|
||
'if (-not [IO.Directory]::Exists($orcaClaudeHome)) { $null = New-Item -ItemType Directory -Path $orcaClaudeHome -Force -ErrorAction SilentlyContinue }'
|
||
)
|
||
expect(script).toContain('-not [IO.Path]::IsPathRooted($orcaClaudeHome)')
|
||
})
|
||
|
||
it('escapes its arguments for the resolved PowerShell binary before splatting them', () => {
|
||
expect(getPowerShellClaudeShellFunction()).toContain(
|
||
'$claudeArgs = @(__OrcaEscapeNativeArgs $binary.Source $args)'
|
||
)
|
||
})
|
||
|
||
it('trims the PowerShell pointer read as POSIX command substitution does', () => {
|
||
expect(getPowerShellClaudeShellFunction()).toContain(
|
||
'[IO.File]::ReadAllText($env:ORCA_CLAUDE_PROFILE_POINTER).TrimEnd()'
|
||
)
|
||
})
|
||
|
||
it('restores the user’s own value on PowerShell System default rather than removing it', () => {
|
||
const script = getPowerShellClaudeShellFunction()
|
||
expect(script).toContain(
|
||
'if ($env:CLAUDE_CONFIG_DIR -and $env:ORCA_CLAUDE_USER_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR = $env:ORCA_CLAUDE_USER_CONFIG_DIR }'
|
||
)
|
||
expect(script).not.toContain(
|
||
'Remove-Item Env:CLAUDE_CONFIG_DIR, Env:ORCA_CLAUDE_INJECTED_CONFIG_DIR'
|
||
)
|
||
})
|
||
|
||
it('follows the PowerShell selection over the user’s own value, whatever its separators', () => {
|
||
const script = getPowerShellClaudeShellFunction()
|
||
expect(script).toContain(
|
||
`$own = ("$env:ORCA_CLAUDE_USER_CONFIG_DIR" -replace '/', '\\').TrimEnd('\\')`
|
||
)
|
||
expect(script).toContain(
|
||
`(-not $own -or ("$env:CLAUDE_CONFIG_DIR" -replace '/', '\\').TrimEnd('\\') -ne $own)) {`
|
||
)
|
||
})
|