@jackwener/opencli 1.7.12 → 1.7.14

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.
Files changed (419) hide show
  1. package/README.md +8 -7
  2. package/README.zh-CN.md +9 -8
  3. package/cli-manifest.json +12128 -6665
  4. package/clis/1point3acres/digest.js +35 -0
  5. package/clis/1point3acres/forum.js +51 -0
  6. package/clis/1point3acres/forums.js +44 -0
  7. package/clis/1point3acres/hot.js +35 -0
  8. package/clis/1point3acres/latest.js +35 -0
  9. package/clis/1point3acres/notifications.js +64 -0
  10. package/clis/1point3acres/search.js +71 -0
  11. package/clis/1point3acres/thread.js +117 -0
  12. package/clis/1point3acres/user.js +77 -0
  13. package/clis/1point3acres/utils.js +247 -0
  14. package/clis/_shared/desktop-commands.js +4 -0
  15. package/clis/aibase/news.js +110 -0
  16. package/clis/aibase/news.test.js +59 -0
  17. package/clis/amazon/discussion.test.js +1 -28
  18. package/clis/antigravity/watch.js +3 -2
  19. package/clis/arxiv/author.js +44 -0
  20. package/clis/baidu-scholar/search.js +0 -1
  21. package/clis/bbc/topic.js +57 -0
  22. package/clis/bbc/utils.js +79 -0
  23. package/clis/chaoxing/assignments.js +1 -1
  24. package/clis/chaoxing/exams.js +1 -1
  25. package/clis/chatgpt/ask.js +57 -0
  26. package/clis/chatgpt/commands.test.js +45 -0
  27. package/clis/chatgpt/detail.js +46 -0
  28. package/clis/chatgpt/history.js +39 -0
  29. package/clis/chatgpt/image.js +12 -11
  30. package/clis/chatgpt/image.test.js +23 -0
  31. package/clis/chatgpt/new.js +25 -0
  32. package/clis/chatgpt/read.js +43 -0
  33. package/clis/chatgpt/send.js +46 -0
  34. package/clis/chatgpt/status.js +29 -0
  35. package/clis/chatgpt/utils.js +294 -4
  36. package/clis/chatgpt/utils.test.js +13 -0
  37. package/clis/chatgpt-app/ask.js +6 -3
  38. package/clis/chatwise/ask.js +16 -43
  39. package/clis/chatwise/composer.test.js +186 -0
  40. package/clis/chatwise/send.js +2 -24
  41. package/clis/chatwise/utils.js +143 -0
  42. package/clis/claude/ask.js +1 -1
  43. package/clis/claude/detail.js +1 -0
  44. package/clis/claude/history.js +1 -0
  45. package/clis/claude/new.js +1 -0
  46. package/clis/claude/read.js +1 -0
  47. package/clis/claude/send.js +1 -0
  48. package/clis/claude/status.js +1 -0
  49. package/clis/codex/ask.js +15 -9
  50. package/clis/codex/history.js +16 -33
  51. package/clis/codex/projects.js +28 -0
  52. package/clis/codex/read.js +10 -4
  53. package/clis/codex/send.js +10 -3
  54. package/clis/codex/sidebar.js +356 -0
  55. package/clis/codex/sidebar.test.js +329 -0
  56. package/clis/coingecko/categories.js +75 -0
  57. package/clis/coingecko/coin.js +107 -0
  58. package/clis/coingecko/coingecko.test.js +109 -0
  59. package/clis/coingecko/derivatives.js +84 -0
  60. package/clis/coingecko/exchanges.js +74 -0
  61. package/clis/coingecko/global.js +71 -0
  62. package/clis/coingecko/top.js +64 -0
  63. package/clis/coingecko/trending.js +55 -0
  64. package/clis/coupang/add-to-cart.js +21 -13
  65. package/clis/coupang/coupang.test.js +159 -0
  66. package/clis/coupang/product.js +257 -0
  67. package/clis/coupang/search.js +38 -16
  68. package/clis/coupang/utils.js +55 -1
  69. package/clis/crates/crate.js +62 -0
  70. package/clis/crates/search.js +44 -0
  71. package/clis/crates/utils.js +72 -0
  72. package/clis/ctrip/ctrip.test.js +234 -0
  73. package/clis/ctrip/hotel-suggest.js +45 -0
  74. package/clis/ctrip/search.js +22 -68
  75. package/clis/ctrip/utils.js +175 -0
  76. package/clis/cursor/ask.js +6 -3
  77. package/clis/dblp/author.js +133 -0
  78. package/clis/dblp/venue.js +64 -0
  79. package/clis/deepseek/ask.js +12 -7
  80. package/clis/deepseek/ask.test.js +13 -13
  81. package/clis/deepseek/detail.js +38 -0
  82. package/clis/deepseek/detail.test.js +81 -0
  83. package/clis/deepseek/history.js +1 -0
  84. package/clis/deepseek/new.js +1 -0
  85. package/clis/deepseek/read.js +1 -0
  86. package/clis/deepseek/send.js +140 -0
  87. package/clis/deepseek/send.test.js +107 -0
  88. package/clis/deepseek/status.js +1 -0
  89. package/clis/deepseek/utils.js +66 -0
  90. package/clis/deepseek/utils.test.js +107 -1
  91. package/clis/defillama/defillama.test.js +99 -0
  92. package/clis/defillama/protocol.js +84 -0
  93. package/clis/defillama/protocols.js +55 -0
  94. package/clis/defillama/utils.js +99 -0
  95. package/clis/devto/latest.js +74 -0
  96. package/clis/dockerhub/image.js +52 -0
  97. package/clis/dockerhub/search.js +47 -0
  98. package/clis/dockerhub/utils.js +100 -0
  99. package/clis/doubao/ask.js +7 -3
  100. package/clis/doubao/detail.js +1 -0
  101. package/clis/doubao/history.js +1 -0
  102. package/clis/doubao/meeting-summary.js +1 -0
  103. package/clis/doubao/meeting-transcript.js +1 -0
  104. package/clis/doubao/new.js +1 -0
  105. package/clis/doubao/read.js +1 -0
  106. package/clis/doubao/send.js +1 -0
  107. package/clis/doubao/status.js +1 -0
  108. package/clis/douyin/draft.test.js +1 -30
  109. package/clis/endoflife/endoflife.test.js +51 -0
  110. package/clis/endoflife/product.js +55 -0
  111. package/clis/endoflife/utils.js +89 -0
  112. package/clis/facebook/__fixtures__/notifications-page.html +13 -0
  113. package/clis/facebook/notifications.js +326 -30
  114. package/clis/facebook/notifications.test.js +458 -0
  115. package/clis/flathub/app.js +71 -0
  116. package/clis/flathub/flathub.test.js +90 -0
  117. package/clis/flathub/search.js +80 -0
  118. package/clis/flathub/utils.js +114 -0
  119. package/clis/gemini/ask.js +7 -3
  120. package/clis/gemini/ask.test.js +2 -2
  121. package/clis/gemini/deep-research-result.js +6 -2
  122. package/clis/gemini/deep-research-result.test.js +15 -14
  123. package/clis/gemini/deep-research.js +8 -4
  124. package/clis/gemini/deep-research.test.js +15 -18
  125. package/clis/gemini/image.js +7 -2
  126. package/clis/gemini/new.js +1 -0
  127. package/clis/gemini/utils.js +0 -4
  128. package/clis/google-scholar/cite.js +0 -1
  129. package/clis/google-scholar/profile.js +0 -1
  130. package/clis/google-scholar/search.js +0 -1
  131. package/clis/goproxy/goproxy.test.js +103 -0
  132. package/clis/goproxy/module.js +47 -0
  133. package/clis/goproxy/utils.js +165 -0
  134. package/clis/goproxy/versions.js +59 -0
  135. package/clis/gov-law/recent.js +0 -1
  136. package/clis/gov-law/search.js +0 -1
  137. package/clis/gov-policy/__fixtures__/recent.html +16 -0
  138. package/clis/gov-policy/__fixtures__/search.html +41 -0
  139. package/clis/gov-policy/gov-policy.test.js +224 -0
  140. package/clis/gov-policy/recent.js +66 -24
  141. package/clis/gov-policy/search.js +65 -23
  142. package/clis/gov-policy/utils.js +54 -0
  143. package/clis/grok/ask.js +49 -265
  144. package/clis/grok/ask.test.js +21 -46
  145. package/clis/grok/detail.js +60 -0
  146. package/clis/grok/history.js +48 -0
  147. package/clis/grok/{image.ts → image.js} +56 -70
  148. package/clis/grok/image.test.ts +20 -0
  149. package/clis/grok/new.js +20 -0
  150. package/clis/grok/read.js +39 -0
  151. package/clis/grok/send.js +50 -0
  152. package/clis/grok/status.js +41 -0
  153. package/clis/grok/utils.js +326 -0
  154. package/clis/grok/utils.test.js +103 -0
  155. package/clis/hf/datasets.js +88 -0
  156. package/clis/hf/hf.test.js +16 -0
  157. package/clis/hf/models.js +91 -0
  158. package/clis/hf/paper.js +79 -0
  159. package/clis/hf/spaces.js +101 -0
  160. package/clis/hf/top.js +1 -0
  161. package/clis/homebrew/cask.js +39 -0
  162. package/clis/homebrew/formula.js +41 -0
  163. package/clis/homebrew/popular.js +54 -0
  164. package/clis/homebrew/utils.js +100 -0
  165. package/clis/hupu/__fixtures__/hot-home.html +64 -0
  166. package/clis/hupu/detail.js +0 -1
  167. package/clis/hupu/hot.js +156 -35
  168. package/clis/hupu/hot.test.js +224 -0
  169. package/clis/hupu/search.js +0 -1
  170. package/clis/instagram/note.js +1 -1
  171. package/clis/instagram/note.test.js +1 -29
  172. package/clis/instagram/post.js +1 -1
  173. package/clis/instagram/post.test.js +1 -1
  174. package/clis/instagram/reel.js +1 -1
  175. package/clis/instagram/story.js +1 -1
  176. package/clis/instagram/story.test.js +1 -34
  177. package/clis/jd/commands.test.js +1 -24
  178. package/clis/lichess/lichess.test.js +85 -0
  179. package/clis/lichess/top.js +46 -0
  180. package/clis/lichess/user.js +91 -0
  181. package/clis/lichess/utils.js +97 -0
  182. package/clis/linkedin/search.js +107 -10
  183. package/clis/linkedin/search.test.js +222 -0
  184. package/clis/linux-do/feed.js +2 -5
  185. package/clis/linux-do/feed.test.js +35 -0
  186. package/clis/lobsters/domain.js +92 -0
  187. package/clis/maven/artifact.js +49 -0
  188. package/clis/maven/search.js +51 -0
  189. package/clis/maven/utils.js +110 -0
  190. package/clis/mdn/search.js +97 -0
  191. package/clis/medium/tag.js +135 -0
  192. package/clis/npm/downloads.js +59 -0
  193. package/clis/npm/package.js +70 -0
  194. package/clis/npm/search.js +49 -0
  195. package/clis/npm/utils.js +76 -0
  196. package/clis/nuget/nuget.test.js +111 -0
  197. package/clis/nuget/package.js +101 -0
  198. package/clis/nuget/search.js +69 -0
  199. package/clis/nuget/utils.js +87 -0
  200. package/clis/nvd/cve.js +121 -0
  201. package/clis/oeis/oeis.test.js +88 -0
  202. package/clis/oeis/search.js +63 -0
  203. package/clis/oeis/sequence.js +71 -0
  204. package/clis/oeis/utils.js +88 -0
  205. package/clis/openalex/search.js +69 -0
  206. package/clis/openalex/utils.js +160 -0
  207. package/clis/openalex/work.js +65 -0
  208. package/clis/openfda/drug-label.js +74 -0
  209. package/clis/openfda/food-recall.js +65 -0
  210. package/clis/openfda/openfda.test.js +114 -0
  211. package/clis/openfda/utils.js +67 -0
  212. package/clis/osv/osv.test.js +97 -0
  213. package/clis/osv/query.js +72 -0
  214. package/clis/osv/utils.js +169 -0
  215. package/clis/osv/vulnerability.js +54 -0
  216. package/clis/packagist/package.js +49 -0
  217. package/clis/packagist/search.js +43 -0
  218. package/clis/packagist/utils.js +113 -0
  219. package/clis/paperreview/feedback.js +1 -1
  220. package/clis/paperreview/review.js +1 -1
  221. package/clis/paperreview/submit.js +1 -1
  222. package/clis/pixiv/download.test.js +1 -1
  223. package/clis/pixiv/illusts.test.js +1 -1
  224. package/clis/pixiv/search.test.js +1 -1
  225. package/clis/pubmed/article.js +50 -0
  226. package/clis/pubmed/author.js +64 -0
  227. package/clis/pubmed/citations.js +36 -0
  228. package/clis/pubmed/pubmed.test.js +276 -0
  229. package/clis/pubmed/related.js +45 -0
  230. package/clis/pubmed/search.js +75 -0
  231. package/clis/pubmed/utils.js +309 -0
  232. package/clis/pypi/downloads.js +66 -0
  233. package/clis/pypi/package.js +79 -0
  234. package/clis/pypi/utils.js +55 -0
  235. package/clis/quark/mv.js +1 -1
  236. package/clis/quark/save.js +1 -1
  237. package/clis/qwen/ask.js +85 -0
  238. package/clis/qwen/detail.js +62 -0
  239. package/clis/qwen/history.js +61 -0
  240. package/clis/qwen/image.js +179 -0
  241. package/clis/qwen/new.js +23 -0
  242. package/clis/qwen/read.js +41 -0
  243. package/clis/qwen/send.js +55 -0
  244. package/clis/qwen/status.js +37 -0
  245. package/clis/qwen/utils.js +409 -0
  246. package/clis/qwen/utils.test.js +45 -0
  247. package/clis/rest-countries/country.js +65 -0
  248. package/clis/rest-countries/region.js +64 -0
  249. package/clis/rest-countries/rest-countries.test.js +83 -0
  250. package/clis/rest-countries/utils.js +126 -0
  251. package/clis/reuters/article-detail.js +53 -0
  252. package/clis/reuters/reuters.test.js +299 -0
  253. package/clis/reuters/search.js +45 -34
  254. package/clis/reuters/utils.js +159 -0
  255. package/clis/rfc/rfc.js +52 -0
  256. package/clis/rfc/rfc.test.js +74 -0
  257. package/clis/rfc/utils.js +72 -0
  258. package/clis/rubygems/gem.js +42 -0
  259. package/clis/rubygems/search.js +47 -0
  260. package/clis/rubygems/utils.js +86 -0
  261. package/clis/stackoverflow/related.js +66 -0
  262. package/clis/stackoverflow/stackoverflow.test.js +58 -0
  263. package/clis/stackoverflow/tag.js +60 -0
  264. package/clis/stackoverflow/user.js +50 -0
  265. package/clis/stackoverflow/utils.js +118 -0
  266. package/clis/steam/app.js +67 -0
  267. package/clis/steam/search.js +58 -0
  268. package/clis/steam/steam.test.js +46 -0
  269. package/clis/steam/utils.js +107 -0
  270. package/clis/taobao/commands.test.js +1 -24
  271. package/clis/test-utils.js +61 -0
  272. package/clis/tieba/hot.js +0 -1
  273. package/clis/tiktok/comment.js +128 -41
  274. package/clis/tiktok/creator-videos.js +270 -0
  275. package/clis/tiktok/creator-videos.test.js +113 -0
  276. package/clis/tiktok/explore.js +137 -29
  277. package/clis/tiktok/follow.js +115 -33
  278. package/clis/tiktok/following.js +157 -36
  279. package/clis/tiktok/friends.js +139 -37
  280. package/clis/tiktok/live.js +137 -41
  281. package/clis/tiktok/notifications.js +141 -38
  282. package/clis/tiktok/refactor.test.js +389 -0
  283. package/clis/tiktok/unfollow.js +124 -38
  284. package/clis/tiktok/user.js +203 -29
  285. package/clis/tiktok/utils.js +505 -0
  286. package/clis/tiktok/write-refactor.test.js +370 -0
  287. package/clis/toutiao/articles.js +36 -62
  288. package/clis/toutiao/hot.js +63 -0
  289. package/clis/toutiao/toutiao.test.js +378 -0
  290. package/clis/toutiao/utils.js +161 -0
  291. package/clis/tvmaze/search.js +61 -0
  292. package/clis/tvmaze/show.js +60 -0
  293. package/clis/tvmaze/tvmaze.test.js +93 -0
  294. package/clis/tvmaze/utils.js +110 -0
  295. package/clis/twitter/accept.js +1 -1
  296. package/clis/twitter/followers.js +134 -69
  297. package/clis/twitter/quote.js +139 -0
  298. package/clis/twitter/quote.test.js +106 -0
  299. package/clis/twitter/reply-dm.js +1 -1
  300. package/clis/twitter/reply.test.js +1 -29
  301. package/clis/twitter/retweet.js +99 -0
  302. package/clis/twitter/retweet.test.js +69 -0
  303. package/clis/twitter/shared.js +38 -0
  304. package/clis/twitter/shared.test.js +28 -1
  305. package/clis/twitter/unlike.js +87 -0
  306. package/clis/twitter/unlike.test.js +72 -0
  307. package/clis/twitter/unretweet.js +99 -0
  308. package/clis/twitter/unretweet.test.js +69 -0
  309. package/clis/uisdc/news.js +105 -0
  310. package/clis/uisdc/news.test.js +66 -0
  311. package/clis/wanfang/search.js +0 -1
  312. package/clis/web/read.js +47 -17
  313. package/clis/web/read.test.js +101 -1
  314. package/clis/weixin/create-draft.js +1 -1
  315. package/clis/weixin/drafts.js +1 -1
  316. package/clis/weixin/drafts.test.js +5 -1
  317. package/clis/weixin/search.js +157 -0
  318. package/clis/weixin/search.test.js +227 -0
  319. package/clis/wikidata/entity.js +60 -0
  320. package/clis/wikidata/search.js +50 -0
  321. package/clis/wikidata/utils.js +117 -0
  322. package/clis/wikidata/wikidata.test.js +83 -0
  323. package/clis/wikipedia/page.js +95 -0
  324. package/clis/wttr/current.js +63 -0
  325. package/clis/wttr/forecast.js +71 -0
  326. package/clis/wttr/utils.js +50 -0
  327. package/clis/wttr/wttr.test.js +84 -0
  328. package/clis/xianyu/chat.js +16 -4
  329. package/clis/xianyu/chat.test.js +64 -0
  330. package/clis/xianyu/publish.js +485 -0
  331. package/clis/xianyu/publish.test.js +220 -0
  332. package/clis/xiaoe/catalog.js +105 -40
  333. package/clis/xiaoe/content.js +164 -29
  334. package/clis/xiaoe/courses.js +86 -29
  335. package/clis/xiaoe/xiaoe.test.js +486 -0
  336. package/clis/xiaohongshu/creator-notes-summary.js +1 -1
  337. package/clis/xiaohongshu/publish.js +16 -3
  338. package/clis/xiaohongshu/publish.test.js +46 -1
  339. package/clis/youtube/transcript.js +13 -19
  340. package/clis/youtube/transcript.test.js +17 -0
  341. package/clis/yuanbao/ask.js +17 -66
  342. package/clis/yuanbao/ask.test.js +5 -5
  343. package/clis/yuanbao/detail.js +65 -0
  344. package/clis/yuanbao/history.js +51 -0
  345. package/clis/yuanbao/new.js +1 -0
  346. package/clis/yuanbao/read.js +38 -0
  347. package/clis/yuanbao/send.js +57 -0
  348. package/clis/yuanbao/shared.js +297 -5
  349. package/clis/yuanbao/shared.test.js +80 -0
  350. package/clis/yuanbao/status.js +44 -0
  351. package/clis/zlibrary/commands.test.js +1 -11
  352. package/dist/src/browser/base-page.d.ts +9 -0
  353. package/dist/src/browser/base-page.js +44 -1
  354. package/dist/src/browser/base-page.test.js +66 -0
  355. package/dist/src/browser/bridge.js +47 -45
  356. package/dist/src/browser/cdp.d.ts +1 -0
  357. package/dist/src/browser/cdp.js +51 -9
  358. package/dist/src/browser/daemon-client.d.ts +4 -0
  359. package/dist/src/browser/errors.js +1 -1
  360. package/dist/src/browser/page.d.ts +1 -1
  361. package/dist/src/browser/page.js +3 -1
  362. package/dist/src/browser/page.test.js +29 -0
  363. package/dist/src/browser/target-errors.d.ts +2 -1
  364. package/dist/src/browser/target-errors.js +1 -0
  365. package/dist/src/browser/target-resolver.d.ts +25 -0
  366. package/dist/src/browser/target-resolver.js +43 -0
  367. package/dist/src/browser.test.js +18 -0
  368. package/dist/src/build-manifest.js +9 -4
  369. package/dist/src/build-manifest.test.js +2 -8
  370. package/dist/src/capabilityRouting.d.ts +16 -1
  371. package/dist/src/capabilityRouting.js +24 -1
  372. package/dist/src/capabilityRouting.test.js +19 -1
  373. package/dist/src/cli.js +76 -11
  374. package/dist/src/cli.test.js +241 -1
  375. package/dist/src/commanderAdapter.js +23 -9
  376. package/dist/src/commanderAdapter.test.js +0 -1
  377. package/dist/src/discovery.js +2 -5
  378. package/dist/src/errors.js +1 -1
  379. package/dist/src/execution.d.ts +1 -1
  380. package/dist/src/execution.js +111 -27
  381. package/dist/src/execution.test.js +326 -17
  382. package/dist/src/help.d.ts +27 -2
  383. package/dist/src/help.js +196 -23
  384. package/dist/src/help.test.d.ts +1 -0
  385. package/dist/src/help.test.js +54 -0
  386. package/dist/src/main.js +14 -1
  387. package/dist/src/manifest-types.d.ts +5 -3
  388. package/dist/src/pipeline/executor.js +1 -1
  389. package/dist/src/pipeline/executor.test.js +8 -0
  390. package/dist/src/pipeline/registry.d.ts +9 -0
  391. package/dist/src/pipeline/registry.js +13 -1
  392. package/dist/src/pipeline/steps/browser.d.ts +1 -0
  393. package/dist/src/pipeline/steps/browser.js +10 -0
  394. package/dist/src/pipeline/steps/download.test.js +1 -0
  395. package/dist/src/registry-api.d.ts +1 -1
  396. package/dist/src/registry.d.ts +12 -11
  397. package/dist/src/registry.js +16 -6
  398. package/dist/src/registry.test.js +2 -2
  399. package/dist/src/runtime.d.ts +2 -1
  400. package/dist/src/runtime.js +1 -1
  401. package/dist/src/serialization.d.ts +2 -2
  402. package/dist/src/serialization.js +4 -6
  403. package/dist/src/serialization.test.js +17 -0
  404. package/dist/src/types.d.ts +17 -0
  405. package/dist/src/validate.js +15 -11
  406. package/dist/src/validate.test.d.ts +9 -0
  407. package/dist/src/validate.test.js +90 -0
  408. package/package.json +1 -1
  409. package/scripts/fetch-adapters.js +1 -1
  410. package/scripts/typed-error-lint-baseline.json +5 -77
  411. package/clis/ctrip/search.test.js +0 -64
  412. package/clis/gov-policy/commands.test.js +0 -27
  413. package/clis/linux-do/category.js +0 -37
  414. package/clis/linux-do/hot.js +0 -26
  415. package/clis/linux-do/latest.js +0 -19
  416. package/clis/pixiv/test-utils.js +0 -23
  417. package/clis/toutiao/articles.test.js +0 -30
  418. package/dist/src/analysis.d.ts +0 -40
  419. package/dist/src/analysis.js +0 -172
