vanilla-agent-proxy 0.1.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 ADDED
@@ -0,0 +1,106 @@
1
+ ## Vanilla Agent Proxy
2
+
3
+ Proxy server library for `vanilla-agent` widget. Handles flow configuration and forwards requests to Travrse or other AI backends.
4
+
5
+ ### Installation
6
+
7
+ ```bash
8
+ npm install vanilla-agent-proxy
9
+ ```
10
+
11
+ ### Usage
12
+
13
+ The proxy server handles flow configuration and forwards requests to Travrse. You can configure it in three ways:
14
+
15
+ **Option 1: Use default flow (recommended for getting started)**
16
+
17
+ ```ts
18
+ // api/chat.ts
19
+ import { createChatProxyApp } from 'vanilla-agent-proxy';
20
+
21
+ export default createChatProxyApp({
22
+ path: '/api/chat/dispatch',
23
+ allowedOrigins: ['https://www.example.com']
24
+ });
25
+ ```
26
+
27
+ **Option 2: Reference a Travrse flow ID**
28
+
29
+ ```ts
30
+ import { createChatProxyApp } from 'vanilla-agent-proxy';
31
+
32
+ export default createChatProxyApp({
33
+ path: '/api/chat/dispatch',
34
+ allowedOrigins: ['https://www.example.com'],
35
+ flowId: 'flow_abc123' // Flow created in Travrse dashboard or API
36
+ });
37
+ ```
38
+
39
+ **Option 3: Define a custom flow**
40
+
41
+ ```ts
42
+ import { createChatProxyApp } from 'vanilla-agent-proxy';
43
+
44
+ export default createChatProxyApp({
45
+ path: '/api/chat/dispatch',
46
+ allowedOrigins: ['https://www.example.com'],
47
+ flowConfig: {
48
+ name: "Custom Chat Flow",
49
+ description: "Specialized assistant flow",
50
+ steps: [
51
+ {
52
+ id: "custom_prompt",
53
+ name: "Custom Prompt",
54
+ type: "prompt",
55
+ enabled: true,
56
+ config: {
57
+ model: "meta/llama3.1-8b-instruct-free",
58
+ responseFormat: "markdown",
59
+ outputVariable: "prompt_result",
60
+ userPrompt: "{{user_message}}",
61
+ systemPrompt: "you are a helpful assistant, chatting with a user",
62
+ previousMessages: "{{messages}}"
63
+ }
64
+ }
65
+ ]
66
+ }
67
+ });
68
+ ```
69
+
70
+ **Hosting on Vercel:**
71
+
72
+ ```ts
73
+ import { createVercelHandler } from 'vanilla-agent-proxy';
74
+
75
+ export default createVercelHandler({
76
+ allowedOrigins: ['https://www.example.com'],
77
+ flowId: 'flow_abc123' // Optional
78
+ });
79
+ ```
80
+
81
+ ### Configuration Options
82
+
83
+ | Option | Type | Description |
84
+ | --- | --- | --- |
85
+ | `upstreamUrl` | `string` | Travrse API endpoint (defaults to `https://api.travrse.ai/v1/dispatch`) |
86
+ | `apiKey` | `string` | Travrse API key (defaults to `TRAVRSE_API_KEY` environment variable) |
87
+ | `path` | `string` | Proxy endpoint path (defaults to `/api/chat/dispatch`) |
88
+ | `allowedOrigins` | `string[]` | CORS allowed origins |
89
+ | `flowId` | `string` | Travrse flow ID to use |
90
+ | `flowConfig` | `TravrseFlowConfig` | Custom flow configuration |
91
+
92
+ ### Environment Setup
93
+
94
+ Add `TRAVRSE_API_KEY` to your environment. The proxy constructs the Travrse payload (including flow configuration) and streams the response back to the client.
95
+
96
+ ### Building
97
+
98
+ ```bash
99
+ pnpm build
100
+ ```
101
+
102
+ This generates:
103
+ - `dist/index.js` (ESM)
104
+ - `dist/index.cjs` (CJS)
105
+ - Type definitions in `dist/index.d.ts`
106
+
package/dist/index.cjs ADDED
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createChatProxyApp: () => createChatProxyApp,
24
+ createVercelHandler: () => createVercelHandler,
25
+ default: () => index_default
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var import_hono = require("hono");
29
+ var import_vercel = require("hono/vercel");
30
+ var DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
31
+ var DEFAULT_PATH = "/api/chat/dispatch";
32
+ var DEFAULT_FLOW = {
33
+ name: "Streaming Prompt Flow",
34
+ description: "Streaming chat generated by the widget",
35
+ steps: [
36
+ {
37
+ id: "widget_prompt",
38
+ name: "Prompt",
39
+ type: "prompt",
40
+ enabled: true,
41
+ config: {
42
+ model: "meta/llama3.1-8b-instruct-free",
43
+ // model: "gpt-4o",
44
+ response_format: "markdown",
45
+ output_variable: "prompt_result",
46
+ user_prompt: "{{user_message}}",
47
+ system_prompt: "you are a helpful assistant, chatting with a user",
48
+ // tools: {
49
+ // tool_ids: [
50
+ // "builtin:dalle"
51
+ // ]
52
+ // },
53
+ previous_messages: "{{messages}}"
54
+ }
55
+ }
56
+ ]
57
+ };
58
+ var withCors = (allowedOrigins) => async (c, next) => {
59
+ var _a, _b;
60
+ const origin = (_a = c.req.header("origin")) != null ? _a : "*";
61
+ const headers = {
62
+ "Access-Control-Allow-Origin": allowedOrigins && allowedOrigins.length ? allowedOrigins.includes(origin) ? origin : allowedOrigins[0] : origin,
63
+ "Access-Control-Allow-Headers": (_b = c.req.header("access-control-request-headers")) != null ? _b : "Content-Type, Authorization",
64
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
65
+ Vary: "Origin"
66
+ };
67
+ if (c.req.method === "OPTIONS") {
68
+ return new Response(null, { status: 204, headers });
69
+ }
70
+ await next();
71
+ Object.entries(headers).forEach(
72
+ ([key, value]) => c.header(key, value, { append: false })
73
+ );
74
+ };
75
+ var createChatProxyApp = (options = {}) => {
76
+ var _a, _b;
77
+ const app = new import_hono.Hono();
78
+ const path = (_a = options.path) != null ? _a : DEFAULT_PATH;
79
+ const upstream = (_b = options.upstreamUrl) != null ? _b : DEFAULT_ENDPOINT;
80
+ app.use("*", withCors(options.allowedOrigins));
81
+ app.post(path, async (c) => {
82
+ var _a2, _b2, _c, _d, _e;
83
+ const apiKey = (_a2 = options.apiKey) != null ? _a2 : process.env.TRAVRSE_API_KEY;
84
+ if (!apiKey) {
85
+ return c.json(
86
+ { error: "Missing API key. Set TRAVRSE_API_KEY." },
87
+ 401
88
+ );
89
+ }
90
+ let clientPayload;
91
+ try {
92
+ clientPayload = await c.req.json();
93
+ } catch (error) {
94
+ return c.json(
95
+ { error: "Invalid JSON body", details: error },
96
+ 400
97
+ );
98
+ }
99
+ const messages = (_b2 = clientPayload.messages) != null ? _b2 : [];
100
+ const sortedMessages = [...messages].sort((a, b) => {
101
+ const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
102
+ const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
103
+ return timeA - timeB;
104
+ });
105
+ const formattedMessages = sortedMessages.map((message) => ({
106
+ role: message.role,
107
+ content: message.content
108
+ }));
109
+ const flowId = (_c = clientPayload.flowId) != null ? _c : options.flowId;
110
+ const flowConfig = (_d = options.flowConfig) != null ? _d : DEFAULT_FLOW;
111
+ const travrsePayload = {
112
+ record: {
113
+ name: "Streaming Chat Widget",
114
+ type: "standalone",
115
+ metadata: {}
116
+ },
117
+ messages: formattedMessages,
118
+ options: {
119
+ stream_response: true,
120
+ record_mode: "virtual",
121
+ flow_mode: flowId ? "existing" : "virtual",
122
+ auto_append_metadata: false
123
+ }
124
+ };
125
+ if (flowId) {
126
+ travrsePayload.flow = {
127
+ "name": "Chat with 8b",
128
+ "description": "Flow with 1 step",
129
+ "steps": [
130
+ {
131
+ "id": "step_01k8wnwpdcferbrq79tzj49aec",
132
+ "name": "Prompt 1",
133
+ "type": "prompt",
134
+ "order": 0,
135
+ "enabled": true,
136
+ "config": {
137
+ "text": "{{user_message}}",
138
+ "model": "qwen/qwen3-8b",
139
+ // "tools": {
140
+ // "tool_ids": [
141
+ // "tool_01k8ky2xpjfzybye5ywcmjr379",
142
+ // "builtin:firecrawl"
143
+ // ]
144
+ // },
145
+ "reasoning": false,
146
+ "user_prompt": "{{user_message}}",
147
+ "output_variable": "prompt_result",
148
+ "response_format": "JSON"
149
+ }
150
+ }
151
+ ]
152
+ };
153
+ } else {
154
+ travrsePayload.flow = flowConfig;
155
+ }
156
+ const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
157
+ if (isDevelopment) {
158
+ console.log("\n=== Travrse Proxy Request ===");
159
+ console.log("URL:", upstream);
160
+ console.log("API Key Used:", apiKey ? "Yes" : "No");
161
+ console.log("API Key (first 12 chars):", apiKey ? apiKey.substring(0, 12) : "N/A");
162
+ console.log("Request Payload:", JSON.stringify(travrsePayload, null, 2));
163
+ }
164
+ const response = await fetch(upstream, {
165
+ method: "POST",
166
+ headers: {
167
+ Authorization: `Bearer ${apiKey}`,
168
+ "Content-Type": "application/json"
169
+ },
170
+ body: JSON.stringify(travrsePayload)
171
+ });
172
+ if (isDevelopment) {
173
+ console.log("Response Status:", response.status);
174
+ console.log("Response Status Text:", response.statusText);
175
+ if (!response.ok) {
176
+ const clonedResponse = response.clone();
177
+ try {
178
+ const errorBody = await clonedResponse.text();
179
+ console.log("Error Response Body:", errorBody);
180
+ } catch (e) {
181
+ console.log("Could not read error response body:", e);
182
+ }
183
+ }
184
+ console.log("=== End Travrse Proxy Request ===\n");
185
+ }
186
+ return new Response(response.body, {
187
+ status: response.status,
188
+ headers: {
189
+ "Content-Type": (_e = response.headers.get("content-type")) != null ? _e : "application/json",
190
+ "Cache-Control": "no-store"
191
+ }
192
+ });
193
+ });
194
+ return app;
195
+ };
196
+ var createVercelHandler = (options) => (0, import_vercel.handle)(createChatProxyApp(options));
197
+ var index_default = createChatProxyApp;
198
+ // Annotate the CommonJS export names for ESM import in node:
199
+ 0 && (module.exports = {
200
+ createChatProxyApp,
201
+ createVercelHandler
202
+ });
203
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport { handle } from \"hono/vercel\";\n\nexport type TravrseFlowStep = {\n id: string;\n name: string;\n type: string;\n enabled: boolean;\n config: Record<string, unknown>;\n};\n\nexport type TravrseFlowConfig = {\n name: string;\n description: string;\n steps: TravrseFlowStep[];\n};\n\nexport type ChatProxyOptions = {\n upstreamUrl?: string;\n apiKey?: string;\n path?: string;\n allowedOrigins?: string[];\n flowId?: string;\n flowConfig?: TravrseFlowConfig;\n};\n\nconst DEFAULT_ENDPOINT = \"https://api.travrse.ai/v1/dispatch\";\nconst DEFAULT_PATH = \"/api/chat/dispatch\";\n\nconst DEFAULT_FLOW: TravrseFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"meta/llama3.1-8b-instruct-free\",\n // model: \"gpt-4o\",\n response_format: \"markdown\",\n output_variable: \"prompt_result\",\n user_prompt: \"{{user_message}}\",\n system_prompt: \"you are a helpful assistant, chatting with a user\",\n // tools: {\n // tool_ids: [\n // \"builtin:dalle\"\n // ]\n // },\n previous_messages: \"{{messages}}\"\n }\n }\n ]\n};\n\nconst withCors =\n (allowedOrigins: string[] | undefined) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\") ?? \"*\";\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Origin\":\n allowedOrigins && allowedOrigins.length\n ? allowedOrigins.includes(origin)\n ? origin\n : allowedOrigins[0]\n : origin,\n \"Access-Control-Allow-Headers\":\n c.req.header(\"access-control-request-headers\") ??\n \"Content-Type, Authorization\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n Vary: \"Origin\"\n };\n\n if (c.req.method === \"OPTIONS\") {\n return new Response(null, { status: 204, headers });\n }\n\n await next();\n Object.entries(headers).forEach(([key, value]) =>\n c.header(key, value, { append: false })\n );\n };\n\nexport const createChatProxyApp = (options: ChatProxyOptions = {}) => {\n const app = new Hono();\n const path = options.path ?? DEFAULT_PATH;\n const upstream = options.upstreamUrl ?? DEFAULT_ENDPOINT;\n\n app.use(\"*\", withCors(options.allowedOrigins));\n\n app.post(path, async (c) => {\n const apiKey = options.apiKey ?? process.env.TRAVRSE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set TRAVRSE_API_KEY.\" },\n 401\n );\n }\n\n let clientPayload: {\n messages?: Array<{ role: string; content: string; createdAt?: string }>;\n flowId?: string;\n };\n try {\n clientPayload = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n // Build the Travrse payload\n const messages = clientPayload.messages ?? [];\n // Sort messages by timestamp to ensure correct order\n const sortedMessages = [...messages].sort((a, b) => {\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n const formattedMessages = sortedMessages.map((message) => ({\n role: message.role,\n content: message.content\n }));\n\n // Determine which flow to use\n const flowId = clientPayload.flowId ?? options.flowId;\n const flowConfig = options.flowConfig ?? DEFAULT_FLOW;\n\n const travrsePayload: Record<string, unknown> = {\n record: {\n name: \"Streaming Chat Widget\",\n type: \"standalone\",\n metadata: {}\n },\n messages: formattedMessages,\n options: {\n stream_response: true,\n record_mode: \"virtual\",\n flow_mode: flowId ? \"existing\" : \"virtual\",\n auto_append_metadata: false\n }\n };\n\n // Use flow ID if provided, otherwise use flow config\n if (flowId) {\n travrsePayload.flow = {\n \"name\": \"Chat with 8b\",\n \"description\": \"Flow with 1 step\",\n \"steps\": [\n {\n \"id\": \"step_01k8wnwpdcferbrq79tzj49aec\",\n \"name\": \"Prompt 1\",\n \"type\": \"prompt\",\n \"order\": 0,\n \"enabled\": true,\n \"config\": {\n \"text\": \"{{user_message}}\",\n \"model\": \"qwen/qwen3-8b\",\n // \"tools\": {\n // \"tool_ids\": [\n // \"tool_01k8ky2xpjfzybye5ywcmjr379\",\n // \"builtin:firecrawl\"\n // ]\n // },\n \"reasoning\": false,\n \"user_prompt\": \"{{user_message}}\",\n \"output_variable\": \"prompt_result\",\n \"response_format\": \"JSON\"\n }\n }\n ]\n }\n } else {\n travrsePayload.flow = flowConfig;\n }\n\n // Development logging\n const isDevelopment = process.env.NODE_ENV === \"development\" || !process.env.NODE_ENV;\n\n if (isDevelopment) {\n console.log(\"\\n=== Travrse Proxy Request ===\");\n console.log(\"URL:\", upstream);\n console.log(\"API Key Used:\", apiKey ? \"Yes\" : \"No\");\n console.log(\"API Key (first 12 chars):\", apiKey ? apiKey.substring(0, 12) : \"N/A\");\n console.log(\"Request Payload:\", JSON.stringify(travrsePayload, null, 2));\n }\n\n const response = await fetch(upstream, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(travrsePayload)\n });\n\n if (isDevelopment) {\n console.log(\"Response Status:\", response.status);\n console.log(\"Response Status Text:\", response.statusText);\n\n // If there's an error, try to read and log the response body\n if (!response.ok) {\n const clonedResponse = response.clone();\n try {\n const errorBody = await clonedResponse.text();\n console.log(\"Error Response Body:\", errorBody);\n } catch (e) {\n console.log(\"Could not read error response body:\", e);\n }\n }\n console.log(\"=== End Travrse Proxy Request ===\\n\");\n }\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n return app;\n};\n\nexport const createVercelHandler = (options?: ChatProxyOptions) =>\n handle(createChatProxyApp(options));\n\n\nexport default createChatProxyApp;\n\n"],"mappings":"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","names":["_a","_b"]}
@@ -0,0 +1,27 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+
4
+ type TravrseFlowStep = {
5
+ id: string;
6
+ name: string;
7
+ type: string;
8
+ enabled: boolean;
9
+ config: Record<string, unknown>;
10
+ };
11
+ type TravrseFlowConfig = {
12
+ name: string;
13
+ description: string;
14
+ steps: TravrseFlowStep[];
15
+ };
16
+ type ChatProxyOptions = {
17
+ upstreamUrl?: string;
18
+ apiKey?: string;
19
+ path?: string;
20
+ allowedOrigins?: string[];
21
+ flowId?: string;
22
+ flowConfig?: TravrseFlowConfig;
23
+ };
24
+ declare const createChatProxyApp: (options?: ChatProxyOptions) => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
25
+ declare const createVercelHandler: (options?: ChatProxyOptions) => (req: Request) => Response | Promise<Response>;
26
+
27
+ export { type ChatProxyOptions, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createVercelHandler, createChatProxyApp as default };
@@ -0,0 +1,27 @@
1
+ import * as hono_types from 'hono/types';
2
+ import { Hono } from 'hono';
3
+
4
+ type TravrseFlowStep = {
5
+ id: string;
6
+ name: string;
7
+ type: string;
8
+ enabled: boolean;
9
+ config: Record<string, unknown>;
10
+ };
11
+ type TravrseFlowConfig = {
12
+ name: string;
13
+ description: string;
14
+ steps: TravrseFlowStep[];
15
+ };
16
+ type ChatProxyOptions = {
17
+ upstreamUrl?: string;
18
+ apiKey?: string;
19
+ path?: string;
20
+ allowedOrigins?: string[];
21
+ flowId?: string;
22
+ flowConfig?: TravrseFlowConfig;
23
+ };
24
+ declare const createChatProxyApp: (options?: ChatProxyOptions) => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
25
+ declare const createVercelHandler: (options?: ChatProxyOptions) => (req: Request) => Response | Promise<Response>;
26
+
27
+ export { type ChatProxyOptions, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createVercelHandler, createChatProxyApp as default };
package/dist/index.js ADDED
@@ -0,0 +1,177 @@
1
+ // src/index.ts
2
+ import { Hono } from "hono";
3
+ import { handle } from "hono/vercel";
4
+ var DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
5
+ var DEFAULT_PATH = "/api/chat/dispatch";
6
+ var DEFAULT_FLOW = {
7
+ name: "Streaming Prompt Flow",
8
+ description: "Streaming chat generated by the widget",
9
+ steps: [
10
+ {
11
+ id: "widget_prompt",
12
+ name: "Prompt",
13
+ type: "prompt",
14
+ enabled: true,
15
+ config: {
16
+ model: "meta/llama3.1-8b-instruct-free",
17
+ // model: "gpt-4o",
18
+ response_format: "markdown",
19
+ output_variable: "prompt_result",
20
+ user_prompt: "{{user_message}}",
21
+ system_prompt: "you are a helpful assistant, chatting with a user",
22
+ // tools: {
23
+ // tool_ids: [
24
+ // "builtin:dalle"
25
+ // ]
26
+ // },
27
+ previous_messages: "{{messages}}"
28
+ }
29
+ }
30
+ ]
31
+ };
32
+ var withCors = (allowedOrigins) => async (c, next) => {
33
+ var _a, _b;
34
+ const origin = (_a = c.req.header("origin")) != null ? _a : "*";
35
+ const headers = {
36
+ "Access-Control-Allow-Origin": allowedOrigins && allowedOrigins.length ? allowedOrigins.includes(origin) ? origin : allowedOrigins[0] : origin,
37
+ "Access-Control-Allow-Headers": (_b = c.req.header("access-control-request-headers")) != null ? _b : "Content-Type, Authorization",
38
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
39
+ Vary: "Origin"
40
+ };
41
+ if (c.req.method === "OPTIONS") {
42
+ return new Response(null, { status: 204, headers });
43
+ }
44
+ await next();
45
+ Object.entries(headers).forEach(
46
+ ([key, value]) => c.header(key, value, { append: false })
47
+ );
48
+ };
49
+ var createChatProxyApp = (options = {}) => {
50
+ var _a, _b;
51
+ const app = new Hono();
52
+ const path = (_a = options.path) != null ? _a : DEFAULT_PATH;
53
+ const upstream = (_b = options.upstreamUrl) != null ? _b : DEFAULT_ENDPOINT;
54
+ app.use("*", withCors(options.allowedOrigins));
55
+ app.post(path, async (c) => {
56
+ var _a2, _b2, _c, _d, _e;
57
+ const apiKey = (_a2 = options.apiKey) != null ? _a2 : process.env.TRAVRSE_API_KEY;
58
+ if (!apiKey) {
59
+ return c.json(
60
+ { error: "Missing API key. Set TRAVRSE_API_KEY." },
61
+ 401
62
+ );
63
+ }
64
+ let clientPayload;
65
+ try {
66
+ clientPayload = await c.req.json();
67
+ } catch (error) {
68
+ return c.json(
69
+ { error: "Invalid JSON body", details: error },
70
+ 400
71
+ );
72
+ }
73
+ const messages = (_b2 = clientPayload.messages) != null ? _b2 : [];
74
+ const sortedMessages = [...messages].sort((a, b) => {
75
+ const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
76
+ const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
77
+ return timeA - timeB;
78
+ });
79
+ const formattedMessages = sortedMessages.map((message) => ({
80
+ role: message.role,
81
+ content: message.content
82
+ }));
83
+ const flowId = (_c = clientPayload.flowId) != null ? _c : options.flowId;
84
+ const flowConfig = (_d = options.flowConfig) != null ? _d : DEFAULT_FLOW;
85
+ const travrsePayload = {
86
+ record: {
87
+ name: "Streaming Chat Widget",
88
+ type: "standalone",
89
+ metadata: {}
90
+ },
91
+ messages: formattedMessages,
92
+ options: {
93
+ stream_response: true,
94
+ record_mode: "virtual",
95
+ flow_mode: flowId ? "existing" : "virtual",
96
+ auto_append_metadata: false
97
+ }
98
+ };
99
+ if (flowId) {
100
+ travrsePayload.flow = {
101
+ "name": "Chat with 8b",
102
+ "description": "Flow with 1 step",
103
+ "steps": [
104
+ {
105
+ "id": "step_01k8wnwpdcferbrq79tzj49aec",
106
+ "name": "Prompt 1",
107
+ "type": "prompt",
108
+ "order": 0,
109
+ "enabled": true,
110
+ "config": {
111
+ "text": "{{user_message}}",
112
+ "model": "qwen/qwen3-8b",
113
+ // "tools": {
114
+ // "tool_ids": [
115
+ // "tool_01k8ky2xpjfzybye5ywcmjr379",
116
+ // "builtin:firecrawl"
117
+ // ]
118
+ // },
119
+ "reasoning": false,
120
+ "user_prompt": "{{user_message}}",
121
+ "output_variable": "prompt_result",
122
+ "response_format": "JSON"
123
+ }
124
+ }
125
+ ]
126
+ };
127
+ } else {
128
+ travrsePayload.flow = flowConfig;
129
+ }
130
+ const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
131
+ if (isDevelopment) {
132
+ console.log("\n=== Travrse Proxy Request ===");
133
+ console.log("URL:", upstream);
134
+ console.log("API Key Used:", apiKey ? "Yes" : "No");
135
+ console.log("API Key (first 12 chars):", apiKey ? apiKey.substring(0, 12) : "N/A");
136
+ console.log("Request Payload:", JSON.stringify(travrsePayload, null, 2));
137
+ }
138
+ const response = await fetch(upstream, {
139
+ method: "POST",
140
+ headers: {
141
+ Authorization: `Bearer ${apiKey}`,
142
+ "Content-Type": "application/json"
143
+ },
144
+ body: JSON.stringify(travrsePayload)
145
+ });
146
+ if (isDevelopment) {
147
+ console.log("Response Status:", response.status);
148
+ console.log("Response Status Text:", response.statusText);
149
+ if (!response.ok) {
150
+ const clonedResponse = response.clone();
151
+ try {
152
+ const errorBody = await clonedResponse.text();
153
+ console.log("Error Response Body:", errorBody);
154
+ } catch (e) {
155
+ console.log("Could not read error response body:", e);
156
+ }
157
+ }
158
+ console.log("=== End Travrse Proxy Request ===\n");
159
+ }
160
+ return new Response(response.body, {
161
+ status: response.status,
162
+ headers: {
163
+ "Content-Type": (_e = response.headers.get("content-type")) != null ? _e : "application/json",
164
+ "Cache-Control": "no-store"
165
+ }
166
+ });
167
+ });
168
+ return app;
169
+ };
170
+ var createVercelHandler = (options) => handle(createChatProxyApp(options));
171
+ var index_default = createChatProxyApp;
172
+ export {
173
+ createChatProxyApp,
174
+ createVercelHandler,
175
+ index_default as default
176
+ };
177
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport { handle } from \"hono/vercel\";\n\nexport type TravrseFlowStep = {\n id: string;\n name: string;\n type: string;\n enabled: boolean;\n config: Record<string, unknown>;\n};\n\nexport type TravrseFlowConfig = {\n name: string;\n description: string;\n steps: TravrseFlowStep[];\n};\n\nexport type ChatProxyOptions = {\n upstreamUrl?: string;\n apiKey?: string;\n path?: string;\n allowedOrigins?: string[];\n flowId?: string;\n flowConfig?: TravrseFlowConfig;\n};\n\nconst DEFAULT_ENDPOINT = \"https://api.travrse.ai/v1/dispatch\";\nconst DEFAULT_PATH = \"/api/chat/dispatch\";\n\nconst DEFAULT_FLOW: TravrseFlowConfig = {\n name: \"Streaming Prompt Flow\",\n description: \"Streaming chat generated by the widget\",\n steps: [\n {\n id: \"widget_prompt\",\n name: \"Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"meta/llama3.1-8b-instruct-free\",\n // model: \"gpt-4o\",\n response_format: \"markdown\",\n output_variable: \"prompt_result\",\n user_prompt: \"{{user_message}}\",\n system_prompt: \"you are a helpful assistant, chatting with a user\",\n // tools: {\n // tool_ids: [\n // \"builtin:dalle\"\n // ]\n // },\n previous_messages: \"{{messages}}\"\n }\n }\n ]\n};\n\nconst withCors =\n (allowedOrigins: string[] | undefined) =>\n async (c: Context, next: () => Promise<void>) => {\n const origin = c.req.header(\"origin\") ?? \"*\";\n const headers: Record<string, string> = {\n \"Access-Control-Allow-Origin\":\n allowedOrigins && allowedOrigins.length\n ? allowedOrigins.includes(origin)\n ? origin\n : allowedOrigins[0]\n : origin,\n \"Access-Control-Allow-Headers\":\n c.req.header(\"access-control-request-headers\") ??\n \"Content-Type, Authorization\",\n \"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n Vary: \"Origin\"\n };\n\n if (c.req.method === \"OPTIONS\") {\n return new Response(null, { status: 204, headers });\n }\n\n await next();\n Object.entries(headers).forEach(([key, value]) =>\n c.header(key, value, { append: false })\n );\n };\n\nexport const createChatProxyApp = (options: ChatProxyOptions = {}) => {\n const app = new Hono();\n const path = options.path ?? DEFAULT_PATH;\n const upstream = options.upstreamUrl ?? DEFAULT_ENDPOINT;\n\n app.use(\"*\", withCors(options.allowedOrigins));\n\n app.post(path, async (c) => {\n const apiKey = options.apiKey ?? process.env.TRAVRSE_API_KEY;\n if (!apiKey) {\n return c.json(\n { error: \"Missing API key. Set TRAVRSE_API_KEY.\" },\n 401\n );\n }\n\n let clientPayload: {\n messages?: Array<{ role: string; content: string; createdAt?: string }>;\n flowId?: string;\n };\n try {\n clientPayload = await c.req.json();\n } catch (error) {\n return c.json(\n { error: \"Invalid JSON body\", details: error },\n 400\n );\n }\n\n // Build the Travrse payload\n const messages = clientPayload.messages ?? [];\n // Sort messages by timestamp to ensure correct order\n const sortedMessages = [...messages].sort((a, b) => {\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n const formattedMessages = sortedMessages.map((message) => ({\n role: message.role,\n content: message.content\n }));\n\n // Determine which flow to use\n const flowId = clientPayload.flowId ?? options.flowId;\n const flowConfig = options.flowConfig ?? DEFAULT_FLOW;\n\n const travrsePayload: Record<string, unknown> = {\n record: {\n name: \"Streaming Chat Widget\",\n type: \"standalone\",\n metadata: {}\n },\n messages: formattedMessages,\n options: {\n stream_response: true,\n record_mode: \"virtual\",\n flow_mode: flowId ? \"existing\" : \"virtual\",\n auto_append_metadata: false\n }\n };\n\n // Use flow ID if provided, otherwise use flow config\n if (flowId) {\n travrsePayload.flow = {\n \"name\": \"Chat with 8b\",\n \"description\": \"Flow with 1 step\",\n \"steps\": [\n {\n \"id\": \"step_01k8wnwpdcferbrq79tzj49aec\",\n \"name\": \"Prompt 1\",\n \"type\": \"prompt\",\n \"order\": 0,\n \"enabled\": true,\n \"config\": {\n \"text\": \"{{user_message}}\",\n \"model\": \"qwen/qwen3-8b\",\n // \"tools\": {\n // \"tool_ids\": [\n // \"tool_01k8ky2xpjfzybye5ywcmjr379\",\n // \"builtin:firecrawl\"\n // ]\n // },\n \"reasoning\": false,\n \"user_prompt\": \"{{user_message}}\",\n \"output_variable\": \"prompt_result\",\n \"response_format\": \"JSON\"\n }\n }\n ]\n }\n } else {\n travrsePayload.flow = flowConfig;\n }\n\n // Development logging\n const isDevelopment = process.env.NODE_ENV === \"development\" || !process.env.NODE_ENV;\n\n if (isDevelopment) {\n console.log(\"\\n=== Travrse Proxy Request ===\");\n console.log(\"URL:\", upstream);\n console.log(\"API Key Used:\", apiKey ? \"Yes\" : \"No\");\n console.log(\"API Key (first 12 chars):\", apiKey ? apiKey.substring(0, 12) : \"N/A\");\n console.log(\"Request Payload:\", JSON.stringify(travrsePayload, null, 2));\n }\n\n const response = await fetch(upstream, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify(travrsePayload)\n });\n\n if (isDevelopment) {\n console.log(\"Response Status:\", response.status);\n console.log(\"Response Status Text:\", response.statusText);\n\n // If there's an error, try to read and log the response body\n if (!response.ok) {\n const clonedResponse = response.clone();\n try {\n const errorBody = await clonedResponse.text();\n console.log(\"Error Response Body:\", errorBody);\n } catch (e) {\n console.log(\"Could not read error response body:\", e);\n }\n }\n console.log(\"=== End Travrse Proxy Request ===\\n\");\n }\n\n return new Response(response.body, {\n status: response.status,\n headers: {\n \"Content-Type\":\n response.headers.get(\"content-type\") ?? \"application/json\",\n \"Cache-Control\": \"no-store\"\n }\n });\n });\n\n return app;\n};\n\nexport const createVercelHandler = (options?: ChatProxyOptions) =>\n handle(createChatProxyApp(options));\n\n\nexport default createChatProxyApp;\n\n"],"mappings":"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","names":["_a","_b"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "vanilla-agent-proxy",
3
+ "version": "0.1.0",
4
+ "description": "Proxy server for vanilla-agent widget - handles flow configuration and forwards requests to Travrse or other AI backends.",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "src"
19
+ ],
20
+ "dependencies": {
21
+ "hono": "^4.4.9"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.12.7",
25
+ "eslint": "^8.57.0",
26
+ "eslint-config-prettier": "^9.1.0",
27
+ "rimraf": "^5.0.5",
28
+ "tsup": "^8.0.1",
29
+ "typescript": "^5.4.5"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.17.0"
33
+ },
34
+ "license": "MIT",
35
+ "keywords": [
36
+ "proxy",
37
+ "server",
38
+ "travrse",
39
+ "chat",
40
+ "typescript"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/becomevocal/chaty.git"
45
+ },
46
+ "scripts": {
47
+ "build": "rimraf dist && tsup src/index.ts --format esm,cjs --dts --sourcemap",
48
+ "lint": "eslint . --ext .ts",
49
+ "typecheck": "tsc --noEmit"
50
+ }
51
+ }
package/src/index.ts ADDED
@@ -0,0 +1,235 @@
1
+ import { Hono } from "hono";
2
+ import type { Context } from "hono";
3
+ import { handle } from "hono/vercel";
4
+
5
+ export type TravrseFlowStep = {
6
+ id: string;
7
+ name: string;
8
+ type: string;
9
+ enabled: boolean;
10
+ config: Record<string, unknown>;
11
+ };
12
+
13
+ export type TravrseFlowConfig = {
14
+ name: string;
15
+ description: string;
16
+ steps: TravrseFlowStep[];
17
+ };
18
+
19
+ export type ChatProxyOptions = {
20
+ upstreamUrl?: string;
21
+ apiKey?: string;
22
+ path?: string;
23
+ allowedOrigins?: string[];
24
+ flowId?: string;
25
+ flowConfig?: TravrseFlowConfig;
26
+ };
27
+
28
+ const DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
29
+ const DEFAULT_PATH = "/api/chat/dispatch";
30
+
31
+ const DEFAULT_FLOW: TravrseFlowConfig = {
32
+ name: "Streaming Prompt Flow",
33
+ description: "Streaming chat generated by the widget",
34
+ steps: [
35
+ {
36
+ id: "widget_prompt",
37
+ name: "Prompt",
38
+ type: "prompt",
39
+ enabled: true,
40
+ config: {
41
+ model: "meta/llama3.1-8b-instruct-free",
42
+ // model: "gpt-4o",
43
+ response_format: "markdown",
44
+ output_variable: "prompt_result",
45
+ user_prompt: "{{user_message}}",
46
+ system_prompt: "you are a helpful assistant, chatting with a user",
47
+ // tools: {
48
+ // tool_ids: [
49
+ // "builtin:dalle"
50
+ // ]
51
+ // },
52
+ previous_messages: "{{messages}}"
53
+ }
54
+ }
55
+ ]
56
+ };
57
+
58
+ const withCors =
59
+ (allowedOrigins: string[] | undefined) =>
60
+ async (c: Context, next: () => Promise<void>) => {
61
+ const origin = c.req.header("origin") ?? "*";
62
+ const headers: Record<string, string> = {
63
+ "Access-Control-Allow-Origin":
64
+ allowedOrigins && allowedOrigins.length
65
+ ? allowedOrigins.includes(origin)
66
+ ? origin
67
+ : allowedOrigins[0]
68
+ : origin,
69
+ "Access-Control-Allow-Headers":
70
+ c.req.header("access-control-request-headers") ??
71
+ "Content-Type, Authorization",
72
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
73
+ Vary: "Origin"
74
+ };
75
+
76
+ if (c.req.method === "OPTIONS") {
77
+ return new Response(null, { status: 204, headers });
78
+ }
79
+
80
+ await next();
81
+ Object.entries(headers).forEach(([key, value]) =>
82
+ c.header(key, value, { append: false })
83
+ );
84
+ };
85
+
86
+ export const createChatProxyApp = (options: ChatProxyOptions = {}) => {
87
+ const app = new Hono();
88
+ const path = options.path ?? DEFAULT_PATH;
89
+ const upstream = options.upstreamUrl ?? DEFAULT_ENDPOINT;
90
+
91
+ app.use("*", withCors(options.allowedOrigins));
92
+
93
+ app.post(path, async (c) => {
94
+ const apiKey = options.apiKey ?? process.env.TRAVRSE_API_KEY;
95
+ if (!apiKey) {
96
+ return c.json(
97
+ { error: "Missing API key. Set TRAVRSE_API_KEY." },
98
+ 401
99
+ );
100
+ }
101
+
102
+ let clientPayload: {
103
+ messages?: Array<{ role: string; content: string; createdAt?: string }>;
104
+ flowId?: string;
105
+ };
106
+ try {
107
+ clientPayload = await c.req.json();
108
+ } catch (error) {
109
+ return c.json(
110
+ { error: "Invalid JSON body", details: error },
111
+ 400
112
+ );
113
+ }
114
+
115
+ // Build the Travrse payload
116
+ const messages = clientPayload.messages ?? [];
117
+ // Sort messages by timestamp to ensure correct order
118
+ const sortedMessages = [...messages].sort((a, b) => {
119
+ const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
120
+ const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
121
+ return timeA - timeB;
122
+ });
123
+ const formattedMessages = sortedMessages.map((message) => ({
124
+ role: message.role,
125
+ content: message.content
126
+ }));
127
+
128
+ // Determine which flow to use
129
+ const flowId = clientPayload.flowId ?? options.flowId;
130
+ const flowConfig = options.flowConfig ?? DEFAULT_FLOW;
131
+
132
+ const travrsePayload: Record<string, unknown> = {
133
+ record: {
134
+ name: "Streaming Chat Widget",
135
+ type: "standalone",
136
+ metadata: {}
137
+ },
138
+ messages: formattedMessages,
139
+ options: {
140
+ stream_response: true,
141
+ record_mode: "virtual",
142
+ flow_mode: flowId ? "existing" : "virtual",
143
+ auto_append_metadata: false
144
+ }
145
+ };
146
+
147
+ // Use flow ID if provided, otherwise use flow config
148
+ if (flowId) {
149
+ travrsePayload.flow = {
150
+ "name": "Chat with 8b",
151
+ "description": "Flow with 1 step",
152
+ "steps": [
153
+ {
154
+ "id": "step_01k8wnwpdcferbrq79tzj49aec",
155
+ "name": "Prompt 1",
156
+ "type": "prompt",
157
+ "order": 0,
158
+ "enabled": true,
159
+ "config": {
160
+ "text": "{{user_message}}",
161
+ "model": "qwen/qwen3-8b",
162
+ // "tools": {
163
+ // "tool_ids": [
164
+ // "tool_01k8ky2xpjfzybye5ywcmjr379",
165
+ // "builtin:firecrawl"
166
+ // ]
167
+ // },
168
+ "reasoning": false,
169
+ "user_prompt": "{{user_message}}",
170
+ "output_variable": "prompt_result",
171
+ "response_format": "JSON"
172
+ }
173
+ }
174
+ ]
175
+ }
176
+ } else {
177
+ travrsePayload.flow = flowConfig;
178
+ }
179
+
180
+ // Development logging
181
+ const isDevelopment = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
182
+
183
+ if (isDevelopment) {
184
+ console.log("\n=== Travrse Proxy Request ===");
185
+ console.log("URL:", upstream);
186
+ console.log("API Key Used:", apiKey ? "Yes" : "No");
187
+ console.log("API Key (first 12 chars):", apiKey ? apiKey.substring(0, 12) : "N/A");
188
+ console.log("Request Payload:", JSON.stringify(travrsePayload, null, 2));
189
+ }
190
+
191
+ const response = await fetch(upstream, {
192
+ method: "POST",
193
+ headers: {
194
+ Authorization: `Bearer ${apiKey}`,
195
+ "Content-Type": "application/json"
196
+ },
197
+ body: JSON.stringify(travrsePayload)
198
+ });
199
+
200
+ if (isDevelopment) {
201
+ console.log("Response Status:", response.status);
202
+ console.log("Response Status Text:", response.statusText);
203
+
204
+ // If there's an error, try to read and log the response body
205
+ if (!response.ok) {
206
+ const clonedResponse = response.clone();
207
+ try {
208
+ const errorBody = await clonedResponse.text();
209
+ console.log("Error Response Body:", errorBody);
210
+ } catch (e) {
211
+ console.log("Could not read error response body:", e);
212
+ }
213
+ }
214
+ console.log("=== End Travrse Proxy Request ===\n");
215
+ }
216
+
217
+ return new Response(response.body, {
218
+ status: response.status,
219
+ headers: {
220
+ "Content-Type":
221
+ response.headers.get("content-type") ?? "application/json",
222
+ "Cache-Control": "no-store"
223
+ }
224
+ });
225
+ });
226
+
227
+ return app;
228
+ };
229
+
230
+ export const createVercelHandler = (options?: ChatProxyOptions) =>
231
+ handle(createChatProxyApp(options));
232
+
233
+
234
+ export default createChatProxyApp;
235
+