@z-qinghui/migpt-claw 1.0.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +690 -0
  3. package/dist/index.d.ts +23 -0
  4. package/dist/index.js +33 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/setup-entry.d.ts +3 -0
  7. package/dist/setup-entry.js +7 -0
  8. package/dist/setup-entry.js.map +1 -0
  9. package/dist/src/channel.d.ts +10 -0
  10. package/dist/src/channel.js +444 -0
  11. package/dist/src/channel.js.map +1 -0
  12. package/dist/src/config.d.ts +125 -0
  13. package/dist/src/config.js +146 -0
  14. package/dist/src/config.js.map +1 -0
  15. package/dist/src/message.d.ts +51 -0
  16. package/dist/src/message.js +145 -0
  17. package/dist/src/message.js.map +1 -0
  18. package/dist/src/mi/account.d.ts +5 -0
  19. package/dist/src/mi/account.js +162 -0
  20. package/dist/src/mi/account.js.map +1 -0
  21. package/dist/src/mi/common.d.ts +15 -0
  22. package/dist/src/mi/common.js +80 -0
  23. package/dist/src/mi/common.js.map +1 -0
  24. package/dist/src/mi/index.d.ts +4 -0
  25. package/dist/src/mi/index.js +10 -0
  26. package/dist/src/mi/index.js.map +1 -0
  27. package/dist/src/mi/mina.d.ts +66 -0
  28. package/dist/src/mi/mina.js +225 -0
  29. package/dist/src/mi/mina.js.map +1 -0
  30. package/dist/src/mi/miot.d.ts +35 -0
  31. package/dist/src/mi/miot.js +168 -0
  32. package/dist/src/mi/miot.js.map +1 -0
  33. package/dist/src/mi/typing.d.ts +90 -0
  34. package/dist/src/mi/typing.js +1 -0
  35. package/dist/src/mi/typing.js.map +1 -0
  36. package/dist/src/onboarding.d.ts +5 -0
  37. package/dist/src/onboarding.js +118 -0
  38. package/dist/src/onboarding.js.map +1 -0
  39. package/dist/src/openclaw-plugin-sdk.d.d.ts +185 -0
  40. package/dist/src/openclaw-plugin-sdk.d.js +1 -0
  41. package/dist/src/openclaw-plugin-sdk.d.js.map +1 -0
  42. package/dist/src/outbound.d.ts +5 -0
  43. package/dist/src/outbound.js +108 -0
  44. package/dist/src/outbound.js.map +1 -0
  45. package/dist/src/runtime.d.ts +6 -0
  46. package/dist/src/runtime.js +15 -0
  47. package/dist/src/runtime.js.map +1 -0
  48. package/dist/src/service.d.ts +70 -0
  49. package/dist/src/service.js +200 -0
  50. package/dist/src/service.js.map +1 -0
  51. package/dist/src/speaker.d.ts +62 -0
  52. package/dist/src/speaker.js +211 -0
  53. package/dist/src/speaker.js.map +1 -0
  54. package/dist/src/tts/mimo.d.ts +50 -0
  55. package/dist/src/tts/mimo.js +214 -0
  56. package/dist/src/tts/mimo.js.map +1 -0
  57. package/dist/src/types.d.ts +30 -0
  58. package/dist/src/types.js +1 -0
  59. package/dist/src/types.js.map +1 -0
  60. package/dist/src/utils/codec.d.ts +31 -0
  61. package/dist/src/utils/codec.js +144 -0
  62. package/dist/src/utils/codec.js.map +1 -0
  63. package/dist/src/utils/debug.d.ts +10 -0
  64. package/dist/src/utils/debug.js +15 -0
  65. package/dist/src/utils/debug.js.map +1 -0
  66. package/dist/src/utils/hash.d.ts +40 -0
  67. package/dist/src/utils/hash.js +75 -0
  68. package/dist/src/utils/hash.js.map +1 -0
  69. package/dist/src/utils/http.d.ts +24 -0
  70. package/dist/src/utils/http.js +151 -0
  71. package/dist/src/utils/http.js.map +1 -0
  72. package/dist/src/utils/index.d.ts +6 -0
  73. package/dist/src/utils/index.js +10 -0
  74. package/dist/src/utils/index.js.map +1 -0
  75. package/dist/src/utils/io.d.ts +26 -0
  76. package/dist/src/utils/io.js +53 -0
  77. package/dist/src/utils/io.js.map +1 -0
  78. package/dist/src/utils/parse.d.ts +26 -0
  79. package/dist/src/utils/parse.js +51 -0
  80. package/dist/src/utils/parse.js.map +1 -0
  81. package/index.ts +26 -0
  82. package/openclaw.plugin.json +344 -0
  83. package/package.json +106 -0
  84. package/setup-entry.ts +12 -0
  85. package/skills/migpt-volume/SKILL.md +182 -0
  86. package/skills/migpt-volume/index.ts +50 -0
  87. package/src/channel.ts +519 -0
  88. package/src/config.ts +299 -0
  89. package/src/message.ts +186 -0
  90. package/src/mi/account.ts +184 -0
  91. package/src/mi/common.ts +105 -0
  92. package/src/mi/index.ts +4 -0
  93. package/src/mi/mina.ts +261 -0
  94. package/src/mi/miot.ts +193 -0
  95. package/src/mi/typing.ts +93 -0
  96. package/src/onboarding.ts +136 -0
  97. package/src/openclaw-plugin-sdk.d.ts +185 -0
  98. package/src/outbound.ts +137 -0
  99. package/src/runtime.ts +14 -0
  100. package/src/service.ts +246 -0
  101. package/src/speaker.ts +264 -0
  102. package/src/tts/mimo.ts +300 -0
  103. package/src/types.ts +34 -0
  104. package/src/utils/codec.ts +206 -0
  105. package/src/utils/debug.ts +16 -0
  106. package/src/utils/hash.ts +104 -0
  107. package/src/utils/http.ts +193 -0
  108. package/src/utils/index.ts +5 -0
  109. package/src/utils/io.ts +68 -0
  110. package/src/utils/parse.ts +64 -0
  111. package/tsconfig.json +25 -0
