@morphllm/morphsdk 0.2.39 → 0.2.41
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/dist/{chunk-NCVWIW7L.js → chunk-EYHXBQQX.js} +7 -81
- package/dist/chunk-EYHXBQQX.js.map +1 -0
- package/dist/{chunk-YSBSDU75.js → chunk-FP7INILQ.js} +2 -2
- package/dist/{chunk-OKCGU7UD.js → chunk-GDR65N2J.js} +61 -2
- package/dist/chunk-GDR65N2J.js.map +1 -0
- package/dist/{chunk-W5CHJ6OX.js → chunk-GGBB2HDM.js} +2 -2
- package/dist/{chunk-LN4CTQZG.js → chunk-GU3SULV5.js} +2 -2
- package/dist/{chunk-Y2IY7NYY.js → chunk-O5DA5V5S.js} +2 -2
- package/dist/{chunk-4A7UBGLS.js → chunk-OUEJ6XEO.js} +2 -2
- package/dist/chunk-SQN4DUQS.js +84 -0
- package/dist/chunk-SQN4DUQS.js.map +1 -0
- package/dist/{chunk-EAA7D24N.js → chunk-W3XLPMV3.js} +7 -7
- package/dist/{chunk-I7WWUHEC.js → chunk-W7NRFNLI.js} +4 -4
- package/dist/{chunk-AG3ICTC5.js → chunk-WM77HRKO.js} +4 -7
- package/dist/chunk-WM77HRKO.js.map +1 -0
- package/dist/{chunk-WHZQDTM6.js → chunk-ZRLEAPZV.js} +2 -2
- package/dist/client.cjs +63 -7
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +5 -4
- package/dist/git/client.cjs +60 -1
- package/dist/git/client.cjs.map +1 -1
- package/dist/git/client.d.ts +15 -1
- package/dist/git/client.js +1 -1
- package/dist/git/index.cjs +60 -1
- package/dist/git/index.cjs.map +1 -1
- package/dist/git/index.js +1 -1
- package/dist/git/types.cjs.map +1 -1
- package/dist/git/types.d.ts +14 -1
- package/dist/index.cjs +63 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -4
- package/dist/tools/browser/anthropic.js +2 -1
- package/dist/tools/browser/anthropic.js.map +1 -1
- package/dist/tools/browser/core.js +2 -1
- package/dist/tools/browser/index.d.ts +2 -69
- package/dist/tools/browser/index.js +7 -5
- package/dist/tools/browser/live.cjs +112 -0
- package/dist/tools/browser/live.cjs.map +1 -0
- package/dist/tools/browser/live.d.ts +75 -0
- package/dist/tools/browser/live.js +16 -0
- package/dist/tools/browser/live.js.map +1 -0
- package/dist/tools/browser/openai.js +2 -1
- package/dist/tools/browser/openai.js.map +1 -1
- package/dist/tools/browser/vercel.js +2 -1
- package/dist/tools/browser/vercel.js.map +1 -1
- package/dist/tools/codebase_search/anthropic.cjs +2 -5
- package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
- package/dist/tools/codebase_search/anthropic.js +2 -2
- package/dist/tools/codebase_search/core.cjs +3 -6
- package/dist/tools/codebase_search/core.cjs.map +1 -1
- package/dist/tools/codebase_search/core.js +1 -1
- package/dist/tools/codebase_search/index.cjs +2 -5
- package/dist/tools/codebase_search/index.cjs.map +1 -1
- package/dist/tools/codebase_search/index.js +6 -6
- package/dist/tools/codebase_search/openai.cjs +2 -5
- package/dist/tools/codebase_search/openai.cjs.map +1 -1
- package/dist/tools/codebase_search/openai.js +2 -2
- package/dist/tools/codebase_search/vercel.cjs +2 -5
- package/dist/tools/codebase_search/vercel.cjs.map +1 -1
- package/dist/tools/codebase_search/vercel.js +2 -2
- package/dist/tools/warp_grep/agent/runner.js +3 -3
- package/dist/tools/warp_grep/anthropic.js +4 -4
- package/dist/tools/warp_grep/index.js +6 -6
- package/dist/tools/warp_grep/openai.js +4 -4
- package/dist/tools/warp_grep/vercel.js +4 -4
- package/package.json +2 -3
- package/dist/chunk-AG3ICTC5.js.map +0 -1
- package/dist/chunk-NCVWIW7L.js.map +0 -1
- package/dist/chunk-OKCGU7UD.js.map +0 -1
- /package/dist/{chunk-YSBSDU75.js.map → chunk-FP7INILQ.js.map} +0 -0
- /package/dist/{chunk-W5CHJ6OX.js.map → chunk-GGBB2HDM.js.map} +0 -0
- /package/dist/{chunk-LN4CTQZG.js.map → chunk-GU3SULV5.js.map} +0 -0
- /package/dist/{chunk-Y2IY7NYY.js.map → chunk-O5DA5V5S.js.map} +0 -0
- /package/dist/{chunk-4A7UBGLS.js.map → chunk-OUEJ6XEO.js.map} +0 -0
- /package/dist/{chunk-EAA7D24N.js.map → chunk-W3XLPMV3.js.map} +0 -0
- /package/dist/{chunk-I7WWUHEC.js.map → chunk-W7NRFNLI.js.map} +0 -0
- /package/dist/{chunk-WHZQDTM6.js.map → chunk-ZRLEAPZV.js.map} +0 -0
|
@@ -1,72 +1,5 @@
|
|
|
1
1
|
export { BrowserClient, checkHealth, executeBrowserTask, executeWithRecording, getErrors, getRecording, waitForRecording } from './core.js';
|
|
2
|
-
|
|
3
|
-
export { BrowserConfig, BrowserError, BrowserModel, BrowserTaskInput, BrowserTaskInputWithSchema, BrowserTaskResult, BrowserTaskWithPromise, BrowserTaskWithPromiseAndSchema, ErrorsResponse, RecordingStatus } from './types.js';
|
|
2
|
+
export { BrowserConfig, BrowserError, BrowserModel, BrowserTaskInput, BrowserTaskInputWithSchema, BrowserTaskResult, BrowserTaskWithPromise, BrowserTaskWithPromiseAndSchema, ErrorsResponse, IframeOptions, LiveSessionOptions, RecordingStatus } from './types.js';
|
|
4
3
|
export { BROWSER_SYSTEM_PROMPT, BROWSER_TOOL_DESCRIPTION } from './prompts.js';
|
|
4
|
+
export { LIVE_PRESETS, buildEmbedCode, buildLiveIframe, buildLiveUrl } from './live.js';
|
|
5
5
|
import '../utils/resilience.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Live session utilities for Morph browser sessions
|
|
9
|
-
*
|
|
10
|
-
* Provides helpers for embedding and sharing live browser sessions with WebRTC streaming.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Preset configurations for common use cases
|
|
15
|
-
*/
|
|
16
|
-
declare const LIVE_PRESETS: {
|
|
17
|
-
/** Read-only monitoring (no interaction) */
|
|
18
|
-
readonly readonly: LiveSessionOptions;
|
|
19
|
-
/** Interactive control (human-in-the-loop) */
|
|
20
|
-
readonly interactive: LiveSessionOptions;
|
|
21
|
-
/** Watch-only without controls */
|
|
22
|
-
readonly monitoring: LiveSessionOptions;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Build a live session URL with query parameters
|
|
26
|
-
*
|
|
27
|
-
* @param debugUrl - Live session debug URL (e.g., from task.debugUrl)
|
|
28
|
-
* @param options - Live session configuration options
|
|
29
|
-
* @returns URL with query parameters for iframe embedding
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```typescript
|
|
33
|
-
* const url = buildLiveUrl(task.debugUrl, { interactive: true });
|
|
34
|
-
* // Returns: https://example.com/sessions/abc?interactive=true
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
declare function buildLiveUrl(debugUrl: string, options?: LiveSessionOptions): string;
|
|
38
|
-
/**
|
|
39
|
-
* Build iframe HTML for embedding a live session
|
|
40
|
-
*
|
|
41
|
-
* @param debugUrl - Live session debug URL
|
|
42
|
-
* @param options - Iframe configuration including dimensions and session options
|
|
43
|
-
* @returns HTML iframe element as string
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* const iframe = buildLiveIframe(task.debugUrl, {
|
|
48
|
-
* interactive: true,
|
|
49
|
-
* width: '100%',
|
|
50
|
-
* height: '600px'
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
declare function buildLiveIframe(debugUrl: string, options?: IframeOptions): string;
|
|
55
|
-
/**
|
|
56
|
-
* Build complete embed code with HTML snippet
|
|
57
|
-
*
|
|
58
|
-
* @param debugUrl - Live session debug URL
|
|
59
|
-
* @param options - Iframe configuration
|
|
60
|
-
* @returns Multi-line HTML snippet ready to copy-paste
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* const code = buildEmbedCode(task.debugUrl, { interactive: false });
|
|
65
|
-
* console.log(code);
|
|
66
|
-
* // <!-- Embed Morph Live Session -->
|
|
67
|
-
* // <iframe src="..." style="..."></iframe>
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
declare function buildEmbedCode(debugUrl: string, options?: IframeOptions): string;
|
|
71
|
-
|
|
72
|
-
export { IframeOptions, LIVE_PRESETS, LiveSessionOptions, buildEmbedCode, buildLiveIframe, buildLiveUrl };
|
|
@@ -4,17 +4,19 @@ import {
|
|
|
4
4
|
} from "../../chunk-EI4UKP24.js";
|
|
5
5
|
import {
|
|
6
6
|
BrowserClient,
|
|
7
|
-
LIVE_PRESETS,
|
|
8
|
-
buildEmbedCode,
|
|
9
|
-
buildLiveIframe,
|
|
10
|
-
buildLiveUrl,
|
|
11
7
|
checkHealth,
|
|
12
8
|
executeBrowserTask,
|
|
13
9
|
executeWithRecording,
|
|
14
10
|
getErrors,
|
|
15
11
|
getRecording,
|
|
16
12
|
waitForRecording
|
|
17
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-EYHXBQQX.js";
|
|
14
|
+
import {
|
|
15
|
+
LIVE_PRESETS,
|
|
16
|
+
buildEmbedCode,
|
|
17
|
+
buildLiveIframe,
|
|
18
|
+
buildLiveUrl
|
|
19
|
+
} from "../../chunk-SQN4DUQS.js";
|
|
18
20
|
import "../../chunk-4VWJFZVS.js";
|
|
19
21
|
import "../../chunk-PZ5AY32C.js";
|
|
20
22
|
export {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// tools/browser/live.ts
|
|
21
|
+
var live_exports = {};
|
|
22
|
+
__export(live_exports, {
|
|
23
|
+
LIVE_PRESETS: () => LIVE_PRESETS,
|
|
24
|
+
buildEmbedCode: () => buildEmbedCode,
|
|
25
|
+
buildLiveIframe: () => buildLiveIframe,
|
|
26
|
+
buildLiveUrl: () => buildLiveUrl,
|
|
27
|
+
resolvePreset: () => resolvePreset
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(live_exports);
|
|
30
|
+
var LIVE_PRESETS = {
|
|
31
|
+
/** Read-only monitoring (no interaction) */
|
|
32
|
+
readonly: { interactive: false },
|
|
33
|
+
/** Interactive control (human-in-the-loop) */
|
|
34
|
+
interactive: { interactive: true },
|
|
35
|
+
/** Watch-only without controls */
|
|
36
|
+
monitoring: { interactive: false, showControls: false }
|
|
37
|
+
};
|
|
38
|
+
function buildLiveUrl(debugUrl, options = {}) {
|
|
39
|
+
if (!debugUrl) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"debugUrl is required. Ensure your backend returns debugUrl in the task response. Contact support@morphllm.com if you need help."
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const url = new URL(debugUrl);
|
|
45
|
+
if (options.interactive !== void 0) {
|
|
46
|
+
url.searchParams.set("interactive", String(options.interactive));
|
|
47
|
+
}
|
|
48
|
+
if (options.theme) {
|
|
49
|
+
url.searchParams.set("theme", options.theme);
|
|
50
|
+
}
|
|
51
|
+
if (options.showControls !== void 0) {
|
|
52
|
+
url.searchParams.set("showControls", String(options.showControls));
|
|
53
|
+
}
|
|
54
|
+
if (options.pageId) {
|
|
55
|
+
url.searchParams.set("pageId", options.pageId);
|
|
56
|
+
}
|
|
57
|
+
if (options.pageIndex) {
|
|
58
|
+
url.searchParams.set("pageIndex", options.pageIndex);
|
|
59
|
+
}
|
|
60
|
+
return url.toString();
|
|
61
|
+
}
|
|
62
|
+
function buildLiveIframe(debugUrl, options = {}) {
|
|
63
|
+
const {
|
|
64
|
+
width = "100%",
|
|
65
|
+
height = "600px",
|
|
66
|
+
style = "",
|
|
67
|
+
className = "",
|
|
68
|
+
...sessionOptions
|
|
69
|
+
} = options;
|
|
70
|
+
const src = buildLiveUrl(debugUrl, sessionOptions);
|
|
71
|
+
const widthStr = typeof width === "number" ? `${width}px` : width;
|
|
72
|
+
const heightStr = typeof height === "number" ? `${height}px` : height;
|
|
73
|
+
const baseStyle = `width: ${widthStr}; height: ${heightStr}; border: none;`;
|
|
74
|
+
const fullStyle = style ? `${baseStyle} ${style}` : baseStyle;
|
|
75
|
+
const attributes = [
|
|
76
|
+
`src="${src}"`,
|
|
77
|
+
`style="${fullStyle}"`
|
|
78
|
+
];
|
|
79
|
+
if (className) {
|
|
80
|
+
attributes.push(`class="${className}"`);
|
|
81
|
+
}
|
|
82
|
+
return `<iframe ${attributes.join(" ")}></iframe>`;
|
|
83
|
+
}
|
|
84
|
+
function buildEmbedCode(debugUrl, options = {}) {
|
|
85
|
+
const iframe = buildLiveIframe(debugUrl, options);
|
|
86
|
+
return `<!-- Embed Morph Live Session -->
|
|
87
|
+
${iframe}`;
|
|
88
|
+
}
|
|
89
|
+
function resolvePreset(optionsOrPreset) {
|
|
90
|
+
if (!optionsOrPreset) {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
if (typeof optionsOrPreset === "string") {
|
|
94
|
+
const preset = LIVE_PRESETS[optionsOrPreset];
|
|
95
|
+
if (!preset) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Unknown preset: ${optionsOrPreset}. Available presets: ${Object.keys(LIVE_PRESETS).join(", ")}`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return preset;
|
|
101
|
+
}
|
|
102
|
+
return optionsOrPreset;
|
|
103
|
+
}
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
LIVE_PRESETS,
|
|
107
|
+
buildEmbedCode,
|
|
108
|
+
buildLiveIframe,
|
|
109
|
+
buildLiveUrl,
|
|
110
|
+
resolvePreset
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=live.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../tools/browser/live.ts"],"sourcesContent":["/**\n * Live session utilities for Morph browser sessions\n * \n * Provides helpers for embedding and sharing live browser sessions with WebRTC streaming.\n */\n\nimport type { LiveSessionOptions, IframeOptions } from './types.js';\n\n/**\n * Preset configurations for common use cases\n */\nexport const LIVE_PRESETS = {\n /** Read-only monitoring (no interaction) */\n readonly: { interactive: false } as LiveSessionOptions,\n /** Interactive control (human-in-the-loop) */\n interactive: { interactive: true } as LiveSessionOptions,\n /** Watch-only without controls */\n monitoring: { interactive: false, showControls: false } as LiveSessionOptions,\n} as const;\n\n/**\n * Build a live session URL with query parameters\n * \n * @param debugUrl - Live session debug URL (e.g., from task.debugUrl)\n * @param options - Live session configuration options\n * @returns URL with query parameters for iframe embedding\n * \n * @example\n * ```typescript\n * const url = buildLiveUrl(task.debugUrl, { interactive: true });\n * // Returns: https://example.com/sessions/abc?interactive=true\n * ```\n */\nexport function buildLiveUrl(\n debugUrl: string,\n options: LiveSessionOptions = {}\n): string {\n if (!debugUrl) {\n throw new Error(\n 'debugUrl is required. Ensure your backend returns debugUrl in the task response. ' +\n 'Contact support@morphllm.com if you need help.'\n );\n } \n\n const url = new URL(debugUrl);\n \n // Add query parameters for supported options\n if (options.interactive !== undefined) {\n url.searchParams.set('interactive', String(options.interactive));\n }\n \n if (options.theme) {\n url.searchParams.set('theme', options.theme);\n }\n \n if (options.showControls !== undefined) {\n url.searchParams.set('showControls', String(options.showControls));\n }\n \n if (options.pageId) {\n url.searchParams.set('pageId', options.pageId);\n }\n \n if (options.pageIndex) {\n url.searchParams.set('pageIndex', options.pageIndex);\n }\n \n return url.toString();\n}\n\n/**\n * Build iframe HTML for embedding a live session\n * \n * @param debugUrl - Live session debug URL\n * @param options - Iframe configuration including dimensions and session options\n * @returns HTML iframe element as string\n * \n * @example\n * ```typescript\n * const iframe = buildLiveIframe(task.debugUrl, {\n * interactive: true,\n * width: '100%',\n * height: '600px'\n * });\n * ```\n */\nexport function buildLiveIframe(\n debugUrl: string,\n options: IframeOptions = {}\n): string {\n const {\n width = '100%',\n height = '600px',\n style = '',\n className = '',\n ...sessionOptions\n } = options;\n\n const src = buildLiveUrl(debugUrl, sessionOptions);\n \n // Convert numeric dimensions to pixels\n const widthStr = typeof width === 'number' ? `${width}px` : width;\n const heightStr = typeof height === 'number' ? `${height}px` : height;\n \n // Build style attribute\n const baseStyle = `width: ${widthStr}; height: ${heightStr}; border: none;`;\n const fullStyle = style ? `${baseStyle} ${style}` : baseStyle;\n \n // Build iframe attributes\n const attributes = [\n `src=\"${src}\"`,\n `style=\"${fullStyle}\"`,\n ];\n \n if (className) {\n attributes.push(`class=\"${className}\"`);\n }\n \n return `<iframe ${attributes.join(' ')}></iframe>`;\n}\n\n/**\n * Build complete embed code with HTML snippet\n * \n * @param debugUrl - Live session debug URL\n * @param options - Iframe configuration\n * @returns Multi-line HTML snippet ready to copy-paste\n * \n * @example\n * ```typescript\n * const code = buildEmbedCode(task.debugUrl, { interactive: false });\n * console.log(code);\n * // <!-- Embed Morph Live Session -->\n * // <iframe src=\"...\" style=\"...\"></iframe>\n * ```\n */\nexport function buildEmbedCode(\n debugUrl: string,\n options: IframeOptions = {}\n): string {\n const iframe = buildLiveIframe(debugUrl, options);\n return `<!-- Embed Morph Live Session -->\\n${iframe}`;\n}\n\n/**\n * Get live session options from preset name or custom config\n * \n * @internal\n */\nexport function resolvePreset(\n optionsOrPreset?: string | IframeOptions\n): IframeOptions {\n if (!optionsOrPreset) {\n return {};\n }\n \n if (typeof optionsOrPreset === 'string') {\n const preset = LIVE_PRESETS[optionsOrPreset as keyof typeof LIVE_PRESETS];\n if (!preset) {\n throw new Error(\n `Unknown preset: ${optionsOrPreset}. Available presets: ${Object.keys(LIVE_PRESETS).join(', ')}`\n );\n }\n return preset;\n }\n \n return optionsOrPreset;\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWO,IAAM,eAAe;AAAA;AAAA,EAE1B,UAAU,EAAE,aAAa,MAAM;AAAA;AAAA,EAE/B,aAAa,EAAE,aAAa,KAAK;AAAA;AAAA,EAEjC,YAAY,EAAE,aAAa,OAAO,cAAc,MAAM;AACxD;AAeO,SAAS,aACd,UACA,UAA8B,CAAC,GACvB;AACR,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,IAAI,QAAQ;AAG5B,MAAI,QAAQ,gBAAgB,QAAW;AACrC,QAAI,aAAa,IAAI,eAAe,OAAO,QAAQ,WAAW,CAAC;AAAA,EACjE;AAEA,MAAI,QAAQ,OAAO;AACjB,QAAI,aAAa,IAAI,SAAS,QAAQ,KAAK;AAAA,EAC7C;AAEA,MAAI,QAAQ,iBAAiB,QAAW;AACtC,QAAI,aAAa,IAAI,gBAAgB,OAAO,QAAQ,YAAY,CAAC;AAAA,EACnE;AAEA,MAAI,QAAQ,QAAQ;AAClB,QAAI,aAAa,IAAI,UAAU,QAAQ,MAAM;AAAA,EAC/C;AAEA,MAAI,QAAQ,WAAW;AACrB,QAAI,aAAa,IAAI,aAAa,QAAQ,SAAS;AAAA,EACrD;AAEA,SAAO,IAAI,SAAS;AACtB;AAkBO,SAAS,gBACd,UACA,UAAyB,CAAC,GAClB;AACR,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,MAAM,aAAa,UAAU,cAAc;AAGjD,QAAM,WAAW,OAAO,UAAU,WAAW,GAAG,KAAK,OAAO;AAC5D,QAAM,YAAY,OAAO,WAAW,WAAW,GAAG,MAAM,OAAO;AAG/D,QAAM,YAAY,UAAU,QAAQ,aAAa,SAAS;AAC1D,QAAM,YAAY,QAAQ,GAAG,SAAS,IAAI,KAAK,KAAK;AAGpD,QAAM,aAAa;AAAA,IACjB,QAAQ,GAAG;AAAA,IACX,UAAU,SAAS;AAAA,EACrB;AAEA,MAAI,WAAW;AACb,eAAW,KAAK,UAAU,SAAS,GAAG;AAAA,EACxC;AAEA,SAAO,WAAW,WAAW,KAAK,GAAG,CAAC;AACxC;AAiBO,SAAS,eACd,UACA,UAAyB,CAAC,GAClB;AACR,QAAM,SAAS,gBAAgB,UAAU,OAAO;AAChD,SAAO;AAAA,EAAsC,MAAM;AACrD;AAOO,SAAS,cACd,iBACe;AACf,MAAI,CAAC,iBAAiB;AACpB,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,oBAAoB,UAAU;AACvC,UAAM,SAAS,aAAa,eAA4C;AACxE,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,eAAe,wBAAwB,OAAO,KAAK,YAAY,EAAE,KAAK,IAAI,CAAC;AAAA,MAChG;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { LiveSessionOptions, IframeOptions } from './types.js';
|
|
2
|
+
import '../utils/resilience.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Live session utilities for Morph browser sessions
|
|
6
|
+
*
|
|
7
|
+
* Provides helpers for embedding and sharing live browser sessions with WebRTC streaming.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Preset configurations for common use cases
|
|
12
|
+
*/
|
|
13
|
+
declare const LIVE_PRESETS: {
|
|
14
|
+
/** Read-only monitoring (no interaction) */
|
|
15
|
+
readonly readonly: LiveSessionOptions;
|
|
16
|
+
/** Interactive control (human-in-the-loop) */
|
|
17
|
+
readonly interactive: LiveSessionOptions;
|
|
18
|
+
/** Watch-only without controls */
|
|
19
|
+
readonly monitoring: LiveSessionOptions;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Build a live session URL with query parameters
|
|
23
|
+
*
|
|
24
|
+
* @param debugUrl - Live session debug URL (e.g., from task.debugUrl)
|
|
25
|
+
* @param options - Live session configuration options
|
|
26
|
+
* @returns URL with query parameters for iframe embedding
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const url = buildLiveUrl(task.debugUrl, { interactive: true });
|
|
31
|
+
* // Returns: https://example.com/sessions/abc?interactive=true
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function buildLiveUrl(debugUrl: string, options?: LiveSessionOptions): string;
|
|
35
|
+
/**
|
|
36
|
+
* Build iframe HTML for embedding a live session
|
|
37
|
+
*
|
|
38
|
+
* @param debugUrl - Live session debug URL
|
|
39
|
+
* @param options - Iframe configuration including dimensions and session options
|
|
40
|
+
* @returns HTML iframe element as string
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const iframe = buildLiveIframe(task.debugUrl, {
|
|
45
|
+
* interactive: true,
|
|
46
|
+
* width: '100%',
|
|
47
|
+
* height: '600px'
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function buildLiveIframe(debugUrl: string, options?: IframeOptions): string;
|
|
52
|
+
/**
|
|
53
|
+
* Build complete embed code with HTML snippet
|
|
54
|
+
*
|
|
55
|
+
* @param debugUrl - Live session debug URL
|
|
56
|
+
* @param options - Iframe configuration
|
|
57
|
+
* @returns Multi-line HTML snippet ready to copy-paste
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const code = buildEmbedCode(task.debugUrl, { interactive: false });
|
|
62
|
+
* console.log(code);
|
|
63
|
+
* // <!-- Embed Morph Live Session -->
|
|
64
|
+
* // <iframe src="..." style="..."></iframe>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function buildEmbedCode(debugUrl: string, options?: IframeOptions): string;
|
|
68
|
+
/**
|
|
69
|
+
* Get live session options from preset name or custom config
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
declare function resolvePreset(optionsOrPreset?: string | IframeOptions): IframeOptions;
|
|
74
|
+
|
|
75
|
+
export { LIVE_PRESETS, buildEmbedCode, buildLiveIframe, buildLiveUrl, resolvePreset };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LIVE_PRESETS,
|
|
3
|
+
buildEmbedCode,
|
|
4
|
+
buildLiveIframe,
|
|
5
|
+
buildLiveUrl,
|
|
6
|
+
resolvePreset
|
|
7
|
+
} from "../../chunk-SQN4DUQS.js";
|
|
8
|
+
import "../../chunk-PZ5AY32C.js";
|
|
9
|
+
export {
|
|
10
|
+
LIVE_PRESETS,
|
|
11
|
+
buildEmbedCode,
|
|
12
|
+
buildLiveIframe,
|
|
13
|
+
buildLiveUrl,
|
|
14
|
+
resolvePreset
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../tools/browser/openai.ts"],"sourcesContent":["/**\n * OpenAI SDK adapter for browser automation tool\n */\n\nimport type { ChatCompletionTool } from 'openai/resources/chat/completions.mjs';\nimport { executeBrowserTask } from './core.js';\nimport type {\n BrowserConfig,\n BrowserTaskInput,\n BrowserTaskResult,\n} from './types.js';\nimport { BROWSER_TOOL_DESCRIPTION, BROWSER_SYSTEM_PROMPT } from './prompts.js';\n\n/**\n * OpenAI tool definition for browser automation\n */\nexport const browserTool: ChatCompletionTool = {\n type: 'function',\n function: {\n name: 'browser_task',\n description: BROWSER_TOOL_DESCRIPTION,\n parameters: {\n type: 'object',\n properties: {\n task: {\n type: 'string',\n description: 'Natural language description of what to do (e.g., \"Test checkout flow for buying a pineapple\")',\n },\n url: {\n type: 'string',\n description: 'Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page.',\n },\n max_steps: {\n type: 'number',\n description: 'Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.',\n default: 10,\n },\n region: {\n type: 'string',\n enum: ['sfo', 'lon'],\n description: 'Browserless region: sfo (US West Coast) or lon (Europe). Default: sfo.',\n default: 'sfo',\n },\n },\n required: ['task'],\n },\n },\n};\n\n/**\n * Execute a browser task (for use in tool call handling)\n * \n * @param input - Tool input parameters (may be JSON string)\n * @param config - Optional browser worker configuration\n * @returns Task execution result\n */\nexport async function execute(\n input: BrowserTaskInput | string,\n config?: BrowserConfig\n): Promise<BrowserTaskResult> {\n // Parse input if it's a JSON string (from OpenAI tool call)\n const parsedInput = typeof input === 'string' ? JSON.parse(input) : input;\n return executeBrowserTask(parsedInput, config);\n}\n\n/**\n * Format browser task result for OpenAI tool result\n * \n * Returns a concise summary suitable for agent context. The full result object\n * (with urls, errors, action_history, judgement, etc.) is available when calling\n * execute() directly, but this formatted string omits those details to save tokens.\n * \n * @param result - Browser task result with full history data\n * @returns Formatted string summary for tool result\n */\nexport function formatResult(result: BrowserTaskResult): string {\n if (result.success) {\n const parts = [\n '✅ Browser task completed successfully',\n `Steps taken: ${result.steps_taken ?? 0}`,\n result.execution_time_ms ? `Execution time: ${result.execution_time_ms}ms` : null,\n '',\n 'Result:',\n result.result || 'Task completed',\n ];\n return parts.filter(Boolean).join('\\n');\n }\n\n return `❌ Browser task failed: ${result.error || 'Unknown error'}`;\n}\n\n/**\n * Get system prompt for browser automation\n */\nexport function getSystemPrompt(): string {\n return BROWSER_SYSTEM_PROMPT;\n}\n\n/**\n * Create a configured browser tool with execute and formatResult methods\n * \n * @param config - Browser worker configuration\n * @returns Tool definition with execute and formatResult methods\n * \n * @example\n * ```typescript\n * import OpenAI from 'openai';\n * import { createBrowserTool } from 'morphsdk/tools/browser/openai';\n * \n * const tool = createBrowserTool({\n * apiUrl: 'https://browser-worker.example.com'\n * });\n * \n * const client = new OpenAI();\n * \n * const response = await client.chat.completions.create({\n * model: 'gpt-4o',\n * tools: [tool], // tool itself is the ChatCompletionTool\n * messages: [{\n * role: 'user',\n * content: 'Test the checkout at https://3000-abc.e2b.dev'\n * }]\n * });\n * \n * // Execute and format\n * const result = await tool.execute(toolCallArgs);\n * const formatted = tool.formatResult(result);\n * ```\n */\nexport function createBrowserTool(config?: BrowserConfig) {\n return Object.assign({}, browserTool, {\n execute: async (input: BrowserTaskInput | string): Promise<BrowserTaskResult> => {\n return execute(input, config);\n },\n formatResult: (result: BrowserTaskResult): string => {\n return formatResult(result);\n },\n getSystemPrompt: (): string => {\n return getSystemPrompt();\n },\n });\n}\n\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../tools/browser/openai.ts"],"sourcesContent":["/**\n * OpenAI SDK adapter for browser automation tool\n */\n\nimport type { ChatCompletionTool } from 'openai/resources/chat/completions.mjs';\nimport { executeBrowserTask } from './core.js';\nimport type {\n BrowserConfig,\n BrowserTaskInput,\n BrowserTaskResult,\n} from './types.js';\nimport { BROWSER_TOOL_DESCRIPTION, BROWSER_SYSTEM_PROMPT } from './prompts.js';\n\n/**\n * OpenAI tool definition for browser automation\n */\nexport const browserTool: ChatCompletionTool = {\n type: 'function',\n function: {\n name: 'browser_task',\n description: BROWSER_TOOL_DESCRIPTION,\n parameters: {\n type: 'object',\n properties: {\n task: {\n type: 'string',\n description: 'Natural language description of what to do (e.g., \"Test checkout flow for buying a pineapple\")',\n },\n url: {\n type: 'string',\n description: 'Starting URL (e.g., https://3000-xyz.e2b.dev). Required if navigating to a specific page.',\n },\n max_steps: {\n type: 'number',\n description: 'Maximum number of browser actions to take (1-50). Default: 10. Use 15-30 for complex flows.',\n default: 10,\n },\n region: {\n type: 'string',\n enum: ['sfo', 'lon'],\n description: 'Browserless region: sfo (US West Coast) or lon (Europe). Default: sfo.',\n default: 'sfo',\n },\n },\n required: ['task'],\n },\n },\n};\n\n/**\n * Execute a browser task (for use in tool call handling)\n * \n * @param input - Tool input parameters (may be JSON string)\n * @param config - Optional browser worker configuration\n * @returns Task execution result\n */\nexport async function execute(\n input: BrowserTaskInput | string,\n config?: BrowserConfig\n): Promise<BrowserTaskResult> {\n // Parse input if it's a JSON string (from OpenAI tool call)\n const parsedInput = typeof input === 'string' ? JSON.parse(input) : input;\n return executeBrowserTask(parsedInput, config);\n}\n\n/**\n * Format browser task result for OpenAI tool result\n * \n * Returns a concise summary suitable for agent context. The full result object\n * (with urls, errors, action_history, judgement, etc.) is available when calling\n * execute() directly, but this formatted string omits those details to save tokens.\n * \n * @param result - Browser task result with full history data\n * @returns Formatted string summary for tool result\n */\nexport function formatResult(result: BrowserTaskResult): string {\n if (result.success) {\n const parts = [\n '✅ Browser task completed successfully',\n `Steps taken: ${result.steps_taken ?? 0}`,\n result.execution_time_ms ? `Execution time: ${result.execution_time_ms}ms` : null,\n '',\n 'Result:',\n result.result || 'Task completed',\n ];\n return parts.filter(Boolean).join('\\n');\n }\n\n return `❌ Browser task failed: ${result.error || 'Unknown error'}`;\n}\n\n/**\n * Get system prompt for browser automation\n */\nexport function getSystemPrompt(): string {\n return BROWSER_SYSTEM_PROMPT;\n}\n\n/**\n * Create a configured browser tool with execute and formatResult methods\n * \n * @param config - Browser worker configuration\n * @returns Tool definition with execute and formatResult methods\n * \n * @example\n * ```typescript\n * import OpenAI from 'openai';\n * import { createBrowserTool } from 'morphsdk/tools/browser/openai';\n * \n * const tool = createBrowserTool({\n * apiUrl: 'https://browser-worker.example.com'\n * });\n * \n * const client = new OpenAI();\n * \n * const response = await client.chat.completions.create({\n * model: 'gpt-4o',\n * tools: [tool], // tool itself is the ChatCompletionTool\n * messages: [{\n * role: 'user',\n * content: 'Test the checkout at https://3000-abc.e2b.dev'\n * }]\n * });\n * \n * // Execute and format\n * const result = await tool.execute(toolCallArgs);\n * const formatted = tool.formatResult(result);\n * ```\n */\nexport function createBrowserTool(config?: BrowserConfig) {\n return Object.assign({}, browserTool, {\n execute: async (input: BrowserTaskInput | string): Promise<BrowserTaskResult> => {\n return execute(input, config);\n },\n formatResult: (result: BrowserTaskResult): string => {\n return formatResult(result);\n },\n getSystemPrompt: (): string => {\n return getSystemPrompt();\n },\n });\n}\n\n"],"mappings":";;;;;;;;;;;;AAgBO,IAAM,cAAkC;AAAA,EAC7C,MAAM;AAAA,EACN,UAAU;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,KAAK;AAAA,UACH,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,MAAM;AAAA,UACN,aAAa;AAAA,UACb,SAAS;AAAA,QACX;AAAA,QACA,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,OAAO,KAAK;AAAA,UACnB,aAAa;AAAA,UACb,SAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,UAAU,CAAC,MAAM;AAAA,IACnB;AAAA,EACF;AACF;AASA,eAAsB,QACpB,OACA,QAC4B;AAE5B,QAAM,cAAc,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;AACpE,SAAO,mBAAmB,aAAa,MAAM;AAC/C;AAYO,SAAS,aAAa,QAAmC;AAC9D,MAAI,OAAO,SAAS;AAClB,UAAM,QAAQ;AAAA,MACZ;AAAA,MACA,gBAAgB,OAAO,eAAe,CAAC;AAAA,MACvC,OAAO,oBAAoB,mBAAmB,OAAO,iBAAiB,OAAO;AAAA,MAC7E;AAAA,MACA;AAAA,MACA,OAAO,UAAU;AAAA,IACnB;AACA,WAAO,MAAM,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,EACxC;AAEA,SAAO,+BAA0B,OAAO,SAAS,eAAe;AAClE;AAKO,SAAS,kBAA0B;AACxC,SAAO;AACT;AAiCO,SAAS,kBAAkB,QAAwB;AACxD,SAAO,OAAO,OAAO,CAAC,GAAG,aAAa;AAAA,IACpC,SAAS,OAAO,UAAiE;AAC/E,aAAO,QAAQ,OAAO,MAAM;AAAA,IAC9B;AAAA,IACA,cAAc,CAAC,WAAsC;AACnD,aAAO,aAAa,MAAM;AAAA,IAC5B;AAAA,IACA,iBAAiB,MAAc;AAC7B,aAAO,gBAAgB;AAAA,IACzB;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../tools/browser/vercel.ts"],"sourcesContent":["/**\n * Vercel AI SDK adapter for browser automation tool\n */\n\nimport { tool as createTool } from 'ai';\nimport { z } from 'zod';\nimport { executeBrowserTask } from './core.js';\nimport type { BrowserConfig } from './types.js';\nimport { BROWSER_TOOL_DESCRIPTION } from './prompts.js';\n\n/**\n * Create Vercel AI SDK tool for browser automation\n * \n * @param config - Optional browser worker configuration\n * @returns Vercel AI SDK tool\n * \n * @example\n * ```typescript\n * import { generateText } from 'ai';\n * import { anthropic } from '@ai-sdk/anthropic';\n * import { createBrowserTool } from 'morphsdk/tools/browser/vercel';\n * \n * const browserTool = createBrowserTool({\n * apiUrl: 'https://browser-worker.example.com'\n * });\n * \n * const result = await generateText({\n * model: anthropic('claude-sonnet-4-5-20250929'),\n * tools: { browserTask: browserTool },\n * prompt: 'Test the checkout flow at https://3000-abc.e2b.dev',\n * maxSteps: 5\n * });\n * ```\n */\nexport function createBrowserTool(config?: BrowserConfig) {\n const schema = z.object({\n task: z.string().describe('Natural language description of what to do (e.g., \"Test checkout flow for buying a pineapple\")'),\n url: z.string().optional().describe('Starting URL (e.g., https://3000-xyz.e2b.dev)'),\n max_steps: z.number().min(1).max(50).default(10).describe('Maximum number of browser actions to take'),\n region: z.enum(['sfo', 'lon']).default('sfo').describe('Browserless region: sfo (US West) or lon (Europe)'),\n });\n\n return createTool({\n description: BROWSER_TOOL_DESCRIPTION,\n inputSchema: schema,\n execute: async (params) => {\n const { task, url, max_steps, region } = params;\n const result = await executeBrowserTask(\n { task, url, max_steps, region },\n config\n );\n\n // Return minimal summary for agent context (to save tokens)\n // Full result with urls, errors, action_history, judgement, etc. is available\n // when calling executeBrowserTask() directly outside of agent tools\n if (result.success) {\n return {\n success: true,\n result: result.result,\n steps_taken: result.steps_taken,\n execution_time_ms: result.execution_time_ms,\n };\n }\n\n return {\n success: false,\n error: result.error,\n };\n },\n });\n}\n\n/**\n * Default browser tool for Vercel AI SDK\n */\nexport const browserTool = createBrowserTool();\n\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../tools/browser/vercel.ts"],"sourcesContent":["/**\n * Vercel AI SDK adapter for browser automation tool\n */\n\nimport { tool as createTool } from 'ai';\nimport { z } from 'zod';\nimport { executeBrowserTask } from './core.js';\nimport type { BrowserConfig } from './types.js';\nimport { BROWSER_TOOL_DESCRIPTION } from './prompts.js';\n\n/**\n * Create Vercel AI SDK tool for browser automation\n * \n * @param config - Optional browser worker configuration\n * @returns Vercel AI SDK tool\n * \n * @example\n * ```typescript\n * import { generateText } from 'ai';\n * import { anthropic } from '@ai-sdk/anthropic';\n * import { createBrowserTool } from 'morphsdk/tools/browser/vercel';\n * \n * const browserTool = createBrowserTool({\n * apiUrl: 'https://browser-worker.example.com'\n * });\n * \n * const result = await generateText({\n * model: anthropic('claude-sonnet-4-5-20250929'),\n * tools: { browserTask: browserTool },\n * prompt: 'Test the checkout flow at https://3000-abc.e2b.dev',\n * maxSteps: 5\n * });\n * ```\n */\nexport function createBrowserTool(config?: BrowserConfig) {\n const schema = z.object({\n task: z.string().describe('Natural language description of what to do (e.g., \"Test checkout flow for buying a pineapple\")'),\n url: z.string().optional().describe('Starting URL (e.g., https://3000-xyz.e2b.dev)'),\n max_steps: z.number().min(1).max(50).default(10).describe('Maximum number of browser actions to take'),\n region: z.enum(['sfo', 'lon']).default('sfo').describe('Browserless region: sfo (US West) or lon (Europe)'),\n });\n\n return createTool({\n description: BROWSER_TOOL_DESCRIPTION,\n inputSchema: schema,\n execute: async (params) => {\n const { task, url, max_steps, region } = params;\n const result = await executeBrowserTask(\n { task, url, max_steps, region },\n config\n );\n\n // Return minimal summary for agent context (to save tokens)\n // Full result with urls, errors, action_history, judgement, etc. is available\n // when calling executeBrowserTask() directly outside of agent tools\n if (result.success) {\n return {\n success: true,\n result: result.result,\n steps_taken: result.steps_taken,\n execution_time_ms: result.execution_time_ms,\n };\n }\n\n return {\n success: false,\n error: result.error,\n };\n },\n });\n}\n\n/**\n * Default browser tool for Vercel AI SDK\n */\nexport const browserTool = createBrowserTool();\n\n"],"mappings":";;;;;;;;;;;AAIA,SAAS,QAAQ,kBAAkB;AACnC,SAAS,SAAS;AA6BX,SAAS,kBAAkB,QAAwB;AACxD,QAAM,SAAS,EAAE,OAAO;AAAA,IACtB,MAAM,EAAE,OAAO,EAAE,SAAS,gGAAgG;AAAA,IAC1H,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,IACnF,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,2CAA2C;AAAA,IACrG,QAAQ,EAAE,KAAK,CAAC,OAAO,KAAK,CAAC,EAAE,QAAQ,KAAK,EAAE,SAAS,mDAAmD;AAAA,EAC5G,CAAC;AAED,SAAO,WAAW;AAAA,IAChB,aAAa;AAAA,IACb,aAAa;AAAA,IACb,SAAS,OAAO,WAAW;AACzB,YAAM,EAAE,MAAM,KAAK,WAAW,OAAO,IAAI;AACzC,YAAM,SAAS,MAAM;AAAA,QACnB,EAAE,MAAM,KAAK,WAAW,OAAO;AAAA,QAC/B;AAAA,MACF;AAKA,UAAI,OAAO,SAAS;AAClB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,QAAQ,OAAO;AAAA,UACf,aAAa,OAAO;AAAA,UACpB,mBAAmB,OAAO;AAAA,QAC5B;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAKO,IAAM,cAAc,kBAAkB;","names":[]}
|
|
@@ -104,12 +104,11 @@ async function executeCodebaseSearch(input, config) {
|
|
|
104
104
|
if (!apiKey) {
|
|
105
105
|
throw new Error("MORPH_API_KEY not found. Set environment variable or pass in config");
|
|
106
106
|
}
|
|
107
|
-
const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || "
|
|
107
|
+
const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || "https://repos.morphllm.com";
|
|
108
108
|
const timeout = config.timeout || 3e4;
|
|
109
109
|
const debug = config.debug || false;
|
|
110
110
|
if (debug) {
|
|
111
|
-
|
|
112
|
-
console.log(`[CodebaseSearch] Query: "${input.query.slice(0, 60)}..." repo=${config.repoId} (${ref})`);
|
|
111
|
+
console.log(`[CodebaseSearch] Query: "${input.query.slice(0, 60)}..." repo=${config.repoId}`);
|
|
113
112
|
console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);
|
|
114
113
|
}
|
|
115
114
|
const startTime = Date.now();
|
|
@@ -125,8 +124,6 @@ async function executeCodebaseSearch(input, config) {
|
|
|
125
124
|
body: JSON.stringify({
|
|
126
125
|
query: input.query,
|
|
127
126
|
repoId: config.repoId,
|
|
128
|
-
branch: config.branch,
|
|
129
|
-
commitHash: config.commitHash,
|
|
130
127
|
targetDirectories: input.target_directories || [],
|
|
131
128
|
limit: input.limit || 10,
|
|
132
129
|
candidateLimit: 50
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../tools/codebase_search/anthropic.ts","../../../tools/utils/resilience.ts","../../../tools/codebase_search/core.ts","../../../tools/codebase_search/prompts.ts"],"sourcesContent":["/**\n * Anthropic SDK adapter for codebase_search tool\n */\n\nimport type { Tool } from '@anthropic-ai/sdk/resources/messages';\nimport { executeCodebaseSearch } from './core.js';\nimport { CODEBASE_SEARCH_DESCRIPTION, CODEBASE_SEARCH_SYSTEM_PROMPT } from './prompts.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n\n/**\n * Create Anthropic-native codebase_search tool with execute and formatResult methods\n * \n * @param config - Configuration with repoId\n * @returns Anthropic Tool definition with execute and formatResult methods\n * \n * @example\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { createCodebaseSearchTool } from 'morphsdk/tools/anthropic';\n * \n * const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\n * const tool = createCodebaseSearchTool({ repoId: 'my-project' });\n * \n * const response = await client.messages.create({\n * model: \"claude-sonnet-4-5-20250929\",\n * tools: [tool], // tool itself is the Tool definition\n * messages: [{ role: \"user\", content: \"Find authentication code\" }]\n * });\n * \n * // Execute tool and format result\n * const result = await tool.execute(toolUseBlock.input);\n * const formatted = tool.formatResult(result);\n * ```\n */\nexport function createCodebaseSearchTool(config: CodebaseSearchConfig) {\n const toolDefinition: Tool = {\n name: 'codebase_search',\n description: CODEBASE_SEARCH_DESCRIPTION,\n input_schema: {\n type: 'object',\n properties: {\n explanation: {\n type: 'string',\n description: 'One sentence explanation as to why this tool is being used, and how it contributes to the goal.',\n },\n query: {\n type: 'string',\n description: 'A complete question about what you want to understand. Ask as if talking to a colleague: \"How does X work?\", \"What happens when Y?\", \"Where is Z handled?\"',\n },\n target_directories: {\n type: 'array',\n items: { type: 'string' },\n description: 'Prefix directory paths to limit search scope (single directory only, no glob patterns). Use [] to search entire repo.',\n },\n limit: {\n type: 'number',\n description: 'Maximum results to return (default: 10)',\n },\n },\n required: ['query', 'target_directories', 'explanation'],\n },\n cache_control: { type: 'ephemeral' } as any,\n } as any;\n\n return Object.assign(toolDefinition, {\n execute: async (input: CodebaseSearchInput): Promise<CodebaseSearchResult> => {\n return executeCodebaseSearch(input, config);\n },\n formatResult: (result: CodebaseSearchResult): string => {\n return formatResult(result);\n },\n getSystemPrompt: (): string => {\n return CODEBASE_SEARCH_SYSTEM_PROMPT;\n },\n });\n}\n\n/**\n * Format search results for Claude\n * \n * @param result - Search result from endpoint\n * @returns Formatted string for tool_result\n */\nfunction formatResult(result: CodebaseSearchResult): string {\n if (!result.success) {\n return `Search failed: ${result.error}`;\n }\n\n if (result.results.length === 0) {\n return 'No matching code found. Try rephrasing your query or broadening the search scope.';\n }\n\n const lines: string[] = [];\n \n lines.push(`Found ${result.results.length} relevant code sections (searched ${result.stats.candidatesRetrieved} candidates in ${result.stats.searchTimeMs}ms):\\n`);\n\n result.results.forEach((r, i) => {\n const relevance = (r.rerankScore * 100).toFixed(1);\n lines.push(`${i + 1}. ${r.filepath} (${relevance}% relevant)`);\n lines.push(` Symbol: ${r.symbolPath}`);\n lines.push(` Language: ${r.language}`);\n lines.push(` Lines: ${r.startLine}-${r.endLine}`);\n lines.push(` Code:`);\n \n // Show code content with indentation\n const codeLines = r.content.split('\\n');\n codeLines.slice(0, Math.min(codeLines.length, 20)).forEach(line => {\n lines.push(` ${line}`);\n });\n \n if (codeLines.length > 20) {\n lines.push(` ... (${codeLines.length - 20} more lines)`);\n }\n \n lines.push('');\n });\n\n return lines.join('\\n');\n}\n\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n","/**\n * Core implementation for codebase search\n * Calls Morph rerank service for two-stage semantic search\n */\n\nimport { fetchWithRetry, withTimeout } from '../utils/resilience.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n/**\n * CodebaseSearch client for programmatic semantic search\n */\nexport class CodebaseSearchClient {\n private config: { \n apiKey?: string; \n searchUrl?: string; \n debug?: boolean; \n timeout?: number; \n retryConfig?: any;\n };\n\n constructor(config: { apiKey?: string; debug?: boolean; timeout?: number; retryConfig?: any } = {}) {\n this.config = {\n apiKey: config.apiKey,\n searchUrl: process.env.MORPH_SEARCH_URL || 'http://embedrerank.morphllm.com:8081',\n debug: config.debug,\n timeout: config.timeout || 30000,\n retryConfig: config.retryConfig,\n };\n }\n\n /**\n * Execute a semantic code search\n * \n * @param input - Search parameters including query, repoId, and target directories\n * @param overrides - Optional config overrides for this operation\n * @returns Search results with ranked code matches\n */\n async search(\n input: { query: string; repoId: string; target_directories?: string[]; explanation?: string; limit?: number },\n overrides?: any\n ): Promise<CodebaseSearchResult> {\n return executeCodebaseSearch(\n {\n query: input.query,\n target_directories: input.target_directories,\n explanation: input.explanation,\n limit: input.limit,\n },\n { ...this.config, repoId: input.repoId, ...overrides }\n );\n }\n}\n\n/**\n * Execute semantic code search\n */\nexport async function executeCodebaseSearch(\n input: CodebaseSearchInput,\n config: CodebaseSearchConfig\n): Promise<CodebaseSearchResult> {\n const apiKey = config.apiKey || process.env.MORPH_API_KEY;\n if (!apiKey) {\n throw new Error('MORPH_API_KEY not found. Set environment variable or pass in config');\n }\n\n const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || 'http://embedrerank.morphllm.com:8081';\n const timeout = config.timeout || 30000;\n const debug = config.debug || false;\n\n if (debug) {\n const ref = config.commitHash ? `commit=${config.commitHash.slice(0, 8)}` : config.branch ? `branch=${config.branch}` : 'latest main';\n console.log(`[CodebaseSearch] Query: \"${input.query.slice(0, 60)}...\" repo=${config.repoId} (${ref})`);\n console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);\n }\n\n const startTime = Date.now();\n\n try {\n const fetchPromise = fetchWithRetry(\n `${searchUrl}/v1/codebase_search`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n query: input.query,\n repoId: config.repoId,\n branch: config.branch,\n commitHash: config.commitHash,\n targetDirectories: input.target_directories || [],\n limit: input.limit || 10,\n candidateLimit: 50,\n }),\n },\n config.retryConfig\n );\n\n const response = await withTimeout(fetchPromise, timeout, `Codebase search timed out after ${timeout}ms`);\n\n if (!response.ok) {\n const errorText = await response.text();\n if (debug) console.error(`[CodebaseSearch] Error: ${response.status} - ${errorText}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: `Search failed (${response.status}): ${errorText}`,\n };\n }\n\n const data = await response.json();\n const elapsed = Date.now() - startTime;\n\n if (debug) {\n console.log(`[CodebaseSearch] ✅ ${data.results?.length || 0} results in ${elapsed}ms`);\n }\n\n return {\n success: true,\n results: data.results || [],\n stats: data.stats || { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: elapsed },\n };\n\n } catch (error) {\n if (debug) console.error(`[CodebaseSearch] Exception: ${error instanceof Error ? error.message : error}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n","/**\n * Tool descriptions and system prompts for codebase search\n */\n\nexport const CODEBASE_SEARCH_DESCRIPTION = `Semantic search that finds code by meaning, not exact text.\n\nUse this to explore unfamiliar codebases or ask \"how/where/what\" questions:\n- \"How does X work?\" - Find implementation details\n- \"Where is Y handled?\" - Locate specific functionality\n- \"What happens when Z?\" - Understand flow\n\nThe tool uses two-stage retrieval (embedding similarity + reranking) to find the most semantically relevant code chunks.\n\nReturns code chunks with file paths, line ranges, and full content ranked by relevance.`;\n\nexport const CODEBASE_SEARCH_SYSTEM_PROMPT = `You have access to the codebase_search tool that performs semantic code search.\n\nWhen searching:\n- Use natural language queries describing what you're looking for\n- Be specific about functionality, not variable names\n- Use target_directories to narrow search if you know the area\n- Results are ranked by relevance (rerank score is most important)\n\nThe tool returns:\n- File paths with symbol names (e.g. \"src/auth.ts::AuthService@L1-L17\")\n- Line ranges for precise navigation\n- Full code content for each match\n- Dual relevance scores: embedding similarity + rerank score\n\nUse results to understand code or answer questions. The content is provided in full - avoid re-reading unless you need more context.`;\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../tools/codebase_search/anthropic.ts","../../../tools/utils/resilience.ts","../../../tools/codebase_search/core.ts","../../../tools/codebase_search/prompts.ts"],"sourcesContent":["/**\n * Anthropic SDK adapter for codebase_search tool\n */\n\nimport type { Tool } from '@anthropic-ai/sdk/resources/messages';\nimport { executeCodebaseSearch } from './core.js';\nimport { CODEBASE_SEARCH_DESCRIPTION, CODEBASE_SEARCH_SYSTEM_PROMPT } from './prompts.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n\n/**\n * Create Anthropic-native codebase_search tool with execute and formatResult methods\n * \n * @param config - Configuration with repoId\n * @returns Anthropic Tool definition with execute and formatResult methods\n * \n * @example\n * ```ts\n * import Anthropic from '@anthropic-ai/sdk';\n * import { createCodebaseSearchTool } from 'morphsdk/tools/anthropic';\n * \n * const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\n * const tool = createCodebaseSearchTool({ repoId: 'my-project' });\n * \n * const response = await client.messages.create({\n * model: \"claude-sonnet-4-5-20250929\",\n * tools: [tool], // tool itself is the Tool definition\n * messages: [{ role: \"user\", content: \"Find authentication code\" }]\n * });\n * \n * // Execute tool and format result\n * const result = await tool.execute(toolUseBlock.input);\n * const formatted = tool.formatResult(result);\n * ```\n */\nexport function createCodebaseSearchTool(config: CodebaseSearchConfig) {\n const toolDefinition: Tool = {\n name: 'codebase_search',\n description: CODEBASE_SEARCH_DESCRIPTION,\n input_schema: {\n type: 'object',\n properties: {\n explanation: {\n type: 'string',\n description: 'One sentence explanation as to why this tool is being used, and how it contributes to the goal.',\n },\n query: {\n type: 'string',\n description: 'A complete question about what you want to understand. Ask as if talking to a colleague: \"How does X work?\", \"What happens when Y?\", \"Where is Z handled?\"',\n },\n target_directories: {\n type: 'array',\n items: { type: 'string' },\n description: 'Prefix directory paths to limit search scope (single directory only, no glob patterns). Use [] to search entire repo.',\n },\n limit: {\n type: 'number',\n description: 'Maximum results to return (default: 10)',\n },\n },\n required: ['query', 'target_directories', 'explanation'],\n },\n cache_control: { type: 'ephemeral' } as any,\n } as any;\n\n return Object.assign(toolDefinition, {\n execute: async (input: CodebaseSearchInput): Promise<CodebaseSearchResult> => {\n return executeCodebaseSearch(input, config);\n },\n formatResult: (result: CodebaseSearchResult): string => {\n return formatResult(result);\n },\n getSystemPrompt: (): string => {\n return CODEBASE_SEARCH_SYSTEM_PROMPT;\n },\n });\n}\n\n/**\n * Format search results for Claude\n * \n * @param result - Search result from endpoint\n * @returns Formatted string for tool_result\n */\nfunction formatResult(result: CodebaseSearchResult): string {\n if (!result.success) {\n return `Search failed: ${result.error}`;\n }\n\n if (result.results.length === 0) {\n return 'No matching code found. Try rephrasing your query or broadening the search scope.';\n }\n\n const lines: string[] = [];\n \n lines.push(`Found ${result.results.length} relevant code sections (searched ${result.stats.candidatesRetrieved} candidates in ${result.stats.searchTimeMs}ms):\\n`);\n\n result.results.forEach((r, i) => {\n const relevance = (r.rerankScore * 100).toFixed(1);\n lines.push(`${i + 1}. ${r.filepath} (${relevance}% relevant)`);\n lines.push(` Symbol: ${r.symbolPath}`);\n lines.push(` Language: ${r.language}`);\n lines.push(` Lines: ${r.startLine}-${r.endLine}`);\n lines.push(` Code:`);\n \n // Show code content with indentation\n const codeLines = r.content.split('\\n');\n codeLines.slice(0, Math.min(codeLines.length, 20)).forEach(line => {\n lines.push(` ${line}`);\n });\n \n if (codeLines.length > 20) {\n lines.push(` ... (${codeLines.length - 20} more lines)`);\n }\n \n lines.push('');\n });\n\n return lines.join('\\n');\n}\n\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n","/**\n * Core implementation for codebase search\n * Calls Morph rerank service for two-stage semantic search\n */\n\nimport { fetchWithRetry, withTimeout } from '../utils/resilience.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n/**\n * CodebaseSearch client for programmatic semantic search\n */\nexport class CodebaseSearchClient {\n private config: { \n apiKey?: string; \n searchUrl?: string; \n debug?: boolean; \n timeout?: number; \n retryConfig?: any;\n };\n\n constructor(config: { apiKey?: string; debug?: boolean; timeout?: number; retryConfig?: any } = {}) {\n this.config = {\n apiKey: config.apiKey,\n searchUrl: process.env.MORPH_SEARCH_URL || 'https://repos.morphllm.com',\n debug: config.debug,\n timeout: config.timeout || 30000,\n retryConfig: config.retryConfig,\n };\n }\n\n /**\n * Execute a semantic code search\n * \n * @param input - Search parameters including query, repoId, and target directories\n * @param overrides - Optional config overrides for this operation\n * @returns Search results with ranked code matches\n */\n async search(\n input: { query: string; repoId: string; target_directories?: string[]; explanation?: string; limit?: number },\n overrides?: any\n ): Promise<CodebaseSearchResult> {\n return executeCodebaseSearch(\n {\n query: input.query,\n target_directories: input.target_directories,\n explanation: input.explanation,\n limit: input.limit,\n },\n { ...this.config, repoId: input.repoId, ...overrides }\n );\n }\n}\n\n/**\n * Execute semantic code search\n */\nexport async function executeCodebaseSearch(\n input: CodebaseSearchInput,\n config: CodebaseSearchConfig\n): Promise<CodebaseSearchResult> {\n const apiKey = config.apiKey || process.env.MORPH_API_KEY;\n if (!apiKey) {\n throw new Error('MORPH_API_KEY not found. Set environment variable or pass in config');\n }\n\n const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || 'https://repos.morphllm.com';\n const timeout = config.timeout || 30000;\n const debug = config.debug || false;\n\n if (debug) {\n console.log(`[CodebaseSearch] Query: \"${input.query.slice(0, 60)}...\" repo=${config.repoId}`);\n console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);\n }\n\n const startTime = Date.now();\n\n try {\n const fetchPromise = fetchWithRetry(\n `${searchUrl}/v1/codebase_search`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n query: input.query,\n repoId: config.repoId,\n targetDirectories: input.target_directories || [],\n limit: input.limit || 10,\n candidateLimit: 50,\n }),\n },\n config.retryConfig\n );\n\n const response = await withTimeout(fetchPromise, timeout, `Codebase search timed out after ${timeout}ms`);\n\n if (!response.ok) {\n const errorText = await response.text();\n if (debug) console.error(`[CodebaseSearch] Error: ${response.status} - ${errorText}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: `Search failed (${response.status}): ${errorText}`,\n };\n }\n\n const data = await response.json();\n const elapsed = Date.now() - startTime;\n\n if (debug) {\n console.log(`[CodebaseSearch] ✅ ${data.results?.length || 0} results in ${elapsed}ms`);\n }\n\n return {\n success: true,\n results: data.results || [],\n stats: data.stats || { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: elapsed },\n };\n\n } catch (error) {\n if (debug) console.error(`[CodebaseSearch] Exception: ${error instanceof Error ? error.message : error}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n","/**\n * Tool descriptions and system prompts for codebase search\n */\n\nexport const CODEBASE_SEARCH_DESCRIPTION = `Semantic search that finds code by meaning, not exact text.\n\nUse this to explore unfamiliar codebases or ask \"how/where/what\" questions:\n- \"How does X work?\" - Find implementation details\n- \"Where is Y handled?\" - Locate specific functionality\n- \"What happens when Z?\" - Understand flow\n\nThe tool uses two-stage retrieval (embedding similarity + reranking) to find the most semantically relevant code chunks.\n\nReturns code chunks with file paths, line ranges, and full content ranked by relevance.`;\n\nexport const CODEBASE_SEARCH_SYSTEM_PROMPT = `You have access to the codebase_search tool that performs semantic code search.\n\nWhen searching:\n- Use natural language queries describing what you're looking for\n- Be specific about functionality, not variable names\n- Use target_directories to narrow search if you know the area\n- Results are ranked by relevance (rerank score is most important)\n\nThe tool returns:\n- File paths with symbol names (e.g. \"src/auth.ts::AuthService@L1-L17\")\n- Line ranges for precise navigation\n- Full code content for each match\n- Dual relevance scores: embedding similarity + rerank score\n\nUse results to understand code or answer questions. The content is provided in full - avoid re-reading unless you need more context.`;\n\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createCodebaseSearchTool
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-OUEJ6XEO.js";
|
|
4
4
|
import "../../chunk-YQMPVJ2L.js";
|
|
5
|
-
import "../../chunk-
|
|
5
|
+
import "../../chunk-WM77HRKO.js";
|
|
6
6
|
import "../../chunk-4VWJFZVS.js";
|
|
7
7
|
import "../../chunk-PZ5AY32C.js";
|
|
8
8
|
export {
|
|
@@ -105,7 +105,7 @@ var CodebaseSearchClient = class {
|
|
|
105
105
|
constructor(config = {}) {
|
|
106
106
|
this.config = {
|
|
107
107
|
apiKey: config.apiKey,
|
|
108
|
-
searchUrl: process.env.MORPH_SEARCH_URL || "
|
|
108
|
+
searchUrl: process.env.MORPH_SEARCH_URL || "https://repos.morphllm.com",
|
|
109
109
|
debug: config.debug,
|
|
110
110
|
timeout: config.timeout || 3e4,
|
|
111
111
|
retryConfig: config.retryConfig
|
|
@@ -135,12 +135,11 @@ async function executeCodebaseSearch(input, config) {
|
|
|
135
135
|
if (!apiKey) {
|
|
136
136
|
throw new Error("MORPH_API_KEY not found. Set environment variable or pass in config");
|
|
137
137
|
}
|
|
138
|
-
const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || "
|
|
138
|
+
const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || "https://repos.morphllm.com";
|
|
139
139
|
const timeout = config.timeout || 3e4;
|
|
140
140
|
const debug = config.debug || false;
|
|
141
141
|
if (debug) {
|
|
142
|
-
|
|
143
|
-
console.log(`[CodebaseSearch] Query: "${input.query.slice(0, 60)}..." repo=${config.repoId} (${ref})`);
|
|
142
|
+
console.log(`[CodebaseSearch] Query: "${input.query.slice(0, 60)}..." repo=${config.repoId}`);
|
|
144
143
|
console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);
|
|
145
144
|
}
|
|
146
145
|
const startTime = Date.now();
|
|
@@ -156,8 +155,6 @@ async function executeCodebaseSearch(input, config) {
|
|
|
156
155
|
body: JSON.stringify({
|
|
157
156
|
query: input.query,
|
|
158
157
|
repoId: config.repoId,
|
|
159
|
-
branch: config.branch,
|
|
160
|
-
commitHash: config.commitHash,
|
|
161
158
|
targetDirectories: input.target_directories || [],
|
|
162
159
|
limit: input.limit || 10,
|
|
163
160
|
candidateLimit: 50
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../tools/codebase_search/core.ts","../../../tools/utils/resilience.ts"],"sourcesContent":["/**\n * Core implementation for codebase search\n * Calls Morph rerank service for two-stage semantic search\n */\n\nimport { fetchWithRetry, withTimeout } from '../utils/resilience.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n/**\n * CodebaseSearch client for programmatic semantic search\n */\nexport class CodebaseSearchClient {\n private config: { \n apiKey?: string; \n searchUrl?: string; \n debug?: boolean; \n timeout?: number; \n retryConfig?: any;\n };\n\n constructor(config: { apiKey?: string; debug?: boolean; timeout?: number; retryConfig?: any } = {}) {\n this.config = {\n apiKey: config.apiKey,\n searchUrl: process.env.MORPH_SEARCH_URL || 'http://embedrerank.morphllm.com:8081',\n debug: config.debug,\n timeout: config.timeout || 30000,\n retryConfig: config.retryConfig,\n };\n }\n\n /**\n * Execute a semantic code search\n * \n * @param input - Search parameters including query, repoId, and target directories\n * @param overrides - Optional config overrides for this operation\n * @returns Search results with ranked code matches\n */\n async search(\n input: { query: string; repoId: string; target_directories?: string[]; explanation?: string; limit?: number },\n overrides?: any\n ): Promise<CodebaseSearchResult> {\n return executeCodebaseSearch(\n {\n query: input.query,\n target_directories: input.target_directories,\n explanation: input.explanation,\n limit: input.limit,\n },\n { ...this.config, repoId: input.repoId, ...overrides }\n );\n }\n}\n\n/**\n * Execute semantic code search\n */\nexport async function executeCodebaseSearch(\n input: CodebaseSearchInput,\n config: CodebaseSearchConfig\n): Promise<CodebaseSearchResult> {\n const apiKey = config.apiKey || process.env.MORPH_API_KEY;\n if (!apiKey) {\n throw new Error('MORPH_API_KEY not found. Set environment variable or pass in config');\n }\n\n const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || 'http://embedrerank.morphllm.com:8081';\n const timeout = config.timeout || 30000;\n const debug = config.debug || false;\n\n if (debug) {\n const ref = config.commitHash ? `commit=${config.commitHash.slice(0, 8)}` : config.branch ? `branch=${config.branch}` : 'latest main';\n console.log(`[CodebaseSearch] Query: \"${input.query.slice(0, 60)}...\" repo=${config.repoId} (${ref})`);\n console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);\n }\n\n const startTime = Date.now();\n\n try {\n const fetchPromise = fetchWithRetry(\n `${searchUrl}/v1/codebase_search`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n query: input.query,\n repoId: config.repoId,\n branch: config.branch,\n commitHash: config.commitHash,\n targetDirectories: input.target_directories || [],\n limit: input.limit || 10,\n candidateLimit: 50,\n }),\n },\n config.retryConfig\n );\n\n const response = await withTimeout(fetchPromise, timeout, `Codebase search timed out after ${timeout}ms`);\n\n if (!response.ok) {\n const errorText = await response.text();\n if (debug) console.error(`[CodebaseSearch] Error: ${response.status} - ${errorText}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: `Search failed (${response.status}): ${errorText}`,\n };\n }\n\n const data = await response.json();\n const elapsed = Date.now() - startTime;\n\n if (debug) {\n console.log(`[CodebaseSearch] ✅ ${data.results?.length || 0} results in ${elapsed}ms`);\n }\n\n return {\n success: true,\n results: data.results || [],\n stats: data.stats || { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: elapsed },\n };\n\n } catch (error) {\n if (debug) console.error(`[CodebaseSearch] Exception: ${error instanceof Error ? error.message : error}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../tools/codebase_search/core.ts","../../../tools/utils/resilience.ts"],"sourcesContent":["/**\n * Core implementation for codebase search\n * Calls Morph rerank service for two-stage semantic search\n */\n\nimport { fetchWithRetry, withTimeout } from '../utils/resilience.js';\nimport type { CodebaseSearchConfig, CodebaseSearchInput, CodebaseSearchResult } from './types.js';\n\n/**\n * CodebaseSearch client for programmatic semantic search\n */\nexport class CodebaseSearchClient {\n private config: { \n apiKey?: string; \n searchUrl?: string; \n debug?: boolean; \n timeout?: number; \n retryConfig?: any;\n };\n\n constructor(config: { apiKey?: string; debug?: boolean; timeout?: number; retryConfig?: any } = {}) {\n this.config = {\n apiKey: config.apiKey,\n searchUrl: process.env.MORPH_SEARCH_URL || 'https://repos.morphllm.com',\n debug: config.debug,\n timeout: config.timeout || 30000,\n retryConfig: config.retryConfig,\n };\n }\n\n /**\n * Execute a semantic code search\n * \n * @param input - Search parameters including query, repoId, and target directories\n * @param overrides - Optional config overrides for this operation\n * @returns Search results with ranked code matches\n */\n async search(\n input: { query: string; repoId: string; target_directories?: string[]; explanation?: string; limit?: number },\n overrides?: any\n ): Promise<CodebaseSearchResult> {\n return executeCodebaseSearch(\n {\n query: input.query,\n target_directories: input.target_directories,\n explanation: input.explanation,\n limit: input.limit,\n },\n { ...this.config, repoId: input.repoId, ...overrides }\n );\n }\n}\n\n/**\n * Execute semantic code search\n */\nexport async function executeCodebaseSearch(\n input: CodebaseSearchInput,\n config: CodebaseSearchConfig\n): Promise<CodebaseSearchResult> {\n const apiKey = config.apiKey || process.env.MORPH_API_KEY;\n if (!apiKey) {\n throw new Error('MORPH_API_KEY not found. Set environment variable or pass in config');\n }\n\n const searchUrl = config.searchUrl || process.env.MORPH_SEARCH_URL || 'https://repos.morphllm.com';\n const timeout = config.timeout || 30000;\n const debug = config.debug || false;\n\n if (debug) {\n console.log(`[CodebaseSearch] Query: \"${input.query.slice(0, 60)}...\" repo=${config.repoId}`);\n console.log(`[CodebaseSearch] URL: ${searchUrl}/v1/codebase_search`);\n }\n\n const startTime = Date.now();\n\n try {\n const fetchPromise = fetchWithRetry(\n `${searchUrl}/v1/codebase_search`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n query: input.query,\n repoId: config.repoId,\n targetDirectories: input.target_directories || [],\n limit: input.limit || 10,\n candidateLimit: 50,\n }),\n },\n config.retryConfig\n );\n\n const response = await withTimeout(fetchPromise, timeout, `Codebase search timed out after ${timeout}ms`);\n\n if (!response.ok) {\n const errorText = await response.text();\n if (debug) console.error(`[CodebaseSearch] Error: ${response.status} - ${errorText}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: `Search failed (${response.status}): ${errorText}`,\n };\n }\n\n const data = await response.json();\n const elapsed = Date.now() - startTime;\n\n if (debug) {\n console.log(`[CodebaseSearch] ✅ ${data.results?.length || 0} results in ${elapsed}ms`);\n }\n\n return {\n success: true,\n results: data.results || [],\n stats: data.stats || { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: elapsed },\n };\n\n } catch (error) {\n if (debug) console.error(`[CodebaseSearch] Exception: ${error instanceof Error ? error.message : error}`);\n return {\n success: false,\n results: [],\n stats: { totalResults: 0, candidatesRetrieved: 0, searchTimeMs: 0 },\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n}\n\n","/**\n * Resilience utilities for retry logic and timeout handling\n */\n\nexport interface RetryConfig {\n maxRetries?: number; // Default: 3\n initialDelay?: number; // Default: 1000ms\n maxDelay?: number; // Default: 30000ms\n backoffMultiplier?: number; // Default: 2\n retryableErrors?: string[]; // Default: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND']\n onRetry?: (attempt: number, error: Error) => void;\n}\n\nconst DEFAULT_RETRY_CONFIG: Required<Omit<RetryConfig, 'onRetry'>> = {\n maxRetries: 3,\n initialDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n retryableErrors: ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND'],\n};\n\n/**\n * Retry a fetch request with exponential backoff\n * \n * @param url - Request URL\n * @param options - Fetch options\n * @param retryConfig - Retry configuration\n * @returns Response from fetch\n * \n * @example\n * ```typescript\n * const response = await fetchWithRetry(\n * 'https://api.example.com/data',\n * { method: 'POST', body: JSON.stringify(data) },\n * { maxRetries: 5, initialDelay: 500 }\n * );\n * ```\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit,\n retryConfig: RetryConfig = {}\n): Promise<Response> {\n const {\n maxRetries = DEFAULT_RETRY_CONFIG.maxRetries,\n initialDelay = DEFAULT_RETRY_CONFIG.initialDelay,\n maxDelay = DEFAULT_RETRY_CONFIG.maxDelay,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n retryableErrors = DEFAULT_RETRY_CONFIG.retryableErrors,\n onRetry,\n } = retryConfig;\n\n let lastError: Error | null = null;\n let delay = initialDelay;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const response = await fetch(url, options);\n \n // Retry on 429 (rate limit) or 503 (service unavailable)\n if (response.status === 429 || response.status === 503) {\n if (attempt < maxRetries) {\n // Check for Retry-After header\n const retryAfter = response.headers.get('Retry-After');\n const waitTime = retryAfter \n ? parseInt(retryAfter) * 1000 \n : Math.min(delay, maxDelay);\n \n const error = new Error(`HTTP ${response.status}: Retrying after ${waitTime}ms`);\n if (onRetry) {\n onRetry(attempt + 1, error);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n continue;\n }\n }\n\n return response;\n } catch (error) {\n lastError = error as Error;\n \n // Check if error is retryable\n const isRetryable = retryableErrors.some(errType => \n lastError?.message?.includes(errType)\n );\n\n if (!isRetryable || attempt === maxRetries) {\n throw lastError;\n }\n\n // Exponential backoff\n const waitTime = Math.min(delay, maxDelay);\n if (onRetry) {\n onRetry(attempt + 1, lastError);\n }\n \n await sleep(waitTime);\n delay *= backoffMultiplier;\n }\n }\n\n throw lastError || new Error('Max retries exceeded');\n}\n\n/**\n * Add timeout to any promise\n * \n * @param promise - Promise to wrap with timeout\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Optional custom error message\n * @returns Promise that rejects if timeout is reached\n * \n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Data fetch timed out'\n * );\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage?: string\n): Promise<T> {\n let timeoutId: NodeJS.Timeout | number;\n \n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage || `Operation timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch (error) {\n clearTimeout(timeoutId!);\n throw error;\n }\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Unified error type for all tools\n */\nexport class MorphError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public retryable: boolean = false\n ) {\n super(message);\n this.name = 'MorphError';\n }\n}\n\n\n"],"mappings":"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","names":[]}
|