vanilla-agent-proxy 0.2.0 → 1.0.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/dist/index.cjs +261 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +257 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/flows/conversational.ts +26 -0
- package/src/flows/index.ts +9 -0
- package/src/flows/shopping-assistant.ts +171 -0
- package/src/index.ts +7 -1
- package/src/utils/index.ts +10 -0
- package/src/utils/stripe.ts +115 -0
package/dist/index.cjs
CHANGED
|
@@ -20,13 +20,269 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
CONVERSATIONAL_FLOW: () => CONVERSATIONAL_FLOW,
|
|
24
|
+
SHOPPING_ASSISTANT_FLOW: () => SHOPPING_ASSISTANT_FLOW,
|
|
25
|
+
SHOPPING_ASSISTANT_METADATA_FLOW: () => SHOPPING_ASSISTANT_METADATA_FLOW,
|
|
23
26
|
createChatProxyApp: () => createChatProxyApp,
|
|
27
|
+
createCheckoutSession: () => createCheckoutSession,
|
|
24
28
|
createVercelHandler: () => createVercelHandler,
|
|
25
29
|
default: () => index_default
|
|
26
30
|
});
|
|
27
31
|
module.exports = __toCommonJS(index_exports);
|
|
28
32
|
var import_hono = require("hono");
|
|
29
33
|
var import_vercel = require("hono/vercel");
|
|
34
|
+
|
|
35
|
+
// src/flows/conversational.ts
|
|
36
|
+
var CONVERSATIONAL_FLOW = {
|
|
37
|
+
name: "Streaming Prompt Flow",
|
|
38
|
+
description: "Streaming chat generated by the widget",
|
|
39
|
+
steps: [
|
|
40
|
+
{
|
|
41
|
+
id: "widget_prompt",
|
|
42
|
+
name: "Prompt",
|
|
43
|
+
type: "prompt",
|
|
44
|
+
enabled: true,
|
|
45
|
+
config: {
|
|
46
|
+
model: "meta/llama3.1-8b-instruct-free",
|
|
47
|
+
response_format: "markdown",
|
|
48
|
+
output_variable: "prompt_result",
|
|
49
|
+
user_prompt: "{{user_message}}",
|
|
50
|
+
system_prompt: "you are a helpful assistant, chatting with a user",
|
|
51
|
+
previous_messages: "{{messages}}"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/flows/shopping-assistant.ts
|
|
58
|
+
var SHOPPING_ASSISTANT_FLOW = {
|
|
59
|
+
name: "Shopping Assistant Flow",
|
|
60
|
+
description: "Returns JSON actions for page interaction",
|
|
61
|
+
steps: [
|
|
62
|
+
{
|
|
63
|
+
id: "action_prompt",
|
|
64
|
+
name: "Action Prompt",
|
|
65
|
+
type: "prompt",
|
|
66
|
+
enabled: true,
|
|
67
|
+
config: {
|
|
68
|
+
model: "qwen/qwen3-8b",
|
|
69
|
+
reasoning: false,
|
|
70
|
+
responseFormat: "JSON",
|
|
71
|
+
outputVariable: "prompt_result",
|
|
72
|
+
userPrompt: "{{user_message}}",
|
|
73
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
74
|
+
You will receive information about the current page's elements (class names and text content)
|
|
75
|
+
and user messages. You must respond with JSON in one of these formats:
|
|
76
|
+
|
|
77
|
+
1. Simple message:
|
|
78
|
+
{
|
|
79
|
+
"action": "message",
|
|
80
|
+
"text": "Your response text here"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
2. Navigate then show message (for navigation to another page):
|
|
84
|
+
{
|
|
85
|
+
"action": "nav_then_click",
|
|
86
|
+
"page": "http://site.com/page-url",
|
|
87
|
+
"on_load_text": "Message to show after navigation"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
3. Show message and click an element:
|
|
91
|
+
{
|
|
92
|
+
"action": "message_and_click",
|
|
93
|
+
"element": ".className-of-element",
|
|
94
|
+
"text": "Your message text"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
4. Create Stripe checkout:
|
|
98
|
+
{
|
|
99
|
+
"action": "checkout",
|
|
100
|
+
"text": "Your message text",
|
|
101
|
+
"items": [
|
|
102
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
Guidelines:
|
|
107
|
+
- Use "message" for simple conversational responses
|
|
108
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
109
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
110
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
111
|
+
- When selecting elements, use the class names provided in the page context
|
|
112
|
+
- Always respond with valid JSON only, no additional text
|
|
113
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
114
|
+
- Be helpful and conversational in your messages
|
|
115
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
116
|
+
|
|
117
|
+
Example conversation flow:
|
|
118
|
+
- User: "I am looking for a black shirt in medium"
|
|
119
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
120
|
+
|
|
121
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
122
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
123
|
+
|
|
124
|
+
- User: "yes"
|
|
125
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
126
|
+
previousMessages: "{{messages}}"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
};
|
|
131
|
+
var SHOPPING_ASSISTANT_METADATA_FLOW = {
|
|
132
|
+
name: "Metadata-Based Shopping Assistant",
|
|
133
|
+
description: "Uses DOM context from record metadata for page interaction",
|
|
134
|
+
steps: [
|
|
135
|
+
{
|
|
136
|
+
id: "metadata_action_prompt",
|
|
137
|
+
name: "Metadata Action Prompt",
|
|
138
|
+
type: "prompt",
|
|
139
|
+
enabled: true,
|
|
140
|
+
config: {
|
|
141
|
+
model: "qwen/qwen3-8b",
|
|
142
|
+
reasoning: false,
|
|
143
|
+
responseFormat: "JSON",
|
|
144
|
+
outputVariable: "prompt_result",
|
|
145
|
+
userPrompt: "{{user_message}}",
|
|
146
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
147
|
+
|
|
148
|
+
IMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:
|
|
149
|
+
- dom_elements: Array of page elements with className, innerText, and tagName
|
|
150
|
+
- dom_body: Complete HTML body of the page (if provided)
|
|
151
|
+
- page_url: Current page URL
|
|
152
|
+
- page_title: Page title
|
|
153
|
+
|
|
154
|
+
The dom_elements array provides information about clickable elements and their text content.
|
|
155
|
+
Use this metadata to understand what's available on the page and help users interact with it.
|
|
156
|
+
|
|
157
|
+
You must respond with JSON in one of these formats:
|
|
158
|
+
|
|
159
|
+
1. Simple message:
|
|
160
|
+
{
|
|
161
|
+
"action": "message",
|
|
162
|
+
"text": "Your response text here"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
2. Navigate then show message (for navigation to another page):
|
|
166
|
+
{
|
|
167
|
+
"action": "nav_then_click",
|
|
168
|
+
"page": "http://site.com/page-url",
|
|
169
|
+
"on_load_text": "Message to show after navigation"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
3. Show message and click an element:
|
|
173
|
+
{
|
|
174
|
+
"action": "message_and_click",
|
|
175
|
+
"element": ".className-of-element",
|
|
176
|
+
"text": "Your message text"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
4. Create Stripe checkout:
|
|
180
|
+
{
|
|
181
|
+
"action": "checkout",
|
|
182
|
+
"text": "Your message text",
|
|
183
|
+
"items": [
|
|
184
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Guidelines:
|
|
189
|
+
- Use "message" for simple conversational responses
|
|
190
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
191
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
192
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
193
|
+
- When selecting elements, use the class names from the dom_elements in the metadata
|
|
194
|
+
- Always respond with valid JSON only, no additional text
|
|
195
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
196
|
+
- Be helpful and conversational in your messages
|
|
197
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
198
|
+
|
|
199
|
+
Example conversation flow:
|
|
200
|
+
- User: "I am looking for a black shirt in medium"
|
|
201
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
202
|
+
|
|
203
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
204
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
205
|
+
|
|
206
|
+
- User: "yes"
|
|
207
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
208
|
+
previousMessages: "{{messages}}"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/utils/stripe.ts
|
|
215
|
+
async function createCheckoutSession(options) {
|
|
216
|
+
const { secretKey, items, successUrl, cancelUrl } = options;
|
|
217
|
+
try {
|
|
218
|
+
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
219
|
+
return {
|
|
220
|
+
success: false,
|
|
221
|
+
error: "Items array is required"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
for (const item of items) {
|
|
225
|
+
if (!item.name || typeof item.price !== "number" || typeof item.quantity !== "number") {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: "Each item must have name (string), price (number in cents), and quantity (number)"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const lineItems = items.map((item) => ({
|
|
233
|
+
price_data: {
|
|
234
|
+
currency: "usd",
|
|
235
|
+
product_data: {
|
|
236
|
+
name: item.name
|
|
237
|
+
},
|
|
238
|
+
unit_amount: item.price
|
|
239
|
+
},
|
|
240
|
+
quantity: item.quantity
|
|
241
|
+
}));
|
|
242
|
+
const params = new URLSearchParams({
|
|
243
|
+
"payment_method_types[0]": "card",
|
|
244
|
+
"mode": "payment",
|
|
245
|
+
"success_url": successUrl,
|
|
246
|
+
"cancel_url": cancelUrl
|
|
247
|
+
});
|
|
248
|
+
lineItems.forEach((item, index) => {
|
|
249
|
+
params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);
|
|
250
|
+
params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);
|
|
251
|
+
params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());
|
|
252
|
+
params.append(`line_items[${index}][quantity]`, item.quantity.toString());
|
|
253
|
+
});
|
|
254
|
+
const stripeResponse = await fetch("https://api.stripe.com/v1/checkout/sessions", {
|
|
255
|
+
method: "POST",
|
|
256
|
+
headers: {
|
|
257
|
+
"Authorization": `Bearer ${secretKey}`,
|
|
258
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
259
|
+
},
|
|
260
|
+
body: params
|
|
261
|
+
});
|
|
262
|
+
if (!stripeResponse.ok) {
|
|
263
|
+
const errorData = await stripeResponse.text();
|
|
264
|
+
console.error("Stripe API error:", errorData);
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
error: "Failed to create checkout session"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const session = await stripeResponse.json();
|
|
271
|
+
return {
|
|
272
|
+
success: true,
|
|
273
|
+
checkoutUrl: session.url,
|
|
274
|
+
sessionId: session.id
|
|
275
|
+
};
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error("Stripe checkout error:", error);
|
|
278
|
+
return {
|
|
279
|
+
success: false,
|
|
280
|
+
error: error instanceof Error ? error.message : "Failed to create checkout session"
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/index.ts
|
|
30
286
|
var DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
|
|
31
287
|
var DEFAULT_PATH = "/api/chat/dispatch";
|
|
32
288
|
var DEFAULT_FLOW = {
|
|
@@ -112,7 +368,7 @@ var createChatProxyApp = (options = {}) => {
|
|
|
112
368
|
record: {
|
|
113
369
|
name: "Streaming Chat Widget",
|
|
114
370
|
type: "standalone",
|
|
115
|
-
metadata: {}
|
|
371
|
+
metadata: clientPayload.metadata || {}
|
|
116
372
|
},
|
|
117
373
|
messages: formattedMessages,
|
|
118
374
|
options: {
|
|
@@ -197,7 +453,11 @@ var createVercelHandler = (options) => (0, import_vercel.handle)(createChatProxy
|
|
|
197
453
|
var index_default = createChatProxyApp;
|
|
198
454
|
// Annotate the CommonJS export names for ESM import in node:
|
|
199
455
|
0 && (module.exports = {
|
|
456
|
+
CONVERSATIONAL_FLOW,
|
|
457
|
+
SHOPPING_ASSISTANT_FLOW,
|
|
458
|
+
SHOPPING_ASSISTANT_METADATA_FLOW,
|
|
200
459
|
createChatProxyApp,
|
|
460
|
+
createCheckoutSession,
|
|
201
461
|
createVercelHandler
|
|
202
462
|
});
|
|
203
463
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAqB;AAErB,oBAAuB;AAyBvB,IAAM,mBAAmB;AACzB,IAAM,eAAe;AAErB,IAAM,eAAkC;AAAA,EACtC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,OAAO;AAAA;AAAA,QAEP,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMf,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,WACJ,CAAC,mBACC,OAAO,GAAY,SAA8B;AA3DrD;AA4DM,QAAM,UAAS,OAAE,IAAI,OAAO,QAAQ,MAArB,YAA0B;AACzC,QAAM,UAAkC;AAAA,IACtC,+BACE,kBAAkB,eAAe,SAC7B,eAAe,SAAS,MAAM,IAC5B,SACA,eAAe,CAAC,IAClB;AAAA,IACN,iCACE,OAAE,IAAI,OAAO,gCAAgC,MAA7C,YACA;AAAA,IACF,gCAAgC;AAAA,IAChC,MAAM;AAAA,EACR;AAEA,MAAI,EAAE,IAAI,WAAW,WAAW;AAC9B,WAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,EACpD;AAEA,QAAM,KAAK;AACX,SAAO,QAAQ,OAAO,EAAE;AAAA,IAAQ,CAAC,CAAC,KAAK,KAAK,MAC1C,EAAE,OAAO,KAAK,OAAO,EAAE,QAAQ,MAAM,CAAC;AAAA,EACxC;AACF;AAEG,IAAM,qBAAqB,CAAC,UAA4B,CAAC,MAAM;AArFtE;AAsFE,QAAM,MAAM,IAAI,iBAAK;AACrB,QAAM,QAAO,aAAQ,SAAR,YAAgB;AAC7B,QAAM,YAAW,aAAQ,gBAAR,YAAuB;AAExC,MAAI,IAAI,KAAK,SAAS,QAAQ,cAAc,CAAC;AAE7C,MAAI,KAAK,MAAM,OAAO,MAAM;AA5F9B,QAAAA,KAAAC,KAAA;AA6FI,UAAM,UAASD,MAAA,QAAQ,WAAR,OAAAA,MAAkB,QAAQ,IAAI;AAC7C,QAAI,CAAC,QAAQ;AACX,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,wCAAwC;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAIJ,QAAI;AACF,sBAAgB,MAAM,EAAE,IAAI,KAAK;AAAA,IACnC,SAAS,OAAO;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,qBAAqB,SAAS,MAAM;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAGA,UAAM,YAAWC,MAAA,cAAc,aAAd,OAAAA,MAA0B,CAAC;AAE5C,UAAM,iBAAiB,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM;AAClD,YAAM,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI;AAC9D,YAAM,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI;AAC9D,aAAO,QAAQ;AAAA,IACjB,CAAC;AACD,UAAM,oBAAoB,eAAe,IAAI,CAAC,aAAa;AAAA,MACzD,MAAM,QAAQ;AAAA,MACd,SAAS,QAAQ;AAAA,IACnB,EAAE;AAGF,UAAM,UAAS,mBAAc,WAAd,YAAwB,QAAQ;AAC/C,UAAM,cAAa,aAAQ,eAAR,YAAsB;AAEzC,UAAM,iBAA0C;AAAA,MAC9C,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,MACb;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,QACP,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,WAAW,SAAS,aAAa;AAAA,QACjC,sBAAsB;AAAA,MACxB;AAAA,IACF;AAGA,QAAI,QAAQ;AACV,qBAAe,OAAO;AAAA,QACpB,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,YACX,UAAU;AAAA,cACR,QAAQ;AAAA,cACR,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOT,aAAa;AAAA,cACb,eAAe;AAAA,cACf,mBAAmB;AAAA,cACnB,mBAAmB;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,qBAAe,OAAO;AAAA,IACxB;AAGA,UAAM,gBAAgB,QAAQ,IAAI,aAAa,iBAAiB,CAAC,QAAQ,IAAI;AAE7E,QAAI,eAAe;AACjB,cAAQ,IAAI,iCAAiC;AAC7C,cAAQ,IAAI,QAAQ,QAAQ;AAC5B,cAAQ,IAAI,iBAAiB,SAAS,QAAQ,IAAI;AAClD,cAAQ,IAAI,6BAA6B,SAAS,OAAO,UAAU,GAAG,EAAE,IAAI,KAAK;AACjF,cAAQ,IAAI,oBAAoB,KAAK,UAAU,gBAAgB,MAAM,CAAC,CAAC;AAAA,IACzE;AAEA,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,cAAc;AAAA,IACrC,CAAC;AAED,QAAI,eAAe;AACjB,cAAQ,IAAI,oBAAoB,SAAS,MAAM;AAC/C,cAAQ,IAAI,yBAAyB,SAAS,UAAU;AAGxD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,iBAAiB,SAAS,MAAM;AACtC,YAAI;AACF,gBAAM,YAAY,MAAM,eAAe,KAAK;AAC5C,kBAAQ,IAAI,wBAAwB,SAAS;AAAA,QAC/C,SAAS,GAAG;AACV,kBAAQ,IAAI,uCAAuC,CAAC;AAAA,QACtD;AAAA,MACF;AACA,cAAQ,IAAI,qCAAqC;AAAA,IACnD;AAEA,WAAO,IAAI,SAAS,SAAS,MAAM;AAAA,MACjC,QAAQ,SAAS;AAAA,MACjB,SAAS;AAAA,QACP,iBACE,cAAS,QAAQ,IAAI,cAAc,MAAnC,YAAwC;AAAA,QAC1C,iBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEO,IAAM,sBAAsB,CAAC,gBAClC,sBAAO,mBAAmB,OAAO,CAAC;AAGpC,IAAO,gBAAQ;","names":["_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/flows/conversational.ts","../src/flows/shopping-assistant.ts","../src/utils/stripe.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 metadata?: Record<string, unknown>;\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: clientPayload.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// Export pre-configured flows\nexport * from \"./flows/index.js\";\n\n// Export utility functions\nexport * from \"./utils/index.js\";\n\nexport default createChatProxyApp;\n\n","import type { TravrseFlowConfig } from \"../index.js\";\n\n/**\n * Basic conversational assistant flow\n * This is the default flow for simple chat interactions\n */\nexport const CONVERSATIONAL_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 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 previous_messages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { TravrseFlowConfig } from \"../index.js\";\n\n/**\n * Shopping assistant flow configuration\n * This flow returns JSON actions for page interaction including:\n * - Simple messages\n * - Navigation with messages\n * - Element clicks with messages\n * - Stripe checkout\n */\nexport const SHOPPING_ASSISTANT_FLOW: TravrseFlowConfig = {\n name: \"Shopping Assistant Flow\",\n description: \"Returns JSON actions for page interaction\",\n steps: [\n {\n id: \"action_prompt\",\n name: \"Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"qwen/qwen3-8b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\nYou will receive information about the current page's elements (class names and text content)\nand user messages. You must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names provided in the page context\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\n/**\n * Metadata-based shopping assistant flow configuration\n * This flow uses DOM context from record metadata instead of user message.\n * The metadata should include dom_elements, dom_body, page_url, and page_title.\n */\nexport const SHOPPING_ASSISTANT_METADATA_FLOW: TravrseFlowConfig = {\n name: \"Metadata-Based Shopping Assistant\",\n description: \"Uses DOM context from record metadata for page interaction\",\n steps: [\n {\n id: \"metadata_action_prompt\",\n name: \"Metadata Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"qwen/qwen3-8b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\n\nIMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:\n- dom_elements: Array of page elements with className, innerText, and tagName\n- dom_body: Complete HTML body of the page (if provided)\n- page_url: Current page URL\n- page_title: Page title\n\nThe dom_elements array provides information about clickable elements and their text content.\nUse this metadata to understand what's available on the page and help users interact with it.\n\nYou must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names from the dom_elements in the metadata\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","/**\n * Stripe checkout helpers using the REST API\n * This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.\n */\n\nexport interface CheckoutItem {\n name: string;\n price: number; // Price in cents\n quantity: number;\n}\n\nexport interface CreateCheckoutSessionOptions {\n secretKey: string;\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n}\n\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\n}\n\n/**\n * Creates a Stripe checkout session using the REST API\n * @param options - Checkout session configuration\n * @returns Checkout session response with URL and session ID\n */\nexport async function createCheckoutSession(\n options: CreateCheckoutSessionOptions\n): Promise<CheckoutSessionResponse> {\n const { secretKey, items, successUrl, cancelUrl } = options;\n\n try {\n // Validate items\n if (!items || !Array.isArray(items) || items.length === 0) {\n return {\n success: false,\n error: \"Items array is required\"\n };\n }\n\n for (const item of items) {\n if (!item.name || typeof item.price !== \"number\" || typeof item.quantity !== \"number\") {\n return {\n success: false,\n error: \"Each item must have name (string), price (number in cents), and quantity (number)\"\n };\n }\n }\n\n // Build line items for URL encoding\n const lineItems = items.map((item) => ({\n price_data: {\n currency: \"usd\",\n product_data: {\n name: item.name,\n },\n unit_amount: item.price,\n },\n quantity: item.quantity,\n }));\n\n // Convert line items to URL-encoded format\n const params = new URLSearchParams({\n \"payment_method_types[0]\": \"card\",\n \"mode\": \"payment\",\n \"success_url\": successUrl,\n \"cancel_url\": cancelUrl,\n });\n\n // Add line items to params\n lineItems.forEach((item, index) => {\n params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);\n params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);\n params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());\n params.append(`line_items[${index}][quantity]`, item.quantity.toString());\n });\n\n // Create Stripe checkout session using REST API\n const stripeResponse = await fetch(\"https://api.stripe.com/v1/checkout/sessions\", {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: \"Failed to create checkout session\"\n };\n }\n\n const session = await stripeResponse.json() as { url: string; id: string };\n\n return {\n success: true,\n checkoutUrl: session.url,\n sessionId: session.id,\n };\n } catch (error) {\n console.error(\"Stripe checkout error:\", error);\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to create checkout session\"\n };\n }\n}\n"],"mappings":"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","names":["_a","_b"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Basic conversational assistant flow
|
|
6
|
+
* This is the default flow for simple chat interactions
|
|
7
|
+
*/
|
|
8
|
+
declare const CONVERSATIONAL_FLOW: TravrseFlowConfig;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shopping assistant flow configuration
|
|
12
|
+
* This flow returns JSON actions for page interaction including:
|
|
13
|
+
* - Simple messages
|
|
14
|
+
* - Navigation with messages
|
|
15
|
+
* - Element clicks with messages
|
|
16
|
+
* - Stripe checkout
|
|
17
|
+
*/
|
|
18
|
+
declare const SHOPPING_ASSISTANT_FLOW: TravrseFlowConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Metadata-based shopping assistant flow configuration
|
|
21
|
+
* This flow uses DOM context from record metadata instead of user message.
|
|
22
|
+
* The metadata should include dom_elements, dom_body, page_url, and page_title.
|
|
23
|
+
*/
|
|
24
|
+
declare const SHOPPING_ASSISTANT_METADATA_FLOW: TravrseFlowConfig;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Stripe checkout helpers using the REST API
|
|
28
|
+
* This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.
|
|
29
|
+
*/
|
|
30
|
+
interface CheckoutItem {
|
|
31
|
+
name: string;
|
|
32
|
+
price: number;
|
|
33
|
+
quantity: number;
|
|
34
|
+
}
|
|
35
|
+
interface CreateCheckoutSessionOptions {
|
|
36
|
+
secretKey: string;
|
|
37
|
+
items: CheckoutItem[];
|
|
38
|
+
successUrl: string;
|
|
39
|
+
cancelUrl: string;
|
|
40
|
+
}
|
|
41
|
+
interface CheckoutSessionResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
checkoutUrl?: string;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a Stripe checkout session using the REST API
|
|
49
|
+
* @param options - Checkout session configuration
|
|
50
|
+
* @returns Checkout session response with URL and session ID
|
|
51
|
+
*/
|
|
52
|
+
declare function createCheckoutSession(options: CreateCheckoutSessionOptions): Promise<CheckoutSessionResponse>;
|
|
53
|
+
|
|
4
54
|
type TravrseFlowStep = {
|
|
5
55
|
id: string;
|
|
6
56
|
name: string;
|
|
@@ -24,4 +74,4 @@ type ChatProxyOptions = {
|
|
|
24
74
|
declare const createChatProxyApp: (options?: ChatProxyOptions) => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
25
75
|
declare const createVercelHandler: (options?: ChatProxyOptions) => (req: Request) => Response | Promise<Response>;
|
|
26
76
|
|
|
27
|
-
export { type ChatProxyOptions, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createVercelHandler, createChatProxyApp as default };
|
|
77
|
+
export { CONVERSATIONAL_FLOW, type ChatProxyOptions, type CheckoutItem, type CheckoutSessionResponse, type CreateCheckoutSessionOptions, SHOPPING_ASSISTANT_FLOW, SHOPPING_ASSISTANT_METADATA_FLOW, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createCheckoutSession, createVercelHandler, createChatProxyApp as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Basic conversational assistant flow
|
|
6
|
+
* This is the default flow for simple chat interactions
|
|
7
|
+
*/
|
|
8
|
+
declare const CONVERSATIONAL_FLOW: TravrseFlowConfig;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shopping assistant flow configuration
|
|
12
|
+
* This flow returns JSON actions for page interaction including:
|
|
13
|
+
* - Simple messages
|
|
14
|
+
* - Navigation with messages
|
|
15
|
+
* - Element clicks with messages
|
|
16
|
+
* - Stripe checkout
|
|
17
|
+
*/
|
|
18
|
+
declare const SHOPPING_ASSISTANT_FLOW: TravrseFlowConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Metadata-based shopping assistant flow configuration
|
|
21
|
+
* This flow uses DOM context from record metadata instead of user message.
|
|
22
|
+
* The metadata should include dom_elements, dom_body, page_url, and page_title.
|
|
23
|
+
*/
|
|
24
|
+
declare const SHOPPING_ASSISTANT_METADATA_FLOW: TravrseFlowConfig;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Stripe checkout helpers using the REST API
|
|
28
|
+
* This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.
|
|
29
|
+
*/
|
|
30
|
+
interface CheckoutItem {
|
|
31
|
+
name: string;
|
|
32
|
+
price: number;
|
|
33
|
+
quantity: number;
|
|
34
|
+
}
|
|
35
|
+
interface CreateCheckoutSessionOptions {
|
|
36
|
+
secretKey: string;
|
|
37
|
+
items: CheckoutItem[];
|
|
38
|
+
successUrl: string;
|
|
39
|
+
cancelUrl: string;
|
|
40
|
+
}
|
|
41
|
+
interface CheckoutSessionResponse {
|
|
42
|
+
success: boolean;
|
|
43
|
+
checkoutUrl?: string;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a Stripe checkout session using the REST API
|
|
49
|
+
* @param options - Checkout session configuration
|
|
50
|
+
* @returns Checkout session response with URL and session ID
|
|
51
|
+
*/
|
|
52
|
+
declare function createCheckoutSession(options: CreateCheckoutSessionOptions): Promise<CheckoutSessionResponse>;
|
|
53
|
+
|
|
4
54
|
type TravrseFlowStep = {
|
|
5
55
|
id: string;
|
|
6
56
|
name: string;
|
|
@@ -24,4 +74,4 @@ type ChatProxyOptions = {
|
|
|
24
74
|
declare const createChatProxyApp: (options?: ChatProxyOptions) => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
25
75
|
declare const createVercelHandler: (options?: ChatProxyOptions) => (req: Request) => Response | Promise<Response>;
|
|
26
76
|
|
|
27
|
-
export { type ChatProxyOptions, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createVercelHandler, createChatProxyApp as default };
|
|
77
|
+
export { CONVERSATIONAL_FLOW, type ChatProxyOptions, type CheckoutItem, type CheckoutSessionResponse, type CreateCheckoutSessionOptions, SHOPPING_ASSISTANT_FLOW, SHOPPING_ASSISTANT_METADATA_FLOW, type TravrseFlowConfig, type TravrseFlowStep, createChatProxyApp, createCheckoutSession, createVercelHandler, createChatProxyApp as default };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,258 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { handle } from "hono/vercel";
|
|
4
|
+
|
|
5
|
+
// src/flows/conversational.ts
|
|
6
|
+
var CONVERSATIONAL_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
|
+
response_format: "markdown",
|
|
18
|
+
output_variable: "prompt_result",
|
|
19
|
+
user_prompt: "{{user_message}}",
|
|
20
|
+
system_prompt: "you are a helpful assistant, chatting with a user",
|
|
21
|
+
previous_messages: "{{messages}}"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/flows/shopping-assistant.ts
|
|
28
|
+
var SHOPPING_ASSISTANT_FLOW = {
|
|
29
|
+
name: "Shopping Assistant Flow",
|
|
30
|
+
description: "Returns JSON actions for page interaction",
|
|
31
|
+
steps: [
|
|
32
|
+
{
|
|
33
|
+
id: "action_prompt",
|
|
34
|
+
name: "Action Prompt",
|
|
35
|
+
type: "prompt",
|
|
36
|
+
enabled: true,
|
|
37
|
+
config: {
|
|
38
|
+
model: "qwen/qwen3-8b",
|
|
39
|
+
reasoning: false,
|
|
40
|
+
responseFormat: "JSON",
|
|
41
|
+
outputVariable: "prompt_result",
|
|
42
|
+
userPrompt: "{{user_message}}",
|
|
43
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
44
|
+
You will receive information about the current page's elements (class names and text content)
|
|
45
|
+
and user messages. You must respond with JSON in one of these formats:
|
|
46
|
+
|
|
47
|
+
1. Simple message:
|
|
48
|
+
{
|
|
49
|
+
"action": "message",
|
|
50
|
+
"text": "Your response text here"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
2. Navigate then show message (for navigation to another page):
|
|
54
|
+
{
|
|
55
|
+
"action": "nav_then_click",
|
|
56
|
+
"page": "http://site.com/page-url",
|
|
57
|
+
"on_load_text": "Message to show after navigation"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
3. Show message and click an element:
|
|
61
|
+
{
|
|
62
|
+
"action": "message_and_click",
|
|
63
|
+
"element": ".className-of-element",
|
|
64
|
+
"text": "Your message text"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
4. Create Stripe checkout:
|
|
68
|
+
{
|
|
69
|
+
"action": "checkout",
|
|
70
|
+
"text": "Your message text",
|
|
71
|
+
"items": [
|
|
72
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Guidelines:
|
|
77
|
+
- Use "message" for simple conversational responses
|
|
78
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
79
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
80
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
81
|
+
- When selecting elements, use the class names provided in the page context
|
|
82
|
+
- Always respond with valid JSON only, no additional text
|
|
83
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
84
|
+
- Be helpful and conversational in your messages
|
|
85
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
86
|
+
|
|
87
|
+
Example conversation flow:
|
|
88
|
+
- User: "I am looking for a black shirt in medium"
|
|
89
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
90
|
+
|
|
91
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
92
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
93
|
+
|
|
94
|
+
- User: "yes"
|
|
95
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
96
|
+
previousMessages: "{{messages}}"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
};
|
|
101
|
+
var SHOPPING_ASSISTANT_METADATA_FLOW = {
|
|
102
|
+
name: "Metadata-Based Shopping Assistant",
|
|
103
|
+
description: "Uses DOM context from record metadata for page interaction",
|
|
104
|
+
steps: [
|
|
105
|
+
{
|
|
106
|
+
id: "metadata_action_prompt",
|
|
107
|
+
name: "Metadata Action Prompt",
|
|
108
|
+
type: "prompt",
|
|
109
|
+
enabled: true,
|
|
110
|
+
config: {
|
|
111
|
+
model: "qwen/qwen3-8b",
|
|
112
|
+
reasoning: false,
|
|
113
|
+
responseFormat: "JSON",
|
|
114
|
+
outputVariable: "prompt_result",
|
|
115
|
+
userPrompt: "{{user_message}}",
|
|
116
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
117
|
+
|
|
118
|
+
IMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:
|
|
119
|
+
- dom_elements: Array of page elements with className, innerText, and tagName
|
|
120
|
+
- dom_body: Complete HTML body of the page (if provided)
|
|
121
|
+
- page_url: Current page URL
|
|
122
|
+
- page_title: Page title
|
|
123
|
+
|
|
124
|
+
The dom_elements array provides information about clickable elements and their text content.
|
|
125
|
+
Use this metadata to understand what's available on the page and help users interact with it.
|
|
126
|
+
|
|
127
|
+
You must respond with JSON in one of these formats:
|
|
128
|
+
|
|
129
|
+
1. Simple message:
|
|
130
|
+
{
|
|
131
|
+
"action": "message",
|
|
132
|
+
"text": "Your response text here"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
2. Navigate then show message (for navigation to another page):
|
|
136
|
+
{
|
|
137
|
+
"action": "nav_then_click",
|
|
138
|
+
"page": "http://site.com/page-url",
|
|
139
|
+
"on_load_text": "Message to show after navigation"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
3. Show message and click an element:
|
|
143
|
+
{
|
|
144
|
+
"action": "message_and_click",
|
|
145
|
+
"element": ".className-of-element",
|
|
146
|
+
"text": "Your message text"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
4. Create Stripe checkout:
|
|
150
|
+
{
|
|
151
|
+
"action": "checkout",
|
|
152
|
+
"text": "Your message text",
|
|
153
|
+
"items": [
|
|
154
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
Guidelines:
|
|
159
|
+
- Use "message" for simple conversational responses
|
|
160
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
161
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
162
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
163
|
+
- When selecting elements, use the class names from the dom_elements in the metadata
|
|
164
|
+
- Always respond with valid JSON only, no additional text
|
|
165
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
166
|
+
- Be helpful and conversational in your messages
|
|
167
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
168
|
+
|
|
169
|
+
Example conversation flow:
|
|
170
|
+
- User: "I am looking for a black shirt in medium"
|
|
171
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
172
|
+
|
|
173
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
174
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
175
|
+
|
|
176
|
+
- User: "yes"
|
|
177
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
178
|
+
previousMessages: "{{messages}}"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/utils/stripe.ts
|
|
185
|
+
async function createCheckoutSession(options) {
|
|
186
|
+
const { secretKey, items, successUrl, cancelUrl } = options;
|
|
187
|
+
try {
|
|
188
|
+
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
error: "Items array is required"
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
for (const item of items) {
|
|
195
|
+
if (!item.name || typeof item.price !== "number" || typeof item.quantity !== "number") {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
error: "Each item must have name (string), price (number in cents), and quantity (number)"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const lineItems = items.map((item) => ({
|
|
203
|
+
price_data: {
|
|
204
|
+
currency: "usd",
|
|
205
|
+
product_data: {
|
|
206
|
+
name: item.name
|
|
207
|
+
},
|
|
208
|
+
unit_amount: item.price
|
|
209
|
+
},
|
|
210
|
+
quantity: item.quantity
|
|
211
|
+
}));
|
|
212
|
+
const params = new URLSearchParams({
|
|
213
|
+
"payment_method_types[0]": "card",
|
|
214
|
+
"mode": "payment",
|
|
215
|
+
"success_url": successUrl,
|
|
216
|
+
"cancel_url": cancelUrl
|
|
217
|
+
});
|
|
218
|
+
lineItems.forEach((item, index) => {
|
|
219
|
+
params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);
|
|
220
|
+
params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);
|
|
221
|
+
params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());
|
|
222
|
+
params.append(`line_items[${index}][quantity]`, item.quantity.toString());
|
|
223
|
+
});
|
|
224
|
+
const stripeResponse = await fetch("https://api.stripe.com/v1/checkout/sessions", {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: {
|
|
227
|
+
"Authorization": `Bearer ${secretKey}`,
|
|
228
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
229
|
+
},
|
|
230
|
+
body: params
|
|
231
|
+
});
|
|
232
|
+
if (!stripeResponse.ok) {
|
|
233
|
+
const errorData = await stripeResponse.text();
|
|
234
|
+
console.error("Stripe API error:", errorData);
|
|
235
|
+
return {
|
|
236
|
+
success: false,
|
|
237
|
+
error: "Failed to create checkout session"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const session = await stripeResponse.json();
|
|
241
|
+
return {
|
|
242
|
+
success: true,
|
|
243
|
+
checkoutUrl: session.url,
|
|
244
|
+
sessionId: session.id
|
|
245
|
+
};
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error("Stripe checkout error:", error);
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
error: error instanceof Error ? error.message : "Failed to create checkout session"
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/index.ts
|
|
4
256
|
var DEFAULT_ENDPOINT = "https://api.travrse.ai/v1/dispatch";
|
|
5
257
|
var DEFAULT_PATH = "/api/chat/dispatch";
|
|
6
258
|
var DEFAULT_FLOW = {
|
|
@@ -86,7 +338,7 @@ var createChatProxyApp = (options = {}) => {
|
|
|
86
338
|
record: {
|
|
87
339
|
name: "Streaming Chat Widget",
|
|
88
340
|
type: "standalone",
|
|
89
|
-
metadata: {}
|
|
341
|
+
metadata: clientPayload.metadata || {}
|
|
90
342
|
},
|
|
91
343
|
messages: formattedMessages,
|
|
92
344
|
options: {
|
|
@@ -170,7 +422,11 @@ var createChatProxyApp = (options = {}) => {
|
|
|
170
422
|
var createVercelHandler = (options) => handle(createChatProxyApp(options));
|
|
171
423
|
var index_default = createChatProxyApp;
|
|
172
424
|
export {
|
|
425
|
+
CONVERSATIONAL_FLOW,
|
|
426
|
+
SHOPPING_ASSISTANT_FLOW,
|
|
427
|
+
SHOPPING_ASSISTANT_METADATA_FLOW,
|
|
173
428
|
createChatProxyApp,
|
|
429
|
+
createCheckoutSession,
|
|
174
430
|
createVercelHandler,
|
|
175
431
|
index_default as default
|
|
176
432
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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":";AAAA,SAAS,YAAY;AAErB,SAAS,cAAc;AAyBvB,IAAM,mBAAmB;AACzB,IAAM,eAAe;AAErB,IAAM,eAAkC;AAAA,EACtC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,OAAO;AAAA,IACL;AAAA,MACE,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,OAAO;AAAA;AAAA,QAEP,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMf,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,WACJ,CAAC,mBACC,OAAO,GAAY,SAA8B;AA3DrD;AA4DM,QAAM,UAAS,OAAE,IAAI,OAAO,QAAQ,MAArB,YAA0B;AACzC,QAAM,UAAkC;AAAA,IACtC,+BACE,kBAAkB,eAAe,SAC7B,eAAe,SAAS,MAAM,IAC5B,SACA,eAAe,CAAC,IAClB;AAAA,IACN,iCACE,OAAE,IAAI,OAAO,gCAAgC,MAA7C,YACA;AAAA,IACF,gCAAgC;AAAA,IAChC,MAAM;AAAA,EACR;AAEA,MAAI,EAAE,IAAI,WAAW,WAAW;AAC9B,WAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,EACpD;AAEA,QAAM,KAAK;AACX,SAAO,QAAQ,OAAO,EAAE;AAAA,IAAQ,CAAC,CAAC,KAAK,KAAK,MAC1C,EAAE,OAAO,KAAK,OAAO,EAAE,QAAQ,MAAM,CAAC;AAAA,EACxC;AACF;AAEG,IAAM,qBAAqB,CAAC,UAA4B,CAAC,MAAM;AArFtE;AAsFE,QAAM,MAAM,IAAI,KAAK;AACrB,QAAM,QAAO,aAAQ,SAAR,YAAgB;AAC7B,QAAM,YAAW,aAAQ,gBAAR,YAAuB;AAExC,MAAI,IAAI,KAAK,SAAS,QAAQ,cAAc,CAAC;AAE7C,MAAI,KAAK,MAAM,OAAO,MAAM;AA5F9B,QAAAA,KAAAC,KAAA;AA6FI,UAAM,UAASD,MAAA,QAAQ,WAAR,OAAAA,MAAkB,QAAQ,IAAI;AAC7C,QAAI,CAAC,QAAQ;AACX,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,wCAAwC;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AAIJ,QAAI;AACF,sBAAgB,MAAM,EAAE,IAAI,KAAK;AAAA,IACnC,SAAS,OAAO;AACd,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,qBAAqB,SAAS,MAAM;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAGA,UAAM,YAAWC,MAAA,cAAc,aAAd,OAAAA,MAA0B,CAAC;AAE5C,UAAM,iBAAiB,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM;AAClD,YAAM,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI;AAC9D,YAAM,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI;AAC9D,aAAO,QAAQ;AAAA,IACjB,CAAC;AACD,UAAM,oBAAoB,eAAe,IAAI,CAAC,aAAa;AAAA,MACzD,MAAM,QAAQ;AAAA,MACd,SAAS,QAAQ;AAAA,IACnB,EAAE;AAGF,UAAM,UAAS,mBAAc,WAAd,YAAwB,QAAQ;AAC/C,UAAM,cAAa,aAAQ,eAAR,YAAsB;AAEzC,UAAM,iBAA0C;AAAA,MAC9C,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,MACb;AAAA,MACA,UAAU;AAAA,MACV,SAAS;AAAA,QACP,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,WAAW,SAAS,aAAa;AAAA,QACjC,sBAAsB;AAAA,MACxB;AAAA,IACF;AAGA,QAAI,QAAQ;AACV,qBAAe,OAAO;AAAA,QACpB,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,SAAS;AAAA,YACT,WAAW;AAAA,YACX,UAAU;AAAA,cACR,QAAQ;AAAA,cACR,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOT,aAAa;AAAA,cACb,eAAe;AAAA,cACf,mBAAmB;AAAA,cACnB,mBAAmB;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,qBAAe,OAAO;AAAA,IACxB;AAGA,UAAM,gBAAgB,QAAQ,IAAI,aAAa,iBAAiB,CAAC,QAAQ,IAAI;AAE7E,QAAI,eAAe;AACjB,cAAQ,IAAI,iCAAiC;AAC7C,cAAQ,IAAI,QAAQ,QAAQ;AAC5B,cAAQ,IAAI,iBAAiB,SAAS,QAAQ,IAAI;AAClD,cAAQ,IAAI,6BAA6B,SAAS,OAAO,UAAU,GAAG,EAAE,IAAI,KAAK;AACjF,cAAQ,IAAI,oBAAoB,KAAK,UAAU,gBAAgB,MAAM,CAAC,CAAC;AAAA,IACzE;AAEA,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,cAAc;AAAA,IACrC,CAAC;AAED,QAAI,eAAe;AACjB,cAAQ,IAAI,oBAAoB,SAAS,MAAM;AAC/C,cAAQ,IAAI,yBAAyB,SAAS,UAAU;AAGxD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,iBAAiB,SAAS,MAAM;AACtC,YAAI;AACF,gBAAM,YAAY,MAAM,eAAe,KAAK;AAC5C,kBAAQ,IAAI,wBAAwB,SAAS;AAAA,QAC/C,SAAS,GAAG;AACV,kBAAQ,IAAI,uCAAuC,CAAC;AAAA,QACtD;AAAA,MACF;AACA,cAAQ,IAAI,qCAAqC;AAAA,IACnD;AAEA,WAAO,IAAI,SAAS,SAAS,MAAM;AAAA,MACjC,QAAQ,SAAS;AAAA,MACjB,SAAS;AAAA,QACP,iBACE,cAAS,QAAQ,IAAI,cAAc,MAAnC,YAAwC;AAAA,QAC1C,iBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEO,IAAM,sBAAsB,CAAC,YAClC,OAAO,mBAAmB,OAAO,CAAC;AAGpC,IAAO,gBAAQ;","names":["_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/flows/conversational.ts","../src/flows/shopping-assistant.ts","../src/utils/stripe.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 metadata?: Record<string, unknown>;\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: clientPayload.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// Export pre-configured flows\nexport * from \"./flows/index.js\";\n\n// Export utility functions\nexport * from \"./utils/index.js\";\n\nexport default createChatProxyApp;\n\n","import type { TravrseFlowConfig } from \"../index.js\";\n\n/**\n * Basic conversational assistant flow\n * This is the default flow for simple chat interactions\n */\nexport const CONVERSATIONAL_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 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 previous_messages: \"{{messages}}\"\n }\n }\n ]\n};\n","import type { TravrseFlowConfig } from \"../index.js\";\n\n/**\n * Shopping assistant flow configuration\n * This flow returns JSON actions for page interaction including:\n * - Simple messages\n * - Navigation with messages\n * - Element clicks with messages\n * - Stripe checkout\n */\nexport const SHOPPING_ASSISTANT_FLOW: TravrseFlowConfig = {\n name: \"Shopping Assistant Flow\",\n description: \"Returns JSON actions for page interaction\",\n steps: [\n {\n id: \"action_prompt\",\n name: \"Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"qwen/qwen3-8b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\nYou will receive information about the current page's elements (class names and text content)\nand user messages. You must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names provided in the page context\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n\n/**\n * Metadata-based shopping assistant flow configuration\n * This flow uses DOM context from record metadata instead of user message.\n * The metadata should include dom_elements, dom_body, page_url, and page_title.\n */\nexport const SHOPPING_ASSISTANT_METADATA_FLOW: TravrseFlowConfig = {\n name: \"Metadata-Based Shopping Assistant\",\n description: \"Uses DOM context from record metadata for page interaction\",\n steps: [\n {\n id: \"metadata_action_prompt\",\n name: \"Metadata Action Prompt\",\n type: \"prompt\",\n enabled: true,\n config: {\n model: \"qwen/qwen3-8b\",\n reasoning: false,\n responseFormat: \"JSON\",\n outputVariable: \"prompt_result\",\n userPrompt: \"{{user_message}}\",\n systemPrompt: `You are a helpful shopping assistant that can interact with web pages.\n\nIMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:\n- dom_elements: Array of page elements with className, innerText, and tagName\n- dom_body: Complete HTML body of the page (if provided)\n- page_url: Current page URL\n- page_title: Page title\n\nThe dom_elements array provides information about clickable elements and their text content.\nUse this metadata to understand what's available on the page and help users interact with it.\n\nYou must respond with JSON in one of these formats:\n\n1. Simple message:\n{\n \"action\": \"message\",\n \"text\": \"Your response text here\"\n}\n\n2. Navigate then show message (for navigation to another page):\n{\n \"action\": \"nav_then_click\",\n \"page\": \"http://site.com/page-url\",\n \"on_load_text\": \"Message to show after navigation\"\n}\n\n3. Show message and click an element:\n{\n \"action\": \"message_and_click\",\n \"element\": \".className-of-element\",\n \"text\": \"Your message text\"\n}\n\n4. Create Stripe checkout:\n{\n \"action\": \"checkout\",\n \"text\": \"Your message text\",\n \"items\": [\n {\"name\": \"Product Name\", \"price\": 2999, \"quantity\": 1}\n ]\n}\n\nGuidelines:\n- Use \"message\" for simple conversational responses\n- Use \"nav_then_click\" when you need to navigate to a different page (like a product detail page)\n- Use \"message_and_click\" when you want to click a button or element on the current page\n- Use \"checkout\" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)\n- When selecting elements, use the class names from the dom_elements in the metadata\n- Always respond with valid JSON only, no additional text\n- For product searches, format results as markdown links: [Product Name](url)\n- Be helpful and conversational in your messages\n- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)\n\nExample conversation flow:\n- User: \"I am looking for a black shirt in medium\"\n- You: {\"action\": \"message\", \"text\": \"Here are the products I found:\\\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\\\n\\\\nWould you like me to navigate to the first result and add it to your cart?\"}\n\n- User: \"No, I would like to add another shirt to the cart\"\n- You: {\"action\": \"message_and_click\", \"element\": \".AddToCartButton-blue-shirt-large\", \"text\": \"I've added the Blue Shirt - Large to your cart. Ready to checkout?\"}\n\n- User: \"yes\"\n- You: {\"action\": \"checkout\", \"text\": \"Perfect! I'll set up the checkout for you.\", \"items\": [{\"name\": \"Black Shirt - Medium\", \"price\": 2999, \"quantity\": 1}]}`,\n previousMessages: \"{{messages}}\"\n }\n }\n ]\n};\n","/**\n * Stripe checkout helpers using the REST API\n * This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.\n */\n\nexport interface CheckoutItem {\n name: string;\n price: number; // Price in cents\n quantity: number;\n}\n\nexport interface CreateCheckoutSessionOptions {\n secretKey: string;\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n}\n\nexport interface CheckoutSessionResponse {\n success: boolean;\n checkoutUrl?: string;\n sessionId?: string;\n error?: string;\n}\n\n/**\n * Creates a Stripe checkout session using the REST API\n * @param options - Checkout session configuration\n * @returns Checkout session response with URL and session ID\n */\nexport async function createCheckoutSession(\n options: CreateCheckoutSessionOptions\n): Promise<CheckoutSessionResponse> {\n const { secretKey, items, successUrl, cancelUrl } = options;\n\n try {\n // Validate items\n if (!items || !Array.isArray(items) || items.length === 0) {\n return {\n success: false,\n error: \"Items array is required\"\n };\n }\n\n for (const item of items) {\n if (!item.name || typeof item.price !== \"number\" || typeof item.quantity !== \"number\") {\n return {\n success: false,\n error: \"Each item must have name (string), price (number in cents), and quantity (number)\"\n };\n }\n }\n\n // Build line items for URL encoding\n const lineItems = items.map((item) => ({\n price_data: {\n currency: \"usd\",\n product_data: {\n name: item.name,\n },\n unit_amount: item.price,\n },\n quantity: item.quantity,\n }));\n\n // Convert line items to URL-encoded format\n const params = new URLSearchParams({\n \"payment_method_types[0]\": \"card\",\n \"mode\": \"payment\",\n \"success_url\": successUrl,\n \"cancel_url\": cancelUrl,\n });\n\n // Add line items to params\n lineItems.forEach((item, index) => {\n params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);\n params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);\n params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());\n params.append(`line_items[${index}][quantity]`, item.quantity.toString());\n });\n\n // Create Stripe checkout session using REST API\n const stripeResponse = await fetch(\"https://api.stripe.com/v1/checkout/sessions\", {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${secretKey}`,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: params,\n });\n\n if (!stripeResponse.ok) {\n const errorData = await stripeResponse.text();\n console.error(\"Stripe API error:\", errorData);\n return {\n success: false,\n error: \"Failed to create checkout session\"\n };\n }\n\n const session = await stripeResponse.json() as { url: string; id: string };\n\n return {\n success: true,\n checkoutUrl: session.url,\n sessionId: session.id,\n };\n } catch (error) {\n console.error(\"Stripe checkout error:\", error);\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Failed to create checkout session\"\n };\n }\n}\n"],"mappings":"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","names":["_a","_b"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanilla-agent-proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Proxy server for vanilla-agent widget - handles flow configuration and forwards requests to Travrse or other AI backends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TravrseFlowConfig } from "../index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Basic conversational assistant flow
|
|
5
|
+
* This is the default flow for simple chat interactions
|
|
6
|
+
*/
|
|
7
|
+
export const CONVERSATIONAL_FLOW: TravrseFlowConfig = {
|
|
8
|
+
name: "Streaming Prompt Flow",
|
|
9
|
+
description: "Streaming chat generated by the widget",
|
|
10
|
+
steps: [
|
|
11
|
+
{
|
|
12
|
+
id: "widget_prompt",
|
|
13
|
+
name: "Prompt",
|
|
14
|
+
type: "prompt",
|
|
15
|
+
enabled: true,
|
|
16
|
+
config: {
|
|
17
|
+
model: "meta/llama3.1-8b-instruct-free",
|
|
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
|
+
previous_messages: "{{messages}}"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { TravrseFlowConfig } from "../index.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shopping assistant flow configuration
|
|
5
|
+
* This flow returns JSON actions for page interaction including:
|
|
6
|
+
* - Simple messages
|
|
7
|
+
* - Navigation with messages
|
|
8
|
+
* - Element clicks with messages
|
|
9
|
+
* - Stripe checkout
|
|
10
|
+
*/
|
|
11
|
+
export const SHOPPING_ASSISTANT_FLOW: TravrseFlowConfig = {
|
|
12
|
+
name: "Shopping Assistant Flow",
|
|
13
|
+
description: "Returns JSON actions for page interaction",
|
|
14
|
+
steps: [
|
|
15
|
+
{
|
|
16
|
+
id: "action_prompt",
|
|
17
|
+
name: "Action Prompt",
|
|
18
|
+
type: "prompt",
|
|
19
|
+
enabled: true,
|
|
20
|
+
config: {
|
|
21
|
+
model: "qwen/qwen3-8b",
|
|
22
|
+
reasoning: false,
|
|
23
|
+
responseFormat: "JSON",
|
|
24
|
+
outputVariable: "prompt_result",
|
|
25
|
+
userPrompt: "{{user_message}}",
|
|
26
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
27
|
+
You will receive information about the current page's elements (class names and text content)
|
|
28
|
+
and user messages. You must respond with JSON in one of these formats:
|
|
29
|
+
|
|
30
|
+
1. Simple message:
|
|
31
|
+
{
|
|
32
|
+
"action": "message",
|
|
33
|
+
"text": "Your response text here"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
2. Navigate then show message (for navigation to another page):
|
|
37
|
+
{
|
|
38
|
+
"action": "nav_then_click",
|
|
39
|
+
"page": "http://site.com/page-url",
|
|
40
|
+
"on_load_text": "Message to show after navigation"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
3. Show message and click an element:
|
|
44
|
+
{
|
|
45
|
+
"action": "message_and_click",
|
|
46
|
+
"element": ".className-of-element",
|
|
47
|
+
"text": "Your message text"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
4. Create Stripe checkout:
|
|
51
|
+
{
|
|
52
|
+
"action": "checkout",
|
|
53
|
+
"text": "Your message text",
|
|
54
|
+
"items": [
|
|
55
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Guidelines:
|
|
60
|
+
- Use "message" for simple conversational responses
|
|
61
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
62
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
63
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
64
|
+
- When selecting elements, use the class names provided in the page context
|
|
65
|
+
- Always respond with valid JSON only, no additional text
|
|
66
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
67
|
+
- Be helpful and conversational in your messages
|
|
68
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
69
|
+
|
|
70
|
+
Example conversation flow:
|
|
71
|
+
- User: "I am looking for a black shirt in medium"
|
|
72
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
73
|
+
|
|
74
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
75
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
76
|
+
|
|
77
|
+
- User: "yes"
|
|
78
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
79
|
+
previousMessages: "{{messages}}"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Metadata-based shopping assistant flow configuration
|
|
87
|
+
* This flow uses DOM context from record metadata instead of user message.
|
|
88
|
+
* The metadata should include dom_elements, dom_body, page_url, and page_title.
|
|
89
|
+
*/
|
|
90
|
+
export const SHOPPING_ASSISTANT_METADATA_FLOW: TravrseFlowConfig = {
|
|
91
|
+
name: "Metadata-Based Shopping Assistant",
|
|
92
|
+
description: "Uses DOM context from record metadata for page interaction",
|
|
93
|
+
steps: [
|
|
94
|
+
{
|
|
95
|
+
id: "metadata_action_prompt",
|
|
96
|
+
name: "Metadata Action Prompt",
|
|
97
|
+
type: "prompt",
|
|
98
|
+
enabled: true,
|
|
99
|
+
config: {
|
|
100
|
+
model: "qwen/qwen3-8b",
|
|
101
|
+
reasoning: false,
|
|
102
|
+
responseFormat: "JSON",
|
|
103
|
+
outputVariable: "prompt_result",
|
|
104
|
+
userPrompt: "{{user_message}}",
|
|
105
|
+
systemPrompt: `You are a helpful shopping assistant that can interact with web pages.
|
|
106
|
+
|
|
107
|
+
IMPORTANT: You have access to the current page's DOM elements through the record metadata, which includes:
|
|
108
|
+
- dom_elements: Array of page elements with className, innerText, and tagName
|
|
109
|
+
- dom_body: Complete HTML body of the page (if provided)
|
|
110
|
+
- page_url: Current page URL
|
|
111
|
+
- page_title: Page title
|
|
112
|
+
|
|
113
|
+
The dom_elements array provides information about clickable elements and their text content.
|
|
114
|
+
Use this metadata to understand what's available on the page and help users interact with it.
|
|
115
|
+
|
|
116
|
+
You must respond with JSON in one of these formats:
|
|
117
|
+
|
|
118
|
+
1. Simple message:
|
|
119
|
+
{
|
|
120
|
+
"action": "message",
|
|
121
|
+
"text": "Your response text here"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
2. Navigate then show message (for navigation to another page):
|
|
125
|
+
{
|
|
126
|
+
"action": "nav_then_click",
|
|
127
|
+
"page": "http://site.com/page-url",
|
|
128
|
+
"on_load_text": "Message to show after navigation"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
3. Show message and click an element:
|
|
132
|
+
{
|
|
133
|
+
"action": "message_and_click",
|
|
134
|
+
"element": ".className-of-element",
|
|
135
|
+
"text": "Your message text"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
4. Create Stripe checkout:
|
|
139
|
+
{
|
|
140
|
+
"action": "checkout",
|
|
141
|
+
"text": "Your message text",
|
|
142
|
+
"items": [
|
|
143
|
+
{"name": "Product Name", "price": 2999, "quantity": 1}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Guidelines:
|
|
148
|
+
- Use "message" for simple conversational responses
|
|
149
|
+
- Use "nav_then_click" when you need to navigate to a different page (like a product detail page)
|
|
150
|
+
- Use "message_and_click" when you want to click a button or element on the current page
|
|
151
|
+
- Use "checkout" when the user wants to proceed to checkout/payment. Include items array with name (string), price (number in cents), and quantity (number)
|
|
152
|
+
- When selecting elements, use the class names from the dom_elements in the metadata
|
|
153
|
+
- Always respond with valid JSON only, no additional text
|
|
154
|
+
- For product searches, format results as markdown links: [Product Name](url)
|
|
155
|
+
- Be helpful and conversational in your messages
|
|
156
|
+
- Product prices: Black Shirt - Medium: $29.99 (2999 cents), Blue Shirt - Large: $34.99 (3499 cents), Red T-Shirt - Small: $19.99 (1999 cents), Jeans - Medium: $49.99 (4999 cents)
|
|
157
|
+
|
|
158
|
+
Example conversation flow:
|
|
159
|
+
- User: "I am looking for a black shirt in medium"
|
|
160
|
+
- You: {"action": "message", "text": "Here are the products I found:\\n1. [Black Shirt - Medium](/products.html?product=black-shirt-medium) - $29.99\\n2. [Blue Shirt - Large](/products.html?product=blue-shirt-large) - $34.99\\n3. [Red T-Shirt - Small](/products.html?product=red-tshirt-small) - $19.99\\n4. [Jeans - Medium](/products.html?product=jeans-medium) - $49.99\\n\\nWould you like me to navigate to the first result and add it to your cart?"}
|
|
161
|
+
|
|
162
|
+
- User: "No, I would like to add another shirt to the cart"
|
|
163
|
+
- You: {"action": "message_and_click", "element": ".AddToCartButton-blue-shirt-large", "text": "I've added the Blue Shirt - Large to your cart. Ready to checkout?"}
|
|
164
|
+
|
|
165
|
+
- User: "yes"
|
|
166
|
+
- You: {"action": "checkout", "text": "Perfect! I'll set up the checkout for you.", "items": [{"name": "Black Shirt - Medium", "price": 2999, "quantity": 1}]}`,
|
|
167
|
+
previousMessages: "{{messages}}"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -102,6 +102,7 @@ export const createChatProxyApp = (options: ChatProxyOptions = {}) => {
|
|
|
102
102
|
let clientPayload: {
|
|
103
103
|
messages?: Array<{ role: string; content: string; createdAt?: string }>;
|
|
104
104
|
flowId?: string;
|
|
105
|
+
metadata?: Record<string, unknown>;
|
|
105
106
|
};
|
|
106
107
|
try {
|
|
107
108
|
clientPayload = await c.req.json();
|
|
@@ -133,7 +134,7 @@ export const createChatProxyApp = (options: ChatProxyOptions = {}) => {
|
|
|
133
134
|
record: {
|
|
134
135
|
name: "Streaming Chat Widget",
|
|
135
136
|
type: "standalone",
|
|
136
|
-
metadata: {}
|
|
137
|
+
metadata: clientPayload.metadata || {}
|
|
137
138
|
},
|
|
138
139
|
messages: formattedMessages,
|
|
139
140
|
options: {
|
|
@@ -230,6 +231,11 @@ export const createChatProxyApp = (options: ChatProxyOptions = {}) => {
|
|
|
230
231
|
export const createVercelHandler = (options?: ChatProxyOptions) =>
|
|
231
232
|
handle(createChatProxyApp(options));
|
|
232
233
|
|
|
234
|
+
// Export pre-configured flows
|
|
235
|
+
export * from "./flows/index.js";
|
|
236
|
+
|
|
237
|
+
// Export utility functions
|
|
238
|
+
export * from "./utils/index.js";
|
|
233
239
|
|
|
234
240
|
export default createChatProxyApp;
|
|
235
241
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe checkout helpers using the REST API
|
|
3
|
+
* This approach works on all platforms including Cloudflare Workers, Vercel Edge, etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface CheckoutItem {
|
|
7
|
+
name: string;
|
|
8
|
+
price: number; // Price in cents
|
|
9
|
+
quantity: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CreateCheckoutSessionOptions {
|
|
13
|
+
secretKey: string;
|
|
14
|
+
items: CheckoutItem[];
|
|
15
|
+
successUrl: string;
|
|
16
|
+
cancelUrl: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CheckoutSessionResponse {
|
|
20
|
+
success: boolean;
|
|
21
|
+
checkoutUrl?: string;
|
|
22
|
+
sessionId?: string;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a Stripe checkout session using the REST API
|
|
28
|
+
* @param options - Checkout session configuration
|
|
29
|
+
* @returns Checkout session response with URL and session ID
|
|
30
|
+
*/
|
|
31
|
+
export async function createCheckoutSession(
|
|
32
|
+
options: CreateCheckoutSessionOptions
|
|
33
|
+
): Promise<CheckoutSessionResponse> {
|
|
34
|
+
const { secretKey, items, successUrl, cancelUrl } = options;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Validate items
|
|
38
|
+
if (!items || !Array.isArray(items) || items.length === 0) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: "Items array is required"
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const item of items) {
|
|
46
|
+
if (!item.name || typeof item.price !== "number" || typeof item.quantity !== "number") {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: "Each item must have name (string), price (number in cents), and quantity (number)"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Build line items for URL encoding
|
|
55
|
+
const lineItems = items.map((item) => ({
|
|
56
|
+
price_data: {
|
|
57
|
+
currency: "usd",
|
|
58
|
+
product_data: {
|
|
59
|
+
name: item.name,
|
|
60
|
+
},
|
|
61
|
+
unit_amount: item.price,
|
|
62
|
+
},
|
|
63
|
+
quantity: item.quantity,
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// Convert line items to URL-encoded format
|
|
67
|
+
const params = new URLSearchParams({
|
|
68
|
+
"payment_method_types[0]": "card",
|
|
69
|
+
"mode": "payment",
|
|
70
|
+
"success_url": successUrl,
|
|
71
|
+
"cancel_url": cancelUrl,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Add line items to params
|
|
75
|
+
lineItems.forEach((item, index) => {
|
|
76
|
+
params.append(`line_items[${index}][price_data][currency]`, item.price_data.currency);
|
|
77
|
+
params.append(`line_items[${index}][price_data][product_data][name]`, item.price_data.product_data.name);
|
|
78
|
+
params.append(`line_items[${index}][price_data][unit_amount]`, item.price_data.unit_amount.toString());
|
|
79
|
+
params.append(`line_items[${index}][quantity]`, item.quantity.toString());
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Create Stripe checkout session using REST API
|
|
83
|
+
const stripeResponse = await fetch("https://api.stripe.com/v1/checkout/sessions", {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: {
|
|
86
|
+
"Authorization": `Bearer ${secretKey}`,
|
|
87
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
88
|
+
},
|
|
89
|
+
body: params,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!stripeResponse.ok) {
|
|
93
|
+
const errorData = await stripeResponse.text();
|
|
94
|
+
console.error("Stripe API error:", errorData);
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
error: "Failed to create checkout session"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const session = await stripeResponse.json() as { url: string; id: string };
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
checkoutUrl: session.url,
|
|
106
|
+
sessionId: session.id,
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("Stripe checkout error:", error);
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
error: error instanceof Error ? error.message : "Failed to create checkout session"
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|