Skip to content

Commit 463763c

Browse files
committed
Merge pull request #38 from Activer007/codex/p0-startup-decoupling
# Conflicts: # src/main/libs/externalAgentEnvironment.ts
2 parents 32fc86b + bd1bfe5 commit 463763c

14 files changed

Lines changed: 303 additions & 33 deletions

.github/workflows/labeler.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Labeler
22

33
on:
4-
pull_request:
4+
pull_request_target:
55
types: [opened, synchronize, reopened]
66

77
env:
@@ -12,9 +12,9 @@ jobs:
1212
runs-on: ubuntu-latest
1313
permissions:
1414
contents: read
15+
issues: write
1516
pull-requests: write
1617
steps:
17-
- uses: actions/checkout@v4
1818
- uses: actions/labeler@v5
1919
with:
2020
configuration-path: .github/labeler.yml

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Changelog
2+
3+
本文档记录 WeSight 的重要变更。
4+
5+
发布说明应从对应版本条目生成。
6+
7+
## 2026.6.2 - 2026-06-03
8+
9+
### 新增
10+
11+
- 新增 `docs/release-process.md`,定义最小发布流程。
12+
- 新增根目录 `CHANGELOG.md`,作为发布说明来源。
13+
14+
### 变更
15+
16+
- 明确 changelog 条目是 release note 的生成来源。
17+
- 明确门禁失败时不得发布稳定版;若共享候选版本,必须记录为已知问题或阻断项。
18+
19+
### 修复
20+
21+
- 本次为文档更新,不包含产品修复。
22+
23+
### 已知问题
24+
25+
- 当前基线下 `npm run lint` 尚未通过。
26+
- 当前基线下 `npm test` 尚未通过。

docs/release-process.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 发布规范
2+
3+
本文档定义 WeSight 的最小发布流程。
4+
5+
## 发布门禁
6+
7+
发布稳定版前,必须运行并记录结果:
8+
9+
```bash
10+
npm run build
11+
npm run lint
12+
npm test
13+
```
14+
15+
任一门禁失败时,不得发布稳定版。若仍需要共享候选版本,必须把失败项写入已知问题或阻断项。
16+
17+
## Changelog
18+
19+
每个版本都必须在 `CHANGELOG.md` 中记录:
20+
21+
- 版本
22+
- 日期
23+
- 新增
24+
- 变更
25+
- 修复
26+
- 已知问题
27+
28+
GitHub release note 应从 `CHANGELOG.md` 的对应版本条目生成。
29+
30+
## 发布审查
31+
32+
打 tag 前确认:
33+
34+
- 版本号与包元数据一致。
35+
- 已知问题明确、可行动。
36+
- 发布说明只描述对开发者或用户有意义的项目级变化。
37+
- 涉及安全、签名、校验和平台差异时,已有对应检查记录。

src/main/libs/externalAgentEnvironment.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,18 @@ test('does not treat WeSight placeholders as local CLI credentials', () => {
112112

113113
expect(result.authStatus).toBe('logged_out');
114114
});
115+
116+
test('limits probes to requested app types', async () => {
117+
writeExecutable('codex', '#!/bin/sh\necho "codex-test 1.0.0"\n');
118+
119+
const { snapshot, report } = await getExternalAgentEnvironmentSnapshot({ appTypes: ['codex'] });
120+
121+
expect(snapshot.engines).toHaveLength(1);
122+
expect(snapshot.engines[0]).toMatchObject({
123+
appType: 'codex',
124+
found: true,
125+
path: path.join(tempDir, 'codex'),
126+
version: 'codex-test 1.0.0',
127+
});
128+
expect(report.metrics.map(metric => metric.command)).toEqual(['codex']);
129+
});

src/main/libs/externalAgentEnvironment.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,11 +1033,14 @@ const readBaseSnapshotInputs = (): {
10331033
return { appDir, settingsPath, dbPath, settings };
10341034
};
10351035

