chore: remove untested feishu/wechat adapters, polish CLI-ELECTRON.md
- Remove feishu and wechat adapters (not tested yet, will re-add later) - Remove their rows from README.md and README.zh-CN.md - Significantly polish CLI-ELECTRON.md skill guide: - Add Electron detection guide (check for Electron Framework) - Add Non-Electron AppleScript pattern section - Add port assignment table for all CDP adapters - Improve code examples with real working TypeScript
This commit is contained in:
1 parent
c370bd0582
commit
63489fb596
17 files changed
+89
-461
No files matched your search
@@ -1,21 +0,0 @@
|
||||
# Feishu (飞书/Lark) Desktop Adapter
|
||||
|
||||
Control the **Feishu Desktop App** from the terminal via macOS AppleScript automation.
|
||||
|
||||
> **Note:** Feishu/Lark uses a custom `Lark Framework` (Chromium v131 embedded) but does **not** expose CDP. This adapter uses AppleScript + clipboard instead.
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `feishu status` | Check if Feishu is running |
|
||||
| `feishu send "message"` | Send a message in the active conversation |
|
||||
| `feishu new` | Start a new chat (Cmd+N) |
|
||||
| `feishu search "query"` | Open global search (Cmd+K) |
|
||||
| `feishu read` | Copy recent messages from the active chat |
|
||||
|
||||
## Requirements
|
||||
|
||||
- macOS only (uses AppleScript)
|
||||
- **Accessibility permissions** must be granted to your terminal app
|
||||
- Feishu must be running with a conversation window open
|
||||
@@ -1,21 +0,0 @@
|
||||
# 飞书桌面端适配器
|
||||
|
||||
通过 macOS AppleScript 在终端中控制 **飞书桌面应用**。
|
||||
|
||||
> **注意:** 飞书使用自研 `Lark Framework`(内嵌 Chromium v131),但不暴露 CDP。因此使用 AppleScript + 剪贴板方式。
|
||||
|
||||
## 命令
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `feishu status` | 检查飞书是否在运行 |
|
||||
| `feishu send "消息"` | 在当前对话窗口发送消息 |
|
||||
| `feishu new` | 开始新对话(Cmd+N) |
|
||||
| `feishu search "关键词"` | 打开全局搜索(Cmd+K) |
|
||||
| `feishu read` | 复制当前对话的聊天记录 |
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 仅 macOS(使用 AppleScript)
|
||||
- 需要为终端应用开启**辅助功能权限**
|
||||
- 飞书必须打开且有对话窗口
|
||||
@@ -1,24 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const newCommand = cli({
|
||||
site: 'feishu',
|
||||
name: 'new',
|
||||
description: 'Start a new chat in Feishu (Cmd+N)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
execSync("osascript -e 'tell application \"System Events\" to keystroke \"n\" using command down'");
|
||||
return [{ Status: 'Success' }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const readCommand = cli({
|
||||
site: 'feishu',
|
||||
name: 'read',
|
||||
description: 'Copy recent messages from the active Feishu conversation (Cmd+A → Cmd+C)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Content'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
let clipBackup = '';
|
||||
try { clipBackup = execSync('pbpaste', { encoding: 'utf-8' }); } catch {}
|
||||
|
||||
spawnSync('pbcopy', { input: '__OPENCLI_EMPTY__' });
|
||||
|
||||
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
const cmd = "osascript " +
|
||||
"-e 'tell application \"System Events\"' " +
|
||||
"-e 'keystroke \"a\" using command down' " +
|
||||
"-e 'delay 0.2' " +
|
||||
"-e 'keystroke \"c\" using command down' " +
|
||||
"-e 'end tell'";
|
||||
execSync(cmd);
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
const result = execSync('pbpaste', { encoding: 'utf-8' }).trim();
|
||||
|
||||
if (clipBackup) spawnSync('pbcopy', { input: clipBackup });
|
||||
|
||||
if (!result || result === '__OPENCLI_EMPTY__') {
|
||||
return [{ Content: 'Could not copy chat content. Try clicking in the message area first.' }];
|
||||
}
|
||||
|
||||
return [{ Content: result }];
|
||||
} catch (err: any) {
|
||||
return [{ Content: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const searchCommand = cli({
|
||||
site: 'feishu',
|
||||
name: 'search',
|
||||
description: 'Open Feishu search and type a query (Cmd+K)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null, kwargs: any) => {
|
||||
const query = kwargs.query as string;
|
||||
try {
|
||||
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
// Feishu uses Cmd+K for global search (similar to Slack/Notion)
|
||||
execSync("osascript -e 'tell application \"System Events\" to keystroke \"k\" using command down'");
|
||||
execSync("osascript -e 'delay 0.5'");
|
||||
|
||||
const escaped = query.replace(/'/g, "'\\''");
|
||||
execSync(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`);
|
||||
|
||||
return [{ Status: `Searched for: ${query}` }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const sendCommand = cli({
|
||||
site: 'feishu',
|
||||
name: 'send',
|
||||
description: 'Send a message in the active Feishu conversation window',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null, kwargs: any) => {
|
||||
const text = kwargs.text as string;
|
||||
try {
|
||||
let clipBackup = '';
|
||||
try { clipBackup = execSync('pbpaste', { encoding: 'utf-8' }); } catch {}
|
||||
|
||||
spawnSync('pbcopy', { input: text });
|
||||
|
||||
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
||||
execSync("osascript -e 'delay 0.5'");
|
||||
|
||||
const cmd = "osascript " +
|
||||
"-e 'tell application \"System Events\"' " +
|
||||
"-e 'keystroke \"v\" using command down' " +
|
||||
"-e 'delay 0.2' " +
|
||||
"-e 'keystroke return' " +
|
||||
"-e 'end tell'";
|
||||
execSync(cmd);
|
||||
|
||||
if (clipBackup) spawnSync('pbcopy', { input: clipBackup });
|
||||
|
||||
return [{ Status: 'Success' }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const statusCommand = cli({
|
||||
site: 'feishu',
|
||||
name: 'status',
|
||||
description: 'Check if Feishu (Lark) Desktop App is running on macOS',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
const output = execSync("osascript -e 'application \"Lark\" is running'", { encoding: 'utf-8' }).trim();
|
||||
return [{ Status: output === 'true' ? 'Running' : 'Stopped' }];
|
||||
} catch {
|
||||
return [{ Status: 'Error querying application state' }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
# WeChat (微信) Desktop Adapter
|
||||
|
||||
Control the **WeChat Desktop App** from the terminal via macOS AppleScript automation.
|
||||
|
||||
> **Note:** WeChat Mac is a native Cocoa app (not Electron), so this adapter uses AppleScript + clipboard instead of CDP. Requires macOS Accessibility permissions for your terminal app.
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `wechat status` | Check if WeChat is running |
|
||||
| `wechat send "message"` | Send a message in the active conversation |
|
||||
| `wechat new` | Start a new chat (Cmd+N) |
|
||||
| `wechat search "query"` | Open search and type a query (Cmd+F) |
|
||||
| `wechat read` | Copy recent messages from the active chat |
|
||||
|
||||
## Requirements
|
||||
|
||||
- macOS only (uses AppleScript)
|
||||
- **Accessibility permissions** must be granted to your terminal app (System Settings → Privacy & Security → Accessibility)
|
||||
- WeChat must be running with a conversation window open
|
||||
@@ -1,21 +0,0 @@
|
||||
# 微信桌面端适配器
|
||||
|
||||
通过 macOS AppleScript 在终端中控制 **微信桌面应用**。
|
||||
|
||||
> **注意:** 微信 Mac 版是原生 Cocoa 应用(非 Electron),因此使用 AppleScript + 剪贴板方式,而非 CDP。需要为终端开启 macOS 辅助功能权限。
|
||||
|
||||
## 命令
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `wechat status` | 检查微信是否在运行 |
|
||||
| `wechat send "消息"` | 在当前对话窗口发送消息 |
|
||||
| `wechat new` | 开始新对话(Cmd+N) |
|
||||
| `wechat search "关键词"` | 打开搜索并输入(Cmd+F) |
|
||||
| `wechat read` | 复制当前对话的聊天记录 |
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 仅 macOS(使用 AppleScript)
|
||||
- 需要为终端应用开启**辅助功能权限**(系统设置 → 隐私与安全 → 辅助功能)
|
||||
- 微信必须打开且有对话窗口
|
||||
@@ -1,24 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const newCommand = cli({
|
||||
site: 'wechat',
|
||||
name: 'new',
|
||||
description: 'Start a new chat in WeChat (Cmd+N)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
execSync("osascript -e 'tell application \"WeChat\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
execSync("osascript -e 'tell application \"System Events\" to keystroke \"n\" using command down'");
|
||||
return [{ Status: 'Success' }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const readCommand = cli({
|
||||
site: 'wechat',
|
||||
name: 'read',
|
||||
description: 'Copy recent messages from the active WeChat conversation (Cmd+A → Cmd+C)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Content'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
// Backup clipboard
|
||||
let clipBackup = '';
|
||||
try { clipBackup = execSync('pbpaste', { encoding: 'utf-8' }); } catch {}
|
||||
|
||||
// Clear clipboard marker
|
||||
spawnSync('pbcopy', { input: '__OPENCLI_EMPTY__' });
|
||||
|
||||
execSync("osascript -e 'tell application \"WeChat\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
// Try Cmd+A then Cmd+C to select and copy chat content
|
||||
const cmd = "osascript " +
|
||||
"-e 'tell application \"System Events\"' " +
|
||||
"-e 'keystroke \"a\" using command down' " +
|
||||
"-e 'delay 0.2' " +
|
||||
"-e 'keystroke \"c\" using command down' " +
|
||||
"-e 'end tell'";
|
||||
execSync(cmd);
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
const result = execSync('pbpaste', { encoding: 'utf-8' }).trim();
|
||||
|
||||
// Restore clipboard
|
||||
if (clipBackup) spawnSync('pbcopy', { input: clipBackup });
|
||||
|
||||
if (!result || result === '__OPENCLI_EMPTY__') {
|
||||
return [{ Content: 'Could not copy chat content. WeChat may not support Cmd+A in the message area.' }];
|
||||
}
|
||||
|
||||
return [{ Content: result }];
|
||||
} catch (err: any) {
|
||||
return [{ Content: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const searchCommand = cli({
|
||||
site: 'wechat',
|
||||
name: 'search',
|
||||
description: 'Open WeChat search and type a query (Cmd+F)',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [{ name: 'query', required: true, positional: true, help: 'Search query (contact name, group, or message)' }],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null, kwargs: any) => {
|
||||
const query = kwargs.query as string;
|
||||
try {
|
||||
execSync("osascript -e 'tell application \"WeChat\" to activate'");
|
||||
execSync("osascript -e 'delay 0.3'");
|
||||
|
||||
// Cmd+F opens search in WeChat Mac
|
||||
execSync("osascript -e 'tell application \"System Events\" to keystroke \"f\" using command down'");
|
||||
execSync("osascript -e 'delay 0.5'");
|
||||
|
||||
// Type the search query
|
||||
const escaped = query.replace(/'/g, "'\\''");
|
||||
execSync(`osascript -e 'tell application "System Events" to keystroke "${escaped}"'`);
|
||||
|
||||
return [{ Status: `Searched for: ${query}` }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const sendCommand = cli({
|
||||
site: 'wechat',
|
||||
name: 'send',
|
||||
description: 'Send a message in the active WeChat conversation window',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null, kwargs: any) => {
|
||||
const text = kwargs.text as string;
|
||||
try {
|
||||
// Backup clipboard
|
||||
let clipBackup = '';
|
||||
try { clipBackup = execSync('pbpaste', { encoding: 'utf-8' }); } catch {}
|
||||
|
||||
// Copy text to clipboard
|
||||
spawnSync('pbcopy', { input: text });
|
||||
|
||||
// Activate WeChat and paste + send
|
||||
execSync("osascript -e 'tell application \"WeChat\" to activate'");
|
||||
execSync("osascript -e 'delay 0.5'");
|
||||
|
||||
const cmd = "osascript " +
|
||||
"-e 'tell application \"System Events\"' " +
|
||||
"-e 'keystroke \"v\" using command down' " +
|
||||
"-e 'delay 0.2' " +
|
||||
"-e 'keystroke return' " +
|
||||
"-e 'end tell'";
|
||||
execSync(cmd);
|
||||
|
||||
// Restore clipboard
|
||||
if (clipBackup) spawnSync('pbcopy', { input: clipBackup });
|
||||
|
||||
return [{ Status: 'Success' }];
|
||||
} catch (err: any) {
|
||||
return [{ Status: 'Error: ' + err.message }];
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { cli, Strategy } from '../../registry.js';
|
||||
import type { IPage } from '../../types.js';
|
||||
|
||||
export const statusCommand = cli({
|
||||
site: 'wechat',
|
||||
name: 'status',
|
||||
description: 'Check if WeChat Desktop App is running on macOS',
|
||||
domain: 'localhost',
|
||||
strategy: Strategy.PUBLIC,
|
||||
browser: false,
|
||||
args: [],
|
||||
columns: ['Status'],
|
||||
func: async (page: IPage | null) => {
|
||||
try {
|
||||
const output = execSync("osascript -e 'application \"WeChat\" is running'", { encoding: 'utf-8' }).trim();
|
||||
return [{ Status: output === 'true' ? 'Running' : 'Stopped' }];
|
||||
} catch {
|
||||
return [{ Status: 'Error querying application state' }];
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user