@jackwener/opencli 0.9.6 → 1.0.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/.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/CDP.md +1 -1
- package/CDP.zh-CN.md +1 -1
- package/CLI-ELECTRON.md +89 -36
- package/CLI-EXPLORER.md +4 -4
- package/CONTRIBUTING.md +167 -0
- package/README.md +113 -89
- package/README.zh-CN.md +114 -91
- package/SKILL.md +10 -8
- package/TESTING.md +7 -7
- package/dist/browser/daemon-client.d.ts +37 -0
- package/dist/browser/daemon-client.js +82 -0
- package/dist/browser/discover.d.ts +11 -34
- package/dist/browser/discover.js +15 -190
- package/dist/browser/errors.d.ts +6 -20
- package/dist/browser/errors.js +24 -63
- package/dist/browser/index.d.ts +2 -11
- package/dist/browser/index.js +5 -11
- package/dist/browser/mcp.d.ts +9 -18
- package/dist/browser/mcp.js +70 -284
- package/dist/browser/page.d.ts +28 -6
- package/dist/browser/page.js +210 -85
- package/dist/browser.test.js +4 -202
- 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 +1582 -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/neteasemusic/like.d.ts +1 -0
- package/dist/clis/neteasemusic/like.js +25 -0
- package/dist/clis/neteasemusic/lyrics.d.ts +1 -0
- package/dist/clis/neteasemusic/lyrics.js +47 -0
- package/dist/clis/neteasemusic/next.d.ts +1 -0
- package/dist/clis/neteasemusic/next.js +26 -0
- package/dist/clis/neteasemusic/play.d.ts +1 -0
- package/dist/clis/neteasemusic/play.js +26 -0
- package/dist/clis/neteasemusic/playing.d.ts +1 -0
- package/dist/clis/neteasemusic/playing.js +59 -0
- package/dist/clis/neteasemusic/playlist.d.ts +1 -0
- package/dist/clis/neteasemusic/playlist.js +46 -0
- package/dist/clis/neteasemusic/prev.d.ts +1 -0
- package/dist/clis/neteasemusic/prev.js +25 -0
- package/dist/clis/neteasemusic/search.d.ts +1 -0
- package/dist/clis/neteasemusic/search.js +52 -0
- package/dist/clis/neteasemusic/status.d.ts +1 -0
- package/dist/clis/neteasemusic/status.js +16 -0
- package/dist/clis/neteasemusic/volume.d.ts +1 -0
- package/dist/clis/neteasemusic/volume.js +54 -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/daemon.d.ts +13 -0
- package/dist/daemon.js +187 -0
- package/dist/doctor.d.ts +27 -61
- package/dist/doctor.js +70 -601
- package/dist/doctor.test.js +30 -170
- 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 +22 -28
- package/dist/pipeline/executor.test.js +1 -0
- package/dist/pipeline/registry.js +2 -0
- package/dist/pipeline/steps/browser.js +2 -2
- package/dist/pipeline/steps/download.d.ts +34 -0
- package/dist/pipeline/steps/download.js +251 -0
- package/dist/pipeline/steps/intercept.js +1 -2
- package/dist/pipeline/template.js +28 -0
- package/dist/setup.d.ts +6 -0
- package/dist/setup.js +46 -160
- package/dist/types.d.ts +6 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-32.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/manifest.json +31 -0
- package/extension/package.json +16 -0
- package/extension/src/background.ts +293 -0
- package/extension/src/cdp.ts +125 -0
- package/extension/src/protocol.ts +57 -0
- package/extension/store-assets/screenshot-1280x800.png +0 -0
- package/extension/tsconfig.json +15 -0
- package/extension/vite.config.ts +18 -0
- package/package.json +8 -7
- package/scripts/test-site.mjs +70 -0
- package/src/browser/daemon-client.ts +113 -0
- package/src/browser/discover.ts +18 -216
- package/src/browser/errors.ts +30 -100
- package/src/browser/index.ts +6 -12
- package/src/browser/mcp.ts +78 -278
- package/src/browser/page.ts +222 -88
- package/src/browser.test.ts +3 -210
- 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/chatgpt/README.md +1 -1
- package/src/clis/chatgpt/README.zh-CN.md +1 -1
- 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/neteasemusic/README.md +31 -0
- package/src/clis/neteasemusic/README.zh-CN.md +31 -0
- package/src/clis/neteasemusic/like.ts +28 -0
- package/src/clis/neteasemusic/lyrics.ts +53 -0
- package/src/clis/neteasemusic/next.ts +30 -0
- package/src/clis/neteasemusic/play.ts +30 -0
- package/src/clis/neteasemusic/playing.ts +62 -0
- package/src/clis/neteasemusic/playlist.ts +51 -0
- package/src/clis/neteasemusic/prev.ts +29 -0
- package/src/clis/neteasemusic/search.ts +58 -0
- package/src/clis/neteasemusic/status.ts +18 -0
- package/src/clis/neteasemusic/volume.ts +61 -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/daemon.ts +217 -0
- package/src/doctor.test.ts +32 -193
- package/src/doctor.ts +74 -668
- 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 +18 -26
- package/src/pipeline/executor.test.ts +1 -0
- package/src/pipeline/registry.ts +2 -0
- package/src/pipeline/steps/browser.ts +2 -2
- package/src/pipeline/steps/download.ts +310 -0
- package/src/pipeline/steps/intercept.ts +1 -2
- package/src/pipeline/template.ts +26 -0
- package/src/setup.ts +47 -183
- package/src/types.ts +1 -0
- package/tests/e2e/browser-auth.test.ts +25 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const membersCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'members',
|
|
5
|
+
description: 'List online members in the current Discord channel',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['Index', 'Name', 'Status'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
const members = await page.evaluate(`
|
|
13
|
+
(function() {
|
|
14
|
+
const results = [];
|
|
15
|
+
// Discord member list sidebar
|
|
16
|
+
const items = document.querySelectorAll('[class*="member_"], [data-list-item-id*="members"]');
|
|
17
|
+
|
|
18
|
+
items.forEach((item, i) => {
|
|
19
|
+
const nameEl = item.querySelector('[class*="username_"], [class*="nameTag"]');
|
|
20
|
+
const statusEl = item.querySelector('[class*="activity"], [class*="customStatus"]');
|
|
21
|
+
|
|
22
|
+
const name = nameEl ? nameEl.textContent.trim() : (item.textContent || '').trim().substring(0, 50);
|
|
23
|
+
const status = statusEl ? statusEl.textContent.trim() : '';
|
|
24
|
+
|
|
25
|
+
if (name && name.length > 0) {
|
|
26
|
+
results.push({ Index: i + 1, Name: name, Status: status || 'Online' });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return results.slice(0, 50); // Limit to 50
|
|
31
|
+
})()
|
|
32
|
+
`);
|
|
33
|
+
if (members.length === 0) {
|
|
34
|
+
return [{ Index: 0, Name: 'No members visible', Status: 'Toggle member list first' }];
|
|
35
|
+
}
|
|
36
|
+
return members;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const readCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'read',
|
|
5
|
+
description: 'Read recent messages from the active Discord channel',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'count', required: false, help: 'Number of messages to read (default: 20)', default: '20' },
|
|
11
|
+
],
|
|
12
|
+
columns: ['Author', 'Time', 'Message'],
|
|
13
|
+
func: async (page, kwargs) => {
|
|
14
|
+
const count = parseInt(kwargs.count, 10) || 20;
|
|
15
|
+
const messages = await page.evaluate(`
|
|
16
|
+
(function(limit) {
|
|
17
|
+
const results = [];
|
|
18
|
+
// Discord renders messages in list items with id starting with "chat-messages-"
|
|
19
|
+
const msgNodes = document.querySelectorAll('[id^="chat-messages-"] > div, [class*="messageListItem"]');
|
|
20
|
+
|
|
21
|
+
const slice = Array.from(msgNodes).slice(-limit);
|
|
22
|
+
|
|
23
|
+
slice.forEach(node => {
|
|
24
|
+
const authorEl = node.querySelector('[class*="username"], [class*="headerText"] span');
|
|
25
|
+
const timeEl = node.querySelector('time');
|
|
26
|
+
const contentEl = node.querySelector('[id^="message-content-"], [class*="messageContent"]');
|
|
27
|
+
|
|
28
|
+
if (contentEl) {
|
|
29
|
+
results.push({
|
|
30
|
+
Author: authorEl ? authorEl.textContent.trim() : '—',
|
|
31
|
+
Time: timeEl ? timeEl.getAttribute('datetime') || timeEl.textContent.trim() : '',
|
|
32
|
+
Message: (contentEl.textContent || '').trim().substring(0, 300),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return results;
|
|
38
|
+
})(${count})
|
|
39
|
+
`);
|
|
40
|
+
if (messages.length === 0) {
|
|
41
|
+
return [{ Author: 'System', Time: '', Message: 'No messages found in the current channel.' }];
|
|
42
|
+
}
|
|
43
|
+
return messages;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const searchCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const searchCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'search',
|
|
5
|
+
description: 'Search messages in the current Discord server/channel (Cmd+F)',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
|
10
|
+
columns: ['Index', 'Author', 'Message'],
|
|
11
|
+
func: async (page, kwargs) => {
|
|
12
|
+
const query = kwargs.query;
|
|
13
|
+
// Open search with Cmd+F
|
|
14
|
+
const isMac = process.platform === 'darwin';
|
|
15
|
+
await page.pressKey(isMac ? 'Meta+F' : 'Control+F');
|
|
16
|
+
await page.wait(0.5);
|
|
17
|
+
// Type query into search box
|
|
18
|
+
await page.evaluate(`
|
|
19
|
+
(function(q) {
|
|
20
|
+
const input = document.querySelector('[aria-label*="Search"], [class*="searchBar"] input, [placeholder*="Search"]');
|
|
21
|
+
if (!input) throw new Error('Search input not found');
|
|
22
|
+
input.focus();
|
|
23
|
+
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
|
|
24
|
+
setter.call(input, q);
|
|
25
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
26
|
+
})(${JSON.stringify(query)})
|
|
27
|
+
`);
|
|
28
|
+
await page.pressKey('Enter');
|
|
29
|
+
await page.wait(2);
|
|
30
|
+
// Scrape search results
|
|
31
|
+
const results = await page.evaluate(`
|
|
32
|
+
(function() {
|
|
33
|
+
const items = [];
|
|
34
|
+
const resultNodes = document.querySelectorAll('[class*="searchResult_"], [id*="search-result"]');
|
|
35
|
+
|
|
36
|
+
resultNodes.forEach((node, i) => {
|
|
37
|
+
const author = node.querySelector('[class*="username"]')?.textContent?.trim() || '—';
|
|
38
|
+
const content = node.querySelector('[id^="message-content-"], [class*="messageContent"]')?.textContent?.trim() || node.textContent?.trim();
|
|
39
|
+
items.push({
|
|
40
|
+
Index: i + 1,
|
|
41
|
+
Author: author,
|
|
42
|
+
Message: (content || '').substring(0, 200),
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return items;
|
|
47
|
+
})()
|
|
48
|
+
`);
|
|
49
|
+
// Close search
|
|
50
|
+
await page.pressKey('Escape');
|
|
51
|
+
if (results.length === 0) {
|
|
52
|
+
return [{ Index: 0, Author: 'System', Message: `No results for "${query}"` }];
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sendCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const sendCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'send',
|
|
5
|
+
description: 'Send a message in the active Discord channel',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
|
|
10
|
+
columns: ['Status'],
|
|
11
|
+
func: async (page, kwargs) => {
|
|
12
|
+
const text = kwargs.text;
|
|
13
|
+
await page.evaluate(`
|
|
14
|
+
(function(text) {
|
|
15
|
+
// Discord uses a Slate-based editor with [data-slate-editor="true"] or role="textbox"
|
|
16
|
+
const editor = document.querySelector('[role="textbox"][data-slate-editor="true"], [class*="slateTextArea"]');
|
|
17
|
+
if (!editor) throw new Error('Could not find Discord message input. Make sure a channel is open.');
|
|
18
|
+
|
|
19
|
+
editor.focus();
|
|
20
|
+
document.execCommand('insertText', false, text);
|
|
21
|
+
})(${JSON.stringify(text)})
|
|
22
|
+
`);
|
|
23
|
+
await page.wait(0.3);
|
|
24
|
+
await page.pressKey('Enter');
|
|
25
|
+
return [{ Status: 'Success' }];
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const serversCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const serversCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'servers',
|
|
5
|
+
description: 'List all Discord servers (guilds) in the sidebar',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['Index', 'Server'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
const servers = await page.evaluate(`
|
|
13
|
+
(function() {
|
|
14
|
+
const results = [];
|
|
15
|
+
// Discord guild icons in the sidebar
|
|
16
|
+
const items = document.querySelectorAll('[data-list-item-id*="guildsnav___"], [class*="listItem_"]');
|
|
17
|
+
|
|
18
|
+
items.forEach((item, i) => {
|
|
19
|
+
const nameAttr = item.querySelector('[data-dnd-name]');
|
|
20
|
+
const ariaLabel = item.getAttribute('aria-label') || (item.querySelector('[aria-label]') || {}).getAttribute?.('aria-label');
|
|
21
|
+
const name = nameAttr ? nameAttr.getAttribute('data-dnd-name') : (ariaLabel || (item.textContent || '').trim());
|
|
22
|
+
|
|
23
|
+
if (name && name.length > 0) {
|
|
24
|
+
results.push({ Index: i + 1, Server: name.substring(0, 80) });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return results;
|
|
29
|
+
})()
|
|
30
|
+
`);
|
|
31
|
+
if (servers.length === 0) {
|
|
32
|
+
return [{ Index: 0, Server: 'No servers found' }];
|
|
33
|
+
}
|
|
34
|
+
return servers;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const statusCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const statusCommand = cli({
|
|
3
|
+
site: 'discord-app',
|
|
4
|
+
name: 'status',
|
|
5
|
+
description: 'Check active CDP connection to Discord Desktop',
|
|
6
|
+
domain: 'localhost',
|
|
7
|
+
strategy: Strategy.UI,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [],
|
|
10
|
+
columns: ['Status', 'Url', 'Title'],
|
|
11
|
+
func: async (page) => {
|
|
12
|
+
const url = await page.evaluate('window.location.href');
|
|
13
|
+
const title = await page.evaluate('document.title');
|
|
14
|
+
return [{ Status: 'Connected', Url: url, Title: title }];
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const newCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const newCommand = cli({
|
|
4
|
+
site: 'feishu',
|
|
5
|
+
name: 'new',
|
|
6
|
+
description: 'Create a new message or document in Feishu',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [],
|
|
11
|
+
columns: ['Status'],
|
|
12
|
+
func: async (page) => {
|
|
13
|
+
try {
|
|
14
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
15
|
+
execSync("osascript -e 'delay 0.3'");
|
|
16
|
+
// Cmd+N for new conversation/document
|
|
17
|
+
execSync("osascript " +
|
|
18
|
+
"-e 'tell application \"System Events\"' " +
|
|
19
|
+
"-e 'keystroke \"n\" using command down' " +
|
|
20
|
+
"-e 'end tell'");
|
|
21
|
+
return [{ Status: 'New item dialog opened (Cmd+N)' }];
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const readCommand = cli({
|
|
4
|
+
site: 'feishu',
|
|
5
|
+
name: 'read',
|
|
6
|
+
description: 'Read the current chat content by selecting all and copying',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [],
|
|
11
|
+
columns: ['Content'],
|
|
12
|
+
func: async (page) => {
|
|
13
|
+
try {
|
|
14
|
+
let clipBackup = '';
|
|
15
|
+
try {
|
|
16
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
17
|
+
}
|
|
18
|
+
catch { /* empty */ }
|
|
19
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
20
|
+
execSync("osascript -e 'delay 0.3'");
|
|
21
|
+
execSync("osascript " +
|
|
22
|
+
"-e 'tell application \"System Events\"' " +
|
|
23
|
+
"-e 'keystroke \"a\" using command down' " +
|
|
24
|
+
"-e 'delay 0.2' " +
|
|
25
|
+
"-e 'keystroke \"c\" using command down' " +
|
|
26
|
+
"-e 'delay 0.2' " +
|
|
27
|
+
"-e 'end tell'");
|
|
28
|
+
const content = execSync('pbpaste', { encoding: 'utf-8' }).trim();
|
|
29
|
+
if (clipBackup) {
|
|
30
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
31
|
+
}
|
|
32
|
+
// Deselect
|
|
33
|
+
execSync("osascript -e 'tell application \"System Events\" to key code 53'");
|
|
34
|
+
return [{ Content: content || '(no content captured)' }];
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return [{ Content: 'Error: ' + err.message }];
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const searchCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const searchCommand = cli({
|
|
4
|
+
site: 'feishu',
|
|
5
|
+
name: 'search',
|
|
6
|
+
description: 'Open Feishu global search and type a query (Cmd+K)',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
|
11
|
+
columns: ['Status'],
|
|
12
|
+
func: async (page, kwargs) => {
|
|
13
|
+
const query = kwargs.query;
|
|
14
|
+
try {
|
|
15
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
16
|
+
execSync("osascript -e 'delay 0.3'");
|
|
17
|
+
// Feishu uses Cmd+K for global search (similar to Slack/Notion)
|
|
18
|
+
execSync("osascript " +
|
|
19
|
+
"-e 'tell application \"System Events\"' " +
|
|
20
|
+
"-e 'keystroke \"k\" using command down' " +
|
|
21
|
+
"-e 'delay 0.5' " +
|
|
22
|
+
`-e 'keystroke ${JSON.stringify(query)}' ` +
|
|
23
|
+
"-e 'end tell'");
|
|
24
|
+
return [{ Status: `Searching for: ${query}` }];
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sendCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const sendCommand = cli({
|
|
4
|
+
site: 'feishu',
|
|
5
|
+
name: 'send',
|
|
6
|
+
description: 'Send a message in the active Feishu (Lark) conversation',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [{ name: 'text', required: true, positional: true, help: 'Message to send' }],
|
|
11
|
+
columns: ['Status'],
|
|
12
|
+
func: async (page, kwargs) => {
|
|
13
|
+
const text = kwargs.text;
|
|
14
|
+
try {
|
|
15
|
+
// Backup clipboard
|
|
16
|
+
let clipBackup = '';
|
|
17
|
+
try {
|
|
18
|
+
clipBackup = execSync('pbpaste', { encoding: 'utf-8' });
|
|
19
|
+
}
|
|
20
|
+
catch { /* empty */ }
|
|
21
|
+
spawnSync('pbcopy', { input: text });
|
|
22
|
+
execSync("osascript -e 'tell application \"Lark\" to activate'");
|
|
23
|
+
execSync("osascript -e 'delay 0.5'");
|
|
24
|
+
execSync("osascript " +
|
|
25
|
+
"-e 'tell application \"System Events\"' " +
|
|
26
|
+
"-e 'keystroke \"v\" using command down' " +
|
|
27
|
+
"-e 'delay 0.2' " +
|
|
28
|
+
"-e 'keystroke return' " +
|
|
29
|
+
"-e 'end tell'");
|
|
30
|
+
if (clipBackup) {
|
|
31
|
+
spawnSync('pbcopy', { input: clipBackup });
|
|
32
|
+
}
|
|
33
|
+
return [{ Status: 'Success' }];
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
return [{ Status: 'Error: ' + err.message }];
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const statusCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
export const statusCommand = cli({
|
|
4
|
+
site: 'feishu',
|
|
5
|
+
name: 'status',
|
|
6
|
+
description: 'Check if Feishu (Lark) Desktop is running on macOS',
|
|
7
|
+
domain: 'localhost',
|
|
8
|
+
strategy: Strategy.PUBLIC,
|
|
9
|
+
browser: false,
|
|
10
|
+
args: [],
|
|
11
|
+
columns: ['Status', 'Detail'],
|
|
12
|
+
func: async (page) => {
|
|
13
|
+
try {
|
|
14
|
+
const running = execSync("osascript -e 'application \"Lark\" is running'", { encoding: 'utf-8' }).trim();
|
|
15
|
+
if (running !== 'true') {
|
|
16
|
+
return [{ Status: 'Stopped', Detail: 'Feishu/Lark is not running' }];
|
|
17
|
+
}
|
|
18
|
+
const windowCount = execSync("osascript -e 'tell application \"System Events\" to count windows of application process \"Lark\"'", { encoding: 'utf-8' }).trim();
|
|
19
|
+
return [{
|
|
20
|
+
Status: 'Running',
|
|
21
|
+
Detail: `${windowCount} window(s) open`,
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return [{ Status: 'Error', Detail: err.message }];
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const askCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const askCommand = cli({
|
|
3
|
+
site: 'grok',
|
|
4
|
+
name: 'ask',
|
|
5
|
+
description: 'Send a message to Grok and get response',
|
|
6
|
+
domain: 'grok.com',
|
|
7
|
+
strategy: Strategy.COOKIE,
|
|
8
|
+
browser: true,
|
|
9
|
+
args: [
|
|
10
|
+
{ name: 'prompt', type: 'string', required: true },
|
|
11
|
+
{ name: 'timeout', type: 'int', default: 120 },
|
|
12
|
+
{ name: 'new', type: 'boolean', default: false },
|
|
13
|
+
],
|
|
14
|
+
columns: ['response'],
|
|
15
|
+
func: async (page, kwargs) => {
|
|
16
|
+
const prompt = kwargs.prompt;
|
|
17
|
+
const timeoutMs = (kwargs.timeout || 120) * 1000;
|
|
18
|
+
const newChat = kwargs.new;
|
|
19
|
+
if (newChat) {
|
|
20
|
+
await page.goto('https://grok.com');
|
|
21
|
+
await page.wait(2);
|
|
22
|
+
await page.evaluate(`(() => {
|
|
23
|
+
const btn = [...document.querySelectorAll('a, button')].find(b => {
|
|
24
|
+
const t = (b.textContent || '').trim().toLowerCase();
|
|
25
|
+
return t.includes('new') || b.getAttribute('href') === '/';
|
|
26
|
+
});
|
|
27
|
+
if (btn) btn.click();
|
|
28
|
+
})()`);
|
|
29
|
+
await page.wait(2);
|
|
30
|
+
}
|
|
31
|
+
await page.goto('https://grok.com');
|
|
32
|
+
await page.wait(3);
|
|
33
|
+
const promptJson = JSON.stringify(prompt);
|
|
34
|
+
const sendResult = await page.evaluate(`(async () => {
|
|
35
|
+
try {
|
|
36
|
+
const box = document.querySelector('textarea');
|
|
37
|
+
if (!box) return { ok: false, msg: 'no textarea' };
|
|
38
|
+
box.focus(); box.value = '';
|
|
39
|
+
document.execCommand('selectAll');
|
|
40
|
+
document.execCommand('insertText', false, ${promptJson});
|
|
41
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
42
|
+
const btn = document.querySelector('button[aria-label="\\u63d0\\u4ea4"]');
|
|
43
|
+
if (btn && !btn.disabled) { btn.click(); return { ok: true, msg: 'clicked' }; }
|
|
44
|
+
const sub = [...document.querySelectorAll('button[type="submit"]')].find(b => !b.disabled);
|
|
45
|
+
if (sub) { sub.click(); return { ok: true, msg: 'clicked-submit' }; }
|
|
46
|
+
box.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true }));
|
|
47
|
+
return { ok: true, msg: 'enter' };
|
|
48
|
+
} catch (e) { return { ok: false, msg: e.toString() }; }
|
|
49
|
+
})()`);
|
|
50
|
+
if (!sendResult || !sendResult.ok) {
|
|
51
|
+
return [{ response: '[SEND FAILED] ' + JSON.stringify(sendResult) }];
|
|
52
|
+
}
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
let lastText = '';
|
|
55
|
+
let stableCount = 0;
|
|
56
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
57
|
+
await page.wait(3);
|
|
58
|
+
const response = await page.evaluate(`(() => {
|
|
59
|
+
const bubbles = document.querySelectorAll('div.message-bubble, [data-testid="message-bubble"]');
|
|
60
|
+
if (bubbles.length < 2) return '';
|
|
61
|
+
const last = bubbles[bubbles.length - 1];
|
|
62
|
+
const text = (last.innerText || '').trim();
|
|
63
|
+
if (!text || text.length < 2) return '';
|
|
64
|
+
return text;
|
|
65
|
+
})()`);
|
|
66
|
+
if (response && response.length > 2) {
|
|
67
|
+
if (response === lastText) {
|
|
68
|
+
stableCount++;
|
|
69
|
+
if (stableCount >= 2)
|
|
70
|
+
return [{ response }];
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
stableCount = 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
lastText = response || '';
|
|
77
|
+
}
|
|
78
|
+
if (lastText)
|
|
79
|
+
return [{ response: lastText }];
|
|
80
|
+
return [{ response: '[NO RESPONSE]' }];
|
|
81
|
+
},
|
|
82
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const debugCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
export const debugCommand = cli({
|
|
3
|
+
site: 'grok',
|
|
4
|
+
name: 'debug',
|
|
5
|
+
description: 'Debug grok page structure',
|
|
6
|
+
domain: 'grok.com',
|
|
7
|
+
strategy: Strategy.COOKIE,
|
|
8
|
+
browser: true,
|
|
9
|
+
columns: ['data'],
|
|
10
|
+
func: async (page, _kwargs) => {
|
|
11
|
+
await page.goto('https://grok.com');
|
|
12
|
+
await page.wait(3);
|
|
13
|
+
// Get all button-like elements near textarea
|
|
14
|
+
const debug = await page.evaluate(`(() => {
|
|
15
|
+
const ta = document.querySelector('textarea');
|
|
16
|
+
if (!ta) return { error: 'no textarea' };
|
|
17
|
+
|
|
18
|
+
// Get parent containers
|
|
19
|
+
let parent = ta.parentElement;
|
|
20
|
+
const parents = [];
|
|
21
|
+
for (let i = 0; i < 5 && parent; i++) {
|
|
22
|
+
parents.push({
|
|
23
|
+
tag: parent.tagName,
|
|
24
|
+
class: parent.className?.substring(0, 80),
|
|
25
|
+
childCount: parent.children.length,
|
|
26
|
+
});
|
|
27
|
+
parent = parent.parentElement;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Find buttons in the form/container near textarea
|
|
31
|
+
const form = ta.closest('form') || ta.closest('[class*="composer"]') || ta.closest('[class*="input"]') || ta.parentElement?.parentElement;
|
|
32
|
+
const buttons = form ? [...form.querySelectorAll('button')].map(b => ({
|
|
33
|
+
testid: b.getAttribute('data-testid'),
|
|
34
|
+
type: b.type,
|
|
35
|
+
disabled: b.disabled,
|
|
36
|
+
text: (b.textContent || '').substring(0, 30),
|
|
37
|
+
html: b.outerHTML.substring(0, 200),
|
|
38
|
+
rect: b.getBoundingClientRect().toJSON(),
|
|
39
|
+
})) : [];
|
|
40
|
+
|
|
41
|
+
return { parents, buttons, formTag: form?.tagName, formClass: form?.className?.substring(0, 80) };
|
|
42
|
+
})()`);
|
|
43
|
+
return [{ data: JSON.stringify(debug, null, 2) }];
|
|
44
|
+
},
|
|
45
|
+
});
|
|
@@ -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 }}
|