@sovovs/bycli 2.0.0 → 2.1.1
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/cli-manifest.json +169 -0
- package/clis/twitter/search.js +3 -3
- package/clis/weixin/_wechat/args.js +48 -0
- package/clis/weixin/_wechat/article-content.js +53 -0
- package/clis/weixin/_wechat/article-service.js +124 -0
- package/clis/weixin/_wechat/auth-session.js +142 -0
- package/clis/weixin/_wechat/fingerprint.js +443 -0
- package/clis/weixin/_wechat/fixtures/articles-auth-expired.json +3 -0
- package/clis/weixin/_wechat/fixtures/articles-page.json +4 -0
- package/clis/weixin/_wechat/fixtures/search-auth-expired.json +4 -0
- package/clis/weixin/_wechat/fixtures/search-success.json +7 -0
- package/clis/weixin/_wechat/markdown.js +29 -0
- package/clis/weixin/_wechat/redact.js +405 -0
- package/clis/weixin/_wechat/save-service.js +175 -0
- package/clis/weixin/_wechat/search-biz.js +102 -0
- package/clis/weixin/_wechat/wechat-api.js +133 -0
- package/clis/weixin/accounts.js +38 -0
- package/clis/weixin/articles.js +35 -0
- package/clis/weixin/download.js +5 -47
- package/clis/weixin/save-articles.js +175 -0
- package/dist/src/browser/cdp.js +3 -0
- package/dist/src/browser/daemon-client.d.ts +2 -0
- package/dist/src/browser/daemon-client.js +1 -1
- package/dist/src/browser/extension-capabilities.d.ts +13 -0
- package/dist/src/browser/extension-capabilities.js +22 -0
- package/dist/src/browser/extension-capabilities.test.d.ts +1 -0
- package/dist/src/browser/extension-version-metadata.test.d.ts +1 -0
- package/dist/src/browser/page.d.ts +1 -0
- package/dist/src/browser/page.js +20 -1
- package/dist/src/build-manifest.js +4 -2
- package/dist/src/capabilityRouting.d.ts +3 -2
- package/dist/src/capabilityRouting.js +10 -2
- package/dist/src/cli.js +1 -1
- package/dist/src/commanderAdapter.js +5 -5
- package/dist/src/daemon.js +16 -0
- package/dist/src/discovery.d.ts +5 -0
- package/dist/src/discovery.js +12 -4
- package/dist/src/discovery.test.d.ts +1 -0
- package/dist/src/download/article-download.d.ts +6 -0
- package/dist/src/download/article-download.js +78 -17
- package/dist/src/download/wechat-article.d.ts +8 -0
- package/dist/src/download/wechat-article.js +137 -0
- package/dist/src/download/wechat-article.test.d.ts +1 -0
- package/dist/src/execution.d.ts +5 -0
- package/dist/src/execution.js +269 -50
- package/dist/src/help.js +8 -8
- package/dist/src/manifest-schema.d.ts +9 -0
- package/dist/src/manifest-schema.js +162 -0
- package/dist/src/manifest-schema.test.d.ts +1 -0
- package/dist/src/manifest-types.d.ts +1 -1
- package/dist/src/observation/redaction.js +10 -4
- package/dist/src/recorder/highlevel/verify.d.ts +3 -0
- package/dist/src/recorder/highlevel/verify.js +4 -0
- package/dist/src/recorder/highlevel/verify.test.d.ts +1 -0
- package/dist/src/recorder/runner/runner-port.js +1 -0
- package/dist/src/recorder/runner/verify-runner-main.d.ts +23 -7
- package/dist/src/recorder/runner/verify-runner-main.js +92 -19
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry-api.types.test.d.ts +1 -0
- package/dist/src/registry-transaction.d.ts +42 -0
- package/dist/src/registry-transaction.js +194 -0
- package/dist/src/registry-transaction.test.d.ts +1 -0
- package/dist/src/registry.d.ts +58 -16
- package/dist/src/registry.js +131 -15
- package/dist/src/serialization.d.ts +1 -1
- package/dist/src/serialization.js +3 -3
- package/dist/src/types.d.ts +2 -0
- package/dist/src/weixin-built-in-docs.test.d.ts +1 -0
- package/package.json +7 -3
- package/scripts/check-package-install.mjs +71 -0
- package/scripts/recorder.sh +0 -186
package/dist/src/registry.d.ts
CHANGED
|
@@ -20,7 +20,11 @@ export interface Arg {
|
|
|
20
20
|
choices?: string[];
|
|
21
21
|
}
|
|
22
22
|
export type CommandArgs = Record<string, any>;
|
|
23
|
+
export type BrowserRequirementResolver = (args: CommandArgs) => boolean;
|
|
24
|
+
export type BrowserDeclaration = boolean | BrowserRequirementResolver;
|
|
25
|
+
export type NormalizedBrowserRequirement = boolean | 'conditional';
|
|
23
26
|
export type BrowserCommandFunc = (page: IPage, kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
|
|
27
|
+
export type ConditionalBrowserCommandFunc = (page: IPage | null, kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
|
|
24
28
|
export type NonBrowserCommandFunc = (kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
|
|
25
29
|
export type CommandAccess = 'read' | 'write';
|
|
26
30
|
export type SiteSessionMode = 'ephemeral' | 'persistent';
|
|
@@ -28,8 +32,9 @@ export type SiteSessionMode = 'ephemeral' | 'persistent';
|
|
|
28
32
|
* 所有已注册 adapter command 的共享元数据和运行选项。
|
|
29
33
|
*
|
|
30
34
|
* 这里故意不包含 `browser` 和 `func` 这类执行形态字段,因为浏览器命令和
|
|
31
|
-
*
|
|
32
|
-
* 会在 normalize
|
|
35
|
+
* 非浏览器命令、条件浏览器命令的执行签名不同。`BrowserCliCommand`、
|
|
36
|
+
* `NonBrowserCliCommand` 和 `ConditionalBrowserCliCommand` 会在 normalize 之后
|
|
37
|
+
* 扩展这个共同底座,形成最终可执行的命令类型。
|
|
33
38
|
*/
|
|
34
39
|
interface BaseCliCommand {
|
|
35
40
|
/** 站点或命名空间名称,对应命令中的 `<site>`,例如 `devto`、`brave`。 */
|
|
@@ -81,8 +86,8 @@ interface BaseCliCommand {
|
|
|
81
86
|
defaultFormat?: 'table' | 'plain' | 'json' | 'yaml' | 'yml' | 'md' | 'markdown' | 'csv';
|
|
82
87
|
}
|
|
83
88
|
export interface BrowserCliCommand extends BaseCliCommand {
|
|
84
|
-
/** Browser commands receive an IPage.
|
|
85
|
-
browser
|
|
89
|
+
/** Browser commands receive an IPage. */
|
|
90
|
+
browser: true;
|
|
86
91
|
func?: BrowserCommandFunc;
|
|
87
92
|
}
|
|
88
93
|
export interface NonBrowserCliCommand extends BaseCliCommand {
|
|
@@ -90,25 +95,50 @@ export interface NonBrowserCliCommand extends BaseCliCommand {
|
|
|
90
95
|
browser: false;
|
|
91
96
|
func?: NonBrowserCommandFunc;
|
|
92
97
|
}
|
|
93
|
-
export
|
|
98
|
+
export interface ConditionalBrowserCliCommand extends BaseCliCommand {
|
|
99
|
+
/** Browser use is resolved from the final command arguments at execution time. */
|
|
100
|
+
browser: 'conditional';
|
|
101
|
+
requiresBrowser: BrowserRequirementResolver;
|
|
102
|
+
func?: ConditionalBrowserCommandFunc;
|
|
103
|
+
}
|
|
104
|
+
export type CliCommand = BrowserCliCommand | NonBrowserCliCommand | ConditionalBrowserCliCommand;
|
|
94
105
|
/**
|
|
95
106
|
* `cli()` 注册 adapter 时使用的内部预归一化命令形态。
|
|
96
107
|
*
|
|
97
108
|
* adapter 作者传入的是 `CliOptions`,它的 TypeScript union 会保证公开调用点足够精确。
|
|
98
|
-
* registry
|
|
99
|
-
* 根据 `strategy` 推导 `browser`、`navigateBefore`
|
|
100
|
-
* `CliCommand`。
|
|
109
|
+
* registry 内部会先把这些选项复制成对应的预归一化分支,再交给
|
|
110
|
+
* `normalizeCommand()` 根据 `strategy` 推导 `browser`、`navigateBefore` 等运行时意图,
|
|
111
|
+
* 最后存成具体的 `CliCommand`。
|
|
101
112
|
*/
|
|
102
|
-
type
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
type RawCliCommandBase = Omit<BaseCliCommand, 'strategy'>;
|
|
114
|
+
type RawBrowserCliCommand = RawCliCommandBase & {
|
|
115
|
+
func?: BrowserCommandFunc;
|
|
116
|
+
} & ({
|
|
117
|
+
browser: true;
|
|
118
|
+
strategy?: Strategy;
|
|
119
|
+
} | {
|
|
120
|
+
browser?: true;
|
|
121
|
+
strategy?: BrowserStrategy;
|
|
122
|
+
});
|
|
123
|
+
type RawNonBrowserCliCommand = RawCliCommandBase & {
|
|
124
|
+
func?: NonBrowserCommandFunc;
|
|
125
|
+
} & ({
|
|
126
|
+
browser: false;
|
|
127
|
+
strategy?: Strategy;
|
|
128
|
+
} | {
|
|
129
|
+
browser?: false;
|
|
130
|
+
strategy: Strategy.PUBLIC | Strategy.LOCAL;
|
|
131
|
+
});
|
|
132
|
+
type RawConditionalBrowserCliCommand = RawCliCommandBase & {
|
|
133
|
+
browser: BrowserRequirementResolver;
|
|
134
|
+
strategy?: Strategy;
|
|
135
|
+
func?: ConditionalBrowserCommandFunc;
|
|
107
136
|
};
|
|
108
137
|
/** Internal extension for lazy-loaded TS modules (not exposed in public API) */
|
|
109
138
|
export type InternalCliCommand = CliCommand & {
|
|
110
139
|
_lazy?: boolean;
|
|
111
140
|
_modulePath?: string;
|
|
141
|
+
_hydrateBeforeBrowserRouting?: boolean;
|
|
112
142
|
};
|
|
113
143
|
type RequiredCliOptions = {
|
|
114
144
|
site: string;
|
|
@@ -131,13 +161,25 @@ type NonBrowserCliOptions = Partial<Omit<NonBrowserCliCommand, 'args' | 'descrip
|
|
|
131
161
|
strategy: Strategy.PUBLIC | Strategy.LOCAL;
|
|
132
162
|
browser?: false;
|
|
133
163
|
});
|
|
134
|
-
|
|
164
|
+
type ConditionalBrowserCliOptions = Partial<Omit<ConditionalBrowserCliCommand, 'args' | 'description' | 'browser' | 'requiresBrowser'>> & RequiredCliOptions & {
|
|
165
|
+
browser: BrowserRequirementResolver;
|
|
166
|
+
};
|
|
167
|
+
export type CliOptions = BrowserCliOptions | NonBrowserCliOptions | ConditionalBrowserCliOptions;
|
|
135
168
|
declare global {
|
|
136
169
|
var __bycli_registry__: Map<string, CliCommand> | undefined;
|
|
137
170
|
}
|
|
138
|
-
export declare function cli(opts:
|
|
171
|
+
export declare function cli(opts: ConditionalBrowserCliOptions): ConditionalBrowserCliCommand;
|
|
172
|
+
export declare function cli(opts: NonBrowserCliOptions): NonBrowserCliCommand;
|
|
173
|
+
export declare function cli(opts: BrowserCliOptions): BrowserCliCommand;
|
|
139
174
|
export declare function getRegistry(): Map<string, CliCommand>;
|
|
140
175
|
export declare function fullName(cmd: Pick<BaseCliCommand, 'site' | 'name'>): string;
|
|
141
176
|
export declare function strategyLabel(cmd: CliCommand): string;
|
|
142
|
-
|
|
177
|
+
/** Whether a command may use browser-backed execution for some invocation. */
|
|
178
|
+
export declare function hasBrowserCapability(cmd: CliCommand): boolean;
|
|
179
|
+
/** Stable human-readable label for the normalized browser requirement. */
|
|
180
|
+
export declare function browserRequirementLabel(cmd: CliCommand): 'yes' | 'no' | 'conditional';
|
|
181
|
+
export declare function registerCommand(cmd: RawConditionalBrowserCliCommand): void;
|
|
182
|
+
export declare function registerCommand(cmd: RawNonBrowserCliCommand): void;
|
|
183
|
+
export declare function registerCommand(cmd: RawBrowserCliCommand): void;
|
|
184
|
+
export declare function registerCommand(cmd: CliCommand): void;
|
|
143
185
|
export {};
|
package/dist/src/registry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core registry: Strategy enum, Arg/CliCommand interfaces, cli() registration.
|
|
3
3
|
*/
|
|
4
|
+
import { pruneRegistryMutationKey, recordRegistryMutation, registryMutationKeys, withRegistryMutationGroup, } from './registry-transaction.js';
|
|
4
5
|
export var Strategy;
|
|
5
6
|
(function (Strategy) {
|
|
6
7
|
Strategy["PUBLIC"] = "public";
|
|
@@ -9,9 +10,77 @@ export var Strategy;
|
|
|
9
10
|
Strategy["INTERCEPT"] = "intercept";
|
|
10
11
|
Strategy["UI"] = "ui";
|
|
11
12
|
})(Strategy || (Strategy = {}));
|
|
12
|
-
const
|
|
13
|
+
const TRACKED_REGISTRY_MARKER = Symbol.for('@sovovs/bycli/tracked-registry');
|
|
14
|
+
function isTrackedRegistry(registry) {
|
|
15
|
+
return registry !== undefined
|
|
16
|
+
&& registry[TRACKED_REGISTRY_MARKER] === true;
|
|
17
|
+
}
|
|
18
|
+
function instrumentRegistry(registry) {
|
|
19
|
+
if (isTrackedRegistry(registry))
|
|
20
|
+
return registry;
|
|
21
|
+
Object.defineProperties(registry, {
|
|
22
|
+
set: {
|
|
23
|
+
value(key, value) {
|
|
24
|
+
const before = { present: this.has(key), value: this.get(key) };
|
|
25
|
+
recordRegistryMutation(key, before, { present: true, value });
|
|
26
|
+
return Map.prototype.set.call(this, key, value);
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
delete: {
|
|
30
|
+
value(key) {
|
|
31
|
+
const before = { present: this.has(key), value: this.get(key) };
|
|
32
|
+
recordRegistryMutation(key, before, { present: false, value: undefined });
|
|
33
|
+
const deleted = Map.prototype.delete.call(this, key);
|
|
34
|
+
pruneRegistryMutationKey(key, this);
|
|
35
|
+
return deleted;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
clear: {
|
|
39
|
+
value() {
|
|
40
|
+
const keys = new Set([...this.keys(), ...registryMutationKeys()]);
|
|
41
|
+
if (keys.size === 0)
|
|
42
|
+
return;
|
|
43
|
+
withRegistryMutationGroup(() => {
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const present = this.has(key);
|
|
46
|
+
recordRegistryMutation(key, { present, value: present ? this.get(key) : undefined }, { present: false, value: undefined });
|
|
47
|
+
}
|
|
48
|
+
Map.prototype.clear.call(this);
|
|
49
|
+
for (const key of keys)
|
|
50
|
+
pruneRegistryMutationKey(key, this);
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
[TRACKED_REGISTRY_MARKER]: { value: true },
|
|
55
|
+
});
|
|
56
|
+
return registry;
|
|
57
|
+
}
|
|
58
|
+
const existingRegistry = globalThis.__bycli_registry__ ?? new Map();
|
|
59
|
+
const _registry = instrumentRegistry(existingRegistry);
|
|
60
|
+
globalThis.__bycli_registry__ = _registry;
|
|
13
61
|
export function cli(opts) {
|
|
14
|
-
const
|
|
62
|
+
const base = rawCommandBase(opts);
|
|
63
|
+
let cmd;
|
|
64
|
+
if (typeof opts.browser === 'function') {
|
|
65
|
+
cmd = { ...base, strategy: opts.strategy, browser: opts.browser, func: opts.func };
|
|
66
|
+
}
|
|
67
|
+
else if (opts.browser === false) {
|
|
68
|
+
cmd = { ...base, strategy: opts.strategy, browser: false, func: opts.func };
|
|
69
|
+
}
|
|
70
|
+
else if (opts.browser === true) {
|
|
71
|
+
cmd = { ...base, strategy: opts.strategy, browser: true, func: opts.func };
|
|
72
|
+
}
|
|
73
|
+
else if (isImplicitNonBrowserOptions(opts)) {
|
|
74
|
+
cmd = { ...base, strategy: opts.strategy, browser: opts.browser, func: opts.func };
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
cmd = { ...base, strategy: opts.strategy, browser: opts.browser, func: opts.func };
|
|
78
|
+
}
|
|
79
|
+
registerCommandInput(cmd);
|
|
80
|
+
return _registry.get(fullName(cmd));
|
|
81
|
+
}
|
|
82
|
+
function rawCommandBase(opts) {
|
|
83
|
+
return {
|
|
15
84
|
site: opts.site,
|
|
16
85
|
name: opts.name,
|
|
17
86
|
aliases: opts.aliases,
|
|
@@ -19,11 +88,8 @@ export function cli(opts) {
|
|
|
19
88
|
access: opts.access,
|
|
20
89
|
example: opts.example,
|
|
21
90
|
domain: opts.domain,
|
|
22
|
-
strategy: opts.strategy,
|
|
23
|
-
browser: opts.browser,
|
|
24
91
|
args: opts.args ?? [],
|
|
25
92
|
columns: opts.columns,
|
|
26
|
-
func: opts.func,
|
|
27
93
|
pipeline: opts.pipeline,
|
|
28
94
|
footerExtra: opts.footerExtra,
|
|
29
95
|
validateArgs: opts.validateArgs,
|
|
@@ -31,8 +97,10 @@ export function cli(opts) {
|
|
|
31
97
|
siteSession: opts.siteSession,
|
|
32
98
|
defaultFormat: opts.defaultFormat,
|
|
33
99
|
};
|
|
34
|
-
|
|
35
|
-
|
|
100
|
+
}
|
|
101
|
+
function isImplicitNonBrowserOptions(opts) {
|
|
102
|
+
return opts.browser === undefined
|
|
103
|
+
&& (opts.strategy === Strategy.PUBLIC || opts.strategy === Strategy.LOCAL);
|
|
36
104
|
}
|
|
37
105
|
export function getRegistry() {
|
|
38
106
|
return _registry;
|
|
@@ -43,6 +111,14 @@ export function fullName(cmd) {
|
|
|
43
111
|
export function strategyLabel(cmd) {
|
|
44
112
|
return cmd.strategy ?? Strategy.PUBLIC;
|
|
45
113
|
}
|
|
114
|
+
/** Whether a command may use browser-backed execution for some invocation. */
|
|
115
|
+
export function hasBrowserCapability(cmd) {
|
|
116
|
+
return cmd.browser !== false;
|
|
117
|
+
}
|
|
118
|
+
/** Stable human-readable label for the normalized browser requirement. */
|
|
119
|
+
export function browserRequirementLabel(cmd) {
|
|
120
|
+
return cmd.browser === 'conditional' ? 'conditional' : cmd.browser ? 'yes' : 'no';
|
|
121
|
+
}
|
|
46
122
|
/**
|
|
47
123
|
* Normalize a command's runtime fields. This is the single place where
|
|
48
124
|
* `strategy` is decoded into the concrete fields that the execution path
|
|
@@ -57,10 +133,8 @@ export function strategyLabel(cmd) {
|
|
|
57
133
|
* 2. Derived from strategy + domain (the defaults below)
|
|
58
134
|
*/
|
|
59
135
|
function normalizeCommand(cmd) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const strategy = cmd.strategy ?? (cmd.browser === false ? Strategy.PUBLIC : Strategy.COOKIE);
|
|
63
|
-
const browser = cmd.browser ?? (strategy !== Strategy.PUBLIC && strategy !== Strategy.LOCAL);
|
|
136
|
+
const declaredBrowser = cmd.browser;
|
|
137
|
+
const strategy = cmd.strategy ?? (declaredBrowser === false ? Strategy.PUBLIC : Strategy.COOKIE);
|
|
64
138
|
let navigateBefore = cmd.navigateBefore;
|
|
65
139
|
if (navigateBefore === undefined) {
|
|
66
140
|
if (strategy === Strategy.COOKIE && cmd.domain) {
|
|
@@ -73,9 +147,34 @@ function normalizeCommand(cmd) {
|
|
|
73
147
|
navigateBefore = true;
|
|
74
148
|
}
|
|
75
149
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
150
|
+
if (typeof cmd.browser === 'function') {
|
|
151
|
+
const normalized = {
|
|
152
|
+
...cmd,
|
|
153
|
+
strategy,
|
|
154
|
+
browser: 'conditional',
|
|
155
|
+
requiresBrowser: cmd.browser,
|
|
156
|
+
navigateBefore,
|
|
157
|
+
};
|
|
158
|
+
return normalized;
|
|
159
|
+
}
|
|
160
|
+
if (cmd.browser === false) {
|
|
161
|
+
const normalized = { ...cmd, strategy, browser: false, navigateBefore };
|
|
162
|
+
return normalized;
|
|
163
|
+
}
|
|
164
|
+
if (cmd.browser === true) {
|
|
165
|
+
const normalized = { ...cmd, strategy, browser: true, navigateBefore };
|
|
166
|
+
return normalized;
|
|
167
|
+
}
|
|
168
|
+
if (isImplicitNonBrowserCommand(cmd)) {
|
|
169
|
+
const normalized = { ...cmd, strategy, browser: false, navigateBefore };
|
|
170
|
+
return normalized;
|
|
171
|
+
}
|
|
172
|
+
const normalized = { ...cmd, strategy, browser: true, navigateBefore };
|
|
173
|
+
return normalized;
|
|
174
|
+
}
|
|
175
|
+
function isImplicitNonBrowserCommand(cmd) {
|
|
176
|
+
return cmd.browser === undefined
|
|
177
|
+
&& (cmd.strategy === Strategy.PUBLIC || cmd.strategy === Strategy.LOCAL);
|
|
79
178
|
}
|
|
80
179
|
function assertCommandAccess(cmd) {
|
|
81
180
|
if (cmd.access === 'read' || cmd.access === 'write')
|
|
@@ -92,7 +191,24 @@ function assertSiteSession(cmd) {
|
|
|
92
191
|
}
|
|
93
192
|
}
|
|
94
193
|
export function registerCommand(cmd) {
|
|
95
|
-
|
|
194
|
+
registerCommandInput(cmd);
|
|
195
|
+
}
|
|
196
|
+
function registerCommandInput(cmd) {
|
|
197
|
+
withRegistryMutationGroup(() => {
|
|
198
|
+
assertCommandAccess(cmd);
|
|
199
|
+
assertSiteSession(cmd);
|
|
200
|
+
if (cmd.browser === 'conditional') {
|
|
201
|
+
if (typeof cmd.requiresBrowser !== 'function') {
|
|
202
|
+
const key = `${cmd.site}/${cmd.name}`;
|
|
203
|
+
throw new Error(`Command ${key} requiresBrowser must be a function`);
|
|
204
|
+
}
|
|
205
|
+
insertNormalizedCommand(cmd);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
insertNormalizedCommand(normalizeCommand(cmd));
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function insertNormalizedCommand(normalized) {
|
|
96
212
|
const canonicalKey = fullName(normalized);
|
|
97
213
|
const existing = _registry.get(canonicalKey);
|
|
98
214
|
if (existing?.aliases) {
|
|
@@ -26,7 +26,7 @@ export declare function serializeCommand(cmd: CliCommand): {
|
|
|
26
26
|
description: string;
|
|
27
27
|
access: import("./registry.js").CommandAccess;
|
|
28
28
|
strategy: string;
|
|
29
|
-
browser: boolean;
|
|
29
|
+
browser: boolean | "conditional";
|
|
30
30
|
args: SerializedArg[];
|
|
31
31
|
columns: string[];
|
|
32
32
|
domain: string | null;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Used by the `list` command, Commander --help, and build-manifest.
|
|
5
5
|
* Separated from registry.ts to keep the registry focused on types + registration.
|
|
6
6
|
*/
|
|
7
|
-
import { fullName, strategyLabel } from './registry.js';
|
|
7
|
+
import { browserRequirementLabel, fullName, strategyLabel } from './registry.js';
|
|
8
8
|
/** Stable arg schema — every field is always present (no sparse objects). */
|
|
9
9
|
export function serializeArg(a) {
|
|
10
10
|
return {
|
|
@@ -28,7 +28,7 @@ export function serializeCommand(cmd) {
|
|
|
28
28
|
description: cmd.description,
|
|
29
29
|
access: cmd.access,
|
|
30
30
|
strategy: strategyLabel(cmd),
|
|
31
|
-
browser:
|
|
31
|
+
browser: cmd.browser,
|
|
32
32
|
args: cmd.args.map(serializeArg),
|
|
33
33
|
columns: cmd.columns ?? [],
|
|
34
34
|
domain: cmd.domain ?? null,
|
|
@@ -87,7 +87,7 @@ export function formatRegistryHelpText(cmd) {
|
|
|
87
87
|
}
|
|
88
88
|
const meta = [];
|
|
89
89
|
meta.push(`Access: ${cmd.access}`);
|
|
90
|
-
meta.push(`Browser: ${cmd
|
|
90
|
+
meta.push(`Browser: ${browserRequirementLabel(cmd)}`);
|
|
91
91
|
if (cmd.domain)
|
|
92
92
|
meta.push(`Domain: ${cmd.domain}`);
|
|
93
93
|
if (cmd.defaultFormat)
|
package/dist/src/types.d.ts
CHANGED
|
@@ -207,6 +207,8 @@ export interface IPage {
|
|
|
207
207
|
* Useful for rich editors that ignore synthetic DOM value/text mutations.
|
|
208
208
|
*/
|
|
209
209
|
insertText?(text: string): Promise<void>;
|
|
210
|
+
/** Focus the browser window containing the active page for interactive login. */
|
|
211
|
+
focusWindow?(): Promise<void>;
|
|
210
212
|
closeWindow?(): Promise<void>;
|
|
211
213
|
/** Returns the current page URL, or null if unavailable. */
|
|
212
214
|
getCurrentUrl?(): Promise<string | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sovovs/bycli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"description": "Make any website or Electron App your CLI. AI-powered.",
|
|
8
8
|
"engines": {
|
|
9
|
-
"node": ">=20.
|
|
9
|
+
"node": ">=20.6.0"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"workspaces": [
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"./download/article-download": "./dist/src/download/article-download.js",
|
|
33
33
|
"./download/media-download": "./dist/src/download/media-download.js",
|
|
34
34
|
"./download/progress": "./dist/src/download/progress.js",
|
|
35
|
+
"./download/wechat-article": "./dist/src/download/wechat-article.js",
|
|
35
36
|
"./pipeline": "./dist/src/pipeline/index.js"
|
|
36
37
|
},
|
|
37
38
|
"files": [
|
|
@@ -66,6 +67,7 @@
|
|
|
66
67
|
"advise:listing-id-pairing": "node scripts/check-listing-id-pairing.mjs",
|
|
67
68
|
"check:silent-column-drop": "node scripts/check-silent-column-drop.mjs",
|
|
68
69
|
"check:typed-error-lint": "node scripts/check-typed-error-lint.mjs",
|
|
70
|
+
"check:package-install": "node scripts/check-package-install.mjs",
|
|
69
71
|
"docs:dev": "vitepress dev docs",
|
|
70
72
|
"docs:build": "vitepress build docs",
|
|
71
73
|
"docs:preview": "vitepress preview docs"
|
|
@@ -87,17 +89,19 @@
|
|
|
87
89
|
},
|
|
88
90
|
"dependencies": {
|
|
89
91
|
"@mozilla/readability": "^0.6.0",
|
|
92
|
+
"@sovovs/bycli-recorder-core": "^0.1.0",
|
|
90
93
|
"cli-table3": "^0.6.5",
|
|
91
94
|
"commander": "^14.0.3",
|
|
92
95
|
"js-yaml": "^4.1.0",
|
|
96
|
+
"parse5": "^7.3.0",
|
|
93
97
|
"turndown": "^7.2.2",
|
|
94
98
|
"turndown-plugin-gfm": "^1.0.2",
|
|
95
99
|
"undici": "^6.25.0",
|
|
96
100
|
"ws": "^8.18.0"
|
|
97
101
|
},
|
|
98
102
|
"devDependencies": {
|
|
99
|
-
"@types/jsdom": "^27.0.0",
|
|
100
103
|
"@types/js-yaml": "^4.0.9",
|
|
104
|
+
"@types/jsdom": "^27.0.0",
|
|
101
105
|
"@types/node": "^25.5.2",
|
|
102
106
|
"@types/turndown": "^5.0.6",
|
|
103
107
|
"@types/ws": "^8.5.13",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const temp = mkdtempSync(join(tmpdir(), 'bycli-package-install-'));
|
|
10
|
+
const artifacts = join(temp, 'artifacts');
|
|
11
|
+
const project = join(temp, 'project');
|
|
12
|
+
const mainStage = join(temp, 'main-package');
|
|
13
|
+
|
|
14
|
+
function run(command, args, cwd = root) {
|
|
15
|
+
return execFileSync(command, args, {
|
|
16
|
+
cwd,
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function pack(cwd) {
|
|
23
|
+
const result = JSON.parse(run('npm', [
|
|
24
|
+
'pack', '--json', '--ignore-scripts', '--pack-destination', artifacts,
|
|
25
|
+
], cwd));
|
|
26
|
+
assert.equal(result.length, 1);
|
|
27
|
+
return {
|
|
28
|
+
tarball: join(artifacts, result[0].filename),
|
|
29
|
+
files: new Set(result[0].files.map(({ path }) => path)),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
mkdirSync(artifacts, { recursive: true });
|
|
35
|
+
mkdirSync(project, { recursive: true });
|
|
36
|
+
mkdirSync(mainStage, { recursive: true });
|
|
37
|
+
for (const path of [
|
|
38
|
+
'package.json', 'dist', 'clis', 'cli-manifest.json', 'scripts',
|
|
39
|
+
'README.md', 'README.zh-CN.md', 'LICENSE', 'NOTICE',
|
|
40
|
+
]) {
|
|
41
|
+
cpSync(join(root, path), join(mainStage, path), { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const core = pack(join(root, 'packages/recorder-core'));
|
|
45
|
+
const main = pack(mainStage);
|
|
46
|
+
|
|
47
|
+
for (const file of ['dist/index.js', 'dist/index.d.ts', 'README.md', 'LICENSE']) {
|
|
48
|
+
assert(core.files.has(file), `recorder-core tarball is missing ${file}`);
|
|
49
|
+
}
|
|
50
|
+
assert(![...core.files].some((file) => file.startsWith('src/')), 'recorder-core tarball includes src/');
|
|
51
|
+
|
|
52
|
+
writeFileSync(join(project, 'package.json'), JSON.stringify({ private: true, type: 'module' }));
|
|
53
|
+
run('npm', [
|
|
54
|
+
'install', '--ignore-scripts', '--no-audit', '--no-fund', core.tarball, main.tarball,
|
|
55
|
+
], project);
|
|
56
|
+
|
|
57
|
+
const mainManifest = JSON.parse(readFileSync(join(
|
|
58
|
+
project, 'node_modules/@sovovs/bycli/package.json',
|
|
59
|
+
), 'utf8'));
|
|
60
|
+
assert.equal(mainManifest.dependencies?.['@sovovs/bycli-recorder-core'], '^0.1.0');
|
|
61
|
+
|
|
62
|
+
const coreDirectory = join(project, 'node_modules/@sovovs/bycli-recorder-core');
|
|
63
|
+
const recorderEntry = join(
|
|
64
|
+
project, 'node_modules/@sovovs/bycli/dist/src/browser/analyze.js',
|
|
65
|
+
);
|
|
66
|
+
await import(pathToFileURL(recorderEntry).href);
|
|
67
|
+
await import(pathToFileURL(join(coreDirectory, 'dist/index.js')).href);
|
|
68
|
+
console.log('package install smoke test passed');
|
|
69
|
+
} finally {
|
|
70
|
+
rmSync(temp, { recursive: true, force: true });
|
|
71
|
+
}
|