@jackwener/opencli 0.9.6 → 0.9.8
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/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +42 -0
- package/.github/ISSUE_TEMPLATE/new_site_adapter.yml +57 -0
- package/.github/dependabot.yml +27 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/ci.yml +14 -8
- package/.github/workflows/e2e-headed.yml +6 -2
- package/.github/workflows/pkg-pr-new.yml +2 -2
- package/.github/workflows/release-please.yml +25 -0
- package/.github/workflows/release.yml +2 -2
- package/.github/workflows/security.yml +36 -0
- package/CLI-ELECTRON.md +89 -36
- package/CONTRIBUTING.md +167 -0
- package/README.md +98 -32
- package/README.zh-CN.md +99 -33
- package/dist/browser/discover.js +22 -7
- package/dist/browser.test.js +23 -0
- package/dist/build-manifest.d.ts +26 -0
- package/dist/build-manifest.js +132 -60
- package/dist/build-manifest.test.d.ts +1 -0
- package/dist/build-manifest.test.js +26 -0
- package/dist/cli-manifest.json +1415 -29
- package/dist/clis/bilibili/download.d.ts +10 -0
- package/dist/clis/bilibili/download.js +135 -0
- package/dist/clis/chatwise/ask.d.ts +1 -0
- package/dist/clis/chatwise/ask.js +76 -0
- package/dist/clis/chatwise/export.d.ts +1 -0
- package/dist/clis/chatwise/export.js +46 -0
- package/dist/clis/chatwise/history.d.ts +1 -0
- package/dist/clis/chatwise/history.js +43 -0
- package/dist/clis/chatwise/model.d.ts +1 -0
- package/dist/clis/chatwise/model.js +81 -0
- package/dist/clis/chatwise/new.d.ts +1 -0
- package/dist/clis/chatwise/new.js +18 -0
- package/dist/clis/chatwise/read.d.ts +1 -0
- package/dist/clis/chatwise/read.js +39 -0
- package/dist/clis/chatwise/screenshot.d.ts +1 -0
- package/dist/clis/chatwise/screenshot.js +27 -0
- package/dist/clis/chatwise/send.d.ts +1 -0
- package/dist/clis/chatwise/send.js +45 -0
- package/dist/clis/chatwise/status.d.ts +1 -0
- package/dist/clis/chatwise/status.js +22 -0
- package/dist/clis/discord-app/channels.d.ts +1 -0
- package/dist/clis/discord-app/channels.js +45 -0
- package/dist/clis/discord-app/members.d.ts +1 -0
- package/dist/clis/discord-app/members.js +38 -0
- package/dist/clis/discord-app/read.d.ts +1 -0
- package/dist/clis/discord-app/read.js +45 -0
- package/dist/clis/discord-app/search.d.ts +1 -0
- package/dist/clis/discord-app/search.js +56 -0
- package/dist/clis/discord-app/send.d.ts +1 -0
- package/dist/clis/discord-app/send.js +27 -0
- package/dist/clis/discord-app/servers.d.ts +1 -0
- package/dist/clis/discord-app/servers.js +36 -0
- package/dist/clis/discord-app/status.d.ts +1 -0
- package/dist/clis/discord-app/status.js +16 -0
- package/dist/clis/feishu/new.d.ts +1 -0
- package/dist/clis/feishu/new.js +27 -0
- package/dist/clis/feishu/read.d.ts +1 -0
- package/dist/clis/feishu/read.js +40 -0
- package/dist/clis/feishu/search.d.ts +1 -0
- package/dist/clis/feishu/search.js +30 -0
- package/dist/clis/feishu/send.d.ts +1 -0
- package/dist/clis/feishu/send.js +39 -0
- package/dist/clis/feishu/status.d.ts +1 -0
- package/dist/clis/feishu/status.js +28 -0
- package/dist/clis/grok/ask.d.ts +1 -0
- package/dist/clis/grok/ask.js +82 -0
- package/dist/clis/grok/debug.d.ts +1 -0
- package/dist/clis/grok/debug.js +45 -0
- package/dist/clis/jimeng/generate.yaml +84 -0
- package/dist/clis/jimeng/history.yaml +47 -0
- package/dist/clis/linux-do/categories.yaml +41 -0
- package/dist/clis/linux-do/category.yaml +49 -0
- package/dist/clis/linux-do/hot.yaml +50 -0
- package/dist/clis/linux-do/latest.yaml +40 -0
- package/dist/clis/linux-do/search.yaml +45 -0
- package/dist/clis/linux-do/topic.yaml +38 -0
- package/dist/clis/notion/export.d.ts +1 -0
- package/dist/clis/notion/export.js +31 -0
- package/dist/clis/notion/favorites.d.ts +1 -0
- package/dist/clis/notion/favorites.js +84 -0
- package/dist/clis/notion/new.d.ts +1 -0
- package/dist/clis/notion/new.js +34 -0
- package/dist/clis/notion/read.d.ts +1 -0
- package/dist/clis/notion/read.js +30 -0
- package/dist/clis/notion/search.d.ts +1 -0
- package/dist/clis/notion/search.js +46 -0
- package/dist/clis/notion/sidebar.d.ts +1 -0
- package/dist/clis/notion/sidebar.js +41 -0
- package/dist/clis/notion/status.d.ts +1 -0
- package/dist/clis/notion/status.js +16 -0
- package/dist/clis/notion/write.d.ts +1 -0
- package/dist/clis/notion/write.js +40 -0
- package/dist/clis/twitter/download.d.ts +8 -0
- package/dist/clis/twitter/download.js +204 -0
- package/dist/clis/wechat/chats.d.ts +1 -0
- package/dist/clis/wechat/chats.js +28 -0
- package/dist/clis/wechat/contacts.d.ts +1 -0
- package/dist/clis/wechat/contacts.js +28 -0
- package/dist/clis/wechat/read.d.ts +1 -0
- package/dist/clis/wechat/read.js +58 -0
- package/dist/clis/wechat/search.d.ts +1 -0
- package/dist/clis/wechat/search.js +31 -0
- package/dist/clis/wechat/send.d.ts +1 -0
- package/dist/clis/wechat/send.js +42 -0
- package/dist/clis/wechat/status.d.ts +1 -0
- package/dist/clis/wechat/status.js +29 -0
- package/dist/clis/xiaohongshu/creator-note-detail.d.ts +10 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +88 -0
- package/dist/clis/xiaohongshu/creator-notes.d.ts +11 -0
- package/dist/clis/xiaohongshu/creator-notes.js +109 -0
- package/dist/clis/xiaohongshu/creator-profile.d.ts +10 -0
- package/dist/clis/xiaohongshu/creator-profile.js +54 -0
- package/dist/clis/xiaohongshu/creator-stats.d.ts +10 -0
- package/dist/clis/xiaohongshu/creator-stats.js +74 -0
- package/dist/clis/xiaohongshu/download.d.ts +7 -0
- package/dist/clis/xiaohongshu/download.js +155 -0
- package/dist/clis/xiaohongshu/search.js +1 -1
- package/dist/clis/xiaohongshu/user-helpers.d.ts +15 -0
- package/dist/clis/xiaohongshu/user-helpers.js +67 -0
- package/dist/clis/xiaohongshu/user-helpers.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/user-helpers.test.js +81 -0
- package/dist/clis/xiaohongshu/user.js +46 -29
- package/dist/clis/zhihu/download.d.ts +11 -0
- package/dist/clis/zhihu/download.js +186 -0
- package/dist/clis/zhihu/download.test.d.ts +1 -0
- package/dist/clis/zhihu/download.test.js +10 -0
- package/dist/download/index.d.ts +79 -0
- package/dist/download/index.js +325 -0
- package/dist/download/progress.d.ts +36 -0
- package/dist/download/progress.js +111 -0
- package/dist/engine.test.js +15 -0
- package/dist/main.js +16 -3
- package/dist/pipeline/registry.js +2 -0
- package/dist/pipeline/steps/download.d.ts +34 -0
- package/dist/pipeline/steps/download.js +251 -0
- package/dist/pipeline/template.js +28 -0
- package/package.json +4 -3
- package/scripts/test-site.mjs +70 -0
- package/src/browser/discover.ts +23 -7
- package/src/browser.test.ts +23 -0
- package/src/build-manifest.test.ts +28 -0
- package/src/build-manifest.ts +147 -57
- package/src/clis/bilibili/download.ts +161 -0
- package/src/clis/chatwise/README.md +38 -0
- package/src/clis/chatwise/README.zh-CN.md +38 -0
- package/src/clis/chatwise/ask.ts +87 -0
- package/src/clis/chatwise/export.ts +51 -0
- package/src/clis/chatwise/history.ts +47 -0
- package/src/clis/chatwise/model.ts +87 -0
- package/src/clis/chatwise/new.ts +21 -0
- package/src/clis/chatwise/read.ts +42 -0
- package/src/clis/chatwise/screenshot.ts +33 -0
- package/src/clis/chatwise/send.ts +50 -0
- package/src/clis/chatwise/status.ts +25 -0
- package/src/clis/discord-app/README.md +28 -0
- package/src/clis/discord-app/README.zh-CN.md +28 -0
- package/src/clis/discord-app/channels.ts +48 -0
- package/src/clis/discord-app/members.ts +41 -0
- package/src/clis/discord-app/read.ts +49 -0
- package/src/clis/discord-app/search.ts +64 -0
- package/src/clis/discord-app/send.ts +32 -0
- package/src/clis/discord-app/servers.ts +39 -0
- package/src/clis/discord-app/status.ts +18 -0
- package/src/clis/feishu/README.md +20 -0
- package/src/clis/feishu/README.zh-CN.md +20 -0
- package/src/clis/feishu/new.ts +32 -0
- package/src/clis/feishu/read.ts +48 -0
- package/src/clis/feishu/search.ts +35 -0
- package/src/clis/feishu/send.ts +46 -0
- package/src/clis/feishu/status.ts +34 -0
- package/src/clis/grok/ask.ts +90 -0
- package/src/clis/grok/debug.ts +49 -0
- package/src/clis/jimeng/generate.yaml +84 -0
- package/src/clis/jimeng/history.yaml +47 -0
- package/src/clis/linux-do/categories.yaml +41 -0
- package/src/clis/linux-do/category.yaml +49 -0
- package/src/clis/linux-do/hot.yaml +50 -0
- package/src/clis/linux-do/latest.yaml +40 -0
- package/src/clis/linux-do/search.yaml +45 -0
- package/src/clis/linux-do/topic.yaml +38 -0
- package/src/clis/notion/README.md +29 -0
- package/src/clis/notion/README.zh-CN.md +29 -0
- package/src/clis/notion/export.ts +36 -0
- package/src/clis/notion/favorites.ts +87 -0
- package/src/clis/notion/new.ts +39 -0
- package/src/clis/notion/read.ts +33 -0
- package/src/clis/notion/search.ts +54 -0
- package/src/clis/notion/sidebar.ts +44 -0
- package/src/clis/notion/status.ts +18 -0
- package/src/clis/notion/write.ts +45 -0
- package/src/clis/twitter/download.ts +227 -0
- package/src/clis/wechat/README.md +28 -0
- package/src/clis/wechat/README.zh-CN.md +28 -0
- package/src/clis/wechat/chats.ts +33 -0
- package/src/clis/wechat/contacts.ts +33 -0
- package/src/clis/wechat/read.ts +72 -0
- package/src/clis/wechat/search.ts +36 -0
- package/src/clis/wechat/send.ts +49 -0
- package/src/clis/wechat/status.ts +35 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +95 -0
- package/src/clis/xiaohongshu/creator-notes.ts +116 -0
- package/src/clis/xiaohongshu/creator-profile.ts +60 -0
- package/src/clis/xiaohongshu/creator-stats.ts +81 -0
- package/src/clis/xiaohongshu/download.ts +173 -0
- package/src/clis/xiaohongshu/search.ts +1 -1
- package/src/clis/xiaohongshu/user-helpers.test.ts +106 -0
- package/src/clis/xiaohongshu/user-helpers.ts +85 -0
- package/src/clis/xiaohongshu/user.ts +52 -32
- package/src/clis/zhihu/download.test.ts +12 -0
- package/src/clis/zhihu/download.ts +223 -0
- package/src/download/index.ts +395 -0
- package/src/download/progress.ts +125 -0
- package/src/engine.test.ts +17 -0
- package/src/main.ts +12 -3
- package/src/pipeline/registry.ts +2 -0
- package/src/pipeline/steps/download.ts +310 -0
- package/src/pipeline/template.ts +26 -0
- package/tests/e2e/browser-auth.test.ts +25 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const newCommand = cli({
|
|
6
|
+
site: 'feishu',
|
|
7
|
+
name: 'new',
|
|
8
|
+
description: 'Create a new message or document in Feishu',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.PUBLIC,
|
|
11
|
+
browser: false,
|
|
12
|
+
args: [],
|
|
13
|
+
columns: ['Status'],
|
|
14
|
+
func: async (page: IPage | null) => {
|
|
15
|
+
try {
|
|
16
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
17
|
+
execSync("osascript -e 'delay 0.3'");
|
|
18
|
+
|
|
19
|
+
// Cmd+N for new conversation/document
|
|
20
|
+
execSync(
|
|
21
|
+
"osascript " +
|
|
22
|
+
"-e 'tell application \"System Events\"' " +
|
|
23
|
+
"-e 'keystroke \"n\" using command down' " +
|
|
24
|
+
"-e 'end tell'"
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return [{ Status: 'New item dialog opened (Cmd+N)' }];
|
|
28
|
+
} catch (err: any) {
|
|
29
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const readCommand = cli({
|
|
6
|
+
site: 'feishu',
|
|
7
|
+
name: 'read',
|
|
8
|
+
description: 'Read the current chat content by selecting all and copying',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.PUBLIC,
|
|
11
|
+
browser: false,
|
|
12
|
+
args: [],
|
|
13
|
+
columns: ['Content'],
|
|
14
|
+
func: async (page: IPage | null) => {
|
|
15
|
+
try {
|
|
16
|
+
let clipBackup = '';
|
|
17
|
+
try {
|
|
18
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
19
|
+
} catch { /* empty */ }
|
|
20
|
+
|
|
21
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
22
|
+
execSync("osascript -e 'delay 0.3'");
|
|
23
|
+
|
|
24
|
+
execSync(
|
|
25
|
+
"osascript " +
|
|
26
|
+
"-e 'tell application \"System Events\"' " +
|
|
27
|
+
"-e 'keystroke \"a\" using command down' " +
|
|
28
|
+
"-e 'delay 0.2' " +
|
|
29
|
+
"-e 'keystroke \"c\" using command down' " +
|
|
30
|
+
"-e 'delay 0.2' " +
|
|
31
|
+
"-e 'end tell'"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const content = execSync('pbpaste', { encoding: 'utf-8' }).trim();
|
|
35
|
+
|
|
36
|
+
if (clipBackup) {
|
|
37
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Deselect
|
|
41
|
+
execSync("osascript -e 'tell application \"System Events\" to key code 53'");
|
|
42
|
+
|
|
43
|
+
return [{ Content: content || '(no content captured)' }];
|
|
44
|
+
} catch (err: any) {
|
|
45
|
+
return [{ Content: 'Error: ' + err.message }];
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const searchCommand = cli({
|
|
6
|
+
site: 'feishu',
|
|
7
|
+
name: 'search',
|
|
8
|
+
description: 'Open Feishu global search and type a query (Cmd+K)',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.PUBLIC,
|
|
11
|
+
browser: false,
|
|
12
|
+
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
|
13
|
+
columns: ['Status'],
|
|
14
|
+
func: async (page: IPage | null, kwargs: any) => {
|
|
15
|
+
const query = kwargs.query as string;
|
|
16
|
+
try {
|
|
17
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
18
|
+
execSync("osascript -e 'delay 0.3'");
|
|
19
|
+
|
|
20
|
+
// Feishu uses Cmd+K for global search (similar to Slack/Notion)
|
|
21
|
+
execSync(
|
|
22
|
+
"osascript " +
|
|
23
|
+
"-e 'tell application \"System Events\"' " +
|
|
24
|
+
"-e 'keystroke \"k\" using command down' " +
|
|
25
|
+
"-e 'delay 0.5' " +
|
|
26
|
+
`-e 'keystroke ${JSON.stringify(query)}' ` +
|
|
27
|
+
"-e 'end tell'"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return [{ Status: `Searching for: ${query}` }];
|
|
31
|
+
} catch (err: any) {
|
|
32
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const sendCommand = cli({
|
|
6
|
+
site: 'feishu',
|
|
7
|
+
name: 'send',
|
|
8
|
+
description: 'Send a message in the active Feishu (Lark) conversation',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.PUBLIC,
|
|
11
|
+
browser: false,
|
|
12
|
+
args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
|
|
13
|
+
columns: ['Status'],
|
|
14
|
+
func: async (page: IPage | null, kwargs: any) => {
|
|
15
|
+
const text = kwargs.text as string;
|
|
16
|
+
try {
|
|
17
|
+
// Backup clipboard
|
|
18
|
+
let clipBackup = '';
|
|
19
|
+
try {
|
|
20
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
21
|
+
} catch { /* empty */ }
|
|
22
|
+
|
|
23
|
+
spawnSync('pbcopy', { input: text });
|
|
24
|
+
|
|
25
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
26
|
+
execSync("osascript -e 'delay 0.5'");
|
|
27
|
+
|
|
28
|
+
execSync(
|
|
29
|
+
"osascript " +
|
|
30
|
+
"-e 'tell application \"System Events\"' " +
|
|
31
|
+
"-e 'keystroke \"v\" using command down' " +
|
|
32
|
+
"-e 'delay 0.2' " +
|
|
33
|
+
"-e 'keystroke return' " +
|
|
34
|
+
"-e 'end tell'"
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (clipBackup) {
|
|
38
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return [{ Status: 'Success' }];
|
|
42
|
+
} catch (err: any) {
|
|
43
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const statusCommand = cli({
|
|
6
|
+
site: 'feishu',
|
|
7
|
+
name: 'status',
|
|
8
|
+
description: 'Check if Feishu (Lark) Desktop is running on macOS',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.PUBLIC,
|
|
11
|
+
browser: false,
|
|
12
|
+
args: [],
|
|
13
|
+
columns: ['Status', 'Detail'],
|
|
14
|
+
func: async (page: IPage | null) => {
|
|
15
|
+
try {
|
|
16
|
+
const running = execSync("osascript -e 'application \"Lark\" is running'", { encoding: 'utf-8' }).trim();
|
|
17
|
+
if (running !== 'true') {
|
|
18
|
+
return [{ Status: 'Stopped', Detail: 'Feishu/Lark is not running' }];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const windowCount = execSync(
|
|
22
|
+
"osascript -e 'tell application \"System Events\" to count windows of application process \"Lark\"'",
|
|
23
|
+
{ encoding: 'utf-8' }
|
|
24
|
+
).trim();
|
|
25
|
+
|
|
26
|
+
return [{
|
|
27
|
+
Status: 'Running',
|
|
28
|
+
Detail: `${windowCount} window(s) open`,
|
|
29
|
+
}];
|
|
30
|
+
} catch (err: any) {
|
|
31
|
+
return [{ Status: 'Error', Detail: err.message }];
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const askCommand = cli({
|
|
5
|
+
site: 'grok',
|
|
6
|
+
name: 'ask',
|
|
7
|
+
description: 'Send a message to Grok and get response',
|
|
8
|
+
domain: 'grok.com',
|
|
9
|
+
strategy: Strategy.COOKIE,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'prompt', type: 'string', required: true },
|
|
13
|
+
{ name: 'timeout', type: 'int', default: 120 },
|
|
14
|
+
{ name: 'new', type: 'boolean', default: false },
|
|
15
|
+
],
|
|
16
|
+
columns: ['response'],
|
|
17
|
+
func: async (page: IPage, kwargs: Record<string, any>) => {
|
|
18
|
+
const prompt = kwargs.prompt as string;
|
|
19
|
+
const timeoutMs = ((kwargs.timeout as number) || 120) * 1000;
|
|
20
|
+
const newChat = kwargs.new as boolean;
|
|
21
|
+
|
|
22
|
+
if (newChat) {
|
|
23
|
+
await page.goto('https://grok.com');
|
|
24
|
+
await page.wait(2);
|
|
25
|
+
await page.evaluate(`(() => {
|
|
26
|
+
const btn = [...document.querySelectorAll('a, button')].find(b => {
|
|
27
|
+
const t = (b.textContent || '').trim().toLowerCase();
|
|
28
|
+
return t.includes('new') || b.getAttribute('href') === '/';
|
|
29
|
+
});
|
|
30
|
+
if (btn) btn.click();
|
|
31
|
+
})()`);
|
|
32
|
+
await page.wait(2);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await page.goto('https://grok.com');
|
|
36
|
+
await page.wait(3);
|
|
37
|
+
|
|
38
|
+
const promptJson = JSON.stringify(prompt);
|
|
39
|
+
|
|
40
|
+
const sendResult = await page.evaluate(`(async () => {
|
|
41
|
+
try {
|
|
42
|
+
const box = document.querySelector('textarea');
|
|
43
|
+
if (!box) return { ok: false, msg: 'no textarea' };
|
|
44
|
+
box.focus(); box.value = '';
|
|
45
|
+
document.execCommand('selectAll');
|
|
46
|
+
document.execCommand('insertText', false, ${promptJson});
|
|
47
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
48
|
+
const btn = document.querySelector('button[aria-label="\\u63d0\\u4ea4"]');
|
|
49
|
+
if (btn && !btn.disabled) { btn.click(); return { ok: true, msg: 'clicked' }; }
|
|
50
|
+
const sub = [...document.querySelectorAll('button[type="submit"]')].find(b => !b.disabled);
|
|
51
|
+
if (sub) { sub.click(); return { ok: true, msg: 'clicked-submit' }; }
|
|
52
|
+
box.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
|
|
53
|
+
return { ok: true, msg: 'enter' };
|
|
54
|
+
} catch (e) { return { ok: false, msg: e.toString() }; }
|
|
55
|
+
})()`);
|
|
56
|
+
|
|
57
|
+
if (!sendResult || !sendResult.ok) {
|
|
58
|
+
return [{ response: '[SEND FAILED] ' + JSON.stringify(sendResult) }];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
let lastText = '';
|
|
63
|
+
let stableCount = 0;
|
|
64
|
+
|
|
65
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
66
|
+
await page.wait(3);
|
|
67
|
+
const response = await page.evaluate(`(() => {
|
|
68
|
+
const bubbles = document.querySelectorAll('div.message-bubble, [data-testid="message-bubble"]');
|
|
69
|
+
if (bubbles.length < 2) return '';
|
|
70
|
+
const last = bubbles[bubbles.length - 1];
|
|
71
|
+
const text = (last.innerText || '').trim();
|
|
72
|
+
if (!text || text.length < 2) return '';
|
|
73
|
+
return text;
|
|
74
|
+
})()`);
|
|
75
|
+
|
|
76
|
+
if (response && response.length > 2) {
|
|
77
|
+
if (response === lastText) {
|
|
78
|
+
stableCount++;
|
|
79
|
+
if (stableCount >= 2) return [{ response }];
|
|
80
|
+
} else {
|
|
81
|
+
stableCount = 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
lastText = response || '';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (lastText) return [{ response: lastText }];
|
|
88
|
+
return [{ response: '[NO RESPONSE]' }];
|
|
89
|
+
},
|
|
90
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const debugCommand = cli({
|
|
5
|
+
site: 'grok',
|
|
6
|
+
name: 'debug',
|
|
7
|
+
description: 'Debug grok page structure',
|
|
8
|
+
domain: 'grok.com',
|
|
9
|
+
strategy: Strategy.COOKIE,
|
|
10
|
+
browser: true,
|
|
11
|
+
columns: ['data'],
|
|
12
|
+
func: async (page: IPage, _kwargs: Record<string, any>) => {
|
|
13
|
+
await page.goto('https://grok.com');
|
|
14
|
+
await page.wait(3);
|
|
15
|
+
|
|
16
|
+
// Get all button-like elements near textarea
|
|
17
|
+
const debug = await page.evaluate(`(() => {
|
|
18
|
+
const ta = document.querySelector('textarea');
|
|
19
|
+
if (!ta) return { error: 'no textarea' };
|
|
20
|
+
|
|
21
|
+
// Get parent containers
|
|
22
|
+
let parent = ta.parentElement;
|
|
23
|
+
const parents = [];
|
|
24
|
+
for (let i = 0; i < 5 && parent; i++) {
|
|
25
|
+
parents.push({
|
|
26
|
+
tag: parent.tagName,
|
|
27
|
+
class: parent.className?.substring(0, 80),
|
|
28
|
+
childCount: parent.children.length,
|
|
29
|
+
});
|
|
30
|
+
parent = parent.parentElement;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Find buttons in the form/container near textarea
|
|
34
|
+
const form = ta.closest('form') || ta.closest('[class*="composer"]') || ta.closest('[class*="input"]') || ta.parentElement?.parentElement;
|
|
35
|
+
const buttons = form ? [...form.querySelectorAll('button')].map(b => ({
|
|
36
|
+
testid: b.getAttribute('data-testid'),
|
|
37
|
+
type: b.type,
|
|
38
|
+
disabled: b.disabled,
|
|
39
|
+
text: (b.textContent || '').substring(0, 30),
|
|
40
|
+
html: b.outerHTML.substring(0, 200),
|
|
41
|
+
rect: b.getBoundingClientRect().toJSON(),
|
|
42
|
+
})) : [];
|
|
43
|
+
|
|
44
|
+
return { parents, buttons, formTag: form?.tagName, formClass: form?.className?.substring(0, 80) };
|
|
45
|
+
})()`);
|
|
46
|
+
|
|
47
|
+
return [{ data: JSON.stringify(debug, null, 2) }];
|
|
48
|
+
},
|
|
49
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
site: jimeng
|
|
2
|
+
name: generate
|
|
3
|
+
description: 即梦AI 文生图 — 输入 prompt 生成图片
|
|
4
|
+
domain: jimeng.jianying.com
|
|
5
|
+
strategy: cookie
|
|
6
|
+
browser: true
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
prompt:
|
|
10
|
+
type: string
|
|
11
|
+
required: true
|
|
12
|
+
description: "图片描述 prompt"
|
|
13
|
+
model:
|
|
14
|
+
type: string
|
|
15
|
+
default: "high_aes_general_v50"
|
|
16
|
+
description: "模型: high_aes_general_v50 (5.0 Lite), high_aes_general_v42 (4.6), high_aes_general_v40 (4.0)"
|
|
17
|
+
wait:
|
|
18
|
+
type: int
|
|
19
|
+
default: 40
|
|
20
|
+
description: "等待生成完成的秒数"
|
|
21
|
+
|
|
22
|
+
columns: [status, prompt, image_count, image_urls]
|
|
23
|
+
|
|
24
|
+
pipeline:
|
|
25
|
+
- navigate: https://jimeng.jianying.com/ai-tool/generate?type=image&workspace=0
|
|
26
|
+
- wait: 3
|
|
27
|
+
- evaluate: |
|
|
28
|
+
(async () => {
|
|
29
|
+
const prompt = ${{ args.prompt | json }};
|
|
30
|
+
const waitSec = ${{ args.wait }};
|
|
31
|
+
|
|
32
|
+
// Step 1: Count existing images before generation
|
|
33
|
+
const beforeImgs = document.querySelectorAll('img[src*="dreamina-sign"], img[src*="tb4s082cfz"]').length;
|
|
34
|
+
|
|
35
|
+
// Step 2: Clear and set prompt
|
|
36
|
+
const editors = document.querySelectorAll('[contenteditable="true"]');
|
|
37
|
+
const editor = editors[0];
|
|
38
|
+
if (!editor) return [{ status: 'failed', prompt: prompt, image_count: 0, image_urls: 'Editor not found' }];
|
|
39
|
+
|
|
40
|
+
editor.focus();
|
|
41
|
+
await new Promise(r => setTimeout(r, 200));
|
|
42
|
+
document.execCommand('selectAll');
|
|
43
|
+
await new Promise(r => setTimeout(r, 100));
|
|
44
|
+
document.execCommand('delete');
|
|
45
|
+
await new Promise(r => setTimeout(r, 200));
|
|
46
|
+
document.execCommand('insertText', false, prompt);
|
|
47
|
+
await new Promise(r => setTimeout(r, 500));
|
|
48
|
+
|
|
49
|
+
// Step 3: Click generate
|
|
50
|
+
const btn = document.querySelector('.lv-btn.lv-btn-primary[class*="circle"]');
|
|
51
|
+
if (!btn) return [{ status: 'failed', prompt: prompt, image_count: 0, image_urls: 'Generate button not found' }];
|
|
52
|
+
btn.click();
|
|
53
|
+
|
|
54
|
+
// Step 4: Wait for new images to appear
|
|
55
|
+
let newImgs = [];
|
|
56
|
+
for (let i = 0; i < waitSec; i++) {
|
|
57
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
58
|
+
const allImgs = document.querySelectorAll('img[src*="dreamina-sign"], img[src*="tb4s082cfz"]');
|
|
59
|
+
if (allImgs.length > beforeImgs) {
|
|
60
|
+
// New images appeared — generation complete
|
|
61
|
+
newImgs = Array.from(allImgs).slice(0, allImgs.length - beforeImgs);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (newImgs.length === 0) {
|
|
67
|
+
return [{ status: 'timeout', prompt: prompt, image_count: 0, image_urls: 'Generation may still be in progress' }];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Step 5: Extract image URLs (use thumbnail URLs which are accessible)
|
|
71
|
+
const urls = newImgs.map(img => img.src);
|
|
72
|
+
|
|
73
|
+
return [{
|
|
74
|
+
status: 'success',
|
|
75
|
+
prompt: prompt.substring(0, 80),
|
|
76
|
+
image_count: urls.length,
|
|
77
|
+
image_urls: urls.join('\n')
|
|
78
|
+
}];
|
|
79
|
+
})()
|
|
80
|
+
- map:
|
|
81
|
+
status: ${{ item.status }}
|
|
82
|
+
prompt: ${{ item.prompt }}
|
|
83
|
+
image_count: ${{ item.image_count }}
|
|
84
|
+
image_urls: ${{ item.image_urls }}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
site: jimeng
|
|
2
|
+
name: history
|
|
3
|
+
description: 即梦AI 查看最近生成的作品
|
|
4
|
+
domain: jimeng.jianying.com
|
|
5
|
+
strategy: cookie
|
|
6
|
+
browser: true
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
limit:
|
|
10
|
+
type: int
|
|
11
|
+
default: 5
|
|
12
|
+
|
|
13
|
+
columns: [prompt, model, status, image_url, created_at]
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
- navigate: https://jimeng.jianying.com/ai-tool/generate?type=image&workspace=0
|
|
17
|
+
- wait: 3
|
|
18
|
+
- evaluate: |
|
|
19
|
+
(async () => {
|
|
20
|
+
const limit = ${{ args.limit }};
|
|
21
|
+
const res = await fetch('/mweb/v1/get_history?aid=513695&device_platform=web®ion=cn&da_version=3.3.11&web_version=7.5.0&aigc_features=app_lip_sync', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
credentials: 'include',
|
|
24
|
+
headers: { 'Content-Type': 'application/json' },
|
|
25
|
+
body: JSON.stringify({ cursor: '', count: limit, need_page_item: true, need_aigc_data: true, aigc_mode_list: ['workbench'] })
|
|
26
|
+
});
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
const items = data?.data?.history_list || [];
|
|
29
|
+
return items.slice(0, limit).map(item => {
|
|
30
|
+
const params = item.aigc_image_params?.text2image_params || {};
|
|
31
|
+
const images = item.image?.large_images || [];
|
|
32
|
+
return {
|
|
33
|
+
prompt: params.prompt || item.common_attr?.title || 'N/A',
|
|
34
|
+
model: params.model_config?.model_name || 'unknown',
|
|
35
|
+
status: item.common_attr?.status === 102 ? 'completed' : 'pending',
|
|
36
|
+
image_url: images[0]?.image_url || '',
|
|
37
|
+
created_at: new Date((item.common_attr?.create_time || 0) * 1000).toLocaleString('zh-CN'),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
})()
|
|
41
|
+
- map:
|
|
42
|
+
prompt: ${{ item.prompt }}
|
|
43
|
+
model: ${{ item.model }}
|
|
44
|
+
status: ${{ item.status }}
|
|
45
|
+
image_url: ${{ item.image_url }}
|
|
46
|
+
created_at: ${{ item.created_at }}
|
|
47
|
+
- limit: ${{ args.limit }}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: categories
|
|
3
|
+
description: linux.do 分类列表
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
limit:
|
|
9
|
+
type: int
|
|
10
|
+
default: 20
|
|
11
|
+
description: Number of categories
|
|
12
|
+
|
|
13
|
+
pipeline:
|
|
14
|
+
- navigate: https://linux.do
|
|
15
|
+
|
|
16
|
+
- evaluate: |
|
|
17
|
+
(async () => {
|
|
18
|
+
const res = await fetch('/categories.json', { credentials: 'include' });
|
|
19
|
+
if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
|
|
20
|
+
let data;
|
|
21
|
+
try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
|
|
22
|
+
const cats = data?.category_list?.categories || [];
|
|
23
|
+
return cats.slice(0, ${{ args.limit }}).map(c => ({
|
|
24
|
+
name: c.name,
|
|
25
|
+
slug: c.slug,
|
|
26
|
+
id: c.id,
|
|
27
|
+
topics: c.topic_count,
|
|
28
|
+
description: (c.description_text || '').slice(0, 80),
|
|
29
|
+
}));
|
|
30
|
+
})()
|
|
31
|
+
|
|
32
|
+
- map:
|
|
33
|
+
name: ${{ item.name }}
|
|
34
|
+
slug: ${{ item.slug }}
|
|
35
|
+
id: ${{ item.id }}
|
|
36
|
+
topics: ${{ item.topics }}
|
|
37
|
+
description: ${{ item.description }}
|
|
38
|
+
|
|
39
|
+
- limit: ${{ args.limit }}
|
|
40
|
+
|
|
41
|
+
columns: [name, slug, id, topics, description]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: category
|
|
3
|
+
description: linux.do 分类内话题
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
slug:
|
|
9
|
+
type: str
|
|
10
|
+
required: true
|
|
11
|
+
description: Category slug (use 'categories' command to find)
|
|
12
|
+
id:
|
|
13
|
+
type: int
|
|
14
|
+
required: true
|
|
15
|
+
description: Category ID (use 'categories' command to find)
|
|
16
|
+
limit:
|
|
17
|
+
type: int
|
|
18
|
+
default: 20
|
|
19
|
+
description: Number of topics
|
|
20
|
+
|
|
21
|
+
pipeline:
|
|
22
|
+
- navigate: https://linux.do
|
|
23
|
+
|
|
24
|
+
- evaluate: |
|
|
25
|
+
(async () => {
|
|
26
|
+
const slug = ${{ args.slug | json }};
|
|
27
|
+
const res = await fetch('/c/' + encodeURIComponent(slug) + '/${{ args.id }}.json', { credentials: 'include' });
|
|
28
|
+
if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
|
|
29
|
+
let data;
|
|
30
|
+
try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
|
|
31
|
+
const topics = data?.topic_list?.topics || [];
|
|
32
|
+
return topics.slice(0, ${{ args.limit }}).map(t => ({
|
|
33
|
+
title: t.title,
|
|
34
|
+
replies: (t.posts_count || 1) - 1,
|
|
35
|
+
views: t.views,
|
|
36
|
+
likes: t.like_count,
|
|
37
|
+
}));
|
|
38
|
+
})()
|
|
39
|
+
|
|
40
|
+
- map:
|
|
41
|
+
rank: ${{ index + 1 }}
|
|
42
|
+
title: ${{ item.title }}
|
|
43
|
+
replies: ${{ item.replies }}
|
|
44
|
+
views: ${{ item.views }}
|
|
45
|
+
likes: ${{ item.likes }}
|
|
46
|
+
|
|
47
|
+
- limit: ${{ args.limit }}
|
|
48
|
+
|
|
49
|
+
columns: [rank, title, replies, views, likes]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: hot
|
|
3
|
+
description: linux.do 热门话题
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
limit:
|
|
9
|
+
type: int
|
|
10
|
+
default: 20
|
|
11
|
+
description: Number of topics
|
|
12
|
+
period:
|
|
13
|
+
type: str
|
|
14
|
+
default: weekly
|
|
15
|
+
description: Time period
|
|
16
|
+
choices: [all, daily, weekly, monthly, yearly]
|
|
17
|
+
|
|
18
|
+
pipeline:
|
|
19
|
+
- navigate: https://linux.do
|
|
20
|
+
|
|
21
|
+
- evaluate: |
|
|
22
|
+
(async () => {
|
|
23
|
+
const period = ${{ args.period | json }};
|
|
24
|
+
const res = await fetch('/top.json?period=' + encodeURIComponent(period), { credentials: 'include' });
|
|
25
|
+
if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
|
|
26
|
+
let data;
|
|
27
|
+
try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
|
|
28
|
+
const topics = data?.topic_list?.topics || [];
|
|
29
|
+
const cats = data?.topic_list?.categories || data?.categories || [];
|
|
30
|
+
const catMap = Object.fromEntries(cats.map(c => [c.id, c.name]));
|
|
31
|
+
return topics.slice(0, ${{ args.limit }}).map(t => ({
|
|
32
|
+
title: t.title,
|
|
33
|
+
replies: (t.posts_count || 1) - 1,
|
|
34
|
+
views: t.views,
|
|
35
|
+
likes: t.like_count,
|
|
36
|
+
category: catMap[t.category_id] || String(t.category_id),
|
|
37
|
+
}));
|
|
38
|
+
})()
|
|
39
|
+
|
|
40
|
+
- map:
|
|
41
|
+
rank: ${{ index + 1 }}
|
|
42
|
+
title: ${{ item.title }}
|
|
43
|
+
replies: ${{ item.replies }}
|
|
44
|
+
views: ${{ item.views }}
|
|
45
|
+
likes: ${{ item.likes }}
|
|
46
|
+
category: ${{ item.category }}
|
|
47
|
+
|
|
48
|
+
- limit: ${{ args.limit }}
|
|
49
|
+
|
|
50
|
+
columns: [rank, title, replies, views, likes, category]
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: latest
|
|
3
|
+
description: linux.do 最新话题
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
limit:
|
|
9
|
+
type: int
|
|
10
|
+
default: 20
|
|
11
|
+
description: Number of topics
|
|
12
|
+
|
|
13
|
+
pipeline:
|
|
14
|
+
- navigate: https://linux.do
|
|
15
|
+
|
|
16
|
+
- evaluate: |
|
|
17
|
+
(async () => {
|
|
18
|
+
const res = await fetch('/latest.json', { credentials: 'include' });
|
|
19
|
+
if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
|
|
20
|
+
let data;
|
|
21
|
+
try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
|
|
22
|
+
const topics = data?.topic_list?.topics || [];
|
|
23
|
+
return topics.slice(0, ${{ args.limit }}).map(t => ({
|
|
24
|
+
title: t.title,
|
|
25
|
+
replies: (t.posts_count || 1) - 1,
|
|
26
|
+
views: t.views,
|
|
27
|
+
likes: t.like_count,
|
|
28
|
+
}));
|
|
29
|
+
})()
|
|
30
|
+
|
|
31
|
+
- map:
|
|
32
|
+
rank: ${{ index + 1 }}
|
|
33
|
+
title: ${{ item.title }}
|
|
34
|
+
replies: ${{ item.replies }}
|
|
35
|
+
views: ${{ item.views }}
|
|
36
|
+
likes: ${{ item.likes }}
|
|
37
|
+
|
|
38
|
+
- limit: ${{ args.limit }}
|
|
39
|
+
|
|
40
|
+
columns: [rank, title, replies, views, likes]
|