@honor-claw/yoyo 1.1.2 → 1.1.4-beta.1
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/index.ts +25 -25
- package/package.json +5 -5
- package/src/apis/claw-cloud.ts +124 -124
- package/src/apis/helpers.ts +10 -10
- package/src/apis/honor-auth.ts +158 -158
- package/src/apis/http-client.ts +239 -239
- package/src/apis/index.ts +8 -8
- package/src/apis/types.ts +77 -73
- package/src/cloud-channel/channel.ts +117 -117
- package/src/cloud-channel/client.ts +3 -3
- package/src/cloud-channel/index.ts +4 -4
- package/src/cloud-channel/message-handler.ts +50 -42
- package/src/cloud-channel/session-manager.ts +14 -9
- package/src/cloud-channel/types.ts +115 -115
- package/src/commands/env/impl.ts +58 -58
- package/src/commands/env/index.ts +1 -1
- package/src/commands/index.ts +30 -30
- package/src/commands/login/impl.ts +30 -30
- package/src/commands/login/index.ts +1 -1
- package/src/commands/logout/index.ts +1 -1
- package/src/commands/status/index.ts +194 -194
- package/src/gateway-client/client.ts +90 -90
- package/src/gateway-client/device/auth.ts +57 -57
- package/src/gateway-client/device/builder.ts +105 -105
- package/src/gateway-client/device/helpers.ts +40 -40
- package/src/gateway-client/device/identity.ts +251 -251
- package/src/gateway-client/device/index.ts +40 -40
- package/src/gateway-client/device/types.ts +57 -57
- package/src/gateway-client/index.ts +5 -5
- package/src/gateway-client/protocol-client.ts +49 -34
- package/src/honor-auth/browser.ts +2 -2
- package/src/honor-auth/callback-server.ts +109 -109
- package/src/honor-auth/cloud.ts +57 -57
- package/src/honor-auth/config.ts +43 -43
- package/src/honor-auth/index.ts +3 -3
- package/src/honor-auth/token-manager.ts +90 -90
- package/src/honor-auth/types.ts +50 -50
- package/src/index.ts +10 -10
- package/src/modules/claw-configs/config-manager.ts +409 -409
- package/src/modules/claw-configs/hosts.ts +48 -48
- package/src/modules/claw-configs/index.ts +8 -8
- package/src/modules/claw-configs/provider.ts +394 -394
- package/src/modules/claw-configs/types.ts +34 -34
- package/src/modules/device/device-info.ts +1 -1
- package/src/modules/device/index.ts +3 -3
- package/src/modules/device/providers/base.ts +32 -32
- package/src/modules/device/providers/pad.ts +107 -107
- package/src/modules/device/providers/windows.ts +130 -130
- package/src/modules/device/registry.ts +48 -43
- package/src/modules/login/impl.ts +31 -26
- package/src/modules/login/index.ts +6 -6
- package/src/schemas.ts +23 -23
- package/src/services/connection/impl.ts +339 -339
- package/src/services/connection/status-tracker/events.ts +127 -127
- package/src/services/connection/status-tracker/index.ts +31 -31
- package/src/services/connection/status-tracker/storage.ts +133 -133
- package/src/services/connection/status-tracker/tracker.ts +370 -370
- package/src/services/connection/status-tracker/types.ts +131 -131
- package/src/services/connection/types.ts +20 -20
- package/src/types.ts +64 -64
- package/src/utils/id.ts +8 -8
- package/src/utils/jwt.ts +37 -37
- package/src/utils/logger.ts +20 -20
- package/src/utils/proxy.ts +58 -58
- package/src/utils/version.ts +29 -29
- package/src/utils/ws.ts +21 -21
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Device identity information
|
|
3
|
-
*/
|
|
4
|
-
export type DeviceIdentity = {
|
|
5
|
-
deviceId: string;
|
|
6
|
-
publicKeyPem: string;
|
|
7
|
-
privateKeyPem: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Device authentication payload parameters
|
|
12
|
-
*/
|
|
13
|
-
export type DeviceAuthPayloadParams = {
|
|
14
|
-
deviceId: string;
|
|
15
|
-
clientId: string;
|
|
16
|
-
clientMode: string;
|
|
17
|
-
role: string;
|
|
18
|
-
scopes: string[];
|
|
19
|
-
signedAtMs: number;
|
|
20
|
-
token?: string | null;
|
|
21
|
-
nonce: string;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Device authentication payload V3 parameters (with platform/device family)
|
|
26
|
-
*/
|
|
27
|
-
export type DeviceAuthPayloadV3Params = DeviceAuthPayloadParams & {
|
|
28
|
-
platform?: string | null;
|
|
29
|
-
deviceFamily?: string | null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Device information for gateway connection
|
|
34
|
-
*/
|
|
35
|
-
export type DeviceInfo = {
|
|
36
|
-
id: string;
|
|
37
|
-
publicKey: string;
|
|
38
|
-
signature: string;
|
|
39
|
-
signedAt: number;
|
|
40
|
-
nonce: string;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Device builder options
|
|
45
|
-
*/
|
|
46
|
-
export type DeviceBuilderOptions = {
|
|
47
|
-
deviceIdentity?: DeviceIdentity;
|
|
48
|
-
clientName?: string;
|
|
49
|
-
clientMode?: string;
|
|
50
|
-
role?: string;
|
|
51
|
-
scopes?: string[];
|
|
52
|
-
platform?: string;
|
|
53
|
-
deviceFamily?: string;
|
|
54
|
-
authToken?: string | null;
|
|
55
|
-
nonce: string;
|
|
56
|
-
signedAtMs?: number;
|
|
57
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Device identity information
|
|
3
|
+
*/
|
|
4
|
+
export type DeviceIdentity = {
|
|
5
|
+
deviceId: string;
|
|
6
|
+
publicKeyPem: string;
|
|
7
|
+
privateKeyPem: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Device authentication payload parameters
|
|
12
|
+
*/
|
|
13
|
+
export type DeviceAuthPayloadParams = {
|
|
14
|
+
deviceId: string;
|
|
15
|
+
clientId: string;
|
|
16
|
+
clientMode: string;
|
|
17
|
+
role: string;
|
|
18
|
+
scopes: string[];
|
|
19
|
+
signedAtMs: number;
|
|
20
|
+
token?: string | null;
|
|
21
|
+
nonce: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Device authentication payload V3 parameters (with platform/device family)
|
|
26
|
+
*/
|
|
27
|
+
export type DeviceAuthPayloadV3Params = DeviceAuthPayloadParams & {
|
|
28
|
+
platform?: string | null;
|
|
29
|
+
deviceFamily?: string | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Device information for gateway connection
|
|
34
|
+
*/
|
|
35
|
+
export type DeviceInfo = {
|
|
36
|
+
id: string;
|
|
37
|
+
publicKey: string;
|
|
38
|
+
signature: string;
|
|
39
|
+
signedAt: number;
|
|
40
|
+
nonce: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Device builder options
|
|
45
|
+
*/
|
|
46
|
+
export type DeviceBuilderOptions = {
|
|
47
|
+
deviceIdentity?: DeviceIdentity;
|
|
48
|
+
clientName?: string;
|
|
49
|
+
clientMode?: string;
|
|
50
|
+
role?: string;
|
|
51
|
+
scopes?: string[];
|
|
52
|
+
platform?: string;
|
|
53
|
+
deviceFamily?: string;
|
|
54
|
+
authToken?: string | null;
|
|
55
|
+
nonce: string;
|
|
56
|
+
signedAtMs?: number;
|
|
57
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from "./client.js";
|
|
2
|
-
export * from "./protocol-client.js";
|
|
3
|
-
export * from "./protocol/index.js";
|
|
4
|
-
export * from "./node-gateway-client.js";
|
|
5
|
-
export * from './admin-gateway-client.js'
|
|
1
|
+
export * from "./client.js";
|
|
2
|
+
export * from "./protocol-client.js";
|
|
3
|
+
export * from "./protocol/index.js";
|
|
4
|
+
export * from "./node-gateway-client.js";
|
|
5
|
+
export * from './admin-gateway-client.js'
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { useClawLogger } from "../utils/logger.js";
|
|
3
|
+
import { GatewayClient } from "./client.js";
|
|
4
|
+
import {
|
|
5
|
+
loadOrCreateDeviceIdentity,
|
|
6
|
+
signDevicePayload,
|
|
7
|
+
publicKeyRawBase64UrlFromPem,
|
|
8
|
+
buildDeviceAuthPayloadV3,
|
|
9
|
+
} from "./device/index.js";
|
|
2
10
|
import {
|
|
3
11
|
type ConnectParams,
|
|
4
12
|
type EventFrame,
|
|
@@ -9,11 +17,13 @@ import {
|
|
|
9
17
|
ErrorCodes,
|
|
10
18
|
PROTOCOL_VERSION,
|
|
11
19
|
} from "./protocol/index.js";
|
|
12
|
-
import { GatewayClient } from "./client.js";
|
|
13
20
|
import { type ProtocolClientOptions } from "./protocol/index.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
import {
|
|
22
|
+
type GatewayClientId,
|
|
23
|
+
type GatewayClientMode,
|
|
24
|
+
GATEWAY_CLIENT_IDS,
|
|
25
|
+
GATEWAY_CLIENT_MODES,
|
|
26
|
+
} from "./protocol/index.js";
|
|
17
27
|
|
|
18
28
|
type PendingRequest = {
|
|
19
29
|
resolve: (value: unknown) => void;
|
|
@@ -134,12 +144,14 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
134
144
|
pending.resolve(response.payload);
|
|
135
145
|
} else {
|
|
136
146
|
const error = response.error || { code: ErrorCodes.UNKNOWN, message: "Unknown error" };
|
|
137
|
-
pending.reject(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
pending.reject(
|
|
148
|
+
new WebSocketClientError(
|
|
149
|
+
this.toErrorCode(error.code),
|
|
150
|
+
error.message,
|
|
151
|
+
error.details,
|
|
152
|
+
error.retryable,
|
|
153
|
+
),
|
|
154
|
+
);
|
|
143
155
|
}
|
|
144
156
|
}
|
|
145
157
|
|
|
@@ -194,19 +206,21 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
194
206
|
device,
|
|
195
207
|
};
|
|
196
208
|
|
|
197
|
-
this.sendRequest("connect", params)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
this.sendRequest("connect", params)
|
|
210
|
+
.then((payload) => {
|
|
211
|
+
this.helloOk = payload as HelloOk;
|
|
212
|
+
this.connected = true;
|
|
213
|
+
this.authenticated = true;
|
|
214
|
+
this.protocolOpts.onAuthenticated?.();
|
|
215
|
+
})
|
|
216
|
+
.catch((error) => {
|
|
217
|
+
this.authenticated = false;
|
|
218
|
+
if (error instanceof WebSocketClientError) {
|
|
219
|
+
this.protocolOpts.onClose?.(`auth failed: [${error.code}] ${error.message}`);
|
|
220
|
+
} else {
|
|
221
|
+
this.protocolOpts.onClose?.(`auth failed: ${error.message}`);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
210
224
|
}
|
|
211
225
|
|
|
212
226
|
protected async sendRequest(method: string, params?: unknown): Promise<unknown> {
|
|
@@ -221,10 +235,12 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
221
235
|
this.send(JSON.stringify(frame));
|
|
222
236
|
} catch (error) {
|
|
223
237
|
this.pendingRequests.delete(id);
|
|
224
|
-
reject(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
238
|
+
reject(
|
|
239
|
+
new WebSocketClientError(
|
|
240
|
+
ErrorCodes.CONNECTION_FAILED,
|
|
241
|
+
`WebSocket is not connected, ${JSON.stringify(error)}`,
|
|
242
|
+
),
|
|
243
|
+
);
|
|
228
244
|
return;
|
|
229
245
|
}
|
|
230
246
|
|
|
@@ -232,12 +248,11 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
232
248
|
if (this.pendingRequests.has(id)) {
|
|
233
249
|
this.pendingRequests.delete(id);
|
|
234
250
|
const t = this.pendingTimers.get(id);
|
|
235
|
-
if (t)
|
|
251
|
+
if (t) {
|
|
252
|
+
clearTimeout(t);
|
|
253
|
+
}
|
|
236
254
|
this.pendingTimers.delete(id);
|
|
237
|
-
reject(new WebSocketClientError(
|
|
238
|
-
ErrorCodes.TIMEOUT,
|
|
239
|
-
`Request timeout: ${method}`
|
|
240
|
-
));
|
|
255
|
+
reject(new WebSocketClientError(ErrorCodes.TIMEOUT, `Request timeout: ${method}`));
|
|
241
256
|
}
|
|
242
257
|
}, DEFAULT_REQUEST_TIMEOUT_MS);
|
|
243
258
|
this.pendingTimers.set(id, timer);
|
|
@@ -306,7 +321,7 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
306
321
|
|
|
307
322
|
if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
308
323
|
useClawLogger().error(
|
|
309
|
-
`[protocol-gateway] max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached, giving up
|
|
324
|
+
`[protocol-gateway] max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached, giving up`,
|
|
310
325
|
);
|
|
311
326
|
this.protocolOpts.onReconnectFailed?.();
|
|
312
327
|
return;
|
|
@@ -315,7 +330,7 @@ export class ProtocolGatewayClient extends GatewayClient {
|
|
|
315
330
|
this.reconnectAttempts++;
|
|
316
331
|
this.isReconnecting = true;
|
|
317
332
|
useClawLogger().info(
|
|
318
|
-
`[protocol-gateway] attempting to reconnect (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})
|
|
333
|
+
`[protocol-gateway] attempting to reconnect (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`,
|
|
319
334
|
);
|
|
320
335
|
|
|
321
336
|
this.reconnectTimer = setTimeout(() => {
|
|
@@ -14,7 +14,7 @@ export async function performOAuth2AuthWithBrowser(_: DeviceInfo, configOverride
|
|
|
14
14
|
const config = getConfig(configOverrides);
|
|
15
15
|
const logger = useClawLogger();
|
|
16
16
|
|
|
17
|
-
logger.debug('Starting Honor login flow...');
|
|
17
|
+
logger.debug?.('Starting Honor login flow...');
|
|
18
18
|
|
|
19
19
|
// 创建认证客户端
|
|
20
20
|
const authClient = createHonorAuthClient(config);
|
|
@@ -61,7 +61,7 @@ export async function performOAuth2AuthWithBrowser(_: DeviceInfo, configOverride
|
|
|
61
61
|
try {
|
|
62
62
|
const tokenInfo = await authClient.exchangeToken(receivedCode);
|
|
63
63
|
|
|
64
|
-
if (!tokenInfo
|
|
64
|
+
if (!tokenInfo?.token || !tokenInfo.userInfo?.userId) {
|
|
65
65
|
throw new Error('failed to get user info');
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 本地HTTP回调服务器 - 接收OAuth2授权回调
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createServer, type IncomingMessage, type ServerResponse } from 'http';
|
|
6
|
-
import { URL } from 'url';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 回调服务器选项
|
|
10
|
-
*/
|
|
11
|
-
export interface CallbackServerOptions {
|
|
12
|
-
/** 监听端口 */
|
|
13
|
-
port: number;
|
|
14
|
-
/** 超时时间(毫秒) */
|
|
15
|
-
timeout?: number;
|
|
16
|
-
/** 授权码回调处理 */
|
|
17
|
-
onCodeReceived: (code: string) => void;
|
|
18
|
-
/** 错误回调 */
|
|
19
|
-
onError?: (error: Error) => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 启动本地回调服务器
|
|
24
|
-
*/
|
|
25
|
-
export function startCallbackServer(options: CallbackServerOptions): Promise<void> {
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
const { port, timeout = 600000, onCodeReceived, onError } = options;
|
|
28
|
-
let timeoutId: NodeJS.Timeout | null = null;
|
|
29
|
-
let serverClosed = false;
|
|
30
|
-
let hasReceivedCode = false;
|
|
31
|
-
let promiseResolved = false;
|
|
32
|
-
|
|
33
|
-
const server = createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
34
|
-
try {
|
|
35
|
-
const url = new URL(req.url || '', `http://localhost:${port}`);
|
|
36
|
-
const code = url.searchParams.get('code') || '';
|
|
37
|
-
|
|
38
|
-
if (code && !hasReceivedCode) {
|
|
39
|
-
hasReceivedCode = true;
|
|
40
|
-
|
|
41
|
-
// 返回成功响应
|
|
42
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
43
|
-
res.end('<h1>授权成功!可以关闭此窗口</h1>');
|
|
44
|
-
|
|
45
|
-
// 延迟关闭服务器,确保响应已发送
|
|
46
|
-
setTimeout(() => {
|
|
47
|
-
if (!serverClosed) {
|
|
48
|
-
serverClosed = true;
|
|
49
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
50
|
-
server.close();
|
|
51
|
-
}
|
|
52
|
-
}, 100);
|
|
53
|
-
|
|
54
|
-
// 调用回调
|
|
55
|
-
onCodeReceived(code);
|
|
56
|
-
} else if (!hasReceivedCode) {
|
|
57
|
-
// 返回错误响应
|
|
58
|
-
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
59
|
-
res.end('<h1>授权失败:未获取到授权码</h1>');
|
|
60
|
-
} else {
|
|
61
|
-
// 重复请求,返回成功响应但不处理
|
|
62
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
63
|
-
res.end('<h1>授权成功!可以关闭此窗口</h1>');
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error('authorize callback error:', error);
|
|
67
|
-
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
68
|
-
res.end('<h1>服务器错误</h1>');
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
server.on('error', (error) => {
|
|
73
|
-
if (!serverClosed) {
|
|
74
|
-
serverClosed = true;
|
|
75
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
76
|
-
onError?.(error);
|
|
77
|
-
if (!promiseResolved) {
|
|
78
|
-
promiseResolved = true;
|
|
79
|
-
reject(error);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
server.listen(port, () => {
|
|
85
|
-
// 设置超时
|
|
86
|
-
timeoutId = setTimeout(() => {
|
|
87
|
-
if (!serverClosed) {
|
|
88
|
-
serverClosed = true;
|
|
89
|
-
server.close();
|
|
90
|
-
const error = new Error('authorize timeout');
|
|
91
|
-
onError?.(error);
|
|
92
|
-
if (!promiseResolved) {
|
|
93
|
-
promiseResolved = true;
|
|
94
|
-
reject(error);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}, timeout);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// 服务器关闭时解析Promise
|
|
101
|
-
server.on('close', () => {
|
|
102
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
103
|
-
if (!promiseResolved) {
|
|
104
|
-
promiseResolved = true;
|
|
105
|
-
resolve();
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 本地HTTP回调服务器 - 接收OAuth2授权回调
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createServer, type IncomingMessage, type ServerResponse } from 'http';
|
|
6
|
+
import { URL } from 'url';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 回调服务器选项
|
|
10
|
+
*/
|
|
11
|
+
export interface CallbackServerOptions {
|
|
12
|
+
/** 监听端口 */
|
|
13
|
+
port: number;
|
|
14
|
+
/** 超时时间(毫秒) */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/** 授权码回调处理 */
|
|
17
|
+
onCodeReceived: (code: string) => void;
|
|
18
|
+
/** 错误回调 */
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 启动本地回调服务器
|
|
24
|
+
*/
|
|
25
|
+
export function startCallbackServer(options: CallbackServerOptions): Promise<void> {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const { port, timeout = 600000, onCodeReceived, onError } = options;
|
|
28
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
29
|
+
let serverClosed = false;
|
|
30
|
+
let hasReceivedCode = false;
|
|
31
|
+
let promiseResolved = false;
|
|
32
|
+
|
|
33
|
+
const server = createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(req.url || '', `http://localhost:${port}`);
|
|
36
|
+
const code = url.searchParams.get('code') || '';
|
|
37
|
+
|
|
38
|
+
if (code && !hasReceivedCode) {
|
|
39
|
+
hasReceivedCode = true;
|
|
40
|
+
|
|
41
|
+
// 返回成功响应
|
|
42
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
43
|
+
res.end('<h1>授权成功!可以关闭此窗口</h1>');
|
|
44
|
+
|
|
45
|
+
// 延迟关闭服务器,确保响应已发送
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
if (!serverClosed) {
|
|
48
|
+
serverClosed = true;
|
|
49
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
50
|
+
server.close();
|
|
51
|
+
}
|
|
52
|
+
}, 100);
|
|
53
|
+
|
|
54
|
+
// 调用回调
|
|
55
|
+
onCodeReceived(code);
|
|
56
|
+
} else if (!hasReceivedCode) {
|
|
57
|
+
// 返回错误响应
|
|
58
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
59
|
+
res.end('<h1>授权失败:未获取到授权码</h1>');
|
|
60
|
+
} else {
|
|
61
|
+
// 重复请求,返回成功响应但不处理
|
|
62
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
63
|
+
res.end('<h1>授权成功!可以关闭此窗口</h1>');
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('authorize callback error:', error);
|
|
67
|
+
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
68
|
+
res.end('<h1>服务器错误</h1>');
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
server.on('error', (error) => {
|
|
73
|
+
if (!serverClosed) {
|
|
74
|
+
serverClosed = true;
|
|
75
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
76
|
+
onError?.(error);
|
|
77
|
+
if (!promiseResolved) {
|
|
78
|
+
promiseResolved = true;
|
|
79
|
+
reject(error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
server.listen(port, () => {
|
|
85
|
+
// 设置超时
|
|
86
|
+
timeoutId = setTimeout(() => {
|
|
87
|
+
if (!serverClosed) {
|
|
88
|
+
serverClosed = true;
|
|
89
|
+
server.close();
|
|
90
|
+
const error = new Error('authorize timeout');
|
|
91
|
+
onError?.(error);
|
|
92
|
+
if (!promiseResolved) {
|
|
93
|
+
promiseResolved = true;
|
|
94
|
+
reject(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}, timeout);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 服务器关闭时解析Promise
|
|
101
|
+
server.on('close', () => {
|
|
102
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
103
|
+
if (!promiseResolved) {
|
|
104
|
+
promiseResolved = true;
|
|
105
|
+
resolve();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
package/src/honor-auth/cloud.ts
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
import { createClawCloudClient } from '../apis/claw-cloud.js';
|
|
3
|
-
import { isOKResponse } from '../apis/index.js';
|
|
4
|
-
import type { HonorUserInfo } from '../types.js';
|
|
5
|
-
import { getConfigManager, takeApiHost } from '../modules/claw-configs/index.js';
|
|
6
|
-
import { getDeviceInfo } from '../modules/device/device-info.js';
|
|
7
|
-
import { clearToken } from './token-manager.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 执行登出流程
|
|
12
|
-
*/
|
|
13
|
-
export async function performLogout(): Promise<void> {
|
|
14
|
-
try {
|
|
15
|
-
const configManager = getConfigManager();
|
|
16
|
-
const userConfig = configManager.getUserConfig();
|
|
17
|
-
|
|
18
|
-
if (!userConfig || !userConfig.token) {
|
|
19
|
-
console.log('⚠️ Not logged in');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// 获取设备信息
|
|
24
|
-
const deviceInfo = getDeviceInfo();
|
|
25
|
-
|
|
26
|
-
// 调用登出接口
|
|
27
|
-
const hosts = takeApiHost();
|
|
28
|
-
const baseUrl = `https://${hosts.clawCloud}/aicloud/yoyo-claw-service`;
|
|
29
|
-
|
|
30
|
-
// 如果有灰度标签,设置默认headers
|
|
31
|
-
const options = hosts.grayTag ? {
|
|
32
|
-
defaultHeaders: {
|
|
33
|
-
'x-gray': hosts.grayTag
|
|
34
|
-
}
|
|
35
|
-
} : undefined;
|
|
36
|
-
|
|
37
|
-
const client = createClawCloudClient(baseUrl, options);
|
|
38
|
-
|
|
39
|
-
const userInfo: HonorUserInfo = {
|
|
40
|
-
userId: userConfig.userId,
|
|
41
|
-
token: userConfig.token,
|
|
42
|
-
userName: userConfig.userName,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const response = await client.logoutDevice(deviceInfo, userInfo);
|
|
46
|
-
|
|
47
|
-
if (!isOKResponse(response)) {
|
|
48
|
-
throw new Error(`Logout failed: ${response.data?.cnMessage || 'Unknown error'}`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 清除Token
|
|
52
|
-
await clearToken();
|
|
53
|
-
} catch (error) {
|
|
54
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
55
|
-
throw new Error(`failed to logout: ${errorMessage}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
|
|
2
|
+
import { createClawCloudClient } from '../apis/claw-cloud.js';
|
|
3
|
+
import { isOKResponse } from '../apis/index.js';
|
|
4
|
+
import type { HonorUserInfo } from '../types.js';
|
|
5
|
+
import { getConfigManager, takeApiHost } from '../modules/claw-configs/index.js';
|
|
6
|
+
import { getDeviceInfo } from '../modules/device/device-info.js';
|
|
7
|
+
import { clearToken } from './token-manager.js';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 执行登出流程
|
|
12
|
+
*/
|
|
13
|
+
export async function performLogout(): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
const configManager = getConfigManager();
|
|
16
|
+
const userConfig = configManager.getUserConfig();
|
|
17
|
+
|
|
18
|
+
if (!userConfig || !userConfig.token) {
|
|
19
|
+
console.log('⚠️ Not logged in');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 获取设备信息
|
|
24
|
+
const deviceInfo = getDeviceInfo();
|
|
25
|
+
|
|
26
|
+
// 调用登出接口
|
|
27
|
+
const hosts = takeApiHost();
|
|
28
|
+
const baseUrl = `https://${hosts.clawCloud}/aicloud/yoyo-claw-service`;
|
|
29
|
+
|
|
30
|
+
// 如果有灰度标签,设置默认headers
|
|
31
|
+
const options = hosts.grayTag ? {
|
|
32
|
+
defaultHeaders: {
|
|
33
|
+
'x-gray': hosts.grayTag
|
|
34
|
+
}
|
|
35
|
+
} : undefined;
|
|
36
|
+
|
|
37
|
+
const client = createClawCloudClient(baseUrl, options);
|
|
38
|
+
|
|
39
|
+
const userInfo: HonorUserInfo = {
|
|
40
|
+
userId: userConfig.userId,
|
|
41
|
+
token: userConfig.token,
|
|
42
|
+
userName: userConfig.userName,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const response = await client.logoutDevice(deviceInfo, userInfo);
|
|
46
|
+
|
|
47
|
+
if (!isOKResponse(response)) {
|
|
48
|
+
throw new Error(`Logout failed: ${response.data?.cnMessage || 'Unknown error'}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 清除Token
|
|
52
|
+
await clearToken();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
55
|
+
throw new Error(`failed to logout: ${errorMessage}`);
|
|
56
|
+
}
|
|
57
|
+
}
|