@securecoms/agent-runtime 0.5.0 → 0.6.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/dist/bin.js +63 -23
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -440,10 +440,16 @@ var LLMDispatcher = class {
|
|
|
440
440
|
endpoint;
|
|
441
441
|
model;
|
|
442
442
|
apiKey;
|
|
443
|
+
chatPath;
|
|
444
|
+
chatAuth;
|
|
445
|
+
authHeaderName;
|
|
443
446
|
constructor(endpoint, model, opts = {}) {
|
|
444
447
|
this.endpoint = normalizeEndpoint(endpoint);
|
|
445
448
|
this.model = model;
|
|
446
449
|
this.apiKey = opts.apiKey;
|
|
450
|
+
this.chatPath = opts.chatPath ?? "/v1/chat/completions";
|
|
451
|
+
this.chatAuth = opts.chatAuth ?? "bearer";
|
|
452
|
+
this.authHeaderName = opts.authHeaderName ?? "x-api-key";
|
|
447
453
|
}
|
|
448
454
|
async complete(messages, tools) {
|
|
449
455
|
const body = {
|
|
@@ -454,10 +460,12 @@ var LLMDispatcher = class {
|
|
|
454
460
|
if (tools && tools.length > 0) {
|
|
455
461
|
body.tools = tools;
|
|
456
462
|
}
|
|
457
|
-
const url = `${this.endpoint}
|
|
463
|
+
const url = `${this.endpoint}${this.chatPath}`;
|
|
458
464
|
const headers = { "Content-Type": "application/json" };
|
|
459
|
-
if (this.apiKey) {
|
|
465
|
+
if (this.apiKey && this.chatAuth === "bearer") {
|
|
460
466
|
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
467
|
+
} else if (this.apiKey && this.chatAuth === "x-api-key") {
|
|
468
|
+
headers[this.authHeaderName] = this.apiKey;
|
|
461
469
|
}
|
|
462
470
|
const resp = await fetch(url, {
|
|
463
471
|
method: "POST",
|
|
@@ -486,10 +494,13 @@ import { join as join2 } from "node:path";
|
|
|
486
494
|
|
|
487
495
|
// src/llm-providers.json
|
|
488
496
|
var llm_providers_default = {
|
|
489
|
-
openrouter: { chat_base: "https://openrouter.ai/api/v1", models_url: "https://openrouter.ai/api/v1/models", models_id_path: "data[].id", validate_url: "https://openrouter.ai/api/v1/key", needs_key: true },
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
497
|
+
openrouter: { chat_base: "https://openrouter.ai/api/v1", chat_path: "/v1/chat/completions", chat_auth: "bearer", models_url: "https://openrouter.ai/api/v1/models", models_id_path: "data[].id", validate_url: "https://openrouter.ai/api/v1/key", probe_auth: "bearer", extra_headers: null, catalog_prefix: "self", needs_key: true },
|
|
498
|
+
openai: { chat_base: "https://api.openai.com/v1", chat_path: "/v1/chat/completions", chat_auth: "bearer", models_url: "https://api.openai.com/v1/models", models_id_path: "data[].id", validate_url: "https://api.openai.com/v1/models", probe_auth: "bearer", extra_headers: null, catalog_prefix: "openai/", needs_key: true },
|
|
499
|
+
anthropic: { chat_base: "https://api.anthropic.com/v1", chat_path: "/v1/chat/completions", chat_auth: "bearer", models_url: "https://api.anthropic.com/v1/models", models_id_path: "data[].id", validate_url: "https://api.anthropic.com/v1/models", probe_auth: "x-api-key", extra_headers: { "anthropic-version": "2023-06-01" }, catalog_prefix: "anthropic/", needs_key: true },
|
|
500
|
+
gemini: { chat_base: "https://generativelanguage.googleapis.com/v1beta/openai", chat_path: "/chat/completions", chat_auth: "bearer", models_url: "https://generativelanguage.googleapis.com/v1beta/openai/models", models_id_path: "data[].id", validate_url: "https://generativelanguage.googleapis.com/v1beta/openai/models", probe_auth: "bearer", extra_headers: null, catalog_prefix: "google/", needs_key: true },
|
|
501
|
+
meta: { chat_base: "https://api.meta.ai/v1", chat_path: "/v1/chat/completions", chat_auth: "bearer", models_url: "https://api.meta.ai/v1/models", models_id_path: "data[].id", validate_url: "https://api.meta.ai/v1/models", probe_auth: "bearer", extra_headers: null, catalog_prefix: null, needs_key: true },
|
|
502
|
+
xai: { chat_base: "https://api.x.ai/v1", chat_path: "/v1/chat/completions", chat_auth: "bearer", models_url: "https://api.x.ai/v1/models", models_id_path: "data[].id", validate_url: "https://api.x.ai/v1/models", probe_auth: "bearer", extra_headers: null, catalog_prefix: "x-ai/", needs_key: true },
|
|
503
|
+
local: { chat_base: "http://127.0.0.1:11434", chat_path: "/v1/chat/completions", chat_auth: "none", models_url: "http://127.0.0.1:11434/api/tags", models_id_path: "models[].name", validate_url: null, probe_auth: "none", extra_headers: null, catalog_prefix: null, needs_key: false }
|
|
493
504
|
};
|
|
494
505
|
|
|
495
506
|
// src/llm-key.ts
|
|
@@ -513,7 +524,12 @@ function resolveLlmApiKey(cfg, readFile) {
|
|
|
513
524
|
|
|
514
525
|
// src/llm-source.ts
|
|
515
526
|
var PROVIDER_TABLE = Object.freeze(Object.fromEntries(
|
|
516
|
-
Object.entries(llm_providers_default).map(([p, e]) => [p, Object.freeze({
|
|
527
|
+
Object.entries(llm_providers_default).map(([p, e]) => [p, Object.freeze({
|
|
528
|
+
endpoint: e.chat_base,
|
|
529
|
+
needsKey: e.needs_key,
|
|
530
|
+
chatPath: e.chat_path,
|
|
531
|
+
chatAuth: e.chat_auth
|
|
532
|
+
})])
|
|
517
533
|
));
|
|
518
534
|
var LlmSourceUnavailableError = class extends Error {
|
|
519
535
|
};
|
|
@@ -523,6 +539,9 @@ function providerOrigin(provider) {
|
|
|
523
539
|
const u = new URL(entry.endpoint);
|
|
524
540
|
return `${u.protocol}//${u.host}`.toLowerCase();
|
|
525
541
|
}
|
|
542
|
+
function providerSlug(provider) {
|
|
543
|
+
return providerOrigin(provider).replace("://", "_").replace(/:/g, "-");
|
|
544
|
+
}
|
|
526
545
|
var explicitEndpointSet = (env) => Boolean(env.SECURECOMS_LLM_ENDPOINT || env.SECURECOMS_LLM_API_KEY_FILE || env.SECURECOMS_LLM_API_KEY);
|
|
527
546
|
function isExplicitSource(cfg, env) {
|
|
528
547
|
return explicitEndpointSet(env) || Boolean(cfg.llmApiKeyFile) || Boolean(cfg.llmApiKey);
|
|
@@ -539,7 +558,9 @@ function resolveLlmSource(cfg, explicit, deps) {
|
|
|
539
558
|
model: cfg.llmModel,
|
|
540
559
|
apiKey: resolveLlmApiKey(cfg, deps.readFile),
|
|
541
560
|
provider: "env",
|
|
542
|
-
origin: "env"
|
|
561
|
+
origin: "env",
|
|
562
|
+
chatPath: "/v1/chat/completions",
|
|
563
|
+
chatAuth: "bearer"
|
|
543
564
|
};
|
|
544
565
|
}
|
|
545
566
|
const managedDir = join2(deps.home, ".securecoms", "runtime-config", cfg.identityId);
|
|
@@ -567,6 +588,11 @@ function resolveLlmSource(cfg, explicit, deps) {
|
|
|
567
588
|
if (typeof provider !== "string" || typeof model !== "string") {
|
|
568
589
|
throw new LlmSourceUnavailableError(`${sourcePath} is missing "provider" or "model"`);
|
|
569
590
|
}
|
|
591
|
+
if (!Object.hasOwn(llm_providers_default, provider)) {
|
|
592
|
+
throw new LlmSourceUnavailableError(
|
|
593
|
+
`unknown LLM provider ${JSON.stringify(provider)} in ${sourcePath}`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
570
596
|
const entry = PROVIDER_TABLE[provider];
|
|
571
597
|
if (!entry) {
|
|
572
598
|
throw new LlmSourceUnavailableError(
|
|
@@ -575,53 +601,63 @@ function resolveLlmSource(cfg, explicit, deps) {
|
|
|
575
601
|
}
|
|
576
602
|
let apiKey;
|
|
577
603
|
if (entry.needsKey) {
|
|
578
|
-
const
|
|
579
|
-
const
|
|
580
|
-
|
|
604
|
+
const expectedOrigin = providerOrigin(provider);
|
|
605
|
+
const slug = providerSlug(provider);
|
|
606
|
+
const keyPath = join2(managedDir, `llm.key.${slug}`);
|
|
607
|
+
const hostPath = join2(managedDir, `llm.key.${slug}.host`);
|
|
608
|
+
const legacyKey = join2(managedDir, "llm.key");
|
|
609
|
+
const legacyHost = join2(managedDir, "llm.key.host");
|
|
610
|
+
const useLegacy = !deps.exists(keyPath) && deps.exists(legacyKey) && deps.exists(legacyHost);
|
|
611
|
+
const boundKey = useLegacy ? legacyKey : keyPath;
|
|
612
|
+
const boundHost = useLegacy ? legacyHost : hostPath;
|
|
613
|
+
if (!deps.exists(boundKey)) {
|
|
581
614
|
throw new LlmSourceUnavailableError(
|
|
582
615
|
`${keyPath} does not exist \u2014 no key has been written for this managed source, refusing to read it`
|
|
583
616
|
);
|
|
584
617
|
}
|
|
585
618
|
let mode;
|
|
586
619
|
try {
|
|
587
|
-
({ mode } = deps.statFile(
|
|
620
|
+
({ mode } = deps.statFile(boundKey));
|
|
588
621
|
} catch (e) {
|
|
589
622
|
throw new LlmSourceUnavailableError(
|
|
590
|
-
`cannot stat ${
|
|
623
|
+
`cannot stat ${boundKey}: ${e instanceof Error ? e.message : String(e)}`
|
|
591
624
|
);
|
|
592
625
|
}
|
|
593
626
|
if ((mode & 63) !== 0) {
|
|
594
627
|
throw new LlmSourceUnavailableError(
|
|
595
|
-
`${
|
|
628
|
+
`${boundKey} is readable by other users on this machine \u2014 tighten its permissions to owner-only`
|
|
596
629
|
);
|
|
597
630
|
}
|
|
598
|
-
|
|
599
|
-
if (!deps.exists(hostPath)) {
|
|
631
|
+
if (!deps.exists(boundHost)) {
|
|
600
632
|
throw new LlmSourceUnavailableError(
|
|
601
|
-
`${
|
|
633
|
+
`${boundKey} has no ${boundHost} sidecar recording which origin it belongs to \u2014 refusing to send it to ${expectedOrigin}`
|
|
602
634
|
);
|
|
603
635
|
}
|
|
604
|
-
const actualOrigin = deps.readFile(
|
|
636
|
+
const actualOrigin = deps.readFile(boundHost).trim();
|
|
605
637
|
if (actualOrigin !== expectedOrigin) {
|
|
606
638
|
throw new LlmSourceUnavailableError(
|
|
607
|
-
`${
|
|
639
|
+
`${boundHost} is bound to ${actualOrigin}, but provider ${JSON.stringify(provider)} requires ${expectedOrigin} \u2014 refusing to send the key across origins`
|
|
608
640
|
);
|
|
609
641
|
}
|
|
610
|
-
apiKey = resolveLlmApiKey({ llmApiKeyFile:
|
|
642
|
+
apiKey = resolveLlmApiKey({ llmApiKeyFile: boundKey }, deps.readFile);
|
|
611
643
|
}
|
|
612
644
|
return {
|
|
613
645
|
endpoint: entry.endpoint,
|
|
614
646
|
model,
|
|
615
647
|
...apiKey !== void 0 ? { apiKey } : {},
|
|
616
648
|
provider,
|
|
617
|
-
origin: "managed"
|
|
649
|
+
origin: "managed",
|
|
650
|
+
chatPath: entry.chatPath,
|
|
651
|
+
chatAuth: entry.chatAuth
|
|
618
652
|
};
|
|
619
653
|
}
|
|
620
654
|
return {
|
|
621
655
|
endpoint: cfg.llmEndpoint,
|
|
622
656
|
model: cfg.llmModel,
|
|
623
657
|
provider: "default",
|
|
624
|
-
origin: "default"
|
|
658
|
+
origin: "default",
|
|
659
|
+
chatPath: "/v1/chat/completions",
|
|
660
|
+
chatAuth: "bearer"
|
|
625
661
|
};
|
|
626
662
|
}
|
|
627
663
|
|
|
@@ -637,7 +673,11 @@ function buildLlmShim(cfg, explicit, deps) {
|
|
|
637
673
|
return {
|
|
638
674
|
async complete(messages, tools) {
|
|
639
675
|
const src = resolveLlmSource(cfg, explicit, deps);
|
|
640
|
-
const dispatcher = new LLMDispatcher(src.endpoint, src.model, {
|
|
676
|
+
const dispatcher = new LLMDispatcher(src.endpoint, src.model, {
|
|
677
|
+
apiKey: src.apiKey,
|
|
678
|
+
chatPath: src.chatPath,
|
|
679
|
+
chatAuth: src.chatAuth
|
|
680
|
+
});
|
|
641
681
|
return dispatcher.complete(messages, tools);
|
|
642
682
|
}
|
|
643
683
|
};
|