diff --git a/src/komodo-client.ts b/src/komodo-client.ts index f9c54c5..929952e 100644 --- a/src/komodo-client.ts +++ b/src/komodo-client.ts @@ -47,9 +47,13 @@ export class KomodoClient { const res = await fetch(`${this.baseUrl}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, + // Komodo Core v2.3.3 login shape (verified live): {type, params} body: JSON.stringify({ - username: this.username, - password: this.password, + type: "LoginLocalUser", + params: { + username: this.username, + password: this.password, + }, }), signal: AbortSignal.timeout(30000), }); @@ -57,7 +61,13 @@ export class KomodoClient { const text = await res.text().catch(() => ""); throw new Error(`Komodo login failed (${res.status}): ${text}`); } - this.token = await res.text(); + // Response shape: {type, data: {jwt}} — NOT raw text + const body = (await res.json()) as { data?: { jwt?: string } }; + const jwt = body?.data?.jwt; + if (!jwt) { + throw new Error(`Komodo login succeeded but no data.jwt in response`); + } + this.token = jwt; } async rpc( diff --git a/src/tools/inspect.ts b/src/tools/inspect.ts index b289e17..27211aa 100644 --- a/src/tools/inspect.ts +++ b/src/tools/inspect.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { KomodoClient } from "../komodo-client.js"; -import { INSPECT_REQUEST_MAP } from "../types.js"; +import { INSPECT_REQUEST_MAP, INSPECT_PARAM_KEY } from "../types.js"; const InspectType = z.enum([ "container", "image", "network", "volume", @@ -33,8 +33,16 @@ export async function handleInspect( throw new Error(`Unknown inspect type: ${inspect_type}`); } - const params: Record = { id }; - if (server) params.server = server; + const params: Record = {}; + const paramKey = server ? INSPECT_PARAM_KEY[inspect_type] : undefined; + if (paramKey) { + // Server-scoped Docker objects: {server, } + params.server = server; + params[paramKey] = id; + } else { + params.id = id; + if (server) params.server = server; + } const result = await client.rpc("read", requestName, params); diff --git a/src/tools/search-logs.ts b/src/tools/search-logs.ts index b9d6a6a..295026a 100644 --- a/src/tools/search-logs.ts +++ b/src/tools/search-logs.ts @@ -11,6 +11,18 @@ export const searchLogsInputSchema = { id: z.string().describe("Resource ID or name"), query: z.string().describe("Search query to filter log lines"), tail: z.number().optional().describe("Number of recent log lines to return"), + server: z + .string() + .optional() + .describe("Server name (required for container logs)"), + swarm: z + .string() + .optional() + .describe("Swarm name (required for swarm_service logs)"), + services: z + .array(z.string()) + .optional() + .describe("Service names (required for stack logs)"), }; export async function handleSearchLogs( @@ -19,19 +31,45 @@ export async function handleSearchLogs( id: string; query: string; tail?: number; + server?: string; + swarm?: string; + services?: string[]; }, client: KomodoClient, ): Promise<{ content: { type: "text"; text: string }[] }> { - const { resource_type, id, query, tail } = args; + const { resource_type, id, query, tail, server, swarm, services } = args; const requestName = SEARCH_LOG_REQUEST_MAP[resource_type]; if (!requestName) { throw new Error(`No log search endpoint for resource type: ${resource_type}`); } - const params: Record = { id, query }; + // Core log search shapes are not uniform per docs.rs: + // - deployment: {deployment, terms, tail?} + // - container: {server, container, terms, tail?} + // - stack: {stack, services, terms, tail?} + // - swarm_service: {swarm, service, terms, tail?} + const params: Record = { terms: [query] }; if (tail !== undefined) params.tail = tail; + switch (resource_type) { + case "deployment": + params.deployment = id; + break; + case "container": + params.server = server; + params.container = id; + break; + case "stack": + params.stack = id; + params.services = services; + break; + case "swarm_service": + params.swarm = swarm; + params.service = id; + break; + } + const result = await client.rpc("read", requestName, params); return { diff --git a/src/types.ts b/src/types.ts index f324062..d92bd2d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -449,7 +449,18 @@ export const SUMMARY_REQUEST_MAP: Partial> = { action: "GetActionsSummary", }; -// Maps inspect type to the inspect RPC request name +// Maps inspect type to the inspect RPC request name. +// Server-scoped Docker objects also need an object param key (see +// INSPECT_PARAM_KEY below): Core v2.3.3 shapes are {server, container}, +// {server, image}, {server, network}, {server, volume} — sending +// {id, server} fails with "duplicate field `server`". +export const INSPECT_PARAM_KEY: Record = { + container: "container", + image: "image", + network: "network", + volume: "volume", +}; + export const INSPECT_REQUEST_MAP: Record = { container: "InspectContainer", image: "InspectImage",