@waniwani/sdk 0.1.20-beta.24 → 0.1.20-beta.26
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.
|
@@ -201,6 +201,8 @@ interface NextJsHandlerResult {
|
|
|
201
201
|
GET: (request: Request) => Promise<Response>;
|
|
202
202
|
/** POST handler: proxies chat messages to the WaniWani API */
|
|
203
203
|
POST: (request: Request) => Promise<Response>;
|
|
204
|
+
/** Forces Next.js to treat this route as dynamic (skips static generation caching for GET) */
|
|
205
|
+
dynamic: "force-dynamic";
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
/**
|
|
@@ -220,7 +222,7 @@ interface NextJsHandlerResult {
|
|
|
220
222
|
*
|
|
221
223
|
* const wani = waniwani();
|
|
222
224
|
*
|
|
223
|
-
* export const { GET, POST } = toNextJsHandler(wani, {
|
|
225
|
+
* export const { GET, POST, dynamic } = toNextJsHandler(wani, {
|
|
224
226
|
* chat: {
|
|
225
227
|
* systemPrompt: "You are a helpful assistant.",
|
|
226
228
|
* mcpServerUrl: process.env.MCP_SERVER_URL!,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function C(d,i){return i?(...s)=>console.log(`[waniwani:${d}]`,...s):()=>{}}var y=class extends Error{constructor(s,l){super(s);this.status=l;this.name="WaniWaniError"}};function S(d){let{apiKey:i,baseUrl:s,systemPrompt:l,maxSteps:e,beforeRequest:m,mcpServerUrl:f,resolveConfig:t,debug:c}=d,r=C("chat",c);return async function(g){r("\u2192 POST",g.url);try{let u=await g.json(),n=u.messages??[],a=u.sessionId,h=l;if(r("body parsed \u2014 messages:",n.length,"sessionId:",a??"(none)"),m){r("running beforeRequest hook");try{let p=await m({messages:n,sessionId:a,request:g});p&&(p.messages&&(n=p.messages),p.systemPrompt!==void 0&&(h=p.systemPrompt),p.sessionId!==void 0&&(a=p.sessionId)),r("beforeRequest hook done \u2014 messages:",n.length,"sessionId:",a??"(none)")}catch(p){console.error("[waniwani:chat] beforeRequest hook error:",p);let E=p instanceof y?p.status:400,A=p instanceof Error?p.message:"Request rejected";return r("\u2190 returning",E,"from hook error"),Response.json({error:A},{status:E})}}let x=f??(await t()).mcpServerUrl;r("mcpServerUrl:",x);let w=`${s}/api/mcp/chat`;r("forwarding to",w);let o=await fetch(w,{method:"POST",headers:{"Content-Type":"application/json",...i?{Authorization:`Bearer ${i}`}:{}},body:JSON.stringify({messages:n,mcpServerUrl:x,sessionId:a,systemPrompt:h,maxSteps:e}),signal:g.signal});if(r("upstream response status:",o.status),!o.ok){let p=await o.text().catch(()=>"");return r("\u2190 returning",o.status,"upstream error:",p),new Response(p,{status:o.status,headers:{"Content-Type":o.headers.get("Content-Type")??"application/json"}})}let P=new Headers({"Content-Type":o.headers.get("Content-Type")??"text/event-stream"}),H=o.headers.get("x-session-id");return H&&P.set("x-session-id",H),r("\u2190 streaming response",o.status,"body null?",o.body===null),new Response(o.body,{status:o.status,headers:P})}catch(u){console.error("[waniwani:chat] handler error:",u);let n=u instanceof Error?u.message:"Unknown error occurred",a=u instanceof y?u.status:500;return r("\u2190 returning",a,"from caught error"),Response.json({error:n},{status:a})}}}function T(d){let{mcpServerUrl:i,resolveConfig:s,debug:l}=d,e=C("resource",l);return async function(f){e("\u2192 GET",f.toString());try{let t=f.searchParams.get("uri");if(e("uri:",t??"(missing)"),!t)return e("\u2190 400 missing uri"),Response.json({error:"Missing uri query parameter"},{status:400});let c=i??(await s()).mcpServerUrl;e("mcpServerUrl:",c);let r,v;try{[{createMCPClient:r},{StreamableHTTPClientTransport:v}]=await Promise.all([import("@ai-sdk/mcp"),import("@modelcontextprotocol/sdk/client/streamableHttp.js")]),e("MCP deps loaded")}catch(u){return console.error("[waniwani:resource] MCP deps import failed:",u),Response.json({error:"MCP resource handler requires @ai-sdk/mcp and @modelcontextprotocol/sdk. Install them to enable resource serving."},{status:501})}e("creating MCP client for",c);let g=await r({transport:new v(new URL(c))});try{e("reading resource:",t);let u=await g.readResource({uri:t});e("resource contents count:",u.contents.length);let n=u.contents[0];if(!n)return e("\u2190 404 resource not found"),Response.json({error:"Resource not found"},{status:404});let a;return"text"in n&&typeof n.text=="string"?a=n.text:"blob"in n&&typeof n.blob=="string"&&(a=atob(n.blob)),a?(e("\u2190 200 HTML length:",a.length),new Response(a,{headers:{"Content-Type":"text/html","Cache-Control":"private, max-age=300"}})):(e("\u2190 404 resource has no content, keys:",Object.keys(n)),Response.json({error:"Resource has no content"},{status:404}))}finally{await g.close(),e("MCP client closed")}}catch(t){console.error("[waniwani:resource] handler error:",t);let c=t instanceof Error?t.message:"Unknown error occurred",r=t instanceof y?t.status:500;return e("\u2190 returning",r,"from caught error"),Response.json({error:c},{status:r})}}}var k=300*1e3;function U(d,i){let s=null,l=null;return async function(){if(s&&Date.now()<s.expiresAt)return s.config;if(l)return l;l=(async()=>{if(!i)throw new y("WANIWANI_API_KEY is required for createChatHandler",401);let m=await fetch(`${d}/api/mcp/environments/config`,{method:"GET",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"}});if(!m.ok){let t=await m.text().catch(()=>"");throw new y(`Failed to resolve MCP environment config: ${m.status} ${t}`,m.status)}let f=await m.json();return s={config:f,expiresAt:Date.now()+k},f})();try{return await l}finally{l=null}}}function j(d={}){let{apiKey:i=process.env.WANIWANI_API_KEY,baseUrl:s="https://app.waniwani.ai",systemPrompt:l,maxSteps:e=5,beforeRequest:m,mcpServerUrl:f,debug:t=!1}=d,c=C("router",t),r=U(s,i),v=S({apiKey:i,baseUrl:s,systemPrompt:l,maxSteps:e,beforeRequest:m,mcpServerUrl:f,resolveConfig:r,debug:t}),g=T({mcpServerUrl:f,resolveConfig:r,debug:t});async function u(){return Response.json({debug:t})}async function n(a){c("\u2192 GET",a.url);try{let h=new URL(a.url),w=h.pathname.replace(/\/$/,"").split("/").filter(Boolean).at(-1);if(c("pathname:",h.pathname,"subRoute:",w),w==="resource"){c("dispatching to resource handler");let o=await g(h);return c("\u2190 resource handler returned",o.status),o}if(w==="config"){c("dispatching to config handler");let o=await u();return c("\u2190 config handler returned",o.status),o}return c("\u2190 404 no matching sub-route for",w),Response.json({error:"Not found"},{status:404})}catch(h){console.error("[waniwani:router] GET handler error:",h);let x=h instanceof Error?h.message:"Unknown error occurred";return c("\u2190 500 from caught error"),Response.json({error:x},{status:500})}}return{handleChat:v,handleResource:g,routeGet:n}}function re(d,i){let{apiKey:s,baseUrl:l}=d._config,e=i?.debug??process.env.WANIWANI_DEBUG==="1",m=j({...i?.chat,apiKey:s,baseUrl:l,debug:e});return{POST:m.handleChat,GET:m.routeGet}}export{re as toNextJsHandler};
|
|
1
|
+
function C(d,i){return i?(...s)=>console.log(`[waniwani:${d}]`,...s):()=>{}}var y=class extends Error{constructor(s,l){super(s);this.status=l;this.name="WaniWaniError"}};function S(d){let{apiKey:i,baseUrl:s,systemPrompt:l,maxSteps:e,beforeRequest:m,mcpServerUrl:f,resolveConfig:t,debug:c}=d,r=C("chat",c);return async function(g){r("\u2192 POST",g.url);try{let u=await g.json(),n=u.messages??[],a=u.sessionId,h=l;if(r("body parsed \u2014 messages:",n.length,"sessionId:",a??"(none)"),m){r("running beforeRequest hook");try{let p=await m({messages:n,sessionId:a,request:g});p&&(p.messages&&(n=p.messages),p.systemPrompt!==void 0&&(h=p.systemPrompt),p.sessionId!==void 0&&(a=p.sessionId)),r("beforeRequest hook done \u2014 messages:",n.length,"sessionId:",a??"(none)")}catch(p){console.error("[waniwani:chat] beforeRequest hook error:",p);let E=p instanceof y?p.status:400,A=p instanceof Error?p.message:"Request rejected";return r("\u2190 returning",E,"from hook error"),Response.json({error:A},{status:E})}}let x=f??(await t()).mcpServerUrl;r("mcpServerUrl:",x);let w=`${s}/api/mcp/chat`;r("forwarding to",w);let o=await fetch(w,{method:"POST",headers:{"Content-Type":"application/json",...i?{Authorization:`Bearer ${i}`}:{}},body:JSON.stringify({messages:n,mcpServerUrl:x,sessionId:a,systemPrompt:h,maxSteps:e}),signal:g.signal});if(r("upstream response status:",o.status),!o.ok){let p=await o.text().catch(()=>"");return r("\u2190 returning",o.status,"upstream error:",p),new Response(p,{status:o.status,headers:{"Content-Type":o.headers.get("Content-Type")??"application/json"}})}let P=new Headers({"Content-Type":o.headers.get("Content-Type")??"text/event-stream"}),H=o.headers.get("x-session-id");return H&&P.set("x-session-id",H),r("\u2190 streaming response",o.status,"body null?",o.body===null),new Response(o.body,{status:o.status,headers:P})}catch(u){console.error("[waniwani:chat] handler error:",u);let n=u instanceof Error?u.message:"Unknown error occurred",a=u instanceof y?u.status:500;return r("\u2190 returning",a,"from caught error"),Response.json({error:n},{status:a})}}}function T(d){let{mcpServerUrl:i,resolveConfig:s,debug:l}=d,e=C("resource",l);return async function(f){e("\u2192 GET",f.toString());try{let t=f.searchParams.get("uri");if(e("uri:",t??"(missing)"),!t)return e("\u2190 400 missing uri"),Response.json({error:"Missing uri query parameter"},{status:400});let c=i??(await s()).mcpServerUrl;e("mcpServerUrl:",c);let r,v;try{[{createMCPClient:r},{StreamableHTTPClientTransport:v}]=await Promise.all([import("@ai-sdk/mcp"),import("@modelcontextprotocol/sdk/client/streamableHttp.js")]),e("MCP deps loaded")}catch(u){return console.error("[waniwani:resource] MCP deps import failed:",u),Response.json({error:"MCP resource handler requires @ai-sdk/mcp and @modelcontextprotocol/sdk. Install them to enable resource serving."},{status:501})}e("creating MCP client for",c);let g=await r({transport:new v(new URL(c))});try{e("reading resource:",t);let u=await g.readResource({uri:t});e("resource contents count:",u.contents.length);let n=u.contents[0];if(!n)return e("\u2190 404 resource not found"),Response.json({error:"Resource not found"},{status:404});let a;return"text"in n&&typeof n.text=="string"?a=n.text:"blob"in n&&typeof n.blob=="string"&&(a=atob(n.blob)),a?(e("\u2190 200 HTML length:",a.length),new Response(a,{headers:{"Content-Type":"text/html","Cache-Control":"private, max-age=300"}})):(e("\u2190 404 resource has no content, keys:",Object.keys(n)),Response.json({error:"Resource has no content"},{status:404}))}finally{await g.close(),e("MCP client closed")}}catch(t){console.error("[waniwani:resource] handler error:",t);let c=t instanceof Error?t.message:"Unknown error occurred",r=t instanceof y?t.status:500;return e("\u2190 returning",r,"from caught error"),Response.json({error:c},{status:r})}}}var k=300*1e3;function U(d,i){let s=null,l=null;return async function(){if(s&&Date.now()<s.expiresAt)return s.config;if(l)return l;l=(async()=>{if(!i)throw new y("WANIWANI_API_KEY is required for createChatHandler",401);let m=await fetch(`${d}/api/mcp/environments/config`,{method:"GET",headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json"}});if(!m.ok){let t=await m.text().catch(()=>"");throw new y(`Failed to resolve MCP environment config: ${m.status} ${t}`,m.status)}let f=await m.json();return s={config:f,expiresAt:Date.now()+k},f})();try{return await l}finally{l=null}}}function j(d={}){let{apiKey:i=process.env.WANIWANI_API_KEY,baseUrl:s="https://app.waniwani.ai",systemPrompt:l,maxSteps:e=5,beforeRequest:m,mcpServerUrl:f,debug:t=!1}=d,c=C("router",t),r=U(s,i),v=S({apiKey:i,baseUrl:s,systemPrompt:l,maxSteps:e,beforeRequest:m,mcpServerUrl:f,resolveConfig:r,debug:t}),g=T({mcpServerUrl:f,resolveConfig:r,debug:t});async function u(){return Response.json({debug:t})}async function n(a){c("\u2192 GET",a.url);try{let h=new URL(a.url),w=h.pathname.replace(/\/$/,"").split("/").filter(Boolean).at(-1);if(c("pathname:",h.pathname,"subRoute:",w),w==="resource"){c("dispatching to resource handler");let o=await g(h);return c("\u2190 resource handler returned",o.status),o}if(w==="config"){c("dispatching to config handler");let o=await u();return c("\u2190 config handler returned",o.status),o}return c("\u2190 404 no matching sub-route for",w),Response.json({error:"Not found"},{status:404})}catch(h){console.error("[waniwani:router] GET handler error:",h);let x=h instanceof Error?h.message:"Unknown error occurred";return c("\u2190 500 from caught error"),Response.json({error:x},{status:500})}}return{handleChat:v,handleResource:g,routeGet:n}}function re(d,i){let{apiKey:s,baseUrl:l}=d._config,e=i?.debug??process.env.WANIWANI_DEBUG==="1",m=j({...i?.chat,apiKey:s,baseUrl:l,debug:e});return{POST:m.handleChat,GET:m.routeGet,dynamic:"force-dynamic"}}export{re as toNextJsHandler};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/logger.ts","../../../src/error.ts","../../../src/chat/server/handle-chat.ts","../../../src/chat/server/handle-resource.ts","../../../src/chat/server/mcp-config-resolver.ts","../../../src/chat/server/api-handler.ts","../../../src/chat/server/next-js/index.ts"],"sourcesContent":["/**\n * Creates a namespaced logger that writes to console.log when enabled,\n * or is a no-op when disabled.\n *\n * @example\n * const log = createLogger(\"chat\", debug);\n * log(\"→ POST\", request.url); // [waniwani:chat] → POST ...\n */\nexport function createLogger(\n\tnamespace: string,\n\tenabled: boolean,\n): (...args: unknown[]) => void {\n\treturn enabled\n\t\t? (...args: unknown[]) => console.log(`[waniwani:${namespace}]`, ...args)\n\t\t: () => {};\n}\n","// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","// Handle Chat - Proxies chat requests to the WaniWani API\n\nimport { WaniWaniError } from \"../../error\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ApiHandlerDeps } from \"./@types\";\n\nexport function createChatRequestHandler(deps: ApiHandlerDeps) {\n\tconst {\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tsystemPrompt,\n\t\tmaxSteps,\n\t\tbeforeRequest,\n\t\tmcpServerUrl: mcpServerUrlOverride,\n\t\tresolveConfig,\n\t\tdebug,\n\t} = deps;\n\n\tconst log = createLogger(\"chat\", debug);\n\n\treturn async function handleChat(request: Request): Promise<Response> {\n\t\tlog(\"→ POST\", request.url);\n\t\ttry {\n\t\t\t// 1. Parse request body\n\t\t\tconst body = await request.json();\n\t\t\tlet messages = body.messages ?? [];\n\t\t\tlet sessionId: string | undefined = body.sessionId;\n\t\t\tlet effectiveSystemPrompt = systemPrompt;\n\n\t\t\tlog(\n\t\t\t\t\"body parsed — messages:\",\n\t\t\t\tmessages.length,\n\t\t\t\t\"sessionId:\",\n\t\t\t\tsessionId ?? \"(none)\",\n\t\t\t);\n\n\t\t\t// 2. Run beforeRequest hook\n\t\t\tif (beforeRequest) {\n\t\t\t\tlog(\"running beforeRequest hook\");\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await beforeRequest({\n\t\t\t\t\t\tmessages,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\trequest,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tif (result.messages) messages = result.messages;\n\t\t\t\t\t\tif (result.systemPrompt !== undefined)\n\t\t\t\t\t\t\teffectiveSystemPrompt = result.systemPrompt;\n\t\t\t\t\t\tif (result.sessionId !== undefined) sessionId = result.sessionId;\n\t\t\t\t\t}\n\t\t\t\t\tlog(\n\t\t\t\t\t\t\"beforeRequest hook done — messages:\",\n\t\t\t\t\t\tmessages.length,\n\t\t\t\t\t\t\"sessionId:\",\n\t\t\t\t\t\tsessionId ?? \"(none)\",\n\t\t\t\t\t);\n\t\t\t\t} catch (hookError) {\n\t\t\t\t\tconsole.error(\"[waniwani:chat] beforeRequest hook error:\", hookError);\n\t\t\t\t\tconst status =\n\t\t\t\t\t\thookError instanceof WaniWaniError ? hookError.status : 400;\n\t\t\t\t\tconst message =\n\t\t\t\t\t\thookError instanceof Error ? hookError.message : \"Request rejected\";\n\t\t\t\t\tlog(\"← returning\", status, \"from hook error\");\n\t\t\t\t\treturn Response.json({ error: message }, { status });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Resolve MCP server URL\n\t\t\tconst mcpServerUrl =\n\t\t\t\tmcpServerUrlOverride ?? (await resolveConfig()).mcpServerUrl;\n\t\t\tlog(\"mcpServerUrl:\", mcpServerUrl);\n\n\t\t\t// 4. Forward to WaniWani API\n\t\t\tconst upstreamUrl = `${baseUrl}/api/mcp/chat`;\n\t\t\tlog(\"forwarding to\", upstreamUrl);\n\t\t\tconst response = await fetch(upstreamUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmessages,\n\t\t\t\t\tmcpServerUrl,\n\t\t\t\t\tsessionId,\n\t\t\t\t\tsystemPrompt: effectiveSystemPrompt,\n\t\t\t\t\tmaxSteps,\n\t\t\t\t}),\n\t\t\t\tsignal: request.signal,\n\t\t\t});\n\n\t\t\tlog(\"upstream response status:\", response.status);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\t\tlog(\"← returning\", response.status, \"upstream error:\", errorBody);\n\t\t\t\treturn new Response(errorBody, {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\":\n\t\t\t\t\t\t\tresponse.headers.get(\"Content-Type\") ?? \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 5. Stream the response back\n\t\t\tconst headers = new Headers({\n\t\t\t\t\"Content-Type\":\n\t\t\t\t\tresponse.headers.get(\"Content-Type\") ?? \"text/event-stream\",\n\t\t\t});\n\t\t\tconst upstreamSessionId = response.headers.get(\"x-session-id\");\n\t\t\tif (upstreamSessionId) {\n\t\t\t\theaders.set(\"x-session-id\", upstreamSessionId);\n\t\t\t}\n\n\t\t\tlog(\n\t\t\t\t\"← streaming response\",\n\t\t\t\tresponse.status,\n\t\t\t\t\"body null?\",\n\t\t\t\tresponse.body === null,\n\t\t\t);\n\t\t\treturn new Response(response.body, {\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:chat] handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tconst status = error instanceof WaniWaniError ? error.status : 500;\n\t\t\tlog(\"← returning\", status, \"from caught error\");\n\t\t\treturn Response.json({ error: message }, { status });\n\t\t}\n\t};\n}\n","// Handle Resource - Serves MCP resource content (HTML widgets)\n\nimport { WaniWaniError } from \"../../error\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ResourceHandlerDeps } from \"./@types\";\n\nexport function createResourceHandler(deps: ResourceHandlerDeps) {\n\tconst { mcpServerUrl: mcpServerUrlOverride, resolveConfig, debug } = deps;\n\n\tconst log = createLogger(\"resource\", debug);\n\n\treturn async function handleResource(url: URL): Promise<Response> {\n\t\tlog(\"→ GET\", url.toString());\n\t\ttry {\n\t\t\tconst uri = url.searchParams.get(\"uri\");\n\t\t\tlog(\"uri:\", uri ?? \"(missing)\");\n\n\t\t\tif (!uri) {\n\t\t\t\tlog(\"← 400 missing uri\");\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{ error: \"Missing uri query parameter\" },\n\t\t\t\t\t{ status: 400 },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst mcpServerUrl =\n\t\t\t\tmcpServerUrlOverride ?? (await resolveConfig()).mcpServerUrl;\n\t\t\tlog(\"mcpServerUrl:\", mcpServerUrl);\n\n\t\t\t// Dynamic imports — these are optional peer dependencies\n\t\t\tlet createMCPClient: typeof import(\"@ai-sdk/mcp\")[\"createMCPClient\"];\n\t\t\tlet StreamableHTTPClientTransport: typeof import(\"@modelcontextprotocol/sdk/client/streamableHttp.js\")[\"StreamableHTTPClientTransport\"];\n\n\t\t\ttry {\n\t\t\t\t[{ createMCPClient }, { StreamableHTTPClientTransport }] =\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\timport(\"@ai-sdk/mcp\"),\n\t\t\t\t\t\timport(\"@modelcontextprotocol/sdk/client/streamableHttp.js\"),\n\t\t\t\t\t]);\n\t\t\t\tlog(\"MCP deps loaded\");\n\t\t\t} catch (importError) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[waniwani:resource] MCP deps import failed:\",\n\t\t\t\t\timportError,\n\t\t\t\t);\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\"MCP resource handler requires @ai-sdk/mcp and @modelcontextprotocol/sdk. Install them to enable resource serving.\",\n\t\t\t\t\t},\n\t\t\t\t\t{ status: 501 },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlog(\"creating MCP client for\", mcpServerUrl);\n\t\t\tconst mcp = await createMCPClient({\n\t\t\t\ttransport: new StreamableHTTPClientTransport(new URL(mcpServerUrl)),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tlog(\"reading resource:\", uri);\n\t\t\t\tconst result = await mcp.readResource({ uri });\n\t\t\t\tlog(\"resource contents count:\", result.contents.length);\n\n\t\t\t\tconst content = result.contents[0];\n\t\t\t\tif (!content) {\n\t\t\t\t\tlog(\"← 404 resource not found\");\n\t\t\t\t\treturn Response.json(\n\t\t\t\t\t\t{ error: \"Resource not found\" },\n\t\t\t\t\t\t{ status: 404 },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tlet html: string | undefined;\n\t\t\t\tif (\"text\" in content && typeof content.text === \"string\") {\n\t\t\t\t\thtml = content.text;\n\t\t\t\t} else if (\"blob\" in content && typeof content.blob === \"string\") {\n\t\t\t\t\thtml = atob(content.blob);\n\t\t\t\t}\n\n\t\t\t\tif (!html) {\n\t\t\t\t\tlog(\"← 404 resource has no content, keys:\", Object.keys(content));\n\t\t\t\t\treturn Response.json(\n\t\t\t\t\t\t{ error: \"Resource has no content\" },\n\t\t\t\t\t\t{ status: 404 },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tlog(\"← 200 HTML length:\", html.length);\n\t\t\t\treturn new Response(html, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"text/html\",\n\t\t\t\t\t\t\"Cache-Control\": \"private, max-age=300\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tawait mcp.close();\n\t\t\t\tlog(\"MCP client closed\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:resource] handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tconst status = error instanceof WaniWaniError ? error.status : 500;\n\t\t\tlog(\"← returning\", status, \"from caught error\");\n\t\t\treturn Response.json({ error: message }, { status });\n\t\t}\n\t};\n}\n","// MCP Config Resolver - Lazy-loads and caches MCP environment config\n\nimport { WaniWaniError } from \"../../error\";\n\ninterface McpEnvironmentConfig {\n\tmcpServerUrl: string;\n}\n\nconst TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function createMcpConfigResolver(\n\tbaseUrl: string,\n\tapiKey: string | undefined,\n) {\n\tlet cached: { config: McpEnvironmentConfig; expiresAt: number } | null = null;\n\tlet inflight: Promise<McpEnvironmentConfig> | null = null;\n\n\treturn async function resolve(): Promise<McpEnvironmentConfig> {\n\t\tif (cached && Date.now() < cached.expiresAt) {\n\t\t\treturn cached.config;\n\t\t}\n\n\t\t// Deduplicate concurrent requests (cold start scenario)\n\t\tif (inflight) {\n\t\t\treturn inflight;\n\t\t}\n\n\t\tinflight = (async () => {\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\t\"WANIWANI_API_KEY is required for createChatHandler\",\n\t\t\t\t\t401,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${baseUrl}/api/mcp/environments/config`, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst body = await response.text().catch(() => \"\");\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\t`Failed to resolve MCP environment config: ${response.status} ${body}`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst data = (await response.json()) as McpEnvironmentConfig;\n\t\t\tcached = { config: data, expiresAt: Date.now() + TTL_MS };\n\t\t\treturn data;\n\t\t})();\n\n\t\ttry {\n\t\t\treturn await inflight;\n\t\t} finally {\n\t\t\tinflight = null;\n\t\t}\n\t};\n}\n","// API Handler - Composes chat and resource handlers into a unified API handler\n\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ApiHandler, ApiHandlerOptions } from \"./@types\";\nimport { createChatRequestHandler } from \"./handle-chat\";\nimport { createResourceHandler } from \"./handle-resource\";\nimport { createMcpConfigResolver } from \"./mcp-config-resolver\";\n\n/**\n * Create a framework-agnostic API handler for chat and MCP resources.\n *\n * Returns an object with handler methods that can be wired into\n * any framework (Next.js, Hono, Express, etc.):\n *\n * - `handleChat(request)` → proxies chat messages to WaniWani API\n * - `handleResource(url)` → serves MCP resource content (HTML widgets)\n * - `routeGet(request)` → routes GET sub-paths (e.g. /resource)\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani();\n *\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function createApiHandler(options: ApiHandlerOptions = {}): ApiHandler {\n\tconst {\n\t\tapiKey = process.env.WANIWANI_API_KEY,\n\t\tbaseUrl = \"https://app.waniwani.ai\",\n\t\tsystemPrompt,\n\t\tmaxSteps = 5,\n\t\tbeforeRequest,\n\t\tmcpServerUrl,\n\t\tdebug = false,\n\t} = options;\n\n\tconst log = createLogger(\"router\", debug);\n\n\tconst resolveConfig = createMcpConfigResolver(baseUrl, apiKey);\n\n\tconst handleChat = createChatRequestHandler({\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tsystemPrompt,\n\t\tmaxSteps,\n\t\tbeforeRequest,\n\t\tmcpServerUrl,\n\t\tresolveConfig,\n\t\tdebug,\n\t});\n\n\tconst handleResource = createResourceHandler({\n\t\tmcpServerUrl,\n\t\tresolveConfig,\n\t\tdebug,\n\t});\n\n\tasync function handleConfig(): Promise<Response> {\n\t\treturn Response.json({ debug });\n\t}\n\n\tasync function routeGet(request: Request): Promise<Response> {\n\t\tlog(\"→ GET\", request.url);\n\t\ttry {\n\t\t\tconst url = new URL(request.url);\n\t\t\tconst segments = url.pathname\n\t\t\t\t.replace(/\\/$/, \"\")\n\t\t\t\t.split(\"/\")\n\t\t\t\t.filter(Boolean);\n\t\t\tconst subRoute = segments.at(-1);\n\t\t\tlog(\"pathname:\", url.pathname, \"subRoute:\", subRoute);\n\n\t\t\tif (subRoute === \"resource\") {\n\t\t\t\tlog(\"dispatching to resource handler\");\n\t\t\t\tconst response = await handleResource(url);\n\t\t\t\tlog(\"← resource handler returned\", response.status);\n\t\t\t\treturn response;\n\t\t\t}\n\n\t\t\tif (subRoute === \"config\") {\n\t\t\t\tlog(\"dispatching to config handler\");\n\t\t\t\tconst response = await handleConfig();\n\t\t\t\tlog(\"← config handler returned\", response.status);\n\t\t\t\treturn response;\n\t\t\t}\n\n\t\t\tlog(\"← 404 no matching sub-route for\", subRoute);\n\t\t\treturn Response.json({ error: \"Not found\" }, { status: 404 });\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:router] GET handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tlog(\"← 500 from caught error\");\n\t\t\treturn Response.json({ error: message }, { status: 500 });\n\t\t}\n\t}\n\n\treturn { handleChat, handleResource, routeGet };\n}\n","// WaniWani SDK - Next.js Adapter\n\nimport type { WaniWaniClient } from \"../../../types.js\";\nimport { createApiHandler } from \"../api-handler.js\";\nimport type { NextJsHandlerOptions, NextJsHandlerResult } from \"./@types.js\";\n\nexport type { NextJsHandlerOptions, NextJsHandlerResult } from \"./@types.js\";\n\n/**\n * Create Next.js route handlers from a WaniWani client.\n *\n * Returns `{ GET, POST }` for use with catch-all routes.\n * Mount at `app/api/waniwani/[[...path]]/route.ts`:\n *\n * - `POST /api/waniwani` → chat (proxied to WaniWani API)\n * - `GET /api/waniwani/resource?uri=…` → MCP resource content\n *\n * @example\n * ```typescript\n * // app/api/waniwani/[[...path]]/route.ts\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani();\n *\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: {\n * systemPrompt: \"You are a helpful assistant.\",\n * mcpServerUrl: process.env.MCP_SERVER_URL!,\n * },\n * });\n * ```\n */\nexport function toNextJsHandler(\n\tclient: WaniWaniClient,\n\toptions?: NextJsHandlerOptions,\n): NextJsHandlerResult {\n\tconst { apiKey, baseUrl } = client._config;\n\n\tconst debugEnabled = options?.debug ?? process.env.WANIWANI_DEBUG === \"1\";\n\n\tconst handler = createApiHandler({\n\t\t...options?.chat,\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tdebug: debugEnabled,\n\t});\n\n\treturn {\n\t\tPOST: handler.handleChat,\n\t\tGET: handler.routeGet,\n\t};\n}\n"],"mappings":"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","names":["createLogger","namespace","enabled","args","WaniWaniError","message","status","createChatRequestHandler","deps","apiKey","baseUrl","systemPrompt","maxSteps","beforeRequest","mcpServerUrlOverride","resolveConfig","debug","log","createLogger","request","body","messages","sessionId","effectiveSystemPrompt","result","hookError","status","WaniWaniError","message","mcpServerUrl","upstreamUrl","response","errorBody","headers","upstreamSessionId","error","createResourceHandler","deps","mcpServerUrlOverride","resolveConfig","debug","log","createLogger","url","uri","mcpServerUrl","createMCPClient","StreamableHTTPClientTransport","importError","mcp","result","content","html","error","message","status","WaniWaniError","TTL_MS","createMcpConfigResolver","baseUrl","apiKey","cached","inflight","WaniWaniError","response","body","data","createApiHandler","options","apiKey","baseUrl","systemPrompt","maxSteps","beforeRequest","mcpServerUrl","debug","log","createLogger","resolveConfig","createMcpConfigResolver","handleChat","createChatRequestHandler","handleResource","createResourceHandler","handleConfig","routeGet","request","url","subRoute","response","error","message","toNextJsHandler","client","options","apiKey","baseUrl","debugEnabled","handler","createApiHandler"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/logger.ts","../../../src/error.ts","../../../src/chat/server/handle-chat.ts","../../../src/chat/server/handle-resource.ts","../../../src/chat/server/mcp-config-resolver.ts","../../../src/chat/server/api-handler.ts","../../../src/chat/server/next-js/index.ts"],"sourcesContent":["/**\n * Creates a namespaced logger that writes to console.log when enabled,\n * or is a no-op when disabled.\n *\n * @example\n * const log = createLogger(\"chat\", debug);\n * log(\"→ POST\", request.url); // [waniwani:chat] → POST ...\n */\nexport function createLogger(\n\tnamespace: string,\n\tenabled: boolean,\n): (...args: unknown[]) => void {\n\treturn enabled\n\t\t? (...args: unknown[]) => console.log(`[waniwani:${namespace}]`, ...args)\n\t\t: () => {};\n}\n","// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","// Handle Chat - Proxies chat requests to the WaniWani API\n\nimport { WaniWaniError } from \"../../error\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ApiHandlerDeps } from \"./@types\";\n\nexport function createChatRequestHandler(deps: ApiHandlerDeps) {\n\tconst {\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tsystemPrompt,\n\t\tmaxSteps,\n\t\tbeforeRequest,\n\t\tmcpServerUrl: mcpServerUrlOverride,\n\t\tresolveConfig,\n\t\tdebug,\n\t} = deps;\n\n\tconst log = createLogger(\"chat\", debug);\n\n\treturn async function handleChat(request: Request): Promise<Response> {\n\t\tlog(\"→ POST\", request.url);\n\t\ttry {\n\t\t\t// 1. Parse request body\n\t\t\tconst body = await request.json();\n\t\t\tlet messages = body.messages ?? [];\n\t\t\tlet sessionId: string | undefined = body.sessionId;\n\t\t\tlet effectiveSystemPrompt = systemPrompt;\n\n\t\t\tlog(\n\t\t\t\t\"body parsed — messages:\",\n\t\t\t\tmessages.length,\n\t\t\t\t\"sessionId:\",\n\t\t\t\tsessionId ?? \"(none)\",\n\t\t\t);\n\n\t\t\t// 2. Run beforeRequest hook\n\t\t\tif (beforeRequest) {\n\t\t\t\tlog(\"running beforeRequest hook\");\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await beforeRequest({\n\t\t\t\t\t\tmessages,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\trequest,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\tif (result.messages) messages = result.messages;\n\t\t\t\t\t\tif (result.systemPrompt !== undefined)\n\t\t\t\t\t\t\teffectiveSystemPrompt = result.systemPrompt;\n\t\t\t\t\t\tif (result.sessionId !== undefined) sessionId = result.sessionId;\n\t\t\t\t\t}\n\t\t\t\t\tlog(\n\t\t\t\t\t\t\"beforeRequest hook done — messages:\",\n\t\t\t\t\t\tmessages.length,\n\t\t\t\t\t\t\"sessionId:\",\n\t\t\t\t\t\tsessionId ?? \"(none)\",\n\t\t\t\t\t);\n\t\t\t\t} catch (hookError) {\n\t\t\t\t\tconsole.error(\"[waniwani:chat] beforeRequest hook error:\", hookError);\n\t\t\t\t\tconst status =\n\t\t\t\t\t\thookError instanceof WaniWaniError ? hookError.status : 400;\n\t\t\t\t\tconst message =\n\t\t\t\t\t\thookError instanceof Error ? hookError.message : \"Request rejected\";\n\t\t\t\t\tlog(\"← returning\", status, \"from hook error\");\n\t\t\t\t\treturn Response.json({ error: message }, { status });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Resolve MCP server URL\n\t\t\tconst mcpServerUrl =\n\t\t\t\tmcpServerUrlOverride ?? (await resolveConfig()).mcpServerUrl;\n\t\t\tlog(\"mcpServerUrl:\", mcpServerUrl);\n\n\t\t\t// 4. Forward to WaniWani API\n\t\t\tconst upstreamUrl = `${baseUrl}/api/mcp/chat`;\n\t\t\tlog(\"forwarding to\", upstreamUrl);\n\t\t\tconst response = await fetch(upstreamUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmessages,\n\t\t\t\t\tmcpServerUrl,\n\t\t\t\t\tsessionId,\n\t\t\t\t\tsystemPrompt: effectiveSystemPrompt,\n\t\t\t\t\tmaxSteps,\n\t\t\t\t}),\n\t\t\t\tsignal: request.signal,\n\t\t\t});\n\n\t\t\tlog(\"upstream response status:\", response.status);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorBody = await response.text().catch(() => \"\");\n\t\t\t\tlog(\"← returning\", response.status, \"upstream error:\", errorBody);\n\t\t\t\treturn new Response(errorBody, {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\":\n\t\t\t\t\t\t\tresponse.headers.get(\"Content-Type\") ?? \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// 5. Stream the response back\n\t\t\tconst headers = new Headers({\n\t\t\t\t\"Content-Type\":\n\t\t\t\t\tresponse.headers.get(\"Content-Type\") ?? \"text/event-stream\",\n\t\t\t});\n\t\t\tconst upstreamSessionId = response.headers.get(\"x-session-id\");\n\t\t\tif (upstreamSessionId) {\n\t\t\t\theaders.set(\"x-session-id\", upstreamSessionId);\n\t\t\t}\n\n\t\t\tlog(\n\t\t\t\t\"← streaming response\",\n\t\t\t\tresponse.status,\n\t\t\t\t\"body null?\",\n\t\t\t\tresponse.body === null,\n\t\t\t);\n\t\t\treturn new Response(response.body, {\n\t\t\t\tstatus: response.status,\n\t\t\t\theaders,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:chat] handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tconst status = error instanceof WaniWaniError ? error.status : 500;\n\t\t\tlog(\"← returning\", status, \"from caught error\");\n\t\t\treturn Response.json({ error: message }, { status });\n\t\t}\n\t};\n}\n","// Handle Resource - Serves MCP resource content (HTML widgets)\n\nimport { WaniWaniError } from \"../../error\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ResourceHandlerDeps } from \"./@types\";\n\nexport function createResourceHandler(deps: ResourceHandlerDeps) {\n\tconst { mcpServerUrl: mcpServerUrlOverride, resolveConfig, debug } = deps;\n\n\tconst log = createLogger(\"resource\", debug);\n\n\treturn async function handleResource(url: URL): Promise<Response> {\n\t\tlog(\"→ GET\", url.toString());\n\t\ttry {\n\t\t\tconst uri = url.searchParams.get(\"uri\");\n\t\t\tlog(\"uri:\", uri ?? \"(missing)\");\n\n\t\t\tif (!uri) {\n\t\t\t\tlog(\"← 400 missing uri\");\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{ error: \"Missing uri query parameter\" },\n\t\t\t\t\t{ status: 400 },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst mcpServerUrl =\n\t\t\t\tmcpServerUrlOverride ?? (await resolveConfig()).mcpServerUrl;\n\t\t\tlog(\"mcpServerUrl:\", mcpServerUrl);\n\n\t\t\t// Dynamic imports — these are optional peer dependencies\n\t\t\tlet createMCPClient: typeof import(\"@ai-sdk/mcp\")[\"createMCPClient\"];\n\t\t\tlet StreamableHTTPClientTransport: typeof import(\"@modelcontextprotocol/sdk/client/streamableHttp.js\")[\"StreamableHTTPClientTransport\"];\n\n\t\t\ttry {\n\t\t\t\t[{ createMCPClient }, { StreamableHTTPClientTransport }] =\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\timport(\"@ai-sdk/mcp\"),\n\t\t\t\t\t\timport(\"@modelcontextprotocol/sdk/client/streamableHttp.js\"),\n\t\t\t\t\t]);\n\t\t\t\tlog(\"MCP deps loaded\");\n\t\t\t} catch (importError) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[waniwani:resource] MCP deps import failed:\",\n\t\t\t\t\timportError,\n\t\t\t\t);\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\"MCP resource handler requires @ai-sdk/mcp and @modelcontextprotocol/sdk. Install them to enable resource serving.\",\n\t\t\t\t\t},\n\t\t\t\t\t{ status: 501 },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlog(\"creating MCP client for\", mcpServerUrl);\n\t\t\tconst mcp = await createMCPClient({\n\t\t\t\ttransport: new StreamableHTTPClientTransport(new URL(mcpServerUrl)),\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tlog(\"reading resource:\", uri);\n\t\t\t\tconst result = await mcp.readResource({ uri });\n\t\t\t\tlog(\"resource contents count:\", result.contents.length);\n\n\t\t\t\tconst content = result.contents[0];\n\t\t\t\tif (!content) {\n\t\t\t\t\tlog(\"← 404 resource not found\");\n\t\t\t\t\treturn Response.json(\n\t\t\t\t\t\t{ error: \"Resource not found\" },\n\t\t\t\t\t\t{ status: 404 },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tlet html: string | undefined;\n\t\t\t\tif (\"text\" in content && typeof content.text === \"string\") {\n\t\t\t\t\thtml = content.text;\n\t\t\t\t} else if (\"blob\" in content && typeof content.blob === \"string\") {\n\t\t\t\t\thtml = atob(content.blob);\n\t\t\t\t}\n\n\t\t\t\tif (!html) {\n\t\t\t\t\tlog(\"← 404 resource has no content, keys:\", Object.keys(content));\n\t\t\t\t\treturn Response.json(\n\t\t\t\t\t\t{ error: \"Resource has no content\" },\n\t\t\t\t\t\t{ status: 404 },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tlog(\"← 200 HTML length:\", html.length);\n\t\t\t\treturn new Response(html, {\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"text/html\",\n\t\t\t\t\t\t\"Cache-Control\": \"private, max-age=300\",\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tawait mcp.close();\n\t\t\t\tlog(\"MCP client closed\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:resource] handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tconst status = error instanceof WaniWaniError ? error.status : 500;\n\t\t\tlog(\"← returning\", status, \"from caught error\");\n\t\t\treturn Response.json({ error: message }, { status });\n\t\t}\n\t};\n}\n","// MCP Config Resolver - Lazy-loads and caches MCP environment config\n\nimport { WaniWaniError } from \"../../error\";\n\ninterface McpEnvironmentConfig {\n\tmcpServerUrl: string;\n}\n\nconst TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function createMcpConfigResolver(\n\tbaseUrl: string,\n\tapiKey: string | undefined,\n) {\n\tlet cached: { config: McpEnvironmentConfig; expiresAt: number } | null = null;\n\tlet inflight: Promise<McpEnvironmentConfig> | null = null;\n\n\treturn async function resolve(): Promise<McpEnvironmentConfig> {\n\t\tif (cached && Date.now() < cached.expiresAt) {\n\t\t\treturn cached.config;\n\t\t}\n\n\t\t// Deduplicate concurrent requests (cold start scenario)\n\t\tif (inflight) {\n\t\t\treturn inflight;\n\t\t}\n\n\t\tinflight = (async () => {\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\t\"WANIWANI_API_KEY is required for createChatHandler\",\n\t\t\t\t\t401,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst response = await fetch(`${baseUrl}/api/mcp/environments/config`, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst body = await response.text().catch(() => \"\");\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\t`Failed to resolve MCP environment config: ${response.status} ${body}`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst data = (await response.json()) as McpEnvironmentConfig;\n\t\t\tcached = { config: data, expiresAt: Date.now() + TTL_MS };\n\t\t\treturn data;\n\t\t})();\n\n\t\ttry {\n\t\t\treturn await inflight;\n\t\t} finally {\n\t\t\tinflight = null;\n\t\t}\n\t};\n}\n","// API Handler - Composes chat and resource handlers into a unified API handler\n\nimport { createLogger } from \"../../utils/logger.js\";\nimport type { ApiHandler, ApiHandlerOptions } from \"./@types\";\nimport { createChatRequestHandler } from \"./handle-chat\";\nimport { createResourceHandler } from \"./handle-resource\";\nimport { createMcpConfigResolver } from \"./mcp-config-resolver\";\n\n/**\n * Create a framework-agnostic API handler for chat and MCP resources.\n *\n * Returns an object with handler methods that can be wired into\n * any framework (Next.js, Hono, Express, etc.):\n *\n * - `handleChat(request)` → proxies chat messages to WaniWani API\n * - `handleResource(url)` → serves MCP resource content (HTML widgets)\n * - `routeGet(request)` → routes GET sub-paths (e.g. /resource)\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani();\n *\n * export const { GET, POST, dynamic } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function createApiHandler(options: ApiHandlerOptions = {}): ApiHandler {\n\tconst {\n\t\tapiKey = process.env.WANIWANI_API_KEY,\n\t\tbaseUrl = \"https://app.waniwani.ai\",\n\t\tsystemPrompt,\n\t\tmaxSteps = 5,\n\t\tbeforeRequest,\n\t\tmcpServerUrl,\n\t\tdebug = false,\n\t} = options;\n\n\tconst log = createLogger(\"router\", debug);\n\n\tconst resolveConfig = createMcpConfigResolver(baseUrl, apiKey);\n\n\tconst handleChat = createChatRequestHandler({\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tsystemPrompt,\n\t\tmaxSteps,\n\t\tbeforeRequest,\n\t\tmcpServerUrl,\n\t\tresolveConfig,\n\t\tdebug,\n\t});\n\n\tconst handleResource = createResourceHandler({\n\t\tmcpServerUrl,\n\t\tresolveConfig,\n\t\tdebug,\n\t});\n\n\tasync function handleConfig(): Promise<Response> {\n\t\treturn Response.json({ debug });\n\t}\n\n\tasync function routeGet(request: Request): Promise<Response> {\n\t\tlog(\"→ GET\", request.url);\n\t\ttry {\n\t\t\tconst url = new URL(request.url);\n\t\t\tconst segments = url.pathname\n\t\t\t\t.replace(/\\/$/, \"\")\n\t\t\t\t.split(\"/\")\n\t\t\t\t.filter(Boolean);\n\t\t\tconst subRoute = segments.at(-1);\n\t\t\tlog(\"pathname:\", url.pathname, \"subRoute:\", subRoute);\n\n\t\t\tif (subRoute === \"resource\") {\n\t\t\t\tlog(\"dispatching to resource handler\");\n\t\t\t\tconst response = await handleResource(url);\n\t\t\t\tlog(\"← resource handler returned\", response.status);\n\t\t\t\treturn response;\n\t\t\t}\n\n\t\t\tif (subRoute === \"config\") {\n\t\t\t\tlog(\"dispatching to config handler\");\n\t\t\t\tconst response = await handleConfig();\n\t\t\t\tlog(\"← config handler returned\", response.status);\n\t\t\t\treturn response;\n\t\t\t}\n\n\t\t\tlog(\"← 404 no matching sub-route for\", subRoute);\n\t\t\treturn Response.json({ error: \"Not found\" }, { status: 404 });\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[waniwani:router] GET handler error:\", error);\n\t\t\tconst message =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tlog(\"← 500 from caught error\");\n\t\t\treturn Response.json({ error: message }, { status: 500 });\n\t\t}\n\t}\n\n\treturn { handleChat, handleResource, routeGet };\n}\n","// WaniWani SDK - Next.js Adapter\n\nimport type { WaniWaniClient } from \"../../../types.js\";\nimport { createApiHandler } from \"../api-handler.js\";\nimport type { NextJsHandlerOptions, NextJsHandlerResult } from \"./@types.js\";\n\nexport type { NextJsHandlerOptions, NextJsHandlerResult } from \"./@types.js\";\n\n/**\n * Create Next.js route handlers from a WaniWani client.\n *\n * Returns `{ GET, POST }` for use with catch-all routes.\n * Mount at `app/api/waniwani/[[...path]]/route.ts`:\n *\n * - `POST /api/waniwani` → chat (proxied to WaniWani API)\n * - `GET /api/waniwani/resource?uri=…` → MCP resource content\n *\n * @example\n * ```typescript\n * // app/api/waniwani/[[...path]]/route.ts\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani();\n *\n * export const { GET, POST, dynamic } = toNextJsHandler(wani, {\n * chat: {\n * systemPrompt: \"You are a helpful assistant.\",\n * mcpServerUrl: process.env.MCP_SERVER_URL!,\n * },\n * });\n * ```\n */\nexport function toNextJsHandler(\n\tclient: WaniWaniClient,\n\toptions?: NextJsHandlerOptions,\n): NextJsHandlerResult {\n\tconst { apiKey, baseUrl } = client._config;\n\n\tconst debugEnabled = options?.debug ?? process.env.WANIWANI_DEBUG === \"1\";\n\n\tconst handler = createApiHandler({\n\t\t...options?.chat,\n\t\tapiKey,\n\t\tbaseUrl,\n\t\tdebug: debugEnabled,\n\t});\n\n\treturn {\n\t\tPOST: handler.handleChat,\n\t\tGET: handler.routeGet,\n\t\tdynamic: \"force-dynamic\" as const,\n\t};\n}\n"],"mappings":"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","names":["createLogger","namespace","enabled","args","WaniWaniError","message","status","createChatRequestHandler","deps","apiKey","baseUrl","systemPrompt","maxSteps","beforeRequest","mcpServerUrlOverride","resolveConfig","debug","log","createLogger","request","body","messages","sessionId","effectiveSystemPrompt","result","hookError","status","WaniWaniError","message","mcpServerUrl","upstreamUrl","response","errorBody","headers","upstreamSessionId","error","createResourceHandler","deps","mcpServerUrlOverride","resolveConfig","debug","log","createLogger","url","uri","mcpServerUrl","createMCPClient","StreamableHTTPClientTransport","importError","mcp","result","content","html","error","message","status","WaniWaniError","TTL_MS","createMcpConfigResolver","baseUrl","apiKey","cached","inflight","WaniWaniError","response","body","data","createApiHandler","options","apiKey","baseUrl","systemPrompt","maxSteps","beforeRequest","mcpServerUrl","debug","log","createLogger","resolveConfig","createMcpConfigResolver","handleChat","createChatRequestHandler","handleResource","createResourceHandler","handleConfig","routeGet","request","url","subRoute","response","error","message","toNextJsHandler","client","options","apiKey","baseUrl","debugEnabled","handler","createApiHandler"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
interface Chunk {
|
|
2
|
+
id: string;
|
|
3
|
+
source: string;
|
|
4
|
+
heading: string;
|
|
5
|
+
content: string;
|
|
6
|
+
embedding: number[];
|
|
7
|
+
}
|
|
8
|
+
interface EmbeddingsFile {
|
|
9
|
+
model: string;
|
|
10
|
+
dimensions?: number;
|
|
11
|
+
generatedAt: string;
|
|
12
|
+
chunks: Chunk[];
|
|
13
|
+
}
|
|
14
|
+
interface SearchResult {
|
|
15
|
+
source: string;
|
|
16
|
+
heading: string;
|
|
17
|
+
content: string;
|
|
18
|
+
score: number;
|
|
19
|
+
}
|
|
20
|
+
interface GenerateEmbeddingsOptions {
|
|
21
|
+
/** Directory containing .md files to embed */
|
|
22
|
+
knowledgeDir: string;
|
|
23
|
+
/** Output path for embeddings.json */
|
|
24
|
+
outputPath: string;
|
|
25
|
+
/** Embedding model string (default: "openai/text-embedding-3-small") */
|
|
26
|
+
model?: string;
|
|
27
|
+
/** Embedding dimensions (default: 512) */
|
|
28
|
+
dimensions?: number;
|
|
29
|
+
}
|
|
30
|
+
interface KnowledgeBase {
|
|
31
|
+
/** Search the knowledge base for relevant chunks */
|
|
32
|
+
search(query: string, topK?: number): Promise<SearchResult[]>;
|
|
33
|
+
/** Number of chunks in the knowledge base */
|
|
34
|
+
readonly chunkCount: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare function chunkMarkdown(filename: string, content: string): Omit<Chunk, "embedding">[];
|
|
38
|
+
declare function generateEmbeddings(options: GenerateEmbeddingsOptions): Promise<void>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a knowledge base instance from pre-loaded embeddings data.
|
|
42
|
+
*
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import embeddings from "./embeddings.json";
|
|
45
|
+
* const kb = loadKnowledgeBase(embeddings);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function loadKnowledgeBase(source: EmbeddingsFile): KnowledgeBase;
|
|
49
|
+
|
|
50
|
+
export { type Chunk, type EmbeddingsFile, type GenerateEmbeddingsOptions, type KnowledgeBase, type SearchResult, chunkMarkdown, generateEmbeddings, loadKnowledgeBase };
|
package/dist/kb/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{readdir as b,readFile as f,writeFile as k}from"fs/promises";import{join as $}from"path";import{embedMany as w}from"ai";var E="openai/text-embedding-3-small",y=512;function p(o,d){let a=d.split(`
|
|
2
|
+
`),i=[],s=d.match(/^# (.+)$/m)?.[1]??o.replace(".md",""),n=s,r=[],c=0;for(let e of a){if(e.match(/^# /))continue;let m=e.match(/^## (.+)$/);if(m){if(r.length>0){let g=r.join(`
|
|
3
|
+
`).trim();g&&(i.push({id:`${o.replace(".md","")}#${c}`,source:o,heading:n,content:`${s}: ${n}
|
|
4
|
+
|
|
5
|
+
${g}`}),c++)}n=m[1],r=[]}else r.push(e)}let t=r.join(`
|
|
6
|
+
`).trim();return t&&i.push({id:`${o.replace(".md","")}#${c}`,source:o,heading:n,content:`${s}: ${n}
|
|
7
|
+
|
|
8
|
+
${t}`}),i}async function O(o){let{knowledgeDir:d,outputPath:a,model:i=E,dimensions:l=y}=o,s=(await b(d)).filter(e=>e.endsWith(".md"));console.log(`Found ${s.length} knowledge files`);let n=[];for(let e of s){let m=await f($(d,e),"utf-8"),g=p(e,m);n.push(...g),console.log(` ${e}: ${g.length} chunk(s)`)}console.log(`
|
|
9
|
+
Total chunks: ${n.length}`),console.log(`Generating embeddings with ${i}...`);let{embeddings:r}=await w({model:i,values:n.map(e=>e.content),providerOptions:{openai:{dimensions:l}}}),c=n.map((e,m)=>({...e,embedding:r[m]})),t={model:i,dimensions:l,generatedAt:new Date().toISOString(),chunks:c};await k(a,JSON.stringify(t)),console.log(`
|
|
10
|
+
Written ${a} (${c.length} chunks)`)}import{cosineSimilarity as F,embed as x}from"ai";function C(o){function d(){return o}return{get chunkCount(){return d().chunks.length},async search(a,i=5){let{model:l,dimensions:s,chunks:n}=d(),{embedding:r}=await x({model:l,value:a,...s&&{providerOptions:{openai:{dimensions:s}}}});return n.map(t=>({source:t.source,heading:t.heading,content:t.content,score:F(r,t.embedding)})).sort((t,e)=>e.score-t.score).slice(0,i)}}}export{p as chunkMarkdown,O as generateEmbeddings,C as loadKnowledgeBase};
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/kb/embed.ts","../../src/kb/search.ts"],"sourcesContent":["import { readdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { embedMany } from \"ai\";\nimport type { Chunk, EmbeddingsFile, GenerateEmbeddingsOptions } from \"./types\";\n\nconst DEFAULT_MODEL = \"openai/text-embedding-3-small\";\nconst DEFAULT_DIMENSIONS = 512;\n\nexport function chunkMarkdown(\n\tfilename: string,\n\tcontent: string,\n): Omit<Chunk, \"embedding\">[] {\n\tconst lines = content.split(\"\\n\");\n\tconst chunks: Omit<Chunk, \"embedding\">[] = [];\n\n\tconst h1Match = content.match(/^# (.+)$/m);\n\tconst title = h1Match?.[1] ?? filename.replace(\".md\", \"\");\n\n\tlet currentHeading = title;\n\tlet currentContent: string[] = [];\n\tlet chunkIndex = 0;\n\n\tfor (const line of lines) {\n\t\tif (line.match(/^# /)) continue;\n\n\t\tconst h2Match = line.match(/^## (.+)$/);\n\t\tif (h2Match) {\n\t\t\tif (currentContent.length > 0) {\n\t\t\t\tconst text = currentContent.join(\"\\n\").trim();\n\t\t\t\tif (text) {\n\t\t\t\t\tchunks.push({\n\t\t\t\t\t\tid: `${filename.replace(\".md\", \"\")}#${chunkIndex}`,\n\t\t\t\t\t\tsource: filename,\n\t\t\t\t\t\theading: currentHeading,\n\t\t\t\t\t\tcontent: `${title}: ${currentHeading}\\n\\n${text}`,\n\t\t\t\t\t});\n\t\t\t\t\tchunkIndex++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentHeading = h2Match[1];\n\t\t\tcurrentContent = [];\n\t\t} else {\n\t\t\tcurrentContent.push(line);\n\t\t}\n\t}\n\n\tconst text = currentContent.join(\"\\n\").trim();\n\tif (text) {\n\t\tchunks.push({\n\t\t\tid: `${filename.replace(\".md\", \"\")}#${chunkIndex}`,\n\t\t\tsource: filename,\n\t\t\theading: currentHeading,\n\t\t\tcontent: `${title}: ${currentHeading}\\n\\n${text}`,\n\t\t});\n\t}\n\n\treturn chunks;\n}\n\nexport async function generateEmbeddings(\n\toptions: GenerateEmbeddingsOptions,\n): Promise<void> {\n\tconst {\n\t\tknowledgeDir,\n\t\toutputPath,\n\t\tmodel = DEFAULT_MODEL,\n\t\tdimensions = DEFAULT_DIMENSIONS,\n\t} = options;\n\n\tconst files = (await readdir(knowledgeDir)).filter((f) => f.endsWith(\".md\"));\n\tconsole.log(`Found ${files.length} knowledge files`);\n\n\tconst allChunks: Omit<Chunk, \"embedding\">[] = [];\n\tfor (const file of files) {\n\t\tconst content = await readFile(join(knowledgeDir, file), \"utf-8\");\n\t\tconst chunks = chunkMarkdown(file, content);\n\t\tallChunks.push(...chunks);\n\t\tconsole.log(` ${file}: ${chunks.length} chunk(s)`);\n\t}\n\n\tconsole.log(`\\nTotal chunks: ${allChunks.length}`);\n\tconsole.log(`Generating embeddings with ${model}...`);\n\n\tconst { embeddings } = await embedMany({\n\t\tmodel,\n\t\tvalues: allChunks.map((c) => c.content),\n\t\tproviderOptions: { openai: { dimensions } },\n\t});\n\n\tconst chunks: Chunk[] = allChunks.map((chunk, i) => ({\n\t\t...chunk,\n\t\tembedding: embeddings[i],\n\t}));\n\n\tconst output: EmbeddingsFile = {\n\t\tmodel,\n\t\tdimensions,\n\t\tgeneratedAt: new Date().toISOString(),\n\t\tchunks,\n\t};\n\n\tawait writeFile(outputPath, JSON.stringify(output));\n\tconsole.log(`\\nWritten ${outputPath} (${chunks.length} chunks)`);\n}\n","import { cosineSimilarity, embed } from \"ai\";\nimport type { EmbeddingsFile, KnowledgeBase, SearchResult } from \"./types\";\n\n/**\n * Create a knowledge base instance from pre-loaded embeddings data.\n *\n * ```typescript\n * import embeddings from \"./embeddings.json\";\n * const kb = loadKnowledgeBase(embeddings);\n * ```\n */\nexport function loadKnowledgeBase(source: EmbeddingsFile): KnowledgeBase {\n\tfunction getEmbeddings(): EmbeddingsFile {\n\t\treturn source;\n\t}\n\n\treturn {\n\t\tget chunkCount() {\n\t\t\treturn getEmbeddings().chunks.length;\n\t\t},\n\n\t\tasync search(query: string, topK = 5): Promise<SearchResult[]> {\n\t\t\tconst { model, dimensions, chunks } = getEmbeddings();\n\n\t\t\tconst { embedding: queryEmbedding } = await embed({\n\t\t\t\tmodel,\n\t\t\t\tvalue: query,\n\t\t\t\t...(dimensions && {\n\t\t\t\t\tproviderOptions: { openai: { dimensions } },\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tconst scored = chunks.map((chunk) => ({\n\t\t\t\tsource: chunk.source,\n\t\t\t\theading: chunk.heading,\n\t\t\t\tcontent: chunk.content,\n\t\t\t\tscore: cosineSimilarity(queryEmbedding, chunk.embedding),\n\t\t\t}));\n\n\t\t\treturn scored.sort((a, b) => b.score - a.score).slice(0, topK);\n\t\t},\n\t};\n}\n"],"mappings":"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","names":["readdir","readFile","writeFile","join","embedMany","DEFAULT_MODEL","DEFAULT_DIMENSIONS","chunkMarkdown","filename","content","lines","chunks","title","currentHeading","currentContent","chunkIndex","line","h2Match","text","generateEmbeddings","options","knowledgeDir","outputPath","model","dimensions","files","f","allChunks","file","embeddings","c","chunk","i","output","cosineSimilarity","embed","loadKnowledgeBase","source","getEmbeddings","query","topK","model","dimensions","chunks","queryEmbedding","chunk","a","b"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waniwani/sdk",
|
|
3
|
-
"version": "0.1.20-beta.
|
|
3
|
+
"version": "0.1.20-beta.26",
|
|
4
4
|
"description": "WaniWani SDK - MCP event tracking, widget framework, and tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"types": "./dist/chat/next-js/index.d.ts",
|
|
35
35
|
"import": "./dist/chat/next-js/index.js",
|
|
36
36
|
"default": "./dist/chat/next-js/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./kb": {
|
|
39
|
+
"types": "./dist/kb/index.d.ts",
|
|
40
|
+
"import": "./dist/kb/index.js",
|
|
41
|
+
"default": "./dist/kb/index.js"
|
|
37
42
|
}
|
|
38
43
|
},
|
|
39
44
|
"files": [
|