@saleso.innovations/bridge 0.1.41 → 0.1.43

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 (61) hide show
  1. package/dist/client.d.ts +2 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +5 -1
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/constants.js +3 -0
  7. package/dist/cronList.d.ts +2 -0
  8. package/dist/cronList.d.ts.map +1 -1
  9. package/dist/cronList.js +11 -8
  10. package/dist/cronWatcher.d.ts +1 -1
  11. package/dist/cronWatcher.d.ts.map +1 -1
  12. package/dist/cronWatcher.js +70 -29
  13. package/dist/ensureHermesApi.d.ts +1 -1
  14. package/dist/ensureHermesApi.d.ts.map +1 -1
  15. package/dist/ensureHermesApi.js +14 -10
  16. package/dist/gatewayControl.d.ts +3 -3
  17. package/dist/gatewayControl.d.ts.map +1 -1
  18. package/dist/gatewayControl.js +58 -24
  19. package/dist/hermesCommands.d.ts +1 -1
  20. package/dist/hermesCommands.d.ts.map +1 -1
  21. package/dist/hermesCommands.js +48 -27
  22. package/dist/hermesFileCommands.d.ts +5 -5
  23. package/dist/hermesFileCommands.d.ts.map +1 -1
  24. package/dist/hermesFileCommands.js +10 -10
  25. package/dist/hermesFiles.d.ts +7 -6
  26. package/dist/hermesFiles.d.ts.map +1 -1
  27. package/dist/hermesFiles.js +11 -14
  28. package/dist/hermesForwarder.d.ts +2 -0
  29. package/dist/hermesForwarder.d.ts.map +1 -1
  30. package/dist/hermesForwarder.js +49 -24
  31. package/dist/hermesSessionDb.d.ts +10 -6
  32. package/dist/hermesSessionDb.d.ts.map +1 -1
  33. package/dist/hermesSessionDb.js +48 -24
  34. package/dist/hermesStructuredContent.d.ts +8 -0
  35. package/dist/hermesStructuredContent.d.ts.map +1 -0
  36. package/dist/hermesStructuredContent.js +197 -0
  37. package/dist/hermesStructuredContent.test.d.ts +2 -0
  38. package/dist/hermesStructuredContent.test.d.ts.map +1 -0
  39. package/dist/hermesStructuredContent.test.js +15 -0
  40. package/dist/index.d.ts +3 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -1
  43. package/dist/mcpList.d.ts +3 -2
  44. package/dist/mcpList.d.ts.map +1 -1
  45. package/dist/mcpList.js +5 -5
  46. package/dist/profiles.d.ts +66 -0
  47. package/dist/profiles.d.ts.map +1 -0
  48. package/dist/profiles.js +409 -0
  49. package/dist/renameHermesSession.d.ts +1 -1
  50. package/dist/renameHermesSession.d.ts.map +1 -1
  51. package/dist/renameHermesSession.js +2 -2
  52. package/dist/skillLearnedDetector.d.ts +1 -1
  53. package/dist/skillLearnedDetector.d.ts.map +1 -1
  54. package/dist/skillLearnedDetector.js +2 -3
  55. package/dist/skillsList.d.ts +5 -2
  56. package/dist/skillsList.d.ts.map +1 -1
  57. package/dist/skillsList.js +14 -14
  58. package/dist/toolsList.d.ts +2 -2
  59. package/dist/toolsList.d.ts.map +1 -1
  60. package/dist/toolsList.js +4 -4
  61. package/package.json +2 -2
