mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
refactor(preload): split the preload contract into per-domain api modules (#14403)
`src/preload/api-types.ts` was 3,752 raw lines (3,533 counted, 11.8x the 300-line budget) behind an `eslint-disable max-lines`. Almost all of it was a single `PreloadApi` object type whose ~83 namespace properties were declared inline, so any IPC surface change meant editing one 2,600-line type. Give each namespace a named type in its own module under `src/preload/api/` (`pty-api.ts`, `filesystem-api.ts`, `github-pull-request-api.ts`, ...) and recompose `PreloadApi` from those names. `api-types.ts` keeps the `declare global` Window augmentation and re-exports every moved name, so all 52 import sites are untouched. Two shapes needed care to stay type-identical rather than merely compatible: - Three keys (`gh`, `git`, `ui`) are composed from two modules each. A plain intersection is NOT identical to the original flat object literal, so those use a `Merged<T>` mapped type; a negative control confirmed that dropping it fails the parity assertion. - Keys whose module groups several namespaces use indexed access (`fs: FilesystemApi['fs']`) to preserve exact identity and source order. `config/tsconfig.web.json` and `tsconfig.tc.web.json` enumerate files by path, so they need `src/preload/api/**/*` alongside the existing `api-types.ts` seed or the web projects fail TS6307. Verified by exact type identity, not assignability: 41 assertions of the form `Equals<Now.X, Before.X>` against a frozen pre-split snapshot, covering every exported name, plus a per-key pass over all 83 `PreloadApi` keys. All three projects typecheck clean with those assertions active. Verification note: these tsconfigs are `composite: true`, and `tsc --noEmit` will reuse a stale `.tsbuildinfo` and report clean for a state that genuinely fails. Every result above was produced after deleting the buildinfo, including a negative control confirming the gate still fails on deliberate drift. Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
This commit is contained in:
@@ -167,7 +167,6 @@ inline src/main/window/attach-main-window-services.ts
|
||||
inline src/main/window/createMainWindow.test.ts
|
||||
inline src/main/window/createMainWindow.ts
|
||||
inline src/main/workspace-space-analysis.ts
|
||||
inline src/preload/api-types.ts
|
||||
inline src/preload/index.ts
|
||||
inline src/relay/agent-hook-server.ts
|
||||
inline src/relay/dispatcher.ts
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"../src/renderer/src/**/*",
|
||||
"../src/renderer/src/**/*.tsx",
|
||||
"../src/preload/api-types.ts",
|
||||
"../src/preload/api/**/*",
|
||||
"../src/shared/**/*",
|
||||
"../src/main/gitlab/mappers.ts",
|
||||
"../src/main/ipc/worktree-branch-name.ts",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"../src/renderer/src/**/*",
|
||||
"../src/renderer/src/**/*.tsx",
|
||||
"../src/preload/api-types.ts",
|
||||
"../src/preload/api/**/*",
|
||||
"../src/shared/**/*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
||||
+181
-3722
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
import type { ClaudeRateLimitAccountsState, CodexRateLimitAccountsState } from '../../shared/types'
|
||||
import type { CodexConfigSyncStatus } from '../../shared/codex-config-sync-types'
|
||||
import type { GrokAccountStatus } from '../../shared/rate-limit-types'
|
||||
|
||||
export type CodexAccountsApi = {
|
||||
list: () => Promise<CodexRateLimitAccountsState>
|
||||
add: (args?: {
|
||||
runtime?: 'host' | 'wsl'
|
||||
wslDistro?: string | null
|
||||
}) => Promise<CodexRateLimitAccountsState>
|
||||
reauthenticate: (args: { accountId: string }) => Promise<CodexRateLimitAccountsState>
|
||||
remove: (args: { accountId: string }) => Promise<CodexRateLimitAccountsState>
|
||||
select: (args: {
|
||||
accountId: string | null
|
||||
runtime?: 'host' | 'wsl'
|
||||
wslDistro?: string | null
|
||||
}) => Promise<CodexRateLimitAccountsState>
|
||||
/** Live PTYs whose baked CODEX_HOME still points at a deselected account. */
|
||||
listStalePanes: (args: { ptyIds: string[] }) => Promise<
|
||||
{
|
||||
ptyId: string
|
||||
launchAccountId: string | null
|
||||
activeAccountId: string | null
|
||||
/** Optional for compatibility with a pre-reason main process. */
|
||||
reason?: 'account-change' | 'home-route-change'
|
||||
}[]
|
||||
>
|
||||
/** The selection lane each PTY launched from, keyed by pty id; unrecorded panes are absent. */
|
||||
listRecordedPaneLanes: (args: { ptyIds: string[] }) => Promise<Record<string, string>>
|
||||
/** Drops launch records so a dismissed prompt stays dismissed across restarts. */
|
||||
forgetStalePanes: (args: { ptyIds: string[] }) => Promise<void>
|
||||
}
|
||||
|
||||
export type ClaudeAccountsApi = {
|
||||
list: () => Promise<ClaudeRateLimitAccountsState>
|
||||
add: (args?: {
|
||||
runtime?: 'host' | 'wsl'
|
||||
wslDistro?: string | null
|
||||
}) => Promise<ClaudeRateLimitAccountsState>
|
||||
cancelPendingLogin: () => Promise<boolean>
|
||||
reauthenticate: (args: { accountId: string }) => Promise<ClaudeRateLimitAccountsState>
|
||||
remove: (args: { accountId: string }) => Promise<ClaudeRateLimitAccountsState>
|
||||
select: (args: {
|
||||
accountId: string | null
|
||||
runtime?: 'host' | 'wsl'
|
||||
wslDistro?: string | null
|
||||
}) => Promise<ClaudeRateLimitAccountsState>
|
||||
}
|
||||
|
||||
export type GrokAccountsApi = {
|
||||
getStatus: () => Promise<GrokAccountStatus>
|
||||
}
|
||||
|
||||
export type MinimaxCredentialsApi = {
|
||||
getStatus: () => Promise<{ configured: boolean }>
|
||||
saveCookie: (cookie: string) => Promise<{ configured: boolean }>
|
||||
clearCookie: () => Promise<{ configured: boolean }>
|
||||
}
|
||||
|
||||
export type CodexConfigSyncApi = {
|
||||
status: () => Promise<CodexConfigSyncStatus>
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { OrcaHooks, WorktreeSetupLaunch } from '../../shared/types'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type { SetupScriptImportCandidate } from '../../shared/setup-script-imports'
|
||||
import type { AgentHookInstallStatus } from '../../shared/agent-hook-types'
|
||||
|
||||
export type HooksApi = {
|
||||
check: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{
|
||||
status?: 'ok' | 'error'
|
||||
hasHooks: boolean
|
||||
hooks: OrcaHooks | null
|
||||
mayNeedUpdate: boolean
|
||||
}>
|
||||
inspectSetupScriptImports: (args: {
|
||||
repoId: string
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<SetupScriptImportCandidate[]>
|
||||
createIssueCommandRunner: (args: {
|
||||
repoId: string
|
||||
worktreePath: string
|
||||
command: string
|
||||
}) => Promise<WorktreeSetupLaunch>
|
||||
readIssueCommand: (args: { repoId: string; hostId?: ExecutionHostId }) => Promise<{
|
||||
status?: 'ok' | 'error'
|
||||
localContent: string | null
|
||||
sharedContent: string | null
|
||||
effectiveContent: string | null
|
||||
localFilePath: string
|
||||
source: 'local' | 'shared' | 'none'
|
||||
}>
|
||||
writeIssueCommand: (args: {
|
||||
repoId: string
|
||||
content: string
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<void>
|
||||
}
|
||||
|
||||
export type AgentHooksApi = {
|
||||
claudeStatus: () => Promise<AgentHookInstallStatus>
|
||||
openClaudeStatus: () => Promise<AgentHookInstallStatus>
|
||||
codexStatus: () => Promise<AgentHookInstallStatus>
|
||||
geminiStatus: () => Promise<AgentHookInstallStatus>
|
||||
antigravityStatus: () => Promise<AgentHookInstallStatus>
|
||||
ampStatus: () => Promise<AgentHookInstallStatus>
|
||||
cursorStatus: () => Promise<AgentHookInstallStatus>
|
||||
droidStatus: () => Promise<AgentHookInstallStatus>
|
||||
commandCodeStatus: () => Promise<AgentHookInstallStatus>
|
||||
grokStatus: () => Promise<AgentHookInstallStatus>
|
||||
copilotStatus: () => Promise<AgentHookInstallStatus>
|
||||
hermesStatus: () => Promise<AgentHookInstallStatus>
|
||||
devinStatus: () => Promise<AgentHookInstallStatus>
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { SkillDiscoveryResult, SkillDiscoveryTarget } from '../../shared/skills'
|
||||
import type {
|
||||
SkillFreshnessInventory,
|
||||
SkillUpdateRun,
|
||||
SkillUpdateStartResult
|
||||
} from '../../shared/skill-freshness'
|
||||
|
||||
export type SkillsApi = {
|
||||
discover: (target?: SkillDiscoveryTarget) => Promise<SkillDiscoveryResult>
|
||||
freshnessInventory: () => Promise<SkillFreshnessInventory>
|
||||
startUpdateRun: (names: string[]) => Promise<SkillUpdateStartResult>
|
||||
cancelUpdateRun: () => Promise<void>
|
||||
acknowledgeUpdateRun: () => Promise<void>
|
||||
getUpdateRun: () => Promise<SkillUpdateRun>
|
||||
onUpdateRun: (callback: (run: SkillUpdateRun) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type {
|
||||
AgentStatusClearIpcPayload,
|
||||
AgentStatusIpcPayload,
|
||||
MigrationUnsupportedPtyEntry
|
||||
} from '../../shared/agent-status-types'
|
||||
import type { AgentInterruptInferenceRequest } from '../../shared/agent-interrupt-intent'
|
||||
import type { AgentQuestionAnsweredInferenceRequest } from '../../shared/agent-question-answered-intent'
|
||||
import type { ComputerAwakeStatus } from '../../shared/computer-awake-mode'
|
||||
|
||||
export type AgentStatusApi = {
|
||||
/** Listen for agent status updates forwarded from native hook receivers. */
|
||||
onSet: (callback: (data: AgentStatusIpcPayload) => void) => () => void
|
||||
/** Listen for main-process cleanup that evicted cached hook status. */
|
||||
onClear: (callback: (data: AgentStatusClearIpcPayload) => void) => () => void
|
||||
/** Return the current main-process hook cache after renderer hydration. */
|
||||
getSnapshot: () => Promise<AgentStatusIpcPayload[]>
|
||||
inferInterrupt: (request: AgentInterruptInferenceRequest) => Promise<boolean>
|
||||
/** Guarded clear for an answered AskUserQuestion wait — the CLI emits no hook at answer time, so the renderer reports the submit keystroke. */
|
||||
inferQuestionAnswered: (request: AgentQuestionAnsweredInferenceRequest) => Promise<boolean>
|
||||
/** Listen for PTYs on a legacy numeric pane key that have registry-backed UUID pane proof. */
|
||||
onMigrationUnsupported: (callback: (entry: MigrationUnsupportedPtyEntry) => void) => () => void
|
||||
onMigrationUnsupportedClear: (callback: (data: { ptyId: string }) => void) => () => void
|
||||
onLegacyWorkerTerminalRecovery: (
|
||||
callback: (data: {
|
||||
paneKey: string
|
||||
resolution: 'adopted' | 'exited' | 'rolled_back'
|
||||
ptyId?: string
|
||||
}) => void
|
||||
) => () => void
|
||||
getMigrationUnsupportedSnapshot: () => Promise<MigrationUnsupportedPtyEntry[]>
|
||||
/** Drop a paneKey from the main-process hook cache and on-disk last-status file. Fire-and-forget. */
|
||||
drop: (paneKey: string) => void
|
||||
/** Drop every cached hook status under one terminal tab prefix. Fire-and-forget. */
|
||||
dropByTabPrefix: (tabId: string) => void
|
||||
/** Permanently retire one pane's hook authority while siblings stay live. */
|
||||
retirePaneAuthority: (paneKey: string) => void
|
||||
/** Move hook authority when a live pane is detached into another tab. */
|
||||
transferPaneAuthority: (args: { fromPaneKey: string; toPaneKey: string; ptyId?: string }) => void
|
||||
}
|
||||
|
||||
export type AgentTrustApi = {
|
||||
markTrusted: (args: {
|
||||
preset: 'cursor' | 'copilot' | 'codex'
|
||||
workspacePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
}
|
||||
|
||||
export type AgentAwakeApi = {
|
||||
getStatus: () => Promise<ComputerAwakeStatus>
|
||||
onChanged: (callback: (status: ComputerAwakeStatus) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { ClaudeUsageBreakdownKind, ClaudeUsageSnapshot } from '../../shared/claude-usage-types'
|
||||
import type { CodexUsageBreakdownKind, CodexUsageSnapshot } from '../../shared/codex-usage-types'
|
||||
import type {
|
||||
OpenCodeUsageBreakdownKind,
|
||||
OpenCodeUsageSnapshot
|
||||
} from '../../shared/opencode-usage-types'
|
||||
import type {
|
||||
CodexRateLimitResetResult,
|
||||
RateLimitRuntimeTarget,
|
||||
RateLimitState
|
||||
} from '../../shared/rate-limit-types'
|
||||
|
||||
export type UsageProviderSnapshot = {
|
||||
scanState: unknown
|
||||
summary: { scope: string; range: string }
|
||||
daily: unknown[]
|
||||
modelBreakdown: unknown[]
|
||||
recentSessions: unknown[]
|
||||
}
|
||||
|
||||
export type UsageQueryArgs<Snapshot extends UsageProviderSnapshot> = Pick<
|
||||
Snapshot['summary'],
|
||||
'scope' | 'range'
|
||||
>
|
||||
|
||||
export type UsageProviderApi<Snapshot extends UsageProviderSnapshot, BreakdownKind> = {
|
||||
getScanState: () => Promise<Snapshot['scanState']>
|
||||
setEnabled: (args: { enabled: boolean }) => Promise<Snapshot['scanState']>
|
||||
refresh: (args?: { force?: boolean }) => Promise<Snapshot['scanState']>
|
||||
getSnapshot: (args: UsageQueryArgs<Snapshot> & { limit?: number }) => Promise<Snapshot>
|
||||
getSummary: (args: UsageQueryArgs<Snapshot>) => Promise<Snapshot['summary']>
|
||||
getDaily: (args: UsageQueryArgs<Snapshot>) => Promise<Snapshot['daily']>
|
||||
getBreakdown: (
|
||||
args: UsageQueryArgs<Snapshot> & { kind: BreakdownKind }
|
||||
) => Promise<Snapshot['modelBreakdown']>
|
||||
getRecentSessions: (
|
||||
args: UsageQueryArgs<Snapshot> & { limit?: number }
|
||||
) => Promise<Snapshot['recentSessions']>
|
||||
}
|
||||
|
||||
export type ClaudeUsageApi = UsageProviderApi<ClaudeUsageSnapshot, ClaudeUsageBreakdownKind>
|
||||
|
||||
export type CodexUsageApi = UsageProviderApi<CodexUsageSnapshot, CodexUsageBreakdownKind>
|
||||
|
||||
export type OpenCodeUsageApi = UsageProviderApi<OpenCodeUsageSnapshot, OpenCodeUsageBreakdownKind>
|
||||
|
||||
export type RateLimitsApi = {
|
||||
get: () => Promise<RateLimitState>
|
||||
refresh: () => Promise<RateLimitState>
|
||||
refreshCodexForTarget: (target: RateLimitRuntimeTarget) => Promise<RateLimitState>
|
||||
consumeCodexResetCredit: () => Promise<CodexRateLimitResetResult>
|
||||
refreshClaudeForTarget: (target: RateLimitRuntimeTarget) => Promise<RateLimitState>
|
||||
setPollingInterval: (ms: number) => Promise<void>
|
||||
fetchInactiveClaudeAccounts: () => Promise<void>
|
||||
fetchInactiveCodexAccounts: () => Promise<void>
|
||||
refreshMiniMax: () => Promise<RateLimitState>
|
||||
refreshGrok: () => Promise<RateLimitState>
|
||||
onUpdate: (callback: (state: RateLimitState) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
AiVaultDeleteSessionArgs,
|
||||
AiVaultDeleteSessionResult
|
||||
} from '../../shared/ai-vault-session-deletion'
|
||||
import type {
|
||||
AiVaultFirstUserPromptArgs,
|
||||
AiVaultFirstUserPromptResult,
|
||||
AiVaultListArgs,
|
||||
AiVaultListResult,
|
||||
AiVaultSubagentListArgs,
|
||||
AiVaultSubagentListResult
|
||||
} from '../../shared/ai-vault-types'
|
||||
import type {
|
||||
AiVaultSessionTitlesArgs,
|
||||
AiVaultSessionTitlesResult
|
||||
} from '../../shared/ai-vault-session-title'
|
||||
import type {
|
||||
AiVaultPrepareSessionResumeArgs,
|
||||
AiVaultPrepareSessionResumeResult
|
||||
} from '../../shared/ai-vault-resume-preparation'
|
||||
|
||||
export type AiVaultApi = {
|
||||
listSessions: (args?: AiVaultListArgs) => Promise<AiVaultListResult>
|
||||
resolveSessionTitles: (args: AiVaultSessionTitlesArgs) => Promise<AiVaultSessionTitlesResult>
|
||||
cancelListSessions: (args: { requestToken: string }) => Promise<void>
|
||||
prepareSessionResume: (
|
||||
args: AiVaultPrepareSessionResumeArgs
|
||||
) => Promise<AiVaultPrepareSessionResumeResult>
|
||||
/** Lists the Task subagent transcripts of one session, on demand. */
|
||||
listSubagentSessions: (args: AiVaultSubagentListArgs) => Promise<AiVaultSubagentListResult>
|
||||
/** Full first user prompt for copy/reuse (re-parses one transcript). */
|
||||
getFirstUserPrompt: (args: AiVaultFirstUserPromptArgs) => Promise<AiVaultFirstUserPromptResult>
|
||||
/** Moves a deletable session's transcript to the OS trash; local sessions only. */
|
||||
deleteSession: (args: AiVaultDeleteSessionArgs) => Promise<AiVaultDeleteSessionResult>
|
||||
/** Fires when any app window regains OS focus; returns an unsubscribe. */
|
||||
onWindowFocused: (callback: () => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { AppIdentity } from '../../shared/app-identity'
|
||||
import type { E2EConfig } from '../../shared/e2e-config'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type {
|
||||
WriteTerminalRenderDesyncEvidenceArgs,
|
||||
WriteTerminalRenderDesyncEvidenceResult
|
||||
} from '../../shared/terminal-render-desync-evidence'
|
||||
import type {
|
||||
FloatingTerminalCwdRequest,
|
||||
MarkdownDocument,
|
||||
PersistedUIState,
|
||||
WorkspaceSessionState
|
||||
} from '../../shared/types'
|
||||
|
||||
export type AppApi = {
|
||||
/** Returns the app identity currently exposed to native chrome and the titlebar. */
|
||||
getIdentity: () => Promise<AppIdentity>
|
||||
/** Returns a URL base for feature-wall assets. In dev this is Vite /@fs;
|
||||
* in packaged builds this is file:// resources. Renderer appends filenames. */
|
||||
getFeatureWallAssetBaseUrl: () => Promise<string>
|
||||
/** Relaunches the app (app.relaunch() + app.exit(0)) for settings that need a full restart to apply. */
|
||||
relaunch: () => Promise<void>
|
||||
/** Restarts Orca through the normal quit pipeline so daemon-backed terminal
|
||||
* sessions survive and can reattach after the new process starts. */
|
||||
restart: () => Promise<void>
|
||||
/** Reloads the current app renderer through main so expected renderer
|
||||
* teardown can be classified before Electron emits process-gone events. */
|
||||
reload: () => Promise<void>
|
||||
/** Stages the renderer's final state synchronously before unload. */
|
||||
stageBeforeUnloadSync: (args: {
|
||||
sessions: { state: WorkspaceSessionState; hostId?: ExecutionHostId }[]
|
||||
ui: Partial<PersistedUIState>
|
||||
}) => void
|
||||
/** Resolves once the last staged checkpoint is durably written; rejects if that
|
||||
* write failed, so a reload/restart can abort instead of losing the snapshot. */
|
||||
awaitBeforeUnloadCheckpoint: () => Promise<void>
|
||||
/** Resolves when the daemon PTY provider and hook receiver have either
|
||||
* started or failed open for the first BrowserWindow. */
|
||||
awaitFirstWindowStartupServices: () => Promise<void>
|
||||
/** Reconciles legacy worker authority around persisted terminal reconnect. */
|
||||
recoverLegacyWorkerTerminalsForRendererStartup: () => Promise<void>
|
||||
/** Emits a startup benchmark marker when ORCA_STARTUP_DIAGNOSTICS is enabled. */
|
||||
startupDiagnostic: (event: string, details?: Record<string, unknown>) => Promise<void>
|
||||
/** macOS active input mode, or layout ID when no IME is selected (e.g. `com.apple.keylayout.PolishPro`).
|
||||
* Distinguishes CJK IMEs and Option-layer-composing layouts that look like US QWERTY (issue #1205).
|
||||
* Returns null on non-Darwin or when the defaults read fails. */
|
||||
getKeyboardInputSourceId: () => Promise<string | null>
|
||||
/** Updates the macOS Dock unread badge. No-op on Windows/Linux. */
|
||||
setUnreadDockBadgeCount: (count: number) => Promise<void>
|
||||
/** Resolves the launch directory for global Floating Terminal tabs. */
|
||||
getFloatingTerminalCwd: (args?: FloatingTerminalCwdRequest) => Promise<string>
|
||||
/** Resolves Orca's app-owned directory for auto-created Floating Workspace
|
||||
* markdown notes. */
|
||||
getFloatingMarkdownDirectory: () => Promise<string>
|
||||
/** Opens a native picker for markdown documents, rooted in the floating
|
||||
* workspace, and authorizes the selected file for editor reads/writes. */
|
||||
pickFloatingMarkdownDocument: () => Promise<MarkdownDocument | null>
|
||||
/** Opens a native directory picker and authorizes the selected directory
|
||||
* for Floating Workspace markdown file creation. */
|
||||
pickFloatingWorkspaceDirectory: () => Promise<string | null>
|
||||
/** Persists flag-gated terminal render evidence under app-owned userData. */
|
||||
writeTerminalRenderDesyncEvidence: (
|
||||
args: WriteTerminalRenderDesyncEvidenceArgs
|
||||
) => Promise<WriteTerminalRenderDesyncEvidenceResult>
|
||||
}
|
||||
|
||||
/** Panel contribution as surfaced by the main-process plugin service. */
|
||||
|
||||
export type PlatformApi = {
|
||||
get: () => {
|
||||
platform: NodeJS.Platform
|
||||
osRelease: string
|
||||
arch: string
|
||||
/** Login shell or ComSpec when available. */
|
||||
shell: string
|
||||
displayServer: 'wayland' | 'x11' | null
|
||||
}
|
||||
}
|
||||
|
||||
export type E2EApi = {
|
||||
getConfig: () => E2EConfig
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import type {
|
||||
Automation,
|
||||
AutomationCreateInput,
|
||||
AutomationDispatchRequest,
|
||||
AutomationDispatchResult,
|
||||
ExternalAutomationCreateInput,
|
||||
ExternalAutomationActionInput,
|
||||
ExternalAutomationManager,
|
||||
ExternalAutomationRunsInput,
|
||||
ExternalAutomationRunsPage,
|
||||
ExternalAutomationUpdateInput,
|
||||
AutomationRun,
|
||||
AutomationPrecheckResult,
|
||||
AutomationUpdateInput
|
||||
} from '../../shared/automations-types'
|
||||
|
||||
export type AutomationsApi = {
|
||||
list: () => Promise<Automation[]>
|
||||
listRuns: (args?: { automationId?: string }) => Promise<AutomationRun[]>
|
||||
listExternalManagers: () => Promise<ExternalAutomationManager[]>
|
||||
listExternalRuns: (input: ExternalAutomationRunsInput) => Promise<ExternalAutomationRunsPage>
|
||||
createExternal: (input: ExternalAutomationCreateInput) => Promise<void>
|
||||
updateExternal: (input: ExternalAutomationUpdateInput) => Promise<void>
|
||||
runExternalAction: (input: ExternalAutomationActionInput) => Promise<void>
|
||||
create: (input: AutomationCreateInput) => Promise<Automation>
|
||||
update: (args: { id: string; updates: AutomationUpdateInput }) => Promise<Automation>
|
||||
delete: (args: { id: string }) => Promise<void>
|
||||
runNow: (args: { id: string }) => Promise<AutomationRun>
|
||||
runPrecheck: (args: {
|
||||
automationId: string
|
||||
runId: string
|
||||
}) => Promise<AutomationPrecheckResult | null>
|
||||
markDispatchResult: (result: AutomationDispatchResult) => Promise<AutomationRun>
|
||||
snapshotWorkspaceName: (args: { workspaceId: string; displayName: string }) => Promise<number>
|
||||
rendererReady: () => Promise<void>
|
||||
onDispatchRequested: (callback: (request: AutomationDispatchRequest) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import type { BrowserSetAnnotationViewportBridgeArgs } from '../../shared/browser-annotation-viewport-bridge'
|
||||
import type {
|
||||
BrowserSetGrabModeArgs,
|
||||
BrowserSetGrabModeResult,
|
||||
BrowserAwaitGrabSelectionArgs,
|
||||
BrowserGrabResult,
|
||||
BrowserCancelGrabArgs,
|
||||
BrowserCaptureSelectionScreenshotArgs,
|
||||
BrowserCaptureSelectionScreenshotResult,
|
||||
BrowserExtractHoverArgs,
|
||||
BrowserExtractHoverResult
|
||||
} from '../../shared/browser-grab-types'
|
||||
import type {
|
||||
BrowserContextMenuDismissedEvent,
|
||||
BrowserContextMenuRequestedEvent,
|
||||
BrowserDownloadFinishedEvent,
|
||||
BrowserDownloadProgressEvent,
|
||||
BrowserDownloadRequestedEvent,
|
||||
BrowserPermissionDeniedEvent,
|
||||
BrowserPopupEvent
|
||||
} from '../../shared/browser-guest-events'
|
||||
import type {
|
||||
BrowserCookieImportResult,
|
||||
BrowserCertificateFailure,
|
||||
BrowserCertificateProceedResult,
|
||||
BrowserLoadError,
|
||||
BrowserSessionProfile,
|
||||
BrowserSessionProfileCreateOptions,
|
||||
BrowserSessionProfileScope,
|
||||
BrowserSessionProfileSource,
|
||||
BrowserViewportOverride
|
||||
} from '../../shared/types'
|
||||
|
||||
export type BrowserApi = {
|
||||
registerGuest: (args: {
|
||||
browserPageId: string
|
||||
workspaceId: string
|
||||
worktreeId: string
|
||||
sessionProfileId?: string | null
|
||||
webContentsId: number
|
||||
}) => Promise<boolean>
|
||||
isGuestRegistered: (args: { browserPageId: string; webContentsId: number }) => Promise<boolean>
|
||||
repairGuestRegistration: (args: {
|
||||
browserPageId: string
|
||||
workspaceId: string
|
||||
worktreeId: string
|
||||
sessionProfileId?: string | null
|
||||
webContentsId: number
|
||||
}) => Promise<boolean>
|
||||
unregisterGuest: (args: { browserPageId: string }) => Promise<void>
|
||||
openDevTools: (args: { browserPageId: string }) => Promise<boolean>
|
||||
setViewportOverride: (args: {
|
||||
browserPageId: string
|
||||
override: BrowserViewportOverride | null
|
||||
}) => Promise<boolean>
|
||||
setAnnotationViewportBridge: (args: BrowserSetAnnotationViewportBridgeArgs) => Promise<boolean>
|
||||
onGuestLoadFailed: (
|
||||
callback: (args: { browserPageId: string; loadError: BrowserLoadError }) => void
|
||||
) => () => void
|
||||
onCertificateFailureChanged: (
|
||||
callback: (event: { browserPageId: string; failure: BrowserCertificateFailure | null }) => void
|
||||
) => () => void
|
||||
proceedCertificate: (args: {
|
||||
browserPageId: string
|
||||
challengeId: string
|
||||
}) => Promise<BrowserCertificateProceedResult>
|
||||
onPermissionDenied: (callback: (event: BrowserPermissionDeniedEvent) => void) => () => void
|
||||
onPopup: (callback: (event: BrowserPopupEvent) => void) => () => void
|
||||
onDownloadRequested: (callback: (event: BrowserDownloadRequestedEvent) => void) => () => void
|
||||
onDownloadProgress: (callback: (event: BrowserDownloadProgressEvent) => void) => () => void
|
||||
onDownloadFinished: (callback: (event: BrowserDownloadFinishedEvent) => void) => () => void
|
||||
onContextMenuRequested: (
|
||||
callback: (event: BrowserContextMenuRequestedEvent) => void
|
||||
) => () => void
|
||||
onContextMenuDismissed: (
|
||||
callback: (event: BrowserContextMenuDismissedEvent) => void
|
||||
) => () => void
|
||||
onNavigationUpdate: (
|
||||
callback: (event: { browserPageId: string; url: string; title: string }) => void
|
||||
) => () => void
|
||||
onActivateView: (
|
||||
callback: (data: { worktreeId?: string; browserPageId?: string }) => void
|
||||
) => () => void
|
||||
onPaneFocus: (
|
||||
callback: (data: { worktreeId: string | null; browserPageId: string }) => void
|
||||
) => () => void
|
||||
onOpenLinkInOrcaTab: (
|
||||
callback: (event: { browserPageId: string; url: string }) => void
|
||||
) => () => void
|
||||
cancelDownload: (args: { downloadId: string }) => Promise<boolean>
|
||||
setGrabMode: (args: BrowserSetGrabModeArgs) => Promise<BrowserSetGrabModeResult>
|
||||
awaitGrabSelection: (args: BrowserAwaitGrabSelectionArgs) => Promise<BrowserGrabResult>
|
||||
cancelGrab: (args: BrowserCancelGrabArgs) => Promise<boolean>
|
||||
captureSelectionScreenshot: (
|
||||
args: BrowserCaptureSelectionScreenshotArgs
|
||||
) => Promise<BrowserCaptureSelectionScreenshotResult>
|
||||
extractHoverPayload: (args: BrowserExtractHoverArgs) => Promise<BrowserExtractHoverResult>
|
||||
onGrabModeToggle: (callback: (browserPageId: string) => void) => () => void
|
||||
onGrabActionShortcut: (
|
||||
callback: (args: { browserPageId: string; key: 'c' | 's' }) => void
|
||||
) => () => void
|
||||
sessionListProfiles: () => Promise<BrowserSessionProfile[]>
|
||||
sessionCreateProfile: (
|
||||
args: {
|
||||
scope: BrowserSessionProfileScope
|
||||
label: string
|
||||
} & BrowserSessionProfileCreateOptions
|
||||
) => Promise<BrowserSessionProfile | null>
|
||||
sessionDeleteProfile: (args: { profileId: string }) => Promise<boolean>
|
||||
sessionImportCookies: (args: { profileId: string }) => Promise<BrowserCookieImportResult>
|
||||
sessionResolvePartition: (args: { profileId: string | null }) => Promise<string | null>
|
||||
sessionDetectBrowsers: () => Promise<DetectedBrowserInfo[]>
|
||||
sessionImportFromBrowser: (args: {
|
||||
profileId: string
|
||||
browserFamily: string
|
||||
browserProfile?: string
|
||||
}) => Promise<BrowserCookieImportResult>
|
||||
sessionClearDefaultCookies: () => Promise<boolean>
|
||||
notifyActiveTabChanged: (args: { browserPageId: string }) => Promise<boolean>
|
||||
}
|
||||
|
||||
export type DetectedBrowserProfileInfo = {
|
||||
name: string
|
||||
directory: string
|
||||
}
|
||||
|
||||
export type DetectedBrowserInfo = {
|
||||
family: BrowserSessionProfileSource['browserFamily']
|
||||
label: string
|
||||
profiles: DetectedBrowserProfileInfo[]
|
||||
selectedProfile: string
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { CliInstallStatus } from '../../shared/cli-install-types'
|
||||
|
||||
export type CliApi = {
|
||||
getInstallStatus: () => Promise<CliInstallStatus>
|
||||
install: () => Promise<CliInstallStatus>
|
||||
remove: () => Promise<CliInstallStatus>
|
||||
getWslInstallStatus: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
|
||||
installWsl: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
|
||||
removeWsl: (args?: { distro?: string | null }) => Promise<CliInstallStatus>
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type {
|
||||
CrashReportBreadcrumbData,
|
||||
CrashReportCopyDiagnosticsArgs,
|
||||
CrashReportRecord,
|
||||
CrashReportSubmitArgs,
|
||||
CrashReportSubmitResult,
|
||||
ReactErrorBoundaryReportArgs,
|
||||
ReactErrorBoundaryReportResult
|
||||
} from '../../shared/crash-reporting'
|
||||
import type { RendererHeapStatistics } from '../../shared/renderer-heap-statistics'
|
||||
|
||||
export type CrashReportsApi = {
|
||||
getLatestPending: () => Promise<CrashReportRecord | null>
|
||||
getLatestReport: () => Promise<CrashReportRecord | null>
|
||||
dismiss: (args: { reportId: string }) => Promise<CrashReportRecord | null>
|
||||
recordRendererError: (
|
||||
args: ReactErrorBoundaryReportArgs
|
||||
) => Promise<ReactErrorBoundaryReportResult>
|
||||
recordBreadcrumb: (args: { name: string; data?: CrashReportBreadcrumbData }) => void
|
||||
submit: (args: CrashReportSubmitArgs) => Promise<CrashReportSubmitResult>
|
||||
copyLatestDiagnostics: (
|
||||
args?: CrashReportCopyDiagnosticsArgs
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
/** Exact V8/Blink heap sizes; null when the runtime withholds them. */
|
||||
readHeapStatistics: () => RendererHeapStatistics | null
|
||||
}
|
||||
|
||||
export type FeedbackApi = {
|
||||
submit: (args: {
|
||||
feedback: string
|
||||
submitAnonymously?: boolean
|
||||
githubLogin: string | null
|
||||
githubEmail: string | null
|
||||
images?: { contentType: string; data: Uint8Array }[]
|
||||
}) => Promise<
|
||||
{ ok: true; imagesDelivered?: boolean } | { ok: false; status: number | null; error: string }
|
||||
>
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type {
|
||||
DashboardRevealAgentArgs,
|
||||
DashboardSleepWorkspaceArgs,
|
||||
DashboardSnapshot,
|
||||
DashboardSpawnAgentArgs
|
||||
} from '../../shared/dashboard-snapshot'
|
||||
import type {
|
||||
TerminalPreviewConnectResult,
|
||||
TerminalPreviewDataPayload
|
||||
} from '../../shared/terminal-preview'
|
||||
|
||||
export type DashboardApi = {
|
||||
openPopout: (view?: 'board' | 'map') => Promise<void>
|
||||
publishSnapshot: (snapshot: DashboardSnapshot) => Promise<void>
|
||||
getPopoutOpen: () => Promise<boolean>
|
||||
onPopoutOpenChanged: (callback: (open: boolean) => void) => () => void
|
||||
onSnapshotRequested: (callback: () => void) => () => void
|
||||
onRevealAgent: (callback: (args: DashboardRevealAgentArgs) => void) => () => void
|
||||
onAckAgent: (callback: (paneKey: string) => void) => () => void
|
||||
onSpawnAgent: (callback: (args: DashboardSpawnAgentArgs) => void) => () => void
|
||||
onSleepWorkspace: (callback: (args: DashboardSleepWorkspaceArgs) => void) => () => void
|
||||
requestSnapshot: () => Promise<void>
|
||||
onSnapshot: (callback: (snapshot: DashboardSnapshot) => void) => () => void
|
||||
onViewRequested: (callback: (view: 'board' | 'map') => void) => () => void
|
||||
revealAgent: (args: DashboardRevealAgentArgs) => Promise<void>
|
||||
ackAgent: (paneKey: string) => Promise<void>
|
||||
spawnAgent: (args: DashboardSpawnAgentArgs) => Promise<void>
|
||||
sleepWorkspace: (args: DashboardSleepWorkspaceArgs) => Promise<void>
|
||||
}
|
||||
|
||||
export type TerminalPreviewApi = {
|
||||
connect: (
|
||||
ptyId: string,
|
||||
opts?: { scrollbackRows?: number }
|
||||
) => Promise<TerminalPreviewConnectResult>
|
||||
input: (ptyId: string, data: string) => Promise<boolean>
|
||||
/** Claim the PTY grid for the preview dialog; resolves to the size actually in effect. */
|
||||
fit: (ptyId: string, cols: number, rows: number) => Promise<{ cols: number; rows: number } | null>
|
||||
ack: (ptyId: string, bytes: number) => Promise<void>
|
||||
unsubscribe: (ptyId: string) => Promise<void>
|
||||
onData: (callback: (payload: TerminalPreviewDataPayload) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export type EmulatorApi = {
|
||||
onPaneFocus: (callback: (data: { worktreeId: string }) => void) => () => void
|
||||
onAutoAttach: (
|
||||
callback: (data: {
|
||||
worktreeId: string
|
||||
info: { deviceUdid: string; streamUrl: string; wsUrl: string; axUrl?: string }
|
||||
}) => void
|
||||
) => () => void
|
||||
startFrameStream: (args: { streamUrl: string; streamKey?: string }) => Promise<{
|
||||
streamId: string
|
||||
}>
|
||||
stopFrameStream: (args: { streamId: string }) => Promise<void>
|
||||
onFrameStreamFrame: (
|
||||
callback: (data: { streamId: string; bytes: ArrayBuffer }) => void
|
||||
) => () => void
|
||||
onFrameStreamError: (
|
||||
callback: (data: { streamId: string; message: string }) => void
|
||||
) => () => void
|
||||
startVideoStream: (args: { deviceId: string; streamId: string }) => Promise<{ streamId: string }>
|
||||
stopVideoStream: (args: { streamId: string }) => Promise<void>
|
||||
onVideoStreamMeta: (
|
||||
callback: (data: {
|
||||
streamId: string
|
||||
deviceId: string
|
||||
meta: { codecId: string; width: number; height: number }
|
||||
}) => void
|
||||
) => () => void
|
||||
onVideoStreamFrame: (
|
||||
callback: (data: {
|
||||
streamId: string
|
||||
deviceId: string
|
||||
config: boolean
|
||||
keyFrame: boolean
|
||||
bytes: ArrayBuffer
|
||||
}) => void
|
||||
) => () => void
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { OrcaHooks } from '../../shared/types'
|
||||
import type { PublicKnownRuntimeEnvironment } from '../../shared/runtime-environments'
|
||||
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'
|
||||
|
||||
export type EphemeralVmApi = {
|
||||
listRecipes: (args: { repoId: string }) => Promise<{
|
||||
status: 'ok' | 'error'
|
||||
repoPath: string | null
|
||||
recipes: OrcaHooks['environmentRecipes']
|
||||
diagnostics: NonNullable<OrcaHooks['environmentRecipeDiagnostics']>
|
||||
message?: string
|
||||
}>
|
||||
listRecipeCatalog: () => Promise<
|
||||
{
|
||||
repoId: string
|
||||
repoName: string
|
||||
repoPath: string
|
||||
recipes: NonNullable<OrcaHooks['environmentRecipes']>
|
||||
diagnostics: NonNullable<OrcaHooks['environmentRecipeDiagnostics']>
|
||||
}[]
|
||||
>
|
||||
doctor: (args: { repoId: string; recipeId: string }) => Promise<EphemeralVmRecipeDoctorResult>
|
||||
provision: (args: {
|
||||
repoId: string
|
||||
recipeId: string
|
||||
workspaceName?: string
|
||||
projectId?: string
|
||||
workspaceId?: string
|
||||
branch?: string
|
||||
ref?: string
|
||||
provisionId?: string
|
||||
}) => Promise<
|
||||
| {
|
||||
ok: true
|
||||
connectionType: 'orca-server'
|
||||
runtime: EphemeralVmRuntimeRecord
|
||||
environment: PublicKnownRuntimeEnvironment
|
||||
stderr: string
|
||||
warnings: EphemeralVmRecipeResultWarning[]
|
||||
}
|
||||
| {
|
||||
ok: true
|
||||
connectionType: 'ssh'
|
||||
runtime: EphemeralVmRuntimeRecord
|
||||
sshTargetId: string
|
||||
stderr: string
|
||||
warnings: EphemeralVmRecipeResultWarning[]
|
||||
}
|
||||
| { ok: false; error: string; stderr: string; stdout: string }
|
||||
>
|
||||
cancelProvision: (args: { provisionId: string }) => Promise<{ cancelled: boolean }>
|
||||
onProvisionEvent: (
|
||||
callback: (event: { provisionId: string; stream: 'stdout' | 'stderr'; chunk: string }) => void
|
||||
) => () => void
|
||||
listRuntimes: () => Promise<EphemeralVmRuntimeRecord[]>
|
||||
attachWorkspace: (args: {
|
||||
runtimeId: string
|
||||
workspaceId: string
|
||||
}) => Promise<EphemeralVmRuntimeRecord>
|
||||
suspendWorkspace: (args: { workspaceId: string }) => Promise<EphemeralVmRuntimeRecord | null>
|
||||
resumeWorkspace: (args: { workspaceId: string }) => Promise<EphemeralVmRuntimeRecord | null>
|
||||
cleanup: (args: { runtimeId: string }) => Promise<EphemeralVmRuntimeRecord>
|
||||
getCleanupCommand: (args: { runtimeId: string }) => Promise<{
|
||||
runtimeId: string
|
||||
command: string | null
|
||||
payloadJson: string
|
||||
cleanupDisabled: boolean
|
||||
message?: string
|
||||
}>
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import type {
|
||||
DirEntry,
|
||||
FsChangedPayload,
|
||||
MarkdownDocument,
|
||||
SearchOptions,
|
||||
SearchResult
|
||||
} from '../../shared/types'
|
||||
import type {
|
||||
LocalLogTailChangedPayload,
|
||||
LocalLogTailReadArgs,
|
||||
LocalLogTailReadResult,
|
||||
LocalLogTailWatchArgs
|
||||
} from '../../shared/local-log-tail-types'
|
||||
import type { SshMutationExpectation } from '../../shared/ssh-types'
|
||||
|
||||
export type ExportApi = {
|
||||
htmlToPdf: (args: {
|
||||
html: string
|
||||
title: string
|
||||
}) => Promise<
|
||||
{ success: true; filePath: string } | { success: false; cancelled?: boolean; error?: string }
|
||||
>
|
||||
}
|
||||
|
||||
export type FilesystemApi = {
|
||||
fs: {
|
||||
readDir: (args: { dirPath: string; connectionId?: string }) => Promise<DirEntry[]>
|
||||
readFile: (args: {
|
||||
filePath: string
|
||||
connectionId?: string
|
||||
includeLocalLogMetadata?: boolean
|
||||
}) => Promise<{
|
||||
content: string
|
||||
isBinary: boolean
|
||||
isImage?: boolean
|
||||
mimeType?: string
|
||||
fileIdentity?: string
|
||||
}>
|
||||
readLocalLogTail: (args: LocalLogTailReadArgs) => Promise<LocalLogTailReadResult>
|
||||
startLocalLogTail: (args: LocalLogTailWatchArgs) => Promise<void>
|
||||
stopLocalLogTail: (args: { subscriptionId: string }) => Promise<void>
|
||||
onLocalLogTailChanged: (callback: (payload: LocalLogTailChangedPayload) => void) => () => void
|
||||
downloadFile: (args: {
|
||||
filePath: string
|
||||
connectionId: string
|
||||
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
|
||||
downloadFolder: (args: {
|
||||
dirPath: string
|
||||
connectionId: string
|
||||
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
|
||||
saveDownloadedFile: (args: {
|
||||
suggestedName: string
|
||||
content: string
|
||||
encoding: 'utf8' | 'base64'
|
||||
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
|
||||
startDownloadedFile: (args: {
|
||||
suggestedName: string
|
||||
}) => Promise<
|
||||
{ canceled: true } | { canceled: false; transferId: string; destinationPath: string }
|
||||
>
|
||||
appendDownloadedFileChunk: (args: {
|
||||
transferId: string
|
||||
contentBase64: string
|
||||
}) => Promise<{ ok: true }>
|
||||
finishDownloadedFile: (args: {
|
||||
transferId: string
|
||||
}) => Promise<{ canceled: false; destinationPath: string }>
|
||||
cancelDownloadedFile: (args: { transferId: string }) => Promise<{ ok: true }>
|
||||
listMarkdownDocuments: (args: {
|
||||
rootPath: string
|
||||
connectionId?: string
|
||||
}) => Promise<MarkdownDocument[]>
|
||||
writeFile: (
|
||||
args: {
|
||||
filePath: string
|
||||
content: string
|
||||
connectionId?: string
|
||||
} & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
createFile: (
|
||||
args: {
|
||||
filePath: string
|
||||
connectionId?: string
|
||||
} & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
createDir: (
|
||||
args: { dirPath: string; connectionId?: string } & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
rename: (
|
||||
args: {
|
||||
oldPath: string
|
||||
newPath: string
|
||||
connectionId?: string
|
||||
} & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
copy: (
|
||||
args: {
|
||||
sourcePath: string
|
||||
destinationPath: string
|
||||
connectionId?: string
|
||||
} & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
deletePath: (
|
||||
args: {
|
||||
targetPath: string
|
||||
connectionId?: string
|
||||
recursive?: boolean
|
||||
} & SshMutationExpectation
|
||||
) => Promise<void>
|
||||
authorizeExternalPath: (args: { targetPath: string }) => Promise<void>
|
||||
stat: (args: {
|
||||
filePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<{ size: number; isDirectory: boolean; mtime: number }>
|
||||
pathExists: (args: { filePath: string; connectionId?: string }) => Promise<boolean>
|
||||
listFiles: (args: {
|
||||
rootPath: string
|
||||
connectionId?: string
|
||||
excludePaths?: string[]
|
||||
requestToken?: string
|
||||
}) => Promise<string[]>
|
||||
cancelListFiles: (args: { requestToken: string }) => Promise<void>
|
||||
search: (args: SearchOptions & { connectionId?: string }) => Promise<SearchResult>
|
||||
importExternalPaths: (
|
||||
args: {
|
||||
sourcePaths: string[]
|
||||
destDir: string
|
||||
connectionId?: string
|
||||
ensureDir?: boolean
|
||||
} & SshMutationExpectation
|
||||
) => Promise<{
|
||||
results: (
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'imported'
|
||||
destPath: string
|
||||
kind: 'file' | 'directory'
|
||||
renamed: boolean
|
||||
}
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'skipped'
|
||||
reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported'
|
||||
}
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'failed'
|
||||
reason: string
|
||||
}
|
||||
)[]
|
||||
}>
|
||||
stageExternalPathsForRuntimeUpload: (args: { sourcePaths: string[] }) => Promise<{
|
||||
sources: (
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'staged'
|
||||
name: string
|
||||
kind: 'file' | 'directory'
|
||||
entries: (
|
||||
| { relativePath: string; kind: 'directory' }
|
||||
| { relativePath: string; kind: 'file'; contentBase64: string }
|
||||
)[]
|
||||
}
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'skipped'
|
||||
reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported'
|
||||
}
|
||||
| {
|
||||
sourcePath: string
|
||||
status: 'failed'
|
||||
reason: string
|
||||
}
|
||||
)[]
|
||||
}>
|
||||
resolveDroppedPathsForAgent: (
|
||||
args: {
|
||||
paths: string[]
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
} & SshMutationExpectation
|
||||
) => Promise<{
|
||||
resolvedPaths: string[]
|
||||
skipped: {
|
||||
sourcePath: string
|
||||
reason: 'missing' | 'symlink' | 'permission-denied' | 'unsupported'
|
||||
}[]
|
||||
failed: { sourcePath: string; reason: string }[]
|
||||
}>
|
||||
watchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
|
||||
unwatchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
|
||||
onFsChanged: (callback: (payload: FsChangedPayload) => void) => () => void
|
||||
}
|
||||
notebook: {
|
||||
runPythonCell: (args: {
|
||||
filePath: string
|
||||
code: string
|
||||
preamble?: string
|
||||
connectionId?: string | null
|
||||
}) => Promise<{
|
||||
stdout: string
|
||||
stderr: string
|
||||
exitCode: number | null
|
||||
error?: string
|
||||
}>
|
||||
}
|
||||
export: ExportApi
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import type {
|
||||
GitBranchCompareResult,
|
||||
GitCommitCompareResult,
|
||||
GitConflictOperation,
|
||||
GitDiffResult,
|
||||
GitPushTarget,
|
||||
GitStagingArea,
|
||||
GitStatusResult,
|
||||
GitUpstreamStatus
|
||||
} from '../../shared/types'
|
||||
import type { GitHistoryOptions, GitHistoryResult } from '../../shared/git-history'
|
||||
import type {
|
||||
CommitMessageAgentCapability,
|
||||
CommitMessageModelCapability
|
||||
} from '../../shared/commit-message-agent-spec'
|
||||
|
||||
export type GitInspectionApi = {
|
||||
status: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
includeIgnored?: boolean
|
||||
bypassEffectiveUpstreamNegativeCache?: boolean
|
||||
reuseLineStats?: boolean
|
||||
/** Merge-base OID to measure the branch line total against; omit to skip the work. */
|
||||
branchLineTotalMergeBase?: string
|
||||
requestToken?: string
|
||||
}) => Promise<GitStatusResult>
|
||||
cancelStatus: (args: { requestToken: string }) => Promise<void>
|
||||
setStatusUpstreamRefWatch: (args: {
|
||||
worktreeId: string
|
||||
worktreePath: string
|
||||
executionHostId: string
|
||||
connectionId?: string
|
||||
branch?: string
|
||||
upstreamName?: string
|
||||
}) => Promise<void>
|
||||
submoduleStatus: (args: {
|
||||
worktreePath: string
|
||||
submodulePath: string
|
||||
connectionId?: string
|
||||
area?: GitStagingArea
|
||||
}) => Promise<GitStatusResult>
|
||||
checkIgnored: (args: {
|
||||
worktreePath: string
|
||||
paths: string[]
|
||||
connectionId?: string
|
||||
}) => Promise<string[]>
|
||||
findHugeFoldersToIgnore: (args: { worktreePath: string }) => Promise<string[]>
|
||||
history: (
|
||||
args: { worktreePath: string; connectionId?: string } & GitHistoryOptions
|
||||
) => Promise<GitHistoryResult>
|
||||
conflictOperation: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<GitConflictOperation>
|
||||
diff: (args: {
|
||||
worktreePath: string
|
||||
filePath: string
|
||||
staged: boolean
|
||||
compareAgainstHead?: boolean
|
||||
connectionId?: string
|
||||
}) => Promise<GitDiffResult>
|
||||
branchCompare: (args: {
|
||||
worktreePath: string
|
||||
baseRef: string
|
||||
connectionId?: string
|
||||
}) => Promise<GitBranchCompareResult>
|
||||
commitCompare: (args: {
|
||||
worktreePath: string
|
||||
commitId: string
|
||||
connectionId?: string
|
||||
}) => Promise<GitCommitCompareResult>
|
||||
upstreamStatus: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<GitUpstreamStatus>
|
||||
branchDiff: (args: {
|
||||
worktreePath: string
|
||||
compare: {
|
||||
baseRef: string
|
||||
baseOid: string
|
||||
headOid: string
|
||||
mergeBase: string
|
||||
}
|
||||
filePath: string
|
||||
oldPath?: string
|
||||
connectionId?: string
|
||||
}) => Promise<GitDiffResult>
|
||||
commitDiff: (args: {
|
||||
worktreePath: string
|
||||
commitOid: string
|
||||
parentOid?: string | null
|
||||
filePath: string
|
||||
oldPath?: string
|
||||
connectionId?: string
|
||||
}) => Promise<GitDiffResult>
|
||||
discoverCommitMessageModels: (args: {
|
||||
agentId: string
|
||||
worktreePath?: string
|
||||
connectionId?: string
|
||||
}) => Promise<
|
||||
| {
|
||||
success: true
|
||||
capability: CommitMessageAgentCapability
|
||||
models: CommitMessageModelCapability[]
|
||||
defaultModelId: string
|
||||
catalogOrigin: 'probe' | 'spec'
|
||||
}
|
||||
| { success: false; error: string }
|
||||
>
|
||||
remoteFileUrl: (args: {
|
||||
worktreePath: string
|
||||
relativePath: string
|
||||
line: number
|
||||
connectionId?: string
|
||||
}) => Promise<string | null>
|
||||
remoteCommitUrl: (args: {
|
||||
worktreePath: string
|
||||
sha: string
|
||||
connectionId?: string
|
||||
}) => Promise<string | null>
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import type {
|
||||
GitForkSyncExpectedUpstream,
|
||||
GitForkSyncResult,
|
||||
GitPushTarget,
|
||||
TuiAgent
|
||||
} from '../../shared/types'
|
||||
import type { HostedReviewProvider } from '../../shared/hosted-review'
|
||||
import type { ResolvedSourceControlAiGenerationParams } from '../../shared/source-control-ai'
|
||||
import type { SourceControlAiSettings } from '../../shared/source-control-ai-types'
|
||||
|
||||
export type GitOperationApi = {
|
||||
appendGitignore: (args: { worktreePath: string; folderName: string }) => Promise<boolean>
|
||||
abortMerge: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
|
||||
abortRebase: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
|
||||
fetch: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<void>
|
||||
syncFork: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
expectedUpstream: GitForkSyncExpectedUpstream
|
||||
}) => Promise<GitForkSyncResult>
|
||||
push: (args: {
|
||||
worktreePath: string
|
||||
publish?: boolean
|
||||
forceWithLease?: boolean
|
||||
connectionId?: string
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<void>
|
||||
pull: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<void>
|
||||
fastForward: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
pushTarget?: GitPushTarget
|
||||
}) => Promise<void>
|
||||
rebaseFromBase: (args: {
|
||||
worktreePath: string
|
||||
baseRef: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
commit: (args: {
|
||||
worktreePath: string
|
||||
message: string
|
||||
connectionId?: string
|
||||
}) => Promise<{ success: boolean; error?: string }>
|
||||
generateCommitMessage: (args: {
|
||||
worktreePath: string
|
||||
/** Raw (unstripped) worktree meta key; validated against worktreePath in main. */
|
||||
worktreeId?: string
|
||||
repoId?: string
|
||||
connectionId?: string
|
||||
sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams
|
||||
sourceControlAi?: SourceControlAiSettings
|
||||
agentCmdOverrides?: Partial<Record<TuiAgent, string>>
|
||||
}) => Promise<
|
||||
| { success: true; message: string; agentLabel?: string }
|
||||
| { success: false; error: string; canceled?: boolean }
|
||||
>
|
||||
cancelGenerateCommitMessage: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
generatePullRequestFields: (args: {
|
||||
worktreePath: string
|
||||
/** Raw (unstripped) worktree meta key; validated against worktreePath in main. */
|
||||
worktreeId?: string
|
||||
repoId?: string
|
||||
base: string
|
||||
title: string
|
||||
body: string
|
||||
draft: boolean
|
||||
provider?: HostedReviewProvider
|
||||
useTemplate?: boolean
|
||||
connectionId?: string
|
||||
sourceControlAiResolvedParams?: ResolvedSourceControlAiGenerationParams
|
||||
sourceControlAi?: SourceControlAiSettings
|
||||
agentCmdOverrides?: Partial<Record<TuiAgent, string>>
|
||||
}) => Promise<
|
||||
| {
|
||||
success: true
|
||||
fields: { base: string; title: string; body: string; draft: boolean }
|
||||
agentLabel?: string
|
||||
branchChangedByPreparation?: boolean
|
||||
}
|
||||
| {
|
||||
success: false
|
||||
error: string
|
||||
canceled?: boolean
|
||||
branchChangedByPreparation?: boolean
|
||||
}
|
||||
>
|
||||
cancelGeneratePullRequestFields: (args: {
|
||||
worktreePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
stage: (args: { worktreePath: string; filePath: string; connectionId?: string }) => Promise<void>
|
||||
bulkStage: (args: {
|
||||
worktreePath: string
|
||||
filePaths: string[]
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
unstage: (args: {
|
||||
worktreePath: string
|
||||
filePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
bulkUnstage: (args: {
|
||||
worktreePath: string
|
||||
filePaths: string[]
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
discard: (args: {
|
||||
worktreePath: string
|
||||
filePath: string
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
bulkDiscard: (args: {
|
||||
worktreePath: string
|
||||
filePaths: string[]
|
||||
connectionId?: string
|
||||
}) => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import type { AppStarSource } from '../../shared/gh-star-source'
|
||||
import type { GhAuthDiagnostic } from '../../shared/github/auth-types'
|
||||
import type { TaskSourceContext } from '../../shared/task-source-context'
|
||||
import type {
|
||||
GetRateLimitResult,
|
||||
GitHubCommentResult,
|
||||
GitHubOwnerRepo,
|
||||
GitHubPRFile,
|
||||
GitHubPRFileContents,
|
||||
GitHubPRRefreshCandidate,
|
||||
GitHubPRRefreshEnqueueResult,
|
||||
GitHubPRRefreshEvent,
|
||||
GitHubPRRefreshReason,
|
||||
GitHubPRReviewCommentInput,
|
||||
GitHubReactionContent,
|
||||
GitHubViewer,
|
||||
PRCheckDetail,
|
||||
PRCheckRunDetails,
|
||||
PRComment,
|
||||
PRInfo,
|
||||
PRRefreshOutcome
|
||||
} from '../../shared/types'
|
||||
import type { GitHubRepoSelectorArgs } from './github-work-item-api'
|
||||
|
||||
export type GithubPullRequestApi = {
|
||||
viewer: () => Promise<GitHubViewer | null>
|
||||
repoSlug: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
}) => Promise<{ owner: string; repo: string; host?: string } | null>
|
||||
repoUpstream: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
}) => Promise<{ owner: string; repo: string; host?: string } | null>
|
||||
prForBranch: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
branch: string
|
||||
linkedPRNumber?: number | null
|
||||
fallbackPRNumber?: number | null
|
||||
acceptMergedFallbackPR?: boolean
|
||||
currentHeadOid?: string | null
|
||||
}) => Promise<PRInfo | null>
|
||||
refreshPRNow: (args: { candidate: GitHubPRRefreshCandidate }) => Promise<PRRefreshOutcome>
|
||||
enqueuePRRefresh: (args: {
|
||||
candidate: GitHubPRRefreshCandidate
|
||||
reason: GitHubPRRefreshReason
|
||||
priority?: number
|
||||
}) => Promise<GitHubPRRefreshEnqueueResult | false>
|
||||
reportVisiblePRRefreshCandidates: (args: {
|
||||
candidates: GitHubPRRefreshCandidate[]
|
||||
generation: number
|
||||
}) => Promise<boolean>
|
||||
onPRRefreshEvent: (callback: (event: GitHubPRRefreshEvent) => void) => () => void
|
||||
prFileContents: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
path: string
|
||||
oldPath?: string
|
||||
status: GitHubPRFile['status']
|
||||
headSha: string
|
||||
baseSha: string
|
||||
}
|
||||
) => Promise<GitHubPRFileContents>
|
||||
prChecks: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
headSha?: string
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
noCache?: boolean
|
||||
}
|
||||
) => Promise<PRCheckDetail[]>
|
||||
prCheckDetails: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
checkRunId?: number
|
||||
workflowRunId?: number
|
||||
checkName?: string
|
||||
url?: string | null
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}) => Promise<PRCheckRunDetails | null>
|
||||
rerunPRChecks: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
headSha?: string
|
||||
failedOnly?: boolean
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true; count: number } | { ok: false; error: string }>
|
||||
prComments: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
prNumber: number
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
noCache?: boolean
|
||||
}) => Promise<PRComment[]>
|
||||
setPRCommentReaction: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
reactionSubjectId: string
|
||||
content: GitHubReactionContent
|
||||
reacted: boolean
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}) => Promise<boolean>
|
||||
resolveReviewThread: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
threadId: string
|
||||
resolve: boolean
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}) => Promise<boolean>
|
||||
setPRFileViewed: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
pullRequestId: string
|
||||
path: string
|
||||
viewed: boolean
|
||||
}
|
||||
) => Promise<boolean>
|
||||
updatePRTitle: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
prNumber: number
|
||||
title: string
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}) => Promise<boolean>
|
||||
mergePR: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
method?: 'merge' | 'squash' | 'rebase'
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
setPRAutoMerge: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
enabled: boolean
|
||||
method?: 'merge' | 'squash' | 'rebase'
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
updatePRState: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
updates: { state: 'open' | 'closed' }
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
requestPRReviewers: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
reviewers: string[]
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
removePRReviewers: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
reviewers: string[]
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
addPRReviewCommentReply: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
prNumber: number
|
||||
commentId: number
|
||||
body: string
|
||||
threadId?: string
|
||||
path?: string
|
||||
line?: number
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<GitHubCommentResult>
|
||||
addPRReviewComment: (
|
||||
args: GitHubPRReviewCommentInput & {
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
}
|
||||
) => Promise<GitHubCommentResult>
|
||||
checkOrcaStarred: () => Promise<boolean | null>
|
||||
starOrca: (source: AppStarSource) => Promise<boolean>
|
||||
/**
|
||||
* GitHub API rate-limit snapshot. Does NOT consume quota (the
|
||||
* `rate_limit` endpoint is exempt). Cached 30s server-side — pass
|
||||
* `force: true` to bust after a known-expensive op.
|
||||
*/
|
||||
rateLimit: (args?: { force?: boolean }) => Promise<GetRateLimitResult>
|
||||
/** Explains scope_missing ProjectV2 failures — notably a shell `GITHUB_TOKEN` shadowing the keyring credential, where `gh auth refresh` is a no-op. */
|
||||
diagnoseAuth: (args?: { host?: string }) => Promise<GhAuthDiagnostic>
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import type {
|
||||
AddIssueCommentBySlugArgs,
|
||||
ClearProjectItemFieldArgs,
|
||||
DeleteIssueCommentBySlugArgs,
|
||||
GetProjectViewTableArgs,
|
||||
GetProjectViewTableResult,
|
||||
GitHubProjectCommentMutationResult,
|
||||
GitHubProjectMutationResult,
|
||||
ListAccessibleProjectsArgs,
|
||||
ListAccessibleProjectsResult,
|
||||
ListAssignableUsersBySlugArgs,
|
||||
ListAssignableUsersBySlugResult,
|
||||
ListIssueTypesBySlugArgs,
|
||||
ListIssueTypesBySlugResult,
|
||||
ListLabelsBySlugArgs,
|
||||
ListLabelsBySlugResult,
|
||||
ListProjectViewsArgs,
|
||||
ListProjectViewsResult,
|
||||
ProjectWorkItemDetailsBySlugArgs,
|
||||
ProjectWorkItemDetailsBySlugResult,
|
||||
ResolveProjectRefArgs,
|
||||
ResolveProjectRefResult,
|
||||
UpdateIssueBySlugArgs,
|
||||
UpdateIssueCommentBySlugArgs,
|
||||
UpdateIssueTypeBySlugArgs,
|
||||
UpdateProjectItemFieldArgs,
|
||||
UpdatePullRequestBySlugArgs
|
||||
} from '../../shared/github/project-types'
|
||||
import type { TaskSourceContext } from '../../shared/task-source-context'
|
||||
import type {
|
||||
GitHubAssignableUser,
|
||||
GitHubCommentResult,
|
||||
GitHubCreateIssueResult,
|
||||
GitHubIssueUpdate,
|
||||
GitHubOwnerRepo,
|
||||
GitHubWorkItem,
|
||||
GitHubWorkItemDetails,
|
||||
IssueInfo,
|
||||
ListWorkItemsResult
|
||||
} from '../../shared/types'
|
||||
|
||||
export type GitHubRepoSelectorArgs = {
|
||||
repoPath: string
|
||||
repoId?: string | null
|
||||
sourceContext?: TaskSourceContext | null
|
||||
}
|
||||
|
||||
export type GithubWorkItemApi = {
|
||||
issue: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
number: number
|
||||
}) => Promise<IssueInfo | null>
|
||||
workItem: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
number: number
|
||||
type?: 'issue' | 'pr'
|
||||
}) => Promise<Omit<GitHubWorkItem, 'repoId'> | null>
|
||||
workItemByOwnerRepo: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
owner: string
|
||||
repo: string
|
||||
host?: string
|
||||
number: number
|
||||
type: 'issue' | 'pr'
|
||||
}) => Promise<Omit<GitHubWorkItem, 'repoId'> | null>
|
||||
workItemDetails: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
number: number
|
||||
type?: 'issue' | 'pr'
|
||||
}
|
||||
) => Promise<GitHubWorkItemDetails | null>
|
||||
notifyWorkItemMutated: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
type: 'issue' | 'pr'
|
||||
number: number
|
||||
}) => Promise<boolean>
|
||||
listIssues: (args: { repoPath: string; repoId?: string; limit?: number }) => Promise<IssueInfo[]>
|
||||
createIssue: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
title: string
|
||||
body: string
|
||||
labels?: string[]
|
||||
assignees?: string[]
|
||||
}) => Promise<GitHubCreateIssueResult>
|
||||
countWorkItems: (args: { repoPath: string; repoId?: string; query?: string }) => Promise<number>
|
||||
listWorkItems: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
limit?: number
|
||||
query?: string
|
||||
page?: number
|
||||
noCache?: boolean
|
||||
}) => Promise<ListWorkItemsResult<Omit<GitHubWorkItem, 'repoId'>>>
|
||||
updateIssue: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
number: number
|
||||
updates: GitHubIssueUpdate
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
addIssueComment: (
|
||||
args: GitHubRepoSelectorArgs & {
|
||||
number: number
|
||||
body: string
|
||||
/** Why: scopes the cross-window cache invalidation so a PR and issue sharing the same number don't evict each other. */
|
||||
type?: 'issue' | 'pr'
|
||||
prRepo?: GitHubOwnerRepo | null
|
||||
}
|
||||
) => Promise<GitHubCommentResult>
|
||||
listLabels: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
}) => Promise<string[]>
|
||||
listAssignableUsers: (args: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
sourceContext?: TaskSourceContext | null
|
||||
}) => Promise<GitHubAssignableUser[]>
|
||||
/** Subscribe to local-mutation broadcasts so the work-item-drawer cache can invalidate across windows. Returns an unsubscribe. */
|
||||
onWorkItemMutated: (
|
||||
callback: (payload: {
|
||||
repoPath: string
|
||||
repoId?: string
|
||||
type: 'issue' | 'pr'
|
||||
number: number
|
||||
}) => void
|
||||
) => () => void
|
||||
// ── ProjectV2 (GitHub Projects) ─────────────────────────────────
|
||||
listAccessibleProjects: (
|
||||
args?: ListAccessibleProjectsArgs
|
||||
) => Promise<ListAccessibleProjectsResult>
|
||||
resolveProjectRef: (args: ResolveProjectRefArgs) => Promise<ResolveProjectRefResult>
|
||||
listProjectViews: (args: ListProjectViewsArgs) => Promise<ListProjectViewsResult>
|
||||
getProjectViewTable: (args: GetProjectViewTableArgs) => Promise<GetProjectViewTableResult>
|
||||
projectWorkItemDetailsBySlug: (
|
||||
args: ProjectWorkItemDetailsBySlugArgs
|
||||
) => Promise<ProjectWorkItemDetailsBySlugResult>
|
||||
updateProjectItemField: (args: UpdateProjectItemFieldArgs) => Promise<GitHubProjectMutationResult>
|
||||
clearProjectItemField: (args: ClearProjectItemFieldArgs) => Promise<GitHubProjectMutationResult>
|
||||
updateIssueBySlug: (args: UpdateIssueBySlugArgs) => Promise<GitHubProjectMutationResult>
|
||||
updatePullRequestBySlug: (
|
||||
args: UpdatePullRequestBySlugArgs
|
||||
) => Promise<GitHubProjectMutationResult>
|
||||
addIssueCommentBySlug: (
|
||||
args: AddIssueCommentBySlugArgs
|
||||
) => Promise<GitHubProjectCommentMutationResult>
|
||||
updateIssueCommentBySlug: (
|
||||
args: UpdateIssueCommentBySlugArgs
|
||||
) => Promise<GitHubProjectMutationResult>
|
||||
deleteIssueCommentBySlug: (
|
||||
args: DeleteIssueCommentBySlugArgs
|
||||
) => Promise<GitHubProjectMutationResult>
|
||||
listLabelsBySlug: (args: ListLabelsBySlugArgs) => Promise<ListLabelsBySlugResult>
|
||||
listAssignableUsersBySlug: (
|
||||
args: ListAssignableUsersBySlugArgs
|
||||
) => Promise<ListAssignableUsersBySlugResult>
|
||||
listIssueTypesBySlug: (args: ListIssueTypesBySlugArgs) => Promise<ListIssueTypesBySlugResult>
|
||||
updateIssueTypeBySlug: (args: UpdateIssueTypeBySlugArgs) => Promise<GitHubProjectMutationResult>
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import type { TaskSourceContext } from '../../shared/task-source-context'
|
||||
import type {
|
||||
ClassifiedError,
|
||||
GetGitLabRateLimitResult,
|
||||
GitLabAssignableUser,
|
||||
GitLabAuthDiagnostic,
|
||||
GitLabCommentResult,
|
||||
GitLabDiscussionResolveResult,
|
||||
GitLabIssueInfo,
|
||||
GitLabIssueUpdate,
|
||||
GitLabJobTraceResult,
|
||||
GitLabMRInlineCommentInput,
|
||||
GitLabMRReviewersUpdateResult,
|
||||
GitLabMRUpdate,
|
||||
GitLabProjectRef,
|
||||
GitLabRetryJobResult,
|
||||
GitLabTodo,
|
||||
GitLabViewer,
|
||||
GitLabWorkItem,
|
||||
GitLabWorkItemDetails,
|
||||
ListMergeRequestsResult,
|
||||
MRInfo,
|
||||
MRListState
|
||||
} from '../../shared/types'
|
||||
|
||||
export type GitLabRepoSelectorArgs = {
|
||||
repoPath: string
|
||||
repoId?: string | null
|
||||
sourceContext?: TaskSourceContext | null
|
||||
}
|
||||
|
||||
// ── GitLab — parallel to gh, MR/issue surface only in v1 ────────
|
||||
// Shapes mirror gh.* except where GitLab's API differs (MR states, host-qualified project path, `glab api -i` paging).
|
||||
export type GitLabApi = {
|
||||
viewer: () => Promise<GitLabViewer | null>
|
||||
diagnoseAuth: () => Promise<GitLabAuthDiagnostic>
|
||||
rateLimit: (args?: { force?: boolean; host?: string | null }) => Promise<GetGitLabRateLimitResult>
|
||||
projectSlug: (args: GitLabRepoSelectorArgs) => Promise<GitLabProjectRef | null>
|
||||
mrForBranch: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
branch: string
|
||||
linkedMRIid?: number | null
|
||||
}
|
||||
) => Promise<MRInfo | null>
|
||||
mr: (args: GitLabRepoSelectorArgs & { iid: number }) => Promise<MRInfo | null>
|
||||
listMRs: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
state?: MRListState
|
||||
page?: number
|
||||
perPage?: number
|
||||
query?: string
|
||||
}
|
||||
) => Promise<ListMergeRequestsResult>
|
||||
/** Combined MR + issue list filtered by state. Issues are skipped
|
||||
* when state is 'merged' (issues don't merge). */
|
||||
listWorkItems: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
state?: MRListState
|
||||
page?: number
|
||||
perPage?: number
|
||||
query?: string
|
||||
}
|
||||
) => Promise<ListMergeRequestsResult>
|
||||
issue: (args: GitLabRepoSelectorArgs & { number: number }) => Promise<GitLabIssueInfo | null>
|
||||
listIssues: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
state?: 'opened' | 'closed' | 'all'
|
||||
assignee?: string
|
||||
limit?: number
|
||||
}
|
||||
) => Promise<{ items: GitLabWorkItem[]; error?: ClassifiedError }>
|
||||
createIssue: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
title: string
|
||||
body: string
|
||||
}
|
||||
) => Promise<{ ok: true; number: number; url: string } | { ok: false; error: string }>
|
||||
updateIssue: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
number: number
|
||||
updates: GitLabIssueUpdate
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
addIssueComment: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
number: number
|
||||
body: string
|
||||
}
|
||||
) => Promise<GitLabCommentResult>
|
||||
listLabels: (args: GitLabRepoSelectorArgs) => Promise<string[]>
|
||||
listAssignableUsers: (args: GitLabRepoSelectorArgs) => Promise<GitLabAssignableUser[]>
|
||||
/** Cross-project user-scoped todos (gitlab.com/dashboard/todos). */
|
||||
todos: (args: GitLabRepoSelectorArgs) => Promise<GitLabTodo[]>
|
||||
/** Aggregated dialog payload — body + discussions + pipeline jobs. */
|
||||
workItemDetails: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
type: 'issue' | 'mr'
|
||||
}
|
||||
) => Promise<GitLabWorkItemDetails | null>
|
||||
closeMR: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
reopenMR: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
mergeMR: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
method?: 'merge' | 'squash' | 'rebase'
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
updateMR: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
updates: GitLabMRUpdate
|
||||
}
|
||||
) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
updateMRReviewers: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
reviewerIds: number[]
|
||||
projectRef?: GitLabProjectRef | null
|
||||
}
|
||||
) => Promise<GitLabMRReviewersUpdateResult>
|
||||
addMRComment: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
body: string
|
||||
}
|
||||
) => Promise<GitLabCommentResult>
|
||||
addMRInlineComment: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
input: GitLabMRInlineCommentInput
|
||||
projectRef?: GitLabProjectRef | null
|
||||
}
|
||||
) => Promise<GitLabCommentResult>
|
||||
resolveMRDiscussion: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
iid: number
|
||||
discussionId: string
|
||||
resolved: boolean
|
||||
}
|
||||
) => Promise<GitLabDiscussionResolveResult>
|
||||
jobTrace: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
jobId: number
|
||||
projectRef?: GitLabProjectRef | null
|
||||
/** Bound the trace in main to a readable excerpt (see gitLabJobTraceToLogExcerpt). */
|
||||
logExcerpt?: boolean
|
||||
}
|
||||
) => Promise<GitLabJobTraceResult>
|
||||
retryJob: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
jobId: number
|
||||
projectRef?: GitLabProjectRef | null
|
||||
}
|
||||
) => Promise<GitLabRetryJobResult>
|
||||
workItemByPath: (
|
||||
args: GitLabRepoSelectorArgs & {
|
||||
host: string
|
||||
path: string
|
||||
iid: number
|
||||
type: 'issue' | 'mr'
|
||||
}
|
||||
) => Promise<Omit<GitLabWorkItem, 'repoId'> | null>
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import type {
|
||||
BitbucketConnectArgs,
|
||||
BitbucketConnectionStatus
|
||||
} from '../../shared/bitbucket-credentials'
|
||||
import type {
|
||||
CreateHostedReviewArgs,
|
||||
CreateHostedReviewResult,
|
||||
CreateStackedHostedReviewArgs,
|
||||
CreateStackedHostedReviewResult,
|
||||
HostedReviewCreationEligibility,
|
||||
HostedReviewCreationEligibilityArgs,
|
||||
HostedReviewForBranchArgs,
|
||||
HostedReviewInfo
|
||||
} from '../../shared/hosted-review'
|
||||
|
||||
export type HostedReviewApi = {
|
||||
forBranch: (args: HostedReviewForBranchArgs) => Promise<HostedReviewInfo | null>
|
||||
getCreationEligibility: (
|
||||
args: HostedReviewCreationEligibilityArgs
|
||||
) => Promise<HostedReviewCreationEligibility>
|
||||
create: (args: CreateHostedReviewArgs) => Promise<CreateHostedReviewResult>
|
||||
createStacked: (args: CreateStackedHostedReviewArgs) => Promise<CreateStackedHostedReviewResult>
|
||||
}
|
||||
|
||||
export type BitbucketApi = {
|
||||
connect: (
|
||||
args: BitbucketConnectArgs
|
||||
) => Promise<{ ok: true; account: string | null } | { ok: false; error: string }>
|
||||
disconnect: () => Promise<void>
|
||||
status: () => Promise<BitbucketConnectionStatus>
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import type {
|
||||
JiraComment,
|
||||
JiraConnectionStatus,
|
||||
JiraCreateField,
|
||||
JiraCreateIssueArgs,
|
||||
JiraIssue,
|
||||
JiraIssueFilter,
|
||||
JiraIssueType,
|
||||
JiraIssueUpdate,
|
||||
JiraPriority,
|
||||
JiraProject,
|
||||
JiraProjectStatusOrder,
|
||||
JiraSiteSelection,
|
||||
JiraTransition,
|
||||
JiraUser,
|
||||
JiraViewer
|
||||
} from '../../shared/types'
|
||||
|
||||
export type JiraApi = {
|
||||
connect: (args: {
|
||||
siteUrl: string
|
||||
email: string
|
||||
apiToken: string
|
||||
authType?: 'cloud' | 'server'
|
||||
}) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }>
|
||||
disconnect: (args?: { siteId?: string }) => Promise<void>
|
||||
selectSite: (args: { siteId: JiraSiteSelection }) => Promise<JiraConnectionStatus>
|
||||
status: () => Promise<JiraConnectionStatus>
|
||||
readStatus: () => Promise<JiraConnectionStatus>
|
||||
testConnection: (args?: {
|
||||
siteId?: string
|
||||
}) => Promise<{ ok: true; viewer: JiraViewer } | { ok: false; error: string }>
|
||||
searchIssues: (args: {
|
||||
jql: string
|
||||
limit?: number
|
||||
siteId?: JiraSiteSelection
|
||||
requestId?: string
|
||||
}) => Promise<JiraIssue[]>
|
||||
cancelSearchIssues: (args: { requestId: string }) => Promise<void>
|
||||
listIssues: (args?: {
|
||||
filter?: JiraIssueFilter
|
||||
limit?: number
|
||||
siteId?: JiraSiteSelection
|
||||
}) => Promise<JiraIssue[]>
|
||||
getIssue: (args: { key: string; siteId?: string }) => Promise<JiraIssue | null>
|
||||
lookupIssueSummary: (args: {
|
||||
key: string
|
||||
siteId: string
|
||||
requestId?: string
|
||||
}) => Promise<JiraIssue | null>
|
||||
cancelIssueSummary: (args: { requestId: string }) => Promise<void>
|
||||
createIssue: (
|
||||
args: JiraCreateIssueArgs
|
||||
) => Promise<{ ok: true; id: string; key: string; url: string } | { ok: false; error: string }>
|
||||
updateIssue: (args: {
|
||||
key: string
|
||||
updates: JiraIssueUpdate
|
||||
siteId?: string
|
||||
}) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
addIssueComment: (args: {
|
||||
key: string
|
||||
body: string
|
||||
siteId?: string
|
||||
}) => Promise<{ ok: true; id: string } | { ok: false; error: string }>
|
||||
issueComments: (args: { key: string; siteId?: string }) => Promise<JiraComment[]>
|
||||
listProjects: (args?: { siteId?: JiraSiteSelection }) => Promise<JiraProject[]>
|
||||
listIssueTypes: (args: { projectIdOrKey: string; siteId?: string }) => Promise<JiraIssueType[]>
|
||||
listCreateFields: (args: {
|
||||
projectIdOrKey: string
|
||||
issueTypeId: string
|
||||
siteId?: string
|
||||
}) => Promise<JiraCreateField[]>
|
||||
listPriorities: (args?: { siteId?: string }) => Promise<JiraPriority[]>
|
||||
listAssignableUsers: (args: {
|
||||
key: string
|
||||
query?: string
|
||||
siteId?: string
|
||||
}) => Promise<JiraUser[]>
|
||||
listTransitions: (args: { key: string; siteId?: string }) => Promise<JiraTransition[]>
|
||||
getProjectStatusOrder: (args: {
|
||||
projectKey: string
|
||||
siteId?: string
|
||||
}) => Promise<JiraProjectStatusOrder>
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { LinearIssueAttributeFilter } from '../../shared/linear/issue-attribute-filter'
|
||||
import type {
|
||||
LinearCollectionResult,
|
||||
LinearComment,
|
||||
LinearConnectionStatus,
|
||||
LinearCustomViewModel,
|
||||
LinearCustomViewSummary,
|
||||
LinearIssue,
|
||||
LinearIssueUpdate,
|
||||
LinearLabel,
|
||||
LinearMember,
|
||||
LinearProjectDetail,
|
||||
LinearProjectSummary,
|
||||
LinearTeam,
|
||||
LinearViewer,
|
||||
LinearWorkflowState,
|
||||
LinearWorkspaceSelection
|
||||
} from '../../shared/types'
|
||||
|
||||
export type LinearApi = {
|
||||
connect: (args: {
|
||||
apiKey: string
|
||||
}) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }>
|
||||
disconnect: (args?: { workspaceId?: string }) => Promise<void>
|
||||
selectWorkspace: (args: {
|
||||
workspaceId: LinearWorkspaceSelection
|
||||
}) => Promise<LinearConnectionStatus>
|
||||
status: () => Promise<LinearConnectionStatus>
|
||||
testConnection: (args?: {
|
||||
workspaceId?: string
|
||||
}) => Promise<{ ok: true; viewer: LinearViewer } | { ok: false; error: string }>
|
||||
searchIssues: (args: {
|
||||
query: string
|
||||
limit?: number
|
||||
workspaceId?: LinearWorkspaceSelection
|
||||
}) => Promise<LinearIssue[]>
|
||||
listIssues: (args?: {
|
||||
filter?: 'assigned' | 'created' | 'all' | 'completed'
|
||||
limit?: number
|
||||
workspaceId?: LinearWorkspaceSelection
|
||||
attributeFilter?: LinearIssueAttributeFilter
|
||||
}) => Promise<LinearCollectionResult<LinearIssue>>
|
||||
createIssue: (args: {
|
||||
teamId: string
|
||||
title: string
|
||||
description?: string
|
||||
workspaceId?: string
|
||||
parentIssueId?: string
|
||||
projectId?: string | null
|
||||
stateId?: string
|
||||
priority?: number
|
||||
assigneeId?: string | null
|
||||
labelIds?: string[]
|
||||
}) => Promise<
|
||||
| { ok: true; id: string; identifier: string; title: string; url: string }
|
||||
| { ok: false; error: string }
|
||||
>
|
||||
getIssue: (args: { id: string; workspaceId?: string }) => Promise<LinearIssue | null>
|
||||
updateIssue: (args: {
|
||||
id: string
|
||||
updates: LinearIssueUpdate
|
||||
workspaceId?: string
|
||||
}) => Promise<{ ok: true } | { ok: false; error: string }>
|
||||
addIssueComment: (args: {
|
||||
issueId: string
|
||||
body: string
|
||||
workspaceId?: string
|
||||
}) => Promise<{ ok: true; id: string } | { ok: false; error: string }>
|
||||
issueComments: (args: { issueId: string; workspaceId?: string }) => Promise<LinearComment[]>
|
||||
listTeams: (args?: { workspaceId?: LinearWorkspaceSelection }) => Promise<LinearTeam[]>
|
||||
listProjects: (args?: {
|
||||
query?: string
|
||||
limit?: number
|
||||
workspaceId?: LinearWorkspaceSelection
|
||||
force?: boolean
|
||||
}) => Promise<LinearCollectionResult<LinearProjectSummary>>
|
||||
createProject: (args: {
|
||||
name: string
|
||||
description?: string
|
||||
content?: string
|
||||
teamIds: string[]
|
||||
workspaceId?: string
|
||||
leadId?: string | null
|
||||
memberIds?: string[]
|
||||
labelIds?: string[]
|
||||
priority?: number
|
||||
startDate?: string
|
||||
targetDate?: string
|
||||
}) => Promise<{ ok: true; project: LinearProjectDetail } | { ok: false; error: string }>
|
||||
getProject: (args: {
|
||||
id: string
|
||||
workspaceId: string
|
||||
force?: boolean
|
||||
}) => Promise<LinearProjectDetail | null>
|
||||
listProjectIssues: (args: {
|
||||
projectId: string
|
||||
limit?: number
|
||||
workspaceId: string
|
||||
force?: boolean
|
||||
}) => Promise<LinearCollectionResult<LinearIssue>>
|
||||
listCustomViews: (args: {
|
||||
model: LinearCustomViewModel
|
||||
limit?: number
|
||||
workspaceId?: LinearWorkspaceSelection
|
||||
force?: boolean
|
||||
}) => Promise<LinearCollectionResult<LinearCustomViewSummary>>
|
||||
getCustomView: (args: {
|
||||
viewId: string
|
||||
model: LinearCustomViewModel
|
||||
workspaceId: string
|
||||
force?: boolean
|
||||
}) => Promise<LinearCustomViewSummary | null>
|
||||
listCustomViewIssues: (args: {
|
||||
viewId: string
|
||||
limit?: number
|
||||
workspaceId: string
|
||||
force?: boolean
|
||||
}) => Promise<LinearCollectionResult<LinearIssue>>
|
||||
listCustomViewProjects: (args: {
|
||||
viewId: string
|
||||
limit?: number
|
||||
workspaceId: string
|
||||
force?: boolean
|
||||
}) => Promise<LinearCollectionResult<LinearProjectSummary>>
|
||||
teamStates: (args: { teamId: string; workspaceId?: string }) => Promise<LinearWorkflowState[]>
|
||||
teamLabels: (args: { teamId: string; workspaceId?: string }) => Promise<LinearLabel[]>
|
||||
teamMembers: (args: { teamId: string; workspaceId?: string }) => Promise<LinearMember[]>
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { MobileRelayStatus } from '../../shared/mobile-relay-status'
|
||||
import type { MobilePairingConnectionMode } from '../../shared/mobile-pairing-connection-mode'
|
||||
import type { RuntimePairingReach } from '../../shared/runtime-pairing-reach'
|
||||
import type { MobileRelayMintFailure } from '../../shared/mobile-relay-mint-failure'
|
||||
import type { RuntimeAccessGrant } from '../../shared/runtime-access-grants'
|
||||
|
||||
export type MobileApi = {
|
||||
listNetworkInterfaces: () => Promise<{
|
||||
interfaces: { name: string; address: string; hasDefaultRoute?: boolean }[]
|
||||
}>
|
||||
getPairingQR: (args?: {
|
||||
address?: string
|
||||
connectionMode?: MobilePairingConnectionMode
|
||||
rotate?: boolean
|
||||
}) => Promise<
|
||||
| {
|
||||
available: false
|
||||
reason?: string
|
||||
guidance?: string
|
||||
relayFailure?: MobileRelayMintFailure
|
||||
}
|
||||
| {
|
||||
available: true
|
||||
qrDataUrl: string | null
|
||||
qrError?: 'encoding_failed'
|
||||
pairingUrl: string
|
||||
/** Null when no direct address was advertised — the QR pairs over Relay alone. */
|
||||
endpoint: string | null
|
||||
deviceId: string
|
||||
/** Mode the QR actually encodes. */
|
||||
connectionMode: MobilePairingConnectionMode
|
||||
}
|
||||
>
|
||||
getWindowsFirewallStatus: (args?: { address?: string }) => Promise<
|
||||
| { supported: false }
|
||||
| {
|
||||
supported: true
|
||||
port: number
|
||||
ruleAllowed: boolean
|
||||
blockingRuleDetected: boolean
|
||||
privateFirewallEnabled: boolean
|
||||
networkCategory: 'private' | 'public' | 'domain' | 'unknown'
|
||||
inspectionAvailable: boolean
|
||||
}
|
||||
>
|
||||
repairWindowsFirewall: () => Promise<
|
||||
{ ok: true } | { ok: false; reason: 'cancelled' | 'failed' | 'unsupported' }
|
||||
>
|
||||
openWindowsNetworkSettings: () => Promise<boolean>
|
||||
getRuntimePairingUrl: (args?: {
|
||||
address?: string
|
||||
rotate?: boolean
|
||||
reach?: RuntimePairingReach
|
||||
}) => Promise<
|
||||
| {
|
||||
available: false
|
||||
reason?: 'network_exposure_failed'
|
||||
guidance?: string
|
||||
}
|
||||
| {
|
||||
available: true
|
||||
pairingUrl: string
|
||||
webClientUrl: string | null
|
||||
endpoint: string
|
||||
deviceId: string
|
||||
}
|
||||
>
|
||||
listDevices: () => Promise<{
|
||||
devices: {
|
||||
deviceId: string
|
||||
name: string
|
||||
pairedAt: number
|
||||
lastSeenAt: number
|
||||
}[]
|
||||
}>
|
||||
revokeDevice: (args: { deviceId: string }) => Promise<{ revoked: boolean }>
|
||||
listRuntimeAccessGrants: () => Promise<{ grants: RuntimeAccessGrant[] }>
|
||||
revokeRuntimeAccess: (args: { deviceId: string }) => Promise<{ revoked: boolean }>
|
||||
isWebSocketReady: () => Promise<{ ready: boolean; endpoint: string | null }>
|
||||
getRelayStatus: () => Promise<{ status: MobileRelayStatus }>
|
||||
onRelayStatusChanged: (callback: (status: MobileRelayStatus) => void) => () => void
|
||||
/** Consumes an auth-failure notification that arrived before the renderer listener mounted. */
|
||||
consumePendingUnpairedDeviceAuthFailure?: () => Promise<boolean>
|
||||
/** Fires (throttled, once per session) when an unpaired phone repeatedly fails direct-transport auth. */
|
||||
onUnpairedDeviceAuthFailure?: (callback: () => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import type {
|
||||
AgentType,
|
||||
NativeChatMessage,
|
||||
NativeChatTurnLifecycle
|
||||
} from '../../shared/native-chat-types'
|
||||
|
||||
// notFound marks a not-yet-on-disk miss (retry-worthy) vs a real read/parse error (#8401).
|
||||
export type NativeChatReadSessionResult =
|
||||
| {
|
||||
messages: NativeChatMessage[]
|
||||
lifecycle?: NativeChatTurnLifecycle
|
||||
}
|
||||
| { error: string; notFound?: true }
|
||||
|
||||
/** Messages appended to a live-tailed transcript since the previous emit. */
|
||||
export type NativeChatAppendedMessages = NativeChatMessage[]
|
||||
|
||||
export type NativeChatSubscriptionFrame =
|
||||
| {
|
||||
type: 'snapshot'
|
||||
messages: NativeChatMessage[]
|
||||
hasMore: boolean
|
||||
error?: string
|
||||
lifecycle?: NativeChatTurnLifecycle
|
||||
}
|
||||
| {
|
||||
type: 'replacement'
|
||||
messages: NativeChatMessage[]
|
||||
hasMore: boolean
|
||||
lifecycle?: NativeChatTurnLifecycle
|
||||
}
|
||||
| {
|
||||
type: 'appended'
|
||||
messages: NativeChatMessage[]
|
||||
lifecycle?: NativeChatTurnLifecycle
|
||||
}
|
||||
|
||||
/** Wire payload for the `nativeChat:appended` push channel. */
|
||||
export type NativeChatAppendedPayload = {
|
||||
subscriptionId: string
|
||||
frame: NativeChatSubscriptionFrame
|
||||
}
|
||||
|
||||
export type NativeChatSubscribeArgs = {
|
||||
/** Unique per-caller id, echoed on every append so multiple live panes in
|
||||
* one renderer don't cross-talk. */
|
||||
subscriptionId: string
|
||||
agent: AgentType
|
||||
sessionId: string
|
||||
/** Authoritative transcript path from the agent hook (providerSession). */
|
||||
transcriptPath?: string
|
||||
/** First snapshot size; later readSession calls grow this for pagination. */
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export type NativeChatApi = {
|
||||
/** Read the on-disk transcript for an agent + session id, windowed to the most recent `limit`
|
||||
* turns. `transcriptPath` is the hook-reported authoritative path, preferred over the id glob. */
|
||||
readSession: (
|
||||
agent: AgentType,
|
||||
sessionId: string,
|
||||
limit?: number,
|
||||
transcriptPath?: string
|
||||
) => Promise<NativeChatReadSessionResult>
|
||||
/** Live-tail a transcript. The first frame is a bounded race-safe snapshot;
|
||||
* later frames contain only newly appended messages. */
|
||||
subscribe: (
|
||||
args: NativeChatSubscribeArgs,
|
||||
onFrame: (frame: NativeChatSubscriptionFrame) => void
|
||||
) => () => void
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { OnboardingState } from '../../shared/types'
|
||||
|
||||
export type OnboardingApi = {
|
||||
get: () => Promise<OnboardingState>
|
||||
// Why: main merges the checklist field-by-field, so a partial checklist is fine.
|
||||
update: (
|
||||
updates: Partial<Omit<OnboardingState, 'checklist'>> & {
|
||||
checklist?: Partial<OnboardingState['checklist']>
|
||||
}
|
||||
) => Promise<OnboardingState>
|
||||
}
|
||||
|
||||
export type StarNagApi = {
|
||||
onShow: (
|
||||
callback: (payload?: { mode?: 'gh' | 'web'; surface?: 'card' | 'toast' }) => void
|
||||
) => () => void
|
||||
onHide: (callback: () => void) => () => void
|
||||
dismiss: () => Promise<void>
|
||||
later: () => Promise<void>
|
||||
complete: () => Promise<void>
|
||||
disable: () => Promise<void>
|
||||
openWeb: () => Promise<void>
|
||||
starOrca: () => Promise<boolean>
|
||||
forceShow: () => Promise<void>
|
||||
agentValueMoment: () => Promise<{ status: 'ready'; mode: 'gh' | 'web' } | { status: 'skipped' }>
|
||||
showAgentValueMoment: () => Promise<void>
|
||||
onboardingCompleted: () => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type {
|
||||
ConnectCurrentOrcaProfileResult,
|
||||
CreateCloudLinkedOrcaProfileArgs,
|
||||
CreateCloudLinkedOrcaProfileResult,
|
||||
CreateLocalOrcaProfileArgs,
|
||||
CreateLocalOrcaProfileResult,
|
||||
FindOrcaProfileProjectsByPathArgs,
|
||||
FindOrcaProfileProjectsByPathResult,
|
||||
OrcaProfileAuthStatus,
|
||||
OrcaProfileListResult,
|
||||
OrcaProfileOrgInviteRevokeArgs,
|
||||
OrcaProfileOrgMemberChangeRoleArgs,
|
||||
OrcaProfileOrgMemberInviteArgs,
|
||||
OrcaProfileOrgMemberMutationResult,
|
||||
OrcaProfileOrgMemberRemoveArgs,
|
||||
OrcaProfileOrgMembersListArgs,
|
||||
OrcaProfileOrgMembersListResult,
|
||||
RefreshCurrentOrcaProfileAuthResult,
|
||||
SelectOrcaProfileOrgArgs,
|
||||
SelectOrcaProfileOrgResult,
|
||||
SignOutCurrentOrcaProfileResult,
|
||||
SwitchOrcaProfileArgs,
|
||||
SwitchOrcaProfileResult,
|
||||
TransferOrcaProfileProjectArgs,
|
||||
TransferOrcaProfileProjectResult
|
||||
} from '../../shared/orca-profiles'
|
||||
|
||||
export type OrcaProfileApi = {
|
||||
list: () => Promise<OrcaProfileListResult>
|
||||
authStatus: () => Promise<OrcaProfileAuthStatus>
|
||||
createLocal: (args?: CreateLocalOrcaProfileArgs) => Promise<CreateLocalOrcaProfileResult>
|
||||
createCloudLinked: (
|
||||
args?: CreateCloudLinkedOrcaProfileArgs
|
||||
) => Promise<CreateCloudLinkedOrcaProfileResult>
|
||||
switchProfile: (args: SwitchOrcaProfileArgs) => Promise<SwitchOrcaProfileResult>
|
||||
transferProject: (
|
||||
args: TransferOrcaProfileProjectArgs
|
||||
) => Promise<TransferOrcaProfileProjectResult>
|
||||
findProjectProfiles: (
|
||||
args: FindOrcaProfileProjectsByPathArgs
|
||||
) => Promise<FindOrcaProfileProjectsByPathResult>
|
||||
connectCurrent: () => Promise<ConnectCurrentOrcaProfileResult>
|
||||
refreshAuth: () => Promise<RefreshCurrentOrcaProfileAuthResult>
|
||||
signOutCurrent: () => Promise<SignOutCurrentOrcaProfileResult>
|
||||
selectOrg: (args: SelectOrcaProfileOrgArgs) => Promise<SelectOrcaProfileOrgResult>
|
||||
orgMembersList: (args: OrcaProfileOrgMembersListArgs) => Promise<OrcaProfileOrgMembersListResult>
|
||||
orgMemberInvite: (
|
||||
args: OrcaProfileOrgMemberInviteArgs
|
||||
) => Promise<OrcaProfileOrgMemberMutationResult>
|
||||
orgInviteRevoke: (
|
||||
args: OrcaProfileOrgInviteRevokeArgs
|
||||
) => Promise<OrcaProfileOrgMemberMutationResult>
|
||||
orgMemberChangeRole: (
|
||||
args: OrcaProfileOrgMemberChangeRoleArgs
|
||||
) => Promise<OrcaProfileOrgMemberMutationResult>
|
||||
orgMemberRemove: (
|
||||
args: OrcaProfileOrgMemberRemoveArgs
|
||||
) => Promise<OrcaProfileOrgMemberMutationResult>
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type {
|
||||
ComputerUsePermissionId,
|
||||
ComputerUsePermissionResetResult,
|
||||
ComputerUsePermissionSetupResult,
|
||||
ComputerUsePermissionStatusResult
|
||||
} from '../../shared/computer-use-permissions-types'
|
||||
import type {
|
||||
DeveloperPermissionId,
|
||||
DeveloperPermissionRequestResult,
|
||||
DeveloperPermissionState,
|
||||
LocalNetworkConnectionTestResult
|
||||
} from '../../shared/developer-permissions-types'
|
||||
import type {
|
||||
NotificationDispatchRequest,
|
||||
NotificationDispatchResult,
|
||||
NotificationDeliveryProbeResult,
|
||||
NotificationDismissResult,
|
||||
NotificationPermissionStatusResult,
|
||||
NotificationSoundResult
|
||||
} from '../../shared/types'
|
||||
|
||||
export type NotificationsApi = {
|
||||
dispatch: (args: NotificationDispatchRequest) => Promise<NotificationDispatchResult>
|
||||
dismiss: (ids: string[]) => Promise<NotificationDismissResult>
|
||||
openSystemSettings: () => Promise<void>
|
||||
getPermissionStatus: () => Promise<NotificationPermissionStatusResult>
|
||||
probeDelivery: (args?: { force?: boolean }) => Promise<NotificationDeliveryProbeResult>
|
||||
playSound: (options?: { force?: boolean; volume?: number }) => Promise<NotificationSoundResult>
|
||||
}
|
||||
|
||||
export type MacosTccPromptsApi = {
|
||||
/** Fires once macOS has raised its Nth consent dialog naming Orca (#9756). */
|
||||
onThreshold: (callback: (payload: { promptCount: number }) => void) => () => void
|
||||
consumePending: () => Promise<{ claimId: number; promptCount: number } | null>
|
||||
acknowledgePending: (claimId: number) => Promise<void>
|
||||
releasePending: (claimId: number) => Promise<void>
|
||||
dismiss: () => Promise<void>
|
||||
}
|
||||
|
||||
export type DeveloperPermissionsApi = {
|
||||
getStatus: () => Promise<DeveloperPermissionState[]>
|
||||
request: (args: { id: DeveloperPermissionId }) => Promise<DeveloperPermissionRequestResult>
|
||||
openSettings: (args: { id: DeveloperPermissionId }) => Promise<void>
|
||||
testLocalNetworkConnection: (args: {
|
||||
host: string
|
||||
port: number
|
||||
}) => Promise<LocalNetworkConnectionTestResult>
|
||||
}
|
||||
|
||||
export type ComputerUsePermissionsApi = {
|
||||
getStatus: () => Promise<ComputerUsePermissionStatusResult>
|
||||
openSetup: (args?: { id?: ComputerUsePermissionId }) => Promise<ComputerUsePermissionSetupResult>
|
||||
reset: () => Promise<ComputerUsePermissionResetResult>
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { CustomPet } from '../../shared/types'
|
||||
|
||||
export type PetApi = {
|
||||
import: () => Promise<CustomPet | null>
|
||||
importPetBundle: () => Promise<CustomPet | null>
|
||||
read: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise<ArrayBuffer | null>
|
||||
delete: (id: string, fileName: string, kind?: 'image' | 'bundle') => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
import type {
|
||||
PluginPanelActionOutcome,
|
||||
PluginPanelEntry
|
||||
} from '../../shared/plugins/plugin-panel-bridge'
|
||||
import type { PluginConsentRequest } from '../../shared/plugins/plugin-consent-request'
|
||||
import type { PluginLanguagePackRegistration } from '../../shared/plugins/plugin-language-pack-artifact'
|
||||
import type { PluginChangeEvent } from '../../shared/plugins/plugin-change-event'
|
||||
import type { PluginManifest } from '../../shared/plugins/plugin-manifest'
|
||||
import type { PluginMarketplaceGitSource } from '../../shared/plugins/plugin-marketplace'
|
||||
|
||||
/** Panel contribution as surfaced by the main-process plugin service. */
|
||||
export type PluginHostPanel = {
|
||||
id: string
|
||||
title: string
|
||||
/** Lucide icon name declared in the plugin manifest. */
|
||||
icon?: string
|
||||
tabKey: `plugin:${string}`
|
||||
}
|
||||
|
||||
/** `pending` = awaiting (re-)consent; `idle` = enabled, worker not running
|
||||
* (lazy); `restarting` = waiting for supervised backoff; `errored` = crashed past the restart budget or failed to start;
|
||||
* `invalid` = unreadable manifest. */
|
||||
export type PluginHostStatus =
|
||||
| 'running'
|
||||
| 'restarting'
|
||||
| 'idle'
|
||||
| 'pending'
|
||||
| 'disabled'
|
||||
| 'errored'
|
||||
| 'invalid'
|
||||
|
||||
/** Wire shape of plugins:list — must stay assignable from the main-process
|
||||
* projection in src/main/plugins/plugin-list-projection.ts. */
|
||||
export type PluginHostListEntry = {
|
||||
pluginKey: string
|
||||
consentFingerprint: string | null
|
||||
name: string
|
||||
version: string
|
||||
publisher: string
|
||||
description?: string
|
||||
status: PluginHostStatus
|
||||
needsReconsent: boolean
|
||||
error?: string
|
||||
isDev: boolean
|
||||
official: boolean
|
||||
bundled: boolean
|
||||
capabilities: { kind: string; description: string }[]
|
||||
panels: PluginHostPanel[]
|
||||
commands: {
|
||||
id: string
|
||||
title: string
|
||||
context: 'global' | 'worktree'
|
||||
handler: { type: 'built-in'; action: string } | { type: 'worker' }
|
||||
keybindings: { key: string; when: 'global' | 'worktree' }[]
|
||||
}[]
|
||||
hasWorker: boolean
|
||||
vmRecipes?: {
|
||||
id: string
|
||||
name: string
|
||||
description?: string
|
||||
commands: {
|
||||
phase: 'create' | 'suspend' | 'resume' | 'destroy'
|
||||
command: string
|
||||
}[]
|
||||
}[]
|
||||
restarts: number
|
||||
blockedByKillList?: { reason: string; advisoryUrl?: string }
|
||||
source?: {
|
||||
kind: 'local-path' | 'git' | 'marketplace' | 'bundled'
|
||||
reference: string
|
||||
resolvedCommit: string | null
|
||||
contentHash: string
|
||||
marketplace?: { reference: string; resolvedCommit: string }
|
||||
}
|
||||
}
|
||||
|
||||
export type PluginHostLogLine = {
|
||||
ts: number
|
||||
level: 'info' | 'warn' | 'error'
|
||||
line: string
|
||||
}
|
||||
|
||||
export type PluginHostInstallSource =
|
||||
| { kind: 'local-path'; path: string }
|
||||
| { kind: 'git'; url: string; ref: string }
|
||||
|
||||
export type PluginHostInstallResult =
|
||||
| {
|
||||
ok: true
|
||||
pluginKey: string
|
||||
version: string
|
||||
contentHash: string
|
||||
consentFingerprint: string
|
||||
resolvedCommit: string | null
|
||||
}
|
||||
| { ok: false; error: string }
|
||||
|
||||
export type PluginMarketplaceHostSourceState = {
|
||||
id: string
|
||||
source: PluginMarketplaceGitSource
|
||||
addedAt: number
|
||||
marketplace: {
|
||||
name: string
|
||||
owner: string
|
||||
resolvedCommit: string
|
||||
fetchedAt: number
|
||||
} | null
|
||||
stale: boolean
|
||||
official: boolean
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type PluginMarketplaceHostListing = {
|
||||
marketplaceSourceId: string
|
||||
marketplaceName: string
|
||||
marketplaceOwner: string
|
||||
marketplaceCommit: string
|
||||
pluginKey: string
|
||||
source: PluginMarketplaceGitSource
|
||||
description?: string
|
||||
categories: string[]
|
||||
official: boolean
|
||||
bundled: boolean
|
||||
blockedByKillList?: { reason: string; advisoryUrl?: string }
|
||||
}
|
||||
|
||||
export type PluginMarketplaceHostInstallPreview = {
|
||||
marketplaceSourceId: string
|
||||
marketplaceName: string
|
||||
marketplaceOwner: string
|
||||
marketplaceCommit: string
|
||||
pluginKey: string
|
||||
source: PluginMarketplaceGitSource
|
||||
resolvedCommit: string
|
||||
contentHash: string
|
||||
consentFingerprint: string
|
||||
manifest: PluginManifest
|
||||
official: boolean
|
||||
bundled: boolean
|
||||
blockedByKillList?: { reason: string; advisoryUrl?: string }
|
||||
}
|
||||
|
||||
export type PluginsApi = {
|
||||
list: () => Promise<PluginHostListEntry[]>
|
||||
listLanguagePacks: () => Promise<PluginLanguagePackRegistration[]>
|
||||
/** Records the consent-dialog answer; approval is keyed to the plugin's
|
||||
* current capability and trusted-worker fingerprint. */
|
||||
consent: (args: PluginConsentRequest) => Promise<PluginHostListEntry[]>
|
||||
setEnabled: (args: { pluginKey: string; enabled: boolean }) => Promise<PluginHostListEntry[]>
|
||||
/** Returns the panel's CSP-wrapped HTML, or null when the plugin or
|
||||
* panel is missing/disabled. Rendered only inside a sandboxed iframe. */
|
||||
readPanelEntry: (args: { pluginKey: string; panelId: string }) => Promise<PluginPanelEntry | null>
|
||||
invokeCommand: (args: {
|
||||
pluginKey: string
|
||||
commandId: string
|
||||
args?: unknown
|
||||
}) => Promise<unknown>
|
||||
/** Relays a sandboxed panel's bridge request to main, which enforces the
|
||||
* plugin's consented capabilities before executing. */
|
||||
panelAction: (args: {
|
||||
sessionToken: string
|
||||
action: string
|
||||
params?: unknown
|
||||
}) => Promise<PluginPanelActionOutcome>
|
||||
install: (source: PluginHostInstallSource) => Promise<PluginHostInstallResult>
|
||||
listMarketplaces: () => Promise<PluginMarketplaceHostSourceState[]>
|
||||
addMarketplace: (source: PluginMarketplaceGitSource) => Promise<PluginMarketplaceHostSourceState>
|
||||
removeMarketplace: (args: { sourceId: string }) => Promise<PluginMarketplaceHostSourceState[]>
|
||||
refreshMarketplaces: (args?: { sourceId?: string }) => Promise<PluginMarketplaceHostSourceState[]>
|
||||
listMarketplacePlugins: () => Promise<PluginMarketplaceHostListing[]>
|
||||
previewMarketplacePlugin: (args: {
|
||||
marketplaceSourceId: string
|
||||
pluginKey: string
|
||||
}) => Promise<PluginMarketplaceHostInstallPreview>
|
||||
installMarketplacePlugin: (
|
||||
preview: Pick<
|
||||
PluginMarketplaceHostInstallPreview,
|
||||
'marketplaceSourceId' | 'marketplaceCommit' | 'pluginKey' | 'resolvedCommit'
|
||||
>
|
||||
) => Promise<PluginHostInstallResult>
|
||||
previewMarketplaceUpdate: (args: {
|
||||
pluginKey: string
|
||||
}) => Promise<PluginMarketplaceHostInstallPreview>
|
||||
rollbackMarketplacePlugin: (args: { pluginKey: string }) => Promise<PluginHostInstallResult>
|
||||
remove: (args: { pluginKey: string }) => Promise<PluginHostListEntry[]>
|
||||
getLogs: (args: { pluginKey: string }) => Promise<PluginHostLogLine[]>
|
||||
/** Re-discovers after settings edits (feature flag, dev paths). */
|
||||
refresh: () => Promise<PluginHostListEntry[]>
|
||||
/** Fires whenever installed plugins, worker states, panels, or content packs change. */
|
||||
onChanged: (callback: (event: PluginChangeEvent) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime'
|
||||
import type { PathSource, ShellHydrationFailureReason } from '../../shared/types'
|
||||
|
||||
export type PreflightStatus = {
|
||||
git: { installed: boolean }
|
||||
gh: { installed: boolean; authenticated: boolean }
|
||||
/** Optional — older preload payloads predating GitLab support omit it; consumers gate on `glab?.installed`. */
|
||||
glab?: { installed: boolean; authenticated: boolean }
|
||||
bitbucket?: { configured: boolean; authenticated: boolean; account: string | null }
|
||||
azureDevOps?: {
|
||||
configured: boolean
|
||||
authenticated: boolean
|
||||
account: string | null
|
||||
baseUrl: string | null
|
||||
tokenConfigured: boolean
|
||||
}
|
||||
gitea?: {
|
||||
configured: boolean
|
||||
authenticated: boolean
|
||||
account: string | null
|
||||
baseUrl: string | null
|
||||
tokenConfigured: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type RefreshAgentsResult = {
|
||||
agents: string[]
|
||||
addedPathSegments: string[]
|
||||
shellHydrationOk: boolean
|
||||
/** Drives agent_picks `on_path:false` triage (dashboard 1562016). `'shell_hydrate'` = detection saw the user's
|
||||
* full shell PATH; `'sync_seed_only'` = hydration failed and detection ran against the `patchPackagedProcessPath` seed list. */
|
||||
pathSource: PathSource
|
||||
/** Classified hydration outcome: `'none'` on success, else a failure mode when `shellHydrationOk` is false. */
|
||||
pathFailureReason: ShellHydrationFailureReason
|
||||
}
|
||||
|
||||
export type PreflightRuntimeContext = {
|
||||
wslDistro?: string | null
|
||||
wslDefault?: boolean
|
||||
projectRuntime?: ProjectExecutionRuntimeResolution
|
||||
}
|
||||
|
||||
export type PreflightApi = {
|
||||
check: (args?: PreflightRuntimeContext & { force?: boolean }) => Promise<PreflightStatus>
|
||||
detectAgents: (args?: PreflightRuntimeContext) => Promise<string[]>
|
||||
refreshAgents: (args?: PreflightRuntimeContext) => Promise<RefreshAgentsResult>
|
||||
detectRemoteAgents: (args: { connectionId: string }) => Promise<string[]>
|
||||
detectRemoteWindowsTerminalCapabilities: (args: { connectionId: string }) => Promise<{
|
||||
wslAvailable: boolean
|
||||
wslDistros: string[]
|
||||
pwshAvailable: boolean
|
||||
gitBashAvailable: boolean
|
||||
hostPlatform: NodeJS.Platform | null
|
||||
}>
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
import type {
|
||||
AgentProviderSessionMetadata,
|
||||
SleepingAgentLaunchConfig
|
||||
} from '../../shared/agent-session-resume'
|
||||
import type { StartupCommandDelivery } from '../../shared/codex-startup-delivery'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime'
|
||||
import type { PtyListedSession } from '../../shared/pty-listed-session'
|
||||
import type { PtyMainDeliveryDiagnostics } from '../../shared/pty-delivery-diagnostics'
|
||||
import type { PtyModelRestoreNeededEvent } from '../../shared/pty-model-restore-marker'
|
||||
import type {
|
||||
PtyRendererDeliveryHealthReply,
|
||||
PtyRendererDeliveryStateReport
|
||||
} from '../../shared/pty-renderer-delivery-health'
|
||||
import type { AgentKind, LaunchSource, RequestKind } from '../../shared/telemetry-events'
|
||||
import type { TerminalSideEffectBatch } from '../../shared/terminal-side-effect-facts'
|
||||
import type { TerminalViewAttributes } from '../../shared/terminal-view-attributes'
|
||||
import type { TuiAgent } from '../../shared/types'
|
||||
import type { PtyManagementApi } from './pty-management-api'
|
||||
|
||||
export type PtyApi = {
|
||||
spawn: (opts: {
|
||||
cols: number
|
||||
rows: number
|
||||
cwd?: string
|
||||
cwdFallback?: 'worktree'
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
command?: string
|
||||
commandDelivery?: 'renderer' | 'provider'
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
resumeProviderSession?: AgentProviderSessionMetadata
|
||||
launchToken?: string
|
||||
launchAgent?: TuiAgent
|
||||
startupCommandDelivery?: StartupCommandDelivery
|
||||
connectionId?: string | null
|
||||
worktreeId?: string
|
||||
sessionId?: string
|
||||
// Why: lets a single tab open in a different shell than the user's default.
|
||||
shellOverride?: string
|
||||
projectRuntime?: ProjectExecutionRuntimeResolution
|
||||
terminalColorQueryReplies?: { foreground?: string; background?: string }
|
||||
// Why: mark the PTY hidden before its first byte so the delivery gate owns spawn-time queries (terminal-query-authority.md §races).
|
||||
initiallyHidden?: boolean
|
||||
// Why: main sync-flushes the (worktreeId,tabId,leafId→ptyId) binding before pty:spawn returns to close a SIGKILL race (INVESTIGATION.md).
|
||||
tabId?: string
|
||||
leafId?: string
|
||||
// Why: main fires `agent_started` only on spawn success, so launch metadata rides this field (telemetry-plan.md §Agent launch semantics).
|
||||
telemetry?: { agent_kind: AgentKind; launch_source: LaunchSource; request_kind: RequestKind }
|
||||
}) => Promise<{
|
||||
id: string
|
||||
launchAgent?: TuiAgent
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
snapshot?: string
|
||||
snapshotCols?: number
|
||||
snapshotRows?: number
|
||||
snapshotPrefixAnsi?: string
|
||||
snapshotFrameAnsi?: string
|
||||
snapshotFrameRestoreAnsi?: string
|
||||
snapshotKittyKeyboardFlags?: number
|
||||
snapshotSeq?: number
|
||||
isReattach?: boolean
|
||||
isAlternateScreen?: boolean
|
||||
replay?: string
|
||||
sessionExpired?: boolean
|
||||
coldRestore?: { scrollback: string; cwd: string; cols?: number; rows?: number }
|
||||
startupCwdFallback?: { kind: 'worktree'; cwd: string }
|
||||
agentResumeUnavailable?: true
|
||||
}>
|
||||
write: (id: string, data: string) => void
|
||||
writeAccepted: (id: string, data: string) => Promise<boolean>
|
||||
onWriteUnavailable?: (callback: (payload: { id: string }) => void) => () => void
|
||||
resize: (id: string, cols: number, rows: number) => void
|
||||
claimViewport: (id: string, cols: number, rows: number) => void
|
||||
reportGeometry: (id: string, cols: number, rows: number) => void
|
||||
signal: (id: string, signal: string) => void
|
||||
clearBuffer: (id: string) => void
|
||||
kill: (id: string, opts?: { keepHistory?: boolean }) => Promise<void>
|
||||
ackColdRestore: (id: string) => void
|
||||
ackData: (id: string, charCount: number, processedChars?: number) => void
|
||||
onDeliveryResyncRequest: (callback: (payload: { requestId: number }) => void) => () => void
|
||||
respondDeliveryResync: (payload: {
|
||||
requestId: number
|
||||
processedCharsByPty: Record<string, number>
|
||||
}) => void
|
||||
/** Renderer-initiated delivery health/heal lane over invoke — reaches main
|
||||
* even when every main→renderer push channel is dead (field wedge). */
|
||||
reportRendererDeliveryState: (
|
||||
report: PtyRendererDeliveryStateReport
|
||||
) => Promise<PtyRendererDeliveryHealthReply>
|
||||
/** Live pty:data listener count on the preload emitter (sync) — heal-time
|
||||
* discriminator between a detached listener and a dead channel. */
|
||||
getPtyDataListenerCount: () => number
|
||||
/** One-shot signal that this page's pty:data dispatcher is registered, so
|
||||
* main can release sends held during the load/reload boot window. */
|
||||
rendererDispatcherReady: () => void
|
||||
setActiveRendererPty: (id: string, active: boolean) => void
|
||||
setRendererPtyVisible: (id: string, visible: boolean) => void
|
||||
/** Hidden-delivery gate (Phase 4): hidden=true lets main drop renderer
|
||||
* byte delivery after model ingestion; reveal restores from snapshots. */
|
||||
setHiddenRendererPty: (id: string, hidden: boolean) => void
|
||||
/** Ref-counted-on-the-renderer delivery-interest signal that suppresses
|
||||
* the hidden-delivery gate while any raw-byte consumer is registered. */
|
||||
setPtyDeliveryInterest: (id: string, interested: boolean) => void
|
||||
/** View-attribute bridge (Phase 5 slice 2): app-global composed terminal
|
||||
* appearance push backing main's hidden-PTY OSC/DSR color replies. */
|
||||
publishTerminalViewAttributes: (attributes: TerminalViewAttributes) => void
|
||||
hasChildProcesses: (id: string) => Promise<boolean>
|
||||
getForegroundProcess: (id: string) => Promise<string | null>
|
||||
inspectProcess: (id: string) => Promise<{
|
||||
foregroundProcess: string | null
|
||||
hasChildProcesses: boolean
|
||||
unavailable?: true
|
||||
}>
|
||||
confirmForegroundProcess: (id: string) => Promise<string | null>
|
||||
getCwd: (id: string) => Promise<string>
|
||||
getSize: (id: string) => Promise<{ cols: number; rows: number } | null>
|
||||
listSessions: () => Promise<PtyListedSession[]>
|
||||
getAuthoritativeBufferSnapshotCapabilities?: (
|
||||
ids: string[]
|
||||
) => Promise<{ id: string; authoritative: boolean | null }[]>
|
||||
hasPty: (id: string) => Promise<boolean | null>
|
||||
getMainBufferSnapshot: (
|
||||
id: string,
|
||||
opts?: { scrollbackRows?: number }
|
||||
) => Promise<{
|
||||
data: string
|
||||
frameRestoreAnsi?: string
|
||||
cols: number
|
||||
rows: number
|
||||
cwd?: string | null
|
||||
seq?: number
|
||||
/** Start of main's pending renderer-delivery queue at snapshot time
|
||||
* (equals `seq` when empty) — bounds the renderer's post-restore
|
||||
* duplicate window. */
|
||||
pendingDeliveryStartSeq?: number
|
||||
source?: 'headless' | 'renderer'
|
||||
alternateScreen?: boolean
|
||||
/** Authoritative normal buffer paired with an alternate-screen frame. */
|
||||
scrollbackAnsi?: string
|
||||
/** Trailing incomplete escape the emulator ingested; the restorer must
|
||||
* write it after its post-replay resets, last before live chunks. */
|
||||
pendingEscapeTailAnsi?: string
|
||||
/** Effective kitty flags the snapshot owner proved at `seq`. Absent means
|
||||
* unknown; consumers must not turn that into a known `0`. */
|
||||
kittyKeyboardFlags?: number
|
||||
} | null>
|
||||
getRendererDeliveryDebugSnapshot: () => Promise<{
|
||||
pendingPtyCount: number
|
||||
pendingChars: number
|
||||
maxPendingCharsByPty: number
|
||||
rendererInFlightPtyCount: number
|
||||
rendererInFlightChars: number
|
||||
maxRendererInFlightCharsByPty: number
|
||||
activeRendererPtyCount: number
|
||||
flushScheduled: boolean
|
||||
peakPendingChars: number
|
||||
peakMaxPendingCharsByPty: number
|
||||
peakRendererInFlightChars: number
|
||||
peakMaxRendererInFlightCharsByPty: number
|
||||
ackGatedFlushSkipCount: number
|
||||
hiddenDeliveryGatedPtyCount: number
|
||||
hiddenDeliveryGatedVisiblePtyCount: number
|
||||
hiddenDeliveryGatedActivePtyCount: number
|
||||
deliveryInterestPtyCount: number
|
||||
hiddenDeliveryDroppedChars: number
|
||||
hiddenDeliveryDroppedChunks: number
|
||||
pendingDroppedChars: number
|
||||
diagnostics: PtyMainDeliveryDiagnostics
|
||||
rendererLifecycleResetCount: number
|
||||
lastLifecycleResetClearedChars: number
|
||||
rendererPtyDispatcherReady: boolean
|
||||
rendererDispatcherReadyForcedCount: number
|
||||
}>
|
||||
resetRendererDeliveryDebug: () => Promise<void>
|
||||
onData: (
|
||||
callback: (data: {
|
||||
id: string
|
||||
data: string
|
||||
seq?: number
|
||||
rawLength?: number
|
||||
transformed?: boolean
|
||||
background?: boolean
|
||||
droppedOutput?: boolean
|
||||
}) => void
|
||||
) => () => void
|
||||
onReplay: (callback: (data: { id: string; data: string }) => void) => () => void
|
||||
/** Out-of-band main→renderer signal that renderer-bound bytes were
|
||||
* dropped (hidden-delivery gate / pending cap); the pane restores from
|
||||
* the model snapshot. Never delivered in-band on pty:data. */
|
||||
onModelRestoreNeeded: (callback: (event: PtyModelRestoreNeededEvent) => void) => () => void
|
||||
/** Batched derived side-effect facts for PTYs whose bytes transit local
|
||||
* main. */
|
||||
onSideEffect: (callback: (batch: TerminalSideEffectBatch) => void) => () => void
|
||||
/** Title-only replay snapshot for (re)attach; attention facts never replay. */
|
||||
getSideEffectSnapshot: (id: string) => Promise<TerminalSideEffectBatch | null>
|
||||
onExit: (
|
||||
callback: (data: { id: string; code: number; preserveRendererBinding?: boolean }) => void
|
||||
) => () => void
|
||||
onSpawned: (callback: (data: { id: string }) => void) => () => void
|
||||
onSerializeBufferRequest: (
|
||||
callback: (data: {
|
||||
requestId: string
|
||||
ptyId: string
|
||||
opts?: { scrollbackRows?: number; altScreenForcesZeroRows?: boolean }
|
||||
}) => void
|
||||
) => () => void
|
||||
onClearBufferRequest: (callback: (data: { ptyId: string }) => void) => () => void
|
||||
sendSerializedBuffer: (
|
||||
requestId: string,
|
||||
snapshot: {
|
||||
data: string
|
||||
cols: number
|
||||
rows: number
|
||||
seq?: number
|
||||
lastTitle?: string
|
||||
kittyKeyboardFlags?: number
|
||||
} | null
|
||||
) => void
|
||||
declarePendingPaneSerializer: (paneKey: string) => Promise<number>
|
||||
settlePaneSerializer: (paneKey: string, gen: number) => Promise<void>
|
||||
clearPendingPaneSerializer: (paneKey: string, gen: number) => Promise<void>
|
||||
reportRendererSerializerReady?: (ptyId: string) => Promise<void>
|
||||
management: PtyManagementApi
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Mirror of daemon's `DaemonSessionInfo` (src/main/daemon/types.ts); not imported — preload can't depend on main-only protocol types.
|
||||
export type PtyManagementSession = {
|
||||
sessionId: string
|
||||
state: 'created' | 'spawning' | 'running' | 'exiting' | 'exited'
|
||||
shellState: 'pending' | 'ready' | 'timed_out' | 'unsupported'
|
||||
isAlive: boolean
|
||||
pid: number | null
|
||||
cwd: string | null
|
||||
cols: number
|
||||
rows: number
|
||||
createdAt: number
|
||||
protocolVersion: number
|
||||
}
|
||||
|
||||
// 'severed': macOS can no longer attribute daemon terminals to Orca, so Accessibility/
|
||||
// Automation grants silently stop applying until the daemon is restarted (STA-3491).
|
||||
export type PtyManagementMacTccAttributionHealth = 'intact' | 'severed' | 'unknown'
|
||||
|
||||
export type PtyManagementApi = {
|
||||
// `degraded`: daemon is alive but can't spawn fresh PTYs, so new terminals run locally without daemon persistence.
|
||||
listSessions: () => Promise<{ sessions: PtyManagementSession[]; degraded: boolean }>
|
||||
killAll: () => Promise<{
|
||||
killedCount: number
|
||||
remainingCount: number
|
||||
killedSessionIds?: string[]
|
||||
}>
|
||||
killOne: (args: { sessionId: string }) => Promise<{ success: boolean }>
|
||||
restart: () => Promise<{ success: boolean }>
|
||||
macTccAttribution: () => Promise<{ health: PtyManagementMacTccAttributionHealth }>
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type {
|
||||
HostRepoCatalogSnapshot,
|
||||
ListReposForExecutionHostArgs
|
||||
} from '../../shared/host-repo-catalog-contract'
|
||||
import type {
|
||||
BaseRefDefaultResult,
|
||||
BaseRefSearchResult,
|
||||
NestedRepoScanResult,
|
||||
Project,
|
||||
ProjectGroup,
|
||||
ProjectGroupImportMode,
|
||||
ProjectGroupImportResult,
|
||||
ProjectHostSetup,
|
||||
ProjectHostSetupCreateArgs,
|
||||
ProjectHostSetupCreateResult,
|
||||
ProjectHostSetupDeleteArgs,
|
||||
ProjectHostSetupDeleteResult,
|
||||
ProjectHostSetupExistingFolderArgs,
|
||||
ProjectHostSetupResult,
|
||||
ProjectHostSetupUpdateArgs,
|
||||
ProjectHostSetupUpdateResult,
|
||||
ProjectUpdateArgs,
|
||||
Repo
|
||||
} from '../../shared/types'
|
||||
|
||||
export type RepositoryApi = {
|
||||
list: () => Promise<Repo[]>
|
||||
listForExecutionHost?: (args: ListReposForExecutionHostArgs) => Promise<HostRepoCatalogSnapshot>
|
||||
// Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`.
|
||||
add: (args: {
|
||||
path: string
|
||||
kind?: 'git' | 'folder'
|
||||
}) => Promise<{ repo: Repo } | { error: string }>
|
||||
remove: (args: { repoId: string }) => Promise<void>
|
||||
// Forget a project on one execution host only, leaving the same repo id on other hosts intact.
|
||||
removeForHost: (args: { repoId: string; hostId: string }) => Promise<void>
|
||||
reorder: (args: { orderedIds: string[] }) => Promise<{ status: 'applied' | 'rejected' }>
|
||||
reorderForHost: (args: {
|
||||
orderedIds: string[]
|
||||
hostId: string
|
||||
}) => Promise<{ status: 'applied' | 'rejected' }>
|
||||
update: (args: {
|
||||
repoId: string
|
||||
hostId?: ExecutionHostId
|
||||
updates: Partial<
|
||||
Pick<
|
||||
Repo,
|
||||
| 'displayName'
|
||||
| 'badgeColor'
|
||||
| 'repoIcon'
|
||||
| 'upstream'
|
||||
| 'hookSettings'
|
||||
| 'worktreeBaseRef'
|
||||
| 'worktreeBasePath'
|
||||
| 'kind'
|
||||
| 'issueSourcePreference'
|
||||
| 'externalWorktreeVisibility'
|
||||
| 'externalWorktreeVisibilityPromptDismissedAt'
|
||||
| 'externalWorktreeInboxBaselinePaths'
|
||||
| 'importedExternalWorktreePaths'
|
||||
| 'agentWorktreeVisibility'
|
||||
| 'customWorktreeVisibilitySources'
|
||||
| 'worktreeVisibilitySourcePreferences'
|
||||
| 'projectGroupId'
|
||||
| 'projectGroupOrder'
|
||||
| 'forkSyncMode'
|
||||
>
|
||||
> & {
|
||||
sourceControlAi?: Repo['sourceControlAi'] | null
|
||||
externalWorktreeDiscoverySuppressedAt?: Repo['externalWorktreeDiscoverySuppressedAt'] | null
|
||||
}
|
||||
}) => Promise<Repo>
|
||||
pickFolder: () => Promise<string | null>
|
||||
pickFolders: () => Promise<string[]>
|
||||
pickDirectory: () => Promise<string | null>
|
||||
clone: (args: { url: string; destination: string }) => Promise<Repo>
|
||||
cloneRemote: (args: { connectionId: string; url: string; destination: string }) => Promise<Repo>
|
||||
createRemote: (args: {
|
||||
connectionId: string
|
||||
parentPath: string
|
||||
name: string
|
||||
kind: 'git' | 'folder'
|
||||
}) => Promise<{ repo: Repo } | { error: string }>
|
||||
cloneAbort: () => Promise<void>
|
||||
// Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`.
|
||||
addRemote: (args: {
|
||||
connectionId: string
|
||||
remotePath: string
|
||||
displayName?: string
|
||||
kind?: 'git' | 'folder'
|
||||
}) => Promise<{ repo: Repo } | { error: string }>
|
||||
// Why: error union matches the IPC handler's return shape; renderer callers branch on `'error' in result`.
|
||||
create: (args: {
|
||||
parentPath: string
|
||||
name: string
|
||||
kind: 'git' | 'folder'
|
||||
}) => Promise<{ repo: Repo } | { error: string }>
|
||||
isGitAvailable: () => Promise<boolean>
|
||||
getDefaultCreateProjectParent: () => Promise<string>
|
||||
onCloneProgress: (callback: (data: { phase: string; percent: number }) => void) => () => void
|
||||
getGitUsername: (args: { repoId: string }) => Promise<string>
|
||||
getBaseRefDefault: (args: {
|
||||
repoId: string
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<BaseRefDefaultResult>
|
||||
searchBaseRefs: (args: {
|
||||
repoId: string
|
||||
query: string
|
||||
limit?: number
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<string[]>
|
||||
searchBaseRefDetails: (args: {
|
||||
repoId: string
|
||||
query: string
|
||||
limit?: number
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<BaseRefSearchResult[]>
|
||||
onChanged: (callback: () => void) => () => void
|
||||
}
|
||||
|
||||
export type ProjectsApi = {
|
||||
list: () => Promise<Project[]>
|
||||
update: (args: ProjectUpdateArgs) => Promise<Project | null>
|
||||
listHostSetups: () => Promise<ProjectHostSetup[]>
|
||||
createHostSetup: (args: ProjectHostSetupCreateArgs) => Promise<ProjectHostSetupCreateResult>
|
||||
setupExistingFolder: (args: ProjectHostSetupExistingFolderArgs) => Promise<ProjectHostSetupResult>
|
||||
updateHostSetup: (args: ProjectHostSetupUpdateArgs) => Promise<ProjectHostSetupUpdateResult>
|
||||
deleteHostSetup: (args: ProjectHostSetupDeleteArgs) => Promise<ProjectHostSetupDeleteResult>
|
||||
}
|
||||
|
||||
export type ProjectGroupsApi = {
|
||||
list: () => Promise<ProjectGroup[]>
|
||||
create: (args: {
|
||||
name: string
|
||||
parentPath?: string | null
|
||||
connectionId?: string | null
|
||||
parentGroupId?: string | null
|
||||
createdFrom?: ProjectGroup['createdFrom']
|
||||
}) => Promise<ProjectGroup>
|
||||
update: (args: {
|
||||
groupId: string
|
||||
updates: Partial<Pick<ProjectGroup, 'name' | 'isCollapsed' | 'tabOrder' | 'color'>>
|
||||
}) => Promise<ProjectGroup | null>
|
||||
delete: (args: { groupId: string }) => Promise<boolean>
|
||||
moveProject: (args: {
|
||||
projectId: string
|
||||
groupId: string | null
|
||||
order?: number
|
||||
}) => Promise<Repo | null>
|
||||
scanNested: (args: {
|
||||
path: string
|
||||
connectionId?: string
|
||||
scanId?: string
|
||||
options?: Record<string, unknown>
|
||||
}) => Promise<NestedRepoScanResult>
|
||||
cancelNestedScan: (args: { scanId: string }) => Promise<boolean>
|
||||
onNestedScanProgress: (
|
||||
callback: (data: { scanId: string; scan: NestedRepoScanResult }) => void
|
||||
) => () => void
|
||||
importNested: (args: {
|
||||
parentPath: string
|
||||
groupName: string
|
||||
projectPaths: string[]
|
||||
connectionId?: string
|
||||
scanId?: string
|
||||
mode: ProjectGroupImportMode
|
||||
}) => Promise<ProjectGroupImportResult>
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import type {
|
||||
RuntimeBrowserDriverState,
|
||||
RuntimeStatus,
|
||||
RuntimeSyncWindowGraph,
|
||||
RuntimeSyncWindowGraphResult,
|
||||
RuntimeTerminalDriverState
|
||||
} from '../../shared/runtime-types'
|
||||
import type { RuntimeRpcResponse } from '../../shared/runtime-rpc-envelope'
|
||||
import type { PublicKnownRuntimeEnvironment } from '../../shared/runtime-environments'
|
||||
import type { VerifyAndAddRuntimeEnvironmentResult } from '../../shared/remote-pairing-verification'
|
||||
|
||||
export type RuntimeEnvironmentSubscriptionHandle = {
|
||||
unsubscribe: () => void
|
||||
sendBinary: (bytes: Uint8Array<ArrayBufferLike>) => void
|
||||
}
|
||||
|
||||
export type RuntimeApi = {
|
||||
runtime: {
|
||||
syncWindowGraph: (graph: RuntimeSyncWindowGraph) => Promise<RuntimeSyncWindowGraphResult>
|
||||
getStatus: () => Promise<RuntimeStatus>
|
||||
call: (args: { method: string; params?: unknown }) => Promise<RuntimeRpcResponse<unknown>>
|
||||
getTerminalFitOverrides: () => Promise<
|
||||
{ ptyId: string; mode: 'mobile-fit' | 'remote-desktop-fit'; cols: number; rows: number }[]
|
||||
>
|
||||
getTerminalDrivers: () => Promise<
|
||||
{
|
||||
ptyId: string
|
||||
driver: RuntimeTerminalDriverState
|
||||
}[]
|
||||
>
|
||||
getBrowserDrivers: () => Promise<
|
||||
{
|
||||
browserPageId: string
|
||||
driver: RuntimeBrowserDriverState
|
||||
}[]
|
||||
>
|
||||
restoreTerminalFit: (ptyId: string) => Promise<{ restored: boolean }>
|
||||
reclaimBrowserForDesktop: (browserPageId: string) => Promise<{ reclaimed: boolean }>
|
||||
onTerminalFitOverrideChanged: (
|
||||
callback: (event: {
|
||||
ptyId: string
|
||||
mode: 'mobile-fit' | 'remote-desktop-fit' | 'desktop-fit'
|
||||
cols: number
|
||||
rows: number
|
||||
}) => void
|
||||
) => () => void
|
||||
onTerminalDriverChanged: (
|
||||
callback: (event: { ptyId: string; driver: RuntimeTerminalDriverState }) => void
|
||||
) => () => void
|
||||
onNativeChatLaunchDraftResolved?: (
|
||||
callback: (event: { tabId: string; text: string; createdAt: number }) => void
|
||||
) => () => void
|
||||
onBrowserDriverChanged: (
|
||||
callback: (event: { browserPageId: string; driver: RuntimeBrowserDriverState }) => void
|
||||
) => () => void
|
||||
}
|
||||
runtimeEnvironments: {
|
||||
list: () => Promise<PublicKnownRuntimeEnvironment[]>
|
||||
addFromPairingCode: (args: {
|
||||
name: string
|
||||
pairingCode: string
|
||||
}) => Promise<{ environment: PublicKnownRuntimeEnvironment }>
|
||||
verifyAndAddFromPairingCode: (args: {
|
||||
name: string
|
||||
pairingCode: string
|
||||
allowLoopback?: boolean
|
||||
}) => Promise<VerifyAndAddRuntimeEnvironmentResult>
|
||||
resolve: (args: { selector: string }) => Promise<PublicKnownRuntimeEnvironment>
|
||||
remove: (args: { selector: string }) => Promise<{ removed: PublicKnownRuntimeEnvironment }>
|
||||
disconnect: (args: {
|
||||
selector: string
|
||||
}) => Promise<{ disconnected: PublicKnownRuntimeEnvironment }>
|
||||
connect: (args: {
|
||||
selector: string
|
||||
timeoutMs?: number
|
||||
}) => Promise<RuntimeRpcResponse<RuntimeStatus>>
|
||||
getStatus: (args: {
|
||||
selector: string
|
||||
timeoutMs?: number
|
||||
}) => Promise<RuntimeRpcResponse<RuntimeStatus>>
|
||||
// Why: system resume / browser online advance pending shared-control reconnect timers only.
|
||||
retryConnectionsNow?: () => Promise<void>
|
||||
call: (args: {
|
||||
selector: string
|
||||
method: string
|
||||
params?: unknown
|
||||
timeoutMs?: number
|
||||
expectedEnvironmentPairingRevision?: number
|
||||
}) => Promise<RuntimeRpcResponse<unknown>>
|
||||
subscribe: (
|
||||
args: {
|
||||
selector: string
|
||||
method: string
|
||||
params?: unknown
|
||||
timeoutMs?: number
|
||||
expectedEnvironmentPairingRevision?: number
|
||||
},
|
||||
callbacks: {
|
||||
onResponse: (response: RuntimeRpcResponse<unknown>) => void
|
||||
onBinary?: (bytes: Uint8Array<ArrayBufferLike>) => void
|
||||
onError?: (error: { code: string; message: string }) => void
|
||||
onClose?: () => void
|
||||
}
|
||||
) => Promise<RuntimeEnvironmentSubscriptionHandle>
|
||||
}
|
||||
wsl: {
|
||||
isAvailable: () => Promise<boolean>
|
||||
listDistros: () => Promise<string[]>
|
||||
}
|
||||
pwsh: {
|
||||
isAvailable: () => Promise<boolean>
|
||||
}
|
||||
gitBash: {
|
||||
isAvailable: () => Promise<boolean>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { KeybindingActionId, KeybindingFileSnapshot } from '../../shared/keybindings'
|
||||
import type {
|
||||
WarpThemeImportPreview,
|
||||
WarpThemeImportSource
|
||||
} from '../../shared/terminal-custom-themes'
|
||||
import type { GhosttyImportPreview, GlobalSettings } from '../../shared/types'
|
||||
|
||||
export type SettingsApi = {
|
||||
get: () => Promise<GlobalSettings>
|
||||
/** Synchronous persisted-settings read for startup decisions that can't wait for async hydration. Blocking IPC — call sparingly. */
|
||||
getSync: () => GlobalSettings | null
|
||||
set: (args: Partial<GlobalSettings>) => Promise<GlobalSettings>
|
||||
setActiveRuntimeEnvironmentPreference: (args: {
|
||||
environmentId: string | null
|
||||
}) => Promise<GlobalSettings>
|
||||
updatePRBotAuthorOverride: (args: { author: string; isBot: boolean }) => Promise<GlobalSettings>
|
||||
listFonts: () => Promise<string[]>
|
||||
previewGhosttyImport: () => Promise<GhosttyImportPreview>
|
||||
previewWarpThemeImport: (source: WarpThemeImportSource) => Promise<WarpThemeImportPreview>
|
||||
/** Subscribe to out-of-band settings updates (e.g. View > Appearance toggles) to stay in sync with main. */
|
||||
onChanged: (callback: (updates: Partial<GlobalSettings>) => void) => () => void
|
||||
}
|
||||
|
||||
export type KeybindingsApi = {
|
||||
get: () => Promise<KeybindingFileSnapshot>
|
||||
ensureFile: () => Promise<KeybindingFileSnapshot>
|
||||
setAction: (args: {
|
||||
actionId: KeybindingActionId
|
||||
bindings: string[] | null
|
||||
}) => Promise<KeybindingFileSnapshot>
|
||||
reload: () => Promise<KeybindingFileSnapshot>
|
||||
openFile: () => Promise<KeybindingFileSnapshot>
|
||||
revealFile: () => Promise<KeybindingFileSnapshot>
|
||||
onChanged: (callback: (snapshot: KeybindingFileSnapshot) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type {
|
||||
ShellOpenExternalEditorRequest,
|
||||
ShellOpenExternalEditorResult,
|
||||
ShellOpenLocalPathResult
|
||||
} from '../../shared/shell-open-types'
|
||||
|
||||
export type {
|
||||
ShellOpenExternalEditorRequest,
|
||||
ShellOpenExternalEditorResult,
|
||||
ShellOpenLocalPathResult
|
||||
} from '../../shared/shell-open-types'
|
||||
|
||||
export type ShellApi = {
|
||||
openPath: (path: string) => Promise<void>
|
||||
openInFileManager: (path: string) => Promise<ShellOpenLocalPathResult>
|
||||
openInExternalEditor: (
|
||||
request: ShellOpenExternalEditorRequest
|
||||
) => Promise<ShellOpenExternalEditorResult>
|
||||
openUrl: (url: string) => Promise<void>
|
||||
openFilePath: (path: string) => Promise<boolean>
|
||||
openFileUri: (uri: string) => Promise<void>
|
||||
pathExists: (path: string) => Promise<boolean>
|
||||
pickAttachment: () => Promise<string | null>
|
||||
pickImage: () => Promise<string | null>
|
||||
pickRepoIconImage: () => Promise<{
|
||||
dataUrl: string
|
||||
fileName: string
|
||||
} | null>
|
||||
pickAudio: () => Promise<string | null>
|
||||
pickDirectory: (args: { defaultPath?: string }) => Promise<string | null>
|
||||
copyFile: (args: { srcPath: string; destPath: string }) => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type {
|
||||
SpeechErrorEvent,
|
||||
SpeechLifecycleEvent,
|
||||
SpeechModelManifest,
|
||||
SpeechModelState,
|
||||
SpeechTranscriptEvent
|
||||
} from '../../shared/speech-types'
|
||||
|
||||
export type SpeechApi = {
|
||||
getCatalog: () => Promise<SpeechModelManifest[]>
|
||||
getModelStates: () => Promise<SpeechModelState[]>
|
||||
getOpenAiApiKeyStatus: () => Promise<{ configured: boolean }>
|
||||
saveOpenAiApiKey: (apiKey: string) => Promise<{ configured: boolean }>
|
||||
clearOpenAiApiKey: () => Promise<{ configured: boolean }>
|
||||
downloadModel: (modelId: string) => Promise<void>
|
||||
cancelDownload: (modelId: string) => Promise<void>
|
||||
deleteModel: (modelId: string) => Promise<void>
|
||||
startDictation: (
|
||||
modelId: string,
|
||||
hotwords: string[] | undefined,
|
||||
sessionId: string
|
||||
) => Promise<void>
|
||||
feedAudio: (samples: Float32Array, sampleRate: number, sessionId?: string) => Promise<void>
|
||||
stopDictation: (sessionId?: string) => Promise<void>
|
||||
onPartialTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void
|
||||
onFinalTranscript: (callback: (data: SpeechTranscriptEvent) => void) => () => void
|
||||
onDownloadProgress: (
|
||||
callback: (data: { modelId: string; progress: number }) => void
|
||||
) => () => void
|
||||
onReady: (callback: (data: SpeechLifecycleEvent) => void) => () => void
|
||||
onStopped: (callback: (data: SpeechLifecycleEvent) => void) => () => void
|
||||
onError: (callback: (data: SpeechErrorEvent) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type {
|
||||
EnrichedDetectedPort,
|
||||
PortForwardEntry,
|
||||
SshConfigHostListArgs,
|
||||
SshConfigHostListResult,
|
||||
SshConfigHostResolution,
|
||||
SshConfigImportResult,
|
||||
SshConnectionState,
|
||||
SshTarget,
|
||||
SshTargetAddResult
|
||||
} from '../../shared/ssh-types'
|
||||
import type { FilesystemPathFlavor } from '../../shared/types'
|
||||
|
||||
export type SshApi = {
|
||||
listTargets: () => Promise<SshTarget[]>
|
||||
// Removed-target id → last known label, for a friendly host name on workspaces still pinned to a removed target.
|
||||
listRemovedTargetLabels: () => Promise<Record<string, string>>
|
||||
addTarget: (args: { target: Omit<SshTarget, 'id'> }) => Promise<SshTargetAddResult>
|
||||
updateTarget: (args: {
|
||||
id: string
|
||||
updates: Partial<Omit<SshTarget, 'id'>>
|
||||
}) => Promise<SshTarget>
|
||||
removeTarget: (args: { id: string }) => Promise<void>
|
||||
importConfig: (args?: { reAdopt?: boolean }) => Promise<SshConfigImportResult>
|
||||
listConfigHosts: (args?: SshConfigHostListArgs) => Promise<SshConfigHostListResult>
|
||||
resolveConfigHost: (args: { alias: string }) => Promise<SshConfigHostResolution | null>
|
||||
connect: (args: { targetId: string }) => Promise<SshConnectionState | null>
|
||||
disconnect: (args: { targetId: string }) => Promise<void>
|
||||
terminateSessions: (args: { targetId: string }) => Promise<void>
|
||||
resetRelay: (args: { targetId: string }) => Promise<void>
|
||||
getState: (args: { targetId: string }) => Promise<SshConnectionState | null>
|
||||
needsPassphrasePrompt: (args: { targetId: string }) => Promise<boolean>
|
||||
testConnection: (args: {
|
||||
targetId: string
|
||||
}) => Promise<{ success: boolean; error?: string; state?: SshConnectionState }>
|
||||
onStateChanged: (
|
||||
callback: (data: { targetId: string; state: SshConnectionState }) => void
|
||||
) => () => void
|
||||
addPortForward: (args: {
|
||||
targetId: string
|
||||
localPort: number
|
||||
remoteHost: string
|
||||
remotePort: number
|
||||
label?: string
|
||||
}) => Promise<PortForwardEntry>
|
||||
updatePortForward: (args: {
|
||||
id: string
|
||||
targetId: string
|
||||
localPort: number
|
||||
remoteHost: string
|
||||
remotePort: number
|
||||
label?: string
|
||||
}) => Promise<PortForwardEntry>
|
||||
removePortForward: (args: { id: string }) => Promise<PortForwardEntry | null>
|
||||
listPortForwards: (args?: { targetId?: string }) => Promise<PortForwardEntry[]>
|
||||
listDetectedPorts: (args: { targetId: string }) => Promise<EnrichedDetectedPort[]>
|
||||
onPortForwardsChanged: (
|
||||
callback: (data: { targetId: string; forwards: PortForwardEntry[] }) => void
|
||||
) => () => void
|
||||
onDetectedPortsChanged: (
|
||||
callback: (data: { targetId: string; ports: EnrichedDetectedPort[] }) => void
|
||||
) => () => void
|
||||
browseDir: (args: { targetId: string; dirPath: string }) => Promise<{
|
||||
entries: { name: string; isDirectory: boolean }[]
|
||||
resolvedPath: string
|
||||
pathFlavor: FilesystemPathFlavor
|
||||
}>
|
||||
onCredentialRequest: (
|
||||
callback: (data: {
|
||||
requestId: string
|
||||
targetId: string
|
||||
kind: 'passphrase' | 'password'
|
||||
detail: string
|
||||
}) => void
|
||||
) => () => void
|
||||
onCredentialResolved: (callback: (data: { requestId: string }) => void) => () => void
|
||||
submitCredential: (args: { requestId: string; value: string | null }) => Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { TelemetryConsentState } from '../../shared/telemetry-consent-types'
|
||||
import type { MemorySnapshot, StatsSummary } from '../../shared/types'
|
||||
|
||||
export type StatsApi = {
|
||||
getSummary: () => Promise<StatsSummary>
|
||||
}
|
||||
|
||||
// Diagnostics IPC payloads; mirror the runtime types in `src/main/observability/{index,bundle}.ts`.
|
||||
export type DiagnosticsStatusPayload = {
|
||||
readonly localFileEnabled: boolean
|
||||
readonly bundleEnabled: boolean
|
||||
readonly traceFilePath: string
|
||||
readonly traceFamilySize: number
|
||||
readonly disabledReason?:
|
||||
| 'do_not_track'
|
||||
| 'orca_telemetry_disabled'
|
||||
| 'orca_diagnostics_disabled'
|
||||
| 'ci'
|
||||
}
|
||||
export type DiagnosticsBundlePayload = {
|
||||
readonly bundleSubmissionId: string
|
||||
readonly bytes: number
|
||||
readonly spanCount: number
|
||||
}
|
||||
export type DiagnosticsUploadPayload =
|
||||
| {
|
||||
readonly ticketId: string
|
||||
}
|
||||
| {
|
||||
readonly canceled: true
|
||||
}
|
||||
|
||||
export type MemoryApi = {
|
||||
getSnapshot: () => Promise<MemorySnapshot>
|
||||
}
|
||||
|
||||
export type DiagnosticsApi = {
|
||||
getStatus: () => Promise<DiagnosticsStatusPayload>
|
||||
collectBundle: (lookbackMinutes?: number) => Promise<DiagnosticsBundlePayload>
|
||||
openBundlePreview: (bundleSubmissionId: string) => Promise<void>
|
||||
discardBundlePreview: (bundleSubmissionId: string) => Promise<void>
|
||||
uploadBundle: (bundleSubmissionId: string) => Promise<DiagnosticsUploadPayload>
|
||||
deleteBundle: (ticketId: string) => Promise<void>
|
||||
}
|
||||
|
||||
export type TelemetryApi = {
|
||||
/** Fire-and-forget track. Loose IPC typing on purpose — the main-side validator enforces;
|
||||
* renderer sites should import `track<N>()` from lib/telemetry.ts, not reach here. */
|
||||
telemetryTrack: (name: string, props: Record<string, unknown>) => Promise<void>
|
||||
/** Flip the persisted opt-in preference. Subject to a per-session
|
||||
* consent-mutation rate limit on the main side (≤5/session). */
|
||||
telemetrySetOptIn: (optedIn: boolean) => Promise<void>
|
||||
/** Diagnostic file controls (telemetry-error-tracking.md §User controls). Main does the FS/network
|
||||
* work and retains upload payloads so the renderer can't read or substitute arbitrary bytes. */
|
||||
diagnostics: DiagnosticsApi
|
||||
/** Read-only effective consent state (+ reason if disabled) — env vars are main-side state the renderer can't read directly. */
|
||||
telemetryGetConsentState: () => Promise<TelemetryConsentState>
|
||||
/** Banner ✕ — persist `optedIn = true` silently. Separate channel from `telemetrySetOptIn`,
|
||||
* whose `via` derivation would wrongly fire `telemetry_opted_in`. Same per-session rate limit. */
|
||||
telemetryAcknowledgeBanner: () => Promise<void>
|
||||
stats: StatsApi
|
||||
memory: MemoryApi
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import type {
|
||||
PersistedUIState,
|
||||
TuiAgent,
|
||||
WorktreeDefaultTabsLaunch,
|
||||
WorktreeSetupLaunch,
|
||||
WorktreeStartupLaunch
|
||||
} from '../../shared/types'
|
||||
import type { FeatureInteractionId } from '../../shared/feature-interactions'
|
||||
import type { KeybindingActionId } from '../../shared/keybindings'
|
||||
import type { BrowserFindSource } from '../../shared/browser-find-source'
|
||||
import type {
|
||||
AgentProviderSessionMetadata,
|
||||
SleepingAgentLaunchConfig
|
||||
} from '../../shared/agent-session-resume'
|
||||
import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry'
|
||||
import type {
|
||||
RuntimeMobileSessionTabMove,
|
||||
RuntimeTerminalCreateRequestPayload,
|
||||
RuntimeTerminalPresentation
|
||||
} from '../../shared/runtime-types'
|
||||
import type {
|
||||
RuntimeMobileMarkdownRequest,
|
||||
RuntimeMobileMarkdownResponse
|
||||
} from '../../shared/mobile-markdown-document'
|
||||
import type { TerminalTabCreateReply } from '../../shared/terminal-reveal-identity'
|
||||
import type {
|
||||
TerminalTabCloseRequest,
|
||||
TerminalTabCloseResponse
|
||||
} from '../../shared/terminal-tab-close'
|
||||
|
||||
export type UiCommandEventApi = {
|
||||
get: () => Promise<PersistedUIState>
|
||||
set: (args: Partial<PersistedUIState>) => Promise<void>
|
||||
recordFeatureInteraction: (id: FeatureInteractionId) => Promise<PersistedUIState>
|
||||
onStateChanged: (callback: (ui: PersistedUIState) => void) => () => void
|
||||
onOpenSettings: (callback: () => void) => () => void
|
||||
/** Consumes a one-shot tray/menu-bar "open settings" intent queued before mount. */
|
||||
consumePendingOpenSettings: () => Promise<boolean>
|
||||
onOpenSetupGuide: (callback: () => void) => () => void
|
||||
onOpenFeatureTour: (callback: () => void) => () => void
|
||||
onOpenCrashReport: (callback: () => void) => () => void
|
||||
onToggleLeftSidebar: (callback: () => void) => () => void
|
||||
onToggleRightSidebar: (callback: () => void) => () => void
|
||||
onToggleWorktreePalette: (callback: () => void) => () => void
|
||||
onToggleFloatingTerminal: (callback: () => void) => () => void
|
||||
onTerminalShortcutCaptured: (
|
||||
callback: (data: { actionId: KeybindingActionId }) => void
|
||||
) => () => void
|
||||
onOpenQuickOpen: (callback: () => void) => () => void
|
||||
onToggleQuickCommandsMenu: (callback: () => void) => () => void
|
||||
onOpenNewWorkspace: (callback: () => void) => () => void
|
||||
onDeleteCurrentWorkspace: (callback: () => void) => () => void
|
||||
onOpenWorkspaceBoard: (callback: () => void) => () => void
|
||||
onOpenTasks: (callback: () => void) => () => void
|
||||
onJumpToWorktreeIndex: (callback: (index: number) => void) => () => void
|
||||
onJumpToTabIndex: (callback: (index: number) => void) => () => void
|
||||
onWorktreeHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void
|
||||
onNewBrowserTab: (callback: () => void) => () => void
|
||||
onNewMarkdownTab: (callback: () => void) => () => void
|
||||
onNewSimulatorTab: (callback: () => void) => () => void
|
||||
onRequestTabCreate: (
|
||||
callback: (data: {
|
||||
requestId: string
|
||||
url: string
|
||||
worktreeId?: string
|
||||
sessionProfileId?: string | null
|
||||
sessionPartition?: string
|
||||
activate?: boolean
|
||||
}) => void
|
||||
) => () => void
|
||||
replyTabCreate: (reply: { requestId: string; browserPageId?: string; error?: string }) => void
|
||||
onRequestTabSetProfile: (
|
||||
callback: (data: {
|
||||
requestId: string
|
||||
browserPageId: string
|
||||
profileId: string
|
||||
sessionPartition?: string
|
||||
}) => void
|
||||
) => () => void
|
||||
replyTabSetProfile: (reply: { requestId: string; error?: string }) => void
|
||||
onRequestTabClose: (
|
||||
callback: (data: { requestId: string; tabId: string | null; worktreeId?: string }) => void
|
||||
) => () => void
|
||||
replyTabClose: (reply: { requestId: string; error?: string }) => void
|
||||
onNewTerminalTab: (callback: () => void) => () => void
|
||||
onFocusBrowserAddressBar: (callback: () => void) => () => void
|
||||
onFindInBrowserPage: (source: BrowserFindSource, callback: () => void) => () => void
|
||||
onReloadBrowserPage: (callback: () => void) => () => void
|
||||
onBrowserHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void
|
||||
onZoomBrowserPage: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void
|
||||
onHardReloadBrowserPage: (callback: () => void) => () => void
|
||||
onCloseActiveTab: (callback: () => void) => () => void
|
||||
onCloseFloatingItem: (callback: (payload: { sourceId: string }) => void) => () => void
|
||||
onSelectFloatingIndex: (callback: (payload: { index: number }) => void) => () => void
|
||||
onSwitchTab: (callback: (direction: 1 | -1) => void) => () => void
|
||||
onSwitchTabAcrossAllTypes: (callback: (direction: 1 | -1) => void) => () => void
|
||||
onSwitchRecentTab: (callback: () => void) => () => void
|
||||
onSwitchTerminalTab: (callback: (direction: 1 | -1) => void) => () => void
|
||||
onCtrlTabKeyDown: (callback: (data: { shiftKey: boolean }) => void) => () => void
|
||||
onCtrlTabKeyUp: (callback: () => void) => () => void
|
||||
onToggleStatusBar: (callback: () => void) => () => void
|
||||
onDictationKeyDown: (callback: () => void) => () => void
|
||||
onExportPdfRequested: (callback: () => void) => () => void
|
||||
onAppMenuPaste: (callback: () => void) => () => void
|
||||
onAppMenuSelectionAction: (callback: (action: 'copy' | 'select-all') => void) => () => void
|
||||
onEditableContextPaste: (callback: (data: { plainTextOnly: boolean }) => void) => () => void
|
||||
onActivateWorktree: (
|
||||
callback: (data: {
|
||||
repoId: string
|
||||
worktreeId: string
|
||||
setup?: WorktreeSetupLaunch
|
||||
startup?: WorktreeStartupLaunch
|
||||
defaultTabs?: WorktreeDefaultTabsLaunch
|
||||
}) => void
|
||||
) => () => void
|
||||
onCreateTerminal: (
|
||||
callback: (data: {
|
||||
requestId?: string
|
||||
worktreeId: string
|
||||
command?: string
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
resumeProviderSession?: AgentProviderSessionMetadata
|
||||
launchToken?: string
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
title?: string
|
||||
ptyId?: string
|
||||
activate?: boolean
|
||||
focus?: boolean
|
||||
presentation?: RuntimeTerminalPresentation
|
||||
surfaceOwner?: false
|
||||
tabId?: string
|
||||
leafId?: string
|
||||
splitFromLeafId?: string
|
||||
splitDirection?: 'horizontal' | 'vertical'
|
||||
splitTelemetrySource?: TerminalPaneSplitSource
|
||||
}) => void
|
||||
) => () => void
|
||||
onRequestTerminalCreate: (
|
||||
callback: (data: RuntimeTerminalCreateRequestPayload) => void
|
||||
) => () => void
|
||||
onRequestTerminalTabMount: (
|
||||
callback: (data: { worktreeId: string; tabId?: string; ptyId?: string }) => void
|
||||
) => () => void
|
||||
replyTerminalCreate: (reply: TerminalTabCreateReply) => void
|
||||
onSplitTerminal: (
|
||||
callback: (data: {
|
||||
tabId: string
|
||||
paneRuntimeId: number
|
||||
direction: 'horizontal' | 'vertical'
|
||||
command?: string
|
||||
telemetrySource?: TerminalPaneSplitSource
|
||||
}) => void
|
||||
) => () => void
|
||||
onRenameTerminal: (
|
||||
callback: (data: { tabId: string; title: string | null }) => void
|
||||
) => () => void
|
||||
onFocusTerminal: (
|
||||
callback: (data: {
|
||||
tabId: string
|
||||
worktreeId: string
|
||||
leafId?: string | null
|
||||
ackPaneKeyOnSuccess?: string
|
||||
flashFocusedPane?: boolean
|
||||
scrollToBottomIfOutputSinceLastView?: boolean
|
||||
}) => void
|
||||
) => () => void
|
||||
onFocusEditorTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void
|
||||
onCloseSessionTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void
|
||||
onMoveSessionTab: (
|
||||
callback: (data: { worktreeId: string } & RuntimeMobileSessionTabMove) => void
|
||||
) => () => void
|
||||
onOpenFileFromMobile: (
|
||||
callback: (data: {
|
||||
worktreeId: string
|
||||
filePath: string
|
||||
relativePath: string
|
||||
runtimeEnvironmentId?: string
|
||||
}) => void
|
||||
) => () => void
|
||||
onOpenDiffFromMobile: (
|
||||
callback: (data: {
|
||||
worktreeId: string
|
||||
filePath: string
|
||||
relativePath: string
|
||||
staged: boolean
|
||||
runtimeEnvironmentId?: string
|
||||
}) => void
|
||||
) => () => void
|
||||
onMobileMarkdownRequest: (callback: (request: RuntimeMobileMarkdownRequest) => void) => () => void
|
||||
respondMobileMarkdownRequest: (response: RuntimeMobileMarkdownResponse) => void
|
||||
onCloseTerminal: (
|
||||
callback: (data: { tabId: string; paneRuntimeId?: number }) => void
|
||||
) => () => void
|
||||
onTerminalTabCloseRequest: (callback: (request: TerminalTabCloseRequest) => void) => () => void
|
||||
respondTerminalTabClose: (response: TerminalTabCloseResponse) => void
|
||||
onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void
|
||||
onResumeSleepingAgents: (callback: (data: { worktreeId: string }) => void) => () => void
|
||||
onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void
|
||||
onSystemResumed: (callback: () => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { ReadClipboardTextOptions } from '../../shared/clipboard-text'
|
||||
import type { NativeFileDropPayload } from '../../shared/native-file-drop'
|
||||
import type {
|
||||
RichMarkdownContextMenuCommandPayload,
|
||||
RichMarkdownContextMenuTableTarget
|
||||
} from '../../shared/rich-markdown-context-menu'
|
||||
|
||||
export type UiWindowApi = {
|
||||
readClipboardText: (options?: ReadClipboardTextOptions) => Promise<string>
|
||||
readSelectionClipboardText: (options?: ReadClipboardTextOptions) => Promise<string>
|
||||
saveClipboardImageAsTempFile: (args?: {
|
||||
connectionId?: string | null
|
||||
runtimeEnvironmentId?: string | null
|
||||
}) => Promise<string | null>
|
||||
writeClipboardText: (text: string) => Promise<void>
|
||||
writeTerminalClipboardText: (text: string) => Promise<void>
|
||||
writeSelectionClipboardText: (text: string) => Promise<void>
|
||||
writeClipboardImage: (dataUrl: string) => Promise<void>
|
||||
performNativePaste: (options?: { mode?: 'paste' | 'paste-and-match-style' }) => void
|
||||
performNativeSelectionAction: (action: 'copy' | 'select-all') => void
|
||||
writeClipboardFile: (
|
||||
args:
|
||||
| {
|
||||
filePath: string
|
||||
connectionId?: string | null
|
||||
}
|
||||
| string
|
||||
) => Promise<{ ok: boolean; reason?: string }>
|
||||
onFileDrop: (callback: (data: NativeFileDropPayload) => void) => () => void
|
||||
getZoomLevel: () => number
|
||||
setZoomLevel: (level: number) => void
|
||||
syncTrafficLights: (zoomFactor: number) => void
|
||||
setMarkdownEditorFocused: (focused: boolean) => void
|
||||
setRichMarkdownContextMenuTarget: (target: RichMarkdownContextMenuTableTarget | null) => void
|
||||
setTerminalInputFocused: (focused: boolean) => void
|
||||
setFloatingFocus: (state: { panelFocused: boolean; terminalFocused: boolean }) => void
|
||||
setShortcutRecorderFocused: (focused: boolean) => void
|
||||
onRichMarkdownContextCommand: (
|
||||
callback: (payload: RichMarkdownContextMenuCommandPayload) => void
|
||||
) => () => void
|
||||
onFullscreenChanged: (callback: (isFullScreen: boolean) => void) => () => void
|
||||
minimize: () => void
|
||||
maximize: () => void
|
||||
isMaximized: () => Promise<boolean>
|
||||
onMaximizeChanged: (callback: (isMaximized: boolean) => void) => () => void
|
||||
requestClose: () => void
|
||||
popupMenu: () => void
|
||||
onWindowCloseRequested: (callback: (data: { isQuitting: boolean }) => void) => () => void
|
||||
confirmWindowClose: () => void
|
||||
notifyWindowRevealed: () => void
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type {
|
||||
LinuxPackageInstallInstructions,
|
||||
ReleaseBuildListResult,
|
||||
UpdateCheckOptions,
|
||||
UpdateStatus
|
||||
} from '../../shared/types'
|
||||
import type { ReleaseChannel } from '../../shared/release-channel'
|
||||
|
||||
export type UpdaterApi = {
|
||||
getVersion: () => Promise<string>
|
||||
getStatus: () => Promise<UpdateStatus>
|
||||
check: (options?: UpdateCheckOptions) => Promise<void>
|
||||
download: () => Promise<void>
|
||||
quitAndInstall: () => Promise<void>
|
||||
dismissNudge: () => Promise<void>
|
||||
dismissAvailableUpdate: () => Promise<void>
|
||||
/** Desktop-only. Rejects unless the current status carries `linux-package-install` recovery. */
|
||||
getLinuxPackageInstallInstructions: () => Promise<LinuxPackageInstallInstructions>
|
||||
/** Desktop-only. Reveals the revalidated cached package in the native file manager. */
|
||||
showLinuxPackage: () => Promise<void>
|
||||
listBuilds: (channel: ReleaseChannel) => Promise<ReleaseBuildListResult>
|
||||
|
||||
onStatus: (callback: (status: UpdateStatus) => void) => () => void
|
||||
onClearDismissal: (callback: () => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type {
|
||||
WorkspaceCleanupDismissArgs,
|
||||
WorkspaceCleanupLocalProcessArgs,
|
||||
WorkspaceCleanupLocalProcessResult,
|
||||
WorkspaceCleanupScanArgs,
|
||||
WorkspaceCleanupScanProgress,
|
||||
WorkspaceCleanupScanResult
|
||||
} from '../../shared/workspace-cleanup'
|
||||
import type {
|
||||
WorkspaceSpaceAnalyzeResult,
|
||||
WorkspaceSpaceScanProgress
|
||||
} from '../../shared/workspace-space-types'
|
||||
|
||||
export type WorkspaceCleanupApi = {
|
||||
scan: (
|
||||
args?: WorkspaceCleanupScanArgs,
|
||||
onProgress?: (progress: WorkspaceCleanupScanProgress) => void
|
||||
) => Promise<WorkspaceCleanupScanResult>
|
||||
dismiss: (args: WorkspaceCleanupDismissArgs) => Promise<void>
|
||||
clearDismissals: () => Promise<void>
|
||||
hasKillableLocalProcesses: (
|
||||
args: WorkspaceCleanupLocalProcessArgs
|
||||
) => Promise<WorkspaceCleanupLocalProcessResult>
|
||||
}
|
||||
|
||||
export type WorkspaceSpaceApi = {
|
||||
analyze: () => Promise<WorkspaceSpaceAnalyzeResult>
|
||||
cancel: () => Promise<boolean>
|
||||
onProgress: (callback: (progress: WorkspaceSpaceScanProgress) => void) => () => void
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type {
|
||||
LocalhostWorktreeLabelResult,
|
||||
LocalhostWorktreeLabelRoute
|
||||
} from '../../shared/localhost-worktree-labels'
|
||||
import type {
|
||||
WorkspacePortAdvertisedUrlChangedEvent,
|
||||
WorkspacePortKillRequest,
|
||||
WorkspacePortKillResult,
|
||||
WorkspacePortScanRequest,
|
||||
WorkspacePortScanResult
|
||||
} from '../../shared/workspace-ports'
|
||||
|
||||
export type WorkspacePortsApi = {
|
||||
scan: (args: WorkspacePortScanRequest) => Promise<WorkspacePortScanResult>
|
||||
kill: (args: WorkspacePortKillRequest) => Promise<WorkspacePortKillResult>
|
||||
onAdvertisedUrlChanged: (
|
||||
callback: (event: WorkspacePortAdvertisedUrlChangedEvent) => void
|
||||
) => () => void
|
||||
}
|
||||
|
||||
export type LocalhostWorktreeLabelsApi = {
|
||||
register: (args: LocalhostWorktreeLabelRoute) => Promise<LocalhostWorktreeLabelResult>
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import type {
|
||||
IssueInfo,
|
||||
PRInfo,
|
||||
WorkspaceSessionPatch,
|
||||
WorkspaceSessionState
|
||||
} from '../../shared/types'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type {
|
||||
RemoteWorkspaceChangedEvent,
|
||||
RemoteWorkspaceConnectedClient,
|
||||
RemoteWorkspacePatchResult,
|
||||
RemoteWorkspaceSnapshot
|
||||
} from '../../shared/remote-workspace-types'
|
||||
|
||||
export type WorkspaceSessionApi = {
|
||||
session: {
|
||||
// hostId defaults to the 'local' partition on main, so omitting it stays backward-compatible.
|
||||
get: (hostId?: ExecutionHostId) => Promise<WorkspaceSessionState>
|
||||
set: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => Promise<void>
|
||||
patch: (args: WorkspaceSessionPatch, hostId?: ExecutionHostId) => Promise<void>
|
||||
flush: () => Promise<void>
|
||||
readTerminalScrollback: (args: { ref: string }) => string | null
|
||||
setSync: (args: WorkspaceSessionState, hostId?: ExecutionHostId) => void
|
||||
}
|
||||
cache: {
|
||||
getGitHub: () => Promise<{
|
||||
pr: Record<string, { data: PRInfo | null; fetchedAt: number }>
|
||||
issue: Record<string, { data: IssueInfo | null; fetchedAt: number }>
|
||||
}>
|
||||
setGitHub: (args: {
|
||||
cache: {
|
||||
pr: Record<string, { data: PRInfo | null; fetchedAt: number }>
|
||||
issue: Record<string, { data: IssueInfo | null; fetchedAt: number }>
|
||||
}
|
||||
}) => Promise<void>
|
||||
}
|
||||
remoteWorkspace: {
|
||||
get: (args: { targetId: string }) => Promise<RemoteWorkspaceSnapshot | null>
|
||||
setForConnectedTargets: (args: {
|
||||
session?: WorkspaceSessionState
|
||||
hydratedTargetIds?: string[]
|
||||
}) => Promise<{ targetId: string; result: RemoteWorkspacePatchResult }[]>
|
||||
listEnabledConnectedTargets: () => Promise<string[]>
|
||||
listConnectedClients: (args?: {
|
||||
targetIds?: string[]
|
||||
}) => Promise<{ targetId: string; clients: RemoteWorkspaceConnectedClient[] }[]>
|
||||
clientId: () => Promise<string>
|
||||
onChanged: (callback: (event: RemoteWorkspaceChangedEvent) => void) => () => void
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
import type {
|
||||
ForgetRemovedWorktreesForExecutionHostArgs,
|
||||
ForgetRemovedWorktreesForExecutionHostResult,
|
||||
HostQualifiedDetectedWorktreeResult,
|
||||
HostQualifiedKnownWorktreeResult,
|
||||
LegacyDetectedWorktreeRequest,
|
||||
ListDetectedWorktreesArgs,
|
||||
ListKnownWorktreesForExecutionHostArgs,
|
||||
ProviderRequestId
|
||||
} from '../../shared/detected-worktree-provider-contract'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type {
|
||||
FolderWorkspacePathStatus,
|
||||
FolderWorkspacePathStatusRequest
|
||||
} from '../../shared/folder-workspace-path-status'
|
||||
import type {
|
||||
HostLineageSnapshot,
|
||||
ListDesktopLineageForHostArgs
|
||||
} from '../../shared/host-lineage-contract'
|
||||
import type {
|
||||
AdoptProvisionedRootArgs,
|
||||
CreateWorktreeArgs,
|
||||
CreateWorktreeResult,
|
||||
DetectedWorktreeListResult,
|
||||
FolderWorkspace,
|
||||
ForceDeleteWorktreeBranchResult,
|
||||
GitHubPrStartPoint,
|
||||
GitPushTarget,
|
||||
RemoveWorktreeResult,
|
||||
SparsePreset,
|
||||
WorkspaceLineage,
|
||||
Worktree,
|
||||
WorktreeBaseStatusEvent,
|
||||
WorktreeHeadIdentity,
|
||||
WorktreeLineage,
|
||||
WorktreeMeta,
|
||||
WorktreeRemoteBranchConflictEvent
|
||||
} from '../../shared/types'
|
||||
|
||||
export type WorktreeApi = {
|
||||
list: (args: { repoId: string }) => Promise<Worktree[]>
|
||||
listDetected: {
|
||||
(
|
||||
args: ListDetectedWorktreesArgs
|
||||
): Promise<HostQualifiedDetectedWorktreeResult | DetectedWorktreeListResult>
|
||||
(args: LegacyDetectedWorktreeRequest): Promise<DetectedWorktreeListResult>
|
||||
}
|
||||
listKnownForExecutionHost?: (
|
||||
args: ListKnownWorktreesForExecutionHostArgs
|
||||
) => Promise<HostQualifiedKnownWorktreeResult>
|
||||
/** Retires the persisted metadata an authoritative scan proved gone, so it stops feeding the read above. */
|
||||
forgetRemovedForExecutionHost?: (
|
||||
args: ForgetRemovedWorktreesForExecutionHostArgs
|
||||
) => Promise<ForgetRemovedWorktreesForExecutionHostResult>
|
||||
cancelListDetected?: (args: { providerRequestId: ProviderRequestId }) => Promise<void>
|
||||
listAll: () => Promise<Worktree[]>
|
||||
create: (args: CreateWorktreeArgs) => Promise<CreateWorktreeResult>
|
||||
adoptProvisionedRoot: (args: AdoptProvisionedRootArgs) => Promise<CreateWorktreeResult>
|
||||
/** Two-phase progress for a background `create`, correlated by `creationId`. The remote/runtime
|
||||
* create path emits nothing, so the surface falls back to an indeterminate spinner. */
|
||||
onCreateProgress: (
|
||||
callback: (data: { creationId?: string; phase: 'fetching' | 'creating' }) => void
|
||||
) => () => void
|
||||
prefetchCreateBase: (args: { repoId: string; baseBranch?: string }) => Promise<void>
|
||||
resolvePrBase: (args: {
|
||||
repoId: string
|
||||
prNumber: number
|
||||
headRefName?: string
|
||||
baseRefName?: string
|
||||
isCrossRepository?: boolean
|
||||
}) => Promise<GitHubPrStartPoint | { error: string }>
|
||||
/** GitLab parallel of resolvePrBase. For same-project MRs returns
|
||||
* `<remote>/<source_branch>`; for fork MRs fetches
|
||||
* refs/merge-requests/<iid>/head and returns the SHA. */
|
||||
resolveMrBase: (args: {
|
||||
repoId: string
|
||||
mrIid: number
|
||||
sourceBranch?: string
|
||||
targetBranch?: string
|
||||
isCrossRepository?: boolean
|
||||
}) => Promise<
|
||||
{ baseBranch: string; compareBaseRef?: string; pushTarget?: GitPushTarget } | { error: string }
|
||||
>
|
||||
remove: (args: {
|
||||
worktreeId: string
|
||||
hostId?: ExecutionHostId
|
||||
force?: boolean
|
||||
// Why (#11960): distinct from `force`, which the plain Delete confirmation
|
||||
// already sets to skip the dirty-file prompt. Only an explicit Force Delete
|
||||
// may waive the proof that every PTY stopped.
|
||||
allowUnverifiedPtyStop?: boolean
|
||||
skipArchive?: boolean
|
||||
}) => Promise<RemoveWorktreeResult>
|
||||
// Forget a workspace from Orca only (no remote Git/FS work) — for workspaces pinned to a removed/disconnected SSH host.
|
||||
forgetLocal: (args: {
|
||||
worktreeId: string
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<RemoveWorktreeResult>
|
||||
forceDeletePreservedBranch: (args: {
|
||||
worktreeId: string
|
||||
branchName: string
|
||||
expectedHead: string
|
||||
hostId?: ExecutionHostId
|
||||
}) => Promise<ForceDeleteWorktreeBranchResult>
|
||||
updateMeta: (args: { worktreeId: string; updates: Partial<WorktreeMeta> }) => Promise<Worktree>
|
||||
listLineage: () => Promise<{
|
||||
lineage: Record<string, WorktreeLineage>
|
||||
workspaceLineage?: Record<string, WorkspaceLineage>
|
||||
}>
|
||||
listLineageForHost?: (args: ListDesktopLineageForHostArgs) => Promise<HostLineageSnapshot>
|
||||
updateLineage: (args: {
|
||||
worktreeId: string
|
||||
parentWorktreeId?: string
|
||||
noParent?: boolean
|
||||
}) => Promise<WorktreeLineage | null>
|
||||
persistSortOrder: (args: { orderedIds: string[] }) => Promise<void>
|
||||
/** Full CLI output of the last branch auto-rename generation failure, held
|
||||
* in main memory only — null after a restart or once the failure clears. */
|
||||
getBranchRenameFailureOutput: (args: { worktreeId: string }) => Promise<string | null>
|
||||
onChanged: (callback: (data: { repoId: string }) => void) => () => void
|
||||
onGitStatusMetadataChanged: (callback: (data: { repoId: string }) => void) => () => void
|
||||
onHeadIdentitiesChanged: (
|
||||
callback: (data: { repoId: string; identities: WorktreeHeadIdentity[] }) => void
|
||||
) => () => void
|
||||
onBaseStatus: (callback: (data: WorktreeBaseStatusEvent) => void) => () => void
|
||||
onRemoteBranchConflict: (
|
||||
callback: (data: WorktreeRemoteBranchConflictEvent) => void
|
||||
) => () => void
|
||||
}
|
||||
|
||||
export type FolderWorkspacesApi = {
|
||||
list: () => Promise<FolderWorkspace[]>
|
||||
getPathStatus: (args: FolderWorkspacePathStatusRequest) => Promise<FolderWorkspacePathStatus>
|
||||
create: (args: {
|
||||
projectGroupId: string
|
||||
name?: string
|
||||
folderPath?: string | null
|
||||
connectionId?: string | null
|
||||
linkedTask?: FolderWorkspace['linkedTask']
|
||||
createdWithAgent?: FolderWorkspace['createdWithAgent']
|
||||
pendingFirstAgentMessageRename?: boolean
|
||||
}) => Promise<FolderWorkspace>
|
||||
update: (args: {
|
||||
folderWorkspaceId: string
|
||||
updates: Partial<
|
||||
Pick<
|
||||
FolderWorkspace,
|
||||
| 'name'
|
||||
| 'folderPath'
|
||||
| 'linkedTask'
|
||||
| 'comment'
|
||||
| 'isArchived'
|
||||
| 'isUnread'
|
||||
| 'isPinned'
|
||||
| 'sortOrder'
|
||||
| 'manualOrder'
|
||||
| 'workspaceStatus'
|
||||
| 'createdWithAgent'
|
||||
| 'pendingFirstAgentMessageRename'
|
||||
| 'firstAgentMessageRenameError'
|
||||
| 'lastActivityAt'
|
||||
| 'diffComments'
|
||||
>
|
||||
>
|
||||
}) => Promise<FolderWorkspace | null>
|
||||
delete: (args: { folderWorkspaceId: string }) => Promise<boolean>
|
||||
}
|
||||
|
||||
export type SparsePresetsApi = {
|
||||
list: (args: { repoId: string }) => Promise<SparsePreset[]>
|
||||
save: (args: {
|
||||
repoId: string
|
||||
id?: string
|
||||
name: string
|
||||
directories: string[]
|
||||
}) => Promise<SparsePreset>
|
||||
remove: (args: { repoId: string; presetId: string }) => Promise<void>
|
||||
onChanged: (callback: (data: { repoId: string }) => void) => () => void
|
||||
}
|
||||
Reference in New Issue
Block a user