@fideliosai/adapter-hermes-local 0.0.38 → 0.0.40
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/package.json +2 -2
- package/src/server/execute.js +20 -1
- package/src/server/index.d.ts +1 -1
- package/src/server/index.js +1 -1
- package/src/server/test.d.ts +1 -0
- package/src/server/test.js +120 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fideliosai/adapter-hermes-local",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Hermes AI agent adapter for FideliOS",
|
|
6
6
|
"homepage": "https://github.com/fideliosai/fidelios",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@fideliosai/adapter-utils": "0.0.
|
|
26
|
+
"@fideliosai/adapter-utils": "0.0.40",
|
|
27
27
|
"picocolors": "^1.1.0"
|
|
28
28
|
}
|
|
29
29
|
}
|
package/src/server/execute.js
CHANGED
|
@@ -298,13 +298,32 @@ export async function execute(ctx) {
|
|
|
298
298
|
};
|
|
299
299
|
if (ctx.runId)
|
|
300
300
|
env.FIDELIOS_RUN_ID = ctx.runId;
|
|
301
|
-
const taskId = cfgString(ctx.config?.taskId);
|
|
301
|
+
const taskId = cfgString(ctx.context?.taskId) || cfgString(ctx.context?.issueId) || cfgString(ctx.config?.taskId);
|
|
302
302
|
if (taskId)
|
|
303
303
|
env.FIDELIOS_TASK_ID = taskId;
|
|
304
|
+
const wakeReason = cfgString(ctx.context?.wakeReason);
|
|
305
|
+
if (wakeReason)
|
|
306
|
+
env.FIDELIOS_WAKE_REASON = wakeReason;
|
|
307
|
+
const wakeCommentId = cfgString(ctx.context?.wakeCommentId) || cfgString(ctx.context?.commentId);
|
|
308
|
+
if (wakeCommentId)
|
|
309
|
+
env.FIDELIOS_WAKE_COMMENT_ID = wakeCommentId;
|
|
310
|
+
const approvalId = cfgString(ctx.context?.approvalId);
|
|
311
|
+
if (approvalId)
|
|
312
|
+
env.FIDELIOS_APPROVAL_ID = approvalId;
|
|
313
|
+
const approvalStatus = cfgString(ctx.context?.approvalStatus);
|
|
314
|
+
if (approvalStatus)
|
|
315
|
+
env.FIDELIOS_APPROVAL_STATUS = approvalStatus;
|
|
316
|
+
const linkedIssueIds = cfgString(ctx.context?.linkedIssueIds);
|
|
317
|
+
if (linkedIssueIds)
|
|
318
|
+
env.FIDELIOS_LINKED_ISSUE_IDS = linkedIssueIds;
|
|
304
319
|
const userEnv = config.env;
|
|
320
|
+
const hasExplicitApiKey = typeof userEnv?.FIDELIOS_API_KEY === "string" && userEnv.FIDELIOS_API_KEY.trim().length > 0;
|
|
305
321
|
if (userEnv && typeof userEnv === "object") {
|
|
306
322
|
Object.assign(env, userEnv);
|
|
307
323
|
}
|
|
324
|
+
if (!hasExplicitApiKey && ctx.authToken) {
|
|
325
|
+
env.FIDELIOS_API_KEY = ctx.authToken;
|
|
326
|
+
}
|
|
308
327
|
// ── Resolve working directory ──────────────────────────────────────────
|
|
309
328
|
const cwd = cfgString(config.cwd) || cfgString(ctx.config?.workspaceDir) || ".";
|
|
310
329
|
try {
|
package/src/server/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Server-side adapter module exports.
|
|
3
3
|
*/
|
|
4
4
|
export { execute } from "./execute.js";
|
|
5
|
-
export { testEnvironment } from "./test.js";
|
|
5
|
+
export { testEnvironment, isHermesAuthRequiredText } from "./test.js";
|
|
6
6
|
export { detectModel } from "./detect-model.js";
|
|
7
7
|
export { listHermesSkills as listSkills, syncHermesSkills as syncSkills, resolveHermesDesiredSkillNames as resolveDesiredSkillNames, } from "./skills.js";
|
|
8
8
|
import type { AdapterSessionCodec } from "@fideliosai/adapter-utils";
|
package/src/server/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Server-side adapter module exports.
|
|
3
3
|
*/
|
|
4
4
|
export { execute } from "./execute.js";
|
|
5
|
-
export { testEnvironment } from "./test.js";
|
|
5
|
+
export { testEnvironment, isHermesAuthRequiredText } from "./test.js";
|
|
6
6
|
export { detectModel } from "./detect-model.js";
|
|
7
7
|
export { listHermesSkills as listSkills, syncHermesSkills as syncSkills, resolveHermesDesiredSkillNames as resolveDesiredSkillNames, } from "./skills.js";
|
|
8
8
|
function readNonEmptyString(value) {
|
package/src/server/test.d.ts
CHANGED
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AdapterEnvironmentTestContext, AdapterEnvironmentTestResult } from "@fideliosai/adapter-utils";
|
|
8
8
|
export declare function testEnvironment(ctx: AdapterEnvironmentTestContext): Promise<AdapterEnvironmentTestResult>;
|
|
9
|
+
export declare function isHermesAuthRequiredText(text: unknown): boolean;
|
|
9
10
|
//# sourceMappingURL=test.d.ts.map
|
package/src/server/test.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Verifies that Hermes Agent is installed, accessible, and configured
|
|
5
5
|
* before allowing the adapter to be used.
|
|
6
6
|
*/
|
|
7
|
+
import path from "node:path";
|
|
7
8
|
import { execFile } from "node:child_process";
|
|
8
9
|
import { promisify } from "node:util";
|
|
9
10
|
import { HERMES_CLI, ADAPTER_TYPE } from "../shared/constants.js";
|
|
@@ -11,6 +12,28 @@ const execFileAsync = promisify(execFile);
|
|
|
11
12
|
function asString(v) {
|
|
12
13
|
return typeof v === "string" ? v : undefined;
|
|
13
14
|
}
|
|
15
|
+
// Mirrors the patterns used by codex/opencode/pi adapters; matches the most
|
|
16
|
+
// common auth-failure substrings emitted by upstream LLM providers when a key
|
|
17
|
+
// is missing/invalid or Hermes itself isn't logged in.
|
|
18
|
+
const HERMES_AUTH_REQUIRED_RE = /(?:auth(?:entication)?\s+required|invalid(?:\s+or\s+missing)?\s+api(?:[_\s-]?key)?|api[_\s-]?key.*required|not\s+logged\s+in|please\s+log\s+in|unauthorized|401\s+unauthorized|insufficient_quota|free\s+usage\s+exceeded|please\s+run\s+`?hermes\s+(?:login|auth)`?)/i;
|
|
19
|
+
export function isHermesAuthRequiredText(text) {
|
|
20
|
+
return typeof text === "string" && HERMES_AUTH_REQUIRED_RE.test(text);
|
|
21
|
+
}
|
|
22
|
+
function commandLooksLikeHermes(command) {
|
|
23
|
+
const base = path.basename(command).toLowerCase();
|
|
24
|
+
return base === "hermes" || base === "hermes.cmd" || base === "hermes.exe";
|
|
25
|
+
}
|
|
26
|
+
function summarizeProbeDetail(stdout, stderr) {
|
|
27
|
+
const firstLine = (text) => text
|
|
28
|
+
.split(/\r?\n/)
|
|
29
|
+
.map((l) => l.trim())
|
|
30
|
+
.find(Boolean) ?? "";
|
|
31
|
+
const raw = firstLine(stderr) || firstLine(stdout);
|
|
32
|
+
if (!raw)
|
|
33
|
+
return null;
|
|
34
|
+
const clean = raw.replace(/\s+/g, " ").trim();
|
|
35
|
+
return clean.length > 240 ? `${clean.slice(0, 239)}…` : clean;
|
|
36
|
+
}
|
|
14
37
|
// ---------------------------------------------------------------------------
|
|
15
38
|
// Checks
|
|
16
39
|
// ---------------------------------------------------------------------------
|
|
@@ -147,6 +170,96 @@ function checkApiKeys(config) {
|
|
|
147
170
|
code: "hermes_api_keys_found",
|
|
148
171
|
};
|
|
149
172
|
}
|
|
173
|
+
async function checkHelloProbe(command, config, hasAnyApiKey) {
|
|
174
|
+
if (!commandLooksLikeHermes(command)) {
|
|
175
|
+
return {
|
|
176
|
+
level: "info",
|
|
177
|
+
message: "Skipped hello probe because command is not `hermes`.",
|
|
178
|
+
hint: "Use the `hermes` CLI command to enable the automatic auth probe.",
|
|
179
|
+
code: "hermes_hello_probe_skipped_custom_command",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const model = asString(config.model);
|
|
183
|
+
if (!model && !hasAnyApiKey) {
|
|
184
|
+
return {
|
|
185
|
+
level: "info",
|
|
186
|
+
message: "Skipped hello probe — no model and no provider API key configured.",
|
|
187
|
+
hint: "Set a model and at least one provider key (e.g. ANTHROPIC_API_KEY, OPENROUTER_API_KEY) to enable the probe.",
|
|
188
|
+
code: "hermes_hello_probe_skipped_no_config",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const envConfig = (config.env ?? {});
|
|
192
|
+
const env = { ...process.env };
|
|
193
|
+
for (const [k, v] of Object.entries(envConfig)) {
|
|
194
|
+
if (typeof v === "string")
|
|
195
|
+
env[k] = v;
|
|
196
|
+
}
|
|
197
|
+
const args = ["chat", "-q", "Respond with hello.", "-Q", "--yolo", "--source", "tool"];
|
|
198
|
+
if (model)
|
|
199
|
+
args.push("-m", model);
|
|
200
|
+
const provider = asString(config.provider);
|
|
201
|
+
if (provider)
|
|
202
|
+
args.push("--provider", provider);
|
|
203
|
+
try {
|
|
204
|
+
const { stdout, stderr } = await execFileAsync(command, args, {
|
|
205
|
+
timeout: 30_000,
|
|
206
|
+
env,
|
|
207
|
+
maxBuffer: 1024 * 1024,
|
|
208
|
+
});
|
|
209
|
+
const combined = `${stdout}\n${stderr}`;
|
|
210
|
+
if (HERMES_AUTH_REQUIRED_RE.test(combined)) {
|
|
211
|
+
return {
|
|
212
|
+
level: "warn",
|
|
213
|
+
message: "Hermes hello probe reported an auth/credential failure.",
|
|
214
|
+
detail: summarizeProbeDetail(stdout, stderr) ?? undefined,
|
|
215
|
+
hint: "Run `hermes auth status` and verify upstream provider credentials (ANTHROPIC_API_KEY, OPENROUTER_API_KEY, etc.).",
|
|
216
|
+
code: "hermes_hello_probe_auth_required",
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const replied = /\bhello\b/i.test(stdout);
|
|
220
|
+
return {
|
|
221
|
+
level: replied ? "info" : "warn",
|
|
222
|
+
message: replied
|
|
223
|
+
? "Hermes hello probe succeeded."
|
|
224
|
+
: "Hermes probe ran but did not return `hello` as expected.",
|
|
225
|
+
detail: summarizeProbeDetail(stdout, stderr) ?? undefined,
|
|
226
|
+
...(replied
|
|
227
|
+
? {}
|
|
228
|
+
: {
|
|
229
|
+
hint: "Try `hermes chat -q 'Respond with hello.' -Q` manually to debug provider/model behavior.",
|
|
230
|
+
}),
|
|
231
|
+
code: replied ? "hermes_hello_probe_passed" : "hermes_hello_probe_unexpected_output",
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
const e = err;
|
|
236
|
+
const text = `${e.stdout ?? ""}\n${e.stderr ?? ""}\n${e.message ?? ""}`;
|
|
237
|
+
if (e.killed || /etimedout|timeout/i.test(e.message ?? "")) {
|
|
238
|
+
return {
|
|
239
|
+
level: "warn",
|
|
240
|
+
message: "Hermes hello probe timed out.",
|
|
241
|
+
hint: "Retry the probe. If this persists, run `hermes chat -q 'ping' -Q` manually to verify the upstream provider responds.",
|
|
242
|
+
code: "hermes_hello_probe_timed_out",
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (HERMES_AUTH_REQUIRED_RE.test(text)) {
|
|
246
|
+
return {
|
|
247
|
+
level: "warn",
|
|
248
|
+
message: "Hermes hello probe reported an auth/credential failure.",
|
|
249
|
+
detail: summarizeProbeDetail(e.stdout ?? "", e.stderr ?? "") ?? undefined,
|
|
250
|
+
hint: "Run `hermes auth status` and verify upstream provider credentials.",
|
|
251
|
+
code: "hermes_hello_probe_auth_required",
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
level: "error",
|
|
256
|
+
message: "Hermes hello probe failed.",
|
|
257
|
+
detail: summarizeProbeDetail(e.stdout ?? "", e.stderr ?? "") ?? e.message,
|
|
258
|
+
hint: "Run `hermes chat -q 'Respond with hello.' -Q` manually in this directory to debug.",
|
|
259
|
+
code: "hermes_hello_probe_failed",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
150
263
|
// ---------------------------------------------------------------------------
|
|
151
264
|
// Main test
|
|
152
265
|
// ---------------------------------------------------------------------------
|
|
@@ -183,6 +296,13 @@ export async function testEnvironment(ctx) {
|
|
|
183
296
|
const apiKeyCheck = checkApiKeys(config);
|
|
184
297
|
if (apiKeyCheck)
|
|
185
298
|
checks.push(apiKeyCheck);
|
|
299
|
+
// 6. Hello probe — actually exercise the configured model/provider.
|
|
300
|
+
// Skipped when command is custom or when there's no model AND no provider key
|
|
301
|
+
// (so we never spam an LLM with no way to authenticate).
|
|
302
|
+
const hasAnyApiKey = apiKeyCheck.code === "hermes_api_keys_found";
|
|
303
|
+
const probeCheck = await checkHelloProbe(command, config, hasAnyApiKey);
|
|
304
|
+
if (probeCheck)
|
|
305
|
+
checks.push(probeCheck);
|
|
186
306
|
// Determine overall status
|
|
187
307
|
const hasErrors = checks.some((c) => c.level === "error");
|
|
188
308
|
const hasWarnings = checks.some((c) => c.level === "warn");
|