getzero11 commited on
Commit
2592650
·
verified ·
1 Parent(s): 2bf73d1

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +149 -193
server.js CHANGED
@@ -1,56 +1,62 @@
1
  import express from "express";
2
  import { spawn } from "child_process";
3
  import dotenv from "dotenv";
 
 
 
4
  dotenv.config();
5
-
6
- const app = express();
7
- app.use(express.json());
8
-
9
- /* ===============================
10
- 1. BASIC AUTH
11
- ================================ */
12
-
13
- const GATE_KEY = process.env.OPENCLAW_GATE_KEY;
14
-
15
- app.use((req, res, next) => {
16
- if (!GATE_KEY) return next();
17
- if (req.headers["x-openclaw-key"] !== GATE_KEY) {
18
- return res.status(401).json({ error: "Unauthorized" });
19
- }
20
- next();
21
- });
22
-
23
- /* ===============================
24
- 2. RATE LIMITING (IP-based)
25
- ================================ */
26
-
27
- const RATE_LIMIT = 30; // requests
28
- const WINDOW_MS = 60_000;
29
- const ipHits = new Map();
30
-
31
- app.use((req, res, next) => {
32
- const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
33
- const now = Date.now();
34
-
35
- const record = ipHits.get(ip) || { count: 0, ts: now };
36
- if (now - record.ts > WINDOW_MS) {
37
- record.count = 0;
38
- record.ts = now;
39
- }
40
-
41
- record.count++;
42
- ipHits.set(ip, record);
43
-
44
- if (record.count > RATE_LIMIT) {
45
- return res.status(429).json({ error: "Rate limit exceeded" });
46
- }
47
- next();
48
- });
49
-
50
- /* ===============================
51
- 3. LOAD KEY POOLS
52
- ================================ */
53
-
 
 
 
