aicq-codex 0.1.2 → 0.1.4

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/lib/chat.js CHANGED
@@ -1276,7 +1276,7 @@ class ChatManager {
1276
1276
  parsed && parsed.originalName, parsed && parsed.name,
1277
1277
  ];
1278
1278
  // file_info may be a JSON string (server persists it as TEXT) or object
1279
- for (const fi of [data && data.file_info, inner && inner.file_info]) {
1279
+ for (const fi of [data && data.file_info, inner && inner.file_info, parsed && parsed.file_info]) {
1280
1280
  if (!fi) continue;
1281
1281
  try {
1282
1282
  const obj = typeof fi === 'string' ? JSON.parse(fi) : fi;
package/lib/database.js CHANGED
@@ -462,8 +462,14 @@ class PluginDatabase {
462
462
  if (this._saveTimer) {
463
463
  clearTimeout(this._saveTimer);
464
464
  }
465
- this._save(); // Final save before closing
466
- this.db.close();
465
+ // [fix 2026-08-29] init() may still be awaiting initSqlJs() when close()
466
+ // is called (plugin dispose during startup): this.db is null then, and
467
+ // the unconditional _save()/db.close() crashed with
468
+ // "Cannot read properties of null (reading 'export')".
469
+ if (this.db) {
470
+ this._save(); // Final save before closing
471
+ this.db.close();
472
+ }
467
473
  }
468
474
  }
469
475
 
@@ -36,9 +36,25 @@ class ServerClient {
36
36
  opts.body = JSON.stringify(body);
37
37
  }
38
38
  const resp = await fetch(`${this.apiUrl}${path}`, opts);
39
- const data = await resp.json();
39
+ const data = await resp.json().catch(() => ({}));
40
40
  if (!resp.ok) {
41
- throw new Error(data.error || data.message || `HTTP ${resp.status}`);
41
+ // [fix 2026-08-29] the AICQ server errors come back as
42
+ // {"error": {"code": "...", "message": "..."}} — a nested OBJECT.
43
+ // The old `new Error(data.error || ...)` interpolated the object and
44
+ // every failure surfaced to users/models as "[object Object]".
45
+ const err = data.error;
46
+ let msg;
47
+ if (err && typeof err === 'object') {
48
+ msg = err.message || err.code;
49
+ } else if (typeof err === 'string' && err) {
50
+ msg = err;
51
+ } else {
52
+ msg = data.message;
53
+ }
54
+ const e = new Error(msg || `HTTP ${resp.status}`);
55
+ e.status = resp.status;
56
+ e.code = (err && typeof err === 'object' && err.code) || undefined;
57
+ throw e;
42
58
  }
43
59
  return data;
44
60
  }
@@ -230,6 +246,9 @@ class ServerClient {
230
246
 
231
247
  connectWS() {
232
248
  if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
249
+ // [fix 2026-08-29] a direct connectWS() call (non-start() path) also
250
+ // implies the client wants to stay connected.
251
+ this._running = true;
233
252
 
234
253
  const identity = this.identity.loadAgent(this.currentAgentId);
235
254
  if (!identity || !this.jwtToken) {
@@ -339,6 +358,11 @@ class ServerClient {
339
358
  }
340
359
 
341
360
  _scheduleReconnect() {
361
+ // [fix 2026-08-29] never reschedule after an explicit stop()/disconnect():
362
+ // the WS 'close' handler calls this even for intentional teardown, which
363
+ // left a reconnect timer (+ ping timer + open socket) keeping the host
364
+ // process alive forever after plugin dispose.
365
+ if (!this._running) return;
342
366
  if (this._reconnectTimer) return;
343
367
  this._reconnectTimer = setTimeout(() => {
344
368
  this._reconnectTimer = null;
@@ -354,8 +378,10 @@ class ServerClient {
354
378
  async start(agentId) {
355
379
  try {
356
380
  await this.ensureAuth(agentId);
357
- this.connectWS();
381
+ // [fix 2026-08-29] set _running BEFORE connecting so a fast 'close'
382
+ // event (e.g. server hiccup) is allowed to schedule a reconnect.
358
383
  this._running = true;
384
+ this.connectWS();
359
385
  console.log('[ServerClient] Started for agent:', agentId);
360
386
  } catch (e) {
361
387
  console.error('[ServerClient] Start failed:', e.message);
@@ -372,6 +398,11 @@ class ServerClient {
372
398
  }
373
399
 
374
400
  disconnect() {
401
+ // [fix 2026-08-29] mark stopped FIRST so the ws 'close' handler cannot
402
+ // schedule a zombie reconnect, and clear the ping interval here (it was
403
+ // only cleared in the 'close' event handler, which never fires if the
404
+ // socket never opened).
405
+ this._running = false;
375
406
  if (this.ws) {
376
407
  // SPEC 合规: offline 消息必须带 nodeId 字段
377
408
  // 见 aicqSDK/SPEC.md 第 215-219 行
@@ -383,6 +414,7 @@ class ServerClient {
383
414
  this.ws = null;
384
415
  }
385
416
  this.connected = false;
417
+ if (this._pingTimer) { clearInterval(this._pingTimer); this._pingTimer = null; }
386
418
  if (this._reconnectTimer) {
387
419
  clearTimeout(this._reconnectTimer);
388
420
  this._reconnectTimer = null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aicq-codex",
3
- "version": "0.1.2",
4
- "description": "AICQ End-to-end Encrypted Chat plugin for OpenAI Codex Agent Plugins v1 manifest + stdio MCP server exposing AICQ friend management, chat, file transfer, and group messaging tools",
3
+ "version": "0.1.4",
4
+ "description": "AICQ End-to-end Encrypted Chat plugin for OpenAI Codex \u2014 Agent Plugins v1 manifest + stdio MCP server exposing AICQ friend management, chat, file transfer, and group messaging tools",
5
5
  "type": "module",
6
6
  "main": "./mcp/server.js",
7
7
  "bin": {
@@ -53,4 +53,4 @@
53
53
  "engines": {
54
54
  "node": ">=20.0.0"
55
55
  }
56
- }
56
+ }