@yancyyu/openhermit 1.6.13 → 1.6.15
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 +42 -7
- package/bin/postinstall.mjs +32 -0
- package/package.json +2 -1
- package/src/main/server.ts +13 -4
- package/src/renderer/components/settings/sections/PlatformsSection.tsx +22 -3
- package/src/renderer/components/team/TeamDetailView.tsx +13 -1
- package/src/renderer/components/team/TeamListView.tsx +13 -1
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 { closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { appendFileSync, 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,7 +55,7 @@ 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
59
|
--daemon Run openHermit in the background
|
|
60
60
|
--version Show current version
|
|
61
61
|
--help Show this help message
|
|
@@ -90,6 +90,7 @@ const daemonRequested = args.includes('--daemon');
|
|
|
90
90
|
const daemonChild = process.env.HERMIT_DAEMON_CHILD === '1';
|
|
91
91
|
const daemonPidPath = path.join(hermitHome, 'openhermit.pid');
|
|
92
92
|
const daemonLogPath = path.join(hermitHome, 'logs', 'openhermit.log');
|
|
93
|
+
const serverLogPath = path.join(hermitHome, 'logs', 'openhermit-server.log');
|
|
93
94
|
const ccConnectConfigPath =
|
|
94
95
|
process.env.HERMIT_CC_CONNECT_CONFIG ||
|
|
95
96
|
process.env.CC_CONNECT_CONFIG ||
|
|
@@ -517,10 +518,10 @@ if (!skipCcConnect) {
|
|
|
517
518
|
const ccBaseUrl = process.env.CC_CONNECT_BASE_URL || 'http://127.0.0.1:9820';
|
|
518
519
|
const alreadyRunning = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 1_000);
|
|
519
520
|
if (alreadyRunning) {
|
|
520
|
-
console.log(`[openHermit]
|
|
521
|
+
console.log(`[openHermit] Runtime service already running: ${ccBaseUrl}`);
|
|
521
522
|
} else {
|
|
522
|
-
console.log('[openHermit] Starting bundled
|
|
523
|
-
console.log(`[openHermit]
|
|
523
|
+
console.log('[openHermit] Starting bundled runtime service...');
|
|
524
|
+
console.log(`[openHermit] Runtime config: ${ccConnectConfigPath}`);
|
|
524
525
|
ccConnectProcess = spawn(process.execPath, [resolveCcConnectRunner(), '-config', ccConnectConfigPath], {
|
|
525
526
|
cwd: repoRoot,
|
|
526
527
|
detached: true,
|
|
@@ -534,7 +535,7 @@ if (!skipCcConnect) {
|
|
|
534
535
|
});
|
|
535
536
|
const ready = await waitForCcConnect(ccBaseUrl, ccTokens.managementToken, 30_000);
|
|
536
537
|
if (!ready) {
|
|
537
|
-
console.warn('[openHermit]
|
|
538
|
+
console.warn('[openHermit] Runtime service did not become ready within 30s; openHermit will keep trying via API.');
|
|
538
539
|
}
|
|
539
540
|
}
|
|
540
541
|
}
|
|
@@ -576,6 +577,29 @@ if (!existsSync(distRenderererDir) || !existsSync(path.join(distRenderererDir, '
|
|
|
576
577
|
// Start the server
|
|
577
578
|
console.log('[openHermit] Launching server...\n');
|
|
578
579
|
|
|
580
|
+
function appendServerLog(chunk) {
|
|
581
|
+
try {
|
|
582
|
+
mkdirSync(path.dirname(serverLogPath), { recursive: true });
|
|
583
|
+
appendFileSync(serverLogPath, chunk);
|
|
584
|
+
} catch {
|
|
585
|
+
// Logging must never block startup.
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function printServerLogTail() {
|
|
590
|
+
try {
|
|
591
|
+
const content = readFileSync(serverLogPath, 'utf-8');
|
|
592
|
+
const lines = content.trimEnd().split(/\r?\n/).slice(-60);
|
|
593
|
+
if (lines.length > 0) {
|
|
594
|
+
console.error(`[openHermit] Server log: ${serverLogPath}`);
|
|
595
|
+
console.error('[openHermit] Last server log lines:');
|
|
596
|
+
console.error(lines.join('\n'));
|
|
597
|
+
}
|
|
598
|
+
} catch {
|
|
599
|
+
console.error(`[openHermit] Server log: ${serverLogPath}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
579
603
|
const serverProcess = spawn(process.execPath, ['--import', resolveAliasLoaderRegister(), '--import', resolveTsxLoader(), 'src/main/server.ts'], {
|
|
580
604
|
cwd: repoRoot,
|
|
581
605
|
detached: true,
|
|
@@ -590,7 +614,17 @@ const serverProcess = spawn(process.execPath, ['--import', resolveAliasLoaderReg
|
|
|
590
614
|
CC_CONNECT_BRIDGE_TOKEN: ccTokens.bridgeToken,
|
|
591
615
|
CC_CONNECT_CONFIG: ccConnectConfigPath,
|
|
592
616
|
},
|
|
593
|
-
stdio: '
|
|
617
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
serverProcess.stdout?.on('data', (chunk) => {
|
|
621
|
+
process.stdout.write(chunk);
|
|
622
|
+
appendServerLog(chunk);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
serverProcess.stderr?.on('data', (chunk) => {
|
|
626
|
+
process.stderr.write(chunk);
|
|
627
|
+
appendServerLog(chunk);
|
|
594
628
|
});
|
|
595
629
|
|
|
596
630
|
serverProcess.on('exit', (code) => {
|
|
@@ -598,6 +632,7 @@ serverProcess.on('exit', (code) => {
|
|
|
598
632
|
signalDaemon(ccConnectProcess?.pid, 'SIGTERM');
|
|
599
633
|
if (code !== 0) {
|
|
600
634
|
console.error(`[openHermit] Server exited with code ${code}`);
|
|
635
|
+
printServerLogTail();
|
|
601
636
|
process.exit(code ?? 1);
|
|
602
637
|
}
|
|
603
638
|
});
|
|
@@ -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.15",
|
|
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
|
@@ -50,6 +50,16 @@ 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
|
+
|
|
53
63
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
54
64
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, '../../package.json'), 'utf-8'));
|
|
55
65
|
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
@@ -1033,11 +1043,10 @@ app.delete<{ Params: { name: string }; Querystring: { deleteFiles?: string } }>(
|
|
|
1033
1043
|
async (request, reply) => {
|
|
1034
1044
|
const teamName = request.params.name;
|
|
1035
1045
|
try {
|
|
1046
|
+
let restartRequired = false;
|
|
1036
1047
|
try {
|
|
1037
1048
|
const result = await cc.deleteProject(teamName);
|
|
1038
|
-
|
|
1039
|
-
await cc.restart();
|
|
1040
|
-
}
|
|
1049
|
+
restartRequired = result.restart_required === true;
|
|
1041
1050
|
} catch (err) {
|
|
1042
1051
|
request.log.warn({ err, teamName }, 'delete cc-connect project failed or project missing');
|
|
1043
1052
|
}
|
|
@@ -1048,7 +1057,7 @@ app.delete<{ Params: { name: string }; Querystring: { deleteFiles?: string } }>(
|
|
|
1048
1057
|
request.log.warn({ err, teamName }, 'delete local team metadata failed or already missing');
|
|
1049
1058
|
}
|
|
1050
1059
|
|
|
1051
|
-
return { ok: true, restartRequired
|
|
1060
|
+
return { ok: true, restartRequired };
|
|
1052
1061
|
} catch (err) {
|
|
1053
1062
|
return reply.code(500).send(reply500(err));
|
|
1054
1063
|
}
|
|
@@ -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 {
|
|
@@ -1830,7 +1830,19 @@ export const TeamDetailView = ({
|
|
|
1830
1830
|
setDeleteConfirmOpen(false);
|
|
1831
1831
|
void (async () => {
|
|
1832
1832
|
try {
|
|
1833
|
-
await deleteTeam(teamName);
|
|
1833
|
+
const result = await deleteTeam(teamName);
|
|
1834
|
+
if (result.restartRequired) {
|
|
1835
|
+
const shouldRestart = await confirm({
|
|
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
|
+
}
|
|
1845
|
+
}
|
|
1834
1846
|
if (tabId) closeTab(tabId);
|
|
1835
1847
|
openTeamsTab();
|
|
1836
1848
|
} catch {
|
|
@@ -539,7 +539,19 @@ export const TeamListView = (): React.JSX.Element => {
|
|
|
539
539
|
});
|
|
540
540
|
if (confirmed) {
|
|
541
541
|
try {
|
|
542
|
-
await deleteTeam(teamName);
|
|
542
|
+
const result = await deleteTeam(teamName);
|
|
543
|
+
if (result.restartRequired) {
|
|
544
|
+
const shouldRestart = await confirm({
|
|
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
|
+
}
|
|
554
|
+
}
|
|
543
555
|
} catch {
|
|
544
556
|
// error via store
|
|
545
557
|
}
|