@zrhsh/wukong-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.
- package/README.md +172 -0
- package/dist/cli.js +1718 -0
- package/dist/cli.js.map +1 -0
- package/package.json +50 -0
- package/scripts/install.js +49 -0
- package/wukong-cli.json.template +25 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1718 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
24
|
+
import path from "path";
|
|
25
|
+
import { fileURLToPath } from "url";
|
|
26
|
+
var init_esm_shims = __esm({
|
|
27
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
28
|
+
"use strict";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// src/constants/config.ts
|
|
33
|
+
var API_ENDPOINTS, POLL_CONFIG;
|
|
34
|
+
var init_config = __esm({
|
|
35
|
+
"src/constants/config.ts"() {
|
|
36
|
+
"use strict";
|
|
37
|
+
init_esm_shims();
|
|
38
|
+
API_ENDPOINTS = {
|
|
39
|
+
AUTH: {
|
|
40
|
+
DEVICE_AUTHORIZE: "/oceanet-auth/pkce/device/authorize",
|
|
41
|
+
DEVICE_TOKEN: "/oceanet-auth/pkce/device/token",
|
|
42
|
+
REFRESH_TOKEN: "/oceanet-auth/manage/refreshToken",
|
|
43
|
+
LOGOUT: "/oceanet-auth/manage/logout"
|
|
44
|
+
},
|
|
45
|
+
API: {
|
|
46
|
+
USER_INFO: "/oceanet-auth/web/userInfo"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
POLL_CONFIG = {
|
|
50
|
+
INTERVAL: 3,
|
|
51
|
+
// 轮询间隔(秒)
|
|
52
|
+
TIMEOUT: 300
|
|
53
|
+
// 轮询超时(秒)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// src/config/environments.ts
|
|
59
|
+
function isValidEnvironment(env) {
|
|
60
|
+
return env in ENVIRONMENTS;
|
|
61
|
+
}
|
|
62
|
+
var ENVIRONMENTS, DEFAULT_ENVIRONMENT;
|
|
63
|
+
var init_environments = __esm({
|
|
64
|
+
"src/config/environments.ts"() {
|
|
65
|
+
"use strict";
|
|
66
|
+
init_esm_shims();
|
|
67
|
+
ENVIRONMENTS = {
|
|
68
|
+
dev: {
|
|
69
|
+
name: "dev",
|
|
70
|
+
displayName: "\u5F00\u53D1\u73AF\u5883 (Development)",
|
|
71
|
+
authBaseUrl: "https://portal-dev.zrhsh.com",
|
|
72
|
+
apiBaseUrl: "https://nrp-recode-dev.zrhsh.com",
|
|
73
|
+
clientId: "wukong-cli-dev"
|
|
74
|
+
},
|
|
75
|
+
beta: {
|
|
76
|
+
name: "beta",
|
|
77
|
+
displayName: "\u6D4B\u8BD5\u73AF\u5883 (Testing)",
|
|
78
|
+
authBaseUrl: "https://portal-beta.zrhsh.com",
|
|
79
|
+
apiBaseUrl: "https://nrp-recode.zrhsh.com",
|
|
80
|
+
clientId: "wukong-cli-beta"
|
|
81
|
+
},
|
|
82
|
+
uat: {
|
|
83
|
+
name: "uat",
|
|
84
|
+
displayName: "UAT\u73AF\u5883 (User Acceptance Testing)",
|
|
85
|
+
authBaseUrl: "https://portal-uat.zrhsh.com",
|
|
86
|
+
apiBaseUrl: "https://nrp-recode-uat.zrhsh.com",
|
|
87
|
+
clientId: "wukong-cli-uat"
|
|
88
|
+
},
|
|
89
|
+
prod: {
|
|
90
|
+
name: "prod",
|
|
91
|
+
displayName: "\u751F\u4EA7\u73AF\u5883 (Production)",
|
|
92
|
+
authBaseUrl: "https://portal.zrhsh.com",
|
|
93
|
+
apiBaseUrl: "https://nrp-recode-prod.zrhsh.com",
|
|
94
|
+
clientId: "wukong-cli-prod"
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
DEFAULT_ENVIRONMENT = "prod";
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// src/config/config-loader.ts
|
|
102
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
103
|
+
import { join, dirname } from "path";
|
|
104
|
+
import { homedir } from "os";
|
|
105
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
106
|
+
function getUserConfigPath() {
|
|
107
|
+
return join(homedir(), "wukong-cli.json");
|
|
108
|
+
}
|
|
109
|
+
function createDefaultConfig() {
|
|
110
|
+
const configPath = getUserConfigPath();
|
|
111
|
+
if (existsSync(configPath)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const templatePath = join(getProjectRoot(), "wukong-cli.json.template");
|
|
116
|
+
if (!existsSync(templatePath)) {
|
|
117
|
+
console.warn(`Warning: Template config not found: ${templatePath}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const templateContent = readFileSync(templatePath, "utf-8");
|
|
121
|
+
const defaultConfig = JSON.parse(templateContent);
|
|
122
|
+
const dir = dirname(configPath);
|
|
123
|
+
if (!existsSync(dir)) {
|
|
124
|
+
mkdirSync(dir, { recursive: true });
|
|
125
|
+
}
|
|
126
|
+
writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
127
|
+
} catch (error) {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function getProjectRoot() {
|
|
131
|
+
let currentDir = dirname(fileURLToPath2(import.meta.url));
|
|
132
|
+
while (currentDir !== dirname(currentDir)) {
|
|
133
|
+
if (existsSync(join(currentDir, "package.json"))) {
|
|
134
|
+
return currentDir;
|
|
135
|
+
}
|
|
136
|
+
currentDir = dirname(currentDir);
|
|
137
|
+
}
|
|
138
|
+
return process.cwd();
|
|
139
|
+
}
|
|
140
|
+
function findConfigFile() {
|
|
141
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
142
|
+
const localPath = join(process.cwd(), filename);
|
|
143
|
+
if (existsSync(localPath)) {
|
|
144
|
+
return localPath;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const userConfigPath = getUserConfigPath();
|
|
148
|
+
if (existsSync(userConfigPath)) {
|
|
149
|
+
return userConfigPath;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function loadConfig() {
|
|
154
|
+
const configPath = findConfigFile();
|
|
155
|
+
if (!configPath) {
|
|
156
|
+
createDefaultConfig();
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
const content = readFileSync(configPath, "utf-8");
|
|
161
|
+
const config = JSON.parse(content);
|
|
162
|
+
return config;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.warn(`Warning: Failed to load config from ${configPath}: ${error}`);
|
|
165
|
+
return {};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function getMergedEnvironmentConfig(env) {
|
|
169
|
+
const config = loadConfig();
|
|
170
|
+
const defaultConfig = ENVIRONMENTS[env] || ENVIRONMENTS[DEFAULT_ENVIRONMENT];
|
|
171
|
+
const envAuthUrl = process.env[`OCEANET_${env.toUpperCase()}_AUTH_URL`];
|
|
172
|
+
const envApiUrl = process.env[`OCEANET_${env.toUpperCase()}_API_URL`];
|
|
173
|
+
const envClientId = process.env[`OCEANET_${env.toUpperCase()}_CLIENT_ID`];
|
|
174
|
+
const customConfig = config.environments?.[env] || {};
|
|
175
|
+
return {
|
|
176
|
+
name: env,
|
|
177
|
+
displayName: defaultConfig.displayName,
|
|
178
|
+
authBaseUrl: envAuthUrl || customConfig.authBaseUrl || defaultConfig.authBaseUrl,
|
|
179
|
+
apiBaseUrl: envApiUrl || customConfig.apiBaseUrl || defaultConfig.apiBaseUrl,
|
|
180
|
+
clientId: envClientId || customConfig.clientId || defaultConfig.clientId
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function getAllEnvironments() {
|
|
184
|
+
const config = loadConfig();
|
|
185
|
+
const result = { ...ENVIRONMENTS };
|
|
186
|
+
if (config.customEnvironments) {
|
|
187
|
+
for (const [name, customEnv] of Object.entries(config.customEnvironments)) {
|
|
188
|
+
result[name] = {
|
|
189
|
+
name,
|
|
190
|
+
displayName: customEnv.displayName || name,
|
|
191
|
+
authBaseUrl: customEnv.authBaseUrl,
|
|
192
|
+
apiBaseUrl: customEnv.apiBaseUrl,
|
|
193
|
+
clientId: customEnv.clientId
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
function getDefaultEnvironment() {
|
|
200
|
+
const envFromVar = process.env.WUKONG_CLI_ENV;
|
|
201
|
+
if (envFromVar && isValidEnvironment(envFromVar)) {
|
|
202
|
+
return envFromVar;
|
|
203
|
+
}
|
|
204
|
+
const config = loadConfig();
|
|
205
|
+
if (config.defaultEnv && isValidEnvironment(config.defaultEnv)) {
|
|
206
|
+
return config.defaultEnv;
|
|
207
|
+
}
|
|
208
|
+
return DEFAULT_ENVIRONMENT;
|
|
209
|
+
}
|
|
210
|
+
var CONFIG_FILENAMES;
|
|
211
|
+
var init_config_loader = __esm({
|
|
212
|
+
"src/config/config-loader.ts"() {
|
|
213
|
+
"use strict";
|
|
214
|
+
init_esm_shims();
|
|
215
|
+
init_environments();
|
|
216
|
+
CONFIG_FILENAMES = [
|
|
217
|
+
"wukong-cli.json",
|
|
218
|
+
".wukong-cli.json",
|
|
219
|
+
".wukongclirc"
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// src/config/oceanet.ts
|
|
225
|
+
function setCurrentEnvironment(env) {
|
|
226
|
+
currentEnv = env;
|
|
227
|
+
}
|
|
228
|
+
function getCurrentEnvironment() {
|
|
229
|
+
const envFromVar = process.env.WUKONG_CLI_ENV;
|
|
230
|
+
if (envFromVar && isValidEnvironment(envFromVar)) {
|
|
231
|
+
return envFromVar;
|
|
232
|
+
}
|
|
233
|
+
return currentEnv;
|
|
234
|
+
}
|
|
235
|
+
function getEnvironmentConfig() {
|
|
236
|
+
return getMergedEnvironmentConfig(getCurrentEnvironment());
|
|
237
|
+
}
|
|
238
|
+
function getOceanetConfig() {
|
|
239
|
+
const envConfig = getEnvironmentConfig();
|
|
240
|
+
const env = getCurrentEnvironment();
|
|
241
|
+
return {
|
|
242
|
+
// 认证服务基础地址 (设备码授权、登录、登出等)
|
|
243
|
+
AUTH_BASE_URL: getEnv("OCEANET_AUTH_BASE_URL", envConfig.authBaseUrl),
|
|
244
|
+
// 业务 API 基础地址
|
|
245
|
+
API_BASE_URL: getEnv("OCEANET_API_BASE_URL", envConfig.apiBaseUrl),
|
|
246
|
+
// 客户端 ID
|
|
247
|
+
CLIENT_ID: getEnv("OCEANET_CLIENT_ID", envConfig.clientId),
|
|
248
|
+
// Token 存储服务名(不同环境分开存储)
|
|
249
|
+
SERVICE_NAME: `wukong-cli-${env}`,
|
|
250
|
+
// 轮询配置
|
|
251
|
+
POLL: POLL_CONFIG,
|
|
252
|
+
// 认证服务端点
|
|
253
|
+
AUTH_ENDPOINTS: API_ENDPOINTS.AUTH,
|
|
254
|
+
// 业务 API 端点
|
|
255
|
+
API_ENDPOINTS: API_ENDPOINTS.API,
|
|
256
|
+
// 调试模式
|
|
257
|
+
DEBUG: getEnv("WUKONG_CLI_DEBUG", "false") === "true",
|
|
258
|
+
// 当前环境信息
|
|
259
|
+
ENVIRONMENT: env,
|
|
260
|
+
ENVIRONMENT_DISPLAY: envConfig.displayName
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
var currentEnv, getEnv, OCEANET_CONFIG;
|
|
264
|
+
var init_oceanet = __esm({
|
|
265
|
+
"src/config/oceanet.ts"() {
|
|
266
|
+
"use strict";
|
|
267
|
+
init_esm_shims();
|
|
268
|
+
init_config();
|
|
269
|
+
init_environments();
|
|
270
|
+
init_config_loader();
|
|
271
|
+
currentEnv = getDefaultEnvironment();
|
|
272
|
+
getEnv = (key, defaultValue) => {
|
|
273
|
+
return process.env[key] || defaultValue;
|
|
274
|
+
};
|
|
275
|
+
OCEANET_CONFIG = getOceanetConfig();
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// src/core/auth/device-flow-service.ts
|
|
280
|
+
var device_flow_service_exports = {};
|
|
281
|
+
__export(device_flow_service_exports, {
|
|
282
|
+
DeviceFlowService: () => DeviceFlowService,
|
|
283
|
+
getDeviceFlowService: () => getDeviceFlowService
|
|
284
|
+
});
|
|
285
|
+
function getDeviceFlowService() {
|
|
286
|
+
return new DeviceFlowService();
|
|
287
|
+
}
|
|
288
|
+
var DeviceFlowService;
|
|
289
|
+
var init_device_flow_service = __esm({
|
|
290
|
+
"src/core/auth/device-flow-service.ts"() {
|
|
291
|
+
"use strict";
|
|
292
|
+
init_esm_shims();
|
|
293
|
+
init_oceanet();
|
|
294
|
+
DeviceFlowService = class {
|
|
295
|
+
/**
|
|
296
|
+
* 获取设备授权码
|
|
297
|
+
*/
|
|
298
|
+
async getDeviceCode() {
|
|
299
|
+
const config = getOceanetConfig();
|
|
300
|
+
const url = `${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.DEVICE_AUTHORIZE}`;
|
|
301
|
+
const requestBody = {
|
|
302
|
+
param: {
|
|
303
|
+
clientId: config.CLIENT_ID
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
const response = await fetch(url, {
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers: {
|
|
309
|
+
"Content-Type": "application/json"
|
|
310
|
+
},
|
|
311
|
+
body: JSON.stringify(requestBody)
|
|
312
|
+
});
|
|
313
|
+
const data = await response.json();
|
|
314
|
+
if (data.code !== 200) {
|
|
315
|
+
throw new Error(
|
|
316
|
+
`(${data.code}) ${data.message || "Failed to get device code"}`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const { verificationUri, expiresIn, interval } = data.result;
|
|
320
|
+
const urlObj = new URL(verificationUri);
|
|
321
|
+
const deviceCode = urlObj.searchParams.get("code") || "";
|
|
322
|
+
return {
|
|
323
|
+
verificationUri,
|
|
324
|
+
deviceCode,
|
|
325
|
+
expiresIn,
|
|
326
|
+
interval: interval || config.POLL.INTERVAL
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* 轮询获取 token
|
|
331
|
+
*/
|
|
332
|
+
async pollToken(deviceCode) {
|
|
333
|
+
const startTime = Date.now();
|
|
334
|
+
const config = getOceanetConfig();
|
|
335
|
+
while (Date.now() - startTime < config.POLL.TIMEOUT * 1e3) {
|
|
336
|
+
const url = `${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.DEVICE_TOKEN}`;
|
|
337
|
+
const requestBody = {
|
|
338
|
+
param: {
|
|
339
|
+
clientId: config.CLIENT_ID,
|
|
340
|
+
deviceCode
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const response = await fetch(url, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: {
|
|
346
|
+
"Content-Type": "application/json"
|
|
347
|
+
},
|
|
348
|
+
body: JSON.stringify(requestBody)
|
|
349
|
+
});
|
|
350
|
+
const data = await response.json();
|
|
351
|
+
if (data.code !== 200) {
|
|
352
|
+
await new Promise(
|
|
353
|
+
(resolve) => setTimeout(resolve, config.POLL.INTERVAL * 1e3)
|
|
354
|
+
);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (!data.result) {
|
|
358
|
+
await new Promise(
|
|
359
|
+
(resolve) => setTimeout(resolve, config.POLL.INTERVAL * 1e3)
|
|
360
|
+
);
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const {
|
|
364
|
+
access_token,
|
|
365
|
+
refresh_token,
|
|
366
|
+
expires_in,
|
|
367
|
+
token_type,
|
|
368
|
+
scope
|
|
369
|
+
} = data.result;
|
|
370
|
+
return {
|
|
371
|
+
accessToken: access_token,
|
|
372
|
+
refreshToken: refresh_token,
|
|
373
|
+
expiresIn: expires_in,
|
|
374
|
+
tokenType: token_type,
|
|
375
|
+
scope: Array.isArray(scope) ? scope : [scope || ""]
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
throw new Error("Authorization timed out. Please try again.");
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// src/adapters/ora-ui-callbacks.ts
|
|
385
|
+
var ora_ui_callbacks_exports = {};
|
|
386
|
+
__export(ora_ui_callbacks_exports, {
|
|
387
|
+
OraUICallbacks: () => OraUICallbacks
|
|
388
|
+
});
|
|
389
|
+
import ora from "ora";
|
|
390
|
+
var OraUICallbacks;
|
|
391
|
+
var init_ora_ui_callbacks = __esm({
|
|
392
|
+
"src/adapters/ora-ui-callbacks.ts"() {
|
|
393
|
+
"use strict";
|
|
394
|
+
init_esm_shims();
|
|
395
|
+
OraUICallbacks = class {
|
|
396
|
+
spinner = null;
|
|
397
|
+
onStart(message) {
|
|
398
|
+
this.spinner = ora(message).start();
|
|
399
|
+
}
|
|
400
|
+
onSuccess(message) {
|
|
401
|
+
if (this.spinner) {
|
|
402
|
+
this.spinner.succeed(message);
|
|
403
|
+
this.spinner = null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
onError(message) {
|
|
407
|
+
if (this.spinner) {
|
|
408
|
+
this.spinner.fail(message);
|
|
409
|
+
this.spinner = null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
onUpdate(message) {
|
|
413
|
+
if (this.spinner) {
|
|
414
|
+
this.spinner.text = message;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// src/core/auth/keytar-adapter.ts
|
|
422
|
+
import { createRequire } from "module";
|
|
423
|
+
async function setPassword(service, account, password) {
|
|
424
|
+
await keytar.setPassword(service, account, password);
|
|
425
|
+
}
|
|
426
|
+
async function getPassword(service, account) {
|
|
427
|
+
return await keytar.getPassword(service, account);
|
|
428
|
+
}
|
|
429
|
+
async function deletePassword(service, account) {
|
|
430
|
+
return await keytar.deletePassword(service, account);
|
|
431
|
+
}
|
|
432
|
+
var require2, keytar;
|
|
433
|
+
var init_keytar_adapter = __esm({
|
|
434
|
+
"src/core/auth/keytar-adapter.ts"() {
|
|
435
|
+
"use strict";
|
|
436
|
+
init_esm_shims();
|
|
437
|
+
require2 = createRequire(import.meta.url);
|
|
438
|
+
keytar = require2("keytar");
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// src/providers/credential-store.ts
|
|
443
|
+
var credential_store_exports = {};
|
|
444
|
+
__export(credential_store_exports, {
|
|
445
|
+
InMemoryCredentialStore: () => InMemoryCredentialStore,
|
|
446
|
+
KeytarCredentialStore: () => KeytarCredentialStore,
|
|
447
|
+
getCredentialStore: () => getCredentialStore,
|
|
448
|
+
getTestCredentialStore: () => getTestCredentialStore
|
|
449
|
+
});
|
|
450
|
+
function getCredentialStore() {
|
|
451
|
+
return new KeytarCredentialStore();
|
|
452
|
+
}
|
|
453
|
+
function getTestCredentialStore() {
|
|
454
|
+
return new InMemoryCredentialStore();
|
|
455
|
+
}
|
|
456
|
+
var KeytarCredentialStore, InMemoryCredentialStore;
|
|
457
|
+
var init_credential_store = __esm({
|
|
458
|
+
"src/providers/credential-store.ts"() {
|
|
459
|
+
"use strict";
|
|
460
|
+
init_esm_shims();
|
|
461
|
+
init_keytar_adapter();
|
|
462
|
+
KeytarCredentialStore = class {
|
|
463
|
+
async setPassword(service, account, password) {
|
|
464
|
+
await setPassword(service, account, password);
|
|
465
|
+
}
|
|
466
|
+
async getPassword(service, account) {
|
|
467
|
+
return await getPassword(service, account);
|
|
468
|
+
}
|
|
469
|
+
async deletePassword(service, account) {
|
|
470
|
+
return await deletePassword(service, account);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
InMemoryCredentialStore = class {
|
|
474
|
+
store = /* @__PURE__ */ new Map();
|
|
475
|
+
getKey(service, account) {
|
|
476
|
+
return `${service}:${account}`;
|
|
477
|
+
}
|
|
478
|
+
async setPassword(service, account, password) {
|
|
479
|
+
this.store.set(this.getKey(service, account), password);
|
|
480
|
+
}
|
|
481
|
+
async getPassword(service, account) {
|
|
482
|
+
return this.store.get(this.getKey(service, account)) || null;
|
|
483
|
+
}
|
|
484
|
+
async deletePassword(service, account) {
|
|
485
|
+
return this.store.delete(this.getKey(service, account));
|
|
486
|
+
}
|
|
487
|
+
clear() {
|
|
488
|
+
this.store.clear();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// src/core/auth/token-cache.ts
|
|
495
|
+
var token_cache_exports = {};
|
|
496
|
+
__export(token_cache_exports, {
|
|
497
|
+
MemoryTokenCache: () => MemoryTokenCache,
|
|
498
|
+
getGlobalTokenCache: () => getGlobalTokenCache,
|
|
499
|
+
initializeTokenCache: () => initializeTokenCache,
|
|
500
|
+
resetGlobalTokenCache: () => resetGlobalTokenCache
|
|
501
|
+
});
|
|
502
|
+
function getGlobalTokenCache() {
|
|
503
|
+
if (!globalCacheInstance) {
|
|
504
|
+
globalCacheInstance = new MemoryTokenCache();
|
|
505
|
+
}
|
|
506
|
+
return globalCacheInstance;
|
|
507
|
+
}
|
|
508
|
+
function resetGlobalTokenCache() {
|
|
509
|
+
globalCacheInstance = null;
|
|
510
|
+
}
|
|
511
|
+
async function initializeTokenCache(loadTokens) {
|
|
512
|
+
const cache = getGlobalTokenCache();
|
|
513
|
+
const { accessToken, refreshToken } = await loadTokens();
|
|
514
|
+
if (accessToken && refreshToken) {
|
|
515
|
+
cache.setTokens(accessToken, refreshToken);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
var MemoryTokenCache, globalCacheInstance;
|
|
519
|
+
var init_token_cache = __esm({
|
|
520
|
+
"src/core/auth/token-cache.ts"() {
|
|
521
|
+
"use strict";
|
|
522
|
+
init_esm_shims();
|
|
523
|
+
MemoryTokenCache = class {
|
|
524
|
+
accessToken = null;
|
|
525
|
+
refreshToken = null;
|
|
526
|
+
getAccessToken() {
|
|
527
|
+
return this.accessToken;
|
|
528
|
+
}
|
|
529
|
+
getRefreshToken() {
|
|
530
|
+
return this.refreshToken;
|
|
531
|
+
}
|
|
532
|
+
setTokens(accessToken, refreshToken) {
|
|
533
|
+
this.accessToken = accessToken;
|
|
534
|
+
this.refreshToken = refreshToken;
|
|
535
|
+
}
|
|
536
|
+
clear() {
|
|
537
|
+
this.accessToken = null;
|
|
538
|
+
this.refreshToken = null;
|
|
539
|
+
}
|
|
540
|
+
hasAccessToken() {
|
|
541
|
+
return this.accessToken !== null && this.accessToken.length > 0;
|
|
542
|
+
}
|
|
543
|
+
hasRefreshToken() {
|
|
544
|
+
return this.refreshToken !== null && this.refreshToken.length > 0;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
globalCacheInstance = null;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// src/cli.ts
|
|
552
|
+
init_esm_shims();
|
|
553
|
+
import { Command as Command4 } from "commander";
|
|
554
|
+
|
|
555
|
+
// src/utils/debug.ts
|
|
556
|
+
init_esm_shims();
|
|
557
|
+
import chalk from "chalk";
|
|
558
|
+
var debugMode = null;
|
|
559
|
+
var explicitSet = false;
|
|
560
|
+
function setDebugMode(enabled, explicit = true) {
|
|
561
|
+
if (explicit) {
|
|
562
|
+
debugMode = enabled;
|
|
563
|
+
explicitSet = true;
|
|
564
|
+
}
|
|
565
|
+
global.__debugMode = enabled;
|
|
566
|
+
}
|
|
567
|
+
function isDebugMode() {
|
|
568
|
+
if (explicitSet) {
|
|
569
|
+
return debugMode === true;
|
|
570
|
+
}
|
|
571
|
+
if (global.__debugMode === true) {
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
return process.env.WUKONG_CLI_DEBUG === "true";
|
|
575
|
+
}
|
|
576
|
+
function debugRequest(method, url, headers, body) {
|
|
577
|
+
if (!isDebugMode()) return;
|
|
578
|
+
console.log("");
|
|
579
|
+
console.log(chalk.dim("=== HTTP Request ==="));
|
|
580
|
+
console.log(chalk.cyan(`${method} ${url}`));
|
|
581
|
+
if (headers) {
|
|
582
|
+
console.log(chalk.dim("Headers:"));
|
|
583
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
584
|
+
if (key.toLowerCase() === "authorization") {
|
|
585
|
+
const truncated = value.length > 50 ? value.substring(0, 50) + "..." : value;
|
|
586
|
+
console.log(chalk.dim(` ${key}: ${truncated}`));
|
|
587
|
+
} else {
|
|
588
|
+
console.log(chalk.dim(` ${key}: ${value}`));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (body) {
|
|
593
|
+
console.log(chalk.dim("Body:"));
|
|
594
|
+
console.log(chalk.dim(JSON.stringify(body, null, 2)));
|
|
595
|
+
}
|
|
596
|
+
console.log("");
|
|
597
|
+
}
|
|
598
|
+
function debugResponse(status, statusText, body, duration) {
|
|
599
|
+
if (!isDebugMode()) return;
|
|
600
|
+
const statusColor = status >= 200 && status < 300 ? chalk.green : chalk.red;
|
|
601
|
+
console.log(chalk.dim("=== HTTP Response") + chalk.dim(` (${duration}ms) ===`));
|
|
602
|
+
console.log(statusColor(`Status: ${status} ${statusText}`));
|
|
603
|
+
console.log(chalk.dim("Body:"));
|
|
604
|
+
console.log(chalk.dim(JSON.stringify(body, null, 2)));
|
|
605
|
+
console.log("");
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/commands/auth.ts
|
|
609
|
+
init_esm_shims();
|
|
610
|
+
import { Command } from "commander";
|
|
611
|
+
import chalk4 from "chalk";
|
|
612
|
+
import ora3 from "ora";
|
|
613
|
+
|
|
614
|
+
// src/core/auth/device-flow.ts
|
|
615
|
+
init_esm_shims();
|
|
616
|
+
init_oceanet();
|
|
617
|
+
import chalk2 from "chalk";
|
|
618
|
+
|
|
619
|
+
// src/adapters/cli-device-flow.ts
|
|
620
|
+
init_esm_shims();
|
|
621
|
+
var CliDeviceFlowAdapter = class {
|
|
622
|
+
/**
|
|
623
|
+
* 构造函数
|
|
624
|
+
* @param deviceFlow 设备流程服务实例
|
|
625
|
+
* @param ui UI 回调实例
|
|
626
|
+
*/
|
|
627
|
+
constructor(deviceFlow, ui) {
|
|
628
|
+
this.deviceFlow = deviceFlow;
|
|
629
|
+
this.ui = ui;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* 获取设备授权码(带 UI)
|
|
633
|
+
*/
|
|
634
|
+
async getDeviceCode() {
|
|
635
|
+
this.ui.onStart("Getting device authorization...");
|
|
636
|
+
try {
|
|
637
|
+
const result = await this.deviceFlow.getDeviceCode();
|
|
638
|
+
this.ui.onSuccess("Device code obtained");
|
|
639
|
+
return result;
|
|
640
|
+
} catch (error) {
|
|
641
|
+
this.ui.onError("Failed to get device code");
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 轮询获取 token(带 UI)
|
|
647
|
+
*/
|
|
648
|
+
async pollToken(deviceCode) {
|
|
649
|
+
this.ui.onStart("Waiting for authorization...");
|
|
650
|
+
try {
|
|
651
|
+
const result = await this.deviceFlow.pollToken(deviceCode);
|
|
652
|
+
this.ui.onSuccess("Authorization successful!");
|
|
653
|
+
return result;
|
|
654
|
+
} catch (error) {
|
|
655
|
+
this.ui.onError("Authorization error");
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
function createCliDeviceFlowAdapter() {
|
|
661
|
+
const { getDeviceFlowService: getDeviceFlowService2 } = (init_device_flow_service(), __toCommonJS(device_flow_service_exports));
|
|
662
|
+
const { OraUICallbacks: OraUICallbacks2 } = (init_ora_ui_callbacks(), __toCommonJS(ora_ui_callbacks_exports));
|
|
663
|
+
const deviceFlow = getDeviceFlowService2();
|
|
664
|
+
const ui = new OraUICallbacks2();
|
|
665
|
+
return new CliDeviceFlowAdapter(deviceFlow, ui);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// src/core/auth/device-flow.ts
|
|
669
|
+
init_device_flow_service();
|
|
670
|
+
|
|
671
|
+
// src/adapters/ui-callbacks.ts
|
|
672
|
+
init_esm_shims();
|
|
673
|
+
|
|
674
|
+
// src/core/auth/device-flow.ts
|
|
675
|
+
init_ora_ui_callbacks();
|
|
676
|
+
async function getDeviceCode() {
|
|
677
|
+
const config = getOceanetConfig();
|
|
678
|
+
const adapter = createCliDeviceFlowAdapter();
|
|
679
|
+
if (config.DEBUG) {
|
|
680
|
+
console.log("");
|
|
681
|
+
console.log(chalk2.dim("=== Debug Info ==="));
|
|
682
|
+
console.log(chalk2.dim("Getting device authorization..."));
|
|
683
|
+
console.log(chalk2.dim("Client ID:"), chalk2.yellow(config.CLIENT_ID));
|
|
684
|
+
console.log("");
|
|
685
|
+
}
|
|
686
|
+
const result = await adapter.getDeviceCode();
|
|
687
|
+
if (config.DEBUG) {
|
|
688
|
+
console.log(chalk2.dim("=== Response ==="));
|
|
689
|
+
console.log(chalk2.dim("Device Code:"), chalk2.yellow(result.deviceCode));
|
|
690
|
+
console.log(chalk2.dim("Verification URI:"), chalk2.cyan(result.verificationUri));
|
|
691
|
+
console.log(chalk2.dim("Expires In:"), chalk2.yellow(result.expiresIn));
|
|
692
|
+
console.log("");
|
|
693
|
+
}
|
|
694
|
+
return result;
|
|
695
|
+
}
|
|
696
|
+
async function pollToken(deviceCode) {
|
|
697
|
+
const adapter = createCliDeviceFlowAdapter();
|
|
698
|
+
return adapter.pollToken(deviceCode);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/core/auth/token-manager.ts
|
|
702
|
+
init_esm_shims();
|
|
703
|
+
init_oceanet();
|
|
704
|
+
import ora2 from "ora";
|
|
705
|
+
var TokenManager = class {
|
|
706
|
+
/**
|
|
707
|
+
* 构造函数
|
|
708
|
+
* @param credentialStore 凭据存储实例
|
|
709
|
+
* @param tokenCache token 缓存实例
|
|
710
|
+
*/
|
|
711
|
+
constructor(credentialStore, tokenCache) {
|
|
712
|
+
this.credentialStore = credentialStore;
|
|
713
|
+
this.tokenCache = tokenCache;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* 保存 Token
|
|
717
|
+
*/
|
|
718
|
+
async saveToken(accessToken, refreshToken) {
|
|
719
|
+
const config = getOceanetConfig();
|
|
720
|
+
await this.credentialStore.setPassword(config.SERVICE_NAME, "access_token", accessToken);
|
|
721
|
+
await this.credentialStore.setPassword(config.SERVICE_NAME, "refresh_token", refreshToken);
|
|
722
|
+
this.tokenCache.setTokens(accessToken, refreshToken);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* 获取 Access Token
|
|
726
|
+
*/
|
|
727
|
+
async getAccessToken() {
|
|
728
|
+
const cached = this.tokenCache.getAccessToken();
|
|
729
|
+
if (cached) {
|
|
730
|
+
return cached;
|
|
731
|
+
}
|
|
732
|
+
const config = getOceanetConfig();
|
|
733
|
+
const token = await this.credentialStore.getPassword(config.SERVICE_NAME, "access_token");
|
|
734
|
+
if (token) {
|
|
735
|
+
this.tokenCache.setTokens(token, null);
|
|
736
|
+
}
|
|
737
|
+
return token;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* 获取 Refresh Token
|
|
741
|
+
*/
|
|
742
|
+
async getRefreshToken() {
|
|
743
|
+
const cached = this.tokenCache.getRefreshToken();
|
|
744
|
+
if (cached) {
|
|
745
|
+
return cached;
|
|
746
|
+
}
|
|
747
|
+
const config = getOceanetConfig();
|
|
748
|
+
const token = await this.credentialStore.getPassword(config.SERVICE_NAME, "refresh_token");
|
|
749
|
+
if (token) {
|
|
750
|
+
this.tokenCache.setTokens(null, token);
|
|
751
|
+
}
|
|
752
|
+
return token;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* 刷新 Token
|
|
756
|
+
*/
|
|
757
|
+
async refreshAccessToken(refreshToken) {
|
|
758
|
+
const spinner = ora2("Refreshing access token...").start();
|
|
759
|
+
try {
|
|
760
|
+
const config = getOceanetConfig();
|
|
761
|
+
const url = `${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.REFRESH_TOKEN}`;
|
|
762
|
+
const response = await fetch(url, {
|
|
763
|
+
method: "POST",
|
|
764
|
+
headers: {
|
|
765
|
+
"Content-Type": "application/json"
|
|
766
|
+
},
|
|
767
|
+
body: JSON.stringify({ param: refreshToken })
|
|
768
|
+
});
|
|
769
|
+
const data = await response.json();
|
|
770
|
+
if (data.code !== 200) {
|
|
771
|
+
spinner.fail("Token refresh failed");
|
|
772
|
+
throw new Error(data.message || "Refresh token failed");
|
|
773
|
+
}
|
|
774
|
+
const {
|
|
775
|
+
access_token,
|
|
776
|
+
refresh_token: new_refresh_token,
|
|
777
|
+
expires_in,
|
|
778
|
+
token_type,
|
|
779
|
+
scope
|
|
780
|
+
} = data.result;
|
|
781
|
+
spinner.succeed("Token refreshed");
|
|
782
|
+
return {
|
|
783
|
+
accessToken: access_token,
|
|
784
|
+
refreshToken: new_refresh_token,
|
|
785
|
+
expiresIn: expires_in,
|
|
786
|
+
tokenType: token_type,
|
|
787
|
+
scope: Array.isArray(scope) ? scope : [scope || ""]
|
|
788
|
+
};
|
|
789
|
+
} catch (error) {
|
|
790
|
+
spinner.fail("Token refresh error");
|
|
791
|
+
throw error;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* 清除本地 Token
|
|
796
|
+
*/
|
|
797
|
+
async clearTokens() {
|
|
798
|
+
const config = getOceanetConfig();
|
|
799
|
+
await this.credentialStore.deletePassword(config.SERVICE_NAME, "access_token");
|
|
800
|
+
await this.credentialStore.deletePassword(config.SERVICE_NAME, "refresh_token");
|
|
801
|
+
this.tokenCache.clear();
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* 退出登录
|
|
805
|
+
*/
|
|
806
|
+
async logout(accessToken) {
|
|
807
|
+
try {
|
|
808
|
+
const config = getOceanetConfig();
|
|
809
|
+
await fetch(`${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.LOGOUT}`, {
|
|
810
|
+
method: "POST",
|
|
811
|
+
headers: {
|
|
812
|
+
"Content-Type": "application/json",
|
|
813
|
+
"Authorization": `Bearer ${accessToken}`
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
} catch (error) {
|
|
817
|
+
}
|
|
818
|
+
await this.clearTokens();
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* 初始化 Token 缓存
|
|
822
|
+
* 从持久化存储加载 token 到内存缓存
|
|
823
|
+
*/
|
|
824
|
+
async initTokenCache() {
|
|
825
|
+
const config = getOceanetConfig();
|
|
826
|
+
const [accessToken, refreshToken] = await Promise.all([
|
|
827
|
+
this.credentialStore.getPassword(config.SERVICE_NAME, "access_token"),
|
|
828
|
+
this.credentialStore.getPassword(config.SERVICE_NAME, "refresh_token")
|
|
829
|
+
]);
|
|
830
|
+
if (accessToken || refreshToken) {
|
|
831
|
+
this.tokenCache.setTokens(accessToken || null, refreshToken || null);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
var tokenManagerInstance = null;
|
|
836
|
+
function getTokenManager() {
|
|
837
|
+
if (!tokenManagerInstance) {
|
|
838
|
+
const { getCredentialStore: getCredentialStore2 } = (init_credential_store(), __toCommonJS(credential_store_exports));
|
|
839
|
+
const { getGlobalTokenCache: getGlobalTokenCache2 } = (init_token_cache(), __toCommonJS(token_cache_exports));
|
|
840
|
+
const credentialStore = getCredentialStore2();
|
|
841
|
+
const tokenCache = getGlobalTokenCache2();
|
|
842
|
+
tokenManagerInstance = new TokenManager(credentialStore, tokenCache);
|
|
843
|
+
}
|
|
844
|
+
return tokenManagerInstance;
|
|
845
|
+
}
|
|
846
|
+
async function saveToken(accessToken, refreshToken) {
|
|
847
|
+
const manager = getTokenManager();
|
|
848
|
+
await manager.saveToken(accessToken, refreshToken);
|
|
849
|
+
}
|
|
850
|
+
async function getAccessToken() {
|
|
851
|
+
const manager = getTokenManager();
|
|
852
|
+
return await manager.getAccessToken();
|
|
853
|
+
}
|
|
854
|
+
async function logout(accessToken) {
|
|
855
|
+
const manager = getTokenManager();
|
|
856
|
+
await manager.logout(accessToken);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/core/http/client.ts
|
|
860
|
+
init_esm_shims();
|
|
861
|
+
|
|
862
|
+
// src/core/http/base-http-client.ts
|
|
863
|
+
init_esm_shims();
|
|
864
|
+
init_oceanet();
|
|
865
|
+
function buildUrl(endpoint, params) {
|
|
866
|
+
if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
|
|
867
|
+
let url2 = endpoint;
|
|
868
|
+
if (params) {
|
|
869
|
+
const searchParams = new URLSearchParams(params);
|
|
870
|
+
const separator = url2.includes("?") ? "&" : "?";
|
|
871
|
+
url2 += `${separator}${searchParams.toString()}`;
|
|
872
|
+
}
|
|
873
|
+
return url2;
|
|
874
|
+
}
|
|
875
|
+
const config = getOceanetConfig();
|
|
876
|
+
let url = `${config.API_BASE_URL}${endpoint}`;
|
|
877
|
+
if (params) {
|
|
878
|
+
const searchParams = new URLSearchParams(params);
|
|
879
|
+
url += `?${searchParams.toString()}`;
|
|
880
|
+
}
|
|
881
|
+
return url;
|
|
882
|
+
}
|
|
883
|
+
var BaseHttpClient = class {
|
|
884
|
+
/**
|
|
885
|
+
* 发起 HTTP 请求
|
|
886
|
+
*/
|
|
887
|
+
async request(endpoint, options = {}) {
|
|
888
|
+
const {
|
|
889
|
+
method = "GET",
|
|
890
|
+
headers = {},
|
|
891
|
+
body,
|
|
892
|
+
params
|
|
893
|
+
} = options;
|
|
894
|
+
const url = buildUrl(endpoint, params);
|
|
895
|
+
const requestHeaders = {
|
|
896
|
+
...headers,
|
|
897
|
+
"Content-Type": "application/json"
|
|
898
|
+
};
|
|
899
|
+
debugRequest(method, url, requestHeaders, body);
|
|
900
|
+
const startTime = Date.now();
|
|
901
|
+
const response = await fetch(url, {
|
|
902
|
+
method,
|
|
903
|
+
headers: requestHeaders,
|
|
904
|
+
body: body ? JSON.stringify(body) : void 0
|
|
905
|
+
});
|
|
906
|
+
const duration = Date.now() - startTime;
|
|
907
|
+
const data = await response.json();
|
|
908
|
+
debugResponse(response.status, response.statusText, data, duration);
|
|
909
|
+
return data;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* GET 请求
|
|
913
|
+
*/
|
|
914
|
+
async get(endpoint, params) {
|
|
915
|
+
return this.request(endpoint, { method: "GET", params });
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* POST 请求
|
|
919
|
+
*/
|
|
920
|
+
async post(endpoint, data, headers) {
|
|
921
|
+
return this.request(endpoint, {
|
|
922
|
+
method: "POST",
|
|
923
|
+
body: data,
|
|
924
|
+
headers
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* PUT 请求
|
|
929
|
+
*/
|
|
930
|
+
async put(endpoint, data, headers) {
|
|
931
|
+
return this.request(endpoint, {
|
|
932
|
+
method: "PUT",
|
|
933
|
+
body: data,
|
|
934
|
+
headers
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* DELETE 请求
|
|
939
|
+
*/
|
|
940
|
+
async delete(endpoint) {
|
|
941
|
+
return this.request(endpoint, { method: "DELETE" });
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* PATCH 请求
|
|
945
|
+
*/
|
|
946
|
+
async patch(endpoint, data, headers) {
|
|
947
|
+
return this.request(endpoint, {
|
|
948
|
+
method: "PATCH",
|
|
949
|
+
body: data,
|
|
950
|
+
headers
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
// src/core/http/authenticating-http-client.ts
|
|
956
|
+
init_esm_shims();
|
|
957
|
+
|
|
958
|
+
// src/core/http/api-error-handler.ts
|
|
959
|
+
init_esm_shims();
|
|
960
|
+
import chalk3 from "chalk";
|
|
961
|
+
var ApiError = class extends Error {
|
|
962
|
+
constructor(code, message, retryable = false) {
|
|
963
|
+
super(message);
|
|
964
|
+
this.code = code;
|
|
965
|
+
this.retryable = retryable;
|
|
966
|
+
this.name = "ApiError";
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
var ApiErrorHandler = class {
|
|
970
|
+
errorHandlers = /* @__PURE__ */ new Map();
|
|
971
|
+
constructor() {
|
|
972
|
+
this.registerDefaultHandlers();
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* 注册默认的 Oceanet 错误处理器
|
|
976
|
+
*/
|
|
977
|
+
registerDefaultHandlers() {
|
|
978
|
+
this.register(9913, (response) => {
|
|
979
|
+
const error = new ApiError(
|
|
980
|
+
response.code,
|
|
981
|
+
response.message || "Token expired or invalid",
|
|
982
|
+
true
|
|
983
|
+
// 可重试
|
|
984
|
+
);
|
|
985
|
+
throw error;
|
|
986
|
+
});
|
|
987
|
+
[401, 403].forEach((code) => {
|
|
988
|
+
this.register(code, (response) => {
|
|
989
|
+
throw new ApiError(
|
|
990
|
+
response.code,
|
|
991
|
+
response.message || "Authentication failed",
|
|
992
|
+
code === 401
|
|
993
|
+
// 401 可以尝试刷新 token
|
|
994
|
+
);
|
|
995
|
+
});
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* 注册错误处理器
|
|
1000
|
+
* @param code 错误码
|
|
1001
|
+
* @param handler 处理函数
|
|
1002
|
+
*/
|
|
1003
|
+
register(code, handler) {
|
|
1004
|
+
this.errorHandlers.set(code, handler);
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* 处理 API 响应
|
|
1008
|
+
* @param response API 响应
|
|
1009
|
+
* @throws {ApiError} 当响应包含错误时
|
|
1010
|
+
*/
|
|
1011
|
+
handle(response) {
|
|
1012
|
+
if (response.code !== 200) {
|
|
1013
|
+
const handler = this.errorHandlers.get(response.code);
|
|
1014
|
+
if (handler) {
|
|
1015
|
+
handler(response);
|
|
1016
|
+
}
|
|
1017
|
+
throw new ApiError(
|
|
1018
|
+
response.code,
|
|
1019
|
+
response.message || "Unknown API error"
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* 检查响应是否为错误
|
|
1025
|
+
* @param response API 响应
|
|
1026
|
+
* @returns 是否为错误
|
|
1027
|
+
*/
|
|
1028
|
+
isError(response) {
|
|
1029
|
+
return response.code !== 200;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* 尝试从错误中恢复
|
|
1033
|
+
* @param error 错误对象
|
|
1034
|
+
* @param recoverFunction 恢复函数
|
|
1035
|
+
* @returns 恢复结果或抛出错误
|
|
1036
|
+
*/
|
|
1037
|
+
async tryRecover(error, recoverFunction) {
|
|
1038
|
+
if (error instanceof ApiError && error.retryable) {
|
|
1039
|
+
console.log("");
|
|
1040
|
+
console.log(chalk3.yellow("\u26A0 Token expired, attempting refresh..."));
|
|
1041
|
+
try {
|
|
1042
|
+
return await recoverFunction();
|
|
1043
|
+
} catch (refreshError) {
|
|
1044
|
+
console.log("");
|
|
1045
|
+
console.log(chalk3.yellow("\u26A0 Auto-refresh failed:"), chalk3.dim(refreshError.message));
|
|
1046
|
+
throw new ApiError(
|
|
1047
|
+
error.code,
|
|
1048
|
+
`${error.message} (Auto-refresh failed)`
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
throw error;
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
var errorHandlerInstance = null;
|
|
1056
|
+
function getApiErrorHandler() {
|
|
1057
|
+
if (!errorHandlerInstance) {
|
|
1058
|
+
errorHandlerInstance = new ApiErrorHandler();
|
|
1059
|
+
}
|
|
1060
|
+
return errorHandlerInstance;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/core/http/interceptors.ts
|
|
1064
|
+
init_esm_shims();
|
|
1065
|
+
init_oceanet();
|
|
1066
|
+
import { createRetoken } from "ts-retoken";
|
|
1067
|
+
function createRetokenInstance(credentialStore, tokenCache) {
|
|
1068
|
+
const config = getOceanetConfig();
|
|
1069
|
+
const store = credentialStore || (init_credential_store(), __toCommonJS(credential_store_exports)).getCredentialStore();
|
|
1070
|
+
const cache = tokenCache || (init_token_cache(), __toCommonJS(token_cache_exports)).getGlobalTokenCache();
|
|
1071
|
+
return createRetoken({
|
|
1072
|
+
refreshEndpoint: {
|
|
1073
|
+
url: `${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.REFRESH_TOKEN}`,
|
|
1074
|
+
method: "POST",
|
|
1075
|
+
// Oceanet API 要求: { "param": "refresh_token_string" }
|
|
1076
|
+
buildBody: (token) => JSON.stringify({ param: token }),
|
|
1077
|
+
headers: {
|
|
1078
|
+
"Content-Type": "application/json"
|
|
1079
|
+
},
|
|
1080
|
+
parseResponse: (data) => {
|
|
1081
|
+
if (config.DEBUG || global.__debugMode) {
|
|
1082
|
+
console.log("");
|
|
1083
|
+
console.log("=== HTTP Request ===");
|
|
1084
|
+
console.log(`POST ${config.AUTH_BASE_URL}${config.AUTH_ENDPOINTS.REFRESH_TOKEN}`);
|
|
1085
|
+
console.log("Headers:");
|
|
1086
|
+
console.log(" Content-Type: application/json");
|
|
1087
|
+
console.log("Body:");
|
|
1088
|
+
const refreshToken = cache.getRefreshToken();
|
|
1089
|
+
const truncated = refreshToken && refreshToken.length > 50 ? refreshToken.substring(0, 50) + "..." : refreshToken || "(none)";
|
|
1090
|
+
console.log(` {"param":"${truncated}"}`);
|
|
1091
|
+
console.log("");
|
|
1092
|
+
console.log("=== HTTP Response ===");
|
|
1093
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1094
|
+
console.log("");
|
|
1095
|
+
}
|
|
1096
|
+
if (data.code === 401 || data.code === 403) {
|
|
1097
|
+
const error = new Error(data.message || "Refresh token expired or invalid");
|
|
1098
|
+
error.code = data.code;
|
|
1099
|
+
error.isAuthFailure = true;
|
|
1100
|
+
throw error;
|
|
1101
|
+
}
|
|
1102
|
+
const result = data.result || data;
|
|
1103
|
+
if (!result.access_token && !result.accessToken) {
|
|
1104
|
+
throw new Error("Invalid token response: missing access_token field");
|
|
1105
|
+
}
|
|
1106
|
+
if (!result.refresh_token && !result.refreshToken) {
|
|
1107
|
+
throw new Error("Invalid token response: missing refresh_token field");
|
|
1108
|
+
}
|
|
1109
|
+
return {
|
|
1110
|
+
accessToken: result.access_token || result.accessToken,
|
|
1111
|
+
refreshToken: result.refresh_token || result.refreshToken
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
// 从缓存获取 token(同步函数)
|
|
1116
|
+
getAccessToken: () => {
|
|
1117
|
+
return cache.getAccessToken();
|
|
1118
|
+
},
|
|
1119
|
+
getRefreshToken: () => {
|
|
1120
|
+
return cache.getRefreshToken();
|
|
1121
|
+
},
|
|
1122
|
+
// 保存 token 到持久化存储和缓存(异步)
|
|
1123
|
+
setTokens: async (tokens) => {
|
|
1124
|
+
const accessToken = tokens?.accessToken || tokens?.access_token;
|
|
1125
|
+
const refreshToken = tokens?.refreshToken || tokens?.refresh_token;
|
|
1126
|
+
if (!accessToken || !refreshToken) {
|
|
1127
|
+
throw new Error("Invalid tokens: missing access_token or refresh_token");
|
|
1128
|
+
}
|
|
1129
|
+
const cfg = getOceanetConfig();
|
|
1130
|
+
await store.setPassword(cfg.SERVICE_NAME, "access_token", accessToken);
|
|
1131
|
+
await store.setPassword(cfg.SERVICE_NAME, "refresh_token", refreshToken);
|
|
1132
|
+
cache.setTokens(accessToken, refreshToken);
|
|
1133
|
+
},
|
|
1134
|
+
// 清除 token
|
|
1135
|
+
clearTokens: async () => {
|
|
1136
|
+
const cfg = getOceanetConfig();
|
|
1137
|
+
await store.deletePassword(cfg.SERVICE_NAME, "access_token");
|
|
1138
|
+
await store.deletePassword(cfg.SERVICE_NAME, "refresh_token");
|
|
1139
|
+
cache.clear();
|
|
1140
|
+
},
|
|
1141
|
+
// 提前 1 分钟(60 秒)主动刷新
|
|
1142
|
+
expirationLeeway: 60,
|
|
1143
|
+
// 401 时触发重试
|
|
1144
|
+
retryStatuses: [401],
|
|
1145
|
+
// 认证完全失败时的回调
|
|
1146
|
+
onAuthFailure: async () => {
|
|
1147
|
+
const cfg = getOceanetConfig();
|
|
1148
|
+
try {
|
|
1149
|
+
await store.deletePassword(cfg.SERVICE_NAME, "access_token");
|
|
1150
|
+
await store.deletePassword(cfg.SERVICE_NAME, "refresh_token");
|
|
1151
|
+
cache.clear();
|
|
1152
|
+
} catch {
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
var retokenInstances = {};
|
|
1158
|
+
function getRetoken() {
|
|
1159
|
+
const config = getOceanetConfig();
|
|
1160
|
+
const env = config.ENVIRONMENT;
|
|
1161
|
+
if (!retokenInstances[env]) {
|
|
1162
|
+
retokenInstances[env] = createRetokenInstance();
|
|
1163
|
+
}
|
|
1164
|
+
return retokenInstances[env];
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// src/core/http/authenticating-http-client.ts
|
|
1168
|
+
var AuthenticatingHttpClient = class {
|
|
1169
|
+
constructor(baseClient, tokenCache) {
|
|
1170
|
+
this.baseClient = baseClient;
|
|
1171
|
+
this.tokenCache = tokenCache;
|
|
1172
|
+
}
|
|
1173
|
+
initialized = false;
|
|
1174
|
+
/**
|
|
1175
|
+
* 确保 token 缓存已初始化
|
|
1176
|
+
*/
|
|
1177
|
+
async ensureInitialized() {
|
|
1178
|
+
if (!this.initialized) {
|
|
1179
|
+
const tokenManager = getTokenManager();
|
|
1180
|
+
await tokenManager.initTokenCache();
|
|
1181
|
+
this.initialized = true;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* 发起认证的 HTTP 请求
|
|
1186
|
+
*/
|
|
1187
|
+
async request(endpoint, options = {}) {
|
|
1188
|
+
await this.ensureInitialized();
|
|
1189
|
+
const accessToken = this.tokenCache.getAccessToken();
|
|
1190
|
+
if (!accessToken) {
|
|
1191
|
+
throw new Error(
|
|
1192
|
+
"Not authenticated. Please run: wukong-cli auth login"
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
const headers = {
|
|
1196
|
+
...options.headers,
|
|
1197
|
+
"Authorization": `Bearer ${accessToken}`
|
|
1198
|
+
};
|
|
1199
|
+
const url = `${global.__apiBaseUrl || ""}${endpoint}`;
|
|
1200
|
+
debugRequest(options.method || "GET", url, headers, options.body);
|
|
1201
|
+
const retoken = getRetoken();
|
|
1202
|
+
const startTime = Date.now();
|
|
1203
|
+
const response = await retoken.fetch(
|
|
1204
|
+
url,
|
|
1205
|
+
{
|
|
1206
|
+
method: options.method || "GET",
|
|
1207
|
+
headers,
|
|
1208
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
1209
|
+
}
|
|
1210
|
+
);
|
|
1211
|
+
const data = await response.json();
|
|
1212
|
+
const duration = Date.now() - startTime;
|
|
1213
|
+
debugResponse(response.status, response.statusText, data, duration);
|
|
1214
|
+
const errorHandler = getApiErrorHandler();
|
|
1215
|
+
if (data.code === 9913) {
|
|
1216
|
+
return errorHandler.tryRecover(
|
|
1217
|
+
new ApiError(data.code, data.message || "Token expired", true),
|
|
1218
|
+
async () => {
|
|
1219
|
+
await retoken.refreshToken();
|
|
1220
|
+
const newAccessToken = this.tokenCache.getAccessToken();
|
|
1221
|
+
if (newAccessToken) {
|
|
1222
|
+
const retryResponse = await fetch(
|
|
1223
|
+
`${global.__apiBaseUrl || ""}${endpoint}`,
|
|
1224
|
+
{
|
|
1225
|
+
method: options.method || "GET",
|
|
1226
|
+
headers: {
|
|
1227
|
+
...headers,
|
|
1228
|
+
"Authorization": `Bearer ${newAccessToken}`
|
|
1229
|
+
},
|
|
1230
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
1231
|
+
}
|
|
1232
|
+
);
|
|
1233
|
+
const retryData = await retryResponse.json();
|
|
1234
|
+
if (retryData.code === 200) {
|
|
1235
|
+
return retryData.result;
|
|
1236
|
+
}
|
|
1237
|
+
throw new Error(
|
|
1238
|
+
`API Error (${retryData.code}): ${retryData.message || "Unknown error"}`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
throw new Error("Failed to refresh token");
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
errorHandler.handle(data);
|
|
1246
|
+
return data.result;
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* GET 请求
|
|
1250
|
+
*/
|
|
1251
|
+
async get(endpoint, params) {
|
|
1252
|
+
return this.request(endpoint, { method: "GET", params });
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* POST 请求
|
|
1256
|
+
*/
|
|
1257
|
+
async post(endpoint, data, headers) {
|
|
1258
|
+
return this.request(endpoint, {
|
|
1259
|
+
method: "POST",
|
|
1260
|
+
body: data,
|
|
1261
|
+
headers
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* PUT 请求
|
|
1266
|
+
*/
|
|
1267
|
+
async put(endpoint, data, headers) {
|
|
1268
|
+
return this.request(endpoint, {
|
|
1269
|
+
method: "PUT",
|
|
1270
|
+
body: data,
|
|
1271
|
+
headers
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* DELETE 请求
|
|
1276
|
+
*/
|
|
1277
|
+
async delete(endpoint) {
|
|
1278
|
+
return this.request(endpoint, { method: "DELETE" });
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* PATCH 请求
|
|
1282
|
+
*/
|
|
1283
|
+
async patch(endpoint, data, headers) {
|
|
1284
|
+
return this.request(endpoint, {
|
|
1285
|
+
method: "PATCH",
|
|
1286
|
+
body: data,
|
|
1287
|
+
headers
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* 检查 token 是否即将过期
|
|
1292
|
+
*/
|
|
1293
|
+
async isTokenExpiringSoon() {
|
|
1294
|
+
await this.ensureInitialized();
|
|
1295
|
+
const retoken = getRetoken();
|
|
1296
|
+
return retoken.isTokenExpiringSoon();
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* 手动刷新 token
|
|
1300
|
+
*/
|
|
1301
|
+
async refreshToken() {
|
|
1302
|
+
await this.ensureInitialized();
|
|
1303
|
+
const retoken = getRetoken();
|
|
1304
|
+
await retoken.refreshToken();
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
// src/core/http/client.ts
|
|
1309
|
+
init_oceanet();
|
|
1310
|
+
var OceanetClient = class {
|
|
1311
|
+
authenticatingClient;
|
|
1312
|
+
/**
|
|
1313
|
+
* 构造函数
|
|
1314
|
+
* @param tokenCache token 缓存实例(可选)
|
|
1315
|
+
*/
|
|
1316
|
+
constructor(tokenCache) {
|
|
1317
|
+
let cache;
|
|
1318
|
+
if (tokenCache) {
|
|
1319
|
+
cache = tokenCache;
|
|
1320
|
+
} else {
|
|
1321
|
+
const { getGlobalTokenCache: getGlobalTokenCache2 } = (init_token_cache(), __toCommonJS(token_cache_exports));
|
|
1322
|
+
cache = getGlobalTokenCache2();
|
|
1323
|
+
}
|
|
1324
|
+
const baseClient = new BaseHttpClient();
|
|
1325
|
+
this.authenticatingClient = new AuthenticatingHttpClient(baseClient, cache);
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* 发起 HTTP 请求
|
|
1329
|
+
*/
|
|
1330
|
+
async request(endpoint, options) {
|
|
1331
|
+
return this.authenticatingClient.request(endpoint, options);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* GET 请求
|
|
1335
|
+
*/
|
|
1336
|
+
async get(endpoint, params) {
|
|
1337
|
+
return this.authenticatingClient.get(endpoint, params);
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* POST 请求
|
|
1341
|
+
*/
|
|
1342
|
+
async post(endpoint, data, headers) {
|
|
1343
|
+
return this.authenticatingClient.post(endpoint, data, headers);
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* PUT 请求
|
|
1347
|
+
*/
|
|
1348
|
+
async put(endpoint, data, headers) {
|
|
1349
|
+
return this.authenticatingClient.put(endpoint, data, headers);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* DELETE 请求
|
|
1353
|
+
*/
|
|
1354
|
+
async delete(endpoint) {
|
|
1355
|
+
return this.authenticatingClient.delete(endpoint);
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* PATCH 请求
|
|
1359
|
+
*/
|
|
1360
|
+
async patch(endpoint, data, headers) {
|
|
1361
|
+
return this.authenticatingClient.patch(endpoint, data, headers);
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* 检查 token 是否即将过期
|
|
1365
|
+
*/
|
|
1366
|
+
async isTokenExpiringSoon() {
|
|
1367
|
+
return this.authenticatingClient.isTokenExpiringSoon();
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* 手动刷新 token
|
|
1371
|
+
*/
|
|
1372
|
+
async refreshToken() {
|
|
1373
|
+
return this.authenticatingClient.refreshToken();
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
var clientInstance = null;
|
|
1377
|
+
var lastEnvironment = null;
|
|
1378
|
+
function clearTokenCache() {
|
|
1379
|
+
const { getGlobalTokenCache: getGlobalTokenCache2 } = (init_token_cache(), __toCommonJS(token_cache_exports));
|
|
1380
|
+
const cache = getGlobalTokenCache2();
|
|
1381
|
+
cache.clear();
|
|
1382
|
+
}
|
|
1383
|
+
function getClient() {
|
|
1384
|
+
const currentEnv2 = getOceanetConfig().ENVIRONMENT;
|
|
1385
|
+
if (!clientInstance || lastEnvironment !== currentEnv2) {
|
|
1386
|
+
clearTokenCache();
|
|
1387
|
+
clientInstance = new OceanetClient();
|
|
1388
|
+
lastEnvironment = currentEnv2;
|
|
1389
|
+
}
|
|
1390
|
+
return clientInstance;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// src/commands/auth.ts
|
|
1394
|
+
init_oceanet();
|
|
1395
|
+
init_config_loader();
|
|
1396
|
+
var authCommands = new Command("auth").description("Authentication commands");
|
|
1397
|
+
authCommands.command("login").description("Login using Device Authorization Flow").action(async () => {
|
|
1398
|
+
try {
|
|
1399
|
+
const env = getCurrentEnvironment();
|
|
1400
|
+
const allEnvs = getAllEnvironments();
|
|
1401
|
+
setCurrentEnvironment(env);
|
|
1402
|
+
const config = getOceanetConfig();
|
|
1403
|
+
const envConfig = allEnvs[env];
|
|
1404
|
+
console.log("");
|
|
1405
|
+
console.log(chalk4.bgBlue.white.bold(" Wukong CLI Login "));
|
|
1406
|
+
console.log("");
|
|
1407
|
+
console.log(chalk4.dim(`Environment: ${chalk4.cyan(env)} - ${envConfig.displayName}`));
|
|
1408
|
+
console.log(chalk4.dim(`Auth URL: ${config.AUTH_BASE_URL}`));
|
|
1409
|
+
console.log(chalk4.dim(`API URL: ${config.API_BASE_URL}`));
|
|
1410
|
+
console.log(chalk4.dim(`Client ID: ${config.CLIENT_ID}`));
|
|
1411
|
+
console.log("");
|
|
1412
|
+
const { verificationUri, deviceCode, expiresIn, interval } = await getDeviceCode();
|
|
1413
|
+
console.log("");
|
|
1414
|
+
console.log(chalk4.bold("\u2550".repeat(50)));
|
|
1415
|
+
console.log(chalk4.bold(" \u8BF7\u5B8C\u6210\u6388\u6743 / Please complete authorization"));
|
|
1416
|
+
console.log(chalk4.bold("\u2550".repeat(50)));
|
|
1417
|
+
console.log("");
|
|
1418
|
+
console.log(chalk4.green(" \u6388\u6743\u94FE\u63A5 / Authorization URL:"));
|
|
1419
|
+
console.log("");
|
|
1420
|
+
console.log(chalk4.cyan(` ${verificationUri}`));
|
|
1421
|
+
console.log("");
|
|
1422
|
+
console.error(chalk4.yellow.bold(">>> \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u6B64\u94FE\u63A5\u5B8C\u6210\u6388\u6743 <<<"));
|
|
1423
|
+
console.error(chalk4.cyan(verificationUri));
|
|
1424
|
+
console.error("");
|
|
1425
|
+
const open = await import("open").catch(() => null);
|
|
1426
|
+
if (open) {
|
|
1427
|
+
try {
|
|
1428
|
+
await open.default(verificationUri);
|
|
1429
|
+
console.log(chalk4.dim(" \u6D4F\u89C8\u5668\u5DF2\u81EA\u52A8\u6253\u5F00 / Browser opened automatically"));
|
|
1430
|
+
} catch {
|
|
1431
|
+
console.log(chalk4.dim(" \u8BF7\u624B\u52A8\u590D\u5236\u94FE\u63A5\u5230\u6D4F\u89C8\u5668 / Please copy link to browser"));
|
|
1432
|
+
}
|
|
1433
|
+
} else {
|
|
1434
|
+
console.log(chalk4.dim(" \u8BF7\u624B\u52A8\u590D\u5236\u94FE\u63A5\u5230\u6D4F\u89C8\u5668 / Please copy link to browser"));
|
|
1435
|
+
}
|
|
1436
|
+
console.log("");
|
|
1437
|
+
console.log(chalk4.dim("\u2550".repeat(50)));
|
|
1438
|
+
console.log(chalk4.dim(` Device Code: ${deviceCode}`));
|
|
1439
|
+
console.log(chalk4.dim(` Expires in: ${expiresIn} seconds`));
|
|
1440
|
+
console.log(chalk4.dim("\u2550".repeat(50)));
|
|
1441
|
+
console.log("");
|
|
1442
|
+
const tokens = await pollToken(deviceCode);
|
|
1443
|
+
await saveToken(tokens.accessToken, tokens.refreshToken);
|
|
1444
|
+
console.log("");
|
|
1445
|
+
console.log(chalk4.bgGreen.black.bold(" \u2713 \u767B\u5F55\u6210\u529F / Login Successful "));
|
|
1446
|
+
console.log("");
|
|
1447
|
+
console.log(chalk4.green("\u2713 Tokens saved securely"));
|
|
1448
|
+
console.log(chalk4.dim(`Environment: ${env} - ${envConfig.displayName}`));
|
|
1449
|
+
console.log(chalk4.dim(`Access Token expires in: ${Math.floor(tokens.expiresIn / 60)} minutes`));
|
|
1450
|
+
console.log("");
|
|
1451
|
+
console.log(chalk4.dim("Next:"), chalk4.cyan("wukong-cli auth status"));
|
|
1452
|
+
console.log("");
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
console.log("");
|
|
1455
|
+
console.log(chalk4.red(`\u2717 Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1456
|
+
console.log("");
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
authCommands.command("logout").description("Logout and clear saved tokens").action(async () => {
|
|
1460
|
+
const env = getCurrentEnvironment();
|
|
1461
|
+
const allEnvs = getAllEnvironments();
|
|
1462
|
+
setCurrentEnvironment(env);
|
|
1463
|
+
const envConfig = allEnvs[env];
|
|
1464
|
+
try {
|
|
1465
|
+
const accessToken = await getAccessToken();
|
|
1466
|
+
if (accessToken) {
|
|
1467
|
+
await logout(accessToken);
|
|
1468
|
+
}
|
|
1469
|
+
console.log("");
|
|
1470
|
+
console.log(chalk4.green(`\u2713 Logged out from ${env}`), chalk4.dim(`(${envConfig.displayName})`));
|
|
1471
|
+
console.log("");
|
|
1472
|
+
} catch {
|
|
1473
|
+
console.log("");
|
|
1474
|
+
console.log(chalk4.yellow(`\u25CB Already logged out from ${env}`), chalk4.dim(`(${envConfig.displayName})`));
|
|
1475
|
+
console.log("");
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
authCommands.command("refresh").description("Manually refresh access token").action(async () => {
|
|
1479
|
+
const env = getCurrentEnvironment();
|
|
1480
|
+
const config = getOceanetConfig();
|
|
1481
|
+
try {
|
|
1482
|
+
const accessToken = await getAccessToken();
|
|
1483
|
+
if (!accessToken) {
|
|
1484
|
+
console.log("");
|
|
1485
|
+
console.log(chalk4.yellow("\u2717 Not authenticated"));
|
|
1486
|
+
console.log(chalk4.dim(`Environment: ${env}`));
|
|
1487
|
+
console.log("");
|
|
1488
|
+
console.log(chalk4.dim(`Run: wukong-cli auth login`));
|
|
1489
|
+
console.log("");
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const spinner = ora3("Refreshing access token...").start();
|
|
1493
|
+
try {
|
|
1494
|
+
const client = getClient();
|
|
1495
|
+
await client.refreshToken();
|
|
1496
|
+
spinner.succeed("Token refreshed successfully!");
|
|
1497
|
+
console.log("");
|
|
1498
|
+
console.log(chalk4.dim("Environment:"), chalk4.cyan(env));
|
|
1499
|
+
console.log(chalk4.dim("New tokens saved securely"));
|
|
1500
|
+
console.log("");
|
|
1501
|
+
} catch (error) {
|
|
1502
|
+
spinner.fail("Token refresh failed");
|
|
1503
|
+
throw error;
|
|
1504
|
+
}
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
console.log("");
|
|
1507
|
+
console.log(chalk4.red(`\u2717 Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1508
|
+
console.log("");
|
|
1509
|
+
console.log(chalk4.dim(`Environment: ${env}`));
|
|
1510
|
+
console.log(chalk4.dim(`Your session may have expired. Please run:`));
|
|
1511
|
+
console.log(chalk4.dim(` wukong-cli auth login`));
|
|
1512
|
+
console.log("");
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
authCommands.command("status").description("Show authentication status").action(async () => {
|
|
1516
|
+
console.log("");
|
|
1517
|
+
const env = getCurrentEnvironment();
|
|
1518
|
+
const allEnvs = getAllEnvironments();
|
|
1519
|
+
setCurrentEnvironment(env);
|
|
1520
|
+
const envConfig = allEnvs[env];
|
|
1521
|
+
const config = getOceanetConfig();
|
|
1522
|
+
try {
|
|
1523
|
+
const accessToken = await getAccessToken();
|
|
1524
|
+
if (!accessToken) {
|
|
1525
|
+
console.log(chalk4.yellow(`\u2717 Not authenticated`));
|
|
1526
|
+
console.log("");
|
|
1527
|
+
console.log(chalk4.dim(`Environment: ${env} - ${envConfig.displayName}`));
|
|
1528
|
+
console.log(chalk4.dim(`Run: wukong-cli auth login`));
|
|
1529
|
+
console.log("");
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
const client = getClient();
|
|
1534
|
+
const userInfoUrl = `${config.AUTH_BASE_URL}/oceanet-auth/web/userInfo`;
|
|
1535
|
+
const userInfo = await client.get(userInfoUrl);
|
|
1536
|
+
console.log(chalk4.green(`\u2713 Authenticated`));
|
|
1537
|
+
console.log("");
|
|
1538
|
+
const displayUser = userInfo.firstName ? `${userInfo.firstName} (${userInfo.username})` : userInfo.username || "N/A";
|
|
1539
|
+
console.log(chalk4.dim("Environment:"), chalk4.cyan(`${env} - ${envConfig.displayName}`));
|
|
1540
|
+
console.log(chalk4.dim("User:"), chalk4.cyan(displayUser));
|
|
1541
|
+
console.log(chalk4.dim("Email:"), chalk4.cyan(userInfo.email || "N/A"));
|
|
1542
|
+
console.log(chalk4.dim("OrgCode:"), chalk4.cyan(userInfo.ouCode || "N/A"));
|
|
1543
|
+
console.log(chalk4.dim("OrgName:"), chalk4.cyan(userInfo.ouName || "N/A"));
|
|
1544
|
+
console.log("");
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1547
|
+
console.log(chalk4.yellow(`\u2717 Not authenticated`));
|
|
1548
|
+
console.log("");
|
|
1549
|
+
console.log(chalk4.red("Error:"), chalk4.dim(errorMsg));
|
|
1550
|
+
console.log("");
|
|
1551
|
+
console.log(chalk4.dim(`Environment: ${env} - ${envConfig.displayName}`));
|
|
1552
|
+
console.log(chalk4.dim(`Run: wukong-cli auth login`));
|
|
1553
|
+
console.log("");
|
|
1554
|
+
}
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
console.log(chalk4.yellow(`\u2717 Not authenticated`));
|
|
1557
|
+
console.log("");
|
|
1558
|
+
console.log(chalk4.dim(`Environment: ${env} - ${envConfig.displayName}`));
|
|
1559
|
+
console.log(chalk4.dim(`Run: wukong-cli auth login`));
|
|
1560
|
+
console.log("");
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
// src/commands/test.ts
|
|
1565
|
+
init_esm_shims();
|
|
1566
|
+
import { Command as Command2 } from "commander";
|
|
1567
|
+
import ora4 from "ora";
|
|
1568
|
+
import chalk5 from "chalk";
|
|
1569
|
+
var testCommand = new Command2("test").description("Test API request with saved token").action(async () => {
|
|
1570
|
+
try {
|
|
1571
|
+
const accessToken = await getAccessToken();
|
|
1572
|
+
if (!accessToken) {
|
|
1573
|
+
console.log("");
|
|
1574
|
+
console.log(chalk5.red("\u2717 Not authenticated"));
|
|
1575
|
+
console.log("");
|
|
1576
|
+
console.log(chalk5.dim("Please run: wukong-cli auth login"));
|
|
1577
|
+
console.log("");
|
|
1578
|
+
process.exit(1);
|
|
1579
|
+
}
|
|
1580
|
+
const spinner = ora4("Testing API connection...").start();
|
|
1581
|
+
try {
|
|
1582
|
+
const client = getClient();
|
|
1583
|
+
const userInfo = await client.get("/oceanet-auth/web/userInfo");
|
|
1584
|
+
spinner.succeed("API request successful");
|
|
1585
|
+
console.log("");
|
|
1586
|
+
console.log(chalk5.green("User Info:"));
|
|
1587
|
+
console.log(chalk5.dim(JSON.stringify(userInfo, null, 2)));
|
|
1588
|
+
console.log("");
|
|
1589
|
+
} catch (error) {
|
|
1590
|
+
spinner.fail("API request failed");
|
|
1591
|
+
if (error.message.includes("401") || error.message.includes("expired")) {
|
|
1592
|
+
console.log("");
|
|
1593
|
+
console.log(chalk5.yellow("Token expired or invalid"));
|
|
1594
|
+
console.log(chalk5.dim("Try: wukong-cli auth refresh"));
|
|
1595
|
+
console.log("");
|
|
1596
|
+
} else {
|
|
1597
|
+
throw error;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
} catch (error) {
|
|
1601
|
+
console.log("");
|
|
1602
|
+
console.log(chalk5.red(`\u2717 Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1603
|
+
console.log("");
|
|
1604
|
+
process.exit(1);
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
// src/commands/http.ts
|
|
1609
|
+
init_esm_shims();
|
|
1610
|
+
import { Command as Command3 } from "commander";
|
|
1611
|
+
import ora5 from "ora";
|
|
1612
|
+
import chalk6 from "chalk";
|
|
1613
|
+
init_oceanet();
|
|
1614
|
+
function fixGitBashPath(url) {
|
|
1615
|
+
if (url.includes(":") && !url.startsWith("http")) {
|
|
1616
|
+
const oceanetIndex = url.indexOf("/oceanet-");
|
|
1617
|
+
if (oceanetIndex >= 0) {
|
|
1618
|
+
return url.substring(oceanetIndex);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return url;
|
|
1622
|
+
}
|
|
1623
|
+
function parseHeaders(headers) {
|
|
1624
|
+
const result = {};
|
|
1625
|
+
if (Array.isArray(headers)) {
|
|
1626
|
+
for (const h of headers) {
|
|
1627
|
+
const [key, ...valueParts] = h.split(":");
|
|
1628
|
+
if (key && valueParts.length > 0) {
|
|
1629
|
+
result[key.trim()] = valueParts.join(":").trim();
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return result;
|
|
1634
|
+
}
|
|
1635
|
+
function buildUrl2(url, baseUrl) {
|
|
1636
|
+
const cleanUrl = fixGitBashPath(url);
|
|
1637
|
+
if (cleanUrl.startsWith("http")) {
|
|
1638
|
+
return cleanUrl;
|
|
1639
|
+
}
|
|
1640
|
+
const base = baseUrl || OCEANET_CONFIG.API_BASE_URL;
|
|
1641
|
+
return `${base}${cleanUrl}`;
|
|
1642
|
+
}
|
|
1643
|
+
async function executeRequest(method, url, options) {
|
|
1644
|
+
const spinner = ora5("Sending request...").start();
|
|
1645
|
+
try {
|
|
1646
|
+
const accessToken = await getAccessToken();
|
|
1647
|
+
if (!accessToken) {
|
|
1648
|
+
spinner.fail("Not authenticated");
|
|
1649
|
+
console.error(chalk6.red("Please run: wukong-cli auth login"));
|
|
1650
|
+
process.exit(1);
|
|
1651
|
+
}
|
|
1652
|
+
const fullUrl = buildUrl2(url, options.baseUrl);
|
|
1653
|
+
const customHeaders = parseHeaders(options.headers);
|
|
1654
|
+
const headers = {
|
|
1655
|
+
"Content-Type": "application/json",
|
|
1656
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
1657
|
+
...customHeaders
|
|
1658
|
+
};
|
|
1659
|
+
let body;
|
|
1660
|
+
if (options.data) {
|
|
1661
|
+
body = options.data;
|
|
1662
|
+
if (!customHeaders["Content-Type"]) {
|
|
1663
|
+
headers["Content-Type"] = "application/json";
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
spinner.text = `${method} ${chalk6.cyan(fullUrl)}`;
|
|
1667
|
+
const response = await fetch(fullUrl, {
|
|
1668
|
+
method,
|
|
1669
|
+
headers,
|
|
1670
|
+
body: method !== "GET" && method !== "DELETE" ? body : void 0
|
|
1671
|
+
});
|
|
1672
|
+
const data = await response.json();
|
|
1673
|
+
spinner.succeed("Response received");
|
|
1674
|
+
console.log("");
|
|
1675
|
+
console.log(chalk6.dim("Status:"), response.status, response.statusText);
|
|
1676
|
+
console.log("");
|
|
1677
|
+
console.log(chalk6.dim("Response:"));
|
|
1678
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1679
|
+
} catch (error) {
|
|
1680
|
+
spinner.fail("Request failed");
|
|
1681
|
+
if (error instanceof Error) {
|
|
1682
|
+
console.error(chalk6.red(error.message));
|
|
1683
|
+
}
|
|
1684
|
+
process.exit(1);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
var httpCommand = new Command3("http");
|
|
1688
|
+
httpCommand.command("get <url>").description("Send GET request (uses OCEANET_API_BASE_URL or override with -b)").option("-b, --base-url <url>", "Override base URL").option("-H, --header <key:value>", "Custom header (can be used multiple times)", []).action(async (url, options) => {
|
|
1689
|
+
await executeRequest("GET", url, options);
|
|
1690
|
+
});
|
|
1691
|
+
httpCommand.command("post <url>").description("Send POST request with JSON data").option("-b, --base-url <url>", "Override base URL").option("-H, --header <key:value>", "Custom header (can be used multiple times)", []).option("-d, --data <json>", "Request body as JSON string").action(async (url, options) => {
|
|
1692
|
+
await executeRequest("POST", url, options);
|
|
1693
|
+
});
|
|
1694
|
+
httpCommand.command("put <url>").description("Send PUT request with JSON data").option("-b, --base-url <url>", "Override base URL").option("-H, --header <key:value>", "Custom header (can be used multiple times)", []).option("-d, --data <json>", "Request body as JSON string").action(async (url, options) => {
|
|
1695
|
+
await executeRequest("PUT", url, options);
|
|
1696
|
+
});
|
|
1697
|
+
httpCommand.command("delete <url>").description("Send DELETE request").option("-b, --base-url <url>", "Override base URL").option("-H, --header <key:value>", "Custom header (can be used multiple times)", []).action(async (url, options) => {
|
|
1698
|
+
await executeRequest("DELETE", url, options);
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
// src/cli.ts
|
|
1702
|
+
var program = new Command4();
|
|
1703
|
+
program.name("wukong-cli").description("Wukong CLI - TypeScript implementation").version("0.1.0").option("--debug", "Enable debug mode (show HTTP requests)").hook("preAction", (thisCommand) => {
|
|
1704
|
+
const options = thisCommand.opts();
|
|
1705
|
+
if (options.debug === true) {
|
|
1706
|
+
setDebugMode(true, true);
|
|
1707
|
+
} else {
|
|
1708
|
+
setDebugMode(false, false);
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
program.addCommand(authCommands);
|
|
1712
|
+
program.addCommand(testCommand);
|
|
1713
|
+
program.addCommand(httpCommand);
|
|
1714
|
+
if (process.argv.length === 2) {
|
|
1715
|
+
program.help();
|
|
1716
|
+
}
|
|
1717
|
+
program.parse();
|
|
1718
|
+
//# sourceMappingURL=cli.js.map
|