@jackwener/opencli 0.9.5 → 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 +1875 -271
- package/dist/clis/antigravity/model.js +2 -2
- package/dist/clis/antigravity/send.js +2 -2
- package/dist/clis/bilibili/download.d.ts +10 -0
- package/dist/clis/bilibili/download.js +135 -0
- package/dist/clis/chatgpt/ask.d.ts +1 -0
- package/dist/clis/chatgpt/ask.js +68 -0
- package/dist/clis/chatgpt/send.js +11 -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/codex/ask.d.ts +1 -0
- package/dist/clis/codex/ask.js +67 -0
- package/dist/clis/codex/export.d.ts +1 -0
- package/dist/clis/codex/export.js +37 -0
- package/dist/clis/codex/history.d.ts +1 -0
- package/dist/clis/codex/history.js +43 -0
- package/dist/clis/codex/read.js +3 -5
- package/dist/clis/codex/screenshot.d.ts +1 -0
- package/dist/clis/codex/screenshot.js +27 -0
- package/dist/clis/codex/send.js +3 -6
- package/dist/clis/codex/status.js +2 -1
- package/dist/clis/cursor/ask.d.ts +1 -0
- package/dist/clis/cursor/ask.js +69 -0
- package/dist/clis/cursor/composer.js +9 -28
- package/dist/clis/cursor/export.d.ts +1 -0
- package/dist/clis/cursor/export.js +51 -0
- package/dist/clis/cursor/history.d.ts +1 -0
- package/dist/clis/cursor/history.js +43 -0
- package/dist/clis/cursor/new.js +4 -13
- package/dist/clis/cursor/screenshot.d.ts +1 -0
- package/dist/clis/cursor/screenshot.js +31 -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/antigravity/README.md +2 -3
- package/src/clis/antigravity/README.zh-CN.md +2 -3
- package/src/clis/antigravity/SKILL.md +1 -1
- package/src/clis/antigravity/model.ts +2 -2
- package/src/clis/antigravity/send.ts +2 -2
- package/src/clis/bilibili/download.ts +161 -0
- package/src/clis/chatgpt/README.md +25 -16
- package/src/clis/chatgpt/README.zh-CN.md +27 -18
- package/src/clis/chatgpt/ask.ts +77 -0
- package/src/clis/chatgpt/send.ts +12 -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/codex/ask.ts +77 -0
- package/src/clis/codex/export.ts +42 -0
- package/src/clis/codex/extract-diff.ts +1 -0
- package/src/clis/codex/history.ts +47 -0
- package/src/clis/codex/read.ts +5 -6
- package/src/clis/codex/screenshot.ts +33 -0
- package/src/clis/codex/send.ts +6 -7
- package/src/clis/codex/status.ts +4 -2
- package/src/clis/cursor/ask.ts +81 -0
- package/src/clis/cursor/composer.ts +9 -30
- package/src/clis/cursor/export.ts +57 -0
- package/src/clis/cursor/history.ts +47 -0
- package/src/clis/cursor/new.ts +4 -15
- package/src/clis/cursor/screenshot.ts +38 -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,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]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: search
|
|
3
|
+
description: 搜索 linux.do
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
keyword:
|
|
9
|
+
type: str
|
|
10
|
+
required: true
|
|
11
|
+
description: Search keyword
|
|
12
|
+
limit:
|
|
13
|
+
type: int
|
|
14
|
+
default: 20
|
|
15
|
+
description: Number of results
|
|
16
|
+
|
|
17
|
+
pipeline:
|
|
18
|
+
- navigate: https://linux.do
|
|
19
|
+
|
|
20
|
+
- evaluate: |
|
|
21
|
+
(async () => {
|
|
22
|
+
const keyword = ${{ args.keyword | json }};
|
|
23
|
+
const res = await fetch('/search.json?q=' + encodeURIComponent(keyword), { credentials: 'include' });
|
|
24
|
+
if (!res.ok) throw new Error('HTTP ' + res.status + ' - 请先登录 linux.do');
|
|
25
|
+
let data;
|
|
26
|
+
try { data = await res.json(); } catch { throw new Error('响应不是有效 JSON - 请先登录 linux.do'); }
|
|
27
|
+
const topics = data?.topics || [];
|
|
28
|
+
return topics.slice(0, ${{ args.limit }}).map(t => ({
|
|
29
|
+
title: t.title,
|
|
30
|
+
views: t.views,
|
|
31
|
+
likes: t.like_count,
|
|
32
|
+
replies: (t.posts_count || 1) - 1,
|
|
33
|
+
}));
|
|
34
|
+
})()
|
|
35
|
+
|
|
36
|
+
- map:
|
|
37
|
+
rank: ${{ index + 1 }}
|
|
38
|
+
title: ${{ item.title }}
|
|
39
|
+
views: ${{ item.views }}
|
|
40
|
+
likes: ${{ item.likes }}
|
|
41
|
+
replies: ${{ item.replies }}
|
|
42
|
+
|
|
43
|
+
- limit: ${{ args.limit }}
|
|
44
|
+
|
|
45
|
+
columns: [rank, title, views, likes, replies]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
site: linux-do
|
|
2
|
+
name: topic
|
|
3
|
+
description: linux.do 帖子详情和回复(首页)
|
|
4
|
+
domain: linux.do
|
|
5
|
+
browser: true
|
|
6
|
+
|
|
7
|
+
args:
|
|
8
|
+
id:
|
|
9
|
+
type: int
|
|
10
|
+
required: true
|
|
11
|
+
description: Topic ID
|
|
12
|
+
|
|
13
|
+
pipeline:
|
|
14
|
+
- navigate: https://linux.do
|
|
15
|
+
|
|
16
|
+
- evaluate: |
|
|
17
|
+
(async () => {
|
|
18
|
+
const res = await fetch('/t/${{ args.id }}.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 strip = (html) => (html || '').replace(/<br\s*\/?>/gi, ' ').replace(/<\/(p|div|li|blockquote|h[1-6])>/gi, ' ').replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/&#(?:(\d+)|x([0-9a-fA-F]+));/g, (_, dec, hex) => { try { return String.fromCodePoint(dec !== undefined ? Number(dec) : parseInt(hex, 16)); } catch { return ''; } }).replace(/\s+/g, ' ').trim();
|
|
23
|
+
const posts = data?.post_stream?.posts || [];
|
|
24
|
+
return posts.map(p => ({
|
|
25
|
+
author: p.username,
|
|
26
|
+
content: strip(p.cooked).slice(0, 200),
|
|
27
|
+
likes: p.like_count,
|
|
28
|
+
created_at: p.created_at,
|
|
29
|
+
}));
|
|
30
|
+
})()
|
|
31
|
+
|
|
32
|
+
- map:
|
|
33
|
+
author: ${{ item.author }}
|
|
34
|
+
content: ${{ item.content }}
|
|
35
|
+
likes: ${{ item.likes }}
|
|
36
|
+
created_at: ${{ item.created_at }}
|
|
37
|
+
|
|
38
|
+
columns: [author, content, likes, created_at]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Notion Desktop Adapter
|
|
2
|
+
|
|
3
|
+
Control the **Notion Desktop App** from the terminal via Chrome DevTools Protocol (CDP).
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Launch with remote debugging port:
|
|
8
|
+
```bash
|
|
9
|
+
/Applications/Notion.app/Contents/MacOS/Notion --remote-debugging-port=9230
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9230"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
| Command | Description |
|
|
21
|
+
|---------|-------------|
|
|
22
|
+
| `notion status` | Check CDP connection |
|
|
23
|
+
| `notion search "query"` | Quick Find search (Cmd+P) |
|
|
24
|
+
| `notion read` | Read the current page content |
|
|
25
|
+
| `notion new "title"` | Create a new page (Cmd+N) |
|
|
26
|
+
| `notion write "text"` | Append text to the current page |
|
|
27
|
+
| `notion sidebar` | List pages from the sidebar |
|
|
28
|
+
| `notion favorites` | List pages from the Favorites section |
|
|
29
|
+
| `notion export` | Export page as Markdown |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Notion 桌面端适配器
|
|
2
|
+
|
|
3
|
+
通过 Chrome DevTools Protocol (CDP) 在终端中控制 **Notion 桌面应用**。
|
|
4
|
+
|
|
5
|
+
## 前置条件
|
|
6
|
+
|
|
7
|
+
通过远程调试端口启动:
|
|
8
|
+
```bash
|
|
9
|
+
/Applications/Notion.app/Contents/MacOS/Notion --remote-debugging-port=9230
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 配置
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
export OPENCLI_CDP_ENDPOINT="http://127.0.0.1:9230"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 命令
|
|
19
|
+
|
|
20
|
+
| 命令 | 说明 |
|
|
21
|
+
|------|------|
|
|
22
|
+
| `notion status` | 检查 CDP 连接 |
|
|
23
|
+
| `notion search "关键词"` | 快速搜索(Cmd+P) |
|
|
24
|
+
| `notion read` | 读取当前页面内容 |
|
|
25
|
+
| `notion new "标题"` | 新建页面(Cmd+N) |
|
|
26
|
+
| `notion write "文本"` | 在当前页面追加文字 |
|
|
27
|
+
| `notion sidebar` | 列出侧边栏页面列表 |
|
|
28
|
+
| `notion favorites` | 列出收藏夹页面列表 |
|
|
29
|
+
| `notion export` | 导出页面为 Markdown |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { cli, Strategy } from '../../registry.js';
|
|
3
|
+
import type { IPage } from '../../types.js';
|
|
4
|
+
|
|
5
|
+
export const exportCommand = cli({
|
|
6
|
+
site: 'notion',
|
|
7
|
+
name: 'export',
|
|
8
|
+
description: 'Export the current Notion page as Markdown',
|
|
9
|
+
domain: 'localhost',
|
|
10
|
+
strategy: Strategy.UI,
|
|
11
|
+
browser: true,
|
|
12
|
+
args: [
|
|
13
|
+
{ name: 'output', required: false, positional: true, help: 'Output file (default: /tmp/notion-export.md)' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['Status', 'File'],
|
|
16
|
+
func: async (page: IPage, kwargs: any) => {
|
|
17
|
+
const outputPath = (kwargs.output as string) || '/tmp/notion-export.md';
|
|
18
|
+
|
|
19
|
+
const result = await page.evaluate(`
|
|
20
|
+
(function() {
|
|
21
|
+
const titleEl = document.querySelector('[data-block-id] [placeholder="Untitled"], h1.notion-title, [class*="title"]');
|
|
22
|
+
const title = titleEl ? (titleEl.textContent || '').trim() : document.title;
|
|
23
|
+
|
|
24
|
+
const frame = document.querySelector('.notion-page-content, [class*="page-content"], main');
|
|
25
|
+
const content = frame ? (frame.innerText || '').trim() : document.body.innerText;
|
|
26
|
+
|
|
27
|
+
return { title, content };
|
|
28
|
+
})()
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
const md = `# ${result.title}\n\n${result.content}`;
|
|
32
|
+
fs.writeFileSync(outputPath, md);
|
|
33
|
+
|
|
34
|
+
return [{ Status: 'Success', File: outputPath }];
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const favoritesCommand = cli({
|
|
5
|
+
site: 'notion',
|
|
6
|
+
name: 'favorites',
|
|
7
|
+
description: 'List pages from the Notion Favorites section in the sidebar',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [],
|
|
12
|
+
columns: ['Index', 'Title', 'Icon'],
|
|
13
|
+
func: async (page: IPage) => {
|
|
14
|
+
const items = await page.evaluate(`
|
|
15
|
+
(function() {
|
|
16
|
+
const results = [];
|
|
17
|
+
|
|
18
|
+
// Strategy 1: Use Notion's own class 'notion-outliner-bookmarks-header-container'
|
|
19
|
+
const headerContainer = document.querySelector('.notion-outliner-bookmarks-header-container');
|
|
20
|
+
if (headerContainer) {
|
|
21
|
+
// Walk up to the section parent that wraps header + items
|
|
22
|
+
let section = headerContainer.parentElement;
|
|
23
|
+
if (section && section.children.length === 1) section = section.parentElement;
|
|
24
|
+
|
|
25
|
+
if (section) {
|
|
26
|
+
const treeItems = section.querySelectorAll('[role="treeitem"]');
|
|
27
|
+
treeItems.forEach((item) => {
|
|
28
|
+
// Title text is in a div.notranslate sibling of the icon area
|
|
29
|
+
const titleEl = item.querySelector('div.notranslate:not(.notion-record-icon)');
|
|
30
|
+
const title = titleEl
|
|
31
|
+
? titleEl.textContent.trim()
|
|
32
|
+
: (item.textContent || '').trim().substring(0, 80);
|
|
33
|
+
|
|
34
|
+
// Icon/emoji is in the notion-record-icon element
|
|
35
|
+
const iconEl = item.querySelector('.notion-record-icon');
|
|
36
|
+
const icon = iconEl ? iconEl.textContent.trim().substring(0, 4) : '';
|
|
37
|
+
|
|
38
|
+
if (title && title.length > 0) {
|
|
39
|
+
results.push({ Index: results.length + 1, Title: title, Icon: icon || '📄' });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Strategy 2: Fallback — find "Favorites" text node and walk DOM
|
|
46
|
+
if (results.length === 0) {
|
|
47
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
|
|
48
|
+
let node;
|
|
49
|
+
let favEl = null;
|
|
50
|
+
while (node = walker.nextNode()) {
|
|
51
|
+
const text = node.textContent.trim();
|
|
52
|
+
if (text === 'Favorites' || text === '收藏' || text === '收藏夹') {
|
|
53
|
+
favEl = node.parentElement;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (favEl) {
|
|
59
|
+
let section = favEl;
|
|
60
|
+
for (let i = 0; i < 6; i++) {
|
|
61
|
+
const p = section.parentElement;
|
|
62
|
+
if (!p || p === document.body) break;
|
|
63
|
+
const treeItems = p.querySelectorAll(':scope > [role="treeitem"]');
|
|
64
|
+
if (treeItems.length > 0) { section = p; break; }
|
|
65
|
+
section = p;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const treeItems = section.querySelectorAll('[role="treeitem"]');
|
|
69
|
+
treeItems.forEach((item) => {
|
|
70
|
+
const text = (item.textContent || '').trim().substring(0, 120);
|
|
71
|
+
if (text && text.length > 1 && !text.match(/^(Favorites|收藏夹?)$/)) {
|
|
72
|
+
results.push({ Index: results.length + 1, Title: text, Icon: '📄' });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return results;
|
|
79
|
+
})()
|
|
80
|
+
`);
|
|
81
|
+
|
|
82
|
+
if (items.length === 0) {
|
|
83
|
+
return [{ Index: 0, Title: 'No favorites found. Make sure sidebar is visible and you have favorites.', Icon: '⚠️' }];
|
|
84
|
+
}
|
|
85
|
+
return items;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const newCommand = cli({
|
|
5
|
+
site: 'notion',
|
|
6
|
+
name: 'new',
|
|
7
|
+
description: 'Create a new page in Notion',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'title', required: false, positional: true, help: 'Page title (optional)' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['Status'],
|
|
15
|
+
func: async (page: IPage, kwargs: any) => {
|
|
16
|
+
const title = kwargs.title as string | undefined;
|
|
17
|
+
|
|
18
|
+
// Cmd+N creates a new page in Notion
|
|
19
|
+
const isMac = process.platform === 'darwin';
|
|
20
|
+
await page.pressKey(isMac ? 'Meta+N' : 'Control+N');
|
|
21
|
+
await page.wait(1);
|
|
22
|
+
|
|
23
|
+
// If title is provided, type it into the title field
|
|
24
|
+
if (title) {
|
|
25
|
+
await page.evaluate(`
|
|
26
|
+
(function(t) {
|
|
27
|
+
const titleEl = document.querySelector('[placeholder="Untitled"], [data-content-editable-leaf] [placeholder]');
|
|
28
|
+
if (titleEl) {
|
|
29
|
+
titleEl.focus();
|
|
30
|
+
document.execCommand('insertText', false, t);
|
|
31
|
+
}
|
|
32
|
+
})(${JSON.stringify(title)})
|
|
33
|
+
`);
|
|
34
|
+
await page.wait(0.5);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return [{ Status: title ? `Created page: ${title}` : 'New blank page created' }];
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const readCommand = cli({
|
|
5
|
+
site: 'notion',
|
|
6
|
+
name: 'read',
|
|
7
|
+
description: 'Read the content of the currently open Notion page',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [],
|
|
12
|
+
columns: ['Title', 'Content'],
|
|
13
|
+
func: async (page: IPage) => {
|
|
14
|
+
const result = await page.evaluate(`
|
|
15
|
+
(function() {
|
|
16
|
+
// Get the page title
|
|
17
|
+
const titleEl = document.querySelector('[data-block-id] [placeholder="Untitled"], .notion-page-block .notranslate, h1.notion-title, [class*="title"]');
|
|
18
|
+
const title = titleEl ? (titleEl.textContent || '').trim() : document.title;
|
|
19
|
+
|
|
20
|
+
// Get the page content — Notion renders blocks in a frame
|
|
21
|
+
const frame = document.querySelector('.notion-page-content, [class*="page-content"], .layout-content, main');
|
|
22
|
+
const content = frame ? (frame.innerText || frame.textContent || '').trim() : '';
|
|
23
|
+
|
|
24
|
+
return { title, content };
|
|
25
|
+
})()
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
return [{
|
|
29
|
+
Title: result.title || 'Untitled',
|
|
30
|
+
Content: result.content || '(empty page)',
|
|
31
|
+
}];
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const searchCommand = cli({
|
|
5
|
+
site: 'notion',
|
|
6
|
+
name: 'search',
|
|
7
|
+
description: 'Search pages and databases in Notion via Quick Find (Cmd+P)',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [{ name: 'query', required: true, positional: true, help: 'Search query' }],
|
|
12
|
+
columns: ['Index', 'Title'],
|
|
13
|
+
func: async (page: IPage, kwargs: any) => {
|
|
14
|
+
const query = kwargs.query as string;
|
|
15
|
+
|
|
16
|
+
// Open Quick Find
|
|
17
|
+
const isMac = process.platform === 'darwin';
|
|
18
|
+
await page.pressKey(isMac ? 'Meta+P' : 'Control+P');
|
|
19
|
+
await page.wait(0.5);
|
|
20
|
+
|
|
21
|
+
// Type the search query
|
|
22
|
+
await page.evaluate(`
|
|
23
|
+
(function(q) {
|
|
24
|
+
const input = document.querySelector('input[placeholder*="Search"], input[type="text"]');
|
|
25
|
+
if (input) {
|
|
26
|
+
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
|
|
27
|
+
setter.call(input, q);
|
|
28
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
29
|
+
}
|
|
30
|
+
})(${JSON.stringify(query)})
|
|
31
|
+
`);
|
|
32
|
+
|
|
33
|
+
await page.wait(1.5);
|
|
34
|
+
|
|
35
|
+
// Scrape results
|
|
36
|
+
const results = await page.evaluate(`
|
|
37
|
+
(function() {
|
|
38
|
+
const items = document.querySelectorAll('[role="option"], [class*="searchResult"], [class*="quick-find"] [role="button"]');
|
|
39
|
+
return Array.from(items).slice(0, 20).map((item, i) => ({
|
|
40
|
+
Index: i + 1,
|
|
41
|
+
Title: (item.textContent || '').trim().substring(0, 120),
|
|
42
|
+
}));
|
|
43
|
+
})()
|
|
44
|
+
`);
|
|
45
|
+
|
|
46
|
+
// Close Quick Find
|
|
47
|
+
await page.pressKey('Escape');
|
|
48
|
+
|
|
49
|
+
if (results.length === 0) {
|
|
50
|
+
return [{ Index: 0, Title: `No results for "${query}"` }];
|
|
51
|
+
}
|
|
52
|
+
return results;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
export const sidebarCommand = cli({
|
|
5
|
+
site: 'notion',
|
|
6
|
+
name: 'sidebar',
|
|
7
|
+
description: 'List pages and databases from the Notion sidebar',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [],
|
|
12
|
+
columns: ['Index', 'Title'],
|
|
13
|
+
func: async (page: IPage) => {
|
|
14
|
+
const items = await page.evaluate(`
|
|
15
|
+
(function() {
|
|
16
|
+
const results = [];
|
|
17
|
+
// Notion sidebar items
|
|
18
|
+
const selectors = [
|
|
19
|
+
'[class*="sidebar"] [role="treeitem"]',
|
|
20
|
+
'[class*="sidebar"] a',
|
|
21
|
+
'.notion-sidebar [role="button"]',
|
|
22
|
+
'nav [role="treeitem"]',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const sel of selectors) {
|
|
26
|
+
const nodes = document.querySelectorAll(sel);
|
|
27
|
+
if (nodes.length > 0) {
|
|
28
|
+
nodes.forEach((n, i) => {
|
|
29
|
+
const text = (n.textContent || '').trim().substring(0, 100);
|
|
30
|
+
if (text && text.length > 1) results.push({ Index: i + 1, Title: text });
|
|
31
|
+
});
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
})()
|
|
37
|
+
`);
|
|
38
|
+
|
|
39
|
+
if (items.length === 0) {
|
|
40
|
+
return [{ Index: 0, Title: 'No sidebar items found. Toggle the sidebar first.' }];
|
|
41
|
+
}
|
|
42
|
+
return items;
|
|
43
|
+
},
|
|
44
|
+
});
|