mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-11-14 10:56:33 +00:00
* perf: add opt-in startup timeline and real-app startup harness - electron/perfTrace.cjs records first-occurrence startup marks in the main process when NETCATTY_PERF_TRACE=1 (a no-op otherwise), then writes the main-window renderer timeline (navigation, paint, netcatty:* marks, JS heap) and app.getAppMetrics() to JSON after the first renderer-ready signal. - Marks: main.cjs entry / modules loaded / whenReady, main window creation, registerBridges and plugin-host start/end, loadURL, did-finish-load, renderer ready, window shown. The renderer adds three performance.mark() calls (entry evaluated, App mounted, renderer ready), also visible in DevTools. - scripts/perf-startup.live.cjs (npm run perf:startup) launches the built app N times against a throwaway --user-data-dir profile with ssh:// deep-link registration disabled, optionally seeds synthetic hosts over CDP, and reports cold plus warm p50/p90. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(harness): add --profile option to reuse a startup profile Lets the harness keep one profile across invocations, e.g. to compare warm V8 code caches across builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: retrigger CI after flaky monitorRuntimeIsolation failure Previous lint-and-test failed on known-preexisting `useEffect is not defined` in monitorRuntimeIsolation.test.ts. * chore: retrigger CI after monitorRuntimeIsolation useEffect flake Known pre-existing flake: monitor controls test fails with `useEffect is not defined`. Empty commit to re-run once. * test(app): bind React hooks in monitorRuntimeIsolation VM sandbox Extracting component declarations drops their React imports, leaving bare hooks such as useEffect undefined in the VM. Inject common React hooks and APIs while preserving explicit binding overrides to prevent intermittent monitor isolation failures in CI. Stub the startup performance mark through App test bindings so the mount effect can execute without browser globals. Production components remain unchanged. Validation: monitorRuntimeIsolation passed five consecutive runs. Plain npm run lint found 17 existing errors in git-ignored scratch files; excluding only _t.mjs and sim-4107.mjs passed with zero errors and eight existing warnings. * chore: retrigger CI after plugin-contract runner cancel Lint/schema passed; plugin-contract step cancelled by the runner (not a test failure). Empty commit to re-queue. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: binaricat <binaricat@users.noreply.github.com>
113 lines
4.8 KiB
TypeScript
Executable File
113 lines
4.8 KiB
TypeScript
Executable File
import { MonitorHostKeyDialog } from './components/vault/MonitorHostKeyDialog';
|
|
import { MonitorRuntimeHost } from './application/app/MonitorRuntimeHost';
|
|
import React, { useEffect } from 'react';
|
|
|
|
import { initializeFonts } from './application/state/fontStore';
|
|
import { initializeUIFonts } from './application/state/uiFontStore';
|
|
import { useSettingsChromeStore } from './application/state/settingsChromeStore';
|
|
import { I18nProvider } from './application/i18n/I18nProvider';
|
|
import type { useSettingsState } from './application/state/useSettingsState';
|
|
import type { useAppLockState } from './application/state/useAppLockState';
|
|
import { ToastProvider } from './components/ui/toast';
|
|
import { TooltipProvider } from './components/ui/tooltip';
|
|
import { ScriptAutomationRoot } from './components/scripts/ScriptAutomationRoot';
|
|
import { ExternalMcpApprovalsHost } from './components/ai/ExternalMcpApprovalsHost';
|
|
import { PluginPermissionHost } from './components/plugins/PluginPermissionHost';
|
|
import { PluginHostInteractionHost } from './components/plugins/PluginHostInteractionHost';
|
|
import { PluginHostActionHost } from './components/plugins/PluginHostActionHost';
|
|
import { PluginAuthenticationHost } from './components/plugins/PluginAuthenticationHost';
|
|
import { MultilinePasteConfirmHost } from './components/terminal/MultilinePasteConfirmDialog';
|
|
import { useExternalMcpGrantPersister } from './components/ai/useExternalMcpGrantPersister';
|
|
import { setupMcpApprovalBridge } from './infrastructure/ai/shared/approvalGate';
|
|
import { setupCodexAppServerInteractionBridge } from './infrastructure/ai/shared/codexAppServerInteractions';
|
|
import { AppShell } from './application/app/AppShell';
|
|
import { AppLocalStateProvider } from './application/app/AppLocalState';
|
|
import { AppSideEffects } from './application/app/AppSideEffects';
|
|
import { SessionPublisher } from './application/app/publishers/SessionPublisher';
|
|
import { SettingsPublisher } from './application/app/publishers/SettingsPublisher';
|
|
import { VaultPublisher } from './application/app/publishers/VaultPublisher';
|
|
import { AppLockRuntimePublisher } from './application/app/publishers/AppLockRuntimePublisher';
|
|
|
|
// Initialize fonts eagerly at app startup
|
|
initializeFonts();
|
|
initializeUIFonts();
|
|
|
|
type SettingsState = ReturnType<typeof useSettingsState>;
|
|
type AppLockState = ReturnType<typeof useAppLockState>;
|
|
|
|
/**
|
|
* Bridges the locale out of settings chrome without pulling App into a
|
|
* settings-subscribing mega component.
|
|
*/
|
|
function SettingsI18nProvider({ children }: { children: React.ReactNode }) {
|
|
const { uiLanguage } = useSettingsChromeStore();
|
|
return <I18nProvider locale={uiLanguage}>{children}</I18nProvider>;
|
|
}
|
|
|
|
/**
|
|
* Thin coordinator: no vault/session/settings React subscriptions and no
|
|
* domain bag construction. Publishers own mega hooks; Host islands build
|
|
* shell bags; AppSideEffects owns reactive effects + local UI publishing.
|
|
*/
|
|
function App() {
|
|
useEffect(() => {
|
|
performance.mark('netcatty:renderer:app-mounted');
|
|
}, []);
|
|
return (
|
|
<AppLocalStateProvider>
|
|
<MonitorRuntimeHost />
|
|
<AppSideEffects />
|
|
<MonitorHostKeyDialog />
|
|
<AppShell />
|
|
</AppLocalStateProvider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* `AppLockGate` (index.tsx) owns `useSettingsState` / `useAppLockState` so the
|
|
* lock overlay can render before app children mount, and it owns the
|
|
* splash-removal + rendererReady signal (deep links must wait for unlock).
|
|
* App forwards the gate's settings instance into SettingsPublisher so the
|
|
* runtime slot/context still publish a single settings runtime.
|
|
*/
|
|
function AppWithProviders({ settings, appLock }: { settings: SettingsState; appLock: AppLockState }) {
|
|
const isPeerSessionWindow = typeof window !== 'undefined' && window.location.hash.startsWith('#/session-window');
|
|
|
|
useEffect(() => {
|
|
return setupMcpApprovalBridge();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
return setupCodexAppServerInteractionBridge();
|
|
}, []);
|
|
|
|
useExternalMcpGrantPersister();
|
|
|
|
return (
|
|
<SettingsPublisher settings={settings}>
|
|
<AppLockRuntimePublisher appLock={appLock} appLockEnabled={settings.appLockSettings.enabled}>
|
|
<SettingsI18nProvider>
|
|
<ToastProvider>
|
|
<TooltipProvider delayDuration={300}>
|
|
<ScriptAutomationRoot />
|
|
<ExternalMcpApprovalsHost />
|
|
<PluginAuthenticationHost />
|
|
<PluginPermissionHost />
|
|
<PluginHostInteractionHost />
|
|
<MultilinePasteConfirmHost />
|
|
<VaultPublisher>
|
|
<SessionPublisher persistSessionRestore={!isPeerSessionWindow}>
|
|
<App />
|
|
<PluginHostActionHost />
|
|
</SessionPublisher>
|
|
</VaultPublisher>
|
|
</TooltipProvider>
|
|
</ToastProvider>
|
|
</SettingsI18nProvider>
|
|
</AppLockRuntimePublisher>
|
|
</SettingsPublisher>
|
|
);
|
|
}
|
|
|
|
export default AppWithProviders;
|