@jackwener/opencli 1.4.1 → 1.5.1
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/workflows/build-extension.yml +2 -6
- package/.github/workflows/ci.yml +21 -1
- package/README.md +35 -6
- package/README.zh-CN.md +12 -5
- package/SKILL.md +2 -0
- package/dist/browser/cdp.d.ts +2 -1
- package/dist/browser/cdp.js +5 -0
- package/dist/browser/discover.d.ts +4 -1
- package/dist/browser/discover.js +6 -2
- package/dist/browser/errors.d.ts +2 -2
- package/dist/browser/errors.js +4 -12
- package/dist/browser/mcp.d.ts +2 -1
- package/dist/browser/page.d.ts +3 -0
- package/dist/browser/page.js +24 -1
- package/dist/build-manifest.d.ts +2 -0
- package/dist/build-manifest.js +39 -14
- package/dist/build-manifest.test.js +21 -0
- package/dist/capabilityRouting.d.ts +2 -0
- package/dist/capabilityRouting.js +2 -1
- package/dist/cli-manifest.json +1567 -108
- package/dist/cli.js +68 -6
- package/dist/clis/36kr/article.d.ts +1 -0
- package/dist/clis/36kr/article.js +62 -0
- package/dist/clis/36kr/hot.d.ts +3 -0
- package/dist/clis/36kr/hot.js +80 -0
- package/dist/clis/36kr/hot.test.d.ts +1 -0
- package/dist/clis/36kr/hot.test.js +15 -0
- package/dist/clis/36kr/news.d.ts +1 -0
- package/dist/clis/36kr/news.js +51 -0
- package/dist/clis/36kr/news.test.d.ts +1 -0
- package/dist/clis/36kr/news.test.js +85 -0
- package/dist/clis/36kr/search.d.ts +1 -0
- package/dist/clis/36kr/search.js +72 -0
- package/dist/clis/bilibili/comments.d.ts +5 -0
- package/dist/clis/bilibili/comments.js +40 -0
- package/dist/clis/bilibili/comments.test.d.ts +1 -0
- package/dist/clis/bilibili/comments.test.js +82 -0
- package/dist/clis/bluesky/feeds.yaml +29 -0
- package/dist/clis/bluesky/followers.yaml +33 -0
- package/dist/clis/bluesky/following.yaml +33 -0
- package/dist/clis/bluesky/profile.yaml +27 -0
- package/dist/clis/bluesky/search.yaml +34 -0
- package/dist/clis/bluesky/starter-packs.yaml +34 -0
- package/dist/clis/bluesky/thread.yaml +32 -0
- package/dist/clis/bluesky/trending.yaml +27 -0
- package/dist/clis/bluesky/user.yaml +34 -0
- package/dist/clis/chatgpt/ask.js +29 -14
- package/dist/clis/chatgpt/ax.d.ts +6 -0
- package/dist/clis/chatgpt/ax.js +172 -1
- package/dist/clis/chatgpt/model.d.ts +1 -0
- package/dist/clis/chatgpt/model.js +24 -0
- package/dist/clis/chatgpt/send.js +12 -3
- package/dist/clis/douban/download.d.ts +1 -0
- package/dist/clis/douban/download.js +67 -0
- package/dist/clis/douban/download.test.d.ts +1 -0
- package/dist/clis/douban/download.test.js +170 -0
- package/dist/clis/douban/photos.d.ts +1 -0
- package/dist/clis/douban/photos.js +34 -0
- package/dist/clis/douban/utils.d.ts +25 -0
- package/dist/clis/douban/utils.js +190 -1
- package/dist/clis/douban/utils.test.d.ts +1 -0
- package/dist/clis/douban/utils.test.js +64 -0
- package/dist/clis/imdb/person.d.ts +1 -0
- package/dist/clis/imdb/person.js +203 -0
- package/dist/clis/imdb/reviews.d.ts +1 -0
- package/dist/clis/imdb/reviews.js +88 -0
- package/dist/clis/imdb/search.d.ts +1 -0
- package/dist/clis/imdb/search.js +161 -0
- package/dist/clis/imdb/title.d.ts +1 -0
- package/dist/clis/imdb/title.js +93 -0
- package/dist/clis/imdb/top.d.ts +1 -0
- package/dist/clis/imdb/top.js +53 -0
- package/dist/clis/imdb/trending.d.ts +1 -0
- package/dist/clis/imdb/trending.js +52 -0
- package/dist/clis/imdb/utils.d.ts +46 -0
- package/dist/clis/imdb/utils.js +285 -0
- package/dist/clis/imdb/utils.test.d.ts +1 -0
- package/dist/clis/imdb/utils.test.js +88 -0
- package/dist/clis/jd/item.d.ts +4 -0
- package/dist/clis/jd/item.js +16 -15
- package/dist/clis/jd/item.test.js +16 -1
- package/dist/clis/linux-do/categories.yaml +38 -9
- package/dist/clis/linux-do/category.d.ts +1 -0
- package/dist/clis/linux-do/category.js +36 -0
- package/dist/clis/linux-do/feed.d.ts +45 -0
- package/dist/clis/linux-do/feed.js +397 -0
- package/dist/clis/linux-do/feed.test.d.ts +1 -0
- package/dist/clis/linux-do/feed.test.js +118 -0
- package/dist/clis/linux-do/hot.d.ts +1 -0
- package/dist/clis/linux-do/hot.js +25 -0
- package/dist/clis/linux-do/latest.d.ts +1 -0
- package/dist/clis/linux-do/latest.js +18 -0
- package/dist/clis/linux-do/tags.yaml +41 -0
- package/dist/clis/linux-do/topic.yaml +41 -3
- package/dist/clis/linux-do/user-posts.yaml +67 -0
- package/dist/clis/linux-do/user-topics.yaml +54 -0
- package/dist/clis/paperreview/commands.test.d.ts +3 -0
- package/dist/clis/paperreview/commands.test.js +243 -0
- package/dist/clis/paperreview/feedback.d.ts +1 -0
- package/dist/clis/paperreview/feedback.js +52 -0
- package/dist/clis/paperreview/review.d.ts +1 -0
- package/dist/clis/paperreview/review.js +37 -0
- package/dist/clis/paperreview/submit.d.ts +1 -0
- package/dist/clis/paperreview/submit.js +85 -0
- package/dist/clis/paperreview/utils.d.ts +46 -0
- package/dist/clis/paperreview/utils.js +197 -0
- package/dist/clis/paperreview/utils.test.d.ts +1 -0
- package/dist/clis/paperreview/utils.test.js +49 -0
- package/dist/clis/producthunt/browse.d.ts +1 -0
- package/dist/clis/producthunt/browse.js +99 -0
- package/dist/clis/producthunt/hot.d.ts +1 -0
- package/dist/clis/producthunt/hot.js +110 -0
- package/dist/clis/producthunt/posts.d.ts +1 -0
- package/dist/clis/producthunt/posts.js +28 -0
- package/dist/clis/producthunt/today.d.ts +1 -0
- package/dist/clis/producthunt/today.js +35 -0
- package/dist/clis/producthunt/utils.d.ts +29 -0
- package/dist/clis/producthunt/utils.js +99 -0
- package/dist/clis/producthunt/utils.test.d.ts +1 -0
- package/dist/clis/producthunt/utils.test.js +64 -0
- package/dist/clis/twitter/article.js +4 -28
- package/dist/clis/twitter/likes.d.ts +24 -0
- package/dist/clis/twitter/likes.js +217 -0
- package/dist/clis/twitter/likes.test.d.ts +1 -0
- package/dist/clis/twitter/likes.test.js +85 -0
- package/dist/clis/twitter/profile.js +4 -28
- package/dist/clis/twitter/search.js +2 -1
- package/dist/clis/twitter/search.test.js +2 -0
- package/dist/clis/twitter/shared.d.ts +6 -0
- package/dist/clis/twitter/shared.js +35 -0
- package/dist/clis/twitter/timeline.js +2 -13
- package/dist/clis/twitter/trending.js +29 -61
- package/dist/clis/v2ex/hot.yaml +17 -3
- package/dist/clis/weixin/download.d.ts +17 -0
- package/dist/clis/weixin/download.js +88 -20
- package/dist/clis/weread/book.js +2 -2
- package/dist/clis/weread/commands.test.d.ts +3 -0
- package/dist/clis/weread/commands.test.js +43 -0
- package/dist/clis/weread/highlights.js +2 -2
- package/dist/clis/weread/notebooks.js +2 -2
- package/dist/clis/weread/notes.js +3 -3
- package/dist/clis/weread/shelf.js +2 -2
- package/dist/clis/weread/utils.d.ts +4 -4
- package/dist/clis/weread/utils.js +32 -14
- package/dist/clis/weread/utils.test.js +1 -28
- package/dist/clis/xiaohongshu/comments.d.ts +5 -0
- package/dist/clis/xiaohongshu/comments.js +74 -0
- package/dist/clis/xiaohongshu/comments.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/comments.test.js +79 -0
- package/dist/clis/xiaohongshu/publish.js +179 -47
- package/dist/clis/xiaohongshu/publish.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/publish.test.js +131 -0
- package/dist/clis/xiaohongshu/search.d.ts +8 -1
- package/dist/clis/xiaohongshu/search.js +20 -1
- package/dist/clis/xiaohongshu/search.test.d.ts +1 -1
- package/dist/clis/xiaohongshu/search.test.js +32 -1
- package/dist/commanderAdapter.d.ts +1 -0
- package/dist/commanderAdapter.js +176 -29
- package/dist/commanderAdapter.test.d.ts +1 -0
- package/dist/commanderAdapter.test.js +62 -0
- package/dist/daemon.js +17 -1
- package/dist/discovery.js +48 -42
- package/dist/doctor.d.ts +2 -2
- package/dist/doctor.js +11 -4
- package/dist/download/index.js +63 -51
- package/dist/download/index.test.js +17 -4
- package/dist/engine.test.js +42 -0
- package/dist/errors.d.ts +4 -2
- package/dist/errors.js +17 -34
- package/dist/execution.d.ts +1 -3
- package/dist/execution.js +66 -8
- package/dist/execution.test.d.ts +1 -0
- package/dist/execution.test.js +40 -0
- package/dist/external.js +6 -1
- package/dist/hooks.js +2 -0
- package/dist/main.js +6 -0
- package/dist/output.js +5 -1
- package/dist/pipeline/executor.js +3 -4
- package/dist/plugin-manifest.d.ts +70 -0
- package/dist/plugin-manifest.js +160 -0
- package/dist/plugin-manifest.test.d.ts +4 -0
- package/dist/plugin-manifest.test.js +179 -0
- package/dist/plugin-scaffold.d.ts +28 -0
- package/dist/plugin-scaffold.js +142 -0
- package/dist/plugin-scaffold.test.d.ts +4 -0
- package/dist/plugin-scaffold.test.js +83 -0
- package/dist/plugin.d.ts +82 -11
- package/dist/plugin.js +870 -84
- package/dist/plugin.test.js +1032 -17
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +2 -0
- package/dist/runtime-detect.d.ts +21 -0
- package/dist/runtime-detect.js +32 -0
- package/dist/runtime-detect.test.d.ts +1 -0
- package/dist/runtime-detect.test.js +27 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +2 -2
- package/dist/serialization.d.ts +2 -0
- package/dist/serialization.js +6 -0
- package/dist/types.d.ts +3 -0
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +112 -0
- package/dist/weixin-download.test.d.ts +1 -0
- package/dist/weixin-download.test.js +30 -0
- package/dist/weread-private-api-regression.test.d.ts +1 -0
- package/dist/weread-private-api-regression.test.js +122 -0
- package/dist/yaml-schema.d.ts +3 -0
- package/dist/yaml-schema.js +18 -1
- package/docs/.vitepress/config.mts +4 -0
- package/docs/adapters/browser/36kr.md +47 -0
- package/docs/adapters/browser/bluesky.md +53 -0
- package/docs/adapters/browser/douban.md +14 -0
- package/docs/adapters/browser/imdb.md +47 -0
- package/docs/adapters/browser/jd.md +2 -2
- package/docs/adapters/browser/linux-do.md +181 -20
- package/docs/adapters/browser/paperreview.md +43 -0
- package/docs/adapters/browser/producthunt.md +49 -0
- package/docs/adapters/desktop/chatgpt.md +5 -0
- package/docs/adapters/index.md +6 -2
- package/docs/advanced/download.md +4 -0
- package/docs/advanced/rate-limiter-plugin.md +99 -0
- package/docs/guide/electron-app-cli.md +200 -0
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +97 -0
- package/docs/zh/guide/electron-app-cli.md +188 -0
- package/docs/zh/guide/getting-started.md +1 -0
- package/docs/zh/guide/plugins.md +65 -0
- package/extension/package.json +1 -0
- package/extension/scripts/package-release.mjs +179 -0
- package/extension/src/background.ts +2 -0
- package/package.json +4 -1
- package/scripts/postinstall.js +10 -0
- package/src/browser/cdp.ts +8 -1
- package/src/browser/discover.ts +8 -3
- package/src/browser/errors.ts +13 -14
- package/src/browser/mcp.ts +2 -1
- package/src/browser/page.ts +24 -1
- package/src/build-manifest.test.ts +23 -0
- package/src/build-manifest.ts +40 -15
- package/src/capabilityRouting.ts +2 -1
- package/src/cli.ts +69 -6
- package/src/clis/36kr/article.ts +69 -0
- package/src/clis/36kr/hot.test.ts +19 -0
- package/src/clis/36kr/hot.ts +100 -0
- package/src/clis/36kr/news.test.ts +90 -0
- package/src/clis/36kr/news.ts +54 -0
- package/src/clis/36kr/search.ts +78 -0
- package/src/clis/bilibili/comments.test.ts +102 -0
- package/src/clis/bilibili/comments.ts +44 -0
- package/src/clis/bluesky/feeds.yaml +29 -0
- package/src/clis/bluesky/followers.yaml +33 -0
- package/src/clis/bluesky/following.yaml +33 -0
- package/src/clis/bluesky/profile.yaml +27 -0
- package/src/clis/bluesky/search.yaml +34 -0
- package/src/clis/bluesky/starter-packs.yaml +34 -0
- package/src/clis/bluesky/thread.yaml +32 -0
- package/src/clis/bluesky/trending.yaml +27 -0
- package/src/clis/bluesky/user.yaml +34 -0
- package/src/clis/chatgpt/ask.ts +28 -14
- package/src/clis/chatgpt/ax.ts +180 -1
- package/src/clis/chatgpt/model.ts +27 -0
- package/src/clis/chatgpt/send.ts +16 -6
- package/src/clis/douban/download.test.ts +196 -0
- package/src/clis/douban/download.ts +78 -0
- package/src/clis/douban/photos.ts +36 -0
- package/src/clis/douban/utils.test.ts +97 -0
- package/src/clis/douban/utils.ts +232 -1
- package/src/clis/imdb/person.ts +232 -0
- package/src/clis/imdb/reviews.ts +111 -0
- package/src/clis/imdb/search.ts +179 -0
- package/src/clis/imdb/title.ts +121 -0
- package/src/clis/imdb/top.ts +67 -0
- package/src/clis/imdb/trending.ts +66 -0
- package/src/clis/imdb/utils.test.ts +117 -0
- package/src/clis/imdb/utils.ts +305 -0
- package/src/clis/jd/item.test.ts +18 -1
- package/src/clis/jd/item.ts +18 -15
- package/src/clis/linux-do/categories.yaml +38 -9
- package/src/clis/linux-do/category.ts +37 -0
- package/src/clis/linux-do/feed.test.ts +132 -0
- package/src/clis/linux-do/feed.ts +501 -0
- package/src/clis/linux-do/hot.ts +26 -0
- package/src/clis/linux-do/latest.ts +19 -0
- package/src/clis/linux-do/tags.yaml +41 -0
- package/src/clis/linux-do/topic.yaml +41 -3
- package/src/clis/linux-do/user-posts.yaml +67 -0
- package/src/clis/linux-do/user-topics.yaml +54 -0
- package/src/clis/paperreview/commands.test.ts +283 -0
- package/src/clis/paperreview/feedback.ts +64 -0
- package/src/clis/paperreview/review.ts +47 -0
- package/src/clis/paperreview/submit.ts +119 -0
- package/src/clis/paperreview/utils.test.ts +68 -0
- package/src/clis/paperreview/utils.ts +276 -0
- package/src/clis/producthunt/browse.ts +109 -0
- package/src/clis/producthunt/hot.ts +127 -0
- package/src/clis/producthunt/posts.ts +29 -0
- package/src/clis/producthunt/today.ts +37 -0
- package/src/clis/producthunt/utils.test.ts +72 -0
- package/src/clis/producthunt/utils.ts +122 -0
- package/src/clis/twitter/article.ts +5 -28
- package/src/clis/twitter/likes.test.ts +91 -0
- package/src/clis/twitter/likes.ts +256 -0
- package/src/clis/twitter/profile.ts +5 -28
- package/src/clis/twitter/search.test.ts +2 -0
- package/src/clis/twitter/search.ts +3 -1
- package/src/clis/twitter/shared.ts +45 -0
- package/src/clis/twitter/timeline.ts +2 -13
- package/src/clis/twitter/trending.ts +29 -77
- package/src/clis/v2ex/hot.yaml +17 -3
- package/src/clis/weixin/download.ts +114 -20
- package/src/clis/weread/book.ts +2 -2
- package/src/clis/weread/commands.test.ts +57 -0
- package/src/clis/weread/highlights.ts +2 -2
- package/src/clis/weread/notebooks.ts +2 -2
- package/src/clis/weread/notes.ts +3 -3
- package/src/clis/weread/shelf.ts +2 -2
- package/src/clis/weread/utils.test.ts +1 -32
- package/src/clis/weread/utils.ts +41 -16
- package/src/clis/xiaohongshu/comments.test.ts +96 -0
- package/src/clis/xiaohongshu/comments.ts +81 -0
- package/src/clis/xiaohongshu/publish.test.ts +151 -0
- package/src/clis/xiaohongshu/publish.ts +206 -54
- package/src/clis/xiaohongshu/search.test.ts +39 -1
- package/src/clis/xiaohongshu/search.ts +19 -1
- package/src/commanderAdapter.test.ts +78 -0
- package/src/commanderAdapter.ts +188 -24
- package/src/daemon.ts +19 -1
- package/src/discovery.ts +49 -48
- package/src/doctor.ts +15 -5
- package/src/download/index.test.ts +14 -4
- package/src/download/index.ts +67 -55
- package/src/engine.test.ts +38 -0
- package/src/errors.ts +26 -63
- package/src/execution.test.ts +47 -0
- package/src/execution.ts +67 -9
- package/src/external.ts +6 -1
- package/src/hooks.ts +1 -0
- package/src/main.ts +7 -0
- package/src/output.ts +3 -1
- package/src/pipeline/executor.ts +4 -6
- package/src/plugin-manifest.test.ts +223 -0
- package/src/plugin-manifest.ts +206 -0
- package/src/plugin-scaffold.test.ts +98 -0
- package/src/plugin-scaffold.ts +170 -0
- package/src/plugin.test.ts +1104 -17
- package/src/plugin.ts +1101 -86
- package/src/registry.ts +6 -1
- package/src/runtime-detect.test.ts +30 -0
- package/src/runtime-detect.ts +36 -0
- package/src/runtime.ts +3 -3
- package/src/serialization.ts +4 -0
- package/src/types.ts +3 -0
- package/src/update-check.ts +114 -0
- package/src/weixin-download.test.ts +64 -0
- package/src/weread-private-api-regression.test.ts +150 -0
- package/src/yaml-schema.ts +20 -0
- package/tests/e2e/browser-auth.test.ts +13 -9
- package/tests/e2e/browser-public-extended.test.ts +1 -1
- package/tests/e2e/browser-public.test.ts +62 -4
- package/tests/e2e/helpers.ts +2 -1
- package/tests/e2e/public-commands.test.ts +37 -3
- package/tests/smoke/api-health.test.ts +1 -1
- package/vitest.config.ts +10 -0
- package/dist/clis/linux-do/category.yaml +0 -51
- package/dist/clis/linux-do/hot.yaml +0 -50
- package/dist/clis/linux-do/latest.yaml +0 -40
- package/src/clis/linux-do/category.yaml +0 -51
- package/src/clis/linux-do/hot.yaml +0 -50
- package/src/clis/linux-do/latest.yaml +0 -40
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: followers
|
|
3
|
+
description: List followers of a Bluesky user
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
handle:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Bluesky handle"
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 20
|
|
17
|
+
description: Number of followers
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.graph.getFollowers?actor=${{ args.handle }}&limit=${{ args.limit }}
|
|
22
|
+
|
|
23
|
+
- select: followers
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
rank: ${{ index + 1 }}
|
|
27
|
+
handle: ${{ item.handle }}
|
|
28
|
+
name: ${{ item.displayName }}
|
|
29
|
+
description: ${{ item.description }}
|
|
30
|
+
|
|
31
|
+
- limit: ${{ args.limit }}
|
|
32
|
+
|
|
33
|
+
columns: [rank, handle, name, description]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: following
|
|
3
|
+
description: List accounts a Bluesky user is following
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
handle:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Bluesky handle"
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 20
|
|
17
|
+
description: Number of accounts
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.graph.getFollows?actor=${{ args.handle }}&limit=${{ args.limit }}
|
|
22
|
+
|
|
23
|
+
- select: follows
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
rank: ${{ index + 1 }}
|
|
27
|
+
handle: ${{ item.handle }}
|
|
28
|
+
name: ${{ item.displayName }}
|
|
29
|
+
description: ${{ item.description }}
|
|
30
|
+
|
|
31
|
+
- limit: ${{ args.limit }}
|
|
32
|
+
|
|
33
|
+
columns: [rank, handle, name, description]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: profile
|
|
3
|
+
description: Get Bluesky user profile info
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
handle:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Bluesky handle (e.g. bsky.app, jay.bsky.team)"
|
|
14
|
+
|
|
15
|
+
pipeline:
|
|
16
|
+
- fetch:
|
|
17
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${{ args.handle }}
|
|
18
|
+
|
|
19
|
+
- map:
|
|
20
|
+
handle: ${{ item.handle }}
|
|
21
|
+
name: ${{ item.displayName }}
|
|
22
|
+
followers: ${{ item.followersCount }}
|
|
23
|
+
following: ${{ item.followsCount }}
|
|
24
|
+
posts: ${{ item.postsCount }}
|
|
25
|
+
description: ${{ item.description }}
|
|
26
|
+
|
|
27
|
+
columns: [handle, name, followers, following, posts, description]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: search
|
|
3
|
+
description: Search Bluesky users
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
query:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: Search query
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 10
|
|
17
|
+
description: Number of results
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.actor.searchActors?q=${{ args.query }}&limit=${{ args.limit }}
|
|
22
|
+
|
|
23
|
+
- select: actors
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
rank: ${{ index + 1 }}
|
|
27
|
+
handle: ${{ item.handle }}
|
|
28
|
+
name: ${{ item.displayName }}
|
|
29
|
+
followers: ${{ item.followersCount }}
|
|
30
|
+
description: ${{ item.description }}
|
|
31
|
+
|
|
32
|
+
- limit: ${{ args.limit }}
|
|
33
|
+
|
|
34
|
+
columns: [rank, handle, name, followers, description]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: starter-packs
|
|
3
|
+
description: Get starter packs created by a Bluesky user
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
handle:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Bluesky handle"
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 10
|
|
17
|
+
description: Number of starter packs
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.graph.getActorStarterPacks?actor=${{ args.handle }}&limit=${{ args.limit }}
|
|
22
|
+
|
|
23
|
+
- select: starterPacks
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
rank: ${{ index + 1 }}
|
|
27
|
+
name: ${{ item.record.name }}
|
|
28
|
+
description: ${{ item.record.description }}
|
|
29
|
+
members: ${{ item.listItemCount }}
|
|
30
|
+
joins: ${{ item.joinedAllTimeCount }}
|
|
31
|
+
|
|
32
|
+
- limit: ${{ args.limit }}
|
|
33
|
+
|
|
34
|
+
columns: [rank, name, description, members, joins]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: thread
|
|
3
|
+
description: Get a Bluesky post thread with replies
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
uri:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Post AT URI (at://did:.../app.bsky.feed.post/...) or bsky.app URL"
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 20
|
|
17
|
+
description: Number of replies
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${{ args.uri }}&depth=2
|
|
22
|
+
|
|
23
|
+
- select: thread
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
author: ${{ item.post.author.handle }}
|
|
27
|
+
text: ${{ item.post.record.text }}
|
|
28
|
+
likes: ${{ item.post.likeCount }}
|
|
29
|
+
reposts: ${{ item.post.repostCount }}
|
|
30
|
+
replies_count: ${{ item.post.replyCount }}
|
|
31
|
+
|
|
32
|
+
columns: [author, text, likes, reposts, replies_count]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: trending
|
|
3
|
+
description: Trending topics on Bluesky
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
limit:
|
|
10
|
+
type: int
|
|
11
|
+
default: 20
|
|
12
|
+
description: Number of topics
|
|
13
|
+
|
|
14
|
+
pipeline:
|
|
15
|
+
- fetch:
|
|
16
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.unspecced.getTrendingTopics
|
|
17
|
+
|
|
18
|
+
- select: topics
|
|
19
|
+
|
|
20
|
+
- map:
|
|
21
|
+
rank: ${{ index + 1 }}
|
|
22
|
+
topic: ${{ item.topic }}
|
|
23
|
+
link: ${{ item.link }}
|
|
24
|
+
|
|
25
|
+
- limit: ${{ args.limit }}
|
|
26
|
+
|
|
27
|
+
columns: [rank, topic, link]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
site: bluesky
|
|
2
|
+
name: user
|
|
3
|
+
description: Get recent posts from a Bluesky user
|
|
4
|
+
domain: public.api.bsky.app
|
|
5
|
+
strategy: public
|
|
6
|
+
browser: false
|
|
7
|
+
|
|
8
|
+
args:
|
|
9
|
+
handle:
|
|
10
|
+
type: str
|
|
11
|
+
required: true
|
|
12
|
+
positional: true
|
|
13
|
+
description: "Bluesky handle (e.g. bsky.app)"
|
|
14
|
+
limit:
|
|
15
|
+
type: int
|
|
16
|
+
default: 20
|
|
17
|
+
description: Number of posts
|
|
18
|
+
|
|
19
|
+
pipeline:
|
|
20
|
+
- fetch:
|
|
21
|
+
url: https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${{ args.handle }}&limit=${{ args.limit }}
|
|
22
|
+
|
|
23
|
+
- select: feed
|
|
24
|
+
|
|
25
|
+
- map:
|
|
26
|
+
rank: ${{ index + 1 }}
|
|
27
|
+
text: ${{ item.post.record.text }}
|
|
28
|
+
likes: ${{ item.post.likeCount }}
|
|
29
|
+
reposts: ${{ item.post.repostCount }}
|
|
30
|
+
replies: ${{ item.post.replyCount }}
|
|
31
|
+
|
|
32
|
+
- limit: ${{ args.limit }}
|
|
33
|
+
|
|
34
|
+
columns: [rank, text, likes, reposts, replies]
|
package/dist/clis/chatgpt/ask.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import { cli, Strategy } from '../../registry.js';
|
|
3
3
|
import { ConfigError } from '../../errors.js';
|
|
4
|
-
import { getVisibleChatMessages } from './ax.js';
|
|
4
|
+
import { activateChatGPT, getVisibleChatMessages, selectModel, MODEL_CHOICES, isGenerating } from './ax.js';
|
|
5
5
|
export const askCommand = cli({
|
|
6
6
|
site: 'chatgpt',
|
|
7
7
|
name: 'ask',
|
|
@@ -11,6 +11,7 @@ export const askCommand = cli({
|
|
|
11
11
|
browser: false,
|
|
12
12
|
args: [
|
|
13
13
|
{ name: 'text', required: true, positional: true, help: 'Prompt to send' },
|
|
14
|
+
{ name: 'model', required: false, help: 'Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking', choices: MODEL_CHOICES },
|
|
14
15
|
{ name: 'timeout', required: false, help: 'Max seconds to wait for response (default: 30)', default: '30' },
|
|
15
16
|
],
|
|
16
17
|
columns: ['Role', 'Text'],
|
|
@@ -19,7 +20,13 @@ export const askCommand = cli({
|
|
|
19
20
|
throw new ConfigError('ChatGPT Desktop integration requires macOS (osascript is not available on this platform)');
|
|
20
21
|
}
|
|
21
22
|
const text = kwargs.text;
|
|
23
|
+
const model = kwargs.model;
|
|
22
24
|
const timeout = parseInt(kwargs.timeout, 10) || 30;
|
|
25
|
+
// Switch model before sending if requested
|
|
26
|
+
if (model) {
|
|
27
|
+
activateChatGPT();
|
|
28
|
+
selectModel(model);
|
|
29
|
+
}
|
|
23
30
|
// Backup clipboard
|
|
24
31
|
let clipBackup = '';
|
|
25
32
|
try {
|
|
@@ -29,8 +36,7 @@ export const askCommand = cli({
|
|
|
29
36
|
const messagesBefore = getVisibleChatMessages();
|
|
30
37
|
// Send the message
|
|
31
38
|
spawnSync('pbcopy', { input: text });
|
|
32
|
-
|
|
33
|
-
execSync("osascript -e 'delay 0.5'");
|
|
39
|
+
activateChatGPT();
|
|
34
40
|
const cmd = "osascript " +
|
|
35
41
|
"-e 'tell application \"System Events\"' " +
|
|
36
42
|
"-e 'keystroke \"v\" using command down' " +
|
|
@@ -41,23 +47,32 @@ export const askCommand = cli({
|
|
|
41
47
|
// Restore clipboard after the prompt is sent.
|
|
42
48
|
if (clipBackup)
|
|
43
49
|
spawnSync('pbcopy', { input: clipBackup });
|
|
44
|
-
// Wait for response
|
|
45
|
-
|
|
50
|
+
// Wait for response: poll until ChatGPT stops generating ("Stop generating" button disappears),
|
|
51
|
+
// then read the final response text.
|
|
52
|
+
const pollInterval = 2;
|
|
46
53
|
const maxPolls = Math.ceil(timeout / pollInterval);
|
|
47
54
|
let response = '';
|
|
55
|
+
let generationStarted = false;
|
|
48
56
|
for (let i = 0; i < maxPolls; i++) {
|
|
49
57
|
execSync(`sleep ${pollInterval}`);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (messagesNow.length <= messagesBefore.length)
|
|
58
|
+
const generating = isGenerating();
|
|
59
|
+
if (generating) {
|
|
60
|
+
generationStarted = true;
|
|
54
61
|
continue;
|
|
55
|
-
const newMessages = messagesNow.slice(messagesBefore.length);
|
|
56
|
-
const candidate = [...newMessages].reverse().find((message) => message !== text);
|
|
57
|
-
if (candidate) {
|
|
58
|
-
response = candidate;
|
|
59
|
-
break;
|
|
60
62
|
}
|
|
63
|
+
// Generation finished (or never started yet)
|
|
64
|
+
if (!generationStarted && i < 3)
|
|
65
|
+
continue; // give it a moment to start
|
|
66
|
+
// Read final response
|
|
67
|
+
activateChatGPT(0.3);
|
|
68
|
+
const messagesNow = getVisibleChatMessages();
|
|
69
|
+
if (messagesNow.length > messagesBefore.length) {
|
|
70
|
+
const newMessages = messagesNow.slice(messagesBefore.length);
|
|
71
|
+
const candidate = [...newMessages].reverse().find((message) => message !== text);
|
|
72
|
+
if (candidate)
|
|
73
|
+
response = candidate;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
61
76
|
}
|
|
62
77
|
if (!response) {
|
|
63
78
|
return [
|
|
@@ -1 +1,7 @@
|
|
|
1
|
+
type ModelChoice = 'auto' | 'instant' | 'thinking' | '5.2-instant' | '5.2-thinking';
|
|
2
|
+
export declare const MODEL_CHOICES: ModelChoice[];
|
|
3
|
+
export declare function activateChatGPT(delaySeconds?: number): void;
|
|
4
|
+
export declare function selectModel(model: string): string;
|
|
5
|
+
export declare function isGenerating(): boolean;
|
|
1
6
|
export declare function getVisibleChatMessages(): string[];
|
|
7
|
+
export {};
|
package/dist/clis/chatgpt/ax.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
1
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
2
2
|
const AX_READ_SCRIPT = `
|
|
3
3
|
import Cocoa
|
|
4
4
|
import ApplicationServices
|
|
@@ -60,6 +60,177 @@ for list in lists {
|
|
|
60
60
|
let data = try! JSONSerialization.data(withJSONObject: best, options: [])
|
|
61
61
|
print(String(data: data, encoding: .utf8)!)
|
|
62
62
|
`;
|
|
63
|
+
const AX_MODEL_SCRIPT = `
|
|
64
|
+
import Cocoa
|
|
65
|
+
import ApplicationServices
|
|
66
|
+
|
|
67
|
+
func attr(_ el: AXUIElement, _ name: String) -> AnyObject? {
|
|
68
|
+
var value: CFTypeRef?
|
|
69
|
+
guard AXUIElementCopyAttributeValue(el, name as CFString, &value) == .success else { return nil }
|
|
70
|
+
return value as AnyObject?
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func s(_ el: AXUIElement, _ name: String) -> String? {
|
|
74
|
+
if let v = attr(el, name) as? String, !v.isEmpty { return v }
|
|
75
|
+
return nil
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func children(_ el: AXUIElement) -> [AXUIElement] {
|
|
79
|
+
(attr(el, kAXChildrenAttribute as String) as? [AnyObject] ?? []).map { $0 as! AXUIElement }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func press(_ el: AXUIElement) {
|
|
83
|
+
AXUIElementPerformAction(el, kAXPressAction as CFString)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func findByDesc(_ el: AXUIElement, _ target: String, prefix: Bool = false, depth: Int = 0) -> AXUIElement? {
|
|
87
|
+
guard depth < 20 else { return nil }
|
|
88
|
+
let desc = s(el, kAXDescriptionAttribute as String) ?? ""
|
|
89
|
+
if prefix ? desc.hasPrefix(target) : (desc == target) { return el }
|
|
90
|
+
for c in children(el) {
|
|
91
|
+
if let found = findByDesc(c, target, prefix: prefix, depth: depth + 1) { return found }
|
|
92
|
+
}
|
|
93
|
+
return nil
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func findPopover(_ el: AXUIElement, depth: Int = 0) -> AXUIElement? {
|
|
97
|
+
guard depth < 20 else { return nil }
|
|
98
|
+
let role = s(el, kAXRoleAttribute as String) ?? ""
|
|
99
|
+
if role == "AXPopover" { return el }
|
|
100
|
+
for c in children(el) {
|
|
101
|
+
if let found = findPopover(c, depth: depth + 1) { return found }
|
|
102
|
+
}
|
|
103
|
+
return nil
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func pressEscape() {
|
|
107
|
+
let src = CGEventSource(stateID: .combinedSessionState)
|
|
108
|
+
if let esc = CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: true) { esc.post(tap: .cghidEventTap) }
|
|
109
|
+
if let esc = CGEvent(keyboardEventSource: src, virtualKey: 0x35, keyDown: false) { esc.post(tap: .cghidEventTap) }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
guard let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.openai.chat").first else {
|
|
113
|
+
fputs("ChatGPT not running\\n", stderr); exit(1)
|
|
114
|
+
}
|
|
115
|
+
let axApp = AXUIElementCreateApplication(app.processIdentifier)
|
|
116
|
+
guard let win = attr(axApp, kAXFocusedWindowAttribute as String) as! AXUIElement? else {
|
|
117
|
+
fputs("No focused ChatGPT window\\n", stderr); exit(1)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let args = CommandLine.arguments
|
|
121
|
+
let target = args.count > 1 ? args[1] : ""
|
|
122
|
+
let needsLegacy = args.count > 2 && args[2] == "legacy"
|
|
123
|
+
|
|
124
|
+
// Step 1: Click the "Options" button to open the popover
|
|
125
|
+
guard let optionsBtn = findByDesc(win, "Options") else {
|
|
126
|
+
fputs("Could not find Options button\\n", stderr); exit(1)
|
|
127
|
+
}
|
|
128
|
+
press(optionsBtn)
|
|
129
|
+
Thread.sleep(forTimeInterval: 0.8)
|
|
130
|
+
|
|
131
|
+
// Step 2: Find the popover that appeared, search ONLY within it
|
|
132
|
+
guard let popover = findPopover(win) else {
|
|
133
|
+
pressEscape()
|
|
134
|
+
fputs("Popover did not appear\\n", stderr); exit(1)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Step 3: If legacy, click "Legacy models" to expand submenu
|
|
138
|
+
if needsLegacy {
|
|
139
|
+
guard let legacyBtn = findByDesc(popover, "Legacy models") else {
|
|
140
|
+
pressEscape()
|
|
141
|
+
fputs("Could not find Legacy models button\\n", stderr); exit(1)
|
|
142
|
+
}
|
|
143
|
+
press(legacyBtn)
|
|
144
|
+
Thread.sleep(forTimeInterval: 0.8)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Step 4: Click the target model button within the popover (prefix match)
|
|
148
|
+
guard let modelBtn = findByDesc(popover, target, prefix: true) else {
|
|
149
|
+
pressEscape()
|
|
150
|
+
fputs("Could not find button starting with '\\(target)' in popover\\n", stderr); exit(1)
|
|
151
|
+
}
|
|
152
|
+
press(modelBtn)
|
|
153
|
+
print("Selected: \\(target)")
|
|
154
|
+
`;
|
|
155
|
+
const AX_GENERATING_SCRIPT = `
|
|
156
|
+
import Cocoa
|
|
157
|
+
import ApplicationServices
|
|
158
|
+
|
|
159
|
+
func attr(_ el: AXUIElement, _ name: String) -> AnyObject? {
|
|
160
|
+
var value: CFTypeRef?
|
|
161
|
+
guard AXUIElementCopyAttributeValue(el, name as CFString, &value) == .success else { return nil }
|
|
162
|
+
return value as AnyObject?
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func s(_ el: AXUIElement, _ name: String) -> String? {
|
|
166
|
+
if let v = attr(el, name) as? String, !v.isEmpty { return v }
|
|
167
|
+
return nil
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
func children(_ el: AXUIElement) -> [AXUIElement] {
|
|
171
|
+
(attr(el, kAXChildrenAttribute as String) as? [AnyObject] ?? []).map { $0 as! AXUIElement }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
func hasButton(_ el: AXUIElement, desc target: String, depth: Int = 0) -> Bool {
|
|
175
|
+
guard depth < 15 else { return false }
|
|
176
|
+
let role = s(el, kAXRoleAttribute as String) ?? ""
|
|
177
|
+
let desc = s(el, kAXDescriptionAttribute as String) ?? ""
|
|
178
|
+
if role == "AXButton" && desc == target { return true }
|
|
179
|
+
for c in children(el) {
|
|
180
|
+
if hasButton(c, desc: target, depth: depth + 1) { return true }
|
|
181
|
+
}
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
guard let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.openai.chat").first else {
|
|
186
|
+
print("false"); exit(0)
|
|
187
|
+
}
|
|
188
|
+
let axApp = AXUIElementCreateApplication(app.processIdentifier)
|
|
189
|
+
guard let win = attr(axApp, kAXFocusedWindowAttribute as String) as! AXUIElement? else {
|
|
190
|
+
print("false"); exit(0)
|
|
191
|
+
}
|
|
192
|
+
print(hasButton(win, desc: "Stop generating") ? "true" : "false")
|
|
193
|
+
`;
|
|
194
|
+
const MODEL_MAP = {
|
|
195
|
+
'auto': { desc: 'Auto' },
|
|
196
|
+
'instant': { desc: 'Instant' },
|
|
197
|
+
'thinking': { desc: 'Thinking' },
|
|
198
|
+
'5.2-instant': { desc: 'GPT-5.2 Instant', legacy: true },
|
|
199
|
+
'5.2-thinking': { desc: 'GPT-5.2 Thinking', legacy: true },
|
|
200
|
+
};
|
|
201
|
+
export const MODEL_CHOICES = Object.keys(MODEL_MAP);
|
|
202
|
+
export function activateChatGPT(delaySeconds = 0.5) {
|
|
203
|
+
execSync("osascript -e 'tell application \"ChatGPT\" to activate'");
|
|
204
|
+
execSync(`osascript -e 'delay ${delaySeconds}'`);
|
|
205
|
+
}
|
|
206
|
+
export function selectModel(model) {
|
|
207
|
+
const entry = MODEL_MAP[model];
|
|
208
|
+
if (!entry) {
|
|
209
|
+
throw new Error(`Unknown model "${model}". Choose from: ${MODEL_CHOICES.join(', ')}`);
|
|
210
|
+
}
|
|
211
|
+
const swiftArgs = ['-', entry.desc];
|
|
212
|
+
if (entry.legacy)
|
|
213
|
+
swiftArgs.push('legacy');
|
|
214
|
+
const output = execFileSync('swift', swiftArgs, {
|
|
215
|
+
input: AX_MODEL_SCRIPT,
|
|
216
|
+
encoding: 'utf-8',
|
|
217
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
218
|
+
}).trim();
|
|
219
|
+
return output;
|
|
220
|
+
}
|
|
221
|
+
export function isGenerating() {
|
|
222
|
+
try {
|
|
223
|
+
const output = execFileSync('swift', ['-'], {
|
|
224
|
+
input: AX_GENERATING_SCRIPT,
|
|
225
|
+
encoding: 'utf-8',
|
|
226
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
227
|
+
}).trim();
|
|
228
|
+
return output === 'true';
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
63
234
|
export function getVisibleChatMessages() {
|
|
64
235
|
const output = execFileSync('swift', ['-'], {
|
|
65
236
|
input: AX_READ_SCRIPT,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const modelCommand: import("../../registry.js").CliCommand;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { ConfigError } from '../../errors.js';
|
|
3
|
+
import { activateChatGPT, selectModel, MODEL_CHOICES } from './ax.js';
|
|
4
|
+
export const modelCommand = cli({
|
|
5
|
+
site: 'chatgpt',
|
|
6
|
+
name: 'model',
|
|
7
|
+
description: 'Switch ChatGPT Desktop model/mode (auto, instant, thinking, 5.2-instant, 5.2-thinking)',
|
|
8
|
+
domain: 'localhost',
|
|
9
|
+
strategy: Strategy.PUBLIC,
|
|
10
|
+
browser: false,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'model', required: true, positional: true, help: 'Model to switch to', choices: MODEL_CHOICES },
|
|
13
|
+
],
|
|
14
|
+
columns: ['Status', 'Model'],
|
|
15
|
+
func: async (page, kwargs) => {
|
|
16
|
+
if (process.platform !== 'darwin') {
|
|
17
|
+
throw new ConfigError('ChatGPT Desktop integration requires macOS');
|
|
18
|
+
}
|
|
19
|
+
const model = kwargs.model;
|
|
20
|
+
activateChatGPT();
|
|
21
|
+
const result = selectModel(model);
|
|
22
|
+
return [{ Status: 'Success', Model: result }];
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execSync, spawnSync } from 'node:child_process';
|
|
2
2
|
import { cli, Strategy } from '../../registry.js';
|
|
3
3
|
import { getErrorMessage } from '../../errors.js';
|
|
4
|
+
import { activateChatGPT, selectModel, MODEL_CHOICES } from './ax.js';
|
|
4
5
|
export const sendCommand = cli({
|
|
5
6
|
site: 'chatgpt',
|
|
6
7
|
name: 'send',
|
|
@@ -8,11 +9,20 @@ export const sendCommand = cli({
|
|
|
8
9
|
domain: 'localhost',
|
|
9
10
|
strategy: Strategy.PUBLIC,
|
|
10
11
|
browser: false,
|
|
11
|
-
args: [
|
|
12
|
+
args: [
|
|
13
|
+
{ name: 'text', required: true, positional: true, help: 'Message to send' },
|
|
14
|
+
{ name: 'model', required: false, help: 'Model/mode to use: auto, instant, thinking, 5.2-instant, 5.2-thinking', choices: MODEL_CHOICES },
|
|
15
|
+
],
|
|
12
16
|
columns: ['Status'],
|
|
13
17
|
func: async (page, kwargs) => {
|
|
14
18
|
const text = kwargs.text;
|
|
19
|
+
const model = kwargs.model;
|
|
15
20
|
try {
|
|
21
|
+
// Switch model before sending if requested
|
|
22
|
+
if (model) {
|
|
23
|
+
activateChatGPT();
|
|
24
|
+
selectModel(model);
|
|
25
|
+
}
|
|
16
26
|
// Backup current clipboard content
|
|
17
27
|
let clipBackup = '';
|
|
18
28
|
try {
|
|
@@ -21,8 +31,7 @@ export const sendCommand = cli({
|
|
|
21
31
|
catch { /* clipboard may be empty */ }
|
|
22
32
|
// Copy text to clipboard
|
|
23
33
|
spawnSync('pbcopy', { input: text });
|
|
24
|
-
|
|
25
|
-
execSync("osascript -e 'delay 0.5'");
|
|
34
|
+
activateChatGPT();
|
|
26
35
|
const cmd = "osascript " +
|
|
27
36
|
"-e 'tell application \"System Events\"' " +
|
|
28
37
|
"-e 'keystroke \"v\" using command down' " +
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { formatBytes } from '../../download/progress.js';
|
|
4
|
+
import { httpDownload, sanitizeFilename } from '../../download/index.js';
|
|
5
|
+
import { EmptyResultError } from '../../errors.js';
|
|
6
|
+
import { cli, Strategy } from '../../registry.js';
|
|
7
|
+
import { getDoubanPhotoExtension, loadDoubanSubjectPhotos, normalizeDoubanSubjectId } from './utils.js';
|
|
8
|
+
function buildDoubanPhotoFilename(subjectId, photo) {
|
|
9
|
+
const index = String(photo.index).padStart(3, '0');
|
|
10
|
+
const suffix = sanitizeFilename(photo.title || photo.photoId || 'photo', 80) || 'photo';
|
|
11
|
+
return `${subjectId}_${index}_${photo.photoId || 'photo'}_${suffix}${getDoubanPhotoExtension(photo.imageUrl)}`;
|
|
12
|
+
}
|
|
13
|
+
cli({
|
|
14
|
+
site: 'douban',
|
|
15
|
+
name: 'download',
|
|
16
|
+
description: '下载电影海报/剧照图片',
|
|
17
|
+
domain: 'movie.douban.com',
|
|
18
|
+
strategy: Strategy.COOKIE,
|
|
19
|
+
args: [
|
|
20
|
+
{ name: 'id', positional: true, required: true, help: '电影 subject ID' },
|
|
21
|
+
{ name: 'type', default: 'Rb', help: '豆瓣 photos 的 type 参数,默认 Rb(海报)' },
|
|
22
|
+
{ name: 'limit', type: 'int', default: 120, help: '最多下载多少张图片' },
|
|
23
|
+
{ name: 'photo-id', help: '只下载指定 photo_id 的图片' },
|
|
24
|
+
{ name: 'output', default: './douban-downloads', help: '输出目录' },
|
|
25
|
+
],
|
|
26
|
+
columns: ['index', 'title', 'status', 'size'],
|
|
27
|
+
func: async (page, kwargs) => {
|
|
28
|
+
const subjectId = normalizeDoubanSubjectId(String(kwargs.id || ''));
|
|
29
|
+
const output = String(kwargs.output || './douban-downloads');
|
|
30
|
+
const requestedPhotoId = String(kwargs['photo-id'] || '').trim();
|
|
31
|
+
const loadOptions = {
|
|
32
|
+
type: String(kwargs.type || 'Rb'),
|
|
33
|
+
};
|
|
34
|
+
if (requestedPhotoId)
|
|
35
|
+
loadOptions.targetPhotoId = requestedPhotoId;
|
|
36
|
+
else
|
|
37
|
+
loadOptions.limit = Number(kwargs.limit) || 120;
|
|
38
|
+
const data = await loadDoubanSubjectPhotos(page, subjectId, loadOptions);
|
|
39
|
+
const photos = requestedPhotoId
|
|
40
|
+
? data.photos.filter((photo) => photo.photoId === requestedPhotoId)
|
|
41
|
+
: data.photos;
|
|
42
|
+
if (requestedPhotoId && !photos.length) {
|
|
43
|
+
throw new EmptyResultError('douban download', `Photo ID ${requestedPhotoId} was not found under subject ${subjectId}. Try "douban photos ${subjectId} -f json" first.`);
|
|
44
|
+
}
|
|
45
|
+
const outputDir = path.join(output, subjectId);
|
|
46
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
47
|
+
const results = [];
|
|
48
|
+
for (const photo of photos) {
|
|
49
|
+
const filename = buildDoubanPhotoFilename(subjectId, photo);
|
|
50
|
+
const destPath = path.join(outputDir, filename);
|
|
51
|
+
const result = await httpDownload(photo.imageUrl, destPath, {
|
|
52
|
+
headers: { Referer: photo.detailUrl || `https://movie.douban.com/subject/${subjectId}/photos?type=${encodeURIComponent(String(kwargs.type || 'Rb'))}` },
|
|
53
|
+
timeout: 60000,
|
|
54
|
+
});
|
|
55
|
+
results.push({
|
|
56
|
+
index: photo.index,
|
|
57
|
+
title: photo.title,
|
|
58
|
+
photo_id: photo.photoId,
|
|
59
|
+
image_url: photo.imageUrl,
|
|
60
|
+
detail_url: photo.detailUrl,
|
|
61
|
+
status: result.success ? 'success' : 'failed',
|
|
62
|
+
size: result.success ? formatBytes(result.size) : (result.error || 'unknown error'),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|