eprolo-cli 1.0.44 → 1.0.46

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 +106 -57
  2. package/package.json +1 -1
package/bin/eprolo-cli.js CHANGED
@@ -4,14 +4,16 @@
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 REFRESH_API = "https://wixtest.eprolo.com/v1/auth/refresh-code";
11
+ const ORDERS_API = "https://wixtest.eprolo.com/test/orders";
10
12
  const CONFIG_DIR = path.join(os.homedir(), ".eprolo-toolkit");
11
13
  const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
12
14
 
13
15
  // ---------------------------------------------------------------------------
14
- // 凭据文件读写(~/.dxb-toolkit/config.json)
16
+ // 凭据文件读写(~/.eprolo-toolkit/config.json)
15
17
  // 框架判定连接成功的唯一依据:凭据文件出现
16
18
  // 框架读顶层明文 account 字段做授权卡片标签
17
19
  // ---------------------------------------------------------------------------
@@ -32,26 +34,6 @@ function writeConfig(config) {
32
34
  // 参数解析
33
35
  // ---------------------------------------------------------------------------
34
36
 
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
37
  function readFlag(argv, flag) {
56
38
  const idx = argv.indexOf(flag);
57
39
  if (idx === -1 || idx + 1 >= argv.length) return undefined;
@@ -59,21 +41,22 @@ function readFlag(argv, flag) {
59
41
  }
60
42
 
61
43
  // ---------------------------------------------------------------------------
62
- // HTTP POST 请求
44
+ // HTTPS POST 请求
63
45
  // ---------------------------------------------------------------------------
64
46
 
65
- function postJson(url, body) {
47
+ function postJson(url, body, headers = {}) {
66
48
  return new Promise((resolve, reject) => {
67
49
  const target = new URL(url);
68
50
  const payload = JSON.stringify(body);
69
- const req = http.request({
51
+ const req = https.request({
70
52
  hostname: target.hostname,
71
- port: target.port,
53
+ port: target.port || 443,
72
54
  path: `${target.pathname}${target.search}`,
73
55
  method: "POST",
74
56
  headers: {
75
57
  "content-type": "application/json",
76
- "content-length": Buffer.byteLength(payload)
58
+ "content-length": Buffer.byteLength(payload),
59
+ ...headers
77
60
  }
78
61
  }, (res) => {
79
62
  const chunks = [];
@@ -86,13 +69,7 @@ function postJson(url, body) {
86
69
  } catch (_) {
87
70
  data = { raw: text };
88
71
  }
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);
72
+ resolve({ statusCode: res.statusCode, data });
96
73
  });
97
74
  });
98
75
  req.on("error", reject);
@@ -101,32 +78,79 @@ function postJson(url, body) {
101
78
  });
102
79
  }
103
80
 
81
+ // ---------------------------------------------------------------------------
82
+ // 带 401 自动刷新 token 重试的请求
83
+ // 1. 发起请求
84
+ // 2. 如果返回 401 → 调用 refresh-code 刷新 token → 回写凭据文件 → 重试一次
85
+ // 3. 仍然 401 → 抛错
86
+ // ---------------------------------------------------------------------------
87
+
88
+ async function postJsonWithRetry(url, body, config) {
89
+ const headers = {};
90
+ if (config.accessToken) headers["authorization"] = `Bearer ${config.accessToken}`;
91
+
92
+ let result = await postJson(url, body, headers);
93
+
94
+ if (result.statusCode === 401) {
95
+ // 刷新 token
96
+ const refreshRes = await postJson(REFRESH_API, {
97
+ accessToken: config.accessToken,
98
+ accountId: config.accountId,
99
+ shopId: config.shopId
100
+ });
101
+
102
+ if (refreshRes.statusCode >= 200 && refreshRes.statusCode < 300 && refreshRes.data.accessToken) {
103
+ // 更新凭据文件
104
+ config.accessToken = refreshRes.data.accessToken;
105
+ if (refreshRes.data.account) config.account = refreshRes.data.account;
106
+ writeConfig(config);
107
+
108
+ // 用新 token 重试
109
+ headers["authorization"] = `Bearer ${config.accessToken}`;
110
+ result = await postJson(url, body, headers);
111
+ } else {
112
+ const err = new Error("Token 刷新失败,请重新登录授权");
113
+ err.response = refreshRes.data;
114
+ throw err;
115
+ }
116
+ }
117
+
118
+ if (result.statusCode < 200 || result.statusCode >= 300) {
119
+ const err = new Error(`HTTP ${result.statusCode}`);
120
+ err.response = result.data;
121
+ throw err;
122
+ }
123
+
124
+ return result.data;
125
+ }
126
+
104
127
  // ---------------------------------------------------------------------------
105
128
  // 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
129
  // ---------------------------------------------------------------------------
114
130
 
115
131
  async function login(argv) {
116
132
  const userCode = process.env.EPROLO_USER_CODE; // env 通道
117
133
  if (!userCode) { console.error("缺少用户密文:请在授权弹窗中填写"); process.exit(2); }
118
134
 
119
- const defaultShop = readFlag(argv, "--default-shop"); // arg 通道,可能不存在
135
+ const defaultShop = readFlag(argv, "--default-shop"); // arg 通道,可能不存在
120
136
 
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); }
137
+ const res = await postJson(LOGIN_API, { openApiKey: userCode });
138
+
139
+ if (res.statusCode < 200 || res.statusCode >= 300) {
140
+ console.error(JSON.stringify({
141
+ success: false,
142
+ errorMessage: "密文验证失败,请到账号设置页重新复制",
143
+ statusCode: res.statusCode,
144
+ response: res.data
145
+ }));
146
+ process.exit(1);
147
+ }
148
+
149
+ const { account, accessToken, accountId, shopId } = res.data;
150
+ const config = { account, accessToken, defaultShop, accountId, shopId, version: 1 };
151
+ writeConfig(config);
126
152
 
127
- const { account, accessToken,accountId,shopId } = await res.json();
128
- writeConfig({ account, accessToken, defaultShop,accountId,shopId }); // → D3
129
- console.log("login ok"); // 不打印任何敏感值
153
+ console.log("login ok");
130
154
  process.exit(0);
131
155
  }
132
156
 
@@ -155,33 +179,53 @@ function authLogout() {
155
179
  process.exit(0);
156
180
  }
157
181
 
182
+ // ---------------------------------------------------------------------------
183
+ // orders query — 查询订单(带 401 自动刷新)
184
+ // ---------------------------------------------------------------------------
185
+
186
+ async function queryOrders() {
187
+ const config = readConfig();
188
+ if (!config.accessToken) {
189
+ console.error(JSON.stringify({ success: false, errorMessage: "未授权,请先登录" }));
190
+ process.exit(1);
191
+ }
192
+
193
+ const data = await postJsonWithRetry(ORDERS_API, {}, config);
194
+ console.log(JSON.stringify(data, null, 2));
195
+ process.exit(0);
196
+ }
197
+
158
198
  // ---------------------------------------------------------------------------
159
199
  // help
160
200
  // ---------------------------------------------------------------------------
161
201
 
162
202
  function printHelp() {
163
- console.log(`DXB CLI (cli-login 形态)
203
+ console.log(`EPROLO CLI (cli-login 形态)
164
204
 
165
205
  Usage:
166
- eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入 DXB_USER_CODE
206
+ eprolo-cli auth login [--default-shop <shop>] 登录并写凭据文件(框架通过 env 注入 EPROLO_USER_CODE
167
207
  eprolo-cli auth status 查看授权状态
168
208
  eprolo-cli auth logout 退出授权
209
+ eprolo-cli orders 查询订单列表
169
210
 
170
211
  Authorization:
171
212
  框架通过 fieldInjection 把表单值递交给 CLI:
172
- userCode → env 通道 → process.env.DXB_USER_CODE
213
+ userCode → env 通道 → process.env.EPROLO_USER_CODE
173
214
  defaultShop → arg 通道 → --default-shop
174
215
 
175
- 连接成功的唯一判定:凭据文件 ~/.dxb-toolkit/config.json 出现
216
+ 连接成功的唯一判定:凭据文件 ~/.eprolo-toolkit/config.json 出现
176
217
  凭据文件顶层明文 account 字段用于授权卡片标签
177
218
 
178
219
  登录时调用 ${LOGIN_API} 验证密文
220
+ 401 时自动调用 ${REFRESH_API} 刷新 token 并重试
179
221
 
180
- Credential file (~/.dxb-toolkit/config.json):
222
+ Credential file (~/.eprolo-toolkit/config.json):
181
223
  {
182
- "account": "user@example.com", // 顶层明文,框架读
183
- "userCode": "...",
224
+ "account": "user@example.com",
225
+ "accessToken": "...",
184
226
  "defaultShop": "shop-001",
227
+ "accountId": "...",
228
+ "shopId": "...",
185
229
  "version": 1
186
230
  }
187
231
  `);
@@ -209,6 +253,11 @@ async function main() {
209
253
  return;
210
254
  }
211
255
 
256
+ if (argv[0] === "orders") {
257
+ await queryOrders();
258
+ return;
259
+ }
260
+
212
261
  printHelp();
213
262
  }
214
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eprolo-cli",
3
- "version": "1.0.44",
3
+ "version": "1.0.46",
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"