@yancyyu/openhermit 1.5.8 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +10 -1
  2. package/dist-renderer/assets/{ProjectEditorOverlay-BNoDw9T1.js → ProjectEditorOverlay-C5D83Zxv.js} +1 -1
  3. package/dist-renderer/assets/{TeamGraphOverlay-CfGRKQIu.js → TeamGraphOverlay-ajzuM1-u.js} +1 -1
  4. package/dist-renderer/assets/{_basePickBy-Ct8Hm5_h.js → _basePickBy-C9H2zmVj.js} +1 -1
  5. package/dist-renderer/assets/{_baseUniq-BofrAFBx.js → _baseUniq-CpGZGemc.js} +1 -1
  6. package/dist-renderer/assets/{arc-AbJgatzR.js → arc-CbGBDw-m.js} +1 -1
  7. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-gpniCJVk.js → architectureDiagram-VXUJARFQ-nuKXUIpb.js} +1 -1
  8. package/dist-renderer/assets/{blockDiagram-VD42YOAC-aBbbmONC.js → blockDiagram-VD42YOAC-DHUUE7Jc.js} +1 -1
  9. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DJio1IsU.js → c4Diagram-YG6GDRKO-OILHhqLM.js} +1 -1
  10. package/dist-renderer/assets/channel-DpUKLLrj.js +1 -0
  11. package/dist-renderer/assets/{chunk-4BX2VUAB-D1_HKao2.js → chunk-4BX2VUAB-dqNpZaQ8.js} +1 -1
  12. package/dist-renderer/assets/{chunk-55IACEB6-NAmVxF4k.js → chunk-55IACEB6-BCoSJQM-.js} +1 -1
  13. package/dist-renderer/assets/{chunk-B4BG7PRW-Ce829laz.js → chunk-B4BG7PRW-BLbg8yVR.js} +1 -1
  14. package/dist-renderer/assets/{chunk-DI55MBZ5-Ct2Le12y.js → chunk-DI55MBZ5-CUUWOs1Q.js} +1 -1
  15. package/dist-renderer/assets/{chunk-FMBD7UC4-Cie3DzKk.js → chunk-FMBD7UC4-D9geTN5P.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QN33PNHL-4f5Yb50e.js → chunk-QN33PNHL-BGT8-BRX.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QZHKN3VN-D9ranl9c.js → chunk-QZHKN3VN-CC8ebGaM.js} +1 -1
  18. package/dist-renderer/assets/{chunk-TZMSLE5B-bdGZWlEy.js → chunk-TZMSLE5B-CajekcT6.js} +1 -1
  19. package/dist-renderer/assets/classDiagram-2ON5EDUG-LL85aSlz.js +1 -0
  20. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-LL85aSlz.js +1 -0
  21. package/dist-renderer/assets/clone-BHWsFzFA.js +1 -0
  22. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-C6tvfcVi.js → cose-bilkent-S5V4N54A-C_x7hSy3.js} +1 -1
  23. package/dist-renderer/assets/{dagre-6UL2VRFP-B-4qcZam.js → dagre-6UL2VRFP-C4Y1k4DZ.js} +1 -1
  24. package/dist-renderer/assets/{diagram-PSM6KHXK-CwT3TLjx.js → diagram-PSM6KHXK-oRIeULoh.js} +1 -1
  25. package/dist-renderer/assets/{diagram-QEK2KX5R-BWH6-ZFd.js → diagram-QEK2KX5R-DwSqw5HF.js} +1 -1
  26. package/dist-renderer/assets/{diagram-S2PKOQOG-DfpPnfi1.js → diagram-S2PKOQOG-DqjGYje2.js} +1 -1
  27. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BFbEFR4x.js → erDiagram-Q2GNP2WA-CEV5bBgg.js} +1 -1
  28. package/dist-renderer/assets/{flowDiagram-NV44I4VS-Dg3cf5hW.js → flowDiagram-NV44I4VS-BQQkrRyu.js} +1 -1
  29. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-B21y55W5.js → ganttDiagram-JELNMOA3-CLy4WR1G.js} +1 -1
  30. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BDV3BJzn.js → gitGraphDiagram-V2S2FVAM-6W3ioQu_.js} +1 -1
  31. package/dist-renderer/assets/{graph-BfaZ4hZt.js → graph-BnLKQvhH.js} +1 -1
  32. package/dist-renderer/assets/{index-CCqtDawH.js → index-B4aiRxoU.js} +1 -1
  33. package/dist-renderer/assets/{index-pMg_LlsS.js → index-B8lKqPVq.js} +1 -1
  34. package/dist-renderer/assets/{index-CZltVMDP.js → index-BRuhNKyU.js} +12 -12
  35. package/dist-renderer/assets/{index-BMXHMpkG.js → index-BufvLVIl.js} +1 -1
  36. package/dist-renderer/assets/{index-Ct0-y9TF.js → index-C1xShqKH.js} +1 -1
  37. package/dist-renderer/assets/{index-CVMSpK8C.js → index-zIOLLI7O.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DvMlS0CL.js → infoDiagram-HS3SLOUP-BoBweEEY.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DIyMluRv.js → journeyDiagram-XKPGCS4Q-DLL0V5oP.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CVOx8f-7.js → kanban-definition-3W4ZIXB7-HsFtEDG3.js} +1 -1
  41. package/dist-renderer/assets/{layout-BPKIXUf4.js → layout-ClIooAAq.js} +1 -1
  42. package/dist-renderer/assets/{linear-CScZGLr2.js → linear-r3RJcj8y.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CmDQ7Wo6.js → mindmap-definition-VGOIOE7T-BA_P1U4V.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DbVClin-.js → pieDiagram-ADFJNKIX-CzPAfkTB.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CAB0MYcW.js → quadrantDiagram-AYHSOK5B-PvdPWzFJ.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-w2Lfpg3T.js → requirementDiagram-UZGBJVZJ-CHqIL_Od.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-kvG1QoKY.js → sankeyDiagram-TZEHDZUN-ConzpACM.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DCVBQ23J.js → sequenceDiagram-WL72ISMW-Zryq4oxP.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-ItZ0JBvq.js → stateDiagram-FKZM4ZOC-BA9V7NHF.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-Hpmw4dMm.js → stateDiagram-v2-4FDKWEC3-CGnaujD-.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BzSFaAjV.js → timeline-definition-IT6M3QCI-DPs2ZjMm.js} +1 -1
  52. package/dist-renderer/assets/{treemap-GDKQZRPO-fSz4hQn0.js → treemap-GDKQZRPO-B0lzrLxb.js} +1 -1
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CT1kaGlv.js → xychartDiagram-PRI3JC2R-CINGmMxX.js} +1 -1
  54. package/dist-renderer/index.html +1 -1
  55. package/package.json +2 -1
  56. package/src/main/server.ts +993 -764
  57. package/src/main/services/UpdateService.ts +4 -1
  58. package/src/main/services/ccConnect/CcConnectBridge.ts +1 -8
  59. package/src/main/services/ccConnect/CcConnectClient.ts +7 -2
  60. package/src/main/services/teams-mvp/TeamProvisioningService.ts +14 -4
  61. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +11 -6
  62. package/src/renderer/App.tsx +18 -7
  63. package/src/renderer/api/httpClient.ts +136 -42
  64. package/src/renderer/components/chat/ChatHistory.tsx +11 -8
  65. package/src/renderer/components/dashboard/DashboardView.tsx +4 -2
  66. package/src/renderer/components/extensions/ExtensionStoreView.tsx +2 -7
  67. package/src/renderer/components/layout/Sidebar.tsx +3 -1
  68. package/src/renderer/components/schedules/SchedulesView.tsx +15 -13
  69. package/src/renderer/components/settings/SettingsTabs.tsx +2 -1
  70. package/src/renderer/components/settings/hooks/useSettingsHandlers.ts +4 -5
  71. package/src/renderer/components/settings/sections/AdvancedSection.tsx +19 -4
  72. package/src/renderer/components/settings/sections/CliStatusSection.tsx +63 -59
  73. package/src/renderer/components/settings/sections/GeneralSection.tsx +5 -11
  74. package/src/renderer/components/settings/sections/HarnessSection.tsx +30 -15
  75. package/src/renderer/components/settings/sections/PlatformsSection.tsx +110 -51
  76. package/src/renderer/components/sidebar/SidebarSessions.tsx +100 -67
  77. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +26 -43
  78. package/src/renderer/components/team/CcSessionsSection.tsx +34 -14
  79. package/src/renderer/components/team/TeamDetailView.tsx +150 -148
  80. package/src/renderer/components/team/TeamEmptyState.tsx +27 -16
  81. package/src/renderer/components/team/TeamListView.tsx +4 -2
  82. package/src/renderer/components/team/activity/ActivityItem.tsx +6 -1
  83. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +282 -75
  84. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +2 -1
  85. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +64 -21
  86. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +68 -19
  87. package/src/renderer/components/team/dialogs/ProjectPathSelector.tsx +20 -16
  88. package/src/renderer/components/team/dialogs/platformMeta.ts +66 -11
  89. package/src/renderer/components/team/editor/EditorFileTree.tsx +9 -7
  90. package/src/renderer/components/team/kanban/KanbanBoard.tsx +7 -10
  91. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +1 -3
  92. package/src/renderer/components/team/members/MemberDetailDialog.tsx +1 -5
  93. package/src/renderer/components/team/messages/MessageComposer.tsx +3 -1
  94. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -26
  95. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +1 -3
  96. package/src/renderer/components/team/schedule/ScheduleSection.tsx +9 -10
  97. package/src/renderer/store/slices/scheduleSlice.ts +4 -1
  98. package/src/renderer/store/slices/teamSlice.ts +3 -1
  99. package/src/shared/types/api.ts +70 -21
  100. package/src/shared/utils/leadDetection.ts +5 -1
  101. package/tsconfig.json +26 -0
  102. package/dist-renderer/assets/channel-CZ8sd5Xf.js +0 -1
  103. package/dist-renderer/assets/classDiagram-2ON5EDUG-CMcfSKj5.js +0 -1
  104. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CMcfSKj5.js +0 -1
  105. package/dist-renderer/assets/clone-CMuwA8RV.js +0 -1
