hermes-web-ui 0.3.1 → 0.3.3

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 (84) hide show
  1. package/dist/client/assets/{Add-cXqy3eJz.js → Add-CIZmky1Q.js} +1 -1
  2. package/dist/client/assets/{Button-BeWnBoDR.js → Button-Cpfqx5bj.js} +1 -1
  3. package/dist/client/assets/{ChannelsView-Do43S3tP.css → ChannelsView-CSo2o-P4.css} +1 -1
  4. package/dist/client/assets/ChannelsView-CqgEMY6f.js +1 -0
  5. package/dist/client/assets/{ChatView-CqA3Wo54.js → ChatView-o_KHRYai.js} +1 -1
  6. package/dist/client/assets/{Close-ySsuLJTC.js → Close-Dx71Ggz9.js} +1 -1
  7. package/dist/client/assets/{FormItem-EcbFnIOF.js → FormItem-D0pABOgK.js} +1 -1
  8. package/dist/client/assets/{Input-DP5LDvGa.js → Input-DmGT4Mu3.js} +1 -1
  9. package/dist/client/assets/{InputNumber-CU7XKbbt.js → InputNumber-CQVOZP6a.js} +1 -1
  10. package/dist/client/assets/JobsView-BwxWBXo3.js +2 -0
  11. package/dist/client/assets/{JobsView-CvuV9mZY.css → JobsView-CVx2Yv-y.css} +1 -1
  12. package/dist/client/assets/{LoginView-CDJXoOEZ.js → LoginView-C4Id2mSY.js} +1 -1
  13. package/dist/client/assets/{LogsView-DS_E9xkh.js → LogsView-H_gfkSSN.js} +1 -1
  14. package/dist/client/assets/{MarkdownRenderer-BntkPd0f.js → MarkdownRenderer-DJK1b6T2.js} +1 -1
  15. package/dist/client/assets/{MemoryView-CCCXW1tv.js → MemoryView-DO4Y2le5.js} +1 -1
  16. package/dist/client/assets/{Modal-D_vF9k8c.js → Modal-fhZL63EZ.js} +1 -1
  17. package/dist/client/assets/ModelsView-9CSt8tCa.js +1 -0
  18. package/dist/client/assets/{ModelsView-BhNEZwC0.css → ModelsView-D1p_2trx.css} +1 -1
  19. package/dist/client/assets/{Popconfirm-IKYJxM7n.js → Popconfirm-CLvbZW3I.js} +1 -1
  20. package/dist/client/assets/{Popover-BLBLYanQ.js → Popover-Dwi5NArH.js} +1 -1
  21. package/dist/client/assets/{ProfilesView-qt3NXito.js → ProfilesView-R_u1mtsl.js} +1 -1
  22. package/dist/client/assets/{Scrollbar-DZGif2x4.js → Scrollbar-zYdleApO.js} +1 -1
  23. package/dist/client/assets/{Select-3KS3TLPL.js → Select-0jFWg4Lf.js} +1 -1
  24. package/dist/client/assets/{SettingRow-D26AUezg.js → SettingRow-LTP_cMLl.js} +1 -1
  25. package/dist/client/assets/{SettingsView-30x1265A.js → SettingsView-5o2GfsQf.js} +2 -2
  26. package/dist/client/assets/{SettingsView-C78xbLXK.css → SettingsView-BIEQOPzq.css} +1 -1
  27. package/dist/client/assets/{SkillsView-qq8Oz7fo.js → SkillsView-Cez6jHIn.js} +1 -1
  28. package/dist/client/assets/{Spin-C9izy3tZ.js → Spin-CfKqXT5a.js} +1 -1
  29. package/dist/client/assets/{Suffix-CIX5CF-j.js → Suffix-jKQO068E.js} +1 -1
  30. package/dist/client/assets/{Switch-D9x35tOv.js → Switch-tk7Pkw6L.js} +1 -1
  31. package/dist/client/assets/{TerminalView-zaRNn2aX.js → TerminalView-C-oFmyFh.js} +1 -1
  32. package/dist/client/assets/{Tooltip-A5--U35v.js → Tooltip-Dc4tc92D.js} +1 -1
  33. package/dist/client/assets/{UsageView-DQ3t8CHE.js → UsageView-SSxSWAFO.js} +1 -1
  34. package/dist/client/assets/{Warning-BZfjWCrB.js → Warning-BPlnDrmx.js} +1 -1
  35. package/dist/client/assets/{_plugin-vue_export-helper-B4hqCVU_.js → _plugin-vue_export-helper-CcX4e_Is.js} +1 -1
  36. package/dist/client/assets/{app-DG_zFxqi.js → app-CcLe99Xa.js} +1 -1
  37. package/dist/client/assets/app-ML9hRyDO.js +1 -0
  38. package/dist/client/assets/{browser-BanYmQkE.js → browser-BfJIjfY3.js} +1 -1
  39. package/dist/client/assets/{chat-C6pq-bYQ.js → chat-CuVG21Hv.js} +2 -2
  40. package/dist/client/assets/composables-BTIEu8ZB.js +1 -0
  41. package/dist/client/assets/{fade-in-scale-up.cssr-B100njI2.js → fade-in-scale-up.cssr-e30I2JjN.js} +1 -1
  42. package/dist/client/assets/index-XufFb2mL.js +284 -0
  43. package/dist/client/assets/{jobs-DK_GVjqW.js → jobs-LBrAH2Mz.js} +1 -1
  44. package/dist/client/assets/{light-B8HxMQKs.js → light-BF--Dz10.js} +1 -1
  45. package/dist/client/assets/{light-BT2gqEar.js → light-BJ4xZyd0.js} +1 -1
  46. package/dist/client/assets/{light-CDN7aXQb.js → light-BLbGAFtS.js} +1 -1
  47. package/dist/client/assets/{light-Dav3NshO.js → light-CCZMDqco.js} +1 -1
  48. package/dist/client/assets/{light-BfPLur6t.js → light-CDuRPTUd.js} +1 -1
  49. package/dist/client/assets/{light-CeMZM90j.js → light-DTBcb4qq.js} +1 -1
  50. package/dist/client/assets/{pinia-DEKB7D30.js → pinia-D2g49IcO.js} +1 -1
  51. package/dist/client/assets/{profiles-BSAOLUpt.js → profiles-DbCu_blp.js} +1 -1
  52. package/dist/client/assets/{router-Zpu1Tghg.js → router-0X4x3p8e.js} +2 -2
  53. package/dist/client/assets/{sessions-1rFjfO8M.js → sessions-SzjWXR9A.js} +1 -1
  54. package/dist/client/assets/{skills-Bu5JdqX-.js → skills-CsjvGVsY.js} +1 -1
  55. package/dist/client/assets/use-compitable-DY4l-k3U.js +1 -0
  56. package/dist/client/assets/{use-message-CFWjMwGX.js → use-message-4kASja7X.js} +1 -1
  57. package/dist/client/assets/{useTheme-CHkzsMnk.js → useTheme-Dh7NTo_V.js} +1 -1
  58. package/dist/client/index.html +27 -27
  59. package/dist/server/index.js +2 -2
  60. package/dist/server/routes/hermes/codex-auth.d.ts +2 -0
  61. package/dist/server/routes/hermes/codex-auth.js +302 -0
  62. package/dist/server/routes/hermes/config.js +15 -3
  63. package/dist/server/routes/hermes/filesystem.js +92 -18
  64. package/dist/server/routes/hermes/index.js +2 -0
  65. package/dist/server/routes/hermes/logs.js +1 -1
  66. package/dist/server/routes/hermes/profiles.js +1 -1
  67. package/dist/server/routes/hermes/sessions.js +1 -1
  68. package/dist/server/routes/hermes/weixin.js +2 -2
  69. package/dist/server/routes/webhook.js +1 -1
  70. package/dist/server/services/hermes/hermes-cli.d.ts +125 -0
  71. package/dist/server/services/hermes/hermes-cli.js +488 -0
  72. package/dist/server/services/hermes/hermes-profile.d.ts +22 -0
  73. package/dist/server/services/hermes/hermes-profile.js +60 -0
  74. package/dist/server/services/hermes/hermes.d.ts +40 -0
  75. package/dist/server/services/hermes/hermes.js +118 -0
  76. package/dist/server/shared/providers.js +64 -20
  77. package/package.json +4 -1
  78. package/dist/client/assets/ChannelsView-tUAkDoSF.js +0 -1
  79. package/dist/client/assets/JobsView-g3zcs8_m.js +0 -2
  80. package/dist/client/assets/ModelsView-C8rPRq6p.js +0 -1
  81. package/dist/client/assets/app-DVQEsGHs.js +0 -1
  82. package/dist/client/assets/composables-xsQwqT-T.js +0 -1
  83. package/dist/client/assets/index-DtDTIaVj.js +0 -284
  84. package/dist/client/assets/use-compitable-Dl0AqAGv.js +0 -1
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.logRoutes = void 0;
40
40
  const router_1 = __importDefault(require("@koa/router"));
