@jackwener/opencli 1.7.8 → 1.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -14
- package/README.zh-CN.md +30 -10
- package/cli-manifest.json +646 -30
- package/clis/36kr/news.js +1 -1
- package/clis/apple-podcasts/commands.test.js +4 -4
- package/clis/apple-podcasts/episodes.js +1 -1
- package/clis/apple-podcasts/search.js +1 -1
- package/clis/apple-podcasts/top.js +1 -1
- package/clis/arxiv/paper.js +1 -1
- package/clis/arxiv/search.js +1 -1
- package/clis/band/mentions.js +3 -3
- package/clis/bbc/news.js +1 -1
- package/clis/bilibili/subtitle.js +2 -2
- package/clis/bloomberg/businessweek.js +1 -1
- package/clis/bloomberg/economics.js +1 -1
- package/clis/bloomberg/industries.js +1 -1
- package/clis/bloomberg/main.js +1 -1
- package/clis/bloomberg/markets.js +1 -1
- package/clis/bloomberg/opinions.js +1 -1
- package/clis/bloomberg/politics.js +1 -1
- package/clis/bloomberg/tech.js +1 -1
- package/clis/boss/search.js +49 -8
- package/clis/boss/search.test.js +78 -0
- package/clis/boss/send.js +3 -3
- package/clis/chatgpt/image.js +37 -8
- package/clis/chatgpt/image.test.js +92 -0
- package/clis/chatgpt/utils.js +39 -6
- package/clis/chatgpt/utils.test.js +63 -0
- package/clis/chatgpt-app/ask.js +1 -1
- package/clis/chatgpt-app/ax.js +4 -2
- package/clis/chatgpt-app/ax.test.js +12 -0
- package/clis/chatgpt-app/model.js +1 -1
- package/clis/chatgpt-app/new.js +1 -1
- package/clis/chatgpt-app/read.js +1 -1
- package/clis/chatgpt-app/send.js +1 -1
- package/clis/chatgpt-app/status.js +1 -1
- package/clis/chatwise/ask.js +2 -2
- package/clis/chatwise/model.js +2 -2
- package/clis/chatwise/send.js +2 -2
- package/clis/claude/ask.js +128 -0
- package/clis/claude/ask.test.js +338 -0
- package/clis/claude/commands.test.js +118 -0
- package/clis/claude/detail.js +29 -0
- package/clis/claude/history.js +31 -0
- package/clis/claude/new.js +21 -0
- package/clis/claude/read.js +24 -0
- package/clis/claude/send.js +41 -0
- package/clis/claude/status.js +24 -0
- package/clis/claude/utils.js +440 -0
- package/clis/claude/utils.test.js +148 -0
- package/clis/codex/ask.js +2 -2
- package/clis/codex/send.js +2 -2
- package/clis/ctrip/search.js +1 -1
- package/clis/ctrip/search.test.js +4 -4
- package/clis/cursor/ask.js +2 -2
- package/clis/cursor/composer.js +2 -2
- package/clis/cursor/send.js +2 -2
- package/clis/deepseek/ask.js +17 -4
- package/clis/deepseek/ask.test.js +46 -0
- package/clis/deepseek/utils.js +55 -16
- package/clis/deepseek/utils.test.js +124 -5
- package/clis/doubao/utils.js +53 -11
- package/clis/doubao/utils.test.js +22 -2
- package/clis/eastmoney/announcement.js +1 -1
- package/clis/eastmoney/convertible.js +1 -1
- package/clis/eastmoney/etf.js +1 -1
- package/clis/eastmoney/holders.js +1 -1
- package/clis/eastmoney/index-board.js +1 -1
- package/clis/eastmoney/kline.js +1 -1
- package/clis/eastmoney/kuaixun.js +1 -1
- package/clis/eastmoney/longhu.js +1 -1
- package/clis/eastmoney/money-flow.js +1 -1
- package/clis/eastmoney/northbound.js +1 -1
- package/clis/eastmoney/quote.js +1 -1
- package/clis/eastmoney/rank.js +1 -1
- package/clis/eastmoney/sectors.js +1 -1
- package/clis/facebook/marketplace-inbox.js +83 -0
- package/clis/facebook/marketplace-listings.js +83 -0
- package/clis/facebook/marketplace.test.js +91 -0
- package/clis/google/news.js +1 -1
- package/clis/google/suggest.js +1 -1
- package/clis/google/trends.js +1 -1
- package/clis/google-scholar/cite.js +74 -0
- package/clis/google-scholar/cite.test.js +47 -0
- package/clis/google-scholar/profile.js +92 -0
- package/clis/google-scholar/profile.test.js +49 -0
- package/clis/google-scholar/search.js +1 -1
- package/clis/google-scholar/search.test.js +15 -0
- package/clis/hf/top.js +1 -1
- package/clis/instagram/collection-create.js +57 -0
- package/clis/instagram/saved.js +21 -7
- package/clis/jd/item.js +679 -47
- package/clis/jd/item.test.js +318 -7
- package/clis/jd/item.test.ts +517 -0
- package/clis/lesswrong/comments.js +1 -1
- package/clis/lesswrong/curated.js +1 -1
- package/clis/lesswrong/frontpage.js +1 -1
- package/clis/lesswrong/new.js +1 -1
- package/clis/lesswrong/read.js +1 -1
- package/clis/lesswrong/sequences.js +1 -1
- package/clis/lesswrong/shortform.js +1 -1
- package/clis/lesswrong/tag.js +1 -1
- package/clis/lesswrong/tags.js +1 -1
- package/clis/lesswrong/top-month.js +1 -1
- package/clis/lesswrong/top-week.js +1 -1
- package/clis/lesswrong/top-year.js +1 -1
- package/clis/lesswrong/top.js +1 -1
- package/clis/lesswrong/user-posts.js +1 -1
- package/clis/lesswrong/user.js +1 -1
- package/clis/paperreview/commands.test.js +6 -6
- package/clis/paperreview/feedback.js +1 -1
- package/clis/paperreview/review.js +1 -1
- package/clis/paperreview/submit.js +1 -1
- package/clis/producthunt/posts.js +1 -1
- package/clis/producthunt/today.js +1 -1
- package/clis/sinablog/search.js +1 -1
- package/clis/sinafinance/news.js +1 -1
- package/clis/sinafinance/stock.js +1 -1
- package/clis/sinafinance/stock.test.js +2 -2
- package/clis/spotify/spotify.js +6 -6
- package/clis/substack/search.js +1 -1
- package/clis/toutiao/articles.js +5 -6
- package/clis/toutiao/articles.test.js +22 -15
- package/clis/twitter/followers.js +2 -2
- package/clis/twitter/following.js +224 -73
- package/clis/twitter/following.test.js +277 -0
- package/clis/twitter/post.js +184 -47
- package/clis/twitter/post.test.js +114 -34
- package/clis/uiverse/_shared.js +63 -4
- package/clis/uiverse/_shared.test.js +7 -0
- package/clis/uiverse/code.js +1 -0
- package/clis/uiverse/navigation.test.js +12 -0
- package/clis/uiverse/preview.js +1 -0
- package/clis/web/read.js +319 -81
- package/clis/web/read.test.js +221 -5
- package/clis/weibo/favorites.js +169 -0
- package/clis/weibo/favorites.test.js +114 -0
- package/clis/weibo/publish.js +282 -0
- package/clis/weibo/publish.test.js +183 -0
- package/clis/weread/ranking.js +1 -1
- package/clis/weread/search-regression.test.js +8 -8
- package/clis/weread/search.js +1 -1
- package/clis/wikipedia/random.js +1 -1
- package/clis/wikipedia/search.js +1 -1
- package/clis/wikipedia/summary.js +1 -1
- package/clis/wikipedia/trending.js +1 -1
- package/clis/xianyu/chat.js +3 -3
- package/clis/xianyu/item.js +2 -2
- package/clis/xianyu/item.test.js +3 -3
- package/clis/xiaohongshu/search.js +17 -2
- package/clis/xiaohongshu/search.test.js +37 -1
- package/clis/xiaoyuzhou/download.js +1 -1
- package/clis/xiaoyuzhou/download.test.js +3 -3
- package/clis/xiaoyuzhou/episode.js +1 -1
- package/clis/xiaoyuzhou/podcast-episodes.js +1 -1
- package/clis/xiaoyuzhou/podcast-episodes.test.js +2 -2
- package/clis/xiaoyuzhou/podcast.js +1 -1
- package/clis/xiaoyuzhou/transcript.js +1 -1
- package/clis/xiaoyuzhou/transcript.test.js +5 -5
- package/clis/yollomi/models.js +1 -1
- package/clis/youtube/channel.js +24 -1
- package/clis/youtube/channel.test.js +59 -0
- package/clis/zhihu/answer.js +21 -162
- package/clis/zhihu/answer.test.js +26 -53
- package/clis/zhihu/collection.js +197 -0
- package/clis/zhihu/collection.test.js +290 -0
- package/clis/zhihu/collections.js +127 -0
- package/clis/zhihu/collections.test.js +182 -0
- package/clis/zhihu/comment.js +24 -305
- package/clis/zhihu/comment.test.js +31 -35
- package/clis/zhihu/favorite.js +44 -182
- package/clis/zhihu/favorite.test.js +30 -167
- package/clis/zhihu/follow.js +25 -56
- package/clis/zhihu/follow.test.js +20 -23
- package/clis/zhihu/like.js +22 -67
- package/clis/zhihu/like.test.js +19 -42
- package/clis/zhihu/search.js +3 -2
- package/clis/zhihu/write-shared.js +8 -1
- package/clis/zhihu/write-shared.test.js +1 -0
- package/clis/zlibrary/commands.test.js +75 -0
- package/clis/zlibrary/info.js +47 -0
- package/clis/zlibrary/search.js +46 -0
- package/clis/zlibrary/utils.js +136 -0
- package/dist/src/adapter-source.d.ts +11 -0
- package/dist/src/adapter-source.js +24 -0
- package/dist/src/adapter-source.test.js +29 -0
- package/dist/src/browser/base-page.d.ts +3 -1
- package/dist/src/browser/base-page.js +76 -1
- package/dist/src/browser/base-page.test.d.ts +1 -0
- package/dist/src/browser/base-page.test.js +74 -0
- package/dist/src/browser/bridge.d.ts +1 -2
- package/dist/src/browser/bridge.js +40 -41
- package/dist/src/browser/cdp.d.ts +1 -0
- package/dist/src/browser/cdp.js +3 -3
- package/dist/src/browser/daemon-client.d.ts +38 -4
- package/dist/src/browser/daemon-client.js +24 -7
- package/dist/src/browser/daemon-client.test.js +49 -0
- package/dist/src/browser/daemon-lifecycle.d.ts +23 -0
- package/dist/src/browser/daemon-lifecycle.js +67 -0
- package/dist/src/browser/daemon-version.d.ts +4 -0
- package/dist/src/browser/daemon-version.js +12 -0
- package/dist/src/browser/errors.js +3 -0
- package/dist/src/browser/errors.test.js +3 -0
- package/dist/src/browser/network-cache.d.ts +1 -0
- package/dist/src/browser/page.d.ts +3 -1
- package/dist/src/browser/page.js +10 -2
- package/dist/src/browser/profile.d.ts +14 -0
- package/dist/src/browser/profile.js +85 -0
- package/dist/src/build-manifest.d.ts +2 -0
- package/dist/src/build-manifest.js +13 -3
- package/dist/src/build-manifest.test.js +20 -2
- package/dist/src/cli.d.ts +6 -0
- package/dist/src/cli.js +477 -35
- package/dist/src/cli.test.js +303 -2
- package/dist/src/commanderAdapter.js +17 -9
- package/dist/src/commanderAdapter.test.js +67 -2
- package/dist/src/commands/daemon.d.ts +2 -0
- package/dist/src/commands/daemon.js +42 -1
- package/dist/src/commands/daemon.test.js +103 -2
- package/dist/src/completion-shared.js +1 -2
- package/dist/src/completion.test.js +3 -2
- package/dist/src/daemon.js +125 -41
- package/dist/src/doctor.d.ts +5 -6
- package/dist/src/doctor.js +77 -19
- package/dist/src/doctor.test.js +117 -0
- package/dist/src/engine.test.js +6 -5
- package/dist/src/errors.d.ts +14 -8
- package/dist/src/errors.js +36 -30
- package/dist/src/errors.test.js +5 -5
- package/dist/src/execution.d.ts +4 -0
- package/dist/src/execution.js +173 -25
- package/dist/src/execution.test.js +171 -1
- package/dist/src/main.js +10 -0
- package/dist/src/observation/artifact.d.ts +16 -0
- package/dist/src/observation/artifact.js +260 -0
- package/dist/src/observation/artifact.test.d.ts +1 -0
- package/dist/src/observation/artifact.test.js +121 -0
- package/dist/src/observation/events.d.ts +89 -0
- package/dist/src/observation/events.js +1 -0
- package/dist/src/observation/index.d.ts +7 -0
- package/dist/src/observation/index.js +7 -0
- package/dist/src/observation/manager.d.ts +9 -0
- package/dist/src/observation/manager.js +27 -0
- package/dist/src/observation/manager.test.d.ts +1 -0
- package/dist/src/observation/manager.test.js +13 -0
- package/dist/src/observation/redaction.d.ts +11 -0
- package/dist/src/observation/redaction.js +81 -0
- package/dist/src/observation/redaction.test.d.ts +1 -0
- package/dist/src/observation/redaction.test.js +32 -0
- package/dist/src/observation/retention.d.ts +32 -0
- package/dist/src/observation/retention.js +160 -0
- package/dist/src/observation/retention.test.d.ts +1 -0
- package/dist/src/observation/retention.test.js +118 -0
- package/dist/src/observation/ring-buffer.d.ts +22 -0
- package/dist/src/observation/ring-buffer.js +45 -0
- package/dist/src/observation/ring-buffer.test.d.ts +1 -0
- package/dist/src/observation/ring-buffer.test.js +22 -0
- package/dist/src/observation/session.d.ts +25 -0
- package/dist/src/observation/session.js +50 -0
- package/dist/src/pipeline/executor.test.js +1 -0
- package/dist/src/pipeline/steps/download.test.js +1 -0
- package/dist/src/pipeline/steps/fetch.js +1 -21
- package/dist/src/pipeline/steps/fetch.test.js +6 -12
- package/dist/src/plugin-scaffold.js +1 -1
- package/dist/src/plugin-scaffold.test.js +1 -1
- package/dist/src/registry.d.ts +40 -9
- package/dist/src/registry.js +3 -1
- package/dist/src/runtime-detect.d.ts +10 -0
- package/dist/src/runtime-detect.js +19 -0
- package/dist/src/runtime-detect.test.js +12 -1
- package/dist/src/runtime.d.ts +2 -0
- package/dist/src/runtime.js +1 -0
- package/dist/src/types.d.ts +22 -0
- package/dist/src/update-check.d.ts +31 -1
- package/dist/src/update-check.js +62 -16
- package/dist/src/update-check.test.js +86 -1
- package/package.json +1 -1
- package/dist/src/diagnostic.d.ts +0 -63
- package/dist/src/diagnostic.js +0 -292
- package/dist/src/diagnostic.test.js +0 -302
- /package/dist/src/{diagnostic.test.d.ts → adapter-source.test.d.ts} +0 -0
package/dist/src/doctor.test.js
CHANGED
|
@@ -23,15 +23,32 @@ describe('doctor report rendering', () => {
|
|
|
23
23
|
});
|
|
24
24
|
it('renders OK-style report when daemon and extension connected', () => {
|
|
25
25
|
const text = strip(renderBrowserDoctorReport({
|
|
26
|
+
cliVersion: '1.7.9',
|
|
26
27
|
daemonRunning: true,
|
|
28
|
+
daemonVersion: '1.7.9',
|
|
27
29
|
extensionConnected: true,
|
|
28
30
|
extensionVersion: '1.6.8',
|
|
29
31
|
issues: [],
|
|
30
32
|
}));
|
|
31
33
|
expect(text).toContain('[OK] Daemon: running on port 19825');
|
|
34
|
+
expect(text).toContain('(v1.7.9)');
|
|
32
35
|
expect(text).toContain('[OK] Extension: connected (v1.6.8)');
|
|
33
36
|
expect(text).toContain('Everything looks good!');
|
|
34
37
|
});
|
|
38
|
+
it('renders a warning when daemon version is stale', () => {
|
|
39
|
+
const text = strip(renderBrowserDoctorReport({
|
|
40
|
+
cliVersion: '1.7.9',
|
|
41
|
+
daemonRunning: true,
|
|
42
|
+
daemonVersion: '1.7.6',
|
|
43
|
+
daemonStale: true,
|
|
44
|
+
extensionConnected: true,
|
|
45
|
+
extensionVersion: '1.0.3',
|
|
46
|
+
issues: ['Stale daemon detected: daemon v1.7.6 != CLI v1.7.9.\n Run: opencli daemon restart'],
|
|
47
|
+
}));
|
|
48
|
+
expect(text).toContain('[WARN] Daemon: running on port 19825 (v1.7.6, stale; CLI v1.7.9)');
|
|
49
|
+
expect(text).toContain('Run: opencli daemon restart');
|
|
50
|
+
expect(text).not.toContain('Everything looks good!');
|
|
51
|
+
});
|
|
35
52
|
it('renders MISSING when daemon not running', () => {
|
|
36
53
|
const text = strip(renderBrowserDoctorReport({
|
|
37
54
|
daemonRunning: false,
|
|
@@ -78,6 +95,64 @@ describe('doctor report rendering', () => {
|
|
|
78
95
|
}));
|
|
79
96
|
expect(text).toContain('[SKIP] Connectivity: skipped (--no-live)');
|
|
80
97
|
});
|
|
98
|
+
it('renders sessions with tab leases and no idle timer', () => {
|
|
99
|
+
const text = strip(renderBrowserDoctorReport({
|
|
100
|
+
daemonRunning: true,
|
|
101
|
+
extensionConnected: true,
|
|
102
|
+
issues: [],
|
|
103
|
+
sessions: [
|
|
104
|
+
{
|
|
105
|
+
workspace: 'bound:default',
|
|
106
|
+
windowId: 2,
|
|
107
|
+
preferredTabId: 42,
|
|
108
|
+
ownership: 'borrowed',
|
|
109
|
+
surface: 'borrowed-user-tab',
|
|
110
|
+
tabCount: 1,
|
|
111
|
+
idleMsRemaining: null,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
}));
|
|
115
|
+
expect(text).toContain('bound:default → tab 42, mode=borrowed, surface=borrowed-user-tab, tabs=1, idle=none');
|
|
116
|
+
});
|
|
117
|
+
it('renders connected profiles and groups sessions by profile', () => {
|
|
118
|
+
const text = strip(renderBrowserDoctorReport({
|
|
119
|
+
daemonRunning: true,
|
|
120
|
+
extensionConnected: false,
|
|
121
|
+
profiles: [
|
|
122
|
+
{ contextId: 'work', extensionConnected: true, extensionVersion: '1.2.3', pending: 0 },
|
|
123
|
+
{ contextId: 'personal', extensionConnected: true, extensionVersion: '1.2.3', pending: 0 },
|
|
124
|
+
],
|
|
125
|
+
issues: [],
|
|
126
|
+
sessions: [
|
|
127
|
+
{
|
|
128
|
+
contextId: 'work',
|
|
129
|
+
workspace: 'bound:default',
|
|
130
|
+
windowId: 2,
|
|
131
|
+
preferredTabId: 42,
|
|
132
|
+
ownership: 'borrowed',
|
|
133
|
+
surface: 'borrowed-user-tab',
|
|
134
|
+
tabCount: 1,
|
|
135
|
+
idleMsRemaining: null,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
contextId: 'personal',
|
|
139
|
+
workspace: 'site:foo',
|
|
140
|
+
windowId: 1,
|
|
141
|
+
preferredTabId: 10,
|
|
142
|
+
ownership: 'owned',
|
|
143
|
+
surface: 'dedicated-container',
|
|
144
|
+
tabCount: 1,
|
|
145
|
+
idleMsRemaining: 1000,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
}));
|
|
149
|
+
expect(text).toContain('Profiles:');
|
|
150
|
+
expect(text).toContain('work: connected v1.2.3');
|
|
151
|
+
expect(text).toContain('[profile: work]');
|
|
152
|
+
expect(text).toContain('[profile: personal]');
|
|
153
|
+
expect(text).toContain('bound:default → tab 42');
|
|
154
|
+
expect(text).toContain('site:foo → tab 10');
|
|
155
|
+
});
|
|
81
156
|
it('renders unstable extension state when live connectivity and status disagree', () => {
|
|
82
157
|
const text = strip(renderBrowserDoctorReport({
|
|
83
158
|
daemonRunning: true,
|
|
@@ -142,16 +217,19 @@ describe('doctor report rendering', () => {
|
|
|
142
217
|
});
|
|
143
218
|
it('uses the fast default timeout for live connectivity checks', async () => {
|
|
144
219
|
let timeoutSeen;
|
|
220
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
145
221
|
mockConnect.mockImplementationOnce(async (opts) => {
|
|
146
222
|
timeoutSeen = opts?.timeout;
|
|
147
223
|
return {
|
|
148
224
|
evaluate: vi.fn().mockResolvedValue(2),
|
|
225
|
+
closeWindow,
|
|
149
226
|
};
|
|
150
227
|
});
|
|
151
228
|
mockClose.mockResolvedValueOnce(undefined);
|
|
152
229
|
mockGetDaemonHealth.mockResolvedValueOnce({ state: 'ready', status: { extensionConnected: true } });
|
|
153
230
|
await runBrowserDoctor({ live: true });
|
|
154
231
|
expect(timeoutSeen).toBe(8);
|
|
232
|
+
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
155
233
|
});
|
|
156
234
|
it('skips auto-start in no-live mode when daemon is already running', async () => {
|
|
157
235
|
mockGetDaemonHealth.mockResolvedValueOnce({ state: 'no-extension', status: { extensionConnected: false } });
|
|
@@ -177,4 +255,43 @@ describe('doctor report rendering', () => {
|
|
|
177
255
|
expect.stringContaining('did not report a version'),
|
|
178
256
|
]));
|
|
179
257
|
});
|
|
258
|
+
it('reports an issue when daemon version differs from CLI version', async () => {
|
|
259
|
+
const status = {
|
|
260
|
+
state: 'ready',
|
|
261
|
+
status: {
|
|
262
|
+
daemonVersion: '1.7.6',
|
|
263
|
+
extensionConnected: true,
|
|
264
|
+
extensionVersion: '1.0.3',
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
mockGetDaemonHealth
|
|
268
|
+
.mockResolvedValueOnce(status)
|
|
269
|
+
.mockResolvedValueOnce(status);
|
|
270
|
+
const report = await runBrowserDoctor({ live: false, cliVersion: '1.7.9' });
|
|
271
|
+
expect(report.daemonStale).toBe(true);
|
|
272
|
+
expect(report.issues).toEqual(expect.arrayContaining([
|
|
273
|
+
expect.stringContaining('Stale daemon detected: daemon v1.7.6 != CLI v1.7.9'),
|
|
274
|
+
]));
|
|
275
|
+
});
|
|
276
|
+
it('reports profile-required when multiple profiles are connected without a selection', async () => {
|
|
277
|
+
const status = {
|
|
278
|
+
state: 'profile-required',
|
|
279
|
+
status: {
|
|
280
|
+
extensionConnected: false,
|
|
281
|
+
profileRequired: true,
|
|
282
|
+
profiles: [
|
|
283
|
+
{ contextId: 'work', extensionConnected: true, pending: 0 },
|
|
284
|
+
{ contextId: 'personal', extensionConnected: true, pending: 0 },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
mockGetDaemonHealth
|
|
289
|
+
.mockResolvedValueOnce(status)
|
|
290
|
+
.mockResolvedValueOnce(status);
|
|
291
|
+
const report = await runBrowserDoctor({ live: false });
|
|
292
|
+
expect(report.profiles).toHaveLength(2);
|
|
293
|
+
expect(report.issues).toEqual(expect.arrayContaining([
|
|
294
|
+
expect.stringContaining('Multiple Chrome profiles are connected'),
|
|
295
|
+
]));
|
|
296
|
+
});
|
|
180
297
|
});
|
package/dist/src/engine.test.js
CHANGED
|
@@ -137,6 +137,7 @@ describe('discoverPlugins', () => {
|
|
|
137
137
|
const symlinkTargetDir = path.join(os.tmpdir(), '__test-plugin-symlink-target__');
|
|
138
138
|
const symlinkPluginDir = path.join(PLUGINS_DIR, '__test-plugin-symlink__');
|
|
139
139
|
const brokenSymlinkDir = path.join(PLUGINS_DIR, '__test-plugin-broken__');
|
|
140
|
+
const dirSymlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
|
140
141
|
afterEach(async () => {
|
|
141
142
|
try {
|
|
142
143
|
await fs.promises.rm(testPluginDir, { recursive: true });
|
|
@@ -183,14 +184,14 @@ description: Test plugin greeting via symlink
|
|
|
183
184
|
strategy: public
|
|
184
185
|
browser: false
|
|
185
186
|
`);
|
|
186
|
-
await fs.promises.symlink(symlinkTargetDir, symlinkPluginDir,
|
|
187
|
+
await fs.promises.symlink(symlinkTargetDir, symlinkPluginDir, dirSymlinkType);
|
|
187
188
|
await discoverPlugins();
|
|
188
189
|
const cmd = getRegistry().get('__test-plugin-symlink__/hello');
|
|
189
190
|
expect(cmd).toBeUndefined();
|
|
190
191
|
});
|
|
191
192
|
it('skips broken plugin symlinks without throwing', async () => {
|
|
192
193
|
await fs.promises.mkdir(PLUGINS_DIR, { recursive: true });
|
|
193
|
-
await fs.promises.symlink(path.join(os.tmpdir(), '__missing-plugin-target__'), brokenSymlinkDir,
|
|
194
|
+
await fs.promises.symlink(path.join(os.tmpdir(), '__missing-plugin-target__'), brokenSymlinkDir, dirSymlinkType);
|
|
194
195
|
await expect(discoverPlugins()).resolves.not.toThrow();
|
|
195
196
|
expect(getRegistry().get('__test-plugin-broken__/hello')).toBeUndefined();
|
|
196
197
|
});
|
|
@@ -210,7 +211,7 @@ describe('executeCommand', () => {
|
|
|
210
211
|
args: [
|
|
211
212
|
{ name: 'note-id', required: true, help: 'Note ID' },
|
|
212
213
|
],
|
|
213
|
-
func: async (
|
|
214
|
+
func: async (kwargs) => [{ noteId: kwargs['note-id'] }],
|
|
214
215
|
});
|
|
215
216
|
const result = await executeCommand(cmd, { 'note-id': 'abc123' });
|
|
216
217
|
expect(result).toEqual([{ noteId: 'abc123' }]);
|
|
@@ -222,7 +223,7 @@ describe('executeCommand', () => {
|
|
|
222
223
|
description: 'test command with func',
|
|
223
224
|
browser: false,
|
|
224
225
|
strategy: Strategy.PUBLIC,
|
|
225
|
-
func: async (
|
|
226
|
+
func: async (kwargs) => {
|
|
226
227
|
return [{ title: kwargs.query ?? 'default' }];
|
|
227
228
|
},
|
|
228
229
|
});
|
|
@@ -260,7 +261,7 @@ describe('executeCommand', () => {
|
|
|
260
261
|
name: 'debug-test',
|
|
261
262
|
description: 'debug test',
|
|
262
263
|
browser: false,
|
|
263
|
-
func: async (
|
|
264
|
+
func: async (_kwargs, debug) => {
|
|
264
265
|
receivedDebug = debug ?? false;
|
|
265
266
|
return [];
|
|
266
267
|
},
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
* 1 Generic / unexpected error
|
|
14
14
|
* 2 Argument / usage error (ArgumentError)
|
|
15
15
|
* 66 No input / empty result (EmptyResultError)
|
|
16
|
-
* 69 Service unavailable (BrowserConnectError,
|
|
16
|
+
* 69 Service unavailable (BrowserConnectError, adapter load failures)
|
|
17
17
|
* 75 Temporary failure, retry later (TimeoutError) EX_TEMPFAIL
|
|
18
18
|
* 77 Permission denied / auth needed (AuthRequiredError)
|
|
19
19
|
* 78 Configuration error (ConfigError)
|
|
20
20
|
* 130 Interrupted by Ctrl-C (set by tui.ts SIGINT handler)
|
|
21
21
|
*/
|
|
22
|
+
import type { ObservationTraceReceipt } from './observation/events.js';
|
|
22
23
|
export declare const EXIT_CODES: {
|
|
23
24
|
readonly SUCCESS: 0;
|
|
24
25
|
readonly GENERIC_ERROR: 1;
|
|
@@ -40,14 +41,13 @@ export declare class CliError extends Error {
|
|
|
40
41
|
readonly exitCode: ExitCode;
|
|
41
42
|
constructor(code: string, message: string, hint?: string, exitCode?: ExitCode);
|
|
42
43
|
}
|
|
43
|
-
export
|
|
44
|
+
export declare function attachTraceReceipt(err: unknown, receipt: ObservationTraceReceipt): void;
|
|
45
|
+
export declare function getTraceReceipt(err: unknown): ObservationTraceReceipt | undefined;
|
|
46
|
+
export type BrowserConnectKind = 'daemon-not-running' | 'extension-not-connected' | 'profile-required' | 'profile-disconnected' | 'command-failed' | 'unknown';
|
|
44
47
|
export declare class BrowserConnectError extends CliError {
|
|
45
48
|
readonly kind: BrowserConnectKind;
|
|
46
49
|
constructor(message: string, hint?: string, kind?: BrowserConnectKind);
|
|
47
50
|
}
|
|
48
|
-
export declare class AdapterLoadError extends CliError {
|
|
49
|
-
constructor(message: string, hint?: string);
|
|
50
|
-
}
|
|
51
51
|
export declare class CommandExecutionError extends CliError {
|
|
52
52
|
constructor(message: string, hint?: string);
|
|
53
53
|
}
|
|
@@ -67,9 +67,8 @@ export declare class ArgumentError extends CliError {
|
|
|
67
67
|
export declare class EmptyResultError extends CliError {
|
|
68
68
|
constructor(command: string, hint?: string);
|
|
69
69
|
}
|
|
70
|
-
export declare
|
|
71
|
-
|
|
72
|
-
}
|
|
70
|
+
export declare function adapterLoadError(message: string, hint?: string): CliError;
|
|
71
|
+
export declare function selectorError(selector: string, hint?: string): CliError;
|
|
73
72
|
export declare class PluginError extends CliError {
|
|
74
73
|
constructor(message: string, hint?: string);
|
|
75
74
|
}
|
|
@@ -84,6 +83,13 @@ export interface ErrorEnvelope {
|
|
|
84
83
|
stack?: string;
|
|
85
84
|
cause?: string;
|
|
86
85
|
};
|
|
86
|
+
trace?: {
|
|
87
|
+
traceId: string;
|
|
88
|
+
dir: string;
|
|
89
|
+
summaryPath: string;
|
|
90
|
+
receiptPath: string;
|
|
91
|
+
status: ObservationTraceReceipt['status'];
|
|
92
|
+
};
|
|
87
93
|
}
|
|
88
94
|
/** Extract a human-readable message from an unknown caught value. */
|
|
89
95
|
export declare function getErrorMessage(error: unknown): string;
|
package/dist/src/errors.js
CHANGED
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified error types for opencli.
|
|
3
|
-
*
|
|
4
|
-
* All errors thrown by the framework should extend CliError so that
|
|
5
|
-
* the top-level handler in commanderAdapter.ts can render consistent,
|
|
6
|
-
* helpful output with emoji-coded severity and actionable hints.
|
|
7
|
-
*
|
|
8
|
-
* ## Exit codes
|
|
9
|
-
*
|
|
10
|
-
* opencli follows Unix conventions (sysexits.h) for process exit codes:
|
|
11
|
-
*
|
|
12
|
-
* 0 Success
|
|
13
|
-
* 1 Generic / unexpected error
|
|
14
|
-
* 2 Argument / usage error (ArgumentError)
|
|
15
|
-
* 66 No input / empty result (EmptyResultError)
|
|
16
|
-
* 69 Service unavailable (BrowserConnectError, AdapterLoadError)
|
|
17
|
-
* 75 Temporary failure, retry later (TimeoutError) EX_TEMPFAIL
|
|
18
|
-
* 77 Permission denied / auth needed (AuthRequiredError)
|
|
19
|
-
* 78 Configuration error (ConfigError)
|
|
20
|
-
* 130 Interrupted by Ctrl-C (set by tui.ts SIGINT handler)
|
|
21
|
-
*/
|
|
22
1
|
// ── Exit code table ──────────────────────────────────────────────────────────
|
|
23
2
|
export const EXIT_CODES = {
|
|
24
3
|
SUCCESS: 0,
|
|
@@ -47,6 +26,27 @@ export class CliError extends Error {
|
|
|
47
26
|
this.exitCode = exitCode;
|
|
48
27
|
}
|
|
49
28
|
}
|
|
29
|
+
const TRACE_RECEIPT_SYMBOL = Symbol.for('opencli.traceReceipt');
|
|
30
|
+
export function attachTraceReceipt(err, receipt) {
|
|
31
|
+
if (!err || (typeof err !== 'object' && typeof err !== 'function'))
|
|
32
|
+
return;
|
|
33
|
+
try {
|
|
34
|
+
Object.defineProperty(err, TRACE_RECEIPT_SYMBOL, {
|
|
35
|
+
value: receipt,
|
|
36
|
+
enumerable: false,
|
|
37
|
+
configurable: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Non-extensible thrown objects are rare; trace export should never mask the
|
|
42
|
+
// original adapter error just because metadata attachment failed.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function getTraceReceipt(err) {
|
|
46
|
+
if (!err || (typeof err !== 'object' && typeof err !== 'function'))
|
|
47
|
+
return undefined;
|
|
48
|
+
return err[TRACE_RECEIPT_SYMBOL];
|
|
49
|
+
}
|
|
50
50
|
export class BrowserConnectError extends CliError {
|
|
51
51
|
kind;
|
|
52
52
|
constructor(message, hint, kind = 'unknown') {
|
|
@@ -54,11 +54,6 @@ export class BrowserConnectError extends CliError {
|
|
|
54
54
|
this.kind = kind;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
-
export class AdapterLoadError extends CliError {
|
|
58
|
-
constructor(message, hint) {
|
|
59
|
-
super('ADAPTER_LOAD', message, hint, EXIT_CODES.SERVICE_UNAVAIL);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
57
|
export class CommandExecutionError extends CliError {
|
|
63
58
|
constructor(message, hint) {
|
|
64
59
|
super('COMMAND_EXEC', message, hint, EXIT_CODES.GENERIC_ERROR);
|
|
@@ -91,10 +86,11 @@ export class EmptyResultError extends CliError {
|
|
|
91
86
|
super('EMPTY_RESULT', `${command} returned no data`, hint ?? 'The page structure may have changed, or you may need to log in', EXIT_CODES.EMPTY_RESULT);
|
|
92
87
|
}
|
|
93
88
|
}
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
export function adapterLoadError(message, hint) {
|
|
90
|
+
return new CliError('ADAPTER_LOAD', message, hint, EXIT_CODES.SERVICE_UNAVAIL);
|
|
91
|
+
}
|
|
92
|
+
export function selectorError(selector, hint) {
|
|
93
|
+
return new CliError('SELECTOR', `Could not find element: ${selector}`, hint ?? 'The page UI may have changed. Please report this issue.', EXIT_CODES.GENERIC_ERROR);
|
|
98
94
|
}
|
|
99
95
|
export class PluginError extends CliError {
|
|
100
96
|
constructor(message, hint) {
|
|
@@ -121,6 +117,14 @@ function serializeCause(cause, depth = 0) {
|
|
|
121
117
|
/** Build an ErrorEnvelope from any caught value. */
|
|
122
118
|
export function toEnvelope(err) {
|
|
123
119
|
const cause = err instanceof Error && err.cause ? serializeCause(err.cause) : undefined;
|
|
120
|
+
const traceReceipt = getTraceReceipt(err);
|
|
121
|
+
const trace = traceReceipt ? {
|
|
122
|
+
traceId: traceReceipt.traceId,
|
|
123
|
+
dir: traceReceipt.traceDir,
|
|
124
|
+
summaryPath: traceReceipt.summaryPath,
|
|
125
|
+
receiptPath: traceReceipt.receiptPath,
|
|
126
|
+
status: traceReceipt.status,
|
|
127
|
+
} : undefined;
|
|
124
128
|
if (err instanceof CliError) {
|
|
125
129
|
return {
|
|
126
130
|
ok: false,
|
|
@@ -131,6 +135,7 @@ export function toEnvelope(err) {
|
|
|
131
135
|
exitCode: err.exitCode,
|
|
132
136
|
...(cause ? { cause } : {}),
|
|
133
137
|
},
|
|
138
|
+
...(trace ? { trace } : {}),
|
|
134
139
|
};
|
|
135
140
|
}
|
|
136
141
|
const msg = getErrorMessage(err);
|
|
@@ -142,5 +147,6 @@ export function toEnvelope(err) {
|
|
|
142
147
|
exitCode: EXIT_CODES.GENERIC_ERROR,
|
|
143
148
|
...(cause ? { cause } : {}),
|
|
144
149
|
},
|
|
150
|
+
...(trace ? { trace } : {}),
|
|
145
151
|
};
|
|
146
152
|
}
|
package/dist/src/errors.test.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { CliError, BrowserConnectError,
|
|
2
|
+
import { CliError, BrowserConnectError, adapterLoadError, CommandExecutionError, ConfigError, AuthRequiredError, TimeoutError, ArgumentError, EmptyResultError, selectorError, toEnvelope, } from './errors.js';
|
|
3
3
|
describe('Error type hierarchy', () => {
|
|
4
4
|
it('all error types extend CliError', () => {
|
|
5
5
|
const errors = [
|
|
6
6
|
new BrowserConnectError('test'),
|
|
7
|
-
|
|
7
|
+
adapterLoadError('test'),
|
|
8
8
|
new CommandExecutionError('test'),
|
|
9
9
|
new ConfigError('test'),
|
|
10
10
|
new AuthRequiredError('example.com'),
|
|
11
11
|
new TimeoutError('test', 30),
|
|
12
12
|
new ArgumentError('test'),
|
|
13
13
|
new EmptyResultError('test/cmd'),
|
|
14
|
-
|
|
14
|
+
selectorError('.btn'),
|
|
15
15
|
];
|
|
16
16
|
for (const err of errors) {
|
|
17
17
|
expect(err).toBeInstanceOf(CliError);
|
|
@@ -46,8 +46,8 @@ describe('Error type hierarchy', () => {
|
|
|
46
46
|
expect(err.message).toBe('hackernews/top returned no data');
|
|
47
47
|
expect(err.hint).toBeTruthy();
|
|
48
48
|
});
|
|
49
|
-
it('
|
|
50
|
-
const err =
|
|
49
|
+
it('selectorError has default hint about page changes', () => {
|
|
50
|
+
const err = selectorError('.submit-btn');
|
|
51
51
|
expect(err.code).toBe('SELECTOR');
|
|
52
52
|
expect(err.message).toContain('.submit-btn');
|
|
53
53
|
expect(err.hint).toContain('report');
|
package/dist/src/execution.d.ts
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* 6. Lifecycle hooks (onBeforeExecute / onAfterExecute)
|
|
11
11
|
*/
|
|
12
12
|
import { type CliCommand, type Arg, type CommandArgs } from './registry.js';
|
|
13
|
+
import { type ObservationExportResult } from './observation/index.js';
|
|
13
14
|
export declare function coerceAndValidateArgs(cmdArgs: Arg[], kwargs: CommandArgs): CommandArgs;
|
|
14
15
|
export declare function executeCommand(cmd: CliCommand, rawKwargs: CommandArgs, debug?: boolean, opts?: {
|
|
15
16
|
prepared?: boolean;
|
|
17
|
+
profile?: string;
|
|
18
|
+
trace?: string;
|
|
19
|
+
onTraceExport?: (trace: ObservationExportResult) => void;
|
|
16
20
|
}): Promise<unknown>;
|
|
17
21
|
export declare function prepareCommandArgs(cmd: CliCommand, rawKwargs: CommandArgs): CommandArgs;
|