@@ -0,0 +1,193 @@
1
+ import { sleep } from './parse.js';
2
+ import { jsonEncode } from './parse.js';
3
+ import axios from 'axios';
4
+ import type { MiAccount } from '../mi/typing.js';
5
+ import { Debugger } from './debug.js';
6
+
7
+ const _baseConfig: any = {
8
+ proxy: false,
9
+ decompress: true,
10
+ headers: {
11
+ 'Accept-Encoding': 'gzip, deflate',
12
+ 'Content-Type': 'application/x-www-form-urlencoded',
13
+ 'User-Agent':
14
+ 'Dalvik/2.1.0 (Linux; U; Android 10; RMX2111 Build/QP1A.190711.020) APP/xiaomi.mico APPV/2004040 MK/Uk1YMjExMQ== PassportSDK/3.8.3 passport-ui/3.8.3',
15
+ },
16
+ };
17
+
18
+ const _http = axios.create(_baseConfig);
19
+
20
+ interface HttpError {
21
+ isError: true;
22
+ error: any;
23
+ code: string;
24
+ message: string;
25
+ }
26
+
27
+ type RequestConfig = any & {
28
+ account?: MiAccount;
29
+ setAccount?: (newAccount: any) => void;
30
+ rawResponse?: boolean;
31
+ cookies?: Record<string, string | number | boolean | undefined>;
32
+ };
33
+
34
+ _http.interceptors.response.use(
35
+ (res: any) => {
36
+ if (res.config.rawResponse) {
37
+ return res;
38
+ }
39
+ return res.data;
40
+ },
41
+ async (err) => {
42
+ // 401 登录凭证过期后,自动刷新 token
43
+ const newResult = await tokenRefresher.refreshTokenAndRetry(err);
44
+ if (newResult) {
45
+ return newResult;
46
+ }
47
+ const error = err.response?.data?.error || err.response?.data;
48
+ const request = {
49
+ method: err.config.method,
50
+ url: err.config.url,
51
+ headers: jsonEncode(err.config.headers),
52
+ data: jsonEncode({ body: err.config.data }),
53
+ };
54
+ const response = !err.response
55
+ ? undefined
56
+ : {
57
+ url: err.config.url,
58
+ status: err.response.status,
59
+ headers: jsonEncode(err.response.headers),
60
+ data: jsonEncode({ body: err.response.data }),
61
+ };
62
+ return {
63
+ isError: true,
64
+ code: error?.code || err.response?.status || err.code || '未知',
65
+ message: error?.message || err.response?.statusText || err.message || '未知',
66
+ error: { request, response },
67
+ };
68
+ },
69
+ );
70
+
71
+ class HTTPClient {
72
+ // 默认 5 秒超时
73
+ timeout = 5 * 1000;
74
+
75
+ async get<T = any>(
76
+ url: string,
77
+ _query?: Record<string, string | number | boolean | undefined> | RequestConfig,
78
+ _config?: RequestConfig,
79
+ ): Promise<T | HttpError> {
80
+ let query = _query;
81
+ let config = _config;
82
+ if (_config === undefined) {
83
+ config = _query;
84
+ query = undefined;
85
+ }
86
+ return _http.get<T>(HTTPClient.buildURL(url, query), HTTPClient.buildConfig(config)) as any;
87
+ }
88
+
89
+ async post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T | HttpError> {
90
+ return _http.post<T>(url, data, HTTPClient.buildConfig(config)) as any;
91
+ }
92
+
93
+ static buildURL = (url: string, query?: Record<string, any>) => {
94
+ const _url = new URL(url);
95
+ for (const [key, value] of Object.entries(query ?? {})) {
96
+ if (isNotEmpty(value)) {
97
+ _url.searchParams.append(key, value.toString());
98
+ }
99
+ }
100
+ return _url.href;
101
+ };
102
+
103
+ static buildConfig = (config?: RequestConfig) => {
104
+ if (config?.cookies) {
105
+ config.headers = {
106
+ ...config.headers,
107
+ Cookie: Object.entries(config.cookies)
108
+ .map(([key, value]) => `${key}=${value == null ? '' : value.toString()};`)
109
+ .join(' '),
110
+ };
111
+ }
112
+ if (config && !config.timeout) {
113
+ config.timeout = Http.timeout; // 默认超时时间为 5 秒
114
+ }
115
+ return config;
116
+ };
117
+ }
118
+
119
+ export const Http = new HTTPClient();
120
+
121
+ function isNotEmpty(value: any): boolean {
122
+ return value !== null && value !== undefined && value !== '';
123
+ }
124
+
125
+ class TokenRefresher {
126
+ isRefreshing = false;
127
+
128
+ /**
129
+ * 自动刷新过期的凭证,并重新发送请求
130
+ */
131
+ async refreshTokenAndRetry(err: any, maxRetry = 3) {
132
+ const isMiNA = err?.config?.url?.includes('mina.mi.com');
133
+ const isMIoT = err?.config?.url?.includes('io.mi.com');
134
+ if ((!isMiNA && !isMIoT) || err.response?.status !== 401) {
135
+ return;
136
+ }
137
+ if (this.isRefreshing) {
138
+ return;
139
+ }
140
+ let result: any;
141
+ this.isRefreshing = true;
142
+ let newServiceAccount = undefined;
143
+ for (let i = 0; i < maxRetry; i++) {
144
+ if (Debugger.debug) {
145
+ console.log(`❌ 登录凭证已过期,正在尝试刷新 Token ${i + 1}`);
146
+ }
147
+ newServiceAccount = await this.refreshToken(err);
148
+ if (newServiceAccount) {
149
+ // 刷新成功,重新请求
150
+ result = await this.retry(err, newServiceAccount);
151
+ break;
152
+ }
153
+ // 隔 3 秒后重试
154
+ await sleep(3000);
155
+ }
156
+ this.isRefreshing = false;
157
+ if (!newServiceAccount) {
158
+ console.error('❌ 刷新登录凭证失败,请检查账号密码是否仍然有效。');
159
+ }
160
+ return result;
161
+ }
162
+
163
+ /**
164
+ * 刷新登录凭证并同步到本地
165
+ */
166
+ async refreshToken(_err: any) {
167
+ // 注意:这里需要循环依赖,暂时简化处理
168
+ // 实际应该从配置中读取并刷新
169
+ return undefined;
170
+ }
171
+
172
+ /**
173
+ * 重新请求
174
+ */
175
+ async retry(_err: any, account: any) {
176
+ // 更新 cookies
177
+ const cookies = _err.config.cookies ?? {};
178
+ for (const key of ['serviceToken']) {
179
+ if (cookies[key] && account[key]) {
180
+ cookies[key] = account[key];
181
+ }
182
+ }
183
+ for (const key of ['deviceSNProfile']) {
184
+ if (cookies[key] && account.device?.[key]) {
185
+ cookies[key] = account.device[key];
186
+ }
187
+ }
188
+ // 重新请求
189
+ return _http(HTTPClient.buildConfig(_err.config)!);
190
+ }
191
+ }
192
+
193
+ const tokenRefresher = new TokenRefresher();
@@ -0,0 +1,5 @@
1
+ export { Http } from './http.js';
2
+ export { Debugger } from './debug.js';
3
+ export * from './codec.js';
4
+ export * from './hash.js';
5
+ export * from './io.js';
@@ -0,0 +1,68 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ /**
5
+ * 获取数据目录
6
+ */
7
+ export function getDataDir(subdir?: string): string {
8
+ const baseDir = join(process.cwd(), '.migpt');
9
+ if (subdir) {
10
+ return join(baseDir, subdir);
11
+ }
12
+ return baseDir;
13
+ }
14
+
15
+ /**
16
+ * 读取 JSON 文件
17
+ */
18
+ export async function readJSON<T = any>(filename: string): Promise<T | undefined> {
19
+ try {
20
+ const filepath = join(getDataDir(), filename);
21
+ const content = await fs.readFile(filepath, 'utf-8');
22
+ return JSON.parse(content);
23
+ } catch {
24
+ return undefined;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * 写入 JSON 文件
30
+ */
31
+ export async function writeJSON(filename: string, data: any): Promise<void> {
32
+ try {
33
+ const dir = getDataDir();
34
+ await fs.mkdir(dir, { recursive: true });
35
+ const filepath = join(dir, filename);
36
+ await fs.writeFile(filepath, JSON.stringify(data, null, 2), 'utf-8');
37
+ } catch (err: any) {
38
+ console.error('❌ 写入文件失败:', err.message);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * 检查文件是否存在
44
+ */
45
+ export async function fileExists(filepath: string): Promise<boolean> {
46
+ try {
47
+ await fs.access(filepath);
48
+ return true;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * 读取文件
56
+ */
57
+ export async function readFile(filepath: string): Promise<Buffer> {
58
+ return fs.readFile(filepath);
59
+ }
60
+
61
+ /**
62
+ * 写入文件
63
+ */
64
+ export async function writeFile(filepath: string, content: string | Buffer): Promise<void> {
65
+ const dir = join(filepath, '..');
66
+ await fs.mkdir(dir, { recursive: true });
67
+ await fs.writeFile(filepath, content);
68
+ }
@@ -0,0 +1,64 @@
1
+ export function jsonEncode<T>(obj: T, options?: { prettier?: boolean }) {
2
+ const { prettier } = options ?? {};
3
+ try {
4
+ return JSON.stringify(obj, undefined, prettier ? 4 : 0);
5
+ } catch (_) {
6
+ return undefined;
7
+ }
8
+ }
9
+
10
+ export function jsonDecode<T = any>(json: string | null | undefined) {
11
+ if (!json) {
12
+ return undefined;
13
+ }
14
+ try {
15
+ return JSON.parse(json) as T;
16
+ } catch (_) {
17
+ return undefined;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 清理 JSON 字符串并解码
23
+ */
24
+ export function cleanJsonAndDecode(input: string | undefined | null) {
25
+ if (input == undefined) return undefined;
26
+ const pattern = /(\{[\s\S]*?"\s*:\s*[\s\S]*?})/;
27
+ const match = input.match(pattern);
28
+
29
+ if (!match) {
30
+ return undefined;
31
+ }
32
+
33
+ return jsonDecode(match[0]);
34
+ }
35
+
36
+ /**
37
+ * 获取数组第一个元素
38
+ */
39
+ export function firstOf<T>(items?: T[]) {
40
+ return items ? (items.length < 1 ? undefined : items[0]) : undefined;
41
+ }
42
+
43
+ /**
44
+ * 获取数组最后一个元素
45
+ */
46
+ export function lastOf<T>(items?: T[]) {
47
+ return items?.length ? items[items.length - 1] : undefined;
48
+ }
49
+
50
+ /**
51
+ * 休眠
52
+ */
53
+ export function sleep(ms: number): Promise<void> {
54
+ return new Promise((resolve) => setTimeout(resolve, ms));
55
+ }
56
+
57
+ /**
58
+ * 断言
59
+ */
60
+ export function assert(condition: boolean, message: string): asserts condition {
61
+ if (!condition) {
62
+ throw new Error(message);
63
+ }
64
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "lib": ["ES2022"],
6
+ "moduleResolution": "bundler",
7
+ "outDir": "./dist",
8
+ "rootDir": ".",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noImplicitReturns": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "allowSyntheticDefaultImports": true
22
+ },
23
+ "include": ["src/**/*", "index.ts"],
24
+ "exclude": ["node_modules", "dist"]
25
+ }