1036-
export function getPlaceholderExternalAgentEnvironmentSnapshot(): ExternalAgentEnvironmentSnapshot {
1036+
export function getPlaceholderExternalAgentEnvironmentSnapshot(
1037+
options: ExternalAgentEnvironmentProbeOptions = {},
1038+
): ExternalAgentEnvironmentSnapshot {
10371039
const { appDir, settingsPath, dbPath, settings } = readBaseSnapshotInputs();
1040+
const commands = listAgentEngineCommands(options);
10381041
return {
10391042
ccSwitch: buildCcSwitchSnapshot(appDir, settingsPath, dbPath, settings),
1040-
engines: AGENT_ENGINE_COMMANDS.map(({ engine, appType, command }) => (
1043+
engines: commands.map(({ engine, appType, command }) => (
10411044
buildPlaceholderCommandStatus(engine, appType, command, settings, dbPath)
10421045
)),
10431046
};

src/main/libs/performanceMetrics.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,26 @@ test('keeps the first startup timing value by default', () => {
6363
expect(getPerformanceSnapshot().startupTimings.first_paint_ms).toBe(120);
6464
});
6565

66+
test('includes startup and selected engine timings in performance snapshot', () => {
67+
resetPerformanceMetricsForTesting();
68+
69+
markTimingValue('app_ready_ms', 12);
70+
markTimingValue('window_created_ms', 45);
71+
markTimingValue('first_paint_ms', 90);
72+
markTimingValue('first_interactive_ms', 130);
73+
markTimingValue('selected_engine_detect_ms', 18);
74+
markTimingValue('selected_engine_ready_ms', 18);
75+
76+
expect(getPerformanceSnapshot().startupTimings).toMatchObject({
77+
app_ready_ms: 12,
78+
window_created_ms: 45,
79+
first_paint_ms: 90,
80+
first_interactive_ms: 130,
81+
selected_engine_detect_ms: 18,
82+
selected_engine_ready_ms: 18,
83+
});
84+
});
85+
6686
test('keeps a bounded slow DB operation ring buffer', () => {
6787
resetPerformanceMetricsForTesting();
6888
setDbSlowThresholdForTesting(1);

src/main/main.ts

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,23 @@ const resolveCoworkAgentEngine = (): CoworkAgentEngine => {
14271427
return isCoworkAgentEngine(configured) ? configured : CoworkAgentEngineValue.YdCowork;
14281428
};
14291429

1430+
const ensureSelectedEngineReadyForStartup = async (engine: CoworkAgentEngine): Promise<void> => {
1431+
if (isOpenClawCoworkAgentEngine(engine)) {
1432+
const status = await getOpenClawEngineManager().ensureReady();
1433+
if (status.phase === 'error' || status.phase === 'not_installed') {
1434+
throw new Error(status.message || 'OpenClaw CLI is not ready.');
1435+
}
1436+
return;
1437+
}
1438+
1439+
if (engine === CoworkAgentEngineValue.Hermes) {
1440+
const status = await getHermesEngineManager().ensureReady();
1441+
if (status.phase === 'error' || status.phase === 'not_installed') {
1442+
throw new Error(status.message || 'Hermes Agent CLI is not ready.');
1443+
}
1444+
}
1445+
};
1446+
14301447
type FeishuIMAgentEngine =
14311448
| typeof CoworkAgentEngineValue.OpenClaw
14321449
| typeof CoworkAgentEngineValue.Hermes
@@ -1676,6 +1693,13 @@ const isExternalAgentProviderAppType = (value: unknown): value is ExternalAgentP
16761693
|| value === 'deepseek_tui'
16771694
);
16781695

1696+
const normalizeAgentEngineSnapshotAppTypes = (value: unknown): ExternalAgentProviderAppType[] => {
1697+
if (!Array.isArray(value)) {
1698+
return [];
1699+
}
1700+
return Array.from(new Set(value.filter(isExternalAgentProviderAppType)));
1701+
};
1702+
16791703
const AGENT_ENGINE_SNAPSHOT_TTL_MS = 30_000;
16801704

16811705
interface AgentEngineSnapshotResponse {
@@ -1774,6 +1798,20 @@ const getCachedAgentEngineSnapshot = (options: { forceRefresh?: boolean } = {}):
17741798
};
17751799
};
17761800

1801+
const getFilteredAgentEngineSnapshot = async (
1802+
appTypes: ExternalAgentProviderAppType[],
1803+
): Promise<AgentEngineSnapshotResponse> => {
1804+
const { snapshot, report } = await getExternalAgentEnvironmentSnapshot({ appTypes });
1805+
const mergedSnapshot = mergeCodexAppStatus(snapshot);
1806+
summarizeAgentEngineProbeReport(report);
1807+
return {
1808+
success: true,
1809+
snapshot: mergedSnapshot,
1810+
refreshing: false,
1811+
cachedAt: Date.now(),
1812+
};
1813+
};
1814+
17771815
const getOpenClawConfigSync = (): OpenClawConfigSync => {
17781816
if (!openClawConfigSync) {
17791817
openClawConfigSync = new OpenClawConfigSync({
@@ -5110,8 +5148,12 @@ if (!gotTheLock) {
51105148
}
51115149
});
51125150

5113-
ipcMain.handle('cowork:agentEngines:list', async (_event, input: { forceRefresh?: unknown } = {}) => {
5151+
ipcMain.handle('cowork:agentEngines:list', async (_event, input: { forceRefresh?: unknown; appTypes?: unknown } = {}) => {
51145152
try {
5153+
const appTypes = normalizeAgentEngineSnapshotAppTypes(input?.appTypes);
5154+
if (appTypes.length > 0) {
5155+
return await getFilteredAgentEngineSnapshot(appTypes);
5156+
}
51155157
return getCachedAgentEngineSnapshot({ forceRefresh: input?.forceRefresh === true });
51165158
} catch (error) {
51175159
return {
@@ -7677,6 +7719,7 @@ if (!gotTheLock) {
76777719
| 'enterprise_sync'
76787720
| 'runtime_forwarders'
76797721
| 'openclaw_config_sync'
7722+
| 'openclaw_proxy_config_sync'
76807723
| 'hermes_config_sync'
76817724
| 'selected_engine'
76827725
| 'scheduled_tasks'
@@ -7704,6 +7747,7 @@ if (!gotTheLock) {
77047747
'enterprise_sync',
77057748
'runtime_forwarders',
77067749
'openclaw_config_sync',
7750+
'openclaw_proxy_config_sync',
77077751
'hermes_config_sync',
77087752
'selected_engine',
77097753
'scheduled_tasks',
@@ -8060,12 +8104,13 @@ if (!gotTheLock) {
80608104
}, { degradedOnError: true });
80618105
await runStartupService('selected_engine', async () => {
80628106
const selectedEngineDetectStartedAt = nowMs();
8063-
const selectedEngine = resolveCoworkAgentEngine();
8064-
markTiming('selected_engine_detect_ms', selectedEngineDetectStartedAt);
8065-
if (isOpenClawCoworkAgentEngine(selectedEngine)) {
8066-
await ensureOpenClawRunningForCowork();
8107+
try {
8108+
const selectedEngine = resolveCoworkAgentEngine();
8109+
await ensureSelectedEngineReadyForStartup(selectedEngine);
8110+
} finally {
8111+
markTiming('selected_engine_detect_ms', selectedEngineDetectStartedAt);
8112+
markTiming('selected_engine_ready_ms', selectedEngineDetectStartedAt);
80678113
}
8068-
markTiming('selected_engine_ready_ms', selectedEngineDetectStartedAt);
80698114
}, { degradedOnError: true });
80708115

80718116
await runStartupService('scheduled_tasks', () => {
@@ -8128,14 +8173,16 @@ if (!gotTheLock) {
81288173

81298174
// Re-sync OpenClaw config after proxy is ready so that providers that route
81308175
// through the proxy (e.g. github-copilot) get the correct baseUrl.
8131-
if (isOpenClawCoworkAgentEngine(resolveCoworkAgentEngine())) {
8132-
const proxyResync = await syncOpenClawConfig({
8133-
reason: 'proxy-ready',
8134-
});
8135-
if (proxyResync.changed) {
8136-
console.log('[Main] OpenClaw config updated after proxy ready, gateway will restart to pick up new config');
8176+
await runStartupService('openclaw_proxy_config_sync', async () => {
8177+
if (isOpenClawCoworkAgentEngine(resolveCoworkAgentEngine())) {
8178+
const proxyResync = await syncOpenClawConfig({
8179+
reason: 'proxy-ready',
8180+
});
8181+
if (proxyResync.changed) {
8182+
console.log('[Main] OpenClaw config updated after proxy ready, gateway will restart to pick up new config');
8183+
}
81378184
}
8138-
}
8185+
}, { degradedOnError: true });
81398186

81408187
// Auto-reconnect IM bots that were enabled before restart.
81418188
await runStartupService('im_gateways', async () => {

src/main/preload.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,10 @@ contextBridge.exposeInMainWorld('electron', {
351351
memoryUserMemoriesMaxItems?: number;
352352
}) =>
353353
ipcRenderer.invoke('cowork:config:set', config),
354-
listAgentEngines: (input?: { forceRefresh?: boolean }) =>
354+
listAgentEngines: (input?: {
355+
forceRefresh?: boolean;
356+
appTypes?: Array<'claude' | 'codex' | 'hermes' | 'openclaw' | 'opencode' | 'grok' | 'qwen' | 'deepseek_tui'>;
357+
}) =>
355358
ipcRenderer.invoke('cowork:agentEngines:list', input),
356359
getRuntimeMetricsSummary: (filters: any) =>
357360
ipcRenderer.invoke(CoworkIpcChannel.RuntimeMetricsSummary, filters),

0 commit comments

Comments
 (0)