@jackwener/opencli 0.7.11 → 0.9.0
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/CDP.md +103 -0
- package/CDP.zh-CN.md +103 -0
- package/README.md +5 -0
- package/README.zh-CN.md +5 -0
- package/dist/browser/discover.d.ts +15 -0
- package/dist/browser/discover.js +68 -2
- package/dist/browser/errors.d.ts +2 -1
- package/dist/browser/errors.js +13 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/mcp.js +8 -3
- package/dist/browser/page.js +11 -2
- package/dist/cli-manifest.json +246 -0
- package/dist/clis/antigravity/dump.d.ts +1 -0
- package/dist/clis/antigravity/dump.js +28 -0
- package/dist/clis/antigravity/extract-code.d.ts +1 -0
- package/dist/clis/antigravity/extract-code.js +32 -0
- package/dist/clis/antigravity/model.d.ts +1 -0
- package/dist/clis/antigravity/model.js +44 -0
- package/dist/clis/antigravity/new.d.ts +1 -0
- package/dist/clis/antigravity/new.js +25 -0
- package/dist/clis/antigravity/read.d.ts +1 -0
- package/dist/clis/antigravity/read.js +34 -0
- package/dist/clis/antigravity/send.d.ts +1 -0
- package/dist/clis/antigravity/send.js +35 -0
- package/dist/clis/antigravity/status.d.ts +1 -0
- package/dist/clis/antigravity/status.js +18 -0
- package/dist/clis/antigravity/watch.d.ts +1 -0
- package/dist/clis/antigravity/watch.js +41 -0
- package/dist/clis/barchart/flow.js +56 -58
- package/dist/clis/xiaoyuzhou/episode.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/episode.js +28 -0
- package/dist/clis/xiaoyuzhou/podcast-episodes.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/podcast-episodes.js +36 -0
- package/dist/clis/xiaoyuzhou/podcast.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/podcast.js +27 -0
- package/dist/clis/xiaoyuzhou/utils.d.ts +16 -0
- package/dist/clis/xiaoyuzhou/utils.js +55 -0
- package/dist/clis/xiaoyuzhou/utils.test.d.ts +1 -0
- package/dist/clis/xiaoyuzhou/utils.test.js +99 -0
- package/dist/doctor.js +8 -0
- package/dist/engine.d.ts +1 -1
- package/dist/engine.js +59 -1
- package/dist/main.js +2 -15
- package/dist/pipeline/executor.js +2 -24
- package/dist/pipeline/registry.d.ts +19 -0
- package/dist/pipeline/registry.js +41 -0
- package/package.json +1 -1
- package/src/browser/discover.ts +79 -5
- package/src/browser/errors.ts +17 -1
- package/src/browser/index.ts +1 -0
- package/src/browser/mcp.ts +8 -3
- package/src/browser/page.ts +21 -2
- package/src/clis/antigravity/README.md +49 -0
- package/src/clis/antigravity/README.zh-CN.md +52 -0
- package/src/clis/antigravity/SKILL.md +42 -0
- package/src/clis/antigravity/dump.ts +30 -0
- package/src/clis/antigravity/extract-code.ts +34 -0
- package/src/clis/antigravity/model.ts +47 -0
- package/src/clis/antigravity/new.ts +28 -0
- package/src/clis/antigravity/read.ts +36 -0
- package/src/clis/antigravity/send.ts +40 -0
- package/src/clis/antigravity/status.ts +19 -0
- package/src/clis/antigravity/watch.ts +45 -0
- package/src/clis/barchart/flow.ts +57 -58
- package/src/clis/xiaoyuzhou/episode.ts +28 -0
- package/src/clis/xiaoyuzhou/podcast-episodes.ts +36 -0
- package/src/clis/xiaoyuzhou/podcast.ts +27 -0
- package/src/clis/xiaoyuzhou/utils.test.ts +122 -0
- package/src/clis/xiaoyuzhou/utils.ts +65 -0
- package/src/doctor.ts +9 -0
- package/src/engine.ts +58 -1
- package/src/main.ts +6 -11
- package/src/pipeline/executor.ts +2 -28
- package/src/pipeline/registry.ts +60 -0
- package/tests/e2e/public-commands.test.ts +62 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic registry for pipeline steps.
|
|
3
|
+
* Allows core and third-party plugins to register custom YAML operations.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IPage } from '../types.js';
|
|
7
|
+
|
|
8
|
+
// Import core steps
|
|
9
|
+
import { stepNavigate, stepClick, stepType, stepWait, stepPress, stepSnapshot, stepEvaluate } from './steps/browser.js';
|
|
10
|
+
import { stepFetch } from './steps/fetch.js';
|
|
11
|
+
import { stepSelect, stepMap, stepFilter, stepSort, stepLimit } from './steps/transform.js';
|
|
12
|
+
import { stepIntercept } from './steps/intercept.js';
|
|
13
|
+
import { stepTap } from './steps/tap.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Step handler: all pipeline steps conform to this generic interface.
|
|
17
|
+
* TData is the type of the `data` state flowing into the step.
|
|
18
|
+
* TResult is the expected return type.
|
|
19
|
+
*/
|
|
20
|
+
export type StepHandler<TData = any, TResult = any> = (
|
|
21
|
+
page: IPage | null,
|
|
22
|
+
params: any,
|
|
23
|
+
data: TData,
|
|
24
|
+
args: Record<string, any>
|
|
25
|
+
) => Promise<TResult>;
|
|
26
|
+
|
|
27
|
+
const _stepRegistry = new Map<string, StepHandler>();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get a registered step handler by name.
|
|
31
|
+
*/
|
|
32
|
+
export function getStep(name: string): StepHandler | undefined {
|
|
33
|
+
return _stepRegistry.get(name);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a new custom step handler for the YAML pipeline.
|
|
38
|
+
*/
|
|
39
|
+
export function registerStep(name: string, handler: StepHandler): void {
|
|
40
|
+
_stepRegistry.set(name, handler);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// -------------------------------------------------------------
|
|
44
|
+
// Auto-Register Core Steps
|
|
45
|
+
// -------------------------------------------------------------
|
|
46
|
+
registerStep('navigate', stepNavigate);
|
|
47
|
+
registerStep('fetch', stepFetch);
|
|
48
|
+
registerStep('select', stepSelect);
|
|
49
|
+
registerStep('evaluate', stepEvaluate);
|
|
50
|
+
registerStep('snapshot', stepSnapshot);
|
|
51
|
+
registerStep('click', stepClick);
|
|
52
|
+
registerStep('type', stepType);
|
|
53
|
+
registerStep('wait', stepWait);
|
|
54
|
+
registerStep('press', stepPress);
|
|
55
|
+
registerStep('map', stepMap);
|
|
56
|
+
registerStep('filter', stepFilter);
|
|
57
|
+
registerStep('sort', stepSort);
|
|
58
|
+
registerStep('limit', stepLimit);
|
|
59
|
+
registerStep('intercept', stepIntercept);
|
|
60
|
+
registerStep('tap', stepTap);
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
import { describe, it, expect } from 'vitest';
|
|
7
7
|
import { runCli, parseJsonOutput } from './helpers.js';
|
|
8
8
|
|
|
9
|
+
function isExpectedXiaoyuzhouRestriction(code: number, stderr: string): boolean {
|
|
10
|
+
if (code === 0) return false;
|
|
11
|
+
return /Error \[FETCH_ERROR\]: HTTP (403|429|451|503)\b/.test(stderr);
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
describe('public commands E2E', () => {
|
|
10
15
|
// ── hackernews ──
|
|
11
16
|
it('hackernews top returns structured data', async () => {
|
|
@@ -53,4 +58,61 @@ describe('public commands E2E', () => {
|
|
|
53
58
|
expect(data).toBeDefined();
|
|
54
59
|
}
|
|
55
60
|
}, 30_000);
|
|
61
|
+
|
|
62
|
+
// ── xiaoyuzhou (Chinese site — may return empty on overseas CI runners) ──
|
|
63
|
+
it('xiaoyuzhou podcast returns podcast profile', async () => {
|
|
64
|
+
const { stdout, stderr, code } = await runCli(['xiaoyuzhou', 'podcast', '6013f9f58e2f7ee375cf4216', '-f', 'json']);
|
|
65
|
+
if (isExpectedXiaoyuzhouRestriction(code, stderr)) {
|
|
66
|
+
console.warn(`xiaoyuzhou podcast skipped: ${stderr.trim()}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
expect(code).toBe(0);
|
|
70
|
+
const data = parseJsonOutput(stdout);
|
|
71
|
+
expect(Array.isArray(data)).toBe(true);
|
|
72
|
+
expect(data.length).toBe(1);
|
|
73
|
+
expect(data[0]).toHaveProperty('title');
|
|
74
|
+
expect(data[0]).toHaveProperty('subscribers');
|
|
75
|
+
expect(data[0]).toHaveProperty('episodes');
|
|
76
|
+
}, 30_000);
|
|
77
|
+
|
|
78
|
+
it('xiaoyuzhou podcast-episodes returns episode list', async () => {
|
|
79
|
+
const { stdout, stderr, code } = await runCli(['xiaoyuzhou', 'podcast-episodes', '6013f9f58e2f7ee375cf4216', '-f', 'json']);
|
|
80
|
+
if (isExpectedXiaoyuzhouRestriction(code, stderr)) {
|
|
81
|
+
console.warn(`xiaoyuzhou podcast-episodes skipped: ${stderr.trim()}`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
expect(code).toBe(0);
|
|
85
|
+
const data = parseJsonOutput(stdout);
|
|
86
|
+
expect(Array.isArray(data)).toBe(true);
|
|
87
|
+
expect(data.length).toBeGreaterThanOrEqual(1);
|
|
88
|
+
expect(data[0]).toHaveProperty('eid');
|
|
89
|
+
expect(data[0]).toHaveProperty('title');
|
|
90
|
+
expect(data[0]).toHaveProperty('duration');
|
|
91
|
+
}, 30_000);
|
|
92
|
+
|
|
93
|
+
it('xiaoyuzhou episode returns episode detail', async () => {
|
|
94
|
+
const { stdout, stderr, code } = await runCli(['xiaoyuzhou', 'episode', '69b3b675772ac2295bfc01d0', '-f', 'json']);
|
|
95
|
+
if (isExpectedXiaoyuzhouRestriction(code, stderr)) {
|
|
96
|
+
console.warn(`xiaoyuzhou episode skipped: ${stderr.trim()}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
expect(code).toBe(0);
|
|
100
|
+
const data = parseJsonOutput(stdout);
|
|
101
|
+
expect(Array.isArray(data)).toBe(true);
|
|
102
|
+
expect(data.length).toBe(1);
|
|
103
|
+
expect(data[0]).toHaveProperty('title');
|
|
104
|
+
expect(data[0]).toHaveProperty('podcast');
|
|
105
|
+
expect(data[0]).toHaveProperty('plays');
|
|
106
|
+
expect(data[0]).toHaveProperty('comments');
|
|
107
|
+
}, 30_000);
|
|
108
|
+
|
|
109
|
+
it('xiaoyuzhou podcast-episodes rejects invalid limit', async () => {
|
|
110
|
+
const { stderr, code } = await runCli(['xiaoyuzhou', 'podcast-episodes', '6013f9f58e2f7ee375cf4216', '--limit', 'abc', '-f', 'json']);
|
|
111
|
+
if (isExpectedXiaoyuzhouRestriction(code, stderr)) {
|
|
112
|
+
console.warn(`xiaoyuzhou invalid-limit skipped: ${stderr.trim()}`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
expect(code).not.toBe(0);
|
|
116
|
+
expect(stderr).toMatch(/limit must be a positive integer|Argument "limit" must be a valid number/);
|
|
117
|
+
}, 30_000);
|
|
56
118
|
});
|