@@ -103,7 +103,10 @@ export class UpdateService {
103
103
  return false;
104
104
  }
105
105
 
106
- onProgress?.({ phase: 'downloading', message: `Downloading Hermit ${versionInfo.latestVersion}...` });
106
+ onProgress?.({
107
+ phase: 'downloading',
108
+ message: `Downloading Hermit ${versionInfo.latestVersion}...`,
109
+ });
107
110
 
108
111
  onProgress?.({ phase: 'installing', message: 'Installing update...' });
109
112
  execSync('npm update -g hermit', { cwd: REPO_ROOT, stdio: 'pipe' });
@@ -26,14 +26,7 @@ const logger = createLogger('CcConnectBridge');
26
26
 
27
27
  const RECONNECT_DELAY_MS = 3_000;
28
28
  const PING_INTERVAL_MS = 30_000;
29
- const BRIDGE_CAPABILITIES = [
30
- 'text',
31
- 'buttons',
32
- 'card',
33
- 'typing',
34
- 'update_message',
35
- 'preview',
36
- ];
29
+ const BRIDGE_CAPABILITIES = ['text', 'buttons', 'card', 'typing', 'update_message', 'preview'];
37
30
 
38
31
  export interface CcConnectBridgeEvents {
39
32
  connected: [];
@@ -154,7 +154,9 @@ export class CcConnectClient {
154
154
  logger.info(`cc-connect project "${name}" stopped`);
155
155
  } catch (err) {
156
156
  // Project might already not exist — log but don't throw
157
- logger.warn(`Failed to stop project "${name}": ${err instanceof Error ? err.message : String(err)}`);
157
+ logger.warn(
158
+ `Failed to stop project "${name}": ${err instanceof Error ? err.message : String(err)}`
159
+ );
158
160
  }
159
161
  }
