mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
perf(rpc): stop the dispatcher importing every RPC method by default (#39669)
* perf(rpc): stop the dispatcher importing every RPC method by default Callers pass the method list. A test that builds a dispatcher no longer loads the full method registry, cutting the dispatcher's import graph from ~4k to ~550 modules. * chore(lint): keep the RPC dispatcher and plugin RPC off the method registry --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -212,6 +212,39 @@
|
||||
"rules": {
|
||||
"max-lines": ["error", { "max": 800, "skipBlankLines": true, "skipComments": true }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"src/main/runtime/rpc/dispatcher.ts",
|
||||
"src/main/plugins/plugin-runtime-rpc.ts",
|
||||
"src/main/plugins/plugin-runtime-binding.ts"
|
||||
],
|
||||
"rules": {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "@linear/sdk",
|
||||
"allowTypeImports": true,
|
||||
"message": "Value-importing @linear/sdk hoists its ~2.6MB CJS bundle into the eager top-level require block and defeats the lazy loader. Use `import type` plus loadLinearSdk() from src/main/linear/linear-sdk.ts."
|
||||
},
|
||||
{
|
||||
"name": "ssh2",
|
||||
"allowTypeImports": true,
|
||||
"message": "Value-importing ssh2 hoists it into the eager main-process require block and blocks startup (#35731). Use `import type` plus loadSsh2() from src/main/ssh/ssh2-module-load.ts."
|
||||
}
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["**/rpc-method-registry"],
|
||||
"allowTypeImports": true,
|
||||
"message": "The registry imports every RPC method (~3.4k modules); take the method list as a parameter so importers stay light."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
|
||||
@@ -7,6 +7,7 @@ import { installPluginHookRunner } from './hooks/plugin-hook-participants'
|
||||
import { installPluginResidentWorkerWarmer } from './workers/plugin-resident-worker'
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
import { pluginRuntimeRpc } from './plugin-runtime-rpc'
|
||||
import type { DispatcherOptions } from '../runtime/rpc/dispatcher'
|
||||
import type { PluginRuntimeRpc } from './workers/plugin-host-workspace-method-bindings'
|
||||
import {
|
||||
pluginTerminalTitle,
|
||||
@@ -47,10 +48,11 @@ export function pluginRuntimeDelegate(
|
||||
export function bindPluginRuntime(
|
||||
service: PluginService,
|
||||
runtime: OrcaRuntimeService,
|
||||
hookServer: { getStatusSnapshot(): readonly PluginAgentStatusRow[] }
|
||||
hookServer: { getStatusSnapshot(): readonly PluginAgentStatusRow[] },
|
||||
methods: DispatcherOptions['methods']
|
||||
): PluginRuntimeDelegate {
|
||||
const statuses = () => hookServer.getStatusSnapshot()
|
||||
const delegate = pluginRuntimeDelegate(runtime, statuses, pluginRuntimeRpc(runtime))
|
||||
const delegate = pluginRuntimeDelegate(runtime, statuses, pluginRuntimeRpc(runtime, methods))
|
||||
service.setRuntimeDelegate(delegate)
|
||||
installPluginHookRunner(service, runtime)
|
||||
installPluginResidentWorkerWarmer(service)
|
||||
|
||||
@@ -19,11 +19,11 @@ export class PluginRuntimeRpcError extends Error {
|
||||
*/
|
||||
export function pluginRuntimeRpc(
|
||||
runtime: OrcaRuntimeService,
|
||||
methods?: DispatcherOptions['methods']
|
||||
methods: DispatcherOptions['methods']
|
||||
): PluginRuntimeRpc {
|
||||
let dispatcher: RpcDispatcher | null = null
|
||||
return async (method, params, options = {}) => {
|
||||
dispatcher ??= new RpcDispatcher({ runtime, ...(methods ? { methods } : {}) })
|
||||
dispatcher ??= new RpcDispatcher({ runtime, methods })
|
||||
// The dispatcher maps a throw to an error answer; keep the original so its code reaches the plugin.
|
||||
let refusal: unknown = null
|
||||
const response = await dispatcher.dispatch(
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from './core'
|
||||
|
||||
import { successResponse } from './errors'
|
||||
import { ALL_RPC_METHODS } from './methods/rpc-method-registry'
|
||||
import { withoutMethodsThisHostDoesNotServe } from './methods/terminal/session-terminal-scrollback'
|
||||
import { emulatorProbe, emulatorProbeError } from '../../emulator/emulator-probe'
|
||||
import type { OrcaRuntimeService } from '../orca-runtime'
|
||||
@@ -37,7 +36,7 @@ import { resolveOrcadFeatureRequirement, rpcMethodFeatureWait } from './orcad-fe
|
||||
|
||||
export type DispatcherOptions = {
|
||||
runtime: OrcaRuntimeService
|
||||
methods?: readonly RpcAnyMethodDeclaration[]
|
||||
methods: readonly RpcAnyMethodDeclaration[]
|
||||
/** Pins every call to this scope, for in-process bridges that relay a non-owner caller. */
|
||||
callerScope?: RpcCallerScope
|
||||
}
|
||||
@@ -56,7 +55,7 @@ export class RpcDispatcher {
|
||||
private readonly legacyOrchestration: OrchestrationLegacyCompatibility
|
||||
private readonly pinnedCallerScope: RpcCallerScope | undefined
|
||||
|
||||
constructor({ runtime, methods = ALL_RPC_METHODS, callerScope }: DispatcherOptions) {
|
||||
constructor({ runtime, methods, callerScope }: DispatcherOptions) {
|
||||
this.runtime = runtime
|
||||
this.pinnedCallerScope = callerScope
|
||||
this.registry = buildRegistry(withoutMethodsThisHostDoesNotServe(methods))
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { OrcaRuntimeService } from '../orca-runtime'
|
||||
import type { RpcRequest, RpcResponse } from './core'
|
||||
import { RpcDispatcher } from './dispatcher'
|
||||
import { ALL_RPC_METHODS } from './methods/rpc-method-registry'
|
||||
import type { RpcCallerScope } from './rpc-caller-scope'
|
||||
|
||||
const PAIRED: RpcCallerScope = { kind: 'runtime-paired', grants: [] }
|
||||
@@ -16,7 +17,7 @@ async function dispatchAs(
|
||||
params?: unknown,
|
||||
runtime = new OrcaRuntimeService()
|
||||
): Promise<RpcResponse> {
|
||||
return new RpcDispatcher({ runtime }).dispatch(
|
||||
return new RpcDispatcher({ runtime, methods: ALL_RPC_METHODS }).dispatch(
|
||||
request(method, params),
|
||||
scope ? { callerScope: scope } : undefined
|
||||
)
|
||||
@@ -50,6 +51,7 @@ describe('a pinned in-process bridge dispatcher', () => {
|
||||
it('ignores a wider per-call scope', async () => {
|
||||
const dispatcher = new RpcDispatcher({
|
||||
runtime: new OrcaRuntimeService(),
|
||||
methods: ALL_RPC_METHODS,
|
||||
callerScope: PAIRED
|
||||
})
|
||||
const response = await dispatcher.dispatch(request('computer.click', { app: 'Finder' }), {
|
||||
|
||||
@@ -45,6 +45,7 @@ import { mainProcessState as state } from './main-process-state'
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
import { PluginStatusBarState } from '../plugins/status-bar/plugin-status-bar-state'
|
||||
import { createDesktopPluginAppearance } from '../plugins/appearance/desktop-plugin-appearance'
|
||||
import { ALL_RPC_METHODS } from '../runtime/rpc/methods/rpc-method-registry'
|
||||
|
||||
const PLUGIN_AUTO_UPDATE_STARTUP_DELAY_MS = 60_000
|
||||
// Why: the official catalog fetch must not compete with first paint; opening the marketplace seeds sooner.
|
||||
@@ -240,7 +241,7 @@ export async function initializeMainProcessPlugins(runtime: OrcaRuntimeService):
|
||||
}
|
||||
})
|
||||
// Bound here, not in the IPC handlers: a headless serve never opens a window to register them.
|
||||
const pluginDelegate = bindPluginRuntime(pluginService, runtime, agentHookServer)
|
||||
const pluginDelegate = bindPluginRuntime(pluginService, runtime, agentHookServer, ALL_RPC_METHODS)
|
||||
bindPluginRpcServices(store, pluginService, {
|
||||
marketplace: state.pluginMarketplaceService,
|
||||
installer: state.pluginMarketplaceInstaller
|
||||
|
||||
@@ -44,6 +44,7 @@ import { runGatedWslMoveDesktopRewritePass } from '../persistence/moving-wsl-pro
|
||||
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../shared/layout-owner-switch'
|
||||
import { repairDuplicatePtyPanesOnStart } from '../runtime/moved-pane-owner-rekey'
|
||||
import { WslUsageReporter } from '../telemetry/wsl-usage-reporter'
|
||||
import { ALL_RPC_METHODS } from '../runtime/rpc/methods/rpc-method-registry'
|
||||
|
||||
// Headless serve has no first window; timer fallback must still sweep trash.
|
||||
const WORKTREE_TRASH_SWEEP_FALLBACK_MS = 15_000
|
||||
@@ -73,7 +74,7 @@ export async function initializeReadyRuntimeServices(): Promise<void> {
|
||||
runtime.setAgentBrowserBridge(state.agentBrowserBridge)
|
||||
// Reclaim browser daemons orphaned by prior crashes/SIGKILL.
|
||||
void state.agentBrowserBridge.sweepOrphanedSessions()
|
||||
const browserClientAutomationDispatcher = new RpcDispatcher({ runtime })
|
||||
const browserClientAutomationDispatcher = new RpcDispatcher({ runtime, methods: ALL_RPC_METHODS })
|
||||
configureBrowserClientPageAutomationRuntime({
|
||||
browserManager,
|
||||
getAgentBrowserBridge: () => state.agentBrowserBridge,
|
||||
|
||||
Reference in New Issue
Block a user