noumen 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +767 -51
- package/dist/a2a/index.d.ts +148 -0
- package/dist/a2a/index.js +579 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/acp/index.d.ts +129 -0
- package/dist/acp/index.js +498 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/agent-BrkbZyOT.d.ts +1028 -0
- package/dist/cache-DVqaCX8v.d.ts +38 -0
- package/dist/chunk-2ZTGQLYK.js +356 -0
- package/dist/chunk-2ZTGQLYK.js.map +1 -0
- package/dist/chunk-42PHHZUA.js +132 -0
- package/dist/chunk-42PHHZUA.js.map +1 -0
- package/dist/chunk-4SQA2UCV.js +26 -0
- package/dist/chunk-4SQA2UCV.js.map +1 -0
- package/dist/chunk-5GEX6ZSB.js +179 -0
- package/dist/chunk-5GEX6ZSB.js.map +1 -0
- package/dist/chunk-7ZMN7XJE.js +94 -0
- package/dist/chunk-7ZMN7XJE.js.map +1 -0
- package/dist/chunk-AMYIJSAZ.js +57 -0
- package/dist/chunk-AMYIJSAZ.js.map +1 -0
- package/dist/chunk-BGG2E6JD.js +10 -0
- package/dist/chunk-BGG2E6JD.js.map +1 -0
- package/dist/chunk-BZSFUEWM.js +43 -0
- package/dist/chunk-BZSFUEWM.js.map +1 -0
- package/dist/chunk-CPFHEPW4.js +139 -0
- package/dist/chunk-CPFHEPW4.js.map +1 -0
- package/dist/chunk-D43BWEZA.js +346 -0
- package/dist/chunk-D43BWEZA.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-JACGEMTF.js +43 -0
- package/dist/chunk-JACGEMTF.js.map +1 -0
- package/dist/chunk-JX7CLUCV.js +21 -0
- package/dist/chunk-JX7CLUCV.js.map +1 -0
- package/dist/chunk-KXDB56YW.js +39 -0
- package/dist/chunk-KXDB56YW.js.map +1 -0
- package/dist/chunk-KY6ZPWHO.js +112 -0
- package/dist/chunk-KY6ZPWHO.js.map +1 -0
- package/dist/chunk-NBDFQYUZ.js +7992 -0
- package/dist/chunk-NBDFQYUZ.js.map +1 -0
- package/dist/chunk-OGXNFXFA.js +196 -0
- package/dist/chunk-OGXNFXFA.js.map +1 -0
- package/dist/chunk-QTJ7VTJY.js +1994 -0
- package/dist/chunk-QTJ7VTJY.js.map +1 -0
- package/dist/chunk-UVSSQBDY.js +192 -0
- package/dist/chunk-UVSSQBDY.js.map +1 -0
- package/dist/chunk-Y45R3PQL.js +684 -0
- package/dist/chunk-Y45R3PQL.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +868 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +64 -0
- package/dist/client/index.js +409 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CRRO2376.js +10 -0
- package/dist/client-CRRO2376.js.map +1 -0
- package/dist/headless-Q7XHHZIW.js +143 -0
- package/dist/headless-Q7XHHZIW.js.map +1 -0
- package/dist/history-snip-64GYP4ZL.js +12 -0
- package/dist/history-snip-64GYP4ZL.js.map +1 -0
- package/dist/index.d.ts +1305 -418
- package/dist/index.js +384 -1757
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc/index.d.ts +54 -0
- package/dist/jsonrpc/index.js +34 -0
- package/dist/jsonrpc/index.js.map +1 -0
- package/dist/lsp/index.d.ts +36 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp-PS3BWIHC.js +8 -0
- package/dist/lsp-PS3BWIHC.js.map +1 -0
- package/dist/manager-DLXK63XC.js +8 -0
- package/dist/manager-DLXK63XC.js.map +1 -0
- package/dist/mcp/index.d.ts +111 -0
- package/dist/mcp/index.js +104 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp-auth-AEI2R4ZC.js +9 -0
- package/dist/mcp-auth-AEI2R4ZC.js.map +1 -0
- package/dist/ollama-YNXAYP3R.js +18 -0
- package/dist/ollama-YNXAYP3R.js.map +1 -0
- package/dist/provider-factory-34MSWJZ3.js +20 -0
- package/dist/provider-factory-34MSWJZ3.js.map +1 -0
- package/dist/providers/anthropic.d.ts +19 -0
- package/dist/providers/anthropic.js +33 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +39 -0
- package/dist/providers/bedrock.js +54 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/gemini.d.ts +16 -0
- package/dist/providers/gemini.js +224 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +18 -0
- package/dist/providers/openai.js +8 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +16 -0
- package/dist/providers/openrouter.js +23 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/vertex.d.ts +40 -0
- package/dist/providers/vertex.js +64 -0
- package/dist/providers/vertex.js.map +1 -0
- package/dist/render-GRN4ZSSW.js +14 -0
- package/dist/render-GRN4ZSSW.js.map +1 -0
- package/dist/resolve-XM52G7YE.js +14 -0
- package/dist/resolve-XM52G7YE.js.map +1 -0
- package/dist/server/index.d.ts +128 -0
- package/dist/server/index.js +626 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server-Cg1yWGaV.d.ts +96 -0
- package/dist/spinner-OJNR6NFO.js +8 -0
- package/dist/spinner-OJNR6NFO.js.map +1 -0
- package/dist/types-2kTLUCnD.d.ts +107 -0
- package/dist/types-3c88cRKH.d.ts +547 -0
- package/dist/types-CwKKucOF.d.ts +620 -0
- package/dist/types-DwdzmXfs.d.ts +107 -0
- package/dist/types-NIyVwQ4h.d.ts +109 -0
- package/dist/types-QwfylltH.d.ts +71 -0
- package/package.json +134 -6
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// src/utils/image-resizer.ts
|
|
2
|
+
var API_IMAGE_MAX_BASE64_SIZE = 5 * 1024 * 1024;
|
|
3
|
+
var IMAGE_TARGET_RAW_SIZE = Math.floor(
|
|
4
|
+
API_IMAGE_MAX_BASE64_SIZE * 3 / 4
|
|
5
|
+
);
|
|
6
|
+
var IMAGE_MAX_WIDTH = 8e3;
|
|
7
|
+
var IMAGE_MAX_HEIGHT = 8e3;
|
|
8
|
+
var _sharp;
|
|
9
|
+
async function getSharp() {
|
|
10
|
+
if (_sharp !== void 0) return _sharp;
|
|
11
|
+
try {
|
|
12
|
+
const moduleName = "sharp";
|
|
13
|
+
const mod = await import(
|
|
14
|
+
/* @vite-ignore */
|
|
15
|
+
moduleName
|
|
16
|
+
);
|
|
17
|
+
_sharp = mod.default ?? mod;
|
|
18
|
+
return _sharp;
|
|
19
|
+
} catch {
|
|
20
|
+
_sharp = null;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function maybeResizeAndDownsampleImageBuffer(imageBuffer, originalSize, ext) {
|
|
25
|
+
const sharp = await getSharp();
|
|
26
|
+
if (!sharp) {
|
|
27
|
+
if (imageBuffer.length > IMAGE_TARGET_RAW_SIZE) {
|
|
28
|
+
console.warn(
|
|
29
|
+
`[noumen] Image is ${(imageBuffer.length / 1024 / 1024).toFixed(1)}MB but sharp is not installed \u2014 cannot resize. Install sharp for image optimization.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
buffer: imageBuffer,
|
|
34
|
+
mediaType: extToMediaType(ext)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
let img = sharp(imageBuffer);
|
|
38
|
+
const meta = await img.metadata();
|
|
39
|
+
const width = meta.width ?? 0;
|
|
40
|
+
const height = meta.height ?? 0;
|
|
41
|
+
let needsResize = width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT || imageBuffer.length > IMAGE_TARGET_RAW_SIZE;
|
|
42
|
+
if (!needsResize) {
|
|
43
|
+
return {
|
|
44
|
+
buffer: imageBuffer,
|
|
45
|
+
mediaType: extToMediaType(ext),
|
|
46
|
+
dimensions: { width, height }
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT) {
|
|
50
|
+
const scale = Math.min(IMAGE_MAX_WIDTH / width, IMAGE_MAX_HEIGHT / height);
|
|
51
|
+
const newWidth = Math.round(width * scale);
|
|
52
|
+
const newHeight = Math.round(height * scale);
|
|
53
|
+
img = img.resize(newWidth, newHeight, { fit: "inside", withoutEnlargement: true });
|
|
54
|
+
}
|
|
55
|
+
const qualities = [85, 60, 40];
|
|
56
|
+
for (const q of qualities) {
|
|
57
|
+
const buf = await img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
58
|
+
if (buf.length <= IMAGE_TARGET_RAW_SIZE) {
|
|
59
|
+
const jpgMeta = await sharp(buf).metadata();
|
|
60
|
+
return {
|
|
61
|
+
buffer: buf,
|
|
62
|
+
mediaType: "jpeg",
|
|
63
|
+
dimensions: {
|
|
64
|
+
width: jpgMeta.width ?? 0,
|
|
65
|
+
height: jpgMeta.height ?? 0
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const pngBuf = await img.png({ palette: true, quality: 40 }).toBuffer();
|
|
71
|
+
if (pngBuf.length <= IMAGE_TARGET_RAW_SIZE) {
|
|
72
|
+
const pngMeta = await sharp(pngBuf).metadata();
|
|
73
|
+
return {
|
|
74
|
+
buffer: pngBuf,
|
|
75
|
+
mediaType: "png",
|
|
76
|
+
dimensions: {
|
|
77
|
+
width: pngMeta.width ?? 0,
|
|
78
|
+
height: pngMeta.height ?? 0
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const fallback = await img.jpeg({ quality: 40, mozjpeg: true }).toBuffer();
|
|
83
|
+
const fallbackMeta = await sharp(fallback).metadata();
|
|
84
|
+
return {
|
|
85
|
+
buffer: fallback,
|
|
86
|
+
mediaType: "jpeg",
|
|
87
|
+
dimensions: {
|
|
88
|
+
width: fallbackMeta.width ?? 0,
|
|
89
|
+
height: fallbackMeta.height ?? 0
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function maybeResizeAndDownsampleImageBlock(imageBlock) {
|
|
94
|
+
const imageBuffer = Buffer.from(imageBlock.data, "base64");
|
|
95
|
+
const ext = imageBlock.media_type.split("/")[1] || "png";
|
|
96
|
+
const resized = await maybeResizeAndDownsampleImageBuffer(
|
|
97
|
+
imageBuffer,
|
|
98
|
+
imageBuffer.length,
|
|
99
|
+
ext
|
|
100
|
+
);
|
|
101
|
+
return {
|
|
102
|
+
data: resized.buffer.toString("base64"),
|
|
103
|
+
media_type: `image/${resized.mediaType}`,
|
|
104
|
+
dimensions: resized.dimensions
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function compressImageBufferWithTokenLimit(imageBuffer, maxTokens, originalMediaType) {
|
|
108
|
+
const maxBase64Chars = Math.floor(maxTokens / 0.125);
|
|
109
|
+
const maxBytes = Math.floor(maxBase64Chars * 0.75);
|
|
110
|
+
const sharp = await getSharp();
|
|
111
|
+
if (!sharp) {
|
|
112
|
+
const base64 = imageBuffer.toString("base64");
|
|
113
|
+
return {
|
|
114
|
+
base64,
|
|
115
|
+
mediaType: originalMediaType?.split("/")[1] || "png"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const qualities = [85, 60, 40, 20];
|
|
119
|
+
for (const q of qualities) {
|
|
120
|
+
const buf2 = await sharp(imageBuffer).jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
121
|
+
if (buf2.length <= maxBytes) {
|
|
122
|
+
return { base64: buf2.toString("base64"), mediaType: "jpeg" };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const meta = await sharp(imageBuffer).metadata();
|
|
126
|
+
let w = meta.width ?? 800;
|
|
127
|
+
let h = meta.height ?? 600;
|
|
128
|
+
for (let scale = 0.75; scale >= 0.25; scale -= 0.25) {
|
|
129
|
+
const nw = Math.round(w * scale);
|
|
130
|
+
const nh = Math.round(h * scale);
|
|
131
|
+
const buf2 = await sharp(imageBuffer).resize(nw, nh, { fit: "inside" }).jpeg({ quality: 40, mozjpeg: true }).toBuffer();
|
|
132
|
+
if (buf2.length <= maxBytes) {
|
|
133
|
+
return { base64: buf2.toString("base64"), mediaType: "jpeg" };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const buf = await sharp(imageBuffer).resize(Math.round(w * 0.25), Math.round(h * 0.25), { fit: "inside" }).jpeg({ quality: 20, mozjpeg: true }).toBuffer();
|
|
137
|
+
return { base64: buf.toString("base64"), mediaType: "jpeg" };
|
|
138
|
+
}
|
|
139
|
+
function extToMediaType(ext) {
|
|
140
|
+
const lower = ext.toLowerCase().replace(/^\./, "");
|
|
141
|
+
switch (lower) {
|
|
142
|
+
case "jpg":
|
|
143
|
+
case "jpeg":
|
|
144
|
+
return "jpeg";
|
|
145
|
+
case "png":
|
|
146
|
+
return "png";
|
|
147
|
+
case "gif":
|
|
148
|
+
return "gif";
|
|
149
|
+
case "webp":
|
|
150
|
+
return "webp";
|
|
151
|
+
case "svg":
|
|
152
|
+
return "svg+xml";
|
|
153
|
+
default:
|
|
154
|
+
return "png";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
158
|
+
".png",
|
|
159
|
+
".jpg",
|
|
160
|
+
".jpeg",
|
|
161
|
+
".gif",
|
|
162
|
+
".webp",
|
|
163
|
+
".svg"
|
|
164
|
+
]);
|
|
165
|
+
function createImageMetadataText(dims) {
|
|
166
|
+
return `Image dimensions: ${dims.width}\xD7${dims.height}px`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
API_IMAGE_MAX_BASE64_SIZE,
|
|
171
|
+
IMAGE_MAX_WIDTH,
|
|
172
|
+
IMAGE_MAX_HEIGHT,
|
|
173
|
+
maybeResizeAndDownsampleImageBuffer,
|
|
174
|
+
maybeResizeAndDownsampleImageBlock,
|
|
175
|
+
compressImageBufferWithTokenLimit,
|
|
176
|
+
IMAGE_EXTENSIONS,
|
|
177
|
+
createImageMetadataText
|
|
178
|
+
};
|
|
179
|
+
//# sourceMappingURL=chunk-5GEX6ZSB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/image-resizer.ts"],"sourcesContent":["/**\n * Image resize / compress pipeline.\n *\n * Ported from claude-code's imageResizer.ts. Uses `sharp` (optional peer\n * dependency) for dimension caps, iterative quality reduction, and API\n * base64 size guards. Gracefully degrades when sharp is not installed.\n */\n\n/** Maximum base64-encoded image size (API enforced by most providers). */\nexport const API_IMAGE_MAX_BASE64_SIZE = 5 * 1024 * 1024; // 5 MB\n\n/** Target raw size before base64 encoding (base64 inflates by ~4/3). */\nexport const IMAGE_TARGET_RAW_SIZE = Math.floor(\n (API_IMAGE_MAX_BASE64_SIZE * 3) / 4,\n); // ~3.75 MB\n\nexport const IMAGE_MAX_WIDTH = 8000;\nexport const IMAGE_MAX_HEIGHT = 8000;\n\nexport interface ImageDimensions {\n width: number;\n height: number;\n}\n\nexport interface ResizedImage {\n buffer: Buffer;\n mediaType: string;\n dimensions?: ImageDimensions;\n}\n\nexport interface CompressedImageResult {\n base64: string;\n mediaType: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _sharp: any | null | undefined;\n\nasync function getSharp(): Promise<any | null> {\n if (_sharp !== undefined) return _sharp;\n try {\n // Dynamic import with variable to prevent TypeScript from resolving at compile time\n const moduleName = \"sharp\";\n const mod = await import(/* @vite-ignore */ moduleName);\n _sharp = mod.default ?? mod;\n return _sharp;\n } catch {\n _sharp = null;\n return null;\n }\n}\n\n/**\n * Resize and downsample an image buffer if it exceeds dimension or size\n * limits. Returns the (possibly unchanged) buffer with mediaType info.\n */\nexport async function maybeResizeAndDownsampleImageBuffer(\n imageBuffer: Buffer,\n originalSize: number,\n ext: string,\n): Promise<ResizedImage> {\n const sharp = await getSharp();\n if (!sharp) {\n if (imageBuffer.length > IMAGE_TARGET_RAW_SIZE) {\n console.warn(\n `[noumen] Image is ${(imageBuffer.length / 1024 / 1024).toFixed(1)}MB ` +\n `but sharp is not installed — cannot resize. Install sharp for image optimization.`,\n );\n }\n return {\n buffer: imageBuffer,\n mediaType: extToMediaType(ext),\n };\n }\n\n let img = sharp(imageBuffer);\n const meta = await img.metadata();\n const width = meta.width ?? 0;\n const height = meta.height ?? 0;\n\n let needsResize =\n width > IMAGE_MAX_WIDTH ||\n height > IMAGE_MAX_HEIGHT ||\n imageBuffer.length > IMAGE_TARGET_RAW_SIZE;\n\n if (!needsResize) {\n return {\n buffer: imageBuffer,\n mediaType: extToMediaType(ext),\n dimensions: { width, height },\n };\n }\n\n // Dimension cap\n if (width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT) {\n const scale = Math.min(IMAGE_MAX_WIDTH / width, IMAGE_MAX_HEIGHT / height);\n const newWidth = Math.round(width * scale);\n const newHeight = Math.round(height * scale);\n img = img.resize(newWidth, newHeight, { fit: \"inside\", withoutEnlargement: true });\n }\n\n // Try JPEG at decreasing quality levels\n const qualities = [85, 60, 40];\n for (const q of qualities) {\n const buf = await img.jpeg({ quality: q, mozjpeg: true }).toBuffer();\n if (buf.length <= IMAGE_TARGET_RAW_SIZE) {\n const jpgMeta = await sharp(buf).metadata();\n return {\n buffer: buf,\n mediaType: \"jpeg\",\n dimensions: {\n width: jpgMeta.width ?? 0,\n height: jpgMeta.height ?? 0,\n },\n };\n }\n }\n\n // Try PNG palette mode as last resort\n const pngBuf = await img.png({ palette: true, quality: 40 }).toBuffer();\n if (pngBuf.length <= IMAGE_TARGET_RAW_SIZE) {\n const pngMeta = await sharp(pngBuf).metadata();\n return {\n buffer: pngBuf,\n mediaType: \"png\",\n dimensions: {\n width: pngMeta.width ?? 0,\n height: pngMeta.height ?? 0,\n },\n };\n }\n\n // Return best-effort JPEG Q40 even if over budget\n const fallback = await img.jpeg({ quality: 40, mozjpeg: true }).toBuffer();\n const fallbackMeta = await sharp(fallback).metadata();\n return {\n buffer: fallback,\n mediaType: \"jpeg\",\n dimensions: {\n width: fallbackMeta.width ?? 0,\n height: fallbackMeta.height ?? 0,\n },\n };\n}\n\n/**\n * Decode base64 image block, resize, re-encode.\n */\nexport async function maybeResizeAndDownsampleImageBlock(imageBlock: {\n data: string;\n media_type: string;\n}): Promise<{\n data: string;\n media_type: string;\n dimensions?: ImageDimensions;\n}> {\n const imageBuffer = Buffer.from(imageBlock.data, \"base64\");\n const ext = imageBlock.media_type.split(\"/\")[1] || \"png\";\n\n const resized = await maybeResizeAndDownsampleImageBuffer(\n imageBuffer,\n imageBuffer.length,\n ext,\n );\n\n return {\n data: resized.buffer.toString(\"base64\"),\n media_type: `image/${resized.mediaType}`,\n dimensions: resized.dimensions,\n };\n}\n\n/**\n * Compress an image to fit within a token budget.\n * Token formula: tokens ≈ base64_chars × 0.125\n */\nexport async function compressImageBufferWithTokenLimit(\n imageBuffer: Buffer,\n maxTokens: number,\n originalMediaType?: string,\n): Promise<CompressedImageResult> {\n const maxBase64Chars = Math.floor(maxTokens / 0.125);\n const maxBytes = Math.floor(maxBase64Chars * 0.75);\n\n const sharp = await getSharp();\n if (!sharp) {\n const base64 = imageBuffer.toString(\"base64\");\n return {\n base64,\n mediaType: originalMediaType?.split(\"/\")[1] || \"png\",\n };\n }\n\n const qualities = [85, 60, 40, 20];\n for (const q of qualities) {\n const buf = await sharp(imageBuffer)\n .jpeg({ quality: q, mozjpeg: true })\n .toBuffer();\n if (buf.length <= maxBytes) {\n return { base64: buf.toString(\"base64\"), mediaType: \"jpeg\" };\n }\n }\n\n // Progressive dimension reduction\n const meta = await sharp(imageBuffer).metadata();\n let w = meta.width ?? 800;\n let h = meta.height ?? 600;\n\n for (let scale = 0.75; scale >= 0.25; scale -= 0.25) {\n const nw = Math.round(w * scale);\n const nh = Math.round(h * scale);\n const buf = await sharp(imageBuffer)\n .resize(nw, nh, { fit: \"inside\" })\n .jpeg({ quality: 40, mozjpeg: true })\n .toBuffer();\n if (buf.length <= maxBytes) {\n return { base64: buf.toString(\"base64\"), mediaType: \"jpeg\" };\n }\n }\n\n // Best effort\n const buf = await sharp(imageBuffer)\n .resize(Math.round(w * 0.25), Math.round(h * 0.25), { fit: \"inside\" })\n .jpeg({ quality: 20, mozjpeg: true })\n .toBuffer();\n return { base64: buf.toString(\"base64\"), mediaType: \"jpeg\" };\n}\n\nfunction extToMediaType(ext: string): string {\n const lower = ext.toLowerCase().replace(/^\\./, \"\");\n switch (lower) {\n case \"jpg\":\n case \"jpeg\":\n return \"jpeg\";\n case \"png\":\n return \"png\";\n case \"gif\":\n return \"gif\";\n case \"webp\":\n return \"webp\";\n case \"svg\":\n return \"svg+xml\";\n default:\n return \"png\";\n }\n}\n\nexport const IMAGE_EXTENSIONS = new Set([\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".svg\",\n]);\n\n/**\n * Create dimension metadata text for the model (helps with coordinate reasoning).\n */\nexport function createImageMetadataText(dims: ImageDimensions): string {\n return `Image dimensions: ${dims.width}×${dims.height}px`;\n}\n"],"mappings":"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","names":["buf"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/providers/resolve.ts
|
|
2
|
+
var ENV_KEY_MAP = {
|
|
3
|
+
openai: "OPENAI_API_KEY",
|
|
4
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
5
|
+
gemini: "GEMINI_API_KEY",
|
|
6
|
+
openrouter: "OPENROUTER_API_KEY"
|
|
7
|
+
};
|
|
8
|
+
var DEFAULT_MODELS = {
|
|
9
|
+
openai: "gpt-5.4",
|
|
10
|
+
anthropic: "claude-opus-4.6",
|
|
11
|
+
gemini: "gemini-2.5-flash",
|
|
12
|
+
openrouter: "anthropic/claude-opus-4.6",
|
|
13
|
+
bedrock: "us.anthropic.claude-opus-4.6-v1:0",
|
|
14
|
+
vertex: "claude-opus-4.6",
|
|
15
|
+
ollama: "qwen2.5-coder:32b"
|
|
16
|
+
};
|
|
17
|
+
var SUPPORTED_PROVIDERS = Object.keys(DEFAULT_MODELS);
|
|
18
|
+
function getProviderEnvKey(name) {
|
|
19
|
+
const envVar = ENV_KEY_MAP[name];
|
|
20
|
+
return envVar ? process.env[envVar] : void 0;
|
|
21
|
+
}
|
|
22
|
+
async function resolveProvider(input, opts) {
|
|
23
|
+
if (typeof input !== "string") return input;
|
|
24
|
+
const name = input;
|
|
25
|
+
if (!SUPPORTED_PROVIDERS.includes(name)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Unknown provider "${name}". Supported: ${SUPPORTED_PROVIDERS.join(", ")}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const key = opts?.apiKey ?? getProviderEnvKey(name) ?? process.env.NOUMEN_API_KEY;
|
|
31
|
+
switch (name) {
|
|
32
|
+
case "openai": {
|
|
33
|
+
if (!key) throw new Error("OpenAI requires an API key. Set OPENAI_API_KEY or pass apiKey.");
|
|
34
|
+
const { OpenAIProvider } = await import("./providers/openai.js");
|
|
35
|
+
return new OpenAIProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });
|
|
36
|
+
}
|
|
37
|
+
case "anthropic": {
|
|
38
|
+
if (!key) throw new Error("Anthropic requires an API key. Set ANTHROPIC_API_KEY or pass apiKey.");
|
|
39
|
+
const { AnthropicProvider } = await import("./providers/anthropic.js");
|
|
40
|
+
return new AnthropicProvider({ apiKey: key, model: opts?.model });
|
|
41
|
+
}
|
|
42
|
+
case "gemini": {
|
|
43
|
+
if (!key) throw new Error("Gemini requires an API key. Set GEMINI_API_KEY or pass apiKey.");
|
|
44
|
+
const { GeminiProvider } = await import("./providers/gemini.js");
|
|
45
|
+
return new GeminiProvider({ apiKey: key, model: opts?.model });
|
|
46
|
+
}
|
|
47
|
+
case "openrouter": {
|
|
48
|
+
if (!key) throw new Error("OpenRouter requires an API key. Set OPENROUTER_API_KEY or pass apiKey.");
|
|
49
|
+
const { OpenRouterProvider } = await import("./providers/openrouter.js");
|
|
50
|
+
return new OpenRouterProvider({ apiKey: key, model: opts?.model, appName: "noumen" });
|
|
51
|
+
}
|
|
52
|
+
case "bedrock": {
|
|
53
|
+
const { BedrockAnthropicProvider } = await import("./providers/bedrock.js");
|
|
54
|
+
return new BedrockAnthropicProvider({ model: opts?.model });
|
|
55
|
+
}
|
|
56
|
+
case "vertex": {
|
|
57
|
+
const { VertexAnthropicProvider } = await import("./providers/vertex.js");
|
|
58
|
+
return new VertexAnthropicProvider({ model: opts?.model });
|
|
59
|
+
}
|
|
60
|
+
case "ollama": {
|
|
61
|
+
const { OllamaProvider } = await import("./ollama-YNXAYP3R.js");
|
|
62
|
+
const base = opts?.baseURL ?? (process.env.OLLAMA_HOST ? `${process.env.OLLAMA_HOST.replace(/\/+$/, "")}/v1` : void 0);
|
|
63
|
+
return new OllamaProvider({ model: opts?.model, baseURL: base });
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
throw new Error(`Unhandled provider: ${name}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function detectProvider() {
|
|
70
|
+
if (process.env.ANTHROPIC_API_KEY) return "anthropic";
|
|
71
|
+
if (process.env.OPENAI_API_KEY) return "openai";
|
|
72
|
+
if (process.env.GEMINI_API_KEY) return "gemini";
|
|
73
|
+
if (process.env.OPENROUTER_API_KEY) return "openrouter";
|
|
74
|
+
if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE) return "bedrock";
|
|
75
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GCLOUD_PROJECT) return "vertex";
|
|
76
|
+
if (process.env.OLLAMA_HOST) return "ollama";
|
|
77
|
+
try {
|
|
78
|
+
const host = process.env.OLLAMA_HOST ?? "http://localhost:11434";
|
|
79
|
+
const res = await fetch(`${host.replace(/\/+$/, "")}/api/tags`, {
|
|
80
|
+
signal: AbortSignal.timeout(500)
|
|
81
|
+
});
|
|
82
|
+
if (res.ok) return "ollama";
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
DEFAULT_MODELS,
|
|
90
|
+
SUPPORTED_PROVIDERS,
|
|
91
|
+
resolveProvider,
|
|
92
|
+
detectProvider
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=chunk-7ZMN7XJE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/resolve.ts"],"sourcesContent":["import type { AIProvider } from \"./types.js\";\n\nexport type ProviderName =\n | \"openai\"\n | \"anthropic\"\n | \"gemini\"\n | \"openrouter\"\n | \"bedrock\"\n | \"vertex\"\n | \"ollama\";\n\nconst ENV_KEY_MAP: Record<string, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n gemini: \"GEMINI_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n};\n\nexport const DEFAULT_MODELS: Record<string, string> = {\n openai: \"gpt-5.4\",\n anthropic: \"claude-opus-4.6\",\n gemini: \"gemini-2.5-flash\",\n openrouter: \"anthropic/claude-opus-4.6\",\n bedrock: \"us.anthropic.claude-opus-4.6-v1:0\",\n vertex: \"claude-opus-4.6\",\n ollama: \"qwen2.5-coder:32b\",\n};\n\nexport const SUPPORTED_PROVIDERS: ProviderName[] = Object.keys(DEFAULT_MODELS) as ProviderName[];\n\nfunction getProviderEnvKey(name: string): string | undefined {\n const envVar = ENV_KEY_MAP[name];\n return envVar ? process.env[envVar] : undefined;\n}\n\nexport interface ResolveProviderOptions {\n apiKey?: string;\n model?: string;\n baseURL?: string;\n}\n\n/**\n * Resolve a provider from a name string or pass through an AIProvider instance.\n * API key resolution order:\n * 1. Explicit apiKey option\n * 2. Provider-specific env var (OPENAI_API_KEY, etc.)\n * 3. NOUMEN_API_KEY generic env var\n */\nexport async function resolveProvider(\n input: AIProvider | ProviderName,\n opts?: ResolveProviderOptions,\n): Promise<AIProvider> {\n if (typeof input !== \"string\") return input;\n\n const name = input;\n if (!SUPPORTED_PROVIDERS.includes(name)) {\n throw new Error(\n `Unknown provider \"${name}\". Supported: ${SUPPORTED_PROVIDERS.join(\", \")}`,\n );\n }\n\n const key =\n opts?.apiKey ??\n getProviderEnvKey(name) ??\n process.env.NOUMEN_API_KEY;\n\n switch (name) {\n case \"openai\": {\n if (!key) throw new Error(\"OpenAI requires an API key. Set OPENAI_API_KEY or pass apiKey.\");\n const { OpenAIProvider } = await import(\"./openai.js\");\n return new OpenAIProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"anthropic\": {\n if (!key) throw new Error(\"Anthropic requires an API key. Set ANTHROPIC_API_KEY or pass apiKey.\");\n const { AnthropicProvider } = await import(\"./anthropic.js\");\n return new AnthropicProvider({ apiKey: key, model: opts?.model });\n }\n case \"gemini\": {\n if (!key) throw new Error(\"Gemini requires an API key. Set GEMINI_API_KEY or pass apiKey.\");\n const { GeminiProvider } = await import(\"./gemini.js\");\n return new GeminiProvider({ apiKey: key, model: opts?.model });\n }\n case \"openrouter\": {\n if (!key) throw new Error(\"OpenRouter requires an API key. Set OPENROUTER_API_KEY or pass apiKey.\");\n const { OpenRouterProvider } = await import(\"./openrouter.js\");\n return new OpenRouterProvider({ apiKey: key, model: opts?.model, appName: \"noumen\" });\n }\n case \"bedrock\": {\n const { BedrockAnthropicProvider } = await import(\"./bedrock.js\");\n return new BedrockAnthropicProvider({ model: opts?.model });\n }\n case \"vertex\": {\n const { VertexAnthropicProvider } = await import(\"./vertex.js\");\n return new VertexAnthropicProvider({ model: opts?.model });\n }\n case \"ollama\": {\n const { OllamaProvider } = await import(\"./ollama.js\");\n const base = opts?.baseURL ?? (process.env.OLLAMA_HOST\n ? `${process.env.OLLAMA_HOST.replace(/\\/+$/, \"\")}/v1`\n : undefined);\n return new OllamaProvider({ model: opts?.model, baseURL: base });\n }\n default:\n throw new Error(`Unhandled provider: ${name}`);\n }\n}\n\n/**\n * Auto-detect provider from available environment variables.\n */\nexport async function detectProvider(): Promise<ProviderName | undefined> {\n if (process.env.ANTHROPIC_API_KEY) return \"anthropic\";\n if (process.env.OPENAI_API_KEY) return \"openai\";\n if (process.env.GEMINI_API_KEY) return \"gemini\";\n if (process.env.OPENROUTER_API_KEY) return \"openrouter\";\n if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE) return \"bedrock\";\n if (process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GCLOUD_PROJECT) return \"vertex\";\n if (process.env.OLLAMA_HOST) return \"ollama\";\n\n try {\n const host = process.env.OLLAMA_HOST ?? \"http://localhost:11434\";\n const res = await fetch(`${host.replace(/\\/+$/, \"\")}/api/tags`, {\n signal: AbortSignal.timeout(500),\n });\n if (res.ok) return \"ollama\";\n } catch { /* not running */ }\n\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/jsonrpc/index.ts
|
|
2
|
+
var JSONRPC_VERSION = "2.0";
|
|
3
|
+
var PARSE_ERROR = -32700;
|
|
4
|
+
var INVALID_REQUEST = -32600;
|
|
5
|
+
var METHOD_NOT_FOUND = -32601;
|
|
6
|
+
var INVALID_PARAMS = -32602;
|
|
7
|
+
var INTERNAL_ERROR = -32603;
|
|
8
|
+
function isRequest(msg) {
|
|
9
|
+
return "method" in msg && "id" in msg;
|
|
10
|
+
}
|
|
11
|
+
function isNotification(msg) {
|
|
12
|
+
return "method" in msg && !("id" in msg);
|
|
13
|
+
}
|
|
14
|
+
function isResponse(msg) {
|
|
15
|
+
return "result" in msg || "error" in msg;
|
|
16
|
+
}
|
|
17
|
+
function formatResponse(id, result) {
|
|
18
|
+
return { jsonrpc: JSONRPC_VERSION, id, result };
|
|
19
|
+
}
|
|
20
|
+
function formatError(id, code, message, data) {
|
|
21
|
+
return {
|
|
22
|
+
jsonrpc: JSONRPC_VERSION,
|
|
23
|
+
id,
|
|
24
|
+
error: { code, message, ...data !== void 0 ? { data } : {} }
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function formatNotification(method, params) {
|
|
28
|
+
return { jsonrpc: JSONRPC_VERSION, method, ...params !== void 0 ? { params } : {} };
|
|
29
|
+
}
|
|
30
|
+
function formatRequest(id, method, params) {
|
|
31
|
+
return { jsonrpc: JSONRPC_VERSION, id, method, ...params !== void 0 ? { params } : {} };
|
|
32
|
+
}
|
|
33
|
+
function parseMessage(raw) {
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
36
|
+
throw new Error("JSON-RPC message must be an object");
|
|
37
|
+
}
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
JSONRPC_VERSION,
|
|
43
|
+
PARSE_ERROR,
|
|
44
|
+
INVALID_REQUEST,
|
|
45
|
+
METHOD_NOT_FOUND,
|
|
46
|
+
INVALID_PARAMS,
|
|
47
|
+
INTERNAL_ERROR,
|
|
48
|
+
isRequest,
|
|
49
|
+
isNotification,
|
|
50
|
+
isResponse,
|
|
51
|
+
formatResponse,
|
|
52
|
+
formatError,
|
|
53
|
+
formatNotification,
|
|
54
|
+
formatRequest,
|
|
55
|
+
parseMessage
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=chunk-AMYIJSAZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/jsonrpc/index.ts"],"sourcesContent":["/**\n * Minimal JSON-RPC 2.0 types and helpers shared by ACP and A2A adapters.\n *\n * Spec: https://www.jsonrpc.org/specification\n */\n\nexport const JSONRPC_VERSION = \"2.0\" as const;\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface JsonRpcRequest {\n jsonrpc: typeof JSONRPC_VERSION;\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: typeof JSONRPC_VERSION;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcSuccessResponse {\n jsonrpc: typeof JSONRPC_VERSION;\n id: string | number;\n result: unknown;\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: typeof JSONRPC_VERSION;\n id: string | number | null;\n error: JsonRpcErrorObject;\n}\n\nexport interface JsonRpcErrorObject {\n code: number;\n message: string;\n data?: unknown;\n}\n\nexport type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResponse;\n\n// ── Standard error codes ────────────────────────────────────────────────────\n\nexport const PARSE_ERROR = -32700;\nexport const INVALID_REQUEST = -32600;\nexport const METHOD_NOT_FOUND = -32601;\nexport const INVALID_PARAMS = -32602;\nexport const INTERNAL_ERROR = -32603;\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return \"method\" in msg && \"id\" in msg;\n}\n\nexport function isNotification(msg: JsonRpcMessage): msg is JsonRpcNotification {\n return \"method\" in msg && !(\"id\" in msg);\n}\n\nexport function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse {\n return \"result\" in msg || \"error\" in msg;\n}\n\nexport function formatResponse(\n id: string | number,\n result: unknown,\n): JsonRpcSuccessResponse {\n return { jsonrpc: JSONRPC_VERSION, id, result };\n}\n\nexport function formatError(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): JsonRpcErrorResponse {\n return {\n jsonrpc: JSONRPC_VERSION,\n id,\n error: { code, message, ...(data !== undefined ? { data } : {}) },\n };\n}\n\nexport function formatNotification(\n method: string,\n params?: unknown,\n): JsonRpcNotification {\n return { jsonrpc: JSONRPC_VERSION, method, ...(params !== undefined ? { params } : {}) };\n}\n\nexport function formatRequest(\n id: string | number,\n method: string,\n params?: unknown,\n): JsonRpcRequest {\n return { jsonrpc: JSONRPC_VERSION, id, method, ...(params !== undefined ? { params } : {}) };\n}\n\n/**\n * Parse a raw string into a JSON-RPC message. Throws on invalid JSON.\n * Does NOT validate against the full JSON-RPC schema — callers should\n * check `isRequest` / `isNotification` / `isResponse` after parsing.\n */\nexport function parseMessage(raw: string): JsonRpcMessage {\n const parsed = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new Error(\"JSON-RPC message must be an object\");\n }\n return parsed as JsonRpcMessage;\n}\n"],"mappings":";AAMO,IAAM,kBAAkB;AA4CxB,IAAM,cAAc;AACpB,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AACzB,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAIvB,SAAS,UAAU,KAA4C;AACpE,SAAO,YAAY,OAAO,QAAQ;AACpC;AAEO,SAAS,eAAe,KAAiD;AAC9E,SAAO,YAAY,OAAO,EAAE,QAAQ;AACtC;AAEO,SAAS,WAAW,KAA6C;AACtE,SAAO,YAAY,OAAO,WAAW;AACvC;AAEO,SAAS,eACd,IACA,QACwB;AACxB,SAAO,EAAE,SAAS,iBAAiB,IAAI,OAAO;AAChD;AAEO,SAAS,YACd,IACA,MACA,SACA,MACsB;AACtB,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,OAAO,EAAE,MAAM,SAAS,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC,EAAG;AAAA,EAClE;AACF;AAEO,SAAS,mBACd,QACA,QACqB;AACrB,SAAO,EAAE,SAAS,iBAAiB,QAAQ,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC,EAAG;AACzF;AAEO,SAAS,cACd,IACA,QACA,QACgB;AAChB,SAAO,EAAE,SAAS,iBAAiB,IAAI,QAAQ,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC,EAAG;AAC7F;AAOO,SAAS,aAAa,KAA6B;AACxD,QAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/uuid.ts"],"sourcesContent":["import { v4 } from \"uuid\";\n\nexport type UUID = string & { readonly __brand: unique symbol };\n\nexport function generateUUID(): UUID {\n return v4() as UUID;\n}\n"],"mappings":";AAAA,SAAS,UAAU;AAIZ,SAAS,eAAqB;AACnC,SAAO,GAAG;AACZ;","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildMcpToolName
|
|
3
|
+
} from "./chunk-4SQA2UCV.js";
|
|
4
|
+
|
|
5
|
+
// src/tools/mcp-auth.ts
|
|
6
|
+
function createMcpAuthTool(serverName, mcpManager) {
|
|
7
|
+
const toolName = buildMcpToolName(serverName, "authenticate");
|
|
8
|
+
return {
|
|
9
|
+
name: toolName,
|
|
10
|
+
description: `Authenticate with the MCP server "${serverName}". Call this tool when the server requires OAuth authentication before its tools can be used. Returns an authorization URL for the user to visit, or confirms that authentication succeeded.`,
|
|
11
|
+
isReadOnly: true,
|
|
12
|
+
isConcurrencySafe: false,
|
|
13
|
+
parameters: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {}
|
|
16
|
+
},
|
|
17
|
+
async call(_args, _ctx) {
|
|
18
|
+
try {
|
|
19
|
+
const result = await mcpManager.performAuth(serverName);
|
|
20
|
+
if (result.authUrl) {
|
|
21
|
+
return {
|
|
22
|
+
content: `Authentication required for MCP server "${serverName}". Please visit: ${result.authUrl}
|
|
23
|
+
|
|
24
|
+
After authorizing, the server will be reconnected automatically.`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
content: `Successfully authenticated with MCP server "${serverName}". Its tools are now available.`
|
|
29
|
+
};
|
|
30
|
+
} catch (err) {
|
|
31
|
+
return {
|
|
32
|
+
content: `Failed to authenticate with MCP server "${serverName}": ${err instanceof Error ? err.message : String(err)}`,
|
|
33
|
+
isError: true
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
createMcpAuthTool
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=chunk-BZSFUEWM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tools/mcp-auth.ts"],"sourcesContent":["import type { Tool, ToolResult, ToolContext } from \"./types.js\";\nimport type { McpClientManager } from \"../mcp/client.js\";\nimport { buildMcpToolName } from \"../mcp/normalization.js\";\n\n/**\n * Create an MCP authenticate tool for a specific server that requires OAuth.\n * The agent calls this tool to trigger interactive authentication, then the\n * server is reconnected and its tools become available.\n */\nexport function createMcpAuthTool(\n serverName: string,\n mcpManager: McpClientManager,\n): Tool {\n const toolName = buildMcpToolName(serverName, \"authenticate\");\n\n return {\n name: toolName,\n description:\n `Authenticate with the MCP server \"${serverName}\". ` +\n \"Call this tool when the server requires OAuth authentication \" +\n \"before its tools can be used. Returns an authorization URL for \" +\n \"the user to visit, or confirms that authentication succeeded.\",\n isReadOnly: true,\n isConcurrencySafe: false,\n parameters: {\n type: \"object\",\n properties: {},\n },\n\n async call(\n _args: Record<string, unknown>,\n _ctx: ToolContext,\n ): Promise<ToolResult> {\n try {\n const result = await mcpManager.performAuth(serverName);\n\n if (result.authUrl) {\n return {\n content:\n `Authentication required for MCP server \"${serverName}\". ` +\n `Please visit: ${result.authUrl}\\n\\n` +\n \"After authorizing, the server will be reconnected automatically.\",\n };\n }\n\n return {\n content:\n `Successfully authenticated with MCP server \"${serverName}\". ` +\n \"Its tools are now available.\",\n };\n } catch (err) {\n return {\n content:\n `Failed to authenticate with MCP server \"${serverName}\": ` +\n `${err instanceof Error ? err.message : String(err)}`,\n isError: true,\n };\n }\n },\n };\n}\n"],"mappings":";;;;;AASO,SAAS,kBACd,YACA,YACM;AACN,QAAM,WAAW,iBAAiB,YAAY,cAAc;AAE5D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aACE,qCAAqC,UAAU;AAAA,IAIjD,YAAY;AAAA,IACZ,mBAAmB;AAAA,IACnB,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,IACf;AAAA,IAEA,MAAM,KACJ,OACA,MACqB;AACrB,UAAI;AACF,cAAM,SAAS,MAAM,WAAW,YAAY,UAAU;AAEtD,YAAI,OAAO,SAAS;AAClB,iBAAO;AAAA,YACL,SACE,2CAA2C,UAAU,oBACpC,OAAO,OAAO;AAAA;AAAA;AAAA,UAEnC;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SACE,+CAA+C,UAAU;AAAA,QAE7D;AAAA,MACF,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,SACE,2CAA2C,UAAU,MAClD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,UACrD,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/providers/openai.ts
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
var O_SERIES_PATTERN = /^o[1-9]/;
|
|
4
|
+
var OpenAIProvider = class _OpenAIProvider {
|
|
5
|
+
client;
|
|
6
|
+
defaultModel;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
this.client = new OpenAI({
|
|
9
|
+
apiKey: opts.apiKey ?? "not-needed",
|
|
10
|
+
baseURL: opts.baseURL,
|
|
11
|
+
defaultHeaders: opts.defaultHeaders
|
|
12
|
+
});
|
|
13
|
+
this.defaultModel = opts.model ?? "gpt-5.4";
|
|
14
|
+
}
|
|
15
|
+
async *chat(params) {
|
|
16
|
+
const messages = this.buildMessages(params.system, params.messages);
|
|
17
|
+
const model = params.model ?? this.defaultModel;
|
|
18
|
+
const isOSeries = O_SERIES_PATTERN.test(model);
|
|
19
|
+
const createParams = {
|
|
20
|
+
model,
|
|
21
|
+
messages,
|
|
22
|
+
tools: params.tools?.map((t) => ({
|
|
23
|
+
type: "function",
|
|
24
|
+
function: t.function
|
|
25
|
+
})),
|
|
26
|
+
stream: true,
|
|
27
|
+
stream_options: { include_usage: true }
|
|
28
|
+
};
|
|
29
|
+
if (isOSeries && params.thinking?.type === "enabled") {
|
|
30
|
+
createParams.reasoning_effort = "high";
|
|
31
|
+
} else {
|
|
32
|
+
createParams.max_tokens = params.max_tokens;
|
|
33
|
+
createParams.temperature = params.temperature;
|
|
34
|
+
}
|
|
35
|
+
if (params.outputFormat?.type === "json_schema") {
|
|
36
|
+
createParams.response_format = {
|
|
37
|
+
type: "json_schema",
|
|
38
|
+
json_schema: {
|
|
39
|
+
name: params.outputFormat.name ?? "response",
|
|
40
|
+
schema: params.outputFormat.schema,
|
|
41
|
+
strict: params.outputFormat.strict ?? false
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
} else if (params.outputFormat?.type === "json_object") {
|
|
45
|
+
createParams.response_format = {
|
|
46
|
+
type: "json_object"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const stream = await this.client.chat.completions.create(createParams);
|
|
50
|
+
for await (const chunk of stream) {
|
|
51
|
+
const usage = chunk.usage;
|
|
52
|
+
let mappedUsage;
|
|
53
|
+
if (usage) {
|
|
54
|
+
const u = usage;
|
|
55
|
+
const promptDetails = u.prompt_tokens_details;
|
|
56
|
+
const completionDetails = u.completion_tokens_details;
|
|
57
|
+
mappedUsage = {
|
|
58
|
+
prompt_tokens: usage.prompt_tokens,
|
|
59
|
+
completion_tokens: usage.completion_tokens,
|
|
60
|
+
total_tokens: usage.total_tokens,
|
|
61
|
+
cache_read_tokens: promptDetails?.cached_tokens,
|
|
62
|
+
thinking_tokens: completionDetails?.reasoning_tokens
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
yield {
|
|
66
|
+
id: chunk.id,
|
|
67
|
+
model: chunk.model,
|
|
68
|
+
choices: chunk.choices.map((c) => ({
|
|
69
|
+
index: c.index,
|
|
70
|
+
delta: {
|
|
71
|
+
role: c.delta.role,
|
|
72
|
+
content: c.delta.content,
|
|
73
|
+
tool_calls: c.delta.tool_calls?.map((tc) => ({
|
|
74
|
+
index: tc.index,
|
|
75
|
+
id: tc.id,
|
|
76
|
+
type: tc.type,
|
|
77
|
+
function: tc.function ? {
|
|
78
|
+
name: tc.function.name,
|
|
79
|
+
arguments: tc.function.arguments
|
|
80
|
+
} : void 0
|
|
81
|
+
}))
|
|
82
|
+
},
|
|
83
|
+
finish_reason: c.finish_reason
|
|
84
|
+
})),
|
|
85
|
+
usage: mappedUsage
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
static contentPartsToOpenAI(parts) {
|
|
90
|
+
return parts.map((part) => {
|
|
91
|
+
if (part.type === "text") {
|
|
92
|
+
return { type: "text", text: part.text };
|
|
93
|
+
}
|
|
94
|
+
if (part.type === "image") {
|
|
95
|
+
return {
|
|
96
|
+
type: "image_url",
|
|
97
|
+
image_url: { url: `data:${part.media_type};base64,${part.data}` }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { type: "image_url", image_url: { url: part.url } };
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
buildMessages(system, messages) {
|
|
104
|
+
const result = [];
|
|
105
|
+
if (system) {
|
|
106
|
+
result.push({ role: "system", content: system });
|
|
107
|
+
}
|
|
108
|
+
for (const msg of messages) {
|
|
109
|
+
if (msg.role === "tool") {
|
|
110
|
+
const content = Array.isArray(msg.content) ? _OpenAIProvider.contentPartsToOpenAI(msg.content) : msg.content;
|
|
111
|
+
result.push({
|
|
112
|
+
role: "tool",
|
|
113
|
+
tool_call_id: msg.tool_call_id,
|
|
114
|
+
content
|
|
115
|
+
});
|
|
116
|
+
} else if (msg.role === "assistant") {
|
|
117
|
+
const entry = {
|
|
118
|
+
role: "assistant",
|
|
119
|
+
content: msg.content
|
|
120
|
+
};
|
|
121
|
+
if (msg.tool_calls) {
|
|
122
|
+
entry.tool_calls = msg.tool_calls;
|
|
123
|
+
}
|
|
124
|
+
result.push(entry);
|
|
125
|
+
} else if (msg.role === "user") {
|
|
126
|
+
const content = Array.isArray(msg.content) ? _OpenAIProvider.contentPartsToOpenAI(msg.content) : msg.content;
|
|
127
|
+
result.push({ role: "user", content });
|
|
128
|
+
} else {
|
|
129
|
+
result.push({ role: msg.role, content: msg.content });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
OpenAIProvider
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=chunk-CPFHEPW4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/openai.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n AIProvider,\n ChatParams,\n ChatStreamChunk,\n} from \"./types.js\";\nimport type { ChatMessage, ContentPart } from \"../session/types.js\";\n\nexport interface OpenAIProviderOptions {\n apiKey?: string;\n baseURL?: string;\n model?: string;\n defaultHeaders?: Record<string, string | undefined>;\n}\n\nconst O_SERIES_PATTERN = /^o[1-9]/;\n\nexport class OpenAIProvider implements AIProvider {\n private client: OpenAI;\n private defaultModel: string;\n\n constructor(opts: OpenAIProviderOptions) {\n this.client = new OpenAI({\n apiKey: opts.apiKey ?? \"not-needed\",\n baseURL: opts.baseURL,\n defaultHeaders: opts.defaultHeaders,\n });\n this.defaultModel = opts.model ?? \"gpt-5.4\";\n }\n\n async *chat(params: ChatParams): AsyncIterable<ChatStreamChunk> {\n const messages = this.buildMessages(params.system, params.messages);\n const model = params.model ?? this.defaultModel;\n const isOSeries = O_SERIES_PATTERN.test(model);\n\n const createParams: OpenAI.ChatCompletionCreateParamsStreaming = {\n model,\n messages: messages as unknown as OpenAI.ChatCompletionMessageParam[],\n tools: params.tools?.map((t) => ({\n type: \"function\" as const,\n function: t.function,\n })),\n stream: true,\n stream_options: { include_usage: true },\n };\n\n if (isOSeries && params.thinking?.type === \"enabled\") {\n (createParams as unknown as Record<string, unknown>).reasoning_effort = \"high\";\n } else {\n createParams.max_tokens = params.max_tokens;\n createParams.temperature = params.temperature;\n }\n\n if (params.outputFormat?.type === \"json_schema\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_schema\",\n json_schema: {\n name: params.outputFormat.name ?? \"response\",\n schema: params.outputFormat.schema,\n strict: params.outputFormat.strict ?? false,\n },\n };\n } else if (params.outputFormat?.type === \"json_object\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_object\",\n };\n }\n\n const stream = await this.client.chat.completions.create(createParams);\n\n for await (const chunk of stream) {\n const usage = chunk.usage;\n let mappedUsage: ChatStreamChunk[\"usage\"] | undefined;\n if (usage) {\n const u = usage as unknown as Record<string, unknown>;\n const promptDetails = u.prompt_tokens_details as Record<string, unknown> | undefined;\n const completionDetails = u.completion_tokens_details as Record<string, unknown> | undefined;\n mappedUsage = {\n prompt_tokens: usage.prompt_tokens,\n completion_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n cache_read_tokens: promptDetails?.cached_tokens as number | undefined,\n thinking_tokens: completionDetails?.reasoning_tokens as number | undefined,\n };\n }\n\n yield {\n id: chunk.id,\n model: chunk.model,\n choices: chunk.choices.map((c) => ({\n index: c.index,\n delta: {\n role: c.delta.role as \"assistant\" | undefined,\n content: c.delta.content,\n tool_calls: c.delta.tool_calls?.map((tc) => ({\n index: tc.index,\n id: tc.id,\n type: tc.type as \"function\" | undefined,\n function: tc.function\n ? {\n name: tc.function.name,\n arguments: tc.function.arguments,\n }\n : undefined,\n })),\n },\n finish_reason: c.finish_reason,\n })),\n usage: mappedUsage,\n };\n }\n }\n\n private static contentPartsToOpenAI(\n parts: ContentPart[],\n ): Array<Record<string, unknown>> {\n return parts.map((part) => {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n if (part.type === \"image\") {\n return {\n type: \"image_url\",\n image_url: { url: `data:${part.media_type};base64,${part.data}` },\n };\n }\n // image_url\n return { type: \"image_url\", image_url: { url: part.url } };\n });\n }\n\n private buildMessages(\n system: string | undefined,\n messages: ChatMessage[],\n ): Array<Record<string, unknown>> {\n const result: Array<Record<string, unknown>> = [];\n if (system) {\n result.push({ role: \"system\", content: system });\n }\n for (const msg of messages) {\n if (msg.role === \"tool\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({\n role: \"tool\",\n tool_call_id: msg.tool_call_id,\n content,\n });\n } else if (msg.role === \"assistant\") {\n const entry: Record<string, unknown> = {\n role: \"assistant\",\n content: msg.content,\n };\n if (msg.tool_calls) {\n entry.tool_calls = msg.tool_calls;\n }\n result.push(entry);\n } else if (msg.role === \"user\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({ role: \"user\", content });\n } else {\n result.push({ role: msg.role, content: msg.content });\n }\n }\n return result;\n }\n}\n"],"mappings":"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","names":[]}
|