@jackwener/opencli 1.6.7 → 1.6.9
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 +5 -1
- package/README.zh-CN.md +8 -3
- package/dist/clis/1688/assets.d.ts +42 -0
- package/dist/clis/1688/assets.js +204 -0
- package/dist/clis/1688/assets.test.d.ts +1 -0
- package/dist/clis/1688/assets.test.js +39 -0
- package/dist/clis/1688/download.d.ts +9 -0
- package/dist/clis/1688/download.js +76 -0
- package/dist/clis/1688/download.test.d.ts +1 -0
- package/dist/clis/1688/download.test.js +31 -0
- package/dist/clis/1688/shared.d.ts +10 -0
- package/dist/clis/1688/shared.js +43 -0
- package/dist/clis/jianyu/search.d.ts +14 -0
- package/dist/clis/jianyu/search.js +135 -0
- package/dist/clis/jianyu/search.test.d.ts +1 -0
- package/dist/clis/jianyu/search.test.js +23 -0
- package/dist/clis/linux-do/topic-content.d.ts +35 -0
- package/dist/clis/linux-do/topic-content.js +154 -0
- package/dist/clis/linux-do/topic-content.test.d.ts +1 -0
- package/dist/clis/linux-do/topic-content.test.js +59 -0
- package/dist/clis/linux-do/topic.yaml +1 -16
- package/dist/clis/quark/ls.d.ts +1 -0
- package/dist/clis/quark/ls.js +63 -0
- package/dist/clis/quark/mkdir.d.ts +1 -0
- package/dist/clis/quark/mkdir.js +36 -0
- package/dist/clis/quark/mv.d.ts +1 -0
- package/dist/clis/quark/mv.js +53 -0
- package/dist/clis/quark/rename.d.ts +1 -0
- package/dist/clis/quark/rename.js +26 -0
- package/dist/clis/quark/rm.d.ts +1 -0
- package/dist/clis/quark/rm.js +24 -0
- package/dist/clis/quark/save.d.ts +1 -0
- package/dist/clis/quark/save.js +80 -0
- package/dist/clis/quark/share-tree.d.ts +1 -0
- package/dist/clis/quark/share-tree.js +45 -0
- package/dist/clis/quark/utils.d.ts +50 -0
- package/dist/clis/quark/utils.js +146 -0
- package/dist/clis/quark/utils.test.d.ts +1 -0
- package/dist/clis/quark/utils.test.js +58 -0
- package/dist/clis/twitter/reply.js +3 -8
- package/dist/clis/twitter/reply.test.js +5 -5
- package/dist/clis/xiaohongshu/note.js +8 -3
- package/dist/clis/xiaohongshu/note.test.js +11 -0
- package/dist/clis/xueqiu/groups.yaml +23 -0
- package/dist/clis/xueqiu/kline.yaml +65 -0
- package/dist/clis/xueqiu/watchlist.yaml +9 -9
- package/dist/clis/zhihu/answer.d.ts +1 -0
- package/dist/clis/zhihu/answer.js +194 -0
- package/dist/clis/zhihu/answer.test.d.ts +1 -0
- package/dist/clis/zhihu/answer.test.js +81 -0
- package/dist/clis/zhihu/comment.d.ts +1 -0
- package/dist/clis/zhihu/comment.js +335 -0
- package/dist/clis/zhihu/comment.test.d.ts +1 -0
- package/dist/clis/zhihu/comment.test.js +54 -0
- package/dist/clis/zhihu/favorite.d.ts +1 -0
- package/dist/clis/zhihu/favorite.js +224 -0
- package/dist/clis/zhihu/favorite.test.d.ts +1 -0
- package/dist/clis/zhihu/favorite.test.js +196 -0
- package/dist/clis/zhihu/follow.d.ts +1 -0
- package/dist/clis/zhihu/follow.js +80 -0
- package/dist/clis/zhihu/follow.test.d.ts +1 -0
- package/dist/clis/zhihu/follow.test.js +45 -0
- package/dist/clis/zhihu/like.d.ts +1 -0
- package/dist/clis/zhihu/like.js +91 -0
- package/dist/clis/zhihu/like.test.d.ts +1 -0
- package/dist/clis/zhihu/like.test.js +64 -0
- package/dist/clis/zhihu/target.d.ts +24 -0
- package/dist/clis/zhihu/target.js +91 -0
- package/dist/clis/zhihu/target.test.d.ts +1 -0
- package/dist/clis/zhihu/target.test.js +77 -0
- package/dist/clis/zhihu/write-shared.d.ts +32 -0
- package/dist/clis/zhihu/write-shared.js +221 -0
- package/dist/clis/zhihu/write-shared.test.d.ts +1 -0
- package/dist/clis/zhihu/write-shared.test.js +175 -0
- package/dist/src/analysis.d.ts +2 -0
- package/dist/src/analysis.js +6 -0
- package/dist/src/browser/bridge.d.ts +2 -0
- package/dist/src/browser/bridge.js +30 -24
- package/dist/src/browser/cdp.js +96 -0
- package/dist/src/browser/daemon-client.d.ts +17 -8
- package/dist/src/browser/daemon-client.js +12 -13
- package/dist/src/browser/daemon-client.test.js +32 -25
- package/dist/src/browser/index.d.ts +2 -1
- package/dist/src/browser/index.js +1 -1
- package/dist/src/browser.test.js +2 -3
- package/dist/src/build-manifest.d.ts +3 -1
- package/dist/src/build-manifest.js +10 -7
- package/dist/src/build-manifest.test.js +8 -4
- package/dist/src/cli.d.ts +2 -1
- package/dist/src/cli.js +48 -46
- package/dist/src/clis/binance/commands.test.d.ts +1 -0
- package/dist/src/clis/binance/commands.test.js +54 -0
- package/dist/src/commanderAdapter.js +19 -6
- package/dist/src/commands/daemon.js +2 -10
- package/dist/src/diagnostic.d.ts +28 -2
- package/dist/src/diagnostic.js +263 -25
- package/dist/src/diagnostic.test.js +220 -1
- package/dist/src/discovery.js +7 -17
- package/dist/src/doctor.d.ts +2 -0
- package/dist/src/doctor.js +59 -31
- package/dist/src/doctor.test.js +89 -16
- package/dist/src/download/progress.js +7 -2
- package/dist/src/execution.js +1 -13
- package/dist/src/explore.d.ts +0 -2
- package/dist/src/explore.js +61 -38
- package/dist/src/extension-manifest-regression.test.js +0 -1
- package/dist/src/generate.d.ts +3 -6
- package/dist/src/generate.js +4 -8
- package/dist/src/package-paths.d.ts +8 -0
- package/dist/src/package-paths.js +41 -0
- package/dist/src/plugin-scaffold.js +1 -3
- package/dist/src/plugin.d.ts +2 -1
- package/dist/src/plugin.js +25 -8
- package/dist/src/plugin.test.js +16 -1
- package/dist/src/record.d.ts +1 -2
- package/dist/src/record.js +14 -52
- package/dist/src/synthesize.d.ts +0 -2
- package/dist/src/synthesize.js +8 -4
- package/package.json +3 -3
- package/dist/cli-manifest.json +0 -17250
- package/dist/src/browser/discover.d.ts +0 -15
- package/dist/src/browser/discover.js +0 -19
package/dist/src/plugin.d.ts
CHANGED
|
@@ -142,4 +142,5 @@ declare function parseSource(source: string): ParsedSource | null;
|
|
|
142
142
|
* Resolve the path to the esbuild CLI executable with fallback strategies.
|
|
143
143
|
*/
|
|
144
144
|
export declare function resolveEsbuildBin(): string | null;
|
|
145
|
-
|
|
145
|
+
declare function resolveHostOpencliRoot(startFile?: string): string;
|
|
146
|
+
export { resolveHostOpencliRoot as _resolveHostOpencliRoot, resolveEsbuildBin as _resolveEsbuildBin, getCommitHash as _getCommitHash, installDependencies as _installDependencies, parseSource as _parseSource, postInstallMonorepoLifecycle as _postInstallMonorepoLifecycle, readLockFile as _readLockFile, readLockFileWithWriter as _readLockFileWithWriter, updateAllPlugins as _updateAllPlugins, validatePluginStructure as _validatePluginStructure, writeLockFile as _writeLockFile, writeLockFileWithFs as _writeLockFileWithFs, isSymlinkSync as _isSymlinkSync, getMonoreposDir as _getMonoreposDir, installLocalPlugin as _installLocalPlugin, isLocalPluginSource as _isLocalPluginSource, moveDir as _moveDir, promoteDir as _promoteDir, replaceDir as _replaceDir, resolvePluginSource as _resolvePluginSource, resolveStoredPluginSource as _resolveStoredPluginSource, toStoredPluginSource as _toStoredPluginSource, toLocalPluginSource as _toLocalPluginSource, };
|
package/dist/src/plugin.js
CHANGED
|
@@ -1100,11 +1100,7 @@ function parseSource(source) {
|
|
|
1100
1100
|
*/
|
|
1101
1101
|
function linkHostOpencli(pluginDir) {
|
|
1102
1102
|
try {
|
|
1103
|
-
|
|
1104
|
-
// Both dev (tsx src/plugin.ts) and prod (node dist/plugin.js) are one level
|
|
1105
|
-
// deep, so path.dirname + '..' always gives us the package root.
|
|
1106
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
1107
|
-
const hostRoot = path.resolve(path.dirname(thisFile), '..');
|
|
1103
|
+
const hostRoot = resolveHostOpencliRoot();
|
|
1108
1104
|
const targetLink = path.join(pluginDir, 'node_modules', '@jackwener', 'opencli');
|
|
1109
1105
|
// Remove existing (npm-installed copy or stale symlink)
|
|
1110
1106
|
if (fs.existsSync(targetLink)) {
|
|
@@ -1126,8 +1122,7 @@ function linkHostOpencli(pluginDir) {
|
|
|
1126
1122
|
* Resolve the path to the esbuild CLI executable with fallback strategies.
|
|
1127
1123
|
*/
|
|
1128
1124
|
export function resolveEsbuildBin() {
|
|
1129
|
-
const
|
|
1130
|
-
const hostRoot = path.resolve(path.dirname(thisFile), '..');
|
|
1125
|
+
const hostRoot = resolveHostOpencliRoot();
|
|
1131
1126
|
// Strategy 1 (Windows): prefer the .cmd wrapper which is executable via shell
|
|
1132
1127
|
if (isWindows) {
|
|
1133
1128
|
const cmdPath = path.join(hostRoot, 'node_modules', '.bin', 'esbuild.cmd');
|
|
@@ -1180,6 +1175,28 @@ export function resolveEsbuildBin() {
|
|
|
1180
1175
|
}
|
|
1181
1176
|
return null;
|
|
1182
1177
|
}
|
|
1178
|
+
function resolveHostOpencliRoot(startFile = fileURLToPath(import.meta.url)) {
|
|
1179
|
+
let dir = path.dirname(startFile);
|
|
1180
|
+
while (true) {
|
|
1181
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
1182
|
+
if (fs.existsSync(pkgPath)) {
|
|
1183
|
+
try {
|
|
1184
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
1185
|
+
if (pkg?.name === '@jackwener/opencli') {
|
|
1186
|
+
return dir;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
catch {
|
|
1190
|
+
// Keep walking; a malformed package.json should not hide an ancestor package root.
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const parent = path.dirname(dir);
|
|
1194
|
+
if (parent === dir)
|
|
1195
|
+
break;
|
|
1196
|
+
dir = parent;
|
|
1197
|
+
}
|
|
1198
|
+
return path.resolve(path.dirname(startFile), '..');
|
|
1199
|
+
}
|
|
1183
1200
|
/**
|
|
1184
1201
|
* Transpile TS plugin files to JS so they work in production mode.
|
|
1185
1202
|
* Uses esbuild from the host opencli's node_modules for fast single-file transpilation.
|
|
@@ -1218,4 +1235,4 @@ function transpilePluginTs(pluginDir) {
|
|
|
1218
1235
|
log.warn(`TS transpilation setup failed: ${getErrorMessage(err)}`);
|
|
1219
1236
|
}
|
|
1220
1237
|
}
|
|
1221
|
-
export { resolveEsbuildBin as _resolveEsbuildBin, getCommitHash as _getCommitHash, installDependencies as _installDependencies, parseSource as _parseSource, postInstallMonorepoLifecycle as _postInstallMonorepoLifecycle, readLockFile as _readLockFile, readLockFileWithWriter as _readLockFileWithWriter, updateAllPlugins as _updateAllPlugins, validatePluginStructure as _validatePluginStructure, writeLockFile as _writeLockFile, writeLockFileWithFs as _writeLockFileWithFs, isSymlinkSync as _isSymlinkSync, getMonoreposDir as _getMonoreposDir, installLocalPlugin as _installLocalPlugin, isLocalPluginSource as _isLocalPluginSource, moveDir as _moveDir, promoteDir as _promoteDir, replaceDir as _replaceDir, resolvePluginSource as _resolvePluginSource, resolveStoredPluginSource as _resolveStoredPluginSource, toStoredPluginSource as _toStoredPluginSource, toLocalPluginSource as _toLocalPluginSource, };
|
|
1238
|
+
export { resolveHostOpencliRoot as _resolveHostOpencliRoot, resolveEsbuildBin as _resolveEsbuildBin, getCommitHash as _getCommitHash, installDependencies as _installDependencies, parseSource as _parseSource, postInstallMonorepoLifecycle as _postInstallMonorepoLifecycle, readLockFile as _readLockFile, readLockFileWithWriter as _readLockFileWithWriter, updateAllPlugins as _updateAllPlugins, validatePluginStructure as _validatePluginStructure, writeLockFile as _writeLockFile, writeLockFileWithFs as _writeLockFileWithFs, isSymlinkSync as _isSymlinkSync, getMonoreposDir as _getMonoreposDir, installLocalPlugin as _installLocalPlugin, isLocalPluginSource as _isLocalPluginSource, moveDir as _moveDir, promoteDir as _promoteDir, replaceDir as _replaceDir, resolvePluginSource as _resolvePluginSource, resolveStoredPluginSource as _resolveStoredPluginSource, toStoredPluginSource as _toStoredPluginSource, toLocalPluginSource as _toLocalPluginSource, };
|
package/dist/src/plugin.test.js
CHANGED
|
@@ -12,7 +12,7 @@ const { mockExecFileSync, mockExecSync } = vi.hoisted(() => ({
|
|
|
12
12
|
mockExecFileSync: vi.fn(),
|
|
13
13
|
mockExecSync: vi.fn(),
|
|
14
14
|
}));
|
|
15
|
-
const { _getCommitHash, _installDependencies, _postInstallMonorepoLifecycle, _promoteDir, _replaceDir, installPlugin, listPlugins, _readLockFile, _readLockFileWithWriter, _resolveEsbuildBin, uninstallPlugin, updatePlugin, _parseSource, _updateAllPlugins, _validatePluginStructure, _writeLockFile, _writeLockFileWithFs, _isSymlinkSync, _getMonoreposDir, getLockFilePath, _installLocalPlugin, _isLocalPluginSource, _moveDir, _resolvePluginSource, _resolveStoredPluginSource, _toStoredPluginSource, _toLocalPluginSource, } = pluginModule;
|
|
15
|
+
const { _getCommitHash, _installDependencies, _postInstallMonorepoLifecycle, _promoteDir, _replaceDir, installPlugin, listPlugins, _readLockFile, _readLockFileWithWriter, _resolveEsbuildBin, _resolveHostOpencliRoot, uninstallPlugin, updatePlugin, _parseSource, _updateAllPlugins, _validatePluginStructure, _writeLockFile, _writeLockFileWithFs, _isSymlinkSync, _getMonoreposDir, getLockFilePath, _installLocalPlugin, _isLocalPluginSource, _moveDir, _resolvePluginSource, _resolveStoredPluginSource, _toStoredPluginSource, _toLocalPluginSource, } = pluginModule;
|
|
16
16
|
describe('parseSource', () => {
|
|
17
17
|
it('parses github:user/repo format', () => {
|
|
18
18
|
const result = _parseSource('github:ByteYue/opencli-plugin-github-trending');
|
|
@@ -336,6 +336,21 @@ describe('resolveEsbuildBin', () => {
|
|
|
336
336
|
expect(binPath).toMatch(/esbuild(\.cmd)?$/);
|
|
337
337
|
});
|
|
338
338
|
});
|
|
339
|
+
describe('resolveHostOpencliRoot', () => {
|
|
340
|
+
let tmpDir;
|
|
341
|
+
beforeEach(() => {
|
|
342
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-host-root-test-'));
|
|
343
|
+
});
|
|
344
|
+
afterEach(() => {
|
|
345
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
346
|
+
});
|
|
347
|
+
it('walks up from compiled dist/src files to the package root', () => {
|
|
348
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({ name: '@jackwener/opencli' }));
|
|
349
|
+
const distSrcDir = path.join(tmpDir, 'dist', 'src');
|
|
350
|
+
fs.mkdirSync(distSrcDir, { recursive: true });
|
|
351
|
+
expect(_resolveHostOpencliRoot(path.join(distSrcDir, 'plugin.js'))).toBe(tmpDir);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
339
354
|
describe('listPlugins', () => {
|
|
340
355
|
const testDir = path.join(PLUGINS_DIR, '__test-list-plugin__');
|
|
341
356
|
afterEach(() => {
|
package/dist/src/record.d.ts
CHANGED
|
@@ -45,7 +45,6 @@ type RecordedCandidateKind = 'read' | 'write';
|
|
|
45
45
|
export interface RecordedCandidate {
|
|
46
46
|
kind: RecordedCandidateKind;
|
|
47
47
|
req: RecordedRequest;
|
|
48
|
-
score: number;
|
|
49
48
|
arrayResult: ReturnType<typeof findArrayPath> | null;
|
|
50
49
|
}
|
|
51
50
|
interface GeneratedRecordedCandidate {
|
|
@@ -70,7 +69,7 @@ export declare function createRecordedEntry(input: {
|
|
|
70
69
|
* for every JSON response. No URL pattern filter — captures everything.
|
|
71
70
|
*/
|
|
72
71
|
export declare function generateFullCaptureInterceptorJs(): string;
|
|
73
|
-
/** Analyze recorded requests into read and write candidates. */
|
|
72
|
+
/** Analyze recorded requests into read and write candidates, filtering out noise. */
|
|
74
73
|
export declare function analyzeRecordedRequests(requests: RecordedRequest[]): {
|
|
75
74
|
candidates: RecordedCandidate[];
|
|
76
75
|
};
|
package/dist/src/record.js
CHANGED
|
@@ -18,26 +18,11 @@ import chalk from 'chalk';
|
|
|
18
18
|
import yaml from 'js-yaml';
|
|
19
19
|
import { sendCommand } from './browser/daemon-client.js';
|
|
20
20
|
import { SEARCH_PARAMS, PAGINATION_PARAMS, FIELD_ROLES } from './constants.js';
|
|
21
|
-
import { urlToPattern, findArrayPath, inferCapabilityName, inferStrategy, detectAuthFromContent, classifyQueryParams, } from './analysis.js';
|
|
22
|
-
/** Keep the
|
|
23
|
-
function preferRecordedCandidate(
|
|
24
|
-
if (next.score > current.score)
|
|
25
|
-
return next;
|
|
26
|
-
if (next.score < current.score)
|
|
27
|
-
return current;
|
|
21
|
+
import { urlToPattern, findArrayPath, inferCapabilityName, inferStrategy, detectAuthFromContent, classifyQueryParams, isNoiseUrl, } from './analysis.js';
|
|
22
|
+
/** Keep the later candidate when multiple recordings share one bucket (prefer fresher data). */
|
|
23
|
+
function preferRecordedCandidate(_current, next) {
|
|
28
24
|
return next;
|
|
29
25
|
}
|
|
30
|
-
/** Apply shared endpoint score tweaks. */
|
|
31
|
-
function applyCommonEndpointScoreAdjustments(req, score) {
|
|
32
|
-
let adjusted = score;
|
|
33
|
-
if (req.url.includes('/api/'))
|
|
34
|
-
adjusted += 3;
|
|
35
|
-
if (req.url.match(/\/(track|log|analytics|beacon|pixel|stats|metric)/i))
|
|
36
|
-
adjusted -= 10;
|
|
37
|
-
if (req.url.match(/\/(ping|heartbeat|keep.?alive)/i))
|
|
38
|
-
adjusted -= 10;
|
|
39
|
-
return adjusted;
|
|
40
|
-
}
|
|
41
26
|
/** Build a candidate-level dedupe key. */
|
|
42
27
|
function getRecordedCandidateKey(candidate) {
|
|
43
28
|
return `${candidate.kind} ${getRecordedRequestKey(candidate.req)}`;
|
|
@@ -262,23 +247,6 @@ function generateReadRecordedJs() {
|
|
|
262
247
|
`;
|
|
263
248
|
}
|
|
264
249
|
// ── Analysis helpers ───────────────────────────────────────────────────────
|
|
265
|
-
function scoreRequest(req, arrayResult) {
|
|
266
|
-
let s = 0;
|
|
267
|
-
if (arrayResult) {
|
|
268
|
-
s += 10;
|
|
269
|
-
s += Math.min(arrayResult.items.length, 10);
|
|
270
|
-
// Bonus for detected semantic fields
|
|
271
|
-
const sample = arrayResult.items[0];
|
|
272
|
-
if (sample && typeof sample === 'object') {
|
|
273
|
-
const keys = Object.keys(sample).map(k => k.toLowerCase());
|
|
274
|
-
for (const aliases of Object.values(FIELD_ROLES)) {
|
|
275
|
-
if (aliases.some(a => keys.includes(a)))
|
|
276
|
-
s += 2;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return applyCommonEndpointScoreAdjustments(req, s);
|
|
281
|
-
}
|
|
282
250
|
/** Check whether one recorded request is safe to treat as a write candidate. */
|
|
283
251
|
function isWriteCandidate(req) {
|
|
284
252
|
return ['POST', 'PUT', 'PATCH'].includes(req.method)
|
|
@@ -290,25 +258,19 @@ function isWriteCandidate(req) {
|
|
|
290
258
|
&& typeof req.responseBody === 'object'
|
|
291
259
|
&& !Array.isArray(req.responseBody);
|
|
292
260
|
}
|
|
293
|
-
/**
|
|
294
|
-
function scoreWriteRequest(req) {
|
|
295
|
-
return applyCommonEndpointScoreAdjustments(req, 6);
|
|
296
|
-
}
|
|
297
|
-
/** Analyze recorded requests into read and write candidates. */
|
|
261
|
+
/** Analyze recorded requests into read and write candidates, filtering out noise. */
|
|
298
262
|
export function analyzeRecordedRequests(requests) {
|
|
299
263
|
const candidates = [];
|
|
300
264
|
for (const req of requests) {
|
|
265
|
+
if (isNoiseUrl(req.url))
|
|
266
|
+
continue;
|
|
301
267
|
const arrayResult = findArrayPath(req.responseBody);
|
|
302
268
|
if (isWriteCandidate(req)) {
|
|
303
|
-
|
|
304
|
-
if (score > 0)
|
|
305
|
-
candidates.push({ kind: 'write', req, score, arrayResult: null });
|
|
269
|
+
candidates.push({ kind: 'write', req, arrayResult: null });
|
|
306
270
|
continue;
|
|
307
271
|
}
|
|
308
272
|
if (arrayResult) {
|
|
309
|
-
|
|
310
|
-
if (score > 0)
|
|
311
|
-
candidates.push({ kind: 'read', req, score, arrayResult });
|
|
273
|
+
candidates.push({ kind: 'read', req, arrayResult });
|
|
312
274
|
}
|
|
313
275
|
}
|
|
314
276
|
return { candidates };
|
|
@@ -465,9 +427,9 @@ export function generateRecordedCandidates(site, pageUrl, requests) {
|
|
|
465
427
|
const current = deduped.get(key);
|
|
466
428
|
deduped.set(key, current ? preferRecordedCandidate(current, candidate) : candidate);
|
|
467
429
|
}
|
|
430
|
+
// Sort reads by array item count (richer data first), then take top 5
|
|
468
431
|
const selected = [...deduped.values()]
|
|
469
|
-
.
|
|
470
|
-
.sort((a, b) => b.score - a.score)
|
|
432
|
+
.sort((a, b) => (b.arrayResult?.items.length ?? 0) - (a.arrayResult?.items.length ?? 0))
|
|
471
433
|
.slice(0, 5);
|
|
472
434
|
const usedNames = new Set();
|
|
473
435
|
return selected.map((candidate) => {
|
|
@@ -636,13 +598,13 @@ function analyzeAndWrite(site, pageUrl, requests, outDir) {
|
|
|
636
598
|
// Generate candidate YAMLs (top 5)
|
|
637
599
|
const candidates = [];
|
|
638
600
|
const usedNames = new Set();
|
|
639
|
-
console.log(chalk.bold('\n Captured endpoints
|
|
640
|
-
for (const entry of analysis.candidates.sort((a, b) => b.
|
|
601
|
+
console.log(chalk.bold('\n Captured endpoints:\n'));
|
|
602
|
+
for (const entry of analysis.candidates.sort((a, b) => (b.arrayResult?.items.length ?? 0) - (a.arrayResult?.items.length ?? 0)).slice(0, 8)) {
|
|
641
603
|
const itemCount = entry.arrayResult?.items.length ?? 0;
|
|
642
604
|
const strategy = entry.kind === 'write'
|
|
643
605
|
? 'cookie'
|
|
644
606
|
: inferStrategy(detectAuthFromContent(entry.req.url, entry.req.responseBody));
|
|
645
|
-
const marker = entry.
|
|
607
|
+
const marker = entry.kind === 'write' ? chalk.magenta('✎') : itemCount > 5 ? chalk.green('★') : chalk.dim('·');
|
|
646
608
|
console.log(` ${marker} ${chalk.white(urlToPattern(entry.req.url))}` +
|
|
647
609
|
chalk.dim(` [${strategy}]`) +
|
|
648
610
|
(entry.kind === 'write'
|
|
@@ -664,7 +626,7 @@ function analyzeAndWrite(site, pageUrl, requests, outDir) {
|
|
|
664
626
|
console.log(chalk.dim(` → ${filePath}`));
|
|
665
627
|
}
|
|
666
628
|
if (candidates.length === 0) {
|
|
667
|
-
console.log(chalk.yellow(' No
|
|
629
|
+
console.log(chalk.yellow(' No candidates found.'));
|
|
668
630
|
console.log(chalk.dim(' Tip: make sure you triggered JSON API calls (open lists, search, scroll).'));
|
|
669
631
|
}
|
|
670
632
|
return {
|
package/dist/src/synthesize.d.ts
CHANGED
|
@@ -17,7 +17,6 @@ export interface SynthesizeCapability {
|
|
|
17
17
|
name: string;
|
|
18
18
|
description: string;
|
|
19
19
|
strategy: string;
|
|
20
|
-
confidence?: number;
|
|
21
20
|
endpoint?: string;
|
|
22
21
|
itemPath?: string | null;
|
|
23
22
|
recommendedColumns?: string[];
|
|
@@ -70,7 +69,6 @@ export interface SynthesizeCandidateSummary {
|
|
|
70
69
|
name: string;
|
|
71
70
|
path: string;
|
|
72
71
|
strategy: string;
|
|
73
|
-
confidence?: number;
|
|
74
72
|
}
|
|
75
73
|
export interface SynthesizeResult {
|
|
76
74
|
site: string;
|
package/dist/src/synthesize.js
CHANGED
|
@@ -13,7 +13,6 @@ export function synthesizeFromExplore(target, opts = {}) {
|
|
|
13
13
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
14
14
|
const site = bundle.manifest.site;
|
|
15
15
|
const capabilities = (bundle.capabilities ?? [])
|
|
16
|
-
.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
17
16
|
.slice(0, opts.top ?? 3);
|
|
18
17
|
const candidates = [];
|
|
19
18
|
for (const cap of capabilities) {
|
|
@@ -23,7 +22,7 @@ export function synthesizeFromExplore(target, opts = {}) {
|
|
|
23
22
|
const candidate = buildCandidateYaml(site, bundle.manifest, cap, endpoint);
|
|
24
23
|
const filePath = path.join(targetDir, `${candidate.name}.yaml`);
|
|
25
24
|
fs.writeFileSync(filePath, yaml.dump(candidate.yaml, { sortKeys: false, lineWidth: 120 }));
|
|
26
|
-
candidates.push({ name: candidate.name, path: filePath, strategy: cap.strategy
|
|
25
|
+
candidates.push({ name: candidate.name, path: filePath, strategy: cap.strategy });
|
|
27
26
|
}
|
|
28
27
|
const index = { site, target_url: bundle.manifest.target_url, generated_from: exploreDir, candidate_count: candidates.length, candidates };
|
|
29
28
|
fs.writeFileSync(path.join(targetDir, 'candidates.json'), JSON.stringify(index, null, 2));
|
|
@@ -32,7 +31,7 @@ export function synthesizeFromExplore(target, opts = {}) {
|
|
|
32
31
|
export function renderSynthesizeSummary(result) {
|
|
33
32
|
const lines = ['opencli synthesize: OK', `Site: ${result.site}`, `Source: ${result.explore_dir}`, `Candidates: ${result.candidate_count}`];
|
|
34
33
|
for (const c of result.candidates ?? [])
|
|
35
|
-
lines.push(` • ${c.name} (${c.strategy}
|
|
34
|
+
lines.push(` • ${c.name} (${c.strategy}) → ${c.path}`);
|
|
36
35
|
return lines.join('\n');
|
|
37
36
|
}
|
|
38
37
|
export function resolveExploreDir(target) {
|
|
@@ -61,7 +60,12 @@ function chooseEndpoint(cap, endpoints) {
|
|
|
61
60
|
if (match)
|
|
62
61
|
return match;
|
|
63
62
|
}
|
|
64
|
-
|
|
63
|
+
// Fallback: prefer endpoint with most data (item count + detected fields)
|
|
64
|
+
return [...endpoints].sort((a, b) => {
|
|
65
|
+
const aKey = (a.itemCount ?? 0) * 10 + Object.keys(a.detectedFields ?? {}).length;
|
|
66
|
+
const bKey = (b.itemCount ?? 0) * 10 + Object.keys(b.detectedFields ?? {}).length;
|
|
67
|
+
return bKey - aKey;
|
|
68
|
+
})[0];
|
|
65
69
|
}
|
|
66
70
|
// ── URL templating ─────────────────────────────────────────────────────────
|
|
67
71
|
function buildTemplatedUrl(rawUrl, cap, _endpoint) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jackwener/opencli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.9",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"commander": "^14.0.3",
|
|
81
81
|
"js-yaml": "^4.1.0",
|
|
82
82
|
"turndown": "^7.2.2",
|
|
83
|
-
"undici": "^
|
|
83
|
+
"undici": "^8.0.2",
|
|
84
84
|
"ws": "^8.18.0"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
87
|
"@types/js-yaml": "^4.0.9",
|
|
88
|
-
"@types/node": "^
|
|
88
|
+
"@types/node": "^25.5.2",
|
|
89
89
|
"@types/turndown": "^5.0.6",
|
|
90
90
|
"@types/ws": "^8.5.13",
|
|
91
91
|
"tsx": "^4.19.3",
|