160
162
 
@@ -374,7 +376,10 @@ export class CcConnectClient {
374
376
  return this.request<CcCronJob>('POST', '/api/v1/cron', input);
375
377
  }
376
378
 
377
- async updateCronJob(id: string, patch: Partial<CcCreateCronJobRequest> & { enabled?: boolean }): Promise<CcCronJob> {
379
+ async updateCronJob(
380
+ id: string,
381
+ patch: Partial<CcCreateCronJobRequest> & { enabled?: boolean }
382
+ ): Promise<CcCronJob> {
378
383
  return this.request<CcCronJob>('PATCH', `/api/v1/cron/${encodeURIComponent(id)}`, patch);
379
384
  }
380
385
 
@@ -76,7 +76,9 @@ export class TeamProvisioningService {
76
76
  logger.info(`cc-connect restarted after creating project ${manifest.bindProject}`);
77
77
  }
78
78
  } catch (err) {
79
- logger.warn(`cc-connect project creation failed (team=${slug}): ${err instanceof Error ? err.message : String(err)}`);
79
+ logger.warn(
80
+ `cc-connect project creation failed (team=${slug}): ${err instanceof Error ? err.message : String(err)}`
81
+ );
80
82
  // 不中断流程 — project 可能已存在
81
83
  }
82
84
  }
@@ -194,7 +196,9 @@ export class TeamProvisioningService {
194
196
  `dispatched task ${task.id} → team:${targetSlug} (cc-project:${targetManifest.bindProject})`
195
197
  );
196
198
  } catch (err) {
197
- logger.warn(`dispatchTask failed (target=${targetSlug}): ${err instanceof Error ? err.message : String(err)}`);
199
+ logger.warn(
200
+ `dispatchTask failed (target=${targetSlug}): ${err instanceof Error ? err.message : String(err)}`
201
+ );
198
202
  }
199
203
 
200
204
  // 记录消息到来源团队
@@ -218,7 +222,11 @@ export class TeamProvisioningService {
218
222
  return this.workspace.createTask(teamSlug, payload);
219
223
  }
220
224
 
221
- patchTask(teamSlug: string, taskId: string, patch: Parameters<TeamWorkspaceService['patchTask']>[2]) {
225
+ patchTask(
226
+ teamSlug: string,
227
+ taskId: string,
228
+ patch: Parameters<TeamWorkspaceService['patchTask']>[2]
229
+ ) {
222
230
  return this.workspace.patchTask(teamSlug, taskId, patch);
223
231
  }
224
232
 
@@ -269,7 +277,9 @@ export class TeamProvisioningService {
269
277
  await fs.promises.writeFile(settingsPath, JSON.stringify(updated, null, 2), 'utf8');
270
278
  logger.info(`injected MCP config → ${settingsPath}`);
271
279
  } catch (err) {
272
- logger.warn(`MCP config injection failed (workDir=${workDir}): ${err instanceof Error ? err.message : String(err)}`);
280
+ logger.warn(
281
+ `MCP config injection failed (workDir=${workDir}): ${err instanceof Error ? err.message : String(err)}`
282
+ );
273
283
  }
274
284
  }
275
285
  }
