chatcc-agent 0.3.5 → 0.3.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatcc-agent",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "CCLink Agent - bridges Claude Code CLI with instant messaging",
5
5
  "bin": {
6
6
  "chatcc": "src/cli.js"
@@ -614,6 +614,11 @@ class ClaudeBridge {
614
614
  session_id: sessionID,
615
615
  tool_use_id: block.id,
616
616
  questions: block.input?.questions || [],
617
+ }, {
618
+ title: 'Claude 有问题',
619
+ desc: '需要您的选择',
620
+ ext: JSON.stringify({ session_id: sessionID, push_type: 'agent_reply' }),
621
+ ignoreBadge: false,
617
622
  }).catch(e => console.error('[Bridge] user_question:', e.message));
618
623
  } else {
619
624
  this._handleToolUseBlock(block, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, useControl, sendStreamEnd);
package/src/constants.js CHANGED
@@ -3,6 +3,11 @@
3
3
  * Chinese display labels live on the iOS side only.
4
4
  */
5
5
 
6
+ // Protocol versioning — bump CURRENT when introducing breaking changes.
7
+ // MIN is the oldest client/agent version we still support.
8
+ const CURRENT_PROTOCOL_VERSION = 1;
9
+ const MIN_PROTOCOL_VERSION = 1;
10
+
6
11
  // Default permission modes (sent as default_mode in session settings)
7
12
  const PERMISSION_MODES = {
8
13
  CONFIRM_EVERY: 'confirm_every', // 每次确认
@@ -34,4 +39,4 @@ function normalizePermission(value) {
34
39
  return LEGACY_MODE_MAP[value] || value;
35
40
  }
36
41
 
37
- module.exports = { PERMISSION_MODES, PERMISSION_ACTIONS, LEGACY_MODE_MAP, normalizePermission };
42
+ module.exports = { CURRENT_PROTOCOL_VERSION, MIN_PROTOCOL_VERSION, PERMISSION_MODES, PERMISSION_ACTIONS, LEGACY_MODE_MAP, normalizePermission };
package/src/im-client.js CHANGED
@@ -1,7 +1,8 @@
1
1
  const WebSocket = require('ws');
2
2
  global.WebSocket = WebSocket;
3
3
 
4
- const PROTOCOL_VERSION = 1;
4
+ const { CURRENT_PROTOCOL_VERSION, MIN_PROTOCOL_VERSION } = require('./constants');
5
+ const PROTOCOL_VERSION = CURRENT_PROTOCOL_VERSION;
5
6
 
6
7
  class IMClient {
7
8
  constructor(sdkAppID, userID, userSig, options = {}) {
@@ -134,16 +135,31 @@ class IMClient {
134
135
  this._messageHandlers.push(handler);
135
136
  }
136
137
 
137
- async sendCustomMessage(to, ccType, payload = {}) {
138
+ async sendCustomMessage(to, ccType, payload = {}, pushOptions = null) {
138
139
  const message = this.tim.createCustomMessage({
139
140
  to,
140
141
  conversationType: this.TIM.TYPES.CONV_C2C,
141
142
  payload: {
142
- data: JSON.stringify({ cc_type: ccType, v: PROTOCOL_VERSION, ...payload }),
143
+ data: JSON.stringify({ cc_type: ccType, v: PROTOCOL_VERSION, min_v: MIN_PROTOCOL_VERSION, ...payload }),
143
144
  description: 'CCLink',
144
145
  },
145
146
  });
146
- await this.tim.sendMessage(message);
147
+
148
+ // Default: silent (disablePush). Only messages with explicit pushOptions trigger notifications.
149
+ const sendOpts = {};
150
+ if (pushOptions) {
151
+ sendOpts.offlinePushInfo = {
152
+ title: pushOptions.title || 'CCLink',
153
+ description: pushOptions.desc || '',
154
+ extension: pushOptions.ext || '',
155
+ disablePush: false,
156
+ apnsInfo: { ignoreIOSBadge: pushOptions.ignoreBadge ?? false },
157
+ };
158
+ } else {
159
+ sendOpts.offlinePushInfo = { disablePush: true };
160
+ }
161
+
162
+ await this.tim.sendMessage(message, sendOpts);
147
163
  return message.ID;
148
164
  }
149
165
  }
package/src/index.js CHANGED
@@ -133,6 +133,28 @@ async function shutdown(signal) {
133
133
 
134
134
  process.on('exit', cleanupTempFiles);
135
135
 
136
+ async function checkForUpdates(agentID, pairedClientIDs) {
137
+ try {
138
+ const { execSync } = require('child_process');
139
+ const currentVersion = getVersion();
140
+ const latestVersion = execSync('npm view chatcc-agent version', {
141
+ encoding: 'utf-8', timeout: 15000,
142
+ }).trim();
143
+ if (latestVersion && latestVersion !== currentVersion) {
144
+ console.log(`[Update] New version available: ${currentVersion} -> ${latestVersion}`);
145
+ for (const clientID of pairedClientIDs) {
146
+ imClient.sendCustomMessage(clientID, 'update_available', {
147
+ current_version: currentVersion,
148
+ latest_version: latestVersion,
149
+ }).catch(() => {});
150
+ }
151
+ }
152
+ } catch (e) {
153
+ // Non-fatal: network issues, npm unavailable, etc.
154
+ console.log('[Update] Check failed:', e.message);
155
+ }
156
+ }
157
+
136
158
  async function main() {
137
159
  // Pre-flight: check Claude CLI is available
138
160
  const { execFileSync } = require('child_process');
@@ -208,6 +230,13 @@ async function main() {
208
230
  }
209
231
  }
210
232
 
233
+ // Auto-update check: on startup and every 24h
234
+ checkForUpdates(config.agentUserID, pairedClientIDs);
235
+ const updateInterval = setInterval(() => {
236
+ checkForUpdates(config.agentUserID, pairedClientIDs);
237
+ }, 24 * 60 * 60 * 1000);
238
+ updateInterval.unref();
239
+
211
240
  // Setup services
212
241
  sessions = new SessionStore(path.join(CHATCC_DIR, 'sessions.json'), {
213
242
  maxSessions: MAX_SESSIONS,
@@ -232,6 +261,11 @@ async function main() {
232
261
  request_id: reqId,
233
262
  path: filePath,
234
263
  operation,
264
+ }, {
265
+ title: '权限请求',
266
+ desc: `${operation}: ${filePath.length > 40 ? '...' + filePath.slice(-37) : filePath}`,
267
+ ext: JSON.stringify({ push_type: 'tool_confirm' }),
268
+ ignoreBadge: false,
235
269
  }).catch(() => {});
236
270
  const timer = setTimeout(() => {
237
271
  fileService._pendingPermissions.delete(reqId);
@@ -409,6 +443,26 @@ async function main() {
409
443
  break;
410
444
  }
411
445
 
446
+ case 'clear_request': {
447
+ const sid = data.session_id;
448
+ const sess = sessions.get(sid) || null;
449
+ if (!sess || sess.clientID !== from) {
450
+ imClient.sendCustomMessage(from, 'clear_response', {
451
+ request_id: data.request_id, success: false, error: '会话不存在或无权限',
452
+ }).catch(() => {});
453
+ break;
454
+ }
455
+ // Clear Claude session so next message starts fresh
456
+ sess.claudeSessionId = null;
457
+ sess.lastActiveAt = Date.now();
458
+ sessions.set(sid, sess);
459
+ console.log(`[Session] Clear context for ${sid}, claudeSessionId reset`);
460
+ imClient.sendCustomMessage(from, 'clear_response', {
461
+ request_id: data.request_id, success: true,
462
+ }).catch(e => console.error('[Clear] Reply failed:', e.message));
463
+ break;
464
+ }
465
+
412
466
  case 'compact_request': {
413
467
  const sid = data.session_id;
414
468
  const sess = sessions.get(sid) || null;
@@ -478,6 +532,10 @@ async function main() {
478
532
 
479
533
  default:
480
534
  console.log('[Message] Unknown cc_type:', data.cc_type, 'v=', data.v || 0);
535
+ imClient.sendCustomMessage(from, 'unknown_type_error', {
536
+ original_type: data.cc_type,
537
+ message: `Unknown cc_type: ${data.cc_type}`,
538
+ }).catch(() => {});
481
539
  }
482
540
  });
483
541
 
@@ -2,6 +2,7 @@ const os = require('os');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { getVersion } = require('./version');
5
+ const { CURRENT_PROTOCOL_VERSION, MIN_PROTOCOL_VERSION } = require('./constants');
5
6
 
6
7
  function collectServerMeta() {
7
8
  const suggestedWorkspaces = [];
@@ -23,7 +24,8 @@ function collectServerMeta() {
23
24
  cpuCount: os.cpus().length,
24
25
  memoryGB: Math.round(os.totalmem() / 1024 / 1024 / 1024),
25
26
  agentVersion: getVersion(),
26
- protocol_version: 1,
27
+ protocol_version: CURRENT_PROTOCOL_VERSION,
28
+ min_protocol_version: MIN_PROTOCOL_VERSION,
27
29
  suggestedWorkspaces: suggestedWorkspaces,
28
30
  };
29
31
  }