indieclaw-agent 2.4.1 → 2.4.2

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 (2) hide show
  1. package/index.js +102 -15
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -146,28 +146,85 @@ try {
146
146
  }
147
147
 
148
148
  // --- OpenClaw Detection, Config & Gateway Client ---
149
- const OPENCLAW_CONFIG_PATHS = [
150
- path.join(os.homedir(), '.openclaw', 'openclaw.json'),
151
- path.join(os.homedir(), '.openclaw', 'openclaw.json5'),
152
- ];
149
+
150
+ // Search multiple possible config locations (systemd may resolve homedir differently)
151
+ function getConfigPaths() {
152
+ const paths = [];
153
+ const home = os.homedir();
154
+ paths.push(path.join(home, '.openclaw', 'openclaw.json'));
155
+ paths.push(path.join(home, '.openclaw', 'openclaw.json5'));
156
+ // Also check /root explicitly (systemd services may not resolve ~ to /root)
157
+ if (home !== '/root') {
158
+ paths.push('/root/.openclaw/openclaw.json');
159
+ paths.push('/root/.openclaw/openclaw.json5');
160
+ }
161
+ return paths;
162
+ }
163
+
164
+ const OPENCLAW_CONFIG_PATHS = getConfigPaths();
165
+
166
+ function stripJsonComments(raw) {
167
+ // Remove comments WITHOUT corrupting URLs inside strings
168
+ // Walk char-by-char, track if we're inside a string
169
+ let result = '';
170
+ let inString = false;
171
+ let escaped = false;
172
+ for (let i = 0; i < raw.length; i++) {
173
+ const ch = raw[i];
174
+ if (inString) {
175
+ result += ch;
176
+ if (escaped) { escaped = false; continue; }
177
+ if (ch === '\\') { escaped = true; continue; }
178
+ if (ch === '"') { inString = false; }
179
+ continue;
180
+ }
181
+ // Not in string
182
+ if (ch === '"') { inString = true; result += ch; continue; }
183
+ // Line comment
184
+ if (ch === '/' && raw[i + 1] === '/') {
185
+ // Skip to end of line
186
+ while (i < raw.length && raw[i] !== '\n') i++;
187
+ result += '\n';
188
+ continue;
189
+ }
190
+ // Block comment
191
+ if (ch === '/' && raw[i + 1] === '*') {
192
+ i += 2;
193
+ while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/')) i++;
194
+ i++; // skip closing /
195
+ continue;
196
+ }
197
+ result += ch;
198
+ }
199
+ return result;
200
+ }
153
201
 
154
202
  function readOpenClawConfig() {
155
203
  for (const cfgPath of OPENCLAW_CONFIG_PATHS) {
156
204
  try {
157
- if (!fs.existsSync(cfgPath)) continue;
205
+ const exists = fs.existsSync(cfgPath);
206
+ console.log(` [OpenClaw] Config ${cfgPath}: ${exists ? 'EXISTS' : 'not found'}`);
207
+ if (!exists) continue;
158
208
  let raw = fs.readFileSync(cfgPath, 'utf-8');
159
- raw = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
209
+ // Strip comments safely (preserves URLs in strings)
210
+ raw = stripJsonComments(raw);
211
+ // Remove trailing commas
160
212
  raw = raw.replace(/,\s*([\]}])/g, '$1');
161
213
  const config = JSON.parse(raw);
162
214
  const gw = config.gateway || {};
163
- return {
215
+ const result = {
164
216
  port: gw.port || 18789,
165
217
  token: gw.auth?.token || gw.auth?.password || null,
166
218
  host: gw.bind || '127.0.0.1',
167
219
  };
168
- } catch {}
220
+ console.log(` [OpenClaw] Config parsed: port=${result.port}, host=${result.host}, token=${result.token ? 'SET' : 'NONE'}`);
221
+ return result;
222
+ } catch (err) {
223
+ console.log(` [OpenClaw] Config parse error for ${cfgPath}: ${err.message}`);
224
+ }
169
225
  }
