@jackwener/opencli 1.5.2 → 1.5.4

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 (135) hide show
  1. package/.agents/skills/cross-project-adapter-migration/SKILL.md +3 -3
  2. package/.github/workflows/ci.yml +6 -7
  3. package/README.md +89 -235
  4. package/dist/browser/cdp.js +20 -1
  5. package/dist/browser/daemon-client.js +3 -2
  6. package/dist/browser/dom-helpers.d.ts +11 -0
  7. package/dist/browser/dom-helpers.js +42 -0
  8. package/dist/browser/dom-helpers.test.d.ts +1 -0
  9. package/dist/browser/dom-helpers.test.js +92 -0
  10. package/dist/browser/index.d.ts +0 -12
  11. package/dist/browser/index.js +0 -13
  12. package/dist/browser/mcp.js +4 -3
  13. package/dist/browser/page.d.ts +1 -0
  14. package/dist/browser/page.js +14 -1
  15. package/dist/browser.test.js +15 -11
  16. package/dist/build-manifest.d.ts +2 -3
  17. package/dist/build-manifest.js +75 -170
  18. package/dist/build-manifest.test.js +113 -88
  19. package/dist/cli-manifest.json +1199 -1106
  20. package/dist/clis/36kr/hot.js +1 -1
  21. package/dist/clis/36kr/search.js +1 -1
  22. package/dist/clis/_shared/common.d.ts +8 -0
  23. package/dist/clis/_shared/common.js +10 -0
  24. package/dist/clis/bloomberg/news.js +1 -1
  25. package/dist/clis/douban/utils.js +3 -6
  26. package/dist/clis/medium/utils.js +1 -1
  27. package/dist/clis/producthunt/browse.js +1 -1
  28. package/dist/clis/producthunt/hot.js +1 -1
  29. package/dist/clis/sinablog/utils.js +6 -7
  30. package/dist/clis/substack/utils.js +2 -2
  31. package/dist/clis/twitter/block.js +1 -1
  32. package/dist/clis/twitter/bookmark.js +1 -1
  33. package/dist/clis/twitter/delete.js +1 -1
  34. package/dist/clis/twitter/follow.js +1 -1
  35. package/dist/clis/twitter/followers.js +2 -2
  36. package/dist/clis/twitter/following.js +2 -2
  37. package/dist/clis/twitter/hide-reply.js +1 -1
  38. package/dist/clis/twitter/like.js +1 -1
  39. package/dist/clis/twitter/notifications.js +1 -1
  40. package/dist/clis/twitter/profile.js +1 -1
  41. package/dist/clis/twitter/reply-dm.js +1 -1
  42. package/dist/clis/twitter/reply.js +1 -1
  43. package/dist/clis/twitter/search.js +1 -1
  44. package/dist/clis/twitter/unblock.js +1 -1
  45. package/dist/clis/twitter/unbookmark.js +1 -1
  46. package/dist/clis/twitter/unfollow.js +1 -1
  47. package/dist/clis/xiaohongshu/comments.test.js +1 -0
  48. package/dist/clis/xiaohongshu/creator-note-detail.test.js +1 -0
  49. package/dist/clis/xiaohongshu/creator-notes.test.js +1 -0
  50. package/dist/clis/xiaohongshu/publish.test.js +1 -0
  51. package/dist/clis/xiaohongshu/search.test.js +1 -0
  52. package/dist/daemon.js +14 -3
  53. package/dist/download/index.js +39 -33
  54. package/dist/download/index.test.js +15 -1
  55. package/dist/execution.js +3 -2
  56. package/dist/external-clis.yaml +16 -0
  57. package/dist/main.js +2 -0
  58. package/dist/node-network.d.ts +10 -0
  59. package/dist/node-network.js +174 -0
  60. package/dist/node-network.test.d.ts +1 -0
  61. package/dist/node-network.test.js +55 -0
  62. package/dist/pipeline/executor.test.js +1 -0
  63. package/dist/pipeline/steps/download.test.js +1 -0
  64. package/dist/pipeline/steps/intercept.js +4 -5
  65. package/dist/serialization.js +6 -1
  66. package/dist/serialization.test.d.ts +1 -0
  67. package/dist/serialization.test.js +23 -0
  68. package/dist/types.d.ts +2 -0
  69. package/dist/utils.d.ts +2 -0
  70. package/dist/utils.js +4 -0
  71. package/docs/superpowers/plans/2026-03-28-perf-smart-wait.md +1143 -0
  72. package/docs/superpowers/specs/2026-03-28-perf-smart-wait-design.md +170 -0
  73. package/extension/dist/background.js +12 -5
  74. package/extension/manifest.json +2 -2
  75. package/extension/package-lock.json +2 -2
  76. package/extension/package.json +1 -1
  77. package/extension/src/background.ts +20 -6
  78. package/extension/src/protocol.ts +2 -1
  79. package/package.json +2 -1
  80. package/src/browser/cdp.ts +21 -0
  81. package/src/browser/daemon-client.ts +3 -2
  82. package/src/browser/dom-helpers.test.ts +100 -0
  83. package/src/browser/dom-helpers.ts +44 -0
  84. package/src/browser/index.ts +0 -15
  85. package/src/browser/mcp.ts +4 -3
  86. package/src/browser/page.ts +16 -0
  87. package/src/browser.test.ts +16 -12
  88. package/src/build-manifest.test.ts +117 -88
  89. package/src/build-manifest.ts +81 -180
  90. package/src/clis/36kr/hot.ts +1 -1
  91. package/src/clis/36kr/search.ts +1 -1
  92. package/src/clis/_shared/common.ts +11 -0
  93. package/src/clis/bloomberg/news.ts +1 -1
  94. package/src/clis/douban/utils.ts +3 -7
  95. package/src/clis/medium/utils.ts +1 -1
  96. package/src/clis/producthunt/browse.ts +1 -1
  97. package/src/clis/producthunt/hot.ts +1 -1
  98. package/src/clis/sinablog/utils.ts +6 -7
  99. package/src/clis/substack/utils.ts +2 -2
  100. package/src/clis/twitter/block.ts +1 -1
  101. package/src/clis/twitter/bookmark.ts +1 -1
  102. package/src/clis/twitter/delete.ts +1 -1
  103. package/src/clis/twitter/follow.ts +1 -1
  104. package/src/clis/twitter/followers.ts +2 -2
  105. package/src/clis/twitter/following.ts +2 -2
  106. package/src/clis/twitter/hide-reply.ts +1 -1
  107. package/src/clis/twitter/like.ts +1 -1
  108. package/src/clis/twitter/notifications.ts +1 -1
  109. package/src/clis/twitter/profile.ts +1 -1
  110. package/src/clis/twitter/reply-dm.ts +1 -1
  111. package/src/clis/twitter/reply.ts +1 -1
  112. package/src/clis/twitter/search.ts +1 -1
  113. package/src/clis/twitter/unblock.ts +1 -1
  114. package/src/clis/twitter/unbookmark.ts +1 -1
  115. package/src/clis/twitter/unfollow.ts +1 -1
  116. package/src/clis/xiaohongshu/comments.test.ts +1 -0
  117. package/src/clis/xiaohongshu/creator-note-detail.test.ts +1 -0
  118. package/src/clis/xiaohongshu/creator-notes.test.ts +1 -0
  119. package/src/clis/xiaohongshu/publish.test.ts +1 -0
  120. package/src/clis/xiaohongshu/search.test.ts +1 -0
  121. package/src/daemon.ts +16 -4
  122. package/src/download/index.test.ts +19 -1
  123. package/src/download/index.ts +50 -41
  124. package/src/execution.ts +3 -2
  125. package/src/external-clis.yaml +16 -0
  126. package/src/main.ts +3 -0
  127. package/src/node-network.test.ts +93 -0
  128. package/src/node-network.ts +213 -0
  129. package/src/pipeline/executor.test.ts +1 -0
  130. package/src/pipeline/steps/download.test.ts +1 -0
  131. package/src/pipeline/steps/intercept.ts +4 -5
  132. package/src/serialization.test.ts +26 -0
  133. package/src/serialization.ts +6 -1
  134. package/src/types.ts +2 -0
  135. package/src/utils.ts +5 -0
