@jackwener/opencli 1.6.1 → 1.6.2

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 (384) hide show
  1. package/CONTRIBUTING.md +1 -1
  2. package/README.md +27 -45
  3. package/README.zh-CN.md +32 -34
  4. package/autoresearch/browse-tasks.json +18 -20
  5. package/autoresearch/commands/debug.ts +163 -0
  6. package/autoresearch/commands/fix.ts +145 -0
  7. package/autoresearch/commands/plan.ts +88 -0
  8. package/autoresearch/commands/run.ts +138 -0
  9. package/autoresearch/config.ts +82 -0
  10. package/autoresearch/engine.ts +359 -0
  11. package/autoresearch/eval-all.ts +127 -0
  12. package/autoresearch/eval-browse.ts +1 -1
  13. package/autoresearch/eval-publish.ts +238 -0
  14. package/autoresearch/eval-save.ts +249 -0
  15. package/autoresearch/eval-skill.ts +14 -8
  16. package/autoresearch/eval-v2ex.ts +220 -0
  17. package/autoresearch/eval-zhihu.ts +230 -0
  18. package/autoresearch/logger.ts +69 -0
  19. package/autoresearch/presets/combined-reliability.ts +27 -0
  20. package/autoresearch/presets/index.ts +23 -0
  21. package/autoresearch/presets/operate-reliability.ts +24 -0
  22. package/autoresearch/presets/save-reliability.ts +26 -0
  23. package/autoresearch/presets/skill-quality.ts +20 -0
  24. package/autoresearch/presets/v2ex-reliability.ts +24 -0
  25. package/autoresearch/presets/zhihu-reliability.ts +25 -0
  26. package/autoresearch/publish-tasks.json +345 -0
  27. package/autoresearch/run-save.sh +11 -0
  28. package/autoresearch/save-adapters/xhs-explore-deep.ts +64 -0
  29. package/autoresearch/save-adapters/xhs-note-comments.ts +61 -0
  30. package/autoresearch/save-adapters/xhs-search-full.ts +62 -0
  31. package/autoresearch/save-adapters/zhihu-hot-detail.ts +52 -0
  32. package/autoresearch/save-adapters/zhihu-question-full.ts +57 -0
  33. package/autoresearch/save-adapters/zhihu-search-detail.ts +53 -0
  34. package/autoresearch/save-tasks.json +281 -0
  35. package/autoresearch/v2ex-tasks.json +899 -0
  36. package/autoresearch/zhihu-tasks.json +848 -0
  37. package/dist/browser/base-page.d.ts +4 -2
  38. package/dist/browser/base-page.js +37 -4
  39. package/dist/browser/bridge.js +10 -8
  40. package/dist/browser/cdp.js +2 -6
  41. package/dist/browser/daemon-client.d.ts +11 -1
  42. package/dist/browser/daemon-client.js +3 -0
  43. package/dist/browser/dom-helpers.d.ts +4 -2
  44. package/dist/browser/dom-helpers.js +42 -31
  45. package/dist/browser/dom-snapshot.js +23 -1
  46. package/dist/browser/page.d.ts +7 -2
  47. package/dist/browser/page.js +112 -30
  48. package/dist/browser.test.js +1 -1
  49. package/dist/build-manifest.d.ts +1 -0
  50. package/dist/build-manifest.js +1 -0
  51. package/dist/cli-manifest.json +1135 -184
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.js +48 -7
  54. package/dist/cli.test.d.ts +1 -0
  55. package/dist/cli.test.js +88 -0
  56. package/dist/clis/1688/item.d.ts +70 -0
  57. package/dist/clis/1688/item.js +187 -0
  58. package/dist/clis/1688/item.test.d.ts +1 -0
  59. package/dist/clis/1688/item.test.js +67 -0
  60. package/dist/clis/1688/search.d.ts +56 -0
  61. package/dist/clis/1688/search.js +309 -0
  62. package/dist/clis/1688/search.test.d.ts +1 -0
  63. package/dist/clis/1688/search.test.js +75 -0
  64. package/dist/clis/1688/shared.d.ts +112 -0
  65. package/dist/clis/1688/shared.js +514 -0
  66. package/dist/clis/1688/shared.test.d.ts +1 -0
  67. package/dist/clis/1688/shared.test.js +57 -0
  68. package/dist/clis/1688/store.d.ts +45 -0
  69. package/dist/clis/1688/store.js +226 -0
  70. package/dist/clis/1688/store.test.d.ts +1 -0
  71. package/dist/clis/1688/store.test.js +62 -0
  72. package/dist/clis/amazon/bestsellers.d.ts +0 -20
  73. package/dist/clis/amazon/bestsellers.js +6 -129
  74. package/dist/clis/amazon/bestsellers.test.js +12 -3
  75. package/dist/clis/amazon/movers-shakers.d.ts +1 -0
  76. package/dist/clis/amazon/movers-shakers.js +7 -0
  77. package/dist/clis/amazon/new-releases.d.ts +1 -0
  78. package/dist/clis/amazon/new-releases.js +7 -0
  79. package/dist/clis/amazon/rankings.d.ts +59 -0
  80. package/dist/clis/amazon/rankings.js +226 -0
  81. package/dist/clis/amazon/rankings.test.d.ts +1 -0
  82. package/dist/clis/amazon/rankings.test.js +41 -0
  83. package/dist/clis/amazon/shared.d.ts +11 -0
  84. package/dist/clis/amazon/shared.js +121 -11
  85. package/dist/clis/amazon/shared.test.js +11 -0
  86. package/dist/clis/bilibili/comments.js +2 -2
  87. package/dist/clis/bilibili/comments.test.js +3 -2
  88. package/dist/clis/bilibili/download.js +2 -1
  89. package/dist/clis/bilibili/subtitle.js +4 -3
  90. package/dist/clis/bilibili/subtitle.test.js +2 -1
  91. package/dist/clis/bilibili/utils.d.ts +5 -0
  92. package/dist/clis/bilibili/utils.js +30 -0
  93. package/dist/clis/bilibili/utils.test.d.ts +1 -0
  94. package/dist/clis/bilibili/utils.test.js +17 -0
  95. package/dist/clis/douban/marks.js +1 -1
  96. package/dist/clis/douban/subject.yaml +50 -19
  97. package/dist/clis/doubao/utils.js +32 -12
  98. package/dist/clis/douyin/_shared/browser-fetch.test.js +0 -1
  99. package/dist/clis/douyin/_shared/transcode.test.js +0 -2
  100. package/dist/clis/douyin/draft.test.js +0 -2
  101. package/dist/clis/facebook/search.test.js +0 -2
  102. package/dist/clis/gemini/ask.js +9 -3
  103. package/dist/clis/gemini/ask.test.d.ts +1 -0
  104. package/dist/clis/gemini/ask.test.js +100 -0
  105. package/dist/clis/gemini/reply-state.test.d.ts +1 -0
  106. package/dist/clis/gemini/reply-state.test.js +641 -0
  107. package/dist/clis/gemini/utils.d.ts +44 -1
  108. package/dist/clis/gemini/utils.js +528 -61
  109. package/dist/clis/gemini/utils.test.js +149 -2
  110. package/dist/clis/hupu/detail.d.ts +1 -0
  111. package/dist/clis/hupu/detail.js +72 -0
  112. package/dist/clis/hupu/hot.yaml +43 -0
  113. package/dist/clis/hupu/like.d.ts +1 -0
  114. package/dist/clis/hupu/like.js +75 -0
  115. package/dist/clis/hupu/reply.d.ts +1 -0
  116. package/dist/clis/hupu/reply.js +71 -0
  117. package/dist/clis/hupu/search.d.ts +1 -0
  118. package/dist/clis/hupu/search.js +59 -0
  119. package/dist/clis/hupu/unlike.d.ts +1 -0
  120. package/dist/clis/hupu/unlike.js +75 -0
  121. package/dist/clis/hupu/utils.d.ts +20 -0
  122. package/dist/clis/hupu/utils.js +319 -0
  123. package/dist/clis/instagram/_shared/private-publish.d.ts +138 -0
  124. package/dist/clis/instagram/_shared/private-publish.js +1030 -0
  125. package/dist/clis/instagram/_shared/private-publish.test.d.ts +1 -0
  126. package/dist/clis/instagram/_shared/private-publish.test.js +705 -0
  127. package/dist/clis/instagram/_shared/protocol-capture.d.ts +26 -0
  128. package/dist/clis/instagram/_shared/protocol-capture.js +282 -0
  129. package/dist/clis/instagram/_shared/protocol-capture.test.d.ts +1 -0
  130. package/dist/clis/instagram/_shared/protocol-capture.test.js +114 -0
  131. package/dist/clis/instagram/_shared/runtime-info.d.ts +9 -0
  132. package/dist/clis/instagram/_shared/runtime-info.js +81 -0
  133. package/dist/clis/instagram/note.d.ts +1 -0
  134. package/dist/clis/instagram/note.js +222 -0
  135. package/dist/clis/instagram/note.test.d.ts +1 -0
  136. package/dist/clis/instagram/note.test.js +81 -0
  137. package/dist/clis/instagram/post.d.ts +4 -0
  138. package/dist/clis/instagram/post.js +1496 -0
  139. package/dist/clis/instagram/post.test.d.ts +1 -0
  140. package/dist/clis/instagram/post.test.js +1647 -0
  141. package/dist/clis/instagram/reel.d.ts +1 -0
  142. package/dist/clis/instagram/reel.js +826 -0
  143. package/dist/clis/instagram/reel.test.d.ts +1 -0
  144. package/dist/clis/instagram/reel.test.js +167 -0
  145. package/dist/clis/instagram/story.d.ts +1 -0
  146. package/dist/clis/instagram/story.js +115 -0
  147. package/dist/clis/instagram/story.test.d.ts +1 -0
  148. package/dist/clis/instagram/story.test.js +167 -0
  149. package/dist/clis/sinafinance/stock-rank.d.ts +4 -0
  150. package/dist/clis/sinafinance/stock-rank.js +65 -0
  151. package/dist/clis/substack/utils.test.js +0 -2
  152. package/dist/clis/twitter/post.js +72 -45
  153. package/dist/clis/twitter/post.test.d.ts +1 -0
  154. package/dist/clis/twitter/post.test.js +116 -0
  155. package/dist/clis/twitter/reply.d.ts +12 -0
  156. package/dist/clis/twitter/reply.js +257 -35
  157. package/dist/clis/twitter/reply.test.d.ts +1 -0
  158. package/dist/clis/twitter/reply.test.js +151 -0
  159. package/dist/clis/xianyu/chat.d.ts +7 -0
  160. package/dist/clis/xianyu/chat.js +146 -0
  161. package/dist/clis/xianyu/chat.test.d.ts +1 -0
  162. package/dist/clis/xianyu/chat.test.js +15 -0
  163. package/dist/clis/xianyu/item.d.ts +7 -0
  164. package/dist/clis/xianyu/item.js +152 -0
  165. package/dist/clis/xianyu/item.test.d.ts +1 -0
  166. package/dist/clis/xianyu/item.test.js +56 -0
  167. package/dist/clis/xianyu/search.d.ts +10 -0
  168. package/dist/clis/xianyu/search.js +134 -0
  169. package/dist/clis/xianyu/search.test.d.ts +1 -0
  170. package/dist/clis/xianyu/search.test.js +17 -0
  171. package/dist/clis/xianyu/utils.d.ts +1 -0
  172. package/dist/clis/xianyu/utils.js +8 -0
  173. package/dist/clis/xiaoe/catalog.yaml +129 -0
  174. package/dist/clis/xiaoe/content.yaml +43 -0
  175. package/dist/clis/xiaoe/courses.yaml +73 -0
  176. package/dist/clis/xiaoe/detail.yaml +39 -0
  177. package/dist/clis/xiaoe/play-url.yaml +124 -0
  178. package/dist/clis/xiaohongshu/comments.test.js +0 -2
  179. package/dist/clis/xiaohongshu/creator-note-detail.test.js +0 -2
  180. package/dist/clis/xiaohongshu/creator-notes.test.js +0 -2
  181. package/dist/clis/xiaohongshu/download.test.js +0 -2
  182. package/dist/clis/xiaohongshu/note.test.js +0 -2
  183. package/dist/clis/xiaohongshu/publish.test.js +0 -2
  184. package/dist/clis/xiaohongshu/search.js +29 -20
  185. package/dist/clis/xiaohongshu/search.test.js +56 -48
  186. package/dist/clis/yuanbao/ask.d.ts +21 -0
  187. package/dist/clis/yuanbao/ask.js +427 -0
  188. package/dist/clis/yuanbao/ask.test.d.ts +1 -0
  189. package/dist/clis/yuanbao/ask.test.js +124 -0
  190. package/dist/clis/yuanbao/new.d.ts +1 -0
  191. package/dist/clis/yuanbao/new.js +70 -0
  192. package/dist/clis/yuanbao/new.test.d.ts +1 -0
  193. package/dist/clis/yuanbao/new.test.js +30 -0
  194. package/dist/clis/yuanbao/shared.d.ts +13 -0
  195. package/dist/clis/yuanbao/shared.js +49 -0
  196. package/dist/clis/zhihu/question.js +30 -19
  197. package/dist/clis/zhihu/question.test.js +34 -16
  198. package/dist/commanderAdapter.js +8 -4
  199. package/dist/commanderAdapter.test.js +42 -0
  200. package/dist/completion.js +3 -1
  201. package/dist/completion.test.d.ts +1 -0
  202. package/dist/completion.test.js +23 -0
  203. package/dist/doctor.js +1 -1
  204. package/dist/electron-apps.d.ts +2 -0
  205. package/dist/electron-apps.js +7 -1
  206. package/dist/errors.js +1 -1
  207. package/dist/execution.js +25 -35
  208. package/dist/explore.js +1 -1
  209. package/dist/launcher.d.ts +4 -0
  210. package/dist/launcher.js +64 -8
  211. package/dist/launcher.test.js +88 -7
  212. package/dist/output.d.ts +2 -0
  213. package/dist/output.js +10 -1
  214. package/dist/output.test.d.ts +0 -3
  215. package/dist/output.test.js +59 -92
  216. package/dist/pipeline/executor.test.js +0 -2
  217. package/dist/pipeline/steps/download.test.js +0 -2
  218. package/dist/registry.d.ts +2 -0
  219. package/dist/serialization.d.ts +1 -0
  220. package/dist/serialization.js +1 -0
  221. package/dist/types.d.ts +9 -2
  222. package/docs/.vitepress/config.mts +4 -0
  223. package/docs/adapters/browser/1688.md +52 -0
  224. package/docs/adapters/browser/36kr.md +2 -1
  225. package/docs/adapters/browser/doubao.md +5 -1
  226. package/docs/adapters/browser/hupu.md +53 -0
  227. package/docs/adapters/browser/sinafinance.md +32 -2
  228. package/docs/adapters/browser/weibo.md +6 -1
  229. package/docs/adapters/browser/wikipedia.md +2 -0
  230. package/docs/adapters/browser/xianyu.md +42 -0
  231. package/docs/adapters/browser/xiaoe.md +44 -0
  232. package/docs/adapters/browser/yuanbao.md +64 -0
  233. package/docs/adapters/index.md +14 -5
  234. package/docs/comparison.md +1 -1
  235. package/docs/developer/ai-workflow.md +2 -2
  236. package/docs/developer/contributing.md +1 -1
  237. package/docs/developer/testing.md +2 -0
  238. package/docs/guide/plugins.md +1 -0
  239. package/docs/guide/troubleshooting.md +11 -0
  240. package/docs/superpowers/specs/2026-04-03-v2ex-autoresearch-design.md +41 -0
  241. package/docs/zh/guide/plugins.md +1 -0
  242. package/extension/dist/background.js +1127 -0
  243. package/extension/src/background.test.ts +39 -0
  244. package/extension/src/background.ts +223 -34
  245. package/extension/src/cdp.ts +194 -4
  246. package/extension/src/protocol.ts +22 -1
  247. package/package.json +3 -2
  248. package/scripts/postinstall.js +1 -1
  249. package/skills/opencli-explorer/SKILL.md +1 -1
  250. package/skills/opencli-oneshot/SKILL.md +2 -2
  251. package/skills/opencli-operate/SKILL.md +120 -27
  252. package/skills/opencli-usage/SKILL.md +31 -20
  253. package/skills/opencli-usage/browser.md +114 -16
  254. package/skills/opencli-usage/public-api.md +32 -3
  255. package/skills/smart-search/SKILL.md +156 -0
  256. package/skills/smart-search/references/sources-ai.md +74 -0
  257. package/skills/smart-search/references/sources-info.md +43 -0
  258. package/skills/smart-search/references/sources-media.md +50 -0
  259. package/skills/smart-search/references/sources-other.md +42 -0
  260. package/skills/smart-search/references/sources-shopping.md +31 -0
  261. package/skills/smart-search/references/sources-social.md +51 -0
  262. package/skills/smart-search/references/sources-tech.md +42 -0
  263. package/skills/smart-search/references/sources-travel.md +20 -0
  264. package/src/browser/base-page.ts +41 -6
  265. package/src/browser/bridge.ts +11 -8
  266. package/src/browser/cdp.ts +1 -8
  267. package/src/browser/daemon-client.ts +11 -1
  268. package/src/browser/dom-helpers.ts +43 -31
  269. package/src/browser/dom-snapshot.ts +23 -1
  270. package/src/browser/page.ts +115 -31
  271. package/src/browser.test.ts +1 -1
  272. package/src/build-manifest.ts +2 -0
  273. package/src/cli.test.ts +133 -0
  274. package/src/cli.ts +73 -11
  275. package/src/clis/1688/item.test.ts +69 -0
  276. package/src/clis/1688/item.ts +282 -0
  277. package/src/clis/1688/search.test.ts +81 -0
  278. package/src/clis/1688/search.ts +402 -0
  279. package/src/clis/1688/shared.test.ts +75 -0
  280. package/src/clis/1688/shared.ts +623 -0
  281. package/src/clis/1688/store.test.ts +69 -0
  282. package/src/clis/1688/store.ts +300 -0
  283. package/src/clis/amazon/bestsellers.test.ts +12 -3
  284. package/src/clis/amazon/bestsellers.ts +6 -178
  285. package/src/clis/amazon/movers-shakers.ts +8 -0
  286. package/src/clis/amazon/new-releases.ts +8 -0
  287. package/src/clis/amazon/rankings.test.ts +47 -0
  288. package/src/clis/amazon/rankings.ts +312 -0
  289. package/src/clis/amazon/shared.test.ts +16 -0
  290. package/src/clis/amazon/shared.ts +134 -12
  291. package/src/clis/bilibili/comments.test.ts +4 -3
  292. package/src/clis/bilibili/comments.ts +2 -2
  293. package/src/clis/bilibili/download.ts +2 -1
  294. package/src/clis/bilibili/subtitle.test.ts +2 -1
  295. package/src/clis/bilibili/subtitle.ts +4 -3
  296. package/src/clis/bilibili/utils.test.ts +21 -0
  297. package/src/clis/bilibili/utils.ts +27 -0
  298. package/src/clis/douban/marks.ts +1 -1
  299. package/src/clis/douban/subject.yaml +50 -19
  300. package/src/clis/doubao/utils.ts +32 -12
  301. package/src/clis/douyin/_shared/browser-fetch.test.ts +0 -1
  302. package/src/clis/douyin/_shared/transcode.test.ts +0 -2
  303. package/src/clis/douyin/draft.test.ts +0 -2
  304. package/src/clis/facebook/search.test.ts +0 -2
  305. package/src/clis/gemini/ask.test.ts +116 -0
  306. package/src/clis/gemini/ask.ts +10 -3
  307. package/src/clis/gemini/reply-state.test.ts +708 -0
  308. package/src/clis/gemini/utils.test.ts +184 -2
  309. package/src/clis/gemini/utils.ts +588 -60
  310. package/src/clis/hupu/detail.ts +126 -0
  311. package/src/clis/hupu/hot.yaml +43 -0
  312. package/src/clis/hupu/like.ts +76 -0
  313. package/src/clis/hupu/reply.ts +76 -0
  314. package/src/clis/hupu/search.ts +95 -0
  315. package/src/clis/hupu/unlike.ts +76 -0
  316. package/src/clis/hupu/utils.ts +381 -0
  317. package/src/clis/instagram/_shared/private-publish.test.ts +827 -0
  318. package/src/clis/instagram/_shared/private-publish.ts +1303 -0
  319. package/src/clis/instagram/_shared/protocol-capture.test.ts +148 -0
  320. package/src/clis/instagram/_shared/protocol-capture.ts +321 -0
  321. package/src/clis/instagram/_shared/runtime-info.ts +91 -0
  322. package/src/clis/instagram/note.test.ts +96 -0
  323. package/src/clis/instagram/note.ts +254 -0
  324. package/src/clis/instagram/post.test.ts +1716 -0
  325. package/src/clis/instagram/post.ts +1620 -0
  326. package/src/clis/instagram/reel.test.ts +191 -0
  327. package/src/clis/instagram/reel.ts +886 -0
  328. package/src/clis/instagram/story.test.ts +191 -0
  329. package/src/clis/instagram/story.ts +151 -0
  330. package/src/clis/sinafinance/stock-rank.ts +68 -0
  331. package/src/clis/substack/utils.test.ts +0 -2
  332. package/src/clis/twitter/post.test.ts +157 -0
  333. package/src/clis/twitter/post.ts +82 -48
  334. package/src/clis/twitter/reply.test.ts +177 -0
  335. package/src/clis/twitter/reply.ts +285 -39
  336. package/src/clis/xianyu/chat.test.ts +20 -0
  337. package/src/clis/xianyu/chat.ts +175 -0
  338. package/src/clis/xianyu/item.test.ts +67 -0
  339. package/src/clis/xianyu/item.ts +172 -0
  340. package/src/clis/xianyu/search.test.ts +22 -0
  341. package/src/clis/xianyu/search.ts +151 -0
  342. package/src/clis/xianyu/utils.ts +9 -0
  343. package/src/clis/xiaoe/catalog.yaml +129 -0
  344. package/src/clis/xiaoe/content.yaml +43 -0
  345. package/src/clis/xiaoe/courses.yaml +73 -0
  346. package/src/clis/xiaoe/detail.yaml +39 -0
  347. package/src/clis/xiaoe/play-url.yaml +124 -0
  348. package/src/clis/xiaohongshu/comments.test.ts +0 -2
  349. package/src/clis/xiaohongshu/creator-note-detail.test.ts +0 -2
  350. package/src/clis/xiaohongshu/creator-notes.test.ts +0 -2
  351. package/src/clis/xiaohongshu/download.test.ts +0 -2
  352. package/src/clis/xiaohongshu/note.test.ts +0 -2
  353. package/src/clis/xiaohongshu/publish.test.ts +0 -2
  354. package/src/clis/xiaohongshu/search.test.ts +59 -48
  355. package/src/clis/xiaohongshu/search.ts +31 -21
  356. package/src/clis/yuanbao/ask.test.ts +156 -0
  357. package/src/clis/yuanbao/ask.ts +522 -0
  358. package/src/clis/yuanbao/new.test.ts +36 -0
  359. package/src/clis/yuanbao/new.ts +81 -0
  360. package/src/clis/yuanbao/shared.ts +57 -0
  361. package/src/clis/zhihu/question.test.ts +42 -17
  362. package/src/clis/zhihu/question.ts +31 -26
  363. package/src/commanderAdapter.test.ts +51 -0
  364. package/src/commanderAdapter.ts +8 -4
  365. package/src/completion.test.ts +30 -0
  366. package/src/completion.ts +3 -1
  367. package/src/doctor.ts +1 -1
  368. package/src/electron-apps.ts +9 -1
  369. package/src/errors.ts +1 -1
  370. package/src/execution.ts +26 -30
  371. package/src/explore.ts +1 -1
  372. package/src/launcher.test.ts +121 -7
  373. package/src/launcher.ts +87 -9
  374. package/src/output.test.ts +50 -90
  375. package/src/output.ts +10 -1
  376. package/src/pipeline/executor.test.ts +0 -2
  377. package/src/pipeline/steps/download.test.ts +0 -2
  378. package/src/registry.ts +2 -0
  379. package/src/serialization.ts +2 -0
  380. package/src/types.ts +9 -2
  381. package/tests/e2e/browser-auth.test.ts +9 -0
  382. package/CLI-EXPLORER.md +0 -724
  383. package/CLI-ONESHOT.md +0 -216
  384. package/SKILL.md +0 -59
