eprolo-cli 1.0.43 → 1.0.45

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 (2) hide show
  1. package/bin/eprolo-cli.js +84 -58
  2. package/package.json +1 -1
package/bin/eprolo-cli.js CHANGED
@@ -4,14 +4,15 @@
4
4
  const fs = require("fs");
5
5
  const os = require("os");
6
6
  const path = require("path");
7
- const http = require("http");
7
+ const https = require("https");
8
8
 
9
9
  const LOGIN_API = "https://wixtest.eprolo.com/v1/auth/verify-code";
10
- const CONFIG_DIR = path.join(os.homedir(), ".dxb-toolkit");
10
+ const REFRESH_API = "https://wixtest.eprolo.com/v1/auth/refresh-code";
11
+ const CONFIG_DIR = path.join(os.homedir(), ".eprolo-toolkit");
11
12
  const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
12
13
 
13
14
  // ---------------------------------------------------------------------------
14
- // 凭据文件读写(~/.dxb-toolkit/config.json)
15
+ // 凭据文件读写(~/.eprolo-toolkit/config.json)
15
16
  // 框架判定连接成功的唯一依据:凭据文件出现
16
17
  // 框架读顶层明文 account 字段做授权卡片标签
17
18
  // ---------------------------------------------------------------------------
@@ -32,26 +33,6 @@ function writeConfig(config) {
32
33
  // 参数解析
33
34
  // ---------------------------------------------------------------------------
34
35
 
35
- function parseArgs(argv) {
36
- const args = { _: [] };
37
- for (let i = 0; i < argv.length; i += 1) {
38
- const token = argv[i];
39
- if (!token.startsWith("--")) {
40
- args._.push(token);
41
- continue;
42
- }
43
- const key = token.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
44
- const next = argv[i + 1];
45
- if (!next || next.startsWith("--")) {
46
- args[key] = true;
47
- } else {
48
- args[key] = next;
49
- i += 1;
50
- }
51
- }
52
- return args;
53
- }
54
-
55
36
  function readFlag(argv, flag) {
56
37
  const idx = argv.indexOf(flag);
57
38
  if (idx === -1 || idx + 1 >= argv.length) return undefined;
@@ -59,21 +40,22 @@ function readFlag(argv, flag) {
59
40
  }
60
41
 
61
42
  // ---------------------------------------------------------------------------
62
- // HTTP POST 请求
43
+ // HTTPS POST 请求
63
44
  // ---------------------------------------------------------------------------
64
45
 
65
- function postJson(url, body) {
46
+ function postJson(url, body, headers = {}) {
66
47
  return new Promise((resolve, reject) => {
67
48
  const target = new URL(url);
68
49
  const payload = JSON.stringify(body);
69
- const req = http.request({
50
+ const req = https.request({
70
51
  hostname: target.hostname,
71
- port: target.port,
52
+ port: target.port || 443,
72
53
  path: `${target.pathname}${target.search}`,
73
54
  method: "POST",
74
55
  headers: {
75
56
  "content-type": "application/json",
76
- "content-length": Buffer.byteLength(payload)
57
+ "content-length": Buffer.byteLength(payload),
58
+ ...headers
77
59
  }
78
60
  }, (res) => {
79
61
  const chunks = [];
@@ -86,13 +68,7 @@ function postJson(url, body) {
86
68
  } catch (_) {
87
69
  data = { raw: text };
88
70
  }
89
- if (res.statusCode < 200 || res.statusCode >= 300) {
90
- const err = new Error(`HTTP ${res.statusCode} ${res.statusMessage || ""}`.trim());
91
- err.response = data;
92
- reject(err);
93
- return;
94
- }
95
- resolve(data);
71
+ resolve({ statusCode: res.statusCode, data });
96
72
  });
97
73
  });
98
74
  req.on("error", reject);
@@ -101,32 +77,79 @@ function postJson(url, body) {
101
77
  });
102
78
  }
103
79
 
80
+ // ---------------------------------------------------------------------------
81
+ // 带 401 自动刷新 token 重试的请求
82
+ // 1. 发起请求
83
+ // 2. 如果返回 401 → 调用 refresh-code 刷新 token → 回写凭据文件 → 重试一次
84
+ // 3. 仍然 401 → 抛错
85
+ // ---------------------------------------------------------------------------
86
+
87
+ async function postJsonWithRetry(url, body, config) {
88
+ const headers = {};
89
+ if (config.accessToken) headers["authorization"] = `Bearer ${config.accessToken}`;
90
+
91
+ let result = await postJson(url, body, headers);
92
+
93
+ if (result.statusCode === 401) {
94
+ // 刷新 token
95
+ const refreshRes = await postJson(REFRESH_API, {
96
+ accessToken: config.accessToken,
97
+ accountId: config.accountId,
98
+ shopId: config.shopId
99
+ });
100
+
101
+ if (refreshRes.statusCode >= 200 && refreshRes.statusCode < 300 && refreshRes.data.accessToken) {
102
+ // 更新凭据文件
103
+ config.accessToken = refreshRes.data.accessToken;
104
+ if (refreshRes.data.account) config.account = refreshRes.data.account;
105
+ writeConfig(config);
106
+
107
+ // 用新 token 重试
108
+ headers["authorization"] = `Bearer ${config.accessToken}`;
109
+ result = await postJson(url, body, headers);
110
+ } else {
111
+ const err = new Error("Token 刷新失败,请重新登录授权");
112
+ err.response = refreshRes.data;
113
+ throw err;
114
+ }
115
+ }
116
+
117
+ if (result.statusCode < 200 || result.statusCode >= 300) {
118
+ const err = new Error(`HTTP ${result.statusCode}`);
119
+ err.response = result.data;
120
+ throw err;
121
+ }
122
+
123
+ return result.data;
124
+ }
125
+
104
126
  // ---------------------------------------------------------------------------
105
127
  // D2 · login 命令
106
- // 输入契约(框架 → CLI):
107
- // userCode ← env 通道 process.env.DXB_USER_CODE
108
- // defaultShop ← arg 通道 --default-shop
109
- // 行为契约:
110
- // 成功 → 写凭据文件 + exit 0
111
- // 失败 → 非零退出 + stderr 错误信息
112
- // ❌ 禁止交互式 prompt
113
128
  // ---------------------------------------------------------------------------
114
129
 
115
130
  async function login(argv) {
116
131
  const userCode = process.env.EPROLO_USER_CODE; // env 通道
117
132
  if (!userCode) { console.error("缺少用户密文:请在授权弹窗中填写"); process.exit(2); }
118
133
 
119
- const defaultShop = readFlag(argv, "--default-shop"); // arg 通道,可能不存在
134
+ const defaultShop = readFlag(argv, "--default-shop"); // arg 通道,可能不存在
120
135
 
121
- const res = await fetch(LOGIN_API, {
122
- method: "POST", headers: { "Content-Type": "application/json" },
123
- body: JSON.stringify({ openApiKey:userCode }),
124
- });
125
- // if (!res.ok) { console.error("密文无效或已吊销,请到账号设置页重新复制"); process.exit(1); }
136
+ const res = await postJson(LOGIN_API, { openApiKey: userCode });
137
+
138
+ if (res.statusCode < 200 || res.statusCode >= 300) {
139
+ console.error(JSON.stringify({
140
+ success: false,
141
+ errorMessage: "密文验证失败,请到账号设置页重新复制",
142
+ statusCode: res.statusCode,
143
+ response: res.data
144
+ }));
145
+ process.exit(1);
146
+ }
147
+
148
+ const { account, accessToken, accountId, shopId } = res.data;
149
+ const config = { account, accessToken, defaultShop, accountId, shopId, version: 1 };
150
+ writeConfig(config);
126
151
 
127
- const { account, accessToken,accountId,shopId } = await res.json();
128
- writeConfig({ account, accessToken, defaultShop,accountId,shopId }); // → D3
129
- console.log("login ok"); // 不打印任何敏感值
152
+ console.log("login ok");
130
153
  process.exit(0);
131
154
  }
132
155
 
@@ -160,28 +183,31 @@ function authLogout() {
160
183
  // ---------------------------------------------------------------------------
161
184
 
162
185
  function printHelp() {
163
- console.log(`DXB CLI (cli-login 形态)
186
+ console.log(`EPROLO CLI (cli-login 形态)
164
187
 
165
188
  Usage:
166
- eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入 DXB_USER_CODE
189
+ eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入 EPROLO_USER_CODE
167
190
  eprolo-cli auth status 查看授权状态
168
191
  eprolo-cli auth logout 退出授权
169
192
 
170
193
  Authorization:
171
194
  框架通过 fieldInjection 把表单值递交给 CLI:
172
- userCode → env 通道 → process.env.DXB_USER_CODE
195
+ userCode → env 通道 → process.env.EPROLO_USER_CODE
173
196
  defaultShop → arg 通道 → --default-shop
174
197
 
175
- 连接成功的唯一判定:凭据文件 ~/.dxb-toolkit/config.json 出现
198
+ 连接成功的唯一判定:凭据文件 ~/.eprolo-toolkit/config.json 出现
176
199
  凭据文件顶层明文 account 字段用于授权卡片标签
177
200
 
178
201
  登录时调用 ${LOGIN_API} 验证密文
202
+ 401 时自动调用 ${REFRESH_API} 刷新 token 并重试
179
203
 
180
- Credential file (~/.dxb-toolkit/config.json):
204
+ Credential file (~/.eprolo-toolkit/config.json):
181
205
  {
182
- "account": "user@example.com", // 顶层明文,框架读
183
- "userCode": "...",
206
+ "account": "user@example.com",
207
+ "accessToken": "...",
184
208
  "defaultShop": "shop-001",
209
+ "accountId": "...",
210
+ "shopId": "...",
185
211
  "version": 1
186
212
  }
187
213
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eprolo-cli",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "description": "CLI for Dianxiaobao ICBU AI publishing, authorization profile management, task submission, and task status checks.",
5
5
  "bin": {
6
6
  "eprolo": "bin/eprolo-cli.js"