@mekareteriker/opencode-mcp 1.10.2-mekareteriker.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/LICENSE +22 -0
  3. package/README.md +174 -0
  4. package/dist/client.d.ts +60 -0
  5. package/dist/client.js +282 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/helpers.d.ts +150 -0
  8. package/dist/helpers.js +575 -0
  9. package/dist/helpers.js.map +1 -0
  10. package/dist/index.d.ts +30 -0
  11. package/dist/index.js +198 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/prompts.d.ts +9 -0
  14. package/dist/prompts.js +210 -0
  15. package/dist/prompts.js.map +1 -0
  16. package/dist/resources.d.ts +10 -0
  17. package/dist/resources.js +197 -0
  18. package/dist/resources.js.map +1 -0
  19. package/dist/server-manager.d.ts +72 -0
  20. package/dist/server-manager.js +264 -0
  21. package/dist/server-manager.js.map +1 -0
  22. package/dist/tools/config.d.ts +3 -0
  23. package/dist/tools/config.js +105 -0
  24. package/dist/tools/config.js.map +1 -0
  25. package/dist/tools/events.d.ts +6 -0
  26. package/dist/tools/events.js +63 -0
  27. package/dist/tools/events.js.map +1 -0
  28. package/dist/tools/file.d.ts +3 -0
  29. package/dist/tools/file.js +153 -0
  30. package/dist/tools/file.js.map +1 -0
  31. package/dist/tools/global.d.ts +3 -0
  32. package/dist/tools/global.js +17 -0
  33. package/dist/tools/global.js.map +1 -0
  34. package/dist/tools/message.d.ts +3 -0
  35. package/dist/tools/message.js +169 -0
  36. package/dist/tools/message.js.map +1 -0
  37. package/dist/tools/misc.d.ts +3 -0
  38. package/dist/tools/misc.js +298 -0
  39. package/dist/tools/misc.js.map +1 -0
  40. package/dist/tools/project.d.ts +3 -0
  41. package/dist/tools/project.js +62 -0
  42. package/dist/tools/project.js.map +1 -0
  43. package/dist/tools/provider.d.ts +3 -0
  44. package/dist/tools/provider.js +175 -0
  45. package/dist/tools/provider.js.map +1 -0
  46. package/dist/tools/session.d.ts +3 -0
  47. package/dist/tools/session.js +392 -0
  48. package/dist/tools/session.js.map +1 -0
  49. package/dist/tools/tui.d.ts +7 -0
  50. package/dist/tools/tui.js +121 -0
  51. package/dist/tools/tui.js.map +1 -0
  52. package/dist/tools/workflow.d.ts +7 -0
  53. package/dist/tools/workflow.js +775 -0
  54. package/dist/tools/workflow.js.map +1 -0
  55. package/package.json +68 -0
