@yancyyu/openhermit 1.6.12 → 1.6.14
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/bin/alias-loader.mjs +2 -2
- package/bin/hermit.mjs +201 -12
- package/bin/postinstall.mjs +32 -0
- package/package.json +2 -1
- package/src/main/server.ts +6 -15
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +22 -3
- package/src/renderer/components/team/TeamDetailView.tsx +2 -3
- package/src/renderer/components/team/TeamListView.tsx +5 -6
package/bin/alias-loader.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
4
|
|
|
5
|
-
const repoRoot = path.resolve(path.dirname(
|
|
5
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
6
6
|
|
|
7
7
|
const ALIASES = [
|
|
8
8
|
['@features/', 'src/features/'],
|
package/bin/hermit.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { spawn, execSync } from 'node:child_process';
|
|
18
18
|
import crypto from 'node:crypto';
|
|
19
|
-
import { mkdirSync,
|
|
19
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { createRequire } from 'node:module';
|
|
21
21
|
import os from 'node:os';
|
|
22
22
|
import path from 'node:path';
|
|
@@ -55,15 +55,21 @@ Usage:
|
|
|
55
55
|
|
|
56
56
|
Options:
|
|
57
57
|
--port <number> HTTP server port (default: 5680)
|
|
58
|
-
--no-cc-connect Do not auto-start bundled
|
|
58
|
+
--no-cc-connect Do not auto-start bundled runtime service
|
|
59
|
+
--daemon Run openHermit in the background
|
|
59
60
|
--version Show current version
|
|
60
61
|
--help Show this help message
|
|
62
|
+
status Show background service status
|
|
63
|
+
stop Stop background service
|
|
61
64
|
update Check and install updates
|
|
62
65
|
|
|
63
66
|
Examples:
|
|
64
67
|
npx @yancyyu/openhermit # Run without installing
|
|
65
68
|
npx @yancyyu/openhermit --port 8080
|
|
66
69
|
openhermit # After global install
|
|
70
|
+
openhermit --daemon
|
|
71
|
+
openhermit status
|
|
72
|
+
openhermit stop
|
|
67
73
|
openhermit --version
|
|
68
74
|
openhermit update
|
|
69
75
|
`);
|
|
@@ -80,6 +86,10 @@ const portIndex = args.indexOf('--port');
|
|
|
80
86
|
const port = portIndex !== -1 && args[portIndex + 1] ? args[portIndex + 1] : '5680';
|
|
81
87
|
const skipCcConnect = args.includes('--no-cc-connect') || process.env.HERMIT_NO_CC_CONNECT === '1';
|
|
82
88
|
const hermitHome = process.env.HERMIT_HOME || path.join(os.homedir(), '.hermit');
|
|
89
|
+
const daemonRequested = args.includes('--daemon');
|
|
90
|
+
const daemonChild = process.env.HERMIT_DAEMON_CHILD === '1';
|
|
91
|
+
const daemonPidPath = path.join(hermitHome, 'openhermit.pid');
|
|
92
|
+
const daemonLogPath = path.join(hermitHome, 'logs', 'openhermit.log');
|
|
83
93
|
const ccConnectConfigPath =
|
|
84
94
|
process.env.HERMIT_CC_CONNECT_CONFIG ||
|
|
85
95
|
process.env.CC_CONNECT_CONFIG ||
|
|
@@ -142,6 +152,171 @@ async function runUpdate() {
|
|
|
142
152
|
}
|
|
143
153
|
}
|
|
144
154
|
|
|
155
|
+
function readDaemonPid() {
|
|
156
|
+
try {
|
|
157
|
+
const raw = readFileSync(daemonPidPath, 'utf-8').trim();
|
|
158
|
+
const pid = Number.parseInt(raw, 10);
|
|
159
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isPidRunning(pid) {
|
|
166
|
+
try {
|
|
167
|
+
process.kill(pid, 0);
|
|
168
|
+
return true;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function removeDaemonPidFile() {
|
|
175
|
+
try {
|
|
176
|
+
unlinkSync(daemonPidPath);
|
|
177
|
+
} catch {
|
|
178
|
+
// Already gone.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function signalDaemon(pid, signal) {
|
|
183
|
+
if (!pid) return false;
|
|
184
|
+
try {
|
|
185
|
+
process.kill(-pid, signal);
|
|
186
|
+
return true;
|
|
187
|
+
} catch {
|
|
188
|
+
// Fall back to direct process signal.
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
process.kill(pid, signal);
|
|
192
|
+
return true;
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function collectFallbackPids() {
|
|
199
|
+
const pids = new Set();
|
|
200
|
+
const commands = [
|
|
201
|
+
`lsof -tiTCP:${port} -sTCP:LISTEN 2>/dev/null || true`,
|
|
202
|
+
'lsof -tiTCP:9810 -sTCP:LISTEN 2>/dev/null || true',
|
|
203
|
+
'lsof -tiTCP:9820 -sTCP:LISTEN 2>/dev/null || true',
|
|
204
|
+
"pgrep -f '@yancyyu/openhermit|openhermit/bin/hermit\\.mjs|src/main/server\\.ts|cc-connect' 2>/dev/null || true",
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
for (const command of commands) {
|
|
208
|
+
try {
|
|
209
|
+
const out = execSync(command, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
210
|
+
for (const line of out.split(/\s+/)) {
|
|
211
|
+
const pid = Number.parseInt(line, 10);
|
|
212
|
+
if (Number.isFinite(pid) && pid > 0 && pid !== process.pid) {
|
|
213
|
+
pids.add(pid);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
// Ignore missing lsof/pgrep or races with exiting processes.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return [...pids];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function stopFallbackProcesses() {
|
|
225
|
+
const pids = collectFallbackPids();
|
|
226
|
+
if (pids.length === 0) return false;
|
|
227
|
+
|
|
228
|
+
for (const pid of pids) {
|
|
229
|
+
signalDaemon(pid, 'SIGTERM');
|
|
230
|
+
}
|
|
231
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
232
|
+
for (const pid of pids) {
|
|
233
|
+
if (isPidRunning(pid)) {
|
|
234
|
+
signalDaemon(pid, 'SIGKILL');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function printDaemonStatus() {
|
|
241
|
+
const pid = readDaemonPid();
|
|
242
|
+
if (pid && isPidRunning(pid)) {
|
|
243
|
+
console.log(`[openHermit] Running in background (pid ${pid})`);
|
|
244
|
+
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
if (pid) removeDaemonPidFile();
|
|
248
|
+
const fallbackPids = collectFallbackPids();
|
|
249
|
+
if (fallbackPids.length > 0) {
|
|
250
|
+
console.log(`[openHermit] Running without daemon pidfile (pids ${fallbackPids.join(', ')})`);
|
|
251
|
+
process.exit(0);
|
|
252
|
+
}
|
|
253
|
+
console.log('[openHermit] Not running');
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function stopDaemon() {
|
|
258
|
+
const pid = readDaemonPid();
|
|
259
|
+
if (!pid || !isPidRunning(pid)) {
|
|
260
|
+
if (pid) removeDaemonPidFile();
|
|
261
|
+
const stoppedFallback = await stopFallbackProcesses();
|
|
262
|
+
console.log(stoppedFallback ? '[openHermit] Stopped orphaned service processes' : '[openHermit] Not running');
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
console.log(`[openHermit] Stopping background service (pid ${pid})...`);
|
|
266
|
+
signalDaemon(pid, 'SIGTERM');
|
|
267
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
268
|
+
if (isPidRunning(pid)) {
|
|
269
|
+
signalDaemon(pid, 'SIGKILL');
|
|
270
|
+
}
|
|
271
|
+
removeDaemonPidFile();
|
|
272
|
+
console.log('[openHermit] Stopped');
|
|
273
|
+
process.exit(0);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function startDaemon() {
|
|
277
|
+
const existingPid = readDaemonPid();
|
|
278
|
+
if (existingPid && isPidRunning(existingPid)) {
|
|
279
|
+
console.log(`[openHermit] Already running in background (pid ${existingPid})`);
|
|
280
|
+
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
mkdirSync(path.dirname(daemonPidPath), { recursive: true });
|
|
285
|
+
mkdirSync(path.dirname(daemonLogPath), { recursive: true });
|
|
286
|
+
const out = openSync(daemonLogPath, 'a');
|
|
287
|
+
const err = openSync(daemonLogPath, 'a');
|
|
288
|
+
const childArgs = process.argv.slice(2).filter((arg) => arg !== '--daemon');
|
|
289
|
+
const child = spawn(process.execPath, [fileURLToPath(import.meta.url), ...childArgs], {
|
|
290
|
+
cwd: repoRoot,
|
|
291
|
+
detached: true,
|
|
292
|
+
env: {
|
|
293
|
+
...process.env,
|
|
294
|
+
HERMIT_DAEMON_CHILD: '1',
|
|
295
|
+
},
|
|
296
|
+
stdio: ['ignore', out, err],
|
|
297
|
+
});
|
|
298
|
+
child.unref();
|
|
299
|
+
closeSync(out);
|
|
300
|
+
closeSync(err);
|
|
301
|
+
writeFileSync(daemonPidPath, String(child.pid), 'utf-8');
|
|
302
|
+
console.log(`[openHermit] Started in background (pid ${child.pid})`);
|
|
303
|
+
console.log(`[openHermit] URL: http://127.0.0.1:${port}`);
|
|
304
|
+
console.log(`[openHermit] Log: ${daemonLogPath}`);
|
|
305
|
+
process.exit(0);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (args.includes('status')) {
|
|
309
|
+
printDaemonStatus();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (args.includes('stop')) {
|
|
313
|
+
await stopDaemon();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (daemonRequested && !daemonChild) {
|
|
317
|
+
startDaemon();
|
|
318
|
+
}
|
|
319
|
+
|
|
145
320
|
// ---------------------------------------------------------------------------
|
|
146
321
|
// Check dependencies
|
|
147
322
|
// ---------------------------------------------------------------------------
|
|
@@ -342,12 +517,13 @@ if (!skipCcConnect) {
|
|
|
342
517
|
const ccBaseUrl = process.env.CC_CONNECT_BASE_URL || 'http://127.0.0.1:9820';
|
|
343
518
|
const alreadyRunning = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 1_000);
|
|
344
519
|
if (alreadyRunning) {
|
|
345
|
-
console.log(`[openHermit]
|
|
520
|
+
console.log(`[openHermit] Runtime service already running: ${ccBaseUrl}`);
|
|
346
521
|
} else {
|
|
347
|
-
console.log('[openHermit] Starting bundled
|
|
348
|
-
console.log(`[openHermit]
|
|
522
|
+
console.log('[openHermit] Starting bundled runtime service...');
|
|
523
|
+
console.log(`[openHermit] Runtime config: ${ccConnectConfigPath}`);
|
|
349
524
|
ccConnectProcess = spawn(process.execPath, [resolveCcConnectRunner(), '-config', ccConnectConfigPath], {
|
|
350
525
|
cwd: repoRoot,
|
|
526
|
+
detached: true,
|
|
351
527
|
env: {
|
|
352
528
|
...process.env,
|
|
353
529
|
CC_CONNECT_TOKEN: ccTokens.managementToken,
|
|
@@ -358,7 +534,7 @@ if (!skipCcConnect) {
|
|
|
358
534
|
});
|
|
359
535
|
const ready = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 30_000);
|
|
360
536
|
if (!ready) {
|
|
361
|
-
console.warn('[openHermit]
|
|
537
|
+
console.warn('[openHermit] Runtime service did not become ready within 30s; openHermit will keep trying via API.');
|
|
362
538
|
}
|
|
363
539
|
}
|
|
364
540
|
}
|
|
@@ -402,6 +578,7 @@ console.log('[openHermit] Launching server...\n');
|
|
|
402
578
|
|
|
403
579
|
const serverProcess = spawn(process.execPath, ['--import', resolveAliasLoaderRegister(), '--import', resolveTsxLoader(), 'src/main/server.ts'], {
|
|
404
580
|
cwd: repoRoot,
|
|
581
|
+
detached: true,
|
|
405
582
|
env: {
|
|
406
583
|
...process.env,
|
|
407
584
|
PORT: port,
|
|
@@ -417,22 +594,34 @@ const serverProcess = spawn(process.execPath, ['--import', resolveAliasLoaderReg
|
|
|
417
594
|
});
|
|
418
595
|
|
|
419
596
|
serverProcess.on('exit', (code) => {
|
|
597
|
+
if (shuttingDown) return;
|
|
598
|
+
signalDaemon(ccConnectProcess?.pid, 'SIGTERM');
|
|
420
599
|
if (code !== 0) {
|
|
421
600
|
console.error(`[openHermit] Server exited with code ${code}`);
|
|
422
601
|
process.exit(code ?? 1);
|
|
423
602
|
}
|
|
424
603
|
});
|
|
425
604
|
|
|
426
|
-
|
|
605
|
+
let shuttingDown = false;
|
|
606
|
+
function shutdown(exitCode = 0) {
|
|
607
|
+
if (shuttingDown) return;
|
|
608
|
+
shuttingDown = true;
|
|
427
609
|
console.log('\n[openHermit] Shutting down...');
|
|
428
|
-
serverProcess
|
|
429
|
-
ccConnectProcess?.
|
|
610
|
+
signalDaemon(serverProcess?.pid, 'SIGTERM');
|
|
611
|
+
signalDaemon(ccConnectProcess?.pid, 'SIGTERM');
|
|
612
|
+
setTimeout(() => {
|
|
613
|
+
signalDaemon(serverProcess?.pid, 'SIGKILL');
|
|
614
|
+
signalDaemon(ccConnectProcess?.pid, 'SIGKILL');
|
|
615
|
+
process.exit(exitCode);
|
|
616
|
+
}, 2_000).unref();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
process.on('SIGINT', () => {
|
|
620
|
+
shutdown(0);
|
|
430
621
|
});
|
|
431
622
|
|
|
432
623
|
process.on('SIGTERM', () => {
|
|
433
|
-
|
|
434
|
-
serverProcess.kill('SIGTERM');
|
|
435
|
-
ccConnectProcess?.kill('SIGTERM');
|
|
624
|
+
shutdown(0);
|
|
436
625
|
});
|
|
437
626
|
|
|
438
627
|
console.log(`[openHermit] Server starting on http://127.0.0.1:${port}`);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
|
|
12
|
+
let version = 'unknown';
|
|
13
|
+
try {
|
|
14
|
+
version = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf-8')).version;
|
|
15
|
+
} catch {
|
|
16
|
+
// Keep install non-blocking.
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let runtimeVersion = 'bundled';
|
|
20
|
+
try {
|
|
21
|
+
runtimeVersion = JSON.parse(readFileSync(require.resolve('cc-connect/package.json'), 'utf-8')).version;
|
|
22
|
+
} catch {
|
|
23
|
+
// Keep install non-blocking.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hermitHome = process.env.HERMIT_HOME || path.join(os.homedir(), '.hermit');
|
|
27
|
+
|
|
28
|
+
console.log(`[openHermit] Installed ${version}`);
|
|
29
|
+
console.log(`[openHermit] Bundled runtime service: ${runtimeVersion}`);
|
|
30
|
+
console.log(`[openHermit] Data directory: ${hermitHome}`);
|
|
31
|
+
console.log('[openHermit] Start with: openhermit');
|
|
32
|
+
console.log('[openHermit] Background mode: openhermit --daemon');
|
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.14",
|
|
5
5
|
"description": "openHermit: team-oriented agent management workbench atop cc-connect.",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
7
7
|
"author": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"dev:server": "tsx watch src/main/server.ts",
|
|
35
35
|
"dev:web": "vite --config vite.web.config.ts",
|
|
36
36
|
"build:web": "vite build --config vite.web.config.ts --outDir ../../dist-renderer --emptyOutDir",
|
|
37
|
+
"postinstall": "node ./bin/postinstall.mjs",
|
|
37
38
|
"prepack": "pnpm build:web",
|
|
38
39
|
"start": "node bin/hermit.mjs",
|
|
39
40
|
"typecheck": "tsc --noEmit",
|
package/src/main/server.ts
CHANGED
|
@@ -1037,25 +1037,16 @@ app.delete<{ Params: { name: string }; Querystring: { deleteFiles?: string } }>(
|
|
|
1037
1037
|
try {
|
|
1038
1038
|
const result = await cc.deleteProject(teamName);
|
|
1039
1039
|
restartRequired = result.restart_required === true;
|
|
1040
|
-
await svc.updateTeam(teamName, {
|
|
1041
|
-
pendingDelete: true,
|
|
1042
|
-
restartRequired,
|
|
1043
|
-
});
|
|
1044
1040
|
} catch (err) {
|
|
1045
1041
|
request.log.warn({ err, teamName }, 'delete cc-connect project failed or project missing');
|
|
1046
|
-
try {
|
|
1047
|
-
await svc.updateTeam(teamName, {
|
|
1048
|
-
pendingDelete: true,
|
|
1049
|
-
restartRequired: true,
|
|
1050
|
-
});
|
|
1051
|
-
restartRequired = true;
|
|
1052
|
-
} catch {
|
|
1053
|
-
// Local metadata may already be gone; keep deletion best-effort.
|
|
1054
|
-
}
|
|
1055
1042
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1043
|
+
|
|
1044
|
+
try {
|
|
1045
|
+
await svc.deleteTeam(teamName, { deleteFiles: request.query.deleteFiles === 'true' });
|
|
1046
|
+
} catch (err) {
|
|
1047
|
+
request.log.warn({ err, teamName }, 'delete local team metadata failed or already missing');
|
|
1058
1048
|
}
|
|
1049
|
+
|
|
1059
1050
|
return { ok: true, restartRequired };
|
|
1060
1051
|
} catch (err) {
|
|
1061
1052
|
return reply.code(500).send(reply500(err));
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { useCallback, useEffect, useState } from 'react';
|
|
9
9
|
|
|
10
|
+
import { api } from '@renderer/api';
|
|
11
|
+
import { confirm } from '@renderer/components/common/ConfirmDialog';
|
|
10
12
|
import { Button } from '@renderer/components/ui/button';
|
|
11
13
|
import {
|
|
12
14
|
Dialog,
|
|
@@ -198,14 +200,20 @@ async function addPlatform(
|
|
|
198
200
|
projectName: string,
|
|
199
201
|
type: string,
|
|
200
202
|
options: Record<string, string>
|
|
201
|
-
): Promise<
|
|
203
|
+
): Promise<{ restartRequired: boolean }> {
|
|
202
204
|
const res = await fetch(`/api/v1/projects/${encodeURIComponent(projectName)}/add-platform`, {
|
|
203
205
|
method: 'POST',
|
|
204
206
|
headers: { 'Content-Type': 'application/json' },
|
|
205
207
|
body: JSON.stringify({ type, options }),
|
|
206
208
|
});
|
|
207
|
-
const json = (await res.json()) as {
|
|
209
|
+
const json = (await res.json()) as {
|
|
210
|
+
ok: boolean;
|
|
211
|
+
error?: string;
|
|
212
|
+
data?: { restart_required?: boolean };
|
|
213
|
+
restart_required?: boolean;
|
|
214
|
+
};
|
|
208
215
|
if (!json.ok) throw new Error(json.error ?? '添加失败');
|
|
216
|
+
return { restartRequired: json.data?.restart_required === true || json.restart_required === true };
|
|
209
217
|
}
|
|
210
218
|
|
|
211
219
|
// ---------------------------------------------------------------------------
|
|
@@ -482,8 +490,19 @@ function AddPlatformDialog({
|
|
|
482
490
|
else if (v === 'false') ccOptions[k] = false;
|
|
483
491
|
else ccOptions[k] = v;
|
|
484
492
|
}
|
|
485
|
-
await addPlatform(projectName, platformType, ccOptions as Record<string, string>);
|
|
493
|
+
const result = await addPlatform(projectName, platformType, ccOptions as Record<string, string>);
|
|
486
494
|
onAdded(projectName);
|
|
495
|
+
if (result.restartRequired) {
|
|
496
|
+
const shouldRestart = await confirm({
|
|
497
|
+
title: '重启 cc-connect',
|
|
498
|
+
message: '渠道已添加。需要重启 cc-connect 才会生效。',
|
|
499
|
+
confirmLabel: '立即重启',
|
|
500
|
+
cancelLabel: '稍后重启',
|
|
501
|
+
});
|
|
502
|
+
if (shouldRestart) {
|
|
503
|
+
await api.ccSettings.restart();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
487
506
|
} catch (e) {
|
|
488
507
|
setError(e instanceof Error ? e.message : '添加失败');
|
|
489
508
|
} finally {
|
|
@@ -1833,9 +1833,8 @@ export const TeamDetailView = ({
|
|
|
1833
1833
|
const result = await deleteTeam(teamName);
|
|
1834
1834
|
if (result.restartRequired) {
|
|
1835
1835
|
const shouldRestart = await confirm({
|
|
1836
|
-
title: '
|
|
1837
|
-
message:
|
|
1838
|
-
'已调用 cc-connect 删除团队配置。cc-connect 需要重启后才会真正移除该团队并停止相关运行时。',
|
|
1836
|
+
title: '重启 cc-connect',
|
|
1837
|
+
message: '团队已从配置中删除。需要重启 cc-connect 才会停止对应运行时。',
|
|
1839
1838
|
confirmLabel: '立即重启',
|
|
1840
1839
|
cancelLabel: '稍后重启',
|
|
1841
1840
|
variant: 'danger',
|
|
@@ -531,9 +531,9 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
531
531
|
return;
|
|
532
532
|
}
|
|
533
533
|
const confirmed = await confirm({
|
|
534
|
-
title: '
|
|
535
|
-
message:
|
|
536
|
-
confirmLabel: '
|
|
534
|
+
title: '删除团队',
|
|
535
|
+
message: `确定删除团队“${teamDisplayName}”吗?此操作会同步删除 cc-connect 项目并移除本地团队数据。`,
|
|
536
|
+
confirmLabel: '删除',
|
|
537
537
|
cancelLabel: '取消',
|
|
538
538
|
variant: 'danger',
|
|
539
539
|
});
|
|
@@ -542,9 +542,8 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
542
542
|
const result = await deleteTeam(teamName);
|
|
543
543
|
if (result.restartRequired) {
|
|
544
544
|
const shouldRestart = await confirm({
|
|
545
|
-
title: '
|
|
546
|
-
message:
|
|
547
|
-
'已调用 cc-connect 删除团队配置。cc-connect 需要重启后才会真正移除该团队并停止相关运行时。',
|
|
545
|
+
title: '重启 cc-connect',
|
|
546
|
+
message: '团队已从配置中删除。需要重启 cc-connect 才会停止对应运行时。',
|
|
548
547
|
confirmLabel: '立即重启',
|
|
549
548
|
cancelLabel: '稍后重启',
|
|
550
549
|
variant: 'danger',
|