pi-harness-runtime 1.1.374 → 1.1.375

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.
Files changed (118) hide show
  1. package/package.json +1 -1
  2. package/packages/a2a-adapter/package.json +1 -1
  3. package/packages/agent-policy/package.json +1 -1
  4. package/packages/architecture-generator/package.json +1 -1
  5. package/packages/auth/package.json +1 -1
  6. package/packages/autonomous-refactor/package.json +1 -1
  7. package/packages/autonomous-runtime/package.json +1 -1
  8. package/packages/cache-strategy/package.json +1 -1
  9. package/packages/capability-registry/package.json +1 -1
  10. package/packages/checkpoint/package.json +1 -1
  11. package/packages/cli-plugin-sdk/package.json +1 -1
  12. package/packages/clipboard/package.json +1 -1
  13. package/packages/clipboard-plugin/package.json +1 -1
  14. package/packages/code-generation/package.json +1 -1
  15. package/packages/code-review/package.json +1 -1
  16. package/packages/codex-adapter/package.json +1 -1
  17. package/packages/codex-watcher/package.json +1 -1
  18. package/packages/config-capture/package.json +1 -1
  19. package/packages/connection-runtime/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/contracts/package.json +1 -1
  24. package/packages/cookie-sanitizer/package.json +1 -1
  25. package/packages/cost-optimizer/package.json +1 -1
  26. package/packages/dependency-analyzer/package.json +1 -1
  27. package/packages/django-plugin/package.json +1 -1
  28. package/packages/doc-generator/package.json +1 -1
  29. package/packages/durable-review/package.json +1 -1
  30. package/packages/e2e-tester/package.json +1 -1
  31. package/packages/evaluation-engine/package.json +1 -1
  32. package/packages/evaluation-runner/package.json +1 -1
  33. package/packages/event-bus/package.json +1 -1
  34. package/packages/event-sink/package.json +1 -1
  35. package/packages/event-store/package.json +1 -1
  36. package/packages/experience-replay/package.json +1 -1
  37. package/packages/feedback-collector/package.json +1 -1
  38. package/packages/file-copy-helper/package.json +1 -1
  39. package/packages/framework-detector/package.json +1 -1
  40. package/packages/framework-plugin-sdk/package.json +1 -1
  41. package/packages/frappe-plugin/package.json +1 -1
  42. package/packages/generic-web-plugin/package.json +1 -1
  43. package/packages/health-monitor/package.json +1 -1
  44. package/packages/intent-analyzer/package.json +1 -1
  45. package/packages/jev-judge/package.json +1 -1
  46. package/packages/knowledge-graph/package.json +1 -1
  47. package/packages/knowledge-retrieval/package.json +1 -1
  48. package/packages/laravel-plugin/package.json +1 -1
  49. package/packages/learning-engine/package.json +1 -1
  50. package/packages/mcp-adapter/package.json +1 -1
  51. package/packages/memory-engine/package.json +1 -1
  52. package/packages/milestone-manager/package.json +1 -1
  53. package/packages/model-registry/package.json +1 -1
  54. package/packages/nextjs-plugin/package.json +1 -1
  55. package/packages/notification/dist/adapters/telegram-adapter.d.ts +5 -0
  56. package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
  57. package/packages/notification/dist/adapters/telegram-adapter.js +23 -2
  58. package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
  59. package/packages/notification/dist/notification-center.d.ts.map +1 -1
  60. package/packages/notification/dist/notification-center.js +5 -1
  61. package/packages/notification/dist/notification-center.js.map +1 -1
  62. package/packages/notification/package.json +1 -1
  63. package/packages/notification/src/adapters/telegram-adapter.ts +26 -5
  64. package/packages/notification/src/notification-center.ts +5 -1
  65. package/packages/observability/package.json +1 -1
  66. package/packages/okf-indexer/package.json +1 -1
  67. package/packages/paperclip-integration/package.json +1 -1
  68. package/packages/performance-optimizer/package.json +1 -1
  69. package/packages/permission-mode/package.json +1 -1
  70. package/packages/privilege-broker/package.json +1 -1
  71. package/packages/project-analyzer/package.json +1 -1
  72. package/packages/project-bootstrap/package.json +1 -1
  73. package/packages/projection-engine/package.json +1 -1
  74. package/packages/prompt-compiler/package.json +1 -1
  75. package/packages/prompt-versioning/package.json +1 -1
  76. package/packages/provider-adapter-sdk/package.json +1 -1
  77. package/packages/provider-router/package.json +1 -1
  78. package/packages/provider-selector/package.json +1 -1
  79. package/packages/providers/package.json +1 -1
  80. package/packages/qdrant-skills/package.json +1 -1
  81. package/packages/quota-manager/package.json +1 -1
  82. package/packages/rate-limiter/package.json +1 -1
  83. package/packages/react-vite-plugin/package.json +1 -1
  84. package/packages/relay/package.json +1 -1
  85. package/packages/release-manager/package.json +1 -1
  86. package/packages/requirement-compiler/package.json +1 -1
  87. package/packages/runtime/package.json +1 -1
  88. package/packages/scheduler/package.json +1 -1
  89. package/packages/scheduler-adapter/package.json +1 -1
  90. package/packages/session/package.json +1 -1
  91. package/packages/session-api/package.json +1 -1
  92. package/packages/session-export/package.json +1 -1
  93. package/packages/shared-context/package.json +1 -1
  94. package/packages/skill-mcp-client/package.json +1 -1
  95. package/packages/skill-registry/package.json +1 -1
  96. package/packages/skills/package.json +1 -1
  97. package/packages/sprint-planner/package.json +1 -1
  98. package/packages/subscription-engine/package.json +1 -1
  99. package/packages/task-compiler/package.json +1 -1
  100. package/packages/telegram/package.json +1 -1
  101. package/packages/tencentdb-memory/package.json +1 -1
  102. package/packages/tencentdb-sync/package.json +1 -1
  103. package/packages/test-data-generator/package.json +1 -1
  104. package/packages/test-generator/package.json +1 -1
  105. package/packages/todo-bd-sync/package.json +1 -1
  106. package/packages/token-estimation/package.json +1 -1
  107. package/packages/token-optimizer/package.json +1 -1
  108. package/packages/trajectory/package.json +1 -1
  109. package/packages/tui/package.json +1 -1
  110. package/packages/types/package.json +1 -1
  111. package/packages/voice-api/package.json +1 -1
  112. package/packages/webmcp-tester/package.json +1 -1
  113. package/packages/wiki/package.json +1 -1
  114. package/packages/workflow-events/package.json +1 -1
  115. package/packages/workspace-scanner/package.json +1 -1
  116. package/packages/worktree/package.json +1 -1
  117. package/packages/write-review/package.json +1 -1
  118. package/scripts/unified-webhook-server.ts +331 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/task-compiler",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Task Compiler for pi-harness-runtime — produces deterministic task DAG from CompiledRequirement",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/telegram",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "pi-harness-runtime — Telegram inbound/outbound",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/tencentdb-memory",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "TencentDB Agent Memory client - sync skills to centralized knowledge base",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tencentdb-sync",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Sync SKILL.md skills to TencentDB-Agent-Memory server (RFC-0105/0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/test-data-generator",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Test data generation for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/test-generator",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Auto-Test Generator — generate unit and E2E tests from source code (RFC-0013)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/todo-bd-sync",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Two-way sync between rpiv-todo overlay and bd issue tracker",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-estimation",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "pi-harness-runtime — token-estimation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-optimizer",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Token Budget Optimizer — maximize prompt value within token limits (RFC-0011)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/trajectory",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Trajectory capture for the write-review loop — persists cycle records for learning",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "pi-harness-runtime — tui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/types",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/voice-api",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "type": "module",