54
  function loadPool(prefix) {
55
  const keys = Object.keys(process.env)
56
  .filter(k => k.startsWith(prefix))
@@ -60,102 +66,51 @@ function loadPool(prefix) {
60
  console.log(`Loaded ${keys.length} keys for ${prefix}`);
61
  return keys;
62
  }
63
-
64
- const PROVIDERS = {
65
- openai: loadPool("OPENAI_API_KEY_"),
66
- deepseek: loadPool("DEEPSEEK_API_KEY_"),
67
- gemini: loadPool("GEMINI_API_KEY_"),
68
- openrouter: loadPool("OPENROUTER_API_KEY_"),
69
- dashscope: loadPool("DASHSCOPE_API_KEY_")
70
- };
71
-
72
- function randomKey(pool) {
73
- return pool[Math.floor(Math.random() * pool.length)];
74
- }
75
-
76
- /* ===============================
77
- 4. TASK ROUTING
78
- ================================ */
79
-
80
- const TASKS = new Set([
81
- "market_research",
82
- "summarize",
83
- "classify"
84
- ]);
85
-
86
- /* ===============================
87
- 5. RUN AGENT
88
- ================================ */
89
- app.use((req, res, next) => {
90
- if (req.method === "POST" && !req.is("application/json")) {
91
- return res.status(400).json({ error: "JSON body required" });
92
- }
93
- next();
94
- });
95
-
96
- app.get("/", (req, res) => {
97
- res.json({
98
- status: "OpenClaw running",
99
- service: "market_research_agent"
100
- });
101
- });
102
-
103
- app.post("/run", async (req, res) => {
104
- console.log('OpenClaw: Received request:', {
105
- task: req.body.task,
106
- keyword: req.body.keyword,
107
- timestamp: new Date().toISOString()
108
- });
109
-
110
- const { task = "market_research", provider, model, ...payload } = req.body;
111
-
112
- if (!TASKS.has(task)) {
113
- return res.status(400).json({ error: "Unknown task" });
114
- }
115
-
116
- const providersToTry = provider
117
- ? [provider, ...Object.keys(PROVIDERS).filter(p => p !== provider)]
118
- : Object.keys(PROVIDERS);
119
-
120
- let lastError;
121
 
122
- for (const p of providersToTry) {
123
- const pool = PROVIDERS[p];
124
- if (!pool || pool.length === 0) continue;
125
-
126
- for (let i = 0; i < pool.length; i++) {
127
- const apiKey = randomKey(pool);
128
-
129
- const env = {
130
- ...process.env,
131
- OPENCLAW_PROVIDER: p,
132
- OPENCLAW_MODEL: model || "",
133
- OPENCLAW_API_KEY: apiKey,
134
- OPENCLAW_TASK: task,
135
- OPENCLAW_TIMEOUT: "180000"
136
  };
137
 
138
- try {
139
- const result = await runOpenClaw(env, payload);
140
- return res.json(result);
141
- } catch (err) {
142
- lastError = err;
143
- if (!isRateLimit(err)) break;
144
- }
145
- }
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
 
 
147
 
148
- res.status(500).json({
149
- error: "All providers failed",
150
- details: lastError?.message,
151
- keyword: req.body.keyword,
 
152
  timestamp: new Date().toISOString()
153
  });
154
  });
155
 
156
- // Add specific market research endpoint for n8n compatibility
157
  app.post("/api/market-research", async (req, res) => {
158
- console.log('OpenClaw: Market research request:', {
159
  keyword: req.body.keyword,
160
  timestamp: new Date().toISOString()
161
  });
@@ -176,7 +131,7 @@ app.post("/api/market-research", async (req, res) => {
176
  apiKeys[provider] = [api_key];
177
  providersToTry = [provider];
178
  } else {
179
- // Fallback to environment variables (for local testing)
180
  console.log('OpenClaw: Using API keys from environment');
181
  providersToTry = Object.keys(PROVIDERS);
182
  apiKeys = PROVIDERS;
@@ -203,7 +158,7 @@ app.post("/api/market-research", async (req, res) => {
203
  };
204
 
205
  try {
206
- console.log(`OpenClaw: Trying provider ${p} with key ${apiKey.substring(0, 10)}...`);
207
  const result = await runOpenClaw(env, { keyword });
208
  console.log('OpenClaw: Success with provider', p);
209
  return res.json(result);
@@ -222,34 +177,35 @@ app.post("/api/market-research", async (req, res) => {
222
  timestamp: new Date().toISOString()
223
  });
224
  });
225
-
226
- /* ===============================
227
- 6. EXECUTION + JSON PARSE
228
- ================================ */
229
-
230
- function runOpenClaw(env, payload) {
231
- return new Promise((resolve, reject) => {
232
- const proc = spawn(process.execPath, ["src/index.js"], {
233
- cwd: "/app",
234
- env
235
- });
236
-
237
- let stdout = "";
238
- let stderr = "";
239
-
240
- // CRITICAL: handle spawn errors
241
- proc.on("error", err => {
242
- reject(err);
243
- });
244
-
245
- proc.stdout.on("data", data => {
246
- stdout += data.toString();
247
- });
248
-
249
- proc.stderr.on("data", data => {
250
- stderr += data.toString();
251
- });
252
-
 
253
  proc.on("close", code => {
254
  if (code !== 0) {
255
  console.error('OpenClaw: Agent exited with code:', code);
@@ -260,40 +216,40 @@ function runOpenClaw(env, payload) {
260
  }
261
 
262
  try {
263
- console.log('OpenClaw: Agent stdout (first 500 chars):', stdout.substring(0, 500));
264
  const json = JSON.parse(stdout);
265
  console.log('OpenClaw: JSON parsed successfully');
266
  resolve(json);
267
  } catch (err) {
268
  console.error('OpenClaw: JSON parse error:', err.message);
269
- console.error('OpenClaw: Raw stdout:', stdout);
270
  reject(new Error("Invalid JSON from OpenClaw agent: " + err.message));
271
  }
272
  });
273
-
274
- // SEND INPUT TO AGENT
275
- proc.stdin.write(JSON.stringify(payload));
276
- proc.stdin.end();
277
- });
278
- }
279
-
280
-
281
- function isRateLimit(err) {
282
- const msg = err.message?.toLowerCase() || "";
283
- return (
284
- msg.includes("429") ||
285
- msg.includes("rate") ||
286
- msg.includes("quota") ||
287
- msg.includes("limit")
288
- );
289
- }
290
-
291
-
292
- /* ===============================
293
- 7. START
294
- ================================ */
295
-
296
- const PORT = process.env.PORT || 3000;
297
- app.listen(PORT, () => {
298
  console.log(`🚀 OpenClaw Agent Gateway running on port ${PORT}`);
299
- });
 
 
 
1
  import express from "express";
2
  import { spawn } from "child_process";
3
  import dotenv from "dotenv";
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
  dotenv.config();
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ const app = express();
13
+ app.use(express.json());
14
+
15
+ /* ===============================
16
+ 1. BASIC AUTH
17
+ =============================== */
18
+
19
+ const GATE_KEY = process.env.OPENCLAW_GATE_KEY;
20
+
21
+ app.use((req, res, next) => {
22
+ if (!GATE_KEY) return next();
23
+ if (req.headers["x-openclaw-key"] !== GATE_KEY) {
24
+ return res.status(401).json({ error: "Unauthorized" });
25
+ }
26
+ next();
27
+ });
28
+
29
+ /* ===============================
30
+ 2. RATE LIMITING (IP-based)
31
+ =============================== */
32
+
33
+ const RATE_LIMIT = 30; // requests
34
+ const WINDOW_MS = 60_000;
35
+ const ipHits = new Map();
36
+
37
+ app.use((req, res, next) => {
38
+ const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
39
+ const now = Date.now();
40
+
41
+ const record = ipHits.get(ip) || { count: 0, ts: now };
42
+ if (now - record.ts > WINDOW_MS) {
43
+ record.count = 0;
44
+ record.ts = now;
45
+ }
46
+
47
+ record.count++;
48
+ ipHits.set(ip, record);
49
+
50
+ if (record.count > RATE_LIMIT) {
51
+ return res.status(429).json({ error: "Rate limit exceeded" });
52
+ }
53
+ next();
54
+ });
55
+
56
+ /* ===============================
57
+ 3. LOAD KEY POOLS
58
+ =============================== */
59
+
60
  function loadPool(prefix) {
61
  const keys = Object.keys(process.env)
62
  .filter(k => k.startsWith(prefix))
 
66
  console.log(`Loaded ${keys.length} keys for ${prefix}`);
67
  return keys;
68
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
+ const PROVIDERS = {
71
+ openai: loadPool("OPENAI_API_KEY_"),
72
+ deepseek: loadPool("DEEPSEEK_API_KEY_"),
73
+ gemini: loadPool("GEMINI_API_KEY_"),
74
+ openrouter: loadPool("OPENROUTER_API_KEY_"),
75
+ dashscope: loadPool("DASHSCOPE_API_KEY_")
 
 
 
 
 
 
 
 
76
  };
77
 
78
+ function randomKey(pool) {
79
+ return pool[Math.floor(Math.random() * pool.length)];
80
+ }
81
+
82
+ /* ===============================
83
+ 4. TASK ROUTING
84
+ =============================== */
85
+
86
+ const TASKS = new Set([
87
+ "market_research",
88
+ "summarize",
89
+ "classify"
90
+ ]);
91
+
92
+ /* ===============================
93
+ 5. RUN AGENT
94
+ =============================== */
95
+ app.use((req, res, next) => {
96
+ if (req.method === "POST" && !req.is("application/json")) {
97
+ return res.status(400).json({ error: "JSON body required" });
98
  }
99
+ next();
100
+ });
101
 
102
+ app.get("/", (req, res) => {
103
+ res.json({
104
+ status: "OpenClaw running on HF Space",
105
+ service: "market_research_agent",
106
+ endpoints: ["/api/market-research", "/run"],
107
  timestamp: new Date().toISOString()
108
  });
109
  });
110
 
111
+ // Market research endpoint for n8n
112
  app.post("/api/market-research", async (req, res) => {
113
+ console.log('OpenClaw HF Space: Market research request:', {
114
  keyword: req.body.keyword,
115
  timestamp: new Date().toISOString()
116
  });
 
131
  apiKeys[provider] = [api_key];
132
  providersToTry = [provider];
133
  } else {
134
+ // Fallback to environment variables
135
  console.log('OpenClaw: Using API keys from environment');
136
  providersToTry = Object.keys(PROVIDERS);
137
  apiKeys = PROVIDERS;
 
158
  };
159
 
160
  try {
161
+ console.log(`OpenClaw: Trying provider ${p}`);
162
  const result = await runOpenClaw(env, { keyword });
163
  console.log('OpenClaw: Success with provider', p);
164
  return res.json(result);
 
177
  timestamp: new Date().toISOString()
178
  });
179
  });
180
+
181
+ /* ===============================
182
+ 6. EXECUTION + JSON PARSE (HF Space compatible)
183
+ =============================== */
184
+
185
+ function runOpenClaw(env, payload) {
186
+ return new Promise((resolve, reject) => {
187
+ // Use current directory for HF Space
188
+ const proc = spawn(process.execPath, ["src/index.js"], {
189
+ cwd: __dirname,
190
+ env
191
+ });
192
+
193
+ let stdout = "";
194
+ let stderr = "";
195
+
196
+ proc.on("error", err => {
197
+ console.error('OpenClaw: Spawn error:', err);
198
+ reject(err);
199
+ });
200
+
201
+ proc.stdout.on("data", data => {
202
+ stdout += data.toString();
203
+ });
204
+
205
+ proc.stderr.on("data", data => {
206
+ stderr += data.toString();
207
+ });
208
+
209
  proc.on("close", code => {
210
  if (code !== 0) {
211
  console.error('OpenClaw: Agent exited with code:', code);
 
216
  }
217
 
218
  try {
219
+ console.log('OpenClaw: Agent response length:', stdout.length);
220
  const json = JSON.parse(stdout);
221
  console.log('OpenClaw: JSON parsed successfully');
222
  resolve(json);
223
  } catch (err) {
224
  console.error('OpenClaw: JSON parse error:', err.message);
225
+ console.error('OpenClaw: Raw stdout (first 500 chars):', stdout.substring(0, 500));
226
  reject(new Error("Invalid JSON from OpenClaw agent: " + err.message));
227
  }
228
  });
229
+
230
+ // Send input to agent
231
+ proc.stdin.write(JSON.stringify(payload));
232
+ proc.stdin.end();
233
+ });
234
+ }
235
+
236
+ function isRateLimit(err) {
237
+ const msg = err.message?.toLowerCase() || "";
238
+ return (
239
+ msg.includes("429") ||
240
+ msg.includes("rate") ||
241
+ msg.includes("quota") ||
242
+ msg.includes("limit")
243
+ );
244
+ }
245
+
246
+ /* ===============================
247
+ 7. START SERVER (HF Space uses port 7860)
248
+ =============================== */
249
+
250
+ const PORT = process.env.PORT || 7860;
251
+ app.listen(PORT, "0.0.0.0", () => {
 
 
252
  console.log(`🚀 OpenClaw Agent Gateway running on port ${PORT}`);
253
+ console.log(`📡 Endpoint: /api/market-research`);
254
+ console.log(`🔑 Providers available: ${Object.keys(PROVIDERS).join(', ')}`);
255
+ });