@besile/scm-cli 2026.3.29

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.zh.md ADDED
@@ -0,0 +1,451 @@
1
+ # SCM CLI
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D14-blue.svg)](https://nodejs.org/)
5
+
6
+ 供应链管理(SCM)系统 CLI 工具 — 在终端中查询大货生产订单、物料订单和 QC 质检订单,对接飞书身份认证系统。
7
+
8
+ [安装](#安装与快速开始) · [认证](#认证) · [三层命令调用](#三层命令调用) · [快捷命令](#快捷命令-shortcuts) · [进阶用法](#进阶用法) · [安全](#安全与风险提示使用前必读) · [贡献](#贡献)
9
+
10
+ ## 为什么选 scm-cli?
11
+
12
+ - **飞书身份集成** — 基于飞书 openId 认证,无需记忆额外账号密码
13
+ - **三分钟上手** — 一键安装,交互式登录,从安装到第一次查询只需三步
14
+ - **双层调用架构** — 标准命令(全面控制)→ 快捷命令(快速上手),按需选择
15
+ - **AES 加密存储** — 用户密码和 Token 均加密存储于本地
16
+ - **多格式输出** — 支持 table / json / pretty 三种输出格式
17
+ - **Token 自动续期** — 缓存 Token 自动检查,过期前无缝续期
18
+
19
+ ## 功能
20
+
21
+ | 类别 | 能力 |
22
+ | -------------- | -------------------------------------------------------------------- |
23
+ | 🔐 认证管理 | 登录、登出、Token 状态检查、Token 列表管理 |
24
+ | 📦 大货生产订单 | 查询订单列表,支持订单状态、供应商、商品信息、时间、设计师等多维筛选 |
25
+ | 🧴 物料订单 | 查询物料订单,支持按订单号、大货款号、批次、供应商等条件筛选 |
26
+ | 🔍 QC 质检订单 | 查询 QC 质检订单,支持质检状态、款号、供应商、时间等筛选条件 |
27
+
28
+ ## 安装与快速开始
29
+
30
+ ### 环境要求
31
+
32
+ - Node.js(`npm`/`npx`)14+
33
+ - SCM API 服务地址(配置方式见下文)
34
+
35
+ ### 安装
36
+
37
+ 以下两种方式**任选其一**:
38
+
39
+ **方式一 — 从源码安装(推荐):**
40
+
41
+ ```bash
42
+ git clone <your-repo-url>
43
+ cd scm-cli
44
+ ./install.sh
45
+ ```
46
+
47
+ **方式二 — 手动安装:**
48
+
49
+ ```bash
50
+ # 克隆项目
51
+ git clone <your-repo-url>
52
+ cd scm-cli
53
+
54
+ # 安装依赖
55
+ npm install
56
+
57
+ # 链接到全局
58
+ npm link
59
+ ```
60
+
61
+ #### 验证安装
62
+
63
+ ```bash
64
+ scm-cli --help
65
+ ```
66
+
67
+ ### 配置
68
+
69
+ 安装完成后,需要配置环境变量才能连接 SCM API:
70
+
71
+ ```bash
72
+ # 方式一:创建 .env 文件(项目根目录)
73
+ cp .env.example .env
74
+ # 编辑 .env 填入以下配置
75
+
76
+ # 方式二:直接设置环境变量
77
+ export SCM_BASE_URL=http://localhost:48080
78
+ export SCM_DEBUG=false
79
+ ```
80
+
81
+ **可选的环境变量:**
82
+
83
+ | 变量 | 说明 | 默认值 |
84
+ | -------------- | ----------------------------- | ------------------------------------- |
85
+ | `SCM_BASE_URL` | SCM API 端点地址(**必需**) | `https://fbscmtest.cogo.club` |
86
+ | `SCM_DEBUG` | 开启调试模式 | `false` |
87
+
88
+ > **说明**:`SCM_AES_KEY` 和 `SCM_AES_IV` 已内置于代码中,无需配置。如需自定义加密密钥,请联系 SCM 系统管理员。
89
+
90
+ ### 快速开始
91
+
92
+ **第 1 步 — 登录**
93
+
94
+ ```bash
95
+ scm-cli auth login \
96
+ --openId <飞书openId> \
97
+ --mobile <手机号> \
98
+ --password <密码>
99
+ ```
100
+
101
+ **第 2 步 — 查询大货生产订单**
102
+
103
+ ```bash
104
+ # 标准命令
105
+ scm-cli production-orders --openId <openId> --pageSize 20
106
+
107
+ # 快捷命令(更简洁)
108
+ scm-cli production-orders +list --openId <openId>
109
+ ```
110
+
111
+ **第 3 步 — 查看帮助**
112
+
113
+ ```bash
114
+ scm-cli --help
115
+ scm-cli auth --help
116
+ scm-cli production-orders --help
117
+ ```
118
+
119
+ ## 认证
120
+
121
+ | 命令 | 说明 |
122
+ | ------------------------------ | ------------------------------------ |
123
+ | `auth login` | 登录 SCM 系统(基于飞书身份) |
124
+ | `auth logout` | 登出,删除本地 Token |
125
+ | `auth status` | 检查 Token 状态 |
126
+ | `auth token list` | 列出所有缓存的 Token |
127
+ | `auth token show <openId>` | 显示指定用户的 Token 详情 |
128
+ | `auth token clear <openId>` | 清除指定用户的 Token |
129
+ | `auth token clear-all` | 清除所有缓存的 Token |
130
+
131
+ ```bash
132
+ # 登录
133
+ scm-cli auth login --openId ou_xxxx --mobile 13800138000 --password 123456
134
+
135
+ # 检查 Token 状态
136
+ scm-cli auth status --openId ou_xxxx
137
+
138
+ # 查看所有缓存的 Token
139
+ scm-cli auth token list
140
+
141
+ # 登出
142
+ scm-cli auth logout --openId ou_xxxx
143
+ ```
144
+
145
+ ### Token 存储
146
+
147
+ - Token 缓存路径:`~/.scm_cache/user_tokens.json`
148
+ - 密码使用 **AES-128-CBC** 加密后再传输和存储
149
+ - Token 有效期约 12 小时,过期前自动刷新
150
+
151
+ ## 三层命令调用
152
+
153
+ CLI 提供两种粒度的调用方式,覆盖从快速查询到精确筛选的全部场景:
154
+
155
+ ### 1. 标准命令
156
+
157
+ 标准命令提供最完整的筛选参数集,适合需要精确控制查询条件的场景:
158
+
159
+ ```bash
160
+ # 大货生产订单 — 所有筛选参数
161
+ scm-cli production-orders \
162
+ --openId <openId> \
163
+ --pageNo 1 \
164
+ --pageSize 20 \
165
+ --prodStatus 3 \
166
+ --confirmSupplier 1 \
167
+ --supplierOrderStatus 1 \
168
+ --quotationStatus 2 \
169
+ --washStatus 1 \
170
+ --qcInspection 1 \
171
+ --goodsFullCode "款号001" \
172
+ --goodsBatch "批次001" \
173
+ --goodsType 0 \
174
+ --supplierName "某供应商" \
175
+ --cooperationMode 0 \
176
+ --pdUserIdList "1,2,3" \
177
+ --purchaserIdList "4,5,6" \
178
+ --designerName "设计师A" \
179
+ --type 2 \
180
+ --startTime "2026-01-01" \
181
+ --endTime "2026-12-31" \
182
+ --saleArea "1,2,3"
183
+
184
+ # 物料订单 — 所有筛选参数
185
+ scm-cli material-orders \
186
+ --openId <openId> \
187
+ --pageNo 1 \
188
+ --pageSize 20 \
189
+ --orderNo "MO202603001" \
190
+ --status 1 \
191
+ --cooperationMode 0 \
192
+ --goodsFullCode "款号001" \
193
+ --goodsBatch "批次001" \
194
+ --goodsType 0 \
195
+ --supplierName "某供应商" \
196
+ --purchaserIdList "4,5,6" \
197
+ --pdUserIdList "1,2,3" \
198
+ --designerName "设计师A" \
199
+ --type 2 \
200
+ --startTime "2026-01-01" \
201
+ --endTime "2026-12-31"
202
+
203
+ # QC 质检订单 — 所有筛选参数
204
+ scm-cli qc-orders \
205
+ --openId <openId> \
206
+ --pageNo 1 \
207
+ --pageSize 20 \
208
+ --qcOrderStatus 1 \
209
+ --goodsFullCode "款号001" \
210
+ --goodsType 1 \
211
+ --goodsGradeList "A,B" \
212
+ --supplierName "某供应商" \
213
+ --midManager "中查管理员A" \
214
+ --checkGoods "查货人员B" \
215
+ --tailManager "尾查管理员C" \
216
+ --qcUserIdList "1,2,3" \
217
+ --prodManagerIdList "4,5,6" \
218
+ --purchaserIdList "7,8,9" \
219
+ --timeType 2 \
220
+ --startTime "2026-01-01" \
221
+ --endTime "2026-12-31"
222
+ ```
223
+
224
+ ### 2. 快捷命令(Shortcuts)
225
+
226
+ 以 `+` 为前缀,是对人机友好化封装,内置智能默认值,适合快速查询:
227
+
228
+ ```bash
229
+ # 大货生产订单 — 列表查询
230
+ scm-cli production-orders +list --openId <openId>
231
+
232
+ # 大货生产订单 — 高级查询
233
+ scm-cli production-orders +query \
234
+ --openId <openId> \
235
+ --prod-status 3 \
236
+ --supplier-name "某供应商"
237
+
238
+ # 物料订单 — 列表查询
239
+ scm-cli material-orders +list --openId <openId>
240
+
241
+ # QC 质检订单 — 列表查询
242
+ scm-cli qc-orders +list --openId <openId>
243
+ ```
244
+
245
+ ## 快捷命令(Shortcuts)
246
+
247
+ | 命令 | 说明 |
248
+ | ------------------------------ | ------------------------------------------ |
249
+ | `production-orders +list` | 查询大货生产订单列表(分页,默认 20 条/页) |
250
+ | `production-orders +query` | 查询大货生产订单(支持全部筛选条件) |
251
+ | `material-orders +list` | 查询物料订单列表(分页) |
252
+ | `qc-orders +list` | 查询 QC 质检订单列表(分页) |
253
+
254
+ ### 生产订单快捷命令详解
255
+
256
+ **`+list` — 快速列表查询**
257
+
258
+ ```bash
259
+ scm-cli production-orders +list \
260
+ --open-id <openId> \
261
+ [--page-no 1] \
262
+ [--page-size 20]
263
+ ```
264
+
265
+ **`+query` — 完整条件查询**
266
+
267
+ 支持以下所有筛选参数:
268
+
269
+ | 参数 | 说明 |
270
+ | --------------------- | --------------------------------------------------------------------- |
271
+ | `--prod-status` | 大货生产订单状态: `-1`-全部`0`-待确定`1`-待同步`2`-在途中`3`-已完成`100`-已取消 |
272
+ | `--confirm-supplier` | 确认供应商: `0`-未确认`1`-已确认 |
273
+ | `--goods-full-code` | 大货款号 |
274
+ | `--goods-batch` | 大货批次 |
275
+ | `--goods-type` | 大货类型: `0`-首单`1`-加单`2`-翻单 |
276
+ | `--supplier-name` | 供应商名称 |
277
+ | `--cooperation-mode` | 合作模式: `0`-CMT`1`-FOB |
278
+ | `--designer-name` | 设计师名称 |
279
+ | `--start-time` / `--end-time` | 时间范围 (ISO 8601) |
280
+ | `--qc-inspection` | QC 质检结果 |
281
+
282
+ ### 物料订单快捷命令详解
283
+
284
+ **`+list` — 物料订单列表**
285
+
286
+ ```bash
287
+ scm-cli material-orders +list \
288
+ --open-id <openId> \
289
+ [--page-no 1] \
290
+ [--page-size 20] \
291
+ [--order-no <订单号>] \
292
+ [--goods-full-code <款号>] \
293
+ [--goods-batch <批次>] \
294
+ [--goods-type <类型>] \
295
+ [--supplier-name <供应商名>] \
296
+ [--status <状态值>]
297
+ ```
298
+
299
+ ### QC 质检订单快捷命令详解
300
+
301
+ **`+list` — QC 质检订单列表**
302
+
303
+ ```bash
304
+ scm-cli qc-orders +list \
305
+ --open-id <openId> \
306
+ [--page-no 1] \
307
+ [--page-size 10] \
308
+ [--qc-order-status <状态值>] \
309
+ [--goods-full-code <款号>] \
310
+ [--goods-type <类型>] \
311
+ [--supplier-name <供应商名>] \
312
+ [--mid-manager <中查管理员>] \
313
+ [--check-goods <查货人员>] \
314
+ [--tail-manager <尾查管理员>] \
315
+ [--qc-user-id-list <QC用户ID列表>] \
316
+ [--prod-manager-id-list <生产经理ID列表>] \
317
+ [--purchaser-id-list <采购员ID列表>] \
318
+ [--time-type <时间类型>] \
319
+ [--start-time <开始时间>] \
320
+ [--end-time <结束时间>]
321
+ ```
322
+
323
+ ## 进阶用法
324
+
325
+ ### 输出格式
326
+
327
+ ```bash
328
+ --format json # 完整 JSON 响应(API 原生格式)
329
+ --format pretty # 人性化格式输出
330
+ --format table # 易读表格(默认)
331
+ ```
332
+
333
+ ```bash
334
+ # 输出 JSON
335
+ scm-cli production-orders +list --openId ou_xxxx --format json
336
+
337
+ # 输出易读格式
338
+ scm-cli material-orders +list --openId ou_xxxx --format pretty
339
+ ```
340
+
341
+ ### 调试模式
342
+
343
+ ```bash
344
+ # 全局调试
345
+ scm-cli --debug production-orders +list --openId ou_xxxx
346
+
347
+ # 或设置环境变量
348
+ export SCM_DEBUG=true
349
+ scm-cli production-orders +list --openId ou_xxxx
350
+ ```
351
+
352
+ ### 常用查询示例
353
+
354
+ ```bash
355
+ # 查询生产中(状态=3)的订单
356
+ scm-cli production-orders +query \
357
+ --open-id ou_xxxx \
358
+ --prod-status 3 \
359
+ --page-size 50
360
+
361
+ # 查询指定供应商的所有物料订单
362
+ scm-cli material-orders +list \
363
+ --open-id ou_xxxx \
364
+ --supplier-name "某服装厂"
365
+
366
+ # 查询 QC 待检订单
367
+ scm-cli qc-orders +list \
368
+ --open-id ou_xxxx \
369
+ --qc-order-status 0
370
+
371
+ # 按时间范围查询(近一个月)
372
+ scm-cli production-orders +query \
373
+ --open-id ou_xxxx \
374
+ --start-time "2026-03-01" \
375
+ --end-time "2026-04-13"
376
+ ```
377
+
378
+ ## 安全与风险提示(使用前必读)
379
+
380
+ 本工具对接 SCM 供应链管理系统,涉及生产订单、物料采购等商业敏感数据。请在使用前充分知悉以下风险:
381
+
382
+ **认证风险** — Token 缓存于本地文件,密码通过 AES-128-CBC 加密存储,请勿将本工具暴露于不受信任的环境。
383
+
384
+ **数据安全** — Token 有效期约 12 小时,过期后需要重新认证。请勿将 Token 或配置文件泄露给他人。
385
+
386
+ **权限管控** — 所有操作权限取决于飞书账号的 SCM 系统授权范围,请按最小权限原则分配飞书身份。
387
+
388
+ 请您充分知悉全部使用风险,使用本工具即视为您自愿承担相关所有责任。
389
+
390
+ ## 项目结构
391
+
392
+ ```
393
+ scm-cli/
394
+ ├── bin/
395
+ │ └── scm-cli.js # CLI 入口点
396
+ ├── src/
397
+ │ ├── cmd/ # Layer 1: Commander 命令注册
398
+ │ │ ├── root.js # 根命令 + 全局参数(--format, --debug)
399
+ │ │ ├── index.js # 注册所有子命令
400
+ │ │ ├── auth/ # 认证子命令
401
+ │ │ │ ├── login.js # 登录
402
+ │ │ │ ├── logout.js # 登出
403
+ │ │ │ ├── status.js # Token 状态检查
404
+ │ │ │ └── token.js # Token 管理(列表/清除)
405
+ │ │ └── orders/ # 订单子命令
406
+ │ │ ├── production-orders.js
407
+ │ │ ├── material-orders.js
408
+ │ │ └── qc-orders.js
409
+ │ ├── shortcuts/ # Layer 2: 快捷命令(Shortcuts)
410
+ │ │ ├── common/
411
+ │ │ │ ├── types.js # Shortcut 类型定义
412
+ │ │ │ └── runner.js # Shortcut 执行管道
413
+ │ │ ├── register.js # 注册 Shortcuts 到命令树
414
+ │ │ ├── production-orders/ # +list, +query 快捷命令
415
+ │ │ ├── material-orders/ # +list 快捷命令
416
+ │ │ └── qc-orders/ # +list 快捷命令
417
+ │ └── internal/ # Layer 3: 核心库
418
+ │ ├── index.js # 统一导出
419
+ │ ├── client/ # ScmClient 单例
420
+ │ ├── errors/ # 错误类层次结构
421
+ │ ├── output/ # 格式化输出 + 日志
422
+ │ ├── config/ # 配置文件加载
423
+ │ └── auth/ # Token 存储与认证
424
+ ├── tests/ # 测试用例
425
+ ├── install.sh # 安装脚本
426
+ └── package.json
427
+ ```
428
+
429
+ ## 开发
430
+
431
+ ```bash
432
+ # 安装依赖
433
+ npm install
434
+
435
+ # 运行测试
436
+ npm test
437
+
438
+ # 监听模式运行测试
439
+ npm run test:watch
440
+
441
+ # 带覆盖率运行测试
442
+ npm run test:coverage
443
+ ```
444
+
445
+ ## 贡献
446
+
447
+ 欢迎提交 Issue 或 Pull Request!对于较大的改动,建议先通过 Issue 讨论。
448
+
449
+ ## 许可证
450
+
451
+ 本项目基于 **MIT 许可证** 开源。
package/bin/scm-cli.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Copyright (c) 2026 WFS
5
+ * SPDX-License-Identifier: MIT
6
+ *
7
+ * scm-cli — SCM CLI 工具入口
8
+ *
9
+ * 用法:
10
+ * scm-cli --help
11
+ * scm-cli auth login --openId <openId> --mobile <手机号> --password <密码>
12
+ * scm-cli auth logout --openId <openId>
13
+ * scm-cli auth status --openId <openId>
14
+ * scm-cli production-orders --openId <openId> [--goodsFullCode <款号>]
15
+ * scm-cli material-orders --openId <openId> [--orderNo <订单号>]
16
+ * scm-cli qc-orders --openId <openId> [--qcOrderStatus <状态>]
17
+ * scm-cli production-orders +list --openId <openId> [--format table]
18
+ * scm-cli production-orders +query --openId <openId> [--prod-status 3]
19
+ */
20
+
21
+ import { readFileSync } from 'node:fs';
22
+ import { join, dirname } from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
24
+ import { createProgram, buildInternalContext } from '../src/cmd/root.js';
25
+ import { registerCommands } from '../src/cmd/index.js';
26
+
27
+ const __filename = fileURLToPath(import.meta.url);
28
+ const __dirname = dirname(__filename);
29
+ const projectRoot = dirname(__dirname);
30
+
31
+ const { version } = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8'));
32
+
33
+ const program = createProgram({ version, name: 'scm-cli' });
34
+ const internalCtx = buildInternalContext({ debug: process.env.SCM_DEBUG === 'true' });
35
+
36
+ registerCommands(program, internalCtx);
37
+
38
+ program.parse();
package/index.d.ts ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2026 WFS
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * OpenClaw SCM Plugin entry point.
6
+ *
7
+ * SCM 供应链管理插件入口
8
+ *
9
+ * 注册工具:
10
+ * - scm_login: 登录认证
11
+ * - scm_production_orders: 大货生产订单查询
12
+ * - scm_material_orders: 物料订单查询
13
+ * - scm_qc_orders: QC 质检订单查询
14
+ */
15
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
16
+
17
+ // SCM 客户端
18
+ export { scmClient } from './src/core/scm-client';
19
+
20
+ // SCM 错误类
21
+ export { ScmError, ScmAuthError, ScmTokenExpiredError, ScmTokenInvalidError, ScmApiError, ScmTimeoutError, ScmNetworkError } from './src/core/scm-errors';
22
+ export type { ScmErrorOptions, ScmApiErrorOptions } from './src/core/scm-errors';
23
+
24
+ // 工具执行函数 - scm_login
25
+ export { executeScmLogin } from './src/tools/login/execute';
26
+
27
+ // 工具执行函数 - scm_production_orders
28
+ export { executeProductionOrders } from './src/tools/production-orders/execute';
29
+
30
+ // 工具执行函数 - scm_material_orders
31
+ export { executeMaterialOrders } from './src/tools/material-orders/execute';
32
+
33
+ // 工具执行函数 - scm_qc_orders
34
+ export { executeQcOrders, buildRequestBody as buildQcOrdersRequestBody } from './src/tools/qc-orders/execute';
35
+
36
+ // 工具注册函数
37
+ export { registerScmLoginTool } from './src/tools/login/index';
38
+ export { registerScmProductionOrdersTool } from './src/tools/production-orders/index';
39
+ export { registerScmMaterialOrdersTool } from './src/tools/material-orders/index';
40
+ export { registerScmQcOrdersTool } from './src/tools/qc-orders/index';
41
+
42
+ // SCM 插件 Channel
43
+ export { scmPlugin } from './src/channel/plugin';
44
+
45
+ // 工具辅助函数
46
+ export { createToolContext, formatResult, formatSuccess, formatError, formatApiError, getUserToken, clearUserToken } from './src/tools/helpers';
47
+ export type { ToolContext } from './src/tools/helpers';
48
+
49
+ // 日志辅助函数
50
+ export { scmLogger, setRuntimeLoggerFactory } from './src/core/scm-logger';
51
+
52
+ declare const plugin: {
53
+ id: string;
54
+ name: string;
55
+ description: string;
56
+ configSchema: Record<string, unknown>;
57
+ register(api: OpenClawPluginApi): void;
58
+ };
59
+ export default plugin;
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@besile/scm-cli",
3
+ "version": "2026.3.29",
4
+ "description": "SCM CLI — Supply Chain Management CLI Tool",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "WFS",
8
+ "engines": {
9
+ "node": ">=18.0.0"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/wfs/scm-cli"
14
+ },
15
+ "keywords": [
16
+ "scm",
17
+ "supply-chain",
18
+ "cli",
19
+ "production-order",
20
+ "manufacturing"
21
+ ],
22
+ "files": [
23
+ "bin/",
24
+ "src/",
25
+ "index.d.ts"
26
+ ],
27
+ "bin": {
28
+ "scm-cli": "./bin/scm-cli.js"
29
+ },
30
+ "exports": {
31
+ ".": {
32
+ "import": "./src/internal/index.js",
33
+ "default": "./src/internal/index.js"
34
+ }
35
+ },
36
+ "scripts": {
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "test:coverage": "vitest run --coverage"
40
+ },
41
+ "dependencies": {
42
+ "@larksuiteoapi/node-sdk": "^1.7.0",
43
+ "@sinclair/typebox": "^0.34.48",
44
+ "commander": "^14.0.3",
45
+ "dotenv": "^17.3.1"
46
+ },
47
+ "devDependencies": {
48
+ "@vitest/coverage-v8": "^2.1.9",
49
+ "vitest": "^2.0.0"
50
+ }
51
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 WFS
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * scm-cli auth login command
7
+ */
8
+ import { scmClient, loadConfig, createLogger } from '../../internal/index.js';
9
+
10
+ /**
11
+ * Execute login
12
+ *
13
+ * @param {string} openId - 飞书用户 openId
14
+ * @param {string} mobile - 手机号
15
+ * @param {string} password - 密码
16
+ * @returns {Promise<Object>} Login result
17
+ */
18
+ export async function doLogin(openId, mobile, password) {
19
+ const config = loadConfig();
20
+ const log = createLogger(config.debug);
21
+
22
+ // Encrypt password with AES-128-CBC
23
+ const { createCipheriv } = await import('node:crypto');
24
+ const cipher = createCipheriv(
25
+ 'aes-128-cbc',
26
+ Buffer.from(config.aesKey, 'ascii'),
27
+ Buffer.from(config.aesIv, 'ascii')
28
+ );
29
+ const encrypted = Buffer.concat([
30
+ cipher.update(Buffer.from(password, 'utf-8')),
31
+ cipher.final(),
32
+ ]).toString('base64');
33
+
34
+ const loginUrl = `${config.baseUrl}/admin-api/system/auth/login`;
35
+
36
+ const response = await fetch(loginUrl, {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ mobile, password: encrypted }),
40
+ });
41
+
42
+ const data = await response.json();
43
+
44
+ if (!response.ok) {
45
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
46
+ }
47
+
48
+ if (data.code !== 200 && data.success !== true) {
49
+ throw new Error(data.msg || `登录失败,错误码: ${data.code}`);
50
+ }
51
+
52
+ const tokenInfo = data.data || {};
53
+ return {
54
+ openId,
55
+ mobile,
56
+ username: tokenInfo.username || mobile,
57
+ userId: tokenInfo.userId || '',
58
+ expiresIn: tokenInfo.expires_in || 12 * 3600,
59
+ accessToken: tokenInfo.access_token || tokenInfo.token,
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Register auth login subcommand
65
+ */
66
+ export function loginCommand(program) {
67
+ program
68
+ .command('login')
69
+ .description('登录 SCM 系统')
70
+ .requiredOption('--openId <openId>', '飞书用户 openId')
71
+ .requiredOption('--mobile <mobile>', '手机号(11位)')
72
+ .requiredOption('--password <password>', '密码')
73
+ .action(async (opts) => {
74
+ try {
75
+ console.log('正在登录 SCM...');
76
+ const result = await doLogin(opts.openId, opts.mobile, opts.password);
77
+
78
+ console.log('\n✅ 登录成功!');
79
+ console.log('───────────────');
80
+ console.log(`用户: ${result.username}`);
81
+ console.log(`User ID: ${result.userId}`);
82
+ console.log(`Open ID: ${result.openId}`);
83
+ console.log(`Token: ${result.accessToken.substring(0, 30)}...`);
84
+ console.log(`有效期: ${Math.round(result.expiresIn / 60)} 分钟`);
85
+ console.log('───────────────');
86
+ console.log('Token 已缓存到 ~/.scm_cache/user_tokens.json');
87
+ } catch (err) {
88
+ console.error('\n❌ 登录失败:', err.message);
89
+ process.exit(1);
90
+ }
91
+ });
92
+ }