@morphllm/morphsdk 0.2.6

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 (168) hide show
  1. package/README.md +39 -0
  2. package/dist/chunk-4UVEBIDK.js +358 -0
  3. package/dist/chunk-4UVEBIDK.js.map +1 -0
  4. package/dist/chunk-4V46N27D.js +169 -0
  5. package/dist/chunk-4V46N27D.js.map +1 -0
  6. package/dist/chunk-4VWJFZVS.js +89 -0
  7. package/dist/chunk-4VWJFZVS.js.map +1 -0
  8. package/dist/chunk-5COKN3XD.js +91 -0
  9. package/dist/chunk-5COKN3XD.js.map +1 -0
  10. package/dist/chunk-5VQEQSJQ.js +394 -0
  11. package/dist/chunk-5VQEQSJQ.js.map +1 -0
  12. package/dist/chunk-63WE2C5R.js +43 -0
  13. package/dist/chunk-63WE2C5R.js.map +1 -0
  14. package/dist/chunk-74ZHKB54.js +9 -0
  15. package/dist/chunk-74ZHKB54.js.map +1 -0
  16. package/dist/chunk-7PZJQFCY.js +39 -0
  17. package/dist/chunk-7PZJQFCY.js.map +1 -0
  18. package/dist/chunk-BILUTNBC.js +83 -0
  19. package/dist/chunk-BILUTNBC.js.map +1 -0
  20. package/dist/chunk-G4DJ6VSM.js +78 -0
  21. package/dist/chunk-G4DJ6VSM.js.map +1 -0
  22. package/dist/chunk-HGIFACNP.js +59 -0
  23. package/dist/chunk-HGIFACNP.js.map +1 -0
  24. package/dist/chunk-OI5YYE36.js +189 -0
  25. package/dist/chunk-OI5YYE36.js.map +1 -0
  26. package/dist/chunk-PZ5AY32C.js +10 -0
  27. package/dist/chunk-PZ5AY32C.js.map +1 -0
  28. package/dist/chunk-VJK4PH5V.js +105 -0
  29. package/dist/chunk-VJK4PH5V.js.map +1 -0
  30. package/dist/chunk-WXBUVKYL.js +128 -0
  31. package/dist/chunk-WXBUVKYL.js.map +1 -0
  32. package/dist/chunk-X2K57BH6.js +1 -0
  33. package/dist/chunk-X2K57BH6.js.map +1 -0
  34. package/dist/chunk-YQMPVJ2L.js +32 -0
  35. package/dist/chunk-YQMPVJ2L.js.map +1 -0
  36. package/dist/chunk-YWS2GRQC.js +97 -0
  37. package/dist/chunk-YWS2GRQC.js.map +1 -0
  38. package/dist/chunk-ZQEWQ7LJ.js +97 -0
  39. package/dist/chunk-ZQEWQ7LJ.js.map +1 -0
  40. package/dist/client.cjs +1358 -0
  41. package/dist/client.cjs.map +1 -0
  42. package/dist/client.js +15 -0
  43. package/dist/client.js.map +1 -0
  44. package/dist/git/client.cjs +428 -0
  45. package/dist/git/client.cjs.map +1 -0
  46. package/dist/git/client.js +8 -0
  47. package/dist/git/client.js.map +1 -0
  48. package/dist/git/config.cjs +41 -0
  49. package/dist/git/config.cjs.map +1 -0
  50. package/dist/git/config.js +17 -0
  51. package/dist/git/config.js.map +1 -0
  52. package/dist/git/index.cjs +438 -0
  53. package/dist/git/index.cjs.map +1 -0
  54. package/dist/git/index.js +14 -0
  55. package/dist/git/index.js.map +1 -0
  56. package/dist/git/types.cjs +19 -0
  57. package/dist/git/types.cjs.map +1 -0
  58. package/dist/git/types.js +1 -0
  59. package/dist/git/types.js.map +1 -0
  60. package/dist/index.cjs +1372 -0
  61. package/dist/index.cjs.map +1 -0
  62. package/dist/index.js +34 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/tools/browser/anthropic.cjs +281 -0
  65. package/dist/tools/browser/anthropic.cjs.map +1 -0
  66. package/dist/tools/browser/anthropic.js +72 -0
  67. package/dist/tools/browser/anthropic.js.map +1 -0
  68. package/dist/tools/browser/core.cjs +459 -0
  69. package/dist/tools/browser/core.cjs.map +1 -0
  70. package/dist/tools/browser/core.js +21 -0
  71. package/dist/tools/browser/core.js.map +1 -0
  72. package/dist/tools/browser/index.cjs +497 -0
  73. package/dist/tools/browser/index.cjs.map +1 -0
  74. package/dist/tools/browser/index.js +27 -0
  75. package/dist/tools/browser/index.js.map +1 -0
  76. package/dist/tools/browser/openai.cjs +297 -0
  77. package/dist/tools/browser/openai.cjs.map +1 -0
  78. package/dist/tools/browser/openai.js +85 -0
  79. package/dist/tools/browser/openai.js.map +1 -0
  80. package/dist/tools/browser/prompts.cjs +64 -0
  81. package/dist/tools/browser/prompts.cjs.map +1 -0
  82. package/dist/tools/browser/prompts.js +10 -0
  83. package/dist/tools/browser/prompts.js.map +1 -0
  84. package/dist/tools/browser/types.cjs +19 -0
  85. package/dist/tools/browser/types.cjs.map +1 -0
  86. package/dist/tools/browser/types.js +1 -0
  87. package/dist/tools/browser/types.js.map +1 -0
  88. package/dist/tools/browser/vercel.cjs +242 -0
  89. package/dist/tools/browser/vercel.cjs.map +1 -0
  90. package/dist/tools/browser/vercel.js +49 -0
  91. package/dist/tools/browser/vercel.js.map +1 -0
  92. package/dist/tools/codebase_search/anthropic.cjs +267 -0
  93. package/dist/tools/codebase_search/anthropic.cjs.map +1 -0
  94. package/dist/tools/codebase_search/anthropic.js +11 -0
  95. package/dist/tools/codebase_search/anthropic.js.map +1 -0
  96. package/dist/tools/codebase_search/core.cjs +201 -0
  97. package/dist/tools/codebase_search/core.cjs.map +1 -0
  98. package/dist/tools/codebase_search/core.js +11 -0
  99. package/dist/tools/codebase_search/core.js.map +1 -0
  100. package/dist/tools/codebase_search/index.cjs +393 -0
  101. package/dist/tools/codebase_search/index.cjs.map +1 -0
  102. package/dist/tools/codebase_search/index.js +27 -0
  103. package/dist/tools/codebase_search/index.js.map +1 -0
  104. package/dist/tools/codebase_search/openai.cjs +316 -0
  105. package/dist/tools/codebase_search/openai.cjs.map +1 -0
  106. package/dist/tools/codebase_search/openai.js +21 -0
  107. package/dist/tools/codebase_search/openai.js.map +1 -0
  108. package/dist/tools/codebase_search/prompts.cjs +57 -0
  109. package/dist/tools/codebase_search/prompts.cjs.map +1 -0
  110. package/dist/tools/codebase_search/prompts.js +10 -0
  111. package/dist/tools/codebase_search/prompts.js.map +1 -0
  112. package/dist/tools/codebase_search/types.cjs +19 -0
  113. package/dist/tools/codebase_search/types.cjs.map +1 -0
  114. package/dist/tools/codebase_search/types.js +1 -0
  115. package/dist/tools/codebase_search/types.js.map +1 -0
  116. package/dist/tools/codebase_search/vercel.cjs +230 -0
  117. package/dist/tools/codebase_search/vercel.cjs.map +1 -0
  118. package/dist/tools/codebase_search/vercel.js +15 -0
  119. package/dist/tools/codebase_search/vercel.js.map +1 -0
  120. package/dist/tools/fastapply/anthropic.cjs +335 -0
  121. package/dist/tools/fastapply/anthropic.cjs.map +1 -0
  122. package/dist/tools/fastapply/anthropic.js +13 -0
  123. package/dist/tools/fastapply/anthropic.js.map +1 -0
  124. package/dist/tools/fastapply/core.cjs +267 -0
  125. package/dist/tools/fastapply/core.cjs.map +1 -0
  126. package/dist/tools/fastapply/core.js +15 -0
  127. package/dist/tools/fastapply/core.js.map +1 -0
  128. package/dist/tools/fastapply/index.cjs +500 -0
  129. package/dist/tools/fastapply/index.cjs.map +1 -0
  130. package/dist/tools/fastapply/index.js +32 -0
  131. package/dist/tools/fastapply/index.js.map +1 -0
  132. package/dist/tools/fastapply/openai.cjs +353 -0
  133. package/dist/tools/fastapply/openai.cjs.map +1 -0
  134. package/dist/tools/fastapply/openai.js +21 -0
  135. package/dist/tools/fastapply/openai.js.map +1 -0
  136. package/dist/tools/fastapply/prompts.cjs +68 -0
  137. package/dist/tools/fastapply/prompts.cjs.map +1 -0
  138. package/dist/tools/fastapply/prompts.js +10 -0
  139. package/dist/tools/fastapply/prompts.js.map +1 -0
  140. package/dist/tools/fastapply/types.cjs +19 -0
  141. package/dist/tools/fastapply/types.cjs.map +1 -0
  142. package/dist/tools/fastapply/types.js +1 -0
  143. package/dist/tools/fastapply/types.js.map +1 -0
  144. package/dist/tools/fastapply/vercel.cjs +347 -0
  145. package/dist/tools/fastapply/vercel.cjs.map +1 -0
  146. package/dist/tools/fastapply/vercel.js +17 -0
  147. package/dist/tools/fastapply/vercel.js.map +1 -0
  148. package/dist/tools/index.cjs +500 -0
  149. package/dist/tools/index.cjs.map +1 -0
  150. package/dist/tools/index.js +32 -0
  151. package/dist/tools/index.js.map +1 -0
  152. package/dist/tools/modelrouter/core.cjs +286 -0
  153. package/dist/tools/modelrouter/core.cjs.map +1 -0
  154. package/dist/tools/modelrouter/core.js +13 -0
  155. package/dist/tools/modelrouter/core.js.map +1 -0
  156. package/dist/tools/modelrouter/index.cjs +286 -0
  157. package/dist/tools/modelrouter/index.cjs.map +1 -0
  158. package/dist/tools/modelrouter/index.js +13 -0
  159. package/dist/tools/modelrouter/index.js.map +1 -0
  160. package/dist/tools/modelrouter/types.cjs +19 -0
  161. package/dist/tools/modelrouter/types.cjs.map +1 -0
  162. package/dist/tools/modelrouter/types.js +1 -0
  163. package/dist/tools/modelrouter/types.js.map +1 -0
  164. package/dist/tools/utils/resilience.cjs +115 -0
  165. package/dist/tools/utils/resilience.cjs.map +1 -0
  166. package/dist/tools/utils/resilience.js +12 -0
  167. package/dist/tools/utils/resilience.js.map +1 -0
  168. package/package.json +159 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,1372 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AnthropicRouter: () => AnthropicRouter,
