indieclaw-agent 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +61 -36
- package/install.sh +10 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -90,6 +90,7 @@ if (TLS_ENABLED) {
|
|
|
90
90
|
|
|
91
91
|
const terminals = new Map(); // id -> pty process
|
|
92
92
|
const activeChats = new Map(); // id -> http.ClientRequest
|
|
93
|
+
const activeSearches = new Map(); // ws -> child_process (one search per connection)
|
|
93
94
|
|
|
94
95
|
// --- Deep Link & QR Code ---
|
|
95
96
|
function getMachineIP() {
|
|
@@ -259,7 +260,9 @@ wss.on('connection', (ws) => {
|
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
// Route message
|
|
262
|
-
handleMessage(ws, msg)
|
|
263
|
+
handleMessage(ws, msg).catch((err) => {
|
|
264
|
+
console.error('[handleMessage] Unhandled error:', err.message || err);
|
|
265
|
+
});
|
|
263
266
|
});
|
|
264
267
|
|
|
265
268
|
ws.on('close', () => {
|
|
@@ -277,6 +280,12 @@ wss.on('connection', (ws) => {
|
|
|
277
280
|
activeChats.delete(id);
|
|
278
281
|
}
|
|
279
282
|
}
|
|
283
|
+
// Kill any active search process for this connection
|
|
284
|
+
const search = activeSearches.get(ws);
|
|
285
|
+
if (search) {
|
|
286
|
+
search.kill();
|
|
287
|
+
activeSearches.delete(ws);
|
|
288
|
+
}
|
|
280
289
|
});
|
|
281
290
|
});
|
|
282
291
|
|
|
@@ -354,9 +363,9 @@ async function handleMessage(ws, msg) {
|
|
|
354
363
|
case 'cron.history':
|
|
355
364
|
return handleCronHistory(ws, msg);
|
|
356
365
|
case 'agent.list':
|
|
357
|
-
return handleAgentList(ws, msg);
|
|
366
|
+
return await handleAgentList(ws, msg);
|
|
358
367
|
case 'agent.logs':
|
|
359
|
-
return handleAgentLogs(ws, msg);
|
|
368
|
+
return await handleAgentLogs(ws, msg);
|
|
360
369
|
default:
|
|
361
370
|
return replyError(ws, id, `Unknown message type: ${type}`);
|
|
362
371
|
}
|
|
@@ -1078,7 +1087,7 @@ function handleLogsSystem(ws, { id, lines = 200 }) {
|
|
|
1078
1087
|
const platform = os.platform();
|
|
1079
1088
|
|
|
1080
1089
|
if (platform === 'linux') {
|
|
1081
|
-
exec(`journalctl -n ${lines} --no-pager -o json`, { timeout: 10000, maxBuffer: 1024 * 1024 * 5 }, (err, stdout) => {
|
|
1090
|
+
exec(`journalctl -n ${lines} --no-pager --since "1 hour ago" -o json`, { timeout: 10000, maxBuffer: 1024 * 1024 * 5 }, (err, stdout) => {
|
|
1082
1091
|
if (err) return replyError(ws, id, err.message);
|
|
1083
1092
|
try {
|
|
1084
1093
|
const entries = stdout.trim().split('\n').filter(Boolean).map((line) => {
|
|
@@ -1137,32 +1146,44 @@ function handleLogsSystem(ws, { id, lines = 200 }) {
|
|
|
1137
1146
|
function handleLogsSearch(ws, { id, query, lines = 100 }) {
|
|
1138
1147
|
const platform = os.platform();
|
|
1139
1148
|
// Sanitize query to prevent command injection
|
|
1140
|
-
const safeQuery = query.replace(/["
|
|
1149
|
+
const safeQuery = query.replace(/["`$\\!;']/g, '');
|
|
1150
|
+
|
|
1151
|
+
if (!safeQuery || safeQuery.length < 2) {
|
|
1152
|
+
return reply(ws, id, []);
|
|
1153
|
+
}
|
|
1141
1154
|
|
|
1155
|
+
// Kill any previous search for this connection to prevent pile-up
|
|
1156
|
+
const prev = activeSearches.get(ws);
|
|
1157
|
+
if (prev) {
|
|
1158
|
+
prev.kill();
|
|
1159
|
+
activeSearches.delete(ws);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
let cmd;
|
|
1142
1163
|
if (platform === 'linux') {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
const entries = stdout.trim().split('\n').filter(Boolean).map((line) => ({
|
|
1146
|
-
timestamp: null,
|
|
1147
|
-
level: 'info',
|
|
1148
|
-
message: line,
|
|
1149
|
-
source: '',
|
|
1150
|
-
}));
|
|
1151
|
-
reply(ws, id, entries);
|
|
1152
|
-
});
|
|
1164
|
+
// --since "24h ago" limits search scope instead of grepping entire journal
|
|
1165
|
+
cmd = `journalctl -n ${lines} --no-pager --since "24 hours ago" -g "${safeQuery}"`;
|
|
1153
1166
|
} else {
|
|
1154
|
-
|
|
1155
|
-
exec(`grep -i "${safeQuery}" /var/log/system.log | tail -${lines}`, { timeout: 10000 }, (err, stdout) => {
|
|
1156
|
-
if (err) return replyError(ws, id, err.message || 'No matches found');
|
|
1157
|
-
const entries = stdout.trim().split('\n').filter(Boolean).map((line) => ({
|
|
1158
|
-
timestamp: null,
|
|
1159
|
-
level: 'info',
|
|
1160
|
-
message: line,
|
|
1161
|
-
source: '',
|
|
1162
|
-
}));
|
|
1163
|
-
reply(ws, id, entries);
|
|
1164
|
-
});
|
|
1167
|
+
cmd = `grep -i "${safeQuery}" /var/log/system.log | tail -${lines}`;
|
|
1165
1168
|
}
|
|
1169
|
+
|
|
1170
|
+
const child = exec(cmd, { timeout: 10000, maxBuffer: 1024 * 1024 * 2 }, (err, stdout) => {
|
|
1171
|
+
activeSearches.delete(ws);
|
|
1172
|
+
if (err) {
|
|
1173
|
+
// journalctl returns exit code 1 when no matches found — not an error
|
|
1174
|
+
if (err.killed) return; // process was killed by a newer search
|
|
1175
|
+
return reply(ws, id, []);
|
|
1176
|
+
}
|
|
1177
|
+
const entries = stdout.trim().split('\n').filter(Boolean).map((line) => ({
|
|
1178
|
+
timestamp: null,
|
|
1179
|
+
level: 'info',
|
|
1180
|
+
message: line,
|
|
1181
|
+
source: '',
|
|
1182
|
+
}));
|
|
1183
|
+
reply(ws, id, entries);
|
|
1184
|
+
});
|
|
1185
|
+
|
|
1186
|
+
activeSearches.set(ws, child);
|
|
1166
1187
|
}
|
|
1167
1188
|
|
|
1168
1189
|
// --- Cron History ---
|
|
@@ -1207,17 +1228,21 @@ function handleCronHistory(ws, { id }) {
|
|
|
1207
1228
|
|
|
1208
1229
|
// --- Agent List (OpenClaw Models) ---
|
|
1209
1230
|
async function handleAgentList(ws, { id }) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1231
|
+
try {
|
|
1232
|
+
const oc = await detectOpenClaw();
|
|
1233
|
+
if (oc.available) {
|
|
1234
|
+
const models = oc.models.map((modelId) => ({
|
|
1235
|
+
id: modelId,
|
|
1236
|
+
name: modelId,
|
|
1237
|
+
status: 'running',
|
|
1238
|
+
port: oc.port,
|
|
1239
|
+
}));
|
|
1240
|
+
return reply(ws, id, models);
|
|
1241
|
+
}
|
|
1242
|
+
reply(ws, id, []);
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
replyError(ws, id, 'OpenClaw detection failed: ' + (err.message || String(err)));
|
|
1219
1245
|
}
|
|
1220
|
-
reply(ws, id, []);
|
|
1221
1246
|
}
|
|
1222
1247
|
|
|
1223
1248
|
// --- Agent Logs ---
|
package/install.sh
CHANGED
|
@@ -135,12 +135,18 @@ if [ -n "$TOKEN" ]; then
|
|
|
135
135
|
echo -e " ${BOLD}Auth Token:${NC}"
|
|
136
136
|
echo -e " ${CYAN}${TOKEN}${NC}"
|
|
137
137
|
echo ""
|
|
138
|
-
echo -e " ${BOLD}Deep Link (paste in
|
|
138
|
+
echo -e " ${BOLD}Deep Link (paste in IndieClaw app → Add Machine → Paste Link):${NC}"
|
|
139
139
|
echo -e " ${CYAN}${DEEP_LINK}${NC}"
|
|
140
140
|
echo ""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
|
|
142
|
+
# Generate QR code using the installed qrcode-terminal package
|
|
143
|
+
AGENT_MODULES=$(npm root -g 2>/dev/null)/indieclaw-agent/node_modules
|
|
144
|
+
if [ -d "$AGENT_MODULES/qrcode-terminal" ]; then
|
|
145
|
+
echo -e " ${BOLD}Scan with IndieClaw app:${NC}"
|
|
146
|
+
echo ""
|
|
147
|
+
node -e "require('${AGENT_MODULES}/qrcode-terminal').generate('${DEEP_LINK}', {small: true}, function(qr) { qr.split('\n').forEach(function(l) { console.log(' ' + l) }) })" 2>/dev/null || true
|
|
148
|
+
echo ""
|
|
149
|
+
fi
|
|
144
150
|
fi
|
|
145
151
|
|
|
146
152
|
echo ""
|