mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-09-01 02:56:14 +00:00
refactor(desktop): drop server_mode and fold runtime locale into track
This commit is contained in:
parent
0c41ca675a
commit
0aa8eea38b
9 changed files with 37 additions and 77 deletions
|
|
@ -138,7 +138,7 @@
|
|||
- [x] **埋点扩充(观测指标文档批次)**(已完成,desktop 专属,2026-07)
|
||||
- 依据飞书《桌面端观测指标 & 埋点补充》:新增事件——主进程 `app_launched`(launch_intent,jump-list argv 判定)、`startup_timing`(main_ready / window_shown / renderer_loaded / renderer_ready 四阶段,`process.uptime()` 累计)、`renderer_crashed`(render-process-gone 的 reason + exit_code,Electron 新枚举 memory-eviction 折叠为 oom)、`startup_connect_result`(ok / failure_phase / retry_count / error_class,connect.ts 失败分类纯函数)、`startup_failure_screen_shown`;renderer `session_created`(kind new/resumed + source,见下)、`notification_shown/clicked`(kind 由通知 tag 前缀映射)、`onboarding_completed/abandoned`(wizard 记时,step1 skip=abandoned、step2 skip=completed)、`search_opened/executed`(TranscriptSearch;仅查询驱动上报,MutationObserver 流式刷新不报,结果数分桶)、`logout`、`plan_usage_card_viewed`(PlanUsageCard 加 active prop——Account 面板是 v-show,onMounted 不等于曝光;usage_bucket 取各行最严重级别)、`telemetry_consent_changed`、`renderer_error`(trace.ts 的 window error/unhandledrejection,只报 Error.name)、`connection_lost/restored`(useKimiWebClient 连接状态迁移,首次连接前的断开不报)、`workspace_added/removed`(workspace_count 为操作后总数)。
|
||||
- 参数修订:`app_crashed` + process(main/gpu,gpu 来自 child-process-gone)+app_uptime_ms;`update_status_changed` version→to_version + from_version/prev_state/error_class;`window_lifecycle` + reason(quit/close_to_tray/deactivate)/visible_duration_ms(close→hide 路径=close_to_tray、minimize=deactivate、before-quit=quit);`approval_decision` + request_id(ApprovalCard 加可选 prop,父级透传 approvalId);`onboarding_step` 改为**离开步骤时**上报 + step_index/total_steps/duration_ms;`oauth_login_step` + method/duration_ms/error_class(start_failed/poll_failed/expired/cancelled);`settings_changed` + source_panel(LanguageSwitcher 与 useUpdateStatus.setAutoDownload 需调用方传参区分面板);`attachment_added` + size_bucket/count;`ui_element_toggled` + sample_rate(literal 1,未真实采样);`system_metrics` + window_count 与 session_count(后者经内嵌 core 的 `ISessionIndex.countActive` 进程内查询,external 模式省略)。
|
||||
- 公共参数注入:`telemetry.ts` 的 track impl 包装给每个事件合并 server_mode(connect.ts 解析后写 `runtime-context.ts` 新模块,避免循环依赖)/ app_uptime_ms / days_since_install(device_id 文件 birthtime)/ locale(`kimi:locale` 推送经 tray.ts 落入 runtime-context)/ theme(nativeTheme),事件自带字段优先。
|
||||
- 公共参数注入:`telemetry.ts` 的 track impl 包装给每个事件合并 app_uptime_ms / days_since_install(device_id 文件 birthtime)/ locale(`kimi:locale` 推送经 tray.ts 落入 `track.ts`)/ theme(nativeTheme),事件自带字段优先。server_mode 与 `runtime-context.ts` 已于 2026-07 移除:外部 server 仅 dev 联调、生产恒 embedded,该维度无分析价值,locale 状态并入 `track.ts`。
|
||||
- **启动事件缓冲**:`track.ts` 在 impl 安装前缓冲事件(上限 200 丢最旧),`setDesktopTrackImpl` 时回放——telemetry 要等内嵌 server 就绪才接线,此前 app_launched/startup_timing 全丢;window_lifecycle 的原 replayWindowLifecycle 随之删除(缓冲覆盖)。外部 server 模式(KIMI_SERVER_URL,dev 联调用)仍无 telemetry——appender 挂在内嵌 core 上,生产无影响。注意口径:回放事件的管道时间戳与 super property `app_uptime_ms` 取**接线时刻**(非事件发生时刻,偏移为 shell-env 探测 + server 启动耗时;`startup_timing` 自带的 `duration_ms` 不受影响)。
|
||||
- `session_created` 的 source 归因:`lib/session-intent.ts`(**不同步 web**,同 track.ts 先例)模块级 pending intent,入口点(runShortcutAction 按 source 映射 / jump-list 回调 / tray / 通知 onclick / 搜索框选择 / 斜杠命令)先 set,`createDraftSession`(kind:new)与 `selectSession`(kind:resumed)consume;createDraftSession 内部 selectSession 传 skipTrack 去重;**自动恢复路径全部 skipTrack**(首载 deep-link/自动选择、popstate 死链兜底、归档/会话消失兜底)——漏斗只计用户主动打开。
|
||||
- 未做(需 kimi-code core / 服务端 / 产品 UI 先行):upgrade_clicked 与 limit_reached_shown(无升级/限额 UI,ManagedUsageResult 无 plan_type)、user_id / install_id / ingest_ts / is_backfilled(管道侧)、consent 热切换(维持重启生效——由此 `telemetry_consent_changed{enabled:true}` 永不送达:consent 关闭时管线不接线,事件只进内存缓冲并随退出丢弃;`enabled:false` 方向正常送达)。
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import { isOnboarded, isVibrancyEnabled } from './ui-state';
|
|||
import { resolveConnectTarget } from './connect-target';
|
||||
import { dataUrl, errorHtml } from './screens';
|
||||
import { log, redactUrlForLog } from './log';
|
||||
import { setServerMode } from './runtime-context';
|
||||
import { trackDesktopEvent } from './track';
|
||||
import type { StartupConnectResultEvent } from './telemetry-events';
|
||||
import { DESKTOP_PRODUCT_NAME } from '../shared/identity';
|
||||
|
|
@ -136,7 +135,6 @@ async function connectOnce(win: BrowserWindow): Promise<void> {
|
|||
// builds always use `app://renderer`.
|
||||
const devBase = app.isPackaged ? undefined : rendererDevBase(process.env['KIMI_RENDERER_DEV_URL']);
|
||||
const target = resolveConnectTarget(process.env['KIMI_SERVER_URL'], readServerToken);
|
||||
setServerMode(target.external ? 'external' : 'embedded');
|
||||
if (target.external) {
|
||||
({ origin, token } = target);
|
||||
// Redact: KIMI_SERVER_URL may carry basic-auth userinfo, which must not
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// Module-level runtime context, merged into every telemetry event as super
|
||||
// properties (telemetry.ts). Kept dependency-free so connect.ts / tray.ts can
|
||||
// write it without pulling the telemetry wiring module (agent-core-v2) into
|
||||
// their load-time graph.
|
||||
|
||||
export type ServerMode = 'embedded' | 'external';
|
||||
|
||||
let serverMode: ServerMode | undefined;
|
||||
let locale: string | undefined;
|
||||
|
||||
export function setServerMode(mode: ServerMode | undefined): void {
|
||||
serverMode = mode;
|
||||
}
|
||||
|
||||
export function getServerMode(): ServerMode | undefined {
|
||||
return serverMode;
|
||||
}
|
||||
|
||||
export function setRuntimeLocale(next: string | undefined): void {
|
||||
locale = next;
|
||||
}
|
||||
|
||||
export function getRuntimeLocale(): string | undefined {
|
||||
return locale;
|
||||
}
|
||||
|
|
@ -16,9 +16,8 @@ import { resolveKimiHome } from '@moonshot-ai/kimi-code-sdk';
|
|||
|
||||
import { DESKTOP_PRODUCT_NAME, DESKTOP_UI_MODE } from '../shared/identity';
|
||||
import { log } from './log';
|
||||
import { getRuntimeLocale, getServerMode } from './runtime-context';
|
||||
import { startDesktopSystemMetrics, stopDesktopSystemMetrics } from './system-metrics';
|
||||
import { setDesktopTrackImpl } from './track';
|
||||
import { getRuntimeLocale, setDesktopTrackImpl } from './track';
|
||||
|
||||
export interface DesktopTelemetryHandle {
|
||||
/** Emits `exit`, flushes the buffer, stops periodic flush. Idempotent. */
|
||||
|
|
@ -82,8 +81,6 @@ function withSuperProperties(properties: TelemetryProperties | undefined): Telem
|
|||
app_uptime_ms: Math.round(process.uptime() * 1000),
|
||||
theme: nativeTheme.shouldUseDarkColors ? 'dark' : 'light',
|
||||
};
|
||||
const serverMode = getServerMode();
|
||||
if (serverMode !== undefined) injected['server_mode'] = serverMode;
|
||||
const daysSinceInstall = readDaysSinceInstall();
|
||||
if (daysSinceInstall !== undefined) injected['days_since_install'] = daysSinceInstall;
|
||||
const locale = getRuntimeLocale();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Host telemetry facade. Events fired before embedded telemetry is wired (app
|
||||
// launch, startup phases) are buffered and replayed once wiring completes;
|
||||
// without an impl the buffer is the only place they exist, so it stays small.
|
||||
// Also holds the runtime locale (renderer-pushed via tray.ts) that
|
||||
// telemetry.ts merges into every event as the `locale` super property.
|
||||
|
||||
import type { TelemetryProperties } from '@moonshot-ai/agent-core-v2';
|
||||
|
||||
|
|
@ -44,3 +46,13 @@ export function trackDesktopEvent<K extends DesktopEventName>(
|
|||
}
|
||||
impl(event, props);
|
||||
}
|
||||
|
||||
let locale: string | undefined;
|
||||
|
||||
export function setRuntimeLocale(next: string | undefined): void {
|
||||
locale = next;
|
||||
}
|
||||
|
||||
export function getRuntimeLocale(): string | undefined {
|
||||
return locale;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ import { join } from 'node:path';
|
|||
import { app, Menu, nativeImage, Tray } from 'electron';
|
||||
import type { MenuItemConstructorOptions } from 'electron';
|
||||
|
||||
import { trackDesktopEvent } from './track';
|
||||
import { setRuntimeLocale } from './runtime-context';
|
||||
import { setRuntimeLocale, trackDesktopEvent } from './track';
|
||||
import { setTaskbarAttention } from './taskbar';
|
||||
|
||||
// System tray (macOS menu-bar / Windows notification area). Desktop-only — the
|
||||
|
|
|
|||
|
|
@ -413,19 +413,6 @@ describe('startup_connect_result', () => {
|
|||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('records the server mode in the runtime context', async () => {
|
||||
const { connect } = await importConnect();
|
||||
const { getServerMode } = await import('../../src/main/runtime-context');
|
||||
mocks.startDesktopServer.mockResolvedValue(fakeHandle());
|
||||
|
||||
await connect(fakeWindow() as unknown as BrowserWindow);
|
||||
expect(getServerMode()).toBe('embedded');
|
||||
|
||||
process.env['KIMI_SERVER_URL'] = 'http://127.0.0.1:58627';
|
||||
await connect(fakeWindow() as unknown as BrowserWindow);
|
||||
expect(getServerMode()).toBe('external');
|
||||
});
|
||||
});
|
||||
|
||||
describe('classifyConnectFailure', () => {
|
||||
|
|
|
|||
|
|
@ -38,13 +38,11 @@ import {
|
|||
resetDaysSinceInstallCacheForTests,
|
||||
wireDesktopTelemetry,
|
||||
} from '../../src/main/telemetry';
|
||||
import { setDesktopTrackImpl, trackDesktopEvent } from '../../src/main/track';
|
||||
import {
|
||||
getRuntimeLocale,
|
||||
getServerMode,
|
||||
setDesktopTrackImpl,
|
||||
setRuntimeLocale,
|
||||
setServerMode,
|
||||
} from '../../src/main/runtime-context';
|
||||
trackDesktopEvent,
|
||||
} from '../../src/main/track';
|
||||
|
||||
const EXISTING_DEVICE = { deviceId: 'device-1', firstLaunch: false } as const;
|
||||
|
||||
|
|
@ -272,7 +270,6 @@ describe('super properties injection', () => {
|
|||
beforeEach(async () => {
|
||||
createCloudAppenderMock.mockReset();
|
||||
nativeThemeMock.shouldUseDarkColors = false;
|
||||
setServerMode(undefined);
|
||||
setRuntimeLocale(undefined);
|
||||
resetDaysSinceInstallCacheForTests();
|
||||
homeDir = await mkdtemp(join(tmpdir(), 'kimi-telemetry-'));
|
||||
|
|
@ -281,7 +278,6 @@ describe('super properties injection', () => {
|
|||
|
||||
afterEach(() => {
|
||||
setDesktopTrackImpl(null);
|
||||
setServerMode(undefined);
|
||||
setRuntimeLocale(undefined);
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
|
@ -295,7 +291,6 @@ describe('super properties injection', () => {
|
|||
await writeFile(join(homeDir, 'device_id'), 'device-1');
|
||||
wiredTrack();
|
||||
const { core, telemetryService } = makeCore(undefined);
|
||||
setServerMode('embedded');
|
||||
setRuntimeLocale('zh');
|
||||
|
||||
const handle = await wireDesktopTelemetry(core as never, EXISTING_DEVICE);
|
||||
|
|
@ -304,7 +299,6 @@ describe('super properties injection', () => {
|
|||
trackDesktopEvent('window_lifecycle', { action: 'shown' });
|
||||
expect(telemetryService.track).toHaveBeenCalledWith('window_lifecycle', {
|
||||
action: 'shown',
|
||||
server_mode: 'embedded',
|
||||
locale: 'zh',
|
||||
theme: 'light',
|
||||
days_since_install: 0,
|
||||
|
|
@ -322,8 +316,8 @@ describe('super properties injection', () => {
|
|||
});
|
||||
|
||||
it('omits context values that are unset or unreadable', async () => {
|
||||
// No device_id file in homeDir → days_since_install is dropped; server_mode
|
||||
// and locale stay unset.
|
||||
// No device_id file in homeDir → days_since_install is dropped; locale
|
||||
// stays unset.
|
||||
wiredTrack();
|
||||
const { core, telemetryService } = makeCore(undefined);
|
||||
|
||||
|
|
@ -332,7 +326,6 @@ describe('super properties injection', () => {
|
|||
|
||||
trackDesktopEvent('global_shortcut_invoked', {});
|
||||
const properties = telemetryService.track.mock.calls.at(-1)?.[1] as Record<string, unknown>;
|
||||
expect(properties['server_mode']).toBeUndefined();
|
||||
expect(properties['locale']).toBeUndefined();
|
||||
expect(properties['days_since_install']).toBeUndefined();
|
||||
expect(properties['app_uptime_ms']).toEqual(expect.any(Number));
|
||||
|
|
@ -347,30 +340,14 @@ describe('super properties injection', () => {
|
|||
nativeThemeMock.shouldUseDarkColors = true;
|
||||
|
||||
const handle = await wireDesktopTelemetry(core as never, EXISTING_DEVICE);
|
||||
setServerMode('external');
|
||||
setRuntimeLocale('en');
|
||||
|
||||
trackDesktopEvent('global_shortcut_invoked', {});
|
||||
expect(telemetryService.track).toHaveBeenCalledWith(
|
||||
'global_shortcut_invoked',
|
||||
expect.objectContaining({ server_mode: 'external', locale: 'en', theme: 'dark' }),
|
||||
expect.objectContaining({ locale: 'en', theme: 'dark' }),
|
||||
);
|
||||
|
||||
await handle!.shutdown();
|
||||
});
|
||||
});
|
||||
|
||||
describe('runtime-context', () => {
|
||||
it('stores server_mode and locale, defaulting to undefined', () => {
|
||||
expect(getServerMode()).toBeUndefined();
|
||||
expect(getRuntimeLocale()).toBeUndefined();
|
||||
setServerMode('embedded');
|
||||
setRuntimeLocale('zh');
|
||||
expect(getServerMode()).toBe('embedded');
|
||||
expect(getRuntimeLocale()).toBe('zh');
|
||||
setServerMode(undefined);
|
||||
setRuntimeLocale(undefined);
|
||||
expect(getServerMode()).toBeUndefined();
|
||||
expect(getRuntimeLocale()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { asRendererTrackEvent } from '../../src/main/renderer-track-validation';
|
||||
import { setDesktopTrackImpl, trackDesktopEvent } from '../../src/main/track';
|
||||
import {
|
||||
getRuntimeLocale,
|
||||
setDesktopTrackImpl,
|
||||
setRuntimeLocale,
|
||||
trackDesktopEvent,
|
||||
} from '../../src/main/track';
|
||||
|
||||
beforeEach(() => {
|
||||
setDesktopTrackImpl(null);
|
||||
|
|
@ -78,6 +83,16 @@ describe('trackDesktopEvent', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('runtime locale', () => {
|
||||
it('stores the pushed locale, defaulting to undefined', () => {
|
||||
expect(getRuntimeLocale()).toBeUndefined();
|
||||
setRuntimeLocale('zh');
|
||||
expect(getRuntimeLocale()).toBe('zh');
|
||||
setRuntimeLocale(undefined);
|
||||
expect(getRuntimeLocale()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('asRendererTrackEvent', () => {
|
||||
it('accepts action_invoked with a valid source', () => {
|
||||
expect(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue