aicq-openclaw-plugin 1.0.6 → 1.0.7

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/index.js CHANGED
@@ -7826,12 +7826,12 @@ var v4_default = v4;
7826
7826
 
7827
7827
  // dist/config.js
7828
7828
  var __filename = fileURLToPath(import.meta.url);
7829
- var __dirname = path.dirname(__filename);
7829
+ var __dirname2 = path.dirname(__filename);
7830
7830
  var SERVER_URL = process.env.AICQ_SERVER_URL || "https://aicq.online:61018";
7831
7831
  function loadConfig(overrides) {
7832
7832
  let schemaDefaults = {};
7833
7833
  try {
7834
- const manifestPath = path.resolve(__dirname, "..", "openclaw.plugin.json");
7834
+ const manifestPath = path.resolve(__dirname2, "..", "openclaw.plugin.json");
7835
7835
  const manifestRaw = fs.readFileSync(manifestPath, "utf-8");
7836
7836
  const manifest = JSON.parse(manifestRaw);
7837
7837
  const schema = manifest.configSchema;
@@ -10192,91 +10192,86 @@ function loadPage(page) {
10192
10192
  }
10193
10193
 
10194
10194
  // \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
10195
- // PAGE: Dashboard
10195
+ // PAGE: Dashboard \u2014 render skeleton immediately, load data async
10196
10196
  // \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
10197
10197
  async function loadDashboard() {
10198
10198
  const el = $('#dashboard-content');
10199
- html(el, '<div class="loading-mask"><div class="spinner"></div>Loading dashboard...</div>');
10199
+ // Render skeleton immediately \u2014 never show spinner that blocks UI
10200
+ html(el, \\\`
10201
+ <div class="stats-grid">
10202
+ <div class="stat-card"><div class="stat-icon" style="background:var(--accent-bg)">\u{1F4E1}</div><div class="stat-label">Server Status</div><div class="stat-value" style="font-size:16px"><div class="spinner" style="width:16px;height:16px;border-width:2px;margin:0 auto"></div></div><div class="stat-sub" id="dash-server-url">Loading...</div></div>
10203
+ <div class="stat-card"><div class="stat-icon" style="background:var(--ok-bg)">\u{1F465}</div><div class="stat-label">Total Friends</div><div class="stat-value" id="dash-friend-count"><div class="spinner" style="width:16px;height:16px;border-width:2px;margin:0 auto"></div></div><div class="stat-sub" id="dash-friend-sub">loading...</div></div>
10204
+ <div class="stat-card"><div class="stat-icon" style="background:var(--info-bg)">\u{1F517}</div><div class="stat-label">Active Sessions</div><div class="stat-value" id="dash-sessions"><div class="spinner" style="width:16px;height:16px;border-width:2px;margin:0 auto"></div></div><div class="stat-sub">Encrypted sessions</div></div>
10205
+ <div class="stat-card"><div class="stat-icon" style="background:var(--warn-bg)">\u{1F511}</div><div class="stat-label">Agent ID</div><div class="stat-value mono" style="font-size:13px" id="dash-agent-id">\u2014</div><div class="stat-sub" id="dash-fingerprint">\u2014</div></div>
10206
+ </div>
10207
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
10208
+ <div class="card"><div class="card-header"><div class="card-title">\u{1F4CB} Friends</div><button class="btn btn-sm btn-ghost" onclick="navigate('friends')">View All \u2192</button></div><div id="dash-friends-list"><div class="loading-mask" style="padding:20px"><div class="spinner"></div>Loading friends...</div></div></div>
10209
+ <div class="card"><div class="card-header"><div class="card-title">\u{1F916} Identity Info</div></div><div id="dash-identity"><div class="loading-mask" style="padding:20px"><div class="spinner"></div>Loading...</div></div></div>
10210
+ </div>
10211
+ \\\`);
10200
10212
 
10201
- // Load local data first (status + identity), then remote (friends) \u2014 each independent
10202
- const [statusRes, identityRes] = await Promise.all([safeApi('/status'), safeApi('/identity')]);
10213
+ // Load each data source independently \u2014 no await blocking between them
10214
+ const statusRes = await safeApi('/status', { _timeout: 5000 });
10203
10215
  const status = statusRes.data || {};
10204
- const identity = identityRes.data || {};
10205
-
10206
- // Friends is remote \u2014 load independently, don't block
10207
- const friendsRes = await safeApi('/friends', { _timeout: 10000 });
10208
- const friendList = (friendsRes.data?.friends) || [];
10209
- const friendsError = friendsRes.error;
10210
10216
 
10217
+ // Update status card immediately
10211
10218
  const connCls = status.connected ? 'dot-ok' : 'dot-err';
10212
10219
  const connText = status.connected ? 'Connected' : 'Disconnected';
10213
- const aiFriends = friendList.filter(f => f.friendType === 'ai').length;
10214
- const humanFriends = friendList.filter(f => f.friendType !== 'ai').length;
10220
+ const connEl = $('#dash-server-url');
10221
+ if (connEl) connEl.textContent = status.serverUrl || '\u2014';
10222
+ const statusValEl = el.querySelector('.stat-value');
10223
+ if (statusValEl) statusValEl.innerHTML = '<span class="dot ' + connCls + '" style="display:inline-block;width:8px;height:8px;vertical-align:middle"></span> <span style="font-size:16px">' + connText + '</span>';
10215
10224
 
10216
- // Update header status dot
10225
+ // Update header dot
10217
10226
  const hDot = $('#header-dot');
10218
- if (hDot) hDot.className = 'dot ' + (status.connected ? 'dot-ok' : 'dot-err');
10227
+ if (hDot) hDot.className = 'dot ' + connCls;
10219
10228
  const hTxt = $('#header-status');
10220
- if (hTxt) hTxt.textContent = status.connected ? 'Connected' : 'Disconnected';
10221
-
10222
- // Build friends section
10223
- let friendsSection = '';
10224
- if (friendsError) {
10225
- friendsSection = '<div class="card"><div class="card-header"><div class="card-title">\u{1F4CB} Friends</div><button class="btn btn-sm btn-ghost" onclick="navigate('friends')">View All \u2192</button></div>' + errorBlock('Friend List Unavailable', friendsError + ' \u2014 Remote AICQ server may be unreachable. <button class="btn btn-sm btn-default" style="margin-top:8px" onclick="loadDashboard()">\u{1F504} Retry</button>') + '</div>';
10226
- } else {
10227
- friendsSection = '<div class="card"><div class="card-header"><div class="card-title">\u{1F4CB} Recent Friends</div><button class="btn btn-sm btn-ghost" onclick="navigate('friends')">View All \u2192</button></div>' + renderMiniFriendList(friendList.slice(0, 5)) + '</div>';
10228
- }
10229
-
10230
- // Build identity section
10231
- let identitySection = '';
10232
- if (identityRes.error) {
10233
- identitySection = errorBlock('Identity Unavailable', identityRes.error);
10234
- } else {
10235
- identitySection = \\\`
10229
+ if (hTxt) hTxt.textContent = connText;
10230
+
10231
+ // Agent ID card
10232
+ const agentEl = $('#dash-agent-id');
10233
+ if (agentEl) agentEl.textContent = status.agentId || '\u2014';
10234
+ const fpEl = $('#dash-fingerprint');
10235
+ if (fpEl) fpEl.textContent = 'Fingerprint: ' + (status.fingerprint || '\u2014');
10236
+ const sessEl = $('#dash-sessions');
10237
+ if (sessEl) sessEl.textContent = status.sessionCount || 0;
10238
+
10239
+ // Identity \u2014 load independently
10240
+ safeApi('/identity', { _timeout: 5000 }).then(identityRes => {
10241
+ const identityEl = $('#dash-identity');
10242
+ if (!identityEl) return;
10243
+ if (identityRes.error) {
10244
+ identityEl.innerHTML = errorBlock('Identity Unavailable', identityRes.error + ' <button class="btn btn-sm btn-default" style="margin-top:8px" onclick="loadDashboard()">\u{1F504} Retry</button>');
10245
+ return;
10246
+ }
10247
+ const identity = identityRes.data || {};
10248
+ identityEl.innerHTML = \\\`
10236
10249
  <div class="detail-row"><div class="detail-key">Agent ID</div><div class="detail-val mono" style="cursor:pointer" onclick="copyText('\${escHtml(identity.agentId || '')}')">\${escHtml(identity.agentId || '\u2014')} \u{1F4CB}</div></div>
10237
10250
  <div class="detail-row"><div class="detail-key">Fingerprint</div><div class="detail-val mono">\${escHtml(identity.publicKeyFingerprint || '\u2014')}</div></div>
10238
10251
  <div class="detail-row"><div class="detail-key">Server URL</div><div class="detail-val mono" style="cursor:pointer" onclick="copyText('\${escHtml(identity.serverUrl || '')}')">\${escHtml(identity.serverUrl || '\u2014')} \u{1F4CB}</div></div>
10239
10252
  <div class="detail-row"><div class="detail-key">Connection</div><div class="detail-val"><span class="badge badge-\${identity.connected ? 'ok' : 'danger'}">\${identity.connected ? 'Online' : 'Offline'}</span></div></div>
10240
10253
  \\\`;
10241
- }
10254
+ });
10242
10255
 
10243
- html(el, \\\`
10244
- <div class="stats-grid">
10245
- <div class="stat-card">
10246
- <div class="stat-icon" style="background:var(--accent-bg)">\u{1F4E1}</div>
10247
- <div class="stat-label">Server Status</div>
10248
- <div class="stat-value" style="font-size:16px;display:flex;align-items:center;gap:8px">
10249
- <span class="dot \${connCls}"></span> \${connText}
10250
- </div>
10251
- <div class="stat-sub">\${escHtml(status.serverUrl || '\u2014')}</div>
10252
- </div>
10253
- <div class="stat-card">
10254
- <div class="stat-icon" style="background:var(--ok-bg)">\u{1F465}</div>
10255
- <div class="stat-label">Total Friends</div>
10256
- <div class="stat-value">\${friendsError ? '\u2014' : friendList.length}</div>
10257
- <div class="stat-sub">\${friendsError ? 'unavailable' : aiFriends + ' AI \xB7 ' + humanFriends + ' Human'}</div>
10258
- </div>
10259
- <div class="stat-card">
10260
- <div class="stat-icon" style="background:var(--info-bg)">\u{1F517}</div>
10261
- <div class="stat-label">Active Sessions</div>
10262
- <div class="stat-value">\${status.sessionCount || 0}</div>
10263
- <div class="stat-sub">Encrypted sessions</div>
10264
- </div>
10265
- <div class="stat-card">
10266
- <div class="stat-icon" style="background:var(--warn-bg)">\u{1F511}</div>
10267
- <div class="stat-label">Agent ID</div>
10268
- <div class="stat-value mono" style="font-size:13px">\${escHtml(status.agentId || '\u2014')}</div>
10269
- <div class="stat-sub">Fingerprint: \${escHtml(status.fingerprint || '\u2014')}</div>
10270
- </div>
10271
- </div>
10272
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px">
10273
- \${friendsSection}
10274
- <div class="card">
10275
- <div class="card-header"><div class="card-title">\u{1F916} Identity Info</div></div>
10276
- \${identitySection}
10277
- </div>
10278
- </div>
10279
- \\\`);
10256
+ // Friends \u2014 load independently from remote server
10257
+ safeApi('/friends', { _timeout: 10000 }).then(friendsRes => {
10258
+ const fcEl = $('#dash-friend-count');
10259
+ const fsEl = $('#dash-friend-sub');
10260
+ const flEl = $('#dash-friends-list');
10261
+ if (friendsRes.error) {
10262
+ if (fcEl) fcEl.textContent = '\u2014';
10263
+ if (fsEl) fsEl.textContent = 'unavailable';
10264
+ if (flEl) flEl.innerHTML = errorBlock('Friend List Unavailable', friendsRes.error + ' \u2014 Remote server unreachable. <button class="btn btn-sm btn-default" style="margin-top:8px" onclick="loadDashboard()">\u{1F504} Retry</button>');
10265
+ toast('Cannot load friends: ' + friendsRes.error, 'warn');
10266
+ return;
10267
+ }
10268
+ const friendList = friendsRes.data?.friends || [];
10269
+ const aiFriends = friendList.filter(f => f.friendType === 'ai').length;
10270
+ const humanFriends = friendList.filter(f => f.friendType !== 'ai').length;
10271
+ if (fcEl) fcEl.textContent = friendList.length;
10272
+ if (fsEl) fsEl.textContent = aiFriends + ' AI \xB7 ' + humanFriends + ' Human';
10273
+ if (flEl) flEl.innerHTML = renderMiniFriendList(friendList.slice(0, 5));
10274
+ });
10280
10275
  }
10281
10276
 
10282
10277
  function renderMiniFriendList(friends) {
@@ -10678,14 +10673,66 @@ async function saveModelConfig() {
10678
10673
  }
10679
10674
 
10680
10675
  // \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
10681
- // PAGE: Settings
10676
+ // PAGE: Settings \u2014 with Update button
10682
10677
  // \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
10678
+ let _updating = false;
10679
+
10683
10680
  async function loadSettings() {
10684
10681
  const el = $('#settings-content');
10685
- html(el, '<div class="loading-mask"><div class="spinner"></div>Loading settings...</div>');
10686
- const [statusRes, identityRes, configRes] = await Promise.all([safeApi('/status'), safeApi('/identity'), safeApi('/config')]);
10682
+ // Render skeleton immediately
10683
+ html(el, \\\`
10684
+ <p class="section-desc">AICQ plugin runtime configuration and system information.</p>
10685
+ <div class="card" id="settings-update-card"><div class="loading-mask" style="padding:20px"><div class="spinner"></div>Loading...</div></div>
10686
+ <div id="settings-rest"></div>
10687
+ \\\`);
10687
10688
 
10688
- let out = '<p class="section-desc">AICQ plugin runtime configuration and system information.</p>';
10689
+ // Load update check, status, identity, config \u2014 all independently
10690
+ const [updateRes, statusRes, identityRes, configRes, pluginRes] = await Promise.all([
10691
+ safeApi('/update/check', { _timeout: 15000 }),
10692
+ safeApi('/status', { _timeout: 5000 }),
10693
+ safeApi('/identity', { _timeout: 5000 }),
10694
+ safeApi('/config', { _timeout: 5000 }),
10695
+ safeApi('/plugin-info', { _timeout: 5000 }),
10696
+ ]);
10697
+
10698
+ // \u2500\u2500 Update Card \u2500\u2500
10699
+ const updateCard = $('#settings-update-card');
10700
+ if (updateCard) {
10701
+ const upd = updateRes.data || {};
10702
+ const pInfo = pluginRes.data || {};
10703
+ const currentVer = pInfo.version || upd.currentVersion || 'unknown';
10704
+ const latestVer = upd.latestVersion || currentVer;
10705
+ const hasUpdate = upd.updateAvailable;
10706
+
10707
+ updateCard.innerHTML = \\\`
10708
+ <div class="card-header">
10709
+ <div class="card-title">\u{1F504} Plugin Update</div>
10710
+ <div style="display:flex;gap:6px;align-items:center">
10711
+ <span class="badge badge-\${hasUpdate ? 'warn' : 'ok'}">\${hasUpdate ? 'Update Available!' : 'Up to date'}</span>
10712
+ </div>
10713
+ </div>
10714
+ <div class="detail-row"><div class="detail-key">Current Version</div><div class="detail-val mono">\${escHtml(currentVer)}</div></div>
10715
+ <div class="detail-row"><div class="detail-key">Latest Version</div><div class="detail-val mono">\${escHtml(latestVer)}</div></div>
10716
+ <div class="detail-row"><div class="detail-key">Package</div><div class="detail-val mono">aicq-openclaw-plugin</div></div>
10717
+ \${pluginRes.error ? '' : \\\`
10718
+ <div class="detail-row"><div class="detail-key">Node.js</div><div class="detail-val mono">\${escHtml(pInfo.nodeVersion || '\u2014')}</div></div>
10719
+ <div class="detail-row"><div class="detail-key">Uptime</div><div class="detail-val">\${pInfo.uptime ? Math.floor(pInfo.uptime / 3600) + 'h ' + Math.floor((pInfo.uptime % 3600) / 60) + 'm' : '\u2014'}</div></div>
10720
+ \\\`}
10721
+ <div style="margin-top:16px;display:flex;gap:8px;flex-wrap:wrap">
10722
+ <button class="btn btn-primary" id="btn-check-update" onclick="checkForUpdates()">\u{1F50D} Check for Updates</button>
10723
+ <button class="btn btn-ok \${hasUpdate ? '' : 'btn-ghost'}" id="btn-do-update" onclick="doUpdate(false)">\u{1F4E5} \${hasUpdate ? 'Update to v' + escHtml(latestVer) : 'Incremental Update'}</button>
10724
+ <button class="btn btn-warn" id="btn-force-update" onclick="doUpdate(true)">\u{1F503} Force Reinstall</button>
10725
+ </div>
10726
+ <div id="update-output" style="display:none;margin-top:12px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:12px;max-height:300px;overflow-y:auto">
10727
+ <pre style="font-size:12px;color:var(--text2);white-space:pre-wrap;margin:0" id="update-log"></pre>
10728
+ </div>
10729
+ \\\`;
10730
+ }
10731
+
10732
+ // \u2500\u2500 Rest of settings \u2500\u2500
10733
+ const restEl = $('#settings-rest');
10734
+ if (!restEl) return;
10735
+ let out = '';
10689
10736
 
10690
10737
  // Connection section
10691
10738
  if (statusRes.error) {
@@ -10737,12 +10784,67 @@ async function loadSettings() {
10737
10784
  <div class="card-header"><div class="card-title">\u{1F4CA} Statistics</div></div>
10738
10785
  <div class="detail-row"><div class="detail-key">Friends Count</div><div class="detail-val">\${s.friendCount || 0}</div></div>
10739
10786
  <div class="detail-row"><div class="detail-key">Active Sessions</div><div class="detail-val">\${s.sessionCount || 0}</div></div>
10740
- <div class="detail-row"><div class="detail-key">Plugin Version</div><div class="detail-val">1.0.5</div></div>
10787
+ <div class="detail-row"><div class="detail-key">Plugin Version</div><div class="detail-val">\${escHtml(pluginRes.data?.version || 'unknown')}</div></div>
10741
10788
  </div>
10742
10789
  \\\`;
10743
10790
  }
10744
10791
 
10745
- html(el, out);
10792
+ restEl.innerHTML = out;
10793
+ }
10794
+
10795
+ async function checkForUpdates() {
10796
+ const btn = $('#btn-check-update');
10797
+ if (btn) { btn.disabled = true; btn.textContent = '\u{1F50D} Checking...'; }
10798
+ toast('Checking for updates...', 'info');
10799
+ const data = await api('/update/check', { _timeout: 15000 });
10800
+ if (data.error) { toast('Check failed: ' + data.error, 'err'); }
10801
+ else if (data.updateAvailable) { toast('New version available: v' + data.latestVersion, 'ok'); }
10802
+ else { toast('Plugin is up to date (v' + data.currentVersion + ')', 'ok'); }
10803
+ if (btn) { btn.disabled = false; btn.textContent = '\u{1F50D} Check for Updates'; }
10804
+ loadSettings();
10805
+ }
10806
+
10807
+ async function doUpdate(force) {
10808
+ if (_updating) { toast('Update already in progress...', 'warn'); return; }
10809
+ const label = force ? 'Force reinstall' : 'Incremental update';
10810
+ if (!confirm(label + ' aicq-openclaw-plugin? This will download and install the latest version from npm.')) return;
10811
+
10812
+ _updating = true;
10813
+ const btnDo = $('#btn-do-update');
10814
+ const btnForce = $('#btn-force-update');
10815
+ if (btnDo) btnDo.disabled = true;
10816
+ if (btnForce) btnForce.disabled = true;
10817
+
10818
+ const outputEl = $('#update-output');
10819
+ const logEl = $('#update-log');
10820
+ if (outputEl) outputEl.style.display = 'block';
10821
+ if (logEl) logEl.textContent = 'Starting ' + label.toLowerCase() + '...
10822
+ ';
10823
+ toast(label + ' starting...', 'info');
10824
+
10825
+ const r = await api('/update', {
10826
+ method: 'POST',
10827
+ body: JSON.stringify({ force }),
10828
+ _timeout: 120000,
10829
+ });
10830
+
10831
+ if (logEl) logEl.textContent = r.output || r.message || (r.success ? 'Done!' : 'Failed.');
10832
+ if (r.success) {
10833
+ toast('Update successful! Restart the service to apply.', 'ok');
10834
+ if (logEl) logEl.textContent += '
10835
+
10836
+ \u2705 ' + (r.message || 'Done! Please restart the service.');
10837
+ } else {
10838
+ toast('Update failed: ' + (r.message || 'Unknown error'), 'err');
10839
+ if (logEl) logEl.textContent += '
10840
+
10841
+ \u274C ' + (r.message || 'Failed.');
10842
+ }
10843
+
10844
+ _updating = false;
10845
+ if (btnDo) btnDo.disabled = false;
10846
+ if (btnForce) btnForce.disabled = false;
10847
+ loadSettings();
10746
10848
  }
10747
10849
 
10748
10850
  // \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
@@ -10823,7 +10925,7 @@ var HTML = `<!DOCTYPE html>
10823
10925
  <div class="main-content">
10824
10926
 
10825
10927
  <!-- Dashboard -->
10826
- <div class="page active" id="page-dashboard"><div id="dashboard-content"><div class="loading-mask"><div class="spinner"></div>Loading...</div></div></div>
10928
+ <div class="page active" id="page-dashboard"><div id="dashboard-content"></div></div>
10827
10929
 
10828
10930
  <!-- Agents -->
10829
10931
  <div class="page" id="page-agents"><div id="agents-content"></div></div>
@@ -10928,6 +11030,7 @@ function getManagementHTML() {
10928
11030
  import * as fs5 from "fs";
10929
11031
  import * as path5 from "path";
10930
11032
  import * as os from "os";
11033
+ import { execSync, exec } from "child_process";
10931
11034
  var MODEL_PROVIDERS = [
10932
11035
  { id: "openai", name: "OpenAI", description: "GPT-4o, GPT-4, GPT-3.5, o1, o3", apiKeyHint: "sk-...", modelHint: "gpt-4o", baseUrlHint: "https://api.openai.com/v1", configKey: "openai" },
10933
11036
  { id: "anthropic", name: "Anthropic", description: "Claude 4, Claude 3.5 Sonnet, Haiku, Opus", apiKeyHint: "sk-ant-...", modelHint: "claude-sonnet-4-20250514", baseUrlHint: "https://api.anthropic.com", configKey: "anthropic" },
@@ -11412,6 +11515,146 @@ function createManagementHandler(ctx) {
11412
11515
  logger.info("[API] Model config saved for provider: " + providerId);
11413
11516
  return json(res, { success: true, message: "Model configuration saved for " + provider.name });
11414
11517
  }
11518
+ if (apiPath === "/update/check" && method === "GET") {
11519
+ try {
11520
+ const pkgPath = path5.join(__dirname, "..", "package.json");
11521
+ let currentVersion = "unknown";
11522
+ try {
11523
+ const pkgRaw = fs5.readFileSync(pkgPath, "utf-8");
11524
+ const pkg = JSON.parse(pkgRaw);
11525
+ currentVersion = pkg.version || "unknown";
11526
+ } catch {
11527
+ }
11528
+ if (currentVersion === "unknown") {
11529
+ const altPkgPath = path5.join(process.cwd(), "plugin", "package.json");
11530
+ try {
11531
+ const pkg = JSON.parse(fs5.readFileSync(altPkgPath, "utf-8"));
11532
+ currentVersion = pkg.version || "unknown";
11533
+ } catch {
11534
+ }
11535
+ }
11536
+ let latestVersion = currentVersion;
11537
+ let updateAvailable = false;
11538
+ try {
11539
+ const npmView = execSync("npm view aicq-openclaw-plugin version --registry https://registry.npmjs.org 2>/dev/null", {
11540
+ timeout: 15e3,
11541
+ encoding: "utf-8"
11542
+ }).trim();
11543
+ if (npmView) {
11544
+ latestVersion = npmView.replace(/^\^|~|\s+.*$/g, "").trim();
11545
+ updateAvailable = latestVersion !== currentVersion;
11546
+ }
11547
+ } catch (npmErr) {
11548
+ logger.warn("[API] npm view failed: " + (npmErr instanceof Error ? npmErr.message : String(npmErr)));
11549
+ }
11550
+ return json(res, {
11551
+ currentVersion,
11552
+ latestVersion,
11553
+ updateAvailable,
11554
+ packageName: "aicq-openclaw-plugin"
11555
+ });
11556
+ } catch (err) {
11557
+ const msg = err instanceof Error ? err.message : String(err);
11558
+ return json(res, { error: "Failed to check for updates: " + msg }, 500);
11559
+ }
11560
+ }
11561
+ if (apiPath === "/update" && method === "POST") {
11562
+ const body = await readBody(req);
11563
+ const force = body.force;
11564
+ try {
11565
+ logger.info("[API] Starting npm update for aicq-openclaw-plugin...");
11566
+ const pluginDir = __dirname;
11567
+ let npmRoot = pluginDir;
11568
+ for (let i = 0; i < 5; i++) {
11569
+ if (npmRoot.endsWith("node_modules"))
11570
+ break;
11571
+ npmRoot = path5.dirname(npmRoot);
11572
+ }
11573
+ const parentDir = path5.dirname(npmRoot);
11574
+ let installDir = parentDir;
11575
+ const candidates = [
11576
+ parentDir,
11577
+ path5.join(os.homedir(), ".openclaw", "plugins"),
11578
+ path5.join(os.homedir(), ".config", "openclaw", "plugins")
11579
+ ];
11580
+ let updateCmd;
11581
+ if (force) {
11582
+ updateCmd = `npm install aicq-openclaw-plugin@latest --dangerously-force-unsafe-install --registry https://registry.npmjs.org`;
11583
+ } else {
11584
+ updateCmd = `npm update aicq-openclaw-plugin --dangerously-force-unsafe-install --registry https://registry.npmjs.org`;
11585
+ }
11586
+ let installPath = parentDir;
11587
+ try {
11588
+ const pkgJsonPath = path5.join(pluginDir, "package.json");
11589
+ const realPkgDir = fs5.realpathSync(path5.dirname(pkgJsonPath));
11590
+ if (realPkgDir.includes("node_modules")) {
11591
+ const nmIdx = realPkgDir.lastIndexOf("node_modules");
11592
+ installPath = realPkgDir.substring(0, nmIdx + 13);
11593
+ }
11594
+ } catch {
11595
+ }
11596
+ logger.info("[API] Running: " + updateCmd + " in " + installPath);
11597
+ const result = await new Promise((resolve3) => {
11598
+ const child = exec(updateCmd, {
11599
+ cwd: installPath,
11600
+ timeout: 12e4,
11601
+ encoding: "utf-8",
11602
+ env: { ...process.env, NODE_NO_WARNINGS: "1" }
11603
+ }, (error, stdout, stderr) => {
11604
+ if (error) {
11605
+ resolve3("ERROR: " + (error.message || String(error)) + "\n\n" + (stderr || ""));
11606
+ } else {
11607
+ resolve3(stdout || stderr || "Update completed (no output)");
11608
+ }
11609
+ });
11610
+ child.stdout?.on("data", (d) => {
11611
+ });
11612
+ child.stderr?.on("data", (d) => {
11613
+ });
11614
+ });
11615
+ let newVersion = "unknown";
11616
+ try {
11617
+ const pkgRaw = fs5.readFileSync(path5.join(__dirname, "..", "package.json"), "utf-8");
11618
+ newVersion = JSON.parse(pkgRaw).version || "unknown";
11619
+ } catch {
11620
+ }
11621
+ const success = !result.startsWith("ERROR");
11622
+ logger.info("[API] Update " + (success ? "succeeded" : "failed") + ": " + result.substring(0, 200));
11623
+ return json(res, {
11624
+ success,
11625
+ message: success ? "Plugin updated successfully! Restart the service to apply changes." : "Update failed.",
11626
+ output: result.substring(0, 2e3),
11627
+ newVersion,
11628
+ needsRestart: success
11629
+ });
11630
+ } catch (err) {
11631
+ const msg = err instanceof Error ? err.message : String(err);
11632
+ logger.error("[API] Update error: " + msg);
11633
+ return json(res, { success: false, message: "Update failed: " + msg }, 500);
11634
+ }
11635
+ }
11636
+ if (apiPath === "/plugin-info" && method === "GET") {
11637
+ let pkgVersion = "unknown";
11638
+ try {
11639
+ const pkgPath = path5.join(__dirname, "..", "package.json");
11640
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
11641
+ pkgVersion = pkg.version || "unknown";
11642
+ } catch {
11643
+ const altPkgPath = path5.join(process.cwd(), "plugin", "package.json");
11644
+ try {
11645
+ pkgVersion = JSON.parse(fs5.readFileSync(altPkgPath, "utf-8")).version || "unknown";
11646
+ } catch {
11647
+ }
11648
+ }
11649
+ return json(res, {
11650
+ version: pkgVersion,
11651
+ name: "aicq-openclaw-plugin",
11652
+ uptime: process.uptime(),
11653
+ nodeVersion: process.version,
11654
+ platform: process.platform,
11655
+ pid: process.pid
11656
+ });
11657
+ }
11415
11658
  res.writeHead(404, { "Content-Type": "application/json" });
11416
11659
  res.end(JSON.stringify({ error: "Not found: " + apiPath }));
11417
11660
  } catch (err) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "aicq-chat",
3
3
  "name": "AICQ Encrypted Chat",
4
- "version": "1.0.4",
4
+ "version": "1.0.7",
5
5
  "description": "End-to-end encrypted chat plugin supporting AI-AI, Human-AI communication with P2P messaging via Noise-XK handshake, Ed25519/X25519/AES-256-GCM encryption",
6
6
  "enabledByDefault": false,
7
7
  "channels": ["encrypted-chat"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicq-openclaw-plugin",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "AICQ OpenClaw plugin - end-to-end encrypted P2P chat for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",