@@ -1,5 +1,6 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
2
  import { selectorError } from '@jackwener/opencli/errors';
3
+ import { buildChatwiseInjectTextJs } from './utils.js';
3
4
  export const sendCommand = cli({
4
5
  site: 'chatwise',
5
6
  name: 'send',
@@ -12,30 +13,7 @@ export const sendCommand = cli({
12
13
  columns: ['Status', 'InjectedText'],
13
14
  func: async (page, kwargs) => {
14
15
  const text = kwargs.text;
15
- const injected = await page.evaluate(`
16
- (function(text) {
17
- // ChatWise input can be textarea or contenteditable
18
- let composer = document.querySelector('textarea');
19
- if (!composer) {
20
- const editables = Array.from(document.querySelectorAll('[contenteditable="true"]'));
21
- composer = editables.length > 0 ? editables[editables.length - 1] : null;
22
- }
23
-
24
- if (!composer) return false;
25
-
26
- composer.focus();
27
-
28
- if (composer.tagName === 'TEXTAREA') {
29
- // For textarea, set value and dispatch input event
30
- const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
31
- nativeInputValueSetter.call(composer, text);
32
- composer.dispatchEvent(new Event('input', { bubbles: true }));
33
- } else {
34
- document.execCommand('insertText', false, text);
35
- }
36
- return true;
37
- })(${JSON.stringify(text)})
38
- `);
16
+ const injected = await page.evaluate(buildChatwiseInjectTextJs(text));
39
17
  if (!injected)
40
18
  throw selectorError('ChatWise input element');
41
19
  await page.wait(0.5);
@@ -0,0 +1,143 @@
1
+ import { ArgumentError } from '@jackwener/opencli/errors';
2
+
3
+ export const MESSAGE_WRAPPER_SELECTOR = '[class*="group/message"]';
4
+ export const MIN_COMPOSER_SCORE = 120;
5
+
6
+ export function requirePositiveTimeout(value) {
7
+ const timeout = value;
8
+ if (!Number.isInteger(timeout) || timeout <= 0) {
9
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
10
+ }
11
+ return timeout;
12
+ }
13
+
14
+ export function scoreChatwiseComposerCandidate(candidate, viewportHeight = 0) {
15
+ if (candidate.hidden) return -1000;
16
+
17
+ let score = 0;
18
+ const normalizedRole = String(candidate.role || '').toLowerCase();
19
+ if (normalizedRole === 'textbox') score += 10;
20
+
21
+ const normalizedClasses = `${candidate.classes || ''} ${candidate.editorClasses || ''} ${candidate.ariaLabel || ''}`.toLowerCase();
22
+ if (normalizedClasses.includes('cm-content')) score += 20;
23
+ if (normalizedClasses.includes('cm-editor')) score += 30;
24
+ if (normalizedClasses.includes('simple-editor')) score -= 140;
25
+
26
+ const searchableText = `${candidate.placeholder || ''} ${candidate.ariaLabel || ''} ${candidate.text || ''}`.toLowerCase();
27
+ if (searchableText.includes('enter a message here')) score += 220;
28
+ if (searchableText.includes('press ⏎ to send')) score += 80;
29
+ if (searchableText.includes('press enter to send')) score += 80;
30
+ if (searchableText.includes('message')) score += 20;
31
+ if (searchableText.includes('optional description')) score -= 140;
32
+ if (searchableText.includes('user context document')) score -= 220;
33
+
34
+ if (viewportHeight > 0 && candidate.rect) {
35
+ const bottom = candidate.rect.y + candidate.rect.h;
36
+ const distanceFromBottom = Math.abs(viewportHeight - bottom);
37
+ score += Math.max(0, 80 - distanceFromBottom / 8);
38
+ }
39
+
40
+ return score;
41
+ }
42
+
43
+ export function selectBestChatwiseComposer(candidates, viewportHeight = 0, minScore = MIN_COMPOSER_SCORE) {
44
+ if (!Array.isArray(candidates) || candidates.length === 0) return null;
45
+ const best = [...candidates]
46
+ .sort((left, right) => {
47
+ const delta = scoreChatwiseComposerCandidate(right, viewportHeight)
48
+ - scoreChatwiseComposerCandidate(left, viewportHeight);
49
+ return delta !== 0 ? delta : left.index - right.index;
50
+ })[0] ?? null;
51
+ if (!best || scoreChatwiseComposerCandidate(best, viewportHeight) < minScore) return null;
52
+ return best;
53
+ }
54
+
55
+ export function buildChatwiseInjectTextJs(text) {
56
+ const scoreFn = scoreChatwiseComposerCandidate.toString();
57
+ const selectFn = selectBestChatwiseComposer.toString();
58
+ const textJs = JSON.stringify(String(text ?? ''));
59
+
60
+ return `
61
+ (function(text) {
62
+ const scoreChatwiseComposerCandidate = ${scoreFn};
63
+ const selectBestChatwiseComposer = ${selectFn};
64
+ const MIN_COMPOSER_SCORE = ${MIN_COMPOSER_SCORE};
65
+
66
+ const composers = Array.from(document.querySelectorAll([
67
+ 'textarea[aria-label*="message" i]',
68
+ 'textarea[placeholder*="message" i]',
69
+ '[contenteditable="true"][role="textbox"]',
70
+ '[contenteditable="true"]'
71
+ ].join(',')));
72
+ const candidates = composers.map((el, index) => {
73
+ const rect = el.getBoundingClientRect();
74
+ const editor = el.closest('.cm-editor');
75
+ const placeholderEl = editor?.querySelector('.cm-placeholder');
76
+ return {
77
+ index,
78
+ hidden: !(el.offsetWidth || el.offsetHeight || el.getClientRects().length),
79
+ role: el.getAttribute('role'),
80
+ classes: el.className || '',
81
+ editorClasses: editor?.className || '',
82
+ ariaLabel: el.getAttribute('aria-label') || '',
83
+ placeholder: placeholderEl?.getAttribute('aria-label') || placeholderEl?.textContent || el.getAttribute('placeholder') || '',
84
+ text: (el.textContent || '').trim(),
85
+ rect: { y: rect.y, h: rect.height },
86
+ };
87
+ });
88
+
89
+ const best = selectBestChatwiseComposer(candidates, window.innerHeight, MIN_COMPOSER_SCORE);
90
+ if (!best) return false;
91
+
92
+ const composer = composers[best.index];
93
+ composer.focus();
94
+
95
+ if (composer.tagName === 'TEXTAREA') {
96
+ const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
97
+ if (setter) setter.call(composer, text);
98
+ else composer.value = text;
99
+ composer.dispatchEvent(new Event('input', { bubbles: true }));
100
+ return true;
101
+ }
102
+
103
+ const selection = window.getSelection();
104
+ const range = document.createRange();
105
+ range.selectNodeContents(composer);
106
+ selection?.removeAllRanges();
107
+ selection?.addRange(range);
108
+
109
+ const inserted = document.execCommand?.('insertText', false, text);
110
+ if (!inserted) {
111
+ composer.textContent = text;
112
+ }
113
+ composer.dispatchEvent(new Event('input', { bubbles: true }));
114
+ return true;
115
+ })(${textJs})
116
+ `;
117
+ }
118
+
119
+ export function buildChatwiseMessageCountJs() {
120
+ return `
121
+ (function() {
122
+ return Array.from(document.querySelectorAll(${JSON.stringify(MESSAGE_WRAPPER_SELECTOR)}))
123
+ .map(node => (node.innerText || node.textContent || '').trim())
124
+ .filter(Boolean)
125
+ .length;
126
+ })()
127
+ `;
128
+ }
129
+
130
+ export function buildChatwiseResponseAfterJs(previousCount, userText) {
131
+ return `
132
+ (function(previousCount, userText) {
133
+ const messages = Array.from(document.querySelectorAll(${JSON.stringify(MESSAGE_WRAPPER_SELECTOR)}))
134
+ .map(node => (node.innerText || node.textContent || '').trim())
135
+ .filter(Boolean);
136
+ if (messages.length <= previousCount) return null;
137
+ const fresh = messages.slice(previousCount)
138
+ .filter(text => text && text !== userText);
139
+ if (fresh.length === 0) return null;
140
+ return fresh[fresh.length - 1];
141
+ })(${Number(previousCount) || 0}, ${JSON.stringify(String(userText ?? ''))})
142
+ `;
143
+ }
@@ -14,8 +14,8 @@ export const askCommand = cli({
14
14
  domain: CLAUDE_DOMAIN,
15
15
  strategy: Strategy.COOKIE,
16
16
  browser: true,
17
+ browserSession: { reuse: 'site' },
17
18
  navigateBefore: false,
18
- timeoutSeconds: 180,
19
19
  args: [
20
20
  { name: 'prompt', positional: true, required: true, help: 'Prompt to send' },
21
21
  { name: 'timeout', type: 'int', default: 120, help: 'Max seconds to wait for response' },
@@ -10,6 +10,7 @@ export const detailCommand = cli({
10
10
  domain: CLAUDE_DOMAIN,
11
11
  strategy: Strategy.COOKIE,
12
12
  browser: true,
13
+ browserSession: { reuse: 'site' },
13
14
  navigateBefore: false,
14
15
  args: [
15
16
  { name: 'id', positional: true, required: true, help: 'Conversation ID (UUID from /chat/<id>)' },
@@ -10,6 +10,7 @@ export const historyCommand = cli({
10
10
  domain: CLAUDE_DOMAIN,
11
11
  strategy: Strategy.COOKIE,
12
12
  browser: true,
13
+ browserSession: { reuse: 'site' },
13
14
  navigateBefore: false,
14
15
  args: [
15
16
  { name: 'limit', type: 'int', default: 20, help: 'Max conversations to show' },
@@ -9,6 +9,7 @@ export const newCommand = cli({
9
9
  domain: CLAUDE_DOMAIN,
10
10
  strategy: Strategy.COOKIE,
11
11
  browser: true,
12
+ browserSession: { reuse: 'site' },
12
13
  navigateBefore: false,
13
14
  args: [],
14
15
  columns: ['Status'],
@@ -10,6 +10,7 @@ export const readCommand = cli({
10
10
  domain: CLAUDE_DOMAIN,
11
11
  strategy: Strategy.COOKIE,
12
12
  browser: true,
13
+ browserSession: { reuse: 'site' },
13
14
  navigateBefore: false,
14
15
  args: [],
15
16
  columns: ['Index', 'Role', 'Text'],
@@ -10,6 +10,7 @@ export const sendCommand = cli({
10
10
  domain: CLAUDE_DOMAIN,
11
11
  strategy: Strategy.COOKIE,
12
12
  browser: true,
13
+ browserSession: { reuse: 'site' },
13
14
  navigateBefore: false,
14
15
  args: [
15
16
  { name: 'prompt', positional: true, required: true, help: 'Prompt to send' },
@@ -9,6 +9,7 @@ export const statusCommand = cli({
9
9
  domain: CLAUDE_DOMAIN,
10
10
  strategy: Strategy.COOKIE,
11
11
  browser: true,
12
+ browserSession: { reuse: 'site' },
12
13
  navigateBefore: false,
13
14
  args: [],
14
15
  columns: ['Status', 'Login', 'Url'],
package/clis/codex/ask.js CHANGED
@@ -1,21 +1,27 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
- import { selectorError } from '@jackwener/opencli/errors';
2
+ import { ArgumentError, selectorError } from '@jackwener/opencli/errors';
3
+ import { conversationSelectionArgs, openCodexConversation } from './sidebar.js';
3
4
  export const askCommand = cli({
4
5
  site: 'codex',
5
6
  name: 'ask',
6
7
  access: 'write',
7
- description: 'Send a prompt and wait for the AI response (send + wait + read)',
8
+ description: 'Send a prompt to the current or selected Codex conversation and wait for the AI response',
8
9
  domain: 'localhost',
9
10
  strategy: Strategy.UI,
10
11
  browser: true,
11
12
  args: [
12
13
  { name: 'text', required: true, positional: true, help: 'Prompt to send' },
13
- { name: 'timeout', required: false, help: 'Max seconds to wait for response (default: 60)', default: '60' },
14
+ { name: 'timeout', type: 'int', required: false, help: 'Max seconds to wait for response (default: 60)', default: 60 },
15
+ ...conversationSelectionArgs,
14
16
  ],
15
- columns: ['Role', 'Text'],
17
+ columns: ['Role', 'Project', 'Conversation', 'Text'],
16
18
  func: async (page, kwargs) => {
17
19
  const text = kwargs.text;
18
- const timeout = parseInt(kwargs.timeout, 10) || 60;
20
+ const timeout = kwargs.timeout;
21
+ if (!Number.isInteger(timeout) || timeout < 1) {
22
+ throw new ArgumentError('--timeout must be a positive integer (seconds)');
23
+ }
24
+ const selected = await openCodexConversation(page, kwargs);
19
25
  // Snapshot the current content length before sending
20
26
  const beforeLen = await page.evaluate(`
21
27
  (function() {
@@ -60,13 +66,13 @@ export const askCommand = cli({
60
66
  }
61
67
  if (!response) {
62
68
  return [
63
- { Role: 'User', Text: text },
64
- { Role: 'System', Text: `No response within ${timeout}s. The agent may still be working.` },
69
+ { Role: 'User', Project: selected?.project || '', Conversation: selected?.conversation || '', Text: text },
70
+ { Role: 'System', Project: selected?.project || '', Conversation: selected?.conversation || '', Text: `No response within ${timeout}s. The agent may still be working.` },
65
71
  ];
66
72
  }
67
73
  return [
68
- { Role: 'User', Text: text },
69
- { Role: 'Assistant', Text: response },
74
+ { Role: 'User', Project: selected?.project || '', Conversation: selected?.conversation || '', Text: text },
75
+ { Role: 'Assistant', Project: selected?.project || '', Conversation: selected?.conversation || '', Text: response },
70
76
  ];
71
77
  },
72
78
  });
@@ -1,44 +1,27 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
+ import { EmptyResultError } from '@jackwener/opencli/errors';
3
+ import { flattenCodexProjects, readCodexProjects } from './sidebar.js';
2
4
  export const historyCommand = cli({
3
5
  site: 'codex',
4
6
  name: 'history',
5
7
  access: 'read',
6
- description: 'List recent conversation threads in Codex',
8
+ description: 'List visible Codex conversation threads grouped by project',
7
9
  domain: 'localhost',
8
10
  strategy: Strategy.UI,
9
11
  browser: true,
10
- args: [],
11
- columns: ['Index', 'Title'],
12
- func: async (page) => {
13
- const items = await page.evaluate(`
14
- (function() {
15
- const results = [];
16
- // Codex thread list items
17
- const entries = document.querySelectorAll('[data-testid*="thread"], [class*="thread-list"] a, [role="listbox"] [role="option"]');
18
-
19
- entries.forEach((item, i) => {
20
- const title = (item.textContent || item.innerText || '').trim().substring(0, 100);
21
- if (title) results.push({ Index: i + 1, Title: title });
22
- });
23
-
24
- // Fallback: sidebar/nav links
25
- if (results.length === 0) {
26
- const nav = document.querySelector('nav, [role="navigation"], aside');
27
- if (nav) {
28
- const links = nav.querySelectorAll('a, button');
29
- links.forEach((link, i) => {
30
- const text = (link.textContent || '').trim().substring(0, 100);
31
- if (text && text.length > 3) results.push({ Index: i + 1, Title: text });
32
- });
33
- }
12
+ args: [
13
+ { name: 'project', required: false, help: 'Filter by project label or path' },
14
+ { name: 'limit', required: false, help: 'Max conversations per project' },
15
+ ],
16
+ columns: ['Project', 'Index', 'Title', 'Updated', 'Active'],
17
+ func: async (page, kwargs) => {
18
+ const projects = await readCodexProjects(page);
19
+ const rows = flattenCodexProjects(projects, kwargs);
20
+ if (rows.length === 0) {
21
+ throw new EmptyResultError('codex history', kwargs.project
22
+ ? `No Codex conversations were visible for project "${kwargs.project}".`
23
+ : 'No Codex conversations were visible. Open the Codex sidebar and retry.');
34
24
  }
35
-
36
- return results;
37
- })()
38
- `);
39
- if (items.length === 0) {
40
- return [{ Index: 0, Title: 'No threads found. Try opening the thread list first.' }];
41
- }
42
- return items;
25
+ return rows;
43
26
  },
44
27
  });
@@ -0,0 +1,28 @@
1
+ import { cli, Strategy } from '@jackwener/opencli/registry';
2
+ import { EmptyResultError } from '@jackwener/opencli/errors';
3
+ import { flattenCodexProjects, readCodexProjects } from './sidebar.js';
4
+
5
+ export const projectsCommand = cli({
6
+ site: 'codex',
7
+ name: 'projects',
8
+ access: 'read',
9
+ description: 'List Codex projects and visible conversations from the sidebar',
10
+ domain: 'localhost',
11
+ strategy: Strategy.UI,
12
+ browser: true,
13
+ args: [
14
+ { name: 'project', required: false, help: 'Filter by project label or path' },
15
+ { name: 'limit', required: false, help: 'Max conversations per project' },
16
+ ],
17
+ columns: ['Project', 'Index', 'Title', 'Updated', 'Active'],
18
+ func: async (page, kwargs) => {
19
+ const projects = await readCodexProjects(page);
20
+ const rows = flattenCodexProjects(projects, kwargs);
21
+ if (rows.length === 0) {
22
+ throw new EmptyResultError('codex projects', kwargs.project
23
+ ? `No Codex projects matched "${kwargs.project}".`
24
+ : 'No Codex projects were visible. Open the Codex sidebar and retry.');
25
+ }
26
+ return rows;
27
+ },
28
+ });
@@ -1,15 +1,19 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
+ import { conversationSelectionArgs, openCodexConversation } from './sidebar.js';
2
3
  export const readCommand = cli({
3
4
  site: 'codex',
4
5
  name: 'read',
5
6
  access: 'read',
6
- description: 'Read the contents of the current Codex conversation thread',
7
+ description: 'Read the contents of the current or selected Codex conversation thread',
7
8
  domain: 'localhost',
8
9
  strategy: Strategy.UI,
9
10
  browser: true,
10
- args: [],
11
- columns: ['Content'],
12
- func: async (page) => {
11
+ args: [
12
+ ...conversationSelectionArgs,
13
+ ],
14
+ columns: ['Project', 'Conversation', 'Content'],
15
+ func: async (page, kwargs) => {
16
+ const selected = await openCodexConversation(page, kwargs);
13
17
  const historyText = await page.evaluate(`
14
18
  (function() {
15
19
  const turns = Array.from(document.querySelectorAll('[data-content-search-turn-key]'));
@@ -28,6 +32,8 @@ export const readCommand = cli({
28
32
  `);
29
33
  return [
30
34
  {
35
+ Project: selected?.project || '',
36
+ Conversation: selected?.conversation || '',
31
37
  Content: historyText,
32
38
  },
33
39
  ];
@@ -1,17 +1,22 @@
1
1
  import { cli, Strategy } from '@jackwener/opencli/registry';
2
2
  import { selectorError } from '@jackwener/opencli/errors';
3
+ import { conversationSelectionArgs, openCodexConversation } from './sidebar.js';
3
4
  export const sendCommand = cli({
4
5
  site: 'codex',
5
6
  name: 'send',
6
7
  access: 'write',
7
- description: 'Send text/commands to the Codex AI composer',
8
+ description: 'Send text/commands to the current or selected Codex AI composer',
8
9
  domain: 'localhost',
9
10
  strategy: Strategy.UI,
10
11
  browser: true,
11
- args: [{ name: 'text', required: true, positional: true, help: 'Text, command (e.g. /review), or skill (e.g. $imagegen)' }],
12
- columns: ['Status', 'InjectedText'],
12
+ args: [
13
+ { name: 'text', required: true, positional: true, help: 'Text, command (e.g. /review), or skill (e.g. $imagegen)' },
14
+ ...conversationSelectionArgs,
15
+ ],
16
+ columns: ['Status', 'Project', 'Conversation', 'InjectedText'],
13
17
  func: async (page, kwargs) => {
14
18
  const textToInsert = kwargs.text;
19
+ const selected = await openCodexConversation(page, kwargs);
15
20
  const injected = await page.evaluate(`
16
21
  (function(text) {
17
22
  let composer = document.querySelector('textarea, [contenteditable="true"]');
@@ -37,6 +42,8 @@ export const sendCommand = cli({
37
42
  return [
38
43
  {
39
44
  Status: 'Success',
45
+ Project: selected?.project || '',
46
+ Conversation: selected?.conversation || '',
40
47
  InjectedText: textToInsert,
41
48
  },
42
49
  ];