170
226
  // Fallback: check env var
227
+ console.log(' [OpenClaw] No config file found, using defaults (port 18789)');
171
228
  return {
172
229
  port: parseInt(process.env.OPENCLAW_GATEWAY_PORT || '18789', 10),
173
230
  token: process.env.OPENCLAW_GATEWAY_TOKEN || null,
@@ -180,26 +237,43 @@ let openClawConfig = readOpenClawConfig();
180
237
  // TCP port check — fast and reliable, no auth needed
181
238
  function isPortListening(port, host = '127.0.0.1') {
182
239
  return new Promise((resolve) => {
240
+ console.log(` [OpenClaw] TCP probe ${host}:${port}...`);
183
241
  const sock = net.createConnection({ port, host }, () => {
242
+ console.log(` [OpenClaw] TCP probe ${host}:${port} → OPEN`);
184
243
  sock.destroy();
185
244
  resolve(true);
186
245
  });
187
- sock.on('error', () => resolve(false));
188
- sock.setTimeout(2000, () => { sock.destroy(); resolve(false); });
246
+ sock.on('error', (err) => {
247
+ console.log(` [OpenClaw] TCP probe ${host}:${port} ERROR: ${err.message}`);
248
+ resolve(false);
249
+ });
250
+ sock.setTimeout(2000, () => {
251
+ console.log(` [OpenClaw] TCP probe ${host}:${port} → TIMEOUT`);
252
+ sock.destroy();
253
+ resolve(false);
254
+ });
189
255
  });
190
256
  }
191
257
 
192
258
  async function detectOpenClaw() {
259
+ console.log(' [OpenClaw] Running detection...');
193
260
  openClawConfig = readOpenClawConfig();
194
- const listening = await isPortListening(openClawConfig.port);
261
+
262
+ // Always probe 127.0.0.1 — the gateway listens on loopback
263
+ const port = openClawConfig.port || 18789;
264
+ const listening = await isPortListening(port, '127.0.0.1');
195
265
  if (listening) {
196
- return { available: true, models: ['openclaw'], port: openClawConfig.port };
266
+ console.log(` [OpenClaw] Detection result: AVAILABLE (port ${port})`);
267
+ return { available: true, models: ['openclaw'], port };
197
268
  }
269
+
198
270
  // Fallback: check if config file exists (installed but not running)
199
271
  const installed = OPENCLAW_CONFIG_PATHS.some((p) => fs.existsSync(p));
200
272
  if (installed) {
201
- return { available: false, models: [], port: openClawConfig.port, installed: true };
273
+ console.log(` [OpenClaw] Detection result: INSTALLED but gateway not running`);
274
+ return { available: false, models: [], port, installed: true };
202
275
  }
276
+ console.log(' [OpenClaw] Detection result: NOT FOUND');
203
277
  return { available: false, models: [], port: null };
204
278
  }
205
279
 
@@ -213,6 +287,7 @@ function connectOcGateway() {
213
287
  return new Promise((resolve, reject) => {
214
288
  openClawConfig = readOpenClawConfig();
215
289
  const url = `ws://127.0.0.1:${openClawConfig.port}`;
290
+ console.log(` [OpenClaw] Connecting to gateway: ${url}`);
216
291
 
217
292
  if (ocGateway) { try { ocGateway.close(); } catch {} }
218
293
  ocGateway = null;
@@ -220,12 +295,14 @@ function connectOcGateway() {
220
295
 
221
296
  const ws = new WebSocket(url);
222
297
  let connectReqId = null;
223
- const timeout = setTimeout(() => { ws.close(); reject(new Error('Gateway timeout')); }, 10000);
298
+ const timeout = setTimeout(() => { console.log(' [OpenClaw] Gateway connection TIMEOUT'); ws.close(); reject(new Error('Gateway timeout')); }, 10000);
224
299
 
225
300
  ws.on('message', (data) => {
226
301
  let msg;
227
302
  try { msg = JSON.parse(data.toString()); } catch { return; }
228
303
 
304
+ console.log(` [OpenClaw] Gateway msg: type=${msg.type}, event=${msg.event || ''}, id=${msg.id || ''}, ok=${msg.ok}`);
305
+
229
306
  // Step 1: Gateway sends connect.challenge
230
307
  if (msg.type === 'event' && msg.event === 'connect.challenge') {
231
308
  connectReqId = crypto.randomUUID();
@@ -292,8 +369,9 @@ function connectOcGateway() {
292
369
  }
293
370
  });
294
371
 
295
- ws.on('error', (err) => { clearTimeout(timeout); ocReady = false; reject(err); });
372
+ ws.on('error', (err) => { console.log(` [OpenClaw] Gateway error: ${err.message}`); clearTimeout(timeout); ocReady = false; reject(err); });
296
373
  ws.on('close', () => {
374
+ console.log(' [OpenClaw] Gateway connection closed');
297
375
  ocReady = false;
298
376
  ocGateway = null;
299
377
  // Notify all pending chat streams that gateway disconnected
@@ -383,6 +461,7 @@ wss.on('connection', (ws) => {
383
461
  if (msg.type === 'auth' && msg.token === AUTH_TOKEN) {
384
462
  authenticated = true;
385
463
  const openclaw = await detectOpenClaw();
464
+ console.log(` [Auth] Sending auth success, openclaw:`, JSON.stringify(openclaw));
386
465
  return ws.send(JSON.stringify({ type: 'auth', success: true, openclaw }));
387
466
  }
388
467
  if (msg.type === 'ping') {
@@ -1105,14 +1184,18 @@ function handleTerminalStop(ws, { id }) {
1105
1184
  // --- Chat (OpenClaw Gateway Proxy) ---
1106
1185
  async function handleChatSend(ws, { id, messages }) {
1107
1186
  try {
1187
+ console.log(` [Chat] handleChatSend id=${id}, messages=${messages.length}`);
1188
+
1108
1189
  // Connect to OpenClaw gateway (auto-reconnects if needed)
1109
1190
  await getOcGateway();
1191
+ console.log(' [Chat] Gateway connected');
1110
1192
 
1111
1193
  // Extract the last user message from the conversation
1112
1194
  const lastUserMsg = [...messages].reverse().find(m => m.role === 'user');
1113
1195
  if (!lastUserMsg) {
1114
1196
  return send(ws, { type: 'chat.done', id, error: 'No user message found' });
1115
1197
  }
1198
+ console.log(` [Chat] Sending to OpenClaw: "${lastUserMsg.content.substring(0, 80)}..."`);
1116
1199
 
1117
1200
  // Build a session key — unique per chat conversation
1118
1201
  const sessionKey = `agent:indieclaw:mobile:${id}`;
@@ -1125,12 +1208,16 @@ async function handleChatSend(ws, { id, messages }) {
1125
1208
  idempotencyKey,
1126
1209
  });
1127
1210
 
1211
+ console.log(` [Chat] ocRequest result:`, JSON.stringify(result).substring(0, 200));
1212
+
1128
1213
  // Register for streaming events using the runId from the response
1129
1214
  const runId = result?.runId || result?.id || id;
1215
+ console.log(` [Chat] Registered callback for runId=${runId}`);
1130
1216
  ocChatCallbacks.set(runId, { ws, chatId: id });
1131
1217
  activeChats.set(id, { runId, _ws: ws });
1132
1218
 
1133
1219
  } catch (err) {
1220
+ console.log(` [Chat] ERROR: ${err.message}`);
1134
1221
  send(ws, { type: 'chat.done', id, error: `OpenClaw: ${err.message}` });
1135
1222
  }
1136
1223
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indieclaw-agent",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "description": "Manage your server from your phone. Agent for the IndieClaw mobile app.",
5
5
  "main": "index.js",
6
6
  "bin": {