@@ -0,0 +1,26 @@
1
+ import type { IPage } from '../../../types.js';
2
+ export interface InstagramProtocolCaptureEntry {
3
+ kind: 'fetch' | 'xhr';
4
+ url: string;
5
+ method: string;
6
+ requestHeaders?: Record<string, string>;
7
+ requestBodyKind?: string;
8
+ requestBodyPreview?: string;
9
+ responseStatus?: number;
10
+ responseContentType?: string;
11
+ responsePreview?: string;
12
+ timestamp: number;
13
+ }
14
+ export declare function buildInstallInstagramProtocolCaptureJs(captureVar?: string, captureErrorsVar?: string): string;
15
+ export declare function buildReadInstagramProtocolCaptureJs(captureVar?: string, captureErrorsVar?: string): string;
16
+ export declare function installInstagramProtocolCapture(page: IPage): Promise<void>;
17
+ export declare function readInstagramProtocolCapture(page: IPage): Promise<{
18
+ data: InstagramProtocolCaptureEntry[];
19
+ errors: string[];
20
+ }>;
21
+ export declare function dumpInstagramProtocolCaptureIfEnabled(page: IPage): Promise<void>;
22
+ export declare function instagramPrivateApiFetch(page: IPage, input: string | URL, init?: {
23
+ method?: 'GET' | 'POST';
24
+ headers?: Record<string, string>;
25
+ body?: unknown;
26
+ }): Promise<Response>;
@@ -0,0 +1,282 @@
1
+ import * as fs from 'node:fs';
2
+ import { resolveInstagramRuntimeInfo } from './runtime-info.js';
3
+ const DEFAULT_CAPTURE_VAR = '__opencli_ig_protocol_capture';
4
+ const DEFAULT_CAPTURE_ERRORS_VAR = '__opencli_ig_protocol_capture_errors';
5
+ const TRACE_OUTPUT_PATH = '/tmp/instagram_post_protocol_trace.json';
6
+ const INSTAGRAM_PROTOCOL_CAPTURE_PATTERN = [
7
+ '/rupload_igphoto/',
8
+ '/rupload_igvideo/',
9
+ '/api/v1/',
10
+ '/media/configure/',
11
+ '/media/configure_sidecar/',
12
+ '/media/configure_to_story/',
13
+ '/api/graphql/',
14
+ ].join('|');
15
+ export function buildInstallInstagramProtocolCaptureJs(captureVar = DEFAULT_CAPTURE_VAR, captureErrorsVar = DEFAULT_CAPTURE_ERRORS_VAR) {
16
+ return `
17
+ (() => {
18
+ const CAPTURE_VAR = ${JSON.stringify(captureVar)};
19
+ const CAPTURE_ERRORS_VAR = ${JSON.stringify(captureErrorsVar)};
20
+ const PATCH_GUARD = CAPTURE_VAR + '_patched';
21
+ const FILTERS = [
22
+ '/rupload_igphoto/',
23
+ '/rupload_igvideo/',
24
+ '/api/v1/',
25
+ '/media/configure/',
26
+ '/media/configure_sidecar/',
27
+ '/media/configure_to_story/',
28
+ '/api/graphql/',
29
+ ];
30
+
31
+ const shouldCapture = (url) => {
32
+ const value = String(url || '');
33
+ return FILTERS.some((filter) => value.includes(filter));
34
+ };
35
+
36
+ const normalizeHeaders = (headersLike) => {
37
+ const out = {};
38
+ try {
39
+ if (!headersLike) return out;
40
+ if (headersLike instanceof Headers) {
41
+ headersLike.forEach((value, key) => { out[key] = value; });
42
+ return out;
43
+ }
44
+ if (Array.isArray(headersLike)) {
45
+ for (const pair of headersLike) {
46
+ if (Array.isArray(pair) && pair.length >= 2) out[String(pair[0])] = String(pair[1]);
47
+ }
48
+ return out;
49
+ }
50
+ if (typeof headersLike === 'object') {
51
+ for (const [key, value] of Object.entries(headersLike)) out[key] = String(value);
52
+ }
53
+ } catch {}
54
+ return out;
55
+ };
56
+
57
+ const summarizeBody = async (body) => {
58
+ if (body == null) return { kind: 'empty', preview: '' };
59
+ try {
60
+ if (typeof body === 'string') {
61
+ return { kind: 'string', preview: body.slice(0, 1000) };
62
+ }
63
+ if (body instanceof URLSearchParams) {
64
+ return { kind: 'urlencoded', preview: body.toString().slice(0, 1000) };
65
+ }
66
+ if (body instanceof FormData) {
67
+ const parts = [];
68
+ for (const [key, value] of body.entries()) {
69
+ if (value instanceof File) {
70
+ parts.push(key + '=File(' + value.name + ',' + value.type + ',' + value.size + ')');
71
+ } else {
72
+ parts.push(key + '=' + String(value));
73
+ }
74
+ }
75
+ return { kind: 'formdata', preview: parts.join('&').slice(0, 2000) };
76
+ }
77
+ if (body instanceof Blob) {
78
+ return { kind: 'blob', preview: 'Blob(' + body.type + ',' + body.size + ')' };
79
+ }
80
+ if (body instanceof ArrayBuffer) {
81
+ return { kind: 'arraybuffer', preview: 'ArrayBuffer(' + body.byteLength + ')' };
82
+ }
83
+ if (ArrayBuffer.isView(body)) {
84
+ return { kind: 'typed-array', preview: body.constructor.name + '(' + body.byteLength + ')' };
85
+ }
86
+ return { kind: typeof body, preview: String(body).slice(0, 1000) };
87
+ } catch (error) {
88
+ return { kind: 'unknown', preview: 'body-preview-error:' + String(error) };
89
+ }
90
+ };
91
+
92
+ const capture = async (kind, url, method, headers, body, response) => {
93
+ if (!shouldCapture(url)) return;
94
+ try {
95
+ const bodyInfo = await summarizeBody(body);
96
+ const contentType = response?.headers?.get?.('content-type') || '';
97
+ let responsePreview = '';
98
+ try {
99
+ if (response && typeof response.clone === 'function') {
100
+ const clone = response.clone();
101
+ responsePreview = (await clone.text()).slice(0, 4000);
102
+ }
103
+ } catch (error) {
104
+ responsePreview = 'response-preview-error:' + String(error);
105
+ }
106
+ window[CAPTURE_VAR].push({
107
+ kind,
108
+ url: String(url || ''),
109
+ method: String(method || 'GET').toUpperCase(),
110
+ requestHeaders: normalizeHeaders(headers),
111
+ requestBodyKind: bodyInfo.kind,
112
+ requestBodyPreview: bodyInfo.preview,
113
+ responseStatus: response?.status,
114
+ responseContentType: contentType,
115
+ responsePreview,
116
+ timestamp: Date.now(),
117
+ });
118
+ } catch (error) {
119
+ window[CAPTURE_ERRORS_VAR].push(String(error));
120
+ }
121
+ };
122
+
123
+ if (!Array.isArray(window[CAPTURE_VAR])) window[CAPTURE_VAR] = [];
124
+ if (!Array.isArray(window[CAPTURE_ERRORS_VAR])) window[CAPTURE_ERRORS_VAR] = [];
125
+ if (window[PATCH_GUARD]) return { ok: true };
126
+
127
+ const origFetch = window.fetch;
128
+ window.fetch = async function(...args) {
129
+ const input = args[0];
130
+ const init = args[1] || {};
131
+ const url = typeof input === 'string'
132
+ ? input
133
+ : input instanceof Request
134
+ ? input.url
135
+ : String(input || '');
136
+ const method = init.method || (input instanceof Request ? input.method : 'GET');
137
+ const headers = init.headers || (input instanceof Request ? input.headers : undefined);
138
+ const body = init.body || (input instanceof Request ? input.body : undefined);
139
+ const response = await origFetch.apply(this, args);
140
+ capture('fetch', url, method, headers, body, response);
141
+ return response;
142
+ };
143
+
144
+ const origOpen = XMLHttpRequest.prototype.open;
145
+ const origSend = XMLHttpRequest.prototype.send;
146
+ const origSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
147
+
148
+ XMLHttpRequest.prototype.open = function(method, url) {
149
+ this.__opencli_method = method;
150
+ this.__opencli_url = url;
151
+ this.__opencli_headers = {};
152
+ return origOpen.apply(this, arguments);
153
+ };
154
+ XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
155
+ try {
156
+ this.__opencli_headers = this.__opencli_headers || {};
157
+ this.__opencli_headers[String(name)] = String(value);
158
+ } catch {}
159
+ return origSetRequestHeader.apply(this, arguments);
160
+ };
161
+ XMLHttpRequest.prototype.send = function(body) {
162
+ this.addEventListener('load', () => {
163
+ if (!shouldCapture(this.__opencli_url)) return;
164
+ try {
165
+ window[CAPTURE_VAR].push({
166
+ kind: 'xhr',
167
+ url: String(this.__opencli_url || ''),
168
+ method: String(this.__opencli_method || 'GET').toUpperCase(),
169
+ requestHeaders: this.__opencli_headers || {},
170
+ requestBodyKind: body == null ? 'empty' : (body instanceof FormData ? 'formdata' : typeof body),
171
+ requestBodyPreview: body == null ? '' : (body instanceof FormData ? '[formdata]' : String(body).slice(0, 2000)),
172
+ responseStatus: this.status,
173
+ responseContentType: this.getResponseHeader('content-type') || '',
174
+ responsePreview: String(this.responseText || '').slice(0, 4000),
175
+ timestamp: Date.now(),
176
+ });
177
+ } catch (error) {
178
+ window[CAPTURE_ERRORS_VAR].push(String(error));
179
+ }
180
+ });
181
+ return origSend.apply(this, arguments);
182
+ };
183
+
184
+ window[PATCH_GUARD] = true;
185
+ return { ok: true };
186
+ })()
187
+ `;
188
+ }
189
+ export function buildReadInstagramProtocolCaptureJs(captureVar = DEFAULT_CAPTURE_VAR, captureErrorsVar = DEFAULT_CAPTURE_ERRORS_VAR) {
190
+ return `
191
+ (() => {
192
+ const data = Array.isArray(window[${JSON.stringify(captureVar)}]) ? window[${JSON.stringify(captureVar)}] : [];
193
+ const errors = Array.isArray(window[${JSON.stringify(captureErrorsVar)}]) ? window[${JSON.stringify(captureErrorsVar)}] : [];
194
+ window[${JSON.stringify(captureVar)}] = [];
195
+ window[${JSON.stringify(captureErrorsVar)}] = [];
196
+ return { data, errors };
197
+ })()
198
+ `;
199
+ }
200
+ export async function installInstagramProtocolCapture(page) {
201
+ if (typeof page.startNetworkCapture === 'function') {
202
+ try {
203
+ await page.startNetworkCapture(INSTAGRAM_PROTOCOL_CAPTURE_PATTERN);
204
+ return;
205
+ }
206
+ catch (error) {
207
+ const message = error instanceof Error ? error.message : String(error);
208
+ if (!message.includes('Unknown action') && !message.includes('network-capture')) {
209
+ throw error;
210
+ }
211
+ }
212
+ }
213
+ await page.evaluate(buildInstallInstagramProtocolCaptureJs());
214
+ }
215
+ export async function readInstagramProtocolCapture(page) {
216
+ if (typeof page.readNetworkCapture === 'function') {
217
+ try {
218
+ const data = await page.readNetworkCapture();
219
+ return {
220
+ data: Array.isArray(data) ? data : [],
221
+ errors: [],
222
+ };
223
+ }
224
+ catch (error) {
225
+ const message = error instanceof Error ? error.message : String(error);
226
+ if (!message.includes('Unknown action') && !message.includes('network-capture')) {
227
+ throw error;
228
+ }
229
+ }
230
+ }
231
+ const result = await page.evaluate(buildReadInstagramProtocolCaptureJs());
232
+ return {
233
+ data: Array.isArray(result?.data) ? result.data : [],
234
+ errors: Array.isArray(result?.errors) ? result.errors : [],
235
+ };
236
+ }
237
+ export async function dumpInstagramProtocolCaptureIfEnabled(page) {
238
+ if (process.env.OPENCLI_INSTAGRAM_CAPTURE !== '1')
239
+ return;
240
+ const payload = await readInstagramProtocolCapture(page);
241
+ fs.writeFileSync(TRACE_OUTPUT_PATH, JSON.stringify(payload, null, 2));
242
+ }
243
+ function buildCookieHeader(cookies) {
244
+ return cookies
245
+ .filter((cookie) => cookie?.name && cookie?.value)
246
+ .map((cookie) => `${cookie.name}=${cookie.value}`)
247
+ .join('; ');
248
+ }
249
+ export async function instagramPrivateApiFetch(page, input, init = {}) {
250
+ const url = String(input);
251
+ const [urlCookies, domainCookies] = await Promise.all([
252
+ page.getCookies({ url }),
253
+ page.getCookies({ domain: 'instagram.com' }),
254
+ ]);
255
+ const merged = new Map();
256
+ for (const cookie of domainCookies)
257
+ merged.set(cookie.name, cookie);
258
+ for (const cookie of urlCookies)
259
+ merged.set(cookie.name, cookie);
260
+ const cookieHeader = buildCookieHeader(Array.from(merged.values()));
261
+ const csrf = merged.get('csrftoken')?.value || '';
262
+ const initHeaders = init.headers ?? {};
263
+ const requestedAppIdHeader = Object.entries(initHeaders).find(([key]) => key.toLowerCase() === 'x-ig-app-id')?.[1] || '';
264
+ const runtimeInfo = requestedAppIdHeader ? null : await resolveInstagramRuntimeInfo(page);
265
+ const appId = requestedAppIdHeader || runtimeInfo?.appId || '';
266
+ const hasContentType = Object.keys(init.headers ?? {}).some((key) => key.toLowerCase() === 'content-type');
267
+ return fetch(url, {
268
+ method: init.method ?? 'GET',
269
+ headers: {
270
+ 'Accept': 'application/json, text/plain, */*',
271
+ 'X-CSRFToken': csrf,
272
+ 'X-Requested-With': 'XMLHttpRequest',
273
+ 'Origin': 'https://www.instagram.com',
274
+ 'Referer': 'https://www.instagram.com/',
275
+ ...(appId ? { 'X-IG-App-ID': appId } : {}),
276
+ ...(cookieHeader ? { 'Cookie': cookieHeader } : {}),
277
+ ...(typeof init.body === 'string' && !hasContentType ? { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } : {}),
278
+ ...initHeaders,
279
+ },
280
+ ...(init.body !== undefined ? { body: init.body } : {}),
281
+ });
282
+ }
@@ -0,0 +1,114 @@
1
+ import * as fs from 'node:fs';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { buildInstallInstagramProtocolCaptureJs, buildReadInstagramProtocolCaptureJs, dumpInstagramProtocolCaptureIfEnabled, instagramPrivateApiFetch, installInstagramProtocolCapture, readInstagramProtocolCapture, } from './protocol-capture.js';
4
+ describe('instagram protocol capture helpers', () => {
5
+ afterEach(() => {
6
+ vi.restoreAllMocks();
7
+ delete process.env.OPENCLI_INSTAGRAM_CAPTURE;
8
+ try {
9
+ fs.rmSync('/tmp/instagram_post_protocol_trace.json', { force: true });
10
+ }
11
+ catch { }
12
+ });
13
+ it('installs the protocol capture patch in page context', async () => {
14
+ const evaluate = vi.fn().mockResolvedValue({ ok: true });
15
+ const page = { evaluate };
16
+ await installInstagramProtocolCapture(page);
17
+ expect(evaluate).toHaveBeenCalledTimes(1);
18
+ expect(String(evaluate.mock.calls[0]?.[0] || '')).toContain('__opencli_ig_protocol_capture');
19
+ expect(String(evaluate.mock.calls[0]?.[0] || '')).toContain('/media/configure_sidecar/');
20
+ });
21
+ it('prefers native page network capture when available', async () => {
22
+ const startNetworkCapture = vi.fn().mockResolvedValue(undefined);
23
+ const evaluate = vi.fn();
24
+ const page = { startNetworkCapture, evaluate };
25
+ await installInstagramProtocolCapture(page);
26
+ expect(startNetworkCapture).toHaveBeenCalledTimes(1);
27
+ expect(evaluate).not.toHaveBeenCalled();
28
+ });
29
+ it('reads and normalizes captured protocol entries', async () => {
30
+ const evaluate = vi.fn().mockResolvedValue({
31
+ data: [{ kind: 'fetch', url: 'https://www.instagram.com/api/v1/media/configure/' }],
32
+ errors: ['ignored'],
33
+ });
34
+ const page = { evaluate };
35
+ const result = await readInstagramProtocolCapture(page);
36
+ expect(String(evaluate.mock.calls[0]?.[0] || '')).toContain('__opencli_ig_protocol_capture');
37
+ expect(result).toEqual({
38
+ data: [{ kind: 'fetch', url: 'https://www.instagram.com/api/v1/media/configure/' }],
39
+ errors: ['ignored'],
40
+ });
41
+ });
42
+ it('prefers native page network capture reads when available', async () => {
43
+ const readNetworkCapture = vi.fn().mockResolvedValue([
44
+ { kind: 'cdp', url: 'https://www.instagram.com/rupload_igphoto/test', method: 'POST' },
45
+ ]);
46
+ const evaluate = vi.fn();
47
+ const page = { readNetworkCapture, evaluate };
48
+ const result = await readInstagramProtocolCapture(page);
49
+ expect(readNetworkCapture).toHaveBeenCalledTimes(1);
50
+ expect(evaluate).not.toHaveBeenCalled();
51
+ expect(result).toEqual({
52
+ data: [{ kind: 'cdp', url: 'https://www.instagram.com/rupload_igphoto/test', method: 'POST' }],
53
+ errors: [],
54
+ });
55
+ });
56
+ it('dumps protocol traces to /tmp only when capture env is enabled', async () => {
57
+ process.env.OPENCLI_INSTAGRAM_CAPTURE = '1';
58
+ const page = {
59
+ evaluate: vi.fn().mockResolvedValue({
60
+ data: [{ kind: 'fetch', url: 'https://www.instagram.com/rupload_igphoto/test' }],
61
+ errors: [],
62
+ }),
63
+ };
64
+ await dumpInstagramProtocolCaptureIfEnabled(page);
65
+ const raw = fs.readFileSync('/tmp/instagram_post_protocol_trace.json', 'utf8');
66
+ expect(raw).toContain('rupload_igphoto');
67
+ });
68
+ it('does not dump protocol traces when capture env is disabled', async () => {
69
+ const page = {
70
+ evaluate: vi.fn(),
71
+ };
72
+ await dumpInstagramProtocolCaptureIfEnabled(page);
73
+ expect(page.evaluate).not.toHaveBeenCalled();
74
+ expect(fs.existsSync('/tmp/instagram_post_protocol_trace.json')).toBe(false);
75
+ });
76
+ });
77
+ describe('instagram private api fetch', () => {
78
+ afterEach(() => {
79
+ vi.restoreAllMocks();
80
+ });
81
+ it('uses browser cookies to build instagram private api requests', async () => {
82
+ const getCookies = vi.fn()
83
+ .mockResolvedValueOnce([{ name: 'sessionid', value: 'sess', domain: '.instagram.com' }])
84
+ .mockResolvedValueOnce([
85
+ { name: 'csrftoken', value: 'csrf', domain: '.instagram.com' },
86
+ { name: 'sessionid', value: 'sess', domain: '.instagram.com' },
87
+ ]);
88
+ const evaluate = vi.fn().mockResolvedValue({
89
+ appId: 'dynamic-app-id',
90
+ csrfToken: 'csrf',
91
+ instagramAjax: 'dynamic-rollout',
92
+ });
93
+ const page = { getCookies, evaluate };
94
+ const fetchMock = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
95
+ vi.stubGlobal('fetch', fetchMock);
96
+ await instagramPrivateApiFetch(page, 'https://www.instagram.com/api/v1/media/configure/', {
97
+ method: 'POST',
98
+ body: 'caption=test',
99
+ });
100
+ expect(fetchMock).toHaveBeenCalledWith('https://www.instagram.com/api/v1/media/configure/', expect.objectContaining({
101
+ method: 'POST',
102
+ headers: expect.objectContaining({
103
+ 'X-CSRFToken': 'csrf',
104
+ 'X-IG-App-ID': 'dynamic-app-id',
105
+ 'Cookie': expect.stringContaining('sessionid=sess'),
106
+ }),
107
+ body: 'caption=test',
108
+ }));
109
+ });
110
+ it('exposes stable browser-side JS builders', () => {
111
+ expect(buildInstallInstagramProtocolCaptureJs()).toContain('/rupload_igphoto/');
112
+ expect(buildReadInstagramProtocolCaptureJs()).toContain('__opencli_ig_protocol_capture');
113
+ });
114
+ });
@@ -0,0 +1,9 @@
1
+ import type { IPage } from '../../../types.js';
2
+ export interface InstagramRuntimeInfo {
3
+ appId: string;
4
+ csrfToken: string;
5
+ instagramAjax: string;
6
+ }
7
+ export declare function extractInstagramRuntimeInfo(html: string): InstagramRuntimeInfo;
8
+ export declare function buildReadInstagramRuntimeInfoJs(): string;
9
+ export declare function resolveInstagramRuntimeInfo(page: IPage): Promise<InstagramRuntimeInfo>;
@@ -0,0 +1,81 @@
1
+ function pickMatch(input, patterns) {
2
+ for (const pattern of patterns) {
3
+ const match = input.match(pattern);
4
+ if (!match)
5
+ continue;
6
+ for (let index = 1; index < match.length; index += 1) {
7
+ if (match[index])
8
+ return match[index];
9
+ }
10
+ return match[0] || '';
11
+ }
12
+ return '';
13
+ }
14
+ export function extractInstagramRuntimeInfo(html) {
15
+ return {
16
+ appId: pickMatch(html, [
17
+ /"X-IG-App-ID":"(\d+)"/,
18
+ /"appId":"(\d+)"/,
19
+ /"app_id":"(\d+)"/,
20
+ /"instagramWebAppId":"(\d+)"/,
21
+ ]),
22
+ csrfToken: pickMatch(html, [
23
+ /"csrf_token":"([^"]+)"/,
24
+ /"csrfToken":"([^"]+)"/,
25
+ ]),
26
+ instagramAjax: pickMatch(html, [
27
+ /"rollout_hash":"([^"]+)"/,
28
+ /"X-Instagram-AJAX":"([^"]+)"/,
29
+ /"Instagram-AJAX":"([^"]+)"/,
30
+ ]),
31
+ };
32
+ }
33
+ export function buildReadInstagramRuntimeInfoJs() {
34
+ return `
35
+ (() => {
36
+ const html = document.documentElement?.outerHTML || '';
37
+ const pick = (patterns) => {
38
+ for (const pattern of patterns) {
39
+ const match = html.match(new RegExp(pattern, 'i'));
40
+ if (!match) continue;
41
+ for (let index = 1; index < match.length; index += 1) {
42
+ if (match[index]) return match[index];
43
+ }
44
+ return match[0] || '';
45
+ }
46
+ return '';
47
+ };
48
+ return {
49
+ appId: pick([
50
+ '"X-IG-App-ID":"(\\\\d+)"',
51
+ '"appId":"(\\\\d+)"',
52
+ '"app_id":"(\\\\d+)"',
53
+ '"instagramWebAppId":"(\\\\d+)"',
54
+ ]),
55
+ csrfToken: pick([
56
+ '"csrf_token":"([^"]+)"',
57
+ '"csrfToken":"([^"]+)"',
58
+ ]),
59
+ instagramAjax: pick([
60
+ '"rollout_hash":"([^"]+)"',
61
+ '"X-Instagram-AJAX":"([^"]+)"',
62
+ '"Instagram-AJAX":"([^"]+)"',
63
+ ]),
64
+ };
65
+ })()
66
+ `;
67
+ }
68
+ function getCookieValue(cookies, name) {
69
+ return cookies.find((cookie) => cookie.name === name)?.value || '';
70
+ }
71
+ export async function resolveInstagramRuntimeInfo(page) {
72
+ const [runtime, cookies] = await Promise.all([
73
+ page.evaluate(buildReadInstagramRuntimeInfoJs()),
74
+ page.getCookies({ domain: 'instagram.com' }),
75
+ ]);
76
+ return {
77
+ appId: runtime?.appId || '',
78
+ csrfToken: runtime?.csrfToken || getCookieValue(cookies, 'csrftoken') || '',
79
+ instagramAjax: runtime?.instagramAjax || '',
80
+ };
81
+ }
@@ -0,0 +1 @@
1
+ export {};