@@ -230,7 +230,9 @@ export class TeamWorkspaceService {
230
230
  try {
231
231
  return await this.readTeamManifest(projectName);
232
232
  } catch {
233
- const match = (await this.listTeams()).find((manifest) => manifest.bindProject === projectName);
233
+ const match = (await this.listTeams()).find(
234
+ (manifest) => manifest.bindProject === projectName
235
+ );
234
236
  if (match) return match;
235
237
  throw new Error(`团队 "${projectName}" 不存在 (${teamsRoot()})`);
236
238
  }
@@ -325,7 +327,11 @@ export class TeamWorkspaceService {
325
327
  const lines = raw.split(/\n+/).filter(Boolean);
326
328
  const all: GroupMessage[] = [];
327
329
  for (const line of lines) {
328
- try { all.push(JSON.parse(line) as GroupMessage); } catch { /* skip */ }
330
+ try {
331
+ all.push(JSON.parse(line) as GroupMessage);
332
+ } catch {
333
+ /* skip */
334
+ }
329
335
  }
330
336
  return all.length <= limit ? all : all.slice(all.length - limit);
331
337
  }
@@ -334,10 +340,9 @@ export class TeamWorkspaceService {
334
340
 
335
341
  private async readBoard(teamSlug: string): Promise<{ tasks: Task[] }> {
336
342
  const storageSlug = await this.resolveStorageSlug(teamSlug);
337
- return readJson<{ tasks: Task[] }>(
338
- path.join(teamRoot(storageSlug), 'tasks', 'board.json'),
339
- { tasks: [] }
340
- );
343
+ return readJson<{ tasks: Task[] }>(path.join(teamRoot(storageSlug), 'tasks', 'board.json'), {
344
+ tasks: [],
345
+ });
341
346
  }
342
347
 
343
348
  private async writeBoard(teamSlug: string, board: { tasks: Task[] }): Promise<void> {
@@ -41,7 +41,9 @@ function buildPathForTab(activeTab: Tab | null): string {
41
41
  }
42
42
  switch (activeTab.type) {
43
43
  case 'team':
44
- return activeTab.teamName ? `/team/${encodeURIComponent(activeTab.teamName)}` : DEFAULT_APP_PATH;
44
+ return activeTab.teamName
45
+ ? `/team/${encodeURIComponent(activeTab.teamName)}`
46
+ : DEFAULT_APP_PATH;
45
47
  case 'teams':
46
48
  return '/teams';
47
49
  case 'settings':
@@ -59,9 +61,7 @@ function buildPathForTab(activeTab: Tab | null): string {
59
61
  case 'notifications':
60
62
  return '/notifications';
61
63
  case 'graph':
62
- return activeTab.teamName
63
- ? `/graph/${encodeURIComponent(activeTab.teamName)}`
64
- : '/graph';
64
+ return activeTab.teamName ? `/graph/${encodeURIComponent(activeTab.teamName)}` : '/graph';
65
65
  case 'report':
66
66
  return activeTab.projectId && activeTab.sessionId
67
67
  ? `/report/${encodeURIComponent(activeTab.projectId)}/${encodeURIComponent(activeTab.sessionId)}`
@@ -87,14 +87,20 @@ function useTeamPersistence() {
87
87
  }
88
88
  }, 500);
89
89
  }
90
- } catch { /* ignore */ }
90
+ } catch {
91
+ /* ignore */
92
+ }
91
93
  }, []);
92
94
 
93
95
  // Save team when it changes
