@yizhuan-cli/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +37 -0
  2. package/package.json +31 -0
  3. package/src/index.js +166 -0
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @yizhuan-cli/cli
2
+
3
+ 易撰命令行工具,用于通过 API Key 查询易撰真实数据。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g @yizhuan-cli/cli
9
+ ```
10
+
11
+ 如果发布在私有 npm registry,请加上对应 registry:
12
+
13
+ ```bash
14
+ npm install -g @yizhuan-cli/cli --registry=https://your-private-registry.example
15
+ ```
16
+
17
+ ## 配置
18
+
19
+ 创建 `~/.config/yizhuan/config.json`:
20
+
21
+ ```json
22
+ {
23
+ "apiBaseUrl": "https://api.yizhuan5.com",
24
+ "apiKey": "yz_cli_xxx"
25
+ }
26
+ ```
27
+
28
+ ## 使用
29
+
30
+ ```bash
31
+ yizhuan --help
32
+ yizhuan --version
33
+ yizhuan config path
34
+ yizhuan config show
35
+ yizhuan execute --ability membership_info
36
+ yizhuan execute --ability content_search --query "百家号昨天新增了哪些作品"
37
+ ```
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@yizhuan-cli/cli",
3
+ "version": "0.1.0",
4
+ "description": "易撰命令行工具,用于通过 API Key 查询易撰真实数据。",
5
+ "private": false,
6
+ "type": "module",
7
+ "bin": {
8
+ "yizhuan": "./src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "start": "node src/index.js",
16
+ "pack:dry-run": "npm pack --dry-run",
17
+ "smoke": "node src/index.js --help && node src/index.js --version"
18
+ },
19
+ "engines": {
20
+ "node": ">=22"
21
+ },
22
+ "keywords": [
23
+ "yizhuan",
24
+ "cli"
25
+ ],
26
+ "author": "zoy",
27
+ "license": "UNLICENSED",
28
+ "publishConfig": {
29
+ "access": "restricted"
30
+ }
31
+ }
package/src/index.js ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const CLI_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
8
+ const PACKAGE_PATH = path.join(CLI_DIR, 'package.json')
9
+ const CONFIG_PATH = path.join(os.homedir(), '.config', 'yizhuan', 'config.json')
10
+ const ABILITIES = new Set([
11
+ 'author_query',
12
+ 'statistics',
13
+ 'content_search',
14
+ 'hot_topic_analysis',
15
+ 'work_query',
16
+ 'membership_info',
17
+ 'favorites_query'
18
+ ])
19
+
20
+ function printHelp() {
21
+ console.log(`易撰 CLI
22
+
23
+ Usage:
24
+ yizhuan --help
25
+ yizhuan --version
26
+ yizhuan execute --ability <ability> [--query <text>] [--params <json>]
27
+ yizhuan config path
28
+ yizhuan config show
29
+
30
+ Abilities:
31
+ author_query 作者查询
32
+ statistics 数据统计
33
+ content_search 内容检索
34
+ hot_topic_analysis 热点分析
35
+ work_query 作品查询
36
+ membership_info 会员信息
37
+ favorites_query 收藏
38
+
39
+ Config:
40
+ ${CONFIG_PATH}
41
+ `)
42
+ }
43
+
44
+ function readPackageVersion() {
45
+ try {
46
+ const packageJson = JSON.parse(fs.readFileSync(PACKAGE_PATH, 'utf8'))
47
+ return packageJson.version || '0.0.0'
48
+ } catch {
49
+ return '0.0.0'
50
+ }
51
+ }
52
+
53
+ function parseArgs(argv) {
54
+ const args = { _: [] }
55
+ for (let index = 0; index < argv.length; index += 1) {
56
+ const value = argv[index]
57
+ if (value.startsWith('--')) {
58
+ const key = value.slice(2)
59
+ const next = argv[index + 1]
60
+ if (!next || next.startsWith('--')) {
61
+ args[key] = true
62
+ } else {
63
+ args[key] = next
64
+ index += 1
65
+ }
66
+ } else {
67
+ args._.push(value)
68
+ }
69
+ }
70
+ return args
71
+ }
72
+
73
+ function readConfig() {
74
+ if (!fs.existsSync(CONFIG_PATH)) {
75
+ throw new Error(`未找到配置文件:${CONFIG_PATH}`)
76
+ }
77
+
78
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'))
79
+ if (!config.apiBaseUrl || !config.apiKey) {
80
+ throw new Error('配置文件必须包含 apiBaseUrl 和 apiKey')
81
+ }
82
+
83
+ return {
84
+ apiBaseUrl: String(config.apiBaseUrl).replace(/\/+$/, ''),
85
+ apiKey: String(config.apiKey)
86
+ }
87
+ }
88
+
89
+ function parseParams(value) {
90
+ if (!value) return undefined
91
+ try {
92
+ return JSON.parse(value)
93
+ } catch {
94
+ throw new Error('--params 必须是 JSON,例如:--params "{\\"page\\":1}"')
95
+ }
96
+ }
97
+
98
+ async function execute(args) {
99
+ const ability = args.ability
100
+ if (!ABILITIES.has(ability)) {
101
+ throw new Error(`未知 ability:${ability || ''},请执行 yizhuan --help 查看可用能力`)
102
+ }
103
+
104
+ const config = readConfig()
105
+ const response = await fetch(`${config.apiBaseUrl}/api/cli.execute`, {
106
+ method: 'POST',
107
+ headers: {
108
+ authorization: config.apiKey,
109
+ 'content-type': 'application/json',
110
+ 'x-device-type': 'pc'
111
+ },
112
+ body: JSON.stringify({
113
+ json: {
114
+ ability,
115
+ query: args.query,
116
+ params: parseParams(args.params)
117
+ }
118
+ })
119
+ })
120
+ const text = await response.text()
121
+
122
+ if (!response.ok) {
123
+ throw new Error(`请求失败 HTTP ${response.status}:${text}`)
124
+ }
125
+
126
+ const payload = JSON.parse(text)
127
+ console.log(JSON.stringify(payload.result?.data?.json ?? payload, null, 2))
128
+ }
129
+
130
+ async function main() {
131
+ const args = parseArgs(process.argv.slice(2))
132
+ const command = args._[0]
133
+
134
+ if (args.version || command === '--version' || command === '-v') {
135
+ console.log(readPackageVersion())
136
+ return
137
+ }
138
+
139
+ if (!command || command === '--help' || command === '-h') {
140
+ printHelp()
141
+ return
142
+ }
143
+
144
+ if (command === 'config' && args._[1] === 'path') {
145
+ console.log(CONFIG_PATH)
146
+ return
147
+ }
148
+
149
+ if (command === 'config' && args._[1] === 'show') {
150
+ const config = readConfig()
151
+ console.log(JSON.stringify({ ...config, apiKey: `${config.apiKey.slice(0, 18)}...` }, null, 2))
152
+ return
153
+ }
154
+
155
+ if (command === 'execute') {
156
+ await execute(args)
157
+ return
158
+ }
159
+
160
+ throw new Error(`未知命令:${command}`)
161
+ }
162
+
163
+ main().catch((error) => {
164
+ console.error(error.message)
165
+ process.exit(1)
166
+ })