@haven_ai/connect 0.1.20-alpha.0 → 0.1.22-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/cli.cjs +220 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +221 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +220 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +221 -6
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.cts
CHANGED
|
@@ -126,7 +126,7 @@ declare function preflightCredentialStorage(input?: PreflightCredentialStorageIn
|
|
|
126
126
|
declare function writeCredentialFiles(input: WriteCredentialInput): Promise<StoredCredentialPaths>;
|
|
127
127
|
declare function defaultAgentDirectory(agentId: string, baseDir?: string): string;
|
|
128
128
|
|
|
129
|
-
type RuntimeId = 'claude-code' | 'codex-desktop' | 'codex-cli' | 'cursor' | 'vscode' | 'vscode-insiders' | 'claude-desktop' | 'other';
|
|
129
|
+
type RuntimeId = 'claude-code' | 'codex-desktop' | 'codex-cli' | 'cursor' | 'vscode' | 'vscode-insiders' | 'claude-desktop' | 'hermes' | 'other';
|
|
130
130
|
type RestartMode = 'restart-session' | 'restart-app' | 'hot-reload' | 'manual';
|
|
131
131
|
interface RuntimeProfile {
|
|
132
132
|
id: RuntimeId;
|
|
@@ -264,7 +264,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
|
|
|
264
264
|
restartRequired: boolean;
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
-
declare const CONNECTOR_VERSION = "0.1.
|
|
267
|
+
declare const CONNECTOR_VERSION = "0.1.22-alpha.0";
|
|
268
268
|
interface ConnectOptions {
|
|
269
269
|
setupToken: string;
|
|
270
270
|
apiBaseUrl: string;
|
|
@@ -307,11 +307,11 @@ declare function shortAddress(address: string): string;
|
|
|
307
307
|
|
|
308
308
|
declare const MCP_RUNTIME_MANIFEST: {
|
|
309
309
|
readonly mcpPackage: "@haven_ai/mcp";
|
|
310
|
-
readonly mcpVersion: "0.1.
|
|
310
|
+
readonly mcpVersion: "0.1.22-alpha.0";
|
|
311
311
|
readonly sdkPackage: "@haven_ai/sdk";
|
|
312
|
-
readonly sdkVersion: "0.1.
|
|
312
|
+
readonly sdkVersion: "0.1.22-alpha.0";
|
|
313
313
|
readonly signerPackage: "@haven_ai/signer";
|
|
314
|
-
readonly signerVersion: "0.1.
|
|
314
|
+
readonly signerVersion: "0.1.22-alpha.0";
|
|
315
315
|
readonly minimumNodeVersion: "24.0.0";
|
|
316
316
|
readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
|
|
317
317
|
readonly requiredTools: readonly string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -126,7 +126,7 @@ declare function preflightCredentialStorage(input?: PreflightCredentialStorageIn
|
|
|
126
126
|
declare function writeCredentialFiles(input: WriteCredentialInput): Promise<StoredCredentialPaths>;
|
|
127
127
|
declare function defaultAgentDirectory(agentId: string, baseDir?: string): string;
|
|
128
128
|
|
|
129
|
-
type RuntimeId = 'claude-code' | 'codex-desktop' | 'codex-cli' | 'cursor' | 'vscode' | 'vscode-insiders' | 'claude-desktop' | 'other';
|
|
129
|
+
type RuntimeId = 'claude-code' | 'codex-desktop' | 'codex-cli' | 'cursor' | 'vscode' | 'vscode-insiders' | 'claude-desktop' | 'hermes' | 'other';
|
|
130
130
|
type RestartMode = 'restart-session' | 'restart-app' | 'hot-reload' | 'manual';
|
|
131
131
|
interface RuntimeProfile {
|
|
132
132
|
id: RuntimeId;
|
|
@@ -264,7 +264,7 @@ declare function runtimeInstallCapabilities(runtime: string | undefined, env?: N
|
|
|
264
264
|
restartRequired: boolean;
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
-
declare const CONNECTOR_VERSION = "0.1.
|
|
267
|
+
declare const CONNECTOR_VERSION = "0.1.22-alpha.0";
|
|
268
268
|
interface ConnectOptions {
|
|
269
269
|
setupToken: string;
|
|
270
270
|
apiBaseUrl: string;
|
|
@@ -307,11 +307,11 @@ declare function shortAddress(address: string): string;
|
|
|
307
307
|
|
|
308
308
|
declare const MCP_RUNTIME_MANIFEST: {
|
|
309
309
|
readonly mcpPackage: "@haven_ai/mcp";
|
|
310
|
-
readonly mcpVersion: "0.1.
|
|
310
|
+
readonly mcpVersion: "0.1.22-alpha.0";
|
|
311
311
|
readonly sdkPackage: "@haven_ai/sdk";
|
|
312
|
-
readonly sdkVersion: "0.1.
|
|
312
|
+
readonly sdkVersion: "0.1.22-alpha.0";
|
|
313
313
|
readonly signerPackage: "@haven_ai/signer";
|
|
314
|
-
readonly signerVersion: "0.1.
|
|
314
|
+
readonly signerVersion: "0.1.22-alpha.0";
|
|
315
315
|
readonly minimumNodeVersion: "24.0.0";
|
|
316
316
|
readonly supportedClients: readonly ["codex-cli", "codex-desktop", "claude-code"];
|
|
317
317
|
readonly requiredTools: readonly string[];
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
import { Wallet } from 'ethers';
|
|
3
|
-
import { mkdir, rm, writeFile, access, chmod, readFile } from 'fs/promises';
|
|
3
|
+
import { mkdir, rm, writeFile, access, chmod, readFile, unlink } from 'fs/promises';
|
|
4
4
|
import { homedir, platform } from 'os';
|
|
5
5
|
import { join, resolve, dirname } from 'path';
|
|
6
6
|
import { execFile, spawn } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
+
import { parseDocument, isMap, stringify } from 'yaml';
|
|
8
9
|
import { registeredToolNames, MCP_VERSION, ensureConsent, computeConsentHash, loadCredentials, consentInputFromClient } from '@haven_ai/mcp';
|
|
9
10
|
import { HAVEN_MINIMUM_NODE_VERSION, SKILL_FOLDER_NAME, HAVEN_SKILL_MD, isSupportedNodeVersion, unsupportedNodeVersionMessage } from '@haven_ai/sdk';
|
|
10
11
|
import { ensureSignerConsent, computeSignerConsentHash, loadSignerCredentials, createEdgeSigner, toolSchemas } from '@haven_ai/signer';
|
|
@@ -229,9 +230,9 @@ var MCP_RUNTIME_MANIFEST = {
|
|
|
229
230
|
mcpPackage: "@haven_ai/mcp",
|
|
230
231
|
mcpVersion: MCP_VERSION,
|
|
231
232
|
sdkPackage: "@haven_ai/sdk",
|
|
232
|
-
sdkVersion: "0.1.
|
|
233
|
+
sdkVersion: "0.1.22-alpha.0",
|
|
233
234
|
signerPackage: "@haven_ai/signer",
|
|
234
|
-
signerVersion: "0.1.
|
|
235
|
+
signerVersion: "0.1.22-alpha.0",
|
|
235
236
|
// Sourced from the SDK, never a literal (#1161). This field read '20.0.0'
|
|
236
237
|
// while every package's `engines` said `>=24` and the docs said `>=24.0.0`,
|
|
237
238
|
// so the guard that was supposed to enforce the floor waved Node v23 through
|
|
@@ -253,13 +254,20 @@ function signerPackageSpec() {
|
|
|
253
254
|
}
|
|
254
255
|
|
|
255
256
|
// src/config-writers.ts
|
|
257
|
+
var HERMES_API_KEY_ENV = "MCP_HAVEN_API_KEY";
|
|
258
|
+
var HermesConfigRecoveryError = class extends Error {
|
|
259
|
+
constructor() {
|
|
260
|
+
super("Hermes configuration recovery did not complete");
|
|
261
|
+
this.name = "HermesConfigRecoveryError";
|
|
262
|
+
}
|
|
263
|
+
};
|
|
256
264
|
var InvalidCodexTomlError = class extends Error {
|
|
257
265
|
constructor(message) {
|
|
258
266
|
super(message);
|
|
259
267
|
this.name = "InvalidCodexTomlError";
|
|
260
268
|
}
|
|
261
269
|
};
|
|
262
|
-
async function writeRuntimeConfig(input) {
|
|
270
|
+
async function writeRuntimeConfig(input, deps = {}) {
|
|
263
271
|
switch (input.runtime) {
|
|
264
272
|
case "codex-cli":
|
|
265
273
|
case "codex-desktop":
|
|
@@ -272,6 +280,8 @@ async function writeRuntimeConfig(input) {
|
|
|
272
280
|
return writeJsonRuntimeConfig(input, vscodeInsidersConfigPath(input.homeDir), "servers");
|
|
273
281
|
case "claude-desktop":
|
|
274
282
|
return writeJsonRuntimeConfig(input, claudeDesktopConfigPath(input.homeDir), "mcpServers");
|
|
283
|
+
case "hermes":
|
|
284
|
+
return writeHermesConfig(input, deps);
|
|
275
285
|
default:
|
|
276
286
|
return {
|
|
277
287
|
hostedConfigured: false,
|
|
@@ -325,6 +335,115 @@ function mergeJsonMcpConfig(existingJson, serverRoot, hostedServer, signerServer
|
|
|
325
335
|
return `${JSON.stringify(config, null, 2)}
|
|
326
336
|
`;
|
|
327
337
|
}
|
|
338
|
+
function mergeHermesYaml(existingYaml, hostedServer, signerServer) {
|
|
339
|
+
if (!existingYaml?.trim()) return renderHermesYaml({ haven: hostedServer, "haven-signer": signerServer });
|
|
340
|
+
const doc = parseDocument(existingYaml, { keepSourceTokens: true });
|
|
341
|
+
if (doc.errors.length > 0 || !isMap(doc.contents)) {
|
|
342
|
+
throw new Error("Hermes config must be a YAML object");
|
|
343
|
+
}
|
|
344
|
+
const mcpPair = doc.contents.items.find((item) => item.key?.toString() === "mcp_servers");
|
|
345
|
+
const existingServers = mcpPair && isMap(mcpPair.value) ? mcpPair.value.toJSON() : {};
|
|
346
|
+
const servers = isRecord(existingServers) ? existingServers : {};
|
|
347
|
+
const mergedServers = {
|
|
348
|
+
...servers,
|
|
349
|
+
haven: hostedServer,
|
|
350
|
+
"haven-signer": signerServer
|
|
351
|
+
};
|
|
352
|
+
if (!mcpPair) {
|
|
353
|
+
return appendHermesMcpServers(existingYaml, mergedServers);
|
|
354
|
+
}
|
|
355
|
+
if (!mcpPair.value?.range) return replaceEmptyHermesMcpServers(existingYaml, mcpPair.key?.range, mergedServers);
|
|
356
|
+
return replaceHermesMcpServers(existingYaml, mcpPair.key?.range, mcpPair.value.range, mergedServers);
|
|
357
|
+
}
|
|
358
|
+
function mergeHermesEnv(existingEnv, apiKey) {
|
|
359
|
+
if (/[\r\n]/.test(apiKey)) throw new Error("Hermes API key must be a single line");
|
|
360
|
+
const assignment = `${HERMES_API_KEY_ENV}=${apiKey}`;
|
|
361
|
+
if (!existingEnv) return `${assignment}
|
|
362
|
+
`;
|
|
363
|
+
const lineEnding = existingEnv.includes("\r\n") ? "\r\n" : "\n";
|
|
364
|
+
const hasTrailingNewline = /\r?\n$/.test(existingEnv);
|
|
365
|
+
const lines = existingEnv.split(/\r?\n/);
|
|
366
|
+
if (hasTrailingNewline) lines.pop();
|
|
367
|
+
let found = false;
|
|
368
|
+
const merged = lines.flatMap((line) => {
|
|
369
|
+
if (isHermesEnvAssignment(line)) {
|
|
370
|
+
if (found) return [];
|
|
371
|
+
found = true;
|
|
372
|
+
return [assignment];
|
|
373
|
+
}
|
|
374
|
+
if (isAmbiguousHermesEnvLine(line)) {
|
|
375
|
+
throw new Error("Hermes environment contains an ambiguous managed key");
|
|
376
|
+
}
|
|
377
|
+
return [line];
|
|
378
|
+
});
|
|
379
|
+
if (!found) {
|
|
380
|
+
return `${existingEnv}${hasTrailingNewline ? "" : lineEnding}${assignment}${lineEnding}`;
|
|
381
|
+
}
|
|
382
|
+
return `${merged.join(lineEnding)}${hasTrailingNewline ? lineEnding : ""}`;
|
|
383
|
+
}
|
|
384
|
+
function isHermesEnvAssignment(line) {
|
|
385
|
+
return /^\s*(?:export[ \t]+)?MCP_HAVEN_API_KEY[ \t]*=/.test(line);
|
|
386
|
+
}
|
|
387
|
+
function isAmbiguousHermesEnvLine(line) {
|
|
388
|
+
return /^\s*(?:export[ \t]+)?MCP_HAVEN_API_KEY\b/.test(line);
|
|
389
|
+
}
|
|
390
|
+
function appendHermesMcpServers(source, servers) {
|
|
391
|
+
const documentEnd = /(?:^|\n)[ \t]*\.\.\.[ \t]*(?:#[^\n]*)?\r?\n?$/.exec(source);
|
|
392
|
+
if (documentEnd) {
|
|
393
|
+
const markerStart = documentEnd.index + (documentEnd[0].startsWith("\n") ? 1 : 0);
|
|
394
|
+
const beforeMarker = source.slice(0, markerStart);
|
|
395
|
+
return `${beforeMarker}${beforeMarker.endsWith("\n") ? "" : "\n"}${renderHermesMcpServers(servers, "")}
|
|
396
|
+
${source.slice(markerStart)}`;
|
|
397
|
+
}
|
|
398
|
+
const separator = source.endsWith("\n") ? "" : "\n";
|
|
399
|
+
return `${source}${separator}${renderHermesMcpServers(servers, "")}
|
|
400
|
+
`;
|
|
401
|
+
}
|
|
402
|
+
function replaceHermesMcpServers(source, keyRange, valueRange, servers) {
|
|
403
|
+
const valueStart = valueRange[0];
|
|
404
|
+
const valueEnd = valueRange[1];
|
|
405
|
+
const valueLineStart = source.lastIndexOf("\n", valueStart - 1) + 1;
|
|
406
|
+
const keyStartsOnValueLine = keyRange?.[0] !== void 0 && keyRange[0] >= valueLineStart;
|
|
407
|
+
const nestedIndent = `${source.slice(valueLineStart, keyRange?.[0] ?? valueLineStart)} `;
|
|
408
|
+
if (keyStartsOnValueLine) {
|
|
409
|
+
const keyStart = keyRange[0];
|
|
410
|
+
const keyEnd = keyRange[1];
|
|
411
|
+
const replacement2 = `${source.slice(keyStart, keyEnd)}:
|
|
412
|
+
${renderHermesMcpServerEntries(servers, nestedIndent)}`;
|
|
413
|
+
const needsTrailingNewline2 = valueEnd < source.length && !source.slice(valueEnd).startsWith("\n");
|
|
414
|
+
return `${source.slice(0, keyStart)}${replacement2}${needsTrailingNewline2 ? "\n" : ""}${source.slice(valueEnd)}`;
|
|
415
|
+
}
|
|
416
|
+
const indent = source.slice(valueLineStart, valueStart);
|
|
417
|
+
const rendered = stringify(servers, { lineWidth: 120 }).trimEnd();
|
|
418
|
+
const replacement = rendered.split("\n").map((line, index) => index === 0 ? line : `${indent}${line}`).join("\n");
|
|
419
|
+
const needsTrailingNewline = valueEnd < source.length && !source.slice(valueEnd).startsWith("\n");
|
|
420
|
+
return `${source.slice(0, valueStart)}${replacement}${needsTrailingNewline ? "\n" : ""}${source.slice(valueEnd)}`;
|
|
421
|
+
}
|
|
422
|
+
function replaceEmptyHermesMcpServers(source, keyRange, servers) {
|
|
423
|
+
if (!keyRange) throw new Error("Hermes config mcp_servers key is invalid");
|
|
424
|
+
const keyStart = keyRange[0];
|
|
425
|
+
const keyEnd = keyRange[1];
|
|
426
|
+
const lineStart = source.lastIndexOf("\n", keyStart - 1) + 1;
|
|
427
|
+
const lineEnd = source.indexOf("\n", keyEnd);
|
|
428
|
+
const nestedIndent = `${source.slice(lineStart, keyStart)} `;
|
|
429
|
+
const replacement = `${source.slice(keyStart, keyEnd)}:
|
|
430
|
+
${renderHermesMcpServerEntries(servers, nestedIndent)}`;
|
|
431
|
+
return `${source.slice(0, keyStart)}${replacement}${lineEnd === -1 ? "\n" : source.slice(lineEnd)}`;
|
|
432
|
+
}
|
|
433
|
+
function renderHermesYaml(servers) {
|
|
434
|
+
return `${renderHermesMcpServers(servers, "")}
|
|
435
|
+
`;
|
|
436
|
+
}
|
|
437
|
+
function renderHermesMcpServers(servers, indent) {
|
|
438
|
+
return `mcp_servers:
|
|
439
|
+
${renderHermesMcpServerEntries(servers, `${indent} `)}`;
|
|
440
|
+
}
|
|
441
|
+
function renderHermesMcpServerEntries(servers, indent) {
|
|
442
|
+
return stringify(servers, { lineWidth: 120 }).trimEnd().split("\n").map((line) => `${indent}${line}`).join("\n");
|
|
443
|
+
}
|
|
444
|
+
function isRecord(value) {
|
|
445
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
446
|
+
}
|
|
328
447
|
function mergeCodexToml(existingToml, localMcpCommand) {
|
|
329
448
|
let next = removeTomlTableTree(removeTomlTableTree(existingToml, "mcp_servers.haven"), "mcp_servers.haven_signer");
|
|
330
449
|
next = next.trimEnd();
|
|
@@ -395,6 +514,82 @@ async function writeJsonRuntimeConfig(input, target, serverRoot) {
|
|
|
395
514
|
};
|
|
396
515
|
}
|
|
397
516
|
}
|
|
517
|
+
async function writeHermesConfig(input, deps) {
|
|
518
|
+
const target = hermesConfigPath(input.homeDir);
|
|
519
|
+
const envTarget = hermesEnvPath(input.homeDir);
|
|
520
|
+
try {
|
|
521
|
+
const [existing, existingEnv] = await Promise.all([readOptional(target), readOptional(envTarget)]);
|
|
522
|
+
const hostedServer = {
|
|
523
|
+
...buildHostedServer(input.hostedMcpUrl, `\${${HERMES_API_KEY_ENV}}`, input.runtime),
|
|
524
|
+
enabled: true
|
|
525
|
+
};
|
|
526
|
+
const signerServer = {
|
|
527
|
+
...buildSignerServer(resolveSignerLaunchSpec(input), input.runtime),
|
|
528
|
+
enabled: true
|
|
529
|
+
};
|
|
530
|
+
const merged = mergeHermesYaml(
|
|
531
|
+
existing,
|
|
532
|
+
hostedServer,
|
|
533
|
+
signerServer
|
|
534
|
+
);
|
|
535
|
+
const mergedEnv = mergeHermesEnv(existingEnv, input.apiKey);
|
|
536
|
+
const writeText = deps.writeOwnerOnlyText ?? writeOwnerOnlyText;
|
|
537
|
+
await writeText(envTarget, mergedEnv);
|
|
538
|
+
try {
|
|
539
|
+
await writeText(target, merged);
|
|
540
|
+
} catch (err) {
|
|
541
|
+
const recovered = await restoreHermesFiles(target, existing, envTarget, existingEnv, writeText);
|
|
542
|
+
if (!recovered) throw new HermesConfigRecoveryError();
|
|
543
|
+
throw err;
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
hostedConfigured: true,
|
|
547
|
+
signerConfigured: true,
|
|
548
|
+
localMcpConfigured: false,
|
|
549
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
550
|
+
target: "Hermes Agent config",
|
|
551
|
+
changed: existing !== merged || existingEnv !== mergedEnv,
|
|
552
|
+
restartRequired: true,
|
|
553
|
+
messages: [
|
|
554
|
+
`Updated Haven MCP entries in ${target}; stored the hosted MCP identity in ${envTarget}.`,
|
|
555
|
+
"Restart Hermes (start a new session; gateway users: /restart), then verify with `hermes mcp list`, `hermes mcp test haven`, and `hermes mcp test haven-signer`.",
|
|
556
|
+
"If no mcp_* tools appear after restart, ensure the MCP SDK is installed in Hermes: pip install mcp"
|
|
557
|
+
]
|
|
558
|
+
};
|
|
559
|
+
} catch (err) {
|
|
560
|
+
const recoveryIncomplete = err instanceof HermesConfigRecoveryError;
|
|
561
|
+
return {
|
|
562
|
+
hostedConfigured: false,
|
|
563
|
+
signerConfigured: false,
|
|
564
|
+
localMcpConfigured: false,
|
|
565
|
+
runtimeMcpMode: "hosted_plus_signer",
|
|
566
|
+
target: "Hermes Agent config",
|
|
567
|
+
changed: false,
|
|
568
|
+
restartRequired: true,
|
|
569
|
+
messages: [recoveryIncomplete ? "Could not update Hermes Agent config. Recovery did not complete; inspect the Hermes configuration before retrying." : "Could not update Hermes Agent config. Existing configuration was left unchanged."],
|
|
570
|
+
errorCode: "runtime_config_write_failed"
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
async function restoreHermesFiles(configPath, existingConfig, envPath, existingEnv, writeText) {
|
|
575
|
+
const [envResult, configResult] = await Promise.allSettled([
|
|
576
|
+
restoreOptionalText(envPath, existingEnv, writeText),
|
|
577
|
+
restoreOptionalText(configPath, existingConfig, writeText)
|
|
578
|
+
]);
|
|
579
|
+
return envResult.status === "fulfilled" && configResult.status === "fulfilled";
|
|
580
|
+
}
|
|
581
|
+
async function restoreOptionalText(path, existing, writeText) {
|
|
582
|
+
if (existing !== null) {
|
|
583
|
+
await writeText(path, existing);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
await unlink(path);
|
|
588
|
+
} catch (err) {
|
|
589
|
+
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return;
|
|
590
|
+
throw err;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
398
593
|
async function writeCodexConfig(input) {
|
|
399
594
|
const target = codexConfigPath(input.homeDir);
|
|
400
595
|
const local = input.mode === "local";
|
|
@@ -685,6 +880,15 @@ function claudeDesktopConfigPath(homeDir = homedir()) {
|
|
|
685
880
|
}
|
|
686
881
|
return resolve(homeDir, ".config", "Claude", "claude_desktop_config.json");
|
|
687
882
|
}
|
|
883
|
+
function hermesConfigPath(homeDir) {
|
|
884
|
+
return join(hermesHomePath(homeDir), "config.yaml");
|
|
885
|
+
}
|
|
886
|
+
function hermesEnvPath(homeDir) {
|
|
887
|
+
return join(hermesHomePath(homeDir), ".env");
|
|
888
|
+
}
|
|
889
|
+
function hermesHomePath(homeDir) {
|
|
890
|
+
return process.env.HERMES_HOME ?? join(homeDir ?? homedir(), ".hermes");
|
|
891
|
+
}
|
|
688
892
|
function configTargetLabel(runtime) {
|
|
689
893
|
switch (runtime) {
|
|
690
894
|
case "codex-cli":
|
|
@@ -1228,6 +1432,12 @@ var RUNTIME_PROFILES = {
|
|
|
1228
1432
|
restartMode: "restart-app",
|
|
1229
1433
|
canWriteRuntimeConfig: true
|
|
1230
1434
|
},
|
|
1435
|
+
hermes: {
|
|
1436
|
+
id: "hermes",
|
|
1437
|
+
label: "Hermes Agent",
|
|
1438
|
+
restartMode: "restart-session",
|
|
1439
|
+
canWriteRuntimeConfig: true
|
|
1440
|
+
},
|
|
1231
1441
|
other: {
|
|
1232
1442
|
id: "other",
|
|
1233
1443
|
label: "Other agent runtime",
|
|
@@ -1265,6 +1475,10 @@ var RUNTIME_ALIASES = {
|
|
|
1265
1475
|
"claude_desktop": "claude-desktop",
|
|
1266
1476
|
claudesktop: "claude-desktop",
|
|
1267
1477
|
desktop: "claude-desktop",
|
|
1478
|
+
hermes: "hermes",
|
|
1479
|
+
"hermes-agent": "hermes",
|
|
1480
|
+
hermes_agent: "hermes",
|
|
1481
|
+
hermesagent: "hermes",
|
|
1268
1482
|
other: "other",
|
|
1269
1483
|
manual: "other"
|
|
1270
1484
|
};
|
|
@@ -1292,6 +1506,7 @@ function detectRuntime(env) {
|
|
|
1292
1506
|
if (env.CLAUDECODE || env.CLAUDE_CODE || env.CLAUDECODE_CWD) return "claude-code";
|
|
1293
1507
|
if (env.CODEX_SANDBOX || env.CODEX_HOME || env.CODEX_CWD) return "codex-cli";
|
|
1294
1508
|
if (env.VSCODE_CWD || env.VSCODE_IPC_HOOK_CLI || env.TERM_PROGRAM === "vscode") return "vscode";
|
|
1509
|
+
if (env.HERMES_HOME || env.HERMES_AGENT) return "hermes";
|
|
1295
1510
|
return null;
|
|
1296
1511
|
}
|
|
1297
1512
|
async function acknowledgeLocalSignerConsent(signerPath, log) {
|
|
@@ -1701,7 +1916,7 @@ function localRuntimePrepareErrorCode(err) {
|
|
|
1701
1916
|
}
|
|
1702
1917
|
|
|
1703
1918
|
// src/runtime.ts
|
|
1704
|
-
var CONNECTOR_VERSION = "0.1.
|
|
1919
|
+
var CONNECTOR_VERSION = "0.1.22-alpha.0";
|
|
1705
1920
|
async function runConnect(options, deps = {}) {
|
|
1706
1921
|
assertSupportedNodeVersion(deps.nodeVersion, MCP_RUNTIME_MANIFEST.minimumNodeVersion);
|
|
1707
1922
|
const connectorVersion = options.connectorVersion ?? CONNECTOR_VERSION;
|
|
@@ -1934,7 +2149,7 @@ function helpText() {
|
|
|
1934
2149
|
"Options:",
|
|
1935
2150
|
" --setup <token> Short-lived setup token from Haven.",
|
|
1936
2151
|
" --api <url> Haven backend API URL. Defaults to HAVEN_API_URL or http://localhost:3001.",
|
|
1937
|
-
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, codex-desktop, cursor, vscode,
|
|
2152
|
+
" --runtime <name> Agent runtime hint, such as claude-code, codex-cli, codex-desktop, cursor, vscode, claude-desktop, or hermes.",
|
|
1938
2153
|
" --credentials-dir <path> Credential directory fallback. Defaults to ~/.haven/agents.",
|
|
1939
2154
|
" --environment-label <text> Non-sensitive label shown in Haven setup review.",
|
|
1940
2155
|
" --ack-local-tools Write the one-time local Haven tools acknowledgement during setup.",
|