too-many-claw 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +965 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +259 -4
- package/dist/index.js +854 -38
- package/dist/index.js.map +1 -1
- package/dist/scripts/postinstall.js +35 -35
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ var AGENT_DEFINITIONS = [
|
|
|
20
20
|
name: "Base",
|
|
21
21
|
emoji: "\u{1F3E0}",
|
|
22
22
|
category: "CORE" /* CORE */,
|
|
23
|
-
model: "claude-opus-4-5" /* OPUS */,
|
|
23
|
+
model: "anthropic/claude-opus-4-5" /* OPUS */,
|
|
24
24
|
role: "Team Coordinator. Always active. Receives and analyzes user requests, then summons appropriate agents. Orchestrates team conversations and synthesizes results for the user upon task completion. Can dismiss agents when necessary.",
|
|
25
25
|
alwaysActive: true
|
|
26
26
|
},
|
|
@@ -32,7 +32,7 @@ var AGENT_DEFINITIONS = [
|
|
|
32
32
|
name: "Search Specialist",
|
|
33
33
|
emoji: "\u{1F50D}",
|
|
34
34
|
category: "RESEARCH" /* RESEARCH */,
|
|
35
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
35
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
36
36
|
role: "Information search and resource collection specialist. Finds necessary information through web searches, document searches, and database queries. Organizes and shares search results with the team."
|
|
37
37
|
},
|
|
38
38
|
{
|
|
@@ -40,7 +40,7 @@ var AGENT_DEFINITIONS = [
|
|
|
40
40
|
name: "Technology Research Specialist",
|
|
41
41
|
emoji: "\u{1F52C}",
|
|
42
42
|
category: "RESEARCH" /* RESEARCH */,
|
|
43
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
43
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
44
44
|
role: "Latest technology trends research specialist. Investigates new technologies, frameworks, libraries, and industry trends. Provides comparative analysis of pros and cons for technology selection."
|
|
45
45
|
},
|
|
46
46
|
{
|
|
@@ -48,7 +48,7 @@ var AGENT_DEFINITIONS = [
|
|
|
48
48
|
name: "Trend Analysis Specialist",
|
|
49
49
|
emoji: "\u{1F4C8}",
|
|
50
50
|
category: "RESEARCH" /* RESEARCH */,
|
|
51
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
51
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
52
52
|
role: "Market and trend analyst. Analyzes current trends, popular topics, and market dynamics. Provides insights on timing and strategic direction."
|
|
53
53
|
},
|
|
54
54
|
{
|
|
@@ -56,7 +56,7 @@ var AGENT_DEFINITIONS = [
|
|
|
56
56
|
name: "Data Preparation Specialist",
|
|
57
57
|
emoji: "\u{1F4CA}",
|
|
58
58
|
category: "RESEARCH" /* RESEARCH */,
|
|
59
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
59
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
60
60
|
role: "Data collection and refinement specialist. Collects, cleanses, and processes data into usable formats. Handles statistics, metrics, and data preparation."
|
|
61
61
|
},
|
|
62
62
|
// ============================================================================
|
|
@@ -67,7 +67,7 @@ var AGENT_DEFINITIONS = [
|
|
|
67
67
|
name: "Psychological Counselor",
|
|
68
68
|
emoji: "\u{1F49A}",
|
|
69
69
|
category: "PSYCHOLOGY" /* PSYCHOLOGY */,
|
|
70
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
70
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
71
71
|
role: "Emotional support and counseling specialist. Provides emotional support when users or team members are struggling or stressed. Offers comfort, empathy, and psychological stability."
|
|
72
72
|
},
|
|
73
73
|
{
|
|
@@ -75,7 +75,7 @@ var AGENT_DEFINITIONS = [
|
|
|
75
75
|
name: "User Psychology Analyst",
|
|
76
76
|
emoji: "\u{1F9E0}",
|
|
77
77
|
category: "PSYCHOLOGY" /* PSYCHOLOGY */,
|
|
78
|
-
model: "claude-opus-4-5" /* OPUS */,
|
|
78
|
+
model: "anthropic/claude-opus-4-5" /* OPUS */,
|
|
79
79
|
role: "User intent and psychology analysis specialist. Analyzes what users truly want and the hidden intentions behind their words. Identifies underlying needs beyond stated requirements."
|
|
80
80
|
},
|
|
81
81
|
{
|
|
@@ -83,7 +83,7 @@ var AGENT_DEFINITIONS = [
|
|
|
83
83
|
name: "Questioning Specialist",
|
|
84
84
|
emoji: "\u2753",
|
|
85
85
|
category: "PSYCHOLOGY" /* PSYCHOLOGY */,
|
|
86
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
86
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
87
87
|
role: "Core questioning and clarification specialist. Asks questions to clarify ambiguous requirements. Identifies missing information and unclear aspects to seek clarification."
|
|
88
88
|
},
|
|
89
89
|
{
|
|
@@ -91,7 +91,7 @@ var AGENT_DEFINITIONS = [
|
|
|
91
91
|
name: "Rational Persuasion Specialist",
|
|
92
92
|
emoji: "\u{1F3AF}",
|
|
93
93
|
category: "PSYCHOLOGY" /* PSYCHOLOGY */,
|
|
94
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
94
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
95
95
|
role: "Logical persuasion and perspective-shifting specialist. Changes or persuades others' views through rational arguments and logic. Also mediates in conflict situations."
|
|
96
96
|
},
|
|
97
97
|
{
|
|
@@ -99,7 +99,7 @@ var AGENT_DEFINITIONS = [
|
|
|
99
99
|
name: "Education Specialist",
|
|
100
100
|
emoji: "\u{1F4DA}",
|
|
101
101
|
category: "PSYCHOLOGY" /* PSYCHOLOGY */,
|
|
102
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
102
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
103
103
|
role: "Explanation and education specialist. Explains complex concepts in simple terms. Teaches and educates users or team members on topics they don't understand."
|
|
104
104
|
},
|
|
105
105
|
// ============================================================================
|
|
@@ -110,7 +110,7 @@ var AGENT_DEFINITIONS = [
|
|
|
110
110
|
name: "Professional Planning Specialist",
|
|
111
111
|
emoji: "\u{1F4CB}",
|
|
112
112
|
category: "PLANNING" /* PLANNING */,
|
|
113
|
-
model: "claude-opus-4-5" /* OPUS */,
|
|
113
|
+
model: "anthropic/claude-opus-4-5" /* OPUS */,
|
|
114
114
|
role: "Planning and roadmap specialist. Breaks down tasks into steps, establishes schedules, and sets priorities. Presents systematic plans and roadmaps."
|
|
115
115
|
},
|
|
116
116
|
{
|
|
@@ -118,7 +118,7 @@ var AGENT_DEFINITIONS = [
|
|
|
118
118
|
name: "Agent Team Composition Specialist",
|
|
119
119
|
emoji: "\u{1F465}",
|
|
120
120
|
category: "PLANNING" /* PLANNING */,
|
|
121
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
121
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
122
122
|
role: "Optimal team composition recommendation specialist. Analyzes and recommends which agents are needed for a given task. Optimizes team composition efficiency."
|
|
123
123
|
},
|
|
124
124
|
{
|
|
@@ -126,7 +126,7 @@ var AGENT_DEFINITIONS = [
|
|
|
126
126
|
name: "Promotion Specialist",
|
|
127
127
|
emoji: "\u{1F4E2}",
|
|
128
128
|
category: "PLANNING" /* PLANNING */,
|
|
129
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
129
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
130
130
|
role: "Marketing and promotion specialist. Handles how to publicize deliverables, branding, and marketing strategies. Refines messaging and positioning."
|
|
131
131
|
},
|
|
132
132
|
{
|
|
@@ -134,7 +134,7 @@ var AGENT_DEFINITIONS = [
|
|
|
134
134
|
name: "Uploader",
|
|
135
135
|
emoji: "\u2B06\uFE0F",
|
|
136
136
|
category: "PLANNING" /* PLANNING */,
|
|
137
|
-
model: "claude-haiku-
|
|
137
|
+
model: "anthropic/claude-haiku-3-5" /* HAIKU */,
|
|
138
138
|
role: "Deployment and upload specialist. Deploys and uploads completed deliverables. Handles launches, releases, and publishing."
|
|
139
139
|
},
|
|
140
140
|
// ============================================================================
|
|
@@ -145,7 +145,7 @@ var AGENT_DEFINITIONS = [
|
|
|
145
145
|
name: "Backend Developer",
|
|
146
146
|
emoji: "\u2699\uFE0F",
|
|
147
147
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
148
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
148
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
149
149
|
role: "Server and backend development specialist. Handles server logic, APIs, databases, and infrastructure-related development."
|
|
150
150
|
},
|
|
151
151
|
{
|
|
@@ -153,7 +153,7 @@ var AGENT_DEFINITIONS = [
|
|
|
153
153
|
name: "Frontend Developer",
|
|
154
154
|
emoji: "\u{1F3A8}",
|
|
155
155
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
156
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
156
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
157
157
|
role: "Frontend and UI development specialist. Handles web/app user interface, screen, and interaction development."
|
|
158
158
|
},
|
|
159
159
|
{
|
|
@@ -161,7 +161,7 @@ var AGENT_DEFINITIONS = [
|
|
|
161
161
|
name: "Professional Designer",
|
|
162
162
|
emoji: "\u{1F58C}\uFE0F",
|
|
163
163
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
164
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
164
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
165
165
|
role: "Design and visual specialist. Handles UI/UX design, visual design, layout, color, and typography."
|
|
166
166
|
},
|
|
167
167
|
{
|
|
@@ -169,7 +169,7 @@ var AGENT_DEFINITIONS = [
|
|
|
169
169
|
name: "Code Reviewer",
|
|
170
170
|
emoji: "\u{1F440}",
|
|
171
171
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
172
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
172
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
173
173
|
role: "Code quality review specialist. Reviews written code, suggests improvements, and identifies bugs or issues."
|
|
174
174
|
},
|
|
175
175
|
{
|
|
@@ -177,7 +177,7 @@ var AGENT_DEFINITIONS = [
|
|
|
177
177
|
name: "Documentation Specialist",
|
|
178
178
|
emoji: "\u{1F4DD}",
|
|
179
179
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
180
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
180
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
181
181
|
role: "Documentation specialist. Handles writing all types of documentation including README, guides, API documentation, and user manuals."
|
|
182
182
|
},
|
|
183
183
|
{
|
|
@@ -185,7 +185,7 @@ var AGENT_DEFINITIONS = [
|
|
|
185
185
|
name: "Automation Specialist",
|
|
186
186
|
emoji: "\u{1F916}",
|
|
187
187
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
188
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
188
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
189
189
|
role: "Automation and workflow specialist. Automates repetitive tasks, creates scripts, and designs efficient workflows."
|
|
190
190
|
},
|
|
191
191
|
{
|
|
@@ -193,7 +193,7 @@ var AGENT_DEFINITIONS = [
|
|
|
193
193
|
name: "Prompt Engineer",
|
|
194
194
|
emoji: "\u{1F4AC}",
|
|
195
195
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
196
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
196
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
197
197
|
role: "AI prompt optimization specialist. Optimizes prompts sent to LLMs and develops AI utilization strategies."
|
|
198
198
|
},
|
|
199
199
|
{
|
|
@@ -201,7 +201,7 @@ var AGENT_DEFINITIONS = [
|
|
|
201
201
|
name: "AI Illustration Generation Specialist",
|
|
202
202
|
emoji: "\u{1F3AD}",
|
|
203
203
|
category: "DEVELOPMENT" /* DEVELOPMENT */,
|
|
204
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
204
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
205
205
|
role: "AI image generation specialist. Handles image generation prompt writing and creation using Midjourney, DALL-E, Stable Diffusion, and similar tools."
|
|
206
206
|
},
|
|
207
207
|
// ============================================================================
|
|
@@ -212,7 +212,7 @@ var AGENT_DEFINITIONS = [
|
|
|
212
212
|
name: "Program Testing Specialist",
|
|
213
213
|
emoji: "\u{1F9EA}",
|
|
214
214
|
category: "TESTING" /* TESTING */,
|
|
215
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
215
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
216
216
|
role: "Technical testing and QA specialist. Handles code testing, unit testing, integration testing, and bug discovery."
|
|
217
217
|
},
|
|
218
218
|
{
|
|
@@ -220,7 +220,7 @@ var AGENT_DEFINITIONS = [
|
|
|
220
220
|
name: "General User Testing Specialist",
|
|
221
221
|
emoji: "\u{1F464}",
|
|
222
222
|
category: "TESTING" /* TESTING */,
|
|
223
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
223
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
224
224
|
role: "User perspective testing specialist. Validates usability, intuitiveness, and UX from the perspective of a general user without technical knowledge."
|
|
225
225
|
},
|
|
226
226
|
{
|
|
@@ -228,7 +228,7 @@ var AGENT_DEFINITIONS = [
|
|
|
228
228
|
name: "Security Check Specialist",
|
|
229
229
|
emoji: "\u{1F6E1}\uFE0F",
|
|
230
230
|
category: "TESTING" /* TESTING */,
|
|
231
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
231
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
232
232
|
role: "Security audit specialist. Reviews basic security checklists, confirms compliance, and verifies adherence to security policies."
|
|
233
233
|
},
|
|
234
234
|
{
|
|
@@ -236,7 +236,7 @@ var AGENT_DEFINITIONS = [
|
|
|
236
236
|
name: "Vulnerability Discovery Specialist",
|
|
237
237
|
emoji: "\u{1F513}",
|
|
238
238
|
category: "TESTING" /* TESTING */,
|
|
239
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
239
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
240
240
|
role: "Vulnerability analysis specialist. Identifies security vulnerabilities and weaknesses in code, systems, and designs."
|
|
241
241
|
},
|
|
242
242
|
{
|
|
@@ -244,7 +244,7 @@ var AGENT_DEFINITIONS = [
|
|
|
244
244
|
name: "Penetration Testing Specialist",
|
|
245
245
|
emoji: "\u{1F480}",
|
|
246
246
|
category: "TESTING" /* TESTING */,
|
|
247
|
-
model: "claude-opus-4-5" /* OPUS */,
|
|
247
|
+
model: "anthropic/claude-opus-4-5" /* OPUS */,
|
|
248
248
|
role: "Penetration testing specialist. Tests systems from an actual attacker's perspective and performs hacking simulations."
|
|
249
249
|
},
|
|
250
250
|
// ============================================================================
|
|
@@ -255,7 +255,7 @@ var AGENT_DEFINITIONS = [
|
|
|
255
255
|
name: "Fact Check Specialist",
|
|
256
256
|
emoji: "\u{1F4A3}",
|
|
257
257
|
category: "CRITIQUE" /* CRITIQUE */,
|
|
258
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
258
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
259
259
|
role: `Fact-checking specialist. Demands evidence for claims, verifies factual accuracy, and points out flaws. The one who asks "What's your evidence?"`
|
|
260
260
|
},
|
|
261
261
|
{
|
|
@@ -263,7 +263,7 @@ var AGENT_DEFINITIONS = [
|
|
|
263
263
|
name: "Blunt Critic",
|
|
264
264
|
emoji: "\u{1F525}",
|
|
265
265
|
category: "CRITIQUE" /* CRITIQUE */,
|
|
266
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
266
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
267
267
|
role: "Sharp direct feedback specialist. Points out problems directly without beating around the bush. Speaks uncomfortable but necessary truths."
|
|
268
268
|
},
|
|
269
269
|
{
|
|
@@ -271,7 +271,7 @@ var AGENT_DEFINITIONS = [
|
|
|
271
271
|
name: "Critic",
|
|
272
272
|
emoji: "\u{1F9D0}",
|
|
273
273
|
category: "CRITIQUE" /* CRITIQUE */,
|
|
274
|
-
model: "claude-opus-4-5" /* OPUS */,
|
|
274
|
+
model: "anthropic/claude-opus-4-5" /* OPUS */,
|
|
275
275
|
role: "Logical criticism specialist. Logically analyzes and critiques problems in plans or deliverables. Provides improvement suggestions alongside criticism."
|
|
276
276
|
},
|
|
277
277
|
{
|
|
@@ -279,7 +279,7 @@ var AGENT_DEFINITIONS = [
|
|
|
279
279
|
name: "Negative Agent",
|
|
280
280
|
emoji: "\u{1F44E}",
|
|
281
281
|
category: "CRITIQUE" /* CRITIQUE */,
|
|
282
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
282
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
283
283
|
role: "Devil's advocate. Intentionally views things from a negative perspective. Raises worst-case scenarios, failure possibilities, and risks. Prevents the team from falling into blind optimism."
|
|
284
284
|
},
|
|
285
285
|
{
|
|
@@ -287,7 +287,7 @@ var AGENT_DEFINITIONS = [
|
|
|
287
287
|
name: "Praise Specialist",
|
|
288
288
|
emoji: "\u{1F44F}",
|
|
289
289
|
category: "CRITIQUE" /* CRITIQUE */,
|
|
290
|
-
model: "claude-haiku-
|
|
290
|
+
model: "anthropic/claude-haiku-3-5" /* HAIKU */,
|
|
291
291
|
role: "Positive feedback specialist. Finds and praises what was done well, boosts morale, and provides motivation. Balances against critical agents."
|
|
292
292
|
},
|
|
293
293
|
// ============================================================================
|
|
@@ -298,7 +298,7 @@ var AGENT_DEFINITIONS = [
|
|
|
298
298
|
name: "Loophole Discovery Specialist",
|
|
299
299
|
emoji: "\u{1F573}\uFE0F",
|
|
300
300
|
category: "SPECIAL" /* SPECIAL */,
|
|
301
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
301
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
302
302
|
role: 'Within-rules optimization specialist. Finds workarounds, shortcuts, and clever solutions within established rules or constraints. Answers the question "Is there a way?"'
|
|
303
303
|
},
|
|
304
304
|
{
|
|
@@ -306,7 +306,7 @@ var AGENT_DEFINITIONS = [
|
|
|
306
306
|
name: "Pressure Specialist",
|
|
307
307
|
emoji: "\u26A1",
|
|
308
308
|
category: "SPECIAL" /* SPECIAL */,
|
|
309
|
-
model: "claude-sonnet-4-5" /* SONNET */,
|
|
309
|
+
model: "anthropic/claude-sonnet-4-5" /* SONNET */,
|
|
310
310
|
role: "Urgency and pressure specialist. Creates urgency by emphasizing deadline pressure, time limits, and severity of consequences. Pressures agents who are slacking or reluctant to work properly. The team's whip."
|
|
311
311
|
},
|
|
312
312
|
{
|
|
@@ -314,7 +314,7 @@ var AGENT_DEFINITIONS = [
|
|
|
314
314
|
name: "Dirty Worker",
|
|
315
315
|
emoji: "\u{1FAA0}",
|
|
316
316
|
category: "SPECIAL" /* SPECIAL */,
|
|
317
|
-
model: "claude-haiku-
|
|
317
|
+
model: "anthropic/claude-haiku-3-5" /* HAIKU */,
|
|
318
318
|
role: "Undesirable tasks handler. Takes on work that other agents dislike or refuse. Performs boring, repetitive, or tasks nobody wants to do."
|
|
319
319
|
}
|
|
320
320
|
];
|
|
@@ -1925,6 +1925,811 @@ var DiscordAdapter = class {
|
|
|
1925
1925
|
}
|
|
1926
1926
|
};
|
|
1927
1927
|
|
|
1928
|
+
// src/daemon/OpenClawDaemon.ts
|
|
1929
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
1930
|
+
import { execSync } from "child_process";
|
|
1931
|
+
|
|
1932
|
+
// src/openclaw/GatewayClient.ts
|
|
1933
|
+
import WebSocket from "ws";
|
|
1934
|
+
import { EventEmitter } from "events";
|
|
1935
|
+
var DEFAULT_CONFIG2 = {
|
|
1936
|
+
url: "ws://127.0.0.1:18789",
|
|
1937
|
+
reconnect: {
|
|
1938
|
+
enabled: true,
|
|
1939
|
+
maxAttempts: 10,
|
|
1940
|
+
baseDelay: 1e3,
|
|
1941
|
+
maxDelay: 3e4
|
|
1942
|
+
},
|
|
1943
|
+
heartbeatInterval: 3e4,
|
|
1944
|
+
connectionTimeout: 1e4
|
|
1945
|
+
};
|
|
1946
|
+
var GatewayClient = class extends EventEmitter {
|
|
1947
|
+
ws = null;
|
|
1948
|
+
config;
|
|
1949
|
+
state = "disconnected" /* DISCONNECTED */;
|
|
1950
|
+
reconnectAttempts = 0;
|
|
1951
|
+
reconnectTimer = null;
|
|
1952
|
+
heartbeatTimer = null;
|
|
1953
|
+
connectionTimeoutTimer = null;
|
|
1954
|
+
intentionalClose = false;
|
|
1955
|
+
lastPongTime = 0;
|
|
1956
|
+
constructor(config = {}) {
|
|
1957
|
+
super();
|
|
1958
|
+
this.config = {
|
|
1959
|
+
...DEFAULT_CONFIG2,
|
|
1960
|
+
...config,
|
|
1961
|
+
reconnect: {
|
|
1962
|
+
...DEFAULT_CONFIG2.reconnect,
|
|
1963
|
+
...config.reconnect
|
|
1964
|
+
}
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
/**
|
|
1968
|
+
* Get current connection state
|
|
1969
|
+
*/
|
|
1970
|
+
get connectionState() {
|
|
1971
|
+
return this.state;
|
|
1972
|
+
}
|
|
1973
|
+
/**
|
|
1974
|
+
* Check if connected to gateway
|
|
1975
|
+
*/
|
|
1976
|
+
get isConnected() {
|
|
1977
|
+
return this.state === "connected" /* CONNECTED */ && this.ws?.readyState === WebSocket.OPEN;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Connect to the OpenClaw Gateway
|
|
1981
|
+
*/
|
|
1982
|
+
async connect() {
|
|
1983
|
+
if (this.state === "connected" /* CONNECTED */ || this.state === "connecting" /* CONNECTING */) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
this.intentionalClose = false;
|
|
1987
|
+
await this.doConnect();
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Disconnect from the gateway
|
|
1991
|
+
*/
|
|
1992
|
+
async disconnect() {
|
|
1993
|
+
this.intentionalClose = true;
|
|
1994
|
+
this.clearTimers();
|
|
1995
|
+
this.setState("closed" /* CLOSED */);
|
|
1996
|
+
if (this.ws) {
|
|
1997
|
+
this.ws.close(1e3, "Client disconnect");
|
|
1998
|
+
this.ws = null;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Send a message to the gateway
|
|
2003
|
+
*/
|
|
2004
|
+
send(message) {
|
|
2005
|
+
if (!this.isConnected || !this.ws) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
try {
|
|
2009
|
+
this.ws.send(JSON.stringify(message));
|
|
2010
|
+
return true;
|
|
2011
|
+
} catch {
|
|
2012
|
+
return false;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
/**
|
|
2016
|
+
* Send a ping to the gateway
|
|
2017
|
+
*/
|
|
2018
|
+
ping() {
|
|
2019
|
+
return this.send({ type: "ping", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Request gateway status
|
|
2023
|
+
*/
|
|
2024
|
+
requestStatus() {
|
|
2025
|
+
return this.send({ type: "status" });
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Request gateway health
|
|
2029
|
+
*/
|
|
2030
|
+
requestHealth() {
|
|
2031
|
+
return this.send({ type: "health" });
|
|
2032
|
+
}
|
|
2033
|
+
// ============================================
|
|
2034
|
+
// Private Methods
|
|
2035
|
+
// ============================================
|
|
2036
|
+
async doConnect() {
|
|
2037
|
+
return new Promise((resolve, reject) => {
|
|
2038
|
+
this.setState("connecting" /* CONNECTING */);
|
|
2039
|
+
try {
|
|
2040
|
+
this.ws = new WebSocket(this.config.url);
|
|
2041
|
+
} catch (error) {
|
|
2042
|
+
this.setState("disconnected" /* DISCONNECTED */);
|
|
2043
|
+
reject(error);
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
this.connectionTimeoutTimer = setTimeout(() => {
|
|
2047
|
+
if (this.state === "connecting" /* CONNECTING */) {
|
|
2048
|
+
this.ws?.close();
|
|
2049
|
+
this.ws = null;
|
|
2050
|
+
this.setState("disconnected" /* DISCONNECTED */);
|
|
2051
|
+
reject(new Error(`Connection timeout after ${this.config.connectionTimeout}ms`));
|
|
2052
|
+
}
|
|
2053
|
+
}, this.config.connectionTimeout);
|
|
2054
|
+
this.ws.on("open", () => {
|
|
2055
|
+
this.clearConnectionTimeout();
|
|
2056
|
+
this.reconnectAttempts = 0;
|
|
2057
|
+
this.lastPongTime = Date.now();
|
|
2058
|
+
this.setState("connected" /* CONNECTED */);
|
|
2059
|
+
this.startHeartbeat();
|
|
2060
|
+
this.emit("connect");
|
|
2061
|
+
resolve();
|
|
2062
|
+
});
|
|
2063
|
+
this.ws.on("message", (data) => {
|
|
2064
|
+
this.handleMessage(data);
|
|
2065
|
+
});
|
|
2066
|
+
this.ws.on("close", (code, reason) => {
|
|
2067
|
+
this.clearTimers();
|
|
2068
|
+
const reasonStr = reason.toString() || "Unknown reason";
|
|
2069
|
+
this.emit("disconnect", code, reasonStr);
|
|
2070
|
+
if (!this.intentionalClose && this.config.reconnect.enabled) {
|
|
2071
|
+
this.scheduleReconnect();
|
|
2072
|
+
} else {
|
|
2073
|
+
this.setState("disconnected" /* DISCONNECTED */);
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
this.ws.on("error", (error) => {
|
|
2077
|
+
this.emit("error", error);
|
|
2078
|
+
});
|
|
2079
|
+
this.ws.on("pong", () => {
|
|
2080
|
+
this.lastPongTime = Date.now();
|
|
2081
|
+
});
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
handleMessage(data) {
|
|
2085
|
+
let message;
|
|
2086
|
+
try {
|
|
2087
|
+
const str = data.toString();
|
|
2088
|
+
message = JSON.parse(str);
|
|
2089
|
+
} catch {
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
this.emit("message", message);
|
|
2093
|
+
switch (message.type) {
|
|
2094
|
+
case "shutdown" /* SHUTDOWN */:
|
|
2095
|
+
this.emit("shutdown");
|
|
2096
|
+
break;
|
|
2097
|
+
case "pong" /* PONG */:
|
|
2098
|
+
this.lastPongTime = Date.now();
|
|
2099
|
+
break;
|
|
2100
|
+
case "agent_response" /* AGENT_RESPONSE */:
|
|
2101
|
+
case "assistant":
|
|
2102
|
+
this.emit("agent_response", message);
|
|
2103
|
+
break;
|
|
2104
|
+
case "agent_delta" /* AGENT_DELTA */:
|
|
2105
|
+
case "delta":
|
|
2106
|
+
this.emit("agent_delta", message);
|
|
2107
|
+
break;
|
|
2108
|
+
case "agent_start" /* AGENT_START */:
|
|
2109
|
+
case "start":
|
|
2110
|
+
this.emit("agent_start", message);
|
|
2111
|
+
break;
|
|
2112
|
+
case "agent_end" /* AGENT_END */:
|
|
2113
|
+
case "end":
|
|
2114
|
+
case "complete":
|
|
2115
|
+
this.emit("agent_end", message);
|
|
2116
|
+
break;
|
|
2117
|
+
case "message" /* MESSAGE */:
|
|
2118
|
+
case "channel_message" /* CHANNEL_MESSAGE */:
|
|
2119
|
+
case "discord":
|
|
2120
|
+
this.emit("channel_message", message);
|
|
2121
|
+
break;
|
|
2122
|
+
case "error" /* ERROR */:
|
|
2123
|
+
this.emit("error", new Error(message.error || "Unknown gateway error"));
|
|
2124
|
+
break;
|
|
2125
|
+
default:
|
|
2126
|
+
break;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
scheduleReconnect() {
|
|
2130
|
+
const { maxAttempts = 10, baseDelay = 1e3, maxDelay = 3e4 } = this.config.reconnect;
|
|
2131
|
+
if (maxAttempts !== -1 && this.reconnectAttempts >= maxAttempts) {
|
|
2132
|
+
this.setState("disconnected" /* DISCONNECTED */);
|
|
2133
|
+
this.emit("reconnect_failed");
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
this.setState("reconnecting" /* RECONNECTING */);
|
|
2137
|
+
this.reconnectAttempts++;
|
|
2138
|
+
const delay = Math.min(
|
|
2139
|
+
baseDelay * Math.pow(2, this.reconnectAttempts - 1),
|
|
2140
|
+
maxDelay
|
|
2141
|
+
);
|
|
2142
|
+
this.emit("reconnecting", this.reconnectAttempts, maxAttempts);
|
|
2143
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
2144
|
+
try {
|
|
2145
|
+
await this.doConnect();
|
|
2146
|
+
} catch {
|
|
2147
|
+
}
|
|
2148
|
+
}, delay);
|
|
2149
|
+
}
|
|
2150
|
+
startHeartbeat() {
|
|
2151
|
+
if (this.config.heartbeatInterval <= 0) {
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
this.heartbeatTimer = setInterval(() => {
|
|
2155
|
+
if (!this.isConnected) {
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
const timeSinceLastPong = Date.now() - this.lastPongTime;
|
|
2159
|
+
if (timeSinceLastPong > this.config.heartbeatInterval * 2) {
|
|
2160
|
+
this.ws?.close(4e3, "Heartbeat timeout");
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
try {
|
|
2164
|
+
this.ws?.ping();
|
|
2165
|
+
} catch {
|
|
2166
|
+
}
|
|
2167
|
+
}, this.config.heartbeatInterval);
|
|
2168
|
+
}
|
|
2169
|
+
setState(state) {
|
|
2170
|
+
if (this.state !== state) {
|
|
2171
|
+
this.state = state;
|
|
2172
|
+
this.emit("state_change", state);
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
clearTimers() {
|
|
2176
|
+
this.clearConnectionTimeout();
|
|
2177
|
+
this.clearReconnectTimer();
|
|
2178
|
+
this.clearHeartbeat();
|
|
2179
|
+
}
|
|
2180
|
+
clearConnectionTimeout() {
|
|
2181
|
+
if (this.connectionTimeoutTimer) {
|
|
2182
|
+
clearTimeout(this.connectionTimeoutTimer);
|
|
2183
|
+
this.connectionTimeoutTimer = null;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
clearReconnectTimer() {
|
|
2187
|
+
if (this.reconnectTimer) {
|
|
2188
|
+
clearTimeout(this.reconnectTimer);
|
|
2189
|
+
this.reconnectTimer = null;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
clearHeartbeat() {
|
|
2193
|
+
if (this.heartbeatTimer) {
|
|
2194
|
+
clearInterval(this.heartbeatTimer);
|
|
2195
|
+
this.heartbeatTimer = null;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2200
|
+
// src/daemon/AgentMapper.ts
|
|
2201
|
+
var AgentMapper = class {
|
|
2202
|
+
idMap = /* @__PURE__ */ new Map();
|
|
2203
|
+
nameMap = /* @__PURE__ */ new Map();
|
|
2204
|
+
aliasMap = /* @__PURE__ */ new Map();
|
|
2205
|
+
constructor() {
|
|
2206
|
+
this.buildMaps();
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Build lookup maps for efficient agent resolution
|
|
2210
|
+
*/
|
|
2211
|
+
buildMaps() {
|
|
2212
|
+
for (const agent of AGENT_DEFINITIONS) {
|
|
2213
|
+
this.idMap.set(agent.id, agent);
|
|
2214
|
+
this.idMap.set(agent.id.toLowerCase(), agent);
|
|
2215
|
+
const normalizedName = this.normalizeName(agent.name);
|
|
2216
|
+
this.nameMap.set(normalizedName, agent);
|
|
2217
|
+
this.buildAliases(agent);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
/**
|
|
2221
|
+
* Build common aliases for an agent
|
|
2222
|
+
*/
|
|
2223
|
+
buildAliases(agent) {
|
|
2224
|
+
const { id, name } = agent;
|
|
2225
|
+
this.aliasMap.set(id.replace(/-/g, "_"), agent);
|
|
2226
|
+
const camelCase = id.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2227
|
+
this.aliasMap.set(camelCase, agent);
|
|
2228
|
+
this.aliasMap.set(camelCase.toLowerCase(), agent);
|
|
2229
|
+
this.aliasMap.set(name.replace(/\s+/g, "").toLowerCase(), agent);
|
|
2230
|
+
this.aliasMap.set(name.replace(/\s+/g, "_").toLowerCase(), agent);
|
|
2231
|
+
const specialAliases = {
|
|
2232
|
+
"base": ["coordinator", "main", "primary", "default", "openclaw"],
|
|
2233
|
+
"backend-dev": ["backend", "server", "api"],
|
|
2234
|
+
"frontend-dev": ["frontend", "ui", "web"],
|
|
2235
|
+
"code-reviewer": ["reviewer", "review"],
|
|
2236
|
+
"program-tester": ["tester", "qa", "testing"],
|
|
2237
|
+
"security-checker": ["security", "securitycheck"],
|
|
2238
|
+
"vuln-finder": ["vulnerability", "vulnscanner"],
|
|
2239
|
+
"tech-researcher": ["researcher", "research"],
|
|
2240
|
+
"doc-writer": ["documentation", "docs", "writer"],
|
|
2241
|
+
"prompt-engineer": ["prompter", "prompt"],
|
|
2242
|
+
"ai-illustrator": ["illustrator", "artist", "image"],
|
|
2243
|
+
"user-psychologist": ["psychologist", "psychology"],
|
|
2244
|
+
"fact-bomber": ["factcheck", "factchecker"],
|
|
2245
|
+
"dirty-worker": ["worker", "grunt"]
|
|
2246
|
+
};
|
|
2247
|
+
if (specialAliases[id]) {
|
|
2248
|
+
for (const alias of specialAliases[id]) {
|
|
2249
|
+
this.aliasMap.set(alias, agent);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
/**
|
|
2254
|
+
* Normalize a name for comparison
|
|
2255
|
+
*/
|
|
2256
|
+
normalizeName(name) {
|
|
2257
|
+
return name.toLowerCase().replace(/[^a-z0-9]/g, "").trim();
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Resolve an OpenClaw agent identifier to a TMC agent
|
|
2261
|
+
* Tries multiple matching strategies
|
|
2262
|
+
*/
|
|
2263
|
+
resolve(identifier) {
|
|
2264
|
+
if (!identifier) {
|
|
2265
|
+
return this.getDefaultAgent();
|
|
2266
|
+
}
|
|
2267
|
+
const normalized = identifier.toLowerCase().trim();
|
|
2268
|
+
const byId = this.idMap.get(normalized);
|
|
2269
|
+
if (byId) return byId;
|
|
2270
|
+
const byName = this.nameMap.get(this.normalizeName(identifier));
|
|
2271
|
+
if (byName) return byName;
|
|
2272
|
+
const byAlias = this.aliasMap.get(normalized);
|
|
2273
|
+
if (byAlias) return byAlias;
|
|
2274
|
+
const partial = this.partialMatch(normalized);
|
|
2275
|
+
if (partial) return partial;
|
|
2276
|
+
return this.getDefaultAgent();
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Try partial/fuzzy matching
|
|
2280
|
+
*/
|
|
2281
|
+
partialMatch(query) {
|
|
2282
|
+
for (const agent of AGENT_DEFINITIONS) {
|
|
2283
|
+
if (agent.id.includes(query) || query.includes(agent.id)) {
|
|
2284
|
+
return agent;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
const normalizedQuery = this.normalizeName(query);
|
|
2288
|
+
for (const agent of AGENT_DEFINITIONS) {
|
|
2289
|
+
const normalizedAgentName = this.normalizeName(agent.name);
|
|
2290
|
+
if (normalizedAgentName.includes(normalizedQuery) || normalizedQuery.includes(normalizedAgentName)) {
|
|
2291
|
+
return agent;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
return null;
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Get the default agent (base/coordinator)
|
|
2298
|
+
*/
|
|
2299
|
+
getDefaultAgent() {
|
|
2300
|
+
return getAgentById("base") || AGENT_DEFINITIONS[0];
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Check if an identifier maps to a known agent
|
|
2304
|
+
*/
|
|
2305
|
+
isKnownAgent(identifier) {
|
|
2306
|
+
return this.resolve(identifier) !== null;
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* Get all registered agent IDs
|
|
2310
|
+
*/
|
|
2311
|
+
getAllAgentIds() {
|
|
2312
|
+
return AGENT_DEFINITIONS.map((a) => a.id);
|
|
2313
|
+
}
|
|
2314
|
+
};
|
|
2315
|
+
|
|
2316
|
+
// src/daemon/OpenClawDaemon.ts
|
|
2317
|
+
var DEFAULT_CONFIG3 = {
|
|
2318
|
+
gatewayUrl: "ws://127.0.0.1:18789",
|
|
2319
|
+
autoStart: true,
|
|
2320
|
+
processCheckInterval: 5e3,
|
|
2321
|
+
verbose: false
|
|
2322
|
+
};
|
|
2323
|
+
var OpenClawDaemon = class extends EventEmitter2 {
|
|
2324
|
+
config;
|
|
2325
|
+
gatewayClient;
|
|
2326
|
+
webhookManager;
|
|
2327
|
+
configManager;
|
|
2328
|
+
agentMapper;
|
|
2329
|
+
running = false;
|
|
2330
|
+
startTime = null;
|
|
2331
|
+
processCheckTimer = null;
|
|
2332
|
+
openclawDetected = false;
|
|
2333
|
+
// Stats
|
|
2334
|
+
messagesProcessed = 0;
|
|
2335
|
+
messagesForwarded = 0;
|
|
2336
|
+
webhookErrors = 0;
|
|
2337
|
+
lastMessageAt = null;
|
|
2338
|
+
reconnectAttempts = 0;
|
|
2339
|
+
// Track active agents in conversation
|
|
2340
|
+
activeAgents = /* @__PURE__ */ new Set();
|
|
2341
|
+
// Buffer for accumulating streaming responses
|
|
2342
|
+
responseBuffers = /* @__PURE__ */ new Map();
|
|
2343
|
+
bufferCleanupTimer = null;
|
|
2344
|
+
// Buffer stale threshold in ms (60 seconds)
|
|
2345
|
+
BUFFER_STALE_THRESHOLD = 6e4;
|
|
2346
|
+
constructor(config = {}) {
|
|
2347
|
+
super();
|
|
2348
|
+
this.config = { ...DEFAULT_CONFIG3, ...config };
|
|
2349
|
+
this.configManager = new ConfigManager();
|
|
2350
|
+
this.webhookManager = new WebhookManager();
|
|
2351
|
+
this.agentMapper = new AgentMapper();
|
|
2352
|
+
this.gatewayClient = new GatewayClient({
|
|
2353
|
+
url: this.config.gatewayUrl,
|
|
2354
|
+
reconnect: {
|
|
2355
|
+
enabled: true,
|
|
2356
|
+
maxAttempts: -1,
|
|
2357
|
+
// Infinite reconnection
|
|
2358
|
+
baseDelay: 1e3,
|
|
2359
|
+
maxDelay: 3e4
|
|
2360
|
+
}
|
|
2361
|
+
});
|
|
2362
|
+
this.setupEventHandlers();
|
|
2363
|
+
this.loadWebhooks();
|
|
2364
|
+
}
|
|
2365
|
+
/**
|
|
2366
|
+
* Start the daemon
|
|
2367
|
+
*/
|
|
2368
|
+
async start() {
|
|
2369
|
+
if (this.running) {
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
this.running = true;
|
|
2373
|
+
this.startTime = /* @__PURE__ */ new Date();
|
|
2374
|
+
this.emit("start");
|
|
2375
|
+
this.forceLog("Daemon starting...");
|
|
2376
|
+
this.startBufferCleanup();
|
|
2377
|
+
if (this.config.autoStart) {
|
|
2378
|
+
this.startProcessDetection();
|
|
2379
|
+
}
|
|
2380
|
+
await this.connect();
|
|
2381
|
+
}
|
|
2382
|
+
/**
|
|
2383
|
+
* Stop the daemon
|
|
2384
|
+
*/
|
|
2385
|
+
async stop() {
|
|
2386
|
+
if (!this.running) {
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
this.running = false;
|
|
2390
|
+
this.forceLog("Daemon stopping...");
|
|
2391
|
+
this.stopProcessDetection();
|
|
2392
|
+
this.stopBufferCleanup();
|
|
2393
|
+
await this.gatewayClient.disconnect();
|
|
2394
|
+
this.webhookManager.destroy();
|
|
2395
|
+
this.emit("stop");
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Connect to OpenClaw Gateway
|
|
2399
|
+
*/
|
|
2400
|
+
async connect() {
|
|
2401
|
+
try {
|
|
2402
|
+
await this.gatewayClient.connect();
|
|
2403
|
+
return true;
|
|
2404
|
+
} catch (error) {
|
|
2405
|
+
this.log(`Connection failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2406
|
+
return false;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
/**
|
|
2410
|
+
* Get daemon statistics
|
|
2411
|
+
*/
|
|
2412
|
+
getStats() {
|
|
2413
|
+
return {
|
|
2414
|
+
connected: this.gatewayClient.isConnected,
|
|
2415
|
+
connectionState: this.gatewayClient.connectionState,
|
|
2416
|
+
messagesProcessed: this.messagesProcessed,
|
|
2417
|
+
messagesForwarded: this.messagesForwarded,
|
|
2418
|
+
webhookErrors: this.webhookErrors,
|
|
2419
|
+
lastMessageAt: this.lastMessageAt,
|
|
2420
|
+
uptimeMs: this.startTime ? Date.now() - this.startTime.getTime() : 0,
|
|
2421
|
+
reconnectAttempts: this.reconnectAttempts
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Check if daemon is running
|
|
2426
|
+
*/
|
|
2427
|
+
get isRunning() {
|
|
2428
|
+
return this.running;
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Check if connected to OpenClaw
|
|
2432
|
+
*/
|
|
2433
|
+
get isConnected() {
|
|
2434
|
+
return this.gatewayClient.isConnected;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Get list of active agents
|
|
2438
|
+
*/
|
|
2439
|
+
getActiveAgents() {
|
|
2440
|
+
return Array.from(this.activeAgents);
|
|
2441
|
+
}
|
|
2442
|
+
// ============================================
|
|
2443
|
+
// Private Methods
|
|
2444
|
+
// ============================================
|
|
2445
|
+
/**
|
|
2446
|
+
* Set up event handlers for the gateway client
|
|
2447
|
+
*/
|
|
2448
|
+
setupEventHandlers() {
|
|
2449
|
+
this.gatewayClient.on("connect", () => {
|
|
2450
|
+
this.forceLog("Connected to OpenClaw Gateway");
|
|
2451
|
+
this.openclawDetected = true;
|
|
2452
|
+
this.emit("connected");
|
|
2453
|
+
this.emit("openclaw_detected");
|
|
2454
|
+
});
|
|
2455
|
+
this.gatewayClient.on("disconnect", (code, reason) => {
|
|
2456
|
+
this.forceLog(`Disconnected from Gateway: ${code} - ${reason}`);
|
|
2457
|
+
this.emit("disconnected", reason);
|
|
2458
|
+
});
|
|
2459
|
+
this.gatewayClient.on("reconnecting", (attempt) => {
|
|
2460
|
+
this.reconnectAttempts = attempt;
|
|
2461
|
+
this.forceLog(`Reconnecting... (attempt ${attempt})`);
|
|
2462
|
+
this.emit("reconnecting", attempt);
|
|
2463
|
+
});
|
|
2464
|
+
this.gatewayClient.on("reconnect_failed", () => {
|
|
2465
|
+
this.forceLog("Reconnection failed - max attempts reached");
|
|
2466
|
+
if (this.openclawDetected) {
|
|
2467
|
+
this.openclawDetected = false;
|
|
2468
|
+
this.emit("openclaw_lost");
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
this.gatewayClient.on("error", (error) => {
|
|
2472
|
+
this.forceLog(`Gateway error: ${error.message}`);
|
|
2473
|
+
this.emit("error", error);
|
|
2474
|
+
});
|
|
2475
|
+
this.gatewayClient.on("shutdown", () => {
|
|
2476
|
+
this.forceLog("Gateway shutdown signal received");
|
|
2477
|
+
this.openclawDetected = false;
|
|
2478
|
+
this.emit("openclaw_lost");
|
|
2479
|
+
});
|
|
2480
|
+
this.gatewayClient.on("message", (message) => {
|
|
2481
|
+
this.handleRawMessage(message);
|
|
2482
|
+
});
|
|
2483
|
+
this.gatewayClient.on("agent_response", (message) => {
|
|
2484
|
+
this.handleAgentResponse(message);
|
|
2485
|
+
});
|
|
2486
|
+
this.gatewayClient.on("agent_delta", (message) => {
|
|
2487
|
+
this.handleAgentDelta(message);
|
|
2488
|
+
});
|
|
2489
|
+
this.gatewayClient.on("agent_start", (message) => {
|
|
2490
|
+
this.handleAgentStart(message);
|
|
2491
|
+
});
|
|
2492
|
+
this.gatewayClient.on("agent_end", (message) => {
|
|
2493
|
+
this.handleAgentEnd(message);
|
|
2494
|
+
});
|
|
2495
|
+
this.gatewayClient.on("channel_message", (message) => {
|
|
2496
|
+
this.handleChannelMessage(message);
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Load webhooks from configuration
|
|
2501
|
+
*/
|
|
2502
|
+
loadWebhooks() {
|
|
2503
|
+
const webhooks = this.configManager.getAllWebhooks();
|
|
2504
|
+
if (Object.keys(webhooks).length > 0) {
|
|
2505
|
+
this.webhookManager.setWebhooks(webhooks);
|
|
2506
|
+
this.log(`Loaded ${Object.keys(webhooks).length} webhooks`);
|
|
2507
|
+
} else {
|
|
2508
|
+
this.log("No webhooks configured - messages will not be forwarded to Discord");
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Handle raw message from gateway (for debugging/logging)
|
|
2513
|
+
*/
|
|
2514
|
+
handleRawMessage(message) {
|
|
2515
|
+
this.messagesProcessed++;
|
|
2516
|
+
this.lastMessageAt = /* @__PURE__ */ new Date();
|
|
2517
|
+
if (this.config.verbose) {
|
|
2518
|
+
this.log(`Raw message: ${JSON.stringify(message).substring(0, 200)}...`);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Handle agent response (complete response)
|
|
2523
|
+
*/
|
|
2524
|
+
async handleAgentResponse(message) {
|
|
2525
|
+
const agentIdentifier = message.agentId || message.agentName || message.agent;
|
|
2526
|
+
const content = message.content || message.text || message.message;
|
|
2527
|
+
if (!content) {
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
const agent = this.agentMapper.resolve(agentIdentifier);
|
|
2531
|
+
if (!agent) {
|
|
2532
|
+
this.log(`Unknown agent: ${agentIdentifier}`);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
this.emit("message_received", agent.id, content);
|
|
2536
|
+
await this.forwardToWebhook(agent, content);
|
|
2537
|
+
}
|
|
2538
|
+
/**
|
|
2539
|
+
* Handle streaming delta (partial response)
|
|
2540
|
+
*/
|
|
2541
|
+
handleAgentDelta(message) {
|
|
2542
|
+
const agentIdentifier = message.agentId || message.agentName || message.agent;
|
|
2543
|
+
const delta = message.delta || message.content || message.text;
|
|
2544
|
+
const messageId = message.id || agentIdentifier || "default";
|
|
2545
|
+
if (!delta) {
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
const existing = this.responseBuffers.get(messageId);
|
|
2549
|
+
if (existing) {
|
|
2550
|
+
existing.content += delta;
|
|
2551
|
+
existing.lastUpdate = Date.now();
|
|
2552
|
+
} else {
|
|
2553
|
+
const agent = this.agentMapper.resolve(agentIdentifier);
|
|
2554
|
+
this.responseBuffers.set(messageId, {
|
|
2555
|
+
agentId: agent?.id || "base",
|
|
2556
|
+
content: delta,
|
|
2557
|
+
lastUpdate: Date.now()
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Handle agent start event
|
|
2563
|
+
*/
|
|
2564
|
+
handleAgentStart(message) {
|
|
2565
|
+
const agentIdentifier = message.agentId || message.agentName || message.agent;
|
|
2566
|
+
const agent = this.agentMapper.resolve(agentIdentifier);
|
|
2567
|
+
if (agent && !this.activeAgents.has(agent.id)) {
|
|
2568
|
+
this.activeAgents.add(agent.id);
|
|
2569
|
+
this.emit("agent_enter", agent);
|
|
2570
|
+
this.log(`Agent entered: ${agent.emoji} ${agent.name}`);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Handle agent end event (flush buffer and send)
|
|
2575
|
+
*/
|
|
2576
|
+
async handleAgentEnd(message) {
|
|
2577
|
+
const messageId = message.id || message.agentId || message.agentName || "default";
|
|
2578
|
+
const agentIdentifier = message.agentId || message.agentName || message.agent;
|
|
2579
|
+
const buffered = this.responseBuffers.get(messageId);
|
|
2580
|
+
if (buffered && buffered.content) {
|
|
2581
|
+
const agent2 = this.agentMapper.resolve(buffered.agentId) || this.agentMapper.getDefaultAgent();
|
|
2582
|
+
this.emit("message_received", agent2.id, buffered.content);
|
|
2583
|
+
await this.forwardToWebhook(agent2, buffered.content);
|
|
2584
|
+
this.responseBuffers.delete(messageId);
|
|
2585
|
+
}
|
|
2586
|
+
const content = message.content || message.text;
|
|
2587
|
+
if (content) {
|
|
2588
|
+
const agent2 = this.agentMapper.resolve(agentIdentifier);
|
|
2589
|
+
if (agent2) {
|
|
2590
|
+
this.emit("message_received", agent2.id, content);
|
|
2591
|
+
await this.forwardToWebhook(agent2, content);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
const agent = this.agentMapper.resolve(agentIdentifier);
|
|
2595
|
+
if (agent && message.complete !== false) {
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Handle channel message (user messages from Discord)
|
|
2600
|
+
*/
|
|
2601
|
+
handleChannelMessage(message) {
|
|
2602
|
+
if (this.config.verbose) {
|
|
2603
|
+
this.log(`Channel message from ${message.author?.name || "unknown"}: ${message.content?.substring(0, 50)}...`);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Forward a message to Discord via webhook
|
|
2608
|
+
*/
|
|
2609
|
+
async forwardToWebhook(agent, content) {
|
|
2610
|
+
let webhookAgentId = agent.id;
|
|
2611
|
+
if (!this.webhookManager.hasWebhook(agent.id)) {
|
|
2612
|
+
if (!this.webhookManager.hasWebhook("base")) {
|
|
2613
|
+
this.log(`No webhook available for agent: ${agent.id}`);
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
webhookAgentId = "base";
|
|
2617
|
+
this.log(`Using base webhook for agent: ${agent.id}`);
|
|
2618
|
+
}
|
|
2619
|
+
try {
|
|
2620
|
+
await this.webhookManager.sendAsAgent(webhookAgentId, content);
|
|
2621
|
+
this.messagesForwarded++;
|
|
2622
|
+
this.emit("message_forwarded", agent.id, content);
|
|
2623
|
+
this.log(`Forwarded message from ${agent.emoji} ${agent.name}`);
|
|
2624
|
+
} catch (error) {
|
|
2625
|
+
this.webhookErrors++;
|
|
2626
|
+
this.log(`Failed to forward message: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2627
|
+
this.emit("error", error instanceof Error ? error : new Error("Webhook send failed"));
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Start process detection loop
|
|
2632
|
+
*/
|
|
2633
|
+
startProcessDetection() {
|
|
2634
|
+
if (this.processCheckTimer) {
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
this.processCheckTimer = setInterval(() => {
|
|
2638
|
+
this.checkOpenClawProcess();
|
|
2639
|
+
}, this.config.processCheckInterval);
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Stop process detection loop
|
|
2643
|
+
*/
|
|
2644
|
+
stopProcessDetection() {
|
|
2645
|
+
if (this.processCheckTimer) {
|
|
2646
|
+
clearInterval(this.processCheckTimer);
|
|
2647
|
+
this.processCheckTimer = null;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Check if OpenClaw process is running
|
|
2652
|
+
*/
|
|
2653
|
+
checkOpenClawProcess() {
|
|
2654
|
+
const isRunning = this.isOpenClawRunning();
|
|
2655
|
+
if (isRunning && !this.gatewayClient.isConnected) {
|
|
2656
|
+
this.log("OpenClaw process detected, attempting to connect...");
|
|
2657
|
+
this.connect();
|
|
2658
|
+
} else if (!isRunning && this.openclawDetected) {
|
|
2659
|
+
this.openclawDetected = false;
|
|
2660
|
+
this.emit("openclaw_lost");
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Start periodic cleanup of stale response buffers
|
|
2665
|
+
*/
|
|
2666
|
+
startBufferCleanup() {
|
|
2667
|
+
if (this.bufferCleanupTimer) {
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
this.bufferCleanupTimer = setInterval(() => {
|
|
2671
|
+
const now = Date.now();
|
|
2672
|
+
let cleanedCount = 0;
|
|
2673
|
+
for (const [id, buffer] of this.responseBuffers) {
|
|
2674
|
+
if (now - buffer.lastUpdate > this.BUFFER_STALE_THRESHOLD) {
|
|
2675
|
+
if (buffer.content.trim()) {
|
|
2676
|
+
const agent = this.agentMapper.resolve(buffer.agentId) || this.agentMapper.getDefaultAgent();
|
|
2677
|
+
this.forwardToWebhook(agent, buffer.content).catch(() => {
|
|
2678
|
+
});
|
|
2679
|
+
}
|
|
2680
|
+
this.responseBuffers.delete(id);
|
|
2681
|
+
cleanedCount++;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
if (cleanedCount > 0 && this.config.verbose) {
|
|
2685
|
+
this.log(`Cleaned ${cleanedCount} stale response buffer(s)`);
|
|
2686
|
+
}
|
|
2687
|
+
}, 3e4);
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Stop buffer cleanup interval
|
|
2691
|
+
*/
|
|
2692
|
+
stopBufferCleanup() {
|
|
2693
|
+
if (this.bufferCleanupTimer) {
|
|
2694
|
+
clearInterval(this.bufferCleanupTimer);
|
|
2695
|
+
this.bufferCleanupTimer = null;
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Check if OpenClaw process is running on the system
|
|
2700
|
+
*/
|
|
2701
|
+
isOpenClawRunning() {
|
|
2702
|
+
try {
|
|
2703
|
+
const platform = process.platform;
|
|
2704
|
+
if (platform === "win32") {
|
|
2705
|
+
const result = execSync('tasklist /FI "IMAGENAME eq node.exe" /FO CSV', { encoding: "utf8", timeout: 5e3 });
|
|
2706
|
+
return result.toLowerCase().includes("openclaw");
|
|
2707
|
+
} else {
|
|
2708
|
+
const result = execSync("ps aux", { encoding: "utf8", timeout: 5e3 });
|
|
2709
|
+
return result.toLowerCase().includes("openclaw");
|
|
2710
|
+
}
|
|
2711
|
+
} catch {
|
|
2712
|
+
return true;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
/**
|
|
2716
|
+
* Log a message (if verbose mode or important)
|
|
2717
|
+
*/
|
|
2718
|
+
log(message) {
|
|
2719
|
+
if (this.config.verbose) {
|
|
2720
|
+
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
2721
|
+
console.log(`[${timestamp}] [TMC Daemon] ${message}`);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Force log regardless of verbose setting
|
|
2726
|
+
*/
|
|
2727
|
+
forceLog(message) {
|
|
2728
|
+
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
2729
|
+
console.log(`[${timestamp}] [TMC Daemon] ${message}`);
|
|
2730
|
+
}
|
|
2731
|
+
};
|
|
2732
|
+
|
|
1928
2733
|
// src/cli.ts
|
|
1929
2734
|
var program = new Command();
|
|
1930
2735
|
program.name("tmc").description("Too Many Claw - 35 AI agents collaborating via Discord").version("1.0.4");
|
|
@@ -2666,6 +3471,131 @@ program.command("debug").description("Debug OpenClaw configuration detection").o
|
|
|
2666
3471
|
console.log(chalk3.gray(" Check the paths above to see where Discord settings are stored.\n"));
|
|
2667
3472
|
}
|
|
2668
3473
|
});
|
|
3474
|
+
program.command("daemon").description("Run daemon mode to auto-connect to OpenClaw and forward agent messages via webhooks").option("-v, --verbose", "Enable verbose logging").option("--url <url>", "OpenClaw Gateway URL", "ws://127.0.0.1:18789").action(async (options) => {
|
|
3475
|
+
console.log(chalk3.cyan(`
|
|
3476
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
3477
|
+
\u2551 \u2551
|
|
3478
|
+
\u2551 \u{1F980} Too Many Claw - Daemon Mode \u2551
|
|
3479
|
+
\u2551 \u2551
|
|
3480
|
+
\u2551 Auto-connects to OpenClaw and forwards agent messages \u2551
|
|
3481
|
+
\u2551 through Discord webhooks \u2551
|
|
3482
|
+
\u2551 \u2551
|
|
3483
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
3484
|
+
`));
|
|
3485
|
+
const config = new ConfigManager();
|
|
3486
|
+
const webhooks = config.getAllWebhooks();
|
|
3487
|
+
if (Object.keys(webhooks).length === 0) {
|
|
3488
|
+
console.log(chalk3.yellow("\u26A0 No webhooks configured."));
|
|
3489
|
+
console.log(chalk3.gray(" Agent messages will not be forwarded to Discord."));
|
|
3490
|
+
console.log(chalk3.gray(" Run `tmc setup` to configure webhooks.\n"));
|
|
3491
|
+
} else {
|
|
3492
|
+
console.log(chalk3.green(`\u2713 ${Object.keys(webhooks).length} webhooks loaded
|
|
3493
|
+
`));
|
|
3494
|
+
}
|
|
3495
|
+
const daemon = new OpenClawDaemon({
|
|
3496
|
+
gatewayUrl: options.url,
|
|
3497
|
+
verbose: options.verbose || false,
|
|
3498
|
+
autoStart: true
|
|
3499
|
+
});
|
|
3500
|
+
let statusSpinner = ora("Connecting to OpenClaw Gateway...").start();
|
|
3501
|
+
let statsInterval = null;
|
|
3502
|
+
let isShuttingDown = false;
|
|
3503
|
+
daemon.on("connected", () => {
|
|
3504
|
+
statusSpinner.succeed("Connected to OpenClaw Gateway");
|
|
3505
|
+
console.log(chalk3.green("\n\u{1F99E} Daemon is now running!"));
|
|
3506
|
+
console.log(chalk3.gray(" Listening for agent responses..."));
|
|
3507
|
+
console.log(chalk3.gray(" Press Ctrl+C to stop.\n"));
|
|
3508
|
+
statsInterval = setInterval(() => {
|
|
3509
|
+
const stats = daemon.getStats();
|
|
3510
|
+
if (stats.messagesProcessed > 0 || options.verbose) {
|
|
3511
|
+
const uptime = formatUptime(stats.uptimeMs);
|
|
3512
|
+
console.log(chalk3.gray(
|
|
3513
|
+
`[Stats] Uptime: ${uptime} | Messages: ${stats.messagesProcessed} processed, ${stats.messagesForwarded} forwarded | Errors: ${stats.webhookErrors}`
|
|
3514
|
+
));
|
|
3515
|
+
}
|
|
3516
|
+
}, 3e4);
|
|
3517
|
+
});
|
|
3518
|
+
daemon.on("disconnected", (reason) => {
|
|
3519
|
+
if (!isShuttingDown) {
|
|
3520
|
+
console.log(chalk3.yellow(`
|
|
3521
|
+
\u26A0 Disconnected: ${reason}`));
|
|
3522
|
+
statusSpinner = ora("Waiting for OpenClaw Gateway...").start();
|
|
3523
|
+
}
|
|
3524
|
+
});
|
|
3525
|
+
daemon.on("reconnecting", (attempt) => {
|
|
3526
|
+
statusSpinner.text = `Reconnecting to OpenClaw Gateway... (attempt ${attempt})`;
|
|
3527
|
+
});
|
|
3528
|
+
daemon.on("openclaw_detected", () => {
|
|
3529
|
+
if (options.verbose) {
|
|
3530
|
+
console.log(chalk3.green("\n\u2713 OpenClaw process detected"));
|
|
3531
|
+
}
|
|
3532
|
+
});
|
|
3533
|
+
daemon.on("openclaw_lost", () => {
|
|
3534
|
+
console.log(chalk3.yellow("\n\u26A0 OpenClaw process no longer detected"));
|
|
3535
|
+
console.log(chalk3.gray(" Daemon will auto-reconnect when OpenClaw starts.\n"));
|
|
3536
|
+
statusSpinner = ora("Waiting for OpenClaw...").start();
|
|
3537
|
+
});
|
|
3538
|
+
daemon.on("message_forwarded", (agentId, content) => {
|
|
3539
|
+
if (options.verbose) {
|
|
3540
|
+
const preview = content.length > 50 ? content.substring(0, 50) + "..." : content;
|
|
3541
|
+
console.log(chalk3.blue(` \u2192 Forwarded from ${agentId}: ${preview}`));
|
|
3542
|
+
}
|
|
3543
|
+
});
|
|
3544
|
+
daemon.on("error", (error) => {
|
|
3545
|
+
if (options.verbose) {
|
|
3546
|
+
console.log(chalk3.red(` \u2717 Error: ${error.message}`));
|
|
3547
|
+
}
|
|
3548
|
+
});
|
|
3549
|
+
const shutdown = async () => {
|
|
3550
|
+
if (isShuttingDown) return;
|
|
3551
|
+
isShuttingDown = true;
|
|
3552
|
+
statusSpinner.stop();
|
|
3553
|
+
console.log(chalk3.yellow("\n\nShutting down daemon..."));
|
|
3554
|
+
if (statsInterval) {
|
|
3555
|
+
clearInterval(statsInterval);
|
|
3556
|
+
}
|
|
3557
|
+
const stats = daemon.getStats();
|
|
3558
|
+
console.log(chalk3.cyan("\n\u2501\u2501\u2501 Final Statistics \u2501\u2501\u2501"));
|
|
3559
|
+
console.log(chalk3.white(` Uptime: ${formatUptime(stats.uptimeMs)}`));
|
|
3560
|
+
console.log(chalk3.white(` Messages processed: ${stats.messagesProcessed}`));
|
|
3561
|
+
console.log(chalk3.white(` Messages forwarded: ${stats.messagesForwarded}`));
|
|
3562
|
+
console.log(chalk3.white(` Webhook errors: ${stats.webhookErrors}`));
|
|
3563
|
+
console.log(chalk3.cyan("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
3564
|
+
await daemon.stop();
|
|
3565
|
+
console.log(chalk3.green("Daemon stopped. Goodbye! \u{1F44B}\n"));
|
|
3566
|
+
process.exit(0);
|
|
3567
|
+
};
|
|
3568
|
+
process.on("SIGINT", shutdown);
|
|
3569
|
+
process.on("SIGTERM", shutdown);
|
|
3570
|
+
try {
|
|
3571
|
+
await daemon.start();
|
|
3572
|
+
if (!daemon.isConnected) {
|
|
3573
|
+
statusSpinner.warn("OpenClaw Gateway not found");
|
|
3574
|
+
console.log(chalk3.yellow("\n\u26A0 Could not connect to OpenClaw Gateway."));
|
|
3575
|
+
console.log(chalk3.gray("\nMake sure OpenClaw is running:"));
|
|
3576
|
+
console.log(chalk3.white(" $ openclaw gateway run\n"));
|
|
3577
|
+
console.log(chalk3.gray("The daemon will keep trying to connect..."));
|
|
3578
|
+
statusSpinner = ora("Waiting for OpenClaw Gateway...").start();
|
|
3579
|
+
}
|
|
3580
|
+
} catch (error) {
|
|
3581
|
+
statusSpinner.fail("Failed to start daemon");
|
|
3582
|
+
console.error(chalk3.red(`
|
|
3583
|
+
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
3584
|
+
process.exit(1);
|
|
3585
|
+
}
|
|
3586
|
+
});
|
|
3587
|
+
function formatUptime(ms) {
|
|
3588
|
+
const seconds = Math.floor(ms / 1e3);
|
|
3589
|
+
const minutes = Math.floor(seconds / 60);
|
|
3590
|
+
const hours = Math.floor(minutes / 60);
|
|
3591
|
+
if (hours > 0) {
|
|
3592
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
3593
|
+
} else if (minutes > 0) {
|
|
3594
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
3595
|
+
} else {
|
|
3596
|
+
return `${seconds}s`;
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
2669
3599
|
program.command("agents").description("List all available agents").option("-c, --category <category>", "Filter by category").action((options) => {
|
|
2670
3600
|
console.log(chalk3.cyan("\n\u{1F980} Too Many Claw - Agent Directory\n"));
|
|
2671
3601
|
let agents = AGENT_DEFINITIONS;
|