94
96
  useEffect(() => {
95
97
  const unsub = useStore.subscribe((state, prevState) => {
96
98
  if (state.selectedTeamName !== prevState.selectedTeamName && state.selectedTeamName) {
97
- try { localStorage.setItem(PERSIST_KEY, state.selectedTeamName); } catch { /* ignore */ }
99
+ try {
100
+ localStorage.setItem(PERSIST_KEY, state.selectedTeamName);
101
+ } catch {
102
+ /* ignore */
103
+ }
98
104
  }
99
105
  });
100
106
  return unsub;
@@ -161,7 +167,12 @@ function useTabPathPersistence() {
161
167
  break;
162
168
  case 'report':
163
169
  if (arg1 && arg2) {
164
- state.openTab({ type: 'report', label: 'Session Report', projectId: arg1, sessionId: arg2 });
170
+ state.openTab({
171
+ type: 'report',
172
+ label: 'Session Report',
173
+ projectId: arg1,
174
+ sessionId: arg2,
175
+ });
165
176
  }
166
177
  break;
167
178
  default:
@@ -339,11 +339,23 @@ export class HttpAPIClient implements ElectronAPI {
339
339
  getAppVersion = (): Promise<string> => this.get<string>('/api/version');
340
340
 
341
341
  hermitConfig = {
342
- get: async (): Promise<{ ccBaseUrl: string; ccBridgeUrl: string; ccToken: string; ccTokenSet: boolean }> => {
343
- const res = await this.get<{ ok: boolean; data: { ccBaseUrl: string; ccBridgeUrl: string; ccToken: string; ccTokenSet: boolean } }>('/api/hermit-config');
342
+ get: async (): Promise<{
343
+ ccBaseUrl: string;
344
+ ccBridgeUrl: string;
345
+ ccToken: string;
346
+ ccTokenSet: boolean;
347
+ }> => {
348
+ const res = await this.get<{
349
+ ok: boolean;
350
+ data: { ccBaseUrl: string; ccBridgeUrl: string; ccToken: string; ccTokenSet: boolean };
351
+ }>('/api/hermit-config');
344
352
  return res.data;
345
353
  },
346
- update: async (patch: { ccBaseUrl?: string; ccToken?: string; ccBridgeUrl?: string }): Promise<void> => {
354
+ update: async (patch: {
355
+ ccBaseUrl?: string;
356
+ ccToken?: string;
357
+ ccBridgeUrl?: string;
358
+ }): Promise<void> => {
347
359
  await this.post('/api/hermit-config', patch);
348
360
  },
349
361
  getRaw: async (): Promise<{ path: string; content: string }> => {
@@ -363,7 +375,10 @@ export class HttpAPIClient implements ElectronAPI {
363
375
  return res.data;
364
376
  },
365
377
  update: async (patch: Record<string, unknown>): Promise<{ needsRestart: boolean }> => {
366
- const res = await this.post<{ ok: boolean; data: { needsRestart: boolean } }>('/api/cc-config', patch);
378
+ const res = await this.post<{ ok: boolean; data: { needsRestart: boolean } }>(
379
+ '/api/cc-config',
380
+ patch
381
+ );
367
382
  return res.data;
368
383
  },
369
384
  getRaw: async (): Promise<{ path: string; content: string }> => {
@@ -379,7 +394,9 @@ export class HttpAPIClient implements ElectronAPI {
379
394
 
380
395
  ccSettings = {
381
396
  get: async (): Promise<Record<string, unknown>> => {
382
- const res = await this.get<{ ok: boolean; data: Record<string, unknown> }>('/api/cc-settings');
397
+ const res = await this.get<{ ok: boolean; data: Record<string, unknown> }>(
398
+ '/api/cc-settings'
399
+ );
383
400
  return res.data;
384
401
  },
385
402
  patch: async (patch: Record<string, unknown>): Promise<void> => {
@@ -395,57 +412,127 @@ export class HttpAPIClient implements ElectronAPI {
395
412
 
396
413
  // cc-connect setup flows (QR code + manual platform binding)
397
414
  ccSetup = {
398
- feishuBegin: async (): Promise<{ device_code: string; qr_url: string; base_url?: string; interval: number; expires_in: number }> => {
399
- const res = await this.post<{ ok: boolean; data: { device_code: string; qr_url: string; base_url?: string; interval: number; expires_in: number } }>('/api/setup/feishu/begin', {});
415
+ feishuBegin: async (): Promise<{
416
+ device_code: string;
417
+ qr_url: string;
418
+ base_url?: string;
419
+ interval: number;
420
+ expires_in: number;
421
+ }> => {
422
+ const res = await this.post<{
423
+ ok: boolean;
424
+ data: {
425
+ device_code: string;
426
+ qr_url: string;
427
+ base_url?: string;
428
+ interval: number;
429
+ expires_in: number;
430
+ };
431
+ }>('/api/setup/feishu/begin', {});
400
432
  return res.data;
401
433
  },
402
- feishuPoll: async (deviceCode: string, baseUrl?: string): Promise<{
403
- status: string; base_url?: string; app_id?: string; app_secret?: string;
404
- platform?: string; owner_open_id?: string; slow_down?: boolean; error?: string;
434
+ feishuPoll: async (
435
+ deviceCode: string,
436
+ baseUrl?: string
437
+ ): Promise<{
438
+ status: string;
439
+ base_url?: string;
440
+ app_id?: string;
441
+ app_secret?: string;
442
+ platform?: string;
443
+ owner_open_id?: string;
444
+ slow_down?: boolean;
445
+ error?: string;
405
446
  }> => {
406
- const res = await this.post<{ ok: boolean; data: {
407
- status: string; base_url?: string; app_id?: string; app_secret?: string;
408
- platform?: string; owner_open_id?: string; slow_down?: boolean; error?: string;
409
- } }>('/api/setup/feishu/poll', { device_code: deviceCode, base_url: baseUrl });
447
+ const res = await this.post<{
448
+ ok: boolean;
449
+ data: {
450
+ status: string;
451
+ base_url?: string;
452
+ app_id?: string;
453
+ app_secret?: string;
454
+ platform?: string;
455
+ owner_open_id?: string;
456
+ slow_down?: boolean;
457
+ error?: string;
458
+ };
459
+ }>('/api/setup/feishu/poll', { device_code: deviceCode, base_url: baseUrl });
410
460
  return res.data;
411
461
  },
412
462
  feishuSave: async (params: {
413
- project: string; app_id: string; app_secret: string;
414
- platform_type?: string; owner_open_id?: string;
415
- work_dir?: string; agent_type?: string;
463
+ project: string;
464
+ app_id: string;
465
+ app_secret: string;
466
+ platform_type?: string;
467
+ owner_open_id?: string;
468
+ work_dir?: string;
469
+ agent_type?: string;
416
470
  }): Promise<{ message: string; restart_required: boolean }> => {
417
- const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>('/api/setup/feishu/save', params);
471
+ const res = await this.post<{
472
+ ok: boolean;
473
+ data: { message: string; restart_required: boolean };
474
+ }>('/api/setup/feishu/save', params);
418
475
  return res.data;
419
476
  },
420
477
 
421
478
  weixinBegin: async (apiUrl?: string): Promise<{ qr_key: string; qr_url: string }> => {
422
- const res = await this.post<{ ok: boolean; data: { qr_key: string; qr_url: string } }>('/api/setup/weixin/begin', { api_url: apiUrl });
479
+ const res = await this.post<{ ok: boolean; data: { qr_key: string; qr_url: string } }>(
480
+ '/api/setup/weixin/begin',
481
+ { api_url: apiUrl }
482
+ );
423
483
  return res.data;
424
484
  },
425
- weixinPoll: async (qrKey: string, apiUrl?: string): Promise<{
426
- status: string; bot_token?: string; ilink_bot_id?: string;
427
- base_url?: string; ilink_user_id?: string;
485
+ weixinPoll: async (
486
+ qrKey: string,
487
+ apiUrl?: string
488
+ ): Promise<{
489
+ status: string;
490
+ bot_token?: string;
491
+ ilink_bot_id?: string;
492
+ base_url?: string;
493
+ ilink_user_id?: string;
428
494
  }> => {
429
- const res = await this.post<{ ok: boolean; data: {
430
- status: string; bot_token?: string; ilink_bot_id?: string;
431
- base_url?: string; ilink_user_id?: string;
432
- } }>('/api/setup/weixin/poll', { qr_key: qrKey, api_url: apiUrl });
495
+ const res = await this.post<{
496
+ ok: boolean;
497
+ data: {
498
+ status: string;
499
+ bot_token?: string;
500
+ ilink_bot_id?: string;
501
+ base_url?: string;
502
+ ilink_user_id?: string;
503
+ };
504
+ }>('/api/setup/weixin/poll', { qr_key: qrKey, api_url: apiUrl });
433
505
  return res.data;
434
506
  },
435
507
  weixinSave: async (params: {
436
- project: string; token: string; base_url?: string;
437
- ilink_bot_id?: string; ilink_user_id?: string;
438
- work_dir?: string; agent_type?: string;
508
+ project: string;
509
+ token: string;
510
+ base_url?: string;
511
+ ilink_bot_id?: string;
512
+ ilink_user_id?: string;
513
+ work_dir?: string;
514
+ agent_type?: string;
439
515
  }): Promise<{ message: string; restart_required: boolean }> => {
440
- const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>('/api/setup/weixin/save', params);
516
+ const res = await this.post<{
517
+ ok: boolean;
518
+ data: { message: string; restart_required: boolean };
519
+ }>('/api/setup/weixin/save', params);
441
520
  return res.data;
442
521
  },
443
522
 
444
- addPlatform: async (projectName: string, body: {
445
- type: string; options?: Record<string, unknown>;
446
- work_dir?: string; agent_type?: string;
447
- }): Promise<{ message: string; restart_required: boolean }> => {
448
- const res = await this.post<{ ok: boolean; data: { message: string; restart_required: boolean } }>(`/api/projects/${encodeURIComponent(projectName)}/add-platform`, body);
523
+ addPlatform: async (
524
+ projectName: string,
525
+ body: {
526
+ type: string;
527
+ options?: Record<string, unknown>;
528
+ work_dir?: string;
529
+ agent_type?: string;
530
+ }
531
+ ): Promise<{ message: string; restart_required: boolean }> => {
532
+ const res = await this.post<{
533
+ ok: boolean;
534
+ data: { message: string; restart_required: boolean };
535
+ }>(`/api/projects/${encodeURIComponent(projectName)}/add-platform`, body);
449
536
  return res.data;
450
537
  },
451
538
  };
@@ -721,11 +808,14 @@ export class HttpAPIClient implements ElectronAPI {
721
808
  // Fallback: return home directory as default
722
809
  return [process.env.HOME ?? '/'];
723
810
  },
724
- browseFolders: async (dirPath?: string): Promise<{ path: string; dirs: string[]; hasParent: boolean }> => {
725
- const res = await this.post<{ success: boolean; data?: { path: string; dirs: string[]; hasParent: boolean }; error?: string }>(
726
- '/api/config/browse-folders',
727
- { path: dirPath ?? '' }
728
- );
811
+ browseFolders: async (
812
+ dirPath?: string
813
+ ): Promise<{ path: string; dirs: string[]; hasParent: boolean }> => {
814
+ const res = await this.post<{
815
+ success: boolean;
816
+ data?: { path: string; dirs: string[]; hasParent: boolean };
817
+ error?: string;
818
+ }>('/api/config/browse-folders', { path: dirPath ?? '' });
729
819
  if (!res.success) throw new Error(res.error ?? '无法浏览目录');
730
820
  return res.data!;
731
821
  },
@@ -1184,7 +1274,9 @@ export class HttpAPIClient implements ElectronAPI {
1184
1274
  await this.post(`/api/teams/${encodeURIComponent(teamName)}/process-send`, { message });
1185
1275
  },
1186
1276
  setCollaboration: async (teamName: string, collaboration: boolean): Promise<void> => {
1187
- await this.patch(`/api/teams/${encodeURIComponent(teamName)}/collaboration`, { collaboration });
1277
+ await this.patch(`/api/teams/${encodeURIComponent(teamName)}/collaboration`, {
1278
+ collaboration,
1279
+ });
1188
1280
  },
1189
1281
  processAlive: async (teamName: string): Promise<boolean> => {
1190
1282
  try {
@@ -1464,7 +1556,9 @@ export class HttpAPIClient implements ElectronAPI {
1464
1556
  );
1465
1557
  },
1466
1558
  cancelSession: async (teamName: string, sessionId: string): Promise<void> =>
1467
- await this.delete(`/api/teams/${encodeURIComponent(teamName)}/sessions/${encodeURIComponent(sessionId)}`),
1559
+ await this.delete(
1560
+ `/api/teams/${encodeURIComponent(teamName)}/sessions/${encodeURIComponent(sessionId)}`
1561
+ ),
1468
1562
  onTeamChange: (callback: (event: unknown, data: TeamChangeEvent) => void): (() => void) => {
1469
1563
  return this.addEventListener('team-change', (data: unknown) =>
1470
1564
  callback(null, data as TeamChangeEvent)
@@ -278,14 +278,17 @@ export const ChatHistory = ({ tabId }: ChatHistoryProps): JSX.Element => {
278
278
  }
279
279
  }, [pageFromLatest, totalConversationPages]);
280
280
 
281
- const goToConversationPage = useCallback((nextPageFromLatest: number): void => {
282
- setPageFromLatest(Math.max(0, Math.min(nextPageFromLatest, totalConversationPages - 1)));
283
- requestAnimationFrame(() => {
284
- if (scrollContainerRef.current) {
285
- scrollContainerRef.current.scrollTop = 0;
286
- }
287
- });
288
- }, [totalConversationPages]);
281
+ const goToConversationPage = useCallback(
282
+ (nextPageFromLatest: number): void => {
283
+ setPageFromLatest(Math.max(0, Math.min(nextPageFromLatest, totalConversationPages - 1)));
284
+ requestAnimationFrame(() => {
285
+ if (scrollContainerRef.current) {
286
+ scrollContainerRef.current.scrollTop = 0;
287
+ }
288
+ });
289
+ },
290
+ [totalConversationPages]
291
+ );
289
292
 
290
293
  const setSearchQueryForTab = useCallback(
291
294
  (query: string): void => {
@@ -124,7 +124,8 @@ export const DashboardView = (): React.JSX.Element => {
124
124
  teamsLoading: state.teamsLoading,
125
125
  }))
126
126
  );
127
- const showQuickstartGuide = searchQuery.trim().length === 0 && !teamsLoading && teams.length === 0;
127
+ const showQuickstartGuide =
128
+ searchQuery.trim().length === 0 && !teamsLoading && teams.length === 0;
128
129
 
129
130
  return (
130
131
  <div className="relative flex-1 overflow-auto bg-surface">
@@ -144,7 +145,8 @@ export const DashboardView = (): React.JSX.Element => {
144
145
  Hermit:一人公司的 AI 团队控制台
145
146
  </h1>
146
147
  <p className="mt-2 text-sm text-text-secondary">
147
- 几乎覆盖所有主流 Harness,支持全渠道接入,把团队编排、消息协作、任务推进和运行状态放在同一个工作台。
148
+ 几乎覆盖所有主流
149
+ Harness,支持全渠道接入,把团队编排、消息协作、任务推进和运行状态放在同一个工作台。
148
150
  </p>
149
151
  </div>
150
152
  <div className="grid gap-4 px-6 py-5 md:grid-cols-3">
@@ -264,10 +264,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
264
264
  projectPath,
265
265
  ]);
266
266
 
267
- const isRefreshing =
268
- effectiveCliStatusLoading ||
269
- mcpBrowseLoading ||
270
- skillsLoading;
267
+ const isRefreshing = effectiveCliStatusLoading || mcpBrowseLoading || skillsLoading;
271
268
  const mcpMutationDisableReason = useMemo(
272
269
  () =>
273
270
  getExtensionActionDisableReason({
@@ -507,9 +504,7 @@ export const ExtensionStoreView = (): React.JSX.Element => {
507
504
  )}
508
505
  <Tabs
509
506
  value={tabState.activeSubTab}
510
- onValueChange={(v) =>
511
- tabState.setActiveSubTab(v as 'mcp-servers' | 'skills')
512
- }
507
+ onValueChange={(v) => tabState.setActiveSubTab(v as 'mcp-servers' | 'skills')}
513
508
  >
514
509
  <div className="-mx-6 flex items-end justify-between border-b border-border px-6">
515
510
  <TabsList className="gap-1 rounded-b-none">
@@ -127,7 +127,9 @@ export const Sidebar = (): React.JSX.Element => {
127
127
  aria-controls="sidebar-workspace-panel"
128
128
  id="sidebar-tab-workspace"
129
129
  className={`relative px-3 py-1.5 text-[11px] font-medium transition-colors ${
130
- sidebarTab === 'workspace' ? 'text-text' : 'text-text-muted hover:text-text-secondary'
130
+ sidebarTab === 'workspace'
131
+ ? 'text-text'
132
+ : 'text-text-muted hover:text-text-secondary'
131
133
  }`}
132
134
  style={
133
135
  sidebarTab === 'workspace'
@@ -143,7 +143,11 @@ const ScheduleListItem = ({
143
143
  onClick={() => onDelete(schedule.id)}
144
144
  disabled={deleting}
145
145
  >
146
- {deleting ? <Loader2 className="size-3.5 animate-spin" /> : <Trash2 className="size-3.5" />}
146
+ {deleting ? (
147
+ <Loader2 className="size-3.5 animate-spin" />
148
+ ) : (
149
+ <Trash2 className="size-3.5" />
150
+ )}
147
151
  </Button>
148
152
  </TooltipTrigger>
149
153
  <TooltipContent side="top">删除</TooltipContent>
@@ -295,18 +299,16 @@ export const SchedulesView = (): React.JSX.Element => {
295
299
  // Filter by search query
296
300
  if (searchQuery.trim()) {
297
301
  const query = searchQuery.toLowerCase();
298
- result = result.filter(
299
- (s) => {
300
- const teamDisplayName = getTeamDisplayName(s.teamName).toLowerCase();
301
- return (
302
- (s.label ?? '').toLowerCase().includes(query) ||
303
- teamDisplayName.includes(query) ||
304
- s.teamName.toLowerCase().includes(query) ||
305
- s.launchConfig.prompt.toLowerCase().includes(query) ||
306
- getCronDescription(s.cronExpression).toLowerCase().includes(query)
307
- );
308
- }
309
- );
302
+ result = result.filter((s) => {
303
+ const teamDisplayName = getTeamDisplayName(s.teamName).toLowerCase();
304
+ return (
305
+ (s.label ?? '').toLowerCase().includes(query) ||
306
+ teamDisplayName.includes(query) ||
307
+ s.teamName.toLowerCase().includes(query) ||
308
+ s.launchConfig.prompt.toLowerCase().includes(query) ||
309
+ getCronDescription(s.cronExpression).toLowerCase().includes(query)
310
+ );
311
+ });
310
312
  }
311
313
 
312
314
  // Sort: active first, then by next run ascending
@@ -35,7 +35,8 @@ const tabs: TabConfig[] = [
35
35
  id: 'channels',
36
36
  label: '渠道',
37
37
  icon: PlugZap,
38
- description: '管理飞书、微信、Telegram 等消息平台的接入配置。每个 cc-connect 项目可绑定一个或多个渠道。',
38
+ description:
39
+ '管理飞书、微信、Telegram 等消息平台的接入配置。每个 cc-connect 项目可绑定一个或多个渠道。',
39
40
  },
40
41
  {
41
42
  id: 'harness',
@@ -105,11 +105,10 @@ export function useSettingsHandlers({
105
105
  (value: string) => {
106
106
  fireAndForgetConfigUpdate('general', { agentLanguage: value });
107
107
  // Sync to cc-connect: map 'system' → browser primary language code
108
- const ccLang =
109
- value === 'system'
110
- ? (navigator.language.split('-')[0] ?? 'zh')
111
- : value;
112
- api.ccSettings.patch({ language: ccLang }).catch(() => {/* best-effort */});
108
+ const ccLang = value === 'system' ? (navigator.language.split('-')[0] ?? 'zh') : value;
109
+ api.ccSettings.patch({ language: ccLang }).catch(() => {
110
+ /* best-effort */
111
+ });
113
112
  },
114
113
  [fireAndForgetConfigUpdate]
115
114
  );
@@ -2,7 +2,13 @@ import { useCallback, useEffect, useState } from 'react';
2
2
 
3
3
  import { api } from '@renderer/api';
4
4
  import { Button } from '@renderer/components/ui/button';
5
- import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '@renderer/components/ui/dialog';
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ } from '@renderer/components/ui/dialog';
6
12
  import { Textarea } from '@renderer/components/ui/textarea';
7
13
  import appIcon from '@renderer/favicon.png';
8
14
  import { Check, FileEdit, Loader2, RefreshCw, RotateCcw, X } from 'lucide-react';
@@ -67,7 +73,12 @@ const CcConnectConfigRawDialog = ({
67
73
  if (!open) return null;
68
74
 
69
75
  return (
70
- <Dialog open={open} onOpenChange={(next) => { if (!next) onClose(); }}>
76
+ <Dialog
77
+ open={open}
78
+ onOpenChange={(next) => {
79
+ if (!next) onClose();
80
+ }}
81
+ >
71
82
  <DialogContent className="max-w-4xl">
72
83
  <DialogHeader>
73
84
  <DialogTitle>编辑 配置</DialogTitle>
@@ -93,7 +104,8 @@ const CcConnectConfigRawDialog = ({
93
104
  </div>
94
105
  )}
95
106
  <p className="text-xs text-[var(--color-text-muted)]">
96
- 保存后将直接覆盖 Hermit 管理的 cc-connect config.toml。若修改了端口或 token,请点击“重启服务”生效。
107
+ 保存后将直接覆盖 Hermit 管理的 cc-connect config.toml。若修改了端口或
108
+ token,请点击“重启服务”生效。
97
109
  </p>
98
110
  </div>
99
111
  <DialogFooter>
@@ -207,7 +219,10 @@ export const AdvancedSection = ({}: AdvancedSectionProps): React.JSX.Element =>
207
219
  </button>
208
220
  </div>
209
221
  {restartMsg && (
210
- <p className="mb-2 text-xs" style={{ color: restartMsg.includes('失败') ? '#f87171' : '#4ade80' }}>
222
+ <p
223
+ className="mb-2 text-xs"
224
+ style={{ color: restartMsg.includes('失败') ? '#f87171' : '#4ade80' }}
225
+ >
211
226
  {restartMsg}
212
227
  </p>
213
228
  )}