@yancyyu/openhermit 1.6.25 → 1.6.27
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.
- package/README.md +127 -77
- package/bin/hermit.mjs +151 -62
- package/dist-renderer/assets/{ProjectEditorOverlay-CZ1LI0pd.js → ProjectEditorOverlay-BBwYdXPv.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-DvyxOPvU.js → TeamGraphOverlay-DVq8rt6_.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-D8IKEBh4.js → _basePickBy-ZbF0pKvS.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-BDBpSAHY.js → _baseUniq-BBLBOeXc.js} +1 -1
- package/dist-renderer/assets/{arc-CZagJLek.js → arc-wGaEgkCf.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-Cq083Uu6.js → architectureDiagram-VXUJARFQ-BpMkdC35.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-CIEANvSv.js → blockDiagram-VD42YOAC-C8Z1xhG4.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-Ca4h_BlA.js → c4Diagram-YG6GDRKO-CJmlw9LA.js} +1 -1
- package/dist-renderer/assets/channel-DJUrwVrK.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-DW9HjNgA.js → chunk-4BX2VUAB-CHPHiRPP.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-BddOEwBk.js → chunk-55IACEB6-DyVohOQb.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-Cmu8IjLN.js → chunk-B4BG7PRW-p5bffh_R.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-BU7nEH8e.js → chunk-DI55MBZ5-BnfGPSUu.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-BZNeq0CB.js → chunk-FMBD7UC4-B6SCKseX.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-DMb3gYzN.js → chunk-QN33PNHL-L12RvLBR.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-8wSSvqz0.js → chunk-QZHKN3VN-DeH1Kxge.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-B4OLgw45.js → chunk-TZMSLE5B-BWnjzSlI.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-blc3DrH7.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-blc3DrH7.js +1 -0
- package/dist-renderer/assets/clone-BftjWakJ.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CS9moNeT.js → cose-bilkent-S5V4N54A-BtzoT5fu.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-3pzdMwjZ.js → dagre-6UL2VRFP-CBBvuoUD.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-CGTnMY5C.js → diagram-PSM6KHXK-Be9BAKws.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-Cyzp8dOw.js → diagram-QEK2KX5R-BDS4PI_i.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-D_5SvdXI.js → diagram-S2PKOQOG-2Rameaq7.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-bWPYfs9c.js → erDiagram-Q2GNP2WA-CSIzCEZD.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-C5z47CKB.js → flowDiagram-NV44I4VS-ForEIVM5.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-eye9Cv6S.js → ganttDiagram-JELNMOA3-BJrli_xr.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DekQZoF8.js → gitGraphDiagram-V2S2FVAM-C_4GuLno.js} +1 -1
- package/dist-renderer/assets/{graph-kNnl-9Fl.js → graph-B1EAT_gw.js} +1 -1
- package/dist-renderer/assets/index-CWpFqEvz.css +1 -0
- package/dist-renderer/assets/{index-hGBnMHVl.js → index-DOA_jbYb.js} +1 -1
- package/dist-renderer/assets/{index-ATiHUmmE.js → index-DR602dwJ.js} +1 -1
- package/dist-renderer/assets/{index-CrOkTuIK.js → index-DYdseEwc.js} +532 -532
- package/dist-renderer/assets/{index-CnxDIJh8.js → index-Dwr5wu5x.js} +1 -1
- package/dist-renderer/assets/{index-CI2l57ID.js → index-eKRmS5kI.js} +1 -1
- package/dist-renderer/assets/{index-DB0k9yRL.js → index-k4tnOFC5.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D3ilrGOS.js → infoDiagram-HS3SLOUP-DjI0uaMz.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BOCG2Rrk.js → journeyDiagram-XKPGCS4Q-jQ6Thae-.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-hZ03nCkx.js → kanban-definition-3W4ZIXB7-CKw6InbL.js} +1 -1
- package/dist-renderer/assets/{layout-D5Mqy2My.js → layout-Dad20y3V.js} +1 -1
- package/dist-renderer/assets/{linear-Clp9OpXU.js → linear-vMgo_2Cv.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-D-w-qhNz.js → mindmap-definition-VGOIOE7T-DYp6YoHL.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D9K0-ecY.js → pieDiagram-ADFJNKIX-BytBecG9.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-B2f19Ki3.js → quadrantDiagram-AYHSOK5B-RUaspLsc.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-VezNpCaU.js → requirementDiagram-UZGBJVZJ-rR2B1Use.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-BpHjEoR9.js → sankeyDiagram-TZEHDZUN-BJi5qYhq.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-Tdb7IZ4t.js → sequenceDiagram-WL72ISMW-BM-wggUb.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-BNA5Q-zz.js → stateDiagram-FKZM4ZOC-BqmcVjnj.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-w_4GEN2a.js → stateDiagram-v2-4FDKWEC3-By3JDVbB.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BghtWMWY.js → timeline-definition-IT6M3QCI-szH0GUyk.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-BVquWjHf.js → treemap-GDKQZRPO-BCMlh-Ex.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-BSV5wEDU.js → xychartDiagram-PRI3JC2R-dwDpvw0w.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +2 -2
- package/src/main/server.ts +52 -12
- package/src/renderer/api/httpClient.ts +1 -1
- package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +2 -0
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +2 -0
- package/src/renderer/components/settings/sections/CliStatusSection.tsx +2 -0
- package/src/renderer/components/settings/sections/HarnessSection.tsx +11 -0
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +10 -2
- package/src/renderer/components/team/TeamDetailView.tsx +53 -42
- package/src/renderer/components/team/TeamListView.tsx +54 -31
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +123 -0
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +98 -2
- package/src/renderer/utils/openHermitEvents.ts +9 -0
- package/src/shared/types/team.ts +5 -0
- package/dist-renderer/assets/channel-CqQK8EX1.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-9A3Cg8IA.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-9A3Cg8IA.js +0 -1
- package/dist-renderer/assets/clone-Bnr-WjeU.js +0 -1
- package/dist-renderer/assets/index-C4x095x4.css +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yancyyu/openhermit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.27",
|
|
5
5
|
"description": "openHermit: team-oriented agent management workbench atop cc-connect.",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
7
7
|
"author": {
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"@tiptap/pm": "^3.20.4",
|
|
108
108
|
"@tiptap/react": "^3.20.4",
|
|
109
109
|
"@tiptap/starter-kit": "^3.20.4",
|
|
110
|
-
"cc-connect": "
|
|
110
|
+
"cc-connect": "1.3.3-beta.3",
|
|
111
111
|
"class-variance-authority": "^0.7.1",
|
|
112
112
|
"clsx": "^2.1.1",
|
|
113
113
|
"cmdk": "1.0.4",
|
package/src/main/server.ts
CHANGED
|
@@ -50,16 +50,6 @@ import { TaskDispatchService } from './services/teams-mvp/TaskDispatchService';
|
|
|
50
50
|
import type { TaskBusConfig } from '@shared/types/team';
|
|
51
51
|
import { UpdateService } from './services/UpdateService';
|
|
52
52
|
|
|
53
|
-
process.on('uncaughtException', (err) => {
|
|
54
|
-
console.error('[openHermit:server] uncaughtException', err);
|
|
55
|
-
process.exit(1);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
process.on('unhandledRejection', (reason) => {
|
|
59
|
-
console.error('[openHermit:server] unhandledRejection', reason);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
53
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
64
54
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
|
65
55
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
@@ -692,7 +682,17 @@ app.patch<{ Body: Record<string, unknown> }>('/api/cc-settings', async (request)
|
|
|
692
682
|
app.post('/api/cc-restart', async () => {
|
|
693
683
|
try {
|
|
694
684
|
await cc.restart();
|
|
695
|
-
|
|
685
|
+
// Wait for cc-connect to come back (restart only signals, process respawns async)
|
|
686
|
+
for (let i = 0; i < 30; i++) {
|
|
687
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
688
|
+
try {
|
|
689
|
+
await cc.listProjects();
|
|
690
|
+
return { ok: true };
|
|
691
|
+
} catch {
|
|
692
|
+
/* not back yet */
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return reply500(new Error('cc-connect did not come back within 30s'));
|
|
696
696
|
} catch (err) {
|
|
697
697
|
return reply500(err);
|
|
698
698
|
}
|
|
@@ -774,7 +774,9 @@ app.get('/api/teams', async () => {
|
|
|
774
774
|
};
|
|
775
775
|
})
|
|
776
776
|
);
|
|
777
|
-
return summaries.filter(
|
|
777
|
+
return summaries.filter(
|
|
778
|
+
(team) => team.pendingDelete !== true && team.teamName !== 'my-project'
|
|
779
|
+
);
|
|
778
780
|
} catch {
|
|
779
781
|
return [];
|
|
780
782
|
}
|
|
@@ -816,6 +818,16 @@ app.post('/api/teams/create', async (request, reply) => {
|
|
|
816
818
|
request.log.warn({ err, teamName: name }, 'failed to persist local team metadata');
|
|
817
819
|
}
|
|
818
820
|
|
|
821
|
+
// Bind provider refs if specified
|
|
822
|
+
const providerRefs = Array.isArray(body.providerRefs) ? (body.providerRefs as string[]) : [];
|
|
823
|
+
if (providerRefs.length > 0) {
|
|
824
|
+
try {
|
|
825
|
+
await cc.setProviderRefs(name, providerRefs);
|
|
826
|
+
} catch (err) {
|
|
827
|
+
request.log.warn({ err, teamName: name, providerRefs }, 'failed to set provider refs');
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
819
831
|
return { ok: true, teamName: name, runId: null };
|
|
820
832
|
} catch (err) {
|
|
821
833
|
return reply.code(500).send({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -915,6 +927,10 @@ app.get<{ Params: { name: string } }>('/api/teams/:name/data', async (request, r
|
|
|
915
927
|
typeof p.agent_mode === 'string' && p.agent_mode.trim().length > 0
|
|
916
928
|
? p.agent_mode.trim()
|
|
917
929
|
: permissionMode;
|
|
930
|
+
const [providerRefs, globalProviders] = await Promise.all([
|
|
931
|
+
cc.getProviderRefs(name).catch(() => []),
|
|
932
|
+
cc.listProviders().catch(() => []),
|
|
933
|
+
]);
|
|
918
934
|
|
|
919
935
|
return {
|
|
920
936
|
teamName: name,
|
|
@@ -955,6 +971,8 @@ app.get<{ Params: { name: string } }>('/api/teams/:name/data', async (request, r
|
|
|
955
971
|
description,
|
|
956
972
|
workDir: p.work_dir ?? workDir,
|
|
957
973
|
permissionMode: resolvedPermissionMode,
|
|
974
|
+
providerRefs,
|
|
975
|
+
globalProviders,
|
|
958
976
|
settings: {
|
|
959
977
|
...projectSettings,
|
|
960
978
|
language: resolvedLanguage,
|
|
@@ -1009,6 +1027,8 @@ app.get<{ Params: { name: string } }>('/api/teams/:name/data', async (request, r
|
|
|
1009
1027
|
description,
|
|
1010
1028
|
workDir,
|
|
1011
1029
|
permissionMode,
|
|
1030
|
+
providerRefs: [],
|
|
1031
|
+
globalProviders: [],
|
|
1012
1032
|
heartbeat: null,
|
|
1013
1033
|
settings: {
|
|
1014
1034
|
language,
|
|
@@ -1042,6 +1062,9 @@ app.delete<{ Params: { name: string }; Querystring: { deleteFiles?: string } }>(
|
|
|
1042
1062
|
'/api/teams/:name',
|
|
1043
1063
|
async (request, reply) => {
|
|
1044
1064
|
const teamName = request.params.name;
|
|
1065
|
+
if (teamName === 'default' || teamName === 'my-project') {
|
|
1066
|
+
return reply.code(403).send({ error: '该团队不可删除' });
|
|
1067
|
+
}
|
|
1045
1068
|
try {
|
|
1046
1069
|
let restartRequired = false;
|
|
1047
1070
|
try {
|
|
@@ -3406,6 +3429,9 @@ async function applyTeamConfigUpdate(
|
|
|
3406
3429
|
const disabledCommands = Array.isArray(body.disabledCommands)
|
|
3407
3430
|
? normalizeStringArray(body.disabledCommands)
|
|
3408
3431
|
: undefined;
|
|
3432
|
+
const providerRefs = Array.isArray(body.providerRefs)
|
|
3433
|
+
? normalizeStringArray(body.providerRefs)
|
|
3434
|
+
: undefined;
|
|
3409
3435
|
const platformAllowFrom = body.platformAllowFrom
|
|
3410
3436
|
? normalizePlatformAllowFrom(body.platformAllowFrom)
|
|
3411
3437
|
: undefined;
|
|
@@ -3470,6 +3496,13 @@ async function applyTeamConfigUpdate(
|
|
|
3470
3496
|
ccSyncError = err instanceof Error ? err.message : String(err);
|
|
3471
3497
|
}
|
|
3472
3498
|
}
|
|
3499
|
+
if (providerRefs !== undefined) {
|
|
3500
|
+
try {
|
|
3501
|
+
await cc.setProviderRefs(teamName, providerRefs);
|
|
3502
|
+
} catch (err) {
|
|
3503
|
+
ccSyncError = err instanceof Error ? err.message : String(err);
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3473
3506
|
|
|
3474
3507
|
return {
|
|
3475
3508
|
name: name || teamName,
|
|
@@ -3486,6 +3519,7 @@ async function applyTeamConfigUpdate(
|
|
|
3486
3519
|
replyFooter: replyFooter ?? false,
|
|
3487
3520
|
injectSender: injectSender ?? false,
|
|
3488
3521
|
platformAllowFrom: platformAllowFrom ?? {},
|
|
3522
|
+
providerRefs: providerRefs ?? [],
|
|
3489
3523
|
ccSyncError,
|
|
3490
3524
|
};
|
|
3491
3525
|
}
|
|
@@ -3557,6 +3591,10 @@ app.get<{ Params: { name: string } }>('/api/teams/:name/config', async (request,
|
|
|
3557
3591
|
typeof p.agent_mode === 'string' && p.agent_mode.trim().length > 0
|
|
3558
3592
|
? p.agent_mode.trim()
|
|
3559
3593
|
: permissionMode;
|
|
3594
|
+
const [providerRefs, globalProviders] = await Promise.all([
|
|
3595
|
+
cc.getProviderRefs(name).catch(() => []),
|
|
3596
|
+
cc.listProviders().catch(() => []),
|
|
3597
|
+
]);
|
|
3560
3598
|
return {
|
|
3561
3599
|
name,
|
|
3562
3600
|
color,
|
|
@@ -3572,6 +3610,8 @@ app.get<{ Params: { name: string } }>('/api/teams/:name/config', async (request,
|
|
|
3572
3610
|
injectSender: resolvedInjectSender,
|
|
3573
3611
|
permissionMode: resolvedPermissionMode,
|
|
3574
3612
|
platformAllowFrom: resolvedPlatformAllowFrom,
|
|
3613
|
+
providerRefs,
|
|
3614
|
+
globalProviders,
|
|
3575
3615
|
settings: {
|
|
3576
3616
|
...projectSettings,
|
|
3577
3617
|
language: resolvedLanguage,
|
|
@@ -278,7 +278,7 @@ export class HttpAPIClient implements ElectronAPI {
|
|
|
278
278
|
try {
|
|
279
279
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
280
280
|
method: 'DELETE',
|
|
281
|
-
headers: { 'Content-Type': 'application/json' },
|
|
281
|
+
headers: body ? { 'Content-Type': 'application/json' } : undefined,
|
|
282
282
|
body: body ? JSON.stringify(body) : undefined,
|
|
283
283
|
signal: controller.signal,
|
|
284
284
|
});
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DialogTitle,
|
|
11
11
|
} from '@renderer/components/ui/dialog';
|
|
12
12
|
import { Input } from '@renderer/components/ui/input';
|
|
13
|
+
import { emitOpenHermitEvent, OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
13
14
|
import { Loader2, RefreshCw } from 'lucide-react';
|
|
14
15
|
|
|
15
16
|
import type { CliProviderId, CliProviderStatus } from '@shared/types';
|
|
@@ -127,6 +128,7 @@ export const ProviderRuntimeSettingsDialog = ({
|
|
|
127
128
|
setNewProviderBaseUrl('');
|
|
128
129
|
setNewProviderApiKey('');
|
|
129
130
|
await refreshProviders();
|
|
131
|
+
emitOpenHermitEvent(OPEN_HERMIT_EVENTS.providersChanged);
|
|
130
132
|
} catch (err) {
|
|
131
133
|
setAddError(err instanceof Error ? err.message : '添加 Provider 失败');
|
|
132
134
|
} finally {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DialogTitle,
|
|
11
11
|
} from '@renderer/components/ui/dialog';
|
|
12
12
|
import { Textarea } from '@renderer/components/ui/textarea';
|
|
13
|
+
import { emitOpenHermitEvent, OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
13
14
|
import appIcon from '@renderer/favicon.png';
|
|
14
15
|
import { Check, FileEdit, Loader2, RefreshCw, RotateCcw, X } from 'lucide-react';
|
|
15
16
|
|
|
@@ -147,6 +148,7 @@ export const AdvancedSection = ({}: AdvancedSectionProps): React.JSX.Element =>
|
|
|
147
148
|
setRestartMsg(null);
|
|
148
149
|
try {
|
|
149
150
|
await api.ccSettings.restart();
|
|
151
|
+
emitOpenHermitEvent(OPEN_HERMIT_EVENTS.runtimeRestarted);
|
|
150
152
|
setRestartMsg('已重启');
|
|
151
153
|
} catch {
|
|
152
154
|
setRestartMsg('重启失败');
|
|
@@ -30,6 +30,7 @@ import { useStore } from '@renderer/store';
|
|
|
30
30
|
import { createLoadingMultimodelCliStatus } from '@renderer/store/slices/cliInstallerSlice';
|
|
31
31
|
import { getMainScreenCliProviders } from '@renderer/utils/claudeCodeOnlyProviders';
|
|
32
32
|
import { formatBytes } from '@renderer/utils/formatters';
|
|
33
|
+
import { emitOpenHermitEvent, OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
33
34
|
import { resolveProjectPathById } from '@renderer/utils/projectLookup';
|
|
34
35
|
import { refreshCliStatusForCurrentMode } from '@renderer/utils/refreshCliStatus';
|
|
35
36
|
import { getRuntimeDisplayName } from '@renderer/utils/runtimeDisplayName';
|
|
@@ -747,6 +748,7 @@ const GenericHarnessProviderDialog = ({
|
|
|
747
748
|
setNewProviderBaseUrl('');
|
|
748
749
|
setNewProviderModel('');
|
|
749
750
|
onRefresh();
|
|
751
|
+
emitOpenHermitEvent(OPEN_HERMIT_EVENTS.providersChanged);
|
|
750
752
|
} catch (err) {
|
|
751
753
|
setAddProviderError(err instanceof Error ? err.message : '添加 Provider 失败');
|
|
752
754
|
} finally {
|
|
@@ -11,6 +11,7 @@ import { providersApi } from '@renderer/api/providers';
|
|
|
11
11
|
import { ALL_AGENT_TYPES, AGENT_TYPE_LABELS } from '@renderer/components/team/HarnessCards';
|
|
12
12
|
import type { CcAgentType } from '@renderer/components/team/HarnessCards';
|
|
13
13
|
import { cn } from '@renderer/lib/utils';
|
|
14
|
+
import { OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
14
15
|
import { CheckCircle2 } from 'lucide-react';
|
|
15
16
|
|
|
16
17
|
import { SettingsSectionHeader } from '../components/SettingsSectionHeader';
|
|
@@ -70,6 +71,16 @@ export const HarnessSection = (): React.JSX.Element => {
|
|
|
70
71
|
void refresh();
|
|
71
72
|
}, [refresh]);
|
|
72
73
|
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const handleProvidersChanged = () => {
|
|
76
|
+
void refresh();
|
|
77
|
+
};
|
|
78
|
+
window.addEventListener(OPEN_HERMIT_EVENTS.providersChanged, handleProvidersChanged);
|
|
79
|
+
return () => {
|
|
80
|
+
window.removeEventListener(OPEN_HERMIT_EVENTS.providersChanged, handleProvidersChanged);
|
|
81
|
+
};
|
|
82
|
+
}, [refresh]);
|
|
83
|
+
|
|
73
84
|
// Build coverage map: agent type -> list of sources (providers + projects)
|
|
74
85
|
const coveredTypes = new Map<CcAgentType, string[]>();
|
|
75
86
|
for (const type of ALL_AGENT_TYPES) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from '@renderer/components/ui/dialog';
|
|
20
20
|
import { Input } from '@renderer/components/ui/input';
|
|
21
21
|
import { Label } from '@renderer/components/ui/label';
|
|
22
|
+
import { emitOpenHermitEvent, OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
22
23
|
import {
|
|
23
24
|
Select,
|
|
24
25
|
SelectContent,
|
|
@@ -213,7 +214,9 @@ async function addPlatform(
|
|
|
213
214
|
restart_required?: boolean;
|
|
214
215
|
};
|
|
215
216
|
if (!json.ok) throw new Error(json.error ?? '添加失败');
|
|
216
|
-
return {
|
|
217
|
+
return {
|
|
218
|
+
restartRequired: json.data?.restart_required === true || json.restart_required === true,
|
|
219
|
+
};
|
|
217
220
|
}
|
|
218
221
|
|
|
219
222
|
// ---------------------------------------------------------------------------
|
|
@@ -490,7 +493,11 @@ function AddPlatformDialog({
|
|
|
490
493
|
else if (v === 'false') ccOptions[k] = false;
|
|
491
494
|
else ccOptions[k] = v;
|
|
492
495
|
}
|
|
493
|
-
const result = await addPlatform(
|
|
496
|
+
const result = await addPlatform(
|
|
497
|
+
projectName,
|
|
498
|
+
platformType,
|
|
499
|
+
ccOptions as Record<string, string>
|
|
500
|
+
);
|
|
494
501
|
onAdded(projectName);
|
|
495
502
|
if (result.restartRequired) {
|
|
496
503
|
const shouldRestart = await confirm({
|
|
@@ -501,6 +508,7 @@ function AddPlatformDialog({
|
|
|
501
508
|
});
|
|
502
509
|
if (shouldRestart) {
|
|
503
510
|
await api.ccSettings.restart();
|
|
511
|
+
emitOpenHermitEvent(OPEN_HERMIT_EVENTS.runtimeRestarted);
|
|
504
512
|
}
|
|
505
513
|
}
|
|
506
514
|
} catch (e) {
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
Plus,
|
|
68
68
|
Terminal,
|
|
69
69
|
Trash2,
|
|
70
|
+
Loader2,
|
|
70
71
|
Users,
|
|
71
72
|
} from 'lucide-react';
|
|
72
73
|
import { useShallow } from 'zustand/react/shallow';
|
|
@@ -1065,6 +1066,7 @@ export const TeamDetailView = ({
|
|
|
1065
1066
|
|
|
1066
1067
|
const [sendDialogOpen, setSendDialogOpen] = useState(false);
|
|
1067
1068
|
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
|
1069
|
+
const [deleting, setDeleting] = useState(false);
|
|
1068
1070
|
const [trashOpen, setTrashOpen] = useState(false);
|
|
1069
1071
|
const [sendDialogRecipient, setSendDialogRecipient] = useState<string | undefined>(undefined);
|
|
1070
1072
|
const [sendDialogDefaultText, setSendDialogDefaultText] = useState<string | undefined>(undefined);
|
|
@@ -1618,16 +1620,13 @@ export const TeamDetailView = ({
|
|
|
1618
1620
|
);
|
|
1619
1621
|
|
|
1620
1622
|
const handleRestartTeamFromEdit = useCallback(async (): Promise<void> => {
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
});
|
|
1629
|
-
await Promise.all([fetchTeams(), selectTeam(teamName)]);
|
|
1630
|
-
}, [data?.config.projectPath, fetchTeams, launchTeam, selectTeam, teamName]);
|
|
1623
|
+
await api.ccSettings.restart();
|
|
1624
|
+
// Wait for cc-connect to come back, then refresh
|
|
1625
|
+
setTimeout(() => {
|
|
1626
|
+
void fetchTeams();
|
|
1627
|
+
void selectTeam(teamName);
|
|
1628
|
+
}, 3000);
|
|
1629
|
+
}, [fetchTeams, selectTeam, teamName]);
|
|
1631
1630
|
|
|
1632
1631
|
const handleSaveAndRestartFromEdit = useCallback(
|
|
1633
1632
|
async (runtimeConfig: {
|
|
@@ -1827,29 +1826,25 @@ export const TeamDetailView = ({
|
|
|
1827
1826
|
}, []);
|
|
1828
1827
|
|
|
1829
1828
|
const confirmDeleteTeam = useCallback((): void => {
|
|
1830
|
-
|
|
1829
|
+
setDeleting(true);
|
|
1831
1830
|
void (async () => {
|
|
1832
1831
|
try {
|
|
1833
1832
|
const result = await deleteTeam(teamName);
|
|
1834
1833
|
if (result.restartRequired) {
|
|
1835
|
-
|
|
1836
|
-
title: '重启 cc-connect',
|
|
1837
|
-
message: '团队已从配置中删除。需要重启 cc-connect 才会停止对应运行时。',
|
|
1838
|
-
confirmLabel: '立即重启',
|
|
1839
|
-
cancelLabel: '稍后重启',
|
|
1840
|
-
variant: 'danger',
|
|
1841
|
-
});
|
|
1842
|
-
if (shouldRestart) {
|
|
1843
|
-
await api.ccSettings.restart();
|
|
1844
|
-
}
|
|
1834
|
+
await api.ccSettings.restart();
|
|
1845
1835
|
}
|
|
1836
|
+
await fetchTeams();
|
|
1837
|
+
setDeleteConfirmOpen(false);
|
|
1846
1838
|
if (tabId) closeTab(tabId);
|
|
1847
1839
|
openTeamsTab();
|
|
1848
|
-
} catch {
|
|
1849
|
-
|
|
1840
|
+
} catch (err) {
|
|
1841
|
+
console.error('Failed to delete team:', err);
|
|
1842
|
+
setDeleteConfirmOpen(false);
|
|
1843
|
+
} finally {
|
|
1844
|
+
setDeleting(false);
|
|
1850
1845
|
}
|
|
1851
1846
|
})();
|
|
1852
|
-
}, [teamName, deleteTeam, openTeamsTab, closeTab, tabId]);
|
|
1847
|
+
}, [teamName, deleteTeam, openTeamsTab, closeTab, tabId, fetchTeams]);
|
|
1853
1848
|
|
|
1854
1849
|
const handleCreateTask = (
|
|
1855
1850
|
subject: string,
|
|
@@ -2163,19 +2158,21 @@ export const TeamDetailView = ({
|
|
|
2163
2158
|
{isTeamProvisioning ? '团队仍在编排中,暂时无法编辑' : '编辑团队'}
|
|
2164
2159
|
</TooltipContent>
|
|
2165
2160
|
</Tooltip>
|
|
2166
|
-
|
|
2167
|
-
<
|
|
2168
|
-
<
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2161
|
+
{teamName !== 'default' && teamName !== 'my-project' && (
|
|
2162
|
+
<Tooltip>
|
|
2163
|
+
<TooltipTrigger asChild>
|
|
2164
|
+
<Button
|
|
2165
|
+
variant="ghost"
|
|
2166
|
+
size="sm"
|
|
2167
|
+
className="h-7 gap-1 px-2 text-xs text-red-400 hover:bg-red-500/10 hover:text-red-300"
|
|
2168
|
+
onClick={handleDeleteTeam}
|
|
2169
|
+
>
|
|
2170
|
+
<Trash2 size={12} />
|
|
2171
|
+
</Button>
|
|
2172
|
+
</TooltipTrigger>
|
|
2173
|
+
<TooltipContent side="bottom">删除团队</TooltipContent>
|
|
2174
|
+
</Tooltip>
|
|
2175
|
+
)}
|
|
2179
2176
|
</div>
|
|
2180
2177
|
</div>
|
|
2181
2178
|
{data.config.description && (
|
|
@@ -2653,6 +2650,8 @@ export const TeamDetailView = ({
|
|
|
2653
2650
|
currentManagedSources={currentManagedSources}
|
|
2654
2651
|
currentDisabledCommands={currentDisabledCommands}
|
|
2655
2652
|
currentPlatformAllowFrom={currentPlatformAllowFrom}
|
|
2653
|
+
currentProviderRefs={data.providerRefs ?? []}
|
|
2654
|
+
globalProviders={data.globalProviders ?? []}
|
|
2656
2655
|
currentMembers={membersWithLiveBranches.filter((m) => !isLeadMember(m))}
|
|
2657
2656
|
leadMember={membersWithLiveBranches.find((m) => isLeadMember(m)) ?? null}
|
|
2658
2657
|
resolvedMemberColorMap={resolvedMemberColorMap}
|
|
@@ -2665,9 +2664,10 @@ export const TeamDetailView = ({
|
|
|
2665
2664
|
void fetchTeams();
|
|
2666
2665
|
void selectTeam(teamName);
|
|
2667
2666
|
}}
|
|
2668
|
-
onDeleteTeam={
|
|
2667
|
+
onDeleteTeam={
|
|
2668
|
+
teamName !== 'default' && teamName !== 'my-project' ? handleDeleteTeam : undefined
|
|
2669
|
+
}
|
|
2669
2670
|
onRestartTeam={handleRestartTeamFromEdit}
|
|
2670
|
-
onSaveAndRestart={handleSaveAndRestartFromEdit}
|
|
2671
2671
|
/>
|
|
2672
2672
|
|
|
2673
2673
|
<Dialog
|
|
@@ -2704,7 +2704,12 @@ export const TeamDetailView = ({
|
|
|
2704
2704
|
</DialogContent>
|
|
2705
2705
|
</Dialog>
|
|
2706
2706
|
|
|
2707
|
-
<Dialog
|
|
2707
|
+
<Dialog
|
|
2708
|
+
open={deleteConfirmOpen}
|
|
2709
|
+
onOpenChange={(v) => {
|
|
2710
|
+
if (!deleting) setDeleteConfirmOpen(v);
|
|
2711
|
+
}}
|
|
2712
|
+
>
|
|
2708
2713
|
<DialogContent className="max-w-sm">
|
|
2709
2714
|
<DialogHeader>
|
|
2710
2715
|
<DialogTitle>删除团队</DialogTitle>
|
|
@@ -2717,8 +2722,14 @@ export const TeamDetailView = ({
|
|
|
2717
2722
|
<Button variant="ghost" size="sm" onClick={() => setDeleteConfirmOpen(false)}>
|
|
2718
2723
|
取消
|
|
2719
2724
|
</Button>
|
|
2720
|
-
<Button
|
|
2721
|
-
|
|
2725
|
+
<Button
|
|
2726
|
+
variant="destructive"
|
|
2727
|
+
size="sm"
|
|
2728
|
+
onClick={confirmDeleteTeam}
|
|
2729
|
+
disabled={deleting}
|
|
2730
|
+
>
|
|
2731
|
+
{deleting && <Loader2 size={14} className="mr-1.5 animate-spin" />}
|
|
2732
|
+
删除并重启
|
|
2722
2733
|
</Button>
|
|
2723
2734
|
</DialogFooter>
|
|
2724
2735
|
</DialogContent>
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from '@renderer/store/utils/stateResetHelpers';
|
|
34
34
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
|
35
35
|
import { buildTaskCountsByTeam, normalizePath } from '@renderer/utils/pathNormalize';
|
|
36
|
+
import { emitOpenHermitEvent, OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
36
37
|
import { getBaseName } from '@renderer/utils/pathUtils';
|
|
37
38
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
|
38
39
|
import {
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
Download,
|
|
43
44
|
FolderOpen,
|
|
44
45
|
GitBranch,
|
|
46
|
+
Loader2,
|
|
45
47
|
Play,
|
|
46
48
|
RotateCcw,
|
|
47
49
|
Search,
|
|
@@ -275,6 +277,7 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
275
277
|
const [copyData, setCopyData] = useState<TeamCopyData | null>(null);
|
|
276
278
|
const [searchQuery, setSearchQuery] = useState('');
|
|
277
279
|
const [filter, setFilter] = useState<TeamListFilterState>(EMPTY_TEAM_FILTER);
|
|
280
|
+
const [deletingTeamName, setDeletingTeamName] = useState<string | null>(null);
|
|
278
281
|
const [aliveTeams, setAliveTeams] = useState<string[]>([]);
|
|
279
282
|
const {
|
|
280
283
|
teams,
|
|
@@ -532,33 +535,28 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
532
535
|
}
|
|
533
536
|
const confirmed = await confirm({
|
|
534
537
|
title: '删除团队',
|
|
535
|
-
message:
|
|
536
|
-
confirmLabel: '
|
|
538
|
+
message: `确定删除团队”${teamDisplayName}”吗?此操作会同步删除 cc-connect 项目并移除本地团队数据。`,
|
|
539
|
+
confirmLabel: '删除并重启',
|
|
537
540
|
cancelLabel: '取消',
|
|
538
541
|
variant: 'danger',
|
|
539
542
|
});
|
|
540
543
|
if (confirmed) {
|
|
544
|
+
setDeletingTeamName(teamName);
|
|
541
545
|
try {
|
|
542
546
|
const result = await deleteTeam(teamName);
|
|
543
547
|
if (result.restartRequired) {
|
|
544
|
-
|
|
545
|
-
title: '重启 cc-connect',
|
|
546
|
-
message: '团队已从配置中删除。需要重启 cc-connect 才会停止对应运行时。',
|
|
547
|
-
confirmLabel: '立即重启',
|
|
548
|
-
cancelLabel: '稍后重启',
|
|
549
|
-
variant: 'danger',
|
|
550
|
-
});
|
|
551
|
-
if (shouldRestart) {
|
|
552
|
-
await api.ccSettings.restart();
|
|
553
|
-
}
|
|
548
|
+
await api.ccSettings.restart();
|
|
554
549
|
}
|
|
555
|
-
|
|
556
|
-
|
|
550
|
+
await fetchTeams();
|
|
551
|
+
} catch (err) {
|
|
552
|
+
console.error('Failed to delete team:', err);
|
|
553
|
+
} finally {
|
|
554
|
+
setDeletingTeamName(null);
|
|
557
555
|
}
|
|
558
556
|
}
|
|
559
557
|
})();
|
|
560
558
|
},
|
|
561
|
-
[deleteTeam, teams]
|
|
559
|
+
[deleteTeam, teams, fetchTeams]
|
|
562
560
|
);
|
|
563
561
|
|
|
564
562
|
const handleRestoreTeam = useCallback(
|
|
@@ -674,6 +672,19 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
674
672
|
void fetchAllTasks();
|
|
675
673
|
}, [fetchTeams, fetchAllTasks]);
|
|
676
674
|
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
const refresh = () => {
|
|
677
|
+
void fetchTeams();
|
|
678
|
+
void fetchAllTasks();
|
|
679
|
+
};
|
|
680
|
+
window.addEventListener(OPEN_HERMIT_EVENTS.runtimeRestarted, refresh);
|
|
681
|
+
window.addEventListener(OPEN_HERMIT_EVENTS.teamsChanged, refresh);
|
|
682
|
+
return () => {
|
|
683
|
+
window.removeEventListener(OPEN_HERMIT_EVENTS.runtimeRestarted, refresh);
|
|
684
|
+
window.removeEventListener(OPEN_HERMIT_EVENTS.teamsChanged, refresh);
|
|
685
|
+
};
|
|
686
|
+
}, [fetchTeams, fetchAllTasks]);
|
|
687
|
+
|
|
677
688
|
const taskCountsByTeam = useMemo(() => buildTaskCountsByTeam(globalTasks), [globalTasks]);
|
|
678
689
|
|
|
679
690
|
const activeTeams = useMemo<ActiveTeamRef[]>(() => {
|
|
@@ -798,6 +809,7 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
798
809
|
async (request: TeamCreateRequest) => {
|
|
799
810
|
await createTeam(request);
|
|
800
811
|
await Promise.all([fetchTeams(), fetchAllTasks()]);
|
|
812
|
+
emitOpenHermitEvent(OPEN_HERMIT_EVENTS.teamsChanged);
|
|
801
813
|
window.setTimeout(() => {
|
|
802
814
|
void fetchTeams();
|
|
803
815
|
void fetchAllTasks();
|
|
@@ -1094,6 +1106,7 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
1094
1106
|
const matchesCurrentProject = currentProjectPath
|
|
1095
1107
|
? teamMatchesProjectSelection(team, currentProjectPath)
|
|
1096
1108
|
: false;
|
|
1109
|
+
const isDeleting = deletingTeamName === team.teamName;
|
|
1097
1110
|
return (
|
|
1098
1111
|
<div
|
|
1099
1112
|
key={team.teamName}
|
|
@@ -1101,14 +1114,22 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
1101
1114
|
tabIndex={0}
|
|
1102
1115
|
className="group relative flex cursor-pointer flex-col overflow-hidden rounded-lg border border-l-[3px] border-[var(--color-border)] bg-[var(--color-surface)] p-4 hover:bg-[var(--color-surface-raised)]"
|
|
1103
1116
|
style={teamColorSet ? { borderLeftColor: teamColorSet.border } : undefined}
|
|
1104
|
-
onClick={
|
|
1117
|
+
onClick={
|
|
1118
|
+
isDeleting ? undefined : () => openTeamTab(team.teamName, team.projectPath)
|
|
1119
|
+
}
|
|
1105
1120
|
onKeyDown={(e) => {
|
|
1106
1121
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
1107
1122
|
e.preventDefault();
|
|
1108
|
-
openTeamTab(team.teamName, team.projectPath);
|
|
1123
|
+
if (!isDeleting) openTeamTab(team.teamName, team.projectPath);
|
|
1109
1124
|
}
|
|
1110
1125
|
}}
|
|
1111
1126
|
>
|
|
1127
|
+
{isDeleting && (
|
|
1128
|
+
<div className="absolute inset-0 z-20 flex items-center justify-center gap-2 bg-black/60 text-sm text-white">
|
|
1129
|
+
<Loader2 size={16} className="animate-spin" />
|
|
1130
|
+
删除并重启中…
|
|
1131
|
+
</div>
|
|
1132
|
+
)}
|
|
1112
1133
|
<div className="flex flex-1 flex-col">
|
|
1113
1134
|
<div className="flex items-start justify-between gap-2">
|
|
1114
1135
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
@@ -1165,20 +1186,22 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
1165
1186
|
<TooltipContent side="bottom">复制团队</TooltipContent>
|
|
1166
1187
|
</Tooltip>
|
|
1167
1188
|
)}
|
|
1168
|
-
|
|
1169
|
-
<
|
|
1170
|
-
<
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1189
|
+
{team.teamName !== 'default' && team.teamName !== 'my-project' && (
|
|
1190
|
+
<Tooltip>
|
|
1191
|
+
<TooltipTrigger asChild>
|
|
1192
|
+
<button
|
|
1193
|
+
type="button"
|
|
1194
|
+
className="shrink-0 rounded p-1 text-[var(--color-text-muted)] opacity-0 transition-opacity hover:bg-red-500/10 hover:text-red-300 group-hover:opacity-100"
|
|
1195
|
+
onClick={(e) =>
|
|
1196
|
+
handleDeleteTeam(team.teamName, !!team.pendingCreate, e)
|
|
1197
|
+
}
|
|
1198
|
+
>
|
|
1199
|
+
<Trash2 size={14} />
|
|
1200
|
+
</button>
|
|
1201
|
+
</TooltipTrigger>
|
|
1202
|
+
<TooltipContent side="bottom">删除团队</TooltipContent>
|
|
1203
|
+
</Tooltip>
|
|
1204
|
+
)}
|
|
1182
1205
|
</div>
|
|
1183
1206
|
</div>
|
|
1184
1207
|
<div className="mt-2 flex min-h-10 items-start gap-2">
|