fix(quality): enforce performance-safe lint baseline (#11074)
* fix(quality): clear safe existing lint findings * fix(quality): keep lint cleanup allocation-free * fix(quality): enforce performance-safe baseline * test(terminal): drain deferred confirmation cleanup
This commit is contained in:
parent
9a3b348e82
commit
badf91101b
|
|
@ -32,6 +32,9 @@ jobs:
|
|||
- name: Lint
|
||||
run: pnpm exec oxlint --format github
|
||||
|
||||
- name: Enforce full code-quality baseline
|
||||
run: pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware
|
||||
|
||||
- name: Check switch exhaustiveness
|
||||
run: pnpm run lint:switch-exhaustiveness
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,16 @@
|
|||
},
|
||||
"rules": {
|
||||
"typescript/await-thenable": "warn",
|
||||
"typescript/no-floating-promises": "warn",
|
||||
"typescript/no-misused-promises": "warn",
|
||||
"typescript/only-throw-error": "warn",
|
||||
"typescript/restrict-plus-operands": "warn",
|
||||
"typescript/restrict-template-expressions": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.test.*", "**/*.spec.*"],
|
||||
"rules": {
|
||||
"typescript/await-thenable": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": ["**/node_modules", "**/dist", "**/out"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
"restriction": "off",
|
||||
"nursery": "off"
|
||||
},
|
||||
"options": {
|
||||
"reportUnusedDisableDirectives": "warn"
|
||||
},
|
||||
"jsPlugins": [
|
||||
{
|
||||
"name": "app-store-performance",
|
||||
|
|
@ -29,15 +26,7 @@
|
|||
"import/no-cycle": ["warn", { "maxDepth": 3 }],
|
||||
"import/no-duplicates": "warn",
|
||||
"import/no-self-import": "warn",
|
||||
"max-params": ["warn", 5],
|
||||
"no-fallthrough": "warn",
|
||||
"no-loop-func": "warn",
|
||||
"no-promise-executor-return": "warn",
|
||||
"no-unmodified-loop-condition": "warn",
|
||||
"preserve-caught-error": "warn",
|
||||
"promise/no-multiple-resolved": "warn",
|
||||
"react/no-unstable-nested-components": "warn",
|
||||
"react/react-compiler": "warn"
|
||||
"no-fallthrough": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
@ -55,9 +44,7 @@
|
|||
"rules": {
|
||||
"vitest/no-conditional-tests": "warn",
|
||||
"vitest/no-focused-tests": "warn",
|
||||
"vitest/no-identical-title": "warn",
|
||||
"vitest/valid-expect": "warn",
|
||||
"vitest/valid-title": "warn"
|
||||
"vitest/no-identical-title": "warn"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,11 +12,10 @@
|
|||
// other work would be a regression, not a win.
|
||||
//
|
||||
// Run with: node config/scripts/claude-usage-yield-benchmark.mjs
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||||
import { homedir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import { readdirSync, statSync } from 'node:fs'
|
||||
|
||||
const REPO_ROOT = new URL('../..', import.meta.url)
|
||||
const ROUNDS = Number(process.env.ORCA_YIELD_BENCH_ROUNDS ?? '10')
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ try {
|
|||
// Each case is (label, argv, env). The runtime-dependent ones point at an
|
||||
// empty user-data dir so both arms get the same deterministic answer.
|
||||
const isolated = { ORCA_USER_DATA_PATH: userDataPath }
|
||||
/** @type {Array<[string, string[], Record<string, string>]>} */
|
||||
const cases = [
|
||||
['orca --help', ['--help'], {}],
|
||||
['orca help worktree', ['help', 'worktree'], {}],
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ export async function setupTerminal(page, repoPath, logPhase) {
|
|||
return pane?.container?.dataset?.ptyId ?? null
|
||||
})
|
||||
)
|
||||
logPhase('setup.pty-bound', `ptyId=${ptyId}`)
|
||||
logPhase('setup.pty-bound', `ptyId=${String(ptyId)}`)
|
||||
return ptyId
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ describe('Windows SSH relay node-pty console-list patch', () => {
|
|||
|
||||
const tamperedPatch = writeNodePtyFixture('1.1.0', publishedAgentSource())
|
||||
patchNodePtyConsoleListAgent(tamperedPatch.root)
|
||||
writeFileSync(tamperedPatch.agentPath, `${readFileSync(tamperedPatch.agentPath)}\n// drift`)
|
||||
writeFileSync(
|
||||
tamperedPatch.agentPath,
|
||||
`${readFileSync(tamperedPatch.agentPath, 'utf8')}\n// drift`
|
||||
)
|
||||
expect(() => assertPatchedNodePtyConsoleListAgent(tamperedPatch.root)).toThrow('not installed')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ export async function publishCompleteDraftReleases({
|
|||
|
||||
for (const release of candidates) {
|
||||
const tag = release.tag_name
|
||||
if (!(await isDraftBuiltFromCurrentRef({ tag, release }))) {
|
||||
if (!(await Promise.resolve(isDraftBuiltFromCurrentRef({ tag, release })))) {
|
||||
const reason = 'tag is not built from the current release ref'
|
||||
skipped.push({ tag, reason })
|
||||
log(`Skipping stale RC draft release ${tag}: ${reason}`)
|
||||
|
|
|
|||
|
|
@ -572,7 +572,7 @@ async function main() {
|
|||
if (options.output) {
|
||||
mkdirSync(path.dirname(path.resolve(options.output)), { recursive: true })
|
||||
writeFileSync(options.output, `${JSON.stringify(report, null, 2)}\n`)
|
||||
console.log(`[idle-cpu] wrote ${options.output}`)
|
||||
console.log(`[idle-cpu] wrote ${String(options.output)}`)
|
||||
}
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { useState, useEffect, useRef, useCallback, useMemo } from 'react'
|
||||
import { Animated, AppState, Linking, type AppStateStatus } from 'react-native'
|
||||
import * as Clipboard from 'expo-clipboard'
|
||||
import {
|
||||
Animated,
|
||||
AppState,
|
||||
Linking,
|
||||
type AppStateStatus,
|
||||
BackHandler,
|
||||
FlatList,
|
||||
Image,
|
||||
|
|
@ -17,6 +19,7 @@ import {
|
|||
type LayoutChangeEvent,
|
||||
type ListRenderItem
|
||||
} from 'react-native'
|
||||
import * as Clipboard from 'expo-clipboard'
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router'
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
|
|
|
|||
|
|
@ -106,11 +106,9 @@ import {
|
|||
} from '../../../src/tasks/setup-hook-trust'
|
||||
import { colors, radii, spacing, typography } from '../../../src/theme/mobile-theme'
|
||||
import { triggerMediumImpact } from '../../../src/platform/haptics'
|
||||
import type {
|
||||
GitHubProjectSortDirection,
|
||||
GitHubProjectTable as SharedGitHubProjectTable
|
||||
} from '../../../src/tasks/mobile-github-project-group-sort'
|
||||
import {
|
||||
type GitHubProjectSortDirection,
|
||||
type GitHubProjectTable as SharedGitHubProjectTable,
|
||||
groupRows,
|
||||
isIterationCurrent,
|
||||
sortRows,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import type { RpcSuccess } from '../transport/types'
|
||||
import type { ConnectionState, RpcSuccess } from '../transport/types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { createBotAuthorOverrideSet } from '../../../src/shared/pr-bot-author-overrides'
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
"main": "./out/main/index.js",
|
||||
"scripts": {
|
||||
"format": "oxfmt --write .",
|
||||
"lint": "oxlint && pnpm run lint:switch-exhaustiveness && node config/scripts/check-styled-scrollbars.mjs && pnpm run check:quadratic-buffer-concat && pnpm run check:reliability-gates && pnpm run check:max-lines-ratchet && pnpm run verify:bundled-skill-guides && pnpm run verify:skill-bundle-manifest && pnpm run verify:localization-catalog && pnpm run verify:localization-coverage",
|
||||
"lint": "oxlint && pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run lint:switch-exhaustiveness && node config/scripts/check-styled-scrollbars.mjs && pnpm run check:quadratic-buffer-concat && pnpm run check:reliability-gates && pnpm run check:max-lines-ratchet && pnpm run verify:bundled-skill-guides && pnpm run verify:skill-bundle-manifest && pnpm run verify:localization-catalog && pnpm run verify:localization-coverage",
|
||||
"audit:code-quality": "pnpm run audit:code-quality:native && pnpm run audit:code-quality:type-aware && pnpm run audit:react-doctor",
|
||||
"audit:code-quality:native": "oxlint --config config/oxlint-code-quality.json --report-unused-disable-directives-severity warn src config tests mobile",
|
||||
"audit:code-quality:type-aware": "oxlint --type-aware --config config/oxlint-code-quality-type-aware.json src config tests mobile",
|
||||
"audit:code-quality:native": "oxlint --config config/oxlint-code-quality.json src config tests mobile --deny-warnings",
|
||||
"audit:code-quality:type-aware": "oxlint --type-aware --config config/oxlint-code-quality-type-aware.json src config tests --deny-warnings",
|
||||
"audit:react-doctor": "pnpm dlx react-doctor@0.9.1 . --yes --no-supply-chain --no-telemetry --blocking none",
|
||||
"check:code-quality:changed": "node config/scripts/check-changed-code-quality.mjs",
|
||||
"check:react-doctor:changed": "node config/scripts/check-react-doctor-changed.mjs",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { RuntimeClientError } from './runtime/types'
|
||||
import { unknownCommandData, unknownFlagData } from './command-suggestion'
|
||||
import { specPaths, type CommandSpec } from './command-spec'
|
||||
|
||||
export { specPaths }
|
||||
export type { CommandSpec }
|
||||
|
||||
export type ParsedArgs = {
|
||||
commandPath: string[]
|
||||
|
|
@ -7,23 +11,6 @@ export type ParsedArgs = {
|
|||
positionalFlagConflicts?: string[]
|
||||
}
|
||||
|
||||
export type CommandSpec = {
|
||||
path: string[]
|
||||
// Why: conventional alternate verbs should resolve without duplicating specs
|
||||
// or handler registrations.
|
||||
aliases?: string[][]
|
||||
argumentMode?: 'parsed' | 'passthrough'
|
||||
// Why: irreversibly destroys persistent state — typo recovery must not steer a
|
||||
// benign mistake into one of these via the agent nextSteps channel. #6303
|
||||
destructive?: boolean
|
||||
summary: string
|
||||
usage: string
|
||||
allowedFlags: string[]
|
||||
positionalArgs?: string[]
|
||||
examples?: string[]
|
||||
notes?: string[]
|
||||
}
|
||||
|
||||
export const GLOBAL_FLAGS = ['help', 'json', 'pairing-code', 'environment']
|
||||
const GLOBAL_VALUE_FLAGS = new Set(['pairing-code', 'environment'])
|
||||
export const BOOLEAN_FLAGS = new Set([
|
||||
|
|
@ -157,12 +144,6 @@ export function matches(actual: string[], expected: string[]): boolean {
|
|||
)
|
||||
}
|
||||
|
||||
// Why: a spec is reachable by its canonical path plus any declared aliases — one
|
||||
// definition so resolution, validation, help, and agent-context never disagree.
|
||||
export function specPaths(spec: CommandSpec): string[][] {
|
||||
return spec.aliases ? [spec.path, ...spec.aliases] : [spec.path]
|
||||
}
|
||||
|
||||
export function supportsBrowserPageFlag(commandPath: string[]): boolean {
|
||||
const joined = commandPath.join(' ')
|
||||
if (['open', 'status'].includes(commandPath[0])) {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ describe('orca cli browser page targeting', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('orca cli browser tab profiles', () => {
|
||||
describe('orca cli browser profile management', () => {
|
||||
beforeEach(() => {
|
||||
callMock.mockReset()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
export type CommandSpec = {
|
||||
path: string[]
|
||||
// Why: conventional alternate verbs should resolve without duplicating specs or handlers.
|
||||
aliases?: string[][]
|
||||
argumentMode?: 'parsed' | 'passthrough'
|
||||
// Why: typo recovery must never steer a benign mistake into destructive state changes.
|
||||
destructive?: boolean
|
||||
summary: string
|
||||
usage: string
|
||||
allowedFlags: string[]
|
||||
positionalArgs?: string[]
|
||||
examples?: string[]
|
||||
notes?: string[]
|
||||
}
|
||||
|
||||
export function specPaths(spec: CommandSpec): string[][] {
|
||||
return spec.aliases ? [spec.path, ...spec.aliases] : [spec.path]
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import type { CommandSpec } from './args'
|
||||
import { specPaths } from './args'
|
||||
import { specPaths, type CommandSpec } from './command-spec'
|
||||
|
||||
// Why: rank the live registry so typo recovery cannot drift from accepted paths.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@ import { dirname, join } from 'node:path'
|
|||
import { randomUUID } from 'node:crypto'
|
||||
import type { CommandHandler } from '../dispatch'
|
||||
import { printResult } from '../format'
|
||||
import { RuntimeClientError, type RuntimeClient, type RuntimeRpcSuccess } from '../runtime-client'
|
||||
import {
|
||||
RuntimeClientError,
|
||||
type RuntimeClient,
|
||||
type RuntimeRpcSuccess,
|
||||
getDefaultUserDataPath
|
||||
} from '../runtime-client'
|
||||
import type { AgentHookInstallStatus } from '../../shared/agent-hook-types'
|
||||
import { getDefaultPersistedState } from '../../shared/constants'
|
||||
import type { PersistedState } from '../../shared/types'
|
||||
|
|
@ -12,7 +17,6 @@ import {
|
|||
applyAgentStatusHooksEnabled,
|
||||
getManagedAgentHookStatuses
|
||||
} from '../../main/agent-hooks/managed-agent-hook-controls'
|
||||
import { getDefaultUserDataPath } from '../runtime-client'
|
||||
|
||||
type AgentHookCommandResult = {
|
||||
enabled: boolean
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import type { CommandHandler } from '../dispatch'
|
||||
import { formatEnvironment, formatEnvironmentList, printResult } from '../format'
|
||||
import { getDefaultUserDataPath } from '../runtime-client'
|
||||
import { getDefaultUserDataPath, RuntimeClientError } from '../runtime-client'
|
||||
import type { RuntimeRpcSuccess } from '../runtime-client'
|
||||
import { RuntimeClientError } from '../runtime-client'
|
||||
import { redactRuntimeEnvironment } from '../../shared/runtime-environments'
|
||||
import {
|
||||
addEnvironmentFromPairingCode,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import { RuntimeClientError } from '../runtime-client'
|
|||
import { parseOrcaYaml } from '../../shared/orca-yaml'
|
||||
import {
|
||||
getEphemeralVmRecipeResultProjectRoot,
|
||||
getEphemeralVmRecipeResultWarnings,
|
||||
redactEphemeralVmRecipeDiagnosticText,
|
||||
type EphemeralVmRecipeDoctorCheck,
|
||||
type EphemeralVmRecipeDoctorResult
|
||||
} from '../../shared/ephemeral-vm-recipes'
|
||||
import {
|
||||
getEphemeralVmRecipeResultWarnings,
|
||||
redactEphemeralVmRecipeDiagnosticText
|
||||
} from '../../shared/ephemeral-vm-recipe-diagnostics'
|
||||
// Why: import directly from the doctor module (not the barrel) — it uses Node
|
||||
// fs/path and must stay out of the browser bundle that imports the barrel.
|
||||
import { doctorEphemeralVmRecipe } from '../../shared/ephemeral-vm-recipe-doctor'
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ export async function sendRequest<TResult>(
|
|||
)
|
||||
})
|
||||
})
|
||||
socket.on('data', (chunk) => {
|
||||
socket.on('data', (chunk: string) => {
|
||||
buffer += chunk
|
||||
// Why: the server may interleave `{"_keepalive":true}\n` frames with the
|
||||
// final success/failure frame to keep both idle timers alive during a
|
||||
|
|
|
|||
|
|
@ -7,33 +7,20 @@ vi.mock('electron', () => ({
|
|||
}
|
||||
}))
|
||||
|
||||
import { CodexHookService } from '../codex/hook-service'
|
||||
import { DroidHookService } from '../droid/hook-service'
|
||||
import { CursorHookService } from '../cursor/hook-service'
|
||||
import { CommandCodeHookService } from '../command-code/hook-service'
|
||||
import { GeminiHookService } from '../gemini/hook-service'
|
||||
import { AntigravityHookService } from '../antigravity/hook-service'
|
||||
import { AmpHookService } from '../amp/hook-service'
|
||||
import { ClaudeHookService } from '../claude/hook-service'
|
||||
import { GrokHookService } from '../grok/hook-service'
|
||||
import { CopilotHookService } from '../copilot/hook-service'
|
||||
import { HermesHookService } from '../hermes/hook-service'
|
||||
import { DevinHookService } from '../devin/hook-service'
|
||||
import { KimiHookService } from '../kimi/hook-service'
|
||||
import { CodexHookService, codexHookService } from '../codex/hook-service'
|
||||
import { DroidHookService, droidHookService } from '../droid/hook-service'
|
||||
import { CursorHookService, cursorHookService } from '../cursor/hook-service'
|
||||
import { CommandCodeHookService, commandCodeHookService } from '../command-code/hook-service'
|
||||
import { GeminiHookService, geminiHookService } from '../gemini/hook-service'
|
||||
import { AntigravityHookService, antigravityHookService } from '../antigravity/hook-service'
|
||||
import { AmpHookService, ampHookService } from '../amp/hook-service'
|
||||
import { ClaudeHookService, claudeHookService } from '../claude/hook-service'
|
||||
import { GrokHookService, grokHookService } from '../grok/hook-service'
|
||||
import { CopilotHookService, copilotHookService } from '../copilot/hook-service'
|
||||
import { HermesHookService, hermesHookService } from '../hermes/hook-service'
|
||||
import { DevinHookService, devinHookService } from '../devin/hook-service'
|
||||
import { KimiHookService, kimiHookService } from '../kimi/hook-service'
|
||||
import { openClaudeHookService } from '../openclaude/hook-service'
|
||||
import { ampHookService } from '../amp/hook-service'
|
||||
import { antigravityHookService } from '../antigravity/hook-service'
|
||||
import { claudeHookService } from '../claude/hook-service'
|
||||
import { codexHookService } from '../codex/hook-service'
|
||||
import { copilotHookService } from '../copilot/hook-service'
|
||||
import { cursorHookService } from '../cursor/hook-service'
|
||||
import { droidHookService } from '../droid/hook-service'
|
||||
import { commandCodeHookService } from '../command-code/hook-service'
|
||||
import { geminiHookService } from '../gemini/hook-service'
|
||||
import { devinHookService } from '../devin/hook-service'
|
||||
import { grokHookService } from '../grok/hook-service'
|
||||
import { hermesHookService } from '../hermes/hook-service'
|
||||
import { kimiHookService } from '../kimi/hook-service'
|
||||
import { MANAGED_AGENT_HOOK_INSTALLERS } from './managed-agent-hook-controls'
|
||||
import {
|
||||
installRemoteManagedAgentHooks,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import type { AiVaultAgent, AiVaultScanIssue } from '../../shared/ai-vault-types'
|
||||
import { buildOpenCodeSqliteCandidatePath } from './session-scanner-opencode-sqlite-paths'
|
||||
import { splitOpenCodeSqliteCandidate } from './session-scanner-opencode-sqlite-paths'
|
||||
import {
|
||||
buildOpenCodeSqliteCandidatePath,
|
||||
splitOpenCodeSqliteCandidate
|
||||
} from './session-scanner-opencode-sqlite-paths'
|
||||
import type { SessionFileCandidate } from './session-scanner-types'
|
||||
import { errorMessage } from './session-scanner-values'
|
||||
import SyncDatabase from '../sqlite/sync-database'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export function asRecord(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import type { CodexUsageSnapshot } from './session-scanner-types'
|
||||
import { asRecord } from './session-scanner-values'
|
||||
import { asRecord } from './session-scanner-record-value'
|
||||
|
||||
export function tokenTotal(value: unknown): number {
|
||||
const usage = asRecord(value)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { AiVaultAgent } from '../../shared/ai-vault-types'
|
||||
import type {
|
||||
AiVaultAgent,
|
||||
AiVaultScanIssue,
|
||||
AiVaultSession,
|
||||
AiVaultSessionPreviewMessage
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { homedir } from 'node:os'
|
||||
import { basename, dirname, join } from 'node:path'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { asRecord } from './session-scanner-record-value'
|
||||
|
||||
export { asRecord }
|
||||
|
||||
export function timestampMs(value: unknown): number {
|
||||
if (typeof value === 'string') {
|
||||
|
|
@ -25,12 +28,6 @@ export function parseJsonObject(line: string): Record<string, unknown> | null {
|
|||
}
|
||||
}
|
||||
|
||||
export function asRecord(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null
|
||||
}
|
||||
|
||||
export function extractString(value: unknown): string | null {
|
||||
if (typeof value !== 'string') {
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ import {
|
|||
CLIPBOARD_TEXT_WRITE_TOO_LARGE_ERROR
|
||||
} from '../../shared/clipboard-text'
|
||||
|
||||
type ExecFileCallback = (error: unknown, stdout?: string, stderr?: string) => void
|
||||
|
||||
// Why: the bridge resolves webContents via dynamic require('electron').webContents.fromId
|
||||
// inside a try/catch. Override the private method to inject our mock.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -116,31 +118,37 @@ function mockWebContents(id: number, url = 'https://example.com', title = 'Examp
|
|||
}
|
||||
|
||||
function succeedWith(data: unknown): void {
|
||||
execFileMock.mockImplementation((_bin: string, _args: string[], _opts: unknown, cb: Function) => {
|
||||
cb(null, JSON.stringify({ success: true, data }), '')
|
||||
return {
|
||||
stdin: { on: vi.fn(), end: (text: string) => stdinWrites.push(text) }
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
cb(null, JSON.stringify({ success: true, data }), '')
|
||||
return {
|
||||
stdin: { on: vi.fn(), end: (text: string) => stdinWrites.push(text) }
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function succeedForContentEditable(data: unknown = { ok: true }): void {
|
||||
execFileMock.mockImplementation((_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
const result =
|
||||
args.includes('get') && args.includes('attr') && args.includes('contenteditable')
|
||||
? { value: 'true' }
|
||||
: data
|
||||
cb(null, JSON.stringify({ success: true, data: result }), '')
|
||||
return {
|
||||
stdin: { on: vi.fn(), end: (text: string) => stdinWrites.push(text) }
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
const result =
|
||||
args.includes('get') && args.includes('attr') && args.includes('contenteditable')
|
||||
? { value: 'true' }
|
||||
: data
|
||||
cb(null, JSON.stringify({ success: true, data: result }), '')
|
||||
return {
|
||||
stdin: { on: vi.fn(), end: (text: string) => stdinWrites.push(text) }
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function failWith(error: string): void {
|
||||
execFileMock.mockImplementation((_bin: string, _args: string[], _opts: unknown, cb: Function) => {
|
||||
cb(null, JSON.stringify({ success: false, error }), '')
|
||||
})
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
cb(null, JSON.stringify({ success: false, error }), '')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class TestEvent {
|
||||
|
|
@ -352,7 +360,7 @@ describe('AgentBrowserBridge', () => {
|
|||
try {
|
||||
const closeKill = vi.fn()
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
return { kill: closeKill }
|
||||
}
|
||||
|
|
@ -445,7 +453,7 @@ describe('AgentBrowserBridge', () => {
|
|||
let releaseSnapshot: (() => void) | null = null
|
||||
const activeChild = { kill: vi.fn() }
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('snapshot')) {
|
||||
releaseSnapshot = () => {
|
||||
cb(null, JSON.stringify({ success: false, error: CDP_DISCOVERY_FAILURE }), '')
|
||||
|
|
@ -486,7 +494,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
it('handles malformed JSON from agent-browser', async () => {
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
cb(null, 'not json at all', '')
|
||||
}
|
||||
)
|
||||
|
|
@ -748,7 +756,7 @@ describe('AgentBrowserBridge', () => {
|
|||
const commandCalls: string[][] = []
|
||||
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
cb(null, JSON.stringify({ success: true, data: { ok: true } }), '')
|
||||
}
|
||||
|
|
@ -782,7 +790,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
let releaseSnapshot: (() => void) | null = null
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -877,7 +885,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
const commandCalls: string[][] = []
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
cb(null, JSON.stringify({ success: true, data: { ok: true } }), '')
|
||||
}
|
||||
|
|
@ -1024,7 +1032,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
let releaseFirstScreenshot: (() => void) | null = null
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -1103,7 +1111,7 @@ describe('AgentBrowserBridge', () => {
|
|||
webContentsFromIdMock.mockReturnValue(wc)
|
||||
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
}
|
||||
)
|
||||
|
|
@ -1137,7 +1145,7 @@ describe('AgentBrowserBridge', () => {
|
|||
const killedError = Object.assign(new Error('timeout'), { killed: true })
|
||||
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -1167,7 +1175,7 @@ describe('AgentBrowserBridge', () => {
|
|||
const commandCalls: string[][] = []
|
||||
let releaseDestroyClose: (() => void) | null = null
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
if (args.includes('close')) {
|
||||
if (!releaseDestroyClose) {
|
||||
|
|
@ -1212,7 +1220,7 @@ describe('AgentBrowserBridge', () => {
|
|||
const commandCalls: string[][] = []
|
||||
let releaseStaleClose: (() => void) | null = null
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
if (args.includes('close') && !releaseStaleClose) {
|
||||
releaseStaleClose = () => {
|
||||
|
|
@ -1270,7 +1278,7 @@ describe('AgentBrowserBridge', () => {
|
|||
}
|
||||
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('snapshot')) {
|
||||
resolveRunningCommand = () => cb(killedError, '', '')
|
||||
return activeChild
|
||||
|
|
@ -1354,7 +1362,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
const commandCalls: string[][] = []
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
cb(null, JSON.stringify({ success: true, data: { ok: true } }), '')
|
||||
}
|
||||
|
|
@ -1390,7 +1398,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
const commandCalls: string[][] = []
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
commandCalls.push(args)
|
||||
cb(null, JSON.stringify({ success: true, data: { ok: true } }), '')
|
||||
}
|
||||
|
|
@ -1577,7 +1585,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
let releaseSnapshot: (() => void) | null = null
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -1875,7 +1883,7 @@ describe('AgentBrowserBridge', () => {
|
|||
let helperSessionIsStale = false
|
||||
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
} else if (args.includes('snapshot')) {
|
||||
|
|
@ -2549,7 +2557,7 @@ describe('AgentBrowserBridge', () => {
|
|||
it('returns browser_timeout for timed conditional waits without recycling the session', async () => {
|
||||
const killedError = Object.assign(new Error('timeout'), { killed: true })
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('wait')) {
|
||||
cb(killedError, '', '')
|
||||
return
|
||||
|
|
@ -2573,7 +2581,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
it('passes stderr through as error message on execFile failure', async () => {
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -2586,7 +2594,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
it('falls back to error.message when stderr is empty', async () => {
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
if (args.includes('close')) {
|
||||
cb(null, JSON.stringify({ success: true, data: null }), '')
|
||||
return
|
||||
|
|
@ -2601,7 +2609,7 @@ describe('AgentBrowserBridge', () => {
|
|||
|
||||
it('returns browser_error with truncated output for malformed JSON', async () => {
|
||||
execFileMock.mockImplementation(
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: Function) => {
|
||||
(_bin: string, _args: string[], _opts: unknown, cb: ExecFileCallback) => {
|
||||
cb(null, 'Error: not json output', '')
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -596,7 +596,7 @@ async function importValidatedCookies(
|
|||
}
|
||||
}
|
||||
diag(
|
||||
` cookie.set FAILED: domain=${cookie.domain} name=${cookie.name} valLen=${val.length} badChar=${badInfo} err=${err}`
|
||||
` cookie.set FAILED: domain=${cookie.domain} name=${cookie.name} valLen=${val.length} badChar=${badInfo} err=${String(err)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1019,7 +1019,7 @@ function getWindowsEncryptionKey(browser: DetectedBrowser): EncryptionKeyResult
|
|||
|
||||
return { key: Buffer.from(result, 'base64'), mode: 'aes-256-gcm' }
|
||||
} catch (err) {
|
||||
diag(` Windows DPAPI key extraction failed: ${err}`)
|
||||
diag(` Windows DPAPI key extraction failed: ${String(err)}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -1339,7 +1339,7 @@ async function importCookiesFromFirefox(
|
|||
return importValidatedCookies(validated, rows.length, targetPartition)
|
||||
} catch (err) {
|
||||
rmSync(tmpDir, { recursive: true, force: true })
|
||||
diag(` Firefox import failed: ${err}`)
|
||||
diag(` Firefox import failed: ${String(err)}`)
|
||||
return {
|
||||
ok: false,
|
||||
reason: 'Could not import cookies from Firefox. Try closing Firefox first.'
|
||||
|
|
@ -1361,7 +1361,7 @@ async function importCookiesFromSafari(
|
|||
try {
|
||||
data = readFileSync(browser.cookiesPath)
|
||||
} catch (err) {
|
||||
diag(` Safari read failed: ${err}`)
|
||||
diag(` Safari read failed: ${String(err)}`)
|
||||
// Why: Safari's Cookies.binarycookies is in a sandbox container; reading it needs Full Disk Access.
|
||||
const isPermError =
|
||||
err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'EPERM'
|
||||
|
|
@ -1392,7 +1392,7 @@ async function importCookiesFromSafari(
|
|||
|
||||
return importValidatedCookies(valid, cookies.length, targetPartition)
|
||||
} catch (err) {
|
||||
diag(` Safari import failed: ${err}`)
|
||||
diag(` Safari import failed: ${String(err)}`)
|
||||
return { ok: false, reason: 'Could not import cookies from Safari.' }
|
||||
}
|
||||
}
|
||||
|
|
@ -1480,7 +1480,7 @@ export async function importCookiesFromBrowser(
|
|||
} catch {
|
||||
/* best-effort */
|
||||
}
|
||||
diag(` Chromium snapshot failed: ${err}`)
|
||||
diag(` Chromium snapshot failed: ${String(err)}`)
|
||||
return {
|
||||
ok: false,
|
||||
reason: `Could not copy ${browser.label} cookies database. Try closing ${browser.label} first.`
|
||||
|
|
@ -1527,7 +1527,7 @@ export async function importCookiesFromBrowser(
|
|||
placeholders = targetCols.map(() => '?').join(', ')
|
||||
stagingDb.exec('DELETE FROM cookies')
|
||||
} catch (err) {
|
||||
diag(` staging database unusable, restart fallback disabled: ${err}`)
|
||||
diag(` staging database unusable, restart fallback disabled: ${String(err)}`)
|
||||
stagingAvailable = false
|
||||
targetColumnInfo = null
|
||||
colList = null
|
||||
|
|
@ -1807,7 +1807,7 @@ export async function importCookiesFromBrowser(
|
|||
} catch {
|
||||
/* may not exist yet */
|
||||
}
|
||||
diag(` SQLite import failed: ${err}`)
|
||||
diag(` SQLite import failed: ${String(err)}`)
|
||||
return {
|
||||
ok: false,
|
||||
reason: reasonWithDiagLog(
|
||||
|
|
@ -1818,7 +1818,7 @@ export async function importCookiesFromBrowser(
|
|||
try {
|
||||
sourceSnapshot.cleanup()
|
||||
} catch (err) {
|
||||
diag(` Chromium snapshot cleanup failed: ${err}`)
|
||||
diag(` Chromium snapshot cleanup failed: ${String(err)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,14 +43,17 @@ import {
|
|||
buildBrowserIframeClickedLinkRoutingScript
|
||||
} from './browser-clicked-link-routing'
|
||||
import { cleanElectronUserAgent } from './browser-session-ua'
|
||||
import type { BrowserViewportOverride } from '../../shared/types'
|
||||
import type {
|
||||
BrowserViewportOverride,
|
||||
BrowserCertificateFailure,
|
||||
BrowserLoadError
|
||||
} from '../../shared/types'
|
||||
import {
|
||||
type BrowserAnnotationViewportBridgeOptions,
|
||||
BROWSER_ANNOTATION_VIEWPORT_BRIDGE_WORLD_ID,
|
||||
buildBrowserAnnotationViewportBridgeScript
|
||||
} from '../../shared/browser-annotation-viewport-bridge'
|
||||
import type { KeybindingOverrides } from '../../shared/keybindings'
|
||||
import type { BrowserCertificateFailure, BrowserLoadError } from '../../shared/types'
|
||||
import {
|
||||
BrowserCertificateTrustController,
|
||||
type ManagedBrowserGuestContext
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ async function sendRequest(
|
|||
let buffer = ''
|
||||
socket.setEncoding('utf8')
|
||||
socket.once('error', reject)
|
||||
socket.on('data', (chunk) => {
|
||||
socket.on('data', (chunk: string) => {
|
||||
buffer += chunk
|
||||
const newlineIndex = buffer.indexOf('\n')
|
||||
if (newlineIndex === -1) {
|
||||
|
|
|
|||
|
|
@ -170,9 +170,9 @@ function isKeychainNotFoundError(error: unknown): boolean {
|
|||
: undefined
|
||||
const message =
|
||||
error && typeof error === 'object'
|
||||
? `${(error as { stderr?: unknown }).stderr ?? ''} ${
|
||||
? `${String((error as { stderr?: unknown }).stderr ?? '')} ${String(
|
||||
(error as { message?: unknown }).message ?? ''
|
||||
}`.toLowerCase()
|
||||
)}`.toLowerCase()
|
||||
: String(error).toLowerCase()
|
||||
return code === 44 || message.includes('could not be found') || message.includes('not be found')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ export class CliInstaller {
|
|||
private isWindowsPackagedBundledCommand(
|
||||
commandPath: string | null,
|
||||
launcherPath: string | null
|
||||
): commandPath is string {
|
||||
): boolean {
|
||||
return (
|
||||
this.platform === 'win32' &&
|
||||
this.isPackaged &&
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import {
|
|||
rmSync,
|
||||
symlinkSync,
|
||||
statSync,
|
||||
writeFileSync
|
||||
writeFileSync,
|
||||
existsSync
|
||||
} from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import type * as Os from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { wrapPosixHookCommand } from '../agent-hooks/installer-utils'
|
||||
import {
|
||||
computeTrustKey,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,15 @@ import type { DaemonPtyAdapter } from './daemon-pty-adapter'
|
|||
import { combineUnsubscribes } from './combine-unsubscribes'
|
||||
import { shutdownDegradedFallbackSessions } from './degraded-daemon-fallback-shutdown'
|
||||
import { inspectPtyProviderProcess } from '../providers/pty-process-inspection'
|
||||
import type { IPtyProvider, PtyBackgroundStreamEvent } from '../providers/types'
|
||||
import type { PtyDataEvent, PtyProviderBufferSnapshot } from '../providers/types'
|
||||
import type { PtyProcessInfo, PtySpawnOptions, PtySpawnResult } from '../providers/types'
|
||||
import type {
|
||||
IPtyProvider,
|
||||
PtyBackgroundStreamEvent,
|
||||
PtyDataEvent,
|
||||
PtyProviderBufferSnapshot,
|
||||
PtyProcessInfo,
|
||||
PtySpawnOptions,
|
||||
PtySpawnResult
|
||||
} from '../providers/types'
|
||||
|
||||
export class DegradedDaemonPtyProvider implements IPtyProvider {
|
||||
readonly routesFreshSpawnsToLocalProvider = true
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ export class HistoryManager {
|
|||
|
||||
private async waitForSessionMutations(sessionId: string): Promise<void> {
|
||||
while (this.pendingSessionMutations.has(sessionId)) {
|
||||
await Promise.allSettled(this.pendingSessionMutations.get(sessionId) ?? [])
|
||||
await Promise.allSettled(this.pendingSessionMutations.get(sessionId)!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export async function fetchGitHubPullRequestHeadRef(
|
|||
options: { localGitExecOptions?: LocalGitExecOptions } = {}
|
||||
): Promise<string> {
|
||||
if (!isValidReviewHeadNumber(prNumber)) {
|
||||
throw new Error(`Invalid pull request number: ${prNumber}`)
|
||||
throw new Error(`Invalid pull request number: ${String(prNumber)}`)
|
||||
}
|
||||
if (!isSafeReviewHeadFetchRemote(remote)) {
|
||||
throw new Error('Pull request fetch remote must not start with "-".')
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export async function fetchGitLabMergeRequestHeadRef(
|
|||
options: { localGitExecOptions?: LocalGitExecOptions } = {}
|
||||
): Promise<string> {
|
||||
if (!isValidReviewHeadNumber(mrIid)) {
|
||||
throw new Error(`Invalid merge request iid: ${mrIid}`)
|
||||
throw new Error(`Invalid merge request iid: ${String(mrIid)}`)
|
||||
}
|
||||
if (!isSafeReviewHeadFetchRemote(remote)) {
|
||||
throw new Error('Merge request fetch remote must not start with "-".')
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import type {
|
|||
GitLabPipelineJob,
|
||||
GitLabWorkItem,
|
||||
GitLabWorkItemDetails,
|
||||
IssueSourcePreference,
|
||||
MRComment
|
||||
} from '../../shared/types'
|
||||
import { mapIssueToWorkItem, mapMRToWorkItem } from './mappers'
|
||||
|
|
@ -24,7 +25,6 @@ import {
|
|||
type LocalGitExecOptions,
|
||||
type ProjectRef
|
||||
} from './gl-utils'
|
||||
import type { IssueSourcePreference } from '../../shared/types'
|
||||
|
||||
function encodedProject(projectPath: string): string {
|
||||
return encodeURIComponent(projectPath)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,15 @@ import { StatsCollector, initStatsPath } from './stats/collector'
|
|||
import { ClaudeUsageStore, initClaudeUsagePath } from './claude-usage/store'
|
||||
import { CodexUsageStore, initCodexUsagePath } from './codex-usage/store'
|
||||
import { OpenCodeUsageStore, initOpenCodeUsagePath } from './opencode-usage/store'
|
||||
import { killAllPty } from './ipc/pty'
|
||||
import {
|
||||
killAllPty,
|
||||
clearProviderPtyState,
|
||||
getPtyIdForPaneKey,
|
||||
registerPaneKeyTeardownListener,
|
||||
getLocalPtyProvider,
|
||||
getSshPtyProvider,
|
||||
registerHeadlessPtyRuntime
|
||||
} from './ipc/pty'
|
||||
import { initDaemonPtyProvider, disconnectDaemon, shutdownDaemon } from './daemon/daemon-init'
|
||||
import { closeAllWatchers } from './ipc/filesystem-watcher'
|
||||
import { disposeWorktreeBaseDirectoryWatchers } from './ipc/worktree-base-directory-watcher'
|
||||
|
|
@ -203,14 +211,6 @@ import { setDefaultWslDistroOverride } from './git/runner'
|
|||
import { getRepoIdFromWorktreeId } from '../shared/worktree-id'
|
||||
import { parseWorkspaceKey } from '../shared/workspace-scope'
|
||||
import { setMigrationUnsupportedPtyListener } from './agent-hooks/migration-unsupported-pty-state'
|
||||
import {
|
||||
clearProviderPtyState,
|
||||
getPtyIdForPaneKey,
|
||||
registerPaneKeyTeardownListener,
|
||||
getLocalPtyProvider,
|
||||
getSshPtyProvider,
|
||||
registerHeadlessPtyRuntime
|
||||
} from './ipc/pty'
|
||||
import { AgentBrowserBridge } from './browser/agent-browser-bridge'
|
||||
import { EmulatorBridge } from './emulator/emulator-bridge'
|
||||
import { browserCertificateTrustController, browserManager } from './browser/browser-manager'
|
||||
|
|
|
|||
|
|
@ -121,21 +121,18 @@ async function scanAiVaultSessionsByHostScope(
|
|||
if (executionHostScope === 'all') {
|
||||
const runtimeHosts = getActiveRuntimeAiVaultHostInfosResult()
|
||||
const runtimeResults = runtimeHosts.issue ? [runtimeHosts.issue] : []
|
||||
return mergeAiVaultListResults(
|
||||
await Promise.all([
|
||||
scanLocalAiVaultSessions(args),
|
||||
...getActiveSshAiVaultHostInfos().map((hostInfo) =>
|
||||
scanSshAiVaultSessions(hostInfo.targetId, args)
|
||||
),
|
||||
...runtimeHosts.hostInfos.map((hostInfo) =>
|
||||
scanRuntimeAiVaultSessions(hostInfo, args, {
|
||||
timeoutMs: AI_VAULT_ALL_HOST_RUNTIME_TIMEOUT_MS
|
||||
})
|
||||
),
|
||||
...runtimeResults
|
||||
]),
|
||||
args?.limit
|
||||
)
|
||||
const scannedResults = await Promise.all([
|
||||
scanLocalAiVaultSessions(args),
|
||||
...getActiveSshAiVaultHostInfos().map((hostInfo) =>
|
||||
scanSshAiVaultSessions(hostInfo.targetId, args)
|
||||
),
|
||||
...runtimeHosts.hostInfos.map((hostInfo) =>
|
||||
scanRuntimeAiVaultSessions(hostInfo, args, {
|
||||
timeoutMs: AI_VAULT_ALL_HOST_RUNTIME_TIMEOUT_MS
|
||||
})
|
||||
)
|
||||
])
|
||||
return mergeAiVaultListResults([...scannedResults, ...runtimeResults], args?.limit)
|
||||
}
|
||||
|
||||
const parsed = parseExecutionHostId(executionHostScope)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@
|
|||
|
||||
import { app, dialog, ipcMain, shell } from 'electron'
|
||||
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
import { arch as osArch, platform as osPlatform, release as osRelease } from 'node:os'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { arch as osArch, platform as osPlatform, release as osRelease, tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
collectDiagnosticBundle,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import { app, ipcMain } from 'electron'
|
|||
import type { Store } from '../persistence'
|
||||
import {
|
||||
getEphemeralVmRecipeResultConnection,
|
||||
getEphemeralVmRecipeResultWarnings,
|
||||
redactEphemeralVmRecipeDiagnosticText,
|
||||
type EphemeralVmRecipeResultWarning,
|
||||
type EphemeralVmRecipeDoctorResult
|
||||
} from '../../shared/ephemeral-vm-recipes'
|
||||
import {
|
||||
getEphemeralVmRecipeResultWarnings,
|
||||
redactEphemeralVmRecipeDiagnosticText,
|
||||
type EphemeralVmRecipeResultWarning
|
||||
} from '../../shared/ephemeral-vm-recipe-diagnostics'
|
||||
// Why: import directly from the doctor module (not the barrel) — it uses Node
|
||||
// fs/path and must stay out of the browser bundle that imports the barrel.
|
||||
import { doctorEphemeralVmRecipe } from '../../shared/ephemeral-vm-recipe-doctor'
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import type {
|
|||
GitHubPRRefreshCandidate,
|
||||
GitHubPRRefreshEnqueueResult,
|
||||
GitHubPRRefreshReason,
|
||||
PRRefreshOutcome
|
||||
PRRefreshOutcome,
|
||||
GitHubPRFile
|
||||
} from '../../shared/types'
|
||||
import { getRepoExecutionHostId } from '../../shared/execution-host'
|
||||
import type { TaskSourceContext } from '../../shared/task-source-context'
|
||||
|
|
@ -68,7 +69,6 @@ import {
|
|||
type PRRefreshValidationDenialReason
|
||||
} from '../github/pr-refresh-validation-backoff'
|
||||
import { getLocalProjectWorktreeGitOptions } from '../project-runtime-git-options'
|
||||
import type { GitHubPRFile } from '../../shared/types'
|
||||
import { dispatchWorkItem, type WorkItemArgs } from './github-work-item-args'
|
||||
import {
|
||||
getProjectViewTable,
|
||||
|
|
|
|||
|
|
@ -205,7 +205,10 @@ vi.mock('../codex/codex-pane-account-registry', () => ({
|
|||
recordCodexPaneAccount: recordCodexPaneAccountMock,
|
||||
forgetCodexPaneAccount: forgetCodexPaneAccountMock
|
||||
}))
|
||||
import { LocalPtyProvider } from '../providers/local-pty-provider'
|
||||
import {
|
||||
LocalPtyProvider,
|
||||
_resetLocalPtyProviderStateForTest
|
||||
} from '../providers/local-pty-provider'
|
||||
import { makePaneKey } from '../../shared/stable-pane-id'
|
||||
import { SETUP_AGENT_SEQUENCE_STARTUP_COMMAND_ENV } from '../../shared/setup-agent-sequencing'
|
||||
import {
|
||||
|
|
@ -228,7 +231,6 @@ import {
|
|||
restorePtyIncarnation,
|
||||
type PrepareCodexSessionResume
|
||||
} from './pty'
|
||||
import { _resetLocalPtyProviderStateForTest } from '../providers/local-pty-provider'
|
||||
import { resetMacosLoginShellPreflightForTests } from '../providers/macos-tcc-login-shell'
|
||||
import {
|
||||
_resetHiddenRendererPtyDeliveryGateForTest,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ import { registerDashboardPopoutHandlers } from './dashboard-popout'
|
|||
import { registerTerminalPreviewHandlers } from './terminal-preview'
|
||||
import { registerDeveloperPermissionHandlers } from './developer-permissions'
|
||||
import { registerComputerUsePermissionHandlers } from './computer-use-permissions'
|
||||
import { setTrustedBrowserRendererWebContentsId, setAgentBrowserBridgeRef } from './browser'
|
||||
import {
|
||||
setTrustedBrowserRendererWebContentsId,
|
||||
setAgentBrowserBridgeRef,
|
||||
registerBrowserHandlers
|
||||
} from './browser'
|
||||
import { registerSessionHandlers } from './session'
|
||||
import { registerSettingsHandlers } from './settings'
|
||||
import { registerDiagnosticsHandlers } from './diagnostics'
|
||||
|
|
@ -45,7 +49,6 @@ import { registerLocalhostWorktreeLabelHandlers } from './localhost-worktree-lab
|
|||
import { registerAutomationHandlers } from './automations'
|
||||
import { registerKeybindingHandlers } from './keybindings'
|
||||
import { registerTelemetryHandlers } from './telemetry'
|
||||
import { registerBrowserHandlers } from './browser'
|
||||
import { registerShellHandlers } from './shell'
|
||||
import { registerPetHandlers } from './pet'
|
||||
import { registerPluginHandlers } from './plugins'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ipcMain } from 'electron'
|
||||
import type { SshConnectionManager } from '../ssh/ssh-connection'
|
||||
import type { SshConnectionManager } from '../ssh/ssh-connection-manager'
|
||||
import type { SshExecOptions } from '../ssh/ssh-connection-utils'
|
||||
import { powerShellCommand, powerShellLiteral } from '../ssh/ssh-remote-powershell'
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ vi.mock('../ssh/ssh-connection-store', () => ({
|
|||
}
|
||||
}))
|
||||
|
||||
vi.mock('../ssh/ssh-connection', () => ({
|
||||
vi.mock('../ssh/ssh-connection-manager', () => ({
|
||||
SshConnectionManager: class MockSshConnectionManager {
|
||||
constructor(callbacks: unknown) {
|
||||
const manager = (mockNextConnectionManagers.shift() ??
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { ipcMain, powerMonitor, type BrowserWindow } from 'electron'
|
|||
import { appendFileSync } from 'node:fs'
|
||||
import type { Store } from '../persistence'
|
||||
import { SshConnectionStore } from '../ssh/ssh-connection-store'
|
||||
import { SshConnectionManager, type SshConnectionCallbacks } from '../ssh/ssh-connection'
|
||||
import type { SshConnectionCallbacks } from '../ssh/ssh-connection'
|
||||
import { SshConnectionManager } from '../ssh/ssh-connection-manager'
|
||||
import type { SshChannelMultiplexer } from '../ssh/ssh-channel-multiplexer'
|
||||
import { SshRelaySession, type SshRelayAiVaultHostInfo } from '../ssh/ssh-relay-session'
|
||||
import { SshPortForwardManager } from '../ssh/ssh-port-forward'
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ import { getOnboardingCohortAtEmit } from '../telemetry/onboarding-cohort-classi
|
|||
import { resolveConsent, type ConsentState } from '../telemetry/consent'
|
||||
import type { Store } from '../persistence'
|
||||
import { isCohortExtendedEvent, isOnboardingEvent } from '../../shared/telemetry-events'
|
||||
import type { EventName, EventProps } from '../../shared/telemetry-events'
|
||||
import type { OptInVia } from '../../shared/telemetry-events'
|
||||
import type { EventName, EventProps, OptInVia } from '../../shared/telemetry-events'
|
||||
|
||||
// Module-level store ref: handlers need a synchronous `settings.telemetry` read to derive `via` before any mutation.
|
||||
let storeRef: Store | null = null
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import {
|
|||
resolveDefaultBaseRefViaExec,
|
||||
resolveDefaultBaseRefWithLocalGit
|
||||
} from '../git/repo'
|
||||
import { resolveLocalGitUsername } from '../git/git-username'
|
||||
import { resolveLocalGitUsername, getSshGitUsername } from '../git/git-username'
|
||||
import { hasCommitObjectViaGitExec } from '../git/commit-object-ref'
|
||||
import { resolveWorktreeCreateBase } from '../worktree-create-base'
|
||||
import { resolveWorktreeAddBaseRef } from '../../shared/worktree-base-ref'
|
||||
|
|
@ -38,8 +38,11 @@ import { validateGitPushTarget } from '../git/push-target-validation'
|
|||
import { assertGitPushTargetShape } from '../../shared/git-push-target-validation'
|
||||
import { gitExecFileAsync } from '../git/runner'
|
||||
import { parseGitHubOwnerRepo } from '../github/gh-utils'
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
import type { RemoteFetchResult, RemoteTrackingBase } from '../runtime/orca-runtime'
|
||||
import type {
|
||||
OrcaRuntimeService,
|
||||
RemoteFetchResult,
|
||||
RemoteTrackingBase
|
||||
} from '../runtime/orca-runtime'
|
||||
import { getProjectHostSetupWorktreeMeta } from '../../shared/project-host-setup-projection'
|
||||
import {
|
||||
buildPosixRunnerScript,
|
||||
|
|
@ -58,7 +61,6 @@ import { getSshFilesystemProvider } from '../providers/ssh-filesystem-dispatch'
|
|||
import type { SshGitProvider } from '../providers/ssh-git-provider'
|
||||
import { TUI_AGENT_CONFIG, isTuiAgent } from '../../shared/tui-agent-config'
|
||||
import { isWindowsAbsolutePathLike } from '../../shared/cross-platform-path'
|
||||
import { getSshGitUsername } from '../git/git-username'
|
||||
import { runWorktreeChangeInvalidators } from './worktree-change-invalidators'
|
||||
import {
|
||||
registerOptionalSshWorktreeCreateRoots,
|
||||
|
|
|
|||
|
|
@ -94,7 +94,16 @@ import {
|
|||
type SshTarget
|
||||
} from '../shared/ssh-types'
|
||||
import { isFolderRepo } from '../shared/repo-kind'
|
||||
import { getRepoExecutionHostId, parseExecutionHostId } from '../shared/execution-host'
|
||||
import {
|
||||
getRepoExecutionHostId,
|
||||
parseExecutionHostId,
|
||||
LOCAL_EXECUTION_HOST_ID,
|
||||
normalizeExecutionHostOrder,
|
||||
normalizeExecutionHostId,
|
||||
normalizeVisibleExecutionHostIds,
|
||||
toSshExecutionHostId,
|
||||
type ExecutionHostId
|
||||
} from '../shared/execution-host'
|
||||
import {
|
||||
getDefaultPersistedState,
|
||||
getDefaultNotificationSettings,
|
||||
|
|
@ -116,14 +125,6 @@ import { normalizeStatusBarUsageMode } from '../shared/status-bar-usage-mode'
|
|||
import { isExistingPersistedProfile } from '../shared/project-order-manual-default-notice'
|
||||
import { resolveUsagePercentageDisplayChangeNoticeDismissed } from '../shared/usage-percentage-display-change-notice'
|
||||
import { normalizePRBotAuthorOverrides } from '../shared/pr-bot-author-overrides'
|
||||
import {
|
||||
LOCAL_EXECUTION_HOST_ID,
|
||||
normalizeExecutionHostOrder,
|
||||
normalizeExecutionHostId,
|
||||
normalizeVisibleExecutionHostIds,
|
||||
toSshExecutionHostId,
|
||||
type ExecutionHostId
|
||||
} from '../shared/execution-host'
|
||||
import { toRelaySshPtyId } from './providers/ssh-pty-id'
|
||||
import {
|
||||
migrateUiHostScopeSshTargetId,
|
||||
|
|
|
|||
|
|
@ -60,11 +60,10 @@ export class PluginContentPackRegistry {
|
|||
while (true) {
|
||||
const approveAtomically = (plugin: ValidDiscoveredPlugin): boolean =>
|
||||
approvedKeys.has(plugin.pluginKey) && !excluded.has(plugin.pluginKey)
|
||||
await Promise.all([
|
||||
this.languagePacks.reconcile(discovered, approveAtomically),
|
||||
this.vmRecipes.reconcile(discovered, approveAtomically),
|
||||
this.commands.reconcile(discovered, approveAtomically, keybindings)
|
||||
])
|
||||
const languagePacks = this.languagePacks.reconcile(discovered, approveAtomically)
|
||||
const vmRecipes = this.vmRecipes.reconcile(discovered, approveAtomically)
|
||||
this.commands.reconcile(discovered, approveAtomically, keybindings)
|
||||
await Promise.all([languagePacks, vmRecipes])
|
||||
|
||||
let foundNewError = false
|
||||
for (const pluginKey of approvedKeys) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-disable max-lines -- Why: splitting spawn() would scatter tightly coupled PTY lifecycle logic (scan → ready → write → exit) with no cleaner ownership seam. */
|
||||
import { basename, delimiter } from 'node:path'
|
||||
import { basename, delimiter, win32 as pathWin32 } from 'node:path'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { win32 as pathWin32 } from 'node:path'
|
||||
import { resolveWindowsShellLaunchArgs } from './windows-shell-args'
|
||||
import {
|
||||
resolveEffectiveWindowsPowerShell,
|
||||
|
|
|
|||
|
|
@ -1009,7 +1009,7 @@ describe('SshGitProvider', () => {
|
|||
expect(mux.request).toHaveBeenCalledTimes(1)
|
||||
pendingDiff.resolve()
|
||||
|
||||
await expect(Promise.all(reads)).resolves.toEqual(Array(8).fill(diff))
|
||||
await expect(Promise.all(reads)).resolves.toEqual(Array.from({ length: 8 }, () => diff))
|
||||
|
||||
mux.request.mockReset()
|
||||
const branchDiffs = [diff]
|
||||
|
|
@ -1026,7 +1026,9 @@ describe('SshGitProvider', () => {
|
|||
await waitForRequestCount(mux.request, 1)
|
||||
expect(mux.request).toHaveBeenCalledTimes(1)
|
||||
pendingBranchDiff.resolve()
|
||||
await expect(Promise.all(branchReads)).resolves.toEqual(Array(8).fill(branchDiffs))
|
||||
await expect(Promise.all(branchReads)).resolves.toEqual(
|
||||
Array.from({ length: 8 }, () => branchDiffs)
|
||||
)
|
||||
|
||||
mux.request.mockReset()
|
||||
const pendingCommitDiff = deferredValue(diff)
|
||||
|
|
@ -1043,7 +1045,7 @@ describe('SshGitProvider', () => {
|
|||
await waitForRequestCount(mux.request, 1)
|
||||
expect(mux.request).toHaveBeenCalledTimes(1)
|
||||
pendingCommitDiff.resolve()
|
||||
await expect(Promise.all(commitReads)).resolves.toEqual(Array(8).fill(diff))
|
||||
await expect(Promise.all(commitReads)).resolves.toEqual(Array.from({ length: 8 }, () => diff))
|
||||
})
|
||||
|
||||
it('retries diff RPCs after an in-flight rejection settles', async () => {
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ import {
|
|||
readActiveClaudeKeychainCredentials,
|
||||
readActiveClaudeKeychainCredentialsStrict,
|
||||
readManagedClaudeKeychainCredentials,
|
||||
writeActiveClaudeKeychainCredentials
|
||||
writeActiveClaudeKeychainCredentials,
|
||||
writeManagedClaudeKeychainCredentials
|
||||
} from '../claude-accounts/keychain'
|
||||
import {
|
||||
readClaudeManagedAuthFile,
|
||||
resolveOwnedClaudeManagedAuthPath,
|
||||
writeClaudeManagedAuthFile
|
||||
} from '../claude-accounts/managed-auth-path'
|
||||
import { writeManagedClaudeKeychainCredentials } from '../claude-accounts/keychain'
|
||||
import {
|
||||
isOauthTokenExpiring,
|
||||
refreshClaudeOauthCredentials
|
||||
|
|
|
|||
|
|
@ -1286,7 +1286,7 @@ export class RuntimeFileCommands {
|
|||
callback: (events: FsChangeEvent[]) => void,
|
||||
onTerminalError: (error: Error) => void = () => undefined,
|
||||
signal?: AbortSignal
|
||||
): Promise<() => void> {
|
||||
): Promise<() => Promise<void>> {
|
||||
const target = await this.resolveFileExplorerPath(worktreeSelector, '')
|
||||
const open = async (): Promise<{
|
||||
unsubscribe: () => Promise<void>
|
||||
|
|
|
|||
|
|
@ -184,7 +184,20 @@ import type {
|
|||
TuiAgent,
|
||||
WorkspaceCreateTelemetrySource,
|
||||
WorkspaceSessionState,
|
||||
DirEntry
|
||||
DirEntry,
|
||||
GitHubIssueUpdate,
|
||||
GitHubPullRequestStateUpdate,
|
||||
GitHubPRFile,
|
||||
GitHubPRReviewCommentInput,
|
||||
GitLabIssueUpdate,
|
||||
GitLabMRInlineCommentInput,
|
||||
GitLabProjectRef,
|
||||
GitLabWorkItem,
|
||||
ListWorkItemsResult,
|
||||
MRListState,
|
||||
PRRefreshOutcome,
|
||||
ClaudeRateLimitAccountsState,
|
||||
CodexRateLimitAccountsState
|
||||
} from '../../shared/types'
|
||||
import { assertWorktreeUnlockedForRemoval } from '../../shared/worktree-removal'
|
||||
import {
|
||||
|
|
@ -237,7 +250,65 @@ import type {
|
|||
} from '../../shared/linear-agent-access'
|
||||
import {
|
||||
HEADLESS_RUNTIME_WINDOW_ID,
|
||||
type RuntimeDesktopWindowStatus
|
||||
type RuntimeDesktopWindowStatus,
|
||||
type RuntimeGraphStatus,
|
||||
type RuntimeRepoSearchRefs,
|
||||
type RuntimeTerminalRead,
|
||||
type RuntimeTerminalRename,
|
||||
type RuntimeTerminalAgentStatus,
|
||||
type RuntimeTerminalSend,
|
||||
type RuntimeTerminalCreate,
|
||||
type RuntimeTerminalPresentation,
|
||||
type RuntimeTerminalSplit,
|
||||
type RuntimeTerminalFocus,
|
||||
type RuntimeTerminalClose,
|
||||
type RuntimeTerminalListResult,
|
||||
type RuntimeTerminalOrphanAdoptionRequest,
|
||||
type RuntimeTerminalOrphanAdoptionResult,
|
||||
type RuntimeWorktreeTerminalSleepResult,
|
||||
type RuntimeTerminalResolvePane,
|
||||
type RuntimeTerminalState,
|
||||
type RuntimeStatus,
|
||||
type RuntimeSyncWindowGraphResult,
|
||||
type RuntimeTerminalWait,
|
||||
type RuntimeTerminalWaitBlockedReason,
|
||||
type RuntimeTerminalWaitCondition,
|
||||
type RuntimeWorktreePsSummary,
|
||||
type RuntimeWorktreeAgentRow,
|
||||
type RuntimeWorktreeStatus,
|
||||
type RuntimeSpeechModelSummary,
|
||||
type RuntimeSpeechSetupState,
|
||||
type RuntimeTerminalShow,
|
||||
type RuntimeTerminalSummary,
|
||||
type RuntimeTerminalVisualGroupNode,
|
||||
type RuntimeTerminalVisualLayout,
|
||||
type RuntimeTerminalVisualLayoutNode,
|
||||
type RuntimeTerminalVisualPaneNode,
|
||||
type RuntimeTerminalVisualTab,
|
||||
type RuntimeSyncedLeaf,
|
||||
type RuntimeSyncedTab,
|
||||
type RuntimeMarkdownReadTabResult,
|
||||
type RuntimeMarkdownSaveTabResult,
|
||||
type RuntimeMobileSessionCreateTerminalResult,
|
||||
type RuntimeMobileSessionClientTab,
|
||||
type RuntimeMobileSessionTabCloseResult,
|
||||
type RuntimeMobileSessionMarkdownTab,
|
||||
type RuntimeMobileSessionTabMove,
|
||||
type RuntimeMobileSessionTabMoveResult,
|
||||
type RuntimeMobileSessionTabGroup,
|
||||
type RuntimeMobileSessionSnapshotTab,
|
||||
type RuntimeMobileSessionTerminalTab,
|
||||
type RuntimeMobileSessionBrowserTab,
|
||||
type RuntimeMobileSessionTabsRemovedResult,
|
||||
type RuntimeMobileSessionTabsResult,
|
||||
type RuntimeMobileSessionTabsSnapshot,
|
||||
type RuntimeSessionTabCloseReason,
|
||||
type RuntimeBrowserDriverState,
|
||||
type RuntimeTerminalDriverState,
|
||||
type RuntimeSyncWindowGraph,
|
||||
type RuntimeWorktreeListResult,
|
||||
type BrowserTabInfo,
|
||||
type BrowserScreencastResult
|
||||
} from '../../shared/runtime-types'
|
||||
import {
|
||||
LINEAR_SEARCH_MAX_LIMIT,
|
||||
|
|
@ -377,66 +448,6 @@ import {
|
|||
scanWorkspacePortProbes
|
||||
} from '../ports/workspace-port-ownership'
|
||||
import { advertisedUrlWatcher } from '../ports/advertised-url-watcher'
|
||||
import type {
|
||||
RuntimeGraphStatus,
|
||||
RuntimeRepoSearchRefs,
|
||||
RuntimeTerminalRead,
|
||||
RuntimeTerminalRename,
|
||||
RuntimeTerminalAgentStatus,
|
||||
RuntimeTerminalSend,
|
||||
RuntimeTerminalCreate,
|
||||
RuntimeTerminalPresentation,
|
||||
RuntimeTerminalSplit,
|
||||
RuntimeTerminalFocus,
|
||||
RuntimeTerminalClose,
|
||||
RuntimeTerminalListResult,
|
||||
RuntimeTerminalOrphanAdoptionRequest,
|
||||
RuntimeTerminalOrphanAdoptionResult,
|
||||
RuntimeWorktreeTerminalSleepResult,
|
||||
RuntimeTerminalResolvePane,
|
||||
RuntimeTerminalState,
|
||||
RuntimeStatus,
|
||||
RuntimeSyncWindowGraphResult,
|
||||
RuntimeTerminalWait,
|
||||
RuntimeTerminalWaitBlockedReason,
|
||||
RuntimeTerminalWaitCondition,
|
||||
RuntimeWorktreePsSummary,
|
||||
RuntimeWorktreeAgentRow,
|
||||
RuntimeWorktreeStatus,
|
||||
RuntimeSpeechModelSummary,
|
||||
RuntimeSpeechSetupState,
|
||||
RuntimeTerminalShow,
|
||||
RuntimeTerminalSummary,
|
||||
RuntimeTerminalVisualGroupNode,
|
||||
RuntimeTerminalVisualLayout,
|
||||
RuntimeTerminalVisualLayoutNode,
|
||||
RuntimeTerminalVisualPaneNode,
|
||||
RuntimeTerminalVisualTab,
|
||||
RuntimeSyncedLeaf,
|
||||
RuntimeSyncedTab,
|
||||
RuntimeMarkdownReadTabResult,
|
||||
RuntimeMarkdownSaveTabResult,
|
||||
RuntimeMobileSessionCreateTerminalResult,
|
||||
RuntimeMobileSessionClientTab,
|
||||
RuntimeMobileSessionTabCloseResult,
|
||||
RuntimeMobileSessionMarkdownTab,
|
||||
RuntimeMobileSessionTabMove,
|
||||
RuntimeMobileSessionTabMoveResult,
|
||||
RuntimeMobileSessionTabGroup,
|
||||
RuntimeMobileSessionSnapshotTab,
|
||||
RuntimeMobileSessionTerminalTab,
|
||||
RuntimeMobileSessionBrowserTab,
|
||||
RuntimeMobileSessionTabsRemovedResult,
|
||||
RuntimeMobileSessionTabsResult,
|
||||
RuntimeMobileSessionTabsSnapshot,
|
||||
RuntimeSessionTabCloseReason,
|
||||
RuntimeBrowserDriverState,
|
||||
RuntimeTerminalDriverState,
|
||||
RuntimeSyncWindowGraph,
|
||||
RuntimeWorktreeListResult,
|
||||
BrowserTabInfo,
|
||||
BrowserScreencastResult
|
||||
} from '../../shared/runtime-types'
|
||||
import type { AutomationService } from '../automations/service'
|
||||
import { RuntimeBrowserCommands } from './orca-runtime-browser'
|
||||
import { RemoteRuntimeTerminalCreateIdempotency } from './remote-runtime-terminal-create-idempotency'
|
||||
|
|
@ -474,7 +485,13 @@ import {
|
|||
deriveClientSessionTabSelection,
|
||||
projectClientSessionTabSelection
|
||||
} from './client-session-tab-selection'
|
||||
import type { PtyProviderBufferSnapshot } from '../providers/types'
|
||||
import type {
|
||||
PtyProviderBufferSnapshot,
|
||||
IFilesystemProvider,
|
||||
IPtyProvider,
|
||||
PtyProcessInfo,
|
||||
PtyTransientFact
|
||||
} from '../providers/types'
|
||||
import { ClaudeAgentTeamsService } from './claude-agent-teams-service'
|
||||
import type {
|
||||
AgentTeamsTmuxCompatRequest,
|
||||
|
|
@ -527,9 +544,9 @@ import {
|
|||
addPRReviewCommentReply,
|
||||
listLabels,
|
||||
listAssignableUsers,
|
||||
type MainWorkItem
|
||||
type MainWorkItem,
|
||||
type GitHubPRBranchLookupOptions
|
||||
} from '../github/client'
|
||||
import type { GitHubPRBranchLookupOptions } from '../github/client'
|
||||
import { resolveGitHubPrStartPoint } from '../github/pr-start-point'
|
||||
import {
|
||||
fetchGitHubPullRequestHeadRef,
|
||||
|
|
@ -579,19 +596,6 @@ import {
|
|||
type GitLabIssueListState
|
||||
} from '../gitlab/gitlab-preload-args'
|
||||
import { recordGitLabProjectRecent } from '../gitlab/gitlab-project-recents'
|
||||
import type {
|
||||
GitHubIssueUpdate,
|
||||
GitHubPullRequestStateUpdate,
|
||||
GitHubPRFile,
|
||||
GitHubPRReviewCommentInput,
|
||||
GitLabIssueUpdate,
|
||||
GitLabMRInlineCommentInput,
|
||||
GitLabProjectRef,
|
||||
GitLabWorkItem,
|
||||
ListWorkItemsResult,
|
||||
MRListState,
|
||||
PRRefreshOutcome
|
||||
} from '../../shared/types'
|
||||
import { inspectSetupScriptImportCandidates } from '../../shared/setup-script-imports'
|
||||
import type {
|
||||
CreateHostedReviewInput,
|
||||
|
|
@ -785,7 +789,7 @@ import {
|
|||
removeWorktree
|
||||
} from '../git/worktree'
|
||||
import type { AddWorktreeOptions, AddWorktreeResult } from '../git/worktree'
|
||||
import { isENOENT } from '../ipc/filesystem-auth'
|
||||
import { isENOENT, invalidateAuthorizedRootsCache } from '../ipc/filesystem-auth'
|
||||
import {
|
||||
createSetupRunnerScript,
|
||||
getDefaultTabCommandTrustContent,
|
||||
|
|
@ -862,7 +866,6 @@ import {
|
|||
UNREGISTERED_MISSING_WORKTREE_MESSAGE
|
||||
} from '../worktree-removal-safety'
|
||||
import { prefetchWorktreeCreateBase } from '../worktree-create-base-prefetch'
|
||||
import { invalidateAuthorizedRootsCache } from '../ipc/filesystem-auth'
|
||||
import { prepareLocalWorktreeRootForRepo } from '../worktree-root-preparation'
|
||||
import {
|
||||
closeLocalWatcherForWorktreePath,
|
||||
|
|
@ -894,12 +897,6 @@ import {
|
|||
createMobileSessionTabsNotifyCoalescer,
|
||||
type MobileSessionTabsNotifyCoalescer
|
||||
} from './mobile-session-tabs-notify-coalescer'
|
||||
import type {
|
||||
IFilesystemProvider,
|
||||
IPtyProvider,
|
||||
PtyProcessInfo,
|
||||
PtyTransientFact
|
||||
} from '../providers/types'
|
||||
import { getSshFilesystemProvider } from '../providers/ssh-filesystem-dispatch'
|
||||
import {
|
||||
assertFolderWorkspacePathUsable,
|
||||
|
|
@ -922,7 +919,6 @@ import type {
|
|||
} from '../codex-accounts/service'
|
||||
import type { CodexAccountSelectionTarget } from '../codex-accounts/runtime-selection'
|
||||
import type { RateLimitService } from '../rate-limits/service'
|
||||
import type { ClaudeRateLimitAccountsState, CodexRateLimitAccountsState } from '../../shared/types'
|
||||
import { applyPRBotAuthorOverride } from '../../shared/pr-bot-author-overrides'
|
||||
import type { CodexRateLimitResetOutcome, RateLimitState } from '../../shared/rate-limit-types'
|
||||
import type { CodexResetCreditExpectedScope } from '../../shared/codex-reset-credit-scope'
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ export class Coordinator {
|
|||
terminals.push(created.handle)
|
||||
this.opts.onLog(`Created worker terminal ${created.handle}`)
|
||||
} catch (err) {
|
||||
this.opts.onLog(`Failed to create terminal: ${err}`)
|
||||
this.opts.onLog(`Failed to create terminal: ${String(err)}`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -383,7 +383,7 @@ export class Coordinator {
|
|||
try {
|
||||
await this.dispatchTask(task, targetHandle)
|
||||
} catch (err) {
|
||||
this.opts.onLog(`Failed to dispatch task ${task.id}: ${err}`)
|
||||
this.opts.onLog(`Failed to dispatch task ${task.id}: ${String(err)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import {
|
|||
RUNTIME_CAPABILITIES,
|
||||
RUNTIME_PROTOCOL_VERSION
|
||||
} from '../../../../shared/protocol-version'
|
||||
import { AGENT_SESSION_RPC_ERROR_CODES } from '../../../../shared/agent-session-host-authority'
|
||||
import { AGENT_SESSION_OPERATION_FUTURE_SKEW_MS } from '../../../../shared/agent-session-host-authority'
|
||||
import {
|
||||
AGENT_SESSION_RPC_ERROR_CODES,
|
||||
AGENT_SESSION_OPERATION_FUTURE_SKEW_MS
|
||||
} from '../../../../shared/agent-session-host-authority'
|
||||
import type { OrcaRuntimeService } from '../../orca-runtime'
|
||||
import type { RpcRequest, RpcResponse } from '../core'
|
||||
import { RpcDispatcher } from '../dispatcher'
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ import { defineMethod, type RpcMethod } from '../core'
|
|||
import { OptionalBoolean } from '../schemas'
|
||||
import { restampAiVaultListResult } from '../../../ai-vault/session-list-results'
|
||||
import { AI_VAULT_AGENTS, AI_VAULT_SCOPE_PATHS_MAX_COUNT } from '../../../../shared/ai-vault-types'
|
||||
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
|
||||
import { parseExecutionHostId } from '../../../../shared/execution-host'
|
||||
import { LOCAL_EXECUTION_HOST_ID, parseExecutionHostId } from '../../../../shared/execution-host'
|
||||
|
||||
// Why: bound limit + scopePaths so a client cannot force an unbounded scan.
|
||||
// Each scopePath is a host-local match prefix (validated/capped, never used for
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ export async function runFileWatchStream(args: {
|
|||
let settled = false
|
||||
let setupFailed = false
|
||||
let watchReady = false
|
||||
let unwatch: (() => void) | null = null
|
||||
let unwatch: (() => Promise<void>) | null = null
|
||||
let terminalError: Error | null = null
|
||||
let setupPromise: Promise<() => void> | null = null
|
||||
let setupPromise: Promise<() => Promise<void>> | null = null
|
||||
let cleanupPromise: Promise<void> | null = null
|
||||
let logicalCleanupStarted = false
|
||||
let endEmitted = false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { z } from 'zod'
|
||||
import type { NativeChatBlock, NativeChatMessage } from '../../../../shared/native-chat-types'
|
||||
import type { AgentType } from '../../../../shared/native-chat-types'
|
||||
import type {
|
||||
NativeChatBlock,
|
||||
NativeChatMessage,
|
||||
AgentType
|
||||
} from '../../../../shared/native-chat-types'
|
||||
import {
|
||||
readNativeChatTranscriptTail,
|
||||
subscribeNativeChatTranscript
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ async function sendRequest(
|
|||
let buffer = ''
|
||||
socket.setEncoding('utf8')
|
||||
socket.once('error', reject)
|
||||
socket.on('data', (chunk) => {
|
||||
socket.on('data', (chunk: string) => {
|
||||
buffer += chunk
|
||||
const newlineIndex = buffer.indexOf('\n')
|
||||
if (newlineIndex === -1) {
|
||||
|
|
@ -4061,7 +4061,7 @@ describe('OrcaRuntimeRpcServer', () => {
|
|||
let buffer = ''
|
||||
socket.setEncoding('utf8')
|
||||
socket.once('error', reject)
|
||||
socket.on('data', (chunk) => {
|
||||
socket.on('data', (chunk: string) => {
|
||||
buffer += chunk
|
||||
const newlineIndex = buffer.indexOf('\n')
|
||||
if (newlineIndex === -1) {
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ export function parseJsonRpcMessage(payload: Buffer): JsonRpcMessage {
|
|||
const text = payload.toString('utf-8')
|
||||
const msg = JSON.parse(text) as JsonRpcMessage
|
||||
if (msg.jsonrpc !== '2.0') {
|
||||
throw new Error(`Invalid JSON-RPC version: ${(msg as Record<string, unknown>).jsonrpc}`)
|
||||
throw new Error(`Invalid JSON-RPC version: ${String((msg as Record<string, unknown>).jsonrpc)}`)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,10 +154,10 @@ vi.mock('./ssh-config-parser', () => ({
|
|||
|
||||
import {
|
||||
SshConnection,
|
||||
SshConnectionManager,
|
||||
shouldUseSystemSshTransport,
|
||||
type SshConnectionCallbacks
|
||||
} from './ssh-connection'
|
||||
import { SshConnectionManager } from './ssh-connection-manager'
|
||||
import { resolveWithSshG, type SshResolvedConfig } from './ssh-config-parser'
|
||||
import {
|
||||
downloadFileViaSystemSsh,
|
||||
|
|
|
|||
|
|
@ -1371,5 +1371,3 @@ export function shouldUseSystemSshTransport(
|
|||
resolved?.proxyJump != null
|
||||
)
|
||||
}
|
||||
|
||||
export { SshConnectionManager } from './ssh-connection-manager'
|
||||
|
|
|
|||
|
|
@ -32,7 +32,15 @@ import type {
|
|||
} from '../shared/terminal-render-desync-evidence'
|
||||
import type { MobileRelayStatus } from '../shared/mobile-relay-status'
|
||||
import type { MobilePairingConnectionMode } from '../shared/mobile-pairing-connection-mode'
|
||||
import type { SshMutationExpectation } from '../shared/ssh-types'
|
||||
import type {
|
||||
SshMutationExpectation,
|
||||
SshConnectionState,
|
||||
SshConfigImportResult,
|
||||
SshTargetAddResult,
|
||||
SshTarget,
|
||||
PortForwardEntry,
|
||||
EnrichedDetectedPort
|
||||
} from '../shared/ssh-types'
|
||||
import type {
|
||||
CreateLocalOrcaProfileArgs,
|
||||
CreateLocalOrcaProfileResult,
|
||||
|
|
@ -260,10 +268,8 @@ import type {
|
|||
import type { SetupScriptImportCandidate } from '../shared/setup-script-imports'
|
||||
import type { GitHistoryOptions, GitHistoryResult } from '../shared/git-history'
|
||||
import type { PublicKnownRuntimeEnvironment } from '../shared/runtime-environments'
|
||||
import type {
|
||||
EphemeralVmRecipeDoctorResult,
|
||||
EphemeralVmRecipeResultWarning
|
||||
} from '../shared/ephemeral-vm-recipes'
|
||||
import type { EphemeralVmRecipeDoctorResult } from '../shared/ephemeral-vm-recipes'
|
||||
import type { EphemeralVmRecipeResultWarning } from '../shared/ephemeral-vm-recipe-diagnostics'
|
||||
import type { EphemeralVmRuntimeRecord } from '../shared/ephemeral-vm-runtimes'
|
||||
import type { RuntimeAccessGrant } from '../shared/runtime-access-grants'
|
||||
import type { RuntimeRpcResponse } from '../shared/runtime-rpc-envelope'
|
||||
|
|
@ -431,14 +437,6 @@ import type {
|
|||
WorkspacePortScanResult
|
||||
} from '../shared/workspace-ports'
|
||||
import type { GhAuthDiagnostic } from '../shared/github-auth-types'
|
||||
import type {
|
||||
SshConnectionState,
|
||||
SshConfigImportResult,
|
||||
SshTargetAddResult,
|
||||
SshTarget,
|
||||
PortForwardEntry,
|
||||
EnrichedDetectedPort
|
||||
} from '../shared/ssh-types'
|
||||
import type {
|
||||
CodexUsageBreakdownKind,
|
||||
CodexUsageBreakdownRow,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,15 @@ import type {
|
|||
} from '../shared/agent-session-resume'
|
||||
import type { MobileRelayStatus } from '../shared/mobile-relay-status'
|
||||
import type { MobilePairingConnectionMode } from '../shared/mobile-pairing-connection-mode'
|
||||
import type { SshMutationExpectation } from '../shared/ssh-types'
|
||||
import type {
|
||||
SshMutationExpectation,
|
||||
SshConnectionState,
|
||||
SshConfigImportResult,
|
||||
SshTargetAddResult,
|
||||
SshTarget,
|
||||
PortForwardEntry,
|
||||
EnrichedDetectedPort
|
||||
} from '../shared/ssh-types'
|
||||
import type {
|
||||
PluginPanelActionOutcome,
|
||||
PluginPanelEntry
|
||||
|
|
@ -158,14 +166,6 @@ import {
|
|||
richMarkdownContextMenuCommandChannel,
|
||||
type RichMarkdownContextMenuCommandPayload
|
||||
} from '../shared/rich-markdown-context-menu'
|
||||
import type {
|
||||
SshConnectionState,
|
||||
SshConfigImportResult,
|
||||
SshTargetAddResult,
|
||||
SshTarget,
|
||||
PortForwardEntry,
|
||||
EnrichedDetectedPort
|
||||
} from '../shared/ssh-types'
|
||||
import type {
|
||||
AgentStatusClearIpcPayload,
|
||||
AgentStatusIpcPayload,
|
||||
|
|
@ -182,7 +182,18 @@ import type {
|
|||
SpeechTranscriptEvent
|
||||
} from '../shared/speech-types'
|
||||
import type { TelemetryConsentState } from '../shared/telemetry-consent-types'
|
||||
import type { PreflightRuntimeContext, RefreshAgentsResult } from './api-types'
|
||||
import type {
|
||||
PreflightRuntimeContext,
|
||||
RefreshAgentsResult,
|
||||
NativeChatAppendedPayload,
|
||||
NativeChatReadSessionResult,
|
||||
NativeChatSubscriptionFrame,
|
||||
PluginHostInstallResult,
|
||||
PluginHostInstallSource,
|
||||
PluginHostListEntry,
|
||||
PluginHostLogLine,
|
||||
PreloadApi
|
||||
} from './api-types'
|
||||
import type { AgentKind, LaunchSource, RequestKind } from '../shared/telemetry-events'
|
||||
import type { AppStarSource } from '../shared/gh-star-source'
|
||||
import type { ExecutionHostId } from '../shared/execution-host'
|
||||
|
|
@ -205,11 +216,6 @@ import type { KeybindingActionId, KeybindingFileSnapshot } from '../shared/keybi
|
|||
import type { AiVaultListArgs, AiVaultSubagentListArgs } from '../shared/ai-vault-types'
|
||||
import type { AiVaultPrepareSessionResumeArgs } from '../shared/ai-vault-resume-preparation'
|
||||
import type { AgentType } from '../shared/native-chat-types'
|
||||
import type {
|
||||
NativeChatAppendedPayload,
|
||||
NativeChatReadSessionResult,
|
||||
NativeChatSubscriptionFrame
|
||||
} from './api-types'
|
||||
import {
|
||||
ORCA_APP_RESTART_ABORTED_EVENT,
|
||||
ORCA_APP_RESTART_STARTED_EVENT,
|
||||
|
|
@ -252,13 +258,6 @@ import type {
|
|||
} from '../shared/crash-reporting'
|
||||
import type { RendererHeapStatistics } from '../shared/renderer-heap-statistics'
|
||||
import { readRendererHeapStatistics } from './renderer-heap-statistics-reader'
|
||||
import type {
|
||||
PluginHostInstallResult,
|
||||
PluginHostInstallSource,
|
||||
PluginHostListEntry,
|
||||
PluginHostLogLine,
|
||||
PreloadApi
|
||||
} from './api-types'
|
||||
import {
|
||||
createUpdaterQuitAbortRelay,
|
||||
prepareRendererForAppRestart
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ export function parseJsonRpcMessage(payload: Buffer): JsonRpcMessage {
|
|||
const text = payload.toString('utf-8')
|
||||
const msg = JSON.parse(text) as JsonRpcMessage
|
||||
if (msg.jsonrpc !== '2.0') {
|
||||
throw new Error(`Invalid JSON-RPC version: ${(msg as Record<string, unknown>).jsonrpc}`)
|
||||
throw new Error(`Invalid JSON-RPC version: ${String((msg as Record<string, unknown>).jsonrpc)}`)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ async function main(): Promise<void> {
|
|||
})
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
relayLogLine(`[relay] Unhandled rejection: ${reason}`)
|
||||
relayLogLine(`[relay] Unhandled rejection: ${String(reason)}`)
|
||||
})
|
||||
|
||||
// Why: guards writes after the stdin/SSH channel drops so keepalive/pty.data frames don't hit a dead pipe (EPIPE).
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import React from 'react'
|
||||
import { act } from 'react'
|
||||
import React, { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import NewWorkspaceComposerCard from './NewWorkspaceComposerCard'
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ import { SettingsSwitch } from '@/components/settings/SettingsFormControls'
|
|||
import type RepoCombobox from '@/components/repo/RepoCombobox'
|
||||
import AgentCombobox from '@/components/agent/AgentCombobox'
|
||||
import { getAgentCatalog } from '@/lib/agent-catalog'
|
||||
import {
|
||||
DEFAULT_DISABLED_TUI_AGENTS,
|
||||
filterEnabledTuiAgents
|
||||
} from '../../../shared/tui-agent-selection'
|
||||
import { useAppStore } from '@/store'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { WORKSPACE_FILE_PATH_MIME } from '@/lib/workspace-file-drag'
|
||||
|
|
@ -44,7 +48,6 @@ import {
|
|||
} from '@/lib/text-control-paste'
|
||||
import { getScreenSubmitModifierLabel } from '@/lib/screen-submit-shortcut'
|
||||
import { useContextualTour } from '@/components/contextual-tours/use-contextual-tour'
|
||||
import { filterEnabledTuiAgents } from '../../../shared/tui-agent-selection'
|
||||
import type {
|
||||
GitHubWorkItem,
|
||||
GitLabWorkItem,
|
||||
|
|
@ -955,7 +958,9 @@ export default function NewWorkspaceComposerCard({
|
|||
const openModal = useAppStore((s) => s.openModal)
|
||||
const activeModal = useAppStore((s) => s.activeModal)
|
||||
const defaultTuiAgent = useAppStore((s) => s.settings?.defaultTuiAgent ?? null)
|
||||
const disabledTuiAgents = useAppStore((s) => s.settings?.disabledTuiAgents ?? [])
|
||||
const disabledTuiAgents = useAppStore(
|
||||
(s) => s.settings?.disabledTuiAgents ?? DEFAULT_DISABLED_TUI_AGENTS
|
||||
)
|
||||
const updateSettings = useAppStore((s) => s.updateSettings)
|
||||
const nameInputFocusFrameRef = React.useRef<number | null>(null)
|
||||
const branchNameInputId = React.useId()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ import { getLocalPreflightContext, localPreflightContextKey } from '@/lib/local-
|
|||
import { getProviderRuntimeContextKey } from '@/lib/provider-runtime-context'
|
||||
import {
|
||||
getSettingsFocusedExecutionHostId,
|
||||
parseExecutionHostId
|
||||
parseExecutionHostId,
|
||||
getRepoExecutionHostId
|
||||
} from '../../../shared/execution-host'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ButtonGroup } from '@/components/ui/button-group'
|
||||
|
|
@ -179,7 +180,6 @@ import {
|
|||
readLinearBoardIssueDragData,
|
||||
writeLinearBoardIssueDragData
|
||||
} from '@/lib/linear-board-drag-payload'
|
||||
import { getRepoExecutionHostId } from '../../../shared/execution-host'
|
||||
import { projectHostSetupProjectionFromRepos } from '../../../shared/project-host-setup-projection'
|
||||
import { TASK_SOURCE_CONTEXT_RUNTIME_CAPABILITY } from '../../../shared/protocol-version'
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import React, { type ReactNode } from 'react'
|
||||
import { act } from 'react'
|
||||
import React, { type ReactNode, act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { AgentSessionContinuationRequest } from '@/lib/agent-session-continuation'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import { createRef, type MutableRefObject } from 'react'
|
||||
import { act } from 'react'
|
||||
import { createRef, type MutableRefObject, act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { DictationState } from '../../../../shared/speech-types'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// @vitest-environment happy-dom
|
||||
import { Suspense } from 'react'
|
||||
import { act } from 'react'
|
||||
import { act, Suspense } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { OpenFile } from '@/store/slices/editor'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import React, { useRef, useCallback, useEffect, useLayoutEffect, useMemo, useSta
|
|||
import Editor, { type OnMount } from '@monaco-editor/react'
|
||||
import type { editor } from 'monaco-editor'
|
||||
import { toast } from 'sonner'
|
||||
import type { MarkdownDocument } from '../../../../shared/types'
|
||||
import type { MarkdownDocument, DiffComment } from '../../../../shared/types'
|
||||
import { useAppStore } from '@/store'
|
||||
import { scrollTopCache, cursorPositionCache, setWithLRU } from '@/lib/scroll-cache'
|
||||
import '@/lib/monaco-setup'
|
||||
|
|
@ -36,7 +36,6 @@ import {
|
|||
} from './monaco-markdown-doc-link-decorations'
|
||||
import { buildGitConflictDecorations, hasGitConflictMarkers } from './monaco-conflict-decorations'
|
||||
import { selectWorktreeDiffComments } from '@/store/worktree-diff-comments-selector'
|
||||
import type { DiffComment } from '../../../../shared/types'
|
||||
import { isMarkdownComment } from '@/lib/diff-comment-compat'
|
||||
import { formatMarkdownReviewNotes, type MarkdownReviewNote } from '@/lib/markdown-review-notes'
|
||||
import { useDiffCommentDecorator } from '../diff-comments/useDiffCommentDecorator'
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ import {
|
|||
deriveNotesSendAgentTargets,
|
||||
type NotesSendAgentTarget
|
||||
} from '@/lib/notes-send-agent-targets'
|
||||
import { agentKindForAgentType, formatAgentTypeLabel } from '@/lib/agent-status'
|
||||
import { agentTypeToIconAgent } from '@/lib/agent-status'
|
||||
import {
|
||||
agentKindForAgentType,
|
||||
formatAgentTypeLabel,
|
||||
agentTypeToIconAgent
|
||||
} from '@/lib/agent-status'
|
||||
import { track } from '@/lib/telemetry'
|
||||
import { useNow } from '@/components/dashboard/useNow'
|
||||
import type { DashboardAgentRow as DashboardAgentRowData } from '@/components/dashboard/useDashboardData'
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ function markdownAfterTextReplace(content: string, search: string, replacement:
|
|||
})
|
||||
|
||||
try {
|
||||
let from: number | null = null
|
||||
let from = -1
|
||||
editor.state.doc.descendants((node, pos) => {
|
||||
if (from !== null || !node.isText || !node.text) {
|
||||
if (from !== -1 || !node.isText || !node.text) {
|
||||
return
|
||||
}
|
||||
const index = node.text.indexOf(search)
|
||||
|
|
@ -42,7 +42,7 @@ function markdownAfterTextReplace(content: string, search: string, replacement:
|
|||
from = pos + index
|
||||
}
|
||||
})
|
||||
if (from === null) {
|
||||
if (from === -1) {
|
||||
throw new Error(`Missing text: ${search}`)
|
||||
}
|
||||
editor.view.dispatch(editor.state.tr.insertText(replacement, from, from + search.length))
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ export function useEditorPanelContentState({
|
|||
[file.id]: {
|
||||
kind: 'text',
|
||||
originalContent: '',
|
||||
modifiedContent: `Error loading diff: ${err}`,
|
||||
modifiedContent: `Error loading diff: ${String(err)}`,
|
||||
originalIsBinary: false,
|
||||
modifiedIsBinary: false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { FEATURE_WALL_MAX_DWELL_MS } from '../../../../shared/feature-wall-telemetry'
|
||||
import type { FeatureWallExitAction } from '../../../../shared/feature-wall-tour-depth'
|
||||
import type { FeatureWallTourDepthSummary } from '../../../../shared/feature-wall-tour-depth'
|
||||
import type {
|
||||
FeatureWallExitAction,
|
||||
FeatureWallTourDepthSummary
|
||||
} from '../../../../shared/feature-wall-tour-depth'
|
||||
import type {
|
||||
EventProps,
|
||||
FeatureWallOpenSourceTelemetry
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ import { buildAgentStartupPlan } from '@/lib/tui-agent-startup'
|
|||
import { tuiAgentToAgentKind } from '@/lib/telemetry'
|
||||
import { useAppStore } from '@/store'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
import { isTuiAgentEnabled } from '../../../../shared/tui-agent-selection'
|
||||
import {
|
||||
DEFAULT_DISABLED_TUI_AGENTS,
|
||||
isTuiAgentEnabled
|
||||
} from '../../../../shared/tui-agent-selection'
|
||||
import {
|
||||
resolveTuiAgentLaunchArgs,
|
||||
resolveTuiAgentLaunchEnv
|
||||
|
|
@ -49,7 +52,9 @@ export function FloatingTerminalWindowControls({
|
|||
const maximizeShortcutLabel = useOptionalShortcutLabel('floatingWorkspace.maximize')
|
||||
const minimizeShortcutLabel = useOptionalShortcutLabel('floatingWorkspace.minimize')
|
||||
|
||||
const disabledTuiAgents = useAppStore((s) => s.settings?.disabledTuiAgents ?? [])
|
||||
const disabledTuiAgents = useAppStore(
|
||||
(s) => s.settings?.disabledTuiAgents ?? DEFAULT_DISABLED_TUI_AGENTS
|
||||
)
|
||||
const defaultAgent =
|
||||
defaultTuiAgent &&
|
||||
defaultTuiAgent !== 'blank' &&
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ import { Input } from '@/components/ui/input'
|
|||
import CommentMarkdown from '@/components/sidebar/CommentMarkdown'
|
||||
import { useAppStore } from '@/store'
|
||||
import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client'
|
||||
import type { GitHubWorkItemDetails } from '../../../../../shared/types'
|
||||
import type { GitHubWorkItemDetails, GlobalSettings } from '../../../../../shared/types'
|
||||
import type { GitHubItemDialogProjectOrigin } from '@/components/GitHubItemDialog'
|
||||
import type { GlobalSettings } from '../../../../../shared/types'
|
||||
import { LabelsEditor } from './LabelsEditor'
|
||||
import { AssigneesEditor } from './AssigneesEditor'
|
||||
import { CommentsList, NewCommentForm } from './Comments'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import { translate } from '@/i18n/i18n'
|
||||
import type { MobileNetworkInterface } from '../settings/mobile-network-interface-selection'
|
||||
import { HeroFlow, HeroIntro, HeroPaired, type PairedDevice, type Platform } from './MobileHero'
|
||||
import type { StepIndex } from './MobileHero'
|
||||
import {
|
||||
HeroFlow,
|
||||
HeroIntro,
|
||||
HeroPaired,
|
||||
type PairedDevice,
|
||||
type Platform,
|
||||
type StepIndex
|
||||
} from './MobileHero'
|
||||
import { getInstallCopy, type IosChannel } from './mobile-platform-copy'
|
||||
import type { MobilePageStage } from './mobile-page-stage'
|
||||
import { MobilePageToolbar } from './MobilePageToolbar'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { act } from 'react'
|
||||
import { act, createElement, useRef, useState } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { createElement, useRef, useState } from 'react'
|
||||
import {
|
||||
clearNativeChatAttachmentCacheForTests,
|
||||
readNativeChatAttachmentCache,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { act } from 'react'
|
||||
import { act, createElement } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { createElement } from 'react'
|
||||
import type { NativeChatAttachmentOwner } from './native-chat-attachment-upload'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { act } from 'react'
|
||||
import { act, createElement } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { createElement } from 'react'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
resolveNativeChatAttachmentOwner: vi.fn(),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import { createElement, useEffect } from 'react'
|
||||
import { act } from 'react'
|
||||
import { createElement, useEffect, act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getDefaultOnboardingState } from '../../../../shared/constants'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import path from 'node:path'
|
||||
import React, { type ReactNode, useState } from 'react'
|
||||
import { act } from 'react'
|
||||
import React, { type ReactNode, useState, act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getDefaultSettings } from '../../../../shared/constants'
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ import {
|
|||
Pencil,
|
||||
SlidersHorizontal,
|
||||
Trash,
|
||||
X
|
||||
X,
|
||||
ExternalLink
|
||||
} from 'lucide-react'
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Plug, Files, GitBranch, ListChecks, PanelRight, Workflow } from 'lucide-react'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { ActiveRightSidebarTab } from '@/store/slices/editor'
|
||||
import type { ActiveRightSidebarTab, ActivityBarPosition } from '@/store/slices/editor'
|
||||
import { useRepoById } from '@/store/selectors'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSidebarResize } from '@/hooks/useSidebarResize'
|
||||
import type { ActivityBarPosition } from '@/store/slices/editor'
|
||||
import { isFolderRepo } from '../../../../shared/repo-kind'
|
||||
import { parseWorkspaceKey } from '../../../../shared/workspace-scope'
|
||||
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip'
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ export function resolveBlockedCreateReviewNoticeMessage(
|
|||
case 'unsupported_provider':
|
||||
// Why: base_not_on_remote is a create-time hard failure surfaced as an error
|
||||
// result, not an inline-actionable eligibility state, so it is non-clickable.
|
||||
// falls through
|
||||
case 'base_not_on_remote':
|
||||
case null:
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { useAppStore } from '@/store'
|
|||
import { OpenInApplicationIcon } from '@/lib/open-in-app-catalog'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { getLocalFileManagerLabel } from '@/lib/local-file-manager-label'
|
||||
import { NO_OPEN_IN_APPLICATIONS } from '@/lib/open-in-application-selection'
|
||||
import {
|
||||
getOpenInEntryAvailability,
|
||||
getWorktreeOpenInEntries,
|
||||
|
|
@ -42,7 +43,9 @@ export function SourceControlEntryContextMenu({
|
|||
onOpenChange,
|
||||
children
|
||||
}: SourceControlEntryContextMenuProps): React.JSX.Element {
|
||||
const openInApplications = useAppStore((s) => s.settings?.openInApplications ?? [])
|
||||
const openInApplications = useAppStore(
|
||||
(s) => s.settings?.openInApplications ?? NO_OPEN_IN_APPLICATIONS
|
||||
)
|
||||
const settings = useAppStore((s) => s.settings)
|
||||
const fileManagerLabel = getLocalFileManagerLabel()
|
||||
const openInEntries = React.useMemo(
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ import { toast } from 'sonner'
|
|||
import { useConfirmationDialog } from '@/components/confirmation-dialog'
|
||||
import type { GitHubPRAutoMergeAction } from '@/components/github-pr-merge-state'
|
||||
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
|
||||
import type { PRInfo, Repo } from '../../../../shared/types'
|
||||
import type { GitHubPRMergeMethod } from '../../../../shared/types'
|
||||
import type { PRInfo, Repo, GitHubPRMergeMethod } from '../../../../shared/types'
|
||||
import {
|
||||
mergeGitHubHostedReview,
|
||||
setGitHubHostedReviewAutoMerge,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import React from 'react'
|
||||
import { act } from 'react'
|
||||
import React, { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { HostedReviewCreationEligibility } from '../../../../shared/hosted-review'
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@ import {
|
|||
getRuntimeRepoBaseRefDefault,
|
||||
searchRuntimeRepoBaseRefDetails
|
||||
} from '@/runtime/runtime-repo-client'
|
||||
import type { Repo } from '../../../../shared/types'
|
||||
import type { Repo, BaseRefSearchResult } from '../../../../shared/types'
|
||||
import type { HostedReviewCreationEligibility } from '../../../../shared/hosted-review'
|
||||
import { normalizeHostedReviewBaseRef } from '../../../../shared/hosted-review-refs'
|
||||
import type { BaseRefSearchResult } from '../../../../shared/types'
|
||||
import {
|
||||
DEFAULT_SOURCE_CONTROL_AI_PR_CREATION_DEFAULTS,
|
||||
resolveSourceControlAiForOperation
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type EmulatorAvailability = {
|
|||
|
||||
type MobileEmulatorSettingsPaneProps = {
|
||||
settings: GlobalSettings
|
||||
updateSettings: (updates: Partial<GlobalSettings>) => void
|
||||
updateSettings: (updates: Partial<GlobalSettings>) => Promise<void>
|
||||
}
|
||||
|
||||
const AUTOMATIC_DEVICE_VALUE = '__orca_automatic_emulator_device__'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// @vitest-environment happy-dom
|
||||
|
||||
import React from 'react'
|
||||
import { act } from 'react'
|
||||
import React, { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { Repo } from '../../../../shared/types'
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ import {
|
|||
getTerminalWindowSearchEntries
|
||||
} from './terminal-search'
|
||||
import { Button } from '../ui/button'
|
||||
import { SettingsRow, SettingsSubsectionHeader } from './SettingsFormControls'
|
||||
import { SettingsRow, SettingsSubsectionHeader, FontAutocomplete } from './SettingsFormControls'
|
||||
import { SearchableSetting } from './SearchableSetting'
|
||||
import { FontAutocomplete } from './SettingsFormControls'
|
||||
import { TerminalFontSizeSetting } from './TerminalFontSizeSetting'
|
||||
import { TerminalAdvancedTypographyControls } from './TerminalAdvancedTypographyControls'
|
||||
import { TerminalThemeCatalogSection } from './TerminalThemeSections'
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue