@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,127 +1,127 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 状态跟踪事件定义
|
|
3
|
-
* 定义所有状态跟踪相关的事件类型
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 事件类型枚举
|
|
8
|
-
*/
|
|
9
|
-
export enum StatusEventType {
|
|
10
|
-
// 云侧Socket事件
|
|
11
|
-
CLOUD_SOCKET_CONNECTING = 'cloud_socket_connecting',
|
|
12
|
-
CLOUD_SOCKET_CONNECTED = 'cloud_socket_connected',
|
|
13
|
-
CLOUD_SOCKET_DISCONNECTED = 'cloud_socket_disconnected',
|
|
14
|
-
CLOUD_SOCKET_ERROR = 'cloud_socket_error',
|
|
15
|
-
CLOUD_SOCKET_RETRY = 'cloud_socket_retry',
|
|
16
|
-
|
|
17
|
-
// Gateway连接事件
|
|
18
|
-
GATEWAY_CLIENT_CONNECTED = 'gateway_client_connected',
|
|
19
|
-
GATEWAY_CLIENT_DISCONNECTED = 'gateway_client_disconnected',
|
|
20
|
-
|
|
21
|
-
// 设备配对事件
|
|
22
|
-
DEVICE_PAIRING = 'device_pairing',
|
|
23
|
-
DEVICE_UNPAIRING = 'device_unpairing',
|
|
24
|
-
|
|
25
|
-
// 整体连接状态事件
|
|
26
|
-
CONNECTION_STATUS_CHANGED = 'connection_status_changed',
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 状态事件基础接口
|
|
31
|
-
*/
|
|
32
|
-
export interface StatusEvent {
|
|
33
|
-
type: StatusEventType;
|
|
34
|
-
timestamp: number;
|
|
35
|
-
data: any;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 云侧Socket连接事件数据
|
|
40
|
-
*/
|
|
41
|
-
export interface CloudSocketConnectingData {
|
|
42
|
-
url: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* 云侧Socket已连接事件数据
|
|
47
|
-
*/
|
|
48
|
-
export interface CloudSocketConnectedData {
|
|
49
|
-
url: string;
|
|
50
|
-
connectedAt: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 云侧Socket断连事件数据
|
|
55
|
-
*/
|
|
56
|
-
export interface CloudSocketDisconnectedData {
|
|
57
|
-
reason: string;
|
|
58
|
-
code?: number;
|
|
59
|
-
disconnectedAt: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 云侧Socket错误事件数据
|
|
64
|
-
*/
|
|
65
|
-
export interface CloudSocketErrorData {
|
|
66
|
-
error: string;
|
|
67
|
-
timestamp: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 云侧Socket重试事件数据
|
|
72
|
-
*/
|
|
73
|
-
export interface CloudSocketRetryData {
|
|
74
|
-
retryCount: number;
|
|
75
|
-
maxRetries: number;
|
|
76
|
-
delay: number;
|
|
77
|
-
lastError?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Gateway客户端连接事件数据
|
|
82
|
-
*/
|
|
83
|
-
export interface GatewayClientConnectedData {
|
|
84
|
-
sessionId: string;
|
|
85
|
-
hardwareDeviceId: string;
|
|
86
|
-
deviceInfo: any;
|
|
87
|
-
connectedAt: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Gateway客户端断连事件数据
|
|
92
|
-
*/
|
|
93
|
-
export interface GatewayClientDisconnectedData {
|
|
94
|
-
sessionId: string;
|
|
95
|
-
hardwareDeviceId: string;
|
|
96
|
-
reason: string;
|
|
97
|
-
disconnectedAt: string;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* 设备配对事件数据
|
|
102
|
-
*/
|
|
103
|
-
export interface DevicePairingData {
|
|
104
|
-
sessionId: string;
|
|
105
|
-
hardwareDeviceId: string;
|
|
106
|
-
deviceInfo: any;
|
|
107
|
-
pairedAt: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 设备取消配对事件数据
|
|
112
|
-
*/
|
|
113
|
-
export interface DeviceUnpairingData {
|
|
114
|
-
sessionId: string;
|
|
115
|
-
hardwareDeviceId: string;
|
|
116
|
-
reason: string;
|
|
117
|
-
unpairedAt: string;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 连接状态变更事件数据
|
|
122
|
-
*/
|
|
123
|
-
export interface ConnectionStatusChangedData {
|
|
124
|
-
status: 'idle' | 'connecting' | 'connected';
|
|
125
|
-
previousStatus?: 'idle' | 'connecting' | 'connected';
|
|
126
|
-
reason?: string;
|
|
127
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 状态跟踪事件定义
|
|
3
|
+
* 定义所有状态跟踪相关的事件类型
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 事件类型枚举
|
|
8
|
+
*/
|
|
9
|
+
export enum StatusEventType {
|
|
10
|
+
// 云侧Socket事件
|
|
11
|
+
CLOUD_SOCKET_CONNECTING = 'cloud_socket_connecting',
|
|
12
|
+
CLOUD_SOCKET_CONNECTED = 'cloud_socket_connected',
|
|
13
|
+
CLOUD_SOCKET_DISCONNECTED = 'cloud_socket_disconnected',
|
|
14
|
+
CLOUD_SOCKET_ERROR = 'cloud_socket_error',
|
|
15
|
+
CLOUD_SOCKET_RETRY = 'cloud_socket_retry',
|
|
16
|
+
|
|
17
|
+
// Gateway连接事件
|
|
18
|
+
GATEWAY_CLIENT_CONNECTED = 'gateway_client_connected',
|
|
19
|
+
GATEWAY_CLIENT_DISCONNECTED = 'gateway_client_disconnected',
|
|
20
|
+
|
|
21
|
+
// 设备配对事件
|
|
22
|
+
DEVICE_PAIRING = 'device_pairing',
|
|
23
|
+
DEVICE_UNPAIRING = 'device_unpairing',
|
|
24
|
+
|
|
25
|
+
// 整体连接状态事件
|
|
26
|
+
CONNECTION_STATUS_CHANGED = 'connection_status_changed',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 状态事件基础接口
|
|
31
|
+
*/
|
|
32
|
+
export interface StatusEvent {
|
|
33
|
+
type: StatusEventType;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
data: any;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 云侧Socket连接事件数据
|
|
40
|
+
*/
|
|
41
|
+
export interface CloudSocketConnectingData {
|
|
42
|
+
url: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 云侧Socket已连接事件数据
|
|
47
|
+
*/
|
|
48
|
+
export interface CloudSocketConnectedData {
|
|
49
|
+
url: string;
|
|
50
|
+
connectedAt: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 云侧Socket断连事件数据
|
|
55
|
+
*/
|
|
56
|
+
export interface CloudSocketDisconnectedData {
|
|
57
|
+
reason: string;
|
|
58
|
+
code?: number;
|
|
59
|
+
disconnectedAt: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 云侧Socket错误事件数据
|
|
64
|
+
*/
|
|
65
|
+
export interface CloudSocketErrorData {
|
|
66
|
+
error: string;
|
|
67
|
+
timestamp: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 云侧Socket重试事件数据
|
|
72
|
+
*/
|
|
73
|
+
export interface CloudSocketRetryData {
|
|
74
|
+
retryCount: number;
|
|
75
|
+
maxRetries: number;
|
|
76
|
+
delay: number;
|
|
77
|
+
lastError?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gateway客户端连接事件数据
|
|
82
|
+
*/
|
|
83
|
+
export interface GatewayClientConnectedData {
|
|
84
|
+
sessionId: string;
|
|
85
|
+
hardwareDeviceId: string;
|
|
86
|
+
deviceInfo: any;
|
|
87
|
+
connectedAt: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Gateway客户端断连事件数据
|
|
92
|
+
*/
|
|
93
|
+
export interface GatewayClientDisconnectedData {
|
|
94
|
+
sessionId: string;
|
|
95
|
+
hardwareDeviceId: string;
|
|
96
|
+
reason: string;
|
|
97
|
+
disconnectedAt: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 设备配对事件数据
|
|
102
|
+
*/
|
|
103
|
+
export interface DevicePairingData {
|
|
104
|
+
sessionId: string;
|
|
105
|
+
hardwareDeviceId: string;
|
|
106
|
+
deviceInfo: any;
|
|
107
|
+
pairedAt: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 设备取消配对事件数据
|
|
112
|
+
*/
|
|
113
|
+
export interface DeviceUnpairingData {
|
|
114
|
+
sessionId: string;
|
|
115
|
+
hardwareDeviceId: string;
|
|
116
|
+
reason: string;
|
|
117
|
+
unpairedAt: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 连接状态变更事件数据
|
|
122
|
+
*/
|
|
123
|
+
export interface ConnectionStatusChangedData {
|
|
124
|
+
status: 'idle' | 'connecting' | 'connected';
|
|
125
|
+
previousStatus?: 'idle' | 'connecting' | 'connected';
|
|
126
|
+
reason?: string;
|
|
127
|
+
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 状态跟踪模块入口
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { StatusTracker } from './tracker.js';
|
|
6
|
-
export { StatusStorage } from './storage.js';
|
|
7
|
-
export {
|
|
8
|
-
StatusEventType,
|
|
9
|
-
type StatusEvent,
|
|
10
|
-
type CloudSocketConnectingData,
|
|
11
|
-
type CloudSocketConnectedData,
|
|
12
|
-
type CloudSocketDisconnectedData,
|
|
13
|
-
type CloudSocketErrorData,
|
|
14
|
-
type CloudSocketRetryData,
|
|
15
|
-
type GatewayClientConnectedData,
|
|
16
|
-
type GatewayClientDisconnectedData,
|
|
17
|
-
type DevicePairingData,
|
|
18
|
-
type DeviceUnpairingData,
|
|
19
|
-
type ConnectionStatusChangedData,
|
|
20
|
-
} from './events.js';
|
|
21
|
-
export {
|
|
22
|
-
type ConnectionStatusData,
|
|
23
|
-
type ConnectionStatus,
|
|
24
|
-
type GatewayConnectionInfo,
|
|
25
|
-
type PhysicalDeviceInfo,
|
|
26
|
-
type CloudSocketStatus,
|
|
27
|
-
type GatewayStatus,
|
|
28
|
-
type DevicesStatus,
|
|
29
|
-
type ConnectionHistory,
|
|
30
|
-
createEmptyStatusData,
|
|
31
|
-
} from './types.js';
|
|
1
|
+
/**
|
|
2
|
+
* 状态跟踪模块入口
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { StatusTracker } from './tracker.js';
|
|
6
|
+
export { StatusStorage } from './storage.js';
|
|
7
|
+
export {
|
|
8
|
+
StatusEventType,
|
|
9
|
+
type StatusEvent,
|
|
10
|
+
type CloudSocketConnectingData,
|
|
11
|
+
type CloudSocketConnectedData,
|
|
12
|
+
type CloudSocketDisconnectedData,
|
|
13
|
+
type CloudSocketErrorData,
|
|
14
|
+
type CloudSocketRetryData,
|
|
15
|
+
type GatewayClientConnectedData,
|
|
16
|
+
type GatewayClientDisconnectedData,
|
|
17
|
+
type DevicePairingData,
|
|
18
|
+
type DeviceUnpairingData,
|
|
19
|
+
type ConnectionStatusChangedData,
|
|
20
|
+
} from './events.js';
|
|
21
|
+
export {
|
|
22
|
+
type ConnectionStatusData,
|
|
23
|
+
type ConnectionStatus,
|
|
24
|
+
type GatewayConnectionInfo,
|
|
25
|
+
type PhysicalDeviceInfo,
|
|
26
|
+
type CloudSocketStatus,
|
|
27
|
+
type GatewayStatus,
|
|
28
|
+
type DevicesStatus,
|
|
29
|
+
type ConnectionHistory,
|
|
30
|
+
createEmptyStatusData,
|
|
31
|
+
} from './types.js';
|
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 状态文件存储
|
|
3
|
-
* 负责将状态数据持久化到文件系统
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { promises as fs, existsSync } from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import * as os from 'os';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
import type { ConnectionStatusData } from './types.js';
|
|
11
|
-
import { createEmptyStatusData } from './types.js';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* 获取项目根目录
|
|
15
|
-
* 通过当前模块的路径向上查找,直到找到 package.json
|
|
16
|
-
*/
|
|
17
|
-
function getProjectRoot(): string {
|
|
18
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
19
|
-
let currentDir = path.dirname(currentFile);
|
|
20
|
-
|
|
21
|
-
// 向上查找,直到找到 package.json
|
|
22
|
-
while (currentDir !== path.dirname(currentDir)) {
|
|
23
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
24
|
-
if (existsSync(packageJsonPath)) {
|
|
25
|
-
return currentDir;
|
|
26
|
-
}
|
|
27
|
-
currentDir = path.dirname(currentDir);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 如果找不到,使用当前目录
|
|
31
|
-
return currentDir;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 状态存储类
|
|
36
|
-
*/
|
|
37
|
-
export class StatusStorage {
|
|
38
|
-
private statusFilePath: string;
|
|
39
|
-
private statusDir: string;
|
|
40
|
-
|
|
41
|
-
constructor() {
|
|
42
|
-
// 状态文件放在项目根目录下的 .local-tmps 文件夹
|
|
43
|
-
const projectRoot = getProjectRoot();
|
|
44
|
-
this.statusDir = path.join(projectRoot, '.local-tmps');
|
|
45
|
-
this.statusFilePath = path.join(this.statusDir, 'yoyo-status.json');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 保存状态到文件
|
|
50
|
-
*/
|
|
51
|
-
async save(status: ConnectionStatusData): Promise<void> {
|
|
52
|
-
try {
|
|
53
|
-
// 确保目录存在
|
|
54
|
-
await fs.mkdir(this.statusDir, { recursive: true });
|
|
55
|
-
|
|
56
|
-
// 写入状态文件
|
|
57
|
-
const content = JSON.stringify(status, null, 2);
|
|
58
|
-
await fs.writeFile(this.statusFilePath, content, 'utf-8');
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error(
|
|
61
|
-
`[StatusStorage] Failed to save status: ${
|
|
62
|
-
error instanceof Error ? error.message : String(error)
|
|
63
|
-
}`
|
|
64
|
-
);
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 从文件加载状态
|
|
71
|
-
*/
|
|
72
|
-
async load(): Promise<ConnectionStatusData | null> {
|
|
73
|
-
try {
|
|
74
|
-
// 检查文件是否存在
|
|
75
|
-
await fs.access(this.statusFilePath);
|
|
76
|
-
|
|
77
|
-
// 读取文件内容
|
|
78
|
-
const content = await fs.readFile(this.statusFilePath, 'utf-8');
|
|
79
|
-
const status = JSON.parse(content) as ConnectionStatusData;
|
|
80
|
-
|
|
81
|
-
return status;
|
|
82
|
-
} catch (error) {
|
|
83
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
84
|
-
// 文件不存在,返回null
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
console.error(
|
|
89
|
-
`[StatusStorage] Failed to load status: ${
|
|
90
|
-
error instanceof Error ? error.message : String(error)
|
|
91
|
-
}`
|
|
92
|
-
);
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* 清除状态文件
|
|
99
|
-
*/
|
|
100
|
-
async clear(): Promise<void> {
|
|
101
|
-
try {
|
|
102
|
-
await fs.unlink(this.statusFilePath);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
105
|
-
console.error(
|
|
106
|
-
`[StatusStorage] Failed to clear status: ${
|
|
107
|
-
error instanceof Error ? error.message : String(error)
|
|
108
|
-
}`
|
|
109
|
-
);
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* 获取状态文件路径
|
|
117
|
-
*/
|
|
118
|
-
getFilePath(): string {
|
|
119
|
-
return this.statusFilePath;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 检查状态文件是否存在
|
|
124
|
-
*/
|
|
125
|
-
async exists(): Promise<boolean> {
|
|
126
|
-
try {
|
|
127
|
-
await fs.access(this.statusFilePath);
|
|
128
|
-
return true;
|
|
129
|
-
} catch {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 状态文件存储
|
|
3
|
+
* 负责将状态数据持久化到文件系统
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { promises as fs, existsSync } from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import type { ConnectionStatusData } from './types.js';
|
|
11
|
+
import { createEmptyStatusData } from './types.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 获取项目根目录
|
|
15
|
+
* 通过当前模块的路径向上查找,直到找到 package.json
|
|
16
|
+
*/
|
|
17
|
+
function getProjectRoot(): string {
|
|
18
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
19
|
+
let currentDir = path.dirname(currentFile);
|
|
20
|
+
|
|
21
|
+
// 向上查找,直到找到 package.json
|
|
22
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
23
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
24
|
+
if (existsSync(packageJsonPath)) {
|
|
25
|
+
return currentDir;
|
|
26
|
+
}
|
|
27
|
+
currentDir = path.dirname(currentDir);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 如果找不到,使用当前目录
|
|
31
|
+
return currentDir;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 状态存储类
|
|
36
|
+
*/
|
|
37
|
+
export class StatusStorage {
|
|
38
|
+
private statusFilePath: string;
|
|
39
|
+
private statusDir: string;
|
|
40
|
+
|
|
41
|
+
constructor() {
|
|
42
|
+
// 状态文件放在项目根目录下的 .local-tmps 文件夹
|
|
43
|
+
const projectRoot = getProjectRoot();
|
|
44
|
+
this.statusDir = path.join(projectRoot, '.local-tmps');
|
|
45
|
+
this.statusFilePath = path.join(this.statusDir, 'yoyo-status.json');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 保存状态到文件
|
|
50
|
+
*/
|
|
51
|
+
async save(status: ConnectionStatusData): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
// 确保目录存在
|
|
54
|
+
await fs.mkdir(this.statusDir, { recursive: true });
|
|
55
|
+
|
|
56
|
+
// 写入状态文件
|
|
57
|
+
const content = JSON.stringify(status, null, 2);
|
|
58
|
+
await fs.writeFile(this.statusFilePath, content, 'utf-8');
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(
|
|
61
|
+
`[StatusStorage] Failed to save status: ${
|
|
62
|
+
error instanceof Error ? error.message : String(error)
|
|
63
|
+
}`
|
|
64
|
+
);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 从文件加载状态
|
|
71
|
+
*/
|
|
72
|
+
async load(): Promise<ConnectionStatusData | null> {
|
|
73
|
+
try {
|
|
74
|
+
// 检查文件是否存在
|
|
75
|
+
await fs.access(this.statusFilePath);
|
|
76
|
+
|
|
77
|
+
// 读取文件内容
|
|
78
|
+
const content = await fs.readFile(this.statusFilePath, 'utf-8');
|
|
79
|
+
const status = JSON.parse(content) as ConnectionStatusData;
|
|
80
|
+
|
|
81
|
+
return status;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
84
|
+
// 文件不存在,返回null
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.error(
|
|
89
|
+
`[StatusStorage] Failed to load status: ${
|
|
90
|
+
error instanceof Error ? error.message : String(error)
|
|
91
|
+
}`
|
|
92
|
+
);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 清除状态文件
|
|
99
|
+
*/
|
|
100
|
+
async clear(): Promise<void> {
|
|
101
|
+
try {
|
|
102
|
+
await fs.unlink(this.statusFilePath);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
105
|
+
console.error(
|
|
106
|
+
`[StatusStorage] Failed to clear status: ${
|
|
107
|
+
error instanceof Error ? error.message : String(error)
|
|
108
|
+
}`
|
|
109
|
+
);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 获取状态文件路径
|
|
117
|
+
*/
|
|
118
|
+
getFilePath(): string {
|
|
119
|
+
return this.statusFilePath;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 检查状态文件是否存在
|
|
124
|
+
*/
|
|
125
|
+
async exists(): Promise<boolean> {
|
|
126
|
+
try {
|
|
127
|
+
await fs.access(this.statusFilePath);
|
|
128
|
+
return true;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|