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