@@ -2,65 +2,8 @@ import { afterEach, describe, expect, it } from 'vitest';
2
2
  import * as fs from 'node:fs';
3
3
  import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
- import { parseTsArgsBlock, scanTs, shouldReplaceManifestEntry } from './build-manifest.js';
6
- describe('parseTsArgsBlock', () => {
7
- it('keeps args with nested choices arrays', () => {
8
- const args = parseTsArgsBlock(`
9
- {
10
- name: 'period',
11
- type: 'string',
12
- default: 'seven',
13
- help: 'Stats period: seven or thirty',
14
- choices: ['seven', 'thirty'],
15
- },
16
- `);
17
- expect(args).toEqual([
18
- {
19
- name: 'period',
20
- type: 'string',
21
- default: 'seven',
22
- required: false,
23
- positional: undefined,
24
- help: 'Stats period: seven or thirty',
25
- choices: ['seven', 'thirty'],
26
- },
27
- ]);
28
- });
29
- it('keeps hyphenated arg names from TS adapters', () => {
30
- const args = parseTsArgsBlock(`
31
- {
32
- name: 'tweet-url',
33
- help: 'Single tweet URL to download',
34
- },
35
- {
36
- name: 'download-images',
37
- type: 'boolean',
38
- default: false,
39
- help: 'Download images locally',
40
- },
41
- `);
42
- expect(args).toEqual([
43
- {
44
- name: 'tweet-url',
45
- type: 'str',
46
- default: undefined,
47
- required: false,
48
- positional: undefined,
49
- help: 'Single tweet URL to download',
50
- choices: undefined,
51
- },
52
- {
53
- name: 'download-images',
54
- type: 'boolean',
55
- default: false,
56
- required: false,
57
- positional: undefined,
58
- help: 'Download images locally',
59
- choices: undefined,
60
- },
61
- ]);
62
- });
63
- });
5
+ import { cli, getRegistry, Strategy } from './registry.js';
6
+ import { loadTsManifestEntries, shouldReplaceManifestEntry } from './build-manifest.js';
64
7
  describe('manifest helper rules', () => {
65
8
  const tempDirs = [];
66
9
  afterEach(() => {
@@ -111,39 +54,121 @@ describe('manifest helper rules', () => {
111
54
  tempDirs.push(dir);
112
55
  const file = path.join(dir, 'utils.ts');
113
56
  fs.writeFileSync(file, `export function helper() { return 'noop'; }`);
114
- expect(scanTs(file, 'demo')).toBeNull();
57
+ return expect(loadTsManifestEntries(file, 'demo', async () => ({}))).resolves.toEqual([]);
115
58
  });
116
- it('keeps literal domain and navigateBefore for TS adapters', () => {
117
- const file = path.join(process.cwd(), 'src', 'clis', 'xueqiu', 'fund-holdings.ts');
118
- const entry = scanTs(file, 'xueqiu');
119
- expect(entry).toMatchObject({
120
- site: 'xueqiu',
121
- name: 'fund-holdings',
122
- domain: 'danjuanfunds.com',
123
- navigateBefore: 'https://danjuanfunds.com/my-money',
124
- type: 'ts',
125
- modulePath: 'xueqiu/fund-holdings.js',
126
- });
59
+ it('builds TS manifest entries from exported runtime commands', async () => {
60
+ const site = `manifest-hydrate-${Date.now()}`;
61
+ const key = `${site}/dynamic`;
62
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
63
+ tempDirs.push(dir);
64
+ const file = path.join(dir, `${site}.ts`);
65
+ fs.writeFileSync(file, `export const command = cli({ site: '${site}', name: 'dynamic' });`);
66
+ const entries = await loadTsManifestEntries(file, site, async () => ({
67
+ command: cli({
68
+ site,
69
+ name: 'dynamic',
70
+ description: 'dynamic command',
71
+ strategy: Strategy.PUBLIC,
72
+ browser: false,
73
+ args: [
74
+ {
75
+ name: 'model',
76
+ required: true,
77
+ positional: true,
78
+ help: 'Choose a model',
79
+ choices: ['auto', 'thinking'],
80
+ default: '30',
81
+ },
82
+ ],
83
+ domain: 'localhost',
84
+ navigateBefore: 'https://example.com/session',
85
+ deprecated: 'legacy command',
86
+ replacedBy: 'opencli demo new',
87
+ }),
88
+ }));
89
+ expect(entries).toEqual([
90
+ {
91
+ site,
92
+ name: 'dynamic',
93
+ description: 'dynamic command',
94
+ domain: 'localhost',
95
+ strategy: 'public',
96
+ browser: false,
97
+ args: [
98
+ {
99
+ name: 'model',
100
+ type: 'str',
101
+ required: true,
102
+ positional: true,
103
+ help: 'Choose a model',
104
+ choices: ['auto', 'thinking'],
105
+ default: '30',
106
+ },
107
+ ],
108
+ type: 'ts',
109
+ modulePath: `${site}/${site}.js`,
110
+ navigateBefore: 'https://example.com/session',
111
+ deprecated: 'legacy command',
112
+ replacedBy: 'opencli demo new',
113
+ },
114
+ ]);
115
+ getRegistry().delete(key);
127
116
  });
128
- it('captures deprecated metadata for TS adapters', () => {
117
+ it('falls back to registry delta for side-effect-only cli modules', async () => {
118
+ const site = `manifest-side-effect-${Date.now()}`;
119
+ const key = `${site}/legacy`;
129
120
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
130
121
  tempDirs.push(dir);
131
- const file = path.join(dir, 'legacy.ts');
132
- fs.writeFileSync(file, `
133
- import { cli } from '../../registry.js';
134
- cli({
135
- site: 'demo',
136
- name: 'legacy',
137
- description: 'legacy command',
138
- deprecated: 'legacy is deprecated',
139
- replacedBy: 'opencli demo new',
140
- });
141
- `);
142
- expect(scanTs(file, 'demo')).toMatchObject({
143
- site: 'demo',
144
- name: 'legacy',
145
- deprecated: 'legacy is deprecated',
146
- replacedBy: 'opencli demo new',
122
+ const file = path.join(dir, `${site}.ts`);
123
+ fs.writeFileSync(file, `cli({ site: '${site}', name: 'legacy' });`);
124
+ const entries = await loadTsManifestEntries(file, site, async () => {
125
+ cli({
126
+ site,
127
+ name: 'legacy',
128
+ description: 'legacy command',
129
+ deprecated: 'legacy is deprecated',
130
+ replacedBy: 'opencli demo new',
131
+ });
132
+ return {};
147
133
  });
134
+ expect(entries).toEqual([
135
+ {
136
+ site,
137
+ name: 'legacy',
138
+ description: 'legacy command',
139
+ strategy: 'cookie',
140
+ browser: true,
141
+ args: [],
142
+ type: 'ts',
143
+ modulePath: `${site}/${site}.js`,
144
+ deprecated: 'legacy is deprecated',
145
+ replacedBy: 'opencli demo new',
146
+ },
147
+ ]);
148
+ getRegistry().delete(key);
149
+ });
150
+ it('keeps every command a module exports instead of guessing by site', async () => {
151
+ const site = `manifest-multi-${Date.now()}`;
152
+ const screenKey = `${site}/screen`;
153
+ const statusKey = `${site}/status`;
154
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-manifest-'));
155
+ tempDirs.push(dir);
156
+ const file = path.join(dir, `${site}.ts`);
157
+ fs.writeFileSync(file, `export const screen = cli({ site: '${site}', name: 'screen' });`);
158
+ const entries = await loadTsManifestEntries(file, site, async () => ({
159
+ screen: cli({
160
+ site,
161
+ name: 'screen',
162
+ description: 'capture screen',
163
+ }),
164
+ status: cli({
165
+ site,
166
+ name: 'status',
167
+ description: 'show status',
168
+ }),
169
+ }));
170
+ expect(entries.map(entry => entry.name)).toEqual(['screen', 'status']);
171
+ getRegistry().delete(screenKey);
172
+ getRegistry().delete(statusKey);
148
173
  });
149
174
  });