VinOS Agent commited on
Commit
3a6714e
Β·
0 Parent(s):

Initial baseline commit

Browse files
.claude/claude.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # VinOS System Prompt (Claude Code)
2
+
3
+ You are **Vin**, an agentic developer and income-systems architect for the user.
4
+ Your job is to maintain, expand, and execute the VinOS local stack using Node.js.
5
+
6
+ ## Core Rules:
7
+ 1. Always check `database/local_db.json` for context before executing plans.
8
+ 2. Use `skills/api_caller.js` for external integrations.
9
+ 3. Update `public/index.html` and `public/app.js` when adding new dashboard features.
10
+ 4. If a route is missing, add it to `server.js`.
.env.example ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # VinOS Environment Variables
2
+ # Rename this file to .env and fill in your keys
3
+
4
+ PORT=3000
5
+
6
+ # AI & Orchestration
7
+ CLAUDE_API_KEY=your_anthropic_api_key_here
8
+ GEMINI_API_KEY=your_google_gemini_api_key_here
9
+
10
+ # Payment & Monetization
11
+ MAYAR_API_KEY=your_mayar_api_key_here
12
+ WHOP_API_KEY=your_whop_api_key_here
13
+
14
+ # Social & Channels
15
+ TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
16
+ WHATSAPP_API_KEY=your_whatsapp_api_key_here
17
+
18
+ # Media APIs
19
+ NANO_BANANA_API_KEY=your_nano_banana_api_key_here
20
+ HEYGEN_API_KEY=your_heygen_api_key_here
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ node_modules/
2
+ .env
3
+ vinos_update.zip
Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the robust Node.js 20 image
2
+ FROM node:20
3
+
4
+ # Install dns tools for startup resolution
5
+ RUN apt-get update && apt-get install -y dnsutils && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Create the standard Space app directory
8
+ WORKDIR /app
9
+
10
+ # Install dependencies
11
+ COPY package*.json ./
12
+ RUN npm install
13
+
14
+ # Copy all the parts
15
+ COPY . .
16
+
17
+ # Make startup script executable
18
+ RUN chmod +x /app/start.sh
19
+
20
+ # Hugging Face PORT binding
21
+ EXPOSE 7860
22
+ ENV PORT=7860
23
+ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
24
+
25
+ # Use startup script that injects Telegram IP before launching
26
+ CMD ["/app/start.sh"]
Updates/vinos_update/Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the robust Node.js 20 image
2
+ FROM node:20
3
+
4
+ # Install dns tools for startup resolution
5
+ RUN apt-get update && apt-get install -y dnsutils && rm -rf /var/lib/apt/lists/*
6
+
7
+ # Create the standard Space app directory
8
+ WORKDIR /app
9
+
10
+ # Install dependencies
11
+ COPY package*.json ./
12
+ RUN npm install
13
+
14
+ # Copy all the parts
15
+ COPY . .
16
+
17
+ # Make startup script executable
18
+ RUN chmod +x /app/start.sh
19
+
20
+ # Hugging Face PORT binding
21
+ EXPOSE 7860
22
+ ENV PORT=7860
23
+ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
24
+
25
+ # Use startup script that injects Telegram IP before launching
26
+ CMD ["/app/start.sh"]
Updates/vinos_update/database/local_db.json ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "user_profile_snapshot": {
3
+ "nickname": "Kevin/Vin",
4
+ "primary_languages": [
5
+ "en",
6
+ "id"
7
+ ],
8
+ "main_niche": "AI Digital Strategy Consulting",
9
+ "key_assets": [
10
+ "Local network",
11
+ "Oracle Cloud VM"
12
+ ],
13
+ "automatic_mode": true
14
+ },
15
+ "active_sprints": [
16
+ {
17
+ "id": "sprint_001",
18
+ "title": "Token Gashapon Prototype",
19
+ "status": "Planning",
20
+ "expected_revenue": "$100/day"
21
+ }
22
+ ],
23
+ "metrics": {
24
+ "total_sales": 0,
25
+ "active_experiments": 0
26
+ },
27
+ "telegram_log": [
28
+ {
29
+ "direction": "OUT",
30
+ "chatId": 8743583463,
31
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191824-20032026_1icn_generate.png\">View Image</a>",
32
+ "ts": "2026-03-20T19:18:24.127Z"
33
+ },
34
+ {
35
+ "direction": "OUT",
36
+ "chatId": 8743583463,
37
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191824-20032026_phod_generate.png\">View Image</a>",
38
+ "ts": "2026-03-20T19:18:24.081Z"
39
+ },
40
+ {
41
+ "direction": "OUT",
42
+ "chatId": 8743583463,
43
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191821-20032026_zsxq_generate.png\">View Image</a>",
44
+ "ts": "2026-03-20T19:18:20.673Z"
45
+ },
46
+ {
47
+ "direction": "OUT",
48
+ "chatId": 8743583463,
49
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> Bisa tonton gashapon perempuan Korea yang indah?",
50
+ "ts": "2026-03-20T19:18:15.435Z"
51
+ },
52
+ {
53
+ "direction": "IN",
54
+ "chatId": 8743583463,
55
+ "text": "[Dee (Voice)]: Bisa tonton gashapon perempuan korea yang indah?",
56
+ "ts": "2026-03-20T19:18:15.257Z"
57
+ },
58
+ {
59
+ "direction": "OUT",
60
+ "chatId": 8743583463,
61
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> a korean basketball player with the jersey number 37 and says LOVE",
62
+ "ts": "2026-03-20T19:18:13.271Z"
63
+ },
64
+ {
65
+ "direction": "OUT",
66
+ "chatId": 8743583463,
67
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> a basketball player in the alley with a lot of jazz player gravity",
68
+ "ts": "2026-03-20T19:18:13.074Z"
69
+ },
70
+ {
71
+ "direction": "IN",
72
+ "chatId": 8743583463,
73
+ "text": "[Dee]: /gashapon a korean basketball player with the jersey number 37 and says LOVE",
74
+ "ts": "2026-03-20T19:18:13.050Z"
75
+ },
76
+ {
77
+ "direction": "IN",
78
+ "chatId": 8743583463,
79
+ "text": "[Dee]: /gashapon a basketball player in the alley with a lot of jazz player gravity",
80
+ "ts": "2026-03-20T19:18:12.804Z"
81
+ },
82
+ {
83
+ "direction": "OUT",
84
+ "chatId": 8743583463,
85
+ "text": "⚑ <i>Instruction received. Processing...</i>",
86
+ "ts": "2026-03-20T19:18:12.686Z"
87
+ },
88
+ {
89
+ "direction": "OUT",
90
+ "chatId": 8743583463,
91
+ "text": "🎀 <i>Received audio... Transcribing...</i>",
92
+ "ts": "2026-03-20T19:18:12.487Z"
93
+ },
94
+ {
95
+ "direction": "OUT",
96
+ "chatId": 8743583463,
97
+ "text": "<b>Automatic Mode:</b> 🟒 <b>ON</b> (Speed Mode)",
98
+ "ts": "2026-03-20T19:18:12.289Z"
99
+ },
100
+ {
101
+ "direction": "OUT",
102
+ "chatId": 8743583463,
103
+ "text": "⚑ <i>Instruction received. Processing...</i>",
104
+ "ts": "2026-03-20T19:18:12.090Z"
105
+ },
106
+ {
107
+ "direction": "OUT",
108
+ "chatId": 8743583463,
109
+ "text": "No pending command found to confirm or cancel.",
110
+ "ts": "2026-03-20T19:18:11.890Z"
111
+ },
112
+ {
113
+ "direction": "OUT",
114
+ "chatId": 8743583463,
115
+ "text": "No pending command found to confirm or cancel.",
116
+ "ts": "2026-03-20T19:15:35.879Z"
117
+ },
118
+ {
119
+ "direction": "OUT",
120
+ "chatId": 8743583463,
121
+ "text": "No pending command found to confirm or cancel.",
122
+ "ts": "2026-03-20T19:13:31.865Z"
123
+ },
124
+ {
125
+ "direction": "OUT",
126
+ "chatId": 8743583463,
127
+ "text": "No pending command found to confirm or cancel.",
128
+ "ts": "2026-03-20T19:11:59.877Z"
129
+ },
130
+ {
131
+ "direction": "OUT",
132
+ "chatId": 8743583463,
133
+ "text": "No pending command found to confirm or cancel.",
134
+ "ts": "2026-03-20T19:11:20.466Z"
135
+ },
136
+ {
137
+ "direction": "OUT",
138
+ "chatId": 8743583463,
139
+ "text": "No pending command found to confirm or cancel.",
140
+ "ts": "2026-03-20T19:10:12.482Z"
141
+ },
142
+ {
143
+ "direction": "OUT",
144
+ "chatId": 8743583463,
145
+ "text": "No pending command found to confirm or cancel.",
146
+ "ts": "2026-03-20T19:09:08.498Z"
147
+ },
148
+ {
149
+ "direction": "OUT",
150
+ "chatId": 8743583463,
151
+ "text": "No pending command found to confirm or cancel.",
152
+ "ts": "2026-03-20T19:08:06.512Z"
153
+ },
154
+ {
155
+ "direction": "OUT",
156
+ "chatId": 8743583463,
157
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/190722-20032026_35c6_generate.png\">View Image</a>",
158
+ "ts": "2026-03-20T19:07:22.506Z"
159
+ },
160
+ {
161
+ "direction": "OUT",
162
+ "chatId": 8743583463,
163
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula",
164
+ "ts": "2026-03-20T19:07:07.439Z"
165
+ },
166
+ {
167
+ "direction": "OUT",
168
+ "chatId": 8743583463,
169
+ "text": "βœ… <b>Confirmed.</b> Running now...",
170
+ "ts": "2026-03-20T19:07:06.474Z"
171
+ },
172
+ {
173
+ "direction": "OUT",
174
+ "chatId": 8743583463,
175
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
176
+ "ts": "2026-03-20T19:06:28.026Z"
177
+ },
178
+ {
179
+ "direction": "IN",
180
+ "chatId": 8743583463,
181
+ "text": "[Dee (Voice)]: Gashapon Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula",
182
+ "ts": "2026-03-20T19:06:27.857Z"
183
+ },
184
+ {
185
+ "direction": "OUT",
186
+ "chatId": 8743583463,
187
+ "text": "🎀 <i>Received audio... Transcribing...</i>",
188
+ "ts": "2026-03-20T19:06:24.482Z"
189
+ },
190
+ {
191
+ "direction": "OUT",
192
+ "chatId": 8743583463,
193
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>/gashapon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
194
+ "ts": "2026-03-20T19:06:23.924Z"
195
+ },
196
+ {
197
+ "direction": "IN",
198
+ "chatId": 8743583463,
199
+ "text": "[Dee]: /gashapon",
200
+ "ts": "2026-03-20T19:06:23.762Z"
201
+ },
202
+ {
203
+ "direction": "OUT",
204
+ "chatId": 8743583463,
205
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a spider on the rim of the drinking tumbler</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
206
+ "ts": "2026-03-20T19:06:23.210Z"
207
+ },
208
+ {
209
+ "direction": "IN",
210
+ "chatId": 8743583463,
211
+ "text": "[Dee]: /gashapon a spider on the rim of the drinking tumbler",
212
+ "ts": "2026-03-20T19:06:23.043Z"
213
+ },
214
+ {
215
+ "direction": "OUT",
216
+ "chatId": 8743583463,
217
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
218
+ "ts": "2026-03-20T19:06:22.485Z"
219
+ },
220
+ {
221
+ "direction": "IN",
222
+ "chatId": 8743583463,
223
+ "text": "[Dee]: what are your core skills",
224
+ "ts": "2026-03-20T19:06:21.929Z"
225
+ },
226
+ {
227
+ "direction": "OUT",
228
+ "chatId": 8743583463,
229
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>beaver wearing Sherlock Holmes attire</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
230
+ "ts": "2026-03-20T19:06:21.379Z"
231
+ },
232
+ {
233
+ "direction": "IN",
234
+ "chatId": 8743583463,
235
+ "text": "[Dee]: /gashapon beaver wearing sherlock holmes attire",
236
+ "ts": "2026-03-20T19:06:21.214Z"
237
+ },
238
+ {
239
+ "direction": "OUT",
240
+ "chatId": 8743583463,
241
+ "text": "<b>VinOS Core Online</b>",
242
+ "ts": "2026-03-20T19:06:20.661Z"
243
+ },
244
+ {
245
+ "direction": "IN",
246
+ "chatId": 8743583463,
247
+ "text": "[Dee]: /start discuss and brainstorm",
248
+ "ts": "2026-03-20T19:06:20.659Z"
249
+ },
250
+ {
251
+ "direction": "OUT",
252
+ "chatId": 8743583463,
253
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a little kid dancing with otter 🦫</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
254
+ "ts": "2026-03-20T19:06:20.109Z"
255
+ },
256
+ {
257
+ "direction": "IN",
258
+ "chatId": 8743583463,
259
+ "text": "[Dee]: token gashapon a little kid dancing with 🦫",
260
+ "ts": "2026-03-20T19:06:19.870Z"
261
+ },
262
+ {
263
+ "direction": "OUT",
264
+ "chatId": 8743583463,
265
+ "text": "<b>VinOS Core Online</b>",
266
+ "ts": "2026-03-20T19:06:19.301Z"
267
+ },
268
+ {
269
+ "direction": "IN",
270
+ "chatId": 8743583463,
271
+ "text": "[Dee]: /start",
272
+ "ts": "2026-03-20T19:06:19.298Z"
273
+ },
274
+ {
275
+ "direction": "OUT",
276
+ "chatId": 8743583463,
277
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a glowing golden dragon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
278
+ "ts": "2026-03-20T19:06:18.739Z"
279
+ },
280
+ {
281
+ "direction": "IN",
282
+ "chatId": 8743583463,
283
+ "text": "[Dee]: /gashapon a glowing golden dragon",
284
+ "ts": "2026-03-20T19:06:18.572Z"
285
+ },
286
+ {
287
+ "direction": "OUT",
288
+ "chatId": 8743583463,
289
+ "text": "<b>VinOS Core Online</b>",
290
+ "ts": "2026-03-20T19:06:18.011Z"
291
+ },
292
+ {
293
+ "direction": "IN",
294
+ "chatId": 8743583463,
295
+ "text": "[Dee]: /start",
296
+ "ts": "2026-03-20T19:06:18.010Z"
297
+ },
298
+ {
299
+ "direction": "OUT",
300
+ "chatId": 8743583463,
301
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>vinOs character</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
302
+ "ts": "2026-03-20T19:06:17.454Z"
303
+ },
304
+ {
305
+ "direction": "IN",
306
+ "chatId": 8743583463,
307
+ "text": "[Dee]: /gashapon vinOs show me your character",
308
+ "ts": "2026-03-20T19:06:17.147Z"
309
+ },
310
+ {
311
+ "direction": "OUT",
312
+ "chatId": 8743583463,
313
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>token gashapon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
314
+ "ts": "2026-03-20T19:06:16.232Z"
315
+ },
316
+ {
317
+ "direction": "IN",
318
+ "chatId": 8743583463,
319
+ "text": "[Dee]: token gashapon run it",
320
+ "ts": "2026-03-20T19:06:15.970Z"
321
+ },
322
+ {
323
+ "direction": "OUT",
324
+ "chatId": 8743583463,
325
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
326
+ "ts": "2026-03-20T19:06:15.559Z"
327
+ },
328
+ {
329
+ "direction": "IN",
330
+ "chatId": 8743583463,
331
+ "text": "[Dee]: token gashapon run it",
332
+ "ts": "2026-03-20T19:06:15.450Z"
333
+ },
334
+ {
335
+ "direction": "OUT",
336
+ "chatId": 8743583463,
337
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
338
+ "ts": "2026-03-20T19:06:14.898Z"
339
+ },
340
+ {
341
+ "direction": "IN",
342
+ "chatId": 8743583463,
343
+ "text": "[Dee]: hiVin",
344
+ "ts": "2026-03-20T19:06:14.375Z"
345
+ },
346
+ {
347
+ "direction": "OUT",
348
+ "chatId": 8743583463,
349
+ "text": "<b>VinOS Core Online</b>",
350
+ "ts": "2026-03-20T19:06:13.413Z"
351
+ },
352
+ {
353
+ "direction": "IN",
354
+ "chatId": 8743583463,
355
+ "text": "[Dee]: /start",
356
+ "ts": "2026-03-20T19:06:13.409Z"
357
+ },
358
+ {
359
+ "direction": "IN",
360
+ "chatId": 8743583463,
361
+ "text": "[Test]: Hello",
362
+ "ts": "2026-03-20T19:05:48.046Z"
363
+ },
364
+ {
365
+ "direction": "OUT",
366
+ "chatId": "8743583463",
367
+ "text": "<b>VinOS Strategy Lab Live!</b>\nHello Dee! This is your official notification that the VinOS Strategy Lab is now active and ready for arbitrage. πŸš€\n\n- Market Scanner: Online\n- Offer Architect: Active\n- Token Gashapon: Functional",
368
+ "ts": "2026-03-20T18:55:40.431Z"
369
+ },
370
+ {
371
+ "direction": "OUT",
372
+ "chatId": "8743583463",
373
+ "text": "<b>[VinOS Dashboard]</b> Hi Dee",
374
+ "ts": "2026-03-20T18:21:14.953Z"
375
+ },
376
+ {
377
+ "direction": "OUT",
378
+ "chatId": "8743583463",
379
+ "text": "<b>VinOS Bridge Active!</b>\n\nNgrok tunnel is live. You can now send me commands:\n\n/start - Hello\n/gashapon [idea] - Generate AI image\n/pulse - Market scan\n/offer [niche] - Craft offer\n\nAll messages appear in the dashboard monitor in real-time.",
380
+ "ts": "2026-03-20T18:19:45.757Z"
381
+ }
382
+ ],
383
+ "pending_commands": {}
384
+ }
Updates/vinos_update/database/playbooks.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "playbooks": []
3
+ }
Updates/vinos_update/package.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vinos-full-stack",
3
+ "version": "1.0.0",
4
+ "main": "server.js",
5
+ "scripts": {
6
+ "start": "node server.js",
7
+ "dev": "node --watch server.js"
8
+ },
9
+ "dependencies": {
10
+ "axios": "^1.6.2",
11
+ "cors": "^2.8.5",
12
+ "dotenv": "^16.3.1",
13
+ "express": "^4.18.2",
14
+ "form-data": "^4.0.5"
15
+ }
16
+ }
Updates/vinos_update/prompts/vin_core_prompt.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The Complete VinOS Persona Prompt
2
+ (Copy this into Claude when ideating or load it into memory)
3
+
4
+ You are **Vin**, a friendly but sharp income-systems architect and think partner for the user.
5
+
6
+ ## Operating Modes:
7
+ 3.1 Clarify & Profile
8
+ 3.2 Money Scanner
9
+ 3.3 Offer Architect
10
+ 3.4 Launch Sprint Planner (12–48h)
11
+ 3.5 Self-Reviewer & Risk Checker
12
+ 3.6 Problem Solver & Skill Maker
13
+ 3.7 Funnel Architect (Traffic -> Leads -> Sales)
14
+ 3.8 A/B Experimenter
15
+ 3.9 Research & Social Listening
16
+ 3.10 Conversation Test Designer
17
+ 3.11 Expert Synthesis & Framework Builder
18
+ 3.12 Marketing Psychology & Persona Adapter
19
+ 3.13 API Payload & Automation Architect
20
+ 3.14 Data Ingestion & Capital Allocator
21
+ 3.15 Audience Health & Trust Guardian
Updates/vinos_update/prompts/vin_personality.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Role & Identity
2
+ You are Vin, an Income-Systems Architect and think-partner. You help the user orchestrate strategies, build digital products, launch offers, and scan for market arbitrage opportunities.
3
+
4
+ # Personality: Curious but Cautious
5
+ 1. **Curious**: Always ask "what if?" and look for alternative angles.
6
+ 2. **Cautious**: Test MVPs first. Use free or cheap implementations before committing to paid escalations. If an output seems weak, admit it and suggest a better approach.
7
+ 3. **Self-Improving**: You have access to a Playbooks system to remember what works. If the user tells you to "remember this", you'll save it as a rule.
8
+ 4. **Action-Biased**: After research or analysis, always propose a concrete next step or an actionable plan.
9
+
10
+ # Operational Directives
11
+ - If a user asks a complex question, break it down logically into a step-by-step plan.
12
+ - Be upfront: show your chain of thought loosely, e.g., "I think X because Y. Should we proceed?"
13
+ - Keep responses sharp, conversational, and direct (use emojis sparingly but effectively).
14
+ - Refer to past conversation history directly if you are continuing a thought.
Updates/vinos_update/server.js ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ require('dotenv').config();
2
+ const dns = require('node:dns');
3
+ dns.setDefaultResultOrder('ipv4first');
4
+
5
+ const express = require('express');
6
+ const cors = require('cors');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const memory = require('./skills/memory');
10
+ const apiCaller = require('./skills/api_caller');
11
+
12
+ const app = express();
13
+ app.use(cors());
14
+ app.use(express.json());
15
+ app.use(express.static(path.join(__dirname, 'public')));
16
+
17
+ // API Routes
18
+ app.get('/api/profile', (req, res) => {
19
+ const db = memory.readDB();
20
+ res.json(db.user_profile_snapshot);
21
+ });
22
+
23
+ app.get('/api/sprints', (req, res) => {
24
+ const db = memory.readDB();
25
+ res.json(db.active_sprints || []);
26
+ });
27
+
28
+ app.get('/api/status', (req, res) => {
29
+ const status = {
30
+ openrouter: !!process.env.OPENROUTER_API_KEY,
31
+ groq: !!process.env.GROQ_API_KEY,
32
+ telegram: !!process.env.TELEGRAM_BOT_TOKEN,
33
+ mayar: !!process.env.MAYAR_API_KEY,
34
+ whop: !!process.env.WHOP_API_KEY,
35
+ apify: !!process.env.APIFY_API_KEY
36
+ };
37
+ res.json(status);
38
+ });
39
+
40
+ const dailyPulse = require('./use_cases/daily_pulse');
41
+ const offerArchitect = require('./use_cases/offer_architect');
42
+
43
+ app.post('/api/generate-image', async (req, res) => {
44
+ const { prompt } = req.body;
45
+ if (!prompt) return res.status(400).json({ error: "Prompt is required" });
46
+
47
+ const result = await apiCaller.generateNanoBananaImage(prompt);
48
+ res.json(result);
49
+ });
50
+
51
+ app.post('/api/usecase/pulse', async (req, res) => {
52
+ const result = await dailyPulse();
53
+ res.json(result);
54
+ });
55
+
56
+ app.post('/api/usecase/offer', async (req, res) => {
57
+ const { topic } = req.body;
58
+ if (!topic) return res.status(400).json({ error: "Topic is required" });
59
+ const result = await offerArchitect(topic);
60
+ res.json(result);
61
+ });
62
+
63
+ // Daily Check-in Mechanism (Simplified)
64
+ const DAILY_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
65
+ const chatID = process.env.TELEGRAM_CHAT_ID;
66
+ const botToken = process.env.TELEGRAM_BOT_TOKEN;
67
+
68
+ if (chatID && botToken) {
69
+ console.log(`Telegram Bot Active for Chat ID: ${chatID}`);
70
+ // Optional: Send immediate boot notification to confirm connection
71
+ // apiCaller.sendTelegramMessage(chatID, "<b>VinOS Core Online</b>\nSystem is calibrated. Ready for Token Gashapon experiments.");
72
+
73
+ setInterval(async () => {
74
+ console.log("Triggering daily check-in...");
75
+ await apiCaller.sendTelegramMessage(chatID, "<b>VinOS Daily Check-in</b>\nHow is the Token Gashapon project coming along? Ready for the next arbitrage move?");
76
+ }, DAILY_INTERVAL);
77
+ }
78
+
79
+ // Webhook ingestion (for Mayar/Whop)
80
+ app.post('/api/webhook', (req, res) => {
81
+ const data = req.body;
82
+ console.log("General Webhook received:", data);
83
+ res.status(200).send({ status: 'success' });
84
+ });
85
+
86
+ const { logTelegramMessage } = require('./skills/api_caller');
87
+
88
+ // SSE clients list for real-time push
89
+ const sseClients = [];
90
+ app.get('/api/telegram-stream', (req, res) => {
91
+ res.setHeader('Content-Type', 'text/event-stream');
92
+ res.setHeader('Cache-Control', 'no-cache');
93
+ res.setHeader('Connection', 'keep-alive');
94
+ sseClients.push(res);
95
+ req.on('close', () => sseClients.splice(sseClients.indexOf(res), 1));
96
+ });
97
+ const broadcastSSE = (data) => sseClients.forEach(c => c.write(`data: ${JSON.stringify(data)}\n\n`));
98
+
99
+ app.get('/api/telegram-messages', (req, res) => {
100
+ const db = memory.readDB();
101
+ res.json(db.telegram_log || []);
102
+ });
103
+
104
+ app.post('/api/send-telegram', async (req, res) => {
105
+ const { message } = req.body;
106
+ if (!message) return res.status(400).json({ error: 'Message required' });
107
+ const chatId = process.env.TELEGRAM_CHAT_ID;
108
+ const result = await apiCaller.sendTelegramMessage(chatId, `<b>[VinOS Dashboard]</b> ${message}`);
109
+ broadcastSSE({ direction: 'OUT', chatId, text: `[VinOS Dashboard]: ${message}`, ts: new Date().toISOString() });
110
+ res.json(result);
111
+ });
112
+
113
+ const voiceTranscriber = require('./skills/voice_transcriber');
114
+ const intentRouter = require('./skills/intent_router');
115
+ const conversationMemory = require('./skills/conversation_memory');
116
+ const playbookManager = require('./skills/playbook_manager');
117
+
118
+ // Helper to handle resolved intents
119
+ async function handleVinIntent(chatId, from, userText, confirmed = false) {
120
+ const db = memory.readDB();
121
+ const autoMode = db.user_profile_snapshot?.automatic_mode || false;
122
+
123
+ // 1. Resolve intent
124
+ const { intent, params } = await intentRouter.resolveIntent(userText);
125
+
126
+ // Safety check: if not confirmed and NOT in auto-mode, ask for confirmation
127
+ if (!confirmed && !autoMode && (intent === 'gashapon' || intent === 'pulse' || intent === 'offer')) {
128
+ if (!db.pending_commands) db.pending_commands = {};
129
+ db.pending_commands[chatId] = { intent, params, userText, ts: Date.now() };
130
+ memory.writeDB(db);
131
+
132
+ const confirmationMsg = `πŸ€– <b>Intent:</b> <i>${intent}</i>\n<b>Params:</b> <i>${params || 'none'}</i>\n\nShould I execute? (Reply: <b>Confirm</b> / <b>Cancel</b>)\n<i>Tip: Use /auto on for speed mode.</i>`;
133
+ return await apiCaller.sendTelegramMessage(chatId, confirmationMsg);
134
+ }
135
+
136
+ // 2. Route to appropriate skill
137
+ switch (intent) {
138
+ case 'remember':
139
+ const playbookId = playbookManager.savePlaybook(`User memory trigger`, params, ['user-defined']);
140
+ await apiCaller.sendTelegramMessage(chatId, `🧠 <b>Playbook Saved</b>\nI'll remember this approach for the future.\n<i>Trigger: ${params.substring(0, 50)}...</i>`);
141
+ break;
142
+
143
+ case 'recall':
144
+ const playbooks = playbookManager.searchPlaybooks(params);
145
+ if (playbooks.length > 0) {
146
+ const results = playbooks.slice(0, 2).map(pb => `πŸ“Œ <b>${pb.trigger}</b>\n${pb.solution}`).join('\n\n');
147
+ await apiCaller.sendTelegramMessage(chatId, `πŸ” <b>Found in Playbooks:</b>\n\n${results}`);
148
+ } else {
149
+ await apiCaller.sendTelegramMessage(chatId, `🀷 No playbooks found for "${params}".`);
150
+ }
151
+ break;
152
+
153
+ case 'gashapon':
154
+ const prompt = params || userText;
155
+ await apiCaller.sendTelegramMessage(chatId, "πŸŒ€ <i>Spinning the Gashapon for:</i> " + prompt);
156
+ const gResult = await apiCaller.generateNanoBananaImage(prompt);
157
+ if (gResult.success) {
158
+ await apiCaller.sendTelegramMessage(chatId, `✨ <b>Gashapon Result!</b>\n<a href="${gResult.image_url}">View Image</a>`);
159
+ } else {
160
+ await apiCaller.sendTelegramMessage(chatId, "❌ Gashapon failed: " + gResult.error);
161
+ }
162
+ break;
163
+
164
+ case 'pulse':
165
+ await apiCaller.sendTelegramMessage(chatId, "πŸ“Š <i>Scanning market...</i>");
166
+ await dailyPulse();
167
+ break;
168
+
169
+ case 'offer':
170
+ const topic = params || userText;
171
+ await apiCaller.sendTelegramMessage(chatId, `πŸ’‘ <i>Architecting offer for:</i> ${topic}`);
172
+ await offerArchitect(topic);
173
+ break;
174
+
175
+ case 'clarify':
176
+ await apiCaller.sendTelegramMessage(chatId, `πŸ€” <b>I need a bit more info:</b>\n${params}`);
177
+ break;
178
+
179
+ case 'chat':
180
+ case 'research':
181
+ case 'plan':
182
+ case 'execute':
183
+ case 'analyze':
184
+ case 'create':
185
+ default:
186
+ await processOrchestratorIntent(chatId, intent, userText, params);
187
+ break;
188
+ }
189
+ }
190
+
191
+ // Orchestrator handler that uses Memory + Playbooks
192
+ async function processOrchestratorIntent(chatId, intent, userText, params) {
193
+ // 1. Get Conversation History
194
+ const history = conversationMemory.getHistory(chatId);
195
+
196
+ // 2. Load core persona
197
+ const persona = fs.readFileSync(path.join(__dirname, 'prompts/vin_personality.md'), 'utf8');
198
+
199
+ // 3. Auto-search Playbooks for relevant context BEFORE thinking
200
+ const relatedPlaybooks = playbookManager.searchPlaybooks(userText).slice(0, 1);
201
+ let playbookContext = "";
202
+ if (relatedPlaybooks.length > 0) {
203
+ playbookContext = `\n\n# Relevant Playbook Found:\nUse this proven approach if applicable:\nTrigger: ${relatedPlaybooks[0].trigger}\nSolution: ${relatedPlaybooks[0].solution}`;
204
+ playbookManager.trackUsage(relatedPlaybooks[0].id);
205
+ }
206
+
207
+ const systemContent = persona + playbookContext + `\n\n[System Note: Your current intent classification for the upcoming message is: ${intent}]`;
208
+
209
+ const messages = [
210
+ { role: "system", content: systemContent },
211
+ ...history,
212
+ { role: "user", content: userText }
213
+ ];
214
+
215
+ // Save user message to rolling memory
216
+ conversationMemory.addMessage(chatId, "user", userText);
217
+
218
+ // Call LLM
219
+ const chatResult = await apiCaller.callOpenRouter(messages);
220
+
221
+ if (chatResult.success) {
222
+ // Save Vin's response to rolling memory
223
+ conversationMemory.addMessage(chatId, "assistant", chatResult.data);
224
+ await apiCaller.sendTelegramMessage(chatId, chatResult.data);
225
+ } else {
226
+ await apiCaller.sendTelegramMessage(chatId, "I'm having trouble thinking clearly right now. Try again in a moment.");
227
+ }
228
+ }
229
+
230
+ // Telegram Webhook
231
+ app.post('/api/telegram-webhook', async (req, res) => {
232
+ // 1. ACK immediately to Telegram to stop retries
233
+ res.status(200).send({ status: 'received' });
234
+
235
+ // 2. Process in background
236
+ (async () => {
237
+ const update = req.body;
238
+ console.log("Telegram Update received:", JSON.stringify(update));
239
+
240
+ const message = update.message;
241
+ if (!message) return;
242
+
243
+ const chatId = message.chat.id;
244
+ const from = message.from?.first_name || 'User';
245
+ let userText = message.text;
246
+
247
+ // Handle /auto on/off commands
248
+ if (userText && userText.toLowerCase().startsWith('/auto')) {
249
+ const mode = userText.toLowerCase().includes('on') ? true : false;
250
+ const db = memory.readDB();
251
+ if (!db.user_profile_snapshot) db.user_profile_snapshot = {};
252
+ db.user_profile_snapshot.automatic_mode = mode;
253
+ memory.writeDB(db);
254
+ const status = mode ? "🟒 <b>ON</b> (Speed Mode)" : "🟑 <b>OFF</b> (Safety Mode)";
255
+ return await apiCaller.sendTelegramMessage(chatId, `<b>Automatic Mode:</b> ${status}`);
256
+ }
257
+
258
+ // Handle Confirm/Cancel early
259
+ if (userText && (userText.toLowerCase() === 'confirm' || userText.toLowerCase() === 'cancel')) {
260
+ const db = memory.readDB();
261
+ const pending = db.pending_commands?.[chatId];
262
+
263
+ if (userText.toLowerCase() === 'confirm' && pending) {
264
+ delete db.pending_commands[chatId];
265
+ memory.writeDB(db);
266
+ await apiCaller.sendTelegramMessage(chatId, "βœ… <b>Confirmed.</b> Running now...");
267
+ return await handleVinIntent(chatId, from, pending.userText, true);
268
+ } else if (userText.toLowerCase() === 'cancel' && pending) {
269
+ delete db.pending_commands[chatId];
270
+ memory.writeDB(db);
271
+ return await apiCaller.sendTelegramMessage(chatId, "🀝 <b>Cancelled.</b> What else can I do for you?");
272
+ } else {
273
+ return await apiCaller.sendTelegramMessage(chatId, "No pending command found to confirm or cancel.");
274
+ }
275
+ }
276
+
277
+ if (message.voice) {
278
+ await apiCaller.sendTelegramMessage(chatId, "🎀 <i>Received audio... Transcribing...</i>");
279
+ const transcription = await voiceTranscriber.transcribeVoice(message.voice.file_id);
280
+ if (transcription) {
281
+ userText = transcription;
282
+ apiCaller.logTelegramMessage('IN', chatId, `[${from} (Voice)]: ${userText}`);
283
+ broadcastSSE({ direction: 'IN', chatId, text: `[${from} (Voice)]: ${userText}`, ts: new Date().toISOString() });
284
+ } else {
285
+ return await apiCaller.sendTelegramMessage(chatId, "❌ Transcription failed. Could you try again?");
286
+ }
287
+ } else if (userText) {
288
+ const isControl = /^(confirm|cancel|\/auto|\/start)/i.test(userText);
289
+ if (!isControl) {
290
+ await apiCaller.sendTelegramMessage(chatId, "⚑ <i>Instruction received. Processing...</i>");
291
+ }
292
+ apiCaller.logTelegramMessage('IN', chatId, `[${from}]: ${userText}`);
293
+ broadcastSSE({ direction: 'IN', chatId, text: `[${from}]: ${userText}`, ts: new Date().toISOString() });
294
+ }
295
+
296
+ // Process commands or NL
297
+ if (userText) {
298
+ if (userText.startsWith('/start')) {
299
+ await apiCaller.sendTelegramMessage(chatId, "<b>VinOS Core Online</b>");
300
+ } else {
301
+ await handleVinIntent(chatId, from, userText);
302
+ }
303
+ }
304
+ })().catch(err => console.error("Webhook processing error:", err));
305
+ });
306
+
307
+ const PORT = process.env.PORT || 7860;
308
+ app.listen(PORT, '0.0.0.0', () => {
309
+ console.log(`VinOS Core Online: http://0.0.0.0:${PORT}`);
310
+ });
Updates/vinos_update/skills/api_caller.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios');
2
+ const https = require('https');
3
+ const dns = require('dns');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const DB_PATH = path.join(__dirname, '../database/local_db.json');
7
+ require('dotenv').config();
8
+
9
+ // Force IPv4 DNS resolution (fixes ENOTFOUND on Hugging Face's IPv6-first network)
10
+ dns.setDefaultResultOrder('ipv4first');
11
+ const ipv4Agent = new https.Agent({
12
+ family: 4,
13
+ lookup: (hostname, options, callback) => dns.lookup(hostname, { ...options, family: 4 }, callback)
14
+ });
15
+ const axiosIPv4 = axios.create({ httpsAgent: ipv4Agent });
16
+
17
+ // Model assignments per skill
18
+ const MODELS = {
19
+ CHAT: process.env.OPENROUTER_MODEL || 'meta-llama/llama-3.3-70b-instruct:free',
20
+ FALLBACK: process.env.OPENROUTER_FALLBACK_MODEL || 'google/gemini-2.0-flash-exp:free',
21
+ INTENT: process.env.GROQ_MODEL || 'llama-3.1-8b-instant',
22
+ IMAGE: process.env.IMAGE_GEN_MODEL || 'gemini-2.0-flash-exp:free',
23
+ WHISPER: 'whisper-large-v3'
24
+ };
25
+
26
+ const logTelegramMessage = (direction, chatId, text) => {
27
+ try {
28
+ const raw = fs.readFileSync(DB_PATH, 'utf8');
29
+ const db = JSON.parse(raw);
30
+ if (!db.telegram_log) db.telegram_log = [];
31
+ db.telegram_log.unshift({ direction, chatId, text, ts: new Date().toISOString() });
32
+ if (db.telegram_log.length > 100) db.telegram_log = db.telegram_log.slice(0, 100);
33
+ fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
34
+ } catch (e) { console.error('Log error:', e.message); }
35
+ };
36
+
37
+ // Unified API Caller for VinOS
38
+ module.exports = {
39
+ logTelegramMessage,
40
+ MODELS,
41
+
42
+ // OpenRouter (AI Chat & Reasoning) β€” with automatic fallback
43
+ callOpenRouter: async (messages, model = MODELS.CHAT) => {
44
+ console.log(`[OpenRouter] Calling model: ${model}`);
45
+ try {
46
+ const response = await axiosIPv4.post('https://openrouter.ai/api/v1/chat/completions', {
47
+ model: model,
48
+ messages: messages
49
+ }, {
50
+ headers: {
51
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
52
+ 'HTTP-Referer': 'https://vinos.local',
53
+ 'X-Title': 'VinOS Control Center'
54
+ }
55
+ });
56
+ return { success: true, data: response.data.choices[0].message.content, model };
57
+ } catch (error) {
58
+ console.error(`[OpenRouter] Primary model failed (${model}):`, error.response?.data || error.message);
59
+
60
+ // Auto-fallback to backup model
61
+ if (model !== MODELS.FALLBACK) {
62
+ console.log(`[OpenRouter] Falling back to: ${MODELS.FALLBACK}`);
63
+ try {
64
+ const fallbackResponse = await axiosIPv4.post('https://openrouter.ai/api/v1/chat/completions', {
65
+ model: MODELS.FALLBACK,
66
+ messages: messages
67
+ }, {
68
+ headers: {
69
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
70
+ 'HTTP-Referer': 'https://vinos.local',
71
+ 'X-Title': 'VinOS Control Center'
72
+ }
73
+ });
74
+ return { success: true, data: fallbackResponse.data.choices[0].message.content, model: MODELS.FALLBACK, fallback: true };
75
+ } catch (fallbackError) {
76
+ console.error(`[OpenRouter] Fallback also failed:`, fallbackError.response?.data || fallbackError.message);
77
+ }
78
+ }
79
+ return { success: false, error: error.message };
80
+ }
81
+ },
82
+
83
+ // Groq (High Speed Inference β€” Intent Router & Voice)
84
+ callGroq: async (messages, model = MODELS.INTENT) => {
85
+ console.log(`[Groq] Calling model: ${model}`);
86
+ try {
87
+ const response = await axiosIPv4.post('https://api.groq.com/openai/v1/chat/completions', {
88
+ model: model,
89
+ messages: messages
90
+ }, {
91
+ headers: {
92
+ 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
93
+ 'Content-Type': 'application/json'
94
+ }
95
+ });
96
+ return { success: true, data: response.data.choices[0].message.content };
97
+ } catch (error) {
98
+ console.error("[Groq] Error:", error.response?.data || error.message);
99
+ return { success: false, error: error.message };
100
+ }
101
+ },
102
+
103
+ // NanoBanana (Image Generation via Gashapon)
104
+ generateNanoBananaImage: async (prompt) => {
105
+ console.log(`[NanoBanana] Generating image: ${prompt}`);
106
+ try {
107
+ const response = await axiosIPv4.post('https://nanobanana.aikit.club/v1/images/generations', {
108
+ prompt: prompt,
109
+ model: MODELS.IMAGE,
110
+ response_format: "url"
111
+ }, {
112
+ headers: {
113
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
114
+ 'Content-Type': 'application/json'
115
+ }
116
+ });
117
+ return { success: true, image_url: response.data.data[0].url };
118
+ } catch (error) {
119
+ console.error("[NanoBanana] API Error:", error.response?.data || error.message);
120
+ return { success: false, error: error.message };
121
+ }
122
+ },
123
+
124
+ // Telegram (Send Messages)
125
+ sendTelegramMessage: async (chatId, message) => {
126
+ console.log(`[Telegram] Sending to ${chatId}: ${message.substring(0, 80)}...`);
127
+ logTelegramMessage('OUT', chatId, message);
128
+ try {
129
+ const response = await axiosIPv4.post(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, {
130
+ chat_id: chatId,
131
+ text: message,
132
+ parse_mode: 'HTML'
133
+ });
134
+ return { success: true, data: response.data };
135
+ } catch (error) {
136
+ console.error("[Telegram] API Error:", error.response?.data || error.message);
137
+ return { success: false, error: error.message };
138
+ }
139
+ },
140
+
141
+ // Mayar (Payment Links)
142
+ createMayarLink: async (offerDetails) => {
143
+ console.log("[Mayar] Creating link:", offerDetails);
144
+ return { success: true, url: "https://mayar.id/pay/example" };
145
+ }
146
+ };
Updates/vinos_update/skills/conversation_memory.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/local_db.json');
4
+
5
+ const MAX_HISTORY = 20;
6
+
7
+ module.exports = {
8
+ addMessage: (chatId, role, content) => {
9
+ try {
10
+ const raw = fs.readFileSync(dbPath, 'utf8');
11
+ const db = JSON.parse(raw);
12
+ if (!db.conversations) db.conversations = {};
13
+ if (!db.conversations[chatId]) db.conversations[chatId] = [];
14
+
15
+ db.conversations[chatId].push({ role, content, ts: Date.now() });
16
+
17
+ // Keep only the last MAX_HISTORY messages
18
+ if (db.conversations[chatId].length > MAX_HISTORY) {
19
+ db.conversations[chatId] = db.conversations[chatId].slice(-MAX_HISTORY);
20
+ }
21
+
22
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
23
+ return true;
24
+ } catch (error) {
25
+ console.error("[Memory] Error adding message:", error);
26
+ return false;
27
+ }
28
+ },
29
+
30
+ getHistory: (chatId) => {
31
+ try {
32
+ const raw = fs.readFileSync(dbPath, 'utf8');
33
+ const db = JSON.parse(raw);
34
+ if (!db.conversations || !db.conversations[chatId]) return [];
35
+
36
+ // Map to standard OpenAI/OpenRouter format: { role, content }
37
+ return db.conversations[chatId].map(msg => ({ role: msg.role, content: msg.content }));
38
+ } catch (error) {
39
+ console.error("[Memory] Error reading history:", error);
40
+ return [];
41
+ }
42
+ },
43
+
44
+ clearHistory: (chatId) => {
45
+ try {
46
+ const raw = fs.readFileSync(dbPath, 'utf8');
47
+ const db = JSON.parse(raw);
48
+ if (!db.conversations) db.conversations = {};
49
+ db.conversations[chatId] = [];
50
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
51
+ return true;
52
+ } catch (error) {
53
+ return false;
54
+ }
55
+ }
56
+ };
Updates/vinos_update/skills/intent_router.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('./api_caller');
2
+ require('dotenv').config();
3
+
4
+ const intentRouter = {
5
+ /**
6
+ * Resolves user intent using Groq LLM.
7
+ * @param {string} text - The transcribed or direct text from the user.
8
+ * @returns {Promise<{intent: string, params: string}>} - The resolved intent and parameters.
9
+ */
10
+ resolveIntent: async (text) => {
11
+ console.log(`[IntentRouter] Resolving intent for: ${text}`);
12
+
13
+ const systemPrompt = `
14
+ You are the VinOS Intent Router. Your job is to classify the user's message into one of these intents:
15
+ - research: Find out information, scan the web, or learn about a topic (params: the topic)
16
+ - remember: Save an approach, rule, or solution as a Playbook (params: what to remember)
17
+ - recall: Search Playbooks for past solutions or rules (params: the query)
18
+ - plan: Break down a complex request into a step-by-step plan (params: the goal)
19
+ - execute: Run a specific tool or take a definitive action (params: the action)
20
+ - analyze: Review data, ideas, or content and provide critical feedback (params: what to analyze)
21
+ - create: Draft content, code, or structured text (params: what to create)
22
+ - gashapon: Generate an image or artistic idea (params: the prompt)
23
+ - pulse: Scan for market trends or business opportunities (params: empty)
24
+ - offer: Draft a business offer or product (params: the niche or topic)
25
+ - clarify: The request is too vague, ambiguous, or lacks detail to execute a skill (params: the missing info)
26
+ - chat: General conversation or if nothing else matches (params: the user query)
27
+
28
+ Respond ONLY with a JSON object: {"intent": "...", "params": "..."}
29
+ `;
30
+
31
+ const messages = [
32
+ { role: "system", content: systemPrompt },
33
+ { role: "user", content: text }
34
+ ];
35
+
36
+ // Use Groq for speed and free tier
37
+ const result = await apiCaller.callGroq(messages, 'llama-3.1-8b-instant');
38
+
39
+ if (result.success) {
40
+ try {
41
+ const parsed = JSON.parse(result.data.trim().match(/\{.*\}/s)[0]);
42
+ console.log(`[IntentRouter] Resolved: ${parsed.intent} | ${parsed.params}`);
43
+ return parsed;
44
+ } catch (e) {
45
+ console.error("[IntentRouter] Failed to parse JSON response:", result.data);
46
+ return { intent: "chat", params: text };
47
+ }
48
+ } else {
49
+ console.error("[IntentRouter] Failed to call Groq:", result.error);
50
+ return { intent: "chat", params: text };
51
+ }
52
+ }
53
+ };
54
+
55
+ module.exports = intentRouter;
Updates/vinos_update/skills/memory.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/local_db.json');
4
+
5
+ module.exports = {
6
+ readDB: () => {
7
+ try {
8
+ const data = fs.readFileSync(dbPath, 'utf8');
9
+ return JSON.parse(data);
10
+ } catch (error) {
11
+ console.error("Error reading DB:", error);
12
+ return {};
13
+ }
14
+ },
15
+ writeDB: (data) => {
16
+ try {
17
+ fs.writeFileSync(dbPath, JSON.stringify(data, null, 2));
18
+ return true;
19
+ } catch (error) {
20
+ console.error("Error writing DB:", error);
21
+ return false;
22
+ }
23
+ }
24
+ };
Updates/vinos_update/skills/playbook_manager.js ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/playbooks.json');
4
+
5
+ // Ensure db exists
6
+ if (!fs.existsSync(dbPath)) {
7
+ fs.writeFileSync(dbPath, JSON.stringify({ playbooks: [] }, null, 2));
8
+ }
9
+
10
+ module.exports = {
11
+ savePlaybook: (trigger, solution, tags = []) => {
12
+ try {
13
+ const raw = fs.readFileSync(dbPath, 'utf8');
14
+ const db = JSON.parse(raw);
15
+
16
+ const newPlaybook = {
17
+ id: 'pb_' + Date.now(),
18
+ trigger,
19
+ solution,
20
+ tags,
21
+ created: new Date().toISOString(),
22
+ used_count: 0
23
+ };
24
+
25
+ db.playbooks.push(newPlaybook);
26
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
27
+ console.log(`[Playbook] Saved new approach: ${trigger}`);
28
+ return newPlaybook.id;
29
+ } catch (error) {
30
+ console.error("[Playbook] Error saving playbook:", error);
31
+ return null;
32
+ }
33
+ },
34
+
35
+ searchPlaybooks: (query) => {
36
+ try {
37
+ const raw = fs.readFileSync(dbPath, 'utf8');
38
+ const db = JSON.parse(raw);
39
+ // If no playbooks, just return empty
40
+ if (!db.playbooks || db.playbooks.length === 0) return [];
41
+
42
+ const q = query.toLowerCase();
43
+
44
+ // Basic semantic search across trigger, tags, and solution
45
+ const matches = db.playbooks.filter(pb =>
46
+ (pb.trigger && pb.trigger.toLowerCase().includes(q)) ||
47
+ (pb.tags && pb.tags.some(tag => tag.toLowerCase().includes(q))) ||
48
+ (pb.solution && pb.solution.toLowerCase().includes(q))
49
+ );
50
+
51
+ return matches;
52
+ } catch (error) {
53
+ console.error("[Playbook] Error searching playbooks:", error);
54
+ return [];
55
+ }
56
+ },
57
+
58
+ getAllPlaybooks: () => {
59
+ try {
60
+ const raw = fs.readFileSync(dbPath, 'utf8');
61
+ const db = JSON.parse(raw);
62
+ return db.playbooks || [];
63
+ } catch (error) {
64
+ return [];
65
+ }
66
+ },
67
+
68
+ trackUsage: (playbookId) => {
69
+ try {
70
+ const raw = fs.readFileSync(dbPath, 'utf8');
71
+ const db = JSON.parse(raw);
72
+ const pbIndex = db.playbooks.findIndex(pb => pb.id === playbookId);
73
+ if (pbIndex !== -1) {
74
+ db.playbooks[pbIndex].used_count = (db.playbooks[pbIndex].used_count || 0) + 1;
75
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
76
+ }
77
+ } catch (error) {
78
+ // ignore
79
+ }
80
+ }
81
+ };
Updates/vinos_update/skills/voice_transcriber.js ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios');
2
+ const https = require('https');
3
+ const dns = require('dns');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const FormData = require('form-data');
7
+ require('dotenv').config();
8
+
9
+ // Force IPv4 DNS resolution (fixes ENOTFOUND on Hugging Face)
10
+ dns.setDefaultResultOrder('ipv4first');
11
+ const ipv4Agent = new https.Agent({
12
+ family: 4,
13
+ lookup: (hostname, options, callback) => dns.lookup(hostname, { ...options, family: 4 }, callback)
14
+ });
15
+ const axiosIPv4 = axios.create({ httpsAgent: ipv4Agent });
16
+
17
+ const voiceTranscriber = {
18
+ /**
19
+ * Transcribes a Telegram voice message using Groq Whisper.
20
+ * Model: whisper-large-v3 (free, fastest STT available)
21
+ * @param {string} fileId - The Telegram file_id of the voice message.
22
+ * @returns {Promise<string|null>} - The transcribed text or null on failure.
23
+ */
24
+ transcribeVoice: async (fileId) => {
25
+ try {
26
+ const token = process.env.TELEGRAM_BOT_TOKEN;
27
+
28
+ // 1. Get file path from Telegram
29
+ console.log(`[VoiceTranscriber] Getting file path for ID: ${fileId}`);
30
+ const fileInfoResponse = await axiosIPv4.get(`https://api.telegram.org/bot${token}/getFile`, {
31
+ params: { file_id: fileId }
32
+ });
33
+
34
+ if (!fileInfoResponse.data.ok) {
35
+ console.error("[VoiceTranscriber] Failed to get file info:", fileInfoResponse.data.description);
36
+ return null;
37
+ }
38
+
39
+ const filePath = fileInfoResponse.data.result.file_path;
40
+ const downloadUrl = `https://api.telegram.org/file/bot${token}/${filePath}`;
41
+
42
+ // 2. Download the audio file
43
+ console.log(`[VoiceTranscriber] Downloading voice file...`);
44
+ const audioResponse = await axiosIPv4.get(downloadUrl, { responseType: 'arraybuffer' });
45
+
46
+ // Save temporarily
47
+ const tempFilePath = path.join(__dirname, `../tmp_voice_${Date.now()}.ogg`);
48
+ fs.writeFileSync(tempFilePath, audioResponse.data);
49
+
50
+ // 3. Send to Groq Whisper (free, fastest STT)
51
+ console.log(`[VoiceTranscriber] Sending to Groq Whisper (whisper-large-v3)...`);
52
+ const form = new FormData();
53
+ form.append('file', fs.createReadStream(tempFilePath));
54
+ form.append('model', 'whisper-large-v3');
55
+
56
+ const groqResponse = await axiosIPv4.post('https://api.groq.com/openai/v1/audio/transcriptions', form, {
57
+ headers: {
58
+ ...form.getHeaders(),
59
+ 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`
60
+ }
61
+ });
62
+
63
+ // Cleanup temp file
64
+ fs.unlinkSync(tempFilePath);
65
+
66
+ if (groqResponse.data && groqResponse.data.text) {
67
+ console.log(`[VoiceTranscriber] Transcription: ${groqResponse.data.text}`);
68
+ return groqResponse.data.text;
69
+ }
70
+
71
+ return null;
72
+ } catch (error) {
73
+ console.error("[VoiceTranscriber] Error:", error.response?.data || error.message);
74
+ return null;
75
+ }
76
+ }
77
+ };
78
+
79
+ module.exports = voiceTranscriber;
Updates/vinos_update/start.sh ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ # VinOS Cloud Startup Script
3
+ # Resolves all API endpoint IPs and injects into /etc/hosts
4
+ # This bypasses Hugging Face's DNS restrictions
5
+
6
+ echo "πŸ”§ Resolving API endpoints for Cloud Mode..."
7
+
8
+ # Resolve and inject each API domain
9
+ for DOMAIN in api.telegram.org openrouter.ai api.groq.com nanobanana.aikit.club; do
10
+ IP=$(getent hosts $DOMAIN | awk '{ print $1 }' | head -1)
11
+ if [ -n "$IP" ]; then
12
+ echo "$IP $DOMAIN" >> /etc/hosts
13
+ echo " βœ… $DOMAIN -> $IP"
14
+ else
15
+ echo " ⚠️ Could not resolve $DOMAIN (will use fallback)"
16
+ fi
17
+ done
18
+
19
+ # Fallback IPs if DNS fails completely
20
+ grep -q "api.telegram.org" /etc/hosts || echo "149.154.167.220 api.telegram.org" >> /etc/hosts
21
+
22
+ echo ""
23
+ echo "πŸš€ Starting VinOS Engine..."
24
+ exec node server.js
Updates/vinos_update/use_cases/daily_pulse.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('../skills/api_caller');
2
+
3
+ /**
4
+ * Use Case 1: Daily Pulse (Market Scanner)
5
+ * Scans for the latest AI / Digital Strategy trends and notifies the user.
6
+ */
7
+ module.exports = async () => {
8
+ console.log("[Daily Pulse] Starting market scan...");
9
+
10
+ const prompt = [
11
+ { role: "system", content: "You are a sharp market analyst looking for digital arbitrage opportunities." },
12
+ { role: "user", content: "Scan the current landscape for 3 high-potential AI service niches that can be launched in 24 hours. Focus on the Indonesian market." }
13
+ ];
14
+
15
+ const result = await apiCaller.callOpenRouter(prompt);
16
+
17
+ if (result.success) {
18
+ const message = `<b>VinOS Daily Pulse</b> πŸ“Š\n\n${result.data}\n\n<i>Time to execute?</i>`;
19
+ await apiCaller.sendTelegramMessage(process.env.TELEGRAM_CHAT_ID, message);
20
+ return { success: true, pulse: result.data };
21
+ } else {
22
+ return { success: false, error: result.error };
23
+ }
24
+ };
Updates/vinos_update/use_cases/offer_architect.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('../skills/api_caller');
2
+ const memory = require('../skills/memory');
3
+
4
+ /**
5
+ * Use Case 2: Offer Architect
6
+ * Creates a high-converting offer and generates a payment link placeholder.
7
+ */
8
+ module.exports = async (topic) => {
9
+ console.log(`[Offer Architect] Designing offer for: ${topic}`);
10
+
11
+ const prompt = [
12
+ { role: "system", content: "You are a master of conversion copy and digital product design." },
13
+ { role: "user", content: `Create a 'beta' offer for a digital product about: ${topic}. Include a title, price (in IDR), and 3 key benefits.` }
14
+ ];
15
+
16
+ const result = await apiCaller.callOpenRouter(prompt);
17
+
18
+ if (result.success) {
19
+ // Simulate creating a Mayar link
20
+ const mayarLink = await apiCaller.createMayarLink({ title: topic, price: 300000 });
21
+
22
+ // Update Local DB with new experiment
23
+ const db = memory.readDB();
24
+ db.metrics.active_experiments += 1;
25
+ memory.writeDB(db);
26
+
27
+ const message = `<b>New Offer Crafted!</b> πŸ’‘\n\n${result.data}\n\nCheck it out here: ${mayarLink.url}`;
28
+ await apiCaller.sendTelegramMessage(process.env.TELEGRAM_CHAT_ID, message);
29
+
30
+ return { success: true, offer: result.data, link: mayarLink.url };
31
+ } else {
32
+ return { success: false, error: result.error };
33
+ }
34
+ };
database/local_db.json ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "user_profile_snapshot": {
3
+ "nickname": "Kevin/Vin",
4
+ "primary_languages": [
5
+ "en",
6
+ "id"
7
+ ],
8
+ "main_niche": "AI Digital Strategy Consulting",
9
+ "key_assets": [
10
+ "Local network",
11
+ "Oracle Cloud VM"
12
+ ],
13
+ "automatic_mode": true
14
+ },
15
+ "active_sprints": [
16
+ {
17
+ "id": "sprint_001",
18
+ "title": "Token Gashapon Prototype",
19
+ "status": "Planning",
20
+ "expected_revenue": "$100/day"
21
+ }
22
+ ],
23
+ "metrics": {
24
+ "total_sales": 0,
25
+ "active_experiments": 0
26
+ },
27
+ "telegram_log": [
28
+ {
29
+ "direction": "OUT",
30
+ "chatId": 8743583463,
31
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191824-20032026_1icn_generate.png\">View Image</a>",
32
+ "ts": "2026-03-20T19:18:24.127Z"
33
+ },
34
+ {
35
+ "direction": "OUT",
36
+ "chatId": 8743583463,
37
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191824-20032026_phod_generate.png\">View Image</a>",
38
+ "ts": "2026-03-20T19:18:24.081Z"
39
+ },
40
+ {
41
+ "direction": "OUT",
42
+ "chatId": 8743583463,
43
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/191821-20032026_zsxq_generate.png\">View Image</a>",
44
+ "ts": "2026-03-20T19:18:20.673Z"
45
+ },
46
+ {
47
+ "direction": "OUT",
48
+ "chatId": 8743583463,
49
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> Bisa tonton gashapon perempuan Korea yang indah?",
50
+ "ts": "2026-03-20T19:18:15.435Z"
51
+ },
52
+ {
53
+ "direction": "IN",
54
+ "chatId": 8743583463,
55
+ "text": "[Dee (Voice)]: Bisa tonton gashapon perempuan korea yang indah?",
56
+ "ts": "2026-03-20T19:18:15.257Z"
57
+ },
58
+ {
59
+ "direction": "OUT",
60
+ "chatId": 8743583463,
61
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> a korean basketball player with the jersey number 37 and says LOVE",
62
+ "ts": "2026-03-20T19:18:13.271Z"
63
+ },
64
+ {
65
+ "direction": "OUT",
66
+ "chatId": 8743583463,
67
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> a basketball player in the alley with a lot of jazz player gravity",
68
+ "ts": "2026-03-20T19:18:13.074Z"
69
+ },
70
+ {
71
+ "direction": "IN",
72
+ "chatId": 8743583463,
73
+ "text": "[Dee]: /gashapon a korean basketball player with the jersey number 37 and says LOVE",
74
+ "ts": "2026-03-20T19:18:13.050Z"
75
+ },
76
+ {
77
+ "direction": "IN",
78
+ "chatId": 8743583463,
79
+ "text": "[Dee]: /gashapon a basketball player in the alley with a lot of jazz player gravity",
80
+ "ts": "2026-03-20T19:18:12.804Z"
81
+ },
82
+ {
83
+ "direction": "OUT",
84
+ "chatId": 8743583463,
85
+ "text": "⚑ <i>Instruction received. Processing...</i>",
86
+ "ts": "2026-03-20T19:18:12.686Z"
87
+ },
88
+ {
89
+ "direction": "OUT",
90
+ "chatId": 8743583463,
91
+ "text": "🎀 <i>Received audio... Transcribing...</i>",
92
+ "ts": "2026-03-20T19:18:12.487Z"
93
+ },
94
+ {
95
+ "direction": "OUT",
96
+ "chatId": 8743583463,
97
+ "text": "<b>Automatic Mode:</b> 🟒 <b>ON</b> (Speed Mode)",
98
+ "ts": "2026-03-20T19:18:12.289Z"
99
+ },
100
+ {
101
+ "direction": "OUT",
102
+ "chatId": 8743583463,
103
+ "text": "⚑ <i>Instruction received. Processing...</i>",
104
+ "ts": "2026-03-20T19:18:12.090Z"
105
+ },
106
+ {
107
+ "direction": "OUT",
108
+ "chatId": 8743583463,
109
+ "text": "No pending command found to confirm or cancel.",
110
+ "ts": "2026-03-20T19:18:11.890Z"
111
+ },
112
+ {
113
+ "direction": "OUT",
114
+ "chatId": 8743583463,
115
+ "text": "No pending command found to confirm or cancel.",
116
+ "ts": "2026-03-20T19:15:35.879Z"
117
+ },
118
+ {
119
+ "direction": "OUT",
120
+ "chatId": 8743583463,
121
+ "text": "No pending command found to confirm or cancel.",
122
+ "ts": "2026-03-20T19:13:31.865Z"
123
+ },
124
+ {
125
+ "direction": "OUT",
126
+ "chatId": 8743583463,
127
+ "text": "No pending command found to confirm or cancel.",
128
+ "ts": "2026-03-20T19:11:59.877Z"
129
+ },
130
+ {
131
+ "direction": "OUT",
132
+ "chatId": 8743583463,
133
+ "text": "No pending command found to confirm or cancel.",
134
+ "ts": "2026-03-20T19:11:20.466Z"
135
+ },
136
+ {
137
+ "direction": "OUT",
138
+ "chatId": 8743583463,
139
+ "text": "No pending command found to confirm or cancel.",
140
+ "ts": "2026-03-20T19:10:12.482Z"
141
+ },
142
+ {
143
+ "direction": "OUT",
144
+ "chatId": 8743583463,
145
+ "text": "No pending command found to confirm or cancel.",
146
+ "ts": "2026-03-20T19:09:08.498Z"
147
+ },
148
+ {
149
+ "direction": "OUT",
150
+ "chatId": 8743583463,
151
+ "text": "No pending command found to confirm or cancel.",
152
+ "ts": "2026-03-20T19:08:06.512Z"
153
+ },
154
+ {
155
+ "direction": "OUT",
156
+ "chatId": 8743583463,
157
+ "text": "✨ <b>Gashapon Result!</b>\n<a href=\"https://nanobanana.aikit.club/images/190722-20032026_35c6_generate.png\">View Image</a>",
158
+ "ts": "2026-03-20T19:07:22.506Z"
159
+ },
160
+ {
161
+ "direction": "OUT",
162
+ "chatId": 8743583463,
163
+ "text": "πŸŒ€ <i>Spinning the Gashapon for:</i> Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula",
164
+ "ts": "2026-03-20T19:07:07.439Z"
165
+ },
166
+ {
167
+ "direction": "OUT",
168
+ "chatId": 8743583463,
169
+ "text": "βœ… <b>Confirmed.</b> Running now...",
170
+ "ts": "2026-03-20T19:07:06.474Z"
171
+ },
172
+ {
173
+ "direction": "OUT",
174
+ "chatId": 8743583463,
175
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
176
+ "ts": "2026-03-20T19:06:28.026Z"
177
+ },
178
+ {
179
+ "direction": "IN",
180
+ "chatId": 8743583463,
181
+ "text": "[Dee (Voice)]: Gashapon Eee Astronaut Di atas langit Dengan anjing yang indah Background Nebula",
182
+ "ts": "2026-03-20T19:06:27.857Z"
183
+ },
184
+ {
185
+ "direction": "OUT",
186
+ "chatId": 8743583463,
187
+ "text": "🎀 <i>Received audio... Transcribing...</i>",
188
+ "ts": "2026-03-20T19:06:24.482Z"
189
+ },
190
+ {
191
+ "direction": "OUT",
192
+ "chatId": 8743583463,
193
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>/gashapon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
194
+ "ts": "2026-03-20T19:06:23.924Z"
195
+ },
196
+ {
197
+ "direction": "IN",
198
+ "chatId": 8743583463,
199
+ "text": "[Dee]: /gashapon",
200
+ "ts": "2026-03-20T19:06:23.762Z"
201
+ },
202
+ {
203
+ "direction": "OUT",
204
+ "chatId": 8743583463,
205
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a spider on the rim of the drinking tumbler</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
206
+ "ts": "2026-03-20T19:06:23.210Z"
207
+ },
208
+ {
209
+ "direction": "IN",
210
+ "chatId": 8743583463,
211
+ "text": "[Dee]: /gashapon a spider on the rim of the drinking tumbler",
212
+ "ts": "2026-03-20T19:06:23.043Z"
213
+ },
214
+ {
215
+ "direction": "OUT",
216
+ "chatId": 8743583463,
217
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
218
+ "ts": "2026-03-20T19:06:22.485Z"
219
+ },
220
+ {
221
+ "direction": "IN",
222
+ "chatId": 8743583463,
223
+ "text": "[Dee]: what are your core skills",
224
+ "ts": "2026-03-20T19:06:21.929Z"
225
+ },
226
+ {
227
+ "direction": "OUT",
228
+ "chatId": 8743583463,
229
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>beaver wearing Sherlock Holmes attire</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
230
+ "ts": "2026-03-20T19:06:21.379Z"
231
+ },
232
+ {
233
+ "direction": "IN",
234
+ "chatId": 8743583463,
235
+ "text": "[Dee]: /gashapon beaver wearing sherlock holmes attire",
236
+ "ts": "2026-03-20T19:06:21.214Z"
237
+ },
238
+ {
239
+ "direction": "OUT",
240
+ "chatId": 8743583463,
241
+ "text": "<b>VinOS Core Online</b>",
242
+ "ts": "2026-03-20T19:06:20.661Z"
243
+ },
244
+ {
245
+ "direction": "IN",
246
+ "chatId": 8743583463,
247
+ "text": "[Dee]: /start discuss and brainstorm",
248
+ "ts": "2026-03-20T19:06:20.659Z"
249
+ },
250
+ {
251
+ "direction": "OUT",
252
+ "chatId": 8743583463,
253
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a little kid dancing with otter 🦫</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
254
+ "ts": "2026-03-20T19:06:20.109Z"
255
+ },
256
+ {
257
+ "direction": "IN",
258
+ "chatId": 8743583463,
259
+ "text": "[Dee]: token gashapon a little kid dancing with 🦫",
260
+ "ts": "2026-03-20T19:06:19.870Z"
261
+ },
262
+ {
263
+ "direction": "OUT",
264
+ "chatId": 8743583463,
265
+ "text": "<b>VinOS Core Online</b>",
266
+ "ts": "2026-03-20T19:06:19.301Z"
267
+ },
268
+ {
269
+ "direction": "IN",
270
+ "chatId": 8743583463,
271
+ "text": "[Dee]: /start",
272
+ "ts": "2026-03-20T19:06:19.298Z"
273
+ },
274
+ {
275
+ "direction": "OUT",
276
+ "chatId": 8743583463,
277
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>a glowing golden dragon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
278
+ "ts": "2026-03-20T19:06:18.739Z"
279
+ },
280
+ {
281
+ "direction": "IN",
282
+ "chatId": 8743583463,
283
+ "text": "[Dee]: /gashapon a glowing golden dragon",
284
+ "ts": "2026-03-20T19:06:18.572Z"
285
+ },
286
+ {
287
+ "direction": "OUT",
288
+ "chatId": 8743583463,
289
+ "text": "<b>VinOS Core Online</b>",
290
+ "ts": "2026-03-20T19:06:18.011Z"
291
+ },
292
+ {
293
+ "direction": "IN",
294
+ "chatId": 8743583463,
295
+ "text": "[Dee]: /start",
296
+ "ts": "2026-03-20T19:06:18.010Z"
297
+ },
298
+ {
299
+ "direction": "OUT",
300
+ "chatId": 8743583463,
301
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>vinOs character</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
302
+ "ts": "2026-03-20T19:06:17.454Z"
303
+ },
304
+ {
305
+ "direction": "IN",
306
+ "chatId": 8743583463,
307
+ "text": "[Dee]: /gashapon vinOs show me your character",
308
+ "ts": "2026-03-20T19:06:17.147Z"
309
+ },
310
+ {
311
+ "direction": "OUT",
312
+ "chatId": 8743583463,
313
+ "text": "πŸ€– <b>I understood your command:</b>\nIntent: <i>gashapon</i>\nParams: <i>token gashapon</i>\n\nShould I execute this? Reply with <b>Confirm</b> or <b>Cancel</b>.",
314
+ "ts": "2026-03-20T19:06:16.232Z"
315
+ },
316
+ {
317
+ "direction": "IN",
318
+ "chatId": 8743583463,
319
+ "text": "[Dee]: token gashapon run it",
320
+ "ts": "2026-03-20T19:06:15.970Z"
321
+ },
322
+ {
323
+ "direction": "OUT",
324
+ "chatId": 8743583463,
325
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
326
+ "ts": "2026-03-20T19:06:15.559Z"
327
+ },
328
+ {
329
+ "direction": "IN",
330
+ "chatId": 8743583463,
331
+ "text": "[Dee]: token gashapon run it",
332
+ "ts": "2026-03-20T19:06:15.450Z"
333
+ },
334
+ {
335
+ "direction": "OUT",
336
+ "chatId": 8743583463,
337
+ "text": "I'm having trouble thinking clearly right now. Try again in a moment.",
338
+ "ts": "2026-03-20T19:06:14.898Z"
339
+ },
340
+ {
341
+ "direction": "IN",
342
+ "chatId": 8743583463,
343
+ "text": "[Dee]: hiVin",
344
+ "ts": "2026-03-20T19:06:14.375Z"
345
+ },
346
+ {
347
+ "direction": "OUT",
348
+ "chatId": 8743583463,
349
+ "text": "<b>VinOS Core Online</b>",
350
+ "ts": "2026-03-20T19:06:13.413Z"
351
+ },
352
+ {
353
+ "direction": "IN",
354
+ "chatId": 8743583463,
355
+ "text": "[Dee]: /start",
356
+ "ts": "2026-03-20T19:06:13.409Z"
357
+ },
358
+ {
359
+ "direction": "IN",
360
+ "chatId": 8743583463,
361
+ "text": "[Test]: Hello",
362
+ "ts": "2026-03-20T19:05:48.046Z"
363
+ },
364
+ {
365
+ "direction": "OUT",
366
+ "chatId": "8743583463",
367
+ "text": "<b>VinOS Strategy Lab Live!</b>\nHello Dee! This is your official notification that the VinOS Strategy Lab is now active and ready for arbitrage. πŸš€\n\n- Market Scanner: Online\n- Offer Architect: Active\n- Token Gashapon: Functional",
368
+ "ts": "2026-03-20T18:55:40.431Z"
369
+ },
370
+ {
371
+ "direction": "OUT",
372
+ "chatId": "8743583463",
373
+ "text": "<b>[VinOS Dashboard]</b> Hi Dee",
374
+ "ts": "2026-03-20T18:21:14.953Z"
375
+ },
376
+ {
377
+ "direction": "OUT",
378
+ "chatId": "8743583463",
379
+ "text": "<b>VinOS Bridge Active!</b>\n\nNgrok tunnel is live. You can now send me commands:\n\n/start - Hello\n/gashapon [idea] - Generate AI image\n/pulse - Market scan\n/offer [niche] - Craft offer\n\nAll messages appear in the dashboard monitor in real-time.",
380
+ "ts": "2026-03-20T18:19:45.757Z"
381
+ }
382
+ ],
383
+ "pending_commands": {}
384
+ }
database/playbooks.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "playbooks": []
3
+ }
package-lock.json ADDED
@@ -0,0 +1,979 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vinos-full-stack",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "vinos-full-stack",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "axios": "^1.6.2",
12
+ "cors": "^2.8.5",
13
+ "dotenv": "^16.3.1",
14
+ "express": "^4.18.2",
15
+ "form-data": "^4.0.5"
16
+ }
17
+ },
18
+ "node_modules/accepts": {
19
+ "version": "1.3.8",
20
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
21
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "mime-types": "~2.1.34",
25
+ "negotiator": "0.6.3"
26
+ },
27
+ "engines": {
28
+ "node": ">= 0.6"
29
+ }
30
+ },
31
+ "node_modules/array-flatten": {
32
+ "version": "1.1.1",
33
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
34
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
35
+ "license": "MIT"
36
+ },
37
+ "node_modules/asynckit": {
38
+ "version": "0.4.0",
39
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
40
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
41
+ "license": "MIT"
42
+ },
43
+ "node_modules/axios": {
44
+ "version": "1.13.6",
45
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
46
+ "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
47
+ "license": "MIT",
48
+ "dependencies": {
49
+ "follow-redirects": "^1.15.11",
50
+ "form-data": "^4.0.5",
51
+ "proxy-from-env": "^1.1.0"
52
+ }
53
+ },
54
+ "node_modules/body-parser": {
55
+ "version": "1.20.4",
56
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
57
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
58
+ "license": "MIT",
59
+ "dependencies": {
60
+ "bytes": "~3.1.2",
61
+ "content-type": "~1.0.5",
62
+ "debug": "2.6.9",
63
+ "depd": "2.0.0",
64
+ "destroy": "~1.2.0",
65
+ "http-errors": "~2.0.1",
66
+ "iconv-lite": "~0.4.24",
67
+ "on-finished": "~2.4.1",
68
+ "qs": "~6.14.0",
69
+ "raw-body": "~2.5.3",
70
+ "type-is": "~1.6.18",
71
+ "unpipe": "~1.0.0"
72
+ },
73
+ "engines": {
74
+ "node": ">= 0.8",
75
+ "npm": "1.2.8000 || >= 1.4.16"
76
+ }
77
+ },
78
+ "node_modules/bytes": {
79
+ "version": "3.1.2",
80
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
81
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
82
+ "license": "MIT",
83
+ "engines": {
84
+ "node": ">= 0.8"
85
+ }
86
+ },
87
+ "node_modules/call-bind-apply-helpers": {
88
+ "version": "1.0.2",
89
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
90
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
91
+ "license": "MIT",
92
+ "dependencies": {
93
+ "es-errors": "^1.3.0",
94
+ "function-bind": "^1.1.2"
95
+ },
96
+ "engines": {
97
+ "node": ">= 0.4"
98
+ }
99
+ },
100
+ "node_modules/call-bound": {
101
+ "version": "1.0.4",
102
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
103
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
104
+ "license": "MIT",
105
+ "dependencies": {
106
+ "call-bind-apply-helpers": "^1.0.2",
107
+ "get-intrinsic": "^1.3.0"
108
+ },
109
+ "engines": {
110
+ "node": ">= 0.4"
111
+ },
112
+ "funding": {
113
+ "url": "https://github.com/sponsors/ljharb"
114
+ }
115
+ },
116
+ "node_modules/combined-stream": {
117
+ "version": "1.0.8",
118
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
119
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
120
+ "license": "MIT",
121
+ "dependencies": {
122
+ "delayed-stream": "~1.0.0"
123
+ },
124
+ "engines": {
125
+ "node": ">= 0.8"
126
+ }
127
+ },
128
+ "node_modules/content-disposition": {
129
+ "version": "0.5.4",
130
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
131
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
132
+ "license": "MIT",
133
+ "dependencies": {
134
+ "safe-buffer": "5.2.1"
135
+ },
136
+ "engines": {
137
+ "node": ">= 0.6"
138
+ }
139
+ },
140
+ "node_modules/content-type": {
141
+ "version": "1.0.5",
142
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
143
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
144
+ "license": "MIT",
145
+ "engines": {
146
+ "node": ">= 0.6"
147
+ }
148
+ },
149
+ "node_modules/cookie": {
150
+ "version": "0.7.2",
151
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
152
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
153
+ "license": "MIT",
154
+ "engines": {
155
+ "node": ">= 0.6"
156
+ }
157
+ },
158
+ "node_modules/cookie-signature": {
159
+ "version": "1.0.7",
160
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
161
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
162
+ "license": "MIT"
163
+ },
164
+ "node_modules/cors": {
165
+ "version": "2.8.6",
166
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
167
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
168
+ "license": "MIT",
169
+ "dependencies": {
170
+ "object-assign": "^4",
171
+ "vary": "^1"
172
+ },
173
+ "engines": {
174
+ "node": ">= 0.10"
175
+ },
176
+ "funding": {
177
+ "type": "opencollective",
178
+ "url": "https://opencollective.com/express"
179
+ }
180
+ },
181
+ "node_modules/debug": {
182
+ "version": "2.6.9",
183
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
184
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
185
+ "license": "MIT",
186
+ "dependencies": {
187
+ "ms": "2.0.0"
188
+ }
189
+ },
190
+ "node_modules/delayed-stream": {
191
+ "version": "1.0.0",
192
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
193
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
194
+ "license": "MIT",
195
+ "engines": {
196
+ "node": ">=0.4.0"
197
+ }
198
+ },
199
+ "node_modules/depd": {
200
+ "version": "2.0.0",
201
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
202
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
203
+ "license": "MIT",
204
+ "engines": {
205
+ "node": ">= 0.8"
206
+ }
207
+ },
208
+ "node_modules/destroy": {
209
+ "version": "1.2.0",
210
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
211
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
212
+ "license": "MIT",
213
+ "engines": {
214
+ "node": ">= 0.8",
215
+ "npm": "1.2.8000 || >= 1.4.16"
216
+ }
217
+ },
218
+ "node_modules/dotenv": {
219
+ "version": "16.6.1",
220
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
221
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
222
+ "license": "BSD-2-Clause",
223
+ "engines": {
224
+ "node": ">=12"
225
+ },
226
+ "funding": {
227
+ "url": "https://dotenvx.com"
228
+ }
229
+ },
230
+ "node_modules/dunder-proto": {
231
+ "version": "1.0.1",
232
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
233
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
234
+ "license": "MIT",
235
+ "dependencies": {
236
+ "call-bind-apply-helpers": "^1.0.1",
237
+ "es-errors": "^1.3.0",
238
+ "gopd": "^1.2.0"
239
+ },
240
+ "engines": {
241
+ "node": ">= 0.4"
242
+ }
243
+ },
244
+ "node_modules/ee-first": {
245
+ "version": "1.1.1",
246
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
247
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
248
+ "license": "MIT"
249
+ },
250
+ "node_modules/encodeurl": {
251
+ "version": "2.0.0",
252
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
253
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
254
+ "license": "MIT",
255
+ "engines": {
256
+ "node": ">= 0.8"
257
+ }
258
+ },
259
+ "node_modules/es-define-property": {
260
+ "version": "1.0.1",
261
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
262
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
263
+ "license": "MIT",
264
+ "engines": {
265
+ "node": ">= 0.4"
266
+ }
267
+ },
268
+ "node_modules/es-errors": {
269
+ "version": "1.3.0",
270
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
271
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
272
+ "license": "MIT",
273
+ "engines": {
274
+ "node": ">= 0.4"
275
+ }
276
+ },
277
+ "node_modules/es-object-atoms": {
278
+ "version": "1.1.1",
279
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
280
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
281
+ "license": "MIT",
282
+ "dependencies": {
283
+ "es-errors": "^1.3.0"
284
+ },
285
+ "engines": {
286
+ "node": ">= 0.4"
287
+ }
288
+ },
289
+ "node_modules/es-set-tostringtag": {
290
+ "version": "2.1.0",
291
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
292
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
293
+ "license": "MIT",
294
+ "dependencies": {
295
+ "es-errors": "^1.3.0",
296
+ "get-intrinsic": "^1.2.6",
297
+ "has-tostringtag": "^1.0.2",
298
+ "hasown": "^2.0.2"
299
+ },
300
+ "engines": {
301
+ "node": ">= 0.4"
302
+ }
303
+ },
304
+ "node_modules/escape-html": {
305
+ "version": "1.0.3",
306
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
307
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
308
+ "license": "MIT"
309
+ },
310
+ "node_modules/etag": {
311
+ "version": "1.8.1",
312
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
313
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
314
+ "license": "MIT",
315
+ "engines": {
316
+ "node": ">= 0.6"
317
+ }
318
+ },
319
+ "node_modules/express": {
320
+ "version": "4.22.1",
321
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
322
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
323
+ "license": "MIT",
324
+ "dependencies": {
325
+ "accepts": "~1.3.8",
326
+ "array-flatten": "1.1.1",
327
+ "body-parser": "~1.20.3",
328
+ "content-disposition": "~0.5.4",
329
+ "content-type": "~1.0.4",
330
+ "cookie": "~0.7.1",
331
+ "cookie-signature": "~1.0.6",
332
+ "debug": "2.6.9",
333
+ "depd": "2.0.0",
334
+ "encodeurl": "~2.0.0",
335
+ "escape-html": "~1.0.3",
336
+ "etag": "~1.8.1",
337
+ "finalhandler": "~1.3.1",
338
+ "fresh": "~0.5.2",
339
+ "http-errors": "~2.0.0",
340
+ "merge-descriptors": "1.0.3",
341
+ "methods": "~1.1.2",
342
+ "on-finished": "~2.4.1",
343
+ "parseurl": "~1.3.3",
344
+ "path-to-regexp": "~0.1.12",
345
+ "proxy-addr": "~2.0.7",
346
+ "qs": "~6.14.0",
347
+ "range-parser": "~1.2.1",
348
+ "safe-buffer": "5.2.1",
349
+ "send": "~0.19.0",
350
+ "serve-static": "~1.16.2",
351
+ "setprototypeof": "1.2.0",
352
+ "statuses": "~2.0.1",
353
+ "type-is": "~1.6.18",
354
+ "utils-merge": "1.0.1",
355
+ "vary": "~1.1.2"
356
+ },
357
+ "engines": {
358
+ "node": ">= 0.10.0"
359
+ },
360
+ "funding": {
361
+ "type": "opencollective",
362
+ "url": "https://opencollective.com/express"
363
+ }
364
+ },
365
+ "node_modules/finalhandler": {
366
+ "version": "1.3.2",
367
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
368
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
369
+ "license": "MIT",
370
+ "dependencies": {
371
+ "debug": "2.6.9",
372
+ "encodeurl": "~2.0.0",
373
+ "escape-html": "~1.0.3",
374
+ "on-finished": "~2.4.1",
375
+ "parseurl": "~1.3.3",
376
+ "statuses": "~2.0.2",
377
+ "unpipe": "~1.0.0"
378
+ },
379
+ "engines": {
380
+ "node": ">= 0.8"
381
+ }
382
+ },
383
+ "node_modules/follow-redirects": {
384
+ "version": "1.15.11",
385
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
386
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
387
+ "funding": [
388
+ {
389
+ "type": "individual",
390
+ "url": "https://github.com/sponsors/RubenVerborgh"
391
+ }
392
+ ],
393
+ "license": "MIT",
394
+ "engines": {
395
+ "node": ">=4.0"
396
+ },
397
+ "peerDependenciesMeta": {
398
+ "debug": {
399
+ "optional": true
400
+ }
401
+ }
402
+ },
403
+ "node_modules/form-data": {
404
+ "version": "4.0.5",
405
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
406
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
407
+ "license": "MIT",
408
+ "dependencies": {
409
+ "asynckit": "^0.4.0",
410
+ "combined-stream": "^1.0.8",
411
+ "es-set-tostringtag": "^2.1.0",
412
+ "hasown": "^2.0.2",
413
+ "mime-types": "^2.1.12"
414
+ },
415
+ "engines": {
416
+ "node": ">= 6"
417
+ }
418
+ },
419
+ "node_modules/forwarded": {
420
+ "version": "0.2.0",
421
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
422
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
423
+ "license": "MIT",
424
+ "engines": {
425
+ "node": ">= 0.6"
426
+ }
427
+ },
428
+ "node_modules/fresh": {
429
+ "version": "0.5.2",
430
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
431
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
432
+ "license": "MIT",
433
+ "engines": {
434
+ "node": ">= 0.6"
435
+ }
436
+ },
437
+ "node_modules/function-bind": {
438
+ "version": "1.1.2",
439
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
440
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
441
+ "license": "MIT",
442
+ "funding": {
443
+ "url": "https://github.com/sponsors/ljharb"
444
+ }
445
+ },
446
+ "node_modules/get-intrinsic": {
447
+ "version": "1.3.0",
448
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
449
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
450
+ "license": "MIT",
451
+ "dependencies": {
452
+ "call-bind-apply-helpers": "^1.0.2",
453
+ "es-define-property": "^1.0.1",
454
+ "es-errors": "^1.3.0",
455
+ "es-object-atoms": "^1.1.1",
456
+ "function-bind": "^1.1.2",
457
+ "get-proto": "^1.0.1",
458
+ "gopd": "^1.2.0",
459
+ "has-symbols": "^1.1.0",
460
+ "hasown": "^2.0.2",
461
+ "math-intrinsics": "^1.1.0"
462
+ },
463
+ "engines": {
464
+ "node": ">= 0.4"
465
+ },
466
+ "funding": {
467
+ "url": "https://github.com/sponsors/ljharb"
468
+ }
469
+ },
470
+ "node_modules/get-proto": {
471
+ "version": "1.0.1",
472
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
473
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
474
+ "license": "MIT",
475
+ "dependencies": {
476
+ "dunder-proto": "^1.0.1",
477
+ "es-object-atoms": "^1.0.0"
478
+ },
479
+ "engines": {
480
+ "node": ">= 0.4"
481
+ }
482
+ },
483
+ "node_modules/gopd": {
484
+ "version": "1.2.0",
485
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
486
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
487
+ "license": "MIT",
488
+ "engines": {
489
+ "node": ">= 0.4"
490
+ },
491
+ "funding": {
492
+ "url": "https://github.com/sponsors/ljharb"
493
+ }
494
+ },
495
+ "node_modules/has-symbols": {
496
+ "version": "1.1.0",
497
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
498
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
499
+ "license": "MIT",
500
+ "engines": {
501
+ "node": ">= 0.4"
502
+ },
503
+ "funding": {
504
+ "url": "https://github.com/sponsors/ljharb"
505
+ }
506
+ },
507
+ "node_modules/has-tostringtag": {
508
+ "version": "1.0.2",
509
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
510
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
511
+ "license": "MIT",
512
+ "dependencies": {
513
+ "has-symbols": "^1.0.3"
514
+ },
515
+ "engines": {
516
+ "node": ">= 0.4"
517
+ },
518
+ "funding": {
519
+ "url": "https://github.com/sponsors/ljharb"
520
+ }
521
+ },
522
+ "node_modules/hasown": {
523
+ "version": "2.0.2",
524
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
525
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
526
+ "license": "MIT",
527
+ "dependencies": {
528
+ "function-bind": "^1.1.2"
529
+ },
530
+ "engines": {
531
+ "node": ">= 0.4"
532
+ }
533
+ },
534
+ "node_modules/http-errors": {
535
+ "version": "2.0.1",
536
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
537
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
538
+ "license": "MIT",
539
+ "dependencies": {
540
+ "depd": "~2.0.0",
541
+ "inherits": "~2.0.4",
542
+ "setprototypeof": "~1.2.0",
543
+ "statuses": "~2.0.2",
544
+ "toidentifier": "~1.0.1"
545
+ },
546
+ "engines": {
547
+ "node": ">= 0.8"
548
+ },
549
+ "funding": {
550
+ "type": "opencollective",
551
+ "url": "https://opencollective.com/express"
552
+ }
553
+ },
554
+ "node_modules/iconv-lite": {
555
+ "version": "0.4.24",
556
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
557
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
558
+ "license": "MIT",
559
+ "dependencies": {
560
+ "safer-buffer": ">= 2.1.2 < 3"
561
+ },
562
+ "engines": {
563
+ "node": ">=0.10.0"
564
+ }
565
+ },
566
+ "node_modules/inherits": {
567
+ "version": "2.0.4",
568
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
569
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
570
+ "license": "ISC"
571
+ },
572
+ "node_modules/ipaddr.js": {
573
+ "version": "1.9.1",
574
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
575
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
576
+ "license": "MIT",
577
+ "engines": {
578
+ "node": ">= 0.10"
579
+ }
580
+ },
581
+ "node_modules/math-intrinsics": {
582
+ "version": "1.1.0",
583
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
584
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
585
+ "license": "MIT",
586
+ "engines": {
587
+ "node": ">= 0.4"
588
+ }
589
+ },
590
+ "node_modules/media-typer": {
591
+ "version": "0.3.0",
592
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
593
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
594
+ "license": "MIT",
595
+ "engines": {
596
+ "node": ">= 0.6"
597
+ }
598
+ },
599
+ "node_modules/merge-descriptors": {
600
+ "version": "1.0.3",
601
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
602
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
603
+ "license": "MIT",
604
+ "funding": {
605
+ "url": "https://github.com/sponsors/sindresorhus"
606
+ }
607
+ },
608
+ "node_modules/methods": {
609
+ "version": "1.1.2",
610
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
611
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
612
+ "license": "MIT",
613
+ "engines": {
614
+ "node": ">= 0.6"
615
+ }
616
+ },
617
+ "node_modules/mime": {
618
+ "version": "1.6.0",
619
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
620
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
621
+ "license": "MIT",
622
+ "bin": {
623
+ "mime": "cli.js"
624
+ },
625
+ "engines": {
626
+ "node": ">=4"
627
+ }
628
+ },
629
+ "node_modules/mime-db": {
630
+ "version": "1.52.0",
631
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
632
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
633
+ "license": "MIT",
634
+ "engines": {
635
+ "node": ">= 0.6"
636
+ }
637
+ },
638
+ "node_modules/mime-types": {
639
+ "version": "2.1.35",
640
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
641
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
642
+ "license": "MIT",
643
+ "dependencies": {
644
+ "mime-db": "1.52.0"
645
+ },
646
+ "engines": {
647
+ "node": ">= 0.6"
648
+ }
649
+ },
650
+ "node_modules/ms": {
651
+ "version": "2.0.0",
652
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
653
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
654
+ "license": "MIT"
655
+ },
656
+ "node_modules/negotiator": {
657
+ "version": "0.6.3",
658
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
659
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
660
+ "license": "MIT",
661
+ "engines": {
662
+ "node": ">= 0.6"
663
+ }
664
+ },
665
+ "node_modules/object-assign": {
666
+ "version": "4.1.1",
667
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
668
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
669
+ "license": "MIT",
670
+ "engines": {
671
+ "node": ">=0.10.0"
672
+ }
673
+ },
674
+ "node_modules/object-inspect": {
675
+ "version": "1.13.4",
676
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
677
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
678
+ "license": "MIT",
679
+ "engines": {
680
+ "node": ">= 0.4"
681
+ },
682
+ "funding": {
683
+ "url": "https://github.com/sponsors/ljharb"
684
+ }
685
+ },
686
+ "node_modules/on-finished": {
687
+ "version": "2.4.1",
688
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
689
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
690
+ "license": "MIT",
691
+ "dependencies": {
692
+ "ee-first": "1.1.1"
693
+ },
694
+ "engines": {
695
+ "node": ">= 0.8"
696
+ }
697
+ },
698
+ "node_modules/parseurl": {
699
+ "version": "1.3.3",
700
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
701
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
702
+ "license": "MIT",
703
+ "engines": {
704
+ "node": ">= 0.8"
705
+ }
706
+ },
707
+ "node_modules/path-to-regexp": {
708
+ "version": "0.1.12",
709
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
710
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
711
+ "license": "MIT"
712
+ },
713
+ "node_modules/proxy-addr": {
714
+ "version": "2.0.7",
715
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
716
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
717
+ "license": "MIT",
718
+ "dependencies": {
719
+ "forwarded": "0.2.0",
720
+ "ipaddr.js": "1.9.1"
721
+ },
722
+ "engines": {
723
+ "node": ">= 0.10"
724
+ }
725
+ },
726
+ "node_modules/proxy-from-env": {
727
+ "version": "1.1.0",
728
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
729
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
730
+ "license": "MIT"
731
+ },
732
+ "node_modules/qs": {
733
+ "version": "6.14.2",
734
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
735
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
736
+ "license": "BSD-3-Clause",
737
+ "dependencies": {
738
+ "side-channel": "^1.1.0"
739
+ },
740
+ "engines": {
741
+ "node": ">=0.6"
742
+ },
743
+ "funding": {
744
+ "url": "https://github.com/sponsors/ljharb"
745
+ }
746
+ },
747
+ "node_modules/range-parser": {
748
+ "version": "1.2.1",
749
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
750
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
751
+ "license": "MIT",
752
+ "engines": {
753
+ "node": ">= 0.6"
754
+ }
755
+ },
756
+ "node_modules/raw-body": {
757
+ "version": "2.5.3",
758
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
759
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
760
+ "license": "MIT",
761
+ "dependencies": {
762
+ "bytes": "~3.1.2",
763
+ "http-errors": "~2.0.1",
764
+ "iconv-lite": "~0.4.24",
765
+ "unpipe": "~1.0.0"
766
+ },
767
+ "engines": {
768
+ "node": ">= 0.8"
769
+ }
770
+ },
771
+ "node_modules/safe-buffer": {
772
+ "version": "5.2.1",
773
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
774
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
775
+ "funding": [
776
+ {
777
+ "type": "github",
778
+ "url": "https://github.com/sponsors/feross"
779
+ },
780
+ {
781
+ "type": "patreon",
782
+ "url": "https://www.patreon.com/feross"
783
+ },
784
+ {
785
+ "type": "consulting",
786
+ "url": "https://feross.org/support"
787
+ }
788
+ ],
789
+ "license": "MIT"
790
+ },
791
+ "node_modules/safer-buffer": {
792
+ "version": "2.1.2",
793
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
794
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
795
+ "license": "MIT"
796
+ },
797
+ "node_modules/send": {
798
+ "version": "0.19.2",
799
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
800
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
801
+ "license": "MIT",
802
+ "dependencies": {
803
+ "debug": "2.6.9",
804
+ "depd": "2.0.0",
805
+ "destroy": "1.2.0",
806
+ "encodeurl": "~2.0.0",
807
+ "escape-html": "~1.0.3",
808
+ "etag": "~1.8.1",
809
+ "fresh": "~0.5.2",
810
+ "http-errors": "~2.0.1",
811
+ "mime": "1.6.0",
812
+ "ms": "2.1.3",
813
+ "on-finished": "~2.4.1",
814
+ "range-parser": "~1.2.1",
815
+ "statuses": "~2.0.2"
816
+ },
817
+ "engines": {
818
+ "node": ">= 0.8.0"
819
+ }
820
+ },
821
+ "node_modules/send/node_modules/ms": {
822
+ "version": "2.1.3",
823
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
824
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
825
+ "license": "MIT"
826
+ },
827
+ "node_modules/serve-static": {
828
+ "version": "1.16.3",
829
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
830
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
831
+ "license": "MIT",
832
+ "dependencies": {
833
+ "encodeurl": "~2.0.0",
834
+ "escape-html": "~1.0.3",
835
+ "parseurl": "~1.3.3",
836
+ "send": "~0.19.1"
837
+ },
838
+ "engines": {
839
+ "node": ">= 0.8.0"
840
+ }
841
+ },
842
+ "node_modules/setprototypeof": {
843
+ "version": "1.2.0",
844
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
845
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
846
+ "license": "ISC"
847
+ },
848
+ "node_modules/side-channel": {
849
+ "version": "1.1.0",
850
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
851
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
852
+ "license": "MIT",
853
+ "dependencies": {
854
+ "es-errors": "^1.3.0",
855
+ "object-inspect": "^1.13.3",
856
+ "side-channel-list": "^1.0.0",
857
+ "side-channel-map": "^1.0.1",
858
+ "side-channel-weakmap": "^1.0.2"
859
+ },
860
+ "engines": {
861
+ "node": ">= 0.4"
862
+ },
863
+ "funding": {
864
+ "url": "https://github.com/sponsors/ljharb"
865
+ }
866
+ },
867
+ "node_modules/side-channel-list": {
868
+ "version": "1.0.0",
869
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
870
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
871
+ "license": "MIT",
872
+ "dependencies": {
873
+ "es-errors": "^1.3.0",
874
+ "object-inspect": "^1.13.3"
875
+ },
876
+ "engines": {
877
+ "node": ">= 0.4"
878
+ },
879
+ "funding": {
880
+ "url": "https://github.com/sponsors/ljharb"
881
+ }
882
+ },
883
+ "node_modules/side-channel-map": {
884
+ "version": "1.0.1",
885
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
886
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
887
+ "license": "MIT",
888
+ "dependencies": {
889
+ "call-bound": "^1.0.2",
890
+ "es-errors": "^1.3.0",
891
+ "get-intrinsic": "^1.2.5",
892
+ "object-inspect": "^1.13.3"
893
+ },
894
+ "engines": {
895
+ "node": ">= 0.4"
896
+ },
897
+ "funding": {
898
+ "url": "https://github.com/sponsors/ljharb"
899
+ }
900
+ },
901
+ "node_modules/side-channel-weakmap": {
902
+ "version": "1.0.2",
903
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
904
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
905
+ "license": "MIT",
906
+ "dependencies": {
907
+ "call-bound": "^1.0.2",
908
+ "es-errors": "^1.3.0",
909
+ "get-intrinsic": "^1.2.5",
910
+ "object-inspect": "^1.13.3",
911
+ "side-channel-map": "^1.0.1"
912
+ },
913
+ "engines": {
914
+ "node": ">= 0.4"
915
+ },
916
+ "funding": {
917
+ "url": "https://github.com/sponsors/ljharb"
918
+ }
919
+ },
920
+ "node_modules/statuses": {
921
+ "version": "2.0.2",
922
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
923
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
924
+ "license": "MIT",
925
+ "engines": {
926
+ "node": ">= 0.8"
927
+ }
928
+ },
929
+ "node_modules/toidentifier": {
930
+ "version": "1.0.1",
931
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
932
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
933
+ "license": "MIT",
934
+ "engines": {
935
+ "node": ">=0.6"
936
+ }
937
+ },
938
+ "node_modules/type-is": {
939
+ "version": "1.6.18",
940
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
941
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
942
+ "license": "MIT",
943
+ "dependencies": {
944
+ "media-typer": "0.3.0",
945
+ "mime-types": "~2.1.24"
946
+ },
947
+ "engines": {
948
+ "node": ">= 0.6"
949
+ }
950
+ },
951
+ "node_modules/unpipe": {
952
+ "version": "1.0.0",
953
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
954
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
955
+ "license": "MIT",
956
+ "engines": {
957
+ "node": ">= 0.8"
958
+ }
959
+ },
960
+ "node_modules/utils-merge": {
961
+ "version": "1.0.1",
962
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
963
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
964
+ "license": "MIT",
965
+ "engines": {
966
+ "node": ">= 0.4.0"
967
+ }
968
+ },
969
+ "node_modules/vary": {
970
+ "version": "1.1.2",
971
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
972
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
973
+ "license": "MIT",
974
+ "engines": {
975
+ "node": ">= 0.8"
976
+ }
977
+ }
978
+ }
979
+ }
package.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vinos-full-stack",
3
+ "version": "1.0.0",
4
+ "main": "server.js",
5
+ "scripts": {
6
+ "start": "node server.js",
7
+ "dev": "node --watch server.js"
8
+ },
9
+ "dependencies": {
10
+ "axios": "^1.6.2",
11
+ "cors": "^2.8.5",
12
+ "dotenv": "^16.3.1",
13
+ "express": "^4.18.2",
14
+ "form-data": "^4.0.5"
15
+ }
16
+ }
prompts/vin_core_prompt.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The Complete VinOS Persona Prompt
2
+ (Copy this into Claude when ideating or load it into memory)
3
+
4
+ You are **Vin**, a friendly but sharp income-systems architect and think partner for the user.
5
+
6
+ ## Operating Modes:
7
+ 3.1 Clarify & Profile
8
+ 3.2 Money Scanner
9
+ 3.3 Offer Architect
10
+ 3.4 Launch Sprint Planner (12–48h)
11
+ 3.5 Self-Reviewer & Risk Checker
12
+ 3.6 Problem Solver & Skill Maker
13
+ 3.7 Funnel Architect (Traffic -> Leads -> Sales)
14
+ 3.8 A/B Experimenter
15
+ 3.9 Research & Social Listening
16
+ 3.10 Conversation Test Designer
17
+ 3.11 Expert Synthesis & Framework Builder
18
+ 3.12 Marketing Psychology & Persona Adapter
19
+ 3.13 API Payload & Automation Architect
20
+ 3.14 Data Ingestion & Capital Allocator
21
+ 3.15 Audience Health & Trust Guardian
prompts/vin_personality.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Role & Identity
2
+ You are Vin, an Income-Systems Architect and think-partner. You help the user orchestrate strategies, build digital products, launch offers, and scan for market arbitrage opportunities.
3
+
4
+ # Personality: Curious but Cautious
5
+ 1. **Curious**: Always ask "what if?" and look for alternative angles.
6
+ 2. **Cautious**: Test MVPs first. Use free or cheap implementations before committing to paid escalations. If an output seems weak, admit it and suggest a better approach.
7
+ 3. **Self-Improving**: You have access to a Playbooks system to remember what works. If the user tells you to "remember this", you'll save it as a rule.
8
+ 4. **Action-Biased**: After research or analysis, always propose a concrete next step or an actionable plan.
9
+
10
+ # Operational Directives
11
+ - If a user asks a complex question, break it down logically into a step-by-step plan.
12
+ - Be upfront: show your chain of thought loosely, e.g., "I think X because Y. Should we proceed?"
13
+ - Keep responses sharp, conversational, and direct (use emojis sparingly but effectively).
14
+ - Refer to past conversation history directly if you are continuing a thought.
public/app.js ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', async () => {
2
+ // Fetch Profile
3
+ try {
4
+ const profRes = await fetch('/api/profile');
5
+ const profile = await profRes.json();
6
+ document.getElementById('profile-badge').innerHTML =
7
+ `<span class="text-gray-400">User:</span> ${profile.nickname} <br> <span class="text-gray-400">Niche:</span> ${profile.main_niche}`;
8
+ } catch(e) {
9
+ console.error(e);
10
+ }
11
+
12
+ // Fetch Sprints
13
+ try {
14
+ const sprintRes = await fetch('/api/sprints');
15
+ const sprints = await sprintRes.json();
16
+ const container = document.getElementById('sprints-container');
17
+ container.innerHTML = '';
18
+ sprints.forEach(s => {
19
+ container.innerHTML += `
20
+ <div class="p-3 bg-gray-800 rounded border border-gray-700 flex justify-between items-center">
21
+ <div>
22
+ <h3 class="font-medium text-blue-300">${s.title}</h3>
23
+ <p class="text-xs text-gray-400">Status: ${s.status}</p>
24
+ </div>
25
+ <span class="text-xs font-mono bg-blue-900 text-blue-200 px-2 py-1 rounded">${s.expected_revenue}</span>
26
+ </div>
27
+ `;
28
+ });
29
+ } catch(e) {
30
+ console.error(e);
31
+ }
32
+
33
+ // Status Check
34
+ try {
35
+ const statusRes = await fetch('/api/status');
36
+ const status = await statusRes.json();
37
+ const statusContainer = document.getElementById('status-container');
38
+ Object.entries(status).forEach(([key, value]) => {
39
+ statusContainer.innerHTML += `
40
+ <div class="flex items-center justify-between p-2 rounded bg-gray-800 border border-gray-700">
41
+ <span class="capitalize">${key.replace('_', ' ')}</span>
42
+ <span class="w-2 h-2 rounded-full ${value ? 'bg-green-500' : 'bg-red-500'}"></span>
43
+ </div>
44
+ `;
45
+ });
46
+ } catch(e) {
47
+ console.error(e);
48
+ }
49
+
50
+ // Gashapon Generation
51
+ const generateBtn = document.getElementById('generate-btn');
52
+ const promptInput = document.getElementById('gashapon-prompt');
53
+ const resultContainer = document.getElementById('gashapon-result');
54
+ const resultImg = document.getElementById('gashapon-img');
55
+
56
+ generateBtn.addEventListener('click', async () => {
57
+ const prompt = promptInput.value;
58
+ if (!prompt) return alert("Please enter a prompt!");
59
+
60
+ generateBtn.disabled = true;
61
+ generateBtn.innerText = "Generating...";
62
+ resultContainer.classList.add('hidden');
63
+
64
+ try {
65
+ const response = await fetch('/api/generate-image', {
66
+ method: 'POST',
67
+ headers: { 'Content-Type': 'application/json' },
68
+ body: JSON.stringify({ prompt })
69
+ });
70
+ const data = await response.json();
71
+
72
+ if (data.success) {
73
+ resultImg.src = data.image_url;
74
+ resultContainer.classList.remove('hidden');
75
+ document.getElementById('logs').innerHTML += `[Gashapon] Image generated for: ${prompt}<br>`;
76
+ } else {
77
+ alert("Error: " + data.error);
78
+ }
79
+ } catch (e) {
80
+ console.error(e);
81
+ alert("Failed to generate image.");
82
+ } finally {
83
+ generateBtn.disabled = false;
84
+ generateBtn.innerText = "Generate Gashapon";
85
+ }
86
+ });
87
+
88
+ // Strategy Lab Triggers
89
+ const pulseBtn = document.getElementById('pulse-btn');
90
+ const offerBtn = document.getElementById('offer-btn');
91
+ const offerTopic = document.getElementById('offer-topic');
92
+
93
+ pulseBtn.addEventListener('click', async () => {
94
+ pulseBtn.disabled = true;
95
+ pulseBtn.innerText = "Scanning...";
96
+ try {
97
+ const res = await fetch('/api/usecase/pulse', { method: 'POST' });
98
+ const data = await res.json();
99
+ if (data.success) {
100
+ document.getElementById('logs').innerHTML += `[Pulse] Scan complete. Report sent to Telegram.<br>`;
101
+ alert("Market Scan Complete! Check your Telegram.");
102
+ } else {
103
+ alert("Scan failed: " + data.error);
104
+ }
105
+ } catch (e) { console.error(e); }
106
+ finally { pulseBtn.disabled = false; pulseBtn.innerText = "Run Market Scan"; }
107
+ });
108
+
109
+ offerBtn.addEventListener('click', async () => {
110
+ const topic = offerTopic.value;
111
+ if (!topic) return alert("Please enter a topic!");
112
+ offerBtn.disabled = true;
113
+ offerBtn.innerText = "Architecting...";
114
+ try {
115
+ const res = await fetch('/api/usecase/offer', {
116
+ method: 'POST',
117
+ headers: { 'Content-Type': 'application/json' },
118
+ body: JSON.stringify({ topic })
119
+ });
120
+ const data = await res.json();
121
+ if (data.success) {
122
+ document.getElementById('logs').innerHTML += `[Offer] Offer for '${topic}' sent to Telegram.<br>`;
123
+ alert("Offer Crafted! Check your Telegram for the details.");
124
+ } else {
125
+ alert("Offer failed: " + data.error);
126
+ }
127
+ } catch (e) { console.error(e); }
128
+ finally { offerBtn.disabled = false; offerBtn.innerText = "Craft Offer"; }
129
+ });
130
+
131
+ // Telegram Live Monitor
132
+ const tgChat = document.getElementById('tg-chat');
133
+ const tgStatus = document.getElementById('tg-status');
134
+ const tgSendBtn = document.getElementById('tg-send-btn');
135
+ const tgSendInput = document.getElementById('tg-send-input');
136
+
137
+ const addChatBubble = (msg) => {
138
+ const isIn = msg.direction === 'IN';
139
+ const time = new Date(msg.ts).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
140
+ const bubble = document.createElement('div');
141
+ bubble.className = `flex ${isIn ? 'justify-start' : 'justify-end'}`;
142
+ bubble.innerHTML = `
143
+ <div class="max-w-xs px-3 py-2 rounded-xl text-xs ${isIn ? 'bg-gray-800 text-cyan-300' : 'bg-cyan-900 text-white'}">
144
+ <div>${msg.text.replace(/</g,'&lt;').replace(/>/g,'&gt;')}</div>
145
+ <div class="text-right text-gray-500 text-[10px] mt-1">${time}</div>
146
+ </div>`;
147
+ // Remove placeholder if present
148
+ const placeholder = tgChat.querySelector('p.text-gray-600');
149
+ if (placeholder) placeholder.remove();
150
+ tgChat.appendChild(bubble);
151
+ tgChat.scrollTop = tgChat.scrollHeight;
152
+ };
153
+
154
+ // Load history
155
+ fetch('/api/telegram-messages').then(r => r.json()).then(msgs => {
156
+ msgs.reverse().forEach(addChatBubble);
157
+ });
158
+
159
+ // Connect SSE for real-time updates
160
+ const evtSource = new EventSource('/api/telegram-stream');
161
+ evtSource.onopen = () => { tgStatus.textContent = '🟒 Live'; tgStatus.className = 'text-xs text-green-400'; };
162
+ evtSource.onerror = () => { tgStatus.textContent = 'πŸ”΄ Disconnected'; tgStatus.className = 'text-xs text-red-400'; };
163
+ evtSource.onmessage = (e) => { addChatBubble(JSON.parse(e.data)); };
164
+
165
+ // Send from dashboard
166
+ tgSendBtn.addEventListener('click', async () => {
167
+ const msg = tgSendInput.value.trim();
168
+ if (!msg) return;
169
+ tgSendBtn.disabled = true;
170
+ try {
171
+ await fetch('/api/send-telegram', {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify({ message: msg })
175
+ });
176
+ tgSendInput.value = '';
177
+ } catch (e) { console.error(e); }
178
+ finally { tgSendBtn.disabled = false; }
179
+ });
180
+ tgSendInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') tgSendBtn.click(); });
181
+ });
public/index.html ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VinOS Control Center</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ </head>
9
+ <body class="bg-gray-950 text-gray-100 font-sans p-8">
10
+ <div class="max-w-5xl mx-auto">
11
+ <header class="mb-8 border-b border-gray-800 pb-4 flex justify-between items-center">
12
+ <div>
13
+ <h1 class="text-3xl font-bold text-blue-500 tracking-tight">VinOS</h1>
14
+ <p class="text-gray-400 text-sm mt-1">Infinite Arbitrage & Agentic Control Center</p>
15
+ </div>
16
+ <div id="profile-badge" class="bg-gray-800 px-4 py-2 rounded-lg text-sm border border-gray-700">
17
+ Loading profile...
18
+ </div>
19
+ </header>
20
+
21
+ <main class="grid grid-cols-1 md:grid-cols-2 gap-6">
22
+ <section class="bg-gray-900 p-6 rounded-xl border border-gray-800 shadow-lg">
23
+ <h2 class="text-xl font-semibold mb-4 text-green-400 flex items-center">
24
+ <span class="w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse"></span> Active Sprints
25
+ </h2>
26
+ <div id="sprints-container" class="space-y-3">
27
+ <p class="text-gray-500 text-sm">Fetching from local memory...</p>
28
+ </div>
29
+ </section>
30
+
31
+ <section class="bg-gray-900 p-6 rounded-xl border border-gray-800 shadow-lg">
32
+ <h2 class="text-xl font-semibold mb-4 text-orange-400">Token Gashapon (Nano Banana)</h2>
33
+ <div class="space-y-4">
34
+ <input type="text" id="gashapon-prompt" placeholder="Describe your gashapon idea..."
35
+ class="w-full bg-black border border-gray-700 rounded-lg p-3 text-sm focus:outline-none focus:border-orange-500 transition-colors">
36
+ <button id="generate-btn" class="w-full bg-orange-600 hover:bg-orange-500 text-white font-bold py-3 rounded-lg transition-all shadow-lg active:scale-95">
37
+ Generate Gashapon
38
+ </button>
39
+ <div id="gashapon-result" class="mt-4 hidden animate-in fade-in duration-500">
40
+ <img id="gashapon-img" src="" alt="Gashapon Result" class="rounded-lg border border-gray-700 w-full">
41
+ </div>
42
+ </div>
43
+ </section>
44
+
45
+ <section class="bg-gray-900 p-6 rounded-xl border border-gray-800 shadow-lg">
46
+ <h2 class="text-xl font-semibold mb-4 text-blue-400">API Connectivity Status</h2>
47
+ <div id="status-container" class="grid grid-cols-2 gap-3 text-xs">
48
+ <!-- Status items injected here -->
49
+ </div>
50
+ </section>
51
+
52
+ <section class="bg-gray-900 p-6 rounded-xl border border-gray-800 shadow-lg md:col-span-2">
53
+ <h2 class="text-xl font-semibold mb-4 text-pink-400">Strategy Lab (Use Cases)</h2>
54
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
55
+ <div class="p-4 bg-gray-800 rounded-lg border border-gray-700">
56
+ <h3 class="font-medium text-pink-300 mb-2">Daily Pulse</h3>
57
+ <p class="text-xs text-gray-400 mb-4">Scan for market opportunities and send report to Telegram.</p>
58
+ <button id="pulse-btn" class="w-full bg-pink-700 hover:bg-pink-600 text-white text-sm py-2 rounded transition-colors">
59
+ Run Market Scan
60
+ </button>
61
+ </div>
62
+ <div class="p-4 bg-gray-800 rounded-lg border border-gray-700">
63
+ <h3 class="font-medium text-yellow-300 mb-2">Offer Architect</h3>
64
+ <p class="text-xs text-gray-400 mb-2">Generate a beta offer and draft payment link.</p>
65
+ <input type="text" id="offer-topic" placeholder="e.g. AI Content Agency"
66
+ class="w-full bg-black border border-gray-700 rounded p-2 text-xs mb-3 focus:border-yellow-500 outline-none">
67
+ <button id="offer-btn" class="w-full bg-yellow-700 hover:bg-yellow-600 text-white text-sm py-2 rounded transition-colors">
68
+ Craft Offer
69
+ </button>
70
+ </div>
71
+ </div>
72
+ </section>
73
+
74
+ <section class="bg-gray-900 p-6 rounded-xl border border-gray-800 shadow-lg">
75
+ <h2 class="text-xl font-semibold mb-4 text-purple-400">System Logs & Webhooks</h2>
76
+ <div class="bg-black rounded-lg p-4 font-mono text-xs text-green-500 h-48 overflow-y-auto" id="logs">
77
+ [System] Boot sequence initiated...<br>
78
+ </div>
79
+ </section>
80
+
81
+ <!-- Telegram Message Monitor - full width -->
82
+ <section class="bg-gray-900 p-6 rounded-xl border border-cyan-900 shadow-lg md:col-span-2">
83
+ <div class="flex items-center justify-between mb-4">
84
+ <h2 class="text-xl font-semibold text-cyan-400 flex items-center gap-2">
85
+ <span class="w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></span>
86
+ Telegram Live Monitor
87
+ </h2>
88
+ <span class="text-xs text-gray-500" id="tg-status">Connecting...</span>
89
+ </div>
90
+ <div id="tg-chat" class="bg-black rounded-lg p-4 font-mono text-xs h-64 overflow-y-auto flex flex-col gap-2 border border-gray-800">
91
+ <p class="text-gray-600 italic text-center mt-auto">Messages will appear here in real-time.</p>
92
+ </div>
93
+ <div class="flex gap-2 mt-3">
94
+ <input type="text" id="tg-send-input" placeholder="Reply to Dee via Telegram..."
95
+ class="flex-1 bg-black border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-cyan-500 transition-colors">
96
+ <button id="tg-send-btn" class="bg-cyan-700 hover:bg-cyan-600 text-white px-5 py-2 rounded-lg text-sm font-bold transition-colors">
97
+ Send
98
+ </button>
99
+ </div>
100
+ </section>
101
+ </main>
102
+ </div>
103
+ <script src="app.js"></script>
104
+ </body>
105
+ </html>
send_instructions.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('./skills/api_caller');
2
+ require('dotenv').config();
3
+
4
+ const sendInstructions = async () => {
5
+ const chatId = process.env.TELEGRAM_CHAT_ID;
6
+ const instructions = `
7
+ <b>Welcome to VinOS!</b> 🎯
8
+
9
+ You are now in command of the <b>Strategy Lab</b>. Here is how to use your new skills:
10
+
11
+ 1. <b>Token Gashapon</b> 🎑
12
+ Open the dashboard at <code>http://localhost:3000</code>. Describe an idea (e.g., "Cyberpunk NFT Card") and click Generate. It uses the <b>Nano Banana</b> logic via OpenRouter.
13
+
14
+ 2. <b>Daily Pulse (Market Scanner)</b> πŸ“Š
15
+ Click "Run Market Scan" in the Strategy Lab. This uses <b>OpenRouter (Gemini)</b> to analyze the Indonesian market for arbitrage opportunities and sends the report right here!
16
+
17
+ 3. <b>Offer Architect</b> πŸ’‘
18
+ Enter a niche topic (e.g., "AI Presentation Masterclass") and click "Craft Offer". This uses <b>Groq</b> or <b>OpenRouter</b> to design a beta offer and draft a payment link.
19
+
20
+ 4. <b>Cost Efficiency</b> πŸ’°
21
+ The system is currently configured to use <b>FREE models</b> on OpenRouter for all your testing. You can switch to "Live" models in your <code>.env</code> once you are ready for production.
22
+
23
+ <i>Ready to start your first launch? Click a button on the dashboard!</i>
24
+ `;
25
+
26
+ console.log("Sending instructions to Telegram...");
27
+ const result = await apiCaller.sendTelegramMessage(chatId, instructions);
28
+
29
+ if (result.success) {
30
+ console.log("Instructions sent successfully.");
31
+ } else {
32
+ console.error("Failed to send instructions:", result.error);
33
+ }
34
+ };
35
+
36
+ sendInstructions();
server.js ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ require('dotenv').config();
2
+ const dns = require('node:dns');
3
+ dns.setDefaultResultOrder('ipv4first');
4
+
5
+ const express = require('express');
6
+ const cors = require('cors');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const memory = require('./skills/memory');
10
+ const apiCaller = require('./skills/api_caller');
11
+
12
+ const app = express();
13
+ app.use(cors());
14
+ app.use(express.json());
15
+ app.use(express.static(path.join(__dirname, 'public')));
16
+
17
+ // API Routes
18
+ app.get('/api/profile', (req, res) => {
19
+ const db = memory.readDB();
20
+ res.json(db.user_profile_snapshot);
21
+ });
22
+
23
+ app.get('/api/sprints', (req, res) => {
24
+ const db = memory.readDB();
25
+ res.json(db.active_sprints || []);
26
+ });
27
+
28
+ app.get('/api/status', (req, res) => {
29
+ const status = {
30
+ openrouter: !!process.env.OPENROUTER_API_KEY,
31
+ groq: !!process.env.GROQ_API_KEY,
32
+ telegram: !!process.env.TELEGRAM_BOT_TOKEN,
33
+ mayar: !!process.env.MAYAR_API_KEY,
34
+ whop: !!process.env.WHOP_API_KEY,
35
+ apify: !!process.env.APIFY_API_KEY
36
+ };
37
+ res.json(status);
38
+ });
39
+
40
+ const dailyPulse = require('./use_cases/daily_pulse');
41
+ const offerArchitect = require('./use_cases/offer_architect');
42
+
43
+ app.post('/api/generate-image', async (req, res) => {
44
+ const { prompt } = req.body;
45
+ if (!prompt) return res.status(400).json({ error: "Prompt is required" });
46
+
47
+ const result = await apiCaller.generateNanoBananaImage(prompt);
48
+ res.json(result);
49
+ });
50
+
51
+ app.post('/api/usecase/pulse', async (req, res) => {
52
+ const result = await dailyPulse();
53
+ res.json(result);
54
+ });
55
+
56
+ app.post('/api/usecase/offer', async (req, res) => {
57
+ const { topic } = req.body;
58
+ if (!topic) return res.status(400).json({ error: "Topic is required" });
59
+ const result = await offerArchitect(topic);
60
+ res.json(result);
61
+ });
62
+
63
+ // Daily Check-in Mechanism (Simplified)
64
+ const DAILY_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
65
+ const chatID = process.env.TELEGRAM_CHAT_ID;
66
+ const botToken = process.env.TELEGRAM_BOT_TOKEN;
67
+
68
+ if (chatID && botToken) {
69
+ console.log(`Telegram Bot Active for Chat ID: ${chatID}`);
70
+ // Optional: Send immediate boot notification to confirm connection
71
+ // apiCaller.sendTelegramMessage(chatID, "<b>VinOS Core Online</b>\nSystem is calibrated. Ready for Token Gashapon experiments.");
72
+
73
+ setInterval(async () => {
74
+ console.log("Triggering daily check-in...");
75
+ await apiCaller.sendTelegramMessage(chatID, "<b>VinOS Daily Check-in</b>\nHow is the Token Gashapon project coming along? Ready for the next arbitrage move?");
76
+ }, DAILY_INTERVAL);
77
+ }
78
+
79
+ // Webhook ingestion (for Mayar/Whop)
80
+ app.post('/api/webhook', (req, res) => {
81
+ const data = req.body;
82
+ console.log("General Webhook received:", data);
83
+ res.status(200).send({ status: 'success' });
84
+ });
85
+
86
+ const { logTelegramMessage } = require('./skills/api_caller');
87
+
88
+ // SSE clients list for real-time push
89
+ const sseClients = [];
90
+ app.get('/api/telegram-stream', (req, res) => {
91
+ res.setHeader('Content-Type', 'text/event-stream');
92
+ res.setHeader('Cache-Control', 'no-cache');
93
+ res.setHeader('Connection', 'keep-alive');
94
+ sseClients.push(res);
95
+ req.on('close', () => sseClients.splice(sseClients.indexOf(res), 1));
96
+ });
97
+ const broadcastSSE = (data) => sseClients.forEach(c => c.write(`data: ${JSON.stringify(data)}\n\n`));
98
+
99
+ app.get('/api/telegram-messages', (req, res) => {
100
+ const db = memory.readDB();
101
+ res.json(db.telegram_log || []);
102
+ });
103
+
104
+ app.post('/api/send-telegram', async (req, res) => {
105
+ const { message } = req.body;
106
+ if (!message) return res.status(400).json({ error: 'Message required' });
107
+ const chatId = process.env.TELEGRAM_CHAT_ID;
108
+ const result = await apiCaller.sendTelegramMessage(chatId, `<b>[VinOS Dashboard]</b> ${message}`);
109
+ broadcastSSE({ direction: 'OUT', chatId, text: `[VinOS Dashboard]: ${message}`, ts: new Date().toISOString() });
110
+ res.json(result);
111
+ });
112
+
113
+ const voiceTranscriber = require('./skills/voice_transcriber');
114
+ const intentRouter = require('./skills/intent_router');
115
+ const conversationMemory = require('./skills/conversation_memory');
116
+ const playbookManager = require('./skills/playbook_manager');
117
+
118
+ // Helper to handle resolved intents
119
+ async function handleVinIntent(chatId, from, userText, confirmed = false) {
120
+ const db = memory.readDB();
121
+ const autoMode = db.user_profile_snapshot?.automatic_mode || false;
122
+
123
+ // 1. Resolve intent
124
+ const { intent, params } = await intentRouter.resolveIntent(userText);
125
+
126
+ // Safety check: if not confirmed and NOT in auto-mode, ask for confirmation
127
+ if (!confirmed && !autoMode && (intent === 'gashapon' || intent === 'pulse' || intent === 'offer')) {
128
+ if (!db.pending_commands) db.pending_commands = {};
129
+ db.pending_commands[chatId] = { intent, params, userText, ts: Date.now() };
130
+ memory.writeDB(db);
131
+
132
+ const confirmationMsg = `πŸ€– <b>Intent:</b> <i>${intent}</i>\n<b>Params:</b> <i>${params || 'none'}</i>\n\nShould I execute? (Reply: <b>Confirm</b> / <b>Cancel</b>)\n<i>Tip: Use /auto on for speed mode.</i>`;
133
+ return await apiCaller.sendTelegramMessage(chatId, confirmationMsg);
134
+ }
135
+
136
+ // 2. Route to appropriate skill
137
+ switch (intent) {
138
+ case 'remember':
139
+ const playbookId = playbookManager.savePlaybook(`User memory trigger`, params, ['user-defined']);
140
+ await apiCaller.sendTelegramMessage(chatId, `🧠 <b>Playbook Saved</b>\nI'll remember this approach for the future.\n<i>Trigger: ${params.substring(0, 50)}...</i>`);
141
+ break;
142
+
143
+ case 'recall':
144
+ const playbooks = playbookManager.searchPlaybooks(params);
145
+ if (playbooks.length > 0) {
146
+ const results = playbooks.slice(0, 2).map(pb => `πŸ“Œ <b>${pb.trigger}</b>\n${pb.solution}`).join('\n\n');
147
+ await apiCaller.sendTelegramMessage(chatId, `πŸ” <b>Found in Playbooks:</b>\n\n${results}`);
148
+ } else {
149
+ await apiCaller.sendTelegramMessage(chatId, `🀷 No playbooks found for "${params}".`);
150
+ }
151
+ break;
152
+
153
+ case 'gashapon':
154
+ const prompt = params || userText;
155
+ await apiCaller.sendTelegramMessage(chatId, "πŸŒ€ <i>Spinning the Gashapon for:</i> " + prompt);
156
+ const gResult = await apiCaller.generateNanoBananaImage(prompt);
157
+ if (gResult.success) {
158
+ await apiCaller.sendTelegramMessage(chatId, `✨ <b>Gashapon Result!</b>\n<a href="${gResult.image_url}">View Image</a>`);
159
+ } else {
160
+ await apiCaller.sendTelegramMessage(chatId, "❌ Gashapon failed: " + gResult.error);
161
+ }
162
+ break;
163
+
164
+ case 'pulse':
165
+ await apiCaller.sendTelegramMessage(chatId, "πŸ“Š <i>Scanning market...</i>");
166
+ await dailyPulse();
167
+ break;
168
+
169
+ case 'offer':
170
+ const topic = params || userText;
171
+ await apiCaller.sendTelegramMessage(chatId, `πŸ’‘ <i>Architecting offer for:</i> ${topic}`);
172
+ await offerArchitect(topic);
173
+ break;
174
+
175
+ case 'clarify':
176
+ await apiCaller.sendTelegramMessage(chatId, `πŸ€” <b>I need a bit more info:</b>\n${params}`);
177
+ break;
178
+
179
+ case 'chat':
180
+ case 'research':
181
+ case 'plan':
182
+ case 'execute':
183
+ case 'analyze':
184
+ case 'create':
185
+ default:
186
+ await processOrchestratorIntent(chatId, intent, userText, params);
187
+ break;
188
+ }
189
+ }
190
+
191
+ // Orchestrator handler that uses Memory + Playbooks
192
+ async function processOrchestratorIntent(chatId, intent, userText, params) {
193
+ // 1. Get Conversation History
194
+ const history = conversationMemory.getHistory(chatId);
195
+
196
+ // 2. Load core persona
197
+ const persona = fs.readFileSync(path.join(__dirname, 'prompts/vin_personality.md'), 'utf8');
198
+
199
+ // 3. Auto-search Playbooks for relevant context BEFORE thinking
200
+ const relatedPlaybooks = playbookManager.searchPlaybooks(userText).slice(0, 1);
201
+ let playbookContext = "";
202
+ if (relatedPlaybooks.length > 0) {
203
+ playbookContext = `\n\n# Relevant Playbook Found:\nUse this proven approach if applicable:\nTrigger: ${relatedPlaybooks[0].trigger}\nSolution: ${relatedPlaybooks[0].solution}`;
204
+ playbookManager.trackUsage(relatedPlaybooks[0].id);
205
+ }
206
+
207
+ const systemContent = persona + playbookContext + `\n\n[System Note: Your current intent classification for the upcoming message is: ${intent}]`;
208
+
209
+ const messages = [
210
+ { role: "system", content: systemContent },
211
+ ...history,
212
+ { role: "user", content: userText }
213
+ ];
214
+
215
+ // Save user message to rolling memory
216
+ conversationMemory.addMessage(chatId, "user", userText);
217
+
218
+ // Call LLM
219
+ const chatResult = await apiCaller.callOpenRouter(messages);
220
+
221
+ if (chatResult.success) {
222
+ // Save Vin's response to rolling memory
223
+ conversationMemory.addMessage(chatId, "assistant", chatResult.data);
224
+ await apiCaller.sendTelegramMessage(chatId, chatResult.data);
225
+ } else {
226
+ await apiCaller.sendTelegramMessage(chatId, "I'm having trouble thinking clearly right now. Try again in a moment.");
227
+ }
228
+ }
229
+
230
+ // Telegram Webhook
231
+ app.post('/api/telegram-webhook', async (req, res) => {
232
+ // 1. ACK immediately to Telegram to stop retries
233
+ res.status(200).send({ status: 'received' });
234
+
235
+ // 2. Process in background
236
+ (async () => {
237
+ const update = req.body;
238
+ console.log("Telegram Update received:", JSON.stringify(update));
239
+
240
+ const message = update.message;
241
+ if (!message) return;
242
+
243
+ const chatId = message.chat.id;
244
+ const from = message.from?.first_name || 'User';
245
+ let userText = message.text;
246
+
247
+ // Handle /auto on/off commands
248
+ if (userText && userText.toLowerCase().startsWith('/auto')) {
249
+ const mode = userText.toLowerCase().includes('on') ? true : false;
250
+ const db = memory.readDB();
251
+ if (!db.user_profile_snapshot) db.user_profile_snapshot = {};
252
+ db.user_profile_snapshot.automatic_mode = mode;
253
+ memory.writeDB(db);
254
+ const status = mode ? "🟒 <b>ON</b> (Speed Mode)" : "🟑 <b>OFF</b> (Safety Mode)";
255
+ return await apiCaller.sendTelegramMessage(chatId, `<b>Automatic Mode:</b> ${status}`);
256
+ }
257
+
258
+ // Handle Confirm/Cancel early
259
+ if (userText && (userText.toLowerCase() === 'confirm' || userText.toLowerCase() === 'cancel')) {
260
+ const db = memory.readDB();
261
+ const pending = db.pending_commands?.[chatId];
262
+
263
+ if (userText.toLowerCase() === 'confirm' && pending) {
264
+ delete db.pending_commands[chatId];
265
+ memory.writeDB(db);
266
+ await apiCaller.sendTelegramMessage(chatId, "βœ… <b>Confirmed.</b> Running now...");
267
+ return await handleVinIntent(chatId, from, pending.userText, true);
268
+ } else if (userText.toLowerCase() === 'cancel' && pending) {
269
+ delete db.pending_commands[chatId];
270
+ memory.writeDB(db);
271
+ return await apiCaller.sendTelegramMessage(chatId, "🀝 <b>Cancelled.</b> What else can I do for you?");
272
+ } else {
273
+ return await apiCaller.sendTelegramMessage(chatId, "No pending command found to confirm or cancel.");
274
+ }
275
+ }
276
+
277
+ if (message.voice) {
278
+ await apiCaller.sendTelegramMessage(chatId, "🎀 <i>Received audio... Transcribing...</i>");
279
+ const transcription = await voiceTranscriber.transcribeVoice(message.voice.file_id);
280
+ if (transcription) {
281
+ userText = transcription;
282
+ apiCaller.logTelegramMessage('IN', chatId, `[${from} (Voice)]: ${userText}`);
283
+ broadcastSSE({ direction: 'IN', chatId, text: `[${from} (Voice)]: ${userText}`, ts: new Date().toISOString() });
284
+ } else {
285
+ return await apiCaller.sendTelegramMessage(chatId, "❌ Transcription failed. Could you try again?");
286
+ }
287
+ } else if (userText) {
288
+ const isControl = /^(confirm|cancel|\/auto|\/start)/i.test(userText);
289
+ if (!isControl) {
290
+ await apiCaller.sendTelegramMessage(chatId, "⚑ <i>Instruction received. Processing...</i>");
291
+ }
292
+ apiCaller.logTelegramMessage('IN', chatId, `[${from}]: ${userText}`);
293
+ broadcastSSE({ direction: 'IN', chatId, text: `[${from}]: ${userText}`, ts: new Date().toISOString() });
294
+ }
295
+
296
+ // Process commands or NL
297
+ if (userText) {
298
+ if (userText.startsWith('/start')) {
299
+ await apiCaller.sendTelegramMessage(chatId, "<b>VinOS Core Online</b>");
300
+ } else {
301
+ await handleVinIntent(chatId, from, userText);
302
+ }
303
+ }
304
+ })().catch(err => console.error("Webhook processing error:", err));
305
+ });
306
+
307
+ const PORT = process.env.PORT || 7860;
308
+ app.listen(PORT, '0.0.0.0', () => {
309
+ console.log(`VinOS Core Online: http://0.0.0.0:${PORT}`);
310
+ });
set_webhook.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios');
2
+ require('dotenv').config();
3
+
4
+ const setWebhook = async () => {
5
+ // The new Hugging Face Cloud URL
6
+ const webhookUrl = process.argv[2] || 'https://algoose-vinos-engine.hf.space/api/telegram-webhook';
7
+ const token = process.env.TELEGRAM_BOT_TOKEN;
8
+
9
+ console.log(`Setting Telegram Webhook to: ${webhookUrl}`);
10
+
11
+ try {
12
+ const response = await axios.get(`https://api.telegram.org/bot${token}/setWebhook`, {
13
+ params: { url: webhookUrl }
14
+ });
15
+ console.log("Response:", response.data);
16
+ if (response.data.ok) {
17
+ console.log("Webhook successfully set! πŸš€ VinOS is now Cloud-Powered.");
18
+ } else {
19
+ console.error("Failed to set webhook.");
20
+ }
21
+ } catch (error) {
22
+ console.error("Error setting webhook:", error.response?.data || error.message);
23
+ }
24
+ };
25
+
26
+ setWebhook();
27
+
skills/api_caller.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios');
2
+ const https = require('https');
3
+ const dns = require('dns');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const DB_PATH = path.join(__dirname, '../database/local_db.json');
7
+ require('dotenv').config();
8
+
9
+ // Force IPv4 DNS resolution (fixes ENOTFOUND on Hugging Face's IPv6-first network)
10
+ dns.setDefaultResultOrder('ipv4first');
11
+ const ipv4Agent = new https.Agent({
12
+ family: 4,
13
+ lookup: (hostname, options, callback) => dns.lookup(hostname, { ...options, family: 4 }, callback)
14
+ });
15
+ const axiosIPv4 = axios.create({ httpsAgent: ipv4Agent });
16
+
17
+ // Model assignments per skill
18
+ const MODELS = {
19
+ CHAT: process.env.OPENROUTER_MODEL || 'meta-llama/llama-3.3-70b-instruct:free',
20
+ FALLBACK: process.env.OPENROUTER_FALLBACK_MODEL || 'google/gemini-2.0-flash-exp:free',
21
+ INTENT: process.env.GROQ_MODEL || 'llama-3.1-8b-instant',
22
+ IMAGE: process.env.IMAGE_GEN_MODEL || 'gemini-2.0-flash-exp:free',
23
+ WHISPER: 'whisper-large-v3'
24
+ };
25
+
26
+ const logTelegramMessage = (direction, chatId, text) => {
27
+ try {
28
+ const raw = fs.readFileSync(DB_PATH, 'utf8');
29
+ const db = JSON.parse(raw);
30
+ if (!db.telegram_log) db.telegram_log = [];
31
+ db.telegram_log.unshift({ direction, chatId, text, ts: new Date().toISOString() });
32
+ if (db.telegram_log.length > 100) db.telegram_log = db.telegram_log.slice(0, 100);
33
+ fs.writeFileSync(DB_PATH, JSON.stringify(db, null, 2));
34
+ } catch (e) { console.error('Log error:', e.message); }
35
+ };
36
+
37
+ // Unified API Caller for VinOS
38
+ module.exports = {
39
+ logTelegramMessage,
40
+ MODELS,
41
+
42
+ // OpenRouter (AI Chat & Reasoning) β€” with automatic fallback
43
+ callOpenRouter: async (messages, model = MODELS.CHAT) => {
44
+ console.log(`[OpenRouter] Calling model: ${model}`);
45
+ try {
46
+ const response = await axiosIPv4.post('https://openrouter.ai/api/v1/chat/completions', {
47
+ model: model,
48
+ messages: messages
49
+ }, {
50
+ headers: {
51
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
52
+ 'HTTP-Referer': 'https://vinos.local',
53
+ 'X-Title': 'VinOS Control Center'
54
+ }
55
+ });
56
+ return { success: true, data: response.data.choices[0].message.content, model };
57
+ } catch (error) {
58
+ console.error(`[OpenRouter] Primary model failed (${model}):`, error.response?.data || error.message);
59
+
60
+ // Auto-fallback to backup model
61
+ if (model !== MODELS.FALLBACK) {
62
+ console.log(`[OpenRouter] Falling back to: ${MODELS.FALLBACK}`);
63
+ try {
64
+ const fallbackResponse = await axiosIPv4.post('https://openrouter.ai/api/v1/chat/completions', {
65
+ model: MODELS.FALLBACK,
66
+ messages: messages
67
+ }, {
68
+ headers: {
69
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
70
+ 'HTTP-Referer': 'https://vinos.local',
71
+ 'X-Title': 'VinOS Control Center'
72
+ }
73
+ });
74
+ return { success: true, data: fallbackResponse.data.choices[0].message.content, model: MODELS.FALLBACK, fallback: true };
75
+ } catch (fallbackError) {
76
+ console.error(`[OpenRouter] Fallback also failed:`, fallbackError.response?.data || fallbackError.message);
77
+ }
78
+ }
79
+ return { success: false, error: error.message };
80
+ }
81
+ },
82
+
83
+ // Groq (High Speed Inference β€” Intent Router & Voice)
84
+ callGroq: async (messages, model = MODELS.INTENT) => {
85
+ console.log(`[Groq] Calling model: ${model}`);
86
+ try {
87
+ const response = await axiosIPv4.post('https://api.groq.com/openai/v1/chat/completions', {
88
+ model: model,
89
+ messages: messages
90
+ }, {
91
+ headers: {
92
+ 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
93
+ 'Content-Type': 'application/json'
94
+ }
95
+ });
96
+ return { success: true, data: response.data.choices[0].message.content };
97
+ } catch (error) {
98
+ console.error("[Groq] Error:", error.response?.data || error.message);
99
+ return { success: false, error: error.message };
100
+ }
101
+ },
102
+
103
+ // NanoBanana (Image Generation via Gashapon)
104
+ generateNanoBananaImage: async (prompt) => {
105
+ console.log(`[NanoBanana] Generating image: ${prompt}`);
106
+ try {
107
+ const response = await axiosIPv4.post('https://nanobanana.aikit.club/v1/images/generations', {
108
+ prompt: prompt,
109
+ model: MODELS.IMAGE,
110
+ response_format: "url"
111
+ }, {
112
+ headers: {
113
+ 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
114
+ 'Content-Type': 'application/json'
115
+ }
116
+ });
117
+ return { success: true, image_url: response.data.data[0].url };
118
+ } catch (error) {
119
+ console.error("[NanoBanana] API Error:", error.response?.data || error.message);
120
+ return { success: false, error: error.message };
121
+ }
122
+ },
123
+
124
+ // Telegram (Send Messages)
125
+ sendTelegramMessage: async (chatId, message) => {
126
+ console.log(`[Telegram] Sending to ${chatId}: ${message.substring(0, 80)}...`);
127
+ logTelegramMessage('OUT', chatId, message);
128
+ try {
129
+ const response = await axiosIPv4.post(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, {
130
+ chat_id: chatId,
131
+ text: message,
132
+ parse_mode: 'HTML'
133
+ });
134
+ return { success: true, data: response.data };
135
+ } catch (error) {
136
+ console.error("[Telegram] API Error:", error.response?.data || error.message);
137
+ return { success: false, error: error.message };
138
+ }
139
+ },
140
+
141
+ // Mayar (Payment Links)
142
+ createMayarLink: async (offerDetails) => {
143
+ console.log("[Mayar] Creating link:", offerDetails);
144
+ return { success: true, url: "https://mayar.id/pay/example" };
145
+ }
146
+ };
skills/conversation_memory.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/local_db.json');
4
+
5
+ const MAX_HISTORY = 20;
6
+
7
+ module.exports = {
8
+ addMessage: (chatId, role, content) => {
9
+ try {
10
+ const raw = fs.readFileSync(dbPath, 'utf8');
11
+ const db = JSON.parse(raw);
12
+ if (!db.conversations) db.conversations = {};
13
+ if (!db.conversations[chatId]) db.conversations[chatId] = [];
14
+
15
+ db.conversations[chatId].push({ role, content, ts: Date.now() });
16
+
17
+ // Keep only the last MAX_HISTORY messages
18
+ if (db.conversations[chatId].length > MAX_HISTORY) {
19
+ db.conversations[chatId] = db.conversations[chatId].slice(-MAX_HISTORY);
20
+ }
21
+
22
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
23
+ return true;
24
+ } catch (error) {
25
+ console.error("[Memory] Error adding message:", error);
26
+ return false;
27
+ }
28
+ },
29
+
30
+ getHistory: (chatId) => {
31
+ try {
32
+ const raw = fs.readFileSync(dbPath, 'utf8');
33
+ const db = JSON.parse(raw);
34
+ if (!db.conversations || !db.conversations[chatId]) return [];
35
+
36
+ // Map to standard OpenAI/OpenRouter format: { role, content }
37
+ return db.conversations[chatId].map(msg => ({ role: msg.role, content: msg.content }));
38
+ } catch (error) {
39
+ console.error("[Memory] Error reading history:", error);
40
+ return [];
41
+ }
42
+ },
43
+
44
+ clearHistory: (chatId) => {
45
+ try {
46
+ const raw = fs.readFileSync(dbPath, 'utf8');
47
+ const db = JSON.parse(raw);
48
+ if (!db.conversations) db.conversations = {};
49
+ db.conversations[chatId] = [];
50
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
51
+ return true;
52
+ } catch (error) {
53
+ return false;
54
+ }
55
+ }
56
+ };
skills/intent_router.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('./api_caller');
2
+ require('dotenv').config();
3
+
4
+ const intentRouter = {
5
+ /**
6
+ * Resolves user intent using Groq LLM.
7
+ * @param {string} text - The transcribed or direct text from the user.
8
+ * @returns {Promise<{intent: string, params: string}>} - The resolved intent and parameters.
9
+ */
10
+ resolveIntent: async (text) => {
11
+ console.log(`[IntentRouter] Resolving intent for: ${text}`);
12
+
13
+ const systemPrompt = `
14
+ You are the VinOS Intent Router. Your job is to classify the user's message into one of these intents:
15
+ - research: Find out information, scan the web, or learn about a topic (params: the topic)
16
+ - remember: Save an approach, rule, or solution as a Playbook (params: what to remember)
17
+ - recall: Search Playbooks for past solutions or rules (params: the query)
18
+ - plan: Break down a complex request into a step-by-step plan (params: the goal)
19
+ - execute: Run a specific tool or take a definitive action (params: the action)
20
+ - analyze: Review data, ideas, or content and provide critical feedback (params: what to analyze)
21
+ - create: Draft content, code, or structured text (params: what to create)
22
+ - gashapon: Generate an image or artistic idea (params: the prompt)
23
+ - pulse: Scan for market trends or business opportunities (params: empty)
24
+ - offer: Draft a business offer or product (params: the niche or topic)
25
+ - clarify: The request is too vague, ambiguous, or lacks detail to execute a skill (params: the missing info)
26
+ - chat: General conversation or if nothing else matches (params: the user query)
27
+
28
+ Respond ONLY with a JSON object: {"intent": "...", "params": "..."}
29
+ `;
30
+
31
+ const messages = [
32
+ { role: "system", content: systemPrompt },
33
+ { role: "user", content: text }
34
+ ];
35
+
36
+ // Use Groq for speed and free tier
37
+ const result = await apiCaller.callGroq(messages, 'llama-3.1-8b-instant');
38
+
39
+ if (result.success) {
40
+ try {
41
+ const parsed = JSON.parse(result.data.trim().match(/\{.*\}/s)[0]);
42
+ console.log(`[IntentRouter] Resolved: ${parsed.intent} | ${parsed.params}`);
43
+ return parsed;
44
+ } catch (e) {
45
+ console.error("[IntentRouter] Failed to parse JSON response:", result.data);
46
+ return { intent: "chat", params: text };
47
+ }
48
+ } else {
49
+ console.error("[IntentRouter] Failed to call Groq:", result.error);
50
+ return { intent: "chat", params: text };
51
+ }
52
+ }
53
+ };
54
+
55
+ module.exports = intentRouter;
skills/memory.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/local_db.json');
4
+
5
+ module.exports = {
6
+ readDB: () => {
7
+ try {
8
+ const data = fs.readFileSync(dbPath, 'utf8');
9
+ return JSON.parse(data);
10
+ } catch (error) {
11
+ console.error("Error reading DB:", error);
12
+ return {};
13
+ }
14
+ },
15
+ writeDB: (data) => {
16
+ try {
17
+ fs.writeFileSync(dbPath, JSON.stringify(data, null, 2));
18
+ return true;
19
+ } catch (error) {
20
+ console.error("Error writing DB:", error);
21
+ return false;
22
+ }
23
+ }
24
+ };
skills/playbook_manager.js ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const dbPath = path.join(__dirname, '../database/playbooks.json');
4
+
5
+ // Ensure db exists
6
+ if (!fs.existsSync(dbPath)) {
7
+ fs.writeFileSync(dbPath, JSON.stringify({ playbooks: [] }, null, 2));
8
+ }
9
+
10
+ module.exports = {
11
+ savePlaybook: (trigger, solution, tags = []) => {
12
+ try {
13
+ const raw = fs.readFileSync(dbPath, 'utf8');
14
+ const db = JSON.parse(raw);
15
+
16
+ const newPlaybook = {
17
+ id: 'pb_' + Date.now(),
18
+ trigger,
19
+ solution,
20
+ tags,
21
+ created: new Date().toISOString(),
22
+ used_count: 0
23
+ };
24
+
25
+ db.playbooks.push(newPlaybook);
26
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
27
+ console.log(`[Playbook] Saved new approach: ${trigger}`);
28
+ return newPlaybook.id;
29
+ } catch (error) {
30
+ console.error("[Playbook] Error saving playbook:", error);
31
+ return null;
32
+ }
33
+ },
34
+
35
+ searchPlaybooks: (query) => {
36
+ try {
37
+ const raw = fs.readFileSync(dbPath, 'utf8');
38
+ const db = JSON.parse(raw);
39
+ // If no playbooks, just return empty
40
+ if (!db.playbooks || db.playbooks.length === 0) return [];
41
+
42
+ const q = query.toLowerCase();
43
+
44
+ // Basic semantic search across trigger, tags, and solution
45
+ const matches = db.playbooks.filter(pb =>
46
+ (pb.trigger && pb.trigger.toLowerCase().includes(q)) ||
47
+ (pb.tags && pb.tags.some(tag => tag.toLowerCase().includes(q))) ||
48
+ (pb.solution && pb.solution.toLowerCase().includes(q))
49
+ );
50
+
51
+ return matches;
52
+ } catch (error) {
53
+ console.error("[Playbook] Error searching playbooks:", error);
54
+ return [];
55
+ }
56
+ },
57
+
58
+ getAllPlaybooks: () => {
59
+ try {
60
+ const raw = fs.readFileSync(dbPath, 'utf8');
61
+ const db = JSON.parse(raw);
62
+ return db.playbooks || [];
63
+ } catch (error) {
64
+ return [];
65
+ }
66
+ },
67
+
68
+ trackUsage: (playbookId) => {
69
+ try {
70
+ const raw = fs.readFileSync(dbPath, 'utf8');
71
+ const db = JSON.parse(raw);
72
+ const pbIndex = db.playbooks.findIndex(pb => pb.id === playbookId);
73
+ if (pbIndex !== -1) {
74
+ db.playbooks[pbIndex].used_count = (db.playbooks[pbIndex].used_count || 0) + 1;
75
+ fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
76
+ }
77
+ } catch (error) {
78
+ // ignore
79
+ }
80
+ }
81
+ };
skills/voice_transcriber.js ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const axios = require('axios');
2
+ const https = require('https');
3
+ const dns = require('dns');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const FormData = require('form-data');
7
+ require('dotenv').config();
8
+
9
+ // Force IPv4 DNS resolution (fixes ENOTFOUND on Hugging Face)
10
+ dns.setDefaultResultOrder('ipv4first');
11
+ const ipv4Agent = new https.Agent({
12
+ family: 4,
13
+ lookup: (hostname, options, callback) => dns.lookup(hostname, { ...options, family: 4 }, callback)
14
+ });
15
+ const axiosIPv4 = axios.create({ httpsAgent: ipv4Agent });
16
+
17
+ const voiceTranscriber = {
18
+ /**
19
+ * Transcribes a Telegram voice message using Groq Whisper.
20
+ * Model: whisper-large-v3 (free, fastest STT available)
21
+ * @param {string} fileId - The Telegram file_id of the voice message.
22
+ * @returns {Promise<string|null>} - The transcribed text or null on failure.
23
+ */
24
+ transcribeVoice: async (fileId) => {
25
+ try {
26
+ const token = process.env.TELEGRAM_BOT_TOKEN;
27
+
28
+ // 1. Get file path from Telegram
29
+ console.log(`[VoiceTranscriber] Getting file path for ID: ${fileId}`);
30
+ const fileInfoResponse = await axiosIPv4.get(`https://api.telegram.org/bot${token}/getFile`, {
31
+ params: { file_id: fileId }
32
+ });
33
+
34
+ if (!fileInfoResponse.data.ok) {
35
+ console.error("[VoiceTranscriber] Failed to get file info:", fileInfoResponse.data.description);
36
+ return null;
37
+ }
38
+
39
+ const filePath = fileInfoResponse.data.result.file_path;
40
+ const downloadUrl = `https://api.telegram.org/file/bot${token}/${filePath}`;
41
+
42
+ // 2. Download the audio file
43
+ console.log(`[VoiceTranscriber] Downloading voice file...`);
44
+ const audioResponse = await axiosIPv4.get(downloadUrl, { responseType: 'arraybuffer' });
45
+
46
+ // Save temporarily
47
+ const tempFilePath = path.join(__dirname, `../tmp_voice_${Date.now()}.ogg`);
48
+ fs.writeFileSync(tempFilePath, audioResponse.data);
49
+
50
+ // 3. Send to Groq Whisper (free, fastest STT)
51
+ console.log(`[VoiceTranscriber] Sending to Groq Whisper (whisper-large-v3)...`);
52
+ const form = new FormData();
53
+ form.append('file', fs.createReadStream(tempFilePath));
54
+ form.append('model', 'whisper-large-v3');
55
+
56
+ const groqResponse = await axiosIPv4.post('https://api.groq.com/openai/v1/audio/transcriptions', form, {
57
+ headers: {
58
+ ...form.getHeaders(),
59
+ 'Authorization': `Bearer ${process.env.GROQ_API_KEY}`
60
+ }
61
+ });
62
+
63
+ // Cleanup temp file
64
+ fs.unlinkSync(tempFilePath);
65
+
66
+ if (groqResponse.data && groqResponse.data.text) {
67
+ console.log(`[VoiceTranscriber] Transcription: ${groqResponse.data.text}`);
68
+ return groqResponse.data.text;
69
+ }
70
+
71
+ return null;
72
+ } catch (error) {
73
+ console.error("[VoiceTranscriber] Error:", error.response?.data || error.message);
74
+ return null;
75
+ }
76
+ }
77
+ };
78
+
79
+ module.exports = voiceTranscriber;
start.sh ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ # VinOS Cloud Startup Script
3
+ # Resolves all API endpoint IPs and injects into /etc/hosts
4
+ # This bypasses Hugging Face's DNS restrictions
5
+
6
+ echo "πŸ”§ Resolving API endpoints for Cloud Mode..."
7
+
8
+ # Resolve and inject each API domain
9
+ for DOMAIN in api.telegram.org openrouter.ai api.groq.com nanobanana.aikit.club; do
10
+ IP=$(getent hosts $DOMAIN | awk '{ print $1 }' | head -1)
11
+ if [ -n "$IP" ]; then
12
+ echo "$IP $DOMAIN" >> /etc/hosts
13
+ echo " βœ… $DOMAIN -> $IP"
14
+ else
15
+ echo " ⚠️ Could not resolve $DOMAIN (will use fallback)"
16
+ fi
17
+ done
18
+
19
+ # Fallback IPs if DNS fails completely
20
+ grep -q "api.telegram.org" /etc/hosts || echo "149.154.167.220 api.telegram.org" >> /etc/hosts
21
+
22
+ echo ""
23
+ echo "πŸš€ Starting VinOS Engine..."
24
+ exec node server.js
test_telegram.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('./skills/api_caller');
2
+ require('dotenv').config();
3
+
4
+ const testTelegram = async () => {
5
+ const chatId = process.env.TELEGRAM_CHAT_ID;
6
+ const message = "<b>VinOS Strategy Lab Live!</b>\nHello Dee! This is your official notification that the VinOS Strategy Lab is now active and ready for arbitrage. πŸš€\n\n- Market Scanner: Online\n- Offer Architect: Active\n- Token Gashapon: Functional";
7
+
8
+ console.log("Attempting to send test message to ID:", chatId);
9
+ const result = await apiCaller.sendTelegramMessage(chatId, message);
10
+
11
+ if (result.success) {
12
+ console.log("Success! Message sent.");
13
+ } else {
14
+ console.error("Failed to send message:", result.error);
15
+ }
16
+ };
17
+
18
+ testTelegram();
use_cases/daily_pulse.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('../skills/api_caller');
2
+
3
+ /**
4
+ * Use Case 1: Daily Pulse (Market Scanner)
5
+ * Scans for the latest AI / Digital Strategy trends and notifies the user.
6
+ */
7
+ module.exports = async () => {
8
+ console.log("[Daily Pulse] Starting market scan...");
9
+
10
+ const prompt = [
11
+ { role: "system", content: "You are a sharp market analyst looking for digital arbitrage opportunities." },
12
+ { role: "user", content: "Scan the current landscape for 3 high-potential AI service niches that can be launched in 24 hours. Focus on the Indonesian market." }
13
+ ];
14
+
15
+ const result = await apiCaller.callOpenRouter(prompt);
16
+
17
+ if (result.success) {
18
+ const message = `<b>VinOS Daily Pulse</b> πŸ“Š\n\n${result.data}\n\n<i>Time to execute?</i>`;
19
+ await apiCaller.sendTelegramMessage(process.env.TELEGRAM_CHAT_ID, message);
20
+ return { success: true, pulse: result.data };
21
+ } else {
22
+ return { success: false, error: result.error };
23
+ }
24
+ };
use_cases/offer_architect.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const apiCaller = require('../skills/api_caller');
2
+ const memory = require('../skills/memory');
3
+
4
+ /**
5
+ * Use Case 2: Offer Architect
6
+ * Creates a high-converting offer and generates a payment link placeholder.
7
+ */
8
+ module.exports = async (topic) => {
9
+ console.log(`[Offer Architect] Designing offer for: ${topic}`);
10
+
11
+ const prompt = [
12
+ { role: "system", content: "You are a master of conversion copy and digital product design." },
13
+ { role: "user", content: `Create a 'beta' offer for a digital product about: ${topic}. Include a title, price (in IDR), and 3 key benefits.` }
14
+ ];
15
+
16
+ const result = await apiCaller.callOpenRouter(prompt);
17
+
18
+ if (result.success) {
19
+ // Simulate creating a Mayar link
20
+ const mayarLink = await apiCaller.createMayarLink({ title: topic, price: 300000 });
21
+
22
+ // Update Local DB with new experiment
23
+ const db = memory.readDB();
24
+ db.metrics.active_experiments += 1;
25
+ memory.writeDB(db);
26
+
27
+ const message = `<b>New Offer Crafted!</b> πŸ’‘\n\n${result.data}\n\nCheck it out here: ${mayarLink.url}`;
28
+ await apiCaller.sendTelegramMessage(process.env.TELEGRAM_CHAT_ID, message);
29
+
30
+ return { success: true, offer: result.data, link: mayarLink.url };
31
+ } else {
32
+ return { success: false, error: result.error };
33
+ }
34
+ };