@aster110/cc2wechat 3.0.0 → 3.1.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/dist/auth.js +3 -1
- package/dist/auth.js.map +1 -1
- package/dist/daemon.js +7 -154
- package/dist/daemon.js.map +1 -1
- package/dist/handlers/pipe.d.ts +3 -0
- package/dist/handlers/pipe.js +47 -0
- package/dist/handlers/pipe.js.map +1 -0
- package/dist/handlers/terminal.d.ts +3 -0
- package/dist/handlers/terminal.js +95 -0
- package/dist/handlers/terminal.js.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +31 -0
- package/dist/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/auth.ts +3 -1
- package/src/daemon.ts +8 -160
- package/src/handlers/pipe.ts +56 -0
- package/src/handlers/terminal.ts +104 -0
- package/src/utils.ts +30 -0
package/dist/auth.js
CHANGED
|
@@ -138,7 +138,9 @@ function buildQRPage(qrUrl) {
|
|
|
138
138
|
</html>`;
|
|
139
139
|
}
|
|
140
140
|
function openBrowser(url) {
|
|
141
|
-
const cmd = process.platform === 'darwin' ? 'open'
|
|
141
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
142
|
+
: process.platform === 'win32' ? 'start'
|
|
143
|
+
: 'xdg-open';
|
|
142
144
|
exec(`${cmd} ${url}`, () => { });
|
|
143
145
|
}
|
|
144
146
|
/**
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.js
CHANGED
|
@@ -1,135 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
2
|
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const replyCli = path.join(__dirname, 'reply-cli.js');
|
|
9
|
-
const IS_MACOS = process.platform === 'darwin';
|
|
10
3
|
import { loginWithQRWeb } from './auth.js';
|
|
11
4
|
import { getActiveAccount, saveAccount, loadSyncBuf, saveSyncBuf } from './store.js';
|
|
12
5
|
import { getUpdates, sendTyping, getConfig } from './wechat-api.js';
|
|
13
|
-
import {
|
|
6
|
+
import { extractText } from './utils.js';
|
|
7
|
+
import { handleMessageTerminal } from './handlers/terminal.js';
|
|
8
|
+
import { handleMessagePipe } from './handlers/pipe.js';
|
|
14
9
|
// ---------------------------------------------------------------------------
|
|
15
10
|
// Constants
|
|
16
11
|
// ---------------------------------------------------------------------------
|
|
12
|
+
const IS_MACOS = process.platform === 'darwin';
|
|
17
13
|
const SESSION_EXPIRED_ERRCODE = -14;
|
|
18
14
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
19
15
|
const BACKOFF_DELAY_MS = 30_000;
|
|
20
16
|
const RETRY_DELAY_MS = 2_000;
|
|
21
17
|
const SESSION_PAUSE_MS = 5 * 60_000;
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Helpers
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
function userIdToSessionUUID(userId) {
|
|
26
|
-
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
27
|
-
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
28
|
-
}
|
|
29
|
-
function extractText(msg) {
|
|
30
|
-
const parts = [];
|
|
31
|
-
for (const item of msg.item_list ?? []) {
|
|
32
|
-
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
33
|
-
parts.push(item.text_item.text);
|
|
34
|
-
}
|
|
35
|
-
else if (item.type === MessageItemType.IMAGE) {
|
|
36
|
-
parts.push('[Image]');
|
|
37
|
-
}
|
|
38
|
-
else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
39
|
-
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
40
|
-
}
|
|
41
|
-
else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
42
|
-
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
43
|
-
}
|
|
44
|
-
else if (item.type === MessageItemType.VIDEO) {
|
|
45
|
-
parts.push('[Video]');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return parts.join('\n') || '[Empty message]';
|
|
49
|
-
}
|
|
50
18
|
function sleep(ms) {
|
|
51
19
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
20
|
}
|
|
53
21
|
// ---------------------------------------------------------------------------
|
|
54
|
-
//
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// Maintain tab state per WeChat user
|
|
57
|
-
const userTabs = new Map(); // userId -> tabName
|
|
58
|
-
// Track tab IDs - persisted to file so it survives daemon restart
|
|
59
|
-
const TAB_REGISTRY_PATH = '/tmp/cc2wechat-tabs.json';
|
|
60
|
-
const tabSessionIds = new Map(); // tabName -> iTerm session id
|
|
61
|
-
// Load persisted tab registry on startup
|
|
62
|
-
try {
|
|
63
|
-
if (fs.existsSync(TAB_REGISTRY_PATH)) {
|
|
64
|
-
const data = JSON.parse(fs.readFileSync(TAB_REGISTRY_PATH, 'utf-8'));
|
|
65
|
-
for (const [k, v] of Object.entries(data)) {
|
|
66
|
-
tabSessionIds.set(k, v);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch { }
|
|
71
|
-
function saveTabRegistry() {
|
|
72
|
-
fs.writeFileSync(TAB_REGISTRY_PATH, JSON.stringify(Object.fromEntries(tabSessionIds)));
|
|
73
|
-
}
|
|
74
|
-
function tabExists(tabName) {
|
|
75
|
-
const windowId = tabSessionIds.get(tabName);
|
|
76
|
-
if (!windowId)
|
|
77
|
-
return false;
|
|
78
|
-
try {
|
|
79
|
-
const result = execSync(`osascript -e '
|
|
80
|
-
tell application "iTerm2"
|
|
81
|
-
try
|
|
82
|
-
set w to (first window whose id is ${windowId})
|
|
83
|
-
return "found"
|
|
84
|
-
on error
|
|
85
|
-
return "not_found"
|
|
86
|
-
end try
|
|
87
|
-
end tell
|
|
88
|
-
'`, { encoding: 'utf-8' }).trim();
|
|
89
|
-
return result === 'found';
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function createTabAndStartCC(tabName, ccSessionId, cwd) {
|
|
96
|
-
// Create NEW WINDOW and capture window ID (same approach as cc-mesh)
|
|
97
|
-
const windowId = execSync(`osascript -e '
|
|
98
|
-
tell application "iTerm2"
|
|
99
|
-
set w to (create window with default profile)
|
|
100
|
-
tell current session of w
|
|
101
|
-
write text "cd ${cwd} && claude --resume ${ccSessionId} --dangerously-skip-permissions"
|
|
102
|
-
end tell
|
|
103
|
-
return id of w
|
|
104
|
-
end tell
|
|
105
|
-
'`, { encoding: 'utf-8' }).trim();
|
|
106
|
-
tabSessionIds.set(tabName, windowId);
|
|
107
|
-
saveTabRegistry();
|
|
108
|
-
console.log(`[cc2wechat] window created: ${tabName} -> window id: ${windowId}`);
|
|
109
|
-
}
|
|
110
|
-
function injectMessage(tabName, message) {
|
|
111
|
-
const windowId = tabSessionIds.get(tabName);
|
|
112
|
-
if (!windowId)
|
|
113
|
-
return;
|
|
114
|
-
const escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
115
|
-
execSync(`osascript -e '
|
|
116
|
-
tell application "iTerm2"
|
|
117
|
-
tell current session of (first window whose id is ${windowId})
|
|
118
|
-
write text "${escaped}"
|
|
119
|
-
end tell
|
|
120
|
-
end tell
|
|
121
|
-
'`);
|
|
122
|
-
}
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// Core message handler
|
|
22
|
+
// Core message handler — routes to platform-specific handler
|
|
125
23
|
// ---------------------------------------------------------------------------
|
|
126
24
|
async function handleMessage(msg, account) {
|
|
127
25
|
const text = extractText(msg);
|
|
128
26
|
const userId = msg.from_user_id ?? '';
|
|
129
27
|
const contextToken = msg.context_token ?? '';
|
|
130
|
-
const sessionId = userIdToSessionUUID(userId);
|
|
131
|
-
const tabName = `wechat-${userId.slice(0, 8)}`;
|
|
132
|
-
const cwd = process.cwd();
|
|
133
28
|
console.log(`[cc2wechat] <- ${userId.slice(0, 10)}...: ${text.slice(0, 50)}`);
|
|
134
29
|
// Write context for reply-cli
|
|
135
30
|
fs.writeFileSync('/tmp/cc2wechat-context.json', JSON.stringify({
|
|
@@ -149,52 +44,10 @@ async function handleMessage(msg, account) {
|
|
|
149
44
|
// non-critical
|
|
150
45
|
}
|
|
151
46
|
if (IS_MACOS) {
|
|
152
|
-
|
|
153
|
-
if (tabExists(tabName)) {
|
|
154
|
-
console.log(`[cc2wechat] -> inject to existing window: ${tabName}`);
|
|
155
|
-
injectMessage(tabName, `[微信] ${text}`);
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
console.log(`[cc2wechat] -> creating window: ${tabName} (session: ${sessionId})`);
|
|
159
|
-
createTabAndStartCC(tabName, sessionId, cwd);
|
|
160
|
-
await sleep(5000);
|
|
161
|
-
injectMessage(tabName, `[微信] ${text}`);
|
|
162
|
-
}
|
|
163
|
-
userTabs.set(userId, tabName);
|
|
47
|
+
await handleMessageTerminal(msg, account);
|
|
164
48
|
}
|
|
165
49
|
else {
|
|
166
|
-
|
|
167
|
-
console.log(`[cc2wechat] -> pipe mode: ${text.slice(0, 30)}...`);
|
|
168
|
-
const prompt = JSON.stringify(text);
|
|
169
|
-
const systemPrompt = JSON.stringify(`You are responding to a WeChat message. Keep replies concise. Use this to reply: node ${replyCli} --text "reply" or node ${replyCli} --image /path/to/file`);
|
|
170
|
-
let result;
|
|
171
|
-
try {
|
|
172
|
-
result = execSync(`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd }).trim();
|
|
173
|
-
}
|
|
174
|
-
catch (err) {
|
|
175
|
-
const execErr = err;
|
|
176
|
-
if (!execErr.stdout?.trim()) {
|
|
177
|
-
try {
|
|
178
|
-
result = execSync(`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd }).trim();
|
|
179
|
-
}
|
|
180
|
-
catch (err2) {
|
|
181
|
-
const execErr2 = err2;
|
|
182
|
-
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
result = execErr.stdout.trim();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// Auto-send text reply
|
|
190
|
-
const { sendMessage: sendMsg } = await import('./wechat-api.js');
|
|
191
|
-
const plain = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, c) => c.trim())
|
|
192
|
-
.replace(/\*\*(.+?)\*\*/g, '$1').replace(/^#{1,6}\s+/gm, '').trim();
|
|
193
|
-
const chunks = plain.length <= 3900 ? [plain] : [plain.slice(0, 3900), plain.slice(3900)];
|
|
194
|
-
for (const chunk of chunks) {
|
|
195
|
-
await sendMsg(account.token, userId, chunk, contextToken, account.baseUrl);
|
|
196
|
-
}
|
|
197
|
-
console.log(`[cc2wechat] -> replied (${chunks.length} chunk)`);
|
|
50
|
+
await handleMessagePipe(msg, account);
|
|
198
51
|
}
|
|
199
52
|
}
|
|
200
53
|
// ---------------------------------------------------------------------------
|
package/dist/daemon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { extractText, userIdToSessionUUID } from '../utils.js';
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const replyCli = path.join(__dirname, '..', 'reply-cli.js');
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Pipe mode message handler (Windows/Linux)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export async function handleMessagePipe(msg, account) {
|
|
11
|
+
const text = extractText(msg);
|
|
12
|
+
const userId = msg.from_user_id ?? '';
|
|
13
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
console.log(`[cc2wechat] -> pipe mode: ${text.slice(0, 30)}...`);
|
|
16
|
+
const prompt = JSON.stringify(text);
|
|
17
|
+
const systemPrompt = JSON.stringify(`You are responding to a WeChat message. Keep replies concise. Use this to reply: node ${replyCli} --text "reply" or node ${replyCli} --image /path/to/file`);
|
|
18
|
+
let result;
|
|
19
|
+
try {
|
|
20
|
+
result = execSync(`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd }).trim();
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const execErr = err;
|
|
24
|
+
if (!execErr.stdout?.trim()) {
|
|
25
|
+
try {
|
|
26
|
+
result = execSync(`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd }).trim();
|
|
27
|
+
}
|
|
28
|
+
catch (err2) {
|
|
29
|
+
const execErr2 = err2;
|
|
30
|
+
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
result = execErr.stdout.trim();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Auto-send text reply
|
|
38
|
+
const { sendMessage: sendMsg } = await import('../wechat-api.js');
|
|
39
|
+
const plain = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, c) => c.trim())
|
|
40
|
+
.replace(/\*\*(.+?)\*\*/g, '$1').replace(/^#{1,6}\s+/gm, '').trim();
|
|
41
|
+
const chunks = plain.length <= 3900 ? [plain] : [plain.slice(0, 3900), plain.slice(3900)];
|
|
42
|
+
for (const chunk of chunks) {
|
|
43
|
+
await sendMsg(account.token, userId, chunk, msg.context_token ?? '', account.baseUrl);
|
|
44
|
+
}
|
|
45
|
+
console.log(`[cc2wechat] -> replied (${chunks.length} chunk)`);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=pipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.js","sourceRoot":"","sources":["../../src/handlers/pipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { extractText, userIdToSessionUUID, sleep } from '../utils.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// iTerm Tab Management
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Maintain tab state per WeChat user
|
|
8
|
+
const userTabs = new Map(); // userId -> tabName
|
|
9
|
+
// Track tab IDs - persisted to file so it survives daemon restart
|
|
10
|
+
const TAB_REGISTRY_PATH = '/tmp/cc2wechat-tabs.json';
|
|
11
|
+
const tabSessionIds = new Map(); // tabName -> iTerm window id
|
|
12
|
+
// Load persisted tab registry on startup
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(TAB_REGISTRY_PATH)) {
|
|
15
|
+
const data = JSON.parse(fs.readFileSync(TAB_REGISTRY_PATH, 'utf-8'));
|
|
16
|
+
for (const [k, v] of Object.entries(data)) {
|
|
17
|
+
tabSessionIds.set(k, v);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
function saveTabRegistry() {
|
|
23
|
+
fs.writeFileSync(TAB_REGISTRY_PATH, JSON.stringify(Object.fromEntries(tabSessionIds)));
|
|
24
|
+
}
|
|
25
|
+
function tabExists(tabName) {
|
|
26
|
+
const windowId = tabSessionIds.get(tabName);
|
|
27
|
+
if (!windowId)
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
const result = execSync(`osascript -e '
|
|
31
|
+
tell application "iTerm2"
|
|
32
|
+
try
|
|
33
|
+
set w to (first window whose id is ${windowId})
|
|
34
|
+
return "found"
|
|
35
|
+
on error
|
|
36
|
+
return "not_found"
|
|
37
|
+
end try
|
|
38
|
+
end tell
|
|
39
|
+
'`, { encoding: 'utf-8' }).trim();
|
|
40
|
+
return result === 'found';
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function createTabAndStartCC(tabName, ccSessionId, cwd) {
|
|
47
|
+
// Create NEW WINDOW and capture window ID (same approach as cc-mesh)
|
|
48
|
+
const windowId = execSync(`osascript -e '
|
|
49
|
+
tell application "iTerm2"
|
|
50
|
+
set w to (create window with default profile)
|
|
51
|
+
tell current session of w
|
|
52
|
+
write text "cd ${cwd} && claude --resume ${ccSessionId} --dangerously-skip-permissions"
|
|
53
|
+
end tell
|
|
54
|
+
return id of w
|
|
55
|
+
end tell
|
|
56
|
+
'`, { encoding: 'utf-8' }).trim();
|
|
57
|
+
tabSessionIds.set(tabName, windowId);
|
|
58
|
+
saveTabRegistry();
|
|
59
|
+
console.log(`[cc2wechat] window created: ${tabName} -> window id: ${windowId}`);
|
|
60
|
+
}
|
|
61
|
+
function injectMessage(tabName, message) {
|
|
62
|
+
const windowId = tabSessionIds.get(tabName);
|
|
63
|
+
if (!windowId)
|
|
64
|
+
return;
|
|
65
|
+
const escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
66
|
+
execSync(`osascript -e '
|
|
67
|
+
tell application "iTerm2"
|
|
68
|
+
tell current session of (first window whose id is ${windowId})
|
|
69
|
+
write text "${escaped}"
|
|
70
|
+
end tell
|
|
71
|
+
end tell
|
|
72
|
+
'`);
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Terminal mode message handler
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
export async function handleMessageTerminal(msg, account) {
|
|
78
|
+
const text = extractText(msg);
|
|
79
|
+
const userId = msg.from_user_id ?? '';
|
|
80
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
81
|
+
const tabName = `wechat-${userId.slice(0, 8)}`;
|
|
82
|
+
const cwd = process.cwd();
|
|
83
|
+
if (tabExists(tabName)) {
|
|
84
|
+
console.log(`[cc2wechat] -> inject to existing window: ${tabName}`);
|
|
85
|
+
injectMessage(tabName, `[微信] ${text}`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.log(`[cc2wechat] -> creating window: ${tabName} (session: ${sessionId})`);
|
|
89
|
+
createTabAndStartCC(tabName, sessionId, cwd);
|
|
90
|
+
await sleep(5000);
|
|
91
|
+
injectMessage(tabName, `[微信] ${text}`);
|
|
92
|
+
}
|
|
93
|
+
userTabs.set(userId, tabName);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/handlers/terminal.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
package/dist/utils.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { MessageItemType } from './types.js';
|
|
3
|
+
export function userIdToSessionUUID(userId) {
|
|
4
|
+
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
5
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
6
|
+
}
|
|
7
|
+
export function extractText(msg) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
for (const item of msg.item_list ?? []) {
|
|
10
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
11
|
+
parts.push(item.text_item.text);
|
|
12
|
+
}
|
|
13
|
+
else if (item.type === MessageItemType.IMAGE) {
|
|
14
|
+
parts.push('[Image]');
|
|
15
|
+
}
|
|
16
|
+
else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
17
|
+
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
18
|
+
}
|
|
19
|
+
else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
20
|
+
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
21
|
+
}
|
|
22
|
+
else if (item.type === MessageItemType.VIDEO) {
|
|
23
|
+
parts.push('[Video]');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return parts.join('\n') || '[Empty message]';
|
|
27
|
+
}
|
|
28
|
+
export function sleep(ms) {
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3E,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AACvH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAkB;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;YAC/D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;YACxE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC;YAC3E,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
package/package.json
CHANGED
package/src/auth.ts
CHANGED
|
@@ -155,7 +155,9 @@ function buildQRPage(qrUrl: string): string {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
function openBrowser(url: string): void {
|
|
158
|
-
const cmd = process.platform === 'darwin' ? 'open'
|
|
158
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
159
|
+
: process.platform === 'win32' ? 'start'
|
|
160
|
+
: 'xdg-open';
|
|
159
161
|
exec(`${cmd} ${url}`, () => {});
|
|
160
162
|
}
|
|
161
163
|
|
package/src/daemon.ts
CHANGED
|
@@ -1,148 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
-
import { createHash } from 'node:crypto';
|
|
5
3
|
import fs from 'node:fs';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
8
|
-
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const replyCli = path.join(__dirname, 'reply-cli.js');
|
|
11
|
-
const IS_MACOS = process.platform === 'darwin';
|
|
12
4
|
|
|
13
5
|
import { loginWithQRWeb } from './auth.js';
|
|
14
6
|
import { getActiveAccount, saveAccount, loadSyncBuf, saveSyncBuf } from './store.js';
|
|
15
7
|
import { getUpdates, sendTyping, getConfig } from './wechat-api.js';
|
|
16
8
|
import type { WeixinMessage } from './types.js';
|
|
17
|
-
import { MessageItemType } from './types.js';
|
|
18
9
|
import type { AccountData } from './store.js';
|
|
10
|
+
import { extractText } from './utils.js';
|
|
11
|
+
import { handleMessageTerminal } from './handlers/terminal.js';
|
|
12
|
+
import { handleMessagePipe } from './handlers/pipe.js';
|
|
19
13
|
|
|
20
14
|
// ---------------------------------------------------------------------------
|
|
21
15
|
// Constants
|
|
22
16
|
// ---------------------------------------------------------------------------
|
|
23
17
|
|
|
18
|
+
const IS_MACOS = process.platform === 'darwin';
|
|
19
|
+
|
|
24
20
|
const SESSION_EXPIRED_ERRCODE = -14;
|
|
25
21
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
26
22
|
const BACKOFF_DELAY_MS = 30_000;
|
|
27
23
|
const RETRY_DELAY_MS = 2_000;
|
|
28
24
|
const SESSION_PAUSE_MS = 5 * 60_000;
|
|
29
25
|
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// Helpers
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
|
|
34
|
-
function userIdToSessionUUID(userId: string): string {
|
|
35
|
-
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
36
|
-
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function extractText(msg: WeixinMessage): string {
|
|
40
|
-
const parts: string[] = [];
|
|
41
|
-
for (const item of msg.item_list ?? []) {
|
|
42
|
-
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
43
|
-
parts.push(item.text_item.text);
|
|
44
|
-
} else if (item.type === MessageItemType.IMAGE) {
|
|
45
|
-
parts.push('[Image]');
|
|
46
|
-
} else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
47
|
-
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
48
|
-
} else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
49
|
-
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
50
|
-
} else if (item.type === MessageItemType.VIDEO) {
|
|
51
|
-
parts.push('[Video]');
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return parts.join('\n') || '[Empty message]';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
26
|
function sleep(ms: number): Promise<void> {
|
|
58
27
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
59
28
|
}
|
|
60
29
|
|
|
61
30
|
// ---------------------------------------------------------------------------
|
|
62
|
-
//
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
|
|
65
|
-
// Maintain tab state per WeChat user
|
|
66
|
-
const userTabs = new Map<string, string>(); // userId -> tabName
|
|
67
|
-
|
|
68
|
-
// Track tab IDs - persisted to file so it survives daemon restart
|
|
69
|
-
const TAB_REGISTRY_PATH = '/tmp/cc2wechat-tabs.json';
|
|
70
|
-
const tabSessionIds = new Map<string, string>(); // tabName -> iTerm session id
|
|
71
|
-
|
|
72
|
-
// Load persisted tab registry on startup
|
|
73
|
-
try {
|
|
74
|
-
if (fs.existsSync(TAB_REGISTRY_PATH)) {
|
|
75
|
-
const data = JSON.parse(fs.readFileSync(TAB_REGISTRY_PATH, 'utf-8'));
|
|
76
|
-
for (const [k, v] of Object.entries(data)) {
|
|
77
|
-
tabSessionIds.set(k, v as string);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {}
|
|
81
|
-
|
|
82
|
-
function saveTabRegistry(): void {
|
|
83
|
-
fs.writeFileSync(TAB_REGISTRY_PATH, JSON.stringify(Object.fromEntries(tabSessionIds)));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function tabExists(tabName: string): boolean {
|
|
87
|
-
const windowId = tabSessionIds.get(tabName);
|
|
88
|
-
if (!windowId) return false;
|
|
89
|
-
try {
|
|
90
|
-
const result = execSync(`osascript -e '
|
|
91
|
-
tell application "iTerm2"
|
|
92
|
-
try
|
|
93
|
-
set w to (first window whose id is ${windowId})
|
|
94
|
-
return "found"
|
|
95
|
-
on error
|
|
96
|
-
return "not_found"
|
|
97
|
-
end try
|
|
98
|
-
end tell
|
|
99
|
-
'`, { encoding: 'utf-8' }).trim();
|
|
100
|
-
return result === 'found';
|
|
101
|
-
} catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function createTabAndStartCC(tabName: string, ccSessionId: string, cwd: string): void {
|
|
107
|
-
// Create NEW WINDOW and capture window ID (same approach as cc-mesh)
|
|
108
|
-
const windowId = execSync(`osascript -e '
|
|
109
|
-
tell application "iTerm2"
|
|
110
|
-
set w to (create window with default profile)
|
|
111
|
-
tell current session of w
|
|
112
|
-
write text "cd ${cwd} && claude --resume ${ccSessionId} --dangerously-skip-permissions"
|
|
113
|
-
end tell
|
|
114
|
-
return id of w
|
|
115
|
-
end tell
|
|
116
|
-
'`, { encoding: 'utf-8' }).trim();
|
|
117
|
-
tabSessionIds.set(tabName, windowId);
|
|
118
|
-
saveTabRegistry();
|
|
119
|
-
console.log(`[cc2wechat] window created: ${tabName} -> window id: ${windowId}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function injectMessage(tabName: string, message: string): void {
|
|
123
|
-
const windowId = tabSessionIds.get(tabName);
|
|
124
|
-
if (!windowId) return;
|
|
125
|
-
const escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
126
|
-
execSync(`osascript -e '
|
|
127
|
-
tell application "iTerm2"
|
|
128
|
-
tell current session of (first window whose id is ${windowId})
|
|
129
|
-
write text "${escaped}"
|
|
130
|
-
end tell
|
|
131
|
-
end tell
|
|
132
|
-
'`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ---------------------------------------------------------------------------
|
|
136
|
-
// Core message handler
|
|
31
|
+
// Core message handler — routes to platform-specific handler
|
|
137
32
|
// ---------------------------------------------------------------------------
|
|
138
33
|
|
|
139
34
|
async function handleMessage(msg: WeixinMessage, account: AccountData): Promise<void> {
|
|
140
35
|
const text = extractText(msg);
|
|
141
36
|
const userId = msg.from_user_id ?? '';
|
|
142
37
|
const contextToken = msg.context_token ?? '';
|
|
143
|
-
const sessionId = userIdToSessionUUID(userId);
|
|
144
|
-
const tabName = `wechat-${userId.slice(0, 8)}`;
|
|
145
|
-
const cwd = process.cwd();
|
|
146
38
|
|
|
147
39
|
console.log(`[cc2wechat] <- ${userId.slice(0, 10)}...: ${text.slice(0, 50)}`);
|
|
148
40
|
|
|
@@ -165,53 +57,9 @@ async function handleMessage(msg: WeixinMessage, account: AccountData): Promise<
|
|
|
165
57
|
}
|
|
166
58
|
|
|
167
59
|
if (IS_MACOS) {
|
|
168
|
-
|
|
169
|
-
if (tabExists(tabName)) {
|
|
170
|
-
console.log(`[cc2wechat] -> inject to existing window: ${tabName}`);
|
|
171
|
-
injectMessage(tabName, `[微信] ${text}`);
|
|
172
|
-
} else {
|
|
173
|
-
console.log(`[cc2wechat] -> creating window: ${tabName} (session: ${sessionId})`);
|
|
174
|
-
createTabAndStartCC(tabName, sessionId, cwd);
|
|
175
|
-
await sleep(5000);
|
|
176
|
-
injectMessage(tabName, `[微信] ${text}`);
|
|
177
|
-
}
|
|
178
|
-
userTabs.set(userId, tabName);
|
|
60
|
+
await handleMessageTerminal(msg, account);
|
|
179
61
|
} else {
|
|
180
|
-
|
|
181
|
-
console.log(`[cc2wechat] -> pipe mode: ${text.slice(0, 30)}...`);
|
|
182
|
-
const prompt = JSON.stringify(text);
|
|
183
|
-
const systemPrompt = JSON.stringify(`You are responding to a WeChat message. Keep replies concise. Use this to reply: node ${replyCli} --text "reply" or node ${replyCli} --image /path/to/file`);
|
|
184
|
-
let result: string;
|
|
185
|
-
try {
|
|
186
|
-
result = execSync(
|
|
187
|
-
`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`,
|
|
188
|
-
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd },
|
|
189
|
-
).trim();
|
|
190
|
-
} catch (err: unknown) {
|
|
191
|
-
const execErr = err as { stdout?: string; stderr?: string; message?: string };
|
|
192
|
-
if (!execErr.stdout?.trim()) {
|
|
193
|
-
try {
|
|
194
|
-
result = execSync(
|
|
195
|
-
`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`,
|
|
196
|
-
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd },
|
|
197
|
-
).trim();
|
|
198
|
-
} catch (err2: unknown) {
|
|
199
|
-
const execErr2 = err2 as { stdout?: string; message?: string };
|
|
200
|
-
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
result = execErr.stdout.trim();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// Auto-send text reply
|
|
207
|
-
const { sendMessage: sendMsg } = await import('./wechat-api.js');
|
|
208
|
-
const plain = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, c: string) => c.trim())
|
|
209
|
-
.replace(/\*\*(.+?)\*\*/g, '$1').replace(/^#{1,6}\s+/gm, '').trim();
|
|
210
|
-
const chunks = plain.length <= 3900 ? [plain] : [plain.slice(0, 3900), plain.slice(3900)];
|
|
211
|
-
for (const chunk of chunks) {
|
|
212
|
-
await sendMsg(account.token, userId, chunk, contextToken, account.baseUrl);
|
|
213
|
-
}
|
|
214
|
-
console.log(`[cc2wechat] -> replied (${chunks.length} chunk)`);
|
|
62
|
+
await handleMessagePipe(msg, account);
|
|
215
63
|
}
|
|
216
64
|
}
|
|
217
65
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
import type { WeixinMessage } from '../types.js';
|
|
6
|
+
import type { AccountData } from '../store.js';
|
|
7
|
+
import { extractText, userIdToSessionUUID } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const replyCli = path.join(__dirname, '..', 'reply-cli.js');
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Pipe mode message handler (Windows/Linux)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export async function handleMessagePipe(msg: WeixinMessage, account: AccountData): Promise<void> {
|
|
17
|
+
const text = extractText(msg);
|
|
18
|
+
const userId = msg.from_user_id ?? '';
|
|
19
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
|
|
22
|
+
console.log(`[cc2wechat] -> pipe mode: ${text.slice(0, 30)}...`);
|
|
23
|
+
const prompt = JSON.stringify(text);
|
|
24
|
+
const systemPrompt = JSON.stringify(`You are responding to a WeChat message. Keep replies concise. Use this to reply: node ${replyCli} --text "reply" or node ${replyCli} --image /path/to/file`);
|
|
25
|
+
let result: string;
|
|
26
|
+
try {
|
|
27
|
+
result = execSync(
|
|
28
|
+
`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`,
|
|
29
|
+
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd },
|
|
30
|
+
).trim();
|
|
31
|
+
} catch (err: unknown) {
|
|
32
|
+
const execErr = err as { stdout?: string; stderr?: string; message?: string };
|
|
33
|
+
if (!execErr.stdout?.trim()) {
|
|
34
|
+
try {
|
|
35
|
+
result = execSync(
|
|
36
|
+
`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${systemPrompt}`,
|
|
37
|
+
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd },
|
|
38
|
+
).trim();
|
|
39
|
+
} catch (err2: unknown) {
|
|
40
|
+
const execErr2 = err2 as { stdout?: string; message?: string };
|
|
41
|
+
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
result = execErr.stdout.trim();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Auto-send text reply
|
|
48
|
+
const { sendMessage: sendMsg } = await import('../wechat-api.js');
|
|
49
|
+
const plain = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, c: string) => c.trim())
|
|
50
|
+
.replace(/\*\*(.+?)\*\*/g, '$1').replace(/^#{1,6}\s+/gm, '').trim();
|
|
51
|
+
const chunks = plain.length <= 3900 ? [plain] : [plain.slice(0, 3900), plain.slice(3900)];
|
|
52
|
+
for (const chunk of chunks) {
|
|
53
|
+
await sendMsg(account.token, userId, chunk, msg.context_token ?? '', account.baseUrl);
|
|
54
|
+
}
|
|
55
|
+
console.log(`[cc2wechat] -> replied (${chunks.length} chunk)`);
|
|
56
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
import type { WeixinMessage } from '../types.js';
|
|
5
|
+
import type { AccountData } from '../store.js';
|
|
6
|
+
import { getConfig, sendTyping } from '../wechat-api.js';
|
|
7
|
+
import { extractText, userIdToSessionUUID, sleep } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// iTerm Tab Management
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
// Maintain tab state per WeChat user
|
|
14
|
+
const userTabs = new Map<string, string>(); // userId -> tabName
|
|
15
|
+
|
|
16
|
+
// Track tab IDs - persisted to file so it survives daemon restart
|
|
17
|
+
const TAB_REGISTRY_PATH = '/tmp/cc2wechat-tabs.json';
|
|
18
|
+
const tabSessionIds = new Map<string, string>(); // tabName -> iTerm window id
|
|
19
|
+
|
|
20
|
+
// Load persisted tab registry on startup
|
|
21
|
+
try {
|
|
22
|
+
if (fs.existsSync(TAB_REGISTRY_PATH)) {
|
|
23
|
+
const data = JSON.parse(fs.readFileSync(TAB_REGISTRY_PATH, 'utf-8'));
|
|
24
|
+
for (const [k, v] of Object.entries(data)) {
|
|
25
|
+
tabSessionIds.set(k, v as string);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} catch {}
|
|
29
|
+
|
|
30
|
+
function saveTabRegistry(): void {
|
|
31
|
+
fs.writeFileSync(TAB_REGISTRY_PATH, JSON.stringify(Object.fromEntries(tabSessionIds)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function tabExists(tabName: string): boolean {
|
|
35
|
+
const windowId = tabSessionIds.get(tabName);
|
|
36
|
+
if (!windowId) return false;
|
|
37
|
+
try {
|
|
38
|
+
const result = execSync(`osascript -e '
|
|
39
|
+
tell application "iTerm2"
|
|
40
|
+
try
|
|
41
|
+
set w to (first window whose id is ${windowId})
|
|
42
|
+
return "found"
|
|
43
|
+
on error
|
|
44
|
+
return "not_found"
|
|
45
|
+
end try
|
|
46
|
+
end tell
|
|
47
|
+
'`, { encoding: 'utf-8' }).trim();
|
|
48
|
+
return result === 'found';
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createTabAndStartCC(tabName: string, ccSessionId: string, cwd: string): void {
|
|
55
|
+
// Create NEW WINDOW and capture window ID (same approach as cc-mesh)
|
|
56
|
+
const windowId = execSync(`osascript -e '
|
|
57
|
+
tell application "iTerm2"
|
|
58
|
+
set w to (create window with default profile)
|
|
59
|
+
tell current session of w
|
|
60
|
+
write text "cd ${cwd} && claude --resume ${ccSessionId} --dangerously-skip-permissions"
|
|
61
|
+
end tell
|
|
62
|
+
return id of w
|
|
63
|
+
end tell
|
|
64
|
+
'`, { encoding: 'utf-8' }).trim();
|
|
65
|
+
tabSessionIds.set(tabName, windowId);
|
|
66
|
+
saveTabRegistry();
|
|
67
|
+
console.log(`[cc2wechat] window created: ${tabName} -> window id: ${windowId}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function injectMessage(tabName: string, message: string): void {
|
|
71
|
+
const windowId = tabSessionIds.get(tabName);
|
|
72
|
+
if (!windowId) return;
|
|
73
|
+
const escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
74
|
+
execSync(`osascript -e '
|
|
75
|
+
tell application "iTerm2"
|
|
76
|
+
tell current session of (first window whose id is ${windowId})
|
|
77
|
+
write text "${escaped}"
|
|
78
|
+
end tell
|
|
79
|
+
end tell
|
|
80
|
+
'`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Terminal mode message handler
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
export async function handleMessageTerminal(msg: WeixinMessage, account: AccountData): Promise<void> {
|
|
88
|
+
const text = extractText(msg);
|
|
89
|
+
const userId = msg.from_user_id ?? '';
|
|
90
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
91
|
+
const tabName = `wechat-${userId.slice(0, 8)}`;
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
|
|
94
|
+
if (tabExists(tabName)) {
|
|
95
|
+
console.log(`[cc2wechat] -> inject to existing window: ${tabName}`);
|
|
96
|
+
injectMessage(tabName, `[微信] ${text}`);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(`[cc2wechat] -> creating window: ${tabName} (session: ${sessionId})`);
|
|
99
|
+
createTabAndStartCC(tabName, sessionId, cwd);
|
|
100
|
+
await sleep(5000);
|
|
101
|
+
injectMessage(tabName, `[微信] ${text}`);
|
|
102
|
+
}
|
|
103
|
+
userTabs.set(userId, tabName);
|
|
104
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { WeixinMessage } from './types.js';
|
|
3
|
+
import { MessageItemType } from './types.js';
|
|
4
|
+
|
|
5
|
+
export function userIdToSessionUUID(userId: string): string {
|
|
6
|
+
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
7
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function extractText(msg: WeixinMessage): string {
|
|
11
|
+
const parts: string[] = [];
|
|
12
|
+
for (const item of msg.item_list ?? []) {
|
|
13
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
14
|
+
parts.push(item.text_item.text);
|
|
15
|
+
} else if (item.type === MessageItemType.IMAGE) {
|
|
16
|
+
parts.push('[Image]');
|
|
17
|
+
} else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
18
|
+
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
19
|
+
} else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
20
|
+
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
21
|
+
} else if (item.type === MessageItemType.VIDEO) {
|
|
22
|
+
parts.push('[Video]');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return parts.join('\n') || '[Empty message]';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function sleep(ms: number): Promise<void> {
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30
|
+
}
|