@jackwener/opencli 1.0.0 → 1.0.3
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/.github/workflows/build-extension.yml +62 -0
- package/.github/workflows/ci.yml +6 -6
- package/.github/workflows/e2e-headed.yml +2 -2
- package/.github/workflows/pkg-pr-new.yml +2 -2
- package/.github/workflows/release.yml +2 -5
- package/.github/workflows/security.yml +2 -2
- package/CDP.md +1 -1
- package/CDP.zh-CN.md +1 -1
- package/README.md +35 -8
- package/README.zh-CN.md +35 -8
- package/SKILL.md +3 -5
- package/dist/browser/cdp.d.ts +27 -0
- package/dist/browser/cdp.js +295 -0
- package/dist/browser/daemon-client.d.ts +1 -1
- package/dist/browser/index.d.ts +4 -2
- package/dist/browser/index.js +5 -5
- package/dist/browser/mcp.d.ts +5 -8
- package/dist/browser/mcp.js +9 -10
- package/dist/browser/page.d.ts +8 -1
- package/dist/browser/page.js +25 -40
- package/dist/browser/utils.d.ts +10 -0
- package/dist/browser/utils.js +27 -0
- package/dist/browser.test.js +48 -7
- package/dist/chaoxing.d.ts +58 -0
- package/dist/chaoxing.js +225 -0
- package/dist/chaoxing.test.d.ts +1 -0
- package/dist/chaoxing.test.js +38 -0
- package/dist/cli-manifest.json +597 -14
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +197 -0
- package/dist/clis/apple-podcasts/episodes.d.ts +1 -0
- package/dist/clis/apple-podcasts/episodes.js +28 -0
- package/dist/clis/apple-podcasts/search.d.ts +1 -0
- package/dist/clis/apple-podcasts/search.js +29 -0
- package/dist/clis/apple-podcasts/top.d.ts +1 -0
- package/dist/clis/apple-podcasts/top.js +34 -0
- package/dist/clis/apple-podcasts/utils.d.ts +11 -0
- package/dist/clis/apple-podcasts/utils.js +30 -0
- package/dist/clis/apple-podcasts/utils.test.d.ts +1 -0
- package/dist/clis/apple-podcasts/utils.test.js +57 -0
- package/dist/clis/boss/chatlist.d.ts +1 -0
- package/dist/clis/boss/chatlist.js +50 -0
- package/dist/clis/boss/chatmsg.d.ts +1 -0
- package/dist/clis/boss/chatmsg.js +73 -0
- package/dist/clis/boss/send.d.ts +1 -0
- package/dist/clis/boss/send.js +176 -0
- package/dist/clis/chaoxing/assignments.d.ts +1 -0
- package/dist/clis/chaoxing/assignments.js +74 -0
- package/dist/clis/chaoxing/exams.d.ts +1 -0
- package/dist/clis/chaoxing/exams.js +74 -0
- package/dist/clis/chatgpt/ask.js +15 -14
- package/dist/clis/chatgpt/ax.d.ts +1 -0
- package/dist/clis/chatgpt/ax.js +78 -0
- package/dist/clis/chatgpt/read.js +5 -6
- package/dist/clis/chatwise/history.js +18 -1
- package/dist/clis/discord-app/channels.js +33 -21
- package/dist/clis/twitter/accept.d.ts +1 -0
- package/dist/clis/twitter/accept.js +202 -0
- package/dist/clis/twitter/followers.js +30 -22
- package/dist/clis/twitter/following.js +19 -14
- package/dist/clis/twitter/notifications.js +29 -22
- package/dist/clis/twitter/post.js +9 -2
- package/dist/clis/twitter/reply-dm.d.ts +1 -0
- package/dist/clis/twitter/reply-dm.js +181 -0
- package/dist/clis/twitter/search.js +30 -11
- package/dist/clis/weread/book.d.ts +1 -0
- package/dist/clis/weread/book.js +26 -0
- package/dist/clis/weread/highlights.d.ts +1 -0
- package/dist/clis/weread/highlights.js +23 -0
- package/dist/clis/weread/notebooks.d.ts +1 -0
- package/dist/clis/weread/notebooks.js +21 -0
- package/dist/clis/weread/notes.d.ts +1 -0
- package/dist/clis/weread/notes.js +29 -0
- package/dist/clis/weread/ranking.d.ts +1 -0
- package/dist/clis/weread/ranking.js +28 -0
- package/dist/clis/weread/search.d.ts +1 -0
- package/dist/clis/weread/search.js +25 -0
- package/dist/clis/weread/shelf.d.ts +1 -0
- package/dist/clis/weread/shelf.js +24 -0
- package/dist/clis/weread/utils.d.ts +20 -0
- package/dist/clis/weread/utils.js +72 -0
- package/dist/clis/weread/utils.test.d.ts +1 -0
- package/dist/clis/weread/utils.test.js +85 -0
- package/dist/clis/xiaohongshu/download.d.ts +1 -1
- package/dist/clis/xiaohongshu/download.js +1 -1
- package/dist/daemon.js +2 -2
- package/dist/doctor.d.ts +0 -21
- package/dist/doctor.js +2 -24
- package/dist/engine.js +24 -13
- package/dist/explore.js +46 -101
- package/dist/main.js +4 -203
- package/dist/output.d.ts +1 -1
- package/dist/registry.d.ts +3 -3
- package/dist/runtime.d.ts +1 -4
- package/dist/runtime.js +1 -4
- package/dist/scripts/framework.d.ts +4 -0
- package/dist/scripts/framework.js +21 -0
- package/dist/scripts/interact.d.ts +4 -0
- package/dist/scripts/interact.js +20 -0
- package/dist/scripts/store.d.ts +9 -0
- package/dist/scripts/store.js +44 -0
- package/dist/setup.js +2 -2
- package/dist/synthesize.js +1 -1
- package/extension/dist/background.js +392 -0
- package/extension/manifest.json +3 -3
- package/extension/package.json +1 -1
- package/extension/src/background.ts +101 -24
- package/extension/src/protocol.ts +1 -1
- package/package.json +1 -1
- package/src/browser/cdp.ts +295 -0
- package/src/browser/daemon-client.ts +1 -1
- package/src/browser/index.ts +5 -6
- package/src/browser/mcp.ts +14 -15
- package/src/browser/page.ts +25 -41
- package/src/browser/utils.ts +27 -0
- package/src/browser.test.ts +52 -6
- package/src/chaoxing.test.ts +45 -0
- package/src/chaoxing.ts +268 -0
- package/src/cli.ts +185 -0
- package/src/clis/antigravity/SKILL.md +5 -0
- package/src/clis/apple-podcasts/episodes.ts +28 -0
- package/src/clis/apple-podcasts/search.ts +29 -0
- package/src/clis/apple-podcasts/top.ts +34 -0
- package/src/clis/apple-podcasts/utils.test.ts +72 -0
- package/src/clis/apple-podcasts/utils.ts +37 -0
- package/src/clis/boss/chatlist.ts +50 -0
- package/src/clis/boss/chatmsg.ts +70 -0
- package/src/clis/boss/send.ts +193 -0
- package/src/clis/chaoxing/README.md +36 -0
- package/src/clis/chaoxing/README.zh-CN.md +35 -0
- package/src/clis/chaoxing/assignments.ts +88 -0
- package/src/clis/chaoxing/exams.ts +88 -0
- package/src/clis/chatgpt/ask.ts +14 -15
- package/src/clis/chatgpt/ax.ts +81 -0
- package/src/clis/chatgpt/read.ts +5 -7
- package/src/clis/chatwise/history.ts +15 -1
- package/src/clis/discord-app/channels.ts +33 -21
- package/src/clis/twitter/accept.ts +213 -0
- package/src/clis/twitter/followers.ts +36 -29
- package/src/clis/twitter/following.ts +25 -20
- package/src/clis/twitter/notifications.ts +34 -27
- package/src/clis/twitter/post.ts +9 -2
- package/src/clis/twitter/reply-dm.ts +193 -0
- package/src/clis/twitter/search.ts +34 -12
- package/src/clis/weread/book.ts +28 -0
- package/src/clis/weread/highlights.ts +25 -0
- package/src/clis/weread/notebooks.ts +23 -0
- package/src/clis/weread/notes.ts +31 -0
- package/src/clis/weread/ranking.ts +29 -0
- package/src/clis/weread/search.ts +26 -0
- package/src/clis/weread/shelf.ts +26 -0
- package/src/clis/weread/utils.test.ts +104 -0
- package/src/clis/weread/utils.ts +74 -0
- package/src/clis/xiaohongshu/download.ts +1 -1
- package/src/daemon.ts +2 -2
- package/src/doctor.ts +2 -19
- package/src/engine.ts +20 -13
- package/src/explore.ts +51 -100
- package/src/main.ts +4 -186
- package/src/output.ts +12 -12
- package/src/registry.ts +3 -3
- package/src/runtime.ts +2 -6
- package/src/scripts/framework.ts +20 -0
- package/src/scripts/interact.ts +22 -0
- package/src/scripts/store.ts +40 -0
- package/src/setup.ts +2 -2
- package/src/synthesize.ts +1 -1
- package/tests/e2e/public-commands.test.ts +68 -1
- package/dist/clis/grok/debug.d.ts +0 -1
- package/dist/clis/grok/debug.js +0 -45
- package/src/clis/grok/debug.ts +0 -49
package/dist/daemon.js
CHANGED
|
@@ -95,8 +95,8 @@ async function handleRequest(req, res) {
|
|
|
95
95
|
const result = await new Promise((resolve, reject) => {
|
|
96
96
|
const timer = setTimeout(() => {
|
|
97
97
|
pending.delete(body.id);
|
|
98
|
-
reject(new Error('Command timeout (
|
|
99
|
-
},
|
|
98
|
+
reject(new Error('Command timeout (120s)'));
|
|
99
|
+
}, 120000);
|
|
100
100
|
pending.set(body.id, { resolve, reject, timer });
|
|
101
101
|
extensionWs.send(JSON.stringify(body));
|
|
102
102
|
});
|
package/dist/doctor.d.ts
CHANGED
|
@@ -30,24 +30,3 @@ export declare function checkConnectivity(opts?: {
|
|
|
30
30
|
}): Promise<ConnectivityResult>;
|
|
31
31
|
export declare function runBrowserDoctor(opts?: DoctorOptions): Promise<DoctorReport>;
|
|
32
32
|
export declare function renderBrowserDoctorReport(report: DoctorReport): string;
|
|
33
|
-
export declare const PLAYWRIGHT_TOKEN_ENV = "PLAYWRIGHT_MCP_EXTENSION_TOKEN";
|
|
34
|
-
export declare function discoverExtensionToken(): string | null;
|
|
35
|
-
export declare function checkExtensionInstalled(): {
|
|
36
|
-
installed: boolean;
|
|
37
|
-
browsers: string[];
|
|
38
|
-
};
|
|
39
|
-
export declare function applyBrowserDoctorFix(): Promise<string[]>;
|
|
40
|
-
export declare function getDefaultShellRcPath(): string;
|
|
41
|
-
export declare function getDefaultMcpConfigPaths(): string[];
|
|
42
|
-
export declare function readTokenFromShellContent(_content: string): string | null;
|
|
43
|
-
export declare function upsertShellToken(content: string): string;
|
|
44
|
-
export declare function upsertJsonConfigToken(content: string): string;
|
|
45
|
-
export declare function readTomlConfigToken(_content: string): string | null;
|
|
46
|
-
export declare function upsertTomlConfigToken(content: string): string;
|
|
47
|
-
export declare function shortenPath(p: string): string;
|
|
48
|
-
export declare function toolName(_p: string): string;
|
|
49
|
-
export declare function fileExists(filePath: string): boolean;
|
|
50
|
-
export declare function writeFileWithMkdir(_p: string, _c: string): void;
|
|
51
|
-
export declare function checkTokenConnectivity(opts?: {
|
|
52
|
-
timeout?: number;
|
|
53
|
-
}): Promise<ConnectivityResult>;
|
package/dist/doctor.js
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { checkDaemonStatus } from './browser/discover.js';
|
|
9
|
-
import {
|
|
9
|
+
import { BrowserBridge } from './browser/index.js';
|
|
10
10
|
/**
|
|
11
11
|
* Test connectivity by attempting a real browser command.
|
|
12
12
|
*/
|
|
13
13
|
export async function checkConnectivity(opts) {
|
|
14
14
|
const start = Date.now();
|
|
15
15
|
try {
|
|
16
|
-
const mcp = new
|
|
16
|
+
const mcp = new BrowserBridge();
|
|
17
17
|
const page = await mcp.connect({ timeout: opts?.timeout ?? 8 });
|
|
18
18
|
// Try a simple eval to verify end-to-end connectivity
|
|
19
19
|
await page.evaluate('1 + 1');
|
|
@@ -82,25 +82,3 @@ export function renderBrowserDoctorReport(report) {
|
|
|
82
82
|
}
|
|
83
83
|
return lines.join('\n');
|
|
84
84
|
}
|
|
85
|
-
// Backward compatibility exports (no-ops for things that no longer exist)
|
|
86
|
-
export const PLAYWRIGHT_TOKEN_ENV = 'PLAYWRIGHT_MCP_EXTENSION_TOKEN';
|
|
87
|
-
export function discoverExtensionToken() { return null; }
|
|
88
|
-
export function checkExtensionInstalled() { return { installed: false, browsers: [] }; }
|
|
89
|
-
export function applyBrowserDoctorFix() { return Promise.resolve([]); }
|
|
90
|
-
export function getDefaultShellRcPath() { return ''; }
|
|
91
|
-
export function getDefaultMcpConfigPaths() { return []; }
|
|
92
|
-
export function readTokenFromShellContent(_content) { return null; }
|
|
93
|
-
export function upsertShellToken(content) { return content; }
|
|
94
|
-
export function upsertJsonConfigToken(content) { return content; }
|
|
95
|
-
export function readTomlConfigToken(_content) { return null; }
|
|
96
|
-
export function upsertTomlConfigToken(content) { return content; }
|
|
97
|
-
export function shortenPath(p) { return p; }
|
|
98
|
-
export function toolName(_p) { return ''; }
|
|
99
|
-
export function fileExists(filePath) { try {
|
|
100
|
-
return require('node:fs').existsSync(filePath);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return false;
|
|
104
|
-
} }
|
|
105
|
-
export function writeFileWithMkdir(_p, _c) { }
|
|
106
|
-
export async function checkTokenConnectivity(opts) { return checkConnectivity(opts); }
|
package/dist/engine.js
CHANGED
|
@@ -24,12 +24,15 @@ export async function discoverClis(...dirs) {
|
|
|
24
24
|
// Fast path: try manifest first (production / post-build)
|
|
25
25
|
for (const dir of dirs) {
|
|
26
26
|
const manifestPath = path.resolve(dir, '..', 'cli-manifest.json');
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
try {
|
|
28
|
+
await fs.promises.access(manifestPath);
|
|
29
|
+
await loadFromManifest(manifestPath, dir);
|
|
29
30
|
continue; // Skip filesystem scan for this directory
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
catch {
|
|
33
|
+
// Fallback: runtime filesystem scan (development)
|
|
34
|
+
await discoverClisFromFs(dir);
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
/**
|
|
@@ -37,9 +40,10 @@ export async function discoverClis(...dirs) {
|
|
|
37
40
|
* YAML pipelines are inlined — zero YAML parsing at runtime.
|
|
38
41
|
* TS modules are deferred — loaded lazily on first execution.
|
|
39
42
|
*/
|
|
40
|
-
function loadFromManifest(manifestPath, clisDir) {
|
|
43
|
+
async function loadFromManifest(manifestPath, clisDir) {
|
|
41
44
|
try {
|
|
42
|
-
const
|
|
45
|
+
const raw = await fs.promises.readFile(manifestPath, 'utf-8');
|
|
46
|
+
const manifest = JSON.parse(raw);
|
|
43
47
|
for (const entry of manifest) {
|
|
44
48
|
if (entry.type === 'yaml') {
|
|
45
49
|
// YAML pipelines fully inlined in manifest — register directly
|
|
@@ -90,17 +94,24 @@ function loadFromManifest(manifestPath, clisDir) {
|
|
|
90
94
|
* Fallback: traditional filesystem scan (used during development with tsx).
|
|
91
95
|
*/
|
|
92
96
|
async function discoverClisFromFs(dir) {
|
|
93
|
-
|
|
97
|
+
try {
|
|
98
|
+
await fs.promises.access(dir);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
94
101
|
return;
|
|
102
|
+
}
|
|
95
103
|
const promises = [];
|
|
96
|
-
|
|
104
|
+
const sites = await fs.promises.readdir(dir);
|
|
105
|
+
for (const site of sites) {
|
|
97
106
|
const siteDir = path.join(dir, site);
|
|
98
|
-
|
|
107
|
+
const stat = await fs.promises.stat(siteDir);
|
|
108
|
+
if (!stat.isDirectory())
|
|
99
109
|
continue;
|
|
100
|
-
|
|
110
|
+
const files = await fs.promises.readdir(siteDir);
|
|
111
|
+
for (const file of files) {
|
|
101
112
|
const filePath = path.join(siteDir, file);
|
|
102
113
|
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
103
|
-
registerYamlCli(filePath, site);
|
|
114
|
+
promises.push(registerYamlCli(filePath, site));
|
|
104
115
|
}
|
|
105
116
|
else if ((file.endsWith('.js') && !file.endsWith('.d.js')) ||
|
|
106
117
|
(file.endsWith('.ts') && !file.endsWith('.d.ts') && !file.endsWith('.test.ts'))) {
|
|
@@ -112,9 +123,9 @@ async function discoverClisFromFs(dir) {
|
|
|
112
123
|
}
|
|
113
124
|
await Promise.all(promises);
|
|
114
125
|
}
|
|
115
|
-
function registerYamlCli(filePath, defaultSite) {
|
|
126
|
+
async function registerYamlCli(filePath, defaultSite) {
|
|
116
127
|
try {
|
|
117
|
-
const raw = fs.
|
|
128
|
+
const raw = await fs.promises.readFile(filePath, 'utf-8');
|
|
118
129
|
const def = yaml.load(raw);
|
|
119
130
|
if (!def || typeof def !== 'object')
|
|
120
131
|
return;
|
package/dist/explore.js
CHANGED
|
@@ -9,6 +9,9 @@ import * as fs from 'node:fs';
|
|
|
9
9
|
import * as path from 'node:path';
|
|
10
10
|
import { DEFAULT_BROWSER_EXPLORE_TIMEOUT, browserSession, runWithTimeout } from './runtime.js';
|
|
11
11
|
import { VOLATILE_PARAMS, SEARCH_PARAMS, PAGINATION_PARAMS, LIMIT_PARAMS, FIELD_ROLES } from './constants.js';
|
|
12
|
+
import { detectFramework } from './scripts/framework.js';
|
|
13
|
+
import { discoverStores } from './scripts/store.js';
|
|
14
|
+
import { interactFuzz } from './scripts/interact.js';
|
|
12
15
|
// ── Site name detection ────────────────────────────────────────────────────
|
|
13
16
|
const KNOWN_SITE_ALIASES = {
|
|
14
17
|
'x.com': 'twitter', 'twitter.com': 'twitter',
|
|
@@ -134,11 +137,13 @@ function flattenFields(obj, prefix, maxDepth) {
|
|
|
134
137
|
if (maxDepth <= 0 || !obj || typeof obj !== 'object')
|
|
135
138
|
return [];
|
|
136
139
|
const names = [];
|
|
137
|
-
|
|
140
|
+
const record = obj;
|
|
141
|
+
for (const key of Object.keys(record)) {
|
|
138
142
|
const full = prefix ? `${prefix}.${key}` : key;
|
|
139
143
|
names.push(full);
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
const val = record[key];
|
|
145
|
+
if (val && typeof val === 'object' && !Array.isArray(val))
|
|
146
|
+
names.push(...flattenFields(val, full, maxDepth - 1));
|
|
142
147
|
}
|
|
143
148
|
return names;
|
|
144
149
|
}
|
|
@@ -200,83 +205,11 @@ function inferStrategy(authIndicators) {
|
|
|
200
205
|
return 'cookie';
|
|
201
206
|
}
|
|
202
207
|
// ── Framework detection ────────────────────────────────────────────────────
|
|
203
|
-
const FRAMEWORK_DETECT_JS =
|
|
204
|
-
() => {
|
|
205
|
-
const r = {};
|
|
206
|
-
try {
|
|
207
|
-
const app = document.querySelector('#app');
|
|
208
|
-
r.vue3 = !!(app && app.__vue_app__);
|
|
209
|
-
r.vue2 = !!(app && app.__vue__);
|
|
210
|
-
r.react = !!window.__REACT_DEVTOOLS_GLOBAL_HOOK__ || !!document.querySelector('[data-reactroot]');
|
|
211
|
-
r.nextjs = !!window.__NEXT_DATA__;
|
|
212
|
-
r.nuxt = !!window.__NUXT__;
|
|
213
|
-
if (r.vue3 && app.__vue_app__) { const gp = app.__vue_app__.config?.globalProperties; r.pinia = !!(gp && gp.$pinia); r.vuex = !!(gp && gp.$store); }
|
|
214
|
-
} catch {}
|
|
215
|
-
return r;
|
|
216
|
-
}
|
|
217
|
-
`;
|
|
208
|
+
const FRAMEWORK_DETECT_JS = detectFramework.toString();
|
|
218
209
|
// ── Store discovery ────────────────────────────────────────────────────────
|
|
219
|
-
const STORE_DISCOVER_JS =
|
|
220
|
-
() => {
|
|
221
|
-
const stores = [];
|
|
222
|
-
try {
|
|
223
|
-
const app = document.querySelector('#app');
|
|
224
|
-
if (!app?.__vue_app__) return stores;
|
|
225
|
-
const gp = app.__vue_app__.config?.globalProperties;
|
|
226
|
-
|
|
227
|
-
// Pinia stores
|
|
228
|
-
const pinia = gp?.$pinia;
|
|
229
|
-
if (pinia?._s) {
|
|
230
|
-
pinia._s.forEach((store, id) => {
|
|
231
|
-
const actions = [];
|
|
232
|
-
const stateKeys = [];
|
|
233
|
-
for (const k in store) {
|
|
234
|
-
try {
|
|
235
|
-
if (k.startsWith('$') || k.startsWith('_')) continue;
|
|
236
|
-
if (typeof store[k] === 'function') actions.push(k);
|
|
237
|
-
else stateKeys.push(k);
|
|
238
|
-
} catch {}
|
|
239
|
-
}
|
|
240
|
-
stores.push({ type: 'pinia', id, actions: actions.slice(0, 20), stateKeys: stateKeys.slice(0, 15) });
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Vuex store modules
|
|
245
|
-
const vuex = gp?.$store;
|
|
246
|
-
if (vuex?._modules?.root?._children) {
|
|
247
|
-
const children = vuex._modules.root._children;
|
|
248
|
-
for (const [modName, mod] of Object.entries(children)) {
|
|
249
|
-
const actions = Object.keys(mod._rawModule?.actions ?? {}).slice(0, 20);
|
|
250
|
-
const stateKeys = Object.keys(mod.state ?? {}).slice(0, 15);
|
|
251
|
-
stores.push({ type: 'vuex', id: modName, actions, stateKeys });
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
} catch {}
|
|
255
|
-
return stores;
|
|
256
|
-
}
|
|
257
|
-
`;
|
|
210
|
+
const STORE_DISCOVER_JS = discoverStores.toString();
|
|
258
211
|
// ── Auto-Interaction (Fuzzing) ─────────────────────────────────────────────
|
|
259
|
-
const INTERACT_FUZZ_JS =
|
|
260
|
-
async () => {
|
|
261
|
-
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
262
|
-
const clickables = Array.from(document.querySelectorAll(
|
|
263
|
-
'button, [role="button"], [role="tab"], .tab, .btn, a[href="javascript:void(0)"], a[href="#"]'
|
|
264
|
-
)).slice(0, 15); // limit to 15 to avoid endless loops
|
|
265
|
-
|
|
266
|
-
let clicked = 0;
|
|
267
|
-
for (const el of clickables) {
|
|
268
|
-
try {
|
|
269
|
-
const rect = el.getBoundingClientRect();
|
|
270
|
-
if (rect.width > 0 && rect.height > 0) {
|
|
271
|
-
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
|
|
272
|
-
clicked++;
|
|
273
|
-
await sleep(300); // give it time to trigger network
|
|
274
|
-
}
|
|
275
|
-
} catch {}
|
|
276
|
-
}
|
|
277
|
-
return clicked;
|
|
278
|
-
}
|
|
279
|
-
`;
|
|
212
|
+
const INTERACT_FUZZ_JS = interactFuzz.toString();
|
|
280
213
|
// ── Main explore function ──────────────────────────────────────────────────
|
|
281
214
|
export async function exploreUrl(url, opts) {
|
|
282
215
|
const waitSeconds = opts.waitSeconds ?? 3.0;
|
|
@@ -286,25 +219,19 @@ export async function exploreUrl(url, opts) {
|
|
|
286
219
|
// Step 1: Navigate
|
|
287
220
|
await page.goto(url);
|
|
288
221
|
await page.wait(waitSeconds);
|
|
289
|
-
// Step 2: Auto-scroll to trigger lazy loading
|
|
290
|
-
|
|
291
|
-
try {
|
|
292
|
-
await page.pressKey('End');
|
|
293
|
-
}
|
|
294
|
-
catch { }
|
|
295
|
-
await page.wait(1);
|
|
296
|
-
}
|
|
222
|
+
// Step 2: Auto-scroll to trigger lazy loading intelligently
|
|
223
|
+
await page.autoScroll({ times: 3, delayMs: 1500 }).catch(() => { });
|
|
297
224
|
// Step 2.5: Interactive Fuzzing (if requested)
|
|
298
225
|
if (opts.auto) {
|
|
299
226
|
try {
|
|
300
227
|
// First: targeted clicks by label (e.g. "字幕", "CC", "评论")
|
|
301
228
|
if (opts.clickLabels?.length) {
|
|
302
229
|
for (const label of opts.clickLabels) {
|
|
303
|
-
const safeLabel =
|
|
230
|
+
const safeLabel = JSON.stringify(label);
|
|
304
231
|
await page.evaluate(`
|
|
305
232
|
(() => {
|
|
306
233
|
const el = [...document.querySelectorAll('button, [role="button"], [role="tab"], a, span')]
|
|
307
|
-
.find(e => e.textContent && e.textContent.trim().includes(
|
|
234
|
+
.find(e => e.textContent && e.textContent.trim().includes(${safeLabel}));
|
|
308
235
|
if (el) el.click();
|
|
309
236
|
})()
|
|
310
237
|
`);
|
|
@@ -324,11 +251,27 @@ export async function exploreUrl(url, opts) {
|
|
|
324
251
|
// Step 4: Capture network traffic
|
|
325
252
|
const rawNetwork = await page.networkRequests(false);
|
|
326
253
|
const networkEntries = parseNetworkRequests(rawNetwork);
|
|
327
|
-
// Step 5: For JSON endpoints, re-fetch
|
|
328
|
-
const jsonEndpoints = networkEntries.filter(e => e.contentType.includes('json') && e.method === 'GET' && e.status === 200);
|
|
329
|
-
for (const ep of jsonEndpoints.slice(0,
|
|
254
|
+
// Step 5: For JSON endpoints missing a body, carefully re-fetch in-browser via a pristine iframe
|
|
255
|
+
const jsonEndpoints = networkEntries.filter(e => e.contentType.includes('json') && e.method === 'GET' && e.status === 200 && !e.responseBody);
|
|
256
|
+
for (const ep of jsonEndpoints.slice(0, 5)) {
|
|
330
257
|
try {
|
|
331
|
-
const body = await page.evaluate(`async () => {
|
|
258
|
+
const body = await page.evaluate(`async () => {
|
|
259
|
+
let iframe = null;
|
|
260
|
+
try {
|
|
261
|
+
iframe = document.createElement('iframe');
|
|
262
|
+
iframe.style.display = 'none';
|
|
263
|
+
document.body.appendChild(iframe);
|
|
264
|
+
const cleanFetch = iframe.contentWindow.fetch || window.fetch;
|
|
265
|
+
const r = await cleanFetch(${JSON.stringify(ep.url)}, { credentials: 'include' });
|
|
266
|
+
if (!r.ok) return null;
|
|
267
|
+
const d = await r.json();
|
|
268
|
+
return JSON.stringify(d).slice(0, 10000);
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
} finally {
|
|
272
|
+
if (iframe && iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
273
|
+
}
|
|
274
|
+
}`);
|
|
332
275
|
if (body && typeof body === 'string') {
|
|
333
276
|
try {
|
|
334
277
|
ep.responseBody = JSON.parse(body);
|
|
@@ -443,7 +386,7 @@ export async function exploreUrl(url, opts) {
|
|
|
443
386
|
const topStrategy = allAuth.has('signature') ? 'intercept' : allAuth.has('bearer') || allAuth.has('csrf') ? 'header' : allAuth.size === 0 ? 'public' : 'cookie';
|
|
444
387
|
const siteName = opts.site ?? detectSiteName(metadata.url || url);
|
|
445
388
|
const targetDir = opts.outDir ?? path.join('.opencli', 'explore', siteName);
|
|
446
|
-
fs.
|
|
389
|
+
await fs.promises.mkdir(targetDir, { recursive: true });
|
|
447
390
|
const result = {
|
|
448
391
|
site: siteName, target_url: url, final_url: metadata.url, title: metadata.title,
|
|
449
392
|
framework, stores, top_strategy: topStrategy,
|
|
@@ -452,24 +395,26 @@ export async function exploreUrl(url, opts) {
|
|
|
452
395
|
capabilities, auth_indicators: [...allAuth],
|
|
453
396
|
};
|
|
454
397
|
// Write artifacts
|
|
455
|
-
|
|
398
|
+
const writeTasks = [];
|
|
399
|
+
writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'manifest.json'), JSON.stringify({
|
|
456
400
|
site: siteName, target_url: url, final_url: metadata.url, title: metadata.title,
|
|
457
401
|
framework, stores: stores.map(s => ({ type: s.type, id: s.id, actions: s.actions })),
|
|
458
402
|
top_strategy: topStrategy, explored_at: new Date().toISOString(),
|
|
459
|
-
}, null, 2));
|
|
460
|
-
fs.
|
|
403
|
+
}, null, 2)));
|
|
404
|
+
writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'endpoints.json'), JSON.stringify(analyzedEndpoints.map(ep => ({
|
|
461
405
|
pattern: ep.pattern, method: ep.method, url: ep.url, status: ep.status,
|
|
462
406
|
contentType: ep.contentType, score: ep.score, queryParams: ep.queryParams,
|
|
463
407
|
itemPath: ep.responseAnalysis?.itemPath ?? null, itemCount: ep.responseAnalysis?.itemCount ?? 0,
|
|
464
408
|
detectedFields: ep.responseAnalysis?.detectedFields ?? {}, authIndicators: ep.authIndicators,
|
|
465
|
-
})), null, 2));
|
|
466
|
-
fs.
|
|
467
|
-
fs.
|
|
409
|
+
})), null, 2)));
|
|
410
|
+
writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'capabilities.json'), JSON.stringify(capabilities, null, 2)));
|
|
411
|
+
writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'auth.json'), JSON.stringify({
|
|
468
412
|
top_strategy: topStrategy, indicators: [...allAuth], framework,
|
|
469
|
-
}, null, 2));
|
|
413
|
+
}, null, 2)));
|
|
470
414
|
if (stores.length > 0) {
|
|
471
|
-
fs.
|
|
415
|
+
writeTasks.push(fs.promises.writeFile(path.join(targetDir, 'stores.json'), JSON.stringify(stores, null, 2)));
|
|
472
416
|
}
|
|
417
|
+
await Promise.all(writeTasks);
|
|
473
418
|
return { ...result, out_dir: targetDir };
|
|
474
419
|
})(), { timeout: exploreTimeout, label: `Explore ${url}` });
|
|
475
420
|
});
|
package/dist/main.js
CHANGED
|
@@ -5,16 +5,9 @@
|
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import { Strategy, fullName, getRegistry, strategyLabel } from './registry.js';
|
|
12
|
-
import { render as renderOutput } from './output.js';
|
|
13
|
-
import { PlaywrightMCP } from './browser/index.js';
|
|
14
|
-
import { browserSession, DEFAULT_BROWSER_COMMAND_TIMEOUT, runWithTimeout } from './runtime.js';
|
|
15
|
-
import { PKG_VERSION } from './version.js';
|
|
16
|
-
import { getCompletions, printCompletionScript } from './completion.js';
|
|
17
|
-
import { CliError } from './errors.js';
|
|
8
|
+
import { discoverClis } from './engine.js';
|
|
9
|
+
import { getCompletions } from './completion.js';
|
|
10
|
+
import { runCli } from './cli.js';
|
|
18
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
12
|
const __dirname = path.dirname(__filename);
|
|
20
13
|
const BUILTIN_CLIS = path.resolve(__dirname, 'clis');
|
|
@@ -42,196 +35,4 @@ if (getCompIdx !== -1) {
|
|
|
42
35
|
process.stdout.write(candidates.join('\n') + '\n');
|
|
43
36
|
process.exit(0);
|
|
44
37
|
}
|
|
45
|
-
|
|
46
|
-
program.name('opencli').description('Make any website your CLI. Zero setup. AI-powered.').version(PKG_VERSION);
|
|
47
|
-
// ── Built-in commands ──────────────────────────────────────────────────────
|
|
48
|
-
program.command('list').description('List all available CLI commands').option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table').option('--json', 'JSON output (deprecated)')
|
|
49
|
-
.action((opts) => {
|
|
50
|
-
const registry = getRegistry();
|
|
51
|
-
const commands = [...registry.values()].sort((a, b) => fullName(a).localeCompare(fullName(b)));
|
|
52
|
-
const rows = commands.map(c => ({
|
|
53
|
-
command: fullName(c),
|
|
54
|
-
site: c.site,
|
|
55
|
-
name: c.name,
|
|
56
|
-
description: c.description,
|
|
57
|
-
strategy: strategyLabel(c),
|
|
58
|
-
browser: c.browser,
|
|
59
|
-
args: c.args.map(a => a.name).join(', '),
|
|
60
|
-
}));
|
|
61
|
-
const fmt = opts.json && opts.format === 'table' ? 'json' : opts.format;
|
|
62
|
-
if (fmt !== 'table') {
|
|
63
|
-
renderOutput(rows, {
|
|
64
|
-
fmt,
|
|
65
|
-
columns: ['command', 'site', 'name', 'description', 'strategy', 'browser', 'args'],
|
|
66
|
-
title: 'opencli/list',
|
|
67
|
-
source: 'opencli list',
|
|
68
|
-
});
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const sites = new Map();
|
|
72
|
-
for (const cmd of commands) {
|
|
73
|
-
const g = sites.get(cmd.site) ?? [];
|
|
74
|
-
g.push(cmd);
|
|
75
|
-
sites.set(cmd.site, g);
|
|
76
|
-
}
|
|
77
|
-
console.log();
|
|
78
|
-
console.log(chalk.bold(' opencli') + chalk.dim(' — available commands'));
|
|
79
|
-
console.log();
|
|
80
|
-
for (const [site, cmds] of sites) {
|
|
81
|
-
console.log(chalk.bold.cyan(` ${site}`));
|
|
82
|
-
for (const cmd of cmds) {
|
|
83
|
-
const tag = strategyLabel(cmd) === 'public' ? chalk.green('[public]') : chalk.yellow(`[${strategyLabel(cmd)}]`);
|
|
84
|
-
console.log(` ${cmd.name} ${tag}${cmd.description ? chalk.dim(` — ${cmd.description}`) : ''}`);
|
|
85
|
-
}
|
|
86
|
-
console.log();
|
|
87
|
-
}
|
|
88
|
-
console.log(chalk.dim(` ${commands.length} commands across ${sites.size} sites`));
|
|
89
|
-
console.log();
|
|
90
|
-
});
|
|
91
|
-
program.command('validate').description('Validate CLI definitions').argument('[target]', 'site or site/name')
|
|
92
|
-
.action(async (target) => {
|
|
93
|
-
const { validateClisWithTarget, renderValidationReport } = await import('./validate.js');
|
|
94
|
-
console.log(renderValidationReport(validateClisWithTarget([BUILTIN_CLIS, USER_CLIS], target)));
|
|
95
|
-
});
|
|
96
|
-
program.command('verify').description('Validate + smoke test').argument('[target]').option('--smoke', 'Run smoke tests', false)
|
|
97
|
-
.action(async (target, opts) => {
|
|
98
|
-
const { verifyClis, renderVerifyReport } = await import('./verify.js');
|
|
99
|
-
const r = await verifyClis({ builtinClis: BUILTIN_CLIS, userClis: USER_CLIS, target, smoke: opts.smoke });
|
|
100
|
-
console.log(renderVerifyReport(r));
|
|
101
|
-
process.exitCode = r.ok ? 0 : 1;
|
|
102
|
-
});
|
|
103
|
-
program.command('explore').alias('probe').description('Explore a website: discover APIs, stores, and recommend strategies').argument('<url>').option('--site <name>').option('--goal <text>').option('--wait <s>', '', '3').option('--auto', 'Enable interactive fuzzing (simulate clicks to trigger lazy APIs)').option('--click <labels>', 'Comma-separated labels to click before fuzzing (e.g. "字幕,CC,评论")')
|
|
104
|
-
.action(async (url, opts) => { const { exploreUrl, renderExploreSummary } = await import('./explore.js'); const clickLabels = opts.click ? opts.click.split(',').map((s) => s.trim()) : undefined; console.log(renderExploreSummary(await exploreUrl(url, { BrowserFactory: PlaywrightMCP, site: opts.site, goal: opts.goal, waitSeconds: parseFloat(opts.wait), auto: opts.auto, clickLabels }))); });
|
|
105
|
-
program.command('synthesize').description('Synthesize CLIs from explore').argument('<target>').option('--top <n>', '', '3')
|
|
106
|
-
.action(async (target, opts) => { const { synthesizeFromExplore, renderSynthesizeSummary } = await import('./synthesize.js'); console.log(renderSynthesizeSummary(synthesizeFromExplore(target, { top: parseInt(opts.top) }))); });
|
|
107
|
-
program.command('generate').description('One-shot: explore → synthesize → register').argument('<url>').option('--goal <text>').option('--site <name>')
|
|
108
|
-
.action(async (url, opts) => { const { generateCliFromUrl, renderGenerateSummary } = await import('./generate.js'); const r = await generateCliFromUrl({ url, BrowserFactory: PlaywrightMCP, builtinClis: BUILTIN_CLIS, userClis: USER_CLIS, goal: opts.goal, site: opts.site }); console.log(renderGenerateSummary(r)); process.exitCode = r.ok ? 0 : 1; });
|
|
109
|
-
program.command('cascade').description('Strategy cascade: find simplest working strategy').argument('<url>').option('--site <name>')
|
|
110
|
-
.action(async (url, opts) => {
|
|
111
|
-
const { cascadeProbe, renderCascadeResult } = await import('./cascade.js');
|
|
112
|
-
const result = await browserSession(PlaywrightMCP, async (page) => {
|
|
113
|
-
// Navigate to the site first for cookie context
|
|
114
|
-
try {
|
|
115
|
-
const siteUrl = new URL(url);
|
|
116
|
-
await page.goto(`${siteUrl.protocol}//${siteUrl.host}`);
|
|
117
|
-
await page.wait(2);
|
|
118
|
-
}
|
|
119
|
-
catch { }
|
|
120
|
-
return cascadeProbe(page, url);
|
|
121
|
-
});
|
|
122
|
-
console.log(renderCascadeResult(result));
|
|
123
|
-
});
|
|
124
|
-
program.command('doctor')
|
|
125
|
-
.description('Diagnose opencli browser bridge connectivity')
|
|
126
|
-
.option('--live', 'Test browser connectivity (requires Chrome running)', false)
|
|
127
|
-
.action(async (opts) => {
|
|
128
|
-
const { runBrowserDoctor, renderBrowserDoctorReport } = await import('./doctor.js');
|
|
129
|
-
const report = await runBrowserDoctor({ live: opts.live, cliVersion: PKG_VERSION });
|
|
130
|
-
console.log(renderBrowserDoctorReport(report));
|
|
131
|
-
});
|
|
132
|
-
program.command('setup')
|
|
133
|
-
.description('Interactive setup: verify browser bridge connectivity')
|
|
134
|
-
.action(async () => {
|
|
135
|
-
const { runSetup } = await import('./setup.js');
|
|
136
|
-
await runSetup({ cliVersion: PKG_VERSION });
|
|
137
|
-
});
|
|
138
|
-
program.command('completion')
|
|
139
|
-
.description('Output shell completion script')
|
|
140
|
-
.argument('<shell>', 'Shell type: bash, zsh, or fish')
|
|
141
|
-
.action((shell) => {
|
|
142
|
-
printCompletionScript(shell);
|
|
143
|
-
});
|
|
144
|
-
// ── Dynamic site commands ──────────────────────────────────────────────────
|
|
145
|
-
const registry = getRegistry();
|
|
146
|
-
const siteGroups = new Map();
|
|
147
|
-
for (const [, cmd] of registry) {
|
|
148
|
-
let siteCmd = siteGroups.get(cmd.site);
|
|
149
|
-
if (!siteCmd) {
|
|
150
|
-
siteCmd = program.command(cmd.site).description(`${cmd.site} commands`);
|
|
151
|
-
siteGroups.set(cmd.site, siteCmd);
|
|
152
|
-
}
|
|
153
|
-
const subCmd = siteCmd.command(cmd.name).description(cmd.description);
|
|
154
|
-
// Register positional args first, then named options
|
|
155
|
-
const positionalArgs = [];
|
|
156
|
-
for (const arg of cmd.args) {
|
|
157
|
-
if (arg.positional) {
|
|
158
|
-
const bracket = arg.required ? `<${arg.name}>` : `[${arg.name}]`;
|
|
159
|
-
subCmd.argument(bracket, arg.help ?? '');
|
|
160
|
-
positionalArgs.push(arg);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
const flag = arg.required ? `--${arg.name} <value>` : `--${arg.name} [value]`;
|
|
164
|
-
if (arg.required)
|
|
165
|
-
subCmd.requiredOption(flag, arg.help ?? '');
|
|
166
|
-
else if (arg.default != null)
|
|
167
|
-
subCmd.option(flag, arg.help ?? '', String(arg.default));
|
|
168
|
-
else
|
|
169
|
-
subCmd.option(flag, arg.help ?? '');
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
subCmd.option('-f, --format <fmt>', 'Output format: table, json, yaml, md, csv', 'table').option('-v, --verbose', 'Debug output', false);
|
|
173
|
-
subCmd.action(async (...actionArgs) => {
|
|
174
|
-
// Commander passes positional args first, then options object, then the Command
|
|
175
|
-
const actionOpts = actionArgs[positionalArgs.length] ?? {};
|
|
176
|
-
const startTime = Date.now();
|
|
177
|
-
const kwargs = {};
|
|
178
|
-
// Collect positional args
|
|
179
|
-
for (let i = 0; i < positionalArgs.length; i++) {
|
|
180
|
-
const arg = positionalArgs[i];
|
|
181
|
-
const v = actionArgs[i];
|
|
182
|
-
if (v !== undefined)
|
|
183
|
-
kwargs[arg.name] = v;
|
|
184
|
-
}
|
|
185
|
-
// Collect named options
|
|
186
|
-
for (const arg of cmd.args) {
|
|
187
|
-
if (arg.positional)
|
|
188
|
-
continue;
|
|
189
|
-
const camelName = arg.name.replace(/-([a-z])/g, (_m, ch) => ch.toUpperCase());
|
|
190
|
-
const v = actionOpts[arg.name] ?? actionOpts[camelName];
|
|
191
|
-
if (v !== undefined)
|
|
192
|
-
kwargs[arg.name] = v;
|
|
193
|
-
}
|
|
194
|
-
try {
|
|
195
|
-
if (actionOpts.verbose)
|
|
196
|
-
process.env.OPENCLI_VERBOSE = '1';
|
|
197
|
-
let result;
|
|
198
|
-
if (cmd.browser) {
|
|
199
|
-
result = await browserSession(PlaywrightMCP, async (page) => {
|
|
200
|
-
// Cookie/header strategies require same-origin context for credentialed fetch.
|
|
201
|
-
// In CDP mode the active tab may be on an unrelated domain, causing CORS failures.
|
|
202
|
-
// Navigate to the command's domain first (mirrors cascade command behavior).
|
|
203
|
-
if ((cmd.strategy === Strategy.COOKIE || cmd.strategy === Strategy.HEADER) && cmd.domain) {
|
|
204
|
-
try {
|
|
205
|
-
await page.goto(`https://${cmd.domain}`);
|
|
206
|
-
await page.wait(2);
|
|
207
|
-
}
|
|
208
|
-
catch { }
|
|
209
|
-
}
|
|
210
|
-
return runWithTimeout(executeCommand(cmd, page, kwargs, actionOpts.verbose), { timeout: cmd.timeoutSeconds ?? DEFAULT_BROWSER_COMMAND_TIMEOUT, label: fullName(cmd) });
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
result = await executeCommand(cmd, null, kwargs, actionOpts.verbose);
|
|
215
|
-
}
|
|
216
|
-
if (actionOpts.verbose && (!result || (Array.isArray(result) && result.length === 0))) {
|
|
217
|
-
console.error(chalk.yellow(`[Verbose] Warning: Command returned an empty result. If the website structural API changed or requires authentication, check the network or update the adapter.`));
|
|
218
|
-
}
|
|
219
|
-
renderOutput(result, { fmt: actionOpts.format, columns: cmd.columns, title: `${cmd.site}/${cmd.name}`, elapsed: (Date.now() - startTime) / 1000, source: fullName(cmd) });
|
|
220
|
-
}
|
|
221
|
-
catch (err) {
|
|
222
|
-
if (err instanceof CliError) {
|
|
223
|
-
console.error(chalk.red(`Error [${err.code}]: ${err.message}`));
|
|
224
|
-
if (err.hint)
|
|
225
|
-
console.error(chalk.yellow(`Hint: ${err.hint}`));
|
|
226
|
-
}
|
|
227
|
-
else if (actionOpts.verbose && err.stack) {
|
|
228
|
-
console.error(chalk.red(err.stack));
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
console.error(chalk.red(`Error: ${err.message ?? err}`));
|
|
232
|
-
}
|
|
233
|
-
process.exitCode = 1;
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
program.parse();
|
|
38
|
+
runCli(BUILTIN_CLIS, USER_CLIS);
|
package/dist/output.d.ts
CHANGED
package/dist/registry.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare enum Strategy {
|
|
|
12
12
|
export interface Arg {
|
|
13
13
|
name: string;
|
|
14
14
|
type?: string;
|
|
15
|
-
default?:
|
|
15
|
+
default?: unknown;
|
|
16
16
|
required?: boolean;
|
|
17
17
|
positional?: boolean;
|
|
18
18
|
help?: string;
|
|
@@ -27,8 +27,8 @@ export interface CliCommand {
|
|
|
27
27
|
browser?: boolean;
|
|
28
28
|
args: Arg[];
|
|
29
29
|
columns?: string[];
|
|
30
|
-
func?: (page: IPage, kwargs: Record<string, any>, debug?: boolean) => Promise<
|
|
31
|
-
pipeline?:
|
|
30
|
+
func?: (page: IPage, kwargs: Record<string, any>, debug?: boolean) => Promise<unknown>;
|
|
31
|
+
pipeline?: Record<string, unknown>[];
|
|
32
32
|
timeoutSeconds?: number;
|
|
33
33
|
source?: string;
|
|
34
34
|
}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime utilities: timeouts and browser session management.
|
|
3
|
-
*/
|
|
4
1
|
import type { IPage } from './types.js';
|
|
5
2
|
export declare const DEFAULT_BROWSER_CONNECT_TIMEOUT: number;
|
|
6
3
|
export declare const DEFAULT_BROWSER_COMMAND_TIMEOUT: number;
|
|
@@ -17,7 +14,7 @@ export declare function runWithTimeout<T>(promise: Promise<T>, opts: {
|
|
|
17
14
|
* Timeout with milliseconds unit. Used for low-level internal timeouts.
|
|
18
15
|
*/
|
|
19
16
|
export declare function withTimeoutMs<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T>;
|
|
20
|
-
/** Interface for browser factory (
|
|
17
|
+
/** Interface for browser factory (BrowserBridge or test mocks) */
|
|
21
18
|
export interface IBrowserFactory {
|
|
22
19
|
connect(opts?: {
|
|
23
20
|
timeout?: number;
|
package/dist/runtime.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime utilities: timeouts and browser session management.
|
|
3
|
-
*/
|
|
4
1
|
export const DEFAULT_BROWSER_CONNECT_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_CONNECT_TIMEOUT ?? '30', 10);
|
|
5
|
-
export const DEFAULT_BROWSER_COMMAND_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_COMMAND_TIMEOUT ?? '
|
|
2
|
+
export const DEFAULT_BROWSER_COMMAND_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_COMMAND_TIMEOUT ?? '60', 10);
|
|
6
3
|
export const DEFAULT_BROWSER_EXPLORE_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_EXPLORE_TIMEOUT ?? '120', 10);
|
|
7
4
|
export const DEFAULT_BROWSER_SMOKE_TIMEOUT = parseInt(process.env.OPENCLI_BROWSER_SMOKE_TIMEOUT ?? '60', 10);
|
|
8
5
|
/**
|