5
5
  "description": "Voice-to-text API using Whisper for Telegram and LINE integration",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/webmcp-tester",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/wiki",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "LLM Wiki integration for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/write-review",
3
- "version": "1.1.374",
3
+ "version": "1.1.375",
4
4
  "description": "Two-agent write-review loop with blackboard coordination",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Unified Webhook Server — Handles both Telegram and LINE on single domain
4
+ *
5
+ * Routes:
6
+ * /telegram → Telegram webhook handler
7
+ * /line → LINE webhook handler
8
+ *
9
+ * Usage:
10
+ * bun run scripts/unified-webhook-server.ts
11
+ *
12
+ * Environment variables (or keys/*.txt files):
13
+ * TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET, TELEGRAM_ALLOWED_USERS
14
+ * LINE_CHANNEL_SECRET, LINE_ACCESS_TOKEN
15
+ *
16
+ * Webhook URLs to configure:
17
+ * Telegram: https://webhook.bunchee.online/telegram
18
+ * LINE: https://webhook.bunchee.online/line
19
+ */
20
+
21
+ import { existsSync, readFileSync, appendFileSync } from "node:fs";
22
+ import { join } from "node:path";
23
+ import { createServer, IncomingMessage, ServerResponse } from "node:http";
24
+
25
+ // --- Configuration ---
26
+ interface Config {
27
+ telegram?: {
28
+ botToken: string;
29
+ secret: string;
30
+ allowedUsers: Set<number>;
31
+ };
32
+ line?: {
33
+ channelSecret: string;
34
+ accessToken: string;
35
+ };
36
+ }
37
+
38
+ function getConfig(): Config {
39
+ const homedir = process.env.HOME || "/home/frappe";
40
+ const keysDir = join(homedir, ".pi-harness-runtime", "keys");
41
+ const config: Config = {};
42
+
43
+ // Telegram config
44
+ const telegramTokenPath = join(keysDir, "telegram-bot-token.txt");
45
+ const telegramSecretPath = join(keysDir, "telegram-webhook-secret.txt");
46
+ const telegramUsersPath = join(keysDir, "telegram-allowed-users.txt");
47
+
48
+ if (existsSync(telegramTokenPath)) {
49
+ const token = process.env.TELEGRAM_BOT_TOKEN || readFileSync(telegramTokenPath, "utf8").trim();
50
+ const secret = process.env.TELEGRAM_WEBHOOK_SECRET || (existsSync(telegramSecretPath) ? readFileSync(telegramSecretPath, "utf8").trim() : "");
51
+
52
+ const allowedUsers = new Set<number>();
53
+ const envUsers = process.env.TELEGRAM_ALLOWED_USERS;
54
+ if (envUsers) {
55
+ for (const part of envUsers.split(",")) {
56
+ const num = parseInt(part.trim(), 10);
57
+ if (!isNaN(num) && num > 0) allowedUsers.add(num);
58
+ }
59
+ }
60
+ if (existsSync(telegramUsersPath)) {
61
+ const content = readFileSync(telegramUsersPath, "utf8").trim();
62
+ for (const part of content.split(",")) {
63
+ const num = parseInt(part.trim(), 10);
64
+ if (!isNaN(num) && num > 0) allowedUsers.add(num);
65
+ }
66
+ }
67
+
68
+ config.telegram = { botToken: token, secret, allowedUsers };
69
+ }
70
+
71
+ // LINE config
72
+ const lineSecretPath = join(keysDir, "line-channel-secret.txt");
73
+ const lineTokenPath = join(keysDir, "line-access-token.txt");
74
+
75
+ if (existsSync(lineSecretPath) && existsSync(lineTokenPath)) {
76
+ const channelSecret = process.env.LINE_CHANNEL_SECRET || readFileSync(lineSecretPath, "utf8").trim();
77
+ const accessToken = process.env.LINE_ACCESS_TOKEN || readFileSync(lineTokenPath, "utf8").trim();
78
+ config.line = { channelSecret, accessToken };
79
+ }
80
+
81
+ return config;
82
+ }
83
+
84
+ // --- Logging ---
85
+ const LOG_FILE = "/tmp/unified-webhook.log";
86
+ function log(prefix: string, ...args: unknown[]) {
87
+ const msg = args.map(a => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ");
88
+ const line = `[${new Date().toISOString()}] [${prefix}] ${msg}\n`;
89
+ appendFileSync(LOG_FILE, line);
90
+ console.log(line.trim());
91
+ }
92
+
93
+ // --- Crypto utilities ---
94
+ async function verifyTelegram(data: string, secret: string, hash: string): Promise<boolean> {
95
+ const encoder = new TextEncoder();
96
+ const key = await crypto.subtle.importKey(
97
+ "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]
98
+ );
99
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
100
+ const expected = Array.from(new Uint8Array(signature)).map(b => b.toString(16).padStart(2, "0")).join("");
101
+ return expected === hash;
102
+ }
103
+
104
+ async function verifyLINE(body: string, channelSecret: string, xLineSignature: string): Promise<boolean> {
105
+ const encoder = new TextEncoder();
106
+ const key = await crypto.subtle.importKey(
107
+ "raw", encoder.encode(channelSecret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]
108
+ );
109
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
110
+ const expected = Buffer.from(signature).toString("base64");
111
+ return expected === xLineSignature;
112
+ }
113
+
114
+ // --- Callback queue ---
115
+ const QUEUE_FILE = join(process.env.HOME || "/tmp", ".pi-harness-runtime", "callback-queue.jsonl");
116
+ function queueCallback(callback: Record<string, unknown>) {
117
+ appendFileSync(QUEUE_FILE, JSON.stringify({ ...callback, timestamp: new Date().toISOString() }) + "\n");
118
+ }
119
+
120
+ // --- Telegram handler ---
121
+ async function handleTelegram(req: IncomingMessage, res: ServerResponse, config: Config) {
122
+ if (!config.telegram) {
123
+ res.writeHead(404);
124
+ res.end("Telegram not configured");
125
+ return;
126
+ }
127
+
128
+ // Verify secret token
129
+ const secretToken = req.headers["x-telegram-bot-api-secret-token"];
130
+ if (secretToken !== config.telegram.secret) {
131
+ log("Telegram", "Unauthorized - secret mismatch");
132
+ res.writeHead(403);
133
+ res.end("Forbidden");
134
+ return;
135
+ }
136
+
137
+ // Collect body
138
+ const chunks: Buffer[] = [];
139
+ for await (const chunk of req) chunks.push(chunk);
140
+ const body = Buffer.concat(chunks).toString();
141
+
142
+ try {
143
+ const update = JSON.parse(body);
144
+
145
+ // Handle callback query (button click)
146
+ if (update.callback_query) {
147
+ const query = update.callback_query;
148
+ const from = query.from;
149
+
150
+ // Check authorization
151
+ if (config.telegram.allowedUsers.size > 0 && !config.telegram.allowedUsers.has(from.id)) {
152
+ log("Telegram", `Unauthorized user: ${from.id}`);
153
+ res.writeHead(403);
154
+ res.end("Forbidden");
155
+ return;
156
+ }
157
+
158
+ // Parse callback data
159
+ const data = query.data || "";
160
+ const colonIdx = data.indexOf(":");
161
+ const underscoreIdx = data.indexOf("_");
162
+ let action: string, targetId: string;
163
+ if (colonIdx !== -1) {
164
+ action = data.slice(0, colonIdx);
165
+ targetId = data.slice(colonIdx + 1).replace(/_+$/, "");
166
+ } else if (underscoreIdx !== -1) {
167
+ action = data.slice(0, underscoreIdx);
168
+ targetId = data.slice(underscoreIdx + 1).replace(/_+$/, "");
169
+ } else {
170
+ action = data;
171
+ targetId = "";
172
+ }
173
+
174
+ // Queue the callback
175
+ queueCallback({
176
+ type: "telegram_callback",
177
+ action,
178
+ targetId,
179
+ userId: from.id,
180
+ username: from.username,
181
+ messageId: query.message?.message_id,
182
+ });
183
+
184
+ // Acknowledge immediately
185
+ await fetch(`https://api.telegram.org/bot${config.telegram.botToken}/answerCallbackQuery`, {
186
+ method: "POST",
187
+ headers: { "Content-Type": "application/json" },
188
+ body: JSON.stringify({ callback_query_id: query.id }),
189
+ });
190
+
191
+ log("Telegram", `Callback: ${action} ${targetId} from @${from.username || from.id}`);
192
+ }
193
+
194
+ res.writeHead(200);
195
+ res.end("OK");
196
+ } catch (err) {
197
+ log("Telegram", "Error:", err);
198
+ res.writeHead(500);
199
+ res.end("Error");
200
+ }
201
+ }
202
+
203
+ // --- LINE handler ---
204
+ async function handleLINE(req: IncomingMessage, res: ServerResponse, config: Config) {
205
+ if (!config.line) {
206
+ res.writeHead(404);
207
+ res.end("LINE not configured");
208
+ return;
209
+ }
210
+
211
+ // Verify signature
212
+ const signature = req.headers["x-line-signature"] as string;
213
+ if (!signature) {
214
+ log("LINE", "Missing signature");
215
+ res.writeHead(400);
216
+ res.end("Bad Request");
217
+ return;
218
+ }
219
+
220
+ // Collect body
221
+ const chunks: Buffer[] = [];
222
+ for await (const chunk of req) chunks.push(chunk);
223
+ const body = Buffer.concat(chunks).toString();
224
+
225
+ // Verify
226
+ if (!(await verifyLINE(body, config.line.channelSecret, signature))) {
227
+ log("LINE", "Invalid signature");
228
+ res.writeHead(403);
229
+ res.end("Forbidden");
230
+ return;
231
+ }
232
+
233
+ try {
234
+ const events = JSON.parse(body).events || [];
235
+
236
+ for (const event of events) {
237
+ if (event.type === "postback") {
238
+ // Parse postback data
239
+ const data = event.postback?.data || "";
240
+ const colonIdx = data.indexOf(":");
241
+ let action: string, targetId: string;
242
+ if (colonIdx !== -1) {
243
+ action = data.slice(0, colonIdx);
244
+ targetId = data.slice(colonIdx + 1);
245
+ } else {
246
+ action = data;
247
+ targetId = "";
248
+ }
249
+
250
+ queueCallback({
251
+ type: "line_callback",
252
+ action,
253
+ targetId,
254
+ userId: event.source?.userId,
255
+ replyToken: event.replyToken,
256
+ });
257
+
258
+ log("LINE", `Postback: ${action} ${targetId}`);
259
+ } else if (event.type === "message" && event.message?.type === "text") {
260
+ // Forward text messages to agent
261
+ queueCallback({
262
+ type: "line_message",
263
+ text: event.message.text,
264
+ userId: event.source?.userId,
265
+ replyToken: event.replyToken,
266
+ });
267
+
268
+ log("LINE", `Message: ${event.message.text}`);
269
+ }
270
+ }
271
+
272
+ res.writeHead(200);
273
+ res.end("OK");
274
+ } catch (err) {
275
+ log("LINE", "Error:", err);
276
+ res.writeHead(500);
277
+ res.end("Error");
278
+ }
279
+ }
280
+
281
+ // --- Main server ---
282
+ const PORT = parseInt(process.env.PORT || "3000", 10);
283
+
284
+ async function main() {
285
+ const config = getConfig();
286
+
287
+ if (!config.telegram && !config.line) {
288
+ console.error("No webhooks configured. Set up Telegram and/or LINE keys.");
289
+ process.exit(1);
290
+ }
291
+
292
+ console.log("Unified Webhook Server");
293
+ console.log(`Listening on port ${PORT}`);
294
+ if (config.telegram) console.log(" /telegram - Telegram webhook");
295
+ if (config.line) console.log(" /line - LINE webhook");
296
+
297
+ const server = createServer(async (req, res) => {
298
+ const url = new URL(req.url || "/", `http://localhost:${PORT}`);
299
+
300
+ try {
301
+ if (url.pathname === "/telegram" && req.method === "POST") {
302
+ await handleTelegram(req, res, config);
303
+ } else if (url.pathname === "/line" && req.method === "POST") {
304
+ await handleLINE(req, res, config);
305
+ } else if (url.pathname === "/health") {
306
+ res.writeHead(200);
307
+ res.end("OK");
308
+ } else {
309
+ res.writeHead(404);
310
+ res.end("Not Found");
311
+ }
312
+ } catch (err) {
313
+ log("Server", "Error:", err);
314
+ res.writeHead(500);
315
+ res.end("Error");
316
+ }
317
+ });
318
+
319
+ server.listen(PORT, () => {
320
+ console.log(`Server running on http://0.0.0.0:${PORT}`);
321
+ console.log(`Configure webhooks:`);
322
+ if (config.telegram) {
323
+ console.log(` Telegram: https://webhook.bunchee.online/telegram`);
324
+ }
325
+ if (config.line) {
326
+ console.log(` LINE: https://webhook.bunchee.online/line`);
327
+ }
328
+ });
329
+ }
330
+
331
+ main();