@@ -2,12 +2,16 @@ import { readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import Database from "better-sqlite3";
5
- function hermesStateDbPath() {
6
- const home = process.env.HERMES_HOME?.trim() || join(homedir(), ".hermes");
7
- return join(home, "state.db");
5
+ import { unwrapHermesStructuredContent } from "./hermesStructuredContent.js";
6
+ /** Default state-db home (the default profile). Kept local to avoid a cycle with profiles.ts. */
7
+ function resolveStateHome(home) {
8
+ return home?.trim() || process.env.HERMES_HOME?.trim() || join(homedir(), ".hermes");
8
9
  }
9
- function openReadOnlyDb() {
10
- const dbPath = hermesStateDbPath();
10
+ function hermesStateDbPath(home) {
11
+ return join(resolveStateHome(home), "state.db");
12
+ }
13
+ function openReadOnlyDb(home) {
14
+ const dbPath = hermesStateDbPath(home);
11
15
  return new Database(dbPath, { readonly: true, fileMustExist: true });
12
16
  }
13
17
  function decodeMessageContent(raw) {
@@ -24,6 +28,9 @@ function decodeMessageContent(raw) {
24
28
  .map((part) => part.text)
25
29
  .join("\n");
26
30
  }
31
+ if (parsed && typeof parsed === "object") {
32
+ return unwrapHermesStructuredContent(raw);
33
+ }
27
34
  }
28
35
  catch {
29
36
  // Plain text content.
@@ -109,11 +116,11 @@ function sessionTitleFromFirstUserMessage(db, sessionId) {
109
116
  function sessionTitleForLookupId(db, sessionId) {
110
117
  return sessionTitleFromChain(db, sessionId) ?? sessionTitleFromFirstUserMessage(db, sessionId);
111
118
  }
112
- export function resolveSessionTitle(sessionId) {
113
- if (!sessionId.trim() || !hermesStateDbExists()) {
119
+ export function resolveSessionTitle(sessionId, home) {
120
+ if (!sessionId.trim() || !hermesStateDbExists(home)) {
114
121
  return null;
115
122
  }
116
- const db = openReadOnlyDb();
123
+ const db = openReadOnlyDb(home);
117
124
  try {
118
125
  return sessionTitleForLookupId(db, sessionId);
119
126
  }
@@ -124,16 +131,16 @@ export function resolveSessionTitle(sessionId) {
124
131
  db.close();
125
132
  }
126
133
  }
127
- export function resolveSessionTitles(sessionIds) {
134
+ export function resolveSessionTitles(sessionIds, home) {
128
135
  const uniqueIds = [...new Set(sessionIds.map((id) => id.trim()).filter(Boolean))];
129
136
  const titles = {};
130
137
  for (const sessionId of uniqueIds) {
131
138
  titles[sessionId] = null;
132
139
  }
133
- if (uniqueIds.length === 0 || !hermesStateDbExists()) {
140
+ if (uniqueIds.length === 0 || !hermesStateDbExists(home)) {
134
141
  return titles;
135
142
  }
136
- const db = openReadOnlyDb();
143
+ const db = openReadOnlyDb(home);
137
144
  try {
138
145
  for (const sessionId of uniqueIds) {
139
146
  titles[sessionId] = sessionTitleForLookupId(db, sessionId);
@@ -148,7 +155,7 @@ export function resolveSessionTitles(sessionIds) {
148
155
  return titles;
149
156
  }
150
157
  export function listSessionMessages(sessionId, options = {}) {
151
- const db = openReadOnlyDb();
158
+ const db = openReadOnlyDb(options.home);
152
159
  try {
153
160
  const resolved = resolveResumeSessionId(db, sessionId);
154
161
  const chain = sessionAncestorChain(db, resolved);
@@ -181,8 +188,8 @@ export function listSessionMessages(sessionId, options = {}) {
181
188
  db.close();
182
189
  }
183
190
  }
184
- export function getLatestTurnMessageIds(sessionId) {
185
- const db = openReadOnlyDb();
191
+ export function getLatestTurnMessageIds(sessionId, home) {
192
+ const db = openReadOnlyDb(home);
186
193
  try {
187
194
  const resolved = resolveResumeSessionId(db, sessionId);
188
195
  const chain = sessionAncestorChain(db, resolved);
@@ -245,12 +252,12 @@ function firstUserMessageContent(db, sessionId) {
245
252
  return decodeMessageContent(row?.content ?? null);
246
253
  }
247
254
  export function listHermesSessions(options = {}) {
248
- if (!hermesStateDbExists()) {
255
+ if (!hermesStateDbExists(options.home)) {
249
256
  return [];
250
257
  }
251
258
  const limit = Math.min(Math.max(options.limit ?? 200, 1), 500);
252
259
  const fetchLimit = Math.min(limit * 4, 500);
253
- const db = openReadOnlyDb();
260
+ const db = openReadOnlyDb(options.home);
254
261
  try {
255
262
  const rows = db
256
263
  .prepare(`SELECT
@@ -313,15 +320,15 @@ function emptySessionUsage(sessionId, resolvedSessionId) {
313
320
  messageCount: 0,
314
321
  };
315
322
  }
316
- export function getSessionUsage(sessionId) {
323
+ export function getSessionUsage(sessionId, home) {
317
324
  const trimmed = sessionId.trim();
318
325
  if (!trimmed) {
319
326
  return emptySessionUsage(sessionId, sessionId);
320
327
  }
321
- if (!hermesStateDbExists()) {
328
+ if (!hermesStateDbExists(home)) {
322
329
  return emptySessionUsage(trimmed, trimmed);
323
330
  }
324
- const db = openReadOnlyDb();
331
+ const db = openReadOnlyDb(home);
325
332
  try {
326
333
  const resolved = resolveResumeSessionId(db, trimmed);
327
334
  const chain = sessionAncestorChain(db, resolved);
@@ -366,20 +373,20 @@ export function getSessionUsage(sessionId) {
366
373
  db.close();
367
374
  }
368
375
  }
369
- export function hermesStateDbExists() {
376
+ export function hermesStateDbExists(home) {
370
377
  try {
371
- readFileSync(hermesStateDbPath());
378
+ readFileSync(hermesStateDbPath(home));
372
379
  return true;
373
380
  }
374
381
  catch {
375
382
  return false;
376
383
  }
377
384
  }
378
- export function countUserMessagesSent() {
379
- if (!hermesStateDbExists()) {
385
+ export function countUserMessagesSent(home) {
386
+ if (!hermesStateDbExists(home)) {
380
387
  return { count: 0 };
381
388
  }
382
- const db = openReadOnlyDb();
389
+ const db = openReadOnlyDb(home);
383
390
  try {
384
391
  const row = db
385
392
  .prepare("SELECT COUNT(*) AS count FROM messages WHERE role = 'user'")
@@ -393,3 +400,20 @@ export function countUserMessagesSent() {
393
400
  db.close();
394
401
  }
395
402
  }
403
+ /** Total number of sessions in a profile's state.db (best-effort). */
404
+ export function countSessions(home) {
405
+ if (!hermesStateDbExists(home)) {
406
+ return 0;
407
+ }
408
+ const db = openReadOnlyDb(home);
409
+ try {
410
+ const row = db.prepare("SELECT COUNT(*) AS count FROM sessions").get();
411
+ return row?.count ?? 0;
412
+ }
413
+ catch {
414
+ return 0;
415
+ }
416
+ finally {
417
+ db.close();
418
+ }
419
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Keep in sync with @repo/backend-types/src/hermesStructuredContent.ts
3
+ * (duplicated here so the published bridge package stays self-contained).
4
+ */
5
+ export declare function isHermesStructuredContent(content: string): boolean;
6
+ export declare function unwrapHermesStructuredContent(content: string): string;
7
+ export declare function looksLikeHermesStructuredStream(text: string): boolean;
8
+ //# sourceMappingURL=hermesStructuredContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermesStructuredContent.d.ts","sourceRoot":"","sources":["../src/hermesStructuredContent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0CH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAElE;AA6JD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMrE;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIrE"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Keep in sync with @repo/backend-types/src/hermesStructuredContent.ts
3
+ * (duplicated here so the published bridge package stays self-contained).
4
+ */
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ }
8
+ function asString(value) {
9
+ return typeof value === "string" ? value : undefined;
10
+ }
11
+ function stringArray(value) {
12
+ if (!Array.isArray(value))
13
+ return [];
14
+ return value.filter((entry) => typeof entry === "string");
15
+ }
16
+ function stringMatrix(value) {
17
+ if (!Array.isArray(value))
18
+ return [];
19
+ return value
20
+ .filter((row) => Array.isArray(row))
21
+ .map((row) => row.filter((cell) => typeof cell === "string"));
22
+ }
23
+ function parseHermesStructuredRoot(text) {
24
+ const trimmed = text.trim();
25
+ if (!trimmed.startsWith("{"))
26
+ return null;
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(trimmed);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ if (!isRecord(parsed) || typeof parsed.component !== "string") {
35
+ return null;
36
+ }
37
+ return parsed;
38
+ }
39
+ export function isHermesStructuredContent(content) {
40
+ return parseHermesStructuredRoot(content) !== null;
41
+ }
42
+ function hermesChildren(node) {
43
+ const children = node.children;
44
+ if (!Array.isArray(children))
45
+ return [];
46
+ return children.filter(isRecord);
47
+ }
48
+ function joinBlocks(blocks) {
49
+ return blocks
50
+ .map((block) => block.trim())
51
+ .filter((block) => block.length > 0)
52
+ .join("\n\n");
53
+ }
54
+ function markdownTable(headers, rows) {
55
+ if (headers.length === 0)
56
+ return "";
57
+ const separator = headers.map(() => "---");
58
+ const body = rows.map((row) => {
59
+ const cells = headers.map((_, index) => row[index] ?? "");
60
+ return `| ${cells.join(" | ")} |`;
61
+ });
62
+ return joinBlocks([
63
+ `| ${headers.join(" | ")} |`,
64
+ `| ${separator.join(" | ")} |`,
65
+ ...body,
66
+ ]);
67
+ }
68
+ function nodeToPlainText(node) {
69
+ const component = node.component;
70
+ if (typeof component !== "string")
71
+ return "";
72
+ switch (component) {
73
+ case "root":
74
+ return joinBlocks(hermesChildren(node).map(nodeToPlainText));
75
+ case "text":
76
+ return asString(node.text) ?? "";
77
+ case "heading": {
78
+ const text = asString(node.text);
79
+ if (!text)
80
+ return "";
81
+ const level = node.level === 3 ? 3 : 2;
82
+ return `${"#".repeat(level)} ${text}`;
83
+ }
84
+ case "table": {
85
+ const headers = stringArray(node.headers).length > 0
86
+ ? stringArray(node.headers)
87
+ : stringArray(node.columns);
88
+ const rows = stringMatrix(node.rows);
89
+ if (headers.length === 0)
90
+ return "";
91
+ const caption = asString(node.caption);
92
+ const table = markdownTable(headers, rows);
93
+ return caption ? joinBlocks([caption, table]) : table;
94
+ }
95
+ case "list": {
96
+ const items = stringArray(node.items);
97
+ if (items.length === 0)
98
+ return "";
99
+ const style = node.style === "numbered" ? "numbered" : "bullet";
100
+ return items
101
+ .map((item, index) => (style === "numbered" ? `${index + 1}. ${item}` : `- ${item}`))
102
+ .join("\n");
103
+ }
104
+ case "code": {
105
+ const content = asString(node.content) ?? "";
106
+ const language = asString(node.language) ?? "";
107
+ return `\`\`\`${language}\n${content}\n\`\`\``;
108
+ }
109
+ case "link": {
110
+ const label = asString(node.label);
111
+ const url = asString(node.url);
112
+ if (label && url)
113
+ return `${label}: ${url}`;
114
+ return label ?? url ?? "";
115
+ }
116
+ case "key_value":
117
+ case "stat_cards": {
118
+ const items = Array.isArray(node.items) ? node.items.filter(isRecord) : [];
119
+ return joinBlocks(items.map((item) => {
120
+ const label = asString(item.label);
121
+ const value = asString(item.value);
122
+ if (!label || !value)
123
+ return "";
124
+ const hint = asString(item.hint);
125
+ return hint ? `${label}: ${value} (${hint})` : `${label}: ${value}`;
126
+ }));
127
+ }
128
+ case "callout": {
129
+ const title = asString(node.title);
130
+ const body = asString(node.body) ?? "";
131
+ return joinBlocks([title, body].filter((part) => Boolean(part)));
132
+ }
133
+ case "choice": {
134
+ const prompt = asString(node.prompt) ?? "";
135
+ const options = Array.isArray(node.options) ? node.options.filter(isRecord) : [];
136
+ const optionLines = options
137
+ .map((option) => asString(option.label) ?? asString(option.id))
138
+ .filter((label) => Boolean(label))
139
+ .map((label) => `- ${label}`);
140
+ return joinBlocks([prompt, optionLines.join("\n")]);
141
+ }
142
+ case "yes_no":
143
+ return asString(node.prompt) ?? "";
144
+ case "suggestions": {
145
+ const items = Array.isArray(node.items) ? node.items.filter(isRecord) : [];
146
+ return items
147
+ .map((item) => asString(item.label) ?? asString(item.id))
148
+ .filter((label) => Boolean(label))
149
+ .map((label) => `- ${label}`)
150
+ .join("\n");
151
+ }
152
+ case "button_row": {
153
+ const buttons = Array.isArray(node.buttons) ? node.buttons.filter(isRecord) : [];
154
+ return buttons
155
+ .map((button) => asString(button.label) ?? asString(button.id))
156
+ .filter((label) => Boolean(label))
157
+ .join(" · ");
158
+ }
159
+ case "chart": {
160
+ const title = asString(node.title);
161
+ const labels = stringArray(node.labels);
162
+ const values = Array.isArray(node.values)
163
+ ? node.values.map((value) => (typeof value === "number" ? String(value) : String(value ?? "")))
164
+ : [];
165
+ const series = labels
166
+ .map((label, index) => {
167
+ const value = values[index];
168
+ return value ? `${label}: ${value}` : label;
169
+ })
170
+ .join("\n");
171
+ return joinBlocks([title, series].filter((part) => Boolean(part)));
172
+ }
173
+ default: {
174
+ const text = asString(node.text) ?? asString(node.body) ?? asString(node.content);
175
+ if (text)
176
+ return text;
177
+ const children = hermesChildren(node);
178
+ if (children.length > 0) {
179
+ return joinBlocks(children.map(nodeToPlainText));
180
+ }
181
+ return "";
182
+ }
183
+ }
184
+ }
185
+ export function unwrapHermesStructuredContent(content) {
186
+ const root = parseHermesStructuredRoot(content);
187
+ if (!root)
188
+ return content;
189
+ const plain = nodeToPlainText(root).trim();
190
+ return plain.length > 0 ? plain : content;
191
+ }
192
+ export function looksLikeHermesStructuredStream(text) {
193
+ const trimmed = text.trimStart();
194
+ if (!trimmed.startsWith("{"))
195
+ return false;
196
+ return /^\{\s*"component"\s*:/.test(trimmed);
197
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hermesStructuredContent.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermesStructuredContent.test.d.ts","sourceRoot":"","sources":["../src/hermesStructuredContent.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { isHermesStructuredContent, looksLikeHermesStructuredStream, unwrapHermesStructuredContent, } from "./hermesStructuredContent.js";
4
+ test("unwrapHermesStructuredContent extracts simple text block", () => {
5
+ const input = JSON.stringify({
6
+ component: "root",
7
+ children: [{ component: "text", text: "Det går fint! Hvordan står det til med deg?" }],
8
+ });
9
+ assert.equal(isHermesStructuredContent(input), true);
10
+ assert.equal(unwrapHermesStructuredContent(input), "Det går fint! Hvordan står det til med deg?");
11
+ });
12
+ test("looksLikeHermesStructuredStream detects early JSON envelope", () => {
13
+ assert.equal(looksLikeHermesStructuredStream('{"component":"root"'), true);
14
+ assert.equal(looksLikeHermesStructuredStream("Hello"), false);
15
+ });
package/dist/index.d.ts CHANGED
@@ -19,5 +19,7 @@ export { backfillCronDeliveries, describeCronDeliveryState } from "./cronBackfil
19
19
  export { resolveActiveConversationId, rememberConversationId } from "./activeConversation.js";
20
20
  export { runBridgeDaemon } from "./daemon.js";
21
21
  export type { RunBridgeOptions } from "./daemon.js";
22
- export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, DEFAULT_BRIDGE_CAPABILITIES, HERMES_COMMANDS_CAPABILITY, HERMES_SESSION_TITLES_CAPABILITY, HERMES_SESSIONS_LIST_CAPABILITY, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
22
+ export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, DEFAULT_BRIDGE_CAPABILITIES, HERMES_COMMANDS_CAPABILITY, HERMES_SESSION_TITLES_CAPABILITY, HERMES_SESSIONS_LIST_CAPABILITY, HERMES_PROFILES_CAPABILITY, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
23
+ export { DEFAULT_PROFILE, isValidProfileName, listHermesProfiles, normalizeProfileName, resolveProfileContext, resolveProfileHome, } from "./profiles.js";
24
+ export type { HermesProfileContext, HermesProfileEntry } from "./profiles.js";
23
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACvF,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,+BAA+B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,0BAA0B,EAC1B,gCAAgC,EAChC,+BAA+B,EAC/B,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvF,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACvF,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,+BAA+B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,0BAA0B,EAC1B,gCAAgC,EAChC,+BAA+B,EAC/B,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -10,4 +10,5 @@ export { startCronWatcher, listPendingCronFiles, clearDeliveredIndex } from "./c
10
10
  export { backfillCronDeliveries, describeCronDeliveryState } from "./cronBackfill.js";
11
11
  export { resolveActiveConversationId, rememberConversationId } from "./activeConversation.js";
12
12
  export { runBridgeDaemon } from "./daemon.js";
13
- export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, DEFAULT_BRIDGE_CAPABILITIES, HERMES_COMMANDS_CAPABILITY, HERMES_SESSION_TITLES_CAPABILITY, HERMES_SESSIONS_LIST_CAPABILITY, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
13
+ export { DEFAULT_BRIDGE_INSTALL_URL, DEFAULT_BRIDGE_UPDATE_URL, DEFAULT_CLEOS_CONVEX_SITE_URL, DEFAULT_HERMES_API_URL, DEFAULT_BRIDGE_CAPABILITIES, HERMES_COMMANDS_CAPABILITY, HERMES_SESSION_TITLES_CAPABILITY, HERMES_SESSIONS_LIST_CAPABILITY, HERMES_PROFILES_CAPABILITY, bridgeInstallCommand, bridgeUpdateCommand, } from "./constants.js";
14
+ export { DEFAULT_PROFILE, isValidProfileName, listHermesProfiles, normalizeProfileName, resolveProfileContext, resolveProfileHome, } from "./profiles.js";
package/dist/mcpList.d.ts CHANGED
@@ -6,17 +6,18 @@ export type HermesMcpServerEntry = {
6
6
  enabled: boolean;
7
7
  };
8
8
  export declare function parseMcpListOutput(stdout: string): HermesMcpServerEntry[];
9
- export declare function listHermesMcpServers(): Promise<{
9
+ export declare function listHermesMcpServers(env?: NodeJS.ProcessEnv): Promise<{
10
10
  servers: HermesMcpServerEntry[];
11
11
  }>;
12
12
  export declare function addHermesMcpServer(input: {
13
13
  name?: string;
14
14
  json: string;
15
+ env?: NodeJS.ProcessEnv;
15
16
  }): Promise<{
16
17
  ok: true;
17
18
  name: string;
18
19
  }>;
19
- export declare function removeHermesMcpServer(name: string): Promise<{
20
+ export declare function removeHermesMcpServer(name: string, env?: NodeJS.ProcessEnv): Promise<{
20
21
  ok: true;
21
22
  name: string;
22
23
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"mcpList.d.ts","sourceRoot":"","sources":["../src/mcpList.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAqBF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAwEzE;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAAC,CAgBzF;AAuLD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtC;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAO7F"}
1
+ {"version":3,"file":"mcpList.d.ts","sourceRoot":"","sources":["../src/mcpList.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAqBF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAwEzE;AAED,wBAAsB,oBAAoB,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAAC,CAgB9C;AAuLD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtC;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAOrC"}
package/dist/mcpList.js CHANGED
@@ -85,12 +85,12 @@ export function parseMcpListOutput(stdout) {
85
85
  }
86
86
  return servers;
87
87
  }
88
- export async function listHermesMcpServers() {
88
+ export async function listHermesMcpServers(env = process.env) {
89
89
  try {
90
90
  const { stdout } = await execFileAsync("hermes", ["mcp", "list"], {
91
91
  timeout: LIST_TIMEOUT_MS,
92
92
  maxBuffer: MAX_OUTPUT_BYTES,
93
- env: process.env,
93
+ env,
94
94
  });
95
95
  return { servers: parseMcpListOutput(stdout) };
96
96
  }
@@ -261,15 +261,15 @@ export async function addHermesMcpServer(input) {
261
261
  await execFileAsync("hermes", argv, {
262
262
  timeout: ADD_TIMEOUT_MS,
263
263
  maxBuffer: MAX_OUTPUT_BYTES,
264
- env: process.env,
264
+ env: input.env ?? process.env,
265
265
  });
266
266
  return { ok: true, name: normalized.name };
267
267
  }
268
- export async function removeHermesMcpServer(name) {
268
+ export async function removeHermesMcpServer(name, env = process.env) {
269
269
  await execFileAsync("hermes", ["mcp", "remove", name], {
270
270
  timeout: REMOVE_TIMEOUT_MS,
271
271
  maxBuffer: MAX_OUTPUT_BYTES,
272
- env: process.env,
272
+ env,
273
273
  });
274
274
  return { ok: true, name };
275
275
  }
@@ -0,0 +1,66 @@
1
+ /** Hermes default profile is the base home (`~/.hermes`) itself. */
2
+ export declare const DEFAULT_PROFILE = "default";
3
+ export declare class HermesProfileError extends Error {
4
+ }
5
+ export declare function isValidProfileName(name: string): boolean;
6
+ export declare function normalizeProfileName(profile?: string | null): string;
7
+ export declare function isDefaultProfile(profile?: string | null): boolean;
8
+ /** Base Hermes home — the default profile's directory. */
9
+ export declare function resolveBaseHermesHome(): string;
10
+ export declare function profilesRootDir(): string;
11
+ /** Resolve the Hermes home directory for a given profile (default → base home). */
12
+ export declare function resolveProfileHome(profile?: string | null): string;
13
+ /** Build an env that scopes child `hermes` processes to the given profile. */
14
+ export declare function profileEnv(profile?: string | null): NodeJS.ProcessEnv;
15
+ /** Best-effort parse of `model.default` from a profile's config.yaml. */
16
+ export declare function readProfileModel(home: string): string | undefined;
17
+ export declare function resolveProfileGatewayPort(profile?: string | null): number | undefined;
18
+ export declare function resolveProfileGatewayUrl(profile?: string | null): string;
19
+ export type HermesProfileContext = {
20
+ profile: string;
21
+ home: string;
22
+ apiUrl: string;
23
+ apiKey: string | undefined;
24
+ env: NodeJS.ProcessEnv;
25
+ };
26
+ export declare function resolveProfileContext(profile?: string | null): HermesProfileContext;
27
+ export type HermesProfileEntry = {
28
+ name: string;
29
+ isDefault: boolean;
30
+ isActive: boolean;
31
+ model?: string;
32
+ gatewayRunning: boolean;
33
+ gatewayPort?: number;
34
+ skillsCount: number;
35
+ sessionsCount: number;
36
+ home: string;
37
+ };
38
+ /** Cheap, sync enumeration of profile homes (default + on-disk profiles). */
39
+ export declare function listProfileHomes(): {
40
+ profile: string;
41
+ home: string;
42
+ }[];
43
+ export declare function listHermesProfiles(): Promise<{
44
+ profiles: HermesProfileEntry[];
45
+ }>;
46
+ export declare function createHermesProfile(name: string, options?: {
47
+ clone?: boolean;
48
+ }): Promise<{
49
+ ok: true;
50
+ name: string;
51
+ }>;
52
+ export declare function renameHermesProfile(oldName: string, newName: string): Promise<{
53
+ ok: true;
54
+ oldName: string;
55
+ newName: string;
56
+ }>;
57
+ export declare function deleteHermesProfile(name: string): Promise<{
58
+ ok: true;
59
+ name: string;
60
+ }>;
61
+ export declare function setHermesProfileModel(profile: string | undefined, model: string): Promise<{
62
+ ok: true;
63
+ profile: string;
64
+ model: string;
65
+ }>;
66
+ //# sourceMappingURL=profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}