package/dist/client.js ADDED
@@ -0,0 +1,282 @@
1
+ /**
2
+ * HTTP client wrapper for the OpenCode server API.
3
+ *
4
+ * Features:
5
+ * - Basic auth support
6
+ * - Automatic retry with exponential backoff for transient errors
7
+ * - Proper 204 No Content handling on all methods
8
+ * - SSE streaming support
9
+ * - Error categorization (transient vs permanent)
10
+ * - Directory path normalization and validation
11
+ * - Lazy server reconnection on connection failure
12
+ */
13
+ import { normalizeDirectory } from "./helpers.js";
14
+ import { ensureServer, isServerRunning } from "./server-manager.js";
15
+ export class OpenCodeError extends Error {
16
+ status;
17
+ method;
18
+ path;
19
+ body;
20
+ constructor(message, status, method, path, body) {
21
+ super(message);
22
+ this.status = status;
23
+ this.method = method;
24
+ this.path = path;
25
+ this.body = body;
26
+ this.name = "OpenCodeError";
27
+ }
28
+ get isTransient() {
29
+ return (this.status === 429 ||
30
+ this.status === 502 ||
31
+ this.status === 503 ||
32
+ this.status === 504);
33
+ }
34
+ get isNotFound() {
35
+ return this.status === 404;
36
+ }
37
+ get isAuth() {
38
+ return this.status === 401 || this.status === 403;
39
+ }
40
+ }
41
+ const MAX_RETRIES = 2;
42
+ const BASE_DELAY_MS = 500;
43
+ /** Max reconnection attempts per MCP session lifetime. */
44
+ const MAX_RECONNECT_ATTEMPTS = 3;
45
+ /** Check if an error looks like a connection failure (server unreachable). */
46
+ function isConnectionError(err) {
47
+ const msg = err.message.toLowerCase();
48
+ return (msg.includes("econnrefused") ||
49
+ msg.includes("enotfound") ||
50
+ msg.includes("ehostunreach") ||
51
+ msg.includes("fetch failed") ||
52
+ msg.includes("network error") ||
53
+ msg.includes("socket hang up"));
54
+ }
55
+ export class OpenCodeClient {
56
+ baseUrl;
57
+ authHeader;
58
+ autoServe;
59
+ reconnectAttempts = 0;
60
+ username;
61
+ password;
62
+ constructor(options) {
63
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
64
+ this.autoServe = options.autoServe ?? false;
65
+ this.username = options.username;
66
+ this.password = options.password;
67
+ if (options.password) {
68
+ const username = options.username ?? "opencode";
69
+ this.authHeader =
70
+ "Basic " +
71
+ Buffer.from(`${username}:${options.password}`).toString("base64");
72
+ }
73
+ }
74
+ getBaseUrl() {
75
+ return this.baseUrl;
76
+ }
77
+ buildUrl(path, query) {
78
+ const url = new URL(path, this.baseUrl);
79
+ if (query) {
80
+ for (const [key, value] of Object.entries(query)) {
81
+ if (value !== undefined && value !== "") {
82
+ url.searchParams.set(key, value);
83
+ }
84
+ }
85
+ }
86
+ return url.toString();
87
+ }
88
+ headers(accept, directory) {
89
+ const h = {
90
+ "Content-Type": "application/json",
91
+ Accept: accept ?? "application/json",
92
+ };
93
+ if (this.authHeader) {
94
+ h["Authorization"] = this.authHeader;
95
+ }
96
+ // Normalize and validate the directory path before sending as header
97
+ const normalized = normalizeDirectory(directory);
98
+ if (normalized) {
99
+ h["x-opencode-directory"] = normalized;
100
+ }
101
+ return h;
102
+ }
103
+ async request(method, path, opts) {
104
+ const url = this.buildUrl(path, opts?.query);
105
+ let lastError;
106
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
107
+ if (attempt > 0) {
108
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1);
109
+ await new Promise((r) => setTimeout(r, delay));
110
+ }
111
+ try {
112
+ const controller = new AbortController();
113
+ const timeoutId = opts?.timeout
114
+ ? setTimeout(() => controller.abort(), opts.timeout)
115
+ : undefined;
116
+ const res = await fetch(url, {
117
+ method,
118
+ headers: this.headers(undefined, opts?.directory),
119
+ body: opts?.body !== undefined ? JSON.stringify(opts.body) : undefined,
120
+ signal: controller.signal,
121
+ });
122
+ if (timeoutId)
123
+ clearTimeout(timeoutId);
124
+ if (!res.ok) {
125
+ const text = await res.text();
126
+ const err = new OpenCodeError(`${method} ${path} failed (${res.status}): ${text}`, res.status, method, path, text);
127
+ if (err.isTransient && attempt < MAX_RETRIES) {
128
+ lastError = err;
129
+ continue;
130
+ }
131
+ throw err;
132
+ }
133
+ // Handle 204 No Content
134
+ if (res.status === 204) {
135
+ return undefined;
136
+ }
137
+ const contentType = res.headers.get("content-type") ?? "";
138
+ if (contentType.includes("application/json")) {
139
+ return (await res.json());
140
+ }
141
+ // Return text for non-JSON responses
142
+ return (await res.text());
143
+ }
144
+ catch (e) {
145
+ if (e instanceof OpenCodeError)
146
+ throw e;
147
+ lastError = e;
148
+ if (attempt >= MAX_RETRIES)
149
+ break;
150
+ }
151
+ }
152
+ // Lazy reconnection: if all retries exhausted and error looks like a
153
+ // connection failure, try restarting the server and retry once.
154
+ if (this.autoServe &&
155
+ this.reconnectAttempts < MAX_RECONNECT_ATTEMPTS &&
156
+ lastError &&
157
+ isConnectionError(lastError)) {
158
+ this.reconnectAttempts++;
159
+ console.error(`Connection failed (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}), attempting server reconnection...`);
160
+ try {
161
+ const status = await isServerRunning(this.baseUrl, this.username, this.password);
162
+ if (!status.healthy) {
163
+ await ensureServer({
164
+ baseUrl: this.baseUrl,
165
+ autoServe: true,
166
+ username: this.username,
167
+ password: this.password
168
+ });
169
+ }
170
+ // Retry the original request once after reconnection
171
+ return this.request(method, path, opts);
172
+ }
173
+ catch (reconnectErr) {
174
+ console.error(`Server reconnection failed: ${reconnectErr instanceof Error ? reconnectErr.message : String(reconnectErr)}`);
175
+ }
176
+ }
177
+ throw lastError ?? new Error(`${method} ${path} failed after retries`);
178
+ }
179
+ async get(path, query, directory) {
180
+ return this.request("GET", path, { query, directory });
181
+ }
182
+ async post(path, body, opts) {
183
+ return this.request("POST", path, {
184
+ body,
185
+ timeout: opts?.timeout,
186
+ directory: opts?.directory,
187
+ });
188
+ }
189
+ async patch(path, body, directory) {
190
+ return this.request("PATCH", path, { body, directory });
191
+ }
192
+ async put(path, body, directory) {
193
+ return this.request("PUT", path, { body, directory });
194
+ }
195
+ async delete(path, query, directory) {
196
+ return this.request("DELETE", path, { query, directory });
197
+ }
198
+ /**
199
+ * Subscribe to SSE events. Returns an async iterable of parsed events.
200
+ * The caller should break out of the loop when done.
201
+ */
202
+ async *subscribeSSE(path, opts) {
203
+ const url = this.buildUrl(path);
204
+ const res = await fetch(url, {
205
+ method: "GET",
206
+ headers: {
207
+ ...this.headers("text/event-stream"),
208
+ Accept: "text/event-stream",
209
+ "Cache-Control": "no-cache",
210
+ },
211
+ signal: opts?.signal,
212
+ });
213
+ if (!res.ok) {
214
+ const text = await res.text();
215
+ throw new OpenCodeError(`SSE ${path} failed (${res.status}): ${text}`, res.status, "GET", path, text);
216
+ }
217
+ if (!res.body) {
218
+ throw new Error("No response body for SSE stream");
219
+ }
220
+ const reader = res.body.getReader();
221
+ const decoder = new TextDecoder();
222
+ let buffer = "";
223
+ let currentEvent = "";
224
+ let currentData = "";
225
+ const abortHandler = () => {
226
+ try {
227
+ // Cancels any pending reader.read() and causes the generator to unwind.
228
+ void reader.cancel().catch(() => {
229
+ // ignore
230
+ });
231
+ }
232
+ catch {
233
+ // ignore
234
+ }
235
+ };
236
+ if (opts?.signal) {
237
+ if (opts.signal.aborted)
238
+ abortHandler();
239
+ else
240
+ opts.signal.addEventListener("abort", abortHandler, { once: true });
241
+ }
242
+ try {
243
+ while (true) {
244
+ if (opts?.signal?.aborted)
245
+ break;
246
+ const { done, value } = await reader.read();
247
+ if (done)
248
+ break;
249
+ buffer += decoder.decode(value, { stream: true });
250
+ const lines = buffer.split("\n");
251
+ buffer = lines.pop() ?? "";
252
+ for (const line of lines) {
253
+ if (line.startsWith("event:")) {
254
+ currentEvent = line.slice(6).trim();
255
+ }
256
+ else if (line.startsWith("data:")) {
257
+ currentData = line.slice(5).trim();
258
+ }
259
+ else if (line === "") {
260
+ if (currentData) {
261
+ yield { event: currentEvent || "message", data: currentData };
262
+ currentEvent = "";
263
+ currentData = "";
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+ finally {
270
+ if (opts?.signal) {
271
+ try {
272
+ opts.signal.removeEventListener("abort", abortHandler);
273
+ }
274
+ catch {
275
+ // ignore
276
+ }
277
+ }
278
+ reader.releaseLock();
279
+ }
280
+ }
281
+ }
282
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Smart response formatting helpers.
3
+ *
4
+ * Instead of dumping raw JSON to the LLM, these helpers extract the
5
+ * meaningful content from OpenCode API responses so the LLM can reason
6
+ * about them efficiently.
7
+ */
8
+ import { z } from "zod";
9
+ /** Read-only tool: does not modify state. */
10
+ export declare const readOnly: {
11
+ readonly readOnlyHint: true;
12
+ readonly destructiveHint: false;
13
+ };
14
+ /** Destructive tool: permanently deletes data or shuts down services. */
15
+ export declare const destructive: {
16
+ readonly readOnlyHint: false;
17
+ readonly destructiveHint: true;
18
+ };
19
+ /**
20
+ * Shared Zod parameter for project directory targeting.
21
+ * When provided, sent as the x-opencode-directory header so the
22
+ * OpenCode server scopes the request to that project.
23
+ */
24
+ export declare const directoryParam: z.ZodOptional<z.ZodString>;
25
+ /**
26
+ * Set the global default provider and model.
27
+ * Called once from index.ts during startup.
28
+ */
29
+ export declare function setModelDefaults(providerID?: string, modelID?: string): void;
30
+ /**
31
+ * Apply model defaults: use explicit params if both are provided,
32
+ * otherwise fall back to env-var defaults if both are set,
33
+ * otherwise return undefined (let the server decide).
34
+ *
35
+ * Returns `{ providerID, modelID }` or `undefined`.
36
+ */
37
+ export declare function applyModelDefaults(providerID?: string, modelID?: string, variant?: string): {
38
+ providerID: string;
39
+ modelID: string;
40
+ variant?: string;
41
+ } | undefined;
42
+ /**
43
+ * Normalize and validate a directory path:
44
+ * - Resolves to absolute (handles "..", ".", trailing slashes, and
45
+ * converts relative inputs against `process.cwd()`)
46
+ * - Confirms the resolved path is absolute for the current platform
47
+ * - Validates that the path exists on disk
48
+ *
49
+ * Accepts both POSIX ("/home/user/my-project") and Windows
50
+ * ("C:\\Users\\me\\my-project", "\\\\server\\share") absolute paths via
51
+ * the platform-aware `resolve` + `isAbsolute` from `node:path`.
52
+ *
53
+ * Returns the normalized path, or undefined if input was undefined.
54
+ * Throws a descriptive Error on validation failure.
55
+ */
56
+ export declare function normalizeDirectory(directory?: string): string | undefined;
57
+ /**
58
+ * Extract a human-readable summary from a message response.
59
+ * Pulls text content from parts, summarizes tool calls, etc.
60
+ * Accepts any shape — casts internally for safety.
61
+ */
62
+ export declare function formatMessageResponse(response: unknown): string;
63
+ /**
64
+ * Format a list of messages, extracting text content from each.
65
+ *
66
+ * When the assistant message has no text content (common with some providers
67
+ * that only emit tool calls), we show a concise summary of tool actions
68
+ * instead of blank output. Cost/token metadata from step-finish parts is
69
+ * appended when available.
70
+ */
71
+ export declare function formatMessageList(messages: unknown[]): string;
72
+ /**
73
+ * Format a diff response into a readable summary.
74
+ */
75
+ export declare function formatDiffResponse(diffs: unknown[]): string;
76
+ /**
77
+ * Format session objects for LLM-friendly display.
78
+ */
79
+ export declare function formatSessionList(sessions: unknown[]): string;
80
+ /**
81
+ * Generic safe JSON stringify with truncation for very large responses.
82
+ */
83
+ export declare function safeStringify(value: unknown, maxLength?: number): string;
84
+ /**
85
+ * Analyze an AI message response for signs of failure:
86
+ * - Completely empty (null/undefined)
87
+ * - Has parts but no text content (provider returned nothing)
88
+ * - Contains error indicators in parts
89
+ *
90
+ * Returns a diagnostic object with `isEmpty`, `hasError`, and `warning` text.
91
+ */
92
+ export declare function analyzeMessageResponse(response: unknown): {
93
+ isEmpty: boolean;
94
+ hasError: boolean;
95
+ warning: string | null;
96
+ };
97
+ /**
98
+ * Redact values that look like API keys, tokens, or secrets.
99
+ * Replaces the value with the first 4 characters + "***REDACTED***".
100
+ * Works recursively on objects and arrays.
101
+ *
102
+ * Three layers of detection:
103
+ * 1. Key-name based: key names matching sensitive patterns (KEY, TOKEN, SECRET, etc.)
104
+ * 2. Value-based: string values matching known API key prefixes or long hex/base64 tokens
105
+ * 3. URL-based: query parameters in URL strings that contain secrets
106
+ */
107
+ export declare function redactSecrets(value: unknown): unknown;
108
+ /**
109
+ * Determine whether a provider object from the OpenCode API is truly configured
110
+ * (i.e. has usable credentials), as opposed to being a built-in default.
111
+ *
112
+ * Detection layers:
113
+ * - source "env" / "config" / "api" → always configured
114
+ * - source "custom" with a non-empty apiKey → configured
115
+ * - source "custom" for "anthropic" with extra option keys (OAuth sets headers) → configured
116
+ * - Everything else → not configured
117
+ */
118
+ export declare function isProviderConfigured(p: Record<string, unknown>): boolean;
119
+ /**
120
+ * Resolve a session status value from the OpenCode API.
121
+ *
122
+ * The API may return status as a plain string ("idle", "running") or as an
123
+ * object like `{ state: "running", ... }`. This helper normalises both forms
124
+ * into a human-readable string.
125
+ */
126
+ export declare function resolveSessionStatus(raw: unknown): string;
127
+ /**
128
+ * Standard tool response builder.
129
+ */
130
+ export declare function toolResult(text: string, isError?: boolean): {
131
+ isError?: boolean | undefined;
132
+ content: {
133
+ type: "text";
134
+ text: string;
135
+ }[];
136
+ };
137
+ export declare function toolError(e: unknown): {
138
+ isError?: boolean | undefined;
139
+ content: {
140
+ type: "text";
141
+ text: string;
142
+ }[];
143
+ };
144
+ export declare function toolJson(value: unknown): {
145
+ isError?: boolean | undefined;
146
+ content: {
147
+ type: "text";
148
+ text: string;
149
+ }[];
150
+ };