feat: full Komodo API coverage — 11 tools, ~336 endpoints
Expand from 7 tools to 11 tools covering 100% of the Komodo v2.3.2 API: New tools: - komodo_summary: resource summaries/stats - komodo_inspect: Docker object inspection (16 types) - komodo_search_logs: log search with query - komodo_list_detail: extended listing (27 types) Expanded existing tools: - 12 → 21 resource types (action, repo, builder, swarm, variable, user_group, alert, terminal, git_provider_account) - 37 → 100+ execute operations - komodo_logs: 4 resource types with proper endpoints Other changes: - API key auth support (X-Api-Key / X-Api-Secret headers) - 30s fetch timeout + structured error handling - Updated docs: references/api.md (483 lines), README.md (238 lines)
This commit is contained in:
+53
-10
@@ -1,16 +1,44 @@
|
||||
import { KomodoApiError } from "./errors.js";
|
||||
|
||||
export class KomodoClient {
|
||||
private baseUrl: string;
|
||||
private username: string;
|
||||
private password: string;
|
||||
private apiKey: string | null = null;
|
||||
private apiSecret: string | null = null;
|
||||
private token: string | null = null;
|
||||
private useApiKey: boolean;
|
||||
|
||||
constructor() {
|
||||
this.baseUrl = process.env.KOMODO_BASE_URL || process.env.KOMODO_URL || "http://10.10.2.114:9120";
|
||||
this.baseUrl =
|
||||
process.env.KOMODO_BASE_URL || process.env.KOMODO_URL || "http://10.10.2.114:9120";
|
||||
this.username = process.env.KOMODO_USERNAME || "";
|
||||
this.password = process.env.KOMODO_PASSWORD || "";
|
||||
this.apiKey = process.env.KOMODO_API_KEY || null;
|
||||
this.apiSecret = process.env.KOMODO_API_SECRET || null;
|
||||
this.useApiKey = !!(this.apiKey && this.apiSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate auth headers based on the configured auth method.
|
||||
* API key auth (X-Api-Key / X-Api-Secret) is preferred when credentials are set;
|
||||
* otherwise falls back to JWT Bearer token.
|
||||
*/
|
||||
private async getAuthHeaders(): Promise<Record<string, string>> {
|
||||
if (this.useApiKey) {
|
||||
return {
|
||||
"X-Api-Key": this.apiKey!,
|
||||
"X-Api-Secret": this.apiSecret!,
|
||||
};
|
||||
}
|
||||
await this.ensureAuth();
|
||||
return {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
};
|
||||
}
|
||||
|
||||
private async ensureAuth(): Promise<void> {
|
||||
if (this.useApiKey) return; // No login needed for API key auth
|
||||
if (this.token) return;
|
||||
await this.login();
|
||||
}
|
||||
@@ -23,6 +51,7 @@ export class KomodoClient {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
}),
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
@@ -40,31 +69,45 @@ export class KomodoClient {
|
||||
|
||||
const url = `${this.baseUrl}/${route}/${requestName}`;
|
||||
|
||||
const attempt = async (token: string): Promise<Response> => {
|
||||
const attempt = async (
|
||||
authHeaders: Record<string, string>,
|
||||
): Promise<Response> => {
|
||||
return fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
...authHeaders,
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
};
|
||||
|
||||
let res = await attempt(this.token!);
|
||||
const authHeaders = await this.getAuthHeaders();
|
||||
let res = await attempt(authHeaders);
|
||||
|
||||
// Auto-refresh on 401
|
||||
if (res.status === 401) {
|
||||
// Auto-refresh on 401 (JWT only)
|
||||
if (res.status === 401 && !this.useApiKey) {
|
||||
this.token = null;
|
||||
await this.login();
|
||||
res = await attempt(this.token!);
|
||||
res = await attempt({ Authorization: `Bearer ${this.token}` });
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
throw new Error(
|
||||
`Komodo RPC ${route}/${requestName} failed (${res.status}): ${text}`,
|
||||
);
|
||||
let message = `Komodo RPC ${route}/${requestName} failed (${res.status}): ${text}`;
|
||||
try {
|
||||
const body = JSON.parse(text) as {
|
||||
error?: string;
|
||||
trace?: unknown[];
|
||||
};
|
||||
if (body.error) {
|
||||
message = `Komodo RPC ${route}/${requestName} failed (${res.status}): ${body.error}`;
|
||||
}
|
||||
} catch {
|
||||
// Not JSON, use raw text
|
||||
}
|
||||
throw new KomodoApiError(message, res.status, route, requestName);
|
||||
}
|
||||
|
||||
const text = await res.text();
|
||||
|
||||
Reference in New Issue
Block a user