@mooncompany/uplink-chat 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @mooncompany/uplink-chat might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +185 -0
- package/bin/uplink.js +279 -0
- package/middleware/error-handler.js +69 -0
- package/package.json +93 -0
- package/public/css/agents.36b98c0f.css +1469 -0
- package/public/css/agents.css +1469 -0
- package/public/css/app.a6a7f8f5.css +2731 -0
- package/public/css/app.css +2731 -0
- package/public/css/artifacts.css +444 -0
- package/public/css/commands.css +55 -0
- package/public/css/connection.css +131 -0
- package/public/css/dashboard.css +233 -0
- package/public/css/developer.css +328 -0
- package/public/css/files.css +123 -0
- package/public/css/markdown.css +156 -0
- package/public/css/message-actions.css +278 -0
- package/public/css/mobile.css +614 -0
- package/public/css/panels-unified.css +483 -0
- package/public/css/premium.css +415 -0
- package/public/css/realtime.css +189 -0
- package/public/css/satellites.css +401 -0
- package/public/css/shortcuts.css +185 -0
- package/public/css/split-view.4def0262.css +673 -0
- package/public/css/split-view.css +673 -0
- package/public/css/theme-generator.css +391 -0
- package/public/css/themes.css +387 -0
- package/public/css/timestamps.css +54 -0
- package/public/css/variables.css +78 -0
- package/public/dist/bundle.b55050c4.js +15757 -0
- package/public/favicon.svg +24 -0
- package/public/img/agents/ada.png +0 -0
- package/public/img/agents/clarice.png +0 -0
- package/public/img/agents/dennis-nedry.png +0 -0
- package/public/img/agents/elliot-alderson.png +0 -0
- package/public/img/agents/main.png +0 -0
- package/public/img/agents/scotty.png +0 -0
- package/public/img/agents/top-flight-security.png +0 -0
- package/public/index.html +1083 -0
- package/public/js/agents-data.js +234 -0
- package/public/js/agents-ui.js +72 -0
- package/public/js/agents.js +1525 -0
- package/public/js/app.js +79 -0
- package/public/js/appearance-settings.js +111 -0
- package/public/js/artifacts.js +432 -0
- package/public/js/audio-queue.js +168 -0
- package/public/js/bootstrap.js +54 -0
- package/public/js/chat.js +1211 -0
- package/public/js/commands.js +581 -0
- package/public/js/connection-api.js +121 -0
- package/public/js/connection.js +1231 -0
- package/public/js/context-tracker.js +271 -0
- package/public/js/core.js +172 -0
- package/public/js/dashboard.js +452 -0
- package/public/js/developer.js +432 -0
- package/public/js/encryption.js +124 -0
- package/public/js/errors.js +122 -0
- package/public/js/event-bus.js +77 -0
- package/public/js/fetch-utils.js +171 -0
- package/public/js/file-handler.js +229 -0
- package/public/js/files.js +352 -0
- package/public/js/gateway-chat.js +538 -0
- package/public/js/logger.js +112 -0
- package/public/js/markdown.js +190 -0
- package/public/js/message-actions.js +431 -0
- package/public/js/message-renderer.js +288 -0
- package/public/js/missed-messages.js +235 -0
- package/public/js/mobile-debug.js +95 -0
- package/public/js/notifications.js +367 -0
- package/public/js/offline-queue.js +178 -0
- package/public/js/onboarding.js +543 -0
- package/public/js/panels.js +156 -0
- package/public/js/premium.js +412 -0
- package/public/js/realtime-voice.js +844 -0
- package/public/js/satellite-sync.js +256 -0
- package/public/js/satellite-ui.js +175 -0
- package/public/js/satellites.js +1516 -0
- package/public/js/settings.js +1087 -0
- package/public/js/shortcuts.js +381 -0
- package/public/js/split-chat.js +1234 -0
- package/public/js/split-resize.js +211 -0
- package/public/js/splitview.js +340 -0
- package/public/js/storage.js +408 -0
- package/public/js/streaming-handler.js +324 -0
- package/public/js/stt-settings.js +316 -0
- package/public/js/theme-generator.js +661 -0
- package/public/js/themes.js +164 -0
- package/public/js/timestamps.js +198 -0
- package/public/js/tts-settings.js +575 -0
- package/public/js/ui.js +267 -0
- package/public/js/update-notifier.js +143 -0
- package/public/js/utils/constants.js +165 -0
- package/public/js/utils/sanitize.js +93 -0
- package/public/js/utils/sse-parser.js +195 -0
- package/public/js/voice.js +883 -0
- package/public/manifest.json +58 -0
- package/public/moon_texture.jpg +0 -0
- package/public/sw.js +221 -0
- package/public/three.min.js +6 -0
- package/server/channel.js +529 -0
- package/server/chat.js +270 -0
- package/server/config-store.js +362 -0
- package/server/config.js +159 -0
- package/server/context.js +131 -0
- package/server/gateway-commands.js +211 -0
- package/server/gateway-proxy.js +318 -0
- package/server/index.js +22 -0
- package/server/logger.js +89 -0
- package/server/middleware/auth.js +188 -0
- package/server/middleware.js +218 -0
- package/server/openclaw-discover.js +308 -0
- package/server/premium/index.js +156 -0
- package/server/premium/license.js +140 -0
- package/server/realtime/bridge.js +837 -0
- package/server/realtime/index.js +349 -0
- package/server/realtime/tts-stream.js +446 -0
- package/server/routes/agents.js +564 -0
- package/server/routes/artifacts.js +174 -0
- package/server/routes/chat.js +311 -0
- package/server/routes/config-settings.js +345 -0
- package/server/routes/config.js +603 -0
- package/server/routes/files.js +307 -0
- package/server/routes/index.js +18 -0
- package/server/routes/media.js +451 -0
- package/server/routes/missed-messages.js +107 -0
- package/server/routes/premium.js +75 -0
- package/server/routes/push.js +156 -0
- package/server/routes/satellite.js +406 -0
- package/server/routes/status.js +251 -0
- package/server/routes/stt.js +35 -0
- package/server/routes/voice.js +260 -0
- package/server/routes/webhooks.js +203 -0
- package/server/routes.js +206 -0
- package/server/runtime-config.js +336 -0
- package/server/share.js +305 -0
- package/server/stt/faster-whisper.js +72 -0
- package/server/stt/groq.js +51 -0
- package/server/stt/index.js +196 -0
- package/server/stt/openai.js +49 -0
- package/server/sync.js +244 -0
- package/server/tailscale-https.js +175 -0
- package/server/tts.js +646 -0
- package/server/update-checker.js +172 -0
- package/server/utils/filename.js +129 -0
- package/server/utils.js +147 -0
- package/server/watchdog.js +318 -0
- package/server/websocket/broadcast.js +359 -0
- package/server/websocket/connections.js +339 -0
- package/server/websocket/index.js +215 -0
- package/server/websocket/routing.js +277 -0
- package/server/websocket/sync.js +102 -0
- package/server.js +404 -0
- package/utils/detect-tool-usage.js +93 -0
- package/utils/errors.js +158 -0
- package/utils/html-escape.js +84 -0
- package/utils/id-sanitize.js +94 -0
- package/utils/response.js +130 -0
- package/utils/with-retry.js +105 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Routes - External integrations and OpenClaw push
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import { badRequest, unauthorized, internalError, ErrorCodes } from '../../utils/errors.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Setup webhook routes
|
|
10
|
+
* @param {Express} app - Express app instance
|
|
11
|
+
* @param {Object} context - Request context
|
|
12
|
+
*/
|
|
13
|
+
export function setupWebhookRoutes(app, context) {
|
|
14
|
+
const {
|
|
15
|
+
verifyWebhookToken,
|
|
16
|
+
fetch: fetchWithTimeout,
|
|
17
|
+
broadcastOpenClawPush,
|
|
18
|
+
broadcastToAll,
|
|
19
|
+
wsClients,
|
|
20
|
+
log,
|
|
21
|
+
config,
|
|
22
|
+
} = context;
|
|
23
|
+
|
|
24
|
+
const { GATEWAY_URL, GATEWAY_TOKEN, OPENCLAW_CALLBACK_SECRET, REQUEST_TIMEOUT } = config;
|
|
25
|
+
|
|
26
|
+
// ===========================================
|
|
27
|
+
// OpenClaw Channel Push
|
|
28
|
+
// ===========================================
|
|
29
|
+
|
|
30
|
+
app.post('/api/openclaw/push', async (req, res) => {
|
|
31
|
+
const headerSecret = req.get('X-Uplink-Secret') || '';
|
|
32
|
+
|
|
33
|
+
// Validate callback secret using timing-safe comparison
|
|
34
|
+
let isValid = false;
|
|
35
|
+
if (OPENCLAW_CALLBACK_SECRET && headerSecret) {
|
|
36
|
+
const providedBuf = Buffer.from(headerSecret);
|
|
37
|
+
const expectedBuf = Buffer.from(OPENCLAW_CALLBACK_SECRET);
|
|
38
|
+
if (providedBuf.length === expectedBuf.length) {
|
|
39
|
+
isValid = crypto.timingSafeEqual(providedBuf, expectedBuf);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!isValid) {
|
|
44
|
+
log('warn', '[OpenClaw Push] Unauthorized request - invalid or missing secret');
|
|
45
|
+
return unauthorized(res, 'Invalid or missing secret');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const payload = req.body || {};
|
|
49
|
+
const allowedTypes = new Set(['message', 'chunk', 'tool', 'complete', 'error']);
|
|
50
|
+
const hasValidType = allowedTypes.has(payload.type);
|
|
51
|
+
const hasSatelliteId = typeof payload.satelliteId === 'string' && payload.satelliteId.length > 0;
|
|
52
|
+
const hasTimestamp = typeof payload.timestamp === 'number';
|
|
53
|
+
|
|
54
|
+
if (!hasValidType || !hasSatelliteId || !hasTimestamp) {
|
|
55
|
+
log('warn', `[OpenClaw Push] Invalid payload: type=${payload.type}, satelliteId=${payload.satelliteId}`);
|
|
56
|
+
return badRequest(res, 'Invalid payload: type, satelliteId, and timestamp required', ErrorCodes.VALIDATION_ERROR);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Broadcast to WebSocket clients
|
|
60
|
+
broadcastOpenClawPush(payload);
|
|
61
|
+
|
|
62
|
+
// Check if any WebSocket clients received the message
|
|
63
|
+
const wsDelivered = wsClients.size > 0;
|
|
64
|
+
|
|
65
|
+
if (!wsDelivered && payload.type === 'message' && payload.message) {
|
|
66
|
+
// No WebSocket clients connected
|
|
67
|
+
|
|
68
|
+
// 1. Queue message for when they open Uplink
|
|
69
|
+
const { addMissedMessage } = await import('./missed-messages.js');
|
|
70
|
+
await addMissedMessage({
|
|
71
|
+
type: payload.type,
|
|
72
|
+
message: payload.message,
|
|
73
|
+
satelliteId: payload.satelliteId,
|
|
74
|
+
author: 'Assistant'
|
|
75
|
+
});
|
|
76
|
+
log('debug', `[OpenClaw Push] Queued missed message for satellite ${payload.satelliteId}`);
|
|
77
|
+
|
|
78
|
+
// 2. Send push notification to wake them up
|
|
79
|
+
try {
|
|
80
|
+
const { sendPushNotification } = await import('./push.js');
|
|
81
|
+
const pushResult = await sendPushNotification('default', {
|
|
82
|
+
title: 'Uplink',
|
|
83
|
+
body: payload.message.length > 100 ? payload.message.substring(0, 100) + '...' : payload.message,
|
|
84
|
+
url: '/',
|
|
85
|
+
satelliteId: payload.satelliteId,
|
|
86
|
+
tag: 'uplink-message'
|
|
87
|
+
});
|
|
88
|
+
if (pushResult.ok) {
|
|
89
|
+
log('debug', `[OpenClaw Push] Sent push notification for satellite ${payload.satelliteId}`);
|
|
90
|
+
}
|
|
91
|
+
} catch (pushErr) {
|
|
92
|
+
log('debug', `[OpenClaw Push] Push notification failed: ${pushErr.message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
log('debug', `[OpenClaw Push] Broadcasted ${payload.type} for satellite ${payload.satelliteId} (WS: ${wsDelivered})`);
|
|
97
|
+
|
|
98
|
+
return res.json({ ok: true });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ===========================================
|
|
102
|
+
// Webhooks
|
|
103
|
+
// ===========================================
|
|
104
|
+
|
|
105
|
+
app.post('/api/webhook/message', verifyWebhookToken, async (req, res) => {
|
|
106
|
+
try {
|
|
107
|
+
const { message, source = 'webhook', metadata = {} } = req.body;
|
|
108
|
+
|
|
109
|
+
if (!message) {
|
|
110
|
+
return badRequest(res, 'message required', ErrorCodes.MISSING_FIELD);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Validate message length (security)
|
|
114
|
+
if (message.length > config.MAX_INPUT_LENGTHS.MESSAGE) {
|
|
115
|
+
return badRequest(res, `message exceeds maximum length of ${config.MAX_INPUT_LENGTHS.MESSAGE} characters`, ErrorCodes.VALIDATION_ERROR);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log('info', `[Webhook] Message from ${source}: "${message.substring(0, 50)}..."`);
|
|
119
|
+
|
|
120
|
+
const formattedMessage = `[External: ${source}] ${message}`;
|
|
121
|
+
|
|
122
|
+
const chatResponse = await fetchWithTimeout(`${GATEWAY_URL}/v1/chat/completions`, {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
'Authorization': `Bearer ${GATEWAY_TOKEN}`
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
model: 'openclaw',
|
|
130
|
+
user: 'webhook',
|
|
131
|
+
messages: [{ role: 'user', content: formattedMessage }]
|
|
132
|
+
})
|
|
133
|
+
}, REQUEST_TIMEOUT);
|
|
134
|
+
|
|
135
|
+
if (!chatResponse.ok) {
|
|
136
|
+
throw new Error(`Gateway error: ${chatResponse.status}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const data = await chatResponse.json();
|
|
140
|
+
const response = data.choices?.[0]?.message?.content || 'No response';
|
|
141
|
+
|
|
142
|
+
broadcastToAll(JSON.stringify({
|
|
143
|
+
type: 'webhook_message',
|
|
144
|
+
source,
|
|
145
|
+
message,
|
|
146
|
+
response,
|
|
147
|
+
metadata,
|
|
148
|
+
timestamp: Date.now()
|
|
149
|
+
}));
|
|
150
|
+
|
|
151
|
+
res.json({ ok: true, response });
|
|
152
|
+
} catch (e) {
|
|
153
|
+
log('error', '[Webhook] Message error:', e.message);
|
|
154
|
+
internalError(res, e.message, ErrorCodes.GATEWAY_ERROR);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
app.post('/api/webhook/notify', verifyWebhookToken, async (req, res) => {
|
|
159
|
+
try {
|
|
160
|
+
const { title, body, type = 'info', data = {} } = req.body;
|
|
161
|
+
|
|
162
|
+
if (!title && !body) {
|
|
163
|
+
return badRequest(res, 'title or body required', ErrorCodes.MISSING_FIELD);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
log('info', `[Webhook] Notification: ${title || body}`);
|
|
167
|
+
|
|
168
|
+
broadcastToAll(JSON.stringify({
|
|
169
|
+
type: 'notification',
|
|
170
|
+
notification: { title, body, type, data, timestamp: Date.now() }
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
res.json({ ok: true, delivered: wsClients.size });
|
|
174
|
+
} catch (e) {
|
|
175
|
+
log('error', '[Webhook] Notify error:', e.message);
|
|
176
|
+
internalError(res, e.message, ErrorCodes.INTERNAL_ERROR);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
app.post('/api/webhook/trigger', verifyWebhookToken, async (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const { action, params = {} } = req.body;
|
|
183
|
+
|
|
184
|
+
if (!action) {
|
|
185
|
+
return badRequest(res, 'action required', ErrorCodes.MISSING_FIELD);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
log('info', `[Webhook] Trigger: ${action}`);
|
|
189
|
+
|
|
190
|
+
broadcastToAll(JSON.stringify({
|
|
191
|
+
type: 'trigger',
|
|
192
|
+
action,
|
|
193
|
+
params,
|
|
194
|
+
timestamp: Date.now()
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
res.json({ ok: true, action, delivered: wsClients.size });
|
|
198
|
+
} catch (e) {
|
|
199
|
+
log('error', '[Webhook] Trigger error:', e.message);
|
|
200
|
+
internalError(res, e.message, ErrorCodes.INTERNAL_ERROR);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
package/server/routes.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routes Module - API endpoints orchestrator
|
|
3
|
+
*
|
|
4
|
+
* This module coordinates all route handlers by importing modular route files
|
|
5
|
+
* and passing shared dependencies to each.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import multer from 'multer';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import os from 'os';
|
|
12
|
+
|
|
13
|
+
import { log, fetchWithTimeout } from './utils.js';
|
|
14
|
+
import { transcribe, chat, chatWithParallelTTS, generateTTS } from './chat.js';
|
|
15
|
+
import { isPrivateIP, verifyWebhookToken, strictLimiter } from './middleware.js';
|
|
16
|
+
import { broadcastOpenClawPush, broadcastToAll, wsClients } from './websocket/index.js';
|
|
17
|
+
import { sendMessage } from './channel.js';
|
|
18
|
+
import { getClientConfig, saveConfig, needsOnboarding, loadConfig } from './runtime-config.js';
|
|
19
|
+
import { createRequestContext } from './context.js';
|
|
20
|
+
import {
|
|
21
|
+
GATEWAY_URL, GATEWAY_TOKEN,
|
|
22
|
+
ALLOWED_IMAGE_TYPES, ALLOWED_AUDIO_TYPES,
|
|
23
|
+
ROOT_DIR, ACTIVITY_FILE, MESSAGES_FILE,
|
|
24
|
+
MAX_ACTIVITY_ITEMS, MAX_SYNC_MESSAGES,
|
|
25
|
+
WAKE_WORD, TTS_VOICE_NAME, SESSION_USER,
|
|
26
|
+
OPENCLAW_CALLBACK_SECRET, REQUEST_TIMEOUT
|
|
27
|
+
} from './config.js';
|
|
28
|
+
|
|
29
|
+
// Route modules
|
|
30
|
+
import {
|
|
31
|
+
setupVoiceRoutes,
|
|
32
|
+
setupChatRoutes,
|
|
33
|
+
setupMediaRoutes,
|
|
34
|
+
setupWebhookRoutes,
|
|
35
|
+
setupConfigRoutes,
|
|
36
|
+
setupStatusRoutes,
|
|
37
|
+
setupSatelliteRoutes,
|
|
38
|
+
setupMissedMessagesRoutes,
|
|
39
|
+
addMissedMessage,
|
|
40
|
+
setupPushRoutes,
|
|
41
|
+
sendPushNotification,
|
|
42
|
+
setupSTTRoutes,
|
|
43
|
+
setupFileRoutes,
|
|
44
|
+
setupAgentRoutes,
|
|
45
|
+
setupPremiumRoutes,
|
|
46
|
+
setupArtifactsRoutes,
|
|
47
|
+
} from './routes/index.js';
|
|
48
|
+
import { initPremium } from './premium/index.js';
|
|
49
|
+
|
|
50
|
+
// ===========================================
|
|
51
|
+
// Directory Setup
|
|
52
|
+
// ===========================================
|
|
53
|
+
|
|
54
|
+
const TEMP_DIR = path.join(os.tmpdir(), 'voice-chat');
|
|
55
|
+
const UPLOADS_DIR = path.join(ROOT_DIR, 'uploads');
|
|
56
|
+
|
|
57
|
+
// ===========================================
|
|
58
|
+
// Multer configurations
|
|
59
|
+
// ===========================================
|
|
60
|
+
|
|
61
|
+
const audioUpload = multer({
|
|
62
|
+
dest: TEMP_DIR,
|
|
63
|
+
limits: { fileSize: 25 * 1024 * 1024 }
|
|
64
|
+
}).single('audio');
|
|
65
|
+
|
|
66
|
+
const imageUpload = multer({
|
|
67
|
+
dest: path.join(TEMP_DIR, 'images'),
|
|
68
|
+
limits: { fileSize: 10 * 1024 * 1024 }
|
|
69
|
+
}).single('image');
|
|
70
|
+
|
|
71
|
+
const videoUpload = multer({
|
|
72
|
+
dest: path.join(TEMP_DIR, 'videos'),
|
|
73
|
+
limits: { fileSize: 50 * 1024 * 1024 }
|
|
74
|
+
}).single('video');
|
|
75
|
+
|
|
76
|
+
const fileUpload = multer({
|
|
77
|
+
dest: path.join(TEMP_DIR, 'files'),
|
|
78
|
+
limits: { fileSize: 10 * 1024 * 1024 }
|
|
79
|
+
}).single('file');
|
|
80
|
+
|
|
81
|
+
// ===========================================
|
|
82
|
+
// Message Sync Helper (sequential write queue to prevent race conditions)
|
|
83
|
+
// ===========================================
|
|
84
|
+
|
|
85
|
+
let syncWriteQueue = Promise.resolve();
|
|
86
|
+
|
|
87
|
+
export async function saveMessageToSync(role, content, timestamp = new Date().toISOString()) {
|
|
88
|
+
syncWriteQueue = syncWriteQueue.then(async () => {
|
|
89
|
+
try {
|
|
90
|
+
let messages = [];
|
|
91
|
+
try {
|
|
92
|
+
const fileContent = await fs.readFile(MESSAGES_FILE, 'utf8');
|
|
93
|
+
messages = JSON.parse(fileContent);
|
|
94
|
+
} catch (readError) { /* File may not exist yet */ }
|
|
95
|
+
|
|
96
|
+
messages.push({
|
|
97
|
+
id: Date.now(),
|
|
98
|
+
role,
|
|
99
|
+
content,
|
|
100
|
+
timestamp
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (messages.length > MAX_SYNC_MESSAGES) {
|
|
104
|
+
messages = messages.slice(-MAX_SYNC_MESSAGES);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await fs.writeFile(MESSAGES_FILE, JSON.stringify(messages, null, 2));
|
|
108
|
+
} catch (err) {
|
|
109
|
+
log('error', 'Failed to save message to sync:', err.message);
|
|
110
|
+
}
|
|
111
|
+
}).catch(err => {
|
|
112
|
+
log('error', 'Sync write queue error:', err.message);
|
|
113
|
+
});
|
|
114
|
+
return syncWriteQueue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ===========================================
|
|
118
|
+
// Setup Routes - Main Orchestrator
|
|
119
|
+
// ===========================================
|
|
120
|
+
|
|
121
|
+
export function setupRoutes(app, requestHelpers) {
|
|
122
|
+
// Shared configuration object
|
|
123
|
+
const config = {
|
|
124
|
+
GATEWAY_URL,
|
|
125
|
+
GATEWAY_TOKEN,
|
|
126
|
+
ALLOWED_IMAGE_TYPES,
|
|
127
|
+
ALLOWED_AUDIO_TYPES,
|
|
128
|
+
ACTIVITY_FILE,
|
|
129
|
+
MESSAGES_FILE,
|
|
130
|
+
MAX_ACTIVITY_ITEMS,
|
|
131
|
+
MAX_SYNC_MESSAGES,
|
|
132
|
+
WAKE_WORD,
|
|
133
|
+
TTS_VOICE_NAME,
|
|
134
|
+
SESSION_USER,
|
|
135
|
+
OPENCLAW_CALLBACK_SECRET,
|
|
136
|
+
REQUEST_TIMEOUT,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Create standardized request context for all route modules
|
|
140
|
+
const context = createRequestContext({
|
|
141
|
+
// Configuration
|
|
142
|
+
config,
|
|
143
|
+
|
|
144
|
+
// Core utilities
|
|
145
|
+
log,
|
|
146
|
+
fetchWithTimeout,
|
|
147
|
+
|
|
148
|
+
// Chat functions
|
|
149
|
+
transcribe,
|
|
150
|
+
chat,
|
|
151
|
+
chatWithParallelTTS,
|
|
152
|
+
generateTTS,
|
|
153
|
+
sendMessage,
|
|
154
|
+
saveMessageToSync,
|
|
155
|
+
|
|
156
|
+
// WebSocket broadcasting
|
|
157
|
+
broadcastOpenClawPush,
|
|
158
|
+
broadcastToAll,
|
|
159
|
+
wsClients,
|
|
160
|
+
|
|
161
|
+
// Middleware
|
|
162
|
+
verifyWebhookToken,
|
|
163
|
+
strictLimiter,
|
|
164
|
+
isPrivateIP,
|
|
165
|
+
|
|
166
|
+
// Config management
|
|
167
|
+
loadConfig,
|
|
168
|
+
getClientConfig,
|
|
169
|
+
saveConfig,
|
|
170
|
+
needsOnboarding,
|
|
171
|
+
|
|
172
|
+
// File uploads
|
|
173
|
+
audioUpload,
|
|
174
|
+
imageUpload,
|
|
175
|
+
videoUpload,
|
|
176
|
+
fileUpload,
|
|
177
|
+
|
|
178
|
+
// Directories
|
|
179
|
+
uploadsDir: UPLOADS_DIR,
|
|
180
|
+
tempDir: TEMP_DIR,
|
|
181
|
+
|
|
182
|
+
// Request tracking
|
|
183
|
+
requestHelpers,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Initialize premium from stored license key
|
|
187
|
+
initPremium();
|
|
188
|
+
|
|
189
|
+
// Initialize all route modules with standardized context
|
|
190
|
+
setupPremiumRoutes(app, context);
|
|
191
|
+
setupStatusRoutes(app, context);
|
|
192
|
+
setupSatelliteRoutes(app, context);
|
|
193
|
+
setupVoiceRoutes(app, context);
|
|
194
|
+
setupChatRoutes(app, context);
|
|
195
|
+
setupMediaRoutes(app, context);
|
|
196
|
+
setupWebhookRoutes(app, context);
|
|
197
|
+
setupConfigRoutes(app, context);
|
|
198
|
+
setupMissedMessagesRoutes(app, context);
|
|
199
|
+
setupPushRoutes(app, context);
|
|
200
|
+
setupSTTRoutes(app, context);
|
|
201
|
+
setupFileRoutes(app, context);
|
|
202
|
+
setupAgentRoutes(app, context);
|
|
203
|
+
setupArtifactsRoutes(app, context);
|
|
204
|
+
|
|
205
|
+
log('info', 'Routes initialized: premium, status, satellite, voice, chat, media, webhooks, config, missed-messages, push, stt, files, agents, artifacts');
|
|
206
|
+
}
|