zoe-agent 0.3.1
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/CHANGELOG.md +154 -0
- package/LICENSE +96 -0
- package/README.md +568 -0
- package/dist/adapters/cli/agent.d.ts +59 -0
- package/dist/adapters/cli/agent.js +232 -0
- package/dist/adapters/cli/bootstrap.d.ts +25 -0
- package/dist/adapters/cli/bootstrap.js +204 -0
- package/dist/adapters/cli/commands/build-registry.d.ts +14 -0
- package/dist/adapters/cli/commands/build-registry.js +88 -0
- package/dist/adapters/cli/commands/clear.d.ts +7 -0
- package/dist/adapters/cli/commands/clear.js +10 -0
- package/dist/adapters/cli/commands/compact.d.ts +13 -0
- package/dist/adapters/cli/commands/compact.js +96 -0
- package/dist/adapters/cli/commands/exit.d.ts +7 -0
- package/dist/adapters/cli/commands/exit.js +9 -0
- package/dist/adapters/cli/commands/gateway.d.ts +7 -0
- package/dist/adapters/cli/commands/gateway.js +152 -0
- package/dist/adapters/cli/commands/help.d.ts +9 -0
- package/dist/adapters/cli/commands/help.js +12 -0
- package/dist/adapters/cli/commands/models.d.ts +10 -0
- package/dist/adapters/cli/commands/models.js +32 -0
- package/dist/adapters/cli/commands/registry.d.ts +70 -0
- package/dist/adapters/cli/commands/registry.js +111 -0
- package/dist/adapters/cli/commands/settings-utils.d.ts +38 -0
- package/dist/adapters/cli/commands/settings-utils.js +182 -0
- package/dist/adapters/cli/commands/settings.d.ts +9 -0
- package/dist/adapters/cli/commands/settings.js +395 -0
- package/dist/adapters/cli/commands/skills.d.ts +7 -0
- package/dist/adapters/cli/commands/skills.js +21 -0
- package/dist/adapters/cli/config-loader.d.ts +27 -0
- package/dist/adapters/cli/config-loader.js +48 -0
- package/dist/adapters/cli/docker-utils.d.ts +37 -0
- package/dist/adapters/cli/docker-utils.js +90 -0
- package/dist/adapters/cli/index.d.ts +2 -0
- package/dist/adapters/cli/index.js +88 -0
- package/dist/adapters/cli/repl.d.ts +22 -0
- package/dist/adapters/cli/repl.js +256 -0
- package/dist/adapters/cli/setup.d.ts +19 -0
- package/dist/adapters/cli/setup.js +613 -0
- package/dist/adapters/cli/system-prompts.d.ts +56 -0
- package/dist/adapters/cli/system-prompts.js +131 -0
- package/dist/adapters/cli/tui/app.d.ts +58 -0
- package/dist/adapters/cli/tui/app.js +314 -0
- package/dist/adapters/cli/tui/components/assistant-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/assistant-message.js +9 -0
- package/dist/adapters/cli/tui/components/autocomplete.d.ts +19 -0
- package/dist/adapters/cli/tui/components/autocomplete.js +75 -0
- package/dist/adapters/cli/tui/components/command-palette.d.ts +15 -0
- package/dist/adapters/cli/tui/components/command-palette.js +50 -0
- package/dist/adapters/cli/tui/components/diff-viewer.d.ts +5 -0
- package/dist/adapters/cli/tui/components/diff-viewer.js +109 -0
- package/dist/adapters/cli/tui/components/error-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/error-message.js +8 -0
- package/dist/adapters/cli/tui/components/footer.d.ts +20 -0
- package/dist/adapters/cli/tui/components/footer.js +19 -0
- package/dist/adapters/cli/tui/components/goal-status.d.ts +12 -0
- package/dist/adapters/cli/tui/components/goal-status.js +22 -0
- package/dist/adapters/cli/tui/components/info-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/info-message.js +8 -0
- package/dist/adapters/cli/tui/components/logo-banner.d.ts +7 -0
- package/dist/adapters/cli/tui/components/logo-banner.js +33 -0
- package/dist/adapters/cli/tui/components/markdown.d.ts +9 -0
- package/dist/adapters/cli/tui/components/markdown.js +92 -0
- package/dist/adapters/cli/tui/components/message-area.d.ts +19 -0
- package/dist/adapters/cli/tui/components/message-area.js +55 -0
- package/dist/adapters/cli/tui/components/permission-prompt.d.ts +13 -0
- package/dist/adapters/cli/tui/components/permission-prompt.js +32 -0
- package/dist/adapters/cli/tui/components/prompt-area.d.ts +22 -0
- package/dist/adapters/cli/tui/components/prompt-area.js +68 -0
- package/dist/adapters/cli/tui/components/text-input.d.ts +27 -0
- package/dist/adapters/cli/tui/components/text-input.js +142 -0
- package/dist/adapters/cli/tui/components/tool-call-block.d.ts +11 -0
- package/dist/adapters/cli/tui/components/tool-call-block.js +68 -0
- package/dist/adapters/cli/tui/components/user-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/user-message.js +8 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.d.ts +11 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.js +11 -0
- package/dist/adapters/cli/tui/diff/line-diff.d.ts +17 -0
- package/dist/adapters/cli/tui/diff/line-diff.js +44 -0
- package/dist/adapters/cli/tui/feed-serializer.d.ts +29 -0
- package/dist/adapters/cli/tui/feed-serializer.js +70 -0
- package/dist/adapters/cli/tui/file-index.d.ts +8 -0
- package/dist/adapters/cli/tui/file-index.js +41 -0
- package/dist/adapters/cli/tui/hooks/use-agent.d.ts +54 -0
- package/dist/adapters/cli/tui/hooks/use-agent.js +177 -0
- package/dist/adapters/cli/tui/hooks/use-feed.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-feed.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.d.ts +10 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.js +43 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-theme.d.ts +8 -0
- package/dist/adapters/cli/tui/hooks/use-theme.js +12 -0
- package/dist/adapters/cli/tui/index.d.ts +19 -0
- package/dist/adapters/cli/tui/index.js +206 -0
- package/dist/adapters/cli/tui/ink-reset.d.ts +29 -0
- package/dist/adapters/cli/tui/ink-reset.js +57 -0
- package/dist/adapters/cli/tui/layout.d.ts +15 -0
- package/dist/adapters/cli/tui/layout.js +15 -0
- package/dist/adapters/cli/tui/logo/gradient.d.ts +11 -0
- package/dist/adapters/cli/tui/logo/gradient.js +31 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.d.ts +4 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.js +26 -0
- package/dist/adapters/cli/tui/overlays/model-selector.d.ts +14 -0
- package/dist/adapters/cli/tui/overlays/model-selector.js +43 -0
- package/dist/adapters/cli/tui/overlays/session-selector.d.ts +35 -0
- package/dist/adapters/cli/tui/overlays/session-selector.js +162 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.d.ts +24 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.js +126 -0
- package/dist/adapters/cli/tui/session-export.d.ts +21 -0
- package/dist/adapters/cli/tui/session-export.js +63 -0
- package/dist/adapters/cli/tui/theme.d.ts +23 -0
- package/dist/adapters/cli/tui/theme.js +22 -0
- package/dist/adapters/cli/tui/types.d.ts +52 -0
- package/dist/adapters/cli/tui/types.js +12 -0
- package/dist/adapters/sdk/agent.d.ts +20 -0
- package/dist/adapters/sdk/agent.js +356 -0
- package/dist/adapters/sdk/http.d.ts +43 -0
- package/dist/adapters/sdk/http.js +61 -0
- package/dist/adapters/sdk/index.d.ts +58 -0
- package/dist/adapters/sdk/index.js +209 -0
- package/dist/adapters/sdk/settings.d.ts +18 -0
- package/dist/adapters/sdk/settings.js +57 -0
- package/dist/adapters/sdk/tools.d.ts +7 -0
- package/dist/adapters/sdk/tools.js +13 -0
- package/dist/adapters/server/auth.d.ts +53 -0
- package/dist/adapters/server/auth.js +168 -0
- package/dist/adapters/server/index.d.ts +40 -0
- package/dist/adapters/server/index.js +255 -0
- package/dist/adapters/server/rest-gateway.d.ts +13 -0
- package/dist/adapters/server/rest-gateway.js +218 -0
- package/dist/adapters/server/rest.d.ts +37 -0
- package/dist/adapters/server/rest.js +341 -0
- package/dist/adapters/server/server-core.d.ts +55 -0
- package/dist/adapters/server/server-core.js +121 -0
- package/dist/adapters/server/session-store.d.ts +81 -0
- package/dist/adapters/server/session-store.js +272 -0
- package/dist/adapters/server/settings-handlers.d.ts +24 -0
- package/dist/adapters/server/settings-handlers.js +360 -0
- package/dist/adapters/server/standalone.d.ts +19 -0
- package/dist/adapters/server/standalone.js +113 -0
- package/dist/adapters/server/websocket.d.ts +26 -0
- package/dist/adapters/server/websocket.js +68 -0
- package/dist/adapters/server/ws-handlers.d.ts +32 -0
- package/dist/adapters/server/ws-handlers.js +523 -0
- package/dist/adapters/server/ws-types.d.ts +304 -0
- package/dist/adapters/server/ws-types.js +7 -0
- package/dist/core/agent-loop.d.ts +68 -0
- package/dist/core/agent-loop.js +423 -0
- package/dist/core/config.d.ts +115 -0
- package/dist/core/config.js +189 -0
- package/dist/core/errors.d.ts +58 -0
- package/dist/core/errors.js +88 -0
- package/dist/core/hooks.d.ts +35 -0
- package/dist/core/hooks.js +49 -0
- package/dist/core/index.d.ts +23 -0
- package/dist/core/index.js +29 -0
- package/dist/core/message-convert.d.ts +41 -0
- package/dist/core/message-convert.js +94 -0
- package/dist/core/middleware/auth.d.ts +24 -0
- package/dist/core/middleware/auth.js +28 -0
- package/dist/core/middleware/logging.d.ts +23 -0
- package/dist/core/middleware/logging.js +28 -0
- package/dist/core/middleware/rate-limit.d.ts +27 -0
- package/dist/core/middleware/rate-limit.js +38 -0
- package/dist/core/middleware/semantic-tools.d.ts +10 -0
- package/dist/core/middleware/semantic-tools.js +43 -0
- package/dist/core/middleware.d.ts +48 -0
- package/dist/core/middleware.js +38 -0
- package/dist/core/permission.d.ts +25 -0
- package/dist/core/permission.js +50 -0
- package/dist/core/provider-config.d.ts +129 -0
- package/dist/core/provider-config.js +273 -0
- package/dist/core/provider-env.d.ts +39 -0
- package/dist/core/provider-env.js +142 -0
- package/dist/core/provider-resolver.d.ts +12 -0
- package/dist/core/provider-resolver.js +12 -0
- package/dist/core/session-store.d.ts +75 -0
- package/dist/core/session-store.js +245 -0
- package/dist/core/settings-manager.d.ts +57 -0
- package/dist/core/settings-manager.js +359 -0
- package/dist/core/settings-schema.d.ts +38 -0
- package/dist/core/settings-schema.js +171 -0
- package/dist/core/skill-catalog.d.ts +6 -0
- package/dist/core/skill-catalog.js +17 -0
- package/dist/core/skill-invoker.d.ts +127 -0
- package/dist/core/skill-invoker.js +182 -0
- package/dist/core/stream-accumulator.d.ts +21 -0
- package/dist/core/stream-accumulator.js +51 -0
- package/dist/core/stream-manager.d.ts +58 -0
- package/dist/core/stream-manager.js +212 -0
- package/dist/core/tool-executor.d.ts +84 -0
- package/dist/core/tool-executor.js +256 -0
- package/dist/core/types.d.ts +259 -0
- package/dist/core/types.js +11 -0
- package/dist/gateway/gateway.d.ts +52 -0
- package/dist/gateway/gateway.js +537 -0
- package/dist/gateway/index.d.ts +21 -0
- package/dist/gateway/index.js +31 -0
- package/dist/gateway/openapi-importer.d.ts +15 -0
- package/dist/gateway/openapi-importer.js +66 -0
- package/dist/gateway/semantic-scorer.d.ts +7 -0
- package/dist/gateway/semantic-scorer.js +24 -0
- package/dist/gateway/settings-adapter.d.ts +49 -0
- package/dist/gateway/settings-adapter.js +137 -0
- package/dist/gateway/tool-factory.d.ts +9 -0
- package/dist/gateway/tool-factory.js +414 -0
- package/dist/gateway/types.d.ts +68 -0
- package/dist/gateway/types.js +7 -0
- package/dist/models-catalog.js +46 -0
- package/dist/providers/anthropic.d.ts +22 -0
- package/dist/providers/anthropic.js +148 -0
- package/dist/providers/factory.d.ts +10 -0
- package/dist/providers/factory.js +25 -0
- package/dist/providers/openai.d.ts +15 -0
- package/dist/providers/openai.js +71 -0
- package/dist/providers/types.d.ts +48 -0
- package/dist/providers/types.js +1 -0
- package/dist/skills/args.d.ts +37 -0
- package/dist/skills/args.js +99 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +23 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/skills/loader.js +59 -0
- package/dist/skills/parser.d.ts +7 -0
- package/dist/skills/parser.js +152 -0
- package/dist/skills/registry.d.ts +13 -0
- package/dist/skills/registry.js +74 -0
- package/dist/skills/resolver.d.ts +19 -0
- package/dist/skills/resolver.js +116 -0
- package/dist/skills/types.d.ts +74 -0
- package/dist/skills/types.js +50 -0
- package/dist/tools/browser.d.ts +2 -0
- package/dist/tools/browser.js +68 -0
- package/dist/tools/core.d.ts +20 -0
- package/dist/tools/core.js +244 -0
- package/dist/tools/email.d.ts +2 -0
- package/dist/tools/email.js +61 -0
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.js +257 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +88 -0
- package/dist/tools/interface.d.ts +22 -0
- package/dist/tools/interface.js +1 -0
- package/dist/tools/notify.d.ts +2 -0
- package/dist/tools/notify.js +100 -0
- package/dist/tools/prompt-optimizer.d.ts +2 -0
- package/dist/tools/prompt-optimizer.js +65 -0
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +184 -0
- package/dist/tools/search.d.ts +2 -0
- package/dist/tools/search.js +78 -0
- package/dist/tools/todos.d.ts +10 -0
- package/dist/tools/todos.js +50 -0
- package/package.json +119 -0
- package/skills/docker-ops/SKILL.md +329 -0
- package/skills/k8s-deploy/SKILL.md +397 -0
- package/skills/log-analyzer/SKILL.md +331 -0
- package/skills/speckit-analyze/SKILL.md +260 -0
- package/skills/speckit-checklist/SKILL.md +374 -0
- package/skills/speckit-clarify/SKILL.md +286 -0
- package/skills/speckit-constitution/SKILL.md +157 -0
- package/skills/speckit-implement/SKILL.md +224 -0
- package/skills/speckit-plan/SKILL.md +171 -0
- package/skills/speckit-specify/SKILL.md +346 -0
- package/skills/speckit-tasks/SKILL.md +215 -0
- package/skills/speckit-taskstoissues/SKILL.md +107 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Gateway — Semantic Scorer
|
|
3
|
+
*
|
|
4
|
+
* Keyword-based relevance scoring for tool injection.
|
|
5
|
+
* Zero dependencies, deterministic, fast.
|
|
6
|
+
*/
|
|
7
|
+
const STOP_WORDS = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been',
|
|
8
|
+
'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
9
|
+
'should', 'may', 'might', 'can', 'shall', 'to', 'of', 'in', 'for', 'on',
|
|
10
|
+
'with', 'at', 'by', 'from', 'as', 'into', 'through', 'during', 'before',
|
|
11
|
+
'after', 'and', 'but', 'or', 'not', 'no', 'so', 'if', 'then', 'than',
|
|
12
|
+
'that', 'this', 'it', 'its', 'me', 'my', 'we', 'our', 'you', 'your',
|
|
13
|
+
'he', 'she', 'they', 'them', 'what', 'which', 'who', 'when', 'where',
|
|
14
|
+
'how', 'all', 'each', 'every', 'both', 'few', 'more', 'most', 'other',
|
|
15
|
+
'some', 'such', 'only', 'own', 'same', 'too', 'very', 'just', 'about',
|
|
16
|
+
'also', 'now', 'here', 'there', 'please', 'want', 'need', 'make',
|
|
17
|
+
'send', 'get', 'use', 'help', 'show', 'tell', 'let', 'go', 'come',
|
|
18
|
+
'take', 'give', 'try', 'ask', 'know', 'think', 'see', 'look',
|
|
19
|
+
]);
|
|
20
|
+
export function scoreRelevance(query, text) {
|
|
21
|
+
const words = query.toLowerCase().split(/\W+/).filter(w => w.length > 1 && !STOP_WORDS.has(w));
|
|
22
|
+
const target = text.toLowerCase();
|
|
23
|
+
return words.reduce((score, word) => score + (target.includes(word) ? 1 : 0), 0);
|
|
24
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Gateway — Settings Adapter
|
|
3
|
+
*
|
|
4
|
+
* Dedicated storage for gateway targets, credentials, and routes.
|
|
5
|
+
* Bypasses the static SettingsManager SETTINGS_MAP which rejects dynamic keys.
|
|
6
|
+
* Uses atomic writes (temp file + rename) matching existing Zoe patterns.
|
|
7
|
+
*/
|
|
8
|
+
import type { Target } from './types.js';
|
|
9
|
+
export declare class GatewaySettingsAdapter {
|
|
10
|
+
private targetsPath;
|
|
11
|
+
private credentialsPath;
|
|
12
|
+
private routesPath;
|
|
13
|
+
private adminTargetsPath;
|
|
14
|
+
private cachedTargets;
|
|
15
|
+
private cachedCredentials;
|
|
16
|
+
private cachedRoutes;
|
|
17
|
+
private cachedAdminTargets;
|
|
18
|
+
constructor(storageDir: string);
|
|
19
|
+
initialize(): Promise<void>;
|
|
20
|
+
loadTargets(): Promise<Record<string, Target>>;
|
|
21
|
+
saveTarget(name: string, target: Target): Promise<void>;
|
|
22
|
+
deleteTarget(name: string): Promise<void>;
|
|
23
|
+
getTargets(): Record<string, Target>;
|
|
24
|
+
loadCredentials(): Promise<Record<string, string>>;
|
|
25
|
+
getCredential(key: string): string | undefined;
|
|
26
|
+
setCredential(key: string, value: string): Promise<void>;
|
|
27
|
+
deleteCredential(key: string): Promise<void>;
|
|
28
|
+
listCredentialKeys(): string[];
|
|
29
|
+
loadRoutes(): Promise<Array<{
|
|
30
|
+
pattern: string;
|
|
31
|
+
target: string;
|
|
32
|
+
priority: number;
|
|
33
|
+
}>>;
|
|
34
|
+
saveRoutes(routes: Array<{
|
|
35
|
+
pattern: string;
|
|
36
|
+
target: string;
|
|
37
|
+
priority: number;
|
|
38
|
+
}>): Promise<void>;
|
|
39
|
+
getRoutes(): Array<{
|
|
40
|
+
pattern: string;
|
|
41
|
+
target: string;
|
|
42
|
+
priority: number;
|
|
43
|
+
}>;
|
|
44
|
+
loadAdminTargets(): Promise<Set<string>>;
|
|
45
|
+
getAdminTargets(): Set<string>;
|
|
46
|
+
addAdminTarget(name: string): Promise<void>;
|
|
47
|
+
removeAdminTarget(name: string): Promise<void>;
|
|
48
|
+
private atomicWrite;
|
|
49
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Gateway — Settings Adapter
|
|
3
|
+
*
|
|
4
|
+
* Dedicated storage for gateway targets, credentials, and routes.
|
|
5
|
+
* Bypasses the static SettingsManager SETTINGS_MAP which rejects dynamic keys.
|
|
6
|
+
* Uses atomic writes (temp file + rename) matching existing Zoe patterns.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
export class GatewaySettingsAdapter {
|
|
11
|
+
targetsPath;
|
|
12
|
+
credentialsPath;
|
|
13
|
+
routesPath;
|
|
14
|
+
adminTargetsPath;
|
|
15
|
+
cachedTargets = {};
|
|
16
|
+
cachedCredentials = {};
|
|
17
|
+
cachedRoutes = [];
|
|
18
|
+
cachedAdminTargets = new Set();
|
|
19
|
+
constructor(storageDir) {
|
|
20
|
+
const base = process.env.ZOE_GATEWAY_DIR ?? path.join(storageDir, 'gateway');
|
|
21
|
+
this.targetsPath = path.join(base, 'targets.json');
|
|
22
|
+
this.credentialsPath = path.join(base, 'credentials.json');
|
|
23
|
+
this.routesPath = path.join(base, 'routes.json');
|
|
24
|
+
this.adminTargetsPath = path.join(base, 'admin-targets.json');
|
|
25
|
+
}
|
|
26
|
+
async initialize() {
|
|
27
|
+
await Promise.all([
|
|
28
|
+
this.loadTargets(),
|
|
29
|
+
this.loadCredentials(),
|
|
30
|
+
this.loadRoutes(),
|
|
31
|
+
this.loadAdminTargets(),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
// ── Targets ───────────────────────────────────────────────────────────
|
|
35
|
+
async loadTargets() {
|
|
36
|
+
try {
|
|
37
|
+
const data = await fs.readFile(this.targetsPath, 'utf-8');
|
|
38
|
+
this.cachedTargets = JSON.parse(data);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
if (e.code !== 'ENOENT')
|
|
42
|
+
throw e;
|
|
43
|
+
this.cachedTargets = {};
|
|
44
|
+
}
|
|
45
|
+
return this.cachedTargets;
|
|
46
|
+
}
|
|
47
|
+
async saveTarget(name, target) {
|
|
48
|
+
this.cachedTargets[name] = target;
|
|
49
|
+
await this.atomicWrite(this.targetsPath, JSON.stringify(this.cachedTargets, null, 2));
|
|
50
|
+
}
|
|
51
|
+
async deleteTarget(name) {
|
|
52
|
+
delete this.cachedTargets[name];
|
|
53
|
+
await this.atomicWrite(this.targetsPath, JSON.stringify(this.cachedTargets, null, 2));
|
|
54
|
+
}
|
|
55
|
+
getTargets() {
|
|
56
|
+
return this.cachedTargets;
|
|
57
|
+
}
|
|
58
|
+
// ── Credentials ───────────────────────────────────────────────────────
|
|
59
|
+
async loadCredentials() {
|
|
60
|
+
try {
|
|
61
|
+
const data = await fs.readFile(this.credentialsPath, 'utf-8');
|
|
62
|
+
this.cachedCredentials = JSON.parse(data);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
if (e.code !== 'ENOENT')
|
|
66
|
+
throw e;
|
|
67
|
+
this.cachedCredentials = {};
|
|
68
|
+
}
|
|
69
|
+
return this.cachedCredentials;
|
|
70
|
+
}
|
|
71
|
+
getCredential(key) {
|
|
72
|
+
return this.cachedCredentials[key];
|
|
73
|
+
}
|
|
74
|
+
async setCredential(key, value) {
|
|
75
|
+
this.cachedCredentials[key] = value;
|
|
76
|
+
await this.atomicWrite(this.credentialsPath, JSON.stringify(this.cachedCredentials, null, 2), 0o600);
|
|
77
|
+
}
|
|
78
|
+
async deleteCredential(key) {
|
|
79
|
+
delete this.cachedCredentials[key];
|
|
80
|
+
await this.atomicWrite(this.credentialsPath, JSON.stringify(this.cachedCredentials, null, 2), 0o600);
|
|
81
|
+
}
|
|
82
|
+
listCredentialKeys() {
|
|
83
|
+
return Object.keys(this.cachedCredentials);
|
|
84
|
+
}
|
|
85
|
+
// ── Routes ────────────────────────────────────────────────────────────
|
|
86
|
+
async loadRoutes() {
|
|
87
|
+
try {
|
|
88
|
+
const data = await fs.readFile(this.routesPath, 'utf-8');
|
|
89
|
+
this.cachedRoutes = JSON.parse(data);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
if (e.code !== 'ENOENT')
|
|
93
|
+
throw e;
|
|
94
|
+
this.cachedRoutes = [];
|
|
95
|
+
}
|
|
96
|
+
return this.cachedRoutes;
|
|
97
|
+
}
|
|
98
|
+
async saveRoutes(routes) {
|
|
99
|
+
this.cachedRoutes = routes;
|
|
100
|
+
await this.atomicWrite(this.routesPath, JSON.stringify(routes, null, 2));
|
|
101
|
+
}
|
|
102
|
+
getRoutes() {
|
|
103
|
+
return this.cachedRoutes;
|
|
104
|
+
}
|
|
105
|
+
// ── Admin targets ────────────────────────────────────────────────────
|
|
106
|
+
async loadAdminTargets() {
|
|
107
|
+
try {
|
|
108
|
+
const data = await fs.readFile(this.adminTargetsPath, 'utf-8');
|
|
109
|
+
const arr = JSON.parse(data);
|
|
110
|
+
this.cachedAdminTargets = new Set(arr);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (e.code !== 'ENOENT')
|
|
114
|
+
throw e;
|
|
115
|
+
this.cachedAdminTargets = new Set();
|
|
116
|
+
}
|
|
117
|
+
return this.cachedAdminTargets;
|
|
118
|
+
}
|
|
119
|
+
getAdminTargets() {
|
|
120
|
+
return this.cachedAdminTargets;
|
|
121
|
+
}
|
|
122
|
+
async addAdminTarget(name) {
|
|
123
|
+
this.cachedAdminTargets.add(name);
|
|
124
|
+
await this.atomicWrite(this.adminTargetsPath, JSON.stringify([...this.cachedAdminTargets], null, 2));
|
|
125
|
+
}
|
|
126
|
+
async removeAdminTarget(name) {
|
|
127
|
+
this.cachedAdminTargets.delete(name);
|
|
128
|
+
await this.atomicWrite(this.adminTargetsPath, JSON.stringify([...this.cachedAdminTargets], null, 2));
|
|
129
|
+
}
|
|
130
|
+
// ── Atomic write ──────────────────────────────────────────────────────
|
|
131
|
+
async atomicWrite(filePath, content, mode) {
|
|
132
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
133
|
+
const tempPath = `${filePath}.tmp`;
|
|
134
|
+
await fs.writeFile(tempPath, content, mode ? { mode } : undefined);
|
|
135
|
+
await fs.rename(tempPath, filePath);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Gateway — Proxy tool factory
|
|
3
|
+
*
|
|
4
|
+
* Creates 10 gateway tools registered in the static tool registry.
|
|
5
|
+
* Each tool delegates to MCPGateway methods.
|
|
6
|
+
*/
|
|
7
|
+
import type { MCPGateway } from './gateway.js';
|
|
8
|
+
import type { ToolModule } from '../tools/interface.js';
|
|
9
|
+
export declare function createGatewayTools(gateway: MCPGateway): ToolModule[];
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Gateway — Proxy tool factory
|
|
3
|
+
*
|
|
4
|
+
* Creates 10 gateway tools registered in the static tool registry.
|
|
5
|
+
* Each tool delegates to MCPGateway methods.
|
|
6
|
+
*/
|
|
7
|
+
export function createGatewayTools(gateway) {
|
|
8
|
+
return [
|
|
9
|
+
// 1. gateway_route
|
|
10
|
+
{
|
|
11
|
+
name: 'gateway_route',
|
|
12
|
+
risk: 'safe',
|
|
13
|
+
definition: {
|
|
14
|
+
type: 'function',
|
|
15
|
+
function: {
|
|
16
|
+
name: 'gateway_route',
|
|
17
|
+
description: 'Given a natural-language description of what you want to do, finds the best-matching external tool or API operation available through connected services (MCP servers or REST APIs). Returns which service and tool to use, or tells you what services are available if nothing matches. Use this when you need to interact with an external service but are not sure which tool to call.',
|
|
18
|
+
parameters: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
request: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Natural-language description of the request to route',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ['request'],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
handler: async (args) => {
|
|
31
|
+
return gateway.routeRequest(args.request);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
// 2. gateway_call_tool
|
|
35
|
+
{
|
|
36
|
+
name: 'gateway_call_tool',
|
|
37
|
+
risk: 'communications',
|
|
38
|
+
definition: {
|
|
39
|
+
type: 'function',
|
|
40
|
+
function: {
|
|
41
|
+
name: 'gateway_call_tool',
|
|
42
|
+
description: 'Call an MCP tool on a gateway target. Proxies the call through the gateway to the target MCP server.',
|
|
43
|
+
parameters: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
target: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Name of the registered MCP target',
|
|
49
|
+
},
|
|
50
|
+
tool: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'Name of the tool to call on the target',
|
|
53
|
+
},
|
|
54
|
+
arguments: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
description: 'Arguments to pass to the tool',
|
|
57
|
+
properties: {},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
required: ['target', 'tool'],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
handler: async (args, config) => {
|
|
65
|
+
const agent = config?.agentName ?? 'zoe';
|
|
66
|
+
const result = await gateway.callMcpTool(agent, args.target, args.tool, args.arguments ?? {});
|
|
67
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// 3. gateway_call_rest
|
|
71
|
+
{
|
|
72
|
+
name: 'gateway_call_rest',
|
|
73
|
+
risk: 'communications',
|
|
74
|
+
definition: {
|
|
75
|
+
type: 'function',
|
|
76
|
+
function: {
|
|
77
|
+
name: 'gateway_call_rest',
|
|
78
|
+
description: 'Make a REST call through the gateway to a registered REST target.',
|
|
79
|
+
parameters: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
target: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'Name of the registered REST target',
|
|
85
|
+
},
|
|
86
|
+
path: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Request path (appended to target baseUrl)',
|
|
89
|
+
},
|
|
90
|
+
method: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'HTTP method (GET, POST, PUT, PATCH, DELETE)',
|
|
93
|
+
},
|
|
94
|
+
query: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
description: 'Query parameters',
|
|
97
|
+
properties: {},
|
|
98
|
+
additionalProperties: { type: 'string' },
|
|
99
|
+
},
|
|
100
|
+
body: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
description: 'Request body (JSON)',
|
|
103
|
+
properties: {},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ['target', 'method'],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
handler: async (args, config) => {
|
|
111
|
+
const agent = config?.agentName ?? 'zoe';
|
|
112
|
+
const result = await gateway.callRest(agent, args.target, args.path ?? '', args.method, args.query, args.body);
|
|
113
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
// 4. gateway_capabilities
|
|
117
|
+
{
|
|
118
|
+
name: 'gateway_capabilities',
|
|
119
|
+
risk: 'safe',
|
|
120
|
+
definition: {
|
|
121
|
+
type: 'function',
|
|
122
|
+
function: {
|
|
123
|
+
name: 'gateway_capabilities',
|
|
124
|
+
description: 'List all registered gateway targets and their capabilities (tools, resources, prompts, operations).',
|
|
125
|
+
parameters: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {},
|
|
128
|
+
required: [],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
handler: async () => {
|
|
133
|
+
const targets = gateway.getTargets();
|
|
134
|
+
const entries = Object.entries(targets);
|
|
135
|
+
if (entries.length === 0)
|
|
136
|
+
return 'No gateway targets registered.';
|
|
137
|
+
const lines = entries.map(([name, t]) => {
|
|
138
|
+
const enabled = t.enabled ? 'enabled' : 'disabled';
|
|
139
|
+
if (t.kind === 'mcp') {
|
|
140
|
+
const tools = t.capabilities?.tools?.length ?? 0;
|
|
141
|
+
const resources = t.capabilities?.resources?.length ?? 0;
|
|
142
|
+
const prompts = t.capabilities?.prompts?.length ?? 0;
|
|
143
|
+
return ` ${name} (${enabled}, MCP/${t.transport}): ${tools} tools, ${resources} resources, ${prompts} prompts — ${t.description}`;
|
|
144
|
+
}
|
|
145
|
+
return ` ${name} (${enabled}, REST ${t.baseUrl}): ${t.operations.length} operations — ${t.description}`;
|
|
146
|
+
});
|
|
147
|
+
return `Gateway targets (${entries.length}):\n${lines.join('\n')}`;
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
// 5. gateway_read_resource
|
|
151
|
+
{
|
|
152
|
+
name: 'gateway_read_resource',
|
|
153
|
+
risk: 'safe',
|
|
154
|
+
definition: {
|
|
155
|
+
type: 'function',
|
|
156
|
+
function: {
|
|
157
|
+
name: 'gateway_read_resource',
|
|
158
|
+
description: 'Read an MCP resource from a gateway target.',
|
|
159
|
+
parameters: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
target: {
|
|
163
|
+
type: 'string',
|
|
164
|
+
description: 'Name of the registered MCP target',
|
|
165
|
+
},
|
|
166
|
+
uri: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
description: 'URI of the resource to read',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
required: ['target', 'uri'],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
handler: async (args, config) => {
|
|
176
|
+
const agent = config?.agentName ?? 'zoe';
|
|
177
|
+
const result = await gateway.readResource(agent, args.target, args.uri);
|
|
178
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
// 6. gateway_get_prompt
|
|
182
|
+
{
|
|
183
|
+
name: 'gateway_get_prompt',
|
|
184
|
+
risk: 'safe',
|
|
185
|
+
definition: {
|
|
186
|
+
type: 'function',
|
|
187
|
+
function: {
|
|
188
|
+
name: 'gateway_get_prompt',
|
|
189
|
+
description: 'Get an MCP prompt template from a gateway target.',
|
|
190
|
+
parameters: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
target: {
|
|
194
|
+
type: 'string',
|
|
195
|
+
description: 'Name of the registered MCP target',
|
|
196
|
+
},
|
|
197
|
+
name: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
description: 'Name of the prompt to retrieve',
|
|
200
|
+
},
|
|
201
|
+
arguments: {
|
|
202
|
+
type: 'object',
|
|
203
|
+
description: 'Arguments for the prompt template',
|
|
204
|
+
properties: {},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
required: ['target', 'name'],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
handler: async (args, config) => {
|
|
212
|
+
const agent = config?.agentName ?? 'zoe';
|
|
213
|
+
const result = await gateway.getPrompt(agent, args.target, args.name, args.arguments);
|
|
214
|
+
return typeof result === 'string' ? result : JSON.stringify(result);
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
// 7. gateway_import_openapi
|
|
218
|
+
{
|
|
219
|
+
name: 'gateway_import_openapi',
|
|
220
|
+
risk: 'safe',
|
|
221
|
+
definition: {
|
|
222
|
+
type: 'function',
|
|
223
|
+
function: {
|
|
224
|
+
name: 'gateway_import_openapi',
|
|
225
|
+
description: 'Import an OpenAPI/Swagger spec as a REST target. Parses the spec, registers it, and returns the list of operations.',
|
|
226
|
+
parameters: {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
name: {
|
|
230
|
+
type: 'string',
|
|
231
|
+
description: 'Name for the registered target',
|
|
232
|
+
},
|
|
233
|
+
specUrl: {
|
|
234
|
+
type: 'string',
|
|
235
|
+
description: 'URL to the OpenAPI/Swagger spec (JSON or YAML)',
|
|
236
|
+
},
|
|
237
|
+
baseUrl: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'Override base URL (defaults to spec servers[0].url)',
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
required: ['name', 'specUrl'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
handler: async (args) => {
|
|
247
|
+
const { importOpenApiSpec } = await import('./openapi-importer.js');
|
|
248
|
+
const result = await importOpenApiSpec(gateway, args.name, args.specUrl, {
|
|
249
|
+
baseUrl: args.baseUrl,
|
|
250
|
+
isAdmin: false, // Agent tool — NOT admin-registered (B3 trust guard)
|
|
251
|
+
});
|
|
252
|
+
return `Imported ${result.imported} operations: ${result.operations.join(', ')}`;
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
// 8. gateway_register_target
|
|
256
|
+
{
|
|
257
|
+
name: 'gateway_register_target',
|
|
258
|
+
risk: 'communications',
|
|
259
|
+
definition: {
|
|
260
|
+
type: 'function',
|
|
261
|
+
function: {
|
|
262
|
+
name: 'gateway_register_target',
|
|
263
|
+
description: 'Register a new MCP or REST target on the gateway. Validates required fields before registration.',
|
|
264
|
+
parameters: {
|
|
265
|
+
type: 'object',
|
|
266
|
+
properties: {
|
|
267
|
+
name: {
|
|
268
|
+
type: 'string',
|
|
269
|
+
description: 'Unique name for the target',
|
|
270
|
+
},
|
|
271
|
+
kind: {
|
|
272
|
+
type: 'string',
|
|
273
|
+
description: 'Target kind: "mcp" or "rest"',
|
|
274
|
+
},
|
|
275
|
+
transport: {
|
|
276
|
+
type: 'string',
|
|
277
|
+
description: 'MCP transport type: "stdio", "sse", or "http"',
|
|
278
|
+
},
|
|
279
|
+
command: {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Command for MCP stdio transport',
|
|
282
|
+
},
|
|
283
|
+
url: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'URL for MCP sse/http transport',
|
|
286
|
+
},
|
|
287
|
+
baseUrl: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
description: 'Base URL for REST targets',
|
|
290
|
+
},
|
|
291
|
+
description: {
|
|
292
|
+
type: 'string',
|
|
293
|
+
description: 'Human-readable description of the target',
|
|
294
|
+
},
|
|
295
|
+
tags: {
|
|
296
|
+
type: 'array',
|
|
297
|
+
items: { type: 'string' },
|
|
298
|
+
description: 'Tags for semantic routing',
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
required: ['name', 'kind'],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
handler: async (args) => {
|
|
306
|
+
if (!['mcp', 'rest'].includes(args.kind)) {
|
|
307
|
+
return `Error: kind must be "mcp" or "rest", got "${args.kind}"`;
|
|
308
|
+
}
|
|
309
|
+
if (args.kind === 'mcp') {
|
|
310
|
+
const transport = args.transport;
|
|
311
|
+
if (!transport || !['stdio', 'sse', 'http'].includes(transport)) {
|
|
312
|
+
return 'Error: MCP targets require transport (stdio, sse, or http)';
|
|
313
|
+
}
|
|
314
|
+
if (transport === 'stdio' && !args.command) {
|
|
315
|
+
return 'Error: MCP stdio targets require a command';
|
|
316
|
+
}
|
|
317
|
+
if ((transport === 'sse' || transport === 'http') && !args.url) {
|
|
318
|
+
return `Error: MCP ${transport} targets require a url`;
|
|
319
|
+
}
|
|
320
|
+
const target = {
|
|
321
|
+
kind: 'mcp',
|
|
322
|
+
transport: transport,
|
|
323
|
+
command: args.command,
|
|
324
|
+
args: args.args,
|
|
325
|
+
env: args.env,
|
|
326
|
+
url: args.url,
|
|
327
|
+
description: args.description ?? `MCP target ${args.name}`,
|
|
328
|
+
tags: args.tags ?? [],
|
|
329
|
+
enabled: true,
|
|
330
|
+
};
|
|
331
|
+
await gateway.registerTarget(args.name, target, false);
|
|
332
|
+
return `Registered MCP target "${args.name}" (${transport})`;
|
|
333
|
+
}
|
|
334
|
+
if (!args.baseUrl) {
|
|
335
|
+
return 'Error: REST targets require a baseUrl';
|
|
336
|
+
}
|
|
337
|
+
const target = {
|
|
338
|
+
kind: 'rest',
|
|
339
|
+
baseUrl: args.baseUrl,
|
|
340
|
+
description: args.description ?? `REST target ${args.name}`,
|
|
341
|
+
auth: { type: 'none' },
|
|
342
|
+
defaultHeaders: {},
|
|
343
|
+
operations: [],
|
|
344
|
+
tags: args.tags ?? [],
|
|
345
|
+
enabled: true,
|
|
346
|
+
};
|
|
347
|
+
await gateway.registerTarget(args.name, target, false);
|
|
348
|
+
return `Registered REST target "${args.name}" (${args.baseUrl})`;
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
// 9. gateway_audit_log
|
|
352
|
+
{
|
|
353
|
+
name: 'gateway_audit_log',
|
|
354
|
+
risk: 'safe',
|
|
355
|
+
definition: {
|
|
356
|
+
type: 'function',
|
|
357
|
+
function: {
|
|
358
|
+
name: 'gateway_audit_log',
|
|
359
|
+
description: 'Retrieve formatted audit logs from the gateway. Optionally filter by target and limit count.',
|
|
360
|
+
parameters: {
|
|
361
|
+
type: 'object',
|
|
362
|
+
properties: {
|
|
363
|
+
target: {
|
|
364
|
+
type: 'string',
|
|
365
|
+
description: 'Filter logs to this target name',
|
|
366
|
+
},
|
|
367
|
+
limit: {
|
|
368
|
+
type: 'number',
|
|
369
|
+
description: 'Maximum number of log entries to return (default: 10)',
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
required: [],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
handler: async (args) => {
|
|
377
|
+
const logs = gateway.getAuditLogs(args.target, args.limit || 10);
|
|
378
|
+
if (logs.length === 0)
|
|
379
|
+
return 'No audit logs found.';
|
|
380
|
+
const lines = logs.map((log) => {
|
|
381
|
+
const ts = new Date(log.timestamp).toISOString();
|
|
382
|
+
const status = log.success ? 'OK' : 'FAIL';
|
|
383
|
+
return ` [${ts}] ${log.agent} -> ${log.target} ${log.operation} (${status}, ${log.durationMs}ms)`;
|
|
384
|
+
});
|
|
385
|
+
return `Audit logs (${logs.length}):\n${lines.join('\n')}`;
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
// 10. gateway_usage_stats
|
|
389
|
+
{
|
|
390
|
+
name: 'gateway_usage_stats',
|
|
391
|
+
risk: 'safe',
|
|
392
|
+
definition: {
|
|
393
|
+
type: 'function',
|
|
394
|
+
function: {
|
|
395
|
+
name: 'gateway_usage_stats',
|
|
396
|
+
description: 'Return usage summary per target: total calls and error counts.',
|
|
397
|
+
parameters: {
|
|
398
|
+
type: 'object',
|
|
399
|
+
properties: {},
|
|
400
|
+
required: [],
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
handler: async () => {
|
|
405
|
+
const summary = gateway.getUsageSummary();
|
|
406
|
+
const entries = Object.entries(summary);
|
|
407
|
+
if (entries.length === 0)
|
|
408
|
+
return 'No gateway usage recorded.';
|
|
409
|
+
const lines = entries.map(([target, stats]) => ` ${target}: ${stats.calls} calls, ${stats.errors} errors`);
|
|
410
|
+
return `Gateway usage:\n${lines.join('\n')}`;
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
];
|
|
414
|
+
}
|