34
+ BrowserClient: () => BrowserClient,
35
+ CodebaseSearchClient: () => CodebaseSearchClient,
36
+ FastApplyClient: () => FastApplyClient,
37
+ GeminiRouter: () => GeminiRouter,
38
+ MorphClient: () => MorphClient,
39
+ MorphGit: () => MorphGit,
40
+ OpenAIRouter: () => OpenAIRouter
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // tools/fastapply/core.ts
45
+ var import_promises = require("fs/promises");
46
+ var import_path = require("path");
47
+ var import_diff = require("diff");
48
+
49
+ // tools/utils/resilience.ts
50
+ var DEFAULT_RETRY_CONFIG = {
51
+ maxRetries: 3,
52
+ initialDelay: 1e3,
53
+ maxDelay: 3e4,
54
+ backoffMultiplier: 2,
55
+ retryableErrors: ["ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]
56
+ };
57
+ async function fetchWithRetry(url, options, retryConfig = {}) {
58
+ const {
59
+ maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,
60
+ initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,
61
+ maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,
62
+ backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,
63
+ retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,
64
+ onRetry
65
+ } = retryConfig;
66
+ let lastError = null;
67
+ let delay = initialDelay;
68
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
69
+ try {
70
+ const response = await fetch(url, options);
71
+ if (response.status === 429 || response.status === 503) {
72
+ if (attempt < maxRetries) {
73
+ const retryAfter = response.headers.get("Retry-After");
74
+ const waitTime = retryAfter ? parseInt(retryAfter) * 1e3 : Math.min(delay, maxDelay);
75
+ const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);
76
+ if (onRetry) {
77
+ onRetry(attempt + 1, error);
78
+ }
79
+ await sleep(waitTime);
80
+ delay *= backoffMultiplier;
81
+ continue;
82
+ }
83
+ }
84
+ return response;
85
+ } catch (error) {
86
+ lastError = error;
87
+ const isRetryable = retryableErrors.some(
88
+ (errType) => lastError?.message?.includes(errType)
89
+ );
90
+ if (!isRetryable || attempt === maxRetries) {
91
+ throw lastError;
92
+ }
93
+ const waitTime = Math.min(delay, maxDelay);
94
+ if (onRetry) {
95
+ onRetry(attempt + 1, lastError);
96
+ }
97
+ await sleep(waitTime);
98
+ delay *= backoffMultiplier;
99
+ }
100
+ }
101
+ throw lastError || new Error("Max retries exceeded");
102
+ }
103
+ async function withTimeout(promise, timeoutMs, errorMessage) {
104
+ let timeoutId;
105
+ const timeoutPromise = new Promise((_, reject) => {
106
+ timeoutId = setTimeout(() => {
107
+ reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));
108
+ }, timeoutMs);
109
+ });
110
+ try {
111
+ const result = await Promise.race([promise, timeoutPromise]);
112
+ clearTimeout(timeoutId);
113
+ return result;
114
+ } catch (error) {
115
+ clearTimeout(timeoutId);
116
+ throw error;
117
+ }
118
+ }
119
+ function sleep(ms) {
120
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
121
+ }
122
+
123
+ // tools/fastapply/core.ts
124
+ var DEFAULT_CONFIG = {
125
+ morphApiUrl: "https://api.morphllm.com",
126
+ baseDir: process.cwd(),
127
+ generateUdiff: true,
128
+ autoWrite: true,
129
+ timeout: 3e4,
130
+ debug: false
131
+ };
132
+ var FastApplyClient = class {
133
+ config;
134
+ constructor(config = {}) {
135
+ this.config = {
136
+ morphApiKey: config.apiKey,
137
+ morphApiUrl: DEFAULT_CONFIG.morphApiUrl,
138
+ debug: config.debug,
139
+ timeout: config.timeout || DEFAULT_CONFIG.timeout,
140
+ retryConfig: config.retryConfig,
141
+ generateUdiff: DEFAULT_CONFIG.generateUdiff,
142
+ autoWrite: DEFAULT_CONFIG.autoWrite
143
+ };
144
+ }
145
+ /**
146
+ * Execute a file edit operation
147
+ *
148
+ * @param input - Edit parameters including filepath, instructions, and code_edit
149
+ * @param overrides - Optional config overrides for this operation
150
+ * @returns Edit result with success status and changes
151
+ */
152
+ async execute(input, overrides) {
153
+ return executeEditFile(input, { ...this.config, ...overrides });
154
+ }
155
+ };
156
+ function generateUdiff(original, modified, filepath) {
157
+ return (0, import_diff.createTwoFilesPatch)(
158
+ filepath,
159
+ filepath,
160
+ original,
161
+ modified,
162
+ "Original",
163
+ "Modified"
164
+ );
165
+ }
166
+ function countChanges(original, modified) {
167
+ const diff = generateUdiff(original, modified, "file");
168
+ const lines = diff.split("\n");
169
+ let linesAdded = 0;
170
+ let linesRemoved = 0;
171
+ for (const line of lines) {
172
+ if (line.startsWith("+") && !line.startsWith("+++")) {
173
+ linesAdded++;
174
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
175
+ linesRemoved++;
176
+ }
177
+ }
178
+ const linesModified = Math.min(linesAdded, linesRemoved);
179
+ return {
180
+ linesAdded: linesAdded - linesModified,
181
+ linesRemoved: linesRemoved - linesModified,
182
+ linesModified
183
+ };
184
+ }
185
+ async function callMorphAPI(originalCode, codeEdit, instructions, filepath, config) {
186
+ const apiKey = config.morphApiKey || process.env.MORPH_API_KEY;
187
+ const apiUrl = config.morphApiUrl || DEFAULT_CONFIG.morphApiUrl;
188
+ const timeout = config.timeout || DEFAULT_CONFIG.timeout;
189
+ const debug = config.debug || false;
190
+ if (!apiKey) {
191
+ throw new Error(
192
+ "Morph API key not found. Set MORPH_API_KEY environment variable or pass morphApiKey in config."
193
+ );
194
+ }
195
+ const message = `<instruction>${instructions}</instruction>
196
+ <code>${originalCode}</code>
197
+ <update>${codeEdit}</update>`;
198
+ if (debug) {
199
+ console.log(`[FastApply] Calling ${apiUrl}/v1/chat/completions`);
200
+ console.log(`[FastApply] File: ${filepath}, Instructions: ${instructions.slice(0, 60)}...`);
201
+ console.log(`[FastApply] Original: ${originalCode.length} chars, Edit: ${codeEdit.length} chars`);
202
+ }
203
+ const startTime = Date.now();
204
+ const fetchPromise = fetchWithRetry(
205
+ `${apiUrl}/v1/chat/completions`,
206
+ {
207
+ method: "POST",
208
+ headers: {
209
+ "Content-Type": "application/json",
210
+ "Authorization": `Bearer ${apiKey}`
211
+ },
212
+ body: JSON.stringify({
213
+ model: "morph-v3-fast",
214
+ messages: [{ role: "user", content: message }]
215
+ })
216
+ },
217
+ config.retryConfig
218
+ );
219
+ const response = await withTimeout(
220
+ fetchPromise,
221
+ timeout,
222
+ `Morph API request timed out after ${timeout}ms`
223
+ );
224
+ if (!response.ok) {
225
+ const error = await response.text();
226
+ if (debug) console.error(`[FastApply] API error: ${response.status} - ${error}`);
227
+ throw new Error(`Morph API error (${response.status}): ${error}`);
228
+ }
229
+ const data = await response.json();
230
+ const elapsed = Date.now() - startTime;
231
+ if (debug) {
232
+ console.log(`[FastApply] \u2705 Success in ${elapsed}ms, merged: ${data.choices[0].message.content.length} chars`);
233
+ }
234
+ return data.choices[0].message.content;
235
+ }
236
+ async function executeEditFile(input, config = {}) {
237
+ const baseDir = config.baseDir || DEFAULT_CONFIG.baseDir;
238
+ const fullPath = (0, import_path.resolve)((0, import_path.join)(baseDir, input.target_filepath));
239
+ const debug = config.debug || false;
240
+ const relativePath = (0, import_path.relative)(baseDir, fullPath);
241
+ if (relativePath.startsWith("..") || fullPath === baseDir) {
242
+ return {
243
+ success: false,
244
+ filepath: input.target_filepath,
245
+ changes: { linesAdded: 0, linesRemoved: 0, linesModified: 0 },
246
+ error: `Invalid filepath: '${input.target_filepath}' is outside baseDir`
247
+ };
248
+ }
249
+ try {
250
+ if (debug) console.log(`[FastApply] Reading file: ${input.target_filepath}`);
251
+ const originalCode = await (0, import_promises.readFile)(fullPath, "utf-8");
252
+ const mergedCode = await callMorphAPI(originalCode, input.code_edit, input.instructions, input.target_filepath, config);
253
+ const udiff = config.generateUdiff !== false ? generateUdiff(originalCode, mergedCode, input.target_filepath) : void 0;
254
+ if (config.autoWrite !== false) {
255
+ await (0, import_promises.writeFile)(fullPath, mergedCode, "utf-8");
256
+ if (debug) console.log(`[FastApply] Wrote ${mergedCode.length} chars to ${input.target_filepath}`);
257
+ }
258
+ const changes = countChanges(originalCode, mergedCode);
259
+ return {
260
+ success: true,
261
+ filepath: input.target_filepath,
262
+ udiff,
263
+ changes
264
+ };
265
+ } catch (error) {
266
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
267
+ if (debug) console.error(`[FastApply] Error: ${errorMessage}`);
268
+ return {
269
+ success: false,
270
+ filepath: input.target_filepath,
271
+ changes: { linesAdded: 0, linesRemoved: 0, linesModified: 0 },
272
+ error: errorMessage
273
+ };
274
+ }
275
+ }
276
+
277
+ // tools/codebase_search/core.ts
278
+ var CodebaseSearchClient = class {
279
+ config;
280
+ constructor(config = {}) {
281
+ this.config = {
282
+ apiKey: config.apiKey,
283
+ searchUrl: process.env.MORPH_SEARCH_URL || "http://embedrerank.morphllm.com:8081",
284
+ debug: config.debug,
285
+ timeout: config.timeout || 3e4,
286
+ retryConfig: config.retryConfig
287
+ };
288
+ }
289
+ /**
290
+ * Execute a semantic code search
291
+ *
292
+ * @param input - Search parameters including query, repoId, and target directories
293
+ * @param overrides - Optional config overrides for this operation
294
+ * @returns Search results with ranked code matches
295
+ */
296
+ async search(input, overrides) {
297
+ return executeCodebaseSearch(
298
+ {
299
+ query: input.query,
300
+ target_directories: input.target_directories,
301
+ explanation: input.explanation,
302
+ limit: input.limit
303
+ },
304
+ { ...this.config, repoId: input.repoId, ...overrides }
305
+ );
306
+ }
307
+ };
308
+ async function executeCodebaseSearch(input, config) {
309
+ const apiKey = config.apiKey || process.env.MORPH_API_KEY;
310
+ if (!apiKey) {
311
+ throw new Error("MORPH_API_KEY not found. Set environment variable or pass in config");
312
+ }
313
+ const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || "http://embedrerank.morphllm.com:8081";
314
+ const timeout = config.timeout || 3e4;
315
+ const debug = config.debug || false;
316
+ if (debug) {
317
+ console.log(`[CodebaseSearch] Query: "${input.query.slice(0, 60)}..." repo=${config.repoId}`);
318
+ console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);
319
+ }
320
+ const startTime = Date.now();
321
+ try {
322
+ const fetchPromise = fetchWithRetry(
323
+ `${searchUrl}/v1/codebase_search`,
324
+ {
325
+ method: "POST",
326
+ headers: {
327
+ "Content-Type": "application/json",
328
+ "Authorization": `Bearer ${apiKey}`
329
+ },
330
+ body: JSON.stringify({
331
+ query: input.query,
332
+ repoId: config.repoId,
333
+ targetDirectories: input.target_directories || [],
334
+ limit: input.limit || 10,
335
+ candidateLimit: 50
336
+ })
337
+ },
338
+ config.retryConfig
339
+ );
340
+ const response = await withTimeout(fetchPromise, timeout, `Codebase search timed out after ${timeout}ms`);
341
+ if (!response.ok) {
342
+ const errorText = await response.text();
343
+ if (debug) console.error(`[CodebaseSearch] Error: ${response.status} - ${errorText}`);
344
+ return {
345
+ success: false,
346
+ results: [],
347
+ stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },
348
+ error: `Search failed (${response.status}): ${errorText}`
349
+ };
350
+ }
351
+ const data = await response.json();
352
+ const elapsed = Date.now() - startTime;
353
+ if (debug) {
354
+ console.log(`[CodebaseSearch] \u2705 ${data.results?.length || 0} results in ${elapsed}ms`);
355
+ }
356
+ return {
357
+ success: true,
358
+ results: data.results || [],
359
+ stats: data.stats || { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: elapsed }
360
+ };
361
+ } catch (error) {
362
+ if (debug) console.error(`[CodebaseSearch] Exception: ${error instanceof Error ? error.message : error}`);
363
+ return {
364
+ success: false,
365
+ results: [],
366
+ stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },
367
+ error: error instanceof Error ? error.message : "Unknown error"
368
+ };
369
+ }
370
+ }
371
+
372
+ // tools/browser/core.ts
373
+ var DEFAULT_CONFIG2 = {
374
+ apiUrl: process.env.MORPH_ENVIRONMENT === "DEV" ? "http://localhost:8000" : "https://browser.morphllm.com",
375
+ timeout: 12e4,
376
+ // 2 minutes for complex tasks
377
+ debug: false
378
+ };
379
+ var BrowserClient = class {
380
+ config;
381
+ constructor(config = {}) {
382
+ this.config = {
383
+ ...DEFAULT_CONFIG2,
384
+ ...config
385
+ };
386
+ }
387
+ /**
388
+ * Execute a browser automation task
389
+ */
390
+ async execute(input) {
391
+ return executeBrowserTask(input, this.config);
392
+ }
393
+ async createTask(input) {
394
+ if ("schema" in input) {
395
+ const taskInput = {
396
+ ...input,
397
+ structured_output: stringifyStructuredOutput(input.schema)
398
+ };
399
+ const result = await executeBrowserTask(taskInput, this.config);
400
+ return wrapTaskResponseWithSchema(result, this.config, input.schema);
401
+ } else {
402
+ const result = await executeBrowserTask(input, this.config);
403
+ return wrapTaskResponse(result, this.config);
404
+ }
405
+ }
406
+ /**
407
+ * Execute task with recording and wait for video to be ready
408
+ */
409
+ async executeWithRecording(input) {
410
+ return executeWithRecording(input, this.config);
411
+ }
412
+ /**
413
+ * Get recording status and URLs
414
+ */
415
+ async getRecording(recordingId) {
416
+ return getRecording(recordingId, this.config);
417
+ }
418
+ /**
419
+ * Wait for recording to complete with automatic polling
420
+ */
421
+ async waitForRecording(recordingId, options) {
422
+ return waitForRecording(recordingId, this.config, options);
423
+ }
424
+ /**
425
+ * Get errors from recording with screenshots
426
+ */
427
+ async getErrors(recordingId) {
428
+ return getErrors(recordingId, this.config);
429
+ }
430
+ /**
431
+ * Check if browser worker service is healthy
432
+ */
433
+ async checkHealth() {
434
+ return checkHealth(this.config);
435
+ }
436
+ };
437
+ async function executeBrowserTask(input, config = {}) {
438
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
439
+ const timeout = config.timeout || DEFAULT_CONFIG2.timeout;
440
+ const debug = config.debug || false;
441
+ if (!input.task || input.task.trim().length === 0) {
442
+ return { success: false, error: "Task description is required" };
443
+ }
444
+ if (input.max_steps !== void 0 && (input.max_steps < 1 || input.max_steps > 50)) {
445
+ return { success: false, error: "max_steps must be between 1 and 50" };
446
+ }
447
+ if (debug) {
448
+ console.log(`[Browser] Task: "${input.task.slice(0, 60)}..." url=${input.url || "none"} maxSteps=${input.max_steps ?? 10}`);
449
+ console.log(`[Browser] Recording: ${input.record_video ? "yes" : "no"} | Calling ${apiUrl}/browser-task`);
450
+ }
451
+ const startTime = Date.now();
452
+ try {
453
+ const headers = { "Content-Type": "application/json" };
454
+ if (config.apiKey) headers["Authorization"] = `Bearer ${config.apiKey}`;
455
+ const fetchPromise = fetchWithRetry(
456
+ `${apiUrl}/browser-task`,
457
+ {
458
+ method: "POST",
459
+ headers,
460
+ body: JSON.stringify({
461
+ task: input.task,
462
+ url: input.url,
463
+ max_steps: input.max_steps ?? 10,
464
+ model: input.model ?? "morph-computer-use-v0",
465
+ viewport_width: input.viewport_width ?? 1280,
466
+ viewport_height: input.viewport_height ?? 720,
467
+ repo_id: input.repo_id,
468
+ commit_id: input.commit_id,
469
+ record_video: input.record_video ?? false,
470
+ video_width: input.video_width ?? input.viewport_width ?? 1280,
471
+ video_height: input.video_height ?? input.viewport_height ?? 720,
472
+ structured_output: input.structured_output
473
+ })
474
+ },
475
+ config.retryConfig
476
+ );
477
+ const response = await withTimeout(
478
+ fetchPromise,
479
+ timeout,
480
+ `Browser task timed out after ${timeout}ms. Consider increasing timeout or reducing max_steps.`
481
+ );
482
+ if (!response.ok) {
483
+ const errorText = await response.text().catch(() => response.statusText);
484
+ if (debug) console.error(`[Browser] Error: ${response.status} - ${errorText}`);
485
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
486
+ }
487
+ const result = await response.json();
488
+ const elapsed = Date.now() - startTime;
489
+ if (debug) {
490
+ console.log(`[Browser] \u2705 ${result.success ? "Success" : "Failed"} in ${elapsed}ms | steps=${result.steps_taken ?? 0} recordingId=${result.recording_id ?? "none"}`);
491
+ }
492
+ return result;
493
+ } catch (error) {
494
+ if (error instanceof Error) {
495
+ if (error.message.includes("ECONNREFUSED") || error.message.includes("fetch failed")) {
496
+ return {
497
+ success: false,
498
+ error: `Cannot connect to browser worker at ${apiUrl}. Ensure the service is running.`
499
+ };
500
+ }
501
+ return {
502
+ success: false,
503
+ error: error.message
504
+ };
505
+ }
506
+ return {
507
+ success: false,
508
+ error: String(error)
509
+ };
510
+ }
511
+ }
512
+ async function getRecording(recordingId, config = {}) {
513
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
514
+ const debug = config.debug || false;
515
+ if (!config.apiKey) {
516
+ throw new Error("API key required for getRecording");
517
+ }
518
+ if (debug) console.log(`[Browser] getRecording: ${recordingId}`);
519
+ const response = await fetch(`${apiUrl}/recordings/${recordingId}`, {
520
+ method: "GET",
521
+ headers: { "Authorization": `Bearer ${config.apiKey}` }
522
+ });
523
+ if (!response.ok) {
524
+ const errorText = await response.text().catch(() => response.statusText);
525
+ if (debug) console.error(`[Browser] getRecording error: ${response.status} - ${errorText}`);
526
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
527
+ }
528
+ const recording = await response.json();
529
+ if (debug) console.log(`[Browser] Recording status: ${recording.status}`);
530
+ return recording;
531
+ }
532
+ async function waitForRecording(recordingId, config = {}, options = {}) {
533
+ const timeout = options.timeout ?? 6e4;
534
+ const pollInterval = options.pollInterval ?? 2e3;
535
+ const startTime = Date.now();
536
+ while (Date.now() - startTime < timeout) {
537
+ const status = await getRecording(recordingId, config);
538
+ if (status.status === "COMPLETED" || status.status === "ERROR") {
539
+ return status;
540
+ }
541
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
542
+ }
543
+ throw new Error(`Recording timeout after ${timeout}ms - status still pending`);
544
+ }
545
+ async function executeWithRecording(input, config = {}) {
546
+ const taskResult = await executeBrowserTask(input, config);
547
+ if (taskResult.recording_id) {
548
+ try {
549
+ const recording = await waitForRecording(
550
+ taskResult.recording_id,
551
+ config,
552
+ { timeout: 6e4, pollInterval: 2e3 }
553
+ );
554
+ return {
555
+ ...taskResult,
556
+ recording
557
+ };
558
+ } catch (error) {
559
+ return {
560
+ ...taskResult,
561
+ recording: {
562
+ id: taskResult.recording_id,
563
+ status: "ERROR",
564
+ error: error instanceof Error ? error.message : String(error),
565
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
566
+ }
567
+ };
568
+ }
569
+ }
570
+ return taskResult;
571
+ }
572
+ async function getErrors(recordingId, config = {}) {
573
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
574
+ const debug = config.debug || false;
575
+ if (!config.apiKey) {
576
+ throw new Error("API key required for getErrors");
577
+ }
578
+ if (debug) console.log(`[Browser] getErrors: ${recordingId}`);
579
+ const response = await fetch(`${apiUrl}/recordings/${recordingId}/errors`, {
580
+ method: "GET",
581
+ headers: { "Authorization": `Bearer ${config.apiKey}` }
582
+ });
583
+ if (!response.ok) {
584
+ const errorText = await response.text().catch(() => response.statusText);
585
+ if (debug) console.error(`[Browser] getErrors error: ${response.status} - ${errorText}`);
586
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
587
+ }
588
+ const errors = await response.json();
589
+ if (debug) console.log(`[Browser] Found ${errors.total_errors} errors`);
590
+ return errors;
591
+ }
592
+ function stringifyStructuredOutput(schema) {
593
+ try {
594
+ return JSON.stringify({
595
+ type: "object",
596
+ description: "Zod schema definition (Zod v3)",
597
+ zodDef: schema._def
598
+ });
599
+ } catch (error) {
600
+ console.warn("[Browser] Failed to serialize Zod schema:", error);
601
+ return JSON.stringify({
602
+ type: "object",
603
+ description: "Schema serialization failed"
604
+ });
605
+ }
606
+ }
607
+ function parseStructuredTaskOutput(result, schema) {
608
+ if (!result.output) {
609
+ return { ...result, parsed: null };
610
+ }
611
+ try {
612
+ const parsed = JSON.parse(result.output);
613
+ const validated = schema.parse(parsed);
614
+ return { ...result, parsed: validated };
615
+ } catch (error) {
616
+ if (error instanceof SyntaxError) {
617
+ return { ...result, parsed: null };
618
+ }
619
+ throw error;
620
+ }
621
+ }
622
+ async function getTaskStatus(taskId, config) {
623
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
624
+ const debug = config.debug || false;
625
+ if (debug) console.log(`[Browser] getTaskStatus: ${taskId}`);
626
+ const headers = {};
627
+ if (config.apiKey) headers["Authorization"] = `Bearer ${config.apiKey}`;
628
+ const response = await fetch(`${apiUrl}/tasks/${taskId}`, {
629
+ method: "GET",
630
+ headers
631
+ });
632
+ if (!response.ok) {
633
+ const errorText = await response.text().catch(() => response.statusText);
634
+ if (debug) console.error(`[Browser] getTaskStatus error: ${response.status} - ${errorText}`);
635
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
636
+ }
637
+ const result = await response.json();
638
+ if (debug) console.log(`[Browser] Task status: ${result.status}`);
639
+ return result;
640
+ }
641
+ function generateLiveUrl(taskId, config) {
642
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
643
+ const baseUrl = apiUrl.replace("/api", "");
644
+ return `${baseUrl}/tasks/${taskId}/live`;
645
+ }
646
+ async function pollTaskUntilComplete(taskId, config, pollConfig = {}) {
647
+ const interval = pollConfig.interval ?? 2e3;
648
+ const timeout = pollConfig.timeout ?? 3e5;
649
+ const startTime = Date.now();
650
+ while (Date.now() - startTime < timeout) {
651
+ const status = await getTaskStatus(taskId, config);
652
+ if (status.status === "completed" || status.status === "failed") {
653
+ return status;
654
+ }
655
+ await new Promise((resolve2) => setTimeout(resolve2, interval));
656
+ }
657
+ throw new Error(`Task polling timeout after ${timeout}ms`);
658
+ }
659
+ function wrapTaskResponse(result, config) {
660
+ if (!result.task_id) {
661
+ throw new Error("task_id is required to wrap response");
662
+ }
663
+ return {
664
+ ...result,
665
+ task_id: result.task_id,
666
+ liveUrl: generateLiveUrl(result.task_id, config),
667
+ complete: async (pollConfig) => {
668
+ return pollTaskUntilComplete(result.task_id, config, pollConfig);
669
+ }
670
+ };
671
+ }
672
+ function wrapTaskResponseWithSchema(result, config, schema) {
673
+ if (!result.task_id) {
674
+ throw new Error("task_id is required to wrap response");
675
+ }
676
+ const parsed = result.output ? parseStructuredTaskOutput(result, schema) : { ...result, parsed: null };
677
+ return {
678
+ ...parsed,
679
+ task_id: result.task_id,
680
+ liveUrl: generateLiveUrl(result.task_id, config),
681
+ complete: async (pollConfig) => {
682
+ const finalResult = await pollTaskUntilComplete(result.task_id, config, pollConfig);
683
+ return parseStructuredTaskOutput(finalResult, schema);
684
+ }
685
+ };
686
+ }
687
+ async function checkHealth(config = {}) {
688
+ const apiUrl = config.apiUrl || DEFAULT_CONFIG2.apiUrl;
689
+ try {
690
+ const response = await fetch(`${apiUrl}/health`, {
691
+ method: "GET",
692
+ headers: config.apiKey ? { "Authorization": `Bearer ${config.apiKey}` } : {}
693
+ });
694
+ if (!response.ok) {
695
+ throw new Error(`HTTP ${response.status}`);
696
+ }
697
+ const data = await response.json();
698
+ return {
699
+ ok: true,
700
+ google_configured: data.google_configured ?? false,
701
+ database_configured: data.database_configured ?? false,
702
+ s3_configured: data.s3_configured ?? false
703
+ };
704
+ } catch (error) {
705
+ return {
706
+ ok: false,
707
+ google_configured: false,
708
+ database_configured: false,
709
+ s3_configured: false,
710
+ error: error instanceof Error ? error.message : String(error)
711
+ };
712
+ }
713
+ }
714
+
715
+ // git/client.ts
716
+ var import_isomorphic_git = __toESM(require("isomorphic-git"), 1);
717
+ var import_node = __toESM(require("isomorphic-git/http/node"), 1);
718
+ var import_fs = __toESM(require("fs"), 1);
719
+ var DEFAULT_PROXY_URL = "https://repos.morphllm.com";
720
+ var MorphGit = class {
721
+ apiKey;
722
+ proxyUrl;
723
+ constructor(config) {
724
+ if (!config.apiKey) {
725
+ throw new Error("API key is required. Get one at https://morphllm.com/dashboard");
726
+ }
727
+ if (!config.apiKey.startsWith("sk-") && !config.apiKey.startsWith("morph-")) {
728
+ throw new Error("Invalid API key format. Expected: sk-... or morph-...");
729
+ }
730
+ this.apiKey = config.apiKey;
731
+ this.proxyUrl = config.proxyUrl || DEFAULT_PROXY_URL;
732
+ }
733
+ /**
734
+ * Get auth callback for isomorphic-git operations
735
+ * @private
736
+ */
737
+ getAuthCallback() {
738
+ return () => ({
739
+ username: "morph",
740
+ password: this.apiKey
741
+ });
742
+ }
743
+ /**
744
+ * Initialize a new repository
745
+ * Creates the repo in the database and in the git provider
746
+ *
747
+ * @example
748
+ * ```ts
749
+ * await morphGit.init({
750
+ * repoId: 'my-project',
751
+ * dir: './my-project',
752
+ * defaultBranch: 'main'
753
+ * });
754
+ * ```
755
+ */
756
+ async init(options) {
757
+ const { repoId, dir, defaultBranch = "main" } = options;
758
+ const response = await fetch(`${this.proxyUrl}/v1/repos`, {
759
+ method: "POST",
760
+ headers: {
761
+ "Authorization": `Bearer ${this.apiKey}`,
762
+ "Content-Type": "application/json"
763
+ },
764
+ body: JSON.stringify({
765
+ repoId,
766
+ name: repoId,
767
+ defaultBranch
768
+ })
769
+ });
770
+ if (!response.ok) {
771
+ const error = await response.text();
772
+ throw new Error(`Failed to create repository: ${error}`);
773
+ }
774
+ await import_isomorphic_git.default.init({
775
+ fs: import_fs.default,
776
+ dir,
777
+ defaultBranch
778
+ });
779
+ await import_isomorphic_git.default.addRemote({
780
+ fs: import_fs.default,
781
+ dir,
782
+ remote: "origin",
783
+ url: `${this.proxyUrl}/v1/repos/${repoId}`
784
+ });
785
+ console.log(`\u2713 Repository '${repoId}' initialized`);
786
+ }
787
+ /**
788
+ * Clone a repository from Morph repos
789
+ *
790
+ * @example
791
+ * ```ts
792
+ * await morphGit.clone({
793
+ * repoId: 'my-project',
794
+ * dir: './my-project'
795
+ * });
796
+ * ```
797
+ */
798
+ async clone(options) {
799
+ const { repoId, dir, branch = "main", depth, singleBranch = true } = options;
800
+ await import_isomorphic_git.default.clone({
801
+ fs: import_fs.default,
802
+ http: import_node.default,
803
+ dir,
804
+ corsProxy: this.proxyUrl,
805
+ url: `${this.proxyUrl}/v1/repos/${repoId}`,
806
+ ref: branch,
807
+ singleBranch,
808
+ depth,
809
+ onAuth: this.getAuthCallback()
810
+ });
811
+ }
812
+ /**
813
+ * Push changes to remote repository
814
+ *
815
+ * @example
816
+ * ```ts
817
+ * await morphGit.push({ dir: './my-project' });
818
+ * ```
819
+ */
820
+ async push(options) {
821
+ const { dir, remote = "origin", branch } = options;
822
+ await import_isomorphic_git.default.push({
823
+ fs: import_fs.default,
824
+ http: import_node.default,
825
+ dir,
826
+ remote,
827
+ ref: branch,
828
+ onAuth: this.getAuthCallback()
829
+ });
830
+ }
831
+ /**
832
+ * Pull changes from remote repository
833
+ *
834
+ * @example
835
+ * ```ts
836
+ * await morphGit.pull({ dir: './my-project' });
837
+ * ```
838
+ */
839
+ async pull(options) {
840
+ const { dir, remote = "origin", branch } = options;
841
+ await import_isomorphic_git.default.pull({
842
+ fs: import_fs.default,
843
+ http: import_node.default,
844
+ dir,
845
+ remote,
846
+ ref: branch,
847
+ onAuth: this.getAuthCallback(),
848
+ author: {
849
+ name: "Morph Agent",
850
+ email: "agent@morph.com"
851
+ }
852
+ });
853
+ }
854
+ /**
855
+ * Stage a file for commit
856
+ *
857
+ * @example
858
+ * ```ts
859
+ * await morphGit.add({
860
+ * dir: './my-project',
861
+ * filepath: 'src/app.ts'
862
+ * });
863
+ * ```
864
+ */
865
+ async add(options) {
866
+ const { dir, filepath } = options;
867
+ await import_isomorphic_git.default.add({
868
+ fs: import_fs.default,
869
+ dir,
870
+ filepath
871
+ });
872
+ }
873
+ /**
874
+ * Remove a file from staging
875
+ *
876
+ * @example
877
+ * ```ts
878
+ * await morphGit.remove({
879
+ * dir: './my-project',
880
+ * filepath: 'src/old-file.ts'
881
+ * });
882
+ * ```
883
+ */
884
+ async remove(options) {
885
+ const { dir, filepath } = options;
886
+ await import_isomorphic_git.default.remove({
887
+ fs: import_fs.default,
888
+ dir,
889
+ filepath
890
+ });
891
+ }
892
+ /**
893
+ * Commit staged changes
894
+ *
895
+ * @example
896
+ * ```ts
897
+ * await morphGit.commit({
898
+ * dir: './my-project',
899
+ * message: 'Add new feature',
900
+ * author: {
901
+ * name: 'AI Agent',
902
+ * email: 'ai@example.com'
903
+ * }
904
+ * });
905
+ * ```
906
+ */
907
+ async commit(options) {
908
+ const { dir, message, author } = options;
909
+ const commitAuthor = author || {
910
+ name: "Morph SDK",
911
+ email: "sdk@morphllm.com"
912
+ };
913
+ const sha = await import_isomorphic_git.default.commit({
914
+ fs: import_fs.default,
915
+ dir,
916
+ message,
917
+ author: commitAuthor
918
+ });
919
+ return sha;
920
+ }
921
+ /**
922
+ * Get status of a file
923
+ *
924
+ * @example
925
+ * ```ts
926
+ * const status = await morphGit.status({
927
+ * dir: './my-project',
928
+ * filepath: 'src/app.ts'
929
+ * });
930
+ * console.log(status); // 'modified', '*added', etc.
931
+ * ```
932
+ */
933
+ async status(options) {
934
+ const { dir, filepath } = options;
935
+ if (!filepath) {
936
+ throw new Error("filepath is required for status check");
937
+ }
938
+ const status = await import_isomorphic_git.default.status({
939
+ fs: import_fs.default,
940
+ dir,
941
+ filepath
942
+ });
943
+ return status;
944
+ }
945
+ /**
946
+ * Get commit history
947
+ *
948
+ * @example
949
+ * ```ts
950
+ * const commits = await morphGit.log({
951
+ * dir: './my-project',
952
+ * depth: 10
953
+ * });
954
+ * ```
955
+ */
956
+ async log(options) {
957
+ const { dir, depth, ref } = options;
958
+ const commits = await import_isomorphic_git.default.log({
959
+ fs: import_fs.default,
960
+ dir,
961
+ depth,
962
+ ref
963
+ });
964
+ return commits;
965
+ }
966
+ /**
967
+ * Checkout a branch or commit
968
+ *
969
+ * @example
970
+ * ```ts
971
+ * await morphGit.checkout({
972
+ * dir: './my-project',
973
+ * ref: 'feature-branch'
974
+ * });
975
+ * ```
976
+ */
977
+ async checkout(options) {
978
+ const { dir, ref } = options;
979
+ await import_isomorphic_git.default.checkout({
980
+ fs: import_fs.default,
981
+ dir,
982
+ ref
983
+ });
984
+ }
985
+ /**
986
+ * Create a new branch
987
+ *
988
+ * @example
989
+ * ```ts
990
+ * await morphGit.branch({
991
+ * dir: './my-project',
992
+ * name: 'feature-branch',
993
+ * checkout: true
994
+ * });
995
+ * ```
996
+ */
997
+ async branch(options) {
998
+ const { dir, name, checkout = false } = options;
999
+ await import_isomorphic_git.default.branch({
1000
+ fs: import_fs.default,
1001
+ dir,
1002
+ ref: name,
1003
+ checkout
1004
+ });
1005
+ }
1006
+ /**
1007
+ * List all branches
1008
+ *
1009
+ * @example
1010
+ * ```ts
1011
+ * const branches = await morphGit.listBranches({
1012
+ * dir: './my-project'
1013
+ * });
1014
+ * ```
1015
+ */
1016
+ async listBranches(options) {
1017
+ const { dir } = options;
1018
+ const branches = await import_isomorphic_git.default.listBranches({
1019
+ fs: import_fs.default,
1020
+ dir
1021
+ });
1022
+ return branches;
1023
+ }
1024
+ /**
1025
+ * Get the current branch name
1026
+ *
1027
+ * @example
1028
+ * ```ts
1029
+ * const branch = await morphGit.currentBranch({
1030
+ * dir: './my-project'
1031
+ * });
1032
+ * ```
1033
+ */
1034
+ async currentBranch(options) {
1035
+ const { dir } = options;
1036
+ const branch = await import_isomorphic_git.default.currentBranch({
1037
+ fs: import_fs.default,
1038
+ dir
1039
+ });
1040
+ return branch || void 0;
1041
+ }
1042
+ /**
1043
+ * Get list of changed files (similar to git diff --name-only)
1044
+ *
1045
+ * @example
1046
+ * ```ts
1047
+ * const changes = await morphGit.statusMatrix({
1048
+ * dir: './my-project'
1049
+ * });
1050
+ * ```
1051
+ */
1052
+ async statusMatrix(options) {
1053
+ const { dir } = options;
1054
+ const matrix = await import_isomorphic_git.default.statusMatrix({
1055
+ fs: import_fs.default,
1056
+ dir
1057
+ });
1058
+ return matrix.map(([filepath, HEADStatus, workdirStatus, stageStatus]) => {
1059
+ let status = "unmodified";
1060
+ if (HEADStatus === 1 && workdirStatus === 2 && stageStatus === 2) {
1061
+ status = "modified";
1062
+ } else if (HEADStatus === 1 && workdirStatus === 2 && stageStatus === 1) {
1063
+ status = "*modified";
1064
+ } else if (HEADStatus === 0 && workdirStatus === 2 && stageStatus === 2) {
1065
+ status = "added";
1066
+ } else if (HEADStatus === 0 && workdirStatus === 2 && stageStatus === 0) {
1067
+ status = "*added";
1068
+ } else if (HEADStatus === 1 && workdirStatus === 0 && stageStatus === 0) {
1069
+ status = "deleted";
1070
+ } else if (HEADStatus === 1 && workdirStatus === 0 && stageStatus === 1) {
1071
+ status = "*deleted";
1072
+ } else if (HEADStatus === 1 && workdirStatus === 1 && stageStatus === 1) {
1073
+ status = "unmodified";
1074
+ } else if (HEADStatus === 0 && workdirStatus === 0 && stageStatus === 0) {
1075
+ status = "absent";
1076
+ }
1077
+ return {
1078
+ filepath,
1079
+ status
1080
+ };
1081
+ });
1082
+ }
1083
+ /**
1084
+ * Get the current commit hash
1085
+ *
1086
+ * @example
1087
+ * ```ts
1088
+ * const hash = await morphGit.resolveRef({
1089
+ * dir: './my-project',
1090
+ * ref: 'HEAD'
1091
+ * });
1092
+ * ```
1093
+ */
1094
+ async resolveRef(options) {
1095
+ const { dir, ref } = options;
1096
+ const oid = await import_isomorphic_git.default.resolveRef({
1097
+ fs: import_fs.default,
1098
+ dir,
1099
+ ref
1100
+ });
1101
+ return oid;
1102
+ }
1103
+ };
1104
+
1105
+ // git/index.ts
1106
+ var import_isomorphic_git2 = __toESM(require("isomorphic-git"), 1);
1107
+ var import_node2 = __toESM(require("isomorphic-git/http/node"), 1);
1108
+
1109
+ // tools/modelrouter/core.ts
1110
+ var DEFAULT_CONFIG3 = {
1111
+ apiUrl: "https://api.morphllm.com",
1112
+ timeout: 1e4,
1113
+ // 10 seconds
1114
+ debug: false
1115
+ };
1116
+ var MODEL_MAPPINGS = {
1117
+ openai: {
1118
+ balanced: {
1119
+ easy: "gpt-5-mini",
1120
+ medium: "gpt-5-low",
1121
+ hard: "gpt-5-medium",
1122
+ "needs-info": "gpt-5-mini"
1123
+ },
1124
+ aggressive: {
1125
+ easy: "gpt-5-low",
1126
+ medium: "gpt-5-medium",
1127
+ hard: "gpt-5-high",
1128
+ "needs-info": "gpt-5-mini"
1129
+ }
1130
+ },
1131
+ anthropic: {
1132
+ balanced: {
1133
+ easy: "claude-4.5-haiku",
1134
+ medium: "claude-4.5-haiku",
1135
+ hard: "claude-4.5-sonnet",
1136
+ "needs-info": "claude-4.5-haiku"
1137
+ },
1138
+ aggressive: {
1139
+ easy: "claude-4.5-haiku",
1140
+ medium: "claude-4.5-sonnet",
1141
+ hard: "claude-4.5-sonnet",
1142
+ "needs-info": "claude-4.5-haiku"
1143
+ }
1144
+ },
1145
+ gemini: {
1146
+ balanced: {
1147
+ easy: "gemini-2.5-flash",
1148
+ medium: "gemini-2.5-flash",
1149
+ hard: "gemini-2.5-pro",
1150
+ "needs-info": "gemini-2.5-flash"
1151
+ },
1152
+ aggressive: {
1153
+ easy: "gemini-2.5-flash",
1154
+ medium: "gemini-2.5-pro",
1155
+ hard: "gemini-2.5-pro",
1156
+ "needs-info": "gemini-2.5-flash"
1157
+ }
1158
+ }
1159
+ };
1160
+ var BaseRouter = class {
1161
+ config;
1162
+ provider;
1163
+ constructor(provider, config = {}) {
1164
+ this.provider = provider;
1165
+ this.config = {
1166
+ apiKey: config.apiKey,
1167
+ apiUrl: config.apiUrl || DEFAULT_CONFIG3.apiUrl,
1168
+ timeout: config.timeout || DEFAULT_CONFIG3.timeout,
1169
+ debug: config.debug || DEFAULT_CONFIG3.debug,
1170
+ retryConfig: config.retryConfig
1171
+ };
1172
+ }
1173
+ /**
1174
+ * Map backend complexity classification to actual model name
1175
+ */
1176
+ mapComplexityToModel(complexity, mode) {
1177
+ const mapping = MODEL_MAPPINGS[this.provider][mode];
1178
+ return mapping[complexity];
1179
+ }
1180
+ /**
1181
+ * Select the optimal model for a given input and mode
1182
+ */
1183
+ async selectModel(input) {
1184
+ const mode = input.mode || "balanced";
1185
+ const apiKey = this.config.apiKey || process.env.MORPH_API_KEY;
1186
+ if (!apiKey) {
1187
+ throw new Error(
1188
+ "Morph API key is required. Set MORPH_API_KEY environment variable or pass apiKey in config."
1189
+ );
1190
+ }
1191
+ const url = `${this.config.apiUrl}/router/${this.provider}`;
1192
+ const payload = {
1193
+ input: input.input,
1194
+ mode
1195
+ };
1196
+ if (this.config.debug) {
1197
+ console.log(`[ModelRouter] Requesting ${this.provider} model selection:`, {
1198
+ mode,
1199
+ inputLength: input.input.length
1200
+ });
1201
+ }
1202
+ try {
1203
+ const fetchPromise = fetchWithRetry(
1204
+ url,
1205
+ {
1206
+ method: "POST",
1207
+ headers: {
1208
+ "Content-Type": "application/json",
1209
+ Authorization: `Bearer ${apiKey}`
1210
+ },
1211
+ body: JSON.stringify(payload)
1212
+ },
1213
+ this.config.retryConfig
1214
+ );
1215
+ const response = await withTimeout(
1216
+ fetchPromise,
1217
+ this.config.timeout,
1218
+ `Router API request timed out after ${this.config.timeout}ms`
1219
+ );
1220
+ if (!response.ok) {
1221
+ const errorText = await response.text();
1222
+ throw new Error(
1223
+ `Router API error (${response.status}): ${errorText || response.statusText}`
1224
+ );
1225
+ }
1226
+ const apiResult = await response.json();
1227
+ const actualModel = this.mapComplexityToModel(apiResult.model, mode);
1228
+ const result = {
1229
+ model: actualModel,
1230
+ reasoning: apiResult.reasoning
1231
+ };
1232
+ if (this.config.debug) {
1233
+ console.log(`[ModelRouter] Complexity: ${apiResult.model}, Selected model: ${actualModel}`);
1234
+ }
1235
+ return result;
1236
+ } catch (error) {
1237
+ if (this.config.debug) {
1238
+ console.error(`[ModelRouter] Error selecting model:`, error);
1239
+ }
1240
+ throw error;
1241
+ }
1242
+ }
1243
+ };
1244
+ var OpenAIRouter = class extends BaseRouter {
1245
+ constructor(config = {}) {
1246
+ super("openai", config);
1247
+ }
1248
+ /**
1249
+ * Select optimal GPT-5 model
1250
+ *
1251
+ * @param input - User input and mode
1252
+ * @returns Selected model name (gpt-5-mini | gpt-5-low | gpt-5-medium | gpt-5-high)
1253
+ */
1254
+ async selectModel(input) {
1255
+ return super.selectModel(input);
1256
+ }
1257
+ };
1258
+ var AnthropicRouter = class extends BaseRouter {
1259
+ constructor(config = {}) {
1260
+ super("anthropic", config);
1261
+ }
1262
+ /**
1263
+ * Select optimal Claude model
1264
+ *
1265
+ * @param input - User input and mode
1266
+ * @returns Selected model name (claude-4.5-haiku | claude-4.5-sonnet)
1267
+ */
1268
+ async selectModel(input) {
1269
+ return super.selectModel(input);
1270
+ }
1271
+ };
1272
+ var GeminiRouter = class extends BaseRouter {
1273
+ constructor(config = {}) {
1274
+ super("gemini", config);
1275
+ }
1276
+ /**
1277
+ * Select optimal Gemini model
1278
+ *
1279
+ * @param input - User input and mode
1280
+ * @returns Selected model name (gemini-2.5-flash | gemini-2.5-pro)
1281
+ */
1282
+ async selectModel(input) {
1283
+ return super.selectModel(input);
1284
+ }
1285
+ };
1286
+
1287
+ // client.ts
1288
+ var MorphClient = class {
1289
+ /** Client configuration */
1290
+ config;
1291
+ /** FastApply tool for editing files with AI-powered merge */
1292
+ fastApply;
1293
+ /** CodebaseSearch tool for semantic code search */
1294
+ codebaseSearch;
1295
+ /** Browser tool for AI-powered browser automation */
1296
+ browser;
1297
+ /** Git tool for version control operations */
1298
+ git;
1299
+ /** Model routers for intelligent model selection */
1300
+ routers;
1301
+ /**
1302
+ * Create a new Morph SDK client
1303
+ *
1304
+ * @param config - Client configuration (apiKey, debug, timeout, retryConfig)
1305
+ *
1306
+ * @example
1307
+ * ```typescript
1308
+ * const morph = new MorphClient({
1309
+ * apiKey: process.env.MORPH_API_KEY,
1310
+ * debug: true,
1311
+ * timeout: 60000
1312
+ * });
1313
+ * ```
1314
+ */
1315
+ constructor(config = {}) {
1316
+ this.config = config;
1317
+ this.fastApply = new FastApplyClient({
1318
+ apiKey: config.apiKey,
1319
+ debug: config.debug,
1320
+ timeout: config.timeout,
1321
+ retryConfig: config.retryConfig
1322
+ });
1323
+ this.codebaseSearch = new CodebaseSearchClient({
1324
+ apiKey: config.apiKey,
1325
+ debug: config.debug,
1326
+ timeout: config.timeout,
1327
+ retryConfig: config.retryConfig
1328
+ });
1329
+ this.browser = new BrowserClient({
1330
+ apiKey: config.apiKey,
1331
+ debug: config.debug,
1332
+ timeout: config.timeout,
1333
+ retryConfig: config.retryConfig
1334
+ });
1335
+ this.git = new MorphGit({
1336
+ apiKey: config.apiKey,
1337
+ retryConfig: config.retryConfig
1338
+ });
1339
+ this.routers = {
1340
+ openai: new OpenAIRouter({
1341
+ apiKey: config.apiKey,
1342
+ debug: config.debug,
1343
+ timeout: config.timeout,
1344
+ retryConfig: config.retryConfig
1345
+ }),
1346
+ anthropic: new AnthropicRouter({
1347
+ apiKey: config.apiKey,
1348
+ debug: config.debug,
1349
+ timeout: config.timeout,
1350
+ retryConfig: config.retryConfig
1351
+ }),
1352
+ gemini: new GeminiRouter({
1353
+ apiKey: config.apiKey,
1354
+ debug: config.debug,
1355
+ timeout: config.timeout,
1356
+ retryConfig: config.retryConfig
1357
+ })
1358
+ };
1359
+ }
1360
+ };
1361
+ // Annotate the CommonJS export names for ESM import in node:
1362
+ 0 && (module.exports = {
1363
+ AnthropicRouter,
1364
+ BrowserClient,
1365
+ CodebaseSearchClient,
1366
+ FastApplyClient,
1367
+ GeminiRouter,
1368
+ MorphClient,
1369
+ MorphGit,
1370
+ OpenAIRouter
1371
+ });
1372
+ //# sourceMappingURL=index.cjs.map