41
- const hermesCli = __importStar(require("../../services/hermes-cli"));
41
+ const hermesCli = __importStar(require("../../services/hermes/hermes-cli"));
42
42
  exports.logRoutes = new router_1.default();
43
43
  // List available log files
44
44
  exports.logRoutes.get('/api/hermes/logs', async (ctx) => {
@@ -43,7 +43,7 @@ const promises_1 = require("fs/promises");
43
43
  const path_1 = require("path");
44
44
  const os_1 = require("os");
45
45
  const js_yaml_1 = __importDefault(require("js-yaml"));
46
- const hermesCli = __importStar(require("../../services/hermes-cli"));
46
+ const hermesCli = __importStar(require("../../services/hermes/hermes-cli"));
47
47
  const apiServerDefaults = {
48
48
  enabled: true,
49
49
  host: '127.0.0.1',
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.sessionRoutes = void 0;
40
40
  const router_1 = __importDefault(require("@koa/router"));
41
- const hermesCli = __importStar(require("../../services/hermes-cli"));
41
+ const hermesCli = __importStar(require("../../services/hermes/hermes-cli"));
42
42
  exports.sessionRoutes = new router_1.default();
43
43
  // List sessions from Hermes
44
44
  exports.sessionRoutes.get('/api/hermes/sessions', async (ctx) => {
@@ -8,8 +8,8 @@ const router_1 = __importDefault(require("@koa/router"));
8
8
  const axios_1 = __importDefault(require("axios"));
9
9
  const promises_1 = require("fs/promises");
10
10
  const promises_2 = require("fs/promises");
11
- const hermes_cli_1 = require("../../services/hermes-cli");
12
- const hermes_profile_1 = require("../../services/hermes-profile");
11
+ const hermes_cli_1 = require("../../services/hermes/hermes-cli");
12
+ const hermes_profile_1 = require("../../services/hermes/hermes-profile");
13
13
  const envPath = () => (0, hermes_profile_1.getActiveEnvPath)();
14
14
  const ILINK_BASE = 'https://ilinkai.weixin.qq.com';
15
15
  exports.weixinRoutes = new router_1.default();
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.webhookRoutes = void 0;
7
7
  const router_1 = __importDefault(require("@koa/router"));
8
- const hermes_1 = require("../services/hermes");
8
+ const hermes_1 = require("../services/hermes/hermes");
9
9
  exports.webhookRoutes = new router_1.default();
10
10
  /**
11
11
  * POST /webhook — receive callbacks from Hermes Agent
@@ -0,0 +1,125 @@
1
+ export interface HermesSession {
2
+ id: string;
3
+ source: string;
4
+ user_id: string | null;
5
+ model: string;
6
+ title: string | null;
7
+ started_at: number;
8
+ ended_at: number | null;
9
+ end_reason: string | null;
10
+ message_count: number;
11
+ tool_call_count: number;
12
+ input_tokens: number;
13
+ output_tokens: number;
14
+ cache_read_tokens: number;
15
+ cache_write_tokens: number;
16
+ reasoning_tokens: number;
17
+ billing_provider: string | null;
18
+ estimated_cost_usd: number;
19
+ actual_cost_usd: number | null;
20
+ cost_status: string;
21
+ messages?: any[];
22
+ }
23
+ /**
24
+ * List sessions from Hermes CLI (without messages)
25
+ */
26
+ export declare function listSessions(source?: string, limit?: number): Promise<HermesSession[]>;
27
+ /**
28
+ * Get a single session with messages from Hermes CLI
29
+ */
30
+ export declare function getSession(id: string): Promise<HermesSession | null>;
31
+ /**
32
+ * Delete a session from Hermes CLI
33
+ */
34
+ export declare function deleteSession(id: string): Promise<boolean>;
35
+ /**
36
+ * Rename a session title via Hermes CLI
37
+ */
38
+ export declare function renameSession(id: string, title: string): Promise<boolean>;
39
+ export interface LogFileInfo {
40
+ name: string;
41
+ size: string;
42
+ modified: string;
43
+ }
44
+ /**
45
+ * Get Hermes version
46
+ */
47
+ export declare function getVersion(): Promise<string>;
48
+ /**
49
+ * Start Hermes gateway (uses launchd/systemd)
50
+ */
51
+ export declare function startGateway(): Promise<string>;
52
+ /**
53
+ * Start Hermes gateway in background (for WSL where launchd/systemd is unavailable)
54
+ * Uses "hermes gateway run" as a detached background process
55
+ */
56
+ export declare function startGatewayBackground(): Promise<number | null>;
57
+ /**
58
+ * Restart Hermes gateway
59
+ */
60
+ export declare function restartGateway(): Promise<string>;
61
+ /**
62
+ * Stop Hermes gateway
63
+ */
64
+ export declare function stopGateway(): Promise<string>;
65
+ /**
66
+ * List available log files
67
+ */
68
+ export declare function listLogFiles(): Promise<LogFileInfo[]>;
69
+ /**
70
+ * Read log lines
71
+ */
72
+ export declare function readLogs(logName?: string, lines?: number, level?: string, session?: string, since?: string): Promise<string>;
73
+ export interface HermesProfile {
74
+ name: string;
75
+ active: boolean;
76
+ model: string;
77
+ gateway: string;
78
+ alias: string;
79
+ }
80
+ export interface HermesProfileDetail {
81
+ name: string;
82
+ path: string;
83
+ model: string;
84
+ provider: string;
85
+ gateway: string;
86
+ skills: number;
87
+ hasEnv: boolean;
88
+ hasSoulMd: boolean;
89
+ }
90
+ /**
91
+ * List all profiles
92
+ */
93
+ export declare function listProfiles(): Promise<HermesProfile[]>;
94
+ /**
95
+ * Get profile details
96
+ */
97
+ export declare function getProfile(name: string): Promise<HermesProfileDetail>;
98
+ /**
99
+ * Create a new profile
100
+ */
101
+ export declare function createProfile(name: string, clone?: boolean): Promise<string>;
102
+ /**
103
+ * Delete a profile
104
+ */
105
+ export declare function deleteProfile(name: string): Promise<boolean>;
106
+ /**
107
+ * Rename a profile
108
+ */
109
+ export declare function renameProfile(oldName: string, newName: string): Promise<boolean>;
110
+ /**
111
+ * Switch active profile
112
+ */
113
+ export declare function useProfile(name: string): Promise<string>;
114
+ /**
115
+ * Export profile to archive
116
+ */
117
+ export declare function exportProfile(name: string, outputPath?: string): Promise<string>;
118
+ /**
119
+ * Run hermes setup --non-interactive --reset to generate default config for current profile
120
+ */
121
+ export declare function setupReset(): Promise<string>;
122
+ /**
123
+ * Import profile from archive
124
+ */
125
+ export declare function importProfile(archivePath: string, name?: string): Promise<string>;
@@ -0,0 +1,488 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listSessions = listSessions;
4
+ exports.getSession = getSession;
5
+ exports.deleteSession = deleteSession;
6
+ exports.renameSession = renameSession;
7
+ exports.getVersion = getVersion;
8
+ exports.startGateway = startGateway;
9
+ exports.startGatewayBackground = startGatewayBackground;
10
+ exports.restartGateway = restartGateway;
11
+ exports.stopGateway = stopGateway;
12
+ exports.listLogFiles = listLogFiles;
13
+ exports.readLogs = readLogs;
14
+ exports.listProfiles = listProfiles;
15
+ exports.getProfile = getProfile;
16
+ exports.createProfile = createProfile;
17
+ exports.deleteProfile = deleteProfile;
18
+ exports.renameProfile = renameProfile;
19
+ exports.useProfile = useProfile;
20
+ exports.exportProfile = exportProfile;
21
+ exports.setupReset = setupReset;
22
+ exports.importProfile = importProfile;
23
+ const child_process_1 = require("child_process");
24
+ const fs_1 = require("fs");
25
+ const util_1 = require("util");
26
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
27
+ const execOpts = { windowsHide: true };
28
+ const isDocker = (0, fs_1.existsSync)('/.dockerenv');
29
+ function resolveHermesBin() {
30
+ const envBin = process.env.HERMES_BIN?.trim();
31
+ if (envBin)
32
+ return envBin;
33
+ return 'hermes';
34
+ }
35
+ const HERMES_BIN = resolveHermesBin();
36
+ /**
37
+ * List sessions from Hermes CLI (without messages)
38
+ */
39
+ async function listSessions(source, limit) {
40
+ const args = ['sessions', 'export', '-'];
41
+ if (source)
42
+ args.push('--source', source);
43
+ try {
44
+ const { stdout } = await execFileAsync(HERMES_BIN, args, {
45
+ maxBuffer: 50 * 1024 * 1024, // 50MB
46
+ timeout: 30000,
47
+ ...execOpts,
48
+ });
49
+ const lines = stdout.trim().split('\n').filter(Boolean);
50
+ const sessions = [];
51
+ for (const line of lines) {
52
+ try {
53
+ const raw = JSON.parse(line);
54
+ let title = raw.title;
55
+ if (!title && raw.messages) {
56
+ const firstUser = raw.messages.find((m) => m.role === 'user');
57
+ if (firstUser?.content) {
58
+ const t = String(firstUser.content).slice(0, 40);
59
+ title = t + (String(firstUser.content).length > 40 ? '...' : '');
60
+ }
61
+ }
62
+ sessions.push({
63
+ id: raw.id,
64
+ source: raw.source,
65
+ user_id: raw.user_id,
66
+ model: raw.model,
67
+ title,
68
+ started_at: raw.started_at,
69
+ ended_at: raw.ended_at,
70
+ end_reason: raw.end_reason,
71
+ message_count: raw.message_count,
72
+ tool_call_count: raw.tool_call_count,
73
+ input_tokens: raw.input_tokens,
74
+ output_tokens: raw.output_tokens,
75
+ cache_read_tokens: raw.cache_read_tokens || 0,
76
+ cache_write_tokens: raw.cache_write_tokens || 0,
77
+ reasoning_tokens: raw.reasoning_tokens || 0,
78
+ billing_provider: raw.billing_provider,
79
+ estimated_cost_usd: raw.estimated_cost_usd,
80
+ actual_cost_usd: raw.actual_cost_usd ?? null,
81
+ cost_status: raw.cost_status || '',
82
+ });
83
+ }
84
+ catch { /* skip malformed lines */ }
85
+ }
86
+ // Sort by started_at descending
87
+ sessions.sort((a, b) => b.started_at - a.started_at);
88
+ if (limit && limit > 0) {
89
+ return sessions.slice(0, limit);
90
+ }
91
+ return sessions;
92
+ }
93
+ catch (err) {
94
+ console.error('[Hermes CLI] sessions export failed:', err.message);
95
+ throw new Error(`Failed to list sessions: ${err.message}`);
96
+ }
97
+ }
98
+ /**
99
+ * Get a single session with messages from Hermes CLI
100
+ */
101
+ async function getSession(id) {
102
+ const args = ['sessions', 'export', '-', '--session-id', id];
103
+ try {
104
+ const { stdout } = await execFileAsync(HERMES_BIN, args, {
105
+ maxBuffer: 50 * 1024 * 1024,
106
+ timeout: 30000,
107
+ ...execOpts,
108
+ });
109
+ const lines = stdout.trim().split('\n').filter(Boolean);
110
+ if (lines.length === 0)
111
+ return null;
112
+ if (!lines[0].startsWith('{'))
113
+ return null;
114
+ const raw = JSON.parse(lines[0]);
115
+ return {
116
+ id: raw.id,
117
+ source: raw.source,
118
+ user_id: raw.user_id,
119
+ model: raw.model,
120
+ title: raw.title,
121
+ started_at: raw.started_at,
122
+ ended_at: raw.ended_at,
123
+ end_reason: raw.end_reason,
124
+ message_count: raw.message_count,
125
+ tool_call_count: raw.tool_call_count,
126
+ input_tokens: raw.input_tokens,
127
+ output_tokens: raw.output_tokens,
128
+ cache_read_tokens: raw.cache_read_tokens || 0,
129
+ cache_write_tokens: raw.cache_write_tokens || 0,
130
+ reasoning_tokens: raw.reasoning_tokens || 0,
131
+ billing_provider: raw.billing_provider,
132
+ estimated_cost_usd: raw.estimated_cost_usd,
133
+ actual_cost_usd: raw.actual_cost_usd ?? null,
134
+ cost_status: raw.cost_status || '',
135
+ messages: raw.messages,
136
+ };
137
+ }
138
+ catch (err) {
139
+ if (err.code === 1 || err.status === 1)
140
+ return null;
141
+ console.error('[Hermes CLI] session export failed:', err.message);
142
+ throw new Error(`Failed to get session: ${err.message}`);
143
+ }
144
+ }
145
+ /**
146
+ * Delete a session from Hermes CLI
147
+ */
148
+ async function deleteSession(id) {
149
+ try {
150
+ await execFileAsync(HERMES_BIN, ['sessions', 'delete', id, '--yes'], {
151
+ timeout: 10000,
152
+ ...execOpts,
153
+ });
154
+ return true;
155
+ }
156
+ catch (err) {
157
+ console.error('[Hermes CLI] session delete failed:', err.message);
158
+ return false;
159
+ }
160
+ }
161
+ /**
162
+ * Rename a session title via Hermes CLI
163
+ */
164
+ async function renameSession(id, title) {
165
+ try {
166
+ await execFileAsync(HERMES_BIN, ['sessions', 'rename', id, title], {
167
+ timeout: 10000,
168
+ ...execOpts,
169
+ });
170
+ return true;
171
+ }
172
+ catch (err) {
173
+ console.error('[Hermes CLI] session rename failed:', err.message);
174
+ return false;
175
+ }
176
+ }
177
+ /**
178
+ * Get Hermes version
179
+ */
180
+ async function getVersion() {
181
+ try {
182
+ const { stdout } = await execFileAsync(HERMES_BIN, ['--version'], { timeout: 5000, ...execOpts });
183
+ return stdout.trim();
184
+ }
185
+ catch {
186
+ return '';
187
+ }
188
+ }
189
+ /**
190
+ * Start Hermes gateway (uses launchd/systemd)
191
+ */
192
+ async function startGateway() {
193
+ if (isDocker) {
194
+ const pid = await startGatewayBackground();
195
+ return pid ? `Gateway started (PID: ${pid})` : 'Gateway start triggered';
196
+ }
197
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'start'], {
198
+ timeout: 30000,
199
+ ...execOpts,
200
+ });
201
+ return stdout || stderr;
202
+ }
203
+ /**
204
+ * Start Hermes gateway in background (for WSL where launchd/systemd is unavailable)
205
+ * Uses "hermes gateway run" as a detached background process
206
+ */
207
+ async function startGatewayBackground() {
208
+ const { spawn } = require('child_process');
209
+ const child = spawn(HERMES_BIN, ['gateway', 'run'], {
210
+ detached: true,
211
+ stdio: 'ignore',
212
+ windowsHide: true,
213
+ });
214
+ child.unref();
215
+ return child.pid ?? null;
216
+ }
217
+ /**
218
+ * Restart Hermes gateway
219
+ */
220
+ async function restartGateway() {
221
+ if (isDocker) {
222
+ try {
223
+ await stopGateway();
224
+ }
225
+ catch { }
226
+ const pid = await startGatewayBackground();
227
+ return pid ? `Gateway restarted (PID: ${pid})` : 'Gateway restart triggered';
228
+ }
229
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'restart'], {
230
+ timeout: 30000,
231
+ ...execOpts,
232
+ });
233
+ return stdout || stderr;
234
+ }
235
+ /**
236
+ * Stop Hermes gateway
237
+ */
238
+ async function stopGateway() {
239
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['gateway', 'stop'], {
240
+ timeout: 30000,
241
+ ...execOpts,
242
+ });
243
+ return stdout || stderr;
244
+ }
245
+ /**
246
+ * List available log files
247
+ */
248
+ async function listLogFiles() {
249
+ try {
250
+ const { stdout } = await execFileAsync(HERMES_BIN, ['logs', 'list'], {
251
+ timeout: 10000,
252
+ ...execOpts,
253
+ });
254
+ const files = [];
255
+ const lines = stdout.trim().split('\n').filter(l => l.includes('.log'));
256
+ for (const line of lines) {
257
+ const match = line.match(/^\s+(\S+)\s+([\d.]+\w+)\s+(.+)$/);
258
+ if (match) {
259
+ const rawName = match[1];
260
+ const name = rawName.replace(/\.log$/, '');
261
+ if (['agent', 'errors', 'gateway'].includes(name)) {
262
+ files.push({ name, size: match[2], modified: match[3].trim() });
263
+ }
264
+ }
265
+ }
266
+ return files;
267
+ }
268
+ catch (err) {
269
+ console.error('[Hermes CLI] logs list failed:', err.message);
270
+ return [];
271
+ }
272
+ }
273
+ /**
274
+ * Read log lines
275
+ */
276
+ async function readLogs(logName = 'agent', lines = 100, level, session, since) {
277
+ const args = ['logs', logName, '-n', String(lines)];
278
+ if (level)
279
+ args.push('--level', level);
280
+ if (session)
281
+ args.push('--session', session);
282
+ if (since)
283
+ args.push('--since', since);
284
+ try {
285
+ const { stdout } = await execFileAsync(HERMES_BIN, args, {
286
+ maxBuffer: 10 * 1024 * 1024,
287
+ timeout: 15000,
288
+ ...execOpts,
289
+ });
290
+ return stdout;
291
+ }
292
+ catch (err) {
293
+ console.error('[Hermes CLI] logs read failed:', err.message);
294
+ throw new Error(`Failed to read logs: ${err.message}`);
295
+ }
296
+ }
297
+ /**
298
+ * List all profiles
299
+ */
300
+ async function listProfiles() {
301
+ try {
302
+ const { stdout } = await execFileAsync(HERMES_BIN, ['profile', 'list'], {
303
+ timeout: 10000,
304
+ ...execOpts,
305
+ });
306
+ const lines = stdout.trim().split('\n').filter(Boolean);
307
+ const profiles = [];
308
+ // Skip header lines (starts with " Profile" or " ─")
309
+ for (const line of lines) {
310
+ if (line.startsWith(' Profile') || line.match(/^ ─/))
311
+ continue;
312
+ const match = line.match(/^\s+(◆)?(\S+)\s{2,}(\S+)\s{2,}(\S+)\s{2,}(.*)$/);
313
+ if (match) {
314
+ profiles.push({
315
+ name: match[2],
316
+ active: !!match[1],
317
+ model: match[3],
318
+ gateway: match[4],
319
+ alias: match[5].trim() === '—' ? '' : match[5].trim(),
320
+ });
321
+ }
322
+ }
323
+ return profiles;
324
+ }
325
+ catch (err) {
326
+ console.error('[Hermes CLI] profile list failed:', err.message);
327
+ throw new Error(`Failed to list profiles: ${err.message}`);
328
+ }
329
+ }
330
+ /**
331
+ * Get profile details
332
+ */
333
+ async function getProfile(name) {
334
+ try {
335
+ const { stdout } = await execFileAsync(HERMES_BIN, ['profile', 'show', name], {
336
+ timeout: 10000,
337
+ ...execOpts,
338
+ });
339
+ const result = {};
340
+ for (const line of stdout.trim().split('\n')) {
341
+ const match = line.match(/^(\w[\w\s]*?):\s+(.+)$/);
342
+ if (match) {
343
+ result[match[1].trim().toLowerCase().replace(/\s+/g, '_')] = match[2].trim();
344
+ }
345
+ }
346
+ const modelFull = result.model || '';
347
+ const providerMatch = modelFull.match(/\((.+)\)/);
348
+ const model = providerMatch ? modelFull.replace(/\s*\(.+\)/, '').trim() : modelFull;
349
+ return {
350
+ name: result.profile || name,
351
+ path: result.path || '',
352
+ model,
353
+ provider: providerMatch ? providerMatch[1] : '',
354
+ gateway: result.gateway || '',
355
+ skills: parseInt(result.skills || '0', 10),
356
+ hasEnv: result['.env'] === 'exists',
357
+ hasSoulMd: result.soul_md === 'exists',
358
+ };
359
+ }
360
+ catch (err) {
361
+ if (err.code === 1 || err.status === 1) {
362
+ throw new Error(`Profile "${name}" not found`);
363
+ }
364
+ console.error('[Hermes CLI] profile show failed:', err.message);
365
+ throw new Error(`Failed to get profile: ${err.message}`);
366
+ }
367
+ }
368
+ /**
369
+ * Create a new profile
370
+ */
371
+ async function createProfile(name, clone) {
372
+ const args = ['profile', 'create', name];
373
+ if (clone)
374
+ args.push('--clone');
375
+ try {
376
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
377
+ timeout: 15000,
378
+ ...execOpts,
379
+ });
380
+ return stdout || stderr;
381
+ }
382
+ catch (err) {
383
+ console.error('[Hermes CLI] profile create failed:', err.message);
384
+ throw new Error(`Failed to create profile: ${err.message}`);
385
+ }
386
+ }
387
+ /**
388
+ * Delete a profile
389
+ */
390
+ async function deleteProfile(name) {
391
+ try {
392
+ await execFileAsync(HERMES_BIN, ['profile', 'delete', name, '--yes'], {
393
+ timeout: 10000,
394
+ ...execOpts,
395
+ });
396
+ return true;
397
+ }
398
+ catch (err) {
399
+ console.error('[Hermes CLI] profile delete failed:', err.message);
400
+ return false;
401
+ }
402
+ }
403
+ /**
404
+ * Rename a profile
405
+ */
406
+ async function renameProfile(oldName, newName) {
407
+ try {
408
+ await execFileAsync(HERMES_BIN, ['profile', 'rename', oldName, newName], {
409
+ timeout: 10000,
410
+ ...execOpts,
411
+ });
412
+ return true;
413
+ }
414
+ catch (err) {
415
+ console.error('[Hermes CLI] profile rename failed:', err.message);
416
+ return false;
417
+ }
418
+ }
419
+ /**
420
+ * Switch active profile
421
+ */
422
+ async function useProfile(name) {
423
+ try {
424
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['profile', 'use', name], {
425
+ timeout: 10000,
426
+ ...execOpts,
427
+ });
428
+ return stdout || stderr;
429
+ }
430
+ catch (err) {
431
+ console.error('[Hermes CLI] profile use failed:', err.message);
432
+ throw new Error(`Failed to switch profile: ${err.message}`);
433
+ }
434
+ }
435
+ /**
436
+ * Export profile to archive
437
+ */
438
+ async function exportProfile(name, outputPath) {
439
+ const args = ['profile', 'export', name];
440
+ if (outputPath)
441
+ args.push('--output', outputPath);
442
+ try {
443
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
444
+ timeout: 60000,
445
+ ...execOpts,
446
+ });
447
+ return stdout || stderr;
448
+ }
449
+ catch (err) {
450
+ console.error('[Hermes CLI] profile export failed:', err.message);
451
+ throw new Error(`Failed to export profile: ${err.message}`);
452
+ }
453
+ }
454
+ /**
455
+ * Run hermes setup --non-interactive --reset to generate default config for current profile
456
+ */
457
+ async function setupReset() {
458
+ try {
459
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, ['setup', '--non-interactive', '--reset'], {
460
+ timeout: 30000,
461
+ ...execOpts,
462
+ });
463
+ return stdout || stderr;
464
+ }
465
+ catch (err) {
466
+ console.error('[Hermes CLI] setup reset failed:', err.message);
467
+ throw new Error(`Failed to reset config: ${err.message}`);
468
+ }
469
+ }
470
+ /**
471
+ * Import profile from archive
472
+ */
473
+ async function importProfile(archivePath, name) {
474
+ const args = ['profile', 'import', archivePath];
475
+ if (name)
476
+ args.push('--name', name);
477
+ try {
478
+ const { stdout, stderr } = await execFileAsync(HERMES_BIN, args, {
479
+ timeout: 60000,
480
+ ...execOpts,
481
+ });
482
+ return stdout || stderr;
483
+ }
484
+ catch (err) {
485
+ console.error('[Hermes CLI] profile import failed:', err.message);
486
+ throw new Error(`Failed to import profile: ${err.message}`);
487
+ }
488
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Get the active profile's home directory.
3
+ * default → ~/.hermes/
4
+ * other → ~/.hermes/profiles/{name}/
5
+ */
6
+ export declare function getActiveProfileDir(): string;
7
+ /**
8
+ * Get the active profile's config.yaml path.
9
+ */
10
+ export declare function getActiveConfigPath(): string;
11
+ /**
12
+ * Get the active profile's auth.json path.
13
+ */
14
+ export declare function getActiveAuthPath(): string;
15
+ /**
16
+ * Get the active profile's .env path.
17
+ */
18
+ export declare function getActiveEnvPath(): string;
19
+ /**
20
+ * Get the active profile name.
21
+ */
22
+ export declare function getActiveProfileName(): string;